Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter fixes for net:

1) Fix NAT IPv6 offload in the flowtable.

2) icmpv6 is printed as unknown in /proc/net/nf_conntrack.

3) Use div64_u64() in nft_limit, from Eric Dumazet.

4) Use pre_exit to unregister ebtables and arptables hooks,
   from Florian Westphal.

5) Fix out-of-bound memset in x_tables compat match/target,
   also from Florian.

6) Clone set elements expression to ensure proper initialization.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2021-04-12 16:17:50 -07:00
commit ccb39c6285
15 changed files with 118 additions and 38 deletions

View File

@ -52,8 +52,9 @@ extern void *arpt_alloc_initial_table(const struct xt_table *);
int arpt_register_table(struct net *net, const struct xt_table *table, int arpt_register_table(struct net *net, const struct xt_table *table,
const struct arpt_replace *repl, const struct arpt_replace *repl,
const struct nf_hook_ops *ops, struct xt_table **res); const struct nf_hook_ops *ops, struct xt_table **res);
void arpt_unregister_table(struct net *net, struct xt_table *table, void arpt_unregister_table(struct net *net, struct xt_table *table);
const struct nf_hook_ops *ops); void arpt_unregister_table_pre_exit(struct net *net, struct xt_table *table,
const struct nf_hook_ops *ops);
extern unsigned int arpt_do_table(struct sk_buff *skb, extern unsigned int arpt_do_table(struct sk_buff *skb,
const struct nf_hook_state *state, const struct nf_hook_state *state,
struct xt_table *table); struct xt_table *table);

View File

@ -110,8 +110,9 @@ extern int ebt_register_table(struct net *net,
const struct ebt_table *table, const struct ebt_table *table,
const struct nf_hook_ops *ops, const struct nf_hook_ops *ops,
struct ebt_table **res); struct ebt_table **res);
extern void ebt_unregister_table(struct net *net, struct ebt_table *table, extern void ebt_unregister_table(struct net *net, struct ebt_table *table);
const struct nf_hook_ops *); void ebt_unregister_table_pre_exit(struct net *net, const char *tablename,
const struct nf_hook_ops *ops);
extern unsigned int ebt_do_table(struct sk_buff *skb, extern unsigned int ebt_do_table(struct sk_buff *skb,
const struct nf_hook_state *state, const struct nf_hook_state *state,
struct ebt_table *table); struct ebt_table *table);

View File

@ -105,14 +105,20 @@ static int __net_init broute_net_init(struct net *net)
&net->xt.broute_table); &net->xt.broute_table);
} }
static void __net_exit broute_net_pre_exit(struct net *net)
{
ebt_unregister_table_pre_exit(net, "broute", &ebt_ops_broute);
}
static void __net_exit broute_net_exit(struct net *net) static void __net_exit broute_net_exit(struct net *net)
{ {
ebt_unregister_table(net, net->xt.broute_table, &ebt_ops_broute); ebt_unregister_table(net, net->xt.broute_table);
} }
static struct pernet_operations broute_net_ops = { static struct pernet_operations broute_net_ops = {
.init = broute_net_init, .init = broute_net_init,
.exit = broute_net_exit, .exit = broute_net_exit,
.pre_exit = broute_net_pre_exit,
}; };
static int __init ebtable_broute_init(void) static int __init ebtable_broute_init(void)

View File

@ -99,14 +99,20 @@ static int __net_init frame_filter_net_init(struct net *net)
&net->xt.frame_filter); &net->xt.frame_filter);
} }
static void __net_exit frame_filter_net_pre_exit(struct net *net)
{
ebt_unregister_table_pre_exit(net, "filter", ebt_ops_filter);
}
static void __net_exit frame_filter_net_exit(struct net *net) static void __net_exit frame_filter_net_exit(struct net *net)
{ {
ebt_unregister_table(net, net->xt.frame_filter, ebt_ops_filter); ebt_unregister_table(net, net->xt.frame_filter);
} }
static struct pernet_operations frame_filter_net_ops = { static struct pernet_operations frame_filter_net_ops = {
.init = frame_filter_net_init, .init = frame_filter_net_init,
.exit = frame_filter_net_exit, .exit = frame_filter_net_exit,
.pre_exit = frame_filter_net_pre_exit,
}; };
static int __init ebtable_filter_init(void) static int __init ebtable_filter_init(void)

