web-dev-qa-db-fra.com

Obtenir l'adresse IP de la machine

Cette question est presque identique à celle précédemment posée Obtenir l'adresse IP de l'ordinateur local - Question. Cependant, je dois trouver l’adresse IP d’une machine Linux .

Alors, comment puis-je - par programme dans C++ - détecter les adresses IP du serveur Linux sur lequel mon application est exécutée. Les serveurs auront au moins deux adresses IP et j'ai besoin d'une adresse 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.x.x
  • Le serveur aura une adresse IP externe (publique): 80.190.x.x

Je dois trouver l'adresse IP externe pour lier mon application à celle-ci. Évidemment, je peux aussi me connecter à INADDR_ANY (et c'est ce que je fais en ce moment). Je préférerais toutefois détecter l'adresse publique.

90
BlaM

J'ai trouvé la solution ioctl problématique sur os x (qui est conforme à POSIX et devrait donc ressembler à 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 identique à celui présenté ci-dessous.

J'ai donné ci-dessous un exemple rapide qui imprimera l'intégralité de l'adresse IPv4 de la machine (vous devez également vérifier que la commande getifaddrs a abouti, c'est-à-dire, renvoie 0).

J'ai mis à jour les adresses IPv6.

#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. Créer une socket.
  2. Effectue ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

Lis /usr/include/linux/if.h pour plus d'informations sur les structures ifconf et ifreq. Cela devrait vous donner l'adresse IP de chaque interface du système. Lisez aussi /usr/include/linux/sockios.h pour des ioctls supplémentaires.

28
Steve Baker

J'aime réponse de jjvainio . Comme Zan Lnyx le dit, 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 un socket UDP connecté, 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 connue devrait faire l'affaire. Pour ce faire, j'aime bien l'adresse du serveur DNS public 8.8.8.8 de Google, mais vous voudrez peut-être 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);
}
24
4dan

Comme vous l'avez découvert, une "adresse IP locale" n'existe pas. Voici comment trouver l’adresse locale pouvant être envoyée à un hôte spécifique.

  1. Créer un socket UDP
  2. Connectez le socket à une adresse externe (l'hôte qui recevra éventuellement l'adresse locale)
  3. Utilisez getsockname pour obtenir l'adresse locale
14
jjvainio

Cela a l’avantage de travailler sur de nombreuses versions de Unix ... et vous pouvez le modifier de manière triviale pour fonctionner sur n’importe quel système. Toutes les solutions ci-dessus me donnent des erreurs de compilation en fonction de la phase de la lune. Le moment où il existe un bon moyen POSIX de le faire ... ne l'utilisez pas (au moment où ceci 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;
}
8
Erik Aronesty

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 souhaitez pas et espérer qu'il vous reste une adresse IP.

4
camh

Ne codez pas cela durement: 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 devait à l'avenir devoir se connecter à quelque chose qui n'est pas une adresse IP publique, il peut le faire.

4
Thanatos

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 se rapprocher de ce que vous souhaitez. Par conséquent, je vais donner quelques conseils sur la manière de le faire.

Si une machine a plus de 2 interfaces (lo compte pour une), vous aurez des problèmes pour détecter facilement la bonne interface. Voici quelques recettes sur la façon de le faire.

Le problème, par exemple, est que les hôtes se trouvent dans un DMZ derrière un NAT pare-feu qui modifie l’adresse IP publique en une adresse IP privée et transmet les demandes). La machine peut avoir 10 interfaces, mais une seule correspond à l'interface publique.

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

Détectez-le via la route par défaut

Ceci est ma façon recommandée de détecter automatiquement les choses

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

Si vous voulez 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 dans /etc/hosts comme

80.190.1.3 publicinterfaceip

Vous pouvez ensuite utiliser cet alias publicinterfaceip pour faire référence à votre interface publique.

Malheureusement, haproxy ne fait pas cette astuce avec IPv6

Utiliser l'environnement

C'est une bonne solution de contournement pour /etc/hosts au cas où vous n'êtes pas root

Pareil que /etc/hosts. mais utilisez l'environnement pour cela. Tu peux essayer /etc/profile ou ~/.profile pour ça.

Par conséquent, si votre programme a besoin d'une variable MYPUBLICIP, vous pouvez inclure du code tel que (c'est du C, n'hésitez pas à en créer du C++):

#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/script comme ça

MYPUBLICIP=80.190.1.3 /path/to/your/script

cela fonctionne même dans crontab.

Énumérer toutes les interfaces et éliminer celles que vous ne voulez pas

La manière 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, il semble déjà y avoir une réponse https://stackoverflow.com/a/265978/490291 pour cette approche.

Faites-le comme DLNA

Le chemin de l'homme ivre qui tente de se noyer dans l'alcool

Vous pouvez essayer d'énumérer toutes les passerelles UPnP de votre réseau et trouver ainsi un itinéraire approprié pour une opération "externe". Cela pourrait même être sur une route où votre route par défaut ne pointe pas.

Pour plus d'informations à ce sujet, voir https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol

Cela vous donne une bonne impression de votre interface publique réelle, même si votre route par défaut pointe ailleurs.

Il y a encore plus

Où la montagne rencontre le prophète

Les routeurs IPv6 s’annoncent pour vous donner le bon préfixe IPv6. En regardant le préfixe, on peut se demander s'il a une adresse IP interne ou 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, un réseau local ne tarde pas à envoyer une requête ping à tous. Cela vous donne un indice statistique sur l'emplacement de la majorité d'Internet de votre point de vue. Cependant, vous devriez être un peu plus sensé 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 qui vous aideront 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 avec acharnement de nouvelles failles de sécurité sur la manière de détecter automatiquement leurs propres périphériques. Ce qui aide souvent beaucoup sur la façon de détecter une interface publique où il ne devrait pas y en avoir une.

'Nuff a dit. En dehors.

4
Tino

Vous pouvez faire une intégration avec curl aussi simple que: curl www.whatismyip.org de Shell vous permettra d’obtenir 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.

1
Zeist

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

Pour simplifier certaines parties du traitement des messages, libmnl est pratique. Si vous voulez en savoir plus sur les différentes options de NETLINK_ROUTE (et sur la manière dont elles sont analysées), la meilleure source est le code source de iproute2 (en particulier l’application de contrôle) ainsi que les fonctions de réception. dans le noyau. La page de manuel de rtnetlink contient également des informations utiles.

1
Kristian Evensen

Une élégante solution scriptée pour Linux est disponible à l’adresse suivante: http://www.damnsmalllinux.org/f/topic-3-23-17031-0.html

0
Dave Sieving