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