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