2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 01:51:26 +00:00

New action NXAST_RESUBMIT_TABLE.

This makes multiple table support in ofproto-dpif useful, by allowing
resubmits into tables other than 0.
This commit is contained in:
Ben Pfaff 2011-08-09 09:24:18 -07:00
parent 0697b5c3c9
commit 299016266e
12 changed files with 190 additions and 42 deletions

6
NEWS
View File

@ -2,6 +2,12 @@ Post-v1.2.0
------------------------
- ovs-appctl:
- New "version" command to determine version of running daemon
- ovs-vswitchd:
- The software switch now supports 255 OpenFlow tables, instead
of just one. By default, only table 0 is consulted, but the
new NXAST_RESUBMIT_TABLE action can look up in additional
tables. Tables 128 and above are reserved for use by the
switch itself; please use only tables 0 through 127.
v1.2.0 - 03 Aug 2011
------------------------

View File

@ -279,7 +279,8 @@ enum nx_action_subtype {
NXAST_MULTIPATH, /* struct nx_action_multipath */
NXAST_AUTOPATH, /* struct nx_action_autopath */
NXAST_BUNDLE, /* struct nx_action_bundle */
NXAST_BUNDLE_LOAD /* struct nx_action_bundle */
NXAST_BUNDLE_LOAD, /* struct nx_action_bundle */
NXAST_RESUBMIT_TABLE /* struct nx_action_resubmit */
};
/* Header for Nicira-defined actions. */
@ -292,31 +293,51 @@ struct nx_action_header {
};
OFP_ASSERT(sizeof(struct nx_action_header) == 16);
/* Action structure for NXAST_RESUBMIT.
/* Action structures for NXAST_RESUBMIT and NXAST_RESUBMIT_TABLE.
*
* NXAST_RESUBMIT searches the flow table again, using a flow that is slightly
* modified from the original lookup:
* These actions search one of the switch's flow tables:
*
* - The 'in_port' member of struct nx_action_resubmit is used as the flow's
* in_port.
* - For NXAST_RESUBMIT_TABLE only, if the 'table' member is not 255, then
* it specifies the table to search.
*
* - If NXAST_RESUBMIT is preceded by actions that affect the flow
* (e.g. OFPAT_SET_VLAN_VID), then the flow is updated with the new
* values.
* - Otherwise (for NXAST_RESUBMIT_TABLE with a 'table' of 255, or for
* NXAST_RESUBMIT regardless of 'table'), it searches the current flow
* table, that is, the OpenFlow flow table that contains the flow from
* which this action was obtained. If this action did not come from a
* flow table (e.g. it came from an OFPT_PACKET_OUT message), then table 0
* is the current table.
*
* The flow table lookup uses a flow that may be slightly modified from the
* original lookup:
*
* - For NXAST_RESUBMIT, the 'in_port' member of struct nx_action_resubmit
* is used as the flow's in_port.
*
* - For NXAST_RESUBMIT_TABLE, if the 'in_port' member is not OFPP_IN_PORT,
* then its value is used as the flow's in_port. Otherwise, the original
* in_port is used.
*
* - If actions that modify the flow (e.g. OFPAT_SET_VLAN_VID) precede the
* resubmit action, then the flow is updated with the new values.
*
* Following the lookup, the original in_port is restored.
*
* If the modified flow matched in the flow table, then the corresponding
* actions are executed. Afterward, actions following NXAST_RESUBMIT in the
* actions are executed. Afterward, actions following the resubmit in the
* original set of actions, if any, are executed; any changes made to the
* packet (e.g. changes to VLAN) by secondary actions persist when those
* actions are executed, although the original in_port is restored.
*
* NXAST_RESUBMIT may be used any number of times within a set of actions.
* Resubmit actions may be used any number of times within a set of actions.
*
* NXAST_RESUBMIT may nest to an implementation-defined depth. Beyond this
* implementation-defined depth, further NXAST_RESUBMIT actions are simply
* ignored. (Open vSwitch 1.0.1 and earlier did not support recursion.)
* Resubmit actions may nest to an implementation-defined depth. Beyond this
* implementation-defined depth, further resubmit actions are simply ignored.
*
* NXAST_RESUBMIT ignores 'table' and 'pad'. NXAST_RESUBMIT_TABLE requires
* 'pad' to be all-bits-zero.
*
* Open vSwitch 1.0.1 and earlier did not support recursion. Open vSwitch
* before 1.2.90 did not support NXAST_RESUBMIT_TABLE.
*/
struct nx_action_resubmit {
ovs_be16 type; /* OFPAT_VENDOR. */
@ -324,7 +345,8 @@ struct nx_action_resubmit {
ovs_be32 vendor; /* NX_VENDOR_ID. */
ovs_be16 subtype; /* NXAST_RESUBMIT. */
ovs_be16 in_port; /* New in_port for checking flow table. */
uint8_t pad[4];
uint8_t table; /* NXAST_RESUBMIT_TABLE: table to use. */
uint8_t pad[3];
};
OFP_ASSERT(sizeof(struct nx_action_resubmit) == 16);

View File

@ -285,7 +285,6 @@ put_dl_addr_action(struct ofpbuf *b, uint16_t type, const char *addr)
str_to_mac(addr, oada->dl_addr);
}
static bool
parse_port_name(const char *name, uint16_t *port)
{
@ -317,6 +316,40 @@ parse_port_name(const char *name, uint16_t *port)
return false;
}
static void
parse_resubmit(struct nx_action_resubmit *nar, char *arg)
{
char *in_port_s, *table_s;
uint16_t in_port;
uint8_t table;
in_port_s = strsep(&arg, ",");
if (in_port_s && in_port_s[0]) {
if (!parse_port_name(in_port_s, &in_port)) {
in_port = str_to_u32(in_port_s);
}
} else {
in_port = OFPP_IN_PORT;
}
table_s = strsep(&arg, ",");
table = table_s && table_s[0] ? str_to_u32(table_s) : 255;
if (in_port == OFPP_IN_PORT && table == 255) {
ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified "
" on resubmit");
}
nar->vendor = htonl(NX_VENDOR_ID);
nar->in_port = htons(in_port);
if (in_port != OFPP_IN_PORT && table == 255) {
nar->subtype = htons(NXAST_RESUBMIT);
} else {
nar->subtype = htons(NXAST_RESUBMIT_TABLE);
nar->table = table;
}
}
static void
str_to_action(char *str, struct ofpbuf *b)
{
@ -421,9 +454,7 @@ str_to_action(char *str, struct ofpbuf *b)
} else if (!strcasecmp(act, "resubmit")) {
struct nx_action_resubmit *nar;
nar = put_action(b, sizeof *nar, OFPAT_VENDOR);
nar->vendor = htonl(NX_VENDOR_ID);
nar->subtype = htons(NXAST_RESUBMIT);
nar->in_port = htons(str_to_u32(arg));
parse_resubmit(nar, arg);
} else if (!strcasecmp(act, "set_tunnel")
|| !strcasecmp(act, "set_tunnel64")) {
uint64_t tun_id = str_to_u64(arg);

View File

@ -295,6 +295,19 @@ ofp_print_action(struct ds *s, const union ofp_action *a,
ofp_print_port_name(s, ntohs(nar->in_port));
break;
case OFPUTIL_NXAST_RESUBMIT_TABLE:
nar = (struct nx_action_resubmit *)a;
ds_put_format(s, "resubmit(");
if (nar->in_port != htons(OFPP_IN_PORT)) {
ofp_print_port_name(s, ntohs(nar->in_port));
}
ds_put_char(s, ',');
if (nar->table != 255) {
ds_put_format(s, "%"PRIu8, nar->table);
}
ds_put_char(s, ')');
break;
case OFPUTIL_NXAST_SET_TUNNEL:
nast = (struct nx_action_set_tunnel *)a;
ds_put_format(s, "set_tunnel:%#"PRIx32, ntohl(nast->tun_id));

View File

@ -1966,6 +1966,15 @@ ofputil_check_output_port(uint16_t port, int max_ports)
}
}
static int
check_resubmit_table(const struct nx_action_resubmit *nar)
{
if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
}
return 0;
}
int
validate_actions(const union ofp_action *actions, size_t n_actions,
const struct flow *flow, int max_ports)
@ -2044,6 +2053,11 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
max_ports, flow);
break;
case OFPUTIL_NXAST_RESUBMIT_TABLE:
error = check_resubmit_table(
(const struct nx_action_resubmit *) a);
break;
case OFPUTIL_OFPAT_STRIP_VLAN:
case OFPUTIL_OFPAT_SET_NW_SRC:
case OFPUTIL_OFPAT_SET_NW_DST:
@ -2151,6 +2165,7 @@ ofputil_decode_nxast_action(const union ofp_action *a)
NXAST_ACTION(NXAST_AUTOPATH, struct nx_action_autopath, false);
NXAST_ACTION(NXAST_BUNDLE, struct nx_action_bundle, true);
NXAST_ACTION(NXAST_BUNDLE_LOAD, struct nx_action_bundle, true);
NXAST_ACTION(NXAST_RESUBMIT_TABLE, struct nx_action_resubmit, false);
#undef NXAST_ACTION
case NXAST_SNAT__OBSOLETE:

View File

@ -304,6 +304,7 @@ enum ofputil_action_code {
OFPUTIL_NXAST_AUTOPATH,
OFPUTIL_NXAST_BUNDLE,
OFPUTIL_NXAST_BUNDLE_LOAD,
OFPUTIL_NXAST_RESUBMIT_TABLE
};
int ofputil_decode_action(const union ofp_action *);

View File

@ -60,7 +60,7 @@ COVERAGE_DEFINE(facet_invalidated);
COVERAGE_DEFINE(facet_revalidate);
COVERAGE_DEFINE(facet_unexpected);
/* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a
/* Maximum depth of flow table recursion (due to resubmit actions) in a
* flow translation. */
#define MAX_RESUBMIT_RECURSION 16
@ -96,8 +96,8 @@ static struct rule_dpif *rule_dpif_cast(const struct rule *rule)
return rule ? CONTAINER_OF(rule, struct rule_dpif, up) : NULL;
}
static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *ofproto,
const struct flow *flow);
static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *,
const struct flow *, uint8_t table);
#define MAX_MIRRORS 32
typedef uint32_t mirror_mask_t;
@ -188,6 +188,7 @@ struct action_xlate_ctx {
uint32_t priority; /* Current flow priority. 0 if none. */
struct flow base_flow; /* Flow at the last commit. */
uint32_t base_priority; /* Priority at the last commit. */
uint8_t table_id; /* OpenFlow table ID where flow was found. */
};
static void action_xlate_ctx_init(struct action_xlate_ctx *,
@ -1654,7 +1655,7 @@ handle_miss_upcall(struct ofproto_dpif *ofproto, struct dpif_upcall *upcall)
facet = facet_lookup_valid(ofproto, &flow);
if (!facet) {
struct rule_dpif *rule = rule_dpif_lookup(ofproto, &flow);
struct rule_dpif *rule = rule_dpif_lookup(ofproto, &flow, 0);
if (!rule) {
/* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */
struct ofport_dpif *port = get_ofp_port(ofproto, flow.in_port);
@ -2434,7 +2435,7 @@ facet_revalidate(struct ofproto_dpif *ofproto, struct facet *facet)
COVERAGE_INC(facet_revalidate);
/* Determine the new rule. */
new_rule = rule_dpif_lookup(ofproto, &facet->flow);
new_rule = rule_dpif_lookup(ofproto, &facet->flow, 0);
if (!new_rule) {
/* No new rule, so delete the facet. */
facet_remove(ofproto, facet);
@ -2592,10 +2593,11 @@ flow_push_stats(const struct rule_dpif *rule,
/* Rules. */
static struct rule_dpif *
rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow)
rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow,
uint8_t table_id)
{
return rule_dpif_cast(rule_from_cls_rule(
classifier_lookup(&ofproto->up.tables[0],
classifier_lookup(&ofproto->up.tables[table_id],
flow)));
}
@ -2717,7 +2719,7 @@ rule_execute(struct rule *rule_, struct flow *flow, struct ofpbuf *packet)
/* Otherwise, if 'rule' is in fact the correct rule for 'packet', then
* create a new facet for it and use that. */
if (rule_dpif_lookup(ofproto, flow) == rule) {
if (rule_dpif_lookup(ofproto, flow, 0) == rule) {
facet = facet_create(rule, flow, packet);
facet_execute(ofproto, facet, packet);
facet_install(ofproto, facet, true);
@ -2889,18 +2891,23 @@ add_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port)
}
static void
xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
xlate_table_action(struct action_xlate_ctx *ctx,
uint16_t in_port, uint8_t table_id)
{
if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
struct rule_dpif *rule;
uint16_t old_in_port;
uint8_t old_table_id;
old_table_id = ctx->table_id;
ctx->table_id = table_id;
/* Look up a flow with 'in_port' as the input port. Then restore the
* original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
* have surprising behavior). */
old_in_port = ctx->flow.in_port;
ctx->flow.in_port = in_port;
rule = rule_dpif_lookup(ctx->ofproto, &ctx->flow);
rule = rule_dpif_lookup(ctx->ofproto, &ctx->flow, table_id);
ctx->flow.in_port = old_in_port;
if (ctx->resubmit_hook) {
@ -2912,14 +2919,31 @@ xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx);
ctx->recurse--;
}
ctx->table_id = old_table_id;
} else {
static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
VLOG_ERR_RL(&recurse_rl, "NXAST_RESUBMIT recursed over %d times",
VLOG_ERR_RL(&recurse_rl, "resubmit actions recursed over %d times",
MAX_RESUBMIT_RECURSION);
}
}
static void
xlate_resubmit_table(struct action_xlate_ctx *ctx,
const struct nx_action_resubmit *nar)
{
uint16_t in_port;
uint8_t table_id;
in_port = (nar->in_port == htons(OFPP_IN_PORT)
? ctx->flow.in_port
: ntohs(nar->in_port));
table_id = nar->table == 255 ? ctx->table_id : nar->table;
xlate_table_action(ctx, in_port, table_id);
}
static void
flood_packets(struct action_xlate_ctx *ctx, ovs_be32 mask)
{
@ -2950,7 +2974,7 @@ xlate_output_action__(struct action_xlate_ctx *ctx,
add_output_action(ctx, ctx->flow.in_port);
break;
case OFPP_TABLE:
xlate_table_action(ctx, ctx->flow.in_port);
xlate_table_action(ctx, ctx->flow.in_port, ctx->table_id);
break;
case OFPP_NORMAL:
xlate_normal(ctx);
@ -3182,7 +3206,11 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
case OFPUTIL_NXAST_RESUBMIT:
nar = (const struct nx_action_resubmit *) ia;
xlate_table_action(ctx, ntohs(nar->in_port));
xlate_table_action(ctx, ntohs(nar->in_port), ctx->table_id);
break;
case OFPUTIL_NXAST_RESUBMIT_TABLE:
xlate_resubmit_table(ctx, (const struct nx_action_resubmit *) ia);
break;
case OFPUTIL_NXAST_SET_TUNNEL:
@ -3272,6 +3300,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
ctx->priority = 0;
ctx->base_priority = 0;
ctx->base_flow = ctx->flow;
ctx->table_id = 0;
if (process_special(ctx->ofproto, &ctx->flow, ctx->packet)) {
ctx->may_set_up_flow = false;
@ -3925,7 +3954,8 @@ struct ofproto_trace {
};
static void
trace_format_rule(struct ds *result, int level, const struct rule_dpif *rule)
trace_format_rule(struct ds *result, uint8_t table_id, int level,
const struct rule_dpif *rule)
{
ds_put_char_multiple(result, '\t', level);
if (!rule) {
@ -3933,8 +3963,8 @@ trace_format_rule(struct ds *result, int level, const struct rule_dpif *rule)
return;
}
ds_put_format(result, "Rule: cookie=%#"PRIx64" ",
ntohll(rule->up.flow_cookie));
ds_put_format(result, "Rule: table=%"PRIu8" cookie=%#"PRIx64" ",
table_id, ntohll(rule->up.flow_cookie));
cls_rule_format(&rule->up.cr, result);
ds_put_char(result, '\n');
@ -3967,7 +3997,7 @@ trace_resubmit(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
ds_put_char(result, '\n');
trace_format_flow(result, ctx->recurse + 1, "Resubmitted flow", trace);
trace_format_rule(result, ctx->recurse + 1, rule);
trace_format_rule(result, ctx->table_id, ctx->recurse + 1, rule);
}
static void
@ -4054,8 +4084,8 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, const char *args_,
flow_format(&result, &flow);
ds_put_char(&result, '\n');
rule = rule_dpif_lookup(ofproto, &flow);
trace_format_rule(&result, 0, rule);
rule = rule_dpif_lookup(ofproto, &flow, 0);
trace_format_rule(&result, 0, 0, rule);
if (rule) {
struct ofproto_trace trace;
struct ofpbuf *odp_actions;

View File

@ -28,6 +28,7 @@ TESTSUITE_AT = \
tests/timeval.at \
tests/lockfile.at \
tests/reconnect.at \
tests/ofproto-dpif.at \
tests/ofproto-macros.at \
tests/ofproto.at \
tests/ovsdb.at \

19
tests/ofproto-dpif.at Normal file
View File

@ -0,0 +1,19 @@
AT_BANNER([ofproto-dpif])
AT_SETUP([ofproto-dpif - resubmit])
OFPROTO_START
AT_DATA([flows.txt], [dnl
table=0 in_port=1 priority=1000 icmp actions=output(10),resubmit(2),output(19),resubmit(3),output(21)
table=0 in_port=2 priority=1500 icmp actions=output(11),resubmit(,1),output(16),resubmit(2,1),output(18)
table=0 in_port=3 priority=2000 icmp actions=output(20)
table=1 in_port=1 priority=1000 icmp actions=output(12),resubmit(4,1),output(13),resubmit(3),output(15)
table=1 in_port=2 priority=1500 icmp actions=output(17),resubmit(,2)
table=1 in_port=3 priority=1500 icmp actions=output(14),resubmit(,2)
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl -t test-openflowd ofproto/trace br0 '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),icmp(type=8,code=0)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: 10,11,12,13,14,15,16,17,18,19,20,21
])
OFPROTO_STOP
AT_CLEANUP

View File

@ -23,7 +23,9 @@ actions=bundle_load(eth_src,50,active_backup,ofport,NXM_NX_REG0[],slaves:1)
actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:2,3)
actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..30],slaves:)
actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1),output:2
actions=resubmit:1,resubmit(2),resubmit(,3),resubmit(2,3)
]])
AT_CHECK([ovs-ofctl parse-flows flows.txt
], [0], [stdout])
AT_CHECK([[sed 's/ (xid=0x[0-9a-fA-F]*)//' stdout]], [0],
@ -49,6 +51,7 @@ NXT_FLOW_MOD: ADD table:255 actions=bundle_load(eth_src,50,active_backup,ofport,
NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:2,3)
NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..30],slaves:)
NXT_FLOW_MOD: ADD table:255 actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1),output:2
NXT_FLOW_MOD: ADD table:255 actions=resubmit:1,resubmit:2,resubmit(,3),resubmit(2,3)
]])
AT_CLEANUP

