summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/proto_udp.c47
1 files changed, 45 insertions, 2 deletions
diff --git a/src/proto_udp.c b/src/proto_udp.c
index 33a4dca46..1dc731778 100644
--- a/src/proto_udp.c
+++ b/src/proto_udp.c
@@ -42,6 +42,7 @@
static int udp_bind_listener(struct listener *listener, char *errmsg, int errlen);
static int udp_suspend_receiver(struct receiver *rx);
+static int udp_resume_receiver(struct receiver *rx);
static void udp_enable_listener(struct listener *listener);
static void udp_disable_listener(struct listener *listener);
static void udp4_add_listener(struct listener *listener, int port);
@@ -62,6 +63,7 @@ static struct protocol proto_udp4 = {
.rx_enable = sock_enable,
.rx_disable = sock_disable,
.rx_suspend = udp_suspend_receiver,
+ .rx_resume = udp_resume_receiver,
.receivers = LIST_HEAD_INIT(proto_udp4.receivers),
.nb_receivers = 0,
};
@@ -83,6 +85,7 @@ static struct protocol proto_udp6 = {
.rx_enable = sock_enable,
.rx_disable = sock_disable,
.rx_suspend = udp_suspend_receiver,
+ .rx_resume = udp_resume_receiver,
.receivers = LIST_HEAD_INIT(proto_udp6.receivers),
.nb_receivers = 0,
};
@@ -181,11 +184,51 @@ static void udp_disable_listener(struct listener *l)
/* Suspend a receiver. Returns < 0 in case of failure, 0 if the receiver
* was totally stopped, or > 0 if correctly suspended.
+ * The principle is a bit ugly but works well, at least on Linux: in order to
+ * suspend the receiver, we want it to stop receiving traffic, which means that
+ * the socket must be unhashed from the kernel's socket table. The simple way
+ * to do this is to connect to any address that is reachable and will not be
+ * used by regular traffic, and a great one is reconnecting to self.
*/
static int udp_suspend_receiver(struct receiver *rx)
{
- /* we don't support suspend on UDP */
- return -1;
+ struct sockaddr_storage ss;
+ socklen_t len = sizeof(ss);
+
+ if (rx->fd < 0)
+ return 0;
+
+ if (getsockname(rx->fd, (struct sockaddr *)&ss, &len) < 0)
+ return -1;
+
+ if (connect(rx->fd, (struct sockaddr *)&ss, len) < 0)
+ return -1;
+
+ /* not necessary but may make debugging clearer */
+ fd_stop_recv(rx->fd);
+ return 1;
+}
+
+/* Resume a receiver. Returns < 0 in case of failure, 0 if the receiver
+ * was totally stopped, or > 0 if correctly suspended.
+ * The principle is to reverse the change above, we'll break the connection by
+ * connecting to AF_UNSPEC. The association breaks and the socket starts to
+ * receive from everywhere again.
+ */
+static int udp_resume_receiver(struct receiver *rx)
+{
+ struct sockaddr sa;
+ socklen_t len = sizeof(sa);
+
+ if (rx->fd < 0)
+ return 0;
+
+ sa.sa_family = AF_UNSPEC;
+ if (connect(rx->fd, &sa, len) < 0)
+ return -1;
+
+ fd_want_recv(rx->fd);
+ return 1;
}
/*