`

Linux内核中流量控制(9)

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

5.9 ingress

ingress流控方法是针对输入数据进行流控处理的,在net/sched/sch_ingress.c中定义, 使用时要求在内核配置中定义CONFIG_NET_CLS_ACT或CONFIG_NETFILTER,不过从代码实现看起来好象没什么意义。

5.9.1 ingress操作结构定义

// ingress私有数据结构
struct ingress_qdisc_data {
// 内部流控
 struct Qdisc  *q;
// 过滤规则
 struct tcf_proto *filter_list;
};

// ingress流控操作结构
static struct Qdisc_ops ingress_qdisc_ops = {
 .next  = NULL,
 .cl_ops  = &ingress_class_ops,
 .id  = "ingress",
 .priv_size = sizeof(struct ingress_qdisc_data),
 .enqueue = ingress_enqueue,
 .dequeue = ingress_dequeue,
 .requeue = ingress_requeue,
 .drop  = ingress_drop,
 .init  = ingress_init,
 .reset  = ingress_reset,
 .destroy = ingress_destroy,
// 无change函数
 .change  = NULL,
 .dump  = ingress_dump,
 .owner  = THIS_MODULE,
};
 
// ingress类别操作结构
static struct Qdisc_class_ops ingress_class_ops = {
 .graft  = ingress_graft,
 .leaf  = ingress_leaf,
 .get  = ingress_get,
 .put  = ingress_put,
 .change  = ingress_change,
 .delete  = NULL,
 .walk  = ingress_walk,
 .tcf_chain = ingress_find_tcf,
 .bind_tcf = ingress_bind_filter,
 .unbind_tcf = ingress_put,
 .dump  = NULL,
};

5.9.1 ingress的netfilter节点

// 没定义NET_CLS_ACT, 而定义了NETFILTER的情况下才有效, 在netfilter节点中进行
// 数据包是否接收的判断
// 但如果定义了NET_CLS_ACT后, 该段代码无效
#ifndef CONFIG_NET_CLS_ACT
#ifdef CONFIG_NETFILTER
/* after ipt_filter */
static struct nf_hook_ops ing_ops = {
// 钩子函数
 .hook           = ing_hook,
 .owner  = THIS_MODULE,
 .pf             = PF_INET,
// 挂接在PREROUTING点
 .hooknum        = NF_IP_PRE_ROUTING,
// 优先权NF_IP_PRI_FILTER + 1, 不过在PREROUTING点缺省好象没定义NF_IP_PRI_FILTER
// 级别的节点
 .priority       = NF_IP_PRI_FILTER + 1,
};
static struct nf_hook_ops ing6_ops = {
 .hook           = ing_hook,
 .owner  = THIS_MODULE,
 .pf             = PF_INET6,
 .hooknum        = NF_IP6_PRE_ROUTING,
 .priority       = NF_IP6_PRI_FILTER + 1,
};

static unsigned int
ing_hook(unsigned int hook, struct sk_buff **pskb,
                             const struct net_device *indev,
                             const struct net_device *outdev,
                      int (*okfn)(struct sk_buff *))
{
 
 struct Qdisc *q;
 struct sk_buff *skb = *pskb;
        struct net_device *dev = skb->dev;
// 缺省动作, 接受
 int fwres=NF_ACCEPT;
 DPRINTK("ing_hook: skb %s dev=%s len=%u\n",
  skb->sk ? "(owned)" : "(unowned)",
  skb->dev ? (*pskb)->dev->name : "(no dev)",
  skb->len);
/*
revisit later: Use a private since lock dev->queue_lock is also
used on the egress (might slow things for an iota)
*/
// 如果设备里定义了接收流控
 if (dev->qdisc_ingress) {
  spin_lock(&dev->queue_lock);
  if ((q = dev->qdisc_ingress) != NULL)
// 调用接收流控的入队函数, 返回值是对数据包的判断结果
   fwres = q->enqueue(skb, q);
  spin_unlock(&dev->queue_lock);
        }
// 如果没定义, 就直接返回缺省动作: 通过 
 return fwres;
}
#endif
#endif

5.9.3 初始化

static int ingress_init(struct Qdisc *sch,struct rtattr *opt)
{
 struct ingress_qdisc_data *p = PRIV(sch);
/* Make sure either netfilter or preferably CLS_ACT is
* compiled in */
// 输入接收流控必须在内核选项中定义分类动作NET_CLS_ACT或NETFILTER
#ifndef CONFIG_NET_CLS_ACT
#ifndef CONFIG_NETFILTER
// 两者都没定义的话就初始化失败
 printk("You MUST compile classifier actions into the kernel\n");
 return -EINVAL;
#else
// 使用NET_CLS_ACT优于使用NETFILTER
 printk("Ingress scheduler: Classifier actions prefered over netfilter\n");
#endif
#endif
                                                                               
#ifndef CONFIG_NET_CLS_ACT
#ifdef CONFIG_NETFILTER
// 没定义NET_CLS_ACT, 而定义了NETFILTER的情况
// 登记输入流控的netfilter钩子节点函数
 if (!nf_registered) {
// 登记IPV4钩子节点
  if (nf_register_hook(&ing_ops) < 0) {
   printk("ingress qdisc registration error \n");
   return -EINVAL;
  }
// 非0表示已经登记了
  nf_registered++;

// 登记IPV4钩子节点
  if (nf_register_hook(&ing6_ops) < 0) {
   printk("IPv6 ingress qdisc registration error, " \
       "disabling IPv6 support.\n");
  } else
   nf_registered++;
 }
#endif
#endif
 DPRINTK("ingress_init(sch %p,[qdisc %p],opt %p)\n",sch,p,opt);
// 初始内部流控初始化为noop, 丢包的
 p->q = &noop_qdisc;
 return 0;
}
 
5.9.5 入队

sstatic int ingress_enqueue(struct sk_buff *skb,struct Qdisc *sch)
{
// 接收流控私有数据
 struct ingress_qdisc_data *p = PRIV(sch);
// 分类结果
 struct tcf_result res;
 int result;
 D2PRINTK("ingress_enqueue(skb %p,sch %p,[qdisc %p])\n", skb, sch, p);
// 对数据进行分类
 result = tc_classify(skb, p->filter_list, &res);
 D2PRINTK("result %d class 0x%04x\n", result, res.classid);
 /*
  * Unlike normal "enqueue" functions, ingress_enqueue returns a
  * firewall FW_* code.
  */
#ifdef CONFIG_NET_CLS_ACT
// 在定义了NET_CLS_ACT的情况, 这时不会返回NF_*, 而是返回TC_ACT_*
 sch->bstats.packets++;
 sch->bstats.bytes += skb->len;
 switch (result) {
  case TC_ACT_SHOT:
// 丢包
   result = TC_ACT_SHOT;
   sch->qstats.drops++;
   break;
  case TC_ACT_STOLEN:
  case TC_ACT_QUEUED:
// 偷包, 重新处理
   result = TC_ACT_STOLEN;
   break;
  case TC_ACT_RECLASSIFY:
  case TC_ACT_OK:
  case TC_ACT_UNSPEC:
  default:
// 接受, 计算tc_index
   skb->tc_index = TC_H_MIN(res.classid);
   result = TC_ACT_OK;
   break;
 };
/* backward compat */
#else
// 在没有定义了NET_CLS_ACT的情况, 这时存在netfilter节点, 返回NF_*结果
#ifdef CONFIG_NET_CLS_POLICE 
// 在定义了NET_CLS_POLICE的情况
 switch (result) {
  case TC_POLICE_SHOT:
// 丢包
  result = NF_DROP;
  sch->qstats.drops++;
  break;
  case TC_POLICE_RECLASSIFY: /* DSCP remarking here ? */
  case TC_POLICE_OK:
  case TC_POLICE_UNSPEC:
  default:
// 其他情况都是ACCEPT
  sch->bstats.packets++;
  sch->bstats.bytes += skb->len;
  result = NF_ACCEPT;
  break;
 };
#else
// 那两个NET_CLS选项都没定义的话直接ACCEPT
 D2PRINTK("Overriding result to ACCEPT\n");
 result = NF_ACCEPT;
 sch->bstats.packets++;
 sch->bstats.bytes += skb->len;
#endif
#endif
 return result;
}
 
5.9.6 重入队

空函数
static int ingress_requeue(struct sk_buff *skb,struct Qdisc *sch)
{
/*
 struct ingress_qdisc_data *p = PRIV(sch);
 D2PRINTK("ingress_requeue(skb %p,sch %p,[qdisc %p])\n",skb,sch,PRIV(p));
*/
 return 0;
}
 
5.9.7 出队

// 空函数,因为不会有真正的dequeue操作
static struct sk_buff *ingress_dequeue(struct Qdisc *sch)
{
/*
 struct ingress_qdisc_data *p = PRIV(sch);
 D2PRINTK("ingress_dequeue(sch %p,[qdisc %p])\n",sch,PRIV(p));
*/
 return NULL;
}
 
5.9.8 丢包
// 基本也是空函数
static unsigned int ingress_drop(struct Qdisc *sch)
{
#ifdef DEBUG_INGRESS
 struct ingress_qdisc_data *p = PRIV(sch);
#endif
 DPRINTK("ingress_drop(sch %p,[qdisc %p])\n", sch, p);
 return 0;
}
 

5.9.9 复位

sstatic void ingress_reset(struct Qdisc *sch)
{
 struct ingress_qdisc_data *p = PRIV(sch);
 DPRINTK("ingress_reset(sch %p,[qdisc %p])\n", sch, p);
/*
#if 0
*/
/* for future use */
// 复位内部流控节点
 qdisc_reset(p->q);
/*
#endif
*/
}
 
5.9.10 释放

static void ingress_destroy(struct Qdisc *sch)
{
// 私有数据
 struct ingress_qdisc_data *p = PRIV(sch);
 struct tcf_proto *tp;
 DPRINTK("ingress_destroy(sch %p,[qdisc %p])\n", sch, p);
// 释放TC过滤规则
 while (p->filter_list) {
  tp = p->filter_list;
  p->filter_list = tp->next;
  tcf_destroy(tp);
 }
#if 0
/* for future use */
 qdisc_destroy(p->q);
#endif
}

5.9.11 输出参数

static int ingress_dump(struct Qdisc *sch, struct sk_buff *skb)
{
 unsigned char *b = skb->tail;
 struct rtattr *rta;
 rta = (struct rtattr *) b;
// 起始什么数据也没有
 RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
 rta->rta_len = skb->tail - b;
 return skb->len;
rtattr_failure:
 skb_trim(skb, b - skb->data);
 return -1;
}

5.9.13 ingress类别操作

// 嫁接, 增加叶子节点
// 基本是空函数, 恒返回1
static int ingress_graft(struct Qdisc *sch,unsigned long arg,
    struct Qdisc *new,struct Qdisc **old)
{
#ifdef DEBUG_INGRESS
 struct ingress_qdisc_data *p = PRIV(sch);
#endif
 DPRINTK("ingress_graft(sch %p,[qdisc %p],new %p,old %p)\n",
  sch, p, new, old);
 DPRINTK("\n ingress_graft: You cannot add qdiscs to classes");
        return 1;
}

// 叶子, 空函数
static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg)
{
 return NULL;
}

// 计数增加, 使用类别ID计算
static unsigned long ingress_get(struct Qdisc *sch,u32 classid)
{
#ifdef DEBUG_INGRESS
 struct ingress_qdisc_data *p = PRIV(sch);
#endif
 DPRINTK("ingress_get(sch %p,[qdisc %p],classid %x)\n", sch, p, classid);
 return TC_H_MIN(classid) + 1;
}

// 绑定TC过滤规则表
static unsigned long ingress_bind_filter(struct Qdisc *sch,
    unsigned long parent, u32 classid)
{
 return ingress_get(sch, classid);
}

// 计数减少, 空函数
static void ingress_put(struct Qdisc *sch, unsigned long cl)
{
}

// 空函数
static int ingress_change(struct Qdisc *sch, u32 classid, u32 parent,
    struct rtattr **tca, unsigned long *arg)
{
#ifdef DEBUG_INGRESS
 struct ingress_qdisc_data *p = PRIV(sch);
#endif
 DPRINTK("ingress_change(sch %p,[qdisc %p],classid %x,parent %x),"
  "arg 0x%lx\n", sch, p, classid, parent, *arg);
 DPRINTK("No effect. sch_ingress doesn't maintain classes at the moment");
 return 0;
}
 
// 遍历, 空函数
static void ingress_walk(struct Qdisc *sch,struct qdisc_walker *walker)
{
#ifdef DEBUG_INGRESS
 struct ingress_qdisc_data *p = PRIV(sch);
#endif
 DPRINTK("ingress_walk(sch %p,[qdisc %p],walker %p)\n", sch, p, walker);
 DPRINTK("No effect. sch_ingress doesn't maintain classes at the moment");
}

// 获取TC过滤规则表
static struct tcf_proto **ingress_find_tcf(struct Qdisc *sch,unsigned long cl)
{
 struct ingress_qdisc_data *p = PRIV(sch);
 return &p->filter_list;
}
 
...... 待续 ......
分享到:
评论

相关推荐

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

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

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

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

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

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

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

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

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

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

    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的范例...

    编写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,隧道的配置与其他部分完全集成了。

    TC限制源和目标IP脚本

    资源描述:TC(Traffic Control)脚本实战指南...TC是Linux内核中强大的网络流量控制和整形工具,适用于网络测试、QoS设置及流量管理等多种场景。本教程适合网络管理员、系统工程师以及对Linux网络管理感兴趣的开发者。

    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...

Global site tag (gtag.js) - Google Analytics