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