FreeBSD/Linux Kernel Cross Reference
sys/netiso/esis.c
1 /* $NetBSD: esis.c,v 1.42 2006/11/24 12:47:43 is 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.42 2006/11/24 12:47:43 is 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), caddr_t);\
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);
149 callout_init(&esis_config_ch);
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 if (rp != 0) {
196 error = EISCONN;
197 break;
198 }
199
200 if (l == NULL) {
201 error = EACCES;
202 break;
203 }
204
205 /* XXX: raw socket permission is checked in socreate() */
206
207 if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
208 error = soreserve(so, esis_sendspace, esis_recvspace);
209 if (error)
210 break;
211 }
212 MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK|M_ZERO);
213 if (rp == 0) {
214 error = ENOBUFS;
215 break;
216 }
217 rp->rcb_socket = so;
218 LIST_INSERT_HEAD(&esis_pcb, rp, rcb_list);
219 so->so_pcb = rp;
220 break;
221
222 case PRU_SEND:
223 if (control && control->m_len) {
224 m_freem(control);
225 m_freem(m);
226 error = EINVAL;
227 break;
228 }
229 if (nam == NULL) {
230 m_freem(m);
231 error = EINVAL;
232 break;
233 }
234 /* error checking here */
235 error = isis_output(m, mtod(nam, struct sockaddr_dl *));
236 break;
237
238 case PRU_SENDOOB:
239 m_freem(control);
240 m_freem(m);
241 error = EOPNOTSUPP;
242 break;
243
244 case PRU_DETACH:
245 raw_detach(rp);
246 break;
247
248 case PRU_SHUTDOWN:
249 socantsendmore(so);
250 break;
251
252 case PRU_SENSE:
253 /*
254 * stat: don't bother with a blocksize.
255 */
256 return (0);
257
258 default:
259 error = EOPNOTSUPP;
260 break;
261 }
262
263 release:
264 return (error);
265 }
266
267 /*
268 * FUNCTION: esis_input
269 *
270 * PURPOSE: Process an incoming esis packet
271 *
272 * RETURNS: nothing
273 *
274 * SIDE EFFECTS:
275 *
276 * NOTES:
277 */
278 void
279 esis_input(struct mbuf *m0, ...)
280 {
281 struct snpa_hdr *shp; /* subnetwork header */
282 struct esis_fixed *pdu = mtod(m0, struct esis_fixed *);
283 int type;
284 struct ifaddr *ifa;
285 va_list ap;
286
287 va_start(ap, m0);
288 shp = va_arg(ap, struct snpa_hdr *);
289 va_end(ap);
290
291 for (ifa = shp->snh_ifp->if_addrlist.tqh_first; ifa != 0;
292 ifa = ifa->ifa_list.tqe_next)
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 caddr_t 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 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 = &satosiso(rt_key(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, caddr_t), MHLEN);
400
401 pdu = mtod(m, struct esis_fixed *);
402 cp = (caddr_t) (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(&cp, &len, rd_dstnsap, m, 0);
416
417 /* Insert the snpa of better next hop */
418 *cp++ = sdl->sdl_alen;
419 bcopy(LLADDR(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(&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 bcopy(mtod(inbound_m, caddr_t) +
477 inbound_oidx->cni_qos_formatp - 2,
478 cp, (unsigned) (inbound_oidx->cni_qos_len + 2));
479 cp += inbound_oidx->cni_qos_len + 2;
480 }
481 if (inbound_oidx->cni_priorp) {
482 bcopy(mtod(inbound_m, caddr_t) +
483 inbound_oidx->cni_priorp - 2, cp, 3);
484 cp += 3;
485 }
486 if (inbound_oidx->cni_securep) {
487 bcopy(mtod(inbound_m, caddr_t) +
488 inbound_oidx->cni_securep - 2, cp,
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((caddr_t) & 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 caddr_t *buf, /* ptr to buffer to put address into */
521 int *len, /* ptr to length of buffer so far */
522 struct iso_addr *isoa, /* ptr to address */
523 struct mbuf *m, /* determine if there remains space */
524 int nsellen)
525 {
526 int newlen, result = 0;
527
528 isoa->isoa_len -= nsellen;
529 newlen = isoa->isoa_len + 1;
530 if (newlen <= M_TRAILINGSPACE(m)) {
531 bcopy((caddr_t) isoa, *buf, newlen);
532 *len += newlen;
533 *buf += newlen;
534 m->m_len += newlen;
535 result = 1;
536 }
537 isoa->isoa_len += nsellen;
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 for (ifa = shp->snh_ifp->if_addrlist.tqh_first; ifa != 0;
590 ifa = ifa->ifa_list.tqe_next)
591 if (ifa->ifa_addr->sa_family == AF_ISO) {
592 nsellength =
593 ((struct iso_ifaddr *) ifa)->ia_addr.siso_tlen;
594 break;
595 }
596 #ifdef ARGO_DEBUG
597 if (argo_debug[D_ESISINPUT]) {
598 printf(
599 "esis_eshinput: esh: ht %d, naddr %d nsellength %d\n",
600 ht, naddr, nsellength);
601 }
602 #endif
603 while (naddr-- > 0) {
604 struct iso_addr *nsap2;
605 u_char *buf2;
606 ESIS_EXTRACT_ADDR(nsap, buf);
607 /*
608 * see if there is at least one more nsap in ESH
609 * differing only by nsel
610 */
611 if (nsellength != 0)
612 for (buf2 = buf; buf2 < buflim;) {
613 ESIS_EXTRACT_ADDR(nsap2, buf2);
614 #ifdef ARGO_DEBUG
615 if (argo_debug[D_ESISINPUT]) {
616 printf(
617 "esis_eshinput: comparing %s ",
618 clnp_iso_addrp(nsap));
619 printf("and %s\n",
620 clnp_iso_addrp(nsap2));
621 }
622 #endif
623 if (Bcmp(nsap->isoa_genaddr,
624 nsap2->isoa_genaddr,
625 nsap->isoa_len - nsellength)
626 == 0) {
627 nlen = nsellength;
628 break;
629 }
630 }
631 new_entry |= snpac_add(shp->snh_ifp,
632 nsap, shp->snh_shost, SNPA_ES, ht, nlen);
633 nlen = 0;
634 }
635 }
636 #ifdef ARGO_DEBUG
637 if (argo_debug[D_ESISINPUT]) {
638 printf("esis_eshinput: nsap %s is %s\n",
639 clnp_iso_addrp(nsap), new_entry ? "new" : "old");
640 }
641 #endif
642 if (new_entry && (iso_systype & SNPA_IS))
643 esis_shoutput(shp->snh_ifp, ESIS_ISH, esis_holding_time,
644 shp->snh_shost, 6, (struct iso_addr *) 0);
645 bad:
646 return;
647 }
648
649 /*
650 * FUNCTION: esis_ishinput
651 *
652 * PURPOSE: process an incoming ISH pdu
653 *
654 * RETURNS:
655 *
656 * SIDE EFFECTS:
657 *
658 * NOTES:
659 */
660 void
661 esis_ishinput(
662 struct mbuf *m, /* esh pdu */
663 struct snpa_hdr *shp) /* subnetwork header */
664 {
665 struct esis_fixed *pdu = mtod(m, struct esis_fixed *);
666 u_short ht, newct; /* holding time */
667 struct iso_addr *nsap; /* Network Entity Title */
668 u_char *buf = (u_char *) (pdu + 1);
669 u_char *buflim = pdu->esis_hdr_len + (u_char *) pdu;
670 int new_entry;
671
672 esis_stat.es_ishrcvd++;
673 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
674
675 #ifdef ARGO_DEBUG
676 if (argo_debug[D_ESISINPUT]) {
677 printf("esis_ishinput: ish: ht %d\n", ht);
678 }
679 #endif
680 if (ESHonly)
681 goto bad;
682
683 ESIS_EXTRACT_ADDR(nsap, buf);
684
685 while (buf < buflim) {
686 switch (*buf) {
687 case ESISOVAL_ESCT:
688 if (iso_systype & SNPA_IS)
689 break;
690 if (buf[1] != 2)
691 goto bad;
692 CTOH(buf[2], buf[3], newct);
693 if ((u_short) esis_config_time != newct) {
694 callout_stop(&esis_config_ch);
695 esis_config_time = newct;
696 esis_config(NULL);
697 }
698 break;
699
700 default:
701 printf("Unknown ISH option: %x\n", *buf);
702 }
703 ESIS_NEXT_OPTION(buf);
704 }
705 new_entry = snpac_add(shp->snh_ifp, nsap, shp->snh_shost, SNPA_IS,
706 ht, 0);
707 #ifdef ARGO_DEBUG
708 if (argo_debug[D_ESISINPUT]) {
709 printf("esis_ishinput: nsap %s is %s\n",
710 clnp_iso_addrp(nsap), new_entry ? "new" : "old");
711 }
712 #endif
713
714 if (new_entry)
715 esis_shoutput(shp->snh_ifp,
716 iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
717 esis_holding_time, shp->snh_shost, 6, (struct iso_addr *) 0);
718 bad:
719 return;
720 }
721
722 /*
723 * FUNCTION: esis_rdinput
724 *
725 * PURPOSE: Process an incoming RD pdu
726 *
727 * RETURNS:
728 *
729 * SIDE EFFECTS:
730 *
731 * NOTES:
732 */
733 void
734 esis_rdinput(
735 struct mbuf *m0, /* esh pdu */
736 struct snpa_hdr *shp) /* subnetwork header */
737 {
738 struct esis_fixed *pdu = mtod(m0, struct esis_fixed *);
739 u_short ht; /* holding time */
740 struct iso_addr *da, *net = 0, *netmask = 0, *snpamask = 0;
741 struct iso_addr *bsnpa;
742 u_char *buf = (u_char *) (pdu + 1);
743 u_char *buflim = pdu->esis_hdr_len + (u_char *) pdu;
744
745 esis_stat.es_rdrcvd++;
746
747 /* intermediate systems ignore redirects */
748 if (iso_systype & SNPA_IS)
749 return;
750 if (ESHonly)
751 return;
752
753 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
754 if (buf >= buflim)
755 return;
756
757 /* Extract DA */
758 ESIS_EXTRACT_ADDR(da, buf);
759
760 /* Extract better snpa */
761 ESIS_EXTRACT_ADDR(bsnpa, buf);
762
763 /* Extract NET if present */
764 if (buf < buflim) {
765 if (*buf == 0)
766 buf++; /* no NET present, skip NETL anyway */
767 else
768 ESIS_EXTRACT_ADDR(net, buf);
769 }
770 /* process options */
771 while (buf < buflim) {
772 switch (*buf) {
773 case ESISOVAL_SNPAMASK:
774 if (snpamask) /* duplicate */
775 return;
776 snpamask = (struct iso_addr *) (buf + 1);
777 break;
778
779 case ESISOVAL_NETMASK:
780 if (netmask) /* duplicate */
781 return;
782 netmask = (struct iso_addr *) (buf + 1);
783 break;
784
785 default:
786 printf("Unknown option in ESIS RD (0x%x)\n", buf[-1]);
787 }
788 ESIS_NEXT_OPTION(buf);
789 }
790
791 #ifdef ARGO_DEBUG
792 if (argo_debug[D_ESISINPUT]) {
793 printf("esis_rdinput: rd: ht %d, da %s\n", ht,
794 clnp_iso_addrp(da));
795 if (net)
796 printf("\t: net %s\n", clnp_iso_addrp(net));
797 }
798 #endif
799 /*
800 * If netl is zero, then redirect is to an ES. We need to add an entry
801 * to the snpa cache for (destination, better snpa).
802 * If netl is not zero, then the redirect is to an IS. In this
803 * case, add an snpa cache entry for (net, better snpa).
804 *
805 * If the redirect is to an IS, add a route entry towards that
806 * IS.
807 */
808 if (net == 0 || net->isoa_len == 0 || snpamask) {
809 /* redirect to an ES */
810 snpac_add(shp->snh_ifp, da,
811 bsnpa->isoa_genaddr, SNPA_ES, ht, 0);
812 } else {
813 snpac_add(shp->snh_ifp, net,
814 bsnpa->isoa_genaddr, SNPA_IS, ht, 0);
815 snpac_addrt(shp->snh_ifp, da, net, netmask);
816 }
817 bad: ; /* Needed by ESIS_NEXT_OPTION */
818 }
819
820 /*
821 * FUNCTION: esis_config
822 *
823 * PURPOSE: Report configuration
824 *
825 * RETURNS:
826 *
827 * SIDE EFFECTS:
828 *
829 * NOTES: Called every esis_config_time seconds
830 */
831 /*ARGSUSED*/
832 void
833 esis_config(void *v)
834 {
835 struct ifnet *ifp;
836
837 callout_reset(&esis_config_ch, hz * esis_config_time,
838 esis_config, NULL);
839
840 /*
841 * Report configuration for each interface that - is UP - has
842 * BROADCAST capability - has an ISO address
843 */
844 /*
845 * Todo: a better way would be to construct the esh or ish once and
846 * copy it out for all devices, possibly calling a method in the
847 * iso_ifaddr structure to encapsulate and transmit it. This could
848 * work to advantage for non-broadcast media
849 */
850
851 for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next) {
852 if ((ifp->if_flags & IFF_UP) &&
853 (ifp->if_flags & IFF_BROADCAST)) {
854 /* search for an ISO address family */
855 struct ifaddr *ifa;
856
857 for (ifa = ifp->if_addrlist.tqh_first; ifa != 0;
858 ifa = ifa->ifa_list.tqe_next) {
859 if (ifa->ifa_addr->sa_family == AF_ISO) {
860 esis_shoutput(ifp,
861 iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
862 esis_holding_time,
863 (iso_systype & SNPA_ES ? all_is_snpa :
864 all_es_snpa), 6, (struct iso_addr *) 0);
865 break;
866 }
867 }
868 }
869 }
870 }
871
872 /*
873 * FUNCTION: esis_shoutput
874 *
875 * PURPOSE: Transmit an esh or ish pdu
876 *
877 * RETURNS: nothing
878 *
879 * SIDE EFFECTS:
880 *
881 * NOTES:
882 */
883 void
884 esis_shoutput(
885 struct ifnet *ifp,
886 int type,
887 int ht,
888 const void *sn_addr,
889 int sn_len,
890 struct iso_addr *isoa)
891 {
892 struct mbuf *m, *m0;
893 caddr_t cp, naddrp;
894 int naddr = 0;
895 struct esis_fixed *pdu;
896 struct iso_ifaddr *ia;
897 int len;
898 struct sockaddr_iso siso;
899
900 if (type == ESIS_ESH)
901 esis_stat.es_eshsent++;
902 else if (type == ESIS_ISH)
903 esis_stat.es_ishsent++;
904 else {
905 printf("esis_shoutput: bad pdu type\n");
906 return;
907 }
908
909 #ifdef ARGO_DEBUG
910 if (argo_debug[D_ESISOUTPUT]) {
911 int i;
912 printf("esis_shoutput: ifp %p (%s), %s, ht %d, to: [%d] ",
913 ifp, ifp->if_xname,
914 type == ESIS_ESH ? "esh" : "ish",
915 ht, sn_len);
916 for (i = 0; i < sn_len; i++)
917 printf("%x%c", *((const char *)sn_addr + i),
918 i < (sn_len - 1) ? ':' : ' ');
919 printf("\n");
920 }
921 #endif
922
923 if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
924 esis_stat.es_nomem++;
925 return;
926 }
927 bzero(mtod(m, caddr_t), MHLEN);
928
929 pdu = mtod(m, struct esis_fixed *);
930 naddrp = cp = (caddr_t) (pdu + 1);
931 len = sizeof(struct esis_fixed);
932
933 /*
934 * Build fixed part of header
935 */
936 pdu->esis_proto_id = ISO9542_ESIS;
937 pdu->esis_vers = ESIS_VERSION;
938 pdu->esis_type = type;
939 HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
940
941 if (type == ESIS_ESH) {
942 cp++;
943 len++;
944 }
945 m->m_len = len;
946 if (isoa) {
947 /*
948 * Here we are responding to a clnp packet sent to an NSAP
949 * that is ours which was sent to the MAC addr all_es's.
950 * It is possible that we did not specifically advertise this
951 * NSAP, even though it is ours, so we will respond
952 * directly to the sender that we are here. If we do have
953 * multiple NSEL's we'll tack them on so he can compress
954 * them out.
955 */
956 (void) esis_insert_addr(&cp, &len, isoa, m, 0);
957 naddr = 1;
958 }
959 for (ia = iso_ifaddr.tqh_first; ia != 0; ia = ia->ia_list.tqe_next) {
960 int nsellen = (type == ESIS_ISH ? ia->ia_addr.siso_tlen : 0);
961 int n = ia->ia_addr.siso_nlen;
962 struct iso_ifaddr *ia2;
963
964 if (type == ESIS_ISH && naddr > 0)
965 break;
966 for (ia2 = iso_ifaddr.tqh_first; ia2 != ia;
967 ia2 = ia2->ia_list.tqe_next)
968 if (Bcmp(ia->ia_addr.siso_data,
969 ia2->ia_addr.siso_data, n) == 0)
970 break;
971 if (ia2 != ia)
972 continue; /* Means we have previously copied
973 * this nsap */
974 if (isoa && Bcmp(ia->ia_addr.siso_data,
975 isoa->isoa_genaddr, n) == 0) {
976 isoa = 0;
977 continue; /* Ditto */
978 }
979 #ifdef ARGO_DEBUG
980 if (argo_debug[D_ESISOUTPUT]) {
981 printf("esis_shoutput: adding NSAP %s\n",
982 clnp_iso_addrp(&ia->ia_addr.siso_addr));
983 }
984 #endif
985 if (!esis_insert_addr(&cp, &len,
986 &ia->ia_addr.siso_addr, m, nsellen)) {
987 EXTEND_PACKET(m, m0, cp);
988 (void) esis_insert_addr(&cp, &len,
989 &ia->ia_addr.siso_addr, m,
990 nsellen);
991 }
992 naddr++;
993 }
994
995 if (type == ESIS_ESH)
996 *naddrp = naddr;
997 else {
998 /* add suggested es config timer option to ISH */
999 if (M_TRAILINGSPACE(m) < 4) {
1000 printf("esis_shoutput: extending packet\n");
1001 EXTEND_PACKET(m, m0, cp);
1002 }
1003 *cp++ = ESISOVAL_ESCT;
1004 *cp++ = 2;
1005 HTOC(*cp, *(cp + 1), esis_esconfig_time);
1006 len += 4;
1007 m->m_len += 4;
1008 #ifdef ARGO_DEBUG
1009 if (argo_debug[D_ESISOUTPUT]) {
1010 printf("m0 %p, m %p, data %p, len %d, cp %p\n",
1011 m0, m, m->m_data, m->m_len, cp);
1012 }
1013 #endif
1014 }
1015
1016 m0->m_pkthdr.len = len;
1017 pdu->esis_hdr_len = len;
1018 iso_gen_csum(m0, ESIS_CKSUM_OFF, (int) pdu->esis_hdr_len);
1019
1020 bzero((caddr_t) & siso, sizeof(siso));
1021 siso.siso_family = AF_ISO;
1022 siso.siso_data[0] = AFI_SNA;
1023 siso.siso_nlen = sn_len + 1;
1024 bcopy(sn_addr, siso.siso_data + 1, (unsigned) sn_len);
1025 (ifp->if_output) (ifp, m0, sisotosa(&siso), 0);
1026 }
1027
1028 /*
1029 * FUNCTION: isis_input
1030 *
1031 * PURPOSE: Process an incoming isis packet
1032 *
1033 * RETURNS: nothing
1034 *
1035 * SIDE EFFECTS:
1036 *
1037 * NOTES:
1038 */
1039 void
1040 isis_input(struct mbuf *m0, ...)
1041 {
1042 struct snpa_hdr *shp; /* subnetwork header */
1043 struct rawcb *rp, *first_rp = 0;
1044 struct ifnet *ifp;
1045 struct mbuf *mm;
1046 va_list ap;
1047
1048 va_start(ap, m0);
1049 shp = va_arg(ap, struct snpa_hdr *);
1050 va_end(ap);
1051 ifp = shp->snh_ifp;
1052
1053 #ifdef ARGO_DEBUG
1054 if (argo_debug[D_ISISINPUT]) {
1055 int i;
1056
1057 printf("isis_input: pkt on ifp %p (%s): from:",
1058 ifp, ifp->if_xname);
1059 for (i = 0; i < 6; i++)
1060 printf("%x%c", shp->snh_shost[i] & 0xff,
1061 (i < 5) ? ':' : ' ');
1062 printf(" to:");
1063 for (i = 0; i < 6; i++)
1064 printf("%x%c", shp->snh_dhost[i] & 0xff,
1065 (i < 5) ? ':' : ' ');
1066 printf("\n");
1067 }
1068 #endif
1069 esis_dl.sdl_alen = ifp->if_addrlen;
1070 esis_dl.sdl_index = ifp->if_index;
1071 bcopy(shp->snh_shost, (caddr_t) esis_dl.sdl_data, esis_dl.sdl_alen);
1072 for (rp = esis_pcb.lh_first; rp != 0; rp = rp->rcb_list.le_next) {
1073 if (first_rp == 0) {
1074 first_rp = rp;
1075 continue;
1076 }
1077 /* can't block at interrupt level */
1078 if ((mm = m_copy(m0, 0, M_COPYALL)) != NULL) {
1079 if (sbappendaddr(&rp->rcb_socket->so_rcv,
1080 (struct sockaddr *) &esis_dl, mm,
1081 (struct mbuf *) 0) != 0) {
1082 sorwakeup(rp->rcb_socket);
1083 } else {
1084 #ifdef ARGO_DEBUG
1085 if (argo_debug[D_ISISINPUT]) {
1086 printf(
1087 "Error in sbappenaddr, mm = %p\n", mm);
1088 }
1089 #endif
1090 m_freem(mm);
1091 }
1092 }
1093 }
1094 if (first_rp && sbappendaddr(&first_rp->rcb_socket->so_rcv,
1095 (struct sockaddr *) &esis_dl, m0, (struct mbuf *) 0) != 0) {
1096 sorwakeup(first_rp->rcb_socket);
1097 return;
1098 }
1099 m_freem(m0);
1100 }
1101
1102 int
1103 isis_output(struct mbuf *m, ...)
1104 {
1105 struct sockaddr_dl *sdl;
1106 struct ifnet *ifp;
1107 struct ifaddr *ifa;
1108 struct sockaddr_iso siso;
1109 int error = 0;
1110 unsigned sn_len;
1111 va_list ap;
1112
1113 va_start(ap, m);
1114 sdl = va_arg(ap, struct sockaddr_dl *);
1115 va_end(ap);
1116
1117 /* we assume here we have a sockaddr_dl ... check it */
1118 if (sdl->sdl_family != AF_LINK) {
1119 error = EINVAL;
1120 goto release;
1121 }
1122 if (sdl->sdl_len < 8 + sdl->sdl_nlen + sdl->sdl_alen + sdl->sdl_slen) {
1123 error = EINVAL;
1124 goto release;
1125 }
1126
1127 ifa = ifa_ifwithnet((struct sockaddr *) sdl); /* get ifp from sdl */
1128 if (ifa == 0) {
1129 #ifdef ARGO_DEBUG
1130 if (argo_debug[D_ISISOUTPUT]) {
1131 printf("isis_output: interface not found\n");
1132 }
1133 #endif
1134 error = EINVAL;
1135 goto release;
1136 }
1137 ifp = ifa->ifa_ifp;
1138 sn_len = sdl->sdl_alen;
1139 #ifdef ARGO_DEBUG
1140 if (argo_debug[D_ISISOUTPUT]) {
1141 u_char *cp = (u_char *) LLADDR(sdl), *cplim = cp + sn_len;
1142 printf("isis_output: ifp %p (%s), to: ",
1143 ifp, ifp->if_xname);
1144 while (cp < cplim) {
1145 printf("%x", *cp++);
1146 printf("%c", (cp < cplim) ? ':' : ' ');
1147 }
1148 printf("\n");
1149 }
1150 #endif
1151 bzero((caddr_t) & siso, sizeof(siso));
1152 siso.siso_family = AF_ISO; /* This convention may be useful for
1153 * X.25 */
1154 if (sn_len == 0)
1155 siso.siso_nlen = 0;
1156 else {
1157 siso.siso_data[0] = AFI_SNA;
1158 siso.siso_nlen = sn_len + 1;
1159 bcopy(LLADDR(sdl), siso.siso_data + 1, sn_len);
1160 }
1161 error = (ifp->if_output) (ifp, m, sisotosa(&siso), 0);
1162 if (error) {
1163 #ifdef ARGO_DEBUG
1164 if (argo_debug[D_ISISOUTPUT]) {
1165 printf("isis_output: error from if_output is %d\n",
1166 error);
1167 }
1168 #endif
1169 }
1170 return (error);
1171
1172 release:
1173 if (m != NULL)
1174 m_freem(m);
1175 return (error);
1176 }
1177
1178
1179 /*
1180 * FUNCTION: esis_ctlinput
1181 *
1182 * PURPOSE: Handle the PRC_IFDOWN transition
1183 *
1184 * RETURNS: nothing
1185 *
1186 * SIDE EFFECTS:
1187 *
1188 * NOTES: Calls snpac_flush for interface specified.
1189 * The loop through iso_ifaddr is stupid because
1190 * back in if_down, we knew the ifp...
1191 */
1192 void *
1193 esis_ctlinput(
1194 int req, /* request: we handle only PRC_IFDOWN */
1195 struct sockaddr *siso, /* address of ifp */
1196 void *dummy)
1197 {
1198 struct iso_ifaddr *ia; /* scan through interface addresses */
1199
1200 /*XXX correct? */
1201 if (siso->sa_family != AF_ISO)
1202 return NULL;
1203 if (req == PRC_IFDOWN)
1204 for (ia = iso_ifaddr.tqh_first; ia != 0;
1205 ia = ia->ia_list.tqe_next) {
1206 if (iso_addrmatch(IA_SIS(ia),
1207 (struct sockaddr_iso *) siso))
1208 snpac_flushifp(ia->ia_ifp);
1209 }
1210 return NULL;
1211 }
1212
1213 #endif /* ISO */
Cache object: 809cccce7c77f6fd791c035609ead1b6
|