UDP打洞原理与N2N内网穿透
UDP打洞原理與N2N內(nèi)網(wǎng)穿透
- UDP打洞原理
- 原理
- Server 端部分代碼
- Client 端部分代碼
- N2N網(wǎng)絡(luò)穿透
- 安裝N2N
- 配置Supernode
- 配置Edgenode
- Ping測試
- 一鍵腳本代碼
UDP打洞原理
通過UDP路由驗(yàn)證實(shí)現(xiàn)NAT穿越是一種在處于使用了NAT的私有網(wǎng)絡(luò)中的Internet主機(jī)之間建立雙向UDP連接的方法。由于NAT的行為是非標(biāo)準(zhǔn)化的,因此它并不能應(yīng)用于所有類型的NAT。
其基本思想是這樣的:讓位于NAT后的兩臺主機(jī)都與處于公共地址空間的、眾所周知的第三臺服務(wù)器相連,然后,一旦NAT設(shè)備建立好UDP狀態(tài)信息就轉(zhuǎn)為直接通信,并寄希望于NAT設(shè)備會在分組其實(shí)是從另外一個(gè)主機(jī)傳送過來的情況下仍然保持當(dāng)前狀態(tài)。
這項(xiàng)技術(shù)需要一個(gè)圓錐型NAT設(shè)備才能夠正常工作。對稱型NAT不能使用這項(xiàng)技術(shù)。
這項(xiàng)技術(shù)在P2P軟件和VoIP電話領(lǐng)域被廣泛采用。它是Skype用以繞過防火墻和NAT設(shè)備的技術(shù)之一。
相同的技術(shù)有時(shí)還被用于TCP連接——盡管遠(yuǎn)沒有UDP成功。
原理
假設(shè)有兩臺分別處于各自的私有網(wǎng)絡(luò)中的主機(jī):A和B;NA和NB是兩個(gè)網(wǎng)絡(luò)的NAT設(shè)備,分別擁有IP地址P1和P2;S是一個(gè)使用了一個(gè)眾所周知的、從全球任何地方都能訪問得到的IP地址的公共服務(wù)器
步驟一:A和B分別和S建立UDP連接;NAT設(shè)備NA和NB創(chuàng)建UDP轉(zhuǎn)換狀態(tài)并分配臨時(shí)的外部端口號
步驟二:S檢查UDP包,看A和B的端口是否是正在被使用的(否則的話N1和N2應(yīng)該是應(yīng)用了端口隨機(jī)分配,這會讓路由驗(yàn)證變得更麻煩)
步驟三:如果端口不是隨機(jī)化的,那么A和B各自選擇端口X和Y,并告知S。S會讓A發(fā)送UDP包到P2:Y,讓B發(fā)送UDP包到P1:X
步驟四:A和B通過轉(zhuǎn)換好的IP地址和端口直接聯(lián)系到對方的NAT設(shè)備;
Server 端部分代碼
//這里是對UDT的啟動記性初始化操作if (UDT::ERROR == UDT::startup()){cout<<"startup: "<<UDT::getlasterror().getErrorMessage()<<endl;}else{cout<<"startup suc..."<<endl;}//socket//像聲明一個(gè)普通的socket一樣聲明一個(gè)UDTSOCKETUDTSOCKET serv = UDT::socket(AF_INET, SOCK_DGRAM, 0);if (UDT::ERROR == serv){cout<<"socket: "<<UDT::getlasterror().getErrorMessage()<<endl;}else{cout<<"client suc..."<<endl;}//聲明udp socket,這里是udp的哈,不是udtint sersocket = socket(AF_INET,SOCK_DGRAM,0);if (SOCKET_ERROR == sersocket){cout<<"udp socket error!"<<endl;}else{cout<<"clientsocket suc..."<<endl;}//為了能夠在局域網(wǎng)中直接進(jìn)行處理,先默認(rèn)設(shè)置兩個(gè)sockaddr_in my_addr,client_addr;my_addr.sin_family = AF_INET;my_addr.sin_port = htons(atoi(argv[1]));my_addr.sin_addr.s_addr = INADDR_ANY;memset(&(my_addr.sin_zero), '\0', 8);bind(sersocket,(struct sockaddr*)&my_addr,sizeof(my_addr));client_addr.sin_family = AF_INET;client_addr.sin_port = htons(atoi(argv[3]));client_addr.sin_addr.s_addr = inet_addr(argv[2]);//client_addr.sin_addr.s_addr = inet_addr("127.0.0.1");memset(&(client_addr.sin_zero), '\0', 8);int mss = 1052;//最大傳輸單位//設(shè)置收發(fā)緩沖區(qū)大小 接收限時(shí) 和地址重用if( !( UDT::ERROR != (UDT::setsockopt(serv, 0, UDT_SNDBUF, new int(32000), sizeof(int)))&& UDT::ERROR != (UDT::setsockopt(serv, 0, UDP_RCVBUF, new int(32000), sizeof(int)))&& UDT::ERROR != (UDT::setsockopt(serv,0,UDT_REUSEADDR,new int(1),sizeof(int)))&& UDT::ERROR != (UDT::setsockopt(serv, 0, UDT_RENDEZVOUS, new bool(true), sizeof(bool))))&& UDT::ERROR != (UDT::setsockopt(serv, 0, UDT_MSS, &mss, sizeof(int)) )){cout<<"udt socket: "<<UDT::getlasterror().getErrorMessage()<<endl;UDT::close(serv);return 0;}//這里是直接將udp的接口綁定在udt的接口之上,如果不這樣做的話是沒法使用UDT中的SOCK_DGRAM的if (UDT::ERROR == UDT::bind2(serv,sersocket)){cout<<"udt bind2:"<<UDT::getlasterror().getErrorMessage()<<endl;return 0;}else{cout<<"bind2 suc"<<endl;}//這里也是關(guān)鍵部分,與client端對應(yīng)的connect操作,就是相互之間的打洞處理if (UDT::ERROR == UDT::connect(serv, (sockaddr*)&client_addr, sizeof(client_addr))){cout << "connect: " << UDT::getlasterror().getErrorMessage();UDT::close(serv);return 0;}else{cout<<"connetc suc"<<endl;}Client 端部分代碼
//startupif (UDT::ERROR == UDT::startup()){cout<<"startup: "<<UDT::getlasterror().getErrorMessage()<<endl;}else{cout<<"startup suc..."<<endl;}//Initialize the UDT libraryUDTSOCKET client = UDT::socket(AF_INET, SOCK_DGRAM, 0);if (UDT::ERROR == client){cout<<"socket: "<<UDT::getlasterror().getErrorMessage()<<endl;}else{cout<<"client suc..."<<endl;}//聲明udp socketint clientsocket = socket(AF_INET,SOCK_DGRAM,0);if (SOCKET_ERROR == clientsocket){cout<<"udp socket error!"<<endl;}else{cout<<"clientsocket suc..."<<endl;}sockaddr_in serv_addr,my_addr;serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(atoi(argv[3]));serv_addr.sin_addr.s_addr = inet_addr(argv[2]);memset(&(serv_addr.sin_zero), '\0', 8);my_addr.sin_family = AF_INET;my_addr.sin_port = htons(atoi(argv[1]));my_addr.sin_addr.s_addr = INADDR_ANY;memset(&(my_addr.sin_zero), '\0', 8);bind(clientsocket,(struct sockaddr*)&my_addr,sizeof(my_addr));int mss = 1052;//最大傳輸單位//設(shè)置收發(fā)緩沖區(qū)大小 接收限時(shí) 和地址重用if( !( UDT::ERROR != (UDT::setsockopt(client, 0, UDT_SNDBUF, new int(32000), sizeof(int)))&& UDT::ERROR != (UDT::setsockopt(client, 0, UDP_RCVBUF, new int(32000), sizeof(int)))&& UDT::ERROR != (UDT::setsockopt(client,0,UDT_REUSEADDR,new int(1),sizeof(int)))&& UDT::ERROR != (UDT::setsockopt(client, 0, UDT_RENDEZVOUS, new bool(true), sizeof(bool))))&& UDT::ERROR != (UDT::setsockopt(client, 0, UDT_MSS, &mss, sizeof(int)))){cout<<"udt socket: "<<UDT::getlasterror().getErrorMessage()<<endl;UDT::close(client);return 0;}if (UDT::ERROR == UDT::bind2(client,clientsocket)){cout<<"udt bind2:"<<UDT::getlasterror().getErrorMessage()<<endl;return 0;}else{cout<<"bind2 suc"<<endl;}// connect to the server, implict bindif (UDT::ERROR == UDT::connect(client, (sockaddr*)&serv_addr, sizeof(serv_addr))){cout << "connect: " << UDT::getlasterror().getErrorMessage();UDT::close(client);return 0;}else{cout<<"connect suc"<<endl;}以上代碼使用UDT協(xié)議進(jìn)行互相打洞,沒有經(jīng)過Server服務(wù)器(沒有),簡單的實(shí)現(xiàn)了網(wǎng)絡(luò)穿透,可穿越防火墻。
N2N網(wǎng)絡(luò)穿透
N2N是一個(gè)開放源代碼的2層跨越3層的×××程序,該程序利用了點(diǎn)對點(diǎn)的架構(gòu)來處理網(wǎng)絡(luò)間的成員關(guān)系和路由,N2N的原理如下圖,在搭建的過程中需要一個(gè)super節(jié)點(diǎn)和多個(gè)edge節(jié)點(diǎn),super節(jié)點(diǎn)建立一個(gè)通信中心,用來路由edge之間的通訊,對于×××使用來說,super node節(jié)點(diǎn)必須有一個(gè)公網(wǎng)的IP地址
安裝N2N
N2N GitHub地址
sudo apt-get install libssl-dev //安裝openssl git clone https://github.com/meyerd/n2n cd n2n/n2n_v2 cmake CMakeLists.txt make make install需要root權(quán)限
配置Supernode
supernode -l 1000 -v >/dev/null & //監(jiān)聽1000端口 root@ubuntu16:# supernode -h //可使用 -h查看命令參數(shù) supernode usage -l <lport> Set UDP main listen port to <lport> // UDP 監(jiān)聽端口 -f Run in foreground. //前臺運(yùn)行 -u <UID> User ID (numeric) to use when privileges are dropped. // 指定運(yùn)行所用的UID -g <GID> Group ID (numeric) to use when privileges are dropped. // 指定運(yùn)行所用的GID -v Increase verbosity. Can be used multiple times. // 輸出比較詳細(xì)的log -h This help message.配置Edgenode
edge -d n2n0 -c mynetwork -k encryptme -a 10.10.10.3 -l X.55.150.X:1000 >/dev/null & -d 虛擬網(wǎng)卡名 -a [static:| dhcp:](虛擬網(wǎng)段ip) -c 用于區(qū)分節(jié)點(diǎn)的組名 -k 用于加密的字符串 -l supernode的IP:端口,可以指定多個(gè)supernode以上為節(jié)點(diǎn)配置,配置完成后 ifconfig 會多出個(gè)虛擬網(wǎng)卡
可在服務(wù)器上配置 supernode節(jié)點(diǎn), 其余內(nèi)網(wǎng)client配置edge節(jié)點(diǎn),-a 自定義ip(網(wǎng)段需一致)-l 參數(shù)填服務(wù)器與監(jiān)聽的端口
Ping測試
搭建成功則可互相ping通
一鍵腳本代碼
shell腳本代碼(抄來的,略作修改),需root權(quán)限運(yùn)行,centos系統(tǒng) apt-get 改為 yum
#!/bin/bash #####此腳本用來實(shí)現(xiàn)安裝N2N的客戶端,實(shí)現(xiàn)內(nèi)網(wǎng)之間的穿透 ####應(yīng)用場景: ###客服的服務(wù)器有A、B、C三臺,其中有一臺可以上外網(wǎng),此處以A為例子,ABC之間的SSH互通 ####N2N的server,即super node為114.114.114.114,端口1000,在阿里云端,可以實(shí)現(xiàn)外網(wǎng)訪問 ####此腳本用來在客戶的內(nèi)網(wǎng)搭建N2N的client,可以實(shí)現(xiàn)和阿里云supernode的通信,這樣通過阿里云端就可以SSH到客戶服務(wù)器內(nèi)網(wǎng) N2N_super_node_ip=100.100.100.100 ###改為自己的服務(wù)器IP N2N_super_node_port=1000 ###自行更改 ###N2N_edge_ip為搭建的edge的IP,需要設(shè)置,網(wǎng)段為10.10.10.* ####但有一個(gè)前提,設(shè)置的這個(gè)IP地址在虛擬局域網(wǎng)中不能沖突,所以需要先判斷IP地址是否沖突 N2N_edge_ip=10.10.10.3 ###自行更改 judge_ip_confilct() {if `ping -c 2 ${N2N_edge_ip} &>/dev/null`;thenecho -e "\033[32m ${N2N_edge_ip} can ping,has client used,please motified N2N_edge_ip,系統(tǒng)退出\033[0m" exit 0elseecho -e "\033[31m ${N2N_edge_ip} not can ping,N2N_edge_ip can be userd\033[0m"fi } check_super_node_service() {ping -c 6 ${N2N_super_node_ip}if `ping -c 2 ${N2N_super_node_ip} &>/dev/null`;thenecho -e "\033[32m super node :${N2N_edge_ip} can ping, N2N server can be used\033[0m"else echo -e "\033[31m super node :${N2N_edge_ip} can not ping ,n2n server can not be used ,please check system quit\033[0m"exitfi } n2n_install_super_node() {if `sudo apt-get install bc &>/dev/null`;thenecho -e "\033[32m yum can be use,starting yum install n2n relative paket:\033[0m"sudo apt-get install subversion gcc-c++ openssl-develecho "git clone install n2n:"git clone https://svn.ntop.org/svn/ntop/trunk/n2nif [ -e n2n ];thenecho "n2n file download successful,beging install n2n"cd n2n/n2n_v2cmake CMakeLists.txtmakemakesudo make installelseecho "n2n file download failed ,has some problems ,please check"exit 0fiecho "n2n install over,beginging start n2n services"supernode -l ${N2N_super_node_port} -v >/dev/null &echo "查看 ps -ef | grep supernode"ps -ef | grep supernodeecho "supernode -l ${N2N_super_node_port} -v >/dev/null &" >> /etc/rc.localelseecho -e "\033[31m yum not can be use,yum install n2 has some problem,please check\033[0m"exitfi } n2n_install_edge_node() {if `sudo apt-get install bc &>/dev/null`;thenecho -e "\033[32m yum can be use,starting yum install n2n relative paket:\033[0m"sudo apt-get install subversion gcc-c++ openssl-develecho "git clone install n2n:"git clone https://github.com/meyerd/n2n.gitif [ -e n2n ];thenecho "n2n file download successful,beging install n2n"cd n2n/n2n_v2cmake CMakeLists.txtmakemakesudo make installelseecho "n2n file download failed ,has some problems ,please check"exit 0fiecho "check super node server is ok or not"check_super_node_serviceecho "n2n install over,begining start edge node"edge -d n2n0 -c mynetwork -k encryptme -M 1200 -a $N2N_edge_ip -l $N2N_super_node_ip:$N2N_super_node_port >/dev/null &echo "查看 ps -ef | grep edge,進(jìn)程是否啟動OK"ps -ef | grep edgesudo echo "edge -d n2n0 -c mynetwork -k encryptme -a $N2N_edge_ip -l $N2N_super_node_ip:$N2N_super_node_port" >> /etc/rc.localelseecho -e "\033[31m yum not can be use,yum install n2 has some problem,please check\033[0m"exitfi } real=`grep -l '\^H' /root/.bash_profile` if [ $? -eq 1 ];then echo 'stty erase ^H' >> /root/.bash_profile source /root/.bash_profile #這幾行主要就是讓在使用read鍵時(shí)能使用回刪鍵。寫錯(cuò)了,回刪了,重啟寫。不用這段的話,回刪鍵會變成亂碼。 fi echo -e '\033[0;33;1m #################nagios################## \033[0m' #讓echo能弄點(diǎn)顏色出來好看點(diǎn)。。。 echo "n2n supernode install please input : 1" echo "n2n edgenode install please input : 2" echo -e '\033[0;33;1m ######################################### \033[0m' read -p "please chose : " frist #定義輸入的值 if [ $frist -eq 1 ];thenn2n_install_super_node elsen2n_install_edge_node fi總結(jié)
以上是生活随笔為你收集整理的UDP打洞原理与N2N内网穿透的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 转载双显示器显示模式介绍
- 下一篇: 利用N2N内网穿透,并实现直连