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.18 (Berkeley) 05/06/91 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 "types.h" 40 #include "param.h" 41 #include "systm.h" 42 #include "mbuf.h" 43 #include "domain.h" 44 #include "protosw.h" 45 #include "user.h" 46 #include "socket.h" 47 #include "socketvar.h" 48 #include "errno.h" 49 #include "kernel.h" 50 51 #include "../net/if.h" 52 #include "../net/if_dl.h" 53 #include "../net/route.h" 54 #include "../net/raw_cb.h" 55 56 #include "iso.h" 57 #include "iso_pcb.h" 58 #include "iso_var.h" 59 #include "iso_snpac.h" 60 #include "clnl.h" 61 #include "clnp.h" 62 #include "clnp_stat.h" 63 #include "esis.h" 64 #include "argo_debug.h" 65 66 /* 67 * Global variables to esis implementation 68 * 69 * esis_holding_time - the holding time (sec) parameter for outgoing pdus 70 * esis_config_time - the frequency (sec) that hellos are generated 71 * esis_esconfig_time - suggested es configuration time placed in the 72 * ish. 73 * 74 */ 75 struct rawcb esis_pcb; 76 int esis_sendspace = 2048; 77 int esis_recvspace = 2048; 78 short esis_holding_time = ESIS_HT; 79 short esis_config_time = ESIS_CONFIG; 80 short esis_esconfig_time = ESIS_CONFIG; 81 extern int iso_systype; 82 struct sockaddr_dl esis_dl = { sizeof(esis_dl), AF_LINK }; 83 extern 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 */ 105 esis_init() 106 { 107 extern struct clnl_protosw clnl_protox[256]; 108 int esis_input(), isis_input(); 109 int esis_config(), snpac_age(); 110 #ifdef ISO_X25ESIS 111 int x25esis_input(); 112 #endif ISO_X25ESIS 113 114 esis_pcb.rcb_next = esis_pcb.rcb_prev = &esis_pcb; 115 llinfo_llc.lc_next = llinfo_llc.lc_prev = &llinfo_llc; 116 117 timeout(snpac_age, (caddr_t)0, hz); 118 timeout(esis_config, (caddr_t)0, hz); 119 120 clnl_protox[ISO9542_ESIS].clnl_input = esis_input; 121 clnl_protox[ISO10589_ISIS].clnl_input = isis_input; 122 #ifdef ISO_X25ESIS 123 clnl_protox[ISO9542X25_ESIS].clnl_input = x25esis_input; 124 #endif ISO_X25ESIS 125 } 126 127 /* 128 * FUNCTION: esis_usrreq 129 * 130 * PURPOSE: Handle user level esis requests 131 * 132 * RETURNS: 0 or appropriate errno 133 * 134 * SIDE EFFECTS: 135 * 136 */ 137 /*ARGSUSED*/ 138 esis_usrreq(so, req, m, nam, control) 139 struct socket *so; /* socket: used only to get to this code */ 140 int req; /* request */ 141 struct mbuf *m; /* data for request */ 142 struct mbuf *nam; /* optional name */ 143 struct mbuf *control; /* optional control */ 144 { 145 struct rawcb *rp = sotorawcb(so); 146 int error = 0; 147 148 if (suser(u.u_cred, &u.u_acflag)) { 149 error = EACCES; 150 goto release; 151 } 152 if (rp == NULL && req != PRU_ATTACH) { 153 error = EINVAL; 154 goto release; 155 } 156 157 switch (req) { 158 case PRU_ATTACH: 159 if (rp != NULL) { 160 error = EINVAL; 161 break; 162 } 163 MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK); 164 if (so->so_pcb = (caddr_t)rp) { 165 bzero(so->so_pcb, sizeof(*rp)); 166 insque(rp, &esis_pcb); 167 rp->rcb_socket = so; 168 error = soreserve(so, esis_sendspace, esis_recvspace); 169 } else 170 error = ENOBUFS; 171 break; 172 173 case PRU_SEND: 174 if (nam == NULL) { 175 error = EINVAL; 176 break; 177 } 178 /* error checking here */ 179 error = isis_output(mtod(nam,struct sockaddr_dl *), m); 180 m = NULL; 181 break; 182 183 case PRU_DETACH: 184 raw_detach(rp); 185 break; 186 187 case PRU_SHUTDOWN: 188 socantsendmore(so); 189 break; 190 191 case PRU_ABORT: 192 soisdisconnected(so); 193 raw_detach(rp); 194 break; 195 196 case PRU_SENSE: 197 return (0); 198 199 default: 200 return (EOPNOTSUPP); 201 } 202 release: 203 if (m != NULL) 204 m_freem(m); 205 206 return (error); 207 } 208 209 /* 210 * FUNCTION: esis_input 211 * 212 * PURPOSE: Process an incoming esis packet 213 * 214 * RETURNS: nothing 215 * 216 * SIDE EFFECTS: 217 * 218 * NOTES: 219 */ 220 esis_input(m0, shp) 221 struct mbuf *m0; /* ptr to first mbuf of pkt */ 222 struct snpa_hdr *shp; /* subnetwork header */ 223 { 224 register struct esis_fixed *pdu = mtod(m0, struct esis_fixed *); 225 register int type; 226 227 /* 228 * check checksum if necessary 229 */ 230 if (ESIS_CKSUM_REQUIRED(pdu) && iso_check_csum(m0, (int)pdu->esis_hdr_len)) { 231 esis_stat.es_badcsum++; 232 goto bad; 233 } 234 235 /* check version */ 236 if (pdu->esis_vers != ESIS_VERSION) { 237 esis_stat.es_badvers++; 238 goto bad; 239 } 240 type = pdu->esis_type & 0x1f; 241 switch (type) { 242 case ESIS_ESH: 243 esis_eshinput(m0, shp); 244 break; 245 246 case ESIS_ISH: 247 esis_ishinput(m0, shp); 248 break; 249 250 case ESIS_RD: 251 esis_rdinput(m0, shp); 252 break; 253 254 default: 255 esis_stat.es_badtype++; 256 } 257 258 bad: 259 if (esis_pcb.rcb_next != &esis_pcb) 260 isis_input(m0, shp); 261 else 262 m_freem(m0); 263 } 264 265 /* 266 * FUNCTION: esis_rdoutput 267 * 268 * PURPOSE: Transmit a redirect pdu 269 * 270 * RETURNS: nothing 271 * 272 * SIDE EFFECTS: 273 * 274 * NOTES: Assumes there is enough space for fixed part of header, 275 * DA, BSNPA and NET in first mbuf. 276 */ 277 esis_rdoutput(inbound_shp, inbound_m, inbound_oidx, rd_dstnsap, rt) 278 struct snpa_hdr *inbound_shp; /* snpa hdr from incoming packet */ 279 struct mbuf *inbound_m; /* incoming pkt itself */ 280 struct clnp_optidx *inbound_oidx; /* clnp options assoc with incoming pkt */ 281 struct iso_addr *rd_dstnsap; /* ultimate destination of pkt */ 282 struct rtentry *rt; /* snpa cache info regarding next hop of 283 pkt */ 284 { 285 struct mbuf *m, *m0; 286 caddr_t cp; 287 struct esis_fixed *pdu; 288 int len, total_len = 0; 289 struct sockaddr_iso siso; 290 struct ifnet *ifp = inbound_shp->snh_ifp; 291 struct sockaddr_dl *sdl; 292 struct iso_addr *rd_gwnsap; 293 294 if (rt->rt_flags & RTF_GATEWAY) { 295 rd_gwnsap = &((struct sockaddr_iso *)rt->rt_gateway)->siso_addr; 296 rt = rtalloc1(rt->rt_gateway, 0); 297 } else 298 rd_gwnsap = &((struct sockaddr_iso *)rt_key(rt))->siso_addr; 299 if (rt == 0 || (sdl = (struct sockaddr_dl *)rt->rt_gateway) == 0 || 300 sdl->sdl_family != AF_LINK) { 301 /* maybe we should have a function that you 302 could put in the iso_ifaddr structure 303 which could translate iso_addrs into snpa's 304 where there is a known mapping for that address type */ 305 esis_stat.es_badtype++; 306 return; 307 } 308 esis_stat.es_rdsent++; 309 IFDEBUG(D_ESISOUTPUT) 310 printf("esis_rdoutput: ifp x%x (%s%d), ht %d, m x%x, oidx x%x\n", 311 ifp, ifp->if_name, ifp->if_unit, esis_holding_time, inbound_m, 312 inbound_oidx); 313 printf("\tdestination: %s\n", clnp_iso_addrp(rd_dstnsap)); 314 printf("\tredirected toward:%s\n", clnp_iso_addrp(rd_gwnsap)); 315 ENDDEBUG 316 317 if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) { 318 esis_stat.es_nomem++; 319 return; 320 } 321 bzero(mtod(m, caddr_t), MHLEN); 322 323 pdu = mtod(m, struct esis_fixed *); 324 cp = (caddr_t)(pdu + 1); /*pointer arith.; 1st byte after header */ 325 len = sizeof(struct esis_fixed); 326 327 /* 328 * Build fixed part of header 329 */ 330 pdu->esis_proto_id = ISO9542_ESIS; 331 pdu->esis_vers = ESIS_VERSION; 332 pdu->esis_type = ESIS_RD; 333 HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, esis_holding_time); 334 335 /* Insert destination address */ 336 (void) esis_insert_addr(&cp, &len, rd_dstnsap, m, 0); 337 338 /* Insert the snpa of better next hop */ 339 *cp++ = sdl->sdl_alen; 340 bcopy(LLADDR(sdl), cp, sdl->sdl_alen); 341 cp += sdl->sdl_alen; 342 len += (sdl->sdl_alen + 1); 343 344 /* 345 * If the next hop is not the destination, then it ought to be 346 * an IS and it should be inserted next. Else, set the 347 * NETL to 0 348 */ 349 /* PHASE2 use mask from ifp of outgoing interface */ 350 if (!iso_addrmatch1(rd_dstnsap, rd_gwnsap)) { 351 /* this should not happen: 352 if ((nhop_sc->sc_flags & SNPA_IS) == 0) { 353 printf("esis_rdoutput: next hop is not dst and not an IS\n"); 354 m_freem(m0); 355 return; 356 } */ 357 (void) esis_insert_addr(&cp, &len, rd_gwnsap, m, 0); 358 } else { 359 *cp++ = 0; /* NETL */ 360 len++; 361 } 362 m->m_len = len; 363 364 /* 365 * PHASE2 366 * If redirect is to an IS, add an address mask. The mask to be 367 * used should be the mask present in the routing entry used to 368 * forward the original data packet. 369 */ 370 371 /* 372 * Copy Qos, priority, or security options present in original npdu 373 */ 374 if (inbound_oidx) { 375 /* THIS CODE IS CURRENTLY (mostly) UNTESTED */ 376 int optlen = 0; 377 if (inbound_oidx->cni_qos_formatp) 378 optlen += (inbound_oidx->cni_qos_len + 2); 379 if (inbound_oidx->cni_priorp) /* priority option is 1 byte long */ 380 optlen += 3; 381 if (inbound_oidx->cni_securep) 382 optlen += (inbound_oidx->cni_secure_len + 2); 383 if (M_TRAILINGSPACE(m) < optlen) { 384 EXTEND_PACKET(m, m0, cp); 385 m->m_len = 0; 386 /* assumes MLEN > optlen */ 387 } 388 /* assume MLEN-len > optlen */ 389 /* 390 * When copying options, copy from ptr - 2 in order to grab 391 * the option code and length 392 */ 393 if (inbound_oidx->cni_qos_formatp) { 394 bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_qos_formatp - 2, 395 cp, (unsigned)(inbound_oidx->cni_qos_len + 2)); 396 cp += inbound_oidx->cni_qos_len + 2; 397 } 398 if (inbound_oidx->cni_priorp) { 399 bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_priorp - 2, 400 cp, 3); 401 cp += 3; 402 } 403 if (inbound_oidx->cni_securep) { 404 bcopy(mtod(inbound_m, caddr_t) + inbound_oidx->cni_securep - 2, cp, 405 (unsigned)(inbound_oidx->cni_secure_len + 2)); 406 cp += inbound_oidx->cni_secure_len + 2; 407 } 408 m->m_len += optlen; 409 len += optlen; 410 } 411 412 pdu->esis_hdr_len = m0->m_pkthdr.len = len; 413 iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len); 414 415 bzero((caddr_t)&siso, sizeof(siso)); 416 siso.siso_family = AF_ISO; 417 siso.siso_data[0] = AFI_SNA; 418 siso.siso_nlen = 6 + 1; /* should be taken from snpa_hdr */ 419 /* +1 is for AFI */ 420 bcopy(inbound_shp->snh_shost, siso.siso_data + 1, 6); 421 (ifp->if_output)(ifp, m0, &siso, 0); 422 } 423 424 /* 425 * FUNCTION: esis_insert_addr 426 * 427 * PURPOSE: Insert an iso_addr into a buffer 428 * 429 * RETURNS: true if buffer was big enough, else false 430 * 431 * SIDE EFFECTS: Increment buf & len according to size of iso_addr 432 * 433 * NOTES: Plus 1 here is for length byte 434 */ 435 esis_insert_addr(buf, len, isoa, m, nsellen) 436 register caddr_t *buf; /* ptr to buffer to put address into */ 437 int *len; /* ptr to length of buffer so far */ 438 register struct iso_addr *isoa; /* ptr to address */ 439 register struct mbuf *m; /* determine if there remains space */ 440 int nsellen; 441 { 442 register int newlen, result = 0; 443 444 isoa->isoa_len -= nsellen; 445 newlen = isoa->isoa_len + 1; 446 if (newlen <= M_TRAILINGSPACE(m)) { 447 bcopy((caddr_t)isoa, *buf, newlen); 448 *len += newlen; 449 *buf += newlen; 450 m->m_len += newlen; 451 result = 1; 452 } 453 isoa->isoa_len += nsellen; 454 return (result); 455 } 456 457 #define ESIS_EXTRACT_ADDR(d, b) { d = (struct iso_addr *)(b); b += (1 + *b); \ 458 if (b > buflim) {esis_stat.es_toosmall++; goto bad;}} 459 #define ESIS_NEXT_OPTION(b) { b += (2 + b[1]); \ 460 if (b > buflim) {esis_stat.es_toosmall++; goto bad;}} 461 int ESHonly = 0; 462 /* 463 464 /* 465 * FUNCTION: esis_eshinput 466 * 467 * PURPOSE: Process an incoming ESH pdu 468 * 469 * RETURNS: nothing 470 * 471 * SIDE EFFECTS: 472 * 473 * NOTES: 474 */ 475 esis_eshinput(m, shp) 476 struct mbuf *m; /* esh pdu */ 477 struct snpa_hdr *shp; /* subnetwork header */ 478 { 479 struct esis_fixed *pdu = mtod(m, struct esis_fixed *); 480 u_short ht; /* holding time */ 481 struct iso_addr *nsap; 482 int naddr; 483 u_char *buf = (u_char *)(pdu + 1); 484 u_char *buflim = pdu->esis_hdr_len + (u_char *)pdu; 485 int new_entry = 0; 486 487 esis_stat.es_eshrcvd++; 488 489 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht); 490 491 naddr = *buf++; 492 if (buf >= buflim) 493 goto bad; 494 if (naddr == 1) { 495 ESIS_EXTRACT_ADDR(nsap, buf); 496 new_entry = snpac_add(shp->snh_ifp, 497 nsap, shp->snh_shost, SNPA_ES, ht, 0); 498 } else { 499 int nsellength = 0, nlen = 0; 500 { 501 /* See if we want to compress out multiple nsaps differing 502 only by nsel */ 503 register struct ifaddr *ifa = shp->snh_ifp->if_addrlist; 504 for (; ifa; ifa = ifa->ifa_next) 505 if (ifa->ifa_addr->sa_family == AF_ISO) { 506 nsellength = ((struct iso_ifaddr *)ifa)->ia_addr.siso_tlen; 507 break; 508 } 509 } 510 IFDEBUG(D_ESISINPUT) 511 printf("esis_eshinput: esh: ht %d, naddr %d nsellength %d\n", 512 ht, naddr, nsellength); 513 ENDDEBUG 514 while (naddr-- > 0) { 515 struct iso_addr *nsap2; u_char *buf2; 516 ESIS_EXTRACT_ADDR(nsap, buf); 517 /* see if there is at least one more nsap in ESH differing 518 only by nsel */ 519 if (nsellength != 0) for (buf2 = buf; buf2 < buflim;) { 520 ESIS_EXTRACT_ADDR(nsap2, buf2); 521 IFDEBUG(D_ESISINPUT) 522 printf("esis_eshinput: comparing %s ", 523 clnp_iso_addrp(nsap)); 524 printf("and %s\n", clnp_iso_addrp(nsap2)); 525 ENDDEBUG 526 if (Bcmp(nsap->isoa_genaddr, nsap2->isoa_genaddr, 527 nsap->isoa_len - nsellength) == 0) { 528 nlen = nsellength; 529 break; 530 } 531 } 532 new_entry |= snpac_add(shp->snh_ifp, 533 nsap, shp->snh_shost, SNPA_ES, ht, nlen); 534 nlen = 0; 535 } 536 } 537 IFDEBUG(D_ESISINPUT) 538 printf("esis_eshinput: nsap %s is %s\n", 539 clnp_iso_addrp(nsap), new_entry ? "new" : "old"); 540 ENDDEBUG 541 if (new_entry && (iso_systype & SNPA_IS)) 542 esis_shoutput(shp->snh_ifp, ESIS_ISH, esis_holding_time, 543 shp->snh_shost, 6, (struct iso_addr *)0); 544 bad: 545 return; 546 } 547 548 /* 549 * FUNCTION: esis_ishinput 550 * 551 * PURPOSE: process an incoming ISH pdu 552 * 553 * RETURNS: 554 * 555 * SIDE EFFECTS: 556 * 557 * NOTES: 558 */ 559 esis_ishinput(m, shp) 560 struct mbuf *m; /* esh pdu */ 561 struct snpa_hdr *shp; /* subnetwork header */ 562 { 563 struct esis_fixed *pdu = mtod(m, struct esis_fixed *); 564 u_short ht, newct; /* holding time */ 565 struct iso_addr *nsap; /* Network Entity Title */ 566 register u_char *buf = (u_char *) (pdu + 1); 567 register u_char *buflim = pdu->esis_hdr_len + (u_char *)pdu; 568 int new_entry; 569 570 esis_stat.es_ishrcvd++; 571 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht); 572 573 IFDEBUG(D_ESISINPUT) 574 printf("esis_ishinput: ish: ht %d\n", ht); 575 ENDDEBUG 576 if (ESHonly) 577 goto bad; 578 579 ESIS_EXTRACT_ADDR(nsap, buf); 580 581 while (buf < buflim) { 582 switch (*buf) { 583 case ESISOVAL_ESCT: 584 if (iso_systype & SNPA_IS) 585 break; 586 if (buf[1] != 2) 587 goto bad; 588 CTOH(buf[2], buf[3], newct); 589 if (esis_config_time != newct) { 590 untimeout(esis_config,0); 591 esis_config_time = newct; 592 esis_config(); 593 } 594 break; 595 596 default: 597 printf("Unknown ISH option: %x\n", *buf); 598 } 599 ESIS_NEXT_OPTION(buf); 600 } 601 new_entry = snpac_add(shp->snh_ifp, nsap, shp->snh_shost, SNPA_IS, ht, 0); 602 IFDEBUG(D_ESISINPUT) 603 printf("esis_ishinput: nsap %s is %s\n", 604 clnp_iso_addrp(nsap), new_entry ? "new" : "old"); 605 ENDDEBUG 606 607 if (new_entry) 608 esis_shoutput(shp->snh_ifp, 609 iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH, 610 esis_holding_time, shp->snh_shost, 6, (struct iso_addr *)0); 611 bad: 612 return; 613 } 614 615 /* 616 * FUNCTION: esis_rdinput 617 * 618 * PURPOSE: Process an incoming RD pdu 619 * 620 * RETURNS: 621 * 622 * SIDE EFFECTS: 623 * 624 * NOTES: 625 */ 626 esis_rdinput(m0, shp) 627 struct mbuf *m0; /* esh pdu */ 628 struct snpa_hdr *shp; /* subnetwork header */ 629 { 630 struct esis_fixed *pdu = mtod(m0, struct esis_fixed *); 631 u_short ht; /* holding time */ 632 struct iso_addr *da, *net = 0, *netmask = 0, *snpamask = 0; 633 register struct iso_addr *bsnpa; 634 register u_char *buf = (u_char *)(pdu + 1); 635 register u_char *buflim = pdu->esis_hdr_len + (u_char *)pdu; 636 637 esis_stat.es_rdrcvd++; 638 639 /* intermediate systems ignore redirects */ 640 if (iso_systype & SNPA_IS) 641 return; 642 if (ESHonly) 643 return; 644 645 CTOH(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht); 646 if (buf >= buflim) 647 return; 648 649 /* Extract DA */ 650 ESIS_EXTRACT_ADDR(da, buf); 651 652 /* Extract better snpa */ 653 ESIS_EXTRACT_ADDR(bsnpa, buf); 654 655 /* Extract NET if present */ 656 if (buf < buflim) { 657 if (*buf == 0) 658 buf++; /* no NET present, skip NETL anyway */ 659 else 660 ESIS_EXTRACT_ADDR(net, buf); 661 } 662 663 /* process options */ 664 while (buf < buflim) { 665 switch (*buf) { 666 case ESISOVAL_SNPAMASK: 667 if (snpamask) /* duplicate */ 668 return; 669 snpamask = (struct iso_addr *)(buf + 1); 670 break; 671 672 case ESISOVAL_NETMASK: 673 if (netmask) /* duplicate */ 674 return; 675 netmask = (struct iso_addr *)(buf + 1); 676 break; 677 678 default: 679 printf("Unknown option in ESIS RD (0x%x)\n", buf[-1]); 680 } 681 ESIS_NEXT_OPTION(buf); 682 } 683 684 IFDEBUG(D_ESISINPUT) 685 printf("esis_rdinput: rd: ht %d, da %s\n", ht, clnp_iso_addrp(da)); 686 if (net) 687 printf("\t: net %s\n", clnp_iso_addrp(net)); 688 ENDDEBUG 689 /* 690 * If netl is zero, then redirect is to an ES. We need to add an entry 691 * to the snpa cache for (destination, better snpa). 692 * If netl is not zero, then the redirect is to an IS. In this 693 * case, add an snpa cache entry for (net, better snpa). 694 * 695 * If the redirect is to an IS, add a route entry towards that 696 * IS. 697 */ 698 if (net == 0 || net->isoa_len == 0 || snpamask) { 699 /* redirect to an ES */ 700 snpac_add(shp->snh_ifp, da, 701 bsnpa->isoa_genaddr, SNPA_ES, ht, 0); 702 } else { 703 snpac_add(shp->snh_ifp, net, 704 bsnpa->isoa_genaddr, SNPA_IS, ht, 0); 705 snpac_addrt(shp->snh_ifp, da, net, netmask); 706 } 707 bad: ; /* Needed by ESIS_NEXT_OPTION */ 708 } 709 710 /* 711 * FUNCTION: esis_config 712 * 713 * PURPOSE: Report configuration 714 * 715 * RETURNS: 716 * 717 * SIDE EFFECTS: 718 * 719 * NOTES: Called every esis_config_time seconds 720 */ 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, &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 &esis_dl, mm, (struct mbuf *)0) != 0) 945 sorwakeup(rp->rcb_socket); 946 else { 947 IFDEBUG(D_ISISINPUT) 948 printf("Error in sbappenaddr, mm = 0x%x\n", mm); 949 ENDDEBUG 950 m_freem(mm); 951 } 952 } 953 } 954 if (first_rp && sbappendaddr(&first_rp->rcb_socket->so_rcv, 955 &esis_dl, m0, (struct mbuf *)0) != 0) { 956 sorwakeup(first_rp->rcb_socket); 957 return; 958 } 959 m_freem(m0); 960 } 961 962 isis_output(sdl, m) 963 register struct sockaddr_dl *sdl; 964 struct mbuf *m; 965 { 966 register struct ifnet *ifp; 967 struct ifaddr *ifa, *ifa_ifwithnet(); 968 struct sockaddr_iso siso; 969 int error = 0; 970 unsigned sn_len; 971 972 ifa = ifa_ifwithnet(sdl); /* extract ifp from sockaddr_dl */ 973 if (ifa == 0) { 974 IFDEBUG(D_ISISOUTPUT) 975 printf("isis_output: interface not found\n"); 976 ENDDEBUG 977 error = EINVAL; 978 goto release; 979 } 980 ifp = ifa->ifa_ifp; 981 sn_len = sdl->sdl_alen; 982 IFDEBUG(D_ISISOUTPUT) 983 u_char *cp = (u_char *)LLADDR(sdl), *cplim = cp + sn_len; 984 printf("isis_output: ifp 0x%x (%s%d), to: ", 985 ifp, ifp->if_name, ifp->if_unit); 986 while (cp < cplim) { 987 printf("%x", *cp++); 988 printf("%c", (cp < cplim) ? ':' : ' '); 989 } 990 printf("\n"); 991 ENDDEBUG 992 bzero((caddr_t)&siso, sizeof(siso)); 993 siso.siso_family = AF_ISO; /* This convention may be useful for X.25 */ 994 siso.siso_data[0] = AFI_SNA; 995 siso.siso_nlen = sn_len + 1; 996 bcopy(LLADDR(sdl), siso.siso_data + 1, sn_len); 997 error = (ifp->if_output)(ifp, m, (struct sockaddr *)&siso, 0); 998 if (error) { 999 IFDEBUG(D_ISISOUTPUT) 1000 printf("isis_output: error from ether_output is %d\n", error); 1001 ENDDEBUG 1002 } 1003 return (error); 1004 1005 release: 1006 if (m != NULL) 1007 m_freem(m); 1008 return(error); 1009 } 1010 1011 1012 /* 1013 * FUNCTION: esis_ctlinput 1014 * 1015 * PURPOSE: Handle the PRC_IFDOWN transition 1016 * 1017 * RETURNS: nothing 1018 * 1019 * SIDE EFFECTS: 1020 * 1021 * NOTES: Calls snpac_flush for interface specified. 1022 * The loop through iso_ifaddr is stupid because 1023 * back in if_down, we knew the ifp... 1024 */ 1025 esis_ctlinput(req, siso) 1026 int req; /* request: we handle only PRC_IFDOWN */ 1027 struct sockaddr_iso *siso; /* address of ifp */ 1028 { 1029 register struct iso_ifaddr *ia; /* scan through interface addresses */ 1030 1031 if (req == PRC_IFDOWN) 1032 for (ia = iso_ifaddr; ia; ia = ia->ia_next) { 1033 if (iso_addrmatch(IA_SIS(ia), siso)) 1034 snpac_flushifp(ia->ia_ifp); 1035 } 1036 } 1037 1038 #endif ISO 1039