diff options
author | Pau Espin Pedrol <pespin@sysmocom.de> | 2023-08-08 19:32:56 +0200 |
---|---|---|
committer | Pau Espin Pedrol <pespin@sysmocom.de> | 2023-08-21 10:28:11 +0200 |
commit | e83227f735e214d269e6d009436601e812cba59a (patch) | |
tree | 262f9ae505c1c8bbc9605cfd7bc12cba84fc0e37 | |
parent | aea78a248324e2e52218132ba25377ac437d7cc9 (diff) |
socket: Add osmo_sock_init flag to enable SCTP ASCONF features
The SCTP_ASCONF_SUPPORTED sockopt, which enables ASCONF features
for the SCTP associations managed by the SCTP socket, gets its default
value from to sysctl "net.sctp.addip_enable", which, at least up to
current kernel 6.4.8 is set to "0" (disabled) by default.
As a result, ASCONF related features such as
setsockopt(SCTP_SET_PEER_PRIMARY_ADDR) fail with -EPERM
since ASNCONF extensions where not negotiated during the SCTP init
handshake.
This commit adds support for setting SCTP_ASCONF_SUPPORTED=1 during
socket creation, since that needs to be applied before the first
bind()/connect() (before first assoc is created).
Furthermore, for ASCONF features to work properly, the assoc also needs
to announce/use the AUTH extension, as per RFC5061 section 4.2.7.
Otherwise, the peer receiving an SCTP INIT with
ExtensionFeatures=ASCONF,ASCONF_ACK but without AUTH, will reject the
assoc with an ABORT since it's not complying with spec (this behavior
can be tweaked through sysctl "net.sctp.addip_noauth_enable").
The AUTH extension is enabled through sockopt SCTP_AUTH_SUPPORTED, and
its default value is that of sysctl "net.sctp.auth_enable", which is
also disabled (0) by default.
Kernel support for those is relatively new:
SCTP_FUTURE_ASSOC added in: 80df2704a375bb4b3c9c5cce9c00052361b16d61
Follows: v5.0-rc4
Precedes: v5.1-rc1
SCTP_ASCONF_SUPPORTED added in: df2c71ffdfae58961981d7cbcccea93688fc4e96
Follows: v5.3-rc5
Precedes: v5.4-rc1
SCTP_AUTH_SUPPORTED added in: 56dd525abd56f7acd7b44a52935726e3ada4916c
Follows: v5.3-rc5
Precedes: v5.4-rc1
Related: OS#6076
Change-Id: Iac07031927b66a9d32d2bb2faab817e4c922a359
-rw-r--r-- | include/osmocom/core/socket.h | 2 | ||||
-rw-r--r-- | src/core/socket.c | 53 |
2 files changed, 55 insertions, 0 deletions
diff --git a/include/osmocom/core/socket.h b/include/osmocom/core/socket.h index 6bac87d5..c68bb6f0 100644 --- a/include/osmocom/core/socket.h +++ b/include/osmocom/core/socket.h @@ -94,6 +94,8 @@ char *osmo_sockaddr_to_str_c(void *ctx, const struct osmo_sockaddr *sockaddr); #define OSMO_SOCK_F_NO_MCAST_ALL (1 << 4) /*! use SO_REUSEADDR on UDP ports (required for multicast) */ #define OSMO_SOCK_F_UDP_REUSEADDR (1 << 5) +/*! use SCTP_AUTH_SUPPORTED + SCTP_ASCONF_SUPPORTED on SCTP socket (required for ASCONF / Peer Primary Address feature) */ +#define OSMO_SOCK_F_SCTP_ASCONF_SUPPORTED (1 << 6) /*! use OSMO_SOCK_F_DSCP(x) to set IP DSCP 'x' for packets transmitted on the socket */ #define OSMO_SOCK_F_DSCP(x) (((x)&0x3f) << 24) diff --git a/src/core/socket.c b/src/core/socket.c index 5a31c8f4..02d05e2a 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -696,6 +696,36 @@ static int addrinfo_to_sockaddr(uint16_t family, const struct addrinfo **result, return 0; } +static int setsockopt_sctp_auth_supported(int fd) +{ +#ifdef SCTP_AUTH_SUPPORTED + struct sctp_assoc_value assoc_val = { + .assoc_id = SCTP_FUTURE_ASSOC, + .assoc_value = 1, + }; + return setsockopt(fd, IPPROTO_SCTP, SCTP_AUTH_SUPPORTED, &assoc_val, sizeof(assoc_val)); +#else +#pragma message "setsockopt(SCTP_AUTH_SUPPORTED) not supported! some SCTP features may not be available!" + LOGP(DLGLOBAL, LOGL_NOTICE, "Built without support for setsockopt(SCTP_AUTH_SUPPORTED), skipping\n"); + return 0; +#endif +} + +static int setsockopt_sctp_asconf_supported(int fd) +{ +#ifdef SCTP_ASCONF_SUPPORTED + struct sctp_assoc_value assoc_val = { + .assoc_id = SCTP_FUTURE_ASSOC, + .assoc_value = 1, + }; + return setsockopt(fd, IPPROTO_SCTP, SCTP_ASCONF_SUPPORTED, &assoc_val, sizeof(assoc_val)); +#else +#pragma message "setsockopt(SCTP_ASCONF_SUPPORTED) not supported! some SCTP features may not be available!" + LOGP(DLGLOBAL, LOGL_NOTICE, "Built without support for setsockopt(SCTP_ASCONF_SUPPORTED), skipping\n"); + return 0; +#endif +} + /*! Initialize a socket (including bind and/or connect) with multiple local or remote addresses. * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM @@ -811,6 +841,29 @@ int osmo_sock_init2_multiaddr(uint16_t family, uint16_t type, uint8_t proto, goto ret_close; } + if (flags & OSMO_SOCK_F_SCTP_ASCONF_SUPPORTED) { + /* RFC 5061 4.2.7: ASCONF also requires AUTH feature. */ + rc = setsockopt_sctp_auth_supported(sfd); + if (rc < 0) { + int err = errno; + multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt); + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot setsockopt(SCTP_AUTH_SUPPORTED) socket: %s:%u: %s\n", + strbuf, local_port, strerror(err)); + goto ret_close; + } + + rc = setsockopt_sctp_asconf_supported(sfd); + if (rc < 0) { + int err = errno; + multiaddr_snprintf(strbuf, sizeof(strbuf), local_hosts, local_hosts_cnt); + LOGP(DLGLOBAL, LOGL_ERROR, + "cannot setsockopt(SCTP_ASCONF_SUPPORTED) socket: %s:%u: %s\n", + strbuf, local_port, strerror(err)); + goto ret_close; + } + } + /* Build array of addresses taking first entry for each host. TODO: Ideally we should use backtracking storing last used indexes and trying next combination if connect() fails .*/ |