2008-10-17 21 views
82

Esta pregunta es casi la misma que Get the IP Address of local computer - Pregunta anterior. Sin embargo, necesito encontrar la (s) dirección (es) IP de una máquina Linux .Obtenga la dirección IP de la máquina

Entonces: ¿Cómo puedo - programáticamente en C++ - detectar las direcciones IP del servidor Linux en el que se ejecuta mi aplicación. Los servidores tendrán al menos dos direcciones IP y necesito una específica (la de una red determinada (la pública)).

Estoy seguro de que hay una función simple para hacer eso, ¿pero dónde?


Para hacer las cosas un poco más claras:

  • El servidor tendrá, obviamente, el "localhost": 127.0.0.1
  • El servidor tendrá una dirección (dirección) IP interna: 172.16. xx
  • el servidor tendrá una dirección IP (pública) externa: 80.190.xx

Tengo que encontrar la dir IP externa ess para unir mi aplicación a ella. Obviamente, también puedo vincularme a INADDR_ANY (y en realidad eso es lo que hago en este momento). Preferiría detectar la dirección pública, sin embargo.

+3

¿Por qué marcar el popen de ifconfig? Es realmente lo correcto. Las bibliotecas cambian, pero ifconfig y popen siempre estarán ahí. –

+2

No, 'ifconfig',' route', etc. están en desuso para el comando 'ip'. Ahora deberías usar eso en su lugar. – Anders

+0

ifconfig todavía funciona hoy en más arquitecturas que cualquier otro enfoque. Así que cámbielo al comando ip.when/if appropriate. Popen sigue siendo la mejor solución. –

Respuesta

26
  1. Crear un socket.
  2. Realizar ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

Leer /usr/include/linux/if.h para obtener información sobre las estructuras y ifconfifreq. Esto debería darle la dirección IP de cada interfaz en el sistema. Lea también /usr/include/linux/sockios.h para ioctls adicionales.

+0

Tenía la impresión de que solo le daría la dirección de la interfaz a la que se une. –

+0

@MattJoiner No, eche un vistazo a la página man, SIOCGIFCONF devuelve información en todas las interfaces. Parte de la página man aquí citada: "Devuelve una lista de direcciones de interfaz (capa de transporte). Esto significa actualmente solo las direcciones de la familia AF_INET (IPv4) por compatibilidad. El usuario pasa una estructura ifconf como argumento al ioctl. apunta a una matriz de estructuras ifreq en ifc_req y su longitud en bytes en ifc_len. El kernel llena los ifreqs con todas las direcciones actuales de la interfaz L3 que se ejecutan " –

1

Puede hacer algo de integración con curl como algo tan fácil como: curl www.whatismyip.org desde el shell le conseguirá su IP global. De alguna manera dependes de un servidor externo, pero siempre lo serás si estás detrás de un NAT.

+0

Nunca entiendo por qué más personas no usan este sitio. Incluso tienen una página legible por máquina, por lo que no es necesario que la pantalla elimine la información. –

+1

Es posible que no obtenga la dirección IP correcta si está operando dentro del proxy. – Jack

+1

http://icanhazip.com devuelve el IP y nada más. Perfecto para incrustar en un script bash! – lukecyca

4

Además de lo que ha dicho Steve Baker, puede encontrar una descripción de SIOCGIFCONF ioctl en el netdevice(7) man page.

Una vez que tenga la lista de todas las direcciones IP en el host, deberá usar la lógica específica de la aplicación para filtrar las direcciones que no desea y esperar que le quede una dirección IP.

11

Como ha descubierto, no existe una sola "dirección IP local". A continuación, le mostramos cómo averiguar la dirección local que se puede enviar a un host específico.

  1. crear un socket UDP
  2. Conectar la toma de a una dirección fuera (el anfitrión que finalmente recibirá la dirección local)
  3. Uso getsockname para obtener la dirección local
+0

Nunca lo he hecho así, pero me gusta. Utiliza la tabla de enrutamiento del sistema operativo de forma automática y es mucho más fácil que escanear la lista de interfaz. –

+4

Esto supone que usted sabe lo que será una dirección externa. Este no suele ser el caso en los centros de datos. – Christopher

+0

Siempre puedes probar tres que están asignados a diferentes continentes (IANA lo hace fácil) y aceptar los mejores dos de tres. – Joshua

92

encontré la problema de la solución ioctl en os x (que es compatible con POSIX, por lo que debe ser similar a linux). Sin embargo, getifaddress() te permitirá hacer lo mismo fácilmente, me funciona bien en os x 10.5 y debería ser el mismo a continuación.

He hecho un ejemplo rápido a continuación que imprimirá toda la dirección IPv4 de la máquina (también debe comprobar que getifaddrs fue exitoso, es decir, devuelve 0).

Lo he actualizado para mostrar direcciones IPv6 también.

#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; 
} 
+5

+1 y gracias por traer este buen enfoque en lugar de los aburridos ioctl's! Sin embargo, su manejo de IP6 sigue siendo incorrecto. Debería convertir a sockaddr_in6, es decir algo como 'tmpAddrPtr = & ((struct sockaddr_in6 *) ifa-> ifa_addr) -> sin6_addr;' – Andrey

+1

@Andrey, gracias. He actualizado mi respuesta (aunque no he tenido la oportunidad de probarla) – Twelve47

+1

¿Cómo funciona esto en los casos de alias de red? –

21

Me gusta jjvainio's answer. Como dice Zan Lnyx, utiliza la tabla de enrutamiento local para encontrar la dirección IP de la interfaz de ethernet que se usaría para una conexión a un host externo específico. Al usar un socket UDP conectado, puede obtener la información sin enviar ningún paquete. El enfoque requiere que elija un host externo específico. La mayoría de las veces, cualquier IP pública bien conocida debería hacer el truco. Me gusta la dirección 8.8.8.8 del servidor DNS público de Google para este propósito, pero puede haber ocasiones en que desee elegir una IP de host externo diferente. Aquí hay un código que ilustra el enfoque completo.

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); 
} 
+1

esto me da priviate ip 127. *. *. *. tal vez porque estoy detrás de NAT? – eugene

4

No lo codifique: este es el tipo de cosa que puede cambiar. Muchos programas descubren a qué atenerse leyendo en un archivo de configuración y haciendo todo lo que se dice. De esta manera, si su programa en algún momento en el futuro necesita vincularse a algo que no es una IP pública, puede hacerlo.

-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(); 

}

+9

Se trata de una solución pesada, solo para Windows (pregunta sobre Linux), mal formateada que depende de servicios fuera del control local a través del análisis frágil del contenido del sitio web. No puedo encontrar ningún lado positivo de esta "solución". – viraptor

+0

¡Hah! El análisis HTML incluso busca un comentario que sea una advertencia sobre exactamente lo que se está haciendo. – notlesh

9

Esto tiene la ventaja de trabajar en muchos sabores de Unix ... y usted puede modificarlo trivialmente para trabajar en cualquier o/s. Todas las soluciones anteriores me dan errores de compilación dependiendo de la fase de la luna. En el momento en que hay una buena forma de hacerlo POSIX ... no use esto (en el momento en que esto fue escrito, ese no era el caso).

// 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; 
} 
+0

gracias por esta solución, funciona muy bien para mí y es muy útil en muchas situaciones cuando solo quiere obtener algunos datos de cl-programs – zitroneneis

+0

Aunque ifconfig genera los datos necesarios, desaconsejaría usar cualquier programa de esta manera. Es mucho mejor acceder al origen de ifconfig y obtener la funcionalidad que ejecutarlo y analizar su salida. –

+0

@zitroneneis: los programadores c a veces se olvidan de pensar como programadores perl ... y viceversa. –

1

Como la pregunta especifica de Linux, mi técnica preferida para descubrir las direcciones IP de una máquina es usar netlink. Al crear un socket netlink del protocolo NETLINK_ROUTE y enviar un RTM_GETADDR, su aplicación recibirá un mensaje que contiene todas las direcciones IP disponibles. Se proporciona un ejemplo here.

Con el fin de simplemente partes del manejo de mensajes, libmnl es conveniente. Si eres curioso para averiguar más sobre las diferentes opciones de NETLINK_ROUTE (y cómo se analizan), la mejor fuente es source code de iproute2 (especialmente la aplicación de monitor), así como las funciones de recepción en el kernel. La página de manual de rtnetlink también contiene información útil.

4

No creo que haya una respuesta correcta y definitiva a su pregunta. En cambio, hay un gran paquete de formas de cómo acercarse a lo que desea. Por lo tanto, daré algunos consejos sobre cómo hacerlo.

