aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2014-07-01 05:49:18 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2014-07-14 05:20:45 -0400
commite688a7f8c6cb7a18aae7e55ccdd175f0ad9e69c0 (patch)
tree192a76d6a42b42d039b9def5b10ede0d02c3cbf4 /net
parent63283dd21ed2bf25a71909a820ed3e8fe412e15d (diff)
netfilter: nf_tables: safe RCU iteration on list when dumping
The dump operation through netlink is not protected by the nfnl_lock. Thus, a reader process can be dumping any of the existing object lists while another process can be updating the list content. This patch resolves this situation by protecting all the object lists with RCU in the netlink dump path which is the reader side. The updater path is already protected via nfnl_lock, so use list manipulation RCU-safe operations. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/nf_tables_api.c94
1 files changed, 53 insertions, 41 deletions
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index da5dc37a7402..a27a7c56e7c3 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -35,7 +35,7 @@ int nft_register_afinfo(struct net *net, struct nft_af_info *afi)
35{ 35{
36 INIT_LIST_HEAD(&afi->tables); 36 INIT_LIST_HEAD(&afi->tables);
37 nfnl_lock(NFNL_SUBSYS_NFTABLES); 37 nfnl_lock(NFNL_SUBSYS_NFTABLES);
38 list_add_tail(&afi->list, &net->nft.af_info); 38 list_add_tail_rcu(&afi->list, &net->nft.af_info);
39 nfnl_unlock(NFNL_SUBSYS_NFTABLES); 39 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
40 return 0; 40 return 0;
41} 41}
@@ -51,7 +51,7 @@ EXPORT_SYMBOL_GPL(nft_register_afinfo);
51void nft_unregister_afinfo(struct nft_af_info *afi) 51void nft_unregister_afinfo(struct nft_af_info *afi)
52{ 52{
53 nfnl_lock(NFNL_SUBSYS_NFTABLES); 53 nfnl_lock(NFNL_SUBSYS_NFTABLES);
54 list_del(&afi->list); 54 list_del_rcu(&afi->list);
55 nfnl_unlock(NFNL_SUBSYS_NFTABLES); 55 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
56} 56}
57EXPORT_SYMBOL_GPL(nft_unregister_afinfo); 57EXPORT_SYMBOL_GPL(nft_unregister_afinfo);
@@ -277,11 +277,12 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
277 struct net *net = sock_net(skb->sk); 277 struct net *net = sock_net(skb->sk);
278 int family = nfmsg->nfgen_family; 278 int family = nfmsg->nfgen_family;
279 279
280 list_for_each_entry(afi, &net->nft.af_info, list) { 280 rcu_read_lock();
281 list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
281 if (family != NFPROTO_UNSPEC && family != afi->family) 282 if (family != NFPROTO_UNSPEC && family != afi->family)
282 continue; 283 continue;
283 284
284 list_for_each_entry(table, &afi->tables, list) { 285 list_for_each_entry_rcu(table, &afi->tables, list) {
285 if (idx < s_idx) 286 if (idx < s_idx)
286 goto cont; 287 goto cont;
287 if (idx > s_idx) 288 if (idx > s_idx)
@@ -299,6 +300,7 @@ cont:
299 } 300 }
300 } 301 }
301done: 302done:
303 rcu_read_unlock();
302 cb->args[0] = idx; 304 cb->args[0] = idx;
303 return skb->len; 305 return skb->len;
304} 306}
@@ -517,7 +519,7 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
517 module_put(afi->owner); 519 module_put(afi->owner);
518 return err; 520 return err;
519 } 521 }
520 list_add_tail(&table->list, &afi->tables); 522 list_add_tail_rcu(&table->list, &afi->tables);
521 return 0; 523 return 0;
522} 524}
523 525
@@ -549,7 +551,7 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
549 if (err < 0) 551 if (err < 0)
550 return err; 552 return err;
551 553
552 list_del(&table->list); 554 list_del_rcu(&table->list);
553 return 0; 555 return 0;
554} 556}
555 557
@@ -764,12 +766,13 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
764 struct net *net = sock_net(skb->sk); 766 struct net *net = sock_net(skb->sk);
765 int family = nfmsg->nfgen_family; 767 int family = nfmsg->nfgen_family;
766 768
767 list_for_each_entry(afi, &net->nft.af_info, list) { 769 rcu_read_lock();
770 list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
768 if (family != NFPROTO_UNSPEC && family != afi->family) 771 if (family != NFPROTO_UNSPEC && family != afi->family)
769 continue; 772 continue;
770 773
771 list_for_each_entry(table, &afi->tables, list) { 774 list_for_each_entry_rcu(table, &afi->tables, list) {
772 list_for_each_entry(chain, &table->chains, list) { 775 list_for_each_entry_rcu(chain, &table->chains, list) {
773 if (idx < s_idx) 776 if (idx < s_idx)
774 goto cont; 777 goto cont;
775 if (idx > s_idx) 778 if (idx > s_idx)
@@ -787,11 +790,11 @@ cont:
787 } 790 }
788 } 791 }
789done: 792done:
793 rcu_read_unlock();
790 cb->args[0] = idx; 794 cb->args[0] = idx;
791 return skb->len; 795 return skb->len;
792} 796}
793 797
794
795static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb, 798static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
796 const struct nlmsghdr *nlh, 799 const struct nlmsghdr *nlh,
797 const struct nlattr * const nla[]) 800 const struct nlattr * const nla[])
@@ -1133,7 +1136,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
1133 goto err2; 1136 goto err2;
1134 1137
1135 table->use++; 1138 table->use++;
1136 list_add_tail(&chain->list, &table->chains); 1139 list_add_tail_rcu(&chain->list, &table->chains);
1137 return 0; 1140 return 0;
1138err2: 1141err2:
1139 if (!(table->flags & NFT_TABLE_F_DORMANT) && 1142 if (!(table->flags & NFT_TABLE_F_DORMANT) &&
@@ -1183,7 +1186,7 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
1183 return err; 1186 return err;
1184 1187
1185 table->use--; 1188 table->use--;
1186 list_del(&chain->list); 1189 list_del_rcu(&chain->list);
1187 return 0; 1190 return 0;
1188} 1191}
1189 1192
@@ -1202,9 +1205,9 @@ int nft_register_expr(struct nft_expr_type *type)
1202{ 1205{
1203 nfnl_lock(NFNL_SUBSYS_NFTABLES); 1206 nfnl_lock(NFNL_SUBSYS_NFTABLES);
1204 if (type->family == NFPROTO_UNSPEC) 1207 if (type->family == NFPROTO_UNSPEC)
1205 list_add_tail(&type->list, &nf_tables_expressions); 1208 list_add_tail_rcu(&type->list, &nf_tables_expressions);
1206 else 1209 else
1207 list_add(&type->list, &nf_tables_expressions); 1210 list_add_rcu(&type->list, &nf_tables_expressions);
1208 nfnl_unlock(NFNL_SUBSYS_NFTABLES); 1211 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1209 return 0; 1212 return 0;
1210} 1213}
@@ -1219,7 +1222,7 @@ EXPORT_SYMBOL_GPL(nft_register_expr);
1219void nft_unregister_expr(struct nft_expr_type *type) 1222void nft_unregister_expr(struct nft_expr_type *type)
1220{ 1223{
1221 nfnl_lock(NFNL_SUBSYS_NFTABLES); 1224 nfnl_lock(NFNL_SUBSYS_NFTABLES);
1222 list_del(&type->list); 1225 list_del_rcu(&type->list);
1223 nfnl_unlock(NFNL_SUBSYS_NFTABLES); 1226 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1224} 1227}
1225EXPORT_SYMBOL_GPL(nft_unregister_expr); 1228EXPORT_SYMBOL_GPL(nft_unregister_expr);
@@ -1555,13 +1558,14 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
1555 u8 genctr = ACCESS_ONCE(net->nft.genctr); 1558 u8 genctr = ACCESS_ONCE(net->nft.genctr);
1556 u8 gencursor = ACCESS_ONCE(net->nft.gencursor); 1559 u8 gencursor = ACCESS_ONCE(net->nft.gencursor);
1557 1560
1558 list_for_each_entry(afi, &net->nft.af_info, list) { 1561 rcu_read_lock();
1562 list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
1559 if (family != NFPROTO_UNSPEC && family != afi->family) 1563 if (family != NFPROTO_UNSPEC && family != afi->family)
1560 continue; 1564 continue;
1561 1565
1562 list_for_each_entry(table, &afi->tables, list) { 1566 list_for_each_entry_rcu(table, &afi->tables, list) {
1563 list_for_each_entry(chain, &table->chains, list) { 1567 list_for_each_entry_rcu(chain, &table->chains, list) {
1564 list_for_each_entry(rule, &chain->rules, list) { 1568 list_for_each_entry_rcu(rule, &chain->rules, list) {
1565 if (!nft_rule_is_active(net, rule)) 1569 if (!nft_rule_is_active(net, rule))
1566 goto cont; 1570 goto cont;
1567 if (idx < s_idx) 1571 if (idx < s_idx)
@@ -1582,6 +1586,8 @@ cont:
1582 } 1586 }
1583 } 1587 }
1584done: 1588done:
1589 rcu_read_unlock();
1590
1585 /* Invalidate this dump, a transition to the new generation happened */ 1591 /* Invalidate this dump, a transition to the new generation happened */
1586 if (gencursor != net->nft.gencursor || genctr != net->nft.genctr) 1592 if (gencursor != net->nft.gencursor || genctr != net->nft.genctr)
1587 return -EBUSY; 1593 return -EBUSY;
@@ -1935,7 +1941,7 @@ static LIST_HEAD(nf_tables_set_ops);
1935int nft_register_set(struct nft_set_ops *ops) 1941int nft_register_set(struct nft_set_ops *ops)
1936{ 1942{
1937 nfnl_lock(NFNL_SUBSYS_NFTABLES); 1943 nfnl_lock(NFNL_SUBSYS_NFTABLES);
1938 list_add_tail(&ops->list, &nf_tables_set_ops); 1944 list_add_tail_rcu(&ops->list, &nf_tables_set_ops);
1939 nfnl_unlock(NFNL_SUBSYS_NFTABLES); 1945 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1940 return 0; 1946 return 0;
1941} 1947}
@@ -1944,7 +1950,7 @@ EXPORT_SYMBOL_GPL(nft_register_set);
1944void nft_unregister_set(struct nft_set_ops *ops) 1950void nft_unregister_set(struct nft_set_ops *ops)
1945{ 1951{
1946 nfnl_lock(NFNL_SUBSYS_NFTABLES); 1952 nfnl_lock(NFNL_SUBSYS_NFTABLES);
1947 list_del(&ops->list); 1953 list_del_rcu(&ops->list);
1948 nfnl_unlock(NFNL_SUBSYS_NFTABLES); 1954 nfnl_unlock(NFNL_SUBSYS_NFTABLES);
1949} 1955}
1950EXPORT_SYMBOL_GPL(nft_unregister_set); 1956EXPORT_SYMBOL_GPL(nft_unregister_set);
@@ -2237,7 +2243,8 @@ static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb,
2237 if (cb->args[1]) 2243 if (cb->args[1])
2238 return skb->len; 2244 return skb->len;
2239 2245
2240 list_for_each_entry(set, &ctx->table->sets, list) { 2246 rcu_read_lock();
2247 list_for_each_entry_rcu(set, &ctx->table->sets, list) {
2241 if (idx < s_idx) 2248 if (idx < s_idx)
2242 goto cont; 2249 goto cont;
2243 if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET, 2250 if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET,
@@ -2250,6 +2257,7 @@ cont:
2250 } 2257 }
2251 cb->args[1] = 1; 2258 cb->args[1] = 1;
2252done: 2259done:
2260 rcu_read_unlock();
2253 return skb->len; 2261 return skb->len;
2254} 2262}
2255 2263
@@ -2263,7 +2271,8 @@ static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb,
2263 if (cb->args[1]) 2271 if (cb->args[1])
2264 return skb->len; 2272 return skb->len;
2265 2273
2266 list_for_each_entry(table, &ctx->afi->tables, list) { 2274 rcu_read_lock();
2275 list_for_each_entry_rcu(table, &ctx->afi->tables, list) {
2267 if (cur_table) { 2276 if (cur_table) {
2268 if (cur_table != table) 2277 if (cur_table != table)
2269 continue; 2278 continue;
@@ -2272,7 +2281,7 @@ static int nf_tables_dump_sets_family(struct nft_ctx *ctx, struct sk_buff *skb,
2272 } 2281 }
2273 ctx->table = table; 2282 ctx->table = table;
2274 idx = 0; 2283 idx = 0;
2275 list_for_each_entry(set, &ctx->table->sets, list) { 2284 list_for_each_entry_rcu(set, &ctx->table->sets, list) {
2276 if (idx < s_idx) 2285 if (idx < s_idx)
2277 goto cont; 2286 goto cont;
2278 if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET, 2287 if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET,
@@ -2287,6 +2296,7 @@ cont:
2287 } 2296 }
2288 cb->args[1] = 1; 2297 cb->args[1] = 1;
2289done: 2298done:
2299 rcu_read_unlock();
2290 return skb->len; 2300 return skb->len;
2291} 2301}
2292 2302
@@ -2303,7 +2313,8 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
2303 if (cb->args[1]) 2313 if (cb->args[1])
2304 return skb->len; 2314 return skb->len;
2305 2315
2306 list_for_each_entry(afi, &net->nft.af_info, list) { 2316 rcu_read_lock();
2317 list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
2307 if (cur_family) { 2318 if (cur_family) {
2308 if (afi->family != cur_family) 2319 if (afi->family != cur_family)
2309 continue; 2320 continue;
@@ -2311,7 +2322,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
2311 cur_family = 0; 2322 cur_family = 0;
2312 } 2323 }
2313 2324
2314 list_for_each_entry(table, &afi->tables, list) { 2325 list_for_each_entry_rcu(table, &afi->tables, list) {
2315 if (cur_table) { 2326 if (cur_table) {
2316 if (cur_table != table) 2327 if (cur_table != table)
2317 continue; 2328 continue;
@@ -2322,7 +2333,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
2322 ctx->table = table; 2333 ctx->table = table;
2323 ctx->afi = afi; 2334 ctx->afi = afi;
2324 idx = 0; 2335 idx = 0;
2325 list_for_each_entry(set, &ctx->table->sets, list) { 2336 list_for_each_entry_rcu(set, &ctx->table->sets, list) {
2326 if (idx < s_idx) 2337 if (idx < s_idx)
2327 goto cont; 2338 goto cont;
2328 if (nf_tables_fill_set(skb, ctx, set, 2339 if (nf_tables_fill_set(skb, ctx, set,
@@ -2342,6 +2353,7 @@ cont:
2342 } 2353 }
2343 cb->args[1] = 1; 2354 cb->args[1] = 1;
2344done: 2355done:
2356 rcu_read_unlock();
2345 return skb->len; 2357 return skb->len;
2346} 2358}
2347 2359
@@ -2600,7 +2612,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
2600 if (err < 0) 2612 if (err < 0)
2601 goto err2; 2613 goto err2;
2602 2614
2603 list_add_tail(&set->list, &table->sets); 2615 list_add_tail_rcu(&set->list, &table->sets);
2604 table->use++; 2616 table->use++;
2605 return 0; 2617 return 0;
2606 2618
@@ -2620,7 +2632,7 @@ static void nft_set_destroy(struct nft_set *set)
2620 2632
2621static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) 2633static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
2622{ 2634{
2623 list_del(&set->list); 2635 list_del_rcu(&set->list);
2624 nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC); 2636 nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
2625 nft_set_destroy(set); 2637 nft_set_destroy(set);
2626} 2638}
@@ -2655,7 +2667,7 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
2655 if (err < 0) 2667 if (err < 0)
2656 return err; 2668 return err;
2657 2669
2658 list_del(&set->list); 2670 list_del_rcu(&set->list);
2659 ctx.table->use--; 2671 ctx.table->use--;
2660 return 0; 2672 return 0;
2661} 2673}
@@ -2707,14 +2719,14 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
2707 } 2719 }
2708bind: 2720bind:
2709 binding->chain = ctx->chain; 2721 binding->chain = ctx->chain;
2710 list_add_tail(&binding->list, &set->bindings); 2722 list_add_tail_rcu(&binding->list, &set->bindings);
2711 return 0; 2723 return 0;
2712} 2724}
2713 2725
2714void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, 2726void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
2715 struct nft_set_binding *binding) 2727 struct nft_set_binding *binding)
2716{ 2728{
2717 list_del(&binding->list); 2729 list_del_rcu(&binding->list);
2718 2730
2719 if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS && 2731 if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS &&
2720 !(set->flags & NFT_SET_INACTIVE)) 2732 !(set->flags & NFT_SET_INACTIVE))
@@ -3494,12 +3506,12 @@ static int nf_tables_abort(struct sk_buff *skb)
3494 } 3506 }
3495 nft_trans_destroy(trans); 3507 nft_trans_destroy(trans);
3496 } else { 3508 } else {
3497 list_del(&trans->ctx.table->list); 3509 list_del_rcu(&trans->ctx.table->list);
3498 } 3510 }
3499 break; 3511 break;
3500 case NFT_MSG_DELTABLE: 3512 case NFT_MSG_DELTABLE:
3501 list_add_tail(&trans->ctx.table->list, 3513 list_add_tail_rcu(&trans->ctx.table->list,
3502 &trans->ctx.afi->tables); 3514 &trans->ctx.afi->tables);
3503 nft_trans_destroy(trans); 3515 nft_trans_destroy(trans);
3504 break; 3516 break;
3505 case NFT_MSG_NEWCHAIN: 3517 case NFT_MSG_NEWCHAIN:
@@ -3510,7 +3522,7 @@ static int nf_tables_abort(struct sk_buff *skb)
3510 nft_trans_destroy(trans); 3522 nft_trans_destroy(trans);
3511 } else { 3523 } else {
3512 trans->ctx.table->use--; 3524 trans->ctx.table->use--;
3513 list_del(&trans->ctx.chain->list); 3525 list_del_rcu(&trans->ctx.chain->list);
3514 if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) && 3526 if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
3515 trans->ctx.chain->flags & NFT_BASE_CHAIN) { 3527 trans->ctx.chain->flags & NFT_BASE_CHAIN) {
3516 nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops, 3528 nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
@@ -3520,8 +3532,8 @@ static int nf_tables_abort(struct sk_buff *skb)
3520 break; 3532 break;
3521 case NFT_MSG_DELCHAIN: 3533 case NFT_MSG_DELCHAIN:
3522 trans->ctx.table->use++; 3534 trans->ctx.table->use++;
3523 list_add_tail(&trans->ctx.chain->list, 3535 list_add_tail_rcu(&trans->ctx.chain->list,
3524 &trans->ctx.table->chains); 3536 &trans->ctx.table->chains);
3525 nft_trans_destroy(trans); 3537 nft_trans_destroy(trans);
3526 break; 3538 break;
3527 case NFT_MSG_NEWRULE: 3539 case NFT_MSG_NEWRULE:
@@ -3535,12 +3547,12 @@ static int nf_tables_abort(struct sk_buff *skb)
3535 break; 3547 break;
3536 case NFT_MSG_NEWSET: 3548 case NFT_MSG_NEWSET:
3537 trans->ctx.table->use--; 3549 trans->ctx.table->use--;
3538 list_del(&nft_trans_set(trans)->list); 3550 list_del_rcu(&nft_trans_set(trans)->list);
3539 break; 3551 break;
3540 case NFT_MSG_DELSET: 3552 case NFT_MSG_DELSET:
3541 trans->ctx.table->use++; 3553 trans->ctx.table->use++;
3542 list_add_tail(&nft_trans_set(trans)->list, 3554 list_add_tail_rcu(&nft_trans_set(trans)->list,
3543 &trans->ctx.table->sets); 3555 &trans->ctx.table->sets);
3544 nft_trans_destroy(trans); 3556 nft_trans_destroy(trans);
3545 break; 3557 break;
3546 case NFT_MSG_NEWSETELEM: 3558 case NFT_MSG_NEWSETELEM: