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.
Unified · Split
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | /* |
| 2 | − * Copyright (c) 2007-2011 Nicira Networks. | |
| 2 | + * Copyright (c) 2007-2012 Nicira Networks. | |
| 3 | 3 | * |
| 4 | 4 | * This program is free software; you can redistribute it and/or |
| 5 | 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, · 139 unmodified lines | ||
| 145 | 145 | inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb, |
| 146 | 146 | *addr, new_addr, 1); |
| 147 | 147 | } 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 | + } | |
| 151 | 158 | } |
| 152 | 159 | |
| 153 | 160 | 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 | ||
| 197 | 204 | skb->rxhash = 0; |
| 198 | 205 | } |
| 199 | 206 | |
| 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) | |
| 202 | 223 | { |
| 203 | 224 | struct udphdr *uh; |
| 204 | 225 | int err; |
| @@ -210,16 +231,15 @@ static int set_udp_port(struct sk_buff *skb, · 5 unmodified lines | ||
| 210 | 231 | |
| 211 | 232 | uh = udp_hdr(skb); |
| 212 | 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 | 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 | 239 | return 0; |
| 219 | 240 | } |
| 220 | 241 | |
| 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) | |
| 223 | 243 | { |
| 224 | 244 | struct tcphdr *th; |
| 225 | 245 | int err; |
| @@ -328,11 +348,11 @@ static int execute_set_action(struct sk_buff *skb, · 102 unmodified lines | ||
| 328 | 348 | break; |
| 329 | 349 | |
| 330 | 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 | 352 | break; |
| 333 | 353 | |
| 334 | 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 | 356 | break; |
| 337 | 357 | } |
| 338 | 358 | |