View File

@ -99,14 +99,20 @@ static int __net_init frame_nat_net_init(struct net *net)
&net->xt.frame_nat); &net->xt.frame_nat);
} }
static void __net_exit frame_nat_net_pre_exit(struct net *net)
{
ebt_unregister_table_pre_exit(net, "nat", ebt_ops_nat);
}
static void __net_exit frame_nat_net_exit(struct net *net) static void __net_exit frame_nat_net_exit(struct net *net)
{ {
ebt_unregister_table(net, net->xt.frame_nat, ebt_ops_nat); ebt_unregister_table(net, net->xt.frame_nat);
} }
static struct pernet_operations frame_nat_net_ops = { static struct pernet_operations frame_nat_net_ops = {
.init = frame_nat_net_init, .init = frame_nat_net_init,
.exit = frame_nat_net_exit, .exit = frame_nat_net_exit,
.pre_exit = frame_nat_net_pre_exit,
}; };
static int __init ebtable_nat_init(void) static int __init ebtable_nat_init(void)

View File

@ -1232,10 +1232,34 @@ out:
return ret; return ret;
} }
void ebt_unregister_table(struct net *net, struct ebt_table *table, static struct ebt_table *__ebt_find_table(struct net *net, const char *name)
const struct nf_hook_ops *ops) {
struct ebt_table *t;
mutex_lock(&ebt_mutex);
list_for_each_entry(t, &net->xt.tables[NFPROTO_BRIDGE], list) {
if (strcmp(t->name, name) == 0) {
mutex_unlock(&ebt_mutex);
return t;
}
}
mutex_unlock(&ebt_mutex);
return NULL;
}
void ebt_unregister_table_pre_exit(struct net *net, const char *name, const struct nf_hook_ops *ops)
{
struct ebt_table *table = __ebt_find_table(net, name);
if (table)
nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
}
EXPORT_SYMBOL(ebt_unregister_table_pre_exit);
void ebt_unregister_table(struct net *net, struct ebt_table *table)
{ {
nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
__ebt_unregister_table(net, table); __ebt_unregister_table(net, table);
} }

View File

@ -1193,6 +1193,8 @@ static int translate_compat_table(struct net *net,
if (!newinfo) if (!newinfo)
goto out_unlock; goto out_unlock;
memset(newinfo->entries, 0, size);
newinfo->number = compatr->num_entries; newinfo->number = compatr->num_entries;
for (i = 0; i < NF_ARP_NUMHOOKS; i++) { for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
newinfo->hook_entry[i] = compatr->hook_entry[i]; newinfo->hook_entry[i] = compatr->hook_entry[i];
@ -1539,10 +1541,15 @@ out_free:
return ret; return ret;
} }
void arpt_unregister_table(struct net *net, struct xt_table *table, void arpt_unregister_table_pre_exit(struct net *net, struct xt_table *table,
const struct nf_hook_ops *ops) const struct nf_hook_ops *ops)
{ {
nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks)); nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
}
EXPORT_SYMBOL(arpt_unregister_table_pre_exit);
void arpt_unregister_table(struct net *net, struct xt_table *table)
{
__arpt_unregister_table(net, table); __arpt_unregister_table(net, table);
} }

View File

