From patchwork Thu Apr 20 03:26:41 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Yang, Yi" X-Patchwork-Id: 752615 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3w7kqY0TwZz9s4s for ; Thu, 20 Apr 2017 13:28:57 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 7BEE3B88; Thu, 20 Apr 2017 03:28:26 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id CBB8A8D7 for ; Thu, 20 Apr 2017 03:28:25 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mga03.intel.com (mga03.intel.com [134.134.136.65]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id 65106124 for ; Thu, 20 Apr 2017 03:28:24 +0000 (UTC) Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga103.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 19 Apr 2017 20:28:24 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos; i="5.37,223,1488873600"; d="scan'208"; a="1121575375" Received: from unknown (HELO localhost.localdomain.bj.intel.com) ([10.240.224.185]) by orsmga001.jf.intel.com with ESMTP; 19 Apr 2017 20:28:22 -0700 From: Yi Yang To: dev@openvswitch.org Date: Thu, 20 Apr 2017 11:26:41 +0800 Message-Id: <1492658801-121522-3-git-send-email-yi.y.yang@intel.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1492658801-121522-1-git-send-email-yi.y.yang@intel.com> References: <1492658801-121522-1-git-send-email-yi.y.yang@intel.com> X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [RFC PATCH 2/2] Add encap_nsh & decap_nsh actions support in datapath X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org Signed-off-by: Yi Yang --- datapath/linux/compat/include/linux/openvswitch.h | 23 ++++ lib/automake.mk | 2 +- lib/dpif-netdev.c | 2 + lib/dpif.c | 2 + lib/odp-execute.c | 18 ++- lib/odp-util.c | 154 ++++++++++++++++++++++ lib/packets.c | 48 +++++++ lib/packets.h | 3 + ofproto/ofproto-dpif-sflow.c | 2 + ofproto/ofproto-dpif-xlate.c | 97 +++++++++++++- 10 files changed, 344 insertions(+), 7 deletions(-) diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index 5a64b38..03b10ff 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -773,6 +773,25 @@ struct ovs_action_endecap { uint8_t props[OVS_ENDECAP_MAX_PROPS_LEN]; }; +#define OVS_ENCAP_NSH_MAX_MD_LEN 256 +/* + * struct ovs_action_encap_nsh - %OVS_ACTION_ATTR_ENCAP_NSH + * @flags: NSH header flags. + * @length: NSH header length/4. + * @md_type: NSH Metadata type. + * @next_proto: Inner packet type. + * @path_hdr: NSH service path and service index. + * @metadata: NSH metadata for MD type 1 or 2 + */ +struct ovs_action_encap_nsh { + uint8_t flags; + uint8_t length; + uint8_t md_type; + uint8_t next_proto; + ovs_be32 path_hdr; + uint8_t metadata[OVS_ENCAP_NSH_MAX_MD_LEN]; +}; + /** * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT. * @@ -852,6 +871,8 @@ enum ovs_nat_attr { * as NSH header, Ethernet header, etc. * @OVS_ACTION_ATTR_DECAP: Generic decap action to remove generic header, such * as NSH header, Ethernet header, etc. + * @OVS_ACTION_ATTR_ENCAP_NSH: encap NSH action to push NSH header. + * @OVS_ACTION_ATTR_DECAP_NSH: decap NSH action to remove NSH header. * * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all * fields within a header are modifiable, e.g. the IPv4 protocol and fragment @@ -889,6 +910,8 @@ enum ovs_action_attr { OVS_ACTION_ATTR_POP_ETH, /* No argument. */ OVS_ACTION_ATTR_ENCAP, /* struct ovs_action_endecap. */ OVS_ACTION_ATTR_DECAP, /* struct ovs_action_endecap. */ + OVS_ACTION_ATTR_ENCAP_NSH, /* struct ovs_action_encap_nsh. */ + OVS_ACTION_ATTR_DECAP_NSH, /* No argument. */ #ifndef __KERNEL__ OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/ diff --git a/lib/automake.mk b/lib/automake.mk index 39977a6..26a51b6 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -149,7 +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-ed-props.c \ lib/ofp-errors.c \ lib/ofp-msgs.c \ lib/ofp-parse.c \ diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index f5fd00c..e3b6862 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -5177,6 +5177,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_, case OVS_ACTION_ATTR_CLONE: case OVS_ACTION_ATTR_ENCAP: case OVS_ACTION_ATTR_DECAP: + case OVS_ACTION_ATTR_ENCAP_NSH: + case OVS_ACTION_ATTR_DECAP_NSH: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } diff --git a/lib/dpif.c b/lib/dpif.c index fa0f617..3febf29 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1226,6 +1226,8 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_, case OVS_ACTION_ATTR_CLONE: case OVS_ACTION_ATTR_ENCAP: case OVS_ACTION_ATTR_DECAP: + case OVS_ACTION_ATTR_ENCAP_NSH: + case OVS_ACTION_ATTR_DECAP_NSH: case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); diff --git a/lib/odp-execute.c b/lib/odp-execute.c index 4df1f0c..af6407d 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -617,6 +617,8 @@ requires_datapath_assistance(const struct nlattr *a) case OVS_ACTION_ATTR_CLONE: case OVS_ACTION_ATTR_ENCAP: case OVS_ACTION_ATTR_DECAP: + case OVS_ACTION_ATTR_ENCAP_NSH: + case OVS_ACTION_ATTR_DECAP_NSH: return false; case OVS_ACTION_ATTR_UNSPEC: @@ -774,11 +776,17 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal, } break; - case OVS_ACTION_ATTR_ENCAP: - /* TODO */ + case OVS_ACTION_ATTR_ENCAP_NSH: { + const struct ovs_action_encap_nsh *oaen = nl_attr_get(a); + DP_PACKET_BATCH_FOR_EACH (packet, batch) { + encap_nsh(packet, oaen); + } break; - case OVS_ACTION_ATTR_DECAP: - /* TODO */ + } + case OVS_ACTION_ATTR_DECAP_NSH: + DP_PACKET_BATCH_FOR_EACH (packet, batch) { + decap_nsh(packet); + } break; case OVS_ACTION_ATTR_OUTPUT: @@ -787,6 +795,8 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal, case OVS_ACTION_ATTR_USERSPACE: case OVS_ACTION_ATTR_RECIRC: case OVS_ACTION_ATTR_CT: + case OVS_ACTION_ATTR_ENCAP: + case OVS_ACTION_ATTR_DECAP: case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); diff --git a/lib/odp-util.c b/lib/odp-util.c index 6da7d78..7389e85 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -130,6 +130,8 @@ odp_action_len(uint16_t type) case OVS_ACTION_ATTR_CLONE: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_ENCAP: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_DECAP: return ATTR_LEN_VARIABLE; + case OVS_ACTION_ATTR_ENCAP_NSH: return ATTR_LEN_VARIABLE; + case OVS_ACTION_ATTR_DECAP_NSH: return 0; case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: @@ -247,6 +249,46 @@ format_odp_clone_action(struct ds *ds, const struct nlattr *attr) ds_put_format(ds, ")"); } +static void +format_nsh_header(struct ds *ds, const struct nsh_hdr * nsh_header) +{ + uint8_t flags = (nsh_header->base.version << 6) & + (nsh_header->base.oam_flag << 5) & + (nsh_header->base.context_flag << 4) & + nsh_header->base.reserved_flags1; + ds_put_format(ds, "flags=%d", flags); + ds_put_format(ds, ",mdtype=%d", nsh_header->base.md_type); + ds_put_format(ds, ",np=%d", nsh_header->base.next_proto); + ds_put_format(ds, ",spi=%d", ntohl(nsh_header->base.path_hdr << 8)); + ds_put_format(ds, ",si=%d", nsh_header->base.svc_idx); + + if (nsh_header->base.md_type == NSH_M_TYPE1) { + const struct nsh_md1_ctx *md1_ctx = nsh_md1_ctx(nsh_header); + ds_put_format(ds, ",c1=0x%08x", ntohl(md1_ctx->c1)); + ds_put_format(ds, ",c2=0x%08x", ntohl(md1_ctx->c2)); + ds_put_format(ds, ",c3=0x%08x", ntohl(md1_ctx->c3)); + ds_put_format(ds, ",c4=0x%08x", ntohl(md1_ctx->c4)); + } else if (nsh_header->base.md_type == NSH_M_TYPE2) { + /* TODO */ + } +} + +static void +format_odp_encap_nsh_action(struct ds *ds, const struct nlattr *attr) +{ + if (nl_attr_type(attr) != OVS_ACTION_ATTR_ENCAP_NSH) { + return; + } + ds_put_cstr(ds, "encap_nsh("); + + size_t len = nl_attr_get_size(attr); + if (len) { + const struct nsh_hdr * nsh_header = (struct nsh_hdr *)nl_attr_get(attr); + format_nsh_header(ds, nsh_header); + } + ds_put_format(ds, ")"); +} + static const char * slow_path_reason_to_string(uint32_t reason) { @@ -919,6 +961,12 @@ format_odp_action(struct ds *ds, const struct nlattr *a) case OVS_ACTION_ATTR_DECAP: /* TODO */ break; + case OVS_ACTION_ATTR_ENCAP_NSH: + format_odp_encap_nsh_action(ds, a); + break; + case OVS_ACTION_ATTR_DECAP_NSH: + ds_put_cstr(ds, "decap_nsh()"); + break; case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: default: @@ -1637,6 +1685,94 @@ find_end: } static int +parse_odp_encap_nsh_action(const char *s, struct ofpbuf *actions) +{ + int n = 0; + int ret = 0; + struct ovs_action_encap_nsh encap_nsh; + struct nsh_hdr * nsh_header = (struct nsh_hdr *)&encap_nsh; + uint32_t spi; + uint8_t si; + + if (!ovs_scan_len(s, &n, "encap_nsh(")) { + ret = -EINVAL; + goto out; + } + + /* The default is NSH_M_TYPE1 */ + nsh_header->base.version = 0; + nsh_header->base.oam_flag = 0; + nsh_header->base.context_flag = 0; //md_type=NSH_M_TYPE1 + nsh_header->base.length = 6; //md_type=NSH_M_TYPE1 + + nsh_header->base.md_type = NSH_M_TYPE1; + nsh_header->base.next_proto = NSH_P_ETHERNET; + nsh_header->base.path_hdr = htonl(255); + + struct nsh_md1_ctx * md1_ctx = nsh_md1_ctx(nsh_header); + md1_ctx->c1 = 0; + md1_ctx->c2 = 0; + md1_ctx->c3 = 0; + md1_ctx->c4 = 0; + + for (;;) { + n += strspn(s + n, delimiters); + if (s[n] == ')') { + break; + } + + if (ovs_scan_len(s, &n, "flags=%"SCNi8, &encap_nsh.flags)) { + continue; + } + if (ovs_scan_len(s, &n, "mdtype=%"SCNi8, &encap_nsh.md_type)) { + continue; + } + if (ovs_scan_len(s, &n, "np=%"SCNi8, &encap_nsh.next_proto)) { + continue; + } + if (ovs_scan_len(s, &n, "spi=%"SCNi32, &spi)) { + spi = htonl(spi); + encap_nsh.path_hdr = encap_nsh.path_hdr & ((spi >> 8) | 0xFF000000); + continue; + } + if (ovs_scan_len(s, &n, "si=%"SCNi8, &si)) { + encap_nsh.path_hdr = encap_nsh.path_hdr & ((si << 24) | 0x00FFFFFF); + continue; + } + if (encap_nsh.md_type == NSH_M_TYPE1) { + if (ovs_scan_len(s, &n, "c1=0x%"SCNx32, &md1_ctx->c1)) { + md1_ctx->c1 = htonl(md1_ctx->c1); + continue; + } + if (ovs_scan_len(s, &n, "c2=0x%"SCNx32, &md1_ctx->c2)) { + md1_ctx->c2 = htonl(md1_ctx->c2); + continue; + } + if (ovs_scan_len(s, &n, "c3=0x%"SCNx32, &md1_ctx->c3)) { + md1_ctx->c3 = htonl(md1_ctx->c3); + continue; + } + if (ovs_scan_len(s, &n, "c4=0x%"SCNx32, &md1_ctx->c4)) { + md1_ctx->c4 = htonl(md1_ctx->c4); + continue; + } + } + else if (encap_nsh.md_type == NSH_M_TYPE2) { + /* TODO */ + continue; + } + } +out: + if (ret < 0) { + return ret; + } else { + size_t size = nsh_header->base.length << 2; + nl_msg_put_unspec(actions, OVS_ACTION_ATTR_ENCAP_NSH, &encap_nsh, size); + return n; + } +} + +static int parse_action_list(const char *s, const struct simap *port_names, struct ofpbuf *actions) { @@ -1838,6 +1974,24 @@ parse_odp_action(const char *s, const struct simap *port_names, } { + if (!strncmp(s, "encap_nsh(", 10)) { + int retval = parse_odp_encap_nsh_action(s, actions); + if (retval < 0) { + return retval; + } + return retval + 1; + } + } + + { + int n; + if (ovs_scan(s, "decap_nsh()%n", &n)) { + nl_msg_put_flag(actions, OVS_ACTION_ATTR_DECAP_NSH); + return n; + } + } + + { uint32_t port; int n; diff --git a/lib/packets.c b/lib/packets.c index 9467768..c235b26 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -226,6 +226,54 @@ eth_pop_vlan(struct dp_packet *packet) } } +void +encap_nsh(struct dp_packet *packet, const struct ovs_action_encap_nsh * oaen) { + struct nsh_hdr * nsh_header = (struct nsh_hdr *)oaen; + uint16_t length = nsh_header->base.length << 2; + uint8_t next_proto = NSH_P_ETHERNET; + + switch(packet->packet_type) { + case PT_ETH: + next_proto = NSH_P_ETHERNET; + break; + case PT_IPV4: + next_proto = NSH_P_IPV4; + break; + case PT_IPV6: + next_proto = NSH_P_IPV6; + break; + } + + nsh_header = (struct nsh_hdr *)dp_packet_push_uninit(packet, length); + memcpy(nsh_header, oaen, length); + nsh_header->base.next_proto = next_proto; + packet->packet_type = htonl(PT_NSH); +} + +void +decap_nsh(struct dp_packet *packet) { + if ((packet->l3_ofs == 0) && (packet->packet_type == htonl(PT_NSH))) { + struct nsh_hdr * nsh_header = (struct nsh_hdr *)dp_packet_l3(packet); + uint8_t next_proto = nsh_header->base.next_proto; + enum packet_type next_pt = PT_ETH; + switch(next_proto) { + case NSH_P_ETHERNET: + next_pt = PT_ETH; + break; + case NSH_P_IPV4: + next_pt = PT_IPV4; + break; + case NSH_P_IPV6: + next_pt = PT_IPV6; + break; + } + + size_t length = nsh_header->base.length << 2; + dp_packet_reset_packet(packet, length); + packet->packet_type = htonl(next_pt); + } +} + /* Push Ethernet header onto 'packet' assuming it is layer 3 */ void push_eth(struct dp_packet *packet, const struct eth_addr *dst, diff --git a/lib/packets.h b/lib/packets.h index 78ea838..8dbf399 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -404,6 +404,9 @@ struct eth_header { }); BUILD_ASSERT_DECL(ETH_HEADER_LEN == sizeof(struct eth_header)); +void encap_nsh(struct dp_packet *packet, const struct ovs_action_encap_nsh * encap_nsh); +void decap_nsh(struct dp_packet *packet); + void push_eth(struct dp_packet *packet, const struct eth_addr *dst, const struct eth_addr *src, ovs_be16 type); void pop_eth(struct dp_packet *packet); diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c index 0d4e8f1..a59727c 100644 --- a/ofproto/ofproto-dpif-sflow.c +++ b/ofproto/ofproto-dpif-sflow.c @@ -1201,6 +1201,8 @@ dpif_sflow_read_actions(const struct flow *flow, case OVS_ACTION_ATTR_CLONE: case OVS_ACTION_ATTR_ENCAP: case OVS_ACTION_ATTR_DECAP: + case OVS_ACTION_ATTR_ENCAP_NSH: + case OVS_ACTION_ATTR_DECAP_NSH: case OVS_ACTION_ATTR_UNSPEC: case __OVS_ACTION_ATTR_MAX: default: diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index a74e1ac..fd717b0 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -4161,6 +4161,8 @@ xlate_fixup_actions(struct ofpbuf *b, const struct nlattr *actions, case OVS_ACTION_ATTR_POP_ETH: case OVS_ACTION_ATTR_ENCAP: case OVS_ACTION_ATTR_DECAP: + case OVS_ACTION_ATTR_ENCAP_NSH: + case OVS_ACTION_ATTR_DECAP_NSH: case OVS_ACTION_ATTR_METER: ofpbuf_put(b, a, nl_attr_len_pad(a, left)); break; @@ -5453,6 +5455,91 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc) } static void +compose_encap_nsh_action(const struct ofpact_encap *ofe, struct ovs_action_encap_nsh * encap_nsh) +{ + char * ptr = (char *)ofe->props; + size_t ofs = 0; + struct nsh_hdr * nsh_header = (struct nsh_hdr *)encap_nsh; + + /* The default is NSH_M_TYPE1 */ + nsh_header->base.version = 0; + nsh_header->base.oam_flag = 0; + nsh_header->base.context_flag = 0; //md_type=NSH_M_TYPE1 + nsh_header->base.length = 6; //md_type=NSH_M_TYPE1 + + nsh_header->base.md_type = NSH_M_TYPE1; + nsh_header->base.next_proto = NSH_P_ETHERNET; + nsh_header->base.path_hdr = htonl(255); + + struct nsh_md1_ctx * md1_ctx = nsh_md1_ctx(nsh_header); + md1_ctx->c1 = 0; + md1_ctx->c2 = 0; + md1_ctx->c3 = 0; + md1_ctx->c4 = 0; + + if (ofe->props_len == 0) { + return; + } + + struct ofp_ed_prop_header * prop_ptr = (struct ofp_ed_prop_header *)ptr; + if ((prop_ptr->prop_class == OFPPPC_NSH) && (prop_ptr->type == OFPPPT_PROP_NSH_MDTYPE)) { + struct ofp_ed_prop_nsh_md_type * md_type_prop = (struct ofp_ed_prop_nsh_md_type *)prop_ptr; + nsh_header->base.md_type = md_type_prop->md_type; + switch(nsh_header->base.md_type) { + case NSH_M_TYPE1: + break; + case NSH_M_TYPE2: + nsh_header->base.context_flag = 1; //md_type=NSH_M_TYPE2 + nsh_header->base.length = 2; //md_type=NSH_M_TYPE2 + break; + } + ptr += sizeof(struct ofp_ed_prop_nsh_md_type); + ofs += sizeof(struct ofp_ed_prop_nsh_md_type); + + + /* Get TLVs */ + struct nsh_md2_ctx * md2_ctx = nsh_md2_ctx(nsh_header); + while (ofs + sizeof(struct ofp_ed_prop_nsh_tlv) < ofe->props_len) { + struct ofp_ed_prop_nsh_tlv * tlv_prop = (struct ofp_ed_prop_nsh_tlv *)ptr; + md2_ctx->md_class = htons(tlv_prop->tlv_class); + md2_ctx->type = tlv_prop->tlv_type; + md2_ctx->length = tlv_prop->tlv_len; + memcpy(md2_ctx->md_value, tlv_prop->data, md2_ctx->length); + nsh_header->base.length += 1 + md2_ctx->length/4; + ptr += sizeof(struct ofp_ed_prop_nsh_tlv) + tlv_prop->tlv_len; + ofs += sizeof(struct ofp_ed_prop_nsh_tlv) + tlv_prop->tlv_len; + md2_ctx = (struct nsh_md2_ctx *)((char *)md2_ctx + 4 + md2_ctx->length); + } + } /* nsh encap */ +} + +static void +compose_generic_encap_action(struct xlate_ctx *ctx, struct ofpact_encap *ofe) +{ + /* Ensure that any prior actions are applied before composing generic + * encap action. */ + xlate_commit_actions(ctx); + + /* encap_nsh */ + if (PACKET_TYPE(ofe->new_pkt_type.ns, ofe->new_pkt_type.type) == PT_NSH) { + struct ovs_action_encap_nsh encap_nsh; + struct nsh_hdr * nsh_header = (struct nsh_hdr *)&encap_nsh; + + compose_encap_nsh_action(ofe, &encap_nsh); + nl_msg_put_unspec(ctx->odp_actions, OVS_ACTION_ATTR_ENCAP_NSH, &encap_nsh, nsh_header->base.length << 2); + } +} + +static void +compose_generic_decap_action(struct xlate_ctx *ctx, struct ofpact_decap *ofd) +{ + /* decap_nsh */ + if (PACKET_TYPE(ofd->new_pkt_type.ns, ofd->new_pkt_type.type) == PT_NSH) { + nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_DECAP_NSH); + } +} + +static void recirc_for_mpls(const struct ofpact *a, struct xlate_ctx *ctx) { /* No need to recirculate if already exiting. */ @@ -5944,11 +6031,17 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; case OFPACT_ENCAP: - /* TODO */ + compose_generic_encap_action(ctx, ofpact_get_ENCAP(a)); + /* Recirculate for parse the encaped header */ + ctx_trigger_freeze(ctx); + a = ofpact_next(a); break; case OFPACT_DECAP: - /* TODO */ + compose_generic_decap_action(ctx, ofpact_get_DECAP(a)); + /* Recirculate for parse inner header */ + ctx_trigger_freeze(ctx); + a = ofpact_next(a); break; case OFPACT_CT: