X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Flearning-switch.c;h=4a46a0f698ffa3cbf2cf3bc2cc38b5b2e06bd172;hb=5549180a542fa7aa17a196d55b2f5eb4cbb3159a;hp=5768fd4d6f61a15c38048a26c605d0017c343029;hpb=7b351ea0762fc73e1e92059e5f16b8269db44bd2;p=sliver-openvswitch.git diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 5768fd4d6..4a46a0f69 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -31,6 +31,7 @@ * derivatives without specific, written prior permission. */ +#include #include "learning-switch.h" #include @@ -39,13 +40,14 @@ #include #include -#include "buffer.h" #include "flow.h" #include "mac-learning.h" +#include "ofpbuf.h" #include "ofp-print.h" #include "openflow.h" #include "queue.h" #include "rconn.h" +#include "timeval.h" #include "vconn.h" #include "xtoxll.h" @@ -53,34 +55,47 @@ #include "vlog.h" struct lswitch { - bool setup_flows; /* Set up flows? (or controller processes all packets) */ + /* 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). + * Otherwise, the switch processes every packet. */ + int max_idle; + uint64_t datapath_id; time_t last_features_request; struct mac_learning *ml; /* NULL to act as hub instead of switch. */ + + /* Number of outgoing queued packets on the rconn. */ + int n_queued; }; -static void queue_tx(struct lswitch *, struct rconn *, struct buffer *); +/* The log messages here could actually be useful in debugging, so keep the + * rate limit relatively high. */ +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_packet_in(struct lswitch *, struct rconn *, struct ofp_packet_in *); +static void process_echo_request(struct lswitch *, struct rconn *, + struct ofp_header *); /* 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 'setup_flows' is true, the new switch will set up flows. Otherwise, the - * new switch will process every packet. + * 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. * * 'rconn' is used to send out an OpenFlow features request. */ struct lswitch * -lswitch_create(struct rconn *rconn, bool learn_macs, bool setup_flows) +lswitch_create(struct rconn *rconn, bool learn_macs, int max_idle) { - struct lswitch *sw = xmalloc(sizeof *sw); - memset(sw, 0, sizeof *sw); - sw->setup_flows = setup_flows; + struct lswitch *sw = xcalloc(1, sizeof *sw); + sw->max_idle = max_idle; sw->datapath_id = 0; - sw->last_features_request = 0; + sw->last_features_request = time_now() - 1; sw->ml = learn_macs ? mac_learning_create() : NULL; send_features_request(sw, rconn); return sw; @@ -102,7 +117,7 @@ lswitch_destroy(struct lswitch *sw) * 'rconn'. */ void lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, - const struct buffer *msg) + const struct ofpbuf *msg) { static const size_t min_size[UINT8_MAX + 1] = { [0 ... UINT8_MAX] = sizeof (struct ofp_header), @@ -113,13 +128,16 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, oh = msg->data; if (msg->size < min_size[oh->type]) { - VLOG_WARN("%s: too short (%zu bytes) for type %"PRIu8" (min %zu)", - rconn_get_name(rconn), - msg->size, oh->type, min_size[oh->type]); + VLOG_WARN_RL(&rl, + "%s: too short (%zu bytes) for type %"PRIu8" (min %zu)", + rconn_get_name(rconn), + msg->size, oh->type, min_size[oh->type]); return; } - if (oh->type == OFPT_FEATURES_REPLY) { + if (oh->type == OFPT_ECHO_REQUEST) { + process_echo_request(sw, rconn, msg->data); + } else if (oh->type == OFPT_FEATURES_REPLY) { struct ofp_switch_features *osf = msg->data; sw->datapath_id = osf->datapath_id; } else if (sw->datapath_id == 0) { @@ -129,7 +147,7 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, } else { if (VLOG_IS_DBG_ENABLED()) { char *p = ofp_to_string(msg->data, msg->size, 2); - VLOG_DBG("OpenFlow packet ignored: %s", p); + VLOG_DBG_RL(&rl, "OpenFlow packet ignored: %s", p); free(p); } } @@ -138,15 +156,15 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, static void send_features_request(struct lswitch *sw, struct rconn *rconn) { - time_t now = time(0); + time_t now = time_now(); if (now >= sw->last_features_request + 1) { - struct buffer *b; + struct ofpbuf *b; struct ofp_header *ofr; struct ofp_switch_config *osc; /* Send OFPT_FEATURES_REQUEST. */ - b = buffer_new(0); - ofr = buffer_put_uninit(b, sizeof *ofr); + b = ofpbuf_new(0); + ofr = ofpbuf_put_uninit(b, sizeof *ofr); memset(ofr, 0, sizeof *ofr); ofr->type = OFPT_FEATURES_REQUEST; ofr->version = OFP_VERSION; @@ -154,8 +172,8 @@ send_features_request(struct lswitch *sw, struct rconn *rconn) queue_tx(sw, rconn, b); /* Send OFPT_SET_CONFIG. */ - b = buffer_new(0); - osc = buffer_put_uninit(b, sizeof *osc); + b = ofpbuf_new(0); + osc = ofpbuf_put_uninit(b, sizeof *osc); memset(osc, 0, sizeof *osc); osc->header.type = OFPT_SET_CONFIG; osc->header.version = OFP_VERSION; @@ -169,20 +187,16 @@ send_features_request(struct lswitch *sw, struct rconn *rconn) } static void -queue_tx(struct lswitch *sw, struct rconn *rconn, struct buffer *b) +queue_tx(struct lswitch *sw, struct rconn *rconn, struct ofpbuf *b) { - int retval = rconn_send(rconn, b); - if (retval) { + int retval = rconn_send_with_limit(rconn, b, &sw->n_queued, 10); + if (retval && retval != ENOTCONN) { if (retval == EAGAIN) { - /* FIXME: ratelimit. */ - VLOG_WARN("%s: tx queue overflow", rconn_get_name(rconn)); - } else if (retval == ENOTCONN) { - /* Ignore. */ + VLOG_WARN_RL(&rl, "%s: tx queue overflow", rconn_get_name(rconn)); } else { - /* FIXME: ratelimit. */ - VLOG_WARN("%s: send: %s", rconn_get_name(rconn), strerror(retval)); + VLOG_WARN_RL(&rl, "%s: send: %s", + rconn_get_name(rconn), strerror(retval)); } - buffer_delete(b); } } @@ -194,7 +208,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, uint16_t out_port = OFPP_FLOOD; size_t pkt_ofs, pkt_len; - struct buffer pkt; + struct ofpbuf pkt; struct flow flow; /* Extract flow data from 'opi' into 'flow'. */ @@ -206,18 +220,22 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, if (sw->ml) { if (mac_learning_learn(sw->ml, flow.dl_src, in_port)) { - VLOG_DBG("learned that "ETH_ADDR_FMT" is on datapath %" - PRIx64" port %"PRIu16, ETH_ADDR_ARGS(flow.dl_src), - ntohll(sw->datapath_id), in_port); + VLOG_DBG_RL(&rl, "learned that "ETH_ADDR_FMT" is on datapath %" + PRIx64" port %"PRIu16, ETH_ADDR_ARGS(flow.dl_src), + ntohll(sw->datapath_id), in_port); } out_port = mac_learning_lookup(sw->ml, flow.dl_dst); } - if (sw->setup_flows && (!sw->ml || out_port != OFPP_FLOOD)) { + if (in_port == out_port) { + /* The input and output port match. Set up a flow to drop packets. */ + queue_tx(sw, rconn, make_add_flow(&flow, ntohl(opi->buffer_id), + sw->max_idle, 0)); + } else if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) { /* The output port is known, or we always flood everything, so add a * new flow. */ queue_tx(sw, rconn, make_add_simple_flow(&flow, ntohl(opi->buffer_id), - out_port)); + out_port, sw->max_idle)); /* If the switch didn't buffer the packet, we need to send a copy. */ if (ntohl(opi->buffer_id) == UINT32_MAX) { @@ -227,7 +245,7 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, } else { /* We don't know that MAC, or we don't set up flows. Send along the * packet without setting up a flow. */ - struct buffer *b; + struct ofpbuf *b; if (ntohl(opi->buffer_id) == UINT32_MAX) { b = make_unbuffered_packet_out(&pkt, in_port, out_port); } else { @@ -237,3 +255,10 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, queue_tx(sw, rconn, b); } } + +static void +process_echo_request(struct lswitch *sw, struct rconn *rconn, + struct ofp_header *rq) +{ + queue_tx(sw, rconn, make_echo_reply(rq)); +}