| @@ -262,29 +262,57 @@ static void tcf_chain_hold(struct tcf_chain *chain) · 261 unmodified lines |
| 262 | ++chain->refcnt; | 262 | ++chain->refcnt; |
| 263 | } | 263 | } |
| 264 | | 264 | |
| 265 | struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, | 265 | static 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 | |
| | 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; |
| 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 | } |
| 279 | EXPORT_SYMBOL(tcf_chain_get); | 299 | EXPORT_SYMBOL(tcf_chain_get); |
| 280 | | 300 | |
| 281 | void tcf_chain_put(struct tcf_chain *chain) | 301 | void 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 | } |
| 286 | EXPORT_SYMBOL(tcf_chain_put); | 308 | EXPORT_SYMBOL(tcf_chain_put); |
| 287 | | 309 | |
| | 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 | |
| 288 | static bool tcf_block_offload_in_use(struct tcf_block *block) | 316 | static 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, · 403 unmodified lines |
| 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: · 907 unmodified lines |
| 1609 | return skb->len; | 1639 | return skb->len; |
| 1610 | } | 1640 | } |
| 1611 | | 1641 | |
| | 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 | |
| 1612 | void tcf_exts_destroy(struct tcf_exts *exts) | 1900 | void 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) · 210 unmodified lines |
| 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 | |