From: Martin Varghese <martin.vargh...@nokia.com> The encap & decap actions are extended to support MPLS packet type. Encap & decap actions adds and removes MPLS header at start of the packet.
Signed-off-by: Martin Varghese <martin.vargh...@nokia.com> --- NEWS | 2 +- .../linux/compat/include/linux/openvswitch.h | 35 ++++++- include/openvswitch/ofp-ed-props.h | 18 ++++ lib/dpif-netdev.c | 1 + lib/dpif.c | 1 + lib/odp-execute.c | 12 +++ lib/odp-util.c | 58 +++++++++--- lib/ofp-actions.c | 5 + lib/ofp-ed-props.c | 91 +++++++++++++++++++ lib/ovs-actions.xml | 31 +++++-- lib/packets.c | 36 ++++++++ lib/packets.h | 2 + ofproto/ofproto-dpif-ipfix.c | 1 + ofproto/ofproto-dpif-sflow.c | 1 + ofproto/ofproto-dpif-xlate.c | 60 ++++++++++++ ofproto/ofproto-dpif.c | 39 ++++++++ ofproto/ofproto-dpif.h | 5 +- tests/system-traffic.at | 36 ++++++++ 18 files changed, 410 insertions(+), 24 deletions(-) diff --git a/NEWS b/NEWS index 95cf922aa..4bf4e9e7b 100644 --- a/NEWS +++ b/NEWS @@ -120,7 +120,7 @@ v2.14.0 - 17 Aug 2020 - GTP-U Tunnel Protocol * Add two new fields: tun_gtpu_flags, tun_gtpu_msgtype. * Only support for userspace datapath. - + - Encap & Decap action support for MPLS packet type. v2.13.0 - 14 Feb 2020 --------------------- diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index 875de2025..8feea7dd4 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -810,8 +810,32 @@ struct ovs_action_push_tnl { }; #endif -/** - * enum ovs_ct_attr - Attributes for %OVS_ACTION_ATTR_CT action. +/* struct ovs_action_add_mpls - %OVS_ACTION_ATTR_ADD_MPLS action + * argument. + * @mpls_lse: MPLS label stack entry to push. + * @mpls_ethertype: Ethertype to set in the encapsulating ethernet frame. + * @tun_flags: MPLS tunnel attributes. + * + * The only values @mpls_ethertype should ever be given are %ETH_P_MPLS_UC and + * %ETH_P_MPLS_MC, indicating MPLS unicast or multicast. Other are rejected. + */ +struct ovs_action_add_mpls { + __be32 mpls_lse; + __be16 mpls_ethertype; /* Either %ETH_P_MPLS_UC or %ETH_P_MPLS_MC */ + __u16 tun_flags; +}; + +#define OVS_MPLS_L3_TUNNEL_FLAG_MASK (1 << 0) /* Flag to specify the place of + * insertion of MPLS header. + * When false, the MPLS header + * will be inserted at the start + * of the packet. + * When true, the MPLS header + * will be inserted at the start + * of the l3 header. + */ + +/* enum ovs_ct_attr - Attributes for %OVS_ACTION_ATTR_CT action. * @OVS_CT_ATTR_COMMIT: If present, commits the connection to the conntrack * table. This allows future packets for the same connection to be identified * as 'established' or 'related'. The flow key for the current packet will @@ -1001,7 +1025,11 @@ struct check_pkt_len_arg { * @OVS_ACTION_ATTR_CHECK_PKT_LEN: Check the packet length and execute a set * of actions if greater than the specified packet length, else execute * another set of actions. - * @OVS_ACTION_ATTR_DROP: Explicit drop action. + * @OVS_ACTION_ATTR_ADD_MPLS: Push a new MPLS label stack entry at the + * start of the packet or at the start of the l3 header depending on the value + * of l3 tunnel flag in the tun_flags field of OVS_ACTION_ATTR_ADD_MPLS + * argument. + * @OVS_ACTION_ATTR_DROP: Explicit drop action. */ enum ovs_action_attr { @@ -1030,6 +1058,7 @@ enum ovs_action_attr { OVS_ACTION_ATTR_METER, /* u32 meter number. */ OVS_ACTION_ATTR_CLONE, /* Nested OVS_CLONE_ATTR_*. */ OVS_ACTION_ATTR_CHECK_PKT_LEN, /* Nested OVS_CHECK_PKT_LEN_ATTR_*. */ + OVS_ACTION_ATTR_ADD_MPLS, /* struct ovs_action_add_mpls. */ #ifndef __KERNEL__ OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/ diff --git a/include/openvswitch/ofp-ed-props.h b/include/openvswitch/ofp-ed-props.h index 306c6fe73..c85f3c283 100644 --- a/include/openvswitch/ofp-ed-props.h +++ b/include/openvswitch/ofp-ed-props.h @@ -46,6 +46,11 @@ enum ofp_ed_nsh_prop_type { OFPPPT_PROP_NSH_TLV = 2, /* property TLV in NSH */ }; +enum ofp_ed_mpls_prop_type { + OFPPPT_PROP_MPLS_NONE = 0, /* unused */ + OFPPPT_PROP_MPLS_ETHERTYPE = 1, /* MPLS Ethertype */ +}; + /* * External representation of encap/decap properties. * These must be padded to a multiple of 8 bytes. @@ -72,6 +77,13 @@ struct ofp_ed_prop_nsh_tlv { uint8_t data[0]; }; +struct ofp_ed_prop_mpls_ethertype { + struct ofp_ed_prop_header header; + uint16_t ether_type; /* MPLS ethertype .*/ + uint8_t pad[2]; /* Padding to 8 bytes. */ +}; + + /* * Internal representation of encap/decap properties */ @@ -96,6 +108,12 @@ struct ofpact_ed_prop_nsh_tlv { /* tlv_len octets of metadata value, padded to a multiple of 8 bytes. */ uint8_t data[0]; }; + +struct ofpact_ed_prop_mpls_ethertype { + struct ofpact_ed_prop header; + uint16_t ether_type; /* MPLS ethertype .*/ + uint8_t pad[2]; /* Padding to 8 bytes. */ +}; 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, diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 94cc9b80c..bbdea5603 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -8044,6 +8044,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_, case OVS_ACTION_ATTR_CT_CLEAR: case OVS_ACTION_ATTR_CHECK_PKT_LEN: case OVS_ACTION_ATTR_DROP: + case OVS_ACTION_ATTR_ADD_MPLS: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } diff --git a/lib/dpif.c b/lib/dpif.c index 56d0b4a65..bbd1296e3 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -1273,6 +1273,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_, case OVS_ACTION_ATTR_UNSPEC: case OVS_ACTION_ATTR_CHECK_PKT_LEN: case OVS_ACTION_ATTR_DROP: + case OVS_ACTION_ATTR_ADD_MPLS: case __OVS_ACTION_ATTR_MAX: OVS_NOT_REACHED(); } diff --git a/lib/odp-execute.c b/lib/odp-execute.c index 6eeda2a61..2f4cdd92c 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -819,6 +819,7 @@ requires_datapath_assistance(const struct nlattr *a) case OVS_ACTION_ATTR_POP_NSH: case OVS_ACTION_ATTR_CT_CLEAR: case OVS_ACTION_ATTR_CHECK_PKT_LEN: + case OVS_ACTION_ATTR_ADD_MPLS: case OVS_ACTION_ATTR_DROP: return false; @@ -1061,6 +1062,17 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal, } break; + case OVS_ACTION_ATTR_ADD_MPLS: { + const struct ovs_action_add_mpls *mpls = nl_attr_get(a); + bool l3_flag = mpls->tun_flags & OVS_MPLS_L3_TUNNEL_FLAG_MASK; + + DP_PACKET_BATCH_FOR_EACH (i, packet, batch) { + add_mpls(packet, mpls->mpls_ethertype, mpls->mpls_lse, + l3_flag); + } + break; + } + case OVS_ACTION_ATTR_DROP:{ const enum xlate_error *drop_reason = nl_attr_get(a); diff --git a/lib/odp-util.c b/lib/odp-util.c index a8598d52a..f24e16d08 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -142,6 +142,8 @@ odp_action_len(uint16_t type) case OVS_ACTION_ATTR_PUSH_NSH: return ATTR_LEN_VARIABLE; case OVS_ACTION_ATTR_POP_NSH: return 0; case OVS_ACTION_ATTR_CHECK_PKT_LEN: return ATTR_LEN_VARIABLE; + case OVS_ACTION_ATTR_ADD_MPLS: + return sizeof(struct ovs_action_add_mpls); case OVS_ACTION_ATTR_DROP: return sizeof(uint32_t); case OVS_ACTION_ATTR_UNSPEC: @@ -1254,6 +1256,14 @@ format_odp_action(struct ds *ds, const struct nlattr *a, case OVS_ACTION_ATTR_CHECK_PKT_LEN: format_odp_check_pkt_len_action(ds, a, portno_names); break; + case OVS_ACTION_ATTR_ADD_MPLS: { + const struct ovs_action_push_mpls *mpls = nl_attr_get(a); + ds_put_cstr(ds, "add_mpls("); + format_mpls_lse(ds, mpls->mpls_lse); + ds_put_format(ds, ",eth_type=0x%"PRIx16")", + ntohs(mpls->mpls_ethertype)); + break; + } case OVS_ACTION_ATTR_DROP: ds_put_cstr(ds, "drop"); break; @@ -7876,7 +7886,8 @@ commit_vlan_action(const struct flow* flow, struct flow *base, /* Wildcarding already done at action translation time. */ static void commit_mpls_action(const struct flow *flow, struct flow *base, - struct ofpbuf *odp_actions) + struct ofpbuf *odp_actions, bool pending_encap, + bool pending_decap) { int base_n = flow_count_mpls_labels(base, NULL); int flow_n = flow_count_mpls_labels(flow, NULL); @@ -7913,7 +7924,11 @@ commit_mpls_action(const struct flow *flow, struct flow *base, if ((!eth_type_mpls(flow->dl_type)) && base_n > 1) { dl_type = htons(ETH_TYPE_MPLS); } else { - dl_type = flow->dl_type; + if ((flow->packet_type == PT_ETH) && pending_decap) { + dl_type = htons(ETH_TYPE_TEB); + } else { + dl_type = flow->dl_type; + } } nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, dl_type); ovs_assert(flow_pop_mpls(base, base_n, flow->dl_type, NULL)); @@ -7924,18 +7939,29 @@ commit_mpls_action(const struct flow *flow, struct flow *base, /* If, after the above popping and setting, there are more LSEs in flow * than base then some LSEs need to be pushed. */ while (base_n < flow_n) { - struct ovs_action_push_mpls *mpls; - mpls = nl_msg_put_unspec_zero(odp_actions, - OVS_ACTION_ATTR_PUSH_MPLS, - sizeof *mpls); - mpls->mpls_ethertype = flow->dl_type; - mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1]; + if (pending_encap) { + struct ovs_action_add_mpls *mpls; + + mpls = nl_msg_put_unspec_zero(odp_actions, + OVS_ACTION_ATTR_ADD_MPLS, + sizeof *mpls); + mpls->mpls_ethertype = flow->dl_type; + mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1]; + } else { + struct ovs_action_push_mpls *mpls; + + mpls = nl_msg_put_unspec_zero(odp_actions, + OVS_ACTION_ATTR_PUSH_MPLS, + sizeof *mpls); + mpls->mpls_ethertype = flow->dl_type; + mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1]; + } /* Update base flow's MPLS stack, but do not clear L3. We need the L3 * headers if the flow is restored later due to returning from a patch * port or group bucket. */ - flow_push_mpls(base, base_n, mpls->mpls_ethertype, NULL, false); - flow_set_mpls_lse(base, 0, mpls->mpls_lse); + flow_push_mpls(base, base_n, flow->dl_type, NULL, false); + flow_set_mpls_lse(base, 0, flow->mpls_lse[flow_n - base_n - 1]); base_n++; } } @@ -8586,6 +8612,10 @@ commit_encap_decap_action(const struct flow *flow, memcpy(&base_flow->dl_dst, &flow->dl_dst, sizeof(*flow) - offsetof(struct flow, dl_dst)); break; + case PT_MPLS: + commit_mpls_action(flow, base_flow, odp_actions, pending_encap, + pending_decap); + break; default: /* Only the above protocols are supported for encap. * The check is done at action translation. */ @@ -8608,6 +8638,10 @@ commit_encap_decap_action(const struct flow *flow, /* pop_nsh. */ odp_put_pop_nsh_action(odp_actions); break; + case PT_MPLS: + commit_mpls_action(flow, base_flow, odp_actions, pending_encap, + pending_decap); + break; default: /* Checks are done during translation. */ OVS_NOT_REACHED(); @@ -8653,7 +8687,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base, /* 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)) { - commit_mpls_action(flow, base, odp_actions); + commit_mpls_action(flow, base, odp_actions, false, false); mpls_done = true; } commit_set_nsh_action(flow, base, odp_actions, wc, use_masked); @@ -8661,7 +8695,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base, commit_set_port_action(flow, base, odp_actions, wc, use_masked); slow2 = commit_set_icmp_action(flow, base, odp_actions, wc); if (!mpls_done) { - commit_mpls_action(flow, base, odp_actions); + commit_mpls_action(flow, base, odp_actions, false, false); } commit_vlan_action(flow, base, odp_actions, wc); commit_set_priority_action(flow, base, odp_actions, wc, use_masked); diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 0342a228b..28a12a569 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -4441,6 +4441,7 @@ decode_NXAST_RAW_ENCAP(const struct nx_action_encap *nae, switch (ntohl(nae->new_pkt_type)) { case PT_ETH: case PT_NSH: + case PT_MPLS: /* Add supported encap header types here. */ break; default: @@ -4492,6 +4493,8 @@ parse_encap_header(const char *hdr, ovs_be32 *packet_type) *packet_type = htonl(PT_ETH); } else if (strcmp(hdr, "nsh") == 0) { *packet_type = htonl(PT_NSH); + } else if (strcmp(hdr, "mpls") == 0) { + *packet_type = htonl(PT_MPLS); } else { return false; } @@ -4573,6 +4576,8 @@ format_encap_pkt_type(const ovs_be32 pkt_type) return "ethernet"; case PT_NSH: return "nsh"; + case PT_MPLS: + return "mpls"; default: return "UNKNOWN"; } diff --git a/lib/ofp-ed-props.c b/lib/ofp-ed-props.c index 02a9235d5..fc261e4c6 100644 --- a/lib/ofp-ed-props.c +++ b/lib/ofp-ed-props.c @@ -79,6 +79,27 @@ decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop, } break; } + case OFPPPC_MPLS: { + switch (prop_type) { + case OFPPPT_PROP_MPLS_ETHERTYPE: { + struct ofp_ed_prop_mpls_ethertype *opnmt = + ALIGNED_CAST(struct ofp_ed_prop_mpls_ethertype *, *ofp_prop); + if (len > sizeof(*opnmt) || len > *remaining) { + return OFPERR_NXBAC_BAD_ED_PROP; + } + struct ofpact_ed_prop_mpls_ethertype *pnmt = + ofpbuf_put_uninit(out, sizeof(*pnmt)); + pnmt->header.prop_class = prop_class; + pnmt->header.type = prop_type; + pnmt->header.len = len; + pnmt->ether_type = opnmt->ether_type; + break; + } + default: + return OFPERR_NXBAC_UNKNOWN_ED_PROP; + } + break; + } default: return OFPERR_NXBAC_UNKNOWN_ED_PROP; } @@ -134,6 +155,27 @@ encode_ed_prop(const struct ofpact_ed_prop **prop, } break; } + case OFPPPC_MPLS: { + switch ((*prop)->type) { + case OFPPPT_PROP_MPLS_ETHERTYPE: { + struct ofpact_ed_prop_mpls_ethertype *pnmt = + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, *prop); + struct ofp_ed_prop_mpls_ethertype *opnmt = + ofpbuf_put_uninit(out, sizeof(*opnmt)); + opnmt->header.prop_class = htons((*prop)->prop_class); + opnmt->header.type = (*prop)->type; + opnmt->header.len = + offsetof(struct ofpact_ed_prop_mpls_ethertype, pad); + opnmt->ether_type = pnmt->ether_type; + prop_len = sizeof(*pnmt); + break; + + } + default: + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + break; + } default: return OFPERR_OFPBAC_BAD_ARGUMENT; } @@ -181,6 +223,13 @@ parse_ed_prop_type(uint16_t prop_class, } else { return false; } + case OFPPPC_MPLS: + if (!strcmp(str, "ether_type")) { + *type = OFPPPT_PROP_MPLS_ETHERTYPE; + return true; + } else { + return false; + } default: return false; } @@ -259,6 +308,28 @@ parse_ed_prop_value(uint16_t prop_class, uint8_t prop_type OVS_UNUSED, OVS_NOT_REACHED(); } break; + case OFPPPC_MPLS: + switch (prop_type) { + case OFPPPT_PROP_MPLS_ETHERTYPE: { + uint16_t ethertype; + error = str_to_u16(value, "ether_type", ðertype); + if (error != NULL) { + return error; + } + struct ofpact_ed_prop_mpls_ethertype *pnmt = + ofpbuf_put_uninit(out, sizeof(*pnmt)); + pnmt->header.prop_class = prop_class; + pnmt->header.type = prop_type; + pnmt->header.len = + offsetof(struct ofpact_ed_prop_mpls_ethertype, pad); + pnmt->ether_type = ethertype; + + break; + } + default: + break; + } + break; default: /* Unsupported property classes rejected before. */ OVS_NOT_REACHED(); @@ -300,6 +371,14 @@ format_ed_prop_type(const struct ofpact_ed_prop *prop) OVS_NOT_REACHED(); } break; + case OFPPPC_MPLS: + switch (prop->type) { + case OFPPPT_PROP_MPLS_ETHERTYPE: + return "ether_type"; + default: + OVS_NOT_REACHED(); + } + break; default: OVS_NOT_REACHED(); } @@ -332,6 +411,18 @@ format_ed_prop(struct ds *s OVS_UNUSED, default: OVS_NOT_REACHED(); } + case OFPPPC_MPLS: + switch (prop->type) { + case OFPPPT_PROP_MPLS_ETHERTYPE: { + struct ofpact_ed_prop_mpls_ethertype *pnmt = + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, prop); + ds_put_format(s, "%s=%d", format_ed_prop_type(prop), + pnmt->ether_type); + return; + } + default: + OVS_NOT_REACHED(); + } default: OVS_NOT_REACHED(); } diff --git a/lib/ovs-actions.xml b/lib/ovs-actions.xml index a2778de4b..e97f818d9 100644 --- a/lib/ovs-actions.xml +++ b/lib/ovs-actions.xml @@ -265,13 +265,13 @@ </p> <p> - When a <code>decap</code> action decapsulates a packet, Open vSwitch - raises this error if it does not support the type of inner packet. - <code>decap</code> of an Ethernet header raises this error if a VLAN - header is present, <code>decap</code> of a NSH packet raises this error - if the NSH inner packet is not Ethernet, IPv4, IPv6, or NSH, and - <code>decap</code> of other types of packets is unsupported and also - raises this error. + The <code>decap</code> action is supported only for packet types + ethernet, NSH and MPLS. Openvswitch raises this error for other + packet types. When a <code>decap</code> action decapsulates a packet, + Open vSwitch raises this error if it does not support the type of inner + packet. <code>decap</code> of an Ethernet header raises this error if a + VLAN header is present, <code>decap</code> of a NSH packet raises this + error if the NSH inner packet is not Ethernet, IPv4, IPv6, or NSH. </p> <p> @@ -1097,6 +1097,8 @@ for <var>i</var> in [1,<var>n_members</var>]: <h2>The <code>encap</code> action</h2> <syntax><code>encap(nsh(</code>[<code>md_type=<var>md_type</var></code>]<code>, </code>[<code>tlv(<var>class</var>,<var>type</var>,<var>value</var>)</code>]...<code>))</code></syntax> <syntax><code>encap(ethernet)</code></syntax> + <syntax><code>encap(mpls(ether_type=<var>ether_type</var>))</code> + </syntax> <p> The <code>encap</code> action encapsulates a packet with a specified @@ -1135,6 +1137,12 @@ for <var>i</var> in [1,<var>n_members</var>]: source and destination are initially zeroed. </p> + <p> + The <code>encap(mpls(ethertype=....))</code> variant encapsulates an + ethernet or L3 packet with a MPLS header. The <var>ethertype</var> + could be MPLS unicast (0x8847) or multicast (0x8848) ethertypes. + </p> + <conformance> This action is an Open vSwitch extension to OpenFlow 1.3 and later, introduced in Open vSwitch 2.8. @@ -1144,6 +1152,9 @@ for <var>i</var> in [1,<var>n_members</var>]: <action name="DECAP"> <h2>The <code>decap</code> action</h2> <syntax><code>decap</code></syntax> + <syntax><code>decap(packet_type(ns=<var>name_space</var>, + type=<var>ethertype</var>))</code></syntax> for decapsulating MPLS + packets. <p> Removes an outermost encapsulation from the packet: @@ -1164,6 +1175,12 @@ for <var>i</var> in [1,<var>n_members</var>]: packet type errors. </li> + <li> + Otherwise, if the packet is a MPLS packet, removes the MPLS header + and classifies the inner packet as mentioned in the packet type + argument of the decap. + </li> + <li> Otherwise, raises an unsupported packet type error. </li> diff --git a/lib/packets.c b/lib/packets.c index 4a7643c5d..5e3c3900f 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -418,6 +418,38 @@ push_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32 lse) pkt_metadata_init_conn(&packet->md); } +void +add_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32 lse, bool l3) +{ + char * header; + + if (!eth_type_mpls(ethtype)) { + return; + } + + if (!l3) { + header = dp_packet_push_uninit(packet, MPLS_HLEN); + memcpy(header, &lse, sizeof lse); + packet->l2_5_ofs = 0; + packet->packet_type = htonl(PT_MPLS); + } else { + size_t len; + + if (!is_mpls(packet)) { + /* Set MPLS label stack offset. */ + packet->l2_5_ofs = packet->l3_ofs; + } + set_ethertype(packet, ethtype); + + /* Push new MPLS shim header onto packet. */ + len = packet->l2_5_ofs; + header = dp_packet_resize_l2_5(packet, MPLS_HLEN); + memmove(header, header + MPLS_HLEN, len); + memcpy(header + len, &lse, sizeof lse); + } + pkt_metadata_init_conn(&packet->md); +} + /* If 'packet' is an MPLS packet, removes its outermost MPLS label stack entry. * If the label that was removed was the only MPLS label, changes 'packet''s * Ethertype to 'ethtype' (which ordinarily should not be an MPLS @@ -429,6 +461,10 @@ pop_mpls(struct dp_packet *packet, ovs_be16 ethtype) struct mpls_hdr *mh = dp_packet_l2_5(packet); size_t len = packet->l2_5_ofs; + if (ethtype == htons(ETH_TYPE_TEB)) { + packet->packet_type = htonl(PT_ETH); + } + set_ethertype(packet, ethtype); if (get_16aligned_be32(&mh->mpls_lse) & htonl(MPLS_BOS_MASK)) { dp_packet_set_l2_5(packet, NULL); diff --git a/lib/packets.h b/lib/packets.h index 481bc22fa..3f5862e08 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -350,6 +350,8 @@ void set_mpls_lse_label(ovs_be32 *lse, ovs_be32 label); void set_mpls_lse_bos(ovs_be32 *lse, uint8_t bos); ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos, ovs_be32 label); +void add_mpls(struct dp_packet *packet, ovs_be16 ethtype, ovs_be32 lse, + bool l3_flag); /* Example: * diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c index 796eb6f88..9280e008e 100644 --- a/ofproto/ofproto-dpif-ipfix.c +++ b/ofproto/ofproto-dpif-ipfix.c @@ -3018,6 +3018,7 @@ dpif_ipfix_read_actions(const struct flow *flow, case OVS_ACTION_ATTR_CHECK_PKT_LEN: case OVS_ACTION_ATTR_UNSPEC: case OVS_ACTION_ATTR_DROP: + case OVS_ACTION_ATTR_ADD_MPLS: case __OVS_ACTION_ATTR_MAX: default: break; diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c index fdcb9eabb..ca46a9bc4 100644 --- a/ofproto/ofproto-dpif-sflow.c +++ b/ofproto/ofproto-dpif-sflow.c @@ -1226,6 +1226,7 @@ dpif_sflow_read_actions(const struct flow *flow, case OVS_ACTION_ATTR_UNSPEC: case OVS_ACTION_ATTR_CHECK_PKT_LEN: case OVS_ACTION_ATTR_DROP: + case OVS_ACTION_ATTR_ADD_MPLS: case __OVS_ACTION_ATTR_MAX: default: break; diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 7108c8a30..a97534233 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -6381,6 +6381,45 @@ rewrite_flow_encap_ethernet(struct xlate_ctx *ctx, ctx->error = XLATE_UNSUPPORTED_PACKET_TYPE; } } +static void +rewrite_flow_encap_mpls(struct xlate_ctx *ctx, + const struct ofpact_encap *encap, + struct flow *flow, + struct flow_wildcards *wc) +{ + int n; + uint32_t i; + uint16_t ether_type; + const char *ptr = (char *) encap->props; + + for (i = 0; i < encap->n_props; i++) { + struct ofpact_ed_prop *prop_ptr = + ALIGNED_CAST(struct ofpact_ed_prop *, ptr); + if (prop_ptr->prop_class == OFPPPC_MPLS) { + switch (prop_ptr->type) { + case OFPPPT_PROP_MPLS_ETHERTYPE: { + struct ofpact_ed_prop_mpls_ethertype *prop_ether_type = + ALIGNED_CAST(struct ofpact_ed_prop_mpls_ethertype *, + prop_ptr); + ether_type = prop_ether_type->ether_type; + break; + } + } + } + } + + wc->masks.packet_type = OVS_BE32_MAX; + if (flow->packet_type != htonl(PT_MPLS)) { + memset(&ctx->wc->masks.mpls_lse, 0x0, + sizeof *wc->masks.mpls_lse * FLOW_MAX_MPLS_LABELS); + memset(&flow->mpls_lse, 0x0, sizeof *flow->mpls_lse * + FLOW_MAX_MPLS_LABELS); + } + flow->packet_type = htonl(PT_MPLS); + n = flow_count_mpls_labels(flow, ctx->wc); + flow_push_mpls(flow, n, htons(ether_type), ctx->wc, true); +} + /* For an MD2 NSH header returns a pointer to an ofpbuf with the encoded * MD2 TLVs provided as encap properties to the encap operation. This @@ -6513,6 +6552,12 @@ xlate_generic_encap_action(struct xlate_ctx *ctx, case PT_NSH: encap_data = rewrite_flow_push_nsh(ctx, encap, flow, wc); break; + case PT_MPLS: + rewrite_flow_encap_mpls(ctx, encap, flow, wc); + if (!ctx->xbridge->support.add_mpls) { + ctx->xout->slow |= SLOW_ACTION; + } + break; default: /* New packet type was checked during decoding. */ OVS_NOT_REACHED(); @@ -6582,6 +6627,21 @@ xlate_generic_decap_action(struct xlate_ctx *ctx, ctx->pending_decap = true; /* Trigger recirculation. */ return true; + case PT_MPLS: { + int n; + ovs_be16 ethertype; + + flow->packet_type = decap->new_pkt_type; + ethertype = pt_ns_type_be(flow->packet_type); + + n = flow_count_mpls_labels(flow, ctx->wc); + flow_pop_mpls(flow, n, ethertype, ctx->wc); + if (!ctx->xbridge->support.add_mpls) { + ctx->xout->slow |= SLOW_ACTION; + } + ctx->pending_decap = true; + return true; + } default: /* Error handling: drop packet. */ xlate_report_debug( diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index fd0b2fdea..d9a2922e7 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -1520,6 +1520,44 @@ check_nd_extensions(struct dpif_backer *backer) return !error; } +/* Tests whether 'backer''s datapath supports the + * OVS_ACTION_ATTR_ADD_MPLS action. */ +static bool +check_add_mpls(struct dpif_backer *backer) +{ + struct odputil_keybuf keybuf; + struct ofpbuf actions; + struct ofpbuf key; + struct flow flow; + bool supported; + + struct odp_flow_key_parms odp_parms = { + .flow = &flow, + .probe = true, + }; + + memset(&flow, 0, sizeof flow); + ofpbuf_use_stack(&key, &keybuf, sizeof keybuf); + odp_flow_key_from_flow(&odp_parms, &key); + ofpbuf_init(&actions, 64); + + struct ovs_action_add_mpls *mpls; + + mpls = nl_msg_put_unspec_zero(&actions, + OVS_ACTION_ATTR_ADD_MPLS, + sizeof *mpls); + mpls->mpls_ethertype = htons(ETH_TYPE_MPLS); + + supported = dpif_probe_feature(backer->dpif, "add_mpls", &key, + &actions, NULL); + ofpbuf_uninit(&actions); + VLOG_INFO("%s: Datapath %s add_mpls action", + dpif_name(backer->dpif), supported ? "supports" + : "does not support"); + return supported; + +} + #define CHECK_FEATURE__(NAME, SUPPORT, FIELD, VALUE, ETHTYPE) \ static bool \ @@ -1590,6 +1628,7 @@ check_support(struct dpif_backer *backer) dpif_supports_explicit_drop_action(backer->dpif); backer->rt_support.lb_output_action= dpif_supports_lb_output_action(backer->dpif); + backer->rt_support.add_mpls = check_add_mpls(backer); /* Flow fields. */ backer->rt_support.odp.ct_state = check_ct_state(backer); diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h index b41c3d82a..c04bfff8d 100644 --- a/ofproto/ofproto-dpif.h +++ b/ofproto/ofproto-dpif.h @@ -204,7 +204,10 @@ struct group_dpif *group_dpif_lookup(struct ofproto_dpif *, DPIF_SUPPORT_FIELD(bool, explicit_drop_action, "Explicit Drop action") \ \ /* True if the datapath supports balance_tcp optimization */ \ - DPIF_SUPPORT_FIELD(bool, lb_output_action, "Optimized Balance TCP mode") + DPIF_SUPPORT_FIELD(bool, lb_output_action, "Optimized Balance TCP mode")\ + \ + /* True if the datapath supports layer 2 MPLS tunnelling */ \ + DPIF_SUPPORT_FIELD(bool, add_mpls, "l2 MPLS tunnelling") /* Stores the various features which the corresponding backer supports. */ diff --git a/tests/system-traffic.at b/tests/system-traffic.at index fb5b9a36d..b865b5210 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at @@ -1027,9 +1027,45 @@ NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + + +AT_SETUP([datapath - ptap mpls actions]) +OVS_TRAFFIC_VSWITCHD_START([_ADD_BR([br1])]) + +ADD_NAMESPACES(at_ns0, at_ns1) + +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") +ADD_VETH(p1, at_ns1, br1, "10.1.1.2/24") + +AT_CHECK([ip link add patch0 type veth peer name patch1]) +on_exit 'ip link del patch0' + +AT_CHECK([ip link set dev patch0 up]) +AT_CHECK([ip link set dev patch1 up]) +AT_CHECK([ovs-vsctl add-port br0 patch0 -- set interface patch0 ofport_request=100]) +AT_CHECK([ovs-vsctl add-port br1 patch1 -- set interface patch1 ofport_request=100]) + +AT_DATA([flows.txt], [dnl +table=0,priority=100,dl_type=0x0800 actions=encap(mpls(ether_type=0x8847)),set_mpls_label:2,encap(ethernet),output:100 +table=0,priority=100,dl_type=0x8847,mpls_label=2 actions=decap(),decap(packet_type(ns=0,type=0)),resubmit(,3) +table=0,priority=10 actions=resubmit(,3) +table=3,priority=10 actions=normal +]) + +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br0 flows.txt]) +AT_CHECK([ovs-ofctl -Oopenflow13 add-flows br1 flows.txt]) + +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], [0], [dnl +3 packets transmitted, 3 received, 0% packet loss, time 0ms +]) + + NS_CHECK_EXEC([at_ns1], [ping -q -c 3 -i 0.3 -w 2 10.1.1.1 | FORMAT_PING], [0], [dnl 3 packets transmitted, 3 received, 0% packet loss, time 0ms ]) + OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP -- 2.18.4 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev