- mac = mac_learning_insert(ofproto->ml, flow->dl_src, vlan);
- if (is_gratuitous_arp(flow)) {
- /* We don't want to learn from gratuitous ARP packets that are
- * reflected back over bond slaves so we lock the learning table. */
- if (!in_bundle->bond) {
- mac_entry_set_grat_arp_lock(mac);
- } else if (mac_entry_is_grat_arp_locked(mac)) {
- return;
- }
- }
-
- if (mac_entry_is_new(mac) || mac->port.p != in_bundle) {
- /* The log messages here could actually be useful in debugging,
- * so keep the rate limit relatively high. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
- VLOG_DBG_RL(&rl, "bridge %s: learned that "ETH_ADDR_FMT" is "
- "on port %s in VLAN %d",
- ofproto->name, ETH_ADDR_ARGS(flow->dl_src),
- in_bundle->name, vlan);
-
- mac->port.p = in_bundle;
- tag_set_add(&ofproto->revalidate_set,
- mac_learning_changed(ofproto->ml, mac));
- }
-}
-
-/* Determines whether packets in 'flow' within 'br' should be forwarded or
- * dropped. Returns true if they may be forwarded, false if they should be
- * dropped.
- *
- * If 'have_packet' is true, it indicates that the caller is processing a
- * received packet. If 'have_packet' is false, then the caller is just
- * revalidating an existing flow because configuration has changed. Either
- * way, 'have_packet' only affects logging (there is no point in logging errors
- * during revalidation).
- *
- * Sets '*in_portp' to the input port. This will be a null pointer if
- * flow->in_port does not designate a known input port (in which case
- * is_admissible() returns false).
- *
- * When returning true, sets '*vlanp' to the effective VLAN of the input
- * packet, as returned by flow_get_vlan().
- *
- * May also add tags to '*tags', although the current implementation only does
- * so in one special case.
- */
-static bool
-is_admissible(struct ofproto *ofproto, const struct flow *flow,
- bool have_packet,
- tag_type *tags, int *vlanp, struct ofbundle **in_bundlep)
-{
- struct ofport *in_port;
- struct ofbundle *in_bundle;
- int vlan;
-
- /* Find the port and bundle for the received packet. */
- in_port = get_port(ofproto, flow->in_port);
- *in_bundlep = in_bundle = in_port->bundle;
- if (!in_port || !in_bundle) {
- /* No interface? Something fishy... */
- if (have_packet) {
- /* Odd. A few possible reasons here:
- *
- * - We deleted a port but there are still a few packets queued up
- * from it.
- *
- * - Someone externally added a port (e.g. "ovs-dpctl add-if") that
- * we don't know about.
- *
- * - Packet arrived on the local port but the local port is not
- * part of a bundle.
- */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
- "port %"PRIu16,
- ofproto->name, flow->in_port);
- }
- return false;
- }
- *vlanp = vlan = flow_get_vlan(ofproto, flow, in_bundle, have_packet);
- if (vlan < 0) {
- return false;
- }
-
- /* Drop frames for reserved multicast addresses. */
- if (eth_addr_is_reserved(flow->dl_dst)) {
- return false;
- }
-
- /* Drop frames on bundles reserved for mirroring. */
- if (in_bundle->mirror_out) {
- if (have_packet) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "bridge %s: dropping packet received on port "
- "%s, which is reserved exclusively for mirroring",
- ofproto->name, in_bundle->name);
- }
- return false;
- }
-
- if (in_bundle->bond) {
- struct mac_entry *mac;
-
- switch (bond_check_admissibility(in_bundle->bond, in_port,
- flow->dl_dst, tags)) {
- case BV_ACCEPT:
- break;
-
- case BV_DROP:
- return false;
-
- case BV_DROP_IF_MOVED:
- mac = mac_learning_lookup(ofproto->ml, flow->dl_src, vlan, NULL);
- if (mac && mac->port.p != in_bundle &&
- (!is_gratuitous_arp(flow)
- || mac_entry_is_grat_arp_locked(mac))) {
- return false;
- }
- break;
- }
- }
-
- return true;
-}
-
-/* If the composed actions may be applied to any packet in the given 'flow',
- * returns true. Otherwise, the actions should only be applied to 'packet', or
- * not at all, if 'packet' was NULL. */
-static bool
-xlate_normal(struct action_xlate_ctx *ctx)
-{
- struct ofbundle *in_bundle;
- struct ofbundle *out_bundle;
- struct mac_entry *mac;
- int vlan;
-
- /* Check whether we should drop packets in this flow. */
- if (!is_admissible(ctx->ofproto, &ctx->flow, ctx->packet != NULL,
- &ctx->tags, &vlan, &in_bundle)) {
- out_bundle = NULL;
- goto done;
- }
-
- /* Learn source MAC (but don't try to learn from revalidation). */
- if (ctx->packet) {
- update_learning_table(ctx->ofproto, &ctx->flow, vlan, in_bundle);
- }
-
- /* Determine output bundle. */
- mac = mac_learning_lookup(ctx->ofproto->ml, ctx->flow.dl_dst, vlan,
- &ctx->tags);
- if (mac) {
- out_bundle = mac->port.p;
- } else if (!ctx->packet && !eth_addr_is_multicast(ctx->flow.dl_dst)) {
- /* If we are revalidating but don't have a learning entry then eject
- * the flow. Installing a flow that floods packets opens up a window
- * of time where we could learn from a packet reflected on a bond and
- * blackhole packets before the learning table is updated to reflect
- * the correct port. */
- return false;
- } else {
- out_bundle = OFBUNDLE_FLOOD;
- }
-
- /* Don't send packets out their input bundles. */
- if (in_bundle == out_bundle) {
- out_bundle = NULL;
- }
-
-done:
- if (in_bundle) {
- compose_actions(ctx, vlan, in_bundle, out_bundle);
- }
-
- return true;
-}
-\f
-static void
-send_error_oh(const struct ofconn *ofconn, const struct ofp_header *oh,
- int error)
-{
- struct ofpbuf *buf = ofputil_encode_error_msg(error, oh);
- if (buf) {
- COVERAGE_INC(ofproto_error);
- ofconn_send_reply(ofconn, buf);
- }
-}
-
-static int
-handle_echo_request(struct ofconn *ofconn, const struct ofp_header *oh)
-{
- ofconn_send_reply(ofconn, make_echo_reply(oh));
- return 0;
-}
-
-static int
-handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh)
-{
- struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
- struct ofp_switch_features *osf;
- struct ofpbuf *buf;
- struct ofport *port;
-
- osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, oh->xid, &buf);
- osf->datapath_id = htonll(ofproto->datapath_id);
- osf->n_buffers = htonl(pktbuf_capacity());
- osf->n_tables = 2;
- osf->capabilities = htonl(OFPC_FLOW_STATS | OFPC_TABLE_STATS |
- OFPC_PORT_STATS | OFPC_ARP_MATCH_IP);
- osf->actions = htonl((1u << OFPAT_OUTPUT) |
- (1u << OFPAT_SET_VLAN_VID) |
- (1u << OFPAT_SET_VLAN_PCP) |
- (1u << OFPAT_STRIP_VLAN) |
- (1u << OFPAT_SET_DL_SRC) |
- (1u << OFPAT_SET_DL_DST) |
- (1u << OFPAT_SET_NW_SRC) |
- (1u << OFPAT_SET_NW_DST) |
- (1u << OFPAT_SET_NW_TOS) |
- (1u << OFPAT_SET_TP_SRC) |
- (1u << OFPAT_SET_TP_DST) |
- (1u << OFPAT_ENQUEUE));
-
- HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) {
- ofpbuf_put(buf, &port->opp, sizeof port->opp);
- }
-
- ofconn_send_reply(ofconn, buf);
- return 0;
-}
-
-static int
-handle_get_config_request(struct ofconn *ofconn, const struct ofp_header *oh)
-{
- struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
- struct ofpbuf *buf;
- struct ofp_switch_config *osc;
- uint16_t flags;
- bool drop_frags;
-
- /* Figure out flags. */
- dpif_get_drop_frags(ofproto->dpif, &drop_frags);
- flags = drop_frags ? OFPC_FRAG_DROP : OFPC_FRAG_NORMAL;
-
- /* Send reply. */
- osc = make_openflow_xid(sizeof *osc, OFPT_GET_CONFIG_REPLY, oh->xid, &buf);
- osc->flags = htons(flags);
- osc->miss_send_len = htons(ofconn_get_miss_send_len(ofconn));
- ofconn_send_reply(ofconn, buf);
-
- return 0;
-}
-
-static int
-handle_set_config(struct ofconn *ofconn, const struct ofp_switch_config *osc)
-{
- struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
- uint16_t flags = ntohs(osc->flags);
-
- if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
- && ofconn_get_role(ofconn) != NX_ROLE_SLAVE) {
- switch (flags & OFPC_FRAG_MASK) {
- case OFPC_FRAG_NORMAL:
- dpif_set_drop_frags(ofproto->dpif, false);
- break;
- case OFPC_FRAG_DROP:
- dpif_set_drop_frags(ofproto->dpif, true);
- break;
- default:
- VLOG_WARN_RL(&rl, "requested bad fragment mode (flags=%"PRIx16")",
- osc->flags);
- break;
- }
- }
-
- ofconn_set_miss_send_len(ofconn, ntohs(osc->miss_send_len));
-
- return 0;
-}
-
-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 & 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.
- */
- }
-
- nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_OUTPUT, port);
- ctx->nf_output_iface = port;
-}
-
-static struct rule *
-rule_lookup(struct ofproto *ofproto, const struct flow *flow)
-{
- return rule_from_cls_rule(classifier_lookup(&ofproto->cls, flow));
-}
-
-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 = rule_lookup(ctx->ofproto, &ctx->flow);
- ctx->flow.in_port = old_in_port;
-
- if (ctx->resubmit_hook) {
- ctx->resubmit_hook(ctx, rule);
- }
-
- if (rule) {
- ctx->recurse++;
- do_xlate_actions(rule->actions, rule->n_actions, ctx);
- ctx->recurse--;
- }
- } else {
- static 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, ovs_be32 mask,
- uint16_t *nf_output_iface, struct ofpbuf *odp_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)) {
- nl_msg_put_u32(odp_actions, ODP_ACTION_ATTR_OUTPUT, 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:
- xlate_normal(ctx);
- break;
- case OFPP_FLOOD:
- flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(OFPPC_NO_FLOOD),
- &ctx->nf_output_iface, ctx->odp_actions);
- break;
- case OFPP_ALL:
- flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(0),
- &ctx->nf_output_iface, ctx->odp_actions);
- break;
- case OFPP_CONTROLLER:
- nl_msg_put_u64(ctx->odp_actions, ODP_ACTION_ATTR_CONTROLLER, 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)
-{
- if (ctx->odp_actions->size == ctx->last_pop_priority) {
- ctx->odp_actions->size -= NLA_ALIGN(NLA_HDRLEN);
- ctx->last_pop_priority = -1;
- }
-}
-
-static void
-add_pop_action(struct action_xlate_ctx *ctx)
-{
- if (ctx->odp_actions->size != ctx->last_pop_priority) {
- nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_POP_PRIORITY);
- ctx->last_pop_priority = ctx->odp_actions->size;
- }
-}
-
-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);
- nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, priority);
- add_output_action(ctx, odp_port);
- add_pop_action(ctx);
-
- /* 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);
- nl_msg_put_u32(ctx->odp_actions, ODP_ACTION_ATTR_SET_PRIORITY, priority);
-}
-
-static void
-xlate_set_dl_tci(struct action_xlate_ctx *ctx)
-{
- ovs_be16 tci = ctx->flow.vlan_tci;
- if (!(tci & htons(VLAN_CFI))) {
- nl_msg_put_flag(ctx->odp_actions, ODP_ACTION_ATTR_STRIP_VLAN);
- } else {
- nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_TCI,
- tci & ~htons(VLAN_CFI));
- }
-}
-
-struct xlate_reg_state {
- ovs_be16 vlan_tci;
- ovs_be64 tun_id;
-};
-
-static void
-save_reg_state(const struct action_xlate_ctx *ctx,
- struct xlate_reg_state *state)
-{
- state->vlan_tci = ctx->flow.vlan_tci;
- state->tun_id = ctx->flow.tun_id;
-}
-
-static void
-update_reg_state(struct action_xlate_ctx *ctx,
- const struct xlate_reg_state *state)
-{
- if (ctx->flow.vlan_tci != state->vlan_tci) {
- xlate_set_dl_tci(ctx);
- }
- if (ctx->flow.tun_id != state->tun_id) {
- nl_msg_put_be64(ctx->odp_actions,
- ODP_ACTION_ATTR_SET_TUNNEL, ctx->flow.tun_id);
- }
-}
-
-static void
-xlate_autopath(struct action_xlate_ctx *ctx,
- const struct nx_action_autopath *naa)
-{
- uint16_t ofp_port = ntohl(naa->id);
- struct ofport *port;
-
- port = get_port(ctx->ofproto, ofp_port_to_odp_port(ofp_port));
- if (!port || !port->bundle) {
- ofp_port = OFPP_NONE;
- } else if (port->bundle->bond) {
- /* Autopath does not support VLAN hashing. */
- struct ofport *slave = bond_choose_output_slave(
- port->bundle->bond, &ctx->flow, OFP_VLAN_NONE, &ctx->tags);
- if (slave) {
- ofp_port = odp_port_to_ofp_port(slave->odp_port);
- }
- }
- autopath_execute(naa, &ctx->flow, ofp_port);
-}
-
-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;
- const struct nx_action_multipath *nam;
- const struct nx_action_autopath *naa;
- enum nx_action_subtype subtype = ntohs(nah->subtype);
- struct xlate_reg_state state;
- ovs_be64 tun_id;
-
- 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;
- tun_id = htonll(ntohl(nast->tun_id));
- nl_msg_put_be64(ctx->odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, tun_id);
- ctx->flow.tun_id = tun_id;
- break;
-
- case NXAST_DROP_SPOOFED_ARP:
- if (ctx->flow.dl_type == htons(ETH_TYPE_ARP)) {
- nl_msg_put_flag(ctx->odp_actions,
- ODP_ACTION_ATTR_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:
- add_pop_action(ctx);
- break;
-
- case NXAST_REG_MOVE:
- save_reg_state(ctx, &state);
- nxm_execute_reg_move((const struct nx_action_reg_move *) nah,
- &ctx->flow);
- update_reg_state(ctx, &state);
- break;
-
- case NXAST_REG_LOAD:
- save_reg_state(ctx, &state);
- nxm_execute_reg_load((const struct nx_action_reg_load *) nah,
- &ctx->flow);
- update_reg_state(ctx, &state);
- break;
-
- case NXAST_NOTE:
- /* Nothing to do. */
- break;
-
- case NXAST_SET_TUNNEL64:
- tun_id = ((const struct nx_action_set_tunnel64 *) nah)->tun_id;
- nl_msg_put_be64(ctx->odp_actions, ODP_ACTION_ATTR_SET_TUNNEL, tun_id);
- ctx->flow.tun_id = tun_id;
- break;
-
- case NXAST_MULTIPATH:
- nam = (const struct nx_action_multipath *) nah;
- multipath_execute(nam, &ctx->flow);
- break;
-
- case NXAST_AUTOPATH:
- naa = (const struct nx_action_autopath *) nah;
- xlate_autopath(ctx, naa);
- 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. */
-
- case NXAST_SNAT__OBSOLETE:
- default:
- VLOG_DBG_RL(&rl, "unknown Nicira action type %d", (int) 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 & htonl(OFPPC_NO_RECV | OFPPC_NO_RECV_STP) &&
- port->opp.config & (eth_addr_equals(ctx->flow.dl_dst, eth_addr_stp)
- ? htonl(OFPPC_NO_RECV_STP)
- : htonl(OFPPC_NO_RECV))) {
- /* Drop this flow. */
- return;
- }
-
- for (ia = actions_first(&iter, in, n_in); ia; ia = actions_next(&iter)) {
- enum ofp_action_type type = ntohs(ia->type);
- const struct ofp_action_dl_addr *oada;
-
- switch (type) {
- case OFPAT_OUTPUT:
- xlate_output_action(ctx, &ia->output);
- break;
-
- case OFPAT_SET_VLAN_VID:
- ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
- ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
- xlate_set_dl_tci(ctx);
- break;
-
- case OFPAT_SET_VLAN_PCP:
- ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
- ctx->flow.vlan_tci |= htons(
- (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
- xlate_set_dl_tci(ctx);
- break;
-
- case OFPAT_STRIP_VLAN:
- ctx->flow.vlan_tci = htons(0);
- xlate_set_dl_tci(ctx);
- break;
-
- case OFPAT_SET_DL_SRC:
- oada = ((struct ofp_action_dl_addr *) ia);
- nl_msg_put_unspec(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_SRC,
- oada->dl_addr, ETH_ADDR_LEN);
- memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN);
- break;
-
- case OFPAT_SET_DL_DST:
- oada = ((struct ofp_action_dl_addr *) ia);
- nl_msg_put_unspec(ctx->odp_actions, ODP_ACTION_ATTR_SET_DL_DST,
- oada->dl_addr, ETH_ADDR_LEN);
- memcpy(ctx->flow.dl_dst, oada->dl_addr, ETH_ADDR_LEN);
- break;
-
- case OFPAT_SET_NW_SRC:
- nl_msg_put_be32(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_SRC,
- ia->nw_addr.nw_addr);
- ctx->flow.nw_src = ia->nw_addr.nw_addr;
- break;
-
- case OFPAT_SET_NW_DST:
- nl_msg_put_be32(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_DST,
- ia->nw_addr.nw_addr);
- ctx->flow.nw_dst = ia->nw_addr.nw_addr;
- break;
-
- case OFPAT_SET_NW_TOS:
- nl_msg_put_u8(ctx->odp_actions, ODP_ACTION_ATTR_SET_NW_TOS,
- ia->nw_tos.nw_tos);
- ctx->flow.nw_tos = ia->nw_tos.nw_tos;
- break;
-
- case OFPAT_SET_TP_SRC:
- nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_TP_SRC,
- ia->tp_port.tp_port);
- ctx->flow.tp_src = ia->tp_port.tp_port;
- break;
-
- case OFPAT_SET_TP_DST:
- nl_msg_put_be16(ctx->odp_actions, ODP_ACTION_ATTR_SET_TP_DST,
- ia->tp_port.tp_port);
- ctx->flow.tp_dst = 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 %d", (int) type);
- break;
- }
- }
-}
-
-static void
-action_xlate_ctx_init(struct action_xlate_ctx *ctx,
- struct ofproto *ofproto, const struct flow *flow,
- const struct ofpbuf *packet)
-{
- ctx->ofproto = ofproto;
- ctx->flow = *flow;
- ctx->packet = packet;
- ctx->resubmit_hook = NULL;
- ctx->check_special = true;
-}
-
-static bool
-ofproto_process_special(struct ofproto *ofproto, const struct flow *flow,
- const struct ofpbuf *packet)
-{
- if (cfm_should_process_flow(flow)) {
- struct ofport *ofport = get_port(ofproto, flow->in_port);
- if (ofport && ofport->cfm) {
- cfm_process_heartbeat(ofport->cfm, packet);
- }
- return true;
- } else if (flow->dl_type == htons(ETH_TYPE_LACP)) {
- struct ofport *port = get_port(ofproto, flow->in_port);
- if (port && port->bundle && port->bundle->lacp) {
- const struct lacp_pdu *pdu = parse_lacp_packet(packet);
- if (pdu) {
- lacp_process_pdu(port->bundle->lacp, port, pdu);
- }
- return true;
- }
- }
- return false;
-}
-
-static struct ofpbuf *
-xlate_actions(struct action_xlate_ctx *ctx,
- const union ofp_action *in, size_t n_in)
-{
- COVERAGE_INC(ofproto_ofp2odp);
-
- ctx->odp_actions = ofpbuf_new(512);
- ctx->tags = 0;
- ctx->may_set_up_flow = true;
- ctx->nf_output_iface = NF_OUT_DROP;
- ctx->recurse = 0;
- ctx->last_pop_priority = -1;
-
- if (ctx->check_special
- && ofproto_process_special(ctx->ofproto, &ctx->flow, ctx->packet)) {
- ctx->may_set_up_flow = false;
- } else {
- 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 (!connmgr_may_set_up_flow(ctx->ofproto->connmgr, &ctx->flow,
- ctx->odp_actions->data,
- ctx->odp_actions->size)) {
- ctx->may_set_up_flow = false;
- }
-
- return ctx->odp_actions;
-}
-
-/* Checks whether 'ofconn' is a slave controller. If so, returns an OpenFlow
- * error message code (composed with ofp_mkerr()) for the caller to propagate
- * upward. Otherwise, returns 0.
- *
- * The log message mentions 'msg_type'. */
-static int
-reject_slave_controller(struct ofconn *ofconn, const const char *msg_type)
-{
- if (ofconn_get_type(ofconn) == OFCONN_PRIMARY
- && ofconn_get_role(ofconn) == NX_ROLE_SLAVE) {
- static struct vlog_rate_limit perm_rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&perm_rl, "rejecting %s message from slave controller",
- msg_type);
-
- return ofp_mkerr(OFPET_BAD_REQUEST, OFPBRC_EPERM);
- } else {
- return 0;
- }
-}
-
-static int
-handle_packet_out(struct ofconn *ofconn, const struct ofp_header *oh)
-{
- struct ofproto *p = ofconn_get_ofproto(ofconn);
- struct ofp_packet_out *opo;
- struct ofpbuf payload, *buffer;
- union ofp_action *ofp_actions;
- struct action_xlate_ctx ctx;
- struct ofpbuf *odp_actions;
- struct ofpbuf request;
- struct flow flow;
- size_t n_ofp_actions;
- uint16_t in_port;
- int error;
-
- COVERAGE_INC(ofproto_packet_out);
-
- error = reject_slave_controller(ofconn, "OFPT_PACKET_OUT");
- if (error) {
- return error;
- }
-
- /* Get ofp_packet_out. */
- ofpbuf_use_const(&request, oh, ntohs(oh->length));
- opo = ofpbuf_pull(&request, offsetof(struct ofp_packet_out, actions));
-
- /* Get actions. */
- error = ofputil_pull_actions(&request, ntohs(opo->actions_len),
- &ofp_actions, &n_ofp_actions);
- if (error) {
- return error;
- }
-
- /* Get payload. */
- if (opo->buffer_id != htonl(UINT32_MAX)) {
- error = ofconn_pktbuf_retrieve(ofconn, ntohl(opo->buffer_id),
- &buffer, &in_port);
- if (error || !buffer) {
- return error;
- }
- payload = *buffer;
- } else {
- payload = request;
- buffer = NULL;
- }
-
- /* Extract flow, check actions. */
- flow_extract(&payload, 0, ofp_port_to_odp_port(ntohs(opo->in_port)),
- &flow);
- error = validate_actions(ofp_actions, n_ofp_actions, &flow, p->max_ports);
- if (error) {
- goto exit;
- }
-
- /* Send. */
- action_xlate_ctx_init(&ctx, p, &flow, &payload);
- odp_actions = xlate_actions(&ctx, ofp_actions, n_ofp_actions);
- dpif_execute(p->dpif, odp_actions->data, odp_actions->size, &payload);
- ofpbuf_delete(odp_actions);
-
-exit:
- ofpbuf_delete(buffer);
- return 0;
-}
-
-static void
-update_port_config(struct ofproto *p, struct ofport *port,
- ovs_be32 config, ovs_be32 mask)
-{
- mask &= config ^ port->opp.config;
- if (mask & htonl(OFPPC_PORT_DOWN)) {
- if (config & htonl(OFPPC_PORT_DOWN)) {
- netdev_turn_flags_off(port->netdev, NETDEV_UP, true);
- } else {
- netdev_turn_flags_on(port->netdev, NETDEV_UP, true);
- }
- }
-#define REVALIDATE_BITS (OFPPC_NO_RECV | OFPPC_NO_RECV_STP | \
- OFPPC_NO_FWD | OFPPC_NO_FLOOD)
- if (mask & htonl(REVALIDATE_BITS)) {
- COVERAGE_INC(ofproto_costly_flags);
- port->opp.config ^= mask & htonl(REVALIDATE_BITS);
- p->need_revalidate = true;
- }
-#undef REVALIDATE_BITS
- if (mask & htonl(OFPPC_NO_PACKET_IN)) {
- port->opp.config ^= htonl(OFPPC_NO_PACKET_IN);
- }
-}
-
-static int
-handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh)
-{
- struct ofproto *p = ofconn_get_ofproto(ofconn);
- const struct ofp_port_mod *opm = (const struct ofp_port_mod *) oh;
- struct ofport *port;
- int error;
-
- error = reject_slave_controller(ofconn, "OFPT_PORT_MOD");
- if (error) {
- return error;
- }
-
- port = get_port(p, ofp_port_to_odp_port(ntohs(opm->port_no)));
- if (!port) {
- return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_PORT);
- } else if (memcmp(port->opp.hw_addr, opm->hw_addr, OFP_ETH_ALEN)) {
- return ofp_mkerr(OFPET_PORT_MOD_FAILED, OFPPMFC_BAD_HW_ADDR);
- } else {
- update_port_config(p, port, opm->config, opm->mask);
- if (opm->advertise) {
- netdev_set_advertisements(port->netdev, ntohl(opm->advertise));
- }
- }
- return 0;
-}
-
-static struct ofpbuf *
-make_ofp_stats_reply(ovs_be32 xid, ovs_be16 type, size_t body_len)
-{
- struct ofp_stats_reply *osr;
- struct ofpbuf *msg;
-
- msg = ofpbuf_new(MIN(sizeof *osr + body_len, UINT16_MAX));
- osr = put_openflow_xid(sizeof *osr, OFPT_STATS_REPLY, xid, msg);
- osr->type = type;
- osr->flags = htons(0);
- return msg;
-}
-
-static struct ofpbuf *
-start_ofp_stats_reply(const struct ofp_header *request, size_t body_len)
-{
- const struct ofp_stats_request *osr
- = (const struct ofp_stats_request *) request;
- return make_ofp_stats_reply(osr->header.xid, osr->type, body_len);
-}
-
-static void *
-append_ofp_stats_reply(size_t nbytes, struct ofconn *ofconn,
- struct ofpbuf **msgp)
-{
- struct ofpbuf *msg = *msgp;
- assert(nbytes <= UINT16_MAX - sizeof(struct ofp_stats_reply));
- if (nbytes + msg->size > UINT16_MAX) {
- struct ofp_stats_reply *reply = msg->data;
- reply->flags = htons(OFPSF_REPLY_MORE);
- *msgp = make_ofp_stats_reply(reply->header.xid, reply->type, nbytes);
- ofconn_send_reply(ofconn, msg);
- }
- return ofpbuf_put_uninit(*msgp, nbytes);
-}
-
-static struct ofpbuf *
-make_nxstats_reply(ovs_be32 xid, ovs_be32 subtype, size_t body_len)
-{
- struct nicira_stats_msg *nsm;
- struct ofpbuf *msg;
-
- msg = ofpbuf_new(MIN(sizeof *nsm + body_len, UINT16_MAX));
- nsm = put_openflow_xid(sizeof *nsm, OFPT_STATS_REPLY, xid, msg);
- nsm->type = htons(OFPST_VENDOR);
- nsm->flags = htons(0);
- nsm->vendor = htonl(NX_VENDOR_ID);
- nsm->subtype = subtype;
- return msg;
-}
-
-static struct ofpbuf *
-start_nxstats_reply(const struct nicira_stats_msg *request, size_t body_len)
-{
- return make_nxstats_reply(request->header.xid, request->subtype, body_len);
-}
-
-static void
-append_nxstats_reply(size_t nbytes, struct ofconn *ofconn,
- struct ofpbuf **msgp)
-{
- struct ofpbuf *msg = *msgp;
- assert(nbytes <= UINT16_MAX - sizeof(struct nicira_stats_msg));
- if (nbytes + msg->size > UINT16_MAX) {
- struct nicira_stats_msg *reply = msg->data;
- reply->flags = htons(OFPSF_REPLY_MORE);
- *msgp = make_nxstats_reply(reply->header.xid, reply->subtype, nbytes);
- ofconn_send_reply(ofconn, msg);
- }
- ofpbuf_prealloc_tailroom(*msgp, nbytes);
-}
-
-static int
-handle_desc_stats_request(struct ofconn *ofconn,
- const struct ofp_header *request)
-{
- struct ofproto *p = ofconn_get_ofproto(ofconn);
- struct ofp_desc_stats *ods;
- struct ofpbuf *msg;
-
- msg = start_ofp_stats_reply(request, sizeof *ods);
- ods = append_ofp_stats_reply(sizeof *ods, ofconn, &msg);
- memset(ods, 0, sizeof *ods);
- ovs_strlcpy(ods->mfr_desc, p->mfr_desc, sizeof ods->mfr_desc);
- ovs_strlcpy(ods->hw_desc, p->hw_desc, sizeof ods->hw_desc);
- ovs_strlcpy(ods->sw_desc, p->sw_desc, sizeof ods->sw_desc);
- ovs_strlcpy(ods->serial_num, p->serial_desc, sizeof ods->serial_num);
- ovs_strlcpy(ods->dp_desc, p->dp_desc, sizeof ods->dp_desc);
- ofconn_send_reply(ofconn, msg);
-
- return 0;
-}
-
-static int
-handle_table_stats_request(struct ofconn *ofconn,
- const struct ofp_header *request)
-{
- struct ofproto *p = ofconn_get_ofproto(ofconn);
- struct ofp_table_stats *ots;
- struct ofpbuf *msg;
-
- msg = start_ofp_stats_reply(request, sizeof *ots * 2);
-
- /* Classifier table. */
- ots = append_ofp_stats_reply(sizeof *ots, ofconn, &msg);
- memset(ots, 0, sizeof *ots);
- strcpy(ots->name, "classifier");
- ots->wildcards = (ofconn_get_flow_format(ofconn) == NXFF_OPENFLOW10
- ? htonl(OFPFW_ALL) : htonl(OVSFW_ALL));
- ots->max_entries = htonl(1024 * 1024); /* An arbitrary big number. */
- ots->active_count = htonl(classifier_count(&p->cls));
- put_32aligned_be64(&ots->lookup_count, htonll(0)); /* XXX */
- put_32aligned_be64(&ots->matched_count, htonll(0)); /* XXX */
-
- ofconn_send_reply(ofconn, msg);
- return 0;
-}
-
-static void
-append_port_stat(struct ofport *port, struct ofconn *ofconn,
- struct ofpbuf **msgp)
-{
- struct netdev_stats stats;
- struct ofp_port_stats *ops;
-
- /* Intentionally ignore return value, since errors will set
- * 'stats' to all-1s, which is correct for OpenFlow, and
- * netdev_get_stats() will log errors. */
- netdev_get_stats(port->netdev, &stats);
-
- ops = append_ofp_stats_reply(sizeof *ops, ofconn, msgp);
- ops->port_no = port->opp.port_no;
- memset(ops->pad, 0, sizeof ops->pad);
- put_32aligned_be64(&ops->rx_packets, htonll(stats.rx_packets));
- put_32aligned_be64(&ops->tx_packets, htonll(stats.tx_packets));
- put_32aligned_be64(&ops->rx_bytes, htonll(stats.rx_bytes));
- put_32aligned_be64(&ops->tx_bytes, htonll(stats.tx_bytes));
- put_32aligned_be64(&ops->rx_dropped, htonll(stats.rx_dropped));
- put_32aligned_be64(&ops->tx_dropped, htonll(stats.tx_dropped));
- put_32aligned_be64(&ops->rx_errors, htonll(stats.rx_errors));
- put_32aligned_be64(&ops->tx_errors, htonll(stats.tx_errors));
- put_32aligned_be64(&ops->rx_frame_err, htonll(stats.rx_frame_errors));
- put_32aligned_be64(&ops->rx_over_err, htonll(stats.rx_over_errors));
- put_32aligned_be64(&ops->rx_crc_err, htonll(stats.rx_crc_errors));
- put_32aligned_be64(&ops->collisions, htonll(stats.collisions));
-}
-
-static int
-handle_port_stats_request(struct ofconn *ofconn, const struct ofp_header *oh)
-{
- struct ofproto *p = ofconn_get_ofproto(ofconn);
- const struct ofp_port_stats_request *psr = ofputil_stats_body(oh);
- struct ofp_port_stats *ops;
- struct ofpbuf *msg;
- struct ofport *port;
-
- msg = start_ofp_stats_reply(oh, sizeof *ops * 16);
- if (psr->port_no != htons(OFPP_NONE)) {
- port = get_port(p, ofp_port_to_odp_port(ntohs(psr->port_no)));
- if (port) {
- append_port_stat(port, ofconn, &msg);
- }
- } else {
- HMAP_FOR_EACH (port, hmap_node, &p->ports) {
- append_port_stat(port, ofconn, &msg);
- }
- }
-
- ofconn_send_reply(ofconn, msg);
- return 0;
-}
-
-static void
-calc_flow_duration__(long long int start, uint32_t *sec, uint32_t *nsec)
-{
- long long int msecs = time_msec() - start;
- *sec = msecs / 1000;
- *nsec = (msecs % 1000) * (1000 * 1000);
-}
-
-static void
-calc_flow_duration(long long int start, ovs_be32 *sec_be, ovs_be32 *nsec_be)
-{
- uint32_t sec, nsec;
-
- calc_flow_duration__(start, &sec, &nsec);
- *sec_be = htonl(sec);
- *nsec_be = htonl(nsec);
-}
-
-static void
-put_ofp_flow_stats(struct ofconn *ofconn, struct rule *rule,
- ovs_be16 out_port, struct ofpbuf **replyp)
-{
- struct ofp_flow_stats *ofs;
- uint64_t packet_count, byte_count;
- ovs_be64 cookie;
- size_t act_len, len;
-
- if (rule_is_hidden(rule) || !rule_has_out_port(rule, out_port)) {
- return;
- }
-
- act_len = sizeof *rule->actions * rule->n_actions;
- len = offsetof(struct ofp_flow_stats, actions) + act_len;
-
- rule_get_stats(rule, &packet_count, &byte_count);
-
- ofs = append_ofp_stats_reply(len, ofconn, replyp);
- ofs->length = htons(len);
- ofs->table_id = 0;
- ofs->pad = 0;
- ofputil_cls_rule_to_match(&rule->cr, ofconn_get_flow_format(ofconn),
- &ofs->match, rule->flow_cookie, &cookie);
- put_32aligned_be64(&ofs->cookie, cookie);
- calc_flow_duration(rule->created, &ofs->duration_sec, &ofs->duration_nsec);
- ofs->priority = htons(rule->cr.priority);
- ofs->idle_timeout = htons(rule->idle_timeout);
- ofs->hard_timeout = htons(rule->hard_timeout);
- memset(ofs->pad2, 0, sizeof ofs->pad2);
- put_32aligned_be64(&ofs->packet_count, htonll(packet_count));
- put_32aligned_be64(&ofs->byte_count, htonll(byte_count));
- if (rule->n_actions > 0) {
- memcpy(ofs->actions, rule->actions, act_len);
- }
-}
-
-static bool
-is_valid_table(uint8_t table_id)
-{
- if (table_id == 0 || table_id == 0xff) {
- return true;
- } else {
- /* It would probably be better to reply with an error but there doesn't
- * seem to be any appropriate value, so that might just be
- * confusing. */
- VLOG_WARN_RL(&rl, "controller asked for invalid table %"PRIu8,
- table_id);
- return false;
- }
-}
-
-static int
-handle_flow_stats_request(struct ofconn *ofconn, const struct ofp_header *oh)
-{
- const struct ofp_flow_stats_request *fsr = ofputil_stats_body(oh);
- struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
- struct ofpbuf *reply;
-
- COVERAGE_INC(ofproto_flows_req);
- reply = start_ofp_stats_reply(oh, 1024);
- if (is_valid_table(fsr->table_id)) {
- struct cls_cursor cursor;
- struct cls_rule target;
- struct rule *rule;
-
- ofputil_cls_rule_from_match(&fsr->match, 0, NXFF_OPENFLOW10, 0,
- &target);
- cls_cursor_init(&cursor, &ofproto->cls, &target);
- CLS_CURSOR_FOR_EACH (rule, cr, &cursor) {
- put_ofp_flow_stats(ofconn, rule, fsr->out_port, &reply);
- }
- }
- ofconn_send_reply(ofconn, reply);
-
- return 0;
-}
-
-static void
-put_nx_flow_stats(struct ofconn *ofconn, struct rule *rule,
- ovs_be16 out_port, struct ofpbuf **replyp)
-{
- struct nx_flow_stats *nfs;
- uint64_t packet_count, byte_count;
- size_t act_len, start_len;
- struct ofpbuf *reply;
-
- if (rule_is_hidden(rule) || !rule_has_out_port(rule, out_port)) {
- return;
- }
-
- rule_get_stats(rule, &packet_count, &byte_count);
-
- act_len = sizeof *rule->actions * rule->n_actions;
-
- append_nxstats_reply(sizeof *nfs + NXM_MAX_LEN + act_len, ofconn, replyp);
- start_len = (*replyp)->size;
- reply = *replyp;
-
- nfs = ofpbuf_put_uninit(reply, sizeof *nfs);
- nfs->table_id = 0;
- nfs->pad = 0;
- calc_flow_duration(rule->created, &nfs->duration_sec, &nfs->duration_nsec);
- nfs->cookie = rule->flow_cookie;
- nfs->priority = htons(rule->cr.priority);
- nfs->idle_timeout = htons(rule->idle_timeout);
- nfs->hard_timeout = htons(rule->hard_timeout);
- nfs->match_len = htons(nx_put_match(reply, &rule->cr));
- memset(nfs->pad2, 0, sizeof nfs->pad2);
- nfs->packet_count = htonll(packet_count);
- nfs->byte_count = htonll(byte_count);
- if (rule->n_actions > 0) {
- ofpbuf_put(reply, rule->actions, act_len);
- }
- nfs->length = htons(reply->size - start_len);
-}
-
-static int
-handle_nxst_flow(struct ofconn *ofconn, const struct ofp_header *oh)
-{
- struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
- struct nx_flow_stats_request *nfsr;
- struct cls_rule target;
- struct ofpbuf *reply;
- struct ofpbuf b;
- int error;
-
- ofpbuf_use_const(&b, oh, ntohs(oh->length));
-
- /* Dissect the message. */
- nfsr = ofpbuf_pull(&b, sizeof *nfsr);
- error = nx_pull_match(&b, ntohs(nfsr->match_len), 0, &target);