X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ofproto%2Fofproto.c;h=6aceedb7e8e36046b65275f0f00ff295db815aec;hb=9cfcdadf934213982a0fb96a29d100e8ba0187f7;hp=1865277136e2294bed068a0ad031f9c1520549e4;hpb=5de1bb5cc9eb5b52c095a02d8afe68bc34ae0523;p=sliver-openvswitch.git diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 186527713..6aceedb7e 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -24,6 +24,7 @@ #include #include #include +#include "autopath.h" #include "byte-order.h" #include "cfm.h" #include "classifier.h" @@ -100,7 +101,7 @@ struct rule; struct ofport { struct hmap_node hmap_node; /* In struct ofproto's "ports" hmap. */ struct netdev *netdev; - struct ofp_phy_port opp; /* In host byte order. */ + struct ofp_phy_port opp; uint16_t odp_port; struct cfm *cfm; /* Connectivity Fault Management, if any. */ }; @@ -325,6 +326,7 @@ static const struct ofhooks default_ofhooks; static uint64_t pick_datapath_id(const struct ofproto *); static uint64_t pick_fallback_dpid(void); +static void ofproto_flush_flows__(struct ofproto *); static int ofproto_expire(struct ofproto *); static void flow_push_stats(struct ofproto *, const struct rule *, struct flow *, uint64_t packets, uint64_t bytes, @@ -655,12 +657,6 @@ ofproto_get_datapath_id(const struct ofproto *ofproto) return ofproto->datapath_id; } -bool -ofproto_has_primary_controller(const struct ofproto *ofproto) -{ - return connmgr_has_controllers(ofproto->connmgr); -} - enum ofproto_fail_mode ofproto_get_fail_mode(const struct ofproto *p) { @@ -690,7 +686,7 @@ ofproto_destroy(struct ofproto *p) shash_find_and_delete(&all_ofprotos, dpif_name(p->dpif)); - ofproto_flush_flows(p); + ofproto_flush_flows__(p); connmgr_destroy(p->connmgr); classifier_destroy(&p->cls); hmap_destroy(&p->facets); @@ -911,7 +907,7 @@ int ofproto_port_del(struct ofproto *ofproto, uint16_t odp_port) { struct ofport *ofport = get_port(ofproto, odp_port); - const char *name = ofport ? ofport->opp.name : ""; + const char *name = ofport ? netdev_get_name(ofport->netdev) : ""; int error; error = dpif_port_del(ofproto->dpif, odp_port); @@ -919,8 +915,8 @@ ofproto_port_del(struct ofproto *ofproto, uint16_t odp_port) VLOG_ERR("%s: failed to remove port %"PRIu16" (%s) interface (%s)", dpif_name(ofproto->dpif), odp_port, name, strerror(error)); } else if (ofport) { - /* 'name' is ofport->opp.name and update_port() is going to destroy - * 'ofport'. Just in case update_port() refers to 'name' after it + /* 'name' is the netdev's name and update_port() is going to close the + * netdev. Just in case update_port() refers to 'name' after it * destroys 'ofport', make a copy of it around the update_port() * call. */ char *devname = xstrdup(name); @@ -936,7 +932,7 @@ bool ofproto_port_is_floodable(struct ofproto *ofproto, uint16_t odp_port) { struct ofport *ofport = get_port(ofproto, odp_port); - return ofport && !(ofport->opp.config & OFPPC_NO_FLOOD); + return ofport && !(ofport->opp.config & htonl(OFPPC_NO_FLOOD)); } /* Sends 'packet' out of port 'port_no' within 'p'. If 'vlan_tci' is zero the @@ -944,7 +940,7 @@ ofproto_port_is_floodable(struct ofproto *ofproto, uint16_t odp_port) * will be sent with the VLAN TCI specified by 'vlan_tci & ~VLAN_CFI'. * * Returns 0 if successful, otherwise a positive errno value. */ -int +static int ofproto_send_packet(struct ofproto *ofproto, uint32_t port_no, uint16_t vlan_tci, const struct ofpbuf *packet) @@ -999,8 +995,8 @@ ofproto_delete_flow(struct ofproto *ofproto, const struct cls_rule *target) } } -void -ofproto_flush_flows(struct ofproto *ofproto) +static void +ofproto_flush_flows__(struct ofproto *ofproto) { struct facet *facet, *next_facet; struct rule *rule, *next_rule; @@ -1025,6 +1021,12 @@ ofproto_flush_flows(struct ofproto *ofproto) } dpif_flow_flush(ofproto->dpif); +} + +void +ofproto_flush_flows(struct ofproto *ofproto) +{ + ofproto_flush_flows__(ofproto); connmgr_flushed(ofproto->connmgr); } @@ -1041,7 +1043,7 @@ reinit_ports(struct ofproto *p) sset_init(&devnames); HMAP_FOR_EACH (ofport, hmap_node, &p->ports) { - sset_add(&devnames, ofport->opp.name); + sset_add(&devnames, netdev_get_name(ofport->netdev)); } DPIF_PORT_FOR_EACH (&dpif_port, &dump, p->dpif) { sset_add(&devnames, dpif_port.name); @@ -1053,12 +1055,14 @@ reinit_ports(struct ofproto *p) sset_destroy(&devnames); } -static struct ofport * -make_ofport(const struct dpif_port *dpif_port) +/* Opens and returns a netdev for 'dpif_port', or a null pointer if the netdev + * cannot be opened. On success, also fills in 'opp'. */ +static struct netdev * +ofport_open(const struct dpif_port *dpif_port, struct ofp_phy_port *opp) { + uint32_t curr, advertised, supported, peer; struct netdev_options netdev_options; enum netdev_flags flags; - struct ofport *ofport; struct netdev *netdev; int error; @@ -1076,22 +1080,20 @@ make_ofport(const struct dpif_port *dpif_port) return NULL; } - ofport = xzalloc(sizeof *ofport); - ofport->netdev = netdev; - ofport->odp_port = dpif_port->port_no; - ofport->opp.port_no = odp_port_to_ofp_port(dpif_port->port_no); - netdev_get_etheraddr(netdev, ofport->opp.hw_addr); - ovs_strlcpy(ofport->opp.name, dpif_port->name, sizeof ofport->opp.name); - netdev_get_flags(netdev, &flags); - ofport->opp.config = flags & NETDEV_UP ? 0 : OFPPC_PORT_DOWN; + netdev_get_features(netdev, &curr, &advertised, &supported, &peer); - ofport->opp.state = netdev_get_carrier(netdev) ? 0 : OFPPS_LINK_DOWN; + opp->port_no = htons(odp_port_to_ofp_port(dpif_port->port_no)); + netdev_get_etheraddr(netdev, opp->hw_addr); + ovs_strzcpy(opp->name, dpif_port->name, sizeof opp->name); + opp->config = flags & NETDEV_UP ? 0 : htonl(OFPPC_PORT_DOWN); + opp->state = netdev_get_carrier(netdev) ? 0 : htonl(OFPPS_LINK_DOWN); + opp->curr = htonl(curr); + opp->advertised = htonl(advertised); + opp->supported = htonl(supported); + opp->peer = htonl(peer); - netdev_get_features(netdev, - &ofport->opp.curr, &ofport->opp.advertised, - &ofport->opp.supported, &ofport->opp.peer); - return ofport; + return netdev; } static bool @@ -1110,29 +1112,42 @@ ofport_conflicts(const struct ofproto *p, const struct dpif_port *dpif_port) } } -static int -ofport_equal(const struct ofport *a_, const struct ofport *b_) +/* Returns true if most fields of 'a' and 'b' are equal. Differences in name, + * port number, and 'config' bits other than OFPPC_PORT_DOWN are + * disregarded. */ +static bool +ofport_equal(const struct ofp_phy_port *a, const struct ofp_phy_port *b) { - const struct ofp_phy_port *a = &a_->opp; - const struct ofp_phy_port *b = &b_->opp; - BUILD_ASSERT_DECL(sizeof *a == 48); /* Detect ofp_phy_port changes. */ - return (a->port_no == b->port_no - && !memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr) - && !strcmp(a->name, b->name) + return (!memcmp(a->hw_addr, b->hw_addr, sizeof a->hw_addr) && a->state == b->state - && a->config == b->config + && !((a->config ^ b->config) & htonl(OFPPC_PORT_DOWN)) && a->curr == b->curr && a->advertised == b->advertised && a->supported == b->supported && a->peer == b->peer); } +/* Adds an ofport to 'p' initialized based on the given 'netdev' and 'opp'. + * The caller must ensure that 'p' does not have a conflicting ofport (that is, + * one with the same name or port number). */ static void -ofport_install(struct ofproto *p, struct ofport *ofport) +ofport_install(struct ofproto *p, + struct netdev *netdev, const struct ofp_phy_port *opp) { - const char *netdev_name = ofport->opp.name; + const char *netdev_name = netdev_get_name(netdev); + struct ofport *ofport; + connmgr_send_port_status(p->connmgr, opp, OFPPR_ADD); + + /* Create ofport. */ + ofport = xmalloc(sizeof *ofport); + ofport->netdev = netdev; + ofport->opp = *opp; + ofport->odp_port = ofp_port_to_odp_port(ntohs(opp->port_no)); + ofport->cfm = NULL; + + /* Add port to 'p'. */ netdev_monitor_add(p->netdev_monitor, ofport->netdev); hmap_insert(&p->ports, &ofport->hmap_node, hash_int(ofport->odp_port, 0)); shash_add(&p->port_by_name, netdev_name, ofport); @@ -1141,16 +1156,59 @@ ofport_install(struct ofproto *p, struct ofport *ofport) } } +/* Removes 'ofport' from 'p' and destroys it. */ static void ofport_remove(struct ofproto *p, struct ofport *ofport) { + connmgr_send_port_status(p->connmgr, &ofport->opp, OFPPR_DELETE); + netdev_monitor_remove(p->netdev_monitor, ofport->netdev); hmap_remove(&p->ports, &ofport->hmap_node); shash_delete(&p->port_by_name, - shash_find(&p->port_by_name, ofport->opp.name)); + shash_find(&p->port_by_name, + netdev_get_name(ofport->netdev))); if (p->sflow) { ofproto_sflow_del_port(p->sflow, ofport->odp_port); } + + ofport_free(ofport); +} + +/* If 'ofproto' contains an ofport named 'name', removes it from 'ofproto' and + * destroys it. */ +static void +ofport_remove_with_name(struct ofproto *ofproto, const char *name) +{ + struct ofport *port = shash_find_data(&ofproto->port_by_name, name); + if (port) { + ofport_remove(ofproto, port); + } +} + +/* Updates 'port' within 'ofproto' with the new 'netdev' and 'opp'. + * + * Does not handle a name or port number change. The caller must implement + * such a change as a delete followed by an add. */ +static void +ofport_modified(struct ofproto *ofproto, struct ofport *port, + struct netdev *netdev, struct ofp_phy_port *opp) +{ + memcpy(port->opp.hw_addr, opp->hw_addr, ETH_ADDR_LEN); + port->opp.config = ((port->opp.config & ~htonl(OFPPC_PORT_DOWN)) + | (opp->config & htonl(OFPPC_PORT_DOWN))); + port->opp.state = opp->state; + port->opp.curr = opp->curr; + port->opp.advertised = opp->advertised; + port->opp.supported = opp->supported; + port->opp.peer = opp->peer; + + netdev_monitor_remove(ofproto->netdev_monitor, port->netdev); + netdev_monitor_add(ofproto->netdev_monitor, netdev); + + netdev_close(port->netdev); + port->netdev = netdev; + + connmgr_send_port_status(ofproto->connmgr, &port->opp, OFPPR_MODIFY); } static void @@ -1206,75 +1264,42 @@ get_port(const struct ofproto *ofproto, uint16_t odp_port) } static void -update_port(struct ofproto *p, const char *devname) +update_port(struct ofproto *ofproto, const char *name) { struct dpif_port dpif_port; - struct ofport *old_ofport; - struct ofport *new_ofport; - int error; + struct ofp_phy_port opp; + struct netdev *netdev; + struct ofport *port; COVERAGE_INC(ofproto_update_port); - /* Query the datapath for port information. */ - error = dpif_port_query_by_name(p->dpif, devname, &dpif_port); - - /* Find the old ofport. */ - old_ofport = shash_find_data(&p->port_by_name, devname); - if (!error) { - if (!old_ofport) { - /* There's no port named 'devname' but there might be a port with - * the same port number. This could happen if a port is deleted - * and then a new one added in its place very quickly, or if a port - * is renamed. In the former case we want to send an OFPPR_DELETE - * and an OFPPR_ADD, and in the latter case we want to send a - * single OFPPR_MODIFY. We can distinguish the cases by comparing - * the old port's ifindex against the new port, or perhaps less - * reliably but more portably by comparing the old port's MAC - * against the new port's MAC. However, this code isn't that smart - * and always sends an OFPPR_MODIFY (XXX). */ - old_ofport = get_port(p, dpif_port.port_no); - } - } else if (error != ENOENT && error != ENODEV) { - VLOG_WARN_RL(&rl, "dpif_port_query_by_name returned unexpected error " - "%s", strerror(error)); - goto exit; - } - - /* Create a new ofport. */ - new_ofport = !error ? make_ofport(&dpif_port) : NULL; - - /* Eliminate a few pathological cases. */ - if (!old_ofport && !new_ofport) { - goto exit; - } else if (old_ofport && new_ofport) { - /* Most of the 'config' bits are OpenFlow soft state, but - * OFPPC_PORT_DOWN is maintained by the kernel. So transfer the - * OpenFlow bits from old_ofport. (make_ofport() only sets - * OFPPC_PORT_DOWN and leaves the other bits 0.) */ - new_ofport->opp.config |= old_ofport->opp.config & ~OFPPC_PORT_DOWN; - - if (ofport_equal(old_ofport, new_ofport)) { - /* False alarm--no change. */ - ofport_free(new_ofport); - goto exit; + /* Fetch 'name''s location and properties from the datapath. */ + netdev = (!dpif_port_query_by_name(ofproto->dpif, name, &dpif_port) + ? ofport_open(&dpif_port, &opp) + : NULL); + if (netdev) { + port = get_port(ofproto, dpif_port.port_no); + if (port && !strcmp(netdev_get_name(port->netdev), name)) { + /* 'name' hasn't changed location. Any properties changed? */ + if (!ofport_equal(&port->opp, &opp)) { + ofport_modified(ofproto, port, netdev, &opp); + } else { + netdev_close(netdev); + } + } else { + /* If 'port' is nonnull then its name differs from 'name' and thus + * we should delete it. If we think there's a port named 'name' + * then its port number must be wrong now so delete it too. */ + if (port) { + ofport_remove(ofproto, port); + } + ofport_remove_with_name(ofproto, name); + ofport_install(ofproto, netdev, &opp); } + } else { + /* Any port named 'name' is gone now. */ + ofport_remove_with_name(ofproto, name); } - - /* Now deal with the normal cases. */ - if (old_ofport) { - ofport_remove(p, old_ofport); - } - if (new_ofport) { - ofport_install(p, new_ofport); - } - connmgr_send_port_status(p->connmgr, - new_ofport ? &new_ofport->opp : &old_ofport->opp, - (!old_ofport ? OFPPR_ADD - : !new_ofport ? OFPPR_DELETE - : OFPPR_MODIFY)); - ofport_free(old_ofport); - -exit: dpif_port_destroy(&dpif_port); } @@ -1286,9 +1311,12 @@ init_ports(struct ofproto *p) DPIF_PORT_FOR_EACH (&dpif_port, &dump, p->dpif) { if (!ofport_conflicts(p, &dpif_port)) { - struct ofport *ofport = make_ofport(&dpif_port); - if (ofport) { - ofport_install(p, ofport); + struct ofp_phy_port opp; + struct netdev *netdev; + + netdev = ofport_open(&dpif_port, &opp); + if (netdev) { + ofport_install(p, netdev, &opp); } } } @@ -1899,7 +1927,7 @@ handle_features_request(struct ofconn *ofconn, const struct ofp_header *oh) (1u << OFPAT_ENQUEUE)); HMAP_FOR_EACH (port, hmap_node, &ofproto->ports) { - hton_ofp_phy_port(ofpbuf_put(buf, &port->opp, sizeof port->opp)); + ofpbuf_put(buf, &port->opp, sizeof port->opp); } ofconn_send_reply(ofconn, buf); @@ -1964,7 +1992,7 @@ add_output_action(struct action_xlate_ctx *ctx, uint16_t port) const struct ofport *ofport = get_port(ctx->ofproto, port); if (ofport) { - if (ofport->opp.config & OFPPC_NO_FWD) { + if (ofport->opp.config & htonl(OFPPC_NO_FWD)) { /* Forwarding disabled on port. */ return; } @@ -2019,7 +2047,7 @@ xlate_table_action(struct action_xlate_ctx *ctx, uint16_t in_port) } static void -flood_packets(struct ofproto *ofproto, uint16_t odp_in_port, uint32_t mask, +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; @@ -2059,11 +2087,11 @@ xlate_output_action__(struct action_xlate_ctx *ctx, } break; case OFPP_FLOOD: - flood_packets(ctx->ofproto, ctx->flow.in_port, OFPPC_NO_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, 0, + flood_packets(ctx->ofproto, ctx->flow.in_port, htonl(0), &ctx->nf_output_iface, ctx->odp_actions); break; case OFPP_CONTROLLER: @@ -2222,8 +2250,11 @@ xlate_nicira_action(struct action_xlate_ctx *ctx, 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); + const struct ofhooks *ofhooks = ctx->ofproto->ofhooks; struct xlate_reg_state state; + uint16_t autopath_port; ovs_be64 tun_id; assert(nah->vendor == htonl(NX_VENDOR_ID)); @@ -2285,6 +2316,15 @@ xlate_nicira_action(struct action_xlate_ctx *ctx, multipath_execute(nam, &ctx->flow); break; + case NXAST_AUTOPATH: + naa = (const struct nx_action_autopath *) nah; + autopath_port = (ofhooks->autopath_cb + ? ofhooks->autopath_cb(&ctx->flow, ntohl(naa->id), + &ctx->tags, ctx->ofproto->aux) + : OFPP_NONE); + autopath_execute(naa, &ctx->flow, autopath_port); + 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. */ @@ -2304,9 +2344,10 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, const struct ofport *port; port = get_port(ctx->ofproto, ctx->flow.in_port); - if (port && port->opp.config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP) && + 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) - ? OFPPC_NO_RECV_STP : OFPPC_NO_RECV)) { + ? htonl(OFPPC_NO_RECV_STP) + : htonl(OFPPC_NO_RECV))) { /* Drop this flow. */ return; } @@ -2548,11 +2589,11 @@ exit: static void update_port_config(struct ofproto *p, struct ofport *port, - uint32_t config, uint32_t mask) + ovs_be32 config, ovs_be32 mask) { mask &= config ^ port->opp.config; - if (mask & OFPPC_PORT_DOWN) { - if (config & OFPPC_PORT_DOWN) { + 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); @@ -2560,14 +2601,14 @@ update_port_config(struct ofproto *p, struct ofport *port, } #define REVALIDATE_BITS (OFPPC_NO_RECV | OFPPC_NO_RECV_STP | \ OFPPC_NO_FWD | OFPPC_NO_FLOOD) - if (mask & REVALIDATE_BITS) { + if (mask & htonl(REVALIDATE_BITS)) { COVERAGE_INC(ofproto_costly_flags); - port->opp.config ^= mask & REVALIDATE_BITS; + port->opp.config ^= mask & htonl(REVALIDATE_BITS); p->need_revalidate = true; } #undef REVALIDATE_BITS - if (mask & OFPPC_NO_PACKET_IN) { - port->opp.config ^= OFPPC_NO_PACKET_IN; + if (mask & htonl(OFPPC_NO_PACKET_IN)) { + port->opp.config ^= htonl(OFPPC_NO_PACKET_IN); } } @@ -2590,7 +2631,7 @@ handle_port_mod(struct ofconn *ofconn, const struct ofp_header *oh) } 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, ntohl(opm->config), ntohl(opm->mask)); + update_port_config(p, port, opm->config, opm->mask); if (opm->advertise) { netdev_set_advertisements(port->netdev, ntohl(opm->advertise)); } @@ -2729,7 +2770,7 @@ append_port_stat(struct ofport *port, struct ofconn *ofconn, netdev_get_stats(port->netdev, &stats); ops = append_ofp_stats_reply(sizeof *ops, ofconn, msgp); - ops->port_no = htons(port->opp.port_no); + 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)); @@ -3085,7 +3126,7 @@ put_queue_stats(struct queue_stats_cbdata *cbdata, uint32_t queue_id, struct ofp_queue_stats *reply; reply = append_ofp_stats_reply(sizeof *reply, cbdata->ofconn, &cbdata->msg); - reply->port_no = htons(cbdata->ofport->opp.port_no); + reply->port_no = cbdata->ofport->opp.port_no; memset(reply->pad, 0, sizeof reply->pad); reply->queue_id = htonl(queue_id); put_32aligned_be64(&reply->tx_bytes, htonll(stats->tx_bytes)); @@ -3753,7 +3794,7 @@ handle_miss_upcall(struct ofproto *p, struct dpif_upcall *upcall) /* Don't send a packet-in if OFPPC_NO_PACKET_IN asserted. */ struct ofport *port = get_port(p, flow.in_port); if (port) { - if (port->opp.config & OFPPC_NO_PACKET_IN) { + if (port->opp.config & htonl(OFPPC_NO_PACKET_IN)) { COVERAGE_INC(ofproto_no_packet_in); /* XXX install 'drop' flow entry */ ofpbuf_delete(upcall->packet); @@ -4396,7 +4437,7 @@ default_normal_ofhook_cb(const struct flow *flow, const struct ofpbuf *packet, /* Determine output port. */ dst_mac = mac_learning_lookup(ofproto->ml, flow->dl_dst, 0, tags); if (!dst_mac) { - flood_packets(ofproto, flow->in_port, OFPPC_NO_FLOOD, + flood_packets(ofproto, flow->in_port, htonl(OFPPC_NO_FLOOD), nf_output_iface, odp_actions); } else { int out_port = dst_mac->port.i; @@ -4415,5 +4456,6 @@ static const struct ofhooks default_ofhooks = { default_normal_ofhook_cb, NULL, NULL, + NULL, NULL };