Puede que se nos haya presentado alguna vez la necesidad de saber la dirección de un dispositivo desde nuestro programa, y no es plan de ponernos a ejecutar ifconfig o algún programa parecido para hallar la dirección.
Podemos hablar con ioctl() una función destinada a definir y obtener información de dispositivos. Hay cientos de llamadas, aquí comentaremos la llamada SIOCGIFADDR cuya función es la que comentábamos, obtener la dirección de un interfaz de red.
Podemos probar el siguiente programa con el que hallaremos la IP del dispositivo eth0:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <sys/types.h> #include <sys/ioctl.h> #include <net/if.h> #include <string.h> int main() { int sock; struct ifreq ifr; sock=socket(AF_INET, SOCK_DGRAM, 0); if (sock<0) return -1; /* No puedo crear el socket */ ifr.ifr_addr.sa_family = AF_INET; strcpy(ifr.ifr_name, "eth0"); if (ioctl(sock, SIOCGIFADDR, &ifr) < 0) return -1; /* No encuentro el dispositivo */ printf("IP de eth0: %s\n", inet_ntoa( (*(struct in_addr *) &ifr.ifr_addr.sa_data[2]))); close(sock); return 0; } |
Primero creamos un socket, luego definimos la familia y el dispositivo que queremos consultar con:
1 2 |
Para comprender cómo obtenemos la dirección debemos echar un ojo a las estructuras:
(struct ifreq)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | struct ifreq { char ifr_name[IFNAMSIZ]; /* Nombre de la interfaz */ union { struct sockaddr ifr_addr; struct sockaddr ifr_dstaddr; struct sockaddr ifr_broadaddr; struct sockaddr ifr_netmask; struct sockaddr ifr_hwaddr; short ifr_flags; int ifr_ifindex; int ifr_metric; int ifr_mtu; struct ifmap ifr_map; char ifr_slave[IFNAMSIZ]; char ifr_newname[IFNAMSIZ]; char * ifr_data; }; } |
(struct sockaddr)
1 2 3 4 5 | struct sockaddr { unsigned short sa_family; /* familia de la dirección */ char sa_data[14]; /* 14 bytes de la dirección del protocolo */ }; |
(struct sockaddr_in)
1 2 3 4 5 6 7 | struct sockaddr_in { short int sin_family; /* Familia de la Dirección */ unsigned short int sin_port; /* Puerto */ struct in_addr sin_addr; /* Dirección de Internet */ unsigned char sin_zero[8]; /* Del mismo tamaño que struct sockaddr */ }; |
(struct in_addr)
1 2 3 4 | struct in_addr { unsigned long s_addr; }; |
Bien, en el programa anterior, la línea más conflictiva es:
1 |
En ella, sacaremos la dirección en forma de struct in_addr que es el que le gusta a inet_ntoa() (esta función sólo coge la dirección y la pone en forma de char* para que podamos entenderla.
Para llegar al struct in_addr, partimos de la estructura struct ifreq (variable ifr) que nos devuelve ioctl(), ésta tiene un campo llamado ifr_addr de tipo struct sockaddr. El tipo struct sockaddr y struct sockaddr_in podemos compararlos, es decir, podemos ponerlos uno encima de otro y pueden tener la misma información en el mismo byte, por tanto, podemos ver que el campo sa_data de un struct sockaddr coincide con sin_port, sin_addr y sin_zero de un struct sockaddr in; vemos que sin_port es de tipo unsigned short por tanto ocupará 2 bytes y como queremos la información encerrada en sa_data a partir del byte 2, de ahí viene dicho número. Si queremos ponerlo de otra forma, ésta podría ser:
1 2 | struct sockaddr_in sain; printf("IP de eth0: %s\n", inet_ntoa( (*(struct in_addr *) &ifr.ifr_addr.sa_data[sizeof(sain.sin_port)]))); |
che esta muy bueno y es re practico y no hace falta nada extra, yo probe un pcap un par de ves pero muy al cohete usar varios include y un linkeo a pcap para averiguar solo eso,
muy bueno el post! quedo re simple 😀
Por eso lo publiqué ! Yo también lo intenté con pcap, pero al final con algo muy sencillo también se puede… el próximo paso será prepararlo para IPv6
Pingback: Poesía binaria » Hallar información de un dispositivo de red en C /
Pingback: Conversaciones sobre Programación. Build 2014.01 | Linea de Codigo /