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