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