+ 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;
+
+ memset(&value, 0xff, sizeof value);
+ for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
+ if (spec->src_type == NX_LEARN_SRC_FIELD) {
+ mf_write_subfield_flow(&spec->src, &value, &wc->masks);
+ }
+ }
+}
+
+/* Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT
+learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
+{
+ const char *full_s = s;
+ const char *arrow = strstr(s, "->");
+ struct mf_subfield dst;
+ union mf_subvalue imm;
+ char *error;
+
+ memset(&imm, 0, sizeof imm);
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && arrow) {
+ const char *in = arrow - 1;
+ uint8_t *out = imm.u8 + sizeof imm.u8 - 1;
+ int n = arrow - (s + 2);
+ int i;
+
+ for (i = 0; i < n; i++) {
+ int hexit = hexit_value(in[-i]);
+ if (hexit < 0) {
+ return xasprintf("%s: bad hex digit in value", full_s);
+ }
+ out[-(i / 2)] |= i % 2 ? hexit << 4 : hexit;
+ }
+ s = arrow;
+ } else {
+ imm.be64[1] = htonll(strtoull(s, (char **) &s, 0));
+ }
+
+ if (strncmp(s, "->", 2)) {
+ return xasprintf("%s: missing `->' following value", full_s);
+ }
+ s += 2;
+
+ error = mf_parse_subfield(&dst, s);
+ if (error) {
+ return error;
+ }
+
+ if (!bitwise_is_all_zeros(&imm, sizeof imm, dst.n_bits,
+ (8 * sizeof imm) - dst.n_bits)) {
+ return xasprintf("%s: value does not fit into %u bits",
+ full_s, dst.n_bits);
+ }
+
+ spec->n_bits = dst.n_bits;
+ spec->src_type = NX_LEARN_SRC_IMMEDIATE;
+ spec->src_imm = imm;
+ spec->dst_type = NX_LEARN_DST_LOAD;
+ spec->dst = dst;
+ return NULL;
+}
+
+/* Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+static char * WARN_UNUSED_RESULT