-queue_tx(struct rconn *rc, struct buffer *b)
-{
- if (rconn_force_send(rc, b)) {
- buffer_delete(b);
- }
-}
-
-static bool
-is_controller_mac(const uint8_t dl_addr[ETH_ADDR_LEN],
- struct rconn *controller)
-{
- static uint32_t ip, last_nonzero_ip;
- static uint8_t mac[ETH_ADDR_LEN], last_nonzero_mac[ETH_ADDR_LEN];
- static time_t next_refresh = 0;
-
- uint32_t last_ip = ip;
-
- time_t now = time(0);
-
- ip = rconn_get_ip(controller);
- if (last_ip != ip || !next_refresh || now >= next_refresh) {
- bool have_mac;
-
- /* Look up MAC address. */
- memset(mac, 0, sizeof mac);
- if (ip) {
- int retval = netdev_arp_lookup(of_device, ip, mac);
- if (retval) {
- VLOG_DBG("cannot look up controller hw address ("IP_FMT"): %s",
- IP_ARGS(&ip), strerror(retval));
- }
- }
- have_mac = !eth_addr_is_zero(mac);
-
- /* Log changes in IP, MAC addresses. */
- if (ip && ip != last_nonzero_ip) {
- VLOG_DBG("controller IP address changed from "IP_FMT
- " to "IP_FMT, IP_ARGS(&last_nonzero_ip), IP_ARGS(&ip));
- last_nonzero_ip = ip;
- }
- if (have_mac && memcmp(last_nonzero_mac, mac, ETH_ADDR_LEN)) {
- VLOG_DBG("controller MAC address changed from "ETH_ADDR_FMT" to "
- ETH_ADDR_FMT,
- ETH_ADDR_ARGS(last_nonzero_mac), ETH_ADDR_ARGS(mac));
- memcpy(last_nonzero_mac, mac, ETH_ADDR_LEN);
- }
-
- /* Schedule next refresh.
- *
- * If we have an IP address but not a MAC address, then refresh
- * quickly, since we probably will get a MAC address soon (via ARP).
- * Otherwise, we can afford to wait a little while. */
- next_refresh = now + (!ip || have_mac ? 10 : 1);
- }
- return !eth_addr_is_zero(mac) && eth_addr_equals(mac, dl_addr);
-}
-
-static bool
-local_hook(struct relay *r)
-{
- struct rconn *rc = r->halves[HALF_LOCAL].rconn;
- struct buffer *msg = r->halves[HALF_LOCAL].rxbuf;
- struct ofp_packet_in *opi;
- struct ofp_header *oh;
- size_t pkt_ofs, pkt_len;
- struct buffer pkt;
- struct flow flow;
- uint16_t in_port, out_port;
-
- if (!local_port) {
- return false;
- }
-
- oh = msg->data;
- if (oh->type != OFPT_PACKET_IN) {
- return false;
- }
- if (msg->size < offsetof (struct ofp_packet_in, data)) {
- VLOG_WARN("packet too short (%zu bytes) for packet_in", msg->size);
- return false;
- }
-
- /* Extract flow data from 'opi' into 'flow'. */
- opi = msg->data;
- in_port = ntohs(opi->in_port);
- pkt_ofs = offsetof(struct ofp_packet_in, data);
- pkt_len = ntohs(opi->header.length) - pkt_ofs;
- pkt.data = opi->data;
- pkt.size = pkt_len;
- flow_extract(&pkt, in_port, &flow);
-
- /* Deal with local stuff. */
- if (in_port == OFPP_LOCAL) {
- out_port = mac_learning_lookup(local_ml, flow.dl_dst);
- } else if (eth_addr_equals(flow.dl_dst, local_mac)) {
- out_port = OFPP_LOCAL;
- if (mac_learning_learn(local_ml, flow.dl_src, in_port)) {
- VLOG_DBG("learned that "ETH_ADDR_FMT" is on port %"PRIu16,
- ETH_ADDR_ARGS(flow.dl_src), in_port);
- }
- } else if (flow.dl_type == htons(ETH_TYPE_ARP)
- && eth_addr_is_broadcast(flow.dl_dst)
- && is_controller_mac(flow.dl_src,
- r->halves[HALF_REMOTE].rconn)) {
- out_port = OFPP_FLOOD;
- } else {
- return false;
- }
-
- if (out_port != OFPP_FLOOD) {
- /* The output port is known, so add a new flow. */
- queue_tx(rc, make_add_simple_flow(&flow, ntohl(opi->buffer_id),
- out_port, max_idle));
-
- /* If the switch didn't buffer the packet, we need to send a copy. */
- if (ntohl(opi->buffer_id) == UINT32_MAX) {
- queue_tx(rc, make_unbuffered_packet_out(&pkt, in_port, out_port));
- }
- } else {
- /* We don't know that MAC. Send along the packet without setting up a
- * flow. */
- struct buffer *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);
- }
- queue_tx(rc, b);
- }
- return true;
-}
-
-/* Causess 'r' to enter or leave fail-open mode, if appropriate. Returns true
- * if 'r' is in fail-open fail, false otherwise. */
-static bool
-failing_open(struct relay *r)
-{
- struct rconn *local = r->halves[HALF_LOCAL].rconn;
- struct rconn *remote = r->halves[HALF_REMOTE].rconn;
- int disconnected_duration;
-
- if (fail_mode == FAIL_CLOSED) {
- /* We fail closed, so there's never anything to do. */
- return false;
- }
-
- disconnected_duration = rconn_disconnected_duration(remote);
- if (disconnected_duration < probe_interval * 3) {
- /* It's not time to fail open yet. */
- if (r->lswitch && rconn_is_connected(remote)) {
- /* We're connected, so drop the learning switch. */
- VLOG_WARN("No longer in fail-open mode");
- lswitch_destroy(r->lswitch);
- r->lswitch = NULL;
- }
- return false;
- }
-
- if (!r->lswitch) {
- VLOG_WARN("Could not connect to controller for %d seconds, "
- "failing open", disconnected_duration);
- r->lswitch = lswitch_create(local, true, max_idle);
- }
- return true;
-}
-
-static bool
-fail_open_hook(struct relay *r)
-{
- if (!failing_open(r)) {
- return false;
- } else {
- struct buffer *msg = r->halves[HALF_LOCAL].rxbuf;
- struct rconn *local = r->halves[HALF_LOCAL].rconn;
- lswitch_process_packet(r->lswitch, local, msg);
- rconn_run(local);
- return true;
- }
-}
-
-static void
-parse_options(int argc, char *argv[])