ofproto-dpif: Make compose_mirror_dsts() harder to screw up.
[sliver-openvswitch.git] / ofproto / ofproto-dpif.c
index 3cceb6b..400b353 100644 (file)
@@ -164,6 +164,8 @@ static void bundle_wait(struct ofbundle *);
 static void stp_run(struct ofproto_dpif *ofproto);
 static void stp_wait(struct ofproto_dpif *ofproto);
 
+static bool ofbundle_includes_vlan(const struct ofbundle *, uint16_t vlan);
+
 struct action_xlate_ctx {
 /* action_xlate_ctx_init() initializes these members. */
 
@@ -949,13 +951,8 @@ send_bpdu_cb(struct ofpbuf *pkt, int port_num, void *ofproto_)
             VLOG_WARN_RL(&rl, "%s: cannot send BPDU on port %d "
                          "with unknown MAC", ofproto->up.name, port_num);
         } else {
-            int error = netdev_send(ofport->up.netdev, pkt);
-            if (error) {
-                VLOG_WARN_RL(&rl, "%s: sending BPDU on port %s failed (%s)",
-                             ofproto->up.name,
-                             netdev_get_name(ofport->up.netdev),
-                             strerror(error));
-            }
+            send_packet(ofproto_dpif_cast(ofport->up.ofproto),
+                        ofport->odp_port, pkt);
         }
     }
     ofpbuf_delete(pkt);
@@ -1040,7 +1037,7 @@ update_stp_port_state(struct ofport_dpif *ofport)
         ofport->stp_state = state;
         ofport->stp_state_entered = time_msec();
 
-        if (fwd_change) {
+        if (fwd_change && ofport->bundle) {
             bundle_update(ofport->bundle);
         }
 
@@ -1070,6 +1067,7 @@ set_stp_port(struct ofport *ofport_,
         if (sp) {
             ofport->stp_port = NULL;
             stp_port_disable(sp);
+            update_stp_port_state(ofport);
         }
         return 0;
     } else if (sp && stp_port_no(sp) != s->port_num
@@ -1109,6 +1107,7 @@ get_stp_port_status(struct ofport *ofport_,
     s->state = stp_port_get_state(sp);
     s->sec_in_state = (time_msec() - ofport->stp_state_entered) / 1000;
     s->role = stp_port_get_role(sp);
+    stp_port_get_counts(sp, &s->tx_count, &s->rx_count, &s->error_count);
 
     return 0;
 }
@@ -1549,12 +1548,8 @@ send_pdu_cb(void *port_, const void *pdu, size_t pdu_size)
                                  pdu_size);
         memcpy(packet_pdu, pdu, pdu_size);
 
-        error = netdev_send(port->up.netdev, &packet);
-        if (error) {
-            VLOG_WARN_RL(&rl, "port %s: sending LACP PDU on iface %s failed "
-                         "(%s)", port->bundle->name,
-                         netdev_get_name(port->up.netdev), strerror(error));
-        }
+        send_packet(ofproto_dpif_cast(port->up.ofproto), port->odp_port,
+                    &packet);
         ofpbuf_uninit(&packet);
     } else {
         VLOG_ERR_RL(&rl, "port %s: cannot obtain Ethernet address of iface "
@@ -1573,7 +1568,16 @@ bundle_send_learning_packets(struct ofbundle *bundle)
     error = n_packets = n_errors = 0;
     LIST_FOR_EACH (e, lru_node, &ofproto->ml->lrus) {
         if (e->port.p != bundle) {
-            int ret = bond_send_learning_packet(bundle->bond, e->mac, e->vlan);
+            struct ofpbuf *learning_packet;
+            struct ofport_dpif *port;
+            int ret;
+
+            learning_packet = bond_compose_learning_packet(bundle->bond, e->mac,
+                                                           e->vlan,
+                                                           (void **)&port);
+            ret = send_packet(ofproto_dpif_cast(port->up.ofproto),
+                              port->odp_port, learning_packet);
+            ofpbuf_delete(learning_packet);
             if (ret) {
                 error = ret;
                 n_errors++;
@@ -2884,10 +2888,10 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet)
      * hash bucket.) */
     vlan_tci = facet->flow.vlan_tci;
     NL_ATTR_FOR_EACH_UNSAFE (a, left, facet->actions, facet->actions_len) {
+        const struct ovs_action_push_vlan *vlan;
         struct ofport_dpif *port;
 
         switch (nl_attr_type(a)) {
-        const struct nlattr *nested;
         case OVS_ACTION_ATTR_OUTPUT:
             port = get_odp_port(ofproto, nl_attr_get_u32(a));
             if (port && port->bundle && port->bundle->bond) {
@@ -2896,20 +2900,13 @@ facet_account(struct ofproto_dpif *ofproto, struct facet *facet)
             }
             break;
 
-        case OVS_ACTION_ATTR_POP:
-            if (nl_attr_get_u16(a) == OVS_KEY_ATTR_8021Q) {
-                vlan_tci = htons(0);
-            }
+        case OVS_ACTION_ATTR_POP_VLAN:
+            vlan_tci = htons(0);
             break;
 
-        case OVS_ACTION_ATTR_PUSH:
-            nested = nl_attr_get(a);
-            if (nl_attr_type(nested) == OVS_KEY_ATTR_8021Q) {
-                const struct ovs_key_8021q *q_key;
-
-                q_key = nl_attr_get_unspec(nested, sizeof(*q_key));
-                vlan_tci = q_key->q_tci;
-            }
+        case OVS_ACTION_ATTR_PUSH_VLAN:
+            vlan = nl_attr_get(a);
+            vlan_tci = vlan->vlan_tci;
             break;
         }
     }
@@ -3247,7 +3244,7 @@ rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow,
     }
 
     cls = &ofproto->up.tables[table_id];
-    if (flow->tos_frag & FLOW_FRAG_ANY
+    if (flow->nw_frag & FLOW_NW_FRAG_ANY
         && ofproto->up.frag_handling == OFPC_FRAG_NORMAL) {
         /* For OFPC_NORMAL frag_handling, we must pretend that transport ports
          * are unavailable. */
@@ -3585,13 +3582,10 @@ fix_sflow_action(struct action_xlate_ctx *ctx)
 }
 
 static void
-commit_action__(struct ofpbuf *odp_actions,
-                enum ovs_action_attr act_type,
-                enum ovs_key_attr key_type,
-                const void *key, size_t key_size)
+commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type,
+                  const void *key, size_t key_size)
 {
-    size_t offset = nl_msg_start_nested(odp_actions, act_type);
-
+    size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
     nl_msg_put_unspec(odp_actions, key_type, key, key_size);
     nl_msg_end_nested(odp_actions, offset);
 }
@@ -3605,8 +3599,8 @@ commit_set_tun_id_action(const struct flow *flow, struct flow *base,
     }
     base->tun_id = flow->tun_id;
 
-    commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
-             OVS_KEY_ATTR_TUN_ID, &base->tun_id, sizeof(base->tun_id));
+    commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
+                      &base->tun_id, sizeof(base->tun_id));
 }
 
 static void
