aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Gross <jesse@nicira.com>2012-03-06 13:09:13 -0800
committerJesse Gross <jesse@nicira.com>2012-03-07 14:39:55 -0800
commit7cde6a8a537575e074caef397e54d35a7d812bcf (patch)
tree8285e2bd88f8cccc5407df8392407f1111008dd4
parenta810f7d725d4ae87690e8790ffdcc2aa26dd8c8f (diff)
datapath: Fix checksum update for actions on UDP packets.
When modifying IP addresses or ports on a UDP packet we don't correctly follow the rules for unchecksummed packets. This meant that packets without a checksum can be given a incorrect new checksum and packets with a checksum can become marked as being unchecksummed. This fixes it to handle those requirements. Bug #8937 Signed-off-by: Jesse Gross <jesse@nicira.com> Acked-by: Ben Pfaff <blp@nicira.com>
-rw-r--r--datapath/actions.c45
-rw-r--r--datapath/linux/Modules.mk1
-rw-r--r--datapath/linux/compat/include/linux/checksum.h10
3 files changed, 44 insertions, 12 deletions
diff --git a/datapath/actions.c b/datapath/actions.c
index 4b076039..29038016 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
@@ -147,9 +147,17 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb,
*addr, new_addr, 1);
} else if (nh->protocol == IPPROTO_UDP) {
- if (likely(transport_len >= sizeof(struct udphdr)))
- inet_proto_csum_replace4(&udp_hdr(skb)->check, skb,
- *addr, new_addr, 1);
+ if (likely(transport_len >= sizeof(struct udphdr))) {
+ struct udphdr *uh = udp_hdr(skb);
+
+ if (uh->check ||
+ get_ip_summed(skb) == OVS_CSUM_PARTIAL) {
+ inet_proto_csum_replace4(&uh->check, skb,
+ *addr, new_addr, 1);
+ if (!uh->check)
+ uh->check = CSUM_MANGLED_0;
+ }
+ }
}
csum_replace4(&nh->check, *addr, new_addr);
@@ -199,8 +207,22 @@ static void set_tp_port(struct sk_buff *skb, __be16 *port,
skb_clear_rxhash(skb);
}
-static int set_udp_port(struct sk_buff *skb,
- const struct ovs_key_udp *udp_port_key)
+static void set_udp_port(struct sk_buff *skb, __be16 *port, __be16 new_port)
+{
+ struct udphdr *uh = udp_hdr(skb);
+
+ if (uh->check && get_ip_summed(skb) != OVS_CSUM_PARTIAL) {
+ set_tp_port(skb, port, new_port, &uh->check);
+
+ if (!uh->check)
+ uh->check = CSUM_MANGLED_0;
+ } else {
+ *port = new_port;
+ skb_clear_rxhash(skb);
+ }
+}
+
+static int set_udp(struct sk_buff *skb, const struct ovs_key_udp *udp_port_key)
{
struct udphdr *uh;
int err;
@@ -212,16 +234,15 @@ static int set_udp_port(struct sk_buff *skb,
uh = udp_hdr(skb);
if (udp_port_key->udp_src != uh->source)
- set_tp_port(skb, &uh->source, udp_port_key->udp_src, &uh->check);
+ set_udp_port(skb, &uh->source, udp_port_key->udp_src);
if (udp_port_key->udp_dst != uh->dest)
- set_tp_port(skb, &uh->dest, udp_port_key->udp_dst, &uh->check);
+ set_udp_port(skb, &uh->dest, udp_port_key->udp_dst);
return 0;
}
-static int set_tcp_port(struct sk_buff *skb,
- const struct ovs_key_tcp *tcp_port_key)
+static int set_tcp(struct sk_buff *skb, const struct ovs_key_tcp *tcp_port_key)
{
struct tcphdr *th;
int err;
@@ -334,11 +355,11 @@ static int execute_set_action(struct sk_buff *skb,
break;
case OVS_KEY_ATTR_TCP:
- err = set_tcp_port(skb, nla_data(nested_attr));
+ err = set_tcp(skb, nla_data(nested_attr));
break;
case OVS_KEY_ATTR_UDP:
- err = set_udp_port(skb, nla_data(nested_attr));
+ err = set_udp(skb, nla_data(nested_attr));
break;
}
diff --git a/datapath/linux/Modules.mk b/datapath/linux/Modules.mk
index 97d977b7..86341ad0 100644
--- a/datapath/linux/Modules.mk
+++ b/datapath/linux/Modules.mk
@@ -13,6 +13,7 @@ openvswitch_sources += \
linux/compat/time.c \
linux/compat/workqueue.c
openvswitch_headers += \
+ linux/compat/include/linux/checksum.h \
linux/compat/include/linux/compiler.h \
linux/compat/include/linux/compiler-gcc.h \
linux/compat/include/linux/cpumask.h \
diff --git a/datapath/linux/compat/include/linux/checksum.h b/datapath/linux/compat/include/linux/checksum.h
new file mode 100644
index 00000000..1d4fefce
--- /dev/null
+++ b/datapath/linux/compat/include/linux/checksum.h
@@ -0,0 +1,10 @@
+#ifndef __LINUX_CHECKSUM_WRAPPER_H
+#define __LINUX_CHECKSUM_WRAPPER_H 1
+
+#include_next <linux/checksum.h>
+
+#ifndef CSUM_MANGLED_0
+#define CSUM_MANGLED_0 ((__force __sum16)0xffff)
+#endif
+
+#endif