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