m3ua: Make connection non-blocking to not block other flows
Currently the connect is blocking leading to one bad connection
(e.g. to a black hole) blocking all other connections leading to
bursty traffic.
Change-Id: Idfca8acbce09176055da3e577566386e07d7a348
diff --git a/src/sctp_m3ua_client.c b/src/sctp_m3ua_client.c
index bf9204a..fc54ab6 100644
--- a/src/sctp_m3ua_client.c
+++ b/src/sctp_m3ua_client.c
@@ -1,5 +1,5 @@
/* Run M3UA over SCTP here */
-/* (C) 2015 by Holger Hans Peter Freyther
+/* (C) 2015-2017 by Holger Hans Peter Freyther
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -31,6 +31,8 @@
#include <unistd.h>
#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
#define SCTP_PPID_M3UA 3
@@ -48,6 +50,27 @@
static void m3ua_send_aspup(struct mtp_m3ua_client_link *link);
static void m3ua_send_aspac(struct mtp_m3ua_client_link *link);
+// TODO: Share with msc_conn.c:setnonblocking
+static int setnonblocking_fd(int fd)
+{
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
+ perror("fcntl get failed");
+ return -1;
+ }
+
+ flags |= O_NONBLOCK;
+ flags = fcntl(fd, F_SETFL, flags);
+ if (flags < 0) {
+ perror("fcntl get failed");
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* boilerplate
*/
@@ -190,12 +213,56 @@
return 0;
}
+static void m3ua_connected(struct mtp_m3ua_client_link *link)
+{
+ link->aspac_ack_timer.data = link;
+ link->aspac_ack_timer.cb = aspac_ack_timeout;
+ osmo_timer_schedule(&link->aspac_ack_timer, link->aspac_ack_timeout, 0);
+ m3ua_send_aspup(link);
+}
+
+static int sctp_m3ua_connected(struct osmo_fd *fd, unsigned int what)
+{
+ struct mtp_m3ua_client_link *link = fd->data;
+ int val, rc;
+ socklen_t len = sizeof(val);
+
+ if ((what & BSC_FD_WRITE) == 0)
+ return -1;
+
+ /* check the socket state */
+ rc = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &val, &len);
+ if (rc != 0) {
+ LOGP(DINP, LOGL_ERROR, "getsockopt for the SCTP socket failed.\n");
+ goto error;
+ }
+ if (val != 0) {
+ LOGP(DINP, LOGL_ERROR, "Not connected to the STP.\n");
+ goto error;
+ }
+
+ /* go to full operation */
+ fd->cb = osmo_wqueue_bfd_cb;
+ fd->when = BSC_FD_READ;
+ if (!llist_empty(&link->queue.msg_queue))
+ fd->when |= BSC_FD_WRITE;
+
+ LOGP(DINP, LOGL_NOTICE, "SCTP M3UA is now connected.\n");
+ m3ua_connected(link);
+ return 0;
+
+error:
+ fail_link(link);
+ return -1;
+}
+
static void m3ua_start(void *data)
{
int sctp, ret;
struct sockaddr_in loc_addr, rem_addr;
struct mtp_m3ua_client_link *link = data;
struct sctp_event_subscribe events;
+ bool is_connected = false;
sctp = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP);
if (!sctp) {
@@ -203,6 +270,12 @@
return fail_link(link);
}
+ if (setnonblocking_fd(sctp) != 0) {
+ LOGP(DINP, LOGL_ERROR, "Failed to set nonblocking\n");
+ close(sctp);
+ return fail_link(link);
+ }
+
memset(&events, 0, sizeof(events));
events.sctp_data_io_event = 1;
ret = setsockopt(sctp, SOL_SCTP, SCTP_EVENTS, &events, sizeof(events));
@@ -222,18 +295,28 @@
rem_addr = link->remote;
rem_addr.sin_family = AF_INET;
- if (connect(sctp, (struct sockaddr *) &rem_addr, sizeof(rem_addr)) != 0) {
+ ret = connect(sctp, (struct sockaddr *) &rem_addr, sizeof(rem_addr));
+
+ /* common code */
+ link->queue.bfd.fd = sctp;
+ link->queue.bfd.data = link;
+ link->queue.read_cb = m3ua_conn_read;
+ link->queue.write_cb = m3ua_conn_write;
+
+ if (ret == -1 && errno == EINPROGRESS) {
+ LOGP(DINP, LOGL_NOTICE, "SCTP M3UA async connect in progrss.\n");
+ link->queue.bfd.when = BSC_FD_WRITE;
+ link->queue.bfd.cb = sctp_m3ua_connected;
+ } else if (ret != 0) {
LOGP(DINP, LOGL_ERROR, "Failed to connect\n");
close(sctp);
return fail_link(link);
+ } else {
+ link->queue.bfd.when = BSC_FD_READ;
+ link->queue.bfd.cb = osmo_wqueue_bfd_cb;
+ is_connected = true;
}
- link->queue.bfd.fd = sctp;
- link->queue.bfd.data = link;
- link->queue.bfd.when = BSC_FD_READ;
- link->queue.read_cb = m3ua_conn_read;
- link->queue.write_cb = m3ua_conn_write;
-
if (osmo_fd_register(&link->queue.bfd) != 0) {
LOGP(DINP, LOGL_ERROR, "Failed to register fd\n");
close(sctp);
@@ -241,10 +324,8 @@
}
/* begin the messages for bring-up */
- link->aspac_ack_timer.data = link;
- link->aspac_ack_timer.cb = aspac_ack_timeout;
- osmo_timer_schedule(&link->aspac_ack_timer, link->aspac_ack_timeout, 0);
- m3ua_send_aspup(link);
+ if (is_connected)
+ return m3ua_connected(link);
}
static int m3ua_write(struct mtp_link *mtp_link, struct msgb *msg)