背景
本身从事网络安全工作,具体为防火墙产品的开发,对Linux 内核而言,Linux 防火墙功能由Netfilter框架实现,因此有了对Linux内核Netfilter实现逻辑的学习研究的兴趣,也想借此平台和大家一起交流学习。
概念
Netfilter 是 Linux 内核中用于进行网络包过滤和操作的框架,在报文接收的处理的路径上,针对不同的协议,在不同的Hook位置调用相应的Hook函数(钩子函数),实现对报文的处理,协议类型标注了钩子函数在处理网络包时所关注的协议,目前Netfilter根据不同的协议,对报文处理的支持分为以下几类:
1)NFPROTO_BRIDGE,用于桥接设备相关的协议类型标识,桥接设备用于连接一个或多个网段的设备,工作在数据链路层,通过MAC地址转发或过滤数据包。
2)NFPROTO_ARP,Netfilter中用于注册于ARP协议相关的钩子函数,用于在ARP报文处理过程中执行特定的操作。
3)NFPROTO_IPV4,ipv4相关报文处理
4)NFPROTO_IPV6, ipv6相关报文处理
与之相对应,存在ebtables、arptables、iptables、ip6tables用于配置相应的规则,从而实现对具体特定特征报文执行特定的动作处理,关于xxtables的内容另外讨论。
Netfilter处理
从Linux 内核实现上看,Netfilter处理分为以下几部分:
1)nf_hook_ops注册
2)NF_HOOK 钩子函数处理调用
3)钩子函数处理后的后续处理
nf_hook_ops的注册
钩子函数的注册通常随着模块的加载,在模块初始化过程中通过直接或间接调用nf_register_net_hooks或nf_register_net_hook(二者的区别在于注册一组还是一个钩子函数)注册(不绝对,有的可能在函数处理逻辑过程中注册),以连接模块的钩子函数注册为例,具体调用过程如下:
这里从nf_register_net_hooks的上层调用函数nf_ct_netns_do_get入手,具体实现如下:
static int nf_ct_netns_do_get(struct net *net, u8 nfproto)
{
struct nf_conntrack_net *cnet = nf_ct_pernet(net);
bool fixup_needed = false, retry = true;
int err = 0;
retry:
mutex_lock(&nf_ct_proto_mutex);
/* 如前所说Netfilter支持不同协议,这里依据NF协议,选择分支处理 */
switch (nfproto) {
case NFPROTO_IPV4:
cnet->users4++;
if (cnet->users4 > 1)
goto out_unlock;
err = nf_defrag_ipv4_enable(net);
if (err) {
cnet->users4 = 0;
goto out_unlock;
}
/* 注册ipv4 连接相关处理钩子函数 */
err = nf_register_net_hooks(net, ipv4_conntrack_ops,
ARRAY_SIZE(ipv4_conntrack_ops));
if (err)
cnet->users4 = 0;
else
fixup_needed = true;
break;
#if IS_ENABLED(CONFIG