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