[PATCH v3] tcp: verify the checksum of the first data segment in a new connection

2018-06-12 Thread Frank van der Linden
commit 079096f103fa ("tcp/dccp: install syn_recv requests into ehash
table") introduced an optimization for the handling of child sockets
created for a new TCP connection.

But this optimization passes any data associated with the last ACK of the
connection handshake up the stack without verifying its checksum, because it
calls tcp_child_process(), which in turn calls tcp_rcv_state_process()
directly.  These lower-level processing functions do not do any checksum
verification.

Insert a tcp_checksum_complete call in the TCP_NEW_SYN_RECEIVE path to
fix this.

Signed-off-by: Frank van der Linden 
---
 net/ipv4/tcp_ipv4.c | 4 
 net/ipv6/tcp_ipv6.c | 4 
 2 files changed, 8 insertions(+)

diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index f70586b..ef8cd0f 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1689,6 +1689,10 @@ int tcp_v4_rcv(struct sk_buff *skb)
reqsk_put(req);
goto discard_it;
}
+   if (tcp_checksum_complete(skb)) {
+   reqsk_put(req);
+   goto csum_error;
+   }
if (unlikely(sk->sk_state != TCP_LISTEN)) {
inet_csk_reqsk_queue_drop_and_put(sk, req);
goto lookup;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 6d664d8..5d4eb9d 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1475,6 +1475,10 @@ static int tcp_v6_rcv(struct sk_buff *skb)
reqsk_put(req);
goto discard_it;
}
+   if (tcp_checksum_complete(skb)) {
+   reqsk_put(req);
+   goto csum_error;
+   }
if (unlikely(sk->sk_state != TCP_LISTEN)) {
inet_csk_reqsk_queue_drop_and_put(sk, req);
goto lookup;
-- 
1.8.3.1



[PATCH v2] tcp: verify the checksum of the first data segment in a new connection

2018-06-12 Thread Frank van der Linden
commit 079096f103fa ("tcp/dccp: install syn_recv requests into ehash
table") introduced an optimization for the handling of child sockets
created for a new TCP connection.

But this optimization passes any data associated with the last ACK of the
connection handshake up the stack without verifying its checksum, because it
calls tcp_child_process(), which in turn calls tcp_rcv_state_process()
directly.  These lower-level processing functions do not do any checksum
verification.

Insert a tcp_checksum_complete call in the TCP_NEW_SYN_RECEIVE path to
fix this.

Signed-off-by: Frank van der Linden 
---
 net/ipv4/tcp_ipv4.c | 10 +-
 net/ipv6/tcp_ipv6.c | 10 +-
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index f70586b..f361cf9 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1639,6 +1639,7 @@ int tcp_v4_rcv(struct sk_buff *skb)
const struct iphdr *iph;
const struct tcphdr *th;
bool refcounted;
+   bool csumerr = false;
struct sock *sk;
int ret;
 
@@ -1703,7 +1704,12 @@ int tcp_v4_rcv(struct sk_buff *skb)
th = (const struct tcphdr *)skb->data;
iph = ip_hdr(skb);
tcp_v4_fill_cb(skb, iph, th);
-   nsk = tcp_check_req(sk, skb, req, false, _stolen);
+
+   csumerr = tcp_checksum_complete(skb);
+   if (!csumerr) {
+   nsk = tcp_check_req(sk, skb, req, false,
+   _stolen);
+   }
}
if (!nsk) {
reqsk_put(req);
@@ -1798,6 +1804,8 @@ int tcp_v4_rcv(struct sk_buff *skb)
sk_drops_add(sk, skb);
if (refcounted)
sock_put(sk);
+   if (csumerr)
+   goto csum_error;
goto discard_it;
 
 do_time_wait:
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 6d664d8..17a20fa 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1425,6 +1425,7 @@ static int tcp_v6_rcv(struct sk_buff *skb)
const struct tcphdr *th;
const struct ipv6hdr *hdr;
bool refcounted;
+   bool csumerr = false;
struct sock *sk;
int ret;
struct net *net = dev_net(skb->dev);
@@ -1486,7 +1487,12 @@ static int tcp_v6_rcv(struct sk_buff *skb)
th = (const struct tcphdr *)skb->data;
hdr = ipv6_hdr(skb);
tcp_v6_fill_cb(skb, hdr, th);
-   nsk = tcp_check_req(sk, skb, req, false, _stolen);
+
+   csumerr = tcp_checksum_complete(skb);
+   if (!csumerr) {
+   nsk = tcp_check_req(sk, skb, req, false,
+   _stolen);
+   }
}
if (!nsk) {
reqsk_put(req);
@@ -1577,6 +1583,8 @@ static int tcp_v6_rcv(struct sk_buff *skb)
sk_drops_add(sk, skb);
if (refcounted)
sock_put(sk);
+   if (csumerr)
+   goto csum_error;
goto discard_it;
 
 do_time_wait:
-- 
1.8.3.1



[PATCH] tcp: verify the checksum of the first data segment in a new connection

2018-06-11 Thread Frank van der Linden
commit 079096f103fa ("tcp/dccp: install syn_recv requests into ehash
table") introduced an optimization for the handling of child sockets
created for a new TCP connection.

But this optimization passes any data associated with the last ACK of the
connection handshake up the stack without verifying its checksum, because it
calls tcp_child_process(), which in turn calls tcp_rcv_state_process()
directly.  These lower-level processing functions do not do any checksum
verification.

Insert a tcp_checksum_complete call in the TCP_NEW_SYN_RECEIVE path to
fix this.

Signed-off-by: Frank van der Linden 
---
 net/ipv4/tcp_ipv4.c | 8 +++-
 net/ipv6/tcp_ipv6.c | 8 +++-
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index f70586b50838..1ec4c0d4aba5 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1703,7 +1703,13 @@ int tcp_v4_rcv(struct sk_buff *skb)
th = (const struct tcphdr *)skb->data;
iph = ip_hdr(skb);
tcp_v4_fill_cb(skb, iph, th);
-   nsk = tcp_check_req(sk, skb, req, false, _stolen);
+
+   if (tcp_checksum_complete(skb)) {
+   __TCP_INC_STATS(net, TCP_MIB_CSUMERRORS);
+   } else {
+   nsk = tcp_check_req(sk, skb, req, false,
+   _stolen);
+   }
}
if (!nsk) {
reqsk_put(req);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 6d664d83cd16..a12b694d3d1e 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1486,7 +1486,13 @@ static int tcp_v6_rcv(struct sk_buff *skb)
th = (const struct tcphdr *)skb->data;
hdr = ipv6_hdr(skb);
tcp_v6_fill_cb(skb, hdr, th);
-   nsk = tcp_check_req(sk, skb, req, false, _stolen);
+
+   if (tcp_checksum_complete(skb)) {
+   __TCP_INC_STATS(net, TCP_MIB_CSUMERRORS);
+   } else {
+   nsk = tcp_check_req(sk, skb, req, false,
+   _stolen);
+   }
}
if (!nsk) {
reqsk_put(req);
-- 
2.14.4