X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Flearning-switch.c;h=4e7cedaf7e581466b418561ae94101b94a25094e;hb=53cf9963ccc60b443d738b31fbb446bc79170693;hp=077251c9df8a905c26fd4c92a30b5450761f947b;hpb=b05072ee5ed4a97d0feb03853b881b3e2e2a5536;p=sliver-openvswitch.git diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 077251c9d..4e7cedaf7 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ #include "hmap.h" #include "mac-learning.h" #include "ofpbuf.h" +#include "ofp-errors.h" #include "ofp-parse.h" #include "ofp-print.h" #include "ofp-util.h" @@ -76,8 +77,8 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300); static void queue_tx(struct lswitch *, struct rconn *, struct ofpbuf *); static void send_features_request(struct lswitch *, struct rconn *); -static void process_switch_features(struct lswitch *, - struct ofp_switch_features *); +static enum ofperr process_switch_features(struct lswitch *, + struct ofp_switch_features *); static void process_packet_in(struct lswitch *, struct rconn *, const struct ofp_packet_in *); static void process_echo_request(struct lswitch *, struct rconn *, @@ -96,7 +97,9 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) sw->max_idle = cfg->max_idle; sw->datapath_id = 0; sw->last_features_request = time_now() - 1; - sw->ml = cfg->mode == LSW_LEARN ? mac_learning_create() : NULL; + sw->ml = (cfg->mode == LSW_LEARN + ? mac_learning_create(MAC_ENTRY_DEFAULT_IDLE_TIME) + : NULL); sw->action_normal = cfg->mode == LSW_NORMAL; flow_wildcards_init_exact(&sw->wc); @@ -136,20 +139,48 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) send_features_request(sw, rconn); if (cfg->default_flows) { - const struct ofpbuf *b; - - LIST_FOR_EACH (b, list_node, cfg->default_flows) { - struct ofpbuf *copy = ofpbuf_clone(b); - int error = rconn_send(rconn, copy, NULL); - if (error) { - VLOG_INFO_RL(&rl, "%s: failed to queue default flows (%s)", - rconn_get_name(rconn), strerror(error)); - ofpbuf_delete(copy); - break; + enum ofputil_protocol usable_protocols; + enum ofputil_protocol protocol; + struct ofpbuf *msg = NULL; + int ofp_version; + int error = 0; + size_t i; + + /* Figure out the initial protocol on the connection. */ + ofp_version = rconn_get_version(rconn); + protocol = ofputil_protocol_from_ofp_version(ofp_version); + + /* If the initial protocol isn't good enough for default_flows, then + * pick one that will work and encode messages to set up that + * protocol. + * + * This could be improved by actually negotiating a mutually acceptable + * flow format with the switch, but that would require an asynchronous + * state machine. This version ought to work fine in practice. */ + usable_protocols = ofputil_flow_mod_usable_protocols( + cfg->default_flows, cfg->n_default_flows); + if (!(protocol & usable_protocols)) { + enum ofputil_protocol want = rightmost_1bit(usable_protocols); + while (!error) { + msg = ofputil_encode_set_protocol(protocol, want, &protocol); + if (!msg) { + break; + } + error = rconn_send(rconn, msg, NULL); } } + + for (i = 0; !error && i < cfg->n_default_flows; i++) { + msg = ofputil_encode_flow_mod(&cfg->default_flows[i], protocol); + error = rconn_send(rconn, msg, NULL); + } + + if (error) { + VLOG_INFO_RL(&rl, "%s: failed to queue default flows (%s)", + rconn_get_name(rconn), strerror(error)); + } } - + return sw; } @@ -225,7 +256,7 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, /* Nothing to do. */ break; - case OFPUTIL_INVALID: + case OFPUTIL_MSG_INVALID: case OFPUTIL_OFPT_HELLO: case OFPUTIL_OFPT_ERROR: case OFPUTIL_OFPT_ECHO_REPLY: @@ -247,18 +278,25 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, case OFPUTIL_OFPST_TABLE_REQUEST: case OFPUTIL_OFPST_PORT_REQUEST: case OFPUTIL_OFPST_QUEUE_REQUEST: + case OFPUTIL_OFPST_PORT_DESC_REQUEST: case OFPUTIL_OFPST_DESC_REPLY: case OFPUTIL_OFPST_FLOW_REPLY: case OFPUTIL_OFPST_QUEUE_REPLY: case OFPUTIL_OFPST_PORT_REPLY: case OFPUTIL_OFPST_TABLE_REPLY: case OFPUTIL_OFPST_AGGREGATE_REPLY: + case OFPUTIL_OFPST_PORT_DESC_REPLY: case OFPUTIL_NXT_ROLE_REQUEST: case OFPUTIL_NXT_ROLE_REPLY: case OFPUTIL_NXT_FLOW_MOD_TABLE_ID: case OFPUTIL_NXT_SET_FLOW_FORMAT: + case OFPUTIL_NXT_SET_PACKET_IN_FORMAT: + case OFPUTIL_NXT_PACKET_IN: case OFPUTIL_NXT_FLOW_MOD: case OFPUTIL_NXT_FLOW_REMOVED: + case OFPUTIL_NXT_FLOW_AGE: + case OFPUTIL_NXT_SET_ASYNC_CONFIG: + case OFPUTIL_NXT_SET_CONTROLLER_ID: case OFPUTIL_NXST_FLOW_REQUEST: case OFPUTIL_NXST_AGGREGATE_REQUEST: case OFPUTIL_NXST_FLOW_REPLY: @@ -310,27 +348,32 @@ queue_tx(struct lswitch *sw, struct rconn *rconn, struct ofpbuf *b) } } -static void +static enum ofperr process_switch_features(struct lswitch *sw, struct ofp_switch_features *osf) { - size_t n_ports; - size_t i; - - sw->datapath_id = ntohll(osf->datapath_id); + struct ofputil_switch_features features; + struct ofputil_phy_port port; + enum ofperr error; + struct ofpbuf b; + + error = ofputil_decode_switch_features(osf, &features, &b); + if (error) { + VLOG_ERR("received invalid switch feature reply (%s)", + ofperr_to_string(error)); + return error; + } - n_ports = (ntohs(osf->header.length) - sizeof *osf) / sizeof *osf->ports; - for (i = 0; i < n_ports; i++) { - struct ofp_phy_port *opp = &osf->ports[i]; - struct lswitch_port *lp; + sw->datapath_id = features.datapath_id; - opp->name[OFP_MAX_PORT_NAME_LEN - 1] = '\0'; - lp = shash_find_data(&sw->queue_names, opp->name); + while (!ofputil_pull_phy_port(osf->header.version, &b, &port)) { + struct lswitch_port *lp = shash_find_data(&sw->queue_names, port.name); if (lp && hmap_node_is_null(&lp->hmap_node)) { - lp->port_no = ntohs(opp->port_no); + lp->port_no = port.port_no; hmap_insert(&sw->queue_numbers, &lp->hmap_node, hash_int(lp->port_no, 0)); } } + return 0; } static uint16_t @@ -404,6 +447,8 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, struct ofp_action_header actions[2]; size_t actions_len; + struct ofputil_packet_out po; + size_t pkt_ofs, pkt_len; struct ofpbuf pkt; struct flow flow; @@ -419,7 +464,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, pkt_ofs = offsetof(struct ofp_packet_in, data); pkt_len = ntohs(opi->header.length) - pkt_ofs; ofpbuf_use_const(&pkt, opi->data, pkt_len); - flow_extract(&pkt, 0, in_port, &flow); + flow_extract(&pkt, 0, 0, in_port, &flow); /* Choose output port. */ out_port = lswitch_choose_destination(sw, &flow); @@ -432,7 +477,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, struct ofp_action_output oao; memset(&oao, 0, sizeof oao); - oao.type = htons(OFPAT_OUTPUT); + oao.type = htons(OFPAT10_OUTPUT); oao.len = htons(sizeof oao); oao.port = htons(out_port); @@ -442,7 +487,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, struct ofp_action_enqueue oae; memset(&oae, 0, sizeof oae); - oae.type = htons(OFPAT_ENQUEUE); + oae.type = htons(OFPAT10_ENQUEUE); oae.len = htons(sizeof oae); oae.port = htons(out_port); oae.queue_id = htonl(queue_id); @@ -452,6 +497,19 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, } assert(actions_len <= sizeof actions); + /* Prepare packet_out in case we need one. */ + po.buffer_id = ntohl(opi->buffer_id); + if (po.buffer_id == UINT32_MAX) { + po.packet = pkt.data; + po.packet_len = pkt.size; + } else { + po.packet = NULL; + po.packet_len = 0; + } + po.in_port = in_port; + po.actions = (union ofp_action *) actions; + po.n_actions = actions_len / sizeof *actions; + /* Send the packet, and possibly the whole flow, to the output port. */ if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) { struct ofpbuf *buffer; @@ -467,17 +525,13 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, /* If the switch didn't buffer the packet, we need to send a copy. */ if (ntohl(opi->buffer_id) == UINT32_MAX && actions_len > 0) { - queue_tx(sw, rconn, - make_packet_out(&pkt, UINT32_MAX, in_port, - actions, actions_len / sizeof *actions)); + queue_tx(sw, rconn, ofputil_encode_packet_out(&po)); } } else { /* We don't know that MAC, or we don't set up flows. Send along the * packet without setting up a flow. */ if (ntohl(opi->buffer_id) != UINT32_MAX || actions_len > 0) { - queue_tx(sw, rconn, - make_packet_out(&pkt, ntohl(opi->buffer_id), in_port, - actions, actions_len / sizeof *actions)); + queue_tx(sw, rconn, ofputil_encode_packet_out(&po)); } } }