Obtenez l'adresse IP de la machine

94

Cette question est presque la même que celle demandée précédemment Obtenir l'adresse IP de l'ordinateur local -Question. Cependant, j'ai besoin de trouver la ou les adresses IP d'une machine Linux .

Donc: Comment puis-je - par programmation en C ++ - détecter les adresses IP du serveur Linux sur lequel mon application s'exécute. Les serveurs auront au moins deux adresses IP et j'en ai besoin d'une spécifique (celle d'un réseau donné (le public)).

Je suis sûr qu'il existe une fonction simple pour le faire - mais où?


Pour rendre les choses un peu plus claires:

  • Le serveur aura évidemment le "localhost": 127.0.0.1
  • Le serveur aura une adresse IP interne (de gestion): 172.16.xx
  • Le serveur aura une adresse IP externe (publique): 80.190.xx

J'ai besoin de trouver l'adresse IP externe pour y lier mon application. Évidemment, je peux aussi me lier à INADDR_ANY (et en fait c'est ce que je fais pour le moment). Je préférerais cependant détecter l'adresse publique.

BlaM
la source
3
Pourquoi marquer le popen d'ifconfig? C'est vraiment la bonne chose à faire. Les bibliothèques changent, mais ifconfig et popen seront toujours là.
Erik Aronesty
3
Non, ifconfig, routeetc sont déconseillés pour la ipcommande. Vous devriez maintenant l'utiliser à la place.
Anders le
ifconfig travaille encore aujourd'hui sur plus d'architectures que toute autre approche. Changez-le donc en commande ip.when / si approprié. popen est toujours la meilleure solution.
Erik Aronesty

Réponses:

104

J'ai trouvé la solution ioctl problématique sur os x (qui est compatible POSIX et devrait donc être similaire à Linux). Cependant, getifaddress () vous permettra de faire la même chose facilement, cela fonctionne très bien pour moi sur os x 10.5 et devrait être le même ci-dessous.

J'ai fait un exemple rapide ci-dessous qui affichera toute l'adresse IPv4 de la machine, (vous devriez également vérifier que getifaddrs a réussi, c'est-à-dire renvoie 0).

Je l'ai mis à jour pour afficher les adresses IPv6 également.

#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;
}
Douze47
la source
7
+1 et merci d'avoir apporté cette belle approche au lieu de ces ioctl ennuyeux! Cependant, votre gestion de l'IP6 est toujours incorrecte - vous devez lancer un cast vers sockaddr_in6, c'est-à-dire quelque chose commetmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
Andrey
2
@Andrey, merci. J'ai mis à jour ma réponse (je n'ai pas eu l'occasion de la tester)
Twelve47
2
donne loet wlan0interfaces dans mon cas. Ce dont j'ai besoin est wlan0ou eth0qui a une connexion Internet.Dois-je utiliser strcmpou est-ce un meilleur moyen?
Sudip Bhattarai
28
  1. Créez une socket.
  2. Effectuer ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

Lisez /usr/include/linux/if.hpour plus d'informations sur les structures ifconfet ifreq. Cela devrait vous donner l'adresse IP de chaque interface du système. Lisez également /usr/include/linux/sockios.hpour d'autres ioctls.

Steve Baker
la source
J'avais l'impression qu'il ne vous donnera que l'adresse de l'interface à laquelle il se lie.
Matt Joiner
@MattJoiner Non, jetez un œil à la page de manuel, SIOCGIFCONF renvoie des informations sur toutes les interfaces. Partie de la page de manuel citée ici: "Renvoie une liste d'adresses d'interface (couche de transport). Cela signifie actuellement que les adresses de la famille AF_INET (IPv4) pour la compatibilité. L'utilisateur transmet une structure ifconf comme argument à l'ioctl. Elle contient un pointeur vers un tableau de structures ifreq dans ifc_req et sa longueur en octets dans ifc_len. Le noyau remplit les ifreqs avec toutes les adresses d'interface L3 en cours d'exécution "
Stéphane
25

J'aime la réponse de jjvainio . Comme le dit Zan Lnyx, il utilise la table de routage locale pour trouver l'adresse IP de l'interface Ethernet qui serait utilisée pour une connexion à un hôte externe spécifique. En utilisant une prise UDP connectée, vous pouvez obtenir les informations sans envoyer de paquets. L'approche nécessite que vous choisissiez un hôte externe spécifique. La plupart du temps, toute adresse IP publique bien connue devrait faire l'affaire. J'aime l'adresse du serveur DNS public 8.8.8.8 de Google à cette fin, mais il se peut que vous souhaitiez parfois choisir une adresse IP d'hôte externe différente. Voici un code qui illustre l'approche complète.

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
la source
2
cela me donne une adresse IP privée 127. *. *. *. peut-être parce que je suis derrière NAT?
eugene le
14

Comme vous l'avez découvert, il n'y a pas une seule "adresse IP locale". Voici comment trouver l'adresse locale qui peut être envoyée à un hôte spécifique.

  1. Créer un socket UDP
  2. Connectez la prise à une adresse extérieure (l'hôte qui recevra éventuellement l'adresse locale)
  3. Utilisez getsockname pour obtenir l'adresse locale
jjvainio
la source
Je ne l'ai jamais fait de cette façon mais j'aime ça. Il utilise automatiquement la table de routage du système d'exploitation et est beaucoup plus facile que de scanner la liste des interfaces.
Zan Lynx
5
Cela suppose que vous savez ce que sera une adresse externe. Ce n'est souvent pas le cas dans les centres de données.
Christopher
Vous pouvez toujours en essayer trois qui sont attribués à des continents différents (l'IANA rend cela facile) et accepter les deux meilleurs sur trois.
Joshua
9

Cela a l'avantage de travailler sur de nombreuses versions d'Unix ... et vous pouvez le modifier de manière triviale pour travailler sur n'importe quel o / s. Toutes les solutions ci-dessus me donnent des erreurs de compilation en fonction de la phase de la lune. Au moment où il existe un bon moyen POSIX de le faire ... ne l'utilisez pas (au moment où cela a été écrit, ce n'était pas le cas).

// 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
la source
merci pour cette solution, fonctionne très bien pour moi et est très pratique dans de nombreuses situations lorsque vous souhaitez simplement obtenir des données de programmes cl
zitroneneis
Bien que ifconfig génère les données requises, je déconseillerais fortement d'utiliser n'importe quel programme de cette façon. Il est bien préférable d'entrer dans la source d'ifconfig et d'obtenir la fonctionnalité que de l'exécuter et d'analyser sa sortie.
Zaur Nasibov
1
@MartinMeeser: a rendu le langage neutre, en recherchant "inet" puis ":" séparément.
Erik Aronesty
1
Ensuite, vous avez d'autres langues qui ne sont même pas un jeu de caractères latin. La solution est de ne pas utiliser d'outils de commande, car ils deviennent obsolètes comme ifconfig. Vous devriez utiliser ipmaintenant. La bonne façon de générer une sortie LANGindépendante du langage est de définir la variable d'environnement sur C, comme:LANG=C ls -l
Anders
1
Merci. C'est une solution simple et géniale!
Ahmad Afkandeh
5

Je ne pense pas qu'il y ait une bonne réponse définitive à votre question. Au lieu de cela, il existe un grand nombre de façons de vous rapprocher de ce que vous souhaitez. Par conséquent, je vais fournir quelques conseils pour y parvenir.

Si une machine a plus de 2 interfaces (lo compte pour une), vous aurez des problèmes pour détecter automatiquement la bonne interface facilement. Voici quelques recettes pour y parvenir.

Le problème, par exemple, est si les hôtes se trouvent dans une zone démilitarisée derrière un pare-feu NAT qui transforme l'adresse IP publique en une adresse IP privée et transmet les demandes. Votre machine peut avoir 10 interfaces, mais une seule correspond à celle publique.

Même la détection automatique ne fonctionne pas si vous êtes en double NAT, où votre pare-feu traduit même l'IP source en quelque chose de complètement différent. Vous ne pouvez donc même pas être sûr que la route par défaut mène à votre interface avec une interface publique.

Détectez-le via l'itinéraire par défaut

C'est ma méthode recommandée pour détecter automatiquement les choses

Quelque chose comme ip r get 1.1.1.1 vous indique généralement l'interface qui a la route par défaut.

Si vous souhaitez recréer cela dans votre langage de script / programmation préféré, utilisez strace ip r get 1.1.1.1 et suivez la route de briques jaunes.

Réglez-le avec /etc/hosts

Ceci est ma recommandation si vous voulez garder le contrôle

Vous pouvez créer une entrée /etc/hostscomme

80.190.1.3 publicinterfaceip

Ensuite, vous pouvez utiliser cet alias publicinterfaceippour faire référence à votre interface publique.

Malheureusement, haproxyne gâche pas cette astuce avec IPv6

Utilisez l'environnement

C'est une bonne solution de contournement /etc/hostsau cas où vous ne seriez pasroot

Identique à /etc/hosts. mais utilisez l'environnement pour cela. Vous pouvez essayer /etc/profileou ~/.profilepour cela.

Par conséquent, si votre programme a besoin d'une variable, MYPUBLICIPvous pouvez inclure du code comme (c'est C, n'hésitez pas à créer C ++ à partir de celui-ci):

#define MYPUBLICIPENVVAR "MYPUBLICIP"

const char *mypublicip = getenv(MYPUBLICIPENVVAR);

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

Vous pouvez donc appeler votre script / programme /path/to/your/scriptcomme ceci

MYPUBLICIP=80.190.1.3 /path/to/your/script

cela fonctionne même dans crontab .

Énumérez toutes les interfaces et éliminez celles que vous ne voulez pas

La voie désespérée si vous ne pouvez pas utiliser ip

Si vous savez ce que vous ne voulez pas, vous pouvez énumérer toutes les interfaces et ignorer toutes les fausses.

Ici semble déjà être une réponse https://stackoverflow.com/a/265978/490291 pour cette approche.

Faites-le comme DLNA

La voie de l'homme ivre qui tente de se noyer dans l'alcool

Vous pouvez essayer d'énumérer toutes les passerelles UPnP sur votre réseau et de cette façon trouver une route appropriée pour quelque chose "externe". Cela peut même être sur une route sur laquelle votre route par défaut ne pointe pas.

Pour en savoir plus, consultez peut-être https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol

Cela vous donne une bonne idée de celle qui est votre véritable interface publique, même si votre itinéraire par défaut pointe ailleurs.

Il y en a encore plus

Où la montagne rencontre le prophète

Les routeurs IPv6 s'annoncent pour vous donner le bon préfixe IPv6. L'examen du préfixe vous donne un indice pour savoir s'il a une adresse IP interne ou une adresse IP globale.

Vous pouvez écouter les trames IGMP ou IBGP pour trouver une passerelle appropriée.

Il y a moins de 2 ^ 32 adresses IP. Par conséquent, il ne faut pas longtemps sur un réseau local pour les envoyer tous. Cela vous donne un indice statistique sur l'endroit où se trouve la majorité d'Internet de votre point de vue. Cependant, vous devriez être un peu plus sensible que le célèbre https://de.wikipedia.org/wiki/SQL_Slammer

ICMP et même ARP sont de bonnes sources d'informations sur la bande latérale du réseau. Cela pourrait aussi vous aider.

Vous pouvez utiliser l'adresse de diffusion Ethernet pour contacter tous les périphériques de votre infrastructure réseau, ce qui vous aidera souvent, comme DHCP (même DHCPv6), etc.

Cette liste supplémentaire est probablement infinie et toujours incomplète, car chaque fabricant de périphériques réseau invente activement de nouvelles failles de sécurité sur la façon de détecter automatiquement ses propres périphériques. Ce qui aide souvent beaucoup à détecter une interface publique là où il ne devrait pas y en avoir.

Dit Nuff. En dehors.

Tino
la source
4

Suite à ce que Steve Baker a dit, vous pouvez trouver une description de l'ioctl SIOCGIFCONF dans la page de manuel netdevice (7) .

Une fois que vous avez la liste de toutes les adresses IP sur l'hôte, vous devrez utiliser une logique spécifique à l'application pour filtrer les adresses que vous ne voulez pas et espérer qu'il vous reste une adresse IP.

camh
la source
4

Ne le codez pas en dur: c'est le genre de chose qui peut changer. De nombreux programmes déterminent à quoi se lier en lisant un fichier de configuration et en faisant tout ce qui est dit. De cette façon, si votre programme a besoin à un moment donné de se lier à quelque chose qui n'est pas une adresse IP publique, il peut le faire.

Thanatos
la source
2

Comme la question spécifie Linux, ma technique préférée pour découvrir les adresses IP d'une machine est d'utiliser netlink. En créant une socket netlink du protocole NETLINK_ROUTE et en envoyant un RTM_GETADDR, votre application recevra un ou plusieurs messages contenant toutes les adresses IP disponibles. Un exemple est fourni ici .

Afin de n'utiliser que des parties de la gestion des messages, libmnl est pratique. Si vous êtes curieux d'en savoir plus sur les différentes options de NETLINK_ROUTE (et comment elles sont analysées), la meilleure source est le code source d'iproute2 (en particulier l'application de moniteur) ainsi que les fonctions de réception dans le noyau. La page de manuel de rtnetlink contient également des informations utiles.

Kristian Evensen
la source
1

Vous pouvez faire une intégration avec curl aussi simplement que: à curl www.whatismyip.orgpartir du shell, vous obtiendrez votre adresse IP globale. Vous dépendez en quelque sorte d'un serveur externe, mais vous le serez toujours si vous êtes derrière un NAT.


la source
Je ne comprends jamais pourquoi plus de gens n'utilisent pas ce site. Ils ont même une page lisible par machine pour que vous n'ayez pas à filtrer les informations.
deft_code
1
Il se peut que vous n'obteniez pas une adresse IP correcte si vous utilisez un proxy.
Jack
1
icanhazip.com renvoie l'IP et rien de plus. Parfait pour intégrer dans un script bash!
lukecyca
ipinfo.io/ip est une autre alternative. Si vous curl ipinfo.io, il renvoie également le nom d'hôte, les informations de géolocalisation et plus encore au format JSON.
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
la source
10
Il s'agit d'une solution lourde, uniquement Windows (question sur Linux), mal formatée, reposant sur des services hors de contrôle local via une analyse fragile du contenu du site Web. Je ne trouve aucun côté positif de cette «solution».
viraptor
1
Hah! L'analyse HTML recherche même un commentaire qui est un avertissement sur ce qui est fait exactement.
notlesh