【问题标题】:Set IP address using SIOCSIFADDR ioctl使用 SIOCSIFADDR ioctl 设置 IP 地址
【发布时间】:2024-01-17 16:45:01
【问题描述】:

我正在尝试使用 Linux 上的 IOCTL 接口获取和设置 IP 地址。 我能够成功获取并设置它。当我设置IP地址时, ifconfig eth0 显示正确的 IP 地址,但随后系统断开连接。 即系统无法ping通。 这是我设置IP地址的代码。如果我遗漏了什么,请告诉我。

struct ifreq ifr;
in_addr_t in_addr;
struct sockaddr_in sin;

memset(&ifr, 0, sizeof(struct ifreq));
memset(&sin, 0, sizeof(struct sockaddr_in));
sockfd = socket(AF_INET, SOCK_STREAM, 0);
sprintf(ifr.ifr_name, "eth0");
in_addr = inet_addr("192.168.101.17");
sin.sin_addr.s_addr = in_addr;
memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
io = ioctl(sockfd, SIOCSIFADDR, (char *)&ifr);

【问题讨论】:

    标签: c ip ip-address ioctl


    【解决方案1】:

    这适用于接口或别名。使用“strace”验证操作是否正确:

    strace ./ifconfig
    
    socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 5
    ioctl(5, SIOCSIFADDR, {ifr_name="eth0:8", ifr_addr={AF_INET, inet_addr("192.168.1.202")}}) = 0
    ioctl(5, SIOCGIFFLAGS, {ifr_name="eth0:8", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
    ioctl(5, SIOCSIFFLAGS, {ifr_name="eth0:8", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0
    close(5)                                = 0
    

    完整的源代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stddef.h>             /* offsetof */
    #include <net/if.h>
    #include <net/if.h>
    #include <linux/sockios.h>
    #include <netinet/in.h>
    #if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
    #include <netpacket/packet.h>
    #include <net/ethernet.h>
    #else
    #include <asm/types.h>
    #include <linux/if_ether.h>
    #endif
    
    #define IFNAME "eth0:2"
    #define HOST "192.168.1.204"
    #define ifreq_offsetof(x)  offsetof(struct ifreq, x)
    
    int main(int argc, char **argv) {
    
            struct ifreq ifr;
            struct sockaddr_in sai;
            int sockfd;                     /* socket fd we use to manipulate stuff with */
            int selector;
            unsigned char mask;
    
            char *p;
    
    
            /* Create a channel to the NET kernel. */
            sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    
            /* get interface name */
            strncpy(ifr.ifr_name, IFNAME, IFNAMSIZ);
    
            memset(&sai, 0, sizeof(struct sockaddr));
            sai.sin_family = AF_INET;
            sai.sin_port = 0;
    
            sai.sin_addr.s_addr = inet_addr(HOST);
    
            p = (char *) &sai;
            memcpy( (((char *)&ifr + ifreq_offsetof(ifr_addr) )),
                            p, sizeof(struct sockaddr));
    
            ioctl(sockfd, SIOCSIFADDR, &ifr);
            ioctl(sockfd, SIOCGIFFLAGS, &ifr);
    
            ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
            // ifr.ifr_flags &= ~selector;  // unset something
    
            ioctl(sockfd, SIOCSIFFLAGS, &ifr);
            close(sockfd);
            return 0;
    }
    

    【讨论】:

    • 请注意,inet_addr 的手册页提到您应该“避免使用它来支持 inet_aton()、inet_pton(3) 或 getaddrinfo(3),它们提供了一种更简洁的方式来指示错误返回。 "
    【解决方案2】:

    也许你忘记设置界面了?

    ioctl(sockfd, SIOCGIFFLAGS, &ifr);
    ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
    ioctl(sockfd, SIOCSIFFLAGS, &ifr);
    

    【讨论】: