学习了 Linux netfilter 钩子执行过程,接下来在内核模块里实现一个自定义的 netfilter 钩子。

netfilter 的网络包处理流程

下图比较详细地描述了 iptables 和 ebtables 的规则执行时机:

netfilter packet flow

图片来源:wikimedia

自定义钩子实验:谁在连接我

如上图,网络层中有 5 条规则链:PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING。我们可以忽略 raw、mangle、nat、filter 等表的定义,因为在内核 netfilter 框架里是没有这些表的概念的;这几张表的定义都存在于 iptables 中。

为了记录是谁尝试跟我进行 TCP 连接,需要在 hook 中判断当前网络包是不是 TCP SYN 包;

该 hook 需要放在 PREROUTING 或者 INPUT 链中。自定义 hook 的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
unsigned int who_connect_me_hook(void *priv,
    struct sk_buff *skb,
    const struct nf_hook_state *state)
{
  __be32 saddr, daddr;

  struct iphdr *iphdr;
  struct tcphdr *tcphdr;

  // 从 skb 中获取 IP 协议头
  iphdr = (struct iphdr *)skb_network_header(skb);
  saddr = iphdr->saddr;
  daddr = iphdr->daddr;
  if (iphdr->protocol == IPPROTO_TCP) {
      // 从 skb 中获取 TCP 协议头
      tcphdr = (struct tcphdr *)skb_transport_header(skb);
      if (tcphdr->syn) { // 判断是不是 SYN 包
        // 将源 IP 和源端口打印出来
        printk(KERN_INFO "[who_connect_me] [%pI4:%d -> %pI4:%d]\n",
            (unsigned char *)(&saddr), ntohs(tcphdr->source),
            (unsigned char *)(&daddr), ntohs(tcphdr->dest));
      }
  }
  return NF_ACCEPT;
}

最终效果如下:

custom netfilter hook result

源代码:github.com/Asphaltt/kernel-module-fun

总结

纸上得来终觉浅,绝知此事要躬行。