[ovs-dev] [PATCH 4/4] netdev: Add support for "patch" type

Justin Pettit jpettit at nicira.com
Wed Apr 14 07:31:25 UTC 2010


This commit introduces a new netdev type called "patch".  A patch is a
pair of interfaces, in which frames sent through one of the devices
pop out of the other.  This is useful for linking together datapaths.

A patch's only argument on creation is "peer", which specifies the other
side of the patch.  A patch must be created in pairs, so a second netdev
must be created with the "name" and "peer" values reversed.

The current implementation is built using veth devices.  Further, it's
limited to the veth devices which support configuration through sysfs.
This limits the ability to use a "patch" on 2.6.18 kernels using the
veth device we include (read: flavors of XenServer 5.5).  In the not too
distant future, the implementation will be modified to use the new
kernel port abstraction introduced by Jesse Gross's forthcoming GRE
work.  At that point, patch devices will work on any Linux platform
supported by OVS.
---
 lib/netdev-linux.c    |  180 ++++++++++++++++++++++++++++++++++++++++++++++++-
 lib/netdev-provider.h |    1 +
 lib/netdev.c          |    1 +
 vswitchd/vswitch.xml  |    3 +
 4 files changed, 183 insertions(+), 2 deletions(-)

diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index 736b588..4d4bf90 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -152,6 +152,7 @@ static struct rtnetlink_notifier netdev_linux_poll_notifier;
  * additional log messages. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 
+static int if_up(const char *name);
 static int destroy_gre(const char *name);
 static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *,
                                    int cmd, const char *cmd_name);
@@ -177,7 +178,7 @@ netdev_dev_linux_cast(const struct netdev_dev *netdev_dev)
 {
     const char *type = netdev_dev_get_type(netdev_dev);
     assert(!strcmp(type, "system") || !strcmp(type, "tap")
-            || !strcmp(type, "gre"));
+            || !strcmp(type, "gre") || !strcmp(type, "patch"));
     return CONTAINER_OF(netdev_dev, struct netdev_dev_linux, netdev_dev);
 }
 
@@ -186,7 +187,7 @@ netdev_linux_cast(const struct netdev *netdev)
 {
     const char *type = netdev_get_type(netdev);
     assert(!strcmp(type, "system") || !strcmp(type, "tap")
-            || !strcmp(type, "gre"));
+            || !strcmp(type, "gre") || !strcmp(type, "patch"));
     return CONTAINER_OF(netdev, struct netdev_linux, netdev);
 }
 
@@ -581,6 +582,81 @@ error:
     return error;
 }
 
+static int
+create_patch(const char *name, const char *peer)
+{
+    FILE *veth_file;
+    int retval;
+
+    /* Assume if both sides exist that they're part of the same patch */
+    if (netdev_exists(name) && netdev_exists(peer)) {
+        return 0;
+    }
+
+    if (netdev_exists(name) || netdev_exists(peer)) {
+        VLOG_WARN("one side of patch %s/%s exists", name, peer);
+        return EEXIST;
+    }
+
+    /* Create the interfaces */
+    veth_file = fopen("/sys/class/net/veth_pairs", "w");
+    if (!veth_file) {
+        VLOG_WARN("could not open veth device.  Are you running a "
+                "supported XenServer with the kernel module loaded?");
+        return ENODEV;
+    }
+    setvbuf(veth_file, NULL, _IONBF, 0);
+    retval = fprintf(veth_file, "+%s,%s", name, peer);
+    fclose(veth_file);
+    if (retval < 0) {
+        VLOG_WARN("could not create patch pair: %s", strerror(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)
+{
+    int error;
+    struct shash_node *node;
+    const char *peer = NULL;
+
+    SHASH_FOR_EACH (node, args) {
+        if (!strcmp(node->name, "peer")) {
+            if (strlen(node->data) > IFNAMSIZ) {
+                VLOG_WARN("patch 'peer' arg too long, must be <= %d", 
+                        IFNAMSIZ);
+                return EINVAL;
+            }
+            peer = node->data;
+        } else {
+            VLOG_WARN("unknown patch argument '%s'", node->name);
+        }
+    }
+
+    if (!peer) {
+        VLOG_WARN("patch type requires valid 'peer' argument");
+        return EINVAL;
+    }
+
+    return create_patch(name, peer);
+
+error:
+    return error;
+}
+
 /* Creates the netdev device of 'type' with 'name'. */
 static int
 netdev_linux_create_system(const char *name, const char *type OVS_UNUSED,
@@ -711,6 +787,29 @@ 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;
+    int error;
+
+    netdev_dev = xzalloc(sizeof *netdev_dev);
+
+    error = setup_patch(name, args);
+    if (error) {
+        goto error;
+    }
+
+    netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_patch_class);
+    *netdev_devp = &netdev_dev->netdev_dev;
+    return 0;
+
+error:
+    free(netdev_dev);
+    return error;
+}
+
+static int
 netdev_linux_reconfigure_gre(struct netdev_dev *netdev_dev_,
                              const struct shash *args)
 {
@@ -799,6 +898,33 @@ destroy_gre(const char *name)
     }
 }
 
+static void
+destroy_patch(const char *name)
+{
+    FILE *veth_file;
+    int retval;
+
+    if (!netdev_exists(name)) {
+        VLOG_WARN("patch device %s does not exist", name);
+        return;
+    }
+
+    /* Destroy both interfaces by destroying either */
+    veth_file = fopen("/sys/class/net/veth_pairs", "w");
+    if (!veth_file) {
+        VLOG_WARN("could not open veth device.  Are you running a "
+                "supported XenServer with the kernel module loaded?");
+        return;
+    }
+    setvbuf(veth_file, NULL, _IONBF, 0);
+    retval = fprintf(veth_file, "-%s", name);
+    fclose(veth_file);
+    if (retval < 0) {
+        VLOG_WARN("could not destroy patch: %s", strerror(retval));
+        return;
+    }
+}
+
 /* Destroys the netdev device 'netdev_dev_'. */
 static void
 netdev_linux_destroy(struct netdev_dev *netdev_dev_)
@@ -816,6 +942,8 @@ netdev_linux_destroy(struct netdev_dev *netdev_dev_)
         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_get_name(&netdev_dev->netdev_dev));
     }
 
     free(netdev_dev_);
@@ -2102,6 +2230,54 @@ const struct netdev_class netdev_gre_class = {
     netdev_linux_poll_add,
     netdev_linux_poll_remove,
 };
+
+const struct netdev_class netdev_patch_class = {
+    "patch",
+
+    netdev_linux_init,
+    netdev_linux_run,
+    netdev_linux_wait,
+
+    netdev_linux_create_patch,
+    netdev_linux_destroy,
+    NULL,                       /* reconfigure */
+
+    netdev_linux_open,
+    netdev_linux_close,
+
+    NULL,                       /* enumerate */
+
+    netdev_linux_recv,
+    netdev_linux_recv_wait,
+    netdev_linux_drain,
+
+    netdev_linux_send,
+    netdev_linux_send_wait,
+
+    netdev_linux_set_etheraddr,
+    netdev_linux_get_etheraddr,
+    netdev_linux_get_mtu,
+    netdev_linux_get_ifindex,
+    netdev_linux_get_carrier,
+    netdev_linux_get_stats,
+
+    netdev_linux_get_features,
+    netdev_linux_set_advertisements,
+    netdev_linux_get_vlan_vid,
+    netdev_linux_set_policing,
+
+    netdev_linux_get_in4,
+    netdev_linux_set_in4,
+    netdev_linux_get_in6,
+    netdev_linux_add_router,
+    netdev_linux_get_next_hop,
+    netdev_linux_arp_lookup,
+
+    netdev_linux_update_flags,
+
+    netdev_linux_poll_add,
+    netdev_linux_poll_remove,
+};
 
 static int
 get_stats_via_netlink(int ifindex, struct netdev_stats *stats)
diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
index e1c18cc..aac482b 100644
--- a/lib/netdev-provider.h
+++ b/lib/netdev-provider.h
@@ -385,6 +385,7 @@ struct netdev_class {
 extern const struct netdev_class netdev_linux_class;
 extern const struct netdev_class netdev_tap_class;
 extern const struct netdev_class netdev_gre_class;
+extern const struct netdev_class netdev_patch_class;
 
 #ifdef  __cplusplus
 }
diff --git a/lib/netdev.c b/lib/netdev.c
index 99b5d24..af2382d 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -45,6 +45,7 @@ static const struct netdev_class *base_netdev_classes[] = {
     &netdev_linux_class,
     &netdev_tap_class,
     &netdev_gre_class,
+    &netdev_patch_class,
 };
 
 static struct shash netdev_classes = SHASH_INITIALIZER(&netdev_classes);
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index d3f3efb..873c145 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -357,6 +357,9 @@
           <dd>A TUN/TAP device managed by Open vSwitch.</dd>
           <dt><code>gre</code></dt>
           <dd>A GRE tunnel device managed by Open vSwitch.</dd>
+          <dt><code>patch</code></dt>
+          <dd>A pair of virtual devices that act as patch cable managed by 
+            Open vSwitch.</dd>
         </dl>
       </column>
 
-- 
1.7.0.3





More information about the dev mailing list