Linux netfilter 学习笔记 之十五 netfilter模块添加一个match

通过这段时间的学习,基本上熟悉了netfilter模块,为了进一步加深对netfilter的认识以及理解iptablesnetfilter的联系,准备添加一个match模块。

 

在看到网关产品会有一个公网限制的功能,就想着添加一个公网数目限制的功能。

 

该模块实现的功能, 通过该match我们可以设置能够通过网关上网的数目,要想进行公网限制,就需要根据mac地址进行限制操作,这个模块放到ebtables里或许更好,或者在linux桥接模块里借助CAM表更容易实现。为了最小限度的修改协议栈的代码,放在netfilter里更好,由于对ebtables的研究不深入,即放在iptables里实现。

 

最后的命令如下:

# iptables  -N access_limit

# iptables  -I FORWARD -j access_limit

# iptables  -A access_limit -i br0 -m maclimit --maclimit 1 --expire 30000 -j  ACCEPT

 

其中 --maclimit 后面跟的数据是公网限制的数目, --expire后面跟的是超时时间(毫秒级),即对于一个已记录的mac,若在超时时间以后,没有收到相应的数据包,则删除该项。

 

1.相应的数据结构:

该数据结构为学习的mac地址表,当限制的数目不为0时,即对连接跟踪数据流进行学习,学习到一个mac地址,则将一个__xt_mac_limit_entry添加到xt_mac_limit_table->head链表中,当学习到的数目大于设置的数目后,则对后续学习到的mac数据,直接丢掉。

其中max_count代表限制的数目,mac_count代表当前已学习到的mac数目,gc_interval代表垃圾回收时间,也就是 --expire 后面的值。

 

typedef struct __xt_mac_limit_table

{

        struct list_head head;

        u_int32_t max_count;

        u_int32_t mac_count;

        spinlock_t lock;

        u_int32_t gc_interval;  /* gc interval */

        bool init_flag;/*主要用于初始化*/

}xt_mac_limit_table;

 

该结构体对应于一个mac项,对于一个新的数据流,当学习到以后,则创建一个该结构的变量。其中list用于与xt_mac_limit_table->head进行链接,timeout定时器用于垃圾回收,即当网关在一定时间内没有再收到该mac地址相关的数据后,则释放该变量占用的内存,其中超时时间即为gc_intervalsrc_addr为源mac地址。

typedef struct __xt_mac_limit_entry

{

        struct list_head list;

    unsigned char src_addr[ETH_ALEN];

        struct timer_list timeout;

}xt_mac_limit_entry;

 

/*tablesͨҲipt_entry_match->data[]ָ*/

typedef struct __xt_mac_limit_info

{

        u_int32_t max_count;

        u_int32_t expire;

}xt_mac_limit_info;

 

 

2. 功能实现

maclimit0时,则不进行公网接入限制,此时就不对数据包进行学习,全部允许通过。

maclimit不为0时,则启动公网限制,对每一个数据流都进行mac学习,当学习到的mac超过限制后,则丢弃新的数据流。

 而对于已学习到的mac,我们也要进行一个垃圾回收机制,即当在一定时间内收不到该mac相关的数据后,则删除该mac项,这就是所谓的垃圾回收,对于网络协议栈相关的功能开发,很多时候都要考虑到垃圾回收机制。

 












闲话少说,下面就是代码实现。
Kernel:
Linux3.X
linux-3.x/net/netfilter/makefile:
obj-m += xt_maclimit.o



linux-3.x/net/netfilter/xt_maclimit.c
#include <linux/netfilter/xt_maclimit.h>

static xt_mac_limit_table *mac_list_table = NULL;

static xt_mac_limit_entry *mac_limit_find_entry(unsigned char *macaddr)
{
	xt_mac_limit_entry *pos, *n;

	if(macaddr == NULL)
		return NULL;
	
	list_for_each_entry_safe(pos, n,&mac_list_table->head, list)
	{
		if(memcmp(pos->src_addr, macaddr, ETH_ALEN) == 0)
		{
			return pos;
		}
	}
	
	return NULL;
}

