`

Linux内核中流量控制(6)

阅读更多
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

5.6 DSMARK(Differentiated Services field marker)
 
DSMARK算法实际不能算特别流控算法, 本身只是一个pfifo流控, 所做的多余功能就是能设置数据包的tos/ds域, 其实意义不大, 该功能在网络层就能作(netfilter/TOS目标)。使用时除了用tc qdisc命令设置外, 还要用tc class/filter命令对数据进行分类, 以设置合适的tos/ds值。

5.6.1 DSMARK操作结构定义

#define NO_DEFAULT_INDEX (1 << 16)
// DSMARK私有数据
struct dsmark_qdisc_data {
// 内部流控节点
 struct Qdisc  *q;
// 分类规则表, 通过tc class命令定义
 struct tcf_proto *filter_list;
// value/mask数组, 用于设置数据包IP头中的tos/ds域
 u8   *mask; /* "owns" the array */
 u8   *value;
// 索引
 u16   indices;
// 缺省索引
 u32   default_index; /* index range is 0...0xffff */
// 是否设置TC索引值标志
 int   set_tc_index;
};
 
// DSMARK流控操作结构
static struct Qdisc_ops dsmark_qdisc_ops = {
 .next  = NULL,
 .cl_ops  = &dsmark_class_ops,
 .id  = "dsmark",
 .priv_size = sizeof(struct dsmark_qdisc_data),
 .enqueue = dsmark_enqueue,
 .dequeue = dsmark_dequeue,
 .requeue = dsmark_requeue,
 .drop  = dsmark_drop,
 .init  = dsmark_init,
 .reset  = dsmark_reset,
 .destroy = dsmark_destroy,
// 没有change函数
 .change  = NULL,
 .dump  = dsmark_dump,
 .owner  = THIS_MODULE,
};
// DSMARK类别操作结构
static struct Qdisc_class_ops dsmark_class_ops = {
 .graft  = dsmark_graft,
 .leaf  = dsmark_leaf,
 .get  = dsmark_get,
 .put  = dsmark_put,
 .change  = dsmark_change,
 .delete  = dsmark_delete,
 .walk  = dsmark_walk,
 .tcf_chain = dsmark_find_tcf,
 .bind_tcf = dsmark_bind_filter,
 .unbind_tcf = dsmark_put,
 .dump  = dsmark_dump_class,
};

5.6.2 初始化

static int dsmark_init(struct Qdisc *sch, struct rtattr *opt)
{
// DSMARK私有数据
 struct dsmark_qdisc_data *p = PRIV(sch);
// 保存相关属性
 struct rtattr *tb[TCA_DSMARK_MAX];
 int err = -EINVAL;
 u32 default_index = NO_DEFAULT_INDEX;
 u16 indices;
 u8 *mask;
 DPRINTK("dsmark_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt);
// 选项参数解析, 至少要提供一个indices参数
 if (!opt || rtattr_parse_nested(tb, TCA_DSMARK_MAX, opt) < 0)
  goto errout;
// 获取indices值并检查是否合法, indices必须是2的整次幂
 indices = RTA_GET_U16(tb[TCA_DSMARK_INDICES-1]);
 if (!indices || !dsmark_valid_indices(indices))
  goto errout;
// 如果有缺省索引参数, 设置之
 if (tb[TCA_DSMARK_DEFAULT_INDEX-1])
  default_index = RTA_GET_U16(tb[TCA_DSMARK_DEFAULT_INDEX-1]);
// 分配value和mask空间, 都是indices大小
 mask = kmalloc(indices * 2, GFP_KERNEL);
 if (mask == NULL) {
  err = -ENOMEM;
  goto errout;
 }
// 前一半空间都初始化0xff作为mask
 p->mask = mask;
 memset(p->mask, 0xff, indices);
// 后一半空间都初始化0作为value
 p->value = p->mask + indices;
 memset(p->value, 0, indices);
// 设置DSMARK参数
 p->indices = indices;
 p->default_index = default_index;
 p->set_tc_index = RTA_GET_FLAG(tb[TCA_DSMARK_SET_TC_INDEX-1]);
// 内部流控节点是一个pfifo类型的qdisc
 p->q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
 if (p->q == NULL)
  p->q = &noop_qdisc;
 DPRINTK("dsmark_init: qdisc %p\n", p->q);
 err = 0;
errout:
rtattr_failure:
 return err;
}
 

5.6.3 入队
 
// 入队操作主要是生成数据包的tc_index值
static int dsmark_enqueue(struct sk_buff *skb,struct Qdisc *sch)
{
 struct dsmark_qdisc_data *p = PRIV(sch);
 int err;
 D2PRINTK("dsmark_enqueue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p);
// 如果有set_tc_index标志
 if (p->set_tc_index) {
  /* FIXME: Safe with non-linear skbs? --RR */
// 提取IPv4头的TOS字段或IPv4的ds字段作为数据包的tc_index
  switch (skb->protocol) {
   case __constant_htons(ETH_P_IP):
    skb->tc_index = ipv4_get_dsfield(skb->nh.iph)
     & ~INET_ECN_MASK;
    break;
   case __constant_htons(ETH_P_IPV6):
    skb->tc_index = ipv6_get_dsfield(skb->nh.ipv6h)
     & ~INET_ECN_MASK;
    break;
   default:
    skb->tc_index = 0;
    break;
  };
 }
// 如果该数据包是由此qdisc处理
 if (TC_H_MAJ(skb->priority) == sch->handle)
// 数据包的tc_index设置为skb->priority的低16位
  skb->tc_index = TC_H_MIN(skb->priority);
 else {
// 否则调用分类规则对数据包进行分类
  struct tcf_result res;
  int result = tc_classify(skb, p->filter_list, &res);
  D2PRINTK("result %d class 0x%04x\n", result, res.classid);
// 分类结果
  switch (result) {
#ifdef CONFIG_NET_CLS_POLICE
// 丢包
   case TC_POLICE_SHOT:
    kfree_skb(skb);
    sch->qstats.drops++;
    return NET_XMIT_POLICED;
#if 0
   case TC_POLICE_RECLASSIFY:
    /* FIXME: what to do here ??? */
#endif
#endif
// 获取了新的分类值
   case TC_POLICE_OK:
    skb->tc_index = TC_H_MIN(res.classid);
    break;
// 使用缺省分类值, 如果没有设置set_tc_index标志, 也没有定义缺省索引值
// 则tc_index值不改变
   case TC_POLICE_UNSPEC:
    /* fall through */
   default:
    if (p->default_index != NO_DEFAULT_INDEX)
     skb->tc_index = p->default_index;
    break;
  };
 }
// 进行pfifo的入队操作
 err = p->q->enqueue(skb,p->q);
 if (err != NET_XMIT_SUCCESS) {
  sch->qstats.drops++;
  return err;
 }
 sch->bstats.bytes += skb->len;
 sch->bstats.packets++;
 sch->q.qlen++;
 return NET_XMIT_SUCCESS;
}

5.6.4 重入队

static int dsmark_requeue(struct sk_buff *skb,struct Qdisc *sch)
{
 struct dsmark_qdisc_data *p = PRIV(sch);
 int err;
 D2PRINTK("dsmark_requeue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p);
// 就是调用pfifo的重入队操作
 err = p->q->ops->requeue(skb, p->q);
 if (err != NET_XMIT_SUCCESS) {
  sch->qstats.drops++;
  return err;
 }
 sch->q.qlen++;
 sch->qstats.requeues++;
 return NET_XMIT_SUCCESS;
}
 
5.6.5 出队

static struct sk_buff *dsmark_dequeue(struct Qdisc *sch)
{
// DSMARK私有数据
 struct dsmark_qdisc_data *p = PRIV(sch);
 struct sk_buff *skb;
 u32 index;
 D2PRINTK("dsmark_dequeue(sch %p,[qdisc %p])\n", sch, p);
// 调用pfifo的出队操作
 skb = p->q->ops->dequeue(p->q);
 if (skb == NULL)
  return NULL;
// 队列长度减
 sch->q.qlen--;
// 获取数据包索引值, 不超过(indices-1)
 index = skb->tc_index & (p->indices - 1);
 D2PRINTK("index %d->%d\n", skb->tc_index, index);
// 以下修改TPv4数据包的TOS值或IPv6的ds值
// 具体修改的值是由索引值对应的value/mask数组中的值确定的
 switch (skb->protocol) {
  case __constant_htons(ETH_P_IP):
   ipv4_change_dsfield(skb->nh.iph, p->mask[index],
         p->value[index]);
   break;
  case __constant_htons(ETH_P_IPV6):
   ipv6_change_dsfield(skb->nh.ipv6h, p->mask[index],
         p->value[index]);
   break;
  default:
   /*
    * Only complain if a change was actually attempted.
    * This way, we can send non-IP traffic through dsmark
    * and don't need yet another qdisc as a bypass.
    */
   if (p->mask[index] != 0xff || p->value[index])
    printk(KERN_WARNING "dsmark_dequeue: "
           "unsupported protocol %d\n",
           htons(skb->protocol));
   break;
 };
// 数据包返回
 return skb;
}

5.6.6 复位

static void dsmark_reset(struct Qdisc *sch)
{
 struct dsmark_qdisc_data *p = PRIV(sch);
 DPRINTK("dsmark_reset(sch %p,[qdisc %p])\n", sch, p);
// 就是调用pfifo的复位函数
 qdisc_reset(p->q);
 sch->q.qlen = 0;
}
 
5.6.7 释放

static void dsmark_destroy(struct Qdisc *sch)
{
 struct dsmark_qdisc_data *p = PRIV(sch);
 struct tcf_proto *tp;
 DPRINTK("dsmark_destroy(sch %p,[qdisc %p])\n", sch, p);
// 释放分类规则
 while (p->filter_list) {
  tp = p->filter_list;
  p->filter_list = tp->next;
  tcf_destroy(tp);
 }
// 释放pfifo
 qdisc_destroy(p->q);
// 释放mask和value, 因为mask就是动态空间头, 所以mask和value一起释放了
 kfree(p->mask);
}

5.6.8 丢包

static unsigned int dsmark_drop(struct Qdisc *sch)
{
 struct dsmark_qdisc_data *p = PRIV(sch);
 unsigned int len;
 
 DPRINTK("dsmark_reset(sch %p,[qdisc %p])\n", sch, p);
 if (p->q->ops->drop == NULL)
  return 0;
// 就是调用pfifo的drop函数
 len = p->q->ops->drop(p->q);
 if (len)
  sch->q.qlen--;
 return len;
}
 
5.6.9 输出参数
 
static int dsmark_dump(struct Qdisc *sch, struct sk_buff *skb)
{
// DSMARK私有数据
 struct dsmark_qdisc_data *p = PRIV(sch);
 struct rtattr *opts = NULL;
// 输出indices
 opts = RTA_NEST(skb, TCA_OPTIONS);
 RTA_PUT_U16(skb, TCA_DSMARK_INDICES, p->indices);
// 输出缺省索引值
 if (p->default_index != NO_DEFAULT_INDEX)
  RTA_PUT_U16(skb, TCA_DSMARK_DEFAULT_INDEX, p->default_index);
// 输出set_tc_index标志
 if (p->set_tc_index)
  RTA_PUT_FLAG(skb, TCA_DSMARK_SET_TC_INDEX);
 return RTA_NEST_END(skb, opts);
rtattr_failure:
 return RTA_NEST_CANCEL(skb, opts);
}

5.6.10 DSMARK类别操作

static int dsmark_graft(struct Qdisc *sch, unsigned long arg,
   struct Qdisc *new, struct Qdisc **old)
{
 struct dsmark_qdisc_data *p = PRIV(sch);
 DPRINTK("dsmark_graft(sch %p,[qdisc %p],new %p,old %p)\n",
  sch, p, new, old);
 if (new == NULL) {
  new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops);
  if (new == NULL)
   new = &noop_qdisc;
 }
 sch_tree_lock(sch);
 *old = xchg(&p->q, new);
 qdisc_reset(*old);
 sch->q.qlen = 0;
 sch_tree_unlock(sch);
        return 0;
}
// 返回叶子节点的qdisc
static struct Qdisc *dsmark_leaf(struct Qdisc *sch, unsigned long arg)
{
 return PRIV(sch)->q;
}

// 获取某类别ID
static unsigned long dsmark_get(struct Qdisc *sch, u32 classid)
{
 DPRINTK("dsmark_get(sch %p,[qdisc %p],classid %x)\n",
  sch, PRIV(sch), classid);
// 就是classid的低16位加1
 return TC_H_MIN(classid) + 1;
}

// 绑定过滤器
static unsigned long dsmark_bind_filter(struct Qdisc *sch,
     unsigned long parent, u32 classid)
{
 return dsmark_get(sch, classid);
}

static void dsmark_put(struct Qdisc *sch, unsigned long cl)
{
}
// 修改dsmark参数: value/mask
static int dsmark_change(struct Qdisc *sch, u32 classid, u32 parent,
    struct rtattr **tca, unsigned long *arg)
{
// DSMARK私有数据
 struct dsmark_qdisc_data *p = PRIV(sch);
// 输入参数
 struct rtattr *opt = tca[TCA_OPTIONS-1];
// 输出参数数组
 struct rtattr *tb[TCA_DSMARK_MAX];
 int err = -EINVAL;
 u8 mask = 0;
 DPRINTK("dsmark_change(sch %p,[qdisc %p],classid %x,parent %x),"
  "arg 0x%lx\n", sch, p, classid, parent, *arg);
// 检查索引值是否合法
 if (!dsmark_valid_index(p, *arg)) {
  err = -ENOENT;
  goto rtattr_failure;
 }
// 解析opt输出到tb
 if (!opt || rtattr_parse_nested(tb, TCA_DSMARK_MAX, opt))
  goto rtattr_failure;
// 解析mask
 if (tb[TCA_DSMARK_MASK-1])
  mask = RTA_GET_U8(tb[TCA_DSMARK_MASK-1]);
// 解析value并保存到指定索引的数组项
 if (tb[TCA_DSMARK_VALUE-1])
  p->value[*arg-1] = RTA_GET_U8(tb[TCA_DSMARK_VALUE-1]);
// 保存mask到指定索引的数组项, mask为什么要解析和设置分开呢?
 if (tb[TCA_DSMARK_MASK-1])
  p->mask[*arg-1] = mask;
 err = 0;
rtattr_failure:
 return err;
}

// 清除索引值对应数组项
static int dsmark_delete(struct Qdisc *sch, unsigned long arg)
{
 struct dsmark_qdisc_data *p = PRIV(sch);
// 检查索引值是否合法
 if (!dsmark_valid_index(p, arg))
  return -EINVAL;
// 将value和mask恢复缺省值, 0/0xff 
 p->mask[arg-1] = 0xff;
 p->value[arg-1] = 0;
 return 0;
}

// 遍历
static void dsmark_walk(struct Qdisc *sch,struct qdisc_walker *walker)
{
 struct dsmark_qdisc_data *p = PRIV(sch);
 int i;
 DPRINTK("dsmark_walk(sch %p,[qdisc %p],walker %p)\n", sch, p, walker);
 if (walker->stop)
  return;
// 遍历数组
 for (i = 0; i < p->indices; i++) {
// mask=0xff和value=0的索引项没用
  if (p->mask[i] == 0xff && !p->value[i])
   goto ignore;
  if (walker->count >= walker->skip) {
// 调用相关处理函数进行处理
   if (walker->fn(sch, i+1, walker) < 0) {
    walker->stop = 1;
    break;
   }
  }
ignore:  
  walker->count++;
        }
}

// 返回过滤规则
static struct tcf_proto **dsmark_find_tcf(struct Qdisc *sch,unsigned long cl)
{
 return &PRIV(sch)->filter_list;
}

// 类别输出, 输出某索引值对应的value和mask
static int dsmark_dump_class(struct Qdisc *sch, unsigned long cl,
        struct sk_buff *skb, struct tcmsg *tcm)
{
// 私有数据
 struct dsmark_qdisc_data *p = PRIV(sch);
 struct rtattr *opts = NULL;
 DPRINTK("dsmark_dump_class(sch %p,[qdisc %p],class %ld\n", sch, p, cl);
// 检查索引值是否合法, 必须大于0, 不超过indices
 if (!dsmark_valid_index(p, cl))
  return -EINVAL;
// qdisc基本参数
 tcm->tcm_handle = TC_H_MAKE(TC_H_MAJ(sch->handle), cl-1);
 tcm->tcm_info = p->q->handle;
// 该索引位置对应的mask和value
 opts = RTA_NEST(skb, TCA_OPTIONS);
 RTA_PUT_U8(skb,TCA_DSMARK_MASK, p->mask[cl-1]);
 RTA_PUT_U8(skb,TCA_DSMARK_VALUE, p->value[cl-1]);
 return RTA_NEST_END(skb, opts);
rtattr_failure:
 return RTA_NEST_CANCEL(skb, opts);
}
 
...... 待续 ......
分享到:
评论

相关推荐

    基于Linux内核扩展模块的P2P流量控制

    基于Linux内核扩展模块的P2P流量控制

    基于Linux内核的BT流量控制的原理与实现.pdf

    基于Linux内核的BT流量控制的原理与实现.pdf

    基于Linux内核扩展模块的P2P流量控制.pdf

    基于Linux内核扩展模块的P2P流量控制.pdf

    Linux内核扩展模块的P2P流量控制方法与研究.pdf

    Linux内核扩展模块的P2P流量控制方法与研究.pdf

    Linux高级路由和流量控制

    15.8. 终极的流量控制:低延迟,高速上/下载 98 15.8.1. 为什么缺省设置不让人满意 99 15.8.2. 实际的脚本(CBQ) 100 15.8.3. 实际的脚本(HTB) 102 15.9. 为单个主机或子网限速 103 15.10. 一个完全NAT和QOS的范例...

    基于Linux LQL流量控制系统的研究与实现

    该方法摒弃了传统方法中所运用的TC命令解析,netlink传输,内核空间执行的3层结构,而直接在Linux内核的框架下,采用LQL库直接对内核进行操控,并改进了相关U32过滤器以对IP段的流量控制,从而实现对系统的智能流量控制。...

    编写Linuxc操作系统设备驱动程序概述

    在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。Linux的网络系统主要是基于BSD unix的socket机制 。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据...

    xt_fset:扩展到Linux内核netfilter子系统(iptables)(插件),使您可以通过发送ICMP包远程操作linux内核ipset(向ipset中添加或删除一些ip地址)。

    xt_fset是linux内核netfilter子系统的内核模块和iptables扩展(插件),允许您通过发送控制ICMP数据包来远程操作linux内核ipset(在ipset中添加或删除一些ip地址)。 该插件的创建是对Linux内核netfilter子系统的...

    Linux Kernel v4.19.1 Stable.zip

    Linux Kernel主要新特性包括:合并了来自Android项目的内核代码,支持新的架构TI C6X,改进了Btrfs...网络优先控制组允许管理员动态设置网络流量的优先次序;支持EFI引导固件;改进内存管理,等等。 Linux Kernel截图

    Linux的高级路由和流量控制HOWTO-中文版

    虽然这些工具能够工作,但它们在 Linux2.2 和更高版本的内核上显 得有一些落伍。比如,现在 GRE 隧道已经成为了路由的一个主要概念,但却不 能通过上述工具来配置。 使用了 iproute2,隧道的配置与其他部分完全集成了。

    lksctp-rs:Rust 的 Linux 内核 SCTP 低级绑定

    流量控制和拥塞控制 此外,它还补充说: 多路复用流:通过单个连接,您可以多路复用多个信息流。 端点的多宿主:一个端点可以有多个 IP 地址,允许网络故障转移/可靠性(注意:它需要是一台机器,或者复制端点的...

    《精通Linux 设备驱动程序开发》.(Sreekrishnan).pdf

    15.2.3 流量控制315 15.3 缓冲区管理和并发控制315 15.4 设备实例:以太网nic316 15.5 isa网络驱动程序321 15.6 atm321 15.7 网络吞吐量322 15.7.1 驱动程序性能322 15.7.2 协议性能323 15.8 查看...

    精通LINUX设备驱动程序开发

    311 15.1.6 统计 312 15.1.7 配置 313 15.1.8 总线相关内容 314 15.2 与协议层会话 314 15.2.1 接收路径 314 15.2.2 发送路径 315 15.2.3 流量控制 315 15.3 缓冲区管理和并发控制 315 15.4 设备实例:...

    基于Linux 的防火墙技术研究

    络地址转换、流量控制及高级的包处理等。Netfilter/Iptables 系统采用模块化的架构方式,其主要模块 有:通用框架Netfilter、数据包选择系统、连接跟踪系统及NAT系统等等。 2.1 Netfilter/Iptables 系统工作原理 ...

    Linux模拟网络丢包与延迟的方法

    netem 与 tc: netem 是 Linux 2.6 及以上...tc 是 Linux 系统中的一个工具,全名为traffic control(流量控制)。tc 可以用来控制 netem 的工作模式,也就是说,如果想使用 netem ,需要至少两个条件,一个是内核中的

    DarkShell_Linux-Win集群版V2014年

    Linux支持路由内核、2.6、3.1等普通内核,路由内核支持路由三大内核、Ubuntu、admin等,独立开发的Linux穿盾CC模式,SYN稳定发包100%,自启动,无需Root权限上线即可发包。 VIP版本攻击代码实时更新,通过服务器...

    ip route2 源码 第二代网络工具

    如果你仍在使用net-tools,而且尤其需要跟上新版Linux内核中的最新最重要的网络特性的话,那么是时候转到iproute2的阵营了。原因就在于使用iproute2可以做很多net-tools无法做到的事情。对于那些想要转到使用iproute...

    Linux C 一站式学习

    7.3. 流量控制 37. socket编程 1. 预备知识 1.1. 网络字节序 1.2. socket地址的数据类型及相关函数 2. 基于TCP协议的网络程序 2.1. 最简单的TCP网络程序 2.2. 错误处理与读写控制 2.3. 把client改为交互式输入 2.4. ...

Global site tag (gtag.js) - Google Analytics