修改netfilter的limit模块实现基于单个ip的流量监控

修改netfilter的limit模块实现基于单个ip的流量监控_dog250的博客-CSDN博客1.问题和思路 linux内核的netfilter框架中有一个叫做limit的模块,用于匹配单位时间内过往的包的数量,注意,这个模块实现了一个match,而不能直接用于流控的目的,因此你不能直接使用下列的命令实现流控:iptables –A FORWARD –s xxx –d yyy –m limit ...  –j DROP因为这样的话,所有匹配到的数据包就都被drop掉了。你应该这https://blog.csdn.net/dog250/article/details/6940578

1.问题和思路
linux内核的netfilter框架中有一个叫做limit的模块,用于匹配单位时间内过往的包的数量,注意,这个模块实现了一个match,而不能直接用于流控的目的,因此你不能直接使用下列的命令实现流控:
iptables –A FORWARD –s xxx –d yyy –m limit ...  –j DROP
因为这样的话,所有匹配到的数据包就都被drop掉了。你应该这么做:
iptables –A FORWARD –s xxx –d yyy –m limit ... –j ACCEPT
iptables –A FORWARD –s xxx –d yyy –j DROP
然而仍然需要注意的是,这个match是基于包的数量的,而不是基于数据字节流量的,因此这种流控方式很不准确,如上,限制单个基于ip地址的流在每秒发送20个数据包,而这20个数据包可能是20个mtu大小的数据包,也可能是20个1字节ip载荷大小的数据包,也可能仅仅是20个tcp的ack包,这样的流控显然不是真正的流控。
    我现在需要做的是基于单个源ip进行秒级别的入口流量的字节限速,怎么做呢?当然可以通过tc来做,那就是使用tc的police策略来进行配置,可是那样的话有问题,第一个问题就是police没有队列,这就意味着所有超额的流量将被丢弃而不是被缓存,这也许就是tc社区为何说linux入口限速做的不甚好的原因之所在吧;第二个问题就是你需要把所有需要被限速的ip地址作为filter的匹配规则显式的配置出来,而这会导致策略表的快速膨胀,大大增加了内存的占用。因此不到万不得已,我不会再考虑使用tc来完成这个流控。
    接下来要考虑的就是使用iptables统计来完成流控。因为netfilter会纪录所有rule的统计信息,因此周期的调用iptables–L –x –n …然后将统计信息相减后除以调用周期,使用外部脚本来完成这个流控实际上也是可以的。然而这又会面对和tc同样的问题,既然需要iptables来统计信息,那么统计哪些流量的信息你同样需要显式配置出来,这同样会导致filter表的膨胀,最终导致内存占用以及遍历filter的转发效率的降低。
    于是乎,办法还要想别的,最直接的办法就是自己实现。简单点考虑,我也不要什么队列,既然tc都没有入口整形队列,那我也不要,超过限额的全部丢弃即可。最直接的方案就是修改netfilter的limit模块,因为它足够简单,扩展它时阻力最小,于是乎,改了它!修改动作很少,基本分为四点:
第一:维护一个list_head,保存所有的到达本机的ip数据报的源ip地址;
第二:修改match函数,在源ip链表中寻找该数据包的源ip,若找到,取出统计信息,看看一秒内流量是否超限,若是,则匹配,若没有则不匹配;如果在链表中没有找到,则创建一个entry,记录下当前时间和当前数据包长度,返回不匹配;将找到的entry取出,重新插入到head位置,或者将新创建的entry插入到head位置,这样可以模拟lru,为第四步创造好处;
第三:如果链表长度满了,则匹配所有的数据包;
第四:需要新增加entry且链表已经满了时,根据entry的上次更新时间以及最短不惑跃时间看是否能删除某一个entry。
上述四个步骤大体上分两个阶段实现,第一阶段暂时不实现第四点,这也符合我的一贯风格,第四点以及模块释放时的善后工作暂时没有测试,首先要把功能先跑通。现在假设已经实现了上述所有,我只需要配置以下的规则就可以实现针对每一个源ip进行限速了:
iptables –A –FORWARD/INPUT –m –limit 20/sec –j MY_CHAIN
注意,上述的20/sec已经不再是基于包数量的了,而是基于字节的,并且,我没有直接drop掉这些包,而是交给了一个自定义的chain来处理,这样可以方便的将机制和策略进行分离,或许管理员并不是想丢弃这些超限包,而只是纪录下日志,或许管理员会永远封死这些ip地址,也许仅仅封死一段时间,待收到罚金之后再给予开放…
2.实现
首先定义数据结构。以下的数据结构是一个包装,定义了一个全局的链表,以及一些控制参数,由于这个只是个测试版,因此没有考虑多处理器的并发处理,因此也就没有定义spin_lock,在正式的实现中,一定要定义一个lock的。
struct src_controler {
        struct list_head src_list;
        int curr;     //当前一共有多少了entry
        int max;    //最多能有多少个entry
};

下面的一个结构体定义了一个源地址entry中包含哪些东西,无非就是一秒内已经过去了多长的数据包以及时间戳等信息。
struct src_entry {
        struct list_head list;
        __u32   src_addr;    //源地址
        unsigned long prev;    //上次的时间戳
        unsigned long passed;    //一秒内已经过去了多少数据
};

struct src_controler *src_ctl;    //全局变量
接下来就是修改模块的初始化和卸载函数
static int __init xt_limit_init(void)
{
        int ret;
        src_ctl = kmalloc(sizeof(struct src_controler), GFP_KERNEL); //初始化全局变量
        memset(src_ctl, 0, sizeof(struct src_controler));
        INIT_LIST_HEAD(&src_ctl->src_list);    //初始化全局变量的链表
        src_ctl->curr = 0;
        src_ctl->max = 1000;    //本应该通过模块参数传进来的,这里写死,毕竟是个测试版
 
        ret = xt_register_match(&ipt_limit_reg);
        if (ret)
                return ret;
 
        ret = xt_register_match(&limit6_reg);
        if (ret)
                xt_unregister_match(&ipt_limit_reg);
 
        return ret;
}
static void __exit xt_limit_fini(void)
{
        xt_unregister_match(&ipt_limit_reg);
        xt_unregister_match(&limit6_reg);
        //这里应该有一个清理链表的操作,测试版没有实现
}

最后,编写match回调函数,删掉原来的,自己写新的逻辑
static int
ipt_limit_match(const struct sk_buff *skb,
                const struct net_device *in,
                const struct net_device *out,
                const struct xt_match *match,
                const void *matchinfo,
                int offset,
                unsigned int protoff,
                int *hotdrop)
{
        struct xt_rateinfo *r = ((struct xt_rateinfo *)matchinfo)->master;
        unsigned long now = jiffies, prev = 0;
        struct list_head *lh;
        struct src_entry *entry = NULL;
        struct src_entry *find_entry;
        unsigned long nowa;
        struct iphdr *iph = skb->nh.iph;
        __u32 this_addr = iph->saddr;
 
        list_for_each(lh, &src_ctl->src_list) { //遍历链表,找到这个ip地址对应的entry
                find_entry = list_entry(lh, struct src_entry, list);
                if (this_addr == find_entry->src_addr) {
                        entry = find_entry;
                        break;
                }
        }
        if (entry) { //如果找到,将其加在头,这样实现了一个简单的lru
                prev = entry->prev;
                list_del(&entry->list);
                list_add(&entry->list, &src_ctl->src_list);
        } else {    //如果没有找到,看看能否添加
                if (src_ctl->curr+1 < src_ctl->max) {
add_entry:
                        entry = kmalloc(sizeof(struct src_entry), GFP_KERNEL);
                        memset(entry, 0, sizeof(struct src_entry));
                        entry->src_addr = this_addr;
                        prev = entry->prev = now - 1000;
                        list_add(&entry->list, &src_ctl->src_list);
            src_ctl->curr++;    //正确做法是atomic_inc
                } else { //如果已经满了,那么看看能否删除最后的那个不活动的entry
                        entry = list_entry(src_ctl->src_list.prev, struct src_entry, list);
                        if (now-entry->prev > 1000)
                                goto add_entry;
                        return 1;
                }
        }
        nowa = entry->passed + skb->len;
        if (now-prev < 1000) {    //这里的1000其实应该是HZ变量的值,由于懒得引头文件了,直接写死了。如果距上次统计还没有到1秒,则累加数据,不匹配
                entry->passed = nowa;
                return 0;
        } else {
                entry->prev = now;    
                entry->passed = 0;
                if (r->burst >= nowa) {    //如果到达了1秒,则判断是否超限,如果超限,则匹配,没有超限则重置字段,不匹配
                        return 0;
                } else {
                        return 1;
                }
        }
        return -1;    //不会到达这里
}

编译之:
make -C /usr/src/kernels/2.6.18-92.el5-i686 SUBDIRS=`pwd` modules
使用之:
#!/bin/bash
iptables -A INPUT -d 192.168.1.247/32 -m limit --limit 1/sec --limit-burst $1 -j $2
运行上述脚本: test.sh 1000 DROP
然后下载大文件,看看是否被限速了!...
注意,上述的实现中,数据单位是字节,其实正常起码应该是100字节,做成可配置的会更好。
3.优化和反思
优化一:上述实现中,使用list_head在大量源ip的情况下,遍历链表的开销比较大,虽然lru原则可以最大的减小这种开销,但是还是很大,特别是用户并不想超限,反而间隔相对久的时间访问一次,大量这样的用户和大量频繁访问的用户混杂在一起,频繁访问的用户的entry会一直在前面,遍历时开销较小,而大量间隔相对久访问的用户的entry会在后面,遍历开销比较大,这会不会导致dos攻击,我由于没有环境还真的没有测试。事实上使用hash表来组织它们是更好的选择,linux的ip_conntrack中的纪录就是使用hash表来组织的,软件嘛,就这几种数据结构。
优化二:上述实现中,仅仅是针对源地址进行流量匹配,而没有管目的地址,因为开始说了,针对目的地址的流控可以用tc实现,然而那样的话,需要显式配置filter,很不方便,因此这个实现应该加个配置,用于针对任意目的地址进行流量控制,比tc方便多了。
优化三:上述实现中,数据单位是字节,这样很不合理,应该是可以配置的才对,比如默认是字节,还可以是k,m,g等等。

优化四:应该实现一个机制,定期清理不活跃的entry,以防止内存占用率过高。
反思:为何在入口位置的流控不实现队列呢?我们还是要想想流控的目的是什么,其一就是避免拥塞-网络的拥塞以及主机上层缓冲区的拥塞,对于接收数据而言,无论如何,流量对到达此地之前的网络的影响已经发生了,对往后的网络的影响还没有发生,因此对于已经发生的影响,没有必要再去进行速率适配了,直接执行动作即可。

     如果你真的还需要limit模块完成它本来的功能,那么就别改limit模块了,还是直接写一个为好,这样也更灵活,毕竟我们也就不需要再配置--limit 1/sec去迎合limit的语法了,具体方法参见《编写iptables模块实现不连续IP地址的DNAT-POOL》

修正:
如果同时下载多个局域网内的大文件,会发现上述的match回调函数工作的不是很好,速度并没有被限制住,这是因为我计时统计统计的粒度太粗,一秒统计一次,这一秒中,很多大包将溜过去,因此需要更细粒度的统计,那就是实时的统计,使用数据量/时间间隔这个除式来统计,代码如下:
static int
ipt_limit_match(const struct sk_buff *skb,
                const struct net_device *in,
                const struct net_device *out,
                const struct xt_match *match,
                const void *matchinfo,
                int offset,
                unsigned int protoff,
                int *hotdrop)
{
        struct xt_rateinfo *r = ((struct xt_rateinfo *)matchinfo)->master;
        unsigned long now = jiffies, prev = 0;
        struct list_head *lh;
        struct src_entry *entry = NULL;
        struct src_entry *find_entry;
        unsigned long nowa;
    unsigned long rate;
        struct iphdr *iph = skb->nh.iph;
        __u32 this_addr = iph->saddr;
 
        list_for_each(lh, &src_ctl->src_list) {
                find_entry = list_entry(lh, struct src_entry, list);
                if (this_addr == find_entry->src_addr) {
                        entry = find_entry;
                        break;
                }
        }
        if (entry) {
                prev = entry->prev;
                list_del(&entry->list);
                list_add(&entry->list, &src_ctl->src_list);
        } else {
                if (src_ctl->curr+1 < src_ctl->max) {
add_entry:
                        entry = kmalloc(sizeof(struct src_entry), GFP_KERNEL);
                        memset(entry, 0, sizeof(struct src_entry));
                        entry->src_addr = this_addr;
                        prev = entry->prev = now - 1000;
                        list_add(&entry->list, &src_ctl->src_list);
                        src_ctl->curr++;
                } else {
                        entry = list_entry(src_ctl->src_list.prev, struct src_entry, list);
                        if (now-entry->prev > 1000)
                                goto add_entry;
                        return 1;
                }
        }
        nowa = entry->passed + skb->len;
    entry->passed = nowa;
    if (now-prev > 0) {
        rate = entry->passed/(now-prev);
    } else
        rate = nowa;
    entry->prev = now;
    entry->passed = 0;
    if (rate > r->burst) {
        return 1;
    }
        return 0;
}

