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 <[email protected]> --- 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(-) diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h index 88f573dcd..c1199a4ec 100644 --- a/include/openvswitch/ofp-actions.h +++ b/include/openvswitch/ofp-actions.h @@ -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[]; diff --git a/lib/learn.c b/lib/learn.c index ce52c35f2..b1b8bc52b 100644 --- a/lib/learn.c +++ b/lib/learn.c @@ -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); diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 78f8c4366..1c77e7bbd 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -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); diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at index 6384c48d9..694afc9f3 100644 --- a/tests/ofp-actions.at +++ b/tests/ofp-actions.at @@ -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 diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 2ee3193d4..85a4f574f 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -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 -- 2.11.0 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
