webentwicklung-frage-antwort-db.com.de

Rufen Sie die IP-Adresse des Geräts ab

Diese Frage ist fast identisch mit der zuvor gestellten Frage IP-Adresse des lokalen Computers abrufen -. Ich muss jedoch die IP-Adresse (n) eines Linux-Rechners ermitteln.

Also: Wie kann ich - programmgesteuert in C++ - die IP-Adressen des Linux-Servers ermitteln, 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 sicher, es gibt eine einfache Funktion, um das zu tun - aber wo?


Um es etwas klarer zu machen:

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

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.

90
BlaM

Ich fand die ioctl-Lösung unter OS X problematisch (POSIX-kompatibel, sollte also Linux-ähnlich sein). Mit getifaddress () können Sie jedoch das Gleiche problemlos tun. Es funktioniert unter OS X 10.5 einwandfrei und sollte im Folgenden identisch sein.

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

Ich habe aktualisiert, dass auch IPv6-Adressen angezeigt werden.

#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;
}
99
Twelve47
  1. Erstellen Sie einen Socket.
  2. Führe ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer); aus

Lesen /usr/include/linux/if.h für Informationen zu den Strukturen ifconf und ifreq. Dies sollte Ihnen die IP-Adresse jeder Schnittstelle im System geben. Lesen Sie auch /usr/include/linux/sockios.h für zusätzliche Ioctls.

28
Steve Baker

Ich mag jjvainios Antwort . 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);
}
24
4dan

Wie Sie herausgefunden haben, gibt es keine einzige "lokale IP-Adresse". So ermitteln Sie die lokale Adresse, die 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 möglicherweise die lokale Adresse erhält).
  3. Verwenden Sie getsockname, um die lokale Adresse abzurufen
14
jjvainio

Dies hat den Vorteil, dass Sie mit vielen Unix-Varianten arbeiten können ... und Sie können es einfach so ändern, dass es mit jedem beliebigen Betriebssystem funktioniert. Alle oben genannten Lösungen führen je nach Mondphase zu Compilerfehlern. In dem Moment gibt es eine gute POSIX-Möglichkeit, dies zu tun ... benutze dies nicht (zu der Zeit, als dies geschrieben wurde, war das 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;
}
8
Erik Aronesty

Zusätzlich zu den Ausführungen von Steve Baker 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 mithilfe der anwendungsspezifischen Logik die Adressen herausfiltern, die Sie nicht möchten, und hoffen, dass Sie noch eine IP-Adresse haben.

4
camh

Codiere es nicht hart: Das ist die Art von Dingen, die sich ändern können. Viele Programme finden heraus, woran sie sich binden müssen, indem sie eine Konfigurationsdatei einlesen und alles tun, was dazugehört. Auf diese Weise kann Ihr Programm dies tun, wenn es irgendwann in der Zukunft an etwas gebunden werden muss, das keine öffentliche IP-Adresse ist.

4
Thanatos

Ich glaube nicht, dass es eine endgültige richtige Antwort auf Ihre Frage gibt. Stattdessen gibt es ein großes Bündel von Möglichkeiten, um Ihren Wünschen näher zu kommen. Daher werde ich einige Hinweise geben, wie man das macht.

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

Das Problem ist beispielsweise, wenn sich Hosts in einer DMZ hinter einer NAT) - Firewall befinden, die die öffentliche IP in eine private IP ändert und die Anforderungen weiterleitet Maschine kann 10 Schnittstellen haben, aber nur eine entspricht der öffentlichen.

Selbst die automatische Erkennung funktioniert nicht, wenn Sie Double-NAT verwenden. Ihre Firewall übersetzt die Quell-IP sogar in etwas völlig anderes. 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 mein empfohlener Weg, um Dinge automatisch zu erkennen

So etwas wie ip r get 1.1.1.1 Gibt normalerweise die Schnittstelle an, die die Standardroute hat.

Wenn Sie dies in Ihrer bevorzugten Skript-/Programmiersprache nachbilden möchten, verwenden Sie strace ip r get 1.1.1.1 Und folgen Sie der gelben Backsteinstraße.

Setze es mit /etc/hosts

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

Sie können einen Eintrag in /etc/hosts Wie erstellen

80.190.1.3 publicinterfaceip

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

Leider macht haproxy diesen Trick mit IPv6 nicht fertig

Nutze die Umwelt

Dies ist eine gute Lösung für /etc/hosts, Falls Sie nicht root sind.

Gleich wie /etc/hosts. Aber nutzen Sie die Umgebung dafür. Sie können dazu /etc/profile Oder ~/.profile Versuchen.

Wenn Ihr Programm also eine Variable MYPUBLICIP benötigt, können Sie Code wie den folgenden einbinden (dies ist C, Sie können auch 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/script Aufrufen

MYPUBLICIP=80.190.1.3 /path/to/your/script

dies funktioniert sogar in crontab.

Zählen Sie alle Schnittstellen auf und entfernen Sie die nicht benötigten

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

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

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

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, um auf diese Weise eine geeignete Route für eine "externe" Sache zu finden. Dies kann sogar auf einer Route 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, auch wenn Ihre Standardroute auf eine andere Stelle verweist.

Es gibt noch mehr

Wo der Berg auf den Propheten trifft

IPv6-Router machen Werbung, 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 auf IGMP- oder IBGP-Frames warten, 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 größte Teil des Internets befindet. Sie sollten jedoch etwas vernünftiger sein als das 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 Abhilfe schaffen, z. B. DHCP (auch DHCPv6) und so weiter.

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, wenn es darum geht, eine öffentliche Schnittstelle zu erkennen, bei der es keine geben sollte.

Sagte Nuff. Aus.

4
Tino

Sie können eine Integration mit Curl so einfach durchführen wie: curl www.whatismyip.org von der Shell erhalten Sie Ihre globale IP. Sie sind auf einen externen Server angewiesen, aber Sie werden es immer sein, wenn Sie sich hinter einem NAT befinden.

1
Zeist

Da die Frage Linux spezifiziert, ist meine bevorzugte Technik zum Ermitteln der IP-Adressen eines Computers die Verwendung von Netlink. Wenn Sie einen Netlink-Socket mit dem Protokoll NETLINK_ROUTE erstellen und ein RTM_GETADDR senden, erhält Ihre Anwendung eine Nachricht mit allen verfügbaren IP-Adressen. Ein Beispiel wird bereitgestellt hier .

Um Teile der Nachrichtenbehandlung zu vereinfachen, ist libmnl praktisch. Wenn Sie mehr über die verschiedenen Optionen von NETLINK_ROUTE (und deren Analyse) erfahren möchten, ist die beste Quelle das Quellcode von iproute2 (insbesondere die Monitoranwendung) sowie die Empfangsfunktionen im Kernel. Die Manpage von rtnetlink enthält auch nützliche Informationen.

1

Eine elegante Skriptlösung für Linux finden Sie unter: http://www.damnsmalllinux.org/f/topic-3-23-17031-0.html

0
Dave Sieving