xref: /csrg-svn/sys/netiso/esis.c (revision 37469)
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 "dir.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 "argo_debug.h"
56 #include "esis.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, rights)
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		*rights;	/* optional rights */
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);
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);
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);
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)
362 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 {
367 	register int newlen = isoa->isoa_len + 1;
368 
369 	if (newlen > M_TRAILINGSPACE(m))
370 		return(0);
371 	bcopy((caddr_t)isoa, *buf, newlen);
372 	*len += newlen;
373 	*buf += newlen;
374 	m->m_len += newlen;
375 	return(1);
376 }
377 
378 /*
379 
380 /*
381  * FUNCTION:		esis_eshinput
382  *
383  * PURPOSE:			Process an incoming ESH pdu
384  *
385  * RETURNS:			nothing
386  *
387  * SIDE EFFECTS:
388  *
389  * NOTES:
390  */
391 esis_eshinput(m, shp)
392 struct mbuf		*m;	/* esh pdu */
393 struct snpa_hdr	*shp;	/* subnetwork header */
394 {
395 	struct esis_fixed	*pdu = mtod(m, struct esis_fixed *);
396 	u_short				ht;		/* holding time */
397 	struct iso_addr		*nsap;
398 	int					naddr = 0;
399 	u_char				*buf = (u_char *)(pdu + 1);
400 	int					len = pdu->esis_hdr_len - sizeof(struct esis_fixed);
401 	int					optlen, new_entry;
402 
403 	esis_stat.es_eshrcvd++;
404 
405 	CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
406 
407 	if (len > 0) {
408 		naddr = *buf++;
409 		len--;
410 	} else
411 		goto bad;
412 
413 	IFDEBUG(D_ESISINPUT)
414 		printf("esis_eshinput: esh: ht %d, naddr %d\n", ht, naddr);
415 	ENDDEBUG
416 
417 	while (naddr-- > 0) {
418 		nsap = (struct iso_addr *)buf;
419 		if ((len -=  (optlen = *buf++)) >= 0) {
420 			buf += optlen;
421 			new_entry = (snpac_look(nsap) == NULL);
422 
423 			IFDEBUG(D_ESISINPUT)
424 				printf("esis_eshinput: nsap %s is %s\n",
425 					clnp_iso_addrp(nsap), new_entry ? "new" : "old");
426 			ENDDEBUG
427 
428 			snpac_add(shp->snh_ifp, nsap, shp->snh_shost, 6, SNPA_ES, ht);
429 			if (new_entry)
430 				esis_shoutput(shp->snh_ifp,
431 					iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
432 					esis_holding_time, shp->snh_shost, 6);
433 		} else {
434 bad:
435 			esis_stat.es_toosmall++;
436 			break;
437 		}
438 	}
439 
440 	m_freem(m);
441 }
442 
443 /*
444  * FUNCTION:		esis_ishinput
445  *
446  * PURPOSE:			process an incoming ISH pdu
447  *
448  * RETURNS:
449  *
450  * SIDE EFFECTS:
451  *
452  * NOTES:
453  */
454 esis_ishinput(m, shp)
455 struct mbuf		*m;	/* esh pdu */
456 struct snpa_hdr	*shp;	/* subnetwork header */
457 {
458 	struct esis_fixed	*pdu = mtod(m, struct esis_fixed *);
459 	u_short				ht;		/* holding time */
460 	struct iso_addr		*nsap;
461 	caddr_t				buf = (caddr_t)pdu + sizeof(struct esis_fixed);
462 	int					len = pdu->esis_hdr_len - sizeof(struct esis_fixed);
463 	int					optlen, new_entry;
464 
465 	esis_stat.es_ishrcvd++;
466 	CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
467 
468 	IFDEBUG(D_ESISINPUT)
469 		printf("esis_ishinput: ish: ht %d\n", ht);
470 	ENDDEBUG
471 
472 	nsap = (struct iso_addr *)buf;
473 	if ((len -=  (optlen = *buf++)) < 0)
474 		goto bad;
475 	buf += optlen;
476 
477 	/* process options */
478 	while (len > 0) {
479 		switch (*buf++) {
480 		case ESISOVAL_ESCT:
481 			if (*buf != 2)
482 				goto bad;
483 			CTOH(buf[0], buf[1], esis_config_time);
484 		}
485 		if ((len -=  (optlen = *buf)) < 0) {
486 	bad:
487 			esis_stat.es_toosmall++;
488 			m_freem(m);
489 			return;
490 		}
491 		buf += 1 + optlen;
492 	}
493 	new_entry = (snpac_look(nsap) == NULL);
494 
495 	IFDEBUG(D_ESISINPUT)
496 		printf("esis_ishinput: nsap %s is %s\n",
497 			clnp_iso_addrp(nsap), new_entry ? "new" : "old");
498 	ENDDEBUG
499 
500 	snpac_add(shp->snh_ifp, nsap, shp->snh_shost, 6, SNPA_IS, ht);
501 	if (new_entry)
502 		esis_shoutput(shp->snh_ifp,
503 			iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
504 			esis_holding_time, shp->snh_shost, 6);
505 	m_freem(m);
506 }
507 
508 /*
509  * FUNCTION:		esis_rdinput
510  *
511  * PURPOSE:			Process an incoming RD pdu
512  *
513  * RETURNS:
514  *
515  * SIDE EFFECTS:
516  *
517  * NOTES:
518  */
519 esis_rdinput(m0, shp)
520 struct mbuf		*m0;	/* esh pdu */
521 struct snpa_hdr	*shp;	/* subnetwork header */
522 {
523 	struct esis_fixed	*pdu = mtod(m0, struct esis_fixed *);
524 	u_short				ht;		/* holding time */
525 	register struct iso_addr *da, *net = 0, *netmask = 0, *snpamask = 0;
526 	u_char				*buf = (u_char *)(pdu + 1);
527 	int					len = pdu->esis_hdr_len - sizeof(struct esis_fixed);
528 	int					optlen, bsnpalen;
529 	caddr_t				bsnpa;
530 
531 	esis_stat.es_rdrcvd++;
532 
533 	/* intermediate systems ignore redirects */
534 	if (iso_systype & SNPA_IS)
535 		goto bad;
536 
537 	CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
538 	if (len <= 0)
539 		goto bad;
540 
541 	/* Extract DA */
542 	da = (struct iso_addr *)buf;
543 	if ((len -=  (optlen = *buf++)) <= 0)
544 		goto bad;
545 	buf += optlen;
546 
547 	/* Extract better snpa */
548 	if ((len -=  (bsnpalen = *buf++)) < 0)
549 		goto bad;
550 	bsnpa = (caddr_t)buf;
551 	buf += optlen;
552 
553 	/* Extract NET if present */
554 	if (len) {
555 		net = (struct iso_addr *)buf;
556 		if ((len -=  (optlen = *buf++)) < 0)
557 			goto bad;
558 		buf += optlen;
559 	}
560 
561 	/* process options */
562 	while (len > 0) {
563 		switch (*buf++) {
564 		case ESISOVAL_SNPAMASK:
565 			if (snpamask) /* duplicate */
566 				goto bad;
567 			snpamask = (struct iso_addr *)buf;
568 			break;
569 
570 		case ESISOVAL_NETMASK:
571 			if (netmask) /* duplicate */
572 				goto bad;
573 			netmask = (struct iso_addr *)buf;
574 		}
575 		if ((len -=  (optlen = *buf)) < 0)
576 			goto bad;
577 		buf += 1 + optlen;
578 	}
579 
580 	IFDEBUG(D_ESISINPUT)
581 		printf("esis_rdinput: rd: ht %d, da %s\n", ht, clnp_iso_addrp(da));
582 		if (net)
583 			printf("\t: net %s\n", clnp_iso_addrp(net));
584 	ENDDEBUG
585 	/*
586 	 *	If netl is zero, then redirect is to an ES. We need to add an entry
587 	 *	to the snpa cache for (destination, better snpa).
588 	 *	If netl is not zero, then the redirect is to an IS. In this
589 	 *	case, add an snpa cache entry for (net, better snpa).
590 	 *
591 	 *	If the redirect is to an IS, add a route entry towards that
592 	 *	IS.
593 	 */
594 	if ((net == 0) || (snpamask)) {
595 		/* redirect to an ES */
596 		snpac_add(shp->snh_ifp, da, bsnpa, bsnpalen, SNPA_ES, ht);
597 	} else {
598 		struct iso_addr bsnpa_ia;
599 
600 		snpac_add(shp->snh_ifp, net, bsnpa, bsnpalen, SNPA_IS, ht);
601 		bcopy(bsnpa, bsnpa_ia.isoa_genaddr, bsnpa_ia.isoa_len = 1 + bsnpalen);
602 		bsnpa_ia.isoa_genaddr[0] = AFI_SNA;
603 		snpac_addrt(da, &bsnpa_ia, net, netmask);
604 	}
605 bad:
606 	m_freem(m0);
607 }
608 
609 /*
610  * FUNCTION:		esis_config
611  *
612  * PURPOSE:			Report configuration
613  *
614  * RETURNS:
615  *
616  * SIDE EFFECTS:
617  *
618  * NOTES:			Called every esis_config_time seconds
619  */
620 esis_config()
621 {
622 	register struct ifnet	*ifp;
623 
624 	timeout(esis_config, (caddr_t)0, hz * esis_config_time);
625 
626 	/*
627 	 *	Report configuration for each interface that
628 	 *	- is UP
629 	 *	- is not loopback
630 	 *	- has broadcast capabilities
631 	 *	- has an ISO address
632 	 */
633 
634 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
635 		if ((ifp->if_flags & IFF_UP) &&
636 			(ifp->if_flags & IFF_BROADCAST) &&
637 			((ifp->if_flags & IFF_LOOPBACK) == 0)) {
638 			/* search for an ISO address family */
639 			struct ifaddr	*ia;
640 
641 			for (ia = ifp->if_addrlist; ia; ia = ia->ifa_next) {
642 				if (ia->ifa_addr->sa_family == AF_ISO) {
643 					esis_shoutput(ifp,
644 						iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
645 						esis_holding_time,
646 						(caddr_t)(iso_systype & SNPA_ES ? all_is.sc_snpa :
647 						all_es.sc_snpa), 6);
648 					break;
649 				}
650 			}
651 		}
652 	}
653 }
654 
655 /*
656  * FUNCTION:		esis_shoutput
657  *
658  * PURPOSE:			Transmit an esh or ish pdu
659  *
660  * RETURNS:			nothing
661  *
662  * SIDE EFFECTS:
663  *
664  * NOTES:
665  */
666 esis_shoutput(ifp, type, ht, sn_addr, sn_len)
667 struct ifnet	*ifp;
668 int				type;
669 short			ht;
670 caddr_t 		sn_addr;
671 int				sn_len;
672 {
673 	struct mbuf			*m, *m0;
674 	caddr_t				cp, naddrp;
675 	int					naddr = 0;
676 	struct esis_fixed	*pdu;
677 	struct ifaddr		*ifa;
678 	int					len;
679 	struct sockaddr_iso	siso;
680 
681 	if (type == ESIS_ESH)
682 		esis_stat.es_eshsent++;
683 	else if (type == ESIS_ISH)
684 		esis_stat.es_ishsent++;
685 	else {
686 		printf("esis_shoutput: bad pdu type\n");
687 		return;
688 	}
689 
690 	IFDEBUG(D_ESISOUTPUT)
691 		int	i;
692 		printf("esis_shoutput: ifp x%x (%s%d), %s, ht %d, to: [%d] ",
693 			ifp, ifp->if_name, ifp->if_unit, type == ESIS_ESH ? "esh" : "ish",
694 			ht, sn_len);
695 		for (i=0; i<sn_len; i++)
696 			printf("%x%c", *(sn_addr+i), i < (sn_len-1) ? ':' : ' ');
697 		printf("\n");
698 	ENDDEBUG
699 
700 	if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
701 		esis_stat.es_nomem++;
702 		return;
703 	}
704 	bzero(mtod(m, caddr_t), MHLEN);
705 
706 	pdu = mtod(m, struct esis_fixed *);
707 	naddrp = cp = (caddr_t)(pdu + 1);
708 	len = sizeof(struct esis_fixed);
709 
710 	/*
711 	 *	Build fixed part of header
712 	 */
713 	pdu->esis_proto_id = ISO9542_ESIS;
714 	pdu->esis_vers = ESIS_VERSION;
715 	pdu->esis_type = type;
716 	HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
717 
718 	if (type == ESIS_ESH) {
719 		cp++;
720 		len++;
721 	}
722 
723 	m->m_len = len;
724 	for (ifa = ifp->if_addrlist; ifa; ifa=ifa->ifa_next) {
725 		if (ifa->ifa_addr->sa_family == AF_ISO) {
726 			IFDEBUG(D_ESISOUTPUT)
727 				printf("esis_shoutput: adding nsap %s\n",
728 					clnp_iso_addrp(&IA_SIS(ifa)->siso_addr));
729 			ENDDEBUG
730 			if (!esis_insert_addr(&cp, &len, &IA_SIS(ifa)->siso_addr, m)) {
731 				EXTEND_PACKET(m, m0, cp);
732 				(void) esis_insert_addr(&cp, &len, &IA_SIS(ifa)->siso_addr, m);
733 			}
734 			naddr++;
735 			if (type == ESIS_ISH)
736 				break;
737 		}
738 	}
739 
740 	if (type == ESIS_ESH)
741 		*naddrp = naddr;
742 
743 	m0->m_pkthdr.len = len;
744 	pdu->esis_hdr_len = len;
745 	iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len);
746 
747 	bzero((caddr_t)&siso, sizeof(siso));
748 	siso.siso_family = AF_ISO;
749 	siso.siso_data[0] = AFI_SNA;
750 	siso.siso_nlen = sn_len + 1;
751 	siso.siso_len  = sn_len + 6;
752 	bcopy(sn_addr, siso.siso_data + 1, (unsigned)sn_len);
753 	(ifp->if_output)(ifp, m0, &siso);
754 }
755 
756 /*
757  * FUNCTION:		esis_ctlinput
758  *
759  * PURPOSE:			Handle the PRC_IFDOWN transition
760  *
761  * RETURNS:			nothing
762  *
763  * SIDE EFFECTS:
764  *
765  * NOTES:			Calls snpac_flush for interface specified.
766  *					The loop through iso_ifaddr is stupid because
767  *					back in if_down, we knew the ifp...
768  */
769 esis_ctlinput(req, siso)
770 int						req;		/* request: we handle only PRC_IFDOWN */
771 struct sockaddr_iso		*siso;		/* address of ifp */
772 {
773 	register struct iso_ifaddr *ia;	/* scan through interface addresses */
774 
775 	if (req == PRC_IFDOWN)
776 		for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
777 			if (iso_addrmatch(IA_SIS(ia), siso))
778 				snpac_flushifp(ia->ia_ifp);
779 		}
780 }
781 
782 #endif	ISO
783