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