内网穿透工具-n2n
2017年04月23日

概述

pc1 <-> center <-> pc2
           ^
           |
           v
pc3 <-> center <-> pc4

P2P在打洞穿透方面优势是挺大的,而且成功打洞之后不通过第三方服务器直接连接,能够不受第三方服务器的带宽限制,而如果没有成功打洞,则可以通过第三方服务器中转,进退皆有路,一定意义上来说是最好的解决方案。
在这一类P2P VPN中比较出名的几种有N2N、Tinc、PeerVPN以及ZeroTier,本次介绍的是N2N

N2N是开源的,其作者是Ntop作者Luca Deri,也是个牛人,可惜的是他现在已经不再维护N2N,也不知道是啥原因,不过还好开源社区有其他人加入了维护,并且已经成熟了,Bug也没啥太严重的,所以没啥问题,还是挺好用的,N2N分为SuperNode和EdgeNode,前者被称为超级节点,能够在EdgeNode之间建立握手并为无法直连的EdgeNode中转数据,是网络的核心部分。当然由于SuperNode的重要性,N2N支持为EdgeNode指定多个SuperNode,以便组成更复杂的网络,并且防止单个SuperNode出问题导致的整个网络瘫痪。

配置部署

N2N的安装其实挺简单的,因为没太多依赖,n2n存在v1和v2两个版本,而且互相不兼容.据说v2在安全性方面得到了增强,但是在实际应用过程中v2版本的服务端,没有对应的android客户端,所以只能选择v1版本,以下部署是v1版

下载编译

cd $HOME
git clone https://github.com/meyerd/n2n
cd n2n/n2n_v1
make

编译得到两个程序supernodeedge,supernode为服务端,放在vps上.edge为客户端,每个需要连入内网的终端都要使用客户端才可以

需要了解一点,edge节点和supernode节点是不冲突的,所以在supernode机器上是可以同时运行edge节点的,所以这个网络可以搞得很复杂的说,就看你需求了
在相同平台下编译的supernode和edge文件直接拷贝过去就能跑,不用再次编译

服务端部署

$HOME/n2n/n2n_v1/supernode -l 9999

服务端的运行很简单,只有一个参数,就是要监听的端口.客户端通过这个端口链接服务端

v1版客户端配置

#edge -a 10.1.1.1 -c <group name> -k <password> -l <vps IP:端口>
edge -a 10.1.1.1 -c mygroup1 -k 123456 -l xxx.xxx.xxx.xxx:9999
  • -a <IP>参数指明客户端在连接supernode后的子网IP,这个IP不要和其他连接的客户端冲突
  • -c <group name>参数标明要加入的群组,相同群组的客户端,会并入一个子网
  • -k <password>参数表示链接这个群组需要的密码
  • -l <vps IP:端口>参数表示要使用的supernode

如果在客户端主机运行时候edge命令,提示找不到libcrypto.so文件,可以把编译环境的libcrypto.so文件复制到客户端主机上

如果在启动客户端程序的时候,有如下提示

ERROR: ioctl() [No such file or directory][2] 

strace ./edge xxxx跟踪调试,发现错误来源于找不到/dev/net/tun文件

ls /dev/net/tun

如果没有这个文件,查看是否引用了tun模块

lsmod tun | grep tun
tun        18784   0
tunnel4     2053   1 sit
ip_tunnel  11448   1 sit

可以看到有tun模块

如果没有加载tun模块,可以手动加载下

modprobe tun
#lsmod tun | grep tun

结果发现模块没有正确加载,也不报错,于是重新查看模块是否存在,再加载

#列出所有可用模块
find /lib/modules -iname 'tun*'
tun.ko

#加载模块
insmod /lib/modules/tun.ko

#重新查看
lsmod | grep tun

再查看虚拟网卡文件是否存在,如果不存在则创建

ls /dev/net/tun
mkdir /dev/net
mknod /dev/net/tun c 10 200

gentoo 部署n2n

  1. 内核添加tun虚拟网卡
-> Device Drivers
    -> Network device support
        -> Network core driver support
            <*>  Universal TUN/TAP device driver support
  1. ifconfig 修复
ln -sf /bin/ifconfig /sbin/ifconfig

n2n的源码会在/sbin目录寻找ifconfig命令,不过gentoo的ifconfig命令在/bin目录

备注:网上还有个说法可以通过创建虚拟网卡完成这些操作

apt-get install uml-utilities 或者 yum install tunctl
tunctl -t tun

相关问题讨论
http://koolshare.cn/thread-126739-1-1.html

#vps主机
cd ~/n2n/n2n_v1/
ldd edge
linux-vdso.so.1 (0x00007ffcff5f3000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fcdc22ac000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcdc1f0d000)
/lib64/ld-linux-x86-64.so.2 (0x00007fcdc24c9000)

#复制对应的.so文件到客户端/lib64目录,重新运行edge命令

针对 N2N_V1,进入 状态 – 系统日志,如果能看到这样开头的信息,每分钟一条,就算与服务器连接好啦!

Received REGISTER_ACK from remote peer ... ...

没有连接成功可能显示的是这样的信息(反正不是上面的信息,就没有成功)

Registering with supernode

对于 N2N_V2,同样的路径下,看到的是这样的开头,每2分钟一次,也算设置完全正确啦:

Rx REGISTER_SUPER_ACK myMAC= ... ...

v2版客户端配置

    edge -d 虚拟网卡名 -a 10.0.0.1 -c testnet -k senrame -l xxx.xxx.xxx.xxxx:port

#参数说明

