【问题标题】:How does Android VpnService protect fd work?Android VpnService 保护 fd 是如何工作的?
【发布时间】:2016-12-01 01:19:51
【问题描述】:

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

保护套接字免受 VPN 连接后,通过此套接字发送的数据将直接进入底层网络,因此其流量不会通过 VPN 转发。如果某些连接需要保留在 VPN 之外,此方法很有用。例如,如果 VPN 的目的地被 VPN 路由覆盖,VPN 隧道应该保护自己。否则,它的传出数据包将被发送回 VPN 接口并导致无限循环。如果应用程序未准备好或被撤销,此方法将失败。

我知道 Android 是建立在 Linux 之上的,这个函数背后使用了哪些 Linux 机制或实用程序,使通过这个套接字发送的数据绕过 VPN,而所有其他都通过 VPN?

【问题讨论】:

    标签: android linux sockets vpn


    【解决方案1】:

    简而言之,Android VPNService 通过策略路由保护 fd 工作,所有通过protected fd 的数据包都会被标记一个特殊的fwmark,所有带有这个fwmark 的数据包都会绕过VPN。

    键码sn-ps如下:

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

    【讨论】:

    • 是否有一种方法可以使用 fwmark 文件编写 protect 函数的相当简单的 C 实现,用于在本机代码中保护 C 套接字,而不必依赖 Java 套接字?与这个问题相关 - stackoverflow.com/questions/44479021/…
    • 你知道这是否是绕过VPN的特别规则吗? 10000: from all fwmark 0xc0000/0xd0000 lookup legacy_system你知道为什么有这么多使用fwmark的附加规则吗?似乎单独使用 vpnservice,即使不保护任何套接字,也能利用 fwmarks?
    • @bennlich Fwmarks 与与Network 对象相关联的网络 ID 相关联。系统管理受保护后将使用哪个Network,并将fwmark 设置为所需的网络ID。
    • 那么,是否有可能手动将网络 ID 设置为 fwmark 中的永久内容(从 C 调用 setsockopt())?
    猜你喜欢
    • 2017-12-05
    • 2013-12-01
    • 2021-10-22
    • 2014-03-30
    • 1970-01-01
    • 1970-01-01
    • 2017-11-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多