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