Creates GRE devices using rtnetlink and tunnel metadata. If the kernel does
not support tunnel metadata, it will return EEXIST because of the fallback
tunnel. However, on kernels between v3.10 and v3.12, it will not. So, we need to
verify the created tunnel has the tunnel metadata attribute.

This was tested on kernels 4.2.3, 4.3.6, 4.4.9 and RHEL 3.10 based. All of them
worked with the system traffic test "datapath - ping over gre tunnel". Also
tested on a 3.10 based kernel without the fix for the existing fallback tunnel.
That is, the kernel would not return EEXIST. Yet, the test works fine.

Signed-off-by: Thadeu Lima de Souza Cascardo <casca...@redhat.com>
---
 lib/dpif-netlink.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 134 insertions(+)

diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 89fd903..7e30341 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -25,6 +25,7 @@
 #include <net/if.h>
 #include <linux/types.h>
 #include <linux/ip.h>
+#include <linux/if_tunnel.h>
 #include <linux/pkt_sched.h>
 #include <linux/rtnetlink.h>
 #include <poll.h>
@@ -963,6 +964,12 @@ netdev_vxlan_destroy(const char *name)
     return netdev_linux_destroy(name);
 }
 
+static int
+netdev_gre_destroy(const char *name)
+{
+    return netdev_linux_destroy(name);
+}
+
 /*
  * On some older systems, these enums are not defined.
  */
@@ -977,7 +984,12 @@ netdev_vxlan_destroy(const char *name)
 #define IFLA_VXLAN_COLLECT_METADATA 25
 #endif
 
+#if IFLA_GRE_MAX < 18
+#define IFLA_GRE_COLLECT_METADATA 18
+#endif
+
 #define MTU_MAX_VXLAN 1500
+#define MTU_MAX_GRE (0xfff8 - ETH_HEADER_LEN - 4 - IP_HEADER_LEN)
 
 static int
 netdev_vxlan_create(struct netdev *netdev)
@@ -1032,6 +1044,114 @@ netdev_vxlan_create(struct netdev *netdev)
     return err;
 }
 
