From c61f3870c208c74d6d95bebc0df9acc68341a605 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 3 Nov 2014 14:24:01 -0800 Subject: [PATCH] flow: Support OF1.5+ (draft) actset_output field. This field allows a flow table to match on the output port currently in the action set. ONF-JIRA: EXT-233 Signed-off-by: Ben Pfaff Acked-by: Jarno Rajahalme --- NEWS | 1 + build-aux/extract-ofp-fields | 1 + include/openflow/openflow-1.0.h | 7 +++--- lib/flow.c | 19 ++++++++------- lib/flow.h | 11 +++++---- lib/match.c | 15 +++++++++++- lib/match.h | 1 + lib/meta-flow.c | 29 ++++++++++++++++++----- lib/meta-flow.h | 13 +++++++++++ lib/nx-match.c | 6 ++++- lib/odp-util.h | 2 +- lib/ofp-util.c | 5 ++-- ofproto/ofproto-dpif-xlate.c | 36 ++++++++++++++++++++++++++--- tests/ofproto-dpif.at | 40 ++++++++++++++++++++++++++++++++ tests/ofproto.at | 3 ++- tests/ovs-ofctl.at | 41 ++++++++++++++++++++++++++++++++- utilities/ovs-ofctl.8.in | 10 ++++++++ 17 files changed, 209 insertions(+), 31 deletions(-) diff --git a/NEWS b/NEWS index d27118823..d487e328b 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,7 @@ Post-v2.3.0 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). diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields index bdbba751a..b15b01d52 100755 --- a/build-aux/extract-ofp-fields +++ b/build-aux/extract-ofp-fields @@ -59,6 +59,7 @@ OXM_CLASSES = {"NXM_OF_": (0, 0x0000), "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_ diff --git a/include/openflow/openflow-1.0.h b/include/openflow/openflow-1.0.h index c67edd97c..22c4be23d 100644 --- a/include/openflow/openflow-1.0.h +++ b/include/openflow/openflow-1.0.h @@ -25,16 +25,17 @@ * --------------- -------------------------------------- * 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. */ diff --git a/lib/flow.c b/lib/flow.c index 3935ea62b..a81dca414 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -72,9 +72,9 @@ BUILD_ASSERT_DECL(offsetof(struct flow, nw_frag) + 3 /* 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) @@ -121,7 +121,7 @@ struct mf_ctx { * 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 " @@ -668,7 +668,7 @@ flow_unwildcard_tp_ports(const struct flow *flow, struct flow_wildcards *wc) 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; @@ -815,7 +815,7 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc, 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) { @@ -840,6 +840,8 @@ void flow_wildcards_init_for_packet(struct flow_wildcards *wc, 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); @@ -910,7 +912,7 @@ uint64_t 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; @@ -962,10 +964,11 @@ void 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 @@ -1519,7 +1522,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type, 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); } diff --git a/lib/flow.h b/lib/flow.h index 14bc41435..2259680fc 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -38,7 +38,7 @@ struct pkt_metadata; /* 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 @@ -102,6 +102,8 @@ struct flow { 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. */ @@ -124,7 +126,7 @@ struct flow { 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. */ @@ -144,6 +146,7 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0); #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) \ @@ -153,8 +156,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0); /* 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. diff --git a/lib/match.c b/lib/match.c index 9a8454692..cbfd73529 100644 --- a/lib/match.c +++ b/lib/match.c @@ -118,6 +118,13 @@ match_set_xreg_masked(struct match *match, unsigned int xreg_idx, 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) { @@ -863,7 +870,7 @@ match_format(const struct match *match, struct ds *s, int priority) 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); @@ -885,6 +892,12 @@ match_format(const struct match *match, struct ds *s, int 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)) { diff --git a/lib/match.h b/lib/match.h index 7469eaa9f..a245bcf42 100644 --- a/lib/match.h +++ b/lib/match.h @@ -54,6 +54,7 @@ void match_set_reg_masked(struct match *, unsigned int reg_idx, 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); diff --git a/lib/meta-flow.c b/lib/meta-flow.c index 787154586..ddf043186 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -129,6 +129,8 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) 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); @@ -397,7 +399,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) 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); } @@ -485,6 +488,9 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow, 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); @@ -691,6 +697,12 @@ mf_set_value(const struct mf_field *mf, 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)); @@ -908,12 +920,12 @@ mf_set_flow_value(const struct mf_field *mf, 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); @@ -1163,6 +1175,10 @@ mf_set_wild(const struct mf_field *mf, struct match *match) 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; @@ -1354,6 +1370,7 @@ mf_set(const struct mf_field *mf, 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: diff --git a/lib/meta-flow.h b/lib/meta-flow.h index 9518ba076..16595221f 100644 --- a/lib/meta-flow.h +++ b/lib/meta-flow.h @@ -450,6 +450,19 @@ enum OVS_PACKED_ENUM mf_field_id { */ 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 diff --git a/lib/nx-match.c b/lib/nx-match.c index 04e0791f6..e70eeac65 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -817,7 +817,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, 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) { @@ -839,6 +839,10 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, 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, diff --git a/lib/odp-util.h b/lib/odp-util.h index 11b54ddea..ef4e82cf1 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -133,7 +133,7 @@ void odp_portno_names_destroy(struct hmap *portno_names); * 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 diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 573f38a17..2f3dec895 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -185,7 +185,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 == 27); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28); /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); @@ -6020,7 +6020,8 @@ ofputil_port_to_ofp11(ofp_port_t ofp10_port) 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 \ diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 12bfb9f91..ed4c58c0d 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -219,6 +219,7 @@ struct xlate_ctx { * 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]; }; @@ -2477,7 +2478,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, /* 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"); @@ -2526,6 +2527,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_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); @@ -3527,8 +3529,31 @@ may_receive(const struct xport *xport, struct xlate_ctx *ctx) 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); } @@ -3884,6 +3909,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, 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: @@ -3930,6 +3957,7 @@ xlate_in_init(struct xlate_in *xin, struct ofproto_dpif *ofproto, 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; @@ -4246,6 +4274,8 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) } 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); diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index 05bb0e3e2..58a2cf9da 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -490,6 +490,46 @@ AT_CHECK([tail -1 stdout], [0], [Datapath actions: 2 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]) diff --git a/tests/ofproto.at b/tests/ofproto.at index 69f8dc960..def078360 100644 --- a/tests/ofproto.at +++ b/tests/ofproto.at @@ -1193,6 +1193,7 @@ OVS_VSWITCHD_START 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 @@ -1263,7 +1264,7 @@ AT_CHECK( # 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 diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index d29b4dee8..446d8f58e 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -1681,7 +1681,7 @@ NXM_OF_IN_PORT(0001), NXM_OF_ETH_TYPE(0800) ]) 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 @@ -2175,6 +2175,29 @@ nx_match|WARN|Rejecting NXM/OXM entry 0:32768:2:1:16 with 1-bits in value for bi 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 @@ -2198,6 +2221,14 @@ NXM_NX_REG0_W(a0e0d050/f0f0f0f0), NXM_NX_REG1_W(a0e0d050/f0f0f0f0) 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 @@ -2221,6 +2252,14 @@ OXM_OF_PKT_REG0_W(a0e0d050a0e0d050/f0f0f0f0f0f0f0f0) 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 diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index bcc3f33f4..b8d41fbe5 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -1119,6 +1119,16 @@ system components in order to facilitate interaction between subsystems. 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 -- 2.20.1