xref: /original-bsd/sys/netiso/iso_pcb.c (revision 4c3b28fe)
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.7 (Berkeley) 04/16/90
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 || siso->siso_tlen != 2) {
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 	register struct sockaddr_iso *siso;
379 
380 	IFDEBUG(D_ISO)
381 		printf("iso_pcbdisconnect(isop 0x%x)\n", isop);
382 	ENDDEBUG
383 	/*
384 	 * Preserver binding infnormation if already bound.
385 	 */
386 	if ((siso = isop->isop_laddr) && siso->siso_nlen && siso->siso_tlen) {
387 		caddr_t otsel = TSEL(siso);
388 		siso->siso_nlen = 0;
389 		ovbcopy(otsel, TSEL(siso), siso->siso_tlen);
390 	}
391 	if (isop->isop_faddr && isop->isop_faddr != &isop->isop_sfaddr)
392 		m_freem(dtom(isop->isop_faddr));
393 	isop->isop_faddr = 0;
394 	if (isop->isop_socket->so_state & SS_NOFDREF)
395 		iso_pcbdetach(isop);
396 }
397 
398 /*
399  * FUNCTION:		iso_pcbdetach
400  *
401  * PURPOSE:			detach the pcb at *(isop) from it's socket and free
402  *					the mbufs associated with the pcb..
403  *					Dequeues (isop) from its head.
404  *
405  * RETURNS:			Nada.
406  *
407  * SIDE EFFECTS:
408  *
409  * NOTES:
410  */
411 void
412 iso_pcbdetach(isop)
413 	struct isopcb *isop;
414 {
415 	struct socket *so = isop->isop_socket;
416 
417 	IFDEBUG(D_ISO)
418 		printf("iso_pcbdetach(isop 0x%x socket 0x%x so 0x%x)\n",
419 			isop, isop->isop_socket, so);
420 	ENDDEBUG
421 	if (so ) { /* in the x.25 domain, we sometimes have no socket */
422 		so->so_pcb = 0;
423 		sofree(so);
424 	}
425 	IFDEBUG(D_ISO)
426 		printf("iso_pcbdetach 2 \n");
427 	ENDDEBUG
428 	if (isop->isop_options)
429 		(void)m_free(isop->isop_options);
430 	IFDEBUG(D_ISO)
431 		printf("iso_pcbdetach 3 \n");
432 	ENDDEBUG
433 	if (isop->isop_route.ro_rt)
434 		rtfree(isop->isop_route.ro_rt);
435 	IFDEBUG(D_ISO)
436 		printf("iso_pcbdetach 3.1\n");
437 	ENDDEBUG
438 	if (isop->isop_clnpcache != NULL) {
439 		struct clnp_cache *clcp =
440 			mtod(isop->isop_clnpcache, struct clnp_cache *);
441 		IFDEBUG(D_ISO)
442 			printf("iso_pcbdetach 3.2: clcp 0x%x freeing clc_hdr x%x\n",
443 				clcp, clcp->clc_hdr);
444 		ENDDEBUG
445 		if (clcp->clc_hdr != NULL)
446 			m_free(clcp->clc_hdr);
447 		IFDEBUG(D_ISO)
448 			printf("iso_pcbdetach 3.3: freeing cache x%x\n",
449 				isop->isop_clnpcache);
450 		ENDDEBUG
451 		m_free(isop->isop_clnpcache);
452 	}
453 	IFDEBUG(D_ISO)
454 		printf("iso_pcbdetach 4 \n");
455 	ENDDEBUG
456 	remque(isop);
457 	IFDEBUG(D_ISO)
458 		printf("iso_pcbdetach 5 \n");
459 	ENDDEBUG
460 	if (isop->isop_laddr && (isop->isop_laddr != &isop->isop_sladdr))
461 		m_freem(dtom(isop->isop_laddr));
462 	free((caddr_t)isop, M_PCB);
463 }
464 
465 
466 /*
467  * FUNCTION:		iso_pcbnotify
468  *
469  * PURPOSE:			notify all connections in this protocol's queue (head)
470  *					that have peer address (dst) of the problem (errno)
471  *					by calling (notify) on the connections' isopcbs.
472  *
473  * RETURNS:			Rien.
474  *
475  * SIDE EFFECTS:
476  *
477  * NOTES:			(notify) is called at splimp!
478  */
479 void
480 iso_pcbnotify(head, siso, errno, notify)
481 	struct isopcb *head;
482 	register struct sockaddr_iso *siso;
483 	int errno, (*notify)();
484 {
485 	register struct isopcb *isop;
486 	int s = splimp();
487 
488 	IFDEBUG(D_ISO)
489 		printf("iso_pcbnotify(head 0x%x, notify 0x%x) dst:\n", head, notify);
490 	ENDDEBUG
491 	for (isop = head->isop_next; isop != head; isop = isop->isop_next) {
492 		if (isop->isop_socket == 0 || isop->isop_faddr == 0 ||
493 			!SAME_ISOADDR(siso, isop->isop_faddr)) {
494 			IFDEBUG(D_ISO)
495 				printf("iso_pcbnotify: CONTINUE isop 0x%x, sock 0x%x\n" ,
496 					isop, isop->isop_socket);
497 				printf("addrmatch cmp'd with (0x%x):\n", isop->isop_faddr);
498 				dump_isoaddr(isop->isop_faddr);
499 			ENDDEBUG
500 			continue;
501 		}
502 		if (errno)
503 			isop->isop_socket->so_error = errno;
504 		if (notify)
505 			(*notify)(isop);
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