X-Git-Url: http://git.monstr.eu/?a=blobdiff_plain;f=net%2Fnetfilter%2Fnf_tables_api.c;h=8d5aa0ac45f4d3a22cdc3926df3c21722d6e35b9;hb=0c14846032f2c0a3b63234e1fc2759f4155b6067;hp=9a080767667b7ff70de644aa9aebea84ad54c2d2;hpb=ff6135959a9150ad45cb92ca38da270903a74343;p=linux-2.6-microblaze.git diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 9a080767667b..8d5aa0ac45f4 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -581,7 +581,8 @@ struct nft_module_request { }; #ifdef CONFIG_MODULES -static int nft_request_module(struct net *net, const char *fmt, ...) +static __printf(2, 3) int nft_request_module(struct net *net, const char *fmt, + ...) { char module_name[MODULE_NAME_LEN]; struct nft_module_request *req; @@ -1282,7 +1283,7 @@ static struct nft_chain *nft_chain_lookup(struct net *net, if (nla == NULL) return ERR_PTR(-EINVAL); - nla_strlcpy(search, nla, sizeof(search)); + nla_strscpy(search, nla, sizeof(search)); WARN_ON(!rcu_read_lock_held() && !lockdep_commit_lock_is_held(net)); @@ -1722,7 +1723,7 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net, goto err_hook_alloc; } - nla_strlcpy(ifname, attr, IFNAMSIZ); + nla_strscpy(ifname, attr, IFNAMSIZ); /* nf_tables_netdev_event() is called under rtnl_mutex, this is * indirectly serializing all the other holders of the commit_mutex with * the rtnl_mutex. @@ -3570,6 +3571,7 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { [NFTA_SET_OBJ_TYPE] = { .type = NLA_U32 }, [NFTA_SET_HANDLE] = { .type = NLA_U64 }, [NFTA_SET_EXPR] = { .type = NLA_NESTED }, + [NFTA_SET_EXPRESSIONS] = { .type = NLA_NESTED }, }; static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = { @@ -3777,6 +3779,7 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, u32 portid = ctx->portid; struct nlattr *nest; u32 seq = ctx->seq; + int i; event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), @@ -3845,11 +3848,22 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, nla_nest_end(skb, nest); - if (set->expr) { + if (set->num_exprs == 1) { nest = nla_nest_start_noflag(skb, NFTA_SET_EXPR); - if (nf_tables_fill_expr_info(skb, set->expr) < 0) + if (nf_tables_fill_expr_info(skb, set->exprs[0]) < 0) + goto nla_put_failure; + + nla_nest_end(skb, nest); + } else if (set->num_exprs > 1) { + nest = nla_nest_start_noflag(skb, NFTA_SET_EXPRESSIONS); + if (nest == NULL) goto nla_put_failure; + for (i = 0; i < set->num_exprs; i++) { + if (nft_expr_dump(skb, NFTA_LIST_ELEM, + set->exprs[i]) < 0) + goto nla_put_failure; + } nla_nest_end(skb, nest); } @@ -4219,7 +4233,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, return err; } - if (nla[NFTA_SET_EXPR]) + if (nla[NFTA_SET_EXPR] || nla[NFTA_SET_EXPRESSIONS]) desc.expr = true; table = nft_table_lookup(net, nla[NFTA_SET_TABLE], family, genmask); @@ -4283,6 +4297,31 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, err = PTR_ERR(expr); goto err_set_alloc_name; } + set->exprs[0] = expr; + set->num_exprs++; + } else if (nla[NFTA_SET_EXPRESSIONS]) { + struct nft_expr *expr; + struct nlattr *tmp; + int left; + + i = 0; + nla_for_each_nested(tmp, nla[NFTA_SET_EXPRESSIONS], left) { + if (i == NFT_SET_EXPR_MAX) { + err = -E2BIG; + goto err_set_init; + } + if (nla_type(tmp) != NFTA_LIST_ELEM) { + err = -EINVAL; + goto err_set_init; + } + expr = nft_set_elem_expr_alloc(&ctx, set, tmp); + if (IS_ERR(expr)) { + err = PTR_ERR(expr); + goto err_set_init; + } + set->exprs[i++] = expr; + set->num_exprs++; + } } udata = NULL; @@ -4300,7 +4339,6 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, set->dtype = dtype; set->objtype = objtype; set->dlen = desc.dlen; - set->expr = expr; set->flags = flags; set->size = desc.size; set->policy = policy; @@ -4329,8 +4367,8 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, err_set_trans: ops->destroy(set); err_set_init: - if (expr) - nft_expr_destroy(&ctx, expr); + for (i = 0; i < set->num_exprs; i++) + nft_expr_destroy(&ctx, set->exprs[i]); err_set_alloc_name: kfree(set->name); err_set_name: @@ -4340,11 +4378,13 @@ err_set_name: static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) { + int i; + if (WARN_ON(set->use > 0)) return; - if (set->expr) - nft_expr_destroy(ctx, set->expr); + for (i = 0; i < set->num_exprs; i++) + nft_expr_destroy(ctx, set->exprs[i]); set->ops->destroy(set); kfree(set->name); @@ -4497,8 +4537,8 @@ const struct nft_set_ext_type nft_set_ext_types[] = { [NFT_SET_EXT_DATA] = { .align = __alignof__(u32), }, - [NFT_SET_EXT_EXPR] = { - .align = __alignof__(struct nft_expr), + [NFT_SET_EXT_EXPRESSIONS] = { + .align = __alignof__(struct nft_set_elem_expr), }, [NFT_SET_EXT_OBJREF] = { .len = sizeof(struct nft_object *), @@ -4541,6 +4581,7 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = { [NFTA_SET_ELEM_OBJREF] = { .type = NLA_STRING, .len = NFT_OBJ_MAXNAMELEN - 1 }, [NFTA_SET_ELEM_KEY_END] = { .type = NLA_NESTED }, + [NFTA_SET_ELEM_EXPRESSIONS] = { .type = NLA_NESTED }, }; static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = { @@ -4574,6 +4615,43 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, struct net *net, return 0; } +static int nft_set_elem_expr_dump(struct sk_buff *skb, + const struct nft_set *set, + const struct nft_set_ext *ext) +{ + struct nft_set_elem_expr *elem_expr; + u32 size, num_exprs = 0; + struct nft_expr *expr; + struct nlattr *nest; + + elem_expr = nft_set_ext_expr(ext); + nft_setelem_expr_foreach(expr, elem_expr, size) + num_exprs++; + + if (num_exprs == 1) { + expr = nft_setelem_expr_at(elem_expr, 0); + if (nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, expr) < 0) + return -1; + + return 0; + } else if (num_exprs > 1) { + nest = nla_nest_start_noflag(skb, NFTA_SET_ELEM_EXPRESSIONS); + if (nest == NULL) + goto nla_put_failure; + + nft_setelem_expr_foreach(expr, elem_expr, size) { + expr = nft_setelem_expr_at(elem_expr, size); + if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr) < 0) + goto nla_put_failure; + } + nla_nest_end(skb, nest); + } + return 0; + +nla_put_failure: + return -1; +} + static int nf_tables_fill_setelem(struct sk_buff *skb, const struct nft_set *set, const struct nft_set_elem *elem) @@ -4601,8 +4679,8 @@ static int nf_tables_fill_setelem(struct sk_buff *skb, set->dlen) < 0) goto nla_put_failure; - if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR) && - nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, nft_set_ext_expr(ext)) < 0) + if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS) && + nft_set_elem_expr_dump(skb, set, ext)) goto nla_put_failure; if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) && @@ -5097,8 +5175,8 @@ void *nft_set_elem_init(const struct nft_set *set, return elem; } -static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx, - struct nft_expr *expr) +static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx, + struct nft_expr *expr) { if (expr->ops->destroy_clone) { expr->ops->destroy_clone(ctx, expr); @@ -5108,6 +5186,16 @@ static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx, } } +static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx, + struct nft_set_elem_expr *elem_expr) +{ + struct nft_expr *expr; + u32 size; + + nft_setelem_expr_foreach(expr, elem_expr, size) + __nft_set_elem_expr_destroy(ctx, expr); +} + void nft_set_elem_destroy(const struct nft_set *set, void *elem, bool destroy_expr) { @@ -5120,7 +5208,7 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem, nft_data_release(nft_set_ext_key(ext), NFT_DATA_VALUE); if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) nft_data_release(nft_set_ext_data(ext), set->dtype); - if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) + if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS)) nft_set_elem_expr_destroy(&ctx, nft_set_ext_expr(ext)); if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF)) @@ -5137,15 +5225,57 @@ static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, { struct nft_set_ext *ext = nft_set_elem_ext(set, elem); - if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) + if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS)) nft_set_elem_expr_destroy(ctx, nft_set_ext_expr(ext)); kfree(elem); } +static int nft_set_elem_expr_clone(const struct nft_ctx *ctx, + struct nft_set *set, + struct nft_expr *expr_array[]) +{ + struct nft_expr *expr; + int err, i, k; + + for (i = 0; i < set->num_exprs; i++) { + expr = kzalloc(set->exprs[i]->ops->size, GFP_KERNEL); + if (!expr) + goto err_expr; + + err = nft_expr_clone(expr, set->exprs[i]); + if (err < 0) { + nft_expr_destroy(ctx, expr); + goto err_expr; + } + expr_array[i] = expr; + } + + return 0; + +err_expr: + for (k = i - 1; k >= 0; k++) + nft_expr_destroy(ctx, expr_array[i]); + + return -ENOMEM; +} + +static void nft_set_elem_expr_setup(const struct nft_set_ext *ext, int i, + struct nft_expr *expr_array[]) +{ + struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext); + struct nft_expr *expr = nft_setelem_expr_at(elem_expr, elem_expr->size); + + memcpy(expr, expr_array[i], expr_array[i]->ops->size); + elem_expr->size += expr_array[i]->ops->size; + kfree(expr_array[i]); + expr_array[i] = NULL; +} + static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, const struct nlattr *attr, u32 nlmsg_flags) { + struct nft_expr *expr_array[NFT_SET_EXPR_MAX] = {}; struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; u8 genmask = nft_genmask_next(ctx->net); struct nft_set_ext_tmpl tmpl; @@ -5153,16 +5283,15 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, struct nft_set_elem elem; struct nft_set_binding *binding; struct nft_object *obj = NULL; - struct nft_expr *expr = NULL; struct nft_userdata *udata; struct nft_data_desc desc; enum nft_registers dreg; struct nft_trans *trans; - u32 flags = 0; + u32 flags = 0, size = 0; u64 timeout; u64 expiration; + int err, i; u8 ulen; - int err; err = nla_parse_nested_deprecated(nla, NFTA_SET_ELEM_MAX, attr, nft_set_elem_policy, NULL); @@ -5195,7 +5324,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, nla[NFTA_SET_ELEM_TIMEOUT] || nla[NFTA_SET_ELEM_EXPIRATION] || nla[NFTA_SET_ELEM_USERDATA] || - nla[NFTA_SET_ELEM_EXPR])) + nla[NFTA_SET_ELEM_EXPR] || + nla[NFTA_SET_ELEM_EXPRESSIONS])) return -EINVAL; timeout = 0; @@ -5220,23 +5350,62 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, return err; } - if (nla[NFTA_SET_ELEM_EXPR] != NULL) { + if (nla[NFTA_SET_ELEM_EXPR]) { + struct nft_expr *expr; + + if (set->num_exprs != 1) + return -EOPNOTSUPP; + expr = nft_set_elem_expr_alloc(ctx, set, nla[NFTA_SET_ELEM_EXPR]); if (IS_ERR(expr)) return PTR_ERR(expr); - err = -EOPNOTSUPP; - if (set->expr && set->expr->ops != expr->ops) + expr_array[0] = expr; + + if (set->exprs[0] && set->exprs[0]->ops != expr->ops) { + err = -EOPNOTSUPP; goto err_set_elem_expr; - } else if (set->expr) { - expr = kzalloc(set->expr->ops->size, GFP_KERNEL); - if (!expr) - return -ENOMEM; + } + } else if (nla[NFTA_SET_ELEM_EXPRESSIONS]) { + struct nft_expr *expr; + struct nlattr *tmp; + int left; - err = nft_expr_clone(expr, set->expr); - if (err < 0) + if (set->num_exprs == 0) + return -EOPNOTSUPP; + + i = 0; + nla_for_each_nested(tmp, nla[NFTA_SET_ELEM_EXPRESSIONS], left) { + if (i == set->num_exprs) { + err = -E2BIG; + goto err_set_elem_expr; + } + if (nla_type(tmp) != NFTA_LIST_ELEM) { + err = -EINVAL; + goto err_set_elem_expr; + } + expr = nft_set_elem_expr_alloc(ctx, set, tmp); + if (IS_ERR(expr)) { + err = PTR_ERR(expr); + goto err_set_elem_expr; + } + expr_array[i] = expr; + + if (expr->ops != set->exprs[i]->ops) { + err = -EOPNOTSUPP; + goto err_set_elem_expr; + } + i++; + } + if (set->num_exprs != i) { + err = -EOPNOTSUPP; goto err_set_elem_expr; + } + } else if (set->num_exprs > 0) { + err = nft_set_elem_expr_clone(ctx, set, expr_array); + if (err < 0) + goto err_set_elem_expr_clone; } err = nft_setelem_parse_key(ctx, set, &elem.key.val, @@ -5261,9 +5430,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT); } - if (expr) - nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPR, - expr->ops->size); + if (set->num_exprs) { + for (i = 0; i < set->num_exprs; i++) + size += expr_array[i]->ops->size; + + nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPRESSIONS, + sizeof(struct nft_set_elem_expr) + + size); + } if (nla[NFTA_SET_ELEM_OBJREF] != NULL) { if (!(set->flags & NFT_SET_OBJECT)) { @@ -5345,11 +5519,8 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, *nft_set_ext_obj(ext) = obj; obj->use++; } - if (expr) { - memcpy(nft_set_ext_expr(ext), expr, expr->ops->size); - kfree(expr); - expr = NULL; - } + for (i = 0; i < set->num_exprs; i++) + nft_set_elem_expr_setup(ext, i, expr_array); trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); if (trans == NULL) @@ -5410,9 +5581,9 @@ err_parse_key_end: err_parse_key: nft_data_release(&elem.key.val, NFT_DATA_VALUE); err_set_elem_expr: - if (expr != NULL) - nft_expr_destroy(ctx, expr); - + for (i = 0; i < set->num_exprs && expr_array[i]; i++) + nft_expr_destroy(ctx, expr_array[i]); +err_set_elem_expr_clone: return err; } @@ -5739,7 +5910,7 @@ struct nft_object *nft_obj_lookup(const struct net *net, struct rhlist_head *tmp, *list; struct nft_object *obj; - nla_strlcpy(search, nla, sizeof(search)); + nla_strscpy(search, nla, sizeof(search)); k.name = search; WARN_ON_ONCE(!rcu_read_lock_held() &&