Rufen Sie die IP-Adresse des Geräts ab

94

Diese Frage entspricht fast der zuvor gestellten Frage: IP-Adresse des lokalen Computers abrufen - Frage. Ich muss jedoch die IP-Adresse (n) eines Linux-Computers finden .

Also: Wie erkenne ich - programmgesteuert in C ++ - die IP-Adressen des Linux-Servers, auf dem meine Anwendung ausgeführt wird? Die Server haben mindestens zwei IP-Adressen und ich benötige eine bestimmte (die in einem bestimmten Netzwerk (die öffentliche)).

Ich bin mir sicher, dass es dafür eine einfache Funktion gibt - aber wo?


Um die Dinge etwas klarer zu machen:

  • Der Server wird offensichtlich den "localhost" haben: 127.0.0.1
  • Der Server hat eine interne (Verwaltungs-) IP-Adresse: 172.16.xx.
  • Der Server hat eine externe (öffentliche) IP-Adresse: 80.190.xx.

Ich muss die externe IP-Adresse finden, um meine Anwendung daran zu binden. Natürlich kann ich mich auch an INADDR_ANY binden (und genau das mache ich gerade). Ich würde es jedoch vorziehen, die öffentliche Adresse zu ermitteln.

BlaM
quelle
3
Warum das Popen von ifconfig markieren? Es ist wirklich das Richtige. Bibliotheken ändern sich, aber ifconfig und popen sind immer da.
Erik Aronesty
3
Nein, ifconfig, routeetc. sind für den veralteten ipBefehl. Jetzt sollten Sie das stattdessen verwenden.
Anders
ifconfig arbeitet heute noch an mehr Architekturen als jeder andere Ansatz. Ändern Sie es daher in den Befehl ip.when /, falls zutreffend. Popen ist immer noch die bessere Lösung.
Erik Aronesty

Antworten:

104

Ich fand die ioctl-Lösung unter os x problematisch (die POSIX-kompatibel ist und daher Linux ähneln sollte). Mit getifaddress () können Sie das Gleiche problemlos tun. Für OS x 10.5 funktioniert es jedoch einwandfrei und sollte unten gleich sein.

Ich habe unten ein kurzes Beispiel erstellt, in dem die gesamte IPv4-Adresse des Geräts gedruckt wird (Sie sollten auch überprüfen, ob getifaddrs erfolgreich war, dh 0 zurückgibt).

Ich habe es aktualisiert, um auch IPv6-Adressen anzuzeigen.

#include <stdio.h>      
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h> 
#include <string.h> 
#include <arpa/inet.h>

int main (int argc, const char * argv[]) {
    struct ifaddrs * ifAddrStruct=NULL;
    struct ifaddrs * ifa=NULL;
    void * tmpAddrPtr=NULL;

    getifaddrs(&ifAddrStruct);

    for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
        if (!ifa->ifa_addr) {
            continue;
        }
        if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4
            // is a valid IP4 Address
            tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
            printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); 
        } else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
            // is a valid IP6 Address
            tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
            char addressBuffer[INET6_ADDRSTRLEN];
            inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
            printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer); 
        } 
    }
    if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
    return 0;
}
Zwölf47
quelle
7
+1 und danke, dass du diesen netten Ansatz anstelle dieser langweiligen Ioctl's gebracht hast! Ihr Umgang mit IP6 ist jedoch immer noch falsch - Sie sollten in sockaddr_in6 umwandeln, dh so etwas wietmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
Andrey
2
@ Andy, danke. Ich habe meine Antwort aktualisiert (hatte aber noch keine Gelegenheit, sie zu testen)
Twelve47
2
gibt lound wlan0Schnittstellen in meinem Fall. Was ich benötige, ist wlan0oder eth0welche Internetverbindung hat. strcmpAlso , sollte ich verwenden oder es ist ein besserer Weg?
Sudip Bhattarai
28
  1. Erstellen Sie einen Socket.
  2. Ausführen ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

Lesen Sie /usr/include/linux/if.hfür Informationen zu ifconfund ifreqStrukturen. Dies sollte Ihnen die IP-Adresse jeder Schnittstelle im System geben. Lesen Sie auch /usr/include/linux/sockios.hfür zusätzliche ioctls.

Steve Baker
quelle
Ich hatte den Eindruck, dass es Ihnen nur die Adresse der Schnittstelle gibt, an die es bindet.
Matt Joiner
@MattJoiner Nein, werfen Sie einen Blick auf die Manpage. SIOCGIFCONF gibt Informationen zu allen Schnittstellen zurück. Ein Teil der hier zitierten Manpage: "Gibt eine Liste der Schnittstellenadressen (Transportschicht) zurück. Dies bedeutet derzeit aus Kompatibilitätsgründen nur Adressen der AF_INET (IPv4) -Familie. Der Benutzer übergibt eine ifconf-Struktur als Argument an das ioctl. Sie enthält a Zeiger auf ein Array von ifreq-Strukturen in ifc_req und seine Länge in Bytes in ifc_len. Der Kernel füllt die ifreqs mit allen aktuellen L3-Schnittstellenadressen, die ausgeführt werden "
Stéphane
25

Ich mag die Antwort von jjvainio . Wie Zan Lnyx sagt, verwendet es die lokale Routing-Tabelle, um die IP-Adresse der Ethernet-Schnittstelle zu ermitteln, die für eine Verbindung zu einem bestimmten externen Host verwendet werden würde. Wenn Sie einen verbundenen UDP-Socket verwenden, können Sie die Informationen abrufen, ohne tatsächlich Pakete zu senden. Der Ansatz erfordert, dass Sie einen bestimmten externen Host auswählen. Meistens sollte jede bekannte öffentliche IP den Trick tun. Ich mag die öffentliche DNS-Serveradresse 8.8.8.8 von Google für diesen Zweck, aber es kann vorkommen, dass Sie eine andere externe Host-IP auswählen möchten. Hier ist ein Code, der den vollständigen Ansatz veranschaulicht.

void GetPrimaryIp(char* buffer, size_t buflen) 
{
    assert(buflen >= 16);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sock != -1);

    const char* kGoogleDnsIp = "8.8.8.8";
    uint16_t kDnsPort = 53;
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp);
    serv.sin_port = htons(kDnsPort);

    int err = connect(sock, (const sockaddr*) &serv, sizeof(serv));
    assert(err != -1);

    sockaddr_in name;
    socklen_t namelen = sizeof(name);
    err = getsockname(sock, (sockaddr*) &name, &namelen);
    assert(err != -1);

    const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, buflen);
    assert(p);

    close(sock);
}
4dan
quelle
2
das gibt mir private ip 127. *. *. *. Vielleicht, weil ich hinter NAT stehe?
Eugene
14

Wie Sie herausgefunden haben, gibt es keine einzige "lokale IP-Adresse". Hier erfahren Sie, welche lokale Adresse an einen bestimmten Host gesendet werden kann.

  1. Erstellen Sie einen UDP-Socket
  2. Verbinden Sie den Socket mit einer externen Adresse (dem Host, der schließlich die lokale Adresse erhält).
  3. Verwenden Sie getockockname, um die lokale Adresse abzurufen
jjvainio
quelle
Ich habe es noch nie so gemacht, aber ich mag es. Es verwendet die OS-Routing-Tabelle automatisch und ist viel einfacher als das Scannen der Schnittstellenliste.
Zan Lynx
5
Dies setzt voraus, dass Sie wissen, wie eine externe Adresse lautet. Dies ist in Rechenzentren häufig nicht der Fall.
Christopher
Sie können immer drei ausprobieren, die verschiedenen Kontinenten zugeordnet sind (IANA macht dies einfach) und die besten zwei von drei akzeptieren.
Joshua
9

Dies hat den Vorteil, dass Sie an vielen Unix-Varianten arbeiten können ... und Sie können es trivial ändern, um an jedem Betriebssystem zu arbeiten. Alle oben genannten Lösungen führen zu Compilerfehlern in Abhängigkeit von der Mondphase. Im Moment gibt es eine gute POSIX-Methode ... verwenden Sie diese nicht (zu dem Zeitpunkt, als dies geschrieben wurde, war dies nicht der Fall).

// ifconfig | perl -ne 'print "$1\n" if /inet addr:([\d.]+)/'

