511 lines
16 KiB
C
511 lines
16 KiB
C
/*-
|
|
* Copyright (c) 1982, 1986, 1988, 1993
|
|
* The Regents of the University of California.
|
|
* Copyright (c) 2006-2007 Robert N. M. Watson
|
|
* Copyright (c) 2010-2011 Juniper Networks, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Portions of this software were developed by Robert N. M. Watson under
|
|
* contract to Juniper Networks, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* From: @(#)tcp_usrreq.c 8.2 (Berkeley) 1/3/94
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include "../tcplp.h"
|
|
#include "../lib/cbuf.h"
|
|
#include "tcp.h"
|
|
#include "tcp_fsm.h"
|
|
#include "tcp_seq.h"
|
|
#include "tcp_var.h"
|
|
#include "tcp_timer.h"
|
|
//#include <sys/socket.h>
|
|
#include "ip6.h"
|
|
|
|
#include "tcp_const.h"
|
|
|
|
#include <openthread/tcp.h>
|
|
|
|
//static void tcp_disconnect(struct tcpcb *);
|
|
static void tcp_usrclosed(struct tcpcb *);
|
|
|
|
/*
|
|
* samkumar: Removed tcp6_usr_bind, since checking if an address/port is free
|
|
* needs to be done at the host system (along with other socket management
|
|
* duties). TCPlp doesn't know what other sockets are in the system, or which
|
|
* other addresses/ports are busy.
|
|
*/
|
|
|
|
/* samkumar: This is based on a function in in6_pcb.c. */
|
|
static int in6_pcbconnect(struct tcpcb* tp, struct sockaddr_in6* nam) {
|
|
register struct sockaddr_in6 *sin6 = nam;
|
|
tp->faddr = sin6->sin6_addr;
|
|
tp->fport = sin6->sin6_port;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initiate connection to peer.
|
|
* Create a template for use in transmissions on this connection.
|
|
* Enter SYN_SENT state, and mark socket as connecting.
|
|
* Start keep-alive timer, and seed output sequence space.
|
|
* Send initial segment on connection.
|
|
*/
|
|
/*
|
|
* samkumar: I removed locking, statistics, and inpcb management. The signature
|
|
* used to be
|
|
*
|
|
* static int
|
|
* tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td)
|
|
*/
|
|
static int
|
|
tcp6_connect(struct tcpcb *tp, struct sockaddr_in6 *nam)
|
|
{
|
|
int error;
|
|
|
|
int sb_max = cbuf_free_space(&tp->recvbuf); // same as sendbuf
|
|
|
|
/*
|
|
* samkumar: For autobind, the original BSD code assigned the port first
|
|
* (with logic that also looked at the address) and then the address. This
|
|
* was done by calling into other parts of the FreeBSD network stack,
|
|
* outside of the TCP stack. Here, we just use the tcplp_sys_autobind
|
|
* function to do all of that work.
|
|
*/
|
|
bool autobind_addr = IN6_IS_ADDR_UNSPECIFIED(&tp->laddr);
|
|
bool autobind_port = (tp->lport == 0);
|
|
if (autobind_addr || autobind_port) {
|
|
otSockAddr foreign;
|
|
otSockAddr local;
|
|
|
|
memcpy(&foreign.mAddress, &nam->sin6_addr, sizeof(foreign.mAddress));
|
|
foreign.mPort = ntohs(nam->sin6_port);
|
|
|
|
if (!autobind_addr) {
|
|
memcpy(&local.mAddress, &tp->laddr, sizeof(local.mAddress));
|
|
}
|
|
|
|
if (!autobind_port) {
|
|
local.mPort = ntohs(tp->lport);
|
|
}
|
|
|
|
if (!tcplp_sys_autobind(tp->instance, &foreign, &local, autobind_addr, autobind_port)) {
|
|
// Autobind failed
|
|
error = EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (autobind_addr) {
|
|
memcpy(&tp->laddr, &local.mAddress, sizeof(tp->laddr));
|
|
}
|
|
|
|
if (autobind_port) {
|
|
tp->lport = htons(local.mPort);
|
|
}
|
|
}
|
|
error = in6_pcbconnect(tp, nam);
|
|
if (error != 0)
|
|
goto out;
|
|
|
|
/* Compute window scaling to request. */
|
|
while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
|
|
(TCP_MAXWIN << tp->request_r_scale) < sb_max)
|
|
tp->request_r_scale++;
|
|
|
|
tcp_state_change(tp, TCPS_SYN_SENT);
|
|
tp->iss = tcp_new_isn(tp);
|
|
tcp_sendseqinit(tp);
|
|
|
|
return 0;
|
|
|
|
out:
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* samkumar: I removed locking, statistics, inpcb management, and debug probes.
|
|
* I also remove codepaths that check for IPv6, since the address is assumed to
|
|
* be IPv6. The signature used to be
|
|
*
|
|
* static int
|
|
* tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
|
|
*/
|
|
int
|
|
tcp6_usr_connect(struct tcpcb* tp, struct sockaddr_in6* sin6p)
|
|
{
|
|
int error = 0;
|
|
|
|
if (tp->t_state != TCPS_CLOSED) { // samkumar: This is a check that I added
|
|
return (EISCONN);
|
|
}
|
|
|
|
/* samkumar: I removed the following error check since we receive sin6p
|
|
* in the function argument and don't need to convert a struct sockaddr to
|
|
* a struct sockaddr_in6 anymore.
|
|
*
|
|
* if (nam->sa_len != sizeof (*sin6p))
|
|
* return (EINVAL);
|
|
*/
|
|
|
|
/*
|
|
* Must disallow TCP ``connections'' to multicast addresses.
|
|
*/
|
|
/* samkumar: I commented out the check on sin6p->sin6_family. */
|
|
if (/*sin6p->sin6_family == AF_INET6
|
|
&& */IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr))
|
|
return (EAFNOSUPPORT);
|
|
|
|
/*
|
|
* samkumar: There was some code here that obtained the TCB (struct tcpcb*)
|
|
* by getting the inpcb from the socket and the TCB from the inpcb. I
|
|
* removed that code.
|
|
*/
|
|
|
|
/*
|
|
* XXXRW: Some confusion: V4/V6 flags relate to binding, and
|
|
* therefore probably require the hash lock, which isn't held here.
|
|
* Is this a significant problem?
|
|
*/
|
|
if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) {
|
|
tcplp_sys_log("V4-Mapped Address!");
|
|
|
|
/*
|
|
* samkumar: There used to be code that woulf handle the case of
|
|
* v4-mapped addresses. It would call in6_sin6_2_sin to convert the
|
|
* struct sockaddr_in6 to a struct sockaddr_in, set the INP_IPV4 flag
|
|
* and clear the INP_IPV6 flag on inp->inp_vflag, do some other
|
|
* processing, and finally call tcp_connect and tcp_output. However,
|
|
* it would first check if the IN6P_IPV6_V6ONLY flag was set in
|
|
* inp->inp_flags, and if so, it would return with EINVAL. In TCPlp, we
|
|
* support IPv6 only, so I removed the check for IN6P_IPV6_V6ONLY and
|
|
* always act as if that flag is set. I kept the code in that if
|
|
* statement making the check, and removed the other code that actually
|
|
* handled this case.
|
|
*/
|
|
error = EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* samkumar: I removed some code here that set/cleared some flags in the`
|
|
* inpcb and called prison_remote_ip6.
|
|
*/
|
|
|
|
/*
|
|
* samkumar: Originally, the struct thread *td was passed along to
|
|
* tcp6_connect.
|
|
*/
|
|
if ((error = tcp6_connect(tp, sin6p)) != 0)
|
|
goto out;
|
|
|
|
tcp_timer_activate(tp, TT_KEEP, TP_KEEPINIT(tp));
|
|
error = tcp_output(tp);
|
|
|
|
out:
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Do a send by putting data in output queue and updating urgent
|
|
* marker if URG set. Possibly send more data. Unlike the other
|
|
* pru_*() routines, the mbuf chains are our responsibility. We
|
|
* must either enqueue them or free them. The other pru_* routines
|
|
* generally are caller-frees.
|
|
*/
|
|
/*
|
|
* samkumar: I removed locking, statistics, inpcb management, and debug probes.
|
|
* I also removed support for the urgent pointer.
|
|
*
|
|
* I changed the signature of this function. It used to be
|
|
* static int
|
|
* tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
|
|
* struct sockaddr *nam, struct mbuf *control, struct thread *td)
|
|
*
|
|
* The new function signature works as follows. DATA is a new linked buffer to
|
|
* add to the end of the send buffer. EXTENDBY is the number of bytes by which
|
|
* to extend the final linked buffer of the send buffer. Either DATA should be
|
|
* NULL, or EXTENDBY should be 0.
|
|
*/
|
|
int tcp_usr_send(struct tcpcb* tp, int moretocome, otLinkedBuffer* data, size_t extendby)
|
|
{
|
|
int error = 0;
|
|
|
|
/*
|
|
* samkumar: This if statement and the next are checks that I added
|
|
*/
|
|
if (tp->t_state < TCPS_ESTABLISHED) {
|
|
error = ENOTCONN;
|
|
goto out;
|
|
}
|
|
|
|
if (tpiscantsend(tp)) {
|
|
error = EPIPE;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* samkumar: There used to be logic here that acquired locks, dealt with
|
|
* INP_TIMEWAIT and INP_DROPPED flags on inp->inp_flags, and handled the
|
|
* control mbuf passed as an argument (which would result in an error since
|
|
* TCP doesn't support control information). I've deleted that code, but
|
|
* left the following if block.
|
|
*/
|
|
if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) {
|
|
error = ECONNRESET;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* The following code used to be wrapped in an if statement:
|
|
* "if (!(flags & PRUS_OOB))", that only executed it if the "out of band"
|
|
* flag was not set. In TCB, "out of band" data is conveyed via the urgent
|
|
* pointer, and TCPlp does not support the urgent pointer. Therefore, I
|
|
* removed the "if" check and put its body below.
|
|
*/
|
|
|
|
/*
|
|
* samkumar; The FreeBSD code calls sbappendstream(&so->so_snd, m, flags);
|
|
* I've replaced it with the following logic, which appends to the
|
|
* send buffer according to TCPlp's data structures.
|
|
*/
|
|
if (data == NULL) {
|
|
if (extendby == 0) {
|
|
goto out;
|
|
}
|
|
lbuf_extend(&tp->sendbuf, extendby);
|
|
} else {
|
|
if (data->mLength == 0) {
|
|
goto out;
|
|
}
|
|
lbuf_append(&tp->sendbuf, data);
|
|
}
|
|
|
|
/*
|
|
* samkumar: There used to be code here to handle "implied connect,"
|
|
* which initiates the TCP handshake if sending data on a socket that
|
|
* isn't yet connected. TCPlp doesn't support this at the moment, but
|
|
* it might be worth revisiting when implementing TCP Fast Open.
|
|
*/
|
|
|
|
/*
|
|
* samkumar: There used to be code here handling the PRUS_EOF flag in
|
|
* the former flags parameter. I've removed this code.
|
|
*/
|
|
|
|
/*
|
|
* samkumar: The code below was previously wrapped in an if statement
|
|
* that checked that the INP_DROPPED flag in inp->inp_flags and the
|
|
* PRUS_NOTREADY flag in the former flags parameter were both clear.
|
|
* If either one was set, then tcp_output would not be called.
|
|
*
|
|
* The "more to come" functionality was previously triggered via the
|
|
* PRUS_MORETOCOME flag in the flags parameter to this function. Since
|
|
* that's the only flag that TCPlp uses here, I replaced the flags
|
|
* parameter with a "moretocome" parameter, which we check instead.
|
|
*/
|
|
if (moretocome)
|
|
tp->t_flags |= TF_MORETOCOME;
|
|
error = tcp_output(tp);
|
|
if (moretocome)
|
|
tp->t_flags &= ~TF_MORETOCOME;
|
|
|
|
/*
|
|
* samkumar: This is where the "if (!(flags & PRUS_OOB))" block would end.
|
|
* There used to be a large "else" block handling out-of-band data, but I
|
|
* removed that entire block since we do not support the urgent pointer in
|
|
* TCPlp.
|
|
*/
|
|
out:
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* After a receive, possibly send window update to peer.
|
|
*/
|
|
int
|
|
tcp_usr_rcvd(struct tcpcb* tp)
|
|
{
|
|
int error = 0;
|
|
|
|
/*
|
|
* samkumar: There used to be logic here that acquired locks, dealt with
|
|
* INP_TIMEWAIT and INP_DROPPED flags on inp->inp_flags, and added debug
|
|
* probes I've deleted that code, but left the following if block.
|
|
*/
|
|
if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) {
|
|
error = ECONNRESET;
|
|
goto out;
|
|
}
|
|
|
|
tcp_output(tp);
|
|
|
|
out:
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* samkumar: Removed the tcp_disconnect function. It is meant to be a
|
|
* "friendly" disconnect to complement the unceremonious "abort" functionality
|
|
* that is also provbided. The FreeBSD implementation called it from
|
|
* tcp_usr_close, which we removed (see the comment below for the reason why).
|
|
* It's not called from anywhere else, so I'm removing this function entirely.
|
|
*/
|
|
|
|
/*
|
|
* Mark the connection as being incapable of further output.
|
|
*/
|
|
/*
|
|
* samkumar: Modified to remove locking, socket/inpcb handling, and debug
|
|
* probes.
|
|
*/
|
|
int
|
|
tcp_usr_shutdown(struct tcpcb* tp)
|
|
{
|
|
int error = 0;
|
|
|
|
/*
|
|
* samkumar: replaced checks on the INP_TIMEWAIT and INP_DROPPED flags on
|
|
* inp->inp_flags with these checks on tp->t_state.
|
|
*/
|
|
if ((tp->t_state == TCPS_TIME_WAIT) || (tp->t_state == TCPS_CLOSED)) {
|
|
error = ECONNRESET;
|
|
goto out;
|
|
}
|
|
|
|
/* samkumar: replaced socantsendmore with tpcantsendmore */
|
|
tpcantsendmore(tp);
|
|
tcp_usrclosed(tp);
|
|
|
|
/*
|
|
* samkumar: replaced check on INP_DROPPED flag in inp->inp_flags with
|
|
* this check on tp->t_state.
|
|
*/
|
|
if (tp->t_state != TCPS_CLOSED)
|
|
error = tcp_output(tp);
|
|
|
|
out:
|
|
return (error);
|
|
}
|
|
|
|
|
|
/*
|
|
* User issued close, and wish to trail through shutdown states:
|
|
* if never received SYN, just forget it. If got a SYN from peer,
|
|
* but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
|
|
* If already got a FIN from peer, then almost done; go to LAST_ACK
|
|
* state. In all other cases, have already sent FIN to peer (e.g.
|
|
* after PRU_SHUTDOWN), and just have to play tedious game waiting
|
|
* for peer to send FIN or not respond to keep-alives, etc.
|
|
* We can let the user exit from the close as soon as the FIN is acked.
|
|
*/
|
|
/*
|
|
* Removed locking, TCP Offload, and socket/inpcb handling.
|
|
*/
|
|
static void
|
|
tcp_usrclosed(struct tcpcb *tp)
|
|
{
|
|
switch (tp->t_state) {
|
|
case TCPS_LISTEN:
|
|
tcp_state_change(tp, TCPS_CLOSED);
|
|
/* FALLTHROUGH */
|
|
case TCPS_CLOSED:
|
|
tp = tcp_close(tp);
|
|
tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL);
|
|
/*
|
|
* tcp_close() should never return NULL here as the socket is
|
|
* still open.
|
|
*/
|
|
KASSERT(tp != NULL,
|
|
("tcp_usrclosed: tcp_close() returned NULL"));
|
|
break;
|
|
|
|
case TCPS_SYN_SENT:
|
|
case TCPS_SYN_RECEIVED:
|
|
tp->t_flags |= TF_NEEDFIN;
|
|
break;
|
|
|
|
case TCPS_ESTABLISHED:
|
|
tcp_state_change(tp, TCPS_FIN_WAIT_1);
|
|
break;
|
|
|
|
case TCPS_CLOSE_WAIT:
|
|
tcp_state_change(tp, TCPS_LAST_ACK);
|
|
break;
|
|
}
|
|
if (tp->t_state >= TCPS_FIN_WAIT_2) {
|
|
/* samkumar: commented out the following "soisdisconnected" line. */
|
|
// soisdisconnected(tp->t_inpcb->inp_socket);
|
|
/* Prevent the connection hanging in FIN_WAIT_2 forever. */
|
|
if (tp->t_state == TCPS_FIN_WAIT_2) {
|
|
int timeout;
|
|
|
|
timeout = (tcp_fast_finwait2_recycle) ?
|
|
tcp_finwait2_timeout : TP_MAXIDLE(tp);
|
|
tcp_timer_activate(tp, TT_2MSL, timeout);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* samkumar: I removed the tcp_usr_close function. It was meant to be called in
|
|
* case the socket is closed. It calls tcp_disconnect if the underlying TCP
|
|
* connection is still alive when the socket is closed ("full TCP state").
|
|
* In TCPlp, we can't handle this because we want to free up the underlying
|
|
* memory immediately when the user deallocates a TCP connection, making it
|
|
* unavailable for the somewhat more ceremonious closing that tcp_disconnect
|
|
* would allow. The host system is expected to simply abort the connection if
|
|
* the application deallocates it.
|
|
*/
|
|
|
|
/*
|
|
* Abort the TCP. Drop the connection abruptly.
|
|
*/
|
|
/*
|
|
* samkumar: Modified to remove locking, socket/inpcb handling, and debug
|
|
* probes.
|
|
*/
|
|
void
|
|
tcp_usr_abort(struct tcpcb* tp)
|
|
{
|
|
/*
|
|
* If we still have full TCP state, and we're not dropped, drop.
|
|
*/
|
|
/*
|
|
* I replaced the checks on inp->inp_flags (which tested for the absence of
|
|
* INP_TIMEWAIT and INP_DROPPED flags), with the following checks on
|
|
* tp->t_state.
|
|
*/
|
|
if (tp->t_state != TCP6S_TIME_WAIT &&
|
|
tp->t_state != TCP6S_CLOSED) {
|
|
tcp_drop(tp, ECONNABORTED);
|
|
} else if (tp->t_state == TCPS_TIME_WAIT) { // samkumar: I added this clause
|
|
tp = tcp_close(tp);
|
|
tcplp_sys_connection_lost(tp, CONN_LOST_NORMAL);
|
|
}
|
|
}
|