————————————————
版权声明:本文为CSDN博主「dog250」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dog250/article/details/6940578

在《 修改netfilter的limit模块实现基于单个ip的流量监控》的最后,写了一个修订版的match回调函数,但是修订的版本还只是能监控单包或者两包的流量,粒度还是过于粗糙,因此使用传统的令牌桶的方式更好。在数据结构上,没有必要真的实现一个令牌桶,而是基于时间的流逝生成受控制数量的令牌即可-以时间的流逝来洗涤旧迹,也就是将两次发包或者收包的间隔和令牌数量联系起来。在Linux内核的标准流控实现(qdisc)以及鲁迅的散文中,这么做很常见。
     以下是所有可编译运行的代码,整个流控代码分为两大部分。第一部分是内核模块,实现了netfilter的一个match;第二部分是用户态iptables扩展库,实现了一个match配置。
以下是内核模块,基于内核版本2.6.32-5-amd64,位于/usr/src/linux-source-2.6.32/net/netfilter/xt_limit.c:
/**
 *     2011/11/06 by marywangran
 *    这个版本实现只能使所有规则共享一个上限,作为全局管理局部特例
 *    很方便,不多的几条iptables规则即可
 */
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <net/ip.h>
 
#include <linux/netfilter/x_tables.h>
#include <linux/types.h>
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("marywangran <marywangran@126.com>");
MODULE_DESCRIPTION("Xtables: rate-limit match");
MODULE_ALIAS("ipt_limit");
MODULE_ALIAS("ip6t_limit");
 
struct xt_rateinfo {
        __u32 type;    //定义类型,1为源地址限速,2为目标地址限速
        __u32 burst;    //定义最大流量,统计值
};
 
struct src_controler {
        struct list_head src_list;
        int curr;             //当前的IP连接数
        int max;            //最大的IP连接数
        spinlock_t lock;
};
 
struct src_entry {
        struct list_head list;
        int type;        //同xt_rateinfo结构体的type
        __u32   addr;   
        unsigned long prev_hit; //上次包到来的时间
        unsigned long toks;    //当前拥有的令牌数
        spinlock_t lock;
};
 
struct src_controler *src_ctl;
 
