组网环境:
A为host上的虚拟机,使用host-only的方式连接。
使用网上《Linux的TUN/TAP编程》中的例子,打开一个tun0端口,响应此端口收到的icmp报文。代码如下:
#include <sys/ioctl.h> #include <linux/if_tun.h> #include <stddef.h> #include <net/if.h> #include <fcntl.h> #include <assert.h> int tun_create(char *dev, int flags) { struct ifreq ifr; int fd, err; assert(dev != NULL); if ((fd = open("/dev/net/tun", O_RDWR)) < 0) //open character device return fd; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags |= flags; if (*dev != '\0') strncpy(ifr.ifr_name, dev, IFNAMSIZ); err = ioctl(fd, TUNSETIFF, (void *)&ifr); if (err < 0) { close(fd); return err; } strcpy(dev, ifr.ifr_name); return fd; } int main(int argc, char *argv[]) { int tun, ret; char tun_name[IFNAMSIZ]; unsigned char buf[4096] = {0}; tun_name[0] = '\0'; tun = tun_create(tun_name, IFF_TUN | IFF_NO_PI); if (tun < 0) { perror("tun_create"); return 1; } printf("TUN name is %s\n", tun_name); while (1) { unsigned char ip[4]; ret = read(tun, buf, sizeof(buf)); if (ret < 0) break; int i = 0; for (i = 0; i < ret; i++) { printf("%02x ", buf + i); if (i % 16 == 0) printf("\n"); } printf("\n"); memcpy(ip, &buf[12], 4); memcpy(&buf[12], &buf[16], 4); memcpy(&buf[16], ip, 4); buf[20] = 0; *((unsigned short*)&buf[22]) += 8; printf("read %d bytes\n", ret); ret = write(tun, buf, ret); printf("write %d bytes\n", ret); } return 0; }
运行程序,在A上打开tun0接口:
tony@ubuntu-a:~/code/nat-pt$ sudo ifconfig tun0 0.0.0.0 up
并增加一条路由,将目的IP为10.10.10.1的报文送至tun0接口:
tony@ubuntu-a:~/code/nat-pt$ sudo route add 10.10.10.1 dev tun0
在A上还要添加一条规则,将A的eth1接口收到的icmp报文的目的地址改为10.10.10.1:
tony@ubuntu-a:~/code$ sudo iptables -t nat -A PREROUTING -d 192.168.16.130 -p icmp -j DNAT --to-destination 10.10.10.1
这样,我们ping 192.168.16.130这个地址的时候,icmp报文被送到host后,目的地址变成10.10.10.1,然后命中路由,送至tun0,经过处理后,从tun0发出去。