Skip to content

Commit b9ee96b

Browse files
committed
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 your net tree, they are: 1) Don't pick fixed hash implementation for NFT_SET_EVAL sets, otherwise userspace hits EOPNOTSUPP with valid rules using the meter statement, from Florian Westphal. 2) If you send a batch that flushes the existing ruleset (that contains a NAT chain) and the new ruleset definition comes with a new NAT chain, don't bogusly hit EBUSY. Also from Florian. 3) Missing netlink policy attribute validation, from Florian. 4) Detach conntrack template from skbuff if IP_NODEFRAG is set on, from Paolo Abeni. 5) Cache device names in flowtable object, otherwise we may end up walking over devices going aways given no rtnl_lock is held. 6) Fix incorrect net_device ingress with ingress hooks. 7) Fix crash when trying to read more data than available in UDP packets from the nf_socket infrastructure, from Subash. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 1bfa26f + 32c1733 commit b9ee96b

File tree

6 files changed

+109
-29
lines changed

6 files changed

+109
-29
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,8 @@ struct nft_object_ops {
10681068
int nft_register_obj(struct nft_object_type *obj_type);
10691069
void nft_unregister_obj(struct nft_object_type *obj_type);
10701070

1071+
#define NFT_FLOWTABLE_DEVICE_MAX 8
1072+
10711073
/**
10721074
* struct nft_flowtable - nf_tables flow table
10731075
*
@@ -1080,6 +1082,7 @@ void nft_unregister_obj(struct nft_object_type *obj_type);
10801082
* @genmask: generation mask
10811083
* @use: number of references to this flow table
10821084
* @handle: unique object handle
1085+
* @dev_name: array of device names
10831086
* @data: rhashtable and garbage collector
10841087
* @ops: array of hooks
10851088
*/
@@ -1093,6 +1096,7 @@ struct nft_flowtable {
10931096
u32 genmask:2,
10941097
use:30;
10951098
u64 handle;
1099+
char *dev_name[NFT_FLOWTABLE_DEVICE_MAX];
10961100
/* runtime data below here */
10971101
struct nf_hook_ops *ops ____cacheline_aligned;
10981102
struct nf_flowtable data;

net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,20 @@ static unsigned int ipv4_conntrack_local(void *priv,
154154
struct sk_buff *skb,
155155
const struct nf_hook_state *state)
156156
{
157-
if (ip_is_fragment(ip_hdr(skb))) /* IP_NODEFRAG setsockopt set */
157+
if (ip_is_fragment(ip_hdr(skb))) { /* IP_NODEFRAG setsockopt set */
158+
enum ip_conntrack_info ctinfo;
159+
struct nf_conn *tmpl;
160+
161+
tmpl = nf_ct_get(skb, &ctinfo);
162+
if (tmpl && nf_ct_is_template(tmpl)) {
163+
/* when skipping ct, clear templates to avoid fooling
164+
* later targets/matches
165+
*/
166+
skb->_nfct = 0;
167+
nf_ct_put(tmpl);
168+
}
158169
return NF_ACCEPT;
170+
}
159171

160172
return nf_conntrack_in(state->net, PF_INET, state->hook, skb);
161173
}

net/ipv4/netfilter/nf_socket_ipv4.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,12 @@ struct sock *nf_sk_lookup_slow_v4(struct net *net, const struct sk_buff *skb,
108108
int doff = 0;
109109

110110
if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) {
111-
struct udphdr _hdr, *hp;
111+
struct tcphdr _hdr;
112+
struct udphdr *hp;
112113

113114
hp = skb_header_pointer(skb, ip_hdrlen(skb),
114-
sizeof(_hdr), &_hdr);
115+
iph->protocol == IPPROTO_UDP ?
116+
sizeof(*hp) : sizeof(_hdr), &_hdr);
115117
if (hp == NULL)
116118
return NULL;
117119

net/ipv6/netfilter/nf_socket_ipv6.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,11 @@ struct sock *nf_sk_lookup_slow_v6(struct net *net, const struct sk_buff *skb,
116116
}
117117

118118
if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) {
119-
struct udphdr _hdr, *hp;
119+
struct tcphdr _hdr;
120+
struct udphdr *hp;
120121

121-
hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
122+
hp = skb_header_pointer(skb, thoff, tproto == IPPROTO_UDP ?
123+
sizeof(*hp) : sizeof(_hdr), &_hdr);
122124
if (hp == NULL)
123125
return NULL;
124126

net/netfilter/nf_tables_api.c

Lines changed: 83 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,77 @@ static void nft_trans_destroy(struct nft_trans *trans)
7474
kfree(trans);
7575
}
7676

77+
/* removal requests are queued in the commit_list, but not acted upon
78+
* until after all new rules are in place.
79+
*
80+
* Therefore, nf_register_net_hook(net, &nat_hook) runs before pending
81+
* nf_unregister_net_hook().
82+
*
83+
* nf_register_net_hook thus fails if a nat hook is already in place
84+
* even if the conflicting hook is about to be removed.
85+
*
86+
* If collision is detected, search commit_log for DELCHAIN matching
87+
* the new nat hooknum; if we find one collision is temporary:
88+
*
89+
* Either transaction is aborted (new/colliding hook is removed), or
90+
* transaction is committed (old hook is removed).
91+
*/
92+
static bool nf_tables_allow_nat_conflict(const struct net *net,
93+
const struct nf_hook_ops *ops)
94+
{
95+
const struct nft_trans *trans;
96+
bool ret = false;
97+
98+
if (!ops->nat_hook)
99+
return false;
100+
101+
list_for_each_entry(trans, &net->nft.commit_list, list) {
102+
const struct nf_hook_ops *pending_ops;
103+
const struct nft_chain *pending;
104+
105+
if (trans->msg_type != NFT_MSG_NEWCHAIN &&
106+
trans->msg_type != NFT_MSG_DELCHAIN)
107+
continue;
108+
109+
pending = trans->ctx.chain;
110+
if (!nft_is_base_chain(pending))
111+
continue;
112+
113+
pending_ops = &nft_base_chain(pending)->ops;
114+
if (pending_ops->nat_hook &&
115+
pending_ops->pf == ops->pf &&
116+
pending_ops->hooknum == ops->hooknum) {
117+
/* other hook registration already pending? */
118+
if (trans->msg_type == NFT_MSG_NEWCHAIN)
119+
return false;
120+
121+
ret = true;
122+
}
123+
}
124+
125+
return ret;
126+
}
127+
77128
static int nf_tables_register_hook(struct net *net,
78129
const struct nft_table *table,
79130
struct nft_chain *chain)
80131
{
132+
struct nf_hook_ops *ops;
133+
int ret;
134+
81135
if (table->flags & NFT_TABLE_F_DORMANT ||
82136
!nft_is_base_chain(chain))
83137
return 0;
84138

85-
return nf_register_net_hook(net, &nft_base_chain(chain)->ops);
139+
ops = &nft_base_chain(chain)->ops;
140+
ret = nf_register_net_hook(net, ops);
141+
if (ret == -EBUSY && nf_tables_allow_nat_conflict(net, ops)) {
142+
ops->nat_hook = false;
143+
ret = nf_register_net_hook(net, ops);
144+
ops->nat_hook = true;
145+
}
146+
147+
return ret;
86148
}
87149

88150
static void nf_tables_unregister_hook(struct net *net,
@@ -1226,8 +1288,6 @@ static void nf_tables_chain_destroy(struct nft_chain *chain)
12261288
free_percpu(basechain->stats);
12271289
if (basechain->stats)
12281290
static_branch_dec(&nft_counters_enabled);
1229-
if (basechain->ops.dev != NULL)
1230-
dev_put(basechain->ops.dev);
12311291
kfree(chain->name);
12321292
kfree(basechain);
12331293
} else {
@@ -1294,7 +1354,7 @@ static int nft_chain_parse_hook(struct net *net,
12941354
}
12951355

12961356
nla_strlcpy(ifname, ha[NFTA_HOOK_DEV], IFNAMSIZ);
1297-
dev = dev_get_by_name(net, ifname);
1357+
dev = __dev_get_by_name(net, ifname);
12981358
if (!dev) {
12991359
module_put(type->owner);
13001360
return -ENOENT;
@@ -1311,8 +1371,6 @@ static int nft_chain_parse_hook(struct net *net,
13111371
static void nft_chain_release_hook(struct nft_chain_hook *hook)
13121372
{
13131373
module_put(hook->type->owner);
1314-
if (hook->dev != NULL)
1315-
dev_put(hook->dev);
13161374
}
13171375

13181376
static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
@@ -1911,6 +1969,7 @@ static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = {
19111969
[NFTA_RULE_POSITION] = { .type = NLA_U64 },
19121970
[NFTA_RULE_USERDATA] = { .type = NLA_BINARY,
19131971
.len = NFT_USERDATA_MAXLEN },
1972+
[NFTA_RULE_ID] = { .type = NLA_U32 },
19141973
};
19151974

19161975
static int nf_tables_fill_rule_info(struct sk_buff *skb, struct net *net,
@@ -2446,6 +2505,9 @@ EXPORT_SYMBOL_GPL(nft_unregister_set);
24462505

24472506
static bool nft_set_ops_candidate(const struct nft_set_ops *ops, u32 flags)
24482507
{
2508+
if ((flags & NFT_SET_EVAL) && !ops->update)
2509+
return false;
2510+
24492511
return (flags & ops->features) == (flags & NFT_SET_FEATURES);
24502512
}
24512513

@@ -2510,7 +2572,7 @@ nft_select_set_ops(const struct nft_ctx *ctx,
25102572
if (est.space == best.space &&
25112573
est.lookup < best.lookup)
25122574
break;
2513-
} else if (est.size < best.size) {
2575+
} else if (est.size < best.size || !bops) {
25142576
break;
25152577
}
25162578
continue;
@@ -3315,6 +3377,8 @@ static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
33153377
[NFTA_SET_ELEM_TIMEOUT] = { .type = NLA_U64 },
33163378
[NFTA_SET_ELEM_USERDATA] = { .type = NLA_BINARY,
33173379
.len = NFT_USERDATA_MAXLEN },
3380+
[NFTA_SET_ELEM_EXPR] = { .type = NLA_NESTED },
3381+
[NFTA_SET_ELEM_OBJREF] = { .type = NLA_STRING },
33183382
};
33193383

33203384
static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = {
@@ -4864,8 +4928,6 @@ nf_tables_flowtable_lookup_byhandle(const struct nft_table *table,
48644928
return ERR_PTR(-ENOENT);
48654929
}
48664930

4867-
#define NFT_FLOWTABLE_DEVICE_MAX 8
4868-
48694931
static int nf_tables_parse_devices(const struct nft_ctx *ctx,
48704932
const struct nlattr *attr,
48714933
struct net_device *dev_array[], int *len)
@@ -4882,7 +4944,7 @@ static int nf_tables_parse_devices(const struct nft_ctx *ctx,
48824944
}
48834945

48844946
nla_strlcpy(ifname, tmp, IFNAMSIZ);
4885-
dev = dev_get_by_name(ctx->net, ifname);
4947+
dev = __dev_get_by_name(ctx->net, ifname);
48864948
if (!dev) {
48874949
err = -ENOENT;
48884950
goto err1;
@@ -4938,13 +5000,11 @@ static int nf_tables_flowtable_parse_hook(const struct nft_ctx *ctx,
49385000
err = nf_tables_parse_devices(ctx, tb[NFTA_FLOWTABLE_HOOK_DEVS],
49395001
dev_array, &n);
49405002
if (err < 0)
4941-
goto err1;
5003+
return err;
49425004

49435005
ops = kzalloc(sizeof(struct nf_hook_ops) * n, GFP_KERNEL);
4944-
if (!ops) {
4945-
err = -ENOMEM;
4946-
goto err1;
4947-
}
5006+
if (!ops)
5007+
return -ENOMEM;
49485008

49495009
flowtable->hooknum = hooknum;
49505010
flowtable->priority = priority;
@@ -4958,13 +5018,10 @@ static int nf_tables_flowtable_parse_hook(const struct nft_ctx *ctx,
49585018
flowtable->ops[i].priv = &flowtable->data.rhashtable;
49595019
flowtable->ops[i].hook = flowtable->data.type->hook;
49605020
flowtable->ops[i].dev = dev_array[i];
5021+
flowtable->dev_name[i] = kstrdup(dev_array[i]->name,
5022+
GFP_KERNEL);
49615023
}
49625024

4963-
err = 0;
4964-
err1:
4965-
for (i = 0; i < n; i++)
4966-
dev_put(dev_array[i]);
4967-
49685025
return err;
49695026
}
49705027

@@ -5135,8 +5192,10 @@ static int nf_tables_newflowtable(struct net *net, struct sock *nlsk,
51355192
err5:
51365193
i = flowtable->ops_len;
51375194
err4:
5138-
for (k = i - 1; k >= 0; k--)
5195+
for (k = i - 1; k >= 0; k--) {
5196+
kfree(flowtable->dev_name[k]);
51395197
nf_unregister_net_hook(net, &flowtable->ops[k]);
5198+
}
51405199

51415200
kfree(flowtable->ops);
51425201
err3:
@@ -5226,9 +5285,9 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,
52265285
goto nla_put_failure;
52275286

52285287
for (i = 0; i < flowtable->ops_len; i++) {
5229-
if (flowtable->ops[i].dev &&
5288+
if (flowtable->dev_name[i][0] &&
52305289
nla_put_string(skb, NFTA_DEVICE_NAME,
5231-
flowtable->ops[i].dev->name))
5290+
flowtable->dev_name[i]))
52325291
goto nla_put_failure;
52335292
}
52345293
nla_nest_end(skb, nest_devs);
@@ -5470,6 +5529,7 @@ static void nft_flowtable_event(unsigned long event, struct net_device *dev,
54705529
continue;
54715530

54725531
nf_unregister_net_hook(dev_net(dev), &flowtable->ops[i]);
5532+
flowtable->dev_name[i][0] = '\0';
54735533
flowtable->ops[i].dev = NULL;
54745534
break;
54755535
}

net/netfilter/nft_set_hash.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ static const struct nft_set_ops *
674674
nft_hash_select_ops(const struct nft_ctx *ctx, const struct nft_set_desc *desc,
675675
u32 flags)
676676
{
677-
if (desc->size && !(flags & NFT_SET_TIMEOUT)) {
677+
if (desc->size && !(flags & (NFT_SET_EVAL | NFT_SET_TIMEOUT))) {
678678
switch (desc->klen) {
679679
case 4:
680680
return &nft_hash_fast_ops;

0 commit comments

Comments
 (0)