简体   繁体   English

Android VpnService如何保护fd工作?

[英]How does Android VpnService protect fd work?

According to https://developer.android.com/reference/android/net/VpnService.html#protect(int) 根据https://developer.android.com/reference/android/net/VpnService.html#protect(int)

After protecting a socket from VPN connections, data sent through this socket will go directly to the underlying network, so its traffic will not be forwarded through the VPN. 保护套接字免受VPN连接后,通过此套接字发送的数据将直接进入底层网络,因此其流量不会通过VPN转发。 This method is useful if some connections need to be kept outside of VPN. 如果某些连接需要保留在VPN之外,则此方法很有用。 For example, a VPN tunnel should protect itself if its destination is covered by VPN routes. 例如,如果VPN路由覆盖其目的地,则VPN隧道应该自我保护。 Otherwise its outgoing packets will be sent back to the VPN interface and cause an infinite loop. 否则,其传出的数据包将被发送回VPN接口并导致无限循环。 This method will fail if the application is not prepared or is revoked. 如果应用程序未准备好或已撤销,则此方法将失败。

I know Android is built upon Linux, what Linux mechanisms or utils are used behind this function, to make just data sent through this socket bypass VPN, but all others go through VPN? 我知道Android是建立在Linux之上的,这个函数背后使用了什么Linux机制或工具,只是通过这个套接字发送数据绕过VPN,但所有其他的都通过VPN?

In short, Android VPNService protecting fd working by policy routing, all packets go through protected fd will be marked with a special fwmark , all packets with this fwmark will bypass VPN. 简而言之,Android VPNService通过策略路由保护fd工作,所有通过protected fd数据包都会标记一个特殊的fwmark ,所有带有这个fwmark数据包都会绕过VPN。

Key code snippets listed as below: 关键代码段如下所示:

// 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;

And an example of routing policy, after an app with VPN service turned on: 在打开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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM