+ cache_notifier_refcount++;
+
+ netdev_dev = xzalloc(sizeof *netdev_dev);
+ netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_linux_class);
+
+ *netdev_devp = &netdev_dev->netdev_dev;
+ return 0;
+}
+
+/* For most types of netdevs we open the device for each call of
+ * netdev_open(). However, this is not the case with tap devices,
+ * since it is only possible to open the device once. In this
+ * situation we share a single file descriptor, and consequently
+ * buffers, across all readers. Therefore once data is read it will
+ * be unavailable to other reads for tap devices. */
+static int
+netdev_linux_create_tap(const char *name, const char *type OVS_UNUSED,
+ const struct shash *args, struct netdev_dev **netdev_devp)
+{
+ struct netdev_dev_linux *netdev_dev;
+ struct tap_state *state;
+ static const char tap_dev[] = "/dev/net/tun";
+ struct ifreq ifr;
+ int error;
+
+ if (!shash_is_empty(args)) {
+ VLOG_WARN("%s: arguments for TAP devices should be empty", name);
+ }
+
+ netdev_dev = xzalloc(sizeof *netdev_dev);
+ state = &netdev_dev->state.tap;
+
+ /* Open tap device. */
+ state->fd = open(tap_dev, O_RDWR);
+ if (state->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(state->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(state->fd);
+ if (error) {
+ goto error;
+ }
+
+ netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_tap_class);
+ *netdev_devp = &netdev_dev->netdev_dev;
+ return 0;
+
+error:
+ free(netdev_dev);
+ return error;
+}
+
+static int
+netdev_linux_create_patch(const char *name, const char *type OVS_UNUSED,
+ const struct shash *args, struct netdev_dev **netdev_devp)
+{
+ struct netdev_dev_linux *netdev_dev;
+ char *peer = NULL;
+ int error;
+
+ error = setup_patch(name, args, &peer);
+ if (error) {
+ free(peer);
+ return error;
+ }
+
+ netdev_dev = xzalloc(sizeof *netdev_dev);
+ netdev_dev->state.patch.peer = peer;
+ netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_patch_class);
+ *netdev_devp = &netdev_dev->netdev_dev;
+
+ return 0;
+}
+
+static void
+destroy_tap(struct netdev_dev_linux *netdev_dev)
+{
+ struct tap_state *state = &netdev_dev->state.tap;
+
+ if (state->fd >= 0) {
+ close(state->fd);
+ }
+}
+
+static void
+destroy_patch(struct netdev_dev_linux *netdev_dev)
+{
+ const char *name = netdev_dev_get_name(&netdev_dev->netdev_dev);
+ struct patch_state *state = &netdev_dev->state.patch;
+
+ /* Only destroy veth if 'peer' doesn't exist as an existing netdev. */
+ if (!netdev_dev_from_name(state->peer)) {
+ modify_veth("-%s", name);
+ }
+ free(state->peer);
+}
+
+/* Destroys the netdev device 'netdev_dev_'. */
+static void
+netdev_linux_destroy(struct netdev_dev *netdev_dev_)
+{
+ struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_dev_);
+ const char *type = netdev_dev_get_type(netdev_dev_);
+
+ if (!strcmp(type, "system")) {
+ cache_notifier_refcount--;
+
+ if (!cache_notifier_refcount) {
+ rtnetlink_notifier_unregister(&netdev_linux_cache_notifier);
+ }
+ } else if (!strcmp(type, "tap")) {
+ destroy_tap(netdev_dev);
+ } else if (!strcmp(type, "patch")) {
+ destroy_patch(netdev_dev);
+ }
+
+ free(netdev_dev);
+}
+
+static int
+netdev_linux_open(struct netdev_dev *netdev_dev_, int ethertype,
+ struct netdev **netdevp)
+{
+ struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_dev_);
+ struct netdev_linux *netdev;
+ enum netdev_flags flags;
+ int error;
+
+ /* Allocate network device. */
+ netdev = xzalloc(sizeof *netdev);
+ netdev->fd = -1;
+ netdev_init(&netdev->netdev, netdev_dev_);