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>
This commit is contained in:
commit
1b5d35358e
|
@ -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,
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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:
|
goto csum_error;
|
||||||
if (!csum_fold(skb->csum))
|
|
||||||
break;
|
|
||||||
/* fall through */
|
|
||||||
case CHECKSUM_NONE:
|
|
||||||
skb->csum = 0;
|
|
||||||
if (__skb_checksum_complete(skb))
|
|
||||||
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)
|
||||||
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:
|
goto drop;
|
||||||
if (!csum_fold(skb->csum))
|
|
||||||
break;
|
|
||||||
/* fall through */
|
|
||||||
case CHECKSUM_NONE:
|
|
||||||
skb->csum = 0;
|
|
||||||
if (__skb_checksum_complete(skb))
|
|
||||||
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)
|
||||||
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,22 +692,11 @@ 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) {
|
LIMIT_NETDEBUG(KERN_DEBUG
|
||||||
case CHECKSUM_COMPLETE:
|
"ICMPv6 checksum failed [%pI6c > %pI6c]\n",
|
||||||
if (!csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6,
|
saddr, daddr);
|
||||||
skb->csum))
|
goto csum_error;
|
||||||
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
|
|
||||||
"ICMPv6 checksum failed [%pI6c > %pI6c]\n",
|
|
||||||
saddr, daddr);
|
|
||||||
goto csum_error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pskb_pull(skb, sizeof(*hdr)))
|
if (!pskb_pull(skb, sizeof(*hdr)))
|
||||||
|
|
|
@ -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)
|
||||||
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 @@ out:
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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));
|
||||||
|
|
Loading…
Reference in New Issue