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