[ovs-dev] [PATCH 5/5] odp: Add stateful NAT action

Thomas Graf tgraf at noironetworks.com
Fri Sep 26 22:00:17 UTC 2014


WIP
---
 lib/dpif-netdev.c            |   1 +
 lib/dpif.c                   |   1 +
 lib/odp-execute.c            |   1 +
 lib/odp-util.c               | 113 +++++++++++++++++++++++++++++++++++++++++++
 lib/ofp-actions.c            | 104 +++++++++++++++++++++++++++++++++++++++
 lib/ofp-actions.h            |  22 +++++++++
 ofproto/ofproto-dpif-xlate.c |  21 ++++++++
 7 files changed, 263 insertions(+)

diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 82dbfd0..a2f9e71 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -2908,6 +2908,7 @@ dp_execute_cb(void *aux_, struct dpif_packet **packets, int cnt,
     case OVS_ACTION_ATTR_SAMPLE:
     case OVS_ACTION_ATTR_UNSPEC:
     case OVS_ACTION_ATTR_CONNTRACK:
+    case OVS_ACTION_ATTR_NAT:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
diff --git a/lib/dpif.c b/lib/dpif.c
index bdb0564..c63f3f3 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1045,6 +1045,7 @@ dpif_execute_helper_cb(void *aux_, struct dpif_packet **packets, int cnt,
     case OVS_ACTION_ATTR_SAMPLE:
     case OVS_ACTION_ATTR_UNSPEC:
     case OVS_ACTION_ATTR_CONNTRACK:
+    case OVS_ACTION_ATTR_NAT:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 59ce7e3..ae346e8 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -529,6 +529,7 @@ odp_execute_actions__(void *dp, struct dpif_packet **packets, int cnt,
             break;
 
         case OVS_ACTION_ATTR_CONNTRACK:
+        case OVS_ACTION_ATTR_NAT:
             /* xxx I don't think there's anything we can do here. */
             break;
 
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 061c8d6..205a186 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -85,6 +85,7 @@ odp_action_len(uint16_t type)
     case OVS_ACTION_ATTR_SET_MASKED: return -2;
     case OVS_ACTION_ATTR_SAMPLE: return -2;
     case OVS_ACTION_ATTR_CONNTRACK: return -2;
+    case OVS_ACTION_ATTR_NAT: return -2;
 
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
@@ -526,6 +527,90 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
 }
 
 static void
+format_odp_nat_action(struct ds *ds, const struct nlattr *attr)
+{
+    static const struct nl_policy ovs_nat_policy[] = {
+        [OVS_NAT_ATTR_TYPE] = { .type = NL_A_U32 },
+        [OVS_NAT_ATTR_IP_MIN] = { .type = NL_A_U32,
+                                  .optional = true, },
+        [OVS_NAT_ATTR_IP_MAX] = { .type = NL_A_U32,
+                                  .optional = true, },
+        [OVS_NAT_ATTR_PROTO_MIN] = { .type = NL_A_U16,
+                                     .optional = true, },
+        [OVS_NAT_ATTR_PROTO_MAX] = { .type = NL_A_U16,
+                                     .optional = true, },
+        [OVS_NAT_ATTR_FLAGS] = { .type = NL_A_U32,
+                                 .optional = true, },
+    };
+    struct nlattr *a[ARRAY_SIZE(ovs_nat_policy)];
+    ovs_be32 ip_min = 0, ip_max = 0;
+    ovs_be16 proto_min = 0, proto_max = 0;
+    uint32_t type, flags = 0;
+
+    if (!nl_parse_nested(attr, ovs_nat_policy, a, ARRAY_SIZE(a))) {
+        ds_put_cstr(ds, "nat(error)");
+        return;
+    }
+
+    type = nl_attr_get_u32(a[OVS_NAT_ATTR_TYPE]);
+
+    if (a[OVS_NAT_ATTR_IP_MIN]) {
+        ip_min = nl_attr_get_be32(a[OVS_NAT_ATTR_IP_MIN]);
+    }
+
+    if (a[OVS_NAT_ATTR_IP_MAX]) {
+        ip_max = nl_attr_get_be32(a[OVS_NAT_ATTR_IP_MAX]);
+    }
+
+    if (a[OVS_NAT_ATTR_PROTO_MIN]) {
+        proto_min = nl_attr_get_be16(a[OVS_NAT_ATTR_PROTO_MIN]);
+    }
+
+    if (a[OVS_NAT_ATTR_PROTO_MAX]) {
+        proto_max = nl_attr_get_be16(a[OVS_NAT_ATTR_PROTO_MAX]);
+    }
+
+    if (a[OVS_NAT_ATTR_FLAGS]) {
+        flags = nl_attr_get_u32(a[OVS_NAT_ATTR_FLAGS]);
+    }
+
+    ds_put_format(ds, "nat(type=%u", type);
+
+    if (ip_min) {
+        ds_put_format(ds, ",ip_min="IP_FMT, IP_ARGS(ip_min));
+
+        if (ip_min != ip_max) {
+            ds_put_format(ds, ",ip_max="IP_FMT, IP_ARGS(ip_max));
+        }
+    }
+
+    if (proto_min) {
+        ds_put_format(ds, ":%"PRIu16, proto_min);
+
+        if (proto_min != proto_max) {
+            ds_put_format(ds, "-%"PRIu16, proto_min);
+        }
+    }
+
+    if (flags) {
+		ds_put_format(ds, ",flags=");
+        if (flags & OVS_NAT_FLAG_PERSISTENT) {
+            ds_put_format(ds, ",persistent");
+        }
+
+        if (flags & OVS_NAT_FLAG_PROTO_RAND) {
+            ds_put_format(ds, ",hash-map");
+        }
+
+        if (flags & OVS_NAT_FLAG_PROTO_FULL_RAND) {
+            ds_put_format(ds, ",random");
+        }
+    }
+
+    ds_put_format(ds, ")");
+}
+
+static void
 format_odp_action(struct ds *ds, const struct nlattr *a)
 {
     int expected_len;
@@ -612,6 +697,9 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
         format_odp_conntrack_action(ds,a);
         break;
     }
+    case OVS_ACTION_ATTR_NAT:
+        format_odp_nat_action(ds, a);
+        break;
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
     default:
@@ -915,6 +1003,31 @@ parse_odp_action(const char *s, const struct simap *port_names,
         }
     }
 
+    {
+        ovs_be32 ip_min = 0, ip_max = 0;
+        size_t nat_ofs;
+        int n = -1, flags = 0, type;
+
+        if (ovs_scan(s, "nat(type=%i,ip_min="IP_SCAN_FMT",ip_max="IP_SCAN_FMT",flags=%i)%n",
+			         &type, IP_SCAN_ARGS(&ip_min), IP_SCAN_ARGS(&ip_max), &flags, &n)) {
+
+            nat_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_NAT);
+
+            /* FIXME: Add correct parsers */
+            nl_msg_put_u32(actions, OVS_NAT_ATTR_TYPE, type);
+
+            if (ip_min) {
+                nl_msg_put_be32(actions, OVS_NAT_ATTR_IP_MIN, ip_min);
+                nl_msg_put_be32(actions, OVS_NAT_ATTR_IP_MAX, ip_max);
+            }
+
+            nl_msg_end_nested(actions, nat_ofs);
+
+            return n;
+
+        }
+    }
+
     return -EINVAL;
 }
 
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 0ab8a94..720bc45 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -270,6 +270,9 @@ enum ofp_raw_action_type {
 
     /* NX1.0+(32): struct nx_action_conntrack. */
     NXAST_RAW_CONNTRACK,
+
+    /* NX1.0+(33): struct nx_action_nat. */
+    NXAST_RAW_NAT,
 };
 
 /* OpenFlow actions are always a multiple of 8 bytes in length. */
