1/*	$NetBSD: iso_pcb.c,v 1.48 2009/04/16 21:37:17 elad Exp $	*/
2
3/*-
4 * Copyright (c) 1991, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 *	@(#)iso_pcb.c	8.3 (Berkeley) 7/19/94
32 */
33
34/***********************************************************
35		Copyright IBM Corporation 1987
36
37                      All Rights Reserved
38
39Permission to use, copy, modify, and distribute this software and its
40documentation for any purpose and without fee is hereby granted,
41provided that the above copyright notice appear in all copies and that
42both that copyright notice and this permission notice appear in
43supporting documentation, and that the name of IBM not be
44used in advertising or publicity pertaining to distribution of the
45software without specific, written prior permission.
46
47IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
48ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
49IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
50ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
51WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
52ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
53SOFTWARE.
54
55******************************************************************/
56
57/*
58 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
59 */
60/*
61 * Iso address family net-layer(s) pcb stuff. NEH 1/29/87
62 */
63
64#include <sys/cdefs.h>
65__KERNEL_RCSID(0, "$NetBSD: iso_pcb.c,v 1.48 2009/04/16 21:37:17 elad Exp $");
66
67#include "opt_iso.h"
68
69#ifdef ISO
70
71#include <sys/param.h>
72#include <sys/systm.h>
73#include <sys/mbuf.h>
74#include <sys/socket.h>
75#include <sys/socketvar.h>
76#include <sys/errno.h>
77#include <sys/proc.h>
78#include <sys/kauth.h>
79
80#include <netiso/argo_debug.h>
81#include <netiso/iso.h>
82#include <netiso/clnp.h>
83#include <netinet/in_systm.h>
84#include <net/if.h>
85#include <net/route.h>
86#include <netiso/iso_pcb.h>
87#include <netiso/iso_var.h>
88#include <sys/protosw.h>
89
90const struct iso_addr zeroiso_addr;
91
92#ifdef ARGO_DEBUG
93unsigned char   argo_debug[128];
94#endif
95
96/*
97 * FUNCTION:		iso_pcballoc
98 *
99 * PURPOSE:		creates an isopcb structure in an mbuf,
100 *			with socket (so), and
101 *			puts it in the queue with head (head)
102 *
103 * RETURNS:		0 if OK, ENOBUFS if can't alloc the necessary mbuf
104 */
105int
106iso_pcballoc(struct socket *so, void *v)
107{
108	struct isopcb  *head = v;
109	struct isopcb *isop;
110
111#ifdef ARGO_DEBUG
112	if (argo_debug[D_ISO]) {
113		printf("iso_pcballoc(so %p)\n", so);
114	}
115#endif
116	isop = malloc(sizeof(*isop), M_PCB, M_NOWAIT|M_ZERO);
117	if (isop == NULL)
118		return ENOBUFS;
119	isop->isop_head = head;
120	isop->isop_socket = so;
121	iso_insque(isop, head);
122	if (so)
123		so->so_pcb = isop;
124	return 0;
125}
126
127/*
128 * FUNCTION:		iso_pcbbind
129 *
130 * PURPOSE:		binds the address given in *(nam) to the socket
131 *			specified by the isopcb in *(isop)
132 *			If the given address is zero, it makes sure the
133 *			address isn't already in use and if it's got a network
134 *			portion, we look for an interface with that network
135 *			address.  If the address given is zero, we allocate
136 *			a port and stuff it in the (nam) structure.
137 *
138 * RETURNS:		errno E* or 0 if ok.
139 *
140 * SIDE EFFECTS:	increments head->isop_lport if it allocates a port #
141 *
142 * NOTES:
143 */
144int
145iso_pcbbind(void *v, struct mbuf *nam, struct lwp *l)
146{
147	struct isopcb *isop = v;
148	struct isopcb *head = isop->isop_head;
149	struct sockaddr_iso *siso;
150	struct iso_ifaddr *ia;
151	union {
152		char            data[2];
153		u_short         s;
154	} suf;
155
156#ifdef ARGO_DEBUG
157	if (argo_debug[D_ISO]) {
158		printf("iso_pcbbind(isop %p, nam %p)\n", isop, nam);
159	}
160#endif
161	suf.s = 0;
162	if (iso_ifaddr.tqh_first == 0)	/* any interfaces attached? */
163		return EADDRNOTAVAIL;
164	if (isop->isop_laddr)	/* already bound */
165		return EADDRINUSE;
166	if (nam == (struct mbuf *) 0) {
167		isop->isop_laddr = &isop->isop_sladdr;
168		isop->isop_sladdr.siso_len = sizeof(struct sockaddr_iso);
169		isop->isop_sladdr.siso_family = AF_ISO;
170		isop->isop_sladdr.siso_tlen = 2;
171		isop->isop_sladdr.siso_nlen = 0;
172		isop->isop_sladdr.siso_slen = 0;
173		isop->isop_sladdr.siso_plen = 0;
174		goto noname;
175	}
176	siso = mtod(nam, struct sockaddr_iso *);
177#ifdef ARGO_DEBUG
178	if (argo_debug[D_ISO]) {
179		printf("iso_pcbbind(name len 0x%x)\n", nam->m_len);
180		printf("The address is %s\n", clnp_iso_addrp(&siso->siso_addr));
181	}
182#endif
183	/*
184	 * We would like sort of length check but since some OSI addrs
185	 * do not have fixed length, we can't really do much.
186	 * The ONLY thing we can say is that an osi addr has to have
187	 * at LEAST an afi and one more byte and had better fit into
188	 * a struct iso_addr.
189	 * However, in fact the size of the whole thing is a struct
190	 * sockaddr_iso, so probably this is what we should check for.
191	 */
192	if ((nam->m_len < 2) || (nam->m_len < siso->siso_len)) {
193		return ENAMETOOLONG;
194	}
195	if (siso->siso_nlen) {
196		/* non-zero net addr- better match one of our interfaces */
197#ifdef ARGO_DEBUG
198		if (argo_debug[D_ISO]) {
199			printf("iso_pcbbind: bind to NOT zeroisoaddr\n");
200		}
201#endif
202		for (ia = iso_ifaddr.tqh_first; ia != 0; ia = ia->ia_list.tqe_next)
203			if (SAME_ISOIFADDR(siso, &ia->ia_addr))
204				break;
205		if (ia == 0)
206			return EADDRNOTAVAIL;
207	}
208	if (siso->siso_len <= sizeof(isop->isop_sladdr)) {
209		isop->isop_laddr = &isop->isop_sladdr;
210	} else {
211		if ((nam = m_copy(nam, 0, (int) M_COPYALL)) == 0)
212			return ENOBUFS;
213		isop->isop_mladdr = nam;
214		isop->isop_laddr = mtod(nam, struct sockaddr_iso *);
215	}
216	memcpy((void *) isop->isop_laddr, (void *) siso, siso->siso_len);
217	if (siso->siso_tlen == 0)
218		goto noname;
219	if ((isop->isop_socket->so_options & SO_REUSEADDR) == 0 &&
220	    iso_pcblookup(head, 0, (void *) 0, isop->isop_laddr))
221		return EADDRINUSE;
222	if (siso->siso_tlen <= 2) {
223		memcpy(suf.data, TSEL(siso), sizeof(suf.data));
224		suf.s = ntohs(suf.s);
225		if (suf.s < ISO_PORT_RESERVED) {
226			int error;
227
228			if (l == NULL)
229				error = EACCES;
230			else
231				error = kauth_authorize_network(l->l_cred,
232				    KAUTH_NETWORK_BIND,
233				    KAUTH_REQ_NETWORK_BIND_PRIVPORT,
234				    isop->isop_socket, siso, NULL);
235
236			if (error)
237				return (error);
238		}
239	} else {
240		char  *cp;
241noname:
242		cp = WRITABLE_TSEL(isop->isop_laddr);
243		isop->isop_laddr->siso_tlen = 2;
244#ifdef ARGO_DEBUG
245		if (argo_debug[D_ISO]) {
246			printf("iso_pcbbind noname\n");
247		}
248#endif
249		do {
250			if (head->isop_lport++ < ISO_PORT_RESERVED ||
251			    head->isop_lport > ISO_PORT_USERRESERVED)
252				head->isop_lport = ISO_PORT_RESERVED;
253			suf.s = htons(head->isop_lport);
254			cp[0] = suf.data[0];
255			cp[1] = suf.data[1];
256		} while (iso_pcblookup(head, 0, (void *) 0, isop->isop_laddr));
257	}
258#ifdef ARGO_DEBUG
259	if (argo_debug[D_ISO]) {
260		printf("iso_pcbbind returns 0, suf 0x%x\n", suf.s);
261	}
262#endif
263	return 0;
264}
265/*
266 * FUNCTION:		iso_pcbconnect
267 *
268 * PURPOSE:		Make the isopcb (isop) look like it's connected.
269 *			In other words, give it the peer address given in
270 *			the mbuf * (nam).   Make sure such a combination
271 *			of local, peer addresses doesn't already exist
272 *			for this protocol.  Internet mentality prevails here,
273 *			wherein a src,dst pair uniquely identifies a connection.
274 * 			Both net address and port must be specified in argument
275 *			(nam).
276 * 			If we don't have a local address for this socket yet,
277 *			we pick one by calling iso_pcbbind().
278 *
279 * RETURNS:		errno E* or 0 if ok.
280 *
281 * SIDE EFFECTS:	Looks up a route, which may cause one to be left
282 *			in the isopcb.
283 *
284 * NOTES:
285 */
286int
287iso_pcbconnect(void *v, struct mbuf *nam, struct lwp *l)
288{
289	struct isopcb *isop = v;
290	struct sockaddr_iso *siso = mtod(nam, struct sockaddr_iso *);
291	int             local_zero, error = 0;
292	struct iso_ifaddr *ia;
293
294#ifdef ARGO_DEBUG
295	if (argo_debug[D_ISO]) {
296		printf("iso_pcbconnect(isop %p sock %p nam %p",
297		    isop, isop->isop_socket, nam);
298		printf("nam->m_len 0x%x), addr:\n", nam->m_len);
299		dump_isoaddr(siso);
300	}
301#endif
302	if (nam->m_len < siso->siso_len)
303		return EINVAL;
304	if (siso->siso_family != AF_ISO)
305		return EAFNOSUPPORT;
306	if (siso->siso_nlen == 0) {
307		if ((ia = iso_ifaddr.tqh_first) != NULL) {
308			int             nlen = ia->ia_addr.siso_nlen;
309			char *tmp;
310			tmp = WRITABLE_TSEL(siso);
311			memmove(tmp + nlen, TSEL(siso),
312			    siso->siso_plen + siso->siso_tlen +
313			    siso->siso_slen);
314			bcopy((void *) & ia->ia_addr.siso_addr,
315			      (void *) & siso->siso_addr, nlen + 1);
316			/* includes siso->siso_nlen = nlen; */
317		} else
318			return EADDRNOTAVAIL;
319	}
320	/*
321	 * Local zero means either not bound, or bound to a TSEL, but no
322	 * particular local interface.  So, if we want to send somebody
323	 * we need to choose a return address.
324	 */
325	local_zero =
326		((isop->isop_laddr == 0) || (isop->isop_laddr->siso_nlen == 0));
327	if (local_zero) {
328		int             flags;
329
330#ifdef ARGO_DEBUG
331		if (argo_debug[D_ISO]) {
332			printf("iso_pcbconnect localzero 1\n");
333		}
334#endif
335		/*
336		 * If route is known or can be allocated now, our src addr is
337		 * taken from the i/f, else punt.
338		 */
339		flags = isop->isop_socket->so_options & SO_DONTROUTE;
340		error = clnp_route(&siso->siso_addr, &isop->isop_route, flags,
341				   NULL, &ia);
342		if (error)
343			return error;
344#ifdef ARGO_DEBUG
345		if (argo_debug[D_ISO]) {
346			printf("iso_pcbconnect localzero 2, rt %p",
347			       rtcache_validate(&isop->isop_route));
348			printf(" ia %p\n", ia);
349		}
350#endif
351	}
352#ifdef ARGO_DEBUG
353	if (argo_debug[D_ISO]) {
354		printf("in iso_pcbconnect before lookup isop %p isop->sock %p\n",
355		       isop, isop->isop_socket);
356	}
357#endif
358	if (local_zero) {
359		int             nlen, tlen, totlen;
360		void *newtsel;
361		const void *oldtsel;
362		siso = isop->isop_laddr;
363		if (siso == 0 || siso->siso_tlen == 0)
364			(void) iso_pcbbind(isop, (struct mbuf *)0,
365			    (struct lwp *)0);
366		/*
367		 * Here we have problem of squezeing in a definite network address
368		 * into an existing sockaddr_iso, which in fact may not have room
369		 * for it.  This gets messy.
370		 */
371		siso = isop->isop_laddr;
372		oldtsel = TSEL(siso);
373		tlen = siso->siso_tlen;
374		nlen = ia->ia_addr.siso_nlen;
375		totlen = tlen + nlen + offsetof(struct sockaddr_iso, siso_data[0]);
376		if ((siso == &isop->isop_sladdr) &&
377		    (totlen > sizeof(isop->isop_sladdr))) {
378			struct mbuf    *m = m_get(M_DONTWAIT, MT_SONAME);
379			if (m == 0)
380				return ENOBUFS;
381			m->m_len = totlen;
382			isop->isop_mladdr = m;
383			isop->isop_laddr = siso = mtod(m, struct sockaddr_iso *);
384		}
385		siso->siso_nlen = ia->ia_addr.siso_nlen;
386		newtsel = WRITABLE_TSEL(siso);
387		memmove(newtsel, oldtsel, tlen);
388		memcpy(siso->siso_data, ia->ia_addr.siso_data, nlen);
389		siso->siso_tlen = tlen;
390		siso->siso_family = AF_ISO;
391		siso->siso_len = totlen;
392		siso = mtod(nam, struct sockaddr_iso *);
393	}
394#ifdef ARGO_DEBUG
395	if (argo_debug[D_ISO]) {
396		printf("in iso_pcbconnect before bcopy isop %p isop->sock %p\n",
397		    isop, isop->isop_socket);
398	}
399#endif
400	/*
401	 * If we had to allocate space to a previous big foreign address,
402	 * and for some reason we didn't free it, we reuse it knowing
403	 * that is going to be big enough, as sockaddrs are delivered in
404	 * 128 byte mbufs.
405	 * If the foreign address is small enough, we use default space;
406	 * otherwise, we grab an mbuf to copy into.
407	 */
408	if (isop->isop_faddr == 0 || isop->isop_faddr == &isop->isop_sfaddr) {
409		if (siso->siso_len <= sizeof(isop->isop_sfaddr))
410			isop->isop_faddr = &isop->isop_sfaddr;
411		else {
412			struct mbuf    *m = m_get(M_DONTWAIT, MT_SONAME);
413			if (m == 0)
414				return ENOBUFS;
415			isop->isop_mfaddr = m;
416			isop->isop_faddr = mtod(m, struct sockaddr_iso *);
417		}
418	}
419	memcpy((void *) isop->isop_faddr, (void *) siso, siso->siso_len);
420#ifdef ARGO_DEBUG
421	if (argo_debug[D_ISO]) {
422		printf("in iso_pcbconnect after bcopy isop %p isop->sock %p\n",
423		    isop, isop->isop_socket);
424		printf("iso_pcbconnect connected to addr:\n");
425		dump_isoaddr(isop->isop_faddr);
426		printf("iso_pcbconnect end: src addr:\n");
427		dump_isoaddr(isop->isop_laddr);
428	}
429#endif
430	return 0;
431}
432
433/*
434 * FUNCTION:		iso_pcbdisconnect()
435 *
436 * PURPOSE:		washes away the peer address info so the socket
437 *			appears to be disconnected.
438 *			If there's no file descriptor associated with the socket
439 *			it detaches the pcb.
440 *
441 * RETURNS:		Nada.
442 *
443 * SIDE EFFECTS:	May detach the pcb.
444 *
445 * NOTES:
446 */
447void
448iso_pcbdisconnect(void *v)
449{
450	struct isopcb  *isop = v;
451	struct sockaddr_iso *siso;
452
453#ifdef ARGO_DEBUG
454	if (argo_debug[D_ISO]) {
455		printf("iso_pcbdisconnect(isop %p)\n", isop);
456	}
457#endif
458	/*
459	 * Preserver binding infnormation if already bound.
460	 */
461	if ((siso = isop->isop_laddr) && siso->siso_nlen && siso->siso_tlen) {
462		const void *otsel = TSEL(siso);
463		siso->siso_nlen = 0;
464		memmove(WRITABLE_TSEL(siso), otsel, siso->siso_tlen);
465	}
466	if (isop->isop_faddr && isop->isop_faddr != &isop->isop_sfaddr)
467		m_freem(isop->isop_mfaddr);
468	isop->isop_faddr = 0;
469	if (isop->isop_socket->so_state & SS_NOFDREF)
470		iso_pcbdetach(isop);
471}
472
473/*
474 * FUNCTION:		iso_pcbdetach
475 *
476 * PURPOSE:		detach the pcb at *(isop) from it's socket and free
477 *			the mbufs associated with the pcb..
478 *			Dequeues (isop) from its head.
479 *
480 * RETURNS:		Nada.
481 *
482 * SIDE EFFECTS:
483 *
484 * NOTES:
485 */
486void
487iso_pcbdetach(void *v)
488{
489	struct isopcb  *isop = v;
490	struct socket  *so = isop->isop_socket;
491
492#ifdef ARGO_DEBUG
493	if (argo_debug[D_ISO]) {
494		printf("iso_pcbdetach(isop %p socket %p so %p)\n",
495		    isop, isop->isop_socket, so);
496	}
497#endif
498	if (so) {		/* in the x.25 domain, we sometimes have no
499				 * socket */
500		so->so_pcb = 0;
501		/* sofree drops the lock */
502		sofree(so);
503		mutex_enter(softnet_lock);
504	}
505#ifdef ARGO_DEBUG
506	if (argo_debug[D_ISO]) {
507		printf("iso_pcbdetach 2 \n");
508	}
509#endif
510	if (isop->isop_options)
511		(void) m_free(isop->isop_options);
512#ifdef ARGO_DEBUG
513	if (argo_debug[D_ISO]) {
514		printf("iso_pcbdetach 3 \n");
515	}
516#endif
517	rtcache_free(&isop->isop_route);
518#ifdef ARGO_DEBUG
519	if (argo_debug[D_ISO]) {
520		printf("iso_pcbdetach 3.1\n");
521	}
522#endif
523	if (isop->isop_clnpcache != NULL) {
524		struct clnp_cache *clcp =
525		mtod(isop->isop_clnpcache, struct clnp_cache *);
526#ifdef ARGO_DEBUG
527		if (argo_debug[D_ISO]) {
528			printf("iso_pcbdetach 3.2: clcp %p freeing clc_hdr %p\n",
529			    clcp, clcp->clc_hdr);
530		}
531#endif
532		if (clcp->clc_hdr != NULL)
533			m_free(clcp->clc_hdr);
534#ifdef ARGO_DEBUG
535		if (argo_debug[D_ISO]) {
536			printf("iso_pcbdetach 3.3: freeing cache %p\n",
537			    isop->isop_clnpcache);
538		}
539#endif
540		m_free(isop->isop_clnpcache);
541	}
542#ifdef ARGO_DEBUG
543	if (argo_debug[D_ISO]) {
544		printf("iso_pcbdetach 4 \n");
545	}
546#endif
547	iso_remque(isop);
548#ifdef ARGO_DEBUG
549	if (argo_debug[D_ISO]) {
550		printf("iso_pcbdetach 5 \n");
551	}
552#endif
553	if (isop->isop_laddr && (isop->isop_laddr != &isop->isop_sladdr))
554		m_freem(isop->isop_mladdr);
555	free((void *) isop, M_PCB);
556}
557
558
559/*
560 * FUNCTION:		iso_pcbnotify
561 *
562 * PURPOSE:		notify all connections in this protocol's queue (head)
563 *			that have peer address (dst) of the problem (errno)
564 *			by calling (notify) on the connections' isopcbs.
565 *
566 * RETURNS:		Rien.
567 *
568 * SIDE EFFECTS:
569 *
570 * NOTES:		(notify) is called at splnet!
571 */
572void
573iso_pcbnotify(struct isopcb *head, const struct sockaddr_iso *siso, int errno,
574	void (*notify) (struct isopcb *))
575{
576	struct isopcb *isop;
577	int s = splnet();
578
579#ifdef ARGO_DEBUG
580	if (argo_debug[D_ISO]) {
581		printf("iso_pcbnotify(head %p, notify %p) dst:\n",
582			head, notify);
583	}
584#endif
585	for (isop = head->isop_next; isop != head; isop = isop->isop_next) {
586		if (isop->isop_socket == 0 || isop->isop_faddr == 0 ||
587		    !SAME_ISOADDR(siso, isop->isop_faddr)) {
588#ifdef ARGO_DEBUG
589			if (argo_debug[D_ISO]) {
590				printf("iso_pcbnotify: CONTINUE isop %p, sock %p\n",
591				    isop, isop->isop_socket);
592				printf("addrmatch cmp'd with (%p):\n",
593					isop->isop_faddr);
594				dump_isoaddr(isop->isop_faddr);
595			}
596#endif
597			continue;
598		}
599		if (errno)
600			isop->isop_socket->so_error = errno;
601		if (notify)
602			(*notify) (isop);
603	}
604	splx(s);
605#ifdef ARGO_DEBUG
606	if (argo_debug[D_ISO]) {
607		printf("END OF iso_pcbnotify\n");
608	}
609#endif
610}
611
612
613/*
614 * FUNCTION:		iso_pcblookup
615 *
616 * PURPOSE:		looks for a given combination of (faddr), (fport),
617 *			(lport), (laddr) in the queue named by (head).
618 *			Argument (flags) is ignored.
619 *
620 * RETURNS:		ptr to the isopcb if it finds a connection matching
621 *			these arguments, o.w. returns zero.
622 *
623 * SIDE EFFECTS:
624 *
625 * NOTES:
626 */
627struct isopcb  *
628iso_pcblookup(
629	struct isopcb  *head,
630	int             fportlen,
631	void *        fport,
632	const struct sockaddr_iso *laddr)
633{
634	struct isopcb *isop;
635	const void *lp = TSEL(laddr);
636	unsigned int    llen = laddr->siso_tlen;
637
638#ifdef ARGO_DEBUG
639	if (argo_debug[D_ISO]) {
640		printf("iso_pcblookup(head %p laddr %p fport %p)\n",
641		       head, laddr, fport);
642	}
643#endif
644	for (isop = head->isop_next; isop != head; isop = isop->isop_next) {
645		if (isop->isop_laddr == 0 || isop->isop_laddr == laddr)
646			continue;
647		if (isop->isop_laddr->siso_tlen != llen)
648			continue;
649		if (memcmp(lp, TSEL(isop->isop_laddr), llen))
650			continue;
651		if (fportlen && isop->isop_faddr &&
652		    memcmp(fport, TSEL(isop->isop_faddr), (unsigned) fportlen))
653			continue;
654		/*
655		 * PHASE2 addrmatch1 should be iso_addrmatch(a, b, mask)
656		 * where mask is taken from isop->isop_laddrmask (new field)
657		 * isop_lnetmask will also be available in isop if (laddr !=
658		 * &zeroiso_addr && !iso_addrmatch1(laddr,
659		 * &(isop->isop_laddr.siso_addr))) continue;
660		 */
661		if (laddr->siso_nlen && (!SAME_ISOADDR(laddr, isop->isop_laddr)))
662			continue;
663		return (isop);
664	}
665	return (struct isopcb *) 0;
666}
667#endif /* ISO */
668