xref: /csrg-svn/sys/netiso/esis.c (revision 36379)
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 "../h/types.h"
34 #include "../h/param.h"
35 #include "../h/mbuf.h"
36 #include "../h/domain.h"
37 #include "../h/protosw.h"
38 #include "../h/dir.h"
39 #include "../h/user.h"
40 #include "../h/socket.h"
41 #include "../h/socketvar.h"
42 #include "../h/errno.h"
43 #include "../h/kernel.h"
44 
45 #include "../net/if.h"
46 #include "../net/route.h"
47 
48 #include "../netiso/iso.h"
49 #include "../netiso/iso_pcb.h"
50 #include "../netiso/iso_var.h"
51 #include "../netiso/iso_snpac.h"
52 #include "../netiso/clnl.h"
53 #include "../netiso/clnp.h"
54 #include "../netiso/clnp_stat.h"
55 #include "../netiso/argo_debug.h"
56 #include "../netiso/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, len, 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_len = (len);\
80 		(m) = (m)->m_next;\
81 		(len) = 0;\
82 		(cp) = mtod((m), caddr_t);\
83 	}
84 /*
85  * FUNCTION:		esis_init
86  *
87  * PURPOSE:			Initialize the kernel portion of esis protocol
88  *
89  * RETURNS:			nothing
90  *
91  * SIDE EFFECTS:
92  *
93  * NOTES:
94  */
95 esis_init()
96 {
97 	extern struct clnl_protosw clnl_protox[256];
98 	int esis_input();
99 	int snpac_age();
100 	int	esis_config();
101 #ifdef	ISO_X25ESIS
102 	x25esis_input();
103 #endif	ISO_X25ESIS
104 
105 	esis_pcb.isop_next = esis_pcb.isop_prev = &esis_pcb;
106 
107 	clnl_protox[ISO9542_ESIS].clnl_input = esis_input;
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 esis_usrreq(so, req, m, nam, rights)
128 struct socket	*so;		/* socket: used only to get to this code */
129 int				req;		/* request */
130 struct mbuf		*m;			/* data for request */
131 struct mbuf		*nam;		/* optional name */
132 struct mbuf		*rights;	/* optional rights */
133 {
134 	if (m != NULL)
135 		m_freem(m);
136 
137 	return(EOPNOTSUPP);
138 }
139 
140 /*
141  * FUNCTION:		esis_input
142  *
143  * PURPOSE:			Process an incoming esis packet
144  *
145  * RETURNS:			nothing
146  *
147  * SIDE EFFECTS:
148  *
149  * NOTES:
150  */
151 esis_input(m0, shp)
152 struct mbuf		*m0;		/* ptr to first mbuf of pkt */
153 struct snpa_hdr	*shp;	/* subnetwork header */
154 {
155 	struct isopcb		*isop;
156 	struct esis_fixed	*pdu = mtod(m0, struct esis_fixed *);
157 
158 	IFDEBUG(D_ESISINPUT)
159 		int i;
160 
161 		printf("esis_input: pdu on ifp x%x (%s%d): from:", shp->snh_ifp,
162 			shp->snh_ifp->if_name, shp->snh_ifp->if_unit);
163 		for (i=0; i<6; i++)
164 			printf("%x%c", shp->snh_shost[i]&0xff, (i<5) ? ':' : ' ');
165 		printf(" to:");
166 		for (i=0; i<6; i++)
167 			printf("%x%c", shp->snh_dhost[i]&0xff, (i<5) ? ':' : ' ');
168 		printf("\n");
169 	ENDDEBUG
170 
171 	/*
172 	 *	check checksum if necessary
173 	 */
174 	if (ESIS_CKSUM_REQUIRED(pdu) && iso_check_csum(m0, pdu->esis_hdr_len)) {
175 		esis_stat.es_badcsum++;
176 		goto bad;
177 	}
178 
179 	/* check version */
180 	if (pdu->esis_vers != ESIS_VERSION) {
181 		esis_stat.es_badvers++;
182 		goto bad;
183 	}
184 
185 	switch(pdu->esis_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 		int	i;
239 		printf("esis_rdoutput: ifp x%x (%s%d), ht %d, m x%x, oidx x%x\n",
240 			ifp, ifp->if_name, ifp->if_unit, esis_holding_time, inbound_m,
241 			inbound_oidx);
242 		printf("\tdestination: %s\n", clnp_iso_addrp(rd_dstnsap));
243 		printf("\tredirected toward:%s\n", clnp_iso_addrp(&nhop_sc->sc_nsap));
244 	ENDDEBUG
245 
246 	if ((m0 = m = m_getclr(M_DONTWAIT, MT_HEADER)) == NULL) {
247 		esis_stat.es_nomem++;
248 		return;
249 	}
250 
251 	pdu = mtod(m, struct esis_fixed *);
252 	cp = (caddr_t)pdu + sizeof(struct esis_fixed);
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, MLEN - len);
265 
266 	/* Insert the snpa of better next hop */
267 	*cp++ = nhop_sc->sc_len;
268 	bcopy(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, MLEN - len);
285 	} else {
286 		*cp++ = 0;	/* NETL */
287 		len++;
288 	}
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 (MLEN-len < optlen) {
310 			total_len += len;
311 			EXTEND_PACKET(m, m0, len, cp);
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(inbound_m + inbound_oidx->cni_qos_formatp - 2, cp,
321 				inbound_oidx->cni_qos_len + 2);
322 			len += inbound_oidx->cni_qos_len + 2;
323 		}
324 		if (inbound_oidx->cni_priorp) {
325 			bcopy(inbound_m + inbound_oidx->cni_priorp - 2, cp, 3);
326 			len += 3;
327 		}
328 		if (inbound_oidx->cni_securep) {
329 			bcopy(inbound_m + inbound_oidx->cni_securep - 2, cp,
330 				inbound_oidx->cni_secure_len + 2);
331 			len += inbound_oidx->cni_secure_len + 2;
332 		}
333 	}
334 
335 	m->m_len = len;
336 	total_len += len;
337 	pdu->esis_hdr_len = total_len;
338 	iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len);
339 
340 	siso.siso_family = AF_ISO;
341 	siso.siso_addr.isoa_afi = AFI_SNA;
342 	siso.siso_addr.isoa_len = 6 + 1;	/* should be taken from snpa_hdr */
343 										/* +1 is for AFI */
344 	bcopy(inbound_shp->snh_shost, siso.siso_addr.sna_idi, 6);
345 	(ifp->if_output)(ifp, m0, &siso);
346 }
347 
348 /*
349  * FUNCTION:		esis_extract_addr
350  *
351  * PURPOSE:			Remove an addr from a buffer, and stuff in iso_addr
352  *
353  * RETURNS:			true if the address was complete, else false
354  *
355  * SIDE EFFECTS:	Increment buf and decrement len according to the
356  *					size of the iso_addr
357  *
358  * NOTES:
359  */
360 esis_extract_addr(buf, isoa, len)
361 caddr_t			*buf;		/* ptr to buffer to put address into */
362 struct iso_addr	*isoa;		/* ptr to address */
363 int				*len;		/* ptr to length of buffer */
364 {
365 	caddr_t		bp = *buf;
366 
367 	if (*len <= 0)
368 		return(0);
369 
370 	bzero((caddr_t)isoa, sizeof (struct iso_addr));
371 	isoa->isoa_len = *bp++;
372 	*len -= 1;
373 	if (isoa->isoa_len > *len)
374 		return(0);	/* too big */
375 	bcopy(bp, (caddr_t)isoa, isoa->isoa_len);
376 	*len -= isoa->isoa_len;
377 	bp += isoa->isoa_len;
378 	*buf = bp;
379 
380 	return(1);
381 }
382 
383 /*
384  * FUNCTION:		esis_insert_addr
385  *
386  * PURPOSE:			Insert an iso_addr into a buffer
387  *
388  * RETURNS:			true if buffer was big enough, else false
389  *
390  * SIDE EFFECTS:	Increment buf & len according to size of iso_addr
391  *
392  * NOTES:			Plus 1 here is for length byte
393  */
394 esis_insert_addr(buf, len, isoa, remaining)
395 caddr_t			*buf;		/* ptr to buffer to put address into */
396 int				*len;		/* ptr to length of buffer so far */
397 struct iso_addr	*isoa;		/* ptr to address */
398 int				remaining;	/* length of buffer */
399 {
400 	caddr_t		bp = *buf;
401 
402 	if ((isoa->isoa_len+1) > remaining)
403 		return(0);
404 	*bp++ = isoa->isoa_len;
405 	*len += 1;
406 	bcopy((caddr_t)isoa, bp, isoa->isoa_len);
407 	bp += isoa->isoa_len;
408 	*len += isoa->isoa_len;
409 	*buf = bp;
410 	return(1);
411 }
412 
413 /*
414 
415 /*
416  * FUNCTION:		esis_eshinput
417  *
418  * PURPOSE:			Process an incoming ESH pdu
419  *
420  * RETURNS:			nothing
421  *
422  * SIDE EFFECTS:
423  *
424  * NOTES:
425  */
426 esis_eshinput(m, shp)
427 struct mbuf		*m;	/* esh pdu */
428 struct snpa_hdr	*shp;	/* subnetwork header */
429 {
430 	struct esis_fixed	*pdu = mtod(m, struct esis_fixed *);
431 	u_short				ht;		/* holding time */
432 	struct iso_addr		nsap;
433 	int					naddr;
434 	caddr_t				buf = (caddr_t)pdu + sizeof(struct esis_fixed);
435 	int					len = pdu->esis_hdr_len - sizeof(struct esis_fixed);
436 
437 	esis_stat.es_eshrcvd++;
438 
439 	CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
440 
441 	if (len > 0) {
442 		naddr = *buf++;
443 		len--;
444 	} else
445 		goto bad;
446 
447 	IFDEBUG(D_ESISINPUT)
448 		printf("esis_eshinput: esh: ht %d, naddr %d\n", ht, naddr);
449 	ENDDEBUG
450 
451 	while (naddr-- > 0) {
452 		if (esis_extract_addr(&buf, &nsap, &len)) {
453 			int new_entry = (snpac_look(&nsap) == NULL);
454 
455 			IFDEBUG(D_ESISINPUT)
456 				printf("esis_eshinput: nsap %s is %s\n",
457 					clnp_iso_addrp(&nsap), new_entry ? "new" : "old");
458 			ENDDEBUG
459 
460 			snpac_add(shp->snh_ifp, &nsap, shp->snh_shost, 6, SNPA_ES, ht);
461 			if (new_entry)
462 				esis_shoutput(shp->snh_ifp,
463 					iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
464 					esis_holding_time, shp->snh_shost, 6);
465 		} else {
466 			esis_stat.es_toosmall++;
467 			break;
468 		}
469 	}
470 
471 bad:
472 	m_freem(m);
473 }
474 
475 /*
476  * FUNCTION:		esis_ishinput
477  *
478  * PURPOSE:			process an incoming ISH pdu
479  *
480  * RETURNS:
481  *
482  * SIDE EFFECTS:
483  *
484  * NOTES:
485  */
486 esis_ishinput(m, shp)
487 struct mbuf		*m;	/* esh pdu */
488 struct snpa_hdr	*shp;	/* subnetwork header */
489 {
490 	struct esis_fixed	*pdu = mtod(m, struct esis_fixed *);
491 	u_short				ht;		/* holding time */
492 	struct iso_addr		nsap;
493 	caddr_t				buf = (caddr_t)pdu + sizeof(struct esis_fixed);
494 	int					len = pdu->esis_hdr_len - sizeof(struct esis_fixed);
495 
496 	esis_stat.es_ishrcvd++;
497 	CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
498 
499 	IFDEBUG(D_ESISINPUT)
500 		printf("esis_ishinput: ish: ht %d\n", ht);
501 	ENDDEBUG
502 
503 	if (esis_extract_addr(&buf, &nsap, &len)) {
504 		int new_entry = (snpac_look(&nsap) == NULL);
505 
506 		IFDEBUG(D_ESISINPUT)
507 			printf("esis_ishinput: nsap %s is %s\n",
508 				clnp_iso_addrp(&nsap), new_entry ? "new" : "old");
509 		ENDDEBUG
510 
511 		snpac_add(shp->snh_ifp, &nsap, shp->snh_shost, 6, SNPA_IS, ht);
512 		if (new_entry)
513 			esis_shoutput(shp->snh_ifp,
514 				iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
515 				esis_holding_time, shp->snh_shost, 6);
516 	} else {
517 		esis_stat.es_toosmall++;
518 	}
519 	m_freem(m);
520 }
521 
522 /*
523  * FUNCTION:		esis_rdinput
524  *
525  * PURPOSE:			Process an incoming RD pdu
526  *
527  * RETURNS:
528  *
529  * SIDE EFFECTS:
530  *
531  * NOTES:
532  */
533 esis_rdinput(m0, shp)
534 struct mbuf		*m0;	/* esh pdu */
535 struct snpa_hdr	*shp;	/* subnetwork header */
536 {
537 	struct esis_fixed	*pdu = mtod(m0, struct esis_fixed *);
538 	u_short				ht;		/* holding time */
539 	struct iso_addr		da;
540 	caddr_t				bsnpa;
541 	int					bsnpalen;
542 	struct iso_addr		net;
543 	int					netl;
544 	caddr_t				buf = (caddr_t)pdu + sizeof(struct esis_fixed);
545 	int					len = pdu->esis_hdr_len - sizeof(struct esis_fixed);
546 
547 	esis_stat.es_rdrcvd++;
548 
549 	/* intermediate systems ignore redirects */
550 	if (iso_systype & SNPA_IS)
551 		goto bad;
552 
553 	CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
554 
555 	/* Extract DA */
556 	if (!esis_extract_addr(&buf, &da, &len)) {
557 		esis_stat.es_toosmall++;
558 		goto bad;
559 	}
560 
561 	/* Extract better snpa */
562 	bsnpalen = *buf++;
563 	if (bsnpalen > len) {
564 		esis_stat.es_toosmall++;
565 		goto bad;
566 	} else {
567 		bsnpa = buf;
568 		buf += bsnpalen;
569 		len -= bsnpalen;
570 	}
571 
572 	/* Extract NET if present */
573 	if ((netl = *buf) > 0) {
574 		if (!esis_extract_addr(&buf, &net, &len)) {
575 			esis_stat.es_toosmall++;
576 			goto bad;
577 		}
578 	}
579 
580 	IFDEBUG(D_ESISINPUT)
581 		printf("esis_rdinput: rd: ht %d, da %s\n", ht, clnp_iso_addrp(&da));
582 		if (netl)
583 			printf("\t: net %s\n", clnp_iso_addrp(&net));
584 	ENDDEBUG
585 
586 	/*
587 	 *	If netl is zero, then redirect is to an ES. We need to add an entry
588 	 *	to the snpa cache for (destination, better snpa).
589 	 *	If netl is not zero, then the redirect is to an IS. In this
590 	 *	case, add an snpa cache entry for (net, better snpa).
591 	 *
592 	 *	If the redirect is to an IS, add a route entry towards that
593 	 *	IS.
594 	 */
595 	if (netl == 0) {
596 		/* redirect to an ES */
597 		snpac_add(shp->snh_ifp, &da, bsnpa, bsnpalen, SNPA_ES, ht);
598 	} else {
599 		snpac_add(shp->snh_ifp, &net, bsnpa, bsnpalen, SNPA_IS, ht);
600 		snpac_addrt(&da, &net);
601 	}
602 bad:
603 	m_freem(m0);
604 }
605 
606 /*
607  * FUNCTION:		esis_config
608  *
609  * PURPOSE:			Report configuration
610  *
611  * RETURNS:
612  *
613  * SIDE EFFECTS:
614  *
615  * NOTES:			Called every esis_config_time seconds
616  */
617 esis_config()
618 {
619 	register struct ifnet	*ifp;
620 
621 	timeout(esis_config, (caddr_t)0, hz * esis_config_time);
622 
623 	/*
624 	 *	Report configuration for each interface that
625 	 *	- is UP
626 	 *	- is not loopback
627 	 *	- has broadcast capabilities
628 	 *	- has an ISO address
629 	 */
630 
631 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
632 		if ((ifp->if_flags & IFF_UP) &&
633 			(ifp->if_flags & IFF_BROADCAST) &&
634 			((ifp->if_flags & IFF_LOOPBACK) == 0)) {
635 			/* search for an ISO address family */
636 			struct ifaddr	*ia;
637 
638 			for (ia = ifp->if_addrlist; ia; ia = ia->ifa_next) {
639 				if (ia->ifa_addr.sa_family == AF_ISO) {
640 					esis_shoutput(ifp,
641 						iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
642 						esis_holding_time,
643 						iso_systype & SNPA_ES ? all_is.sc_snpa :
644 						all_es.sc_snpa, 6);
645 					break;
646 				}
647 			}
648 		}
649 	}
650 }
651 
652 /*
653  * FUNCTION:		esis_shoutput
654  *
655  * PURPOSE:			Transmit an esh or ish pdu
656  *
657  * RETURNS:			nothing
658  *
659  * SIDE EFFECTS:
660  *
661  * NOTES:
662  */
663 esis_shoutput(ifp, type, ht, sn_addr, sn_len)
664 struct ifnet	*ifp;
665 int				type;
666 short			ht;
667 caddr_t 		sn_addr;
668 int				sn_len;
669 {
670 	struct mbuf			*m, *m0;
671 	caddr_t				cp, naddrp;
672 	int					naddr = 0;
673 	struct esis_fixed	*pdu;
674 	struct ifaddr		*ifa;
675 	int					len, total_len = 0;
676 	struct sockaddr_iso	siso;
677 
678 	if (type == ESIS_ESH)
679 		esis_stat.es_eshsent++;
680 	else if (type == ESIS_ISH)
681 		esis_stat.es_ishsent++;
682 	else {
683 		printf("esis_shoutput: bad pdu type\n");
684 		return;
685 	}
686 
687 	IFDEBUG(D_ESISOUTPUT)
688 		int	i;
689 		printf("esis_shoutput: ifp x%x (%s%d), %s, ht %d, to: [%d] ",
690 			ifp, ifp->if_name, ifp->if_unit, type == ESIS_ESH ? "esh" : "ish",
691 			ht, sn_len);
692 		for (i=0; i<sn_len; i++)
693 			printf("%x%c", *(sn_addr+i), i < (sn_len-1) ? ':' : ' ');
694 		printf("\n");
695 	ENDDEBUG
696 
697 	if ((m0 = m = m_getclr(M_DONTWAIT, MT_HEADER)) == NULL) {
698 		esis_stat.es_nomem++;
699 		return;
700 	}
701 
702 	pdu = mtod(m, struct esis_fixed *);
703 	naddrp = cp = (caddr_t)pdu + sizeof(struct esis_fixed);
704 	len = sizeof(struct esis_fixed);
705 
706 	/*
707 	 *	Build fixed part of header
708 	 */
709 	pdu->esis_proto_id = ISO9542_ESIS;
710 	pdu->esis_vers = ESIS_VERSION;
711 	pdu->esis_type = type;
712 	HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
713 
714 	if (type == ESIS_ESH) {
715 		cp++;
716 		len++;
717 	}
718 
719 	for (ifa = ifp->if_addrlist; ifa; ifa=ifa->ifa_next) {
720 		if (ifa->ifa_addr.sa_family == AF_ISO) {
721 			IFDEBUG(D_ESISOUTPUT)
722 				printf("esis_shoutput: adding nsap %s\n",
723 					clnp_iso_addrp(&IA_SIS(ifa)->siso_addr));
724 			ENDDEBUG
725 			if (!esis_insert_addr(&cp, &len, &IA_SIS(ifa)->siso_addr,
726 				MLEN - len)) {
727 				total_len += len;
728 				EXTEND_PACKET(m, m0, len, cp);
729 				(void) esis_insert_addr(&cp, &len, &IA_SIS(ifa)->siso_addr,
730 					MLEN - len);
731 			}
732 			naddr++;
733 			if (type == ESIS_ISH)
734 				break;
735 		}
736 	}
737 
738 	if (type == ESIS_ESH)
739 		*naddrp = naddr;
740 
741 	m->m_len = len;
742 	total_len += len;
743 	pdu->esis_hdr_len = total_len;
744 	iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len);
745 
746 	siso.siso_family = AF_ISO;
747 	siso.siso_addr.isoa_afi = AFI_SNA;
748 	siso.siso_addr.isoa_len = sn_len + 1;
749 	bcopy(sn_addr, siso.siso_addr.sna_idi, sn_len);
750 	(ifp->if_output)(ifp, m0, &siso);
751 }
752 
753 /*
754  * FUNCTION:		esis_ctlinput
755  *
756  * PURPOSE:			Handle the PRC_IFDOWN transition
757  *
758  * RETURNS:			nothing
759  *
760  * SIDE EFFECTS:
761  *
762  * NOTES:			Calls snpac_flush for interface specified.
763  *					The loop through iso_ifaddr is stupid because
764  *					back in if_down, we knew the ifp...
765  */
766 esis_ctlinput(req, siso)
767 int						req;		/* request: we handle only PRC_IFDOWN */
768 struct sockaddr_iso		*siso;		/* address of ifp */
769 {
770 	register struct iso_ifaddr *ia;	/* scan through interface addresses */
771 
772 	for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
773 		if (iso_addrmatch(IA_SIS(ia), siso))
774 			snpac_flushifp(ia->ia_ifp);
775 	}
776 }
777 
778 #endif	ISO
779