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