📄 sch_api.c
字号:
if (!clid) return -EINVAL; if (q->handle == 0) return -ENOENT; if ((err = qdisc_graft(dev, p, clid, NULL, &q)) != 0) return err; if (q) { qdisc_notify(skb, n, clid, q, NULL); qdisc_lock_tree(dev); qdisc_destroy(q); qdisc_unlock_tree(dev); } } else { qdisc_notify(skb, n, clid, NULL, q); } return 0;}/* Create/change qdisc. */static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg){ struct tcmsg *tcm; struct rtattr **tca; struct net_device *dev; u32 clid; struct Qdisc *q, *p; int err;replay: /* Reinit, just in case something touches this. */ tcm = NLMSG_DATA(n); tca = arg; clid = tcm->tcm_parent; q = p = NULL; if ((dev = __dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL) return -ENODEV; if (clid) { if (clid != TC_H_ROOT) { if (clid != TC_H_INGRESS) { if ((p = qdisc_lookup(dev, TC_H_MAJ(clid))) == NULL) return -ENOENT; q = qdisc_leaf(p, clid); } else { /*ingress */ q = dev->qdisc_ingress; } } else { q = dev->qdisc_sleeping; } /* It may be default qdisc, ignore it */ if (q && q->handle == 0) q = NULL; if (!q || !tcm->tcm_handle || q->handle != tcm->tcm_handle) { if (tcm->tcm_handle) { if (q && !(n->nlmsg_flags&NLM_F_REPLACE)) return -EEXIST; if (TC_H_MIN(tcm->tcm_handle)) return -EINVAL; if ((q = qdisc_lookup(dev, tcm->tcm_handle)) == NULL) goto create_n_graft; if (n->nlmsg_flags&NLM_F_EXCL) return -EEXIST; if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id)) return -EINVAL; if (q == p || (p && check_loop(q, p, 0))) return -ELOOP; atomic_inc(&q->refcnt); goto graft; } else { if (q == NULL) goto create_n_graft; /* This magic test requires explanation. * * We know, that some child q is already * attached to this parent and have choice: * either to change it or to create/graft new one. * * 1. We are allowed to create/graft only * if CREATE and REPLACE flags are set. * * 2. If EXCL is set, requestor wanted to say, * that qdisc tcm_handle is not expected * to exist, so that we choose create/graft too. * * 3. The last case is when no flags are set. * Alas, it is sort of hole in API, we * cannot decide what to do unambiguously. * For now we select create/graft, if * user gave KIND, which does not match existing. */ if ((n->nlmsg_flags&NLM_F_CREATE) && (n->nlmsg_flags&NLM_F_REPLACE) && ((n->nlmsg_flags&NLM_F_EXCL) || (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id)))) goto create_n_graft; } } } else { if (!tcm->tcm_handle) return -EINVAL; q = qdisc_lookup(dev, tcm->tcm_handle); } /* Change qdisc parameters */ if (q == NULL) return -ENOENT; if (n->nlmsg_flags&NLM_F_EXCL) return -EEXIST; if (tca[TCA_KIND-1] && rtattr_strcmp(tca[TCA_KIND-1], q->ops->id)) return -EINVAL; err = qdisc_change(q, tca); if (err == 0) qdisc_notify(skb, n, clid, NULL, q); return err;create_n_graft: if (!(n->nlmsg_flags&NLM_F_CREATE)) return -ENOENT; if (clid == TC_H_INGRESS) q = qdisc_create(dev, tcm->tcm_parent, tcm->tcm_parent, tca, &err); else q = qdisc_create(dev, tcm->tcm_parent, tcm->tcm_handle, tca, &err); if (q == NULL) { if (err == -EAGAIN) goto replay; return err; }graft: if (1) { struct Qdisc *old_q = NULL; err = qdisc_graft(dev, p, clid, q, &old_q); if (err) { if (q) { qdisc_lock_tree(dev); qdisc_destroy(q); qdisc_unlock_tree(dev); } return err; } qdisc_notify(skb, n, clid, old_q, q); if (old_q) { qdisc_lock_tree(dev); qdisc_destroy(old_q); qdisc_unlock_tree(dev); } } return 0;}static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, u32 pid, u32 seq, u16 flags, int event){ struct tcmsg *tcm; struct nlmsghdr *nlh; unsigned char *b = skb_tail_pointer(skb); struct gnet_dump d; nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags); tcm = NLMSG_DATA(nlh); tcm->tcm_family = AF_UNSPEC; tcm->tcm__pad1 = 0; tcm->tcm__pad2 = 0; tcm->tcm_ifindex = q->dev->ifindex; tcm->tcm_parent = clid; tcm->tcm_handle = q->handle; tcm->tcm_info = atomic_read(&q->refcnt); RTA_PUT(skb, TCA_KIND, IFNAMSIZ, q->ops->id); if (q->ops->dump && q->ops->dump(q, skb) < 0) goto rtattr_failure; q->qstats.qlen = q->q.qlen; if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS, q->stats_lock, &d) < 0) goto rtattr_failure; if (q->ops->dump_stats && q->ops->dump_stats(q, &d) < 0) goto rtattr_failure; if (gnet_stats_copy_basic(&d, &q->bstats) < 0 || gnet_stats_copy_rate_est(&d, &q->rate_est) < 0 || gnet_stats_copy_queue(&d, &q->qstats) < 0) goto rtattr_failure; if (gnet_stats_finish_copy(&d) < 0) goto rtattr_failure; nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len;nlmsg_failure:rtattr_failure: nlmsg_trim(skb, b); return -1;}static int qdisc_notify(struct sk_buff *oskb, struct nlmsghdr *n, u32 clid, struct Qdisc *old, struct Qdisc *new){ struct sk_buff *skb; u32 pid = oskb ? NETLINK_CB(oskb).pid : 0; skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb) return -ENOBUFS; if (old && old->handle) { if (tc_fill_qdisc(skb, old, clid, pid, n->nlmsg_seq, 0, RTM_DELQDISC) < 0) goto err_out; } if (new) { if (tc_fill_qdisc(skb, new, clid, pid, n->nlmsg_seq, old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0) goto err_out; } if (skb->len) return rtnetlink_send(skb, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);err_out: kfree_skb(skb); return -EINVAL;}static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb){ int idx, q_idx; int s_idx, s_q_idx; struct net_device *dev; struct Qdisc *q; s_idx = cb->args[0]; s_q_idx = q_idx = cb->args[1]; read_lock(&dev_base_lock); idx = 0; for_each_netdev(&init_net, dev) { if (idx < s_idx) goto cont; if (idx > s_idx) s_q_idx = 0; q_idx = 0; list_for_each_entry(q, &dev->qdisc_list, list) { if (q_idx < s_q_idx) { q_idx++; continue; } if (tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) goto done; q_idx++; }cont: idx++; }done: read_unlock(&dev_base_lock); cb->args[0] = idx; cb->args[1] = q_idx; return skb->len;}/************************************************ * Traffic classes manipulation. * ************************************************/static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg){ struct tcmsg *tcm = NLMSG_DATA(n); struct rtattr **tca = arg; struct net_device *dev; struct Qdisc *q = NULL; struct Qdisc_class_ops *cops; unsigned long cl = 0; unsigned long new_cl; u32 pid = tcm->tcm_parent; u32 clid = tcm->tcm_handle; u32 qid = TC_H_MAJ(clid); int err; if ((dev = __dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL) return -ENODEV; /* parent == TC_H_UNSPEC - unspecified parent. parent == TC_H_ROOT - class is root, which has no parent. parent == X:0 - parent is root class. parent == X:Y - parent is a node in hierarchy. parent == 0:Y - parent is X:Y, where X:0 is qdisc. handle == 0:0 - generate handle from kernel pool. handle == 0:Y - class is X:Y, where X:0 is qdisc. handle == X:Y - clear. handle == X:0 - root class. */ /* Step 1. Determine qdisc handle X:0 */ if (pid != TC_H_ROOT) { u32 qid1 = TC_H_MAJ(pid); if (qid && qid1) { /* If both majors are known, they must be identical. */ if (qid != qid1) return -EINVAL; } else if (qid1) { qid = qid1; } else if (qid == 0) qid = dev->qdisc_sleeping->handle; /* Now qid is genuine qdisc handle consistent both with parent and child. TC_H_MAJ(pid) still may be unspecified, complete it now. */ if (pid) pid = TC_H_MAKE(qid, pid); } else { if (qid == 0) qid = dev->qdisc_sleeping->handle; } /* OK. Locate qdisc */ if ((q = qdisc_lookup(dev, qid)) == NULL) return -ENOENT; /* An check that it supports classes */ cops = q->ops->cl_ops; if (cops == NULL) return -EINVAL; /* Now try to get class */ if (clid == 0) { if (pid == TC_H_ROOT) clid = qid; } else clid = TC_H_MAKE(qid, clid); if (clid) cl = cops->get(q, clid); if (cl == 0) { err = -ENOENT; if (n->nlmsg_type != RTM_NEWTCLASS || !(n->nlmsg_flags&NLM_F_CREATE)) goto out; } else { switch (n->nlmsg_type) { case RTM_NEWTCLASS: err = -EEXIST; if (n->nlmsg_flags&NLM_F_EXCL) goto out; break; case RTM_DELTCLASS: err = cops->delete(q, cl); if (err == 0) tclass_notify(skb, n, q, cl, RTM_DELTCLASS); goto out; case RTM_GETTCLASS: err = tclass_notify(skb, n, q, cl, RTM_NEWTCLASS); goto out; default: err = -EINVAL; goto out; } } new_cl = cl; err = cops->change(q, clid, pid, tca, &new_cl); if (err == 0) tclass_notify(skb, n, q, new_cl, RTM_NEWTCLASS);out: if (cl) cops->put(q, cl); return err;}static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q, unsigned long cl, u32 pid, u32 seq, u16 flags, int event){ struct tcmsg *tcm; struct nlmsghdr *nlh; unsigned char *b = skb_tail_pointer(skb); struct gnet_dump d; struct Qdisc_class_ops *cl_ops = q->ops->cl_ops; nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*tcm), flags); tcm = NLMSG_DATA(nlh); tcm->tcm_family = AF_UNSPEC; tcm->tcm_ifindex = q->dev->ifindex; tcm->tcm_parent = q->handle; tcm->tcm_handle = q->handle; tcm->tcm_info = 0; RTA_PUT(skb, TCA_KIND, IFNAMSIZ, q->ops->id); if (cl_ops->dump && cl_ops->dump(q, cl, skb, tcm) < 0) goto rtattr_failure; if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS, q->stats_lock, &d) < 0) goto rtattr_failure; if (cl_ops->dump_stats && cl_ops->dump_stats(q, cl, &d) < 0) goto rtattr_failure; if (gnet_stats_finish_copy(&d) < 0) goto rtattr_failure; nlh->nlmsg_len = skb_tail_pointer(skb) - b; return skb->len;nlmsg_failure:rtattr_failure: nlmsg_trim(skb, b); return -1;}static int tclass_notify(struct sk_buff *oskb, struct nlmsghdr *n, struct Qdisc *q, unsigned long cl, int event){ struct sk_buff *skb; u32 pid = oskb ? NETLINK_CB(oskb).pid : 0; skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (!skb) return -ENOBUFS; if (tc_fill_tclass(skb, q, cl, pid, n->nlmsg_seq, 0, event) < 0) { kfree_skb(skb); return -EINVAL; } return rtnetlink_send(skb, pid, RTNLGRP_TC, n->nlmsg_flags&NLM_F_ECHO);}struct qdisc_dump_args{ struct qdisc_walker w; struct sk_buff *skb; struct netlink_callback *cb;};static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, struct qdisc_walker *arg){ struct qdisc_dump_args *a = (struct qdisc_dump_args *)arg; return tc_fill_tclass(a->skb, q, cl, NETLINK_CB(a->cb->skb).pid, a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTCLASS);}static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb){ int t; int s_t; struct net_device *dev; struct Qdisc *q; struct tcmsg *tcm = (struct tcmsg*)NLMSG_DATA(cb->nlh); struct qdisc_dump_args arg; if (cb->nlh->nlmsg_len < NLMSG_LENGTH(sizeof(*tcm))) return 0; if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL) return 0; s_t = cb->args[0]; t = 0; list_for_each_entry(q, &dev->qdisc_list, list) { if (t < s_t || !q->ops->cl_ops || (tcm->tcm_parent && TC_H_MAJ(tcm->tcm_parent) != q->handle)) { t++; continue; } if (t > s_t) memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0])); arg.w.fn = qdisc_class_dump; arg.skb = skb; arg.cb = cb; arg.w.stop = 0; arg.w.skip = cb->args[1]; arg.w.count = 0; q->ops->cl_ops->walk(q, &arg.w); cb->args[1] = arg.w.count; if (arg.w.stop) break; t++; } cb->args[0] = t; dev_put(dev); return skb->len;}/* Main classifier routine: scans classifier chain attached to this qdisc, (optionally) tests for protocol and asks specific classifiers. */int tc_classify_compat(struct sk_buff *skb, struct tcf_proto *tp, struct tcf_result *res){ __be16 protocol = skb->protocol; int err = 0; for (; tp; tp = tp->next) { if ((tp->protocol == protocol || tp->protocol == htons(ETH_P_ALL)) && (err = tp->classify(skb, tp, res)) >= 0) {#ifdef CONFIG_NET_CLS_ACT if (err != TC_ACT_RECLASSIFY && skb->tc_verd) skb->tc_verd = SET_TC_VERD(skb->tc_verd, 0);#endif return err; } } return -1;}EXPORT_SYMBOL(tc_classify_compat);int tc_classify(struct sk_buff *skb, struct tcf_proto *tp, struct tcf_result *res){ int err = 0; __be16 protocol;#ifdef CONFIG_NET_CLS_ACT struct tcf_proto *otp = tp;reclassify:#endif protocol = skb->protocol; err = tc_classify_compat(skb, tp, res);#ifdef CONFIG_NET_CLS_ACT if (err == TC_ACT_RECLASSIFY) { u32 verd = G_TC_VERD(skb->tc_verd); tp = otp; if (verd++ >= MAX_REC_LOOP) { printk("rule prio %u protocol %02x reclassify loop, " "packet dropped\n", tp->prio&0xffff, ntohs(tp->protocol)); return TC_ACT_SHOT; } skb->tc_verd = SET_TC_VERD(skb->tc_verd, verd); goto reclassify; }#endif return err;}EXPORT_SYMBOL(tc_classify);void tcf_destroy(struct tcf_proto *tp){ tp->ops->destroy(tp); module_put(tp->ops->owner); kfree(tp);}void tcf_destroy_chain(struct tcf_proto *fl){ struct tcf_proto *tp; while ((tp = fl) != NULL) { fl = tp->next; tcf_destroy(tp); }}EXPORT_SYMBOL(tcf_destroy_chain);#ifdef CONFIG_PROC_FSstatic int psched_show(struct seq_file *seq, void *v){ struct timespec ts; hrtimer_get_res(CLOCK_MONOTONIC, &ts); seq_printf(seq, "%08x %08x %08x %08x\n", (u32)NSEC_PER_USEC, (u32)PSCHED_US2NS(1), 1000000, (u32)NSEC_PER_SEC/(u32)ktime_to_ns(timespec_to_ktime(ts))); return 0;}static int psched_open(struct inode *inode, struct file *file){ return single_open(file, psched_show, PDE(inode)->data);}static const struct file_operations psched_fops = { .owner = THIS_MODULE, .open = psched_open, .read = seq_read, .llseek = seq_lseek, .release = single_release,};#endifstatic int __init pktsched_init(void){ register_qdisc(&pfifo_qdisc_ops); register_qdisc(&bfifo_qdisc_ops); proc_net_fops_create(&init_net, "psched", 0, &psched_fops); rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL); rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL); rtnl_register(PF_UNSPEC, RTM_GETQDISC, tc_get_qdisc, tc_dump_qdisc); rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL); rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL); rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass); return 0;}subsys_initcall(pktsched_init);EXPORT_SYMBOL(qdisc_get_rtab);EXPORT_SYMBOL(qdisc_put_rtab);EXPORT_SYMBOL(register_qdisc);EXPORT_SYMBOL(unregister_qdisc);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -