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