1 /*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 * 7 * @(#)esis.c 8.3 (Berkeley) 03/20/95 8 */ 9 10 /*********************************************************** 11 Copyright IBM Corporation 1987 12 13 All Rights Reserved 14 15 Permission to use, copy, modify, and distribute this software and its 16 documentation for any purpose and without fee is hereby granted, 17 provided that the above copyright notice appear in all copies and that 18 both that copyright notice and this permission notice appear in 19 supporting documentation, and that the name of IBM not be 20 used in advertising or publicity pertaining to distribution of the 21 software without specific, written prior permission. 22 23 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 24 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 25 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 26 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 27 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 28 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 29 SOFTWARE. 30 31 ******************************************************************/ 32 33 /* 34 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison 35 */ 36 37 #ifdef ISO 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/mbuf.h> 42 #include <sys/domain.h> 43 #include <sys/protosw.h> 44 #include <sys/socket.h> 45 #include <sys/socketvar.h> 46 #include <sys/errno.h> 47 #include <sys/kernel.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 <netiso/iso.h> 55 #include <netiso/iso_pcb.h> 56 #include <netiso/iso_var.h> 57 #include <netiso/iso_snpac.h> 58 #include <netiso/clnl.h> 59 #include <netiso/clnp.h> 60 #include <netiso/clnp_stat.h> 61 #include <netiso/esis.h> 62 #include <netiso/argo_debug.h> 63 64 /* 65 * Global variables to esis implementation 66 * 67 * esis_holding_time - the holding time (sec) parameter for outgoing pdus 68 * esis_config_time - the frequency (sec) that hellos are generated 69 * esis_esconfig_time - suggested es configuration time placed in the 70 * ish. 71 * 72 */ 73 struct rawcb esis_pcb; 74 void esis_config(), snpac_age(); 75 int esis_sendspace = 2048; 76 int esis_recvspace = 2048; 77 short esis_holding_time = ESIS_HT; 78 short esis_config_time = ESIS_CONFIG; 79 short esis_esconfig_time = ESIS_CONFIG; 80 extern int iso_systype; 81 struct sockaddr_dl esis_dl = { sizeof(esis_dl), AF_LINK }; 82 extern char all_es_snpa[], all_is_snpa[]; 83 84 #define EXTEND_PACKET(m, mhdr, cp)\ 85 if (((m)->m_next = m_getclr(M_DONTWAIT, MT_HEADER)) == NULL) {\ 86 esis_stat.es_nomem++;\ 87 m_freem(mhdr);\ 88 return;\ 89 } else {\ 90 (m) = (m)->m_next;\ 91 (cp) = mtod((m), caddr_t);\ 92 (m)->m_len = 0;\ 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 */ 105 esis_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*/ 137 esis_usrreq(so, req, m, nam, control) 138 struct socket *so; /* socket: used only to get to this code */ 139 int req; /* request */ 140 struct mbuf *m; /* data for request */ 141 struct mbuf *nam; /* optional name */ 142 struct 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 } 201 release: 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 */ 219 esis_input(m0, shp) 220 struct mbuf *m0; /* ptr to first mbuf of pkt */ 221 struct 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 257 bad: 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 */ 276 esis_rdoutput(inbound_shp, inbound_m, inbound_oidx, rd_dstnsap, rt) 277 struct snpa_hdr *inbound_shp; /* snpa hdr from incoming packet */ 278 struct mbuf *inbound_m; /* incoming pkt itself */ 279 struct clnp_optidx *inbound_oidx; /* clnp options assoc with incoming pkt */ 280 struct iso_addr *rd_dstnsap; /* ultimate destination of pkt */ 281 struct 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, (struct sockaddr *)&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 */ 434 esis_insert_addr(buf, len, isoa, m, nsellen) 435 register caddr_t *buf; /* ptr to buffer to put address into */ 436 int *len; /* ptr to length of buffer so far */ 437 register struct iso_addr *isoa; /* ptr to address */ 438 register struct mbuf *m; /* determine if there remains space */ 439 int 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;}} 460 int 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 */ 474 esis_eshinput(m, shp) 475 struct mbuf *m; /* esh pdu */ 476 struct 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); 543 bad: 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 */ 558 esis_ishinput(m, shp) 559 struct mbuf *m; /* esh pdu */ 560 struct 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); 610 bad: 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 */ 625 esis_rdinput(m0, shp) 626 struct mbuf *m0; /* esh pdu */ 627 struct 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 } 706 bad: ; /* 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 */ 720 void 721 esis_config() 722 { 723 register struct ifnet *ifp; 724 725 timeout(esis_config, (caddr_t)0, hz * esis_config_time); 726 727 /* 728 * Report configuration for each interface that 729 * - is UP 730 * - has BROADCAST capability 731 * - has an ISO address 732 */ 733 /* Todo: a better way would be to construct the esh or ish 734 * once and copy it out for all devices, possibly calling 735 * a method in the iso_ifaddr structure to encapsulate and 736 * transmit it. This could work to advantage for non-broadcast media 737 */ 738 739 for (ifp = ifnet; ifp; ifp = ifp->if_next) { 740 if ((ifp->if_flags & IFF_UP) && 741 (ifp->if_flags & IFF_BROADCAST)) { 742 /* search for an ISO address family */ 743 struct ifaddr *ia; 744 745 for (ia = ifp->if_addrlist; ia; ia = ia->ifa_next) { 746 if (ia->ifa_addr->sa_family == AF_ISO) { 747 esis_shoutput(ifp, 748 iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH, 749 esis_holding_time, 750 (caddr_t)(iso_systype & SNPA_ES ? all_is_snpa : 751 all_es_snpa), 6, (struct iso_addr *)0); 752 break; 753 } 754 } 755 } 756 } 757 } 758 759 /* 760 * FUNCTION: esis_shoutput 761 * 762 * PURPOSE: Transmit an esh or ish pdu 763 * 764 * RETURNS: nothing 765 * 766 * SIDE EFFECTS: 767 * 768 * NOTES: 769 */ 770 esis_shoutput(ifp, type, ht, sn_addr, sn_len, isoa) 771 struct ifnet *ifp; 772 int type; 773 short ht; 774 caddr_t sn_addr; 775 int sn_len; 776 struct iso_addr *isoa; 777 { 778 struct mbuf *m, *m0; 779 caddr_t cp, naddrp; 780 int naddr = 0; 781 struct esis_fixed *pdu; 782 struct iso_ifaddr *ia; 783 int len; 784 struct sockaddr_iso siso; 785 786 if (type == ESIS_ESH) 787 esis_stat.es_eshsent++; 788 else if (type == ESIS_ISH) 789 esis_stat.es_ishsent++; 790 else { 791 printf("esis_shoutput: bad pdu type\n"); 792 return; 793 } 794 795 IFDEBUG(D_ESISOUTPUT) 796 int i; 797 printf("esis_shoutput: ifp x%x (%s%d), %s, ht %d, to: [%d] ", 798 ifp, ifp->if_name, ifp->if_unit, type == ESIS_ESH ? "esh" : "ish", 799 ht, sn_len); 800 for (i=0; i<sn_len; i++) 801 printf("%x%c", *(sn_addr+i), i < (sn_len-1) ? ':' : ' '); 802 printf("\n"); 803 ENDDEBUG 804 805 if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) { 806 esis_stat.es_nomem++; 807 return; 808 } 809 bzero(mtod(m, caddr_t), MHLEN); 810 811 pdu = mtod(m, struct esis_fixed *); 812 naddrp = cp = (caddr_t)(pdu + 1); 813 len = sizeof(struct esis_fixed); 814 815 /* 816 * Build fixed part of header 817 */ 818 pdu->esis_proto_id = ISO9542_ESIS; 819 pdu->esis_vers = ESIS_VERSION; 820 pdu->esis_type = type; 821 HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht); 822 823 if (type == ESIS_ESH) { 824 cp++; 825 len++; 826 } 827 828 m->m_len = len; 829 if (isoa) { 830 /* 831 * Here we are responding to a clnp packet sent to an NSAP 832 * that is ours which was sent to the MAC addr all_es's. 833 * It is possible that we did not specifically advertise this 834 * NSAP, even though it is ours, so we will respond 835 * directly to the sender that we are here. If we do have 836 * multiple NSEL's we'll tack them on so he can compress them out. 837 */ 838 (void) esis_insert_addr(&cp, &len, isoa, m, 0); 839 naddr = 1; 840 } 841 for (ia = iso_ifaddr; ia; ia = ia->ia_next) { 842 int nsellen = (type == ESIS_ISH ? ia->ia_addr.siso_tlen : 0); 843 int n = ia->ia_addr.siso_nlen; 844 register struct iso_ifaddr *ia2; 845 846 if (type == ESIS_ISH && naddr > 0) 847 break; 848 for (ia2 = iso_ifaddr; ia2 != ia; ia2 = ia2->ia_next) 849 if (Bcmp(ia->ia_addr.siso_data, ia2->ia_addr.siso_data, n) == 0) 850 break; 851 if (ia2 != ia) 852 continue; /* Means we have previously copied this nsap */ 853 if (isoa && Bcmp(ia->ia_addr.siso_data, isoa->isoa_genaddr, n) == 0) { 854 isoa = 0; 855 continue; /* Ditto */ 856 } 857 IFDEBUG(D_ESISOUTPUT) 858 printf("esis_shoutput: adding NSAP %s\n", 859 clnp_iso_addrp(&ia->ia_addr.siso_addr)); 860 ENDDEBUG 861 if (!esis_insert_addr(&cp, &len, 862 &ia->ia_addr.siso_addr, m, nsellen)) { 863 EXTEND_PACKET(m, m0, cp); 864 (void) esis_insert_addr(&cp, &len, &ia->ia_addr.siso_addr, m, 865 nsellen); 866 } 867 naddr++; 868 } 869 870 if (type == ESIS_ESH) 871 *naddrp = naddr; 872 else { 873 /* add suggested es config timer option to ISH */ 874 if (M_TRAILINGSPACE(m) < 4) { 875 printf("esis_shoutput: extending packet\n"); 876 EXTEND_PACKET(m, m0, cp); 877 } 878 *cp++ = ESISOVAL_ESCT; 879 *cp++ = 2; 880 HTOC(*cp, *(cp+1), esis_esconfig_time); 881 len += 4; 882 m->m_len += 4; 883 IFDEBUG(D_ESISOUTPUT) 884 printf("m0 0x%x, m 0x%x, data 0x%x, len %d, cp 0x%x\n", 885 m0, m, m->m_data, m->m_len, cp); 886 ENDDEBUG 887 } 888 889 m0->m_pkthdr.len = len; 890 pdu->esis_hdr_len = len; 891 iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len); 892 893 bzero((caddr_t)&siso, sizeof(siso)); 894 siso.siso_family = AF_ISO; 895 siso.siso_data[0] = AFI_SNA; 896 siso.siso_nlen = sn_len + 1; 897 bcopy(sn_addr, siso.siso_data + 1, (unsigned)sn_len); 898 (ifp->if_output)(ifp, m0, (struct sockaddr *)&siso, 0); 899 } 900 901 /* 902 * FUNCTION: isis_input 903 * 904 * PURPOSE: Process an incoming isis packet 905 * 906 * RETURNS: nothing 907 * 908 * SIDE EFFECTS: 909 * 910 * NOTES: 911 */ 912 isis_input(m0, shp) 913 struct mbuf *m0; /* ptr to first mbuf of pkt */ 914 struct snpa_hdr *shp; /* subnetwork header */ 915 { 916 register int type; 917 register struct rawcb *rp, *first_rp = 0; 918 struct ifnet *ifp = shp->snh_ifp; 919 char workbuf[16]; 920 struct mbuf *mm; 921 922 IFDEBUG(D_ISISINPUT) 923 int i; 924 925 printf("isis_input: pkt on ifp x%x (%s%d): from:", ifp, 926 ifp->if_name, ifp->if_unit); 927 for (i=0; i<6; i++) 928 printf("%x%c", shp->snh_shost[i]&0xff, (i<5) ? ':' : ' '); 929 printf(" to:"); 930 for (i=0; i<6; i++) 931 printf("%x%c", shp->snh_dhost[i]&0xff, (i<5) ? ':' : ' '); 932 printf("\n"); 933 ENDDEBUG 934 esis_dl.sdl_alen = ifp->if_addrlen; 935 esis_dl.sdl_index = ifp->if_index; 936 bcopy(shp->snh_shost, (caddr_t)esis_dl.sdl_data, esis_dl.sdl_alen); 937 for (rp = esis_pcb.rcb_next; rp != &esis_pcb; rp = rp->rcb_next) { 938 if (first_rp == 0) { 939 first_rp = rp; 940 continue; 941 } 942 if (mm = m_copy(m0, 0, M_COPYALL)) { /*can't block at interrupt level */ 943 if (sbappendaddr(&rp->rcb_socket->so_rcv, 944 (struct sockaddr *)&esis_dl, mm, 945 (struct mbuf *)0) != 0) { 946 sorwakeup(rp->rcb_socket); 947 } else { 948 IFDEBUG(D_ISISINPUT) 949 printf("Error in sbappenaddr, mm = 0x%x\n", mm); 950 ENDDEBUG 951 m_freem(mm); 952 } 953 } 954 } 955 if (first_rp && sbappendaddr(&first_rp->rcb_socket->so_rcv, 956 (struct sockaddr *)&esis_dl, m0, 957 (struct mbuf *)0) != 0) { 958 sorwakeup(first_rp->rcb_socket); 959 return; 960 } 961 m_freem(m0); 962 } 963 964 isis_output(sdl, m) 965 register struct sockaddr_dl *sdl; 966 struct mbuf *m; 967 { 968 register struct ifnet *ifp; 969 struct ifaddr *ifa, *ifa_ifwithnet(); 970 struct sockaddr_iso siso; 971 int error = 0; 972 unsigned sn_len; 973 974 ifa = ifa_ifwithnet((struct sockaddr *)sdl); /* get ifp from sdl */ 975 if (ifa == 0) { 976 IFDEBUG(D_ISISOUTPUT) 977 printf("isis_output: interface not found\n"); 978 ENDDEBUG 979 error = EINVAL; 980 goto release; 981 } 982 ifp = ifa->ifa_ifp; 983 sn_len = sdl->sdl_alen; 984 IFDEBUG(D_ISISOUTPUT) 985 u_char *cp = (u_char *)LLADDR(sdl), *cplim = cp + sn_len; 986 printf("isis_output: ifp 0x%x (%s%d), to: ", 987 ifp, ifp->if_name, ifp->if_unit); 988 while (cp < cplim) { 989 printf("%x", *cp++); 990 printf("%c", (cp < cplim) ? ':' : ' '); 991 } 992 printf("\n"); 993 ENDDEBUG 994 bzero((caddr_t)&siso, sizeof(siso)); 995 siso.siso_family = AF_ISO; /* This convention may be useful for X.25 */ 996 siso.siso_data[0] = AFI_SNA; 997 siso.siso_nlen = sn_len + 1; 998 bcopy(LLADDR(sdl), siso.siso_data + 1, sn_len); 999 error = (ifp->if_output)(ifp, m, (struct sockaddr *)&siso, 0); 1000 if (error) { 1001 IFDEBUG(D_ISISOUTPUT) 1002 printf("isis_output: error from ether_output is %d\n", error); 1003 ENDDEBUG 1004 } 1005 return (error); 1006 1007 release: 1008 if (m != NULL) 1009 m_freem(m); 1010 return(error); 1011 } 1012 1013 1014 /* 1015 * FUNCTION: esis_ctlinput 1016 * 1017 * PURPOSE: Handle the PRC_IFDOWN transition 1018 * 1019 * RETURNS: nothing 1020 * 1021 * SIDE EFFECTS: 1022 * 1023 * NOTES: Calls snpac_flush for interface specified. 1024 * The loop through iso_ifaddr is stupid because 1025 * back in if_down, we knew the ifp... 1026 */ 1027 esis_ctlinput(req, siso) 1028 int req; /* request: we handle only PRC_IFDOWN */ 1029 struct sockaddr_iso *siso; /* address of ifp */ 1030 { 1031 register struct iso_ifaddr *ia; /* scan through interface addresses */ 1032 1033 if (req == PRC_IFDOWN) 1034 for (ia = iso_ifaddr; ia; ia = ia->ia_next) { 1035 if (iso_addrmatch(IA_SIS(ia), siso)) 1036 snpac_flushifp(ia->ia_ifp); 1037 } 1038 } 1039 1040 #endif /* ISO */ 1041