pespin has submitted this change. (
https://gerrit.osmocom.org/c/libosmo-netif/+/40478?usp=email )
Change subject: stream: Support configuring TCP keep-alive pars
......................................................................
stream: Support configuring TCP keep-alive pars
for servers, the param can either be set on the link (which will be
automatically inherited/applied for all accepted conn sockets), or can
be set specifically on a srv conn socket.
The second is useful when user can supply different parameters for different
clients connected to the listening socket, eg. ASP configuration in
libosmo-sigtran.
Change-Id: Ie748ad581c1c42f4c24f9409ce7d34d419cbca8b
---
M TODO-RELEASE
M include/osmocom/netif/stream.h
M include/osmocom/netif/stream_private.h
M src/stream.c
M src/stream_cli.c
M src/stream_srv.c
6 files changed, 232 insertions(+), 1 deletion(-)
Approvals:
pespin: Looks good to me, approved
Jenkins Builder: Verified
diff --git a/TODO-RELEASE b/TODO-RELEASE
index 0ed7189..e519375 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -7,3 +7,4 @@
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line
+stream add OSMO_STREAM_{CLI,SRV,SRV_LINK}_TCP_SOCKOPT_KEEP*,
osmo_stream_srv_set_param()
diff --git a/include/osmocom/netif/stream.h b/include/osmocom/netif/stream.h
index 075e5e9..7b74e87 100644
--- a/include/osmocom/netif/stream.h
+++ b/include/osmocom/netif/stream.h
@@ -98,6 +98,11 @@
OSMO_STREAM_SRV_LINK_PAR_SCTP_SOCKOPT_ASCONF_SUPPORTED, /* uint8_t: 0 disable, 1 enable,
2 force disable, 3 force enable */
OSMO_STREAM_SRV_LINK_PAR_SCTP_INIT_NUM_OSTREAMS, /* uint16_t: amount of streams */
OSMO_STREAM_SRV_LINK_PAR_SCTP_INIT_MAX_INSTREAMS, /* uint16_t: amount of streams */
+
+ OSMO_STREAM_SRV_LINK_PAR_TCP_SOCKOPT_KEEPALIVE = 256, /* uint8_t: 0 disable, 1 enable
*/
+ OSMO_STREAM_SRV_LINK_PAR_TCP_SOCKOPT_KEEPIDLE, /* int: seconds */
+ OSMO_STREAM_SRV_LINK_PAR_TCP_SOCKOPT_KEEPINTVL, /* int: seconds */
+ OSMO_STREAM_SRV_LINK_PAR_TCP_SOCKOPT_KEEPCNT, /* int: Number of probes */
};
int osmo_stream_srv_link_set_param(struct osmo_stream_srv_link *link, enum
osmo_stream_srv_link_param par,
@@ -149,6 +154,15 @@
void osmo_stream_srv_clear_tx_queue(struct osmo_stream_srv *conn);
+enum osmo_stream_srv_param {
+ OSMO_STREAM_SRV_PAR_TCP_SOCKOPT_KEEPALIVE = 256, /* uint8_t: 0 disable, 1 enable */
+ OSMO_STREAM_SRV_PAR_TCP_SOCKOPT_KEEPIDLE, /* int: seconds */
+ OSMO_STREAM_SRV_PAR_TCP_SOCKOPT_KEEPINTVL, /* int: seconds */
+ OSMO_STREAM_SRV_PAR_TCP_SOCKOPT_KEEPCNT, /* int: Number of probes */
+};
+int osmo_stream_srv_set_param(struct osmo_stream_srv *conn, enum osmo_stream_srv_param
par,
+ void *val, size_t val_len);
+
/*! @} */
/*! \defgroup stream_cli Osmocom Stream Client
@@ -255,6 +269,11 @@
OSMO_STREAM_CLI_PAR_SCTP_INIT_MAX_INSTREAMS, /* uint16_t: amount of streams */
OSMO_STREAM_CLI_PAR_SCTP_INIT_MAX_ATTEMPTS, /* uint16_t: amount of attempts */
OSMO_STREAM_CLI_PAR_SCTP_INIT_TIMEOUT, /* uint16_t: milliseconds */
+
+ OSMO_STREAM_CLI_PAR_TCP_SOCKOPT_KEEPALIVE = 256, /* uint8_t: 0 disable, 1 enable */
+ OSMO_STREAM_CLI_PAR_TCP_SOCKOPT_KEEPIDLE, /* int: seconds */
+ OSMO_STREAM_CLI_PAR_TCP_SOCKOPT_KEEPINTVL, /* int: seconds */
+ OSMO_STREAM_CLI_PAR_TCP_SOCKOPT_KEEPCNT, /* int: Number of probes */
};
int osmo_stream_cli_set_param(struct osmo_stream_cli *cli, enum osmo_stream_cli_param
par,
diff --git a/include/osmocom/netif/stream_private.h
b/include/osmocom/netif/stream_private.h
index dc7506d..7fb0de6 100644
--- a/include/osmocom/netif/stream_private.h
+++ b/include/osmocom/netif/stream_private.h
@@ -29,6 +29,26 @@
OSMO_STREAM_MODE_OSMO_IO,
};
+struct stream_tcp_keepalive_pars {
+ bool enable;
+ bool time_present;
+ bool intvl_present;
+ bool probes_present;
+ int time_value; /* seconds */
+ int intvl_value; /* seconds */
+ int probes_value;
+};
+int stream_setsockopt_tcp_keepalive(int fd, int on);
+int stream_setsockopt_tcp_keepidle(int fd, int keepalive_time);
+int stream_setsockopt_tcp_keepintvl(int fd, int keepalive_intvl);
+int stream_setsockopt_tcp_keepcnt(int fd, int keepalive_probes);
+int stream_tcp_keepalive_pars_apply(int fd, const struct stream_tcp_keepalive_pars
*tkp);
+
+struct stream_tcp_pars {
+ struct stream_tcp_keepalive_pars ka;
+ /* Others like TCP_USER_TIMEOUT will be added here in the future. */
+};
+
struct osmo_io_fd;
struct msghdr;
diff --git a/src/stream.c b/src/stream.c
index f0902a4..a1e077a 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -196,6 +196,78 @@
return rc;
}
+int stream_setsockopt_tcp_keepalive(int fd, int on)
+{
+ int ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
+ if (ret < 0) {
+ ret = errno;
+ LOGP(DLINP, LOGL_ERROR, "Failed to enable TCP keepalive on fd %d: %s\n", fd,
strerror(ret));
+ return -ret;
+ }
+ return 0;
+}
+
+int stream_setsockopt_tcp_keepidle(int fd, int keepalive_time)
+{
+ int ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepalive_time,
sizeof(keepalive_time));
+ if (ret < 0) {
+ ret = errno;
+ LOGP(DLINP, LOGL_ERROR, "Failed to set TCP_KEEPIDLE on fd %d: %s\n", fd,
strerror(ret));
+ return -ret;
+ }
+ return 0;
+}
+
+int stream_setsockopt_tcp_keepintvl(int fd, int keepalive_intvl)
+{
+ int ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepalive_intvl,
sizeof(keepalive_intvl));
+ if (ret < 0) {
+ ret = errno;
+ LOGP(DLINP, LOGL_ERROR, "Failed to set TCP_KEEPINTVL on fd %d: %s\n", fd,
strerror(ret));
+ return -ret;
+ }
+ return 0;
+}
+
+int stream_setsockopt_tcp_keepcnt(int fd, int keepalive_probes)
+{
+ int ret = setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keepalive_probes,
sizeof(keepalive_probes));
+ if (ret < 0) {
+ ret = errno;
+ LOGP(DLINP, LOGL_ERROR, "Failed to set TCP_KEEPCNT on fd %d: %s\n", fd,
strerror(ret));
+ return -ret;
+ }
+ return 0;
+}
+
+int stream_tcp_keepalive_pars_apply(int fd, const struct stream_tcp_keepalive_pars *tkp)
+{
+ int ret;
+
+ if (!tkp->enable)
+ return 0;
+
+ if ((ret = stream_setsockopt_tcp_keepalive(fd, tkp->enable)) < 0)
+ return ret;
+
+ if (tkp->time_present) {
+ if ((ret = stream_setsockopt_tcp_keepidle(fd, tkp->time_value)) < 0)
+ return ret;
+ }
+
+ if (tkp->intvl_present) {
+ if ((ret = stream_setsockopt_tcp_keepintvl(fd, tkp->intvl_value)) < 0)
+ return ret;
+ }
+
+ if (tkp->probes_present) {
+ if ((ret = stream_setsockopt_tcp_keepcnt(fd, tkp->probes_value)) < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
#ifdef HAVE_LIBSCTP
static int stream_sctp_recvmsg_trailer(const char *log_pfx, struct msgb *msg, int ret,
const struct sctp_sndrcvinfo *sinfo, int flags)
{
diff --git a/src/stream_cli.c b/src/stream_cli.c
index 028ab07..1a2a768 100644
--- a/src/stream_cli.c
+++ b/src/stream_cli.c
@@ -122,6 +122,7 @@
int flags;
int reconnect_timeout;
struct osmo_sock_init2_multiaddr_pars ma_pars;
+ struct stream_tcp_pars tcp_pars;
uint8_t in_cb_mask; /* IN_CB_MASK_* */
bool delay_free;
};
@@ -1286,6 +1287,12 @@
goto error_close_socket;
}
+ if (cli->proto == IPPROTO_TCP) {
+ ret = stream_tcp_keepalive_pars_apply(fd, &cli->tcp_pars.ka);
+ if (ret < 0)
+ goto error_close_socket;
+ }
+
switch (cli->mode) {
case OSMO_STREAM_MODE_OSMO_FD:
osmo_fd_setup(&cli->ofd, fd, OSMO_FD_READ | OSMO_FD_WRITE,
osmo_stream_cli_fd_cb, cli, 0);
@@ -1532,6 +1539,38 @@
cli->ma_pars.sctp.sockopt_initmsg.max_init_timeo_present = true;
cli->ma_pars.sctp.sockopt_initmsg.max_init_timeo_value = *(uint16_t *)val;
break;
+ /* TCP keepalive params: */
+ case OSMO_STREAM_CLI_PAR_TCP_SOCKOPT_KEEPALIVE:
+ if (!val || val_len != sizeof(uint8_t))
+ return -EINVAL;
+ cli->tcp_pars.ka.enable = !!*(uint8_t *)val;
+ if (stream_cli_is_opened(cli))
+ return stream_setsockopt_tcp_keepalive(osmo_stream_cli_get_fd(cli),
cli->tcp_pars.ka.enable);
+ break;
+ case OSMO_STREAM_CLI_PAR_TCP_SOCKOPT_KEEPIDLE:
+ if (!val || val_len != sizeof(int))
+ return -EINVAL;
+ cli->tcp_pars.ka.time_present = true;
+ cli->tcp_pars.ka.time_value = *(int *)val;
+ if (stream_cli_is_opened(cli))
+ return stream_setsockopt_tcp_keepidle(osmo_stream_cli_get_fd(cli),
cli->tcp_pars.ka.time_value);
+ break;
+ case OSMO_STREAM_CLI_PAR_TCP_SOCKOPT_KEEPINTVL:
+ if (!val || val_len != sizeof(int))
+ return -EINVAL;
+ cli->tcp_pars.ka.intvl_present = true;
+ cli->tcp_pars.ka.intvl_value = *(int *)val;
+ if (stream_cli_is_opened(cli))
+ return stream_setsockopt_tcp_keepintvl(osmo_stream_cli_get_fd(cli),
cli->tcp_pars.ka.intvl_value);
+ break;
+ case OSMO_STREAM_CLI_PAR_TCP_SOCKOPT_KEEPCNT:
+ if (!val || val_len != sizeof(int))
+ return -EINVAL;
+ cli->tcp_pars.ka.probes_present = true;
+ cli->tcp_pars.ka.probes_value = *(int *)val;
+ if (stream_cli_is_opened(cli))
+ return stream_setsockopt_tcp_keepcnt(osmo_stream_cli_get_fd(cli),
cli->tcp_pars.ka.probes_value);
+ break;
default:
return -ENOENT;
};
diff --git a/src/stream_srv.c b/src/stream_srv.c
index 5ffb28b..e80d5e4 100644
--- a/src/stream_srv.c
+++ b/src/stream_srv.c
@@ -99,6 +99,7 @@
unsigned int tx_queue_max_length; /* Max amount of msgbs which can be enqueued */
struct msgb_alloc_info msgb_alloc;
struct osmo_sock_init2_multiaddr_pars ma_pars;
+ struct stream_tcp_pars tcp_pars;
};
static int _setsockopt_nosigpipe(struct osmo_stream_srv_link *link, int new_fd)
@@ -142,11 +143,22 @@
LOGSLNK(link, LOGL_INFO, "accept()ed new link from %s\n",
osmo_sockaddr_to_str(&osa));
- if (link->proto == IPPROTO_SCTP) {
+ switch (link->proto) {
+ case IPPROTO_TCP:
+ ret = stream_tcp_keepalive_pars_apply(sock_fd, &link->tcp_pars.ka);
+ if (ret < 0) {
+ LOGSLNK(link, LOGL_ERROR, "failed applying TCP keep-alive pars on fd %d\n",
sock_fd);
+ goto error_close_socket;
+ }
+ break;
+ case IPPROTO_SCTP:
_setsockopt_nosigpipe(link, sock_fd);
ret = stream_sctp_sock_activate_events(sock_fd);
if (ret < 0)
goto error_close_socket;
+ break;
+ default:
+ break;
}
break;
default:
@@ -670,6 +682,34 @@
link->ma_pars.sctp.sockopt_initmsg.max_instreams_present = true;
link->ma_pars.sctp.sockopt_initmsg.max_instreams_value = *(uint16_t *)val;
break;
+ /* TCP keepalive params: */
+ case OSMO_STREAM_SRV_LINK_PAR_TCP_SOCKOPT_KEEPALIVE:
+ if (!val || val_len != sizeof(uint8_t))
+ return -EINVAL;
+ link->tcp_pars.ka.enable = !!*(uint8_t *)val;
+ /* Will be applied on accepted sockets */
+ break;
+ case OSMO_STREAM_SRV_LINK_PAR_TCP_SOCKOPT_KEEPIDLE:
+ if (!val || val_len != sizeof(int))
+ return -EINVAL;
+ link->tcp_pars.ka.time_present = true;
+ link->tcp_pars.ka.time_value = *(int *)val;
+ /* Will be applied on accepted sockets */
+ break;
+ case OSMO_STREAM_SRV_LINK_PAR_TCP_SOCKOPT_KEEPINTVL:
+ if (!val || val_len != sizeof(int))
+ return -EINVAL;
+ link->tcp_pars.ka.intvl_present = true;
+ link->tcp_pars.ka.intvl_value = *(int *)val;
+ /* Will be applied on accepted sockets */
+ break;
+ case OSMO_STREAM_SRV_LINK_PAR_TCP_SOCKOPT_KEEPCNT:
+ if (!val || val_len != sizeof(int))
+ return -EINVAL;
+ link->tcp_pars.ka.probes_present = true;
+ link->tcp_pars.ka.probes_value = *(int *)val;
+ /* Will be applied on accepted sockets */
+ break;
default:
return -ENOENT;
};
@@ -1400,4 +1440,44 @@
osmo_stream_srv_destroy(conn);
}
+/*! Set given parameter of stream_srv to given value.
+ * \param[in] conn stream server conn on which to set parameter.
+ * \param[in] par identifier of the parameter to be set.
+ * \param[in] val value of the parameter to be set.
+ * \param[in] val_len length of the parameter value.
+ * \returns 0 in success; negative -errno on error. */
+int osmo_stream_srv_set_param(struct osmo_stream_srv *conn, enum osmo_stream_srv_param
par,
+ void *val, size_t val_len)
+{
+ uint8_t on;
+ int i;
+ OSMO_ASSERT(conn);
+
+ switch (par) {
+ /* TCP keepalive params: */
+ case OSMO_STREAM_SRV_PAR_TCP_SOCKOPT_KEEPALIVE:
+ if (!val || val_len != sizeof(uint8_t))
+ return -EINVAL;
+ on = !!*(uint8_t *)val;
+ return stream_setsockopt_tcp_keepalive(osmo_stream_srv_get_fd(conn), on);
+ case OSMO_STREAM_SRV_PAR_TCP_SOCKOPT_KEEPIDLE:
+ if (!val || val_len != sizeof(int))
+ return -EINVAL;
+ i = *(int *)val;
+ return stream_setsockopt_tcp_keepidle(osmo_stream_srv_get_fd(conn), i);
+ case OSMO_STREAM_SRV_PAR_TCP_SOCKOPT_KEEPINTVL:
+ if (!val || val_len != sizeof(int))
+ return -EINVAL;
+ i = *(int *)val;
+ return stream_setsockopt_tcp_keepintvl(osmo_stream_srv_get_fd(conn), i);
+ case OSMO_STREAM_SRV_PAR_TCP_SOCKOPT_KEEPCNT:
+ if (!val || val_len != sizeof(int))
+ return -EINVAL;
+ i = *(int *)val;
+ return stream_setsockopt_tcp_keepcnt(osmo_stream_srv_get_fd(conn), i);
+ default:
+ return -ENOENT;
+ };
+}
+
/*! @} */
--
To view, visit
https://gerrit.osmocom.org/c/libosmo-netif/+/40478?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: libosmo-netif
Gerrit-Branch: master
Gerrit-Change-Id: Ie748ad581c1c42f4c24f9409ce7d34d419cbca8b
Gerrit-Change-Number: 40478
Gerrit-PatchSet: 3
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <dwillmann(a)sysmocom.de>
Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>