日期:2014-05-16  浏览次数:20857 次

Linux内核中流量控制(24)
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
8.10 mirred(mirror and redirection)

packet mirroring and redirect actions
mirred动作是对数据进行镜像和重定向操作, 将数据包从指定网卡发出, 在net/sched/act_mirred.c中定义

8.10.1 数据结构和动作操作结构

/* include/linux/tc_act/tc_ipt.h */
struct tc_mirred
{
 tc_gen;
// 动作
 int                     eaction;   /* one of IN/EGRESS_MIRROR/REDIR */
// 数据包发出网卡索引号
 __u32                   ifindex;  /* ifindex of egress port */
};

/* include/net/tc_act/tc_ipt.h */
// mirred动作结构
struct tcf_mirred {
 struct tcf_common common;
 int   tcfm_eaction;
 int   tcfm_ifindex;
 int   tcfm_ok_push;
 struct net_device *tcfm_dev;
};
#define to_mirred(pc) \
 container_of(pc, struct tcf_mirred, common)
/* net/sched/act_ipt.c */

static struct tcf_hashinfo mirred_hash_info = {
 .htab = tcf_mirred_ht,
 .hmask = MIRRED_TAB_MASK,
 .lock = &mirred_lock,
};
 
// mirred动作操作结构
static struct tc_action_ops act_mirred_ops = {
// 名称
 .kind  = "mirred",
 .hinfo  = &mirred_hash_info,
// 类型
 .type  = TCA_ACT_MIRRED,
 .capab  = TCA_CAP_NONE,
 .owner  = THIS_MODULE,
 .act  = tcf_mirred,
 .dump  = tcf_mirred_dump,
 .cleanup = tcf_mirred_cleanup,
// 查找, 通用函数
 .lookup  = tcf_hash_search,
 .init  = tcf_mirred_init,
// 遍历, 通用函数
 .walk  = tcf_generic_walker
};
 

8.10.2 初始化

static int tcf_mirred_init(struct rtattr *rta, struct rtattr *est,
      struct tc_action *a, int ovr, int bind)
{
 struct rtattr *tb[TCA_MIRRED_MAX];
 struct tc_mirred *parm;
 struct tcf_mirred *m;
 struct tcf_common *pc;
 struct net_device *dev = NULL;
 int ret = 0;
 int ok_push = 0;
// 解析参数, 保存于tb数组, 失败返回
 if (rta == NULL || rtattr_parse_nested(tb, TCA_MIRRED_MAX, rta) < 0)
  return -EINVAL;
// 必须要有MIRRED参数
 if (tb[TCA_MIRRED_PARMS-1] == NULL ||
     RTA_PAYLOAD(tb[TCA_MIRRED_PARMS-1]) < sizeof(*parm))
  return -EINVAL;
 parm = RTA_DATA(tb[TCA_MIRRED_PARMS-1]);

// 如果定义了网卡索引号
 if (parm->ifindex) {
// 查找相应的网卡设备结构
  dev = __dev_get_by_index(parm->ifindex);
  if (dev == NULL)
   return -ENODEV;
  switch (dev->type) {
// 以下类型的网卡扩展硬件头, 这些通常是虚拟网卡
   case ARPHRD_TUNNEL:
   case ARPHRD_TUNNEL6:
   case ARPHRD_SIT:
   case ARPHRD_IPGRE:
   case ARPHRD_VOID:
   case ARPHRD_NONE:
    ok_push = 0;
    break;
   default:
// 其他类型网卡需要扩展硬件头
    ok_push = 1;
    break;
  }
 }
// 根据索引号查找common节点, 绑定到a节点(priv)
 pc = tcf_hash_check(parm->index, a, bind, &mirred_hash_info);
 if (!pc) {
// 如果节点为空
// 必须要有网卡参数
  if (!parm->ifindex)
   return -EINVAL;
// 创建新的common节点
  pc = tcf_hash_create(parm->index, est, a, sizeof(*m), bind,
         &mirred_idx_gen, &mirred_hash_info);
  if (unlikely(!pc))
   return -ENOMEM;
// 新建标志
  ret = ACT_P_CREATED;
 } else {
// ovr是替代标志, 如果不是替代操作, 对象已经存在, 操作失败
  if (!ovr) {
   tcf_mirred_release(to_mirred(pc), bind);
   return -EEXIST;
  }
 }
// 转换为mirred动作结构
 m = to_mirred(pc);
 spin_lock_bh(&m->tcf_lock);
// 动作
 m->tcf_action = parm->action;
// 实际动作
 m->tcfm_eaction = parm->eaction;
 if (parm->ifindex) {
// 填充网卡参数
  m->tcfm_ifindex = parm->ifindex;
// 如果不是新建操作, 减少网卡计数, 因为已经引用过了
  if (ret != ACT_P_CREATED)
   dev_put(m->tcfm_dev);
// 网卡
  m->tcfm_dev = dev;
  dev_hold(dev);
// 硬件头扩展标志
  m->tcfm_ok_push = ok_push;
 }
 spin_unlock_bh(&m->tcf_lock);
// 如果是新建节点, 插入哈希表
 if (ret == ACT_P_CREATED)
  tcf_hash_insert(pc, &mirred_hash_info);
 return ret;
}

8.10.3 动作

// 将数据包从指定网卡发出
static int tcf_mirred(struct sk_buff *skb, struct tc_action *a,
        struct tcf_result *res)
{
// mirred动作结构
 struct tcf_mirred *m = a->priv;
 struct net_device *dev;
 struct sk_buff *skb2 = NULL;
// 数据包自身的动作信息
 u32 at = G_TC_AT(skb->tc_verd);
 spin_lock(&m->tcf_lock);
// 网卡
 dev = m->tcfm_dev;
// 最后使用时间
 m->tcf_tm.lastuse = jiffies;
 if (!(dev->flags&IFF_UP) ) {
// 如果该网卡没运行, 丢包
  if (net_ratelimit())
   printk("mirred to Houston: device %s is gone!\n",
          de