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