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