连接跟踪nf_conntrack_tcp_loose设置

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

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页