Re: [PATCH 1/4] util: introduce a new helper for get interface IPv6 address

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 02/25/2015 12:12 AM, John Ferlan wrote:

On 02/13/2015 02:17 AM, Luyao Huang wrote:
Introduce a new function to help to get interface IPv6 address.

s/Introduce a new function to help to/Introduce virNetDevGetIPv6Address/


Signed-off-by: Luyao Huang <lhuang@xxxxxxxxxx>
---
  src/libvirt_private.syms |  1 +
  src/util/virnetdev.c     | 70 ++++++++++++++++++++++++++++++++++++++++++++++++
  src/util/virnetdev.h     |  2 ++
  3 files changed, 73 insertions(+)

Hmm... maybe rather than introducing a new IPv6 specific routine and
forcing the caller to handle the logic of knowing how/whether to return
an IPv4 or IPv6 address...

Why not change the existing GetIPv4Address into a "shim"
virNetDevGetIPAddress which then makes the decisions regarding returning
only one family or allowing a failed fetch of IPv4 to use the IPv6
routine...

This way you hide the details.  Your first patch would just change the
IPv4 into an GetIPAddress API and that would just call the now
local/static IPv4 API. You won't have a #if #else #endif for the new API
- it would return 0 or -1.

Check out how "safezero" has multiple ways in order to zero out a file
based on what's present. I suspect your new API could follow that
methodology.

In the long run since getifaddrs() can return an IPv4 or IPv6 address it
could be theoretically called first. If it returns nothing, fall back to
the IPv4 API

Your new API could be something like:

virNetDevGetIPAddress(const char *ifname,
                       bool want_ipv6,
                       virSocketAddrPtr addr)

{
     int ret;

     memset(addr, 0, sizeof(*addr));
     addr->data.stor.ss_family = AF_UNSPEC;

     ret = virNetDevGetIfaddrsAddress(ifname, want_ipv6, addr);
     if (ret != -2)
         return ret;

     if (!want_ipv6)
         return virNetDevGetIPv4Address(ifname, addr);

     return -1;
}


The virNetDevGetIfaddrsAddress would follow safezero_posix_fallocate
returning -2 in the #else of #if defined(HAVE_GETIFADDRS).  The logic in
the function would return the first found address of IPv6 if that's
desired or IPv4 otherwise.

Good idea! I just thought add a new functions for ipv6 address but forgot getifaddrs() can also get the IPv4 address :)

Thanks for pointing out and i will rework this patch in next version.
The virNetDevGetIPv4Address() wouldn't need the two stolen lines to
clear addr, but would otherwise function as it does today.

Hopefully this makes sense - you'll be adding more patches, but I think
in the long run based on the following patches it will make it "easier"
on the caller to just get "an" address and force it to be IPv6 only if
that's what's desired.

Yes, i had thought about this before, maybe we can add a new function to get(or chose) the IPv6 address we desired, because one interface can have many IPv6 address and sometimes we just need one of them.
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 645aef1..f60911c 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1659,6 +1659,7 @@ virNetDevDelMulti;
  virNetDevExists;
  virNetDevGetIndex;
  virNetDevGetIPv4Address;
+virNetDevGetIPv6Address;
  virNetDevGetLinkInfo;
  virNetDevGetMAC;
  virNetDevGetMTU;

diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index 2a0eb43..c1a588e 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -33,6 +33,10 @@
  #include "virstring.h"
  #include "virutil.h"
+#if defined(HAVE_GETIFADDRS)
+# include <ifaddrs.h>
+#endif
+
  #include <sys/ioctl.h>
  #include <net/if.h>
  #include <fcntl.h>
@@ -1432,6 +1436,72 @@ int virNetDevGetIPv4Address(const char *ifname ATTRIBUTE_UNUSED,
#endif /* ! SIOCGIFADDR */ +/**
+ *virNetDevGetIPv6Address:
+ *@ifname: name of the interface whose IP address we want
s/IP/IPv6

thanks, but seems this function will be removed (or renamed) in next version :)
+ *@addr: filled with the IPv6 address
+ *
+ *This function gets the IPv6 address for the interface @ifname
+ *and stores it in @addr
+ *
+ *Returns 0 on success, -1 on failure.
+ */
Each of the lines above needs s/*/* /

Sorry for my careless.
+#if defined(HAVE_GETIFADDRS)
+int virNetDevGetIPv6Address(const char *ifname,
+                            virSocketAddrPtr addr)
+{
+    struct ifaddrs *ifap, *ifa;
+    int ret = -1;
+    bool found_one = false;
+
+    if (getifaddrs(&ifap) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Could not get interface list"));
s/list$/list for '%s'/  and of course an ifname parameter ...

Hmm, seems it is not the interface issue when getifaddrs cannot get interface list, however it will give a more nice error. Thanks your advise and i will change it in next version.
+        return -1;
+    }
+
+    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+        if (STRNEQ(ifa->ifa_name, ifname))
+            continue;
+        found_one = true;
+        if (ifa->ifa_addr->sa_family == AF_INET6)
+            break;
 From the getifaddrs(3) man page:

"The ifa_addr field points to a structure containing the interface
address. (The sa_family subfield should be consulted to determine the
format of the address structure.)  This field may contain a null pointer."

So you need to check that here before de-referencing a NULL pointer

Oh, thanks for pointing out this.

Also I'm assuming these API's don't care how usable the address is, just
that it's the first one found. See 'checkProtocols()' for what I mean
about usable - there's an additional getaddrinfo() call there that
handles a special IPv6 case (I forget all the details, but the ::1 has
some special characteristics...).
Thanks for your remind, i checked checkProtocols() and i guess your mean we can use getaddrinfo() to make sure the address we get can be used for a socket bind ?

And i also thought about another issue after your reminding: An interface can have more than one IPv6 address. But i still couldn't find a good way until now to chose which IPv6 address if we find more than one IPv6 address in one interface (maybe users should use "listen type=address" instead of "listen type=network" in this case ;)).

There are some special addresses anddefault address selection for IPv6 address, i don't know if it is a good idea to guess which address is the caller really desired if we get some IPv6 address in one interface.

+    }
+
+    if (!ifa) {
+        if (found_one)
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Interface '%s' do not have a IPv6 address"),
s/do/does/

+                           ifname);
+        else
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Interface '%s' not found"),
+                           ifname);
+        goto cleanup;
+    }
+
+    addr->data.stor.ss_family = AF_INET6;
+    addr->len = sizeof(addr->data.inet6);
+    memcpy(&addr->data.inet6, ifa->ifa_addr, addr->len);

I'd also move this chunk inside that loop above replacing the "break;"
with this code plus of course the following ret = 0; and a goto cleanup.

Allowing then the fall of the end of the loop code to just check if
(found_one) or not (eg, no need for "if (!ifa)"...

Leaving the following - including the capability to get either IPv6 or
IPv4 address depending upon what's desired/required:

     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
         if (!ifa->ifa_addr ||
             STRNEQ(ifa->ifa_name, ifname))
             continue;
         found_one = true;
         if (want_ipv6 && ifa->ifa_addr->sa_family != AF_INET6)
             continue;

         /* Found an address to return */
         addr->data.stor.ss_family = ifa->ifa_addr->sa_family;
         if (ifa->ifa_addr->sa_family == AF_INET6)
             addr->len = sizeof(addr->data.inet6);
             memcpy(&addr->data.inet6, ifa->ifa_addr, addr->len);
         } else {
             addr->len = sizeof(addr->data.inet4);
             memcpy(&addr->data.inet4, ifa->ifa_addr, addr->len);
         }
         ret = 0;
         goto cleanup;
     }

     if (found_one)
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("Interface '%s' does not have an %s address"),
                        ifname, want_ipv6 ? "IPv6" : "IPv4");
     else
         virReportError(VIR_ERR_INTERNAL_ERROR,
                        _("Interface '%s' not found"),
                         ifname);

  cleanup:

Thanks for your code and i have changed some place, and now :

+    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+        if (!ifa->ifa_addr ||
+            STRNEQ(ifa->ifa_name, ifname)) {
+            continue;
+        }
+        found_one = true;
+        if (want_ipv6 && ifa->ifa_addr->sa_family == AF_INET6) {
+            addr->len = sizeof(addr->data.inet6);
+            memcpy(&addr->data.inet6, ifa->ifa_addr, addr->len);
+        } else if (!want_ipv6 && ifa->ifa_addr->sa_family == AF_INET) {
+            addr->len = sizeof(addr->data.inet4);
+            memcpy(&addr->data.inet4, ifa->ifa_addr, addr->len);
+        } else {
+            continue;
+        }
+        addr->data.stor.ss_family = ifa->ifa_addr->sa_family;
+        ret = 0;
+        goto cleanup;
+    }
+
+    if (found_one)
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Interface '%s' does not have a %s address"),
+                       ifname, want_ipv6 ? "IPv6" : "IPv4");
+    else
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Interface '%s' not found"),
+                       ifname);
+
+ cleanup:


Luyao

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]