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