+/* Fix SAMPLE action according to data collected while composing ODP actions.
+ * We need to fix SAMPLE actions OVS_SAMPLE_ATTR_ACTIONS attribute, i.e. nested
+ * USERSPACE action's user-cookie which is required for sflow. */
+static void
+fix_sflow_action(struct action_xlate_ctx *ctx)
+{
+ const struct flow *base = &ctx->base_flow;
+ struct user_action_cookie *cookie;
+
+ if (!ctx->user_cookie_offset) {
+ return;
+ }
+
+ cookie = ofpbuf_at(ctx->odp_actions, ctx->user_cookie_offset,
+ sizeof(*cookie));
+ assert(cookie != NULL);
+ assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
+
+ if (ctx->sflow_n_outputs) {
+ cookie->data = dpif_sflow_odp_port_to_ifindex(ctx->ofproto->sflow,
+ ctx->sflow_odp_port);
+ }
+ if (ctx->sflow_n_outputs >= 255) {
+ cookie->n_output = 255;
+ } else {
+ cookie->n_output = ctx->sflow_n_outputs;
+ }
+ cookie->vlan_tci = base->vlan_tci;
+}
+
+static void
+commit_vlan_tci(struct action_xlate_ctx *ctx, ovs_be16 vlan_tci)
+{
+ struct flow *base = &ctx->base_flow;
+ struct ofpbuf *odp_actions = ctx->odp_actions;
+
+ if (base->vlan_tci != vlan_tci) {
+ if (!(vlan_tci & htons(VLAN_CFI))) {
+ nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
+ } else {
+ if (base->vlan_tci != htons(0)) {
+ nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
+ }
+ nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
+ vlan_tci & ~htons(VLAN_CFI));
+ }
+ base->vlan_tci = vlan_tci;
+ }
+}
+
+static void
+commit_odp_actions(struct action_xlate_ctx *ctx)
+{
+ const struct flow *flow = &ctx->flow;
+ struct flow *base = &ctx->base_flow;
+ struct ofpbuf *odp_actions = ctx->odp_actions;
+
+ if (base->tun_id != flow->tun_id) {
+ nl_msg_put_be64(odp_actions, OVS_ACTION_ATTR_SET_TUNNEL, flow->tun_id);
+ base->tun_id = flow->tun_id;
+ }
+
+ if (base->nw_src != flow->nw_src) {
+ nl_msg_put_be32(odp_actions, OVS_ACTION_ATTR_SET_NW_SRC, flow->nw_src);
+ base->nw_src = flow->nw_src;
+ }
+
+ if (base->nw_dst != flow->nw_dst) {
+ nl_msg_put_be32(odp_actions, OVS_ACTION_ATTR_SET_NW_DST, flow->nw_dst);
+ base->nw_dst = flow->nw_dst;
+ }
+
+ if (base->nw_tos != flow->nw_tos) {
+ nl_msg_put_u8(odp_actions, OVS_ACTION_ATTR_SET_NW_TOS, flow->nw_tos);
+ base->nw_tos = flow->nw_tos;
+ }
+
+ commit_vlan_tci(ctx, flow->vlan_tci);
+
+ if (base->tp_src != flow->tp_src) {
+ nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_SRC, flow->tp_src);
+ base->tp_src = flow->tp_src;
+ }
+
+ if (base->tp_dst != flow->tp_dst) {
+ nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_DST, flow->tp_dst);
+ base->tp_dst = flow->tp_dst;
+ }
+
+ if (!eth_addr_equals(base->dl_src, flow->dl_src)) {
+ nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_DL_SRC,
+ flow->dl_src, ETH_ADDR_LEN);
+ memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
+ }
+
+ if (!eth_addr_equals(base->dl_dst, flow->dl_dst)) {
+ nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_DL_DST,
+ flow->dl_dst, ETH_ADDR_LEN);
+ memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
+ }
+
+ if (ctx->base_priority != ctx->priority) {
+ if (ctx->priority) {
+ nl_msg_put_u32(odp_actions, OVS_ACTION_ATTR_SET_PRIORITY,
+ ctx->priority);
+ } else {
+ nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_PRIORITY);
+ }
+ ctx->base_priority = ctx->priority;
+ }
+}
+
+static void
+compose_output_action(struct action_xlate_ctx *ctx, uint16_t odp_port)
+{
+ nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, odp_port);
+ ctx->sflow_odp_port = odp_port;
+ ctx->sflow_n_outputs++;
+}
+
+static void
+add_output_action(struct action_xlate_ctx *ctx, uint16_t ofp_port)
+{
+ const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
+ uint16_t odp_port = ofp_port_to_odp_port(ofp_port);
+
+ if (ofport) {
+ if (ofport->up.opp.config & htonl(OFPPC_NO_FWD)) {
+ /* Forwarding disabled on port. */
+ return;
+ }
+ } else {
+ /*
+ * We don't have an ofport record for this port, but it doesn't hurt to
+ * allow forwarding to it anyhow. Maybe such a port will appear later
+ * and we're pre-populating the flow table.
+ */
+ }
+
+ commit_odp_actions(ctx);
+ compose_output_action(ctx, odp_port);
+ ctx->nf_output_iface = ofp_port;
+}
+
+static void
+xlate_table_action(struct action_xlate_ctx *ctx,
+ uint16_t in_port, uint8_t table_id)
+{
+ if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
+ struct ofproto_dpif *ofproto = ctx->ofproto;
+ struct rule_dpif *rule;
+ uint16_t old_in_port;
+ uint8_t old_table_id;
+
+ old_table_id = ctx->table_id;
+ ctx->table_id = table_id;
+
+ /* Look up a flow with 'in_port' as the input port. */
+ old_in_port = ctx->flow.in_port;