Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/tcp_subr.c
Show First 20 Lines • Show All 103 Lines • ▼ Show 20 Lines | |||||
#include <netinet/tcp_fsm.h> | #include <netinet/tcp_fsm.h> | ||||
#include <netinet/tcp_seq.h> | #include <netinet/tcp_seq.h> | ||||
#include <netinet/tcp_timer.h> | #include <netinet/tcp_timer.h> | ||||
#include <netinet/tcp_var.h> | #include <netinet/tcp_var.h> | ||||
#include <netinet/tcp_log_buf.h> | #include <netinet/tcp_log_buf.h> | ||||
#include <netinet/tcp_syncache.h> | #include <netinet/tcp_syncache.h> | ||||
#include <netinet/tcp_hpts.h> | #include <netinet/tcp_hpts.h> | ||||
#include <netinet/cc/cc.h> | #include <netinet/cc/cc.h> | ||||
#ifdef INET6 | |||||
#include <netinet6/tcp6_var.h> | |||||
#endif | |||||
#include <netinet/tcpip.h> | #include <netinet/tcpip.h> | ||||
#include <netinet/tcp_fastopen.h> | #include <netinet/tcp_fastopen.h> | ||||
#ifdef TCPPCAP | #ifdef TCPPCAP | ||||
#include <netinet/tcp_pcap.h> | #include <netinet/tcp_pcap.h> | ||||
#endif | #endif | ||||
#ifdef TCPDEBUG | #ifdef TCPDEBUG | ||||
#include <netinet/tcp_debug.h> | #include <netinet/tcp_debug.h> | ||||
#endif | #endif | ||||
#ifdef TCP_OFFLOAD | #ifdef TCP_OFFLOAD | ||||
#include <netinet/tcp_offload.h> | #include <netinet/tcp_offload.h> | ||||
#endif | #endif | ||||
#include <netinet/udp.h> | #include <netinet/udp.h> | ||||
#include <netinet/udp_var.h> | #include <netinet/udp_var.h> | ||||
#ifdef INET6 | |||||
#include <netinet6/tcp6_var.h> | |||||
#endif | |||||
#include <netipsec/ipsec_support.h> | #include <netipsec/ipsec_support.h> | ||||
#include <machine/in_cksum.h> | #include <machine/in_cksum.h> | ||||
#include <crypto/siphash/siphash.h> | #include <crypto/siphash/siphash.h> | ||||
#include <security/mac/mac_framework.h> | #include <security/mac/mac_framework.h> | ||||
static ip6proto_ctlinput_t tcp6_ctlinput; | |||||
static udp_tun_icmp_t tcp6_ctlinput_viaudp; | |||||
VNET_DEFINE(int, tcp_mssdflt) = TCP_MSS; | VNET_DEFINE(int, tcp_mssdflt) = TCP_MSS; | ||||
#ifdef INET6 | #ifdef INET6 | ||||
VNET_DEFINE(int, tcp_v6mssdflt) = TCP6_MSS; | VNET_DEFINE(int, tcp_v6mssdflt) = TCP6_MSS; | ||||
#endif | #endif | ||||
#ifdef NETFLIX_EXP_DETECTION | #ifdef NETFLIX_EXP_DETECTION | ||||
/* Sack attack detection thresholds and such */ | /* Sack attack detection thresholds and such */ | ||||
SYSCTL_NODE(_net_inet_tcp, OID_AUTO, sack_attack, | SYSCTL_NODE(_net_inet_tcp, OID_AUTO, sack_attack, | ||||
▲ Show 20 Lines • Show All 223 Lines • ▼ Show 20 Lines | |||||
static int tcp_default_fb_init(struct tcpcb *tp); | static int tcp_default_fb_init(struct tcpcb *tp); | ||||
static void tcp_default_fb_fini(struct tcpcb *tp, int tcb_is_purged); | static void tcp_default_fb_fini(struct tcpcb *tp, int tcb_is_purged); | ||||
static int tcp_default_handoff_ok(struct tcpcb *tp); | static int tcp_default_handoff_ok(struct tcpcb *tp); | ||||
static struct inpcb *tcp_notify(struct inpcb *, int); | static struct inpcb *tcp_notify(struct inpcb *, int); | ||||
static struct inpcb *tcp_mtudisc_notify(struct inpcb *, int); | static struct inpcb *tcp_mtudisc_notify(struct inpcb *, int); | ||||
static struct inpcb *tcp_mtudisc(struct inpcb *, int); | static struct inpcb *tcp_mtudisc(struct inpcb *, int); | ||||
static char * tcp_log_addr(struct in_conninfo *inc, struct tcphdr *th, | static char * tcp_log_addr(struct in_conninfo *inc, struct tcphdr *th, | ||||
const void *ip4hdr, const void *ip6hdr); | const void *ip4hdr, const void *ip6hdr); | ||||
static ipproto_ctlinput_t tcp_ctlinput; | |||||
static udp_tun_icmp_t tcp_ctlinput_viaudp; | |||||
static struct tcp_function_block tcp_def_funcblk = { | static struct tcp_function_block tcp_def_funcblk = { | ||||
.tfb_tcp_block_name = "freebsd", | .tfb_tcp_block_name = "freebsd", | ||||
.tfb_tcp_output = tcp_default_output, | .tfb_tcp_output = tcp_default_output, | ||||
.tfb_tcp_do_segment = tcp_do_segment, | .tfb_tcp_do_segment = tcp_do_segment, | ||||
.tfb_tcp_ctloutput = tcp_default_ctloutput, | .tfb_tcp_ctloutput = tcp_default_ctloutput, | ||||
.tfb_tcp_handoff_ok = tcp_default_handoff_ok, | .tfb_tcp_handoff_ok = tcp_default_handoff_ok, | ||||
.tfb_tcp_fb_init = tcp_default_fb_init, | .tfb_tcp_fb_init = tcp_default_fb_init, | ||||
▲ Show 20 Lines • Show All 2,461 Lines • ▼ Show 20 Lines | if (!mtu) | ||||
mtu = ip_next_mtu(ntohs(ip->ip_len), 1); | mtu = ip_next_mtu(ntohs(ip->ip_len), 1); | ||||
if (mtu < V_tcp_minmss + sizeof(struct tcpiphdr)) | if (mtu < V_tcp_minmss + sizeof(struct tcpiphdr)) | ||||
mtu = V_tcp_minmss + sizeof(struct tcpiphdr); | mtu = V_tcp_minmss + sizeof(struct tcpiphdr); | ||||
return (mtu); | return (mtu); | ||||
} | } | ||||
static void | static void | ||||
tcp_ctlinput_with_port(int cmd, struct sockaddr *sa, void *vip, uint16_t port) | tcp_ctlinput_with_port(int cmd, struct sockaddr_in *sin, struct ip *ip, | ||||
uint16_t port) | |||||
melifaro: Q: why not pass the actual address instead of `struct sockaddr_in`? | |||||
{ | { | ||||
struct ip *ip = vip; | |||||
struct tcphdr *th; | struct tcphdr *th; | ||||
struct in_addr faddr; | |||||
struct inpcb *inp; | struct inpcb *inp; | ||||
struct tcpcb *tp; | struct tcpcb *tp; | ||||
struct inpcb *(*notify)(struct inpcb *, int) = tcp_notify; | struct inpcb *(*notify)(struct inpcb *, int) = tcp_notify; | ||||
struct icmp *icp; | struct icmp *icp; | ||||
struct in_conninfo inc; | struct in_conninfo inc; | ||||
tcp_seq icmp_tcp_seq; | tcp_seq icmp_tcp_seq; | ||||
int mtu; | int mtu; | ||||
faddr = ((struct sockaddr_in *)sa)->sin_addr; | if (sin->sin_addr.s_addr == INADDR_ANY) | ||||
if (sa->sa_family != AF_INET || faddr.s_addr == INADDR_ANY) | |||||
return; | return; | ||||
if (cmd == PRC_MSGSIZE) | if (cmd == PRC_MSGSIZE) | ||||
notify = tcp_mtudisc_notify; | notify = tcp_mtudisc_notify; | ||||
else if (V_icmp_may_rst && (cmd == PRC_UNREACH_ADMIN_PROHIB || | else if (V_icmp_may_rst && (cmd == PRC_UNREACH_ADMIN_PROHIB || | ||||
cmd == PRC_UNREACH_PORT || cmd == PRC_UNREACH_PROTOCOL || | cmd == PRC_UNREACH_PORT || cmd == PRC_UNREACH_PROTOCOL || | ||||
cmd == PRC_TIMXCEED_INTRANS) && ip) | cmd == PRC_TIMXCEED_INTRANS) && ip) | ||||
notify = tcp_drop_syn_sent; | notify = tcp_drop_syn_sent; | ||||
/* | /* | ||||
* Hostdead is ugly because it goes linearly through all PCBs. | * Hostdead is ugly because it goes linearly through all PCBs. | ||||
* XXX: We never get this from ICMP, otherwise it makes an | * XXX: We never get this from ICMP, otherwise it makes an | ||||
* excellent DoS attack on machines with many connections. | * excellent DoS attack on machines with many connections. | ||||
*/ | */ | ||||
else if (cmd == PRC_HOSTDEAD) | else if (cmd == PRC_HOSTDEAD) | ||||
ip = NULL; | ip = NULL; | ||||
else if ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0) | else if ((unsigned)cmd >= PRC_NCMDS || inetctlerrmap[cmd] == 0) | ||||
return; | return; | ||||
if (ip == NULL) { | if (ip == NULL) { | ||||
in_pcbnotifyall(&V_tcbinfo, faddr, inetctlerrmap[cmd], notify); | in_pcbnotifyall(&V_tcbinfo, sin->sin_addr, inetctlerrmap[cmd], | ||||
notify); | |||||
return; | return; | ||||
} | } | ||||
icp = (struct icmp *)((caddr_t)ip - offsetof(struct icmp, icmp_ip)); | icp = (struct icmp *)((caddr_t)ip - offsetof(struct icmp, icmp_ip)); | ||||
th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); | th = (struct tcphdr *)((caddr_t)ip + (ip->ip_hl << 2)); | ||||
inp = in_pcblookup(&V_tcbinfo, faddr, th->th_dport, ip->ip_src, | inp = in_pcblookup(&V_tcbinfo, sin->sin_addr, th->th_dport, ip->ip_src, | ||||
th->th_sport, INPLOOKUP_WLOCKPCB, NULL); | th->th_sport, INPLOOKUP_WLOCKPCB, NULL); | ||||
if (inp != NULL && PRC_IS_REDIRECT(cmd)) { | if (inp != NULL && PRC_IS_REDIRECT(cmd)) { | ||||
/* signal EHOSTDOWN, as it flushes the cached route */ | /* signal EHOSTDOWN, as it flushes the cached route */ | ||||
inp = (*notify)(inp, EHOSTDOWN); | inp = (*notify)(inp, EHOSTDOWN); | ||||
goto out; | goto out; | ||||
} | } | ||||
icmp_tcp_seq = th->th_seq; | icmp_tcp_seq = th->th_seq; | ||||
if (inp != NULL) { | if (inp != NULL) { | ||||
Show All 26 Lines | #endif | ||||
/* | /* | ||||
* Only process the offered MTU if it | * Only process the offered MTU if it | ||||
* is smaller than the current one. | * is smaller than the current one. | ||||
*/ | */ | ||||
if (mtu < tp->t_maxseg + | if (mtu < tp->t_maxseg + | ||||
sizeof(struct tcpiphdr)) { | sizeof(struct tcpiphdr)) { | ||||
bzero(&inc, sizeof(inc)); | bzero(&inc, sizeof(inc)); | ||||
inc.inc_faddr = faddr; | inc.inc_faddr = sin->sin_addr; | ||||
inc.inc_fibnum = | inc.inc_fibnum = | ||||
inp->inp_inc.inc_fibnum; | inp->inp_inc.inc_fibnum; | ||||
tcp_hc_updatemtu(&inc, mtu); | tcp_hc_updatemtu(&inc, mtu); | ||||
inp = tcp_mtudisc(inp, mtu); | inp = tcp_mtudisc(inp, mtu); | ||||
} | } | ||||
} else | } else | ||||
inp = (*notify)(inp, | inp = (*notify)(inp, | ||||
inetctlerrmap[cmd]); | inetctlerrmap[cmd]); | ||||
} | } | ||||
} | } | ||||
} else { | } else { | ||||
bzero(&inc, sizeof(inc)); | bzero(&inc, sizeof(inc)); | ||||
inc.inc_fport = th->th_dport; | inc.inc_fport = th->th_dport; | ||||
inc.inc_lport = th->th_sport; | inc.inc_lport = th->th_sport; | ||||
inc.inc_faddr = faddr; | inc.inc_faddr = sin->sin_addr; | ||||
inc.inc_laddr = ip->ip_src; | inc.inc_laddr = ip->ip_src; | ||||
syncache_unreach(&inc, icmp_tcp_seq, port); | syncache_unreach(&inc, icmp_tcp_seq, port); | ||||
} | } | ||||
out: | out: | ||||
if (inp != NULL) | if (inp != NULL) | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
} | } | ||||
void | static void | ||||
tcp_ctlinput(int cmd, struct sockaddr *sa, void *vip) | tcp_ctlinput(int cmd, struct sockaddr_in *sin, struct ip *ip) | ||||
{ | { | ||||
tcp_ctlinput_with_port(cmd, sa, vip, htons(0)); | tcp_ctlinput_with_port(cmd, sin, ip, htons(0)); | ||||
} | } | ||||
void | static void | ||||
tcp_ctlinput_viaudp(int cmd, struct sockaddr *sa, void *vip, void *unused) | tcp_ctlinput_viaudp(int cmd, struct sockaddr *sa, void *vip, void *unused) | ||||
{ | { | ||||
/* Its a tunneled TCP over UDP icmp */ | /* Its a tunneled TCP over UDP icmp */ | ||||
struct ip *outer_ip, *inner_ip; | struct ip *outer_ip, *inner_ip; | ||||
struct icmp *icmp; | struct icmp *icmp; | ||||
struct udphdr *udp; | struct udphdr *udp; | ||||
struct tcphdr *th, ttemp; | struct tcphdr *th, ttemp; | ||||
int i_hlen, o_len; | int i_hlen, o_len; | ||||
Show All 18 Lines | tcp_ctlinput_viaudp(int cmd, struct sockaddr *sa, void *vip, void *unused) | ||||
port = udp->uh_dport; | port = udp->uh_dport; | ||||
th = (struct tcphdr *)(udp + 1); | th = (struct tcphdr *)(udp + 1); | ||||
memcpy(&ttemp, th, sizeof(struct tcphdr)); | memcpy(&ttemp, th, sizeof(struct tcphdr)); | ||||
memcpy(udp, &ttemp, sizeof(struct tcphdr)); | memcpy(udp, &ttemp, sizeof(struct tcphdr)); | ||||
/* Now adjust down the size of the outer IP header */ | /* Now adjust down the size of the outer IP header */ | ||||
o_len -= sizeof(struct udphdr); | o_len -= sizeof(struct udphdr); | ||||
outer_ip->ip_len = htons(o_len); | outer_ip->ip_len = htons(o_len); | ||||
/* Now call in to the normal handling code */ | /* Now call in to the normal handling code */ | ||||
tcp_ctlinput_with_port(cmd, sa, vip, port); | tcp_ctlinput_with_port(cmd, (struct sockaddr_in *)sa, vip, port); | ||||
} | } | ||||
#endif /* INET */ | #endif /* INET */ | ||||
#ifdef INET6 | #ifdef INET6 | ||||
static inline int | static inline int | ||||
tcp6_next_pmtu(const struct icmp6_hdr *icmp6) | tcp6_next_pmtu(const struct icmp6_hdr *icmp6) | ||||
{ | { | ||||
int mtu = ntohl(icmp6->icmp6_mtu); | int mtu = ntohl(icmp6->icmp6_mtu); | ||||
/* | /* | ||||
* If no alternative MTU was proposed, or the proposed MTU was too | * If no alternative MTU was proposed, or the proposed MTU was too | ||||
* small, set to the min. | * small, set to the min. | ||||
*/ | */ | ||||
if (mtu < IPV6_MMTU) | if (mtu < IPV6_MMTU) | ||||
mtu = IPV6_MMTU - 8; /* XXXNP: what is the adjustment for? */ | mtu = IPV6_MMTU - 8; /* XXXNP: what is the adjustment for? */ | ||||
return (mtu); | return (mtu); | ||||
} | } | ||||
static void | static void | ||||
tcp6_ctlinput_with_port(int cmd, struct sockaddr *sa, void *d, uint16_t port) | tcp6_ctlinput_with_port(int cmd, struct sockaddr_in6 *sin6, | ||||
struct ip6ctlparam *ip6cp, uint16_t port) | |||||
{ | { | ||||
struct in6_addr *dst; | struct in6_addr *dst; | ||||
struct inpcb *(*notify)(struct inpcb *, int) = tcp_notify; | struct inpcb *(*notify)(struct inpcb *, int) = tcp_notify; | ||||
struct ip6_hdr *ip6; | struct ip6_hdr *ip6; | ||||
struct mbuf *m; | struct mbuf *m; | ||||
struct inpcb *inp; | struct inpcb *inp; | ||||
struct tcpcb *tp; | struct tcpcb *tp; | ||||
struct icmp6_hdr *icmp6; | struct icmp6_hdr *icmp6; | ||||
struct ip6ctlparam *ip6cp = NULL; | |||||
const struct sockaddr_in6 *sa6_src = NULL; | const struct sockaddr_in6 *sa6_src = NULL; | ||||
struct in_conninfo inc; | struct in_conninfo inc; | ||||
struct tcp_ports { | struct tcp_ports { | ||||
uint16_t th_sport; | uint16_t th_sport; | ||||
uint16_t th_dport; | uint16_t th_dport; | ||||
} t_ports; | } t_ports; | ||||
tcp_seq icmp_tcp_seq; | tcp_seq icmp_tcp_seq; | ||||
unsigned int mtu; | unsigned int mtu; | ||||
unsigned int off; | unsigned int off; | ||||
if (sa->sa_family != AF_INET6 || | |||||
sa->sa_len != sizeof(struct sockaddr_in6)) | |||||
return; | |||||
/* if the parameter is from icmp6, decode it. */ | /* if the parameter is from icmp6, decode it. */ | ||||
if (d != NULL) { | if (ip6cp != NULL) { | ||||
ip6cp = (struct ip6ctlparam *)d; | |||||
icmp6 = ip6cp->ip6c_icmp6; | icmp6 = ip6cp->ip6c_icmp6; | ||||
m = ip6cp->ip6c_m; | m = ip6cp->ip6c_m; | ||||
ip6 = ip6cp->ip6c_ip6; | ip6 = ip6cp->ip6c_ip6; | ||||
off = ip6cp->ip6c_off; | off = ip6cp->ip6c_off; | ||||
sa6_src = ip6cp->ip6c_src; | sa6_src = ip6cp->ip6c_src; | ||||
dst = ip6cp->ip6c_finaldst; | dst = ip6cp->ip6c_finaldst; | ||||
} else { | } else { | ||||
m = NULL; | m = NULL; | ||||
Show All 16 Lines | tcp6_ctlinput_with_port(int cmd, struct sockaddr_in6 *sin6, | ||||
* excellent DoS attack on machines with many connections. | * excellent DoS attack on machines with many connections. | ||||
*/ | */ | ||||
else if (cmd == PRC_HOSTDEAD) | else if (cmd == PRC_HOSTDEAD) | ||||
ip6 = NULL; | ip6 = NULL; | ||||
else if ((unsigned)cmd >= PRC_NCMDS || inet6ctlerrmap[cmd] == 0) | else if ((unsigned)cmd >= PRC_NCMDS || inet6ctlerrmap[cmd] == 0) | ||||
return; | return; | ||||
if (ip6 == NULL) { | if (ip6 == NULL) { | ||||
in6_pcbnotify(&V_tcbinfo, sa, 0, | in6_pcbnotify(&V_tcbinfo, sin6, 0, sa6_src, 0, cmd, NULL, | ||||
(const struct sockaddr *)sa6_src, | notify); | ||||
0, cmd, NULL, notify); | |||||
return; | return; | ||||
} | } | ||||
/* Check if we can safely get the ports from the tcp hdr */ | /* Check if we can safely get the ports from the tcp hdr */ | ||||
if (m == NULL || | if (m == NULL || | ||||
(m->m_pkthdr.len < | (m->m_pkthdr.len < | ||||
(int32_t) (off + sizeof(struct tcp_ports)))) { | (int32_t) (off + sizeof(struct tcp_ports)))) { | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | #endif | ||||
inc.inc6_laddr = ip6->ip6_src; | inc.inc6_laddr = ip6->ip6_src; | ||||
syncache_unreach(&inc, icmp_tcp_seq, port); | syncache_unreach(&inc, icmp_tcp_seq, port); | ||||
} | } | ||||
out: | out: | ||||
if (inp != NULL) | if (inp != NULL) | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
} | } | ||||
void | static void | ||||
tcp6_ctlinput(int cmd, struct sockaddr *sa, void *d) | tcp6_ctlinput(int cmd, struct sockaddr_in6 *sin6, struct ip6ctlparam *ctl) | ||||
{ | { | ||||
tcp6_ctlinput_with_port(cmd, sa, d, htons(0)); | tcp6_ctlinput_with_port(cmd, sin6, ctl, htons(0)); | ||||
} | } | ||||
void | static void | ||||
tcp6_ctlinput_viaudp(int cmd, struct sockaddr *sa, void *d, void *unused) | tcp6_ctlinput_viaudp(int cmd, struct sockaddr *sa, void *d, void *unused) | ||||
{ | { | ||||
struct ip6ctlparam *ip6cp; | struct ip6ctlparam *ip6cp; | ||||
struct mbuf *m; | struct mbuf *m; | ||||
struct udphdr *udp; | struct udphdr *udp; | ||||
uint16_t port; | uint16_t port; | ||||
ip6cp = (struct ip6ctlparam *)d; | ip6cp = (struct ip6ctlparam *)d; | ||||
m = m_pulldown(ip6cp->ip6c_m, ip6cp->ip6c_off, sizeof(struct udphdr), NULL); | m = m_pulldown(ip6cp->ip6c_m, ip6cp->ip6c_off, sizeof(struct udphdr), NULL); | ||||
if (m == NULL) { | if (m == NULL) { | ||||
return; | return; | ||||
} | } | ||||
udp = mtod(m, struct udphdr *); | udp = mtod(m, struct udphdr *); | ||||
if (ntohs(udp->uh_sport) != V_tcp_udp_tunneling_port) { | if (ntohs(udp->uh_sport) != V_tcp_udp_tunneling_port) { | ||||
return; | return; | ||||
} | } | ||||
port = udp->uh_dport; | port = udp->uh_dport; | ||||
m_adj(m, sizeof(struct udphdr)); | m_adj(m, sizeof(struct udphdr)); | ||||
if ((m->m_flags & M_PKTHDR) == 0) { | if ((m->m_flags & M_PKTHDR) == 0) { | ||||
ip6cp->ip6c_m->m_pkthdr.len -= sizeof(struct udphdr); | ip6cp->ip6c_m->m_pkthdr.len -= sizeof(struct udphdr); | ||||
} | } | ||||
/* Now call in to the normal handling code */ | /* Now call in to the normal handling code */ | ||||
tcp6_ctlinput_with_port(cmd, sa, d, port); | tcp6_ctlinput_with_port(cmd, (struct sockaddr_in6 *)sa, ip6cp, port); | ||||
} | } | ||||
#endif /* INET6 */ | #endif /* INET6 */ | ||||
static uint32_t | static uint32_t | ||||
tcp_keyed_hash(struct in_conninfo *inc, u_char *key, u_int len) | tcp_keyed_hash(struct in_conninfo *inc, u_char *key, u_int len) | ||||
{ | { | ||||
SIPHASH_CTX ctx; | SIPHASH_CTX ctx; | ||||
▲ Show 20 Lines • Show All 956 Lines • Show Last 20 Lines |
Q: why not pass the actual address instead of struct sockaddr_in?