# OpenWrt导入指定流量到虚拟网卡tun上 OpenWrt指定网卡流量到虚拟网卡tun上,实际用到的策略路由方案操作如下: 1. `/etc/config/network`配置文件新增内容 ```bash config interface 'tun' option proto 'none' option ifname 'tun0' ``` 2. 防火墙增加如下: ``` #新增tun 区域 config zone option name 'tun' option input 'ACCEPT' option output 'ACCEPT' option forward 'ACCEPT' option network 'tun' #新增允许lan0 区域到tun区别的转发功能 config forwarding option dest 'tun' option src 'lan0' ``` ![](media/image-20240402145841838.png) ![](media/image-20240402150056087.png) 3. 启动tun0程序,并读取tun0上的数据如下: ```c #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fr_log.h" #if !defined(LOG_TAG) #define LOG_TAG "main" #endif #include "elog.h" int tun_create(char* dev,int net_addr) { int tun_fd; struct ifreq ifr; // 打开tun设备 tun_fd = open("/dev/net/tun", O_RDWR); if(0 > tun_fd){ log_e("open tun file error!"); return -1; } // 设置tun设备的标志位IFF_TUN | IFF_NO_PI memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ); if(0 > ioctl(tun_fd, TUNSETIFF, &ifr)){ log_e("Failed to set TUN device name:%s",strerror(errno)); close(tun_fd); return -1; } // 创建socket 原始编程 int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if(0 > ioctl(sock, SIOCGIFFLAGS, &ifr)){ log_e("Failed to bring up socket:%s",strerror(errno)); close(tun_fd); return -1; } // 设置socket 属性 up ifr.ifr_flags |= IFF_UP | IFF_RUNNING | IFF_PROMISC; if(0 > ioctl(sock, SIOCSIFFLAGS, &ifr)){ log_e("Failed to set socket flags:%s",strerror(errno)); close(tun_fd); return -1; } // 设置tun设备的ip地址 ifr.ifr_addr.sa_family = AF_INET; ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = net_addr; if(0 > ioctl(sock, SIOCSIFADDR, &ifr)){ log_e("Failed to set socket address:%s",strerror(errno)); close(tun_fd); return -1; } // 设置tun设备的掩码地址 ifr.ifr_netmask.sa_family = AF_INET; ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0"); if(0 > ioctl(sock, SIOCSIFNETMASK, &ifr)){ log_e("Failed to set socket netmask:%s",strerror(errno)); close(tun_fd); return -1; } // 获取tun设备的index if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0){ log_e("Failed to get socket index:%s",strerror(errno)); close (tun_fd); return -1; } // 根据index 绑定 struct sockaddr_ll addr; memset (&addr, 0, sizeof (addr)); addr.sll_family = AF_PACKET; addr.sll_ifindex = ifr.ifr_ifindex; addr.sll_protocol = htons(ETH_P_ALL); log_d("addr.sll_ifindex %d" ,addr.sll_ifindex); if (-1 == bind(sock, (struct sockaddr *)&addr, sizeof(addr))){ log_e("binding socket %d with if %s error: %s",sock, dev, strerror(errno)); close (tun_fd); return -1; } // 轮询接收socket的数据 int N_bytes; unsigned char msg[8000]; in_addr ip_addr; ip_addr.s_addr = net_addr; while(true){ N_bytes = recv (sock, msg, 8000, 0); if (N_bytes > 0){ log_d("%s sock recv msg len: %3d, msg: ", inet_ntoa(ip_addr), N_bytes); } } } int main(int argc, char *argv[]) { fr_log_init(); char tun_name[ IFNAMSIZ]="tun0" ; tun_create(tun_name,inet_addr("10.10.10.1")); return 0; } ``` 启动该程序 ![](media/image-20240402144739471.png) 4. 添加识别规则 ```bash #源地址为192.168.40.200的udp包 标记为 2 iptables -t mangle -A PREROUTING -p udp -s 192.168.40.200 -j MARK --set-mark 2 iptables -t mangle -A PREROUTING -p udp -s 192.168.40.200 -j RETURN ``` 5. 添加路由规则 ```bash #添加自定义的路由规则,识别为标签为2的流量 使用路由表 201 ip rule add fwmark 2 table 201 #给路由表20 添加默认的路由规则,默认路由表201的 流量走 tun0 口出 ip route add default dev tun0 table 201 ``` 6. 设置好就会看到下面的结果 ![](media/image-20240402145547036.png) 7. ## 高级进阶 1. 重新定义路由表编号 上面第5步提到,导入fwmark 标记为2的 流量到路由表201 ,我们可以给这个编号201起个名字如下: ![](media/image-20240402154032727.png) 图中说明:给路由表201起的名字为 tuntable, 注意:第一次可以使用`echo "201 tuntable" >> /etc/iproute2/rt_tables`命令直接增加该条目。 那么上面的第5步就可以改为下面命令: ```bash #添加自定义的路由规则,识别为标签为2的流量 使用路由表 tuntable ip rule add fwmark 2 table tuntable #给路由表201 即 tuntable 添加默认的路由规则,默认路由表tuntable的 流量走 tun0 口出 ip route add default dev tun0 table tuntable ``` 2. 重新定义iptable链 上面第4步提到直接修改mangle表的PREROUTING链,新增MARK的规则,这里可以自定义一条链,在我们自定义的链上操作: ```bash iptables -t mangle -N mymark #在mangle上 新建链 mymark iptables -t mangle -A PREROUTING -j mymark # 先把mangle表上的PREROUTING链上流量导入到mymark链上 #对刚才自定义的mymark链,进行流量识别并,打标为2 iptables -t mangle -A mymark -p udp -s 192.168.40.200 -j MARK --set-mark 2 ``` 紧接着我们可以使用自定义的路由规则把这个打标的流量导入到tun上 ```bash #添加自定义的路由规则,识别为标签为2的流量 使用路由表 tuntable ip rule add fwmark 2 table tuntable #给路由表201 即tuntable 添加默认的路由规则,默认路由表tuntable的 流量走 tun0 口出 ip route add default dev tun0 table tuntable ``` 3. 常用的调试手段 - 查看表mangle上的流量 ```bash iptables -t mangle -L -n -v ``` - 查看路由表里面的路由规则 ```bash ip route show table 201 #或者 ip route show table tuntable ``` - 查看路由rule ```bash ip rule list ``` - 删除静态路由 ``` route del -net 192.168.40.0/24 ``` - 删除路由规则 ```bash #ip rule show 先用该命令查看 然后执行下面的命令, num就是第几号的意思 ip rule delete num ``` - 删除防火墙链的规则 ```bash iptables -t mangle -D PREROUTING 1 #1 代表第一条规则 ``` - 刷新路由表 ``` ip route flush cache ``` - 修改内核参数 ```bash sysctl -w net.ipv4.conf.eth0.rp_filter=0 sysctl -w net.ipv4.all.arp_ignore=1 sysctl -w net.ipv4.conf.all.arp_announce=2 sysctl -w net.ipv4.ip_forward=1 ``` - ## shell 创建tun的方式 **注意**:该方式创建的tun 无法使用上面方式 把流量导入到该tun上,不知为啥。这里列出只是作下记录笔记 ```bash ip tuntap add dev tun0 mod tun ip link set tun0 up ip addr add 10.10.10.1/24 dev tun0 #删除 ip tuntap del dev tun0 mod tun ```