diff options
author | Jesse Gross <jesse@nicira.com> | 2012-03-06 18:05:46 -0500 |
---|---|---|
committer | Jesse Gross <jesse@nicira.com> | 2012-03-07 17:36:57 -0500 |
commit | 81e5d41d7ed4f6c61ba3d2414f4f9ddf6d934ebb (patch) | |
tree | c85f6f4cd07c34a633475a4667f418bf402d21a9 /net/openvswitch | |
parent | 651a68ea2ce9738b84e928836053b2e0fb5db2ba (diff) |
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.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Diffstat (limited to 'net/openvswitch')
-rw-r--r-- | net/openvswitch/actions.c | 44 |
1 files changed, 32 insertions, 12 deletions
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 2725d1bdf291..48badffaafc1 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) 2007-2011 Nicira Networks. | 2 | * Copyright (c) 2007-2012 Nicira Networks. |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or | 4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of version 2 of the GNU General Public | 5 | * 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, | |||
145 | inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb, | 145 | inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb, |
146 | *addr, new_addr, 1); | 146 | *addr, new_addr, 1); |
147 | } else if (nh->protocol == IPPROTO_UDP) { | 147 | } else if (nh->protocol == IPPROTO_UDP) { |
148 | if (likely(transport_len >= sizeof(struct udphdr))) | 148 | if (likely(transport_len >= sizeof(struct udphdr))) { |
149 | inet_proto_csum_replace4(&udp_hdr(skb)->check, skb, | 149 | struct udphdr *uh = udp_hdr(skb); |
150 | *addr, new_addr, 1); | 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 | } | ||
151 | } | 158 | } |
152 | 159 | ||
153 | csum_replace4(&nh->check, *addr, new_addr); | 160 | csum_replace4(&nh->check, *addr, new_addr); |
@@ -197,8 +204,22 @@ static void set_tp_port(struct sk_buff *skb, __be16 *port, | |||
197 | skb->rxhash = 0; | 204 | skb->rxhash = 0; |
198 | } | 205 | } |
199 | 206 | ||
200 | static int set_udp_port(struct sk_buff *skb, | 207 | static void set_udp_port(struct sk_buff *skb, __be16 *port, __be16 new_port) |
201 | const struct ovs_key_udp *udp_port_key) | 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) | ||
202 | { | 223 | { |
203 | struct udphdr *uh; | 224 | struct udphdr *uh; |
204 | int err; | 225 | int err; |
@@ -210,16 +231,15 @@ static int set_udp_port(struct sk_buff *skb, | |||
210 | 231 | ||
211 | uh = udp_hdr(skb); | 232 | uh = udp_hdr(skb); |
212 | if (udp_port_key->udp_src != uh->source) | 233 | 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); |
214 | 235 | ||
215 | if (udp_port_key->udp_dst != uh->dest) | 236 | 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); |
217 | 238 | ||
218 | return 0; | 239 | return 0; |
219 | } | 240 | } |
220 | 241 | ||
221 | static int set_tcp_port(struct sk_buff *skb, | 242 | static int set_tcp(struct sk_buff *skb, const struct ovs_key_tcp *tcp_port_key) |
222 | const struct ovs_key_tcp *tcp_port_key) | ||
223 | { | 243 | { |
224 | struct tcphdr *th; | 244 | struct tcphdr *th; |
225 | int err; | 245 | int err; |
@@ -328,11 +348,11 @@ static int execute_set_action(struct sk_buff *skb, | |||
328 | break; | 348 | break; |
329 | 349 | ||
330 | case OVS_KEY_ATTR_TCP: | 350 | case OVS_KEY_ATTR_TCP: |
331 | err = set_tcp_port(skb, nla_data(nested_attr)); | 351 | err = set_tcp(skb, nla_data(nested_attr)); |
332 | break; | 352 | break; |
333 | 353 | ||
334 | case OVS_KEY_ATTR_UDP: | 354 | case OVS_KEY_ATTR_UDP: |
335 | err = set_udp_port(skb, nla_data(nested_attr)); | 355 | err = set_udp(skb, nla_data(nested_attr)); |
336 | break; | 356 | break; |
337 | } | 357 | } |
338 | 358 | ||