[ovs-dev] [VLAN splinters 08/16] vlandev: New library for working with Linux VLAN devices.

Ethan Jackson ethan at nicira.com
Wed Nov 23 04:55:36 UTC 2011


Looks good.

Ethan

On Tue, Nov 15, 2011 at 17:17, Ben Pfaff <blp at nicira.com> wrote:
> ---
>  lib/automake.mk    |    2 +
>  lib/netdev-linux.c |    8 ++
>  lib/netdev-linux.h |    1 +
>  lib/vlandev.c      |  213 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  lib/vlandev.h      |   52 +++++++++++++
>  5 files changed, 276 insertions(+), 0 deletions(-)
>  create mode 100644 lib/vlandev.c
>  create mode 100644 lib/vlandev.h
>
> diff --git a/lib/automake.mk b/lib/automake.mk
> index 6484518..8a82a72 100644
> --- a/lib/automake.mk
> +++ b/lib/automake.mk
> @@ -182,6 +182,8 @@ lib_libopenvswitch_a_SOURCES = \
>        lib/vconn.h \
>        lib/vlan-bitmap.c \
>        lib/vlan-bitmap.h \
> +       lib/vlandev.c \
> +       lib/vlandev.h \
>        lib/vlog.c \
>        lib/vlog.h
>  nodist_lib_libopenvswitch_a_SOURCES = \
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> index 134d99b..44167da 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -3932,6 +3932,14 @@ tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes)
>
>  /* Linux-only functions declared in netdev-linux.h  */
>
> +/* Returns a fd for an AF_INET socket or a negative errno value. */
> +int
> +netdev_linux_get_af_inet_sock(void)
> +{
> +    int error = netdev_linux_init();
> +    return error ? -error : af_inet_sock;
> +}
> +
>  /* Modifies the 'flag' bit in ethtool's flags field for 'netdev'.  If
>  * 'enable' is true, the bit is set.  Otherwise, it is cleared. */
>  int
> diff --git a/lib/netdev-linux.h b/lib/netdev-linux.h
> index c8caa4e..21a2db2 100644
> --- a/lib/netdev-linux.h
> +++ b/lib/netdev-linux.h
> @@ -29,5 +29,6 @@ struct rtnl_link_stats;
>
>  int netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag,
>                                   const char *flag_name, bool enable);
> +int netdev_linux_get_af_inet_sock(void);
>
>  #endif /* netdev-linux.h */
> diff --git a/lib/vlandev.c b/lib/vlandev.c
> new file mode 100644
> index 0000000..2e5bb79
> --- /dev/null
> +++ b/lib/vlandev.c
> @@ -0,0 +1,213 @@
> +/*
> + * Copyright (c) 2011 Nicira Networks.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +
> +#include "vlandev.h"
> +
> +#include <errno.h>
> +#include <sys/ioctl.h>
> +#include <sys/stat.h>
> +
> +#include "hash.h"
> +#include "rtnetlink-link.h"
> +#include "shash.h"
> +#include "vlog.h"
> +
> +VLOG_DEFINE_THIS_MODULE(vlandev);
> +
> +#ifdef __linux__
> +#include <linux/if_vlan.h>
> +#include <linux/sockios.h>
> +#include "netdev-linux.h"
> +
> +static struct nln_notifier *vlan_cache_notifier;
> +static struct shash vlan_devs = SHASH_INITIALIZER(&vlan_devs);
> +static struct shash vlan_real_devs = SHASH_INITIALIZER(&vlan_real_devs);
> +static bool cache_valid;
> +
> +static void
> +vlan_cache_cb(const struct rtnetlink_link_change *change OVS_UNUSED,
> +              void *aux OVS_UNUSED)
> +{
> +    cache_valid = false;
> +}
> +
> +int
> +vlandev_refresh(void)
> +{
> +    const char *fn = "/proc/net/vlan/config";
> +    struct shash_node *node;
> +    char line[128];
> +    FILE *stream;
> +
> +    if (!vlan_cache_notifier) {
> +        vlan_cache_notifier = rtnetlink_link_notifier_create(vlan_cache_cb,
> +                                                             NULL);
> +        if (!vlan_cache_notifier) {
> +            return EINVAL;
> +        }
> +    }
> +
> +    if (cache_valid) {
> +        return 0;
> +    }
> +
> +    /* Free old cache.
> +     *
> +     * The 'name' members point to strings owned by the "shash"es so we do not
> +     * free them ourselves. */
> +    shash_clear_free_data(&vlan_devs);
> +    SHASH_FOR_EACH (node, &vlan_real_devs) {
> +        struct vlan_real_dev *vrd = node->data;
> +
> +        hmap_destroy(&vrd->vlan_devs);
> +    }
> +    shash_clear_free_data(&vlan_real_devs);
> +
> +    /* Repopulate cache. */
> +    stream = fopen(fn, "r");
> +    if (!stream) {
> +        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> +        int error = errno;
> +        struct stat s;
> +
> +        if (error == ENOENT && !stat("/proc", &s)) {
> +            /* Probably the vlan module just isn't loaded, and probably that's
> +             * because no VLAN devices have been created.
> +             *
> +             * Not really an error. */
> +            return 0;
> +        }
> +
> +        VLOG_WARN_RL(&rl, "%s: open failed (%s)", fn, strerror(error));
> +        return error;
> +    }
> +
> +    while (fgets(line, sizeof line, stream)) {
> +        char vlan_dev[16], real_dev[16];
> +        int vid;
> +
> +        if (sscanf(line, "%15[^ |] | %d | %15s", vlan_dev, &vid, real_dev) == 3
> +            && vid >= 0 && vid <= 4095
> +            && !shash_find(&vlan_devs, vlan_dev)) {
> +            struct vlan_real_dev *vrd;
> +            struct vlan_dev *vd;
> +
> +            vrd = shash_find_data(&vlan_real_devs, real_dev);
> +            if (!vrd) {
> +                vrd = xmalloc(sizeof *vrd);
> +                vrd->name = xstrdup(real_dev);
> +                hmap_init(&vrd->vlan_devs);
> +                shash_add_nocopy(&vlan_real_devs, vrd->name, vrd);
> +            }
> +
> +            vd = xmalloc(sizeof *vd);
> +            hmap_insert(&vrd->vlan_devs, &vd->hmap_node, hash_int(vid, 0));
> +            vd->name = xstrdup(vlan_dev);
> +            vd->vid = vid;
> +            vd->real_dev = vrd;
> +            shash_add_nocopy(&vlan_devs, vd->name, vd);
> +        }
> +    }
> +    fclose(stream);
> +
> +    cache_valid = true;
> +    return 0;
> +}
> +
> +struct shash *
> +vlandev_get_real_devs(void)
> +{
> +    return &vlan_real_devs;
> +}
> +
> +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;
> +}
> +
> +static int
> +do_vlan_ioctl(const char *netdev_name, struct vlan_ioctl_args *via,
> +              int cmd, const char *cmd_name)
> +{
> +    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> +    int error;
> +    int sock;
> +
> +    via->cmd = cmd;
> +    ovs_strlcpy(via->device1, netdev_name, sizeof via->device1);
> +
> +    sock = netdev_linux_get_af_inet_sock();
> +    if (sock < 0) {
> +        return -sock;
> +    }
> +
> +    error = ioctl(sock, SIOCSIFVLAN, via) < 0 ? errno : 0;
> +    if (error) {
> +        VLOG_WARN_RL(&rl, "%s: VLAN ioctl %s failed (%s)",
> +                     netdev_name, cmd_name, strerror(error));
> +    }
> +    return error;
> +}
> +
> +int
> +vlandev_add(const char *real_dev, int vid)
> +{
> +    struct vlan_ioctl_args via;
> +    int error;
> +
> +    memset(&via, 0, sizeof via);
> +    via.u.VID = vid;
> +
> +    error = do_vlan_ioctl(real_dev, &via, ADD_VLAN_CMD, "ADD_VLAN_CMD");
> +    if (!error) {
> +        cache_valid = false;
> +    }
> +    return error;
> +}
> +
> +int
> +vlandev_del(const char *vlan_dev)
> +{
> +    struct vlan_ioctl_args via;
> +    int error;
> +
> +    memset(&via, 0, sizeof via);
> +    error = do_vlan_ioctl(vlan_dev, &via, DEL_VLAN_CMD, "DEL_VLAN_CMD");
> +    if (!error) {
> +        cache_valid = false;
> +    }
> +    return error;
> +}
> +#else  /* !__linux__ */
> +
> +#endif
> diff --git a/lib/vlandev.h b/lib/vlandev.h
> new file mode 100644
> index 0000000..cba0fa4
> --- /dev/null
> +++ b/lib/vlandev.h
> @@ -0,0 +1,52 @@
> +/*
> + * Copyright (c) 2011 Nicira Networks.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef VLANDEV_H
> +#define VLANDEV_H 1
> +
> +#include "hmap.h"
> +
> +/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
> + *
> + * This is deprecated.  It is only for compatibility with broken device
> + * drivers in old versions of Linux that do not properly support VLANs when
> + * VLAN devices are not used.  When broken device drivers are no longer in
> + * widespread use, we will delete these interfaces. */
> +
> +/* A VLAN device (e.g. "eth0.10" for VLAN 10 on eth0). */
> +struct vlan_dev {
> +    struct vlan_real_dev *real_dev; /* Parent, e.g. "eth0". */
> +    struct hmap_node hmap_node;     /* In vlan_real_dev's "vlan_devs" map. */
> +    char *name;                     /* VLAN device name, e.g. "eth0.10". */
> +    int vid;                        /* VLAN ID, e.g. 10. */
> +};
> +
> +/* A device that has VLAN devices broken out of it. */
> +struct vlan_real_dev {
> +    char *name;                 /* Name, e.g. "eth0". */
> +    struct hmap vlan_devs;      /* All child VLAN devices, hashed by VID. */
> +};
> +
> +int vlandev_refresh(void);
> +
> +struct shash *vlandev_get_real_devs(void);
> +
> +const char *vlandev_get_name(const char *real_dev_name, int vid);
> +
> +int vlandev_add(const char *real_dev, int vid);
> +int vlandev_del(const char *vlan_dev);
> +
> +#endif /* vlandev.h */
> --
> 1.7.4.4
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
>



More information about the dev mailing list