Skip to content

Commit 32a4f5e

Browse files
jpirkodavem330
authored andcommitted
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. Signed-off-by: Jiri Pirko <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f71e0ca commit 32a4f5e

File tree

4 files changed

+309
-9
lines changed

4 files changed

+309
-9
lines changed

include/net/sch_generic.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ struct tcf_chain {
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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,13 @@ enum {
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

Lines changed: 300 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -262,29 +262,57 @@ static void tcf_chain_hold(struct tcf_chain *chain)
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,
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 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
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)
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
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;

0 commit comments

Comments
 (0)