[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