xref: /csrg-svn/sys/netiso/esis.c (revision 48963)
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.17 (Berkeley) 05/02/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 	 *	- has BROADCAST capability
726 	 *	- has an ISO address
727 	 */
728 	/* Todo: a better way would be to construct the esh or ish
729 	 * once and copy it out for all devices, possibly calling
730 	 * a method in the iso_ifaddr structure to encapsulate and
731 	 * transmit it.  This could work to advantage for non-broadcast media
732 	 */
733 
734 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
735 		if ((ifp->if_flags & IFF_UP) &&
736 		    (ifp->if_flags & IFF_BROADCAST)) {
737 			/* search for an ISO address family */
738 			struct ifaddr	*ia;
739 
740 			for (ia = ifp->if_addrlist; ia; ia = ia->ifa_next) {
741 				if (ia->ifa_addr->sa_family == AF_ISO) {
742 					esis_shoutput(ifp,
743 						iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
744 						esis_holding_time,
745 						(caddr_t)(iso_systype & SNPA_ES ? all_is_snpa :
746 						all_es_snpa), 6, (struct iso_addr *)0);
747 					break;
748 				}
749 			}
750 		}
751 	}
752 }
753 
754 /*
755  * FUNCTION:		esis_shoutput
756  *
757  * PURPOSE:			Transmit an esh or ish pdu
758  *
759  * RETURNS:			nothing
760  *
761  * SIDE EFFECTS:
762  *
763  * NOTES:
764  */
765 esis_shoutput(ifp, type, ht, sn_addr, sn_len, isoa)
766 struct ifnet	*ifp;
767 int				type;
768 short			ht;
769 caddr_t 		sn_addr;
770 int				sn_len;
771 struct	iso_addr *isoa;
772 {
773 	struct mbuf			*m, *m0;
774 	caddr_t				cp, naddrp;
775 	int					naddr = 0;
776 	struct esis_fixed	*pdu;
777 	struct iso_ifaddr	*ia;
778 	int					len;
779 	struct sockaddr_iso	siso;
780 
781 	if (type == ESIS_ESH)
782 		esis_stat.es_eshsent++;
783 	else if (type == ESIS_ISH)
784 		esis_stat.es_ishsent++;
785 	else {
786 		printf("esis_shoutput: bad pdu type\n");
787 		return;
788 	}
789 
790 	IFDEBUG(D_ESISOUTPUT)
791 		int	i;
792 		printf("esis_shoutput: ifp x%x (%s%d), %s, ht %d, to: [%d] ",
793 			ifp, ifp->if_name, ifp->if_unit, type == ESIS_ESH ? "esh" : "ish",
794 			ht, sn_len);
795 		for (i=0; i<sn_len; i++)
796 			printf("%x%c", *(sn_addr+i), i < (sn_len-1) ? ':' : ' ');
797 		printf("\n");
798 	ENDDEBUG
799 
800 	if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
801 		esis_stat.es_nomem++;
802 		return;
803 	}
804 	bzero(mtod(m, caddr_t), MHLEN);
805 
806 	pdu = mtod(m, struct esis_fixed *);
807 	naddrp = cp = (caddr_t)(pdu + 1);
808 	len = sizeof(struct esis_fixed);
809 
810 	/*
811 	 *	Build fixed part of header
812 	 */
813 	pdu->esis_proto_id = ISO9542_ESIS;
814 	pdu->esis_vers = ESIS_VERSION;
815 	pdu->esis_type = type;
816 	HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
817 
818 	if (type == ESIS_ESH) {
819 		cp++;
820 		len++;
821 	}
822 
823 	m->m_len = len;
824 	if (isoa) {
825 		/*
826 		 * Here we are responding to a clnp packet sent to an NSAP
827 		 * that is ours which was sent to the MAC addr all_es's.
828 		 * It is possible that we did not specifically advertise this
829 		 * NSAP, even though it is ours, so we will respond
830 		 * directly to the sender that we are here.  If we do have
831 		 * multiple NSEL's we'll tack them on so he can compress them out.
832 		 */
833 		(void) esis_insert_addr(&cp, &len, isoa, m, 0);
834 		naddr = 1;
835 	}
836 	for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
837 		int nsellen = (type == ESIS_ISH ? ia->ia_addr.siso_tlen : 0);
838 		int n = ia->ia_addr.siso_nlen;
839 		register struct iso_ifaddr *ia2;
840 
841 		if (type == ESIS_ISH && naddr > 0)
842 			break;
843 		for (ia2 = iso_ifaddr; ia2 != ia; ia2 = ia2->ia_next)
844 			if (Bcmp(ia->ia_addr.siso_data, ia2->ia_addr.siso_data, n) == 0)
845 					break;
846 		if (ia2 != ia)
847 			continue;	/* Means we have previously copied this nsap */
848 		if (isoa && Bcmp(ia->ia_addr.siso_data, isoa->isoa_genaddr, n) == 0) {
849 			isoa = 0;
850 			continue;	/* Ditto */
851 		}
852 		IFDEBUG(D_ESISOUTPUT)
853 			printf("esis_shoutput: adding NSAP %s\n",
854 				clnp_iso_addrp(&ia->ia_addr.siso_addr));
855 		ENDDEBUG
856 		if (!esis_insert_addr(&cp, &len,
857 							  &ia->ia_addr.siso_addr, m, nsellen)) {
858 			EXTEND_PACKET(m, m0, cp);
859 			(void) esis_insert_addr(&cp, &len, &ia->ia_addr.siso_addr, m,
860 									nsellen);
861 		}
862 		naddr++;
863 	}
864 
865 	if (type == ESIS_ESH)
866 		*naddrp = naddr;
867 	else {
868 		/* add suggested es config timer option to ISH */
869 		if (M_TRAILINGSPACE(m) < 4) {
870 			printf("esis_shoutput: extending packet\n");
871 			EXTEND_PACKET(m, m0, cp);
872 		}
873 		*cp++ = ESISOVAL_ESCT;
874 		*cp++ = 2;
875 		HTOC(*cp, *(cp+1), esis_esconfig_time);
876 		len += 4;
877 		m->m_len += 4;
878 		IFDEBUG(D_ESISOUTPUT)
879 			printf("m0 0x%x, m 0x%x, data 0x%x, len %d, cp 0x%x\n",
880 			m0, m, m->m_data, m->m_len, cp);
881 		ENDDEBUG
882 	}
883 
884 	m0->m_pkthdr.len = len;
885 	pdu->esis_hdr_len = len;
886 	iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len);
887 
888 	bzero((caddr_t)&siso, sizeof(siso));
889 	siso.siso_family = AF_ISO;
890 	siso.siso_data[0] = AFI_SNA;
891 	siso.siso_nlen = sn_len + 1;
892 	bcopy(sn_addr, siso.siso_data + 1, (unsigned)sn_len);
893 	(ifp->if_output)(ifp, m0, &siso, 0);
894 }
895 
896 /*
897  * FUNCTION:		isis_input
898  *
899  * PURPOSE:			Process an incoming isis packet
900  *
901  * RETURNS:			nothing
902  *
903  * SIDE EFFECTS:
904  *
905  * NOTES:
906  */
907 isis_input(m0, shp)
908 struct mbuf		*m0;		/* ptr to first mbuf of pkt */
909 struct snpa_hdr	*shp;	/* subnetwork header */
910 {
911 	register int type;
912 	register struct rawcb *rp, *first_rp = 0;
913 	struct ifnet *ifp = shp->snh_ifp;
914 	char workbuf[16];
915 	struct mbuf *mm;
916 
917 	IFDEBUG(D_ISISINPUT)
918 		int i;
919 
920 		printf("isis_input: pkt on ifp x%x (%s%d): from:", ifp,
921 			ifp->if_name, ifp->if_unit);
922 		for (i=0; i<6; i++)
923 			printf("%x%c", shp->snh_shost[i]&0xff, (i<5) ? ':' : ' ');
924 		printf(" to:");
925 		for (i=0; i<6; i++)
926 			printf("%x%c", shp->snh_dhost[i]&0xff, (i<5) ? ':' : ' ');
927 		printf("\n");
928 	ENDDEBUG
929 	esis_dl.sdl_alen = ifp->if_addrlen;
930 	esis_dl.sdl_index = ifp->if_index;
931 	bcopy(shp->snh_shost, (caddr_t)esis_dl.sdl_data, esis_dl.sdl_alen);
932 	for (rp = esis_pcb.rcb_next; rp != &esis_pcb; rp = rp->rcb_next) {
933 		if (first_rp == 0) {
934 			first_rp = rp;
935 			continue;
936 		}
937 		if (mm = m_copy(m0, 0, M_COPYALL)) { /*can't block at interrupt level */
938 			if (sbappendaddr(&rp->rcb_socket->so_rcv,
939 							  &esis_dl, mm, (struct mbuf *)0) != 0)
940 				sorwakeup(rp->rcb_socket);
941 			else {
942 				IFDEBUG(D_ISISINPUT)
943 					printf("Error in sbappenaddr, mm = 0x%x\n", mm);
944 				ENDDEBUG
945 				m_freem(mm);
946 			}
947 		}
948 	}
949 	if (first_rp && sbappendaddr(&first_rp->rcb_socket->so_rcv,
950 							  &esis_dl, m0, (struct mbuf *)0) != 0) {
951 		sorwakeup(first_rp->rcb_socket);
952 		return;
953 	}
954 	m_freem(m0);
955 }
956 
957 isis_output(sdl, m)
958 register struct sockaddr_dl	*sdl;
959 struct mbuf *m;
960 {
961 	register struct ifnet *ifp;
962 	struct ifaddr *ifa, *ifa_ifwithnet();
963 	struct sockaddr_iso siso;
964 	int error = 0;
965 	unsigned sn_len;
966 
967 	ifa = ifa_ifwithnet(sdl);	/* extract ifp from sockaddr_dl */
968 	if (ifa == 0) {
969 		IFDEBUG(D_ISISOUTPUT)
970 			printf("isis_output: interface not found\n");
971 		ENDDEBUG
972 		error = EINVAL;
973 		goto release;
974 	}
975 	ifp = ifa->ifa_ifp;
976 	sn_len = sdl->sdl_alen;
977 	IFDEBUG(D_ISISOUTPUT)
978 		u_char *cp = (u_char *)LLADDR(sdl), *cplim = cp + sn_len;
979 		printf("isis_output: ifp 0x%x (%s%d), to: ",
980 			ifp, ifp->if_name, ifp->if_unit);
981 		while (cp < cplim) {
982 			printf("%x", *cp++);
983 			printf("%c", (cp < cplim) ? ':' : ' ');
984 		}
985 		printf("\n");
986 	ENDDEBUG
987 	bzero((caddr_t)&siso, sizeof(siso));
988 	siso.siso_family = AF_ISO; /* This convention may be useful for X.25 */
989 	siso.siso_data[0] = AFI_SNA;
990 	siso.siso_nlen = sn_len + 1;
991 	bcopy(LLADDR(sdl), siso.siso_data + 1, sn_len);
992 	error = (ifp->if_output)(ifp, m, (struct sockaddr *)&siso, 0);
993 	if (error) {
994 		IFDEBUG(D_ISISOUTPUT)
995 			printf("isis_output: error from ether_output is %d\n", error);
996 		ENDDEBUG
997 	}
998 	return (error);
999 
1000 release:
1001 	if (m != NULL)
1002 		m_freem(m);
1003 	return(error);
1004 }
1005 
1006 
1007 /*
1008  * FUNCTION:		esis_ctlinput
1009  *
1010  * PURPOSE:			Handle the PRC_IFDOWN transition
1011  *
1012  * RETURNS:			nothing
1013  *
1014  * SIDE EFFECTS:
1015  *
1016  * NOTES:			Calls snpac_flush for interface specified.
1017  *					The loop through iso_ifaddr is stupid because
1018  *					back in if_down, we knew the ifp...
1019  */
1020 esis_ctlinput(req, siso)
1021 int						req;		/* request: we handle only PRC_IFDOWN */
1022 struct sockaddr_iso		*siso;		/* address of ifp */
1023 {
1024 	register struct iso_ifaddr *ia;	/* scan through interface addresses */
1025 
1026 	if (req == PRC_IFDOWN)
1027 		for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
1028 			if (iso_addrmatch(IA_SIS(ia), siso))
1029 				snpac_flushifp(ia->ia_ifp);
1030 		}
1031 }
1032 
1033 #endif	ISO
1034