首先在開篇以前介紹下內網打洞原理node
場景:一個服務器S1在公網上有一個IP,兩個私網機器C1,C2linux
C1,C2分別由NAT1和NAT2鏈接到公網,咱們須要藉助S1將C1,C2創建直接的TCP鏈接,即由C1向C2打一個洞,讓C2能夠沿這個洞直接鏈接到C1主機,也就成了局域網訪問的模式。git
實現過程以下:github
n2n項目開源地址:http://github.com/ntop/n2n算法
其實現核心是利用虛擬網卡巧妙實現了網絡隧道的封裝,只利用了tap設備,實用twofish加密接口lzo數據壓縮實現了內網通信。對於我的來講,很是適合創建家庭組網的遠程訪問和管理,本人就基於該方案實現了家中NAS外網訪問的部署。同時基於代碼量很小,實現很巧妙,對其源碼進行了初步閱讀。bash
對幾個核心點進行記錄服務器
typedef struct tuntap_dev { int fd; u_int8_t mac_addr[6];//MAC地址 u_int32_t ip_addr, device_mask;//IP地址與子網掩碼 u_int mtu;//mtu值 } tuntap_dev;//定義虛擬網卡的數據結構 enum packet_type { packet_unreliable_data = 0, /* no ACK needed */ packet_reliable_data, /* needs ACK */ packet_ping, packet_pong };//定義數據包的類型 struct peer_addr { u_int8_t family; u_int16_t port; union { u_int8_t v6_addr[16]; u_int32_t v4_addr; } addr_type; };//定義節點地址端口信息數據結構,預留了IPv6 struct n2n_packet_header { u_int8_t version, msg_type, ttl, sent_by_supernode; //版本號、消息類型、(ttl還有待查明)、服務器節點轉發標示位 char community_name[COMMUNITY_LEN], src_mac[6], dst_mac[6]; //客戶所處子網社區名稱、源MAC、目的MAC struct peer_addr public_ip, private_ip; //節點公網地址端口、私網地址端口信息 enum packet_type pkt_type;//數據包類型 u_int32_t sequence_id;//序列號 u_int32_t crc; // 校驗位,用來區分僞造數據包 };//n2n數據包頭信息 struct peer_info { char community_name[16], mac_addr[6];//子網社區名、MAC地址 struct peer_addr public_ip, private_ip;//公網地址端口、私網地址端口 time_t last_seen;//時間信息 struct peer_info *next;//下一個節點 /* socket */ n2n_sock_info_t sinfo;//sock信息 };//節點信息 struct n2n_edge { u_char re_resolve_supernode_ip; struct peer_addr supernode;//服務器地址端口信息 char supernode_ip[48];//服務器IP地址 char * community_name;//子網社區名,默認爲NULL n2n_sock_info_t sinfo;//sock信息 u_int pkt_sent;//標示位,具體含義待查.默認爲0 tuntap_dev device;//虛擬網卡設備 int allow_routing;//路由轉發標示位,默認爲0 int drop_ipv6_ndp;//標示位,具體含義待查.默認爲0 char * encrypt_key;//加密密鑰 TWOFISH * tf; struct peer_info * known_peers /* = NULL*/; struct peer_info * pending_peers /* = NULL*/; time_t last_register /* = 0*/; };//客戶節點數據結構,最重要的數據結構
發送Gratuitous ARP廣播網絡
使用: static void send_grat_arps(n2n_edge_t * eee,) { char buffer[48]; size_t len; traceEvent(TRACE_NORMAL, "Sending gratuitous ARP..."); len = build_gratuitous_arp(buffer, sizeof(buffer)); send_packet2net(eee, buffer, len); send_packet2net(eee, buffer, len); /* Two is better than one :-) */ } ----包體定義------- static char gratuitous_arp[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* Dest mac */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Src mac idx:6*/ 0x08, 0x06, /* ARP */ 0x00, 0x01, /* Ethernet */ 0x08, 0x00, /* IP */ 0x06, /* Hw Size */ 0x04, /* Protocol Size */ 0x00, 0x01, /* ARP Request */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Src mac idx:22*/ 0x00, 0x00, 0x00, 0x00, /* Src IP idx:28*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Target mac */ 0x00, 0x00, 0x00, 0x00 /* Target IP idx:38*/ }; static int build_gratuitous_arp(char *buffer, u_short buffer_len) { if(buffer_len < sizeof(gratuitous_arp)) return(-1); memcpy(buffer, gratuitous_arp, sizeof(gratuitous_arp)); memcpy(&buffer[6], device.mac_addr, 6); memcpy(&buffer[22], device.mac_addr, 6); memcpy(&buffer[28], &device.ip_addr, 4); /* REVISIT: BbMaj7 - use a real netmask here. This is valid only by accident * for /24 IPv4 networks. */ buffer[31] = 0xFF; /* Use a faked broadcast address */ memcpy(&buffer[38], &device.ip_addr, 4); return(sizeof(gratuitous_arp)); }
int轉ip地址數據結構
char* intoa(u_int32_t /* host order */ addr, char* buf, u_short buf_len) { char *cp, *retStr; u_int byte; int n; /* addr=268435456 >>>268435456&255=0 //右移8位 >>>1048576&255=0 //右移8位 >>>4096&255=0 //右移8位 >>>16&255|16 */ printf(">>>%d|%d\n",addr,addr&255); addr >>= 8; printf(">>>%d|%d\n",addr,addr&255); addr >>= 8; printf(">>>%d|%d\n",addr,addr&255); addr >>= 8; printf(">>>%d|%d\n",addr,addr&255); cp = &buf[buf_len]; *--cp = '\0'; n = 4; do { //0xff=255 byte = addr & 0xff; *--cp = byte % 10 + '0'; byte /= 10; if (byte > 0) { *--cp = byte % 10 + '0'; byte /= 10; if (byte > 0) *--cp = byte + '0'; } *--cp = '.'; addr >>= 8; } while (--n > 0); /* Convert the string to lowercase */ retStr = (char*)(cp+1); return(retStr); }
linux建立虛擬網卡
tunctl -t tun0 tunctl -t tun1 ifconfig tun0 1.2.3.4 up ifconfig tun1 1.2.3.5 up ./edge -d tun0 -l 2000 -r 127.0.0.1:3000 -c hello ./edge -d tun1 -l 3000 -r 127.0.0.1:2000 -c hello tunctl -u UID -t tunX
-----ip4轉int32 及int32轉ip4----
char *ip_str = "111.0.0.8"; u_int32_t ip = inet_addr(ip_str); printf(">> ip:%d\n",ip); struct in_addr addr1; memcpy(&addr1, &ip, 4); printf(">> ip4_s:%s\n",inet_ntoa(addr1));
其餘待補充併發