@@ -4007,6 +4010,100 @@ format_CONNTRACK(const struct ofpact_conntrack *a, struct ds *s)
                   a->flags, a->zone);
 }
 
+/* Action structure for NXAST_NAT.
+ *
+ * Perform stateful network address translation.
+ * Pass traffic to the connection tracker.  If 'flags' is
+ * NX_CONNTRACK_F_RECIRC, traffic is recirculated back to flow table
+ * with the NXM_NX_CONN_STATE and NXM_NX_CONN_STATE_W matches set.  A
+ * standard "resubmit" action is not sufficient, since connection
+ * tracking occurs outside of the classifier.  The 'zone' argument
+ * specifies a context within which the tracking is done. */
+struct nx_action_nat {
+    ovs_be16 type;              /* OFPAT_VENDOR. */
+    ovs_be16 len;               /* 16. */
+    ovs_be32 vendor;            /* NX_VENDOR_ID. */
+    ovs_be16 subtype;           /* NXAST_NAT. */
+    ovs_be16 flags;             /* Either 0 or NX_CONNTRACK_F_RECIRC. */
+    ovs_be32 nat_type;          /* NAT type */
+    ovs_be32 ip_min;            /* Minimum IP range */
+    ovs_be32 ip_max;            /* Maximum IP range */
+    ovs_be16 proto_min;         /* Minimum L4 protocol range */
+    ovs_be16 proto_max;         /* Maximum L4 protocol  range */
+    uint8_t  pad[4];
+};
+OFP_ASSERT(sizeof(struct nx_action_nat) == 32);
+
+static enum ofperr
+decode_NXAST_RAW_NAT(const struct nx_action_nat *nan,
+                           struct ofpbuf *out)
+{
+    struct ofpact_nat *nat;
+
+    nat = ofpact_put_NAT(out);
+    nat->flags = ntohs(nan->flags);
+    nat->nat_type = ntohl(nan->nat_type);
+    nat->ip_min = nan->ip_min;
+    nat->ip_max = nan->ip_max;
+
+    return 0;
+}
+
+static void
+encode_NAT(const struct ofpact_nat *nat,
+           enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
+{
+    struct nx_action_nat *nan;
+
+    nan = put_NXAST_NAT(out);
+    nan->flags = htons(nat->flags);
+    nan->nat_type = htonl(nat->nat_type);
+    nan->ip_min = nat->ip_min;
+    nan->ip_max = nat->ip_max;
+}
+
+/* Parses 'arg' as the argument to a "nat" action, and appends such an
+ * action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error.  The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+parse_NAT(char *arg, struct ofpbuf *ofpacts,
+             enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+    struct ofpact_nat *on = ofpact_put_NAT(ofpacts);
+    char *key, *value;
+
+    while (ofputil_parse_key_value(&arg, &key, &value)) {
+        char *error = NULL;
+
+        if (!strcmp(key, "type")) {
+            error = str_to_u32(value, &on->nat_type);
+        } else if (!strcmp(key, "flags")) {
+            error = str_to_u16(value, "flags", &on->flags);
+        } else if (!strcmp(key, "ip_min")) {
+            error = str_to_ip(value, &on->ip_min);
+        } else if (!strcmp(key, "ip_max")) {
+            error = str_to_ip(value, &on->ip_max);
+        } else {
+            error = xasprintf("invalid key \"%s\" in \"nat\" argument",
+                              key);
+        }
+        if (error) {
+            return error;
+        }
+    }
+    return NULL;
+}
+
+static void
+format_NAT(const struct ofpact_nat *a, struct ds *s)
+{
+    ds_put_format(s, "nat(type=%"PRIu32",ip_min="IP_FMT"," \
+                     "ip_max="IP_FMT",flags=%"PRIu16")",
+                     a->nat_type, IP_ARGS(a->ip_min), IP_ARGS(a->ip_max), a->flags);
+}
+
 /* Meter instruction. */
 
 static void
@@ -4388,6 +4485,7 @@ ofpact_is_set_or_move_action(const struct ofpact *a)
     case OFPACT_BUNDLE:
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_CONNTRACK:
+    case OFPACT_NAT:
     case OFPACT_CONTROLLER:
     case OFPACT_DEC_MPLS_TTL:
     case OFPACT_DEC_TTL:
@@ -4461,6 +4559,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
     case OFPACT_BUNDLE:
     case OFPACT_CONTROLLER:
     case OFPACT_CONNTRACK:
+    case OFPACT_NAT:
     case OFPACT_ENQUEUE:
     case OFPACT_EXIT:
     case OFPACT_FIN_TIMEOUT:
@@ -4686,6 +4785,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
     case OFPACT_EXIT:
     case OFPACT_SAMPLE:
     case OFPACT_CONNTRACK:
+    case OFPACT_NAT:
     default:
         return OVSINST_OFPIT11_APPLY_ACTIONS;
     }
