switch - ¿Cómo enumerar todas las direcciones IP conectadas a una máquina, en POSIX C?
como saber la ip de una mac address cisco (6)
Fondo:
Estoy escribiendo un daemon que hace conexiones salientes de TCP / IP. Se ejecutará en máquinas con múltiples direcciones IP (sin bucle invertido). Me gustaría que los usuarios puedan especificar, en el archivo de configuración del daemon, qué dirección (es) IP usarán para las conexiones salientes, o *
para usar todo.
Las direcciones se usarán en una rotación, cada conexión saldrá de la dirección IP utilizada menos recientemente. Este comportamiento es importante, ya que *
es un reemplazo para "todos", por lo que los daemons que se ejecutan en varias máquinas pueden apuntar al mismo archivo de configuración en un intercambio de archivos, y cada uno usa su propio conjunto de direcciones IP.
Problema:
¿Cómo obtengo una lista de todas las direcciones IP en las que una máquina puede hacer conexiones salientes (es decir, a cualquier otra computadora)? Dada una lista de todas las direcciones IP, ¿cómo puedo filtrar las direcciones de bucle invertido?
Estoy en C, y si es posible, me gustaría usar POSIX solamente, pero el daemon probablemente solo se ejecute en Linux, así que aceptaría una respuesta centrada en Linux.
Cada dirección IP estará disponible en exactamente un dispositivo de red (posiblemente virtual) y viceversa, por lo que una forma de enumerar los dispositivos de red y obtener direcciones IP asociadas también sería suficiente, aunque realmente no me gustaría. (Preguntas secundarias: ¿es posible asociar múltiples direcciones IP con un solo dispositivo? ¿Qué pasa con la misma IP en varios dispositivos? No es importante).
Soluciones insuficientes:
-
gethostname()
/gethostbyname()
(como esta pregunta ). Usando ese método, solo vuelvo a obtener 127.0.0.1 (o .1.1 en Debian). Sospecho que esto se debe a que el nombre de host de la máquina está en el archivohosts
, y eso es tan lejos comogethostbyname()
comprueba. (Creo que es por eso que en Debian siempre obtengo 127.0.1.1: Debian predetermina agregar localhost como 127.0.0.1 y el nombre de máquina como 127.0.1.1 al archivo dehosts
, ¿no?) Me gustaría una solución que ignore loshosts
y brinde yo todo en realidad allí. - No he tenido más suerte con
getaddrinfo()
quegethostname()
/gethostbyname()
. Parece estar obligado por el mismo problema. Probé esto pasando el nombre de host de la máquina y un servicioNULL
(puerto) en él; los documentos dicen que pasar un nombre de hostNULL
Y un servicioNULL
es ilegal, y esto se respalda con una prueba. No estoy seguro de qué otra manera puedo pedirle todo en la máquina, pero estoy abierto a sugerencias en este sentido. - EDITAR: esta respuesta muestra cómo obtener la dirección IP del nombre de un dispositivo, pero no muestra cómo enumerar los nombres de los dispositivos. ¿Algunas ideas?
EDICIÓN FINAL: He aceptado la respuesta de caskey para darle crédito por haberme señalado cómo hacerlo. He publicado mi propia respuesta que enumera el código fuente de cómo exactamente hacerlo en caso de que alguien más lo necesite.
¿Estás seguro de que estás usando gethostname () / gethostbyname () correctamente? mira aquí , el único problema que veo al hacer esto es que es posible que un nombre de dominio tenga asignadas múltiples direcciones IP. Si ese es el caso, entonces no hay forma de saber cuál es la dirección IP que pertenece a la máquina local.
Algunas respuestas a las preguntas secundarias:
Agregar múltiples direcciones IP a un dispositivo se puede hacer con aliasing. Linux crea dispositivos con el nombre eth0: 0 cuando haces esto.
ifconfig eth0:0 10.0.0.1
Tener la misma IP en varios dispositivos se puede hacer con enlaces de canales / agregación de enlaces.
Esto solo puede hacerse de una manera dependiente del sistema operativo. Podría intentar analizar la salida de ''iptables'', pero la respuesta correcta para linux es usar ioctl.
SIOCGIFCONF takes a struct ifconf *. The ifc_buf field points to a buffer of length ifc_len bytes, into which the kernel writes a list of type struct ifreq [].
La estructura ifreq está documentada en linux / if.h:
struct ifreq
{
#define IFHWADDRLEN 6
union
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
} ifr_ifrn;
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
short ifru_flags;
int ifru_ivalue;
int ifru_mtu;
struct ifmap ifru_map;
char ifru_slave[IFNAMSIZ]; /* Just fits the size */
char ifru_newname[IFNAMSIZ];
void * ifru_data;
struct if_settings ifru_settings;
} ifr_ifru;
};
Como puede ver, contiene la información de dirección que desea.
Puede obtener la información de la interfaz requerida de varias maneras, incluyendo llamar a ioctl () con la opción SIOCGIFCONF y recorrer las estructuras devueltas para obtener la información de la dirección de la interfaz.
Dada una lista de todas las direcciones IP, ¿cómo puedo filtrar las direcciones de bucle invertido?
Ver ifreq struct en la respuesta de caskey. Puede determinar el loopback (correctamente) con:
if (ifru_flags & IFF_LOOPBACK)
Las constantes están en if.h
¿Cómo obtengo una lista de todas las direcciones IP en las que una máquina puede hacer conexiones salientes (es decir, a cualquier otra computadora)? Dada una lista de todas las direcciones IP, ¿cómo puedo filtrar las direcciones de bucle invertido?
Mire el código fuente de lsof y netstat . Verá que implica atravesar estructuras de memoria del kernel, no solo hacer llamadas al sistema.
Aquí está mi código de prueba de concepto usando la respuesta aceptada de caskey , por el bien de la posteridad:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
static const char * flags(int sd, const char * name)
{
static char buf[1024];
static struct ifreq ifreq;
strcpy(ifreq.ifr_name, name);
int r = ioctl(sd, SIOCGIFFLAGS, (char *)&ifreq);
assert(r == 0);
int l = 0;
#define FLAG(b) if(ifreq.ifr_flags & b) l += snprintf(buf + l, sizeof(buf) - l, #b " ")
FLAG(IFF_UP);
FLAG(IFF_BROADCAST);
FLAG(IFF_DEBUG);
FLAG(IFF_LOOPBACK);
FLAG(IFF_POINTOPOINT);
FLAG(IFF_RUNNING);
FLAG(IFF_NOARP);
FLAG(IFF_PROMISC);
FLAG(IFF_NOTRAILERS);
FLAG(IFF_ALLMULTI);
FLAG(IFF_MASTER);
FLAG(IFF_SLAVE);
FLAG(IFF_MULTICAST);
FLAG(IFF_PORTSEL);
FLAG(IFF_AUTOMEDIA);
FLAG(IFF_DYNAMIC);
#undef FLAG
return buf;
}
int main(void)
{
static struct ifreq ifreqs[32];
struct ifconf ifconf;
memset(&ifconf, 0, sizeof(ifconf));
ifconf.ifc_req = ifreqs;
ifconf.ifc_len = sizeof(ifreqs);
int sd = socket(PF_INET, SOCK_STREAM, 0);
assert(sd >= 0);
int r = ioctl(sd, SIOCGIFCONF, (char *)&ifconf);
assert(r == 0);
for(int i = 0; i < ifconf.ifc_len/sizeof(struct ifreq); ++i)
{
printf("%s: %s/n", ifreqs[i].ifr_name, inet_ntoa(((struct sockaddr_in *)&ifreqs[i].ifr_addr)->sin_addr));
printf(" flags: %s/n", flags(sd, ifreqs[i].ifr_name));
}
close(sd);
return 0;
}
¡Funciona de maravilla!