参数 含义
-d 虚拟网卡名
-a [static: 或者 dhcp:]虚拟网段(IP),static模式其实可以不用加那个static: 直接写IP就行
-c 用于区分节点的社区(组)名
-k 用于加密的字符串
-K 用于加密的Key文件,和-k不能共存
-s 子网掩码
-l supernode的IP:端口,可以指定多个supernode的
-i NAT打洞间隔
-b 当使用DHCP时定期刷新IP
-p 指定本地端口
-u 指定运行所用的UID
-g 指定运行所用的GID
-f 前台运行
-m 为虚拟网卡指定MAC地址
-r 启用包转发,当-a指定DHCP时需要启用
-E 接收组播MAC地址
-v 输出比较详细的log
-t 指定用于管理的UDP端口

#如果创建虚拟网卡出问题了,可以尝试手动创建

apt-get install tunctl
tunctl -t <虚拟网卡名>
然后使用-d指定虚拟网卡名,-m指定MAC来连接,至于uid和gid这个你不是root的情况下可以研究下
另外如果运行在后台的情况下要看问题,可以在/var/log/message文件中查看日志
正常情况下在配置完supernode和edge后就能正常连接了

android配置

1. google play搜索`n2n`,下载安装
2. 打开app,选择扳手图标,如下配置
* n2n v1:勾选
* supernode:xxx.xxx.xxx.xxx:9999        #之前的vps的IP以及supernode监听的端口
* Group name:mygroup1                     #要连接同一个子网的主机需要使用同一个群组
* Password:123456                       #连接群组使用的密码
* Assign IP:10.1.1.2                   #连接到子网后使用的IP,不要和其他客户端冲突
3. 点击Start开始连接

如果在日志中出现以下提示,可能是服务端使用的v2版本,建议客户端更换v1版本重新部署
```
WARNING: Rx REGISTER_SUPER_ACK with wrong or old cookie
```

openwrt部署n2n客户端

安装n2n

```
opkg install n2n
```

修改n2n配置文件

cat /etc/config/n2n  
    config edge  
    option ipaddr           '10.0.0.1'  
    option supernode        'xxx.xxx.xxx.xxx' vps的ip地址 
    option port             '9999'  
    option community        '123456'  
    option key              '123456'  
    option route            '1'  

修改n2n服务的启动脚本

vi /etc/init.d/n2n  
#!/bin/sh /etc/rc.common  
START=90  
start_instance() {  
    local cfg="$1"  
        config_get type "$cfg" TYPE  
    case "$type" in  
        edge)  
            config_get ipaddr "$cfg" 'ipaddr'  
            [ -n "$ipaddr" ] || return 1  
            config_get supernode "$cfg" 'supernode'  
                config_get port "$cfg" 'port'  
                config_get community "$cfg" 'community'  
                config_get key "$cfg" 'key'  
                config_get_bool route "$cfg" 'route' '0'  
                [ "$route" = "1" ] && args='-r'  
                service_start /usr/sbin/edge -f $args -a $ipaddr -c $community -k $key -l ${supernode}:${port} -M 1300  
                    ;;  
        supernode)  
            config_get port "$cfg" port  
            [ -n "$port" ] || return 1  
            service_start /usr/sbin/supernode -l $port  
                ;;  
        esac  
}  
stop_instance() {  
    local cfg="$1"  
        config_get type "$cfg" TYPE  
    case "$type" in  
        edge)  
            service_stop /usr/sbin/edge  
            ;;  
        supernode)  
            service_stop /usr/sbin/supernode  
            ;;  
        esac  
}  
start() {  
    config_load 'n2n'  
        config_foreach start_instance 'edge'  
        config_foreach start_instance 'supernode'  
}  
stop() {  
    config_load 'n2n'  
        config_foreach stop_instance 'edge'  
        config_foreach stop_instance 'supernode'  
}  

将n2n加入启动项

/etc/init.d/n2n enable  

启动n2n服务

/etc/init.d/n2n start
ifconfig edge0

群晖开机自启动n2n

控制面板-任务计划-新建触发任务-自定义脚本-时间默认是开机-写个任务名称
自定义脚本

sleep 60
insmod /lib/modules/tun.ko
/root/edge -a 10.1.1.21 -c mygroup1 -k 123456 -l xxx.xxx.xxx.xxx:9999 &> /root/n2n.log &

群晖web界面添加计划任务-事件-开机执行

n2n gentoo服务

为了自动化完成n2n edge启动,需要单独写个启动脚本

  1. 配置启动脚本
#!/sbin/openrc-run

pidfile=/var/run/edge.pid
logfile=/var/log/edge
name="n2n edge node daemon"

SELF_IP="10.1.1.1"
GROUP_NAME="mygroup1"
N2N_KEY="123456"
SUPERNODE_IP="xxx.xxx.xxx.xxx"
SUPERNODE_PORT="9999"

depend(){
    need dhcpcd
}
start(){

    ebegin "start n2n daemon"
        start-stop-daemon --start   \
        --make-pidfile              \
        --background                \
        --pidfile ${pidfile}        \
        --stdout ${logfile}         \
        --stderr ${logfile}         \
        --exec edge                 \
        -- -a ${SELF_IP}            \
        -c ${GROUP_NAME}            \
        -k ${N2N_KEY}               \
        -l ${SUPERNODE_IP}:${SUPERNODE_PORT}
    eend $?
}

stop(){
    ebegin 'stop edge daemon'
        start-stop-daemon --stop --pidfile ${pidfile}
    eend 0
}
  1. 设置开机启动
rc-update add edge default

总结

n2n 作为一种p2p方式的内网穿透解决方案,在连接速度上来说,几乎可以达到客户机的带宽上线,而同时服务端负载又很小,这是n2n的优点.但是由于n2n是依靠客户端程序p2p连接,所以每个要连接的主机都需要使用客户端.目前windows/linux/android有客户端.
android的客户端只能使用v1版本,而且需要使用root权限.iphone没有对应的客户端程序