[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CALnjE+peRJga4HK7v9e-1v1F0xftNkejd9O5KngVRuu2P2VtQw@mail.gmail.com>
Date: Thu, 18 Apr 2013 10:23:28 -0700
From: Pravin Shelar <pshelar@...ira.com>
To: Cong Wang <amwang@...hat.com>
Cc: netdev@...r.kernel.org, Jesse Gross <jesse@...ira.com>,
Stephen Hemminger <stephen@...workplumber.org>,
"David S. Miller" <davem@...emloft.net>
Subject: Re: [Patch net-next v4 5/5] ipv6: Add generic UDP Tunnel segmentation
On Tue, Apr 16, 2013 at 10:10 PM, Cong Wang <amwang@...hat.com> wrote:
> From: Cong Wang <amwang@...hat.com>
>
> Similar to commit 731362674580cb0c696cd1b1a03d8461a10cf90a
> (tunneling: Add generic Tunnel segmentation)
>
> This patch adds generic tunneling offloading support for IPv6-UDP
> based tunnels.
>
> This can be used by tunneling protocols like VXLAN.
>
> Cc: Jesse Gross <jesse@...ira.com>
> Cc: Pravin B Shelar <pshelar@...ira.com>
> Cc: Stephen Hemminger <stephen@...workplumber.org>
> Cc: David S. Miller <davem@...emloft.net>
> Signed-off-by: Cong Wang <amwang@...hat.com>
> ---
> net/ipv6/ip6_offload.c | 4 +-
> net/ipv6/udp_offload.c | 155 +++++++++++++++++++++++++++++++++---------------
> 2 files changed, 110 insertions(+), 49 deletions(-)
>
> diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
> index 71b766e..87fbf2e 100644
> --- a/net/ipv6/ip6_offload.c
> +++ b/net/ipv6/ip6_offload.c
> @@ -91,6 +91,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
> unsigned int unfrag_ip6hlen;
> u8 *prevhdr;
> int offset = 0;
> + bool tunnel;
>
> if (unlikely(skb_shinfo(skb)->gso_type &
> ~(SKB_GSO_UDP |
> @@ -105,6 +106,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
> if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
> goto out;
>
> + tunnel = skb->encapsulation;
> ipv6h = ipv6_hdr(skb);
> __skb_pull(skb, sizeof(*ipv6h));
> segs = ERR_PTR(-EPROTONOSUPPORT);
> @@ -125,7 +127,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
> ipv6h = ipv6_hdr(skb);
> ipv6h->payload_len = htons(skb->len - skb->mac_len -
> sizeof(*ipv6h));
> - if (proto == IPPROTO_UDP) {
> + if (!tunnel && proto == IPPROTO_UDP) {
> unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
> fptr = (struct frag_hdr *)(skb_network_header(skb) +
> unfrag_ip6hlen);
> diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
> index 3bb3a89..bbde7ba 100644
> --- a/net/ipv6/udp_offload.c
> +++ b/net/ipv6/udp_offload.c
> @@ -21,26 +21,81 @@ static int udp6_ufo_send_check(struct sk_buff *skb)
> const struct ipv6hdr *ipv6h;
> struct udphdr *uh;
>
> - /* UDP Tunnel offload on ipv6 is not yet supported. */
> - if (skb->encapsulation)
> - return -EINVAL;
> -
> if (!pskb_may_pull(skb, sizeof(*uh)))
> return -EINVAL;
>
> - ipv6h = ipv6_hdr(skb);
> - uh = udp_hdr(skb);
> + if (likely(!skb->encapsulation)) {
> + ipv6h = ipv6_hdr(skb);
> + uh = udp_hdr(skb);
> +
> + uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->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_ipv6_magic(&ipv6h->saddr, &ipv6h->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_udp6_tunnel_segment(struct sk_buff *skb,
> + netdev_features_t features)
> +{
> + struct sk_buff *segs = ERR_PTR(-EINVAL);
> + int mac_len = skb->mac_len;
> + int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
> + int outer_hlen;
> + netdev_features_t enc_features;
> +
> + if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
> + goto out;
> +
> + skb->encapsulation = 0;
> + __skb_pull(skb, tnl_hlen);
> + skb_reset_mac_header(skb);
> + skb_set_network_header(skb, skb_inner_network_offset(skb));
> + skb->mac_len = skb_inner_network_offset(skb);
> +
> + /* segment inner packet. */
> + enc_features = skb->dev->hw_enc_features & netif_skb_features(skb);
> + segs = skb_mac_gso_segment(skb, enc_features);
> + if (!segs || IS_ERR(segs))
> + goto out;
> +
> + outer_hlen = skb_tnl_header_len(skb);
> + skb = segs;
> + do {
> + struct udphdr *uh;
> + int udp_offset = outer_hlen - tnl_hlen;
> +
> + skb->mac_len = mac_len;
> +
> + skb_push(skb, outer_hlen);
> + skb_reset_mac_header(skb);
> + skb_set_network_header(skb, mac_len);
> + skb_set_transport_header(skb, udp_offset);
> + uh = udp_hdr(skb);
> + uh->len = htons(skb->len - udp_offset);
> +
> + /* csum segment if tunnel sets skb with csum. */
> + if (unlikely(uh->check)) {
> + struct ipv6hdr *iph = ipv6_hdr(skb);
> +
> + uh->check = csum_ipv6_magic(&iph->saddr, &iph->daddr,
> + skb->len - udp_offset,
> + IPPROTO_UDP, 0);
> + if (uh->check == 0)
> + uh->check = CSUM_MANGLED_0;
> +
> + }
> + skb->ip_summed = CHECKSUM_NONE;
> + } while ((skb = skb->next));
> +out:
> + return segs;
> +}
> +
skb_udp6_tunnel_segment() looks alot like skb_udp_tunnel_segment(),
difference is checksum calculation. I think we can write a common
function here.
> static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
> - netdev_features_t features)
> + netdev_features_t features)
> {
> struct sk_buff *segs = ERR_PTR(-EINVAL);
> unsigned int mss;
> @@ -73,43 +128,47 @@ static struct sk_buff *udp6_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;
> -
> - /* Check if there is enough headroom to insert fragment header. */
> - if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
> - pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
> - goto out;
> + if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL)
> + segs = skb_udp6_tunnel_segment(skb, features);
> + else {
> + /* 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;
> +
> + /* Check if there is enough headroom to insert fragment header. */
> + if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
> + pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
> + goto out;
>
> - /* Find the unfragmentable header and shift it left by frag_hdr_sz
> - * bytes to insert fragment header.
> - */
> - unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
> - nexthdr = *prevhdr;
> - *prevhdr = NEXTHDR_FRAGMENT;
> - unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
> - unfrag_ip6hlen;
> - mac_start = skb_mac_header(skb);
> - memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
> -
> - skb->mac_header -= frag_hdr_sz;
> - skb->network_header -= frag_hdr_sz;
> -
> - fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
> - fptr->nexthdr = nexthdr;
> - fptr->reserved = 0;
> - ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
> -
> - /* Fragment the skb. ipv6 header and the remaining fields of the
> - * fragment header are updated in ipv6_gso_segment()
> - */
> - segs = skb_segment(skb, features);
> + /* Find the unfragmentable header and shift it left by frag_hdr_sz
> + * bytes to insert fragment header.
> + */
> + unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
> + nexthdr = *prevhdr;
> + *prevhdr = NEXTHDR_FRAGMENT;
> + unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
> + unfrag_ip6hlen;
> + mac_start = skb_mac_header(skb);
> + memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
> +
> + skb->mac_header -= frag_hdr_sz;
> + skb->network_header -= frag_hdr_sz;
> +
> + fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
> + fptr->nexthdr = nexthdr;
> + fptr->reserved = 0;
> + ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
> +
> + /* Fragment the skb. ipv6 header and the remaining fields of the
> + * fragment header are updated in ipv6_gso_segment()
> + */
> + segs = skb_segment(skb, features);
> + }
>
> out:
> return segs;
> --
> 1.7.7.6
>
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists