+/* A vlandev implementation. */
+struct vlandev_class {
+ int (*vd_refresh)(void);
+ int (*vd_add)(const char *real_dev, int vid);
+ int (*vd_del)(const char *vlan_dev);
+};
+
+#ifdef __linux__
+static const struct vlandev_class vlandev_linux_class;
+#endif
+static const struct vlandev_class vlandev_stub_class;
+static const struct vlandev_class vlandev_dummy_class;
+
+/* The in-use vlandev implementation. */
+static const struct vlandev_class *vd_class;
+
+/* Maps from a VLAN device name (e.g. "eth0.10") to struct vlan_dev. */
+static struct shash vlan_devs = SHASH_INITIALIZER(&vlan_devs);
+
+/* Maps from a VLAN real device name (e.g. "eth0") to struct vlan_real_dev. */
+static struct shash vlan_real_devs = SHASH_INITIALIZER(&vlan_real_devs);
+
+static int vlandev_add__(const char *vlan_dev, const char *real_dev, int vid);
+static int vlandev_del__(const char *vlan_dev);
+static void vlandev_clear__(void);
+
+static const struct vlandev_class *
+vlandev_get_class(void)
+{
+ if (!vd_class) {
+#if __linux__
+ vd_class = &vlandev_linux_class;
+#else
+ vd_class = &vlandev_stub_class;
+#endif
+ }
+ return vd_class;
+}
+
+/* On Linux, the default implementation of VLAN devices creates and destroys
+ * Linux VLAN devices. On other OSess, the default implementation is a
+ * nonfunctional stub. In either case, this function replaces this default
+ * implementation by a "dummy" implementation that simply reports back whatever
+ * the client sets up with vlandev_add() and vlandev_del().
+ *
+ * Don't call this function directly; use dummy_enable() from dummy.h. */
+void
+vlandev_dummy_enable(void)
+{
+ if (vd_class != &vlandev_dummy_class) {
+ vd_class = &vlandev_dummy_class;
+ vlandev_clear__();
+ }
+}
+
+/* Creates a new VLAN device for VLAN 'vid' on top of real Ethernet device
+ * 'real_dev'. Returns 0 if successful, otherwise a positive errno value. On
+ * OSes other than Linux, in the absence of dummies (see
+ * vlandev_dummy_enable()), this always fails.
+ *
+ * The name of the new VLAN device is not easily predictable, because Linux
+ * provides multiple naming schemes, does not allow the client to specify a
+ * name, and does not directly report the new VLAN device's name. Use
+ * vlandev_refresh() then vlandev_get_name() to find out the new VLAN device's
+ * name,. */
+int
+vlandev_add(const char *real_dev, int vid)
+{
+ return vlandev_get_class()->vd_add(real_dev, vid);
+}
+
+/* Deletes the VLAN device named 'vlan_dev'. Returns 0 if successful,
+ * otherwise a positive errno value. On OSes other than Linux, in the absence
+ * of dummies (see vlandev_dummy_enable()), this always fails. */
+int
+vlandev_del(const char *vlan_dev)
+{
+ return vlandev_get_class()->vd_del(vlan_dev);
+}
+
+/* Refreshes the cache of real device to VLAN device mappings reported by
+ * vlandev_get_real_devs() and vlandev_get_name(). Without calling this
+ * function, changes made by vlandev_add() and vlandev_del() may not be
+ * reflected by vlandev_get_real_devs() and vlandev_get_name() output. */
+int
+vlandev_refresh(void)
+{
+ const struct vlandev_class *class = vlandev_get_class();
+ return class->vd_refresh ? class->vd_refresh() : 0;
+}
+
+/* Returns a shash mapping from the name of real Ethernet devices used as the
+ * basis of VLAN devices to struct vlan_real_devs. The caller must not modify
+ * or free anything in the returned shash.
+ *
+ * Changes made by vlandev_add() and vlandev_del() may not be reflected in this
+ * function's output without an intervening call to vlandev_refresh(). */
+struct shash *
+vlandev_get_real_devs(void)
+{
+ return &vlan_real_devs;
+}
+
+/* Returns the name of the VLAN device for VLAN 'vid' on top of
+ * 'real_dev_name', or NULL if there is no such VLAN device.
+ *
+ * Changes made by vlandev_add() and vlandev_del() may not be reflected in this
+ * function's output without an intervening call to vlandev_refresh(). */
+const char *
+vlandev_get_name(const char *real_dev_name, int vid)
+{
+ const struct vlan_real_dev *real_dev;
+
+ real_dev = shash_find_data(&vlan_real_devs, real_dev_name);
+ if (real_dev) {
+ const struct vlan_dev *vlan_dev;
+
+ HMAP_FOR_EACH_WITH_HASH (vlan_dev, hmap_node, hash_int(vid, 0),
+ &real_dev->vlan_devs) {
+ if (vlan_dev->vid == vid) {
+ return vlan_dev->name;
+ }
+ }
+ }
+
+ return NULL;
+}
+\f
+/* The Linux vlandev implementation. */
+
+#ifdef __linux__