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.13 (Berkeley) 10/02/91 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 "param.h" 64 #include "socket.h" 65 #include "socketvar.h" 66 #include "domain.h" 67 #include "malloc.h" 68 #include "mbuf.h" 69 #include "errno.h" 70 #include "time.h" 71 #include "protosw.h" 72 73 #include "../net/if.h" 74 #include "../net/route.h" 75 76 #include "argo_debug.h" 77 #include "tp_param.h" 78 #include "tp_stat.h" 79 #include "tp_pcb.h" 80 #include "tp_trace.h" 81 #include "tp_stat.h" 82 #include "tp_tpdu.h" 83 #include "tp_clnp.h" 84 #include "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 int s = splnet(); 443 struct mbuf *tp_inputprep(); 444 int tp_input(), cltp_input(), (*input)() = tp_input; 445 446 IncStat(ts_pkt_rcvd); 447 448 IFDEBUG(D_TPINPUT) 449 printf("tpclnp_input: m 0x%x clnp_len 0x%x\n", m, clnp_len); 450 dump_mbuf(m, "at tpclnp_input"); 451 ENDDEBUG 452 /* 453 * CLNP gives us an mbuf chain WITH the clnp header pulled up, 454 * and the length of the clnp header. 455 * First, strip off the Clnp header. leave the mbuf there for the 456 * pullup that follows. 457 */ 458 459 m->m_len -= clnp_len; 460 m->m_data += clnp_len; 461 462 m = tp_inputprep(m); 463 if (m == 0) 464 return 0; 465 if (mtod(m, u_char *)[1] == UD_TPDU_type) 466 input = cltp_input; 467 468 IFDEBUG(D_TPINPUT) 469 dump_mbuf(m, "after tpclnp_input both pullups"); 470 ENDDEBUG 471 472 IFDEBUG(D_TPISO) 473 printf("calling %sinput : src 0x%x, dst 0x%x, src addr:\n", 474 (input == tp_input ? "tp_" : "clts_"), src, dst); 475 dump_isoaddr(src); 476 printf(" dst addr:\n"); 477 dump_isoaddr(dst); 478 ENDDEBUG 479 480 (void) (*input)(m, (struct sockaddr *)src, (struct sockaddr *)dst, 481 0, tpclnp_output_dg, ce_bit); 482 483 IFDEBUG(D_QUENCH) 484 { 485 if(time.tv_usec & 0x4 && time.tv_usec & 0x40) { 486 printf("tpclnp_input: FAKING %s\n", 487 tp_stat.ts_pkt_rcvd & 0x1?"QUENCH":"QUENCH2"); 488 if(tp_stat.ts_pkt_rcvd & 0x1) { 489 tpclnp_ctlinput(PRC_QUENCH, &src); 490 } else { 491 tpclnp_ctlinput(PRC_QUENCH2, &src); 492 } 493 } 494 } 495 ENDDEBUG 496 497 splx(s); 498 return 0; 499 } 500 501 ProtoHook 502 iso_rtchange() 503 { 504 return 0; 505 } 506 507 /* 508 * CALLED FROM: 509 * tpclnp_ctlinput() 510 * FUNCTION and ARGUMENTS: 511 * find the tpcb pointer and pass it to tp_quench 512 */ 513 void 514 tpiso_decbit(isop) 515 struct isopcb *isop; 516 { 517 tp_quench((struct tp_pcb *)isop->isop_socket->so_pcb, PRC_QUENCH2); 518 } 519 /* 520 * CALLED FROM: 521 * tpclnp_ctlinput() 522 * FUNCTION and ARGUMENTS: 523 * find the tpcb pointer and pass it to tp_quench 524 */ 525 void 526 tpiso_quench(isop) 527 struct isopcb *isop; 528 { 529 tp_quench((struct tp_pcb *)isop->isop_socket->so_pcb, PRC_QUENCH); 530 } 531 532 /* 533 * CALLED FROM: 534 * The network layer through the protosw table. 535 * FUNCTION and ARGUMENTS: 536 * When clnp an ICMP-like msg this gets called. 537 * It either returns an error status to the user or 538 * it causes all connections on this address to be aborted 539 * by calling the appropriate xx_notify() routine. 540 * (cmd) is the type of ICMP error. 541 * (siso) is the address of the guy who sent the ER CLNPDU 542 */ 543 ProtoHook 544 tpclnp_ctlinput(cmd, siso) 545 int cmd; 546 struct sockaddr_iso *siso; 547 { 548 extern u_char inetctlerrmap[]; 549 extern ProtoHook tpiso_abort(); 550 extern ProtoHook iso_rtchange(); 551 extern ProtoHook tpiso_reset(); 552 void iso_pcbnotify(); 553 554 IFDEBUG(D_TPINPUT) 555 printf("tpclnp_ctlinput1: cmd 0x%x addr: \n", cmd); 556 dump_isoaddr(siso); 557 ENDDEBUG 558 559 if (cmd < 0 || cmd > PRC_NCMDS) 560 return 0; 561 if (siso->siso_family != AF_ISO) 562 return 0; 563 switch (cmd) { 564 565 case PRC_QUENCH2: 566 iso_pcbnotify(&tp_isopcb, siso, 0, (int (*)())tpiso_decbit); 567 break; 568 569 case PRC_QUENCH: 570 iso_pcbnotify(&tp_isopcb, siso, 0, (int (*)())tpiso_quench); 571 break; 572 573 case PRC_TIMXCEED_REASS: 574 case PRC_ROUTEDEAD: 575 iso_pcbnotify(&tp_isopcb, siso, 0, tpiso_reset); 576 break; 577 578 case PRC_HOSTUNREACH: 579 case PRC_UNREACH_NET: 580 case PRC_IFDOWN: 581 case PRC_HOSTDEAD: 582 iso_pcbnotify(&tp_isopcb, siso, 583 (int)inetctlerrmap[cmd], iso_rtchange); 584 break; 585 586 default: 587 /* 588 case PRC_MSGSIZE: 589 case PRC_UNREACH_HOST: 590 case PRC_UNREACH_PROTOCOL: 591 case PRC_UNREACH_PORT: 592 case PRC_UNREACH_NEEDFRAG: 593 case PRC_UNREACH_SRCFAIL: 594 case PRC_REDIRECT_NET: 595 case PRC_REDIRECT_HOST: 596 case PRC_REDIRECT_TOSNET: 597 case PRC_REDIRECT_TOSHOST: 598 case PRC_TIMXCEED_INTRANS: 599 case PRC_PARAMPROB: 600 */ 601 iso_pcbnotify(&tp_isopcb, siso, (int)inetctlerrmap[cmd], tpiso_abort); 602 break; 603 } 604 return 0; 605 } 606 /* 607 * XXX - Variant which is called by clnp_er.c with an isoaddr rather 608 * than a sockaddr_iso. 609 */ 610 611 static struct sockaddr_iso siso = {sizeof(siso), AF_ISO}; 612 tpclnp_ctlinput1(cmd, isoa) 613 int cmd; 614 struct iso_addr *isoa; 615 { 616 bzero((caddr_t)&siso.siso_addr, sizeof(siso.siso_addr)); 617 bcopy((caddr_t)isoa, (caddr_t)&siso.siso_addr, isoa->isoa_len); 618 tpclnp_ctlinput(cmd, &siso); 619 } 620 621 /* 622 * These next 2 routines are 623 * CALLED FROM: 624 * xxx_notify() from tp_ctlinput() when 625 * net level gets some ICMP-equiv. type event. 626 * FUNCTION and ARGUMENTS: 627 * Cause the connection to be aborted with some sort of error 628 * reason indicating that the network layer caused the abort. 629 * Fakes an ER TPDU so we can go through the driver. 630 * abort always aborts the TP connection. 631 * reset may or may not, depending on the TP class that's in use. 632 */ 633 ProtoHook 634 tpiso_abort(isop) 635 struct isopcb *isop; 636 { 637 struct tp_event e; 638 639 IFDEBUG(D_CONN) 640 printf("tpiso_abort 0x%x\n", isop); 641 ENDDEBUG 642 e.ev_number = ER_TPDU; 643 e.ATTR(ER_TPDU).e_reason = ECONNABORTED; 644 return tp_driver((struct tp_pcb *)isop->isop_socket->so_pcb, &e); 645 } 646 647 ProtoHook 648 tpiso_reset(isop) 649 struct isopcb *isop; 650 { 651 struct tp_event e; 652 653 e.ev_number = T_NETRESET; 654 return tp_driver((struct tp_pcb *)isop->isop_socket->so_pcb, &e); 655 656 } 657 658 #endif ISO 659