- if (br->ml) {
- mac_learning_run(br->ml, ofproto_get_revalidate_set(br->ofproto));
- }
- bond_run(br);
- brstp_run(br);
-
- error = ofproto_run2(br->ofproto, br->flush);
- br->flush = false;
-
- return error;
-}
-
-static const char *
-bridge_get_controller(const struct bridge *br)
-{
- const char *controller;
-
- controller = cfg_get_string(0, "bridge.%s.controller", br->name);
- if (!controller) {
- controller = cfg_get_string(0, "mgmt.controller");
- }
- return controller && controller[0] ? controller : NULL;
-}
-
-static bool
-check_duplicate_ifaces(struct bridge *br, struct iface *iface, void *ifaces_)
-{
- struct svec *ifaces = ifaces_;
- if (!svec_contains(ifaces, iface->name)) {
- svec_add(ifaces, iface->name);
- svec_sort(ifaces);
- return true;
- } else {
- VLOG_ERR("bridge %s: %s interface is on multiple ports, "
- "removing from %s",
- br->name, iface->name, iface->port->name);
- return false;
- }
-}
-
-static void
-bridge_reconfigure_one(struct bridge *br)
-{
- struct svec old_ports, new_ports, ifaces;
- struct svec listeners, old_listeners;
- struct svec snoops, old_snoops;
- size_t i;
-
- /* Collect old ports. */
- svec_init(&old_ports);
- for (i = 0; i < br->n_ports; i++) {
- svec_add(&old_ports, br->ports[i]->name);
- }
- svec_sort(&old_ports);
- assert(svec_is_unique(&old_ports));
-
- /* Collect new ports. */
- svec_init(&new_ports);
- cfg_get_all_keys(&new_ports, "bridge.%s.port", br->name);
- svec_sort(&new_ports);
- if (bridge_get_controller(br)) {
- char local_name[IF_NAMESIZE];
- int error;
-
- error = dpif_port_get_name(br->dpif, ODPP_LOCAL,
- local_name, sizeof local_name);
- if (!error && !svec_contains(&new_ports, local_name)) {
- svec_add(&new_ports, local_name);
- svec_sort(&new_ports);
- }
- }
- if (!svec_is_unique(&new_ports)) {
- VLOG_WARN("bridge %s: %s specified twice as bridge port",
- br->name, svec_get_duplicate(&new_ports));
- svec_unique(&new_ports);
- }
-
- ofproto_set_mgmt_id(br->ofproto, mgmt_id);
-
- /* Get rid of deleted ports and add new ports. */
- for (i = 0; i < br->n_ports; ) {
- struct port *port = br->ports[i];
- if (!svec_contains(&new_ports, port->name)) {
- port_destroy(port);
- } else {
- i++;
- }
- }
- for (i = 0; i < new_ports.n; i++) {
- const char *name = new_ports.names[i];
- if (!svec_contains(&old_ports, name)) {
- port_create(br, name);
- }
- }
- svec_destroy(&old_ports);
- svec_destroy(&new_ports);
-
- /* Reconfigure all ports. */
- for (i = 0; i < br->n_ports; i++) {
- port_reconfigure(br->ports[i]);
- }
-
- /* Check and delete duplicate interfaces. */
- svec_init(&ifaces);
- iterate_and_prune_ifaces(br, check_duplicate_ifaces, &ifaces);
- svec_destroy(&ifaces);
-
- /* Delete all flows if we're switching from connected to standalone or vice
- * versa. (XXX Should we delete all flows if we are switching from one
- * controller to another?) */
-
- /* Configure OpenFlow management listeners. */
- svec_init(&listeners);
- cfg_get_all_strings(&listeners, "bridge.%s.openflow.listeners", br->name);
- if (!listeners.n) {
- svec_add_nocopy(&listeners, xasprintf("punix:%s/%s.mgmt",
- ovs_rundir, br->name));
- } else if (listeners.n == 1 && !strcmp(listeners.names[0], "none")) {
- svec_clear(&listeners);
- }
- svec_sort_unique(&listeners);
-
- svec_init(&old_listeners);
- ofproto_get_listeners(br->ofproto, &old_listeners);
- svec_sort_unique(&old_listeners);
-
- if (!svec_equal(&listeners, &old_listeners)) {
- ofproto_set_listeners(br->ofproto, &listeners);
- }
- svec_destroy(&listeners);
- svec_destroy(&old_listeners);
-
- /* Configure OpenFlow controller connection snooping. */
- svec_init(&snoops);
- cfg_get_all_strings(&snoops, "bridge.%s.openflow.snoops", br->name);
- if (!snoops.n) {
- svec_add_nocopy(&snoops, xasprintf("punix:%s/%s.snoop",
- ovs_rundir, br->name));
- } else if (snoops.n == 1 && !strcmp(snoops.names[0], "none")) {
- svec_clear(&snoops);
- }
- svec_sort_unique(&snoops);
-
- svec_init(&old_snoops);
- ofproto_get_snoops(br->ofproto, &old_snoops);
- svec_sort_unique(&old_snoops);
-
- if (!svec_equal(&snoops, &old_snoops)) {
- ofproto_set_snoops(br->ofproto, &snoops);
- }
- svec_destroy(&snoops);
- svec_destroy(&old_snoops);
-
- mirror_reconfigure(br);
-}
-
-static void
-bridge_reconfigure_controller(struct bridge *br)
-{
- char *pfx = xasprintf("bridge.%s.controller", br->name);
- const char *controller;
-
- controller = bridge_get_controller(br);
- if ((br->controller != NULL) != (controller != NULL)) {
- ofproto_flush_flows(br->ofproto);
- }
- free(br->controller);
- br->controller = controller ? xstrdup(controller) : NULL;
-
- if (controller) {
- const char *fail_mode;
- int max_backoff, probe;
- int rate_limit, burst_limit;
-
- if (!strcmp(controller, "discover")) {
- bool update_resolv_conf = true;
-
- if (cfg_has("%s.update-resolv.conf", pfx)) {
- update_resolv_conf = cfg_get_bool(0, "%s.update-resolv.conf",
- pfx);
- }
- ofproto_set_discovery(br->ofproto, true,
- cfg_get_string(0, "%s.accept-regex", pfx),
- update_resolv_conf);
- } else {
- struct iface *local_iface;
- bool in_band;
-
- in_band = (!cfg_is_valid(CFG_BOOL | CFG_REQUIRED,
- "%s.in-band", pfx)
- || cfg_get_bool(0, "%s.in-band", pfx));
- ofproto_set_discovery(br->ofproto, false, NULL, NULL);
- ofproto_set_in_band(br->ofproto, in_band);
-
- local_iface = bridge_get_local_iface(br);
- if (local_iface
- && cfg_is_valid(CFG_IP | CFG_REQUIRED, "%s.ip", pfx)) {
- struct netdev *netdev = local_iface->netdev;
- struct in_addr ip, mask, gateway;
- ip.s_addr = cfg_get_ip(0, "%s.ip", pfx);
- mask.s_addr = cfg_get_ip(0, "%s.netmask", pfx);
- gateway.s_addr = cfg_get_ip(0, "%s.gateway", pfx);
-
- netdev_turn_flags_on(netdev, NETDEV_UP, true);
- if (!mask.s_addr) {
- mask.s_addr = guess_netmask(ip.s_addr);
- }
- if (!netdev_set_in4(netdev, ip, mask)) {
- VLOG_INFO("bridge %s: configured IP address "IP_FMT", "
- "netmask "IP_FMT,
- br->name, IP_ARGS(&ip.s_addr),
- IP_ARGS(&mask.s_addr));
- }
-
- if (gateway.s_addr) {
- if (!netdev_add_router(netdev, gateway)) {
- VLOG_INFO("bridge %s: configured gateway "IP_FMT,
- br->name, IP_ARGS(&gateway.s_addr));
- }
- }
- }
- }
-
- fail_mode = cfg_get_string(0, "%s.fail-mode", pfx);
- if (!fail_mode) {
- fail_mode = cfg_get_string(0, "mgmt.fail-mode");
- }
- ofproto_set_failure(br->ofproto,
- (!fail_mode
- || !strcmp(fail_mode, "standalone")
- || !strcmp(fail_mode, "open")));
-
- probe = cfg_get_int(0, "%s.inactivity-probe", pfx);
- if (probe < 5) {
- probe = cfg_get_int(0, "mgmt.inactivity-probe");
- if (probe < 5) {
- probe = 5;
- }
- }
- ofproto_set_probe_interval(br->ofproto, probe);
-
- max_backoff = cfg_get_int(0, "%s.max-backoff", pfx);
- if (!max_backoff) {
- max_backoff = cfg_get_int(0, "mgmt.max-backoff");
- if (!max_backoff) {
- max_backoff = 8;
- }
- }
- ofproto_set_max_backoff(br->ofproto, max_backoff);
-
- rate_limit = cfg_get_int(0, "%s.rate-limit", pfx);
- if (!rate_limit) {
- rate_limit = cfg_get_int(0, "mgmt.rate-limit");
- }
- burst_limit = cfg_get_int(0, "%s.burst-limit", pfx);
- if (!burst_limit) {
- burst_limit = cfg_get_int(0, "mgmt.burst-limit");
- }
- ofproto_set_rate_limit(br->ofproto, rate_limit, burst_limit);
-
- ofproto_set_stp(br->ofproto, cfg_get_bool(0, "%s.stp", pfx));
-
- if (cfg_has("%s.commands.acl", pfx)) {
- struct svec command_acls;
- char *command_acl;
-
- svec_init(&command_acls);
- cfg_get_all_strings(&command_acls, "%s.commands.acl", pfx);
- command_acl = svec_join(&command_acls, ",", "");
-
- ofproto_set_remote_execution(br->ofproto, command_acl,
- cfg_get_string(0, "%s.commands.dir",
- pfx));
-
- svec_destroy(&command_acls);
- free(command_acl);
- } else {
- ofproto_set_remote_execution(br->ofproto, NULL, NULL);
- }
- } else {
- union ofp_action action;
- flow_t flow;
-
- /* Set up a flow that matches every packet and directs them to
- * OFPP_NORMAL (which goes to us). */
- memset(&action, 0, sizeof action);
- action.type = htons(OFPAT_OUTPUT);
- action.output.len = htons(sizeof action);
- action.output.port = htons(OFPP_NORMAL);
- memset(&flow, 0, sizeof flow);
- ofproto_add_flow(br->ofproto, &flow, OFPFW_ALL, 0,
- &action, 1, 0);
-
- ofproto_set_in_band(br->ofproto, false);
- ofproto_set_max_backoff(br->ofproto, 1);
- ofproto_set_probe_interval(br->ofproto, 5);
- ofproto_set_failure(br->ofproto, false);
- ofproto_set_stp(br->ofproto, false);
- }
- free(pfx);
-
- ofproto_set_controller(br->ofproto, br->controller);
-}
-
-static void
-bridge_get_all_ifaces(const struct bridge *br, struct svec *ifaces)
-{
- size_t i, j;
-
- svec_init(ifaces);
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
- for (j = 0; j < port->n_ifaces; j++) {
- struct iface *iface = port->ifaces[j];
- svec_add(ifaces, iface->name);
- }
- if (port->n_ifaces > 1
- && cfg_get_bool(0, "bonding.%s.fake-iface", port->name)) {
- svec_add(ifaces, port->name);
- }
- }
- svec_sort_unique(ifaces);
-}
-
-/* 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 odp_port *dpif_ports;
- size_t n_dpif_ports;
- size_t i, j;
-
- /* Reset all interface numbers. */
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
- for (j = 0; j < port->n_ifaces; j++) {
- struct iface *iface = port->ifaces[j];
- iface->dp_ifidx = -1;
- }
- }
- port_array_clear(&br->ifaces);
-
- dpif_port_list(br->dpif, &dpif_ports, &n_dpif_ports);
- for (i = 0; i < n_dpif_ports; i++) {
- struct odp_port *p = &dpif_ports[i];
- struct iface *iface = iface_lookup(br, p->devname);
- if (iface) {
- if (iface->dp_ifidx >= 0) {
- VLOG_WARN("%s reported interface %s twice",
- dpif_name(br->dpif), p->devname);
- } else if (iface_from_dp_ifidx(br, p->port)) {
- VLOG_WARN("%s reported interface %"PRIu16" twice",
- dpif_name(br->dpif), p->port);
- } else {
- port_array_set(&br->ifaces, p->port, iface);
- iface->dp_ifidx = p->port;
- }
- }
- }
- free(dpif_ports);
-}
-\f
-/* Bridge packet processing functions. */
-
-static int
-bond_hash(const uint8_t mac[ETH_ADDR_LEN])
-{
- return hash_bytes(mac, ETH_ADDR_LEN, 0) & BOND_MASK;
-}
-
-static struct bond_entry *
-lookup_bond_entry(const struct port *port, const uint8_t mac[ETH_ADDR_LEN])
-{
- return &port->bond_hash[bond_hash(mac)];
-}
-
-static int
-bond_choose_iface(const struct port *port)
-{
- size_t i;
- for (i = 0; i < port->n_ifaces; i++) {
- if (port->ifaces[i]->enabled) {
- return i;
- }
- }
- return -1;
-}
-
-static bool
-choose_output_iface(const struct port *port, const uint8_t *dl_src,
- uint16_t *dp_ifidx, tag_type *tags)
-{
- struct iface *iface;
-
- assert(port->n_ifaces);
- if (port->n_ifaces == 1) {
- iface = port->ifaces[0];
- } else {
- struct bond_entry *e = lookup_bond_entry(port, dl_src);
- if (e->iface_idx < 0 || e->iface_idx >= port->n_ifaces
- || !port->ifaces[e->iface_idx]->enabled) {
- /* XXX select interface properly. The current interface selection
- * is only good for testing the rebalancing code. */
- e->iface_idx = bond_choose_iface(port);
- if (e->iface_idx < 0) {
- *tags |= port->no_ifaces_tag;
- return false;
- }
- e->iface_tag = tag_create_random();
- ((struct port *) port)->bond_compat_is_stale = true;
- }
- *tags |= e->iface_tag;
- iface = port->ifaces[e->iface_idx];
- }
- *dp_ifidx = iface->dp_ifidx;
- *tags |= iface->tag; /* Currently only used for bonding. */
- return true;
-}
-
-static void
-bond_link_status_update(struct iface *iface, bool carrier)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
- struct port *port = iface->port;
-
- if ((carrier == iface->enabled) == (iface->delay_expires == LLONG_MAX)) {
- /* Nothing to do. */
- return;
- }
- VLOG_INFO_RL(&rl, "interface %s: carrier %s",
- iface->name, carrier ? "detected" : "dropped");
- if (carrier == iface->enabled) {
- iface->delay_expires = LLONG_MAX;
- VLOG_INFO_RL(&rl, "interface %s: will not be %s",
- iface->name, carrier ? "disabled" : "enabled");
- } else if (carrier && port->updelay && port->active_iface < 0) {
- iface->delay_expires = time_msec();
- VLOG_INFO_RL(&rl, "interface %s: skipping %d ms updelay since no "
- "other interface is up", iface->name, port->updelay);
- } else {
- int delay = carrier ? port->updelay : port->downdelay;
- iface->delay_expires = time_msec() + delay;
- if (delay) {
- VLOG_INFO_RL(&rl,
- "interface %s: will be %s if it stays %s for %d ms",
- iface->name,
- carrier ? "enabled" : "disabled",
- carrier ? "up" : "down",
- delay);
- }
- }
-}
-
-static void
-bond_choose_active_iface(struct port *port)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
-
- port->active_iface = bond_choose_iface(port);
- port->active_iface_tag = tag_create_random();
- if (port->active_iface >= 0) {
- VLOG_INFO_RL(&rl, "port %s: active interface is now %s",
- port->name, port->ifaces[port->active_iface]->name);
- } else {
- VLOG_WARN_RL(&rl, "port %s: all ports disabled, no active interface",
- port->name);
- }
-}
-
-static void
-bond_enable_slave(struct iface *iface, bool enable)
-{
- struct port *port = iface->port;
- struct bridge *br = port->bridge;
-
- iface->delay_expires = LLONG_MAX;
- if (enable == iface->enabled) {
- return;
- }
-
- iface->enabled = enable;
- if (!iface->enabled) {
- VLOG_WARN("interface %s: disabled", iface->name);
- ofproto_revalidate(br->ofproto, iface->tag);
- if (iface->port_ifidx == port->active_iface) {
- ofproto_revalidate(br->ofproto,
- port->active_iface_tag);
- bond_choose_active_iface(port);
- }
- bond_send_learning_packets(port);
- } else {
- VLOG_WARN("interface %s: enabled", iface->name);
- if (port->active_iface < 0) {
- ofproto_revalidate(br->ofproto, port->no_ifaces_tag);
- bond_choose_active_iface(port);
- bond_send_learning_packets(port);
- }
- iface->tag = tag_create_random();
- }
- port_update_bond_compat(port);
-}
-
-static void
-bond_run(struct bridge *br)
-{
- size_t i, j;
-
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
-
- if (port->bond_compat_is_stale) {
- port->bond_compat_is_stale = false;
- port_update_bond_compat(port);
- }
-
- if (port->n_ifaces < 2) {
- continue;
- }
- for (j = 0; j < port->n_ifaces; j++) {
- struct iface *iface = port->ifaces[j];
- if (time_msec() >= iface->delay_expires) {
- bond_enable_slave(iface, !iface->enabled);
- }
- }
- }
-}
-
-static void
-bond_wait(struct bridge *br)
-{
- size_t i, j;
-
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
- if (port->n_ifaces < 2) {
- continue;
- }
- for (j = 0; j < port->n_ifaces; j++) {
- struct iface *iface = port->ifaces[j];
- if (iface->delay_expires != LLONG_MAX) {
- poll_timer_wait(iface->delay_expires - time_msec());
- }
- }
- }
-}
-
-static bool
-set_dst(struct dst *p, const flow_t *flow,
- const struct port *in_port, const struct port *out_port,
- tag_type *tags)
-{
- /* STP handling.
- *
- * XXX This uses too many tags: any broadcast flow will get one tag per
- * destination port, and thus a broadcast on a switch of any size is likely
- * to have all tag bits set. We should figure out a way to be smarter.
- *
- * This is OK when STP is disabled, because stp_state_tag is 0 then. */
- *tags |= out_port->stp_state_tag;
- if (!(out_port->stp_state & (STP_DISABLED | STP_FORWARDING))) {
- return false;
- }
-
- p->vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE
- : in_port->vlan >= 0 ? in_port->vlan
- : ntohs(flow->dl_vlan));
- return choose_output_iface(out_port, flow->dl_src, &p->dp_ifidx, tags);
-}
-
-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 *dsts, size_t n_dsts, int vlan)
-{
- struct dst *first = dsts;
- struct dst *last = dsts + n_dsts;
-
- 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 bool
-dst_is_duplicate(const struct dst *dsts, size_t n_dsts,
- const struct dst *test)
-{
- size_t i;
- for (i = 0; i < n_dsts; i++) {
- if (dsts[i].vlan == test->vlan && 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 && 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 size_t
-compose_dsts(const struct bridge *br, const flow_t *flow, uint16_t vlan,
- const struct port *in_port, const struct port *out_port,
- struct dst dsts[], tag_type *tags)
-{
- mirror_mask_t mirrors = in_port->src_mirrors;
- struct dst *dst = dsts;
- size_t i;
-
- *tags |= in_port->stp_state_tag;
- if (out_port == FLOOD_PORT) {
- /* XXX use ODP_FLOOD if no vlans or bonding. */
- /* XXX even better, define each VLAN as a datapath port group */
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
- if (port != in_port && port_includes_vlan(port, vlan)
- && !port->is_mirror_output_port
- && set_dst(dst, flow, in_port, port, tags)) {
- mirrors |= port->dst_mirrors;
- dst++;
- }
- }
- } else if (out_port && set_dst(dst, flow, in_port, out_port, tags)) {
- mirrors |= out_port->dst_mirrors;
- dst++;
- }
-
- 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(dsts, dst - dsts, dst)) {
- dst++;
- }
- } else {
- for (i = 0; i < br->n_ports; i++) {
- struct port *port = br->ports[i];
- 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(dsts, dst - dsts, dst)) {
- continue;
- }
- if (dst->dp_ifidx == flow->in_port
- && dst->vlan == vlan) {
- /* Don't send out input port on same VLAN. */
- continue;
- }
- dst++;
- }
- }
- }
- }
- mirrors &= mirrors - 1;
- }
-
- partition_dsts(dsts, dst - dsts, ntohs(flow->dl_vlan));
- return dst - dsts;
-}
-
-static void UNUSED
-print_dsts(const struct dst *dsts, size_t n)
-{
- for (; n--; dsts++) {
- printf(">p%"PRIu16, dsts->dp_ifidx);
- if (dsts->vlan != OFP_VLAN_NONE) {
- printf("v%"PRIu16, dsts->vlan);
- }
- }
-}
-
-static void
-compose_actions(struct bridge *br, const flow_t *flow, uint16_t vlan,
- const struct port *in_port, const struct port *out_port,
- tag_type *tags, struct odp_actions *actions)
-{
- struct dst dsts[DP_MAX_PORTS * (MAX_MIRRORS + 1)];
- size_t n_dsts;
- const struct dst *p;
- uint16_t cur_vlan;
-
- n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts, tags);
-
- cur_vlan = ntohs(flow->dl_vlan);
- for (p = dsts; p < &dsts[n_dsts]; p++) {
- union odp_action *a;
- if (p->vlan != cur_vlan) {
- if (p->vlan == OFP_VLAN_NONE) {
- odp_actions_add(actions, ODPAT_STRIP_VLAN);
- } else {
- a = odp_actions_add(actions, ODPAT_SET_VLAN_VID);
- a->vlan_vid.vlan_vid = htons(p->vlan);
- }
- cur_vlan = p->vlan;
- }
- a = odp_actions_add(actions, ODPAT_OUTPUT);
- a->output.port = p->dp_ifidx;
- }
-}
-
-static bool
-is_bcast_arp_reply(const flow_t *flow, const struct ofpbuf *packet)
-{
- struct arp_eth_header *arp = (struct arp_eth_header *) packet->data;
- return (flow->dl_type == htons(ETH_TYPE_ARP)
- && eth_addr_is_broadcast(flow->dl_dst)
- && packet->size >= sizeof(struct arp_eth_header)
- && arp->ar_op == ARP_OP_REQUEST);
-}
-
-/* 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
-process_flow(struct bridge *br, const flow_t *flow,
- const struct ofpbuf *packet, struct odp_actions *actions,
- tag_type *tags)
-{
- struct iface *in_iface;
- struct port *in_port;
- struct port *out_port = NULL; /* By default, drop the packet/flow. */
- int vlan;
-
- /* Find the interface and port structure for the received packet. */
- in_iface = iface_from_dp_ifidx(br, flow->in_port);
- if (!in_iface) {
- /* No interface? Something fishy... */
- if (packet != NULL) {
- /* Odd. A few possible reasons here:
- *
- * - We deleted an interface but there are still a few packets
- * queued up from it.
- *
- * - Someone externally added an interface (e.g. with "ovs-dpctl
- * add-if") that we don't know about.
- *
- * - Packet arrived on the local port but the local port is not
- * one of our bridge ports.
- */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- VLOG_WARN_RL(&rl, "bridge %s: received packet on unknown "
- "interface %"PRIu16, br->name, flow->in_port);
- }
-
- /* Return without adding any actions, to drop packets on this flow. */
- return true;
- }
- in_port = in_iface->port;
-
- /* Figure out what VLAN this packet belongs to.
- *
- * Note that dl_vlan of 0 and of OFP_VLAN_NONE both mean that the packet
- * belongs to VLAN 0, so we should treat both cases identically. (In the
- * former case, the packet has an 802.1Q header that specifies VLAN 0,
- * presumably to allow a priority to be specified. In the latter case, the
- * packet does not have any 802.1Q header.) */
- vlan = ntohs(flow->dl_vlan);
- if (vlan == OFP_VLAN_NONE) {
- vlan = 0;
- }
- if (in_port->vlan >= 0) {
- if (vlan) {
- /* XXX support double tagging? */
- if (packet != NULL) {
- 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 with "
- "implicit VLAN %"PRIu16,
- br->name, ntohs(flow->dl_vlan),
- in_port->name, in_port->vlan);
- }
- goto done;
- }
- vlan = in_port->vlan;
- } else {
- if (!port_includes_vlan(in_port, vlan)) {
- 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);
- goto done;
- }
- }
-
- /* Drop frames for ports that STP wants entirely killed (both for
- * forwarding and for learning). Later, after we do learning, we'll drop
- * the frames that STP wants to do learning but not forwarding on. */
- if (in_port->stp_state & (STP_LISTENING | STP_BLOCKING)) {
- goto done;
- }
-
- /* Drop frames for reserved multicast addresses. */
- if (eth_addr_is_reserved(flow->dl_dst)) {
- goto done;
- }
-
- /* Drop frames on ports reserved for mirroring. */
- if (in_port->is_mirror_output_port) {
- 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",
- br->name, in_port->name);
- goto done;
- }
-
- /* Multicast (and broadcast) packets on bonds need special attention, to
- * avoid receiving duplicates. */
- if (in_port->n_ifaces > 1 && eth_addr_is_multicast(flow->dl_dst)) {
- *tags |= in_port->active_iface_tag;
- if (in_port->active_iface != in_iface->port_ifidx) {
- /* Drop all multicast packets on inactive slaves. */
- goto done;
- } else {
- /* Drop all multicast packets for which we have learned a different
- * input port, because we probably sent the packet on one slave
- * and got it back on the active slave. Broadcast ARP replies are
- * an exception to this rule: the host has moved to another
- * switch. */
- int src_idx = mac_learning_lookup(br->ml, flow->dl_src, vlan);
- if (src_idx != -1 && src_idx != in_port->port_idx) {
- if (packet) {
- if (!is_bcast_arp_reply(flow, packet)) {
- goto done;
- }
- } else {
- /* No way to know whether it's an ARP reply, because the
- * flow entry doesn't include enough information and we
- * don't have a packet. Punt. */
- return false;
- }
- }
- }
- }
-
- /* MAC learning. */
- out_port = FLOOD_PORT;
- if (br->ml) {
- int out_port_idx;
-
- /* Learn source MAC (but don't try to learn from revalidation). */
- if (packet) {
- tag_type rev_tag = mac_learning_learn(br->ml, flow->dl_src,
- vlan, in_port->port_idx);
- if (rev_tag) {
- /* 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",
- br->name, ETH_ADDR_ARGS(flow->dl_src),
- in_port->name, vlan);
- ofproto_revalidate(br->ofproto, rev_tag);
- }
- }
-
- /* Determine output port. */
- out_port_idx = mac_learning_lookup_tag(br->ml, flow->dl_dst, vlan,
- tags);
- if (out_port_idx >= 0 && out_port_idx < br->n_ports) {
- out_port = br->ports[out_port_idx];
- }
- }
-
- /* Don't send packets out their input ports. Don't forward frames that STP
- * wants us to discard. */
- if (in_port == out_port || in_port->stp_state == STP_LEARNING) {
- out_port = NULL;
- }
-
-done:
- compose_actions(br, flow, vlan, in_port, out_port, tags, actions);
-
- /*
- * We send out only a single packet, instead of setting up a flow, if the
- * packet is an ARP directed to broadcast that arrived on a bonded
- * interface. In such a situation ARP requests and replies must be handled
- * differently, but OpenFlow unfortunately can't distinguish them.
- */
- return (in_port->n_ifaces < 2
- || flow->dl_type != htons(ETH_TYPE_ARP)
- || !eth_addr_is_broadcast(flow->dl_dst));
-}
-
-/* Careful: 'opp' is in host byte order and opp->port_no is an OFP port
- * number. */
-static void
-bridge_port_changed_ofhook_cb(enum ofp_port_reason reason,
- const struct ofp_phy_port *opp,
- void *br_)
-{
- struct bridge *br = br_;
- struct iface *iface;
- struct port *port;
-
- iface = iface_from_dp_ifidx(br, ofp_port_to_odp_port(opp->port_no));
- if (!iface) {
- return;
- }
- port = iface->port;
-
- if (reason == OFPPR_DELETE) {
- VLOG_WARN("bridge %s: interface %s deleted unexpectedly",
- br->name, iface->name);
- iface_destroy(iface);
- if (!port->n_ifaces) {
- VLOG_WARN("bridge %s: port %s has no interfaces, dropping",
- br->name, port->name);
- port_destroy(port);
- }
-
- bridge_flush(br);
- } else {
- if (port->n_ifaces > 1) {
- bool up = !(opp->state & OFPPS_LINK_DOWN);
- bond_link_status_update(iface, up);
- port_update_bond_compat(port);
- }
- }
-}
-
-static bool
-bridge_normal_ofhook_cb(const flow_t *flow, const struct ofpbuf *packet,
- struct odp_actions *actions, tag_type *tags, void *br_)