[TOC]
### **Netfilter的钩子**
netfilter在内核的网络栈中注册了五个钩子(Hook),分别是(参考[此文](https://wiki.aalto.fi/download/attachments/69901948/netfilter-paper.pdf),有没有发现这五个钩子的名字和iptables的五条链的名字很像)
* NF_IP_PRE_ROUTING
* NF_IP_POST_ROUTING
* NF_IP_FILTER
* NF_IP_LOCAL_IN
* NF_IP_LOCAL_OUT
数据包根据流向不同会经过不同的钩子,如下
* 入包:`NF_IP_PRE_ROUTING` -》 `NF_IP_LOCAL_IN`
* 出包:`NF_IP_LOCAL_OUTPUT` -》 `NF_IP_POST_ROUTING`
* 经过:`NF_IP_PRE_ROUTING` -》`NF_IP_FORWARD` -》`NF_IP_POST_ROUTING`
### **回调函数**
netfilter的其他模块比如conntrack、nat在上面的钩子点注册自己的回调函数(也叫钩子函数),当数据包到达某个钩子点时,就会依次调用该钩子点注册的回调函数。
每一个注册的回调函数都会有一个优先级,优先级越高,越先调用。
每一个回调函数都会返回一个结果,结果的取值如下:
* NF_ACCEPT:接收该包,交由下一个回调函数处理
* NF_DROP:丢弃该包
* ...
### **连接追踪**
连接追踪模块只对数据包进行跟踪,可能会丢弃数据包,但是不会修改数据包。修改数据包是NAT模块做的事。
连接追踪模块在`NF_IP_PRE_ROUTING`与`NF_IP_LOCAL_OUTPUT`处注册了回调函数`nf_conntrack_in()`, 在`NF_IP_POST_ROUTING`与 `NF_IP_LOCAL_IN`处注册了回调函数`nf_conntrack_confirm()`。
连接追踪模块有四张表,其中两张比较重要的表为unconfirmed表与confirmed表。
##### **`nf_conntrack_in() net/netfilter/nf_conntrack_core.c`**
我们以经过的数据包为例。当数据包经过`NF_IP_PRE_ROUTING`时,内核会调用`nf_conntrack_in()`函数进行处理。
首先,该函数会查找该数据包是否属于某个连接(`nf_conn`),如果找到了连接,则把该连接的信息保存在数据包的`_nfct`字段中,这样数据包就与某个连接关联起来了。如果没有找到对应的连接,则根据该数据包生成一个连接,
```
unsigned int
nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
struct sk_buff *skb)
{
const struct nf_conntrack_l4proto *l4proto;
struct nf_conn *ct, *tmpl;
enum ip_conntrack_info ctinfo;
u_int8_t protonum;
int dataoff, ret;
tmpl = nf_ct_get(skb, &ctinfo);
# by pshizh: 此处返回null
if (tmpl || ctinfo == IP_CT_UNTRACKED) {
/* Previously seen (loopback or untracked)? Ignore. */
if ((tmpl && !nf_ct_is_template(tmpl)) ||
ctinfo == IP_CT_UNTRACKED) {
NF_CT_STAT_INC_ATOMIC(net, ignore);
return NF_ACCEPT;
}
skb->_nfct = 0;
}
/* rcu_read_lock()ed by nf_hook_thresh */
dataoff = get_l4proto(skb, skb_network_offset(skb), pf, &protonum);
if (dataoff <= 0) {
pr_debug("not prepared to track yet or error occurred\n");
NF_CT_STAT_INC_ATOMIC(net, error);
NF_CT_STAT_INC_ATOMIC(net, invalid);
ret = NF_ACCEPT;
goto out;
}
l4proto = __nf_ct_l4proto_find(pf, protonum);
/* It may be an special packet, error, unclean...
* inverse of the return code tells to the netfilter
* core what to do with the packet. */
if (l4proto->error != NULL) {
ret = l4proto->error(net, tmpl, skb, dataoff, pf, hooknum);
if (ret <= 0) {
NF_CT_STAT_INC_ATOMIC(net, error);
NF_CT_STAT_INC_ATOMIC(net, invalid);
ret = -ret;
goto out;
}
/* ICMP[v6] protocol trackers may assign one conntrack. */
if (skb->_nfct)
goto out;
}
repeat:
ret = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum, l4proto);
if (ret < 0) {
/* Too stressed to deal. */
NF_CT_STAT_INC_ATOMIC(net, drop);
ret = NF_DROP;
goto out;
}
ct = nf_ct_get(skb, &ctinfo);
if (!ct) {
/* Not valid part of a connection */
NF_CT_STAT_INC_ATOMIC(net, invalid);
ret = NF_ACCEPT;
goto out;
}
ret = l4proto->packet(ct, skb, dataoff, ctinfo);
if (ret <= 0) {
/* Invalid: inverse of the return code tells
* the netfilter core what to do */
pr_debug("nf_conntrack_in: Can't track with proto module\n");
nf_conntrack_put(&ct->ct_general);
skb->_nfct = 0;
NF_CT_STAT_INC_ATOMIC(net, invalid);
if (ret == -NF_DROP)
NF_CT_STAT_INC_ATOMIC(net, drop);
/* Special case: TCP tracker reports an attempt to reopen a
* closed/aborted connection. We have to go back and create a
* fresh conntrack.
*/
if (ret == -NF_REPEAT)
goto repeat;
ret = -ret;
goto out;
}
if (ctinfo == IP_CT_ESTABLISHED_REPLY &&
!test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status))
nf_conntrack_event_cache(IPCT_REPLY, ct);
out:
if (tmpl)
nf_ct_put(tmpl);
return ret;
}
```
- 常用命令
- 用户与用户组
- 创建用户与用户组
- 快速脚本
- umask
- Yum源
- 基础Yum源
- Epel源
- 制作Yum源
- 同步Yum源
- 为Yum源配置代理
- 下载RPM及依赖
- 系统与内核
- 获取内核的rpm包
- 升级内核
- Iptables
- 基本语法
- 匹配条件
- 基础匹配条件
- 扩展匹配条件
- Addrtype
- Set
- TCP
- Mark
- Multiport
- 目标
- 基本目标
- 扩展目标
- DNAT
- LOG
- CT
- NOTRACK
- MARK
- IP set
- 连接追踪
- 初识连接追踪
- 连接追踪详解
- NAT
- 思路与参考汇总
- 数据结构
- FAQ
- Keepalived
- 单网卡多VIP
- 安装Keepalived
- 双网卡绑VIP
- 别名VIP和辅助VIP
- LVS
- 安装LVS
- Ipvsadm命令
- 磁盘与分区
- 基础知识
- 创建分区
- 格式化与挂载
- Fstab
- LVM
- LVM扩容
- Swap分区
- Tmpfs
- 网络相关
- 重命名网卡
- resolv.conf
- Tcpdump
- nslookup与dig
- ifcg-xxx
- 主机名
- 软件安装
- NFS
- Squid
- Redsocks
- Shadowsocks
- 时钟同步
- Chrony
- FTP
- 文件句柄
- 简介
- 设置文件句柄
- 其他
- SSH密钥登录
- 进程组-会话-终端
- X11转发
- 环境变量
- 常见问题
- 系统进程数
- 系统调用
- 系统调用FAQ
- 用户程序如何进行系统调用