- ofconn->miss_send_len = ntohs(osc->miss_send_len);
-
- return 0;
-}
-
-static void
-add_controller_action(struct odp_actions *actions, uint16_t max_len)
-{
- union odp_action *a = odp_actions_add(actions, ODPAT_CONTROLLER);
- a->controller.arg = max_len;
-}
-
-struct action_xlate_ctx {
- /* Input. */
- flow_t flow; /* Flow to which these actions correspond. */
- int recurse; /* Recursion level, via xlate_table_action. */
- struct ofproto *ofproto;
- const struct ofpbuf *packet; /* The packet corresponding to 'flow', or a
- * null pointer if we are revalidating
- * without a packet to refer to. */
-
- /* Output. */
- struct odp_actions *out; /* Datapath actions. */
- tag_type *tags; /* Tags associated with OFPP_NORMAL actions. */
- bool may_set_up_flow; /* True ordinarily; false if the actions must
- * be reassessed for every packet. */
- uint16_t nf_output_iface; /* Output interface index for NetFlow. */
-};
-
-/* Maximum depth of flow table recursion (due to NXAST_RESUBMIT actions) in a
- * flow translation. */
-#define MAX_RESUBMIT_RECURSION 8
-
-static void do_xlate_actions(const union ofp_action *in, size_t n_in,
- struct action_xlate_ctx *ctx);
-
-static void
-add_output_action(struct action_xlate_ctx *ctx, uint16_t port)
-{
- const struct ofport *ofport = get_port(ctx->ofproto, port);
-
- if (ofport) {
- if (ofport->opp.config & 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.
- */
- }
-
- odp_actions_add(ctx->out, ODPAT_OUTPUT)->output.port = port;
- ctx->nf_output_iface = port;
-}
-
-static struct rule *
-lookup_valid_rule(struct ofproto *ofproto, const flow_t *flow)
-{
- struct rule *rule;
- rule = rule_from_cls_rule(classifier_lookup(&ofproto->cls, flow));
-
- /* The rule we found might not be valid, since we could be in need of
- * revalidation. If it is not valid, don't return it. */
- if (rule
- && rule->super
- && ofproto->need_revalidate
- && !revalidate_rule(ofproto, rule)) {
- COVERAGE_INC(ofproto_invalidated);
- return NULL;
- }
-
- return rule;
-}
-
-static void
-xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port)
-{
- if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
- uint16_t old_in_port;
- struct rule *rule;
-
- /* Look up a flow with 'in_port' as the input port. Then restore the
- * original input port (otherwise OFPP_NORMAL and OFPP_IN_PORT will
- * have surprising behavior). */
- old_in_port = ctx->flow.in_port;
- ctx->flow.in_port = in_port;
- rule = lookup_valid_rule(ctx->ofproto, &ctx->flow);
- ctx->flow.in_port = old_in_port;
-
- if (rule) {
- if (rule->super) {
- rule = rule->super;
- }
-
- ctx->recurse++;
- do_xlate_actions(rule->actions, rule->n_actions, ctx);
- ctx->recurse--;
- }
- } else {
- struct vlog_rate_limit recurse_rl = VLOG_RATE_LIMIT_INIT(1, 1);
-
- VLOG_ERR_RL(&recurse_rl, "NXAST_RESUBMIT recursed over %d times",
- MAX_RESUBMIT_RECURSION);
- }
-}
-
-static void
-flood_packets(struct ofproto *ofproto, uint16_t odp_in_port, uint32_t mask,
- uint16_t *nf_output_iface, struct odp_actions *actions)
-{
- struct ofport *ofport;
-
- HMAP_FOR_EACH (ofport, hmap_node, &ofproto->ports) {
- uint16_t odp_port = ofport->odp_port;
- if (odp_port != odp_in_port && !(ofport->opp.config & mask)) {
- odp_actions_add(actions, ODPAT_OUTPUT)->output.port = odp_port;
- }
- }
- *nf_output_iface = NF_OUT_FLOOD;
-}
-
-static void
-xlate_output_action__(struct action_xlate_ctx *ctx,
- uint16_t port, uint16_t max_len)
-{
- uint16_t odp_port;
- uint16_t prev_nf_output_iface = ctx->nf_output_iface;
-
- ctx->nf_output_iface = NF_OUT_DROP;
-
- switch (port) {
- case OFPP_IN_PORT:
- add_output_action(ctx, ctx->flow.in_port);
- break;
- case OFPP_TABLE:
- xlate_table_action(ctx, ctx->flow.in_port);
- break;
- case OFPP_NORMAL:
- if (!ctx->ofproto->ofhooks->normal_cb(&ctx->flow, ctx->packet,
- ctx->out, ctx->tags,
- &ctx->nf_output_iface,
- ctx->ofproto->aux)) {
- COVERAGE_INC(ofproto_uninstallable);
- ctx->may_set_up_flow = false;
- }
- break;
- case OFPP_FLOOD:
- flood_packets(ctx->ofproto, ctx->flow.in_port, OFPPC_NO_FLOOD,
- &ctx->nf_output_iface, ctx->out);
- case OFPP_ALL:
- flood_packets(ctx->ofproto, ctx->flow.in_port, 0,
- &ctx->nf_output_iface, ctx->out);
- break;
- case OFPP_CONTROLLER:
- add_controller_action(ctx->out, max_len);
- break;
- case OFPP_LOCAL:
- add_output_action(ctx, ODPP_LOCAL);
- break;
- default:
- odp_port = ofp_port_to_odp_port(port);
- if (odp_port != ctx->flow.in_port) {
- add_output_action(ctx, odp_port);
- }
- break;
- }
-
- if (prev_nf_output_iface == NF_OUT_FLOOD) {
- ctx->nf_output_iface = NF_OUT_FLOOD;
- } else if (ctx->nf_output_iface == NF_OUT_DROP) {
- ctx->nf_output_iface = prev_nf_output_iface;
- } else if (prev_nf_output_iface != NF_OUT_DROP &&
- ctx->nf_output_iface != NF_OUT_FLOOD) {
- ctx->nf_output_iface = NF_OUT_MULTI;
- }
-}
-
-static void
-xlate_output_action(struct action_xlate_ctx *ctx,
- const struct ofp_action_output *oao)
-{
- xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len));
-}
-
-/* If the final ODP action in 'ctx' is "pop priority", drop it, as an
- * optimization, because we're going to add another action that sets the
- * priority immediately after, or because there are no actions following the
- * pop. */
-static void
-remove_pop_action(struct action_xlate_ctx *ctx)
-{
- size_t n = ctx->out->n_actions;
- if (n > 0 && ctx->out->actions[n - 1].type == ODPAT_POP_PRIORITY) {
- ctx->out->n_actions--;
- }
-}
-
-static void
-xlate_enqueue_action(struct action_xlate_ctx *ctx,
- const struct ofp_action_enqueue *oae)
-{
- uint16_t ofp_port, odp_port;
- uint32_t priority;
- int error;
-
- error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id),
- &priority);
- if (error) {
- /* Fall back to ordinary output action. */
- xlate_output_action__(ctx, ntohs(oae->port), 0);
- return;
- }
-
- /* Figure out ODP output port. */
- ofp_port = ntohs(oae->port);
- if (ofp_port != OFPP_IN_PORT) {
- odp_port = ofp_port_to_odp_port(ofp_port);
- } else {
- odp_port = ctx->flow.in_port;
- }
-
- /* Add ODP actions. */
- remove_pop_action(ctx);
- odp_actions_add(ctx->out, ODPAT_SET_PRIORITY)->priority.priority
- = priority;
- add_output_action(ctx, odp_port);
- odp_actions_add(ctx->out, ODPAT_POP_PRIORITY);
-
- /* Update NetFlow output port. */
- if (ctx->nf_output_iface == NF_OUT_DROP) {
- ctx->nf_output_iface = odp_port;
- } else if (ctx->nf_output_iface != NF_OUT_FLOOD) {
- ctx->nf_output_iface = NF_OUT_MULTI;
- }
-}
-
-static void
-xlate_set_queue_action(struct action_xlate_ctx *ctx,
- const struct nx_action_set_queue *nasq)
-{
- uint32_t priority;
- int error;
-
- error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(nasq->queue_id),
- &priority);
- if (error) {
- /* Couldn't translate queue to a priority, so ignore. A warning
- * has already been logged. */
- return;
- }
-
- remove_pop_action(ctx);
- odp_actions_add(ctx->out, ODPAT_SET_PRIORITY)->priority.priority
- = priority;
-}
-
-static void
-xlate_nicira_action(struct action_xlate_ctx *ctx,
- const struct nx_action_header *nah)
-{
- const struct nx_action_resubmit *nar;
- const struct nx_action_set_tunnel *nast;
- const struct nx_action_set_queue *nasq;
- union odp_action *oa;
- int subtype = ntohs(nah->subtype);
-
- assert(nah->vendor == htonl(NX_VENDOR_ID));
- switch (subtype) {
- case NXAST_RESUBMIT:
- nar = (const struct nx_action_resubmit *) nah;
- xlate_table_action(ctx, ofp_port_to_odp_port(ntohs(nar->in_port)));
- break;
-
- case NXAST_SET_TUNNEL:
- nast = (const struct nx_action_set_tunnel *) nah;
- oa = odp_actions_add(ctx->out, ODPAT_SET_TUNNEL);
- ctx->flow.tun_id = oa->tunnel.tun_id = nast->tun_id;
- break;
-
- case NXAST_DROP_SPOOFED_ARP:
- if (ctx->flow.dl_type == htons(ETH_TYPE_ARP)) {
- odp_actions_add(ctx->out, ODPAT_DROP_SPOOFED_ARP);
- }
- break;
-
- case NXAST_SET_QUEUE:
- nasq = (const struct nx_action_set_queue *) nah;
- xlate_set_queue_action(ctx, nasq);
- break;
-
- case NXAST_POP_QUEUE:
- odp_actions_add(ctx->out, ODPAT_POP_PRIORITY);
- break;
-
- /* If you add a new action here that modifies flow data, don't forget to
- * update the flow key in ctx->flow at the same time. */
-
- default:
- VLOG_DBG_RL(&rl, "unknown Nicira action type %"PRIu16, subtype);
- break;
- }
-}
-
-static void
-do_xlate_actions(const union ofp_action *in, size_t n_in,
- struct action_xlate_ctx *ctx)
-{
- struct actions_iterator iter;
- const union ofp_action *ia;
- const struct ofport *port;
-
- port = get_port(ctx->ofproto, ctx->flow.in_port);
- if (port && port->opp.config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
- port->opp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp)
- ? OFPPC_NO_RECV_STP : OFPPC_NO_RECV)) {
- /* Drop this flow. */
- return;
- }
-
- for (ia = actions_first(&iter, in, n_in); ia; ia = actions_next(&iter)) {
- uint16_t type = ntohs(ia->type);
- union odp_action *oa;
-
- switch (type) {
- case OFPAT_OUTPUT:
- xlate_output_action(ctx, &ia->output);
- break;
-
- case OFPAT_SET_VLAN_VID:
- oa = odp_actions_add(ctx->out, ODPAT_SET_VLAN_VID);
- ctx->flow.dl_vlan = oa->vlan_vid.vlan_vid = ia->vlan_vid.vlan_vid;
- break;
-
- case OFPAT_SET_VLAN_PCP:
- oa = odp_actions_add(ctx->out, ODPAT_SET_VLAN_PCP);
- ctx->flow.dl_vlan_pcp = oa->vlan_pcp.vlan_pcp = ia->vlan_pcp.vlan_pcp;
- break;
-
- case OFPAT_STRIP_VLAN:
- odp_actions_add(ctx->out, ODPAT_STRIP_VLAN);
- ctx->flow.dl_vlan = htons(OFP_VLAN_NONE);
- ctx->flow.dl_vlan_pcp = 0;
- break;
-
- case OFPAT_SET_DL_SRC:
- oa = odp_actions_add(ctx->out, ODPAT_SET_DL_SRC);
- memcpy(oa->dl_addr.dl_addr,
- ((struct ofp_action_dl_addr *) ia)->dl_addr, ETH_ADDR_LEN);
- memcpy(ctx->flow.dl_src,
- ((struct ofp_action_dl_addr *) ia)->dl_addr, ETH_ADDR_LEN);
- break;
-
- case OFPAT_SET_DL_DST:
- oa = odp_actions_add(ctx->out, ODPAT_SET_DL_DST);
- memcpy(oa->dl_addr.dl_addr,
- ((struct ofp_action_dl_addr *) ia)->dl_addr, ETH_ADDR_LEN);
- memcpy(ctx->flow.dl_dst,
- ((struct ofp_action_dl_addr *) ia)->dl_addr, ETH_ADDR_LEN);
- break;
-
- case OFPAT_SET_NW_SRC:
- oa = odp_actions_add(ctx->out, ODPAT_SET_NW_SRC);
- ctx->flow.nw_src = oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
- break;
-
- case OFPAT_SET_NW_DST:
- oa = odp_actions_add(ctx->out, ODPAT_SET_NW_DST);
- ctx->flow.nw_dst = oa->nw_addr.nw_addr = ia->nw_addr.nw_addr;
- break;
-
- case OFPAT_SET_NW_TOS:
- oa = odp_actions_add(ctx->out, ODPAT_SET_NW_TOS);
- ctx->flow.nw_tos = oa->nw_tos.nw_tos = ia->nw_tos.nw_tos;
- break;
-
- case OFPAT_SET_TP_SRC:
- oa = odp_actions_add(ctx->out, ODPAT_SET_TP_SRC);
- ctx->flow.tp_src = oa->tp_port.tp_port = ia->tp_port.tp_port;
- break;
-
- case OFPAT_SET_TP_DST:
- oa = odp_actions_add(ctx->out, ODPAT_SET_TP_DST);
- ctx->flow.tp_dst = oa->tp_port.tp_port = ia->tp_port.tp_port;
- break;
-
- case OFPAT_VENDOR:
- xlate_nicira_action(ctx, (const struct nx_action_header *) ia);
- break;
-
- case OFPAT_ENQUEUE:
- xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia);
- break;
-
- default:
- VLOG_DBG_RL(&rl, "unknown action type %"PRIu16, type);
- break;
- }
- }
-}
-
-static int
-xlate_actions(const union ofp_action *in, size_t n_in,
- const flow_t *flow, struct ofproto *ofproto,
- const struct ofpbuf *packet,
- struct odp_actions *out, tag_type *tags, bool *may_set_up_flow,
- uint16_t *nf_output_iface)
-{
- tag_type no_tags = 0;
- struct action_xlate_ctx ctx;
- COVERAGE_INC(ofproto_ofp2odp);
- odp_actions_init(out);
- ctx.flow = *flow;
- ctx.recurse = 0;
- ctx.ofproto = ofproto;
- ctx.packet = packet;
- ctx.out = out;
- ctx.tags = tags ? tags : &no_tags;
- ctx.may_set_up_flow = true;
- ctx.nf_output_iface = NF_OUT_DROP;
- do_xlate_actions(in, n_in, &ctx);
- remove_pop_action(&ctx);
-
- /* Check with in-band control to see if we're allowed to set up this
- * flow. */
- if (!in_band_rule_check(ofproto->in_band, flow, out)) {
- ctx.may_set_up_flow = false;
- }