+/* Converts 'learn' into a "struct nx_action_learn" and appends that action to
+ * 'ofpacts'. */
+void
+learn_to_nxast(const struct ofpact_learn *learn, struct ofpbuf *openflow)
+{
+ const struct ofpact_learn_spec *spec;
+ struct nx_action_learn *nal;
+ size_t start_ofs;
+
+ start_ofs = ofpbuf_size(openflow);
+ nal = ofputil_put_NXAST_LEARN(openflow);
+ 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 = htonll(learn->cookie);
+ nal->flags = htons(learn->flags);
+ nal->table_id = learn->table_id;
+
+ for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+ put_u16(openflow, spec->n_bits | spec->dst_type | spec->src_type);
+
+ if (spec->src_type == NX_LEARN_SRC_FIELD) {
+ put_u32(openflow, spec->src.field->nxm_header);
+ put_u16(openflow, spec->src.ofs);
+ } else {
+ size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
+ uint8_t *bits = ofpbuf_put_zeros(openflow, n_dst_bytes);
+ bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0,
+ bits, n_dst_bytes, 0,
+ spec->n_bits);
+ }
+
+ if (spec->dst_type == NX_LEARN_DST_MATCH ||
+ spec->dst_type == NX_LEARN_DST_LOAD) {
+ put_u32(openflow, spec->dst.field->nxm_header);
+ put_u16(openflow, spec->dst.ofs);
+ }
+ }
+
+ if ((ofpbuf_size(openflow) - start_ofs) % 8) {
+ ofpbuf_put_zeros(openflow, 8 - (ofpbuf_size(openflow) - start_ofs) % 8);
+ }
+
+ nal = ofpbuf_at_assert(openflow, start_ofs, sizeof *nal);
+ nal->len = htons(ofpbuf_size(openflow) - start_ofs);
+}
+
+/* Composes 'fm' so that executing it will implement 'learn' given that the
+ * packet being processed has 'flow' as its flow.
+ *
+ * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize
+ * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the
+ * 'ofpacts' buffer.
+ *
+ * The caller has to actually execute 'fm'. */
+void
+learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
+ struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts)
+{
+ const struct ofpact_learn_spec *spec;
+
+ match_init_catchall(&fm->match);
+ fm->priority = learn->priority;
+ fm->cookie = htonll(0);
+ fm->cookie_mask = htonll(0);
+ fm->new_cookie = htonll(learn->cookie);
+ fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX;
+ fm->table_id = learn->table_id;
+ fm->command = OFPFC_MODIFY_STRICT;
+ fm->idle_timeout = learn->idle_timeout;
+ fm->hard_timeout = learn->hard_timeout;
+ fm->buffer_id = UINT32_MAX;
+ fm->out_port = OFPP_NONE;
+ fm->flags = learn->flags;
+ fm->ofpacts = NULL;
+ fm->ofpacts_len = 0;
+
+ if (learn->fin_idle_timeout || learn->fin_hard_timeout) {
+ struct ofpact_fin_timeout *oft;
+
+ oft = ofpact_put_FIN_TIMEOUT(ofpacts);
+ oft->fin_idle_timeout = learn->fin_idle_timeout;
+ oft->fin_hard_timeout = learn->fin_hard_timeout;
+ }
+
+ for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+ union mf_subvalue value;
+ int chunk, ofs;
+
+ if (spec->src_type == NX_LEARN_SRC_FIELD) {
+ mf_read_subfield(&spec->src, flow, &value);
+ } else {
+ value = spec->src_imm;
+ }
+
+ switch (spec->dst_type) {
+ case NX_LEARN_DST_MATCH:
+ mf_write_subfield(&spec->dst, &value, &fm->match);
+ break;
+
+ case NX_LEARN_DST_LOAD:
+ for (ofs = 0; ofs < spec->n_bits; ofs += chunk) {
+ struct ofpact_reg_load *load;
+
+ chunk = MIN(spec->n_bits - ofs, 64);
+
+ load = ofpact_put_REG_LOAD(ofpacts);
+ load->dst.field = spec->dst.field;
+ load->dst.ofs = spec->dst.ofs + ofs;
+ load->dst.n_bits = chunk;
+ bitwise_copy(&value, sizeof value, ofs,
+ &load->subvalue, sizeof load->subvalue, 0,
+ chunk);
+ }
+ break;
+
+ case NX_LEARN_DST_OUTPUT:
+ if (spec->n_bits <= 16
+ || is_all_zeros(value.u8, sizeof value - 2)) {
+ ofp_port_t port = u16_to_ofp(ntohs(value.be16[7]));
+
+ if (ofp_to_u16(port) < ofp_to_u16(OFPP_MAX)
+ || port == OFPP_IN_PORT
+ || port == OFPP_FLOOD
+ || port == OFPP_LOCAL
+ || port == OFPP_ALL) {
+ ofpact_put_OUTPUT(ofpacts)->port = port;
+ }
+ }
+ break;
+ }
+ }
+ ofpact_pad(ofpacts);
+
+ fm->ofpacts = ofpbuf_data(ofpacts);
+ fm->ofpacts_len = ofpbuf_size(ofpacts);
+}
+
+/* Perform a bitwise-OR on 'wc''s fields that are relevant as sources in
+ * the learn action 'learn'. */
+void
+learn_mask(const struct ofpact_learn *learn, struct flow_wildcards *wc)
+{
+ const struct ofpact_learn_spec *spec;
+ union mf_subvalue value;