@@ -3626,8 +3620,8 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
     memcpy(eth_key.eth_src, base->dl_src, ETH_ADDR_LEN);
     memcpy(eth_key.eth_dst, base->dl_dst, ETH_ADDR_LEN);
 
-    commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
-             OVS_KEY_ATTR_ETHERNET, &eth_key, sizeof(eth_key));
+    commit_set_action(odp_actions, OVS_KEY_ATTR_ETHERNET,
+                      &eth_key, sizeof(eth_key));
 }
 
 static void
@@ -3640,18 +3634,16 @@ commit_vlan_action(struct action_xlate_ctx *ctx, ovs_be16 new_tci)
     }
 
     if (base->vlan_tci & htons(VLAN_CFI)) {
-        nl_msg_put_u16(ctx->odp_actions, OVS_ACTION_ATTR_POP,
-                                       OVS_KEY_ATTR_8021Q);
+        nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_VLAN);
     }
 
     if (new_tci & htons(VLAN_CFI)) {
-        struct ovs_key_8021q q_key;
-
-        q_key.q_tpid = htons(ETH_TYPE_VLAN);
-        q_key.q_tci = new_tci & ~htons(VLAN_CFI);
+        struct ovs_action_push_vlan vlan;
 
-        commit_action__(ctx->odp_actions, OVS_ACTION_ATTR_PUSH,
-                            OVS_KEY_ATTR_8021Q, &q_key, sizeof(q_key));
+        vlan.vlan_tpid = htons(ETH_TYPE_VLAN);
+        vlan.vlan_tci = new_tci;
+        nl_msg_put_unspec(ctx->odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
+                          &vlan, sizeof vlan);
     }
     base->vlan_tci = new_tci;
 }
