X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Flearning-switch.c;h=741e0d0e543dc9214b0be942a72e29ba72ef8667;hb=f8da634725a4a30243fd45c5579d300dfca1c853;hp=36594ac636687e36df41fa91a2e607862213f725;hpb=09913dfd259ae81dee6c944db1b92e9722f17667;p=sliver-openvswitch.git diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 36594ac63..741e0d0e5 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -24,6 +24,7 @@ #include #include "flow.h" +#include "hmap.h" #include "mac-learning.h" #include "ofpbuf.h" #include "ofp-parse.h" @@ -33,6 +34,7 @@ #include "poll-loop.h" #include "queue.h" #include "rconn.h" +#include "shash.h" #include "timeval.h" #include "vconn.h" #include "vlog.h" @@ -40,6 +42,12 @@ VLOG_DEFINE_THIS_MODULE(learning_switch) +struct lswitch_port { + struct hmap_node hmap_node; /* Hash node for port number. */ + uint16_t port_no; /* OpenFlow port number, in host byte order. */ + uint32_t queue_id; /* OpenFlow queue number. */ +}; + struct lswitch { /* If nonnegative, the switch sets up flows that expire after the given * number of seconds (or never expire, if the value is OFP_FLOW_PERMANENT). @@ -51,7 +59,11 @@ struct lswitch { struct mac_learning *ml; /* NULL to act as hub instead of switch. */ uint32_t wildcards; /* Wildcards to apply to flows. */ bool action_normal; /* Use OFPP_NORMAL? */ - uint32_t queue; /* OpenFlow queue to use, or UINT32_MAX. */ + + /* Queue distribution. */ + uint32_t default_queue; /* Default OpenFlow queue, or UINT32_MAX. */ + struct hmap queue_numbers; /* Map from port number to lswitch_port. */ + struct shash queue_names; /* Map from port name to lswitch_port. */ /* Number of outgoing queued packets on the rconn. */ struct rconn_packet_counter *queued; @@ -69,35 +81,23 @@ static packet_handler_func process_switch_features; static packet_handler_func process_packet_in; static packet_handler_func process_echo_request; -/* Creates and returns a new learning switch. - * - * If 'learn_macs' is true, the new switch will learn the ports on which MAC - * addresses appear. Otherwise, the new switch will flood all packets. - * - * If 'max_idle' is nonnegative, the new switch will set up flows that expire - * after the given number of seconds (or never expire, if 'max_idle' is - * OFP_FLOW_PERMANENT). Otherwise, the new switch will process every packet. - * - * The caller may provide an ofpbuf 'default_flows' that consists of a chain of - * one or more OpenFlow messages to send to the switch at time of connection. - * Presumably these will be OFPT_FLOW_MOD requests to set up the flow table. +/* Creates and returns a new learning switch whose configuration is given by + * 'cfg'. * * 'rconn' is used to send out an OpenFlow features request. */ struct lswitch * -lswitch_create(struct rconn *rconn, bool learn_macs, - bool exact_flows, int max_idle, bool action_normal, - const struct ofpbuf *default_flows) +lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) { const struct ofpbuf *b; struct lswitch *sw; sw = xzalloc(sizeof *sw); - sw->max_idle = max_idle; + sw->max_idle = cfg->max_idle; sw->datapath_id = 0; sw->last_features_request = time_now() - 1; - sw->ml = learn_macs ? mac_learning_create() : NULL; - sw->action_normal = action_normal; - if (exact_flows) { + sw->ml = cfg->mode == LSW_LEARN ? mac_learning_create() : NULL; + sw->action_normal = cfg->mode == LSW_NORMAL; + if (cfg->exact_flows) { /* Exact match. */ sw->wildcards = 0; } else { @@ -107,11 +107,25 @@ lswitch_create(struct rconn *rconn, bool learn_macs, sw->wildcards = (OFPFW_DL_TYPE | OFPFW_NW_SRC_MASK | OFPFW_NW_DST_MASK | OFPFW_NW_PROTO | OFPFW_TP_SRC | OFPFW_TP_DST); } - sw->queue = UINT32_MAX; + + sw->default_queue = cfg->default_queue; + hmap_init(&sw->queue_numbers); + shash_init(&sw->queue_names); + if (cfg->port_queues) { + struct shash_node *node; + + SHASH_FOR_EACH (node, cfg->port_queues) { + struct lswitch_port *port = xmalloc(sizeof *port); + hmap_node_nullify(&port->hmap_node); + port->queue_id = (uintptr_t) node->data; + shash_add(&sw->queue_names, node->name, port); + } + } + sw->queued = rconn_packet_counter_create(); send_features_request(sw, rconn); - for (b = default_flows; b; b = b->next) { + for (b = cfg->default_flows; b; b = b->next) { queue_tx(sw, rconn, ofpbuf_clone(b)); } @@ -123,21 +137,19 @@ void lswitch_destroy(struct lswitch *sw) { if (sw) { + struct lswitch_port *node, *next; + + HMAP_FOR_EACH_SAFE (node, next, hmap_node, &sw->queue_numbers) { + hmap_remove(&sw->queue_numbers, &node->hmap_node); + free(node); + } + shash_destroy(&sw->queue_names); mac_learning_destroy(sw->ml); rconn_packet_counter_destroy(sw->queued); free(sw); } } -/* Sets 'queue' as the OpenFlow queue used by packets and flows set up by 'sw'. - * Specify UINT32_MAX to avoid specifying a particular queue, which is also the - * default if this function is never called for 'sw'. */ -void -lswitch_set_queue(struct lswitch *sw, uint32_t queue) -{ - sw->queue = queue; -} - /* Takes care of necessary 'sw' activity, except for receiving packets (which * the caller must do). */ void @@ -268,12 +280,32 @@ process_switch_features(struct lswitch *sw, struct rconn *rconn OVS_UNUSED, void *osf_) { struct ofp_switch_features *osf = osf_; + size_t n_ports; + size_t i; + + if (check_ofp_message_array(&osf->header, OFPT_FEATURES_REPLY, + sizeof *osf, sizeof *osf->ports, &n_ports)) { + return; + } sw->datapath_id = ntohll(osf->datapath_id); + + for (i = 0; i < n_ports; i++) { + struct ofp_phy_port *opp = &osf->ports[i]; + struct lswitch_port *lp; + + opp->name[OFP_MAX_PORT_NAME_LEN - 1] = '\0'; + lp = shash_find_data(&sw->queue_names, (char *) opp->name); + if (lp && hmap_node_is_null(&lp->hmap_node)) { + lp->port_no = ntohs(opp->port_no); + hmap_insert(&sw->queue_numbers, &lp->hmap_node, + hash_int(lp->port_no, 0)); + } + } } static uint16_t -lswitch_choose_destination(struct lswitch *sw, const flow_t *flow) +lswitch_choose_destination(struct lswitch *sw, const struct flow *flow) { uint16_t out_port; @@ -312,11 +344,27 @@ lswitch_choose_destination(struct lswitch *sw, const flow_t *flow) return out_port; } +static uint32_t +get_queue_id(const struct lswitch *sw, uint16_t in_port) +{ + const struct lswitch_port *port; + + HMAP_FOR_EACH_WITH_HASH (port, hmap_node, hash_int(in_port, 0), + &sw->queue_numbers) { + if (port->port_no == in_port) { + return port->queue_id; + } + } + + return sw->default_queue; +} + static void process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) { struct ofp_packet_in *opi = opi_; uint16_t in_port = ntohs(opi->in_port); + uint32_t queue_id; uint16_t out_port; struct ofp_action_header actions[2]; @@ -324,7 +372,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) size_t pkt_ofs, pkt_len; struct ofpbuf pkt; - flow_t flow; + struct flow flow; /* Ignore packets sent via output to OFPP_CONTROLLER. This library never * uses such an action. You never know what experiments might be going on, @@ -344,9 +392,10 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) out_port = lswitch_choose_destination(sw, &flow); /* Make actions. */ + queue_id = get_queue_id(sw, in_port); if (out_port == OFPP_NONE) { actions_len = 0; - } else if (sw->queue == UINT32_MAX || out_port >= OFPP_MAX) { + } else if (queue_id == UINT32_MAX || out_port >= OFPP_MAX) { struct ofp_action_output oao; memset(&oao, 0, sizeof oao); @@ -363,7 +412,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) oae.type = htons(OFPAT_ENQUEUE); oae.len = htons(sizeof oae); oae.port = htons(out_port); - oae.queue_id = htonl(sw->queue); + oae.queue_id = htonl(queue_id); memcpy(actions, &oae, sizeof oae); actions_len = sizeof oae;