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