xref: /original-bsd/sys/netiso/iso_pcb.c (revision a91856c6)
1 /*-
2  * Copyright (c) 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)iso_pcb.c	7.9 (Berkeley) 05/06/91
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 "param.h"
46 #include "systm.h"
47 #include "user.h"
48 #include "mbuf.h"
49 #include "socket.h"
50 #include "socketvar.h"
51 #include "argo_debug.h"
52 #include "iso.h"
53 #include "clnp.h"
54 #include "../netinet/in_systm.h"
55 #include "../net/if.h"
56 #include "../net/route.h"
57 #include "iso_pcb.h"
58 #include "iso_var.h"
59 #include "protosw.h"
60 
61 #ifdef TPCONS
62 #include "../netccitt/x25.h"
63 #include "../netccitt/pk.h"
64 #include "../netccitt/pk_var.h"
65 #endif
66 
67 #define PCBNULL (struct isopcb *)0
68 struct	iso_addr zeroiso_addr = {
69 	0
70 };
71 
72 
73 /*
74  * FUNCTION:		iso_pcballoc
75  *
76  * PURPOSE:			creates an isopcb structure in an mbuf,
77  *					with socket (so), and
78  *					puts it in the queue with head (head)
79  *
80  * RETURNS:			0 if OK, ENOBUFS if can't alloc the necessary mbuf
81  */
82 int
83 iso_pcballoc(so, head)
84 	struct socket *so;
85 	struct isopcb *head;
86 {
87 	register struct isopcb *isop;
88 
89 	IFDEBUG(D_ISO)
90 		printf("iso_pcballoc(so 0x%x)\n", so);
91 	ENDDEBUG
92 	MALLOC(isop, struct isopcb *, sizeof(*isop), M_PCB, M_NOWAIT);
93 	if (isop == NULL)
94 		return ENOBUFS;
95 	bzero((caddr_t)isop, sizeof(*isop));
96 	isop->isop_head = head;
97 	isop->isop_socket = so;
98 	insque(isop, head);
99 	if (so)
100 		so->so_pcb = (caddr_t)isop;
101 	return 0;
102 }
103 
104 /*
105  * FUNCTION:		iso_pcbbind
106  *
107  * PURPOSE:			binds the address given in *(nam) to the socket
108  *					specified by the isopcb in *(isop)
109  *					If the given address is zero, it makes sure the
110  *					address isn't already in use and if it's got a network
111  *					portion, we look for an interface with that network
112  *					address.  If the address given is zero, we allocate
113  *					a port and stuff it in the (nam) structure.
114  *
115  * RETURNS:			errno E* or 0 if ok.
116  *
117  * SIDE EFFECTS:	increments head->isop_lport if it allocates a port #
118  *
119  * NOTES:
120  */
121 #define	satosiso(sa)	((struct sockaddr_iso *)(sa))
122 int
123 iso_pcbbind(isop, nam)
124 	register struct isopcb *isop;
125 	struct mbuf *nam;
126 {
127 	register struct isopcb *head = isop->isop_head;
128 	register struct sockaddr_iso *siso;
129 	struct iso_ifaddr *ia;
130 	union {
131 		char data[2];
132 		u_short s;
133 	} suf;
134 
135 	IFDEBUG(D_ISO)
136 		printf("iso_pcbbind(isop 0x%x, nam 0x%x)\n", isop, nam);
137 	ENDDEBUG
138 	suf.s = 0;
139 	if (iso_ifaddr == 0) /* any interfaces attached? */
140 		return EADDRNOTAVAIL;
141 	if (isop->isop_laddr)  /* already bound */
142 		return EADDRINUSE;
143 	if(nam == (struct mbuf *)0) {
144 		isop->isop_laddr = &isop->isop_sladdr;
145 		isop->isop_sladdr.siso_len = sizeof(struct sockaddr_iso);
146 		isop->isop_sladdr.siso_family = AF_ISO;
147 		isop->isop_sladdr.siso_tlen = 2;
148 		isop->isop_sladdr.siso_nlen = 0;
149 		isop->isop_sladdr.siso_slen = 0;
150 		isop->isop_sladdr.siso_plen = 0;
151 		goto noname;
152 	}
153 	siso = mtod(nam, struct sockaddr_iso *);
154 	IFDEBUG(D_ISO)
155 		printf("iso_pcbbind(name len 0x%x)\n", nam->m_len);
156 		printf("The address is %s\n", clnp_iso_addrp(&siso->siso_addr));
157 	ENDDEBUG
158 	/*
159 	 * We would like sort of length check but since some OSI addrs
160 	 * do not have fixed length, we can't really do much.
161 	 * The ONLY thing we can say is that an osi addr has to have
162 	 * at LEAST an afi and one more byte and had better fit into
163 	 * a struct iso_addr.
164 	 * However, in fact the size of the whole thing is a struct
165 	 * sockaddr_iso, so probably this is what we should check for.
166 	 */
167 	if( (nam->m_len < 2) || (nam->m_len < siso->siso_len)) {
168 			return ENAMETOOLONG;
169 	}
170 	if (siso->siso_tlen) {
171 			register char *cp = TSEL(siso);
172 			suf.data[0] = cp[0];
173 			suf.data[1] = cp[1];
174 	}
175 	if (siso->siso_nlen) {
176 		/* non-zero net addr- better match one of our interfaces */
177 		IFDEBUG(D_ISO)
178 			printf("iso_pcbbind: bind to NOT zeroisoaddr\n");
179 		ENDDEBUG
180 		for (ia = iso_ifaddr; ia; ia = ia->ia_next)
181 			if (SAME_ISOADDR(siso, &ia->ia_addr))
182 				break;
183 		if (ia == 0)
184 			return EADDRNOTAVAIL;
185 	}
186 	if (siso->siso_len <= sizeof (isop->isop_sladdr)) {
187 		isop->isop_laddr = &isop->isop_sladdr;
188 	} else {
189 		if ((nam = m_copy(nam, 0, (int)M_COPYALL)) == 0)
190 			return ENOBUFS;
191 		isop->isop_laddr = mtod(nam, struct sockaddr_iso *);
192 	}
193 	bcopy((caddr_t)siso, (caddr_t)isop->isop_laddr, siso->siso_len);
194 	if (suf.s || siso->siso_tlen != 2) {
195 		if((suf.s < ISO_PORT_RESERVED) && (siso->siso_tlen <= 2) &&
196 		   (u.u_uid != 0))
197 			return EACCES;
198 		if ((isop->isop_socket->so_options & SO_REUSEADDR) == 0 &&
199 			iso_pcblookup(head, 0, (caddr_t)0, isop->isop_laddr))
200 			return EADDRINUSE;
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 = 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_refcnt) {
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 			pk_disconnect(lcp);
439 		isop->isop_chan = 0;
440 	}
441 #endif
442 	if (so) { /* in the x.25 domain, we sometimes have no socket */
443 		so->so_pcb = 0;
444 		sofree(so);
445 	}
446 	IFDEBUG(D_ISO)
447 		printf("iso_pcbdetach 2 \n");
448 	ENDDEBUG
449 	if (isop->isop_options)
450 		(void)m_free(isop->isop_options);
451 	IFDEBUG(D_ISO)
452 		printf("iso_pcbdetach 3 \n");
453 	ENDDEBUG
454 	if (isop->isop_route.ro_rt)
455 		rtfree(isop->isop_route.ro_rt);
456 	IFDEBUG(D_ISO)
457 		printf("iso_pcbdetach 3.1\n");
458 	ENDDEBUG
459 	if (isop->isop_clnpcache != NULL) {
460 		struct clnp_cache *clcp =
461 			mtod(isop->isop_clnpcache, struct clnp_cache *);
462 		IFDEBUG(D_ISO)
463 			printf("iso_pcbdetach 3.2: clcp 0x%x freeing clc_hdr x%x\n",
464 				clcp, clcp->clc_hdr);
465 		ENDDEBUG
466 		if (clcp->clc_hdr != NULL)
467 			m_free(clcp->clc_hdr);
468 		IFDEBUG(D_ISO)
469 			printf("iso_pcbdetach 3.3: freeing cache x%x\n",
470 				isop->isop_clnpcache);
471 		ENDDEBUG
472 		m_free(isop->isop_clnpcache);
473 	}
474 	IFDEBUG(D_ISO)
475 		printf("iso_pcbdetach 4 \n");
476 	ENDDEBUG
477 	remque(isop);
478 	IFDEBUG(D_ISO)
479 		printf("iso_pcbdetach 5 \n");
480 	ENDDEBUG
481 	if (isop->isop_laddr && (isop->isop_laddr != &isop->isop_sladdr))
482 		m_freem(dtom(isop->isop_laddr));
483 	free((caddr_t)isop, M_PCB);
484 }
485 
486 
487 /*
488  * FUNCTION:		iso_pcbnotify
489  *
490  * PURPOSE:			notify all connections in this protocol's queue (head)
491  *					that have peer address (dst) of the problem (errno)
492  *					by calling (notify) on the connections' isopcbs.
493  *
494  * RETURNS:			Rien.
495  *
496  * SIDE EFFECTS:
497  *
498  * NOTES:			(notify) is called at splimp!
499  */
500 void
501 iso_pcbnotify(head, siso, errno, notify)
502 	struct isopcb *head;
503 	register struct sockaddr_iso *siso;
504 	int errno, (*notify)();
505 {
506 	register struct isopcb *isop;
507 	int s = splimp();
508 
509 	IFDEBUG(D_ISO)
510 		printf("iso_pcbnotify(head 0x%x, notify 0x%x) dst:\n", head, notify);
511 	ENDDEBUG
512 	for (isop = head->isop_next; isop != head; isop = isop->isop_next) {
513 		if (isop->isop_socket == 0 || isop->isop_faddr == 0 ||
514 			!SAME_ISOADDR(siso, isop->isop_faddr)) {
515 			IFDEBUG(D_ISO)
516 				printf("iso_pcbnotify: CONTINUE isop 0x%x, sock 0x%x\n" ,
517 					isop, isop->isop_socket);
518 				printf("addrmatch cmp'd with (0x%x):\n", isop->isop_faddr);
519 				dump_isoaddr(isop->isop_faddr);
520 			ENDDEBUG
521 			continue;
522 		}
523 		if (errno)
524 			isop->isop_socket->so_error = errno;
525 		if (notify)
526 			(*notify)(isop);
527 	}
528 	splx(s);
529 	IFDEBUG(D_ISO)
530 		printf("END OF iso_pcbnotify\n" );
531 	ENDDEBUG
532 }
533 
534 
535 /*
536  * FUNCTION:		iso_pcblookup
537  *
538  * PURPOSE:			looks for a given combination of (faddr), (fport),
539  *					(lport), (laddr) in the queue named by (head).
540  *					Argument (flags) is ignored.
541  *
542  * RETURNS:			ptr to the isopcb if it finds a connection matching
543  *					these arguments, o.w. returns zero.
544  *
545  * SIDE EFFECTS:
546  *
547  * NOTES:
548  */
549 struct isopcb *
550 iso_pcblookup(head, fportlen, fport, laddr)
551 	struct isopcb *head;
552 	register struct sockaddr_iso *laddr;
553 	caddr_t fport;
554 	int fportlen;
555 {
556 	register struct isopcb *isop;
557 	register caddr_t lp = TSEL(laddr);
558 	unsigned int llen = laddr->siso_tlen;
559 
560 	IFDEBUG(D_ISO)
561 		printf("iso_pcblookup(head 0x%x laddr 0x%x fport 0x%x)\n",
562 			head, laddr, fport);
563 	ENDDEBUG
564 	for (isop = head->isop_next; isop != head; isop = isop->isop_next) {
565 		if (isop->isop_laddr == 0 || isop->isop_laddr == laddr)
566 			continue;
567 		if (isop->isop_laddr->siso_tlen != llen)
568 			continue;
569 		if (bcmp(lp, TSEL(isop->isop_laddr), llen))
570 			continue;
571 		if (fportlen && isop->isop_faddr &&
572 			bcmp(fport, TSEL(isop->isop_faddr), (unsigned)fportlen))
573 			continue;
574 		/*	PHASE2
575 		 *	addrmatch1 should be iso_addrmatch(a, b, mask)
576 		 *	where mask is taken from isop->isop_laddrmask (new field)
577 		 *	isop_lnetmask will also be available in isop
578 		if (laddr != &zeroiso_addr &&
579 			!iso_addrmatch1(laddr, &(isop->isop_laddr.siso_addr)))
580 			continue;
581 		*/
582 		if (laddr->siso_nlen && (!SAME_ISOADDR(laddr, isop->isop_laddr)))
583 			continue;
584 		return (isop);
585 	}
586 	return (struct isopcb *)0;
587 }
588 #endif ISO
589