* [PATCH 1/5] GRE: Add segmentation offload for GRE TAP device.
@ 2013-01-08 2:31 Pravin B Shelar
2013-01-10 19:47 ` Eric Dumazet
0 siblings, 1 reply; 2+ messages in thread
From: Pravin B Shelar @ 2013-01-08 2:31 UTC (permalink / raw)
To: dev-yBygre7rU0TnMu66kgdUjQ, netdev-u79uwXL29TY76Z2rM5mHXA
From: Pravin Shelar <pshelar-l0M0P4e3n4LQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Pravin B Shelar <pshelar-l0M0P4e3n4LQT0dZR+AlfA@public.gmane.org>
---
include/linux/skbuff.h | 12 ++++++
include/net/gre.h | 6 +++
net/ipv4/af_inet.c | 1 +
net/ipv4/gre.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++
net/ipv4/tcp.c | 1 +
net/ipv4/udp.c | 3 +-
net/ipv6/ip6_offload.c | 1 +
net/ipv6/udp_offload.c | 3 +-
8 files changed, 126 insertions(+), 2 deletions(-)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 320e976..43033f0 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -307,6 +307,8 @@ enum {
SKB_GSO_TCPV6 = 1 << 4,
SKB_GSO_FCOE = 1 << 5,
+
+ SKB_GSO_GRE = 1 << 6,
};
#if BITS_PER_LONG > 32
@@ -797,6 +799,16 @@ static inline int skb_cloned(const struct sk_buff *skb)
(atomic_read(&skb_shinfo(skb)->dataref) & SKB_DATAREF_MASK) != 1;
}
+static inline int skb_unclone(struct sk_buff *skb, gfp_t pri)
+{
+ might_sleep_if(pri & __GFP_WAIT);
+
+ if (skb_cloned(skb))
+ return pskb_expand_head(skb, 0, 0, pri);
+
+ return 0;
+}
+
/**
* skb_header_cloned - is the header a clone
* @skb: buffer to check
diff --git a/include/net/gre.h b/include/net/gre.h
index 8266547..f7d5bfb 100644
--- a/include/net/gre.h
+++ b/include/net/gre.h
@@ -15,4 +15,10 @@ struct gre_protocol {
int gre_add_protocol(const struct gre_protocol *proto, u8 version);
int gre_del_protocol(const struct gre_protocol *proto, u8 version);
+struct gre_base_hdr {
+ __be16 flags;
+ __be16 protocol;
+};
+#define GRE_HEADER_SECTION 4
+
#endif
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 24b384b..9a3d5d2 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1306,6 +1306,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
SKB_GSO_UDP |
SKB_GSO_DODGY |
SKB_GSO_TCP_ECN |
+ SKB_GSO_GRE |
0)))
goto out;
diff --git a/net/ipv4/gre.c b/net/ipv4/gre.c
index 42a4910..0eaaf44 100644
--- a/net/ipv4/gre.c
+++ b/net/ipv4/gre.c
@@ -19,6 +19,7 @@
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/netdevice.h>
+#include <linux/if_tunnel.h>
#include <linux/spinlock.h>
#include <net/protocol.h>
#include <net/gre.h>
@@ -112,12 +113,105 @@ static void gre_err(struct sk_buff *skb, u32 info)
rcu_read_unlock();
}
+static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
+ netdev_features_t features)
+{
+ struct sk_buff *segs = ERR_PTR(-EINVAL);
+ struct gre_base_hdr *greh;
+ unsigned char *mac = skb_mac_header(skb);
+ int net_hlen = skb_network_header_len(skb);
+ int ghl = GRE_HEADER_SECTION;
+ int mac_len;
+ int doffset;
+
+ if (unlikely(skb_shinfo(skb)->gso_type &
+ ~(SKB_GSO_TCPV4 |
+ SKB_GSO_UDP |
+ SKB_GSO_DODGY |
+ SKB_GSO_TCP_ECN |
+ SKB_GSO_GRE |
+ 0)))
+ goto out;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(*greh))))
+ goto out;
+
+ greh = (struct gre_base_hdr *)skb_transport_header(skb);
+
+ if (greh->flags & GRE_KEY)
+ ghl += GRE_HEADER_SECTION;
+ if (greh->flags & GRE_CSUM)
+ ghl += GRE_HEADER_SECTION;
+
+ if (unlikely(!pskb_may_pull(skb, ghl)))
+ goto out;
+
+ __skb_pull(skb, ghl);
+ 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;
+
+ mac_len = skb_inner_network_header(skb) - ((unsigned char *)greh + ghl);
+ skb = segs;
+ do {
+ unsigned char *smac;
+
+ skb_push(skb, doffset);
+
+ skb_reset_mac_header(skb);
+ skb_set_network_header(skb, mac_len);
+ skb_set_transport_header(skb,
+ skb_network_offset(skb) + net_hlen);
+ smac = skb_mac_header(skb);
+ skb->mac_len = mac_len;
+ /* Copy entire outer header from original skb. */
+ memcpy(smac, mac, doffset);
+
+ greh = (struct gre_base_hdr *)skb_transport_header(skb);
+ if (greh->flags & GRE_CSUM) {
+ __be32 *gre_csum = (__be32 *)(greh + 1);
+ *gre_csum = 0;
+ *(__sum16 *)gre_csum = csum_fold(skb_checksum(skb,
+ skb_transport_offset(skb),
+ skb->len - skb_transport_offset(skb),
+ 0));
+ }
+ } while ((skb = skb->next));
+
+out:
+ return segs;
+}
+
+static int gre_gso_send_check(struct sk_buff *skb)
+{
+ struct gre_base_hdr *greh;
+
+ if (!skb->encapsulation)
+ return -EINVAL;
+
+ greh = (struct gre_base_hdr *)skb_transport_header(skb);
+ if (greh->flags & GRE_SEQ)
+ return -EINVAL;
+ return 0;
+}
+
static const struct net_protocol net_gre_protocol = {
.handler = gre_rcv,
.err_handler = gre_err,
.netns_ok = 1,
};
+static const struct net_offload gre_offload = {
+ .callbacks = {
+ .gso_send_check = gre_gso_send_check,
+ .gso_segment = gre_gso_segment,
+ },
+};
+
static int __init gre_init(void)
{
pr_info("GRE over IPv4 demultiplexor driver\n");
@@ -127,11 +221,18 @@ static int __init gre_init(void)
return -EAGAIN;
}
+ if (inet_add_offload(&gre_offload, IPPROTO_GRE)) {
+ pr_err("can't add protocol offload\n");
+ inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
+ return -EAGAIN;
+ }
+
return 0;
}
static void __exit gre_exit(void)
{
+ inet_del_offload(&gre_offload, IPPROTO_GRE);
inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
}
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 1ca2536..08d22e3 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -3025,6 +3025,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
SKB_GSO_DODGY |
SKB_GSO_TCP_ECN |
SKB_GSO_TCPV6 |
+ SKB_GSO_GRE |
0) ||
!(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
goto out;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 79c8dbe..c256008 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -2279,7 +2279,8 @@ struct sk_buff *udp4_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_GRE) ||
!(type & (SKB_GSO_UDP))))
goto out;
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index f26f0da..8234c1d 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -99,6 +99,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
~(SKB_GSO_UDP |
SKB_GSO_DODGY |
SKB_GSO_TCP_ECN |
+ SKB_GSO_GRE |
SKB_GSO_TCPV6 |
0)))
goto out;
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index 0c8934a..cf05cf0 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -56,7 +56,8 @@ 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_GRE) ||
!(type & (SKB_GSO_UDP))))
goto out;
--
1.7.1
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH 1/5] GRE: Add segmentation offload for GRE TAP device.
2013-01-08 2:31 [PATCH 1/5] GRE: Add segmentation offload for GRE TAP device Pravin B Shelar
@ 2013-01-10 19:47 ` Eric Dumazet
0 siblings, 0 replies; 2+ messages in thread
From: Eric Dumazet @ 2013-01-10 19:47 UTC (permalink / raw)
To: Pravin B Shelar; +Cc: dev, netdev, jesse
On Mon, 2013-01-07 at 18:31 -0800, Pravin B Shelar wrote:
> From: Pravin Shelar <pshelar@nicira.com>
Do you have some perf numbers to share ?
> Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
> ---
> include/linux/skbuff.h | 12 ++++++
> include/net/gre.h | 6 +++
> net/ipv4/af_inet.c | 1 +
> net/ipv4/gre.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++
> net/ipv4/tcp.c | 1 +
> net/ipv4/udp.c | 3 +-
> net/ipv6/ip6_offload.c | 1 +
> net/ipv6/udp_offload.c | 3 +-
> 8 files changed, 126 insertions(+), 2 deletions(-)
>
> diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
> index 320e976..43033f0 100644
> --- a/include/linux/skbuff.h
> +++ b/include/linux/skbuff.h
> @@ -307,6 +307,8 @@ enum {
> SKB_GSO_TCPV6 = 1 << 4,
>
> SKB_GSO_FCOE = 1 << 5,
> +
> + SKB_GSO_GRE = 1 << 6,
> };
>
> #if BITS_PER_LONG > 32
> @@ -797,6 +799,16 @@ static inline int skb_cloned(const struct sk_buff *skb)
> (atomic_read(&skb_shinfo(skb)->dataref) & SKB_DATAREF_MASK) != 1;
> }
>
> +static inline int skb_unclone(struct sk_buff *skb, gfp_t pri)
> +{
> + might_sleep_if(pri & __GFP_WAIT);
> +
> + if (skb_cloned(skb))
> + return pskb_expand_head(skb, 0, 0, pri);
> +
> + return 0;
> +}
Should be a commit on its own, and use a "bool"
We have some places in network stack that could use this helper.
(net/ipv6/ah6.c, net/ipv6/reassembly.c,
net/ipv6/xfrm6_mode_tunnel.c, ...)
> +
> /**
> * skb_header_cloned - is the header a clone
> * @skb: buffer to check
> diff --git a/include/net/gre.h b/include/net/gre.h
> index 8266547..f7d5bfb 100644
> --- a/include/net/gre.h
> +++ b/include/net/gre.h
> @@ -15,4 +15,10 @@ struct gre_protocol {
> int gre_add_protocol(const struct gre_protocol *proto, u8 version);
> int gre_del_protocol(const struct gre_protocol *proto, u8 version);
>
> +struct gre_base_hdr {
> + __be16 flags;
> + __be16 protocol;
> +};
> +#define GRE_HEADER_SECTION 4
> +
> #endif
> diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
> index 24b384b..9a3d5d2 100644
> --- a/net/ipv4/af_inet.c
> +++ b/net/ipv4/af_inet.c
> @@ -1306,6 +1306,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
> SKB_GSO_UDP |
> SKB_GSO_DODGY |
> SKB_GSO_TCP_ECN |
> + SKB_GSO_GRE |
> 0)))
> goto out;
>
> diff --git a/net/ipv4/gre.c b/net/ipv4/gre.c
> index 42a4910..0eaaf44 100644
> --- a/net/ipv4/gre.c
> +++ b/net/ipv4/gre.c
> @@ -19,6 +19,7 @@
> #include <linux/in.h>
> #include <linux/ip.h>
> #include <linux/netdevice.h>
> +#include <linux/if_tunnel.h>
> #include <linux/spinlock.h>
> #include <net/protocol.h>
> #include <net/gre.h>
> @@ -112,12 +113,105 @@ static void gre_err(struct sk_buff *skb, u32 info)
> rcu_read_unlock();
> }
>
> +static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
> + netdev_features_t features)
> +{
> + struct sk_buff *segs = ERR_PTR(-EINVAL);
> + struct gre_base_hdr *greh;
> + unsigned char *mac = skb_mac_header(skb);
You should not init mac here, but after the pskb_may_pull()
> + int net_hlen = skb_network_header_len(skb);
> + int ghl = GRE_HEADER_SECTION;
> + int mac_len;
> + int doffset;
> +
> + if (unlikely(skb_shinfo(skb)->gso_type &
> + ~(SKB_GSO_TCPV4 |
> + SKB_GSO_UDP |
> + SKB_GSO_DODGY |
> + SKB_GSO_TCP_ECN |
> + SKB_GSO_GRE |
> + 0)))
> + goto out;
> +
> + if (unlikely(!pskb_may_pull(skb, sizeof(*greh))))
> + goto out;
> +
> + greh = (struct gre_base_hdr *)skb_transport_header(skb);
> +
> + if (greh->flags & GRE_KEY)
> + ghl += GRE_HEADER_SECTION;
> + if (greh->flags & GRE_CSUM)
> + ghl += GRE_HEADER_SECTION;
> +
> + if (unlikely(!pskb_may_pull(skb, ghl)))
> + goto out;
> +
mac can now point to freed memory. You should move its init here :
mac = skb_mac_header(skb);
same for greh
greh = (struct gre_base_hdr *)skb_transport_header(skb);
> + __skb_pull(skb, ghl);
> + 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;
> +
> + mac_len = skb_inner_network_header(skb) - ((unsigned char *)greh + ghl);
> + skb = segs;
> + do {
> + unsigned char *smac;
> +
> + skb_push(skb, doffset);
> +
> + skb_reset_mac_header(skb);
> + skb_set_network_header(skb, mac_len);
> + skb_set_transport_header(skb,
> + skb_network_offset(skb) + net_hlen);
> + smac = skb_mac_header(skb);
> + skb->mac_len = mac_len;
> + /* Copy entire outer header from original skb. */
> + memcpy(smac, mac, doffset);
> +
> + greh = (struct gre_base_hdr *)skb_transport_header(skb);
> + if (greh->flags & GRE_CSUM) {
> + __be32 *gre_csum = (__be32 *)(greh + 1);
> + *gre_csum = 0;
> + *(__sum16 *)gre_csum = csum_fold(skb_checksum(skb,
> + skb_transport_offset(skb),
> + skb->len - skb_transport_offset(skb),
> + 0));
> + }
> + } while ((skb = skb->next));
> +
> +out:
> + return segs;
> +}
> +
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2013-01-10 19:47 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-01-08 2:31 [PATCH 1/5] GRE: Add segmentation offload for GRE TAP device Pravin B Shelar
2013-01-10 19:47 ` Eric Dumazet
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox