Skip to content

Commit

Permalink
datapath: Fix L4 checksum handling when dealing with IP fragments
Browse files Browse the repository at this point in the history
openvswitch modifies the L4 checksum of a packet when modifying
the ip address. When an IP packet is fragmented only the first
fragment contains an L4 header and checksum. Prior to this change
openvswitch would modify all fragments, modifying application data
in non-first fragments, causing checksum failures in the
reassembled packet.

Signed-off-by: Glenn Griffin <ggriffin.kernel@gmail.com>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

Upstream: 3576fd794b3 ("openvswitch: Fix L4 checksum handling when
dealing with IP fragments").

Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
  • Loading branch information
Glenn Griffin authored and Pravin B Shelar committed Aug 18, 2015
1 parent 7148fd9 commit 000fcbd
Showing 1 changed file with 13 additions and 4 deletions.
17 changes: 13 additions & 4 deletions datapath/actions.c
Expand Up @@ -280,28 +280,37 @@ static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *flow_key,
return 0;
}

static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
__be32 *addr, __be32 new_addr)
static void update_ip_l4_checksum(struct sk_buff *skb, struct iphdr *nh,
__be32 addr, __be32 new_addr)
{
int transport_len = skb->len - skb_transport_offset(skb);

if (nh->frag_off & htons(IP_OFFSET))
return;

if (nh->protocol == IPPROTO_TCP) {
if (likely(transport_len >= sizeof(struct tcphdr)))
inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb,
*addr, new_addr, 1);
addr, new_addr, 1);
} else if (nh->protocol == IPPROTO_UDP) {
if (likely(transport_len >= sizeof(struct udphdr))) {
struct udphdr *uh = udp_hdr(skb);

if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
inet_proto_csum_replace4(&uh->check, skb,
*addr, new_addr, 1);
addr, new_addr, 1);
if (!uh->check)
uh->check = CSUM_MANGLED_0;
}
}
}

}

static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
__be32 *addr, __be32 new_addr)
{
update_ip_l4_checksum(skb, nh, *addr, new_addr);
csum_replace4(&nh->check, *addr, new_addr);
skb_clear_hash(skb);
*addr = new_addr;
Expand Down

0 comments on commit 000fcbd

Please sign in to comment.