diff mbox

[ovs-dev,v5,3/7] dpif-netlink: Support rtnetlink port creation.

Message ID 20170518201033.13092-4-e@erig.me
State Accepted
Headers show

Commit Message

Eric Garver May 18, 2017, 8:10 p.m. UTC
In order to be able to add those tunnels, we need to add code to create
the tunnels and add them as NETDEV vports. And when there is no support
to create them, we need to fallback to compatibility code and add them
as tunnel vports.

When removing those tunnels, we need to remove the interfaces as well,
and detecting the right type might be important, at least to distinguish
the tunnel vports that we should remove and the interfaces that we
shouldn't.

Co-authored-by: Thadeu Lima de Souza Cascardo <cascardo@redhat.com>
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@redhat.com>
Signed-off-by: Eric Garver <e@erig.me>
---
 lib/automake.mk         |   3 +
 lib/dpif-netlink-rtnl.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/dpif-netlink-rtnl.h |  47 +++++++++
 lib/dpif-netlink.c      |  29 ++++-
 lib/dpif-netlink.h      |   2 +
 5 files changed, 353 insertions(+), 1 deletion(-)
 create mode 100644 lib/dpif-netlink-rtnl.c
 create mode 100644 lib/dpif-netlink-rtnl.h
diff mbox

Patch

diff --git a/lib/automake.mk b/lib/automake.mk
index faace7993e79..f5baba27179f 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -352,6 +352,8 @@  if LINUX
 lib_libopenvswitch_la_SOURCES += \
 	lib/dpif-netlink.c \
 	lib/dpif-netlink.h \
+	lib/dpif-netlink-rtnl.c \
+	lib/dpif-netlink-rtnl.h \
 	lib/if-notifier.c \
 	lib/if-notifier.h \
 	lib/netdev-linux.c \
@@ -382,6 +384,7 @@  if WIN32
 lib_libopenvswitch_la_SOURCES += \
 	lib/dpif-netlink.c \
 	lib/dpif-netlink.h \
+	lib/dpif-netlink-rtnl.h \
 	lib/netdev-windows.c \
 	lib/netlink-conntrack.c \
 	lib/netlink-conntrack.h \
diff --git a/lib/dpif-netlink-rtnl.c b/lib/dpif-netlink-rtnl.c
new file mode 100644
index 000000000000..7dfc945e7444
--- /dev/null
+++ b/lib/dpif-netlink-rtnl.c
@@ -0,0 +1,273 @@ 
+/*
+ * Copyright (c) 2017 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "dpif-netlink-rtnl.h"
+
+#include <net/if.h>
+#include <linux/ip.h>
+#include <linux/rtnetlink.h>
+
+#include "dpif-netlink.h"
+#include "netdev-vport.h"
+#include "netlink-socket.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(dpif_netlink_rtnl);
+
+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 char *
+vport_type_to_kind(enum ovs_vport_type type)
+{
+    switch (type) {
+    case OVS_VPORT_TYPE_VXLAN:
+        return "vxlan";
+    case OVS_VPORT_TYPE_GRE:
+        return "gretap";
+    case OVS_VPORT_TYPE_GENEVE:
+        return "geneve";
+    case OVS_VPORT_TYPE_NETDEV:
+    case OVS_VPORT_TYPE_INTERNAL:
+    case OVS_VPORT_TYPE_LISP:
+    case OVS_VPORT_TYPE_STT:
+    case OVS_VPORT_TYPE_UNSPEC:
+    case __OVS_VPORT_TYPE_MAX:
+    default:
+        break;
+    }
+
+    return NULL;
+}
+
+static int
+rtnl_transact(uint32_t type, uint32_t flags, const char *name,
+              struct ofpbuf **reply)
+{
+    struct ofpbuf request;
+    int err;
+
+    ofpbuf_init(&request, 0);
+    nl_msg_put_nlmsghdr(&request, 0, type, flags);
+    ofpbuf_put_zeros(&request, sizeof(struct ifinfomsg));
+    nl_msg_put_string(&request, IFLA_IFNAME, name);
+
+    err = nl_transact(NETLINK_ROUTE, &request, reply);
+    ofpbuf_uninit(&request);
+
+    return err;
+}
+
+static int
+dpif_netlink_rtnl_destroy(const char *name)
+{
+    return rtnl_transact(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK, name, NULL);
+}
+
+static int OVS_UNUSED
+dpif_netlink_rtnl_getlink(const char *name, struct ofpbuf **reply)
+{
+    return rtnl_transact(RTM_GETLINK, NLM_F_REQUEST, name, reply);
+}
+
+static int OVS_UNUSED
+rtnl_policy_parse(const char *kind, struct ofpbuf *reply,
+                  const struct nl_policy *policy,
+                  struct nlattr *tnl_info[],
+                  size_t policy_size)
+{
+    struct nlattr *linkinfo[ARRAY_SIZE(linkinfo_policy)];
+    struct nlattr *rtlink[ARRAY_SIZE(rtlink_policy)];
+    struct ifinfomsg *ifmsg;
+    int error = 0;
+
+    ifmsg = ofpbuf_at(reply, NLMSG_HDRLEN, sizeof *ifmsg);
+    if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof *ifmsg,
+                         rtlink_policy, rtlink, ARRAY_SIZE(rtlink_policy))
+        || !nl_parse_nested(rtlink[IFLA_LINKINFO], linkinfo_policy,
+                            linkinfo, ARRAY_SIZE(linkinfo_policy))
+        || strcmp(nl_attr_get_string(linkinfo[IFLA_INFO_KIND]), kind)
+        || !nl_parse_nested(linkinfo[IFLA_INFO_DATA], policy,
+                            tnl_info, policy_size)) {
+        error = EINVAL;
+    }
+
+    return error;
+}
+
+static int
+dpif_netlink_rtnl_verify(const struct netdev_tunnel_config OVS_UNUSED *tnl_cfg,
+                         enum ovs_vport_type type, const char OVS_UNUSED *name)
+{
+    const char *kind;
+
+    kind = vport_type_to_kind(type);
+    if (!kind) {
+        return EOPNOTSUPP;
+    }
+
+    switch (type) {
+    case OVS_VPORT_TYPE_VXLAN:
+    case OVS_VPORT_TYPE_GRE:
+    case OVS_VPORT_TYPE_GENEVE:
+    case OVS_VPORT_TYPE_NETDEV:
+    case OVS_VPORT_TYPE_INTERNAL:
+    case OVS_VPORT_TYPE_LISP:
+    case OVS_VPORT_TYPE_STT:
+    case OVS_VPORT_TYPE_UNSPEC:
+    case __OVS_VPORT_TYPE_MAX:
+    default:
+        return EOPNOTSUPP;
+    }
+
+    return 0;
+}
+
+static int OVS_UNUSED
+dpif_netlink_rtnl_create(const struct netdev_tunnel_config OVS_UNUSED *tnl_cfg,
+                         const char *name, enum ovs_vport_type type,
+                         const char *kind, uint32_t flags)
+{
+    size_t linkinfo_off, infodata_off;
+    struct ifinfomsg *ifinfo;
+    struct ofpbuf request;
+    int err;
+
+    ofpbuf_init(&request, 0);
+    nl_msg_put_nlmsghdr(&request, 0, RTM_NEWLINK, flags);
+    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, UINT16_MAX);
+    linkinfo_off = nl_msg_start_nested(&request, IFLA_LINKINFO);
+    nl_msg_put_string(&request, IFLA_INFO_KIND, kind);
+    infodata_off = nl_msg_start_nested(&request, IFLA_INFO_DATA);
+
+    /* tunnel unique info */
+    switch (type) {
+    case OVS_VPORT_TYPE_VXLAN:
+    case OVS_VPORT_TYPE_GRE:
+    case OVS_VPORT_TYPE_GENEVE:
+    case OVS_VPORT_TYPE_NETDEV:
+    case OVS_VPORT_TYPE_INTERNAL:
+    case OVS_VPORT_TYPE_LISP:
+    case OVS_VPORT_TYPE_STT:
+    case OVS_VPORT_TYPE_UNSPEC:
+    case __OVS_VPORT_TYPE_MAX:
+    default:
+        err = EOPNOTSUPP;
+        goto exit;
+    }
+
+    nl_msg_end_nested(&request, infodata_off);
+    nl_msg_end_nested(&request, linkinfo_off);
+
+    err = nl_transact(NETLINK_ROUTE, &request, NULL);
+
+exit:
+    ofpbuf_uninit(&request);
+
+    return err;
+}
+
+int
+dpif_netlink_rtnl_port_create(struct netdev *netdev)
+{
+    const struct netdev_tunnel_config *tnl_cfg;
+    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
+    enum ovs_vport_type type;
+    const char *name;
+    const char *kind;
+    uint32_t flags;
+    int err;
+
+    type = netdev_to_ovs_vport_type(netdev_get_type(netdev));
+    kind = vport_type_to_kind(type);
+    if (!kind) {
+        return EOPNOTSUPP;
+    }
+
+    tnl_cfg = netdev_get_tunnel_config(netdev);
+    if (!tnl_cfg) {
+        return EINVAL;
+    }
+
+    name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
+    flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL;
+
+    err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags);
+
+    /* If the device exists, validate and/or attempt to recreate it. */
+    if (err == EEXIST) {
+        err = dpif_netlink_rtnl_verify(tnl_cfg, type, name);
+        if (!err) {
+            return 0;
+        } else {
+            err = dpif_netlink_rtnl_destroy(name);
+            if (err) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+                VLOG_WARN_RL(&rl, "RTNL device %s exists and cannot be "
+                             "deleted: %s", name, ovs_strerror(err));
+                return err;
+            }
+            err = dpif_netlink_rtnl_create(tnl_cfg, name, type, kind, flags);
+        }
+    }
+    if (err) {
+        return err;
+    }
+
+    err = dpif_netlink_rtnl_verify(tnl_cfg, type, name);
+    if (err) {
+        int err2 = dpif_netlink_rtnl_destroy(name);
+
+        if (err2) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+
+            VLOG_WARN_RL(&rl, "Failed to delete device %s during rtnl port "
+                         "creation: %s", name, ovs_strerror(err2));
+        }
+    }
+
+    return err;
+}
+
+int
+dpif_netlink_rtnl_port_destroy(const char *name OVS_UNUSED, const char *type)
+{
+    switch (netdev_to_ovs_vport_type(type)) {
+    case OVS_VPORT_TYPE_VXLAN:
+    case OVS_VPORT_TYPE_GRE:
+    case OVS_VPORT_TYPE_GENEVE:
+    case OVS_VPORT_TYPE_NETDEV:
+    case OVS_VPORT_TYPE_INTERNAL:
+    case OVS_VPORT_TYPE_LISP:
+    case OVS_VPORT_TYPE_STT:
+    case OVS_VPORT_TYPE_UNSPEC:
+    case __OVS_VPORT_TYPE_MAX:
+    default:
+        return EOPNOTSUPP;
+    }
+    return 0;
+}
diff --git a/lib/dpif-netlink-rtnl.h b/lib/dpif-netlink-rtnl.h
new file mode 100644
index 000000000000..5fef314a20f6
--- /dev/null
+++ b/lib/dpif-netlink-rtnl.h
@@ -0,0 +1,47 @@ 
+/*
+ * Copyright (c) 2017 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DPIF_NETLINK_RTNL_H
+#define DPIF_NETLINK_RTNL_H 1
+
+#include <errno.h>
+
+#include "netdev.h"
+
+/* Declare these to keep sparse happy. */
+int dpif_netlink_rtnl_port_create(struct netdev *netdev);
+int dpif_netlink_rtnl_port_destroy(const char *name OVS_UNUSED,
+                                   const char *type);
+
+#ifndef __linux__
+/* Dummy implementations for non Linux builds. */
+
+static inline int
+dpif_netlink_rtnl_port_create(struct netdev *netdev OVS_UNUSED)
+{
+    return EOPNOTSUPP;
+}
+
+static inline int
+dpif_netlink_rtnl_port_destroy(const char *name OVS_UNUSED,
+                               const char *type OVS_UNUSED)
+{
+    return EOPNOTSUPP;
+}
+
+#endif
+
+#endif /* DPIF_NETLINK_RTNL_H */
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index 4c4106c9b33f..52d28fb39e7b 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -34,6 +34,7 @@ 
 
 #include "bitmap.h"
 #include "dpif-provider.h"
