Linux中netfilter模块编程实践


 上篇我们看了netfilter的实现机制,这篇来实现下netfilter模块实操一把。

为了注册一个钩子,需要填充nf_hook_ops结构体,包括优先级,钩子地点和钩子函数。然后调用nf_register_hook()函数。

1.   数据结构

 

 

struct nf_hook_ops {

        /* User fills in from here down. */

        nf_hookfn               *hook;

        struct net_device       *dev;

        void                    *priv;                   

        u_int8_t                pf;

        unsigned int            hooknum;

        /* Hooks are ordered in ascending priority. */

        int                     priority;                

};

定义在include/linux/netfilter.h

 

2.   模块代码一丢弃所有UDP输入包

本DEMO实现将所有输入的UDP包丢弃。钩子函数为hook_func,通过ip头的协议来指定,协议17就是表示此包是UDP包,如果将17改成1就会丢弃icmp包了。

.hooknum = 1表示是入口点过滤 NF_IP_LOCAL_IN ,如果改成3就是NF_IP_LOCAL_OUT了,这个定在内核文件include/uapi/linux/netfilter_ipv4.h,如下:

/* IP Hooks */           

/* After promisc drops, checksum checks. */

#define NF_IP_PRE_ROUTING       0

/* If the packet is destined for this box. */

#define NF_IP_LOCAL_IN          1

/* If the packet is destined for another interface. */

#define NF_IP_FORWARD           2

/* Packets coming from a local process. */

#define NF_IP_LOCAL_OUT         3

/* Packets about to hit the wire. */

#define NF_IP_POST_ROUTING      4

#define NF_IP_NUMHOOKS          5

编写源码netmod.c如下:

#include

#include

#include

#include

#include

#include

 

/* This function to be called by hook. */

static 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 udphdr *udp_header;

  struct iphdr *ip_header = (struct iphdr *) skb_network_header (skb);

 

  if (ip_header->protocol == 17)

    {

      udp_header = (struct udphdr *) skb_transport_header (skb);

      printk (KERN_INFO "Drop udp packet.\n");

 

      return NF_DROP;

    }

 

  return NF_ACCEPT;

}

 

static struct nf_hook_ops nfho = {

  .hook = hook_func,

  .hooknum = 1,             /* NF_IP_LOCAL_IN */

  .pf = PF_INET,

  .priority = NF_IP_PRI_FIRST,

};

 

static int __init

init_nf (void)

{

  printk (KERN_INFO "Register netfilter module.\n");

  nf_register_hook (&nfho);

 

  return 0;

}

 

static void __exit

exit_nf (void)

{

  printk (KERN_INFO "Unregister netfilter module.\n");

  nf_unregister_hook (&nfho);

}

 

module_init (init_nf);

module_exit (exit_nf);

MODULE_LICENSE ("GPL");

编写Makefile如下:

obj-m := netmod.o

modules-objs:= netmod.o

 

KDIR := /lib/modules/`uname -r`/build

PWD := $(shell pwd)

 

default:

              make -C $(KDIR) M=$(PWD) modules

 

clean:

            rm -rf *.o .* .cmd *.ko *.mod.c .tmp_versions

       编译后即可使用insmod将模块插入到系统中。

       测试完毕后,记得卸载模块,不然后来的同学可能需要去定位网络问题了。

3.   模块代码二丢弃指定IP的TCP输入包

关于模块参数的导入,使用module_param。

module_param的定义可以在include/linux/moduleparam.h文件里面查看到,它的原型为:

module_param(name, type, perm);

module_param_array(name, type, nump, perm);

       其中module_param是用来传递变量参数的,module_param_array是用来传递数组参数的。

name是在模块中定义的变量名称,type是变量的类型,perm是权限掩码。

权限在include/linux/stat.h中有定义

#define S_IRWXU 00700

#define S_IRUSR 00400

#define S_IWUSR 00200

#define S_IXUSR 00100

#define S_IRWXG 00070

#define S_IRGRP 00040

#define S_IWGRP 00020

#define S_IXGRP 00010

#define S_IRWXO 00007

#define S_IROTH 00004

#define S_IWOTH 00002

#define S_IXOTH 00001

另外nump是传入数组的数目,是一个int指针。参数可以是以下类型:

byte、short、ushort、int、uint、long、ulong、char(字符指针)、bool、invbool(布尔的反,invbool 类型颠倒了值, 所以真值变成 false,)

可以在导入的时候修改模块参数值:

insmod netmod.ko name=hello

       源码如下:

#include

#include

#include

#include

#include

#include

#include

#include /*in_aton()*/

 

/* This function to be called by hook. */

MODULE_LICENSE("Dual BSD/GPL"); 

 

static char* n_ip = "192.168.1.63"; //ip=192.168.1.61

module_param(n_ip, charp, S_IRUGO);

 

static 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 tcphdr *tcph = tcp_hdr (skb);

  struct iphdr *iph = ip_hdr (skb);

  struct tcphdr *modtcph;

  unsigned char *tail;

  unsigned char *user_data;

  unsigned char *it;

  struct sk_buff *modskb;

  char *tempPay;

  char *payload;       //Char array to store original payload before modifications

  int lenOrig;

  int lenNew;

  u16 sport, dport;

  u32 saddr, daddr;

  int i1,i2,i3,i4;

 

  //tempPay = kmalloc (1500, GFP_KERNEL);

  //payload = kmalloc (1500, GFP_KERNEL);

  if (!skb)

    return NF_ACCEPT;

  saddr = ntohl (iph->saddr);

  daddr = ntohl (iph->daddr);

  sport = ntohs (tcph->source);

  dport = ntohs (tcph->dest);

  tail = skb_tail_pointer (skb);

 

  user_data = (unsigned char *) ((unsigned char *) tcph + (tcph->doff * 4));

//3232235837 = 192.168.1.61

  if (iph->saddr == in_aton(n_ip) ) //判断ip地址

    {                 

      i1 = saddr>>24;

      i2 = (saddr>>16) & 0x000000ff;

      i3 = (saddr>>8) & 0x000000ff;

      i4 = saddr & 0x000000ff;

 

      printk ("saddr == %d.%d.%d.%d\n ",i1,i2,i3,i4);

      //printk ("saddr == %s\n",iph->saddr);

     

      ip_send_check (iph);

      for (it=user_data;it!=tail;it++)

      {

        *it++;

          printk("%x",*it);

      }

      printk ("\n");

    }

  return NF_ACCEPT;

}

 

static struct nf_hook_ops nfho = {

  .hook = hook_func,

  .hooknum = 3,             /* NF_IP_LOCAL_IN */

  .pf = PF_INET,

  .priority = NF_IP_PRI_FIRST,

};

 

static int __init

init_nf (void)

{

  printk (KERN_INFO "Register netfilter module.\n");

  nf_register_hook (&nfho);

  printk (" n_ip:%s\n",n_ip);

 

  return 0;

}

 

static void __exit

exit_nf (void)

{

  printk (KERN_INFO "Unregister netfilter module.\n");

  nf_unregister_hook (&nfho);

}

 

module_init (init_nf);

module_exit (exit_nf);

MODULE_LICENSE ("GPL");

4.   参考

Writing a Module For netfilter

 

 

 

你可能感兴趣的:(Linux中netfilter模块编程实践)