Wie kann ich die IP-Adresse für Schnittstellen in C erfahren?

Lesezeit: 8 Minuten

Benutzer-Avatar
gvalero87

Nehmen wir an, ich führe ein Programm namens IpAddresses.c aus. Ich möchte, dass dieses Programm alle IP-Adressen erhält, die dieses Gerät für jede Schnittstelle hat. So wie ifconfig. Wie kann ich das machen?

Ich weiß nicht viel darüber ioctlaber ich habe gelesen, dass es mir helfen könnte.

Benutzer-Avatar
chrisaycock

Benutz einfach getifaddrs(). Hier ist ein Beispiel:

#include <arpa/inet.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <stdio.h>

int main ()
{
    struct ifaddrs *ifap, *ifa;
    struct sockaddr_in *sa;
    char *addr;

    getifaddrs (&ifap);
    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr && ifa->ifa_addr->sa_family==AF_INET) {
            sa = (struct sockaddr_in *) ifa->ifa_addr;
            addr = inet_ntoa(sa->sin_addr);
            printf("Interface: %s\tAddress: %s\n", ifa->ifa_name, addr);
        }
    }

    freeifaddrs(ifap);
    return 0;
}

Und hier ist die Ausgabe, die ich auf meiner Maschine bekomme:

Interface: lo   Address: 127.0.0.1
Interface: eth0 Address: 69.72.234.7
Interface: eth0:1       Address: 10.207.9.3

  • warum hast du 2 eth0? Übrigens, danke für deine Antwort

    – gvalero87

    10. November 2010 um 0:32 Uhr


  • @gvalero87 Das erste eth0 weist die Netzwerkkarte an, über das Internet zu kommunizieren. Dieses zweite eth0 kommuniziert über eine private Verbindung (optische Leitung) mit einem Dritten. Es ist eine Einstellung in der Routing-Tabelle, die der Netzwerkadministrator zusammengestellt hat.

    – Chrisaycock

    10. November 2010 um 1:33 Uhr

  • Siehe die Antwort von Luiz Normando unten: Ihr Code kann auf einigen Maschinen abstürzen, da ifa->ifa_addr für einige der Schnittstellen NULL sein kann.

    – Lanzelot

    14. November 2019 um 11:54 Uhr

  • @Lanzelot Ich habe meine Antwort mit dem Patch von Luiz Normando aktualisiert.

    – Chrisaycock

    14. November 2019 um 18:43 Uhr

  • Ich habe einen Fehlerprüfcode hinzugefügt: eine neue Kopfzeile hinzugefügt “#include <stdlib.h>” unter dem stdio.h Zeile und fügte diese Zeile hinzu “if (getifaddrs(&ifap) == -1) { perror("getifaddrs"); exit(1); }“durch Ersetzen dieser Zeile”getifaddrs (&ifap);” . Ohne vorherige (2-zeilige) Änderungen, wenn “ifa_addr” einen NULL-Zeiger hat, dann erzeugt es einen Segmentierungsfehler . Die erwähnten Änderungen sind inspiriert von Michael-Hamptons Code, der hier gezeigt wird: stackoverflow.com/a/27433320/3553808 . Dieser Code funktionierte gut auf macOS (10.15.x/Catalina).

    – beiErik

    9. September 2020 um 5:03 Uhr

Hier sind einige Linux Beispielcode, der Ihnen helfen könnte.

#include <stdio.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>

#define INT_TO_ADDR(_addr) \
(_addr & 0xFF), \
(_addr >> 8 & 0xFF), \
(_addr >> 16 & 0xFF), \
(_addr >> 24 & 0xFF)

