History

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.

Jarno Rajahalme committed 10 years ago

Commit 05752521 parent 28b6e0c4 files · +551 −28Browse files

Unified · Split

4 files
  1. include/uapi/linux/openvswitch.h +49 −0
  2. net/openvswitch/Kconfig +2 −1
  3. net/openvswitch/conntrack.c +498 −26
  4. net/openvswitch/conntrack.h +2 −1
include/uapi/linux/openvswitch.h+49−0
@@ -454,6 +454,14 @@ struct ovs_key_ct_labels { · 453 unmodified lines
454454 #define OVS_CS_F_REPLY_DIR 0x08 /* Flow is in the reply direction. */
455455 #define OVS_CS_F_INVALID 0x10 /* Could not track connection. */
456456 #define OVS_CS_F_TRACKED 0x20 /* Conntrack has occurred. */
457+#define OVS_CS_F_SRC_NAT 0x40 /* Packet's source address/port was
458+ * mangled by NAT.
459+ */
460+#define OVS_CS_F_DST_NAT 0x80 /* Packet's destination address/port
461+ * was mangled by NAT.
462+ */
463+
464+#define OVS_CS_F_NAT_MASK (OVS_CS_F_SRC_NAT | OVS_CS_F_DST_NAT)
457465
458466 /**
459467 * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
@@ -632,6 +640,8 @@ struct ovs_action_hash { · 172 unmodified lines
632640 * mask. For each bit set in the mask, the corresponding bit in the value is
633641 * copied to the connection tracking label field in the connection.
634642 * @OVS_CT_ATTR_HELPER: variable length string defining conntrack ALG.
643+ * @OVS_CT_ATTR_NAT: Nested OVS_NAT_ATTR_* for performing L3 network address
644+ * translation (NAT) on the packet.
635645 */
636646 enum ovs_ct_attr {
637647 OVS_CT_ATTR_UNSPEC,
@@ -641,11 +651,50 @@ enum ovs_ct_attr { · 3 unmodified lines
641651 OVS_CT_ATTR_LABELS, /* labels to associate with this connection. */
642652 OVS_CT_ATTR_HELPER, /* netlink helper to assist detection of
643653 related connections. */
654+ OVS_CT_ATTR_NAT, /* Nested OVS_NAT_ATTR_* */
644655 __OVS_CT_ATTR_MAX
645656 };
646657
647658 #define OVS_CT_ATTR_MAX (__OVS_CT_ATTR_MAX - 1)
648659
660+/**
661+ * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
662+ *
663+ * @OVS_NAT_ATTR_SRC: Flag for Source NAT (mangle source address/port).
664+ * @OVS_NAT_ATTR_DST: Flag for Destination NAT (mangle destination
665+ * address/port). Only one of (@OVS_NAT_ATTR_SRC, @OVS_NAT_ATTR_DST) may be
666+ * specified. Effective only for packets for ct_state NEW connections.
667+ * Packets of committed connections are mangled by the NAT action according to
668+ * the committed NAT type regardless of the flags specified. As a corollary, a
669+ * NAT action without a NAT type flag will only mangle packets of committed
670+ * connections. The following NAT attributes only apply for NEW
671+ * (non-committed) connections, and they may be included only when the CT
672+ * action has the @OVS_CT_ATTR_COMMIT flag and either @OVS_NAT_ATTR_SRC or
673+ * @OVS_NAT_ATTR_DST is also included.
674+ * @OVS_NAT_ATTR_IP_MIN: struct in_addr or struct in6_addr
675+ * @OVS_NAT_ATTR_IP_MAX: struct in_addr or struct in6_addr
676+ * @OVS_NAT_ATTR_PROTO_MIN: u16 L4 protocol specific lower boundary (port)
677+ * @OVS_NAT_ATTR_PROTO_MAX: u16 L4 protocol specific upper boundary (port)
678+ * @OVS_NAT_ATTR_PERSISTENT: Flag for persistent IP mapping across reboots
679+ * @OVS_NAT_ATTR_PROTO_HASH: Flag for pseudo random L4 port mapping (MD5)
680+ * @OVS_NAT_ATTR_PROTO_RANDOM: Flag for fully randomized L4 port mapping
681+ */
682+enum ovs_nat_attr {
683+ OVS_NAT_ATTR_UNSPEC,
684+ OVS_NAT_ATTR_SRC,
685+ OVS_NAT_ATTR_DST,
686+ OVS_NAT_ATTR_IP_MIN,
687+ OVS_NAT_ATTR_IP_MAX,
688+ OVS_NAT_ATTR_PROTO_MIN,
689+ OVS_NAT_ATTR_PROTO_MAX,
690+ OVS_NAT_ATTR_PERSISTENT,
691+ OVS_NAT_ATTR_PROTO_HASH,
692+ OVS_NAT_ATTR_PROTO_RANDOM,
693+ __OVS_NAT_ATTR_MAX,
694+};
695+
696+#define OVS_NAT_ATTR_MAX (__OVS_NAT_ATTR_MAX - 1)
697+
649698 /**
650699 * enum ovs_action_attr - Action types.
651700 *
net/openvswitch/Kconfig+2−1
@@ -6,7 +6,8 @@ config OPENVSWITCH · 5 unmodified lines
66 tristate "Open vSwitch"
77 depends on INET
88 depends on !NF_CONNTRACK || \
9− (NF_CONNTRACK && (!NF_DEFRAG_IPV6 || NF_DEFRAG_IPV6))
9+ (NF_CONNTRACK && ((!NF_DEFRAG_IPV6 || NF_DEFRAG_IPV6) && \
10+ (!NF_NAT || NF_NAT)))
1011 select LIBCRC32C
1112 select MPLS
1213 select NET_MPLS_GSO
net/openvswitch/conntrack.c+498−26
@@ -13,21 +13,31 @@ · 12 unmodified lines
1313
1414 #include <linux/module.h>
1515 #include <linux/openvswitch.h>
16+#include <linux/tcp.h>
17+#include <linux/udp.h>
18+#include <linux/sctp.h>
1619 #include <net/ip.h>
1720 #include <net/netfilter/nf_conntrack_core.h>
1821 #include <net/netfilter/nf_conntrack_helper.h>
1922 #include <net/netfilter/nf_conntrack_labels.h>
23+#include <net/netfilter/nf_conntrack_seqadj.h>
2024 #include <net/netfilter/nf_conntrack_zones.h>
2125 #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
2226
27+#ifdef CONFIG_NF_NAT_NEEDED
28+#include <linux/netfilter/nf_nat.h>
29+#include <net/netfilter/nf_nat_core.h>
30+#include <net/netfilter/nf_nat_l3proto.h>
31+#endif
32+
2333 #include "datapath.h"
2434 #include "conntrack.h"
2535 #include "flow.h"
2636 #include "flow_netlink.h"
2737
2838 struct ovs_ct_len_tbl {
29− size_t maxlen;
30− size_t minlen;
39+ int maxlen;
40+ int minlen;
3141 };
3242
3343 /* Metadata mark for masked write to conntrack mark */
@@ -42,15 +52,25 @@ struct md_labels { · 8 unmodified lines
4252 struct ovs_key_ct_labels mask;
4353 };
4454
55+enum ovs_ct_nat {
56+ OVS_CT_NAT = 1 << 0, /* NAT for committed connections only. */
57+ OVS_CT_SRC_NAT = 1 << 1, /* Source NAT for NEW connections. */
58+ OVS_CT_DST_NAT = 1 << 2, /* Destination NAT for NEW connections. */
59+};
60+
4561 /* Conntrack action context for execution. */
4662 struct ovs_conntrack_info {
4763 struct nf_conntrack_helper *helper;
4864 struct nf_conntrack_zone zone;
4965 struct nf_conn *ct;
5066 u8 commit : 1;
67+ u8 nat : 3; /* enum ovs_ct_nat */
5168 u16 family;
5269 struct md_mark mark;
5370 struct md_labels labels;
71+#ifdef CONFIG_NF_NAT_NEEDED
72+ struct nf_nat_range range; /* Only present for SRC NAT and DST NAT. */
73+#endif
5474 };
5575
5676 static void __ovs_ct_free_action(struct ovs_conntrack_info *ct_info);
@@ -137,12 +157,15 @@ static void __ovs_ct_update_key(struct sw_flow_key *key, u8 state, · 80 unmodified lines
137157 ovs_ct_get_labels(ct, &key->ct.labels);
138158 }
139159
140−/* Update 'key' based on skb->nfct. If 'post_ct' is true, then OVS has
141− * previously sent the packet to conntrack via the ct action.
160+/* Update 'key' based on skb->nfct. If 'post_ct' is true, then OVS has
161+ * previously sent the packet to conntrack via the ct action. If
162+ * 'keep_nat_flags' is true, the existing NAT flags retained, else they are
163+ * initialized from the connection status.
142164 */
143165 static void ovs_ct_update_key(const struct sk_buff *skb,
144166 const struct ovs_conntrack_info *info,
145− struct sw_flow_key *key, bool post_ct)
167+ struct sw_flow_key *key, bool post_ct,
168+ bool keep_nat_flags)
146169 {
147170 const struct nf_conntrack_zone *zone = &nf_ct_zone_dflt;
148171 enum ip_conntrack_info ctinfo;
@@ -160,6 +183,14 @@ static void ovs_ct_update_key(const struct sk_buff *skb, · 11 unmodified lines
160183 */
161184 if (ct->master)
162185 state |= OVS_CS_F_RELATED;
186+ if (keep_nat_flags) {
187+ state |= key->ct.state & OVS_CS_F_NAT_MASK;
188+ } else {
189+ if (ct->status & IPS_SRC_NAT)
190+ state |= OVS_CS_F_SRC_NAT;
191+ if (ct->status & IPS_DST_NAT)
192+ state |= OVS_CS_F_DST_NAT;
193+ }
163194 zone = nf_ct_zone(ct);
164195 } else if (post_ct) {
165196 state = OVS_CS_F_TRACKED | OVS_CS_F_INVALID;
@@ -174,7 +205,7 @@ static void ovs_ct_update_key(const struct sk_buff *skb, · 8 unmodified lines
174205 */
175206 void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key)
176207 {
177− ovs_ct_update_key(skb, NULL, key, false);
208+ ovs_ct_update_key(skb, NULL, key, false, false);
178209 }
179210
180211 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) · 82 unmodified lines
263294 enum ip_conntrack_info ctinfo;
264295 unsigned int protoff;
265296 struct nf_conn *ct;
297+ int err;
266298
267299 ct = nf_ct_get(skb, &ctinfo);
268300 if (!ct || ctinfo == IP_CT_RELATED_REPLY)
@@ -299,7 +331,18 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto) · 30 unmodified lines
299331 return NF_DROP;
300332 }
301333
302− return helper->help(skb, protoff, ct, ctinfo);
334+ err = helper->help(skb, protoff, ct, ctinfo);
335+ if (err != NF_ACCEPT)
336+ return err;
337+
338+ /* Adjust seqs after helper. This is needed due to some helpers (e.g.,
339+ * FTP with NAT) adusting the TCP payload size when mangling IP
340+ * addresses and/or port numbers in the text-based control connection.
341+ */
342+ if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) &&
343+ !nf_ct_seq_adjust(skb, ct, ctinfo, protoff))
344+ return NF_DROP;
345+ return NF_ACCEPT;
303346 }
304347
305348 /* Returns 0 on success, -EINPROGRESS if 'skb' is stolen, or other nonzero
@@ -468,6 +511,200 @@ static bool skb_nfct_cached(struct net *net, · 162 unmodified lines
468511 return true;
469512 }
470513
514+#ifdef CONFIG_NF_NAT_NEEDED
515+/* Modelled after nf_nat_ipv[46]_fn().
516+ * range is only used for new, uninitialized NAT state.
517+ * Returns either NF_ACCEPT or NF_DROP.
518+ */
519+static int ovs_ct_nat_execute(struct sk_buff *skb, struct nf_conn *ct,
520+ enum ip_conntrack_info ctinfo,
521+ const struct nf_nat_range *range,
522+ enum nf_nat_manip_type maniptype)
523+{
524+ int hooknum, nh_off, err = NF_ACCEPT;
525+
526+ nh_off = skb_network_offset(skb);
527+ skb_pull(skb, nh_off);
528+
529+ /* See HOOK2MANIP(). */
530+ if (maniptype == NF_NAT_MANIP_SRC)
531+ hooknum = NF_INET_LOCAL_IN; /* Source NAT */
532+ else
533+ hooknum = NF_INET_LOCAL_OUT; /* Destination NAT */
534+
535+ switch (ctinfo) {
536+ case IP_CT_RELATED:
537+ case IP_CT_RELATED_REPLY:
538+ if (skb->protocol == htons(ETH_P_IP) &&
539+ ip_hdr(skb)->protocol == IPPROTO_ICMP) {
540+ if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo,
541+ hooknum))
542+ err = NF_DROP;
543+ goto push;
544+#if IS_ENABLED(CONFIG_NF_NAT_IPV6)
545+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
546+ __be16 frag_off;
547+ u8 nexthdr = ipv6_hdr(skb)->nexthdr;
548+ int hdrlen = ipv6_skip_exthdr(skb,
549+ sizeof(struct ipv6hdr),
550+ &nexthdr, &frag_off);
551+
552+ if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
553+ if (!nf_nat_icmpv6_reply_translation(skb, ct,
554+ ctinfo,
555+ hooknum,
556+ hdrlen))
557+ err = NF_DROP;
558+ goto push;
559+ }
560+#endif
561+ }
562+ /* Non-ICMP, fall thru to initialize if needed. */
563+ case IP_CT_NEW:
564+ /* Seen it before? This can happen for loopback, retrans,
565+ * or local packets.
566+ */
567+ if (!nf_nat_initialized(ct, maniptype)) {
568+ /* Initialize according to the NAT action. */
569+ err = (range && range->flags & NF_NAT_RANGE_MAP_IPS)
570+ /* Action is set up to establish a new
571+ * mapping.
572+ */
573+ ? nf_nat_setup_info(ct, range, maniptype)
574+ : nf_nat_alloc_null_binding(ct, hooknum);
575+ if (err != NF_ACCEPT)
576+ goto push;
577+ }
578+ break;
579+
580+ case IP_CT_ESTABLISHED:
581+ case IP_CT_ESTABLISHED_REPLY:
582+ break;
583+
584+ default:
585+ err = NF_DROP;
586+ goto push;
587+ }
588+
589+ err = nf_nat_packet(ct, ctinfo, hooknum, skb);
590+push:
591+ skb_push(skb, nh_off);
592+
593+ return err;
594+}
595+
596+static void ovs_nat_update_key(struct sw_flow_key *key,
597+ const struct sk_buff *skb,
598+ enum nf_nat_manip_type maniptype)
599+{
600+ if (maniptype == NF_NAT_MANIP_SRC) {
601+ __be16 src;
602+
603+ key->ct.state |= OVS_CS_F_SRC_NAT;
604+ if (key->eth.type == htons(ETH_P_IP))
605+ key->ipv4.addr.src = ip_hdr(skb)->saddr;
606+ else if (key->eth.type == htons(ETH_P_IPV6))
607+ memcpy(&key->ipv6.addr.src, &ipv6_hdr(skb)->saddr,
608+ sizeof(key->ipv6.addr.src));
609+ else
610+ return;
611+
612+ if (key->ip.proto == IPPROTO_UDP)
613+ src = udp_hdr(skb)->source;
614+ else if (key->ip.proto == IPPROTO_TCP)
615+ src = tcp_hdr(skb)->source;
616+ else if (key->ip.proto == IPPROTO_SCTP)
617+ src = sctp_hdr(skb)->source;
618+ else
619+ return;
620+
621+ key->tp.src = src;
622+ } else {
623+ __be16 dst;
624+
625+ key->ct.state |= OVS_CS_F_DST_NAT;
626+ if (key->eth.type == htons(ETH_P_IP))
627+ key->ipv4.addr.dst = ip_hdr(skb)->daddr;
628+ else if (key->eth.type == htons(ETH_P_IPV6))
629+ memcpy(&key->ipv6.addr.dst, &ipv6_hdr(skb)->daddr,
630+ sizeof(key->ipv6.addr.dst));
631+ else
632+ return;
633+
634+ if (key->ip.proto == IPPROTO_UDP)
635+ dst = udp_hdr(skb)->dest;
636+ else if (key->ip.proto == IPPROTO_TCP)
637+ dst = tcp_hdr(skb)->dest;
638+ else if (key->ip.proto == IPPROTO_SCTP)
639+ dst = sctp_hdr(skb)->dest;
640+ else
641+ return;
642+
643+ key->tp.dst = dst;
644+ }
645+}
646+
647+/* Returns NF_DROP if the packet should be dropped, NF_ACCEPT otherwise. */
648+static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,
649+ const struct ovs_conntrack_info *info,
650+ struct sk_buff *skb, struct nf_conn *ct,
651+ enum ip_conntrack_info ctinfo)
652+{
653+ enum nf_nat_manip_type maniptype;
654+ int err;
655+
656+ if (nf_ct_is_untracked(ct)) {
657+ /* A NAT action may only be performed on tracked packets. */
658+ return NF_ACCEPT;
659+ }
660+
661+ /* Add NAT extension if not confirmed yet. */
662+ if (!nf_ct_is_confirmed(ct) && !nf_ct_nat_ext_add(ct))
663+ return NF_ACCEPT; /* Can't NAT. */
664+
665+ /* Determine NAT type.
666+ * Check if the NAT type can be deduced from the tracked connection.
667+ * Make sure expected traffic is NATted only when committing.
668+ */
669+ if (info->nat & OVS_CT_NAT && ctinfo != IP_CT_NEW &&
670+ ct->status & IPS_NAT_MASK &&
671+ (!(ct->status & IPS_EXPECTED_BIT) || info->commit)) {
672+ /* NAT an established or related connection like before. */
673+ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY)
674+ /* This is the REPLY direction for a connection
675+ * for which NAT was applied in the forward
676+ * direction. Do the reverse NAT.
677+ */
678+ maniptype = ct->status & IPS_SRC_NAT
679+ ? NF_NAT_MANIP_DST : NF_NAT_MANIP_SRC;
680+ else
681+ maniptype = ct->status & IPS_SRC_NAT
682+ ? NF_NAT_MANIP_SRC : NF_NAT_MANIP_DST;
683+ } else if (info->nat & OVS_CT_SRC_NAT) {
684+ maniptype = NF_NAT_MANIP_SRC;
685+ } else if (info->nat & OVS_CT_DST_NAT) {
686+ maniptype = NF_NAT_MANIP_DST;
687+ } else {
688+ return NF_ACCEPT; /* Connection is not NATed. */
689+ }
690+ err = ovs_ct_nat_execute(skb, ct, ctinfo, &info->range, maniptype);
691+
692+ /* Mark NAT done if successful and update the flow key. */
693+ if (err == NF_ACCEPT)
694+ ovs_nat_update_key(key, skb, maniptype);
695+
696+ return err;
697+}
698+#else /* !CONFIG_NF_NAT_NEEDED */
699+static int ovs_ct_nat(struct net *net, struct sw_flow_key *key,
700+ const struct ovs_conntrack_info *info,
701+ struct sk_buff *skb, struct nf_conn *ct,
702+ enum ip_conntrack_info ctinfo)
703+{
704+ return NF_ACCEPT;
705+}
706+#endif
707+
471708 /* Pass 'skb' through conntrack in 'net', using zone configured in 'info', if
472709 * not done already. Update key with new CT state after passing the packet
473710 * through conntrack.
@@ -509,19 +746,43 @@ static int __ovs_ct_lookup(struct net *net, struct sw_flow_key *key, · 35 unmodified lines
509746 if (err != NF_ACCEPT)
510747 return -ENOENT;
511748
512− ovs_ct_update_key(skb, info, key, true);
749+ /* Clear CT state NAT flags to mark that we have not yet done
750+ * NAT after the nf_conntrack_in() call. We can actually clear
751+ * the whole state, as it will be re-initialized below.
752+ */
753+ key->ct.state = 0;
754+
755+ /* Update the key, but keep the NAT flags. */
756+ ovs_ct_update_key(skb, info, key, true, true);
513757 }
514758
515− /* Call the helper only if:
516− * - nf_conntrack_in() was executed above ("!cached") for a confirmed
517− * connection, or
518− * - When committing an unconfirmed connection.
519− */
520759 ct = nf_ct_get(skb, &ctinfo);
521− if (ct && (nf_ct_is_confirmed(ct) ? !cached : info->commit) &&
522− ovs_ct_helper(skb, info->family) != NF_ACCEPT) {
523− WARN_ONCE(1, "helper rejected packet");
524− return -EINVAL;
760+ if (ct) {
761+ /* Packets starting a new connection must be NATted before the
762+ * helper, so that the helper knows about the NAT. We enforce
763+ * this by delaying both NAT and helper calls for unconfirmed
764+ * connections until the committing CT action. For later
765+ * packets NAT and Helper may be called in either order.
766+ *
767+ * NAT will be done only if the CT action has NAT, and only
768+ * once per packet (per zone), as guarded by the NAT bits in
769+ * the key->ct.state.
770+ */
771+ if (info->nat && !(key->ct.state & OVS_CS_F_NAT_MASK) &&
772+ (nf_ct_is_confirmed(ct) || info->commit) &&
773+ ovs_ct_nat(net, key, info, skb, ct, ctinfo) != NF_ACCEPT) {
774+ return -EINVAL;
775+ }
776+
777+ /* Call the helper only if:
778+ * - nf_conntrack_in() was executed above ("!cached") for a
779+ * confirmed connection, or
780+ * - When committing an unconfirmed connection.
781+ */
782+ if ((nf_ct_is_confirmed(ct) ? !cached : info->commit) &&
783+ ovs_ct_helper(skb, info->family) != NF_ACCEPT) {
784+ return -EINVAL;
785+ }
525786 }
526787
527788 return 0;
@@ -545,15 +806,13 @@ static int ovs_ct_lookup(struct net *net, struct sw_flow_key *key, · 17 unmodified lines
545806 if (exp) {
546807 u8 state;
547808
809+ /* NOTE: New connections are NATted and Helped only when
810+ * committed, so we are not calling into NAT here.
811+ */
548812 state = OVS_CS_F_TRACKED | OVS_CS_F_NEW | OVS_CS_F_RELATED;
549813 __ovs_ct_update_key(key, state, &info->zone, exp->master);
550− } else {
551− int err;
552−
553− err = __ovs_ct_lookup(net, key, info, skb);
554− if (err)
555− return err;
556− }
814+ } else
815+ return __ovs_ct_lookup(net, key, info, skb);
557816
558817 return 0;
559818 }
@@ -653,6 +912,135 @@ static int ovs_ct_add_helper(struct ovs_conntrack_info *info, const char *name, · 93 unmodified lines
653912 return 0;
654913 }
655914
915+#ifdef CONFIG_NF_NAT_NEEDED
916+static int parse_nat(const struct nlattr *attr,
917+ struct ovs_conntrack_info *info, bool log)
918+{
919+ struct nlattr *a;
920+ int rem;
921+ bool have_ip_max = false;
922+ bool have_proto_max = false;
923+ bool ip_vers = (info->family == NFPROTO_IPV6);
924+
925+ nla_for_each_nested(a, attr, rem) {
926+ static const int ovs_nat_attr_lens[OVS_NAT_ATTR_MAX + 1][2] = {
927+ [OVS_NAT_ATTR_SRC] = {0, 0},
928+ [OVS_NAT_ATTR_DST] = {0, 0},
929+ [OVS_NAT_ATTR_IP_MIN] = {sizeof(struct in_addr),
930+ sizeof(struct in6_addr)},
931+ [OVS_NAT_ATTR_IP_MAX] = {sizeof(struct in_addr),
932+ sizeof(struct in6_addr)},
933+ [OVS_NAT_ATTR_PROTO_MIN] = {sizeof(u16), sizeof(u16)},
934+ [OVS_NAT_ATTR_PROTO_MAX] = {sizeof(u16), sizeof(u16)},
935+ [OVS_NAT_ATTR_PERSISTENT] = {0, 0},
936+ [OVS_NAT_ATTR_PROTO_HASH] = {0, 0},
937+ [OVS_NAT_ATTR_PROTO_RANDOM] = {0, 0},
938+ };
939+ int type = nla_type(a);
940+
941+ if (type > OVS_NAT_ATTR_MAX) {
942+ OVS_NLERR(log,
943+ "Unknown NAT attribute (type=%d, max=%d).\n",
944+ type, OVS_NAT_ATTR_MAX);
945+ return -EINVAL;
946+ }
947+
948+ if (nla_len(a) != ovs_nat_attr_lens[type][ip_vers]) {
949+ OVS_NLERR(log,
950+ "NAT attribute type %d has unexpected length (%d != %d).\n",
951+ type, nla_len(a),
952+ ovs_nat_attr_lens[type][ip_vers]);
953+ return -EINVAL;
954+ }
955+
956+ switch (type) {
957+ case OVS_NAT_ATTR_SRC:
958+ case OVS_NAT_ATTR_DST:
959+ if (info->nat) {
960+ OVS_NLERR(log,
961+ "Only one type of NAT may be specified.\n"
962+ );
963+ return -ERANGE;
964+ }
965+ info->nat |= OVS_CT_NAT;
966+ info->nat |= ((type == OVS_NAT_ATTR_SRC)
967+ ? OVS_CT_SRC_NAT : OVS_CT_DST_NAT);
968+ break;
969+
970+ case OVS_NAT_ATTR_IP_MIN:
971+ nla_memcpy(&info->range.min_addr, a, nla_len(a));
972+ info->range.flags |= NF_NAT_RANGE_MAP_IPS;
973+ break;
974+
975+ case OVS_NAT_ATTR_IP_MAX:
976+ have_ip_max = true;
977+ nla_memcpy(&info->range.max_addr, a,
978+ sizeof(info->range.max_addr));
979+ info->range.flags |= NF_NAT_RANGE_MAP_IPS;
980+ break;
981+
982+ case OVS_NAT_ATTR_PROTO_MIN:
983+ info->range.min_proto.all = htons(nla_get_u16(a));
984+ info->range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
985+ break;
986+
987+ case OVS_NAT_ATTR_PROTO_MAX:
988+ have_proto_max = true;
989+ info->range.max_proto.all = htons(nla_get_u16(a));
990+ info->range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
991+ break;
992+
993+ case OVS_NAT_ATTR_PERSISTENT:
994+ info->range.flags |= NF_NAT_RANGE_PERSISTENT;
995+ break;
996+
997+ case OVS_NAT_ATTR_PROTO_HASH:
998+ info->range.flags |= NF_NAT_RANGE_PROTO_RANDOM;
999+ break;
1000+
1001+ case OVS_NAT_ATTR_PROTO_RANDOM:
1002+ info->range.flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY;
1003+ break;
1004+
1005+ default:
1006+ OVS_NLERR(log, "Unknown nat attribute (%d).\n", type);
1007+ return -EINVAL;
1008+ }
1009+ }
1010+
1011+ if (rem > 0) {
1012+ OVS_NLERR(log, "NAT attribute has %d unknown bytes.\n", rem);
1013+ return -EINVAL;
1014+ }
1015+ if (!info->nat) {
1016+ /* Do not allow flags if no type is given. */
1017+ if (info->range.flags) {
1018+ OVS_NLERR(log,
1019+ "NAT flags may be given only when NAT range (SRC or DST) is also specified.\n"
1020+ );
1021+ return -EINVAL;
1022+ }
1023+ info->nat = OVS_CT_NAT; /* NAT existing connections. */
1024+ } else if (!info->commit) {
1025+ OVS_NLERR(log,
1026+ "NAT attributes may be specified only when CT COMMIT flag is also specified.\n"
1027+ );
1028+ return -EINVAL;
1029+ }
1030+ /* Allow missing IP_MAX. */
1031+ if (info->range.flags & NF_NAT_RANGE_MAP_IPS && !have_ip_max) {
1032+ memcpy(&info->range.max_addr, &info->range.min_addr,
1033+ sizeof(info->range.max_addr));
1034+ }
1035+ /* Allow missing PROTO_MAX. */
1036+ if (info->range.flags & NF_NAT_RANGE_PROTO_SPECIFIED &&
1037+ !have_proto_max) {
1038+ info->range.max_proto.all = info->range.min_proto.all;
1039+ }
1040+ return 0;
1041+}
1042+#endif
1043+
6561044 static const struct ovs_ct_len_tbl ovs_ct_attr_lens[OVS_CT_ATTR_MAX + 1] = {
6571045 [OVS_CT_ATTR_COMMIT] = { .minlen = 0, .maxlen = 0 },
6581046 [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] = { · 3 unmodified lines
6621050 [OVS_CT_ATTR_LABELS] = { .minlen = sizeof(struct md_labels),
6631051 .maxlen = sizeof(struct md_labels) },
6641052 [OVS_CT_ATTR_HELPER] = { .minlen = 1,
665− .maxlen = NF_CT_HELPER_NAME_LEN }
1053+ .maxlen = NF_CT_HELPER_NAME_LEN },
1054+#ifdef CONFIG_NF_NAT_NEEDED
1055+ /* NAT length is checked when parsing the nested attributes. */
1056+ [OVS_CT_ATTR_NAT] = { .minlen = 0, .maxlen = INT_MAX },
1057+#endif
6661058 };
6671059
6681060 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, · 60 unmodified lines
7291121 return -EINVAL;
7301122 }
7311123 break;
1124+#ifdef CONFIG_NF_NAT_NEEDED
1125+ case OVS_CT_ATTR_NAT: {
1126+ int err = parse_nat(a, info, log);
1127+
1128+ if (err)
1129+ return err;
1130+ break;
1131+ }
1132+#endif
7321133 default:
7331134 OVS_NLERR(log, "Unknown conntrack attr (%d)",
7341135 type);
@@ -816,6 +1217,74 @@ err_free_ct: · 81 unmodified lines
8161217 return err;
8171218 }
8181219
1220+#ifdef CONFIG_NF_NAT_NEEDED
1221+static bool ovs_ct_nat_to_attr(const struct ovs_conntrack_info *info,
1222+ struct sk_buff *skb)
1223+{
1224+ struct nlattr *start;
1225+
1226+ start = nla_nest_start(skb, OVS_CT_ATTR_NAT);
1227+ if (!start)
1228+ return false;
1229+
1230+ if (info->nat & OVS_CT_SRC_NAT) {
1231+ if (nla_put_flag(skb, OVS_NAT_ATTR_SRC))
1232+ return false;
1233+ } else if (info->nat & OVS_CT_DST_NAT) {
1234+ if (nla_put_flag(skb, OVS_NAT_ATTR_DST))
1235+ return false;
1236+ } else {
1237+ goto out;
1238+ }
1239+
1240+ if (info->range.flags & NF_NAT_RANGE_MAP_IPS) {
1241+ if (info->family == NFPROTO_IPV4) {
1242+ if (nla_put_in_addr(skb, OVS_NAT_ATTR_IP_MIN,
1243+ info->range.min_addr.ip) ||
1244+ (info->range.max_addr.ip
1245+ != info->range.min_addr.ip &&
1246+ (nla_put_in_addr(skb, OVS_NAT_ATTR_IP_MAX,
1247+ info->range.max_addr.ip))))
1248+ return false;
1249+#if IS_ENABLED(CONFIG_NF_NAT_IPV6)
1250+ } else if (info->family == NFPROTO_IPV6) {
1251+ if (nla_put_in6_addr(skb, OVS_NAT_ATTR_IP_MIN,
1252+ &info->range.min_addr.in6) ||
1253+ (memcmp(&info->range.max_addr.in6,
1254+ &info->range.min_addr.in6,
1255+ sizeof(info->range.max_addr.in6)) &&
1256+ (nla_put_in6_addr(skb, OVS_NAT_ATTR_IP_MAX,
1257+ &info->range.max_addr.in6))))
1258+ return false;
1259+#endif
1260+ } else {
1261+ return false;
1262+ }
1263+ }
1264+ if (info->range.flags & NF_NAT_RANGE_PROTO_SPECIFIED &&
1265+ (nla_put_u16(skb, OVS_NAT_ATTR_PROTO_MIN,
1266+ ntohs(info->range.min_proto.all)) ||
1267+ (info->range.max_proto.all != info->range.min_proto.all &&
1268+ nla_put_u16(skb, OVS_NAT_ATTR_PROTO_MAX,
1269+ ntohs(info->range.max_proto.all)))))
1270+ return false;
1271+
1272+ if (info->range.flags & NF_NAT_RANGE_PERSISTENT &&
1273+ nla_put_flag(skb, OVS_NAT_ATTR_PERSISTENT))
1274+ return false;
1275+ if (info->range.flags & NF_NAT_RANGE_PROTO_RANDOM &&
1276+ nla_put_flag(skb, OVS_NAT_ATTR_PROTO_HASH))
1277+ return false;
1278+ if (info->range.flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY &&
1279+ nla_put_flag(skb, OVS_NAT_ATTR_PROTO_RANDOM))
1280+ return false;
1281+out:
1282+ nla_nest_end(skb, start);
1283+
1284+ return true;
1285+}
1286+#endif
1287+
8191288 int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info,
8201289 struct sk_buff *skb)
8211290 {
@@ -844,7 +1313,10 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *ct_info, · 22 unmodified lines
8441313 ct_info->helper->name))
8451314 return -EMSGSIZE;
8461315 }
847−
1316+#ifdef CONFIG_NF_NAT_NEEDED
1317+ if (ct_info->nat && !ovs_ct_nat_to_attr(ct_info, skb))
1318+ return -EMSGSIZE;
1319+#endif
8481320 nla_nest_end(skb, start);
8491321
8501322 return 0;
net/openvswitch/conntrack.h+2−1
@@ -37,7 +37,8 @@ void ovs_ct_free_action(const struct nlattr *a); · 36 unmodified lines
3737
3838 #define CT_SUPPORTED_MASK (OVS_CS_F_NEW | OVS_CS_F_ESTABLISHED | \
3939 OVS_CS_F_RELATED | OVS_CS_F_REPLY_DIR | \
40− OVS_CS_F_INVALID | OVS_CS_F_TRACKED)
40+ OVS_CS_F_INVALID | OVS_CS_F_TRACKED | \
41+ OVS_CS_F_SRC_NAT | OVS_CS_F_DST_NAT)
4142 #else
4243 #include <linux/errno.h>
4344

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t