FreeBSD/Linux Kernel Cross Reference
sys/netiso/esis.c
1 /* $NetBSD: esis.c,v 1.52 2008/05/11 20:20:27 dyoung 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
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.
46
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.
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.52 2008/05/11 20:20:27 dyoung 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 #include <machine/stdarg.h>
96
97 /*
98 * Global variables to esis implementation
99 *
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.
103 *
104 */
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,
115 };
116
117 struct callout esis_config_ch;
118
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;\
128 }
129
130 /*
131 * FUNCTION: esis_init
132 *
133 * PURPOSE: Initialize the kernel portion of esis protocol
134 *
135 * RETURNS: nothing
136 *
137 * SIDE EFFECTS:
138 *
139 * NOTES:
140 */
141 void
142 esis_init(void)
143 {
144 extern struct clnl_protosw clnl_protox[256];
145
146 LIST_INIT(&esis_pcb);
147
148 callout_init(&snpac_age_ch, 0);
149 callout_init(&esis_config_ch, 0);
150
151 callout_reset(&snpac_age_ch, hz, snpac_age, NULL);
152 callout_reset(&esis_config_ch, hz, esis_config, NULL);
153
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 */
159 }
160
161 /*
162 * FUNCTION: esis_usrreq
163 *
164 * PURPOSE: Handle user level esis requests
165 *
166 * RETURNS: 0 or appropriate errno
167 *
168 * SIDE EFFECTS:
169 *
170 */
171 /* ARGSUSED */
172 int
173 esis_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam,
174 struct mbuf *control, struct lwp *l)
175 {
176 struct rawcb *rp;
177 int error = 0;
178
179 if (req == PRU_CONTROL)
180 return (EOPNOTSUPP);
181
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;
190 }
191
192 switch (req) {
193
194 case PRU_ATTACH:
195 sosetlock(so);
196 if (rp != 0) {
197 error = EISCONN;
198 break;
199 }
200
201 if (l == NULL) {
202 error = EACCES;
203 break;
204 }
205
206 /* XXX: raw socket permission is checked in socreate() */
207
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;
212 }
213 MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK|M_ZERO);
214 if (rp == 0) {
215 error = ENOBUFS;
216 break;
217 }
218 rp->rcb_socket = so;
219 LIST_INSERT_HEAD(&esis_pcb, rp, rcb_list);
220 so->so_pcb = rp;
221 break;
222
223 case PRU_SEND:
224 if (control && control->m_len) {
225 m_freem(control);
226 m_freem(m);
227 error = EINVAL;
228 break;
229 }
230 if (nam == NULL) {
231 m_freem(m);
232 error = EINVAL;
233 break;
234 }
235 /* error checking here */
236 error = isis_output(m, mtod(nam, struct sockaddr_dl *));
237 break;
238
239 case PRU_SENDOOB:
240 m_freem(control);
241 m_freem(m);
242 error = EOPNOTSUPP;
243 break;
244
245 case PRU_DETACH:
246 raw_detach(rp);
247 break;
248
249 case PRU_SHUTDOWN:
250 socantsendmore(so);
251 break;
252
253 case PRU_SENSE:
254 /*
255 * stat: don't bother with a blocksize.
256 */
257 return (0);
258
259 default:
260 error = EOPNOTSUPP;
261 break;
262 }
263
264 release:
265 return (error);
266 }
267
268 /*
269 * FUNCTION: esis_input
270 *
271 * PURPOSE: Process an incoming esis packet
272 *
273 * RETURNS: nothing
274 *
275 * SIDE EFFECTS:
276 *
277 * NOTES:
278 */
279 void
280 esis_input(struct mbuf *m0, ...)
281 {
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;
287
288 va_start(ap, m0);
289 shp = va_arg(ap, struct snpa_hdr *);
290 va_end(ap);
291
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;
298
299 /*
300 * check checksum if necessary
301 */
302 if (ESIS_CKSUM_REQUIRED(pdu) &&
303 iso_check_csum(m0, (int) pdu->esis_hdr_len)) {
304 esis_stat.es_badcsum++;
305 goto bad;
306 }
307 /* check version */
308 if (pdu->esis_vers != ESIS_VERSION) {
309 esis_stat.es_badvers++;
310 goto bad;
311 }
312 type = pdu->esis_type & 0x1f;
313 switch (type) {
314 case ESIS_ESH:
315 esis_eshinput(m0, shp);
316 break;
317
318 case ESIS_ISH:
319 esis_ishinput(m0, shp);
320 break;
321
322 case ESIS_RD:
323 esis_rdinput(m0, shp);
324 break;
325
326 default:
327 esis_stat.es_badtype++;
328 }
329
330 bad:
331 if (esis_pcb.lh_first != 0)
332 isis_input(m0, shp);
333 else
334 m_freem(m0);
335 }
336
337 /*
338 * FUNCTION: esis_rdoutput
339 *
340 * PURPOSE: Transmit a redirect pdu
341 *
342 * RETURNS: nothing
343 *
344 * SIDE EFFECTS:
345 *
346 * NOTES: Assumes there is enough space for fixed part of header,
347 * DA, BSNPA and NET in first mbuf.
348 */
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 */
357 {
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;
366
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) {
374 /*
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
379 */
380 esis_stat.es_badtype++;
381 return;
382 }
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));
392 }
393 #endif
394
395 if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
396 esis_stat.es_nomem++;
397 return;
398 }
399 bzero(mtod(m, void *), MHLEN);
400
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);
405
406 /*
407 * Build fixed part of header
408 */
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);
413
414 /* Insert destination address */
415 (void) esis_insert_addr((void **)&cp, &len, rd_dstnsap, m, 0);
416
417 /* Insert the snpa of better next hop */
418 *cp++ = sdl->sdl_alen;
419 bcopy(CLLADDR(sdl), cp, sdl->sdl_alen);
420 cp += sdl->sdl_alen;
421 len += (sdl->sdl_alen + 1);
422
423 /*
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
426 */
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;
436 }
437 #endif
438 (void) esis_insert_addr((void **)&cp, &len, rd_gwnsap, m, 0);
439 } else {
440 *cp++ = 0; /* NETL */
441 len++;
442 }
443 m->m_len = len;
444
445 /*
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.
450 */
451
452 /*
453 * Copy Qos, priority, or security options present in original npdu
454 */
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 */
469 }
470 /* assume MLEN-len > optlen */
471 /*
472 * When copying options, copy from ptr - 2 in order to grab
473 * the option code and length
474 */
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;
480 }
481 if (inbound_oidx->cni_priorp) {
482 memcpy(cp, mtod(inbound_m, char *) +
483 inbound_oidx->cni_priorp - 2, 3);
484 cp += 3;
485 }
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;
491 }
492 m->m_len += optlen;
493 len += optlen;
494 }
495 pdu->esis_hdr_len = m0->m_pkthdr.len = len;
496 iso_gen_csum(m0, ESIS_CKSUM_OFF, (int) pdu->esis_hdr_len);
497
498 bzero((void *) & siso, 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 bcopy(inbound_shp->snh_shost, siso.siso_data + 1, 6);
504 (ifp->if_output) (ifp, m0, sisotosa(&siso), 0);
505 }
506
507 /*
508 * FUNCTION: esis_insert_addr
509 *
510 * PURPOSE: Insert an iso_addr into a buffer
511 *
512 * RETURNS: true if buffer was big enough, else false
513 *
514 * SIDE EFFECTS: Increment buf & len according to size of iso_addr
515 *
516 * NOTES: Plus 1 here is for length byte
517 */
518 int
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)
525 {
526 char *buf = *bufv;
527 int newlen, result = 0;
528
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;
536 }
537 *bufv = buf;
538 return (result);
539 }
540
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;
546
547 /*
548 * FUNCTION: esis_eshinput
549 *
550 * PURPOSE: Process an incoming ESH pdu
551 *
552 * RETURNS: nothing
553 *
554 * SIDE EFFECTS:
555 *
556 * NOTES:
557 */
558 void
559 esis_eshinput(
560 struct mbuf *m, /* esh pdu */
561 struct snpa_hdr *shp) /* subnetwork header */
562 {
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;
570
571 esis_stat.es_eshrcvd++;
572
573 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
574
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;
585 /*
586 * See if we want to compress out multiple nsaps
587 * differing only by nsel
588 */
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;
594 }
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);
600 }
601 #endif
602 while (naddr-- > 0) {
603 struct iso_addr *nsap2;
604 u_char *buf2;
605 ESIS_EXTRACT_ADDR(nsap, buf);
606 /*
607 * see if there is at least one more nsap in ESH
608 * differing only by nsel
609 */
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));
620 }
621 #endif
622 if (memcmp(nsap->isoa_genaddr,
623 nsap2->isoa_genaddr,
624 nsap->isoa_len - nsellength)
625 == 0) {
626 nlen = nsellength;
627 break;
628 }
629 }
630 new_entry |= snpac_add(shp->snh_ifp,
631 nsap, shp->snh_shost, SNPA_ES, ht, nlen);
632 nlen = 0;
633 }
634 }
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");
639 }
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;
646 }
647
648 /*
649 * FUNCTION: esis_ishinput
650 *
651 * PURPOSE: process an incoming ISH pdu
652 *
653 * RETURNS:
654 *
655 * SIDE EFFECTS:
656 *
657 * NOTES:
658 */
659 void
660 esis_ishinput(
661 struct mbuf *m, /* esh pdu */
662 struct snpa_hdr *shp) /* subnetwork header */
663 {
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;
670
671 esis_stat.es_ishrcvd++;
672 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
673
674 #ifdef ARGO_DEBUG
675 if (argo_debug[D_ESISINPUT]) {
676 printf("esis_ishinput: ish: ht %d\n", ht);
677 }
678 #endif
679 if (ESHonly)
680 goto bad;
681
682 ESIS_EXTRACT_ADDR(nsap, buf);
683
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);
696 }
697 break;
698
699 default:
700 printf("Unknown ISH option: %x\n", *buf);
701 }
702 ESIS_NEXT_OPTION(buf);
703 }
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");
710 }
711 #endif
712
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;
719 }
720
721 /*
722 * FUNCTION: esis_rdinput
723 *
724 * PURPOSE: Process an incoming RD pdu
725 *
726 * RETURNS:
727 *
728 * SIDE EFFECTS:
729 *
730 * NOTES:
731 */
732 void
733 esis_rdinput(
734 struct mbuf *m0, /* esh pdu */
735 struct snpa_hdr *shp) /* subnetwork header */
736 {
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;
743
744 esis_stat.es_rdrcvd++;
745
746 /* intermediate systems ignore redirects */
747 if (iso_systype & SNPA_IS)
748 return;
749 if (ESHonly)
750 return;
751
752 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
753 if (buf >= buflim)
754 return;
755
756 /* Extract DA */
757 ESIS_EXTRACT_ADDR(da, buf);
758
759 /* Extract better snpa */
760 ESIS_EXTRACT_ADDR(bsnpa, buf);
761
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);
768 }
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;
777
778 case ESISOVAL_NETMASK:
779 if (netmask) /* duplicate */
780 return;
781 netmask = (struct iso_addr *) (buf + 1);
782 break;
783
784 default:
785 printf("Unknown option in ESIS RD (0x%x)\n", buf[-1]);
786 }
787 ESIS_NEXT_OPTION(buf);
788 }
789
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));
796 }
797 #endif
798 /*
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).
803 *
804 * If the redirect is to an IS, add a route entry towards that
805 * IS.
806 */
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);
815 }
816 bad: ; /* Needed by ESIS_NEXT_OPTION */
817 }
818
819 /*
820 * FUNCTION: esis_config
821 *
822 * PURPOSE: Report configuration
823 *
824 * RETURNS:
825 *
826 * SIDE EFFECTS:
827 *
828 * NOTES: Called every esis_config_time seconds
829 */
830 /*ARGSUSED*/
831 void
832 esis_config(void *v)
833 {
834 struct ifnet *ifp;
835
836 callout_reset(&esis_config_ch, hz * esis_config_time,
837 esis_config, NULL);
838
839 /*
840 * Report configuration for each interface that - is UP - has
841 * BROADCAST capability - has an ISO address
842 */
843 /*
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
848 */
849
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;
855
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;
864 }
865 }
866 }
867 }
868 }
869
870 /*
871 * FUNCTION: esis_shoutput
872 *
873 * PURPOSE: Transmit an esh or ish pdu
874 *
875 * RETURNS: nothing
876 *
877 * SIDE EFFECTS:
878 *
879 * NOTES:
880 */
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)
889 {
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;
897
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;
905 }
906
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");
918 }
919 #endif
920
921 if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
922 esis_stat.es_nomem++;
923 return;
924 }
925 bzero(mtod(m, void *), MHLEN);
926
927 pdu = mtod(m, struct esis_fixed *);
928 naddrp = cp = (char *) (pdu + 1);
929 len = sizeof(struct esis_fixed);
930
931 /*
932 * Build fixed part of header
933 */
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);
938
939 if (type == ESIS_ESH) {
940 cp++;
941 len++;
942 }
943 m->m_len = len;
944 if (isoa) {
945 /*
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.
953 */
954 (void) esis_insert_addr((void **)&cp, &len, isoa, m, 0);
955 naddr = 1;
956 }
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;
961
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 */
975 }
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));
980 }
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);
988 }
989 naddr++;
990 }
991
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);
999 }
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);
1009 }
1010 #endif
1011 }
1012
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);
1016
1017 bzero((void *) & siso, sizeof(siso));
1018 siso.siso_family = AF_ISO;
1019 siso.siso_data[0] = AFI_SNA;
1020 siso.siso_nlen = sn_len + 1;
1021 bcopy(sn_addr, siso.siso_data + 1, (unsigned) sn_len);
1022 (ifp->if_output) (ifp, m0, sisotosa(&siso), 0);
1023 }
1024
1025 /*
1026 * FUNCTION: isis_input
1027 *
1028 * PURPOSE: Process an incoming isis packet
1029 *
1030 * RETURNS: nothing
1031 *
1032 * SIDE EFFECTS:
1033 *
1034 * NOTES:
1035 */
1036 void
1037 isis_input(struct mbuf *m0, ...)
1038 {
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;
1044
1045 va_start(ap, m0);
1046 shp = va_arg(ap, struct snpa_hdr *);
1047 va_end(ap);
1048 ifp = shp->snh_ifp;
1049
1050 #ifdef ARGO_DEBUG
1051 if (argo_debug[D_ISISINPUT]) {
1052 int i;
1053
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");
1064 }
1065 #endif
1066 esis_dl.sdl_alen = ifp->if_addrlen;
1067 esis_dl.sdl_index = ifp->if_index;
1068 bcopy(shp->snh_shost, (void *) esis_dl.sdl_data, 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;
1073 }
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);
1085 }
1086 #endif
1087 m_freem(mm);
1088 }
1089 }
1090 }
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;
1095 }
1096 m_freem(m0);
1097 }
1098
1099 int
1100 isis_output(struct mbuf *m, ...)
1101 {
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;
1109
1110 va_start(ap, m);
1111 sdl = va_arg(ap, struct sockaddr_dl *);
1112 va_end(ap);
1113
1114 /* we assume here we have a sockaddr_dl ... check it */
1115 if (sdl->sdl_family != AF_LINK) {
1116 error = EINVAL;
1117 goto release;
1118 }
1119 if (sdl->sdl_len < 8 + sdl->sdl_nlen + sdl->sdl_alen + sdl->sdl_slen) {
1120 error = EINVAL;
1121 goto release;
1122 }
1123
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");
1129 }
1130 #endif
1131 error = EINVAL;
1132 goto release;
1133 }
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) ? ':' : ' ');
1145 }
1146 printf("\n");
1147 }
1148 #endif
1149 bzero((void *) & siso, 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 bcopy(CLLADDR(sdl), siso.siso_data + 1, sn_len);
1158 }
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);
1165 }
1166 #endif
1167 }
1168 return (error);
1169
1170 release:
1171 if (m != NULL)
1172 m_freem(m);
1173 return (error);
1174 }
1175
1176
1177 /*
1178 * FUNCTION: esis_ctlinput
1179 *
1180 * PURPOSE: Handle the PRC_IFDOWN transition
1181 *
1182 * RETURNS: nothing
1183 *
1184 * SIDE EFFECTS:
1185 *
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...
1189 */
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)
1195 {
1196 struct iso_ifaddr *ia; /* scan through interface addresses */
1197
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);
1206 }
1207 return NULL;
1208 }
1209
1210 #endif /* ISO */
Cache object: e02a20941e610b2b32f4cc2253676818
|