服务器 频道

深入Linux网络核心堆栈之三

  ----[ 4.3 - 基于地址进行过滤

  与根据数据包的接口进行过滤类似,基于数据包的源或目的IP地址进行过滤同样简单。这次我们感兴趣的是sk_buff数据结构。还记得skb参数是一个指向sk_buff数据结构的指针的指针吗?为了避免犯错误,声明一个另外的指向skb_buff数据结构的指针并且将skb指针指向的指针赋值给这个新的指针是一个好习惯,就像这样:

    struct sk_buff *sb = *skb;    /* Remove 1 level of indirection* /

  这样,你访问这个数据结构的元素时只需要反引用一次就可以了。获取一个数据包的IP头通过使用sk_buff数据结构中的网络层包头来完成。这个头位于一个联合中,可以通过sk_buff->nh.iph这样的方式来访问。示例代码3中的函数演示了当得到一个数据包的sk_buff数据结构时,如何利用它来检查收到的数据包的源IP地址与被禁止的地址是否相同。这些代码是直接从LWFW中取出来的,唯一不同的是LWFW统计的更新被移除。

  示例代码3 : 检查收到的数据包的源IP

    unsigned char *deny_ip = "\x7f\x00\x00\x01";  /* 127.0.0.1 */
    
    ...

        static int check_ip_packet(struct sk_buff *skb)
        {
            /* We don''t want any NULL pointers in the chain to
             * the IP header. */
            if (!skb )return NF_ACCEPT;
            if (!(skb->nh.iph)) return NF_ACCEPT;
        
            if (skb->nh.iph->saddr == *(unsigned int *)deny_ip) {
            return NF_DROP;
            }

            return NF_ACCEPT;
        }

  这样,如果数据包的源地址与我们设定的丢弃数据包的地址匹配,那么该数据包将被丢弃。为了使这个函数能按预期的方式工作,deny_ip的值应当以网络字节序(Big-endian,与Intel相反)存放。虽然这个函数不太可能以一个空的指针作为参数来调用,带一点点偏执狂从来不会有什么坏处。当然,如果错误确实发生了,那么该函数将会返回NF_ACCEPT。这样Netfilter可以继续处理这个数据包。示例代码4展现了用于演示将基于接口的过滤略做修改以丢弃匹配给定IP地址的数据包的简单模块。

  示例代码4 : 基于数据包源地址的过滤
  /* 安装丢弃所有来自指定IP地址的数据包的Netfilter hook的示例代码 */

  #define __KERNEL__
  #define MODULE

  #include <linux/module.h>
  #include <linux/kernel.h>
  #include <linux/skbuff.h>
  #include <linux/ip.h>                  /* For IP header */
  #include <linux/netfilter.h>
  #include <linux/netfilter_ipv4.h>

  /* 用于注册我们的函数的数据结构 */
  static struct nf_hook_ops nfho;

  /* 我们要丢弃的数据包来自的地址,网络字节序 */
  static unsigned char *drop_ip = "\x7f\x00\x00\x01";

  /* 注册的hook函数的实现 */
  unsigned int hook_func(unsigned int hooknum,
                     struct sk_buff **skb,
                     const struct net_device *in,
                     const struct net_device *out,
                     int (*okfn)(struct sk_buff *))
  {
  struct sk_buff *sb = *skb;

  // 译注:作者提供的代码中比较地址是否相同的方法是错误的,见注释掉的部分
  if (sb->nh.iph->saddr == *(unsigned int *)drop_ip) {
  // if (sb->nh.iph->saddr == drop_ip) {
      printk("Dropped packet from... %d.%d.%d.%d\n",
    *drop_ip, *(drop_ip + 1),
    *(drop_ip + 2), *(drop_ip + 3));
      return NF_DROP;
  } else {
      return NF_ACCEPT;
  }
  }

  /* 初始化程序 */
  int init_module()
  {
  /* 填充我们的hook数据结构 */
  nfho.hook       = hook_func;         /* 处理函数 */
  nfho.hooknum  = NF_IP_PRE_ROUTING; /* 使用IPv4的第一个hook */
  nfho.pf       = PF_INET;
  nfho.priority = NF_IP_PRI_FIRST;   /* 让我们的函数首先执行 */

  nf_register_hook(&nfho);

  return 0;
  }

  /* 清除程序 */
  void cleanup_module()
  {
  nf_unregister_hook(&nfho);
  }

0
相关文章