ofproto-dpif-xlate: Remove obsolete comment.
[sliver-openvswitch.git] / ofproto / ofproto-dpif-xlate.c
index cced7cc..f5bc12c 100644 (file)
@@ -56,6 +56,10 @@ VLOG_DEFINE_THIS_MODULE(ofproto_dpif_xlate);
  * flow translation. */
 #define MAX_RESUBMIT_RECURSION 64
 
+/* Maximum number of resubmit actions in a flow translation, whether they are
+ * recursive or not. */
+#define MAX_RESUBMITS (MAX_RESUBMIT_RECURSION * MAX_RESUBMIT_RECURSION)
+
 struct ovs_rwlock xlate_rwlock = OVS_RWLOCK_INITIALIZER;
 
 struct xbridge {
@@ -165,13 +169,25 @@ struct xlate_ctx {
                                  * prior to an mpls_push so that it may be
                                  * used for a subsequent mpls_pop. */
 
-    int recurse;                /* Recursion level, via xlate_table_action. */
+    /* Resubmit statistics, via xlate_table_action(). */
+    int recurse;                /* Current resubmit nesting depth. */
+    int resubmits;              /* Total number of resubmits. */
+
     uint32_t orig_skb_priority; /* Priority when packet arrived. */
     uint8_t table_id;           /* OpenFlow table ID where flow was found. */
     uint32_t sflow_n_outputs;   /* Number of output ports. */
     odp_port_t sflow_odp_port;  /* Output port for composing sFlow action. */
     uint16_t user_cookie_offset;/* Used for user_action_cookie fixup. */
     bool exit;                  /* No further actions should be processed. */
+
+    /* OpenFlow 1.1+ action set.
+     *
+     * 'action_set' accumulates "struct ofpact"s added by OFPACT_WRITE_ACTIONS.
+     * When translation is otherwise complete, ofpacts_execute_action_set()
+     * converts it to a set of "struct ofpact"s that can be translated into
+     * datapath actions.   */
+    struct ofpbuf action_set;   /* Action set. */
+    uint64_t action_set_stub[1024 / 8];
 };
 
 /* A controller may use OFPP_NONE as the ingress port to indicate that
@@ -199,6 +215,8 @@ static struct hmap xports = HMAP_INITIALIZER(&xports);
 static bool may_receive(const struct xport *, struct xlate_ctx *);
 static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len,
                              struct xlate_ctx *);
+static void xlate_actions__(struct xlate_in *, struct xlate_out *)
+    OVS_REQ_RDLOCK(xlate_rwlock);
 static void xlate_normal(struct xlate_ctx *);
 static void xlate_report(struct xlate_ctx *, const char *);
 static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port,
@@ -1541,7 +1559,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
 
     /* If 'struct flow' gets additional metadata, we'll need to zero it out
      * before traversing a patch port. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 21);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 22);
 
     if (!xport) {
         xlate_report(ctx, "Nonexistent output port");
@@ -1573,7 +1591,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
         special = process_special(ctx, &ctx->xin->flow, peer,
                                   ctx->xin->packet);
         if (special) {
-            ctx->xout->slow = special;
+            ctx->xout->slow |= special;
         } else if (may_receive(peer, ctx)) {
             if (xport_stp_forward_state(peer)) {
                 xlate_table_action(ctx, flow->in_port.ofp_port, 0, true);
@@ -1651,9 +1669,10 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     }
 
     if (out_port != ODPP_NONE) {
-        commit_odp_actions(flow, &ctx->base_flow,
-                           &ctx->xout->odp_actions, &ctx->xout->wc,
-                           &ctx->mpls_depth_delta);
+        ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
+                                              &ctx->xout->odp_actions,
+                                              &ctx->xout->wc,
+                                              &ctx->mpls_depth_delta);
         nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT,
                             out_port);
 
@@ -1685,6 +1704,7 @@ xlate_recursively(struct xlate_ctx *ctx, struct rule_dpif *rule)
         rule_dpif_credit_stats(rule, ctx->xin->resubmit_stats);
     }
 
+    ctx->resubmits++;
     ctx->recurse++;
     ctx->rule = rule;
     actions = rule_dpif_get_actions(rule);
@@ -1698,7 +1718,18 @@ static void
 xlate_table_action(struct xlate_ctx *ctx,
                    ofp_port_t in_port, uint8_t table_id, bool may_packet_in)
 {
-    if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+    if (ctx->recurse >= MAX_RESUBMIT_RECURSION) {
+        VLOG_ERR_RL(&rl, "resubmit actions recursed over %d times",
+                    MAX_RESUBMIT_RECURSION);
+    } else if (ctx->resubmits >= MAX_RESUBMITS) {
+        VLOG_ERR_RL(&rl, "over %d resubmit actions", MAX_RESUBMITS);
+    } else if (ctx->xout->odp_actions.size > UINT16_MAX) {
+        VLOG_ERR_RL(&rl, "resubmits yielded over 64 kB of actions");
+    } else if (ctx->stack.size >= 65536) {
+        VLOG_ERR_RL(&rl, "resubmits yielded over 64 kB of stack");
+    } else {
         struct rule_dpif *rule;
         ofp_port_t old_in_port = ctx->xin->flow.in_port.ofp_port;
         uint8_t old_table_id = ctx->table_id;
@@ -1738,12 +1769,10 @@ xlate_table_action(struct xlate_ctx *ctx,
         }
 
         ctx->table_id = old_table_id;
-    } else {
-        static struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
-
-        VLOG_ERR_RL(&recurse_rl, "resubmit actions recursed over %d times",
-                    MAX_RESUBMIT_RECURSION);
+        return;
     }
+
+    ctx->exit = true;
 }
 
 static void
@@ -1791,12 +1820,11 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
                           enum ofp_packet_in_reason reason,
                           uint16_t controller_id)
 {
-    struct ofputil_packet_in *pin;
+    struct ofproto_packet_in *pin;
     struct ofpbuf *packet;
     struct flow key;
 
-    ovs_assert(!ctx->xout->slow || ctx->xout->slow == SLOW_CONTROLLER);
-    ctx->xout->slow = SLOW_CONTROLLER;
+    ctx->xout->slow |= SLOW_CONTROLLER;
     if (!ctx->xin->packet) {
         return;
     }
@@ -1807,24 +1835,29 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
     key.pkt_mark = 0;
     memset(&key.tunnel, 0, sizeof key.tunnel);
 
-    commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
-                       &ctx->xout->odp_actions, &ctx->xout->wc,
-                       &ctx->mpls_depth_delta);
+    ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
+                                          &ctx->xout->odp_actions,
+                                          &ctx->xout->wc,
+                                          &ctx->mpls_depth_delta);
 
     odp_execute_actions(NULL, packet, &key, ctx->xout->odp_actions.data,
                         ctx->xout->odp_actions.size, NULL, NULL);
 
     pin = xmalloc(sizeof *pin);
-    pin->packet_len = packet->size;
-    pin->packet = ofpbuf_steal_data(packet);
-    pin->reason = reason;
-    pin->controller_id = controller_id;
-    pin->table_id = ctx->table_id;
-    pin->cookie = ctx->rule ? rule_dpif_get_flow_cookie(ctx->rule) : 0;
+    pin->up.packet_len = packet->size;
+    pin->up.packet = ofpbuf_steal_data(packet);
+    pin->up.reason = reason;
+    pin->up.table_id = ctx->table_id;
+    pin->up.cookie = (ctx->rule
+                      ? rule_dpif_get_flow_cookie(ctx->rule)
+                      : OVS_BE64_MAX);
 
-    pin->send_len = len;
-    flow_get_metadata(&ctx->xin->flow, &pin->fmd);
+    flow_get_metadata(&ctx->xin->flow, &pin->up.fmd);
 
+    pin->controller_id = controller_id;
+    pin->send_len = len;
+    pin->generated_by_table_miss = (ctx->rule
+                                    && rule_dpif_is_table_miss(ctx->rule));
     ofproto_dpif_send_packet_in(ctx->xbridge->ofproto, pin);
     ofpbuf_delete(packet);
 }
@@ -2195,9 +2228,10 @@ xlate_sample_action(struct xlate_ctx *ctx,
    * the same percentage. */
   uint32_t probability = (os->probability << 16) | os->probability;
 
-  commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
-                     &ctx->xout->odp_actions, &ctx->xout->wc,
-                     &ctx->mpls_depth_delta);
+  ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
+                                        &ctx->xout->odp_actions,
+                                        &ctx->xout->wc,
+                                        &ctx->mpls_depth_delta);
 
   compose_flow_sample_cookie(os->probability, os->collector_set_id,
                              os->obs_domain_id, os->obs_point_id, &cookie);
