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