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_inet.c 7.11 (Berkeley) 10/11/92 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: tp_inet.c,v 5.3 88/11/18 17:27:29 nhall Exp $ 39 * $Source: /usr/argo/sys/netiso/RCS/tp_inet.c,v $ 40 * 41 * Here is where you find the inet-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 * in_getsufx: gets transport suffix out of an inpcb structure. 48 * in_putsufx: put transport suffix into an inpcb structure. 49 * in_putnetaddr: put a whole net addr into an inpcb. 50 * in_getnetaddr: get a whole net addr from an inpcb. 51 * in_cmpnetaddr: compare a whole net addr from an isopcb. 52 * in_recycle_suffix: clear suffix for reuse in inpcb 53 * tpip_mtu: figure out what size tpdu to use 54 * tpip_input: take a pkt from ip, strip off its ip header, give to tp 55 * tpip_output_dg: package a pkt for ip given 2 addresses & some data 56 * tpip_output: package a pkt for ip given an inpcb & some data 57 */ 58 59 #ifdef INET 60 61 #include <sys/param.h> 62 #include <sys/socket.h> 63 #include <sys/socketvar.h> 64 #include <sys/mbuf.h> 65 #include <sys/errno.h> 66 #include <sys/time.h> 67 68 #include <net/if.h> 69 70 #include <netiso/tp_param.h> 71 #include <netiso/argo_debug.h> 72 #include <netiso/tp_stat.h> 73 #include <netiso/tp_ip.h> 74 #include <netiso/tp_pcb.h> 75 #include <netiso/tp_trace.h> 76 #include <netiso/tp_stat.h> 77 #include <netiso/tp_tpdu.h> 78 #include <netinet/in_var.h> 79 80 #ifndef ISO 81 #include <netiso/iso_chksum.c> 82 #endif 83 84 /* 85 * NAME: in_getsufx() 86 87 * CALLED FROM: pr_usrreq() on PRU_BIND, 88 * PRU_CONNECT, PRU_ACCEPT, and PRU_PEERADDR 89 * 90 * FUNCTION, ARGUMENTS, and RETURN VALUE: 91 * Get a transport suffix from an inpcb structure (inp). 92 * The argument (which) takes the value TP_LOCAL or TP_FOREIGN. 93 * 94 * RETURNS: internet port / transport suffix 95 * (CAST TO AN INT) 96 * 97 * SIDE EFFECTS: 98 * 99 * NOTES: 100 */ 101 in_getsufx(inp, lenp, data_out, which) 102 struct inpcb *inp; 103 u_short *lenp; 104 caddr_t data_out; 105 int which; 106 { 107 *lenp = sizeof(u_short); 108 switch (which) { 109 case TP_LOCAL: 110 *(u_short *)data_out = inp->inp_lport; 111 return; 112 113 case TP_FOREIGN: 114 *(u_short *)data_out = inp->inp_fport; 115 } 116 117 } 118 119 /* 120 * NAME: in_putsufx() 121 * 122 * CALLED FROM: tp_newsocket(); i.e., when a connection 123 * is being established by an incoming CR_TPDU. 124 * 125 * FUNCTION, ARGUMENTS: 126 * Put a transport suffix (found in name) into an inpcb structure (inp). 127 * The argument (which) takes the value TP_LOCAL or TP_FOREIGN. 128 * 129 * RETURNS: Nada 130 * 131 * SIDE EFFECTS: 132 * 133 * NOTES: 134 */ 135 /*ARGSUSED*/ 136 void 137 in_putsufx(inp, sufxloc, sufxlen, which) 138 struct inpcb *inp; 139 caddr_t sufxloc; 140 int which; 141 { 142 if (which == TP_FOREIGN) { 143 bcopy(sufxloc, (caddr_t)&inp->inp_fport, sizeof(inp->inp_fport)); 144 } 145 } 146 147 /* 148 * NAME: in_recycle_tsuffix() 149 * 150 * CALLED FROM: tp.trans whenever we go into REFWAIT state. 151 * 152 * FUNCTION and ARGUMENT: 153 * Called when a ref is frozen, to allow the suffix to be reused. 154 * (inp) is the net level pcb. 155 * 156 * RETURNS: Nada 157 * 158 * SIDE EFFECTS: 159 * 160 * NOTES: This really shouldn't have to be done in a NET level pcb 161 * but... for the internet world that just the way it is done in BSD... 162 * The alternative is to have the port unusable until the reference 163 * timer goes off. 164 */ 165 void 166 in_recycle_tsuffix(inp) 167 struct inpcb *inp; 168 { 169 inp->inp_fport = inp->inp_lport = 0; 170 } 171 172 /* 173 * NAME: in_putnetaddr() 174 * 175 * CALLED FROM: 176 * tp_newsocket(); i.e., when a connection is being established by an 177 * incoming CR_TPDU. 178 * 179 * FUNCTION and ARGUMENTS: 180 * Copy a whole net addr from a struct sockaddr (name). 181 * into an inpcb (inp). 182 * The argument (which) takes values TP_LOCAL or TP_FOREIGN 183 * 184 * RETURNS: Nada 185 * 186 * SIDE EFFECTS: 187 * 188 * NOTES: 189 */ 190 void 191 in_putnetaddr(inp, name, which) 192 register struct inpcb *inp; 193 struct sockaddr_in *name; 194 int which; 195 { 196 switch (which) { 197 case TP_LOCAL: 198 bcopy((caddr_t)&name->sin_addr, 199 (caddr_t)&inp->inp_laddr, sizeof(struct in_addr)); 200 /* won't work if the dst address (name) is INADDR_ANY */ 201 202 break; 203 case TP_FOREIGN: 204 if( name != (struct sockaddr_in *)0 ) { 205 bcopy((caddr_t)&name->sin_addr, 206 (caddr_t)&inp->inp_faddr, sizeof(struct in_addr)); 207 } 208 } 209 } 210 211 /* 212 * NAME: in_putnetaddr() 213 * 214 * CALLED FROM: 215 * tp_input() when a connection is being established by an 216 * incoming CR_TPDU, and considered for interception. 217 * 218 * FUNCTION and ARGUMENTS: 219 * Compare a whole net addr from a struct sockaddr (name), 220 * with that implicitly stored in an inpcb (inp). 221 * The argument (which) takes values TP_LOCAL or TP_FOREIGN 222 * 223 * RETURNS: Nada 224 * 225 * SIDE EFFECTS: 226 * 227 * NOTES: 228 */ 229 in_cmpnetaddr(inp, name, which) 230 register struct inpcb *inp; 231 register struct sockaddr_in *name; 232 int which; 233 { 234 if (which == TP_LOCAL) { 235 if (name->sin_port && name->sin_port != inp->inp_lport) 236 return 0; 237 return (name->sin_addr.s_addr == inp->inp_laddr.s_addr); 238 } 239 if (name->sin_port && name->sin_port != inp->inp_fport) 240 return 0; 241 return (name->sin_addr.s_addr == inp->inp_faddr.s_addr); 242 } 243 244 /* 245 * NAME: in_getnetaddr() 246 * 247 * CALLED FROM: 248 * pr_usrreq() PRU_SOCKADDR, PRU_ACCEPT, PRU_PEERADDR 249 * FUNCTION and ARGUMENTS: 250 * Copy a whole net addr from an inpcb (inp) into 251 * an mbuf (name); 252 * The argument (which) takes values TP_LOCAL or TP_FOREIGN. 253 * 254 * RETURNS: Nada 255 * 256 * SIDE EFFECTS: 257 * 258 * NOTES: 259 */ 260 261 void 262 in_getnetaddr( inp, name, which) 263 register struct mbuf *name; 264 struct inpcb *inp; 265 int which; 266 { 267 register struct sockaddr_in *sin = mtod(name, struct sockaddr_in *); 268 bzero((caddr_t)sin, sizeof(*sin)); 269 switch (which) { 270 case TP_LOCAL: 271 sin->sin_addr = inp->inp_laddr; 272 sin->sin_port = inp->inp_lport; 273 break; 274 case TP_FOREIGN: 275 sin->sin_addr = inp->inp_faddr; 276 sin->sin_port = inp->inp_fport; 277 break; 278 default: 279 return; 280 } 281 name->m_len = sin->sin_len = sizeof (*sin); 282 sin->sin_family = AF_INET; 283 } 284 285 /* 286 * NAME: tpip_mtu() 287 * 288 * CALLED FROM: 289 * tp_route_to() on incoming CR, CC, and pr_usrreq() for PRU_CONNECT 290 * 291 * FUNCTION, ARGUMENTS, and RETURN VALUE: 292 * 293 * Perform subnetwork dependent part of determining MTU information. 294 * It appears that setting a double pointer to the rtentry associated with 295 * the destination, and returning the header size for the network protocol 296 * suffices. 297 * 298 * SIDE EFFECTS: 299 * Sets tp_routep pointer in pcb. 300 * 301 * NOTES: 302 */ 303 304 tpip_mtu(tpcb) 305 register struct tp_pcb *tpcb; 306 { 307 struct inpcb *inp = (struct inpcb *)tpcb->tp_npcb; 308 309 IFDEBUG(D_CONN) 310 printf("tpip_mtu(tpcb)\n", tpcb); 311 printf("tpip_mtu routing to addr 0x%x\n", inp->inp_faddr.s_addr); 312 ENDDEBUG 313 tpcb->tp_routep = &(inp->inp_route.ro_rt); 314 return (sizeof (struct ip)); 315 316 } 317 318 /* 319 * NAME: tpip_output() 320 * 321 * CALLED FROM: tp_emit() 322 * 323 * FUNCTION and ARGUMENTS: 324 * Take a packet(m0) from tp and package it so that ip will accept it. 325 * This means prepending space for the ip header and filling in a few 326 * of the fields. 327 * inp is the inpcb structure; datalen is the length of the data in the 328 * mbuf string m0. 329 * RETURNS: 330 * whatever (E*) is returned form the net layer output routine. 331 * 332 * SIDE EFFECTS: 333 * 334 * NOTES: 335 */ 336 337 int 338 tpip_output(inp, m0, datalen, nochksum) 339 struct inpcb *inp; 340 struct mbuf *m0; 341 int datalen; 342 int nochksum; 343 { 344 return tpip_output_dg( &inp->inp_laddr, &inp->inp_faddr, m0, datalen, 345 &inp->inp_route, nochksum); 346 } 347 348 /* 349 * NAME: tpip_output_dg() 350 * 351 * CALLED FROM: tp_error_emit() 352 * 353 * FUNCTION and ARGUMENTS: 354 * This is a copy of tpip_output that takes the addresses 355 * instead of a pcb. It's used by the tp_error_emit, when we 356 * don't have an in_pcb with which to call the normal output rtn. 357 * 358 * RETURNS: ENOBUFS or whatever (E*) is 359 * returned form the net layer output routine. 360 * 361 * SIDE EFFECTS: 362 * 363 * NOTES: 364 */ 365 366 /*ARGSUSED*/ 367 int 368 tpip_output_dg(laddr, faddr, m0, datalen, ro, nochksum) 369 struct in_addr *laddr, *faddr; 370 struct mbuf *m0; 371 int datalen; 372 struct route *ro; 373 int nochksum; 374 { 375 register struct mbuf *m; 376 register struct ip *ip; 377 int error; 378 379 IFDEBUG(D_EMIT) 380 printf("tpip_output_dg datalen 0x%x m0 0x%x\n", datalen, m0); 381 ENDDEBUG 382 383 384 MGETHDR(m, M_DONTWAIT, TPMT_IPHDR); 385 if (m == 0) { 386 error = ENOBUFS; 387 goto bad; 388 } 389 m->m_next = m0; 390 MH_ALIGN(m, sizeof(struct ip)); 391 m->m_len = sizeof(struct ip); 392 393 ip = mtod(m, struct ip *); 394 bzero((caddr_t)ip, sizeof *ip); 395 396 ip->ip_p = IPPROTO_TP; 397 m->m_pkthdr.len = ip->ip_len = sizeof(struct ip) + datalen; 398 ip->ip_ttl = MAXTTL; 399 /* don't know why you need to set ttl; 400 * overlay doesn't even make this available 401 */ 402 403 ip->ip_src = *laddr; 404 ip->ip_dst = *faddr; 405 406 IncStat(ts_tpdu_sent); 407 IFDEBUG(D_EMIT) 408 dump_mbuf(m, "tpip_output_dg before ip_output\n"); 409 ENDDEBUG 410 411 error = ip_output(m, (struct mbuf *)0, ro, IP_ALLOWBROADCAST); 412 413 IFDEBUG(D_EMIT) 414 printf("tpip_output_dg after ip_output\n"); 415 ENDDEBUG 416 417 return error; 418 419 bad: 420 m_freem(m); 421 IncStat(ts_send_drop); 422 return error; 423 } 424 425 /* 426 * NAME: tpip_input() 427 * 428 * CALLED FROM: 429 * ip's input routine, indirectly through the protosw. 430 * 431 * FUNCTION and ARGUMENTS: 432 * Take a packet (m) from ip, strip off the ip header and give it to tp 433 * 434 * RETURNS: No return value. 435 * 436 * SIDE EFFECTS: 437 * 438 * NOTES: 439 */ 440 ProtoHook 441 tpip_input(m, iplen) 442 struct mbuf *m; 443 int iplen; 444 { 445 struct sockaddr_in src, dst; 446 register struct ip *ip; 447 int s = splnet(), hdrlen; 448 449 IncStat(ts_pkt_rcvd); 450 451 /* 452 * IP layer has already pulled up the IP header, 453 * but the first byte after the IP header may not be there, 454 * e.g. if you came in via loopback, so you have to do an 455 * m_pullup to before you can even look to see how much you 456 * really need. The good news is that m_pullup will round 457 * up to almost the next mbuf's worth. 458 */ 459 460 461 if((m = m_pullup(m, iplen + 1)) == MNULL) 462 goto discard; 463 CHANGE_MTYPE(m, TPMT_DATA); 464 465 /* 466 * Now pull up the whole tp header: 467 * Unfortunately, there may be IP options to skip past so we 468 * just fetch it as an unsigned char. 469 */ 470 hdrlen = iplen + 1 + mtod(m, u_char *)[iplen]; 471 472 if( m->m_len < hdrlen ) { 473 if((m = m_pullup(m, hdrlen)) == MNULL){ 474 IFDEBUG(D_TPINPUT) 475 printf("tp_input, pullup 2!\n"); 476 ENDDEBUG 477 goto discard; 478 } 479 } 480 /* 481 * cannot use tp_inputprep() here 'cause you don't 482 * have quite the same situation 483 */ 484 485 IFDEBUG(D_TPINPUT) 486 dump_mbuf(m, "after tpip_input both pullups"); 487 ENDDEBUG 488 /* 489 * m_pullup may have returned a different mbuf 490 */ 491 ip = mtod(m, struct ip *); 492 493 /* 494 * drop the ip header from the front of the mbuf 495 * this is necessary for the tp checksum 496 */ 497 m->m_len -= iplen; 498 m->m_data += iplen; 499 500 src.sin_addr = *(struct in_addr *)&(ip->ip_src); 501 src.sin_family = AF_INET; 502 src.sin_len = sizeof(src); 503 dst.sin_addr = *(struct in_addr *)&(ip->ip_dst); 504 dst.sin_family = AF_INET; 505 dst.sin_len = sizeof(dst); 506 507 (void) tp_input(m, (struct sockaddr *)&src, (struct sockaddr *)&dst, 508 0, tpip_output_dg, 0); 509 return 0; 510 511 discard: 512 IFDEBUG(D_TPINPUT) 513 printf("tpip_input DISCARD\n"); 514 ENDDEBUG 515 IFTRACE(D_TPINPUT) 516 tptrace(TPPTmisc, "tpip_input DISCARD m", m,0,0,0); 517 ENDTRACE 518 m_freem(m); 519 IncStat(ts_recv_drop); 520 splx(s); 521 return 0; 522 } 523 524 525 #include <sys/protosw.h> 526 #include <netinet/ip_icmp.h> 527 528 extern void tp_quench(); 529 /* 530 * NAME: tpin_quench() 531 * 532 * CALLED FROM: tpip_ctlinput() 533 * 534 * FUNCTION and ARGUMENTS: find the tpcb pointer and pass it to tp_quench 535 * 536 * RETURNS: Nada 537 * 538 * SIDE EFFECTS: 539 * 540 * NOTES: 541 */ 542 543 void 544 tpin_quench(inp) 545 struct inpcb *inp; 546 { 547 tp_quench((struct tp_pcb *)inp->inp_socket->so_pcb, PRC_QUENCH); 548 } 549 550 /* 551 * NAME: tpip_ctlinput() 552 * 553 * CALLED FROM: 554 * The network layer through the protosw table. 555 * 556 * FUNCTION and ARGUMENTS: 557 * When clnp gets an ICMP msg this gets called. 558 * It either returns an error status to the user or 559 * causes all connections on this address to be aborted 560 * by calling the appropriate xx_notify() routine. 561 * (cmd) is the type of ICMP error. 562 * (sa) the address of the sender 563 * 564 * RETURNS: Nothing 565 * 566 * SIDE EFFECTS: 567 * 568 * NOTES: 569 */ 570 ProtoHook 571 tpip_ctlinput(cmd, sin) 572 int cmd; 573 struct sockaddr_in *sin; 574 { 575 extern u_char inetctlerrmap[]; 576 extern ProtoHook tpin_abort(); 577 extern ProtoHook in_rtchange(); 578 extern struct in_addr zeroin_addr; 579 580 if (sin->sin_family != AF_INET && sin->sin_family != AF_IMPLINK) 581 return 0; 582 if (sin->sin_addr.s_addr == INADDR_ANY) 583 return 0; 584 if (cmd < 0 || cmd > PRC_NCMDS) 585 return 0; 586 switch (cmd) { 587 588 case PRC_QUENCH: 589 in_pcbnotify(&tp_inpcb, sin, 0, 590 zeroin_addr, 0, cmd, (int (*)())tp_quench); 591 break; 592 593 case PRC_ROUTEDEAD: 594 case PRC_HOSTUNREACH: 595 case PRC_UNREACH_NET: 596 case PRC_IFDOWN: 597 case PRC_HOSTDEAD: 598 in_pcbnotify(&tp_inpcb, sin, 0, 599 zeroin_addr, 0, cmd, in_rtchange); 600 break; 601 602 default: 603 /* 604 case PRC_MSGSIZE: 605 case PRC_UNREACH_HOST: 606 case PRC_UNREACH_PROTOCOL: 607 case PRC_UNREACH_PORT: 608 case PRC_UNREACH_NEEDFRAG: 609 case PRC_UNREACH_SRCFAIL: 610 case PRC_REDIRECT_NET: 611 case PRC_REDIRECT_HOST: 612 case PRC_REDIRECT_TOSNET: 613 case PRC_REDIRECT_TOSHOST: 614 case PRC_TIMXCEED_INTRANS: 615 case PRC_TIMXCEED_REASS: 616 case PRC_PARAMPROB: 617 */ 618 in_pcbnotify(&tp_inpcb, sin, 0, zeroin_addr, 0, 619 cmd, tpin_abort); 620 } 621 return 0; 622 } 623 624 /* 625 * NAME: tpin_abort() 626 * 627 * CALLED FROM: 628 * xxx_notify() from tp_ctlinput() when 629 * net level gets some ICMP-equiv. type event. 630 * 631 * FUNCTION and ARGUMENTS: 632 * Cause the connection to be aborted with some sort of error 633 * reason indicating that the network layer caused the abort. 634 * Fakes an ER TPDU so we can go through the driver. 635 * 636 * RETURNS: Nothing 637 * 638 * SIDE EFFECTS: 639 * 640 * NOTES: 641 */ 642 643 ProtoHook 644 tpin_abort(inp) 645 struct inpcb *inp; 646 { 647 struct tp_event e; 648 649 e.ev_number = ER_TPDU; 650 e.ATTR(ER_TPDU).e_reason = ENETRESET; 651 (void) tp_driver((struct tp_pcb *)inp->inp_ppcb, &e); 652 return 0; 653 } 654 655 #ifdef ARGO_DEBUG 656 dump_inaddr(addr) 657 register struct sockaddr_in *addr; 658 { 659 printf("INET: port 0x%x; addr 0x%x\n", addr->sin_port, addr->sin_addr); 660 } 661 #endif ARGO_DEBUG 662 #endif INET 663