@@ -2225,6 +2259,26 @@ may_receive(const struct xport *xport, struct xlate_ctx *ctx)
     return true;
 }
 
+static void
+xlate_write_actions(struct xlate_ctx *ctx, const struct ofpact *a)
+{
+    struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a);
+    ofpbuf_put(&ctx->action_set, on->actions, ofpact_nest_get_action_len(on));
+    ofpact_pad(&ctx->action_set);
+}
+
+static void
+xlate_action_set(struct xlate_ctx *ctx)
+{
+    uint64_t action_list_stub[1024 / 64];
+    struct ofpbuf action_list;
+
+    ofpbuf_use_stub(&action_list, action_list_stub, sizeof action_list_stub);
+    ofpacts_execute_action_set(&action_list, &ctx->action_set);
+    do_xlate_actions(action_list.data, action_list.size, ctx);
+    ofpbuf_uninit(&action_list);
+}
+
 static void
 do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
                  struct xlate_ctx *ctx)
@@ -2233,6 +2287,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
     struct flow *flow = &ctx->xin->flow;
     const struct ofpact *a;
 
+    /* dl_type already in the mask, not set below. */
+
     OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
         struct ofpact_controller *controller;
         const struct ofpact_metadata *metadata;
@@ -2264,17 +2320,22 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
 
         case OFPACT_SET_VLAN_VID:
             wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
