diff --git a/.gitignore b/.gitignore index 242ed661..da5f711d 100755 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,7 @@ Thumbs.db .settings .metadata Debug/ +.vscode/ # autoconf ########## diff --git a/src/mod/common/kernel_hook_iptables.c b/src/mod/common/kernel_hook_iptables.c index ba9d82d8..f27a8f7c 100644 --- a/src/mod/common/kernel_hook_iptables.c +++ b/src/mod/common/kernel_hook_iptables.c @@ -2,6 +2,8 @@ #include "mod/common/kernel_hook.h" +#include +#include #include "common/iptables.h" #include "mod/common/core.h" #include "mod/common/log.h" @@ -66,16 +68,22 @@ static struct net *action_param_net(const struct xt_action_param *param) return param->state->net; } -static unsigned int verdict2iptables(verdict result, bool enable_debug) +/* + * @jool: The active instance, or NULL when no instance was found. When NULL + * no debug logging is emitted (there is no debug flag to consult and no + * instance context to print). + */ +static unsigned int verdict2iptables(verdict result, struct xlator *jool) { switch (result) { case VERDICT_STOLEN: + __log_debug(jool, "Packet stolen (translated successfully)."); return NF_STOLEN; /* This is the happy path. */ case VERDICT_UNTRANSLATABLE: - ____log_debug(enable_debug, "Returning packet to the iptables chain."); + __log_debug(jool, "Returning packet to the iptables chain."); return XT_CONTINUE; case VERDICT_DROP: - ____log_debug(enable_debug, "Jool: Dropping packet."); + __log_debug(jool, "Dropping packet."); return NF_DROP; case VERDICT_CONTINUE: WARN(true, "At time of writing, Jool core is not supposed to return CONTINUE after the packet is handled.\n" @@ -96,7 +104,7 @@ unsigned int target_ipv6(struct sk_buff *skb, { struct xlation *state; verdict result; - bool enable_debug = false; + unsigned int xt_result; state = xlation_create(NULL); if (!state) @@ -104,15 +112,23 @@ unsigned int target_ipv6(struct sk_buff *skb, result = find_instance(action_param_net(param), param->targinfo, &state->jool); - if (result != VERDICT_CONTINUE) - goto end; - enable_debug = state->jool.globals.debug; + if (result != VERDICT_CONTINUE) { + xlation_destroy(state); + return verdict2iptables(result, NULL); + } + + log_debug(state, + "target_ipv6: src=%pI6c dst=%pI6c dev=%s", + &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + skb->dev ? skb->dev->name : "(none)"); result = core_6to4(skb, state); + xt_result = verdict2iptables(result, &state->jool); xlator_put(&state->jool); -end: xlation_destroy(state); - return verdict2iptables(result, enable_debug); + xlation_destroy(state); + return xt_result; } EXPORT_SYMBOL_GPL(target_ipv6); @@ -125,7 +141,7 @@ unsigned int target_ipv4(struct sk_buff *skb, { struct xlation *state; verdict result; - bool enable_debug = false; + unsigned int xt_result; state = xlation_create(NULL); if (!state) @@ -133,15 +149,23 @@ unsigned int target_ipv4(struct sk_buff *skb, result = find_instance(action_param_net(param), param->targinfo, &state->jool); - if (result != VERDICT_CONTINUE) - goto end; - enable_debug = state->jool.globals.debug; + if (result != VERDICT_CONTINUE) { + xlation_destroy(state); + return verdict2iptables(result, NULL); + } + + log_debug(state, + "target_ipv4: src=%pI4 dst=%pI4 dev=%s", + &ip_hdr(skb)->saddr, + &ip_hdr(skb)->daddr, + skb->dev ? skb->dev->name : "(none)"); result = core_4to6(skb, state); + xt_result = verdict2iptables(result, &state->jool); xlator_put(&state->jool); -end: xlation_destroy(state); - return verdict2iptables(result, enable_debug); + xlation_destroy(state); + return xt_result; } EXPORT_SYMBOL_GPL(target_ipv4); diff --git a/src/mod/common/kernel_hook_netfilter.c b/src/mod/common/kernel_hook_netfilter.c index b582fdfe..38607541 100644 --- a/src/mod/common/kernel_hook_netfilter.c +++ b/src/mod/common/kernel_hook_netfilter.c @@ -1,15 +1,18 @@ #include "mod/common/kernel_hook.h" +#include +#include #include "mod/common/log.h" #include "mod/common/core.h" /* #pragma GCC diagnostic error "-Wframe-larger-than=1" */ -static verdict find_instance(struct sk_buff *skb, struct xlator *result) +static verdict find_instance(struct sk_buff *skb, xlator_type xt, + struct xlator *result) { int error; - error = xlator_find_netfilter(dev_net(skb->dev), result); + error = xlator_find_netfilter(dev_net(skb->dev), xt, result); switch (error) { case 0: return VERDICT_CONTINUE; @@ -30,16 +33,22 @@ static verdict find_instance(struct sk_buff *skb, struct xlator *result) return VERDICT_UNTRANSLATABLE; } -static unsigned int verdict2netfilter(verdict result, bool enable_debug) +/* + * @jool: The active instance, or NULL when no instance was found. When NULL + * no debug logging is emitted (there is no debug flag to consult and no + * instance context to print). + */ +static unsigned int verdict2netfilter(verdict result, struct xlator *jool) { switch (result) { case VERDICT_STOLEN: + __log_debug(jool, "Packet stolen (translated successfully)."); return NF_STOLEN; /* This is the happy path. */ case VERDICT_UNTRANSLATABLE: - ____log_debug(enable_debug, "Returning the packet to the kernel."); + __log_debug(jool, "Returning the packet to the kernel."); return NF_ACCEPT; case VERDICT_DROP: - ____log_debug(enable_debug, "Dropping packet."); + __log_debug(jool, "Dropping packet."); return NF_DROP; case VERDICT_CONTINUE: WARN(true, "At time of writing, Jool core is not supposed to return CONTINUE after the packet is handled.\n" @@ -60,22 +69,33 @@ unsigned int hook_ipv6(void *priv, struct sk_buff *skb, { struct xlation *state; verdict result; - bool enable_debug = false; + unsigned int nf_result; state = xlation_create(NULL); if (!state) return NF_DROP; - result = find_instance(skb, &state->jool); - if (result != VERDICT_CONTINUE) - goto end; - enable_debug = state->jool.globals.debug; + { + xlator_type xt = (xlator_type)(uintptr_t)priv; + result = find_instance(skb, xt, &state->jool); + } + if (result != VERDICT_CONTINUE) { + xlation_destroy(state); + return verdict2netfilter(result, NULL); + } + + log_debug(state, + "hook_ipv6: src=%pI6c dst=%pI6c dev=%s", + &ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + skb->dev ? skb->dev->name : "(none)"); result = core_6to4(skb, state); + nf_result = verdict2netfilter(result, &state->jool); xlator_put(&state->jool); -end: xlation_destroy(state); - return verdict2netfilter(result, enable_debug); + xlation_destroy(state); + return nf_result; } EXPORT_SYMBOL_GPL(hook_ipv6); @@ -88,21 +108,32 @@ unsigned int hook_ipv4(void *priv, struct sk_buff *skb, { struct xlation *state; verdict result; - bool enable_debug = false; + unsigned int nf_result; state = xlation_create(NULL); if (!state) return NF_DROP; - result = find_instance(skb, &state->jool); - if (result != VERDICT_CONTINUE) - goto end; - enable_debug = state->jool.globals.debug; + { + xlator_type xt = (xlator_type)(uintptr_t)priv; + result = find_instance(skb, xt, &state->jool); + } + if (result != VERDICT_CONTINUE) { + xlation_destroy(state); + return verdict2netfilter(result, NULL); + } + + log_debug(state, + "hook_ipv4: src=%pI4 dst=%pI4 dev=%s", + &ip_hdr(skb)->saddr, + &ip_hdr(skb)->daddr, + skb->dev ? skb->dev->name : "(none)"); result = core_4to6(skb, state); + nf_result = verdict2netfilter(result, &state->jool); xlator_put(&state->jool); -end: xlation_destroy(state); - return verdict2netfilter(result, enable_debug); + xlation_destroy(state); + return nf_result; } EXPORT_SYMBOL_GPL(hook_ipv4); diff --git a/src/mod/common/rfc7915/common.c b/src/mod/common/rfc7915/common.c index c32fca32..232b9fe3 100644 --- a/src/mod/common/rfc7915/common.c +++ b/src/mod/common/rfc7915/common.c @@ -195,18 +195,23 @@ verdict ttpcomm_translate_inner_packet(struct xlation *state, result = steps->xlat_inner_l3(state); if (result == VERDICT_UNTRANSLATABLE) { /* - * Accepting because of an inner packet doesn't make sense. - * Also we couldn't have translated this inner packet. + * This instance cannot translate the inner packet's L3 header + * (e.g. the address does not belong to this translator's pool). + * Return UNTRANSLATABLE so the packet is passed to the kernel + * (NF_ACCEPT) and another Jool instance in the hook chain (e.g. + * a SIIT instance co-existing with this NAT64) gets a chance to + * handle the ICMP error. */ - result = VERDICT_DROP; goto end; } if (result != VERDICT_CONTINUE) goto end; result = xlat_l4_function(state, steps); - if (result == VERDICT_UNTRANSLATABLE) - result = VERDICT_DROP; + /* + * If VERDICT_UNTRANSLATABLE, propagate it unchanged (same rationale as + * the xlat_inner_l3 case above: let the next Jool instance try). + */ end: restore_outer_packet(state, &bkp, true); diff --git a/src/mod/common/xlator.c b/src/mod/common/xlator.c index e0012be8..0c77a7ec 100644 --- a/src/mod/common/xlator.c +++ b/src/mod/common/xlator.c @@ -35,6 +35,14 @@ static struct nf_hook_ops netfilter_hooks[] = { }, }; +/* + * Priority offset added to SIIT instances so they always run after NAT64 + * within the same PRE_ROUTING hook chain. A value of 1 is sufficient since + * both translators share the same hook point and a higher priority number + * means the hook runs later. + */ +#define SIIT_PRIORITY_OFFSET 1 + /** * An xlator, except it's the database node version. */ @@ -442,6 +450,22 @@ static int __xlator_add(struct jool_instance *new, struct xlator *result) memcpy(ops, netfilter_hooks, sizeof(netfilter_hooks)); + /* Tag each hook op with the translator type so the hook callback + * can look up the correct instance (NAT64 vs SIIT). Adjust + * priority so NAT64 (unchanged) always runs before SIIT + * (+1 offset) within the same PRE_ROUTING hook chain. */ + { + int i; + xlator_type xt = xlator_flags2xt(new->jool.flags); + int prio_delta = (xt & XT_NAT64) ? 0 : SIIT_PRIORITY_OFFSET; + + for (i = 0; i < ARRAY_SIZE(netfilter_hooks); i++) { + ops[i].priv = (void *)(uintptr_t)xt; + ops[i].priority = netfilter_hooks[i].priority + + prio_delta; + } + } + error = nf_register_net_hooks(new->jool.ns, ops, ARRAY_SIZE(netfilter_hooks)); if (error) { @@ -832,7 +856,7 @@ int xlator_find_current(const char *iname, xlator_flags flags, return error; } -int xlator_find_netfilter(struct net *ns, struct xlator *result) +int xlator_find_netfilter(struct net *ns, xlator_type xt, struct xlator *result) { struct list_head *list; struct jool_instance *instance; @@ -841,7 +865,8 @@ int xlator_find_netfilter(struct net *ns, struct xlator *result) list = rcu_dereference_bh(netfilter_instances); list_for_each_entry_rcu(instance, list, list_hook) { - if (ns == instance->jool.ns) { + if (ns == instance->jool.ns + && xlator_flags2xt(instance->jool.flags) == xt) { xlator_get(&instance->jool); memcpy(result, &instance->jool, sizeof(*result)); rcu_read_unlock_bh(); diff --git a/src/mod/common/xlator.h b/src/mod/common/xlator.h index 37f451cc..e561dff1 100644 --- a/src/mod/common/xlator.h +++ b/src/mod/common/xlator.h @@ -72,7 +72,7 @@ int xlator_find(struct net *ns, xlator_flags flags, const char *iname, struct xlator *result); int xlator_find_current(const char *iname, xlator_flags flags, struct xlator *result); -int xlator_find_netfilter(struct net *ns, struct xlator *result); +int xlator_find_netfilter(struct net *ns, xlator_type xt, struct xlator *result); void xlator_put(struct xlator *instance); typedef int (*xlator_foreach_cb)(struct xlator *, void *);