xref: /original-bsd/sys/netiso/iso_pcb.c (revision 3705696b)
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.1 (Berkeley) 06/10/93
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_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 (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 	IFDEBUG(D_ISO)
206 		printf("iso_pcbbind noname\n");
207 	ENDDEBUG
208 		do {
209 			if (head->isop_lport++ < ISO_PORT_RESERVED ||
210 			    head->isop_lport > ISO_PORT_USERRESERVED)
211 				head->isop_lport = ISO_PORT_RESERVED;
212 			suf.s = htons(head->isop_lport);
213 			cp[0] = suf.data[0];
214 			cp[1] = suf.data[1];
215 		} while (iso_pcblookup(head, 0, (caddr_t)0, isop->isop_laddr));
216 	}
217 	IFDEBUG(D_ISO)
218 		printf("iso_pcbbind returns 0, suf 0x%x\n", suf);
219 	ENDDEBUG
220 	return 0;
221 }
222 /*
223  * FUNCTION:		iso_pcbconnect
224  *
225  * PURPOSE:			Make the isopcb (isop) look like it's connected.
226  *					In other words, give it the peer address given in
227  *					the mbuf * (nam).   Make sure such a combination
228  *					of local, peer addresses doesn't already exist
229  *					for this protocol.  Internet mentality prevails here,
230  *					wherein a src,dst pair uniquely identifies a connection.
231  * 					Both net address and port must be specified in argument
232  *					(nam).
233  * 					If we don't have a local address for this socket yet,
234  *					we pick one by calling iso_pcbbind().
235  *
236  * RETURNS:			errno E* or 0 if ok.
237  *
238  * SIDE EFFECTS:	Looks up a route, which may cause one to be left
239  *					in the isopcb.
240  *
241  * NOTES:
242  */
243 int
244 iso_pcbconnect(isop, nam)
245 	register struct isopcb *isop;
246 	struct mbuf *nam;
247 {
248 	register struct sockaddr_iso	*siso = mtod(nam, struct sockaddr_iso *);
249 	int								local_zero, error = 0;
250 	struct iso_ifaddr 				*ia;
251 
252 	IFDEBUG(D_ISO)
253 		printf("iso_pcbconnect(isop 0x%x sock 0x%x nam 0x%x",
254 					isop, isop->isop_socket, nam);
255 		printf("nam->m_len 0x%x), addr:\n", nam->m_len);
256 		dump_isoaddr(siso);
257 	ENDDEBUG
258 	if (nam->m_len < siso->siso_len)
259 		return EINVAL;
260 	if (siso->siso_family != AF_ISO)
261 		return EAFNOSUPPORT;
262 	if (siso->siso_nlen == 0) {
263 		if (ia = iso_ifaddr) {
264 			int nlen = ia->ia_addr.siso_nlen;
265 			ovbcopy(TSEL(siso), nlen + TSEL(siso),
266 				siso->siso_plen + siso->siso_tlen + siso->siso_slen);
267 			bcopy((caddr_t)&ia->ia_addr.siso_addr,
268 				  (caddr_t)&siso->siso_addr, nlen + 1);
269 			/* includes siso->siso_nlen = nlen; */
270 		} else
271 			return EADDRNOTAVAIL;
272 	}
273 	/*
274 	 * Local zero means either not bound, or bound to a TSEL, but no
275 	 * particular local interface.  So, if we want to send somebody
276 	 * we need to choose a return address.
277 	 */
278 	local_zero =
279 		((isop->isop_laddr == 0) || (isop->isop_laddr->siso_nlen == 0));
280 	if (local_zero) {
281 		int flags;
282 
283 		IFDEBUG(D_ISO)
284 			printf("iso_pcbconnect localzero 1\n");
285 		ENDDEBUG
286 		/*
287 		 * If route is known or can be allocated now,
288 		 * our src addr is taken from the i/f, else punt.
289 		 */
290 		flags = isop->isop_socket->so_options & SO_DONTROUTE;
291 		if (error = clnp_route(&siso->siso_addr, &isop->isop_route, flags,
292 						(struct sockaddr **)0, &ia))
293 			return error;
294 		IFDEBUG(D_ISO)
295 			printf("iso_pcbconnect localzero 2, ro->ro_rt 0x%x",
296 				isop->isop_route.ro_rt);
297 			printf(" ia 0x%x\n", ia);
298 		ENDDEBUG
299 	}
300 	IFDEBUG(D_ISO)
301 		printf("in iso_pcbconnect before lookup isop 0x%x isop->sock 0x%x\n",
302 			isop, isop->isop_socket);
303 	ENDDEBUG
304 	if (local_zero) {
305 		int nlen, tlen, totlen; caddr_t oldtsel, newtsel;
306 		siso = isop->isop_laddr;
307 		if (siso == 0 || siso->siso_tlen == 0)
308 			(void)iso_pcbbind(isop, (struct mbuf *)0);
309 		/*
310 		 * Here we have problem of squezeing in a definite network address
311 		 * into an existing sockaddr_iso, which in fact may not have room
312 		 * for it.  This gets messy.
313 		 */
314 		siso = isop->isop_laddr;
315 		oldtsel = TSEL(siso);
316 		tlen = siso->siso_tlen;
317 		nlen = ia->ia_addr.siso_nlen;
318 		totlen = tlen + nlen + _offsetof(struct sockaddr_iso, siso_data[0]);
319 		if ((siso == &isop->isop_sladdr) &&
320 			(totlen > sizeof(isop->isop_sladdr))) {
321 			struct mbuf *m = m_get(MT_SONAME, M_DONTWAIT);
322 			if (m == 0)
323 					return ENOBUFS;
324 			m->m_len = totlen;
325 			isop->isop_laddr = siso = mtod(m, struct sockaddr_iso *);
326 		}
327 		siso->siso_nlen = ia->ia_addr.siso_nlen;
328 		newtsel = TSEL(siso);
329 		ovbcopy(oldtsel, newtsel, tlen);
330 		bcopy(ia->ia_addr.siso_data, siso->siso_data, nlen);
331 		siso->siso_tlen = tlen;
332 		siso->siso_family = AF_ISO;
333 		siso->siso_len = totlen;
334 		siso = mtod(nam, struct sockaddr_iso *);
335 	}
336 	IFDEBUG(D_ISO)
337 		printf("in iso_pcbconnect before bcopy isop 0x%x isop->sock 0x%x\n",
338 			isop, isop->isop_socket);
339 	ENDDEBUG
340 	/*
341 	 * If we had to allocate space to a previous big foreign address,
342 	 * and for some reason we didn't free it, we reuse it knowing
343 	 * that is going to be big enough, as sockaddrs are delivered in
344 	 * 128 byte mbufs.
345 	 * If the foreign address is small enough, we use default space;
346 	 * otherwise, we grab an mbuf to copy into.
347 	 */
348 	if (isop->isop_faddr == 0 || isop->isop_faddr == &isop->isop_sfaddr) {
349 		if (siso->siso_len <= sizeof(isop->isop_sfaddr))
350 			isop->isop_faddr = &isop->isop_sfaddr;
351 		else {
352 			struct mbuf *m = m_get(MT_SONAME, M_DONTWAIT);
353 			if (m == 0)
354 				return ENOBUFS;
355 			isop->isop_faddr = mtod(m, struct sockaddr_iso *);
356 		}
357 	}
358 	bcopy((caddr_t)siso, (caddr_t)isop->isop_faddr, siso->siso_len);
359 	IFDEBUG(D_ISO)
360 		printf("in iso_pcbconnect after bcopy isop 0x%x isop->sock 0x%x\n",
361 			isop, isop->isop_socket);
362 		printf("iso_pcbconnect connected to addr:\n");
363 		dump_isoaddr(isop->isop_faddr);
364 		printf("iso_pcbconnect end: src addr:\n");
365 		dump_isoaddr(isop->isop_laddr);
366 	ENDDEBUG
367 	return 0;
368 }
369 
370 /*
371  * FUNCTION:		iso_pcbdisconnect()
372  *
373  * PURPOSE:			washes away the peer address info so the socket
374  *					appears to be disconnected.
375  *					If there's no file descriptor associated with the socket
376  *					it detaches the pcb.
377  *
378  * RETURNS:			Nada.
379  *
380  * SIDE EFFECTS:	May detach the pcb.
381  *
382  * NOTES:
383  */
384 void
385 iso_pcbdisconnect(isop)
386 	struct isopcb *isop;
387 {
388 	void iso_pcbdetach();
389 	register struct sockaddr_iso *siso;
390 
391 	IFDEBUG(D_ISO)
392 		printf("iso_pcbdisconnect(isop 0x%x)\n", isop);
393 	ENDDEBUG
394 	/*
395 	 * Preserver binding infnormation if already bound.
396 	 */
397 	if ((siso = isop->isop_laddr) && siso->siso_nlen && siso->siso_tlen) {
398 		caddr_t otsel = TSEL(siso);
399 		siso->siso_nlen = 0;
400 		ovbcopy(otsel, TSEL(siso), siso->siso_tlen);
401 	}
402 	if (isop->isop_faddr && isop->isop_faddr != &isop->isop_sfaddr)
403 		m_freem(dtom(isop->isop_faddr));
404 	isop->isop_faddr = 0;
405 	if (isop->isop_socket->so_state & SS_NOFDREF)
406 		iso_pcbdetach(isop);
407 }
408 
409 /*
410  * FUNCTION:		iso_pcbdetach
411  *
412  * PURPOSE:			detach the pcb at *(isop) from it's socket and free
413  *					the mbufs associated with the pcb..
414  *					Dequeues (isop) from its head.
415  *
416  * RETURNS:			Nada.
417  *
418  * SIDE EFFECTS:
419  *
420  * NOTES:
421  */
422 void
423 iso_pcbdetach(isop)
424 	struct isopcb *isop;
425 {
426 	struct socket *so = isop->isop_socket;
427 
428 	IFDEBUG(D_ISO)
429 		printf("iso_pcbdetach(isop 0x%x socket 0x%x so 0x%x)\n",
430 			isop, isop->isop_socket, so);
431 	ENDDEBUG
432 #ifdef TPCONS
433 	if (isop->isop_chan) {
434 		register struct pklcd *lcp = (struct pklcd *)isop->isop_chan;
435 		if (--isop->isop_refcnt > 0)
436 			return;
437 		if (lcp && lcp->lcd_state == DATA_TRANSFER) {
438 			lcp->lcd_upper = 0;
439 			lcp->lcd_upnext = 0;
440 			pk_disconnect(lcp);
441 		}
442 		isop->isop_chan = 0;
443 	}
444 #endif
445 	if (so) { /* in the x.25 domain, we sometimes have no socket */
446 		so->so_pcb = 0;
447 		sofree(so);
448 	}
449 	IFDEBUG(D_ISO)
450 		printf("iso_pcbdetach 2 \n");
451 	ENDDEBUG
452 	if (isop->isop_options)
453 		(void)m_free(isop->isop_options);
454 	IFDEBUG(D_ISO)
455 		printf("iso_pcbdetach 3 \n");
456 	ENDDEBUG
457 	if (isop->isop_route.ro_rt)
458 		rtfree(isop->isop_route.ro_rt);
459 	IFDEBUG(D_ISO)
460 		printf("iso_pcbdetach 3.1\n");
461 	ENDDEBUG
462 	if (isop->isop_clnpcache != NULL) {
463 		struct clnp_cache *clcp =
464 			mtod(isop->isop_clnpcache, struct clnp_cache *);
465 		IFDEBUG(D_ISO)
466 			printf("iso_pcbdetach 3.2: clcp 0x%x freeing clc_hdr x%x\n",
467 				clcp, clcp->clc_hdr);
468 		ENDDEBUG
469 		if (clcp->clc_hdr != NULL)
470 			m_free(clcp->clc_hdr);
471 		IFDEBUG(D_ISO)
472 			printf("iso_pcbdetach 3.3: freeing cache x%x\n",
473 				isop->isop_clnpcache);
474 		ENDDEBUG
475 		m_free(isop->isop_clnpcache);
476 	}
477 	IFDEBUG(D_ISO)
478 		printf("iso_pcbdetach 4 \n");
479 	ENDDEBUG
480 	remque(isop);
481 	IFDEBUG(D_ISO)
482 		printf("iso_pcbdetach 5 \n");
483 	ENDDEBUG
484 	if (isop->isop_laddr && (isop->isop_laddr != &isop->isop_sladdr))
485 		m_freem(dtom(isop->isop_laddr));
486 	free((caddr_t)isop, M_PCB);
487 }
488 
489 
490 /*
491  * FUNCTION:		iso_pcbnotify
492  *
493  * PURPOSE:			notify all connections in this protocol's queue (head)
494  *					that have peer address (dst) of the problem (errno)
495  *					by calling (notify) on the connections' isopcbs.
496  *
497  * RETURNS:			Rien.
498  *
499  * SIDE EFFECTS:
500  *
501  * NOTES:			(notify) is called at splimp!
502  */
503 void
504 iso_pcbnotify(head, siso, errno, notify)
505 	struct isopcb *head;
506 	register struct sockaddr_iso *siso;
507 	int errno, (*notify)();
508 {
509 	register struct isopcb *isop;
510 	int s = splimp();
511 
512 	IFDEBUG(D_ISO)
513 		printf("iso_pcbnotify(head 0x%x, notify 0x%x) dst:\n", head, notify);
514 	ENDDEBUG
515 	for (isop = head->isop_next; isop != head; isop = isop->isop_next) {
516 		if (isop->isop_socket == 0 || isop->isop_faddr == 0 ||
517 			!SAME_ISOADDR(siso, isop->isop_faddr)) {
518 			IFDEBUG(D_ISO)
519 				printf("iso_pcbnotify: CONTINUE isop 0x%x, sock 0x%x\n" ,
520 					isop, isop->isop_socket);
521 				printf("addrmatch cmp'd with (0x%x):\n", isop->isop_faddr);
522 				dump_isoaddr(isop->isop_faddr);
523 			ENDDEBUG
524 			continue;
525 		}
526 		if (errno)
527 			isop->isop_socket->so_error = errno;
528 		if (notify)
529 			(*notify)(isop);
530 	}
531 	splx(s);
532 	IFDEBUG(D_ISO)
533 		printf("END OF iso_pcbnotify\n" );
534 	ENDDEBUG
535 }
536 
537 
538 /*
539  * FUNCTION:		iso_pcblookup
540  *
541  * PURPOSE:			looks for a given combination of (faddr), (fport),
542  *					(lport), (laddr) in the queue named by (head).
543  *					Argument (flags) is ignored.
544  *
545  * RETURNS:			ptr to the isopcb if it finds a connection matching
546  *					these arguments, o.w. returns zero.
547  *
548  * SIDE EFFECTS:
549  *
550  * NOTES:
551  */
552 struct isopcb *
553 iso_pcblookup(head, fportlen, fport, laddr)
554 	struct isopcb *head;
555 	register struct sockaddr_iso *laddr;
556 	caddr_t fport;
557 	int fportlen;
558 {
559 	register struct isopcb *isop;
560 	register caddr_t lp = TSEL(laddr);
561 	unsigned int llen = laddr->siso_tlen;
562 
563 	IFDEBUG(D_ISO)
564 		printf("iso_pcblookup(head 0x%x laddr 0x%x fport 0x%x)\n",
565 			head, laddr, fport);
566 	ENDDEBUG
567 	for (isop = head->isop_next; isop != head; isop = isop->isop_next) {
568 		if (isop->isop_laddr == 0 || isop->isop_laddr == laddr)
569 			continue;
570 		if (isop->isop_laddr->siso_tlen != llen)
571 			continue;
572 		if (bcmp(lp, TSEL(isop->isop_laddr), llen))
573 			continue;
574 		if (fportlen && isop->isop_faddr &&
575 			bcmp(fport, TSEL(isop->isop_faddr), (unsigned)fportlen))
576 			continue;
577 		/*	PHASE2
578 		 *	addrmatch1 should be iso_addrmatch(a, b, mask)
579 		 *	where mask is taken from isop->isop_laddrmask (new field)
580 		 *	isop_lnetmask will also be available in isop
581 		if (laddr != &zeroiso_addr &&
582 			!iso_addrmatch1(laddr, &(isop->isop_laddr.siso_addr)))
583 			continue;
584 		*/
585 		if (laddr->siso_nlen && (!SAME_ISOADDR(laddr, isop->isop_laddr)))
586 			continue;
587 		return (isop);
588 	}
589 	return (struct isopcb *)0;
590 }
591 #endif /* ISO */
592