代码编织梦想

注意:该文章中的arp应答部分是有问题的,由于作者现在已经没有实验环境无法再进行修正了,望看该文章的人注意一下

关于tun设备启用tap网卡,就是启用一个字符设备,使用open函数得到一个tun设备的文件描述符,可以使用write和read,或者pcap接口读写网卡,以下的部分为tun设备的控制代码,包括了网卡mac ip mask设置等,主要就是ioctl函数的使用

#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/if_tun.h>
#include <linux/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>

#include <linux/if_ether.h>
#include <netinet/if_ether.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/route.h>

static char * interface_name_cut (char *buf, char **name)  
{  
    char *stat; 

    while (*buf == ' ')                                                                                                                                                                                                                     
        buf++;  
    *name = buf;  
    
    stat = strrchr (buf, ':');  
    *stat = '\0';  
    
    return *name;  
}

//check whether the network card exists.
int check_phy_name(char *interface)  
{  
    FILE *fp;  
    char buf[PROCBUFSIZ];  
    char *name;  

    fp = fopen (_PATH_PROC_NET_DEV, "r");  
    if (fp == NULL) {     
        printf("open proc file error\n");  
        goto EXIT1;
    }     

    fgets (buf, PROCBUFSIZ, fp);  
    fgets (buf, PROCBUFSIZ, fp);  

    while (fgets (buf, PROCBUFSIZ, fp) != NULL) {     
        interface_name_cut(buf, &name);  
        if(strcmp(interface,name)==0) {
            fclose(fp);
            return 1;  
        }
    } 

EXIT1:
    fclose(fp);  
    return -1;  
} 

//设置ip和mask
static int set_addr(char *dev_name, char *ip, int flag)
{
    struct ifreq ifr;
    struct sockaddr_in sin;
    int sockfd;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd == -1) {
        perror("tap");
        goto EXIT2;
    }

    snprintf(ifr.ifr_name, sizeof(dev_name), "%s", dev_name);

    /* Read interface flags */
    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
        perror(ifr.ifr_name);
        goto EXIT2;
    }

    memset(&sin, 0, sizeof(struct sockaddr));
    sin.sin_family = AF_INET;
    inet_aton(ip, &sin.sin_addr);
    memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
   
    if (ioctl(sockfd, flag, &ifr) < 0) {
        perror(ifr.ifr_name);
        goto EXIT2;
    }
    
    close(sockfd);
    return 1;

EXIT2:
    close(sockfd);
    exit(-1);
}

static int get_tun_mac(char *dev_name, uint8_t *tun_mac)
{
    struct ifreq ifr;
    int sockfd, ret;
    uint8_t *mac;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd == -1) {
        perror("tap");
        goto EXIT1;
    }

    snprintf(ifr.ifr_name, sizeof(dev_name), "%s", dev_name);

    ret = ioctl(sockfd, SIOCGIFHWADDR, &ifr);
    if (ret == -1) {
        printf("get tun mac error\n");
        goto EXIT1;
    }   

    mac = (uint8_t *)ifr.ifr_hwaddr.sa_data;
    memcpy(tun_mac, mac, 6);
    
    close(sockfd);
    return 1;

EXIT1:
    close(sockfd);
    exit(-1);
}

static int set_tun_mac(char *dev_name, uint8_t *tun_mac)
{
    struct ifreq ifr;
    int sockfd, ret;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd == -1) {
        perror("tap");
        goto EXIT;
    }

    snprintf(ifr.ifr_name, sizeof(dev_name), "%s", dev_name);
    
    /* Read interface flags */
    if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
        perror(ifr.ifr_name);
        goto EXIT;
    }
    
    ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
    memcpy(&ifr.ifr_hwaddr.sa_data, tun_mac, 6);

    ret = ioctl(sockfd, SIOCSIFHWADDR, &ifr);
    if (ret == -1) {
        printf("set tun mac error\n");
        goto EXIT;
    } 

    close(sockfd);
    return 1;

EXIT:
    close(sockfd);
    return -1;
}

