+
+ if (gre_descriptors.use_ioctl) {
+ error = setup_gre_ioctl(name, &config, create);
+ } else {
+ error = setup_gre_netlink(name, &config, create);
+ }
+ }
+
+error:
+ return error;
+}
+
+/* A veth may be created using the 'command' "+<name>,<peer>". A veth may
+ * be destroyed by using the 'command' "-<name>", where <name> can be
+ * either side of the device.
+ */
+static int
+modify_veth(const char *format, ...)
+{
+ FILE *veth_file;
+ va_list args;
+ int retval;
+
+ veth_file = fopen("/sys/class/net/veth_pairs", "w");
+ if (!veth_file) {
+ VLOG_WARN_RL(&rl, "could not open veth device. Are you running a "
+ "supported XenServer with the kernel module loaded?");
+ return ENODEV;
+ }
+ setvbuf(veth_file, NULL, _IONBF, 0);
+
+ va_start(args, format);
+ retval = vfprintf(veth_file, format, args);
+ va_end(args);
+
+ fclose(veth_file);
+ if (retval < 0) {
+ VLOG_WARN_RL(&rl, "could not destroy patch: %s", strerror(errno));
+ return errno;
+ }
+
+ return 0;
+}
+
+static int
+create_patch(const char *name, const char *peer)
+{
+ int retval;
+ struct netdev_dev *peer_nd;
+
+
+ /* Only create the veth if the peer didn't already do it. */
+ peer_nd = netdev_dev_from_name(peer);
+ if (peer_nd) {
+ if (!strcmp("patch", netdev_dev_get_type(peer_nd))) {
+ struct netdev_dev_linux *ndl = netdev_dev_linux_cast(peer_nd);
+ if (!strcmp(name, ndl->state.patch.peer)) {
+ return 0;
+ } else {
+ VLOG_WARN_RL(&rl, "peer '%s' already paired with '%s'",
+ peer, ndl->state.patch.peer);
+ return EINVAL;
+ }
+ } else {
+ VLOG_WARN_RL(&rl, "peer '%s' exists and is not a patch", peer);
+ return EINVAL;
+ }
+ }
+
+ retval = modify_veth("+%s,%s", name, peer);
+ if (retval) {
+ return retval;
+ }
+
+ retval = if_up(name);
+ if (retval) {
+ return retval;
+ }
+
+ retval = if_up(peer);
+ if (retval) {
+ return retval;
+ }
+
+ return 0;
+}
+
+static int
+setup_patch(const char *name, const struct shash *args, char **peer_)
+{
+ const char *peer;
+
+ peer = shash_find_data(args, "peer");
+ if (!peer) {
+ VLOG_WARN("patch type requires valid 'peer' argument");
+ return EINVAL;
+ }
+
+ if (shash_count(args) > 1) {
+ VLOG_WARN("patch type takes only a 'peer' argument");
+ return EINVAL;
+ }
+
+ if (strlen(peer) >= IFNAMSIZ) {
+ VLOG_WARN_RL(&rl, "patch 'peer' arg too long");
+ return EINVAL;
+ }
+
+ *peer_ = xstrdup(peer);
+ return create_patch(name, peer);
+}
+
+/* Creates the netdev device of 'type' with 'name'. */
+static int
+netdev_linux_create_system(const char *name, const char *type OVS_UNUSED,
+ const struct shash *args, struct netdev_dev **netdev_devp)
+{
+ struct netdev_dev_linux *netdev_dev;
+ int error;
+
+ if (!shash_is_empty(args)) {
+ VLOG_WARN("%s: arguments for system devices should be empty", name);
+ }
+
+ if (!cache_notifier_refcount) {
+ error = rtnetlink_notifier_register(&netdev_linux_cache_notifier,
+ netdev_linux_cache_cb, NULL);
+ if (error) {
+ return error;
+ }
+ }
+ 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
+if_up(const char *name)
+{
+ struct ifreq ifr;
+
+ strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+ ifr.ifr_flags = IFF_UP;
+
+ if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) {
+ VLOG_DBG_RL(&rl, "%s: failed to bring device up: %s",
+ name, strerror(errno));
+ return errno;
+ }
+
+ return 0;
+}
+
+static int
+netdev_linux_create_gre(const char *name, const char *type OVS_UNUSED,
+ const struct shash *args, struct netdev_dev **netdev_devp)
+{
+ struct netdev_dev_linux *netdev_dev;
+ int error;
+
+ netdev_dev = xzalloc(sizeof *netdev_dev);
+
+ error = setup_gre(name, args, true);
+ if (error) {
+ goto error;
+ }
+
+ error = if_up(name);
+ if (error) {
+ goto error;
+ }
+
+ netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_gre_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 int
+netdev_linux_reconfigure_gre(struct netdev_dev *netdev_dev_,
+ const struct shash *args)
+{
+ const char *name = netdev_dev_get_name(netdev_dev_);
+
+ return setup_gre(name, args, false);
+}
+
+/* The arguments are marked as unused to prevent warnings on platforms where
+ * the Netlink interface isn't supported. */
+static int
+destroy_gre_netlink(const char *name OVS_UNUSED)
+{
+#ifdef GRE_IOCTL_ONLY
+ return EOPNOTSUPP;
+#else
+ int error;
+ struct ofpbuf request, *reply;
+ struct ifinfomsg ifinfomsg;
+ int ifindex;
+
+ ofpbuf_init(&request, 0);
+
+ nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, 0, RTM_DELLINK,
+ NLM_F_REQUEST);
+
+ memset(&ifinfomsg, 0, sizeof ifinfomsg);
+ ifinfomsg.ifi_family = AF_UNSPEC;
+ nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg);
+
+ ifindex = do_get_ifindex(name);
+ nl_msg_put_u32(&request, IFLA_LINK, ifindex);
+
+ nl_msg_put_string(&request, IFLA_IFNAME, name);
+
+ error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply);
+ ofpbuf_uninit(&request);
+ if (error) {
+ VLOG_WARN("couldn't transact netlink socket: %s", strerror(error));
+ goto error;
+ }
+ ofpbuf_delete(reply);
+
+error:
+ return 0;
+#endif
+}
+
+static int
+destroy_gre_ioctl(const char *name)
+{
+ struct ip_tunnel_parm p;
+ struct ifreq ifr;
+
+ memset(&p, 0, sizeof p);
+ strncpy(p.name, name, IFNAMSIZ);
+
+ strncpy(ifr.ifr_name, name, IFNAMSIZ);
+ ifr.ifr_ifru.ifru_data = (void *)&p;
+
+ if (ioctl(gre_descriptors.ioctl_fd, SIOCDELGRETAP, &ifr) < 0) {
+ VLOG_WARN("couldn't do gre ioctl: %s\n", strerror(errno));
+ return errno;
+ }
+
+ 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 int
+destroy_gre(const char *name)
+{
+ if (gre_descriptors.use_ioctl) {
+ return destroy_gre_ioctl(name);
+ } else {
+ return destroy_gre_netlink(name);
+ }
+}
+
+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, "gre")) {
+ destroy_gre(netdev_dev_get_name(&netdev_dev->netdev_dev));
+ } else if (!strcmp(type, "patch")) {
+ destroy_patch(netdev_dev);