Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 9 Dec 2015 10:25:39 -0500
From:      Ken Moore <ken@pcbsd.org>
To:        Hiroki Sato <hrs@FreeBSD.org>
Cc:        freebsd-net@freebsd.org
Subject:   Re: IPv6 Address as text (C)
Message-ID:  <566847F3.7060900@pcbsd.org>
In-Reply-To: <20151209.233515.1592617248580059512.hrs@allbsd.org>
References:  <5668369F.9020309@pcbsd.org> <20151209.233515.1592617248580059512.hrs@allbsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On 12/09/2015 09:35, Hiroki Sato wrote:
> Ken Moore <ken@pcbsd.org> wrote
>    in <5668369F.9020309@pcbsd.org>:
>
> ke> Note: Please CC me on replies - I am not subscribed to this list.
> ke>
> ke> I am having a bit of trouble getting an accurate string representation
> ke> of the current IPv6 address for a given device using the C system
> ke> libraries and was wondering of somebody with more experience than me
> ke> might be able to spot the error...
> ke>
> ke> Background:
> ke> I have been working on a couple simple C/C++/Qt functions to return
> ke> printable forms of the current ipv4 and ipv6 addresses assigned to a
> ke> particular device, and while the ipv4 function works fine the ipv6
> ke> address is consistently wrong and almost always the same string -
> ke> making me think it is converting some internal error code to a string
> ke> ("::XXe2:ffff:ff7f:0" where the "X"s are the only two characters which
> ke> ever change).
> ke>
> ke> The two functions are nearly identical, and I think the error probably
> ke> comes from needing to use inet_ntop() for the ipv6 address because
> ke> there is no ipv6-compatible version of the inet_ntoa() function.
> ke> Do you have any thoughts or ideas about where the error might be
> ke> coming from or a better way to read off the ipv6 address as a string?
> ke>
> ke> Here are the two functions:
> ke> Note: "name" is the QString of the device name (wlan0, re0, other...),
> ke> and is an internal variable for the overall "NetDevice" class
> ke> [code]
> ke> //Fetch the IPv4 address and return it as a QString
> ke> QString NetDevice::ipAsString(){
> ke>    struct ifreq ifr;
> ke>    memset(&ifr, 0, sizeof(struct ifreq));
> ke>
> ke>    strncpy(ifr.ifr_name, name.toLocal8Bit(), IFNAMSIZ);
> ke>    int s = socket(PF_INET, SOCK_DGRAM, 0);
> ke>
> ke>    ioctl(s, SIOCGIFADDR, &ifr);
> ke>    struct in_addr in = ((sockaddr_in *) &ifr.ifr_addr)->sin_addr;
> ke>
> ke>    return QString( inet_ntoa(in) );
> ke> }
> ke>
> ke> //Fetch the IPv6 address and return it as a QString
> ke> QString NetDevice::ipv6AsString(){
> ke>    struct ifreq ifr;
> ke>    memset(&ifr, 0, sizeof(struct ifreq));
> ke>
> ke>    strncpy(ifr.ifr_name, name.toLocal8Bit(), IFNAMSIZ);
> ke>    int s = socket(PF_INET6, SOCK_DGRAM, 0);
> ke>
> ke>    ioctl(s, SIOCGIFADDR, &ifr);
>
>   Should this be SIOCGIFADDR_IN6 here?  You should check the error
>   code.
There does not appear to be any *_IN6 definitions in any of the 
/usr/include/[sys, net, netinet]/* include files (sys/sockio.h appears 
to hold the defines needed - nothing ipv6 related though).

>   Anyway, you should use getnameinfo() for IPv4 and IPv6 instead of
>   inet_ntop() and inet_ntoa() for this purpose.  It is an address
>   family independent API which accepts struct sockaddr directly like
>   this:
>
> ----
>    struct sockaddr *sa; /* input */
>    char hbuf[NI_MAXHOST];
>    int error;
>
>    error = getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL,
>        0, NI_NUMERICHOST);
>    if (error) {
>             errx(1, "getnameinfo: %s", gai_strerror(error));
>             /* NOTREACHED */
>    }
>    printf("host=%s\n", hbuf);
> ----
>
>   See getnameinfo(3) for more details.
>
> -- Hiroki

So I adjusted the function to use getnameinfo() as you recommended: and 
the reported error is "ai_family not supported".

[code]
QString NetDevice::ipv6AsString(){
    struct ifreq ifr;
    memset(&ifr, 0, sizeof(struct ifreq));

    strncpy(ifr.ifr_name, name.toLocal8Bit(), IFNAMSIZ);
    int s = socket(PF_INET6, SOCK_DGRAM, 0);

    ioctl(s, SIOCGIFADDR, &ifr);
    sockaddr *in = (&ifr.ifr_addr);
    char straddr[INET6_ADDRSTRLEN];

      int err = getnameinfo(in, in->sa_len, straddr, 
sizeof(straddr),NULL, 0, NI_NUMERICHOST);
      if(err){ return QString(gai_strerror(err)); }
      else{ return QString(straddr); }
}
[/code]


-- 
~~ Ken Moore ~~
PC-BSD/iXsystems




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?566847F3.7060900>