X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Flearning-switch.c;h=aba3525fdc03472d1200da8d68ce6a8df72d52fe;hb=abfec865566e6cce961cc8660de1ddfdc85dae5f;hp=73464c688aacd2770fc0d01a1c8087c29bc75b1e;hpb=a14bc59fb8f27db193d74662dc9c5cb8237177ef;p=openvswitch diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 73464c68..aba3525f 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009 Nicira Networks. + * Copyright (c) 2008, 2009, 2010 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,9 @@ #include "flow.h" #include "mac-learning.h" #include "ofpbuf.h" +#include "ofp-parse.h" #include "ofp-print.h" +#include "ofp-util.h" #include "openflow/openflow.h" #include "poll-loop.h" #include "queue.h" @@ -34,10 +36,10 @@ #include "stp.h" #include "timeval.h" #include "vconn.h" +#include "vlog.h" #include "xtoxll.h" -#define THIS_MODULE VLM_learning_switch -#include "vlog.h" +VLOG_DEFINE_THIS_MODULE(learning_switch) enum port_state { P_DISABLED = 1 << 0, @@ -57,6 +59,9 @@ struct lswitch { uint32_t capabilities; time_t last_features_request; 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. */ /* Number of outgoing queued packets on the rconn. */ struct rconn_packet_counter *queued; @@ -80,6 +85,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 send_default_flows(struct lswitch *sw, struct rconn *rconn, + FILE *default_flows); static void schedule_query(struct lswitch *, long long int delay); static bool may_learn(const struct lswitch *, uint16_t port_no); static bool may_recv(const struct lswitch *, uint16_t port_no, @@ -103,18 +110,38 @@ static packet_handler_func process_stats_reply; * 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 the file stream 'default_flows' that defines + * default flows that should be pushed when a switch connects. Each + * line is a flow entry in the format described for "add-flows" command + * in the Flow Syntax section of the ovs-ofct(8) man page. The caller + * is responsible for closing the stream. + * * 'rconn' is used to send out an OpenFlow features request. */ struct lswitch * -lswitch_create(struct rconn *rconn, bool learn_macs, int max_idle) +lswitch_create(struct rconn *rconn, bool learn_macs, + bool exact_flows, int max_idle, bool action_normal, + FILE *default_flows) { struct lswitch *sw; size_t i; - sw = xcalloc(1, sizeof *sw); + sw = xzalloc(sizeof *sw); sw->max_idle = 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) { + /* Exact match. */ + sw->wildcards = 0; + } else { + /* We cannot wildcard all fields. + * We need in_port to detect moves. + * We need both SA and DA to do learning. */ + 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->queued = rconn_packet_counter_create(); sw->next_query = LLONG_MIN; sw->last_query = LLONG_MIN; @@ -123,6 +150,9 @@ lswitch_create(struct rconn *rconn, bool learn_macs, int max_idle) sw->port_states[i] = P_DISABLED; } send_features_request(sw, rconn); + if (default_flows) { + send_default_flows(sw, rconn, default_flows); + } return sw; } @@ -137,6 +167,15 @@ lswitch_destroy(struct lswitch *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 @@ -151,7 +190,7 @@ lswitch_run(struct lswitch *sw, struct rconn *rconn) /* If we're waiting for more replies, keeping waiting for up to 10 s. */ if (sw->last_reply != LLONG_MIN) { if (now - sw->last_reply > 10000) { - VLOG_ERR_RL(&rl, "%012llx: No more flow stat replies last 10 s", + VLOG_ERR_RL(&rl, "%016llx: No more flow stat replies last 10 s", sw->datapath_id); sw->last_reply = LLONG_MIN; sw->last_query = LLONG_MIN; @@ -164,7 +203,7 @@ lswitch_run(struct lswitch *sw, struct rconn *rconn) /* If we're waiting for any reply at all, keep waiting for up to 10 s. */ if (sw->last_query != LLONG_MIN) { if (now - sw->last_query > 10000) { - VLOG_ERR_RL(&rl, "%012llx: No flow stat replies in last 10 s", + VLOG_ERR_RL(&rl, "%016llx: No flow stat replies in last 10 s", sw->datapath_id); sw->last_query = LLONG_MIN; schedule_query(sw, 0); @@ -184,7 +223,7 @@ lswitch_run(struct lswitch *sw, struct rconn *rconn) struct ofpbuf *b; int error; - VLOG_DBG("%012llx: Sending flow stats request to implement STP", + VLOG_DBG("%016llx: Sending flow stats request to implement STP", sw->datapath_id); sw->last_query = now; @@ -203,7 +242,7 @@ lswitch_run(struct lswitch *sw, struct rconn *rconn) error = rconn_send(rconn, b, NULL); if (error) { - VLOG_WARN_RL(&rl, "%012llx: sending flow stats request " + VLOG_WARN_RL(&rl, "%016llx: sending flow stats request " "failed: %s", sw->datapath_id, strerror(error)); ofpbuf_delete(b); schedule_query(sw, 1000); @@ -215,13 +254,7 @@ lswitch_run(struct lswitch *sw, struct rconn *rconn) static void wait_timeout(long long int started) { - long long int now = time_msec(); - long long int timeout = 10000 - (now - started); - if (timeout <= 0) { - poll_immediate_wake(); - } else { - poll_timer_wait(timeout); - } + poll_timer_wait_until(started + 10000); } void @@ -278,8 +311,8 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, process_stats_reply }, { - OFPT_FLOW_EXPIRED, - sizeof(struct ofp_flow_expired), + OFPT_FLOW_REMOVED, + sizeof(struct ofp_flow_removed), NULL }, }; @@ -298,7 +331,7 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, for (p = processors; p < &processors[n_processors]; p++) { if (oh->type == p->type) { if (msg->size < p->min_size) { - VLOG_WARN_RL(&rl, "%012llx: %s: too short (%zu bytes) for " + 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); @@ -312,7 +345,7 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, } if (VLOG_IS_DBG_ENABLED()) { char *p = ofp_to_string(msg->data, msg->size, 2); - VLOG_DBG_RL(&rl, "%012llx: OpenFlow packet ignored: %s", + VLOG_DBG_RL(&rl, "%016llx: OpenFlow packet ignored: %s", sw->datapath_id, p); free(p); } @@ -332,7 +365,6 @@ send_features_request(struct lswitch *sw, struct rconn *rconn) /* Send OFPT_SET_CONFIG. */ osc = make_openflow(sizeof *osc, OFPT_SET_CONFIG, &b); - osc->flags = htons(OFPC_SEND_FLOW_EXP); osc->miss_send_len = htons(OFP_DEFAULT_MISS_SEND_LEN); queue_tx(sw, rconn, b); @@ -340,16 +372,63 @@ send_features_request(struct lswitch *sw, struct rconn *rconn) } } +static void +send_default_flows(struct lswitch *sw, struct rconn *rconn, + FILE *default_flows) +{ + char line[1024]; + + while (fgets(line, sizeof line, default_flows)) { + struct ofpbuf *b; + struct ofp_flow_mod *ofm; + uint16_t priority, idle_timeout, hard_timeout; + uint64_t cookie; + struct ofp_match match; + + char *comment; + + /* Delete comments. */ + comment = strchr(line, '#'); + if (comment) { + *comment = '\0'; + } + + /* Drop empty lines. */ + if (line[strspn(line, " \t\n")] == '\0') { + continue; + } + + /* Parse and send. str_to_flow() will expand and reallocate the data + * in 'buffer', so we can't keep pointers to across the str_to_flow() + * call. */ + make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &b); + parse_ofp_str(line, &match, b, + NULL, NULL, &priority, &idle_timeout, &hard_timeout, + &cookie); + ofm = b->data; + ofm->match = match; + ofm->command = htons(OFPFC_ADD); + ofm->cookie = htonll(cookie); + ofm->idle_timeout = htons(idle_timeout); + ofm->hard_timeout = htons(hard_timeout); + ofm->buffer_id = htonl(UINT32_MAX); + ofm->priority = htons(priority); + + update_openflow_length(b); + queue_tx(sw, rconn, b); + } +} + static void queue_tx(struct lswitch *sw, struct rconn *rconn, struct ofpbuf *b) { int retval = rconn_send_with_limit(rconn, b, sw->queued, 10); if (retval && retval != ENOTCONN) { if (retval == EAGAIN) { - VLOG_INFO_RL(&rl, "%012llx: %s: tx queue overflow", + VLOG_INFO_RL(&rl, "%016llx: %s: tx queue overflow", sw->datapath_id, rconn_get_name(rconn)); } else { - VLOG_WARN_RL(&rl, "%012llx: %s: send: %s", + VLOG_WARN_RL(&rl, "%016llx: %s: send: %s", sw->datapath_id, rconn_get_name(rconn), strerror(retval)); } @@ -384,12 +463,60 @@ process_switch_features(struct lswitch *sw, struct rconn *rconn, void *osf_) } } +static uint16_t +lswitch_choose_destination(struct lswitch *sw, const flow_t *flow) +{ + uint16_t out_port; + + /* Learn the source MAC. */ + if (may_learn(sw, flow->in_port) && sw->ml) { + if (mac_learning_learn(sw->ml, flow->dl_src, 0, flow->in_port, + GRAT_ARP_LOCK_NONE)) { + 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); + } + } + + /* Drop frames for reserved multicast addresses. */ + if (eth_addr_is_reserved(flow->dl_dst)) { + return OFPP_NONE; + } + + if (!may_recv(sw, flow->in_port, false)) { + /* STP prevents receiving anything on this port. */ + return OFPP_NONE; + } + + out_port = OFPP_FLOOD; + if (sw->ml) { + int learned_port = mac_learning_lookup(sw->ml, flow->dl_dst, 0, NULL); + if (learned_port >= 0 && may_send(sw, learned_port)) { + out_port = learned_port; + if (out_port == flow->in_port) { + /* Don't send a packet back out its input port. */ + return OFPP_NONE; + } + } + } + + /* Check if we need to use "NORMAL" action. */ + if (sw->action_normal && out_port != OFPP_FLOOD) { + return OFPP_NORMAL; + } + + return out_port; +} + 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); - uint16_t out_port = OFPP_FLOOD; + uint16_t out_port; + + struct ofp_action_header actions[2]; + size_t actions_len; size_t pkt_ofs, pkt_len; struct ofpbuf pkt; @@ -400,71 +527,67 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_) pkt_len = ntohs(opi->header.length) - pkt_ofs; pkt.data = opi->data; pkt.size = pkt_len; - flow_extract(&pkt, in_port, &flow); + flow_extract(&pkt, 0, in_port, &flow); - if (may_learn(sw, in_port) && sw->ml) { - if (mac_learning_learn(sw->ml, flow.dl_src, 0, in_port)) { - VLOG_DBG_RL(&rl, "%012llx: learned that "ETH_ADDR_FMT" is on " - "port %"PRIu16, sw->datapath_id, - ETH_ADDR_ARGS(flow.dl_src), in_port); - } - } + /* Choose output port. */ + out_port = lswitch_choose_destination(sw, &flow); - if (eth_addr_is_reserved(flow.dl_src)) { - goto drop_it; - } + /* Make actions. */ + if (out_port == OFPP_NONE) { + actions_len = 0; + } else if (sw->queue == UINT32_MAX || out_port >= OFPP_MAX) { + struct ofp_action_output oao; - if (!may_recv(sw, in_port, false)) { - /* STP prevents receiving anything on this port. */ - goto drop_it; - } + memset(&oao, 0, sizeof oao); + oao.type = htons(OFPAT_OUTPUT); + oao.len = htons(sizeof oao); + oao.port = htons(out_port); - if (sw->ml) { - int learned_port = mac_learning_lookup(sw->ml, flow.dl_dst, 0); - if (learned_port >= 0 && may_send(sw, learned_port)) { - out_port = learned_port; - } + memcpy(actions, &oao, sizeof oao); + actions_len = sizeof oao; + } else { + struct ofp_action_enqueue oae; + + memset(&oae, 0, sizeof oae); + oae.type = htons(OFPAT_ENQUEUE); + oae.len = htons(sizeof oae); + oae.port = htons(out_port); + oae.queue_id = htonl(sw->queue); + + memcpy(actions, &oae, sizeof oae); + actions_len = sizeof oae; } + assert(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; + struct ofp_flow_mod *ofm; - if (in_port == out_port) { - /* Don't send out packets on their input ports. */ - goto drop_it; - } 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, sw->max_idle)); + buffer = make_add_flow(&flow, ntohl(opi->buffer_id), + sw->max_idle, actions_len); + ofpbuf_put(buffer, actions, actions_len); + ofm = buffer->data; + ofm->match.wildcards = htonl(sw->wildcards); + queue_tx(sw, rconn, buffer); /* If the switch didn't buffer the packet, we need to send a copy. */ - if (ntohl(opi->buffer_id) == UINT32_MAX) { + if (ntohl(opi->buffer_id) == UINT32_MAX && actions_len > 0) { queue_tx(sw, rconn, - make_unbuffered_packet_out(&pkt, in_port, out_port)); + make_packet_out(&pkt, UINT32_MAX, in_port, + actions, actions_len / sizeof *actions)); } } else { /* We don't know that MAC, or we don't set up flows. Send along the * packet without setting up a flow. */ - struct ofpbuf *b; - if (ntohl(opi->buffer_id) == UINT32_MAX) { - b = make_unbuffered_packet_out(&pkt, in_port, out_port); - } else { - b = make_buffered_packet_out(ntohl(opi->buffer_id), - in_port, out_port); + 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, b); - } - return; - -drop_it: - if (sw->max_idle >= 0) { - /* Set up a flow to drop packets. */ - queue_tx(sw, rconn, make_add_flow(&flow, ntohl(opi->buffer_id), - sw->max_idle, 0)); - } else { - /* Just drop the packet, since we don't set up flows at all. - * XXX we should send a packet_out with no actions if buffer_id != - * UINT32_MAX, to avoid clogging the kernel buffers. */ } - return; } static void @@ -482,7 +605,8 @@ process_port_status(struct lswitch *sw, struct rconn *rconn, void *ops_) } static void -process_phy_port(struct lswitch *sw, struct rconn *rconn UNUSED, void *opp_) +process_phy_port(struct lswitch *sw, struct rconn *rconn OVS_UNUSED, + void *opp_) { const struct ofp_phy_port *opp = opp_; uint16_t port_no = ntohs(opp->port_no); @@ -579,12 +703,12 @@ process_flow_stats(struct lswitch *sw, struct rconn *rconn, for (a = ofs->actions; (char *) a < end; a += len / 8) { len = ntohs(a->len); if (len > end - (char *) a) { - VLOG_DBG_RL(&rl, "%012llx: action exceeds available space " + VLOG_DBG_RL(&rl, "%016llx: action exceeds available space " "(%zu > %td)", sw->datapath_id, len, end - (char *) a); break; } else if (len % 8) { - VLOG_DBG_RL(&rl, "%012llx: action length (%zu) not multiple " + VLOG_DBG_RL(&rl, "%016llx: action length (%zu) not multiple " "of 8 bytes", sw->datapath_id, len); break; } @@ -630,7 +754,7 @@ process_stats_reply(struct lswitch *sw, struct rconn *rconn, void *osr_) process_flow_stats(sw, rconn, fs); } if (!(osr->flags & htons(OFPSF_REPLY_MORE))) { - VLOG_DBG("%012llx: Deleted %d of %d received flows to " + VLOG_DBG("%016llx: Deleted %d of %d received flows to " "implement STP, %d because of no-recv, %d because of " "no-send", sw->datapath_id, sw->n_no_recv + sw->n_no_send, sw->n_flows,