@@ -3660,7 +3652,6 @@ static void
 commit_set_nw_action(const struct flow *flow, struct flow *base,
                      struct ofpbuf *odp_actions)
 {
-    int frag = base->tos_frag & FLOW_FRAG_MASK;
     struct ovs_key_ipv4 ipv4_key;
 
     if (base->dl_type != htons(ETH_TYPE_IP) ||
@@ -3670,22 +3661,23 @@ commit_set_nw_action(const struct flow *flow, struct flow *base,
 
     if (base->nw_src == flow->nw_src &&
         base->nw_dst == flow->nw_dst &&
-        base->tos_frag == flow->tos_frag) {
+        base->nw_tos == flow->nw_tos &&
+        base->nw_ttl == flow->nw_ttl &&
+        base->nw_frag == flow->nw_frag) {
         return;
     }
 
-
-    memset(&ipv4_key, 0, sizeof(ipv4_key));
     ipv4_key.ipv4_src = base->nw_src = flow->nw_src;
     ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst;
     ipv4_key.ipv4_proto = base->nw_proto;
-    ipv4_key.ipv4_tos = flow->tos_frag & IP_DSCP_MASK;
-    ipv4_key.ipv4_frag = (frag == 0 ? OVS_FRAG_TYPE_NONE
-                          : frag == FLOW_FRAG_ANY ? OVS_FRAG_TYPE_FIRST
-                          : OVS_FRAG_TYPE_LATER);
+    ipv4_key.ipv4_tos = flow->nw_tos;
+    ipv4_key.ipv4_ttl = flow->nw_ttl;
+    ipv4_key.ipv4_frag = (base->nw_frag == 0 ? OVS_FRAG_TYPE_NONE
+                          : base->nw_frag == FLOW_NW_FRAG_ANY
+                          ? OVS_FRAG_TYPE_FIRST : OVS_FRAG_TYPE_LATER);
 
-    commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
-             OVS_KEY_ATTR_IPV4, &ipv4_key, sizeof(ipv4_key));
+    commit_set_action(odp_actions, OVS_KEY_ATTR_IPV4,
+                      &ipv4_key, sizeof(ipv4_key));
 }
 
 static void
@@ -3707,8 +3699,8 @@ commit_set_port_action(const struct flow *flow, struct flow *base,
         port_key.tcp_src = base->tp_src = flow->tp_src;
         port_key.tcp_dst = base->tp_dst = flow->tp_dst;
 
-        commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
-             OVS_KEY_ATTR_TCP, &port_key, sizeof(port_key));
+        commit_set_action(odp_actions, OVS_KEY_ATTR_TCP,
+                          &port_key, sizeof(port_key));
 
     } else if (flow->nw_proto == IPPROTO_UDP) {
         struct ovs_key_udp port_key;
@@ -3716,8 +3708,8 @@ commit_set_port_action(const struct flow *flow, struct flow *base,
         port_key.udp_src = base->tp_src = flow->tp_src;
         port_key.udp_dst = base->tp_dst = flow->tp_dst;
 
-        commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
-             OVS_KEY_ATTR_UDP, &port_key, sizeof(port_key));
+        commit_set_action(odp_actions, OVS_KEY_ATTR_UDP,
+                          &port_key, sizeof(port_key));
     }
 }
 
@@ -3730,9 +3722,8 @@ commit_set_priority_action(const struct flow *flow, struct flow *base,
     }
     base->priority = flow->priority;
 
-    commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
-                    OVS_KEY_ATTR_PRIORITY, &base->priority,
-                    sizeof(base->priority));
+    commit_set_action(odp_actions, OVS_KEY_ATTR_PRIORITY,
+                      &base->priority, sizeof(base->priority));
 }
 
 static void
@@ -4166,8 +4157,8 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
             break;
 
         case OFPUTIL_OFPAT_SET_NW_TOS:
-            ctx->flow.tos_frag &= ~IP_DSCP_MASK;
-            ctx->flow.tos_frag |= ia->nw_tos.nw_tos & IP_DSCP_MASK;
+            ctx->flow.nw_tos &= ~IP_DSCP_MASK;
+            ctx->flow.nw_tos |= ia->nw_tos.nw_tos & IP_DSCP_MASK;
             break;
 
         case OFPUTIL_OFPAT_SET_TP_SRC:
@@ -4308,7 +4299,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
     ctx->table_id = 0;
     ctx->exit = false;
 
