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