-            flow->vlan_tci &= ~htons(VLAN_VID_MASK);
-            flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
-                               | htons(VLAN_CFI));
+            if (flow->vlan_tci & htons(VLAN_CFI) ||
+                ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
+                flow->vlan_tci &= ~htons(VLAN_VID_MASK);
+                flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
+                                   | htons(VLAN_CFI));
+            }
             break;
 
         case OFPACT_SET_VLAN_PCP:
             wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
-            flow->vlan_tci &= ~htons(VLAN_PCP_MASK);
-            flow->vlan_tci |=
-                htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp << VLAN_PCP_SHIFT)
-                      | VLAN_CFI);
+            if (flow->vlan_tci & htons(VLAN_CFI) ||
+                ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
+                flow->vlan_tci &= ~htons(VLAN_PCP_MASK);
+                flow->vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp
+                                         << VLAN_PCP_SHIFT) | VLAN_CFI);
+            }
             break;
 
         case OFPACT_STRIP_VLAN:
@@ -2299,40 +2360,54 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_SET_IPV4_SRC:
-            memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
             if (flow->dl_type == htons(ETH_TYPE_IP)) {
+                memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
                 flow->nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
             }
             break;
 
         case OFPACT_SET_IPV4_DST:
-            memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
             if (flow->dl_type == htons(ETH_TYPE_IP)) {
+                memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
                 flow->nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
             }
             break;
 
