linux nf_conntrack 连接跟踪机制 2
连接跟踪初始化
基础参数的初始化:nf_conntrack_standalone_init 会调用nf_conntrack_init_start 完成连接跟踪基础参数的初始化, hash slab 扩展项 等;
nf_conntrack_l3proto_ipv4_init 函数初始化了协议和tuple操作函数的相关初始化;
static int ipv4_net_init(struct net *net)
{
int ret = 0;
/*
注册了和 IPv4 相关的几个 4 层 TCP、UDP、ICMP等协议
3个l4proto与1个l3proto在pernet的初始化
*/
ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp4);
if (ret < 0) {
pr_err("nf_conntrack_tcp4: pernet registration failed\n");
goto out_tcp;
}
ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp4);
if (ret < 0) {
pr_err("nf_conntrack_udp4: pernet registration failed\n");
goto out_udp;
}
ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmp);
if (ret < 0) {
pr_err("nf_conntrack_icmp4: pernet registration failed\n");
goto out_icmp;
}
ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv4);
if (ret < 0) {
pr_err("nf_conntrack_ipv4: pernet registration failed\n");
goto out_ipv4;
}
return 0;
out_ipv4:
nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmp);
out_icmp:
nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp4);
out_udp:
nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp4);
out_tcp:
return ret;
}
static int __init nf_conntrack_l3proto_ipv4_init(void)
{
int ret = 0; need_conntrack();
nf_defrag_ipv4_enable();
/*
用户态与内核态交互通信的方法sockopt,写法也简单.
缺点就是使用 copy_from_user()/copy_to_user()完成内核和用户的通信, 效率其实不高, 多用在传递控制 选项 信息,不适合做大量的数据传输
*/
ret = nf_register_sockopt(&so_getorigdst);
if (ret < 0) {
pr_err("Unable to register netfilter socket option\n");
return ret;
} /**调用ipv4_net_init 完成相关初始化*/
ret = register_pernet_subsys(&ipv4_net_ops);
if (ret < 0) {
pr_err("nf_conntrack_ipv4: can't register pernet ops\n");
goto cleanup_sockopt;
}
/*注册hook */
ret = nf_register_hooks(ipv4_conntrack_ops,
ARRAY_SIZE(ipv4_conntrack_ops));
if (ret < 0) {
pr_err("nf_conntrack_ipv4: can't register hooks.\n");
goto cleanup_pernet;
}
/* nf_conntrack_l4proto tcp相关初始化 */
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp4);
if (ret < 0) {
pr_err("nf_conntrack_ipv4: can't register tcp4 proto.\n");
goto cleanup_hooks;
}
/* nf_conntrack_l4proto udp相关初始化 */
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp4);
if (ret < 0) {
pr_err("nf_conntrack_ipv4: can't register udp4 proto.\n");
goto cleanup_tcp4;
}
/* nf_conntrack_l4proto icmp相关初始化 */
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmp);
if (ret < 0) {
pr_err("nf_conntrack_ipv4: can't register icmpv4 proto.\n");
goto cleanup_udp4;
}
/* nf_conntrack_l3proto ip相关初始化 */
ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4);
if (ret < 0) {
pr_err("nf_conntrack_ipv4: can't register ipv4 proto.\n");
goto cleanup_icmpv4;
}
return ret; }
nf_conntrack_l3proto_ipv4 结构中包含了基础信息 tuple 钩子回调函数;
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 IS_ENABLED(CONFIG_NF_CT_NETLINK)
.tuple_to_nlattr = ipv4_tuple_to_nlattr,
.nlattr_tuple_size = ipv4_nlattr_tuple_size,
.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 = "net/ipv4/netfilter",
#endif
.init_net = ipv4_init_net,
.me = THIS_MODULE,
};
static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
struct nf_conntrack_tuple *tuple)
{/* 从ip头中获取源目的地址,存入tuple */
const __be32 *ap;
__be32 _addrs[2];
ap = skb_header_pointer(skb, nhoff + offsetof(struct iphdr, saddr),
sizeof(u_int32_t) * 2, _addrs);
if (ap == NULL)
return false; tuple->src.u3.ip = ap[0];
tuple->dst.u3.ip = ap[1]; return true;
} static bool ipv4_invert_tuple(struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_tuple *orig)
{
tuple->src.u3.ip = orig->dst.u3.ip;
tuple->dst.u3.ip = orig->src.u3.ip;
/* 根据原tuple地址设置新tuple,源目的地址均相反 */
return true;
} static void ipv4_print_tuple(struct seq_file *s,
const struct nf_conntrack_tuple *tuple)
{
seq_printf(s, "src=%pI4 dst=%pI4 ",
&tuple->src.u3.ip, &tuple->dst.u3.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;
/* 获取ip头中的协议 */
iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
if (iph == NULL)
return -NF_ACCEPT; /* Conntrack defragments packets, we might still see fragments
* inside ICMP packets though. */
if (iph->frag_off & htons(IP_OFFSET))
return -NF_ACCEPT; *dataoff = nhoff + (iph->ihl << 2);
*protonum = iph->protocol; /* Check bogus IP headers */
if (*dataoff > skb->len) {
pr_debug("nf_conntrack_ipv4: bogus IPv4 packet: "
"nhoff %u, ihl %u, skblen %u\n",
nhoff, iph->ihl << 2, skb->len);
return -NF_ACCEPT;
} return NF_ACCEPT;
}
contrack _in helper confirm 等钩子函数的注册
* 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_in,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_CONNTRACK,
},
{
.hook = ipv4_conntrack_local,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_CONNTRACK,
},
{
.hook = ipv4_helper,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_CONNTRACK_HELPER,
},
{
.hook = ipv4_confirm,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_CONNTRACK_CONFIRM,
},
{
.hook = ipv4_helper,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_CONNTRACK_HELPER,
},
{
.hook = ipv4_confirm,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_CONNTRACK_CONFIRM,
},
};
基于ip层的协议回实现自己的nf_contrack_l4proto,现在以udp为例
das
struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly =
{
.l3proto = PF_INET,
.l4proto = IPPROTO_UDP,
.name = "udp",
.allow_clash = true,
.pkt_to_tuple = udp_pkt_to_tuple,
.invert_tuple = udp_invert_tuple,
.print_tuple = udp_print_tuple,
.packet = udp_packet,
.get_timeouts = udp_get_timeouts,
.new = udp_new,
.error = udp_error,
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
.nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
.nlattr_tuple_size = nf_ct_port_nlattr_tuple_size,
.nla_policy = nf_ct_port_nla_policy,
#endif
#if IS_ENABLED(CONFIG_NF_CT_NETLINK_TIMEOUT)
.ctnl_timeout = {
.nlattr_to_obj = udp_timeout_nlattr_to_obj,
.obj_to_nlattr = udp_timeout_obj_to_nlattr,
.nlattr_max = CTA_TIMEOUT_UDP_MAX,
.obj_size = sizeof(unsigned int) * CTA_TIMEOUT_UDP_MAX,
.nla_policy = udp_timeout_nla_policy,
},
#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
.init_net = udp_init_net,
.get_net_proto = udp_get_net_proto,
};
static bool udp_pkt_to_tuple(const struct sk_buff *skb,
unsigned int dataoff,
struct net *net,
struct nf_conntrack_tuple *tuple)
{
const struct udphdr *hp;
struct udphdr _hdr;
/*获取四册协议对应元祖信息*/
/* Actually only need first 8 bytes. */
hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
if (hp == NULL)
return false; tuple->src.u.udp.port = hp->source;
tuple->dst.u.udp.port = hp->dest; return true;
}
其注册L3 L4回调信息 后结构示意图如下所示:
在netfilter框架中利用nf_register_hook(struct nf_hook_ops *reg)、nf_unregister_hook(struct nf_hook_ops *reg)函数注册自己的钩子项;
上图来自:http://bbs.chinaunix.net/thread-4082396-1-1.html
linux nf_conntrack 连接跟踪机制 2的更多相关文章
- linux nf_conntrack 连接跟踪机制 3-hook
conntrack hook函数分析 enum nf_ip_hook_priorities { NF_IP_PRI_FIRST = INT_MIN, NF_IP_PRI_CONNTRACK_DEFRA ...
- linux nf_conntrack 连接跟踪机制
PRE_ROUTING和LOCAL_OUT点可以看作是整个netfilter的入口,而POST_ROUTING和LOCAL_IN可以看作是其出口; 报文到本地:PRE_ROUTING----LOCAL ...
- Netfilter&iptables:如何理解连接跟踪机制?
如何理解Netfilter中的连接跟踪机制? 本篇我打算以一个问句开头,因为在知识探索的道路上只有多问然后充分调动起思考的机器才能让自己走得更远.连接跟踪定义很简单:用来记录和跟踪连接的状态. 问:为 ...
- Netfilter之连接跟踪实现机制初步分析
Netfilter之连接跟踪实现机制初步分析 原文: http://blog.chinaunix.net/uid-22227409-id-2656910.html 什么是连接跟踪 连接跟踪(CONNT ...
- [转]nf_conntrack: table full, dropping packet 连接跟踪表已满,开始丢包 的解决办法
nf_conntrack: table full, dropping packet 连接跟踪表已满,开始丢包 的解决办法 中午业务说机器不能登录,我通过USM管理界面登录单板的时候发现机器没有僵 ...
- linux内核netfilter连接跟踪的hash算法
linux内核netfilter连接跟踪的hash算法 linux内核中的netfilter是一款强大的基于状态的防火墙,具有连接跟踪(conntrack)的实现.conntrack是netfilte ...
- linux下epoll实现机制
linux下epoll实现机制 原作者:陶辉 链接:http://blog.csdn.net/russell_tao/article/details/7160071 先简单回顾下如何使用C库封装的se ...
- Linux信号(signal) 机制分析
Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...
- 2017-2018-1 20155222 《信息安全系统设计基础》第10周 Linux下的IPC机制
2017-2018-1 20155222 <信息安全系统设计基础>第10周 Linux下的IPC机制 IPC机制 在linux下的多个进程间的通信机制叫做IPC(Inter-Process ...
随机推荐
- linux启动过程中建立临时页表
intel的x86这种架构为了兼容以前同系列的架构有一些很繁琐无用的东西.比如分段和分页两种机制都可以实现隔离进程的内存空间,在x86上两种机制都有,用起来比较繁琐.所以linux内核在启动的时候通过 ...
- 多测师讲解selenium__论坛验证码_处理方法_ 高级讲师肖sir
1.登录我们搭建的论坛环境 2. 3. 4.
- MeteoInfoLab脚本示例:AIRS Grid HDF数据
AIRS Grid HDF数据是HDF4 EOS格式,数据地理坐标信息可以被MeteoInfo自动识别,脚本程序更为简单.需要注意的是读取数据时Y轴是反向的(卫星数据通常如此).脚本程序: #Add ...
- 面试一个百度T7程序员,一道简单的题没答上来!网友却都在吐槽面试官!
程序员面试时都考些什么? 一个面试官得意洋洋地说自己面了一个百度T7,出了一道coding题,结果对方连最长上升子序列都写不出来. 楼主本想嘲弄一下百度T7的代码水平低,没想到网友们炸开了锅,纷纷 ...
- CPU 底层运算之乘法运算
CPU 运算加减法运算 假设计算 3+3 原码是0011 * 0011(以4位存贮单元,因为是原码,最高位不代表符号位) 1. 首先 判断 两个加数是否有 负数(减法) 如果有 负数 先将负数转 ...
- Docker结合.Net Core初步使用
Docker是一项比较流行的容器化技术,可以让开发者将应用以及应用依赖的环境,依赖包一起打包到容器中,然后部署容器到生产环境就可以了,解决了应用程序部署到不同服务器环境带来的问题(很多开发人员都遇到过 ...
- IDEA Cannot resolve plugin org.apache.maven.plugins:maven-site-plugin:3.8.2-plugin爆红错误
如果确认本地库存在,maven仓库配置正确,将其显式声明出来,问题解决 <!--报找不到该依赖的错误, 本地库又存在,将其显式声明在这里,问题解决--> <plugin> &l ...
- 涨知识的一个pwn题:de1ctf_2019_weapon
没做出来,wtcl,看了师傅们的wp才找到思路,收获了很多 怎么说呢,这个题很简单但是很巧妙,逆起来几乎无难度 漏洞点位于free函数,一个简单的UAF漏洞 然后接下来说说我一开始的思路 由于程序没有 ...
- Intellij IDEA创建Maven Web项目(带有webapp文件夹目录的项目)
每日技术点记录一下https://blog.csdn.net/mawei7510/article/details/83089268
- js逻辑程序
JavaScript 是 Web 的编程语言. 所有现代的 HTML 页面都使用 JavaScript. JavaScript 非常容易学. 本教程将教你学习从初级到高级 JavaScript 知识. ...