根据https://developer.android.com/reference/android/net/VpnService.html#protect(int) https://developer.android.com/reference/android/net/VpnService.html#protect(int)
保护套接字不受 VPN 连接影响后,通过该套接字发送的数据将直接进入底层网络,因此其流量将不会通过 VPN 转发。如果某些连接需要保持在 VPN 之外,则此方法非常有用。例如,如果 VPN 隧道的目的地被 VPN 路由覆盖,则 VPN 隧道应该保护自身。否则其出站数据包将被发送回 VPN 接口并导致无限循环。如果申请未准备好或被撤销,此方法将失败。
我知道Android是基于Linux构建的,这个功能后面使用了什么Linux机制或实用程序,使只有通过此套接字发送的数据绕过VPN,但所有其他数据都通过VPN?
简而言之,Android VPNService通过策略路由保护fd工作,所有数据包都经过protected fd
会被特殊标记fwmark
,所有带有此的数据包fwmark
将绕过 VPN。
关键代码片段如下:
// android/frameworks/base/core/java/android/net/VpnService.java
/**
* Protect a socket from VPN connections. After protecting, data sent
* through this socket will go directly to the underlying network,
* so its traffic will not be forwarded through the VPN.
* This method is useful if some connections need to be kept
* outside of VPN. For example, a VPN tunnel should protect itself if its
* destination is covered by VPN routes. Otherwise its outgoing packets
* will be sent back to the VPN interface and cause an infinite loop. This
* method will fail if the application is not prepared or is revoked.
*
* <p class="note">The socket is NOT closed by this method.
*
* @return {@code true} on success.
*/
public boolean protect(int socket) {
return NetworkUtils.protectFromVpn(socket);
}
// android/frameworks/base/core/java/android/net/VpnService.java
/**
* Protect {@code fd} from VPN connections. After protecting, data sent through
* this socket will go directly to the underlying network, so its traffic will not be
* forwarded through the VPN.
*/
public static boolean protectFromVpn(FileDescriptor fd) {
return protectFromVpn(fd.getInt$());
}
// android/system/netd/server/FwmarkServer.cpp
fwmark.permission = permission;
if (setsockopt(*socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue,
sizeof(fwmark.intValue)) == -1) {
return -errno;
}
// android/system/netd/include/Fwmark.h
union Fwmark {
uint32_t intValue;
struct {
unsigned netId : 16;
bool explicitlySelected : 1;
bool protectedFromVpn : 1;
Permission permission : 2;
};
Fwmark() : intValue(0) {}
};
static const unsigned FWMARK_NET_ID_MASK = 0xffff;
以及开启 VPN 服务的应用程序后的路由策略示例:
root@CP8692:/ # ip rule
0: from all lookup local
10000: from all fwmark 0xc0000/0xd0000 lookup legacy_system
11000: from all iif tun0 lookup local_network
12000: from all fwmark 0xc0072/0xcffff lookup tun0
12000: from all fwmark 0x0/0x20000 uidrange 0-99999 lookup tun0
13000: from all fwmark 0x10063/0x1ffff lookup local_network
13000: from all fwmark 0x10071/0x1ffff lookup wlan0
13000: from all fwmark 0x10072/0x1ffff uidrange 0-0 lookup tun0
13000: from all fwmark 0x10072/0x1ffff uidrange 0-99999 lookup tun0
14000: from all oif wlan0 lookup wlan0
14000: from all oif tun0 uidrange 0-99999 lookup tun0
15000: from all fwmark 0x0/0x10000 lookup legacy_system
16000: from all fwmark 0x0/0x10000 lookup legacy_network
17000: from all fwmark 0x0/0x10000 lookup local_network
19000: from all fwmark 0x71/0x1ffff lookup wlan0
21000: from all fwmark 0x72/0x1ffff lookup wlan0
22000: from all fwmark 0x0/0xffff lookup wlan0
23000: from all fwmark 0x0/0xffff uidrange 0-0 lookup main
32000: from all unreachable
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)