@ -56,16 +56,24 @@ static int __net_init arptable_filter_table_init(struct net *net)
return err; return err;
} }
static void __net_exit arptable_filter_net_pre_exit(struct net *net)
{
if (net->ipv4.arptable_filter)
arpt_unregister_table_pre_exit(net, net->ipv4.arptable_filter,
arpfilter_ops);
}
static void __net_exit arptable_filter_net_exit(struct net *net) static void __net_exit arptable_filter_net_exit(struct net *net)
{ {
if (!net->ipv4.arptable_filter) if (!net->ipv4.arptable_filter)
return; return;
arpt_unregister_table(net, net->ipv4.arptable_filter, arpfilter_ops); arpt_unregister_table(net, net->ipv4.arptable_filter);
net->ipv4.arptable_filter = NULL; net->ipv4.arptable_filter = NULL;
} }
static struct pernet_operations arptable_filter_net_ops = { static struct pernet_operations arptable_filter_net_ops = {
.exit = arptable_filter_net_exit, .exit = arptable_filter_net_exit,
.pre_exit = arptable_filter_net_pre_exit,
}; };
static int __init arptable_filter_init(void) static int __init arptable_filter_init(void)

View File

@ -1428,6 +1428,8 @@ translate_compat_table(struct net *net,
if (!newinfo) if (!newinfo)
goto out_unlock; goto out_unlock;
memset(newinfo->entries, 0, size);
newinfo->number = compatr->num_entries; newinfo->number = compatr->num_entries;
for (i = 0; i < NF_INET_NUMHOOKS; i++) { for (i = 0; i < NF_INET_NUMHOOKS; i++) {
newinfo->hook_entry[i] = compatr->hook_entry[i]; newinfo->hook_entry[i] = compatr->hook_entry[i];

View File

@ -1443,6 +1443,8 @@ translate_compat_table(struct net *net,
if (!newinfo) if (!newinfo)
goto out_unlock; goto out_unlock;
memset(newinfo->entries, 0, size);
newinfo->number = compatr->num_entries; newinfo->number = compatr->num_entries;
for (i = 0; i < NF_INET_NUMHOOKS; i++) { for (i = 0; i < NF_INET_NUMHOOKS; i++) {
newinfo->hook_entry[i] = compatr->hook_entry[i]; newinfo->hook_entry[i] = compatr->hook_entry[i];

View File

@ -266,6 +266,7 @@ static const char* l4proto_name(u16 proto)
case IPPROTO_GRE: return "gre"; case IPPROTO_GRE: return "gre";
case IPPROTO_SCTP: return "sctp"; case IPPROTO_SCTP: return "sctp";
case IPPROTO_UDPLITE: return "udplite"; case IPPROTO_UDPLITE: return "udplite";
case IPPROTO_ICMPV6: return "icmpv6";
} }
return "unknown"; return "unknown";

View File

@ -305,12 +305,12 @@ static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule,
const __be32 *addr, const __be32 *mask) const __be32 *addr, const __be32 *mask)
{ {
struct flow_action_entry *entry; struct flow_action_entry *entry;
int i; int i, j;
for (i = 0; i < sizeof(struct in6_addr) / sizeof(u32); i += sizeof(u32)) { for (i = 0, j = 0; i < sizeof(struct in6_addr) / sizeof(u32); i += sizeof(u32), j++) {
entry = flow_action_entry_next(flow_rule); entry = flow_action_entry_next(flow_rule);
flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP6, flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP6,
offset + i, &addr[i], mask); offset + i, &addr[j], mask);
} }
} }

View File

