release. See ovs-vswitchd(8) for details.
- OpenFlow:
* OpenFlow 1.5 (draft) extended registers are now supported.
+ * The OpenFlow 1.5 (draft) actset_output field is now supported.
* OpenFlow 1.5 (draft) Copy-Field action is now supported.
* OpenFlow 1.5 (draft) masked Set-Field action is now supported.
* OpenFlow 1.3+ table features requests are now supported (read-only).
"NXM_NX_": (0, 0x0001),
"OXM_OF_": (0, 0x8000),
"OXM_OF_PKT_REG": (0, 0x8001),
+ "ONFOXM_ET_": (0x4f4e4600, 0xffff),
# This is the experimenter OXM class for Nicira, which is the
# one that OVS would be using instead of NXM_OF_ and NXM_NX_
* --------------- --------------------------------------
* 0x0000 not assigned a meaning by OpenFlow 1.0
* 0x0001...0xfeff "physical" ports
- * 0xff00...0xfff7 "reserved" but not assigned a meaning by OpenFlow 1.0
- * 0xfff8...0xffff "reserved" OFPP_* ports with assigned meanings
+ * 0xff00...0xfff6 "reserved" but not assigned a meaning by OpenFlow 1.x
+ * 0xfff7...0xffff "reserved" OFPP_* ports with assigned meanings
*/
/* Ranges. */
#define OFPP_MAX OFP_PORT_C(0xff00) /* Max # of switch ports. */
-#define OFPP_FIRST_RESV OFP_PORT_C(0xfff8) /* First assigned reserved port. */
+#define OFPP_FIRST_RESV OFP_PORT_C(0xfff7) /* First assigned reserved port. */
#define OFPP_LAST_RESV OFP_PORT_C(0xffff) /* Last assigned reserved port. */
/* Reserved output "ports". */
+#define OFPP_UNSET OFP_PORT_C(0xfff7) /* For OXM_OF_ACTSET_OUTPUT only. */
#define OFPP_IN_PORT OFP_PORT_C(0xfff8) /* Where the packet came in. */
#define OFPP_TABLE OFP_PORT_C(0xfff9) /* Perform actions in flow table. */
#define OFPP_NORMAL OFP_PORT_C(0xfffa) /* Process with normal L2/L3. */
/* TCP flags in the first half of a BE32, zeroes in the other half. */
BUILD_ASSERT_DECL(offsetof(struct flow, tcp_flags) + 2
- == offsetof(struct flow, pad) &&
+ == offsetof(struct flow, pad2) &&
offsetof(struct flow, tcp_flags) / 4
- == offsetof(struct flow, pad) / 4);
+ == offsetof(struct flow, pad2) / 4);
#if WORDS_BIGENDIAN
#define TCP_FLAGS_BE32(tcp_ctl) ((OVS_FORCE ovs_be32)TCP_FLAGS_BE16(tcp_ctl) \
<< 16)
* away. Some GCC versions gave warnings on ALWAYS_INLINE, so these are
* defined as macros. */
-#if (FLOW_WC_SEQ != 27)
+#if (FLOW_WC_SEQ != 28)
#define MINIFLOW_ASSERT(X) ovs_assert(X)
BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime "
"assertions enabled. Consider updating FLOW_WC_SEQ after "
void
flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
fmd->dp_hash = flow->dp_hash;
fmd->recirc_id = flow->recirc_id;
memset(&wc->masks, 0x0, sizeof wc->masks);
/* Update this function whenever struct flow changes. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
if (flow->tunnel.ip_dst) {
if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
WC_MASK_FIELD(wc, dp_hash);
WC_MASK_FIELD(wc, in_port);
+ /* actset_output wildcarded. */
+
WC_MASK_FIELD(wc, dl_dst);
WC_MASK_FIELD(wc, dl_src);
WC_MASK_FIELD(wc, dl_type);
flow_wc_map(const struct flow *flow)
{
/* Update this function whenever struct flow changes. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
uint64_t map = (flow->tunnel.ip_dst) ? MINIFLOW_MAP(tunnel) : 0;
flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc)
{
/* Update this function whenever struct flow changes. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
+ wc->masks.actset_output = 0;
}
/* Returns true if 'wc' matches every packet, false if 'wc' fixes any bits or
flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label));
/* Clear all L3 and L4 fields. */
- BUILD_ASSERT(FLOW_WC_SEQ == 27);
+ BUILD_ASSERT(FLOW_WC_SEQ == 28);
memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
}
/* 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 27
+#define FLOW_WC_SEQ 28
/* Number of Open vSwitch extension 32-bit registers. */
#define FLOW_N_REGS 8
uint32_t pkt_mark; /* Packet mark. */
uint32_t recirc_id; /* Must be exact match. */
union flow_in_port in_port; /* Input port.*/
+ ofp_port_t actset_output; /* Output port in action set. */
+ ovs_be16 pad1; /* Pad to 32 bits. */
/* L2, Order the same as in the Ethernet header! */
uint8_t dl_dst[ETH_ADDR_LEN]; /* Ethernet destination address. */
uint8_t arp_tha[ETH_ADDR_LEN]; /* ARP/ND target hardware address. */
struct in6_addr nd_target; /* IPv6 neighbor discovery (ND) target. */
ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */
- ovs_be16 pad; /* Padding. */
+ ovs_be16 pad2; /* Pad to 32 bits. */
/* L4 */
ovs_be16 tp_src; /* TCP/UDP/SCTP source port. */
#define FLOW_MAX_PACKET_U32S (FLOW_U32S \
/* Unused in datapath */ - FLOW_U32_SIZE(regs) \
- FLOW_U32_SIZE(metadata) \
+ - FLOW_U32_SIZE(actset_output) \
/* L2.5/3 */ - FLOW_U32_SIZE(nw_src) \
- FLOW_U32_SIZE(nw_dst) \
- FLOW_U32_SIZE(mpls_lse) \
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
BUILD_ASSERT_DECL(offsetof(struct flow, dp_hash) + sizeof(uint32_t)
- == sizeof(struct flow_tnl) + 176
- && FLOW_WC_SEQ == 27);
+ == sizeof(struct flow_tnl) + 180
+ && FLOW_WC_SEQ == 28);
/* Incremental points at which flow classification may be performed in
* segments.
flow_set_xreg(&match->flow, xreg_idx, value & mask);
}
+void
+match_set_actset_output(struct match *match, ofp_port_t actset_output)
+{
+ match->wc.masks.actset_output = u16_to_ofp(UINT16_MAX);
+ match->flow.actset_output = actset_output;
+}
+
void
match_set_metadata(struct match *match, ovs_be64 metadata)
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
if (priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "priority=%d,", priority);
ds_put_format(s, "skb_priority=%#"PRIx32",", f->skb_priority);
}
+ if (wc->masks.actset_output) {
+ ds_put_cstr(s, "actset_output=");
+ ofputil_format_port(f->actset_output, s);
+ ds_put_char(s, ',');
+ }
+
if (wc->masks.dl_type) {
skip_type = true;
if (f->dl_type == htons(ETH_TYPE_IP)) {
void match_set_xreg(struct match *, unsigned int xreg_idx, uint64_t value);
void match_set_xreg_masked(struct match *, unsigned int xreg_idx,
uint64_t value, uint64_t mask);
+void match_set_actset_output(struct match *, ofp_port_t actset_output);
void match_set_metadata(struct match *, ovs_be64 metadata);
void match_set_metadata_masked(struct match *,
ovs_be64 metadata, ovs_be64 mask);
return !wc->masks.regs[mf->id - MFF_REG0];
CASE_MFF_XREGS:
return !flow_get_xreg(&wc->masks, mf->id - MFF_XREG0);
+ case MFF_ACTSET_OUTPUT:
+ return !wc->masks.actset_output;
case MFF_ETH_SRC:
return eth_addr_is_zero(wc->masks.dl_src);
case MFF_ND_TLL:
return true;
- case MFF_IN_PORT_OXM: {
+ case MFF_IN_PORT_OXM:
+ case MFF_ACTSET_OUTPUT: {
ofp_port_t port;
return !ofputil_port_from_ofp11(value->be32, &port);
}
case MFF_IN_PORT_OXM:
value->be32 = ofputil_port_to_ofp11(flow->in_port.ofp_port);
break;
+ case MFF_ACTSET_OUTPUT:
+ value->be32 = ofputil_port_to_ofp11(flow->actset_output);
+ break;
case MFF_SKB_PRIORITY:
value->be32 = htonl(flow->skb_priority);
match_set_in_port(match, port);
break;
}
+ case MFF_ACTSET_OUTPUT: {
+ ofp_port_t port;
+ ofputil_port_from_ofp11(value->be32, &port);
+ match_set_actset_output(match, port);
+ break;
+ }
case MFF_SKB_PRIORITY:
match_set_skb_priority(match, ntohl(value->be32));
flow->in_port.ofp_port = u16_to_ofp(ntohs(value->be16));
break;
- case MFF_IN_PORT_OXM: {
- ofp_port_t port;
- ofputil_port_from_ofp11(value->be32, &port);
- flow->in_port.ofp_port = port;
+ case MFF_IN_PORT_OXM:
+ ofputil_port_from_ofp11(value->be32, &flow->in_port.ofp_port);
+ break;
+ case MFF_ACTSET_OUTPUT:
+ ofputil_port_from_ofp11(value->be32, &flow->actset_output);
break;
- }
case MFF_SKB_PRIORITY:
flow->skb_priority = ntohl(value->be32);
match->flow.in_port.ofp_port = 0;
match->wc.masks.in_port.ofp_port = 0;
break;
+ case MFF_ACTSET_OUTPUT:
+ match->flow.actset_output = 0;
+ match->wc.masks.actset_output = 0;
+ break;
case MFF_SKB_PRIORITY:
match->flow.skb_priority = 0;
case MFF_RECIRC_ID:
case MFF_IN_PORT:
case MFF_IN_PORT_OXM:
+ case MFF_ACTSET_OUTPUT:
case MFF_SKB_PRIORITY:
case MFF_ETH_TYPE:
case MFF_DL_VLAN:
*/
MFF_IN_PORT_OXM,
+ /* "actset_output".
+ *
+ * Type: be32.
+ * Maskable: no.
+ * Formatting: OpenFlow 1.1+ port.
+ * Prerequisites: none.
+ * Access: read-only.
+ * NXM: none.
+ * OXM: ONFOXM_ET_ACTSET_OUTPUT(43) since OF1.3 and v2.4,
+ * OXM_OF_ACTSET_OUTPUT(43) since OF1.5 and v2.4.
+ */
+ MFF_ACTSET_OUTPUT,
+
/* "skb_priority".
*
* Designates the queue to which output will be directed. The value in
int match_len;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
/* Metadata. */
if (match->wc.masks.dp_hash) {
htons(ofp_to_u16(in_port)));
}
}
+ if (match->wc.masks.actset_output) {
+ nxm_put_32(b, MFF_ACTSET_OUTPUT, oxm,
+ ofputil_port_to_ofp11(flow->actset_output));
+ }
/* Ethernet. */
nxm_put_eth_masked(b, MFF_ETH_SRC, oxm,
* add another field and forget to adjust this value.
*/
#define ODPUTIL_FLOW_KEY_BYTES 512
-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
/* A buffer with sufficient size and alignment to hold an nlattr-formatted flow
* key. An array of "struct nlattr" might not, in theory, be sufficiently
void
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
/* Initialize most of wc. */
flow_wildcards_init_catchall(wc);
OFPUTIL_NAMED_PORT(ALL) \
OFPUTIL_NAMED_PORT(CONTROLLER) \
OFPUTIL_NAMED_PORT(LOCAL) \
- OFPUTIL_NAMED_PORT(ANY)
+ OFPUTIL_NAMED_PORT(ANY) \
+ OFPUTIL_NAMED_PORT(UNSET)
/* For backwards compatibility, so that "none" is recognized as OFPP_ANY */
#define OFPUTIL_NAMED_PORTS_WITH_NONE \
* When translation is otherwise complete, ofpacts_execute_action_set()
* converts it to a set of "struct ofpact"s that can be translated into
* datapath actions. */
+ bool action_set_has_group; /* Action set contains OFPACT_GROUP? */
struct ofpbuf action_set; /* Action set. */
uint64_t action_set_stub[1024 / 8];
};
/* If 'struct flow' gets additional metadata, we'll need to zero it out
* before traversing a patch port. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
if (!xport) {
xlate_report(ctx, "Nonexistent output port");
flow->metadata = htonll(0);
memset(&flow->tunnel, 0, sizeof flow->tunnel);
memset(flow->regs, 0, sizeof flow->regs);
+ flow->actset_output = OFPP_UNSET;
special = process_special(ctx, &ctx->xin->flow, peer,
ctx->xin->packet);
static void
xlate_write_actions(struct xlate_ctx *ctx, const struct ofpact *a)
{
- struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a);
- ofpbuf_put(&ctx->action_set, on->actions, ofpact_nest_get_action_len(on));
+ const struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a);
+ size_t on_len = ofpact_nest_get_action_len(on);
+ const struct ofpact *inner;
+
+ /* Maintain actset_output depending on the contents of the action set:
+ *
+ * - OFPP_UNSET, if there is no "output" action.
+ *
+ * - The output port, if there is an "output" action and no "group"
+ * action.
+ *
+ * - OFPP_UNSET, if there is a "group" action.
+ */
+ if (!ctx->action_set_has_group) {
+ OFPACT_FOR_EACH (inner, on->actions, on_len) {
+ if (inner->type == OFPACT_OUTPUT) {
+ ctx->xin->flow.actset_output = ofpact_get_OUTPUT(inner)->port;
+ } else if (inner->type == OFPACT_GROUP) {
+ ctx->xin->flow.actset_output = OFPP_UNSET;
+ ctx->action_set_has_group = true;
+ }
+ }
+ }
+
+ ofpbuf_put(&ctx->action_set, on->actions, on_len);
ofpact_pad(&ctx->action_set);
}
case OFPACT_CLEAR_ACTIONS:
ofpbuf_clear(&ctx->action_set);
+ ctx->xin->flow.actset_output = OFPP_UNSET;
+ ctx->action_set_has_group = false;
break;
case OFPACT_WRITE_ACTIONS:
xin->ofproto = ofproto;
xin->flow = *flow;
xin->flow.in_port.ofp_port = in_port;
+ xin->flow.actset_output = OFPP_UNSET;
xin->packet = packet;
xin->may_learn = packet != NULL;
xin->rule = rule;
}
ofpbuf_use_stub(&ctx.stack, ctx.init_stack, sizeof ctx.init_stack);
+
+ ctx.action_set_has_group = false;
ofpbuf_use_stub(&ctx.action_set,
ctx.action_set_stub, sizeof ctx.action_set_stub);
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - actset_output])
+OVS_VSWITCHD_START
+ADD_OF_PORTS(
+ [br0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13])
+AT_DATA([flows.txt], [dnl
+table=0 actions=write_actions(output(2)),goto_table(1)
+table=1 actions=move:ONFOXM_ET_ACTSET_OUTPUT[[0..31]]->OXM_OF_PKT_REG0[[0..31]],goto_table(2)
+
+# Verify that actset_output got set.
+table=2,priority=20,actset_output=2 actions=4,goto_table(3)
+table=2,priority=10 actions=5,goto_table(3)
+
+# Verify that xreg0 got copied properly from actset_output.
+table=3,priority=20,xreg0=2 actions=6,goto_table(4)
+table=3,priority=10 actions=7,goto_table(4)
+
+# Verify that adding a group action unsets actset_output.
+table=4 actions=write_actions(group(5)),goto_table(5)
+table=5,priority=20,actset_output=unset actions=8,goto_table(6)
+table=5,priority=10 actions=9,goto_table(6)
+
+# Verify that adding another output action doesn't change actset_output
+# (since there's still a group).
+table=6 actions=write_actions(output(3)),goto_table(7)
+table=7,priority=20,actset_output=unset actions=10,goto_table(8)
+table=7,priority=10 actions=11,goto_table(8)
+
+# Verify that clearing the action set, then writing an output action,
+# causes actset_output to be set again.
+table=8,actions=clear_actions,write_actions(output(3),output(2)),goto_table(9)
+table=9,priority=20,actset_output=2 actions=12
+table=9,priority=10 actions=13
+])
+AT_CHECK([ovs-ofctl -O OpenFlow13 add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [Datapath actions: 4,6,8,10,12,2
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
AT_SETUP([ofproto-dpif - push-pop])
OVS_VSWITCHD_START
ADD_OF_PORTS([br0], [20], [21], [22], [33], [90])
metadata: arbitrary mask
in_port: exact match or wildcard
in_port_oxm: exact match or wildcard
+ actset_output: exact match or wildcard
pkt_mark: arbitrary mask
reg0: arbitrary mask
reg1: arbitrary mask
# Check that the configuration was updated.
mv expout orig-expout
sed 's/classifier/main/
-73s/1000000/1024/' < orig-expout > expout
+74s/1000000/1024/' < orig-expout > expout
AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0 | sed '/^$/d
/^OFPST_TABLE_FEATURES/d'], [0], [expout])
OVS_VSWITCHD_STOP
])
AT_CLEANUP
-AT_SETUP([ovs-ofctl parse-oxm (OpenFlow 1.5)])
+AT_SETUP([ovs-ofctl parse-oxm (OpenFlow 1.2)])
AT_KEYWORDS([oxm])
AT_DATA([oxm.txt], [dnl
<any>
AT_CHECK([grep -v '1-bits in value' stderr], [1])
AT_CLEANUP
+AT_SETUP([ovs-ofctl parse-oxm (OpenFlow 1.3)])
+AT_KEYWORDS([oxm])
+AT_DATA([oxm.txt], [dnl
+# actset_output
+ONFOXM_ET_ACTSET_OUTPUT(00000000)
+ONFOXM_ET_ACTSET_OUTPUT(fffffffe)
+ONFOXM_ET_ACTSET_OUTPUT(fffffff7)
+OXM_OF_ACTSET_OUTPUT(00000000)
+OXM_OF_ACTSET_OUTPUT(fffffffe)
+OXM_OF_ACTSET_OUTPUT(fffffff7)
+])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' --strict parse-oxm OpenFlow13 < oxm.txt],
+ [0], [dnl
+# actset_output
+ONFOXM_ET_ACTSET_OUTPUT(00000000)
+ONFOXM_ET_ACTSET_OUTPUT(fffffffe)
+ONFOXM_ET_ACTSET_OUTPUT(fffffff7)
+ONFOXM_ET_ACTSET_OUTPUT(00000000)
+ONFOXM_ET_ACTSET_OUTPUT(fffffffe)
+ONFOXM_ET_ACTSET_OUTPUT(fffffff7)
+], [])
+AT_CLEANUP
+
AT_SETUP([ovs-ofctl parse-oxm (OpenFlow 1.5)])
AT_KEYWORDS([oxm])
AT_DATA([oxm.txt], [dnl
NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1(a0e0d050)
NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG2_W(a0e0d050/f0f0f0f0)
NXM_NX_REG1_W(a0e0d050/f0f0f0f0), NXM_NX_REG2(a0e0d050)
+
+# actset_output
+ONFOXM_ET_ACTSET_OUTPUT(00000000)
+ONFOXM_ET_ACTSET_OUTPUT(fffffffe)
+ONFOXM_ET_ACTSET_OUTPUT(fffffff7)
+OXM_OF_ACTSET_OUTPUT(00000000)
+OXM_OF_ACTSET_OUTPUT(fffffffe)
+OXM_OF_ACTSET_OUTPUT(fffffff7)
])
AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' --strict parse-oxm OpenFlow15 < oxm.txt],
[0], [dnl
OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0ffffffff)
OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d05000000000/f0f0f0f000000000)
OXM_OF_PKT_REG0_W(00000000a0e0d050/00000000f0f0f0f0), OXM_OF_PKT_REG1_W(a0e0d05000000000/ffffffff00000000)
+
+# actset_output
+OXM_OF_ACTSET_OUTPUT(00000000)
+OXM_OF_ACTSET_OUTPUT(fffffffe)
+OXM_OF_ACTSET_OUTPUT(fffffff7)
+OXM_OF_ACTSET_OUTPUT(00000000)
+OXM_OF_ACTSET_OUTPUT(fffffffe)
+OXM_OF_ACTSET_OUTPUT(fffffff7)
], [])
AT_CLEANUP
On Linux this corresponds to the skb mark but the exact implementation is
platform-dependent.
.
+.IP \fBactset_output=\fIport\fR
+Matches the output port currently in the OpenFlow action set, where
+\fIport\fR may be an OpenFlow port number or keyword
+(e.g. \fBLOCAL\fR). If there is no output port in the OpenFlow action
+set, or if the output port will be ignored (e.g. because there is an
+output group in the OpenFlow action set), then the value will be
+\fBUNSET\fR.
+.IP
+This field was introduced in Open vSwitch 2.4 to conform with the
+OpenFlow 1.5 (draft) specification.
.PP
Defining IPv6 flows (those with \fBdl_type\fR equal to 0x86dd) requires
support for NXM. The following shorthand notations are available for