tcp_usrreq.c revision 28270
1/* 2 * Copyright (c) 1982, 1986, 1988, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * From: @(#)tcp_usrreq.c 8.2 (Berkeley) 1/3/94 34 * $Id: tcp_usrreq.c,v 1.32 1997/08/02 14:32:58 bde Exp $ 35 */ 36 37#include <sys/param.h> 38#include <sys/systm.h> 39#include <sys/kernel.h> 40#include <sys/sysctl.h> 41#include <sys/mbuf.h> 42#include <sys/socket.h> 43#include <sys/socketvar.h> 44#include <sys/protosw.h> 45 46#include <net/if.h> 47#include <net/route.h> 48 49#include <netinet/in.h> 50#include <netinet/in_systm.h> 51#include <netinet/in_pcb.h> 52#include <netinet/in_var.h> 53#include <netinet/ip_var.h> 54#include <netinet/tcp.h> 55#include <netinet/tcp_fsm.h> 56#include <netinet/tcp_seq.h> 57#include <netinet/tcp_timer.h> 58#include <netinet/tcp_var.h> 59#include <netinet/tcpip.h> 60#ifdef TCPDEBUG 61#include <netinet/tcp_debug.h> 62#endif 63 64/* 65 * TCP protocol interface to socket abstraction. 66 */ 67extern char *tcpstates[]; /* XXX ??? */ 68 69static int tcp_attach __P((struct socket *, struct proc *)); 70static int tcp_connect __P((struct tcpcb *, struct sockaddr *, 71 struct proc *)); 72static struct tcpcb * 73 tcp_disconnect __P((struct tcpcb *)); 74static struct tcpcb * 75 tcp_usrclosed __P((struct tcpcb *)); 76 77#ifdef TCPDEBUG 78#define TCPDEBUG0 int ostate 79#define TCPDEBUG1() ostate = tp ? tp->t_state : 0 80#define TCPDEBUG2(req) if (tp && (so->so_options & SO_DEBUG)) \ 81 tcp_trace(TA_USER, ostate, tp, 0, req) 82#else 83#define TCPDEBUG0 84#define TCPDEBUG1() 85#define TCPDEBUG2(req) 86#endif 87 88/* 89 * TCP attaches to socket via pru_attach(), reserving space, 90 * and an internet control block. 91 */ 92static int 93tcp_usr_attach(struct socket *so, int proto, struct proc *p) 94{ 95 int s = splnet(); 96 int error; 97 struct inpcb *inp = sotoinpcb(so); 98 struct tcpcb *tp = 0; 99 TCPDEBUG0; 100 101 TCPDEBUG1(); 102 if (inp) { 103 error = EISCONN; 104 goto out; 105 } 106 107 error = tcp_attach(so, p); 108 if (error) 109 goto out; 110 111 if ((so->so_options & SO_LINGER) && so->so_linger == 0) 112 so->so_linger = TCP_LINGERTIME * hz; 113 tp = sototcpcb(so); 114out: 115 TCPDEBUG2(PRU_ATTACH); 116 splx(s); 117 return error; 118} 119 120/* 121 * pru_detach() detaches the TCP protocol from the socket. 122 * If the protocol state is non-embryonic, then can't 123 * do this directly: have to initiate a pru_disconnect(), 124 * which may finish later; embryonic TCB's can just 125 * be discarded here. 126 */ 127static int 128tcp_usr_detach(struct socket *so) 129{ 130 int s = splnet(); 131 int error = 0; 132 struct inpcb *inp = sotoinpcb(so); 133 struct tcpcb *tp; 134 TCPDEBUG0; 135 136 if (inp == 0) { 137 splx(s); 138 return EINVAL; /* XXX */ 139 } 140 tp = intotcpcb(inp); 141 TCPDEBUG1(); 142 tp = tcp_disconnect(tp); 143 144 TCPDEBUG2(PRU_DETACH); 145 splx(s); 146 return error; 147} 148 149#define COMMON_START() TCPDEBUG0; \ 150 do { \ 151 if (inp == 0) { \ 152 splx(s); \ 153 return EINVAL; \ 154 } \ 155 tp = intotcpcb(inp); \ 156 TCPDEBUG1(); \ 157 } while(0) 158 159#define COMMON_END(req) out: TCPDEBUG2(req); splx(s); return error; goto out 160 161 162/* 163 * Give the socket an address. 164 */ 165static int 166tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) 167{ 168 int s = splnet(); 169 int error = 0; 170 struct inpcb *inp = sotoinpcb(so); 171 struct tcpcb *tp; 172 struct sockaddr_in *sinp; 173 174 COMMON_START(); 175 176 /* 177 * Must check for multicast addresses and disallow binding 178 * to them. 179 */ 180 sinp = (struct sockaddr_in *)nam; 181 if (sinp->sin_family == AF_INET && 182 IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) { 183 error = EAFNOSUPPORT; 184 goto out; 185 } 186 error = in_pcbbind(inp, nam, p); 187 if (error) 188 goto out; 189 COMMON_END(PRU_BIND); 190 191} 192 193/* 194 * Prepare to accept connections. 195 */ 196static int 197tcp_usr_listen(struct socket *so, struct proc *p) 198{ 199 int s = splnet(); 200 int error = 0; 201 struct inpcb *inp = sotoinpcb(so); 202 struct tcpcb *tp; 203 204 COMMON_START(); 205 if (inp->inp_lport == 0) 206 error = in_pcbbind(inp, (struct sockaddr *)0, p); 207 if (error == 0) 208 tp->t_state = TCPS_LISTEN; 209 COMMON_END(PRU_LISTEN); 210} 211 212/* 213 * Initiate connection to peer. 214 * Create a template for use in transmissions on this connection. 215 * Enter SYN_SENT state, and mark socket as connecting. 216 * Start keep-alive timer, and seed output sequence space. 217 * Send initial segment on connection. 218 */ 219static int 220tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct proc *p) 221{ 222 int s = splnet(); 223 int error = 0; 224 struct inpcb *inp = sotoinpcb(so); 225 struct tcpcb *tp; 226 struct sockaddr_in *sinp; 227 228 COMMON_START(); 229 230 /* 231 * Must disallow TCP ``connections'' to multicast addresses. 232 */ 233 sinp = (struct sockaddr_in *)nam; 234 if (sinp->sin_family == AF_INET 235 && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) { 236 error = EAFNOSUPPORT; 237 goto out; 238 } 239 240 if ((error = tcp_connect(tp, nam, p)) != 0) 241 goto out; 242 error = tcp_output(tp); 243 COMMON_END(PRU_CONNECT); 244} 245 246/* 247 * Initiate disconnect from peer. 248 * If connection never passed embryonic stage, just drop; 249 * else if don't need to let data drain, then can just drop anyways, 250 * else have to begin TCP shutdown process: mark socket disconnecting, 251 * drain unread data, state switch to reflect user close, and 252 * send segment (e.g. FIN) to peer. Socket will be really disconnected 253 * when peer sends FIN and acks ours. 254 * 255 * SHOULD IMPLEMENT LATER PRU_CONNECT VIA REALLOC TCPCB. 256 */ 257static int 258tcp_usr_disconnect(struct socket *so) 259{ 260 int s = splnet(); 261 int error = 0; 262 struct inpcb *inp = sotoinpcb(so); 263 struct tcpcb *tp; 264 265 COMMON_START(); 266 tp = tcp_disconnect(tp); 267 COMMON_END(PRU_DISCONNECT); 268} 269 270/* 271 * Accept a connection. Essentially all the work is 272 * done at higher levels; just return the address 273 * of the peer, storing through addr. 274 */ 275static int 276tcp_usr_accept(struct socket *so, struct sockaddr **nam) 277{ 278 int s = splnet(); 279 int error = 0; 280 struct inpcb *inp = sotoinpcb(so); 281 struct tcpcb *tp; 282 283 COMMON_START(); 284 in_setpeeraddr(so, nam); 285 COMMON_END(PRU_ACCEPT); 286} 287 288/* 289 * Mark the connection as being incapable of further output. 290 */ 291static int 292tcp_usr_shutdown(struct socket *so) 293{ 294 int s = splnet(); 295 int error = 0; 296 struct inpcb *inp = sotoinpcb(so); 297 struct tcpcb *tp; 298 299 COMMON_START(); 300 socantsendmore(so); 301 tp = tcp_usrclosed(tp); 302 if (tp) 303 error = tcp_output(tp); 304 COMMON_END(PRU_SHUTDOWN); 305} 306 307/* 308 * After a receive, possibly send window update to peer. 309 */ 310static int 311tcp_usr_rcvd(struct socket *so, int flags) 312{ 313 int s = splnet(); 314 int error = 0; 315 struct inpcb *inp = sotoinpcb(so); 316 struct tcpcb *tp; 317 318 COMMON_START(); 319 tcp_output(tp); 320 COMMON_END(PRU_RCVD); 321} 322 323/* 324 * Do a send by putting data in output queue and updating urgent 325 * marker if URG set. Possibly send more data. 326 */ 327static int 328tcp_usr_send(struct socket *so, int flags, struct mbuf *m, 329 struct sockaddr *nam, struct mbuf *control, struct proc *p) 330{ 331 int s = splnet(); 332 int error = 0; 333 struct inpcb *inp = sotoinpcb(so); 334 struct tcpcb *tp; 335 336 COMMON_START(); 337 if (control && control->m_len) { 338 m_freem(control); /* XXX shouldn't caller do this??? */ 339 if (m) 340 m_freem(m); 341 return EINVAL; 342 } 343 344 if(!(flags & PRUS_OOB)) { 345 sbappend(&so->so_snd, m); 346 if (nam && tp->t_state < TCPS_SYN_SENT) { 347 /* 348 * Do implied connect if not yet connected, 349 * initialize window to default value, and 350 * initialize maxseg/maxopd using peer's cached 351 * MSS. 352 */ 353 error = tcp_connect(tp, nam, p); 354 if (error) 355 goto out; 356 tp->snd_wnd = TTCP_CLIENT_SND_WND; 357 tcp_mss(tp, -1); 358 } 359 360 if (flags & PRUS_EOF) { 361 /* 362 * Close the send side of the connection after 363 * the data is sent. 364 */ 365 socantsendmore(so); 366 tp = tcp_usrclosed(tp); 367 } 368 if (tp != NULL) 369 error = tcp_output(tp); 370 } else { 371 if (sbspace(&so->so_snd) < -512) { 372 m_freem(m); 373 error = ENOBUFS; 374 goto out; 375 } 376 /* 377 * According to RFC961 (Assigned Protocols), 378 * the urgent pointer points to the last octet 379 * of urgent data. We continue, however, 380 * to consider it to indicate the first octet 381 * of data past the urgent section. 382 * Otherwise, snd_up should be one lower. 383 */ 384 sbappend(&so->so_snd, m); 385 if (nam && tp->t_state < TCPS_SYN_SENT) { 386 /* 387 * Do implied connect if not yet connected, 388 * initialize window to default value, and 389 * initialize maxseg/maxopd using peer's cached 390 * MSS. 391 */ 392 error = tcp_connect(tp, nam, p); 393 if (error) 394 goto out; 395 tp->snd_wnd = TTCP_CLIENT_SND_WND; 396 tcp_mss(tp, -1); 397 } 398 tp->snd_up = tp->snd_una + so->so_snd.sb_cc; 399 tp->t_force = 1; 400 error = tcp_output(tp); 401 tp->t_force = 0; 402 } 403 COMMON_END((flags & PRUS_OOB) ? PRU_SENDOOB : 404 ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); 405} 406 407/* 408 * Abort the TCP. 409 */ 410static int 411tcp_usr_abort(struct socket *so) 412{ 413 int s = splnet(); 414 int error = 0; 415 struct inpcb *inp = sotoinpcb(so); 416 struct tcpcb *tp; 417 418 COMMON_START(); 419 tp = tcp_drop(tp, ECONNABORTED); 420 COMMON_END(PRU_ABORT); 421} 422 423/* 424 * Receive out-of-band data. 425 */ 426static int 427tcp_usr_rcvoob(struct socket *so, struct mbuf *m, int flags) 428{ 429 int s = splnet(); 430 int error = 0; 431 struct inpcb *inp = sotoinpcb(so); 432 struct tcpcb *tp; 433 434 COMMON_START(); 435 if ((so->so_oobmark == 0 && 436 (so->so_state & SS_RCVATMARK) == 0) || 437 so->so_options & SO_OOBINLINE || 438 tp->t_oobflags & TCPOOB_HADDATA) { 439 error = EINVAL; 440 goto out; 441 } 442 if ((tp->t_oobflags & TCPOOB_HAVEDATA) == 0) { 443 error = EWOULDBLOCK; 444 goto out; 445 } 446 m->m_len = 1; 447 *mtod(m, caddr_t) = tp->t_iobc; 448 if ((flags & MSG_PEEK) == 0) 449 tp->t_oobflags ^= (TCPOOB_HAVEDATA | TCPOOB_HADDATA); 450 COMMON_END(PRU_RCVOOB); 451} 452 453/* xxx - should be const */ 454struct pr_usrreqs tcp_usrreqs = { 455 tcp_usr_abort, tcp_usr_accept, tcp_usr_attach, tcp_usr_bind, 456 tcp_usr_connect, pru_connect2_notsupp, in_control, tcp_usr_detach, 457 tcp_usr_disconnect, tcp_usr_listen, in_setpeeraddr, tcp_usr_rcvd, 458 tcp_usr_rcvoob, tcp_usr_send, pru_sense_null, tcp_usr_shutdown, 459 in_setsockaddr, sosend, soreceive, soselect 460}; 461 462/* 463 * Common subroutine to open a TCP connection to remote host specified 464 * by struct sockaddr_in in mbuf *nam. Call in_pcbbind to assign a local 465 * port number if needed. Call in_pcbladdr to do the routing and to choose 466 * a local host address (interface). If there is an existing incarnation 467 * of the same connection in TIME-WAIT state and if the remote host was 468 * sending CC options and if the connection duration was < MSL, then 469 * truncate the previous TIME-WAIT state and proceed. 470 * Initialize connection parameters and enter SYN-SENT state. 471 */ 472static int 473tcp_connect(tp, nam, p) 474 register struct tcpcb *tp; 475 struct sockaddr *nam; 476 struct proc *p; 477{ 478 struct inpcb *inp = tp->t_inpcb, *oinp; 479 struct socket *so = inp->inp_socket; 480 struct tcpcb *otp; 481 struct sockaddr_in *sin = (struct sockaddr_in *)nam; 482 struct sockaddr_in *ifaddr; 483 int error; 484 struct rmxp_tao *taop; 485 struct rmxp_tao tao_noncached; 486 487 if (inp->inp_lport == 0) { 488 error = in_pcbbind(inp, (struct sockaddr *)0, p); 489 if (error) 490 return error; 491 } 492 493 /* 494 * Cannot simply call in_pcbconnect, because there might be an 495 * earlier incarnation of this same connection still in 496 * TIME_WAIT state, creating an ADDRINUSE error. 497 */ 498 error = in_pcbladdr(inp, nam, &ifaddr); 499 if (error) 500 return error; 501 oinp = in_pcblookuphash(inp->inp_pcbinfo, 502 sin->sin_addr, sin->sin_port, 503 inp->inp_laddr.s_addr != INADDR_ANY ? inp->inp_laddr 504 : ifaddr->sin_addr, 505 inp->inp_lport, 0); 506 if (oinp) { 507 if (oinp != inp && (otp = intotcpcb(oinp)) != NULL && 508 otp->t_state == TCPS_TIME_WAIT && 509 otp->t_duration < TCPTV_MSL && 510 (otp->t_flags & TF_RCVD_CC)) 511 otp = tcp_close(otp); 512 else 513 return EADDRINUSE; 514 } 515 if (inp->inp_laddr.s_addr == INADDR_ANY) 516 inp->inp_laddr = ifaddr->sin_addr; 517 inp->inp_faddr = sin->sin_addr; 518 inp->inp_fport = sin->sin_port; 519 in_pcbrehash(inp); 520 521 tp->t_template = tcp_template(tp); 522 if (tp->t_template == 0) { 523 in_pcbdisconnect(inp); 524 return ENOBUFS; 525 } 526 527 /* Compute window scaling to request. */ 528 while (tp->request_r_scale < TCP_MAX_WINSHIFT && 529 (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat) 530 tp->request_r_scale++; 531 532 soisconnecting(so); 533 tcpstat.tcps_connattempt++; 534 tp->t_state = TCPS_SYN_SENT; 535 tp->t_timer[TCPT_KEEP] = tcp_keepinit; 536 tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2; 537 tcp_sendseqinit(tp); 538 539 /* 540 * Generate a CC value for this connection and 541 * check whether CC or CCnew should be used. 542 */ 543 if ((taop = tcp_gettaocache(tp->t_inpcb)) == NULL) { 544 taop = &tao_noncached; 545 bzero(taop, sizeof(*taop)); 546 } 547 548 tp->cc_send = CC_INC(tcp_ccgen); 549 if (taop->tao_ccsent != 0 && 550 CC_GEQ(tp->cc_send, taop->tao_ccsent)) { 551 taop->tao_ccsent = tp->cc_send; 552 } else { 553 taop->tao_ccsent = 0; 554 tp->t_flags |= TF_SENDCCNEW; 555 } 556 557 return 0; 558} 559 560int 561tcp_ctloutput(op, so, level, optname, mp, p) 562 int op; 563 struct socket *so; 564 int level, optname; 565 struct mbuf **mp; 566 struct proc *p; 567{ 568 int error = 0, s; 569 struct inpcb *inp; 570 register struct tcpcb *tp; 571 register struct mbuf *m; 572 register int i; 573 574 s = splnet(); 575 inp = sotoinpcb(so); 576 if (inp == NULL) { 577 splx(s); 578 if (op == PRCO_SETOPT && *mp) 579 (void) m_free(*mp); 580 return (ECONNRESET); 581 } 582 if (level != IPPROTO_TCP) { 583 error = ip_ctloutput(op, so, level, optname, mp, p); 584 splx(s); 585 return (error); 586 } 587 tp = intotcpcb(inp); 588 589 switch (op) { 590 591 case PRCO_SETOPT: 592 m = *mp; 593 switch (optname) { 594 595 case TCP_NODELAY: 596 if (m == NULL || m->m_len < sizeof (int)) 597 error = EINVAL; 598 else if (*mtod(m, int *)) 599 tp->t_flags |= TF_NODELAY; 600 else 601 tp->t_flags &= ~TF_NODELAY; 602 break; 603 604 case TCP_MAXSEG: 605 if (m && (i = *mtod(m, int *)) > 0 && i <= tp->t_maxseg) 606 tp->t_maxseg = i; 607 else 608 error = EINVAL; 609 break; 610 611 case TCP_NOOPT: 612 if (m == NULL || m->m_len < sizeof (int)) 613 error = EINVAL; 614 else if (*mtod(m, int *)) 615 tp->t_flags |= TF_NOOPT; 616 else 617 tp->t_flags &= ~TF_NOOPT; 618 break; 619 620 case TCP_NOPUSH: 621 if (m == NULL || m->m_len < sizeof (int)) 622 error = EINVAL; 623 else if (*mtod(m, int *)) 624 tp->t_flags |= TF_NOPUSH; 625 else 626 tp->t_flags &= ~TF_NOPUSH; 627 break; 628 629 default: 630 error = ENOPROTOOPT; 631 break; 632 } 633 if (m) 634 (void) m_free(m); 635 break; 636 637 case PRCO_GETOPT: 638 *mp = m = m_get(M_WAIT, MT_SOOPTS); 639 m->m_len = sizeof(int); 640 641 switch (optname) { 642 case TCP_NODELAY: 643 *mtod(m, int *) = tp->t_flags & TF_NODELAY; 644 break; 645 case TCP_MAXSEG: 646 *mtod(m, int *) = tp->t_maxseg; 647 break; 648 case TCP_NOOPT: 649 *mtod(m, int *) = tp->t_flags & TF_NOOPT; 650 break; 651 case TCP_NOPUSH: 652 *mtod(m, int *) = tp->t_flags & TF_NOPUSH; 653 break; 654 default: 655 error = ENOPROTOOPT; 656 break; 657 } 658 break; 659 } 660 splx(s); 661 return (error); 662} 663 664/* 665 * tcp_sendspace and tcp_recvspace are the default send and receive window 666 * sizes, respectively. These are obsolescent (this information should 667 * be set by the route). 668 */ 669u_long tcp_sendspace = 1024*16; 670SYSCTL_INT(_net_inet_tcp, TCPCTL_SENDSPACE, sendspace, 671 CTLFLAG_RW, &tcp_sendspace , 0, ""); 672u_long tcp_recvspace = 1024*16; 673SYSCTL_INT(_net_inet_tcp, TCPCTL_RECVSPACE, recvspace, 674 CTLFLAG_RW, &tcp_recvspace , 0, ""); 675 676/* 677 * Attach TCP protocol to socket, allocating 678 * internet protocol control block, tcp control block, 679 * bufer space, and entering LISTEN state if to accept connections. 680 */ 681static int 682tcp_attach(so, p) 683 struct socket *so; 684 struct proc *p; 685{ 686 register struct tcpcb *tp; 687 struct inpcb *inp; 688 int error; 689 690 if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 691 error = soreserve(so, tcp_sendspace, tcp_recvspace); 692 if (error) 693 return (error); 694 } 695 error = in_pcballoc(so, &tcbinfo, p); 696 if (error) 697 return (error); 698 inp = sotoinpcb(so); 699 tp = tcp_newtcpcb(inp); 700 if (tp == 0) { 701 int nofd = so->so_state & SS_NOFDREF; /* XXX */ 702 703 so->so_state &= ~SS_NOFDREF; /* don't free the socket yet */ 704 in_pcbdetach(inp); 705 so->so_state |= nofd; 706 return (ENOBUFS); 707 } 708 tp->t_state = TCPS_CLOSED; 709 return (0); 710} 711 712/* 713 * Initiate (or continue) disconnect. 714 * If embryonic state, just send reset (once). 715 * If in ``let data drain'' option and linger null, just drop. 716 * Otherwise (hard), mark socket disconnecting and drop 717 * current input data; switch states based on user close, and 718 * send segment to peer (with FIN). 719 */ 720static struct tcpcb * 721tcp_disconnect(tp) 722 register struct tcpcb *tp; 723{ 724 struct socket *so = tp->t_inpcb->inp_socket; 725 726 if (tp->t_state < TCPS_ESTABLISHED) 727 tp = tcp_close(tp); 728 else if ((so->so_options & SO_LINGER) && so->so_linger == 0) 729 tp = tcp_drop(tp, 0); 730 else { 731 soisdisconnecting(so); 732 sbflush(&so->so_rcv); 733 tp = tcp_usrclosed(tp); 734 if (tp) 735 (void) tcp_output(tp); 736 } 737 return (tp); 738} 739 740/* 741 * User issued close, and wish to trail through shutdown states: 742 * if never received SYN, just forget it. If got a SYN from peer, 743 * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. 744 * If already got a FIN from peer, then almost done; go to LAST_ACK 745 * state. In all other cases, have already sent FIN to peer (e.g. 746 * after PRU_SHUTDOWN), and just have to play tedious game waiting 747 * for peer to send FIN or not respond to keep-alives, etc. 748 * We can let the user exit from the close as soon as the FIN is acked. 749 */ 750static struct tcpcb * 751tcp_usrclosed(tp) 752 register struct tcpcb *tp; 753{ 754 755 switch (tp->t_state) { 756 757 case TCPS_CLOSED: 758 case TCPS_LISTEN: 759 tp->t_state = TCPS_CLOSED; 760 tp = tcp_close(tp); 761 break; 762 763 case TCPS_SYN_SENT: 764 case TCPS_SYN_RECEIVED: 765 tp->t_flags |= TF_NEEDFIN; 766 break; 767 768 case TCPS_ESTABLISHED: 769 tp->t_state = TCPS_FIN_WAIT_1; 770 break; 771 772 case TCPS_CLOSE_WAIT: 773 tp->t_state = TCPS_LAST_ACK; 774 break; 775 } 776 if (tp && tp->t_state >= TCPS_FIN_WAIT_2) { 777 soisdisconnected(tp->t_inpcb->inp_socket); 778 /* To prevent the connection hanging in FIN_WAIT_2 forever. */ 779 if (tp->t_state == TCPS_FIN_WAIT_2) 780 tp->t_timer[TCPT_2MSL] = tcp_maxidle; 781 } 782 return (tp); 783} 784 785