Commit 7960d1da authored by Jiri Pirko's avatar Jiri Pirko Committed by David S. Miller

net: sched: use block index as a handle instead of qdisc when block is shared

As the tcm_ifindex with value TCM_IFINDEX_MAGIC_BLOCK is invalid ifindex,
use it to indicate that we work with block, instead of qdisc.
So if tcm_ifindex is set to TCM_IFINDEX_MAGIC_BLOCK, tcm_parent is used
to carry block_index.

If the block is set to be shared between at least 2 qdiscs, it is
forbidden to use the qdisc handle to add/delete filters. In that case,
userspace has to pass block_index.

Also, for dump of the filters, in case the block is shared in between at
least 2 qdiscs, the each filter is dumped with tcm_ifindex value
TCM_IFINDEX_MAGIC_BLOCK and tcm_parent set to block_index. That gives
the user clear indication, that the filter belongs to a shared block
and not only to one qdisc under which it is dumped.
Suggested-by: default avatarDavid Ahern <dsahern@gmail.com>
Signed-off-by: default avatarJiri Pirko <jiri@mellanox.com>
Acked-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
Acked-by: default avatarDavid Ahern <dsahern@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent caa72601
...@@ -541,9 +541,19 @@ struct tcmsg { ...@@ -541,9 +541,19 @@ struct tcmsg {
int tcm_ifindex; int tcm_ifindex;
__u32 tcm_handle; __u32 tcm_handle;
__u32 tcm_parent; __u32 tcm_parent;
/* tcm_block_index is used instead of tcm_parent
* in case tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK
*/
#define tcm_block_index tcm_parent
__u32 tcm_info; __u32 tcm_info;
}; };
/* For manipulation of filters in shared block, tcm_ifindex is set to
* TCM_IFINDEX_MAGIC_BLOCK, and tcm_parent is aliased to tcm_block_index
* which is the block index.
*/
#define TCM_IFINDEX_MAGIC_BLOCK (0xFFFFFFFFU)
enum { enum {
TCA_UNSPEC, TCA_UNSPEC,
TCA_KIND, TCA_KIND,
......
...@@ -865,8 +865,9 @@ static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain, ...@@ -865,8 +865,9 @@ static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
} }
static int tcf_fill_node(struct net *net, struct sk_buff *skb, static int tcf_fill_node(struct net *net, struct sk_buff *skb,
struct tcf_proto *tp, struct Qdisc *q, u32 parent, struct tcf_proto *tp, struct tcf_block *block,
void *fh, u32 portid, u32 seq, u16 flags, int event) struct Qdisc *q, u32 parent, void *fh,
u32 portid, u32 seq, u16 flags, int event)
{ {
struct tcmsg *tcm; struct tcmsg *tcm;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
...@@ -879,8 +880,13 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb, ...@@ -879,8 +880,13 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb,
tcm->tcm_family = AF_UNSPEC; tcm->tcm_family = AF_UNSPEC;
tcm->tcm__pad1 = 0; tcm->tcm__pad1 = 0;
tcm->tcm__pad2 = 0; tcm->tcm__pad2 = 0;
tcm->tcm_ifindex = qdisc_dev(q)->ifindex; if (q) {
tcm->tcm_parent = parent; tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
tcm->tcm_parent = parent;
} else {
tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
tcm->tcm_block_index = block->index;
}
tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol); tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol);
if (nla_put_string(skb, TCA_KIND, tp->ops->kind)) if (nla_put_string(skb, TCA_KIND, tp->ops->kind))
goto nla_put_failure; goto nla_put_failure;
...@@ -903,8 +909,8 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb, ...@@ -903,8 +909,8 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb,
static int tfilter_notify(struct net *net, struct sk_buff *oskb, static int tfilter_notify(struct net *net, struct sk_buff *oskb,
struct nlmsghdr *n, struct tcf_proto *tp, struct nlmsghdr *n, struct tcf_proto *tp,
struct Qdisc *q, u32 parent, struct tcf_block *block, struct Qdisc *q,
void *fh, int event, bool unicast) u32 parent, void *fh, int event, bool unicast)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
...@@ -913,8 +919,8 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb, ...@@ -913,8 +919,8 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
if (!skb) if (!skb)
return -ENOBUFS; return -ENOBUFS;
if (tcf_fill_node(net, skb, tp, q, parent, fh, portid, n->nlmsg_seq, if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
n->nlmsg_flags, event) <= 0) { n->nlmsg_seq, n->nlmsg_flags, event) <= 0) {
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
...@@ -928,8 +934,8 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb, ...@@ -928,8 +934,8 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
struct nlmsghdr *n, struct tcf_proto *tp, struct nlmsghdr *n, struct tcf_proto *tp,
struct Qdisc *q, u32 parent, struct tcf_block *block, struct Qdisc *q,
void *fh, bool unicast, bool *last) u32 parent, void *fh, bool unicast, bool *last)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
...@@ -939,8 +945,8 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, ...@@ -939,8 +945,8 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
if (!skb) if (!skb)
return -ENOBUFS; return -ENOBUFS;
if (tcf_fill_node(net, skb, tp, q, parent, fh, portid, n->nlmsg_seq, if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
n->nlmsg_flags, RTM_DELTFILTER) <= 0) { n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER) <= 0) {
kfree_skb(skb); kfree_skb(skb);
return -EINVAL; return -EINVAL;
} }
...@@ -959,15 +965,16 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, ...@@ -959,15 +965,16 @@ static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
} }
static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
struct Qdisc *q, u32 parent, struct tcf_block *block, struct Qdisc *q,
struct nlmsghdr *n, u32 parent, struct nlmsghdr *n,
struct tcf_chain *chain, int event) struct tcf_chain *chain, int event)
{ {
struct tcf_proto *tp; struct tcf_proto *tp;
for (tp = rtnl_dereference(chain->filter_chain); for (tp = rtnl_dereference(chain->filter_chain);
tp; tp = rtnl_dereference(tp->next)) tp; tp = rtnl_dereference(tp->next))
tfilter_notify(net, oskb, n, tp, q, parent, 0, event, false); tfilter_notify(net, oskb, n, tp, block,
q, parent, 0, event, false);
} }
/* Add/change/delete/get a filter node */ /* Add/change/delete/get a filter node */
...@@ -983,13 +990,11 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -983,13 +990,11 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
bool prio_allocate; bool prio_allocate;
u32 parent; u32 parent;
u32 chain_index; u32 chain_index;
struct net_device *dev; struct Qdisc *q = NULL;
struct Qdisc *q;
struct tcf_chain_info chain_info; struct tcf_chain_info chain_info;
struct tcf_chain *chain = NULL; struct tcf_chain *chain = NULL;
struct tcf_block *block; struct tcf_block *block;
struct tcf_proto *tp; struct tcf_proto *tp;
const struct Qdisc_class_ops *cops;
unsigned long cl; unsigned long cl;
void *fh; void *fh;
int err; int err;
...@@ -1036,41 +1041,58 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -1036,41 +1041,58 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
/* Find head of filter chain. */ /* Find head of filter chain. */
/* Find link */ if (t->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
dev = __dev_get_by_index(net, t->tcm_ifindex); block = tcf_block_lookup(net, t->tcm_block_index);
if (dev == NULL) if (!block) {
return -ENODEV; NL_SET_ERR_MSG(extack, "Block of given index was not found");
err = -EINVAL;
/* Find qdisc */ goto errout;
if (!parent) { }
q = dev->qdisc;
parent = q->handle;
} else { } else {
q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent)); const struct Qdisc_class_ops *cops;
if (q == NULL) struct net_device *dev;
return -EINVAL;
}
/* Is it classful? */ /* Find link */
cops = q->ops->cl_ops; dev = __dev_get_by_index(net, t->tcm_ifindex);
if (!cops) if (!dev)
return -EINVAL; return -ENODEV;
if (!cops->tcf_block) /* Find qdisc */
return -EOPNOTSUPP; if (!parent) {
q = dev->qdisc;
parent = q->handle;
} else {
q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent));
if (!q)
return -EINVAL;
}
/* Do we search for filter, attached to class? */ /* Is it classful? */
if (TC_H_MIN(parent)) { cops = q->ops->cl_ops;
cl = cops->find(q, parent); if (!cops)
if (cl == 0) return -EINVAL;
return -ENOENT;
}
/* And the last stroke */ if (!cops->tcf_block)
block = cops->tcf_block(q, cl, extack); return -EOPNOTSUPP;
if (!block) {
err = -EINVAL; /* Do we search for filter, attached to class? */
goto errout; if (TC_H_MIN(parent)) {
cl = cops->find(q, parent);
if (cl == 0)
return -ENOENT;
}
/* And the last stroke */
block = cops->tcf_block(q, cl, extack);
if (!block) {
err = -EINVAL;
goto errout;
}
if (tcf_block_shared(block)) {
NL_SET_ERR_MSG(extack, "This filter block is shared. Please use the block index to manipulate the filters");
err = -EOPNOTSUPP;
goto errout;
}
} }
chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0; chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
...@@ -1086,7 +1108,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -1086,7 +1108,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
} }
if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) { if (n->nlmsg_type == RTM_DELTFILTER && prio == 0) {
tfilter_notify_chain(net, skb, q, parent, n, tfilter_notify_chain(net, skb, block, q, parent, n,
chain, RTM_DELTFILTER); chain, RTM_DELTFILTER);
tcf_chain_flush(chain); tcf_chain_flush(chain);
err = 0; err = 0;
...@@ -1134,7 +1156,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -1134,7 +1156,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
if (!fh) { if (!fh) {
if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) { if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) {
tcf_chain_tp_remove(chain, &chain_info, tp); tcf_chain_tp_remove(chain, &chain_info, tp);
tfilter_notify(net, skb, n, tp, q, parent, fh, tfilter_notify(net, skb, n, tp, block, q, parent, fh,
RTM_DELTFILTER, false); RTM_DELTFILTER, false);
tcf_proto_destroy(tp); tcf_proto_destroy(tp);
err = 0; err = 0;
...@@ -1159,8 +1181,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -1159,8 +1181,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
} }
break; break;
case RTM_DELTFILTER: case RTM_DELTFILTER:
err = tfilter_del_notify(net, skb, n, tp, q, parent, err = tfilter_del_notify(net, skb, n, tp, block,
fh, false, &last); q, parent, fh, false, &last);
if (err) if (err)
goto errout; goto errout;
if (last) { if (last) {
...@@ -1169,8 +1191,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -1169,8 +1191,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
} }
goto errout; goto errout;
case RTM_GETTFILTER: case RTM_GETTFILTER:
err = tfilter_notify(net, skb, n, tp, q, parent, fh, err = tfilter_notify(net, skb, n, tp, block, q, parent,
RTM_NEWTFILTER, true); fh, RTM_NEWTFILTER, true);
goto errout; goto errout;
default: default:
err = -EINVAL; err = -EINVAL;
...@@ -1183,7 +1205,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, ...@@ -1183,7 +1205,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
if (err == 0) { if (err == 0) {
if (tp_created) if (tp_created)
tcf_chain_tp_insert(chain, &chain_info, tp); tcf_chain_tp_insert(chain, &chain_info, tp);
tfilter_notify(net, skb, n, tp, q, parent, fh, tfilter_notify(net, skb, n, tp, block, q, parent, fh,
RTM_NEWTFILTER, false); RTM_NEWTFILTER, false);
} else { } else {
if (tp_created) if (tp_created)
...@@ -1203,6 +1225,7 @@ struct tcf_dump_args { ...@@ -1203,6 +1225,7 @@ struct tcf_dump_args {
struct tcf_walker w; struct tcf_walker w;
struct sk_buff *skb; struct sk_buff *skb;
struct netlink_callback *cb; struct netlink_callback *cb;
struct tcf_block *block;
struct Qdisc *q; struct Qdisc *q;
u32 parent; u32 parent;
}; };
...@@ -1212,7 +1235,7 @@ static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg) ...@@ -1212,7 +1235,7 @@ static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg)
struct tcf_dump_args *a = (void *)arg; struct tcf_dump_args *a = (void *)arg;
struct net *net = sock_net(a->skb->sk); struct net *net = sock_net(a->skb->sk);
return tcf_fill_node(net, a->skb, tp, a->q, a->parent, return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent,
n, NETLINK_CB(a->cb->skb).portid, n, NETLINK_CB(a->cb->skb).portid,
a->cb->nlh->nlmsg_seq, NLM_F_MULTI, a->cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWTFILTER); RTM_NEWTFILTER);
...@@ -1223,6 +1246,7 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, ...@@ -1223,6 +1246,7 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
long index_start, long *p_index) long index_start, long *p_index)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct tcf_block *block = chain->block;
struct tcmsg *tcm = nlmsg_data(cb->nlh); struct tcmsg *tcm = nlmsg_data(cb->nlh);
struct tcf_dump_args arg; struct tcf_dump_args arg;
struct tcf_proto *tp; struct tcf_proto *tp;
...@@ -1241,7 +1265,7 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, ...@@ -1241,7 +1265,7 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
memset(&cb->args[1], 0, memset(&cb->args[1], 0,
sizeof(cb->args) - sizeof(cb->args[0])); sizeof(cb->args) - sizeof(cb->args[0]));
if (cb->args[1] == 0) { if (cb->args[1] == 0) {
if (tcf_fill_node(net, skb, tp, q, parent, 0, if (tcf_fill_node(net, skb, tp, block, q, parent, 0,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh->nlmsg_seq, NLM_F_MULTI,
RTM_NEWTFILTER) <= 0) RTM_NEWTFILTER) <= 0)
...@@ -1254,6 +1278,7 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent, ...@@ -1254,6 +1278,7 @@ static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
arg.w.fn = tcf_node_dump; arg.w.fn = tcf_node_dump;
arg.skb = skb; arg.skb = skb;
arg.cb = cb; arg.cb = cb;
arg.block = block;
arg.q = q; arg.q = q;
arg.parent = parent; arg.parent = parent;
arg.w.stop = 0; arg.w.stop = 0;
...@@ -1272,13 +1297,10 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1272,13 +1297,10 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct nlattr *tca[TCA_MAX + 1]; struct nlattr *tca[TCA_MAX + 1];
struct net_device *dev; struct Qdisc *q = NULL;
struct Qdisc *q;
struct tcf_block *block; struct tcf_block *block;
struct tcf_chain *chain; struct tcf_chain *chain;
struct tcmsg *tcm = nlmsg_data(cb->nlh); struct tcmsg *tcm = nlmsg_data(cb->nlh);
unsigned long cl = 0;
const struct Qdisc_class_ops *cops;
long index_start; long index_start;
long index; long index;
u32 parent; u32 parent;
...@@ -1291,32 +1313,44 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -1291,32 +1313,44 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
if (err) if (err)
return err; return err;
dev = __dev_get_by_index(net, tcm->tcm_ifindex); if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
if (!dev) block = tcf_block_lookup(net, tcm->tcm_block_index);
return skb->len; if (!block)
goto out;
parent = tcm->tcm_parent;
if (!parent) {
q = dev->qdisc;
parent = q->handle;
} else { } else {
q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); const struct Qdisc_class_ops *cops;
} struct net_device *dev;
if (!q) unsigned long cl = 0;
goto out;
cops = q->ops->cl_ops; dev = __dev_get_by_index(net, tcm->tcm_ifindex);
if (!cops) if (!dev)
goto out; return skb->len;
if (!cops->tcf_block)
goto out; parent = tcm->tcm_parent;
if (TC_H_MIN(tcm->tcm_parent)) { if (!parent) {
cl = cops->find(q, tcm->tcm_parent); q = dev->qdisc;
if (cl == 0) parent = q->handle;
} else {
q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
}
if (!q)
goto out; goto out;
cops = q->ops->cl_ops;
if (!cops)
goto out;
if (!cops->tcf_block)
goto out;
if (TC_H_MIN(tcm->tcm_parent)) {
cl = cops->find(q, tcm->tcm_parent);
if (cl == 0)
goto out;
}
block = cops->tcf_block(q, cl, NULL);
if (!block)
goto out;
if (tcf_block_shared(block))
q = NULL;
} }
block = cops->tcf_block(q, cl, NULL);
if (!block)
goto out;
index_start = cb->args[0]; index_start = cb->args[0];
index = 0; index = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment