xref: /original-bsd/sys/netiso/iso_pcb.c (revision 27393bdf)
1 /*-
2  * Copyright (c) 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)iso_pcb.c	8.3 (Berkeley) 07/19/94
8  */
9 
10 /***********************************************************
11 		Copyright IBM Corporation 1987
12 
13                       All Rights Reserved
14 
15 Permission to use, copy, modify, and distribute this software and its
16 documentation for any purpose and without fee is hereby granted,
17 provided that the above copyright notice appear in all copies and that
18 both that copyright notice and this permission notice appear in
19 supporting documentation, and that the name of IBM not be
20 used in advertising or publicity pertaining to distribution of the
21 software without specific, written prior permission.
22 
23 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29 SOFTWARE.
30 
31 ******************************************************************/
32 
33 /*
34  * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
35  */
36 /*
37  * $Header: iso_pcb.c,v 4.5 88/06/29 14:59:56 hagens Exp $
38  * $Source: /usr/argo/sys/netiso/RCS/iso_pcb.c,v $
39  *
40  * Iso address family net-layer(s) pcb stuff. NEH 1/29/87
41  */
42 
43 #ifdef ISO
44 
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/mbuf.h>
48 #include <sys/socket.h>
49 #include <sys/socketvar.h>
50 #include <sys/errno.h>
51 
52 #include <netiso/argo_debug.h>
53 #include <netiso/iso.h>
54 #include <netiso/clnp.h>
55 #include <netinet/in_systm.h>
56 #include <net/if.h>
57 #include <net/route.h>
58 #include <netiso/iso_pcb.h>
59 #include <netiso/iso_var.h>
60 #include <sys/protosw.h>
61 
62 #ifdef TPCONS
63 #include <netccitt/x25.h>
64 #include <netccitt/pk.h>
65 #include <netccitt/pk_var.h>
66 #endif
67 
68 #define PCBNULL (struct isopcb *)0
69 struct	iso_addr zeroiso_addr = {
70 	0
71 };
72 
73 
74 /*
75  * FUNCTION:		iso_pcballoc
76  *
77  * PURPOSE:			creates an isopcb structure in an mbuf,
78  *					with socket (so), and
79  *					puts it in the queue with head (head)
80  *
81  * RETURNS:			0 if OK, ENOBUFS if can't alloc the necessary mbuf
82  */
83 int
84 iso_pcballoc(so, head)
85 	struct socket *so;
86 	struct isopcb *head;
87 {
88 	register struct isopcb *isop;
89 
90 	IFDEBUG(D_ISO)
91 		printf("iso_pcballoc(so 0x%x)\n", so);
92 	ENDDEBUG
93 	MALLOC(isop, struct isopcb *, sizeof(*isop), M_PCB, M_NOWAIT);
94 	if (isop == NULL)
95 		return ENOBUFS;
96 	bzero((caddr_t)isop, sizeof(*isop));
97 	isop->isop_head = head;
98 	isop->isop_socket = so;
99 	insque(isop, head);
100 	if (so)
101 		so->so_pcb = (caddr_t)isop;
102 	return 0;
103 }
104 
105 /*
106  * FUNCTION:		iso_pcbbind
107  *
108  * PURPOSE:			binds the address given in *(nam) to the socket
109  *					specified by the isopcb in *(isop)
110  *					If the given address is zero, it makes sure the
111  *					address isn't already in use and if it's got a network
112  *					portion, we look for an interface with that network
113  *					address.  If the address given is zero, we allocate
114  *					a port and stuff it in the (nam) structure.
115  *
116  * RETURNS:			errno E* or 0 if ok.
117  *
118  * SIDE EFFECTS:	increments head->isop_lport if it allocates a port #
119  *
120  * NOTES:
121  */
122 #define	satosiso(sa)	((struct sockaddr_iso *)(sa))
123 int
124 iso_pcbbind(isop, nam)
125 	register struct isopcb *isop;
126 	struct mbuf *nam;
127 {
128 	register struct isopcb *head = isop->isop_head;
129 	register struct sockaddr_iso *siso;
130 	struct iso_ifaddr *ia;
131 	union {
132 		char data[2];
133 		u_short s;
134 	} suf;
135 
136 	IFDEBUG(D_ISO)
137 		printf("iso_pcbbind(isop 0x%x, nam 0x%x)\n", isop, nam);
138 	ENDDEBUG
139 	suf.s = 0;
140 	if (iso_ifaddr == 0) /* any interfaces attached? */
141 		return EADDRNOTAVAIL;
142 	if (isop->isop_laddr)  /* already bound */
143 		return EADDRINUSE;
144 	if(nam == (struct mbuf *)0) {
145 		isop->isop_laddr = &isop->isop_sladdr;
146 		isop->isop_sladdr.siso_len = sizeof(struct sockaddr_iso);
147 		isop->isop_sladdr.siso_family = AF_ISO;
148 		isop->isop_sladdr.siso_tlen = 2;
149 		isop->isop_sladdr.siso_nlen = 0;
150 		isop->isop_sladdr.siso_slen = 0;
151 		isop->isop_sladdr.siso_plen = 0;
152 		goto noname;
153 	}
154 	siso = mtod(nam, struct sockaddr_iso *);
155 	IFDEBUG(D_ISO)
156 		printf("iso_pcbbind(name len 0x%x)\n", nam->m_len);
157 		printf("The address is %s\n", clnp_iso_addrp(&siso->siso_addr));
158 	ENDDEBUG
159 	/*
160 	 * We would like sort of length check but since some OSI addrs
161 	 * do not have fixed length, we can't really do much.
162 	 * The ONLY thing we can say is that an osi addr has to have
163 	 * at LEAST an afi and one more byte and had better fit into
164 	 * a struct iso_addr.
165 	 * However, in fact the size of the whole thing is a struct
166 	 * sockaddr_iso, so probably this is what we should check for.
167 	 */
168 	if( (nam->m_len < 2) || (nam->m_len < siso->siso_len)) {
169 			return ENAMETOOLONG;
170 	}
171 	if (siso->siso_nlen) {
172 		/* non-zero net addr- better match one of our interfaces */
173 		IFDEBUG(D_ISO)
174 			printf("iso_pcbbind: bind to NOT zeroisoaddr\n");
175 		ENDDEBUG
176 		for (ia = iso_ifaddr; ia; ia = ia->ia_next)
177 			if (SAME_ISOIFADDR(siso, &ia->ia_addr))
178 				break;
179 		if (ia == 0)
180 			return EADDRNOTAVAIL;
181 	}
182 	if (siso->siso_len <= sizeof (isop->isop_sladdr)) {
183 		isop->isop_laddr = &isop->isop_sladdr;
184 	} else {
185 		if ((nam = m_copy(nam, 0, (int)M_COPYALL)) == 0)
186 			return ENOBUFS;
187 		isop->isop_laddr = mtod(nam, struct sockaddr_iso *);
188 	}
189 	bcopy((caddr_t)siso, (caddr_t)isop->isop_laddr, siso->siso_len);
190 	if (siso->siso_tlen == 0)
191 		goto noname;
192 	if ((isop->isop_socket->so_options & SO_REUSEADDR) == 0 &&
193 		iso_pcblookup(head, 0, (caddr_t)0, isop->isop_laddr))
194 		return EADDRINUSE;
195 	if (siso->siso_tlen <= 2) {
196 		bcopy(TSEL(siso), suf.data, sizeof(suf.data));
197 		suf.s = ntohs(suf.s);
198 		if((suf.s < ISO_PORT_RESERVED) &&
199 		   (isop->isop_socket->so_state && SS_PRIV) == 0)
200 			return EACCES;
201 	} else {
202 		register char *cp;
203 noname:
204 		cp = TSEL(isop->isop_laddr);
205 		isop->isop_laddr->siso_tlen = 2;
206 	IFDEBUG(D_ISO)
207 		printf("iso_pcbbind noname\n");
208 	ENDDEBUG
209 		do {
210 			if (head->isop_lport++ < ISO_PORT_RESERVED ||
211 			    head->isop_lport > ISO_PORT_USERRESERVED)
212 				head->isop_lport = ISO_PORT_RESERVED;
213 			suf.s = htons(head->isop_lport);
214 			cp[0] = suf.data[0];
215 			cp[1] = suf.data[1];
216 		} while (iso_pcblookup(head, 0, (caddr_t)0, isop->isop_laddr));
217 	}
218 	IFDEBUG(D_ISO)
219 		printf("iso_pcbbind returns 0, suf 0x%x\n", suf);
220 	ENDDEBUG
221 	return 0;
222 }
223 /*
224  * FUNCTION:		iso_pcbconnect
225  *
226  * PURPOSE:			Make the isopcb (isop) look like it's connected.
227  *					In other words, give it the peer address given in
228  *					the mbuf * (nam).   Make sure such a combination
229  *					of local, peer addresses doesn't already exist
230  *					for this protocol.  Internet mentality prevails here,
231  *					wherein a src,dst pair uniquely identifies a connection.
232  * 					Both net address and port must be specified in argument
233  *					(nam).
234  * 					If we don't have a local address for this socket yet,
235  *					we pick one by calling iso_pcbbind().
236  *
237  * RETURNS:			errno E* or 0 if ok.
238  *
239  * SIDE EFFECTS:	Looks up a route, which may cause one to be left
240  *					in the isopcb.
241  *
242  * NOTES:
243  */
244 int
245 iso_pcbconnect(isop, nam)
246 	register struct isopcb *isop;
247 	struct mbuf *nam;
248 {
249 	register struct sockaddr_iso	*siso = mtod(nam, struct sockaddr_iso *);
250 	int								local_zero, error = 0;
251 	struct iso_ifaddr 				*ia;
252 
253 	IFDEBUG(D_ISO)
254 		printf("iso_pcbconnect(isop 0x%x sock 0x%x nam 0x%x",
255 					isop, isop->isop_socket, nam);
256 		printf("nam->m_len 0x%x), addr:\n", nam->m_len);
257 		dump_isoaddr(siso);
258 	ENDDEBUG
259 	if (nam->m_len < siso->siso_len)
260 		return EINVAL;
261 	if (siso->siso_family != AF_ISO)
262 		return EAFNOSUPPORT;
263 	if (siso->siso_nlen == 0) {
264 		if (ia = iso_ifaddr) {
265 			int nlen = ia->ia_addr.siso_nlen;
266 			ovbcopy(TSEL(siso), nlen + TSEL(siso),
267 				siso->siso_plen + siso->siso_tlen + siso->siso_slen);
268 			bcopy((caddr_t)&ia->ia_addr.siso_addr,
269 				  (caddr_t)&siso->siso_addr, nlen + 1);
270 			/* includes siso->siso_nlen = nlen; */
271 		} else
272 			return EADDRNOTAVAIL;
273 	}
274 	/*
275 	 * Local zero means either not bound, or bound to a TSEL, but no
276 	 * particular local interface.  So, if we want to send somebody
277 	 * we need to choose a return address.
278 	 */
279 	local_zero =
280 		((isop->isop_laddr == 0) || (isop->isop_laddr->siso_nlen == 0));
281 	if (local_zero) {
282 		int flags;
283 
284 		IFDEBUG(D_ISO)
285 			printf("iso_pcbconnect localzero 1\n");
286 		ENDDEBUG
287 		/*
288 		 * If route is known or can be allocated now,
289 		 * our src addr is taken from the i/f, else punt.
290 		 */
291 		flags = isop->isop_socket->so_options & SO_DONTROUTE;
292 		if (error = clnp_route(&siso->siso_addr, &isop->isop_route, flags,
293 						(struct sockaddr **)0, &ia))
294 			return error;
295 		IFDEBUG(D_ISO)
296 			printf("iso_pcbconnect localzero 2, ro->ro_rt 0x%x",
297 				isop->isop_route.ro_rt);
298 			printf(" ia 0x%x\n", ia);
299 		ENDDEBUG
300 	}
301 	IFDEBUG(D_ISO)
302 		printf("in iso_pcbconnect before lookup isop 0x%x isop->sock 0x%x\n",
303 			isop, isop->isop_socket);
304 	ENDDEBUG
305 	if (local_zero) {
306 		int nlen, tlen, totlen; caddr_t oldtsel, newtsel;
307 		siso = isop->isop_laddr;
308 		if (siso == 0 || siso->siso_tlen == 0)
309 			(void)iso_pcbbind(isop, (struct mbuf *)0);
310 		/*
311 		 * Here we have problem of squezeing in a definite network address
312 		 * into an existing sockaddr_iso, which in fact may not have room
313 		 * for it.  This gets messy.
314 		 */
315 		siso = isop->isop_laddr;
316 		oldtsel = TSEL(siso);
317 		tlen = siso->siso_tlen;
318 		nlen = ia->ia_addr.siso_nlen;
319 		totlen = tlen + nlen + _offsetof(struct sockaddr_iso, siso_data[0]);
320 		if ((siso == &isop->isop_sladdr) &&
321 			(totlen > sizeof(isop->isop_sladdr))) {
322 			struct mbuf *m = m_get(MT_SONAME, M_DONTWAIT);
323 			if (m == 0)
324 					return ENOBUFS;
325 			m->m_len = totlen;
326 			isop->isop_laddr = siso = mtod(m, struct sockaddr_iso *);
327 		}
328 		siso->siso_nlen = ia->ia_addr.siso_nlen;
329 		newtsel = TSEL(siso);
330 		ovbcopy(oldtsel, newtsel, tlen);
331 		bcopy(ia->ia_addr.siso_data, siso->siso_data, nlen);
332 		siso->siso_tlen = tlen;
333 		siso->siso_family = AF_ISO;
334 		siso->siso_len = totlen;
335 		siso = mtod(nam, struct sockaddr_iso *);
336 	}
337 	IFDEBUG(D_ISO)
338 		printf("in iso_pcbconnect before bcopy isop 0x%x isop->sock 0x%x\n",
339 			isop, isop->isop_socket);
340 	ENDDEBUG
341 	/*
342 	 * If we had to allocate space to a previous big foreign address,
343 	 * and for some reason we didn't free it, we reuse it knowing
344 	 * that is going to be big enough, as sockaddrs are delivered in
345 	 * 128 byte mbufs.
346 	 * If the foreign address is small enough, we use default space;
347 	 * otherwise, we grab an mbuf to copy into.
348 	 */
349 	if (isop->isop_faddr == 0 || isop->isop_faddr == &isop->isop_sfaddr) {
350 		if (siso->siso_len <= sizeof(isop->isop_sfaddr))
351 			isop->isop_faddr = &isop->isop_sfaddr;
352 		else {
353 			struct mbuf *m = m_get(MT_SONAME, M_DONTWAIT);
354 			if (m == 0)
355 				return ENOBUFS;
356 			isop->isop_faddr = mtod(m, struct sockaddr_iso *);
357 		}
358 	}
359 	bcopy((caddr_t)siso, (caddr_t)isop->isop_faddr, siso->siso_len);
360 	IFDEBUG(D_ISO)
361 		printf("in iso_pcbconnect after bcopy isop 0x%x isop->sock 0x%x\n",
362 			isop, isop->isop_socket);
363 		printf("iso_pcbconnect connected to addr:\n");
364 		dump_isoaddr(isop->isop_faddr);
365 		printf("iso_pcbconnect end: src addr:\n");
366 		dump_isoaddr(isop->isop_laddr);
367 	ENDDEBUG
368 	return 0;
369 }
370 
371 /*
372  * FUNCTION:		iso_pcbdisconnect()
373  *
374  * PURPOSE:			washes away the peer address info so the socket
375  *					appears to be disconnected.
376  *					If there's no file descriptor associated with the socket
377  *					it detaches the pcb.
378  *
379  * RETURNS:			Nada.
380  *
381  * SIDE EFFECTS:	May detach the pcb.
382  *
383  * NOTES:
384  */
385 void
386 iso_pcbdisconnect(isop)
387 	struct isopcb *isop;
388 {
389 	void iso_pcbdetach();
390 	register struct sockaddr_iso *siso;
391 
392 	IFDEBUG(D_ISO)
393 		printf("iso_pcbdisconnect(isop 0x%x)\n", isop);
394 	ENDDEBUG
395 	/*
396 	 * Preserver binding infnormation if already bound.
397 	 */
398 	if ((siso = isop->isop_laddr) && siso->siso_nlen && siso->siso_tlen) {
399 		caddr_t otsel = TSEL(siso);
400 		siso->siso_nlen = 0;
401 		ovbcopy(otsel, TSEL(siso), siso->siso_tlen);
402 	}
403 	if (isop->isop_faddr && isop->isop_faddr != &isop->isop_sfaddr)
404 		m_freem(dtom(isop->isop_faddr));
405 	isop->isop_faddr = 0;
406 	if (isop->isop_socket->so_state & SS_NOFDREF)
407 		iso_pcbdetach(isop);
408 }
409 
410 /*
411  * FUNCTION:		iso_pcbdetach
412  *
413  * PURPOSE:			detach the pcb at *(isop) from it's socket and free
414  *					the mbufs associated with the pcb..
415  *					Dequeues (isop) from its head.
416  *
417  * RETURNS:			Nada.
418  *
419  * SIDE EFFECTS:
420  *
421  * NOTES:
422  */
423 void
424 iso_pcbdetach(isop)
425 	struct isopcb *isop;
426 {
427 	struct socket *so = isop->isop_socket;
428 
429 	IFDEBUG(D_ISO)
430 		printf("iso_pcbdetach(isop 0x%x socket 0x%x so 0x%x)\n",
431 			isop, isop->isop_socket, so);
432 	ENDDEBUG
433 #ifdef TPCONS
434 	if (isop->isop_chan) {
435 		register struct pklcd *lcp = (struct pklcd *)isop->isop_chan;
436 		if (--isop->isop_refcnt > 0)
437 			return;
438 		if (lcp && lcp->lcd_state == DATA_TRANSFER) {
439 			lcp->lcd_upper = 0;
440 			lcp->lcd_upnext = 0;
441 			pk_disconnect(lcp);
442 		}
443 		isop->isop_chan = 0;
444 	}
445 #endif
446 	if (so) { /* in the x.25 domain, we sometimes have no socket */
447 		so->so_pcb = 0;
448 		sofree(so);
449 	}
450 	IFDEBUG(D_ISO)
451 		printf("iso_pcbdetach 2 \n");
452 	ENDDEBUG
453 	if (isop->isop_options)
454 		(void)m_free(isop->isop_options);
455 	IFDEBUG(D_ISO)
456 		printf("iso_pcbdetach 3 \n");
457 	ENDDEBUG
458 	if (isop->isop_route.ro_rt)
459 		rtfree(isop->isop_route.ro_rt);
460 	IFDEBUG(D_ISO)
461 		printf("iso_pcbdetach 3.1\n");
462 	ENDDEBUG
463 	if (isop->isop_clnpcache != NULL) {
464 		struct clnp_cache *clcp =
465 			mtod(isop->isop_clnpcache, struct clnp_cache *);
466 		IFDEBUG(D_ISO)
467 			printf("iso_pcbdetach 3.2: clcp 0x%x freeing clc_hdr x%x\n",
468 				clcp, clcp->clc_hdr);
469 		ENDDEBUG
470 		if (clcp->clc_hdr != NULL)
471 			m_free(clcp->clc_hdr);
472 		IFDEBUG(D_ISO)
473 			printf("iso_pcbdetach 3.3: freeing cache x%x\n",
474 				isop->isop_clnpcache);
475 		ENDDEBUG
476 		m_free(isop->isop_clnpcache);
477 	}
478 	IFDEBUG(D_ISO)
479 		printf("iso_pcbdetach 4 \n");
480 	ENDDEBUG
481 	remque(isop);
482 	IFDEBUG(D_ISO)
483 		printf("iso_pcbdetach 5 \n");
484 	ENDDEBUG
485 	if (isop->isop_laddr && (isop->isop_laddr != &isop->isop_sladdr))
486 		m_freem(dtom(isop->isop_laddr));
487 	free((caddr_t)isop, M_PCB);
488 }
489 
490 
491 /*
492  * FUNCTION:		iso_pcbnotify
493  *
494  * PURPOSE:			notify all connections in this protocol's queue (head)
495  *					that have peer address (dst) of the problem (errno)
496  *					by calling (notify) on the connections' isopcbs.
497  *
498  * RETURNS:			Rien.
499  *
500  * SIDE EFFECTS:
501  *
502  * NOTES:			(notify) is called at splimp!
503  */
504 void
505 iso_pcbnotify(head, siso, errno, notify)
506 	struct isopcb *head;
507 	register struct sockaddr_iso *siso;
508 	int errno, (*notify)();
509 {
510 	register struct isopcb *isop;
511 	int s = splimp();
512 
513 	IFDEBUG(D_ISO)
514 		printf("iso_pcbnotify(head 0x%x, notify 0x%x) dst:\n", head, notify);
515 	ENDDEBUG
516 	for (isop = head->isop_next; isop != head; isop = isop->isop_next) {
517 		if (isop->isop_socket == 0 || isop->isop_faddr == 0 ||
518 			!SAME_ISOADDR(siso, isop->isop_faddr)) {
519 			IFDEBUG(D_ISO)
520 				printf("iso_pcbnotify: CONTINUE isop 0x%x, sock 0x%x\n" ,
521 					isop, isop->isop_socket);
522 				printf("addrmatch cmp'd with (0x%x):\n", isop->isop_faddr);
523 				dump_isoaddr(isop->isop_faddr);
524 			ENDDEBUG
525 			continue;
526 		}
527 		if (errno)
528 			isop->isop_socket->so_error = errno;
529 		if (notify)
530 			(*notify)(isop);
531 	}
532 	splx(s);
533 	IFDEBUG(D_ISO)
534 		printf("END OF iso_pcbnotify\n" );
535 	ENDDEBUG
536 }
537 
538 
539 /*
540  * FUNCTION:		iso_pcblookup
541  *
542  * PURPOSE:			looks for a given combination of (faddr), (fport),
543  *					(lport), (laddr) in the queue named by (head).
544  *					Argument (flags) is ignored.
545  *
546  * RETURNS:			ptr to the isopcb if it finds a connection matching
547  *					these arguments, o.w. returns zero.
548  *
549  * SIDE EFFECTS:
550  *
551  * NOTES:
552  */
553 struct isopcb *
554 iso_pcblookup(head, fportlen, fport, laddr)
555 	struct isopcb *head;
556 	register struct sockaddr_iso *laddr;
557 	caddr_t fport;
558 	int fportlen;
559 {
560 	register struct isopcb *isop;
561 	register caddr_t lp = TSEL(laddr);
562 	unsigned int llen = laddr->siso_tlen;
563 
564 	IFDEBUG(D_ISO)
565 		printf("iso_pcblookup(head 0x%x laddr 0x%x fport 0x%x)\n",
566 			head, laddr, fport);
567 	ENDDEBUG
568 	for (isop = head->isop_next; isop != head; isop = isop->isop_next) {
569 		if (isop->isop_laddr == 0 || isop->isop_laddr == laddr)
570 			continue;
571 		if (isop->isop_laddr->siso_tlen != llen)
572 			continue;
573 		if (bcmp(lp, TSEL(isop->isop_laddr), llen))
574 			continue;
575 		if (fportlen && isop->isop_faddr &&
576 			bcmp(fport, TSEL(isop->isop_faddr), (unsigned)fportlen))
577 			continue;
578 		/*	PHASE2
579 		 *	addrmatch1 should be iso_addrmatch(a, b, mask)
580 		 *	where mask is taken from isop->isop_laddrmask (new field)
581 		 *	isop_lnetmask will also be available in isop
582 		if (laddr != &zeroiso_addr &&
583 			!iso_addrmatch1(laddr, &(isop->isop_laddr.siso_addr)))
584 			continue;
585 		*/
586 		if (laddr->siso_nlen && (!SAME_ISOADDR(laddr, isop->isop_laddr)))
587 			continue;
588 		return (isop);
589 	}
590 	return (struct isopcb *)0;
591 }
592 #endif /* ISO */
593