xref: /csrg-svn/sys/netiso/esis.c (revision 43421)
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 /*	@(#)esis.c	7.9 (Berkeley) 06/22/90 */
28 #ifndef lint
29 static char *rcsid = "$Header: esis.c,v 4.10 88/09/15 18:57:03 hagens Exp $";
30 #endif
31 
32 #ifdef ISO
33 
34 #include "types.h"
35 #include "param.h"
36 #include "mbuf.h"
37 #include "domain.h"
38 #include "protosw.h"
39 #include "user.h"
40 #include "socket.h"
41 #include "socketvar.h"
42 #include "errno.h"
43 #include "kernel.h"
44 
45 #include "../net/if.h"
46 #include "../net/if_dl.h"
47 #include "../net/route.h"
48 #include "../net/raw_cb.h"
49 
50 #include "iso.h"
51 #include "iso_pcb.h"
52 #include "iso_var.h"
53 #include "iso_snpac.h"
54 #include "clnl.h"
55 #include "clnp.h"
56 #include "clnp_stat.h"
57 #include "esis.h"
58 #include "argo_debug.h"
59 
60 /*
61  *	Global variables to esis implementation
62  *
63  *	esis_holding_time - the holding time (sec) parameter for outgoing pdus
64  *	esis_config_time  - the frequency (sec) that hellos are generated
65  *	esis_esconfig_time - suggested es configuration time placed in the
66  *						ish.
67  *
68  */
69 struct rawcb	esis_pcb;
70 int				esis_sendspace = 2048;
71 int				esis_recvspace = 2048;
72 short			esis_holding_time = ESIS_HT;
73 short			esis_config_time = ESIS_CONFIG;
74 short			esis_esconfig_time = ESIS_CONFIG;
75 extern int		iso_systype;
76 struct sockaddr_dl	esis_dl = { sizeof(esis_dl), AF_LINK };
77 extern char		all_es_snpa[], all_is_snpa[];
78 
79 #define EXTEND_PACKET(m, mhdr, cp)\
80 	if (((m)->m_next = m_getclr(M_DONTWAIT, MT_HEADER)) == NULL) {\
81 		esis_stat.es_nomem++;\
82 		m_freem(mhdr);\
83 		return;\
84 	} else {\
85 		(m) = (m)->m_next;\
86 		(cp) = mtod((m), caddr_t);\
87 	}
88 /*
89  * FUNCTION:		esis_init
90  *
91  * PURPOSE:			Initialize the kernel portion of esis protocol
92  *
93  * RETURNS:			nothing
94  *
95  * SIDE EFFECTS:
96  *
97  * NOTES:
98  */
99 esis_init()
100 {
101 	extern struct clnl_protosw clnl_protox[256];
102 	int esis_input();
103 	int	esis_config();
104 	int snpac_age();
105 #ifdef	ISO_X25ESIS
106 	x25esis_input();
107 #endif	ISO_X25ESIS
108 
109 	esis_pcb.rcb_next = esis_pcb.rcb_prev = &esis_pcb;
110 	llinfo_llc.lc_next = llinfo_llc.lc_prev = &llinfo_llc;
111 
112 	clnl_protox[ISO9542_ESIS].clnl_input = esis_input;
113 	timeout(snpac_age, (caddr_t)0, hz);
114 	timeout(esis_config, (caddr_t)0, hz);
115 
116 #ifdef	ISO_X25ESIS
117 	clnl_protox[ISO9542X25_ESIS].clnl_input = x25esis_input;
118 #endif	ISO_X25ESIS
119 }
120 
121 /*
122  * FUNCTION:		esis_usrreq
123  *
124  * PURPOSE:			Handle user level esis requests
125  *
126  * RETURNS:			0 or appropriate errno
127  *
128  * SIDE EFFECTS:
129  *
130  */
131 /*ARGSUSED*/
132 esis_usrreq(so, req, m, nam, control)
133 struct socket	*so;		/* socket: used only to get to this code */
134 int				req;		/* request */
135 struct mbuf		*m;			/* data for request */
136 struct mbuf		*nam;		/* optional name */
137 struct mbuf		*control;	/* optional control */
138 {
139 	struct rawcb *rp = sotorawcb(so);
140 	int error = 0;
141 
142 	if (suser(u.u_cred, &u.u_acflag)) {
143 		error = EACCES;
144 		goto release;
145 	}
146 	if (rp == NULL && req != PRU_ATTACH) {
147 		error = EINVAL;
148 		goto release;
149 	}
150 
151 	switch (req) {
152 	case PRU_ATTACH:
153 		if (rp != NULL) {
154 			error = EINVAL;
155 			break;
156 		}
157 		MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK);
158 		if (so->so_pcb = (caddr_t)rp) {
159 			bzero(so->so_pcb, sizeof(*rp));
160 			insque(rp, &esis_pcb);
161 			error = soreserve(so, esis_sendspace, esis_recvspace);
162 		} else
163 			error = ENOBUFS;
164 		break;
165 
166 	case PRU_SEND:
167 		if (nam == NULL) {
168 			error = EINVAL;
169 			break;
170 		}
171 		/* error checking here */
172 		error = isis_output(mtod(nam,struct sockaddr_dl *), m);
173 		m = NULL;
174 		break;
175 
176 	case PRU_DETACH:
177 		raw_detach(rp);
178 		break;
179 
180 	case PRU_SHUTDOWN:
181 		socantsendmore(so);
182 		break;
183 
184 	case PRU_ABORT:
185 		soisdisconnected(so);
186 		raw_detach(rp);
187 		break;
188 
189 	case PRU_SENSE:
190 		return (0);
191 
192 	default:
193 		return (EOPNOTSUPP);
194 	}
195 release:
196 	if (m != NULL)
197 		m_freem(m);
198 
199 	return (error);
200 }
201 
202 /*
203  * FUNCTION:		esis_input
204  *
205  * PURPOSE:			Process an incoming esis packet
206  *
207  * RETURNS:			nothing
208  *
209  * SIDE EFFECTS:
210  *
211  * NOTES:
212  */
213 esis_input(m0, shp)
214 struct mbuf		*m0;		/* ptr to first mbuf of pkt */
215 struct snpa_hdr	*shp;	/* subnetwork header */
216 {
217 	register struct esis_fixed	*pdu = mtod(m0, struct esis_fixed *);
218 	register int type;
219 
220 	/*
221 	 *	check checksum if necessary
222 	 */
223 	if (ESIS_CKSUM_REQUIRED(pdu) && iso_check_csum(m0, (int)pdu->esis_hdr_len)) {
224 		esis_stat.es_badcsum++;
225 		goto bad;
226 	}
227 
228 	/* check version */
229 	if (pdu->esis_vers != ESIS_VERSION) {
230 		esis_stat.es_badvers++;
231 		goto bad;
232 	}
233 	type = pdu->esis_type & 0x1f;
234 	switch (type) {
235 		case ESIS_ESH:
236 			esis_eshinput(m0, shp);
237 			break;
238 
239 		case ESIS_ISH:
240 			esis_ishinput(m0, shp);
241 			break;
242 
243 		case ESIS_RD:
244 			esis_rdinput(m0, shp);
245 			break;
246 
247 		default:
248 			esis_stat.es_badtype++;
249 	}
250 
251 bad:
252 	if (esis_pcb.rcb_next != &esis_pcb)
253 		isis_input(m0, shp);
254 	else
255 		m_freem(m0);
256 }
257 
258 /*
259  * FUNCTION:		esis_rdoutput
260  *
261  * PURPOSE:			Transmit a redirect pdu
262  *
263  * RETURNS:			nothing
264  *
265  * SIDE EFFECTS:
266  *
267  * NOTES:			Assumes there is enough space for fixed part of header,
268  *					DA, BSNPA and NET in first mbuf.
269  */
270 esis_rdoutput(inbound_shp, inbound_m, inbound_oidx, rd_dstnsap, rt)
271 struct snpa_hdr		*inbound_shp;	/* snpa hdr from incoming packet */
272 struct mbuf			*inbound_m;		/* incoming pkt itself */
273 struct clnp_optidx	*inbound_oidx;	/* clnp options assoc with incoming pkt */
274 struct iso_addr		*rd_dstnsap;	/* ultimate destination of pkt */
275 struct rtentry		*rt;			/* snpa cache info regarding next hop of
276 										pkt */
277 {
278 	struct mbuf			*m, *m0;
279 	caddr_t				cp;
280 	struct esis_fixed	*pdu;
281 	int					len, total_len = 0;
282 	struct sockaddr_iso	siso;
283 	struct ifnet 		*ifp = inbound_shp->snh_ifp;
284 	struct sockaddr_dl *sdl;
285 	struct iso_addr *rd_gwnsap;
286 
287 	if (rt->rt_flags & RTF_GATEWAY) {
288 		rd_gwnsap = &((struct sockaddr_iso *)rt->rt_gateway)->siso_addr;
289 		rt = rtalloc1(rt->rt_gateway, 0);
290 	} else
291 		rd_gwnsap = &((struct sockaddr_iso *)rt_key(rt))->siso_addr;
292 	if (rt == 0 || (sdl = (struct sockaddr_dl *)rt->rt_gateway) == 0 ||
293 		sdl->sdl_family != AF_LINK) {
294 		/* maybe we should have a function that you
295 		   could put in the iso_ifaddr structure
296 		   which could translate iso_addrs into snpa's
297 		   where there is a known mapping for that address type */
298 		esis_stat.es_badtype++;
299 		return;
300 	}
301 	esis_stat.es_rdsent++;
302 	IFDEBUG(D_ESISOUTPUT)
303 		printf("esis_rdoutput: ifp x%x (%s%d), ht %d, m x%x, oidx x%x\n",
304 			ifp, ifp->if_name, ifp->if_unit, esis_holding_time, inbound_m,
305 			inbound_oidx);
306 		printf("\tdestination: %s\n", clnp_iso_addrp(rd_dstnsap));
307 		printf("\tredirected toward:%s\n", clnp_iso_addrp(rd_gwnsap));
308 	ENDDEBUG
309 
310 	if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
311 		esis_stat.es_nomem++;
312 		return;
313 	}
314 	bzero(mtod(m, caddr_t), MHLEN);
315 
316 	pdu = mtod(m, struct esis_fixed *);
317 	cp = (caddr_t)(pdu + 1); /*pointer arith.; 1st byte after header */
318 	len = sizeof(struct esis_fixed);
319 
320 	/*
321 	 *	Build fixed part of header
322 	 */
323 	pdu->esis_proto_id = ISO9542_ESIS;
324 	pdu->esis_vers = ESIS_VERSION;
325 	pdu->esis_type = ESIS_RD;
326 	HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, esis_holding_time);
327 
328 	/* Insert destination address */
329 	(void) esis_insert_addr(&cp, &len, rd_dstnsap, m, 0);
330 
331 	/* Insert the snpa of better next hop */
332 	*cp++ = sdl->sdl_alen;
333 	bcopy(LLADDR(sdl), cp, sdl->sdl_alen);
334 	cp += sdl->sdl_alen;
335 	len += (sdl->sdl_alen + 1);
336 
337 	/*
338 	 *	If the next hop is not the destination, then it ought to be
339 	 *	an IS and it should be inserted next. Else, set the
340 	 *	NETL to 0
341 	 */
342 	/* PHASE2 use mask from ifp of outgoing interface */
343 	if (!iso_addrmatch1(rd_dstnsap, rd_gwnsap)) {
344 		/* this should not happen:
345 		if ((nhop_sc->sc_flags & SNPA_IS) == 0) {
346 			printf("esis_rdoutput: next hop is not dst and not an IS\n");
347 			m_freem(m0);
348 			return;
349 		} */
350 		(void) esis_insert_addr(&cp, &len, rd_gwnsap, m, 0);
351 	} else {
352 		*cp++ = 0;	/* NETL */
353 		len++;
354 	}
355 	m->m_len = len;
356 
357 	/*
358 	 *	PHASE2
359 	 *	If redirect is to an IS, add an address mask. The mask to be
360 	 *	used should be the mask present in the routing entry used to
361 	 *	forward the original data packet.
362 	 */
363 
364 	/*
365 	 *	Copy Qos, priority, or security options present in original npdu
366 	 */
367 	if (inbound_oidx) {
368 		/* THIS CODE IS CURRENTLY (mostly) UNTESTED */
369 		int optlen = 0;
370 		if (inbound_oidx->cni_qos_formatp)
371 			optlen += (inbound_oidx->cni_qos_len + 2);
372 		if (inbound_oidx->cni_priorp)	/* priority option is 1 byte long */
373 			optlen += 3;
374 		if (inbound_oidx->cni_securep)
375 			optlen += (inbound_oidx->cni_secure_len + 2);
376 		if (M_TRAILINGSPACE(m) < optlen) {
377 			EXTEND_PACKET(m, m0, cp);
378 			m->m_len = 0;
379 			/* assumes MLEN > optlen */
380 		}
381 		/* assume MLEN-len > optlen */
382 		/*
383 		 *	When copying options, copy from ptr - 2 in order to grab
384 		 *	the option code and length
385 		 */
386 		if (inbound_oidx->cni_qos_formatp) {
387 			bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_qos_formatp - 2,
388 				cp, (unsigned)(inbound_oidx->cni_qos_len + 2));
389 			cp += inbound_oidx->cni_qos_len + 2;
390 		}
391 		if (inbound_oidx->cni_priorp) {
392 			bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_priorp - 2,
393 					cp, 3);
394 			cp += 3;
395 		}
396 		if (inbound_oidx->cni_securep) {
397 			bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_securep - 2, cp,
398 				(unsigned)(inbound_oidx->cni_secure_len + 2));
399 			cp += inbound_oidx->cni_secure_len + 2;
400 		}
401 		m->m_len += optlen;
402 		len += optlen;
403 	}
404 
405 	pdu->esis_hdr_len = m0->m_pkthdr.len = len;
406 	iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len);
407 
408 	bzero((caddr_t)&siso, sizeof(siso));
409 	siso.siso_family = AF_ISO;
410 	siso.siso_data[0] = AFI_SNA;
411 	siso.siso_nlen = 6 + 1;	/* should be taken from snpa_hdr */
412 										/* +1 is for AFI */
413 	bcopy(inbound_shp->snh_shost, siso.siso_data + 1, 6);
414 	(ifp->if_output)(ifp, m0, &siso, 0);
415 }
416 
417 /*
418  * FUNCTION:		esis_insert_addr
419  *
420  * PURPOSE:			Insert an iso_addr into a buffer
421  *
422  * RETURNS:			true if buffer was big enough, else false
423  *
424  * SIDE EFFECTS:	Increment buf & len according to size of iso_addr
425  *
426  * NOTES:			Plus 1 here is for length byte
427  */
428 esis_insert_addr(buf, len, isoa, m, nsellen)
429 register caddr_t			*buf;		/* ptr to buffer to put address into */
430 int							*len;		/* ptr to length of buffer so far */
431 register struct iso_addr	*isoa;		/* ptr to address */
432 register struct mbuf		*m;			/* determine if there remains space */
433 int							nsellen;
434 {
435 	register int newlen, result = 0;
436 
437 	isoa->isoa_len -= nsellen;
438 	newlen = isoa->isoa_len + 1;
439 	if (newlen <=  M_TRAILINGSPACE(m)) {
440 		bcopy((caddr_t)isoa, *buf, newlen);
441 		*len += newlen;
442 		*buf += newlen;
443 		m->m_len += newlen;
444 		result = 1;
445 	}
446 	isoa->isoa_len += nsellen;
447 	return (result);
448 }
449 
450 #define ESIS_EXTRACT_ADDR(d, b) { d = (struct iso_addr *)(b); b += (1 + *b); \
451 	    if (b > buflim) {esis_stat.es_toosmall++; goto bad;}}
452 #define ESIS_NEXT_OPTION(b)	{ b += (2 + b[1]); \
453 	    if (b > buflim) {esis_stat.es_toosmall++; goto bad;}}
454 int ESHonly = 1;
455 /*
456 
457 /*
458  * FUNCTION:		esis_eshinput
459  *
460  * PURPOSE:			Process an incoming ESH pdu
461  *
462  * RETURNS:			nothing
463  *
464  * SIDE EFFECTS:
465  *
466  * NOTES:
467  */
468 esis_eshinput(m, shp)
469 struct mbuf		*m;	/* esh pdu */
470 struct snpa_hdr	*shp;	/* subnetwork header */
471 {
472 	struct	esis_fixed	*pdu = mtod(m, struct esis_fixed *);
473 	u_short				ht;		/* holding time */
474 	struct	iso_addr	*nsap;
475 	int					naddr;
476 	u_char				*buf = (u_char *)(pdu + 1);
477 	u_char				*buflim = pdu->esis_hdr_len + (u_char *)pdu;
478 	int					new_entry = 0;
479 
480 	esis_stat.es_eshrcvd++;
481 
482 	CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
483 
484 	naddr = *buf++;
485 	if (buf >= buflim)
486 		goto bad;
487 	if (naddr == 1) {
488 		ESIS_EXTRACT_ADDR(nsap, buf);
489 		new_entry = snpac_add(shp->snh_ifp,
490 								 nsap, shp->snh_shost, SNPA_ES, ht, 0);
491 	} else {
492 		int nsellength = 0, nlen = 0;
493 		{
494 		/* See if we want to compress out multiple nsaps differing
495 		   only by nsel */
496 			register struct ifaddr *ifa = shp->snh_ifp->if_addrlist;
497 			for (; ifa; ifa = ifa->ifa_next)
498 				if (ifa->ifa_addr->sa_family == AF_ISO) {
499 					nsellength = ((struct iso_ifaddr *)ifa)->ia_addr.siso_tlen;
500 					break;
501 			}
502 		}
503 		IFDEBUG(D_ESISINPUT)
504 			printf("esis_eshinput: esh: ht %d, naddr %d nsellength %d\n",
505 					ht, naddr, nsellength);
506 		ENDDEBUG
507 		while (naddr-- > 0) {
508 			struct iso_addr *nsap2; u_char *buf2;
509 			ESIS_EXTRACT_ADDR(nsap, buf);
510 			/* see if there is at least one more nsap in ESH differing
511 			   only by nsel */
512 			if (nsellength != 0) for (buf2 = buf; buf2 < buflim;) {
513 				ESIS_EXTRACT_ADDR(nsap2, buf2);
514 				IFDEBUG(D_ESISINPUT)
515 					printf("esis_eshinput: comparing %s ",
516 						clnp_iso_addrp(nsap));
517 					printf("and %s\n", clnp_iso_addrp(nsap2));
518 				ENDDEBUG
519 				if (Bcmp(nsap->isoa_genaddr, nsap2->isoa_genaddr,
520 						 nsap->isoa_len - nsellength) == 0) {
521 					nlen = nsellength;
522 					break;
523 				}
524 			}
525 			new_entry |= snpac_add(shp->snh_ifp,
526 									nsap, shp->snh_shost, SNPA_ES, ht, nlen);
527 			nlen = 0;
528 		}
529 	}
530 	IFDEBUG(D_ESISINPUT)
531 		printf("esis_eshinput: nsap %s is %s\n",
532 			clnp_iso_addrp(nsap), new_entry ? "new" : "old");
533 	ENDDEBUG
534 	if (new_entry && (iso_systype & SNPA_IS))
535 		esis_shoutput(shp->snh_ifp, ESIS_ISH, esis_holding_time,
536 						shp->snh_shost, 6, (struct iso_addr *)0);
537 bad:
538 	return;
539 }
540 
541 /*
542  * FUNCTION:		esis_ishinput
543  *
544  * PURPOSE:			process an incoming ISH pdu
545  *
546  * RETURNS:
547  *
548  * SIDE EFFECTS:
549  *
550  * NOTES:
551  */
552 esis_ishinput(m, shp)
553 struct mbuf		*m;	/* esh pdu */
554 struct snpa_hdr	*shp;	/* subnetwork header */
555 {
556 	struct esis_fixed	*pdu = mtod(m, struct esis_fixed *);
557 	u_short				ht;					/* holding time */
558 	struct iso_addr		*nsap; 				/* Network Entity Title */
559 	register u_char		*buf = (u_char *) (pdu + 1);
560 	register u_char		*buflim = pdu->esis_hdr_len + (u_char *)pdu;
561 	int					new_entry;
562 
563 	esis_stat.es_ishrcvd++;
564 	CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
565 
566 	IFDEBUG(D_ESISINPUT)
567 		printf("esis_ishinput: ish: ht %d\n", ht);
568 	ENDDEBUG
569 	if (ESHonly)
570 		goto bad;
571 
572 	ESIS_EXTRACT_ADDR(nsap, buf);
573 
574 	while (buf < buflim) {
575 		switch (*buf) {
576 		case ESISOVAL_ESCT:
577 			if (buf[1] != 2)
578 				goto bad;
579 			CTOH(buf[2], buf[3], esis_config_time);
580 			break;
581 
582 		default:
583 			printf("Unknown ISH option: %x\n", *buf);
584 		}
585 		ESIS_NEXT_OPTION(buf);
586 	}
587 	new_entry = snpac_add(shp->snh_ifp, nsap, shp->snh_shost, SNPA_IS, ht, 0);
588 	IFDEBUG(D_ESISINPUT)
589 		printf("esis_ishinput: nsap %s is %s\n",
590 			clnp_iso_addrp(nsap), new_entry ? "new" : "old");
591 	ENDDEBUG
592 
593 	if (new_entry)
594 		esis_shoutput(shp->snh_ifp,
595 			iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
596 			esis_holding_time, shp->snh_shost, 6, (struct iso_addr *)0);
597 bad:
598 	return;
599 }
600 
601 /*
602  * FUNCTION:		esis_rdinput
603  *
604  * PURPOSE:			Process an incoming RD pdu
605  *
606  * RETURNS:
607  *
608  * SIDE EFFECTS:
609  *
610  * NOTES:
611  */
612 esis_rdinput(m0, shp)
613 struct mbuf		*m0;	/* esh pdu */
614 struct snpa_hdr	*shp;	/* subnetwork header */
615 {
616 	struct esis_fixed	*pdu = mtod(m0, struct esis_fixed *);
617 	u_short				ht;		/* holding time */
618 	struct iso_addr		*da, *net = 0, *netmask = 0, *snpamask = 0;
619 	register struct iso_addr *bsnpa;
620 	register u_char		*buf = (u_char *)(pdu + 1);
621 	register u_char		*buflim = pdu->esis_hdr_len + (u_char *)pdu;
622 
623 	esis_stat.es_rdrcvd++;
624 
625 	/* intermediate systems ignore redirects */
626 	if (iso_systype & SNPA_IS)
627 		return;
628 	if (ESHonly)
629 		return;
630 
631 	CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
632 	if (buf >= buflim)
633 		return;
634 
635 	/* Extract DA */
636 	ESIS_EXTRACT_ADDR(da, buf);
637 
638 	/* Extract better snpa */
639 	ESIS_EXTRACT_ADDR(bsnpa, buf);
640 
641 	/* Extract NET if present */
642 	if (buf < buflim) {
643 		if (*buf == 0)
644 			buf++; /* no NET present, skip NETL anyway */
645 		else
646 			ESIS_EXTRACT_ADDR(net, buf);
647 	}
648 
649 	/* process options */
650 	while (buf < buflim) {
651 		switch (*buf) {
652 		case ESISOVAL_SNPAMASK:
653 			if (snpamask) /* duplicate */
654 				return;
655 			snpamask = (struct iso_addr *)(buf + 1);
656 			break;
657 
658 		case ESISOVAL_NETMASK:
659 			if (netmask) /* duplicate */
660 				return;
661 			netmask = (struct iso_addr *)(buf + 1);
662 			break;
663 
664 		default:
665 			printf("Unknown option in ESIS RD (0x%x)\n", buf[-1]);
666 		}
667 		ESIS_NEXT_OPTION(buf);
668 	}
669 
670 	IFDEBUG(D_ESISINPUT)
671 		printf("esis_rdinput: rd: ht %d, da %s\n", ht, clnp_iso_addrp(da));
672 		if (net)
673 			printf("\t: net %s\n", clnp_iso_addrp(net));
674 	ENDDEBUG
675 	/*
676 	 *	If netl is zero, then redirect is to an ES. We need to add an entry
677 	 *	to the snpa cache for (destination, better snpa).
678 	 *	If netl is not zero, then the redirect is to an IS. In this
679 	 *	case, add an snpa cache entry for (net, better snpa).
680 	 *
681 	 *	If the redirect is to an IS, add a route entry towards that
682 	 *	IS.
683 	 */
684 	if (net == 0 || net->isoa_len == 0 || snpamask) {
685 		/* redirect to an ES */
686 		snpac_add(shp->snh_ifp, da,
687 				bsnpa->isoa_genaddr, SNPA_ES, ht, 0);
688 	} else {
689 		snpac_add(shp->snh_ifp, net,
690 				bsnpa->isoa_genaddr, SNPA_IS, ht, 0);
691 		snpac_addrt(shp->snh_ifp, da, net, netmask);
692 	}
693 bad: ;    /* Needed by ESIS_NEXT_OPTION */
694 }
695 
696 /*
697  * FUNCTION:		esis_config
698  *
699  * PURPOSE:			Report configuration
700  *
701  * RETURNS:
702  *
703  * SIDE EFFECTS:
704  *
705  * NOTES:			Called every esis_config_time seconds
706  */
707 esis_config()
708 {
709 	register struct ifnet	*ifp;
710 
711 	timeout(esis_config, (caddr_t)0, hz * esis_config_time);
712 
713 	/*
714 	 *	Report configuration for each interface that
715 	 *	- is UP
716 	 *	- is not loopback
717 	 *	- has an ISO address
718 	 */
719 
720 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
721 		if ((ifp->if_flags & IFF_UP) &&
722 			((ifp->if_flags & IFF_LOOPBACK) == 0)) {
723 			/* search for an ISO address family */
724 			struct ifaddr	*ia;
725 
726 			for (ia = ifp->if_addrlist; ia; ia = ia->ifa_next) {
727 				if (ia->ifa_addr->sa_family == AF_ISO) {
728 					esis_shoutput(ifp,
729 						iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
730 						esis_holding_time,
731 						(caddr_t)(iso_systype & SNPA_ES ? all_is_snpa :
732 						all_es_snpa), 6, (struct iso_addr *)0);
733 					break;
734 				}
735 			}
736 		}
737 	}
738 }
739 
740 /*
741  * FUNCTION:		esis_shoutput
742  *
743  * PURPOSE:			Transmit an esh or ish pdu
744  *
745  * RETURNS:			nothing
746  *
747  * SIDE EFFECTS:
748  *
749  * NOTES:
750  */
751 esis_shoutput(ifp, type, ht, sn_addr, sn_len, isoa)
752 struct ifnet	*ifp;
753 int				type;
754 short			ht;
755 caddr_t 		sn_addr;
756 int				sn_len;
757 struct	iso_addr *isoa;
758 {
759 	struct mbuf			*m, *m0;
760 	caddr_t				cp, naddrp;
761 	int					naddr = 0;
762 	struct esis_fixed	*pdu;
763 	struct iso_ifaddr	*ia;
764 	int					len;
765 	struct sockaddr_iso	siso;
766 
767 	if (type == ESIS_ESH)
768 		esis_stat.es_eshsent++;
769 	else if (type == ESIS_ISH)
770 		esis_stat.es_ishsent++;
771 	else {
772 		printf("esis_shoutput: bad pdu type\n");
773 		return;
774 	}
775 
776 	IFDEBUG(D_ESISOUTPUT)
777 		int	i;
778 		printf("esis_shoutput: ifp x%x (%s%d), %s, ht %d, to: [%d] ",
779 			ifp, ifp->if_name, ifp->if_unit, type == ESIS_ESH ? "esh" : "ish",
780 			ht, sn_len);
781 		for (i=0; i<sn_len; i++)
782 			printf("%x%c", *(sn_addr+i), i < (sn_len-1) ? ':' : ' ');
783 		printf("\n");
784 	ENDDEBUG
785 
786 	if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
787 		esis_stat.es_nomem++;
788 		return;
789 	}
790 	bzero(mtod(m, caddr_t), MHLEN);
791 
792 	pdu = mtod(m, struct esis_fixed *);
793 	naddrp = cp = (caddr_t)(pdu + 1);
794 	len = sizeof(struct esis_fixed);
795 
796 	/*
797 	 *	Build fixed part of header
798 	 */
799 	pdu->esis_proto_id = ISO9542_ESIS;
800 	pdu->esis_vers = ESIS_VERSION;
801 	pdu->esis_type = type;
802 	HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
803 
804 	if (type == ESIS_ESH) {
805 		cp++;
806 		len++;
807 	}
808 
809 	m->m_len = len;
810 	if (isoa) {
811 		/*
812 		 * Here we are responding to a clnp packet sent to an NSAP
813 		 * that is ours which was sent to the MAC addr all_es's.
814 		 * It is possible that we did not specifically advertise this
815 		 * NSAP, even though it is ours, so we will respond
816 		 * directly to the sender that we are here.  If we do have
817 		 * multiple NSEL's we'll tack them on so he can compress them out.
818 		 */
819 		(void) esis_insert_addr(&cp, &len, isoa, m, 0);
820 		naddr = 1;
821 	}
822 	for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
823 		int nsellen = (type == ESIS_ISH ? ia->ia_addr.siso_tlen : 0);
824 		int n = ia->ia_addr.siso_nlen;
825 		register struct iso_ifaddr *ia2;
826 
827 		if (type == ESIS_ISH && naddr > 0)
828 			break;
829 		for (ia2 = iso_ifaddr; ia2 != ia; ia2 = ia2->ia_next)
830 			if (Bcmp(ia->ia_addr.siso_data, ia2->ia_addr.siso_data, n) == 0)
831 					break;
832 		if (ia2 != ia)
833 			continue;	/* Means we have previously copied this nsap */
834 		if (isoa && Bcmp(ia->ia_addr.siso_data, isoa->isoa_genaddr, n) == 0) {
835 			isoa = 0;
836 			continue;	/* Ditto */
837 		}
838 		IFDEBUG(D_ESISOUTPUT)
839 			printf("esis_shoutput: adding NSAP %s\n",
840 				clnp_iso_addrp(&ia->ia_addr.siso_addr));
841 		ENDDEBUG
842 		if (!esis_insert_addr(&cp, &len,
843 							  &ia->ia_addr.siso_addr, m, nsellen)) {
844 			EXTEND_PACKET(m, m0, cp);
845 			(void) esis_insert_addr(&cp, &len, &ia->ia_addr.siso_addr, m,
846 									nsellen);
847 		}
848 		naddr++;
849 	}
850 
851 	if (type == ESIS_ESH)
852 		*naddrp = naddr;
853 
854 	m0->m_pkthdr.len = len;
855 	pdu->esis_hdr_len = len;
856 	iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len);
857 
858 	bzero((caddr_t)&siso, sizeof(siso));
859 	siso.siso_family = AF_ISO;
860 	siso.siso_data[0] = AFI_SNA;
861 	siso.siso_nlen = sn_len + 1;
862 	bcopy(sn_addr, siso.siso_data + 1, (unsigned)sn_len);
863 	(ifp->if_output)(ifp, m0, &siso, 0);
864 }
865 
866 /*
867  * FUNCTION:		isis_input
868  *
869  * PURPOSE:			Process an incoming isis packet
870  *
871  * RETURNS:			nothing
872  *
873  * SIDE EFFECTS:
874  *
875  * NOTES:
876  */
877 isis_input(m0, shp)
878 struct mbuf		*m0;		/* ptr to first mbuf of pkt */
879 struct snpa_hdr	*shp;	/* subnetwork header */
880 {
881 	register int type;
882 	struct rawcb *rp;
883 	struct ifnet *ifp = shp->snh_ifp;
884 	struct sockbuf *sb = 0;
885 	char workbuf[16];
886 	struct mbuf *mm;
887 
888 	IFDEBUG(D_ISISINPUT)
889 		int i;
890 
891 		printf("isis_input: pkt on ifp x%x (%s%d): from:", ifp,
892 			ifp->if_name, ifp->if_unit);
893 		for (i=0; i<6; i++)
894 			printf("%x%c", shp->snh_shost[i]&0xff, (i<5) ? ':' : ' ');
895 		printf(" to:");
896 		for (i=0; i<6; i++)
897 			printf("%x%c", shp->snh_dhost[i]&0xff, (i<5) ? ':' : ' ');
898 		printf("\n");
899 	ENDDEBUG
900 	esis_dl.sdl_alen = ifp->if_addrlen;
901 	esis_dl.sdl_index = ifp->if_index;
902 	bcopy(shp->snh_shost, (caddr_t)esis_dl.sdl_data, esis_dl.sdl_alen);
903 	for (rp = esis_pcb.rcb_next; rp != &esis_pcb; rp = rp->rcb_next) {
904 		if (sb == 0) {
905 			sb = &rp->rcb_socket->so_rcv;
906 			continue;
907 		}
908 		if (mm = m_copy(m0, 0, M_COPYALL)) { /*can't block at interrupt level */
909 			if (sbappendaddr(&rp->rcb_socket->so_rcv,
910 							  &esis_dl, mm, (struct mbuf *)0) != 0)
911 				sorwakeup(rp->rcb_socket);
912 			else {
913 				IFDEBUG(D_ISISINPUT)
914 					printf("Error in sbappenaddr, mm = 0x%x\n", mm);
915 				ENDDEBUG
916 				m_freem(mm);
917 			}
918 		}
919 	}
920 	if (sb) {
921 		if (sbappendaddr(&rp->rcb_socket->so_rcv,
922 							  &esis_dl, mm, (struct mbuf *)0) != 0)
923 			sorwakeup(rp->rcb_socket);
924 		else
925 			m_freem(m0);
926 	}
927 }
928 
929 isis_output(sdl, m)
930 register struct sockaddr_dl	*sdl;
931 struct mbuf *m;
932 {
933 	register struct ifnet *ifp;
934 	struct ifaddr *ifa, *ifa_ifwithnet();
935 	struct sockaddr_iso siso;
936 	int error = 0;
937 	unsigned sn_len;
938 
939 	ifa = ifa_ifwithnet(sdl);	/* extract ifp from sockaddr_dl */
940 	if (ifa == 0) {
941 		IFDEBUG(D_ISISOUTPUT)
942 			printf("isis_output: interface not found\n");
943 		ENDDEBUG
944 		error = EINVAL;
945 		goto release;
946 	}
947 	ifp = ifa->ifa_ifp;
948 	sn_len = ifp->if_addrlen;
949 	IFDEBUG(D_ISISOUTPUT)
950 		u_char *cp = (u_char *)LLADDR(sdl), *cplim = cp + sn_len;
951 		printf("isis_output: ifp 0x%x (%s%d), to: ",
952 			ifp, ifp->if_name, ifp->if_unit);
953 		while (cp < cplim) {
954 			printf("%x", *cp++);
955 			printf("%c", (cp < cplim) ? ':' : ' ');
956 		}
957 		printf("\n");
958 	ENDDEBUG
959 	bzero((caddr_t)&siso, sizeof(siso));
960 	siso.siso_family = AF_ISO; /* This convention may be useful for X.25 */
961 	siso.siso_data[0] = AFI_SNA;
962 	siso.siso_nlen = sn_len + 1;
963 	bcopy(LLADDR(sdl), siso.siso_data + 1, sn_len);
964 	error = (ifp->if_output)(ifp, m, (struct sockaddr *)&siso, 0);
965 	if (error) {
966 		IFDEBUG(D_ISISOUTPUT)
967 			printf("isis_output: error from ether_output is %d\n", error);
968 		ENDDEBUG
969 	}
970 	return (error);
971 
972 release:
973 	if (m != NULL)
974 		m_freem(m);
975 	return(error);
976 }
977 
978 
979 /*
980  * FUNCTION:		esis_ctlinput
981  *
982  * PURPOSE:			Handle the PRC_IFDOWN transition
983  *
984  * RETURNS:			nothing
985  *
986  * SIDE EFFECTS:
987  *
988  * NOTES:			Calls snpac_flush for interface specified.
989  *					The loop through iso_ifaddr is stupid because
990  *					back in if_down, we knew the ifp...
991  */
992 esis_ctlinput(req, siso)
993 int						req;		/* request: we handle only PRC_IFDOWN */
994 struct sockaddr_iso		*siso;		/* address of ifp */
995 {
996 	register struct iso_ifaddr *ia;	/* scan through interface addresses */
997 
998 	if (req == PRC_IFDOWN)
999 		for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
1000 			if (iso_addrmatch(IA_SIS(ia), siso))
1001 				snpac_flushifp(ia->ia_ifp);
1002 		}
1003 }
1004 
1005 #endif	ISO
1006