From: Ben Pfaff Date: Wed, 18 Mar 2009 00:02:20 +0000 (-0700) Subject: secchan: Implement in-band control using wildcard rules. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9210cf2268cd246bf92a05d5efb74d90dc61eae5;p=openvswitch secchan: Implement in-band control using wildcard rules. Until now, there has been a problem with flows set up by the controller overriding in-band control decisions: if the controller sets up, say, a flow that matches every packet, then the in-band control flow miss handler will never be called, and the connection to the controller may well fail. This commit fixes the problem by implementing in-band control in terms of wildcard rules that have a higher priority than any rule that can be set up by the OpenFlow controller. This should also fix the issue that in-band control does not respect VLANs and other structure set up by the vswitch, since the rules set up by in-band control use OFPP_NORMAL. --- diff --git a/secchan/in-band.c b/secchan/in-band.c index 0379e748..b84a7751 100644 --- a/secchan/in-band.c +++ b/secchan/in-band.c @@ -48,6 +48,7 @@ #include "ofpbuf.h" #include "openflow/openflow.h" #include "packets.h" +#include "poll-loop.h" #include "rconn.h" #include "status.h" #include "timeval.h" @@ -56,8 +57,31 @@ #define THIS_MODULE VLM_in_band #include "vlog.h" +#define IB_BASE_PRIORITY 18181800 + +enum { + IBR_FROM_LOCAL_PORT, /* Sent by secure channel. */ + IBR_TO_LOCAL_PORT, /* Sent to secure channel. */ + IBR_ARP_FROM_CTL, /* ARP from the controller. */ + IBR_TO_CTL_OFP_SRC, /* To controller, OpenFlow source port. */ + IBR_TO_CTL_OFP_DST, /* To controller, OpenFlow dest port. */ + IBR_FROM_CTL_OFP_SRC, /* From controller, OpenFlow source port. */ + IBR_FROM_CTL_OFP_DST, /* From controller, OpenFlow dest port. */ +#if OFP_TCP_PORT != OFP_SSL_PORT +#error Need to support separate TCP and SSL flows. +#endif + N_IB_RULES +}; + +struct ib_rule { + bool installed; + flow_t flow; + uint32_t wildcards; + unsigned int priority; +}; + struct in_band { - struct mac_learning *mac_learning; + struct ofproto *ofproto; struct netdev *netdev; struct rconn *controller; struct status_category *ss_cat; @@ -72,6 +96,9 @@ struct in_band { /* Keeping track of the local port's MAC address. */ uint8_t local_mac[ETH_ADDR_LEN]; /* Current MAC. */ time_t next_local_refresh; /* Next time to refresh MAC address. */ + + /* Rules that we set up. */ + struct ib_rule rules[N_IB_RULES]; }; static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(60, 60); @@ -123,14 +150,6 @@ get_controller_mac(struct in_band *ib) return !eth_addr_is_zero(ib->mac) ? ib->mac : NULL; } -static bool -is_controller_mac(const uint8_t dl_addr[ETH_ADDR_LEN], - struct in_band *in_band) -{ - const uint8_t *mac = get_controller_mac(in_band); - return mac && eth_addr_equals(mac, dl_addr); -} - static const uint8_t * get_local_mac(struct in_band *ib) { @@ -145,94 +164,6 @@ get_local_mac(struct in_band *ib) return !eth_addr_is_zero(ib->local_mac) ? ib->local_mac : NULL; } -static bool -is_local_mac(const uint8_t dl_addr[ETH_ADDR_LEN], struct in_band *ib) -{ - const uint8_t *local_mac = get_local_mac(ib); - return local_mac && eth_addr_equals(dl_addr, local_mac); -} - -static void -in_band_learn_mac(struct in_band *in_band, - uint16_t in_port, const uint8_t src_mac[ETH_ADDR_LEN]) -{ - if (mac_learning_learn(in_band->mac_learning, src_mac, 0, in_port)) { - VLOG_DBG_RL(&rl, "learned that "ETH_ADDR_FMT" is on port %"PRIu16, - ETH_ADDR_ARGS(src_mac), in_port); - } -} - -bool -in_band_handle_flow_miss(struct in_band *in_band, struct ofproto *ofproto, - const flow_t *flow, const struct ofpbuf *payload) -{ - /* -1 (FLOOD) is coincidentally the value returned by mac_learning_lookup() - * when it doesn't have a entry for that address. */ - enum { FLOOD = -1, DROP = -2 }; - union ofp_action action; - int out_port; - - /* Deal with local stuff. */ - if (flow->in_port == ODPP_LOCAL) { - /* Sent by secure channel. */ - out_port = mac_learning_lookup(in_band->mac_learning, flow->dl_dst, 0); - } else if (is_local_mac(flow->dl_dst, in_band)) { - /* Sent to secure channel. */ - out_port = ODPP_LOCAL; - in_band_learn_mac(in_band, flow->in_port, flow->dl_src); - } else if (flow->dl_type == htons(ETH_TYPE_ARP) - && eth_addr_is_broadcast(flow->dl_dst) - && is_controller_mac(flow->dl_src, in_band)) { - /* ARP sent by controller. */ - out_port = FLOOD; - } else if ((is_controller_mac(flow->dl_dst, in_band) || - is_controller_mac(flow->dl_src, in_band)) - && flow->dl_type == htons(ETH_TYPE_IP) - && flow->nw_proto == IP_TYPE_TCP - && (flow->tp_src == htons(OFP_TCP_PORT) || - flow->tp_src == htons(OFP_SSL_PORT) || - flow->tp_dst == htons(OFP_TCP_PORT) || - flow->tp_dst == htons(OFP_SSL_PORT))) { - /* Traffic to or from controller. Switch it by hand. */ - in_band_learn_mac(in_band, flow->in_port, flow->dl_src); - out_port = mac_learning_lookup(in_band->mac_learning, flow->dl_dst, 0); - } else { - const uint8_t *controller_mac = get_controller_mac(in_band); - if (flow->dl_type == htons(ETH_TYPE_ARP) - && eth_addr_is_broadcast(flow->dl_dst) - && is_controller_mac(flow->dl_src, in_band)) { - /* ARP sent by controller. */ - out_port = FLOOD; - } else if (is_controller_mac(flow->dl_dst, in_band) - && flow->in_port == mac_learning_lookup( - in_band->mac_learning, controller_mac, 0)) { - /* Drop controller traffic that arrives on the controller port. */ - out_port = DROP; - } else { - return false; - } - } - - memset(&action, 0, sizeof action); - action.output.type = htons(OFPAT_OUTPUT); - action.output.len = htons(sizeof action); - if (flow->in_port == out_port || out_port == DROP) { - /* Set up a flow to drop packets. */ - ofproto_add_flow(ofproto, flow, 0, UINT16_MAX, NULL, 0, NULL, -1); - } else if (out_port != FLOOD) { - /* The output port is known, so add a new flow. */ - action.output.port = htons(odp_port_to_ofp_port(out_port)); - ofproto_add_flow(ofproto, flow, 0, UINT16_MAX, - &action, 1, payload, -1); - } else { - /* We don't know that MAC. Send along the packet without setting up a - * flow. */ - action.output.port = htons(OFPP_FLOOD); - ofproto_send_packet(ofproto, flow, &action, 1, payload); - } - return true; -} - static void in_band_status_cb(struct status_reply *sr, void *in_band_) { @@ -263,20 +194,132 @@ in_band_status_cb(struct status_reply *sr, void *in_band_) } } +static void +drop_flow(struct in_band *in_band, int rule_idx) +{ + struct ib_rule *rule = &in_band->rules[rule_idx]; + + if (rule->installed) { + rule->installed = false; + ofproto_delete_flow(in_band->ofproto, &rule->flow, rule->wildcards, + rule->priority); + } +} + +/* out_port and fixed_fields are assumed never to change. */ +static void +setup_flow(struct in_band *in_band, int rule_idx, const flow_t *flow, + uint32_t fixed_fields, uint16_t out_port) +{ + struct ib_rule *rule = &in_band->rules[rule_idx]; + + if (!rule->installed || memcmp(flow, &rule->flow, sizeof *flow)) { + union ofp_action action; + + drop_flow(in_band, rule_idx); + + rule->installed = true; + rule->flow = *flow; + rule->wildcards = OFPFW_ALL & ~fixed_fields; + rule->priority = IB_BASE_PRIORITY + (N_IB_RULES - rule_idx); + + action.type = htons(OFPAT_OUTPUT); + action.output.len = htons(sizeof action); + action.output.port = htons(out_port); + action.output.max_len = htons(0); + ofproto_add_flow(in_band->ofproto, &rule->flow, rule->wildcards, + rule->priority, &action, 1, NULL, 0); + } +} + void in_band_run(struct in_band *in_band) { - mac_learning_run(in_band->mac_learning, NULL); + const uint8_t *controller_mac; + const uint8_t *local_mac; + flow_t flow; + + if (time_now() < MIN(in_band->next_refresh, in_band->next_local_refresh)) { + return; + } + controller_mac = get_controller_mac(in_band); + local_mac = get_local_mac(in_band); + + /* Switch traffic sent by the secure channel. */ + memset(&flow, 0, sizeof flow); + flow.in_port = ODPP_LOCAL; + setup_flow(in_band, IBR_FROM_LOCAL_PORT, &flow, OFPFW_IN_PORT, + OFPP_NORMAL); + + /* Deliver traffic sent to the secure channel to the local port. */ + if (local_mac) { + memset(&flow, 0, sizeof flow); + memcpy(flow.dl_dst, local_mac, ETH_ADDR_LEN); + setup_flow(in_band, IBR_TO_LOCAL_PORT, &flow, OFPFW_DL_DST, + OFPP_LOCAL); + } else { + drop_flow(in_band, IBR_TO_LOCAL_PORT); + } + + if (controller_mac) { + /* Switch ARP requests sent by the controller. (OFPP_NORMAL will "do + * the right thing" regarding VLANs here.) */ + memset(&flow, 0, sizeof flow); + flow.dl_type = htons(ETH_TYPE_ARP); + memcpy(flow.dl_dst, eth_addr_broadcast, ETH_ADDR_LEN); + memcpy(flow.dl_src, controller_mac, ETH_ADDR_LEN); + setup_flow(in_band, IBR_ARP_FROM_CTL, &flow, + OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_DL_SRC, + OFPP_NORMAL); + + /* OpenFlow traffic to or from the controller. + * + * (A given field's value is completely ignored if it is wildcarded, + * which is why we can get away with using a single 'flow' in each + * case here.) */ + memset(&flow, 0, sizeof flow); + flow.dl_type = htons(ETH_TYPE_IP); + memcpy(flow.dl_src, controller_mac, ETH_ADDR_LEN); + memcpy(flow.dl_dst, controller_mac, ETH_ADDR_LEN); + flow.nw_proto = IP_TYPE_TCP; + flow.tp_src = htons(OFP_TCP_PORT); + flow.tp_dst = htons(OFP_TCP_PORT); + setup_flow(in_band, IBR_TO_CTL_OFP_SRC, &flow, + (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO + | OFPFW_TP_SRC), OFPP_NORMAL); + setup_flow(in_band, IBR_TO_CTL_OFP_DST, &flow, + (OFPFW_DL_TYPE | OFPFW_DL_DST | OFPFW_NW_PROTO + | OFPFW_TP_DST), OFPP_NORMAL); + setup_flow(in_band, IBR_FROM_CTL_OFP_SRC, &flow, + (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO + | OFPFW_TP_SRC), OFPP_NORMAL); + setup_flow(in_band, IBR_FROM_CTL_OFP_DST, &flow, + (OFPFW_DL_TYPE | OFPFW_DL_SRC | OFPFW_NW_PROTO + | OFPFW_TP_DST), OFPP_NORMAL); + } else { + drop_flow(in_band, IBR_ARP_FROM_CTL); + drop_flow(in_band, IBR_TO_CTL_OFP_DST); + drop_flow(in_band, IBR_TO_CTL_OFP_SRC); + drop_flow(in_band, IBR_FROM_CTL_OFP_DST); + drop_flow(in_band, IBR_FROM_CTL_OFP_SRC); + } } void in_band_wait(struct in_band *in_band) { - mac_learning_wait(in_band->mac_learning); + time_t now = time_now(); + time_t wakeup = MIN(in_band->next_refresh, in_band->next_local_refresh); + if (wakeup > now) { + poll_timer_wait((wakeup - now) * 1000); + } else { + poll_immediate_wake(); + } } int -in_band_create(struct dpif *dpif, struct switch_status *ss, +in_band_create(struct ofproto *ofproto, + struct dpif *dpif, struct switch_status *ss, struct rconn *controller, struct in_band **in_bandp) { struct in_band *in_band; @@ -298,7 +341,7 @@ in_band_create(struct dpif *dpif, struct switch_status *ss, } in_band = xcalloc(1, sizeof *in_band); - in_band->mac_learning = mac_learning_create(); + in_band->ofproto = ofproto; in_band->netdev = netdev; in_band->controller = controller; in_band->ss_cat = switch_status_register(ss, "in-band", @@ -309,13 +352,14 @@ in_band_create(struct dpif *dpif, struct switch_status *ss, *in_bandp = in_band; return 0; } + void in_band_destroy(struct in_band *in_band) { if (in_band) { - mac_learning_destroy(in_band->mac_learning); netdev_close(in_band->netdev); switch_status_unregister(in_band->ss_cat); /* We don't own the rconn. */ } } + diff --git a/secchan/in-band.h b/secchan/in-band.h index 570f65bf..979580c7 100644 --- a/secchan/in-band.h +++ b/secchan/in-band.h @@ -44,12 +44,10 @@ struct secchan; struct settings; struct switch_status; -int in_band_create(struct dpif *, struct switch_status *, +int in_band_create(struct ofproto *, struct dpif *, struct switch_status *, struct rconn *controller, struct in_band **); void in_band_destroy(struct in_band *); void in_band_run(struct in_band *); void in_band_wait(struct in_band *); -bool in_band_handle_flow_miss(struct in_band *, struct ofproto *, - const flow_t *, const struct ofpbuf *payload); #endif /* in-band.h */ diff --git a/secchan/ofproto.c b/secchan/ofproto.c index 6a932e94..a4792830 100644 --- a/secchan/ofproto.c +++ b/secchan/ofproto.c @@ -2721,12 +2721,6 @@ handle_odp_msg(struct ofproto *p, struct ofpbuf *packet) if (!rule) { struct ofport *port; - if (p->in_band && in_band_handle_flow_miss(p->in_band, p, - &flow, &payload)) { - ofpbuf_delete(packet); - return; - } - if (p->fail_open && fail_open_handle_flow_miss(p->fail_open, p, &flow, &payload)) { ofpbuf_delete(packet);