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 /* 28 * ARGO TP 29 * $Header: /var/src/sys/netiso/RCS/tp_iso.c,v 5.1 89/02/09 16:20:51 hagens Exp $ 30 * $Source: /var/src/sys/netiso/RCS/tp_iso.c,v $ 31 * @(#)tp_iso.c 7.3 (Berkeley) 08/29/89 * 32 * 33 * Here is where you find the iso-dependent code. We've tried 34 * keep all net-level and (primarily) address-family-dependent stuff 35 * out of the tp source, and everthing here is reached indirectly 36 * through a switch table (struct nl_protosw *) tpcb->tp_nlproto 37 * (see tp_pcb.c). 38 * The routines here are: 39 * iso_getsufx: gets transport suffix out of an isopcb structure. 40 * iso_putsufx: put transport suffix into an isopcb structure. 41 * iso_putnetaddr: put a whole net addr into an isopcb. 42 * iso_getnetaddr: get a whole net addr from an isopcb. 43 * iso_recycle_suffix: clear suffix for reuse in isopcb 44 * tpclnp_ctlinput: handle ER CNLPdu : icmp-like stuff 45 * tpclnp_mtu: figure out what size tpdu to use 46 * tpclnp_input: take a pkt from clnp, strip off its clnp header, 47 * give to tp 48 * tpclnp_output_dg: package a pkt for clnp given 2 addresses & some data 49 * tpclnp_output: package a pkt for clnp given an isopcb & some data 50 */ 51 52 #ifndef lint 53 static char *rcsid = "$Header: /var/src/sys/netiso/RCS/tp_iso.c,v 5.1 89/02/09 16:20:51 hagens Exp $"; 54 #endif lint 55 56 #ifdef ISO 57 58 #include "param.h" 59 #include "socket.h" 60 #include "socketvar.h" 61 #include "domain.h" 62 #include "malloc.h" 63 #include "mbuf.h" 64 #include "errno.h" 65 #include "time.h" 66 #include "protosw.h" 67 68 #include "../net/if.h" 69 #include "../net/route.h" 70 71 #include "argo_debug.h" 72 #include "tp_param.h" 73 #include "tp_stat.h" 74 #include "tp_pcb.h" 75 #include "tp_trace.h" 76 #include "tp_stat.h" 77 #include "tp_tpdu.h" 78 #include "tp_clnp.h" 79 80 /* 81 * CALLED FROM: 82 * pr_usrreq() on PRU_BIND, PRU_CONNECT, PRU_ACCEPT, and PRU_PEERADDR 83 * FUNCTION, ARGUMENTS: 84 * The argument (which) takes the value TP_LOCAL or TP_FOREIGN. 85 */ 86 87 iso_getsufx(isop, lenp, data_out, which) 88 struct isopcb *isop; 89 u_short *lenp; 90 caddr_t data_out; 91 int which; 92 { 93 register struct sockaddr_iso *addr = 0; 94 95 switch (which) { 96 case TP_LOCAL: 97 addr = isop->isop_laddr; 98 break; 99 100 case TP_FOREIGN: 101 addr = isop->isop_faddr; 102 } 103 if (addr) 104 bcopy(TSEL(addr), data_out, (*lenp = addr->siso_tlen)); 105 } 106 107 /* CALLED FROM: 108 * tp_newsocket(); i.e., when a connection is being established by an 109 * incoming CR_TPDU. 110 * 111 * FUNCTION, ARGUMENTS: 112 * Put a transport suffix (found in name) into an isopcb structure (isop). 113 * The argument (which) takes the value TP_LOCAL or TP_FOREIGN. 114 */ 115 void 116 iso_putsufx(isop, sufxloc, sufxlen, which) 117 struct isopcb *isop; 118 caddr_t sufxloc; 119 int sufxlen, which; 120 { 121 struct sockaddr_iso **dst, *backup; 122 register struct sockaddr_iso *addr; 123 struct mbuf *m; 124 int len; 125 126 switch (which) { 127 default: 128 return; 129 130 case TP_LOCAL: 131 dst = &isop->isop_laddr; 132 backup = &isop->isop_sladdr; 133 break; 134 135 case TP_FOREIGN: 136 dst = &isop->isop_faddr; 137 backup = &isop->isop_sfaddr; 138 } 139 if ((addr = *dst) == 0) { 140 addr = *dst = backup; 141 addr->siso_nlen = 0; 142 addr->siso_slen = 0; 143 addr->siso_plen = 0; 144 printf("iso_putsufx on un-initialized isopcb\n"); 145 } 146 len = sufxlen + addr->siso_nlen + 147 (sizeof(*addr) - sizeof(addr->siso_data)); 148 if (addr == backup) { 149 if (len > sizeof(*addr)) { 150 m = m_getclr(M_DONTWAIT, MT_SONAME); 151 if (m == 0) 152 return; 153 addr = *dst = mtod(m, struct sockaddr_iso *); 154 *addr = *backup; 155 m->m_len = len; 156 } 157 } 158 bcopy(sufxloc, TSEL(addr), sufxlen); 159 addr->siso_tlen = sufxlen; 160 addr->siso_len = len; 161 } 162 163 /* 164 * CALLED FROM: 165 * tp.trans whenever we go into REFWAIT state. 166 * FUNCTION and ARGUMENT: 167 * Called when a ref is frozen, to allow the suffix to be reused. 168 * (isop) is the net level pcb. This really shouldn't have to be 169 * done in a NET level pcb but... for the internet world that just 170 * the way it is done in BSD... 171 * The alternative is to have the port unusable until the reference 172 * timer goes off. 173 */ 174 void 175 iso_recycle_tsuffix(isop) 176 struct isopcb *isop; 177 { 178 isop->isop_laddr->siso_tlen = isop->isop_faddr->siso_tlen = 0; 179 } 180 181 /* 182 * CALLED FROM: 183 * tp_newsocket(); i.e., when a connection is being established by an 184 * incoming CR_TPDU. 185 * 186 * FUNCTION and ARGUMENTS: 187 * Copy a whole net addr from a struct sockaddr (name). 188 * into an isopcb (isop). 189 * The argument (which) takes values TP_LOCAL or TP_FOREIGN 190 */ 191 void 192 iso_putnetaddr(isop, name, which) 193 register struct isopcb *isop; 194 struct sockaddr_iso *name; 195 int which; 196 { 197 struct sockaddr_iso **sisop, *backup; 198 register struct sockaddr_iso *siso; 199 200 switch (which) { 201 case TP_LOCAL: 202 sisop = &isop->isop_laddr; 203 backup = &isop->isop_sladdr; 204 break; 205 case TP_FOREIGN: 206 sisop = &isop->isop_faddr; 207 backup = &isop->isop_sfaddr; 208 } 209 siso = ((*sisop == 0) ? (*sisop = backup) : *sisop); 210 IFDEBUG(D_TPISO) 211 printf("ISO_PUTNETADDR\n"); 212 dump_isoaddr(isop->isop_faddr); 213 ENDDEBUG 214 siso->siso_addr = name->siso_addr; 215 } 216 217 /* 218 * CALLED FROM: 219 * pr_usrreq() PRU_SOCKADDR, PRU_ACCEPT, PRU_PEERADDR 220 * FUNCTION and ARGUMENTS: 221 * Copy a whole net addr from an isopcb (isop) into 222 * a struct sockaddr (name). 223 * The argument (which) takes values TP_LOCAL or TP_FOREIGN. 224 */ 225 226 void 227 iso_getnetaddr( isop, name, which) 228 struct isopcb *isop; 229 struct mbuf *name; 230 int which; 231 { 232 struct sockaddr_iso *siso = 233 (which == TP_LOCAL ? isop->isop_laddr : isop->isop_faddr); 234 if (siso) 235 bcopy((caddr_t)siso, mtod(name, caddr_t), 236 (unsigned)(name->m_len = siso->siso_len)); 237 else 238 name->m_len = 0; 239 } 240 241 /* 242 * CALLED FROM: 243 * tp_input() on incoming CR, CC, and pr_usrreq() for PRU_CONNECT 244 * FUNCTION, ARGUMENTS, SIDE EFFECTS and RETURN VALUE: 245 * Determine the proper maximum transmission unit, i.e., MTU, to use, given 246 * a) the header size for the network protocol and the max transmission 247 * unit on the subnet interface, determined from the information in (isop), 248 * b) the max size negotiated so far (negot) 249 * c) the window size used by the tp connection (found in so), 250 * 251 * The result is put in the integer *size in its integer form and in 252 * *negot in its logarithmic form. 253 * 254 * The rules are: 255 * a) can only negotiate down from the value found in *negot. 256 * b) the MTU must be < the windowsize, 257 * c) If src and dest are on the same net, 258 * we will negotiate the closest size larger than MTU but really USE 259 * the actual device mtu - ll hdr sizes. 260 * otherwise we negotiate the closest size smaller than MTU - ll hdr sizes. 261 */ 262 263 void 264 tpclnp_mtu(so, isop, size, negot ) 265 struct socket *so; 266 struct isopcb *isop; 267 int *size; 268 u_char *negot; 269 { 270 struct ifnet *ifp; 271 struct iso_ifaddr *ia; 272 register int i; 273 int windowsize = so->so_rcv.sb_hiwat; 274 int clnp_size; 275 int sizeismtu = 0; 276 277 struct iso_ifaddr *iso_routeifa(); 278 279 IFDEBUG(D_CONN) 280 printf("tpclnp_mtu(0x%x,0x%x,0x%x,0x%x)\n", so, isop, size, negot); 281 ENDDEBUG 282 IFTRACE(D_CONN) 283 tptrace(TPPTmisc, "ENTER GET MTU: size negot \n",*size, *negot, 0, 0); 284 ENDTRACE 285 286 *size = 1 << *negot; 287 288 if( *size > windowsize ) { 289 *size = windowsize; 290 } 291 292 if (((ia = iso_routeifa(isop->isop_faddr)) == 0) 293 || (ifp = ia->ia_ifp) == 0) 294 return; 295 296 /* TODO - make this indirect off the socket structure to the 297 * network layer to get headersize 298 */ 299 if (isop->isop_laddr) 300 clnp_size = clnp_hdrsize(isop->isop_laddr->siso_addr.isoa_len); 301 else 302 clnp_size = 20; 303 304 if(*size > ifp->if_mtu - clnp_size) { 305 *size = ifp->if_mtu - clnp_size; 306 sizeismtu = 1; 307 } 308 /* have to transform size to the log2 of size */ 309 for(i=TP_MIN_TPDUSIZE; (i<TP_MAX_TPDUSIZE && ((1<<i) <= *size)) ; i++) 310 ; 311 i--; 312 313 IFTRACE(D_CONN) 314 tptrace(TPPTmisc, "GET MTU MID: tpcb size negot i \n", 315 *size, *negot, i, 0); 316 ENDTRACE 317 318 /* are we on the same LAN? if so, negotiate one tpdu size larger, 319 * and actually send the real mtu size 320 */ 321 if (iso_localifa(isop->isop_faddr)) { 322 i++; 323 } else { 324 *size = 1<<i; 325 } 326 *negot = i; 327 328 IFDEBUG(D_CONN) 329 printf("GET MTU RETURNS: ifp %s size 0x%x negot 0x%x\n", 330 ifp->if_name, *size, *negot); 331 ENDDEBUG 332 IFTRACE(D_CONN) 333 tptrace(TPPTmisc, "EXIT GET MTU: tpcb size negot \n", 334 *size, *negot, 0, 0); 335 ENDTRACE 336 } 337 338 339 /* 340 * CALLED FROM: 341 * tp_emit() 342 * FUNCTION and ARGUMENTS: 343 * Take a packet(m0) from tp and package it so that clnp will accept it. 344 * This means prepending space for the clnp header and filling in a few 345 * of the fields. 346 * inp is the isopcb structure; datalen is the length of the data in the 347 * mbuf string m0. 348 * RETURN VALUE: 349 * whatever (E*) is returned form the net layer output routine. 350 */ 351 352 int 353 tpclnp_output(isop, m0, datalen, nochksum) 354 struct isopcb *isop; 355 struct mbuf *m0; 356 int datalen; 357 int nochksum; 358 { 359 register struct mbuf *m = m0; 360 IncStat(ts_tpdu_sent); 361 362 IFDEBUG(D_TPISO) 363 struct tpdu *hdr = mtod(m0, struct tpdu *); 364 365 printf( 366 "abt to call clnp_output: datalen 0x%x, hdr.li 0x%x, hdr.dutype 0x%x nocsum x%x dst addr:\n", 367 datalen, 368 (int)hdr->tpdu_li, (int)hdr->tpdu_type, nochksum); 369 dump_isoaddr(isop->isop_faddr); 370 printf("\nsrc addr:\n"); 371 dump_isoaddr(isop->isop_laddr); 372 dump_mbuf(m0, "at tpclnp_output"); 373 ENDDEBUG 374 375 return 376 clnp_output(m0, isop, datalen, /* flags */nochksum ? CLNP_NO_CKSUM : 0); 377 } 378 379 /* 380 * CALLED FROM: 381 * tp_error_emit() 382 * FUNCTION and ARGUMENTS: 383 * This is a copy of tpclnp_output that takes the addresses 384 * instead of a pcb. It's used by the tp_error_emit, when we 385 * don't have an iso_pcb with which to call the normal output rtn. 386 * RETURN VALUE: 387 * ENOBUFS or 388 * whatever (E*) is returned form the net layer output routine. 389 */ 390 391 int 392 tpclnp_output_dg(laddr, faddr, m0, datalen, ro, nochksum) 393 struct iso_addr *laddr, *faddr; 394 struct mbuf *m0; 395 int datalen; 396 struct route *ro; 397 int nochksum; 398 { 399 struct isopcb tmppcb; 400 int err; 401 int flags; 402 register struct mbuf *m = m0; 403 404 IFDEBUG(D_TPISO) 405 printf("tpclnp_output_dg datalen 0x%x m0 0x%x\n", datalen, m0); 406 ENDDEBUG 407 408 /* 409 * Fill in minimal portion of isopcb so that clnp can send the 410 * packet. 411 */ 412 bzero((caddr_t)&tmppcb, sizeof(tmppcb)); 413 tmppcb.isop_laddr = &tmppcb.isop_sladdr; 414 tmppcb.isop_laddr->siso_addr = *laddr; 415 tmppcb.isop_faddr = &tmppcb.isop_sfaddr; 416 tmppcb.isop_faddr->siso_addr = *faddr; 417 418 IFDEBUG(D_TPISO) 419 printf("tpclnp_output_dg faddr: \n"); 420 dump_isoaddr(&tmppcb.isop_sfaddr); 421 printf("\ntpclnp_output_dg laddr: \n"); 422 dump_isoaddr(&tmppcb.isop_sladdr); 423 printf("\n"); 424 ENDDEBUG 425 426 /* 427 * Do not use packet cache since this is a one shot error packet 428 */ 429 flags = (CLNP_NOCACHE|(nochksum?CLNP_NO_CKSUM:0)); 430 431 IncStat(ts_tpdu_sent); 432 433 err = clnp_output(m0, &tmppcb, datalen, flags); 434 435 /* 436 * Free route allocated by clnp (if the route was indeed allocated) 437 */ 438 if (tmppcb.isop_route.ro_rt) 439 RTFREE(tmppcb.isop_route.ro_rt); 440 441 return(err); 442 } 443 extern struct sockaddr_iso blank_siso; 444 /* 445 * CALLED FROM: 446 * clnp's input routine, indirectly through the protosw. 447 * FUNCTION and ARGUMENTS: 448 * Take a packet (m) from clnp, strip off the clnp header and give it to tp 449 * No return value. 450 */ 451 ProtoHook 452 tpclnp_input(m, faddr, laddr, clnp_len) 453 struct mbuf *m; 454 struct iso_addr *faddr, *laddr; 455 int clnp_len; 456 { 457 struct sockaddr_iso src, dst; 458 int s = splnet(); 459 struct mbuf *tp_inputprep(); 460 461 IncStat(ts_pkt_rcvd); 462 463 IFDEBUG(D_TPINPUT) 464 printf("tpclnp_input: m 0x%x clnp_len 0x%x\n", m, clnp_len); 465 dump_mbuf(m, "at tpclnp_input"); 466 ENDDEBUG 467 /* 468 * CLNP gives us an mbuf chain WITH the clnp header pulled up, 469 * and the length of the clnp header. 470 * First, strip off the Clnp header. leave the mbuf there for the 471 * pullup that follows. 472 */ 473 474 m->m_len -= clnp_len; 475 m->m_data += clnp_len; 476 477 m = tp_inputprep(m); 478 479 IFDEBUG(D_TPINPUT) 480 dump_mbuf(m, "after tpclnp_input both pullups"); 481 ENDDEBUG 482 483 src = blank_siso; dst = blank_siso; 484 bcopy((caddr_t)faddr, (caddr_t)&src.siso_addr, 1 + faddr->isoa_len); 485 bcopy((caddr_t)laddr, (caddr_t)&dst.siso_addr, 1 + laddr->isoa_len); 486 487 IFDEBUG(D_TPISO) 488 printf("calling tp_input: &src 0x%x &dst 0x%x, src addr:\n", 489 &src, &dst); 490 printf(" dst addr:\n"); 491 dump_isoaddr(&src); 492 dump_isoaddr(&dst); 493 ENDDEBUG 494 495 (void) tp_input(m, (struct sockaddr *)&src, (struct sockaddr *)&dst, 496 0, tpclnp_output_dg); 497 498 IFDEBUG(D_QUENCH) 499 { 500 if(time.tv_usec & 0x4 && time.tv_usec & 0x40) { 501 printf("tpclnp_input: FAKING %s\n", 502 tp_stat.ts_pkt_rcvd & 0x1?"QUENCH":"QUENCH2"); 503 if(tp_stat.ts_pkt_rcvd & 0x1) { 504 tpclnp_ctlinput(PRC_QUENCH, &src); 505 } else { 506 tpclnp_ctlinput(PRC_QUENCH2, &src); 507 } 508 } 509 } 510 ENDDEBUG 511 512 splx(s); 513 return 0; 514 } 515 516 ProtoHook 517 iso_rtchange() 518 { 519 return 0; 520 } 521 522 /* 523 * CALLED FROM: 524 * tpclnp_ctlinput() 525 * FUNCTION and ARGUMENTS: 526 * find the tpcb pointer and pass it to tp_quench 527 */ 528 void 529 tpiso_decbit(isop) 530 struct isopcb *isop; 531 { 532 tp_quench((struct tp_pcb *)isop->isop_socket->so_tpcb, PRC_QUENCH2); 533 } 534 /* 535 * CALLED FROM: 536 * tpclnp_ctlinput() 537 * FUNCTION and ARGUMENTS: 538 * find the tpcb pointer and pass it to tp_quench 539 */ 540 void 541 tpiso_quench(isop) 542 struct isopcb *isop; 543 { 544 tp_quench((struct tp_pcb *)isop->isop_socket->so_tpcb, PRC_QUENCH); 545 } 546 547 /* 548 * CALLED FROM: 549 * The network layer through the protosw table. 550 * FUNCTION and ARGUMENTS: 551 * When clnp an ICMP-like msg this gets called. 552 * It either returns an error status to the user or 553 * it causes all connections on this address to be aborted 554 * by calling the appropriate xx_notify() routine. 555 * (cmd) is the type of ICMP error. 556 * (siso) is the address of the guy who sent the ER CLNPDU 557 */ 558 ProtoHook 559 tpclnp_ctlinput(cmd, siso) 560 int cmd; 561 struct sockaddr_iso *siso; 562 { 563 return tpclnp_ctlinput1(cmd, &siso->siso_addr); 564 } 565 566 /* 567 * Entry to ctlinput with argument of an iso_addr rather than a sockaddr 568 */ 569 ProtoHook 570 tpclnp_ctlinput1(cmd, isoa) 571 int cmd; 572 struct iso_addr *isoa; 573 { 574 extern u_char inetctlerrmap[]; 575 extern ProtoHook tpiso_abort(); 576 extern ProtoHook iso_rtchange(); 577 extern ProtoHook tpiso_reset(); 578 void iso_pcbnotify(); 579 580 IFDEBUG(D_TPINPUT) 581 printf("tpclnp_ctlinput1: cmd 0x%x addr: %s\n", cmd, 582 clnp_iso_addrp(isoa)); 583 ENDDEBUG 584 585 if (cmd < 0 || cmd > PRC_NCMDS) 586 return 0; 587 switch (cmd) { 588 589 case PRC_QUENCH2: 590 iso_pcbnotify(&tp_isopcb, isoa, 0, (int (*)())tpiso_decbit); 591 break; 592 593 case PRC_QUENCH: 594 iso_pcbnotify(&tp_isopcb, isoa, 0, (int (*)())tpiso_quench); 595 break; 596 597 case PRC_TIMXCEED_REASS: 598 case PRC_ROUTEDEAD: 599 iso_pcbnotify(&tp_isopcb, isoa, 0, tpiso_reset); 600 break; 601 602 case PRC_HOSTUNREACH: 603 case PRC_UNREACH_NET: 604 case PRC_IFDOWN: 605 case PRC_HOSTDEAD: 606 iso_pcbnotify(&tp_isopcb, isoa, 607 (int)inetctlerrmap[cmd], iso_rtchange); 608 break; 609 610 default: 611 /* 612 case PRC_MSGSIZE: 613 case PRC_UNREACH_HOST: 614 case PRC_UNREACH_PROTOCOL: 615 case PRC_UNREACH_PORT: 616 case PRC_UNREACH_NEEDFRAG: 617 case PRC_UNREACH_SRCFAIL: 618 case PRC_REDIRECT_NET: 619 case PRC_REDIRECT_HOST: 620 case PRC_REDIRECT_TOSNET: 621 case PRC_REDIRECT_TOSHOST: 622 case PRC_TIMXCEED_INTRANS: 623 case PRC_PARAMPROB: 624 */ 625 iso_pcbnotify(&tp_isopcb, isoa, (int)inetctlerrmap[cmd], tpiso_abort); 626 break; 627 } 628 return 0; 629 } 630 631 /* 632 * These next 2 routines are 633 * CALLED FROM: 634 * xxx_notify() from tp_ctlinput() when 635 * net level gets some ICMP-equiv. type event. 636 * FUNCTION and ARGUMENTS: 637 * Cause the connection to be aborted with some sort of error 638 * reason indicating that the network layer caused the abort. 639 * Fakes an ER TPDU so we can go through the driver. 640 * abort always aborts the TP connection. 641 * reset may or may not, depending on the TP class that's in use. 642 */ 643 ProtoHook 644 tpiso_abort(isop) 645 struct isopcb *isop; 646 { 647 struct tp_event e; 648 649 IFDEBUG(D_CONN) 650 printf("tpiso_abort 0x%x\n", isop); 651 ENDDEBUG 652 e.ev_number = ER_TPDU; 653 e.ATTR(ER_TPDU).e_reason = ECONNABORTED; 654 return tp_driver((struct tp_pcb *)isop->isop_socket->so_tpcb, &e); 655 } 656 657 ProtoHook 658 tpiso_reset(isop) 659 struct isopcb *isop; 660 { 661 struct tp_event e; 662 663 e.ev_number = T_NETRESET; 664 return tp_driver((struct tp_pcb *)isop->isop_socket->so_tpcb, &e); 665 666 } 667 668 #endif ISO 669