static void mac_limit_delete_mac_entry(unsigned char *macaddr)
{

	xt_mac_limit_entry *pos, *n;

	if(macaddr == NULL)
		return ;

	list_for_each_entry_safe(pos, n,&mac_list_table->head, list)
	{
		if(memcmp(pos->src_addr, macaddr, ETH_ALEN) == 0)
		{
			list_del(&pos->list);
			printk(KERN_INFO"%s: delete entry with mac addr is %x:%x:%x:%x:%x:%x\n", __FUNCTION__,
			pos->src_addr[0], pos->src_addr[1], pos->src_addr[2], pos->src_addr[3], pos->src_addr[4], pos->src_addr[5]);
			kfree(pos);
			mac_list_table->mac_count--;
			
			return;
		}
	}


}
static void death_by_timeout(unsigned long mac_ent)
{
	xt_mac_limit_entry *mac_entry = (xt_mac_limit_entry *)mac_ent;

	printk(KERN_INFO "%s: macaddr is %x:%x:%x:%x:%x:%x\n", __FUNCTION__, mac_entry->src_addr[0]
		,mac_entry->src_addr[1], mac_entry->src_addr[2], mac_entry->src_addr[3], mac_entry->src_addr[4],
		mac_entry->src_addr[5]);
	
	spin_lock(&mac_list_table->lock);
	if(!list_empty(&mac_list_table->head))
	{
		mac_limit_delete_mac_entry(mac_entry->src_addr);
	}
	spin_unlock(&mac_list_table->lock);
}

static xt_mac_limit_entry * mac_limit_create_entry(unsigned char *macaddr)
{
	xt_mac_limit_entry *mac_entry;

	if((macaddr == NULL)||(mac_list_table->mac_count >= mac_list_table->max_count))
		return NULL;

	mac_entry = mac_limit_find_entry(macaddr);
	if(mac_entry)
		return mac_entry;

	mac_entry = kmalloc(sizeof(xt_mac_limit_entry), GFP_ATOMIC);
	if((mac_entry == NULL)||(mac_list_table->mac_count >= mac_list_table->max_count))
	{
		printk(KERN_INFO "%s: can not create new mac entry macaddr is %x:%x:%x:%x:%x:%x\n", __FUNCTION__, macaddr[0]
			, macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
		
		return NULL;
	}
	
	memcpy(mac_entry->src_addr, macaddr, ETH_ALEN);
	init_timer(&mac_entry->timeout);
	mac_entry->timeout.data = (unsigned long)mac_entry;
	mac_entry->timeout.function = death_by_timeout;
	mac_entry->timeout.expires = jiffies + msecs_to_jiffies(mac_list_table->gc_interval);
	add_timer(&mac_entry->timeout);
	list_add(&mac_entry->list, &mac_list_table->head);
	mac_list_table->mac_count++;	
	
	return mac_entry;

	
}
static bool mac_limit_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
	bool ret;
	char mac_addr[ETH_ALEN];
	xt_mac_limit_entry *mac_entry;

	memset(mac_addr, 0, ETH_ALEN);

	if (skb->dev == NULL || skb->dev->type != ARPHRD_ETHER)
		return false;
	if (skb_mac_header(skb) < skb->head)
		return false;
	if (skb_mac_header(skb) + ETH_HLEN > skb->data)
		return false;
	
	spin_lock(&mac_list_table->lock);
	
	if(mac_list_table->max_count == 0)
	{
		printk(KERN_INFO"%s: NO LIMIT\n", __FUNCTION__);
		ret = true;
	}
	else
	{
		memcpy(mac_addr, eth_hdr(skb)->h_source, ETH_ALEN);
		mac_entry = mac_limit_find_entry(mac_addr);
		if(mac_entry == NULL)
		{
			if(mac_list_table->mac_count >= mac_list_table->max_count)
			{
				goto hotdrop;
			}
			else
			{
				mac_entry = mac_limit_create_entry(mac_addr);
				if(mac_entry == NULL)
					goto hotdrop;
				
				printk(KERN_INFO "%s: add entry success,macaddr is %x:%x:%x:%x:%x:%x\n", __FUNCTION__, mac_entry->src_addr[0]
					,mac_entry->src_addr[1], mac_entry->src_addr[2], mac_entry->src_addr[3], mac_entry->src_addr[4],
					mac_entry->src_addr[5]);
				ret = true;
			}
		}
		else
		{
			printk(KERN_INFO "%s: find entry , macaddr is %x:%x:%x:%x:%x:%x\n", __FUNCTION__, mac_entry->src_addr[0]
					,mac_entry->src_addr[1], mac_entry->src_addr[2], mac_entry->src_addr[3], mac_entry->src_addr[4],
					mac_entry->src_addr[5]);
			if(del_timer(&mac_entry->timeout))
			{
				printk("%s: update timeout expire\n", __FUNCTION__);
				mac_entry->timeout.expires = jiffies + msecs_to_jiffies(mac_list_table->gc_interval);
				add_timer(&mac_entry->timeout);
			}
			ret = true;
		}
	}

	spin_unlock(&mac_list_table->lock);
	return ret;

hotdrop: 
	 spin_unlock(&mac_list_table->lock);
	 printk(KERN_INFO "%s: drop packets\n", __FUNCTION__);
	par->hotdrop = true;
	return false;	
}
static int mac_limit_checkentry(const struct xt_mtchk_param *par)
{
	const  xt_mac_limit_info *info = par->matchinfo;

	spin_lock(&mac_list_table->lock);
	if(mac_list_table->init_flag == false)
	{
		printk("%s: mac list table init\n", __FUNCTION__);
		mac_list_table->init_flag = true;
		INIT_LIST_HEAD(&mac_list_table->head);
		mac_list_table->max_count = info->max_count;
		mac_list_table->mac_count = 0;
		mac_list_table->gc_interval = info->expire;
	}

	printk(KERN_INFO"%s: max num is %d\n", __FUNCTION__, info->max_count);

	spin_unlock(&mac_list_table->lock);

	return 0;
}

