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:
commit
ccb39c6285
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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];
|
||||||
|
@ -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];
|
||||||
|
@ -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";
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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--;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user