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