@@ -262,29 +262,57 @@ static void tcf_chain_hold(struct tcf_chain *chain)
262
262
++ chain -> refcnt ;
263
263
}
264
264
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 )
267
267
{
268
268
struct tcf_chain * chain ;
269
269
270
270
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 )
273
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
299
EXPORT_SYMBOL (tcf_chain_get );
280
300
281
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
305
tcf_chain_destroy (chain );
306
+ }
285
307
}
286
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
316
static bool tcf_block_offload_in_use (struct tcf_block * block )
289
317
{
290
318
return block -> offloadcnt ;
@@ -694,8 +722,10 @@ void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,
694
722
695
723
if (block -> refcnt == 1 ) {
696
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
727
tcf_chain_put (chain );
728
+ }
699
729
700
730
block -> refcnt -- ;
701
731
if (list_empty (& block -> chain_list ))
@@ -1609,6 +1639,264 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
1609
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
1900
void tcf_exts_destroy (struct tcf_exts * exts )
1613
1901
{
1614
1902
#ifdef CONFIG_NET_CLS_ACT
@@ -1825,6 +2113,10 @@ static int __init tc_filter_init(void)
1825
2113
rtnl_register (PF_UNSPEC , RTM_DELTFILTER , tc_del_tfilter , NULL , 0 );
1826
2114
rtnl_register (PF_UNSPEC , RTM_GETTFILTER , tc_get_tfilter ,
1827
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
2121
return 0 ;
1830
2122
0 commit comments