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