static bool
limit_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{
        const struct xt_rateinfo *r = par->matchinfo;
        unsigned long now = jiffies;
 
        struct list_head *lh;
        struct src_entry *entry = NULL;
        struct src_entry *find_entry;
        long tokens;
        struct iphdr *iph = ip_hdr(skb);
        __u32 this_addr = 0;
 
        if (r->type == 1) {
                this_addr = iph->saddr;
        } else {
                this_addr = iph->daddr;
        }
 
        spin_lock (&src_ctl->lock);  //操作链表一定加锁,多CPU下防止并发修改,访问
        list_for_each(lh, &src_ctl->src_list) {
                find_entry = list_entry(lh, struct src_entry, list);
                if ((this_addr == find_entry->addr) &&
                    (find_entry->type == r->type)) {
                        entry = find_entry;
                        break;
                }
        }
        spin_unlock (&src_ctl->lock);
        if (entry) {
                spin_lock (&src_ctl->lock);
                list_del(&entry->list);
                list_add(&entry->list, &src_ctl->src_list);
                spin_unlock (&src_ctl->lock);
        } else {   
                if (src_ctl->curr+1 < src_ctl->max) {
add_entry:
                        entry = kmalloc(sizeof(struct src_entry), GFP_ATOMIC); //必须使用ATOMIC标志,因为有可能在(软)中断中运行,不能睡眠。
                        memset(entry, 0, sizeof(struct src_entry));
                        entry->addr = this_addr;
                        entry->toks = r->burst; //第一次分配令牌时不加倍(加倍理由为防止使用浮点运算),以防TCP的慢启动增加突发流量,TCP的慢启动实际上很快,指数级的。
                        entry->prev_hit = now;
                        entry->type = r->type;
                        spin_lock_init(&entry->lock);
                        spin_lock (&src_ctl->lock);
                        list_add(&entry->list, &src_ctl->src_list);
                        src_ctl->curr++;     //应该使用atomic_inc进行递增
                        spin_unlock (&src_ctl->lock);
                } else {
                        entry = list_entry(src_ctl->src_list.prev, struct src_entry, list);
                        if (now-entry->now > 1000) {
                                spin_lock (&src_ctl->lock);
                                list_del(&entry->list);
                                src_ctl->curr--;
                                spin_unlock (&src_ctl->lock);
                                vfree(entry);   //解锁后vfree
                                goto add_entry;
                        }
                        return 1;
                }
        }
    //以下根据流逝的时间来确定令牌的数量
        tokens = min_t(long, (now-entry->prev_hit)*r->burst, r->burst*1000);
        tokens += entry->toks;
        if (tokens > (long)r->burst*1000)
                        tokens = r->burst*1000;
                tokens -= skb->len*1000; //统一增加HZ倍,避免在内核使用浮点数和除法。
 
        if (tokens >= 0) {
                spin_lock (&entry->lock);
                entry->prev_hit = now;
                entry->toks = tokens;    //令牌积累
                spin_unlock (&entry->lock);
                return 0;
        }
        return 1;
}
 
static bool limit_mt_check(const struct xt_mtchk_param *par)
{
        struct xt_rateinfo *r = par->matchinfo;
        if (r->burst == 0 || r->type == 0) {
                return false;
        }
        if (r->type != 1 && r->type != 2)
                return false;
        return true;
}
 
static void limit_mt_destroy(const struct xt_mtdtor_param *par)
{
    //TODO
}
 
 
static struct xt_match limit_mt_reg __read_mostly = {
        .name             = "limit",
        .revision         = 0,
        .family           = NFPROTO_UNSPEC,
        .match            = limit_mt,
        .checkentry       = limit_mt_check,
        .destroy          = limit_mt_destroy,
        .matchsize        = sizeof(struct xt_rateinfo),
        .me               = THIS_MODULE,
};
 
static int __init limit_mt_init(void)
{
        src_ctl = kmalloc(sizeof(struct src_controler), GFP_KERNEL); //初始化全局变量,insmod上下文,可以使用KERNEL标志
        memset(src_ctl, 0, sizeof(struct src_controler));
        INIT_LIST_HEAD(&src_ctl->src_list);    //初始化全局变量的链表
        src_ctl->curr = 0;
        src_ctl->max = 2000;
        spin_lock_init(&src_ctl->lock);
        return xt_register_match(&limit_mt_reg);
}
 
static void __exit limit_mt_exit(void)
{
        struct src_entry *entry = NULL;
        struct list_head *lh = NULL, *lh2 = NULL;
        xt_unregister_match(&limit_mt_reg);
        spin_lock(&src_ctl->lock);
        list_for_each_safe(lh, lh2, &src_ctl->src_list) { //一定要用safe宏,因为这是个外部迭代器
                entry = list_entry(lh, struct src_entry, list);
                list_del(&entry->list);
                kfree(entry);
        }
        spin_unlock(&src_ctl->lock);
        kfree(src_ctl);
 
}
 
module_init(limit_mt_init);
module_exit(limit_mt_exit);

为了方便编译和安装,以下是Makefile:
LINUXPATH = /lib/modules/`uname -r`/build
CURDIR = $(shell pwd)
KBUILD_OUTPUT = $(CURDIR)
CROSS_COMPILE =
ARCH =
 
obj-m                           += xt_limit.o
 
all: limit
 
limit:
        $(MAKE) -C $(LINUXPATH) M=$(CURDIR) modules
        @echo "*********************************************"
        @echo "*  The MODULE is OK!!"
        @echo "*********************************************"
