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