-        case OFPACT_SET_IPV4_DSCP:
-            wc->masks.nw_tos |= IP_DSCP_MASK;
-            /* OpenFlow 1.0 only supports IPv4. */
-            if (flow->dl_type == htons(ETH_TYPE_IP)) {
+        case OFPACT_SET_IP_DSCP:
+            if (is_ip_any(flow)) {
+                wc->masks.nw_tos |= IP_DSCP_MASK;
                 flow->nw_tos &= ~IP_DSCP_MASK;
-                flow->nw_tos |= ofpact_get_SET_IPV4_DSCP(a)->dscp;
+                flow->nw_tos |= ofpact_get_SET_IP_DSCP(a)->dscp;
+            }
+            break;
+
+        case OFPACT_SET_IP_ECN:
+            if (is_ip_any(flow)) {
+                wc->masks.nw_tos |= IP_ECN_MASK;
+                flow->nw_tos &= ~IP_ECN_MASK;
+                flow->nw_tos |= ofpact_get_SET_IP_ECN(a)->ecn;
+            }
+            break;
+
+        case OFPACT_SET_IP_TTL:
+            if (is_ip_any(flow)) {
+                wc->masks.nw_ttl = 0xff;
+                flow->nw_ttl = ofpact_get_SET_IP_TTL(a)->ttl;
             }
             break;
 
         case OFPACT_SET_L4_SRC_PORT:
-            memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
-            memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
             if (is_ip_any(flow)) {
+                memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+                memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
                 flow->tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
             }
             break;
 
         case OFPACT_SET_L4_DST_PORT:
-            memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
-            memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
             if (is_ip_any(flow)) {
+                memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+                memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
                 flow->tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
             }
             break;
@@ -2358,7 +2433,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_REG_LOAD:
-            nxm_execute_reg_load(ofpact_get_REG_LOAD(a), flow);
+            nxm_execute_reg_load(ofpact_get_REG_LOAD(a), flow, wc);
             break;
 
         case OFPACT_STACK_PUSH:
@@ -2436,11 +2511,11 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_CLEAR_ACTIONS:
-            /* XXX
-             * Nothing to do because writa-actions is not supported for now.
-             * When writa-actions is supported, clear-actions also must
-             * be supported at the same time.
-             */
+            ofpbuf_clear(&ctx->action_set);
+            break;
+
+        case OFPACT_WRITE_ACTIONS:
+            xlate_write_actions(ctx, a);
             break;
 
         case OFPACT_WRITE_METADATA:
@@ -2454,7 +2529,6 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
             break;
 
         case OFPACT_GOTO_TABLE: {
-            /* It is assumed that goto-table is the last action. */
             struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a);
 
             ovs_assert(ctx->table_id < ogt->table_id);
@@ -2473,7 +2547,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
 void
 xlate_in_init(struct xlate_in *xin, struct ofproto_dpif *ofproto,
               const struct flow *flow, struct rule_dpif *rule,
-              uint8_t tcp_flags, const struct ofpbuf *packet)
+              uint16_t tcp_flags, const struct ofpbuf *packet)
 {
     xin->ofproto = ofproto;
     xin->flow = *flow;
@@ -2622,16 +2696,27 @@ actions_output_to_local_port(const struct xlate_ctx *ctx)
     return false;
 }
 
+/* Thread safe call to xlate_actions__(). */
+void
+xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
+{
+    ovs_rwlock_rdlock(&xlate_rwlock);
+    xlate_actions__(xin, xout);
+    ovs_rwlock_unlock(&xlate_rwlock);
+}
+
 /* Translates the 'ofpacts_len' bytes of "struct ofpacts" starting at 'ofpacts'
  * into datapath actions in 'odp_actions', using 'ctx'.
  *
  * The caller must take responsibility for eventually freeing 'xout', with
  * xlate_out_uninit(). */
