Sync usage with man page.
[netbsd-mini2440.git] / sys / netiso / esis.c
bloba1be47fedb4d6c31e9f2580d9b0b64f8f67ddf1c
1 /* $NetBSD: esis.c,v 1.55 2009/03/18 17:06:52 cegger Exp $ */
3 /*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
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.
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.
31 * @(#)esis.c 8.3 (Berkeley) 3/20/95
34 /***********************************************************
35 Copyright IBM Corporation 1987
37 All Rights Reserved
39 Permission to use, copy, modify, and distribute this software and its
40 documentation for any purpose and without fee is hereby granted,
41 provided that the above copyright notice appear in all copies and that
42 both that copyright notice and this permission notice appear in
43 supporting documentation, and that the name of IBM not be
44 used in advertising or publicity pertaining to distribution of the
45 software without specific, written prior permission.
47 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
48 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
49 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
50 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
51 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
52 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
53 SOFTWARE.
55 ******************************************************************/
58 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
61 #include <sys/cdefs.h>
62 __KERNEL_RCSID(0, "$NetBSD: esis.c,v 1.55 2009/03/18 17:06:52 cegger Exp $");
64 #include "opt_iso.h"
65 #ifdef ISO
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>
80 #include <net/if.h>
81 #include <net/if_dl.h>
82 #include <net/route.h>
83 #include <net/raw_cb.h>
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>
95 #include <machine/stdarg.h>
98 * Global variables to esis implementation
100 * esis_holding_time - the holding time (sec) parameter for outgoing pdus
101 * esis_config_time - the frequency (sec) that hellos are generated
102 * esis_esconfig_time - suggested es configuration time placed in the ish.
105 LIST_HEAD(, rawcb) esis_pcb;
106 struct esis_stat esis_stat;
107 int esis_sendspace = 2048;
108 int esis_recvspace = 2048;
109 short esis_holding_time = ESIS_HT;
110 short esis_config_time = ESIS_CONFIG;
111 short esis_esconfig_time = ESIS_CONFIG;
112 struct sockaddr_dl esis_dl = {
113 .sdl_len = sizeof(esis_dl),
114 .sdl_family = AF_LINK,
117 struct callout esis_config_ch;
119 #define EXTEND_PACKET(m, mhdr, cp)\
120 if (((m)->m_next = m_getclr(M_DONTWAIT, MT_HEADER)) == NULL) {\
121 esis_stat.es_nomem++;\
122 m_freem(mhdr);\
123 return;\
124 } else {\
125 (m) = (m)->m_next;\
126 (cp) = mtod((m), void *);\
127 (m)->m_len = 0;\
131 * FUNCTION: esis_init
133 * PURPOSE: Initialize the kernel portion of esis protocol
135 * RETURNS: nothing
137 * SIDE EFFECTS:
139 * NOTES:
141 void
142 esis_init(void)
144 extern struct clnl_protosw clnl_protox[256];
146 LIST_INIT(&esis_pcb);
148 callout_init(&snpac_age_ch, 0);
149 callout_init(&esis_config_ch, 0);
151 callout_reset(&snpac_age_ch, hz, snpac_age, NULL);
152 callout_reset(&esis_config_ch, hz, esis_config, NULL);
154 clnl_protox[ISO9542_ESIS].clnl_input = esis_input;
155 clnl_protox[ISO10589_ISIS].clnl_input = isis_input;
156 #ifdef ISO_X25ESIS
157 clnl_protox[ISO9542X25_ESIS].clnl_input = x25esis_input;
158 #endif /* ISO_X25ESIS */
162 * FUNCTION: esis_usrreq
164 * PURPOSE: Handle user level esis requests
166 * RETURNS: 0 or appropriate errno
168 * SIDE EFFECTS:
171 /* ARGSUSED */
173 esis_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam,
174 struct mbuf *control, struct lwp *l)
176 struct rawcb *rp;
177 int error = 0;
179 if (req == PRU_CONTROL)
180 return (EOPNOTSUPP);
182 rp = sotorawcb(so);
183 #ifdef DIAGNOSTIC
184 if (req != PRU_SEND && req != PRU_SENDOOB && control)
185 panic("esis_usrreq: unexpected control mbuf");
186 #endif
187 if (rp == 0 && req != PRU_ATTACH) {
188 error = EINVAL;
189 goto release;
192 switch (req) {
194 case PRU_ATTACH:
195 sosetlock(so);
196 if (rp != 0) {
197 error = EISCONN;
198 break;
201 if (l == NULL) {
202 error = EACCES;
203 break;
206 /* XXX: raw socket permission is checked in socreate() */
208 if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
209 error = soreserve(so, esis_sendspace, esis_recvspace);
210 if (error)
211 break;
213 rp = malloc(sizeof(*rp), M_PCB, M_WAITOK|M_ZERO);
214 if (rp == 0) {
215 error = ENOBUFS;
216 break;
218 rp->rcb_socket = so;
219 LIST_INSERT_HEAD(&esis_pcb, rp, rcb_list);
220 so->so_pcb = rp;
221 break;
223 case PRU_SEND:
224 if (control && control->m_len) {
225 m_freem(control);
226 m_freem(m);
227 error = EINVAL;
228 break;
230 if (nam == NULL) {
231 m_freem(m);
232 error = EINVAL;
233 break;
235 /* error checking here */
236 error = isis_output(m, mtod(nam, struct sockaddr_dl *));
237 break;
239 case PRU_SENDOOB:
240 m_freem(control);
241 m_freem(m);
242 error = EOPNOTSUPP;
243 break;
245 case PRU_DETACH:
246 raw_detach(rp);
247 break;
249 case PRU_SHUTDOWN:
250 socantsendmore(so);
251 break;
253 case PRU_SENSE:
255 * stat: don't bother with a blocksize.
257 return (0);
259 default:
260 error = EOPNOTSUPP;
261 break;
264 release:
265 return (error);
269 * FUNCTION: esis_input
271 * PURPOSE: Process an incoming esis packet
273 * RETURNS: nothing
275 * SIDE EFFECTS:
277 * NOTES:
279 void
280 esis_input(struct mbuf *m0, ...)
282 struct snpa_hdr *shp; /* subnetwork header */
283 struct esis_fixed *pdu = mtod(m0, struct esis_fixed *);
284 int type;
285 struct ifaddr *ifa;
286 va_list ap;
288 va_start(ap, m0);
289 shp = va_arg(ap, struct snpa_hdr *);
290 va_end(ap);
292 IFADDR_FOREACH(ifa, shp->snh_ifp)
293 if (ifa->ifa_addr->sa_family == AF_ISO)
294 break;
295 /* if we have no iso address just send it to the sockets */
296 if (ifa == 0)
297 goto bad;
300 * check checksum if necessary
302 if (ESIS_CKSUM_REQUIRED(pdu) &&
303 iso_check_csum(m0, (int) pdu->esis_hdr_len)) {
304 esis_stat.es_badcsum++;
305 goto bad;
307 /* check version */
308 if (pdu->esis_vers != ESIS_VERSION) {
309 esis_stat.es_badvers++;
310 goto bad;
312 type = pdu->esis_type & 0x1f;
313 switch (type) {
314 case ESIS_ESH:
315 esis_eshinput(m0, shp);
316 break;
318 case ESIS_ISH:
319 esis_ishinput(m0, shp);
320 break;
322 case ESIS_RD:
323 esis_rdinput(m0, shp);
324 break;
326 default:
327 esis_stat.es_badtype++;
330 bad:
331 if (esis_pcb.lh_first != 0)
332 isis_input(m0, shp);
333 else
334 m_freem(m0);
338 * FUNCTION: esis_rdoutput
340 * PURPOSE: Transmit a redirect pdu
342 * RETURNS: nothing
344 * SIDE EFFECTS:
346 * NOTES: Assumes there is enough space for fixed part of header,
347 * DA, BSNPA and NET in first mbuf.
349 void
350 esis_rdoutput(
351 struct snpa_hdr *inbound_shp, /* snpa hdr from incoming packet */
352 struct mbuf *inbound_m, /* incoming pkt itself */
353 struct clnp_optidx *inbound_oidx, /* clnp options assoc with
354 * incoming pkt */
355 struct iso_addr *rd_dstnsap, /* ultimate destination of pkt */
356 struct rtentry *rt) /* snpa cache info regarding next hop of pkt */
358 struct mbuf *m, *m0;
359 char *cp;
360 struct esis_fixed *pdu;
361 int len;
362 struct sockaddr_iso siso;
363 struct ifnet *ifp = inbound_shp->snh_ifp;
364 struct sockaddr_dl *sdl;
365 const struct iso_addr *rd_gwnsap;
367 if (rt->rt_flags & RTF_GATEWAY) {
368 rd_gwnsap = &satosiso(rt->rt_gateway)->siso_addr;
369 rt = rtalloc1(rt->rt_gateway, 0);
370 } else
371 rd_gwnsap = &satocsiso(rt_getkey(rt))->siso_addr;
372 if (rt == 0 || (sdl = (struct sockaddr_dl *) rt->rt_gateway) == 0 ||
373 sdl->sdl_family != AF_LINK) {
375 * maybe we should have a function that you could put in the
376 * iso_ifaddr structure which could translate iso_addrs into
377 * snpa's where there is a known mapping for that address
378 * type
380 esis_stat.es_badtype++;
381 return;
383 esis_stat.es_rdsent++;
384 #ifdef ARGO_DEBUG
385 if (argo_debug[D_ESISOUTPUT]) {
386 printf(
387 "esis_rdoutput: ifp %p (%s), ht %d, m %p, oidx %p\n",
388 ifp, ifp->if_xname, esis_holding_time,
389 inbound_m, inbound_oidx);
390 printf("\tdestination: %s\n", clnp_iso_addrp(rd_dstnsap));
391 printf("\tredirected toward:%s\n", clnp_iso_addrp(rd_gwnsap));
393 #endif
395 if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
396 esis_stat.es_nomem++;
397 return;
399 memset(mtod(m, void *), 0, MHLEN);
401 pdu = mtod(m, struct esis_fixed *);
402 cp = (void *) (pdu + 1); /* pointer arith.; 1st byte after
403 * header */
404 len = sizeof(struct esis_fixed);
407 * Build fixed part of header
409 pdu->esis_proto_id = ISO9542_ESIS;
410 pdu->esis_vers = ESIS_VERSION;
411 pdu->esis_type = ESIS_RD;
412 HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, esis_holding_time);
414 /* Insert destination address */
415 (void) esis_insert_addr((void **)&cp, &len, rd_dstnsap, m, 0);
417 /* Insert the snpa of better next hop */
418 *cp++ = sdl->sdl_alen;
419 memcpy(cp, CLLADDR(sdl), sdl->sdl_alen);
420 cp += sdl->sdl_alen;
421 len += (sdl->sdl_alen + 1);
424 * If the next hop is not the destination, then it ought to be an IS
425 * and it should be inserted next. Else, set the NETL to 0
427 /* PHASE2 use mask from ifp of outgoing interface */
428 if (!iso_addrmatch1(rd_dstnsap, rd_gwnsap)) {
429 #if 0
430 /* this should not happen: */
431 if ((nhop_sc->sc_flags & SNPA_IS) == 0) {
432 printf(
433 "esis_rdoutput: next hop is not dst and not an IS\n");
434 m_freem(m0);
435 return;
437 #endif
438 (void) esis_insert_addr((void **)&cp, &len, rd_gwnsap, m, 0);
439 } else {
440 *cp++ = 0; /* NETL */
441 len++;
443 m->m_len = len;
446 * PHASE2
447 * If redirect is to an IS, add an address mask. The mask to be
448 * used should be the mask present in the routing entry used to
449 * forward the original data packet.
453 * Copy Qos, priority, or security options present in original npdu
455 if (inbound_oidx) {
456 /* THIS CODE IS CURRENTLY (mostly) UNTESTED */
457 int optlen = 0;
458 if (inbound_oidx->cni_qos_formatp)
459 optlen += (inbound_oidx->cni_qos_len + 2);
460 if (inbound_oidx->cni_priorp) /* priority option is 1 byte
461 * long */
462 optlen += 3;
463 if (inbound_oidx->cni_securep)
464 optlen += (inbound_oidx->cni_secure_len + 2);
465 if (M_TRAILINGSPACE(m) < optlen) {
466 EXTEND_PACKET(m, m0, cp);
467 m->m_len = 0;
468 /* assumes MLEN > optlen */
470 /* assume MLEN-len > optlen */
472 * When copying options, copy from ptr - 2 in order to grab
473 * the option code and length
475 if (inbound_oidx->cni_qos_formatp) {
476 memcpy(cp, mtod(inbound_m, char *) +
477 inbound_oidx->cni_qos_formatp - 2,
478 (unsigned) (inbound_oidx->cni_qos_len + 2));
479 cp += inbound_oidx->cni_qos_len + 2;
481 if (inbound_oidx->cni_priorp) {
482 memcpy(cp, mtod(inbound_m, char *) +
483 inbound_oidx->cni_priorp - 2, 3);
484 cp += 3;
486 if (inbound_oidx->cni_securep) {
487 memcpy(cp, mtod(inbound_m, char *) +
488 inbound_oidx->cni_securep - 2,
489 (unsigned) (inbound_oidx->cni_secure_len + 2));
490 cp += inbound_oidx->cni_secure_len + 2;
492 m->m_len += optlen;
493 len += optlen;
495 pdu->esis_hdr_len = m0->m_pkthdr.len = len;
496 iso_gen_csum(m0, ESIS_CKSUM_OFF, (int) pdu->esis_hdr_len);
498 memset((void *) & siso, 0, sizeof(siso));
499 siso.siso_family = AF_ISO;
500 siso.siso_data[0] = AFI_SNA;
501 siso.siso_nlen = 6 + 1; /* should be taken from snpa_hdr */
502 /* +1 is for AFI */
503 memcpy(siso.siso_data + 1, inbound_shp->snh_shost, 6);
504 (ifp->if_output) (ifp, m0, sisotosa(&siso), 0);
508 * FUNCTION: esis_insert_addr
510 * PURPOSE: Insert an iso_addr into a buffer
512 * RETURNS: true if buffer was big enough, else false
514 * SIDE EFFECTS: Increment buf & len according to size of iso_addr
516 * NOTES: Plus 1 here is for length byte
519 esis_insert_addr(
520 void **bufv, /* ptr to buffer to put address into */
521 int *len, /* ptr to length of buffer so far */
522 const struct iso_addr *isoa, /* ptr to address */
523 struct mbuf *m, /* determine if there remains space */
524 int nsellen)
526 char *buf = *bufv;
527 int newlen, result = 0;
529 newlen = isoa->isoa_len - nsellen + 1;
530 if (newlen <= M_TRAILINGSPACE(m)) {
531 memcpy(buf, isoa, newlen);
532 *len += newlen;
533 buf += newlen;
534 m->m_len += newlen;
535 result = 1;
537 *bufv = buf;
538 return (result);
541 #define ESIS_EXTRACT_ADDR(d, b) { d = (struct iso_addr *)(b); b += (1 + *b); \
542 if (b > buflim) {esis_stat.es_toosmall++; goto bad;}}
543 #define ESIS_NEXT_OPTION(b) { b += (2 + b[1]); \
544 if (b > buflim) {esis_stat.es_toosmall++; goto bad;}}
545 int ESHonly = 0;
548 * FUNCTION: esis_eshinput
550 * PURPOSE: Process an incoming ESH pdu
552 * RETURNS: nothing
554 * SIDE EFFECTS:
556 * NOTES:
558 void
559 esis_eshinput(
560 struct mbuf *m, /* esh pdu */
561 struct snpa_hdr *shp) /* subnetwork header */
563 struct esis_fixed *pdu = mtod(m, struct esis_fixed *);
564 u_short ht; /* holding time */
565 struct iso_addr *nsap = NULL;
566 int naddr;
567 u_char *buf = (u_char *) (pdu + 1);
568 u_char *buflim = pdu->esis_hdr_len + (u_char *) pdu;
569 int new_entry = 0;
571 esis_stat.es_eshrcvd++;
573 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
575 naddr = *buf++;
576 if (buf >= buflim)
577 goto bad;
578 if (naddr == 1) {
579 ESIS_EXTRACT_ADDR(nsap, buf);
580 new_entry = snpac_add(shp->snh_ifp,
581 nsap, shp->snh_shost, SNPA_ES, ht, 0);
582 } else {
583 int nsellength = 0, nlen = 0;
584 struct ifaddr *ifa;
586 * See if we want to compress out multiple nsaps
587 * differing only by nsel
589 IFADDR_FOREACH(ifa, shp->snh_ifp)
590 if (ifa->ifa_addr->sa_family == AF_ISO) {
591 nsellength =
592 ((struct iso_ifaddr *) ifa)->ia_addr.siso_tlen;
593 break;
595 #ifdef ARGO_DEBUG
596 if (argo_debug[D_ESISINPUT]) {
597 printf(
598 "esis_eshinput: esh: ht %d, naddr %d nsellength %d\n",
599 ht, naddr, nsellength);
601 #endif
602 while (naddr-- > 0) {
603 struct iso_addr *nsap2;
604 u_char *buf2;
605 ESIS_EXTRACT_ADDR(nsap, buf);
607 * see if there is at least one more nsap in ESH
608 * differing only by nsel
610 if (nsellength != 0)
611 for (buf2 = buf; buf2 < buflim;) {
612 ESIS_EXTRACT_ADDR(nsap2, buf2);
613 #ifdef ARGO_DEBUG
614 if (argo_debug[D_ESISINPUT]) {
615 printf(
616 "esis_eshinput: comparing %s ",
617 clnp_iso_addrp(nsap));
618 printf("and %s\n",
619 clnp_iso_addrp(nsap2));
621 #endif
622 if (memcmp(nsap->isoa_genaddr,
623 nsap2->isoa_genaddr,
624 nsap->isoa_len - nsellength)
625 == 0) {
626 nlen = nsellength;
627 break;
630 new_entry |= snpac_add(shp->snh_ifp,
631 nsap, shp->snh_shost, SNPA_ES, ht, nlen);
632 nlen = 0;
635 #ifdef ARGO_DEBUG
636 if (argo_debug[D_ESISINPUT]) {
637 printf("esis_eshinput: nsap %s is %s\n",
638 clnp_iso_addrp(nsap), new_entry ? "new" : "old");
640 #endif
641 if (new_entry && (iso_systype & SNPA_IS))
642 esis_shoutput(shp->snh_ifp, ESIS_ISH, esis_holding_time,
643 shp->snh_shost, 6, (struct iso_addr *) 0);
644 bad:
645 return;
649 * FUNCTION: esis_ishinput
651 * PURPOSE: process an incoming ISH pdu
653 * RETURNS:
655 * SIDE EFFECTS:
657 * NOTES:
659 void
660 esis_ishinput(
661 struct mbuf *m, /* esh pdu */
662 struct snpa_hdr *shp) /* subnetwork header */
664 struct esis_fixed *pdu = mtod(m, struct esis_fixed *);
665 u_short ht, newct; /* holding time */
666 struct iso_addr *nsap; /* Network Entity Title */
667 u_char *buf = (u_char *) (pdu + 1);
668 u_char *buflim = pdu->esis_hdr_len + (u_char *) pdu;
669 int new_entry;
671 esis_stat.es_ishrcvd++;
672 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
674 #ifdef ARGO_DEBUG
675 if (argo_debug[D_ESISINPUT]) {
676 printf("esis_ishinput: ish: ht %d\n", ht);
678 #endif
679 if (ESHonly)
680 goto bad;
682 ESIS_EXTRACT_ADDR(nsap, buf);
684 while (buf < buflim) {
685 switch (*buf) {
686 case ESISOVAL_ESCT:
687 if (iso_systype & SNPA_IS)
688 break;
689 if (buf[1] != 2)
690 goto bad;
691 CTOH(buf[2], buf[3], newct);
692 if ((u_short) esis_config_time != newct) {
693 callout_stop(&esis_config_ch);
694 esis_config_time = newct;
695 esis_config(NULL);
697 break;
699 default:
700 printf("Unknown ISH option: %x\n", *buf);
702 ESIS_NEXT_OPTION(buf);
704 new_entry = snpac_add(shp->snh_ifp, nsap, shp->snh_shost, SNPA_IS,
705 ht, 0);
706 #ifdef ARGO_DEBUG
707 if (argo_debug[D_ESISINPUT]) {
708 printf("esis_ishinput: nsap %s is %s\n",
709 clnp_iso_addrp(nsap), new_entry ? "new" : "old");
711 #endif
713 if (new_entry)
714 esis_shoutput(shp->snh_ifp,
715 iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
716 esis_holding_time, shp->snh_shost, 6, (struct iso_addr *) 0);
717 bad:
718 return;
722 * FUNCTION: esis_rdinput
724 * PURPOSE: Process an incoming RD pdu
726 * RETURNS:
728 * SIDE EFFECTS:
730 * NOTES:
732 void
733 esis_rdinput(
734 struct mbuf *m0, /* esh pdu */
735 struct snpa_hdr *shp) /* subnetwork header */
737 struct esis_fixed *pdu = mtod(m0, struct esis_fixed *);
738 u_short ht; /* holding time */
739 struct iso_addr *da, *net = 0, *netmask = 0, *snpamask = 0;
740 struct iso_addr *bsnpa;
741 u_char *buf = (u_char *) (pdu + 1);
742 u_char *buflim = pdu->esis_hdr_len + (u_char *) pdu;
744 esis_stat.es_rdrcvd++;
746 /* intermediate systems ignore redirects */
747 if (iso_systype & SNPA_IS)
748 return;
749 if (ESHonly)
750 return;
752 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
753 if (buf >= buflim)
754 return;
756 /* Extract DA */
757 ESIS_EXTRACT_ADDR(da, buf);
759 /* Extract better snpa */
760 ESIS_EXTRACT_ADDR(bsnpa, buf);
762 /* Extract NET if present */
763 if (buf < buflim) {
764 if (*buf == 0)
765 buf++; /* no NET present, skip NETL anyway */
766 else
767 ESIS_EXTRACT_ADDR(net, buf);
769 /* process options */
770 while (buf < buflim) {
771 switch (*buf) {
772 case ESISOVAL_SNPAMASK:
773 if (snpamask) /* duplicate */
774 return;
775 snpamask = (struct iso_addr *) (buf + 1);
776 break;
778 case ESISOVAL_NETMASK:
779 if (netmask) /* duplicate */
780 return;
781 netmask = (struct iso_addr *) (buf + 1);
782 break;
784 default:
785 printf("Unknown option in ESIS RD (0x%x)\n", buf[-1]);
787 ESIS_NEXT_OPTION(buf);
790 #ifdef ARGO_DEBUG
791 if (argo_debug[D_ESISINPUT]) {
792 printf("esis_rdinput: rd: ht %d, da %s\n", ht,
793 clnp_iso_addrp(da));
794 if (net)
795 printf("\t: net %s\n", clnp_iso_addrp(net));
797 #endif
799 * If netl is zero, then redirect is to an ES. We need to add an entry
800 * to the snpa cache for (destination, better snpa).
801 * If netl is not zero, then the redirect is to an IS. In this
802 * case, add an snpa cache entry for (net, better snpa).
804 * If the redirect is to an IS, add a route entry towards that
805 * IS.
807 if (net == 0 || net->isoa_len == 0 || snpamask) {
808 /* redirect to an ES */
809 snpac_add(shp->snh_ifp, da,
810 bsnpa->isoa_genaddr, SNPA_ES, ht, 0);
811 } else {
812 snpac_add(shp->snh_ifp, net,
813 bsnpa->isoa_genaddr, SNPA_IS, ht, 0);
814 snpac_addrt(shp->snh_ifp, da, net, netmask);
816 bad: ; /* Needed by ESIS_NEXT_OPTION */
820 * FUNCTION: esis_config
822 * PURPOSE: Report configuration
824 * RETURNS:
826 * SIDE EFFECTS:
828 * NOTES: Called every esis_config_time seconds
830 /*ARGSUSED*/
831 void
832 esis_config(void *v)
834 struct ifnet *ifp;
836 callout_reset(&esis_config_ch, hz * esis_config_time,
837 esis_config, NULL);
840 * Report configuration for each interface that - is UP - has
841 * BROADCAST capability - has an ISO address
844 * Todo: a better way would be to construct the esh or ish once and
845 * copy it out for all devices, possibly calling a method in the
846 * iso_ifaddr structure to encapsulate and transmit it. This could
847 * work to advantage for non-broadcast media
850 TAILQ_FOREACH(ifp, &ifnet, if_list) {
851 if ((ifp->if_flags & IFF_UP) &&
852 (ifp->if_flags & IFF_BROADCAST)) {
853 /* search for an ISO address family */
854 struct ifaddr *ifa;
856 IFADDR_FOREACH(ifa, ifp) {
857 if (ifa->ifa_addr->sa_family == AF_ISO) {
858 esis_shoutput(ifp,
859 iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
860 esis_holding_time,
861 (iso_systype & SNPA_ES ? all_is_snpa :
862 all_es_snpa), 6, (struct iso_addr *) 0);
863 break;
871 * FUNCTION: esis_shoutput
873 * PURPOSE: Transmit an esh or ish pdu
875 * RETURNS: nothing
877 * SIDE EFFECTS:
879 * NOTES:
881 void
882 esis_shoutput(
883 struct ifnet *ifp,
884 int type,
885 int ht,
886 const void *sn_addr,
887 int sn_len,
888 struct iso_addr *isoa)
890 struct mbuf *m, *m0;
891 char *cp, *naddrp;
892 int naddr = 0;
893 struct esis_fixed *pdu;
894 struct iso_ifaddr *ia;
895 int len;
896 struct sockaddr_iso siso;
898 if (type == ESIS_ESH)
899 esis_stat.es_eshsent++;
900 else if (type == ESIS_ISH)
901 esis_stat.es_ishsent++;
902 else {
903 printf("esis_shoutput: bad pdu type\n");
904 return;
907 #ifdef ARGO_DEBUG
908 if (argo_debug[D_ESISOUTPUT]) {
909 int i;
910 printf("esis_shoutput: ifp %p (%s), %s, ht %d, to: [%d] ",
911 ifp, ifp->if_xname,
912 type == ESIS_ESH ? "esh" : "ish",
913 ht, sn_len);
914 for (i = 0; i < sn_len; i++)
915 printf("%x%c", *((const char *)sn_addr + i),
916 i < (sn_len - 1) ? ':' : ' ');
917 printf("\n");
919 #endif
921 if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
922 esis_stat.es_nomem++;
923 return;
925 memset(mtod(m, void *), 0, MHLEN);
927 pdu = mtod(m, struct esis_fixed *);
928 naddrp = cp = (char *) (pdu + 1);
929 len = sizeof(struct esis_fixed);
932 * Build fixed part of header
934 pdu->esis_proto_id = ISO9542_ESIS;
935 pdu->esis_vers = ESIS_VERSION;
936 pdu->esis_type = type;
937 HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
939 if (type == ESIS_ESH) {
940 cp++;
941 len++;
943 m->m_len = len;
944 if (isoa) {
946 * Here we are responding to a clnp packet sent to an NSAP
947 * that is ours which was sent to the MAC addr all_es's.
948 * It is possible that we did not specifically advertise this
949 * NSAP, even though it is ours, so we will respond
950 * directly to the sender that we are here. If we do have
951 * multiple NSEL's we'll tack them on so he can compress
952 * them out.
954 (void) esis_insert_addr((void **)&cp, &len, isoa, m, 0);
955 naddr = 1;
957 TAILQ_FOREACH(ia, &iso_ifaddr, ia_list) {
958 int nsellen = (type == ESIS_ISH ? ia->ia_addr.siso_tlen : 0);
959 int n = ia->ia_addr.siso_nlen;
960 struct iso_ifaddr *ia2;
962 if (type == ESIS_ISH && naddr > 0)
963 break;
964 TAILQ_FOREACH(ia2, &iso_ifaddr, ia_list)
965 if (memcmp(ia->ia_addr.siso_data,
966 ia2->ia_addr.siso_data, n) == 0)
967 break;
968 if (ia2 != ia)
969 continue; /* Means we have previously copied
970 * this nsap */
971 if (isoa && memcmp(ia->ia_addr.siso_data,
972 isoa->isoa_genaddr, n) == 0) {
973 isoa = 0;
974 continue; /* Ditto */
976 #ifdef ARGO_DEBUG
977 if (argo_debug[D_ESISOUTPUT]) {
978 printf("esis_shoutput: adding NSAP %s\n",
979 clnp_iso_addrp(&ia->ia_addr.siso_addr));
981 #endif
982 if (!esis_insert_addr((void **)&cp, &len,
983 &ia->ia_addr.siso_addr, m, nsellen)) {
984 EXTEND_PACKET(m, m0, cp);
985 (void) esis_insert_addr((void **)&cp, &len,
986 &ia->ia_addr.siso_addr, m,
987 nsellen);
989 naddr++;
992 if (type == ESIS_ESH)
993 *naddrp = naddr;
994 else {
995 /* add suggested es config timer option to ISH */
996 if (M_TRAILINGSPACE(m) < 4) {
997 printf("esis_shoutput: extending packet\n");
998 EXTEND_PACKET(m, m0, cp);
1000 *cp++ = ESISOVAL_ESCT;
1001 *cp++ = 2;
1002 HTOC(*cp, *(cp + 1), esis_esconfig_time);
1003 len += 4;
1004 m->m_len += 4;
1005 #ifdef ARGO_DEBUG
1006 if (argo_debug[D_ESISOUTPUT]) {
1007 printf("m0 %p, m %p, data %p, len %d, cp %p\n",
1008 m0, m, m->m_data, m->m_len, cp);
1010 #endif
1013 m0->m_pkthdr.len = len;
1014 pdu->esis_hdr_len = len;
1015 iso_gen_csum(m0, ESIS_CKSUM_OFF, (int) pdu->esis_hdr_len);
1017 memset((void *) & siso, 0, sizeof(siso));
1018 siso.siso_family = AF_ISO;
1019 siso.siso_data[0] = AFI_SNA;
1020 siso.siso_nlen = sn_len + 1;
1021 memcpy(siso.siso_data + 1, sn_addr, (unsigned) sn_len);
1022 (ifp->if_output) (ifp, m0, sisotosa(&siso), 0);
1026 * FUNCTION: isis_input
1028 * PURPOSE: Process an incoming isis packet
1030 * RETURNS: nothing
1032 * SIDE EFFECTS:
1034 * NOTES:
1036 void
1037 isis_input(struct mbuf *m0, ...)
1039 struct snpa_hdr *shp; /* subnetwork header */
1040 struct rawcb *rp, *first_rp = 0;
1041 struct ifnet *ifp;
1042 struct mbuf *mm;
1043 va_list ap;
1045 va_start(ap, m0);
1046 shp = va_arg(ap, struct snpa_hdr *);
1047 va_end(ap);
1048 ifp = shp->snh_ifp;
1050 #ifdef ARGO_DEBUG
1051 if (argo_debug[D_ISISINPUT]) {
1052 int i;
1054 printf("isis_input: pkt on ifp %p (%s): from:",
1055 ifp, ifp->if_xname);
1056 for (i = 0; i < 6; i++)
1057 printf("%x%c", shp->snh_shost[i] & 0xff,
1058 (i < 5) ? ':' : ' ');
1059 printf(" to:");
1060 for (i = 0; i < 6; i++)
1061 printf("%x%c", shp->snh_dhost[i] & 0xff,
1062 (i < 5) ? ':' : ' ');
1063 printf("\n");
1065 #endif
1066 esis_dl.sdl_alen = ifp->if_addrlen;
1067 esis_dl.sdl_index = ifp->if_index;
1068 memcpy((void *) esis_dl.sdl_data, shp->snh_shost, esis_dl.sdl_alen);
1069 for (rp = esis_pcb.lh_first; rp != 0; rp = rp->rcb_list.le_next) {
1070 if (first_rp == 0) {
1071 first_rp = rp;
1072 continue;
1074 /* can't block at interrupt level */
1075 if ((mm = m_copy(m0, 0, M_COPYALL)) != NULL) {
1076 if (sbappendaddr(&rp->rcb_socket->so_rcv,
1077 (struct sockaddr *) &esis_dl, mm,
1078 (struct mbuf *) 0) != 0) {
1079 sorwakeup(rp->rcb_socket);
1080 } else {
1081 #ifdef ARGO_DEBUG
1082 if (argo_debug[D_ISISINPUT]) {
1083 printf(
1084 "Error in sbappenaddr, mm = %p\n", mm);
1086 #endif
1087 m_freem(mm);
1091 if (first_rp && sbappendaddr(&first_rp->rcb_socket->so_rcv,
1092 (struct sockaddr *) &esis_dl, m0, (struct mbuf *) 0) != 0) {
1093 sorwakeup(first_rp->rcb_socket);
1094 return;
1096 m_freem(m0);
1100 isis_output(struct mbuf *m, ...)
1102 struct sockaddr_dl *sdl;
1103 struct ifnet *ifp;
1104 struct ifaddr *ifa;
1105 struct sockaddr_iso siso;
1106 int error = 0;
1107 unsigned sn_len;
1108 va_list ap;
1110 va_start(ap, m);
1111 sdl = va_arg(ap, struct sockaddr_dl *);
1112 va_end(ap);
1114 /* we assume here we have a sockaddr_dl ... check it */
1115 if (sdl->sdl_family != AF_LINK) {
1116 error = EINVAL;
1117 goto release;
1119 if (sdl->sdl_len < 8 + sdl->sdl_nlen + sdl->sdl_alen + sdl->sdl_slen) {
1120 error = EINVAL;
1121 goto release;
1124 ifa = ifa_ifwithnet((struct sockaddr *) sdl); /* get ifp from sdl */
1125 if (ifa == 0) {
1126 #ifdef ARGO_DEBUG
1127 if (argo_debug[D_ISISOUTPUT]) {
1128 printf("isis_output: interface not found\n");
1130 #endif
1131 error = EINVAL;
1132 goto release;
1134 ifp = ifa->ifa_ifp;
1135 sn_len = sdl->sdl_alen;
1136 #ifdef ARGO_DEBUG
1137 if (argo_debug[D_ISISOUTPUT]) {
1138 const u_char *cp = (const u_char *)CLLADDR(sdl),
1139 *cplim = cp + sn_len;
1140 printf("isis_output: ifp %p (%s), to: ",
1141 ifp, ifp->if_xname);
1142 while (cp < cplim) {
1143 printf("%x", *cp++);
1144 printf("%c", (cp < cplim) ? ':' : ' ');
1146 printf("\n");
1148 #endif
1149 memset((void *) & siso, 0, sizeof(siso));
1150 siso.siso_family = AF_ISO; /* This convention may be useful for
1151 * X.25 */
1152 if (sn_len == 0)
1153 siso.siso_nlen = 0;
1154 else {
1155 siso.siso_data[0] = AFI_SNA;
1156 siso.siso_nlen = sn_len + 1;
1157 memcpy(siso.siso_data + 1, CLLADDR(sdl), sn_len);
1159 error = (ifp->if_output) (ifp, m, sisotosa(&siso), 0);
1160 if (error) {
1161 #ifdef ARGO_DEBUG
1162 if (argo_debug[D_ISISOUTPUT]) {
1163 printf("isis_output: error from if_output is %d\n",
1164 error);
1166 #endif
1168 return (error);
1170 release:
1171 if (m != NULL)
1172 m_freem(m);
1173 return (error);
1178 * FUNCTION: esis_ctlinput
1180 * PURPOSE: Handle the PRC_IFDOWN transition
1182 * RETURNS: nothing
1184 * SIDE EFFECTS:
1186 * NOTES: Calls snpac_flush for interface specified.
1187 * The loop through iso_ifaddr is stupid because
1188 * back in if_down, we knew the ifp...
1190 void *
1191 esis_ctlinput(
1192 int req, /* request: we handle only PRC_IFDOWN */
1193 const struct sockaddr *siso, /* address of ifp */
1194 void *dummy)
1196 struct iso_ifaddr *ia; /* scan through interface addresses */
1198 /*XXX correct? */
1199 if (siso->sa_family != AF_ISO)
1200 return NULL;
1201 if (req == PRC_IFDOWN)
1202 TAILQ_FOREACH(ia, &iso_ifaddr, ia_list) {
1203 if (iso_addrmatch(IA_SIS(ia),
1204 (const struct sockaddr_iso *)siso))
1205 snpac_flushifp(ia->ia_ifp);
1207 return NULL;
1210 #endif /* ISO */