datapath: Change vport type from string to integer enumeration.
[sliver-openvswitch.git] / lib / dpif-linux.c
index 530784c..cf3c9b3 100644 (file)
@@ -15,7 +15,8 @@
  */
 
 #include <config.h>
-#include "dpif.h"
+
+#include "dpif-linux.h"
 
 #include <assert.h>
 #include <ctype.h>
@@ -69,6 +70,7 @@ struct dpif_linux {
 static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
 
 static int do_ioctl(const struct dpif *, int cmd, const void *arg);
+static int lookup_internal_device(const char *name, int *dp_idx, int *port_no);
 static int lookup_minor(const char *name, int *minor);
 static int finish_open(struct dpif *, const char *local_ifname);
 static int get_openvswitch_major(void);
@@ -230,33 +232,50 @@ dpif_linux_set_drop_frags(struct dpif *dpif_, bool drop_frags)
     return do_ioctl(dpif_, ODP_SET_DROP_FRAGS, &drop_frags_int);
 }
 
-static void
-translate_vport_type_to_netdev_type(struct odp_port *port)
+static const char *
+vport_type_to_netdev_type(const struct odp_port *odp_port)
 {
-    char *type = port->type;
+    struct tnl_port_config tnl_config;
 
-    if (!strcmp(type, "netdev")) {
-        ovs_strlcpy(type, "system", sizeof port->type);
-    } else if (!strcmp(type, "gre")) {
-        const struct tnl_port_config *config;
+    switch (odp_port->type) {
+    case ODP_VPORT_TYPE_UNSPEC:
+        break;
 
-        config = (struct tnl_port_config *)port->config;
-        if (config->flags & TNL_F_IPSEC) {
-            ovs_strlcpy(type, "ipsec_gre", sizeof port->type);
-        }
+    case ODP_VPORT_TYPE_NETDEV:
+        return "system";
+
+    case ODP_VPORT_TYPE_INTERNAL:
+        return "internal";
+
+    case ODP_VPORT_TYPE_PATCH:
+        return "patch";
+
+    case ODP_VPORT_TYPE_GRE:
+        memcpy(&tnl_config, odp_port->config, sizeof tnl_config);
+        return tnl_config.flags & TNL_F_IPSEC ? "ipsec_gre" : "gre";
+
+    case ODP_VPORT_TYPE_CAPWAP:
+        return "capwap";
+
+    case __ODP_VPORT_TYPE_MAX:
+        break;
     }
+
+    VLOG_WARN_RL(&error_rl, "dp%d: port `%s' has unsupported type %"PRIu32,
+                 odp_port->dp_idx, odp_port->devname, odp_port->type);
+    return "unknown";
 }
 
-static void
-translate_netdev_type_to_vport_type(struct odp_port *port)
+static enum odp_vport_type
+netdev_type_to_vport_type(const char *type)
 {
-    char *type = port->type;
-
-    if (!strcmp(type, "system")) {
-        ovs_strlcpy(type, "netdev", sizeof port->type);
-    } else if (!strcmp(type, "ipsec_gre")) {
-        ovs_strlcpy(type, "gre", sizeof port->type);
-    }
+    return (!strcmp(type, "system") ? ODP_VPORT_TYPE_NETDEV
+            : !strcmp(type, "internal") ? ODP_VPORT_TYPE_INTERNAL
+            : !strcmp(type, "patch") ? ODP_VPORT_TYPE_PATCH
+            : (!strcmp(type, "gre")
+               || !strcmp(type, "ipsec_gre")) ? ODP_VPORT_TYPE_GRE
+            : !strcmp(type, "capwap") ? ODP_VPORT_TYPE_CAPWAP
+            : ODP_VPORT_TYPE_UNSPEC);
 }
 
 static int
@@ -270,9 +289,15 @@ dpif_linux_port_add(struct dpif *dpif, struct netdev *netdev,
 
     memset(&port, 0, sizeof port);
     strncpy(port.devname, name, sizeof port.devname);
-    strncpy(port.type, type, sizeof port.type);
     netdev_vport_get_config(netdev, port.config);
-    translate_netdev_type_to_vport_type(&port);
+
+    port.type = netdev_type_to_vport_type(type);
+    if (port.type == ODP_VPORT_TYPE_UNSPEC) {
+        VLOG_WARN_RL(&error_rl, "%s: cannot create port `%s' because it has "
+                     "unsupported type `%s'",
+                     dpif_name(dpif), name, type);
+        return EINVAL;
+    }
 
     error = do_ioctl(dpif, ODP_VPORT_ATTACH, &port);
     if (!error) {
@@ -290,39 +315,42 @@ dpif_linux_port_del(struct dpif *dpif_, uint16_t port_no_)
 }
 
 static int
-dpif_linux_port_query__(const struct dpif *dpif, struct odp_port *port)
+dpif_linux_port_query__(const struct dpif *dpif, uint32_t port_no,
+                        const char *port_name, struct dpif_port *dpif_port)
 {
-    int error = do_ioctl(dpif, ODP_VPORT_QUERY, port);
-    if (!error) {
-        translate_vport_type_to_netdev_type(port);
+    struct odp_port odp_port;
+    int error;
+
+    memset(&odp_port, 0, sizeof odp_port);
+    odp_port.port = port_no;
+    strncpy(odp_port.devname, port_name, sizeof odp_port.devname);
+
+    error = do_ioctl(dpif, ODP_VPORT_QUERY, &odp_port);
+    if (error) {
+        return error;
+    } else if (odp_port.dp_idx != dpif_linux_cast(dpif)->minor) {
+        /* A vport named 'port_name' exists but in some other datapath.  */
+        return ENOENT;
+    } else {
+        dpif_port->name = xstrdup(odp_port.devname);
+        dpif_port->type = xstrdup(vport_type_to_netdev_type(&odp_port));
+        dpif_port->port_no = odp_port.port;
+        return 0;
     }
-    return error;
 }
 
 static int
 dpif_linux_port_query_by_number(const struct dpif *dpif, uint16_t port_no,
-                                struct odp_port *port)
+                                struct dpif_port *dpif_port)
 {
-    memset(port, 0, sizeof *port);
-    port->port = port_no;
-    return dpif_linux_port_query__(dpif, port);
+    return dpif_linux_port_query__(dpif, port_no, "", dpif_port);
 }
 
 static int
-dpif_linux_port_query_by_name(const struct dpif *dpif_, const char *devname,
-                              struct odp_port *port)
+dpif_linux_port_query_by_name(const struct dpif *dpif, const char *devname,
+                              struct dpif_port *dpif_port)
 {
-    struct dpif_linux *dpif = dpif_linux_cast(dpif_);
-    int error;
-
-    memset(port, 0, sizeof *port);
-    strncpy(port->devname, devname, sizeof port->devname);
-    error = dpif_linux_port_query__(dpif_, port);
-    if (!error && port->dp_idx != dpif->minor) {
-        /* A vport named 'devname' exists but in some other datapath.  */
-        error = ENOENT;
-    }
-    return error;
+    return dpif_linux_port_query__(dpif, 0, devname, dpif_port);
 }
 
 static int
@@ -334,26 +362,30 @@ dpif_linux_flow_flush(struct dpif *dpif_)
 static int
 dpif_linux_port_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep)
 {
-    *statep = xzalloc(sizeof(struct odp_vport_dump));
+    *statep = xzalloc(sizeof(struct odp_port));
     return 0;
 }
 
 static int
 dpif_linux_port_dump_next(const struct dpif *dpif, void *state,
-                          struct odp_port *port)
+                          struct dpif_port *dpif_port)
 {
-    struct odp_vport_dump *dump = state;
+    struct odp_port *odp_port = state;
+    struct odp_vport_dump dump;
     int error;
 
-    dump->port = port;
-    error = do_ioctl(dpif, ODP_VPORT_DUMP, dump);
+    dump.port = odp_port;
+    dump.port_no = odp_port->port;
+    error = do_ioctl(dpif, ODP_VPORT_DUMP, &dump);
     if (error) {
         return error;
-    } else if (port->devname[0] == '\0') {
+    } else if (odp_port->devname[0] == '\0') {
         return EOF;
     } else {
-        dump->port_no = port->port + 1;
-        translate_vport_type_to_netdev_type(port);
+        dpif_port->name = odp_port->devname;
+        dpif_port->type = (char *) vport_type_to_netdev_type(odp_port);
+        dpif_port->port_no = odp_port->port;
+        odp_port->port++;
         return 0;
     }
 }
