c++ - Obtener la dirección IP de la máquina
linux networking (12)
- Crea un socket
- Realice
ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);
Lea /usr/include/linux/if.h
para obtener información sobre las estructuras ifconf
e ifreq
. Esto debería darle la dirección IP de cada interfaz en el sistema. Lea también /usr/include/linux/sockios.h
para obtener ioctls adicionales.
Esta pregunta es casi la misma que la pregunta anterior Obtener la dirección IP de la computadora local . Sin embargo, necesito encontrar las direcciones IP de una máquina Linux .
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 aclarar un poco las cosas:
- El servidor obviamente tendrá el "localhost": 127.0.0.1
- El servidor tendrá una dirección IP interna (administración): 172.16.xx
- El servidor tendrá una dirección IP externa (pública): 80.190.xx
Necesito encontrar la dirección IP externa para vincular mi aplicación. 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.
Además de lo que ha dicho Steve Baker, puede encontrar una descripción de SIOCGIFCONF ioctl en la página de comando man de netdevice (7) .
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.
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.
- Crear un socket UDP
- Conecte el socket a una dirección externa (el host que eventualmente recibirá la dirección local)
- Usa getsockname para obtener la dirección local
Como la pregunta especifica Linux, mi técnica favorita 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. Un ejemplo se proporciona here .
Para 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 el código fuente de iproute2 (especialmente la aplicación de monitor), así como las funciones de recepción en el kernel. La página man de rtnetlink también contiene información útil.
Encontré la solución ioctl problemática en os x (que es compatible con POSIX, por lo que debería 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 verificar 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;
}
Esto tiene la ventaja de trabajar en muchos sabores de Unix ... y puedes 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;
}
Me gusta la respuesta de jjvainio . 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);
}
No codificarlo con dificultad: 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.
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 autodetectar 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 firewall 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 firewall 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 predeterminada
Esta es mi forma recomendada de autodetectar cosas
Algo como ip r get 1.1.1.1
generalmente te dice la interfaz que tiene la ruta predeterminada.
Si desea recrear esto en su lenguaje de programación / scripting favorito, use strace ip r get 1.1.1.1
y siga el camino de ladrillos amarillos.
Establecerlo con /etc/hosts
Esta es mi recomendación si quieres mantener el control
Puede crear una entrada en /etc/hosts
como
80.190.1.3 publicinterfaceip
Luego puede usar este alias publicinterfaceip
para referirse a su interfaz pública.
Lamentablemente,
haproxy
no asimila este truco con IPv6
Usa el ambiente
Esta es una buena solución para
/etc/hosts
en caso de que no searoot
Igual que /etc/hosts
. pero usa el entorno para esto. Puede probar /etc/profile
o ~/.profile
para esto.
Por lo tanto, si su programa necesita una variable MYPUBLICIP
, puede incluir un código similar (esto es C, puede crear C ++ a partir de él):
#define MYPUBLICIPENVVAR "MYPUBLICIP"
const char *mypublicip = getenv(MYPUBLICIPENVVAR);
if (!mypublicip) { fprintf(stderr, "please set environment variable %s/n", MYPUBLICIPENVVAR); exit(3); }
Entonces puede llamar a su script / programa /path/to/your/script
como este
MYPUBLICIP=80.190.1.3 /path/to/your/script
esto incluso funciona en crontab
.
Enumere todas las interfaces y elimine las que no desea
La manera desesperada si no puedes usar
ip
Si sabe lo que no quiere, puede enumerar todas las interfaces e ignorar todas las falsas.
Aquí ya parece haber una respuesta https://.com/a/265978/490291 para este enfoque.
Hazlo como DLNA
El camino del borracho que trata de ahogarse en alcohol
Puede tratar de enumerar todas las puertas de enlace UPnP en su red y de esta manera descubrir una ruta adecuada para algo "externo". Esto incluso podría estar en una ruta a la que su ruta predeterminada no apunta.
Para obtener más información sobre esto, consulte https://en.wikipedia.org/wiki/Internet_Gateway_Device_Protocol
Esto le da una buena impresión de cuál es su interfaz pública real, incluso si su ruta predeterminada apunta a otra parte.
Hay aún más
Donde la montaña se encuentra con el profeta
Los enrutadores IPv6 se anuncian para proporcionarle el prefijo IPv6 correcto. Ver el prefijo le da una pista sobre si tiene alguna IP interna o global.
Puede escuchar 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á inventando afanosamente 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.
''Dijo Nuff. Fuera.
Puede hacer algo de integración con curl como algo tan fácil como: curl www.whatismyip.org
del intérprete de comandos le dará su IP global. De alguna manera dependes de un servidor externo, pero siempre lo serás si estás detrás de un NAT.
Se puede encontrar una elegante solución con scripts para Linux en: http://www.damnsmalllinux.org/f/topic-3-23-17031-0.html
// 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();
}