X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Flearning-switch.c;h=4a5d88918f1e5c075a232e2e8b8265be98abca0a;hb=733a287e17bf3764bf31c9528c6b6d30aafdec5f;hp=511096ab8e798639eecd996fe4ca305e3bb44e15;hpb=0b61210ecb328274be67c23127e15af0809b00da;p=sliver-openvswitch.git diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 511096ab8..4a5d88918 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010 Nicira Networks. + * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,6 @@ #include "ofp-util.h" #include "openflow/openflow.h" #include "poll-loop.h" -#include "queue.h" #include "rconn.h" #include "shash.h" #include "timeval.h" @@ -77,10 +76,12 @@ 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 *); -typedef void packet_handler_func(struct lswitch *, struct rconn *, void *); -static packet_handler_func process_switch_features; -static packet_handler_func process_packet_in; -static packet_handler_func process_echo_request; +static void 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 *, + const struct ofp_header *); /* Creates and returns a new learning switch whose configuration is given by * 'cfg'. @@ -89,7 +90,6 @@ static packet_handler_func process_echo_request; struct lswitch * lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) { - const struct ofpbuf *b; struct lswitch *sw; sw = xzalloc(sizeof *sw); @@ -100,14 +100,22 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) sw->action_normal = cfg->mode == LSW_NORMAL; flow_wildcards_init_exact(&sw->wc); - if (!cfg->exact_flows) { - /* We cannot wildcard all fields. - * We need in_port to detect moves. - * We need both SA and DA to do learning. */ - sw->wc.wildcards = (FWW_DL_TYPE | FWW_NW_PROTO - | FWW_TP_SRC | FWW_TP_DST); - sw->wc.nw_src_mask = htonl(0); - sw->wc.nw_dst_mask = htonl(0); + 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 = (OFPFW_DL_TYPE | OFPFW_DL_VLAN_PCP + | OFPFW_NW_SRC_ALL | OFPFW_NW_DST_ALL + | OFPFW_NW_TOS | OFPFW_NW_PROTO + | OFPFW_TP_SRC | OFPFW_TP_DST); + } else { + ofpfw = cfg->wildcards; + } + + ofputil_wildcard_from_openflow(ofpfw, &sw->wc); } sw->default_queue = cfg->default_queue; @@ -127,10 +135,21 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) sw->queued = rconn_packet_counter_create(); send_features_request(sw, rconn); - for (b = cfg->default_flows; b; b = b->next) { - queue_tx(sw, rconn, ofpbuf_clone(b)); + 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; + } + } } - + return sw; } @@ -178,38 +197,9 @@ void lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, const struct ofpbuf *msg) { - struct processor { - uint8_t type; - size_t min_size; - packet_handler_func *handler; - }; - static const struct processor processors[] = { - { - OFPT_ECHO_REQUEST, - sizeof(struct ofp_header), - process_echo_request - }, - { - OFPT_FEATURES_REPLY, - sizeof(struct ofp_switch_features), - process_switch_features - }, - { - OFPT_PACKET_IN, - offsetof(struct ofp_packet_in, data), - process_packet_in - }, - { - OFPT_FLOW_REMOVED, - sizeof(struct ofp_flow_removed), - NULL - }, - }; - const size_t n_processors = ARRAY_SIZE(processors); - const struct processor *p; - struct ofp_header *oh; - - oh = msg->data; + const struct ofp_header *oh = msg->data; + const struct ofputil_msg_type *type; + if (sw->datapath_id == 0 && oh->type != OFPT_ECHO_REQUEST && oh->type != OFPT_FEATURES_REPLY) { @@ -217,27 +207,70 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, return; } - for (p = processors; p < &processors[n_processors]; p++) { - if (oh->type == p->type) { - if (msg->size < p->min_size) { - VLOG_WARN_RL(&rl, "%016llx: %s: too short (%zu bytes) for " - "type %"PRIu8" (min %zu)", sw->datapath_id, - rconn_get_name(rconn), msg->size, oh->type, - p->min_size); - return; - } - if (p->handler) { - (p->handler)(sw, rconn, msg->data); - } - return; + ofputil_decode_msg_type(oh, &type); + switch (ofputil_msg_type_code(type)) { + case OFPUTIL_OFPT_ECHO_REQUEST: + process_echo_request(sw, rconn, msg->data); + break; + + case OFPUTIL_OFPT_FEATURES_REPLY: + process_switch_features(sw, msg->data); + break; + + case OFPUTIL_OFPT_PACKET_IN: + process_packet_in(sw, rconn, msg->data); + break; + + case OFPUTIL_OFPT_FLOW_REMOVED: + /* Nothing to do. */ + break; + + case OFPUTIL_MSG_INVALID: + case OFPUTIL_OFPT_HELLO: + case OFPUTIL_OFPT_ERROR: + case OFPUTIL_OFPT_ECHO_REPLY: + case OFPUTIL_OFPT_FEATURES_REQUEST: + case OFPUTIL_OFPT_GET_CONFIG_REQUEST: + case OFPUTIL_OFPT_GET_CONFIG_REPLY: + case OFPUTIL_OFPT_SET_CONFIG: + case OFPUTIL_OFPT_PORT_STATUS: + case OFPUTIL_OFPT_PACKET_OUT: + case OFPUTIL_OFPT_FLOW_MOD: + case OFPUTIL_OFPT_PORT_MOD: + case OFPUTIL_OFPT_BARRIER_REQUEST: + case OFPUTIL_OFPT_BARRIER_REPLY: + case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST: + case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY: + case OFPUTIL_OFPST_DESC_REQUEST: + case OFPUTIL_OFPST_FLOW_REQUEST: + case OFPUTIL_OFPST_AGGREGATE_REQUEST: + case OFPUTIL_OFPST_TABLE_REQUEST: + case OFPUTIL_OFPST_PORT_REQUEST: + case OFPUTIL_OFPST_QUEUE_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_NXT_ROLE_REQUEST: + case OFPUTIL_NXT_ROLE_REPLY: + case OFPUTIL_NXT_FLOW_MOD_TABLE_ID: + case OFPUTIL_NXT_SET_FLOW_FORMAT: + case OFPUTIL_NXT_FLOW_MOD: + case OFPUTIL_NXT_FLOW_REMOVED: + case OFPUTIL_NXST_FLOW_REQUEST: + case OFPUTIL_NXST_AGGREGATE_REQUEST: + case OFPUTIL_NXST_FLOW_REPLY: + case OFPUTIL_NXST_AGGREGATE_REPLY: + default: + if (VLOG_IS_DBG_ENABLED()) { + char *s = ofp_to_string(msg->data, msg->size, 2); + VLOG_DBG_RL(&rl, "%016llx: OpenFlow packet ignored: %s", + sw->datapath_id, s); + free(s); } } - if (VLOG_IS_DBG_ENABLED()) { - char *s = ofp_to_string(msg->data, msg->size, 2); - VLOG_DBG_RL(&rl, "%016llx: OpenFlow packet ignored: %s", - sw->datapath_id, s); - free(s); - } } static void @@ -278,20 +311,14 @@ queue_tx(struct lswitch *sw, struct rconn *rconn, struct ofpbuf *b) } static void -process_switch_features(struct lswitch *sw, struct rconn *rconn OVS_UNUSED, - void *osf_) +process_switch_features(struct lswitch *sw, struct ofp_switch_features *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); + 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; @@ -312,12 +339,15 @@ lswitch_choose_destination(struct lswitch *sw, const struct flow *flow) uint16_t out_port; /* Learn the source MAC. */ - if (sw->ml) { - if (mac_learning_learn(sw->ml, flow->dl_src, 0, flow->in_port, - GRAT_ARP_LOCK_NONE)) { + if (mac_learning_may_learn(sw->ml, flow->dl_src, 0)) { + struct mac_entry *mac = mac_learning_insert(sw->ml, flow->dl_src, 0); + if (mac_entry_is_new(mac) || mac->port.i != flow->in_port) { VLOG_DBG_RL(&rl, "%016llx: learned that "ETH_ADDR_FMT" is on " "port %"PRIu16, sw->datapath_id, ETH_ADDR_ARGS(flow->dl_src), flow->in_port); + + mac->port.i = flow->in_port; + mac_learning_changed(sw->ml, mac); } } @@ -328,9 +358,11 @@ lswitch_choose_destination(struct lswitch *sw, const struct flow *flow) out_port = OFPP_FLOOD; if (sw->ml) { - int learned_port = mac_learning_lookup(sw->ml, flow->dl_dst, 0, NULL); - if (learned_port >= 0) { - out_port = learned_port; + struct mac_entry *mac; + + mac = mac_learning_lookup(sw->ml, flow->dl_dst, 0, NULL); + if (mac) { + out_port = mac->port.i; if (out_port == flow->in_port) { /* Don't send a packet back out its input port. */ return OFPP_NONE; @@ -362,9 +394,9 @@ get_queue_id(const struct lswitch *sw, uint16_t in_port) } static void -process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) +process_packet_in(struct lswitch *sw, struct rconn *rconn, + const struct ofp_packet_in *opi) { - struct ofp_packet_in *opi = opi_; uint16_t in_port = ntohs(opi->in_port); uint32_t queue_id; uint16_t out_port; @@ -386,8 +418,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) /* Extract flow data from 'opi' into 'flow'. */ pkt_ofs = offsetof(struct ofp_packet_in, data); pkt_len = ntohs(opi->header.length) - pkt_ofs; - pkt.data = opi->data; - pkt.size = pkt_len; + ofpbuf_use_const(&pkt, opi->data, pkt_len); flow_extract(&pkt, 0, in_port, &flow); /* Choose output port. */ @@ -424,7 +455,6 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) /* 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; - struct ofp_flow_mod *ofm; struct cls_rule rule; /* The output port is known, or we always flood everything, so add a @@ -433,7 +463,6 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) buffer = make_add_flow(&rule, ntohl(opi->buffer_id), sw->max_idle, actions_len); ofpbuf_put(buffer, actions, actions_len); - ofm = buffer->data; queue_tx(sw, rconn, buffer); /* If the switch didn't buffer the packet, we need to send a copy. */ @@ -454,8 +483,8 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) } static void -process_echo_request(struct lswitch *sw, struct rconn *rconn, void *rq_) +process_echo_request(struct lswitch *sw, struct rconn *rconn, + const struct ofp_header *rq) { - struct ofp_header *rq = rq_; queue_tx(sw, rconn, make_echo_reply(rq)); }