提交 3576fd79 编写于 作者: G Glenn Griffin 提交者: David S. Miller

openvswitch: Fix L4 checksum handling when dealing with IP fragments

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: NGlenn Griffin <ggriffin.kernel@gmail.com>
Acked-by: NPravin B Shelar <pshelar@nicira.com>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
上级 3d0e0af4
...@@ -273,28 +273,36 @@ static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *flow_key, ...@@ -273,28 +273,36 @@ static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *flow_key,
return 0; return 0;
} }
static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, static void update_ip_l4_checksum(struct sk_buff *skb, struct iphdr *nh,
__be32 *addr, __be32 new_addr) __be32 addr, __be32 new_addr)
{ {
int transport_len = skb->len - skb_transport_offset(skb); int transport_len = skb->len - skb_transport_offset(skb);
if (nh->frag_off & htons(IP_OFFSET))
return;
if (nh->protocol == IPPROTO_TCP) { if (nh->protocol == IPPROTO_TCP) {
if (likely(transport_len >= sizeof(struct tcphdr))) if (likely(transport_len >= sizeof(struct tcphdr)))
inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb, inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb,
*addr, new_addr, 1); addr, new_addr, 1);
} else if (nh->protocol == IPPROTO_UDP) { } else if (nh->protocol == IPPROTO_UDP) {
if (likely(transport_len >= sizeof(struct udphdr))) { if (likely(transport_len >= sizeof(struct udphdr))) {
struct udphdr *uh = udp_hdr(skb); struct udphdr *uh = udp_hdr(skb);
if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
inet_proto_csum_replace4(&uh->check, skb, inet_proto_csum_replace4(&uh->check, skb,
*addr, new_addr, 1); addr, new_addr, 1);
if (!uh->check) if (!uh->check)
uh->check = CSUM_MANGLED_0; 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); csum_replace4(&nh->check, *addr, new_addr);
skb_clear_hash(skb); skb_clear_hash(skb);
*addr = new_addr; *addr = new_addr;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册