*/
#include <config.h>
-#include "dpif.h"
+
+#include "dpif-linux.h"
#include <assert.h>
#include <ctype.h>
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);
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
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) {
}
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)
+ struct dpif_port *dpif_port)
{
- memset(port, 0, sizeof *port);
- strncpy(port->devname, devname, sizeof port->devname);
- return dpif_linux_port_query__(dpif, port);
+ return dpif_linux_port_query__(dpif, 0, devname, dpif_port);
}
static int
}
static int
-dpif_linux_port_list(const struct dpif *dpif_, struct odp_port *ports, int n)
+dpif_linux_port_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep)
+{
+ *statep = xzalloc(sizeof(struct odp_port));
+ return 0;
+}
+
+static int
+dpif_linux_port_dump_next(const struct dpif *dpif, void *state,
+ struct dpif_port *dpif_port)
{
- struct odp_portvec pv;
- unsigned int i;
+ struct odp_port *odp_port = state;
+ struct odp_vport_dump dump;
int error;
- pv.ports = ports;
- pv.n_ports = n;
- error = do_ioctl(dpif_, ODP_VPORT_LIST, &pv);
+ dump.port = odp_port;
+ dump.port_no = odp_port->port;
+ error = do_ioctl(dpif, ODP_VPORT_DUMP, &dump);
if (error) {
- return -error;
+ return error;
+ } else if (odp_port->devname[0] == '\0') {
+ return EOF;
+ } else {
+ 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;
}
+}
- for (i = 0; i < pv.n_ports; i++) {
- struct odp_port *port = &pv.ports[i];
-
- translate_vport_type_to_netdev_type(port);
- }
- return pv.n_ports;
+static int
+dpif_linux_port_dump_done(const struct dpif *dpif OVS_UNUSED, void *state)
+{
+ free(state);
+ return 0;
}
static int
dpif_linux_port_del,
dpif_linux_port_query_by_number,
dpif_linux_port_query_by_name,
- dpif_linux_port_list,
+ dpif_linux_port_dump_start,
+ dpif_linux_port_dump_next,
+ dpif_linux_port_dump_done,
dpif_linux_port_poll,
dpif_linux_port_poll_wait,
dpif_linux_flow_get,
}
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;
}
+}
+
+static int
+lookup_minor(const char *name, int *minorp)
+{
+ int minor, port_no;
+ int error;
- 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;
+ 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