Commit 1b5d3535 authored by David S. Miller's avatar David S. Miller

Merge branch 'inet_csums_part2'

Tom Herbert says:

====================
net: Checksum offload changes - Part II

I am working on overhauling RX checksum offload. Goals of this effort
are:

- Specify what exactly it means when driver returns CHECKSUM_UNNECESSARY
- Preserve CHECKSUM_COMPLETE through encapsulation layers
- Don't do skb_checksum more than once per packet
- Unify GRO and non-GRO csum verification as much as possible
- Unify the checksum functions (checksum_init)
- Simply code

What is in this second patch set:

- Call common inet checksum validation functions in ICMP{4,6},
  GRE{4,6}, and IGMP.
- In UDP, verify checksum before handing off to encap_rcv.
- Remove custom UDP checksum validation code in L2TP.

Please review carefully and test if possible, mucking with basic
checksum functions is always a little precarious :-)
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 1a91de28 58d6085c
...@@ -93,28 +93,6 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi, ...@@ -93,28 +93,6 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi,
} }
EXPORT_SYMBOL_GPL(gre_build_header); EXPORT_SYMBOL_GPL(gre_build_header);
static __sum16 check_checksum(struct sk_buff *skb)
{
__sum16 csum = 0;
switch (skb->ip_summed) {
case CHECKSUM_COMPLETE:
csum = csum_fold(skb->csum);
if (!csum)
break;
/* Fall through. */
case CHECKSUM_NONE:
skb->csum = 0;
csum = __skb_checksum_complete(skb);
skb->ip_summed = CHECKSUM_COMPLETE;
break;
}
return csum;
}
static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi, static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
bool *csum_err) bool *csum_err)
{ {
...@@ -141,7 +119,7 @@ static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi, ...@@ -141,7 +119,7 @@ static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
options = (__be32 *)(greh + 1); options = (__be32 *)(greh + 1);
if (greh->flags & GRE_CSUM) { if (greh->flags & GRE_CSUM) {
if (check_checksum(skb)) { if (skb_checksum_simple_validate(skb)) {
*csum_err = true; *csum_err = true;
return -EINVAL; return -EINVAL;
} }
......
...@@ -908,16 +908,8 @@ int icmp_rcv(struct sk_buff *skb) ...@@ -908,16 +908,8 @@ int icmp_rcv(struct sk_buff *skb)
ICMP_INC_STATS_BH(net, ICMP_MIB_INMSGS); ICMP_INC_STATS_BH(net, ICMP_MIB_INMSGS);
switch (skb->ip_summed) { if (skb_checksum_simple_validate(skb))
case CHECKSUM_COMPLETE:
if (!csum_fold(skb->csum))
break;
/* fall through */
case CHECKSUM_NONE:
skb->csum = 0;
if (__skb_checksum_complete(skb))
goto csum_error; goto csum_error;
}
if (!pskb_pull(skb, sizeof(*icmph))) if (!pskb_pull(skb, sizeof(*icmph)))
goto error; goto error;
......
...@@ -988,16 +988,8 @@ int igmp_rcv(struct sk_buff *skb) ...@@ -988,16 +988,8 @@ int igmp_rcv(struct sk_buff *skb)
if (!pskb_may_pull(skb, sizeof(struct igmphdr))) if (!pskb_may_pull(skb, sizeof(struct igmphdr)))
goto drop; goto drop;
switch (skb->ip_summed) { if (skb_checksum_simple_validate(skb))
case CHECKSUM_COMPLETE:
if (!csum_fold(skb->csum))
break;
/* fall through */
case CHECKSUM_NONE:
skb->csum = 0;
if (__skb_checksum_complete(skb))
goto drop; goto drop;
}
ih = igmp_hdr(skb); ih = igmp_hdr(skb);
switch (ih->type) { switch (ih->type) {
......
...@@ -1495,6 +1495,10 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -1495,6 +1495,10 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) { if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) {
int ret; int ret;
/* Verify checksum before giving to encap */
if (udp_lib_checksum_complete(skb))
goto csum_error;
ret = encap_rcv(sk, skb); ret = encap_rcv(sk, skb);
if (ret <= 0) { if (ret <= 0) {
UDP_INC_STATS_BH(sock_net(sk), UDP_INC_STATS_BH(sock_net(sk),
......
...@@ -692,23 +692,12 @@ static int icmpv6_rcv(struct sk_buff *skb) ...@@ -692,23 +692,12 @@ static int icmpv6_rcv(struct sk_buff *skb)
saddr = &ipv6_hdr(skb)->saddr; saddr = &ipv6_hdr(skb)->saddr;
daddr = &ipv6_hdr(skb)->daddr; daddr = &ipv6_hdr(skb)->daddr;
/* Perform checksum. */ if (skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo)) {
switch (skb->ip_summed) {
case CHECKSUM_COMPLETE:
if (!csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6,
skb->csum))
break;
/* fall through */
case CHECKSUM_NONE:
skb->csum = ~csum_unfold(csum_ipv6_magic(saddr, daddr, skb->len,
IPPROTO_ICMPV6, 0));
if (__skb_checksum_complete(skb)) {
LIMIT_NETDEBUG(KERN_DEBUG LIMIT_NETDEBUG(KERN_DEBUG
"ICMPv6 checksum failed [%pI6c > %pI6c]\n", "ICMPv6 checksum failed [%pI6c > %pI6c]\n",
saddr, daddr); saddr, daddr);
goto csum_error; goto csum_error;
} }
}
if (!pskb_pull(skb, sizeof(*hdr))) if (!pskb_pull(skb, sizeof(*hdr)))
goto discard_it; goto discard_it;
......
...@@ -468,17 +468,7 @@ static int ip6gre_rcv(struct sk_buff *skb) ...@@ -468,17 +468,7 @@ static int ip6gre_rcv(struct sk_buff *skb)
goto drop; goto drop;
if (flags&GRE_CSUM) { if (flags&GRE_CSUM) {
switch (skb->ip_summed) { csum = skb_checksum_simple_validate(skb);
case CHECKSUM_COMPLETE:
csum = csum_fold(skb->csum);
if (!csum)
break;
/* fall through */
case CHECKSUM_NONE:
skb->csum = 0;
csum = __skb_checksum_complete(skb);
skb->ip_summed = CHECKSUM_COMPLETE;
}
offset += 4; offset += 4;
} }
if (flags&GRE_KEY) { if (flags&GRE_KEY) {
......
...@@ -634,6 +634,10 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -634,6 +634,10 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) { if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) {
int ret; int ret;
/* Verify checksum before giving to encap */
if (udp_lib_checksum_complete(skb))
goto csum_error;
ret = encap_rcv(sk, skb); ret = encap_rcv(sk, skb);
if (ret <= 0) { if (ret <= 0) {
UDP_INC_STATS_BH(sock_net(sk), UDP_INC_STATS_BH(sock_net(sk),
......
...@@ -495,52 +495,6 @@ static void l2tp_recv_dequeue(struct l2tp_session *session) ...@@ -495,52 +495,6 @@ static void l2tp_recv_dequeue(struct l2tp_session *session)
spin_unlock_bh(&session->reorder_q.lock); spin_unlock_bh(&session->reorder_q.lock);
} }
static inline int l2tp_verify_udp_checksum(struct sock *sk,
struct sk_buff *skb)
{
struct udphdr *uh = udp_hdr(skb);
u16 ulen = ntohs(uh->len);
__wsum psum;
if (sk->sk_no_check || skb_csum_unnecessary(skb))
return 0;
#if IS_ENABLED(CONFIG_IPV6)
if (sk->sk_family == PF_INET6 && !l2tp_tunnel(sk)->v4mapped) {
if (!uh->check) {
LIMIT_NETDEBUG(KERN_INFO "L2TP: IPv6: checksum is 0\n");
return 1;
}
if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
!csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr, ulen,
IPPROTO_UDP, skb->csum)) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
return 0;
}
skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
&ipv6_hdr(skb)->daddr,
skb->len, IPPROTO_UDP,
0));
} else
#endif
{
struct inet_sock *inet;
if (!uh->check)
return 0;
inet = inet_sk(sk);
psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr,
ulen, IPPROTO_UDP, 0);
if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
!csum_fold(csum_add(psum, skb->csum)))
return 0;
skb->csum = psum;
}
return __skb_checksum_complete(skb);
}
static int l2tp_seq_check_rx_window(struct l2tp_session *session, u32 nr) static int l2tp_seq_check_rx_window(struct l2tp_session *session, u32 nr)
{ {
u32 nws; u32 nws;
...@@ -895,8 +849,7 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb, ...@@ -895,8 +849,7 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
u16 version; u16 version;
int length; int length;
if (tunnel->sock && l2tp_verify_udp_checksum(tunnel->sock, skb)) /* UDP has verifed checksum */
goto discard_bad_csum;
/* UDP always verifies the packet length. */ /* UDP always verifies the packet length. */
__skb_pull(skb, sizeof(struct udphdr)); __skb_pull(skb, sizeof(struct udphdr));
...@@ -979,14 +932,6 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb, ...@@ -979,14 +932,6 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
return 0; return 0;
discard_bad_csum:
LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name);
UDP_INC_STATS_USER(tunnel->l2tp_net, UDP_MIB_INERRORS, 0);
atomic_long_inc(&tunnel->stats.rx_errors);
kfree_skb(skb);
return 0;
error: error:
/* Put UDP header back */ /* Put UDP header back */
__skb_push(skb, sizeof(struct udphdr)); __skb_push(skb, sizeof(struct udphdr));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment