xref: /original-bsd/sys/netiso/esis.c (revision 73949c1b)
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.16 (Berkeley) 01/09/91 */
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 "systm.h"
37 #include "mbuf.h"
38 #include "domain.h"
39 #include "protosw.h"
40 #include "user.h"
41 #include "socket.h"
42 #include "socketvar.h"
43 #include "errno.h"
44 #include "kernel.h"
45 
46 #include "../net/if.h"
47 #include "../net/if_dl.h"
48 #include "../net/route.h"
49 #include "../net/raw_cb.h"
50 
51 #include "iso.h"
52 #include "iso_pcb.h"
53 #include "iso_var.h"
54 #include "iso_snpac.h"
55 #include "clnl.h"
56 #include "clnp.h"
57 #include "clnp_stat.h"
58 #include "esis.h"
59 #include "argo_debug.h"
60 
61 /*
62  *	Global variables to esis implementation
63  *
64  *	esis_holding_time - the holding time (sec) parameter for outgoing pdus
65  *	esis_config_time  - the frequency (sec) that hellos are generated
66  *	esis_esconfig_time - suggested es configuration time placed in the
67  *						ish.
68  *
69  */
70 struct rawcb	esis_pcb;
71 int				esis_sendspace = 2048;
72 int				esis_recvspace = 2048;
73 short			esis_holding_time = ESIS_HT;
74 short			esis_config_time = ESIS_CONFIG;
75 short			esis_esconfig_time = ESIS_CONFIG;
76 extern int		iso_systype;
77 struct sockaddr_dl	esis_dl = { sizeof(esis_dl), AF_LINK };
78 extern char		all_es_snpa[], all_is_snpa[];
79 
80 #define EXTEND_PACKET(m, mhdr, cp)\
81 	if (((m)->m_next = m_getclr(M_DONTWAIT, MT_HEADER)) == NULL) {\
82 		esis_stat.es_nomem++;\
83 		m_freem(mhdr);\
84 		return;\
85 	} else {\
86 		(m) = (m)->m_next;\
87 		(cp) = mtod((m), caddr_t);\
88 	}
89 /*
90  * FUNCTION:		esis_init
91  *
92  * PURPOSE:			Initialize the kernel portion of esis protocol
93  *
94  * RETURNS:			nothing
95  *
96  * SIDE EFFECTS:
97  *
98  * NOTES:
99  */
100 esis_init()
101 {
102 	extern struct clnl_protosw clnl_protox[256];
103 	int	esis_input(), isis_input();
104 	int	esis_config(), snpac_age();
105 #ifdef	ISO_X25ESIS
106 	int	x25esis_input();
107 #endif	ISO_X25ESIS
108 
109 	esis_pcb.rcb_next = esis_pcb.rcb_prev = &esis_pcb;
110 	llinfo_llc.lc_next = llinfo_llc.lc_prev = &llinfo_llc;
111 
112 	timeout(snpac_age, (caddr_t)0, hz);
113 	timeout(esis_config, (caddr_t)0, hz);
114 
115 	clnl_protox[ISO9542_ESIS].clnl_input = esis_input;
116 	clnl_protox[ISO10589_ISIS].clnl_input = isis_input;
117 #ifdef	ISO_X25ESIS
118 	clnl_protox[ISO9542X25_ESIS].clnl_input = x25esis_input;
119 #endif	ISO_X25ESIS
120 }
121 
122 /*
123  * FUNCTION:		esis_usrreq
124  *
125  * PURPOSE:			Handle user level esis requests
126  *
127  * RETURNS:			0 or appropriate errno
128  *
129  * SIDE EFFECTS:
130  *
131  */
132 /*ARGSUSED*/
133 esis_usrreq(so, req, m, nam, control)
134 struct socket	*so;		/* socket: used only to get to this code */
135 int				req;		/* request */
136 struct mbuf		*m;			/* data for request */
137 struct mbuf		*nam;		/* optional name */
138 struct mbuf		*control;	/* optional control */
139 {
140 	struct rawcb *rp = sotorawcb(so);
141 	int error = 0;
142 
143 	if (suser(u.u_cred, &u.u_acflag)) {
144 		error = EACCES;
145 		goto release;
146 	}
147 	if (rp == NULL && req != PRU_ATTACH) {
148 		error = EINVAL;
149 		goto release;
150 	}
151 
152 	switch (req) {
153 	case PRU_ATTACH:
154 		if (rp != NULL) {
155 			error = EINVAL;
156 			break;
157 		}
158 		MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK);
159 		if (so->so_pcb = (caddr_t)rp) {
160 			bzero(so->so_pcb, sizeof(*rp));
161 			insque(rp, &esis_pcb);
162 			rp->rcb_socket = so;
163 			error = soreserve(so, esis_sendspace, esis_recvspace);
164 		} else
165 			error = ENOBUFS;
166 		break;
167 
168 	case PRU_SEND:
169 		if (nam == NULL) {
170 			error = EINVAL;
171 			break;
172 		}
173 		/* error checking here */
174 		error = isis_output(mtod(nam,struct sockaddr_dl *), m);
175 		m = NULL;
176 		break;
177 
178 	case PRU_DETACH:
179 		raw_detach(rp);
180 		break;
181 
182 	case PRU_SHUTDOWN:
183 		socantsendmore(so);
184 		break;
185 
186 	case PRU_ABORT:
187 		soisdisconnected(so);
188 		raw_detach(rp);
189 		break;
190 
191 	case PRU_SENSE:
192 		return (0);
193 
194 	default:
195 		return (EOPNOTSUPP);
196 	}
197 release:
198 	if (m != NULL)
199 		m_freem(m);
200 
201 	return (error);
202 }
203 
204 /*
205  * FUNCTION:		esis_input
206  *
207  * PURPOSE:			Process an incoming esis packet
208  *
209  * RETURNS:			nothing
210  *
211  * SIDE EFFECTS:
212  *
213  * NOTES:
214  */
215 esis_input(m0, shp)
216 struct mbuf		*m0;		/* ptr to first mbuf of pkt */
217 struct snpa_hdr	*shp;	/* subnetwork header */
218 {
219 	register struct esis_fixed	*pdu = mtod(m0, struct esis_fixed *);
220 	register int type;
221 
222 	/*
223 	 *	check checksum if necessary
224 	 */
225 	if (ESIS_CKSUM_REQUIRED(pdu) && iso_check_csum(m0, (int)pdu->esis_hdr_len)) {
226 		esis_stat.es_badcsum++;
227 		goto bad;
228 	}
229 
230 	/* check version */
231 	if (pdu->esis_vers != ESIS_VERSION) {
232 		esis_stat.es_badvers++;
233 		goto bad;
234 	}
235 	type = pdu->esis_type & 0x1f;
236 	switch (type) {
237 		case ESIS_ESH:
238 			esis_eshinput(m0, shp);
239 			break;
240 
241 		case ESIS_ISH:
242 			esis_ishinput(m0, shp);
243 			break;
244 
245 		case ESIS_RD:
246 			esis_rdinput(m0, shp);
247 			break;
248 
249 		default:
250 			esis_stat.es_badtype++;
251 	}
252 
253 bad:
254 	if (esis_pcb.rcb_next != &esis_pcb)
255 		isis_input(m0, shp);
256 	else
257 		m_freem(m0);
258 }
259 
260 /*
261  * FUNCTION:		esis_rdoutput
262  *
263  * PURPOSE:			Transmit a redirect pdu
264  *
265  * RETURNS:			nothing
266  *
267  * SIDE EFFECTS:
268  *
269  * NOTES:			Assumes there is enough space for fixed part of header,
270  *					DA, BSNPA and NET in first mbuf.
271  */
272 esis_rdoutput(inbound_shp, inbound_m, inbound_oidx, rd_dstnsap, rt)
273 struct snpa_hdr		*inbound_shp;	/* snpa hdr from incoming packet */
274 struct mbuf			*inbound_m;		/* incoming pkt itself */
275 struct clnp_optidx	*inbound_oidx;	/* clnp options assoc with incoming pkt */
276 struct iso_addr		*rd_dstnsap;	/* ultimate destination of pkt */
277 struct rtentry		*rt;			/* snpa cache info regarding next hop of
278 										pkt */
279 {
280 	struct mbuf			*m, *m0;
281 	caddr_t				cp;
282 	struct esis_fixed	*pdu;
283 	int					len, total_len = 0;
284 	struct sockaddr_iso	siso;
285 	struct ifnet 		*ifp = inbound_shp->snh_ifp;
286 	struct sockaddr_dl *sdl;
287 	struct iso_addr *rd_gwnsap;
288 
289 	if (rt->rt_flags & RTF_GATEWAY) {
290 		rd_gwnsap = &((struct sockaddr_iso *)rt->rt_gateway)->siso_addr;
291 		rt = rtalloc1(rt->rt_gateway, 0);
292 	} else
293 		rd_gwnsap = &((struct sockaddr_iso *)rt_key(rt))->siso_addr;
294 	if (rt == 0 || (sdl = (struct sockaddr_dl *)rt->rt_gateway) == 0 ||
295 		sdl->sdl_family != AF_LINK) {
296 		/* maybe we should have a function that you
297 		   could put in the iso_ifaddr structure
298 		   which could translate iso_addrs into snpa's
299 		   where there is a known mapping for that address type */
300 		esis_stat.es_badtype++;
301 		return;
302 	}
303 	esis_stat.es_rdsent++;
304 	IFDEBUG(D_ESISOUTPUT)
305 		printf("esis_rdoutput: ifp x%x (%s%d), ht %d, m x%x, oidx x%x\n",
306 			ifp, ifp->if_name, ifp->if_unit, esis_holding_time, inbound_m,
307 			inbound_oidx);
308 		printf("\tdestination: %s\n", clnp_iso_addrp(rd_dstnsap));
309 		printf("\tredirected toward:%s\n", clnp_iso_addrp(rd_gwnsap));
310 	ENDDEBUG
311 
312 	if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
313 		esis_stat.es_nomem++;
314 		return;
315 	}
316 	bzero(mtod(m, caddr_t), MHLEN);
317 
318 	pdu = mtod(m, struct esis_fixed *);
319 	cp = (caddr_t)(pdu + 1); /*pointer arith.; 1st byte after header */
320 	len = sizeof(struct esis_fixed);
321 
322 	/*
323 	 *	Build fixed part of header
324 	 */
325 	pdu->esis_proto_id = ISO9542_ESIS;
326 	pdu->esis_vers = ESIS_VERSION;
327 	pdu->esis_type = ESIS_RD;
328 	HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, esis_holding_time);
329 
330 	/* Insert destination address */
331 	(void) esis_insert_addr(&cp, &len, rd_dstnsap, m, 0);
332 
333 	/* Insert the snpa of better next hop */
334 	*cp++ = sdl->sdl_alen;
335 	bcopy(LLADDR(sdl), cp, sdl->sdl_alen);
336 	cp += sdl->sdl_alen;
337 	len += (sdl->sdl_alen + 1);
338 
339 	/*
340 	 *	If the next hop is not the destination, then it ought to be
341 	 *	an IS and it should be inserted next. Else, set the
342 	 *	NETL to 0
343 	 */
344 	/* PHASE2 use mask from ifp of outgoing interface */
345 	if (!iso_addrmatch1(rd_dstnsap, rd_gwnsap)) {
346 		/* this should not happen:
347 		if ((nhop_sc->sc_flags & SNPA_IS) == 0) {
348 			printf("esis_rdoutput: next hop is not dst and not an IS\n");
349 			m_freem(m0);
350 			return;
351 		} */
352 		(void) esis_insert_addr(&cp, &len, rd_gwnsap, m, 0);
353 	} else {
354 		*cp++ = 0;	/* NETL */
355 		len++;
356 	}
357 	m->m_len = len;
358 
359 	/*
360 	 *	PHASE2
361 	 *	If redirect is to an IS, add an address mask. The mask to be
362 	 *	used should be the mask present in the routing entry used to
363 	 *	forward the original data packet.
364 	 */
365 
366 	/*
367 	 *	Copy Qos, priority, or security options present in original npdu
368 	 */
369 	if (inbound_oidx) {
370 		/* THIS CODE IS CURRENTLY (mostly) UNTESTED */
371 		int optlen = 0;
372 		if (inbound_oidx->cni_qos_formatp)
373 			optlen += (inbound_oidx->cni_qos_len + 2);
374 		if (inbound_oidx->cni_priorp)	/* priority option is 1 byte long */
375 			optlen += 3;
376 		if (inbound_oidx->cni_securep)
377 			optlen += (inbound_oidx->cni_secure_len + 2);
378 		if (M_TRAILINGSPACE(m) < optlen) {
379 			EXTEND_PACKET(m, m0, cp);
380 			m->m_len = 0;
381 			/* assumes MLEN > optlen */
382 		}
383 		/* assume MLEN-len > optlen */
384 		/*
385 		 *	When copying options, copy from ptr - 2 in order to grab
386 		 *	the option code and length
387 		 */
388 		if (inbound_oidx->cni_qos_formatp) {
389 			bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_qos_formatp - 2,
390 				cp, (unsigned)(inbound_oidx->cni_qos_len + 2));
391 			cp += inbound_oidx->cni_qos_len + 2;
392 		}
393 		if (inbound_oidx->cni_priorp) {
394 			bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_priorp - 2,
395 					cp, 3);
396 			cp += 3;
397 		}
398 		if (inbound_oidx->cni_securep) {
399 			bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_securep - 2, cp,
400 				(unsigned)(inbound_oidx->cni_secure_len + 2));
401 			cp += inbound_oidx->cni_secure_len + 2;
402 		}
403 		m->m_len += optlen;
404 		len += optlen;
405 	}
406 
407 	pdu->esis_hdr_len = m0->m_pkthdr.len = len;
408 	iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len);
409 
410 	bzero((caddr_t)&siso, sizeof(siso));
411 	siso.siso_family = AF_ISO;
412 	siso.siso_data[0] = AFI_SNA;
413 	siso.siso_nlen = 6 + 1;	/* should be taken from snpa_hdr */
414 										/* +1 is for AFI */
415 	bcopy(inbound_shp->snh_shost, siso.siso_data + 1, 6);
416 	(ifp->if_output)(ifp, m0, &siso, 0);
417 }
418 
419 /*
420  * FUNCTION:		esis_insert_addr
421  *
422  * PURPOSE:			Insert an iso_addr into a buffer
423  *
424  * RETURNS:			true if buffer was big enough, else false
425  *
426  * SIDE EFFECTS:	Increment buf & len according to size of iso_addr
427  *
428  * NOTES:			Plus 1 here is for length byte
429  */
430 esis_insert_addr(buf, len, isoa, m, nsellen)
431 register caddr_t			*buf;		/* ptr to buffer to put address into */
432 int							*len;		/* ptr to length of buffer so far */
433 register struct iso_addr	*isoa;		/* ptr to address */
434 register struct mbuf		*m;			/* determine if there remains space */
435 int							nsellen;
436 {
437 	register int newlen, result = 0;
438 
439 	isoa->isoa_len -= nsellen;
440 	newlen = isoa->isoa_len + 1;
441 	if (newlen <=  M_TRAILINGSPACE(m)) {
442 		bcopy((caddr_t)isoa, *buf, newlen);
443 		*len += newlen;
444 		*buf += newlen;
445 		m->m_len += newlen;
446 		result = 1;
447 	}
448 	isoa->isoa_len += nsellen;
449 	return (result);
450 }
451 
452 #define ESIS_EXTRACT_ADDR(d, b) { d = (struct iso_addr *)(b); b += (1 + *b); \
453 	    if (b > buflim) {esis_stat.es_toosmall++; goto bad;}}
454 #define ESIS_NEXT_OPTION(b)	{ b += (2 + b[1]); \
455 	    if (b > buflim) {esis_stat.es_toosmall++; goto bad;}}
456 int ESHonly = 0;
457 /*
458 
459 /*
460  * FUNCTION:		esis_eshinput
461  *
462  * PURPOSE:			Process an incoming ESH pdu
463  *
464  * RETURNS:			nothing
465  *
466  * SIDE EFFECTS:
467  *
468  * NOTES:
469  */
470 esis_eshinput(m, shp)
471 struct mbuf		*m;	/* esh pdu */
472 struct snpa_hdr	*shp;	/* subnetwork header */
473 {
474 	struct	esis_fixed	*pdu = mtod(m, struct esis_fixed *);
475 	u_short				ht;		/* holding time */
476 	struct	iso_addr	*nsap;
477 	int					naddr;
478 	u_char				*buf = (u_char *)(pdu + 1);
479 	u_char				*buflim = pdu->esis_hdr_len + (u_char *)pdu;
480 	int					new_entry = 0;
481 
482 	esis_stat.es_eshrcvd++;
483 
484 	CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
485 
486 	naddr = *buf++;
487 	if (buf >= buflim)
488 		goto bad;
489 	if (naddr == 1) {
490 		ESIS_EXTRACT_ADDR(nsap, buf);
491 		new_entry = snpac_add(shp->snh_ifp,
492 								 nsap, shp->snh_shost, SNPA_ES, ht, 0);
493 	} else {
494 		int nsellength = 0, nlen = 0;
495 		{
496 		/* See if we want to compress out multiple nsaps differing
497 		   only by nsel */
498 			register struct ifaddr *ifa = shp->snh_ifp->if_addrlist;
499 			for (; ifa; ifa = ifa->ifa_next)
500 				if (ifa->ifa_addr->sa_family == AF_ISO) {
501 					nsellength = ((struct iso_ifaddr *)ifa)->ia_addr.siso_tlen;
502 					break;
503 			}
504 		}
505 		IFDEBUG(D_ESISINPUT)
506 			printf("esis_eshinput: esh: ht %d, naddr %d nsellength %d\n",
507 					ht, naddr, nsellength);
508 		ENDDEBUG
509 		while (naddr-- > 0) {
510 			struct iso_addr *nsap2; u_char *buf2;
511 			ESIS_EXTRACT_ADDR(nsap, buf);
512 			/* see if there is at least one more nsap in ESH differing
513 			   only by nsel */
514 			if (nsellength != 0) for (buf2 = buf; buf2 < buflim;) {
515 				ESIS_EXTRACT_ADDR(nsap2, buf2);
516 				IFDEBUG(D_ESISINPUT)
517 					printf("esis_eshinput: comparing %s ",
518 						clnp_iso_addrp(nsap));
519 					printf("and %s\n", clnp_iso_addrp(nsap2));
520 				ENDDEBUG
521 				if (Bcmp(nsap->isoa_genaddr, nsap2->isoa_genaddr,
522 						 nsap->isoa_len - nsellength) == 0) {
523 					nlen = nsellength;
524 					break;
525 				}
526 			}
527 			new_entry |= snpac_add(shp->snh_ifp,
528 									nsap, shp->snh_shost, SNPA_ES, ht, nlen);
529 			nlen = 0;
530 		}
531 	}
532 	IFDEBUG(D_ESISINPUT)
533 		printf("esis_eshinput: nsap %s is %s\n",
534 			clnp_iso_addrp(nsap), new_entry ? "new" : "old");
535 	ENDDEBUG
536 	if (new_entry && (iso_systype & SNPA_IS))
537 		esis_shoutput(shp->snh_ifp, ESIS_ISH, esis_holding_time,
538 						shp->snh_shost, 6, (struct iso_addr *)0);
539 bad:
540 	return;
541 }
542 
543 /*
544  * FUNCTION:		esis_ishinput
545  *
546  * PURPOSE:			process an incoming ISH pdu
547  *
548  * RETURNS:
549  *
550  * SIDE EFFECTS:
551  *
552  * NOTES:
553  */
554 esis_ishinput(m, shp)
555 struct mbuf		*m;	/* esh pdu */
556 struct snpa_hdr	*shp;	/* subnetwork header */
557 {
558 	struct esis_fixed	*pdu = mtod(m, struct esis_fixed *);
559 	u_short				ht, newct;			/* holding time */
560 	struct iso_addr		*nsap; 				/* Network Entity Title */
561 	register u_char		*buf = (u_char *) (pdu + 1);
562 	register u_char		*buflim = pdu->esis_hdr_len + (u_char *)pdu;
563 	int					new_entry;
564 
565 	esis_stat.es_ishrcvd++;
566 	CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
567 
568 	IFDEBUG(D_ESISINPUT)
569 		printf("esis_ishinput: ish: ht %d\n", ht);
570 	ENDDEBUG
571 	if (ESHonly)
572 		goto bad;
573 
574 	ESIS_EXTRACT_ADDR(nsap, buf);
575 
576 	while (buf < buflim) {
577 		switch (*buf) {
578 		case ESISOVAL_ESCT:
579 			if (iso_systype & SNPA_IS)
580 				break;
581 			if (buf[1] != 2)
582 				goto bad;
583 			CTOH(buf[2], buf[3], newct);
584 			if (esis_config_time != newct) {
585 				untimeout(esis_config,0);
586 				esis_config_time = newct;
587 				esis_config();
588 			}
589 			break;
590 
591 		default:
592 			printf("Unknown ISH option: %x\n", *buf);
593 		}
594 		ESIS_NEXT_OPTION(buf);
595 	}
596 	new_entry = snpac_add(shp->snh_ifp, nsap, shp->snh_shost, SNPA_IS, ht, 0);
597 	IFDEBUG(D_ESISINPUT)
598 		printf("esis_ishinput: nsap %s is %s\n",
599 			clnp_iso_addrp(nsap), new_entry ? "new" : "old");
600 	ENDDEBUG
601 
602 	if (new_entry)
603 		esis_shoutput(shp->snh_ifp,
604 			iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
605 			esis_holding_time, shp->snh_shost, 6, (struct iso_addr *)0);
606 bad:
607 	return;
608 }
609 
610 /*
611  * FUNCTION:		esis_rdinput
612  *
613  * PURPOSE:			Process an incoming RD pdu
614  *
615  * RETURNS:
616  *
617  * SIDE EFFECTS:
618  *
619  * NOTES:
620  */
621 esis_rdinput(m0, shp)
622 struct mbuf		*m0;	/* esh pdu */
623 struct snpa_hdr	*shp;	/* subnetwork header */
624 {
625 	struct esis_fixed	*pdu = mtod(m0, struct esis_fixed *);
626 	u_short				ht;		/* holding time */
627 	struct iso_addr		*da, *net = 0, *netmask = 0, *snpamask = 0;
628 	register struct iso_addr *bsnpa;
629 	register u_char		*buf = (u_char *)(pdu + 1);
630 	register u_char		*buflim = pdu->esis_hdr_len + (u_char *)pdu;
631 
632 	esis_stat.es_rdrcvd++;
633 
634 	/* intermediate systems ignore redirects */
635 	if (iso_systype & SNPA_IS)
636 		return;
637 	if (ESHonly)
638 		return;
639 
640 	CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
641 	if (buf >= buflim)
642 		return;
643 
644 	/* Extract DA */
645 	ESIS_EXTRACT_ADDR(da, buf);
646 
647 	/* Extract better snpa */
648 	ESIS_EXTRACT_ADDR(bsnpa, buf);
649 
650 	/* Extract NET if present */
651 	if (buf < buflim) {
652 		if (*buf == 0)
653 			buf++; /* no NET present, skip NETL anyway */
654 		else
655 			ESIS_EXTRACT_ADDR(net, buf);
656 	}
657 
658 	/* process options */
659 	while (buf < buflim) {
660 		switch (*buf) {
661 		case ESISOVAL_SNPAMASK:
662 			if (snpamask) /* duplicate */
663 				return;
664 			snpamask = (struct iso_addr *)(buf + 1);
665 			break;
666 
667 		case ESISOVAL_NETMASK:
668 			if (netmask) /* duplicate */
669 				return;
670 			netmask = (struct iso_addr *)(buf + 1);
671 			break;
672 
673 		default:
674 			printf("Unknown option in ESIS RD (0x%x)\n", buf[-1]);
675 		}
676 		ESIS_NEXT_OPTION(buf);
677 	}
678 
679 	IFDEBUG(D_ESISINPUT)
680 		printf("esis_rdinput: rd: ht %d, da %s\n", ht, clnp_iso_addrp(da));
681 		if (net)
682 			printf("\t: net %s\n", clnp_iso_addrp(net));
683 	ENDDEBUG
684 	/*
685 	 *	If netl is zero, then redirect is to an ES. We need to add an entry
686 	 *	to the snpa cache for (destination, better snpa).
687 	 *	If netl is not zero, then the redirect is to an IS. In this
688 	 *	case, add an snpa cache entry for (net, better snpa).
689 	 *
690 	 *	If the redirect is to an IS, add a route entry towards that
691 	 *	IS.
692 	 */
693 	if (net == 0 || net->isoa_len == 0 || snpamask) {
694 		/* redirect to an ES */
695 		snpac_add(shp->snh_ifp, da,
696 				bsnpa->isoa_genaddr, SNPA_ES, ht, 0);
697 	} else {
698 		snpac_add(shp->snh_ifp, net,
699 				bsnpa->isoa_genaddr, SNPA_IS, ht, 0);
700 		snpac_addrt(shp->snh_ifp, da, net, netmask);
701 	}
702 bad: ;    /* Needed by ESIS_NEXT_OPTION */
703 }
704 
705 /*
706  * FUNCTION:		esis_config
707  *
708  * PURPOSE:			Report configuration
709  *
710  * RETURNS:
711  *
712  * SIDE EFFECTS:
713  *
714  * NOTES:			Called every esis_config_time seconds
715  */
716 esis_config()
717 {
718 	register struct ifnet	*ifp;
719 
720 	timeout(esis_config, (caddr_t)0, hz * esis_config_time);
721 
722 	/*
723 	 *	Report configuration for each interface that
724 	 *	- is UP
725 	 *	- is not loopback
726 	 *	- has an ISO address
727 	 */
728 
729 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
730 		if ((ifp->if_flags & IFF_UP) &&
731 			((ifp->if_flags & IFF_LOOPBACK) == 0)) {
732 			/* search for an ISO address family */
733 			struct ifaddr	*ia;
734 
735 			for (ia = ifp->if_addrlist; ia; ia = ia->ifa_next) {
736 				if (ia->ifa_addr->sa_family == AF_ISO) {
737 					esis_shoutput(ifp,
738 						iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
739 						esis_holding_time,
740 						(caddr_t)(iso_systype & SNPA_ES ? all_is_snpa :
741 						all_es_snpa), 6, (struct iso_addr *)0);
742 					break;
743 				}
744 			}
745 		}
746 	}
747 }
748 
749 /*
750  * FUNCTION:		esis_shoutput
751  *
752  * PURPOSE:			Transmit an esh or ish pdu
753  *
754  * RETURNS:			nothing
755  *
756  * SIDE EFFECTS:
757  *
758  * NOTES:
759  */
760 esis_shoutput(ifp, type, ht, sn_addr, sn_len, isoa)
761 struct ifnet	*ifp;
762 int				type;
763 short			ht;
764 caddr_t 		sn_addr;
765 int				sn_len;
766 struct	iso_addr *isoa;
767 {
768 	struct mbuf			*m, *m0;
769 	caddr_t				cp, naddrp;
770 	int					naddr = 0;
771 	struct esis_fixed	*pdu;
772 	struct iso_ifaddr	*ia;
773 	int					len;
774 	struct sockaddr_iso	siso;
775 
776 	if (type == ESIS_ESH)
777 		esis_stat.es_eshsent++;
778 	else if (type == ESIS_ISH)
779 		esis_stat.es_ishsent++;
780 	else {
781 		printf("esis_shoutput: bad pdu type\n");
782 		return;
783 	}
784 
785 	IFDEBUG(D_ESISOUTPUT)
786 		int	i;
787 		printf("esis_shoutput: ifp x%x (%s%d), %s, ht %d, to: [%d] ",
788 			ifp, ifp->if_name, ifp->if_unit, type == ESIS_ESH ? "esh" : "ish",
789 			ht, sn_len);
790 		for (i=0; i<sn_len; i++)
791 			printf("%x%c", *(sn_addr+i), i < (sn_len-1) ? ':' : ' ');
792 		printf("\n");
793 	ENDDEBUG
794 
795 	if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
796 		esis_stat.es_nomem++;
797 		return;
798 	}
799 	bzero(mtod(m, caddr_t), MHLEN);
800 
801 	pdu = mtod(m, struct esis_fixed *);
802 	naddrp = cp = (caddr_t)(pdu + 1);
803 	len = sizeof(struct esis_fixed);
804 
805 	/*
806 	 *	Build fixed part of header
807 	 */
808 	pdu->esis_proto_id = ISO9542_ESIS;
809 	pdu->esis_vers = ESIS_VERSION;
810 	pdu->esis_type = type;
811 	HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
812 
813 	if (type == ESIS_ESH) {
814 		cp++;
815 		len++;
816 	}
817 
818 	m->m_len = len;
819 	if (isoa) {
820 		/*
821 		 * Here we are responding to a clnp packet sent to an NSAP
822 		 * that is ours which was sent to the MAC addr all_es's.
823 		 * It is possible that we did not specifically advertise this
824 		 * NSAP, even though it is ours, so we will respond
825 		 * directly to the sender that we are here.  If we do have
826 		 * multiple NSEL's we'll tack them on so he can compress them out.
827 		 */
828 		(void) esis_insert_addr(&cp, &len, isoa, m, 0);
829 		naddr = 1;
830 	}
831 	for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
832 		int nsellen = (type == ESIS_ISH ? ia->ia_addr.siso_tlen : 0);
833 		int n = ia->ia_addr.siso_nlen;
834 		register struct iso_ifaddr *ia2;
835 
836 		if (type == ESIS_ISH && naddr > 0)
837 			break;
838 		for (ia2 = iso_ifaddr; ia2 != ia; ia2 = ia2->ia_next)
839 			if (Bcmp(ia->ia_addr.siso_data, ia2->ia_addr.siso_data, n) == 0)
840 					break;
841 		if (ia2 != ia)
842 			continue;	/* Means we have previously copied this nsap */
843 		if (isoa && Bcmp(ia->ia_addr.siso_data, isoa->isoa_genaddr, n) == 0) {
844 			isoa = 0;
845 			continue;	/* Ditto */
846 		}
847 		IFDEBUG(D_ESISOUTPUT)
848 			printf("esis_shoutput: adding NSAP %s\n",
849 				clnp_iso_addrp(&ia->ia_addr.siso_addr));
850 		ENDDEBUG
851 		if (!esis_insert_addr(&cp, &len,
852 							  &ia->ia_addr.siso_addr, m, nsellen)) {
853 			EXTEND_PACKET(m, m0, cp);
854 			(void) esis_insert_addr(&cp, &len, &ia->ia_addr.siso_addr, m,
855 									nsellen);
856 		}
857 		naddr++;
858 	}
859 
860 	if (type == ESIS_ESH)
861 		*naddrp = naddr;
862 	else {
863 		/* add suggested es config timer option to ISH */
864 		if (M_TRAILINGSPACE(m) < 4) {
865 			printf("esis_shoutput: extending packet\n");
866 			EXTEND_PACKET(m, m0, cp);
867 		}
868 		*cp++ = ESISOVAL_ESCT;
869 		*cp++ = 2;
870 		HTOC(*cp, *(cp+1), esis_esconfig_time);
871 		len += 4;
872 		m->m_len += 4;
873 		IFDEBUG(D_ESISOUTPUT)
874 			printf("m0 0x%x, m 0x%x, data 0x%x, len %d, cp 0x%x\n",
875 			m0, m, m->m_data, m->m_len, cp);
876 		ENDDEBUG
877 	}
878 
879 	m0->m_pkthdr.len = len;
880 	pdu->esis_hdr_len = len;
881 	iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len);
882 
883 	bzero((caddr_t)&siso, sizeof(siso));
884 	siso.siso_family = AF_ISO;
885 	siso.siso_data[0] = AFI_SNA;
886 	siso.siso_nlen = sn_len + 1;
887 	bcopy(sn_addr, siso.siso_data + 1, (unsigned)sn_len);
888 	(ifp->if_output)(ifp, m0, &siso, 0);
889 }
890 
891 /*
892  * FUNCTION:		isis_input
893  *
894  * PURPOSE:			Process an incoming isis packet
895  *
896  * RETURNS:			nothing
897  *
898  * SIDE EFFECTS:
899  *
900  * NOTES:
901  */
902 isis_input(m0, shp)
903 struct mbuf		*m0;		/* ptr to first mbuf of pkt */
904 struct snpa_hdr	*shp;	/* subnetwork header */
905 {
906 	register int type;
907 	register struct rawcb *rp, *first_rp = 0;
908 	struct ifnet *ifp = shp->snh_ifp;
909 	char workbuf[16];
910 	struct mbuf *mm;
911 
912 	IFDEBUG(D_ISISINPUT)
913 		int i;
914 
915 		printf("isis_input: pkt on ifp x%x (%s%d): from:", ifp,
916 			ifp->if_name, ifp->if_unit);
917 		for (i=0; i<6; i++)
918 			printf("%x%c", shp->snh_shost[i]&0xff, (i<5) ? ':' : ' ');
919 		printf(" to:");
920 		for (i=0; i<6; i++)
921 			printf("%x%c", shp->snh_dhost[i]&0xff, (i<5) ? ':' : ' ');
922 		printf("\n");
923 	ENDDEBUG
924 	esis_dl.sdl_alen = ifp->if_addrlen;
925 	esis_dl.sdl_index = ifp->if_index;
926 	bcopy(shp->snh_shost, (caddr_t)esis_dl.sdl_data, esis_dl.sdl_alen);
927 	for (rp = esis_pcb.rcb_next; rp != &esis_pcb; rp = rp->rcb_next) {
928 		if (first_rp == 0) {
929 			first_rp = rp;
930 			continue;
931 		}
932 		if (mm = m_copy(m0, 0, M_COPYALL)) { /*can't block at interrupt level */
933 			if (sbappendaddr(&rp->rcb_socket->so_rcv,
934 							  &esis_dl, mm, (struct mbuf *)0) != 0)
935 				sorwakeup(rp->rcb_socket);
936 			else {
937 				IFDEBUG(D_ISISINPUT)
938 					printf("Error in sbappenaddr, mm = 0x%x\n", mm);
939 				ENDDEBUG
940 				m_freem(mm);
941 			}
942 		}
943 	}
944 	if (first_rp && sbappendaddr(&first_rp->rcb_socket->so_rcv,
945 							  &esis_dl, m0, (struct mbuf *)0) != 0) {
946 		sorwakeup(first_rp->rcb_socket);
947 		return;
948 	}
949 	m_freem(m0);
950 }
951 
952 isis_output(sdl, m)
953 register struct sockaddr_dl	*sdl;
954 struct mbuf *m;
955 {
956 	register struct ifnet *ifp;
957 	struct ifaddr *ifa, *ifa_ifwithnet();
958 	struct sockaddr_iso siso;
959 	int error = 0;
960 	unsigned sn_len;
961 
962 	ifa = ifa_ifwithnet(sdl);	/* extract ifp from sockaddr_dl */
963 	if (ifa == 0) {
964 		IFDEBUG(D_ISISOUTPUT)
965 			printf("isis_output: interface not found\n");
966 		ENDDEBUG
967 		error = EINVAL;
968 		goto release;
969 	}
970 	ifp = ifa->ifa_ifp;
971 	sn_len = sdl->sdl_alen;
972 	IFDEBUG(D_ISISOUTPUT)
973 		u_char *cp = (u_char *)LLADDR(sdl), *cplim = cp + sn_len;
974 		printf("isis_output: ifp 0x%x (%s%d), to: ",
975 			ifp, ifp->if_name, ifp->if_unit);
976 		while (cp < cplim) {
977 			printf("%x", *cp++);
978 			printf("%c", (cp < cplim) ? ':' : ' ');
979 		}
980 		printf("\n");
981 	ENDDEBUG
982 	bzero((caddr_t)&siso, sizeof(siso));
983 	siso.siso_family = AF_ISO; /* This convention may be useful for X.25 */
984 	siso.siso_data[0] = AFI_SNA;
985 	siso.siso_nlen = sn_len + 1;
986 	bcopy(LLADDR(sdl), siso.siso_data + 1, sn_len);
987 	error = (ifp->if_output)(ifp, m, (struct sockaddr *)&siso, 0);
988 	if (error) {
989 		IFDEBUG(D_ISISOUTPUT)
990 			printf("isis_output: error from ether_output is %d\n", error);
991 		ENDDEBUG
992 	}
993 	return (error);
994 
995 release:
996 	if (m != NULL)
997 		m_freem(m);
998 	return(error);
999 }
1000 
1001 
1002 /*
1003  * FUNCTION:		esis_ctlinput
1004  *
1005  * PURPOSE:			Handle the PRC_IFDOWN transition
1006  *
1007  * RETURNS:			nothing
1008  *
1009  * SIDE EFFECTS:
1010  *
1011  * NOTES:			Calls snpac_flush for interface specified.
1012  *					The loop through iso_ifaddr is stupid because
1013  *					back in if_down, we knew the ifp...
1014  */
1015 esis_ctlinput(req, siso)
1016 int						req;		/* request: we handle only PRC_IFDOWN */
1017 struct sockaddr_iso		*siso;		/* address of ifp */
1018 {
1019 	register struct iso_ifaddr *ia;	/* scan through interface addresses */
1020 
1021 	if (req == PRC_IFDOWN)
1022 		for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
1023 			if (iso_addrmatch(IA_SIS(ia), siso))
1024 				snpac_flushifp(ia->ia_ifp);
1025 		}
1026 }
1027 
1028 #endif	ISO
1029