Signed-off-by: Pravin B Shelar <pshe...@nicira.com> --- include/linux/skbuff.h | 3 ++ net/ipv4/af_inet.c | 6 ++- net/ipv4/udp.c | 106 ++++++++++++++++++++++++++++++++++++------------ net/ipv6/af_inet6.c | 1 + net/ipv6/udp.c | 7 +++- 5 files changed, 96 insertions(+), 27 deletions(-)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 585aca7..a23df86 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -309,6 +309,8 @@ enum { SKB_GSO_FCOE = 1 << 5, SKB_GSO_GRE = 1 << 6, + + SKB_GSO_UDP_TUNNEL = 1 << 7, }; #if BITS_PER_LONG > 32 @@ -422,6 +424,7 @@ struct sk_buff { __u16 csum_offset; }; }; + __u16 tunnel_hlen; __u32 priority; kmemcheck_bitfield_begin(flags1); __u8 local_df:1, diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 55cecba..b0ea634 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1294,6 +1294,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int ihl; int id; unsigned int offset = 0; + bool udp_tunnel; if (!(features & NETIF_F_V4_CSUM)) features &= ~NETIF_F_SG; @@ -1304,6 +1305,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_GRE | + SKB_GSO_UDP_TUNNEL | 0))) goto out; @@ -1318,6 +1320,8 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, if (unlikely(!pskb_may_pull(skb, ihl))) goto out; + udp_tunnel = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL); + __skb_pull(skb, ihl); skb_reset_transport_header(skb); iph = ip_hdr(skb); @@ -1337,7 +1341,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, skb = segs; do { iph = ip_hdr(skb); - if (proto == IPPROTO_UDP) { + if (!udp_tunnel && proto == IPPROTO_UDP) { iph->id = htons(id); iph->frag_off = htons(offset >> 3); if (skb->next != NULL) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index c256008..1ce522a 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2246,31 +2246,79 @@ void __init udp_init(void) int udp4_ufo_send_check(struct sk_buff *skb) { - const struct iphdr *iph; - struct udphdr *uh; - - if (!pskb_may_pull(skb, sizeof(*uh))) + if (!pskb_may_pull(skb, sizeof(struct udphdr))) return -EINVAL; - iph = ip_hdr(skb); - uh = udp_hdr(skb); + if (!skb->tunnel_hlen) { + const struct iphdr *iph; + struct udphdr *uh; + + iph = ip_hdr(skb); + uh = udp_hdr(skb); - uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, - IPPROTO_UDP, 0); - skb->csum_start = skb_transport_header(skb) - skb->head; - skb->csum_offset = offsetof(struct udphdr, check); - skb->ip_summed = CHECKSUM_PARTIAL; + uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, + IPPROTO_UDP, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + skb->ip_summed = CHECKSUM_PARTIAL; + } return 0; } +static struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + int mac_len = skb->mac_len; + unsigned char *mac = skb_mac_header(skb); + int hlen = sizeof(struct udphdr) + skb->tunnel_hlen; + int doffset; + int network_hlen = skb_network_header_len(skb); + + skb->tunnel_hlen = 0; + if (unlikely(!pskb_may_pull(skb, hlen))) + goto out; + + __skb_pull(skb, hlen); + skb_reset_mac_header(skb); + skb_set_network_header(skb, skb->mac_len); + doffset = skb_mac_header(skb) - mac; + + /* segment inner packet. */ + segs = skb_gso_segment(skb, 0); + if (!segs || IS_ERR(segs)) + goto out; + + skb = segs; + do { + unsigned char *smac; + struct udphdr *uh; + + skb_push(skb, doffset); + + skb_reset_mac_header(skb); + smac = skb_mac_header(skb); + skb->mac_len = mac_len; + + /* Copy entire outer header from original skb. */ + memcpy(smac, mac, doffset); + + skb_set_network_header(skb, mac_len); + skb->ip_summed = CHECKSUM_NONE; + skb_set_transport_header(skb, skb_network_offset(skb) + network_hlen); + uh = udp_hdr(skb); + uh->len = htons(skb->len - (doffset - hlen)); + uh->check = 0; + } while ((skb = skb->next)); +out: + return segs; +} + struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, netdev_features_t features) { struct sk_buff *segs = ERR_PTR(-EINVAL); unsigned int mss; - int offset; - __wsum csum; - mss = skb_shinfo(skb)->gso_size; if (unlikely(skb->len <= mss)) goto out; @@ -2280,6 +2328,7 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int type = skb_shinfo(skb)->gso_type; if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | + SKB_GSO_UDP_TUNNEL | SKB_GSO_GRE) || !(type & (SKB_GSO_UDP)))) goto out; @@ -2290,20 +2339,27 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, goto out; } - /* Do software UFO. Complete and fill in the UDP checksum as HW cannot - * do checksum of UDP packets sent as multiple IP fragments. - */ - offset = skb_checksum_start_offset(skb); - csum = skb_checksum(skb, offset, skb->len - offset, 0); - offset += skb->csum_offset; - *(__sum16 *)(skb->data + offset) = csum_fold(csum); - skb->ip_summed = CHECKSUM_NONE; - /* Fragment the skb. IP headers of the fragments are updated in * inet_gso_segment() */ - segs = skb_segment(skb, features); + if (skb->tunnel_hlen && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) + segs = skb_udp_tunnel_segment(skb, features); + else { + int offset; + __wsum csum; + + /* Do software UFO. Complete and fill in the UDP checksum as + * HW cannot do checksum of UDP packets sent as multiple + * IP fragments. + */ + offset = skb_checksum_start_offset(skb); + csum = skb_checksum(skb, offset, skb->len - offset, 0); + offset += skb->csum_offset; + *(__sum16 *)(skb->data + offset) = csum_fold(csum); + skb->ip_summed = CHECKSUM_NONE; + + segs = skb_segment(skb, features); + } out: return segs; } - diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 820fc1f..5fe4def 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -781,6 +781,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, SKB_GSO_TCP_ECN | SKB_GSO_TCPV6 | SKB_GSO_GRE | + SKB_GSO_UDP_TUNNEL | 0))) goto out; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index e247a2b..cdbbe68 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1348,6 +1348,9 @@ static int udp6_ufo_send_check(struct sk_buff *skb) const struct ipv6hdr *ipv6h; struct udphdr *uh; + if (skb->tunnel_hlen) + return -EINVAL; + if (!pskb_may_pull(skb, sizeof(*uh))) return -EINVAL; @@ -1383,7 +1386,9 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, /* Packet is from an untrusted source, reset gso_segs. */ int type = skb_shinfo(skb)->gso_type; - if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | + if (unlikely(type & ~(SKB_GSO_UDP | + SKB_GSO_DODGY | + SKB_GSO_UDP_TUNNEL | SKB_GSO_GRE) || !(type & (SKB_GSO_UDP)))) goto out; -- 1.7.10 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev