diff options
author | David S. Miller <davem@davemloft.net> | 2014-05-08 23:48:01 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-05-08 23:48:01 -0400 |
commit | 1b5d35358effb776b1ef47b26ec0df54af79d842 (patch) | |
tree | 3e3a8bf63676f025cca73ac50fd7e3645d7b138e | |
parent | 1a91de28831a1bd913e14dacf25763f3672e24a9 (diff) | |
parent | 58d6085c14f5db61c092c90b59813397bc771417 (diff) |
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: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv4/gre_demux.c | 24 | ||||
-rw-r--r-- | net/ipv4/icmp.c | 12 | ||||
-rw-r--r-- | net/ipv4/igmp.c | 12 | ||||
-rw-r--r-- | net/ipv4/udp.c | 4 | ||||
-rw-r--r-- | net/ipv6/icmp.c | 21 | ||||
-rw-r--r-- | net/ipv6/ip6_gre.c | 12 | ||||
-rw-r--r-- | net/ipv6/udp.c | 4 | ||||
-rw-r--r-- | net/l2tp/l2tp_core.c | 57 |
8 files changed, 20 insertions, 126 deletions
diff --git a/net/ipv4/gre_demux.c b/net/ipv4/gre_demux.c index 250be7421ab3..fbfd829f4049 100644 --- a/net/ipv4/gre_demux.c +++ b/net/ipv4/gre_demux.c | |||
@@ -93,28 +93,6 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi, | |||
93 | } | 93 | } |
94 | EXPORT_SYMBOL_GPL(gre_build_header); | 94 | EXPORT_SYMBOL_GPL(gre_build_header); |
95 | 95 | ||
96 | static __sum16 check_checksum(struct sk_buff *skb) | ||
97 | { | ||
98 | __sum16 csum = 0; | ||
99 | |||
100 | switch (skb->ip_summed) { | ||
101 | case CHECKSUM_COMPLETE: | ||
102 | csum = csum_fold(skb->csum); | ||
103 | |||
104 | if (!csum) | ||
105 | break; | ||
106 | /* Fall through. */ | ||
107 | |||
108 | case CHECKSUM_NONE: | ||
109 | skb->csum = 0; | ||
110 | csum = __skb_checksum_complete(skb); | ||
111 | skb->ip_summed = CHECKSUM_COMPLETE; | ||
112 | break; | ||
113 | } | ||
114 | |||
115 | return csum; | ||
116 | } | ||
117 | |||
118 | static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi, | 96 | static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi, |
119 | bool *csum_err) | 97 | bool *csum_err) |
120 | { | 98 | { |
@@ -141,7 +119,7 @@ static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi, | |||
141 | 119 | ||
142 | options = (__be32 *)(greh + 1); | 120 | options = (__be32 *)(greh + 1); |
143 | if (greh->flags & GRE_CSUM) { | 121 | if (greh->flags & GRE_CSUM) { |
144 | if (check_checksum(skb)) { | 122 | if (skb_checksum_simple_validate(skb)) { |
145 | *csum_err = true; | 123 | *csum_err = true; |
146 | return -EINVAL; | 124 | return -EINVAL; |
147 | } | 125 | } |
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 0134663fdbce..fe52666dc43c 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c | |||
@@ -908,16 +908,8 @@ int icmp_rcv(struct sk_buff *skb) | |||
908 | 908 | ||
909 | ICMP_INC_STATS_BH(net, ICMP_MIB_INMSGS); | 909 | ICMP_INC_STATS_BH(net, ICMP_MIB_INMSGS); |
910 | 910 | ||
911 | switch (skb->ip_summed) { | 911 | if (skb_checksum_simple_validate(skb)) |
912 | case CHECKSUM_COMPLETE: | 912 | goto csum_error; |
913 | if (!csum_fold(skb->csum)) | ||
914 | break; | ||
915 | /* fall through */ | ||
916 | case CHECKSUM_NONE: | ||
917 | skb->csum = 0; | ||
918 | if (__skb_checksum_complete(skb)) | ||
919 | goto csum_error; | ||
920 | } | ||
921 | 913 | ||
922 | if (!pskb_pull(skb, sizeof(*icmph))) | 914 | if (!pskb_pull(skb, sizeof(*icmph))) |
923 | goto error; | 915 | goto error; |
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 97e4d1655d26..17d34e3c2ac3 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c | |||
@@ -988,16 +988,8 @@ int igmp_rcv(struct sk_buff *skb) | |||
988 | if (!pskb_may_pull(skb, sizeof(struct igmphdr))) | 988 | if (!pskb_may_pull(skb, sizeof(struct igmphdr))) |
989 | goto drop; | 989 | goto drop; |
990 | 990 | ||
991 | switch (skb->ip_summed) { | 991 | if (skb_checksum_simple_validate(skb)) |
992 | case CHECKSUM_COMPLETE: | 992 | goto drop; |
993 | if (!csum_fold(skb->csum)) | ||
994 | break; | ||
995 | /* fall through */ | ||
996 | case CHECKSUM_NONE: | ||
997 | skb->csum = 0; | ||
998 | if (__skb_checksum_complete(skb)) | ||
999 | goto drop; | ||
1000 | } | ||
1001 | 993 | ||
1002 | ih = igmp_hdr(skb); | 994 | ih = igmp_hdr(skb); |
1003 | switch (ih->type) { | 995 | switch (ih->type) { |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index f2d05d7be743..54ea0a3a48f1 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -1495,6 +1495,10 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
1495 | if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) { | 1495 | if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) { |
1496 | int ret; | 1496 | int ret; |
1497 | 1497 | ||
1498 | /* Verify checksum before giving to encap */ | ||
1499 | if (udp_lib_checksum_complete(skb)) | ||
1500 | goto csum_error; | ||
1501 | |||
1498 | ret = encap_rcv(sk, skb); | 1502 | ret = encap_rcv(sk, skb); |
1499 | if (ret <= 0) { | 1503 | if (ret <= 0) { |
1500 | UDP_INC_STATS_BH(sock_net(sk), | 1504 | UDP_INC_STATS_BH(sock_net(sk), |
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 3b0905b77127..8d3952796d39 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c | |||
@@ -692,22 +692,11 @@ static int icmpv6_rcv(struct sk_buff *skb) | |||
692 | saddr = &ipv6_hdr(skb)->saddr; | 692 | saddr = &ipv6_hdr(skb)->saddr; |
693 | daddr = &ipv6_hdr(skb)->daddr; | 693 | daddr = &ipv6_hdr(skb)->daddr; |
694 | 694 | ||
695 | /* Perform checksum. */ | 695 | if (skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo)) { |
696 | switch (skb->ip_summed) { | 696 | LIMIT_NETDEBUG(KERN_DEBUG |
697 | case CHECKSUM_COMPLETE: | 697 | "ICMPv6 checksum failed [%pI6c > %pI6c]\n", |
698 | if (!csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6, | 698 | saddr, daddr); |
699 | skb->csum)) | 699 | goto csum_error; |
700 | break; | ||
701 | /* fall through */ | ||
702 | case CHECKSUM_NONE: | ||
703 | skb->csum = ~csum_unfold(csum_ipv6_magic(saddr, daddr, skb->len, | ||
704 | IPPROTO_ICMPV6, 0)); | ||
705 | if (__skb_checksum_complete(skb)) { | ||
706 | LIMIT_NETDEBUG(KERN_DEBUG | ||
707 | "ICMPv6 checksum failed [%pI6c > %pI6c]\n", | ||
708 | saddr, daddr); | ||
709 | goto csum_error; | ||
710 | } | ||
711 | } | 700 | } |
712 | 701 | ||
713 | if (!pskb_pull(skb, sizeof(*hdr))) | 702 | if (!pskb_pull(skb, sizeof(*hdr))) |
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 75277b739b04..3873181ed856 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c | |||
@@ -468,17 +468,7 @@ static int ip6gre_rcv(struct sk_buff *skb) | |||
468 | goto drop; | 468 | goto drop; |
469 | 469 | ||
470 | if (flags&GRE_CSUM) { | 470 | if (flags&GRE_CSUM) { |
471 | switch (skb->ip_summed) { | 471 | csum = skb_checksum_simple_validate(skb); |
472 | case CHECKSUM_COMPLETE: | ||
473 | csum = csum_fold(skb->csum); | ||
474 | if (!csum) | ||
475 | break; | ||
476 | /* fall through */ | ||
477 | case CHECKSUM_NONE: | ||
478 | skb->csum = 0; | ||
479 | csum = __skb_checksum_complete(skb); | ||
480 | skb->ip_summed = CHECKSUM_COMPLETE; | ||
481 | } | ||
482 | offset += 4; | 472 | offset += 4; |
483 | } | 473 | } |
484 | if (flags&GRE_KEY) { | 474 | if (flags&GRE_KEY) { |
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index fc2be63e32d5..7edf096867c4 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -634,6 +634,10 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
634 | if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) { | 634 | if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) { |
635 | int ret; | 635 | int ret; |
636 | 636 | ||
637 | /* Verify checksum before giving to encap */ | ||
638 | if (udp_lib_checksum_complete(skb)) | ||
639 | goto csum_error; | ||
640 | |||
637 | ret = encap_rcv(sk, skb); | 641 | ret = encap_rcv(sk, skb); |
638 | if (ret <= 0) { | 642 | if (ret <= 0) { |
639 | UDP_INC_STATS_BH(sock_net(sk), | 643 | UDP_INC_STATS_BH(sock_net(sk), |
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index a4e37d7158dc..aa1a9d44c107 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c | |||
@@ -495,52 +495,6 @@ out: | |||
495 | spin_unlock_bh(&session->reorder_q.lock); | 495 | spin_unlock_bh(&session->reorder_q.lock); |
496 | } | 496 | } |
497 | 497 | ||
498 | static inline int l2tp_verify_udp_checksum(struct sock *sk, | ||
499 | struct sk_buff *skb) | ||
500 | { | ||
501 | struct udphdr *uh = udp_hdr(skb); | ||
502 | u16 ulen = ntohs(uh->len); | ||
503 | __wsum psum; | ||
504 | |||
505 | if (sk->sk_no_check || skb_csum_unnecessary(skb)) | ||
506 | return 0; | ||
507 | |||
508 | #if IS_ENABLED(CONFIG_IPV6) | ||
509 | if (sk->sk_family == PF_INET6 && !l2tp_tunnel(sk)->v4mapped) { | ||
510 | if (!uh->check) { | ||
511 | LIMIT_NETDEBUG(KERN_INFO "L2TP: IPv6: checksum is 0\n"); | ||
512 | return 1; | ||
513 | } | ||
514 | if ((skb->ip_summed == CHECKSUM_COMPLETE) && | ||
515 | !csum_ipv6_magic(&ipv6_hdr(skb)->saddr, | ||
516 | &ipv6_hdr(skb)->daddr, ulen, | ||
517 | IPPROTO_UDP, skb->csum)) { | ||
518 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
519 | return 0; | ||
520 | } | ||
521 | skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr, | ||
522 | &ipv6_hdr(skb)->daddr, | ||
523 | skb->len, IPPROTO_UDP, | ||
524 | 0)); | ||
525 | } else | ||
526 | #endif | ||
527 | { | ||
528 | struct inet_sock *inet; | ||
529 | if (!uh->check) | ||
530 | return 0; | ||
531 | inet = inet_sk(sk); | ||
532 | psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr, | ||
533 | ulen, IPPROTO_UDP, 0); | ||
534 | |||
535 | if ((skb->ip_summed == CHECKSUM_COMPLETE) && | ||
536 | !csum_fold(csum_add(psum, skb->csum))) | ||
537 | return 0; | ||
538 | skb->csum = psum; | ||
539 | } | ||
540 | |||
541 | return __skb_checksum_complete(skb); | ||
542 | } | ||
543 | |||
544 | static int l2tp_seq_check_rx_window(struct l2tp_session *session, u32 nr) | 498 | static int l2tp_seq_check_rx_window(struct l2tp_session *session, u32 nr) |
545 | { | 499 | { |
546 | u32 nws; | 500 | u32 nws; |
@@ -895,8 +849,7 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb, | |||
895 | u16 version; | 849 | u16 version; |
896 | int length; | 850 | int length; |
897 | 851 | ||
898 | if (tunnel->sock && l2tp_verify_udp_checksum(tunnel->sock, skb)) | 852 | /* UDP has verifed checksum */ |
899 | goto discard_bad_csum; | ||
900 | 853 | ||
901 | /* UDP always verifies the packet length. */ | 854 | /* UDP always verifies the packet length. */ |
902 | __skb_pull(skb, sizeof(struct udphdr)); | 855 | __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 | 932 | ||
980 | return 0; | 933 | return 0; |
981 | 934 | ||
982 | discard_bad_csum: | ||
983 | LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name); | ||
984 | UDP_INC_STATS_USER(tunnel->l2tp_net, UDP_MIB_INERRORS, 0); | ||
985 | atomic_long_inc(&tunnel->stats.rx_errors); | ||
986 | kfree_skb(skb); | ||
987 | |||
988 | return 0; | ||
989 | |||
990 | error: | 935 | error: |
991 | /* Put UDP header back */ | 936 | /* Put UDP header back */ |
992 | __skb_push(skb, sizeof(struct udphdr)); | 937 | __skb_push(skb, sizeof(struct udphdr)); |