🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
本节将介绍IpFwd和FirewallCmd两个命令。 **1.IpFwd命令** IpFwd命令比较简单,主要是控制内核ipforward功能,其支持三个选项。 - status:用于判断ipforward功能是否开启。 - enable和disable:分别用于启动和禁止ipforward功能。 上述功能借助TetherController控制对象来完成,其内部代码非常简单,就是通过读写/proc/sys/net/ipv4/ip_forward文件来实现对内核ipforward功能的管理。 >[info] **提示** 读者可上网搜索/proc/sys/net/ipv4目录下其他文件的作用。 **2.FirewallCmd命令** FirewallCmd用于防火墙控制。它支持以下命令选项。 - enable和is_enabled:用于启动防火墙和判断防火墙是否已经启动。 - set_interface_rule:针对单个或多个NIC设备设置防火墙规则。 - set_egress_source_rule和set_egress_dest_rule:基于源IP和目标IP地址设置防火墙。 - set_uid_rule:根据uid设置单个进程的防火墙。 下面重点介绍防火墙功能的启动以及如果针对单个进程的设置防火墙规则。 (1)启动防火墙 本节介绍enable和set_uid_rule。其中,enable将调用FirewallCtronller的enableFireWall函数,代码如下所示。 **FirewallController.cpp::enableFirewall** ~~~ int FirewallController::enableFirewall(void) { int res = 0; // 先清空fw_INPUT/fw_OUTPUT/fw_FORWARD链中的规则 disableFirewall(); // 启动防火墙就是设置Filter表中fw_INPUT/fw_OUTPUT/fw_FORWARD的目标为DROP和REJECT res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL); res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL); res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL); return res; } ~~~ 由上述代码可知,当启动防火墙时,filter表中三个用于防火墙控制的Chain的目标都改为DROP或REJECT,这样系统就无法收发网络数据包。图2-19所示为笔者在Ubuntu机器上设置的规则。 :-: ![](https://box.kancloud.cn/549160290311bc3c07f7716191f6ac32_1182x318.jpg) 图2-19 enable防火墙 图2-19所示是利用Ubuntu机器模拟enableFirewall函数后的结果。防火墙启动后,机器就无法连接网络了。 读者可能好奇,为什么enableFirewall的破坏作用如此之大?这是因为防火墙设置一般有两种方法。 - 先允许整个系统都能上网,然后再单独设置防火墙规则以禁止某些模块连接网络。 - 先禁止整个系统上网,然后再单独设置防火墙规则以允许某些模块能连接网络。 由此可知,Android采用了第二种更为严厉的方式来设置防火墙。接下来的工作就需要设置各种防火墙规则以允许某些程序能够上网了。本节讨论如何为单个进程设置防火墙规则。 * * * * * **提示** 采用Ubuntu来测试enableFirewall,因为模拟器和主机也使用socket通信,一旦启动防火墙,主机就无法再使用adb shell来控制模拟器了。 * * * * * (2)设置进程防火墙 本节介绍的针对单个应用程序设置其防火墙规则,是大部分软件管家禁止某些应用程序上网的通用方法。代码在setUidRule函数中,如下所示。 **FirewallController.cpp::setUidRule** ~~~ int FirewallController::setUidRule(int uid, FirewallRule rule) { char uidStr[16]; sprintf(uidStr, "%d", uid); const char* op; if (rule == ALLOW) { op = "-I"; } else { op = "-D"; } int res = 0; res |= execIptables(V4V6, op, LOCAL_INPUT,"-m", "owner", "--uid-owner", uidStr,"-j", "RETURN", NULL); res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr,"-j", "RETURN", NULL); return res; } ~~~ 该函数很简单,就是调用正确的iptables命令,它们分别如下。 ~~~ iptables-I FORWARD_INPUT-m owner--uid-owner uid-j RETURN iptables-I FORWARD_OUTPUT-m owner--uid-owner uid-j RETURN ~~~ 这里要特别指出的是,目前Android只能根据uid来区分进程。由于Android平台允许不同应用程序共享同一个uid[^①]。多个应用程序共享同一个uid的情况下,如果用户禁止了其中一个应用程序(软件管家的防火墙控制允许用户选择是否禁止某个应用程序上网),则连带其他的相关程序都不能上网,虽然用户并没有选择禁止其他应用程序。 * * * * * **提示** 笔者测试了360安全卫士,当出现上述关联情况时,它能提醒用户哪些关联程序也会被一并禁止上网。 * * * * * [^①]:参考《深入理解Android:卷Ⅱ》4.3.1节关于Android系统中UID/GID知识的介绍。