int main()
{
    struct ifconf ifc;
    struct ifreq ifr[10];
    int sd, ifc_num, addr, bcast, mask, network, i;

    /* Create a socket so we can use ioctl on the file 
     * descriptor to retrieve the interface info. 
     */

    sd = socket(PF_INET, SOCK_DGRAM, 0);
    if (sd > 0)
    {
        ifc.ifc_len = sizeof(ifr);
        ifc.ifc_ifcu.ifcu_buf = (caddr_t)ifr;

        if (ioctl(sd, SIOCGIFCONF, &ifc) == 0)
        {
            ifc_num = ifc.ifc_len / sizeof(struct ifreq);
            printf("%d interfaces found\n", ifc_num);

            for (i = 0; i < ifc_num; ++i)
            {
                if (ifr[i].ifr_addr.sa_family != AF_INET)
                {
                    continue;
                }

                /* display the interface name */
                printf("%d) interface: %s\n", i+1, ifr[i].ifr_name);

                /* Retrieve the IP address, broadcast address, and subnet mask. */
                if (ioctl(sd, SIOCGIFADDR, &ifr[i]) == 0)
                {
                    addr = ((struct sockaddr_in *)(&ifr[i].ifr_addr))->sin_addr.s_addr;
                    printf("%d) address: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(addr));
                }
                if (ioctl(sd, SIOCGIFBRDADDR, &ifr[i]) == 0)
                {
                    bcast = ((struct sockaddr_in *)(&ifr[i].ifr_broadaddr))->sin_addr.s_addr;
                    printf("%d) broadcast: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(bcast));
                }
                if (ioctl(sd, SIOCGIFNETMASK, &ifr[i]) == 0)
                {
                    mask = ((struct sockaddr_in *)(&ifr[i].ifr_netmask))->sin_addr.s_addr;
                    printf("%d) netmask: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(mask));
                }                

                /* Compute the current network value from the address and netmask. */
                network = addr & mask;
                printf("%d) network: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(network));
            }                      
        }

        close(sd);
    }

    return 0;
}

  • Danke für eine ioctl-Alternative. Allerdings weiß ich nie, wann ich es verwenden sollte ioctl und wann getifaddrs.

    – Youda008

    27. Dezember 2015 um 9:25 Uhr

  • Ich glaube ioctl etwas tragbarer sein, auch wenn es keinem einzelnen Standard entspricht. Es wird von den meisten Unix- und Unix-ähnlichen Systemen und dem unterstützt ioctl Der Funktionsaufruf erschien erstmals in Version 7 von AT&T UNIX. Ich glaube getifaddrs wird in BSD und Linux unterstützt und erschien erstmals in glibc 2.3.

    – jschmier

    4. Januar 2016 um 15:40 Uhr


  • Dies funktioniert auch auf Android, die das nicht haben ifaddrs Sache aus irgendeinem Grund, aber nicht unter OS X. Hier ist ein Beispiel das geht bei beiden.

    – Grischka

    30. Oktober 2016 um 3:22 Uhr

  • Normal verwenden htonl()/ntohl() statt Sitte INT_TO_ADDR Makro.

    – Matthias

    10. Juli 2018 um 16:29 Uhr


Die Lösung mit getifaddrs() ist toll. Ich würde nur eine Verbesserung vorschlagen:

--- chrisaycock
+++ normando
@@ -11,7 +11,7 @@

     getifaddrs (&ifap);
     for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
-        if (ifa->ifa_addr->sa_family==AF_INET) {
+        if (ifa->ifa_addr && ifa->ifa_addr->sa_family==AF_INET) {
             sa = (struct sockaddr_in *) ifa->ifa_addr;
             addr = inet_ntoa(sa->sin_addr);
             printf("Interface: %s\tAddress: %s\n", ifa->ifa_name, addr);

Nur weil ich selbst einen Segmentation Fault bekommen habe.

  • Wir haben auch einen Segfault mit dem ursprünglichen Code erhalten. Laut der Manpage von getifaddrs kann das ifa_addr-Member für einige der Schnittstellen NULL sein …

    – Lanzelot

    14. November 2019 um 11:50 Uhr

Benutzer-Avatar
ninjalj

Siehe diese andere Stack Overflow-Frage, Aufzählung jeder IP-Adresse, die Netzwerkschnittstellen zugewiesen ist.

Zusammenfassend können Sie verwenden:

  • ioctl(SIOCGIFCONF) -> das traditionelle ioctl
  • getifaddrs() -> von BSDi, jetzt auch auf Linux und den BSD’s.
  • RTNETLINK (Linux)

Benutzer-Avatar
macgarden

Du könntest so etwas versuchen:

struct ifreq ifr[MAX_INTERFACES];
struct ifconf ifc;
memset(ifr, 0, sizeof(ifr));
ifc.ifc_len = sizeof(ifr);
ifc.ifc_req = ifr;

// Get the list of interfaces
if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) {
    fprintf(stderr, "ioctl SIOCGIFCONF failed: %d", errno);
}

struct ifreq *ifr_iterator = ifc.ireq;
int i = 0;
size_t len;
while (i < ifc.ifc_len) {
   /* DO STUFF */
   // Maybe some more filtering based on SIOCGIFFLAGS 
   // Your code
   // Use ifr_iterator-> ...

   len = IFNAMSIZ + ifr_iterator->ifr_addr.sa_len;
   ifr_iterator = (struct ifreq *)((char *)ifr_iterator + len);
   i += len;
}

  • Beachten Sie, dass dieser Code auf BSD fehlschlägt, wo die Größe der ifreq-Struktur variabel ist. Du kannst nicht einfach davon ausgehen ifc.ifc_len/sizeof(struct ifreq) gibt Ihnen die Anzahl der Schnittstellen. Stattdessen müssen Sie die Elemente in der Liste wie folgt durchlaufen: struct ifreq *ifr_iterator = ifc.ireq; size_t len; while (i < ifc.ifc_len) { /* DO STUFF */ len = IFNAMSIZ + ifr_iterator->ifr_addr.sa_len; ifr_iterator = (struct ifreq *)((char *)ifr_iterator + len); i += len; }

    – Joakim

    24. September 2012 um 9:20 Uhr


  • @Joakim das bedeutet das #define ifc_req ifc_ifcu.ifcu_req /* array of structures ret'd */ ist technisch gesehen kein Array, weil die Größe variiert? Es scheint, dass 5.9 mehr Sachen hinzugefügt hatte. Auch der ifconfig.c-Code schien SIOCGIFCONF nicht zu haben

    – GorillaApe

    15. Juli 2016 um 20:34 Uhr

  • @GorillaApe Auf einem System könnte es ein Array sein, auf einem anderen nicht. Es gibt verschiedene Implementierungen ist mein Punkt. Unter Linux ist dies ein Array, bei dem jedes Element eine feste Größe hat, aber unter BSD kann die Größe jedes Elements variieren, daher ist es richtig, immer das Längenfeld zu verwenden. Wenn Sie tragbaren Code wollen, ist das.

    – Joakim

    27. August 2016 um 11:19 Uhr

Benutzer-Avatar
Steve Townsend

Probier das aus (Windows-spezifisch) IP-Helper-API – Zum Glück braucht man das nicht ioctl dafür unter Windows.

  • Beachten Sie, dass dieser Code auf BSD fehlschlägt, wo die Größe der ifreq-Struktur variabel ist. Du kannst nicht einfach davon ausgehen ifc.ifc_len/sizeof(struct ifreq) gibt Ihnen die Anzahl der Schnittstellen. Stattdessen müssen Sie die Elemente in der Liste wie folgt durchlaufen: struct ifreq *ifr_iterator = ifc.ireq; size_t len; while (i < ifc.ifc_len) { /* DO STUFF */ len = IFNAMSIZ + ifr_iterator->ifr_addr.sa_len; ifr_iterator = (struct ifreq *)((char *)ifr_iterator + len); i += len; }

    – Joakim

    24. September 2012 um 9:20 Uhr


  • @Joakim das bedeutet das #define ifc_req ifc_ifcu.ifcu_req /* array of structures ret'd */ ist technisch gesehen kein Array, weil die Größe variiert? Es scheint, dass 5.9 mehr Sachen hinzugefügt hatte. Auch der ifconfig.c-Code schien SIOCGIFCONF nicht zu haben

    – GorillaApe

    15. Juli 2016 um 20:34 Uhr

  • @GorillaApe Auf einem System könnte es ein Array sein, auf einem anderen nicht. Es gibt verschiedene Implementierungen ist mein Punkt. Unter Linux ist dies ein Array, bei dem jedes Element eine feste Größe hat, aber unter BSD kann die Größe jedes Elements variieren, daher ist es richtig, immer das Längenfeld zu verwenden. Wenn Sie tragbaren Code wollen, ist das.

    – Joakim

    27. August 2016 um 11:19 Uhr

Falls Sie auch IP6-Adressen benötigen, können Sie die akzeptierte Antwort wie folgt erweitern:

#include <arpa/inet.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <netdb.h>

int main() {
    struct ifaddrs *ifap;
    getifaddrs(&ifap);
    for (struct ifaddrs *ifa = ifap; ifa; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr && (ifa->ifa_addr->sa_family == AF_INET6)) {
            char addr[INET6_ADDRSTRLEN];
            getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), addr, sizeof(addr), NULL, 0, NI_NUMERICHOST);
            printf("Interface: %s\tAddress: %s\n", ifa->ifa_name, addr);
        } else if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
            struct sockaddr_in *sa = (struct sockaddr_in *)ifa->ifa_addr;
            char *addr = inet_ntoa(sa->sin_addr);
            printf("Interface: %s\tAddress: %s\n", ifa->ifa_name, addr);
        }
    }
    freeifaddrs(ifap);
    return 0;
}

1141260cookie-checkWie kann ich die IP-Adresse für Schnittstellen in C erfahren?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy