#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "packets.h"
+#include "poll-loop.h"
#include "rconn.h"
#include "status.h"
#include "timeval.h"
#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;
/* 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);
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)
{
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_)
{
}
}
+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;
}
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",
*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. */
}
}
+