OpenWrt导入指定流量到虚拟网卡tun上
OpenWrt指定网卡流量到虚拟网卡tun上,实际用到的策略路由方案操作如下:
/etc/config/network配置文件新增内容config interface 'tun' option proto 'none' option ifname 'tun0'
防火墙增加如下:
#新增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'


启动tun0程序,并读取tun0上的数据如下:
#include <string.h> #include <fcntl.h> #include <sys/socket.h> #include <linux/ip.h> #include <linux/if_tun.h> #include <arpa/inet.h> #include <errno.h> #include <linux/if_ether.h> #include <linux/if_packet.h> #include <sys/un.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <netinet/in.h> #include <unistd.h> #include <net/if.h> #include <uv.h> #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; }
启动该程序

添加识别规则
#源地址为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
添加路由规则
#添加自定义的路由规则,识别为标签为2的流量 使用路由表 201 ip rule add fwmark 2 table 201 #给路由表20 添加默认的路由规则,默认路由表201的 流量走 tun0 口出 ip route add default dev tun0 table 201
设置好就会看到下面的结果

高级进阶
重新定义路由表编号
上面第5步提到,导入fwmark 标记为2的 流量到路由表201 ,我们可以给这个编号201起个名字如下:

图中说明:给路由表201起的名字为 tuntable,
注意:第一次可以使用
echo "201 tuntable" >> /etc/iproute2/rt_tables命令直接增加该条目。那么上面的第5步就可以改为下面命令:
#添加自定义的路由规则,识别为标签为2的流量 使用路由表 tuntable ip rule add fwmark 2 table tuntable #给路由表201 即 tuntable 添加默认的路由规则,默认路由表tuntable的 流量走 tun0 口出 ip route add default dev tun0 table tuntable
重新定义iptable链
上面第4步提到直接修改mangle表的PREROUTING链,新增MARK的规则,这里可以自定义一条链,在我们自定义的链上操作:
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上
#添加自定义的路由规则,识别为标签为2的流量 使用路由表 tuntable ip rule add fwmark 2 table tuntable #给路由表201 即tuntable 添加默认的路由规则,默认路由表tuntable的 流量走 tun0 口出 ip route add default dev tun0 table tuntable
常用的调试手段
查看表mangle上的流量
iptables -t mangle -L -n -v
查看路由表里面的路由规则
ip route show table 201 #或者 ip route show table tuntable
查看路由rule
ip rule list
删除静态路由
route del -net 192.168.40.0/24
删除路由规则
#ip rule show 先用该命令查看 然后执行下面的命令, num就是第几号的意思 ip rule delete num
删除防火墙链的规则
iptables -t mangle -D PREROUTING 1 #1 代表第一条规则
刷新路由表
ip route flush cache
修改内核参数
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上,不知为啥。这里列出只是作下记录笔记
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