Under Construction

Abfragen der IP-Adresse für ein Netzwerk-Interface (SIOCGIFADDR)

Mit dem Kommando SIOCGIFADDR (/usr/include/sys/ioctl.h) lässt sich die IP-Adresse für ein Netzwerk-Interface ermitteln. Für die Informationen wird die Struktur ifreq aus /usr/include/net/if.h verwendet:

/*
* Interface request structure used for socket
* ioctl's.  All interface ioctl's must have parameter
* definitions which begin with ifr_name.  The
* remainder may be interface specific.
*/
struct  ifreq {
#ifndef IFNAMSIZ        /* Also in net_if.h */
#define IFNAMSIZ        16
#endif
        char    ifr_name[IFNAMSIZ];             /* if name, e.g. "en0" */
        union {
                struct  sockaddr ifru_addr;
                struct  sockaddr ifru_dstaddr;
                struct  sockaddr ifru_broadaddr;
                __ulong32_t     ifru_flags;
                int     ifru_metric;
                caddr_t ifru_data;
                u_short ifru_site6;
                __ulong32_t   ifru_mtu;
                int     ifru_obaudrate;
                uint64_t ifru_baudrate;
                int     ifru_checksum_offload[2];
        } ifr_ifru;
#define ifr_addr        ifr_ifru.ifru_addr      /* address */
#define ifr_dstaddr     ifr_ifru.ifru_dstaddr   /* other end of p-to-p link */
#define ifr_broadaddr   ifr_ifru.ifru_broadaddr /* broadcast address */
#define ifr_flags       ifr_ifru.ifru_flags     /* flags */
#define ifr_metric      ifr_ifru.ifru_metric    /* metric */
#define ifr_data        ifr_ifru.ifru_data      /* for use by interface */
#define ifr_site6       ifr_ifru.ifru_site6     /* IPv6 site index */
#define ifr_mtu         ifr_ifru.ifru_mtu       /* mtu of interface */
#define ifr_isno        ifr_ifru.ifru_data      /* pointer to if_netopts */
#define ifr_baudrate    ifr_ifru.ifru_baudrate  /* baudrate of interface */
#define ifr_obaudrate   ifr_ifru.ifru_obaudrate /* OLD baudrate of interface */
#define ifr_checksum_offload    ifr_ifru.ifru_checksum_offload[1]  /* checksum offload active or not */
};

Der Name des Netzwerk-Interfaces muss in ifr_name vor dem Aufruf von ioctl(2) abgesspeichert werden. Der ioctl(2) liefert dann die IP-Addresse über ifru_addr (Union ifr_ifru) in der Struktur sockaddr zurück. Die generische sockaddr Struktur ist in /usr/include/sys/socket.h und /usr/include/netinet/in.h definiert:

struct sockaddr {
        uchar_t         sa_len;         /* total length */
        sa_family_t     sa_family;      /* address family */
        char            sa_data[14];    /* actually longer; address value */
};

Für den Fall einer IP-Adresse ist die Adreß-Familie AF_INET und die zugehörige spezifische Socket-Adreß-Struktur ist sockaddr_in (definiert in /usr/include/netinet/in.h):

/*
* Socket address, internet style.
*/
struct sockaddr_in {
        uchar_t        sin_len;
        sa_family_t    sin_family;
        in_port_t      sin_port;
        struct in_addr sin_addr;
        uchar_t        sin_zero[8];
};

Die IP-Adresse ist dann im Feld sin_addr abgespeichert.

Das folgende kleine Beispiel-Programm demonstriert die Verwendung von SIOCGIFADDR:

$ cat get_ip.c
/*
* Copyright (c) 2021 by PowerCampus 01 GmbH
*/
#include <sys/ioctl.h>     // SIOCGIFADDR
#include <sys/socket.h>    // socket()
#include <netinet/in.h>    // sockaddr, sockaddr_in
#include <net/if.h>        // ifreq
#include <arpa/inet.h>     // inet_ntoa()
#include <errno.h>         // errno, perror()
#include <unistd.h>        // ioctl(), close()
#include <stdio.h>         // fprintf()
#include <stdlib.h>        // exit()
#include <string.h>        //strncpy()

int main( int argc , char** argv )
{
        int sd;
        int ret;
        sockaddr_in* addr;

        // 1. Provide buffer for the request.
        struct ifreq req;

      // 2. Store interface name in ifr_name.
        strncpy(req.ifr_name,argv[1],IFNAMSIZ);

        // 3. Create datagram socket for address family Inet.
        sd = socket(AF_INET,SOCK_DGRAM,0);
        if ( sd == -1 )
        {
                perror("get_ip");
                exit(errno);
        }

        // 4. Call ioctl with command SIOCGIFADDR.
        ret = ioctl(sd,SIOCGIFADDR,&req);
        if ( ret == -1 )
        {
                perror("get_ip");
                exit(errno);
        }

        // 5. (optional) Ensure that returned socket address is for Inet.
        if ( req.ifr_addr.sa_family != AF_INET )
        {
                fprintf(stderr,"get_ip: unexpected address family\n");
                exit(EAFNOSUPPORT);  // may be there is a better error number for this case
        }

        // 6. Print IP-Address.
        addr = (struct sockaddr_in*)&(req.ifr_addr);
        printf("IP: %s\n",inet_ntoa(addr->sin_addr));

        // 7. Close socket descriptor
        close(sd);

        return 0;
}

$

Das Programm erwartet beim Aufruf den Namen eines Netzwerk-Interfaces als Argument und gibt dann für dieses Interface die IP-Adresse aus.

Das Programm kann z.B. mit dem GNU C-Compiler übersetzt werden:

$ gcc -o get_ip get_ip.c
$

Ein kleiner Test mit den Netzwerk-Interfaces lo0 und en0 zeigt, dass das Programm funktioniert:

$ get_ip lo0
IP: 127.0.0.1
$ get_ip en0
IP: 192.168.170.250
$