+/* 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;
+ }