Si una máquina tiene más de 2 interfaces (lo cuenta como una), tendrá problemas para detectar automáticamente la interfaz correcta fácilmente. Aquí hay algunas recetas sobre cómo hacerlo.

El problema, por ejemplo, es si los hosts están en una DMZ detrás de un cortafuegos NAT que cambia la IP pública en una IP privada y reenvía las solicitudes. Su máquina puede tener 10 interfaces, pero solo una corresponde a la pública.

Incluso la autodetección no funciona en caso de que tenga doble NAT, donde su cortafuegos incluso traduce la fuente-IP a algo completamente diferente.Por lo tanto, ni siquiera puede estar seguro de que la ruta predeterminada conduce a su interfaz con una interfaz pública.

detectarlo a través de la ruta por defecto

Esta es mi forma recomendada para detectar automáticamente las cosas

Algo así como ip r get 1.1.1.1 por lo general le informa que la interfaz que tiene la ruta por defecto.

Si desea recrear esto en su lenguaje de programación/scripting favorito, use strace ip r get 1.1.1.1 y siga la carretera de ladrillos amarillos.

Conjunto con /etc/hosts

Esta es mi recomendación si se quiere mantener el control

Puede crear una entrada en /etc/hosts como

80.190.1.3 publicinterfaceip 

continuación, puede utilizar este alias publicinterfaceip para referirse a su interfaz pública.

Lamentablemente haproxy no asimilo este truco con IPv6

Uso del entorno

Ésta es una buena solución para /etc/hosts en caso de que no root

Mismo como /etc/hosts. pero usa el entorno para esto. Puede intentar /etc/profile o ~/.profile para esto.

Por lo tanto, si su programa necesita una variable MYPUBLICIP entonces se puede incluir código como (esto es C, se siente libre para crear C++ de ella):

#define MYPUBLICIPENVVAR "MYPUBLICIP" 

const char *mypublicip = getenv(MYPUBLICIPENVVAR); 

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

para que pueda llamar su script/programa /path/to/your/script como esto

MYPUBLICIP=80.190.1.3 /path/to/your/script

esto funciona incluso en crontab.

Enumerar todas las interfaces y eliminar las que no desea

La forma desesperada si no se puede utilizar ip

Si no sabe lo que no quiere, puede enumerar todas las interfaces y ignora todos los falsos.

Aquí ya parece haber una respuesta https://stackoverflow.com/a/265978/490291 para este enfoque.

Hazlo como DLNA

El camino del hombre borracho que intenta ahogarse en alcohol

Usted puede tratar de enumerar todas las puertas de enlace UPnP de la red y de esta manera saber una ruta adecuada para algo "externo". Esto incluso podría estar en una ruta a la que su ruta predeterminada no apunta.

Para más información sobre esto quizás ver https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol

Esto le da una buena impresión cuál es su interfaz pública real, incluso si sus puntos de ruta por defecto en otros lugares.

hay aún más

donde la montaña se encuentra con el profeta

enrutadores IPv6 se anuncian para darle la derecha prefijo IPv6. Ver el prefijo le da una pista sobre si tiene alguna IP interna o global.

Puede escuchar las tramas IGMP o IBGP para encontrar una pasarela adecuada.

Hay menos de 2^32 direcciones IP. Por lo tanto, no lleva mucho tiempo en una LAN hacer ping a todos. Esto le da una pista estadística sobre dónde se encuentra la mayoría de Internet desde su punto de vista. Sin embargo, debe ser un poco más sensato que el famoso https://de.wikipedia.org/wiki/SQL_Slammer

ICMP e incluso ARP son buenas fuentes de información de banda lateral de red. También podría ayudarte.

Puede usar la dirección de difusión de Ethernet para contactar a todos sus dispositivos de infraestructura de red que a menudo lo ayudarán, como DHCP (incluso DHCPv6), y así sucesivamente.

Esta lista adicional es probablemente interminable y siempre incompleta, porque cada fabricante de dispositivos de red está afanosamente inventando nuevos agujeros de seguridad sobre cómo detectar automáticamente sus propios dispositivos. Lo que a menudo ayuda mucho en cómo detectar alguna interfaz pública donde no debería haber una.

'Nuff dijo. Fuera.

Cuestiones relacionadas