我是靠谱客的博主 会撒娇裙子,最近开发中收集的这篇文章主要介绍连接跟踪子系统之AF_INET初始化1. 初始化2. L3协议3. Netfilter钩子,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

这篇笔记将要分析AF_INET协议族是如何支持连接跟踪子系统的。相关的代码文件有:

代码路径说明
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.cAF_INET协议族连接跟踪子系统的核心实现

1. 初始化

static int __init nf_conntrack_l3proto_ipv4_init(void)
{
int ret = 0;
need_conntrack();
//向Netfilter框架注册xxx_sock_opt()接口
ret = nf_register_sockopt(&so_getorigdst);
if (ret < 0) {
printk(KERN_ERR "Unable to register netfilter socket optionn");
return ret;
}
//向连接跟踪子系统框架注册自己支持的L4协议,分别有tcp、udp、icmp
ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_tcp4);
if (ret < 0) {
printk("nf_conntrack_ipv4: can't register tcp.n");
goto cleanup_sockopt;
}
ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udp4);
if (ret < 0) {
printk("nf_conntrack_ipv4: can't register udp.n");
goto cleanup_tcp;
}
ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_icmp);
if (ret < 0) {
printk("nf_conntrack_ipv4: can't register icmp.n");
goto cleanup_udp;
}
//向连接跟踪子系统框架注册自己,即L3协议--AF_INEET
ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv4);
if (ret < 0) {
printk("nf_conntrack_ipv4: can't register ipv4n");
goto cleanup_icmp;
}
//向Netfilter框架注册钩子函数
ret = nf_register_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
if (ret < 0) {
printk("nf_conntrack_ipv4: can't register hooks.n");
goto cleanup_ipv4;
}
return ret;
...
}

这篇笔记我们只关注L3协议的一些实现,其支持的L4协议后面单独分析。

2. L3协议

如笔记连接跟踪子系统之L3L4协议管理所述,协议族要支持连接跟踪子系统,需要向连接跟踪子系统框架注册一个L3协议对象,AF_INET协议族的L3协议对象定义如下:

struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = {
.l3proto
= PF_INET,
.name
= "ipv4",
.pkt_to_tuple
= ipv4_pkt_to_tuple,
.invert_tuple
= ipv4_invert_tuple,
.print_tuple
= ipv4_print_tuple,
.get_l4proto
= ipv4_get_l4proto,
#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
.tuple_to_nlattr = ipv4_tuple_to_nlattr,
.nlattr_to_tuple = ipv4_nlattr_to_tuple,
.nla_policy
= ipv4_nla_policy,
#endif
#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
.ctl_table_path
= nf_net_ipv4_netfilter_sysctl_path,
.ctl_table
= ip_ct_sysctl_table,
#endif
.me
= THIS_MODULE,
};

主要是一些回调函数指针,这些回调会在连接跟踪子系统在协议栈的固定流程中被调用,下面看一些关键回调函数的实现。

2.1 ipv4_pkt_to_tuple()

在笔记连接跟踪子系统之核心数据结构中,有看到当从skb转换成tuple时,会依次调用L3协议和L4协议的pkt_to_tuple()回调,下面是AF_INET协议族的L3协议的实现。

static int ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff, struct nf_conntrack_tuple *tuple)
{
const __be32 *ap;
__be32 _addrs[2];
//从skb的指定偏移处(入参2)拷贝指定字节(入参3)数据到地址_addrs处
ap = skb_header_pointer(skb, nhoff + offsetof(struct iphdr, saddr), sizeof(u_int32_t) * 2, _addrs);
if (ap == NULL)
return 0;
//ap[0]和ap[1]分别就是IP首部的源IP和目的IP地址字段,并且都是大端表示
tuple->src.u3.ip = ap[0];
tuple->dst.u3.ip = ap[1];
//因为只能获取L3地址信息,L4信息需要由具体的L4协议来解析报文获取,
//所以这里永远返回1,这样pkt_to_tuple()会就调用L4协议的该回调
return 1;
}

可见,AF_INET协议族L3协议的pkt_to_tuple()回调函数逻辑简单清晰,就是将IP数据包中的源IP和目的IP复制到tuple中的指定位置。

2.2 ipv4_get_l4proto()

