]> git.proxmox.com Git - ovs.git/blobdiff - datapath/linux/compat/gre.c
gre: Resolve gre receive issues
[ovs.git] / datapath / linux / compat / gre.c
index 7d16aeed54fb4323c2b900bd04df294397332791..b45f8b4597e6b56aa862bad88931fbe03539f31c 100644 (file)
 #ifndef USE_UPSTREAM_TUNNEL
 #if IS_ENABLED(CONFIG_NET_IPGRE_DEMUX)
 
+static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
+
+int rpl_gre_add_protocol(const struct gre_protocol *proto, u8 version)
+{
+       if (version >= GREPROTO_MAX)
+               return -EINVAL;
+
+       return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ?
+               0 : -EBUSY;
+}
+EXPORT_SYMBOL_GPL(rpl_gre_add_protocol);
+
+int rpl_gre_del_protocol(const struct gre_protocol *proto, u8 version)
+{
+       int ret;
+
+       if (version >= GREPROTO_MAX)
+               return -EINVAL;
+
+       ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ?
+               0 : -EBUSY;
+
+       if (ret)
+               return ret;
+
+       synchronize_rcu();
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rpl_gre_del_protocol);
+
+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;
+
+       ver = skb->data[1]&0x7f;
+       if (ver >= GREPROTO_MAX)
+               goto drop;
+
+       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 NET_RX_DROP;
+}
+
+static void gre_err(struct sk_buff *skb, u32 info)
+{
+       const struct gre_protocol *proto;
+       const struct iphdr *iph = (const struct iphdr *)skb->data;
+       u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f;
+
+       if (ver >= GREPROTO_MAX)
+               return;
+
+       rcu_read_lock();
+       proto = rcu_dereference(gre_proto[ver]);
+       if (proto && proto->err_handler)
+               proto->err_handler(skb, info);
+       rcu_read_unlock();
+}
+
+static const struct net_protocol net_gre_protocol = {
+       .handler     = gre_rcv,
+       .err_handler = gre_err,
+       .netns_ok    = 1,
+};
+
+int rpl_gre_init(void)
+{
+       pr_info("GRE over IPv4 demultiplexor driver\n");
+
+       if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
+               pr_err("can't add protocol\n");
+               return -EAGAIN;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rpl_gre_init);
+
+void rpl_gre_exit(void)
+{
+       inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
+}
+EXPORT_SYMBOL_GPL(rpl_gre_exit);
+
 #define ip_gre_calc_hlen rpl_ip_gre_calc_hlen
 #define gre_calc_hlen rpl_ip_gre_calc_hlen
 static int rpl_ip_gre_calc_hlen(__be16 o_flags)
@@ -142,25 +240,20 @@ static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
        return iptunnel_pull_header(skb, hdr_len, tpi->proto, false);
 }
 
-#endif /* HAVE_DEMUX_PARSE_GRE_HEADER */
-
 static struct gre_cisco_protocol __rcu *gre_cisco_proto;
 static int gre_cisco_rcv(struct sk_buff *skb)
 {
-       struct tnl_ptk_info tpi;
        struct gre_cisco_protocol *proto;
+       struct tnl_ptk_info tpi;
+       bool csum_err = false;
 
        rcu_read_lock();
        proto = rcu_dereference(gre_cisco_proto);
        if (!proto)
                goto drop;
-#ifdef HAVE_DEMUX_PARSE_GRE_HEADER
-       {
-               bool csum_err = false;
-               if (parse_gre_header(skb, &tpi, &csum_err) < 0)
+
+       if (parse_gre_header(skb, &tpi, &csum_err) < 0)
                        goto drop;
-       }
-#endif
        proto->handler(skb, &tpi);
        rcu_read_unlock();
        return 0;
@@ -194,7 +287,6 @@ EXPORT_SYMBOL_GPL(rpl_gre_cisco_register);
 int rpl_gre_cisco_unregister(struct gre_cisco_protocol *proto)
 {
        int ret;
-
        ret = (cmpxchg((struct gre_cisco_protocol **)&gre_cisco_proto, proto, NULL) == proto) ?
                0 : -EINVAL;
 
@@ -207,6 +299,7 @@ int rpl_gre_cisco_unregister(struct gre_cisco_protocol *proto)
 }
 EXPORT_SYMBOL_GPL(rpl_gre_cisco_unregister);
 
+#endif /* HAVE_DEMUX_PARSE_GRE_HEADER */
 #endif /* !HAVE_GRE_CISCO_REGISTER */
 #endif