[libvirt] [PATCH 1/3] add functions: load 8021q module, create/destroy vlan-dev

Shi Lei shilei.massclouds at gmx.com
Wed Jul 18 11:18:56 UTC 2018



> Sent: Wednesday, July 18, 2018 at 12:12 AM
> From: "John Ferlan" <jferlan at redhat.com>
> To: "Shi Lei" <shilei.massclouds at gmx.com>, libvir-list at redhat.com
> Subject: Re: [libvirt] [PATCH 1/3] add functions: load 8021q module, create/destroy vlan-dev
>
> 
> 
> On 07/05/2018 11:36 PM, Shi Lei wrote:
> > Signed-off-by: Shi Lei <shilei.massclouds at gmx.com>
> > ---
> >  configure.ac             |   5 +-
> >  src/libvirt_private.syms |   4 +
> >  src/util/virnetdev.c     | 195 +++++++++++++++++++++++++++++++++++++++++++++++
> >  src/util/virnetdev.h     |  14 ++++
> >  4 files changed, 216 insertions(+), 2 deletions(-)
> > 
> 
> Not exactly in my wheelhouse of knowledge and I suspect when Laine gets
> back from vacation he'd want to look as well, but ... let's see what
> feedback I can provide. I'll probably make a mess, but hopefully its
> understandable.
> 
> First off kudos for actually providing patches first time out that
> compile and pass make check syntax-check!

Thanks for your reviewing, John!
I have thought that my patches have been ignored ...

> 
> > diff --git a/configure.ac b/configure.ac
> > index 59d2d09..141d5b2 100644
> > --- a/configure.ac
> > +++ b/configure.ac
> > @@ -769,8 +769,9 @@ then
> >  fi
> >  AM_CONDITIONAL([WITH_NODE_DEVICES], [test "$with_nodedev" = "yes"])
> >  
> > -dnl GET_VLAN_VID_CMD is required for virNetDevGetVLanID
> 
> Losing this comment for a more generic one probably isn't good.

OK

