TUN/TAP设备浅析(二) -- TUN/TAP的编程
這篇文章想詳細闡述一下有關于 TUN/TAP 設備的編程。
其實關于這兩種設備的編程,基本上屬于八股文,大家一般都這么干。
啟動設備之前
有的linux 并沒有將tun 模塊編譯到內核之中,所以,我們要做的第一件事情就是檢查我們的系統是否支持 TUN/TAP 。具體如何檢查和解決,請查看這里http://blog.csdn.net/lishuhuakai/article/details/70305543,這篇文章就不再贅述。
光有tun 模塊還不夠,我們還要創建上篇文章中所提到的文件,運行命令:
% sudo mknod /dev/net/tun c 10 200 # c表示為字符設備,10和200分別是主設備號和次設備號這樣,你到 /dev/net/ 目錄下就可以看到一個名稱為 tun 的文件了。當然這里的 tun 可以改成任意的你喜歡的名稱。
啟動設備
對于TUN設備,我們一般這樣來初始化:
int tun_alloc(char dev[IFNAMSIZ]) // dev數組用于存儲設備的名稱 {struct ifreq ifr;int fd, err;if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { // 打開文件perror("open");return -1;}bzero(&ifr, sizeof(ifr));/* Flags : IFF_TUN - TUN設備* IFF_TAP - TAP設備* IFF_NO_PI - 不需要提供包的信息*/ifr.ifr_flags = IFF_TUN | IFF_NO_PI; // tun設備不包含以太網頭部,而tap包含,僅此而已if (*dev) {strncpy(ifr.ifr_name, dev, IFNAMSIZ); }if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) { // 打開設備perror("ioctl TUNSETIFF");close(fd);return err;}// 一旦設備開啟成功,系統會給設備分配一個名稱對于tun設備,一般為tunX,X為從0開始的編號,對于tap設備// 一般為tapX,X為從0開始的編號strcpy(dev, ifr.ifr_name); // 拷貝設備的名稱至dev中return fd; }如果我們想啟動一個TAP 設備的話,很簡單,將上面的ifr.ifr_flags = IFF_TUN | IFF_NO_PI;改為ifr.ifr_flags = IFF_TAP | IFF_NO_PI;即可,那么我們就啟動了一個 TAP 設備。
設定網絡地址
上面的代碼打開了文件,并且返回了文件的描述符,但是還不夠,對于一張網卡來說,我們還要給其配置網絡地址,有時候甚至是路由信息,網卡才能夠正常地工作。
一旦虛擬的 TUN/TAP 設備啟動成功,我們便可以通過命令來給其設定地址。
我來舉個例子,以一個 TAP 設備為例:
% sudo ip link set dev tap0 up # 啟動tap0網卡,雖然網卡已經啟動,但是此時使用ipconfig命令并不能看到tap0這個設備,因為我們還沒有給其配置ip地址% sudo ip address add dev tap0 10.0.1.5/24 # 給tap0設置ip地址% ifconfig # 此時在ifconfig命令下已經可以看到tap0設備了 tap0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 10.0.1.5 netmask 255.255.255.0 broadcast 0.0.0.0inet6 fe80::1872:80ff:fe20:46e2 prefixlen 64 scopeid 0x20<link>ether 1a:72:80:20:46:e2 txqueuelen 1000 (Ethernet)RX packets 0 bytes 0 (0.0 B)RX errors 0 dropped 0 overruns 0 frame 0TX packets 0 bytes 0 (0.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0% ip route show # 顯示所有的路由信息 default via 192.168.140.2 dev ens33 proto static metric 100 10.0.0.0/24 dev ens39 proto kernel scope link src 10.0.0.130 metric 100 10.0.1.0/24 dev tap0 proto kernel scope link src 10.0.1.5 # 給網卡設定ip后,系統自動添加了路由 192.168.140.0/24 dev ens33 proto kernel scope link src 192.168.140.133 metric 100通過手動敲命令的方式來配置 tap0 設備,略顯麻煩,其實我們可以直接在程序中調用 system 函數:
int run_cmd(char *cmd, ...) {va_list ap;char buf[CMDBUFLEN];va_start(ap, cmd);vsnprintf(buf, CMDBUFLEN, cmd, ap);va_end(ap);if (debug) { // DEBUG模式下輸出信息printf("EXEC: %s\n", buf);}return system(buf); }將上面的命令直接傳遞給 run_cmd 函數即可.
當然,如果你不喜歡這種方式,我們自然還可以有其他的方法,比如說使用下面的函數:
int set_stack_attribute(char *dev) {struct ifreq ifr;struct sockaddr_in addr;int sockfd, err = -1;bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;inet_pton(AF_INET, tapaddr, &addr.sin_addr);bzero(&ifr, sizeof(ifr));strcpy(ifr.ifr_name, dev);bcopy(&addr, &ifr.ifr_addr, sizeof(addr));sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("socket");return -1;}// ifconfig tap0 10.0.1.5 #設定ip地址if ((err = ioctl(sockfd, SIOCSIFADDR, (void *)&ifr)) < 0) {perror("ioctl SIOSIFADDR");goto done;}/* 獲得接口的標志 */if ((err = ioctl(sockfd, SIOCGIFFLAGS, (void *)&ifr)) < 0) {perror("ioctl SIOCGIFADDR");goto done;}/* 設置接口的標志 */ifr.ifr_flags |= IFF_UP;// ifup tap0 #啟動設備if ((err = ioctl(sockfd, SIOCSIFFLAGS, (void *)&ifr)) < 0) {perror("ioctl SIOCSIFFLAGS");goto done;}inet_pton(AF_INET, "255.255.255.0", &addr.sin_addr);bcopy(&addr, &ifr.ifr_netmask, sizeof(addr));// ifconfig tap0 10.0.1.5/24 #設定子網掩碼if ((err = ioctl(sockfd, SIOCSIFNETMASK, (void *) &ifr)) < 0) {perror("ioctl SIOCSIFNETMASK");goto done;}done:close(sockfd);return err; }上面的函數主要干的事情和上面的命令大致相同。
收發數據
收發數據非常簡單,每次讀取返回的文件描述符即可接收數據,沒有數據到來時,會一直阻塞在哪里,當然,你也可以玩一下非阻塞 IO,然后想要發送數據的話,只需要將數據寫入到該文件描述符對應的文件中即可。
作者:Yihulee
鏈接:https://www.jianshu.com/p/ab91f7cd98cd
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
總結
以上是生活随笔為你收集整理的TUN/TAP设备浅析(二) -- TUN/TAP的编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 仅剩4款,本周利率超5%的银行理财公布,
- 下一篇: 信用卡养卡提额最快方法