Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F154224224
D29469.id86435.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
57 KB
Referenced Files
None
Subscribers
None
D29469.id86435.diff
View Options
diff --git a/share/man/man4/tcp.4 b/share/man/man4/tcp.4
--- a/share/man/man4/tcp.4
+++ b/share/man/man4/tcp.4
@@ -34,7 +34,7 @@
.\" From: @(#)tcp.4 8.1 (Berkeley) 6/5/93
.\" $FreeBSD$
.\"
-.Dd February 13, 2021
+.Dd March 28, 2021
.Dt TCP 4
.Os
.Sh NAME
@@ -329,6 +329,9 @@
This is typically used after a process or thread inherits a listen
socket from its parent, and sets its CPU affinity to a particular core.
.El
+.It Dv TCP_REMOTE_UDP_ENCAPS_PORT
+Set and get the remote UDP encapsulation port.
+It can only be set on a closed TCP socket.
.El
.Pp
The option level for the
@@ -704,6 +707,16 @@
template sampling rates when
.Xr stats 3
sampling is enabled.
+.It Va udp_tunneling_port
+The local UDP encapsulation port.
+A value of 0 indicates that UDP encapsulation is disabled.
+The default is 0.
+.It Va udp_tunneling_overhead
+The overhead taken into account when using UDP encapsulation.
+Since MSS clamping by middleboxes will most likely not work, values larger than
+8 (the size of the UDP header) are also supported.
+Supported values are between 8 and 1024.
+The default is 8.
.El
.Sh ERRORS
A socket operation may fail with one of the following errors returned:
diff --git a/sys/netinet/tcp.h b/sys/netinet/tcp.h
--- a/sys/netinet/tcp.h
+++ b/sys/netinet/tcp.h
@@ -183,6 +183,7 @@
#define TCP_RXTLS_MODE 42 /* Receive TLS mode */
#define TCP_CONGESTION 64 /* get/set congestion control algorithm */
#define TCP_CCALGOOPT 65 /* get/set cc algorithm specific options */
+#define TCP_REMOTE_UDP_ENCAPS_PORT 71 /* Enable TCP over UDP tunneling via the specified port */
#define TCP_DELACK 72 /* socket option for delayed ack */
#define TCP_FIN_IS_RST 73 /* A fin from the peer is treated has a RST */
#define TCP_LOG_LIMIT 74 /* Limit to number of records in tcp-log */
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -123,6 +123,7 @@
#ifdef TCP_OFFLOAD
#include <netinet/tcp_offload.h>
#endif
+#include <netinet/udp.h>
#include <netipsec/ipsec_support.h>
@@ -572,7 +573,7 @@
*/
#ifdef INET6
int
-tcp6_input(struct mbuf **mp, int *offp, int proto)
+tcp6_input_with_port(struct mbuf **mp, int *offp, int proto, uint16_t port)
{
struct mbuf *m;
struct in6_ifaddr *ia6;
@@ -602,12 +603,19 @@
}
*mp = m;
- return (tcp_input(mp, offp, proto));
+ return (tcp_input_with_port(mp, offp, proto, port));
+}
+
+int
+tcp6_input(struct mbuf **mp, int *offp, int proto)
+{
+
+ return(tcp6_input_with_port(mp, offp, proto, 0));
}
#endif /* INET6 */
int
-tcp_input(struct mbuf **mp, int *offp, int proto)
+tcp_input_with_port(struct mbuf **mp, int *offp, int proto, uint16_t port)
{
struct mbuf *m = *mp;
struct tcphdr *th = NULL;
@@ -663,6 +671,8 @@
ip6 = mtod(m, struct ip6_hdr *);
th = (struct tcphdr *)((caddr_t)ip6 + off0);
tlen = sizeof(*ip6) + ntohs(ip6->ip6_plen) - off0;
+ if (port)
+ goto skip6_csum;
if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID_IPV6) {
if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
th->th_sum = m->m_pkthdr.csum_data;
@@ -676,7 +686,7 @@
TCPSTAT_INC(tcps_rcvbadsum);
goto drop;
}
-
+ skip6_csum:
/*
* Be proactive about unspecified IPv6 address in source.
* As we use all-zero to indicate unbounded/unconnected pcb,
@@ -717,6 +727,8 @@
tlen = ntohs(ip->ip_len) - off0;
iptos = ip->ip_tos;
+ if (port)
+ goto skip_csum;
if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) {
if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
th->th_sum = m->m_pkthdr.csum_data;
@@ -746,8 +758,8 @@
ip->ip_v = IPVERSION;
ip->ip_hl = off0 >> 2;
}
-
- if (th->th_sum) {
+ skip_csum:
+ if (th->th_sum && (port == 0)) {
TCPSTAT_INC(tcps_rcvbadsum);
goto drop;
}
@@ -1005,6 +1017,11 @@
goto dropwithreset;
}
+ if ((tp->t_port != port) && (tp->t_state > TCPS_LISTEN)) {
+ rstreason = BANDLIM_RST_CLOSEDPORT;
+ goto dropwithreset;
+ }
+
#ifdef TCP_OFFLOAD
if (tp->t_flags & TF_TOE) {
tcp_offload_input(tp, m);
@@ -1076,7 +1093,7 @@
* NB: syncache_expand() doesn't unlock
* inp and tcpinfo locks.
*/
- rstreason = syncache_expand(&inc, &to, th, &so, m);
+ rstreason = syncache_expand(&inc, &to, th, &so, m, port);
if (rstreason < 0) {
/*
* A failing TCP MD5 signature comparison
@@ -1156,7 +1173,7 @@
* causes.
*/
if (thflags & TH_RST) {
- syncache_chkrst(&inc, th, m);
+ syncache_chkrst(&inc, th, m, port);
goto dropunlock;
}
/*
@@ -1178,7 +1195,7 @@
log(LOG_DEBUG, "%s; %s: Listen socket: "
"SYN|ACK invalid, segment rejected\n",
s, __func__);
- syncache_badack(&inc); /* XXX: Not needed! */
+ syncache_badack(&inc, port); /* XXX: Not needed! */
TCPSTAT_INC(tcps_badsyn);
rstreason = BANDLIM_RST_OPENPORT;
goto dropwithreset;
@@ -1335,7 +1352,8 @@
#endif
TCP_PROBE3(debug__input, tp, th, m);
tcp_dooptions(&to, optp, optlen, TO_SYN);
- if (syncache_add(&inc, &to, th, inp, &so, m, NULL, NULL, iptos))
+ if (syncache_add(&inc, &to, th, inp, &so, m, NULL, NULL, iptos,
+ port))
goto tfo_socket_result;
/*
@@ -1466,6 +1484,12 @@
return (newsize);
}
+int
+tcp_input(struct mbuf **mp, int *offp, int proto)
+{
+ return(tcp_input_with_port(mp, offp, proto, 0));
+}
+
void
tcp_handle_wakeup(struct tcpcb *tp, struct socket *so)
{
@@ -3670,11 +3694,13 @@
sizeof (struct ip6_hdr) + sizeof (struct tcphdr) :
sizeof (struct tcpiphdr);
#else
- const size_t min_protoh = sizeof(struct tcpiphdr);
+ size_t min_protoh = sizeof(struct tcpiphdr);
#endif
INP_WLOCK_ASSERT(tp->t_inpcb);
+ if (tp->t_port)
+ min_protoh += V_tcp_udp_tunneling_overhead;
if (mtuoffer != -1) {
KASSERT(offer == -1, ("%s: conflict", __func__));
offer = mtuoffer - min_protoh;
diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c
--- a/sys/netinet/tcp_output.c
+++ b/sys/netinet/tcp_output.c
@@ -101,6 +101,8 @@
#include <netipsec/ipsec_support.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
#include <machine/in_cksum.h>
#include <security/mac/mac_framework.h>
@@ -207,7 +209,7 @@
#endif
struct tcphdr *th;
u_char opt[TCP_MAXOLEN];
- unsigned ipoptlen, optlen, hdrlen;
+ unsigned ipoptlen, optlen, hdrlen, ulen;
#if defined(IPSEC) || defined(IPSEC_SUPPORT)
unsigned ipsec_optlen = 0;
#endif
@@ -216,6 +218,7 @@
struct sackhole *p;
int tso, mtu;
struct tcpopt to;
+ struct udphdr *udp = NULL;
unsigned int wanted_cookie = 0;
unsigned int dont_sendalot = 0;
#if 0
@@ -558,6 +561,7 @@
#endif
if ((tp->t_flags & TF_TSO) && V_tcp_do_tso && len > tp->t_maxseg &&
+ (tp->t_port == 0) &&
((tp->t_flags & TF_SIGNATURE) == 0) &&
tp->rcv_numsacks == 0 && sack_rxmit == 0 &&
ipoptlen == 0 && !(flags & TH_SYN))
@@ -800,6 +804,8 @@
/* Maximum segment size. */
if (flags & TH_SYN) {
to.to_mss = tcp_mssopt(&tp->t_inpcb->inp_inc);
+ if (tp->t_port)
+ to.to_mss -= V_tcp_udp_tunneling_overhead;
to.to_flags |= TOF_MSS;
/*
@@ -887,7 +893,14 @@
!(to.to_flags & TOF_FASTOPEN))
len = 0;
}
-
+ if (tp->t_port) {
+ if (V_tcp_udp_tunneling_port == 0) {
+ /* The port was removed?? */
+ SOCKBUF_UNLOCK(&so->so_snd);
+ return (EHOSTUNREACH);
+ }
+ hdrlen += sizeof(struct udphdr);
+ }
/*
* Adjust data length if insertion of options will
* bump the packet length beyond the t_maxseg length.
@@ -1140,8 +1153,17 @@
#ifdef INET6
if (isipv6) {
ip6 = mtod(m, struct ip6_hdr *);
- th = (struct tcphdr *)(ip6 + 1);
- tcpip_fillheaders(tp->t_inpcb, ip6, th);
+ if (tp->t_port) {
+ udp = (struct udphdr *)((caddr_t)ip6 + ipoptlen + sizeof(struct ip6_hdr));
+ udp->uh_sport = htons(V_tcp_udp_tunneling_port);
+ udp->uh_dport = tp->t_port;
+ ulen = hdrlen + len - sizeof(struct ip6_hdr);
+ udp->uh_ulen = htons(ulen);
+ th = (struct tcphdr *)(udp + 1);
+ } else {
+ th = (struct tcphdr *)(ip6 + 1);
+ }
+ tcpip_fillheaders(tp->t_inpcb, tp->t_port, ip6, th);
} else
#endif /* INET6 */
{
@@ -1149,8 +1171,16 @@
#ifdef TCPDEBUG
ipov = (struct ipovly *)ip;
#endif
- th = (struct tcphdr *)(ip + 1);
- tcpip_fillheaders(tp->t_inpcb, ip, th);
+ if (tp->t_port) {
+ udp = (struct udphdr *)((caddr_t)ip + ipoptlen + sizeof(struct ip));
+ udp->uh_sport = htons(V_tcp_udp_tunneling_port);
+ udp->uh_dport = tp->t_port;
+ ulen = hdrlen + len - sizeof(struct ip);
+ udp->uh_ulen = htons(ulen);
+ th = (struct tcphdr *)(udp + 1);
+ } else
+ th = (struct tcphdr *)(ip + 1);
+ tcpip_fillheaders(tp->t_inpcb, tp->t_port, ip, th);
}
/*
@@ -1309,7 +1339,6 @@
* checksum extended header and data.
*/
m->m_pkthdr.len = hdrlen + len; /* in6_cksum() need this */
- m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
if (to.to_flags & TOF_SIGNATURE) {
@@ -1336,9 +1365,19 @@
* There is no need to fill in ip6_plen right now.
* It will be filled later by ip6_output.
*/
- m->m_pkthdr.csum_flags = CSUM_TCP_IPV6;
- th->th_sum = in6_cksum_pseudo(ip6, sizeof(struct tcphdr) +
- optlen + len, IPPROTO_TCP, 0);
+ if (tp->t_port) {
+ m->m_pkthdr.csum_flags = CSUM_UDP_IPV6;
+ m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
+ udp->uh_sum = in6_cksum_pseudo(ip6, ulen, IPPROTO_UDP, 0);
+ th->th_sum = htons(0);
+ UDPSTAT_INC(udps_opackets);
+ } else {
+ m->m_pkthdr.csum_flags = CSUM_TCP_IPV6;
+ m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
+ th->th_sum = in6_cksum_pseudo(ip6,
+ sizeof(struct tcphdr) + optlen + len, IPPROTO_TCP,
+ 0);
+ }
}
#endif
#if defined(INET6) && defined(INET)
@@ -1346,9 +1385,20 @@
#endif
#ifdef INET
{
- m->m_pkthdr.csum_flags = CSUM_TCP;
- th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
- htons(sizeof(struct tcphdr) + IPPROTO_TCP + len + optlen));
+ if (tp->t_port) {
+ m->m_pkthdr.csum_flags = CSUM_UDP;
+ m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
+ udp->uh_sum = in_pseudo(ip->ip_src.s_addr,
+ ip->ip_dst.s_addr, htons(ulen + IPPROTO_UDP));
+ th->th_sum = htons(0);
+ UDPSTAT_INC(udps_opackets);
+ } else {
+ m->m_pkthdr.csum_flags = CSUM_TCP;
+ m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
+ th->th_sum = in_pseudo(ip->ip_src.s_addr,
+ ip->ip_dst.s_addr, htons(sizeof(struct tcphdr) +
+ IPPROTO_TCP + len + optlen));
+ }
/* IP version must be set here for ipv4/ipv6 checking later */
KASSERT(ip->ip_v == IPVERSION,
@@ -1473,8 +1523,10 @@
* NB: Don't set DF on small MTU/MSS to have a safe fallback.
*/
if (V_path_mtu_discovery && tp->t_maxseg > V_tcp_minmss) {
- ip->ip_off |= htons(IP_DF);
tp->t_flags2 |= TF2_PLPMTU_PMTUD;
+ if (tp->t_port == 0 || len < V_tcp_minmss) {
+ ip->ip_off |= htons(IP_DF);
+ }
} else {
tp->t_flags2 &= ~TF2_PLPMTU_PMTUD;
}
diff --git a/sys/netinet/tcp_stacks/bbr.c b/sys/netinet/tcp_stacks/bbr.c
--- a/sys/netinet/tcp_stacks/bbr.c
+++ b/sys/netinet/tcp_stacks/bbr.c
@@ -11969,14 +11969,10 @@
#endif
struct tcp_bbr *bbr;
struct tcphdr *th;
-#ifdef NETFLIX_TCPOUDP
struct udphdr *udp = NULL;
-#endif
u_char opt[TCP_MAXOLEN];
unsigned ipoptlen, optlen, hdrlen;
-#ifdef NETFLIX_TCPOUDP
unsigned ulen;
-#endif
uint32_t bbr_seq;
uint32_t delay_calc=0;
uint8_t doing_tlp = 0;
@@ -12991,10 +12987,8 @@
/* Maximum segment size. */
if (flags & TH_SYN) {
to.to_mss = tcp_mssopt(&inp->inp_inc);
-#ifdef NETFLIX_TCPOUDP
if (tp->t_port)
to.to_mss -= V_tcp_udp_tunneling_overhead;
-#endif
to.to_flags |= TOF_MSS;
/*
* On SYN or SYN|ACK transmits on TFO connections,
@@ -13063,7 +13057,6 @@
!(to.to_flags & TOF_FASTOPEN))
len = 0;
}
-#ifdef NETFLIX_TCPOUDP
if (tp->t_port) {
if (V_tcp_udp_tunneling_port == 0) {
/* The port was removed?? */
@@ -13072,7 +13065,6 @@
}
hdrlen += sizeof(struct udphdr);
}
-#endif
#ifdef INET6
if (isipv6)
ipoptlen = ip6_optlen(tp->t_inpcb);
@@ -13408,7 +13400,6 @@
#ifdef INET6
if (isipv6) {
ip6 = mtod(m, struct ip6_hdr *);
-#ifdef NETFLIX_TCPOUDP
if (tp->t_port) {
udp = (struct udphdr *)((caddr_t)ip6 + ipoptlen + sizeof(struct ip6_hdr));
udp->uh_sport = htons(V_tcp_udp_tunneling_port);
@@ -13417,17 +13408,9 @@
udp->uh_ulen = htons(ulen);
th = (struct tcphdr *)(udp + 1);
} else {
-#endif
th = (struct tcphdr *)(ip6 + 1);
-
-#ifdef NETFLIX_TCPOUDP
}
-#endif
- tcpip_fillheaders(inp,
-#ifdef NETFLIX_TCPOUDP
- tp->t_port,
-#endif
- ip6, th);
+ tcpip_fillheaders(inp, tp->t_port, ip6, th);
} else
#endif /* INET6 */
{
@@ -13435,7 +13418,6 @@
#ifdef TCPDEBUG
ipov = (struct ipovly *)ip;
#endif
-#ifdef NETFLIX_TCPOUDP
if (tp->t_port) {
udp = (struct udphdr *)((caddr_t)ip + ipoptlen + sizeof(struct ip));
udp->uh_sport = htons(V_tcp_udp_tunneling_port);
@@ -13443,14 +13425,10 @@
ulen = hdrlen + len - sizeof(struct ip);
udp->uh_ulen = htons(ulen);
th = (struct tcphdr *)(udp + 1);
- } else
-#endif
+ } else {
th = (struct tcphdr *)(ip + 1);
- tcpip_fillheaders(inp,
-#ifdef NETFLIX_TCPOUDP
- tp->t_port,
-#endif
- ip, th);
+ }
+ tcpip_fillheaders(inp, tp->t_port, ip, th);
}
/*
* If we are doing retransmissions, then snd_nxt will not reflect
@@ -13600,7 +13578,6 @@
* ip6_plen is not need to be filled now, and will be filled
* in ip6_output.
*/
-#ifdef NETFLIX_TCPOUDP
if (tp->t_port) {
m->m_pkthdr.csum_flags = CSUM_UDP_IPV6;
m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
@@ -13608,14 +13585,11 @@
th->th_sum = htons(0);
UDPSTAT_INC(udps_opackets);
} else {
-#endif
csum_flags = m->m_pkthdr.csum_flags = CSUM_TCP_IPV6;
m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
th->th_sum = in6_cksum_pseudo(ip6, sizeof(struct tcphdr) +
optlen + len, IPPROTO_TCP, 0);
-#ifdef NETFLIX_TCPOUDP
}
-#endif
}
#endif
#if defined(INET6) && defined(INET)
@@ -13623,7 +13597,6 @@
#endif
#ifdef INET
{
-#ifdef NETFLIX_TCPOUDP
if (tp->t_port) {
m->m_pkthdr.csum_flags = CSUM_UDP;
m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
@@ -13632,15 +13605,12 @@
th->th_sum = htons(0);
UDPSTAT_INC(udps_opackets);
} else {
-#endif
csum_flags = m->m_pkthdr.csum_flags = CSUM_TCP;
m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
th->th_sum = in_pseudo(ip->ip_src.s_addr,
ip->ip_dst.s_addr, htons(sizeof(struct tcphdr) +
IPPROTO_TCP + len + optlen));
-#ifdef NETFLIX_TCPOUDP
}
-#endif
/* IP version must be set here for ipv4/ipv6 checking later */
KASSERT(ip->ip_v == IPVERSION,
("%s: IP version incorrect: %d", __func__, ip->ip_v));
diff --git a/sys/netinet/tcp_stacks/rack.c b/sys/netinet/tcp_stacks/rack.c
--- a/sys/netinet/tcp_stacks/rack.c
+++ b/sys/netinet/tcp_stacks/rack.c
@@ -13008,10 +13008,8 @@
if (flags & TH_SYN) {
tp->snd_nxt = tp->iss;
to.to_mss = tcp_mssopt(&inp->inp_inc);
-#ifdef NETFLIX_TCPOUDP
if (tp->t_port)
to.to_mss -= V_tcp_udp_tunneling_overhead;
-#endif
to.to_flags |= TOF_MSS;
/*
@@ -13088,7 +13086,6 @@
!(to.to_flags & TOF_FASTOPEN))
len = 0;
}
-#ifdef NETFLIX_TCPOUDP
if (tp->t_port) {
if (V_tcp_udp_tunneling_port == 0) {
/* The port was removed?? */
@@ -13097,7 +13094,6 @@
}
hdrlen += sizeof(struct udphdr);
}
-#endif
#ifdef INET6
if (isipv6)
ipoptlen = ip6_optlen(tp->t_inpcb);
@@ -13372,7 +13368,6 @@
#ifdef INET6
if (isipv6) {
ip6 = mtod(m, struct ip6_hdr *);
-#ifdef NETFLIX_TCPOUDP
if (tp->t_port) {
udp = (struct udphdr *)((caddr_t)ip6 + ipoptlen + sizeof(struct ip6_hdr));
udp->uh_sport = htons(V_tcp_udp_tunneling_port);
@@ -13380,14 +13375,10 @@
ulen = hdrlen + len - sizeof(struct ip6_hdr);
udp->uh_ulen = htons(ulen);
th = (struct tcphdr *)(udp + 1);
- } else
-#endif
+ } else {
th = (struct tcphdr *)(ip6 + 1);
- tcpip_fillheaders(inp,
-#ifdef NETFLIX_TCPOUDP
- tp->t_port,
-#endif
- ip6, th);
+ }
+ tcpip_fillheaders(inp, tp->t_port, ip6, th);
} else
#endif /* INET6 */
{
@@ -13395,7 +13386,6 @@
#ifdef TCPDEBUG
ipov = (struct ipovly *)ip;
#endif
-#ifdef NETFLIX_TCPOUDP
if (tp->t_port) {
udp = (struct udphdr *)((caddr_t)ip + ipoptlen + sizeof(struct ip));
udp->uh_sport = htons(V_tcp_udp_tunneling_port);
@@ -13403,14 +13393,10 @@
ulen = hdrlen + len - sizeof(struct ip);
udp->uh_ulen = htons(ulen);
th = (struct tcphdr *)(udp + 1);
- } else
-#endif
+ } else {
th = (struct tcphdr *)(ip + 1);
- tcpip_fillheaders(inp,
-#ifdef NETFLIX_TCPOUDP
- tp->t_port,
-#endif
- ip, th);
+ }
+ tcpip_fillheaders(inp, tp->t_port, ip, th);
}
/*
* Fill in fields, remembering maximum advertised window for use in
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -126,6 +126,8 @@
#ifdef TCP_OFFLOAD
#include <netinet/tcp_offload.h>
#endif
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
#include <netipsec/ipsec_support.h>
@@ -501,6 +503,80 @@
}
}
+static void
+tcp_recv_udp_tunneled_packet(struct mbuf *m, int off, struct inpcb *inp,
+ const struct sockaddr *sa, void *ctx)
+{
+ struct ip *iph;
+#ifdef INET6
+ struct ip6_hdr *ip6;
+#endif
+ struct udphdr *uh;
+ struct tcphdr *th;
+ int thlen;
+ uint16_t port;
+
+ TCPSTAT_INC(tcps_tunneled_pkts);
+ if ((m->m_flags & M_PKTHDR) == 0) {
+ /* Can't handle one that is not a pkt hdr */
+ TCPSTAT_INC(tcps_tunneled_errs);
+ goto out;
+ }
+ thlen = sizeof(struct tcphdr);
+ if (m->m_len < off + sizeof(struct udphdr) + thlen &&
+ (m = m_pullup(m, off + sizeof(struct udphdr) + thlen)) == NULL) {
+ TCPSTAT_INC(tcps_tunneled_errs);
+ goto out;
+ }
+ iph = mtod(m, struct ip *);
+ uh = (struct udphdr *)((caddr_t)iph + off);
+ th = (struct tcphdr *)(uh + 1);
+ thlen = th->th_off << 2;
+ if (m->m_len < off + sizeof(struct udphdr) + thlen) {
+ m = m_pullup(m, off + sizeof(struct udphdr) + thlen);
+ if (m == NULL) {
+ TCPSTAT_INC(tcps_tunneled_errs);
+ goto out;
+ } else {
+ iph = mtod(m, struct ip *);
+ uh = (struct udphdr *)((caddr_t)iph + off);
+ th = (struct tcphdr *)(uh + 1);
+ }
+ }
+ m->m_pkthdr.tcp_tun_port = port = uh->uh_sport;
+ bcopy(th, uh, m->m_len - off);
+ m->m_len -= sizeof(struct udphdr);
+ m->m_pkthdr.len -= sizeof(struct udphdr);
+ /*
+ * We use the same algorithm for
+ * both UDP and TCP for c-sum. So
+ * the code in tcp_input will skip
+ * the checksum. So we do nothing
+ * with the flag (m->m_pkthdr.csum_flags).
+ */
+ switch (iph->ip_v) {
+#ifdef INET
+ case IPVERSION:
+ iph->ip_len = htons(ntohs(iph->ip_len) - sizeof(struct udphdr));
+ tcp_input_with_port(&m, &off, IPPROTO_TCP, port);
+ break;
+#endif
+#ifdef INET6
+ case IPV6_VERSION >> 4:
+ ip6 = mtod(m, struct ip6_hdr *);
+ ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - sizeof(struct udphdr));
+ tcp6_input_with_port(&m, &off, IPPROTO_TCP, port);
+ break;
+#endif
+ default:
+ goto out;
+ break;
+ }
+ return;
+out:
+ m_freem(m);
+}
+
static int
sysctl_net_inet_default_tcp_functions(SYSCTL_HANDLER_ARGS)
{
@@ -598,6 +674,183 @@
NULL, 0, sysctl_net_inet_list_available, "A",
"list available TCP Function sets");
+VNET_DEFINE(int, tcp_udp_tunneling_port) = TCP_TUNNELING_PORT_DEFAULT;
+
+#ifdef INET
+VNET_DEFINE(struct socket *, udp4_tun_socket) = NULL;
+#define V_udp4_tun_socket VNET(udp4_tun_socket)
+#endif
+#ifdef INET6
+VNET_DEFINE(struct socket *, udp6_tun_socket) = NULL;
+#define V_udp6_tun_socket VNET(udp6_tun_socket)
+#endif
+
+static void
+tcp_over_udp_stop(void)
+{
+ /*
+ * This function assumes sysctl caller holds inp_rinfo_lock()
+ * for writting!
+ */
+#ifdef INET
+ if (V_udp4_tun_socket != NULL) {
+ soclose(V_udp4_tun_socket);
+ V_udp4_tun_socket = NULL;
+ }
+#endif
+#ifdef INET6
+ if (V_udp6_tun_socket != NULL) {
+ soclose(V_udp6_tun_socket);
+ V_udp6_tun_socket = NULL;
+ }
+#endif
+}
+
+static int
+tcp_over_udp_start(void)
+{
+ uint16_t port;
+ int ret;
+#ifdef INET
+ struct sockaddr_in sin;
+#endif
+#ifdef INET6
+ struct sockaddr_in6 sin6;
+#endif
+ /*
+ * This function assumes sysctl caller holds inp_info_rlock()
+ * for writting!
+ */
+ port = V_tcp_udp_tunneling_port;
+ if (ntohs(port) == 0) {
+ /* Must have a port set */
+ return (EINVAL);
+ }
+#ifdef INET
+ if (V_udp4_tun_socket != NULL) {
+ /* Already running -- must stop first */
+ return (EALREADY);
+ }
+#endif
+#ifdef INET6
+ if (V_udp6_tun_socket != NULL) {
+ /* Already running -- must stop first */
+ return (EALREADY);
+ }
+#endif
+#ifdef INET
+ if ((ret = socreate(PF_INET, &V_udp4_tun_socket,
+ SOCK_DGRAM, IPPROTO_UDP,
+ curthread->td_ucred, curthread))) {
+ tcp_over_udp_stop();
+ return (ret);
+ }
+ /* Call the special UDP hook. */
+ if ((ret = udp_set_kernel_tunneling(V_udp4_tun_socket,
+ tcp_recv_udp_tunneled_packet,
+ tcp_ctlinput_viaudp,
+ NULL))) {
+ tcp_over_udp_stop();
+ return (ret);
+ }
+ /* Ok, we have a socket, bind it to the port. */
+ memset(&sin, 0, sizeof(struct sockaddr_in));
+ sin.sin_len = sizeof(struct sockaddr_in);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ if ((ret = sobind(V_udp4_tun_socket,
+ (struct sockaddr *)&sin, curthread))) {
+ tcp_over_udp_stop();
+ return (ret);
+ }
+#endif
+#ifdef INET6
+ if ((ret = socreate(PF_INET6, &V_udp6_tun_socket,
+ SOCK_DGRAM, IPPROTO_UDP,
+ curthread->td_ucred, curthread))) {
+ tcp_over_udp_stop();
+ return (ret);
+ }
+ /* Call the special UDP hook. */
+ if ((ret = udp_set_kernel_tunneling(V_udp6_tun_socket,
+ tcp_recv_udp_tunneled_packet,
+ tcp6_ctlinput_viaudp,
+ NULL))) {
+ tcp_over_udp_stop();
+ return (ret);
+ }
+ /* Ok, we have a socket, bind it to the port. */
+ memset(&sin6, 0, sizeof(struct sockaddr_in6));
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(port);
+ if ((ret = sobind(V_udp6_tun_socket,
+ (struct sockaddr *)&sin6, curthread))) {
+ tcp_over_udp_stop();
+ return (ret);
+ }
+#endif
+ return (0);
+}
+
+static int
+sysctl_net_inet_tcp_udp_tunneling_port_check(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ uint32_t old, new;
+
+ old = V_tcp_udp_tunneling_port;
+ new = old;
+ error = sysctl_handle_int(oidp, &new, 0, req);
+ if ((error == 0) &&
+ (req->newptr != NULL)) {
+ if ((new < TCP_TUNNELING_PORT_MIN) ||
+ (new > TCP_TUNNELING_PORT_MAX)) {
+ error = EINVAL;
+ } else {
+ V_tcp_udp_tunneling_port = new;
+ if (old != 0) {
+ tcp_over_udp_stop();
+ }
+ if (new != 0) {
+ error = tcp_over_udp_start();
+ }
+ }
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_net_inet_tcp, OID_AUTO, udp_tunneling_port,
+ CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ &VNET_NAME(tcp_udp_tunneling_port),
+ 0, &sysctl_net_inet_tcp_udp_tunneling_port_check, "IU",
+ "Tunneling port for tcp over udp");
+
+VNET_DEFINE(int, tcp_udp_tunneling_overhead) = TCP_TUNNELING_OVERHEAD_DEFAULT;
+
+static int
+sysctl_net_inet_tcp_udp_tunneling_overhead_check(SYSCTL_HANDLER_ARGS)
+{
+ int error, new;
+
+ new = V_tcp_udp_tunneling_overhead;
+ error = sysctl_handle_int(oidp, &new, 0, req);
+ if (error == 0 && req->newptr) {
+ if ((new < TCP_TUNNELING_OVERHEAD_MIN) ||
+ (new > TCP_TUNNELING_OVERHEAD_MAX))
+ error = EINVAL;
+ else
+ V_tcp_udp_tunneling_overhead = new;
+ }
+ return (error);
+}
+
+SYSCTL_PROC(_net_inet_tcp, OID_AUTO, udp_tunneling_overhead,
+ CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ &VNET_NAME(tcp_udp_tunneling_overhead),
+ 0, &sysctl_net_inet_tcp_udp_tunneling_overhead_check, "IU",
+ "MSS reduction when using tcp over udp");
+
/*
* Exports one (struct tcp_function_info) for each alias/name.
*/
@@ -1314,7 +1567,7 @@
* of the tcpcb each time to conserve mbufs.
*/
void
-tcpip_fillheaders(struct inpcb *inp, void *ip_ptr, void *tcp_ptr)
+tcpip_fillheaders(struct inpcb *inp, uint16_t port, void *ip_ptr, void *tcp_ptr)
{
struct tcphdr *th = (struct tcphdr *)tcp_ptr;
@@ -1329,7 +1582,10 @@
(inp->inp_flow & IPV6_FLOWINFO_MASK);
ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) |
(IPV6_VERSION & IPV6_VERSION_MASK);
- ip6->ip6_nxt = IPPROTO_TCP;
+ if (port == 0)
+ ip6->ip6_nxt = IPPROTO_TCP;
+ else
+ ip6->ip6_nxt = IPPROTO_UDP;
ip6->ip6_plen = htons(sizeof(struct tcphdr));
ip6->ip6_src = inp->in6p_laddr;
ip6->ip6_dst = inp->in6p_faddr;
@@ -1351,7 +1607,10 @@
ip->ip_off = 0;
ip->ip_ttl = inp->inp_ip_ttl;
ip->ip_sum = 0;
- ip->ip_p = IPPROTO_TCP;
+ if (port == 0)
+ ip->ip_p = IPPROTO_TCP;
+ else
+ ip->ip_p = IPPROTO_UDP;
ip->ip_src = inp->inp_laddr;
ip->ip_dst = inp->inp_faddr;
}
@@ -1381,7 +1640,7 @@
t = malloc(sizeof(*t), M_TEMP, M_NOWAIT);
if (t == NULL)
return (NULL);
- tcpip_fillheaders(inp, (void *)&t->tt_ipgen, (void *)&t->tt_t);
+ tcpip_fillheaders(inp, 0, (void *)&t->tt_ipgen, (void *)&t->tt_t);
return (t);
}
@@ -1407,14 +1666,16 @@
struct inpcb *inp;
struct ip *ip;
struct mbuf *optm;
+ struct udphdr *uh = NULL;
struct tcphdr *nth;
u_char *optp;
#ifdef INET6
struct ip6_hdr *ip6;
int isipv6;
#endif /* INET6 */
- int optlen, tlen, win;
+ int optlen, tlen, win, ulen;
bool incl_opts;
+ uint16_t port;
KASSERT(tp != NULL || m != NULL, ("tcp_respond: tp and m both NULL"));
NET_EPOCH_ASSERT();
@@ -1432,6 +1693,19 @@
} else
inp = NULL;
+ if (m != NULL) {
+#ifdef INET6
+ if (isipv6 && ip6 && (ip6->ip6_nxt == IPPROTO_UDP))
+ port = m->m_pkthdr.tcp_tun_port;
+ else
+#endif
+ if (ip && (ip->ip_p == IPPROTO_UDP))
+ port = m->m_pkthdr.tcp_tun_port;
+ else
+ port = 0;
+ } else
+ port = tp->t_port;
+
incl_opts = false;
win = 0;
if (tp != NULL) {
@@ -1454,16 +1728,30 @@
sizeof(struct ip6_hdr));
ip6 = mtod(m, struct ip6_hdr *);
nth = (struct tcphdr *)(ip6 + 1);
+ if (port) {
+ /* Insert a UDP header */
+ uh = (struct udphdr *)nth;
+ uh->uh_sport = htons(V_tcp_udp_tunneling_port);
+ uh->uh_dport = port;
+ nth = (struct tcphdr *)(uh + 1);
+ }
} else
#endif /* INET6 */
{
bcopy((caddr_t)ip, mtod(m, caddr_t), sizeof(struct ip));
ip = mtod(m, struct ip *);
nth = (struct tcphdr *)(ip + 1);
+ if (port) {
+ /* Insert a UDP header */
+ uh = (struct udphdr *)nth;
+ uh->uh_sport = htons(V_tcp_udp_tunneling_port);
+ uh->uh_dport = port;
+ nth = (struct tcphdr *)(uh + 1);
+ }
}
bcopy((caddr_t)th, (caddr_t)nth, sizeof(struct tcphdr));
flags = TH_ACK;
- } else if (!M_WRITABLE(m)) {
+ } else if ((!M_WRITABLE(m)) || (port != 0)) {
struct mbuf *n;
/* Can't reuse 'm', allocate a new mbuf. */
@@ -1489,6 +1777,13 @@
ip6 = mtod(n, struct ip6_hdr *);
xchg(ip6->ip6_dst, ip6->ip6_src, struct in6_addr);
nth = (struct tcphdr *)(ip6 + 1);
+ if (port) {
+ /* Insert a UDP header */
+ uh = (struct udphdr *)nth;
+ uh->uh_sport = htons(V_tcp_udp_tunneling_port);
+ uh->uh_dport = port;
+ nth = (struct tcphdr *)(uh + 1);
+ }
} else
#endif /* INET6 */
{
@@ -1496,6 +1791,13 @@
ip = mtod(n, struct ip *);
xchg(ip->ip_dst.s_addr, ip->ip_src.s_addr, uint32_t);
nth = (struct tcphdr *)(ip + 1);
+ if (port) {
+ /* Insert a UDP header */
+ uh = (struct udphdr *)nth;
+ uh->uh_sport = htons(V_tcp_udp_tunneling_port);
+ uh->uh_dport = port;
+ nth = (struct tcphdr *)(uh + 1);
+ }
}
bcopy((caddr_t)th, (caddr_t)nth, sizeof(struct tcphdr));
xchg(nth->th_dport, nth->th_sport, uint16_t);
@@ -1544,6 +1846,8 @@
#ifdef INET
tlen = sizeof (struct tcpiphdr);
#endif
+ if (port)
+ tlen += sizeof (struct udphdr);
#ifdef INVARIANTS
m->m_len = 0;
KASSERT(M_TRAILINGSPACE(m) >= tlen,
@@ -1587,9 +1891,16 @@
optlen = 0;
#ifdef INET6
if (isipv6) {
+ if (uh) {
+ ulen = tlen - sizeof(struct ip6_hdr);
+ uh->uh_ulen = htons(ulen);
+ }
ip6->ip6_flow = 0;
ip6->ip6_vfc = IPV6_VERSION;
- ip6->ip6_nxt = IPPROTO_TCP;
+ if (port)
+ ip6->ip6_nxt = IPPROTO_UDP;
+ else
+ ip6->ip6_nxt = IPPROTO_TCP;
ip6->ip6_plen = htons(tlen - sizeof(*ip6));
}
#endif
@@ -1598,8 +1909,17 @@
#endif
#ifdef INET
{
+ if (uh) {
+ ulen = tlen - sizeof(struct ip);
+ uh->uh_ulen = htons(ulen);
+ }
ip->ip_len = htons(tlen);
ip->ip_ttl = V_ip_defttl;
+ if (port) {
+ ip->ip_p = IPPROTO_UDP;
+ } else {
+ ip->ip_p = IPPROTO_TCP;
+ }
if (V_path_mtu_discovery)
ip->ip_off |= htons(IP_DF);
}
@@ -1643,12 +1963,19 @@
}
#endif
- m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
#ifdef INET6
if (isipv6) {
- m->m_pkthdr.csum_flags = CSUM_TCP_IPV6;
- nth->th_sum = in6_cksum_pseudo(ip6,
- tlen - sizeof(struct ip6_hdr), IPPROTO_TCP, 0);
+ if (port) {
+ m->m_pkthdr.csum_flags = CSUM_UDP_IPV6;
+ m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
+ uh->uh_sum = in6_cksum_pseudo(ip6, ulen, IPPROTO_UDP, 0);
+ nth->th_sum = 0;
+ } else {
+ m->m_pkthdr.csum_flags = CSUM_TCP_IPV6;
+ m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
+ nth->th_sum = in6_cksum_pseudo(ip6,
+ tlen - sizeof(struct ip6_hdr), IPPROTO_TCP, 0);
+ }
ip6->ip6_hlim = in6_selecthlim(tp != NULL ? tp->t_inpcb :
NULL, NULL);
}
@@ -1658,9 +1985,18 @@
#endif
#ifdef INET
{
- m->m_pkthdr.csum_flags = CSUM_TCP;
- nth->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
- htons((u_short)(tlen - sizeof(struct ip) + ip->ip_p)));
+ if (port) {
+ uh->uh_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
+ htons(ulen + IPPROTO_UDP));
+ m->m_pkthdr.csum_flags = CSUM_UDP;
+ m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
+ nth->th_sum = 0;
+ } else {
+ m->m_pkthdr.csum_flags = CSUM_TCP;
+ m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
+ nth->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
+ htons((u_short)(tlen - sizeof(struct ip) + ip->ip_p)));
+ }
}
#endif /* INET */
#ifdef TCPDEBUG
@@ -2460,8 +2796,8 @@
#endif /* INET6 */
#ifdef INET
-void
-tcp_ctlinput(int cmd, struct sockaddr *sa, void *vip)
+static void
+tcp_ctlinput_with_port(int cmd, struct sockaddr *sa, void *vip, uint16_t port)
{
struct ip *ip = vip;
struct tcphdr *th;
@@ -2515,6 +2851,9 @@
!(inp->inp_flags & INP_DROPPED) &&
!(inp->inp_socket == NULL)) {
tp = intotcpcb(inp);
+ if (tp->t_port != port) {
+ goto out;
+ }
if (SEQ_GEQ(ntohl(icmp_tcp_seq), tp->snd_una) &&
SEQ_LT(ntohl(icmp_tcp_seq), tp->snd_max)) {
if (cmd == PRC_MSGSIZE) {
@@ -2561,17 +2900,61 @@
inc.inc_lport = th->th_sport;
inc.inc_faddr = faddr;
inc.inc_laddr = ip->ip_src;
- syncache_unreach(&inc, icmp_tcp_seq);
+ syncache_unreach(&inc, icmp_tcp_seq, port);
}
out:
if (inp != NULL)
INP_WUNLOCK(inp);
}
+
+void
+tcp_ctlinput(int cmd, struct sockaddr *sa, void *vip)
+{
+ tcp_ctlinput_with_port(cmd, sa, vip, htons(0));
+}
+
+void
+tcp_ctlinput_viaudp(int cmd, struct sockaddr *sa, void *vip, void *unused)
+{
+ /* Its a tunneled TCP over UDP icmp */
+ struct ip *outer_ip, *inner_ip;
+ struct icmp *icmp;
+ struct udphdr *udp;
+ struct tcphdr *th, ttemp;
+ int i_hlen, o_len;
+ uint16_t port;
+
+ inner_ip = (struct ip *)vip;
+ icmp = (struct icmp *)((caddr_t)inner_ip -
+ (sizeof(struct icmp) - sizeof(struct ip)));
+ outer_ip = (struct ip *)((caddr_t)icmp - sizeof(struct ip));
+ i_hlen = inner_ip->ip_hl << 2;
+ o_len = ntohs(outer_ip->ip_len);
+ if (o_len <
+ (sizeof(struct ip) + 8 + i_hlen + sizeof(struct udphdr) + offsetof(struct tcphdr, th_ack))) {
+ /* Not enough data present */
+ return;
+ }
+ /* Ok lets strip out the inner udphdr header by copying up on top of it the tcp hdr */
+ udp = (struct udphdr *)(((caddr_t)inner_ip) + i_hlen);
+ if (ntohs(udp->uh_sport) != V_tcp_udp_tunneling_port) {
+ return;
+ }
+ port = udp->uh_dport;
+ th = (struct tcphdr *)(udp + 1);
+ memcpy(&ttemp, th, sizeof(struct tcphdr));
+ memcpy(udp, &ttemp, sizeof(struct tcphdr));
+ /* Now adjust down the size of the outer IP header */
+ o_len -= sizeof(struct udphdr);
+ outer_ip->ip_len = htons(o_len);
+ /* Now call in to the normal handling code */
+ tcp_ctlinput_with_port(cmd, sa, vip, port);
+}
#endif /* INET */
#ifdef INET6
-void
-tcp6_ctlinput(int cmd, struct sockaddr *sa, void *d)
+static void
+tcp6_ctlinput_with_port(int cmd, struct sockaddr *sa, void *d, uint16_t port)
{
struct in6_addr *dst;
struct inpcb *(*notify)(struct inpcb *, int) = tcp_notify;
@@ -2661,6 +3044,9 @@
!(inp->inp_flags & INP_DROPPED) &&
!(inp->inp_socket == NULL)) {
tp = intotcpcb(inp);
+ if (tp->t_port != port) {
+ goto out;
+ }
if (SEQ_GEQ(ntohl(icmp_tcp_seq), tp->snd_una) &&
SEQ_LT(ntohl(icmp_tcp_seq), tp->snd_max)) {
if (cmd == PRC_MSGSIZE) {
@@ -2710,12 +3096,45 @@
inc.inc_lport = t_ports.th_sport;
inc.inc6_faddr = *dst;
inc.inc6_laddr = ip6->ip6_src;
- syncache_unreach(&inc, icmp_tcp_seq);
+ syncache_unreach(&inc, icmp_tcp_seq, port);
}
out:
if (inp != NULL)
INP_WUNLOCK(inp);
}
+
+void
+tcp6_ctlinput(int cmd, struct sockaddr *sa, void *d)
+{
+ tcp6_ctlinput_with_port(cmd, sa, d, htons(0));
+}
+
+void
+tcp6_ctlinput_viaudp(int cmd, struct sockaddr *sa, void *d, void *unused)
+{
+ struct ip6ctlparam *ip6cp;
+ struct mbuf *m;
+ struct udphdr *udp;
+ uint16_t port;
+
+ ip6cp = (struct ip6ctlparam *)d;
+ m = m_pulldown(ip6cp->ip6c_m, ip6cp->ip6c_off, sizeof(struct udphdr), NULL);
+ if (m == NULL) {
+ return;
+ }
+ udp = mtod(m, struct udphdr *);
+ if (ntohs(udp->uh_sport) != V_tcp_udp_tunneling_port) {
+ return;
+ }
+ port = udp->uh_dport;
+ m_adj(m, sizeof(struct udphdr));
+ if ((m->m_flags & M_PKTHDR) == 0) {
+ ip6cp->ip6c_m->m_pkthdr.len -= sizeof(struct udphdr);
+ }
+ /* Now call in to the normal handling code */
+ tcp6_ctlinput_with_port(cmd, sa, d, port);
+}
+
#endif /* INET6 */
static uint32_t
@@ -3448,11 +3867,13 @@
tcp_inptoxtp(const struct inpcb *inp, struct xtcpcb *xt)
{
struct tcpcb *tp = intotcpcb(inp);
+ struct tcptw *tw = intotw(inp);
sbintime_t now;
bzero(xt, sizeof(*xt));
if (inp->inp_flags & INP_TIMEWAIT) {
xt->t_state = TCPS_TIME_WAIT;
+ xt->xt_encaps_port = tw->t_port;
} else {
xt->t_state = tp->t_state;
xt->t_logstate = tp->t_logstate;
@@ -3484,6 +3905,7 @@
#undef COPYTIMER
xt->t_rcvtime = 1000 * (ticks - tp->t_rcvtime) / hz;
+ xt->xt_encaps_port = tp->t_port;
bcopy(tp->t_fb->tfb_tcp_block_name, xt->xt_stack,
TCP_FUNCTION_NAME_LEN_MAX);
bcopy(CC_ALGO(tp)->name, xt->xt_cc,
diff --git a/sys/netinet/tcp_syncache.h b/sys/netinet/tcp_syncache.h
--- a/sys/netinet/tcp_syncache.h
+++ b/sys/netinet/tcp_syncache.h
@@ -40,14 +40,15 @@
#ifdef VIMAGE
void syncache_destroy(void);
#endif
-void syncache_unreach(struct in_conninfo *, tcp_seq);
+void syncache_unreach(struct in_conninfo *, tcp_seq, uint16_t);
int syncache_expand(struct in_conninfo *, struct tcpopt *,
- struct tcphdr *, struct socket **, struct mbuf *);
+ struct tcphdr *, struct socket **, struct mbuf *, uint16_t);
int syncache_add(struct in_conninfo *, struct tcpopt *,
struct tcphdr *, struct inpcb *, struct socket **, struct mbuf *,
- void *, void *, uint8_t);
-void syncache_chkrst(struct in_conninfo *, struct tcphdr *, struct mbuf *);
-void syncache_badack(struct in_conninfo *);
+ void *, void *, uint8_t, uint16_t);
+void syncache_chkrst(struct in_conninfo *, struct tcphdr *, struct mbuf *,
+ uint16_t);
+void syncache_badack(struct in_conninfo *, uint16_t);
int syncache_pcblist(struct sysctl_req *);
struct syncache {
@@ -55,6 +56,7 @@
struct in_conninfo sc_inc; /* addresses */
int sc_rxttime; /* retransmit time */
u_int16_t sc_rxmits; /* retransmit counter */
+ u_int16_t sc_port; /* remote UDP encaps port */
u_int32_t sc_tsreflect; /* timestamp to reflect */
u_int32_t sc_tsoff; /* ts offset w/ syncookies */
u_int32_t sc_flowlabel; /* IPv6 flowlabel */
diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c
--- a/sys/netinet/tcp_syncache.c
+++ b/sys/netinet/tcp_syncache.c
@@ -96,6 +96,8 @@
#ifdef TCP_OFFLOAD
#include <netinet/toecore.h>
#endif
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
#include <netipsec/ipsec_support.h>
@@ -143,14 +145,14 @@
static struct syncache
*syncookie_lookup(struct in_conninfo *, struct syncache_head *,
struct syncache *, struct tcphdr *, struct tcpopt *,
- struct socket *);
+ struct socket *, uint16_t);
static void syncache_pause(struct in_conninfo *);
static void syncache_unpause(void *);
static void syncookie_reseed(void *);
#ifdef INVARIANTS
static int syncookie_cmp(struct in_conninfo *inc, struct syncache_head *sch,
struct syncache *sc, struct tcphdr *th, struct tcpopt *to,
- struct socket *lso);
+ struct socket *lso, uint16_t port);
#endif
/*
@@ -605,7 +607,8 @@
* If required send a challenge ACK.
*/
void
-syncache_chkrst(struct in_conninfo *inc, struct tcphdr *th, struct mbuf *m)
+syncache_chkrst(struct in_conninfo *inc, struct tcphdr *th, struct mbuf *m,
+ uint16_t port)
{
struct syncache *sc;
struct syncache_head *sch;
@@ -645,6 +648,16 @@
goto done;
}
+ /* The remote UDP encaps port does not match. */
+ if (sc->sc_port != port) {
+ if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
+ log(LOG_DEBUG, "%s; %s: Spurious RST with matching "
+ "syncache entry but non-matching UDP encaps port, "
+ "segment ignored\n", s, __func__);
+ TCPSTAT_INC(tcps_badrst);
+ goto done;
+ }
+
/*
* If the RST bit is set, check the sequence number to see
* if this is a valid reset segment.
@@ -711,7 +724,7 @@
}
void
-syncache_badack(struct in_conninfo *inc)
+syncache_badack(struct in_conninfo *inc, uint16_t port)
{
struct syncache *sc;
struct syncache_head *sch;
@@ -720,7 +733,7 @@
return;
sc = syncache_lookup(inc, &sch); /* returns locked sch */
SCH_LOCK_ASSERT(sch);
- if (sc != NULL) {
+ if ((sc != NULL) && (sc->sc_port == port)) {
syncache_drop(sc, sch);
TCPSTAT_INC(tcps_sc_badack);
}
@@ -728,7 +741,7 @@
}
void
-syncache_unreach(struct in_conninfo *inc, tcp_seq th_seq)
+syncache_unreach(struct in_conninfo *inc, tcp_seq th_seq, uint16_t port)
{
struct syncache *sc;
struct syncache_head *sch;
@@ -740,6 +753,10 @@
if (sc == NULL)
goto done;
+ /* If the port != sc_port, then it's a bogus ICMP msg */
+ if (port != sc->sc_port)
+ goto done;
+
/* If the sequence number != sc_iss, then it's a bogus ICMP msg */
if (ntohl(th_seq) != sc->sc_iss)
goto done;
@@ -946,6 +963,7 @@
tcp_state_change(tp, TCPS_SYN_RECEIVED);
tp->iss = sc->sc_iss;
tp->irs = sc->sc_irs;
+ tp->t_port = sc->sc_port;
tcp_rcvseqinit(tp);
tcp_sendseqinit(tp);
blk = sototcpcb(lso)->t_fb;
@@ -1066,7 +1084,7 @@
*/
int
syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
- struct socket **lsop, struct mbuf *m)
+ struct socket **lsop, struct mbuf *m, uint16_t port)
{
struct syncache *sc;
struct syncache_head *sch;
@@ -1094,7 +1112,7 @@
* values with the reconstructed values from the cookie.
*/
if (sc != NULL)
- syncookie_cmp(inc, sch, sc, th, to, *lsop);
+ syncookie_cmp(inc, sch, sc, th, to, *lsop, port);
#endif
if (sc == NULL) {
@@ -1128,7 +1146,7 @@
goto failed;
}
bzero(&scs, sizeof(scs));
- sc = syncookie_lookup(inc, sch, &scs, th, to, *lsop);
+ sc = syncookie_lookup(inc, sch, &scs, th, to, *lsop, port);
if (locked)
SCH_UNLOCK(sch);
if (sc == NULL) {
@@ -1155,6 +1173,10 @@
}
#endif /* TCP_SIGNATURE */
} else {
+ if (sc->sc_port != port) {
+ SCH_UNLOCK(sch);
+ return (0);
+ }
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
/*
* If listening socket requested TCP digests, check that
@@ -1372,7 +1394,7 @@
int
syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
struct inpcb *inp, struct socket **lsop, struct mbuf *m, void *tod,
- void *todctx, uint8_t iptos)
+ void *todctx, uint8_t iptos, uint16_t port)
{
struct tcpcb *tp;
struct socket *so;
@@ -1634,6 +1656,7 @@
sc->sc_label = maclabel;
#endif
sc->sc_cred = cred;
+ sc->sc_port = port;
cred = NULL;
sc->sc_ipopts = ipopts;
bcopy(inc, &sc->sc_inc, sizeof(struct in_conninfo));
@@ -1794,8 +1817,9 @@
struct ip *ip = NULL;
struct mbuf *m;
struct tcphdr *th = NULL;
+ struct udphdr *udp = NULL;
int optlen, error = 0; /* Make compiler happy */
- u_int16_t hlen, tlen, mssopt;
+ u_int16_t hlen, tlen, mssopt, ulen;
struct tcpopt to;
#ifdef INET6
struct ip6_hdr *ip6 = NULL;
@@ -1809,9 +1833,14 @@
#endif
sizeof(struct ip);
tlen = hlen + sizeof(struct tcphdr);
-
+ if (sc->sc_port) {
+ tlen += sizeof(struct udphdr);
+ }
/* Determine MSS we advertize to other end of connection. */
- mssopt = max(tcp_mssopt(&sc->sc_inc), V_tcp_minmss);
+ mssopt = tcp_mssopt(&sc->sc_inc);
+ if (sc->sc_port)
+ mssopt -= V_tcp_udp_tunneling_overhead;
+ mssopt = max(mssopt, V_tcp_minmss);
/* XXX: Assume that the entire packet will fit in a header mbuf. */
KASSERT(max_linkhdr + tlen + TCP_MAXOLEN <= MHLEN,
@@ -1833,7 +1862,6 @@
if (sc->sc_inc.inc_flags & INC_ISIPV6) {
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_vfc = IPV6_VERSION;
- ip6->ip6_nxt = IPPROTO_TCP;
ip6->ip6_src = sc->sc_inc.inc6_laddr;
ip6->ip6_dst = sc->sc_inc.inc6_faddr;
ip6->ip6_plen = htons(tlen - hlen);
@@ -1841,9 +1869,18 @@
/* Zero out traffic class and flow label. */
ip6->ip6_flow &= ~IPV6_FLOWINFO_MASK;
ip6->ip6_flow |= sc->sc_flowlabel;
+ if (sc->sc_port != 0) {
+ ip6->ip6_nxt = IPPROTO_UDP;
+ udp = (struct udphdr *)(ip6 + 1);
+ udp->uh_sport = htons(V_tcp_udp_tunneling_port);
+ udp->uh_dport = sc->sc_port;
+ ulen = (tlen - sizeof(struct ip6_hdr));
+ th = (struct tcphdr *)(udp + 1);
+ } else {
+ ip6->ip6_nxt = IPPROTO_TCP;
+ th = (struct tcphdr *)(ip6 + 1);
+ }
ip6->ip6_flow |= htonl(sc->sc_ip_tos << 20);
-
- th = (struct tcphdr *)(ip6 + 1);
}
#endif
#if defined(INET6) && defined(INET)
@@ -1858,7 +1895,6 @@
ip->ip_id = 0;
ip->ip_off = 0;
ip->ip_sum = 0;
- ip->ip_p = IPPROTO_TCP;
ip->ip_src = sc->sc_inc.inc_laddr;
ip->ip_dst = sc->sc_inc.inc_faddr;
ip->ip_ttl = sc->sc_ip_ttl;
@@ -1873,8 +1909,17 @@
*/
if (V_path_mtu_discovery && ((sc->sc_flags & SCF_UNREACH) == 0))
ip->ip_off |= htons(IP_DF);
-
- th = (struct tcphdr *)(ip + 1);
+ if (sc->sc_port == 0) {
+ ip->ip_p = IPPROTO_TCP;
+ th = (struct tcphdr *)(ip + 1);
+ } else {
+ ip->ip_p = IPPROTO_UDP;
+ udp = (struct udphdr *)(ip + 1);
+ udp->uh_sport = htons(V_tcp_udp_tunneling_port);
+ udp->uh_dport = sc->sc_port;
+ ulen = (tlen - sizeof(struct ip));
+ th = (struct tcphdr *)(udp + 1);
+ }
}
#endif /* INET */
th->th_sport = sc->sc_inc.inc_lport;
@@ -1954,8 +1999,11 @@
} else
optlen = 0;
+ if (udp) {
+ ulen += optlen;
+ udp->uh_ulen = htons(ulen);
+ }
M_SETFIB(m, sc->sc_inc.inc_fibnum);
- m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
/*
* If we have peer's SYN and it has a flowid, then let's assign it to
* our SYN|ACK. ip6_output() and ip_output() will not assign flowid
@@ -1967,9 +2015,18 @@
}
#ifdef INET6
if (sc->sc_inc.inc_flags & INC_ISIPV6) {
- m->m_pkthdr.csum_flags = CSUM_TCP_IPV6;
- th->th_sum = in6_cksum_pseudo(ip6, tlen + optlen - hlen,
- IPPROTO_TCP, 0);
+ if (sc->sc_port) {
+ m->m_pkthdr.csum_flags = CSUM_UDP_IPV6;
+ m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
+ udp->uh_sum = in6_cksum_pseudo(ip6, ulen,
+ IPPROTO_UDP, 0);
+ th->th_sum = htons(0);
+ } else {
+ m->m_pkthdr.csum_flags = CSUM_TCP_IPV6;
+ m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
+ th->th_sum = in6_cksum_pseudo(ip6, tlen + optlen - hlen,
+ IPPROTO_TCP, 0);
+ }
ip6->ip6_hlim = sc->sc_ip_ttl;
#ifdef TCP_OFFLOAD
if (ADDED_BY_TOE(sc)) {
@@ -1989,9 +2046,18 @@
#endif
#ifdef INET
{
- m->m_pkthdr.csum_flags = CSUM_TCP;
- th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
- htons(tlen + optlen - hlen + IPPROTO_TCP));
+ if (sc->sc_port) {
+ m->m_pkthdr.csum_flags = CSUM_UDP;
+ m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
+ udp->uh_sum = in_pseudo(ip->ip_src.s_addr,
+ ip->ip_dst.s_addr, htons(ulen + IPPROTO_UDP));
+ th->th_sum = htons(0);
+ } else {
+ m->m_pkthdr.csum_flags = CSUM_TCP;
+ m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
+ th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
+ htons(tlen + optlen - hlen + IPPROTO_TCP));
+ }
#ifdef TCP_OFFLOAD
if (ADDED_BY_TOE(sc)) {
struct toedev *tod = sc->sc_tod;
@@ -2221,7 +2287,7 @@
static struct syncache *
syncookie_lookup(struct in_conninfo *inc, struct syncache_head *sch,
struct syncache *sc, struct tcphdr *th, struct tcpopt *to,
- struct socket *lso)
+ struct socket *lso, uint16_t port)
{
uint32_t hash;
uint8_t *secbits;
@@ -2307,6 +2373,8 @@
sc->sc_rxmits = 0;
+ sc->sc_port = port;
+
TCPSTAT_INC(tcps_sc_recvcookie);
return (sc);
}
@@ -2315,13 +2383,13 @@
static int
syncookie_cmp(struct in_conninfo *inc, struct syncache_head *sch,
struct syncache *sc, struct tcphdr *th, struct tcpopt *to,
- struct socket *lso)
+ struct socket *lso, uint16_t port)
{
struct syncache scs, *scx;
char *s;
bzero(&scs, sizeof(scs));
- scx = syncookie_lookup(inc, sch, &scs, th, to, lso);
+ scx = syncookie_lookup(inc, sch, &scs, th, to, lso, port);
if ((s = tcp_log_addrs(inc, th, NULL, NULL)) == NULL)
return (0);
@@ -2506,6 +2574,7 @@
xt.xt_inp.inp_vflag = INP_IPV6;
else
xt.xt_inp.inp_vflag = INP_IPV4;
+ xt.xt_encaps_port = sc->sc_port;
bcopy(&sc->sc_inc, &xt.xt_inp.inp_inc,
sizeof (struct in_conninfo));
error = SYSCTL_OUT(req, &xt, sizeof xt);
diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c
--- a/sys/netinet/tcp_timewait.c
+++ b/sys/netinet/tcp_timewait.c
@@ -93,6 +93,8 @@
#include <netinet6/ip6protosw.h>
#endif
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
#include <machine/in_cksum.h>
#include <security/mac/mac_framework.h>
@@ -318,6 +320,7 @@
}
tw->snd_nxt = tp->snd_nxt;
+ tw->t_port = tp->t_port;
tw->rcv_nxt = tp->rcv_nxt;
tw->iss = tp->iss;
tw->irs = tp->irs;
@@ -436,12 +439,32 @@
* while in TIME_WAIT, drop the old connection
* and start over if the sequence numbers
* are above the previous ones.
+ * Allow UDP port number changes in this case.
*/
if ((thflags & TH_SYN) && SEQ_GT(th->th_seq, tw->rcv_nxt)) {
tcp_twclose(tw, 0);
return (1);
}
+ /*
+ * Send RST if UDP port numbers don't match
+ */
+ if (tw->t_port != m->m_pkthdr.tcp_tun_port) {
+ if (th->th_flags & TH_ACK) {
+ tcp_respond(NULL, mtod(m, void *), th, m,
+ (tcp_seq)0, th->th_ack, TH_RST);
+ } else {
+ if (th->th_flags & TH_SYN)
+ tlen++;
+ if (th->th_flags & TH_FIN)
+ tlen++;
+ tcp_respond(NULL, mtod(m, void *), th, m,
+ th->th_seq+tlen, (tcp_seq)0, TH_RST|TH_ACK);
+ }
+ INP_WUNLOCK(inp);
+ return (0);
+ }
+
/*
* Drop the segment if it does not contain an ACK.
*/
@@ -555,13 +578,14 @@
#ifdef INET
struct ip *ip = NULL;
#endif
- u_int hdrlen, optlen;
+ u_int hdrlen, optlen, ulen;
int error = 0; /* Keep compiler happy */
struct tcpopt to;
#ifdef INET6
struct ip6_hdr *ip6 = NULL;
int isipv6 = inp->inp_inc.inc_flags & INC_ISIPV6;
#endif
+ struct udphdr *udp = NULL;
hdrlen = 0; /* Keep compiler happy */
INP_WLOCK_ASSERT(inp);
@@ -579,8 +603,16 @@
if (isipv6) {
hdrlen = sizeof(struct ip6_hdr) + sizeof(struct tcphdr);
ip6 = mtod(m, struct ip6_hdr *);
- th = (struct tcphdr *)(ip6 + 1);
- tcpip_fillheaders(inp, ip6, th);
+ if (tw->t_port) {
+ udp = (struct udphdr *)(ip6 + 1);
+ hdrlen += sizeof(struct udphdr);
+ udp->uh_sport = htons(V_tcp_udp_tunneling_port);
+ udp->uh_dport = tw->t_port;
+ ulen = (hdrlen - sizeof(struct ip6_hdr));
+ th = (struct tcphdr *)(udp + 1);
+ } else
+ th = (struct tcphdr *)(ip6 + 1);
+ tcpip_fillheaders(inp, tw->t_port, ip6, th);
}
#endif
#if defined(INET6) && defined(INET)
@@ -590,8 +622,16 @@
{
hdrlen = sizeof(struct tcpiphdr);
ip = mtod(m, struct ip *);
- th = (struct tcphdr *)(ip + 1);
- tcpip_fillheaders(inp, ip, th);
+ if (tw->t_port) {
+ udp = (struct udphdr *)(ip + 1);
+ hdrlen += sizeof(struct udphdr);
+ udp->uh_sport = htons(V_tcp_udp_tunneling_port);
+ udp->uh_dport = tw->t_port;
+ ulen = (hdrlen - sizeof(struct ip));
+ th = (struct tcphdr *)(udp + 1);
+ } else
+ th = (struct tcphdr *)(ip + 1);
+ tcpip_fillheaders(inp, tw->t_port, ip, th);
}
#endif
to.to_flags = 0;
@@ -607,6 +647,10 @@
}
optlen = tcp_addoptions(&to, (u_char *)(th + 1));
+ if (udp) {
+ ulen += optlen;
+ udp->uh_ulen = htons(ulen);
+ }
m->m_len = hdrlen + optlen;
m->m_pkthdr.len = m->m_len;
@@ -618,12 +662,19 @@
th->th_flags = flags;
th->th_win = htons(tw->last_win);
- m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
#ifdef INET6
if (isipv6) {
- m->m_pkthdr.csum_flags = CSUM_TCP_IPV6;
- th->th_sum = in6_cksum_pseudo(ip6,
- sizeof(struct tcphdr) + optlen, IPPROTO_TCP, 0);
+ if (tw->t_port) {
+ m->m_pkthdr.csum_flags = CSUM_UDP_IPV6;
+ m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
+ udp->uh_sum = in6_cksum_pseudo(ip6, ulen, IPPROTO_UDP, 0);
+ th->th_sum = htons(0);
+ } else {
+ m->m_pkthdr.csum_flags = CSUM_TCP_IPV6;
+ m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
+ th->th_sum = in6_cksum_pseudo(ip6,
+ sizeof(struct tcphdr) + optlen, IPPROTO_TCP, 0);
+ }
ip6->ip6_hlim = in6_selecthlim(inp, NULL);
TCP_PROBE5(send, NULL, NULL, ip6, NULL, th);
error = ip6_output(m, inp->in6p_outputopts, NULL,
@@ -635,9 +686,18 @@
#endif
#ifdef INET
{
- m->m_pkthdr.csum_flags = CSUM_TCP;
- th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
- htons(sizeof(struct tcphdr) + optlen + IPPROTO_TCP));
+ if (tw->t_port) {
+ m->m_pkthdr.csum_flags = CSUM_UDP;
+ m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
+ udp->uh_sum = in_pseudo(ip->ip_src.s_addr,
+ ip->ip_dst.s_addr, htons(ulen + IPPROTO_UDP));
+ th->th_sum = htons(0);
+ } else {
+ m->m_pkthdr.csum_flags = CSUM_TCP;
+ m->m_pkthdr.csum_data = offsetof(struct tcphdr, th_sum);
+ th->th_sum = in_pseudo(ip->ip_src.s_addr, ip->ip_dst.s_addr,
+ htons(sizeof(struct tcphdr) + optlen + IPPROTO_TCP));
+ }
ip->ip_len = htons(m->m_pkthdr.len);
if (V_path_mtu_discovery)
ip->ip_off |= htons(IP_DF);
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -2049,6 +2049,31 @@
}
goto unlock_and_done;
+ case TCP_REMOTE_UDP_ENCAPS_PORT:
+ INP_WUNLOCK(inp);
+ error = sooptcopyin(sopt, &optval, sizeof optval,
+ sizeof optval);
+ if (error)
+ return (error);
+ if ((optval < TCP_TUNNELING_PORT_MIN) ||
+ (optval > TCP_TUNNELING_PORT_MAX)) {
+ /* Its got to be in range */
+ return (EINVAL);
+ }
+ if ((V_tcp_udp_tunneling_port == 0) && (optval != 0)) {
+ /* You have to have enabled a UDP tunneling port first */
+ return (EINVAL);
+ }
+ INP_WLOCK_RECHECK(inp);
+ if (tp->t_state != TCPS_CLOSED) {
+ /* You can't change after you are connected */
+ error = EINVAL;
+ } else {
+ /* Ok we are all good set the port */
+ tp->t_port = htons(optval);
+ }
+ goto unlock_and_done;
+
case TCP_MAXSEG:
INP_WUNLOCK(inp);
error = sooptcopyin(sopt, &optval, sizeof optval,
@@ -2388,6 +2413,11 @@
INP_WUNLOCK(inp);
error = sooptcopyout(sopt, &optval, sizeof optval);
break;
+ case TCP_REMOTE_UDP_ENCAPS_PORT:
+ optval = ntohs(tp->t_port);
+ INP_WUNLOCK(inp);
+ error = sooptcopyout(sopt, &optval, sizeof optval);
+ break;
case TCP_NOOPT:
optval = tp->t_flags & TF_NOOPT;
INP_WUNLOCK(inp);
diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h
--- a/sys/netinet/tcp_var.h
+++ b/sys/netinet/tcp_var.h
@@ -282,6 +282,16 @@
struct tcphdr tt_t;
};
+/* Enable TCP/UDP tunneling port */
+#define TCP_TUNNELING_PORT_MIN 0
+#define TCP_TUNNELING_PORT_MAX 65535
+#define TCP_TUNNELING_PORT_DEFAULT 0
+
+/* Enable TCP/UDP tunneling port */
+#define TCP_TUNNELING_OVERHEAD_MIN sizeof(struct udphdr)
+#define TCP_TUNNELING_OVERHEAD_MAX 1024
+#define TCP_TUNNELING_OVERHEAD_DEFAULT TCP_TUNNELING_OVERHEAD_MIN
+
/* Minimum map entries limit value, if set */
#define TCP_MIN_MAP_ENTRIES_LIMIT 128
@@ -502,6 +512,8 @@
struct tcptw {
struct inpcb *tw_inpcb; /* XXX back pointer to internet pcb */
+ uint32_t t_port:16, /* UDP port number if TCPoUDP */
+ t_unused:16;
tcp_seq snd_nxt;
tcp_seq rcv_nxt;
tcp_seq iss;
@@ -678,7 +690,10 @@
uint64_t tcps_pmtud_blackhole_activated_min_mss; /* BH at min MSS Count */
uint64_t tcps_pmtud_blackhole_failed; /* Black Hole Failure Count */
- uint64_t _pad[12]; /* 6 UTO, 6 TBD */
+ uint64_t tcps_tunneled_pkts; /* Packets encap's in UDP received */
+ uint64_t tcps_tunneled_errs; /* Packets that had errors that were UDP encaped */
+
+ uint64_t _pad[10]; /* 6 UTO, 6 TBD */
};
#define tcps_rcvmemdrop tcps_rcvreassfull /* compat */
@@ -776,7 +791,9 @@
uint32_t t_rcv_wnd; /* (s) */
uint32_t t_snd_wnd; /* (s) */
uint32_t xt_ecn; /* (s) */
- int32_t spare32[26];
+ uint16_t xt_encaps_port; /* (s) */
+ int16_t spare16;
+ int32_t spare32[25];
} __aligned(8);
#ifdef _KERNEL
@@ -865,6 +882,8 @@
VNET_DECLARE(int, tcp_sack_maxholes);
VNET_DECLARE(int, tcp_sc_rst_sock_fail);
VNET_DECLARE(int, tcp_sendspace);
+VNET_DECLARE(int, tcp_udp_tunneling_overhead);
+VNET_DECLARE(int, tcp_udp_tunneling_port);
VNET_DECLARE(struct inpcbhead, tcb);
VNET_DECLARE(struct inpcbinfo, tcbinfo);
@@ -927,6 +946,7 @@
void tcp_twclose(struct tcptw *, int);
void tcp_ctlinput(int, struct sockaddr *, void *);
int tcp_ctloutput(struct socket *, struct sockopt *);
+void tcp_ctlinput_viaudp(int, struct sockaddr *, void *, void *);
struct tcpcb *
tcp_drop(struct tcpcb *, int);
void tcp_drain(void);
@@ -961,6 +981,7 @@
int tcp_input(struct mbuf **, int *, int);
int tcp_autorcvbuf(struct mbuf *, struct tcphdr *, struct socket *,
struct tcpcb *, int);
+int tcp_input_with_port(struct mbuf **, int *, int, uint16_t);
void tcp_handle_wakeup(struct tcpcb *, struct socket *);
void tcp_do_segment(struct mbuf *, struct tcphdr *,
struct socket *, struct tcpcb *, int, int, uint8_t);
@@ -1031,7 +1052,7 @@
void tcp_slowtimo(void);
struct tcptemp *
tcpip_maketemplate(struct inpcb *);
-void tcpip_fillheaders(struct inpcb *, void *, void *);
+void tcpip_fillheaders(struct inpcb *, uint16_t, void *, void *);
void tcp_timer_activate(struct tcpcb *, uint32_t, u_int);
int tcp_timer_suspend(struct tcpcb *, uint32_t);
void tcp_timers_unsuspend(struct tcpcb *, uint32_t);
diff --git a/sys/netinet/toecore.c b/sys/netinet/toecore.c
--- a/sys/netinet/toecore.c
+++ b/sys/netinet/toecore.c
@@ -352,7 +352,8 @@
INP_WLOCK_ASSERT(inp);
- syncache_add(inc, to, th, inp, &lso, NULL, tod, todctx, iptos);
+ syncache_add(inc, to, th, inp, &lso, NULL, tod, todctx, iptos,
+ htons(0));
}
int
@@ -362,7 +363,7 @@
NET_EPOCH_ASSERT();
- return (syncache_expand(inc, to, th, lsop, NULL));
+ return (syncache_expand(inc, to, th, lsop, NULL, htons(0)));
}
/*
diff --git a/sys/netinet6/tcp6_var.h b/sys/netinet6/tcp6_var.h
--- a/sys/netinet6/tcp6_var.h
+++ b/sys/netinet6/tcp6_var.h
@@ -74,8 +74,10 @@
struct ip6_hdr;
void tcp6_ctlinput(int, struct sockaddr *, void *);
+void tcp6_ctlinput_viaudp(int, struct sockaddr *, void *, void *);
void tcp6_init(void);
int tcp6_input(struct mbuf **, int *, int);
+int tcp6_input_with_port(struct mbuf **, int *, int, uint16_t);
extern struct pr_usrreqs tcp6_usrreqs;
diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h
--- a/sys/sys/mbuf.h
+++ b/sys/sys/mbuf.h
@@ -198,6 +198,7 @@
} PH_loc;
};
#define ether_vtag PH_per.sixteen[0]
+#define tcp_tun_port PH_per.sixteen[0] /* outbound */
#define PH_vt PH_per
#define vt_nrecs sixteen[0] /* mld and v6-ND */
#define tso_segsz PH_per.sixteen[1] /* inbound after LRO */
diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c
--- a/usr.bin/netstat/inet.c
+++ b/usr.bin/netstat/inet.c
@@ -664,6 +664,10 @@
"{N:(for} {:received-ack-bytes/%ju} {N:/byte%s})\n");
p(tcps_rcvdupack, "\t\t{:received-duplicate-acks/%ju} "
"{N:/duplicate ack%s}\n");
+ p(tcps_tunneled_pkts, "\t\t{:received-udp-tunneled-pkts/%ju} "
+ "{N:/UDP tunneled pkt%s}\n");
+ p(tcps_tunneled_errs, "\t\t{:received-bad-udp-tunneled-pkts/%ju} "
+ "{N:/UDP tunneled pkt cnt with error%s}\n");
p(tcps_rcvacktoomuch, "\t\t{:received-acks-for-unsent-data/%ju} "
"{N:/ack%s for unsent data}\n");
p2(tcps_rcvpack, tcps_rcvbyte, "\t\t"
diff --git a/usr.bin/sockstat/sockstat.1 b/usr.bin/sockstat/sockstat.1
--- a/usr.bin/sockstat/sockstat.1
+++ b/usr.bin/sockstat/sockstat.1
@@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd December 30, 2020
+.Dd March 28, 2021
.Dt SOCKSTAT 1
.Os
.Sh NAME
@@ -98,7 +98,7 @@
This is currently only implemented for SCTP and TCP.
.It Fl U
Display the remote UDP encapsulation port number, if applicable.
-This is currently only implemented for SCTP.
+This is currently only implemented for SCTP and TCP.
.It Fl u
Show
.Dv AF_LOCAL
@@ -163,7 +163,7 @@
.It Li ENCAPS
The remote UDP encapsulation port number if
.Fl U
-is specified (only for SCTP).
+is specified (only for SCTP or TCP).
.It Li PATH STATE
The path state if
.Fl s
diff --git a/usr.bin/sockstat/sockstat.c b/usr.bin/sockstat/sockstat.c
--- a/usr.bin/sockstat/sockstat.c
+++ b/usr.bin/sockstat/sockstat.c
@@ -710,6 +710,8 @@
sockaddr(&faddr->address, sock->family,
&xip->in6p_faddr, xip->inp_fport);
}
+ if (proto == IPPROTO_TCP)
+ faddr->encaps_port = xtp->xt_encaps_port;
laddr->next = NULL;
faddr->next = NULL;
sock->laddr = laddr;
@@ -1087,10 +1089,13 @@
}
if (opt_U) {
if (faddr != NULL &&
- s->proto == IPPROTO_SCTP &&
- s->state != SCTP_CLOSED &&
- s->state != SCTP_BOUND &&
- s->state != SCTP_LISTEN) {
+ ((s->proto == IPPROTO_SCTP &&
+ s->state != SCTP_CLOSED &&
+ s->state != SCTP_BOUND &&
+ s->state != SCTP_LISTEN) ||
+ (s->proto == IPPROTO_TCP &&
+ s->state != TCPS_CLOSED &&
+ s->state != TCPS_LISTEN))) {
while (pos < offset)
pos += xprintf(" ");
pos += xprintf("%u",
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Apr 28, 6:08 AM (3 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
32254878
Default Alt Text
D29469.id86435.diff (57 KB)
Attached To
Mode
D29469: Add support for TCP over UDP
Attached
Detach File
Event Timeline
Log In to Comment