View File

@ -60,6 +60,7 @@ m4_include([tests/timeval.at])
m4_include([tests/lockfile.at])
m4_include([tests/reconnect.at])
m4_include([tests/ofproto.at])
m4_include([tests/ofproto-dpif.at])
m4_include([tests/ovsdb.at])
m4_include([tests/ovs-vsctl.at])
m4_include([tests/interface-reconfigure.at])

View File

@ -655,10 +655,16 @@ only known to be implemented by Open vSwitch:
.RS
.
.IP \fBresubmit\fB:\fIport\fR
Re-searches the OpenFlow flow table with the \fBin_port\fR field
replaced by \fIport\fR and executes the actions found, if any, in
addition to any other actions in this flow entry. Recursive
\fBresubmit\fR actions are ignored.
.IQ \fBresubmit\fB(\fR[\fIport\fR]\fB,\fR[\fItable\fR]\fB)
Re-searches this OpenFlow flow table (or the table whose number is
specified by \fItable\fR) with the \fBin_port\fR field replaced by
\fIport\fR (if \fIport\fR is specified) and executes the actions
found, if any, in addition to any other actions in this flow entry.
.IP
Recursive \fBresubmit\fR actions are obeyed up to an
implementation-defined maximum depth. Open vSwitch 1.0.1 and earlier
did not support recursion; Open vSwitch before 1.2.90 did not support
\fItable\fR.
.
.IP \fBset_tunnel\fB:\fIid\fR
.IQ \fBset_tunnel64\fB:\fIid\fR