Cómo configurar la dirección IP desde C en Linux

5 minutos de lectura

Al usar strace e ifconfig, descubrí que puedo configurar la dirección IP de esta manera:

#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <string.h>

int main(int argc, const char *argv[]) {
    struct ifreq ifr;
    const char * name = "eth1";
    int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);

    strncpy(ifr.ifr_name, name, IFNAMSIZ);

    ifr.ifr_addr.sa_family = AF_INET;
    inet_pton(AF_INET, "10.12.0.1", ifr.ifr_addr.sa_data + 2);
    ioctl(fd, SIOCSIFADDR, &ifr);

    inet_pton(AF_INET, "255.255.0.0", ifr.ifr_addr.sa_data + 2);
    ioctl(fd, SIOCSIFNETMASK, &ifr);

    ioctl(fd, SIOCGIFFLAGS, &ifr);
    strncpy(ifr.ifr_name, name, IFNAMSIZ);
    ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);

    ioctl(fd, SIOCSIFFLAGS, &ifr);

    return 0;
}

Pero no estoy muy contento con esta solución:

inet_pton(AF_INET, "10.12.0.1", ifr.ifr_addr.sa_data + 2);

¿Cuál es la forma “correcta” de hacer esto?

  • ¿Es esto algo que esperarías hacer a menudo? No puedo imaginar que haya una API programática ‘agradable’ para ello, especialmente si así es como lo hace ifconfig. (¿Por qué no leer la fuente de ifconfig?) ¿No sería más seguro cambiar los valores en /etc/sysconfig/network (o donde sea que lo guarde su distribución) y reiniciar las interfaces de red?

    – Rup

    11 de julio de 2011 a las 15:20


  • El código no funciona en mi máquina openSUSE. El comando Ifconfig sigue mostrando la misma IP cada vez que ejecuto el programa

    – 3bdalla

    28 de enero de 2015 a las 8:23

  • Parece que necesita permisos de root. Funcionó. Por cierto, ¿por qué no está satisfecho con la solución que mencionó?

    – 3bdalla

    28 de enero de 2015 a las 9:09

La forma “correcta” de IPv4 sin magia +2:

struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr;
inet_pton(AF_INET, "10.12.0.1", &addr->sin_addr);

Para usar IPv6, transmítalo a sockaddr_in6

  • El código no funciona en mi máquina openSUSE. El comando Ifconfig sigue mostrando la misma IP cada vez que ejecuto el programa después de su actualización.

    – 3bdalla

    28 de enero de 2015 a las 8:23

  • Parece que necesita permisos de root. Funcionó. Por cierto, ¿cuál es la diferencia entre su sugerencia y la función original en la pregunta?

    – 3bdalla

    28 de enero de 2015 a las 9:21

  • ¿ioctl manejará la configuración de la dirección IPv6?

    – Youda008

    27 de diciembre de 2015 a las 19:25

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>           // close()
#include <string.h>           // strcpy, memset(), and memcpy()

#include <netdb.h>            // struct addrinfo
#include <sys/types.h>        // needed for socket(), uint8_t, uint16_t
#include <sys/socket.h>       // needed for socket()
#include <netinet/in.h>       // IPPROTO_RAW, INET_ADDRSTRLEN
#include <netinet/ip.h>       // IP_MAXPACKET (which is 65535)
#include <arpa/inet.h>        // inet_pton() and inet_ntop()
#include <sys/ioctl.h>        // macro ioctl is defined
#include <bits/ioctls.h>      // defines values for argument "request" of ioctl.
#include <net/if.h>           // struct ifreq
#include <linux/if_ether.h>   // ETH_P_ARP = 0x0806
#include <linux/if_packet.h>  // struct sockaddr_ll (see man 7 packet)
#include <net/ethernet.h>

#include <errno.h>            // errno, perror()

#include <netinet/in.h>
#include <net/route.h>    


 /**
    * Create socket function
    */
    int create_socket() {

      int sockfd = 0;

      sockfd = socket(AF_INET, SOCK_DGRAM, 0);
      if(sockfd == -1){
        fprintf(stderr, "Could not get socket.\n");
        return -1;
      }

      return sockfd;

    }

    /**
    * Generic ioctrlcall to reduce code size
    */
    int generic_ioctrlcall(int sockfd, u_long *flags, struct ifreq *ifr) {

      if (ioctl(sockfd, (long unsigned int)flags, &ifr) < 0) {
        fprintf(stderr, "ioctl: %s\n", (char *)flags);
        return -1;
      }
      return 1;
    }

    /**
    * Set route with metric 100
    */
    int set_route(int sockfd, char *gateway_addr,  struct sockaddr_in *addr) {
      struct rtentry route;
      int err = 0;
      memset(&route, 0, sizeof(route));
      addr = (struct sockaddr_in*) &route.rt_gateway;
      addr->sin_family = AF_INET;
      addr->sin_addr.s_addr = inet_addr(gateway_addr);
      addr = (struct sockaddr_in*) &route.rt_dst;
      addr->sin_family = AF_INET;
      addr->sin_addr.s_addr = inet_addr("0.0.0.0");
      addr = (struct sockaddr_in*) &route.rt_genmask;
      addr->sin_family = AF_INET;
      addr->sin_addr.s_addr = inet_addr("0.0.0.0");
      route.rt_flags = RTF_UP | RTF_GATEWAY;
      route.rt_metric = 100;
      err = ioctl(sockfd, SIOCADDRT, &route);
      if ((err) < 0) {
        fprintf(stderr, "ioctl: %s\n",  "mahdi MOAHMMADI Error");
        return -1;
      }
      return 1;
    }

    /**
    * Set ip function
    */
    int set_ip(char *iface_name, char *ip_addr, char *gateway_addr)
    {
      if(!iface_name)
        return -1;
      struct ifreq ifr;
      struct sockaddr_in sin;
      int sockfd = create_socket();

      sin.sin_family = AF_INET;

      // Convert IP from numbers and dots to binary notation
      inet_aton(ip_addr,&sin.sin_addr.s_addr);

      /* get interface name */
      strncpy(ifr.ifr_name, iface_name, IFNAMSIZ);

      /* Read interface flags */
      generic_ioctrlcall(sockfd, (u_long *)"SIOCGIFFLAGS", &ifr);
      /*
      * Expected in <net/if.h> according to
      * "UNIX Network Programming".
      */
      #ifdef ifr_flags
      # define IRFFLAGS       ifr_flags
      #else   /* Present on kFreeBSD */
      # define IRFFLAGS       ifr_flagshigh
      #endif
      // If interface is down, bring it up
      if (ifr.IRFFLAGS | ~(IFF_UP)) {
        ifr.IRFFLAGS |= IFF_UP;
        generic_ioctrlcall(sockfd, (u_long *)"SIOCSIFFLAGS", &ifr);
      }
      // Set route
      set_route(sockfd, gateway_addr    ,  &sin);
      memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr)); 
      // Set interface address
      if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
        fprintf(stderr, "Cannot set IP address. ");
        perror(ifr.ifr_name);
        return -1;
      }             
      #undef IRFFLAGS 

      return 0;
    }

uso:

set_ip("eth0", "192.168.181.128", "192.168.181.1");

La forma “correcta” de hacerlo es generar una copia del programa iproute2 “ip” (en /sbin/ip ) con los parámetros relevantes.

la interfaz ioctl generalmente está obsoleta y no le permite configurar todos los parámetros (por ejemplo, alias de IP sin nombre).

Incluso los demonios como dhcpcd que necesitan cambiar la dirección IP, generalmente lo hacen generando un programa externo… no es como si lo hicieras muy a menudo.

  • Entonces, ¿qué hace iproute2? Debe comunicarse con el núcleo en algún momento.

    – Alan

    12 de julio de 2011 a las 10:58

  • Utiliza el protocolo kernel netlink, que es realmente horrible. no lo hagas

    – MarkR

    12 de julio de 2011 a las 12:12

avatar de usuario
mikel ft

Hola: En la funcion set_ip me da error. Tuve que cambiar de:

inet_aton(ip_addr, &sin.sin_addr.s_addr);

para

inet_aton(ip_addr, &sin.sin_addr);

y funcionó bien.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad