if (!internal) {
error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev);
} else {
- char *tapname = xasprintf("tap:%s", devname);
- error = netdev_open(tapname, NETDEV_ETH_TYPE_ANY, &netdev);
- free(tapname);
+ error = netdev_create(devname, "tap", NULL);
+ if (!error) {
+ error = netdev_open(devname, NETDEV_ETH_TYPE_ANY, &netdev);
+ if (error) {
+ netdev_destroy(devname);
+ }
+ }
}
if (error) {
return error;
do_del_port(struct dp_netdev *dp, uint16_t port_no)
{
struct dp_netdev_port *port;
+ char *name;
int error;
error = get_port_by_number(dp, port_no, &port);
dp->n_ports--;
dp->serial++;
+ name = xstrdup(netdev_get_name(port->netdev));
netdev_close(port->netdev);
+ if (port->internal) {
+ netdev_destroy(name);
+ }
+ free(name);
free(port);
return 0;
#define ADVERTISED_Asym_Pause (1 << 14)
#endif
+/* Provider-specific netdev object. Netdev objects are devices that are
+ * created by the netdev library through a netdev_create() call. */
+struct netdev_obj_linux {
+ struct netdev_obj netdev_obj;
+
+ int tap_fd; /* File descriptor for TAP device. */
+};
+
struct netdev_linux {
struct netdev netdev;
static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats);
static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats);
+static struct netdev_obj_linux *
+netdev_obj_linux_cast(const struct netdev_obj *netdev_obj)
+{
+ netdev_obj_assert_class(netdev_obj, &netdev_linux_class);
+ return CONTAINER_OF(netdev_obj, struct netdev_obj_linux, netdev_obj);
+}
+
static struct netdev_linux *
netdev_linux_cast(const struct netdev *netdev)
{
}
}
+/* Creates the netdev object of 'type' with 'name'. */
static int
-netdev_linux_open(const char *name, char *suffix, int ethertype,
- struct netdev **netdevp)
+netdev_linux_create(const char *name, const char *type,
+ const struct shash *args, bool created)
+{
+ struct netdev_obj_linux *netdev_obj;
+ static const char tap_dev[] = "/dev/net/tun";
+ struct ifreq ifr;
+ int error;
+
+ if (!shash_is_empty(args)) {
+ VLOG_WARN("arguments for %s devices should be empty", type);
+ }
+
+ /* Create the name binding in the netdev library for this object. */
+ netdev_obj = xcalloc(1, sizeof *netdev_obj);
+ netdev_obj_init(&netdev_obj->netdev_obj, name, &netdev_linux_class,
+ created);
+ netdev_obj->tap_fd = -1;
+
+ if (strcmp(type, "tap")) {
+ return 0;
+ }
+
+ /* Open tap device. */
+ netdev_obj->tap_fd = open(tap_dev, O_RDWR);
+ if (netdev_obj->tap_fd < 0) {
+ error = errno;
+ VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error));
+ goto error;
+ }
+
+ /* Create tap device. */
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+ if (ioctl(netdev_obj->tap_fd, TUNSETIFF, &ifr) == -1) {
+ VLOG_WARN("%s: creating tap device failed: %s", name,
+ strerror(errno));
+ error = errno;
+ goto error;
+ }
+
+ /* Make non-blocking. */
+ error = set_nonblocking(netdev_obj->tap_fd);
+ if (error) {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ netdev_destroy(name);
+ return error;
+}
+
+/* Destroys the netdev object 'netdev_obj_'. */
+static void
+netdev_linux_destroy(struct netdev_obj *netdev_obj_)
+{
+ struct netdev_obj_linux *netdev_obj = netdev_obj_linux_cast(netdev_obj_);
+
+ if (netdev_obj->tap_fd >= 0) {
+ close(netdev_obj->tap_fd);
+ }
+ free(netdev_obj);
+
+ return;
+}
+
+static int
+netdev_linux_open(const char *name, int ethertype, struct netdev **netdevp)
{
struct netdev_linux *netdev;
enum netdev_flags flags;
/* Allocate network device. */
netdev = xcalloc(1, sizeof *netdev);
- netdev_init(&netdev->netdev, suffix, &netdev_linux_class);
+ netdev_init(&netdev->netdev, name, &netdev_linux_class);
netdev->netdev_fd = -1;
netdev->tap_fd = -1;
- netdev->cache = shash_find_data(&cache_map, suffix);
+ netdev->cache = shash_find_data(&cache_map, name);
if (!netdev->cache) {
if (shash_is_empty(&cache_map)) {
int error = rtnetlink_notifier_register(
}
}
netdev->cache = xmalloc(sizeof *netdev->cache);
- netdev->cache->shash_node = shash_add(&cache_map, suffix,
+ netdev->cache->shash_node = shash_add(&cache_map, name,
netdev->cache);
netdev->cache->valid = 0;
netdev->cache->ref_cnt = 0;
}
netdev->cache->ref_cnt++;
- if (!strncmp(name, "tap:", 4)) {
+ if (!strcmp(netdev_get_type(&netdev->netdev), "tap")) {
static const char tap_dev[] = "/dev/net/tun";
struct ifreq ifr;
/* Create tap device. */
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
- strncpy(ifr.ifr_name, suffix, sizeof ifr.ifr_name);
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
if (ioctl(netdev->tap_fd, TUNSETIFF, &ifr) == -1) {
- VLOG_WARN("%s: creating tap device failed: %s", suffix,
+ VLOG_WARN("%s: creating tap device failed: %s", name,
strerror(errno));
error = errno;
goto error;
if (bind(netdev->netdev_fd,
(struct sockaddr *) &sll, sizeof sll) < 0) {
error = errno;
- VLOG_ERR("bind to %s failed: %s", suffix, strerror(error));
+ VLOG_ERR("bind to %s failed: %s", name, strerror(error));
goto error;
}
}
const struct netdev_class netdev_linux_class = {
- "", /* prefix */
- "linux", /* name */
+ "system", /* type */
netdev_linux_init,
netdev_linux_run,
netdev_linux_wait,
+ netdev_linux_create,
+ netdev_linux_destroy,
+ NULL, /* reconfigure */
+
netdev_linux_open,
netdev_linux_close,
};
const struct netdev_class netdev_tap_class = {
- "tap", /* prefix */
- "tap", /* name */
+ "tap", /* type */
netdev_linux_init,
NULL, /* run */
NULL, /* wait */
+ netdev_linux_create,
+ netdev_linux_destroy,
+ NULL, /* reconfigure */
+
netdev_linux_open,
netdev_linux_close,
#include <assert.h>
#include "netdev.h"
#include "list.h"
+#include "shash.h"
+
+/* A network device object that was created through the netdev_create()
+ * call.
+ *
+ * This structure should be treated as opaque by network device
+ * implementations. */
+struct netdev_obj {
+ const struct netdev_class *class;
+ int ref_cnt;
+ bool created; /* Was netdev_create() called? */
+};
+
+void netdev_obj_init(struct netdev_obj *, const char *name,
+ const struct netdev_class *, bool created);
+static inline void netdev_obj_assert_class(const struct netdev_obj *netdev_obj,
+ const struct netdev_class *class)
+{
+ assert(netdev_obj->class == class);
+}
/* A network device (e.g. an Ethernet device).
*
struct netdev {
const struct netdev_class *class;
char *name; /* e.g. "eth0" */
+
enum netdev_flags save_flags; /* Initial device flags. */
enum netdev_flags changed_flags; /* Flags that we changed. */
struct list node; /* Element in global list. */
{
assert(netdev->class == class);
}
+const char *netdev_get_type(const struct netdev *netdev);
/* A network device notifier.
*
* These functions return 0 if successful or a positive errno value on failure,
* except where otherwise noted. */
struct netdev_class {
- /* Prefix for names of netdevs in this class, e.g. "ndunix:".
+ /* Type of netdevs in this class, e.g. "system", "tap", "gre", etc.
*
- * One netdev class may have the empty string "" as its prefix, in which
- * case that netdev class is associated with netdev names that do not
- * contain a colon. */
- const char *prefix;
-
- /* Class name, for use in error messages. */
- const char *name;
+ * One of the providers should supply a "system" type, since this is
+ * the type assumed when a device name was not bound through the
+ * netdev_create() call. The "system" type corresponds to an
+ * existing network device on the system. */
+ const char *type;
/* Called only once, at program startup. Returning an error from this
* function will prevent any network device in this class from being
* to be called. May be null if nothing is needed here. */
void (*wait)(void);
+ /* Attempts to create a network device object of 'type' with 'name'.
+ * 'type' corresponds to the 'type' field used in the netdev_class
+ * structure.
+ *
+ * The 'created' flag indicates that the user called netdev_create()
+ * and thus will eventually call netdev_destroy(). If the flag is
+ * false, then the object was dynamically created based on a call to
+ * netdev_open() without first calling netdev_create() and will be
+ * automatically destroyed when no more netdevs have 'name' open. A
+ * provider implementation should pass this flag to netdev_obj_init(). */
+ int (*create)(const char *name, const char *type,
+ const struct shash *args, bool created);
+
+ /* Destroys 'netdev_obj'.
+ *
+ * Netdev objects maintain a reference count that is incremented on
+ * netdev_open() and decremented on netdev_close(). If 'netdev_obj'
+ * has a non-zero reference count, then this function will not be
+ * called. */
+ void (*destroy)(struct netdev_obj *netdev_obj);
+
+ /* Reconfigures the device object 'netdev_obj' with 'args'.
+ *
+ * If this netdev class does not support reconfiguring a netdev
+ * object, this may be a null pointer.
+ */
+ int (*reconfigure)(struct netdev_obj *netdev_obj,
+ const struct shash *args);
+
/* Attempts to open a network device. On success, sets '*netdevp' to the
- * new network device. 'name' is the full network device name provided by
+ * new network device. 'name' is the network device name provided by
* the user. This name is useful for error messages but must not be
* modified.
*
- * 'suffix' is a copy of 'name' following the netdev's 'prefix'.
- *
* 'ethertype' may be a 16-bit Ethernet protocol value in host byte order
* to capture frames of that type received on the device. It may also be
* one of the 'enum netdev_pseudo_ethertype' values to receive frames in
* one of those categories. */
- int (*open)(const char *name, char *suffix, int ethertype,
- struct netdev **netdevp);
+ int (*open)(const char *name, int ethertype, struct netdev **netdevp);
/* Closes 'netdev'. */
void (*close)(struct netdev *netdev);
};
static int n_netdev_classes = ARRAY_SIZE(netdev_classes);
+/* All created network devices. */
+static struct shash netdev_obj_shash = SHASH_INITIALIZER(&netdev_obj_shash);
+
/* All open network devices. */
static struct list netdev_list = LIST_INITIALIZER(&netdev_list);
* otherwise a positive errno value.
*
* Calling this function is optional. If not called explicitly, it will
- * automatically be called upon the first attempt to open a network device. */
+ * automatically be called upon the first attempt to open or create a
+ * network device. */
int
netdev_initialize(void)
{
netdev_classes[j++] = class;
} else {
VLOG_ERR("failed to initialize %s network device "
- "class: %s", class->name, strerror(retval));
+ "class: %s", class->type, strerror(retval));
if (!status) {
status = retval;
}
}
}
+/* Attempts to create a network device object of 'type' with 'name'. 'type'
+ * corresponds to the 'type' field used in the netdev_class * structure.
+ * Arguments for creation are provided in 'args', which may be empty or NULL
+ * if none are needed. */
+int
+netdev_create(const char *name, const char *type, const struct shash *args)
+{
+ struct shash empty_args = SHASH_INITIALIZER(&empty_args);
+ int i;
+
+ netdev_initialize();
+
+ if (!args) {
+ args = &empty_args;
+ }
+
+ if (shash_find(&netdev_obj_shash, name)) {
+ VLOG_WARN("attempted to create a netdev object with bound name: %s",
+ name);
+ return EEXIST;
+ }
+
+ for (i = 0; i < n_netdev_classes; i++) {
+ const struct netdev_class *class = netdev_classes[i];
+ if (!strcmp(type, class->type)) {
+ return class->create(name, type, args, true);
+ }
+ }
+
+ VLOG_WARN("could not create netdev object of unknown type: %s", type);
+
+ return EINVAL;
+}
+
+/* Destroys netdev object 'name'. Netdev objects maintain a reference count
+ * which is incremented on netdev_open() and decremented on netdev_close().
+ * If 'name' has a non-zero reference count, it will not destroy the object
+ * and return EBUSY. */
+int
+netdev_destroy(const char *name)
+{
+ struct shash_node *node;
+ struct netdev_obj *netdev_obj;
+
+ node = shash_find(&netdev_obj_shash, name);
+ if (!node) {
+ return ENODEV;
+ }
+
+ netdev_obj = node->data;
+ if (netdev_obj->ref_cnt != 0) {
+ VLOG_WARN("attempt to destroy open netdev object (%d): %s",
+ netdev_obj->ref_cnt, name);
+ return EBUSY;
+ }
+
+ shash_delete(&netdev_obj_shash, node);
+ netdev_obj->class->destroy(netdev_obj);
+
+ return 0;
+}
+
+/* Reconfigures the device object 'name' with 'args'. 'args' may be empty
+ * or NULL if none are needed. */
+int
+netdev_reconfigure(const char *name, const struct shash *args)
+{
+ struct shash empty_args = SHASH_INITIALIZER(&empty_args);
+ struct netdev_obj *netdev_obj;
+
+ if (!args) {
+ args = &empty_args;
+ }
+
+ netdev_obj = shash_find_data(&netdev_obj_shash, name);
+ if (!netdev_obj) {
+ return ENODEV;
+ }
+
+ if (netdev_obj->class->reconfigure) {
+ return netdev_obj->class->reconfigure(netdev_obj, args);
+ }
+
+ return 0;
+}
+
/* Opens the network device named 'name' (e.g. "eth0") and returns zero if
* successful, otherwise a positive errno value. On success, sets '*netdevp'
* to the new network device, otherwise to null.
* the 'enum netdev_pseudo_ethertype' values to receive frames in one of those
* categories. */
int
-netdev_open(const char *name_, int ethertype, struct netdev **netdevp)
+netdev_open(const char *name, int ethertype, struct netdev **netdevp)
{
- char *name = xstrdup(name_);
- char *prefix, *suffix, *colon;
+ struct netdev_obj *netdev_obj;
struct netdev *netdev = NULL;
int error;
int i;
netdev_initialize();
- colon = strchr(name, ':');
- if (colon) {
- *colon = '\0';
- prefix = name;
- suffix = colon + 1;
+
+ netdev_obj = shash_find_data(&netdev_obj_shash, name);
+ if (netdev_obj) {
+ error = netdev_obj->class->open(name, ethertype, &netdev);
} else {
- prefix = "";
- suffix = name;
- }
+ /* Default to "system". */
+ error = EAFNOSUPPORT;
+ for (i = 0; i < n_netdev_classes; i++) {
+ const struct netdev_class *class = netdev_classes[i];
+ if (!strcmp(class->type, "system")) {
+ struct shash empty_args = SHASH_INITIALIZER(&empty_args);
- for (i = 0; i < n_netdev_classes; i++) {
- const struct netdev_class *class = netdev_classes[i];
- if (!strcmp(prefix, class->prefix)) {
- error = class->open(name_, suffix, ethertype, &netdev);
- goto exit;
+ /* Dynamically create the netdev object, but indicate
+ * that it should be destroyed when the the last user
+ * closes its handle. */
+ error = class->create(name, "system", &empty_args, false);
+ if (!error) {
+ error = class->open(name, ethertype, &netdev);
+ netdev_obj = shash_find_data(&netdev_obj_shash, name);
+ }
+ break;
+ }
}
}
- error = EAFNOSUPPORT;
+ if (!error) {
+ netdev_obj->ref_cnt++;
+ }
-exit:
*netdevp = error ? NULL : netdev;
- free(name);
return error;
}
netdev_close(struct netdev *netdev)
{
if (netdev) {
- char *name;
+ struct netdev_obj *netdev_obj;
+ char *name = netdev->name;
int error;
+ netdev_obj = shash_find_data(&netdev_obj_shash, name);
+ assert(netdev_obj);
+ if (netdev_obj->ref_cnt > 0) {
+ netdev_obj->ref_cnt--;
+ } else {
+ VLOG_WARN("netdev %s closed too many times", name);
+ }
+
+ /* If the reference count for the netdev object is zero, and it
+ * was dynamically created by netdev_open(), destroy it. */
+ if (!netdev_obj->ref_cnt && !netdev_obj->created) {
+ netdev_destroy(name);
+ }
+
/* Restore flags that we changed, if any. */
fatal_signal_block();
error = restore_flags(netdev);
fatal_signal_unblock();
if (error) {
VLOG_WARN("failed to restore network device flags on %s: %s",
- netdev->name, strerror(error));
+ name, strerror(error));
}
/* Free. */
- name = netdev->name;
netdev->class->close(netdev);
free(name);
}
int retval = class->enumerate(svec);
if (retval) {
VLOG_WARN("failed to enumerate %s network devices: %s",
- class->name, strerror(retval));
+ class->type, strerror(retval));
if (!error) {
error = retval;
}
return netdev;
}
\f
+/* Initializes 'netdev_obj' as a netdev object named 'name' of the
+ * specified 'class'.
+ *
+ * This function adds 'netdev_obj' to a netdev-owned shash, so it is
+ * very important that 'netdev_obj' only be freed after calling
+ * netdev_destroy(). */
+void
+netdev_obj_init(struct netdev_obj *netdev_obj, const char *name,
+ const struct netdev_class *class, bool created)
+{
+ assert(!shash_find(&netdev_obj_shash, name));
+
+ netdev_obj->class = class;
+ netdev_obj->ref_cnt = 0;
+ netdev_obj->created = created;
+ shash_add(&netdev_obj_shash, name, netdev_obj);
+}
+
/* Initializes 'netdev' as a netdev named 'name' of the specified 'class'.
*
* This function adds 'netdev' to a netdev-owned linked list, so it is very
list_push_back(&netdev_list, &netdev->node);
}
+/* Returns the class type of 'netdev'.
+ *
+ * The caller must not free the returned value. */
+const char *netdev_get_type(const struct netdev *netdev)
+{
+ return netdev->class->type;
+}
+
/* Initializes 'notifier' as a netdev notifier for 'netdev', for which
* notification will consist of calling 'cb', with auxiliary data 'aux'. */
void
struct ofpbuf;
struct in_addr;
struct in6_addr;
+struct shash;
struct svec;
enum netdev_flags {
void netdev_run(void);
void netdev_wait(void);
+int netdev_create(const char *name, const char *type,
+ const struct shash *args);
+int netdev_destroy(const char *name);
+int netdev_reconfigure(const char *name, const struct shash *args);
+
int netdev_open(const char *name, int ethertype, struct netdev **);
void netdev_close(struct netdev *);
#include "port-array.h"
#include "proc-net-compat.h"
#include "process.h"
+#include "shash.h"
#include "socket-util.h"
#include "stp.h"
#include "svec.h"
}
#endif
+/* Attempt to create the network device 'iface_name' through the netdev
+ * library. */
+static int
+set_up_iface(const char *iface_name, bool create)
+{
+ const char *type;
+ const char *arg;
+ struct svec arg_svec;
+ struct shash args;
+ int error;
+ size_t i;
+
+ /* If a type is not explicitly declared, then assume it's an existing
+ * "system" device. */
+ type = cfg_get_string(0, "iface.%s.type", iface_name);
+ if (!type || !strcmp(type, "system")) {
+ return 0;
+ }
+
+ svec_init(&arg_svec);
+ cfg_get_subsections(&arg_svec, "iface.%s.args", iface_name);
+
+ shash_init(&args);
+ SVEC_FOR_EACH (i, arg, &arg_svec) {
+ const char *value;
+
+ value = cfg_get_string(0, "iface.%s.args.%s", iface_name, arg);
+ if (value) {
+ shash_add(&args, arg, xstrdup(value));
+ }
+ }
+
+ if (create) {
+ error = netdev_create(iface_name, type, &args);
+ } else {
+ /* xxx Check to make sure that the type hasn't changed. */
+ error = netdev_reconfigure(iface_name, &args);
+ }
+
+ svec_destroy(&arg_svec);
+ shash_destroy(&args);
+
+ return error;
+}
+
+static int
+create_iface(const char *iface_name)
+{
+ return set_up_iface(iface_name, true);
+}
+
+static int
+reconfigure_iface(const char *iface_name)
+{
+ return set_up_iface(iface_name, false);
+}
+
+static void
+destroy_iface(const char *iface_name)
+{
+ netdev_destroy(iface_name);
+}
+
+
/* iterate_and_prune_ifaces() callback function that opens the network device
* for 'iface', if it is not already open, and retrieves the interface's MAC
* address and carrier status. */
p->devname, dpif_name(br->dpif),
strerror(retval));
}
+ destroy_iface(p->devname);
}
}
svec_destroy(&want_ifaces);
bridge_get_all_ifaces(br, &want_ifaces);
svec_diff(&want_ifaces, &cur_ifaces, &add_ifaces, NULL, NULL);
+ for (i = 0; i < cur_ifaces.n; i++) {
+ const char *if_name = cur_ifaces.names[i];
+ reconfigure_iface(if_name);
+ }
+
for (i = 0; i < add_ifaces.n; i++) {
const char *if_name = add_ifaces.names[i];
bool internal;
int error;
+ /* Attempt to create the network interface in case it
+ * doesn't exist yet. */
+ error = create_iface(if_name);
+ if (error) {
+ VLOG_WARN("could not create iface %s: %s\n", if_name,
+ strerror(error));
+ continue;
+ }
+
/* Add to datapath. */
internal = iface_is_internal(br, if_name);
error = dpif_port_add(br->dpif, if_name,