History

net: sched: introduce chain object to uapi

Allow user to create, destroy, get and dump chain objects. Do that by extending rtnl commands by the chain-specific ones. User will now be able to explicitly create or destroy chains (so far this was done only automatically according the filter/act needs and refcounting). Also, the user will receive notification about any chain creation or destuction.

Jiri Pirko committed 8 years ago

Commit 32a4f5e1 parent f71e0ca4 files · +309 −9Browse files

Unified · Split

4 files
  1. include/net/sch_generic.h +1 −0
  2. include/uapi/linux/rtnetlink.h +7 −0
  3. net/sched/cls_api.c +300 −8
  4. security/selinux/nlmsgtab.c +1 −1
include/net/sch_generic.h+1−0
@@ -304,6 +304,7 @@ struct tcf_chain { · 303 unmodified lines
304304 struct tcf_block *block;
305305 u32 index; /* chain index */
306306 unsigned int refcnt;
307+ bool explicitly_created;
307308 };
308309
309310 struct tcf_block {
include/uapi/linux/rtnetlink.h+7−0
@@ -150,6 +150,13 @@ enum { · 149 unmodified lines
150150 RTM_NEWCACHEREPORT = 96,
151151 #define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT
152152
153+ RTM_NEWCHAIN = 100,
154+#define RTM_NEWCHAIN RTM_NEWCHAIN
155+ RTM_DELCHAIN,
156+#define RTM_DELCHAIN RTM_DELCHAIN
157+ RTM_GETCHAIN,
158+#define RTM_GETCHAIN RTM_GETCHAIN
159+
153160 __RTM_MAX,
154161 #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
155162 };
net/sched/cls_api.c+300−8
@@ -262,29 +262,57 @@ static void tcf_chain_hold(struct tcf_chain *chain) · 261 unmodified lines
262262 ++chain->refcnt;
263263 }
264264
265−struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index,
266− bool create)
265+static struct tcf_chain *tcf_chain_lookup(struct tcf_block *block,
266+ u32 chain_index)
267267 {
268268 struct tcf_chain *chain;
269269
270270 list_for_each_entry(chain, &block->chain_list, list) {
271− if (chain->index == chain_index) {
272− tcf_chain_hold(chain);
271+ if (chain->index == chain_index)
273272 return chain;
274− }
273+ }
274+ return NULL;
275+}
276+
277+static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
278+ u32 seq, u16 flags, int event, bool unicast);
279+
280+struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index,
281+ bool create)
282+{
283+ struct tcf_chain *chain = tcf_chain_lookup(block, chain_index);
284+
285+ if (chain) {
286+ tcf_chain_hold(chain);
287+ return chain;
275288 }
276289
277− return create ? tcf_chain_create(block, chain_index) : NULL;
290+ if (!create)
291+ return NULL;
292+ chain = tcf_chain_create(block, chain_index);
293+ if (!chain)
294+ return NULL;
295+ tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
296+ RTM_NEWCHAIN, false);
297+ return chain;
278298 }
279299 EXPORT_SYMBOL(tcf_chain_get);
280300
281301 void tcf_chain_put(struct tcf_chain *chain)
282302 {
283− if (--chain->refcnt == 0)
303+ if (--chain->refcnt == 0) {
304+ tc_chain_notify(chain, NULL, 0, 0, RTM_DELCHAIN, false);
284305 tcf_chain_destroy(chain);
306+ }
285307 }
286308 EXPORT_SYMBOL(tcf_chain_put);
287309
310+static void tcf_chain_put_explicitly_created(struct tcf_chain *chain)
311+{
312+ if (chain->explicitly_created)
313+ tcf_chain_put(chain);
314+}
315+
288316 static bool tcf_block_offload_in_use(struct tcf_block *block)
289317 {
290318 return block->offloadcnt;
@@ -694,8 +722,10 @@ void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, · 403 unmodified lines
694722
695723 if (block->refcnt == 1) {
696724 /* At this point, all the chains should have refcnt >= 1. */
697− list_for_each_entry_safe(chain, tmp, &block->chain_list, list)
725+ list_for_each_entry_safe(chain, tmp, &block->chain_list, list) {
726+ tcf_chain_put_explicitly_created(chain);
698727 tcf_chain_put(chain);
728+ }
699729
700730 block->refcnt--;
701731 if (list_empty(&block->chain_list))
@@ -1609,6 +1639,264 @@ out: · 907 unmodified lines
16091639 return skb->len;
16101640 }
16111641
1642+static int tc_chain_fill_node(struct tcf_chain *chain, struct net *net,
1643+ struct sk_buff *skb, struct tcf_block *block,
1644+ u32 portid, u32 seq, u16 flags, int event)
1645+{
1646+ unsigned char *b = skb_tail_pointer(skb);
1647+ struct nlmsghdr *nlh;
1648+ struct tcmsg *tcm;
1649+
1650+ nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
1651+ if (!nlh)
1652+ goto out_nlmsg_trim;
1653+ tcm = nlmsg_data(nlh);
1654+ tcm->tcm_family = AF_UNSPEC;
1655+ tcm->tcm__pad1 = 0;
1656+ tcm->tcm__pad2 = 0;
1657+ tcm->tcm_handle = 0;
1658+ if (block->q) {
1659+ tcm->tcm_ifindex = qdisc_dev(block->q)->ifindex;
1660+ tcm->tcm_parent = block->q->handle;
1661+ } else {
1662+ tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
1663+ tcm->tcm_block_index = block->index;
1664+ }
1665+
1666+ if (nla_put_u32(skb, TCA_CHAIN, chain->index))
1667+ goto nla_put_failure;
1668+
1669+ nlh->nlmsg_len = skb_tail_pointer(skb) - b;
1670+ return skb->len;
1671+
1672+out_nlmsg_trim:
1673+nla_put_failure:
1674+ nlmsg_trim(skb, b);
1675+ return -EMSGSIZE;
1676+}
1677+
1678+static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
1679+ u32 seq, u16 flags, int event, bool unicast)
1680+{
1681+ u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
1682+ struct tcf_block *block = chain->block;
1683+ struct net *net = block->net;
1684+ struct sk_buff *skb;
1685+
1686+ skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
1687+ if (!skb)
1688+ return -ENOBUFS;
1689+
1690+ if (tc_chain_fill_node(chain, net, skb, block, portid,
1691+ seq, flags, event) <= 0) {
1692+ kfree_skb(skb);
1693+ return -EINVAL;
1694+ }
1695+
1696+ if (unicast)
1697+ return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
1698+
1699+ return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO);
1700+}
1701+
1702+/* Add/delete/get a chain */
1703+
1704+static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n,
1705+ struct netlink_ext_ack *extack)
1706+{
1707+ struct net *net = sock_net(skb->sk);
1708+ struct nlattr *tca[TCA_MAX + 1];
1709+ struct tcmsg *t;
1710+ u32 parent;
1711+ u32 chain_index;
1712+ struct Qdisc *q = NULL;
1713+ struct tcf_chain *chain = NULL;
1714+ struct tcf_block *block;
1715+ unsigned long cl;
1716+ int err;
1717+
1718+ if (n->nlmsg_type != RTM_GETCHAIN &&
1719+ !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
1720+ return -EPERM;
1721+
1722+replay:
1723+ err = nlmsg_parse(n, sizeof(*t), tca, TCA_MAX, NULL, extack);
1724+ if (err < 0)
1725+ return err;
1726+
1727+ t = nlmsg_data(n);
1728+ parent = t->tcm_parent;
1729+ cl = 0;
1730+
1731+ block = tcf_block_find(net, &q, &parent, &cl,
1732+ t->tcm_ifindex, t->tcm_block_index, extack);
1733+ if (IS_ERR(block))
1734+ return PTR_ERR(block);
1735+
1736+ chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
1737+ if (chain_index > TC_ACT_EXT_VAL_MASK) {
1738+ NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
1739+ return -EINVAL;
1740+ }
1741+ chain = tcf_chain_lookup(block, chain_index);
1742+ if (n->nlmsg_type == RTM_NEWCHAIN) {
1743+ if (chain) {
1744+ NL_SET_ERR_MSG(extack, "Filter chain already exists");
1745+ return -EEXIST;
1746+ }
1747+ if (!(n->nlmsg_flags & NLM_F_CREATE)) {
1748+ NL_SET_ERR_MSG(extack, "Need both RTM_NEWCHAIN and NLM_F_CREATE to create a new chain");
1749+ return -ENOENT;
1750+ }
1751+ chain = tcf_chain_create(block, chain_index);
1752+ if (!chain) {
1753+ NL_SET_ERR_MSG(extack, "Failed to create filter chain");
1754+ return -ENOMEM;
1755+ }
1756+ } else {
1757+ if (!chain) {
1758+ NL_SET_ERR_MSG(extack, "Cannot find specified filter chain");
1759+ return -EINVAL;
1760+ }
1761+ tcf_chain_hold(chain);
1762+ }
1763+
1764+ switch (n->nlmsg_type) {
1765+ case RTM_NEWCHAIN:
1766+ /* In case the chain was successfully added, take a reference
1767+ * to the chain. This ensures that an empty chain
1768+ * does not disappear at the end of this function.
1769+ */
1770+ tcf_chain_hold(chain);
1771+ chain->explicitly_created = true;
1772+ tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
1773+ RTM_NEWCHAIN, false);
1774+ break;
1775+ case RTM_DELCHAIN:
1776+ /* Flush the chain first as the user requested chain removal. */
1777+ tcf_chain_flush(chain);
1778+ /* In case the chain was successfully deleted, put a reference
1779+ * to the chain previously taken during addition.
1780+ */
1781+ tcf_chain_put_explicitly_created(chain);
1782+ break;
1783+ case RTM_GETCHAIN:
1784+ break;
1785+ err = tc_chain_notify(chain, skb, n->nlmsg_seq,
1786+ n->nlmsg_seq, n->nlmsg_type, true);
1787+ if (err < 0)
1788+ NL_SET_ERR_MSG(extack, "Failed to send chain notify message");
1789+ break;
1790+ default:
1791+ err = -EOPNOTSUPP;
1792+ NL_SET_ERR_MSG(extack, "Unsupported message type");
1793+ goto errout;
1794+ }
1795+
1796+errout:
1797+ tcf_chain_put(chain);
1798+ if (err == -EAGAIN)
1799+ /* Replay the request. */
1800+ goto replay;
1801+ return err;
1802+}
1803+
1804+/* called with RTNL */
1805+static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
1806+{
1807+ struct net *net = sock_net(skb->sk);
1808+ struct nlattr *tca[TCA_MAX + 1];
1809+ struct Qdisc *q = NULL;
1810+ struct tcf_block *block;
1811+ struct tcf_chain *chain;
1812+ struct tcmsg *tcm = nlmsg_data(cb->nlh);
1813+ long index_start;
1814+ long index;
1815+ u32 parent;
1816+ int err;
1817+
1818+ if (nlmsg_len(cb->nlh) < sizeof(*tcm))
1819+ return skb->len;
1820+
1821+ err = nlmsg_parse(cb->nlh, sizeof(*tcm), tca, TCA_MAX, NULL, NULL);
1822+ if (err)
1823+ return err;
1824+
1825+ if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
1826+ block = tcf_block_lookup(net, tcm->tcm_block_index);
1827+ if (!block)
1828+ goto out;
1829+ /* If we work with block index, q is NULL and parent value
1830+ * will never be used in the following code. The check
1831+ * in tcf_fill_node prevents it. However, compiler does not
1832+ * see that far, so set parent to zero to silence the warning
1833+ * about parent being uninitialized.
1834+ */
1835+ parent = 0;
1836+ } else {
1837+ const struct Qdisc_class_ops *cops;
1838+ struct net_device *dev;
1839+ unsigned long cl = 0;
1840+
1841+ dev = __dev_get_by_index(net, tcm->tcm_ifindex);
1842+ if (!dev)
1843+ return skb->len;
1844+
1845+ parent = tcm->tcm_parent;
1846+ if (!parent) {
1847+ q = dev->qdisc;
1848+ parent = q->handle;
1849+ } else {
1850+ q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
1851+ }
1852+ if (!q)
1853+ goto out;
1854+ cops = q->ops->cl_ops;
1855+ if (!cops)
1856+ goto out;
1857+ if (!cops->tcf_block)
1858+ goto out;
1859+ if (TC_H_MIN(tcm->tcm_parent)) {
1860+ cl = cops->find(q, tcm->tcm_parent);
1861+ if (cl == 0)
1862+ goto out;
1863+ }
1864+ block = cops->tcf_block(q, cl, NULL);
1865+ if (!block)
1866+ goto out;
1867+ if (tcf_block_shared(block))
1868+ q = NULL;
1869+ }
1870+
1871+ index_start = cb->args[0];
1872+ index = 0;
1873+
1874+ list_for_each_entry(chain, &block->chain_list, list) {
1875+ if ((tca[TCA_CHAIN] &&
1876+ nla_get_u32(tca[TCA_CHAIN]) != chain->index))
1877+ continue;
1878+ if (index < index_start) {
1879+ index++;
1880+ continue;
1881+ }
1882+ err = tc_chain_fill_node(chain, net, skb, block,
1883+ NETLINK_CB(cb->skb).portid,
1884+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
1885+ RTM_NEWCHAIN);
1886+ if (err <= 0)
1887+ break;
1888+ index++;
1889+ }
1890+
1891+ cb->args[0] = index;
1892+
1893+out:
1894+ /* If we did no progress, the error (EMSGSIZE) is real */
1895+ if (skb->len == 0 && err)
1896+ return err;
1897+ return skb->len;
1898+}
1899+
16121900 void tcf_exts_destroy(struct tcf_exts *exts)
16131901 {
16141902 #ifdef CONFIG_NET_CLS_ACT
@@ -1825,6 +2113,10 @@ static int __init tc_filter_init(void) · 210 unmodified lines
18252113 rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL, 0);
18262114 rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter,
18272115 tc_dump_tfilter, 0);
2116+ rtnl_register(PF_UNSPEC, RTM_NEWCHAIN, tc_ctl_chain, NULL, 0);
2117+ rtnl_register(PF_UNSPEC, RTM_DELCHAIN, tc_ctl_chain, NULL, 0);
2118+ rtnl_register(PF_UNSPEC, RTM_GETCHAIN, tc_ctl_chain,
2119+ tc_dump_chain, 0);
18282120
18292121 return 0;
18302122
security/selinux/nlmsgtab.c+1−1
@@ -159,7 +159,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) · 158 unmodified lines
159159 switch (sclass) {
160160 case SECCLASS_NETLINK_ROUTE_SOCKET:
161161 /* RTM_MAX always point to RTM_SETxxxx, ie RTM_NEWxxx + 3 */
162− BUILD_BUG_ON(RTM_MAX != (RTM_NEWCACHEREPORT + 3));
162+ BUILD_BUG_ON(RTM_MAX != (RTM_NEWCHAIN + 3));
163163 err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
164164 sizeof(nlmsg_route_perms));
165165 break;

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t