Android之防火墙功能的实现

需求:可以控制某个应用访问WIFI或移动网络的功能。

Android自带防火墙原理是:一旦开启防火墙,所有的应用都不能访问网络(包括WIFI和移动网络),所以不能满足需求,故需要在原生基础上新增两个方法来实现该功能,方法声明在frameworks/base/core/java/android/os目录下的INetworkManagementService.aidl中:

interface INetworkManagementService
{
    . . .    
    void setMobileDataUidRule(int uid, boolean allow); //根据应用的uid设置是否允许它访问移动网络
    
    void setWifiDataUidRule(int uid, boolean allow); //根据应用的uid设置是否允许它访问WIFI网络
    . . .
}


方法的实现是在frameworks/base/services/core/java/com/android/server目录下的NetworkManagementService.java中:

public class NetworkManagementService extends INetworkManagementService.Stub implements Watchdog.Monitor {
    . . .
    @Override
    public void setMobileDataUidRule(int uid, boolean allow) {
        enforceSystemUid(); // 校验调用者是否是系统uid
        try {
            mConnector.execute("firewall", "set_mobile_data_uid_rule", uid, rule);
        } catch (NativeDaemonConnectorException e) {
            throw e.rethrowAsParcelableException();
        }
    }
    @Override
    public void setWifiDataUidRule(int uid, boolean allow) {
        enforceSystemUid();
        try {
            mConnector.execute("firewall", "set_wifi_data_uid_rule", uid, rule);
        } catch (NativeDaemonConnectorException e) {
            throw e.rethrowAsParcelableException();
        }
    }
    . . .
}


下面又通过socket通信方式调用到system/netd层的system/netd/server目录下的CommandListener.cpp中:

CommandListener::CommandListener() : FrameworkListener("netd", true) {
    . . .
    sFirewallCtrl->initIptableFirewall();// 开机初始化
    . . .
}

int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc, char **argv) {
    . . .
    if (!strcmp(argv[1], "set_mobile_data_uid_rule")) {
        if (argc != 4) {
            cli->sendMsg(ResponseCode::CommandSyntaxError,
                    "Usage: firewall set_mobile_data_uid_rule <1000> ",
                    false);
            return 0;
        }
        int uid = atoi(argv[2]);
        FirewallRule rule = parseRule(argv[3]);
        int res = sFirewallCtrl->setMobileDataUidRule(uid, rule);
        return sendGenericOkFail(cli, res);
    }
    if (!strcmp(argv[1], "set_wifi_data_uid_rule")) {
        if (argc != 4) {
            cli->sendMsg(ResponseCode::CommandSyntaxError,
                    "Usage: firewall set_wifi_data_uid_rule <1000> ",
                    false);
            return 0;
        }
        int uid = atoi(argv[2]);
        FirewallRule rule = parseRule(argv[3]);
        int res = sFirewallCtrl->setWifiDataUidRule(uid, rule);
        return sendGenericOkFail(cli, res);
    }
    . . .
}


上面代码又调用到system/netd/server目录下的FirewallController.cpp中:

. . .
#include 

const char* op_3g;
const char* op_wifi;

. . .

int FirewallController::initIptableFirewall(void) {
    int res = 0;
    char property[PROPERTY_VALUE_MAX];
    property_get("ro.hardware", property, "qcom");
    if (strncmp("qcom", property, 4) == 0) { // 高通平台
        op_3g = "rmnet+";
        op_wifi = "wlan0";
    } else if (strncmp("mt", property, 2) == 0 || strncmp("MT", property, 2) == 0) {
        op_3g = "ccmni+"; // MTK平台
        op_wifi = "wlan0";
    } else {
        op_3g = "rmnet+";
        op_wifi = "wlan0";
    }
    // 新建一个drop_wall链表
    res |= execIptables(V4V6, "-w", "-N", "drop_wall", NULL);
    // 把新建的drop_wall链表添加到OUTPUT链表目录下
    res |= execIptables(V4V6, "-w", "-A", "OUTPUT", "-j", "drop_wall", NULL);
}

int FirewallController::setMobileDataUidRule(int uid, FirewallRule rule) {
    char uidStr[16];
    sprintf(uidStr, "%d", uid);
    const char* op;
    if (rule == ALLOW) {
        op = "-D";// 删除链表中的规则
    } else {
        op = "-A";// 添加规则到链表
    }
    int res = 0;
    res |= execIptables(V4V6, "-w", op, "drop_wall", "-o", op_3g, "-m", "owner",
            "--uid-owner", uidStr, "-j", "REJECT", NULL);
    return res;
}

int FirewallController::setWifiDataUidRule(int uid, FirewallRule rule) {
    char uidStr[16];
    sprintf(uidStr, "%d", uid);
    const char* op;
    if (rule == ALLOW) {
        op = "-D";// 删除链表中的规则
    } else {
        op = "-A";// 添加规则到链表
    }
    int res = 0;
    res |= execIptables(V4V6, "-w", op, "drop_wall", "-o", op_wifi, "-m", "owner",
            "--uid-owner", uidStr, "-j", "REJECT", NULL);
    return res;
}
. . .


方法声明在system/netd/server目录下的FirewallController.h中:

class FirewallController {
    public:
        . . .
        int initIptableFirewall(void);
        int setMobileDataUidRule(int, FirewallRule);
        int setWifiDataUidRule(int, FirewallRule);
        . . .
}


到这里方法就添加完毕了,下面看调用,调用者必须是系统uid,要把aidl文件拷贝到android.os包下,由于NetworkManagementService是隐藏的,故需要使用方式的方式调用:

public static void setUidNetworkState(int uid, boolean enabled) {
    try {
        Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
        IBinder binder = (IBinder) method.invoke(null, new Object[] {"network_management"});
        INetworkManagementService service = INetworkManagementService.Stub.asInterface(binder);
        if (service != null) {
            service.setMobileDataUidRule(uid, enabled);
            service.setWifiDataUidRule(uid, enabled);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}


另外,网络端口的查看方式:

adb shell
netcfg


代码实现方式:

ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
Network[] networks = manager.getAllNetworks();
for (Network item : networks) {
    NetworkInfo info = manager.getNetworkInfo(item);
    if (info.getType() == ConnectivityManager.TYPE_WIFI) {
        String wifi = manager.getLinkProperties(item)
                .getInterfaceName();
        System.out.println("zyf wifi:" + wifi);
    } else if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
        String mobile = manager.getLinkProperties(item)
                .getInterfaceName();
        System.out.println("zyf mobile:" + mobile);
    }
}


 

你可能感兴趣的:(Android)