]> git.proxmox.com Git - ovs.git/commitdiff
dpif-netlink: Support rtnetlink port creation.
authorEric Garver <e@erig.me>
Thu, 18 May 2017 20:10:29 +0000 (16:10 -0400)
committerJoe Stringer <joe@ovn.org>
Fri, 19 May 2017 19:51:57 +0000 (12:51 -0700)
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>
Signed-off-by: Joe Stringer <joe@ovn.org>
lib/automake.mk
lib/dpif-netlink-rtnl.c [new file with mode: 0644]
lib/dpif-netlink-rtnl.h [new file with mode: 0644]
lib/dpif-netlink.c
lib/dpif-netlink.h

index faace7993e7905b4e3f08b93961e8b412818a81e..f5baba27179f0931780ae6d6ddc60c51826af94b 100644 (file)
@@ -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 (file)
index 0000000..7dfc945
--- /dev/null
@@ -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 (file)
index 0000000..5fef314
--- /dev/null
@@ -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 */
index 4c4106c9b33fead8228054b075f2782bea51eb4a..52d28fb39e7bd6f0edca71ed53eb1da1a4e6f489 100644 (file)
@@ -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,
index 6d1505713a72367bfa86e193ad3b910a0f8394b7..568b81441ddc02835b6b65d30cb505ff3b4bca56 100644 (file)
@@ -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 */