> 
> > -AC_CHECK_DECLS([GET_VLAN_VID_CMD], [], [], [[#include <linux/if_vlan.h>]])
> > +dnl GET_VLAN_VID_CMD, ADD_VLAN_CMD, DEL_VLAN_CMD is required
> 
> Rather than combining into one and losing the virNetDevGetVLanID why not
> add separate comments "ADD_VLAN_CMD is required for
> virNetDevCreateVLanDev" and "DEL_VLAN_CMD is required for
> virNetDevDestroyVLanDev".
> 

OK. I'll fix it.

> > +AC_CHECK_DECLS([GET_VLAN_VID_CMD, ADD_VLAN_CMD, DEL_VLAN_CMD],
> > +               [], [], [[#include <linux/if_vlan.h>]])
> >  
> >  # Check for Linux vs. BSD ifreq members
> >  AC_CHECK_MEMBERS([struct ifreq.ifr_newname,
> > diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> > index 3e30490..a61ba02 100644
> > --- a/src/libvirt_private.syms
> > +++ b/src/libvirt_private.syms
> > @@ -2266,7 +2266,9 @@ virModuleLoad;
> >  
> >  # util/virnetdev.h
> >  virNetDevAddMulti;
> > +virNetDevCreateVLanDev;
> >  virNetDevDelMulti;
> > +virNetDevDestroyVLanDev;
> >  virNetDevExists;
> >  virNetDevFeatureTypeFromString;
> >  virNetDevFeatureTypeToString;
> > @@ -2287,10 +2289,12 @@ virNetDevGetRxFilter;
> >  virNetDevGetVirtualFunctionIndex;
> >  virNetDevGetVirtualFunctionInfo;
> >  virNetDevGetVirtualFunctions;
> > +virNetDevGetVLanDevName;
> >  virNetDevGetVLanID;
> >  virNetDevIfStateTypeFromString;
> >  virNetDevIfStateTypeToString;
> >  virNetDevIsVirtualFunction;
> > +virNetDevLoad8021Q;
> >  virNetDevPFGetVF;
> >  virNetDevReadNetConfig;
> >  virNetDevRunEthernetScript;
> > diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
> > index c20022f..50a81d2 100644
> > --- a/src/util/virnetdev.c
> > +++ b/src/util/virnetdev.c
> > @@ -44,6 +44,7 @@
> >  # include <linux/sockios.h>
> >  # include <linux/if_vlan.h>
> >  # define VIR_NETDEV_FAMILY AF_UNIX
> > +# include "virkmod.h"
> >  #elif defined(HAVE_STRUCT_IFREQ) && defined(AF_LOCAL)
> >  # define VIR_NETDEV_FAMILY AF_LOCAL
> >  #else
> > @@ -1048,6 +1049,200 @@ int virNetDevGetVLanID(const char *ifname ATTRIBUTE_UNUSED,
> >  #endif /* ! SIOCGIFVLAN */
> >  
> >  
> > +#if defined(HAVE_STRUCT_IFREQ)
> > +
> > +# define MODULE_8021Q "8021q"
> > +# define PROC_NET_VLAN_CONFIG "/proc/net/vlan/config"
> > +
> > +static int
> > +validate8021Q(void)
> 
> I think this could be replaced in the callers with a:
> 
>     virFileExists(PROC_NET_VLAN_CONFIG)
> 
> since all you seem to care is that the file got created. The open here
> is not doing much other than proving that this process could open the
> file. There's nothing be read AFAICT.

Yes. It's better to use virFileExists here. I didn't know it before.

> 
> > +{
> > +    int fd;
> > +    if ((fd = open(PROC_NET_VLAN_CONFIG, O_RDONLY)) < 0)
> > +        return -1;
> > +    VIR_FORCE_CLOSE(fd);
> > +    return 0;
> > +}
> > +
> 
> Adding new functions should keep 2 empty lines between each.

OK.

> 
> > +static int
> > +getVlanDevName(const char *ifname, unsigned int vlanid,
> > +               char vdname[], size_t vdname_size)
> 
> One line per argument please.

OK.

> 
> Why is it 'char vdname[]' and not 'char **'?

OK. I'll change this.
Generally I prefer to use stack variable if I know the limit of its size.

> 
> > +{
> 
> I think a :
> 
>     if (!ifname) {
>         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>                        _("Interface name not provided"));
>         return -1;
>     }
> 
> should be added.

I'll add it.

> 
> > +    if (!vdname || vdname_size < IFNAMSIZ)
> > +        return -1;
> 
> So if this fails eventually a caller is going to want to know why.
> Adding a virReportError here about invalid arguments would be OK, but
> perhaps unnecessary.  The @vdname is an output that if not provided is
> just wrong coding practices.
> 
> I don't believe vdname_size is relevant can be removed.

These two lines will be removed in patch v2.

> 
> 
> > +    if (snprintf(vdname, IFNAMSIZ, "%s.%d", ifname, vlanid) >= IFNAMSIZ)
> 
> use "return virAsprintf(vdname, "%s.%d", ifname, vlandid);"
> 
> it'll magically do the allocation and return - not really caring about
> IFNAMSIZ "stuff".  See virNetDevSysfsFile for the example.

OK. It's better.

> 
> The virNetDevGetVLanDevName should be moved here and then the subsequent
> callers can use it rather than this helper by name.

OK.

> 
> > +        return -1;
> > +    return 0;
> > +}
> > +
> > +static int
> > +controlVlanDev(unsigned int cmd,
> > +               const char *ifname, unsigned int vlanid)
> 
> One line per argument.

OK

> 
> > +{
> 
>     int ret = -1;
> 

Why need ret?

> > +    int fd;
> > +    struct vlan_ioctl_args if_request;
> > +    memset(&if_request, 0, sizeof(struct vlan_ioctl_args));
> > +    if_request.cmd = cmd;
> > +
> 
> Again, add the :
> 
>     if (!ifname) {
>         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>                        _("Interface name not provided"));
>         return -1;
>     }

OK.

> 
> > +    if (cmd == ADD_VLAN_CMD) {
> > +        strcpy(if_request.device1, ifname);
> > +        if_request.u.VID = vlanid;
> > +    } else if (cmd == DEL_VLAN_CMD) {
> > +        char vdname[IFNAMSIZ];
> 
>     char *vdname = NULL;
> 
> > +        if (getVlanDevName(ifname, vlanid, vdname, sizeof(vdname)) < 0)
> 
>     if (virNetDevGetVLanDevName(ifname, vlanid, &vdname) < 0)
> 
> > +            return -1;
> > +        strcpy(if_request.device1, vdname);
> 
> Hmmm... I know I said the length of the created name doesn't matter, but
> this strcpy would seem to be suspicious...
> 
> let's see, I see "vlan_ioctl_args" as a char device1[24];
> 
> with 24 being a very purely magic number with no apparent rhyme or
> reason. In any case, if vdname is > 24 chars, then there is a whole load
> of problems.  How do I say awful design of vlan_ioctl_args politely? /-|
> 
> Anyway this will need to be handled somehow - perhaps a :
> 
>    /* vlan_ioctl_args field has 'device1[24]' so we need to be
>     * sure we don't over flow */
>     if (strlen(vdname) > 24) {
>         virReportError(VIR_ERR_INTERNAL_ERROR,
>                        _("generated vdname='%s' too large"),
>                        vdname);
>         VIR_FREE(vdname);
>         return -1;
>     }
> 
> BTW: Similarly for ADD if @ifname > 24 chars, that strcpy is going to be
> bad.

OK. I'll add this to make sure. But I think that the length of interface name
on linux can't be larger than 15 because IFNAMSIZ is 16(includes '\0').
Both vdname and ifname are interface's names.

> 
> > +    } else {
> > +        virReportSystemError(ENOSYS,
> > +                             _("unsupport cmd: %d"), cmd);
> 
>     virReportError(VIR_ERR_INTERNAL_ERROR,
>                    _("unsupported command option: %d"), cmd);
> 

OK

> > +        return -1;
> > +    }
> > +
> > +    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
> > +        virReportSystemError(errno, "%s",
> > +                             _("unable to open control socket"));
> 
> s/to open/to open VLAN/

OK

> 
> > +        return -1;
> > +    }
> > +
> > +    if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
> > +        virReportSystemError(errno, "%s",
> > +                             _("control VLAN device error"));
> > +        VIR_FORCE_CLOSE(fd);
> > +        return -1;
> > +    }
> > +
> > +    VIR_FORCE_CLOSE(fd);
> > +    return 0;
> > +}
> > +
> > +/**
> > + * virNetDevLoad8021Q:
> > + *
> > + * Load 8021q module (since kernel v2.6)
> > + *
> > + * Returns 0 on success, -1 on failure
> > + */
> > +int
> > +virNetDevLoad8021Q(void)
> > +{
> > +    if (validate8021Q() < 0) {
> 
>     if (!virFileExists(PROC_NET_VLAN_CONFIG)) {
> 
> or
> 
>     if (virFileExists(PROC_NET_VLAN_CONFIG))
>         return 0;
> 
> for one less level of indents.
> 

OK

> > +        char *errbuf = NULL;
> 
> Looks like you can now use:
> 
>     VIR_AUTOFREE(char *) errbuf = NULL;
> 

I'll try it ...

> 
> > +        if ((errbuf = virKModLoad(MODULE_8021Q, false))) {
> > +            VIR_FREE(errbuf);
> > +            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> > +                           _("Failed to load 8021q module"));
> > +            return -1;
> > +        }
> > +        if (validate8021Q() < 0) {
> 
>     if (!virFileExists(PROC_NET_VLAN_CONFIG)) {
> 

OK.

> Hopefully there's no "strange delay" in generating that file that would
> require some sort synchronization effort here...

Maybe need extra code to do this. I'll think about it.

> 
> > +            virReportError(VIR_ERR_NO_SUPPORT, "%s",
> > +                           _("cannot load 8021q module"));
> > +            return -1;
> > +        }
> > +    }
> > +    return 0;
> > +}
> > +
> > +/**
> > + * virNetDevCreateVLanDev:
> > + * @ifname: name of interface we will create vlan-device on
> > + * @vlanid: VLAN ID
> > + * @vdname: used to return the name of vlan-device
> > + *          (it should be pre-defined on stack and its type is char[])
> > + * @vdname_size: size of the vlan-device's name (do not less than IFNAMSIZ)
> > + *
> > + * Create vlan-device which based on 8021q module.
> > + *
> > + * Returns 0 on success, -1 on failure
> > + */
> > +int
> > +virNetDevCreateVLanDev(const char *ifname, unsigned int vlanid,
> > +                       char vdname[], size_t vdname_size)
> 
> One line per arg ...

OK

> 
> char **vdname
> 
> remove vdname_size

OK

> 
> > +{
> > +    if (controlVlanDev(ADD_VLAN_CMD, ifname, vlanid) < 0)
> > +        return -1;
> > +    return getVlanDevName(ifname, vlanid, vdname, vdname_size);
> 
> Note my suggested new usage...
> 
>     virNetDevDestroyVLanDev(ifname, vlanid, vdname)
> 
> However, if this fails, then we probably should delete the VLAN;
> however, we'd need to have a vdname which if we aren't going to put
> together here then there's little chance that the control delete code
> would be able to either.  It's a conundrum.

I prepare to add this parameter vdname for virNetDevDestroyVLanDev.

> 
> > +}
> > +
> > +/**
> > + * virNetDevDestroyVLanDev:
> > + * @ifname: name of interface vlan-device depends on
> > + * @vlanid: VLAN ID
> > + *
> > + * Destroy vlan-device whick has created by virNetDevCreateVLanDev.
> > + *
> > + * Returns 0 on success, -1 on failure
> > + */
> > +int
> > +virNetDevDestroyVLanDev(const char *ifname, unsigned int vlanid)
> > +{
> > +    if (controlVlanDev(DEL_VLAN_CMD, ifname, vlanid) < 0)
> 
>     return controlVlanDev(DEL_VLAN_CMD, ifname, vlanid);

OK

> 
> > +        return -1;
> > +    return 0;
> > +}
> > +
> > +/**
> > + * virNetDevGetVLanDevName:
> > + * @ifname: name of interface vlan-device depends on
> > + * @vlanid: VLAN ID
> > + * @vdname: used to return the name of vlan-device
> > + *          (it should be pre-defined on stack and its type is char[])
> > + * @vdname_size: size of the vlan-device's name (do not less than IFNAMSIZ)
> > + *
> > + * Get the name of the vlan-device
> > + *
> > + * Returns 0 on success, -1 on failure
> > + */
> > +int
> > +virNetDevGetVLanDevName(const char *ifname, unsigned int vlanid,
> > +                        char vdname[], size_t vdname_size)
> > +{
> > +    return getVlanDevName(ifname, vlanid, vdname, vdname_size);
> > +}
> 
> This should move to where getVlanDevName is and then getVlanDevName can
> be removed having this do the magic as noted before.

OK.

> 
> > +
> > +#else /* !HAVE_STRUCT_IFREQ */
> > +
> > +int
> > +virNetDevLoad8021Q(void)
> > +{
> > +    virReportSystemError(ENOSYS, "%s",
> > +                         _("Unable to load 8021q module on this platform"));
> > +    return -1;
> > +}
> > +
> > +int
> > +virNetDevCreateVLanDev(const char *ifname ATTRIBUTE_UNUSED,
> > +                       unsigned int vlanid ATTRIBUTE_UNUSED,
> > +                       char *vdname[] ATTRIBUTE_UNUSED,
> 
> This doesn't match .h prototype

It's my foolish mistake!

> 
> > +                       size_t vdname_size ATTRIBUTE_UNUSED)
> > +{
> > +    virReportSystemError(ENOSYS, "%s",
> > +                         _("Unable to create vlan-dev on this platform"));
> > +    return -1;
> > +}
> > +
> > +int
> > +virNetDevDestroyVLanDev(const char *ifname ATTRIBUTE_UNUSED,
> > +                        unsigned int vlanid ATTRIBUTE_UNUSED)
> > +{
> > +    virReportSystemError(ENOSYS, "%s",
> > +                         _("Unable to destroy vlan-dev on this platform"));
> > +    return -1;
> > +}
> > +
> > +int
> > +virNetDevGetVLanDevName(const char *ifname ATTRIBUTE_UNUSED,
> > +                        unsigned int vlanid ATTRIBUTE_UNUSED,
> > +                        char vdname[] ATTRIBUTE_UNUSED,
> > +                        size_t vdname_size ATTRIBUTE_UNUSED)
> > +{
> > +    virReportSystemError(ENOSYS, "%s",
> > +                         _("Unable to destroy vlan-dev on this platform"));
> 
> Unable to get vlan-dev name on this platform

Yes. My fault!

> 
> > +    return -1;
> > +}
> 
> Whatever order/changes are made for the other side of the #if, will need
> to be made in this #else before posting again.

Sorry! I'll do this next time.

> 
> > +
> > +#endif /* HAVE_STRUCT_IFREQ */
> > +
> > +
> >  /**
> >   * virNetDevValidateConfig:
> >   * @ifname: Name of the interface
> > diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
> > index 71eaf45..40fb6ee 100644
> > --- a/src/util/virnetdev.h
> > +++ b/src/util/virnetdev.h
> > @@ -206,6 +206,20 @@ int virNetDevGetIndex(const char *ifname, int *ifindex)
> >  int virNetDevGetVLanID(const char *ifname, int *vlanid)
> >      ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
> >  
> > +int virNetDevLoad8021Q(void)
> > +    ATTRIBUTE_RETURN_CHECK;
> > +
> > +int virNetDevCreateVLanDev(const char *ifname, unsigned int vlanid,
> > +                           char vdname[], size_t vdname_size)
> > +    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
> > +
> > +int virNetDevDestroyVLanDev(const char *ifname, unsigned int vlanid)
> > +    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
> > +
> > +int virNetDevGetVLanDevName(const char *ifname, unsigned int vlanid,
> > +                            char vdname[], size_t vdname_size)
> > +    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK;
> 
> Rather than ATTRIBUTE_NONNULL, let's just add checks in the code for
> ifname == NULL, then virReportError.  As for vdname, those should be
> char **vdname anyway and the vdname_size is irrelevant.
> 
> FWIW: The ATTRIBUTE_NONNULL's only work if NULL is passed as an
> argument. If the argument being passed is NULL, then some bad stuff can
> happen.  Furthermore, if someone adds a check in the .c module for
> something marked this way, then Coverity gets upset.
> 
> John
> 

OK. I'll fix these!

Shi Lei

> > +
> >  int virNetDevGetMaster(const char *ifname, char **master)
> >     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
> >  
> > 
> 




More information about the libvir-list mailing list