@@ -100,10 +100,24 @@ enum nft_rule_attributes {
NFTA_RULE_HANDLE,
NFTA_RULE_EXPRESSIONS,
NFTA_RULE_FLAGS,
+ NFTA_RULE_COMPAT,
__NFTA_RULE_MAX
};
#define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1)
+enum nft_rule_compat_flags {
+ NFT_RULE_COMPAT_F_INV = (1 << 1),
+ NFT_RULE_COMPAT_F_MASK = NFT_RULE_COMPAT_F_INV,
+};
+
+enum nft_rule_compat_attributes {
+ NFTA_RULE_COMPAT_UNSPEC,
+ NFTA_RULE_COMPAT_PROTO,
+ NFTA_RULE_COMPAT_FLAGS,
+ __NFTA_RULE_COMPAT_MAX
+};
+#define NFTA_RULE_COMPAT_MAX (__NFTA_RULE_COMPAT_MAX - 1)
+
/**
* enum nft_set_flags - nf_tables set flags
*
@@ -74,6 +74,7 @@ static inline void nft_data_debug(const struct nft_data *data)
* @afi: address family info
* @table: the table the chain is contained in
* @chain: the chain the rule is contained in
+ * @nla: netlink attributes
*/
struct nft_ctx {
struct net *net;
@@ -82,6 +83,7 @@ struct nft_ctx {
const struct nft_af_info *afi;
const struct nft_table *table;
const struct nft_chain *chain;
+ const struct nlattr * const *nla;
};
struct nft_data_desc {
@@ -1037,7 +1037,8 @@ static void nft_ctx_init(struct nft_ctx *ctx,
const struct nlmsghdr *nlh,
const struct nft_af_info *afi,
const struct nft_table *table,
- const struct nft_chain *chain)
+ const struct nft_chain *chain,
+ const struct nlattr * const *nla)
{
ctx->net = sock_net(skb->sk);
ctx->skb = skb;
@@ -1045,6 +1046,7 @@ static void nft_ctx_init(struct nft_ctx *ctx,
ctx->afi = afi;
ctx->table = table;
ctx->chain = chain;
+ ctx->nla = nla;
}
/*
@@ -1252,6 +1254,7 @@ static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = {
[NFTA_RULE_HANDLE] = { .type = NLA_U64 },
[NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED },
[NFTA_RULE_FLAGS] = { .type = NLA_U32 },
+ [NFTA_RULE_COMPAT] = { .type = NLA_NESTED },
};
static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
@@ -1555,7 +1558,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
handle = nf_tables_alloc_handle(table);
}
- nft_ctx_init(&ctx, skb, nlh, afi, table, chain);
+ nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
n = 0;
size = 0;
@@ -1676,7 +1679,7 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
if (IS_ERR(chain))
return PTR_ERR(chain);
- nft_ctx_init(&ctx, skb, nlh, afi, table, chain);
+ nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
if (nla[NFTA_RULE_FLAGS]) {
flags = ntohl(nla_get_be32(nla[NFTA_RULE_FLAGS]));
@@ -1885,7 +1888,7 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
if (IS_ERR(table))
return PTR_ERR(table);
- nft_ctx_init(ctx, skb, nlh, afi, table, NULL);
+ nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
return 0;
}
@@ -2168,7 +2171,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
if (IS_ERR(table))
return PTR_ERR(table);
- nft_ctx_init(&ctx, skb, nlh, afi, table, NULL);
+ nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
set = nf_tables_set_lookup(table, nla[NFTA_SET_NAME]);
if (IS_ERR(set)) {
@@ -2353,7 +2356,7 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
if (IS_ERR(table))
return PTR_ERR(table);
- nft_ctx_init(ctx, skb, nlh, afi, table, NULL);
+ nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
return 0;
}
@@ -107,6 +107,34 @@ static inline int nft_compat_target_offset(struct xt_target *target)
#endif
}
+static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1] = {
+ [NFTA_RULE_COMPAT_PROTO] = { .type = NLA_U32 },
+ [NFTA_RULE_COMPAT_FLAGS] = { .type = NLA_U32 },
+};
+
+static u8 nft_parse_compat(const struct nlattr *attr, bool *inv)
+{
+ struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1];
+ u32 flags;
+ int err;
+
+ err = nla_parse_nested(tb, NFTA_RULE_COMPAT_MAX, attr,
+ nft_rule_compat_policy);
+ if (err < 0)
+ return err;
+
+ if (!tb[NFTA_RULE_COMPAT_PROTO] || !tb[NFTA_RULE_COMPAT_FLAGS])
+ return -EINVAL;
+
+ flags = ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_FLAGS]));
+ if (flags & ~NFT_RULE_COMPAT_F_MASK)
+ return -EINVAL;
+ if (flags & NFT_RULE_COMPAT_F_INV)
+ *inv = true;
+
+ return ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_PROTO]));
+}
+
static int
nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nlattr * const tb[])
@@ -115,14 +143,18 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
struct xt_target *target = expr->ops->data;
struct xt_tgchk_param par;
size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO]));
+ u8 proto = 0;
+ bool inv = false;
int ret;
target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info);
nft_target_set_tgchk_param(&par, ctx, target, info);
- /* FIXME: not checking protocol and inversion */
- ret = xt_check_target(&par, size, 0, false);
+ if (ctx->nla[NFTA_RULE_COMPAT])
+ proto = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &inv);
+
+ ret = xt_check_target(&par, size, proto, inv);
if (ret < 0)
goto err;
@@ -289,14 +321,18 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
struct xt_match *match = expr->ops->data;
struct xt_mtchk_param par;
size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO]));
+ u8 proto = 0;
+ bool inv = false;
int ret;
match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info);
nft_match_set_mtchk_param(&par, ctx, match, info);
- /* FIXME: not checking protocol and inversion, do this in userspace */
- ret = xt_check_match(&par, size, 0, false);
+ if (ctx->nla[NFTA_RULE_COMPAT])
+ proto = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &inv);
+
+ ret = xt_check_match(&par, size, proto, inv);
if (ret < 0)
goto err;