@@ -465,6 +465,7 @@ enum ofp_header_type_namespaces {
OFPHTN_IP_PROTO = 2, /* ns_type is a IP protocol number. */
OFPHTN_UDP_TCP_PORT = 3, /* ns_type is a TCP or UDP port. */
OFPHTN_IPV4_OPTION = 4, /* ns_type is an IPv4 option number. */
+ OFPHTN_N_TYPES
};
#endif /* openflow/openflow-common.h */
@@ -12,6 +12,7 @@ openvswitchinclude_HEADERS = \
include/openvswitch/meta-flow.h \
include/openvswitch/ofpbuf.h \
include/openvswitch/ofp-actions.h \
+ include/openvswitch/ofp-ed-props.h \
include/openvswitch/ofp-errors.h \
include/openvswitch/ofp-msgs.h \
include/openvswitch/ofp-parse.h \
@@ -25,6 +25,7 @@
#include "openvswitch/ofp-util.h"
#include "openvswitch/ofp-errors.h"
#include "openvswitch/types.h"
+#include "openvswitch/ofp-ed-props.h"
struct vl_mff_map;
@@ -89,6 +90,10 @@ struct vl_mff_map;
OFPACT(PUSH_MPLS, ofpact_push_mpls, ofpact, "push_mpls") \
OFPACT(POP_MPLS, ofpact_pop_mpls, ofpact, "pop_mpls") \
\
+ /* Generic encap & decap */ \
+ OFPACT(ENCAP, ofpact_encap, props, "encap") \
+ OFPACT(DECAP, ofpact_decap, ofpact, "decap") \
+ \
/* Metadata. */ \
OFPACT(SET_TUNNEL, ofpact_tunnel, ofpact, "set_tunnel") \
OFPACT(SET_QUEUE, ofpact_queue, ofpact, "set_queue") \
@@ -969,6 +974,32 @@ struct ofpact_unroll_xlate {
ovs_be64 rule_cookie; /* OVS_BE64_MAX if none. */
};
+/* OFPACT_ENCAP.
+ *
+ * Used for OFPAT_ENCAP. */
+
+#define OFPACT_ENCAP_MAX_PROP_SIZE 256
+
+struct ofpact_encap {
+ struct ofpact ofpact;
+ ovs_be32 new_pkt_type; /* Packet type of the header to add. */
+ uint16_t hdr_size; /* New header size in bytes. */
+ uint16_t n_props; /* Number of encap properties. */
+ struct ofpact_ed_prop props[]; /* Properties in internal format. */
+};
+
+/* OFPACT_DECAP.
+ *
+ * Used for OFPAT_DECAP. */
+struct ofpact_decap {
+ struct ofpact ofpact;
+ ovs_be32 new_pkt_type; /* New packet type. The special value
+ (0,0xFFFE) "Use next proto" is used to
+ request OvS to automatically set the
+ new packet type based on the decap'ed
+ header's next protocol.*/
+};
+
/* Converting OpenFlow to ofpacts. */
enum ofperr ofpacts_pull_openflow_actions(struct ofpbuf *openflow,
unsigned int actions_len,
new file mode 100644
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2017 Intel, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OPENVSWITCH_OFP_ED_PROPS_H
+#define OPENVSWITCH_OFP_ED_PROPS_H 1
+
+#include "openvswitch/ofp-errors.h"
+#include "openvswitch/types.h"
+#include "openvswitch/ofpbuf.h"
+
+enum ofp_ed_prop_class {
+ OFPPPC_BASIC = 0, /* ONF Basic class. */
+ OFPPPC_MPLS = 1, /* MPLS property class. */
+ OFPPPC_GRE = 2, /* GRE property class. */
+ OFPPPC_GTP = 3, /* GTP property class. */
+ /* new values go here */
+ OFPPPC_EXPERIMENTER=0xffff, /* Experimenter property class.
+ * First 32 bits of property data is exp
+ * id after that is the experimenter
+ * property data. */
+};
+
+/*
+ * External representation of encap/decap properties.
+ * These must be padded to a multiple of 4 bytes.
+ */
+
+struct ofp_ed_prop_header {
+ ovs_be16 prop_class;
+ uint8_t type;
+ uint8_t len;
+};
+
+/*
+ * Internal representation of encap/decap properties
+ */
+
+struct ofpact_ed_prop {
+ uint16_t prop_class;
+ uint8_t type;
+ uint8_t len;
+};
+
+enum ofperr decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop,
+ struct ofpbuf *out, size_t *remaining);
+enum ofperr encode_ed_prop(const struct ofpact_ed_prop **prop,
+ struct ofpbuf *out);
+bool parse_ed_prop_class(const char *str, uint16_t *prop_class);
+bool parse_ed_prop_type(uint16_t prop_class, const char *str, uint8_t *type);
+char *parse_ed_prop_value(uint16_t prop_class, uint8_t prop_type,
+ const char *str, struct ofpbuf *out);
+char *format_ed_prop_class(const struct ofpact_ed_prop *prop);
+char *format_ed_prop_type(const struct ofpact_ed_prop *prop);
+void format_ed_prop_value(struct ds *s, const struct ofpact_ed_prop *prop);
+
+#endif /* ofp-ed-props.h */
@@ -272,6 +272,15 @@ enum ofperr {
* 64. */
OFPERR_NXBAC_BAD_CONJUNCTION,
+ /* NX1.3+(39). Unsupported packet type in encap or decap. */
+ OFPERR_NXBAC_BAD_HEADER_TYPE,
+
+ /* NX1.3+(40). Unrecognized encap or decap property. */
+ OFPERR_NXBAC_UNKNOWN_ED_PROP,
+
+ /* NX1.3+(41). Error in encap or decap property. */
+ OFPERR_NXBAC_BAD_ED_PROP,
+
/* ## --------------------- ## */
/* ## OFPET_BAD_INSTRUCTION ## */
/* ## --------------------- ## */
@@ -149,6 +149,7 @@ lib_libopenvswitch_la_SOURCES = \
lib/odp-util.c \
lib/odp-util.h \
lib/ofp-actions.c \
+ lib/ofp-ed-props.c \
lib/ofp-errors.c \
lib/ofp-msgs.c \
lib/ofp-parse.c \
@@ -5863,13 +5863,17 @@ put_ethernet_key(const struct ovs_key_ethernet *eth, struct flow *flow)
}
static void
-commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow,
- struct ofpbuf *odp_actions,
- struct flow_wildcards *wc,
- bool use_masked)
+commit_set_ether_action(const struct flow *flow, struct flow *base_flow,
+ struct ofpbuf *odp_actions,
+ struct flow_wildcards *wc,
+ bool use_masked)
{
struct ovs_key_ethernet key, base, mask;
+ if (flow->packet_type != htonl(PT_ETH)) {
+ return;
+ }
+
get_ethernet_key(flow, &key);
get_ethernet_key(base_flow, &base);
get_ethernet_key(&wc->masks, &mask);
@@ -5882,29 +5886,6 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow,
}
static void
-commit_ether_action(const struct flow *flow, struct flow *base_flow,
- struct ofpbuf *odp_actions, struct flow_wildcards *wc,
- bool use_masked)
-{
- if (flow->packet_type == htonl(PT_ETH)) {
- if (base_flow->packet_type != htonl(PT_ETH)) {
- odp_put_push_eth_action(odp_actions, &flow->dl_src, &flow->dl_dst);
- base_flow->packet_type = flow->packet_type;
- base_flow->dl_src = flow->dl_src;
- base_flow->dl_dst = flow->dl_dst;
- } else {
- commit_set_ether_addr_action(flow, base_flow, odp_actions, wc,
- use_masked);
- }
- } else {
- if (base_flow->packet_type == htonl(PT_ETH)) {
- odp_put_pop_eth_action(odp_actions);
- base_flow->packet_type = flow->packet_type;
- }
- }
-}
-
-static void
commit_vlan_action(const struct flow* flow, struct flow *base,
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
{
@@ -6341,6 +6322,50 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow,
}
}
+static void
+commit_packet_type_change(const struct flow *flow,
+ struct flow *base_flow,
+ struct ofpbuf *odp_actions,
+ struct flow_wildcards *wc,
+ bool pending_encap)
+{
+ if (flow->packet_type == base_flow->packet_type) {
+ return;
+ }
+
+ if (pending_encap) {
+ switch (ntohl(flow->packet_type)) {
+ case PT_ETH: {
+ /* push_eth */
+ odp_put_push_eth_action(odp_actions, &flow->dl_src,
+ &flow->dl_dst);
+ base_flow->packet_type = flow->packet_type;
+ base_flow->dl_src = flow->dl_src;
+ base_flow->dl_dst = flow->dl_dst;
+ break;
+ }
+ default:
+ /* Only the above protocols are supported for encap. The check
+ * is done at action decoding. */
+ OVS_NOT_REACHED();
+ }
+ } else {
+ if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE &&
+ base_flow->packet_type == htonl(PT_ETH)) {
+ /* pop_eth */
+ odp_put_pop_eth_action(odp_actions);
+ base_flow->packet_type = flow->packet_type;
+ base_flow->dl_src = eth_addr_zero;
+ base_flow->dl_dst = eth_addr_zero;
+ } else {
+ /* All other cases are handled through recirculation. */
+ OVS_NOT_REACHED();
+ }
+ }
+
+ wc->masks.packet_type = OVS_BE32_MAX;
+}
+
/* If any of the flow key data that ODP actions can modify are different in
* 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
* key from 'base' into 'flow', and then changes 'base' the same way. Does not
@@ -6353,12 +6378,13 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow,
enum slow_path_reason
commit_odp_actions(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
- bool use_masked)
+ bool use_masked, bool pending_encap)
{
enum slow_path_reason slow1, slow2;
bool mpls_done = false;
- commit_ether_action(flow, base, odp_actions, wc, use_masked);
+ commit_packet_type_change(flow, base, odp_actions, wc, pending_encap);
+ commit_set_ether_action(flow, base, odp_actions, wc, use_masked);
/* Make packet a non-MPLS packet before committing L3/4 actions,
* which would otherwise do nothing. */
if (eth_type_mpls(base->dl_type) && !eth_type_mpls(flow->dl_type)) {
@@ -274,7 +274,8 @@ enum slow_path_reason commit_odp_actions(const struct flow *,
struct flow *base,
struct ofpbuf *odp_actions,
struct flow_wildcards *wc,
- bool use_masked);
+ bool use_masked,
+ bool pending_encap);
/* ofproto-dpif interface.
*
@@ -342,6 +342,12 @@ enum ofp_raw_action_type {
/* NX1.0+(43): void. */
NXAST_RAW_CT_CLEAR,
+ /* NX1.3+(46): struct nx_action_encap, ... */
+ NXAST_RAW_ENCAP,
+
+ /* NX1.3+(47): struct nx_action_decap, ... */
+ NXAST_RAW_DECAP,
+
/* ## ------------------ ## */
/* ## Debugging actions. ## */
/* ## ------------------ ## */
@@ -472,6 +478,8 @@ ofpact_next_flattened(const struct ofpact *ofpact)
case OFPACT_WRITE_METADATA:
case OFPACT_GOTO_TABLE:
case OFPACT_NAT:
+ case OFPACT_ENCAP:
+ case OFPACT_DECAP:
return ofpact_next(ofpact);
case OFPACT_CLONE:
@@ -4019,6 +4027,318 @@ format_FIN_TIMEOUT(const struct ofpact_fin_timeout *a,
ds_chomp(s, ',');
ds_put_format(s, "%s)%s", colors.paren, colors.end);
}
+
+/* Action structure for OFPAT_ENCAP */
+struct nx_action_encap {
+ ovs_be16 type; /* OFPAT_ENCAP */
+ ovs_be16 len; /* Total size including any property TLVs. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_ENCAP. */
+ ovs_be16 hdr_size; /* (optional) Header size in bytes to add,
+ 0=not specified.*/
+ ovs_be32 new_pkt_type; /* Header type to add and PACKET_TYPE of result. */
+ struct ofp_ed_prop_header props[];
+ /* Encap TLV properties. */
+};
+OFP_ASSERT(sizeof(struct nx_action_encap) == 16);
+
+static enum ofperr
+decode_NXAST_RAW_ENCAP(const struct nx_action_encap *nae,
+ enum ofp_version ofp_version OVS_UNUSED,
+ struct ofpbuf *out)
+{
+ struct ofpact_encap *encap;
+ const struct ofp_ed_prop_header *ofp_prop;
+ size_t props_len;
+ int err;
+
+ encap = ofpact_put_ENCAP(out);
+ encap->ofpact.raw = NXAST_RAW_ENCAP;
+ switch (ntohl(nae->new_pkt_type)) {
+ case PT_ETH:
+ /* Add supported encap header types here. */
+ break;
+ default:
+ return OFPERR_NXBAC_BAD_HEADER_TYPE;
+ }
+ encap->new_pkt_type = nae->new_pkt_type;
+ encap->hdr_size = ntohs(nae->hdr_size);
+
+ ofp_prop = nae->props;
+ props_len = ntohs(nae->len) - offsetof(struct nx_action_encap, props);
+ while (props_len > 0) {
+ err = decode_ed_prop(&ofp_prop, out, &props_len);
+ if (err) {
+ return err;
+ }
+ encap->n_props++;
+ }
+ out->header = &encap->ofpact;
+ ofpact_finish_ENCAP(out, &encap);
+
+ return 0;
+}
+
+static void
+encode_ENCAP(const struct ofpact_encap *encap,
+ enum ofp_version ofp_version OVS_UNUSED,
+ struct ofpbuf *out)
+{
+ size_t start_ofs = out->size;
+ struct nx_action_encap *nae = put_NXAST_ENCAP(out);
+ int i;
+
+ nae->new_pkt_type = encap->new_pkt_type;
+ nae->hdr_size = htons(encap->hdr_size);
+ const struct ofpact_ed_prop *prop = encap->props;
+ for (i = 0; i < encap->n_props; i++) {
+ encode_ed_prop(&prop, out);
+ }
+ pad_ofpat(out, start_ofs);
+}
+
+static bool
+parse_encap_header(const char *hdr, ovs_be32 *packet_type)
+{
+ if (strcmp(hdr, "ethernet") == 0) {
+ *packet_type = htonl(PT_ETH);
+ } else {
+ return false;
+ }
+ return true;
+}
+
+static char *
+parse_ed_props(char **arg, int *n_props, struct ofpbuf *out)
+{
+ char *key, *value, *err, *prop, *str;
+ uint16_t prop_class;
+ uint8_t prop_type;
+
+ str = *arg;
+ while (ofputil_parse_key_value(arg, &key, &value)) {
+ if (strcmp(key, "prop") != 0) {
+ return xasprintf("Invalid encap argument: %s", key);
+ }
+ /* Parse property class. */
+ str = prop = value;
+ if (!ofputil_parse_key_value(&prop, &key, &value)
+ || strcmp(key, "class") != 0 || value == NULL) {
+ return xasprintf("Missing prop class: %s", str);
+ }
+ if (!parse_ed_prop_class(value, &prop_class)) {
+ return xasprintf("Invalid encap prop class: %s", value);
+ }
+ /* Parse property type. */
+ str = prop;
+ if (!ofputil_parse_key_value(&prop, &key, &value)
+ || strcmp(key, "type") != 0 || value == NULL) {
+ return xasprintf("Missing encap prop type: %s", str);
+ }
+ if (!parse_ed_prop_type(prop_class, value, &prop_type)) {
+ return xasprintf("Invalid property type: %s", value);
+ }
+ /* Parse the prop value dependent on class and type. */
+ str = prop;
+ if (!ofputil_parse_key_value(&prop, &key, &value)
+ || strcmp(key, "val") != 0 || value == NULL) {
+ return xasprintf("Missing encap prop value: %s", str);
+ }
+ err = parse_ed_prop_value(prop_class, prop_type, value, out);
+ if (err != NULL) {
+ return err;
+ }
+ (*n_props)++;
+ }
+ return NULL;
+}
+
+/* The string representation of the encap action is
+ * encap(hdr=<pkt_type>,prop(class=<class>,type=<type>,val=<val>),prop(...),...)
+ */
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_ENCAP(char *arg,
+ const struct ofputil_port_map *port_map OVS_UNUSED,
+ struct ofpbuf *out,
+ enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+ struct ofpact_encap *encap;
+ char *key, *value, *str;
+ char *error = NULL;
+ size_t start_ofs;
+ int n_props = 0;
+
+ start_ofs = out->size;
+ encap = ofpact_put_ENCAP(out);
+ encap->hdr_size = 0;
+ /* Parse encap header type. */
+ str = arg;
+ if (!ofputil_parse_key_value(&arg, &key, &value)
+ || strcmp(key, "hdr") != 0 || value == NULL) {
+ return xasprintf("Missing encap hdr: %s", str);
+ }
+ if (!parse_encap_header(value, &encap->new_pkt_type)) {
+ return xasprintf("Encap hdr not supported: %s", value);
+ }
+ /* Parse encap properties. */
+ error = parse_ed_props(&arg, &n_props, out);
+ if (error != NULL) {
+ return error;
+ }
+ /* ofbuf out may have been re-allocated. */
+ encap = ALIGNED_CAST(struct ofpact_encap *, (char *)out->data + start_ofs);
+ encap->n_props = n_props;
+ ofpact_finish_ENCAP(out, &encap);
+ return NULL;
+}
+
+static char *
+format_encap_pkt_type(const ovs_be32 pkt_type)
+{
+ switch (ntohl(pkt_type)) {
+ case PT_ETH:
+ return "ethernet";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static void
+format_ed_props(struct ds *s, uint16_t n_props,
+ const struct ofpact_ed_prop *prop)
+{
+ const uint8_t *p = (uint8_t *) prop;
+ int i;
+
+ if (n_props == 0) {
+ return;
+ }
+ for (i = 0; i < n_props; i++) {
+ ds_put_format(s, ",prop(class=%s,", format_ed_prop_class(prop));
+ ds_put_format(s, "type=%s,", format_ed_prop_type(prop));
+ format_ed_prop_value(s, prop);
+ ds_put_cstr(s, ")");
+ p += ROUND_UP(prop->len, 8);
+ prop = ALIGNED_CAST(const struct ofpact_ed_prop *, p);
+ }
+}
+
+static void
+format_ENCAP(const struct ofpact_encap *a,
+ const struct ofputil_port_map *port_map OVS_UNUSED,
+ struct ds *s)
+{
+ ds_put_format(s, "%sencap(%s", colors.paren, colors.end);
+ ds_put_format(s, "hdr=%s", format_encap_pkt_type(a->new_pkt_type));
+ format_ed_props(s, a->n_props, a->props);
+ ds_put_format(s, "%s)%s", colors.paren, colors.end);
+}
+
+/* Action structure for OFPAT_DECAP */
+struct nx_action_decap {
+ ovs_be16 type; /* OFPAT_DECAP */
+ ovs_be16 len; /* Total size including any property TLVs. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_DECAP. */
+ ovs_be16 hdr_size; /* (optional) Header size in bytes to remove,
+ 0=not specified. NOT SUPPORTED. */
+ ovs_be32 new_pkt_type; /* Packet type or result. The special value
+ (0,0xFFFE) "Use next proto" is used to
+ request OvS to automatically set the
+ new packet type based on the decap'ed
+ header's next protocol.*/
+ struct ofp_ed_prop_header props[];
+ /* Decap TLV properties. NOT SUPPORTED. */
+
+};
+OFP_ASSERT(sizeof(struct nx_action_decap) == 16);
+
+static enum ofperr
+decode_NXAST_RAW_DECAP(const struct nx_action_decap *nad,
+ enum ofp_version ofp_version OVS_UNUSED,
+ struct ofpbuf *ofpacts)
+{
+ struct ofpact_decap * decap;
+
+ decap = ofpact_put_DECAP(ofpacts);
+ decap->ofpact.raw = NXAST_RAW_DECAP;
+ decap->new_pkt_type = nad->new_pkt_type;
+ return 0;
+}
+
+static void
+encode_DECAP(const struct ofpact_decap *decap,
+ enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
+{
+ struct nx_action_decap *nad = put_NXAST_DECAP(out);
+
+ nad->len = htons(16);
+ nad->new_pkt_type = decap->new_pkt_type;
+ /* No support for hdr_size and decap property TLVs yet. */
+ nad->hdr_size = 0;
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_DECAP(char *arg,
+ const struct ofputil_port_map *port_map OVS_UNUSED,
+ struct ofpbuf *ofpacts,
+ enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+ struct ofpact_decap *decap;
+ char *key, *value, *pos;
+ char *error = NULL;
+ uint16_t ns, type;
+
+ decap = ofpact_put_DECAP(ofpacts);
+ /* Default next packet_type is PT_USE_NEXT_PROTO. */
+ decap->new_pkt_type = htonl(PT_USE_NEXT_PROTO);
+
+ /* Parse decap packet_type if given. */
+ if (ofputil_parse_key_value(&arg, &key, &value)) {
+ if (strcmp(key, "packet_type") == 0) {
+ pos = value;
+ if (!ofputil_parse_key_value(&pos, &key, &value)
+ || strcmp(key, "ns") != 0) {
+ return xstrdup("Missing packet_type attribute ns");
+ }
+ error = str_to_u16(value, "ns", &ns);
+ if (error) {
+ return error;
+ }
+ if (ns >= OFPHTN_N_TYPES) {
+ return xasprintf("Unsupported ns value: %"PRIu16, ns);
+ }
+ if (!ofputil_parse_key_value(&pos, &key, &value)
+ || strcmp(key, "type") != 0) {
+ return xstrdup("Missing packet_type attribute type");
+ }
+ error = str_to_u16(value, "type", &type);
+ if (error) {
+ return error;
+ }
+ decap->new_pkt_type = htonl(PACKET_TYPE(ns, type));
+ } else {
+ return xasprintf("Invalid decap argument: %s", key);
+ }
+ }
+ return NULL;
+}
+
+static void
+format_DECAP(const struct ofpact_decap *a,
+ const struct ofputil_port_map *port_map OVS_UNUSED,
+ struct ds *s)
+{
+ ds_put_format(s, "%sdecap(%s", colors.paren, colors.end);
+ if (a->new_pkt_type != htonl(PT_USE_NEXT_PROTO)) {
+ ds_put_format(s, "packet_type(ns=%"PRIu16",id=%#"PRIx16")",
+ pt_ns(a->new_pkt_type),
+ pt_ns_type(a->new_pkt_type));
+ }
+ ds_put_format(s, "%s)%s", colors.paren, colors.end);
+}
+
/* Action structures for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE, and
* NXAST_RESUBMIT_TABLE_CT.
@@ -6802,6 +7122,8 @@ ofpact_is_set_or_move_action(const struct ofpact *a)
case OFPACT_SET_TUNNEL:
case OFPACT_SET_VLAN_PCP:
case OFPACT_SET_VLAN_VID:
+ case OFPACT_ENCAP:
+ case OFPACT_DECAP:
return true;
case OFPACT_BUNDLE:
case OFPACT_CLEAR_ACTIONS:
@@ -6877,6 +7199,8 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
case OFPACT_SET_VLAN_PCP:
case OFPACT_SET_VLAN_VID:
case OFPACT_STRIP_VLAN:
+ case OFPACT_ENCAP:
+ case OFPACT_DECAP:
return true;
/* In general these actions are excluded because they are not part of
@@ -6984,6 +7308,8 @@ ofpacts_execute_action_set(struct ofpbuf *action_list,
/* The OpenFlow spec "Action Set" section specifies this order. */
ofpacts_copy_last(action_list, action_set, OFPACT_STRIP_VLAN);
ofpacts_copy_last(action_list, action_set, OFPACT_POP_MPLS);
+ ofpacts_copy_last(action_list, action_set, OFPACT_DECAP);
+ ofpacts_copy_last(action_list, action_set, OFPACT_ENCAP);
ofpacts_copy_last(action_list, action_set, OFPACT_PUSH_MPLS);
ofpacts_copy_last(action_list, action_set, OFPACT_PUSH_VLAN);
ofpacts_copy_last(action_list, action_set, OFPACT_DEC_TTL);
@@ -7127,6 +7453,8 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
case OFPACT_CT:
case OFPACT_CT_CLEAR:
case OFPACT_NAT:
+ case OFPACT_ENCAP:
+ case OFPACT_DECAP:
default:
return OVSINST_OFPIT11_APPLY_ACTIONS;
}
@@ -7488,8 +7816,8 @@ inconsistent_match(enum ofputil_protocol *usable_protocols)
*usable_protocols &= OFPUTIL_P_OF10_ANY;
}
-/* May modify flow->dl_type, flow->nw_proto and flow->vlan_tci,
- * caller must restore them.
+/* May modify flow->packet_type, flow->dl_type, flow->nw_proto and
+ * flow->vlan_tci, caller must restore them.
*
* Modifies some actions, filling in fields that could not be properly set
* without context. */
@@ -7501,6 +7829,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
struct flow *flow = &match->flow;
const struct ofpact_enqueue *enqueue;
const struct mf_field *mf;
+ ovs_be16 dl_type = get_dl_type(flow);
switch (a->type) {
case OFPACT_OUTPUT:
@@ -7578,7 +7907,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
case OFPACT_SET_IPV4_SRC:
case OFPACT_SET_IPV4_DST:
- if (flow->dl_type != htons(ETH_TYPE_IP)) {
+ if (dl_type != htons(ETH_TYPE_IP)) {
inconsistent_match(usable_protocols);
}
return 0;
@@ -7643,7 +7972,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
case OFPACT_SET_MPLS_TC:
case OFPACT_SET_MPLS_TTL:
case OFPACT_DEC_MPLS_TTL:
- if (!eth_type_mpls(flow->dl_type)) {
+ if (!eth_type_mpls(dl_type)) {
inconsistent_match(usable_protocols);
}
return 0;
@@ -7681,6 +8010,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
return 0;
case OFPACT_PUSH_MPLS:
+ if (flow->packet_type != htonl(PT_ETH)) {
+ inconsistent_match(usable_protocols);
+ }
flow->dl_type = ofpact_get_PUSH_MPLS(a)->ethertype;
/* The packet is now MPLS and the MPLS payload is opaque.
* Thus nothing can be assumed about the network protocol.
@@ -7689,7 +8021,8 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
return 0;
case OFPACT_POP_MPLS:
- if (!eth_type_mpls(flow->dl_type)) {
+ if (flow->packet_type != htonl(PT_ETH)
+ || !eth_type_mpls(dl_type)) {
inconsistent_match(usable_protocols);
}
flow->dl_type = ofpact_get_POP_MPLS(a)->ethertype;
@@ -7708,7 +8041,7 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
case OFPACT_CT: {
struct ofpact_conntrack *oc = ofpact_get_CT(a);
- if (!dl_type_is_ip_any(flow->dl_type)
+ if (!dl_type_is_ip_any(dl_type)
|| (flow->ct_state & CS_INVALID && oc->flags & NX_CT_F_COMMIT)
|| (oc->alg == IPPORT_FTP && flow->nw_proto != IPPROTO_TCP)
|| (oc->alg == IPPORT_TFTP && flow->nw_proto != IPPROTO_UDP)) {
@@ -7733,10 +8066,10 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
case OFPACT_NAT: {
struct ofpact_nat *on = ofpact_get_NAT(a);
- if (!dl_type_is_ip_any(flow->dl_type) ||
- (on->range_af == AF_INET && flow->dl_type != htons(ETH_TYPE_IP)) ||
+ if (!dl_type_is_ip_any(dl_type) ||
+ (on->range_af == AF_INET && dl_type != htons(ETH_TYPE_IP)) ||
(on->range_af == AF_INET6
- && flow->dl_type != htons(ETH_TYPE_IPV6))) {
+ && dl_type != htons(ETH_TYPE_IPV6))) {
return OFPERR_OFPBAC_MATCH_INCONSISTENT;
}
return 0;
@@ -7785,6 +8118,29 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
case OFPACT_DEBUG_RECIRC:
return 0;
+ case OFPACT_ENCAP:
+ flow->packet_type = ofpact_get_ENCAP(a)->new_pkt_type;
+ if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) {
+ flow->dl_type = htons(pt_ns_type(flow->packet_type));
+ }
+ if (!is_ip_any(flow)) {
+ flow->nw_proto = 0;
+ }
+ return 0;
+
+ case OFPACT_DECAP:
+ if (flow->packet_type == htonl(PT_ETH)) {
+ /* Adjust the packet_type to allow subsequent actions. */
+ flow->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE,
+ ntohs(flow->dl_type));
+ } else {
+ /* The actual packet_type is only known after decapsulation.
+ * Do not allow subsequent actions that depend on packet headers. */
+ flow->packet_type = htonl(PT_UNKNOWN);
+ flow->dl_type = OVS_BE16_MAX;
+ }
+ return 0;
+
default:
OVS_NOT_REACHED();
}
@@ -7810,6 +8166,7 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
enum ofputil_protocol *usable_protocols)
{
struct ofpact *a;
+ ovs_be32 packet_type = match->flow.packet_type;
ovs_be16 dl_type = match->flow.dl_type;
uint8_t nw_proto = match->flow.nw_proto;
enum ofperr error = 0;
@@ -7825,6 +8182,7 @@ ofpacts_check(struct ofpact ofpacts[], size_t ofpacts_len,
}
}
/* Restore fields that may have been modified. */
+ match->flow.packet_type = packet_type;
match->flow.dl_type = dl_type;
memcpy(&match->flow.vlans, &vlans, sizeof(vlans));
match->flow.nw_proto = nw_proto;
@@ -8276,6 +8634,8 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
case OFPACT_CT:
case OFPACT_CT_CLEAR:
case OFPACT_NAT:
+ case OFPACT_ENCAP:
+ case OFPACT_DECAP:
default:
return false;
}
new file mode 100644
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2017 Intel, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <arpa/inet.h>
+#include "openvswitch/ofp-ed-props.h"
+#include "openvswitch/ofp-util.h"
+#include "openvswitch/ofpbuf.h"
+#include "openvswitch/ofp-parse.h"
+#include "util.h"
+#include "lib/packets.h"
+
+
+enum ofperr
+decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop,
+ struct ofpbuf *out OVS_UNUSED,
+ size_t *remaining)
+{
+ uint16_t prop_class = ntohs((*ofp_prop)->prop_class);
+ size_t len = (*ofp_prop)->len;
+ size_t pad_len = ROUND_UP(len, 8);
+
+ switch (prop_class) {
+ default:
+ return OFPERR_NXBAC_UNKNOWN_ED_PROP;
+ }
+
+ *remaining -= pad_len;
+ *ofp_prop = (const struct ofp_ed_prop_header *)
+ ((char *)(*ofp_prop) + pad_len);
+ return 0;
+}
+
+enum ofperr
+encode_ed_prop(const struct ofpact_ed_prop **prop,
+ struct ofpbuf *out OVS_UNUSED)
+{
+ size_t prop_len;
+
+ switch ((*prop)->prop_class) {
+ default:
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+
+ *prop = (const struct ofpact_ed_prop *) ((char *)(*prop) + prop_len);
+ return 0;
+}
+
+bool
+parse_ed_prop_class(const char *str OVS_UNUSED,
+ uint16_t *prop_class)
+{
+ if (!strcmp(str,"basic")) {
+ *prop_class = OFPPPC_BASIC;
+ } else if (!strcmp(str,"mpls")) {
+ *prop_class = OFPPPC_MPLS;
+ } else if (!strcmp(str,"gre")) {
+ *prop_class = OFPPPC_GRE;
+ } else if (!strcmp(str,"gtp")) {
+ *prop_class = OFPPPC_GTP;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool
+parse_ed_prop_type(uint16_t prop_class,
+ const char *str OVS_UNUSED,
+ uint8_t *type OVS_UNUSED)
+{
+ switch (prop_class) {
+ default:
+ return false;
+ }
+}
+
+/* Parse the value of an encap/decap property based on property class
+ * and type and append the parsed property in internal format to the
+ * ofpbuf out.
+ * Returns a malloced string in the event of a parse error. The caller
+ * must free the string.
+ */
+
+char *
+parse_ed_prop_value(uint16_t prop_class, uint8_t prop_type OVS_UNUSED,
+ const char *value, struct ofpbuf *out OVS_UNUSED)
+{
+
+ if (value == NULL || *value == '\0') {
+ return xstrdup("Value missing for encap property");
+ }
+
+ switch (prop_class) {
+ default:
+ /* Unsupported property classes rejected before. */
+ OVS_NOT_REACHED();
+ }
+
+ return NULL;
+}
+
+char *
+format_ed_prop_class(const struct ofpact_ed_prop *prop)
+{
+ switch (prop->prop_class) {
+ case OFPPPC_BASIC:
+ return "basic";
+ case OFPPPC_MPLS:
+ return "mpls";
+ case OFPPPC_GRE:
+ return "gre";
+ case OFPPPC_GTP:
+ return "gtp";
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
+char *
+format_ed_prop_type(const struct ofpact_ed_prop *prop)
+{
+ switch (prop->prop_class) {
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
+void
+format_ed_prop_value(struct ds *s OVS_UNUSED,
+ const struct ofpact_ed_prop *prop)
+{
+ switch (prop->prop_class) {
+ default:
+ OVS_NOT_REACHED();
+ }
+}
@@ -1265,7 +1265,8 @@ pt_ns_type(ovs_be32 packet_type)
/* Well-known packet_type field values. */
enum packet_type {
- PT_ETH = PACKET_TYPE(OFPHTN_ONF, 0x0000), /* Default: Ethernet */
+ PT_ETH = PACKET_TYPE(OFPHTN_ONF, 0x0000), /* Default PT: Ethernet */
+ PT_USE_NEXT_PROTO = PACKET_TYPE(OFPHTN_ONF, 0xfffe), /* Pseudo PT for decap. */
PT_IPV4 = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_IP),
PT_IPV6 = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_IPV6),
PT_MPLS = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS),
@@ -234,6 +234,8 @@ struct xlate_ctx {
bool in_action_set; /* Currently translating action_set, if true. */
bool in_packet_out; /* Currently translating a packet_out msg, if
* true. */
+ bool pending_encap; /* Waiting to commit a pending encap
+ * action, if true. */
uint8_t table_id; /* OpenFlow table ID where flow was found. */
ovs_be64 rule_cookie; /* Cookie of the rule being translated. */
@@ -3234,7 +3236,8 @@ xlate_commit_actions(struct xlate_ctx *ctx)
ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
ctx->odp_actions, ctx->wc,
- use_masked);
+ use_masked, ctx->pending_encap);
+ ctx->pending_encap = false;
}
static void
@@ -5251,6 +5254,8 @@ freeze_unroll_actions(const struct ofpact *a, const struct ofpact *end,
case OFPACT_METER:
case OFPACT_SAMPLE:
case OFPACT_CLONE:
+ case OFPACT_ENCAP:
+ case OFPACT_DECAP:
case OFPACT_DEBUG_RECIRC:
case OFPACT_CT:
case OFPACT_CT_CLEAR:
@@ -5439,6 +5444,85 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
}
static void
+rewrite_flow_encap_ethernet(struct xlate_ctx *ctx,
+ struct flow *flow,
+ struct flow_wildcards *wc)
+{
+ wc->masks.packet_type = OVS_BE32_MAX;
+ if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) {
+ /* Only adjust the packet_type and zero the dummy Ethernet addresses. */
+ ovs_be16 ethertype = pt_ns_type_be(flow->packet_type);
+ flow->packet_type = htonl(PT_ETH);
+ flow->dl_src = eth_addr_zero;
+ flow->dl_dst = eth_addr_zero;
+ flow->dl_type = ethertype;
+ } else {
+ xlate_report_debug(ctx, OFT_ACTION,
+ "encap(ethernet) unsupported for packet type "
+ "ethernet");
+ /* TODO: Error handling: drop packet. */
+ ctx->error = 1;
+ }
+}
+
+static void
+xlate_generic_encap_action(struct xlate_ctx *ctx,
+ const struct ofpact_encap *encap)
+{
+ struct flow *flow = &ctx->xin->flow;
+ struct flow_wildcards *wc = ctx->wc;
+
+ /* Ensure that any pending actions on the inner packet are applied before
+ * rewriting the flow */
+ xlate_commit_actions(ctx);
+
+ /* Rewrite the flow to reflect the effect of pushing the new encap header. */
+ switch (ntohl(encap->new_pkt_type)) {
+ case PT_ETH:
+ rewrite_flow_encap_ethernet(ctx, flow, wc);
+ break;
+ default:
+ /* TODO: Error handling: Should not happen if the PT is checked
+ * at decoding */
+ break;
+ }
+
+ if (!ctx->error) {
+ /* The actual encap datapath action will be generated at next commit. */
+ ctx->pending_encap = true;
+ }
+}
+
+/* Returns true if packet must be recirculated after decapsulation. */
+static bool
+xlate_generic_decap_action(struct xlate_ctx *ctx,
+ const struct ofpact_decap *decap OVS_UNUSED)
+{
+ struct flow *flow = &ctx->xin->flow;
+
+ /* Ensure that any pending actions on the current packet are applied
+ * before generating the decap action. */
+ xlate_commit_actions(ctx);
+
+ /* We assume for now that the new_pkt_type is PT_USE_NEXT_PROTO. */
+ switch (ntohl(flow->packet_type)) {
+ case PT_ETH:
+ /* Just change the packet_type.
+ * Delay generating pop_eth to the next commit. */
+ flow->packet_type = htonl(PACKET_TYPE(OFPHTN_ETHERTYPE,
+ ntohs(flow->dl_type)));
+ return false;
+ default:
+ xlate_report_debug(ctx, OFT_ACTION,
+ "decap() for unsupported packet type %x",
+ ntohl(flow->packet_type));
+ /* TODO: Error handling: drop packet. */
+ ctx->error = 1;
+ return false;
+ }
+}
+
+static void
recirc_for_mpls(const struct ofpact *a, struct xlate_ctx *ctx)
{
/* No need to recirculate if already exiting. */
@@ -5512,6 +5596,8 @@ recirc_for_mpls(const struct ofpact *a, struct xlate_ctx *ctx)
case OFPACT_EXIT:
case OFPACT_SAMPLE:
case OFPACT_CLONE:
+ case OFPACT_ENCAP:
+ case OFPACT_DECAP:
case OFPACT_UNROLL_XLATE:
case OFPACT_CT:
case OFPACT_CT_CLEAR:
@@ -5928,6 +6014,22 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
xlate_clone(ctx, ofpact_get_CLONE(a));
break;
+ case OFPACT_ENCAP:
+ xlate_generic_encap_action(ctx, ofpact_get_ENCAP(a));
+ break;
+
+ case OFPACT_DECAP: {
+ bool recirc_needed =
+ xlate_generic_decap_action(ctx, ofpact_get_DECAP(a));
+ if (!ctx->error && recirc_needed) {
+ /* Recirculate for parsing of inner packet. */
+ ctx_trigger_freeze(ctx);
+ /* Then continue with next action. */
+ a = ofpact_next(a);
+ }
+ break;
+ }
+
case OFPACT_CT:
compose_conntrack_action(ctx, ofpact_get_CT(a));
break;
@@ -6169,7 +6271,7 @@ xlate_wc_finish(struct xlate_ctx *ctx)
* use non-header fields as part of the cache. */
flow_wildcards_clear_non_packet_fields(ctx->wc);
- /* Wildcard ethernet addresses if the original packet type was not
+ /* Wildcard ethernet fields if the original packet type was not
* Ethernet. */
if (ctx->xin->upcall_flow->packet_type != htonl(PT_ETH)) {
ctx->wc->masks.dl_dst = eth_addr_zero;
@@ -6258,6 +6360,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
.in_group = false,
.in_action_set = false,
.in_packet_out = xin->in_packet_out,
+ .pending_encap = false,
.table_id = 0,
.rule_cookie = OVS_BE64_MAX,
@@ -6417,6 +6520,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
flow->packet_type = htonl(PT_ETH);
flow->dl_src = eth_addr_zero;
flow->dl_dst = eth_addr_zero;
+ ctx.pending_encap = true;
}
if (!xin->ofpacts && !ctx.rule) {