This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / sys / netiso / esis.c
CommitLineData
15637ed4
RG
1/*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * @(#)esis.c 7.19 (Berkeley) 6/27/91
34 */
35
36/***********************************************************
37 Copyright IBM Corporation 1987
38
39 All Rights Reserved
40
41Permission to use, copy, modify, and distribute this software and its
42documentation for any purpose and without fee is hereby granted,
43provided that the above copyright notice appear in all copies and that
44both that copyright notice and this permission notice appear in
45supporting documentation, and that the name of IBM not be
46used in advertising or publicity pertaining to distribution of the
47software without specific, written prior permission.
48
49IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
50ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
51IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
52ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
53WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
54ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
55SOFTWARE.
56
57******************************************************************/
58
59/*
60 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
61 */
62
63#ifdef ISO
64
65#include "types.h"
66#include "param.h"
67#include "systm.h"
68#include "mbuf.h"
69#include "domain.h"
70#include "protosw.h"
71#include "socket.h"
72#include "socketvar.h"
73#include "errno.h"
74
75#include "../net/if.h"
76#include "../net/if_dl.h"
77#include "../net/route.h"
78#include "../net/raw_cb.h"
79
80#include "iso.h"
81#include "iso_pcb.h"
82#include "iso_var.h"
83#include "iso_snpac.h"
84#include "clnl.h"
85#include "clnp.h"
86#include "clnp_stat.h"
87#include "esis.h"
88#include "argo_debug.h"
89#include "kernel.h"
90
91/*
92 * Global variables to esis implementation
93 *
94 * esis_holding_time - the holding time (sec) parameter for outgoing pdus
95 * esis_config_time - the frequency (sec) that hellos are generated
96 * esis_esconfig_time - suggested es configuration time placed in the
97 * ish.
98 *
99 */
100struct rawcb esis_pcb;
101int esis_config(), snpac_age();
102int esis_sendspace = 2048;
103int esis_recvspace = 2048;
104short esis_holding_time = ESIS_HT;
105short esis_config_time = ESIS_CONFIG;
106short esis_esconfig_time = ESIS_CONFIG;
107extern int iso_systype;
108struct sockaddr_dl esis_dl = { sizeof(esis_dl), AF_LINK };
109extern char all_es_snpa[], all_is_snpa[];
110
111#define EXTEND_PACKET(m, mhdr, cp)\
112 if (((m)->m_next = m_getclr(M_DONTWAIT, MT_HEADER)) == NULL) {\
113 esis_stat.es_nomem++;\
114 m_freem(mhdr);\
115 return;\
116 } else {\
117 (m) = (m)->m_next;\
118 (cp) = mtod((m), caddr_t);\
119 }
120/*
121 * FUNCTION: esis_init
122 *
123 * PURPOSE: Initialize the kernel portion of esis protocol
124 *
125 * RETURNS: nothing
126 *
127 * SIDE EFFECTS:
128 *
129 * NOTES:
130 */
131esis_init()
132{
133 extern struct clnl_protosw clnl_protox[256];
134 int esis_input(), isis_input();
135#ifdef ISO_X25ESIS
136 int x25esis_input();
137#endif ISO_X25ESIS
138
139 esis_pcb.rcb_next = esis_pcb.rcb_prev = &esis_pcb;
140 llinfo_llc.lc_next = llinfo_llc.lc_prev = &llinfo_llc;
141
142 timeout(snpac_age, (caddr_t)0, hz);
143 timeout(esis_config, (caddr_t)0, hz);
144
145 clnl_protox[ISO9542_ESIS].clnl_input = esis_input;
146 clnl_protox[ISO10589_ISIS].clnl_input = isis_input;
147#ifdef ISO_X25ESIS
148 clnl_protox[ISO9542X25_ESIS].clnl_input = x25esis_input;
149#endif ISO_X25ESIS
150}
151
152/*
153 * FUNCTION: esis_usrreq
154 *
155 * PURPOSE: Handle user level esis requests
156 *
157 * RETURNS: 0 or appropriate errno
158 *
159 * SIDE EFFECTS:
160 *
161 */
162/*ARGSUSED*/
163esis_usrreq(so, req, m, nam, control)
164struct socket *so; /* socket: used only to get to this code */
165int req; /* request */
166struct mbuf *m; /* data for request */
167struct mbuf *nam; /* optional name */
168struct mbuf *control; /* optional control */
169{
170 struct rawcb *rp = sotorawcb(so);
171 int error = 0;
172
173 if ((so->so_state & SS_PRIV) == 0) {
174 error = EACCES;
175 goto release;
176 }
177 if (rp == NULL && req != PRU_ATTACH) {
178 error = EINVAL;
179 goto release;
180 }
181
182 switch (req) {
183 case PRU_ATTACH:
184 if (rp != NULL) {
185 error = EINVAL;
186 break;
187 }
188 MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK);
189 if (so->so_pcb = (caddr_t)rp) {
190 bzero(so->so_pcb, sizeof(*rp));
191 insque(rp, &esis_pcb);
192 rp->rcb_socket = so;
193 error = soreserve(so, esis_sendspace, esis_recvspace);
194 } else
195 error = ENOBUFS;
196 break;
197
198 case PRU_SEND:
199 if (nam == NULL) {
200 error = EINVAL;
201 break;
202 }
203 /* error checking here */
204 error = isis_output(mtod(nam,struct sockaddr_dl *), m);
205 m = NULL;
206 break;
207
208 case PRU_DETACH:
209 raw_detach(rp);
210 break;
211
212 case PRU_SHUTDOWN:
213 socantsendmore(so);
214 break;
215
216 case PRU_ABORT:
217 soisdisconnected(so);
218 raw_detach(rp);
219 break;
220
221 case PRU_SENSE:
222 return (0);
223
224 default:
225 return (EOPNOTSUPP);
226 }
227release:
228 if (m != NULL)
229 m_freem(m);
230
231 return (error);
232}
233
234/*
235 * FUNCTION: esis_input
236 *
237 * PURPOSE: Process an incoming esis packet
238 *
239 * RETURNS: nothing
240 *
241 * SIDE EFFECTS:
242 *
243 * NOTES:
244 */
245esis_input(m0, shp)
246struct mbuf *m0; /* ptr to first mbuf of pkt */
247struct snpa_hdr *shp; /* subnetwork header */
248{
249 register struct esis_fixed *pdu = mtod(m0, struct esis_fixed *);
250 register int type;
251
252 /*
253 * check checksum if necessary
254 */
255 if (ESIS_CKSUM_REQUIRED(pdu) && iso_check_csum(m0, (int)pdu->esis_hdr_len)) {
256 esis_stat.es_badcsum++;
257 goto bad;
258 }
259
260 /* check version */
261 if (pdu->esis_vers != ESIS_VERSION) {
262 esis_stat.es_badvers++;
263 goto bad;
264 }
265 type = pdu->esis_type & 0x1f;
266 switch (type) {
267 case ESIS_ESH:
268 esis_eshinput(m0, shp);
269 break;
270
271 case ESIS_ISH:
272 esis_ishinput(m0, shp);
273 break;
274
275 case ESIS_RD:
276 esis_rdinput(m0, shp);
277 break;
278
279 default:
280 esis_stat.es_badtype++;
281 }
282
283bad:
284 if (esis_pcb.rcb_next != &esis_pcb)
285 isis_input(m0, shp);
286 else
287 m_freem(m0);
288}
289
290/*
291 * FUNCTION: esis_rdoutput
292 *
293 * PURPOSE: Transmit a redirect pdu
294 *
295 * RETURNS: nothing
296 *
297 * SIDE EFFECTS:
298 *
299 * NOTES: Assumes there is enough space for fixed part of header,
300 * DA, BSNPA and NET in first mbuf.
301 */
302esis_rdoutput(inbound_shp, inbound_m, inbound_oidx, rd_dstnsap, rt)
303struct snpa_hdr *inbound_shp; /* snpa hdr from incoming packet */
304struct mbuf *inbound_m; /* incoming pkt itself */
305struct clnp_optidx *inbound_oidx; /* clnp options assoc with incoming pkt */
306struct iso_addr *rd_dstnsap; /* ultimate destination of pkt */
307struct rtentry *rt; /* snpa cache info regarding next hop of
308 pkt */
309{
310 struct mbuf *m, *m0;
311 caddr_t cp;
312 struct esis_fixed *pdu;
313 int len, total_len = 0;
314 struct sockaddr_iso siso;
315 struct ifnet *ifp = inbound_shp->snh_ifp;
316 struct sockaddr_dl *sdl;
317 struct iso_addr *rd_gwnsap;
318
319 if (rt->rt_flags & RTF_GATEWAY) {
320 rd_gwnsap = &((struct sockaddr_iso *)rt->rt_gateway)->siso_addr;
321 rt = rtalloc1(rt->rt_gateway, 0);
322 } else
323 rd_gwnsap = &((struct sockaddr_iso *)rt_key(rt))->siso_addr;
324 if (rt == 0 || (sdl = (struct sockaddr_dl *)rt->rt_gateway) == 0 ||
325 sdl->sdl_family != AF_LINK) {
326 /* maybe we should have a function that you
327 could put in the iso_ifaddr structure
328 which could translate iso_addrs into snpa's
329 where there is a known mapping for that address type */
330 esis_stat.es_badtype++;
331 return;
332 }
333 esis_stat.es_rdsent++;
334 IFDEBUG(D_ESISOUTPUT)
335 printf("esis_rdoutput: ifp x%x (%s%d), ht %d, m x%x, oidx x%x\n",
336 ifp, ifp->if_name, ifp->if_unit, esis_holding_time, inbound_m,
337 inbound_oidx);
338 printf("\tdestination: %s\n", clnp_iso_addrp(rd_dstnsap));
339 printf("\tredirected toward:%s\n", clnp_iso_addrp(rd_gwnsap));
340 ENDDEBUG
341
342 if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
343 esis_stat.es_nomem++;
344 return;
345 }
346 bzero(mtod(m, caddr_t), MHLEN);
347
348 pdu = mtod(m, struct esis_fixed *);
349 cp = (caddr_t)(pdu + 1); /*pointer arith.; 1st byte after header */
350 len = sizeof(struct esis_fixed);
351
352 /*
353 * Build fixed part of header
354 */
355 pdu->esis_proto_id = ISO9542_ESIS;
356 pdu->esis_vers = ESIS_VERSION;
357 pdu->esis_type = ESIS_RD;
358 HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, esis_holding_time);
359
360 /* Insert destination address */
361 (void) esis_insert_addr(&cp, &len, rd_dstnsap, m, 0);
362
363 /* Insert the snpa of better next hop */
364 *cp++ = sdl->sdl_alen;
365 bcopy(LLADDR(sdl), cp, sdl->sdl_alen);
366 cp += sdl->sdl_alen;
367 len += (sdl->sdl_alen + 1);
368
369 /*
370 * If the next hop is not the destination, then it ought to be
371 * an IS and it should be inserted next. Else, set the
372 * NETL to 0
373 */
374 /* PHASE2 use mask from ifp of outgoing interface */
375 if (!iso_addrmatch1(rd_dstnsap, rd_gwnsap)) {
376 /* this should not happen:
377 if ((nhop_sc->sc_flags & SNPA_IS) == 0) {
378 printf("esis_rdoutput: next hop is not dst and not an IS\n");
379 m_freem(m0);
380 return;
381 } */
382 (void) esis_insert_addr(&cp, &len, rd_gwnsap, m, 0);
383 } else {
384 *cp++ = 0; /* NETL */
385 len++;
386 }
387 m->m_len = len;
388
389 /*
390 * PHASE2
391 * If redirect is to an IS, add an address mask. The mask to be
392 * used should be the mask present in the routing entry used to
393 * forward the original data packet.
394 */
395
396 /*
397 * Copy Qos, priority, or security options present in original npdu
398 */
399 if (inbound_oidx) {
400 /* THIS CODE IS CURRENTLY (mostly) UNTESTED */
401 int optlen = 0;
402 if (inbound_oidx->cni_qos_formatp)
403 optlen += (inbound_oidx->cni_qos_len + 2);
404 if (inbound_oidx->cni_priorp) /* priority option is 1 byte long */
405 optlen += 3;
406 if (inbound_oidx->cni_securep)
407 optlen += (inbound_oidx->cni_secure_len + 2);
408 if (M_TRAILINGSPACE(m) < optlen) {
409 EXTEND_PACKET(m, m0, cp);
410 m->m_len = 0;
411 /* assumes MLEN > optlen */
412 }
413 /* assume MLEN-len > optlen */
414 /*
415 * When copying options, copy from ptr - 2 in order to grab
416 * the option code and length
417 */
418 if (inbound_oidx->cni_qos_formatp) {
419 bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_qos_formatp - 2,
420 cp, (unsigned)(inbound_oidx->cni_qos_len + 2));
421 cp += inbound_oidx->cni_qos_len + 2;
422 }
423 if (inbound_oidx->cni_priorp) {
424 bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_priorp - 2,
425 cp, 3);
426 cp += 3;
427 }
428 if (inbound_oidx->cni_securep) {
429 bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_securep - 2, cp,
430 (unsigned)(inbound_oidx->cni_secure_len + 2));
431 cp += inbound_oidx->cni_secure_len + 2;
432 }
433 m->m_len += optlen;
434 len += optlen;
435 }
436
437 pdu->esis_hdr_len = m0->m_pkthdr.len = len;
438 iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len);
439
440 bzero((caddr_t)&siso, sizeof(siso));
441 siso.siso_family = AF_ISO;
442 siso.siso_data[0] = AFI_SNA;
443 siso.siso_nlen = 6 + 1; /* should be taken from snpa_hdr */
444 /* +1 is for AFI */
445 bcopy(inbound_shp->snh_shost, siso.siso_data + 1, 6);
446 (ifp->if_output)(ifp, m0, &siso, 0);
447}
448
449/*
450 * FUNCTION: esis_insert_addr
451 *
452 * PURPOSE: Insert an iso_addr into a buffer
453 *
454 * RETURNS: true if buffer was big enough, else false
455 *
456 * SIDE EFFECTS: Increment buf & len according to size of iso_addr
457 *
458 * NOTES: Plus 1 here is for length byte
459 */
460esis_insert_addr(buf, len, isoa, m, nsellen)
461register caddr_t *buf; /* ptr to buffer to put address into */
462int *len; /* ptr to length of buffer so far */
463register struct iso_addr *isoa; /* ptr to address */
464register struct mbuf *m; /* determine if there remains space */
465int nsellen;
466{
467 register int newlen, result = 0;
468
469 isoa->isoa_len -= nsellen;
470 newlen = isoa->isoa_len + 1;
471 if (newlen <= M_TRAILINGSPACE(m)) {
472 bcopy((caddr_t)isoa, *buf, newlen);
473 *len += newlen;
474 *buf += newlen;
475 m->m_len += newlen;
476 result = 1;
477 }
478 isoa->isoa_len += nsellen;
479 return (result);
480}
481
482#define ESIS_EXTRACT_ADDR(d, b) { d = (struct iso_addr *)(b); b += (1 + *b); \
483 if (b > buflim) {esis_stat.es_toosmall++; goto bad;}}
484#define ESIS_NEXT_OPTION(b) { b += (2 + b[1]); \
485 if (b > buflim) {esis_stat.es_toosmall++; goto bad;}}
486int ESHonly = 0;
487/*
488
489/*
490 * FUNCTION: esis_eshinput
491 *
492 * PURPOSE: Process an incoming ESH pdu
493 *
494 * RETURNS: nothing
495 *
496 * SIDE EFFECTS:
497 *
498 * NOTES:
499 */
500esis_eshinput(m, shp)
501struct mbuf *m; /* esh pdu */
502struct snpa_hdr *shp; /* subnetwork header */
503{
504 struct esis_fixed *pdu = mtod(m, struct esis_fixed *);
505 u_short ht; /* holding time */
506 struct iso_addr *nsap;
507 int naddr;
508 u_char *buf = (u_char *)(pdu + 1);
509 u_char *buflim = pdu->esis_hdr_len + (u_char *)pdu;
510 int new_entry = 0;
511
512 esis_stat.es_eshrcvd++;
513
514 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
515
516 naddr = *buf++;
517 if (buf >= buflim)
518 goto bad;
519 if (naddr == 1) {
520 ESIS_EXTRACT_ADDR(nsap, buf);
521 new_entry = snpac_add(shp->snh_ifp,
522 nsap, shp->snh_shost, SNPA_ES, ht, 0);
523 } else {
524 int nsellength = 0, nlen = 0;
525 {
526 /* See if we want to compress out multiple nsaps differing
527 only by nsel */
528 register struct ifaddr *ifa = shp->snh_ifp->if_addrlist;
529 for (; ifa; ifa = ifa->ifa_next)
530 if (ifa->ifa_addr->sa_family == AF_ISO) {
531 nsellength = ((struct iso_ifaddr *)ifa)->ia_addr.siso_tlen;
532 break;
533 }
534 }
535 IFDEBUG(D_ESISINPUT)
536 printf("esis_eshinput: esh: ht %d, naddr %d nsellength %d\n",
537 ht, naddr, nsellength);
538 ENDDEBUG
539 while (naddr-- > 0) {
540 struct iso_addr *nsap2; u_char *buf2;
541 ESIS_EXTRACT_ADDR(nsap, buf);
542 /* see if there is at least one more nsap in ESH differing
543 only by nsel */
544 if (nsellength != 0) for (buf2 = buf; buf2 < buflim;) {
545 ESIS_EXTRACT_ADDR(nsap2, buf2);
546 IFDEBUG(D_ESISINPUT)
547 printf("esis_eshinput: comparing %s ",
548 clnp_iso_addrp(nsap));
549 printf("and %s\n", clnp_iso_addrp(nsap2));
550 ENDDEBUG
551 if (Bcmp(nsap->isoa_genaddr, nsap2->isoa_genaddr,
552 nsap->isoa_len - nsellength) == 0) {
553 nlen = nsellength;
554 break;
555 }
556 }
557 new_entry |= snpac_add(shp->snh_ifp,
558 nsap, shp->snh_shost, SNPA_ES, ht, nlen);
559 nlen = 0;
560 }
561 }
562 IFDEBUG(D_ESISINPUT)
563 printf("esis_eshinput: nsap %s is %s\n",
564 clnp_iso_addrp(nsap), new_entry ? "new" : "old");
565 ENDDEBUG
566 if (new_entry && (iso_systype & SNPA_IS))
567 esis_shoutput(shp->snh_ifp, ESIS_ISH, esis_holding_time,
568 shp->snh_shost, 6, (struct iso_addr *)0);
569bad:
570 return;
571}
572
573/*
574 * FUNCTION: esis_ishinput
575 *
576 * PURPOSE: process an incoming ISH pdu
577 *
578 * RETURNS:
579 *
580 * SIDE EFFECTS:
581 *
582 * NOTES:
583 */
584esis_ishinput(m, shp)
585struct mbuf *m; /* esh pdu */
586struct snpa_hdr *shp; /* subnetwork header */
587{
588 struct esis_fixed *pdu = mtod(m, struct esis_fixed *);
589 u_short ht, newct; /* holding time */
590 struct iso_addr *nsap; /* Network Entity Title */
591 register u_char *buf = (u_char *) (pdu + 1);
592 register u_char *buflim = pdu->esis_hdr_len + (u_char *)pdu;
593 int new_entry;
594
595 esis_stat.es_ishrcvd++;
596 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
597
598 IFDEBUG(D_ESISINPUT)
599 printf("esis_ishinput: ish: ht %d\n", ht);
600 ENDDEBUG
601 if (ESHonly)
602 goto bad;
603
604 ESIS_EXTRACT_ADDR(nsap, buf);
605
606 while (buf < buflim) {
607 switch (*buf) {
608 case ESISOVAL_ESCT:
609 if (iso_systype & SNPA_IS)
610 break;
611 if (buf[1] != 2)
612 goto bad;
613 CTOH(buf[2], buf[3], newct);
614 if (esis_config_time != newct) {
615 untimeout(esis_config,0);
616 esis_config_time = newct;
617 esis_config();
618 }
619 break;
620
621 default:
622 printf("Unknown ISH option: %x\n", *buf);
623 }
624 ESIS_NEXT_OPTION(buf);
625 }
626 new_entry = snpac_add(shp->snh_ifp, nsap, shp->snh_shost, SNPA_IS, ht, 0);
627 IFDEBUG(D_ESISINPUT)
628 printf("esis_ishinput: nsap %s is %s\n",
629 clnp_iso_addrp(nsap), new_entry ? "new" : "old");
630 ENDDEBUG
631
632 if (new_entry)
633 esis_shoutput(shp->snh_ifp,
634 iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
635 esis_holding_time, shp->snh_shost, 6, (struct iso_addr *)0);
636bad:
637 return;
638}
639
640/*
641 * FUNCTION: esis_rdinput
642 *
643 * PURPOSE: Process an incoming RD pdu
644 *
645 * RETURNS:
646 *
647 * SIDE EFFECTS:
648 *
649 * NOTES:
650 */
651esis_rdinput(m0, shp)
652struct mbuf *m0; /* esh pdu */
653struct snpa_hdr *shp; /* subnetwork header */
654{
655 struct esis_fixed *pdu = mtod(m0, struct esis_fixed *);
656 u_short ht; /* holding time */
657 struct iso_addr *da, *net = 0, *netmask = 0, *snpamask = 0;
658 register struct iso_addr *bsnpa;
659 register u_char *buf = (u_char *)(pdu + 1);
660 register u_char *buflim = pdu->esis_hdr_len + (u_char *)pdu;
661
662 esis_stat.es_rdrcvd++;
663
664 /* intermediate systems ignore redirects */
665 if (iso_systype & SNPA_IS)
666 return;
667 if (ESHonly)
668 return;
669
670 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
671 if (buf >= buflim)
672 return;
673
674 /* Extract DA */
675 ESIS_EXTRACT_ADDR(da, buf);
676
677 /* Extract better snpa */
678 ESIS_EXTRACT_ADDR(bsnpa, buf);
679
680 /* Extract NET if present */
681 if (buf < buflim) {
682 if (*buf == 0)
683 buf++; /* no NET present, skip NETL anyway */
684 else
685 ESIS_EXTRACT_ADDR(net, buf);
686 }
687
688 /* process options */
689 while (buf < buflim) {
690 switch (*buf) {
691 case ESISOVAL_SNPAMASK:
692 if (snpamask) /* duplicate */
693 return;
694 snpamask = (struct iso_addr *)(buf + 1);
695 break;
696
697 case ESISOVAL_NETMASK:
698 if (netmask) /* duplicate */
699 return;
700 netmask = (struct iso_addr *)(buf + 1);
701 break;
702
703 default:
704 printf("Unknown option in ESIS RD (0x%x)\n", buf[-1]);
705 }
706 ESIS_NEXT_OPTION(buf);
707 }
708
709 IFDEBUG(D_ESISINPUT)
710 printf("esis_rdinput: rd: ht %d, da %s\n", ht, clnp_iso_addrp(da));
711 if (net)
712 printf("\t: net %s\n", clnp_iso_addrp(net));
713 ENDDEBUG
714 /*
715 * If netl is zero, then redirect is to an ES. We need to add an entry
716 * to the snpa cache for (destination, better snpa).
717 * If netl is not zero, then the redirect is to an IS. In this
718 * case, add an snpa cache entry for (net, better snpa).
719 *
720 * If the redirect is to an IS, add a route entry towards that
721 * IS.
722 */
723 if (net == 0 || net->isoa_len == 0 || snpamask) {
724 /* redirect to an ES */
725 snpac_add(shp->snh_ifp, da,
726 bsnpa->isoa_genaddr, SNPA_ES, ht, 0);
727 } else {
728 snpac_add(shp->snh_ifp, net,
729 bsnpa->isoa_genaddr, SNPA_IS, ht, 0);
730 snpac_addrt(shp->snh_ifp, da, net, netmask);
731 }
732bad: ; /* Needed by ESIS_NEXT_OPTION */
733}
734
735/*
736 * FUNCTION: esis_config
737 *
738 * PURPOSE: Report configuration
739 *
740 * RETURNS:
741 *
742 * SIDE EFFECTS:
743 *
744 * NOTES: Called every esis_config_time seconds
745 */
746esis_config()
747{
748 register struct ifnet *ifp;
749
750 timeout(esis_config, (caddr_t)0, hz * esis_config_time);
751
752 /*
753 * Report configuration for each interface that
754 * - is UP
755 * - has BROADCAST capability
756 * - has an ISO address
757 */
758 /* Todo: a better way would be to construct the esh or ish
759 * once and copy it out for all devices, possibly calling
760 * a method in the iso_ifaddr structure to encapsulate and
761 * transmit it. This could work to advantage for non-broadcast media
762 */
763
764 for (ifp = ifnet; ifp; ifp = ifp->if_next) {
765 if ((ifp->if_flags & IFF_UP) &&
766 (ifp->if_flags & IFF_BROADCAST)) {
767 /* search for an ISO address family */
768 struct ifaddr *ia;
769
770 for (ia = ifp->if_addrlist; ia; ia = ia->ifa_next) {
771 if (ia->ifa_addr->sa_family == AF_ISO) {
772 esis_shoutput(ifp,
773 iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH,
774 esis_holding_time,
775 (caddr_t)(iso_systype & SNPA_ES ? all_is_snpa :
776 all_es_snpa), 6, (struct iso_addr *)0);
777 break;
778 }
779 }
780 }
781 }
782}
783
784/*
785 * FUNCTION: esis_shoutput
786 *
787 * PURPOSE: Transmit an esh or ish pdu
788 *
789 * RETURNS: nothing
790 *
791 * SIDE EFFECTS:
792 *
793 * NOTES:
794 */
795esis_shoutput(ifp, type, ht, sn_addr, sn_len, isoa)
796struct ifnet *ifp;
797int type;
798short ht;
799caddr_t sn_addr;
800int sn_len;
801struct iso_addr *isoa;
802{
803 struct mbuf *m, *m0;
804 caddr_t cp, naddrp;
805 int naddr = 0;
806 struct esis_fixed *pdu;
807 struct iso_ifaddr *ia;
808 int len;
809 struct sockaddr_iso siso;
810
811 if (type == ESIS_ESH)
812 esis_stat.es_eshsent++;
813 else if (type == ESIS_ISH)
814 esis_stat.es_ishsent++;
815 else {
816 printf("esis_shoutput: bad pdu type\n");
817 return;
818 }
819
820 IFDEBUG(D_ESISOUTPUT)
821 int i;
822 printf("esis_shoutput: ifp x%x (%s%d), %s, ht %d, to: [%d] ",
823 ifp, ifp->if_name, ifp->if_unit, type == ESIS_ESH ? "esh" : "ish",
824 ht, sn_len);
825 for (i=0; i<sn_len; i++)
826 printf("%x%c", *(sn_addr+i), i < (sn_len-1) ? ':' : ' ');
827 printf("\n");
828 ENDDEBUG
829
830 if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) {
831 esis_stat.es_nomem++;
832 return;
833 }
834 bzero(mtod(m, caddr_t), MHLEN);
835
836 pdu = mtod(m, struct esis_fixed *);
837 naddrp = cp = (caddr_t)(pdu + 1);
838 len = sizeof(struct esis_fixed);
839
840 /*
841 * Build fixed part of header
842 */
843 pdu->esis_proto_id = ISO9542_ESIS;
844 pdu->esis_vers = ESIS_VERSION;
845 pdu->esis_type = type;
846 HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht);
847
848 if (type == ESIS_ESH) {
849 cp++;
850 len++;
851 }
852
853 m->m_len = len;
854 if (isoa) {
855 /*
856 * Here we are responding to a clnp packet sent to an NSAP
857 * that is ours which was sent to the MAC addr all_es's.
858 * It is possible that we did not specifically advertise this
859 * NSAP, even though it is ours, so we will respond
860 * directly to the sender that we are here. If we do have
861 * multiple NSEL's we'll tack them on so he can compress them out.
862 */
863 (void) esis_insert_addr(&cp, &len, isoa, m, 0);
864 naddr = 1;
865 }
866 for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
867 int nsellen = (type == ESIS_ISH ? ia->ia_addr.siso_tlen : 0);
868 int n = ia->ia_addr.siso_nlen;
869 register struct iso_ifaddr *ia2;
870
871 if (type == ESIS_ISH && naddr > 0)
872 break;
873 for (ia2 = iso_ifaddr; ia2 != ia; ia2 = ia2->ia_next)
874 if (Bcmp(ia->ia_addr.siso_data, ia2->ia_addr.siso_data, n) == 0)
875 break;
876 if (ia2 != ia)
877 continue; /* Means we have previously copied this nsap */
878 if (isoa && Bcmp(ia->ia_addr.siso_data, isoa->isoa_genaddr, n) == 0) {
879 isoa = 0;
880 continue; /* Ditto */
881 }
882 IFDEBUG(D_ESISOUTPUT)
883 printf("esis_shoutput: adding NSAP %s\n",
884 clnp_iso_addrp(&ia->ia_addr.siso_addr));
885 ENDDEBUG
886 if (!esis_insert_addr(&cp, &len,
887 &ia->ia_addr.siso_addr, m, nsellen)) {
888 EXTEND_PACKET(m, m0, cp);
889 (void) esis_insert_addr(&cp, &len, &ia->ia_addr.siso_addr, m,
890 nsellen);
891 }
892 naddr++;
893 }
894
895 if (type == ESIS_ESH)
896 *naddrp = naddr;
897 else {
898 /* add suggested es config timer option to ISH */
899 if (M_TRAILINGSPACE(m) < 4) {
900 printf("esis_shoutput: extending packet\n");
901 EXTEND_PACKET(m, m0, cp);
902 }
903 *cp++ = ESISOVAL_ESCT;
904 *cp++ = 2;
905 HTOC(*cp, *(cp+1), esis_esconfig_time);
906 len += 4;
907 m->m_len += 4;
908 IFDEBUG(D_ESISOUTPUT)
909 printf("m0 0x%x, m 0x%x, data 0x%x, len %d, cp 0x%x\n",
910 m0, m, m->m_data, m->m_len, cp);
911 ENDDEBUG
912 }
913
914 m0->m_pkthdr.len = len;
915 pdu->esis_hdr_len = len;
916 iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len);
917
918 bzero((caddr_t)&siso, sizeof(siso));
919 siso.siso_family = AF_ISO;
920 siso.siso_data[0] = AFI_SNA;
921 siso.siso_nlen = sn_len + 1;
922 bcopy(sn_addr, siso.siso_data + 1, (unsigned)sn_len);
923 (ifp->if_output)(ifp, m0, &siso, 0);
924}
925
926/*
927 * FUNCTION: isis_input
928 *
929 * PURPOSE: Process an incoming isis packet
930 *
931 * RETURNS: nothing
932 *
933 * SIDE EFFECTS:
934 *
935 * NOTES:
936 */
937isis_input(m0, shp)
938struct mbuf *m0; /* ptr to first mbuf of pkt */
939struct snpa_hdr *shp; /* subnetwork header */
940{
941 register int type;
942 register struct rawcb *rp, *first_rp = 0;
943 struct ifnet *ifp = shp->snh_ifp;
944 char workbuf[16];
945 struct mbuf *mm;
946
947 IFDEBUG(D_ISISINPUT)
948 int i;
949
950 printf("isis_input: pkt on ifp x%x (%s%d): from:", ifp,
951 ifp->if_name, ifp->if_unit);
952 for (i=0; i<6; i++)
953 printf("%x%c", shp->snh_shost[i]&0xff, (i<5) ? ':' : ' ');
954 printf(" to:");
955 for (i=0; i<6; i++)
956 printf("%x%c", shp->snh_dhost[i]&0xff, (i<5) ? ':' : ' ');
957 printf("\n");
958 ENDDEBUG
959 esis_dl.sdl_alen = ifp->if_addrlen;
960 esis_dl.sdl_index = ifp->if_index;
961 bcopy(shp->snh_shost, (caddr_t)esis_dl.sdl_data, esis_dl.sdl_alen);
962 for (rp = esis_pcb.rcb_next; rp != &esis_pcb; rp = rp->rcb_next) {
963 if (first_rp == 0) {
964 first_rp = rp;
965 continue;
966 }
967 if (mm = m_copy(m0, 0, M_COPYALL)) { /*can't block at interrupt level */
968 if (sbappendaddr(&rp->rcb_socket->so_rcv,
969 &esis_dl, mm, (struct mbuf *)0) != 0)
970 sorwakeup(rp->rcb_socket);
971 else {
972 IFDEBUG(D_ISISINPUT)
973 printf("Error in sbappenaddr, mm = 0x%x\n", mm);
974 ENDDEBUG
975 m_freem(mm);
976 }
977 }
978 }
979 if (first_rp && sbappendaddr(&first_rp->rcb_socket->so_rcv,
980 &esis_dl, m0, (struct mbuf *)0) != 0) {
981 sorwakeup(first_rp->rcb_socket);
982 return;
983 }
984 m_freem(m0);
985}
986
987isis_output(sdl, m)
988register struct sockaddr_dl *sdl;
989struct mbuf *m;
990{
991 register struct ifnet *ifp;
992 struct ifaddr *ifa, *ifa_ifwithnet();
993 struct sockaddr_iso siso;
994 int error = 0;
995 unsigned sn_len;
996
997 ifa = ifa_ifwithnet(sdl); /* extract ifp from sockaddr_dl */
998 if (ifa == 0) {
999 IFDEBUG(D_ISISOUTPUT)
1000 printf("isis_output: interface not found\n");
1001 ENDDEBUG
1002 error = EINVAL;
1003 goto release;
1004 }
1005 ifp = ifa->ifa_ifp;
1006 sn_len = sdl->sdl_alen;
1007 IFDEBUG(D_ISISOUTPUT)
1008 u_char *cp = (u_char *)LLADDR(sdl), *cplim = cp + sn_len;
1009 printf("isis_output: ifp 0x%x (%s%d), to: ",
1010 ifp, ifp->if_name, ifp->if_unit);
1011 while (cp < cplim) {
1012 printf("%x", *cp++);
1013 printf("%c", (cp < cplim) ? ':' : ' ');
1014 }
1015 printf("\n");
1016 ENDDEBUG
1017 bzero((caddr_t)&siso, sizeof(siso));
1018 siso.siso_family = AF_ISO; /* This convention may be useful for X.25 */
1019 siso.siso_data[0] = AFI_SNA;
1020 siso.siso_nlen = sn_len + 1;
1021 bcopy(LLADDR(sdl), siso.siso_data + 1, sn_len);
1022 error = (ifp->if_output)(ifp, m, (struct sockaddr *)&siso, 0);
1023 if (error) {
1024 IFDEBUG(D_ISISOUTPUT)
1025 printf("isis_output: error from ether_output is %d\n", error);
1026 ENDDEBUG
1027 }
1028 return (error);
1029
1030release:
1031 if (m != NULL)
1032 m_freem(m);
1033 return(error);
1034}
1035
1036
1037/*
1038 * FUNCTION: esis_ctlinput
1039 *
1040 * PURPOSE: Handle the PRC_IFDOWN transition
1041 *
1042 * RETURNS: nothing
1043 *
1044 * SIDE EFFECTS:
1045 *
1046 * NOTES: Calls snpac_flush for interface specified.
1047 * The loop through iso_ifaddr is stupid because
1048 * back in if_down, we knew the ifp...
1049 */
1050esis_ctlinput(req, siso)
1051int req; /* request: we handle only PRC_IFDOWN */
1052struct sockaddr_iso *siso; /* address of ifp */
1053{
1054 register struct iso_ifaddr *ia; /* scan through interface addresses */
1055
1056 if (req == PRC_IFDOWN)
1057 for (ia = iso_ifaddr; ia; ia = ia->ia_next) {
1058 if (iso_addrmatch(IA_SIS(ia), siso))
1059 snpac_flushifp(ia->ia_ifp);
1060 }
1061}
1062
1063#endif ISO