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