+#include "dpif-netlink-rtnl.h"
 #include "openvswitch/dynamic-string.h"
 #include "flow.h"
 #include "fat-rwlock.h"
@@ -784,7 +785,7 @@  get_vport_type(const struct dpif_netlink_vport *vport)
     return "unknown";
 }
 
-static enum ovs_vport_type
+enum ovs_vport_type
 netdev_to_ovs_vport_type(const char *type)
 {
     if (!strcmp(type, "tap") || !strcmp(type, "system")) {
@@ -947,8 +948,34 @@  dpif_netlink_port_add_compat(struct dpif_netlink *dpif, struct netdev *netdev,
 
 }
 
+static int OVS_UNUSED
+dpif_netlink_rtnl_port_create_and_add(struct dpif_netlink *dpif,
+                                      struct netdev *netdev,
+                                      odp_port_t *port_nop)
+    OVS_REQ_WRLOCK(dpif->upcall_lock)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
+    const char *name;
+    int error;
 
+    error = dpif_netlink_rtnl_port_create(netdev);
+    if (error) {
+        if (error != EOPNOTSUPP) {
+            VLOG_INFO_RL(&rl, "Failed to create %s with rtnetlink: %s",
+                         netdev_get_name(netdev), ovs_strerror(error));
+        }
+        return error;
+    }
 
+    name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
+    error = dpif_netlink_port_add__(dpif, name, OVS_VPORT_TYPE_NETDEV, NULL,
+                                    port_nop);
+    if (error) {
+        dpif_netlink_rtnl_port_destroy(name, netdev_get_type(netdev));
+    }
+    return error;
+}
 
 static int
 dpif_netlink_port_add(struct dpif *dpif_, struct netdev *netdev,
diff --git a/lib/dpif-netlink.h b/lib/dpif-netlink.h
index 6d1505713a72..568b81441ddc 100644
--- a/lib/dpif-netlink.h
+++ b/lib/dpif-netlink.h
@@ -58,4 +58,6 @@  int dpif_netlink_vport_get(const char *name, struct dpif_netlink_vport *reply,
 
 bool dpif_netlink_is_internal_device(const char *name);
 
+enum ovs_vport_type netdev_to_ovs_vport_type(const char *type);
+
 #endif /* dpif-netlink.h */