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) Use CONFIG_NF_TABLES_INET from seltests, not NF_TABLES_INET. From Naresh Kamboju. 2) Add a test to cover masquerading and redirect case, from Florian Westphal. 3) Two packets coming from the same socket may race to set up NAT, ending up with different tuples and the packet losing race being dropped. Update nf_conntrack_tuple_taken() to exercise clash resolution for this case. From Martynas Pumputis and Florian Westphal. 4) Unbind anonymous sets from the commit and abort path, this fixes a splat due to double set list removal/release in case that the transaction needs to be aborted. 5) Do not preserve original output interface for packets that are redirected in the output chain when ip6_route_me_harder() is called. Otherwise packets end up going not going to the loopback device. From Eli Cooper. 6) Fix bogus splat in nft_compat with CONFIG_REFCOUNT_FULL=y, this also simplifies the existing logic to deal with the list insertions of the xtables extensions. From Florian Westphal. Diffstat look rather larger than usual because of the new selftest, but Florian and I consider that having tests soon into the tree is good to improve coverage. If there's a different policy in this regard, please, let me know. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
f09bef61f1
@ -469,9 +469,7 @@ struct nft_set_binding {
|
||||
int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
|
||||
struct nft_set_binding *binding);
|
||||
void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
|
||||
struct nft_set_binding *binding);
|
||||
void nf_tables_rebind_set(const struct nft_ctx *ctx, struct nft_set *set,
|
||||
struct nft_set_binding *binding);
|
||||
struct nft_set_binding *binding, bool commit);
|
||||
void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set);
|
||||
|
||||
/**
|
||||
@ -721,6 +719,13 @@ struct nft_expr_type {
|
||||
#define NFT_EXPR_STATEFUL 0x1
|
||||
#define NFT_EXPR_GC 0x2
|
||||
|
||||
enum nft_trans_phase {
|
||||
NFT_TRANS_PREPARE,
|
||||
NFT_TRANS_ABORT,
|
||||
NFT_TRANS_COMMIT,
|
||||
NFT_TRANS_RELEASE
|
||||
};
|
||||
|
||||
/**
|
||||
* struct nft_expr_ops - nf_tables expression operations
|
||||
*
|
||||
@ -750,7 +755,8 @@ struct nft_expr_ops {
|
||||
void (*activate)(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr);
|
||||
void (*deactivate)(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr);
|
||||
const struct nft_expr *expr,
|
||||
enum nft_trans_phase phase);
|
||||
void (*destroy)(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr);
|
||||
void (*destroy_clone)(const struct nft_ctx *ctx,
|
||||
@ -1323,12 +1329,15 @@ struct nft_trans_rule {
|
||||
struct nft_trans_set {
|
||||
struct nft_set *set;
|
||||
u32 set_id;
|
||||
bool bound;
|
||||
};
|
||||
|
||||
#define nft_trans_set(trans) \
|
||||
(((struct nft_trans_set *)trans->data)->set)
|
||||
#define nft_trans_set_id(trans) \
|
||||
(((struct nft_trans_set *)trans->data)->set_id)
|
||||
#define nft_trans_set_bound(trans) \
|
||||
(((struct nft_trans_set *)trans->data)->bound)
|
||||
|
||||
struct nft_trans_chain {
|
||||
bool update;
|
||||
|
@ -23,9 +23,11 @@ int ip6_route_me_harder(struct net *net, struct sk_buff *skb)
|
||||
struct sock *sk = sk_to_full_sk(skb->sk);
|
||||
unsigned int hh_len;
|
||||
struct dst_entry *dst;
|
||||
int strict = (ipv6_addr_type(&iph->daddr) &
|
||||
(IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL));
|
||||
struct flowi6 fl6 = {
|
||||
.flowi6_oif = sk && sk->sk_bound_dev_if ? sk->sk_bound_dev_if :
|
||||
rt6_need_strict(&iph->daddr) ? skb_dst(skb)->dev->ifindex : 0,
|
||||
strict ? skb_dst(skb)->dev->ifindex : 0,
|
||||
.flowi6_mark = skb->mark,
|
||||
.flowi6_uid = sock_net_uid(net, sk),
|
||||
.daddr = iph->daddr,
|
||||
|
@ -1007,6 +1007,22 @@ nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple,
|
||||
}
|
||||
|
||||
if (nf_ct_key_equal(h, tuple, zone, net)) {
|
||||
/* Tuple is taken already, so caller will need to find
|
||||
* a new source port to use.
|
||||
*
|
||||
* Only exception:
|
||||
* If the *original tuples* are identical, then both
|
||||
* conntracks refer to the same flow.
|
||||
* This is a rare situation, it can occur e.g. when
|
||||
* more than one UDP packet is sent from same socket
|
||||
* in different threads.
|
||||
*
|
||||
* Let nf_ct_resolve_clash() deal with this later.
|
||||
*/
|
||||
if (nf_ct_tuple_equal(&ignored_conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
|
||||
&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple))
|
||||
continue;
|
||||
|
||||
NF_CT_STAT_INC_ATOMIC(net, found);
|
||||
rcu_read_unlock();
|
||||
return 1;
|
||||
|
@ -116,6 +116,23 @@ static void nft_trans_destroy(struct nft_trans *trans)
|
||||
kfree(trans);
|
||||
}
|
||||
|
||||
static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
|
||||
{
|
||||
struct net *net = ctx->net;
|
||||
struct nft_trans *trans;
|
||||
|
||||
if (!nft_set_is_anonymous(set))
|
||||
return;
|
||||
|
||||
list_for_each_entry_reverse(trans, &net->nft.commit_list, list) {
|
||||
if (trans->msg_type == NFT_MSG_NEWSET &&
|
||||
nft_trans_set(trans) == set) {
|
||||
nft_trans_set_bound(trans) = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int nf_tables_register_hook(struct net *net,
|
||||
const struct nft_table *table,
|
||||
struct nft_chain *chain)
|
||||
@ -211,18 +228,6 @@ static int nft_delchain(struct nft_ctx *ctx)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* either expr ops provide both activate/deactivate, or neither */
|
||||
static bool nft_expr_check_ops(const struct nft_expr_ops *ops)
|
||||
{
|
||||
if (!ops)
|
||||
return true;
|
||||
|
||||
if (WARN_ON_ONCE((!ops->activate ^ !ops->deactivate)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void nft_rule_expr_activate(const struct nft_ctx *ctx,
|
||||
struct nft_rule *rule)
|
||||
{
|
||||
@ -238,14 +243,15 @@ static void nft_rule_expr_activate(const struct nft_ctx *ctx,
|
||||
}
|
||||
|
||||
static void nft_rule_expr_deactivate(const struct nft_ctx *ctx,
|
||||
struct nft_rule *rule)
|
||||
struct nft_rule *rule,
|
||||
enum nft_trans_phase phase)
|
||||
{
|
||||
struct nft_expr *expr;
|
||||
|
||||
expr = nft_expr_first(rule);
|
||||
while (expr != nft_expr_last(rule) && expr->ops) {
|
||||
if (expr->ops->deactivate)
|
||||
expr->ops->deactivate(ctx, expr);
|
||||
expr->ops->deactivate(ctx, expr, phase);
|
||||
|
||||
expr = nft_expr_next(expr);
|
||||
}
|
||||
@ -296,7 +302,7 @@ static int nft_delrule(struct nft_ctx *ctx, struct nft_rule *rule)
|
||||
nft_trans_destroy(trans);
|
||||
return err;
|
||||
}
|
||||
nft_rule_expr_deactivate(ctx, rule);
|
||||
nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_PREPARE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1929,9 +1935,6 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk,
|
||||
*/
|
||||
int nft_register_expr(struct nft_expr_type *type)
|
||||
{
|
||||
if (!nft_expr_check_ops(type->ops))
|
||||
return -EINVAL;
|
||||
|
||||
nfnl_lock(NFNL_SUBSYS_NFTABLES);
|
||||
if (type->family == NFPROTO_UNSPEC)
|
||||
list_add_tail_rcu(&type->list, &nf_tables_expressions);
|
||||
@ -2079,10 +2082,6 @@ static int nf_tables_expr_parse(const struct nft_ctx *ctx,
|
||||
err = PTR_ERR(ops);
|
||||
goto err1;
|
||||
}
|
||||
if (!nft_expr_check_ops(ops)) {
|
||||
err = -EINVAL;
|
||||
goto err1;
|
||||
}
|
||||
} else
|
||||
ops = type->ops;
|
||||
|
||||
@ -2511,7 +2510,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
|
||||
static void nf_tables_rule_release(const struct nft_ctx *ctx,
|
||||
struct nft_rule *rule)
|
||||
{
|
||||
nft_rule_expr_deactivate(ctx, rule);
|
||||
nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE);
|
||||
nf_tables_rule_destroy(ctx, rule);
|
||||
}
|
||||
|
||||
@ -3708,39 +3707,30 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
|
||||
bind:
|
||||
binding->chain = ctx->chain;
|
||||
list_add_tail_rcu(&binding->list, &set->bindings);
|
||||
nft_set_trans_bind(ctx, set);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_tables_bind_set);
|
||||
|
||||
void nf_tables_rebind_set(const struct nft_ctx *ctx, struct nft_set *set,
|
||||
struct nft_set_binding *binding)
|
||||
{
|
||||
if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
|
||||
nft_is_active(ctx->net, set))
|
||||
list_add_tail_rcu(&set->list, &ctx->table->sets);
|
||||
|
||||
list_add_tail_rcu(&binding->list, &set->bindings);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_tables_rebind_set);
|
||||
|
||||
void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
|
||||
struct nft_set_binding *binding)
|
||||
struct nft_set_binding *binding, bool event)
|
||||
{
|
||||
list_del_rcu(&binding->list);
|
||||
|
||||
if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
|
||||
nft_is_active(ctx->net, set))
|
||||
if (list_empty(&set->bindings) && nft_set_is_anonymous(set)) {
|
||||
list_del_rcu(&set->list);
|
||||
if (event)
|
||||
nf_tables_set_notify(ctx, set, NFT_MSG_DELSET,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_tables_unbind_set);
|
||||
|
||||
void nf_tables_destroy_set(const struct nft_ctx *ctx, struct nft_set *set)
|
||||
{
|
||||
if (list_empty(&set->bindings) && nft_set_is_anonymous(set) &&
|
||||
nft_is_active(ctx->net, set)) {
|
||||
nf_tables_set_notify(ctx, set, NFT_MSG_DELSET, GFP_ATOMIC);
|
||||
if (list_empty(&set->bindings) && nft_set_is_anonymous(set))
|
||||
nft_set_destroy(set);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_tables_destroy_set);
|
||||
|
||||
@ -6535,6 +6525,9 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
|
||||
nf_tables_rule_notify(&trans->ctx,
|
||||
nft_trans_rule(trans),
|
||||
NFT_MSG_DELRULE);
|
||||
nft_rule_expr_deactivate(&trans->ctx,
|
||||
nft_trans_rule(trans),
|
||||
NFT_TRANS_COMMIT);
|
||||
break;
|
||||
case NFT_MSG_NEWSET:
|
||||
nft_clear(net, nft_trans_set(trans));
|
||||
@ -6621,7 +6614,8 @@ static void nf_tables_abort_release(struct nft_trans *trans)
|
||||
nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
|
||||
break;
|
||||
case NFT_MSG_NEWSET:
|
||||
nft_set_destroy(nft_trans_set(trans));
|
||||
if (!nft_trans_set_bound(trans))
|
||||
nft_set_destroy(nft_trans_set(trans));
|
||||
break;
|
||||
case NFT_MSG_NEWSETELEM:
|
||||
nft_set_elem_destroy(nft_trans_elem_set(trans),
|
||||
@ -6682,7 +6676,9 @@ static int __nf_tables_abort(struct net *net)
|
||||
case NFT_MSG_NEWRULE:
|
||||
trans->ctx.chain->use--;
|
||||
list_del_rcu(&nft_trans_rule(trans)->list);
|
||||
nft_rule_expr_deactivate(&trans->ctx, nft_trans_rule(trans));
|
||||
nft_rule_expr_deactivate(&trans->ctx,
|
||||
nft_trans_rule(trans),
|
||||
NFT_TRANS_ABORT);
|
||||
break;
|
||||
case NFT_MSG_DELRULE:
|
||||
trans->ctx.chain->use++;
|
||||
@ -6692,7 +6688,8 @@ static int __nf_tables_abort(struct net *net)
|
||||
break;
|
||||
case NFT_MSG_NEWSET:
|
||||
trans->ctx.table->use--;
|
||||
list_del_rcu(&nft_trans_set(trans)->list);
|
||||
if (!nft_trans_set_bound(trans))
|
||||
list_del_rcu(&nft_trans_set(trans)->list);
|
||||
break;
|
||||
case NFT_MSG_DELSET:
|
||||
trans->ctx.table->use++;
|
||||
|
@ -61,6 +61,21 @@ static struct nft_compat_net *nft_compat_pernet(struct net *net)
|
||||
return net_generic(net, nft_compat_net_id);
|
||||
}
|
||||
|
||||
static void nft_xt_get(struct nft_xt *xt)
|
||||
{
|
||||
/* refcount_inc() warns on 0 -> 1 transition, but we can't
|
||||
* init the reference count to 1 in .select_ops -- we can't
|
||||
* undo such an increase when another expression inside the same
|
||||
* rule fails afterwards.
|
||||
*/
|
||||
if (xt->listcnt == 0)
|
||||
refcount_set(&xt->refcnt, 1);
|
||||
else
|
||||
refcount_inc(&xt->refcnt);
|
||||
|
||||
xt->listcnt++;
|
||||
}
|
||||
|
||||
static bool nft_xt_put(struct nft_xt *xt)
|
||||
{
|
||||
if (refcount_dec_and_test(&xt->refcnt)) {
|
||||
@ -291,7 +306,7 @@ nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
||||
return -EINVAL;
|
||||
|
||||
nft_xt = container_of(expr->ops, struct nft_xt, ops);
|
||||
refcount_inc(&nft_xt->refcnt);
|
||||
nft_xt_get(nft_xt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -504,7 +519,7 @@ __nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
|
||||
return ret;
|
||||
|
||||
nft_xt = container_of(expr->ops, struct nft_xt, ops);
|
||||
refcount_inc(&nft_xt->refcnt);
|
||||
nft_xt_get(nft_xt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -558,41 +573,16 @@ nft_match_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
|
||||
__nft_match_destroy(ctx, expr, nft_expr_priv(expr));
|
||||
}
|
||||
|
||||
static void nft_compat_activate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr,
|
||||
struct list_head *h)
|
||||
{
|
||||
struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
|
||||
|
||||
if (xt->listcnt == 0)
|
||||
list_add(&xt->head, h);
|
||||
|
||||
xt->listcnt++;
|
||||
}
|
||||
|
||||
static void nft_compat_activate_mt(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
{
|
||||
struct nft_compat_net *cn = nft_compat_pernet(ctx->net);
|
||||
|
||||
nft_compat_activate(ctx, expr, &cn->nft_match_list);
|
||||
}
|
||||
|
||||
static void nft_compat_activate_tg(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
{
|
||||
struct nft_compat_net *cn = nft_compat_pernet(ctx->net);
|
||||
|
||||
nft_compat_activate(ctx, expr, &cn->nft_target_list);
|
||||
}
|
||||
|
||||
static void nft_compat_deactivate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
const struct nft_expr *expr,
|
||||
enum nft_trans_phase phase)
|
||||
{
|
||||
struct nft_xt *xt = container_of(expr->ops, struct nft_xt, ops);
|
||||
|
||||
if (--xt->listcnt == 0)
|
||||
list_del_init(&xt->head);
|
||||
if (phase == NFT_TRANS_ABORT || phase == NFT_TRANS_COMMIT) {
|
||||
if (--xt->listcnt == 0)
|
||||
list_del_init(&xt->head);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -848,7 +838,6 @@ nft_match_select_ops(const struct nft_ctx *ctx,
|
||||
nft_match->ops.eval = nft_match_eval;
|
||||
nft_match->ops.init = nft_match_init;
|
||||
nft_match->ops.destroy = nft_match_destroy;
|
||||
nft_match->ops.activate = nft_compat_activate_mt;
|
||||
nft_match->ops.deactivate = nft_compat_deactivate;
|
||||
nft_match->ops.dump = nft_match_dump;
|
||||
nft_match->ops.validate = nft_match_validate;
|
||||
@ -866,7 +855,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,
|
||||
|
||||
nft_match->ops.size = matchsize;
|
||||
|
||||
nft_match->listcnt = 1;
|
||||
nft_match->listcnt = 0;
|
||||
list_add(&nft_match->head, &cn->nft_match_list);
|
||||
|
||||
return &nft_match->ops;
|
||||
@ -953,7 +942,6 @@ nft_target_select_ops(const struct nft_ctx *ctx,
|
||||
nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize));
|
||||
nft_target->ops.init = nft_target_init;
|
||||
nft_target->ops.destroy = nft_target_destroy;
|
||||
nft_target->ops.activate = nft_compat_activate_tg;
|
||||
nft_target->ops.deactivate = nft_compat_deactivate;
|
||||
nft_target->ops.dump = nft_target_dump;
|
||||
nft_target->ops.validate = nft_target_validate;
|
||||
@ -964,7 +952,7 @@ nft_target_select_ops(const struct nft_ctx *ctx,
|
||||
else
|
||||
nft_target->ops.eval = nft_target_eval_xt;
|
||||
|
||||
nft_target->listcnt = 1;
|
||||
nft_target->listcnt = 0;
|
||||
list_add(&nft_target->head, &cn->nft_target_list);
|
||||
|
||||
return &nft_target->ops;
|
||||
|
@ -235,20 +235,17 @@ err1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void nft_dynset_activate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
{
|
||||
struct nft_dynset *priv = nft_expr_priv(expr);
|
||||
|
||||
nf_tables_rebind_set(ctx, priv->set, &priv->binding);
|
||||
}
|
||||
|
||||
static void nft_dynset_deactivate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
const struct nft_expr *expr,
|
||||
enum nft_trans_phase phase)
|
||||
{
|
||||
struct nft_dynset *priv = nft_expr_priv(expr);
|
||||
|
||||
nf_tables_unbind_set(ctx, priv->set, &priv->binding);
|
||||
if (phase == NFT_TRANS_PREPARE)
|
||||
return;
|
||||
|
||||
nf_tables_unbind_set(ctx, priv->set, &priv->binding,
|
||||
phase == NFT_TRANS_COMMIT);
|
||||
}
|
||||
|
||||
static void nft_dynset_destroy(const struct nft_ctx *ctx,
|
||||
@ -296,7 +293,6 @@ static const struct nft_expr_ops nft_dynset_ops = {
|
||||
.eval = nft_dynset_eval,
|
||||
.init = nft_dynset_init,
|
||||
.destroy = nft_dynset_destroy,
|
||||
.activate = nft_dynset_activate,
|
||||
.deactivate = nft_dynset_deactivate,
|
||||
.dump = nft_dynset_dump,
|
||||
};
|
||||
|
@ -72,10 +72,14 @@ static void nft_immediate_activate(const struct nft_ctx *ctx,
|
||||
}
|
||||
|
||||
static void nft_immediate_deactivate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
const struct nft_expr *expr,
|
||||
enum nft_trans_phase phase)
|
||||
{
|
||||
const struct nft_immediate_expr *priv = nft_expr_priv(expr);
|
||||
|
||||
if (phase == NFT_TRANS_COMMIT)
|
||||
return;
|
||||
|
||||
return nft_data_release(&priv->data, nft_dreg_to_type(priv->dreg));
|
||||
}
|
||||
|
||||
|
@ -121,20 +121,17 @@ static int nft_lookup_init(const struct nft_ctx *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nft_lookup_activate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
{
|
||||
struct nft_lookup *priv = nft_expr_priv(expr);
|
||||
|
||||
nf_tables_rebind_set(ctx, priv->set, &priv->binding);
|
||||
}
|
||||
|
||||
static void nft_lookup_deactivate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
const struct nft_expr *expr,
|
||||
enum nft_trans_phase phase)
|
||||
{
|
||||
struct nft_lookup *priv = nft_expr_priv(expr);
|
||||
|
||||
nf_tables_unbind_set(ctx, priv->set, &priv->binding);
|
||||
if (phase == NFT_TRANS_PREPARE)
|
||||
return;
|
||||
|
||||
nf_tables_unbind_set(ctx, priv->set, &priv->binding,
|
||||
phase == NFT_TRANS_COMMIT);
|
||||
}
|
||||
|
||||
static void nft_lookup_destroy(const struct nft_ctx *ctx,
|
||||
@ -225,7 +222,6 @@ static const struct nft_expr_ops nft_lookup_ops = {
|
||||
.size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)),
|
||||
.eval = nft_lookup_eval,
|
||||
.init = nft_lookup_init,
|
||||
.activate = nft_lookup_activate,
|
||||
.deactivate = nft_lookup_deactivate,
|
||||
.destroy = nft_lookup_destroy,
|
||||
.dump = nft_lookup_dump,
|
||||
|
@ -155,20 +155,17 @@ nla_put_failure:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void nft_objref_map_activate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
{
|
||||
struct nft_objref_map *priv = nft_expr_priv(expr);
|
||||
|
||||
nf_tables_rebind_set(ctx, priv->set, &priv->binding);
|
||||
}
|
||||
|
||||
static void nft_objref_map_deactivate(const struct nft_ctx *ctx,
|
||||
const struct nft_expr *expr)
|
||||
const struct nft_expr *expr,
|
||||
enum nft_trans_phase phase)
|
||||
{
|
||||
struct nft_objref_map *priv = nft_expr_priv(expr);
|
||||
|
||||
nf_tables_unbind_set(ctx, priv->set, &priv->binding);
|
||||
if (phase == NFT_TRANS_PREPARE)
|
||||
return;
|
||||
|
||||
nf_tables_unbind_set(ctx, priv->set, &priv->binding,
|
||||
phase == NFT_TRANS_COMMIT);
|
||||
}
|
||||
|
||||
static void nft_objref_map_destroy(const struct nft_ctx *ctx,
|
||||
@ -185,7 +182,6 @@ static const struct nft_expr_ops nft_objref_map_ops = {
|
||||
.size = NFT_EXPR_SIZE(sizeof(struct nft_objref_map)),
|
||||
.eval = nft_objref_map_eval,
|
||||
.init = nft_objref_map_init,
|
||||
.activate = nft_objref_map_activate,
|
||||
.deactivate = nft_objref_map_deactivate,
|
||||
.destroy = nft_objref_map_destroy,
|
||||
.dump = nft_objref_map_dump,
|
||||
|
@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Makefile for netfilter selftests
|
||||
|
||||
TEST_PROGS := nft_trans_stress.sh
|
||||
TEST_PROGS := nft_trans_stress.sh nft_nat.sh
|
||||
|
||||
include ../lib.mk
|
||||
|
@ -1,2 +1,2 @@
|
||||
CONFIG_NET_NS=y
|
||||
NF_TABLES_INET=y
|
||||
CONFIG_NF_TABLES_INET=y
|
||||
|
762
tools/testing/selftests/netfilter/nft_nat.sh
Executable file
762
tools/testing/selftests/netfilter/nft_nat.sh
Executable file
@ -0,0 +1,762 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This test is for basic NAT functionality: snat, dnat, redirect, masquerade.
|
||||
#
|
||||
|
||||
# Kselftest framework requirement - SKIP code is 4.
|
||||
ksft_skip=4
|
||||
ret=0
|
||||
|
||||
nft --version > /dev/null 2>&1
|
||||
if [ $? -ne 0 ];then
|
||||
echo "SKIP: Could not run test without nft tool"
|
||||
exit $ksft_skip
|
||||
fi
|
||||
|
||||
ip -Version > /dev/null 2>&1
|
||||
if [ $? -ne 0 ];then
|
||||
echo "SKIP: Could not run test without ip tool"
|
||||
exit $ksft_skip
|
||||
fi
|
||||
|
||||
ip netns add ns0
|
||||
ip netns add ns1
|
||||
ip netns add ns2
|
||||
|
||||
ip link add veth0 netns ns0 type veth peer name eth0 netns ns1
|
||||
ip link add veth1 netns ns0 type veth peer name eth0 netns ns2
|
||||
|
||||
ip -net ns0 link set lo up
|
||||
ip -net ns0 link set veth0 up
|
||||
ip -net ns0 addr add 10.0.1.1/24 dev veth0
|
||||
ip -net ns0 addr add dead:1::1/64 dev veth0
|
||||
|
||||
ip -net ns0 link set veth1 up
|
||||
ip -net ns0 addr add 10.0.2.1/24 dev veth1
|
||||
ip -net ns0 addr add dead:2::1/64 dev veth1
|
||||
|
||||
for i in 1 2; do
|
||||
ip -net ns$i link set lo up
|
||||
ip -net ns$i link set eth0 up
|
||||
ip -net ns$i addr add 10.0.$i.99/24 dev eth0
|
||||
ip -net ns$i route add default via 10.0.$i.1
|
||||
ip -net ns$i addr add dead:$i::99/64 dev eth0
|
||||
ip -net ns$i route add default via dead:$i::1
|
||||
done
|
||||
|
||||
bad_counter()
|
||||
{
|
||||
local ns=$1
|
||||
local counter=$2
|
||||
local expect=$3
|
||||
|
||||
echo "ERROR: $counter counter in $ns has unexpected value (expected $expect)" 1>&2
|
||||
ip netns exec $ns nft list counter inet filter $counter 1>&2
|
||||
}
|
||||
|
||||
check_counters()
|
||||
{
|
||||
ns=$1
|
||||
local lret=0
|
||||
|
||||
cnt=$(ip netns exec $ns nft list counter inet filter ns0in | grep -q "packets 1 bytes 84")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter $ns ns0in "packets 1 bytes 84"
|
||||
lret=1
|
||||
fi
|
||||
cnt=$(ip netns exec $ns nft list counter inet filter ns0out | grep -q "packets 1 bytes 84")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter $ns ns0out "packets 1 bytes 84"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
expect="packets 1 bytes 104"
|
||||
cnt=$(ip netns exec $ns nft list counter inet filter ns0in6 | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter $ns ns0in6 "$expect"
|
||||
lret=1
|
||||
fi
|
||||
cnt=$(ip netns exec $ns nft list counter inet filter ns0out6 | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter $ns ns0out6 "$expect"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
return $lret
|
||||
}
|
||||
|
||||
check_ns0_counters()
|
||||
{
|
||||
local ns=$1
|
||||
local lret=0
|
||||
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ns0in | grep -q "packets 0 bytes 0")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns0 ns0in "packets 0 bytes 0"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ns0in6 | grep -q "packets 0 bytes 0")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns0 ns0in6 "packets 0 bytes 0"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ns0out | grep -q "packets 0 bytes 0")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns0 ns0out "packets 0 bytes 0"
|
||||
lret=1
|
||||
fi
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ns0out6 | grep -q "packets 0 bytes 0")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns0 ns0out6 "packets 0 bytes 0"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
for dir in "in" "out" ; do
|
||||
expect="packets 1 bytes 84"
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ${ns}${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns0 $ns$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
expect="packets 1 bytes 104"
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ${ns}${dir}6 | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns0 $ns$dir6 "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
return $lret
|
||||
}
|
||||
|
||||
reset_counters()
|
||||
{
|
||||
for i in 0 1 2;do
|
||||
ip netns exec ns$i nft reset counters inet > /dev/null
|
||||
done
|
||||
}
|
||||
|
||||
test_local_dnat6()
|
||||
{
|
||||
local lret=0
|
||||
ip netns exec ns0 nft -f - <<EOF
|
||||
table ip6 nat {
|
||||
chain output {
|
||||
type nat hook output priority 0; policy accept;
|
||||
ip6 daddr dead:1::99 dnat to dead:2::99
|
||||
}
|
||||
}
|
||||
EOF
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "SKIP: Could not add add ip6 dnat hook"
|
||||
return $ksft_skip
|
||||
fi
|
||||
|
||||
# ping netns1, expect rewrite to netns2
|
||||
ip netns exec ns0 ping -q -c 1 dead:1::99 > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
lret=1
|
||||
echo "ERROR: ping6 failed"
|
||||
return $lret
|
||||
fi
|
||||
|
||||
expect="packets 0 bytes 0"
|
||||
for dir in "in6" "out6" ; do
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ns1${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns0 ns1$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
expect="packets 1 bytes 104"
|
||||
for dir in "in6" "out6" ; do
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns0 ns2$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
# expect 0 count in ns1
|
||||
expect="packets 0 bytes 0"
|
||||
for dir in "in6" "out6" ; do
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
# expect 1 packet in ns2
|
||||
expect="packets 1 bytes 104"
|
||||
for dir in "in6" "out6" ; do
|
||||
cnt=$(ip netns exec ns2 nft list counter inet filter ns0${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns2 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
test $lret -eq 0 && echo "PASS: ipv6 ping to ns1 was NATted to ns2"
|
||||
ip netns exec ns0 nft flush chain ip6 nat output
|
||||
|
||||
return $lret
|
||||
}
|
||||
|
||||
test_local_dnat()
|
||||
{
|
||||
local lret=0
|
||||
ip netns exec ns0 nft -f - <<EOF
|
||||
table ip nat {
|
||||
chain output {
|
||||
type nat hook output priority 0; policy accept;
|
||||
ip daddr 10.0.1.99 dnat to 10.0.2.99
|
||||
}
|
||||
}
|
||||
EOF
|
||||
# ping netns1, expect rewrite to netns2
|
||||
ip netns exec ns0 ping -q -c 1 10.0.1.99 > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
lret=1
|
||||
echo "ERROR: ping failed"
|
||||
return $lret
|
||||
fi
|
||||
|
||||
expect="packets 0 bytes 0"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ns1${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns0 ns1$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
expect="packets 1 bytes 84"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns0 ns2$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
# expect 0 count in ns1
|
||||
expect="packets 0 bytes 0"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
# expect 1 packet in ns2
|
||||
expect="packets 1 bytes 84"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns2 nft list counter inet filter ns0${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns2 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
test $lret -eq 0 && echo "PASS: ping to ns1 was NATted to ns2"
|
||||
|
||||
ip netns exec ns0 nft flush chain ip nat output
|
||||
|
||||
reset_counters
|
||||
ip netns exec ns0 ping -q -c 1 10.0.1.99 > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
lret=1
|
||||
echo "ERROR: ping failed"
|
||||
return $lret
|
||||
fi
|
||||
|
||||
expect="packets 1 bytes 84"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ns1${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns1$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
expect="packets 0 bytes 0"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns0 ns2$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
# expect 1 count in ns1
|
||||
expect="packets 1 bytes 84"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns0 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
# expect 0 packet in ns2
|
||||
expect="packets 0 bytes 0"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns2 nft list counter inet filter ns0${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns2 ns2$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
test $lret -eq 0 && echo "PASS: ping to ns1 OK after nat output chain flush"
|
||||
|
||||
return $lret
|
||||
}
|
||||
|
||||
|
||||
test_masquerade6()
|
||||
{
|
||||
local lret=0
|
||||
|
||||
ip netns exec ns0 sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
|
||||
|
||||
ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "ERROR: cannot ping ns1 from ns2 via ipv6"
|
||||
return 1
|
||||
lret=1
|
||||
fi
|
||||
|
||||
expect="packets 1 bytes 104"
|
||||
for dir in "in6" "out6" ; do
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns2$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns2 ns1$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
reset_counters
|
||||
|
||||
# add masquerading rule
|
||||
ip netns exec ns0 nft -f - <<EOF
|
||||
table ip6 nat {
|
||||
chain postrouting {
|
||||
type nat hook postrouting priority 0; policy accept;
|
||||
meta oif veth0 masquerade
|
||||
}
|
||||
}
|
||||
EOF
|
||||
ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "ERROR: cannot ping ns1 from ns2 with active ipv6 masquerading"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
# ns1 should have seen packets from ns0, due to masquerade
|
||||
expect="packets 1 bytes 104"
|
||||
for dir in "in6" "out6" ; do
|
||||
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns2 ns1$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
# ns1 should not have seen packets from ns2, due to masquerade
|
||||
expect="packets 0 bytes 0"
|
||||
for dir in "in6" "out6" ; do
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns2 ns1$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
ip netns exec ns0 nft flush chain ip6 nat postrouting
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR: Could not flush ip6 nat postrouting" 1>&2
|
||||
lret=1
|
||||
fi
|
||||
|
||||
test $lret -eq 0 && echo "PASS: IPv6 masquerade for ns2"
|
||||
|
||||
return $lret
|
||||
}
|
||||
|
||||
test_masquerade()
|
||||
{
|
||||
local lret=0
|
||||
|
||||
ip netns exec ns0 sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
|
||||
ip netns exec ns0 sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
|
||||
|
||||
ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "ERROR: canot ping ns1 from ns2"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
expect="packets 1 bytes 84"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns2$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns2 ns1$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
reset_counters
|
||||
|
||||
# add masquerading rule
|
||||
ip netns exec ns0 nft -f - <<EOF
|
||||
table ip nat {
|
||||
chain postrouting {
|
||||
type nat hook postrouting priority 0; policy accept;
|
||||
meta oif veth0 masquerade
|
||||
}
|
||||
}
|
||||
EOF
|
||||
ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "ERROR: cannot ping ns1 from ns2 with active ip masquerading"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
# ns1 should have seen packets from ns0, due to masquerade
|
||||
expect="packets 1 bytes 84"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns0${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns2 ns1$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
# ns1 should not have seen packets from ns2, due to masquerade
|
||||
expect="packets 0 bytes 0"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns2 ns1$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
ip netns exec ns0 nft flush chain ip nat postrouting
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR: Could not flush nat postrouting" 1>&2
|
||||
lret=1
|
||||
fi
|
||||
|
||||
test $lret -eq 0 && echo "PASS: IP masquerade for ns2"
|
||||
|
||||
return $lret
|
||||
}
|
||||
|
||||
test_redirect6()
|
||||
{
|
||||
local lret=0
|
||||
|
||||
ip netns exec ns0 sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
|
||||
|
||||
ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "ERROR: cannnot ping ns1 from ns2 via ipv6"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
expect="packets 1 bytes 104"
|
||||
for dir in "in6" "out6" ; do
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns2$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns2 ns1$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
reset_counters
|
||||
|
||||
# add redirect rule
|
||||
ip netns exec ns0 nft -f - <<EOF
|
||||
table ip6 nat {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority 0; policy accept;
|
||||
meta iif veth1 meta l4proto icmpv6 ip6 saddr dead:2::99 ip6 daddr dead:1::99 redirect
|
||||
}
|
||||
}
|
||||
EOF
|
||||
ip netns exec ns2 ping -q -c 1 dead:1::99 > /dev/null # ping ns2->ns1
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "ERROR: cannot ping ns1 from ns2 with active ip6 redirect"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
# ns1 should have seen no packets from ns2, due to redirection
|
||||
expect="packets 0 bytes 0"
|
||||
for dir in "in6" "out6" ; do
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
# ns0 should have seen packets from ns2, due to masquerade
|
||||
expect="packets 1 bytes 104"
|
||||
for dir in "in6" "out6" ; do
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
ip netns exec ns0 nft delete table ip6 nat
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR: Could not delete ip6 nat table" 1>&2
|
||||
lret=1
|
||||
fi
|
||||
|
||||
test $lret -eq 0 && echo "PASS: IPv6 redirection for ns2"
|
||||
|
||||
return $lret
|
||||
}
|
||||
|
||||
test_redirect()
|
||||
{
|
||||
local lret=0
|
||||
|
||||
ip netns exec ns0 sysctl net.ipv4.conf.veth0.forwarding=1 > /dev/null
|
||||
ip netns exec ns0 sysctl net.ipv4.conf.veth1.forwarding=1 > /dev/null
|
||||
|
||||
ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "ERROR: cannot ping ns1 from ns2"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
expect="packets 1 bytes 84"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns2$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
cnt=$(ip netns exec ns2 nft list counter inet filter ns1${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns2 ns1$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
reset_counters
|
||||
|
||||
# add redirect rule
|
||||
ip netns exec ns0 nft -f - <<EOF
|
||||
table ip nat {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority 0; policy accept;
|
||||
meta iif veth1 ip protocol icmp ip saddr 10.0.2.99 ip daddr 10.0.1.99 redirect
|
||||
}
|
||||
}
|
||||
EOF
|
||||
ip netns exec ns2 ping -q -c 1 10.0.1.99 > /dev/null # ping ns2->ns1
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "ERROR: cannot ping ns1 from ns2 with active ip redirect"
|
||||
lret=1
|
||||
fi
|
||||
|
||||
# ns1 should have seen no packets from ns2, due to redirection
|
||||
expect="packets 0 bytes 0"
|
||||
for dir in "in" "out" ; do
|
||||
|
||||
cnt=$(ip netns exec ns1 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
# ns0 should have seen packets from ns2, due to masquerade
|
||||
expect="packets 1 bytes 84"
|
||||
for dir in "in" "out" ; do
|
||||
cnt=$(ip netns exec ns0 nft list counter inet filter ns2${dir} | grep -q "$expect")
|
||||
if [ $? -ne 0 ]; then
|
||||
bad_counter ns1 ns0$dir "$expect"
|
||||
lret=1
|
||||
fi
|
||||
done
|
||||
|
||||
ip netns exec ns0 nft delete table ip nat
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR: Could not delete nat table" 1>&2
|
||||
lret=1
|
||||
fi
|
||||
|
||||
test $lret -eq 0 && echo "PASS: IP redirection for ns2"
|
||||
|
||||
return $lret
|
||||
}
|
||||
|
||||
|
||||
# ip netns exec ns0 ping -c 1 -q 10.0.$i.99
|
||||
for i in 0 1 2; do
|
||||
ip netns exec ns$i nft -f - <<EOF
|
||||
table inet filter {
|
||||
counter ns0in {}
|
||||
counter ns1in {}
|
||||
counter ns2in {}
|
||||
|
||||
counter ns0out {}
|
||||
counter ns1out {}
|
||||
counter ns2out {}
|
||||
|
||||
counter ns0in6 {}
|
||||
counter ns1in6 {}
|
||||
counter ns2in6 {}
|
||||
|
||||
counter ns0out6 {}
|
||||
counter ns1out6 {}
|
||||
counter ns2out6 {}
|
||||
|
||||
map nsincounter {
|
||||
type ipv4_addr : counter
|
||||
elements = { 10.0.1.1 : "ns0in",
|
||||
10.0.2.1 : "ns0in",
|
||||
10.0.1.99 : "ns1in",
|
||||
10.0.2.99 : "ns2in" }
|
||||
}
|
||||
|
||||
map nsincounter6 {
|
||||
type ipv6_addr : counter
|
||||
elements = { dead:1::1 : "ns0in6",
|
||||
dead:2::1 : "ns0in6",
|
||||
dead:1::99 : "ns1in6",
|
||||
dead:2::99 : "ns2in6" }
|
||||
}
|
||||
|
||||
map nsoutcounter {
|
||||
type ipv4_addr : counter
|
||||
elements = { 10.0.1.1 : "ns0out",
|
||||
10.0.2.1 : "ns0out",
|
||||
10.0.1.99: "ns1out",
|
||||
10.0.2.99: "ns2out" }
|
||||
}
|
||||
|
||||
map nsoutcounter6 {
|
||||
type ipv6_addr : counter
|
||||
elements = { dead:1::1 : "ns0out6",
|
||||
dead:2::1 : "ns0out6",
|
||||
dead:1::99 : "ns1out6",
|
||||
dead:2::99 : "ns2out6" }
|
||||
}
|
||||
|
||||
chain input {
|
||||
type filter hook input priority 0; policy accept;
|
||||
counter name ip saddr map @nsincounter
|
||||
icmpv6 type { "echo-request", "echo-reply" } counter name ip6 saddr map @nsincounter6
|
||||
}
|
||||
chain output {
|
||||
type filter hook output priority 0; policy accept;
|
||||
counter name ip daddr map @nsoutcounter
|
||||
icmpv6 type { "echo-request", "echo-reply" } counter name ip6 daddr map @nsoutcounter6
|
||||
}
|
||||
}
|
||||
EOF
|
||||
done
|
||||
|
||||
sleep 3
|
||||
# test basic connectivity
|
||||
for i in 1 2; do
|
||||
ip netns exec ns0 ping -c 1 -q 10.0.$i.99 > /dev/null
|
||||
if [ $? -ne 0 ];then
|
||||
echo "ERROR: Could not reach other namespace(s)" 1>&2
|
||||
ret=1
|
||||
fi
|
||||
|
||||
ip netns exec ns0 ping -c 1 -q dead:$i::99 > /dev/null
|
||||
if [ $? -ne 0 ];then
|
||||
echo "ERROR: Could not reach other namespace(s) via ipv6" 1>&2
|
||||
ret=1
|
||||
fi
|
||||
check_counters ns$i
|
||||
if [ $? -ne 0 ]; then
|
||||
ret=1
|
||||
fi
|
||||
|
||||
check_ns0_counters ns$i
|
||||
if [ $? -ne 0 ]; then
|
||||
ret=1
|
||||
fi
|
||||
reset_counters
|
||||
done
|
||||
|
||||
if [ $ret -eq 0 ];then
|
||||
echo "PASS: netns routing/connectivity: ns0 can reach ns1 and ns2"
|
||||
fi
|
||||
|
||||
reset_counters
|
||||
test_local_dnat
|
||||
test_local_dnat6
|
||||
|
||||
reset_counters
|
||||
test_masquerade
|
||||
test_masquerade6
|
||||
|
||||
reset_counters
|
||||
test_redirect
|
||||
test_redirect6
|
||||
|
||||
for i in 0 1 2; do ip netns del ns$i;done
|
||||
|
||||
exit $ret
|
Loading…
Reference in New Issue
Block a user