@@ -652,6 +652,10 @@ struct ofpact_resubmit {
* If NX_LEARN_F_SEND_FLOW_REM is set, then the learned flows will have their
* OFPFF_SEND_FLOW_REM flag set.
*
+ * If NX_LEARN_F_WRITE_RESULT is set, then the actions will write whether the
+ * learn operation succeded on a bit. If the learn is successful the bit will
+ * be set, otherwise (e.g. if the limit is hit) the bit will be unset.
+ *
* If NX_LEARN_F_DELETE_LEARNED is set, then removing this action will delete
* all the flows from the learn action's 'table_id' that have the learn
* action's 'cookie'. Important points:
@@ -677,6 +681,7 @@ struct ofpact_resubmit {
enum nx_learn_flags {
NX_LEARN_F_SEND_FLOW_REM = 1 << 0,
NX_LEARN_F_DELETE_LEARNED = 1 << 1,
+ NX_LEARN_F_WRITE_RESULT = 1 << 2,
};
#define NX_LEARN_N_BITS_MASK 0x3ff
@@ -740,6 +745,13 @@ struct ofpact_learn {
ovs_be64 cookie; /* Cookie for new flow. */
uint16_t fin_idle_timeout; /* Idle timeout after FIN, if nonzero. */
uint16_t fin_hard_timeout; /* Hard timeout after FIN, if nonzero. */
+ /* If the number of learned flows on 'table_id' with 'cookie' exceeds
+ * this, the learn action will not add a new flow. */
+ uint32_t limit;
+ /* Used only if 'flags' has NX_LEARN_F_WRITE_RESULT. If the execution
+ * failed to install a new flow because 'limit' is exceeded,
+ * result_dst will be set to 0, otherwise to 1. */
+ struct mf_subfield result_dst;
);
struct ofpact_learn_spec specs[];
@@ -406,6 +406,26 @@ learn_parse__(char *orig, char *arg, struct ofpbuf *ofpacts)
learn->flags |= NX_LEARN_F_SEND_FLOW_REM;
} else if (!strcmp(name, "delete_learned")) {
learn->flags |= NX_LEARN_F_DELETE_LEARNED;
+ } else if (!strcmp(name, "limit")) {
+ learn->limit = atoi(value);
+ } else if (!strcmp(name, "result_dst")) {
+ char *error;
+ learn->flags |= NX_LEARN_F_WRITE_RESULT;
+ error = mf_parse_subfield(&learn->result_dst, value);
+ if (error) {
+ return error;
+ }
+ if (!mf_nxm_header(learn->result_dst.field->id)) {
+ return xasprintf("experimenter OXM field '%s' not supported",
+ value);
+ }
+ if (!learn->result_dst.field->writable) {
+ return xasprintf("%s is read-only", value);
+ }
+ if (learn->result_dst.n_bits != 1) {
+ return xasprintf("result_dst in 'learn' action must be a "
+ "single bit");
+ }
} else {
struct ofpact_learn_spec *spec;
char *error;
@@ -487,6 +507,14 @@ learn_format(const struct ofpact_learn *learn, struct ds *s)
ds_put_format(s, ",%scookie=%s%#"PRIx64,
colors.param, colors.end, ntohll(learn->cookie));
}
+ if (learn->limit != 0) {
+ ds_put_format(s, ",%slimit=%s%"PRIu32,
+ colors.param, colors.end, learn->limit);
+ }
+ if (learn->flags & NX_LEARN_F_WRITE_RESULT) {
+ ds_put_format(s, ",%sresult_dst=%s", colors.param, colors.end);
+ mf_format_subfield(&learn->result_dst, s);
+ }
OFPACT_LEARN_SPEC_FOR_EACH (spec, learn) {
unsigned int n_bytes = DIV_ROUND_UP(spec->n_bits, 8);
@@ -292,6 +292,8 @@ enum ofp_raw_action_type {
/* NX1.0+(16): struct nx_action_learn, ... VLMFF */
NXAST_RAW_LEARN,
+ /* NX1.0+(44): struct nx_action_learn2, ... VLMFF */
+ NXAST_RAW_LEARN2,
/* NX1.0+(17): void. */
NXAST_RAW_EXIT,
@@ -4028,7 +4030,7 @@ format_RESUBMIT(const struct ofpact_resubmit *a, struct ds *s)
}
}
-/* Action structure for NXAST_LEARN.
+/* Action structure for NXAST_LEARN and NXAST_LEARN2.
*
* This action adds or modifies a flow in an OpenFlow table, similar to
* OFPT_FLOW_MOD with OFPFC_MODIFY_STRICT as 'command'. The new flow has the
@@ -4259,6 +4261,34 @@ struct nx_action_learn {
};
OFP_ASSERT(sizeof(struct nx_action_learn) == 32);
+struct nx_action_learn2 {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* At least 24. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_LEARN2. */
+ ovs_be16 idle_timeout; /* Idle time before discarding (seconds). */
+ ovs_be16 hard_timeout; /* Max time before discarding (seconds). */
+ ovs_be16 priority; /* Priority level of flow entry. */
+ ovs_be64 cookie; /* Cookie for new flow. */
+ ovs_be16 flags; /* NX_LEARN_F_*. */
+ uint8_t table_id; /* Table to insert flow entry. */
+ uint8_t pad; /* Must be zero. */
+ ovs_be16 fin_idle_timeout; /* Idle timeout after FIN, if nonzero. */
+ ovs_be16 fin_hard_timeout; /* Hard timeout after FIN, if nonzero. */
+ ovs_be32 limit; /* Maximum number of learned flows. */
+
+ /* Where to store the result. */
+ ovs_be16 result_dst_ofs; /* Starting bit offset in destination. */
+
+ ovs_be16 pad2; /* Must be zero. */
+ /* Followed by:
+ * - OXM/NXM header for destination field (4 or 8 bytes),
+ * if NX_LEARN_F_WRITE_RESULT is set in 'flags'
+ * - a sequence of flow_mod_spec elements, as described above,
+ * until the end of the action is reached. */
+};
+OFP_ASSERT(sizeof(struct nx_action_learn2) == 40);
+
static ovs_be16
get_be16(const void **pp)
{
@@ -4431,6 +4461,65 @@ decode_NXAST_RAW_LEARN(const struct nx_action_learn *nal,
vl_mff_map, ofpacts);
}
+/* Converts 'nal' into a "struct ofpact_learn" and appends that struct to
+ * 'ofpacts'. Returns 0 if successful, otherwise an OFPERR_*. */
+static enum ofperr
+decode_NXAST_RAW_LEARN2(const struct nx_action_learn2 *nal,
+ enum ofp_version ofp_version OVS_UNUSED,
+ const struct vl_mff_map *vl_mff_map,
+ struct ofpbuf *ofpacts)
+{
+ struct ofpbuf b = ofpbuf_const_initializer(nal, ntohs(nal->len));
+ struct ofpact_learn *learn;
+
+ if (nal->pad || nal->pad2) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+
+ learn = ofpact_put_LEARN(ofpacts);
+
+ learn->idle_timeout = ntohs(nal->idle_timeout);
+ learn->hard_timeout = ntohs(nal->hard_timeout);
+ learn->priority = ntohs(nal->priority);
+ learn->cookie = nal->cookie;
+ learn->table_id = nal->table_id;
+ learn->fin_idle_timeout = ntohs(nal->fin_idle_timeout);
+ learn->fin_hard_timeout = ntohs(nal->fin_hard_timeout);
+ learn->limit = ntohl(nal->limit);
+
+ learn->flags = ntohs(nal->flags);
+ if (learn->flags & ~(NX_LEARN_F_SEND_FLOW_REM |
+ NX_LEARN_F_DELETE_LEARNED |
+ NX_LEARN_F_WRITE_RESULT)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+
+ if (learn->table_id == 0xff) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+
+ ofpbuf_pull(&b, sizeof *nal);
+
+ if (learn->flags & NX_LEARN_F_WRITE_RESULT) {
+ enum ofperr error = nx_pull_header(&b, vl_mff_map,
+ &learn->result_dst.field,
+ NULL);
+ if (error) {
+ return error;
+ }
+ if (!learn->result_dst.field->writable) {
+ return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
+ }
+ learn->result_dst.ofs = ntohs(nal->result_dst_ofs);
+ learn->result_dst.n_bits = 1;
+ } else if (nal->result_dst_ofs) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+
+ return decode_LEARN_common(b.data, (char *) nal + ntohs(nal->len),
+ vl_mff_map, ofpacts);
+}
+
static void
put_be16(struct ofpbuf *b, ovs_be16 x)
{
@@ -4460,19 +4549,43 @@ encode_LEARN(const struct ofpact_learn *learn,
enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
{
const struct ofpact_learn_spec *spec;
- struct nx_action_learn *nal;
size_t start_ofs;
start_ofs = out->size;
- nal = put_NXAST_LEARN(out);
- nal->idle_timeout = htons(learn->idle_timeout);
- nal->hard_timeout = htons(learn->hard_timeout);
- nal->fin_idle_timeout = htons(learn->fin_idle_timeout);
- nal->fin_hard_timeout = htons(learn->fin_hard_timeout);
- nal->priority = htons(learn->priority);
- nal->cookie = learn->cookie;
- nal->flags = htons(learn->flags);
- nal->table_id = learn->table_id;
+
+ if (learn->ofpact.raw == NXAST_RAW_LEARN2
+ || learn->limit != 0
+ || learn->flags & NX_LEARN_F_WRITE_RESULT) {
+ struct nx_action_learn2 *nal;
+
+ nal = put_NXAST_LEARN2(out);
+ nal->idle_timeout = htons(learn->idle_timeout);
+ nal->hard_timeout = htons(learn->hard_timeout);
+ nal->fin_idle_timeout = htons(learn->fin_idle_timeout);
+ nal->fin_hard_timeout = htons(learn->fin_hard_timeout);
+ nal->priority = htons(learn->priority);
+ nal->cookie = learn->cookie;
+ nal->flags = htons(learn->flags);
+ nal->table_id = learn->table_id;
+ nal->limit = htonl(learn->limit);
+ nal->result_dst_ofs = htons(learn->result_dst.ofs);
+
+ if (learn->flags & NX_LEARN_F_WRITE_RESULT) {
+ nx_put_header(out, learn->result_dst.field->id, 0, false);
+ }
+ } else {
+ struct nx_action_learn *nal;
+
+ nal = put_NXAST_LEARN(out);
+ nal->idle_timeout = htons(learn->idle_timeout);
+ nal->hard_timeout = htons(learn->hard_timeout);
+ nal->fin_idle_timeout = htons(learn->fin_idle_timeout);
+ nal->fin_hard_timeout = htons(learn->fin_hard_timeout);
+ nal->priority = htons(learn->priority);
+ nal->cookie = learn->cookie;
+ nal->flags = htons(learn->flags);
+ nal->table_id = learn->table_id;
+ }
OFPACT_LEARN_SPEC_FOR_EACH (spec, learn) {
put_u16(out, spec->n_bits | spec->dst_type | spec->src_type);
@@ -258,6 +258,20 @@ ffff 0020 00002320 002a 000000000000 dnl
0001 0008 0005 0000 dnl
0000 0008 000a 0000
+# actions=learn(table=2,idle_timeout=10,hard_timeout=20,fin_idle_timeout=2,fin_hard_timeout=4,priority=80,cookie=0x123456789abcdef0,limit=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
+ffff 0050 00002320 002c 000a 0014 0050 123456789abcdef0 0000 02 00 0002 0004 00000001 0000 0000 dnl
+000c 00000802 0000 00000802 0000 dnl
+0030 00000406 0000 00000206 0000 dnl
+1010 00000002 0000 dnl
+00000000
+
+# actions=learn(table=2,idle_timeout=10,hard_timeout=20,fin_idle_timeout=2,fin_hard_timeout=4,priority=80,cookie=0x123456789abcdef0,limit=1,result_dst=NXM_NX_REG0[8],NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[])
+ffff 0050 00002320 002c 000a 0014 0050 123456789abcdef0 0004 02 00 0002 0004 00000001 0008 0000 dnl
+00010004 dnl
+000c 00000802 0000 00000802 0000 dnl
+0030 00000406 0000 00000206 0000 dnl
+1010 00000002 0000
+
# actions=group:5
ffff 0010 00002320 0028 0000 00000005
@@ -1432,6 +1432,25 @@ the specified table with the specified cookie.
.IP
This flag was added in Open vSwitch 2.4.
.
+.IP \fBlimit=\fInumber\fR
+If the number of learned flows in table \fBtable\fR with cookie id
+\fBcookie\fR exceeds \fInumber\fR, a new flow will not be learned by this
+action. Unlike the \fBdelete_learned\fR behavior, only flows inserted by
+a learn action with a limit are counted. If a flow is installed by the
+controller or if it's learned by a learn action without any limit, it's not
+included when checking if the limit is exceeded. By default there's no limit.
+.
+.IP
+This flag was added in Open vSwitch 2.8.
+.
+.IP \fBresult_dst=\fIfield\fB[\fIbit\fB]\fR
+If learning failed (because the number of learned flows exceeds \fBlimit\fR),
+the action sets \fIfield\fB[\fIbit\fB]\fR to 0, otherwise it will be set to 1.
+\fIfield\fB[\fIbit\fB]\fR must be a single bit.
+.
+.IP
+This flag was added in Open vSwitch 2.8.
+.
.IP \fIfield\fB=\fIvalue\fR
.IQ \fIfield\fB[\fIstart\fB..\fIend\fB]=\fIsrc\fB[\fIstart\fB..\fIend\fB]\fR
.IQ \fIfield\fB[\fIstart\fB..\fIend\fB]\fR
This commit adds a new feature to the learn actions: the possibility to limit the number of learned flows. To be compatible with users of the old learn action, a new structure is introduced as well as a new OpenFlow raw action number. This commit only implements parsing of the action and documentation. A later commit will implement the feature in ofproto-dpif. Signed-off-by: Daniele Di Proietto <diproiettod@vmware.com> --- include/openvswitch/ofp-actions.h | 12 ++++ lib/learn.c | 28 ++++++++ lib/ofp-actions.c | 135 ++++++++++++++++++++++++++++++++++---- tests/ofp-actions.at | 14 ++++ utilities/ovs-ofctl.8.in | 19 ++++++ 5 files changed, 197 insertions(+), 11 deletions(-)