[ovs-dev] [PATCH v2.0] Non-Datapath MPLS actions and matches
Simon Horman
horms at verge.net.au
Wed Sep 26 07:13:41 UTC 2012
This patch provides an implementation of the non-datapath portions
of MPLS matches and actions.
This patch is based on top of Ben Pfaff's series,
"set-field action support"
Cc: Isaku Yamahata <yamahata at valinux.co.jp>
Cc: Ravi K <rkerur at gmail.com>
Signed-off-by: Simon Horman <horms at verge.net.au>
---
v2.0 [Simon Horman]
* Rebase to current master
- First by Isaku Yamahata in July;
- Then by me more recently.
* Remove datapath changes
- This is intended to provide a first step for
merging MPLS. The non-datapath changes seem
largely uncontroversial while the datapath
changes were incomplete with respect to
agreement on the implementation for TTL actions
and offloads.
* Correct dec_mpls_ttl to decerment ttl by 1 rather than 2
- Fix by Isaku Yamahata
* Bug fixes to allow test suite to pass
* Make sparse clean
* Introduce encap_dl_type element to struct flow.
This is used to store the dl_type of the encapsulated packet
and is used by odp_flow_key_to_flow() and flow_compose() to
allow eth_from_packet_or_flow() to be able to handle
e.g. an MPLS encoded IPv4 packet.
* update packet.c:get_label_ttl_and_tc() to treat unknown eth type
asn an error.
* Remove datapath changes
Previous Revisions by Ravi K
Todo:
Remove NX matches and actions? Are they necessary?
---
include/linux/openvswitch.h | 2 +
include/openflow/nicira-ext.h | 90 ++++++++++
include/openflow/openflow-1.2.h | 2 +
lib/dpif-netdev.c | 2 +
lib/flow.c | 128 ++++++++++++--
lib/flow.h | 23 ++-
lib/match.c | 69 +++++++-
lib/match.h | 6 +
lib/meta-flow.c | 124 ++++++++++++++
lib/meta-flow.h | 9 +
lib/nx-match.c | 22 ++-
lib/odp-util.c | 179 +++++++++++++++++++-
lib/ofp-actions.c | 265 +++++++++++++++++++++++++++++
lib/ofp-actions.h | 56 ++++++
lib/ofp-parse.c | 44 +++++
lib/ofp-print.c | 4 +
lib/ofp-util.c | 30 +++-
lib/ofp-util.def | 16 ++
lib/ofpbuf.c | 8 +-
lib/ofpbuf.h | 1 +
lib/packets.c | 356 +++++++++++++++++++++++++++++++++++++++
lib/packets.h | 88 ++++++++++
ofproto/ofproto-dpif.c | 232 ++++++++++++++++++++++++-
tests/automake.mk | 5 +
tests/odp.at | 16 +-
tests/ofp-print.at | 6 +-
tests/ofproto-dpif.at | 183 +++++++++++++++++---
tests/ofproto.at | 12 +-
tests/test-bundle.c | 2 +-
tests/test-mpls.c | 288 +++++++++++++++++++++++++++++++
tests/test-multipath.c | 2 +-
utilities/ovs-dpctl.c | 18 +-
utilities/ovs-ofctl.8.in | 45 +++++
33 files changed, 2259 insertions(+), 74 deletions(-)
create mode 100644 tests/test-mpls.c
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index f5c9cca..fcb6895 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -278,6 +278,8 @@ enum ovs_key_attr {
OVS_KEY_ATTR_ICMPV6, /* struct ovs_key_icmpv6 */
OVS_KEY_ATTR_ARP, /* struct ovs_key_arp */
OVS_KEY_ATTR_ND, /* struct ovs_key_nd */
+ OVS_KEY_ATTR_MPLS, /* be32 MPLS Label Stack Entry */
+ OVS_KEY_ATTR_INNER_MPLS,/* be32 Inner MPLS Label Stack Entry */
OVS_KEY_ATTR_TUN_ID = 63, /* be64 tunnel ID */
__OVS_KEY_ATTR_MAX
};
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index 75bf6db..80a7a8b 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -304,6 +304,14 @@ enum nx_action_subtype {
NXAST_FIN_TIMEOUT, /* struct nx_action_fin_timeout */
NXAST_CONTROLLER, /* struct nx_action_controller */
NXAST_DEC_TTL_CNT_IDS, /* struct nx_action_cnt_ids */
+ NXAST_COPY_TTL_OUT, /* struct nx_action_header */
+ NXAST_COPY_TTL_IN, /* struct nx_action_header */
+ NXAST_SET_MPLS_LABEL, /* struct nx_action_mpls_label */
+ NXAST_SET_MPLS_TC, /* struct nx_action_mpls_tc */
+ NXAST_SET_MPLS_TTL, /* struct nx_action_mpls_ttl */
+ NXAST_DEC_MPLS_TTL, /* struct nx_action_header */
+ NXAST_PUSH_MPLS, /* struct nx_action_push_mpls */
+ NXAST_POP_MPLS, /* struct nx_action_pop_mpls */
};
/* Header for Nicira-defined actions. */
@@ -1757,6 +1765,33 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
#define NXM_NX_COOKIE NXM_HEADER (0x0001, 30, 8)
#define NXM_NX_COOKIE_W NXM_HEADER_W(0x0001, 30, 8)
+/* The mpls_label in MPLS shim header.
+ *
+ * Prereqs: NXM_OF_ETH_TYPE must be either 0x8847 or 0x8848.
+ *
+ * Format: 32-bit integer, lower 20 bits
+ *
+ * Masking: Not maskable. */
+#define NXM_NX_MPLS_LABEL NXM_HEADER (0x0001, 31, 4)
+
+/* The mpls_tc in MPLS shim header.
+ *
+ * Prereqs: NXM_OF_ETH_TYPE must be either 0x8847 or 0x8848.
+ *
+ * Format: 8-bit integer, lower 3 bits
+ *
+ * Masking: Not maskable. */
+#define NXM_NX_MPLS_TC NXM_HEADER (0x0001, 32, 1)
+
+/* The mpls_stack in MPLS shim header.
+ *
+ * Prereqs: NXM_OF_ETH_TYPE must be either 0x8847 or 0x8848.
+ *
+ * Format: 8-bit integer, lower 1 bit
+ *
+ * Masking: Not maskable. */
+#define NXM_NX_MPLS_STACK NXM_HEADER (0x0001, 33, 1)
+
/* ## --------------------- ## */
/* ## Requests and replies. ## */
/* ## --------------------- ## */
@@ -2192,4 +2227,59 @@ struct nx_flow_monitor_cancel {
};
OFP_ASSERT(sizeof(struct nx_flow_monitor_cancel) == 4);
+/* Action structure for NXAST_SET_MPLS_LABEL. */
+struct nx_action_mpls_label {
+ ovs_be16 type; /* OFPAT_SET_MPLS_LABEL. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_SET_MPLS_LABEL. */
+ uint8_t pad[2];
+ ovs_be32 mpls_label; /* MPLS label in low 20 bits. */
+};
+OFP_ASSERT(sizeof(struct nx_action_mpls_label) == 16);
+
+/* Action structure for NXAST_SET_MPLS_TC. */
+struct nx_action_mpls_tc {
+ ovs_be16 type; /* OFPAT_SET_MPLS_TC. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_SET_MPLS_TC. */
+ uint8_t mpls_tc; /* MPLS TC */
+ uint8_t pad[5];
+};
+OFP_ASSERT(sizeof(struct nx_action_mpls_tc) == 16);
+
+/* Action structure for NXAST_SET_MPLS_TTL. */
+struct nx_action_mpls_ttl {
+ ovs_be16 type; /* OFPAT_SET_MPLS_TTL. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_SET_MPLS_TTL. */
+ uint8_t mpls_ttl; /* MPLS TTL */
+ uint8_t pad[5];
+};
+OFP_ASSERT(sizeof(struct nx_action_mpls_ttl) == 16);
+
+/* Action structure for NXAST_PUSH_VLAN/MPLS. */
+struct nx_action_push {
+ ovs_be16 type; /* OFPAT_PUSH_VLAN/MPLS. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_PUSH_MPLS. */
+ ovs_be16 ethertype; /* Ethertype */
+ uint8_t pad[4];
+};
+OFP_ASSERT(sizeof(struct nx_action_push) == 16);
+
+/* Action structure for NXAST_POP_MPLS. */
+struct nx_action_pop_mpls {
+ ovs_be16 type; /* OFPAT_POP_MPLS. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_POP_MPLS. */
+ ovs_be16 ethertype; /* Ethertype */
+ uint8_t pad[4];
+};
+OFP_ASSERT(sizeof(struct nx_action_pop_mpls) == 16);
+
#endif /* openflow/nicira-ext.h */
diff --git a/include/openflow/openflow-1.2.h b/include/openflow/openflow-1.2.h
index 1c3f017..771e5bb 100644
--- a/include/openflow/openflow-1.2.h
+++ b/include/openflow/openflow-1.2.h
@@ -106,6 +106,7 @@ enum oxm12_ofb_match_fields {
OFPXMT12_OFB_IPV6_ND_TLL, /* Target link-layer for ND. */
OFPXMT12_OFB_MPLS_LABEL, /* MPLS label. */
OFPXMT12_OFB_MPLS_TC, /* MPLS TC. */
+ OFPXMT12_OFB_MPLS_STACK, /* MPLS stack. */
/* End Marker */
OFPXMT12_OFB_MAX,
@@ -172,6 +173,7 @@ enum oxm12_ofb_match_fields {
#define OXM_OF_IPV6_ND_TLL OXM_HEADER (OFPXMT12_OFB_IPV6_ND_TLL, 6)
#define OXM_OF_MPLS_LABEL OXM_HEADER (OFPXMT12_OFB_MPLS_LABEL, 4)
#define OXM_OF_MPLS_TC OXM_HEADER (OFPXMT12_OFB_MPLS_TC, 1)
+#define OXM_OF_MPLS_STACK OXM_HEADER (OFPXMT12_OFB_MPLS_STACK, 1)
/* The VLAN id is 12-bits, so we can use the entire 16 bits to indicate
* special conditions.
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 144b6b6..778031e 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1209,6 +1209,8 @@ execute_set_action(struct ofpbuf *packet, const struct nlattr *a)
case OVS_KEY_ATTR_ETHERTYPE:
case OVS_KEY_ATTR_IN_PORT:
case OVS_KEY_ATTR_VLAN:
+ case OVS_KEY_ATTR_MPLS:
+ case OVS_KEY_ATTR_INNER_MPLS:
case OVS_KEY_ATTR_ICMP:
case OVS_KEY_ATTR_ICMPV6:
case OVS_KEY_ATTR_ARP:
diff --git a/lib/flow.c b/lib/flow.c
index e517a03..9897a61 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -93,6 +93,33 @@ pull_icmpv6(struct ofpbuf *packet)
}
static void
+parse_remaining_mpls(struct ofpbuf *b, struct flow *flow)
+{
+ /* Proceed with parsing remaining MPLS headers. */
+ struct mpls_hdr *mh = ofpbuf_pull(b, sizeof *mh);
+ while (!(mh->mpls_lse & htonl(MPLS_STACK_MASK))) {
+ if (flow->inner_mpls_lse == htonl(0)) {
+ flow->inner_mpls_lse = mh->mpls_lse;
+ }
+ mh = ofpbuf_pull(b, sizeof *mh);
+ }
+ if (flow->inner_mpls_lse == htonl(0)) {
+ flow->inner_mpls_lse = mh->mpls_lse;
+ }
+}
+
+static void
+parse_mpls(struct ofpbuf *b, struct flow *flow)
+{
+ /* Make sure there is some data following MPLS header
+ before proceeding with parsing MPLS headers. */
+ if (b->size >= sizeof(struct mpls_hdr) + sizeof(ovs_be16)) {
+ struct mpls_hdr *mh = ofpbuf_pull(b, sizeof *mh);
+ flow->mpls_lse = mh->mpls_lse;
+ }
+}
+
+static void
parse_vlan(struct ofpbuf *b, struct flow *flow)
{
struct qtag_prefix {
@@ -323,6 +350,8 @@ invalid:
*
* - packet->l2 to the start of the Ethernet header.
*
+ * - packet->l2_5 to the start of the MPLS shim header.
+ *
* - packet->l3 to just past the Ethernet header, or just past the
* vlan_header if one is present, to the first byte of the payload of the
* Ethernet frame.
@@ -347,10 +376,11 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, ovs_be64 tun_id,
flow->in_port = ofp_in_port;
flow->skb_priority = skb_priority;
- packet->l2 = b.data;
- packet->l3 = NULL;
- packet->l4 = NULL;
- packet->l7 = NULL;
+ packet->l2 = b.data;
+ packet->l2_5 = NULL;
+ packet->l3 = NULL;
+ packet->l4 = NULL;
+ packet->l7 = NULL;
if (b.size < sizeof *eth) {
return;
@@ -368,6 +398,25 @@ flow_extract(struct ofpbuf *packet, uint32_t skb_priority, ovs_be64 tun_id,
}
flow->dl_type = parse_ethertype(&b);
+ /* Parse mpls, copy l3 ttl. */
+ if (flow->dl_type == htons(ETH_TYPE_MPLS) ||
+ flow->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ struct ip_header *ih = b.data;
+ struct ip6_hdr *ih6 = b.data;
+ packet->l2_5 = b.data;
+ parse_mpls(&b, flow);
+ if (!(flow->mpls_lse & htonl(MPLS_STACK_MASK))) {
+ parse_remaining_mpls(&b, flow);
+ }
+ if (ih) {
+ if (IP_VER(ih->ip_ihl_ver) == IP_VERSION) {
+ flow->nw_ttl = ih->ip_ttl;
+ } else if (IP6_VER(ih6->ip6_vfc) == IP6_VERSION) {
+ flow->nw_ttl = ih6->ip6_hlim;
+ }
+ }
+ }
+
/* Network layer. */
packet->l3 = b.data;
if (flow->dl_type == htons(ETH_TYPE_IP)) {
@@ -458,7 +507,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
void
flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
fmd->tun_id = flow->tun_id;
fmd->metadata = flow->metadata;
@@ -500,6 +549,18 @@ flow_format(struct ds *ds, const struct flow *flow)
ETH_ADDR_ARGS(flow->dl_dst),
ntohs(flow->dl_type));
+ ds_put_format(ds, ",mpls(");
+ if (flow->mpls_lse) {
+ ds_put_format(ds, "label:%"PRIu32",tc:%d,ttl:%d,bos:%d",
+ mpls_lse_to_label(flow->mpls_lse),
+ mpls_lse_to_tc(flow->mpls_lse),
+ mpls_lse_to_ttl(flow->mpls_lse),
+ mpls_lse_to_stack(flow->mpls_lse));
+ } else {
+ ds_put_char(ds, '0');
+ }
+ ds_put_char(ds, ')');
+
if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
ds_put_format(ds, " label:%#"PRIx32" proto:%"PRIu8" tos:%#"PRIx8
" ttl:%"PRIu8" ipv6(",
@@ -556,7 +617,7 @@ void
flow_wildcards_init_exact(struct flow_wildcards *wc)
{
memset(&wc->masks, 0xff, sizeof wc->masks);
- memset(wc->masks.zeros, 0, sizeof wc->masks.zeros);
+ flow_zero_pad(&wc->masks);
}
/* Returns true if 'wc' matches every packet, false if 'wc' fixes any bits or
@@ -788,6 +849,39 @@ flow_set_vlan_pcp(struct flow *flow, uint8_t pcp)
flow->vlan_tci |= htons((pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
}
+/* Sets the MPLS Label that 'flow' matches to 'label', which is interpreted
+ * as an OpenFlow 1.1 "mpls_label" value. */
+void
+flow_set_mpls_label(struct flow *flow, ovs_be32 label)
+{
+ if (label == htonl(0)) {
+ flow->mpls_lse = htonl(0);
+ } else {
+ flow->mpls_lse &= ~htonl(MPLS_LABEL_MASK);
+ flow->mpls_lse |=
+ htonl((ntohl(label) << MPLS_LABEL_SHIFT) & MPLS_LABEL_MASK);
+ }
+}
+
+/* Sets the MPLS TC that 'flow' matches to 'tc', which should be in the
+ * range 0...7. */
+void
+flow_set_mpls_tc(struct flow *flow, uint8_t tc)
+{
+ tc &= 0x07;
+ flow->mpls_lse &= ~htonl(MPLS_TC_MASK);
+ flow->mpls_lse |= htonl(tc << MPLS_TC_SHIFT);
+}
+
+/* Sets the MPLS STACK bit that 'flow' matches to which should be 0 or 1. */
+void
+flow_set_mpls_stack(struct flow *flow, uint8_t stack)
+{
+ stack &= 0x01;
+ flow->mpls_lse &= ~htonl(MPLS_STACK_MASK);
+ flow->mpls_lse |= htonl(stack << MPLS_STACK_SHIFT);
+}
+
/* Puts into 'b' a packet that flow_extract() would parse as having the given
* 'flow'.
*
@@ -797,7 +891,12 @@ flow_set_vlan_pcp(struct flow *flow, uint8_t pcp)
void
flow_compose(struct ofpbuf *b, const struct flow *flow)
{
- eth_compose(b, flow->dl_dst, flow->dl_src, ntohs(flow->dl_type), 0);
+ ovs_be16 inner_dl_type;
+
+ inner_dl_type = flow->encap_dl_type == htons(0)
+ ? flow->dl_type
+ : flow->encap_dl_type;
+ eth_compose(b, flow->dl_dst, flow->dl_src, ntohs(inner_dl_type), 0);
if (flow->dl_type == htons(FLOW_DL_TYPE_NONE)) {
struct eth_header *eth = b->l2;
eth->eth_type = htons(b->size);
@@ -808,7 +907,8 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
eth_push_vlan(b, flow->vlan_tci);
}
- if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ if (flow->dl_type == htons(ETH_TYPE_IP) ||
+ flow->encap_dl_type == htons(ETH_TYPE_IP)) {
struct ip_header *ip;
b->l3 = ip = ofpbuf_put_zeros(b, sizeof *ip);
@@ -853,9 +953,11 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
ip->ip_tot_len = htons((uint8_t *) b->data + b->size
- (uint8_t *) b->l3);
ip->ip_csum = csum(ip, sizeof *ip);
- } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+ } else if (flow->dl_type == htons(ETH_TYPE_IPV6) ||
+ flow->encap_dl_type == htons(ETH_TYPE_IPV6)) {
/* XXX */
- } else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
+ } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+ flow->encap_dl_type == htons(ETH_TYPE_ARP)) {
struct arp_eth_header *arp;
b->l3 = arp = ofpbuf_put_zeros(b, sizeof *arp);
@@ -873,6 +975,12 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
memcpy(arp->ar_tha, flow->arp_tha, ETH_ADDR_LEN);
}
}
+
+ if (flow->dl_type == htons(ETH_TYPE_MPLS) ||
+ flow->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ push_mpls(b, flow->dl_type);
+ set_mpls_lse(b, flow->mpls_lse);
+ }
}
/* Compressed flow. */
diff --git a/lib/flow.h b/lib/flow.h
index fc62222..1117bd8 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -36,7 +36,7 @@ struct ofpbuf;
/* This sequence number should be incremented whenever anything involving flows
* or the wildcarding of flows changes. This will cause build assertion
* failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 17
+#define FLOW_WC_SEQ 18
#define FLOW_N_REGS 8
BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -53,6 +53,8 @@ BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
BUILD_ASSERT_DECL(FLOW_NW_FRAG_ANY == NX_IP_FRAG_ANY);
BUILD_ASSERT_DECL(FLOW_NW_FRAG_LATER == NX_IP_FRAG_LATER);
+#define FLOW_PAD_LEN 0
+
struct flow {
ovs_be64 tun_id; /* Encapsulating tunnel ID. */
ovs_be64 metadata; /* OpenFlow Metadata. */
@@ -64,9 +66,12 @@ struct flow {
ovs_be32 nw_src; /* IPv4 source address. */
ovs_be32 nw_dst; /* IPv4 destination address. */
ovs_be32 ipv6_label; /* IPv6 flow label. */
+ ovs_be32 mpls_lse; /* MPLS label stack entry. */
+ ovs_be32 inner_mpls_lse; /* Inner MPLS label stack entry. */
uint16_t in_port; /* OpenFlow port number of input port. */
ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
ovs_be16 dl_type; /* Ethernet frame type. */
+ ovs_be16 encap_dl_type; /* MPLS encapsulated Ethernet frame type */
ovs_be16 tp_src; /* TCP/UDP source port. */
ovs_be16 tp_dst; /* TCP/UDP destination port. */
uint8_t dl_src[6]; /* Ethernet source address. */
@@ -77,14 +82,14 @@ struct flow {
uint8_t arp_tha[6]; /* ARP/ND target hardware address. */
uint8_t nw_ttl; /* IP TTL/Hop Limit. */
uint8_t nw_frag; /* FLOW_FRAG_* flags. */
- uint8_t zeros[2]; /* Must be zero. */
+ uint8_t zeros[FLOW_PAD_LEN]; /* Must be zero. */
};
BUILD_ASSERT_DECL(sizeof(struct flow) % 8 == 0);
#define FLOW_U32S (sizeof(struct flow) / 4)
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
-BUILD_ASSERT_DECL(sizeof(struct flow) == 152 && FLOW_WC_SEQ == 17);
+BUILD_ASSERT_DECL(sizeof(struct flow) == 160 && FLOW_WC_SEQ == 18);
/* Represents the metadata fields of struct flow. */
struct flow_metadata {
@@ -94,6 +99,14 @@ struct flow_metadata {
uint16_t in_port; /* OpenFlow port or zero. */
};
+#if (FLOW_PAD_LEN > 0)
+static inline void flow_zero_pad(struct flow *flow)
+{
+ memset(flow->zeros, 0, sizeof flow->zeros);
+}
+#else
+static inline void flow_zero_pad(struct flow *flow OVS_UNUSED) {;}
+#endif
void flow_extract(struct ofpbuf *, uint32_t priority, ovs_be64 tun_id,
uint16_t in_port, struct flow *);
void flow_zero_wildcards(struct flow *, const struct flow_wildcards *);
@@ -110,6 +123,10 @@ void flow_set_dl_vlan(struct flow *, ovs_be16 vid);
void flow_set_vlan_vid(struct flow *, ovs_be16 vid);
void flow_set_vlan_pcp(struct flow *, uint8_t pcp);
+void flow_set_mpls_label(struct flow *flow, ovs_be32 label);
+void flow_set_mpls_tc(struct flow *flow, uint8_t tc);
+void flow_set_mpls_stack(struct flow *flow, uint8_t stack);
+
void flow_compose(struct ofpbuf *, const struct flow *);
static inline int
diff --git a/lib/match.c b/lib/match.c
index 69129d4..ec883d8 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -280,6 +280,57 @@ match_set_dl_vlan_pcp(struct match *match, uint8_t dl_vlan_pcp)
match->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_PCP_MASK);
}
+/* Modifies 'match' so that the MPLS label is wildcarded. */
+void
+match_set_any_mpls_label(struct match *match)
+{
+ match->wc.masks.mpls_lse |= ~htonl(MPLS_LABEL_MASK);
+ flow_set_mpls_label(&match->flow, htonl(0));
+}
+
+/* Modifies 'match' so that it matches only packets with an MPLS header whose
+ * label equals the low 20 bits of 'mpls_label'. */
+void
+match_set_mpls_label(struct match *match, ovs_be32 mpls_label)
+{
+ match->wc.masks.mpls_lse |= htonl(MPLS_LABEL_MASK);
+ flow_set_mpls_label(&match->flow, mpls_label);
+}
+
+/* Modifies 'match' so that the MPLS TC is wildcarded. */
+void
+match_set_any_mpls_tc(struct match *match)
+{
+ match->wc.masks.mpls_lse |= ~htonl(MPLS_TC_MASK);
+ flow_set_mpls_tc(&match->flow, 0);
+}
+
+/* Modifies 'match' so that it matches only packets with an MPLS header whose
+ * Traffic Class equals the low 3 bits of 'mpls_tc'. */
+void
+match_set_mpls_tc(struct match *match, uint8_t mpls_tc)
+{
+ match->wc.masks.mpls_lse |= htonl(MPLS_TC_MASK);
+ flow_set_mpls_tc(&match->flow, mpls_tc);
+}
+
+/* Modifies 'match' so that the MPLS stack flag is wildcarded. */
+void
+match_set_any_mpls_stack(struct match *match)
+{
+ match->wc.masks.mpls_lse |= ~htonl(MPLS_STACK_MASK);
+ flow_set_mpls_stack(&match->flow, 0);
+}
+
+/* Modifies 'match' so that it matches only packets with an MPLS header whose
+ * Stack Flag equals the lower bit of 'mpls_stack' */
+void
+match_set_mpls_stack(struct match *match, uint8_t mpls_stack)
+{
+ match->wc.masks.mpls_lse |= htonl(MPLS_STACK_MASK);
+ flow_set_mpls_stack(&match->flow, mpls_stack);
+}
+
void
match_set_tp_src(struct match *match, ovs_be16 tp_src)
{
@@ -567,7 +618,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
if (priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "priority=%u,", priority);
@@ -609,6 +660,10 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
}
} else if (f->dl_type == htons(ETH_TYPE_ARP)) {
ds_put_cstr(s, "arp,");
+ } else if (f->dl_type == htons(ETH_TYPE_MPLS)) {
+ ds_put_cstr(s, "mpls,");
+ } else if (f->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ ds_put_cstr(s, "mplsm,");
} else {
skip_type = false;
}
@@ -717,6 +772,18 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
if (wc->masks.nw_ttl) {
ds_put_format(s, "nw_ttl=%"PRIu8",", f->nw_ttl);
}
+ if (wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
+ ds_put_format(s, "mpls_label=%"PRIu32",",
+ mpls_lse_to_label(f->mpls_lse));
+ }
+ if (wc->masks.mpls_lse & htonl(MPLS_TC_MASK)) {
+ ds_put_format(s, "mpls_tc=%"PRIu8",",
+ mpls_lse_to_tc(f->mpls_lse));
+ }
+ if (wc->masks.mpls_lse & htonl(MPLS_STACK_MASK)) {
+ ds_put_format(s, "mpls_stack=%"PRIu8",",
+ mpls_lse_to_stack(f->mpls_lse));
+ }
switch (wc->masks.nw_frag) {
case FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER:
ds_put_format(s, "nw_frag=%s,",
diff --git a/lib/match.h b/lib/match.h
index 2d05819..f7cc033 100644
--- a/lib/match.h
+++ b/lib/match.h
@@ -65,6 +65,12 @@ void match_set_vlan_vid(struct match *, ovs_be16);
void match_set_vlan_vid_masked(struct match *, ovs_be16 vid, ovs_be16 mask);
void match_set_any_pcp(struct match *);
void match_set_dl_vlan_pcp(struct match *, uint8_t);
+void match_set_any_mpls_label(struct match *);
+void match_set_mpls_label(struct match *, ovs_be32);
+void match_set_any_mpls_tc(struct match *);
+void match_set_mpls_tc(struct match *, uint8_t);
+void match_set_any_mpls_stack(struct match *);
+void match_set_mpls_stack(struct match *, uint8_t);
void match_set_tp_src(struct match *, ovs_be16);
void match_set_tp_src_masked(struct match *, ovs_be16 port, ovs_be16 mask);
void match_set_tp_dst(struct match *, ovs_be16);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 38c9a27..504967c 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -194,6 +194,38 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
OXM_OF_VLAN_PCP, "OXM_OF_VLAN_PCP",
},
+ /* ## ---- ## */
+ /* ## L2.5 ## */
+ /* ## ---- ## */
+ {
+ MFF_MPLS_LABEL, "mpls_label", NULL,
+ 4, 20,
+ MFM_NONE,
+ MFS_DECIMAL,
+ MFP_MPLS,
+ true,
+ NXM_NX_MPLS_LABEL, "NXM_NX_MPLS_LABEL",
+ 0, NULL,
+ }, {
+ MFF_MPLS_TC, "mpls_tc", NULL,
+ 1, 3,
+ MFM_NONE,
+ MFS_DECIMAL,
+ MFP_MPLS,
+ true,
+ NXM_NX_MPLS_TC, "NXM_NX_MPLS_TC",
+ 0, NULL,
+ }, {
+ MFF_MPLS_STACK, "mpls_stack", NULL,
+ 1, 1,
+ MFM_NONE,
+ MFS_DECIMAL,
+ MFP_MPLS,
+ true,
+ NXM_NX_MPLS_STACK, "NXM_NX_MPLS_STACK",
+ 0, NULL,
+ },
+
/* ## -- ## */
/* ## L3 ## */
/* ## -- ## */
@@ -607,6 +639,13 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
case MFF_VLAN_PCP:
return !(wc->masks.vlan_tci & htons(VLAN_PCP_MASK));
+ case MFF_MPLS_LABEL:
+ return !(wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK));
+ case MFF_MPLS_TC:
+ return !(wc->masks.mpls_lse & htonl(MPLS_TC_MASK));
+ case MFF_MPLS_STACK:
+ return !(wc->masks.mpls_lse & htonl(MPLS_STACK_MASK));
+
case MFF_IPV4_SRC:
return !wc->masks.nw_src;
case MFF_IPV4_DST:
@@ -707,6 +746,16 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
mask->u8 = vlan_tci_to_pcp(wc->masks.vlan_tci);
break;
+ case MFF_MPLS_LABEL:
+ mask->be32 = htonl(mpls_lse_to_label(wc->masks.mpls_lse));
+ break;
+ case MFF_MPLS_TC:
+ mask->u8 = mpls_lse_to_tc(wc->masks.mpls_lse);
+ break;
+ case MFF_MPLS_STACK:
+ mask->u8 = mpls_lse_to_stack(wc->masks.mpls_lse);
+ break;
+
case MFF_IPV4_SRC:
mask->be32 = wc->masks.nw_src;
break;
@@ -841,6 +890,9 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
return flow->dl_type == htons(ETH_TYPE_IPV6);
case MFP_VLAN_VID:
return (flow->vlan_tci & htons(VLAN_CFI)) != 0;
+ case MFP_MPLS:
+ return (flow->dl_type == htons(ETH_TYPE_MPLS) ||
+ flow->dl_type == htons(ETH_TYPE_MPLS_MCAST));
case MFP_IP_ANY:
return is_ip_any(flow);
@@ -938,6 +990,15 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
case MFF_IPV6_LABEL:
return !(value->be32 & ~htonl(IPV6_LABEL_MASK));
+ case MFF_MPLS_LABEL:
+ return !(value->be32 & ~htonl(MPLS_LABEL_MASK >> MPLS_LABEL_SHIFT));
+
+ case MFF_MPLS_TC:
+ return !(value->u8 & ~7);
+
+ case MFF_MPLS_STACK:
+ return !(value->u8 & ~1);
+
case MFF_N_IDS:
default:
NOT_REACHED();
@@ -994,6 +1055,18 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
value->u8 = vlan_tci_to_pcp(flow->vlan_tci);
break;
+ case MFF_MPLS_LABEL:
+ value->be32 = htonl(mpls_lse_to_label(flow->mpls_lse));
+ break;
+
+ case MFF_MPLS_TC:
+ value->u8 = mpls_lse_to_tc(flow->mpls_lse);
+ break;
+
+ case MFF_MPLS_STACK:
+ value->u8 = mpls_lse_to_stack(flow->mpls_lse);
+ break;
+
case MFF_IPV4_SRC:
value->be32 = flow->nw_src;
break;
@@ -1137,6 +1210,18 @@ mf_set_value(const struct mf_field *mf,
match_set_dl_vlan_pcp(match, value->u8);
break;
+ case MFF_MPLS_TC:
+ match_set_mpls_tc(match, value->u8);
+ break;
+
+ case MFF_MPLS_STACK:
+ match_set_mpls_stack(match, value->u8);
+ break;
+
+ case MFF_MPLS_LABEL:
+ match_set_mpls_label(match, value->be32);
+ break;
+
case MFF_IPV4_SRC:
match_set_nw_src(match, value->be32);
break;
@@ -1280,6 +1365,18 @@ mf_set_flow_value(const struct mf_field *mf,
flow_set_vlan_pcp(flow, value->u8);
break;
+ case MFF_MPLS_LABEL:
+ flow_set_mpls_label(flow, value->be32);
+ break;
+
+ case MFF_MPLS_TC:
+ flow_set_mpls_tc(flow, value->u8);
+ break;
+
+ case MFF_MPLS_STACK:
+ flow_set_mpls_stack(flow, value->u8);
+ break;
+
case MFF_IPV4_SRC:
flow->nw_src = value->be32;
break;
@@ -1439,6 +1536,18 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
match_set_any_pcp(match);
break;
+ case MFF_MPLS_LABEL:
+ match_set_any_mpls_label(match);
+ break;
+
+ case MFF_MPLS_TC:
+ match_set_any_mpls_tc(match);
+ break;
+
+ case MFF_MPLS_STACK:
+ match_set_any_mpls_stack(match);
+ break;
+
case MFF_IPV4_SRC:
case MFF_ARP_SPA:
match_set_nw_src_masked(match, htonl(0), htonl(0));
@@ -1564,6 +1673,9 @@ mf_set(const struct mf_field *mf,
case MFF_DL_VLAN:
case MFF_DL_VLAN_PCP:
case MFF_VLAN_PCP:
+ case MFF_MPLS_LABEL:
+ case MFF_MPLS_TC:
+ case MFF_MPLS_STACK:
case MFF_IP_PROTO:
case MFF_IP_TTL:
case MFF_IP_DSCP:
@@ -1798,6 +1910,18 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
value->u8 &= 0x07;
break;
+ case MFF_MPLS_LABEL:
+ value->be32 &= htonl(MPLS_LABEL_MASK >> MPLS_LABEL_SHIFT);
+ break;
+
+ case MFF_MPLS_TC:
+ value->u8 &= 0x07;
+ break;
+
+ case MFF_MPLS_STACK:
+ value->u8 &= 0x01;
+ break;
+
case MFF_N_IDS:
default:
NOT_REACHED();
diff --git a/lib/meta-flow.h b/lib/meta-flow.h
index 60bfeca..5fdfb00 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -71,6 +71,11 @@ enum mf_field_id {
MFF_DL_VLAN_PCP, /* u8 (OpenFlow 1.0 compatibility) */
MFF_VLAN_PCP, /* be16 (OpenFlow 1.2 compatibility) */
+ /* L2.5 */
+ MFF_MPLS_LABEL, /* be32 */
+ MFF_MPLS_TC, /* u8 */
+ MFF_MPLS_STACK, /* u8 */
+
/* L3. */
MFF_IPV4_SRC, /* be32 */
MFF_IPV4_DST, /* be32 */
@@ -157,6 +162,7 @@ enum mf_prereqs {
/* L2 requirements. */
MFP_ARP,
MFP_VLAN_VID,
+ MFP_MPLS,
MFP_IPV4,
MFP_IPV6,
MFP_IP_ANY,
@@ -212,6 +218,9 @@ struct mf_field {
* - "dl_vlan_pcp" is 1 byte but only 3 bits.
* - "is_frag" is 1 byte but only 2 bits.
* - "ipv6_label" is 4 bytes but only 20 bits.
+ * - "mpls_label" is 4 bytes but only 20 bits.
+ * - "mpls_tc" is 1 byte but only 3 bits.
+ * - "mpls_stack" is 1 byte but only 1 bit.
*/
unsigned int n_bytes; /* Width of the field in bytes. */
unsigned int n_bits; /* Number of significant bits in field. */
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 4254747..7d57418 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -547,7 +547,7 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
int match_len;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
/* Metadata. */
if (match->wc.masks.in_port) {
@@ -589,6 +589,26 @@ nx_put_raw(struct ofpbuf *b, bool oxm, const struct match *match,
match->wc.masks.vlan_tci);
}
+
+ /* MPLS. */
+ if (flow->dl_type == htons(ETH_TYPE_MPLS) ||
+ flow->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ if (match->wc.masks.mpls_lse & htonl(MPLS_TC_MASK)) {
+ nxm_put_8(b, oxm ? OXM_OF_MPLS_TC : NXM_NX_MPLS_TC,
+ mpls_lse_to_tc(flow->mpls_lse));
+ }
+
+ if (match->wc.masks.mpls_lse & htonl(MPLS_STACK_MASK)) {
+ nxm_put_8(b, oxm ? OXM_OF_MPLS_STACK : NXM_NX_MPLS_STACK,
+ mpls_lse_to_stack(flow->mpls_lse));
+ }
+
+ if (match->wc.masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
+ nxm_put_32(b, oxm ? OXM_OF_MPLS_LABEL : NXM_NX_MPLS_LABEL,
+ htonl(mpls_lse_to_label(flow->mpls_lse)));
+ }
+ }
+
/* L3. */
if (flow->dl_type == htons(ETH_TYPE_IP)) {
/* IP. */
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 901dac3..8e351ef 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -18,6 +18,7 @@
#include <arpa/inet.h>
#include "odp-util.h"
#include <errno.h>
+#include <assert.h>
#include <inttypes.h>
#include <math.h>
#include <netinet/in.h>
@@ -106,6 +107,8 @@ ovs_key_attr_to_string(enum ovs_key_attr attr)
case OVS_KEY_ATTR_ARP: return "arp";
case OVS_KEY_ATTR_ND: return "nd";
case OVS_KEY_ATTR_TUN_ID: return "tun_id";
+ case OVS_KEY_ATTR_MPLS: return "mpls";
+ case OVS_KEY_ATTR_INNER_MPLS: return "inner_mpls";
case __OVS_KEY_ATTR_MAX:
default:
@@ -273,6 +276,26 @@ format_vlan_tci(struct ds *ds, ovs_be16 vlan_tci)
}
static void
+format_mpls_lse(struct ds *ds, ovs_be32 mpls_lse)
+{
+ ds_put_format(ds, "label=%"PRIu32",tc=%d,ttl=%d,bos=%d",
+ mpls_lse_to_label(mpls_lse),
+ mpls_lse_to_tc(mpls_lse),
+ mpls_lse_to_ttl(mpls_lse),
+ mpls_lse_to_stack(mpls_lse));
+}
+
+static ovs_be32
+format_mpls_lse_values(int mpls_label, int mpls_tc,
+ int mpls_ttl, int mpls_stack)
+{
+ return(htonl((mpls_label << MPLS_LABEL_SHIFT) |
+ (mpls_tc << MPLS_TC_SHIFT) |
+ (mpls_ttl << MPLS_TTL_SHIFT) |
+ (mpls_stack << MPLS_STACK_SHIFT)));
+}
+
+static void
format_odp_action(struct ds *ds, const struct nlattr *a)
{
int expected_len;
@@ -614,6 +637,8 @@ odp_flow_key_attr_len(uint16_t type)
case OVS_KEY_ATTR_ICMPV6: return sizeof(struct ovs_key_icmpv6);
case OVS_KEY_ATTR_ARP: return sizeof(struct ovs_key_arp);
case OVS_KEY_ATTR_ND: return sizeof(struct ovs_key_nd);
+ case OVS_KEY_ATTR_MPLS: return sizeof(ovs_be32);
+ case OVS_KEY_ATTR_INNER_MPLS: return sizeof(ovs_be32);
case OVS_KEY_ATTR_UNSPEC:
case __OVS_KEY_ATTR_MAX:
@@ -715,6 +740,18 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
ds_put_char(ds, ')');
break;
+ case OVS_KEY_ATTR_MPLS:
+ ds_put_char(ds, '(');
+ format_mpls_lse(ds, nl_attr_get_be32(a));
+ ds_put_char(ds, ')');
+ break;
+
+ case OVS_KEY_ATTR_INNER_MPLS:
+ ds_put_char(ds, '(');
+ format_mpls_lse(ds, nl_attr_get_be32(a));
+ ds_put_char(ds, ')');
+ break;
+
case OVS_KEY_ATTR_ETHERTYPE:
ds_put_format(ds, "(0x%04"PRIx16")",
ntohs(nl_attr_get_be16(a)));
@@ -984,6 +1021,36 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
}
{
+ int mpls_label, mpls_tc, mpls_ttl, mpls_stack;
+ ovs_be32 mpls_lse;
+ int n = -1;
+
+ if (sscanf(s, "mpls(label=%"SCNi32",tc=%i,ttl=%i,bos=%i)%n",
+ &mpls_label, &mpls_tc, &mpls_ttl, &mpls_stack, &n) > 0 &&
+ n > 0) {
+ mpls_lse = format_mpls_lse_values(mpls_label, mpls_tc,
+ mpls_ttl, mpls_stack);
+ nl_msg_put_be32(key, OVS_KEY_ATTR_MPLS, mpls_lse);
+ return n;
+ }
+ }
+
+ {
+ int mpls_label, mpls_tc, mpls_ttl, mpls_stack;
+ ovs_be32 mpls_lse;
+ int n = -1;
+
+ if (sscanf(s, "inner_mpls(label=%"SCNi32",tc=%i,ttl=%i,bos=%i)%n",
+ &mpls_label, &mpls_tc, &mpls_ttl, &mpls_stack, &n) > 0 &&
+ n > 0) {
+ mpls_lse = format_mpls_lse_values(mpls_label, mpls_tc,
+ mpls_ttl, mpls_stack);
+ nl_msg_put_be32(key, OVS_KEY_ATTR_INNER_MPLS, mpls_lse);
+ return n;
+ }
+ }
+
+ {
ovs_be32 ipv4_src;
ovs_be32 ipv4_dst;
int ipv4_proto;
@@ -1291,6 +1358,14 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow)
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, flow->dl_type);
+ if (flow->dl_type == htons(ETH_TYPE_MPLS) ||
+ flow->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ nl_msg_put_be32(buf, OVS_KEY_ATTR_MPLS, flow->mpls_lse);
+ if (flow->inner_mpls_lse != htonl(0)) {
+ nl_msg_put_be32(buf, OVS_KEY_ATTR_INNER_MPLS, flow->inner_mpls_lse);
+ }
+ }
+
if (flow->dl_type == htons(ETH_TYPE_IP)) {
struct ovs_key_ipv4 *ipv4_key;
@@ -1510,6 +1585,20 @@ check_expectations(uint64_t present_attrs, int out_of_range_attr,
return ODP_FIT_PERFECT;
}
+static enum odp_key_fitness
+check_expectations_mpls(uint64_t present_attrs, int out_of_range_attr,
+ uint64_t expected_attrs,
+ const struct nlattr *key, size_t key_len)
+{
+ /* ODP_FIT_TOO_LITTLE to force slow path as
+ * datapath is not aware of MPLS matches yet */
+ return MAX(ODP_FIT_TOO_LITTLE, check_expectations(present_attrs,
+ out_of_range_attr,
+ expected_attrs,
+ key, key_len));
+}
+
+
static bool
parse_ethertype(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
uint64_t present_attrs, uint64_t *expected_attrs,
@@ -1533,13 +1622,93 @@ parse_ethertype(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
static enum odp_key_fitness
parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
+ uint64_t present_attrs, int out_of_range_attr,
+ uint64_t expected_attrs, struct flow *flow,
+ const struct nlattr *key, size_t key_len);
+
+/* Parse MPLS header attributes. */
+static enum odp_key_fitness
+parse_mpls_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
+ uint64_t present_attrs, int out_of_range_attr,
+ uint64_t expected_attrs, struct flow *flow,
+ const struct nlattr *key, size_t key_len)
+{
+ enum odp_key_fitness fitness, encap_fitness;
+ ovs_be32 mpls_lse;
+ ovs_be16 dl_type;
+
+ /* Calulate fitness of outer attributes. */
+ expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
+ fitness = check_expectations(present_attrs, out_of_range_attr,
+ expected_attrs, key, key_len);
+
+ /* Get the MPLS LSE value. */
+ if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS))) {
+ return ODP_FIT_TOO_LITTLE;
+ }
+ mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
+ if (mpls_lse == htonl(0)) {
+ /* Corner case for a truncated MPLS header. */
+ return fitness;
+ }
+
+ /* Set mpls_lse. */
+ flow->mpls_lse = mpls_lse;
+
+ /* Handle Inner MPLS lse. */
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_INNER_MPLS)) {
+ expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_INNER_MPLS);
+ fitness = check_expectations_mpls(present_attrs, out_of_range_attr,
+ expected_attrs, key, key_len);
+ mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_INNER_MPLS]);
+ if (mpls_lse == htonl(0)) {
+ /* Corner case for a truncated MPLS header. */
+ return MAX(ODP_FIT_TOO_LITTLE, fitness);
+ }
+ /* Set mpls_lse. */
+ flow->inner_mpls_lse = mpls_lse;
+ }
+
+ fitness = check_expectations_mpls(present_attrs, out_of_range_attr,
+ expected_attrs, key, key_len);
+
+ /* Try to guess what the encapsualted ethernet type was
+ * in order to try and fill out the flow more fully */
+ dl_type = flow->dl_type;
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
+ flow->dl_type = htons(ETH_TYPE_IP);
+ } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
+ flow->dl_type = htons(ETH_TYPE_IPV6);
+ } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
+ flow->dl_type = htons(ETH_TYPE_ARP);
+ } else {
+ /* Nothing to work with, abandon hope of further processing */
+ return fitness;
+ }
+ encap_fitness = parse_l3_onward(attrs, present_attrs,
+ out_of_range_attr, expected_attrs,
+ flow, key, key_len);
+ flow->encap_dl_type = flow->dl_type;
+ flow->dl_type = dl_type;
+
+ /* The overall fitness is the worse of the outer and inner attributes. */
+ return MAX(encap_fitness, fitness);
+}
+
+static enum odp_key_fitness
+parse_l3_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
uint64_t present_attrs, int out_of_range_attr,
uint64_t expected_attrs, struct flow *flow,
const struct nlattr *key, size_t key_len)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ /* Parse MPLS label stack entry */
+ if (flow->dl_type == htons(ETH_TYPE_MPLS) ||
+ flow->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ return parse_mpls_onward(attrs, present_attrs, out_of_range_attr,
+ expected_attrs, flow, key, key_len);
+ } else if (flow->dl_type == htons(ETH_TYPE_IP)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
const struct ovs_key_ipv4 *ipv4_key;
@@ -1709,8 +1878,10 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow)) {
return ODP_FIT_ERROR;
}
- encap_fitness = parse_l3_onward(attrs, present_attrs, out_of_range_attr,
- expected_attrs, flow, key, key_len);
+
+ encap_fitness = parse_l3_onward(attrs, present_attrs,
+ out_of_range_attr, expected_attrs,
+ flow, key, key_len);
/* The overall fitness is the worse of the outer and inner attributes. */
return MAX(fitness, encap_fitness);
@@ -1789,6 +1960,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
expected_attrs, flow, key, key_len);
}
+
return parse_l3_onward(attrs, present_attrs, out_of_range_attr,
expected_attrs, flow, key, key_len);
}
@@ -1955,7 +2127,6 @@ commit_set_ipv6_action(const struct flow *flow, struct flow *base,
commit_set_action(odp_actions, OVS_KEY_ATTR_IPV6,
&ipv6_key, sizeof(ipv6_key));
}
-
static void
commit_set_nw_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions)
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index a58f8db..4d26105 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -378,6 +378,50 @@ ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code,
case OFPUTIL_NXAST_CONTROLLER:
controller_from_openflow((const struct nx_action_controller *) a, out);
break;
+
+ case OFPUTIL_NXAST_COPY_TTL_OUT:
+ ofpact_put_COPY_TTL_OUT(out);
+ break;
+
+ case OFPUTIL_NXAST_COPY_TTL_IN:
+ ofpact_put_COPY_TTL_IN(out);
+ break;
+
+ case OFPUTIL_NXAST_SET_MPLS_LABEL: {
+ struct nx_action_mpls_label *nxaml =
+ (struct nx_action_mpls_label *)a;
+ ofpact_put_SET_MPLS_LABEL(out)->mpls_label = nxaml->mpls_label;
+ break;
+ }
+
+ case OFPUTIL_NXAST_SET_MPLS_TC: {
+ struct nx_action_mpls_tc *nxamt = (struct nx_action_mpls_tc *)a;
+ ofpact_put_SET_MPLS_TC(out)->mpls_tc = nxamt->mpls_tc;
+ break;
+ }
+
+ case OFPUTIL_NXAST_SET_MPLS_TTL: {
+ struct nx_action_mpls_ttl *nxamt = (struct nx_action_mpls_ttl *)a;
+ ofpact_put_SET_MPLS_TTL(out)->mpls_ttl = nxamt->mpls_ttl;
+ break;
+ }
+
+ case OFPUTIL_NXAST_DEC_MPLS_TTL: {
+ ofpact_put_DEC_MPLS_TTL(out);
+ break;
+ }
+
+ case OFPUTIL_NXAST_PUSH_MPLS: {
+ struct nx_action_push *nxap = (struct nx_action_push *)a;
+ ofpact_put_PUSH_MPLS(out)->ethertype = nxap->ethertype;
+ break;
+ }
+
+ case OFPUTIL_NXAST_POP_MPLS: {
+ struct nx_action_pop_mpls *nxapm = (struct nx_action_pop_mpls *)a;
+ ofpact_put_POP_MPLS(out)->ethertype = nxapm->ethertype;
+ break;
+ }
}
return error;
@@ -722,6 +766,51 @@ ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out)
return nxm_reg_load_from_openflow12_set_field(
(const struct ofp12_action_set_field *)a, out);
+ case OFPUTIL_OFPAT11_SET_MPLS_LABEL: {
+ struct ofp11_action_mpls_label *oaml =
+ (struct ofp11_action_mpls_label *)a;
+ ofpact_put_SET_MPLS_LABEL(out)->mpls_label = oaml->mpls_label;
+ break;
+ }
+
+ case OFPUTIL_OFPAT11_SET_MPLS_TC: {
+ struct ofp11_action_mpls_tc *oamt = (struct ofp11_action_mpls_tc *)a;
+ ofpact_put_SET_MPLS_TC(out)->mpls_tc = oamt->mpls_tc;
+ break;
+ }
+
+ case OFPUTIL_OFPAT11_SET_MPLS_TTL: {
+ struct ofp11_action_mpls_ttl *oasmt =
+ (struct ofp11_action_mpls_ttl *)a;
+ ofpact_put_SET_MPLS_TTL(out)->mpls_ttl = oasmt->mpls_ttl;
+ break;
+ }
+
+ case OFPUTIL_OFPAT11_DEC_MPLS_TTL:
+ ofpact_put_DEC_MPLS_TTL(out);
+ break;
+
+ case OFPUTIL_OFPAT11_PUSH_MPLS: {
+ struct ofp11_action_push *oap = (struct ofp11_action_push *)a;
+ ofpact_put_PUSH_MPLS(out)->ethertype = oap->ethertype;
+ break;
+ }
+
+ case OFPUTIL_OFPAT11_POP_MPLS: {
+ struct ofp11_action_pop_mpls *oapm = (struct ofp11_action_pop_mpls *)a;
+ ofpact_put_POP_MPLS(out)->ethertype = oapm->ethertype;
+ break;
+ }
+
+ case OFPUTIL_OFPAT11_COPY_TTL_OUT:
+ ofpact_put_COPY_TTL_OUT(out);
+ break;
+
+ case OFPUTIL_OFPAT11_COPY_TTL_IN:
+ ofpact_put_COPY_TTL_IN(out);
+ break;
+
+
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
#include "ofp-util.def"
return ofpact_from_nxast(a, code, out);
@@ -1040,6 +1129,54 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports)
case OFPACT_EXIT:
return 0;
+ case OFPACT_PUSH_MPLS: {
+ ovs_be16 etype = ofpact_get_PUSH_MPLS(a)->ethertype;
+ if (etype != htons(ETH_TYPE_MPLS) &&
+ etype != htons(ETH_TYPE_MPLS_MCAST)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ return 0;
+ }
+
+ case OFPACT_POP_MPLS: {
+ ovs_be16 etype = ofpact_get_POP_MPLS(a)->ethertype;
+ if (etype == htons(ETH_TYPE_MPLS) ||
+ etype == htons(ETH_TYPE_MPLS_MCAST)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ return 0;
+ }
+
+ case OFPACT_SET_MPLS_LABEL: {
+ ovs_be32 mpls_label = ofpact_get_SET_MPLS_LABEL(a)->mpls_label;
+ if (mpls_label & ~htonl(MPLS_LABEL_MASK >> MPLS_LABEL_SHIFT)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ return 0;
+ }
+
+ case OFPACT_SET_MPLS_TC: {
+ uint8_t mpls_tc = ofpact_get_SET_MPLS_TC(a)->mpls_tc;
+ if (mpls_tc & ~(MPLS_TC_MASK >> MPLS_TC_SHIFT)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ return 0;
+ }
+
+ case OFPACT_SET_MPLS_TTL: {
+ uint8_t mpls_ttl = ofpact_get_SET_MPLS_TTL(a)->mpls_ttl;
+ if (mpls_ttl == 0 || mpls_ttl == 1) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ return 0;
+ }
+
+ case OFPACT_COPY_TTL_OUT:
+ case OFPACT_COPY_TTL_IN:
+ case OFPACT_DEC_MPLS_TTL:
+ return 0;
+
+
default:
NOT_REACHED();
}
@@ -1243,6 +1380,43 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
ofputil_put_NXAST_EXIT(out);
break;
+ case OFPACT_SET_MPLS_LABEL:
+ ofputil_put_NXAST_SET_MPLS_LABEL(out)->mpls_label =
+ ofpact_get_SET_MPLS_LABEL(a)->mpls_label;
+ break;
+
+ case OFPACT_SET_MPLS_TC:
+ ofputil_put_NXAST_SET_MPLS_TC(out)->mpls_tc =
+ ofpact_get_SET_MPLS_TC(a)->mpls_tc;
+ break;
+
+ case OFPACT_SET_MPLS_TTL:
+ ofputil_put_NXAST_SET_MPLS_TTL(out)->mpls_ttl =
+ ofpact_get_SET_MPLS_TTL(a)->mpls_ttl;
+ break;
+
+ case OFPACT_DEC_MPLS_TTL:
+ ofputil_put_NXAST_DEC_MPLS_TTL(out);
+ break;
+
+ case OFPACT_PUSH_MPLS:
+ ofputil_put_NXAST_PUSH_MPLS(out)->ethertype =
+ ofpact_get_PUSH_MPLS(a)->ethertype;
+ break;
+
+ case OFPACT_POP_MPLS:
+ ofputil_put_NXAST_POP_MPLS(out)->ethertype =
+ ofpact_get_POP_MPLS(a)->ethertype;
+ break;
+
+ case OFPACT_COPY_TTL_OUT:
+ ofputil_put_NXAST_COPY_TTL_OUT(out);
+ break;
+
+ case OFPACT_COPY_TTL_IN:
+ ofputil_put_NXAST_COPY_TTL_IN(out);
+ break;
+
case OFPACT_OUTPUT:
case OFPACT_ENQUEUE:
case OFPACT_SET_VLAN_VID:
@@ -1360,6 +1534,14 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
case OFPACT_AUTOPATH:
case OFPACT_NOTE:
case OFPACT_EXIT:
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
+ case OFPACT_SET_MPLS_TTL:
+ case OFPACT_DEC_MPLS_TTL:
+ case OFPACT_PUSH_MPLS:
+ case OFPACT_POP_MPLS:
+ case OFPACT_COPY_TTL_OUT:
+ case OFPACT_COPY_TTL_IN:
ofpact_to_nxast(a, out);
break;
}
@@ -1452,6 +1634,33 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
= htons(ofpact_get_SET_L4_DST_PORT(a)->port);
break;
+ case OFPACT_SET_MPLS_TTL:
+ ofputil_put_OFPAT11_SET_MPLS_TTL(out)->mpls_ttl =
+ ofpact_get_SET_MPLS_TTL(a)->mpls_ttl;
+ break;
+
+ case OFPACT_DEC_MPLS_TTL:
+ ofputil_put_OFPAT11_DEC_MPLS_TTL(out);
+ break;
+
+ case OFPACT_PUSH_MPLS:
+ ofputil_put_OFPAT11_PUSH_MPLS(out)->ethertype =
+ ofpact_get_PUSH_MPLS(a)->ethertype;
+ break;
+
+ case OFPACT_POP_MPLS:
+ ofputil_put_OFPAT11_POP_MPLS(out)->ethertype =
+ ofpact_get_POP_MPLS(a)->ethertype;
+ break;
+
+ case OFPACT_COPY_TTL_OUT:
+ ofputil_put_OFPAT11_COPY_TTL_OUT(out);
+ break;
+
+ case OFPACT_COPY_TTL_IN:
+ ofputil_put_OFPAT11_COPY_TTL_IN(out);
+ break;
+
case OFPACT_CONTROLLER:
case OFPACT_OUTPUT_REG:
case OFPACT_BUNDLE:
@@ -1468,6 +1677,8 @@ ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out)
case OFPACT_AUTOPATH:
case OFPACT_NOTE:
case OFPACT_EXIT:
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
ofpact_to_nxast(a, out);
break;
}
@@ -1549,6 +1760,15 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port)
case OFPACT_AUTOPATH:
case OFPACT_NOTE:
case OFPACT_EXIT:
+ case OFPACT_COPY_TTL_OUT:
+ case OFPACT_COPY_TTL_IN:
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
+ case OFPACT_SET_MPLS_TTL:
+ case OFPACT_DEC_MPLS_TTL:
+ case OFPACT_PUSH_MPLS:
+ case OFPACT_POP_MPLS:
+
default:
return false;
}
@@ -1639,6 +1859,13 @@ ofpact_format(const struct ofpact *a, struct ds *s)
const struct ofpact_controller *controller;
const struct ofpact_tunnel *tunnel;
uint16_t port;
+#if 0
+ const struct nx_action_mpls_label *naml;
+ const struct nx_action_push_mpls *nampush;
+ const struct nx_action_pop_mpls *nampop;
+ const struct nx_action_mpls_tc *namtc;
+ const struct nx_action_mpls_ttl *namttl;
+#endif
switch (a->type) {
case OFPACT_OUTPUT:
@@ -1812,6 +2039,44 @@ ofpact_format(const struct ofpact *a, struct ds *s)
print_note(ofpact_get_NOTE(a), s);
break;
+ case OFPACT_COPY_TTL_OUT:
+ ds_put_cstr(s, "copy_ttl_out");
+ break;
+
+ case OFPACT_COPY_TTL_IN:
+ ds_put_cstr(s, "copy_ttl_in");
+ break;
+
+ case OFPACT_SET_MPLS_LABEL:
+ ds_put_format(s, "set_mpls_label:%"PRIu32,
+ ntohl(ofpact_get_SET_MPLS_LABEL(a)->mpls_label));
+ break;
+
+ case OFPACT_SET_MPLS_TC:
+ ds_put_format(s, "set_mpls_tc:%"PRIu8,
+ ofpact_get_SET_MPLS_TC(a)->mpls_tc);
+ break;
+
+ case OFPACT_SET_MPLS_TTL:
+ ds_put_format(s, "set_mpls_ttl:%"PRIu8,
+ ofpact_get_SET_MPLS_TTL(a)->mpls_ttl);
+ break;
+
+ case OFPACT_DEC_MPLS_TTL:
+ ds_put_cstr(s, "dec_mpls_ttl");
+ break;
+
+ case OFPACT_PUSH_MPLS:
+ ds_put_format(s, "push_mpls:0x%04"PRIx16,
+ ntohs(ofpact_get_PUSH_MPLS(a)->ethertype));
+ break;
+
+ case OFPACT_POP_MPLS:
+ ds_put_format(s, "pop_mpls:0x%04"PRIx16,
+ ntohs(ofpact_get_POP_MPLS(a)->ethertype));
+ break;
+
+
case OFPACT_EXIT:
ds_put_cstr(s, "exit");
break;
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index fd53e62..c372918 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -70,6 +70,14 @@
DEFINE_OFPACT(REG_MOVE, ofpact_reg_move, ofpact) \
DEFINE_OFPACT(REG_LOAD, ofpact_reg_load, ofpact) \
DEFINE_OFPACT(DEC_TTL, ofpact_cnt_ids, cnt_ids) \
+ DEFINE_OFPACT(COPY_TTL_OUT, ofpact_null, ofpact) \
+ DEFINE_OFPACT(COPY_TTL_IN, ofpact_null, ofpact) \
+ DEFINE_OFPACT(SET_MPLS_LABEL, ofpact_mpls_label, ofpact) \
+ DEFINE_OFPACT(SET_MPLS_TC, ofpact_mpls_tc, ofpact) \
+ DEFINE_OFPACT(SET_MPLS_TTL, ofpact_mpls_ttl, ofpact) \
+ DEFINE_OFPACT(DEC_MPLS_TTL, ofpact_null, ofpact) \
+ DEFINE_OFPACT(PUSH_MPLS, ofpact_push, ofpact) \
+ DEFINE_OFPACT(POP_MPLS, ofpact_pop_mpls, ofpact) \
\
/* Metadata. */ \
DEFINE_OFPACT(SET_TUNNEL, ofpact_tunnel, ofpact) \
@@ -302,6 +310,54 @@ struct ofpact_reg_load {
union mf_subvalue subvalue; /* Least-significant bits are used. */
};
+/* OFPACT_SET_MPLS_LABEL
+ *
+ * used for NXAST_SET_MPLS_LABEL */
+struct ofpact_mpls_label {
+ struct ofpact ofpact;
+ ovs_be32 mpls_label; /* lower 20 bits */
+};
+
+/* OFPACT_SET_MPLS_TC
+ *
+ * used for NXAST_SET_MPLS_TC */
+struct ofpact_mpls_tc {
+ struct ofpact ofpact;
+ uint8_t mpls_tc; /* lower 3 bits */
+};
+
+/* OFPACT_SET_MPLS_STACK
+ *
+ * used for NXAST_SET_MPLS_STACK */
+struct ofpact_mpls_stack {
+ struct ofpact ofpact;
+ uint8_t mpls_stack; /* lower 1 bits */
+};
+
+/* OFPACT_SET_MPLS_TTL
+ *
+ * used for NXAST_SET_MPLS_TTL */
+struct ofpact_mpls_ttl {
+ struct ofpact ofpact;
+ uint8_t mpls_ttl; /* 8 bits */
+};
+
+/* OFPACT_PUSH_VLAN/MPLS/PBB
+ *
+ * used for NXAST_PUSH_MPLS, OFPAT13_PUSH_VLAN/MPSL/BPP */
+struct ofpact_push {
+ struct ofpact ofpact;
+ ovs_be16 ethertype;
+};
+
+/* OFPACT_POP_MPLS
+ *
+ * used for NXAST_POP_MPLS */
+struct ofpact_pop_mpls {
+ struct ofpact ofpact;
+ ovs_be16 ethertype;
+};
+
/* OFPACT_SET_TUNNEL.
*
* Used for NXAST_SET_TUNNEL, NXAST_SET_TUNNEL64. */
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 2d2daa4..2140e5f 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -517,6 +517,48 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
case OFPUTIL_NXAST_CONTROLLER:
parse_controller(ofpacts, arg);
break;
+
+ case OFPUTIL_OFPAT11_COPY_TTL_OUT:
+ case OFPUTIL_NXAST_COPY_TTL_OUT:
+ ofpact_put_COPY_TTL_OUT(ofpacts);
+ break;
+
+ case OFPUTIL_OFPAT11_COPY_TTL_IN:
+ case OFPUTIL_NXAST_COPY_TTL_IN:
+ ofpact_put_COPY_TTL_IN(ofpacts);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_MPLS_LABEL:
+ case OFPUTIL_NXAST_SET_MPLS_LABEL:
+ ofpact_put_SET_MPLS_LABEL(ofpacts)->mpls_label = htonl(str_to_u32(arg));
+ break;
+
+ case OFPUTIL_OFPAT11_SET_MPLS_TC:
+ case OFPUTIL_NXAST_SET_MPLS_TC:
+ ofpact_put_SET_MPLS_TC(ofpacts)->mpls_tc = str_to_u32(arg);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_MPLS_TTL:
+ case OFPUTIL_NXAST_SET_MPLS_TTL:
+ ofpact_put_SET_MPLS_TTL(ofpacts)->mpls_ttl = str_to_u32(arg);
+ break;
+
+ case OFPUTIL_OFPAT11_DEC_MPLS_TTL:
+ case OFPUTIL_NXAST_DEC_MPLS_TTL:
+ ofpact_put_DEC_MPLS_TTL(ofpacts);
+ break;
+
+ case OFPUTIL_OFPAT11_PUSH_MPLS:
+ case OFPUTIL_NXAST_PUSH_MPLS:
+ ofpact_put_PUSH_MPLS(ofpacts)->ethertype =
+ htons(str_to_u16(arg, "push_mpls"));
+ break;
+
+ case OFPUTIL_OFPAT11_POP_MPLS:
+ case OFPUTIL_NXAST_POP_MPLS:
+ ofpact_put_POP_MPLS(ofpacts)->ethertype =
+ htons(str_to_u16(arg, "pop_mpls"));
+ break;
}
}
@@ -574,6 +616,8 @@ parse_protocol(const char *name, const struct protocol **p_out)
{ "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 },
{ "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP },
{ "udp6", ETH_TYPE_IPV6, IPPROTO_UDP },
+ { "mpls", ETH_TYPE_MPLS, 0 },
+ { "mplsm", ETH_TYPE_MPLS_MCAST, 0 },
};
const struct protocol *p;
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 6789625..5cee9ae 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -617,6 +617,10 @@ ofp10_match_to_string(const struct ofp10_match *om, int verbosity)
}
} else if (om->dl_type == htons(ETH_TYPE_ARP)) {
ds_put_cstr(&f, "arp,");
+ } else if (om->dl_type == htons(ETH_TYPE_MPLS)) {
+ ds_put_cstr(&f, "mpls,");
+ } else if (om->dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ ds_put_cstr(&f, "mplsm,");
} else {
skip_type = false;
}
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 366dcae..64fa786 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -84,7 +84,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
void
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
/* Initialize most of wc. */
flow_wildcards_init_catchall(wc);
@@ -139,7 +139,7 @@ ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch,
uint32_t ofpfw = ntohl(ofmatch->wildcards) & OFPFW10_ALL;
/* Initialize match->wc. */
- memset(match->flow.zeros, 0, sizeof match->flow.zeros);
+ flow_zero_pad(&match->flow);
ofputil_wildcard_from_ofpfw10(ofpfw, &match->wc);
/* Initialize most of match->flow. */
@@ -900,7 +900,7 @@ ofputil_usable_protocols(const struct match *match)
{
const struct flow_wildcards *wc = &match->wc;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
/* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */
if (!eth_mask_is_exact(wc->masks.dl_src)
@@ -969,6 +969,21 @@ ofputil_usable_protocols(const struct match *match)
return OFPUTIL_P_NXM_ANY;
}
+ /* NXM and OF1.1+ support matching MPLS label */
+ if (wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK)) {
+ return OFPUTIL_P_NXM_ANY;
+ }
+
+ /* NXM and OF1.1+ support matching MPLS TC */
+ if (wc->masks.mpls_lse & htonl(MPLS_TC_MASK)) {
+ return OFPUTIL_P_NXM_ANY;
+ }
+
+ /* NXM and OF1.1+ support matching MPLS stack flag */
+ if (wc->masks.mpls_lse & htonl(MPLS_STACK_MASK)) {
+ return OFPUTIL_P_NXM_ANY;
+ }
+
/* Other formats can express this rule. */
return OFPUTIL_P_ANY;
}
@@ -3753,7 +3768,8 @@ ofputil_normalize_match__(struct match *match, bool may_log)
MAY_ARP_SHA = 1 << 4, /* arp_sha */
MAY_ARP_THA = 1 << 5, /* arp_tha */
MAY_IPV6 = 1 << 6, /* ipv6_src, ipv6_dst, ipv6_label */
- MAY_ND_TARGET = 1 << 7 /* nd_target */
+ MAY_ND_TARGET = 1 << 7, /* nd_target */
+ MAY_MPLS = 1 << 8, /* mpls label and tc */
} may_match;
struct flow_wildcards wc;
@@ -3781,6 +3797,9 @@ ofputil_normalize_match__(struct match *match, bool may_log)
}
} else if (match->flow.dl_type == htons(ETH_TYPE_ARP)) {
may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
+ } else if (match->flow.dl_type == htons(ETH_TYPE_MPLS) ||
+ match->flow.dl_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ may_match = MAY_MPLS;
} else {
may_match = 0;
}
@@ -3813,6 +3832,9 @@ ofputil_normalize_match__(struct match *match, bool may_log)
if (!(may_match & MAY_ND_TARGET)) {
wc.masks.nd_target = in6addr_any;
}
+ if (!(may_match & MAY_MPLS)) {
+ wc.masks.mpls_lse = htonl(0);
+ }
/* Log any changes. */
if (!flow_wildcards_equal(&wc, &match->wc)) {
diff --git a/lib/ofp-util.def b/lib/ofp-util.def
index 4d451b0..c15c389 100644
--- a/lib/ofp-util.def
+++ b/lib/ofp-util.def
@@ -30,8 +30,16 @@ OFPAT11_ACTION(OFPAT11_SET_NW_TOS, ofp_action_nw_tos, 0, "mod_nw_tos")
//OFPAT11_ACTION(OFPAT11_SET_NW_ECN, ofp11_action_nw_ecn, "0, mod_nw_ecn")
OFPAT11_ACTION(OFPAT11_SET_TP_SRC, ofp_action_tp_port, 0, "mod_tp_src")
OFPAT11_ACTION(OFPAT11_SET_TP_DST, ofp_action_tp_port, 0, "mod_tp_dst")
+OFPAT11_ACTION(OFPAT11_COPY_TTL_OUT, ofp_action_header, 0, "copy_ttl_out")
+OFPAT11_ACTION(OFPAT11_COPY_TTL_IN, ofp_action_header, 0, "copy_ttl_in")
+OFPAT11_ACTION(OFPAT11_SET_MPLS_LABEL, ofp11_action_mpls_label, 0, "set_mpls_label")
+OFPAT11_ACTION(OFPAT11_SET_MPLS_TC, ofp11_action_mpls_tc, 0, "set_mpls_tc")
+OFPAT11_ACTION(OFPAT11_SET_MPLS_TTL, ofp11_action_mpls_ttl, 0, "set_mpls_ttl")
+OFPAT11_ACTION(OFPAT11_DEC_MPLS_TTL, ofp_action_header, 0, "dec_mpls_ttl")
//OFPAT11_ACTION(OFPAT11_PUSH_VLAN, ofp11_action_push, 0, "push_vlan")
//OFPAT11_ACTION(OFPAT11_POP_VLAN, ofp_action_header, 0, "pop_vlan")
+OFPAT11_ACTION(OFPAT11_PUSH_MPLS, ofp11_action_push, 0, "push_mpls")
+OFPAT11_ACTION(OFPAT11_POP_MPLS, ofp11_action_pop_mpls, 0, "pop_mpls")
//OFPAT11_ACTION(OFPAT11_SET_QUEUE, ofp11_action_set_queue, 0, "set_queue")
//OFPAT11_ACTION(OFPAT11_SET_NW_TTL, ofp11_action_nw_ttl, 0, "set_nw_ttl")
//OFPAT11_ACTION(OFPAT11_DEC_NW_TTL, ofp_action_header, 0, "dec_ttl")
@@ -60,6 +68,14 @@ NXAST_ACTION(NXAST_DEC_TTL, nx_action_header, 0, "dec_ttl")
NXAST_ACTION(NXAST_FIN_TIMEOUT, nx_action_fin_timeout, 0, "fin_timeout")
NXAST_ACTION(NXAST_CONTROLLER, nx_action_controller, 0, "controller")
NXAST_ACTION(NXAST_DEC_TTL_CNT_IDS, nx_action_cnt_ids, 1, NULL)
+NXAST_ACTION(NXAST_COPY_TTL_OUT, nx_action_header, 0, "copy_ttl_out")
+NXAST_ACTION(NXAST_COPY_TTL_IN, nx_action_header, 0, "copy_ttl_in")
+NXAST_ACTION(NXAST_SET_MPLS_LABEL, nx_action_mpls_label, 0, "set_mpls_label")
+NXAST_ACTION(NXAST_SET_MPLS_TC, nx_action_mpls_tc, 0, "set_mpls_tc")
+NXAST_ACTION(NXAST_SET_MPLS_TTL, nx_action_mpls_ttl, 0, "set_mpls_ttl")
+NXAST_ACTION(NXAST_DEC_MPLS_TTL, nx_action_header, 0, "dec_mpls_ttl")
+NXAST_ACTION(NXAST_PUSH_MPLS, nx_action_push, 0, "push_mpls")
+NXAST_ACTION(NXAST_POP_MPLS, nx_action_pop_mpls, 0, "pop_mpls")
#undef OFPAT10_ACTION
#undef OFPAT11_ACTION
diff --git a/lib/ofpbuf.c b/lib/ofpbuf.c
index a7d4c73..9f5e908 100644
--- a/lib/ofpbuf.c
+++ b/lib/ofpbuf.c
@@ -30,7 +30,7 @@ ofpbuf_use__(struct ofpbuf *b, void *base, size_t allocated,
b->allocated = allocated;
b->source = source;
b->size = 0;
- b->l2 = b->l3 = b->l4 = b->l7 = NULL;
+ b->l2 = b->l2_5 = b->l3 = b->l4 = b->l7 = NULL;
list_poison(&b->list_node);
b->private_p = NULL;
}
@@ -177,6 +177,9 @@ ofpbuf_clone_with_headroom(const struct ofpbuf *buffer, size_t headroom)
if (buffer->l2) {
new_buffer->l2 = (char *) buffer->l2 + data_delta;
}
+ if (buffer->l2_5) {
+ new_buffer->l2_5 = (char *) buffer->l2_5 + data_delta;
+ }
if (buffer->l3) {
new_buffer->l3 = (char *) buffer->l3 + data_delta;
}
@@ -296,6 +299,9 @@ ofpbuf_resize__(struct ofpbuf *b, size_t new_headroom, size_t new_tailroom)
if (b->l2) {
b->l2 = (char *) b->l2 + data_delta;
}
+ if (b->l2_5) {
+ b->l2_5 = (char *) b->l2_5 + data_delta;
+ }
if (b->l3) {
b->l3 = (char *) b->l3 + data_delta;
}
diff --git a/lib/ofpbuf.h b/lib/ofpbuf.h
index 520455d..bae3c0a 100644
--- a/lib/ofpbuf.h
+++ b/lib/ofpbuf.h
@@ -43,6 +43,7 @@ struct ofpbuf {
size_t size; /* Number of bytes in use. */
void *l2; /* Link-level header. */
+ void *l2_5; /* MPLS label stack */
void *l3; /* Network-level header. */
void *l4; /* Transport-level header. */
void *l7; /* Application data. */
diff --git a/lib/packets.c b/lib/packets.c
index 16f4fe6..c0f2142 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -20,6 +20,7 @@
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <netinet/ip6.h>
#include <stdlib.h>
#include "byte-order.h"
#include "csum.h"
@@ -211,6 +212,361 @@ eth_pop_vlan(struct ofpbuf *packet)
}
}
+/* Set ethertype of the packet. */
+void
+set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type)
+{
+ struct eth_header *eh = packet->data;
+
+ if (eh->eth_type == htons(ETH_TYPE_VLAN)) {
+ /* ethtype for VLAN packets is at L3_offset - 2 bytes. */
+ ovs_be16 *next_ethtype;
+ next_ethtype = (ovs_be16 *)((char *)packet->l3 - 2);
+ *next_ethtype = eth_type;
+ } else {
+ eh->eth_type = eth_type;
+ }
+}
+
+/* Get ethertype of the packet. */
+static ovs_be16
+get_ethertype(struct ofpbuf *packet)
+{
+ struct eth_header *eh = packet->data;
+ char *mh = packet->l2_5;
+ ovs_be16 *ethtype = NULL;
+
+ if (eh->eth_type == htons(ETH_TYPE_VLAN)) {
+ if (mh != NULL) {
+ ethtype = (ovs_be16 *)(mh - 2);
+ } else {
+ ethtype = (ovs_be16 *)((char *)packet->l3 - 2);
+ }
+ return *ethtype;
+ } else {
+ return eh->eth_type;
+ }
+}
+
+/* Extract ttl and tos from ipv4 or ipv6 header
+ for non-IP pick default value. */
+static int
+get_label_ttl_and_tc(struct ofpbuf* packet, uint8_t *ttl,
+ uint8_t *tc, uint8_t *label)
+{
+ struct eth_header *eh = packet->data;
+
+ if (packet->size < sizeof *eh) {
+ return -1;
+ }
+
+ switch (ntohs(get_ethertype(packet))) {
+ case ETH_TYPE_IP: {
+ struct ip_header *ih = packet->l3;
+
+ if (packet->size < sizeof *eh + sizeof *ih) {
+ return -1;
+ }
+ *ttl = ih->ip_ttl;
+ *tc = 0; /* As per OpenFlow 1.1 Spec. */
+ *label = 0; /* IPV4 Explicit null label. */
+ break;
+ }
+
+ case ETH_TYPE_IPV6: {
+ struct ip6_hdr *ih6 = packet->l3;
+
+ if (packet->size < sizeof *eh + sizeof *ih6) {
+ return -1;
+ }
+ *ttl = ih6->ip6_hlim;
+ *tc = 0; /* As per OpenFlow 1.1 Spec. */
+ *label = 2; /* IPV6 Explicit null label. */
+ break;
+ }
+
+ case ETH_TYPE_MPLS:
+ case ETH_TYPE_MPLS_MCAST: {
+ struct mpls_hdr *mh = packet->l2_5;
+
+ if (packet->size < sizeof *eh + sizeof *mh) {
+ return -1;
+ }
+ *ttl = mpls_lse_to_ttl(mh->mpls_lse);
+ *tc = mpls_lse_to_tc(mh->mpls_lse);
+ *label = mpls_lse_to_label(mh->mpls_lse);
+ break;
+ }
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Set MPLS tag time-to-live. */
+static void
+set_mpls_lse_ttl(ovs_be32 *tag, ovs_be32 ttl)
+{
+ *tag &= ~htonl(MPLS_TTL_MASK);
+ *tag |= ttl & htonl(MPLS_TTL_MASK);
+}
+
+/* Set MPLS tag traffic-class. */
+static void
+set_mpls_lse_tc(ovs_be32 *tag, ovs_be32 tc)
+{
+ *tag &= ~htonl(MPLS_TC_MASK);
+ *tag |= tc & htonl(MPLS_TC_MASK);
+}
+
+/* Set MPLS tag label. */
+static void
+set_mpls_lse_label(ovs_be32 *tag, ovs_be32 label)
+{
+ *tag &= ~htonl(MPLS_LABEL_MASK);
+ *tag |= label & htonl(MPLS_LABEL_MASK);
+}
+
+/* Set MPLS tag stack. */
+static void
+set_mpls_lse_stack(ovs_be32 *tag, ovs_be32 stack)
+{
+ *tag &= ~htonl(MPLS_STACK_MASK);
+ *tag |= stack & htonl(MPLS_STACK_MASK);
+}
+
+/* Set MPLS lse from actions. */
+static void
+set_new_mpls_lse(struct mpls_hdr *mh, ovs_be32 mpls_lse)
+{
+ mh->mpls_lse = mpls_lse;
+}
+
+/* Set MPLS label, MPLS TC, MPLS ttl and MPLS stack. */
+static void
+set_mpls_lse_values(ovs_be32 *tag, uint8_t ttl, uint8_t stack,
+ uint8_t tc, uint32_t label)
+{
+ set_mpls_lse_ttl(tag, htonl(ttl << MPLS_TTL_SHIFT));
+ set_mpls_lse_tc(tag, htonl(tc << MPLS_TC_SHIFT));
+ set_mpls_lse_label(tag, htonl(label << MPLS_LABEL_SHIFT));
+ set_mpls_lse_stack(tag, htonl(stack << MPLS_STACK_SHIFT));
+}
+
+/* Adjust L2 and L2.5 data after pushing new mpls shim header. */
+static void
+push_mpls_lse(struct ofpbuf *packet, struct mpls_hdr *mh)
+{
+ char * header;
+ size_t len;
+ header = ofpbuf_push_uninit(packet, MPLS_HLEN);
+ len = (char *)packet->l2_5 - (char *)packet->l2;
+ memmove(header, packet->l2, len);
+ memcpy(header + len, mh, sizeof *mh);
+ packet->l2 = (char*)packet->l2 - MPLS_HLEN;
+ packet->l2_5 = (char*)packet->l2_5 - MPLS_HLEN;
+}
+
+/* Decrement MPLS TTL from the packet.
+ * 'packet->l2_5' must initially point to 'packet''s MPLS Label stack. */
+void
+dec_mpls_ttl(struct ofpbuf *packet, uint8_t new_ttl)
+{
+ ovs_be16 eth_type = htons(0);
+ struct eth_header *eh = packet->data;
+ struct mpls_hdr *mh = packet->l2_5;
+
+ if (packet->size < sizeof *eh) {
+ return;
+ }
+
+ /* Packet type should be mpls to decrement ttl. */
+ eth_type = get_ethertype(packet);
+
+ if (eth_type == htons(ETH_TYPE_MPLS) ||
+ eth_type == htons(ETH_TYPE_MPLS_MCAST)) {
+
+ /* Update decremented ttl into mpls header. */
+ set_mpls_lse_ttl(&mh->mpls_lse, htonl(new_ttl << MPLS_TTL_SHIFT));
+ }
+}
+
+/* Copy MPLS TTL from the packet either ipv4/ipv6.
+ * 'packet->l2_5' must initially point to 'packet''s MPLS Label stack. */
+void
+copy_mpls_ttl_in(struct ofpbuf *packet, uint8_t new_ttl)
+{
+ struct eth_header *eh = packet->data;
+ struct mpls_hdr *mh = packet->l2_5;
+ struct ip_header *ih = packet->l3;
+ struct ip6_hdr *ih6 = packet->l3;
+ ovs_be16 eth_type = htons(0);
+ size_t hdr_size = sizeof *eh + sizeof *mh + sizeof *ih;
+
+ if (packet->size < hdr_size) {
+ return;
+ }
+
+ /* Packet type should be mpls to copy ttl to l3. */
+ eth_type = get_ethertype(packet);
+ if (eth_type == htons(ETH_TYPE_MPLS) ||
+ eth_type == htons(ETH_TYPE_MPLS_MCAST)) {
+
+ /* If bottom of the stack handle IP checksum. */
+ if (mh->mpls_lse & htonl(MPLS_STACK_MASK)) {
+ if (IP_VER(ih->ip_ihl_ver) == IP_VERSION) {
+ /* Change the ip checksum. */
+ uint8_t *field = &ih->ip_ttl;
+ ih->ip_csum = recalc_csum16(ih->ip_csum,
+ htons(*field << 8), htons(new_ttl << 8));
+ ih->ip_ttl = new_ttl;
+ } else if (IP6_VER(ih6->ip6_vfc) == IP6_VERSION) {
+ ih6->ip6_hlim = new_ttl;
+ }
+ } else {
+ struct mpls_hdr *mh2;
+ mh2 = (struct mpls_hdr *)((char *) packet->l2_5 + sizeof *mh);
+ set_mpls_lse_ttl(&mh2->mpls_lse, htonl(new_ttl << MPLS_TTL_SHIFT));
+ }
+ }
+}
+
+/* Copy MPLS TTL to the packet layer3 only ipv4/ipv6.
+ * 'packet->l2_5' must initially point to 'packet''s MPLS Label stack. */
+void
+copy_mpls_ttl_out(struct ofpbuf *packet, uint8_t new_ttl)
+{
+ struct eth_header *eh = packet->data;
+ struct mpls_hdr *mh = packet->l2_5;
+ struct ip_header *ih = packet->l3;
+ struct ip6_hdr *ih6 = packet->l3;
+ ovs_be16 eth_type = htons(0);
+ size_t hdr_size = sizeof *eh + sizeof *mh + sizeof *ih;
+
+ /* TTL sent from ofproto-dpif.c is not the correct one,
+ * hence ignore it. */
+ if (packet->size < hdr_size) {
+ return;
+ }
+
+ /* Packet type should me mpls to copy ttl from l3. */
+ eth_type = get_ethertype(packet);
+ if (eth_type == htons(ETH_TYPE_MPLS) ||
+ eth_type == htons(ETH_TYPE_MPLS_MCAST)) {
+
+ /* If bottom of the stack copy from l3. */
+ if (mh->mpls_lse & htonl(MPLS_STACK_MASK)) {
+ uint8_t nh_ttl;
+ /* Get ipv4 or ipv6 or default ttl. */
+ if (IP_VER(ih->ip_ihl_ver) == IP_VERSION) {
+ nh_ttl = ih->ip_ttl;
+ } else if (IP6_VER(ih6->ip6_vfc) == IP6_VERSION) {
+ nh_ttl = ih6->ip6_hlim;
+ } else {
+ nh_ttl = 64; /* Default ttl for non-IP. */
+ }
+ set_mpls_lse_ttl(&mh->mpls_lse, htonl(nh_ttl << MPLS_TTL_SHIFT));
+ } else {
+ struct mpls_hdr *mh2;
+ mh2 = (struct mpls_hdr *)((char *) packet->l2_5 + sizeof *mh);
+ new_ttl = mpls_lse_to_ttl(mh2->mpls_lse);
+ set_mpls_lse_ttl(&mh->mpls_lse, htonl(new_ttl << MPLS_TTL_SHIFT));
+ }
+ }
+}
+
+/* Set MPLS label stack entry to outermost MPLS header.*/
+void
+set_mpls_lse(struct ofpbuf *packet, ovs_be32 mpls_lse)
+{
+ struct eth_header *eh = packet->data;
+ struct mpls_hdr *mh = packet->l2_5;
+ ovs_be16 eth_type = htons(0);
+
+ if (packet->size < sizeof *eh) {
+ return;
+ }
+
+ /* Packet type should me mpls to set label stack entry. */
+ eth_type = get_ethertype(packet);
+ if (eth_type == htons(ETH_TYPE_MPLS) ||
+ eth_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ /* Update mpls label stack entry. */
+ set_new_mpls_lse(mh, mpls_lse);
+ }
+}
+
+/* Push MPLS label stack entry onto packet. */
+void
+push_mpls(struct ofpbuf *packet, ovs_be16 ethtype)
+{
+ struct eth_header *eh = packet->data;
+ uint8_t nh_ttl, mpls_tc, label;
+ ovs_be16 eth_type = htons(0);
+ struct mpls_hdr mh;
+
+ if (packet->size < sizeof *eh ||
+ (ethtype != htons(ETH_TYPE_MPLS) &&
+ ethtype != htons(ETH_TYPE_MPLS_MCAST))) {
+ return;
+ }
+ /* Get Label, time-to-live and tc from L3 or L2.5. */
+ if (get_label_ttl_and_tc(packet, &nh_ttl, &mpls_tc, &label)) {
+ return;
+ }
+
+ /* Get the packet ether_type. */
+ eth_type = get_ethertype(packet);
+
+ if (eth_type == htons(ETH_TYPE_MPLS) ||
+ eth_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ set_mpls_lse_values(&mh.mpls_lse, nh_ttl, 0, mpls_tc, label);
+ } else {
+ /* Set ethtype and mpls label stack entry. */
+ set_ethertype(packet, ethtype);
+ set_mpls_lse_values(&mh.mpls_lse, nh_ttl, 1, mpls_tc, label);
+ packet->l2_5 = packet->l3;
+ }
+ /* Push new MPLS shim header onto packet. */
+ push_mpls_lse(packet, &mh);
+}
+
+/* Pop outermost MPLS label stack entry from packet. */
+void
+pop_mpls(struct ofpbuf *packet, ovs_be16 ethtype)
+{
+ struct eth_header *eh = packet->data;
+ struct mpls_hdr *mh = NULL;
+ ovs_be16 eth_type = htons(0);
+
+ if (packet->size < sizeof *eh + sizeof *mh)
+ return;
+
+ eth_type = get_ethertype(packet);
+
+ if (eth_type == htons(ETH_TYPE_MPLS) ||
+ eth_type == htons(ETH_TYPE_MPLS_MCAST)) {
+ size_t len;
+ mh = packet->l2_5;
+ len = (char*)packet->l2_5 - (char*)packet->l2;
+ /* If bottom of the stack set ethertype. */
+ if (mh->mpls_lse & htonl(MPLS_STACK_MASK)) {
+ packet->l3 = packet->l2_5;
+ packet->l2_5 = NULL;
+ set_ethertype(packet, ethtype);
+ } else {
+ packet->l2_5 = (char*)packet->l2_5 + MPLS_HLEN;
+ }
+ /* Shift the l2 header forward. */
+ memmove((char*)packet->data + MPLS_HLEN, packet->data, len);
+ packet->size -= MPLS_HLEN;
+ packet->data = (char*)packet->data + MPLS_HLEN;
+ packet->l2 = (char*)packet->l2 + MPLS_HLEN;
+ }
+}
+
/* Converts hex digits in 'hex' to an Ethernet packet in '*packetp'. The
* caller must free '*packetp'. On success, returns NULL. On failure, returns
* an error message and stores NULL in '*packetp'. */
diff --git a/lib/packets.h b/lib/packets.h
index 24b51da..f41d3bb 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -140,6 +140,8 @@ void compose_rarp(struct ofpbuf *, const uint8_t eth_src[ETH_ADDR_LEN]);
void eth_push_vlan(struct ofpbuf *, ovs_be16 tci);
void eth_pop_vlan(struct ofpbuf *);
+void set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type);
+
const char *eth_from_hex(const char *hex, struct ofpbuf **packetp);
void eth_format_masked(const uint8_t eth[ETH_ADDR_LEN],
const uint8_t mask[ETH_ADDR_LEN], struct ds *s);
@@ -147,6 +149,15 @@ void eth_addr_bitand(const uint8_t src[ETH_ADDR_LEN],
const uint8_t mask[ETH_ADDR_LEN],
uint8_t dst[ETH_ADDR_LEN]);
+void set_mpls_ttl(struct ofpbuf *, uint8_t ttl);
+void dec_mpls_ttl(struct ofpbuf *, uint8_t new_ttl);
+void copy_mpls_ttl_in(struct ofpbuf *, uint8_t new_ttl);
+void copy_mpls_ttl_out(struct ofpbuf *, uint8_t new_ttl);
+void set_mpls_tc(struct ofpbuf *, uint8_t tc);
+void set_mpls_lse(struct ofpbuf *, ovs_be32 label);
+void push_mpls(struct ofpbuf *packet, ovs_be16 ethtype);
+void pop_mpls(struct ofpbuf *, ovs_be16 ethtype);
+
/* Example:
*
* uint8_t mac[ETH_ADDR_LEN];
@@ -270,6 +281,76 @@ struct vlan_eth_header {
} __attribute__((packed));
BUILD_ASSERT_DECL(VLAN_ETH_HEADER_LEN == sizeof(struct vlan_eth_header));
+/* MPLS related definitions */
+#define MPLS_TTL_MASK 0x000000ff
+#define MPLS_TTL_SHIFT 0
+
+#define MPLS_STACK_MASK 0x00000100
+#define MPLS_STACK_SHIFT 8
+
+#define MPLS_TC_MASK 0x00000e00
+#define MPLS_TC_SHIFT 9
+
+#define MPLS_LABEL_MASK 0xfffff000
+#define MPLS_LABEL_SHIFT 12
+
+#define MPLS_HLEN 4
+
+struct mpls_hdr {
+ ovs_be32 mpls_lse;
+};
+BUILD_ASSERT_DECL(MPLS_HLEN == sizeof(struct mpls_hdr));
+
+#define MPLS_ETH_HEADER_LEN (ETH_HEADER_LEN + MPLS_HLEN)
+struct mpls_eth_header {
+ uint8_t eth_dst[ETH_ADDR_LEN];
+ uint8_t eth_src[ETH_ADDR_LEN];
+ ovs_be16 eth_type; /* htons(ETH_TYPE_MPLS) or
+ htons(ETH_TYPE_MPLS_MCAST). */
+ ovs_be32 mpls_lse;
+} __attribute__((packed));
+BUILD_ASSERT_DECL(MPLS_ETH_HEADER_LEN == sizeof(struct mpls_eth_header));
+
+/* Given a mpls label stack entry in network byte order
+ * return mpls label */
+static inline uint32_t
+mpls_lse_to_label(ovs_be32 mpls_lse)
+{
+ return (ntohl(mpls_lse) & MPLS_LABEL_MASK) >> MPLS_LABEL_SHIFT;
+}
+
+/* Given a mpls label stack entry in network byte order
+ * return mpls tc */
+static inline int
+mpls_lse_to_tc(ovs_be32 mpls_lse)
+{
+ return (ntohl(mpls_lse) & MPLS_TC_MASK) >> MPLS_TC_SHIFT;
+}
+
+/* Given a mpls label stack entry in network byte order
+ * return mpls ttl */
+static inline int
+mpls_lse_to_ttl(ovs_be32 mpls_lse)
+{
+ return (ntohl(mpls_lse) & MPLS_TTL_MASK) >> MPLS_TTL_SHIFT;
+}
+
+/* Set TTL in mpls lse. */
+static inline void
+flow_set_mpls_lse_ttl(ovs_be32 *mpls_lse, uint8_t ttl)
+{
+ *mpls_lse &= ~htonl(MPLS_TTL_MASK);
+ *mpls_lse |= htonl(ttl << MPLS_TTL_SHIFT);
+}
+
+/* Given a mpls label stack entry in network byte order
+ * return mpls stack */
+static inline int
+mpls_lse_to_stack(ovs_be32 mpls_lse)
+{
+ return (mpls_lse & htonl(MPLS_STACK_MASK)) != 0;
+}
+
/* The "(void) (ip)[0]" below has no effect on the value, since it's the first
* argument of a comma expression, but it makes sure that 'ip' is a pointer.
* This is useful since a common mistake is to pass an integer instead of a
@@ -334,6 +415,8 @@ void ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *);
#define IP_VERSION 4
+#define IP_DSCP(ip_tos) ((ip_tos & IP_DSCP_MASK) >> 2)
+
#define IP_DONT_FRAGMENT 0x4000 /* Don't fragment. */
#define IP_MORE_FRAGMENTS 0x2000 /* More fragments. */
#define IP_FRAG_OFF_MASK 0x1fff /* Fragment offset. */
@@ -434,6 +517,11 @@ BUILD_ASSERT_DECL(ARP_ETH_HEADER_LEN == sizeof(struct arp_eth_header));
/* The IPv6 flow label is in the lower 20 bits of the first 32-bit word. */
#define IPV6_LABEL_MASK 0x000fffff
+#define IP6_VERSION 6
+
+#define IP6_VER(ip6_vfc) ((ip6_vfc) >> 4)
+#define IP6_TC(ip6_flow) ((ip6_flow >> 20) & 0xff)
+
/* Example:
*
* char *string = "1 ::1 2";
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 9f7acd1..b045ffb 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -4951,6 +4951,7 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
uint16_t odp_port = ofp_port_to_odp_port(ofp_port);
ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
+ ovs_be32 flow_mpls_lse = ctx->flow.mpls_lse;
uint8_t flow_nw_tos = ctx->flow.nw_tos;
uint16_t out_port;
@@ -4985,6 +4986,7 @@ compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,
ctx->sflow_n_outputs++;
ctx->nf_output_iface = ofp_port;
ctx->flow.vlan_tci = flow_vlan_tci;
+ ctx->flow.mpls_lse = flow_mpls_lse;
ctx->flow.nw_tos = flow_nw_tos;
}
@@ -5132,9 +5134,20 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len,
/* If the Ethernet type is less than ETH_TYPE_MIN, it's likely an 802.2
* LLC frame. Calculating the Ethernet type of these frames is more
- * trouble than seems appropriate for a simple assertion. */
- assert(ntohs(eh->eth_type) < ETH_TYPE_MIN
- || eh->eth_type == ctx->flow.dl_type);
+ * trouble than seems appropriate for a simple assertion.
+ *
+ * There is a gratuitous exception for MPLS intended to handle
+ * the cases where an MPLS push or pull action has occurred and
+ * ctx->flow.dl_type will has been updated and the original
+ * value lost. This could be handled more cleanly, for instance by
+ * saving the original value of ctx->flow.dl_type when pushing the
+ * first MPLS label, but that seems somewhat excessive */
+ assert(ntohs(eh->eth_type) < ETH_TYPE_MIN ||
+ eh->eth_type == ctx->flow.dl_type ||
+ ctx->flow.dl_type == htons(ETH_TYPE_MPLS) ||
+ eh->eth_type == htons(ETH_TYPE_MPLS) ||
+ ctx->flow.dl_type == htons(ETH_TYPE_MPLS_MCAST) ||
+ eh->eth_type == htons(ETH_TYPE_MPLS_MCAST));
memcpy(eh->eth_src, ctx->flow.dl_src, sizeof eh->eth_src);
memcpy(eh->eth_dst, ctx->flow.dl_dst, sizeof eh->eth_dst);
@@ -5143,6 +5156,17 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len,
eth_push_vlan(packet, ctx->flow.vlan_tci);
}
+ if (ctx->flow.mpls_lse) {
+ push_mpls(packet, ctx->flow.dl_type);
+ set_mpls_lse(packet, ctx->flow.mpls_lse);
+ }
+
+ if (!ctx->flow.mpls_lse &&
+ (eh->eth_type == htons(ETH_TYPE_MPLS) ||
+ eh->eth_type == htons(ETH_TYPE_MPLS_MCAST))) {
+ pop_mpls(packet, ctx->flow.dl_type);
+ }
+
if (packet->l4) {
if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
packet_set_ipv4(packet, ctx->flow.nw_src, ctx->flow.nw_dst,
@@ -5175,6 +5199,77 @@ execute_controller_action(struct action_xlate_ctx *ctx, int len,
ofpbuf_delete(packet);
}
+/* Handle MPLS Push action. Update flow based on incoming packet. */
+static void
+commit_mpls_push_action(struct action_xlate_ctx *ctx, ovs_be16 eth_type)
+{
+ ovs_be32 mpls_label, mpls_tc, mpls_ttl, mpls_stack;
+
+ assert(eth_type == htons(ETH_TYPE_MPLS) ||
+ eth_type == htons(ETH_TYPE_MPLS_MCAST));
+
+ if (ctx->base_flow.mpls_lse != htonl(0)) {
+ ctx->flow.mpls_lse = ctx->base_flow.mpls_lse;
+ ctx->flow.mpls_lse &= ~htonl(MPLS_STACK_MASK);
+ } else {
+ ctx->flow.mpls_lse &= ~htonl(MPLS_LABEL_MASK | MPLS_TC_MASK |
+ MPLS_TTL_MASK | MPLS_STACK_MASK);
+ if (ctx->flow.dl_type == htons(ETH_TYPE_IPV6)) {
+ mpls_label = htonl(0x2); /* IPV6 Explicit Null. */
+ } else {
+ mpls_label = htonl(0x0); /* IPV4 Explicit Null. */
+ }
+ mpls_tc = htonl(((ctx->flow.nw_tos & IP_DSCP_MASK) >> 2)
+ << MPLS_TC_SHIFT);
+ mpls_ttl = htonl(ctx->flow.nw_ttl << MPLS_TTL_SHIFT);
+ if (mpls_ttl == htonl(0)) {
+ mpls_ttl = htonl(0x40); /* Set default ttl for non-IP. */
+ }
+ mpls_stack = htonl(0x1 << MPLS_STACK_SHIFT);
+ ctx->flow.mpls_lse = mpls_label | mpls_tc | mpls_ttl | mpls_stack;
+ ctx->flow.encap_dl_type = ctx->base_flow.encap_dl_type =
+ ctx->flow.dl_type;
+ }
+
+ /* Update dl_type and mpls_lse fields. */
+ ctx->base_flow.dl_type = ctx->flow.dl_type = eth_type;
+ ctx->base_flow.mpls_lse = ctx->flow.mpls_lse;
+
+ compose_output_action(ctx, OFPP_LOCAL);
+}
+
+/* Handle MPLS Pop action. Update packet flow. */
+static void
+commit_mpls_pop_action(struct action_xlate_ctx *ctx, ovs_be16 eth_type)
+{
+ assert(ctx->flow.dl_type == htons(ETH_TYPE_MPLS) ||
+ ctx->flow.dl_type == htons(ETH_TYPE_MPLS_MCAST));
+ assert(eth_type != htons(ETH_TYPE_MPLS) &&
+ eth_type != htons(ETH_TYPE_MPLS_MCAST));
+ /* Update dl_type and mpls_lse fields. */
+ if (ctx->flow.mpls_lse & htonl(MPLS_STACK_MASK)) {
+ ctx->base_flow.dl_type = ctx->flow.dl_type = eth_type;
+ ctx->base_flow.encap_dl_type = ctx->flow.encap_dl_type = htons(0);
+ }
+ ctx->base_flow.mpls_lse = htonl(0);
+
+ compose_output_action(ctx, OFPP_LOCAL);
+}
+
+/* Handle MPLS Label Stack Entry action. Update packet flow. */
+static void
+commit_mpls_lse_action(struct action_xlate_ctx *ctx)
+{
+ if (ctx->base_flow.mpls_lse == ctx->flow.mpls_lse) {
+ return;
+ }
+
+ ctx->base_flow.mpls_lse = ctx->flow.mpls_lse;
+
+ compose_output_action(ctx, OFPP_LOCAL);
+}
+
+
static bool
compose_dec_ttl(struct action_xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
{
@@ -5200,6 +5295,79 @@ compose_dec_ttl(struct action_xlate_ctx *ctx, struct ofpact_cnt_ids *ids)
}
static void
+commit_dec_mpls_ttl_action(struct action_xlate_ctx *ctx)
+{
+ uint8_t ttl = mpls_lse_to_ttl(ctx->flow.mpls_lse);
+ if (ttl > 1) {
+ ttl--;
+ if (ctx->flow.mpls_lse != htonl(0)) {
+ flow_set_mpls_lse_ttl(&ctx->flow.mpls_lse, ttl);
+ }
+ if (ctx->base_flow.mpls_lse != htonl(0)) {
+ flow_set_mpls_lse_ttl(&ctx->base_flow.mpls_lse, ttl);
+ }
+ compose_output_action(ctx, OFPP_LOCAL);
+ } else {
+ execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
+ }
+}
+
+static void
+commit_copy_mpls_ttl_in_action(struct action_xlate_ctx *ctx)
+{
+ /* Get MPLS top label ttl to be copied. */
+ uint8_t ttl = 64;
+ if (ctx->flow.mpls_lse != htonl(0)) {
+ ttl = mpls_lse_to_ttl(ctx->flow.mpls_lse);;
+ } else if (ctx->base_flow.mpls_lse != htonl(0)) {
+ ttl = mpls_lse_to_ttl(ctx->base_flow.mpls_lse);;
+ }
+
+ /* Update ttl into flow inner mpls lse or l3 fields. */
+ if (ctx->base_flow.inner_mpls_lse != htonl(0)) {
+ flow_set_mpls_lse_ttl(&ctx->base_flow.inner_mpls_lse, ttl);
+ } else {
+ ctx->flow.nw_ttl = ttl;
+ }
+
+ if (ctx->flow.inner_mpls_lse != htonl(0)) {
+ flow_set_mpls_lse_ttl(&ctx->flow.inner_mpls_lse, ttl);
+ } else {
+ ctx->flow.nw_ttl = ttl;
+ }
+
+ compose_output_action(ctx, OFPP_LOCAL);
+}
+
+static void
+commit_copy_mpls_ttl_out_action(struct action_xlate_ctx *ctx)
+{
+ /* Packets in actins_xlate_ctx is NULL when pushing down flow stats,
+ * hence rely on kernel/userspace to modify ttl. */
+ uint8_t ttl = 64;
+
+ if (ctx->flow.inner_mpls_lse != htonl(0)) {
+ ttl = mpls_lse_to_ttl(ctx->flow.inner_mpls_lse);
+ } else if (ctx->flow.nw_ttl != 0) {
+ ttl = ctx->flow.nw_ttl;
+ } else if (ctx->base_flow.inner_mpls_lse != htonl(0)) {
+ ttl = mpls_lse_to_ttl(ctx->base_flow.inner_mpls_lse);
+ } else if (ctx->base_flow.nw_ttl != 0) {
+ ttl = ctx->base_flow.nw_ttl;
+ }
+
+ /* Update ttl into flow mpls lse. */
+ if (ctx->flow.mpls_lse != htonl(0)) {
+ flow_set_mpls_lse_ttl(&ctx->flow.mpls_lse, ttl);
+ }
+ if (ctx->base_flow.mpls_lse != htonl(0)) {
+ flow_set_mpls_lse_ttl(&ctx->base_flow.mpls_lse, ttl);
+ }
+
+ compose_output_action(ctx, OFPP_LOCAL);
+}
+
+static void
xlate_output_action(struct action_xlate_ctx *ctx,
uint16_t port, uint16_t max_len, bool may_packet_in)
{
@@ -5547,8 +5715,62 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
nxm_execute_reg_move(ofpact_get_REG_MOVE(a), &ctx->flow);
break;
- case OFPACT_REG_LOAD:
- nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow);
+ case OFPACT_REG_LOAD: {
+ struct ofpact_reg_load *load;
+ load = ofpact_get_REG_LOAD(a);
+ nxm_execute_reg_load(load, &ctx->flow);
+ if (load->dst.field->id == MFF_MPLS_LABEL ||
+ load->dst.field->id == MFF_MPLS_TC ||
+ load->dst.field->id == MFF_MPLS_STACK) {
+ commit_mpls_lse_action(ctx);
+ }
+ break;
+ }
+
+ case OFPACT_PUSH_MPLS:
+ commit_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a)->ethertype);
+ break;
+
+ case OFPACT_POP_MPLS:
+ commit_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype);
+ ctx->flow.mpls_lse = htonl(0);
+ break;
+
+ case OFPACT_SET_MPLS_LABEL: {
+ ovs_be32 mpls_label = ofpact_get_SET_MPLS_LABEL(a)->mpls_label;
+ mpls_label = htonl(ntohl(mpls_label) << MPLS_LABEL_SHIFT);
+ ctx->flow.mpls_lse &= ~htonl(MPLS_LABEL_MASK);
+ ctx->flow.mpls_lse |= mpls_label;
+ commit_mpls_lse_action(ctx);
+ break;
+ }
+
+ case OFPACT_SET_MPLS_TC: {
+ uint32_t mpls_tc = ofpact_get_SET_MPLS_TC(a)->mpls_tc;
+ ctx->flow.mpls_lse &= ~htonl(MPLS_TC_MASK);
+ ctx->flow.mpls_lse |= htonl(mpls_tc << MPLS_TC_SHIFT);
+ commit_mpls_lse_action(ctx);
+ break;
+ }
+
+ case OFPACT_SET_MPLS_TTL: {
+ uint32_t mpls_ttl = ofpact_get_SET_MPLS_TTL(a)->mpls_ttl;
+ ctx->flow.mpls_lse &= ~htonl(MPLS_TTL_MASK);
+ ctx->flow.mpls_lse |= htonl(mpls_ttl << MPLS_TTL_SHIFT);
+ commit_mpls_lse_action(ctx);
+ break;
+ }
+
+ case OFPACT_DEC_MPLS_TTL:
+ commit_dec_mpls_ttl_action(ctx);
+ break;
+
+ case OFPACT_COPY_TTL_IN:
+ commit_copy_mpls_ttl_in_action(ctx);
+ break;
+
+ case OFPACT_COPY_TTL_OUT:
+ commit_copy_mpls_ttl_out_action(ctx);
break;
case OFPACT_DEC_TTL:
diff --git a/tests/automake.mk b/tests/automake.mk
index 20f9e82..c816741 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -101,6 +101,7 @@ valgrind_wrappers = \
tests/valgrind/test-csum \
tests/valgrind/test-file_name \
tests/valgrind/test-flows \
+ tests/valgrind/test-mpls \
tests/valgrind/test-hash \
tests/valgrind/test-heap \
tests/valgrind/test-hmap \
@@ -188,6 +189,10 @@ tests_test_flows_SOURCES = tests/test-flows.c
tests_test_flows_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
dist_check_SCRIPTS = tests/flowgen.pl
+noinst_PROGRAMS += tests/test-mpls
+tests_test_mpls_SOURCES = tests/test-mpls.c
+tests_test_mpls_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+
noinst_PROGRAMS += tests/test-hash
tests_test_hash_SOURCES = tests/test-hash.c
tests_test_hash_LDADD = lib/libopenvswitch.a
diff --git a/tests/odp.at b/tests/odp.at
index 9617af2..696e452 100644
--- a/tests/odp.at
+++ b/tests/odp.at
@@ -24,6 +24,11 @@ in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv
in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,tll=00:0a:0b:0c:0d:0e)
in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e)
in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4,tip=5.6.7.8,op=1,sha=00:0f:10:11:12:13,tha=00:14:15:16:17:18)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=3,ttl=64,bos=1)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=7,ttl=100,bos=1)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=7,ttl=100,bos=0)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8848),mpls(label=1000,tc=4,ttl=200,bos=1)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8848),mpls(label=1000,tc=4,ttl=200,bos=0)
])
(echo '# Valid forms without tun_id or VLAN header.'
@@ -39,6 +44,14 @@ in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp
s/$/)/' odp-base.txt
echo
+ echo '# Valid forms with MPLS header.'
+ sed 's/\(eth([[^)]]*),?\)/\1,eth_type(0x8847),mpls(label=100,tc=7,ttl=64,bos=1)/' odp-base.txt
+
+ echo
+ echo '# Valid forms with MPLS multicast header.'
+ sed 's/\(eth([[^)]]*),?\)/\1,eth_type(0x8848),mpls(label=100,tc=7,ttl=64,bos=1)/' odp-base.txt
+
+ echo
echo '# Valid forms with QoS priority.'
sed 's/^/priority(1234),/' odp-base.txt
@@ -61,8 +74,9 @@ sed -n 's/,frag=no),/,frag=first),/p' odp-base.txt
echo
echo '# Valid forms with IP later fragment.'
sed -n 's/,frag=no),.*/,frag=later)/p' odp-base.txt) > odp.txt
+sed 's/\(.*mpls[(]l\)/ODP_FIT_TOO_LITTLE: \1/' odp.txt > odp-out.txt
AT_CAPTURE_FILE([odp.txt])
-AT_CHECK_UNQUOTED([test-odp parse-keys < odp.txt], [0], [`cat odp.txt`
+AT_CHECK_UNQUOTED([test-odp parse-keys < odp.txt], [0], [`cat odp-out.txt`
])
AT_CLEANUP
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index c2eb002..0dba8a8 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -337,7 +337,7 @@ c0 a8 00 02 27 2f 00 00 78 50 cc 5b 57 af 42 1e \
50 00 02 00 26 e8 00 00 00 00 00 00 00 00 \
"], [0], [dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 (via no_match) data_len=60 buffer=0x00000111
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:06) type:0800 proto:6 tos:0 ttl:64 ip(192.168.0.1->192.168.0.2) port(10031->0) tcp_csum:26e8
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:06) type:0800,mpls(0) proto:6 tos:0 ttl:64 ip(192.168.0.1->192.168.0.2) port(10031->0) tcp_csum:26e8
])
AT_CLEANUP
@@ -351,7 +351,7 @@ AT_CHECK([ovs-ofctl ofp-print "\
00 00 00 23 20 83 c1 5f 00 00 00 00 \
"], [0], [dnl
OFPT_PACKET_IN (OF1.2) (xid=0x0): total_len=42 in_port=LOCAL (via no_match) data_len=42 buffer=0xffffff00
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:23:20:83:c1:5f->ff:ff:ff:ff:ff:ff) type:8035
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:23:20:83:c1:5f->ff:ff:ff:ff:ff:ff) type:8035,mpls(0)
])
AT_CLEANUP
@@ -1154,7 +1154,7 @@ ff ff ff ff ff ff 00 00 00 00 82 82 82 82 82 82 \
31 6d 00 00 00 00 00 00 00 00 \
"], [0], [dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 metadata=0x5a5a5a5a5a5a5a5a reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
])
AT_CLEANUP
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index de56ef8..16048e5 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -98,7 +98,7 @@ AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
NXT_PACKET_IN (xid=0x0): table_id=1 total_len=42 in_port=1 (via invalid_ttl) data_len=42 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:1 tos:0 ttl:1 ip(192.168.0.1->192.168.0.2)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800,mpls(0) proto:1 tos:0 ttl:1 ip(192.168.0.1->192.168.0.2)
])
OVS_VSWITCHD_STOP
AT_CLEANUP
@@ -250,6 +250,12 @@ cookie=0x6 table=4 in_port=83 actions=load:4->NXM_NX_REG3[[]],mod_nw_src:83.83.8
cookie=0x7 table=5 in_port=84 actions=load:5->NXM_NX_REG4[[]],load:6->NXM_NX_TUN_ID[[]],mod_nw_dst:84.84.84.84,controller,resubmit(85,6)
cookie=0x8 table=6 in_port=85 actions=mod_tp_src:85,controller,resubmit(86,7)
cookie=0x9 table=7 in_port=86 actions=mod_tp_dst:86,controller,controller
+cookie=0xa dl_src=40:44:44:44:44:44 actions=push_mpls:0x8847,set_mpls_label:10,set_mpls_tc:3,set_mpls_ttl:64,dec_mpls_ttl,controller
+cookie=0xb dl_src=50:55:55:55:55:55 actions=set_mpls_label:1000,set_mpls_ttl:200,copy_ttl_out,controller
+cookie=0xb dl_src=50:55:55:55:55:56 actions=set_mpls_label:1000,set_mpls_ttl:200,controller
+cookie=0xd dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,controller
+cookie=0xc dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,set_mpls_label:1000,set_mpls_tc:7,set_mpls_ttl:250,controller
+cookie=0xc dl_src=70:77:77:77:77:78 actions=push_mpls:0x8848,set_mpls_label:1000,set_mpls_tc:7,set_mpls_ttl:250,copy_ttl_out,controller
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
@@ -263,13 +269,13 @@ done
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via no_match) data_len=60 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:54:00:00:00:05->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->9) tcp_csum:0
])
dnl Singleton controller action.
@@ -282,13 +288,13 @@ done
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(10:11:11:11:11:11->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
])
dnl Modified controller action.
@@ -301,13 +307,128 @@ done
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
dnl
OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
dnl
OFPT_PACKET_IN (xid=0x0): total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:15,pcp:0) mac(30:33:33:33:33:33->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->10) tcp_csum:0
+])
+
+dnl Modified MPLS controller action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:44,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(40:44:44:44:44:44->50:54:00:00:00:07) type:8847,mpls(label:10,tc:3,ttl:63,bos:1)
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(40:44:44:44:44:44->50:54:00:00:00:07) type:8847,mpls(label:10,tc:3,ttl:63,bos:1)
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(40:44:44:44:44:44->50:54:00:00:00:07) type:8847,mpls(label:10,tc:3,ttl:63,bos:1)
+])
+
+dnl Modified MPLS actions.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:55:55:55:55:55,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=100,tc=7,ttl=64,bos=1),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:55:55:55:55:55->50:54:00:00:00:07) type:8847,mpls(label:1000,tc:7,ttl:64,bos:1)
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:55:55:55:55:55->50:54:00:00:00:07) type:8847,mpls(label:1000,tc:7,ttl:64,bos:1)
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:55:55:55:55:55->50:54:00:00:00:07) type:8847,mpls(label:1000,tc:7,ttl:64,bos:1)
+])
+
+dnl Modified MPLS actions and copy MPLS ttl.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:55:55:55:55:56,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=100,tc=7,ttl=64,bos=1),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:55:55:55:55:56->50:54:00:00:00:07) type:8847,mpls(label:1000,tc:7,ttl:200,bos:1)
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:55:55:55:55:56->50:54:00:00:00:07) type:8847,mpls(label:1000,tc:7,ttl:200,bos:1)
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xb total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(50:55:55:55:55:56->50:54:00:00:00:07) type:8847,mpls(label:1000,tc:7,ttl:200,bos:1)
+])
+
+dnl Modified MPLS ipv6 controller action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=70:77:77:77:77:77,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(70:77:77:77:77:77->50:54:00:00:00:07) type:8848,mpls(label:1000,tc:7,ttl:250,bos:1)
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(70:77:77:77:77:77->50:54:00:00:00:07) type:8848,mpls(label:1000,tc:7,ttl:250,bos:1)
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(70:77:77:77:77:77->50:54:00:00:00:07) type:8848,mpls(label:1000,tc:7,ttl:250,bos:1)
+])
+
+
+dnl Modified MPLS ipv6 controller with copy ttl action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=70:77:77:77:77:78,dst=50:54:00:00:00:07),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(70:77:77:77:77:78->50:54:00:00:00:07) type:8848,mpls(label:1000,tc:7,ttl:64,bos:1)
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(70:77:77:77:77:78->50:54:00:00:00:07) type:8848,mpls(label:1000,tc:7,ttl:64,bos:1)
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xc total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(70:77:77:77:77:78->50:54:00:00:00:07) type:8848,mpls(label:1000,tc:7,ttl:64,bos:1)
+])
+
+dnl Modified MPLS pop action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=60:66:66:66:66:66,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=10,tc=3,ttl=100,bos=1),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=56 in_port=1 (via action) data_len=56 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(60:66:66:66:66:66->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) tcp_csum:0
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=56 in_port=1 (via action) data_len=56 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(60:66:66:66:66:66->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) tcp_csum:0
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=56 in_port=1 (via action) data_len=56 (unbuffered)
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(60:66:66:66:66:66->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) tcp_csum:0
])
dnl Checksum TCP.
@@ -320,31 +441,31 @@ done
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
dnl
NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 reg0=0x1 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
dnl
NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 reg0=0x1 reg1=0x2 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->50:54:00:00:00:07) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->50:54:00:00:00:07) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
dnl
NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) tcp_csum:0
dnl
NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->192.168.0.2) port(8->11) tcp_csum:1a03
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(83.83.83.83->192.168.0.2) port(8->11) tcp_csum:1a03
dnl
NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(8->11) tcp_csum:3205
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(8->11) tcp_csum:3205
dnl
NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->11) tcp_csum:31b8
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->11) tcp_csum:31b8
dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:6 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) tcp_csum:316d
])
dnl Checksum UDP.
@@ -357,31 +478,31 @@ done
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
NXT_PACKET_IN (xid=0x0): cookie=0x1 total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800,mpls(0) proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
dnl
NXT_PACKET_IN (xid=0x0): table_id=1 cookie=0x3 total_len=64 in_port=1 reg0=0x1 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(20:22:22:22:22:22->50:54:00:00:00:07) type:0800,mpls(0) proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
dnl
NXT_PACKET_IN (xid=0x0): table_id=2 cookie=0x4 total_len=64 in_port=1 reg0=0x1 reg1=0x2 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->50:54:00:00:00:07) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->50:54:00:00:00:07) type:0800,mpls(0) proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
dnl
NXT_PACKET_IN (xid=0x0): table_id=3 cookie=0x5 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:17 tos:0 ttl:0 ip(192.168.0.1->192.168.0.2) port(8->11) udp_csum:1234
dnl
NXT_PACKET_IN (xid=0x0): table_id=4 cookie=0x6 total_len=64 in_port=1 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->192.168.0.2) port(8->11) udp_csum:2c37
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:17 tos:0 ttl:0 ip(83.83.83.83->192.168.0.2) port(8->11) udp_csum:2c37
dnl
NXT_PACKET_IN (xid=0x0): table_id=5 cookie=0x7 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(8->11) udp_csum:4439
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(8->11) udp_csum:4439
dnl
NXT_PACKET_IN (xid=0x0): table_id=6 cookie=0x8 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->11) udp_csum:43ec
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->11) udp_csum:43ec
dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) udp_csum:43a1
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) udp_csum:43a1
dnl
NXT_PACKET_IN (xid=0x0): table_id=7 cookie=0x9 total_len=64 in_port=1 tun_id=0x6 reg0=0x1 reg1=0x2 reg2=0x3 reg3=0x4 reg4=0x5 (via action) data_len=64 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800 proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) udp_csum:43a1
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(vlan:80,pcp:0) mac(80:81:81:81:81:81->82:82:82:82:82:82) type:0800,mpls(0) proto:17 tos:0 ttl:0 ip(83.83.83.83->84.84.84.84) port(85->86) udp_csum:43a1
])
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
@@ -394,6 +515,12 @@ AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
cookie=0x7, table=5, n_packets=2, n_bytes=120, in_port=84 actions=load:0x5->NXM_NX_REG4[[]],load:0x6->NXM_NX_TUN_ID[[]],mod_nw_dst:84.84.84.84,CONTROLLER:65535,resubmit(85,6)
cookie=0x8, table=6, n_packets=2, n_bytes=120, in_port=85 actions=mod_tp_src:85,CONTROLLER:65535,resubmit(86,7)
cookie=0x9, table=7, n_packets=2, n_bytes=120, in_port=86 actions=mod_tp_dst:86,CONTROLLER:65535,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:44 actions=push_mpls:0x8847,set_mpls_label:10,set_mpls_tc:3,set_mpls_ttl:64,dec_mpls_ttl,CONTROLLER:65535
+ cookie=0xb, n_packets=3, n_bytes=180, dl_src=50:55:55:55:55:55 actions=set_mpls_label:1000,set_mpls_ttl:200,copy_ttl_out,CONTROLLER:65535
+ cookie=0xb, n_packets=3, n_bytes=180, dl_src=50:55:55:55:55:56 actions=set_mpls_label:1000,set_mpls_ttl:200,CONTROLLER:65535
+ cookie=0xc, n_packets=3, n_bytes=180, dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,set_mpls_label:1000,set_mpls_tc:7,set_mpls_ttl:250,CONTROLLER:65535
+ cookie=0xc, n_packets=3, n_bytes=180, dl_src=70:77:77:77:77:78 actions=push_mpls:0x8848,set_mpls_label:1000,set_mpls_tc:7,set_mpls_ttl:250,copy_ttl_out,CONTROLLER:65535
+ cookie=0xd, n_packets=3, n_bytes=180, dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,CONTROLLER:65535
n_packets=3, n_bytes=180, dl_src=10:11:11:11:11:11 actions=CONTROLLER:65535
NXST_FLOW reply:
])
diff --git a/tests/ofproto.at b/tests/ofproto.at
index af7b7ca..895369f 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -588,21 +588,21 @@ check_async () {
ovs-ofctl -v packet-out br0 none controller '0001020304050010203040501234'
if test X"$1" = X"OFPR_ACTION"; then shift;
echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=NONE (via action) data_len=14 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234"
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234,mpls(0)"
fi
# OFPT_PACKET_IN, OFPR_NO_MATCH (controller_id=123)
ovs-ofctl -v packet-out br0 none 'controller(reason=no_match,id=123)' '0001020304050010203040501234'
if test X"$1" = X"OFPR_NO_MATCH"; then shift;
echo >>expout "OFPT_PACKET_IN: total_len=14 in_port=NONE (via no_match) data_len=14 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234"
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234,mpls(0)"
fi
# OFPT_PACKET_IN, OFPR_INVALID_TTL (controller_id=0)
ovs-ofctl packet-out br0 none dec_ttl '002583dfb4000026b98cb0f908004500003fb7e200000011339bac11370dac100002d7730035002b8f6d86fb0100000100000000000006626c702d7873066e696369726103636f6d00000f00'
if test X"$1" = X"OFPR_INVALID_TTL"; then shift;
echo >>expout "OFPT_PACKET_IN: total_len=76 in_port=NONE (via invalid_ttl) data_len=76 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:26:b9:8c:b0:f9->00:25:83:df:b4:00) type:0800 proto:17 tos:0 ttl:0 ip(172.17.55.13->172.16.0.2) port(55155->53) udp_csum:8f6d"
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:26:b9:8c:b0:f9->00:25:83:df:b4:00) type:0800,mpls(0) proto:17 tos:0 ttl:0 ip(172.17.55.13->172.16.0.2) port(55155->53) udp_csum:8f6d"
fi
# OFPT_PORT_STATUS, OFPPR_ADD
@@ -700,9 +700,9 @@ ovs-appctl -t ovs-ofctl exit
AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
OFPT_PACKET_IN: total_len=14 in_port=NONE (via action) data_len=14 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234,mpls(0)
OFPT_PACKET_IN: total_len=14 in_port=CONTROLLER (via action) data_len=14 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:5678
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:5678,mpls(0)
OFPT_BARRIER_REPLY:
])
@@ -730,7 +730,7 @@ ovs-appctl -t ovs-ofctl exit
AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
NXT_PACKET_IN: total_len=14 in_port=NONE metadata=0xfafafafa5a5a5a5a (via action) data_len=14 (unbuffered)
-priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234
+priority:0,tunnel:0,metadata:0,in_port:0000,tci(0) mac(00:10:20:30:40:50->00:01:02:03:04:05) type:1234,mpls(0)
OFPT_BARRIER_REPLY:
])
diff --git a/tests/test-bundle.c b/tests/test-bundle.c
index aa8b6f0..bd6b778 100644
--- a/tests/test-bundle.c
+++ b/tests/test-bundle.c
@@ -136,7 +136,7 @@ main(int argc, char *argv[])
flows = xmalloc(N_FLOWS * sizeof *flows);
for (i = 0; i < N_FLOWS; i++) {
random_bytes(&flows[i], sizeof flows[i]);
- memset(flows[i].zeros, 0, sizeof flows[i].zeros);
+ flow_zero_pad(flows + i);
flows[i].regs[0] = OFPP_NONE;
}
diff --git a/tests/test-mpls.c b/tests/test-mpls.c
new file mode 100644
index 0000000..05f23f1
--- /dev/null
+++ b/tests/test-mpls.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2012 Nicira, 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <features.h>
+#ifndef __aligned_u64
+#define __aligned_u64 __u64 __attribute__((aligned(8)))
+#endif
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include "csum.h"
+#include "packets.h"
+
+#define PKT_LENGTH 512
+#define ETH_DST_ADDR_OFF 0
+#define ETH_SRC_ADDR_OFF ETH_DST_ADDR_OFF + ETH_ALEN
+#define ETH_TYPE_ADDR_OFF ETH_SRC_ADDR_OFF + ETH_ALEN
+#define VLAN_TPID_ADDR_OFF ETH_TYPE_ADDR_OFF
+#define VLAN_VID_ADDR_OFF VLAN_TPID_ADDR_OFF + 2
+#define VLAN_TYPE_ADDR_OFF VLAN_VID_ADDR_OFF + 2
+#define MPLS_HDR_ADDR_OFF ETH_TYPE_ADDR_OFF + 2
+#define IP_HDR_ADDR_OFF MPLS_HDR_ADDR_OFF + 4
+
+struct vlan_hdr {
+ uint16_t value;
+};
+
+static int
+create_sock (int proto)
+{
+ int sock_fd;
+
+ if ((sock_fd = socket(AF_PACKET, SOCK_RAW, proto)) == -1) {
+ perror("Error creating socket: ");
+ exit(-1);
+ }
+ return sock_fd;
+}
+
+static int
+bind_sock (char *device, int sock_fd, int protocol)
+{
+
+ struct sockaddr_ll sll;
+ struct ifreq ifr;
+ bzero(&sll, sizeof(sll));
+ bzero(&ifr, sizeof(ifr));
+
+ /* First Get the Interface Index */
+ strncpy((char *)ifr.ifr_name, device, IFNAMSIZ);
+ if ((ioctl(sock_fd, SIOCGIFINDEX, &ifr)) == -1) {
+ printf("Error getting Interface index !\n");
+ exit(-1);
+ }
+
+ /* Bind socket to this interface */
+ sll.sll_family = AF_PACKET;
+ sll.sll_ifindex = ifr.ifr_ifindex;
+ sll.sll_protocol = htons(protocol);
+
+ if ((bind(sock_fd, (struct sockaddr *)&sll, sizeof(sll)))== -1) {
+ perror("Error binding socket to interface\n");
+ exit(-1);
+ }
+
+ return 1;
+}
+
+static int
+send_pkt (int sock_fd, uint8_t *pkt, int pkt_len)
+{
+ int sent = 0;
+
+ /* A simple write on the socket ..thats all it takes ! */
+
+ if ((sent = write(sock_fd, pkt, pkt_len)) != pkt_len) {
+ return 0;
+ }
+ return 1;
+}
+
+static void
+write_ether_type (uint8_t *pkt, uint16_t eth_type)
+{
+ ovs_be16 tmp_eth_type;
+ tmp_eth_type = htons(eth_type);
+ memcpy((void*)pkt, (void*)&tmp_eth_type, 2);
+}
+
+static void
+write_ether_hdr (uint8_t *pkt, uint16_t eth_type)
+{
+ ovs_be16 tmp_eth_type;
+ /*MAC address of the host*/
+ uint8_t src_mac[ETH_ALEN] = {0x00, 0x27, 0x13, 0x67, 0xb9, 0x9b};
+
+ /*gateway MAC address*/
+ uint8_t dest_mac[ETH_ALEN] = {0x00, 0x1f, 0x9e, 0x2a, 0x7f, 0xdd};
+
+ tmp_eth_type = htons(eth_type);
+
+ memcpy((void*)(pkt + ETH_DST_ADDR_OFF), (void*)dest_mac, ETH_ALEN);
+ memcpy((void*)(pkt + ETH_SRC_ADDR_OFF), (void*)src_mac, ETH_ALEN);
+ memcpy((void*)(pkt + ETH_TYPE_ADDR_OFF), (void*)&tmp_eth_type, 2);
+}
+
+static void
+write_vlan_hdr (uint8_t *pkt, uint16_t vid, uint16_t pcp, uint16_t id)
+{
+ struct vlan_hdr vlan_h;
+ ovs_be16 vlan_raw;
+ ovs_be16 tpid = htons(id);
+
+ vlan_h.value = ((vid << VLAN_VID_SHIFT) & VLAN_VID_MASK) |
+ ((pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK);
+
+ vlan_raw = htons(vlan_h.value);
+
+ memcpy((void*)pkt, (void *)&tpid, 2);
+ memcpy((void*)(pkt+2), (void *) &vlan_raw, 2);
+}
+
+static void
+write_mpls_hdr (uint8_t *pkt, uint32_t label,
+ uint32_t tc, uint32_t s, uint32_t ttl)
+{
+ struct mpls_hdr mpls_h;
+
+ mpls_h.mpls_lse = htonl(((ttl << MPLS_TTL_SHIFT) & MPLS_TTL_MASK) |
+ ((tc << MPLS_TC_SHIFT) & MPLS_TC_MASK) |
+ ((s << MPLS_STACK_SHIFT) & MPLS_STACK_MASK) |
+ ((label << MPLS_LABEL_SHIFT) & MPLS_LABEL_MASK));
+
+ memcpy((void*)(pkt), (void *) &mpls_h.mpls_lse, 4);
+}
+
+static void
+write_ip_hdr (uint8_t *pkt, uint16_t ip_pkt_len)
+{
+ uint8_t ip_hdr[20] = { 0x45, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x11, 0xa3, 0xfc,
+ 0x0a, 0x75, 0x2e, 0xc8,
+ 0x0a, 0x75, 0x2e, 0xc1};
+
+ ip_hdr[2] = (0xFF00 & ip_pkt_len) >> 8;
+ ip_hdr[3] = 0x00FF & ip_pkt_len;
+
+ memcpy((void *)(pkt), (void *) &ip_hdr, 20);
+}
+
+static void
+write_udp_hdr (uint8_t *pkt, uint16_t udp_len)
+{
+ uint8_t udp_hdr[8] = {0x0F, 0x00, 0x0F, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+ udp_hdr[4] = (0xFF00 & udp_len) >> 8;
+ udp_hdr[5] = (0x00FF & udp_len);
+ memcpy((void *)(pkt), (void *) &udp_hdr, 8);
+}
+
+static void
+write_ip_csum (uint8_t *pkt, uint16_t len)
+{
+ /* len should be just the length of the header */
+ ovs_be16 ip_csum = 0;
+
+ /* initialize the ip checksum field to 0 for
+ * purposes of calculating the header */
+ memcpy(pkt + 10, &ip_csum, 2);
+
+ /* appears to return in network byte order somehow */
+ ip_csum = csum(pkt, len);
+ memcpy(pkt + 10, &ip_csum, 2);
+}
+
+/* argv[1] is the device e.g. eth0
+ argv[2] is the number of pkts to send
+*/
+int
+main (int argc, char **argv)
+{
+
+ int sock_fd;
+ uint8_t pkt[PKT_LENGTH];
+ uint8_t *pkt_pos = pkt;
+ uint8_t *ip_pos;
+ uint32_t label = 101, tc = 4, ttl = 10;
+ uint16_t vid = 101, pcp = 4;
+ uint32_t num_of_pkts, num_labels;
+ uint16_t i = 0;
+ char *str = "FEEDFACE", type[5];
+
+ if (argc != 5) {
+ printf("usage: %s <device> <# pkts> <#labels> <type=vlan/mpls>\n", argv[0]);
+ return -1;
+ }
+
+ num_of_pkts = atoi(argv[2]);
+
+ strncpy(type, argv[argc-1], 5);
+
+ /* Set the magic data 0xfeedface */
+ for (i = 0; i < PKT_LENGTH; i+=8) {
+ memcpy((void*)(pkt + i), (void*)str, 8);
+ }
+
+ num_labels = atoi(argv[3]);
+
+ if (!strcmp(type, "vlan")) {
+ write_ether_hdr(pkt_pos, ETH_TYPE_IP);
+ pkt_pos += ETH_TYPE_ADDR_OFF;
+ for (i = 0; i < num_labels; i++) {
+ if (i == 1 || num_labels == 1) {
+ write_vlan_hdr(pkt_pos, vid++, pcp++, ETH_TYPE_VLAN);
+ }
+ else {
+ write_vlan_hdr(pkt_pos, vid++, pcp++, ETH_TYPE_VLAN);
+ }
+ pkt_pos += 4;
+ }
+ write_ether_type(pkt_pos, ETH_TYPE_IP);
+ pkt_pos+=2;
+ } else {
+ write_ether_hdr(pkt_pos, ETH_TYPE_MPLS);
+ pkt_pos += MPLS_HDR_ADDR_OFF;
+ for (i = 0; i < num_labels; i++) {
+ if (i == num_labels - 1) {
+ write_mpls_hdr(pkt_pos, label++, tc, 1, ttl++);
+ } else {
+ write_mpls_hdr(pkt_pos, label++, tc, 0, ttl++);
+ }
+ pkt_pos += 4;
+ }
+ }
+
+ ip_pos = pkt_pos;
+ write_ip_hdr(pkt_pos, PKT_LENGTH - (ip_pos - pkt));
+ pkt_pos += 20;
+
+ write_udp_hdr(pkt_pos, PKT_LENGTH -(pkt_pos - pkt));
+ pkt_pos += 8;
+
+ write_ip_csum(ip_pos, 20);
+
+ /* Create the socket */
+ sock_fd = create_sock(ETH_P_ALL);
+
+ /* Bind socket to interface */
+ bind_sock(argv[1], sock_fd, ETH_P_ALL);
+
+ while ((num_of_pkts--) > 0) {
+ if (!send_pkt(sock_fd, pkt, PKT_LENGTH)) {
+ perror("Error sending pkt");
+ printf("\n\n");
+ break;
+ }
+ }
+ printf("\nPrinting packet\n");
+ for (i = 0; i < 50; i++)
+ printf("%x ", pkt[i]);
+ if (num_of_pkts == -1)
+ printf("Packets sent successfully\n");
+
+ close(sock_fd);
+ return 0;
+}
diff --git a/tests/test-multipath.c b/tests/test-multipath.c
index b990c13..462a5e1 100644
--- a/tests/test-multipath.c
+++ b/tests/test-multipath.c
@@ -60,7 +60,7 @@ main(int argc, char *argv[])
struct flow flow;
random_bytes(&flow, sizeof flow);
- memset(flow.zeros, 0, sizeof flow.zeros);
+ flow_zero_pad(&flow);
mp.max_link = n - 1;
multipath_execute(&mp, &flow);
diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c
index 6224237..d254d01 100644
--- a/utilities/ovs-dpctl.c
+++ b/utilities/ovs-dpctl.c
@@ -895,14 +895,13 @@ dpctl_normalize_actions(int argc, char *argv[])
hmap_init(&actions_per_flow);
NL_ATTR_FOR_EACH (a, left, odp_actions.data, odp_actions.size) {
- if (nl_attr_type(a) == OVS_ACTION_ATTR_POP_VLAN) {
+ const struct ovs_action_push_vlan *push;
+ switch(nl_attr_type(a)) {
+ case OVS_ACTION_ATTR_POP_VLAN:
flow.vlan_tci = htons(0);
continue;
- }
-
- if (nl_attr_type(a) == OVS_ACTION_ATTR_PUSH_VLAN) {
- const struct ovs_action_push_vlan *push;
+ case OVS_ACTION_ATTR_PUSH_VLAN:
push = nl_attr_get_unspec(a, sizeof *push);
flow.vlan_tci = push->vlan_tci;
continue;
@@ -936,6 +935,15 @@ dpctl_normalize_actions(int argc, char *argv[])
printf("no vlan: ");
}
+ if (af->flow.mpls_lse != htonl(0)) {
+ printf("mpls(label=%"PRIu32",tc=%d,ttl=%d): ",
+ mpls_lse_to_label(af->flow.mpls_lse),
+ mpls_lse_to_tc(af->flow.mpls_lse),
+ mpls_lse_to_ttl(af->flow.mpls_lse));
+ } else {
+ printf("no mpls: ");
+ }
+
ds_clear(&s);
format_odp_actions(&s, af->actions.data, af->actions.size);
puts(ds_cstr(&s));
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index be71f18..f735fd8 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -451,6 +451,21 @@ integer between 0 and 65535, inclusive, either in decimal or as a
hexadecimal number prefixed by \fB0x\fR (e.g. \fB0x0806\fR to match ARP
packets).
.
+.IP \fBmpls_label=\fIlabel\fR
+Matches MPLS Label when \fIethertype\fR is \fI0x8847\fR or \fI0x8848\fR.
+Specify a number between 0 and 2^20-1, inclusive, as the 20-bit MPLS label
+to match. If none specified, all packets which has \fIethertype\fR equal to
+\fI0x8847\fR or \fI0x8848\fR are matched.
+.
+.IP \fBmpls_tc=\fIpriority\fR
+Matches MPLS traffic class when \fIethertype\fR is \fI0x8847\fR or
+\fI0x8848\fR. Specify a value between 0 and 7, inclusive. A higher value
+indicates a higher packet priority level.
+.
+.IP \fBmpls_stack=\fIstack\fR
+Matches MPLS stack bit when \fIethertype\fR is \fI0x8847\fR or \fI0x8848\fR.
+Specify either 0 or 1 to match a packet with a single label or multiple labels.
+.
.IP \fBnw_src=\fIip\fR[\fB/\fInetmask\fR]
.IQ \fBnw_dst=\fIip\fR[\fB/\fInetmask\fR]
When \fBdl_type\fR is 0x0800 (possibly via shorthand, e.g. \fBip\fR
@@ -897,6 +912,36 @@ as necessary to match the value specified. Valid values are between 0
.IP \fBstrip_vlan\fR
Strips the VLAN tag from a packet if it is present.
.
+.IP \fBpush_mpls\fR:\fIethertype\fR
+Modifies the ethertype of a packet with the value specified. The new MPLS
+label stack entry is set with value copied from outermost MPLS label stack
+entry if present and stack bit set to 0 or MPLS label stack entry is set with
+\fBlabel\fR:\fI0 for IPv4, 2 for IPv6 or default 0\fR, \fBtc\fR:\fIleast
+significant 3 bits from IPv4 DSCP or IPv6 traffic-class or 0\fR,
+\fBstack\fR:\fI1\fR, \fBttl\fR:\fIIPv4 ttl or IPv6 hlim or default 64\fR.
+.
+.IP \fBpop_mpls\fR:\fIethertype\fR
+Strips the outermost MPLS label stack entry and modifies the ethertype of a
+packet with the value specified if MPLS stack bit is set(i.e. bottom of stack).
+.
+.IP \fBset_mpls_label\fR:\fIlabel\fR
+Modifies the outermost MPLS label stack entry's label field.
+.
+.IP \fBset_mpls_ttl\fR:\fIttl\fR
+Modifies the outermost MPLS label stack entry's time-to-live field.
+.
+.IP \fBset_mpls_tc\fR:\fItc\fR
+Modifies the outermost MPLS label stack entry's traffic-class field.
+.
+.IP \fBcopy_mpls_ttl_in\fR
+Copies ttl from outermost MPLS header to next-to-outermost MPLS or IP header.
+.
+.IP \fBcopy_mpls_ttl_out\fR
+Copies ttl from next-to-outermost IP or MPLS header to outermost MPLS header.
+.
+.IP \fBdec_mpls_ttl\fR
+Decrements ttl from outermost MPLS header.
+.
.IP \fBmod_dl_src\fB:\fImac\fR
Sets the source Ethernet address to \fImac\fR.
.
--
1.7.10.4
More information about the dev
mailing list