From 26a6b175937fe3b0fece6fb98f623a51c7c905d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 6 Feb 2020 19:40:20 +0200 Subject: [PATCH] sctp: Take some socket configurations from Firefox's datachannel code - Do not send ABORTs for unexpected packets are as response to INIT - Enable interleaving of messages of different streams - Configure 1MB send and receive buffer for the socket - Enable SCTP_SEND_FAILED_EVENT and SCTP_PARTIAL_DELIVERY_EVENT events - Set SCTP_REUSE_PORT configuration - Set SCTP_EXPLICIT_EOR and the corresponding send flag. We probably want to split packets to a maximum size later and only set the flag on the last packet. Firefox uses 0x4000 as maximum size here. - Enable SCTP_ENABLE_CHANGE_ASSOC_REQ - Disable PMTUD and set an maximum initial MTU of 1200 --- ext/sctp/sctpassociation.c | 86 +++++++++++++++++++++++++++++++++++--- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/ext/sctp/sctpassociation.c b/ext/sctp/sctpassociation.c index e6e197a2d0..86f1e660ef 100644 --- a/ext/sctp/sctpassociation.c +++ b/ext/sctp/sctpassociation.c @@ -183,6 +183,16 @@ gst_sctp_association_init (GstSctpAssociation * self) /* Explicit Congestion Notification */ usrsctp_sysctl_set_sctp_ecn_enable (0); + /* Do not send ABORTs in response to INITs (1). + * Do not send ABORTs for received Out of the Blue packets (2). + */ + usrsctp_sysctl_set_sctp_blackhole (2); + + /* Enable interleaving messages for different streams (incoming) + * See: https://tools.ietf.org/html/rfc6458#section-8.1.20 + */ + usrsctp_sysctl_set_sctp_default_frag_interleave (2); + usrsctp_sysctl_set_sctp_nr_outgoing_streams_default (DEFAULT_NUMBER_OF_SCTP_STREAMS); } @@ -449,11 +459,15 @@ gst_sctp_association_send_data (GstSctpAssociation * self, const guint8 * buf, remote_addr = get_sctp_socket_address (self, self->remote_port); g_mutex_unlock (&self->association_mutex); + /* TODO: We probably want to split too large chunks into multiple packets + * and only set the SCTP_EOR flag on the last one. Firefox is using 0x4000 + * as the maximum packet size + */ memset (&spa, 0, sizeof (spa)); spa.sendv_sndinfo.snd_ppid = g_htonl (ppid); spa.sendv_sndinfo.snd_sid = stream_id; - spa.sendv_sndinfo.snd_flags = ordered ? 0 : SCTP_UNORDERED; + spa.sendv_sndinfo.snd_flags = SCTP_EOR | (ordered ? 0 : SCTP_UNORDERED); spa.sendv_sndinfo.snd_context = 0; spa.sendv_sndinfo.snd_assoc_id = 0; spa.sendv_flags = SCTP_SEND_SNDINFO_VALID; @@ -532,15 +546,17 @@ create_sctp_socket (GstSctpAssociation * self) struct linger l; struct sctp_event event; struct sctp_assoc_value stream_reset; + int buf_size = 1024 * 1024; int value = 1; guint16 event_types[] = { SCTP_ASSOC_CHANGE, SCTP_PEER_ADDR_CHANGE, SCTP_REMOTE_ERROR, SCTP_SEND_FAILED, + SCTP_SEND_FAILED_EVENT, SCTP_SHUTDOWN_EVENT, SCTP_ADAPTATION_INDICATION, - /*SCTP_PARTIAL_DELIVERY_EVENT, */ + SCTP_PARTIAL_DELIVERY_EVENT, /*SCTP_AUTHENTICATION_EVENT, */ SCTP_STREAM_RESET_EVENT, /*SCTP_SENDER_DRY_EVENT, */ @@ -559,6 +575,19 @@ create_sctp_socket (GstSctpAssociation * self) goto error; } + if (usrsctp_setsockopt (sock, SOL_SOCKET, SO_RCVBUF, + (const void *) &buf_size, sizeof (buf_size)) < 0) { + GST_ERROR_OBJECT (self, "Could not change receive buffer size: (%u) %s", + errno, g_strerror (errno)); + goto error; + } + if (usrsctp_setsockopt (sock, SOL_SOCKET, SO_SNDBUF, + (const void *) &buf_size, sizeof (buf_size)) < 0) { + GST_ERROR_OBJECT (self, "Could not change send buffer size: (%u) %s", + errno, g_strerror (errno)); + goto error; + } + /* Properly return errors */ if (usrsctp_set_non_blocking (sock, 1) < 0) { GST_ERROR_OBJECT (self, @@ -577,19 +606,34 @@ create_sctp_socket (GstSctpAssociation * self) goto error; } + if (usrsctp_setsockopt (sock, IPPROTO_SCTP, SCTP_REUSE_PORT, &value, + sizeof (int))) { + GST_DEBUG_OBJECT (self, "Could not set SCTP_REUSE_PORT: (%u) %s", errno, + g_strerror (errno)); + } + if (usrsctp_setsockopt (sock, IPPROTO_SCTP, SCTP_NODELAY, &value, sizeof (int))) { - GST_ERROR_OBJECT (self, "Could not set SCTP_NODELAY: (%u) %s", errno, + GST_DEBUG_OBJECT (self, "Could not set SCTP_NODELAY: (%u) %s", errno, + g_strerror (errno)); + goto error; + } + + if (usrsctp_setsockopt (sock, IPPROTO_SCTP, SCTP_EXPLICIT_EOR, &value, + sizeof (int))) { + GST_ERROR_OBJECT (self, "Could not set SCTP_EXPLICIT_EOR: (%u) %s", errno, g_strerror (errno)); goto error; } memset (&stream_reset, 0, sizeof (stream_reset)); stream_reset.assoc_id = SCTP_ALL_ASSOC; - stream_reset.assoc_value = 1; + stream_reset.assoc_value = + SCTP_ENABLE_RESET_STREAM_REQ | SCTP_ENABLE_CHANGE_ASSOC_REQ; if (usrsctp_setsockopt (sock, IPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET, &stream_reset, sizeof (stream_reset))) { - GST_ERROR_OBJECT (self, "Could not set SCTP_ENABLE_STREAM_RESET: (%u) %s", + GST_ERROR_OBJECT (self, + "Could not set SCTP_ENABLE_STREAM_RESET | SCTP_ENABLE_CHANGE_ASSOC_REQ: (%u) %s", errno, g_strerror (errno)); goto error; } @@ -634,6 +678,8 @@ static gboolean client_role_connect (GstSctpAssociation * self) { struct sockaddr_conn local_addr, remote_addr; + struct sctp_paddrparams paddrparams; + socklen_t opt_len; gint ret; g_mutex_lock (&self->association_mutex); @@ -658,6 +704,36 @@ client_role_connect (GstSctpAssociation * self) g_strerror (errno)); goto error; } + + memset (&paddrparams, 0, sizeof (struct sctp_paddrparams)); + memcpy (&paddrparams.spp_address, &remote_addr, + sizeof (struct sockaddr_conn)); + opt_len = (socklen_t) sizeof (struct sctp_paddrparams); + ret = + usrsctp_getsockopt (self->sctp_ass_sock, IPPROTO_SCTP, + SCTP_PEER_ADDR_PARAMS, &paddrparams, &opt_len); + if (ret < 0) { + GST_WARNING_OBJECT (self, + "usrsctp_getsockopt(SCTP_PEER_ADDR_PARAMS) error: (%u) %s", errno, + g_strerror (errno)); + } else { + /* draft-ietf-rtcweb-data-channel-13 section 5: max initial MTU IPV4 1200, IPV6 1280 */ + paddrparams.spp_pathmtu = 1200; + paddrparams.spp_flags &= ~SPP_PMTUD_ENABLE; + paddrparams.spp_flags |= SPP_PMTUD_DISABLE; + opt_len = (socklen_t) sizeof (struct sctp_paddrparams); + ret = usrsctp_setsockopt (self->sctp_ass_sock, IPPROTO_SCTP, + SCTP_PEER_ADDR_PARAMS, &paddrparams, opt_len); + if (ret < 0) { + GST_WARNING_OBJECT (self, + "usrsctp_setsockopt(SCTP_PEER_ADDR_PARAMS) error: (%u) %s", errno, + g_strerror (errno)); + } else { + GST_DEBUG_OBJECT (self, "PMTUD disabled, MTU set to %u", + paddrparams.spp_pathmtu); + } + } + return TRUE; error: return FALSE;