ixp425上QoS开发

    技术2022-05-11  24

    1、  流量控制的实现要求:路由器执行流量控制后,对每一个非特权IP都加以流量控制策略。实现对每一个IP地址的流量限制。

    2、  实现思路:基于HTB方法。 通过c程序来执行的shell程序,来配置策略,创建类和分类器。然后在Netfilter中注册钩子函数,对数据包打标记,以便以分类器根据该标记来对数据包进行匹配。

    3、  具体实现,分为两步:

    (1)用C语言来执行HTB的shell命令(写入文件,最后执行该文件),文件中部分信息如下:

    #!/bin/sh

    tc qdisc del dev eth1 root 2>/dev/null   #删除以前的eth1的qdisc

    tc qdisc add dev eth1 root handle 1: htb   #为eth1创建qdisc的根

    tc class add dev eth1 parent 1:0 classid 1:1 htb rate 1024kbit ceil 1024kbit

    #为创建子类1:1, 并限制该类的速率不超过1024

    tc filter add dev eth1 protocol ip parent 1:0 prio 1 handle 1 fw flowid 1:1

    #为该类创建分类器,标记为“1”的数据包会被送往该类(handle 1:匹配标记为1的数据包)

    tc qdisc add dev eth1 parent 1:1  handle 1 tbf rate 1024kbit burst 20k latency 10ms

    在该类下创建tbf(令牌桶过滤器)队列队则

     

    tc class add dev eth1 parent 1:0 classid 1:2 htb rate 1024kbit ceil 1024kbit

    tc filter add dev eth1 protocol ip parent 1:0 prio 1 handle 2 fw flowid 1:2

    tc qdisc add dev eth1 parent 1:2  handle 2 tbf rate 1024kbit burst 20k latency 10ms

    tc class add dev eth1 parent 1:0 classid 1:89 htb rate 1024kbit ceil 1024kbit

    tc filter add dev eth1 protocol ip parent 1:0 prio 1 handle 89 fw flowid 1:89

    tc qdisc add dev eth1 parent 1:89  handle 89 tbf rate 1024kbit burst 20k latency 10ms

    ……

     

    (2)在内核Netfilter中利用对数据包打标记,标记就对应以该数据包源或目的地址的C网段部分的数字,也就是分类器filter中handle对应的标记。这样,内核会根据打标记来匹配数据包,并根据标记来判断应有哪个分类器filter来处理。

    以控制上行速率为例,在/linux/net/ipv4/netfilter/rate.c中编写针对上行数据包的HOOK(钩子)函数upload_hook,实现片段为:

    unsigned int upload_rate_hook(unsigned int hooknum,

                  struct sk_buff **pskb,

                  const struct net_device *in,

                  const struct net_device *out,

                  int (*okfn)(struct sk_buff *))

    {

           struct sk_buff *skb = *pskb;

           struct iphdr *iph;

           unsigned int n;

           iph = skb->nh.iph;

                  if (skb->dev && skb->dev->name && strcmp(skb->dev->name, LAN_IFNAME) == 0)                //判断数据是否来自LAN口eth0

           {

                  if (ip_counters_enable == 1)

                  {

                         unsigned int subnet = iph->saddr & lan_ip_netmask;

                                if (subnet == lan_ip_subnet)  //判断是都否和LAN口在一个网段

                         {

                         struct tcphdr *tcph = (void *)iph + iph->ihl*4;

                         if (iph->daddr == lan_ip_addr && ntohs(tcph->dest) == 80)

                                 return NF_ACCEPT;

                         add_uprate(iph->saddr, ntohs(iph->tot_len)); //用于流量统计(与这里的QoS实现没关系)

                         n = iph->saddr & (~lan_ip_netmask);  //得到IP地址的C网段部分

                         skb->nfmark = n;             //给该IP包打上标记

                         skb->nfcache |= NFC_ALTERED;   //告知内核(打了标记这个)变化,

                         }

                  }    

           }

           return NF_ACCEPT;

    }           

    其中n = iph->saddr & (~lan_ip_netmask)的作用是提取IP地址和子网掩码做相与,得到IP地址的C网段部分的数字,再利用skb->nfmark = n;给来自该IP地址的数据包打上标记“n”, 这个“n”就与分类器filter中handle对应的标记项对应,filter会将匹配的数据包送到相应的tc的class中,从而实现了对IP数据包的流量控制。skb_buff中的nfcache原来打算是用来做netfilter的cache机制的。就是说,如果一个数据包被netfilter中的某个module动过了的话,动这个数据包的那个模块就应该给这个skb的nfcache字段打个标记。

     

    设计好HOOK函数后,还需要向内核Netfilter注册该HOOK函数,注册的实现方式如下:       在static int __init  sfitc_init(void)函数中,添加:

    static struct nf_hook_ops upload_hook_ops;  //定义一个nf_hook_ops数据结构

    upload_hook_ops.hook = upload_rate_hook;   //指定对应的HOOK函数

    upload_hook_ops.hooknum = NF_IP_PRE_ROUTING; //挂在PREROUTING链

    macfilter_ops.pf     = PF_INET;               //网络协议指定

    macfilter_ops.priority = (NF_IP_PRI_FIRST + 3);   //设置优先级

    nf_register_hook(&upload_hook_ops);  //向内核注册nf_hook_ops数据结构

    (4)在net/ipv4/netfilter下的Makefile中,加上obj-y += rate.o,编译内核的时候会将该模块编译进内核。


    最新回复(0)