#include <stdlib.h>

int main() {
        setenv("LANG","C",1);
        FILE * fp = popen("ifconfig", "r");
        if (fp) {
                char *p=NULL, *e; size_t n;
                while ((getline(&p, &n, fp) > 0) && p) {
                   if (p = strstr(p, "inet ")) {
                        p+=5;
                        if (p = strchr(p, ':')) {
                            ++p;
                            if (e = strchr(p, ' ')) {
                                 *e='\0';
                                 printf("%s\n", p);
                            }
                        }
                   }
              }
        }
        pclose(fp);
        return 0;
}
Erik Aronesty
quelle
Vielen Dank für diese Lösung, funktioniert hervorragend für mich und ist sehr praktisch in vielen Situationen, wenn Sie nur einige Daten von cl-Programmen erhalten
möchten
Obwohl ifconfig die erforderlichen Daten ausgibt, würde ich dringend davon abraten, ein Programm auf diese Weise zu verwenden. Es ist viel besser, tatsächlich in die Quelle von ifconfig zu gelangen und die Funktionalität zu erhalten, als sie auszuführen und ihre Ausgabe zu analysieren.
Zaur Nasibov
1
@MartinMeeser: Sprachneutral gemacht, indem "inet" und dann ":" separat gesucht wurden.
Erik Aronesty
1
Dann haben Sie andere Sprachen, die nicht einmal auf Latein basieren. Die Lösung besteht darin, keine Befehlstools zu verwenden, da diese wie veraltet werden ifconfig. Sie sollten ipjetzt verwenden. Die richtige Methode zum Generieren einer sprachneutralen Ausgabe besteht darin, die LANGUmgebungsvariable Cwie folgt LANG=C ls -l
Anders
1
Vielen Dank. Es ist eine einfache und großartige Lösung!
Ahmad Afkandeh
5

Ich glaube nicht, dass es eine endgültige richtige Antwort auf Ihre Frage gibt. Stattdessen gibt es eine Vielzahl von Möglichkeiten, wie Sie sich Ihren Wünschen nähern können. Daher werde ich einige Hinweise geben, wie es geht.

Wenn eine Maschine mehr als 2 Schnittstellen hat ( lozählt als eine), haben Sie Probleme, die richtige Schnittstelle einfach automatisch zu erkennen. Hier sind einige Rezepte, wie es geht.

Das Problem besteht beispielsweise darin, dass sich Hosts in einer DMZ hinter einer NAT-Firewall befinden, die die öffentliche IP in eine private IP ändert und die Anforderungen weiterleitet. Ihr Computer verfügt möglicherweise über 10 Schnittstellen, aber nur eine entspricht der öffentlichen.

Selbst die automatische Erkennung funktioniert nicht, wenn Sie sich auf Double-NAT befinden, wo Ihre Firewall sogar die Quell-IP in etwas völlig anderes übersetzt. Sie können also nicht einmal sicher sein, dass die Standardroute zu Ihrer Schnittstelle mit einer öffentlichen Schnittstelle führt.

Erkennen Sie es über die Standardroute

Dies ist meine empfohlene Methode, um Dinge automatisch zu erkennen

Etwas wie ip r get 1.1.1.1normalerweise sagt Ihnen die Schnittstelle, die die Standardroute hat.

Wenn Sie dies in Ihrer bevorzugten Skript- / Programmiersprache neu erstellen möchten, verwenden Sie strace ip r get 1.1.1.1die gelbe Backsteinstraße und folgen Sie ihr.

Stellen Sie es mit ein /etc/hosts

Dies ist meine Empfehlung, wenn Sie die Kontrolle behalten möchten

Sie können einen Eintrag in /etc/hostslike erstellen

80.190.1.3 publicinterfaceip

Dann können Sie diesen Alias ​​verwenden publicinterfaceip, um auf Ihre öffentliche Schnittstelle zu verweisen.

Leider haproxyist dieser Trick mit IPv6 nicht möglich

Nutze die Umgebung

Dies ist eine gute Problemumgehung, /etc/hostsfalls Sie dies nicht tunroot

Gleich wie /etc/hosts. aber nutzen Sie die Umgebung dafür. Sie können versuchen /etc/profileoder ~/.profiledafür.

Wenn Ihr Programm eine Variable benötigt MYPUBLICIP, können Sie Code wie folgt einfügen (dies ist C, Sie können C ++ daraus erstellen):

#define MYPUBLICIPENVVAR "MYPUBLICIP"

const char *mypublicip = getenv(MYPUBLICIPENVVAR);

if (!mypublicip) { fprintf(stderr, "please set environment variable %s\n", MYPUBLICIPENVVAR); exit(3); }

So können Sie Ihr Skript / Programm /path/to/your/scriptso aufrufen

MYPUBLICIP=80.190.1.3 /path/to/your/script

das funktioniert sogar in crontab.

Zählen Sie alle Schnittstellen auf und entfernen Sie diejenigen, die Sie nicht möchten

Der verzweifelte Weg, wenn Sie nicht verwenden können ip

Wenn Sie wissen, was Sie nicht wollen, können Sie alle Schnittstellen auflisten und alle falschen ignorieren.

Hier scheint bereits eine Antwort https://stackoverflow.com/a/265978/490291 für diesen Ansatz zu sein.

Mach es wie DLNA

Der Weg des Betrunkenen, der versucht, sich in Alkohol zu ertränken

Sie können versuchen, alle UPnP-Gateways in Ihrem Netzwerk aufzulisten, und auf diese Weise einen geeigneten Weg für eine "externe" Sache finden. Dies kann sogar auf einer Route der Fall sein, auf die Ihre Standardroute nicht verweist.

Weitere Informationen hierzu finden Sie unter https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol

Dies gibt Ihnen einen guten Eindruck davon, welches Ihre echte öffentliche Schnittstelle ist, selbst wenn Ihre Standardroute auf eine andere Stelle verweist.

Es gibt noch mehr

Wo der Berg auf den Propheten trifft

IPv6-Router bewerben sich selbst, um Ihnen das richtige IPv6-Präfix zu geben. Wenn Sie sich das Präfix ansehen, erhalten Sie einen Hinweis darauf, ob es eine interne oder eine globale IP-Adresse hat.

Sie können nach IGMP- oder IBGP-Frames suchen, um ein geeignetes Gateway zu finden.

Es gibt weniger als 2 ^ 32 IP-Adressen. Daher dauert es in einem LAN nicht lange, sie alle anzupingen. Dies gibt Ihnen einen statistischen Hinweis darauf, wo sich aus Ihrer Sicht der Großteil des Internets befindet. Sie sollten jedoch etwas vernünftiger sein als der berühmte https://de.wikipedia.org/wiki/SQL_Slammer

ICMP und sogar ARP sind gute Quellen für Netzwerkseitenbandinformationen. Es könnte Ihnen auch helfen.

Sie können die Ethernet Broadcast-Adresse verwenden, um mit all Ihren Netzwerkinfrastrukturgeräten in Kontakt zu treten, die häufig hilfreich sind, z. B. DHCP (sogar DHCPv6) usw.

Diese zusätzliche Liste ist wahrscheinlich endlos und immer unvollständig, da jeder Hersteller von Netzwerkgeräten fleißig neue Sicherheitslücken erfindet, um seine eigenen Geräte automatisch zu erkennen. Das hilft oft sehr dabei, eine öffentliche Schnittstelle zu erkennen, bei der es keine geben sollte.

'Nuff sagte. Aus.

Tino
quelle
4

Zusätzlich zu dem, was Steve Baker gesagt hat, finden Sie eine Beschreibung des SIOCGIFCONF ioctl in der Manpage netdevice (7) .

Sobald Sie die Liste aller IP-Adressen auf dem Host haben, müssen Sie eine anwendungsspezifische Logik verwenden, um die nicht gewünschten Adressen herauszufiltern, und hoffen, dass Sie noch eine IP-Adresse haben.

camh
quelle
4

Codieren Sie es nicht hart: So etwas kann sich ändern. Viele Programme finden heraus, woran sie sich binden müssen, indem sie eine Konfigurationsdatei einlesen und alles tun, was dazu gehört. Auf diese Weise kann Ihr Programm dies tun, falls es irgendwann in der Zukunft an etwas gebunden werden muss, das keine öffentliche IP-Adresse ist.

