nf_conntrack_tcp_loose选项如果设置为0,对于未完成三次握手的流,内核连接跟踪模块将不会为其创建conntrack结构。反之,值为0的话,将为任意收到的tcp报文创建conntrack结构。默认值为一,如下可通过proc文件nf_conntrack_tcp_loose,获取其当前值。
$ sudo modprobe nf_conntrack_ipv4
$
$ lsmod | grep conn
nf_conntrack_ipv4 16384 0
nf_conntrack 131072 1 nf_conntrack_ipv4
nf_defrag_ipv4 16384 1 nf_conntrack_ipv4
$
$ cat /proc/sys/net/netfilter/nf_conntrack_tcp_loose
1
初始化
以下tcp_init_net初始化函数,将tcp_loose初始化为一。
/* If it is set to zero, we disable picking up already established connections. */
static int nf_ct_tcp_loose __read_mostly = 1;
static int tcp_init_net(struct net *net)
{
struct nf_tcp_net *tn = nf_tcp_pernet(net);
struct nf_proto_net *pn = &tn->pn;
if (!pn->users) {
...
tn->tcp_loose = nf_ct_tcp_loose;
conntrack四层数据
在如下的conntrack入口函数nf_conntrack_in,将调用四层协议处理结构,如TCP,初始化conntrack结构中的四层协议数据。如果失败的话,将释放刚刚创建的conntrack结构,并且结束执行。
unsigned int nf_conntrack_in(struct sk_buff *skb, const struct nf_hook_state *state)
{
const struct nf_conntrack_l4proto *l4proto;
l4proto = __nf_ct_l4proto_find(protonum);
repeat:
ret = resolve_normal_ct(tmpl, skb, dataoff, protonum, l4proto, state);
ret = l4proto->packet(ct, skb, dataoff, ctinfo, state);
if (ret <= 0) {
pr_debug("nf_conntrack_in: Can't track with proto module\n");
nf_conntrack_put(&ct->ct_general);
skb->_nfct = 0;
ret = -ret;
goto out;
}
对于TCP,对应的四层结构为nf_conntrack_l4proto_tcp,注册的处理函数为tcp_packet。
const struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp =
{
.l4proto = IPPROTO_TCP,
.packet = tcp_packet,
.init_net = tcp_init_net,
.get_net_proto = tcp_get_net_proto,
如下函数tcp_packet,调用函数tcp_new初始化TCP相关数据。
static int tcp_packet(struct nf_conn *ct, struct sk_buff *skb,
unsigned int dataoff, enum ip_conntrack_info ctinfo, const struct nf_hook_state *state)
{
...
if (!nf_ct_is_confirmed(ct) && !tcp_new(ct, skb, dataoff, th))
return -NF_ACCEPT;
函数tcp_new,首先判断接收到的是否为TCP的SYN报文,即由tcp_conntracks取得的新状态是否为TCP_CONNTRACK_SYN_SENT,成立的话,说明为SYN报文,进行正常的处理。除此之外,对于tcp_loose等于零的情况,不初始化TCP层的连接跟踪结构,返回false。
static noinline bool tcp_new(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, const struct tcphdr *th)
{
struct net *net = nf_ct_net(ct);
const struct nf_tcp_net *tn = nf_tcp_pernet(net);
new_state = tcp_conntracks[0][get_conntrack_index(th)][TCP_CONNTRACK_NONE];
if (new_state == TCP_CONNTRACK_SYN_SENT) {
memset(&ct->proto.tcp, 0, sizeof(ct->proto.tcp));
/* SYN packet */
...
} else if (tn->tcp_loose == 0) {
/* Don't try to pick up connections. */
return false;
}
由以上的nf_conntrack_in函数可见,四层协议的处理函数tcp_packet(子函数tcp_new)返回负值的话,将不会为此流创建conntrack结构。
conntrack状态INVALID
如下的state匹配处理函数state_mt,对于报文skb中连接跟踪结构为空,并且未设置notrack标志的连接(注意这里并没有创建连接结构),连接状态为无效状态XT_STATE_INVALID。即以上的流将处于此状态。
static bool state_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_state_info *sinfo = par->matchinfo;
enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
if (ct)
statebit = XT_STATE_BIT(ctinfo);
else if (ctinfo == IP_CT_UNTRACKED)
statebit = XT_STATE_UNTRACKED;
else
statebit = XT_STATE_INVALID;
return (sinfo->statemask & statebit);
}
static struct xt_match state_mt_reg __read_mostly = {
.name = "state",
.family = NFPROTO_UNSPEC,
.checkentry = state_mt_check,
.match = state_mt,
可通过以下的iptables命令,丢弃此类报文。
iptables -A INPUT -i eth0 -p tcp -m state --state INVALID -j DROP
内核版本 5.0