int backoff;
time_t last_connected;
unsigned int packets_sent;
+
+ /* Throughout this file, "probe" is shorthand for "inactivity probe".
+ * When nothing has been received from the peer for a while, we send out
+ * an echo request as an inactivity probe packet. We should receive back
+ * a response. */
+ int probe_interval; /* Secs of inactivity before sending probe. */
+ time_t probe_sent; /* Time at which last probe sent, or 0 if none
+ * has been sent since 'last_connected'. */
};
static struct rconn *create_rconn(const char *name, int txq_limit,
- struct vconn *);
+ int probe_interval, struct vconn *);
static int try_send(struct rconn *);
static void disconnect(struct rconn *, int error);
+static time_t probe_deadline(const struct rconn *);
/* Creates and returns a new rconn that connects (and re-connects as necessary)
* to the vconn named 'name'.
*
- * 'txq_limit' is the maximum length of the send queue, in packets. */
+ * 'txq_limit' is the maximum length of the send queue, in packets.
+ *
+ * 'probe_interval' is a number of seconds. If the interval passes once
+ * without an OpenFlow message being received from the peer, the rconn sends
+ * out an "echo request" message. If the interval passes again without a
+ * message being received, the rconn disconnects and re-connects to the peer.
+ * Setting 'probe_interval' to 0 disables this behavior. */
struct rconn *
-rconn_new(const char *name, int txq_limit)
+rconn_new(const char *name, int txq_limit, int probe_interval)
{
- return create_rconn(name, txq_limit, NULL);
+ return create_rconn(name, txq_limit, probe_interval, NULL);
}
/* Creates and returns a new rconn that is initially connected to 'vconn' and
rconn_new_from_vconn(const char *name, int txq_limit, struct vconn *vconn)
{
assert(vconn != NULL);
- return create_rconn(name, txq_limit, vconn);
+ return create_rconn(name, txq_limit, 0, vconn);
}
/* Disconnects 'rc' and frees the underlying storage. */
disconnect(rc, 0);
}
} else {
+ if (rc->probe_interval) {
+ time_t now = time(0);
+ if (now >= probe_deadline(rc)) {
+ if (!rc->probe_sent) {
+ queue_push_tail(&rc->txq, make_echo_request());
+ rc->probe_sent = now;
+ VLOG_DBG("%s: idle %d seconds, sending inactivity probe",
+ rc->name, (int) (now - rc->last_connected));
+ } else {
+ VLOG_ERR("%s: no response to inactivity probe after %d "
+ "seconds, disconnecting",
+ rc->name, (int) (now - rc->probe_sent));
+ disconnect(rc, 0);
+ }
+ }
+ }
while (rc->txq.n > 0) {
int error = try_send(rc);
if (error == EAGAIN) {
/* Causes the next call to poll_block() to wake up when rconn_run() should be
* called on 'rc'. */
void
-rconn_run_wait(struct rconn *rc)
+rconn_run_wait(struct rconn *rc)
{
if (rc->vconn) {
if (rc->txq.n) {
vconn_wait(rc->vconn, WAIT_SEND);
}
+ if (rc->probe_interval) {
+ poll_timer_wait((probe_deadline(rc) - time(0)) * 1000);
+ }
} else {
poll_timer_wait((rc->backoff_deadline - time(0)) * 1000);
}
}
+/* Returns the time at which, should nothing be received, we should send out an
+ * inactivity probe (if none has yet been sent) or conclude that the connection
+ * is dead (if a probe has already been sent). */
+static time_t
+probe_deadline(const struct rconn *rc)
+{
+ assert(rc->probe_interval);
+ return (rc->probe_interval
+ + (rc->probe_sent ? rc->probe_sent : rc->last_connected));
+}
+
/* Attempts to receive a packet from 'rc'. If successful, returns the packet;
* otherwise, returns a null pointer. The caller is responsible for freeing
* the packet (with buffer_delete()). */
int error = vconn_recv(rc->vconn, &buffer);
if (!error) {
rc->last_connected = time(0);
+ rc->probe_sent = 0;
return buffer;
} else if (error != EAGAIN) {
disconnect(rc, error);
* full, or ENOTCONN if 'rc' is not currently connected.
*
* There is no rconn_send_wait() function: an rconn has a send queue that it
- * takes care of sending if you call rconn_wait(), which will have the side
+ * takes care of sending if you call rconn_run(), which will have the side
* effect of waking up poll_block(). */
int
rconn_send(struct rconn *rc, struct buffer *b)
{
return rconn_is_connected(rconn) ? 0 : time(0) - rconn->last_connected;
}
+
+/* Returns the IP address of the peer, or 0 if the peer is not connected over
+ * an IP-based protocol or if its IP address is not known. */
+uint32_t
+rconn_get_ip(const struct rconn *rconn)
+{
+ return rconn->vconn ? vconn_get_ip(rconn->vconn) : 0;
+}
\f
static struct rconn *
-create_rconn(const char *name, int txq_limit, struct vconn *vconn)
+create_rconn(const char *name, int txq_limit, int probe_interval,
+ struct vconn *vconn)
{
struct rconn *rc = xmalloc(sizeof *rc);
assert(txq_limit > 0);
rc->vconn = vconn;
queue_init(&rc->txq);
rc->txq_limit = txq_limit;
- rc->backoff_deadline = 0;
+ rc->backoff_deadline = time(0);
rc->backoff = 0;
rc->last_connected = time(0);
+ rc->probe_interval = (probe_interval
+ ? MAX(5, probe_interval) : 0);
+ rc->probe_sent = 0;
rc->packets_sent = 0;
return rc;
}
rc->name, rc->backoff);
}
rc->backoff_deadline = now + rc->backoff;
+ rc->probe_sent = 0;
}