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 | ||
