移动/PC客户端流量拦截与转发

2021-12-01 17:01:19 浏览数 (1)

各种VPN客户端实现都离不开流量拦截与转发,那么各个客户端如何拦截流量,以及转发给指定的安全通道就成为了各个客户端所面临的重要问题。首先看下图:

虚拟网卡拦截流量原理虚拟网卡拦截流量原理

启动VPN一般流程为建立虚拟网卡,配置虚拟网卡IP、MTU、修改路由表,配置系统DNS,之后操作虚拟网卡文件描述符,拦截用户指定的流量进行转发。

先说移动端:

//////////////////////////////////Android//////////////////////////////////////

Android 客户端流量拦截,主要依赖VpnService类服务拦截流量,其本质是建立了一个虚拟网卡”/dev/tun“文件,可在启动网卡建立连接前设置路由表,分包规则等。

Intent intent = VpnService.prepare(this);

StartActivityForResult(intent,0) //启动VPN申请权限

VpnService.Builder builder = new VpnService.Builder();

builder.addAddress(VPN_ADDRESS, 32);//vpn网卡的ip builder.addRoute(VPN_ROUTE, ROUTE_PREFIX);//设置路由规则

builder.setMtu(15000);//数据超过多少之后分包

vpnInterface = builder.setSession(app_name).setConfigureIntent(pendingIntent).establish();

FileDescriptor vpnFileDescriptor=vpnInterface.getFileDescriptor(); //获取文件描述符

拿到设备的文件描述符之后,就可以读取网卡拦截到的所有流量

//////////////////////////////////IOS/////////////////////////////////////

IOS 客户端流量拦截,Apple提供了 NetworkExtension 框架,让开发者可以在iOS、Mac os中进行VPN开发。iOS中的VPN开发分为 个人VPN 和 非个人VPN 开发。个人VPN开发比较简单,可以直接使用系统提供的IPSec、IKEv2协议来进行VPN连接。而在iOS9之后,Apple开放了新的api,可以让开发者开发自己的私密协议的VPN。

个人VPN主要依赖 NEVPNManager 类来进行开发,NEVPNManager 用于创建和管理VPN配置并控制最终的VPN隧道连接。参考地址:https://developer.apple.com/reference/networkextension/nevpnmanager 。

然而我们在实际的开发中往往需要开发安全程度更高的私密协议的VPN,以满足客户需求,这就需要用到NetworkExtension框架的另外一个类 NEPacketTunnelProvider,利用该类建立虚拟网卡拦截流量。

NEPacketTunnelProvider,是真正的 vpn 核心代码。项目中 PacketTunnelProvider 是其子类,并且以下两个方法必须实现。

/////////////////////////////////开启隧道//////////////////////////////////////////////////

- (void)startTunnelWithOptions:(NSDictionary<NSString *,NSObject *> *)options completionHandler:(void (^)(NSError * _Nullable))completionHandler {

    [self addObserver:self

           forKeyPath:@"defaultPath"

              options:0

              context:NULL];

    if (!_tmfPacketTunnel) {

        _tmfPacketTunnel = [[TMFPacketTunnel alloc] initWithData:self];

    }

    NETunnelProviderProtocol *protocol = (NETunnelProviderProtocol*) self.protocolConfiguration;

    [_tmfPacketTunnel startTunnelWithOptions:protocol completionHandler:completionHandler];

}

/////////////////////////////////停止隧道//////////////////////////////////////////////////

- (void)stopTunnelWithReason:(NEProviderStopReason)reason completionHandler:(void (^)(void))completionHandler {

    // NEProviderStopReasonUserInitiated:用户主动断开或切换了别的VPN

    // NEProviderStopReasonSuperceded:被其他VPN抢占

    if (_tmfPacketTunnel) {

        [_tmfPacketTunnel stopTunnelWithReason:reason completionHandler:completionHandler];

    }

}

这一步就只做了一件事情: 将配置信息传给系统, 也就是调用setTunnelNetworkSettings方法, 完成建立 VPN 的过程.

   [_tunnelProvider setTunnelNetworkSettings:settings completionHandler:^(NSError * _Nullable error) {

        if (error) {

            if (completionHandler) {

                completionHandler(error);

            }

        } else {

            if (completionHandler) {

                completionHandler(nil);

            }

        }

    }];

官方已经说了, 通过packetFlow来收发系统/app 与服务器进行的通讯数据包.理论上, 只要 VPN 一建立就需要监听往返的数据包了

代码语言:javascript复制
/// Make the initial readPacketsWithCompletionHandler call.
func startHandlingPackets() {
    packetFlow.readPackets { inPackets, inProtocols in
        self.handlePackets(inPackets, protocols: inProtocols)
    }
}
代码语言:javascript复制
// 读数据包进行解析
self.packetFlow.readPackets { inPackets, inProtocols in
     self.handlePackets(inPackets, protocols: inProtocols)
}
代码语言:javascript复制
// 将从服务端接收到的数据包写入虚拟网卡
override func sendPackets(_ packets: [Data], protocols: [NSNumber]) {
    packetFlow.writePackets(packets, withProtocols: protocols)
}

IOS本地读到数据包后想怎么处理就怎么处理,随后可以连接TCP,建立相关通道进行转发之类。

代码语言:javascript复制
let tunFd = self.packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32;

同样拿到虚拟网卡描述符也很重要,可以自定义读取tunFd

PC 端与移动端的最大不同再于建立虚拟网卡,移动端有专门的API给用户创建虚拟网卡,修改路由表,然而PC端创建虚拟网卡,修改路由表,要靠自己编写代码实现,稍微复杂一点。

Windows主要用到命令有:

netsh、route、ipconfig等,具体使用方法可以自行查阅。

Linux主要用到命令有:

ifconfig、route等其它相关系统调用

MAC主要用到有:

系统调用:syscall.AF_SYSTEM、syscall.AF_IOCTL、syscall.AF_CONNECT等

命令行:route、ifconfig、networksetup等

然而无论是移动端还是PC端都能获取其虚拟网卡的文件描述符,因此底层流量拦截库可以统一实现。

下图为TCP状态机

虚拟网卡拦截的数据包均为IPV4包,被TCP解析后如果是Syn包,且是白名单拦截目标,则建立TCP加密连接,之后向服务端发送HTTP-Connect协议建立VPN通道。

当网关返回TCP数据给TCP状态机时,TCP状态机将TCP回复包重新组装为IPV4包,写入虚拟网卡,虚拟网卡转回给用户APP 请求。

服务端必须支持http-connect协议,如果是nginx打包编译时必须添加http-connect模块。

0 人点赞