想玩虛擬網卡一定要玩TUN/TAP(以下簡稱TAP),想玩TAP一定要知道uIP。uIP是一個使用者態實現的一個超級輕量級的麻雀雖小五髒俱全的TCP/IP協議棧,相比lwIP要好用好玩得多,具體怎麼個意思,還是請教它的大帥哥作者吧。本文要說的只是它的一個應用,既如何將TUN模式的虛擬網卡適配成TAP模式的虛擬網卡,這個需求確實是需要的。
1.將資料從TUN字元裝置讀出來後封裝一個以太頭;
simpletun -s -p 12345 -a -i zz0
和標準的uIP不同的是,tun2tap版本的uIP並不從虛擬網卡讀取以太幀,而是從網路通訊端讀取以太幀,虛擬網卡只是tun2tap版本uIP的“上層”,這一點是和標準的uIP的unix/main.c邏輯完全相反的。以下就是新版的main邏輯代碼:
int main(int argc, char **argv){ ....... // 設定本端的IP地址以及MAC,正常邏輯應該在擷取虛擬IP地址以後再設定 uip_ipaddr(ipaddr, 1,2,3,5); uip_sethostaddr(ipaddr); uip_ipaddr(ipaddr, 1,2,3,4); uip_setdraddr(ipaddr); uip_ipaddr(ipaddr, 255,255,255,0); uip_setnetmask(ipaddr); ... // 設定MAC地址 // 初始化和simpletun串連的通訊端 net_init(); while(1) { char raw_tun[UIP_BUFSIZE]; memset(uip_buf, 0, sizeof(uip_buf)); // 從通訊端讀取資料,要麼為ARP,要麼為IP uip_len = net_read(); if(uip_len > 0) { // 如果是IP的話,摘掉以太頭送往TUN網卡 if(BUF->type == htons(UIP_ETHTYPE_IP)) { char *buf2tun = BUF; buf2tun += sizeof(struct uip_eth_hdr); // 送往TUN虛擬網卡 tapdev_send(buf2tun, uip_len-sizeof(struct uip_eth_hdr)); // 如果是ARP的話,回複ARP請求或者... } else if(BUF->type == htons(UIP_ETHTYPE_ARP)) { // 處理ARP uip_arp_arpin(); if(uip_len > 0) { // 回複ARP net_send(); } } } else if(timer_expired(&periodic_timer)) { timer_reset(&periodic_timer); for(i = 0; i < UIP_CONNS; i++) { uip_periodic(i); if(uip_len > 0) { uip_arp_out(); net_send(); } } if(timer_expired(&arp_timer)) { timer_reset(&arp_timer); uip_arp_timer(); } } // 繼續處理TAP網卡讀出的資料,封裝上以太幀頭 memset(uip_buf, 0, sizeof(uip_buf)); uip_len = tapdev_read(raw_tun, UIP_BUFSIZE); if(uip_len > 0) { memcpy(uip_buf+sizeof(struct uip_eth_hdr), raw_tun, uip_len); // 要麼請求ARP,要麼直接發送資料 uip_arp_out(); net_send(); } } return 0;}