@ -5295,16 +5295,35 @@ err_expr:
return -ENOMEM; return -ENOMEM;
} }
static void nft_set_elem_expr_setup(const struct nft_set_ext *ext, int i, static int nft_set_elem_expr_setup(struct nft_ctx *ctx,
struct nft_expr *expr_array[]) const struct nft_set_ext *ext,
struct nft_expr *expr_array[],
u32 num_exprs)
{ {
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext); 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); struct nft_expr *expr;
int i, err;
memcpy(expr, expr_array[i], expr_array[i]->ops->size); for (i = 0; i < num_exprs; i++) {
elem_expr->size += expr_array[i]->ops->size; expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
kfree(expr_array[i]); err = nft_expr_clone(expr, expr_array[i]);
expr_array[i] = NULL; if (err < 0)
goto err_elem_expr_setup;
elem_expr->size += expr_array[i]->ops->size;
nft_expr_destroy(ctx, expr_array[i]);
expr_array[i] = NULL;
}
return 0;
err_elem_expr_setup:
for (; i < num_exprs; i++) {
nft_expr_destroy(ctx, expr_array[i]);
expr_array[i] = NULL;
}
return -ENOMEM;
} }
static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
@ -5556,12 +5575,15 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
*nft_set_ext_obj(ext) = obj; *nft_set_ext_obj(ext) = obj;
obj->use++; obj->use++;
} }
for (i = 0; i < num_exprs; i++) err = nft_set_elem_expr_setup(ctx, ext, expr_array, num_exprs);
nft_set_elem_expr_setup(ext, i, expr_array); if (err < 0)
goto err_elem_expr;
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
if (trans == NULL) if (trans == NULL) {
goto err_trans; err = -ENOMEM;
goto err_elem_expr;
}
ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK; ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
err = set->ops->insert(ctx->net, set, &elem, &ext2); err = set->ops->insert(ctx->net, set, &elem, &ext2);
@ -5605,7 +5627,7 @@ err_set_full:
set->ops->remove(ctx->net, set, &elem); set->ops->remove(ctx->net, set, &elem);
err_element_clash: err_element_clash:
kfree(trans); kfree(trans);
err_trans: err_elem_expr:
if (obj) if (obj)
obj->use--; obj->use--;

View File

@ -76,13 +76,13 @@ static int nft_limit_init(struct nft_limit *limit,
return -EOVERFLOW; return -EOVERFLOW;
if (pkts) { if (pkts) {
tokens = div_u64(limit->nsecs, limit->rate) * limit->burst; tokens = div64_u64(limit->nsecs, limit->rate) * limit->burst;
} else { } else {
/* The token bucket size limits the number of tokens can be /* The token bucket size limits the number of tokens can be
* accumulated. tokens_max specifies the bucket size. * accumulated. tokens_max specifies the bucket size.
* tokens_max = unit * (rate + burst) / rate. * tokens_max = unit * (rate + burst) / rate.
*/ */
tokens = div_u64(limit->nsecs * (limit->rate + limit->burst), tokens = div64_u64(limit->nsecs * (limit->rate + limit->burst),
limit->rate); limit->rate);
} }

View File

@ -733,7 +733,7 @@ void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
{ {
const struct xt_match *match = m->u.kernel.match; const struct xt_match *match = m->u.kernel.match;
struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m; struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m;
int pad, off = xt_compat_match_offset(match); int off = xt_compat_match_offset(match);
u_int16_t msize = cm->u.user.match_size; u_int16_t msize = cm->u.user.match_size;
char name[sizeof(m->u.user.name)]; char name[sizeof(m->u.user.name)];
@ -743,9 +743,6 @@ void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
match->compat_from_user(m->data, cm->data); match->compat_from_user(m->data, cm->data);
else else
memcpy(m->data, cm->data, msize - sizeof(*cm)); memcpy(m->data, cm->data, msize - sizeof(*cm));
pad = XT_ALIGN(match->matchsize) - match->matchsize;
if (pad > 0)
memset(m->data + match->matchsize, 0, pad);
msize += off; msize += off;
m->u.user.match_size = msize; m->u.user.match_size = msize;
@ -1116,7 +1113,7 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
{ {
const struct xt_target *target = t->u.kernel.target; const struct xt_target *target = t->u.kernel.target;
struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t; struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t;
int pad, off = xt_compat_target_offset(target); int off = xt_compat_target_offset(target);
u_int16_t tsize = ct->u.user.target_size; u_int16_t tsize = ct->u.user.target_size;
char name[sizeof(t->u.user.name)]; char name[sizeof(t->u.user.name)];
@ -1126,9 +1123,6 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
target->compat_from_user(t->data, ct->data); target->compat_from_user(t->data, ct->data);
else else
memcpy(t->data, ct->data, tsize - sizeof(*ct)); memcpy(t->data, ct->data, tsize - sizeof(*ct));
pad = XT_ALIGN(target->targetsize) - target->targetsize;
if (pad > 0)
memset(t->data + target->targetsize, 0, pad);
tsize += off; tsize += off;
t->u.user.target_size = tsize; t->u.user.target_size = tsize;