From cc1d0dea7f970ec97207f1c9aca82b70422c7b00 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Sun, 22 Dec 2013 19:43:58 -0800 Subject: [PATCH] datapath: compat: Configure check GRE DEMUX. RHEL6-openstack kernel has backported gre DEMUX module, Therefore add configure check to detect it. Signed-off-by: Pravin B Shelar Acked-by: Jesse Gross Bug #21936 --- acinclude.m4 | 1 + datapath/linux/compat/gre.c | 281 +++++++++++------------- datapath/linux/compat/include/net/gre.h | 42 ++-- 3 files changed, 146 insertions(+), 178 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 99b4054bf..7078654b8 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -274,6 +274,7 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [ OVS_GREP_IFELSE([$KSRC/include/net/checksum.h], [csum_unfold]) OVS_GREP_IFELSE([$KSRC/include/net/genetlink.h], [parallel_ops]) + OVS_GREP_IFELSE([$KSRC/include/net/gre.h], [gre_cisco_register]) OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_get_be16]) OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_put_be16]) OVS_GREP_IFELSE([$KSRC/include/net/netlink.h], [nla_put_be32]) diff --git a/datapath/linux/compat/gre.c b/datapath/linux/compat/gre.c index f35f11fd6..58b1e73e9 100644 --- a/datapath/linux/compat/gre.c +++ b/datapath/linux/compat/gre.c @@ -39,80 +39,88 @@ #include "gso.h" -static void gre_csum_fix(struct sk_buff *skb) -{ - struct gre_base_hdr *greh; - __be32 *options; - int gre_offset = skb_transport_offset(skb); +#ifndef HAVE_GRE_CISCO_REGISTER - greh = (struct gre_base_hdr *)skb_transport_header(skb); - options = ((__be32 *)greh + 1); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) - *options = 0; - *(__sum16 *)options = csum_fold(skb_checksum(skb, gre_offset, - skb->len - gre_offset, 0)); -} +#define GREPROTO_CISCO 0 +#define GREPROTO_MAX 1 -struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum) +struct gre_protocol { + int (*handler)(struct sk_buff *skb); +}; +static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly; + +static int gre_rcv(struct sk_buff *skb) { - int err; + const struct gre_protocol *proto; + u8 ver; + int ret; - skb_reset_inner_headers(skb); + if (!pskb_may_pull(skb, 12)) + goto drop; - if (skb_is_gso(skb)) { - if (gre_csum) - OVS_GSO_CB(skb)->fix_segment = gre_csum_fix; - } else { - if (skb->ip_summed == CHECKSUM_PARTIAL && gre_csum) { - err = skb_checksum_help(skb); - if (err) - goto error; + ver = skb->data[1] & 0x7f; + if (ver >= GREPROTO_MAX) + goto drop; - } else if (skb->ip_summed != CHECKSUM_PARTIAL) - skb->ip_summed = CHECKSUM_NONE; - } - return skb; -error: + rcu_read_lock(); + proto = rcu_dereference(gre_proto[ver]); + if (!proto || !proto->handler) + goto drop_unlock; + ret = proto->handler(skb); + rcu_read_unlock(); + return ret; + +drop_unlock: + rcu_read_unlock(); +drop: kfree_skb(skb); - return ERR_PTR(err); + return NET_RX_DROP; } -static bool is_gre_gso(struct sk_buff *skb) +static const struct net_protocol net_gre_protocol = { + .handler = gre_rcv, + .netns_ok = 1, +}; + +static int gre_add_protocol(const struct gre_protocol *proto, u8 version) { - return skb_is_gso(skb); + if (version >= GREPROTO_MAX) + return -EINVAL; + + if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) { + pr_err("%s: cannot register gre protocol handler\n", __func__); + return -EAGAIN; + } + + return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ? + 0 : -EBUSY; } -void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi, - int hdr_len) +static int gre_del_protocol(const struct gre_protocol *proto, u8 version) { - struct gre_base_hdr *greh; + int ret; - __skb_push(skb, hdr_len); + if (version >= GREPROTO_MAX) + return -EINVAL; - greh = (struct gre_base_hdr *)skb->data; - greh->flags = tnl_flags_to_gre_flags(tpi->flags); - greh->protocol = tpi->proto; + ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ? + 0 : -EBUSY; - if (tpi->flags & (TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_SEQ)) { - __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4); + if (ret) + return ret; - if (tpi->flags & TUNNEL_SEQ) { - *ptr = tpi->seq; - ptr--; - } - if (tpi->flags & TUNNEL_KEY) { - *ptr = tpi->key; - ptr--; - } - if (tpi->flags & TUNNEL_CSUM && !is_gre_gso(skb)) { - *ptr = 0; - *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0, - skb->len, 0)); - } - } + synchronize_net(); + + ret = inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); + if (ret) + return ret; + + return 0; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) +#endif static __sum16 check_checksum(struct sk_buff *skb) { @@ -198,7 +206,6 @@ static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi, } static struct gre_cisco_protocol __rcu *gre_cisco_proto; - static int gre_cisco_rcv(struct sk_buff *skb) { struct tnl_ptk_info tpi; @@ -226,136 +233,110 @@ static const struct gre_protocol ipgre_protocol = { .handler = gre_cisco_rcv, }; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) -static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly; - -int gre_add_protocol(const struct gre_protocol *proto, u8 version) +int gre_cisco_register(struct gre_cisco_protocol *newp) { - if (version >= GREPROTO_MAX) - return -EINVAL; + int err; - return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ? + err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO); + if (err) { + pr_warn("%s: cannot register gre_cisco protocol handler\n", __func__); + return err; + } + + + return (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, NULL, newp) == NULL) ? 0 : -EBUSY; } -int gre_del_protocol(const struct gre_protocol *proto, u8 version) +int gre_cisco_unregister(struct gre_cisco_protocol *proto) { int ret; - if (version >= GREPROTO_MAX) - return -EINVAL; - - ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ? - 0 : -EBUSY; + ret = (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, proto, NULL) == proto) ? + 0 : -EINVAL; if (ret) return ret; synchronize_net(); - return 0; + ret = gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); + return ret; } -static int gre_rcv(struct sk_buff *skb) -{ - const struct gre_protocol *proto; - u8 ver; - int ret; - - if (!pskb_may_pull(skb, 12)) - goto drop; +#endif /* !HAVE_GRE_CISCO_REGISTER */ - ver = skb->data[1] & 0x7f; - if (ver >= GREPROTO_MAX) - goto drop; +/* GRE TX side. */ +static void gre_csum_fix(struct sk_buff *skb) +{ + struct gre_base_hdr *greh; + __be32 *options; + int gre_offset = skb_transport_offset(skb); - rcu_read_lock(); - proto = rcu_dereference(gre_proto[ver]); - if (!proto || !proto->handler) - goto drop_unlock; - ret = proto->handler(skb); - rcu_read_unlock(); - return ret; + greh = (struct gre_base_hdr *)skb_transport_header(skb); + options = ((__be32 *)greh + 1); -drop_unlock: - rcu_read_unlock(); -drop: - kfree_skb(skb); - return NET_RX_DROP; + *options = 0; + *(__sum16 *)options = csum_fold(skb_checksum(skb, gre_offset, + skb->len - gre_offset, 0)); } -static const struct net_protocol net_gre_protocol = { - .handler = gre_rcv, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) - .netns_ok = 1, -#endif -}; -#endif - -static int gre_compat_init(void) +struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum) { int err; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) - if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) { - pr_err("%s: cannot register gre protocol handler\n", __func__); - return -EAGAIN; - } -#endif - err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO); - if (err) { - pr_warn("%s: cannot register gre_cisco protocol handler\n", __func__); + skb_reset_inner_headers(skb); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) - inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); -#endif - } + if (skb_is_gso(skb)) { + if (gre_csum) + OVS_GSO_CB(skb)->fix_segment = gre_csum_fix; + } else { + if (skb->ip_summed == CHECKSUM_PARTIAL && gre_csum) { + err = skb_checksum_help(skb); + if (err) + goto error; - return err; + } else if (skb->ip_summed != CHECKSUM_PARTIAL) + skb->ip_summed = CHECKSUM_NONE; + } + return skb; +error: + kfree_skb(skb); + return ERR_PTR(err); } -static int gre_compat_exit(void) +static bool is_gre_gso(struct sk_buff *skb) { - int ret; - - ret = gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); - if (ret) - return ret; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) - ret = inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); - if (ret) - return ret; -#endif - return 0; + return skb_is_gso(skb); } -int gre_cisco_register(struct gre_cisco_protocol *newp) +void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi, + int hdr_len) { - int err; - - err = gre_compat_init(); - if (err) - return err; - - return (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, NULL, newp) == NULL) ? - 0 : -EBUSY; -} + struct gre_base_hdr *greh; -int gre_cisco_unregister(struct gre_cisco_protocol *proto) -{ - int ret; + __skb_push(skb, hdr_len); - ret = (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, proto, NULL) == proto) ? - 0 : -EINVAL; + greh = (struct gre_base_hdr *)skb->data; + greh->flags = tnl_flags_to_gre_flags(tpi->flags); + greh->protocol = tpi->proto; - if (ret) - return ret; + if (tpi->flags & (TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_SEQ)) { + __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4); - synchronize_net(); - ret = gre_compat_exit(); - return ret; + if (tpi->flags & TUNNEL_SEQ) { + *ptr = tpi->seq; + ptr--; + } + if (tpi->flags & TUNNEL_KEY) { + *ptr = tpi->key; + ptr--; + } + if (tpi->flags & TUNNEL_CSUM && !is_gre_gso(skb)) { + *ptr = 0; + *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0, + skb->len, 0)); + } + } } -#endif /* 3.11 */ - #endif /* CONFIG_NET_IPGRE_DEMUX */ diff --git a/datapath/linux/compat/include/net/gre.h b/datapath/linux/compat/include/net/gre.h index 91fb7af4e..a6f29c45c 100644 --- a/datapath/linux/compat/include/net/gre.h +++ b/datapath/linux/compat/include/net/gre.h @@ -4,22 +4,26 @@ #include #include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) || \ + defined(HAVE_GRE_CISCO_REGISTER) #include_next +#endif -#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) */ +#ifndef HAVE_GRE_CISCO_REGISTER -#define GREPROTO_CISCO 0 -#define GREPROTO_MAX 2 +/* GRE demux not available, implement our own demux. */ +#define MAX_GRE_PROTO_PRIORITY 255 -struct gre_protocol { - int (*handler)(struct sk_buff *skb); +struct gre_cisco_protocol { + int (*handler)(struct sk_buff *skb, const struct tnl_ptk_info *tpi); + u8 priority; }; -int gre_add_protocol(const struct gre_protocol *proto, u8 version); -int gre_del_protocol(const struct gre_protocol *proto, u8 version); +#define gre_cisco_register rpl_gre_cisco_register +int gre_cisco_register(struct gre_cisco_protocol *proto); -#endif +#define gre_cisco_unregister rpl_gre_cisco_unregister +int gre_cisco_unregister(struct gre_cisco_protocol *proto); #if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) struct gre_base_hdr { @@ -72,24 +76,7 @@ static inline __be16 tnl_flags_to_gre_flags(__be16 tflags) return flags; } #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) */ - -#if LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0) -/* GRE demux not available, implement our own demux. */ -#define MAX_GRE_PROTO_PRIORITY 255 -#define gre_cisco_protocol rpl_gre_cisco_protocol - -struct gre_cisco_protocol { - int (*handler)(struct sk_buff *skb, const struct tnl_ptk_info *tpi); - u8 priority; -}; - -#define gre_cisco_register rpl_gre_cisco_register -int gre_cisco_register(struct gre_cisco_protocol *proto); - -#define gre_cisco_unregister rpl_gre_cisco_unregister -int gre_cisco_unregister(struct gre_cisco_protocol *proto); - -#endif +#endif /* HAVE_GRE_CISCO_REGISTER */ #define gre_build_header rpl_gre_build_header void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi, @@ -112,5 +99,4 @@ static inline int ip_gre_calc_hlen(__be16 o_flags) return addend; } - #endif -- 2.43.0