+ struct dp_netdev_execute_aux *aux = aux_;
+ int type = nl_attr_type(a);
+ struct dp_netdev_port *p;
+ uint32_t *depth = recirc_depth_get();
+
+ switch ((enum ovs_action_attr)type) {
+ case OVS_ACTION_ATTR_OUTPUT:
+ p = dp_netdev_lookup_port(aux->dp, u32_to_odp(nl_attr_get_u32(a)));
+ if (p) {
+ netdev_send(p->netdev, packet, may_steal);
+ }
+ break;
+
+ case OVS_ACTION_ATTR_USERSPACE: {
+ const struct nlattr *userdata;
+
+ userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
+
+ dp_netdev_output_userspace(aux->dp, packet,
+ miniflow_hash_5tuple(aux->key, 0)
+ % aux->dp->n_handlers,
+ DPIF_UC_ACTION, aux->key,
+ userdata);
+
+ if (may_steal) {
+ ofpbuf_delete(packet);
+ }
+ break;
+ }
+
+ case OVS_ACTION_ATTR_HASH: {
+ const struct ovs_action_hash *hash_act;
+ uint32_t hash;
+
+ hash_act = nl_attr_get(a);
+ if (hash_act->hash_alg == OVS_HASH_ALG_L4) {
+ /* Hash need not be symmetric, nor does it need to include
+ * L2 fields. */
+ hash = miniflow_hash_5tuple(aux->key, hash_act->hash_basis);
+ if (!hash) {
+ hash = 1; /* 0 is not valid */
+ }
+
+ } else {
+ VLOG_WARN("Unknown hash algorithm specified for the hash action.");
+ hash = 2;
+ }
+
+ md->dp_hash = hash;
+ break;
+ }
+
+ case OVS_ACTION_ATTR_RECIRC:
+ if (*depth < MAX_RECIRC_DEPTH) {
+ struct pkt_metadata recirc_md = *md;
+ struct ofpbuf *recirc_packet;
+
+ recirc_packet = may_steal ? packet : ofpbuf_clone(packet);
+ recirc_md.recirc_id = nl_attr_get_u32(a);
+
+ (*depth)++;
+ dp_netdev_input(aux->dp, recirc_packet, &recirc_md);
+ (*depth)--;
+
+ break;
+ } else {
+ VLOG_WARN("Packet dropped. Max recirculation depth exceeded.");
+ }
+ break;
+
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ case OVS_ACTION_ATTR_POP_VLAN:
+ case OVS_ACTION_ATTR_PUSH_MPLS:
+ case OVS_ACTION_ATTR_POP_MPLS:
+ case OVS_ACTION_ATTR_SET:
+ case OVS_ACTION_ATTR_SAMPLE:
+ case OVS_ACTION_ATTR_UNSPEC:
+ case __OVS_ACTION_ATTR_MAX:
+ OVS_NOT_REACHED();
+ }