static void mac_limit_destroy(const struct xt_mtdtor_param *par)
{
	xt_mac_limit_entry *pos, *n;


	spin_lock(&mac_list_table->lock);

	printk(KERN_INFO"%s: destory mac list table\n", __FUNCTION__);
	if(mac_list_table->max_count != 0)
	{
		if(!list_empty(&mac_list_table->head))
		{
			list_for_each_entry_safe(pos, n,&mac_list_table->head, list)
			{	
				if(timer_pending(&pos->timeout))
					del_timer(&pos->timeout);
					
				list_del(&pos->list);
				kfree(pos);
			}
			INIT_LIST_HEAD(&mac_list_table->head);
		}
		
	}
	mac_list_table->init_flag = false;
	spin_unlock(&mac_list_table->lock);
}
static struct xt_match mac_limit_mt_reg __read_mostly = {
	.name      = "maclimit",
	.revision  = 0,
	.family    = NFPROTO_UNSPEC,
	.match     = mac_limit_mt,
	.checkentry = mac_limit_checkentry,
	.destroy = mac_limit_destroy,
	.matchsize = sizeof(xt_mac_limit_info),
	.hooks     = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN) |
	             (1 << NF_INET_FORWARD),
	.me        = THIS_MODULE,
};
static int __init mac_limit_mt_init(void)
{
	mac_list_table = kmalloc(sizeof(xt_mac_limit_table),GFP_ATOMIC);
	spin_lock_init(&mac_list_table->lock);
	mac_list_table->init_flag = false;
	return xt_register_match(&mac_limit_mt_reg);
}

static void __exit mac_limit_mt_exit(void)
{
	kfree(mac_list_table);
	xt_unregister_match(&mac_limit_mt_reg);
}

module_init(mac_limit_mt_init);
module_exit(mac_limit_mt_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jerry_chg");
MODULE_DESCRIPTION("Xtables:access limit");
MODULE_ALIAS("ipt_maclimit");




 include/linux/netfilter/xt_maclimit.h
#ifndef _XT_MAC_LIMIT_H
#define _XT_MAC_LIMIT_H
#include <linux/module.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/etherdevice.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter/x_tables.h>
#include <linux/spinlock.h>
#include <linux/random.h>
#include <linux/jhash.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/list.h>

#include <linux/skbuff.h>
#include <linux/mm.h>
#include <linux/in.h>
#include <linux/ip.h>
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
#include <linux/ipv6.h>
#include <net/ipv6.h>
#endif

#include <net/net_namespace.h>
#include <net/netns/generic.h>

#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <linux/mutex.h>

/*mac count limit table */
typedef struct __xt_mac_limit_table
{
        struct list_head head;
        u_int32_t max_count;
        u_int32_t mac_count;
        spinlock_t lock;
        u_int32_t gc_interval;  /* gc interval */
        bool init_flag;
}xt_mac_limit_table;

/*for a  mac struct*/
typedef struct __xt_mac_limit_entry
{
        struct list_head list;
    unsigned char src_addr[ETH_ALEN];
        struct timer_list timeout;
}xt_mac_limit_entry;

/*tablesͨҲipt_entry_match->data[]ָ*/
typedef struct __xt_mac_limit_info
{
        u_int32_t max_count;
        u_int32_t expire;
}xt_mac_limit_info;

#endif /*_XT_MAC_LIMIT_H*/






Iptables下添加一个match还是比较简单的,代码如下:

iptables-1.4.20/extensions/libxt_maclimit.c
iptables-1.4.20/include/linux/netfilter/xt_maclimit.h


xt_maclimit.h:

#ifndef _XT_MAC_LIMIT_H
#define _XT_MAC_LIMIT_H

struct xt_maclimit_info {
    uint32_t limit_count;
        uint32_t expire;
};
#endif /*_XT_MAC_H*/


libxt_maclimit.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <xtables.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_maclimit.h>

enum {
        O_MAC_LIMIT = 0,
        O_MAC_EXPIRE = 1,
};

static void maclimit_help(void)
{
        printf(
"mac match options:\n"
"--maclimit max_count\n"
"--expire  after which time are idle entries expired?\n"
"                               mac count limit module\n");
}

#define s struct xt_maclimit_info
static const struct xt_option_entry maclimit_opts[] = {
        {.name = "maclimit", .id = O_MAC_LIMIT, .type = XTTYPE_UINT32,
                .flags = XTOPT_MAND | XTOPT_PUT,
                XTOPT_POINTER(s, limit_count)
        },
        {.name = "expire", .id = O_MAC_EXPIRE, .type = XTTYPE_UINT32,
                .flags = XTOPT_MAND | XTOPT_PUT,
                 XTOPT_POINTER(s, expire)
        },
        XTOPT_TABLEEND,
};
#undef s





static int  maclimit_max_count_parse(const char *max, struct xt_maclimit_info *info)
{
        uintmax_t v;
        char *end;

        if (!xtables_strtoul(max, &end, &v, 0, 0xffffffff))
        {
                xtables_error(PARAMETER_PROBLEM, "bad value for option "
                        "\"--maclimit\", or out of range (0-32).");
        }

        return v;
}


static int  maclimit_expire_parse(const char *expire, struct xt_maclimit_info *info)
{
        uintmax_t v;
        char *end;

        if (!xtables_strtoul(expire, &end, &v, 0, 0xffffffff))
        {
                xtables_error(PARAMETER_PROBLEM, "bad value for option "
                        "\"--maclimit\", or out of range (0-32).");
        }

        return v;
}

static void maclimit_parse(struct xt_option_call *cb)
{
        struct xt_maclimit_info *maclimitinfo = cb->data;
        struct xt_option_entry *entry = cb->entry;

        xtables_option_parse(cb);
        switch(entry->id)
        {
                case O_MAC_LIMIT:
                        maclimitinfo->limit_count = maclimit_max_count_parse(cb->arg, maclimitinfo);
                        break;

                case O_MAC_EXPIRE:
                        maclimitinfo->expire = maclimit_expire_parse(cb->arg, maclimitinfo);
                        break;
        }

        printf("maclimit count is %d\n", maclimitinfo->limit_count);
        printf("expire is %d\n", maclimitinfo->expire);

}

static void print_maclimit(const uint32_t maclimit)
{
        printf("mac limit count 0x%x", maclimit);
}
static void print_mac_expire(const uint32_t expire)
{
        printf("mac entry expire 0x%x", expire);
}
static void
maclimit_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
        const struct xt_maclimit_info *info = (void *)match->data;

        print_maclimit(info->limit_count);
        print_mac_expire(info->expire);
}

static void maclimit_save(const void *ip, const struct xt_entry_match *match)
{
        const struct xt_maclimit_info *info = (void *)match->data;

        printf(" --maclimit");
        print_maclimit(info->limit_count);
        print_mac_expire(info->expire);
}

static struct xtables_match maclimit_match = {
        .family         = NFPROTO_IPV4,
        .name           = "maclimit",
        .version        = XTABLES_VERSION,
        .size           = XT_ALIGN(sizeof(struct xt_maclimit_info)),
        .userspacesize  = XT_ALIGN(sizeof(struct xt_maclimit_info)),
        .help           = maclimit_help,
        .x6_parse       = maclimit_parse,
        .print          = maclimit_print,
        .save           = maclimit_save,
        .x6_options     = maclimit_opts,
};

void _init(void)
{
        xtables_register_match(&maclimit_match);
}







上文只是简单的实现了公网接入限制,没有在iptables中增加对ipv6的支持,仅仅在iptables中增加了对ipv4的支持,通过以上代码能够简单实现公网接入限制,这也加深了我对netfilter模块中增加match的认识。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值