.PHONY: clean
clean:
        rm -rf *.o *.ko *.mod.c *.symvers *.mod.o .*.cmd  ../common/*.o .tmp_versions 

安装:
直接make后insmod或者将ko文件拷贝到/lib/modules/`uname -r`/kernel/net/netfilter/下面,然后modprobe xt_limit

****************************************************************************************************************************
以下是用户态iptables模块的代码,基于iptables版本1.4.12,位于iptables-1.4.12/extensions/libxt_limit.c:
/**
 *    2011/11/06 by marywangran
 *    修改自iptables-1.4.12/extensions/libxt_limit.c
 */
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <xtables.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_limit.h>
 
#define XT_LIMIT_BURST  500000
 
/**
 *    新增轻量级rateinfo结构,对应于内核的等价结构
 */
struct xt_rateinfo_new {
        __u32 type;
        __u32 burst;
};
 
enum {
        O_TYPE = 0,
        O_BURST,
};
 
static void limit_help(void)
{
        printf(
"limit match options:\n"
"--type source[1]|destination[2]        define source limit or destination limit\n"
"--limit-burst rate                     rate to match in a burst, default %u\n",
XT_LIMIT_BURST);
}
 
static const struct xt_option_entry limit_opts[] = {
        {.name = "type", .id = O_TYPE, .type = XTTYPE_STRING},
        {.name = "limit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
         .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_rateinfo_new, burst),
         .min = 0, .max = 1073741824}, //1024*1024*1024
        XTOPT_TABLEEND,
};
 
static
int parse_rate(const char *rate, uint32_t *val)
{
        uint32_t r;
        r = atoi(rate);
        if (!r)
                return 0;
        if (r != 1 && r != 2) //1为限制源地址,2为限制目的地址
                return 0;
        *val = r;
        return 1;
}
 
static void limit_init(struct xt_entry_match *m)
{
        struct xt_rateinfo_new *r = (struct xt_rateinfo_new *)m->data;
        parse_rate("1", &r->type);
        r->burst = XT_LIMIT_BURST;
}
 
static void limit_parse(struct xt_option_call *cb)
{
        struct xt_rateinfo_new *r = cb->data;
        xtables_option_parse(cb);
        switch (cb->entry->id) {
        case O_TYPE:
                if (!parse_rate(cb->arg, &r->type))
                        xtables_error(PARAMETER_PROBLEM,
                                   "bad rate \"%s\"'", cb->arg);
                break;
        }
        if (cb->invert)
                xtables_error(PARAMETER_PROBLEM,
                           "limit does not support invert");
}
 
static void print_rate(uint32_t period)
{
        printf(" %u", period);
}
 
static void
limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
{
        const struct xt_rateinfo_new *r = (const void *)match->data;
        printf(" type: avg"); print_rate(r->type);
        printf(" burst %u", r->burst);
}
 
static void limit_save(const void *ip, const struct xt_entry_match *match)
{
        const struct xt_rateinfo_new *r = (const void *)match->data;
 
        printf(" --type"); print_rate(r->type);
        if (r->burst != XT_LIMIT_BURST)
                printf(" --limit-burst %u", r->burst);
}
 
static struct xtables_match limit_match = {
        .family         = NFPROTO_UNSPEC,
        .name           = "limit",
        .version        = XTABLES_VERSION,
        .size           = XT_ALIGN(sizeof(struct xt_rateinfo_new)),
        .help           = limit_help,
        .init           = limit_init,
        .x6_parse       = limit_parse,
        .print          = limit_print,
        .save           = limit_save,
        .x6_options     = limit_opts,
};
 
void _init(void)
{
        xtables_register_match(&limit_match);
}

安装:

进入$IPTABLES_SOURCE/extensions目录,执行make install,    
测试:
此时添加下列的规则:
iptables -A INPUT -m limit --type 1 --limit-burst 500000 -j DROP
iptables -A OUTPUT -m limit --type 2 --limit-burst 100000 -j DROP
即可对本机发出的和接收的基于IP地址的数据流进行限速,如果在本机往同一台机器scp多个文件,那么这多个数据传输流将共享配置的限速额带宽。如果想限制某一个而不是完全限制,则在规则中添加其它的match进行筛选。
说明:以上的代码都是基于现有的Linux内核以及iptables源码修改的,框架虽在,然逻辑已全非,如果需保留原来的limit功能,请自行实现新的文件。
————————————————
版权声明:本文为CSDN博主「dog250」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dog250/article/details/6945502

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值