proc net route/net/psched 文件里的四个整数表示什么意思

Linux TC(28)
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:13512次
排名:千里之外
转载:202篇
(7)(13)(16)(94)(34)(34)(18)proc_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
上传于|0|0|暂无简介
你可能喜欢来自CSDN博客:ingress入口排队规则分析
最后更新时间
blog__5103536
&strong&一、ingress入口排队规则模块初始化&/strong&ingress_module_init  //注册INGRESS类型排队规则  register_qdisc(&ingress_qdisc_ops)  write_lock(&qdisc_mod_lock);    //查找如果排列规则类链表中如果已经注册,则直接跳出  for (qp = &qdisc_base; (q = *qp) != NULL; qp = &q-&next)  if (!strcmp(qops-&id, q-&id))  goto out;    //如果当前注册的排队规则中没有对应的回调,则使用noop的默认值  if (qops-&enqueue == NULL)  qops-&enqueue = noop_qdisc_ops.enqueue;  if (qops-&requeue == NULL)  qops-&requeue = noop_qdisc_ops.requeue;  if (qops-&dequeue == NULL)  qops-&dequeue = noop_qdisc_ops.dequeue;  //将当前注册的排队规则加入到qdisc_base链表末尾  qops-&next = NULL;  *qp = qops;  out:  write_unlock(&qdisc_mod_lock);&strong&二、包调度API子系统初始化&/strong&pktsched_init  //计算出每纳秒多少TICKET,以及每TICKET多少纳秒  #ifdef CONFIG_NET_SCH_CLK_CPU  psched_calibrate_clock()  #elif defined(CONFIG_NET_SCH_CLK_JIFFIES)  psched_tick_per_us = HZ&&PSCHED_JSCALE;  psched_us_per_tick = 1000000;  #endif  //向ROUTE类型的netlink套接口挂载处理回调,当用户侧使用tc命令处理排队规则  //及类时,这些回调对应在内核侧进行排队规则及类的添加、删除等操作。  link_p = rtnetlink_links[PF_UNSPEC];  link_p[RTM_NEWQDISC-RTM_BASE].doit = tc_modify_qdisc;  link_p[RTM_DELQDISC-RTM_BASE].doit = tc_get_qdisc;  link_p[RTM_GETQDISC-RTM_BASE].doit = tc_get_qdisc;  link_p[RTM_GETQDISC-RTM_BASE].dumpit = tc_dump_qdisc;  link_p[RTM_NEWTCLASS-RTM_BASE].doit = tc_ctl_tclass;  link_p[RTM_DELTCLASS-RTM_BASE].doit = tc_ctl_tclass;  link_p[RTM_GETTCLASS-RTM_BASE].doit = tc_ctl_tclass;  link_p[RTM_GETTCLASS-RTM_BASE].dumpit = tc_dump_tclass;    //向qdisc_base链表注册pfifo、bfifo两种排队规则  register_qdisc(&pfifo_qdisc_ops);  register_qdisc(&bfifo_qdisc_ops);  //向/proc/net/psched创建文件,用于用户侧获取psched_tick_per_us等参数  proc_net_fops_create(&psched&, 0, &psched_fops);&strong&三、TC过滤子系统初始化&/strong&tc_filter_init  //向ROUTE类型的netlink套接口注册过滤相关的消息处理函数  link_p = rtnetlink_links[PF_UNSPEC];  link_p[RTM_NEWTFILTER-RTM_BASE].doit = tc_ctl_tfilter;  link_p[RTM_DELTFILTER-RTM_BASE].doit = tc_ctl_tfilter;  link_p[RTM_GETTFILTER-RTM_BASE].doit = tc_ctl_tfilter;  link_p[RTM_GETTFILTER-RTM_BASE].dumpit = tc_dump_tfilter;&strong&四、FW分类器模块初始化&/strong&init_fw  //向tcf_proto_base链表中注册FW分类器  register_tcf_proto_ops(&cls_fw_ops);  for (tp = &tcf_proto_base; (t = *tp) != NULL; tp = &t-&next)  if (!strcmp(ops-&kind, t-&kind))  goto out;    ops-&next = NULL;  *tp = ops;  rc = 0;out:  return rc;&strong&五、给网络接口设置INGRESS排队规则&/strong&命令:tc qdisc add dev eth0 ingress1、用户层代码//初始化,获取每纳秒对应多少TICKETtc_core_init();  fp = fopen(&/proc/net/psched&, &r&);  fscanf(fp, &%08x%08x%08x&, &t2us, &us2t, &clock_res);  fclose(fp);    if (clock_res == )  t2us = us2t;    clock_factor
= (double)clock_res / TIME_UNITS_PER_SEC;  tick_in_usec = (double)t2us / us2t * clock_factor;//创建一个ROUTE类型的netlink套接口rtnl_open(&rth, 0)  rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);  rth-&fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);  setsockopt(rth-&fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf))  setsockopt(rth-&fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf))  rth-&local.nl_family = AF_NETLINK;  rth-&local.nl_groups = subscriptions; //0  bind(rth-&fd, (struct sockaddr*)&rth-&local, sizeof(rth-&local))  rth-&seq = time(NULL);do_cmd(argc-1, argv+1);  if (matches(*argv, &qdisc&) == 0)  //执行设置排队规则的命令  do_qdisc(argc-1, argv+1);  if (matches(*argv, &add&) == 0)  tc_qdisc_modify(RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE,   argc-1, argv+1);  req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));  req.n.nlmsg_flags = NLM_F_REQUEST|flags;  req.n.nlmsg_type = cmd; //RTM_NEWQDISC  req.t.tcm_family = AF_UNSPEC;  while (argc & 0)  if (strcmp(*argv, &dev&) == 0)  NEXT_ARG();  strncpy(d, *argv, sizeof(d)-1);
//eth0  else if (strcmp(*argv, &ingress&) == 0)  //ingress排队规则没有父类,所以会设置特定的值  req.t.tcm_parent = TC_H_INGRESS;    //如果有/usr/lib/tc/ingress.so动态库中则从中获  //取ingress_qdisc_util符号结构,否则检测当前tc  //程序是否有ingress_qdisc_util符号结构则从中获取  //,否则返回q 为空。  strncpy(k, &ingress&, sizeof(k)-1);  q = get_qdisc_kind(k);  //ingress排队规则特定的句柄  req.t.tcm_handle = 0xffff0000;    //在消息尾部追加属性值  //rta-&rta_type =
//TCA_KIND  //rta-&rta_len =
  //属性值为 “ingress”  addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);  //当前q为空  if (q)  //不走此流程  //根据接口名获取接口索引  if (d[0])  idx = ll_name_to_index(d)  req.t.tcm_ifindex = idx;  //给内核发送该netlink消息  rtnl_talk(&rth, &req.n, 0, 0, NULL)rtnl_close(&rth);2、内核层代码用户侧发出RTM_NEWQDISC套接口消息后,在内核侧对应的处理回调函数为tc_modify_qdisc,该函数是在pktsched_init中初始化的。tc_modify_qdisc  tcm = NLMSG_DATA(n);  clid = tcm-&tcm_parent; //当前用户侧传入值为 TC_H_INGRESS    //根据设备索引获取设备对象,上面用户侧传入设备名为eth0  dev = __dev_get_by_index(tcm-&tcm_ifindex)  if (clid)  //ingress类型入口排队规则比较特殊,使用单独的qdisc_ingress  if (clid != TC_H_ROOT)  if (clid != TC_H_INGRESS)  //不走此流程  else  q = dev-&qdisc_ingress;  if (!q || !tcm-&tcm_handle || q-&handle != tcm-&tcm_handle)  if (tcm-&tcm_handle) //用户侧传入为特定的0xffff0000  //当前设备的qdisc_list排队规则链表中不含有此规则,进行创建  if ((q = qdisc_lookup(dev, tcm-&tcm_handle)) == NULL)  goto create_n_graft;  create_n_graft:  if (clid == TC_H_INGRESS)  //创建排队规则  q = qdisc_create(dev, tcm-&tcm_parent, tca, &err);  //从已经注册到qdisc_base链表中获取匹配排队规则,当前ingress已经注册  //,则ops = ingress_qdisc_ops  ops = qdisc_lookup_ops(kind);  sch = qdisc_alloc(dev, ops);  INIT_LIST_HEAD(&sch-&list);  skb_queue_head_init(&sch-&q); //初始化规则中的SKB队列  sch-&ops = ops; //ingress_qdisc_ops  sch-&enqueue = ops-&enqueue; //ingress_enqueue  sch-&dequeue = ops-&dequeue; //ingress_dequeue  sch-&dev = dev;
//eth0设备对象  dev_hold(dev);
//设备对象引用递增  sch-&stats_lock = &dev-&queue_lock;  atomic_set(&sch-&refcnt, 1);    if (handle == TC_H_INGRESS)  sch-&flags |= TCQ_F_INGRESS;  //handle = 0xFFFF0000  handle = TC_H_MAKE(TC_H_INGRESS, 0);    sch-&handle = handle;    //使用排队规则中的初始化回调进行初始化,当前ingress的回调函数为  //ingress_init  ops-&init(sch, tca[TCA_OPTIONS-1])  ingress_init(tca[TCA_OPTIONS-1])  ingress_qdisc_data *p = PRIV(sch); //指向排队规则的私有数据  //当没有开启分类动作编译功能宏时,使用netfilter的钩子来实现  //ingress的分类处理。之前在《网卡驱动收包》小节分析收包时,  //也在netif_receive_skb函数中看到有对  //CONFIG_NET_CLS_ACT功能宏的处理,也就是说如果该功能宏  //开启,则ingress入口排队规则处理从netif_receive_skb接口进入。  //否则,就在netfilter的基础上从PRE_ROUTING链上注册的钩子  //函数ing_hook进入。#ifndef CONFIG_NET_CLS_ACT#ifdef CONFIG_NETFILTER  //向netfillter的nf_hooks中注册IPV4和IPV6的钩子处理函数。  //当前ingress将钩子放置在netfilter的PRE_ROUTING链上,优先级  //在FILTER过滤的优先级之后,钩子回调函数分别为ing_hook  nf_register_hook(&ing_ops)  nf_register_hook(&ing6_ops)#endif#endif  p-&q = &noop_qdisc; //私有数据中存储的q为无效的排队规则  //将当前排队规则加入到设备的qdisc_list链表中  qdisc_lock_tree(dev);  list_add_tail(&sch-&list, &dev-&qdisc_list);  qdisc_unlock_tree(dev);  //排队规则嫁接处理  qdisc_graft(dev, p, clid, q, &old_q);  //ingress类型排队规则没有父类  if (parent == NULL)  //将排队规则加入到设备根规则上,其中ingress类型的设置到特殊的  //dev-&qdisc_ingress位置  dev_graft_qdisc(dev, new);  //设备激活的情况下,先去激活  if (dev-&flags & IFF_UP)  dev_deactivate(dev);    qdisc_lock_tree(dev);    //把当前构造好的排队规则设置到qdisc_ingress  if (qdisc && qdisc-&flags&TCQ_F_INGRESS)  dev-&qdisc_ingress = qdisc;    qdisc_unlock_tree(dev);    //激活设备  if (dev-&flags & IFF_UP)  dev_activate(dev);    //当前old_q老的排队规则不存在,仅存在新的,发送netlink消息,告知添加成功  qdisc_notify(skb, n, clid, old_q, q);&strong&六、设置速率限制&/strong&命令:tc filter add dev eth0 parent ffff: protocol ip prio 50 handle 1 fw police rate 1kbit burst 40 mtu 9k drop flowid :11、用户层代码//初始化,获取每纳秒对应多少TICKETtc_core_init();  fp = fopen(&/proc/net/psched&, &r&);  fscanf(fp, &%08x%08x%08x&, &t2us, &us2t, &clock_res);  fclose(fp);    if (clock_res == )  t2us = us2t;    clock_factor
= (double)clock_res / TIME_UNITS_PER_SEC;  tick_in_usec = (double)t2us / us2t * clock_factor;//创建一个ROUTE类型的netlink套接口rtnl_open(&rth, 0)  rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);  rth-&fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);  setsockopt(rth-&fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf))  setsockopt(rth-&fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf))  rth-&local.nl_family = AF_NETLINK;  rth-&local.nl_groups = subscriptions; //0  bind(rth-&fd, (struct sockaddr*)&rth-&local, sizeof(rth-&local))  rth-&seq = time(NULL);do_cmd(argc-1, argv+1);  do_filter(argc-1, argv+1);  tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE, argc-1,   argv+1);  req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));  req.n.nlmsg_flags = NLM_F_REQUEST|flags;  req.n.nlmsg_type = cmd;
//RTM_NEWTFILTER  req.t.tcm_family = AF_UNSPEC;  //新建的过滤规则,如果没有设置protocol,则默认为匹配、所有以太网下  //的上层协议类型  if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE)  protocol = htons(ETH_P_ALL);  while (argc & 0)  if (strcmp(*argv, &dev&) == 0)  NEXT_ARG();  strncpy(d, *argv, sizeof(d)-1);
//eth0  else if (strcmp(*argv, &parent&) == 0)  NEXT_ARG();  get_tc_classid(&handle, *argv);
//将输入字符转换成类ID  req.t.tcm_parent = handle;
//类ID为 0xFFFF0000  else if (matches(*argv, &protocol&) == 0)  NEXT_ARG();  ll_proto_a2n(&id, *argv)  protocol = id;
//ETH_P_IP  else if (matches(*argv, &priority&) == 0)  NEXT_ARG();  get_u32(&prio, *argv, 0)
//50  else if (strcmp(*argv, &handle&) == 0)  NEXT_ARG();  fhandle = *argv;
//1  else   //如果有/usr/lib/tc/f_fw.so共享库,则从中获取fw_filter_util的符号结 //构,否则使用tc程序中的fw_filter_util的符号结构,当前假设从tc  //程序中取fw_filter_util符号结构。  strncpy(k, *argv, sizeof(k)-1);  q = get_filter_kind(k);    //高16位为优先级,低16位为匹配协议  req.t.tcm_info = TC_H_MAKE(prio&&16, protocol);    //在消息尾部追加KIND属性项  //rta-&rta_type =
//TCA_KIND  //rta-&rta_len =
  //属性值为 “fw”  addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);    //进行fw规则后继的解析处时  //q-&parse_fopt当前为fw_parse_opt  q-&parse_fopt(q, fhandle, argc, argv, &req.n)  fw_parse_opt  //当前handle为1,在fw规则中,命令行中的handle值用于匹配  //iptables规则的mark,就是说fw规则需要和iptables进行配合处理  //比如此时我们命令行中handle 1作用所有经过PREROUTING链  //中的TCP报文,则iptables的规则设置为  //iptables -A PREROUTING -i eth0 -t managle -p tcp -j MARK  // --set-mark 1  if (handle)  get_u32(&t-&tcm_handle, handle, 0) //tcm_handle = 1    //在消息尾部增加OPTIONS属性项,值为空值  addattr_l(n, 4096, TCA_OPTIONS, NULL, 0);    while (argc & 0)  else if (matches(*argv, &police&) == 0)  NEXT_ARG();  //策略规则解析  parse_police(&argc, &argv, TCA_FW_POLICE, n)  act_parse_police(NULL,argc_p,argv_p,tca_id,n);  //1kbit * 1000 / 8 = 125bps  get_rate(&p.rate.rate, *argv)    //buffer = 40byte  get_size_and_cell(&buffer, &Rcell_log, *argv)    //mtu = 9 * 1024 byte  get_size_and_cell(&mtu, &Pcell_log, *argv)    //drop规则  p.action = TC_POLICE_SHOT;    if (p.rate.rate)  p.rate.mpu = mpu;
//9 * 1024 byte  p.rate.overhead = overhead;
当前没配置    //计算速率表  //Pcell_log = -1  //mtu = 0  //linklayer = LINKLAYER_ETHERNET  tc_calc_rtable(&p.peakrate, ptab, Pcell_log,   mtu, linklayer)  if (mtu == 0)  mtu = 2047;  //根据最大传输单元计算需要多少槽位  //我理解是不可能每个字节都有准确速  //率,所以划定字节范围,从多少字节到  //多少字节的速率相同。  if (cell_log & 0)  cell_log = 0;  while ((mtu && cell_log) & 255)  cell_log++;    for (i=0; i&256; i++)  //校正当前槽位的字节大小。这个算  //法比较简单,当前链路类型为以太  //网,则包根据原值处理,不会影响  //包大小。mpu为最小包大小,如果  //槽位字节小于mpu,则校正为mpu  //的值。  sz = tc_adjust_size((i + 1) && cell_log,   mpu, linklayer);    //根据当前槽位字节大小,及用户  //配置的速率,计算当前槽位所需  //ticket时间  rtab[i] = tc_calc_xmittime(bps, sz);    r-&cell_align=-1;  r-&cell_log=cell_log;  r-&linklayer = (linklayer &   TC_LINKLAYER_MASK);    //计算单包的峰值,这里通过单包峰值的大小  //转换成所需要的ticket时间  p.burst = tc_calc_xmittime(p.rate.rate, buffer);  p.mtu = 0;  //在消息尾部增加TCA_FW_POLICE属性项  addattr_l(n, MAX_MSG, tca_id, NULL, 0);    //在消息尾部增加TCA_POLICE_TBF属性项  addattr_l(n, MAX_MSG, TCA_POLICE_TBF, &p,   sizeof(p));    //在消息尾部增加TCA_POLICE_RATE属性项  addattr_l(n, MAX_MSG, TCA_POLICE_RATE,   rtab, 1024);    if (matches(*argv, &flowid&) == 0)  //handle = 0x0000001  get_tc_classid(&handle, *argv)    //在消息尾部追加FW_CLASSID属性项  //rta = NLMSG_TAIL(n);  //rta-&rta_type = //TCA_FW_CLASSID  //rta-&rta_len =  addattr_l(n, 4096, TCA_FW_CLASSID, &handle, 4);    //根据接口名获取接口索引  if (d[0])  idx = ll_name_to_index(d)  req.t.tcm_ifindex = idx;  //给内核发送该netlink消息  rtnl_talk(&rth, &req.n, 0, 0, NULL)  rtnl_close(&rth);2、内核层代码用户侧发出RTM_NEWTFILTER套接口消息后,在内核侧对应的处理回调函数为tc_ctl_tfilter,该函数是在tc_filter_init中初始化的。protocol = TC_H_MIN(t-&tcm_info); //ETH_P_IPprio = TC_H_MAJ(t-&tcm_info);
//50nprio = prio;parent = t-&tcm_parent;
//0xFFFF0000dev = __dev_get_by_index(t-&tcm_ifindex) //eth0设备对象//从设备的qdisc_list列表中查找排队规则,之前ingress排队规则已经加入到链表中,所以//这里的q就等于ingress排队规则。q = qdisc_lookup(dev, TC_H_MAJ(t-&tcm_parent))//前2个字节为排队规则索引,后2个字节为类索引,ingress类型是没无类的排队规则,//该条件不是满足,此时cl变量取值为初始的0。if (TC_H_MIN(parent))  //不走此流程//ingress排队规则中对应的回调为ingress_find_tcf//获取该排队规则中的过滤链表。chain = cops-&tcf_chain(q, cl);  ingress_find_tcf  ingress_qdisc_data *p = PRIV(sch);  return &p-&filter_list;//查找待插入的位置,优先级的值越小表示越高。for (back = chain; (tp=*back) != NULL; back = &tp-&next)  if (tp-&prio &= prio)  if (tp-&prio == prio)  if (!nprio || (tp-&protocol != protocol && protocol))  goto errout;  else  tp = NULL;  break;//新建过滤项if (tp == NULL)  //从tcf_proto_base链表中查找fw分类器,当前tp_ops为cls_fw_ops  tp_ops = tcf_proto_lookup_ops(tca[TCA_KIND-1]);  tp-&ops = tp_ops;
//cls_fw_ops  tp-&protocol = protocol;
//ETH_P_IP  tp-&prio = nprio;
//50  tp-&q = q;
//ingress类型排队规则  tp-&classify = tp_ops-&classify; //fw_classify  tp-&classid = parent;
//0xFFFF0000    //当前fw类的初始化回调为fw_init,该函数内容为空。  tp_ops-&init(tp)    //将当前过滤器加入到当前排队规则的过滤链表中  tp-&next = *back;  *back = tp;//fw分类器的get回调为fw_get,这里tcm_handle是之前命令行的handle值,当前为1tp-&ops-&get(tp, t-&tcm_handle);  //当前分类器还没有存储对应的处理handle,返回为0  fw_get  head = (struct fw_head*)tp-&root;  if (head == NULL)  return 0;  //fw分类器的change回调为tp-&ops-&change(tp, cl, t-&tcm_handle, tca, &fh);  fw_change  head = (struct fw_head*)tp-&root;    opt = tca[TCA_OPTIONS-1]    //把OPTIONS之后的属性项全部复制到临时变量tb中。  rtattr_parse_nested(tb, TCA_FW_MAX, opt)  //当前fw分类器还没有head  if (head == NULL)  u32 mask = 0xFFFFFFFF;  head = kzalloc(sizeof(struct fw_head), GFP_KERNEL);  head-&mask = mask;
//0xFFFFFFFF    //将当前过滤对象的root指向新创建的head控制块  tp-&root = head;  f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL);  f-&id = handle;
//1    //fw过滤对象属性修改  fw_change_attrs(tp, f, tb, tca, base);  //这里面有两个互斥的功能编译宏NET_CLS_ACT和  //NET_CLS_POLICE,一些提示显示NET_CLS_POLICE已经不建议使用  //,所以这里仅分析NET_CLS_ACT相关代码。  tcf_exts_validate(tp, tb, tca[TCA_RATE-1], &e, &fw_ext_map);   //当前命令行输入为police,走此流程  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);  //查找已经注册的police动作模块,a_o为act_police_ops  a_o = tc_lookup_action_n(act_name);    //police对象初始化,当前回调为tcf_act_police_locate  a_o-&init(rta, est, a, ovr, bind);  tcf_act_police_locate  //获取用户侧设置的TBF过滤规则参数  parm = RTA_DATA(tb[TCA_POLICE_TBF-1]);    police = kzalloc(sizeof(*police), GFP_KERNEL);    police-&tcf_refcnt = 1;  spin_lock_init(&police-&tcf_lock);  police-&tcf_stats_lock = &police-&tcf_lock;  police-&tcf_bindcnt = 1;    if (parm-&rate.rate)  //将速率参数加入到qdisc_rtab_list速率链表中  R_tab = qdisc_get_rtab(&parm-&rate,   tb[TCA_POLICE_RATE-1]);    //释放之前老的速率表对象  qdisc_put_rtab(police-&tcfp_R_tab);    //加载新的速率对象  police-&tcfp_R_tab = R_tab;    //数据包峰值、最大传送单元、动作类型  police-&tcfp_toks = police-&tcfp_burst = parm-&burst;  police-&tcfp_mtu = parm-&mtu;  police-&tcf_action = parm-&action;    //获取系统当前时间  PSCHED_GET_TIME(police-&tcfp_t_c);    //生成新的策略对象索引  police-&tcf_index =   tcf_hash_new_index(&police_idx_gen,   &police_hash_info);    //这里tcf_next是一个宏  //#define tcf_next common.tcfc_next  //将策略对象与tcf_police_ht互相引用。  h = tcf_hash(police-&tcf_index, POL_TAB_MASK);  police-&tcf_next = tcf_police_ht[h];  tcf_police_ht[h] = &police-&common;    //新建的策略对象关联到action对象的私有数据  a-&priv = police;    a-&ops = a_o; //act-&ops指向act_police_ops    //动作对象类型  act-&type = TCA_OLD_COMPAT;    //构建好的动作对象先临时存于临时变量exts中,后面在  //tcf_exts_change函数中会把act存入fw过滤对象的  //exts.police中,使各fw过滤对象关联该动作。  exts-&action = act;    if (tb[TCA_FW_CLASSID-1])  //classid = 0x  f-&res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);    //将该过滤对象与类绑定  tcf_bind_filter(tp, &f-&res, base);  //调用排队规则对象的ops-&cl_ops-&bind_tcf,当前排队规则  //为ingress,对应回调为ingress_bind_filter  cl = tp-&q-&ops-&cl_ops-&bind_tcf(tp-&q, base, r-&classid);  ingress_bind_filter  //0x0001 + 1 = 2  return ingress_get(sch, classid);    //r-&class = cl
//2  //同时返回值为r-&class原来的值  cl = cls_set_class(tp, &r-&class, cl);  old_cl = __cls_set_class(clp, cl);  old_cl = *clp;  *clp = cl;  return old_cl;  return old_cl;  //如果之前过滤对象与类有关联,则去除绑定  if (cl)  tp-&q-&ops-&cl_ops-&unbind_tcf(tp-&q, cl);    //这里传参e是在上面tcf_exts_validate中构造的扩展动作对象  //将上面构造的扩展动作对象存储到过滤对象f-&exts-&action中,完成  //过滤对象与动作对象的关联。  tcf_exts_change(tp, &f-&exts, &e);    //将fw过滤对象加入到head的hash链表中  f-&next = head-&ht[fw_hash(handle)];  head-&ht[fw_hash(handle)] = f;//告知添加成功tfilter_notify(skb, n, tp, fh, RTM_NEWTFILTER);&strong&七、收包处理(NET_CLS_ACT开启)&/strong&详细收包流程参见《网卡驱动收包》,这里仅分析在收包处理流程中涉及入口排队规则的代码。网卡驱动调用netif_receive_skb来进行收包处理。netif_receive_skb  ......  #ifdef CONFIG_NET_CLS_ACT  //查看代码发现在IFB接口设备中会设置NCLS值,不再需要进行入口排队规则处理,  //直接跳过到ncls标记处。  if (skb-&tc_verd & TC_NCLS)  skb-&tc_verd = CLR_TC_NCLS(skb-&tc_verd);  goto ncls;  #endif  ......    #ifdef CONFIG_NET_CLS_ACT    //设置OK2MUNGE标记,查看代码,当前仅在pedit类型的动作中触发,如果没有此  //标记,则pedit类型动作在进行处理时,需要复制一次skb。  skb-&tc_verd = SET_TC_OK2MUNGE(skb-&tc_verd);    //进行过滤  ret = ing_filter(skb);  result = TC_ACT_OK;    //如果用户向该接口配置了ingress排队规则,则此条件成功,否则返回默认OK,  //让进来的报文直接向下继续处理。  if (dev-&qdisc_ingress)  //如果出现报文多次内部环回,则将报文丢弃。  __u32 ttl = (__u32) G_TC_RTTL(skb-&tc_verd);  if (MAX_RED_LOOP & ttl++)  return TC_ACT_SHOT;    //更新报文环回值  skb-&tc_verd = SET_TC_RTTL(skb-&tc_verd,ttl);    //标记报文是接收方向  skb-&tc_verd = SET_TC_AT(skb-&tc_verd,AT_INGRESS);    //调用当前排队规则的入队处理回调,当前ingress的回调为ingress_enqueue  result = q-&enqueue(skb, q)  ingress_enqueue  //使用过滤器进行分类处理  result = tc_classify(skb, p-&filter_list, &res);  __be16 protocol = skb-&protocol;  protocol = skb-&protocol;    reclassify:    //遍历所有过滤器,当前例子仅注册了一个fw过滤器  for ( ; tp; tp = tp-&next)  //先进行协议匹配,如果之前用户配置过滤器未设置协议参  //数,则protocol为ETH_P_ALL表示匹配任意协议。  //之后使用当前过滤器的classify回调进行处理。  //当前fw过滤器的回调为fw_classify,下面单独分析。  if ((tp-&protocol == protocol ||  tp-&protocol == __constant_htons(ETH_P_ALL))  && (err = tp-&classify(skb, tp, res)) &= 0)  #ifdef CONFIG_NET_CLS_ACT  //如果过滤结果为需要重分类,则继续尝试重分类,当  //然也需要对重分类次数进行限制。  if ( TC_ACT_RECLASSIFY == err)  __u32 verd = (__u32) G_TC_VERD(skb-&tc_verd);  if (MAX_REC_LOOP & verd++)  return TC_ACT_SHOT;  skb-&tc_verd = SET_TC_VERD(skb-&tc_verd,verd);  goto reclassify;  //其它过滤结果则直接返回,同时清除用于重分类限制  //的VERD标记。  else  if (skb-&tc_verd)  skb-&tc_verd =   SET_TC_VERD(skb-&tc_verd,0);  return err;  #endif  return -1;    #ifdef CONFIG_NET_CLS_ACT  //处理包个数、字节数统计  sch-&bstats.packets++;  sch-&bstats.bytes += skb-&len;    //根据过滤器结果进行返回值映射  switch (result)  case TC_ACT_SHOT:  result = TC_ACT_SHOT;  sch-&qstats.drops++;  case TC_ACT_STOLEN:  case TC_ACT_QUEUED:  result = TC_ACT_STOLEN;  case TC_ACT_RECLASSIFY:  case TC_ACT_OK:  case TC_ACT_UNSPEC:  default:  skb-&tc_index = TC_H_MIN(res.classid);  result = TC_ACT_OK;  return result;  #endif    return result;    //根据过滤结果,决定把进来的包丢弃,还是继续处理。  if (ret == TC_ACT_SHOT || (ret == TC_ACT_STOLEN))  kfree_skb(skb);  goto out;    skb-&tc_verd = 0;    ncls:  #endif  ......---------------------------------------------------------------------------------------------------------------------fw_classify  //分类器头列表  head = (struct fw_head*)tp-&root;  //包的mark,该mark是由iptables或ebtables打上的标签,fw分类器主要用于和iptables、  //ebtables配合来完成对特定包的匹配。  id = skb-&mark;  if (head != NULL)  id &= head-&mask;
//当前用例没有设置掩码,则完整匹配    //遍历满足该id的hash链表  for (f=head-&ht[fw_hash(id)]; f; f=f-&next)  //找到匹配的过滤对象  if (f-&id == id)  *res = f-&res;
//这里记载了该匹配对象绑定的类,参见命令分析  //进行过滤对象的扩展处理,当前例子扩展为速率的限制  r = tcf_exts_exec(skb, &f-&exts, res);#ifdef CONFIG_NET_CLS_ACT  return tcf_action_exec(skb, exts-&action, res);  
/查看代码发现在IFB接口设备中会设置NCLS值,不再需要进  //行入口排队规则处理  if (skb-&tc_verd & TC_NCLS)  skb-&tc_verd = CLR_TC_NCLS(skb-&tc_verd);  ret = TC_ACT_OK;  goto exec_done;    //遍历该过滤器下所有动作,当前实例仅一个限速  while ((a = act) != NULL)repeat:  //进行当前动作的act回调处理,当前动作为policce,则对  //应的回调为tcf_act_police,下面单独分析。  ret = a-&ops-&act(skb, a, res);    //当前仅pedit动作对象会设置TC_MUNGED标记,当设置  //了该标记后,则去除该标记,同时设置OK2MUNGE标记  //后续在进行pedit时,已经复制过了,就不怕乱处理了?  if (TC_MUNGED & skb-&tc_verd)  skb-&tc_verd = SET_TC_OK2MUNGE(skb-&tc_verd);  skb-&tc_verd = CLR_TC_MUNGED(skb-&tc_verd);    //重复处理当前动作  if (ret == TC_ACT_REPEAT)  goto repeat;  //当动作结果为非PIPE时,则处理完成返回结果,否则继  //续使用下一个动作对象进行处理。  if (ret != TC_ACT_PIPE)  goto exec_done;    act = a-&next;  exec_done:  return ret;#endif    if (r & 0)  continue;  return r;----------------------------------------------------------------------------------------------------------------------tcf_act_police  //统计  police-&tcf_bstats.bytes += skb-&len;  police-&tcf_bstats.packets++;  //如果当前报文长度不大于用户设置的MTU值,则为合法条件  if (skb-&len &= police-&tcfp_mtu)  //获取当前系统时间  PSCHED_GET_TIME(now);    //计算从上一次到现在经过的ticket值,该值不能超过用户设置的最大单包峰值。  toks = PSCHED_TDIFF_SAFE(now, police-&tcfp_t_c,police-&tcfp_burst);  //在经过一段时间流逝后,当前可以继续多处理一些数据包,这里将之前的值进行  //累加补充,得到这个时间点可以处理的总的数据量(这里单位是tickt,是将根据  //用户设置的速率进行的字节到ticket的转换,参见上面用户命令的分析可以更清  //楚这里的转换机制)  toks += police-&tcfp_toks;  //将当前报文大小,根据用户侧的速率表进行计算得到在当前速率下这个大小的数  //据需要消耗多少ticket  toks -= L2T(police, skb-&len);    //如果这次发包未达到用户设置的速率限制,则条件满足,此时记录当前剩余的  //令牌值,并返回结果。这里的tcfp_result用户也可以在命令行自行设置,如果  //用户没有设置则为0,对应的值为TC_ACT_OK  if ((toks|ptoks) &= 0)  police-&tcfp_t_c = now;  police-&tcfp_toks = toks;  police-&tcfp_ptoks = ptoks;  return police-&tcfp_result;    //当前报文长度大于用户设置的MTU值,或者已经超过当前速率,执行用户设置的  //动作,当前设置为drop,对应的动作值为TC_POLICE_SHOT,该报文被丢弃。  police-&tcf_qstats.overlimits++;  return police-&tcf_action;&strong&八、收包处理(NET_CLS_ACT未开启)&/strong&当用户没有开启NET_CLS_ACT宏时,入口排队规则的处理被延迟到netfilter架构的PRE_ROUTING链上,优先级在FILTER过滤之后,钩子回调函数为ing_hook。该钩子注册点详见上面“给接口设置ingress入口排队规则”。这里仅关注相关钩子函数,netfilter架构在网上有好多经验文章,这里不再对这块进行详细分析。ing_hook  int fwres=NF_ACCEPT;    //如果用户设置入口排队规则,则使用该排队规则的回调enqueue进行入队列的分类处  //理,否则直接通过。入队的处理在上面已经分析过了,这里不再重复。可以看到使用  //NET_CLS_ACT机制和netfilter的机制来处理入口排队规则因为时间点不同,各有各  //优缺点。NET_CLS_ACT机制是在包刚进来就进行QOS处理,必免了很多分支流程  //的干扰,而使用netfilter的机制,可以让包先进行网桥处理、之后再进行iptables的  //过滤链处理,之后才进行QOS处理,可以在进行QOS处理前做一些其它事情。  if (dev-&qdisc_ingress)  fwres = q-&enqueue(skb, q);  return fwres;

我要回帖

更多关于 proc net dev 详解 的文章

 

随机推荐