• Android多网络并存


    1 基本概念
    1.1 Android任何网络类型
    1)与netd打交道通过INetworkManagementService 
    2)与ConnectivityService 打交道通过NetworkAgent

    1.2 检查是否有网络连接
    Java网络库API路径:Android_project/libcore
    Android判断网络是否连接:
    import android.content.Context;
    import android.net.ConnectivityManager;
    import android.net.NetworkInfo;
    public static boolean isNetworkConnected(
        Context context) {
        if (context != null) {
            ConnectivityManager connectivityManager =
                (ConnectivityManager)context
                .getSystemService(
                Context.CONNECTIVITY_SERVICE);
            // Ethernet, WiFi, Mobile
            NetworkInfo networkInfo =
            connectivityManager.getActiveNetworkInfo();
            if (networkInfo != null) {
                return networkInfo.isAvailable();
            }
        }
        return false;
    }

    使用命令dumpsys connectivity查看激活的网络和该网络的得分。

    1.3 实施方法
    连接高优先级的网络,低优先级的不断开。
    frameworks/base/services/core/java/com/android/server/ConnectivityService.java
    private void
    teardownUnneededNetwork(NetworkAgentInfo nai) {
        if (SystemProperties.getBoolean(
            "ro.net.coexist", false)) {
            return;
        }
        [...]
    }

    2 路由添加语法
    ip route add {NETWORK/MASK} via {GATEWAYIP}
    ip route add {NETWORK/MASK} dev {DEVICE}
    ip route add default {NETWORK/MASK} dev {DEVICE}
    ip route add default {NETWORK/MASK} via {GATEWAYIP}

    ip route add default via 192.168.0.1 dev eth0 \
    table 1

    # ip rule add from all lookup main pref 9999
    # 上一条命令等价于在netd中添加modifyIpRule(RTM_NEWRULE, 9999, 254, 0, 0);
    ip rule add from 192.168.0.0/24 table 1
    ip rule add to 192.168.0.0/24 table 1
    table 1中包含了eth0默认网关,而table main中包含了正常的eth0路由。

    3 Android netId
    3.1 获取网络接口的netId
    netd中将接口名(wlan0、eth0等)转化为netId的函数:
    server/NetworkController.cpp
    NetworkController::getNetworkForInterface(
        const char* interface)

    3.2 代码示例
    server/CommandListener.cpp
    函数CommandListener::NetworkCommand::runCommand()的最后添加如下的代码:
    //       0              1              2
    // network query_netid if_name
    if (!strcmp(argv[1], "query_netid")) {
        if (argc < 3) {
            return syntaxError(client,
                "Missing argument");
        }
        int _netId =
            gCtls->netCtrl.getNetworkForInterface(
                argv[2]);
        char msg[16] = {0};
        int retval = snprintf(msg, 15, "%d", _netId);
        if (retval > 0) {
            client->sendMsg(
                ResponseCode::CommandOkay,
                msg, false);
            return 0;
        } else {
            return operationError(client,
                "Can not get netId", retval);
        }
    }
    用法:ndc network query_netid wlan0

    4 Android UID
    4.1 Android-T fs_config
    system/core/libcutils/include/private/android_filesystem_config.h
    build/tools/fs_config
    out/soong/.intermediates/bionic/libc/generated_android_ids/gen/generated_android_ids.h
    Show string UID: ls -l /dev/xxx
    Show numeric UID: ls -n /dev/xxx
    chown 0:5008 /dev/xxx

    4.2 UID查找方法
    Android中,使用iptables绑定一个APP到特定的路由时,需要用到UID。
    ps -A后每一行的第一个字符串表示UID,查看该UID对应的数值,使用id
    例如:
    id u0_a50

    其中u0_a50的含义:
    u0:默认的手机第一个用户
    a:代表app
    50:代表着第50个应用

    转换公式:
    u0_a50 = “u0_” + “a” + (uid(这里是10050) – FIRST_APPLICATION_UID(固定值10000))

    4.3 UID查找脚本示例
    #!/vendor/bin/sh

    APP_LIST=("com.android.email"
     "com.android.systemui"
     "android.hardware.audio@2.0-service")

    SH_PS="/vendor/bin/ps"
    SH_GREP="/vendor/bin/grep"
    SH_ID="/vendor/bin/id"
    SH_TR="/vendor/bin/tr"
    SH_ECHO="/vendor/bin/echo"
    SH_TEE="/vendor/bin/tee"
    SH_USLP="/vendor/bin/usleep"

    for app in ${APP_LIST[@]}
    do
        count=1
        while [ $count -le 10 ]; do
            line=$($SH_PS -A | $SH_GREP ${app})
            if [ $? -eq 0 ]; then
                $SH_ECHO "got ${app}, count $count" \
                    | $SH_TEE /dev/kmsg
                break
            fi

            $SH_ECHO "waiting ${app}, count $count" \
                    | $SH_TEE /dev/kmsg
            $SH_USLP 500000
            count=$((count + 1))
        done

        line=$($SH_PS -A | $SH_GREP ${app})
        if [ $? -eq 0 ]; then
            list=(${line}) # switch str to list
            # len=${#list[@]} # length of list
            # len=$((len - 1))
            uid_str=${list[0]}

            line=$($SH_ID $uid_str)
            list=(${line}) # switch str to list
            uid_str=${list[0]}
            # uid=10017(u0_a17) -> uid=10017
            uid_str=${uid_str%\(*}

            uid=$($SH_ECHO $uid_str | \
                $SH_TR -cd "[0-9]")
            # Now got the process UID
            $SH_ECHO $uid | $SH_TEE /dev/kmsg
        fi
    done

    5 绑定某个应用到指定的网络
    5.1 Linux netfilter hook
    Linux内核网络代码被netfilter cut off的函数都分为了两个阶段:XXX以及XXX_finish。
    struct nf_hook_ops
    nf_register_net_hook()
    nf_unregister_net_hook()

    kernel IPv4 print: %pI4
    user IPv4 print: inet_ntoa
    meta|match1|match2|...|target

    5.1.1 match
    iptables -m xxx: xxx = kernel struct xt_match, libipt_xxx.so
    net/netfilter/xt_owner.c
    external/iptables/extensions/libxt_owner.c

    5.1.2 target
    iptables -j xxx: xxx = kernel struct xt_target, libipt_xxx.so

    xt_register_target()
    xt_unregister_target()

    5.2 Android API
    bindProcessToNetwork()
    netd中hook了socket()、connect()和accept()等API。
    bionic/libc/bionic/socket.cpp
    bionic/libc/bionic/connect.cpp
    system/netd/client/NetdClient.cpp
    FwmarkClient::shouldSetFwmark(int family)

    5.3 命令行方式
    1)添加main rule
    ip rule add from all lookup main pref 9999

    2)为eth0接口添加VLAN ID 5
    ip link add link eth0 name eth0.5 type vlan id 5
    ip -d link show eth0.5

    3)设置VLAN QOS
    i=0
    while [ $i -le 7 ]; do
        ip link set dev eth0.5 type vlan \
            egress-qos-map $i:0
        # ip link set eth0.5 type vlan egress $i:0
        i=$((i + 1))
    done
    其中$i表示skb_priority,0表示vlan_qos,vlan_qos对应到VLAN以太网帧中的3bit PCP。因为PCP的值越大优先级越高,由于AVB PCP的值为2(Class B,2B,250us)和3(Class A,3A,125us),所以将普通应用程序的PCP值都设置为0后,就低于AVB流的优先级了。

    4)分配IP并启动
    ip addr add 192.168.5.200/24 brd 192.168.5.255 \
    dev eth0.5
    ip link set dev eth0.5 up
    或者
    ifconfig eth0.5 192.168.5.200/24 up

    5)设置iptables fwmark
    ip rule add fwmark 0x66 priority 9998 table 200
    ip route add 192.168.5.0/24 via 192.168.5.1 \
    dev eth0.5 table 200
    ip route flush cache

    # -j means jump, u0_a50 = 10050
    # net/netfilter/xt_owner.c
    # external/iptables/extensions/libxt_owner.c
    iptables -t mangle -A OUTPUT -m owner \
    --uid-owner u0_a50 -j MARK --set-mark 0x66
    echo 0x66 > \
    /proc/sys/net/ipv4/conf/eth0.5/rp_filter

    # delete this fwmark
    # iptables -t mangle -D OUTPUT -m owner \
    --uid-owner u0_a50 -j MARK --set-mark 0x66
    # echo 0 > \
    /proc/sys/net/ipv4/conf/eth0.5/rp_filter

    # merge ip rule and iptables to one
    # ip rule add uidrange 10050-10050 priority 9998 table 200
    # ip rule del uidrange 10050-10050 priority 9998 table 200

    6)查看是否生效
    CONFIG_NETFILTER_NETLINK_LOG=m
    CONFIG_NETFILTER_XT_TARGET_LOG=m

    iptables -A PREROUTING -t raw -i eth0 \
    -j TRACE
    iptables -A OUTPUT -t raw -o eth0 \
    -j TRACE
    echo "nf_log_ipv4" > \
    /proc/sys/net/netfilter/nf_log/2

    dmesg | grep TRACE

    6 Abbreviations
    fwmark:firewall mark
    ojluni:Android libcore中的模块,包含OpenJDK、Language、Util、Net、IO
    svc:Supervisor Call

  • 相关阅读:
    力扣二分篇
    Cesium性能优化:高效加载billboard图标和label文字标签
    【Websocket 第二篇】问题解惑
    MySql进阶篇---006:存储引擎,索引,SQL优化,视图、存储过程、变量、流程控制、游标、存储函数、触发器
    ant-design-vue 修改a-button disabled默认样式
    SpringBoot使用手册
    Python 零基础入门的第一天——开发环境的搭建
    蓝桥杯回文日期C语言
    http服务网络请求如何确保数据安全(含python示例源码)
    06c++呵呵老师【FPS实现游戏角色】
  • 原文地址:https://blog.csdn.net/zoosenpin/article/details/104055926