`

Linux内核中流量控制(17)

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

7.6 tcf_proto_ops的一些相关操作

7.6.1 登记和撤销

/* Register(unregister) new classifier type */
// 登记新的tcf_proto_ops分类操作结构
int register_tcf_proto_ops(struct tcf_proto_ops *ops)
{
 struct tcf_proto_ops *t, **tp;
 int rc = -EEXIST;
 write_lock(&cls_mod_lock);
// 遍历当前tcf_proto_ops链表
 for (tp = &tcf_proto_base; (t = *tp) != NULL; tp = &t->next)
// 检查是否有名称相同的项, 有的话返回对象已存在错误
  if (!strcmp(ops->kind, t->kind))
   goto out;
// 添加到链表末尾, 也是dummy header算法
 ops->next = NULL;
 *tp = ops;
 rc = 0;
out:
 write_unlock(&cls_mod_lock);
 return rc;
}

// 撤销tcf_proto_ops分类结构
int unregister_tcf_proto_ops(struct tcf_proto_ops *ops)
{
 struct tcf_proto_ops *t, **tp;
 int rc = -ENOENT;
 write_lock(&cls_mod_lock);
// 遍历链表
 for (tp = &tcf_proto_base; (t=*tp) != NULL; tp = &t->next)
// 直接进行tcf_proto_ops结构地址比较, 相同的话中断循环
  if (t == ops)
   break;
 if (!t)
  goto out;
// 将找到的tp节点从链表中断开, 不用释放操作, 因为这些ops其实都是静态定义的
 *tp = t->next;
 rc = 0;
out:
 write_unlock(&cls_mod_lock);
 return rc;
}

7.6.2 tcf扩展

tcf扩展增加了对分类后数据进行某种操作的功能, 有点象netfilter的target,使用这些功能需要在配置内核时定义NET_CLS_ACT或NET_CLS_POLICE。

/* include/net/pkt_cls.h */
// tcf扩展结构, 如果没定义NET_CLS_ACT和NET_CLS_POLICE的话就是个空结构
struct tcf_exts
{
#ifdef CONFIG_NET_CLS_ACT
// 动作
 struct tc_action *action;
#elif defined CONFIG_NET_CLS_POLICE
// 策略
 struct tcf_police *police;
#endif
};
/* Map to export classifier specific extension TLV types to the
 * generic extensions API. Unsupported extensions must be set to 0.
 */
struct tcf_ext_map
{
 int action;
 int police;
};

/**
 * tcf_exts_is_predicative - check if a predicative extension is present
 * @exts: tc filter extensions handle
 *
 * Returns 1 if a predicative extension is present, i.e. an extension which
 * might cause further actions and thus overrule the regular tcf_result.
 */
// 返回扩展结构中的元素是否为空
static inline int
tcf_exts_is_predicative(struct tcf_exts *exts)
{
#ifdef CONFIG_NET_CLS_ACT
// !!是为了保证返回值0或1
 return !!exts->action;
#elif defined CONFIG_NET_CLS_POLICE
 return !!exts->police;
#else
 return 0;
#endif
}
/**
 * tcf_exts_is_available - check if at least one extension is present
 * @exts: tc filter extensions handle
 *
 * Returns 1 if at least one extension is present.
 */
// 实际就是cf_exts_is_predicative函数
static inline int
tcf_exts_is_available(struct tcf_exts *exts)
{
 /* All non-predicative extensions must be added here. */
 return tcf_exts_is_predicative(exts);
}
/**
 * tcf_exts_exec - execute tc filter extensions
 * @skb: socket buffer
 * @exts: tc filter extensions handle
 * @res: desired result
 *
 * Executes all configured extensions. Returns 0 on a normal execution,
 * a negative number if the filter must be considered unmatched or
 * a positive action code (TC_ACT_*) which must be returned to the
 * underlying layer.
 */
static inline int
tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts,
        struct tcf_result *res)
{
#ifdef CONFIG_NET_CLS_ACT
 if (exts->action)
  return tcf_action_exec(skb, exts->action, res);
#elif defined CONFIG_NET_CLS_POLICE
 if (exts->police)
  return tcf_police(skb, exts->police);
#endif
 return 0;
}
 
/* net/sched/cls_api.c */
// 是否tcf扩展结构
void
tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts)
{
#ifdef CONFIG_NET_CLS_ACT
 if (exts->action) {
// 释放tcf动作
  tcf_action_destroy(exts->action, TCA_ACT_UNBIND);
  exts->action = NULL;
 }
#elif defined CONFIG_NET_CLS_POLICE
 if (exts->police) {
// 释放tcf策略
  tcf_police_release(exts->police, TCA_ACT_UNBIND);
  exts->police = NULL;
 }
#endif
}
 
int
tcf_exts_validate(struct tcf_proto *tp, struct rtattr **tb,
           struct rtattr *rate_tlv, struct tcf_exts *exts,
           struct tcf_ext_map *map)
{
// 结构清零
 memset(exts, 0, sizeof(*exts));
 
#ifdef CONFIG_NET_CLS_ACT
 {
  int err;
  struct tc_action *act;
// 如果策略存在
  if (map->police && tb[map->police-1]) {
// 进行动作初始化, 生成新动作指针
   act = tcf_action_init_1(tb[map->police-1], rate_tlv, "police",
    TCA_ACT_NOREPLACE, TCA_ACT_BIND, &err);
   if (act == NULL)
    return err;
// TCA_OLD_COMPAT标志是策略
   act->type = TCA_OLD_COMPAT;
   exts->action = act;
  } else
// 如果动作存在
  if (map->action && tb[map->action-1]) {
// 动作初始化, 生成动作指针
   act = tcf_action_init(tb[map->action-1], rate_tlv, NULL,
    TCA_ACT_NOREPLACE, TCA_ACT_BIND, &err);
   if (act == NULL)
    return err;
// action赋值
   exts->action = act;
  }
 }
#elif defined CONFIG_NET_CLS_POLICE
// 如果策略存在
 if (map->police && tb[map->police-1]) {
  struct tcf_police *p;
// 生成新策略结构
  p = tcf_police_locate(tb[map->police-1], rate_tlv);
  if (p == NULL)
   return -EINVAL;
// police赋值
  exts->police = p;
 } else if (map->action && tb[map->action-1])
  return -EOPNOTSUPP;
#else
 if ((map->action && tb[map->action-1]) ||
     (map->police && tb[map->police-1]))
  return -EOPNOTSUPP;
#endif
 return 0;
}

// 修改扩展结构, 将src中的参数填到dst中
void
tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
         struct tcf_exts *src)
{
#ifdef CONFIG_NET_CLS_ACT
// 动作操作
 if (src->action) {
  struct tc_action *act;
  tcf_tree_lock(tp);
// dst和src的action进行交换, 返回原来dst->action
  act = xchg(&dst->action, src->action);
  tcf_tree_unlock(tp);
// 如果原来的action非空, 释放之
  if (act)
   tcf_action_destroy(act, TCA_ACT_UNBIND);
 }
#elif defined CONFIG_NET_CLS_POLICE
// 策略操作
 if (src->police) {
  struct tcf_police *p;
  tcf_tree_lock(tp);
// dst和src的police进行交换, 返回原来dst->police
  p = xchg(&dst->police, src->police);
  tcf_tree_unlock(tp);
// 如果原来的police非空, 释放之
  if (p)
   tcf_police_release(p, TCA_ACT_UNBIND);
 }
#endif
}

// 输出扩展结构
int
tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts,
       struct tcf_ext_map *map)
{
#ifdef CONFIG_NET_CLS_ACT
// 输出动作
 if (map->action && exts->action) {
// 参数存在
  /*
   * again for backward compatible mode - we want
   * to work with both old and new modes of entering
   * tc data even if iproute2  was newer - jhs
   */
// 在数据包缓冲区中定位
  struct rtattr * p_rta = (struct rtattr*) skb->tail;
  if (exts->action->type != TCA_OLD_COMPAT) {
// 类型是动作
   RTA_PUT(skb, map->action, 0, NULL);
// 动作输出
   if (tcf_action_dump(skb, exts->action, 0, 0) < 0)
    goto rtattr_failure;
// 数据长度
   p_rta->rta_len = skb->tail - (u8*)p_rta;
  } else if (map->police) {
// 类型是策略
   RTA_PUT(skb, map->police, 0, NULL);
// 策略输出
   if (tcf_action_dump_old(skb, exts->action, 0, 0) < 0)
    goto rtattr_failure;
// 数据长度
   p_rta->rta_len = skb->tail - (u8*)p_rta;
  }
 }
#elif defined CONFIG_NET_CLS_POLICE
// 输出策略
 if (map->police && exts->police) {
// 策略存在
// 在数据包缓冲区中定位
  struct rtattr * p_rta = (struct rtattr*) skb->tail;
  RTA_PUT(skb, map->police, 0, NULL);
// 策略输出
  if (tcf_police_dump(skb, exts->police) < 0)
   goto rtattr_failure;
// 数据长度
  p_rta->rta_len = skb->tail - (u8*)p_rta;
 }
#endif
 return 0;
rtattr_failure: __attribute__ ((unused))
 return -1;
}
 
// 输出统计值
int
tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts,
             struct tcf_ext_map *map)
{
#ifdef CONFIG_NET_CLS_ACT
// 动作统计
 if (exts->action)
// 输出策略统计值到skb
  if (tcf_action_copy_stats(skb, exts->action, 1) < 0)
   goto rtattr_failure;
#elif defined CONFIG_NET_CLS_POLICE
// 策略统计
 if (exts->police)
// 输出策略统计值到skb
  if (tcf_police_dump_stats(skb, exts->police) < 0)
   goto rtattr_failure;
#endif
 return 0;
rtattr_failure: __attribute__ ((unused))
 return -1;
}
 
7.7 TC分类操作

下面看一下分类函数是如何被调用的, 分类操作通过tc_classify()函数完成, 在以前介绍的各种分类流控算法中都见过该函数:
/* net/sched/sch_api.c */
/* Main classifier routine: scans classifier chain attached
   to this qdisc, (optionally) tests for protocol and asks
   specific classifiers.
 */
//
int tc_classify(struct sk_buff *skb, struct tcf_proto *tp,
 struct tcf_result *res)
{
 int err = 0;
// 协议, 这里应该是数据链路层中的协议值, 如IP包就是0x0800
 u32 protocol = skb->protocol;
#ifdef CONFIG_NET_CLS_ACT
 struct tcf_proto *otp = tp;
reclassify:
#endif
 protocol = skb->protocol;
// 遍历分类规则链表
 for ( ; tp; tp = tp->next) {
// 首先要求协议匹配
  if ((tp->protocol == protocol ||
   tp->protocol == __constant_htons(ETH_P_ALL)) &&
// 然后调用tcf_proto中的分类函数进行处理, 该函数实际就是tcf_proto_ops的分类函数
// 分类结构>=0表示分类成功
   (err = tp->classify(skb, tp, res)) >= 0) {
#ifdef CONFIG_NET_CLS_ACT
// 返回结果是需要重新分类
   if ( TC_ACT_RECLASSIFY == err) {
// 将skb中的tc_verd值转换为判断值, 实际是个计数器
    __u32 verd = (__u32) G_TC_VERD(skb->tc_verd);
    tp = otp;
// 转换次数太多, 返回丢包
    if (MAX_REC_LOOP < verd++) {
     printk("rule prio %d protocol %02x reclassify is buggy packet dropped\n",
      tp->prio&0xffff, ntohs(tp->protocol));
     return TC_ACT_SHOT;
    }
// 转换回tc_verd
    skb->tc_verd = SET_TC_VERD(skb->tc_verd,verd);
// 重新分类操作
    goto reclassify;
   } else {
// 非重新分类的话, 更新tc_verd, 返回分类结果
    if (skb->tc_verd)
     skb->tc_verd = SET_TC_VERD(skb->tc_verd,0);
    return err;
   }
#else
// 如果内核没定义NET_CLS_ACT, 直接返回分类操作结果
   return err;
#endif
  }
 }
// 循环退出, 分类失败
 return -1;
}
tc_classify()的核心函数就是tp->classify()函数
 
7.8 fw过滤操作结构

fw分类方法主要是根据skb中nfmark参数来进行数据分类, 而该参数是由netfilter定义的, 如果内核里没有定义netfilter, 这该分类方法意义不大,该分类方法在 net/sched/cls_fw.c 中定义。
7.8.1 结构定义
static struct tcf_proto_ops cls_fw_ops = {
// 这个参数可以不用明确写出来, 这种定义方法参数缺省就是0了
 .next  = NULL,
// 名称
 .kind  = "fw",
// 各种操作函数
 .classify = fw_classify,
 .init  = fw_init,
 .destroy = fw_destroy,
 .get  = fw_get,
 .put  = fw_put,
 .change  = fw_change,
 .delete  = fw_delete,
 .walk  = fw_walk,
 .dump  = fw_dump,
 .owner  = THIS_MODULE,
};

// 哈希表数量, 空间限制为一页内存, x86是4K, 32系统指针是4字节, 因此应该是1024个
#define HTSIZE (PAGE_SIZE/sizeof(struct fw_filter *))
// 链表头
struct fw_head
{
 struct fw_filter *ht[HTSIZE];
 u32 mask;
};

// fw过滤器
struct fw_filter
{
 struct fw_filter *next;
 u32   id;
 struct tcf_result res;
#ifdef CONFIG_NET_CLS_IND
 char   indev[IFNAMSIZ];
#endif /* CONFIG_NET_CLS_IND */
 struct tcf_exts  exts;
};

static struct tcf_ext_map fw_ext_map = {
 .action = TCA_FW_ACT,
 .police = TCA_FW_POLICE
};

7.8.2 初始化

// 空函数
static int fw_init(struct tcf_proto *tp)
{
 return 0;
}
 
7.8.3 分类

// 计算哈希值
static __inline__ int fw_hash(u32 handle)
{
// 如果是4096, 2^12, 按8:12:12分割异或
 if (HTSIZE == 4096)
  return ((handle >> 24) & 0xFFF) ^
         ((handle >> 12) & 0xFFF) ^
         (handle & 0xFFF);
// 2048, 2^11, 按10:11:11分割异或
 else if (HTSIZE == 2048)
  return ((handle >> 22) & 0x7FF) ^
         ((handle >> 11) & 0x7FF) ^
         (handle & 0x7FF);
// 1024, 2^10, 按12:10:10分割异或
 else if (HTSIZE == 1024)
  return ((handle >> 20) & 0x3FF) ^
         ((handle >> 10) & 0x3FF) ^
         (handle & 0x3FF);
// 512, 2^9, 按5:9:9:9分割异或
 else if (HTSIZE == 512)
  return (handle >> 27) ^
         ((handle >> 18) & 0x1FF) ^
         ((handle >> 9) & 0x1FF) ^
         (handle & 0x1FF);
// 256, 2^8, 按8:8:8:8分割异或
 else if (HTSIZE == 256) {
  u8 *t = (u8 *) &handle;
  return t[0] ^ t[1] ^ t[2] ^ t[3];
 } else
  return handle & (HTSIZE - 1);
}
 
// fw分类方法, 返回负数表示分类失败, 返回0表示分类成功,分类结果在res中返回
static int fw_classify(struct sk_buff *skb, struct tcf_proto *tp,
     struct tcf_result *res)
{
// HASH链表头
 struct fw_head *head = (struct fw_head*)tp->root;
 struct fw_filter *f;
 int r;
#ifdef CONFIG_NETFILTER
// 如果定义了netfilter, 使用nfmark作为ID, 用掩码掩一下
 u32 id = skb->nfmark & head->mask;
#else
// 否则ID为0,也就是说如果内核中没定义netfilter,分类的意义不大,都是同一类数据包
 u32 id = 0;
#endif
 if (head != NULL) {
// 根据id进行hash, 遍历合适的链表
  for (f=head->ht[fw_hash(id)]; f; f=f->next) {
// 如果ID相同, 合适的话可以返回
   if (f->id == id) {
    *res = f->res;
#ifdef CONFIG_NET_CLS_IND
// 网卡设备匹配
    if (!tcf_match_indev(skb, f->indev))
     continue;
#endif /* CONFIG_NET_CLS_IND */
// 如果没有定义NET_CLS_ACT和NET_CLS_POLICE的话就是个空函数, 返回0
    r = tcf_exts_exec(skb, &f->exts, res);
    if (r < 0)
     continue;
    return r;
   }
  }
 } else {
// 老分类方法, id非0, id高16为0或和Qdisc的handle的高16位相同时, 分类成功
  /* old method */
  if (id && (TC_H_MAJ(id) == 0 || !(TC_H_MAJ(id^tp->q->handle)))) {
   res->classid = id;
   res->class = 0;
   return 0;
  }
 }
 return -1;
}

7.8.4 释放

static void fw_destroy(struct tcf_proto *tp)
{
// tcf_proto的根链表头置零, 原值保存准备用于释放
 struct fw_head *head = (struct fw_head*)xchg(&tp->root, NULL);
 struct fw_filter *f;
 int h;
// 如果链表为空, 返回
 if (head == NULL)
  return;
// 遍历所有哈希表
 for (h=0; h<HTSIZE; h++) {
// 遍历链表
  while ((f=head->ht[h]) != NULL) {
   head->ht[h] = f->next;
// 释放该filter
   fw_delete_filter(tp, f);
  }
 }
 kfree(head);
}

// 释放fw过滤器节点
static inline void
fw_delete_filter(struct tcf_proto *tp, struct fw_filter *f)
{
// 将过滤器和tcf_proto断开, 调用Qdisc_class_ops中的unbind_tcf()成员函数
 tcf_unbind_filter(tp, &f->res);
// 释放tcf扩展元素
 tcf_exts_destroy(tp, &f->exts);
// 释放fw过滤器内存
 kfree(f);
}
 
7.8.5 获取过滤器

static unsigned long fw_get(struct tcf_proto *tp, u32 handle)
{
 struct fw_head *head = (struct fw_head*)tp->root;
 struct fw_filter *f;
 if (head == NULL)
  return 0;
// 用handle进行哈希, 遍历指定的hash表
 for (f=head->ht[fw_hash(handle)]; f; f=f->next) {
// 如果有filter的id和handle相同, 返回
  if (f->id == handle)
   return (unsigned long)f;
 }
 return 0;
}

7.8.6 放弃过滤器

// 空函数
static void fw_put(struct tcf_proto *tp, unsigned long f)
{
}

7.8.7 参数修改

// 新建, 修改都通过该函数完成
static int fw_change(struct tcf_proto *tp, unsigned long base,
       u32 handle,
       struct rtattr **tca,
       unsigned long *arg)
{
// 根哈希节点
 struct fw_head *head = (struct fw_head*)tp->root;
// fw过滤器指针
 struct fw_filter *f = (struct fw_filter *) *arg;
// 选项参数
 struct rtattr *opt = tca[TCA_OPTIONS-1];
 struct rtattr *tb[TCA_FW_MAX];
 int err;
// 如果没提供选项, 在提供了handle的情况下错误, 否则返回成功
 if (!opt)
  return handle ? -EINVAL : 0;
// 解析选项参数是否合法
 if (rtattr_parse_nested(tb, TCA_FW_MAX, opt) < 0)
  return -EINVAL;

// fw过滤器非空, 修改操作
 if (f != NULL) {
// 修改的情况下, 如果handle值非0, 而且和fw过滤器的id不同的话, 返回参数错误
  if (f->id != handle && handle)
   return -EINVAL;
// 进行参数修改操作
  return fw_change_attrs(tp, f, tb, tca, base);
 }
// 新建fw过滤器的情况, 如果handle为0, 返回参数错误
 if (!handle)
  return -EINVAL;
// 链表头为空, 第一次操作
 if (head == NULL) {
// 缺省掩码
  u32 mask = 0xFFFFFFFF;
// 如果在命令参数中定义了掩码, 获取之
  if (tb[TCA_FW_MASK-1]) {
   if (RTA_PAYLOAD(tb[TCA_FW_MASK-1]) != sizeof(u32))
    return -EINVAL;
   mask = *(u32*)RTA_DATA(tb[TCA_FW_MASK-1]);
  }
// 分配链表头空间
  head = kzalloc(sizeof(struct fw_head), GFP_KERNEL);
  if (head == NULL)
   return -ENOBUFS;
// 掩码
  head->mask = mask;
  tcf_tree_lock(tp);
// 作为系统的根哈希链表头
  tp->root = head;
  tcf_tree_unlock(tp);
 }
// 分配新的fw过滤器结构指针
 f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL);
 if (f == NULL)
  return -ENOBUFS;
// 使用handle值作为fw过滤器的ID
 f->id = handle;
// 调用修改函数进行赋值操作
 err = fw_change_attrs(tp, f, tb, tca, base);
 if (err < 0)
  goto errout;
// 添加到合适的hash链表的头, 注意锁的使用
 f->next = head->ht[fw_hash(handle)];
 tcf_tree_lock(tp);
 head->ht[fw_hash(handle)] = f;
 tcf_tree_unlock(tp);
// 将fw过滤器作为参数返回
 *arg = (unsigned long)f;
 return 0;
errout:
 kfree(f);
 return err;
}
 
// 参数修改处理
static int
fw_change_attrs(struct tcf_proto *tp, struct fw_filter *f,
 struct rtattr **tb, struct rtattr **tca, unsigned long base)
{
 struct fw_head *head = (struct fw_head *)tp->root;
 struct tcf_exts e;
 u32 mask;
 int err;
// tcf扩展验证操作
 err = tcf_exts_validate(tp, tb, tca[TCA_RATE-1], &e, &fw_ext_map);
 if (err < 0)
  return err;
 err = -EINVAL;
// 命令参数中提供了类别ID
 if (tb[TCA_FW_CLASSID-1]) {
  if (RTA_PAYLOAD(tb[TCA_FW_CLASSID-1]) != sizeof(u32))
   goto errout;
// 类别ID赋值
  f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);
  tcf_bind_filter(tp, &f->res, base);
 }
#ifdef CONFIG_NET_CLS_IND
// 网卡设备
 if (tb[TCA_FW_INDEV-1]) {
  err = tcf_change_indev(tp, f->indev, tb[TCA_FW_INDEV-1]);
  if (err < 0)
   goto errout;
 }
#endif /* CONFIG_NET_CLS_IND */
// FW掩码
 if (tb[TCA_FW_MASK-1]) {
  if (RTA_PAYLOAD(tb[TCA_FW_MASK-1]) != sizeof(u32))
   goto errout;
  mask = *(u32*)RTA_DATA(tb[TCA_FW_MASK-1]);
  if (mask != head->mask)
   goto errout;
 } else if (head->mask != 0xFFFFFFFF)
  goto errout;
// 将e中的数据赋值到f->exts中
 tcf_exts_change(tp, &f->exts, &e);
 return 0;
errout:
 tcf_exts_destroy(tp, &e);
 return err;
}
 
7.8.8 删除

// 将fw过滤器节点从系统哈希链表中断开, 释放节点
// 注意一定要在系统链表中真正找到该节点才进行释放操作, 否则失败
static int fw_delete(struct tcf_proto *tp, unsigned long arg)
{
// 根节点
 struct fw_head *head = (struct fw_head*)tp->root;
// 要删除的fw过滤器节点
 struct fw_filter *f = (struct fw_filter*)arg;
 struct fw_filter **fp;
 if (head == NULL || f == NULL)
  goto out;
// 根据FW过滤器节点的ID哈希, 遍历相应的哈希链表
 for (fp=&head->ht[fw_hash(f->id)]; *fp; fp = &(*fp)->next) {
// 找到该节点
  if (*fp == f) {
   tcf_tree_lock(tp);
// 将该节点从链表中断开
   *fp = f->next;
   tcf_tree_unlock(tp);
// 释放fw过滤器节点
   fw_delete_filter(tp, f);
   return 0;
  }
 }
out:
 return -EINVAL;
}

7.8.9 输出

static int fw_dump(struct tcf_proto *tp, unsigned long fh,
     struct sk_buff *skb, struct tcmsg *t)
{
 struct fw_head *head = (struct fw_head *)tp->root;
 struct fw_filter *f = (struct fw_filter*)fh;
// 准备填数据的skb缓冲区起始位置
 unsigned char  *b = skb->tail;
 struct rtattr *rta;
// fw过滤器为空,直接返回
 if (f == NULL)
  return skb->len;
// 将fw过滤器的ID作为handle
 t->tcm_handle = f->id;
// 检查一下fw过滤器是否合法
 if (!f->res.classid && !tcf_exts_is_available(&f->exts))
  return skb->len;
 rta = (struct rtattr*)b;
 RTA_PUT(skb, TCA_OPTIONS, 0, NULL);
// 如果类别ID非0, 填充之到缓冲区
 if (f->res.classid)
  RTA_PUT(skb, TCA_FW_CLASSID, 4, &f->res.classid);
#ifdef CONFIG_NET_CLS_IND
// 网卡非空,填网卡名称到缓冲区
 if (strlen(f->indev))
  RTA_PUT(skb, TCA_FW_INDEV, IFNAMSIZ, f->indev);
#endif /* CONFIG_NET_CLS_IND */
// 掩码非全1值,填充到缓冲区
 if (head->mask != 0xFFFFFFFF)
  RTA_PUT(skb, TCA_FW_MASK, 4, &head->mask);
// 扩展元素的(动作/策略)输出
 if (tcf_exts_dump(skb, &f->exts, &fw_ext_map) < 0)
  goto rtattr_failure;
// 新增的数据长度
 rta->rta_len = skb->tail - b;
// 输出统计参数
 if (tcf_exts_dump_stats(skb, &f->exts, &fw_ext_map) < 0)
  goto rtattr_failure;
// 返回数据包当前的总长度
 return skb->len;
rtattr_failure:
 skb_trim(skb, b - skb->data);
 return -1;
}
 

...... 待续 ......
分享到:
评论

相关推荐

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

    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设备驱动程序开发

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

    基于Linux 的防火墙技术研究

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

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

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

    ip route2 源码 第二代网络工具

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

    DarkShell_Linux-Win集群版V2014年

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

    Kali Linux渗透测试(安全牛).txt

    │ 任务085:答疑(Conky、、Linux4.4内核发布),手动漏洞挖掘.mp4 │ 任务086:手动漏洞挖掘(二).mp4 │ 任务087:手动漏洞挖掘(三).mp4 │ 任务088:手动漏洞挖掘(四).mp4 │ 任务089:KALI版本更新(第一个...

Global site tag (gtag.js) - Google Analytics