+/*
+ * On some Linux versions, creating the device with IFLA_GRE_COLLECT_METADATA
+ * will succeed, even though that attribute is not supported. We need to verify
+ * the device has been created with that attribute. In case it has not, we
+ * destroy it and use the compat code.
+ */
+static int
+netdev_gre_verify(const char *name)
+{
+    int err;
+    struct ofpbuf request, *reply;
+    struct ifinfomsg *ifmsg;
+
+    static const struct nl_policy rtlink_policy[] = {
+        [IFLA_LINKINFO] = { .type = NL_A_NESTED },
+    };
+    static const struct nl_policy linkinfo_policy[] = {
+        [IFLA_INFO_KIND] = { .type = NL_A_STRING },
+        [IFLA_INFO_DATA] = { .type = NL_A_NESTED },
+    };
+    static const struct nl_policy gre_policy[] = {
+        [IFLA_GRE_COLLECT_METADATA] = { .type = NL_A_FLAG },
+    };
+
+    ofpbuf_init(&request, 0);
+    nl_msg_put_nlmsghdr(&request, 0, RTM_GETLINK,
+                        NLM_F_REQUEST);
+    ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
+    nl_msg_put_string(&request, IFLA_IFNAME, name);
+
+    err = nl_transact(NETLINK_ROUTE, &request, &reply);
+    if (!err) {
+        struct nlattr *rtlink[ARRAY_SIZE(rtlink_policy)];
+        struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)];
+        struct nlattr *gre[ARRAY_SIZE(gre_policy)];
+
+        err = EINVAL;
+        ifmsg = ofpbuf_at(reply, NLMSG_HDRLEN, sizeof *ifmsg);
+        if (nl_policy_parse(reply, NLMSG_HDRLEN + sizeof *ifmsg,
+            rtlink_policy, rtlink, ARRAY_SIZE(rtlink_policy))) {
+            if (nl_parse_nested(rtlink[IFLA_LINKINFO], linkinfo_policy,
+                linkinfo, ARRAY_SIZE(linkinfo_policy)) &&
+                !strcmp(nl_attr_get_string(linkinfo[IFLA_INFO_KIND]), "gre")) {
+                if (nl_parse_nested(linkinfo[IFLA_INFO_DATA], gre_policy, gre,
+                    ARRAY_SIZE(gre_policy)) &&
+                    nl_attr_get_flag(gre[IFLA_GRE_COLLECT_METADATA])) {
+                        err = 0;
+                }
+            }
+        }
+        ofpbuf_uninit(reply);
+    }
+    ofpbuf_uninit(&request);
+    return err;
+}
+
+static int
+netdev_gre_create(struct netdev *netdev)
+{
+    int err;
+    struct ofpbuf request, *reply;
+    size_t linkinfo_off, infodata_off;
+    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
+    const char *name = netdev_vport_get_dpif_port(netdev,
+                                                  namebuf, sizeof namebuf);
+    struct ifinfomsg *ifinfo;
+    const struct netdev_tunnel_config *tnl_cfg;
+    tnl_cfg = netdev_get_tunnel_config(netdev);
+    if (!tnl_cfg) { /* or assert? */
+        return EINVAL;
+    }
+
+    ofpbuf_init(&request, 0);
+    nl_msg_put_nlmsghdr(&request, 0, RTM_NEWLINK,
+                        NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE);
+    ifinfo = ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
+    ifinfo->ifi_change = ifinfo->ifi_flags = IFF_UP;
+    nl_msg_put_string(&request, IFLA_IFNAME, name);
+    nl_msg_put_u32(&request, IFLA_MTU, MTU_MAX_GRE);
+    linkinfo_off = nl_msg_start_nested(&request, IFLA_LINKINFO);
+        nl_msg_put_string(&request, IFLA_INFO_KIND, "gretap");
+        infodata_off = nl_msg_start_nested(&request, IFLA_INFO_DATA);
+            nl_msg_put_flag(&request, IFLA_GRE_COLLECT_METADATA);
+        nl_msg_end_nested(&request, infodata_off);
+    nl_msg_end_nested(&request, linkinfo_off);
+
+    err = nl_transact(NETLINK_ROUTE, &request, &reply);
+
+    if (!err) {
+        ofpbuf_uninit(reply);
+    }
+
+    if (!err && (err = netdev_gre_verify(name))) {
+        netdev_gre_destroy(name);
+    }
+
+    /*
+     * If tunnel metadata is not supported, EEXIST will be returned for zero
+     * addresses tunnel. We still need to verify metadata has been set as 
above.
+     */
+    if (err == EINVAL || err == EEXIST) {
+        err = EOPNOTSUPP;
+    }
+
+    ofpbuf_uninit(&request);
+    return err;
+}
+
 #else
 
 static int
@@ -1041,11 +1161,23 @@ netdev_vxlan_create(struct netdev *netdev OVS_UNUSED)
 }
 
 static int
+netdev_gre_create(struct netdev *netdev OVS_UNUSED)
+{
+    return EOPNOTSUPP;
+}
+
+static int
 netdev_vxlan_destroy(const char *name OVS_UNUSED)
 {
     return EOPNOTSUPP;
 }
 
+static int
+netdev_gre_destroy(const char *name OVS_UNUSED)
+{
+    return EOPNOTSUPP;
+}
+
 #endif
 
 static int
@@ -1059,6 +1191,7 @@ dpif_netlink_port_create(struct netdev *netdev)
     case OVS_VPORT_TYPE_VXLAN:
         return netdev_vxlan_create(netdev);
     case OVS_VPORT_TYPE_GRE:
+        return netdev_gre_create(netdev);
     case OVS_VPORT_TYPE_GENEVE:
     case OVS_VPORT_TYPE_NETDEV:
     case OVS_VPORT_TYPE_INTERNAL:
@@ -1079,6 +1212,7 @@ dpif_netlink_port_destroy(const char *name, const char 
*type)
     case OVS_VPORT_TYPE_VXLAN:
         return netdev_vxlan_destroy(name);
     case OVS_VPORT_TYPE_GRE:
+        return netdev_gre_destroy(name);
     case OVS_VPORT_TYPE_GENEVE:
     case OVS_VPORT_TYPE_NETDEV:
     case OVS_VPORT_TYPE_INTERNAL:
-- 
2.5.5

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to