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:
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.
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;
}
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.
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);
}
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.
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;
}
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.
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.
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.
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.
/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
C'est une bonne solution de contournement pour
/etc/hosts
au cas où vous n'êtes pasroot
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
.
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.
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.
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.
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.
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.
Une élégante solution scriptée pour Linux est disponible à l’adresse suivante: http://www.damnsmalllinux.org/f/topic-3-23-17031-0.html