netfilter: nf_tables: cache device name in flowtable object
Devices going away have to grab the nfnl_lock from the netdev event path to avoid races with control plane updates. However, netlink dumps in netfilter do not hold nfnl_lock mutex. Cache the device name into the objects to avoid an use-after-free situation for a device that is going away. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
parent
aebfa52a92
commit
d92191aa84
|
@ -1068,6 +1068,8 @@ struct nft_object_ops {
|
||||||
int nft_register_obj(struct nft_object_type *obj_type);
|
int nft_register_obj(struct nft_object_type *obj_type);
|
||||||
void nft_unregister_obj(struct nft_object_type *obj_type);
|
void nft_unregister_obj(struct nft_object_type *obj_type);
|
||||||
|
|
||||||
|
#define NFT_FLOWTABLE_DEVICE_MAX 8
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct nft_flowtable - nf_tables flow table
|
* struct nft_flowtable - nf_tables flow table
|
||||||
*
|
*
|
||||||
|
@ -1080,6 +1082,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type);
|
||||||
* @genmask: generation mask
|
* @genmask: generation mask
|
||||||
* @use: number of references to this flow table
|
* @use: number of references to this flow table
|
||||||
* @handle: unique object handle
|
* @handle: unique object handle
|
||||||
|
* @dev_name: array of device names
|
||||||
* @data: rhashtable and garbage collector
|
* @data: rhashtable and garbage collector
|
||||||
* @ops: array of hooks
|
* @ops: array of hooks
|
||||||
*/
|
*/
|
||||||
|
@ -1093,6 +1096,7 @@ struct nft_flowtable {
|
||||||
u32 genmask:2,
|
u32 genmask:2,
|
||||||
use:30;
|
use:30;
|
||||||
u64 handle;
|
u64 handle;
|
||||||
|
char *dev_name[NFT_FLOWTABLE_DEVICE_MAX];
|
||||||
/* runtime data below here */
|
/* runtime data below here */
|
||||||
struct nf_hook_ops *ops ____cacheline_aligned;
|
struct nf_hook_ops *ops ____cacheline_aligned;
|
||||||
struct nf_flowtable data;
|
struct nf_flowtable data;
|
||||||
|
|
|
@ -4932,8 +4932,6 @@ nf_tables_flowtable_lookup_byhandle(const struct nft_table *table,
|
||||||
return ERR_PTR(-ENOENT);
|
return ERR_PTR(-ENOENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NFT_FLOWTABLE_DEVICE_MAX 8
|
|
||||||
|
|
||||||
static int nf_tables_parse_devices(const struct nft_ctx *ctx,
|
static int nf_tables_parse_devices(const struct nft_ctx *ctx,
|
||||||
const struct nlattr *attr,
|
const struct nlattr *attr,
|
||||||
struct net_device *dev_array[], int *len)
|
struct net_device *dev_array[], int *len)
|
||||||
|
@ -5006,7 +5004,7 @@ static int nf_tables_flowtable_parse_hook(const struct nft_ctx *ctx,
|
||||||
err = nf_tables_parse_devices(ctx, tb[NFTA_FLOWTABLE_HOOK_DEVS],
|
err = nf_tables_parse_devices(ctx, tb[NFTA_FLOWTABLE_HOOK_DEVS],
|
||||||
dev_array, &n);
|
dev_array, &n);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto err1;
|
return err;
|
||||||
|
|
||||||
ops = kzalloc(sizeof(struct nf_hook_ops) * n, GFP_KERNEL);
|
ops = kzalloc(sizeof(struct nf_hook_ops) * n, GFP_KERNEL);
|
||||||
if (!ops) {
|
if (!ops) {
|
||||||
|
@ -5026,6 +5024,8 @@ static int nf_tables_flowtable_parse_hook(const struct nft_ctx *ctx,
|
||||||
flowtable->ops[i].priv = &flowtable->data.rhashtable;
|
flowtable->ops[i].priv = &flowtable->data.rhashtable;
|
||||||
flowtable->ops[i].hook = flowtable->data.type->hook;
|
flowtable->ops[i].hook = flowtable->data.type->hook;
|
||||||
flowtable->ops[i].dev = dev_array[i];
|
flowtable->ops[i].dev = dev_array[i];
|
||||||
|
flowtable->dev_name[i] = kstrdup(dev_array[i]->name,
|
||||||
|
GFP_KERNEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = 0;
|
err = 0;
|
||||||
|
@ -5203,8 +5203,10 @@ static int nf_tables_newflowtable(struct net *net, struct sock *nlsk,
|
||||||
err5:
|
err5:
|
||||||
i = flowtable->ops_len;
|
i = flowtable->ops_len;
|
||||||
err4:
|
err4:
|
||||||
for (k = i - 1; k >= 0; k--)
|
for (k = i - 1; k >= 0; k--) {
|
||||||
|
kfree(flowtable->dev_name[k]);
|
||||||
nf_unregister_net_hook(net, &flowtable->ops[k]);
|
nf_unregister_net_hook(net, &flowtable->ops[k]);
|
||||||
|
}
|
||||||
|
|
||||||
kfree(flowtable->ops);
|
kfree(flowtable->ops);
|
||||||
err3:
|
err3:
|
||||||
|
@ -5294,9 +5296,9 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
|
||||||
for (i = 0; i < flowtable->ops_len; i++) {
|
for (i = 0; i < flowtable->ops_len; i++) {
|
||||||
if (flowtable->ops[i].dev &&
|
if (flowtable->dev_name[i][0] &&
|
||||||
nla_put_string(skb, NFTA_DEVICE_NAME,
|
nla_put_string(skb, NFTA_DEVICE_NAME,
|
||||||
flowtable->ops[i].dev->name))
|
flowtable->dev_name[i]))
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
}
|
}
|
||||||
nla_nest_end(skb, nest_devs);
|
nla_nest_end(skb, nest_devs);
|
||||||
|
@ -5538,6 +5540,7 @@ static void nft_flowtable_event(unsigned long event, struct net_device *dev,
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
nf_unregister_net_hook(dev_net(dev), &flowtable->ops[i]);
|
nf_unregister_net_hook(dev_net(dev), &flowtable->ops[i]);
|
||||||
|
flowtable->dev_name[i][0] = '\0';
|
||||||
flowtable->ops[i].dev = NULL;
|
flowtable->ops[i].dev = NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue