受不了xxxx恶心人的行为,遂搬迁至博客园。 始发:2016-05-22 11:09:05 版本信息: Android 4.4.4
1、netfilter 与 iptables 关系
Netfilter:
http://www.netfilter.org/: Netfilter is a framework provided by the Linux kernel that allows various networking-related operations to be implemented in the form of customized handlers. Netfilter offers various functions and operations for packet filtering, network address translation, and port translation, which provide the functionality required for directing packets through a network, as well as for providing ability to prohibit packets from reaching sensitive locations within a computer network.
netfilter 利用一些封包过滤的【规则】设定,来定义出什么数据包可以接收,什么数据包需要剔除。位于内核层。
iptables 通过命令的方式对 netfilter 规则进行排序与修改。位于用户层。
用户层的 iptables 命令和内核层的 netfilter 之间的关系和其通讯方式:
图片来自:http://blog.chinaunix.net/uid-23069658-id-3160506.html
netfilter 与 IP 协议栈无缝契合,因此它的效率非常的高。主要完成:
- 拒绝让 Internet 的封包进入主机的某些端口
- 拒绝让某些来源 IP 的封包进入
- 拒绝让带有某些特殊标志(flag)的封包进入,最常拒绝的就是带有 SYN 的主动联机的 flag,只要一经发现就将该封包丢弃
- 分析硬件地址(MAC)来决定联机与否
2、netfilter 工作流程
图片来自:http://blog.chinaunix.net/uid-23069658-id-3160506.html
收到的每个数据包,都从“A”点进来,经过路由判决,如果是发送给本机的就经过“B”点,然后往协议栈的上层继续传递;否则,如果该数据包的目的地不是本机,那么就经过“C”点,然后顺着“E”点将该包转发出去。
netfilter在A,B,C,D和E设置回调函数(hook函数),对每一个进出的数据包进行检测,检测完向 netfilter 报告一下该数据包的情况,返回结果含:
netfilter.h (kernel\include\uapi\linux)
- NF_ACCEPT 继续正常传输数据报,这个返回值告诉 netfilter:到目前为止,该数据包还是被接受的并且该数据包应当被递交到网络协议栈的下一个阶段。
- NF_DROP 丢弃该数据报,不再传输。
- NF_STOLEN 回调函数接管该数据报,该回调函数从此开始对数据包的处理,并且netfilter应当放弃对该数据包做任何的处理。
- NF_QUEUE 对该数据报进行排队(通常用于将数据报给用户空间的进程进行处理)。
- NF_REPEAT 再次调用该回调函数,应当谨慎使用这个值,以免造成死循环。
- NF_STOP 功能和 NF_ACCEPT 类似但强于 NF_ACCEPT,一旦挂接链表中某个hook节点返回 NF_STOP,该 skb 包就立即结束检查而被其他模块接受,不再需要进入后续hook点检查。
和 A、B、C、D、E 五个点对应的在Netfilter中的术语为:
图片来自:http://blog.chinaunix.net/uid-23069658-id-3160506.html
netfilter支持的协议栈:
netfilter.h (kernel\include\uapi\linux)
1 enum { 2 NFPROTO_IPV4 = 2, 3 NFPROTO_ARP = 3, 4 NFPROTO_BRIDGE = 7, 5 NFPROTO_IPV6 = 10, 6 NFPROTO_DECNET = 12, 7 };
以 IPv4 为例,从协议栈正常流程,经过 A、B、C、D、E 每个点时,切入到 netfilter 框架中,依次去调用每个 hook 点的回调(钩子)函数,检查完后返回,根据结果确定之后走向:
ip_input.c (kernel\net\ipv4)
ip_output.c (kernel\net\ipv4)
ip_forward.c (kernel\net\ipv4)
1 (1):NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL,ip_rcv_finish) 2 (2):NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL,ip_local_deliver_finish); 3 (3):NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, rt->u.dst.dev,ip_forward_finish); 4 (4):NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL, skb->dst->dev, dst_output); 5 (5):NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev,ip_finish_output, cond); 6 7 8 NF_HOOK(pf, hook, skb, in, out, okfn) 9 pf:协议栈名称,定义在socket.h (kernel\include\linux) 10 hook:HOOK点的名字,对于IPv4就是上述五个值 11 skb:内核中网络数据包的结构体 12 in:数据包进来的设备,以struct net_device结构表示 13 out:数据包出去的设备,以struct net_device结构表示 14 okfn:函数指针,该HOOK点的所有登记的函数调用完后调用该函数
3、用户层 iptables 与内核交互方式、流程
netfilter 框架具有三个主要模块,如下:
iptables维护这三张表,查看或设置使用参数-t:
1 iptables -t filter -L 2 iptables -t nat -L 3 iptables -t mangle -L
内核负责和用户层 iptables 命令交互的是 ip-tables 模块:
ip_tables.c (kernel\net\ipv4\netfilter) static int __init ip_tables_init(void) { /* Register setsockopt */ ret = nf_register_sockopt(&ipt_sockopts); if (ret < 0) goto err5; pr_info("(C) 2000-2006 Netfilter Core Team\n"); // 系统启动打印该log return 0; } static void __exit ip_tables_fini(void) module_init(ip_tables_init); module_exit(ip_tables_fini);
用户层 iptables 通过 setsockopt/getsockopt 设置/查询这三张表,并最终调用 ip_tables_init() 注册的 do_ipt_set_ctl/do_ipt_get_ctl() 函数:
1 ip_tables.c (kernel\net\ipv4\netfilter) 2 static struct nf_sockopt_ops ipt_sockopts = { 3 .set = do_ipt_set_ctl, 4 .get = do_ipt_get_ctl, 5 .owner = THIS_MODULE, 6 }; 7 8 9 ip_tables.c (kernel\net\ipv4\netfilter) 10 static int do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len) 11 { 12 switch (cmd) { 13 case IPT_SO_SET_REPLACE: 14 ret = do_replace(sock_net(sk), user, len); 15 break; 16 case IPT_SO_SET_ADD_COUNTERS: 17 ret = do_add_counters(sock_net(sk), user, len, 0); 18 break; 19 default: 20 ret = -EINVAL; 21 } 22 return ret; 23 } 24 static intdo_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) 25 { 26 switch (cmd) { 27 case IPT_SO_GET_INFO: 28 ret = get_info(sock_net(sk), user, len, 0); 29 break; 30 case IPT_SO_GET_ENTRIES: 31 ret = get_entries(sock_net(sk), user, len); 32 break; 33 case IPT_SO_GET_REVISION_MATCH: 34 case IPT_SO_GET_REVISION_TARGET: 35 break; 36 } 37 default: 38 ret = -EINVAL; 39 } 40 return ret; 41 }
4、Netfilter默认rules的创建
在没有使用iptables命令设置规则表(filter表、nat表、mangle表)的情况下,系统也自动生成默认的规则表,如下filter表(只列出INPUT链):
1 root@rocky:/ # iptables -t filter -L 2 Chain INPUT (policy ACCEPT) 3 target prot opt source destination 4 bw_INPUT all -- anywhere anywhere 5 fw_INPUT all -- anywhere anywhere
这个默认规则在代码里通过 iptables 命令生成:
1 CommandListener.cpp (system\netd) 2 CommandListener::CommandListener(UidMarkMap *map) :FrameworkListener("netd", true) 3 { 4 // Create chains for children modules 5 createChildChains(V4V6, "filter", "INPUT", FILTER_INPUT); 6 createChildChains(V4V6, "filter", "FORWARD", FILTER_FORWARD); 7 createChildChains(V4V6, "filter", "OUTPUT", FILTER_OUTPUT); 8 createChildChains(V4V6, "raw", "PREROUTING", RAW_PREROUTING); 9 createChildChains(V4V6, "mangle", "POSTROUTING", MANGLE_POSTROUTING); 10 createChildChains(V4V6, "mangle", "OUTPUT", MANGLE_OUTPUT); 11 createChildChains(V4, "nat", "PREROUTING", NAT_PREROUTING); 12 createChildChains(V4, "nat", "POSTROUTING", NAT_POSTROUTING); 13 } 14 15 CommandListener.cpp (system\netd) 16 /** 17 * List of module chains to be created, along with explicit ordering. ORDERING 18 * IS CRITICAL, AND SHOULD BE TRIPLE-CHECKED WITH EACH CHANGE. 19 */ 20 static const char* FILTER_INPUT[] = { 21 // Bandwidth should always be early in input chain, to make sure we 22 // correctly count incoming traffic against data plan. 23 BandwidthController::LOCAL_INPUT, // "bw_INPUT" 24 FirewallController::LOCAL_INPUT, // "fw_INPUT" 25 NULL, 26 }; 27 28 【log截图中绿色框部分】 29 void createChildChains(IptablesTarget target, const char* table, const char* parentChain, const char** childChains); 30 -->int execIptablesSilently(IptablesTarget target, ...); 31 ---->int execIptables(IptablesTarget target, bool silent, va_list args) { 32 const char *argv[argsList.size()]; 33 ... 34 argv[0] = IPTABLES_PATH; // IPTABLES_PATH = "/system/bin/iptables" 35 } 36 ------>int execIptablesCommand(int argc, const char *argv[], bool silent); 37 -------->int android_fork_execvp(int argc, char* argv[], int *status,boolignore_int_quit, bool logwrap) { 38 pid = fork(); 39 if (pid == 0) { 40 child(argc, argv); 41 } 42 } 43 ---------->static void child(int argc, char* argv[]) { 44 // create null terminated argv_child array 45 char* argv_child[argc + 1]; 46 memcpy(argv_child, argv, argc * sizeof(char *)); 47 argv_child[argc] = NULL; 48 if (execvp(argv_child[0], argv_child)) { 49 FATAL_CHILD("executing %s failed: %s\n", argv_child[0], 50 strerror(errno)); 51 } 52 }
还有一个地方,也用 iptables 命令设置了规则表:
int NatController::setupIptablesHooks() { res = setDefaults(); 【log截图中蓝色框部分】 struct CommandsAndArgs defaultCommands[] = { {{IPTABLES_PATH, "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0}, {{IPTABLES_PATH, "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0}, {{IPTABLES_PATH, "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1}, }; for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE; cmdNum++) { if (runCmd(ARRAY_SIZE, defaultCommands[cmdNum].cmd) && defaultCommands[cmdNum].checkRes) {} } } 【log截图中黄色框部分】 int NatController::setDefaults() { struct CommandsAndArgs defaultCommands[] = { {{IPTABLES_PATH, "-F", LOCAL_FORWARD,}, 1}, {{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1}, {{IPTABLES_PATH, "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1}, {{IP_PATH, "rule", "flush"}, 0}, {{IP_PATH, "-6", "rule", "flush"}, 0}, {{IP_PATH, "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0}, {{IP_PATH, "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0}, {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0}, {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0}, {{IP_PATH, "route", "flush", "cache"}, 0}, }; for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE; cmdNum++) { if (runCmd(ARRAY_SIZE, defaultCommands[cmdNum].cmd) && defaultCommands[cmdNum].checkRes) {} } } int NatController::runCmd(int argc, const char **argv) { res = android_fork_execvp(argc, (char **)argv, NULL, false, false); ALOGV("runCmd(%s) res=%d", full_cmd.c_str(), res); return res; }
Log:
参考资料:
1、鸟哥的Linux私房菜服务器架设篇(第三版)——第九章、防火墙与 NAT 服务器 2、http://www.netfilter.org/ 3、iptables使用文档:https://www.frozentux.net/iptables-tutorial/iptables-tutorial.html 4、系列: (一)洞悉linux下的Netfilter&iptables:什么是Netfilter?() (二)洞悉linux下的Netfilter&iptables:内核中的ip_tables小觑 (http://blog.chinaunix.net/uid-23069658-id-3162264.html) (三)洞悉linux下的Netfilter&iptables:内核中的rule,match和target (http://blog.chinaunix.net/uid-23069658-id-3163999.html) (四)洞悉linux下的Netfilter&iptables:包过滤子系统iptable_filter (http://blog.chinaunix.net/uid-23069658-id-3166140.html) Source Code: arp_tables.c (kernel\net\ipv4\netfilter) BandwidthController.cpp (system\netd) BandwidthController.h (system\netd) CommandListener.cpp (system\netd) CommandListener.h (system\netd) core.c (kernel\net\netfilter) FirewallController.cpp (system\netd) FirewallController.h (system\netd) iptables.h (external\iptables\include) iptable_filter.c (kernel\net\ipv4\netfilter) iptable_nat.c (kernel\net\ipv4\netfilter) iptable_raw.c (kernel\net\ipv4\netfilter) ip_forward.c (kernel\net\ipv4) ip_input.c (kernel\net\ipv4) ip_output.c (kernel\net\ipv4) ip_tables.c (kernel\net\ipv4\netfilter) ip_tables.h (kernel\include\linux\netfilter_ipv4) ip_tables.h (kernel\include\uapi\linux\netfilter_ipv4) kmod.h (kernel\include\linux) logwrap.c (system\core\logwrapper) logwrap.h (system\core\logwrapper\include\logwrap) NetdCommand.cpp (system\netd) NetdCommand.h (system\netd) NetdConstants.cpp (system\netd) NetdConstants.h (system\netd) netfilter.h (kernel\include\linux) netfilter.h (kernel\include\uapi\linux) nfnetlink.c (kernel\net\netfilter) nf_sockopt.c (kernel\net\netfilter) socket.h (kernel\include\linux) x_tables.c (kernel\net\netfilter) x_tables.h (kernel\include\linux\netfilter) x_tables.h (kernel\include\uapi\linux\netfilter)