You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在C语言中配置TUN接口IP地址时遇ioctl(SIOCSIFADDR)无效参数错误求助

问题描述

尝试用C语言创建TUN隧道,但在设置IP地址时遇到问题:调用带SIOCSIFADDR参数的ioctl函数时返回“invalid argument”错误。想知道如果该函数不能用于TUN接口,该如何为其配置IP地址,同时不太理解ioctl的工作机制,相关文档对TUN接口的帮助有限。

原代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/if_tun.h>
#include <linux/if.h>
#include <fcntl.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>


#define IP_DEST "127.0.0.1"
#define IP_SOURCE "127.0.0.1"



int tun_alloc(char* dev) {
    //dev will store the name of the tun created

    struct ifreq ifr;
    int fd, err;
    char *clonedev = "/dev/net/tun";

    //open the clone device 
    if( (fd = open(clonedev, O_RDWR)) < 0 ) {
        printf("Error opening directory");
        return fd;
    }

    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = IFF_TUN;  
    if (*dev) {
     strncpy(ifr.ifr_name, dev, IFNAMSIZ);
   }

    //Create the TUN
    if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
        close(fd);
        printf("Error creating the tun");
        return err;
    }
    strcpy(dev, ifr.ifr_name);

    return fd;
}

int main(int argc, char* argv[]) {

    //Creating the TUN interface
    char tun_name[IFNAMSIZ];
    strncpy(tun_name, "tun3", IFNAMSIZ);

    int tunfd = tun_alloc(tun_name);
    if (tunfd < 0) {
        perror("tun_create");
        return 1;
    }
    printf("TUN interface %s created\n", tun_name);

    //Setting its IP Adress
    struct ifreq ifr;
    struct sockaddr_in addr;

    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, tun_name, IFNAMSIZ);

    addr.sin_family = AF_INET;
    if(inet_pton(AF_INET,IP_DEST,&(addr.sin_addr))<0){
        fprintf(stderr,"ERROR with the IP address");
        return 1;
    };

    memcpy(&(ifr.ifr_addr), &addr, sizeof (struct sockaddr));
    if (ioctl(tunfd, SIOCSIFADDR, &ifr) < 0) {
        perror("ioctl");
        exit(1);
    }
    printf("TUN interface %s set IP address to %s\n", tun_name, IP_DEST);

    ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
    if(ioctl(tunfd, SIOCSIFFLAGS, &ifr)<0){
        perror("ioctl");
        exit(1);
    };
    printf("TUN running");
    return 0;
}
问题原因与解决方法

错误根源

你不能用TUN设备的文件描述符(tunfd)来调用SIOCSIFADDR、SIOCSIFFLAGS这类网络接口配置命令。ioctl是通用系统调用,不同文件描述符对应不同设备驱动,支持的命令集合完全不同:

  • tunfd关联/dev/net/tun设备,仅支持TUN专属命令(比如TUNSETIFF、TUNSETOWNER)
  • 网络接口配置类命令(SIOCSIFADDR、SIOCSIFFLAGS等)属于内核网络协议栈范畴,必须用通用网络套接字描述符触发。

正确配置步骤

  1. 创建AF_INET类型的套接字(无需绑定端口,用SOCK_DGRAM类型即可)
  2. 用这个套接字的fd执行SIOCSIFADDR、SIOCSIFFLAGS等配置命令
  3. 配置完成后可关闭该套接字,不影响已创建的TUN设备

修改后的代码(main函数部分)

int main(int argc, char* argv[]) {

    //Creating the TUN interface
    char tun_name[IFNAMSIZ];
    strncpy(tun_name, "tun3", IFNAMSIZ);

    int tunfd = tun_alloc(tun_name);
    if (tunfd < 0) {
        perror("tun_create");
        return 1;
    }
    printf("TUN interface %s created\n", tun_name);

    // 创建用于配置网络接口的套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket");
        close(tunfd);
        return 1;
    }

    //Setting its IP Address
    struct ifreq ifr;
    struct sockaddr_in addr;

    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, tun_name, IFNAMSIZ);

    addr.sin_family = AF_INET;
    if(inet_pton(AF_INET, IP_DEST, &(addr.sin_addr)) < 0){
        fprintf(stderr,"ERROR with the IP address");
        close(sockfd);
        close(tunfd);
        return 1;
    };

    memcpy(&(ifr.ifr_addr), &addr, sizeof(struct sockaddr));
    // 使用sockfd而非tunfd调用ioctl
    if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
        perror("ioctl set addr");
        close(sockfd);
        close(tunfd);
        exit(1);
    }
    printf("TUN interface %s set IP address to %s\n", tun_name, IP_DEST);

    // 设置接口UP状态(先获取当前flags再修改)
    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
        perror("ioctl get flags");
        close(sockfd);
        close(tunfd);
        exit(1);
    }
    ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
    if(ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0){
        perror("ioctl set flags");
        close(sockfd);
        close(tunfd);
        exit(1);
    };
    printf("TUN running");

    // 关闭配置用的套接字
    close(sockfd);
    // tunfd保持打开,用于后续读写数据包
    // close(tunfd);
    return 0;
}

补充说明

  • ioctl核心逻辑:通过fd指定目标设备/子系统,第二个参数指定执行命令,第三个参数是命令参数(通常为结构体),不同子系统命令不能跨fd混用。
  • TUN设备创建后只是虚拟接口的“数据通道”,接口的网络配置(IP、状态、路由等)需通过网络协议栈接口完成,这就是必须用普通套接字fd的原因。

内容的提问来源于stack exchange,提问作者Antoine Bourgeois

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 17:21:07