1 /*********************************************************** 2 Copyright IBM Corporation 1987 3 4 All Rights Reserved 5 6 Permission to use, copy, modify, and distribute this software and its 7 documentation for any purpose and without fee is hereby granted, 8 provided that the above copyright notice appear in all copies and that 9 both that copyright notice and this permission notice appear in 10 supporting documentation, and that the name of IBM not be 11 used in advertising or publicity pertaining to distribution of the 12 software without specific, written prior permission. 13 14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20 SOFTWARE. 21 22 ******************************************************************/ 23 24 /* 25 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison 26 */ 27 /* $Header: /var/src/sys/netiso/RCS/clnp_subr.c,v 5.1 89/02/09 16:20:46 hagens Exp $ */ 28 /* $Source: /var/src/sys/netiso/RCS/clnp_subr.c,v $ */ 29 30 #ifndef lint 31 static char *rcsid = "$Header: /var/src/sys/netiso/RCS/clnp_subr.c,v 5.1 89/02/09 16:20:46 hagens Exp $"; 32 #endif lint 33 34 #ifdef ISO 35 36 #include "../h/types.h" 37 #include "../h/param.h" 38 #include "../h/mbuf.h" 39 #include "../h/domain.h" 40 #include "../h/protosw.h" 41 #include "../h/socket.h" 42 #include "../h/socketvar.h" 43 #include "../h/errno.h" 44 #include "../h/time.h" 45 46 #include "../net/if.h" 47 #include "../net/route.h" 48 49 #include "../netiso/iso.h" 50 #include "../netiso/iso_var.h" 51 #include "../netiso/clnp.h" 52 #include "../netiso/clnp_stat.h" 53 #include "../netiso/argo_debug.h" 54 #include "../netiso/iso_snpac.h" 55 56 /* 57 * FUNCTION: clnp_data_ck 58 * 59 * PURPOSE: Check that the amount of data in the mbuf chain is 60 * at least as much as the clnp header would have us 61 * expect. Trim mbufs if longer than expected, drop 62 * packet if shorter than expected. 63 * 64 * RETURNS: success - ptr to mbuf chain 65 * failure - 0 66 * 67 * SIDE EFFECTS: 68 * 69 * NOTES: 70 */ 71 struct mbuf * 72 clnp_data_ck(m, length) 73 register struct mbuf *m; /* ptr to mbuf chain containing hdr & data */ 74 int length; /* length (in bytes) of packet */ 75 { 76 register int len; /* length of data */ 77 register struct mbuf *mhead; /* ptr to head of chain */ 78 79 len = -length; 80 mhead = m; 81 for (;;) { 82 len += m->m_len; 83 if (m->m_next == 0) 84 break; 85 m = m->m_next; 86 } 87 if (len != 0) { 88 if (len < 0) { 89 INCSTAT(cns_toosmall); 90 clnp_discard(mhead, GEN_INCOMPLETE); 91 return 0; 92 } 93 if (len <= m->m_len) 94 m->m_len -= len; 95 else 96 m_adj(mhead, -len); 97 } 98 return mhead; 99 } 100 101 #ifdef ndef 102 /* 103 * FUNCTION: clnp_extract_addr 104 * 105 * PURPOSE: Extract the source and destination address from the 106 * supplied buffer. Place them in the supplied address buffers. 107 * If insufficient data is supplied, then fail. 108 * 109 * RETURNS: success - Address of first byte in the packet past 110 * the address part. 111 * failure - 0 112 * 113 * SIDE EFFECTS: 114 * 115 * NOTES: 116 */ 117 caddr_t 118 clnp_extract_addr(bufp, buflen, srcp, destp) 119 caddr_t bufp; /* ptr to buffer containing addresses */ 120 int buflen; /* length of buffer */ 121 register struct iso_addr *srcp; /* ptr to source address buffer */ 122 register struct iso_addr *destp; /* ptr to destination address buffer */ 123 { 124 int len; /* argument to bcopy */ 125 126 /* 127 * check that we have enough data. Plus1 is for length octet 128 */ 129 if ((u_char)*bufp + 1 > buflen) { 130 return((caddr_t)0); 131 } 132 len = destp->isoa_len = (u_char)*bufp++; 133 (void) bcopy(bufp, (caddr_t)destp, len); 134 buflen -= len; 135 bufp += len; 136 137 /* 138 * check that we have enough data. Plus1 is for length octet 139 */ 140 if ((u_char)*bufp + 1 > buflen) { 141 return((caddr_t)0); 142 } 143 len = srcp->isoa_len = (u_char)* bufp++; 144 (void) bcopy(bufp, (caddr_t)srcp, len); 145 bufp += len; 146 147 /* 148 * Insure that the addresses make sense 149 */ 150 if (iso_ck_addr(srcp) && iso_ck_addr(destp)) 151 return bufp; 152 else 153 return (caddr_t) 0; 154 } 155 #endif ndef 156 157 /* 158 * FUNCTION: clnp_ours 159 * 160 * PURPOSE: Decide whether the supplied packet is destined for 161 * us, or that it should be forwarded on. 162 * 163 * RETURNS: packet is for us - 1 164 * packet is not for us - 0 165 * 166 * SIDE EFFECTS: 167 * 168 * NOTES: 169 */ 170 clnp_ours(dst) 171 register struct iso_addr *dst; /* ptr to destination address */ 172 { 173 register struct iso_ifaddr *ia; /* scan through interface addresses */ 174 175 for (ia = iso_ifaddr; ia; ia = ia->ia_next) { 176 IFDEBUG(D_ROUTE) 177 printf("clnp_ours: ia_sis x%x, dst x%x\n", &IA_SIS(ia)->siso_addr, 178 dst); 179 ENDDEBUG 180 /* PHASE 2: uses iso_addrmatch & mask from iso_ifaddr */ 181 if (iso_addrmatch1(&IA_SIS(ia)->siso_addr, dst)) 182 return 1; 183 } 184 return 0; 185 } 186 187 /* Dec bit set if ifp qlen is greater than congest_threshold */ 188 int congest_threshold = 0; 189 190 /* 191 * FUNCTION: clnp_forward 192 * 193 * PURPOSE: Forward the datagram passed 194 * clnpintr guarantees that the header will be 195 * contigious (a cluster mbuf will be used if necessary). 196 * 197 * If oidx is NULL, no options are present. 198 * 199 * RETURNS: nothing 200 * 201 * SIDE EFFECTS: 202 * 203 * NOTES: 204 */ 205 clnp_forward(m, len, dst, oidx, seg_off, inbound_shp) 206 struct mbuf *m; /* pkt to forward */ 207 int len; /* length of pkt */ 208 struct iso_addr *dst; /* destination address */ 209 struct clnp_optidx *oidx; /* option index */ 210 int seg_off;/* offset of segmentation part */ 211 struct snpa_hdr *inbound_shp; /* subnetwork header of inbound packet */ 212 { 213 struct clnp_fixed *clnp; /* ptr to fixed part of header */ 214 int error; /* return value of route function */ 215 struct sockaddr *next_hop; /* next hop for dgram */ 216 struct ifnet *ifp; /* ptr to outgoing interface */ 217 struct route_iso route; /* filled in by clnp_route */ 218 extern int iso_systype; 219 220 clnp = mtod(m, struct clnp_fixed *); 221 bzero((caddr_t)&route, sizeof(route)); /* MUST be done before "bad:" */ 222 223 /* 224 * Don't forward multicast or broadcast packets 225 */ 226 if ((inbound_shp) && (IS_MULTICAST(inbound_shp->snh_dhost))) { 227 IFDEBUG(D_FORWARD) 228 printf("clnp_forward: dropping multicast packet\n"); 229 ENDDEBUG 230 clnp->cnf_err_ok = 0; /* so we don't generate an ER */ 231 clnp_discard(m, 0); 232 goto done; 233 } 234 235 IFDEBUG(D_FORWARD) 236 printf("clnp_forward: %d bytes, to %s, options x%x\n", len, 237 clnp_iso_addrp(dst), oidx); 238 ENDDEBUG 239 240 /* 241 * Decrement ttl, and if zero drop datagram 242 * Can't compare ttl as less than zero 'cause its a unsigned 243 */ 244 if ((clnp->cnf_ttl == 0) || (--clnp->cnf_ttl == 0)) { 245 IFDEBUG(D_FORWARD) 246 printf("clnp_forward: discarding datagram because ttl is zero\n"); 247 ENDDEBUG 248 INCSTAT(cns_ttlexpired); 249 clnp_discard(m, TTL_EXPTRANSIT); 250 goto done; 251 } 252 253 /* 254 * Route packet; special case for source rt 255 */ 256 if CLNPSRCRT_VALID(oidx) { 257 /* 258 * Update src route first 259 */ 260 clnp_update_srcrt(m, oidx); 261 error = clnp_srcroute(m, oidx, &route, &next_hop, &ifp, dst); 262 } else { 263 error = clnp_route(dst, &route, 0, &next_hop, &ifp); 264 } 265 if (error) { 266 IFDEBUG(D_FORWARD) 267 printf("clnp_forward: can't route packet (errno %d)\n", error); 268 ENDDEBUG 269 clnp_discard(m, ADDR_DESTUNREACH); 270 goto done; 271 } 272 273 IFDEBUG(D_FORWARD) 274 printf("clnp_forward: packet routed to %s\n", 275 clnp_iso_addrp(&((struct sockaddr_iso *)next_hop)->siso_addr)); 276 ENDDEBUG 277 278 INCSTAT(cns_forward); 279 280 /* 281 * If we are an intermediate system and 282 * we are routing outbound on the same ifp that the packet 283 * arrived upon, and we know the next hop snpa, 284 * then generate a redirect request 285 */ 286 if ((iso_systype & SNPA_IS) && (inbound_shp) && 287 (ifp == inbound_shp->snh_ifp)) { 288 struct snpa_cache *sc; 289 290 sc = snpac_look(&((struct sockaddr_iso *)next_hop)->siso_addr); 291 if (sc != NULL) { 292 esis_rdoutput(inbound_shp, m, oidx, dst, sc); 293 } 294 } 295 296 /* 297 * If options are present, update them 298 */ 299 if (oidx) { 300 struct iso_addr *mysrc = 301 clnp_srcaddr(ifp, &((struct sockaddr_iso *)next_hop)->siso_addr); 302 if (mysrc == NULL) { 303 clnp_discard(m, ADDR_DESTUNREACH); 304 goto done; 305 } else { 306 (void) clnp_dooptions(m, oidx, ifp, mysrc); 307 } 308 } 309 310 #ifdef DECBIT 311 if (ifp->if_snd.ifq_len > congest_threshold) { 312 /* 313 * Congestion! Set the Dec Bit and thank Dave Oran 314 */ 315 IFDEBUG(D_FORWARD) 316 printf("clnp_forward: congestion experienced\n"); 317 ENDDEBUG 318 if ((oidx) && (oidx->cni_qos_formatp)) { 319 caddr_t qosp = CLNP_OFFTOOPT(m, oidx->cni_qos_formatp); 320 u_char qos = *qosp; 321 IFDEBUG(D_FORWARD) 322 printf("clnp_forward: setting congestion bit (qos x%x)\n", qos); 323 ENDDEBUG 324 if ((qos & CLNPOVAL_GLOBAL) == CLNPOVAL_GLOBAL) { 325 qos |= CLNPOVAL_CONGESTED; 326 INCSTAT(cns_congest_set); 327 *qosp = qos; 328 } 329 } 330 } 331 #endif DECBIT 332 333 /* 334 * Dispatch the datagram if it is small enough, otherwise fragment 335 */ 336 if (len <= SN_MTU(ifp)) { 337 iso_gen_csum(m, CLNP_CKSUM_OFF, (int)clnp->cnf_hdr_len); 338 (void) (*ifp->if_output)(ifp, m, next_hop); 339 } else { 340 (void) clnp_fragment(ifp, m, next_hop, len, seg_off, /* flags */0); 341 } 342 343 done: 344 /* 345 * Free route 346 */ 347 if (route.ro_rt != NULL) { 348 RTFREE(route.ro_rt); 349 } 350 } 351 352 #ifdef ndef 353 /* 354 * FUNCTION: clnp_insert_addr 355 * 356 * PURPOSE: Insert the address part into a clnp datagram. 357 * 358 * RETURNS: Address of first byte after address part in datagram. 359 * 360 * SIDE EFFECTS: 361 * 362 * NOTES: Assume that there is enough space for the address part. 363 */ 364 caddr_t 365 clnp_insert_addr(bufp, srcp, dstp) 366 caddr_t bufp; /* address of where addr part goes */ 367 register struct iso_addr *srcp; /* ptr to src addr */ 368 register struct iso_addr *dstp; /* ptr to dst addr */ 369 { 370 *bufp++ = dstp->isoa_len; 371 (void) bcopy((caddr_t)dstp, bufp, dstp->isoa_len); 372 bufp += dstp->isoa_len; 373 374 *bufp++ = srcp->isoa_len; 375 (void) bcopy((caddr_t)srcp, bufp, srcp->isoa_len); 376 bufp += srcp->isoa_len; 377 378 return bufp; 379 } 380 381 #endif ndef 382 383 /* 384 * FUNCTION: clnp_route 385 * 386 * PURPOSE: Route a clnp datagram to the first hop toward its 387 * destination. In many cases, the first hop will be 388 * the destination. The address of a route 389 * is specified. If a routing entry is present in 390 * that route, and it is still up to the same destination, 391 * then no further action is necessary. Otherwise, a 392 * new routing entry will be allocated. 393 * 394 * RETURNS: route found - 0 395 * unix error code 396 * 397 * SIDE EFFECTS: 398 * 399 * NOTES: It is up to the caller to free the routing entry 400 * allocated in route. 401 */ 402 clnp_route(dst, ro, flags, first_hop, ifp) 403 struct iso_addr *dst; /* ptr to datagram destination */ 404 struct route_iso *ro; /* existing route structure */ 405 int flags; /* flags for routing */ 406 struct sockaddr **first_hop; /* result: fill in with ptr to firsthop */ 407 struct ifnet **ifp; /* result: fill in with ptr to interface */ 408 { 409 register struct sockaddr_iso *ro_dst; /* ptr to route's destination */ 410 411 ro_dst = (struct sockaddr_iso *)&ro->ro_dst; 412 413 /* 414 * If there is a cached route, check that it is still up and to 415 * the same destination. If not, free it and try again. 416 */ 417 if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || 418 (!iso_addrmatch1(&ro_dst->siso_addr, dst)))) { 419 IFDEBUG(D_ROUTE) 420 printf("clnp_route: freeing old route: ro->ro_rt 0x%x\n", 421 ro->ro_rt); 422 printf("clnp_route: old route refcnt: 0x%x\n", 423 ro->ro_rt->rt_refcnt); 424 ENDDEBUG 425 426 /* free old route entry */ 427 RTFREE(ro->ro_rt); 428 ro->ro_rt = (struct rtentry *)0; 429 } else { 430 IFDEBUG(D_ROUTE) 431 printf("clnp_route: OK route exists\n"); 432 ENDDEBUG 433 } 434 435 if (ro->ro_rt == 0) { 436 /* set up new route structure */ 437 ro_dst->siso_family = AF_ISO; 438 ro_dst->siso_addr = *dst; 439 440 /* allocate new route */ 441 IFDEBUG(D_ROUTE) 442 printf("clnp_route: allocating new route to %s\n", 443 clnp_iso_addrp(dst)); 444 ENDDEBUG 445 rtalloc(ro); 446 } 447 448 if ((ro->ro_rt == 0) || ((*ifp = ro->ro_rt->rt_ifp) == 0)) { 449 return(ENETUNREACH); /* rtalloc failed */ 450 } 451 452 ro->ro_rt->rt_use++; 453 if (ro->ro_rt->rt_flags & (RTF_GATEWAY|RTF_HOST)) 454 *first_hop = &ro->ro_rt->rt_gateway; 455 else 456 *first_hop = (struct sockaddr *)ro_dst; 457 458 return(0); 459 } 460 461 /* 462 * FUNCTION: clnp_srcroute 463 * 464 * PURPOSE: Source route the datagram. If complete source 465 * routing is specified but not possible, then 466 * return an error. If src routing is terminated, then 467 * try routing on destination. 468 * Usage of first_hop, 469 * ifp, and error return is identical to clnp_route. 470 * 471 * RETURNS: 0 or unix error code 472 * 473 * SIDE EFFECTS: 474 * 475 * NOTES: Remember that option index pointers are really 476 * offsets from the beginning of the mbuf. 477 */ 478 clnp_srcroute(options, oidx, route, first_hop, ifp, final_dst) 479 struct mbuf *options; /* ptr to options */ 480 struct clnp_optidx *oidx; /* index to options */ 481 struct route *route; /* route structure */ 482 struct sockaddr **first_hop; /* RETURN: fill in with ptr to firsthop */ 483 struct ifnet **ifp; /* RETURN: fill in with ptr to interface */ 484 struct iso_addr *final_dst; /* final destination */ 485 { 486 struct iso_addr dst; /* first hop specified by src rt */ 487 int error = 0; /* return code */ 488 489 /* 490 * Check if we have run out of routes 491 * If so, then try to route on destination. 492 */ 493 if CLNPSRCRT_TERM(oidx, options) { 494 dst.isoa_len = final_dst->isoa_len; 495 bcopy((caddr_t)final_dst, (caddr_t)&dst, dst.isoa_len); 496 } else { 497 /* 498 * setup dst based on src rt specified 499 */ 500 dst.isoa_len = CLNPSRCRT_CLEN(oidx, options); 501 bcopy(CLNPSRCRT_CADDR(oidx, options), (caddr_t)&dst, dst.isoa_len); 502 } 503 504 /* 505 * try to route it 506 */ 507 error = clnp_route(&dst, route, 0, first_hop, ifp); 508 if (error != 0) 509 return error; 510 511 /* 512 * If complete src rt, first hop must be equal to dst 513 */ 514 if ((CLNPSRCRT_TYPE(oidx, options) == CLNPOVAL_COMPRT) && 515 (!iso_addrmatch1(&(*(struct sockaddr_iso **)first_hop)->siso_addr,&dst))){ 516 IFDEBUG(D_OPTIONS) 517 printf("clnp_srcroute: complete src route failed\n"); 518 ENDDEBUG 519 return EHOSTUNREACH; /* RAH? would like ESRCRTFAILED */ 520 } 521 522 return error; 523 } 524 525 /* 526 * FUNCTION: clnp_srcaddr 527 * 528 * PURPOSE: Build the correct source address for a datagram based on the 529 * outgoing interface and firsthop. The firsthop information 530 * is needed inorder to decide which addr to use if 531 * >1 ISO addr is present for an ifp. 532 * 533 * RETURNS: a ptr to a static copy of the source address. 534 * or NULL 535 * 536 * SIDE EFFECTS: 537 * 538 * NOTES: The ifp must be valid, or we will return NULL 539 */ 540 static struct iso_addr mysrc; 541 struct iso_addr * 542 clnp_srcaddr(ifp, firsthop) 543 struct ifnet *ifp; /* ptr to interface to send packet on */ 544 struct iso_addr *firsthop; /* ptr to first hop for packet */ 545 { 546 register struct iso_ifaddr *ia; /* scan through interface addresses */ 547 struct iso_addr *maybe = NULL; 548 549 for (ia = iso_ifaddr; ia; ia = ia->ia_next) { 550 if (ia->ia_ifp == ifp) { 551 struct iso_addr *isoa = &IA_SIS(ia)->siso_addr; 552 553 IFDEBUG(D_ROUTE) 554 printf("clnp_srcaddr: isoa is %s\n", clnp_iso_addrp(isoa)); 555 ENDDEBUG 556 557 if (iso_eqtype(isoa, firsthop)) { 558 mysrc.isoa_len = isoa->isoa_len; 559 bcopy((caddr_t)isoa, (caddr_t)&mysrc, mysrc.isoa_len); 560 return(&mysrc); 561 } else 562 maybe = isoa; 563 } 564 } 565 566 if (maybe != NULL) { 567 mysrc.isoa_len = maybe->isoa_len; 568 bcopy((caddr_t)maybe, (caddr_t)&mysrc, mysrc.isoa_len); 569 return(&mysrc); 570 } else { 571 /* 572 * This will only happen if there are routes involving 573 * an interface that has just had all iso addresses deleted 574 * from it. This will happen if esisd has added a default 575 * route to an interface, and then the interface was 576 * marked down. As soon as esisd tries to send a pdu on that 577 * interface, it will discover it is down, and remove the 578 * route. Nonetheless, there is a window for this discrepancy, 579 * so we will return null here rather than panicing. 580 */ 581 return(NULL); 582 } 583 } 584 585 /* 586 * FUNCTION: clnp_ypocb - backwards bcopy 587 * 588 * PURPOSE: bcopy starting at end of src rather than beginning. 589 * 590 * RETURNS: none 591 * 592 * SIDE EFFECTS: 593 * 594 * NOTES: No attempt has been made to make this efficient 595 */ 596 clnp_ypocb(from, to, len) 597 caddr_t from; /* src buffer */ 598 caddr_t to; /* dst buffer */ 599 u_int len; /* number of bytes */ 600 { 601 while (len--) 602 *(to + len) = *(from + len); 603 } 604 605 /* 606 * FUNCTION: clnp_hdrsize 607 * 608 * PURPOSE: Return the size of a typical clnp hdr. 609 * 610 * RETURNS: Size of hdr in bytes. 611 * 612 * SIDE EFFECTS: 613 * 614 * NOTES: Assumes segmenting subset. If addrlen is 615 * zero, default to largest nsap address size. 616 */ 617 clnp_hdrsize(addrlen) 618 u_char addrlen; /* length of nsap address */ 619 { 620 if (addrlen == 0) 621 addrlen = 20; 622 623 addrlen++; /* length of address byte */ 624 addrlen *= 2; /* src and dst addresses */ 625 addrlen += sizeof(struct clnp_fixed) + sizeof(struct clnp_segment); 626 627 return(addrlen); 628 } 629 #endif ISO 630