X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Flearning-switch.c;h=7a60f3c32adb92c5fe9fa0f044f66cc6629d8545;hb=66e7d7a2cd928a5f1c64c7289b042d2fe2ebc21e;hp=d53f147a3656952caa6568e2ed7314021f344a1e;hpb=002c3f1734454492d921de29f2d5be6428ed1a18;p=openvswitch diff --git a/lib/learning-switch.c b/lib/learning-switch.c index d53f147a..7a60f3c3 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -52,8 +52,15 @@ struct lswitch_port { uint32_t queue_id; /* OpenFlow queue number. */ }; +enum lswitch_state { + S_CONNECTING, /* Waiting for connection to complete. */ + S_FEATURES_REPLY, /* Waiting for features reply. */ + S_SWITCHING, /* Switching flows. */ +}; + struct lswitch { struct rconn *rconn; + enum lswitch_state state; /* 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). @@ -62,7 +69,6 @@ struct lswitch { enum ofputil_protocol protocol; unsigned long long int datapath_id; - time_t last_features_request; struct mac_learning *ml; /* NULL to act as hub instead of switch. */ struct flow_wildcards wc; /* Wildcards to apply to flows. */ bool action_normal; /* Use OFPP_NORMAL? */ @@ -78,6 +84,11 @@ struct lswitch { /* If true, do not reply to any messages from the switch (for debugging * fail-open mode). */ bool mute; + + /* Optional "flow mod" requests to send to the switch at connection time, + * to set up the flow table. */ + const struct ofputil_flow_mod *default_flows; + size_t n_default_flows; }; /* The log messages here could actually be useful in debugging, so keep the @@ -100,37 +111,39 @@ static void process_echo_request(struct lswitch *, const struct ofp_header *); struct lswitch * lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) { - enum ofputil_protocol protocol; struct lswitch *sw; + uint32_t ofpfw; sw = xzalloc(sizeof *sw); sw->rconn = rconn; + sw->state = S_CONNECTING; 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(MAC_ENTRY_DEFAULT_IDLE_TIME) : NULL); sw->action_normal = cfg->mode == LSW_NORMAL; - flow_wildcards_init_exact(&sw->wc); - if (cfg->wildcards) { - uint32_t ofpfw; - - if (cfg->wildcards == UINT32_MAX) { - /* Try to wildcard as many fields as possible, but we cannot - * wildcard all fields. We need in_port to detect moves. We need - * Ethernet source and dest and VLAN VID to do L2 learning. */ - ofpfw = (OFPFW10_DL_TYPE | OFPFW10_DL_VLAN_PCP - | OFPFW10_NW_SRC_ALL | OFPFW10_NW_DST_ALL - | OFPFW10_NW_TOS | OFPFW10_NW_PROTO - | OFPFW10_TP_SRC | OFPFW10_TP_DST); - } else { - ofpfw = cfg->wildcards; - } + switch (cfg->wildcards) { + case 0: + ofpfw = 0; + break; + + case UINT32_MAX: + /* Try to wildcard as many fields as possible, but we cannot + * wildcard all fields. We need in_port to detect moves. We need + * Ethernet source and dest and VLAN VID to do L2 learning. */ + ofpfw = (OFPFW10_DL_TYPE | OFPFW10_DL_VLAN_PCP + | OFPFW10_NW_SRC_ALL | OFPFW10_NW_DST_ALL + | OFPFW10_NW_TOS | OFPFW10_NW_PROTO + | OFPFW10_TP_SRC | OFPFW10_TP_DST); + break; - ofputil_wildcard_from_ofpfw10(ofpfw, &sw->wc); + default: + ofpfw = cfg->wildcards; + break; } + ofputil_wildcard_from_ofpfw10(ofpfw, &sw->wc); sw->default_queue = cfg->default_queue; hmap_init(&sw->queue_numbers); @@ -146,11 +159,23 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) } } + sw->default_flows = cfg->default_flows; + sw->n_default_flows = cfg->n_default_flows; + sw->queued = rconn_packet_counter_create(); + + return sw; +} + +static void +lswitch_handshake(struct lswitch *sw) +{ + enum ofputil_protocol protocol; + send_features_request(sw); - protocol = ofputil_protocol_from_ofp_version(rconn_get_version(rconn)); - if (cfg->default_flows) { + protocol = ofputil_protocol_from_ofp_version(rconn_get_version(sw->rconn)); + if (sw->default_flows) { enum ofputil_protocol usable_protocols; struct ofpbuf *msg = NULL; int error = 0; @@ -164,7 +189,7 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) * 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); + sw->default_flows, sw->n_default_flows); if (!(protocol & usable_protocols)) { enum ofputil_protocol want = rightmost_1bit(usable_protocols); while (!error) { @@ -172,23 +197,21 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) if (!msg) { break; } - error = rconn_send(rconn, msg, NULL); + error = rconn_send(sw->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); + for (i = 0; !error && i < sw->n_default_flows; i++) { + msg = ofputil_encode_flow_mod(&sw->default_flows[i], protocol); + error = rconn_send(sw->rconn, msg, NULL); } if (error) { VLOG_INFO_RL(&rl, "%s: failed to queue default flows (%s)", - rconn_get_name(rconn), strerror(error)); + rconn_get_name(sw->rconn), strerror(error)); } } sw->protocol = protocol; - - return sw; } bool @@ -229,6 +252,14 @@ lswitch_run(struct lswitch *sw) rconn_run(sw->rconn); + if (sw->state == S_CONNECTING) { + if (rconn_get_version(sw->rconn) != -1) { + lswitch_handshake(sw); + sw->state = S_FEATURES_REPLY; + } + return; + } + for (i = 0; i < 50; i++) { struct ofpbuf *msg; @@ -250,7 +281,7 @@ lswitch_wait(struct lswitch *sw) if (sw->ml) { mac_learning_wait(sw->ml); } - rconn_run(sw->rconn); + rconn_run_wait(sw->rconn); rconn_recv_wait(sw->rconn); } @@ -269,10 +300,9 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg) return; } - if (sw->datapath_id == 0 + if (sw->state == S_FEATURES_REPLY && type != OFPTYPE_ECHO_REQUEST && type != OFPTYPE_FEATURES_REPLY) { - send_features_request(sw); return; } @@ -282,7 +312,13 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg) break; case OFPTYPE_FEATURES_REPLY: - process_switch_features(sw, msg->data); + if (sw->state == S_FEATURES_REPLY) { + if (!process_switch_features(sw, msg->data)) { + sw->state = S_SWITCHING; + } else { + rconn_disconnect(sw->rconn); + } + } break; case OFPTYPE_PACKET_IN: @@ -346,26 +382,21 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg) static void send_features_request(struct lswitch *sw) { - time_t now = time_now(); - if (now >= sw->last_features_request + 1) { - struct ofpbuf *b; - struct ofp_switch_config *osc; - int ofp_version = rconn_get_version(sw->rconn); - - assert(ofp_version > 0 && ofp_version < 0xff); + struct ofpbuf *b; + struct ofp_switch_config *osc; + int ofp_version = rconn_get_version(sw->rconn); - /* Send OFPT_FEATURES_REQUEST. */ - b = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, ofp_version, 0); - queue_tx(sw, b); + assert(ofp_version > 0 && ofp_version < 0xff); - /* Send OFPT_SET_CONFIG. */ - b = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, ofp_version, sizeof *osc); - osc = ofpbuf_put_zeros(b, sizeof *osc); - osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN); - queue_tx(sw, b); + /* Send OFPT_FEATURES_REQUEST. */ + b = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, ofp_version, 0); + queue_tx(sw, b); - sw->last_features_request = now; - } + /* Send OFPT_SET_CONFIG. */ + b = ofpraw_alloc(OFPRAW_OFPT_SET_CONFIG, ofp_version, sizeof *osc); + osc = ofpbuf_put_zeros(b, sizeof *osc); + osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN); + queue_tx(sw, b); } static void @@ -504,7 +535,8 @@ process_packet_in(struct lswitch *sw, const struct ofp_header *oh) /* Extract flow data from 'opi' into 'flow'. */ ofpbuf_use_const(&pkt, pi.packet, pi.packet_len); - flow_extract(&pkt, 0, pi.fmd.tun_id, pi.fmd.in_port, &flow); + flow_extract(&pkt, 0, NULL, pi.fmd.in_port, &flow); + flow.tunnel.tun_id = pi.fmd.tun_id; /* Choose output port. */ out_port = lswitch_choose_destination(sw, &flow); @@ -544,7 +576,9 @@ process_packet_in(struct lswitch *sw, const struct ofp_header *oh) /* The output port is known, or we always flood everything, so add a * new flow. */ memset(&fm, 0, sizeof fm); - cls_rule_init(&flow, &sw->wc, 0, &fm.cr); + match_init(&fm.match, &flow, &sw->wc); + ofputil_normalize_match_quiet(&fm.match); + fm.priority = 0; fm.table_id = 0xff; fm.command = OFPFC_ADD; fm.idle_timeout = sw->max_idle; @@ -558,13 +592,13 @@ process_packet_in(struct lswitch *sw, const struct ofp_header *oh) /* If the switch didn't buffer the packet, we need to send a copy. */ if (pi.buffer_id == UINT32_MAX && out_port != OFPP_NONE) { - queue_tx(sw, ofputil_encode_packet_out(&po)); + queue_tx(sw, ofputil_encode_packet_out(&po, sw->protocol)); } } else { /* We don't know that MAC, or we don't set up flows. Send along the * packet without setting up a flow. */ if (pi.buffer_id != UINT32_MAX || out_port != OFPP_NONE) { - queue_tx(sw, ofputil_encode_packet_out(&po)); + queue_tx(sw, ofputil_encode_packet_out(&po, sw->protocol)); } } }