-    if (ctx->flow.tos_frag & FLOW_FRAG_ANY) {
+    if (ctx->flow.nw_frag & FLOW_NW_FRAG_ANY) {
         switch (ctx->ofproto->up.frag_handling) {
         case OFPC_FRAG_NORMAL:
             /* We must pretend that transport ports are unavailable. */
@@ -4396,6 +4387,58 @@ input_vid_to_vlan(const struct ofbundle *in_bundle, uint16_t vid)
     }
 }
 
+/* Checks whether a packet with the given 'vid' may ingress on 'in_bundle'.
+ * If so, returns true.  Otherwise, returns false and, if 'warn' is true, logs
+ * a warning.
+ *
+ * 'vid' should be the VID obtained from the 802.1Q header that was received as
+ * part of a packet (specify 0 if there was no 802.1Q header), in the range
+ * 0...4095. */
+static bool
+input_vid_is_valid(uint16_t vid, struct ofbundle *in_bundle, bool warn)
+{
+    switch (in_bundle->vlan_mode) {
+    case PORT_VLAN_ACCESS:
+        if (vid) {
+            if (warn) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
+                             "packet received on port %s configured as VLAN "
+                             "%"PRIu16" access port",
+                             in_bundle->ofproto->up.name, vid,
+                             in_bundle->name, in_bundle->vlan);
+            }
+            return false;
+        }
+        return true;
+
+    case PORT_VLAN_NATIVE_UNTAGGED:
+    case PORT_VLAN_NATIVE_TAGGED:
+        if (!vid) {
+            /* Port must always carry its native VLAN. */
+            return true;
+        }
+        /* Fall through. */
+    case PORT_VLAN_TRUNK:
+        if (!ofbundle_includes_vlan(in_bundle, vid)) {
+            if (warn) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" packet "
+                             "received on port %s not configured for trunking "
+                             "VLAN %"PRIu16,
+                             in_bundle->ofproto->up.name, vid,
+                             in_bundle->name, vid);
+            }
+            return false;
+        }
+        return true;
+
+    default:
+        NOT_REACHED();
+    }
+
+}
+
 /* Given 'vlan', the VLAN that a packet belongs to, and
  * 'out_bundle', a bundle on which the packet is to be output, returns the VID
  * that should be included in the 802.1Q header.  (If the return value is 0,
@@ -4607,7 +4650,7 @@ compose_mirror_dsts(struct action_xlate_ctx *ctx,
     }
 
     flow_vid = vlan_tci_to_vid(ctx->flow.vlan_tci);
-    while (mirrors) {
+    for (; mirrors; mirrors &= mirrors - 1) {
         struct ofmirror *m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1];
         if (vlan_is_mirrored(m, vlan)) {
             struct dst dst;
@@ -4622,6 +4665,7 @@ compose_mirror_dsts(struct action_xlate_ctx *ctx,
 
                 HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
                     if (ofbundle_includes_vlan(bundle, m->out_vlan)
+                        && !bundle->mirror_out
                         && set_dst(ctx, &dst, in_bundle, bundle))
                     {
                         /* set_dst() got dst->vid from the input packet's VLAN,
@@ -4641,7 +4685,6 @@ compose_mirror_dsts(struct action_xlate_ctx *ctx,
                 }
             }
         }
-        mirrors &= mirrors - 1;
     }
 }
 
@@ -4696,49 +4739,6 @@ compose_actions(struct action_xlate_ctx *ctx, uint16_t vlan,
     dst_set_free(&set);
 }
 
-/* Returns the effective vlan of a packet, taking into account both the
- * 802.1Q header and implicitly tagged ports.  A value of 0 indicates that
- * the packet is untagged and -1 indicates it has an invalid header and
- * should be dropped. */
-static int
-flow_get_vlan(struct ofproto_dpif *ofproto, const struct flow *flow,
-              struct ofbundle *in_bundle, bool have_packet)
-{
-    int vlan = vlan_tci_to_vid(flow->vlan_tci);
-    if (vlan) {
-        if (in_bundle->vlan_mode == PORT_VLAN_ACCESS) {
-            /* Drop tagged packet on access port */
-            if (have_packet) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
-                             "packet received on port %s configured with "
-                             "implicit VLAN %"PRIu16,
-                             ofproto->up.name, vlan,
-                             in_bundle->name, in_bundle->vlan);
-            }
-            return -1;
-        } else if (ofbundle_includes_vlan(in_bundle, vlan)) {
-            return vlan;
-        } else {
-            /* Drop packets from a VLAN not member of the trunk */
-            if (have_packet) {
-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
-                             "packet received on port %s not configured for "
-                             "trunking VLAN %d",
-                             ofproto->up.name, vlan, in_bundle->name, vlan);
-            }
-            return -1;
-        }
-    } else {
-        if (in_bundle->vlan_mode != PORT_VLAN_TRUNK) {
-            return in_bundle->vlan;
-        } else {
-            return ofbundle_includes_vlan(in_bundle, 0) ? 0 : -1;
-        }
-    }
-}
-
 /* A VM broadcasts a gratuitous ARP to indicate that it has resumed after
  * migration.  Older Citrix-patched Linux DomU used gratuitous ARP replies to
  * indicate this; newer upstream kernels use gratuitous ARP requests. */