数据包skb进入连接跟踪子系统后,首先需要做的事情就是找到能够处理该skb的L3和L4协议,这样才能交由具体的协议来正确的处理数据包。作为L3协议,需要提供get_l4proto()回调来在找到L3协议的基础上,进一步找到能够处理该skb的L4协议,下面就是AF_INET协议族的该回调实现(实际上可以想得到,就是从IP数据包首部协议字段)。

static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
unsigned int *dataoff, u_int8_t *protonum)
{
const struct iphdr *iph;
struct iphdr _iph;
iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
if (iph == NULL)
return -NF_DROP;
//这里不处理分片
if (iph->frag_off & htons(IP_OFFSET))
return -NF_DROP;
*dataoff = nhoff + (iph->ihl << 2);
*protonum = iph->protocol;
return NF_ACCEPT;
}

3. Netfilter钩子

要想真正的让整个连接跟踪子系统动起来,当然还需要向Netfilter框架注册钩子函数,AF_INET协议族为支持连接跟踪子系统,注册了如下钩子函数:

/* Connection tracking may drop packets, but never alters them, so
make it the first hook. */
static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
{
.hook
= ipv4_conntrack_defrag,
.owner
= THIS_MODULE,
.pf
= PF_INET,
.hooknum	= NF_INET_PRE_ROUTING,
.priority	= NF_IP_PRI_CONNTRACK_DEFRAG,
},
{
.hook
= ipv4_conntrack_in,
.owner
= THIS_MODULE,
.pf
= PF_INET,
.hooknum	= NF_INET_PRE_ROUTING,
.priority	= NF_IP_PRI_CONNTRACK,
},
{
.hook
= ipv4_conntrack_defrag,
.owner
= THIS_MODULE,
.pf
= PF_INET,
.hooknum
= NF_INET_LOCAL_OUT,
.priority
= NF_IP_PRI_CONNTRACK_DEFRAG,
},
{
.hook
= ipv4_conntrack_local,
.owner
= THIS_MODULE,
.pf
= PF_INET,
.hooknum	= NF_INET_LOCAL_OUT,
.priority	= NF_IP_PRI_CONNTRACK,
},
{
.hook
= ipv4_conntrack_help,
.owner
= THIS_MODULE,
.pf
= PF_INET,
.hooknum	= NF_INET_POST_ROUTING,
.priority	= NF_IP_PRI_CONNTRACK_HELPER,
},
{
.hook
= ipv4_conntrack_help,
.owner
= THIS_MODULE,
.pf
= PF_INET,
.hooknum	= NF_INET_LOCAL_IN,
.priority	= NF_IP_PRI_CONNTRACK_HELPER,
},
{
.hook
= ipv4_confirm,
.owner
= THIS_MODULE,
.pf
= PF_INET,
.hooknum	= NF_INET_POST_ROUTING,
.priority	= NF_IP_PRI_CONNTRACK_CONFIRM,
},
{
.hook
= ipv4_confirm,
.owner
= THIS_MODULE,
.pf
= PF_INET,
.hooknum	= NF_INET_LOCAL_IN,
.priority	= NF_IP_PRI_CONNTRACK_CONFIRM,
},
};

钩子较多,但是它们是有规律的。把连接跟踪子系统看做一个黑盒,进入该黑盒的点为PRE_ROUTING和LOCAL_OUT,从该黑盒出去的点为LOCAL_IN和POST_ROUTING,所以连接跟踪子系统在每个入口点和出口点个注册了2个钩子。以PRE_ROUTING点为例:优先级-400注册了钩子ipv4_conntrack_defrag(),该优先级很高,保证连接跟踪模块能够最先处理数据包,该函数完成IP报文的组装(连接跟踪模块不处理分片报文);还有一个优先级为-200的钩子ipv4_conntrack_in(),执行数据包进入连接跟踪子系统时处理。在出口处有help和confirm,它们只是操作不同,但设计思想是一样的。

最后

以上就是会撒娇裙子为你收集整理的连接跟踪子系统之AF_INET初始化1. 初始化2. L3协议3. Netfilter钩子的全部内容,希望文章能够帮你解决连接跟踪子系统之AF_INET初始化1. 初始化2. L3协议3. Netfilter钩子所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部