+ return 0;
+}
+
+static void
+put_be16(struct ofpbuf *b, ovs_be16 x)
+{
+ ofpbuf_put(b, &x, sizeof x);
+}
+
+static void
+put_be32(struct ofpbuf *b, ovs_be32 x)
+{
+ ofpbuf_put(b, &x, sizeof x);
+}
+
+static void
+put_u16(struct ofpbuf *b, uint16_t x)
+{
+ put_be16(b, htons(x));
+}
+
+static void
+put_u32(struct ofpbuf *b, uint32_t x)
+{
+ put_be32(b, htonl(x));
+}
+
+/* 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);
+ }