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.23 (Berkeley) 04/06/93 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 } 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 void 720 esis_config() 721 { 722 register struct ifnet *ifp; 723 724 timeout(esis_config, (caddr_t)0, hz * esis_config_time); 725 726 /* 727 * Report configuration for each interface that 728 * - is UP 729 * - has BROADCAST capability 730 * - has an ISO address 731 */ 732 /* Todo: a better way would be to construct the esh or ish 733 * once and copy it out for all devices, possibly calling 734 * a method in the iso_ifaddr structure to encapsulate and 735 * transmit it. This could work to advantage for non-broadcast media 736 */ 737 738 for (ifp = ifnet; ifp; ifp = ifp->if_next) { 739 if ((ifp->if_flags & IFF_UP) && 740 (ifp->if_flags & IFF_BROADCAST)) { 741 /* search for an ISO address family */ 742 struct ifaddr *ia; 743 744 for (ia = ifp->if_addrlist; ia; ia = ia->ifa_next) { 745 if (ia->ifa_addr->sa_family == AF_ISO) { 746 esis_shoutput(ifp, 747 iso_systype & SNPA_ES ? ESIS_ESH : ESIS_ISH, 748 esis_holding_time, 749 (caddr_t)(iso_systype & SNPA_ES ? all_is_snpa : 750 all_es_snpa), 6, (struct iso_addr *)0); 751 break; 752 } 753 } 754 } 755 } 756 } 757 758 /* 759 * FUNCTION: esis_shoutput 760 * 761 * PURPOSE: Transmit an esh or ish pdu 762 * 763 * RETURNS: nothing 764 * 765 * SIDE EFFECTS: 766 * 767 * NOTES: 768 */ 769 esis_shoutput(ifp, type, ht, sn_addr, sn_len, isoa) 770 struct ifnet *ifp; 771 int type; 772 short ht; 773 caddr_t sn_addr; 774 int sn_len; 775 struct iso_addr *isoa; 776 { 777 struct mbuf *m, *m0; 778 caddr_t cp, naddrp; 779 int naddr = 0; 780 struct esis_fixed *pdu; 781 struct iso_ifaddr *ia; 782 int len; 783 struct sockaddr_iso siso; 784 785 if (type == ESIS_ESH) 786 esis_stat.es_eshsent++; 787 else if (type == ESIS_ISH) 788 esis_stat.es_ishsent++; 789 else { 790 printf("esis_shoutput: bad pdu type\n"); 791 return; 792 } 793 794 IFDEBUG(D_ESISOUTPUT) 795 int i; 796 printf("esis_shoutput: ifp x%x (%s%d), %s, ht %d, to: [%d] ", 797 ifp, ifp->if_name, ifp->if_unit, type == ESIS_ESH ? "esh" : "ish", 798 ht, sn_len); 799 for (i=0; i<sn_len; i++) 800 printf("%x%c", *(sn_addr+i), i < (sn_len-1) ? ':' : ' '); 801 printf("\n"); 802 ENDDEBUG 803 804 if ((m0 = m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL) { 805 esis_stat.es_nomem++; 806 return; 807 } 808 bzero(mtod(m, caddr_t), MHLEN); 809 810 pdu = mtod(m, struct esis_fixed *); 811 naddrp = cp = (caddr_t)(pdu + 1); 812 len = sizeof(struct esis_fixed); 813 814 /* 815 * Build fixed part of header 816 */ 817 pdu->esis_proto_id = ISO9542_ESIS; 818 pdu->esis_vers = ESIS_VERSION; 819 pdu->esis_type = type; 820 HTOC(pdu->esis_ht_msb, pdu->esis_ht_lsb, ht); 821 822 if (type == ESIS_ESH) { 823 cp++; 824 len++; 825 } 826 827 m->m_len = len; 828 if (isoa) { 829 /* 830 * Here we are responding to a clnp packet sent to an NSAP 831 * that is ours which was sent to the MAC addr all_es's. 832 * It is possible that we did not specifically advertise this 833 * NSAP, even though it is ours, so we will respond 834 * directly to the sender that we are here. If we do have 835 * multiple NSEL's we'll tack them on so he can compress them out. 836 */ 837 (void) esis_insert_addr(&cp, &len, isoa, m, 0); 838 naddr = 1; 839 } 840 for (ia = iso_ifaddr; ia; ia = ia->ia_next) { 841 int nsellen = (type == ESIS_ISH ? ia->ia_addr.siso_tlen : 0); 842 int n = ia->ia_addr.siso_nlen; 843 register struct iso_ifaddr *ia2; 844 845 if (type == ESIS_ISH && naddr > 0) 846 break; 847 for (ia2 = iso_ifaddr; ia2 != ia; ia2 = ia2->ia_next) 848 if (Bcmp(ia->ia_addr.siso_data, ia2->ia_addr.siso_data, n) == 0) 849 break; 850 if (ia2 != ia) 851 continue; /* Means we have previously copied this nsap */ 852 if (isoa && Bcmp(ia->ia_addr.siso_data, isoa->isoa_genaddr, n) == 0) { 853 isoa = 0; 854 continue; /* Ditto */ 855 } 856 IFDEBUG(D_ESISOUTPUT) 857 printf("esis_shoutput: adding NSAP %s\n", 858 clnp_iso_addrp(&ia->ia_addr.siso_addr)); 859 ENDDEBUG 860 if (!esis_insert_addr(&cp, &len, 861 &ia->ia_addr.siso_addr, m, nsellen)) { 862 EXTEND_PACKET(m, m0, cp); 863 (void) esis_insert_addr(&cp, &len, &ia->ia_addr.siso_addr, m, 864 nsellen); 865 } 866 naddr++; 867 } 868 869 if (type == ESIS_ESH) 870 *naddrp = naddr; 871 else { 872 /* add suggested es config timer option to ISH */ 873 if (M_TRAILINGSPACE(m) < 4) { 874 printf("esis_shoutput: extending packet\n"); 875 EXTEND_PACKET(m, m0, cp); 876 } 877 *cp++ = ESISOVAL_ESCT; 878 *cp++ = 2; 879 HTOC(*cp, *(cp+1), esis_esconfig_time); 880 len += 4; 881 m->m_len += 4; 882 IFDEBUG(D_ESISOUTPUT) 883 printf("m0 0x%x, m 0x%x, data 0x%x, len %d, cp 0x%x\n", 884 m0, m, m->m_data, m->m_len, cp); 885 ENDDEBUG 886 } 887 888 m0->m_pkthdr.len = len; 889 pdu->esis_hdr_len = len; 890 iso_gen_csum(m0, ESIS_CKSUM_OFF, (int)pdu->esis_hdr_len); 891 892 bzero((caddr_t)&siso, sizeof(siso)); 893 siso.siso_family = AF_ISO; 894 siso.siso_data[0] = AFI_SNA; 895 siso.siso_nlen = sn_len + 1; 896 bcopy(sn_addr, siso.siso_data + 1, (unsigned)sn_len); 897 (ifp->if_output)(ifp, m0, (struct sockaddr *)&siso, 0); 898 } 899 900 /* 901 * FUNCTION: isis_input 902 * 903 * PURPOSE: Process an incoming isis packet 904 * 905 * RETURNS: nothing 906 * 907 * SIDE EFFECTS: 908 * 909 * NOTES: 910 */ 911 isis_input(m0, shp) 912 struct mbuf *m0; /* ptr to first mbuf of pkt */ 913 struct snpa_hdr *shp; /* subnetwork header */ 914 { 915 register int type; 916 register struct rawcb *rp, *first_rp = 0; 917 struct ifnet *ifp = shp->snh_ifp; 918 char workbuf[16]; 919 struct mbuf *mm; 920 921 IFDEBUG(D_ISISINPUT) 922 int i; 923 924 printf("isis_input: pkt on ifp x%x (%s%d): from:", ifp, 925 ifp->if_name, ifp->if_unit); 926 for (i=0; i<6; i++) 927 printf("%x%c", shp->snh_shost[i]&0xff, (i<5) ? ':' : ' '); 928 printf(" to:"); 929 for (i=0; i<6; i++) 930 printf("%x%c", shp->snh_dhost[i]&0xff, (i<5) ? ':' : ' '); 931 printf("\n"); 932 ENDDEBUG 933 esis_dl.sdl_alen = ifp->if_addrlen; 934 esis_dl.sdl_index = ifp->if_index; 935 bcopy(shp->snh_shost, (caddr_t)esis_dl.sdl_data, esis_dl.sdl_alen); 936 for (rp = esis_pcb.rcb_next; rp != &esis_pcb; rp = rp->rcb_next) { 937 if (first_rp == 0) { 938 first_rp = rp; 939 continue; 940 } 941 if (mm = m_copy(m0, 0, M_COPYALL)) { /*can't block at interrupt level */ 942 if (sbappendaddr(&rp->rcb_socket->so_rcv, 943 &esis_dl, mm, (struct mbuf *)0) != 0) { 944 sorwakeup(rp->rcb_socket); 945 } else { 946 IFDEBUG(D_ISISINPUT) 947 printf("Error in sbappenaddr, mm = 0x%x\n", mm); 948 ENDDEBUG 949 m_freem(mm); 950 } 951 } 952 } 953 if (first_rp && sbappendaddr(&first_rp->rcb_socket->so_rcv, 954 &esis_dl, m0, (struct mbuf *)0) != 0) { 955 sorwakeup(first_rp->rcb_socket); 956 return; 957 } 958 m_freem(m0); 959 } 960 961 isis_output(sdl, m) 962 register struct sockaddr_dl *sdl; 963 struct mbuf *m; 964 { 965 register struct ifnet *ifp; 966 struct ifaddr *ifa, *ifa_ifwithnet(); 967 struct sockaddr_iso siso; 968 int error = 0; 969 unsigned sn_len; 970 971 ifa = ifa_ifwithnet((struct sockaddr *)sdl); /* get ifp from sdl */ 972 if (ifa == 0) { 973 IFDEBUG(D_ISISOUTPUT) 974 printf("isis_output: interface not found\n"); 975 ENDDEBUG 976 error = EINVAL; 977 goto release; 978 } 979 ifp = ifa->ifa_ifp; 980 sn_len = sdl->sdl_alen; 981 IFDEBUG(D_ISISOUTPUT) 982 u_char *cp = (u_char *)LLADDR(sdl), *cplim = cp + sn_len; 983 printf("isis_output: ifp 0x%x (%s%d), to: ", 984 ifp, ifp->if_name, ifp->if_unit); 985 while (cp < cplim) { 986 printf("%x", *cp++); 987 printf("%c", (cp < cplim) ? ':' : ' '); 988 } 989 printf("\n"); 990 ENDDEBUG 991 bzero((caddr_t)&siso, sizeof(siso)); 992 siso.siso_family = AF_ISO; /* This convention may be useful for X.25 */ 993 siso.siso_data[0] = AFI_SNA; 994 siso.siso_nlen = sn_len + 1; 995 bcopy(LLADDR(sdl), siso.siso_data + 1, sn_len); 996 error = (ifp->if_output)(ifp, m, (struct sockaddr *)&siso, 0); 997 if (error) { 998 IFDEBUG(D_ISISOUTPUT) 999 printf("isis_output: error from ether_output is %d\n", error); 1000 ENDDEBUG 1001 } 1002 return (error); 1003 1004 release: 1005 if (m != NULL) 1006 m_freem(m); 1007 return(error); 1008 } 1009 1010 1011 /* 1012 * FUNCTION: esis_ctlinput 1013 * 1014 * PURPOSE: Handle the PRC_IFDOWN transition 1015 * 1016 * RETURNS: nothing 1017 * 1018 * SIDE EFFECTS: 1019 * 1020 * NOTES: Calls snpac_flush for interface specified. 1021 * The loop through iso_ifaddr is stupid because 1022 * back in if_down, we knew the ifp... 1023 */ 1024 esis_ctlinput(req, siso) 1025 int req; /* request: we handle only PRC_IFDOWN */ 1026 struct sockaddr_iso *siso; /* address of ifp */ 1027 { 1028 register struct iso_ifaddr *ia; /* scan through interface addresses */ 1029 1030 if (req == PRC_IFDOWN) 1031 for (ia = iso_ifaddr; ia; ia = ia->ia_next) { 1032 if (iso_addrmatch(IA_SIS(ia), siso)) 1033 snpac_flushifp(ia->ia_ifp); 1034 } 1035 } 1036 1037 #endif ISO 1038