Thanatos
quelle
2

Da die Frage Linux angibt, ist meine bevorzugte Technik zum Ermitteln der IP-Adressen eines Computers die Verwendung von Netlink. Durch Erstellen eines Netlink-Sockets des Protokolls NETLINK_ROUTE und Senden eines RTM_GETADDR erhält Ihre Anwendung eine Nachricht mit allen verfügbaren IP-Adressen. Ein Beispiel finden Sie hier .

Libmnl ist praktisch, um Teile der Nachrichtenverarbeitung einfach zu gestalten. Wenn Sie neugierig sind, mehr über die verschiedenen Optionen von NETLINK_ROUTE (und wie sie analysiert werden) herauszufinden, ist die beste Quelle der Quellcode von iproute2 (insbesondere die Monitoranwendung) sowie die Empfangsfunktionen im Kernel. Die Manpage von rtnetlink enthält auch nützliche Informationen.

Kristian Evensen
quelle
1

Sie können eine Integration mit Curl so einfach wie möglich durchführen: Über curl www.whatismyip.orgdie Shell erhalten Sie Ihre globale IP. Sie sind auf einen externen Server angewiesen, aber Sie werden es immer sein, wenn Sie hinter einem NAT stehen.


quelle
Ich verstehe nie, warum mehr Leute diese Seite nicht benutzen. Sie haben sogar eine maschinenlesbare Seite, so dass Sie die Informationen nicht auf dem Bildschirm verschrotten müssen.
Deft_code
1
Möglicherweise erhalten Sie keine korrekte IP-Adresse, wenn Sie im Proxy arbeiten.
Jack
1
icanhazip.com gibt die IP zurück und nichts weiter. Perfekt zum Einbetten in ein Bash-Skript!
Lukecyca
ipinfo.io/ip ist eine weitere Alternative. Wenn Sie ipinfo.io locken , werden auch der Hostname, die Geolocation-Informationen und mehr im JSON-Format zurückgegeben.
Ben Dowling
-10

// Use a HTTP request to a well known server that echo's back the public IP address void GetPublicIP(CString & csIP) { // Initialize COM bool bInit = false; if (SUCCEEDED(CoInitialize(NULL))) { // COM was initialized bInit = true;

    // Create a HTTP request object
    MSXML2::IXMLHTTPRequestPtr HTTPRequest;
    HRESULT hr = HTTPRequest.CreateInstance("MSXML2.XMLHTTP");
    if (SUCCEEDED(hr))
    {
        // Build a request to a web site that returns the public IP address
        VARIANT Async;
        Async.vt = VT_BOOL;
        Async.boolVal = VARIANT_FALSE;
        CComBSTR ccbRequest = L"http://whatismyipaddress.com/";

        // Open the request
        if (SUCCEEDED(HTTPRequest->raw_open(L"GET",ccbRequest,Async)))
        {
            // Send the request
            if (SUCCEEDED(HTTPRequest->raw_send()))
            {
                // Get the response
                CString csRequest = HTTPRequest->GetresponseText();

                // Parse the IP address
                CString csMarker = "<!-- contact us before using a script to get your IP address -->";
                int iPos = csRequest.Find(csMarker);
                if (iPos == -1)
                    return;
                iPos += csMarker.GetLength();
                int iPos2 = csRequest.Find(csMarker,iPos);
                if (iPos2 == -1)
                    return;

                // Build the IP address
                int nCount = iPos2 - iPos;
                csIP = csRequest.Mid(iPos,nCount);
            }
        }
    }
}

// Unitialize COM
if (bInit)
    CoUninitialize();

}

Chaza
quelle
10
Dies ist eine schwere, nur für Windows (Frage zu Linux) schlecht formatierte Lösung, die sich auf Dienste stützt, die durch fragiles Parsen von Website-Inhalten außerhalb der lokalen Kontrolle liegen. Ich kann keine positive Seite dieser "Lösung" finden.
Viraptor
1
Hah! Die HTML-Analyse sucht sogar nach einem Kommentar, der eine Warnung darüber ist, was genau getan wird.
Notlesh