aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2014-01-25 08:03:51 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2014-02-06 05:46:06 -0500
commit0165d9325d6a3cf856e2cbbe64a0f4635ac75893 (patch)
tree0e33bf5e9bac4ac772b45d41899ca16024c2ff7e /net
parentb8ecbee67c732ef9fc47fcf50aed6b7bb6231d98 (diff)
netfilter: nf_tables: fix racy rule deletion
We may lost race if we flush the rule-set (which happens asynchronously via call_rcu) and we try to remove the table (that userspace assumes to be empty). Fix this by recovering synchronous rule and chain deletion. This was introduced time ago before we had no batch support, and synchronous rule deletion performance was not good. Now that we have the batch support, we can just postpone the purge of old rule in a second step in the commit phase. All object deletions are synchronous after this patch. As a side effect, we save memory as we don't need rcu_head per rule anymore. Cc: Patrick McHardy <kaber@trash.net> Reported-by: Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/nf_tables_api.c40
1 files changed, 23 insertions, 17 deletions
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 113c469c7579..3a2e4800b415 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -1008,10 +1008,8 @@ notify:
1008 return 0; 1008 return 0;
1009} 1009}
1010 1010
1011static void nf_tables_rcu_chain_destroy(struct rcu_head *head) 1011static void nf_tables_chain_destroy(struct nft_chain *chain)
1012{ 1012{
1013 struct nft_chain *chain = container_of(head, struct nft_chain, rcu_head);
1014
1015 BUG_ON(chain->use > 0); 1013 BUG_ON(chain->use > 0);
1016 1014
1017 if (chain->flags & NFT_BASE_CHAIN) { 1015 if (chain->flags & NFT_BASE_CHAIN) {
@@ -1059,7 +1057,9 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
1059 family); 1057 family);
1060 1058
1061 /* Make sure all rule references are gone before this is released */ 1059 /* Make sure all rule references are gone before this is released */
1062 call_rcu(&chain->rcu_head, nf_tables_rcu_chain_destroy); 1060 synchronize_rcu();
1061
1062 nf_tables_chain_destroy(chain);
1063 return 0; 1063 return 0;
1064} 1064}
1065 1065
@@ -1531,9 +1531,8 @@ err:
1531 return err; 1531 return err;
1532} 1532}
1533 1533
1534static void nf_tables_rcu_rule_destroy(struct rcu_head *head) 1534static void nf_tables_rule_destroy(struct nft_rule *rule)
1535{ 1535{
1536 struct nft_rule *rule = container_of(head, struct nft_rule, rcu_head);
1537 struct nft_expr *expr; 1536 struct nft_expr *expr;
1538 1537
1539 /* 1538 /*
@@ -1548,11 +1547,6 @@ static void nf_tables_rcu_rule_destroy(struct rcu_head *head)
1548 kfree(rule); 1547 kfree(rule);
1549} 1548}
1550 1549
1551static void nf_tables_rule_destroy(struct nft_rule *rule)
1552{
1553 call_rcu(&rule->rcu_head, nf_tables_rcu_rule_destroy);
1554}
1555
1556#define NFT_RULE_MAXEXPRS 128 1550#define NFT_RULE_MAXEXPRS 128
1557 1551
1558static struct nft_expr_info *info; 1552static struct nft_expr_info *info;
@@ -1819,9 +1813,6 @@ static int nf_tables_commit(struct sk_buff *skb)
1819 synchronize_rcu(); 1813 synchronize_rcu();
1820 1814
1821 list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { 1815 list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
1822 /* Delete this rule from the dirty list */
1823 list_del(&rupd->list);
1824
1825 /* This rule was inactive in the past and just became active. 1816 /* This rule was inactive in the past and just became active.
1826 * Clear the next bit of the genmask since its meaning has 1817 * Clear the next bit of the genmask since its meaning has
1827 * changed, now it is the future. 1818 * changed, now it is the future.
@@ -1832,6 +1823,7 @@ static int nf_tables_commit(struct sk_buff *skb)
1832 rupd->chain, rupd->rule, 1823 rupd->chain, rupd->rule,
1833 NFT_MSG_NEWRULE, 0, 1824 NFT_MSG_NEWRULE, 0,
1834 rupd->family); 1825 rupd->family);
1826 list_del(&rupd->list);
1835 kfree(rupd); 1827 kfree(rupd);
1836 continue; 1828 continue;
1837 } 1829 }
@@ -1841,7 +1833,15 @@ static int nf_tables_commit(struct sk_buff *skb)
1841 nf_tables_rule_notify(skb, rupd->nlh, rupd->table, rupd->chain, 1833 nf_tables_rule_notify(skb, rupd->nlh, rupd->table, rupd->chain,
1842 rupd->rule, NFT_MSG_DELRULE, 0, 1834 rupd->rule, NFT_MSG_DELRULE, 0,
1843 rupd->family); 1835 rupd->family);
1836 }
1837
1838 /* Make sure we don't see any packet traversing old rules */
1839 synchronize_rcu();
1840
1841 /* Now we can safely release unused old rules */
1842 list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
1844 nf_tables_rule_destroy(rupd->rule); 1843 nf_tables_rule_destroy(rupd->rule);
1844 list_del(&rupd->list);
1845 kfree(rupd); 1845 kfree(rupd);
1846 } 1846 }
1847 1847
@@ -1854,20 +1854,26 @@ static int nf_tables_abort(struct sk_buff *skb)
1854 struct nft_rule_trans *rupd, *tmp; 1854 struct nft_rule_trans *rupd, *tmp;
1855 1855
1856 list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { 1856 list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
1857 /* Delete all rules from the dirty list */
1858 list_del(&rupd->list);
1859
1860 if (!nft_rule_is_active_next(net, rupd->rule)) { 1857 if (!nft_rule_is_active_next(net, rupd->rule)) {
1861 nft_rule_clear(net, rupd->rule); 1858 nft_rule_clear(net, rupd->rule);
1859 list_del(&rupd->list);
1862 kfree(rupd); 1860 kfree(rupd);
1863 continue; 1861 continue;
1864 } 1862 }
1865 1863
1866 /* This rule is inactive, get rid of it */ 1864 /* This rule is inactive, get rid of it */
1867 list_del_rcu(&rupd->rule->list); 1865 list_del_rcu(&rupd->rule->list);
1866 }
1867
1868 /* Make sure we don't see any packet accessing aborted rules */
1869 synchronize_rcu();
1870
1871 list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
1868 nf_tables_rule_destroy(rupd->rule); 1872 nf_tables_rule_destroy(rupd->rule);
1873 list_del(&rupd->list);
1869 kfree(rupd); 1874 kfree(rupd);
1870 } 1875 }
1876
1871 return 0; 1877 return 0;
1872} 1878}
1873 1879