mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	openvswitch: Interface with NAT.
Extend OVS conntrack interface to cover NAT. New nested OVS_CT_ATTR_NAT attribute may be used to include NAT with a CT action. A bare OVS_CT_ATTR_NAT only mangles existing and expected connections. If OVS_NAT_ATTR_SRC or OVS_NAT_ATTR_DST is included within the nested attributes, new (non-committed/non-confirmed) connections are mangled according to the rest of the nested attributes. The corresponding OVS userspace patch series includes test cases (in tests/system-traffic.at) that also serve as example uses. This work extends on a branch by Thomas Graf at https://github.com/tgraf/ovs/tree/nat. Signed-off-by: Jarno Rajahalme <jarno@ovn.org> Acked-by: Thomas Graf <tgraf@suug.ch> Acked-by: Joe Stringer <joe@ovn.org> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
		
							parent
							
								
									28b6e0c1ac
								
							
						
					
					
						commit
						05752523e5
					
				
					 4 changed files with 551 additions and 28 deletions
				
			
		| 
						 | 
					@ -454,6 +454,14 @@ struct ovs_key_ct_labels {
 | 
				
			||||||
#define OVS_CS_F_REPLY_DIR         0x08 /* Flow is in the reply direction. */
 | 
					#define OVS_CS_F_REPLY_DIR         0x08 /* Flow is in the reply direction. */
 | 
				
			||||||
#define OVS_CS_F_INVALID           0x10 /* Could not track connection. */
 | 
					#define OVS_CS_F_INVALID           0x10 /* Could not track connection. */
 | 
				
			||||||
#define OVS_CS_F_TRACKED           0x20 /* Conntrack has occurred. */
 | 
					#define OVS_CS_F_TRACKED           0x20 /* Conntrack has occurred. */
 | 
				
			||||||
 | 
					#define OVS_CS_F_SRC_NAT           0x40 /* Packet's source address/port was
 | 
				
			||||||
 | 
										 * mangled by NAT.
 | 
				
			||||||
 | 
										 */
 | 
				
			||||||
 | 
					#define OVS_CS_F_DST_NAT           0x80 /* Packet's destination address/port
 | 
				
			||||||
 | 
										 * was mangled by NAT.
 | 
				
			||||||
 | 
										 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define OVS_CS_F_NAT_MASK (OVS_CS_F_SRC_NAT | OVS_CS_F_DST_NAT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
 | 
					 * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
 | 
				
			||||||
| 
						 | 
					@ -632,6 +640,8 @@ struct ovs_action_hash {
 | 
				
			||||||
 * mask. For each bit set in the mask, the corresponding bit in the value is
 | 
					 * mask. For each bit set in the mask, the corresponding bit in the value is
 | 
				
			||||||
 * copied to the connection tracking label field in the connection.
 | 
					 * copied to the connection tracking label field in the connection.
 | 
				
			||||||
 * @OVS_CT_ATTR_HELPER: variable length string defining conntrack ALG.
 | 
					 * @OVS_CT_ATTR_HELPER: variable length string defining conntrack ALG.
 | 
				
			||||||
 | 
					 * @OVS_CT_ATTR_NAT: Nested OVS_NAT_ATTR_* for performing L3 network address
 | 
				
			||||||
 | 
					 * translation (NAT) on the packet.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
enum ovs_ct_attr {
 | 
					enum ovs_ct_attr {
 | 
				
			||||||
	OVS_CT_ATTR_UNSPEC,
 | 
						OVS_CT_ATTR_UNSPEC,
 | 
				
			||||||
| 
						 | 
					@ -641,11 +651,50 @@ enum ovs_ct_attr {
 | 
				
			||||||
	OVS_CT_ATTR_LABELS,     /* labels to associate with this connection. */
 | 
						OVS_CT_ATTR_LABELS,     /* labels to associate with this connection. */
 | 
				
			||||||
	OVS_CT_ATTR_HELPER,     /* netlink helper to assist detection of
 | 
						OVS_CT_ATTR_HELPER,     /* netlink helper to assist detection of
 | 
				
			||||||
				   related connections. */
 | 
									   related connections. */
 | 
				
			||||||
 | 
						OVS_CT_ATTR_NAT,        /* Nested OVS_NAT_ATTR_* */
 | 
				
			||||||
	__OVS_CT_ATTR_MAX
 | 
						__OVS_CT_ATTR_MAX
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define OVS_CT_ATTR_MAX (__OVS_CT_ATTR_MAX - 1)
 | 
					#define OVS_CT_ATTR_MAX (__OVS_CT_ATTR_MAX - 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
 | 
				
			||||||
 | 
					 * @OVS_NAT_ATTR_DST: Flag for Destination NAT (mangle destination
 | 
				
			||||||
 | 
					 * address/port).  Only one of (@OVS_NAT_ATTR_SRC, @OVS_NAT_ATTR_DST) may be
 | 
				
			||||||
 | 
					 * specified.  Effective only for packets for ct_state NEW connections.
 | 
				
			||||||
 | 
					 * Packets of committed connections are mangled by the NAT action according to
 | 
				
			||||||
 | 
					 * the committed NAT type regardless of the flags specified.  As a corollary, a
 | 
				
			||||||
 | 
					 * NAT action without a NAT type flag will only mangle packets of committed
 | 
				
			||||||
 | 
					 * connections.  The following NAT attributes only apply for NEW
 | 
				
			||||||
 | 
					 * (non-committed) connections, and they may be included only when the CT
 | 
				
			||||||
 | 
					 * action has the @OVS_CT_ATTR_COMMIT flag and either @OVS_NAT_ATTR_SRC or
 | 
				
			||||||
 | 
					 * @OVS_NAT_ATTR_DST is also included.
 | 
				
			||||||
 | 
					 * @OVS_NAT_ATTR_IP_MIN: struct in_addr or struct in6_addr
 | 
				
			||||||
 | 
					 * @OVS_NAT_ATTR_IP_MAX: struct in_addr or struct in6_addr
 | 
				
			||||||
 | 
					 * @OVS_NAT_ATTR_PROTO_MIN: u16 L4 protocol specific lower boundary (port)
 | 
				
			||||||
 | 
					 * @OVS_NAT_ATTR_PROTO_MAX: u16 L4 protocol specific upper boundary (port)
 | 
				
			||||||
 | 
					 * @OVS_NAT_ATTR_PERSISTENT: Flag for persistent IP mapping across reboots
 | 
				
			||||||
 | 
					 * @OVS_NAT_ATTR_PROTO_HASH: Flag for pseudo random L4 port mapping (MD5)
 | 
				
			||||||
 | 
					 * @OVS_NAT_ATTR_PROTO_RANDOM: Flag for fully randomized L4 port mapping
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					enum ovs_nat_attr {
 | 
				
			||||||
 | 
						OVS_NAT_ATTR_UNSPEC,
 | 
				
			||||||
 | 
						OVS_NAT_ATTR_SRC,
 | 
				
			||||||
 | 
						OVS_NAT_ATTR_DST,
 | 
				
			||||||
 | 
						OVS_NAT_ATTR_IP_MIN,
 | 
				
			||||||
 | 
						OVS_NAT_ATTR_IP_MAX,
 | 
				
			||||||
 | 
						OVS_NAT_ATTR_PROTO_MIN,
 | 
				
			||||||
 | 
						OVS_NAT_ATTR_PROTO_MAX,
 | 
				
			||||||
 | 
						OVS_NAT_ATTR_PERSISTENT,
 | 
				
			||||||
 | 
						OVS_NAT_ATTR_PROTO_HASH,
 | 
				
			||||||
 | 
						OVS_NAT_ATTR_PROTO_RANDOM,
 | 
				
			||||||
 | 
						__OVS_NAT_ATTR_MAX,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define OVS_NAT_ATTR_MAX (__OVS_NAT_ATTR_MAX - 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * enum ovs_action_attr - Action types.
 | 
					 * enum ovs_action_attr - Action types.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,8 @@ config OPENVSWITCH
 | 
				
			||||||
	tristate "Open vSwitch"
 | 
						tristate "Open vSwitch"
 | 
				
			||||||
	depends on INET
 | 
						depends on INET
 | 
				
			||||||
	depends on !NF_CONNTRACK || \
 | 
						depends on !NF_CONNTRACK || \
 | 
				
			||||||
		   (NF_CONNTRACK && (!NF_DEFRAG_IPV6 || NF_DEFRAG_IPV6))
 | 
							   (NF_CONNTRACK && ((!NF_DEFRAG_IPV6 || NF_DEFRAG_IPV6) && \
 | 
				
			||||||
 | 
									     (!NF_NAT || NF_NAT)))
 | 
				
			||||||
	select LIBCRC32C
 | 
						select LIBCRC32C
 | 
				
			||||||
	select MPLS
 | 
						select MPLS
 | 
				
			||||||
	select NET_MPLS_GSO
 | 
						select NET_MPLS_GSO
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,21 +13,31 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/openvswitch.h>
 | 
					#include <linux/openvswitch.h>
 | 
				
			||||||
 | 
					#include <linux/tcp.h>
 | 
				
			||||||
 | 
					#include <linux/udp.h>
 | 
				
			||||||
 | 
					#include <linux/sctp.h>
 | 
				
			||||||
#include <net/ip.h>
 | 
					#include <net/ip.h>
 | 
				
			||||||
#include <net/netfilter/nf_conntrack_core.h>
 | 
					#include <net/netfilter/nf_conntrack_core.h>
 | 
				
			||||||
#include <net/netfilter/nf_conntrack_helper.h>
 | 
					#include <net/netfilter/nf_conntrack_helper.h>
 | 
				
			||||||
#include <net/netfilter/nf_conntrack_labels.h>
 | 
					#include <net/netfilter/nf_conntrack_labels.h>
 | 
				
			||||||
 | 
					#include <net/netfilter/nf_conntrack_seqadj.h>
 | 
				
			||||||
#include <net/netfilter/nf_conntrack_zones.h>
 | 
					#include <net/netfilter/nf_conntrack_zones.h>
 | 
				
			||||||
#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
 | 
					#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_NF_NAT_NEEDED
 | 
				
			||||||
 | 
					#include <linux/netfilter/nf_nat.h>
 | 
				
			||||||
 | 
					#include <net/netfilter/nf_nat_core.h>
 | 
				
			||||||
 | 
					#include <net/netfilter/nf_nat_l3proto.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "datapath.h"
 | 
					#include "datapath.h"
 | 
				
			||||||
#include "conntrack.h"
 | 
					#include "conntrack.h"
 | 
				
			||||||
#include "flow.h"
 | 
					#include "flow.h"
 | 
				
			||||||
#include "flow_netlink.h"
 | 
					#include "flow_netlink.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ovs_ct_len_tbl {
 | 
					struct ovs_ct_len_tbl {
 | 
				
			||||||
	size_t maxlen;
 | 
						int maxlen;
 | 
				
			||||||
	size_t minlen;
 | 
						int minlen;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Metadata mark for masked write to conntrack mark */
 | 
					/* Metadata mark for masked write to conntrack mark */
 | 
				
			||||||
| 
						 | 
					@ -42,15 +52,25 @@ struct md_labels {
 | 
				
			||||||
	struct ovs_key_ct_labels mask;
 | 
						struct ovs_key_ct_labels mask;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum ovs_ct_nat {
 | 
				
			||||||
 | 
						OVS_CT_NAT = 1 << 0,     /* NAT for committed connections only. */
 | 
				
			||||||
 | 
						OVS_CT_SRC_NAT = 1 << 1, /* Source NAT for NEW connections. */
 | 
				
			||||||
 | 
						OVS_CT_DST_NAT = 1 << 2, /* Destination NAT for NEW connections. */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Conntrack action context for execution. */
 | 
					/* Conntrack action context for execution. */
 | 
				
			||||||
struct ovs_conntrack_info {
 | 
					struct ovs_conntrack_info {
 | 
				
			||||||
	struct nf_conntrack_helper *helper;
 | 
						struct nf_conntrack_helper *helper;
 | 
				
			||||||
	struct nf_conntrack_zone zone;
 | 
						struct nf_conntrack_zone zone;
 | 
				
			||||||
	struct nf_conn *ct;
 | 
						struct nf_conn *ct;
 | 
				
			||||||
	u8 commit : 1;
 | 
						u8 commit : 1;
 | 
				
			||||||
 | 
						u8 nat : 3;                 /* enum ovs_ct_nat */
 | 
				
			||||||
	u16 family;
 | 
						u16 family;
 | 
				
			||||||
	struct md_mark mark;
 | 
						struct md_mark mark;
 | 
				
			||||||
	struct md_labels labels;
 | 
						struct md_labels labels;
 | 
				
			||||||
 | 
					#ifdef CONFIG_NF_NAT_NEEDED
 | 
				
			||||||
 | 
						struct nf_nat_range range;  /* Only present for SRC NAT and DST NAT. */
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info);
 | 
					static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info);
 | 
				
			||||||
| 
						 | 
					@ -138,11 +158,14 @@ static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Update 'key' based on skb->nfct.  If 'post_ct' is true, then OVS has
 | 
					/* Update 'key' based on skb->nfct.  If 'post_ct' is true, then OVS has
 | 
				
			||||||
 * previously sent the packet to conntrack via the ct action.
 | 
					 * previously sent the packet to conntrack via the ct action.  If
 | 
				
			||||||
 | 
					 * 'keep_nat_flags' is true, the existing NAT flags retained, else they are
 | 
				
			||||||
 | 
					 * initialized from the connection status.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void ovs_ct_update_key(const struct sk_buff *skb,
 | 
					static void ovs_ct_update_key(const struct sk_buff *skb,
 | 
				
			||||||
			      const struct ovs_conntrack_info *info,
 | 
								      const struct ovs_conntrack_info *info,
 | 
				
			||||||
			      struct sw_flow_key *key, bool post_ct)
 | 
								      struct sw_flow_key *key, bool post_ct,
 | 
				
			||||||
 | 
								      bool keep_nat_flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt;
 | 
						const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt;
 | 
				
			||||||
	enum ip_conntrack_info ctinfo;
 | 
						enum ip_conntrack_info ctinfo;
 | 
				
			||||||
| 
						 | 
					@ -160,6 +183,14 @@ static void ovs_ct_update_key(const struct sk_buff *skb,
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		if (ct->master)
 | 
							if (ct->master)
 | 
				
			||||||
			state |= OVS_CS_F_RELATED;
 | 
								state |= OVS_CS_F_RELATED;
 | 
				
			||||||
 | 
							if (keep_nat_flags) {
 | 
				
			||||||
 | 
								state |= key->ct.state & OVS_CS_F_NAT_MASK;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if (ct->status & IPS_SRC_NAT)
 | 
				
			||||||
 | 
									state |= OVS_CS_F_SRC_NAT;
 | 
				
			||||||
 | 
								if (ct->status & IPS_DST_NAT)
 | 
				
			||||||
 | 
									state |= OVS_CS_F_DST_NAT;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		zone = nf_ct_zone(ct);
 | 
							zone = nf_ct_zone(ct);
 | 
				
			||||||
	} else if (post_ct) {
 | 
						} else if (post_ct) {
 | 
				
			||||||
		state = OVS_CS_F_TRACKED | OVS_CS_F_INVALID;
 | 
							state = OVS_CS_F_TRACKED | OVS_CS_F_INVALID;
 | 
				
			||||||
| 
						 | 
					@ -174,7 +205,7 @@ static void ovs_ct_update_key(const struct sk_buff *skb,
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key)
 | 
					void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ovs_ct_update_key(skb, NULL, key, false);
 | 
						ovs_ct_update_key(skb, NULL, key, false, false);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb)
 | 
					int ovs_ct_put_key(const struct sw_flow_key *key, struct sk_buff *skb)
 | 
				
			||||||
| 
						 | 
					@ -263,6 +294,7 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto)
 | 
				
			||||||
	enum ip_conntrack_info ctinfo;
 | 
						enum ip_conntrack_info ctinfo;
 | 
				
			||||||
	unsigned int protoff;
 | 
						unsigned int protoff;
 | 
				
			||||||
	struct nf_conn *ct;
 | 
						struct nf_conn *ct;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ct = nf_ct_get(skb, &ctinfo);
 | 
						ct = nf_ct_get(skb, &ctinfo);
 | 
				
			||||||
	if (!ct || ctinfo == IP_CT_RELATED_REPLY)
 | 
						if (!ct || ctinfo == IP_CT_RELATED_REPLY)
 | 
				
			||||||
| 
						 | 
					@ -299,7 +331,18 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto)
 | 
				
			||||||
		return NF_DROP;
 | 
							return NF_DROP;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return helper->help(skb, protoff, ct, ctinfo);
 | 
						err = helper->help(skb, protoff, ct, ctinfo);
 | 
				
			||||||
 | 
						if (err != NF_ACCEPT)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Adjust seqs after helper.  This is needed due to some helpers (e.g.,
 | 
				
			||||||
 | 
						 * FTP with NAT) adusting the TCP payload size when mangling IP
 | 
				
			||||||
 | 
						 * addresses and/or port numbers in the text-based control connection.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
 | 
				
			||||||
 | 
						    !nf_ct_seq_adjust(skb, ct, ctinfo, protoff))
 | 
				
			||||||
 | 
							return NF_DROP;
 | 
				
			||||||
 | 
						return NF_ACCEPT;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
 | 
					/* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
 | 
				
			||||||
| 
						 | 
					@ -468,6 +511,200 @@ static bool skb_nfct_cached(struct net *net,
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_NF_NAT_NEEDED
 | 
				
			||||||
 | 
					/* 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_range *range,
 | 
				
			||||||
 | 
								      enum nf_nat_manip_type maniptype)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int hooknum, nh_off, err = NF_ACCEPT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nh_off = skb_network_offset(skb);
 | 
				
			||||||
 | 
						skb_pull(skb, nh_off);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* 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 (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 push;
 | 
				
			||||||
 | 
					#if IS_ENABLED(CONFIG_NF_NAT_IPV6)
 | 
				
			||||||
 | 
							} else if (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 push;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							/* Non-ICMP, fall thru to initialize if needed. */
 | 
				
			||||||
 | 
						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 push;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case IP_CT_ESTABLISHED:
 | 
				
			||||||
 | 
						case IP_CT_ESTABLISHED_REPLY:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							err = NF_DROP;
 | 
				
			||||||
 | 
							goto push;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = nf_nat_packet(ct, ctinfo, hooknum, skb);
 | 
				
			||||||
 | 
					push:
 | 
				
			||||||
 | 
						skb_push(skb, nh_off);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void ovs_nat_update_key(struct sw_flow_key *key,
 | 
				
			||||||
 | 
								       const struct sk_buff *skb,
 | 
				
			||||||
 | 
								       enum nf_nat_manip_type maniptype)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (maniptype == NF_NAT_MANIP_SRC) {
 | 
				
			||||||
 | 
							__be16 src;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							key->ct.state |= OVS_CS_F_SRC_NAT;
 | 
				
			||||||
 | 
							if (key->eth.type == htons(ETH_P_IP))
 | 
				
			||||||
 | 
								key->ipv4.addr.src = ip_hdr(skb)->saddr;
 | 
				
			||||||
 | 
							else if (key->eth.type == htons(ETH_P_IPV6))
 | 
				
			||||||
 | 
								memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr,
 | 
				
			||||||
 | 
								       sizeof(key->ipv6.addr.src));
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (key->ip.proto == IPPROTO_UDP)
 | 
				
			||||||
 | 
								src = udp_hdr(skb)->source;
 | 
				
			||||||
 | 
							else if (key->ip.proto == IPPROTO_TCP)
 | 
				
			||||||
 | 
								src = tcp_hdr(skb)->source;
 | 
				
			||||||
 | 
							else if (key->ip.proto == IPPROTO_SCTP)
 | 
				
			||||||
 | 
								src = sctp_hdr(skb)->source;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							key->tp.src = src;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							__be16 dst;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							key->ct.state |= OVS_CS_F_DST_NAT;
 | 
				
			||||||
 | 
							if (key->eth.type == htons(ETH_P_IP))
 | 
				
			||||||
 | 
								key->ipv4.addr.dst = ip_hdr(skb)->daddr;
 | 
				
			||||||
 | 
							else if (key->eth.type == htons(ETH_P_IPV6))
 | 
				
			||||||
 | 
								memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr,
 | 
				
			||||||
 | 
								       sizeof(key->ipv6.addr.dst));
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (key->ip.proto == IPPROTO_UDP)
 | 
				
			||||||
 | 
								dst = udp_hdr(skb)->dest;
 | 
				
			||||||
 | 
							else if (key->ip.proto == IPPROTO_TCP)
 | 
				
			||||||
 | 
								dst = tcp_hdr(skb)->dest;
 | 
				
			||||||
 | 
							else if (key->ip.proto == IPPROTO_SCTP)
 | 
				
			||||||
 | 
								dst = sctp_hdr(skb)->dest;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							key->tp.dst = dst;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 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,
 | 
				
			||||||
 | 
							      const struct ovs_conntrack_info *info,
 | 
				
			||||||
 | 
							      struct sk_buff *skb, struct nf_conn *ct,
 | 
				
			||||||
 | 
							      enum ip_conntrack_info ctinfo)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						enum nf_nat_manip_type maniptype;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (nf_ct_is_untracked(ct)) {
 | 
				
			||||||
 | 
							/* A NAT action may only be performed on tracked packets. */
 | 
				
			||||||
 | 
							return NF_ACCEPT;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Add NAT extension if not confirmed yet. */
 | 
				
			||||||
 | 
						if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct))
 | 
				
			||||||
 | 
							return NF_ACCEPT;   /* Can't NAT. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Determine NAT type.
 | 
				
			||||||
 | 
						 * Check if the NAT type can be deduced from the tracked connection.
 | 
				
			||||||
 | 
						 * Make sure expected traffic is NATted only when committing.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (info->nat & OVS_CT_NAT && ctinfo != IP_CT_NEW &&
 | 
				
			||||||
 | 
						    ct->status & IPS_NAT_MASK &&
 | 
				
			||||||
 | 
						    (!(ct->status & IPS_EXPECTED_BIT) || 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Mark NAT done if successful and update the flow key. */
 | 
				
			||||||
 | 
						if (err == NF_ACCEPT)
 | 
				
			||||||
 | 
							ovs_nat_update_key(key, skb, maniptype);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#else /* !CONFIG_NF_NAT_NEEDED */
 | 
				
			||||||
 | 
					static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,
 | 
				
			||||||
 | 
							      const struct ovs_conntrack_info *info,
 | 
				
			||||||
 | 
							      struct sk_buff *skb, struct nf_conn *ct,
 | 
				
			||||||
 | 
							      enum ip_conntrack_info ctinfo)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return NF_ACCEPT;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Pass 'skb' through conntrack in 'net', using zone configured in 'info', if
 | 
					/* Pass 'skb' through conntrack in 'net', using zone configured in 'info', if
 | 
				
			||||||
 * not done already.  Update key with new CT state after passing the packet
 | 
					 * not done already.  Update key with new CT state after passing the packet
 | 
				
			||||||
 * through conntrack.
 | 
					 * through conntrack.
 | 
				
			||||||
| 
						 | 
					@ -509,20 +746,44 @@ static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key,
 | 
				
			||||||
		if (err != NF_ACCEPT)
 | 
							if (err != NF_ACCEPT)
 | 
				
			||||||
			return -ENOENT;
 | 
								return -ENOENT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ovs_ct_update_key(skb, info, key, true);
 | 
							/* Clear CT state NAT flags to mark that we have not yet done
 | 
				
			||||||
 | 
							 * NAT after the nf_conntrack_in() call.  We can actually clear
 | 
				
			||||||
 | 
							 * the whole state, as it will be re-initialized below.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							key->ct.state = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Update the key, but keep the NAT flags. */
 | 
				
			||||||
 | 
							ovs_ct_update_key(skb, info, key, true, true);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ct = nf_ct_get(skb, &ctinfo);
 | 
				
			||||||
 | 
						if (ct) {
 | 
				
			||||||
 | 
							/* Packets starting a new connection must be NATted before the
 | 
				
			||||||
 | 
							 * helper, so that the helper knows about the NAT.  We enforce
 | 
				
			||||||
 | 
							 * this by delaying both NAT and helper calls for unconfirmed
 | 
				
			||||||
 | 
							 * connections until the committing CT action.  For later
 | 
				
			||||||
 | 
							 * packets NAT and Helper may be called in either order.
 | 
				
			||||||
 | 
							 *
 | 
				
			||||||
 | 
							 * NAT will be done only if the CT action has NAT, and only
 | 
				
			||||||
 | 
							 * once per packet (per zone), as guarded by the NAT bits in
 | 
				
			||||||
 | 
							 * the key->ct.state.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (info->nat && !(key->ct.state & OVS_CS_F_NAT_MASK) &&
 | 
				
			||||||
 | 
							    (nf_ct_is_confirmed(ct) || info->commit) &&
 | 
				
			||||||
 | 
							    ovs_ct_nat(net, key, info, skb, ct, ctinfo) != NF_ACCEPT) {
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Call the helper only if:
 | 
							/* Call the helper only if:
 | 
				
			||||||
	 * - nf_conntrack_in() was executed above ("!cached") for a confirmed
 | 
							 * - nf_conntrack_in() was executed above ("!cached") for a
 | 
				
			||||||
	 *   connection, or
 | 
							 *   confirmed connection, or
 | 
				
			||||||
		 * - When committing an unconfirmed connection.
 | 
							 * - When committing an unconfirmed connection.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
	ct = nf_ct_get(skb, &ctinfo);
 | 
							if ((nf_ct_is_confirmed(ct) ? !cached : info->commit) &&
 | 
				
			||||||
	if (ct && (nf_ct_is_confirmed(ct) ? !cached : info->commit) &&
 | 
					 | 
				
			||||||
		    ovs_ct_helper(skb, info->family) != NF_ACCEPT) {
 | 
							    ovs_ct_helper(skb, info->family) != NF_ACCEPT) {
 | 
				
			||||||
		WARN_ONCE(1, "helper rejected packet");
 | 
					 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -545,15 +806,13 @@ static int ovs_ct_lookup(struct net *net, struct sw_flow_key *key,
 | 
				
			||||||
	if (exp) {
 | 
						if (exp) {
 | 
				
			||||||
		u8 state;
 | 
							u8 state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* NOTE: New connections are NATted and Helped only when
 | 
				
			||||||
 | 
							 * committed, so we are not calling into NAT here.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
		state = OVS_CS_F_TRACKED | OVS_CS_F_NEW | OVS_CS_F_RELATED;
 | 
							state = OVS_CS_F_TRACKED | OVS_CS_F_NEW | OVS_CS_F_RELATED;
 | 
				
			||||||
		__ovs_ct_update_key(key, state, &info->zone, exp->master);
 | 
							__ovs_ct_update_key(key, state, &info->zone, exp->master);
 | 
				
			||||||
	} else {
 | 
						} else
 | 
				
			||||||
		int err;
 | 
							return __ovs_ct_lookup(net, key, info, skb);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		err = __ovs_ct_lookup(net, key, info, skb);
 | 
					 | 
				
			||||||
		if (err)
 | 
					 | 
				
			||||||
			return err;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -653,6 +912,135 @@ static int ovs_ct_add_helper(struct ovs_conntrack_info *info, const char *name,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_NF_NAT_NEEDED
 | 
				
			||||||
 | 
					static int parse_nat(const struct nlattr *attr,
 | 
				
			||||||
 | 
							     struct ovs_conntrack_info *info, bool log)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct nlattr *a;
 | 
				
			||||||
 | 
						int rem;
 | 
				
			||||||
 | 
						bool have_ip_max = false;
 | 
				
			||||||
 | 
						bool have_proto_max = false;
 | 
				
			||||||
 | 
						bool ip_vers = (info->family == NFPROTO_IPV6);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nla_for_each_nested(a, attr, rem) {
 | 
				
			||||||
 | 
							static const int ovs_nat_attr_lens[OVS_NAT_ATTR_MAX + 1][2] = {
 | 
				
			||||||
 | 
								[OVS_NAT_ATTR_SRC] = {0, 0},
 | 
				
			||||||
 | 
								[OVS_NAT_ATTR_DST] = {0, 0},
 | 
				
			||||||
 | 
								[OVS_NAT_ATTR_IP_MIN] = {sizeof(struct in_addr),
 | 
				
			||||||
 | 
											 sizeof(struct in6_addr)},
 | 
				
			||||||
 | 
								[OVS_NAT_ATTR_IP_MAX] = {sizeof(struct in_addr),
 | 
				
			||||||
 | 
											 sizeof(struct in6_addr)},
 | 
				
			||||||
 | 
								[OVS_NAT_ATTR_PROTO_MIN] = {sizeof(u16), sizeof(u16)},
 | 
				
			||||||
 | 
								[OVS_NAT_ATTR_PROTO_MAX] = {sizeof(u16), sizeof(u16)},
 | 
				
			||||||
 | 
								[OVS_NAT_ATTR_PERSISTENT] = {0, 0},
 | 
				
			||||||
 | 
								[OVS_NAT_ATTR_PROTO_HASH] = {0, 0},
 | 
				
			||||||
 | 
								[OVS_NAT_ATTR_PROTO_RANDOM] = {0, 0},
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							int type = nla_type(a);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (type > OVS_NAT_ATTR_MAX) {
 | 
				
			||||||
 | 
								OVS_NLERR(log,
 | 
				
			||||||
 | 
									  "Unknown NAT attribute (type=%d, max=%d).\n",
 | 
				
			||||||
 | 
									  type, OVS_NAT_ATTR_MAX);
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (nla_len(a) != ovs_nat_attr_lens[type][ip_vers]) {
 | 
				
			||||||
 | 
								OVS_NLERR(log,
 | 
				
			||||||
 | 
									  "NAT attribute type %d has unexpected length (%d != %d).\n",
 | 
				
			||||||
 | 
									  type, nla_len(a),
 | 
				
			||||||
 | 
									  ovs_nat_attr_lens[type][ip_vers]);
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch (type) {
 | 
				
			||||||
 | 
							case OVS_NAT_ATTR_SRC:
 | 
				
			||||||
 | 
							case OVS_NAT_ATTR_DST:
 | 
				
			||||||
 | 
								if (info->nat) {
 | 
				
			||||||
 | 
									OVS_NLERR(log,
 | 
				
			||||||
 | 
										  "Only one type of NAT may be specified.\n"
 | 
				
			||||||
 | 
										  );
 | 
				
			||||||
 | 
									return -ERANGE;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								info->nat |= OVS_CT_NAT;
 | 
				
			||||||
 | 
								info->nat |= ((type == OVS_NAT_ATTR_SRC)
 | 
				
			||||||
 | 
										? OVS_CT_SRC_NAT : OVS_CT_DST_NAT);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case OVS_NAT_ATTR_IP_MIN:
 | 
				
			||||||
 | 
								nla_memcpy(&info->range.min_addr, a, nla_len(a));
 | 
				
			||||||
 | 
								info->range.flags |= NF_NAT_RANGE_MAP_IPS;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case OVS_NAT_ATTR_IP_MAX:
 | 
				
			||||||
 | 
								have_ip_max = true;
 | 
				
			||||||
 | 
								nla_memcpy(&info->range.max_addr, a,
 | 
				
			||||||
 | 
									   sizeof(info->range.max_addr));
 | 
				
			||||||
 | 
								info->range.flags |= NF_NAT_RANGE_MAP_IPS;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case OVS_NAT_ATTR_PROTO_MIN:
 | 
				
			||||||
 | 
								info->range.min_proto.all = htons(nla_get_u16(a));
 | 
				
			||||||
 | 
								info->range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case OVS_NAT_ATTR_PROTO_MAX:
 | 
				
			||||||
 | 
								have_proto_max = true;
 | 
				
			||||||
 | 
								info->range.max_proto.all = htons(nla_get_u16(a));
 | 
				
			||||||
 | 
								info->range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case OVS_NAT_ATTR_PERSISTENT:
 | 
				
			||||||
 | 
								info->range.flags |= NF_NAT_RANGE_PERSISTENT;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case OVS_NAT_ATTR_PROTO_HASH:
 | 
				
			||||||
 | 
								info->range.flags |= NF_NAT_RANGE_PROTO_RANDOM;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case OVS_NAT_ATTR_PROTO_RANDOM:
 | 
				
			||||||
 | 
								info->range.flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								OVS_NLERR(log, "Unknown nat attribute (%d).\n", type);
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (rem > 0) {
 | 
				
			||||||
 | 
							OVS_NLERR(log, "NAT attribute has %d unknown bytes.\n", rem);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!info->nat) {
 | 
				
			||||||
 | 
							/* Do not allow flags if no type is given. */
 | 
				
			||||||
 | 
							if (info->range.flags) {
 | 
				
			||||||
 | 
								OVS_NLERR(log,
 | 
				
			||||||
 | 
									  "NAT flags may be given only when NAT range (SRC or DST) is also specified.\n"
 | 
				
			||||||
 | 
									  );
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							info->nat = OVS_CT_NAT;   /* NAT existing connections. */
 | 
				
			||||||
 | 
						} else if (!info->commit) {
 | 
				
			||||||
 | 
							OVS_NLERR(log,
 | 
				
			||||||
 | 
								  "NAT attributes may be specified only when CT COMMIT flag is also specified.\n"
 | 
				
			||||||
 | 
								  );
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/* Allow missing IP_MAX. */
 | 
				
			||||||
 | 
						if (info->range.flags & NF_NAT_RANGE_MAP_IPS && !have_ip_max) {
 | 
				
			||||||
 | 
							memcpy(&info->range.max_addr, &info->range.min_addr,
 | 
				
			||||||
 | 
							       sizeof(info->range.max_addr));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						/* Allow missing PROTO_MAX. */
 | 
				
			||||||
 | 
						if (info->range.flags & NF_NAT_RANGE_PROTO_SPECIFIED &&
 | 
				
			||||||
 | 
						    !have_proto_max) {
 | 
				
			||||||
 | 
							info->range.max_proto.all = info->range.min_proto.all;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = {
 | 
					static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = {
 | 
				
			||||||
	[OVS_CT_ATTR_COMMIT]	= { .minlen = 0, .maxlen = 0 },
 | 
						[OVS_CT_ATTR_COMMIT]	= { .minlen = 0, .maxlen = 0 },
 | 
				
			||||||
	[OVS_CT_ATTR_ZONE]	= { .minlen = sizeof(u16),
 | 
						[OVS_CT_ATTR_ZONE]	= { .minlen = sizeof(u16),
 | 
				
			||||||
| 
						 | 
					@ -662,7 +1050,11 @@ static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = {
 | 
				
			||||||
	[OVS_CT_ATTR_LABELS]	= { .minlen = sizeof(struct md_labels),
 | 
						[OVS_CT_ATTR_LABELS]	= { .minlen = sizeof(struct md_labels),
 | 
				
			||||||
				    .maxlen = sizeof(struct md_labels) },
 | 
									    .maxlen = sizeof(struct md_labels) },
 | 
				
			||||||
	[OVS_CT_ATTR_HELPER]	= { .minlen = 1,
 | 
						[OVS_CT_ATTR_HELPER]	= { .minlen = 1,
 | 
				
			||||||
				    .maxlen = NF_CT_HELPER_NAME_LEN }
 | 
									    .maxlen = NF_CT_HELPER_NAME_LEN },
 | 
				
			||||||
 | 
					#ifdef CONFIG_NF_NAT_NEEDED
 | 
				
			||||||
 | 
						/* NAT length is checked when parsing the nested attributes. */
 | 
				
			||||||
 | 
						[OVS_CT_ATTR_NAT]	= { .minlen = 0, .maxlen = INT_MAX },
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
 | 
					static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
 | 
				
			||||||
| 
						 | 
					@ -729,6 +1121,15 @@ static int parse_ct(const struct nlattr *attr, struct ovs_conntrack_info *info,
 | 
				
			||||||
				return -EINVAL;
 | 
									return -EINVAL;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					#ifdef CONFIG_NF_NAT_NEEDED
 | 
				
			||||||
 | 
							case OVS_CT_ATTR_NAT: {
 | 
				
			||||||
 | 
								int err = parse_nat(a, info, log);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (err)
 | 
				
			||||||
 | 
									return err;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			OVS_NLERR(log, "Unknown conntrack attr (%d)",
 | 
								OVS_NLERR(log, "Unknown conntrack attr (%d)",
 | 
				
			||||||
				  type);
 | 
									  type);
 | 
				
			||||||
| 
						 | 
					@ -816,6 +1217,74 @@ int ovs_ct_copy_action(struct net *net, const struct nlattr *attr,
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_NF_NAT_NEEDED
 | 
				
			||||||
 | 
					static bool ovs_ct_nat_to_attr(const struct ovs_conntrack_info *info,
 | 
				
			||||||
 | 
								       struct sk_buff *skb)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct nlattr *start;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						start = nla_nest_start(skb, OVS_CT_ATTR_NAT);
 | 
				
			||||||
 | 
						if (!start)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (info->nat & OVS_CT_SRC_NAT) {
 | 
				
			||||||
 | 
							if (nla_put_flag(skb, OVS_NAT_ATTR_SRC))
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
						} else if (info->nat & OVS_CT_DST_NAT) {
 | 
				
			||||||
 | 
							if (nla_put_flag(skb, OVS_NAT_ATTR_DST))
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (info->range.flags & NF_NAT_RANGE_MAP_IPS) {
 | 
				
			||||||
 | 
							if (info->family == NFPROTO_IPV4) {
 | 
				
			||||||
 | 
								if (nla_put_in_addr(skb, OVS_NAT_ATTR_IP_MIN,
 | 
				
			||||||
 | 
										    info->range.min_addr.ip) ||
 | 
				
			||||||
 | 
								    (info->range.max_addr.ip
 | 
				
			||||||
 | 
								     != info->range.min_addr.ip &&
 | 
				
			||||||
 | 
								     (nla_put_in_addr(skb, OVS_NAT_ATTR_IP_MAX,
 | 
				
			||||||
 | 
										      info->range.max_addr.ip))))
 | 
				
			||||||
 | 
									return false;
 | 
				
			||||||
 | 
					#if IS_ENABLED(CONFIG_NF_NAT_IPV6)
 | 
				
			||||||
 | 
							} else if (info->family == NFPROTO_IPV6) {
 | 
				
			||||||
 | 
								if (nla_put_in6_addr(skb, OVS_NAT_ATTR_IP_MIN,
 | 
				
			||||||
 | 
										     &info->range.min_addr.in6) ||
 | 
				
			||||||
 | 
								    (memcmp(&info->range.max_addr.in6,
 | 
				
			||||||
 | 
									    &info->range.min_addr.in6,
 | 
				
			||||||
 | 
									    sizeof(info->range.max_addr.in6)) &&
 | 
				
			||||||
 | 
								     (nla_put_in6_addr(skb, OVS_NAT_ATTR_IP_MAX,
 | 
				
			||||||
 | 
										       &info->range.max_addr.in6))))
 | 
				
			||||||
 | 
									return false;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (info->range.flags & NF_NAT_RANGE_PROTO_SPECIFIED &&
 | 
				
			||||||
 | 
						    (nla_put_u16(skb, OVS_NAT_ATTR_PROTO_MIN,
 | 
				
			||||||
 | 
								 ntohs(info->range.min_proto.all)) ||
 | 
				
			||||||
 | 
						     (info->range.max_proto.all != info->range.min_proto.all &&
 | 
				
			||||||
 | 
						      nla_put_u16(skb, OVS_NAT_ATTR_PROTO_MAX,
 | 
				
			||||||
 | 
								  ntohs(info->range.max_proto.all)))))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (info->range.flags & NF_NAT_RANGE_PERSISTENT &&
 | 
				
			||||||
 | 
						    nla_put_flag(skb, OVS_NAT_ATTR_PERSISTENT))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						if (info->range.flags & NF_NAT_RANGE_PROTO_RANDOM &&
 | 
				
			||||||
 | 
						    nla_put_flag(skb, OVS_NAT_ATTR_PROTO_HASH))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						if (info->range.flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY &&
 | 
				
			||||||
 | 
						    nla_put_flag(skb, OVS_NAT_ATTR_PROTO_RANDOM))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						nla_nest_end(skb, start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info,
 | 
					int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info,
 | 
				
			||||||
			  struct sk_buff *skb)
 | 
								  struct sk_buff *skb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -844,7 +1313,10 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info,
 | 
				
			||||||
				   ct_info->helper->name))
 | 
									   ct_info->helper->name))
 | 
				
			||||||
			return -EMSGSIZE;
 | 
								return -EMSGSIZE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					#ifdef CONFIG_NF_NAT_NEEDED
 | 
				
			||||||
 | 
						if (ct_info->nat && !ovs_ct_nat_to_attr(ct_info, skb))
 | 
				
			||||||
 | 
							return -EMSGSIZE;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	nla_nest_end(skb, start);
 | 
						nla_nest_end(skb, start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,7 +37,8 @@ void ovs_ct_free_action(const struct nlattr *a);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define CT_SUPPORTED_MASK (OVS_CS_F_NEW | OVS_CS_F_ESTABLISHED | \
 | 
					#define CT_SUPPORTED_MASK (OVS_CS_F_NEW | OVS_CS_F_ESTABLISHED | \
 | 
				
			||||||
			   OVS_CS_F_RELATED | OVS_CS_F_REPLY_DIR | \
 | 
								   OVS_CS_F_RELATED | OVS_CS_F_REPLY_DIR | \
 | 
				
			||||||
			   OVS_CS_F_INVALID | OVS_CS_F_TRACKED)
 | 
								   OVS_CS_F_INVALID | OVS_CS_F_TRACKED | \
 | 
				
			||||||
 | 
								   OVS_CS_F_SRC_NAT | OVS_CS_F_DST_NAT)
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
#include <linux/errno.h>
 | 
					#include <linux/errno.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue