Linux支持基于TAP/TUN的虚拟网卡实现(TAP实现2层协议,TUN实现3层协议),根据这个特点我们可以实现针对某种物理层的特定网卡,如串口。
下面是大概的设计思路:
1、打开内核的TAP/TUN支持。
位于:Device Drivers->Network Device Support->Universe TAP/TUN device driver support。
2、根据不同的内核版本,TAP驱动可能位于/dev/tun或者/dev/net/tun下,打开设备:
fd = open("/dev/tun", O_RDWR);
3、设置驱动属性及设备名:
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
strncpy(ifr.ifr_name, "tap", IFNAMSIZ);
ioctl(fd, TUNSETIFF, (void *)&ifr);
成功则表示虚拟网卡已经建立完毕,可以通过ifconfig tap up启用网卡并设置相关属性。
但这时虚拟网卡还不能做任何有意义的工作,因为没有对接任何物理设备,接下来我们分别对接串口和eth0。
1、串口
struct termios Opt;
if_fd = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY);
cfmakeraw(&Opt);
cfsetispeed(&Opt, B115200);
cfsetospeed(&Opt, B115200);
tcsetattr(if_fd, TCSANOW, &Opt);
串口属性设置完毕。
2、eth0
创建一个raw socket并设置eth0为混杂模式,上传所有数据到tap虚拟网卡。
struct sockaddr_ll sll;
if_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, "eth0");
ioctl(if_fd, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags |= IFF_PROMISC;
ioctl(if_fd, SIOCSIFFLAGS, &ifr);
ioctl(if_fd, SIOCGIFINDEX, &ifr);
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons(ETH_P_ALL);
bind(if_fd, (struct sockaddr *)&sll, sizeof(sll));
eth0设置完毕。
3、服务线程
创建2个线程,分别处理上行和下行数据:
len = read(if_fd, _buf, 2000);
if(len > 0) write(fd, _buf, len);
和:
len = read(fd, _buf, 2000);
if(len > 0) write(if_fd, _buf, len);
至此,这个虚拟网卡已经可以通过串口或者eth0与外界通信了。