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