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