[C/C++]Name des Netzwerkinterface ermitteln - Linux

Online-Skater

Erfahrenes Mitglied
Hi Leute,

wie im Thema angekündigt geht es darum zu einer gegebenen IP-Adresse das zugehörige logische Interface auszugeben z.b. eth0. Dazu habe ich folgende Funktion geschrieben:
C:
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <string>
#include <boost/asio/ip/address.hpp>
#include <iostream>

static const std::string getIFaceNameFromIpAddr(const boost::asio::ip::address& ipAddr)
{
    std::string retVal = "unknown";
    if (ipAddr.is_v4())
    {
        char          buf[1024] = {0};
        struct ifconf ifc = {0};
        struct ifreq *ifr = NULL;
        int           sck = 0;
        int           nInterfaces = 0;
        int           i = 0;

        /* Get a socket handle. */
        sck = socket(AF_INET, SOCK_DGRAM, 0);
        if (sck < 0)
        {
            perror("socket");
            return retVal;
        }

        /* Query available interfaces. */
        ifc.ifc_len = sizeof(buf);
        ifc.ifc_buf = buf;
        if (ioctl(sck, SIOCGIFCONF, &ifc) < 0)
        {
            perror("ioctl(SIOCGIFCONF)");
            return retVal;
        }

        /* Iterate through the list of interfaces. */
        ifr = ifc.ifc_req;
        nInterfaces = ifc.ifc_len / sizeof(struct ifreq);
        for(i = 0; i < nInterfaces; i++)
        {
            struct ifreq *item = &ifr[i];

            /* Search device name that have a given ip address */
            struct sockaddr *addr = &(item->ifr_addr);
            char ip[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &(((struct sockaddr_in *)addr)->sin_addr), ip, INET_ADDRSTRLEN);
            if (strcmp(ip , ipAddr.to_string().c_str()) == 0)
            {
                return retVal.assign(item->ifr_name);
            }
        }
    }
    else if (ipAddr.is_v6())
    {
        // must finding a better alternative ?
        std::string ipv6 = ipAddr.to_string();
        // count colons
        short anz = 0;
        size_t found = ipv6.find_first_of(':');
        while (found != std::string::npos)
        {
            anz++;
            found = ipv6.find_first_of(':', found+1);
        }
        // find double colons for replacing with zeros
        found = ipv6.find("::");
        if (found != std::string::npos)
        {
            short zeros = 32 - (ipv6.length() - anz);
            ipv6.insert(found, zeros, '0'); // insert zeros
        }
        // remove colons
        for (std::string::iterator it = ipv6.begin(); it != ipv6.end(); ++it)
        {
            if (*it == ':')
                ipv6.erase(it--);
        }

        // parse /proc/net/if_inet6
        FILE *fp;
        char str_addr[40];
        unsigned int plen, scope, dad_status, if_idx;
        char devname[IFNAMSIZ];
        char buf[256];

        if ((fp = fopen("/proc/net/if_inet6", "r")) != NULL)
        {
            while ((fgets(buf, sizeof(buf), fp) != NULL)
            && sscanf(buf, "%32s %02x %02x %02x %02x %15s\n", str_addr, &if_idx, &plen, &scope, &dad_status, devname) != EOF)
            {
                if (strcmp(str_addr, ipv6.c_str()) == 0)
                {
                    return retVal.assign(devname);
                }
            }
            fclose(fp);
        }
    }

    return retVal;
}
Wie ihr seht, soll die Funktion mit ipv4 und ipv6 Adressen umgehen können. Die Funktion funktioniert, jedoch empfinde ich den Ansatz bei ipv6 nicht zufriedenstellend. Leider lässt mein Kernel keine ioctl-Aufrufe bzgl. IPv6 zu, daher den Umweg mit dem Parsen. Sicher könnte man auch Programmausgaben wie ifconfig parsen, jedoch soll das später auf einem eingebettetem System laufen und da kann man nicht für Programmexistenzen garantieren. Daher suche ich einen Weg der möglichst sicher eine Antwort geben kann. Vielleicht hat einer von euch noch eine Idee bzw. Vorschläge den Code eleganter bzw. zuverlässiger zu machen.

Habt dank ;)
 
Schau doch mal in den Quelltext von "ifconfig" rein - vielleicht findest du da ja was du suchst?
Oder guck mit strace welche ioctrls ifconfig verwendet.
 
Danke für den Hinweis mit strace :)
Jetzt weiß ich schonmal das ifconfig den gleichen Ansatz verfolgt.
Code:
&> strace ifconfig eth0

open("/proc/net/if_inet6", O_RDONLY)    = 6
fstat64(6, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7636000
read(6, "00000000000000000000000000000001"..., 1024) = 108
read(6, "", 1024)                       = 0
write(1, "          inet6 addr: fe80::211:"..., 61          inet6 addr: fe80::211:25ff:fece:577a/64 Scope:Link
) = 61
read(6, "", 1024)                       = 0
close(6)                                = 0
Leider sieht man diese Befehle nicht in der ifconfig.c, wahrscheinlich führen irgendwelche Kernelfunktionen diese aus. Mich wundert ein wenig das die Adresse ohne Doppelpunkte nicht im read-Befehl steht. Wahrscheinlich wird intern auch noch eine Übersetzungsfunktion verwendet um die Doppelpunktnotation auszugeben.
Ich hoffe das die im Anhang stehende ifconfig.txt aus net-tools_1.60.orig.tar.gz die richtige ist, da ich absolut kein Zusammenhang zwischen der strace-Ausgabe und dem Quellcode sehe :(
Im Quellcode wird eine ipv6addr-Struktur verwendet welche aber irgendwie nicht ausgegeben wird...

Danke ersma :)
 

Anhänge

  • ifconfig.txt
    26 KB · Aufrufe: 28
Zuletzt bearbeitet:
Zurück