+static int
+encode_packet_in_reason(enum ofp_packet_in_reason reason,
+ enum ofp_version version)
+{
+ switch (reason) {
+ case OFPR_NO_MATCH:
+ case OFPR_ACTION:
+ case OFPR_INVALID_TTL:
+ return reason;
+
+ case OFPR_ACTION_SET:
+ case OFPR_GROUP:
+ case OFPR_PACKET_OUT:
+ return version < OFP14_VERSION ? OFPR_ACTION : reason;
+
+ case OFPR_EXPLICIT_MISS:
+ return version < OFP13_VERSION ? OFPR_ACTION : OFPR_NO_MATCH;
+
+ case OFPR_IMPLICIT_MISS:
+ return OFPR_NO_MATCH;
+
+ case OFPR_N_REASONS:
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
+/* Only NXT_PACKET_IN2 (not NXT_RESUME) should include NXCPT_USERDATA, so this
+ * function omits it. The caller can add it itself if desired. */
+static void
+ofputil_put_packet_in(const struct ofputil_packet_in *pin,
+ enum ofp_version version, uint32_t buffer_id,
+ size_t include_bytes, struct ofpbuf *msg)
+{
+ /* Add packet properties. */
+ ofpprop_put(msg, NXPINT_PACKET, pin->packet, include_bytes);
+ if (include_bytes != pin->packet_len) {
+ ofpprop_put_u32(msg, NXPINT_FULL_LEN, pin->packet_len);
+ }
+ if (buffer_id != UINT32_MAX) {
+ ofpprop_put_u32(msg, NXPINT_BUFFER_ID, buffer_id);
+ }
+
+ /* Add flow properties. */
+ ofpprop_put_u8(msg, NXPINT_TABLE_ID, pin->table_id);
+ if (pin->cookie != OVS_BE64_MAX) {
+ ofpprop_put_be64(msg, NXPINT_COOKIE, pin->cookie);
+ }
+
+ /* Add other properties. */
+ ofpprop_put_u8(msg, NXPINT_REASON,
+ encode_packet_in_reason(pin->reason, version));
+
+ size_t start = ofpprop_start(msg, NXPINT_METADATA);
+ oxm_put_raw(msg, &pin->flow_metadata, version);
+ ofpprop_end(msg, start);
+}
+
+static void
+put_actions_property(struct ofpbuf *msg, uint64_t prop_type,
+ enum ofp_version version,
+ const struct ofpact *actions, size_t actions_len)
+{
+ if (actions_len) {
+ size_t start = ofpprop_start_nested(msg, prop_type);
+ ofpacts_put_openflow_actions(actions, actions_len, msg, version);
+ ofpprop_end(msg, start);
+ }
+}
+
+enum nx_continuation_prop_type {
+ NXCPT_BRIDGE = 0x8000,
+ NXCPT_STACK,
+ NXCPT_MIRRORS,
+ NXCPT_CONNTRACKED,
+ NXCPT_TABLE_ID,
+ NXCPT_COOKIE,
+ NXCPT_ACTIONS,
+ NXCPT_ACTION_SET,
+};
+
+/* Only NXT_PACKET_IN2 (not NXT_RESUME) should include NXCPT_USERDATA, so this
+ * function omits it. The caller can add it itself if desired. */
+static void
+ofputil_put_packet_in_private(const struct ofputil_packet_in_private *pin,
+ enum ofp_version version, uint32_t buffer_id,
+ size_t include_bytes, struct ofpbuf *msg)
+{
+ ofputil_put_packet_in(&pin->public, version, buffer_id,
+ include_bytes, msg);
+
+ size_t continuation_ofs = ofpprop_start_nested(msg, NXPINT_CONTINUATION);
+ size_t inner_ofs = msg->size;
+
+ if (!uuid_is_zero(&pin->bridge)) {
+ ofpprop_put_uuid(msg, NXCPT_BRIDGE, &pin->bridge);
+ }
+
+ for (size_t i = 0; i < pin->n_stack; i++) {
+ const union mf_subvalue *s = &pin->stack[i];
+ size_t ofs;
+ for (ofs = 0; ofs < sizeof *s; ofs++) {
+ if (s->u8[ofs]) {
+ break;
+ }
+ }
+
+ ofpprop_put(msg, NXCPT_STACK, &s->u8[ofs], sizeof *s - ofs);
+ }
+
+ if (pin->mirrors) {
+ ofpprop_put_u32(msg, NXCPT_MIRRORS, pin->mirrors);
+ }
+
+ if (pin->conntracked) {
+ ofpprop_put_flag(msg, NXCPT_CONNTRACKED);
+ }
+
+ if (pin->actions_len) {
+ /* Divide 'pin->actions' into groups that begins with an
+ * unroll_xlate action. For each group, emit a NXCPT_TABLE_ID and
+ * NXCPT_COOKIE property (if either has changed; each is initially
+ * assumed 0), then a NXCPT_ACTIONS property with the grouped
+ * actions.
+ *
+ * The alternative is to make OFPACT_UNROLL_XLATE public. We can
+ * always do that later, since this is a private property. */
+ const struct ofpact *const end = ofpact_end(pin->actions,
+ pin->actions_len);
+ const struct ofpact_unroll_xlate *unroll = NULL;
+ uint8_t table_id = 0;
+ ovs_be64 cookie = 0;
+
+ const struct ofpact *a;
+ for (a = pin->actions; ; a = ofpact_next(a)) {
+ if (a == end || a->type == OFPACT_UNROLL_XLATE) {
+ if (unroll) {
+ if (table_id != unroll->rule_table_id) {
+ ofpprop_put_u8(msg, NXCPT_TABLE_ID,
+ unroll->rule_table_id);
+ table_id = unroll->rule_table_id;
+ }
+ if (cookie != unroll->rule_cookie) {
+ ofpprop_put_be64(msg, NXCPT_COOKIE,
+ unroll->rule_cookie);
+ cookie = unroll->rule_cookie;
+ }
+ }
+
+ const struct ofpact *start
+ = unroll ? ofpact_next(&unroll->ofpact) : pin->actions;
+ put_actions_property(msg, NXCPT_ACTIONS, version,
+ start, (a - start) * sizeof *a);
+
+ if (a == end) {
+ break;
+ }
+ unroll = ofpact_get_UNROLL_XLATE(a);
+ }
+ }
+ }
+
+ if (pin->action_set_len) {
+ size_t start = ofpprop_start_nested(msg, NXCPT_ACTION_SET);
+ ofpacts_put_openflow_actions(pin->action_set,
+ pin->action_set_len, msg, version);
+ ofpprop_end(msg, start);
+ }
+
+ if (msg->size > inner_ofs) {
+ ofpprop_end(msg, continuation_ofs);
+ } else {
+ msg->size = continuation_ofs;
+ }
+}
+