Android Firewall 相关 debug 记录

Posted by Vector Blog on December 19, 2016

bug: 手机在连上wifi之后, 熄屏静置 10 min 以上发现无法ping 到手机,一直显示超时。

从log 中可以看到在大约 10 min时有出现 “enable FIREWALL_CHAIN_DOZABLE “ 相关的log, 然后顺着往下追:

/* frameworks/base/services/core/java/com/android/server/NetworkManagementService.java */
    public void setFirewallChainEnabled(int chain, boolean enable) {
        enforceSystemUid();
        synchronized (mQuotaLock) {
            ......
            final String operation = enable ? "enable_chain" : "disable_chain";
            final String chainName;
            switch(chain) {
                ......
                case FIREWALL_CHAIN_DOZABLE:
                    chainName = FIREWALL_CHAIN_NAME_DOZABLE;
                    break;
                ......
            }

            try {
                mConnector.execute("firewall", operation, chainName);
            } catch (NativeDaemonConnectorException e) {
                throw e.rethrowAsParcelableException();
            }
	   ......
        }
    }

其中:

private final NativeDaemonConnector mConnector;

NativeDaemonConnector 中维护着与 Netd 中 CommandListener 相关的内部socket线程,NetworkManagementService 就是通过它来和 Netd 进行通信,发送command .

/* system/netd/server/CommandListener.cpp */
int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc, char **argv){
    if (!strcmp(argv[1], "enable_chain")) {
        if (argc != 3) {
            cli->sendMsg(ResponseCode::CommandSyntaxError,
                         "Usage: firewall enable_chain <dozable|standby>",
                         false);
            return 0;
        }

        ChildChain childChain = parseChildChain(argv[2]);
        int res = gCtls->firewallCtrl.enableChildChains(childChain, true);
        return sendGenericOkFail(cli, res);
    }
}

/* system/netd/server/FirewallController.cpp */
int FirewallController::enableChildChains(ChildChain chain, bool enable) {
    int res = 0;
    const char* name;
    switch(chain) {
        case DOZABLE:
            name = LOCAL_DOZABLE;
            break;
        case STANDBY:
            name = LOCAL_STANDBY;
            break;
        case POWERSAVE:
            name = LOCAL_POWERSAVE;
            break;
        default:
            return res;
    }

    if (enable) {
        res |= attachChain(name, LOCAL_INPUT);
        res |= attachChain(name, LOCAL_OUTPUT);
    } else {
        res |= detachChain(name, LOCAL_INPUT);
        res |= detachChain(name, LOCAL_OUTPUT);
    }
    return res;
}

int FirewallController::attachChain(const char* childChain, const char* parentChain) {
    return execIptables(V4V6, "-t", TABLE, "-A", parentChain, "-j", childChain, NULL);
}

这里最后调用的还是 iptables , 其中 V4V6 的作用是调用判断调用 iptables 还是 ip6tables。 上面command 的意思是将doze chain 加到 filter table 中的 INPUT 和 OUTPUT 中。iptable 的用法网上资料很多,介绍一个: http://blog.csdn.net/wlzx120/article/details/52300774

大概看一下这里 doze chain 的内容:

</center> 这里可以看到会先检查前面的3个rule , 如果有符合的直接return 否则检查到最后一项全部 drop. 这里在大概看一下第一项中的 “owner UID match u0_a26” ,这里需要查询一下 u0_a26 表示的package.
这里 u0_a26 的打印方式可以参考 https://zhuanlan.zhihu.com/p/22620803?refer=kelvin-love-wmy , 这里 u0_a26 即为UID 10026, 通过 UID 可以在 /data/system/packages.list 中查询到.