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

Reply via email to