@@ -5250,6 +5350,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
     case OFPACT_CONNTRACK:
         return 0;
 
+    case OFPACT_NAT:
+        return 0;
+
     case OFPACT_CLEAR_ACTIONS:
         return 0;
 
@@ -5670,6 +5773,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
     case OFPACT_METER:
     case OFPACT_GROUP:
     case OFPACT_CONNTRACK:
+    case OFPACT_NAT:
     default:
         return false;
     }
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index a3a4b41..66754f5 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -106,6 +106,7 @@
     OFPACT(EXIT,            ofpact_null,        ofpact, "exit")         \
     OFPACT(SAMPLE,          ofpact_sample,      ofpact, "sample")       \
     OFPACT(CONNTRACK,       ofpact_conntrack,   ofpact, "conntrack")    \
+    OFPACT(NAT,             ofpact_nat,         ofpact, "nat")          \
                                                                         \
     /* Instructions. */                                                 \
     OFPACT(METER,           ofpact_meter,       ofpact, "meter")        \
@@ -490,6 +491,27 @@ struct ofpact_conntrack {
     uint16_t zone;
 };
 
+/* Bits for 'flags' in struct nx_action_nat.
+ */
+enum nx_nat_flags {
+    NX_NAT_F_PERSISTENT = 1 << 0,
+    NX_NAT_F_PROTO_RAND = 1 << 1,
+    NX_NAT_F_PROTO_FULL_RAND = 1 << 2,
+};
+
+/* OFPACT_NAT.
+ *
+ * Used for NXAST_NAT. */
+struct ofpact_nat {
+    struct ofpact ofpact;
+    uint32_t nat_type;
+    ovs_be32 ip_min;
+    ovs_be32 ip_max;
+    ovs_be16 proto_min;
+    ovs_be16 proto_max;
+    uint16_t flags;
+};
+
 static inline size_t
 ofpact_nest_get_action_len(const struct ofpact_nest *on)
 {
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index bb187a1..a341c84 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -3591,6 +3591,7 @@ ofpact_needs_recirculation_after_mpls(const struct xlate_ctx *ctx,
     case OFPACT_CLEAR_ACTIONS:
     case OFPACT_SAMPLE:
     case OFPACT_CONNTRACK:
+    case OFPACT_NAT:
         return false;
 
     case OFPACT_SET_IPV4_SRC:
@@ -3652,6 +3653,22 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
 }
 
 static void
+compose_nat_action(struct xlate_ctx *ctx, struct ofpact_nat *ofn)
+{
+    size_t nat_offset;
+    struct ofpbuf *odp_actions = ctx->xout->odp_actions;
+
+    nat_offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_NAT);
+    nl_msg_put_u32(odp_actions, OVS_NAT_ATTR_TYPE, ofn->nat_type);
+    nl_msg_put_u32(odp_actions, OVS_NAT_ATTR_IP_MIN, ofn->ip_min);
+    nl_msg_put_u32(odp_actions, OVS_NAT_ATTR_IP_MAX, ofn->ip_max);
+    nl_msg_put_u16(odp_actions, OVS_NAT_ATTR_PROTO_MIN, ofn->proto_min);
+    nl_msg_put_u16(odp_actions, OVS_NAT_ATTR_PROTO_MIN, ofn->proto_max);
+    nl_msg_put_u32(odp_actions, OVS_NAT_ATTR_FLAGS, ofn->flags);
+    nl_msg_end_nested(odp_actions, nat_offset);
+}
+
+static void
 do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
                  struct xlate_ctx *ctx)
 {
@@ -3957,6 +3974,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
         case OFPACT_CONNTRACK:
             compose_conntrack_action(ctx, ofpact_get_CONNTRACK(a));
             break;
+
+        case OFPACT_NAT:
+            compose_nat_action(ctx, ofpact_get_NAT(a));
+            break;
         }
     }
 }
-- 
1.9.3




More information about the dev mailing list