Publi

Hallar la IP de un dispositivo en C

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
  ifr.ifr_addr.sa_family = AF_INET;
  strcpy(ifr.ifr_name, "eth0");

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
  printf("IP de eth0: %s\n", inet_ntoa( (*(struct in_addr *) &ifr.ifr_addr.sa_data[2])));

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

También podría interesarte...

There are 4 comments left Ir a comentario

  1. Sergio /
    Usando Google Chrome Google Chrome 18.0.1025.162 en Linux Linux

    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 😀

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 11.0 en Ubuntu Linux Ubuntu Linux

      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

  2. Pingback: Poesía binaria » Hallar información de un dispositivo de red en C /

  3. Pingback: Conversaciones sobre Programación. Build 2014.01 | Linea de Codigo /

Leave a Reply