Commit 21af0d55 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ovs-tc-dedup'



Xin Long says:

====================
net: eliminate the duplicate code in the ct nat functions of ovs and tc

The changes in the patchset:

  "net: add helper support in tc act_ct for ovs offloading"

had moved some common ct code used by both OVS and TC into netfilter.

There are still some big functions pretty similar defined and used in
each of OVS and TC. It is not good to maintain such big function in 2
places. This patchset is to extract the functions for NAT processing
from OVS and TC to netfilter.

To make this change clear and safe, this patchset gets the common code
out of OVS and TC step by step: The patch 1-4 make some minor changes
in OVS and TC to make the NAT code of them completely the same, then
the patch 5 moves the common code to the netfilter and exports one
function called by each of OVS and TC.

v1->v2:
  - Create nf_nat_ovs.c to include the nat functions, as Pablo suggested.
v2->v3:
  - fix a typo in subject of patch 2/5, as Marcelo noticed.
  - fix in openvswitch to keep OVS ct nat and TC ct nat consistent in
    patch 3/5 instead of in tc, as Marcelo noticed.
  - use BIT(var) macro instead of (1 << var) in patch 5/5, as Marcelo
    suggested.
  - use ifdef in netfilter/Makefile to build nf_nat_ovs only when OVS
    or TC ct action is enabled in patch 5/5, as Marcelo suggested.
v3->v4:
  - add NF_NAT_OVS in netfilter/Kconfig and add select NF_NAT_OVS in
    OVS and TC Kconfig instead of using ifdef in netfilter/Makefile,
    as Pablo suggested.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a2d40ce7 ebddb140
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -104,6 +104,10 @@ unsigned int
nf_nat_inet_fn(void *priv, struct sk_buff *skb,
	       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,
				     enum nf_nat_manip_type manip)
{
+3 −0
Original line number Diff line number Diff line
@@ -459,6 +459,9 @@ config NF_NAT_REDIRECT
config NF_NAT_MASQUERADE
	bool

config NF_NAT_OVS
	bool

config NETFILTER_SYNPROXY
	tristate

+1 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ obj-$(CONFIG_NF_LOG_SYSLOG) += nf_log_syslog.o
obj-$(CONFIG_NF_NAT) += nf_nat.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_OVS) += nf_nat_ovs.o

ifeq ($(CONFIG_NF_NAT),m)
nf_nat-$(CONFIG_DEBUG_INFO_BTF_MODULES) += nf_nat_bpf.o
+135 −0
Original line number Diff line number Diff line
// 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);
+1 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ config OPENVSWITCH
	select NET_MPLS_GSO
	select DST_CACHE
	select NET_NSH
	select NF_NAT_OVS if NF_NAT
	help
	  Open vSwitch is a multilayer Ethernet switch targeted at virtualized
	  environments.  In addition to supporting a variety of features
Loading