@@ -638,60 +670,74 @@ do_ioctl(const struct dpif *dpif_, int cmd, const void *arg)
 }
 
 static int
-lookup_minor(const char *name, int *minorp)
+lookup_internal_device(const char *name, int *dp_idx, int *port_no)
 {
-    struct ethtool_drvinfo drvinfo;
-    int minor, port_no;
-    struct ifreq ifr;
-    int error;
-    int sock;
+    struct odp_port odp_port;
+    static int dp0_fd = -1;
 
-    sock = socket(AF_INET, SOCK_DGRAM, 0);
-    if (sock < 0) {
-        VLOG_WARN("socket(AF_INET) failed: %s", strerror(errno));
-        error = errno;
-        goto error;
-    }
+    if (dp0_fd < 0) {
+        int error;
+        char *fn;
 
-    memset(&ifr, 0, sizeof ifr);
-    strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
-    ifr.ifr_data = (caddr_t) &drvinfo;
+        error = make_openvswitch_device(0, &fn);
+        if (error) {
+            return error;
+        }
 
-    memset(&drvinfo, 0, sizeof drvinfo);
-    drvinfo.cmd = ETHTOOL_GDRVINFO;
-    if (ioctl(sock, SIOCETHTOOL, &ifr)) {
-        VLOG_WARN("ioctl(SIOCETHTOOL) failed: %s", strerror(errno));
-        error = errno;
-        goto error_close_sock;
+        dp0_fd = open(fn, O_RDONLY | O_NONBLOCK);
+        if (dp0_fd < 0) {
+            VLOG_WARN_RL(&error_rl, "%s: open failed (%s)",
+                         fn, strerror(errno));
+            free(fn);
+            return errno;
+        }
+        free(fn);
     }
 
-    if (strcmp(drvinfo.driver, "openvswitch")) {
-        VLOG_WARN("%s is not an openvswitch device", name);
-        error = EOPNOTSUPP;
-        goto error_close_sock;
+    memset(&odp_port, 0, sizeof odp_port);
+    strncpy(odp_port.devname, name, sizeof odp_port.devname);
+    if (ioctl(dp0_fd, ODP_VPORT_QUERY, &odp_port)) {
+        if (errno != ENODEV) {
+            VLOG_WARN_RL(&error_rl, "%s: vport query failed (%s)",
+                         name, strerror(errno));
+        }
+        return errno;
+    } else if (odp_port.type == ODP_VPORT_TYPE_INTERNAL) {
+        *dp_idx = odp_port.dp_idx;
+        *port_no = odp_port.port;
+        return 0;
+    } else {
+        return EINVAL;
     }
+}
 
-    if (sscanf(drvinfo.bus_info, "%d.%d", &minor, &port_no) != 2) {
-        VLOG_WARN("%s ethtool bus_info has unexpected format", name);
-        error = EPROTOTYPE;
-        goto error_close_sock;
+static int
+lookup_minor(const char *name, int *minorp)
+{
+    int minor, port_no;
+    int error;
+
+    error = lookup_internal_device(name, &minor, &port_no);
+    if (error) {
+        return error;
     } else if (port_no != ODPP_LOCAL) {
         /* This is an Open vSwitch device but not the local port.  We
          * intentionally support only using the name of the local port as the
          * name of a datapath; otherwise, it would be too difficult to
          * enumerate all the names of a datapath. */
-        error = EOPNOTSUPP;
-        goto error_close_sock;
+        return EOPNOTSUPP;
+    } else {
+        *minorp = minor;
+        return 0;
     }
+}
 
-    *minorp = minor;
-    close(sock);
-    return 0;
+bool
+dpif_linux_is_internal_device(const char *name)
+{
+    int minor, port_no;
 
-error_close_sock:
-    close(sock);
-error:
-    return error;
+    return !lookup_internal_device(name, &minor, &port_no);
 }
 
 static int