frp 原理与代码分析(1):ssh访问内网机器

以内网穿透,通过ssh访问内网机器为例,讲解frp的原理

简单的原理如下:

frp 原理与代码分析(1):ssh访问内网机器

简单说,就是建立上图的三个通道,并把三个通道进行连通,即实现了ssh的内网穿透。ssh代理的简单流程如下:

1. frpc登录frps,frps要求frpc与其建立新的工作连接。frpc建立新的工作连接。

2. frpc请求建立Tcp代理,frps建立Tcp代理。

3. 用户连接Tcp代理,frps通知frpc有用户连接ssh,frpc连接本地ssh服务。

4. frps连通用户连接和(frps和frpc建立的新的工作连接),frpc连通本地的ssh连接和(frps和frpc的建立的新的工作连接),至此,通道打通。

frpc客户端开启Tcp代理,与用户ssh登录内网机器的,具体服务端frps和客户端frpc交互流程如下:

frp 原理与代码分析(1):ssh访问内网机器
frps与frpc交互流程图

 

1. frpc和frps建立tcp连接,frpc通过yamux,打开一个数据流stream(暂时命名为stream 1),在该数据流上发送Login消息给frps。(yamux 依赖底层可靠的连接(tcp、unix domain sockets),在可靠的连接之上提供了面向流 I/O 多路复用,其核心思想是在发送数据的时候增加头部信息,附加一层协议。 在 yamux 中,一条连接称为 session,一个 session 可以开启多个数据流,数据流称为 stream。)

frp 原理与代码分析(1):ssh访问内网机器
yamux I/O多路复用

2. frps接收到frpc打开的stream上的Login消息,创建control控制器,control负责该stream上消息的收发和处理;启动control,control启动后响应LoginResp消息,并发送ReqWorkConn消息,该消息的作用是,要求frpc主动与frps开启一个或者多个stream,这些stream会被用于后面用户数据的转发。

3. frpc收到该消息后,主动开启stream,开启的stream相当于数据通道,第一步frpc和frps开启的stream相当于控制通道,控制通道用于传输控制消息。

frpc每收到一条ReqWorkConn消息,开启一个线程,主动与frps开启一个数据流,并在该数据流上发送一个NewWorkConn消息,发送完成后,阻塞在等待该数据流上的StartWorkConn消息。

4. frps收到frpc打开的stream的上NewWorkConn消息,把该stream放入连接池(数据流池)中。

5. frpc在stream 1上发送NewProxy消息,消息内容包括(remote_port,proxy_name,proxy_type等,remote_port是tcp代理监听的端口,即用户即将连接的ssh端口;proxy_type是Tcp)。

6. frps收到stream 1上frpc发来的NewProxy消息,frps的control控制器负责处理该消息。

control根据NewProxy消息内容,创建tcp代理,在NewProxy中的remote_port上开启tcp监听,等待用户主动连接。

7. frps创建的tcp代理,接收到用户的ssh tcp连接,在第四步中的连接池中,拿出一个stream,在该stream上发送一个StartWorkConn消息,通知frpc该stream开始工作。并把用户连接与该stream进行数据连通(即相互把从自己连接上收到的数据,通过对方的连接发送出去,实现一种管道互通的效果)。

8. frpc端,一个阻塞等待一个stream上StartWorkConn消息的线程收到该消息,主动与本地ssh(127.0.0.1:22)建立一个tcp连接。并把stream和该tcp连接进行数据连通。至此,所用tcp通道已经打通,用户ssh可以正常登录了。

注:这种数据连通是阻塞的,直到任何一个连接上发生错误,发生错误后,会同时关闭两个连接。因为一个完整的内网穿透ssh通道,包含三个TCP连接((用户创建的ssh连接)与(frps和frpc建立的连接)连通,(frps和frpc建立的连接)与(frpc和ssh服务器建立的连接)连通),任何一个连接关闭,其他的连接也会感知到,也会一同关闭。即代码中Join函数的精妙之处。