History

openvswitch: 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.

Jesse Gross committed 14 years ago

Commit 81e5d411 parent 651a68e1 file · +32 −12Browse files

Unified · Split

net/openvswitch/actions.c+32−12
@@ -1,5 +1,5 @@
11 /*
2− * Copyright (c) 2007-2011 Nicira Networks.
2+ * Copyright (c) 2007-2012 Nicira Networks.
33 *
44 * This program is free software; you can redistribute it and/or
55 * modify it under the terms of version 2 of the GNU General Public
@@ -145,9 +145,16 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, · 139 unmodified lines
145145 inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb,
146146 *addr, new_addr, 1);
147147 } else if (nh->protocol == IPPROTO_UDP) {
148− if (likely(transport_len >= sizeof(struct udphdr)))
149− inet_proto_csum_replace4(&udp_hdr(skb)->check, skb,
150− *addr, new_addr, 1);
148+ if (likely(transport_len >= sizeof(struct udphdr))) {
149+ struct udphdr *uh = udp_hdr(skb);
150+
151+ if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
152+ inet_proto_csum_replace4(&uh->check, skb,
153+ *addr, new_addr, 1);
154+ if (!uh->check)
155+ uh->check = CSUM_MANGLED_0;
156+ }
157+ }
151158 }
152159
153160 csum_replace4(&nh->check, *addr, new_addr);
@@ -197,8 +204,22 @@ static void set_tp_port(struct sk_buff *skb, __be16 *port, · 43 unmodified lines
197204 skb->rxhash = 0;
198205 }
199206
200−static int set_udp_port(struct sk_buff *skb,
201− const struct ovs_key_udp *udp_port_key)
207+static void set_udp_port(struct sk_buff *skb, __be16 *port, __be16 new_port)
208+{
209+ struct udphdr *uh = udp_hdr(skb);
210+
211+ if (uh->check && skb->ip_summed != CHECKSUM_PARTIAL) {
212+ set_tp_port(skb, port, new_port, &uh->check);
213+
214+ if (!uh->check)
215+ uh->check = CSUM_MANGLED_0;
216+ } else {
217+ *port = new_port;
218+ skb->rxhash = 0;
219+ }
220+}
221+
222+static int set_udp(struct sk_buff *skb, const struct ovs_key_udp *udp_port_key)
202223 {
203224 struct udphdr *uh;
204225 int err;
@@ -210,16 +231,15 @@ static int set_udp_port(struct sk_buff *skb, · 5 unmodified lines
210231
211232 uh = udp_hdr(skb);
212233 if (udp_port_key->udp_src != uh->source)
213− set_tp_port(skb, &uh->source, udp_port_key->udp_src, &uh->check);
234+ set_udp_port(skb, &uh->source, udp_port_key->udp_src);
214235
215236 if (udp_port_key->udp_dst != uh->dest)
216− set_tp_port(skb, &uh->dest, udp_port_key->udp_dst, &uh->check);
237+ set_udp_port(skb, &uh->dest, udp_port_key->udp_dst);
217238
218239 return 0;
219240 }
220241
221−static int set_tcp_port(struct sk_buff *skb,
222− const struct ovs_key_tcp *tcp_port_key)
242+static int set_tcp(struct sk_buff *skb, const struct ovs_key_tcp *tcp_port_key)
223243 {
224244 struct tcphdr *th;
225245 int err;
@@ -328,11 +348,11 @@ static int execute_set_action(struct sk_buff *skb, · 102 unmodified lines
328348 break;
329349
330350 case OVS_KEY_ATTR_TCP:
331− err = set_tcp_port(skb, nla_data(nested_attr));
351+ err = set_tcp(skb, nla_data(nested_attr));
332352 break;
333353
334354 case OVS_KEY_ATTR_UDP:
335− err = set_udp_port(skb, nla_data(nested_attr));
355+ err = set_udp(skb, nla_data(nested_attr));
336356 break;
337357 }
338358