static int set_routing(char *dst, char *gw, char *genmask, char *dev)
{
    int sockfd;
    struct rtentry route;
    struct sockaddr_in *addr;
    int err = 0;

    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("set routing");
        close(sockfd);
        return -1;
    }

    memset(&route, 0, sizeof(route));
    
    route.rt_dev = dev;
    
    addr = (struct sockaddr_in*)&route.rt_gateway;
    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = inet_addr(gw);

    addr = (struct sockaddr_in*) &route.rt_dst;
    addr->sin_family = AF_INET;
    addr->sin_addr.s_addr = inet_addr(dst);

    addr = (struct sockaddr_in*) &route.rt_genmask;
    addr->sin_family = AF_INET;
    inet_aton(genmask, &addr->sin_addr);

    route.rt_flags = RTF_UP | RTF_GATEWAY;
    route.rt_metric = 0;
    if ((err = ioctl(sockfd, SIOCADDRT, &route)) != 0) 
    {
        perror("ioctl routing");
        close(sockfd);
        return -1;
    }
    
    close(sockfd);
    return 1;
}

static int set_gateway(char ip[16])
{
    int sockfd;
    struct sockaddr_in sockaddr;
    struct rtentry rt;
    
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("set gateway");
        close(sockfd);
        exit(-1);
    } 

    memset(&rt, 0, sizeof(struct rtentry));
    memset(&sockaddr, 0, sizeof(struct sockaddr_in));
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = 0;
    inet_aton(ip, &sockaddr.sin_addr);

    memcpy (&rt.rt_gateway, &sockaddr, sizeof(struct sockaddr_in));
    ((struct sockaddr_in *)&rt.rt_dst)->sin_family = AF_INET;
    ((struct sockaddr_in *)&rt.rt_genmask)->sin_family = AF_INET;
    rt.rt_flags = RTF_GATEWAY;

    if (ioctl(sockfd, SIOCADDRT, &rt) < 0)
    {
        perror("ioctl(SIOCADDRT) error in set_default_route\n");
        close(sockfd);
        exit(-1);
    }
    
    close(sockfd);
    return 1;
}

int interface_up(char *name)
{
    int fd;
    struct ifreq ifr;
    
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        fprintf(stderr, "tun : interface sokcet open failed!\n");
        return -1;
    }

    strcpy(ifr.ifr_name, name);
    
    if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)
    {
        perror("ioctl");
        exit(-1);
    }

    ifr.ifr_flags |= IFF_UP;

    if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
    {
        perror("ioctl");
        exit(-1);
    }

    return 1;
}

int interface_down(char *name)
{
    int fd;
    struct ifreq ifr;
    
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0)
    {
        fprintf(stderr, "tun : interface sokcet open failed!\n");
        return -1;
    }

    strcpy(ifr.ifr_name, name);
    
    if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)
    {
        perror("ioctl");
        return -1;
    }

    ifr.ifr_flags &= ~IFF_UP;

    if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
    {
        perror("ioctl");
        return -1;
    }

    return 1;
}

//arp应答就是填充一个mac地址然后对换mac,将数据包发出
static int build_arp_response(int tunfd, char *origin_pkts,int pkts_len)
{
    char buf[128]={0};
    unsigned char mymac[6] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
    struct ethhdr *eth_request = (struct ethhdr *)origin_pkts;
    struct ethhdr *eth_reply = (struct ethhdr *)buf;

    //原本的代码部分存在问题,所以删除了,如果有需要的自行添加,
    //arp的应答只需要修改下行流量的目标mac即可,将ff:ff:ff:ff:ff:ff换成mymac
    //调换ip和mac然后write写数据到tunfd即可
    return 0;
}

int main(int argc, char *argv[])
{
    struct ifreq ifr;
	int tunfd;

    //检测主网卡是否存在 -1 no exist
    if (check_phy_name("tap") == -1)  
    {
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_flags |= (IFF_TAP | IFF_NO_PI);
        strncpy(ifr.ifr_name, "tap", IFNAMSIZ);

        tunfd = open("/dev/net/tun", O_RDWR|O_NONBLOCK);
        if (tunfd == -1)
        {
            fprintf(stderr, "tun : /dev/net/tun open failed\n");
            return -1;
        }

        int ior;
        ior = ioctl(tunfd, TUNSETIFF, (void *)&ifr);
        if (ior < 0)
        {
            fprintf(stderr, "tun : virtual network create failed\n");
            return -1;
        }
    }

	unsigned char mac[6] = {0x43, 0x42, 0x15, 0x57, 0x45, 0x65};
	char ip[] = "192.168.0.1"
	char mask[] = "255.255.255.0";
    set_tap_mac("tap", mac);

    if (set_addr("tap", ip, SIOCSIFADDR) == -1)                       //set ip
        return -1;
    if (set_addr("tap", mask, SIOCSIFNETMASK) == -1)  //set netmask
        return -1;

    if (interface_up("tap") == -1)
    {
        fprintf(stderr, "virtual network up failed!\n");
        return -1;
    }

    if (set_routing("0.0.0.0", ip, "0.0.0.0", "tap") == -1)
    {
        fprintf(stderr, "virtual network set routing failed!\n");
        return -1;
    }
  
    get_tap_mac("tap", mac);
    printf("tun mac : %x:%x:%x:%x:%x:%x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 
    return 1;
}

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/LK_whq/article/details/88960204

绑定多个tap网卡与绑定多个tun网卡-附带tun/tap适配_iteye_3759的博客-爱代码爱编程

TUN/TAP网卡是个好东西,不仅仅在Linux上,在所有支持它的操作系统上,都不愧为一件利器,虽不像瑞士军刀那么万能,然所涉及之处恢恢乎其于游刃必有余地矣。只是,在Windows上,其强大的功能埋没于封闭且花哨的网络协议栈,在Mac OS以及iOS,埋没于各种手到擒来的APP,本质上,TUN/TAP是用来Hack的。 本人曾经在Windows以及Mac

openstack底层技术-虚拟网络设备(bridge,vlan)_hdnrnfgf的博客-爱代码爱编程

转载链接:https://opengers.github.io/openstack/openstack-base-virtual-network-devices-bridge-and-vlan/ Posted on September 24, 2017 by opengers in openstack  openstack底层技术-各种虚拟网络设备一(

c语言创建tap设备并且设置ip_xxb249的博客-爱代码爱编程

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/socket.h> #include <netinet/in.h> #include <net/

cloud_11虚拟网络设备tap tun veth-pair总结_z_ieee的博客-爱代码爱编程

随着虚拟化技术的出现,网络也随之被虚拟化,相较于单一的物理网络,虚拟网络变得非常复杂,在一个主机系统里面,需要实现诸如交换、路由、隧道、隔离、聚合等多种网络功能。 而实现这些功能的基本元素就是虚拟的网络设备,比如 tap、t

Linux arm 内核选项和busybox选项 加载tun模块 -- 创建/dev/tun 字符设备-爱代码爱编程

Linux arm 内核选项和busybox选项 加载tun/tap模块 – 创建/dev/tun 字符设备 可以参考博客1: linux下TUN/TAP虚拟网卡的使用 可以参考博客2:ubuntu下安装tun模块图文详细教程 1.查看tuncrl --h ,帮助说明,其中 -f 指定设备文件 -u 指定用户 ~ # tunctl --h tunc

Android 如何使用原生代码打开 TAP 字符驱动设备?-爱代码爱编程

1、打开TAP驱动设备于 Android 设备上面是几乎不可能得事情 2、原生代码打开TUNTAP驱动设备于 Android 设备上面需要ROOT令牌,仅限TUN设备 3、Android 上面打开TUN驱动设备需要打开两个不同驱动文件,因为不同 Android 发行版不一定都按照套路来。 4、Android 上面通过原生代码(常指 NDK C/C++

linux 虚拟网络设备 tun/tap veth pair_aska小强的博客-爱代码爱编程

Linux 虚拟网络设备 tun/tap veth pair 本篇主要介绍一下 linux 下面的 虚拟网络设备 tun/tap veth pair 随着容器逐步取代虚拟机,成为云基础架构的标准,然而容器的网络管理部分是离不开 Linux虚拟网络设备的,所以了解常用的Linux 虚拟网络设备对于我们云理解网络架构很有帮助 Linux

搭建arm64的qemu环境_麻辣小新的博客-爱代码爱编程

说明 qemu在调试内核方面还是比较方便、效率的。以前基本上多是用arm32的平台,网上大部分资源也是关于arm32的。现在arm64的也比较普遍了,最近刚好要看一些内核的东西,花了2天的时间搭建了这个环境,希望看到的朋友

linux tun:tap 详解_改变自己-do的博客-爱代码爱编程

在计算机网络中,tun与tap是操作系统内核中的虚拟网络设备。不同于普通靠硬件网络适配器实现的设备,这些虚拟的网络设备全部用软件实现,并向运行于操作系统上的软件提供与硬件的网络设备完全相同的功能。 tun/tap是什么? t

利用qemu模拟启动uboot 内核 busybox_qemu uboot-爱代码爱编程

文章目录 1.QEMU是什么2.安装QEMU2.1源码安装2.2联网安装2.3安装交叉编译链 3.Linux内核kernel3.1内核编译 4.Linux根文件系统busybox4.1 编译busy