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