pespin has uploaded this change for review. ( 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, 237 insertions(+), 3 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmo-netif refs/changes/78/40478/1
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..4b20a61 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 keelapive 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 be950ce..b7bc378 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: @@ -628,7 +640,7 @@ }
/*! Set given parameter of stream_srv_link to given value. - * \param[in] cli stream client on which to set parameter. + * \param[in] link stream server link 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. @@ -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 keelapive 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; }; @@ -959,7 +999,10 @@ * \param[in] link Stream Server Link to which we belong * \param[in] fd system file descriptor of the new connection * \param[in] data User data to save in the new Stream Server struct - * \returns Stream Server in case of success; NULL on error */ + * \returns Stream Server in case of success; NULL on error + * + * Note: In case of error, the caller is responsible for closing the fd. + * */ struct osmo_stream_srv * osmo_stream_srv_create2(void *ctx, struct osmo_stream_srv_link *link, int fd, void *data) { @@ -1394,4 +1437,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 keelapive 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; + }; +} + /*! @} */