aboutsummaryrefslogtreecommitdiffstats
path: root/net/sched/cls_api.c
diff options
context:
space:
mode:
authorJiri Pirko <jiri@mellanox.com>2018-07-23 03:23:06 -0400
committerDavid S. Miller <davem@davemloft.net>2018-07-23 23:44:12 -0400
commit32a4f5ecd7381f30ae3bb36dea77a150ba68af2e (patch)
treeb6d291d08192b09e78cd72791b53b53e3852af65 /net/sched/cls_api.c
parentf71e0ca4db187af7c44987e9d21e9042c3046070 (diff)
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 <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sched/cls_api.c')
-rw-r--r--net/sched/cls_api.c308
1 files changed, 300 insertions, 8 deletions
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index eb0bf9037ef9..e65b390336aa 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -262,29 +262,57 @@ static void tcf_chain_hold(struct tcf_chain *chain)
262 ++chain->refcnt; 262 ++chain->refcnt;
263} 263}
264 264
265struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, 265static struct tcf_chain *tcf_chain_lookup(struct tcf_block *block,
266 bool create) 266 u32 chain_index)
267{ 267{
268 struct tcf_chain *chain; 268 struct tcf_chain *chain;
269 269
270 list_for_each_entry(chain, &block->chain_list, list) { 270 list_for_each_entry(chain, &block->chain_list, list) {
271 if (chain->index == chain_index) { 271 if (chain->index == chain_index)
272 tcf_chain_hold(chain);
273 return chain; 272 return chain;
274 } 273 }
274 return NULL;
275}
276
277static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
278 u32 seq, u16 flags, int event, bool unicast);
279
280struct 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;
275 } 288 }
276 289
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;
278} 298}
279EXPORT_SYMBOL(tcf_chain_get); 299EXPORT_SYMBOL(tcf_chain_get);
280 300
281void tcf_chain_put(struct tcf_chain *chain) 301void tcf_chain_put(struct tcf_chain *chain)
282{ 302{
283 if (--chain->refcnt == 0) 303 if (--chain->refcnt == 0) {
304 tc_chain_notify(chain, NULL, 0, 0, RTM_DELCHAIN, false);
284 tcf_chain_destroy(chain); 305 tcf_chain_destroy(chain);
306 }
285} 307}
286EXPORT_SYMBOL(tcf_chain_put); 308EXPORT_SYMBOL(tcf_chain_put);
287 309
310static void tcf_chain_put_explicitly_created(struct tcf_chain *chain)
311{
312 if (chain->explicitly_created)
313 tcf_chain_put(chain);
314}
315
288static bool tcf_block_offload_in_use(struct tcf_block *block) 316static bool tcf_block_offload_in_use(struct tcf_block *block)
289{ 317{
290 return block->offloadcnt; 318 return block->offloadcnt;
@@ -694,8 +722,10 @@ void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,
694 722
695 if (block->refcnt == 1) { 723 if (block->refcnt == 1) {
696 /* At this point, all the chains should have refcnt >= 1. */ 724 /* 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);
698 tcf_chain_put(chain); 727 tcf_chain_put(chain);
728 }
699 729
700 block->refcnt--; 730 block->refcnt--;
701 if (list_empty(&block->chain_list)) 731 if (list_empty(&block->chain_list))
@@ -1609,6 +1639,264 @@ out:
1609 return skb->len; 1639 return skb->len;
1610} 1640}
1611 1641
1642static 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
1672out_nlmsg_trim:
1673nla_put_failure:
1674 nlmsg_trim(skb, b);
1675 return -EMSGSIZE;
1676}
1677
1678static 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
1704static 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
1722replay:
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
1796errout:
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 */
1805static 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
1893out:
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
1612void tcf_exts_destroy(struct tcf_exts *exts) 1900void tcf_exts_destroy(struct tcf_exts *exts)
1613{ 1901{
1614#ifdef CONFIG_NET_CLS_ACT 1902#ifdef CONFIG_NET_CLS_ACT
@@ -1825,6 +2113,10 @@ static int __init tc_filter_init(void)
1825 rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL, 0); 2113 rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL, 0);
1826 rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter, 2114 rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter,
1827 tc_dump_tfilter, 0); 2115 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);
1828 2120
1829 return 0; 2121 return 0;
1830 2122