@@ -4789,7 +4789,7 @@ update_learning_table(struct ofproto_dpif *ofproto,
     }
 }
 
-/* Determines whether packets in 'flow' within 'br' should be forwarded or
+/* Determines whether packets in 'flow' within 'ofproto' should be forwarded or
  * dropped.  Returns true if they may be forwarded, false if they should be
  * dropped.
  *
@@ -4799,12 +4799,12 @@ update_learning_table(struct ofproto_dpif *ofproto,
  * 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
+ * Sets '*in_bundlep' to the input bundle.  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().
+ * packet, as returned by input_vid_to_vlan().
  *
  * May also add tags to '*tags', although the current implementation only does
  * so in one special case.
@@ -4816,8 +4816,11 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow,
 {
     struct ofport_dpif *in_port;
     struct ofbundle *in_bundle;
+    uint16_t vid;
     int vlan;
 
+    *vlanp = -1;
+
     /* Find the port and bundle for the received packet. */
     in_port = get_ofp_port(ofproto, flow->in_port);
     *in_bundlep = in_bundle = in_port ? in_port->bundle : NULL;
@@ -4841,13 +4844,23 @@ is_admissible(struct ofproto_dpif *ofproto, const struct flow *flow,
                          "port %"PRIu16,
                          ofproto->up.name, flow->in_port);
         }
-        *vlanp = -1;
         return false;
     }
-    *vlanp = vlan = flow_get_vlan(ofproto, flow, in_bundle, have_packet);
-    if (vlan < 0) {
+
+    if (flow->dl_type == htons(ETH_TYPE_VLAN) &&
+        !(flow->vlan_tci & htons(VLAN_CFI))) {
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+        VLOG_WARN_RL(&rl, "bridge %s: dropping packet with partial "
+                     "VLAN tag received on port %s",
+                     ofproto->up.name, in_bundle->name);
+        return -1;
+    }
+
+    vid = vlan_tci_to_vid(flow->vlan_tci);
+    if (!input_vid_is_valid(vid, in_bundle, have_packet)) {
         return false;
     }
+    *vlanp = vlan = input_vid_to_vlan(in_bundle, vid);
 
     /* Drop frames for reserved multicast addresses only if forward_bpdu
      * option is absent. */
@@ -5077,6 +5090,10 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
     struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
     int error;
 
+    if (flow->in_port >= ofproto->max_ports && flow->in_port < OFPP_MAX) {
+        return ofp_mkerr_nicira(OFPET_BAD_REQUEST, NXBRC_BAD_IN_PORT);
+    }
+
     error = validate_actions(ofp_actions, n_ofp_actions, flow,
                              ofproto->max_ports);
     if (!error) {
@@ -5115,6 +5132,22 @@ ofproto_dpif_lookup(const char *name)
             : NULL);
 }
 
+static void
+ofproto_unixctl_fdb_flush(struct unixctl_conn *conn,
+                         const char *args, void *aux OVS_UNUSED)
+{
+    const struct ofproto_dpif *ofproto;
+
+    ofproto = ofproto_dpif_lookup(args);
+    if (!ofproto) {
+        unixctl_command_reply(conn, 501, "no such bridge");
+        return;
+    }
+    mac_learning_flush(ofproto->ml);
+
+    unixctl_command_reply(conn, 200, "table successfully flushed");
+}
+
 static void
 ofproto_unixctl_fdb_show(struct unixctl_conn *conn,
                          const char *args, void *aux OVS_UNUSED)
@@ -5367,6 +5400,8 @@ ofproto_dpif_unixctl_init(void)
     unixctl_command_register("ofproto/trace",
                       "bridge {tun_id in_port packet | odp_flow [-generate]}",
                       ofproto_unixctl_trace, NULL);
+    unixctl_command_register("fdb/flush", "bridge", ofproto_unixctl_fdb_flush,
+                             NULL);
     unixctl_command_register("fdb/show", "bridge", ofproto_unixctl_fdb_show,
                              NULL); 
     unixctl_command_register("ofproto/clog", "", ofproto_dpif_clog, NULL);