Commit f4b29d2e authored by David S. Miller's avatar David S. Miller

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) Nicolas Dichtel updates MAINTAINERS file to add Netfilter IRC channel.

2) Skip non-IPv6 packets in nft_exthdr.

3) Skip non-TCP packets in nft_osf.

4) Skip non-TCP/UDP packets in nft_tproxy.

5) Memleak in hardware offload infrastructure when counters are used
   for first time in a rule.

6) The VLAN transfer routine must use FLOW_DISSECTOR_KEY_BASIC instead
   of FLOW_DISSECTOR_KEY_CONTROL. Moreover, make a more robust check
   for 802.1q and 802.1ad to restore simple matching on transport
   protocols.

7) Fix bogus EPERM when listing a ruleset when table ownership flag
   is set on.

8) Honor table ownership flag when table is referenced by handle.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ce8eb4c7 e31f072f
...@@ -12659,6 +12659,7 @@ W: http://www.netfilter.org/ ...@@ -12659,6 +12659,7 @@ W: http://www.netfilter.org/
W: http://www.iptables.org/ W: http://www.iptables.org/
W: http://www.nftables.org/ W: http://www.nftables.org/
Q: http://patchwork.ozlabs.org/project/netfilter-devel/list/ Q: http://patchwork.ozlabs.org/project/netfilter-devel/list/
C: irc://irc.libera.chat/netfilter
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next.git
F: include/linux/netfilter* F: include/linux/netfilter*
......
...@@ -571,7 +571,7 @@ static struct nft_table *nft_table_lookup(const struct net *net, ...@@ -571,7 +571,7 @@ static struct nft_table *nft_table_lookup(const struct net *net,
table->family == family && table->family == family &&
nft_active_genmask(table, genmask)) { nft_active_genmask(table, genmask)) {
if (nft_table_has_owner(table) && if (nft_table_has_owner(table) &&
table->nlpid != nlpid) nlpid && table->nlpid != nlpid)
return ERR_PTR(-EPERM); return ERR_PTR(-EPERM);
return table; return table;
...@@ -583,7 +583,7 @@ static struct nft_table *nft_table_lookup(const struct net *net, ...@@ -583,7 +583,7 @@ static struct nft_table *nft_table_lookup(const struct net *net,
static struct nft_table *nft_table_lookup_byhandle(const struct net *net, static struct nft_table *nft_table_lookup_byhandle(const struct net *net,
const struct nlattr *nla, const struct nlattr *nla,
u8 genmask) u8 genmask, u32 nlpid)
{ {
struct nftables_pernet *nft_net; struct nftables_pernet *nft_net;
struct nft_table *table; struct nft_table *table;
...@@ -591,9 +591,14 @@ static struct nft_table *nft_table_lookup_byhandle(const struct net *net, ...@@ -591,9 +591,14 @@ static struct nft_table *nft_table_lookup_byhandle(const struct net *net,
nft_net = nft_pernet(net); nft_net = nft_pernet(net);
list_for_each_entry(table, &nft_net->tables, list) { list_for_each_entry(table, &nft_net->tables, list) {
if (be64_to_cpu(nla_get_be64(nla)) == table->handle && if (be64_to_cpu(nla_get_be64(nla)) == table->handle &&
nft_active_genmask(table, genmask)) nft_active_genmask(table, genmask)) {
if (nft_table_has_owner(table) &&
nlpid && table->nlpid != nlpid)
return ERR_PTR(-EPERM);
return table; return table;
} }
}
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
} }
...@@ -1279,7 +1284,8 @@ static int nf_tables_deltable(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -1279,7 +1284,8 @@ static int nf_tables_deltable(struct sk_buff *skb, const struct nfnl_info *info,
if (nla[NFTA_TABLE_HANDLE]) { if (nla[NFTA_TABLE_HANDLE]) {
attr = nla[NFTA_TABLE_HANDLE]; attr = nla[NFTA_TABLE_HANDLE];
table = nft_table_lookup_byhandle(net, attr, genmask); table = nft_table_lookup_byhandle(net, attr, genmask,
NETLINK_CB(skb).portid);
} else { } else {
attr = nla[NFTA_TABLE_NAME]; attr = nla[NFTA_TABLE_NAME];
table = nft_table_lookup(net, attr, family, genmask, table = nft_table_lookup(net, attr, family, genmask,
...@@ -3243,9 +3249,9 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -3243,9 +3249,9 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
u8 genmask = nft_genmask_next(info->net); u8 genmask = nft_genmask_next(info->net);
struct nft_rule *rule, *old_rule = NULL; struct nft_rule *rule, *old_rule = NULL;
struct nft_expr_info *expr_info = NULL; struct nft_expr_info *expr_info = NULL;
struct nft_flow_rule *flow = NULL;
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
struct net *net = info->net; struct net *net = info->net;
struct nft_flow_rule *flow;
struct nft_userdata *udata; struct nft_userdata *udata;
struct nft_table *table; struct nft_table *table;
struct nft_chain *chain; struct nft_chain *chain;
...@@ -3340,13 +3346,13 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -3340,13 +3346,13 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
nla_for_each_nested(tmp, nla[NFTA_RULE_EXPRESSIONS], rem) { nla_for_each_nested(tmp, nla[NFTA_RULE_EXPRESSIONS], rem) {
err = -EINVAL; err = -EINVAL;
if (nla_type(tmp) != NFTA_LIST_ELEM) if (nla_type(tmp) != NFTA_LIST_ELEM)
goto err1; goto err_release_expr;
if (n == NFT_RULE_MAXEXPRS) if (n == NFT_RULE_MAXEXPRS)
goto err1; goto err_release_expr;
err = nf_tables_expr_parse(&ctx, tmp, &expr_info[n]); err = nf_tables_expr_parse(&ctx, tmp, &expr_info[n]);
if (err < 0) { if (err < 0) {
NL_SET_BAD_ATTR(extack, tmp); NL_SET_BAD_ATTR(extack, tmp);
goto err1; goto err_release_expr;
} }
size += expr_info[n].ops->size; size += expr_info[n].ops->size;
n++; n++;
...@@ -3355,7 +3361,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -3355,7 +3361,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
/* Check for overflow of dlen field */ /* Check for overflow of dlen field */
err = -EFBIG; err = -EFBIG;
if (size >= 1 << 12) if (size >= 1 << 12)
goto err1; goto err_release_expr;
if (nla[NFTA_RULE_USERDATA]) { if (nla[NFTA_RULE_USERDATA]) {
ulen = nla_len(nla[NFTA_RULE_USERDATA]); ulen = nla_len(nla[NFTA_RULE_USERDATA]);
...@@ -3366,7 +3372,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -3366,7 +3372,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
err = -ENOMEM; err = -ENOMEM;
rule = kzalloc(sizeof(*rule) + size + usize, GFP_KERNEL); rule = kzalloc(sizeof(*rule) + size + usize, GFP_KERNEL);
if (rule == NULL) if (rule == NULL)
goto err1; goto err_release_expr;
nft_activate_next(net, rule); nft_activate_next(net, rule);
...@@ -3385,7 +3391,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -3385,7 +3391,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
err = nf_tables_newexpr(&ctx, &expr_info[i], expr); err = nf_tables_newexpr(&ctx, &expr_info[i], expr);
if (err < 0) { if (err < 0) {
NL_SET_BAD_ATTR(extack, expr_info[i].attr); NL_SET_BAD_ATTR(extack, expr_info[i].attr);
goto err2; goto err_release_rule;
} }
if (expr_info[i].ops->validate) if (expr_info[i].ops->validate)
...@@ -3395,16 +3401,24 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -3395,16 +3401,24 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
expr = nft_expr_next(expr); expr = nft_expr_next(expr);
} }
if (chain->flags & NFT_CHAIN_HW_OFFLOAD) {
flow = nft_flow_rule_create(net, rule);
if (IS_ERR(flow)) {
err = PTR_ERR(flow);
goto err_release_rule;
}
}
if (info->nlh->nlmsg_flags & NLM_F_REPLACE) { if (info->nlh->nlmsg_flags & NLM_F_REPLACE) {
trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule); trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule);
if (trans == NULL) { if (trans == NULL) {
err = -ENOMEM; err = -ENOMEM;
goto err2; goto err_destroy_flow_rule;
} }
err = nft_delrule(&ctx, old_rule); err = nft_delrule(&ctx, old_rule);
if (err < 0) { if (err < 0) {
nft_trans_destroy(trans); nft_trans_destroy(trans);
goto err2; goto err_destroy_flow_rule;
} }
list_add_tail_rcu(&rule->list, &old_rule->list); list_add_tail_rcu(&rule->list, &old_rule->list);
...@@ -3412,7 +3426,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -3412,7 +3426,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule); trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule);
if (!trans) { if (!trans) {
err = -ENOMEM; err = -ENOMEM;
goto err2; goto err_destroy_flow_rule;
} }
if (info->nlh->nlmsg_flags & NLM_F_APPEND) { if (info->nlh->nlmsg_flags & NLM_F_APPEND) {
...@@ -3430,21 +3444,19 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -3430,21 +3444,19 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
kvfree(expr_info); kvfree(expr_info);
chain->use++; chain->use++;
if (flow)
nft_trans_flow_rule(trans) = flow;
if (nft_net->validate_state == NFT_VALIDATE_DO) if (nft_net->validate_state == NFT_VALIDATE_DO)
return nft_table_validate(net, table); return nft_table_validate(net, table);
if (chain->flags & NFT_CHAIN_HW_OFFLOAD) {
flow = nft_flow_rule_create(net, rule);
if (IS_ERR(flow))
return PTR_ERR(flow);
nft_trans_flow_rule(trans) = flow;
}
return 0; return 0;
err2:
err_destroy_flow_rule:
nft_flow_rule_destroy(flow);
err_release_rule:
nf_tables_rule_release(&ctx, rule); nf_tables_rule_release(&ctx, rule);
err1: err_release_expr:
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (expr_info[i].ops) { if (expr_info[i].ops) {
module_put(expr_info[i].ops->type->owner); module_put(expr_info[i].ops->type->owner);
...@@ -8839,11 +8851,16 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action) ...@@ -8839,11 +8851,16 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_rule_expr_deactivate(&trans->ctx, nft_rule_expr_deactivate(&trans->ctx,
nft_trans_rule(trans), nft_trans_rule(trans),
NFT_TRANS_ABORT); NFT_TRANS_ABORT);
if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
break; break;
case NFT_MSG_DELRULE: case NFT_MSG_DELRULE:
trans->ctx.chain->use++; trans->ctx.chain->use++;
nft_clear(trans->ctx.net, nft_trans_rule(trans)); nft_clear(trans->ctx.net, nft_trans_rule(trans));
nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans)); nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans));
if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
nft_trans_destroy(trans); nft_trans_destroy(trans);
break; break;
case NFT_MSG_NEWSET: case NFT_MSG_NEWSET:
......
...@@ -54,15 +54,10 @@ static void nft_flow_rule_transfer_vlan(struct nft_offload_ctx *ctx, ...@@ -54,15 +54,10 @@ static void nft_flow_rule_transfer_vlan(struct nft_offload_ctx *ctx,
struct nft_flow_rule *flow) struct nft_flow_rule *flow)
{ {
struct nft_flow_match *match = &flow->match; struct nft_flow_match *match = &flow->match;
struct nft_offload_ethertype ethertype; struct nft_offload_ethertype ethertype = {
.value = match->key.basic.n_proto,
if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL) && .mask = match->mask.basic.n_proto,
match->key.basic.n_proto != htons(ETH_P_8021Q) && };
match->key.basic.n_proto != htons(ETH_P_8021AD))
return;
ethertype.value = match->key.basic.n_proto;
ethertype.mask = match->mask.basic.n_proto;
if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_VLAN) && if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_VLAN) &&
(match->key.vlan.vlan_tpid == htons(ETH_P_8021Q) || (match->key.vlan.vlan_tpid == htons(ETH_P_8021Q) ||
...@@ -76,7 +71,9 @@ static void nft_flow_rule_transfer_vlan(struct nft_offload_ctx *ctx, ...@@ -76,7 +71,9 @@ static void nft_flow_rule_transfer_vlan(struct nft_offload_ctx *ctx,
match->dissector.offset[FLOW_DISSECTOR_KEY_CVLAN] = match->dissector.offset[FLOW_DISSECTOR_KEY_CVLAN] =
offsetof(struct nft_flow_key, cvlan); offsetof(struct nft_flow_key, cvlan);
match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_CVLAN); match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_CVLAN);
} else { } else if (match->dissector.used_keys & BIT(FLOW_DISSECTOR_KEY_BASIC) &&
(match->key.basic.n_proto == htons(ETH_P_8021Q) ||
match->key.basic.n_proto == htons(ETH_P_8021AD))) {
match->key.basic.n_proto = match->key.vlan.vlan_tpid; match->key.basic.n_proto = match->key.vlan.vlan_tpid;
match->mask.basic.n_proto = match->mask.vlan.vlan_tpid; match->mask.basic.n_proto = match->mask.vlan.vlan_tpid;
match->key.vlan.vlan_tpid = ethertype.value; match->key.vlan.vlan_tpid = ethertype.value;
...@@ -594,23 +591,6 @@ int nft_flow_rule_offload_commit(struct net *net) ...@@ -594,23 +591,6 @@ int nft_flow_rule_offload_commit(struct net *net)
} }
} }
list_for_each_entry(trans, &nft_net->commit_list, list) {
if (trans->ctx.family != NFPROTO_NETDEV)
continue;
switch (trans->msg_type) {
case NFT_MSG_NEWRULE:
case NFT_MSG_DELRULE:
if (!(trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD))
continue;
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
break;
default:
break;
}
}
return err; return err;
} }
......
...@@ -42,6 +42,9 @@ static void nft_exthdr_ipv6_eval(const struct nft_expr *expr, ...@@ -42,6 +42,9 @@ static void nft_exthdr_ipv6_eval(const struct nft_expr *expr,
unsigned int offset = 0; unsigned int offset = 0;
int err; int err;
if (pkt->skb->protocol != htons(ETH_P_IPV6))
goto err;
err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL); err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL);
if (priv->flags & NFT_EXTHDR_F_PRESENT) { if (priv->flags & NFT_EXTHDR_F_PRESENT) {
nft_reg_store8(dest, err >= 0); nft_reg_store8(dest, err >= 0);
......
...@@ -28,6 +28,11 @@ static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs, ...@@ -28,6 +28,11 @@ static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs,
struct nf_osf_data data; struct nf_osf_data data;
struct tcphdr _tcph; struct tcphdr _tcph;
if (pkt->tprot != IPPROTO_TCP) {
regs->verdict.code = NFT_BREAK;
return;
}
tcp = skb_header_pointer(skb, ip_hdrlen(skb), tcp = skb_header_pointer(skb, ip_hdrlen(skb),
sizeof(struct tcphdr), &_tcph); sizeof(struct tcphdr), &_tcph);
if (!tcp) { if (!tcp) {
......
...@@ -30,6 +30,12 @@ static void nft_tproxy_eval_v4(const struct nft_expr *expr, ...@@ -30,6 +30,12 @@ static void nft_tproxy_eval_v4(const struct nft_expr *expr,
__be16 tport = 0; __be16 tport = 0;
struct sock *sk; struct sock *sk;
if (pkt->tprot != IPPROTO_TCP &&
pkt->tprot != IPPROTO_UDP) {
regs->verdict.code = NFT_BREAK;
return;
}
hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr); hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
if (!hp) { if (!hp) {
regs->verdict.code = NFT_BREAK; regs->verdict.code = NFT_BREAK;
...@@ -91,7 +97,8 @@ static void nft_tproxy_eval_v6(const struct nft_expr *expr, ...@@ -91,7 +97,8 @@ static void nft_tproxy_eval_v6(const struct nft_expr *expr,
memset(&taddr, 0, sizeof(taddr)); memset(&taddr, 0, sizeof(taddr));
if (!pkt->tprot_set) { if (pkt->tprot != IPPROTO_TCP &&
pkt->tprot != IPPROTO_UDP) {
regs->verdict.code = NFT_BREAK; regs->verdict.code = NFT_BREAK;
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment