Diese Frage ist fast die gleiche wie die zuvor gestellte Wie erhalte ich die IP-Adresse eines lokalen Computers? -Frage. Allerdings muss ich die IP-Adresse(n) von a finden Linux-Maschine.
Also: Wie kann ich – programmgesteuert in C++ – Ermitteln Sie die IP-Adressen des Linux-Servers, auf dem meine Anwendung ausgeführt wird. Die Server haben mindestens zwei IP-Adressen und ich brauche 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 Sache 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 auch an INADDR_ANY binden (und das mache ich im Moment auch). Ich würde es jedoch vorziehen, die öffentliche Adresse zu erkennen.
Ich fand die ioctl-Lösung unter OS X problematisch (das POSIX-kompatibel ist und daher Linux ähneln sollte). Mit getifaddress() können Sie jedoch dasselbe einfach tun. Es funktioniert für mich unter OS x 10.5 einwandfrei und sollte unten dasselbe sein.
Ich habe unten ein kurzes Beispiel erstellt, das die gesamte IPv4-Adresse des Computers druckt (Sie sollten auch überprüfen, ob getifaddrs erfolgreich war, dh 0 zurückgibt).
Ich habe es aktualisiert und zeigt auch IPv6-Adressen an.
#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;
}
- Erstellen Sie eine Steckdose.
- Ausführen
ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);
Lesen /usr/include/linux/if.h
für Informationen über die ifconf
und ifreq
Strukturen. Dies sollte Ihnen die IP-Adresse jeder Schnittstelle im System geben. Lesen Sie auch /usr/include/linux/sockios.h
für zusätzliche ioctls.
Ich mag jjvainios Antwort. Wie Zan Lnyx sagt, verwendet es die lokale Routing-Tabelle, um die IP-Adresse der Ethernet-Schnittstelle zu finden, die für eine Verbindung zu einem bestimmten externen Host verwendet 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 ausreichen. Ich mag für diesen Zweck die öffentliche DNS-Serveradresse 8.8.8.8 von Google, aber es kann vorkommen, dass Sie eine andere externe Host-IP wä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);
}
Wie Sie herausgefunden haben, gibt es keine einzelne “lokale IP-Adresse”. So finden Sie die lokale Adresse heraus, die an einen bestimmten Host gesendet werden kann.
- Erstellen Sie einen UDP-Socket
- Verbinden Sie den Socket mit einer externen Adresse (dem Host, der schließlich die lokale Adresse erhält)
- Verwenden Sie getsockname, um die lokale Adresse abzurufen
Dies hat den Vorteil, dass es auf vielen Arten von Unix funktioniert … und Sie können es trivial modifizieren, um auf jedem Betriebssystem zu arbeiten. Alle oben genannten Lösungen geben mir abhängig von der Mondphase Compilerfehler. Im Moment gibt es einen guten POSIX-Weg, dies zu tun … verwenden Sie dies nicht (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;
}
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, wie Sie dem, was Sie wünschen, nahe kommen können. Daher werde ich einige Hinweise geben, wie man es macht.
Wenn eine Maschine mehr als 2 Schnittstellen hat (lo
als eins zählt) 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 Anfragen weiterleitet. Ihre Maschine kann 10 Schnittstellen haben, 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.1
teilt Ihnen normalerweise die Schnittstelle mit, 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.1
und folgen Sie dem gelben Ziegelsteinweg.
Stellen Sie es mit ein /etc/hosts
Das ist meine Empfehlung, wenn Sie die Kontrolle behalten wollen
Sie können einen Eintrag erstellen in /etc/hosts
wie
80.190.1.3 publicinterfaceip
Dann können Sie diesen Alias verwenden publicinterfaceip
um auf Ihre öffentliche Schnittstelle zu verweisen.
Leider haproxy
grok nicht diesen Trick mit IPv6
Nutze die Umgebung
Dies ist eine gute Problemumgehung für /etc/hosts
falls du es nicht bist root
Gleich wie /etc/hosts
. sondern nutze dafür die Umgebung. Du kannst es versuchen /etc/profile
oder ~/.profile
dafür.
Wenn Ihr Programm also eine Variable benötigt MYPUBLICIP
dann können Sie Code einfügen wie (das 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 aufrufen /path/to/your/script
so was
MYPUBLICIP=80.190.1.3 /path/to/your/script
das geht sogar rein crontab
.
Zählen Sie alle Schnittstellen auf und eliminieren Sie diejenigen, die Sie nicht wollen
Der verzweifelte Weg, wenn Sie nicht verwenden können ip
Wenn Sie wissen, was Sie nicht wollen, können Sie alle Schnittstellen aufzählen und alle falschen ignorieren.
Hier scheint es bereits eine Antwort https://stackoverflow.com/a/265978/490291 für diesen Ansatz zu geben.
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 eine geeignete Route für etwas “Externes” herauszufinden. Dies kann sogar auf einer Route liegen, auf die Ihre Standardroute nicht zeigt.
Mehr dazu vielleicht unter https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol
Dies gibt Ihnen einen guten Eindruck, welches Ihre wirkliche öffentliche Schnittstelle ist, auch wenn Ihre Standardroute woanders hinführt.
Es gibt noch mehr
Wo der Berg den Propheten trifft
IPv6-Router werben für sich selbst, um Ihnen das richtige IPv6-Präfix zu geben. Ein Blick auf das Präfix gibt Ihnen einen Hinweis darauf, ob es eine interne IP oder eine globale hat.
Sie können auf IGMP- oder IBGP-Frames hören, 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. So erhalten Sie einen statistischen Hinweis darauf, wo sich aus Ihrer Sicht der Großteil des Internets befindet. Allerdings sollte man etwas vernünftiger sein als die Berühmten https://de.wikipedia.org/wiki/SQL_Slammer
ICMP und sogar ARP sind gute Quellen für Netzwerkseitenbandinformationen. Es könnte dir auch weiterhelfen.
Sie können die Ethernet-Broadcast-Adresse verwenden, um alle Ihre Netzwerkinfrastrukturgeräte zu kontaktieren, die oft hilfreich sind, wie DHCP (sogar DHCPv6) und so weiter.
Diese zusätzliche Liste ist wahrscheinlich endlos und immer unvollständig, da jeder Hersteller von Netzwerkgeräten eifrig neue Sicherheitslücken erfindet, wie er seine eigenen Geräte automatisch erkennen kann. Was oft sehr hilfreich ist, um eine öffentliche Schnittstelle zu erkennen, wo keine sein sollte.
‘Nuff sagte. Aus.
Zusätzlich zu dem, was Steve Baker gesagt hat, finden Sie eine Beschreibung des SIOCGIFCONF ioctl in der netdevice(7) Manpage.
Sobald Sie die Liste aller IP-Adressen auf dem Host haben, müssen Sie anwendungsspezifische Logik verwenden, um die Adressen herauszufiltern, die Sie nicht wollen, und hoffen, dass Sie eine IP-Adresse übrig haben.
Warum den Popen von ifconfig notieren? Es ist wirklich das Richtige. Bibliotheken ändern sich, aber ifconfig und popen werden immer da sein.
– Erik Aronesty
18. Mai 2011 um 13:40 Uhr
Nein,
ifconfig
,route
usw. sind für die veraltetip
Befehl. Jetzt sollten Sie das stattdessen verwenden.– Anders
12. September 2014 um 22:58 Uhr
ifconfig funktioniert auch heute noch auf mehr Architekturen als jeder andere Ansatz. Ändern Sie es also in das ip-Kommando, wenn/falls angemessen. popen ist immer noch die bessere lösung.
– Erik Aronesty
13. September 2016 um 12:54 Uhr