-void
-xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
+static void
+xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
+    OVS_REQ_RDLOCK(xlate_rwlock)
 {
     struct flow_wildcards *wc = &xout->wc;
     struct flow *flow = &xin->flow;
+    struct rule_dpif *rule = NULL;
 
     struct rule_actions *actions = NULL;
     enum slow_path_reason special;
@@ -2644,8 +2729,6 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
 
     COVERAGE_INC(xlate_actions);
 
-    ovs_rwlock_rdlock(&xlate_rwlock);
-
     /* Flow initialization rules:
      * - 'base_flow' must match the kernel's view of the packet at the
      *   time that action processing starts.  'flow' represents any
@@ -2702,16 +2785,26 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
     }
 
     ctx.recurse = 0;
+    ctx.resubmits = 0;
     ctx.orig_skb_priority = flow->skb_priority;
     ctx.table_id = 0;
     ctx.exit = false;
     ctx.mpls_depth_delta = 0;
 
+    if (!xin->ofpacts && !ctx.rule) {
+        rule_dpif_lookup(ctx.xbridge->ofproto, flow, wc, &rule);
+        if (ctx.xin->resubmit_stats) {
+            rule_dpif_credit_stats(rule, ctx.xin->resubmit_stats);
+        }
+        ctx.rule = rule;
+    }
+    xout->fail_open = ctx.rule && rule_dpif_is_fail_open(ctx.rule);
+
     if (xin->ofpacts) {
         ofpacts = xin->ofpacts;
         ofpacts_len = xin->ofpacts_len;
-    } else if (xin->rule) {
-        actions = rule_dpif_get_actions(xin->rule);
+    } else if (ctx.rule) {
+        actions = rule_dpif_get_actions(ctx.rule);
         ofpacts = actions->ofpacts;
         ofpacts_len = actions->ofpacts_len;
     } else {
@@ -2719,6 +2812,8 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
     }
 
     ofpbuf_use_stub(&ctx.stack, ctx.init_stack, sizeof ctx.init_stack);
+    ofpbuf_use_stub(&ctx.action_set,
+                    ctx.action_set_stub, sizeof ctx.action_set_stub);
 
     if (mbridge_has_mirrors(ctx.xbridge->mbridge)) {
         /* Do this conditionally because the copy is expensive enough that it
@@ -2752,7 +2847,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
     in_port = get_ofp_port(ctx.xbridge, flow->in_port.ofp_port);
     special = process_special(&ctx, flow, in_port, ctx.xin->packet);
     if (special) {
-        ctx.xout->slow = special;
+        ctx.xout->slow |= special;
     } else {
         size_t sample_actions_len;
 
@@ -2777,6 +2872,10 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
             }
         }
 
+        if (ctx.action_set.size) {
+            xlate_action_set(&ctx);
+        }
+
         if (ctx.xbridge->has_in_band
             && in_band_must_output_to_local_port(flow)
             && !actions_output_to_local_port(&ctx)) {
@@ -2790,7 +2889,17 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
         }
     }
 
+    if (nl_attr_oversized(ctx.xout->odp_actions.size)) {
+        /* These datapath actions are too big for a Netlink attribute, so we
+         * can't execute them. */
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+
+        VLOG_ERR_RL(&rl, "discarding oversize datapath actions");
+        ofpbuf_clear(&ctx.xout->odp_actions);
+    }
+
     ofpbuf_uninit(&ctx.stack);
+    ofpbuf_uninit(&ctx.action_set);
 
     /* Clear the metadata and register wildcard masks, because we won't
      * use non-header fields as part of the cache. */
@@ -2798,7 +2907,38 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
     memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
 
 out:
-    ovs_rwlock_unlock(&xlate_rwlock);
-
     rule_actions_unref(actions);
+    rule_dpif_unref(rule);
+}
+
+/* Sends 'packet' out 'ofport'.
+ * May modify 'packet'.
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+xlate_send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
+{
+    struct xport *xport;
+    struct ofpact_output output;
+    struct flow flow;
+    union flow_in_port in_port_;
+    int error;
+
+    ofpact_init(&output.ofpact, OFPACT_OUTPUT, sizeof output);
+    /* Use OFPP_NONE as the in_port to avoid special packet processing. */
+    in_port_.ofp_port = OFPP_NONE;
+    flow_extract(packet, 0, 0, NULL, &in_port_, &flow);
+
+    ovs_rwlock_rdlock(&xlate_rwlock);
+    xport = xport_lookup(ofport);
+    if (!xport) {
+        ovs_rwlock_unlock(&xlate_rwlock);
+        return EINVAL;
+    }
+    output.port = xport->ofp_port;
+    output.max_len = 0;
+    error =  ofproto_dpif_execute_actions(xport->xbridge->ofproto, &flow, NULL,
+                                          &output.ofpact, sizeof output,
+                                          packet);
+    ovs_rwlock_unlock(&xlate_rwlock);
+    return error;
 }