ingress入口排队规则分析

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. <strong>一、ingress入口排队规则模块初始化</strong>  
  2. ingress_module_init  
  3.   //注册INGRESS类型排队规则  
  4.   register_qdisc(&ingress_qdisc_ops)  
  5.   write_lock(&qdisc_mod_lock);  
  6.     
  7.   //查找如果排列规则类链表中如果已经注册,则直接跳出  
  8.   for (qp = &qdisc_base; (q = *qp) != NULL; qp = &q->next)  
  9.   if (!strcmp(qops->id, q->id))  
  10.   goto out;  
  11.     
  12.   //如果当前注册的排队规则中没有对应的回调,则使用noop的默认值  
  13.   if (qops->enqueue == NULL)  
  14.   qops->enqueue = noop_qdisc_ops.enqueue;  
  15.   if (qops->requeue == NULL)  
  16.   qops->requeue = noop_qdisc_ops.requeue;  
  17.   if (qops->dequeue == NULL)  
  18.   qops->dequeue = noop_qdisc_ops.dequeue;  
  19.   
  20.   //将当前注册的排队规则加入到qdisc_base链表末尾  
  21.   qops->next = NULL;  
  22.   *qp = qops;  
  23.   
  24.   out:  
  25.   write_unlock(&qdisc_mod_lock);  
  26.   
  27. <strong>二、包调度API子系统初始化</strong>  
  28. pktsched_init  
  29.   //计算出每纳秒多少TICKET,以及每TICKET多少纳秒  
  30.   #ifdef CONFIG_NET_SCH_CLK_CPU  
  31.   psched_calibrate_clock()  
  32.   #elif defined(CONFIG_NET_SCH_CLK_JIFFIES)  
  33.   psched_tick_per_us = HZ<<PSCHED_JSCALE;  
  34.   psched_us_per_tick = 1000000;  
  35.   #endif  
  36.   
  37.   //向ROUTE类型的netlink套接口挂载处理回调,当用户侧使用tc命令处理排队规则  
  38.   //及类时,这些回调对应在内核侧进行排队规则及类的添加、删除等操作。  
  39.   link_p = rtnetlink_links[PF_UNSPEC];  
  40.   link_p[RTM_NEWQDISC-RTM_BASE].doit = tc_modify_qdisc;  
  41.   link_p[RTM_DELQDISC-RTM_BASE].doit = tc_get_qdisc;  
  42.   link_p[RTM_GETQDISC-RTM_BASE].doit = tc_get_qdisc;  
  43.   link_p[RTM_GETQDISC-RTM_BASE].dumpit = tc_dump_qdisc;  
  44.   link_p[RTM_NEWTCLASS-RTM_BASE].doit = tc_ctl_tclass;  
  45.   link_p[RTM_DELTCLASS-RTM_BASE].doit = tc_ctl_tclass;  
  46.   link_p[RTM_GETTCLASS-RTM_BASE].doit = tc_ctl_tclass;  
  47.   link_p[RTM_GETTCLASS-RTM_BASE].dumpit = tc_dump_tclass;  
  48.     
  49.   //向qdisc_base链表注册pfifo、bfifo两种排队规则  
  50.   register_qdisc(&pfifo_qdisc_ops);  
  51.   register_qdisc(&bfifo_qdisc_ops);  
  52.   
  53.   //向/proc/net/psched创建文件,用于用户侧获取psched_tick_per_us等参数  
  54.   proc_net_fops_create("psched", 0, &psched_fops);  
  55. <strong>  
  56. 三、TC过滤子系统初始化</strong>  
  57. tc_filter_init  
  58.   //向ROUTE类型的netlink套接口注册过滤相关的消息处理函数  
  59.   link_p = rtnetlink_links[PF_UNSPEC];  
  60.   link_p[RTM_NEWTFILTER-RTM_BASE].doit = tc_ctl_tfilter;  
  61.   link_p[RTM_DELTFILTER-RTM_BASE].doit = tc_ctl_tfilter;  
  62.   link_p[RTM_GETTFILTER-RTM_BASE].doit = tc_ctl_tfilter;  
  63.   link_p[RTM_GETTFILTER-RTM_BASE].dumpit = tc_dump_tfilter;  
  64. <strong>  
  65. 四、FW分类器模块初始化</strong>  
  66. init_fw  
  67.   //向tcf_proto_base链表中注册FW分类器  
  68.   register_tcf_proto_ops(&cls_fw_ops);  
  69.   for (tp = &tcf_proto_base; (t = *tp) != NULL; tp = &t->next)  
  70.   if (!strcmp(ops->kind, t->kind))  
  71.   goto out;  
  72.     
  73.   ops->next = NULL;  
  74.   *tp = ops;  
  75.   rc = 0;  
  76. out:  
  77.   return rc;  
  78.   
  79. <strong>五、给网络接口设置INGRESS排队规则</strong>  
  80. 命令:tc qdisc add dev eth0 ingress  
  81.   
  82. 1、用户层代码  
  83. //初始化,获取每纳秒对应多少TICKET  
  84. tc_core_init();  
  85.   fp = fopen("/proc/net/psched""r");  
  86.   fscanf(fp, "%08x%08x%08x", &t2us, &us2t, &clock_res);  
  87.   fclose(fp);  
  88.     
  89.   if (clock_res == 1000000000)  
  90.   t2us = us2t;  
  91.     
  92.   clock_factor  = (double)clock_res / TIME_UNITS_PER_SEC;  
  93.   tick_in_usec = (double)t2us / us2t * clock_factor;  
  94.   
  95. //创建一个ROUTE类型的netlink套接口  
  96. rtnl_open(&rth, 0)  
  97.   rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);  
  98.   rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);  
  99.   setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf))  
  100.   setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf))  
  101.   rth->local.nl_family = AF_NETLINK;  
  102.   rth->local.nl_groups = subscriptions;  //0  
  103.   bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local))  
  104.   rth->seq = time(NULL);  
  105.   
  106. do_cmd(argc-1, argv+1);  
  107.   if (matches(*argv, "qdisc") == 0)  
  108.   //执行设置排队规则的命令  
  109.   do_qdisc(argc-1, argv+1);  
  110.   if (matches(*argv, "add") == 0)  
  111.   tc_qdisc_modify(RTM_NEWQDISC, NLM_F_EXCL|NLM_F_CREATE,   
  112.   argc-1, argv+1);  
  113.   req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));  
  114.   req.n.nlmsg_flags = NLM_F_REQUEST|flags;  
  115.   req.n.nlmsg_type = cmd;   //RTM_NEWQDISC  
  116.   req.t.tcm_family = AF_UNSPEC;  
  117.   
  118.   while (argc > 0)  
  119.   if (strcmp(*argv, "dev") == 0)  
  120.   NEXT_ARG();  
  121.   strncpy(d, *argv, sizeof(d)-1);       //eth0  
  122.   else if (strcmp(*argv, "ingress") == 0)  
  123.   //ingress排队规则没有父类,所以会设置特定的值  
  124.   req.t.tcm_parent = TC_H_INGRESS;  
  125.     
  126.   //如果有/usr/lib/tc/ingress.so动态库中则从中获  
  127.   //取ingress_qdisc_util符号结构,否则检测当前tc  
  128.   //程序是否有ingress_qdisc_util符号结构则从中获取  
  129.   //,否则返回q 为空。  
  130.   strncpy(k, "ingress"sizeof(k)-1);  
  131.   q = get_qdisc_kind(k);  
  132.   
  133.   //ingress排队规则特定的句柄  
  134.   req.t.tcm_handle = 0xffff0000;  
  135.     
  136.   //在消息尾部追加属性值  
  137.   //rta->rta_type = type;    //TCA_KIND  
  138.   //rta->rta_len = len;        
  139.   //属性值为 “ingress”  
  140.   addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);  
  141.   
  142.   //当前q为空  
  143.   if (q)  
  144.   //不走此流程  
  145.   
  146.   //根据接口名获取接口索引  
  147.   if (d[0])  
  148.   idx = ll_name_to_index(d)  
  149.   req.t.tcm_ifindex = idx;  
  150.   
  151.   //给内核发送该netlink消息  
  152.   rtnl_talk(&rth, &req.n, 0, 0, NULL)  
  153.   
  154. rtnl_close(&rth);  
  155.   
  156. 2、内核层代码  
  157. 用户侧发出RTM_NEWQDISC套接口消息后,在内核侧对应的处理回调函数为tc_modify_qdisc,该函数是在pktsched_init中初始化的。  
  158.   
  159. tc_modify_qdisc  
  160.   tcm = NLMSG_DATA(n);  
  161.   clid = tcm->tcm_parent;    //当前用户侧传入值为 TC_H_INGRESS  
  162.     
  163.   //根据设备索引获取设备对象,上面用户侧传入设备名为eth0  
  164.   dev = __dev_get_by_index(tcm->tcm_ifindex)  
  165.   
  166.   if (clid)  
  167.   //ingress类型入口排队规则比较特殊,使用单独的qdisc_ingress  
  168.   if (clid != TC_H_ROOT)  
  169.   if (clid != TC_H_INGRESS)  
  170.   //不走此流程  
  171.   else  
  172.   q = dev->qdisc_ingress;  
  173.   
  174.   if (!q || !tcm->tcm_handle || q->handle != tcm->tcm_handle)  
  175.   if (tcm->tcm_handle)   //用户侧传入为特定的0xffff0000  
  176.   //当前设备的qdisc_list排队规则链表中不含有此规则,进行创建  
  177.   if ((q = qdisc_lookup(dev, tcm->tcm_handle)) == NULL)  
  178.   goto create_n_graft;  
  179.   
  180.   create_n_graft:  
  181.   if (clid == TC_H_INGRESS)  
  182.   //创建排队规则  
  183.   q = qdisc_create(dev, tcm->tcm_parent, tca, &err);  
  184.   //从已经注册到qdisc_base链表中获取匹配排队规则,当前ingress已经注册  
  185.   //,则ops = ingress_qdisc_ops  
  186.   ops = qdisc_lookup_ops(kind);  
  187.   
  188.   sch = qdisc_alloc(dev, ops);  
  189.   INIT_LIST_HEAD(&sch->list);  
  190.   skb_queue_head_init(&sch->q);  //初始化规则中的SKB队列  
  191.   sch->ops = ops;    //ingress_qdisc_ops  
  192.   sch->enqueue = ops->enqueue;    //ingress_enqueue  
  193.   sch->dequeue = ops->dequeue;    //ingress_dequeue  
  194.   sch->dev = dev;                //eth0设备对象  
  195.   dev_hold(dev);                //设备对象引用递增  
  196.   sch->stats_lock = &dev->queue_lock;  
  197.   atomic_set(&sch->refcnt, 1);  
  198.     
  199.   if (handle == TC_H_INGRESS)  
  200.   sch->flags |= TCQ_F_INGRESS;  
  201.   //handle = 0xFFFF0000  
  202.   handle = TC_H_MAKE(TC_H_INGRESS, 0);  
  203.     
  204.   sch->handle = handle;  
  205.     
  206.   //使用排队规则中的初始化回调进行初始化,当前ingress的回调函数为  
  207.   //ingress_init  
  208.   ops->init(sch, tca[TCA_OPTIONS-1])  
  209.   ingress_init(tca[TCA_OPTIONS-1])  
  210.   ingress_qdisc_data *p = PRIV(sch);    //指向排队规则的私有数据  
  211.   
  212.   //当没有开启分类动作编译功能宏时,使用netfilter的钩子来实现  
  213.   //ingress的分类处理。之前在《网卡驱动收包》小节分析收包时,  
  214.   //也在netif_receive_skb函数中看到有对  
  215.   //CONFIG_NET_CLS_ACT功能宏的处理,也就是说如果该功能宏  
  216.   //开启,则ingress入口排队规则处理从netif_receive_skb接口进入。  
  217.   //否则,就在netfilter的基础上从PRE_ROUTING链上注册的钩子  
  218.   //函数ing_hook进入。  
  219. #ifndef CONFIG_NET_CLS_ACT  
  220. #ifdef CONFIG_NETFILTER  
  221.   //向netfillter的nf_hooks中注册IPV4和IPV6的钩子处理函数。  
  222.   //当前ingress将钩子放置在netfilter的PRE_ROUTING链上,优先级  
  223.   //在FILTER过滤的优先级之后,钩子回调函数分别为ing_hook  
  224.   nf_register_hook(&ing_ops)  
  225.   nf_register_hook(&ing6_ops)  
  226. #endif  
  227. #endif  
  228.   
  229.   p->q = &noop_qdisc;    //私有数据中存储的q为无效的排队规则  
  230.   
  231.   //将当前排队规则加入到设备的qdisc_list链表中  
  232.   qdisc_lock_tree(dev);  
  233.   list_add_tail(&sch->list, &dev->qdisc_list);  
  234.   qdisc_unlock_tree(dev);  
  235.   
  236.   //排队规则嫁接处理  
  237.   qdisc_graft(dev, p, clid, q, &old_q);  
  238.   //ingress类型排队规则没有父类  
  239.   if (parent == NULL)  
  240.   //将排队规则加入到设备根规则上,其中ingress类型的设置到特殊的  
  241.   //dev->qdisc_ingress位置  
  242.   dev_graft_qdisc(dev, new);  
  243.   //设备激活的情况下,先去激活  
  244.   if (dev->flags & IFF_UP)  
  245.   dev_deactivate(dev);  
  246.     
  247.   qdisc_lock_tree(dev);  
  248.     
  249.   //把当前构造好的排队规则设置到qdisc_ingress  
  250.   if (qdisc && qdisc->flags&TCQ_F_INGRESS)  
  251.   dev->qdisc_ingress = qdisc;  
  252.     
  253.   qdisc_unlock_tree(dev);  
  254.     
  255.   //激活设备  
  256.   if (dev->flags & IFF_UP)  
  257.   dev_activate(dev);  
  258.     
  259.   //当前old_q老的排队规则不存在,仅存在新的,发送netlink消息,告知添加成功  
  260.   qdisc_notify(skb, n, clid, old_q, q);  
  261.   
  262. <strong>六、设置速率限制</strong>  
  263. 命令:tc filter add dev eth0 parent ffff: protocol ip prio 50 handle 1 fw police rate 1kbit burst 40 mtu 9k drop flowid :1  
  264.   
  265. 1、用户层代码  
  266. //初始化,获取每纳秒对应多少TICKET  
  267. tc_core_init();  
  268.   fp = fopen("/proc/net/psched""r");  
  269.   fscanf(fp, "%08x%08x%08x", &t2us, &us2t, &clock_res);  
  270.   fclose(fp);  
  271.     
  272.   if (clock_res == 1000000000)  
  273.   t2us = us2t;  
  274.     
  275.   clock_factor  = (double)clock_res / TIME_UNITS_PER_SEC;  
  276.   tick_in_usec = (double)t2us / us2t * clock_factor;  
  277.   
  278. //创建一个ROUTE类型的netlink套接口  
  279. rtnl_open(&rth, 0)  
  280.   rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);  
  281.   rth->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol);  
  282.   setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf))  
  283.   setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf))  
  284.   rth->local.nl_family = AF_NETLINK;  
  285.   rth->local.nl_groups = subscriptions;  //0  
  286.   bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local))  
  287.   rth->seq = time(NULL);  
  288.   
  289. do_cmd(argc-1, argv+1);  
  290.   do_filter(argc-1, argv+1);  
  291.   tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE, argc-1,   
  292.   argv+1);  
  293.   req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));  
  294.   req.n.nlmsg_flags = NLM_F_REQUEST|flags;  
  295.   req.n.nlmsg_type = cmd;       //RTM_NEWTFILTER  
  296.   req.t.tcm_family = AF_UNSPEC;  
  297.   
  298.   //新建的过滤规则,如果没有设置protocol,则默认为匹配、所有以太网下  
  299.   //的上层协议类型  
  300.   if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE)  
  301.   protocol = htons(ETH_P_ALL);  
  302.   
  303.   while (argc > 0)  
  304.   if (strcmp(*argv, "dev") == 0)  
  305.   NEXT_ARG();  
  306.   strncpy(d, *argv, sizeof(d)-1);       //eth0  
  307.   else if (strcmp(*argv, "parent") == 0)  
  308.   NEXT_ARG();  
  309.   get_tc_classid(&handle, *argv);  //将输入字符转换成类ID  
  310.   req.t.tcm_parent = handle;        //类ID为 0xFFFF0000  
  311.   else if (matches(*argv, "protocol") == 0)  
  312.   NEXT_ARG();  
  313.   ll_proto_a2n(&id, *argv)  
  314.   protocol = id;                    //ETH_P_IP  
  315.   else if (matches(*argv, "priority") == 0)  
  316.   NEXT_ARG();  
  317.   get_u32(&prio, *argv, 0)          //50  
  318.   else if (strcmp(*argv, "handle") == 0)  
  319.   NEXT_ARG();  
  320.   fhandle = *argv;              //1  
  321.   else   
  322.   //如果有/usr/lib/tc/f_fw.so共享库,则从中获取fw_filter_util的符号结   //构,否则使用tc程序中的fw_filter_util的符号结构,当前假设从tc  
  323.   //程序中取fw_filter_util符号结构。  
  324.   strncpy(k, *argv, sizeof(k)-1);  
  325.   q = get_filter_kind(k);  
  326.     
  327.   //高16位为优先级,低16位为匹配协议  
  328.   req.t.tcm_info = TC_H_MAKE(prio<<16, protocol);  
  329.     
  330.   //在消息尾部追加KIND属性项  
  331.   //rta->rta_type = type;    //TCA_KIND  
  332.   //rta->rta_len = len;        
  333.   //属性值为 “fw”  
  334.   addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);  
  335.     
  336.   //进行fw规则后继的解析处时  
  337.   //q->parse_fopt当前为fw_parse_opt  
  338.   q->parse_fopt(q, fhandle, argc, argv, &req.n)  
  339.   fw_parse_opt  
  340.   //当前handle为1,在fw规则中,命令行中的handle值用于匹配  
  341.   //iptables规则的mark,就是说fw规则需要和iptables进行配合处理  
  342.   //比如此时我们命令行中handle 1作用所有经过PREROUTING链  
  343.   //中的TCP报文,则iptables的规则设置为  
  344.   //iptables -A PREROUTING -i eth0 -t managle -p tcp -j MARK  
  345.   // --set-mark 1  
  346.   if (handle)  
  347.   get_u32(&t->tcm_handle, handle, 0) //tcm_handle = 1  
  348.     
  349.   //在消息尾部增加OPTIONS属性项,值为空值  
  350.   addattr_l(n, 4096, TCA_OPTIONS, NULL, 0);  
  351.     
  352.   while (argc > 0)  
  353.   else if (matches(*argv, "police") == 0)  
  354.   NEXT_ARG();  
  355.   //策略规则解析  
  356.   parse_police(&argc, &argv, TCA_FW_POLICE, n)  
  357.   act_parse_police(NULL,argc_p,argv_p,tca_id,n);  
  358.   //1kbit * 1000 / 8 = 125bps  
  359.   get_rate(&p.rate.rate, *argv)  
  360.     
  361.   //buffer = 40byte  
  362.   get_size_and_cell(&buffer, &Rcell_log, *argv)  
  363.     
  364.   //mtu = 9 * 1024 byte  
  365.   get_size_and_cell(&mtu, &Pcell_log, *argv)  
  366.     
  367.   //drop规则  
  368.   p.action = TC_POLICE_SHOT;  
  369.     
  370.   if (p.rate.rate)  
  371.   p.rate.mpu = mpu;         //9 * 1024 byte  
  372.   p.rate.overhead = overhead;   //0  当前没配置  
  373.     
  374.   //计算速率表  
  375.   //Pcell_log = -1  
  376.   //mtu = 0  
  377.   //linklayer = LINKLAYER_ETHERNET  
  378.   tc_calc_rtable(&p.peakrate, ptab, Pcell_log,   
  379.   mtu, linklayer)  
  380.   if (mtu == 0)  
  381.   mtu = 2047;  
  382.   
  383.   //根据最大传输单元计算需要多少槽位  
  384.   //我理解是不可能每个字节都有准确速  
  385.   //率,所以划定字节范围,从多少字节到  
  386.   //多少字节的速率相同。  
  387.   if (cell_log < 0)  
  388.   cell_log = 0;  
  389.   while ((mtu >> cell_log) > 255)  
  390.   cell_log++;  
  391.     
  392.   for (i=0; i<256; i++)  
  393.   //校正当前槽位的字节大小。这个算  
  394.   //法比较简单,当前链路类型为以太  
  395.   //网,则包根据原值处理,不会影响  
  396.   //包大小。mpu为最小包大小,如果  
  397.   //槽位字节小于mpu,则校正为mpu  
  398.   //的值。  
  399.   sz = tc_adjust_size((i + 1) << cell_log,   
  400.   mpu, linklayer);  
  401.     
  402.   //根据当前槽位字节大小,及用户  
  403.   //配置的速率,计算当前槽位所需  
  404.   //ticket时间  
  405.   rtab[i] = tc_calc_xmittime(bps, sz);  
  406.     
  407.   r->cell_align=-1;  
  408.   r->cell_log=cell_log;  
  409.   r->linklayer = (linklayer &   
  410.   TC_LINKLAYER_MASK);  
  411.     
  412.   //计算单包的峰值,这里通过单包峰值的大小  
  413.   //转换成所需要的ticket时间  
  414.   p.burst = tc_calc_xmittime(p.rate.rate, buffer);  
  415.   
  416.   p.mtu = 0;  
  417.   
  418.   //在消息尾部增加TCA_FW_POLICE属性项  
  419.   addattr_l(n, MAX_MSG, tca_id, NULL, 0);  
  420.     
  421.   //在消息尾部增加TCA_POLICE_TBF属性项  
  422.   addattr_l(n, MAX_MSG, TCA_POLICE_TBF, &p,   
  423.   sizeof(p));  
  424.     
  425.   //在消息尾部增加TCA_POLICE_RATE属性项  
  426.   addattr_l(n, MAX_MSG, TCA_POLICE_RATE,   
  427.   rtab, 1024);  
  428.     
  429.   if (matches(*argv, "flowid") == 0)  
  430.   //handle = 0x0000001  
  431.   get_tc_classid(&handle, *argv)  
  432.     
  433.   //在消息尾部追加FW_CLASSID属性项  
  434.   //rta = NLMSG_TAIL(n);  
  435.   //rta->rta_type = type;    //TCA_FW_CLASSID  
  436.   //rta->rta_len = len;  
  437.   addattr_l(n, 4096, TCA_FW_CLASSID, &handle, 4);  
  438.     
  439.   //根据接口名获取接口索引  
  440.   if (d[0])  
  441.   idx = ll_name_to_index(d)  
  442.   req.t.tcm_ifindex = idx;  
  443.   
  444.   //给内核发送该netlink消息  
  445.   rtnl_talk(&rth, &req.n, 0, 0, NULL)  
  446.     
  447. rtnl_close(&rth);  
  448.   
  449. 2、内核层代码  
  450. 用户侧发出RTM_NEWTFILTER套接口消息后,在内核侧对应的处理回调函数为tc_ctl_tfilter,该函数是在tc_filter_init中初始化的。  
  451.   
  452. protocol = TC_H_MIN(t->tcm_info);    //ETH_P_IP  
  453. prio = TC_H_MAJ(t->tcm_info);        //50  
  454. nprio = prio;  
  455. parent = t->tcm_parent;              //0xFFFF0000  
  456.   
  457. dev = __dev_get_by_index(t->tcm_ifindex) //eth0设备对象  
  458.   
  459. //从设备的qdisc_list列表中查找排队规则,之前ingress排队规则已经加入到链表中,所以  
  460. //这里的q就等于ingress排队规则。  
  461. q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent))  
  462.   
  463. //前2个字节为排队规则索引,后2个字节为类索引,ingress类型是没无类的排队规则,  
  464. //该条件不是满足,此时cl变量取值为初始的0。  
  465. if (TC_H_MIN(parent))  
  466.   //不走此流程  
  467.   
  468. //ingress排队规则中对应的回调为ingress_find_tcf  
  469. //获取该排队规则中的过滤链表。  
  470. chain = cops->tcf_chain(q, cl);  
  471.   ingress_find_tcf  
  472.   ingress_qdisc_data *p = PRIV(sch);  
  473.   return &p->filter_list;  
  474.   
  475. //查找待插入的位置,优先级的值越小表示越高。  
  476. for (back = chain; (tp=*back) != NULL; back = &tp->next)  
  477.   if (tp->prio >= prio)  
  478.   if (tp->prio == prio)  
  479.   if (!nprio || (tp->protocol != protocol && protocol))  
  480.   goto errout;  
  481.   else  
  482.   tp = NULL;  
  483.   break;  
  484.   
  485. //新建过滤项  
  486. if (tp == NULL)  
  487.   //从tcf_proto_base链表中查找fw分类器,当前tp_ops为cls_fw_ops  
  488.   tp_ops = tcf_proto_lookup_ops(tca[TCA_KIND-1]);  
  489.   
  490.   tp->ops = tp_ops;          //cls_fw_ops  
  491.   tp->protocol = protocol;       //ETH_P_IP  
  492.   tp->prio = nprio;          //50  
  493.   tp->q = q;             //ingress类型排队规则  
  494.   tp->classify = tp_ops->classify;    //fw_classify  
  495.   tp->classid = parent;          //0xFFFF0000  
  496.     
  497.   //当前fw类的初始化回调为fw_init,该函数内容为空。  
  498.   tp_ops->init(tp)  
  499.     
  500.   //将当前过滤器加入到当前排队规则的过滤链表中  
  501.   tp->next = *back;  
  502.   *back = tp;  
  503.   
  504. //fw分类器的get回调为fw_get,这里tcm_handle是之前命令行的handle值,当前为1  
  505. tp->ops->get(tp, t->tcm_handle);  
  506.   //当前分类器还没有存储对应的处理handle,返回为0  
  507.   fw_get  
  508.   head = (struct fw_head*)tp->root;  
  509.   if (head == NULL)  
  510.   return 0;  
  511.     
  512. //fw分类器的change回调为  
  513. tp->ops->change(tp, cl, t->tcm_handle, tca, &fh);  
  514.   fw_change  
  515.   head = (struct fw_head*)tp->root;  
  516.     
  517.   opt = tca[TCA_OPTIONS-1]  
  518.     
  519.   //把OPTIONS之后的属性项全部复制到临时变量tb中。  
  520.   rtattr_parse_nested(tb, TCA_FW_MAX, opt)  
  521.   
  522.   //当前fw分类器还没有head  
  523.   if (head == NULL)  
  524.   u32 mask = 0xFFFFFFFF;  
  525.   head = kzalloc(sizeof(struct fw_head), GFP_KERNEL);  
  526.   head->mask = mask;         //0xFFFFFFFF  
  527.     
  528.   //将当前过滤对象的root指向新创建的head控制块  
  529.   tp->root = head;  
  530.   
  531.   f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL);  
  532.   f->id = handle;                    //1  
  533.     
  534.   //fw过滤对象属性修改  
  535.   fw_change_attrs(tp, f, tb, tca, base);  
  536.   //这里面有两个互斥的功能编译宏NET_CLS_ACT和  
  537.   //NET_CLS_POLICE,一些提示显示NET_CLS_POLICE已经不建议使用  
  538.   //,所以这里仅分析NET_CLS_ACT相关代码。  
  539.   tcf_exts_validate(tp, tb, tca[TCA_RATE-1], &e, &fw_ext_map);    
  540.   //当前命令行输入为police,走此流程  
  541.   if (map->police && tb[map->police-1])  
  542.   act = tcf_action_init_1(tb[map->police-1], rate_tlv, "police",  
  543.   TCA_ACT_NOREPLACE, TCA_ACT_BIND, &err);  
  544.   //查找已经注册的police动作模块,a_o为act_police_ops  
  545.   a_o = tc_lookup_action_n(act_name);  
  546.     
  547.   //police对象初始化,当前回调为tcf_act_police_locate  
  548.   a_o->init(rta, est, a, ovr, bind);  
  549.   tcf_act_police_locate  
  550.   //获取用户侧设置的TBF过滤规则参数  
  551.   parm = RTA_DATA(tb[TCA_POLICE_TBF-1]);  
  552.     
  553.   police = kzalloc(sizeof(*police), GFP_KERNEL);  
  554.     
  555.   police->tcf_refcnt = 1;  
  556.   spin_lock_init(&police->tcf_lock);  
  557.   police->tcf_stats_lock = &police->tcf_lock;  
  558.   police->tcf_bindcnt = 1;  
  559.     
  560.   if (parm->rate.rate)  
  561.   //将速率参数加入到qdisc_rtab_list速率链表中  
  562.   R_tab = qdisc_get_rtab(&parm->rate,   
  563.   tb[TCA_POLICE_RATE-1]);  
  564.     
  565.   //释放之前老的速率表对象  
  566.   qdisc_put_rtab(police->tcfp_R_tab);  
  567.     
  568.   //加载新的速率对象  
  569.   police->tcfp_R_tab = R_tab;  
  570.     
  571.   //数据包峰值、最大传送单元、动作类型  
  572.   police->tcfp_toks = police->tcfp_burst = parm->burst;  
  573.   police->tcfp_mtu = parm->mtu;  
  574.   police->tcf_action = parm->action;  
  575.     
  576.   //获取系统当前时间  
  577.   PSCHED_GET_TIME(police->tcfp_t_c);  
  578.     
  579.   //生成新的策略对象索引  
  580.   police->tcf_index =   
  581.   tcf_hash_new_index(&police_idx_gen,   
  582.   &police_hash_info);  
  583.     
  584.   //这里tcf_next是一个宏  
  585.   //#define tcf_next    common.tcfc_next  
  586.   //将策略对象与tcf_police_ht互相引用。  
  587.   h = tcf_hash(police->tcf_index, POL_TAB_MASK);  
  588.   police->tcf_next = tcf_police_ht[h];  
  589.   tcf_police_ht[h] = &police->common;  
  590.     
  591.   //新建的策略对象关联到action对象的私有数据  
  592.   a->priv = police;  
  593.     
  594.   a->ops = a_o;  //act->ops指向act_police_ops  
  595.     
  596.   //动作对象类型  
  597.   act->type = TCA_OLD_COMPAT;  
  598.     
  599.   //构建好的动作对象先临时存于临时变量exts中,后面在  
  600.   //tcf_exts_change函数中会把act存入fw过滤对象的  
  601.   //exts.police中,使各fw过滤对象关联该动作。  
  602.   exts->action = act;  
  603.     
  604.   if (tb[TCA_FW_CLASSID-1])  
  605.   //classid = 0x00000001  
  606.   f->res.classid = *(u32*)RTA_DATA(tb[TCA_FW_CLASSID-1]);  
  607.     
  608.   //将该过滤对象与类绑定  
  609.   tcf_bind_filter(tp, &f->res, base);  
  610.   //调用排队规则对象的ops->cl_ops->bind_tcf,当前排队规则  
  611.   //为ingress,对应回调为ingress_bind_filter  
  612.   cl = tp->q->ops->cl_ops->bind_tcf(tp->q, base, r->classid);  
  613.   ingress_bind_filter  
  614.   //0x0001 + 1 = 2  
  615.   return ingress_get(sch, classid);  
  616.     
  617.   //r->class = cl   //2  
  618.   //同时返回值为r->class原来的值  
  619.   cl = cls_set_class(tp, &r->class, cl);  
  620.   old_cl = __cls_set_class(clp, cl);  
  621.   old_cl = *clp;  
  622.   *clp = cl;  
  623.   return old_cl;  
  624.   return old_cl;  
  625.   
  626.   //如果之前过滤对象与类有关联,则去除绑定  
  627.   if (cl)  
  628.   tp->q->ops->cl_ops->unbind_tcf(tp->q, cl);  
  629.     
  630.   //这里传参e是在上面tcf_exts_validate中构造的扩展动作对象  
  631.   //将上面构造的扩展动作对象存储到过滤对象f->exts->action中,完成  
  632.   //过滤对象与动作对象的关联。  
  633.   tcf_exts_change(tp, &f->exts, &e);  
  634.     
  635.   //将fw过滤对象加入到head的hash链表中  
  636.   f->next = head->ht[fw_hash(handle)];  
  637.   head->ht[fw_hash(handle)] = f;  
  638.   
  639. //告知添加成功  
  640. tfilter_notify(skb, n, tp, fh, RTM_NEWTFILTER);  
  641.   
  642. <strong>七、收包处理(NET_CLS_ACT开启)</strong>  
  643. 详细收包流程参见《网卡驱动收包》,这里仅分析在收包处理流程中涉及入口排队规则的代码。  
  644.   
  645. 网卡驱动调用netif_receive_skb来进行收包处理。  
  646. netif_receive_skb  
  647.   ......  
  648.   
  649.   #ifdef CONFIG_NET_CLS_ACT  
  650.   //查看代码发现在IFB接口设备中会设置NCLS值,不再需要进行入口排队规则处理,  
  651.   //直接跳过到ncls标记处。  
  652.   if (skb->tc_verd & TC_NCLS)  
  653.   skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);  
  654.   goto ncls;  
  655.   #endif  
  656.   
  657.   ......  
  658.     
  659.   #ifdef CONFIG_NET_CLS_ACT  
  660.     
  661.   //设置OK2MUNGE标记,查看代码,当前仅在pedit类型的动作中触发,如果没有此  
  662.   //标记,则pedit类型动作在进行处理时,需要复制一次skb。  
  663.   skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd);  
  664.     
  665.   //进行过滤  
  666.   ret = ing_filter(skb);  
  667.   result = TC_ACT_OK;  
  668.     
  669.   //如果用户向该接口配置了ingress排队规则,则此条件成功,否则返回默认OK,  
  670.   //让进来的报文直接向下继续处理。  
  671.   if (dev->qdisc_ingress)  
  672.   //如果出现报文多次内部环回,则将报文丢弃。  
  673.   __u32 ttl = (__u32) G_TC_RTTL(skb->tc_verd);  
  674.   if (MAX_RED_LOOP < ttl++)  
  675.   return TC_ACT_SHOT;  
  676.     
  677.   //更新报文环回值  
  678.   skb->tc_verd = SET_TC_RTTL(skb->tc_verd,ttl);  
  679.     
  680.   //标记报文是接收方向  
  681.   skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_INGRESS);  
  682.     
  683.   //调用当前排队规则的入队处理回调,当前ingress的回调为ingress_enqueue  
  684.   result = q->enqueue(skb, q)  
  685.   ingress_enqueue  
  686.   //使用过滤器进行分类处理  
  687.   result = tc_classify(skb, p->filter_list, &res);  
  688.   __be16 protocol = skb->protocol;  
  689.   protocol = skb->protocol;  
  690.     
  691.   reclassify:  
  692.     
  693.   //遍历所有过滤器,当前例子仅注册了一个fw过滤器  
  694.   for ( ; tp; tp = tp->next)  
  695.   //先进行协议匹配,如果之前用户配置过滤器未设置协议参  
  696.   //数,则protocol为ETH_P_ALL表示匹配任意协议。  
  697.   //之后使用当前过滤器的classify回调进行处理。  
  698.   //当前fw过滤器的回调为fw_classify,下面单独分析。  
  699.   if ((tp->protocol == protocol ||  
  700.   tp->protocol == __constant_htons(ETH_P_ALL))  
  701.   && (err = tp->classify(skb, tp, res)) >= 0)  
  702.   #ifdef CONFIG_NET_CLS_ACT  
  703.   //如果过滤结果为需要重分类,则继续尝试重分类,当  
  704.   //然也需要对重分类次数进行限制。  
  705.   if ( TC_ACT_RECLASSIFY == err)  
  706.   __u32 verd = (__u32) G_TC_VERD(skb->tc_verd);  
  707.   if (MAX_REC_LOOP < verd++)  
  708.   return TC_ACT_SHOT;  
  709.   
  710.   skb->tc_verd = SET_TC_VERD(skb->tc_verd,verd);  
  711.   goto reclassify;  
  712.   //其它过滤结果则直接返回,同时清除用于重分类限制  
  713.   //的VERD标记。  
  714.   else  
  715.   if (skb->tc_verd)  
  716.   skb->tc_verd =   
  717.   SET_TC_VERD(skb->tc_verd,0);  
  718.   return err;  
  719.   #endif  
  720.   return -1;  
  721.     
  722.   #ifdef CONFIG_NET_CLS_ACT  
  723.   //处理包个数、字节数统计  
  724.   sch->bstats.packets++;  
  725.   sch->bstats.bytes += skb->len;  
  726.     
  727.   //根据过滤器结果进行返回值映射  
  728.   switch (result)  
  729.   case TC_ACT_SHOT:  
  730.   result = TC_ACT_SHOT;  
  731.   sch->qstats.drops++;  
  732.   case TC_ACT_STOLEN:  
  733.   case TC_ACT_QUEUED:  
  734.   result = TC_ACT_STOLEN;  
  735.   case TC_ACT_RECLASSIFY:  
  736.   case TC_ACT_OK:  
  737.   case TC_ACT_UNSPEC:  
  738.   default:  
  739.   skb->tc_index = TC_H_MIN(res.classid);  
  740.   result = TC_ACT_OK;  
  741.   return result;  
  742.   #endif  
  743.     
  744.   return result;  
  745.     
  746.   //根据过滤结果,决定把进来的包丢弃,还是继续处理。  
  747.   if (ret == TC_ACT_SHOT || (ret == TC_ACT_STOLEN))  
  748.   kfree_skb(skb);  
  749.   goto out;  
  750.     
  751.   skb->tc_verd = 0;  
  752.     
  753.   ncls:  
  754.   #endif  
  755.   
  756.   ......  
  757.   
  758. ---------------------------------------------------------------------------------------------------------------------  
  759. fw_classify  
  760.   //分类器头列表  
  761.   head = (struct fw_head*)tp->root;  
  762.   
  763.   //包的mark,该mark是由iptables或ebtables打上的标签,fw分类器主要用于和iptables、  
  764.   //ebtables配合来完成对特定包的匹配。  
  765.   id = skb->mark;  
  766.   
  767.   if (head != NULL)  
  768.   id &= head->mask;      //当前用例没有设置掩码,则完整匹配  
  769.     
  770.   //遍历满足该id的hash链表  
  771.   for (f=head->ht[fw_hash(id)]; f; f=f->next)  
  772.   //找到匹配的过滤对象  
  773.   if (f->id == id)  
  774.   *res = f->res;     //这里记载了该匹配对象绑定的类,参见命令分析  
  775.   
  776.   //进行过滤对象的扩展处理,当前例子扩展为速率的限制  
  777.   r = tcf_exts_exec(skb, &f->exts, res);  
  778. #ifdef CONFIG_NET_CLS_ACT  
  779.   return tcf_action_exec(skb, exts->action, res);  
  780.                     /查看代码发现在IFB接口设备中会设置NCLS值,不再需要进  
  781.   //行入口排队规则处理  
  782.   if (skb->tc_verd & TC_NCLS)  
  783.   skb->tc_verd = CLR_TC_NCLS(skb->tc_verd);  
  784.   ret = TC_ACT_OK;  
  785.   goto exec_done;  
  786.     
  787.   //遍历该过滤器下所有动作,当前实例仅一个限速  
  788.   while ((a = act) != NULL)  
  789. repeat:  
  790.   //进行当前动作的act回调处理,当前动作为policce,则对  
  791.   //应的回调为tcf_act_police,下面单独分析。  
  792.   ret = a->ops->act(skb, a, res);  
  793.     
  794.   //当前仅pedit动作对象会设置TC_MUNGED标记,当设置  
  795.   //了该标记后,则去除该标记,同时设置OK2MUNGE标记  
  796.   //后续在进行pedit时,已经复制过了,就不怕乱处理了?  
  797.   if (TC_MUNGED & skb->tc_verd)  
  798.   skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd);  
  799.   skb->tc_verd = CLR_TC_MUNGED(skb->tc_verd);  
  800.     
  801.   //重复处理当前动作  
  802.   if (ret == TC_ACT_REPEAT)  
  803.   goto repeat;  
  804.   
  805.   //当动作结果为非PIPE时,则处理完成返回结果,否则继  
  806.   //续使用下一个动作对象进行处理。  
  807.   if (ret != TC_ACT_PIPE)  
  808.   goto exec_done;  
  809.     
  810.   act = a->next;  
  811.   exec_done:  
  812.   return ret;  
  813. #endif  
  814.     
  815.   if (r < 0)  
  816.   continue;  
  817.   return r;  
  818.   
  819. ----------------------------------------------------------------------------------------------------------------------  
  820. tcf_act_police  
  821.   //统计  
  822.   police->tcf_bstats.bytes += skb->len;  
  823.   police->tcf_bstats.packets++;  
  824.   
  825.   //如果当前报文长度不大于用户设置的MTU值,则为合法条件  
  826.   if (skb->len <= police->tcfp_mtu)  
  827.   //获取当前系统时间  
  828.   PSCHED_GET_TIME(now);  
  829.     
  830.   //计算从上一次到现在经过的ticket值,该值不能超过用户设置的最大单包峰值。  
  831.   toks = PSCHED_TDIFF_SAFE(now, police->tcfp_t_c,police->tcfp_burst);  
  832.   //在经过一段时间流逝后,当前可以继续多处理一些数据包,这里将之前的值进行  
  833.   //累加补充,得到这个时间点可以处理的总的数据量(这里单位是tickt,是将根据  
  834.   //用户设置的速率进行的字节到ticket的转换,参见上面用户命令的分析可以更清  
  835.   //楚这里的转换机制)  
  836.   toks += police->tcfp_toks;  
  837.   
  838.   //将当前报文大小,根据用户侧的速率表进行计算得到在当前速率下这个大小的数  
  839.   //据需要消耗多少ticket  
  840.   toks -= L2T(police, skb->len);  
  841.     
  842.   //如果这次发包未达到用户设置的速率限制,则条件满足,此时记录当前剩余的  
  843.   //令牌值,并返回结果。这里的tcfp_result用户也可以在命令行自行设置,如果  
  844.   //用户没有设置则为0,对应的值为TC_ACT_OK  
  845.   if ((toks|ptoks) >= 0)  
  846.   police->tcfp_t_c = now;  
  847.   police->tcfp_toks = toks;  
  848.   police->tcfp_ptoks = ptoks;  
  849.   return police->tcfp_result;  
  850.     
  851.   //当前报文长度大于用户设置的MTU值,或者已经超过当前速率,执行用户设置的  
  852.   //动作,当前设置为drop,对应的动作值为TC_POLICE_SHOT,该报文被丢弃。  
  853.   police->tcf_qstats.overlimits++;  
  854.   return police->tcf_action;  
  855. <strong>  
  856. 八、收包处理(NET_CLS_ACT未开启)</strong>  
  857. 当用户没有开启NET_CLS_ACT宏时,入口排队规则的处理被延迟到netfilter架构的PRE_ROUTING链上,优先级在FILTER过滤之后,钩子回调函数为ing_hook。该钩子注册点详见上面“给接口设置ingress入口排队规则”。这里仅关注相关钩子函数,netfilter架构在网上有好多经验文章,这里不再对这块进行详细分析。  
  858.   
  859. ing_hook  
  860.   int fwres=NF_ACCEPT;  
  861.     
  862.   //如果用户设置入口排队规则,则使用该排队规则的回调enqueue进行入队列的分类处  
  863.   //理,否则直接通过。入队的处理在上面已经分析过了,这里不再重复。可以看到使用  
  864.   //NET_CLS_ACT机制和netfilter的机制来处理入口排队规则因为时间点不同,各有各  
  865.   //优缺点。NET_CLS_ACT机制是在包刚进来就进行QOS处理,必免了很多分支流程  
  866.   //的干扰,而使用netfilter的机制,可以让包先进行网桥处理、之后再进行iptables的  
  867.   //过滤链处理,之后才进行QOS处理,可以在进行QOS处理前做一些其它事情。  
  868.   if (dev->qdisc_ingress)  
  869.   fwres = q->enqueue(skb, q);  
  870.   
  871.   return fwres;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kubernetes Ingress是一个控制器,它允许以一致的方式暴露服务并将流量路由到不同的服务,它可以在同一个IP地址和同一个端口上处理多个规则,这些规则定义了如何将入站请求路由到不同的服务或URI路径。 以下是如何配置Ingress规则的步骤: 1. 确认是否已经安装和启用了Ingress控制器。使用kubectl命令来查看是否有ingress-controller pod运行。 2. 在kubernetes集群中创建一个Ingress对象。这一步需要指定Ingress对象的metadata和spec字段。metadata中必须要指定一个名称,spec中必须要指定规则列表(rules),每个规则中需要定义host和对应的http(s)路径。 例如: ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress spec: rules: - host: example.com http: paths: - path: /api/v1 pathType: Prefix backend: service: name: api-service port: name: http ``` - host:用于匹配请求的主机名。 - http:HTTP协议规则,还有一个TLS协议规则,需要使用secret配置。HTTP和TLS规则不能同时出现。 - paths:一组定义要匹配请求和转发服务的路径规则。 - path:用于匹配传入请求的路径前缀。 - backend:后端服务的配置。 3. 创建服务对象 ```yaml apiVersion: v1 kind: Service metadata: name: api-service namespace: default spec: selector: app: api ports: - name: http port: 80 targetPort: 8080 ``` - 应该根据实际情况配置targetPort和selector字段。targetPort是要暴露的端口,selector指定要将请求转发到哪个Pod。 4. 部署后端服务的Pod ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: api spec: selector: matchLabels: app: api template: metadata: labels: app: api spec: containers: - name: api image: my-api-image ports: - containerPort: 8080 ``` - deployment对象需要指定pod的镜像和容器端口。 5. 应用ingress和service和deployment. ```bash kubectl apply -f your-configuration.yaml ``` 这些规则定义了如何将入站http请求路由到不同的Kubernetes服务。可以使用类似于nginx-ingress等的Ingress控制器来处理这些规则并负载均衡流量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值