Commit ebddb140 authored by Xin Long's avatar Xin Long Committed by David S. Miller

net: move the nat function to nf_nat_ovs for ovs and tc

There are two nat functions are nearly the same in both OVS and
TC code, (ovs_)ct_nat_execute() and ovs_ct_nat/tcf_ct_act_nat().

This patch creates nf_nat_ovs.c under netfilter and moves them
there then exports nf_ct_nat() so that it can be shared by both
OVS and TC, and keeps the nat (type) check and nat flag update
in OVS and TC's own place, as these parts are different between
OVS and TC.

Note that in OVS nat function it was using skb->protocol to get
the proto as it already skips vlans in key_extract(), while it
doesn't in TC, and TC has to call skb_protocol() to get proto.
So in nf_ct_nat_execute(), we keep using skb_protocol() which
works for both OVS and TC contrack.
Signed-off-by: default avatarXin Long <lucien.xin@gmail.com>
Acked-by: default avatarAaron Conole <aconole@redhat.com>
Acked-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0564c3e5
...@@ -104,6 +104,10 @@ unsigned int ...@@ -104,6 +104,10 @@ unsigned int
nf_nat_inet_fn(void *priv, struct sk_buff *skb, nf_nat_inet_fn(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state); const struct nf_hook_state *state);
int nf_ct_nat(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo, int *action,
const struct nf_nat_range2 *range, bool commit);
static inline int nf_nat_initialized(const struct nf_conn *ct, static inline int nf_nat_initialized(const struct nf_conn *ct,
enum nf_nat_manip_type manip) enum nf_nat_manip_type manip)
{ {
......
...@@ -459,6 +459,9 @@ config NF_NAT_REDIRECT ...@@ -459,6 +459,9 @@ config NF_NAT_REDIRECT
config NF_NAT_MASQUERADE config NF_NAT_MASQUERADE
bool bool
config NF_NAT_OVS
bool
config NETFILTER_SYNPROXY config NETFILTER_SYNPROXY
tristate tristate
......
...@@ -59,6 +59,7 @@ obj-$(CONFIG_NF_LOG_SYSLOG) += nf_log_syslog.o ...@@ -59,6 +59,7 @@ obj-$(CONFIG_NF_LOG_SYSLOG) += nf_log_syslog.o
obj-$(CONFIG_NF_NAT) += nf_nat.o obj-$(CONFIG_NF_NAT) += nf_nat.o
nf_nat-$(CONFIG_NF_NAT_REDIRECT) += nf_nat_redirect.o nf_nat-$(CONFIG_NF_NAT_REDIRECT) += nf_nat_redirect.o
nf_nat-$(CONFIG_NF_NAT_MASQUERADE) += nf_nat_masquerade.o nf_nat-$(CONFIG_NF_NAT_MASQUERADE) += nf_nat_masquerade.o
nf_nat-$(CONFIG_NF_NAT_OVS) += nf_nat_ovs.o
ifeq ($(CONFIG_NF_NAT),m) ifeq ($(CONFIG_NF_NAT),m)
nf_nat-$(CONFIG_DEBUG_INFO_BTF_MODULES) += nf_nat_bpf.o nf_nat-$(CONFIG_DEBUG_INFO_BTF_MODULES) += nf_nat_bpf.o
......
// SPDX-License-Identifier: GPL-2.0-only
/* Support nat functions for openvswitch and used by OVS and TC conntrack. */
#include <net/netfilter/nf_nat.h>
/* Modelled after nf_nat_ipv[46]_fn().
* range is only used for new, uninitialized NAT state.
* Returns either NF_ACCEPT or NF_DROP.
*/
static int nf_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo, int *action,
const struct nf_nat_range2 *range,
enum nf_nat_manip_type maniptype)
{
__be16 proto = skb_protocol(skb, true);
int hooknum, err = NF_ACCEPT;
/* See HOOK2MANIP(). */
if (maniptype == NF_NAT_MANIP_SRC)
hooknum = NF_INET_LOCAL_IN; /* Source NAT */
else
hooknum = NF_INET_LOCAL_OUT; /* Destination NAT */
switch (ctinfo) {
case IP_CT_RELATED:
case IP_CT_RELATED_REPLY:
if (proto == htons(ETH_P_IP) &&
ip_hdr(skb)->protocol == IPPROTO_ICMP) {
if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
hooknum))
err = NF_DROP;
goto out;
} else if (IS_ENABLED(CONFIG_IPV6) && proto == htons(ETH_P_IPV6)) {
__be16 frag_off;
u8 nexthdr = ipv6_hdr(skb)->nexthdr;
int hdrlen = ipv6_skip_exthdr(skb,
sizeof(struct ipv6hdr),
&nexthdr, &frag_off);
if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
if (!nf_nat_icmpv6_reply_translation(skb, ct,
ctinfo,
hooknum,
hdrlen))
err = NF_DROP;
goto out;
}
}
/* Non-ICMP, fall thru to initialize if needed. */
fallthrough;
case IP_CT_NEW:
/* Seen it before? This can happen for loopback, retrans,
* or local packets.
*/
if (!nf_nat_initialized(ct, maniptype)) {
/* Initialize according to the NAT action. */
err = (range && range->flags & NF_NAT_RANGE_MAP_IPS)
/* Action is set up to establish a new
* mapping.
*/
? nf_nat_setup_info(ct, range, maniptype)
: nf_nat_alloc_null_binding(ct, hooknum);
if (err != NF_ACCEPT)
goto out;
}
break;
case IP_CT_ESTABLISHED:
case IP_CT_ESTABLISHED_REPLY:
break;
default:
err = NF_DROP;
goto out;
}
err = nf_nat_packet(ct, ctinfo, hooknum, skb);
if (err == NF_ACCEPT)
*action |= BIT(maniptype);
out:
return err;
}
int nf_ct_nat(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo, int *action,
const struct nf_nat_range2 *range, bool commit)
{
enum nf_nat_manip_type maniptype;
int err, ct_action = *action;
*action = 0;
/* Add NAT extension if not confirmed yet. */
if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct))
return NF_DROP; /* Can't NAT. */
if (ctinfo != IP_CT_NEW && (ct->status & IPS_NAT_MASK) &&
(ctinfo != IP_CT_RELATED || commit)) {
/* NAT an established or related connection like before. */
if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY)
/* This is the REPLY direction for a connection
* for which NAT was applied in the forward
* direction. Do the reverse NAT.
*/
maniptype = ct->status & IPS_SRC_NAT
? NF_NAT_MANIP_DST : NF_NAT_MANIP_SRC;
else
maniptype = ct->status & IPS_SRC_NAT
? NF_NAT_MANIP_SRC : NF_NAT_MANIP_DST;
} else if (ct_action & BIT(NF_NAT_MANIP_SRC)) {
maniptype = NF_NAT_MANIP_SRC;
} else if (ct_action & BIT(NF_NAT_MANIP_DST)) {
maniptype = NF_NAT_MANIP_DST;
} else {
return NF_ACCEPT;
}
err = nf_ct_nat_execute(skb, ct, ctinfo, action, range, maniptype);
if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) {
if (ct->status & IPS_SRC_NAT) {
if (maniptype == NF_NAT_MANIP_SRC)
maniptype = NF_NAT_MANIP_DST;
else
maniptype = NF_NAT_MANIP_SRC;
err = nf_ct_nat_execute(skb, ct, ctinfo, action, range,
maniptype);
} else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
err = nf_ct_nat_execute(skb, ct, ctinfo, action, NULL,
NF_NAT_MANIP_SRC);
}
}
return err;
}
EXPORT_SYMBOL_GPL(nf_ct_nat);
...@@ -15,6 +15,7 @@ config OPENVSWITCH ...@@ -15,6 +15,7 @@ config OPENVSWITCH
select NET_MPLS_GSO select NET_MPLS_GSO
select DST_CACHE select DST_CACHE
select NET_NSH select NET_NSH
select NF_NAT_OVS if NF_NAT
help help
Open vSwitch is a multilayer Ethernet switch targeted at virtualized Open vSwitch is a multilayer Ethernet switch targeted at virtualized
environments. In addition to supporting a variety of features environments. In addition to supporting a variety of features
......
...@@ -726,144 +726,27 @@ static void ovs_nat_update_key(struct sw_flow_key *key, ...@@ -726,144 +726,27 @@ static void ovs_nat_update_key(struct sw_flow_key *key,
} }
} }
/* Modelled after nf_nat_ipv[46]_fn().
* range is only used for new, uninitialized NAT state.
* Returns either NF_ACCEPT or NF_DROP.
*/
static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
const struct nf_nat_range2 *range,
enum nf_nat_manip_type maniptype, struct sw_flow_key *key)
{
int hooknum, err = NF_ACCEPT;
/* See HOOK2MANIP(). */
if (maniptype == NF_NAT_MANIP_SRC)
hooknum = NF_INET_LOCAL_IN; /* Source NAT */
else
hooknum = NF_INET_LOCAL_OUT; /* Destination NAT */
switch (ctinfo) {
case IP_CT_RELATED:
case IP_CT_RELATED_REPLY:
if (IS_ENABLED(CONFIG_NF_NAT) &&
skb->protocol == htons(ETH_P_IP) &&
ip_hdr(skb)->protocol == IPPROTO_ICMP) {
if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
hooknum))
err = NF_DROP;
goto out;
} else if (IS_ENABLED(CONFIG_IPV6) &&
skb->protocol == htons(ETH_P_IPV6)) {
__be16 frag_off;
u8 nexthdr = ipv6_hdr(skb)->nexthdr;
int hdrlen = ipv6_skip_exthdr(skb,
sizeof(struct ipv6hdr),
&nexthdr, &frag_off);
if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
if (!nf_nat_icmpv6_reply_translation(skb, ct,
ctinfo,
hooknum,
hdrlen))
err = NF_DROP;
goto out;
}
}
/* Non-ICMP, fall thru to initialize if needed. */
fallthrough;
case IP_CT_NEW:
/* Seen it before? This can happen for loopback, retrans,
* or local packets.
*/
if (!nf_nat_initialized(ct, maniptype)) {
/* Initialize according to the NAT action. */
err = (range && range->flags & NF_NAT_RANGE_MAP_IPS)
/* Action is set up to establish a new
* mapping.
*/
? nf_nat_setup_info(ct, range, maniptype)
: nf_nat_alloc_null_binding(ct, hooknum);
if (err != NF_ACCEPT)
goto out;
}
break;
case IP_CT_ESTABLISHED:
case IP_CT_ESTABLISHED_REPLY:
break;
default:
err = NF_DROP;
goto out;
}
err = nf_nat_packet(ct, ctinfo, hooknum, skb);
out:
/* Update the flow key if NAT successful. */
if (err == NF_ACCEPT)
ovs_nat_update_key(key, skb, maniptype);
return err;
}
/* Returns NF_DROP if the packet should be dropped, NF_ACCEPT otherwise. */ /* Returns NF_DROP if the packet should be dropped, NF_ACCEPT otherwise. */
static int ovs_ct_nat(struct net *net, struct sw_flow_key *key, static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,
const struct ovs_conntrack_info *info, const struct ovs_conntrack_info *info,
struct sk_buff *skb, struct nf_conn *ct, struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo) enum ip_conntrack_info ctinfo)
{ {
enum nf_nat_manip_type maniptype; int err, action = 0;
int err;
if (!(info->nat & OVS_CT_NAT)) if (!(info->nat & OVS_CT_NAT))
return NF_ACCEPT; return NF_ACCEPT;
if (info->nat & OVS_CT_SRC_NAT)
action |= BIT(NF_NAT_MANIP_SRC);
if (info->nat & OVS_CT_DST_NAT)
action |= BIT(NF_NAT_MANIP_DST);
/* Add NAT extension if not confirmed yet. */ err = nf_ct_nat(skb, ct, ctinfo, &action, &info->range, info->commit);
if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct))
return NF_DROP; /* Can't NAT. */
/* Determine NAT type.
* Check if the NAT type can be deduced from the tracked connection.
* Make sure new expected connections (IP_CT_RELATED) are NATted only
* when committing.
*/
if (ctinfo != IP_CT_NEW && ct->status & IPS_NAT_MASK &&
(ctinfo != IP_CT_RELATED || info->commit)) {
/* NAT an established or related connection like before. */
if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY)
/* This is the REPLY direction for a connection
* for which NAT was applied in the forward
* direction. Do the reverse NAT.
*/
maniptype = ct->status & IPS_SRC_NAT
? NF_NAT_MANIP_DST : NF_NAT_MANIP_SRC;
else
maniptype = ct->status & IPS_SRC_NAT
? NF_NAT_MANIP_SRC : NF_NAT_MANIP_DST;
} else if (info->nat & OVS_CT_SRC_NAT) {
maniptype = NF_NAT_MANIP_SRC;
} else if (info->nat & OVS_CT_DST_NAT) {
maniptype = NF_NAT_MANIP_DST;
} else {
return NF_ACCEPT; /* Connection is not NATed. */
}
err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype, key);
if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) {
if (ct->status & IPS_SRC_NAT) {
if (maniptype == NF_NAT_MANIP_SRC)
maniptype = NF_NAT_MANIP_DST;
else
maniptype = NF_NAT_MANIP_SRC;
err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, if (action & BIT(NF_NAT_MANIP_SRC))
maniptype, key); ovs_nat_update_key(key, skb, NF_NAT_MANIP_SRC);
} else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { if (action & BIT(NF_NAT_MANIP_DST))
err = ovs_ct_nat_execute(skb, ct, ctinfo, NULL, ovs_nat_update_key(key, skb, NF_NAT_MANIP_DST);
NF_NAT_MANIP_SRC, key);
}
}
return err; return err;
} }
......
...@@ -977,6 +977,7 @@ config NET_ACT_TUNNEL_KEY ...@@ -977,6 +977,7 @@ config NET_ACT_TUNNEL_KEY
config NET_ACT_CT config NET_ACT_CT
tristate "connection tracking tc action" tristate "connection tracking tc action"
depends on NET_CLS_ACT && NF_CONNTRACK && (!NF_NAT || NF_NAT) && NF_FLOW_TABLE depends on NET_CLS_ACT && NF_CONNTRACK && (!NF_NAT || NF_NAT) && NF_FLOW_TABLE
select NF_NAT_OVS if NF_NAT
help help
Say Y here to allow sending the packets to conntrack module. Say Y here to allow sending the packets to conntrack module.
......
...@@ -864,90 +864,6 @@ static void tcf_ct_params_free_rcu(struct rcu_head *head) ...@@ -864,90 +864,6 @@ static void tcf_ct_params_free_rcu(struct rcu_head *head)
tcf_ct_params_free(params); tcf_ct_params_free(params);
} }
#if IS_ENABLED(CONFIG_NF_NAT)
/* Modelled after nf_nat_ipv[46]_fn().
* range is only used for new, uninitialized NAT state.
* Returns either NF_ACCEPT or NF_DROP.
*/
static int ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct,
enum ip_conntrack_info ctinfo,
const struct nf_nat_range2 *range,
enum nf_nat_manip_type maniptype)
{
__be16 proto = skb_protocol(skb, true);
int hooknum, err = NF_ACCEPT;
/* See HOOK2MANIP(). */
if (maniptype == NF_NAT_MANIP_SRC)
hooknum = NF_INET_LOCAL_IN; /* Source NAT */
else
hooknum = NF_INET_LOCAL_OUT; /* Destination NAT */
switch (ctinfo) {
case IP_CT_RELATED:
case IP_CT_RELATED_REPLY:
if (proto == htons(ETH_P_IP) &&
ip_hdr(skb)->protocol == IPPROTO_ICMP) {
if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
hooknum))
err = NF_DROP;
goto out;
} else if (IS_ENABLED(CONFIG_IPV6) && proto == htons(ETH_P_IPV6)) {
__be16 frag_off;
u8 nexthdr = ipv6_hdr(skb)->nexthdr;
int hdrlen = ipv6_skip_exthdr(skb,
sizeof(struct ipv6hdr),
&nexthdr, &frag_off);
if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
if (!nf_nat_icmpv6_reply_translation(skb, ct,
ctinfo,
hooknum,
hdrlen))
err = NF_DROP;
goto out;
}
}
/* Non-ICMP, fall thru to initialize if needed. */
fallthrough;
case IP_CT_NEW:
/* Seen it before? This can happen for loopback, retrans,
* or local packets.
*/
if (!nf_nat_initialized(ct, maniptype)) {
/* Initialize according to the NAT action. */
err = (range && range->flags & NF_NAT_RANGE_MAP_IPS)
/* Action is set up to establish a new
* mapping.
*/
? nf_nat_setup_info(ct, range, maniptype)
: nf_nat_alloc_null_binding(ct, hooknum);
if (err != NF_ACCEPT)
goto out;
}
break;
case IP_CT_ESTABLISHED:
case IP_CT_ESTABLISHED_REPLY:
break;
default:
err = NF_DROP;
goto out;
}
err = nf_nat_packet(ct, ctinfo, hooknum, skb);
out:
if (err == NF_ACCEPT) {
if (maniptype == NF_NAT_MANIP_SRC)
tc_skb_cb(skb)->post_ct_snat = 1;
if (maniptype == NF_NAT_MANIP_DST)
tc_skb_cb(skb)->post_ct_dnat = 1;
}
return err;
}
#endif /* CONFIG_NF_NAT */
static void tcf_ct_act_set_mark(struct nf_conn *ct, u32 mark, u32 mask) static void tcf_ct_act_set_mark(struct nf_conn *ct, u32 mark, u32 mask)
{ {
#if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) #if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK)
...@@ -987,52 +903,22 @@ static int tcf_ct_act_nat(struct sk_buff *skb, ...@@ -987,52 +903,22 @@ static int tcf_ct_act_nat(struct sk_buff *skb,
bool commit) bool commit)
{ {
#if IS_ENABLED(CONFIG_NF_NAT) #if IS_ENABLED(CONFIG_NF_NAT)
int err; int err, action = 0;
enum nf_nat_manip_type maniptype;
if (!(ct_action & TCA_CT_ACT_NAT)) if (!(ct_action & TCA_CT_ACT_NAT))
return NF_ACCEPT; return NF_ACCEPT;
if (ct_action & TCA_CT_ACT_NAT_SRC)
action |= BIT(NF_NAT_MANIP_SRC);
if (ct_action & TCA_CT_ACT_NAT_DST)
action |= BIT(NF_NAT_MANIP_DST);
/* Add NAT extension if not confirmed yet. */ err = nf_ct_nat(skb, ct, ctinfo, &action, range, commit);
if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct))
return NF_DROP; /* Can't NAT. */
if (ctinfo != IP_CT_NEW && (ct->status & IPS_NAT_MASK) &&
(ctinfo != IP_CT_RELATED || commit)) {
/* NAT an established or related connection like before. */
if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY)
/* This is the REPLY direction for a connection
* for which NAT was applied in the forward
* direction. Do the reverse NAT.
*/
maniptype = ct->status & IPS_SRC_NAT
? NF_NAT_MANIP_DST : NF_NAT_MANIP_SRC;
else
maniptype = ct->status & IPS_SRC_NAT
? NF_NAT_MANIP_SRC : NF_NAT_MANIP_DST;
} else if (ct_action & TCA_CT_ACT_NAT_SRC) {
maniptype = NF_NAT_MANIP_SRC;
} else if (ct_action & TCA_CT_ACT_NAT_DST) {
maniptype = NF_NAT_MANIP_DST;
} else {
return NF_ACCEPT;
}
err = ct_nat_execute(skb, ct, ctinfo, range, maniptype); if (action & BIT(NF_NAT_MANIP_SRC))
if (err == NF_ACCEPT && ct->status & IPS_DST_NAT) { tc_skb_cb(skb)->post_ct_snat = 1;
if (ct->status & IPS_SRC_NAT) { if (action & BIT(NF_NAT_MANIP_DST))
if (maniptype == NF_NAT_MANIP_SRC) tc_skb_cb(skb)->post_ct_dnat = 1;
maniptype = NF_NAT_MANIP_DST;
else
maniptype = NF_NAT_MANIP_SRC;
err = ct_nat_execute(skb, ct, ctinfo, range,
maniptype);
} else if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
err = ct_nat_execute(skb, ct, ctinfo, NULL,
NF_NAT_MANIP_SRC);
}
}
return err; return err;
#else #else
return NF_ACCEPT; return NF_ACCEPT;
......
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