- had_primary = ofproto_has_primary_controller(br->ofproto);
-
- n_controllers = bridge_get_controllers(br, &controllers);
-
- ocs = xmalloc((n_controllers + 1) * sizeof *ocs);
- n_ocs = 0;
-
- bridge_ofproto_controller_for_mgmt(br, &ocs[n_ocs++]);
- for (i = 0; i < n_controllers; i++) {
- struct ovsrec_controller *c = controllers[i];
-
- if (!strncmp(c->target, "punix:", 6)
- || !strncmp(c->target, "unix:", 5)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- /* Prevent remote ovsdb-server users from accessing arbitrary Unix
- * domain sockets and overwriting arbitrary local files. */
- VLOG_ERR_RL(&rl, "%s: not adding Unix domain socket controller "
- "\"%s\" due to possibility for remote exploit",
- dpif_name(br->dpif), c->target);
- continue;
- }
-
- bridge_configure_local_iface_netdev(br, c);
- bridge_ofproto_controller_from_ovsrec(c, &ocs[n_ocs]);
- if (disable_in_band) {
- ocs[n_ocs].band = OFPROTO_OUT_OF_BAND;
- }
- n_ocs++;
- }
-
- ofproto_set_controllers(br->ofproto, ocs, n_ocs);
- free(ocs[0].target); /* From bridge_ofproto_controller_for_mgmt(). */
- free(ocs);
-
- if (had_primary != ofproto_has_primary_controller(br->ofproto)) {
- ofproto_flush_flows(br->ofproto);
- }
-
- /* If there are no controllers and the bridge is in standalone
- * mode, set up a flow that matches every packet and directs
- * them to OFPP_NORMAL (which goes to us). Otherwise, the
- * switch is in secure mode and we won't pass any traffic until
- * a controller has been defined and it tells us to do so. */
- if (!n_controllers
- && ofproto_get_fail_mode(br->ofproto) == OFPROTO_FAIL_STANDALONE) {
- union ofp_action action;
- struct cls_rule rule;
-
- memset(&action, 0, sizeof action);
- action.type = htons(OFPAT_OUTPUT);
- action.output.len = htons(sizeof action);
- action.output.port = htons(OFPP_NORMAL);
- cls_rule_init_catchall(&rule, 0);
- ofproto_add_flow(br->ofproto, &rule, &action, 1);
- }
-}
-
-static void
-bridge_get_all_ifaces(const struct bridge *br, struct shash *ifaces)
-{
- struct port *port;
-
- shash_init(ifaces);
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- struct iface *iface;
-
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- shash_add_once(ifaces, iface->name, iface);
- }
- if (port->n_ifaces > 1 && port->cfg->bond_fake_iface) {
- shash_add_once(ifaces, port->name, NULL);
- }
- }
-}
-
-/* For robustness, in case the administrator moves around datapath ports behind
- * our back, we re-check all the datapath port numbers here.
- *
- * This function will set the 'dp_ifidx' members of interfaces that have
- * disappeared to -1, so only call this function from a context where those
- * 'struct iface's will be removed from the bridge. Otherwise, the -1
- * 'dp_ifidx'es will cause trouble later when we try to send them to the
- * datapath, which doesn't support UINT16_MAX+1 ports. */
-static void
-bridge_fetch_dp_ifaces(struct bridge *br)
-{
- struct dpif_port_dump dump;
- struct dpif_port dpif_port;
- struct port *port;
-
- /* Reset all interface numbers. */
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- struct iface *iface;
-
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- iface->dp_ifidx = -1;
- }
- }
- hmap_clear(&br->ifaces);
-
- DPIF_PORT_FOR_EACH (&dpif_port, &dump, br->dpif) {
- struct iface *iface = iface_lookup(br, dpif_port.name);
- if (iface) {
- if (iface->dp_ifidx >= 0) {
- VLOG_WARN("%s reported interface %s twice",
- dpif_name(br->dpif), dpif_port.name);
- } else if (iface_from_dp_ifidx(br, dpif_port.port_no)) {
- VLOG_WARN("%s reported interface %"PRIu16" twice",
- dpif_name(br->dpif), dpif_port.port_no);
- } else {
- iface->dp_ifidx = dpif_port.port_no;
- hmap_insert(&br->ifaces, &iface->dp_ifidx_node,
- hash_int(iface->dp_ifidx, 0));
- }
-
- iface_set_ofport(iface->cfg,
- (iface->dp_ifidx >= 0
- ? odp_port_to_ofp_port(iface->dp_ifidx)
- : -1));
- }
- }
-}
-\f
-/* Bridge packet processing functions. */
-
-static bool
-set_dst(struct dst *dst, const struct flow *flow,
- const struct port *in_port, const struct port *out_port,
- tag_type *tags)
-{
- struct iface *iface;
- uint16_t vlan;
-
- vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE
- : in_port->vlan >= 0 ? in_port->vlan
- : flow->vlan_tci == 0 ? OFP_VLAN_NONE
- : vlan_tci_to_vid(flow->vlan_tci));
-
- iface = (!out_port->bond
- ? port_get_an_iface(out_port)
- : bond_choose_output_slave(out_port->bond, flow, vlan, tags));
- if (iface) {
- dst->vlan = vlan;
- dst->dp_ifidx = iface->dp_ifidx;
- return true;
- } else {
- return false;
- }
-}
-
-static void
-swap_dst(struct dst *p, struct dst *q)
-{
- struct dst tmp = *p;
- *p = *q;
- *q = tmp;
-}
-
-/* Moves all the dsts with vlan == 'vlan' to the front of the 'n_dsts' in
- * 'dsts'. (This may help performance by reducing the number of VLAN changes
- * that we push to the datapath. We could in fact fully sort the array by
- * vlan, but in most cases there are at most two different vlan tags so that's
- * possibly overkill.) */
-static void
-partition_dsts(struct dst_set *set, int vlan)
-{
- struct dst *first = set->dsts;
- struct dst *last = set->dsts + set->n;
-
- while (first != last) {
- /* Invariants:
- * - All dsts < first have vlan == 'vlan'.
- * - All dsts >= last have vlan != 'vlan'.
- * - first < last. */
- while (first->vlan == vlan) {
- if (++first == last) {
- return;
- }
- }
-
- /* Same invariants, plus one additional:
- * - first->vlan != vlan.
- */
- while (last[-1].vlan != vlan) {
- if (--last == first) {
- return;
- }
- }
-
- /* Same invariants, plus one additional:
- * - last[-1].vlan == vlan.*/
- swap_dst(first++, --last);
- }
-}
-
-static int
-mirror_mask_ffs(mirror_mask_t mask)
-{
- BUILD_ASSERT_DECL(sizeof(unsigned int) >= sizeof(mask));
- return ffs(mask);
-}
-
-static void
-dst_set_init(struct dst_set *set)
-{
- set->dsts = set->builtin;
- set->n = 0;
- set->allocated = ARRAY_SIZE(set->builtin);
-}
-
-static void
-dst_set_add(struct dst_set *set, const struct dst *dst)
-{
- if (set->n >= set->allocated) {
- size_t new_allocated;
- struct dst *new_dsts;
-
- new_allocated = set->allocated * 2;
- new_dsts = xmalloc(new_allocated * sizeof *new_dsts);
- memcpy(new_dsts, set->dsts, set->n * sizeof *new_dsts);
-
- dst_set_free(set);
-
- set->dsts = new_dsts;
- set->allocated = new_allocated;
- }
- set->dsts[set->n++] = *dst;
-}
-
-static void
-dst_set_free(struct dst_set *set)
-{
- if (set->dsts != set->builtin) {
- free(set->dsts);
- }
-}
-
-static bool
-dst_is_duplicate(const struct dst_set *set, const struct dst *test)
-{
- size_t i;
- for (i = 0; i < set->n; i++) {
- if (set->dsts[i].vlan == test->vlan
- && set->dsts[i].dp_ifidx == test->dp_ifidx) {
- return true;
- }
- }
- return false;
-}
-
-static bool
-port_trunks_vlan(const struct port *port, uint16_t vlan)
-{
- return (port->vlan < 0
- && (!port->trunks || bitmap_is_set(port->trunks, vlan)));
-}
-
-static bool
-port_includes_vlan(const struct port *port, uint16_t vlan)
-{
- return vlan == port->vlan || port_trunks_vlan(port, vlan);
-}
-
-static bool
-port_is_floodable(const struct port *port)
-{
- struct iface *iface;
-
- LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
- if (!ofproto_port_is_floodable(port->bridge->ofproto,
- iface->dp_ifidx)) {
- return false;
- }
- }
- return true;
-}
-
-/* Returns an arbitrary interface within 'port'. */
-static struct iface *
-port_get_an_iface(const struct port *port)
-{
- return CONTAINER_OF(list_front(&port->ifaces), struct iface, port_elem);
-}
-
-static void
-compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
- const struct port *in_port, const struct port *out_port,
- struct dst_set *set, tag_type *tags, uint16_t *nf_output_iface)
-{
- mirror_mask_t mirrors = in_port->src_mirrors;
- struct dst dst;
- int flow_vlan;
-
- flow_vlan = vlan_tci_to_vid(flow->vlan_tci);
- if (flow_vlan == 0) {
- flow_vlan = OFP_VLAN_NONE;
- }
-
- if (out_port == FLOOD_PORT) {
- struct port *port;
-
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- if (port != in_port
- && port_is_floodable(port)
- && port_includes_vlan(port, vlan)
- && !port->is_mirror_output_port
- && set_dst(&dst, flow, in_port, port, tags)) {
- mirrors |= port->dst_mirrors;
- dst_set_add(set, &dst);
- }
- }
- *nf_output_iface = NF_OUT_FLOOD;
- } else if (out_port && set_dst(&dst, flow, in_port, out_port, tags)) {
- dst_set_add(set, &dst);
- *nf_output_iface = dst.dp_ifidx;
- mirrors |= out_port->dst_mirrors;
- }
-
- while (mirrors) {
- struct mirror *m = br->mirrors[mirror_mask_ffs(mirrors) - 1];
- if (!m->n_vlans || vlan_is_mirrored(m, vlan)) {
- if (m->out_port) {
- if (set_dst(&dst, flow, in_port, m->out_port, tags)
- && !dst_is_duplicate(set, &dst)) {
- dst_set_add(set, &dst);
- }
- } else {
- struct port *port;
-
- HMAP_FOR_EACH (port, hmap_node, &br->ports) {
- if (port_includes_vlan(port, m->out_vlan)
- && set_dst(&dst, flow, in_port, port, tags))
- {
- if (port->vlan < 0) {
- dst.vlan = m->out_vlan;
- }
- if (dst_is_duplicate(set, &dst)) {
- continue;
- }
-
- /* Use the vlan tag on the original flow instead of
- * the one passed in the vlan parameter. This ensures
- * that we compare the vlan from before any implicit
- * tagging tags place. This is necessary because
- * dst->vlan is the final vlan, after removing implicit
- * tags. */
- if (port == in_port && dst.vlan == flow_vlan) {
- /* Don't send out input port on same VLAN. */
- continue;
- }
- dst_set_add(set, &dst);
- }
- }
- }
- }
- mirrors &= mirrors - 1;
- }
-
- partition_dsts(set, flow_vlan);
-}
-
-static void OVS_UNUSED
-print_dsts(const struct dst_set *set)
-{
- size_t i;
-
- for (i = 0; i < set->n; i++) {
- const struct dst *dst = &set->dsts[i];
-
- printf(">p%"PRIu16, dst->dp_ifidx);
- if (dst->vlan != OFP_VLAN_NONE) {
- printf("v%"PRIu16, dst->vlan);
- }
- }
-}
-
-static void
-compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan,
- const struct port *in_port, const struct port *out_port,
- tag_type *tags, struct ofpbuf *actions,
- uint16_t *nf_output_iface)
-{
- struct dst_set set;
- uint16_t cur_vlan;
- size_t i;
-
- dst_set_init(&set);
- compose_dsts(br, flow, vlan, in_port, out_port, &set, tags,
- nf_output_iface);
-
- cur_vlan = vlan_tci_to_vid(flow->vlan_tci);
- if (cur_vlan == 0) {
- cur_vlan = OFP_VLAN_NONE;
- }
- for (i = 0; i < set.n; i++) {
- const struct dst *dst = &set.dsts[i];
- if (dst->vlan != cur_vlan) {
- if (dst->vlan == OFP_VLAN_NONE) {
- nl_msg_put_flag(actions, ODP_ACTION_ATTR_STRIP_VLAN);
- } else {
- ovs_be16 tci;
- tci = htons(dst->vlan & VLAN_VID_MASK);
- tci |= flow->vlan_tci & htons(VLAN_PCP_MASK);
- nl_msg_put_be16(actions, ODP_ACTION_ATTR_SET_DL_TCI, tci);
- }
- cur_vlan = dst->vlan;
- }
- nl_msg_put_u32(actions, ODP_ACTION_ATTR_OUTPUT, dst->dp_ifidx);
- }
- 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 bridge *br, const struct flow *flow,
- struct port *in_port, bool have_packet)
-{
- int vlan = vlan_tci_to_vid(flow->vlan_tci);
- if (in_port->vlan >= 0) {
- if (vlan) {
- 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,
- br->name, vlan, in_port->name, in_port->vlan);
- }
- return -1;
- }
- vlan = in_port->vlan;
- } else {
- if (!port_includes_vlan(in_port, vlan)) {
- 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",
- br->name, vlan, in_port->name, vlan);
- }
- return -1;
- }
- }
-
- return vlan;
-}
-
-/* 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. */
-static bool
-is_gratuitous_arp(const struct flow *flow)
-{
- return (flow->dl_type == htons(ETH_TYPE_ARP)
- && eth_addr_is_broadcast(flow->dl_dst)
- && (flow->nw_proto == ARP_OP_REPLY
- || (flow->nw_proto == ARP_OP_REQUEST
- && flow->nw_src == flow->nw_dst)));
-}
-
-static void
-update_learning_table(struct bridge *br, const struct flow *flow, int vlan,
- struct port *in_port)
-{
- struct mac_entry *mac;