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