diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index 327ad6809..e29ae3248 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -180,7 +180,7 @@ int parse_args(FileTransmitConfig &cfg, int argc, char** argv) return 2; } - cfg.chunk_size = stoul(Option(params, "1456", o_chunk)); + cfg.chunk_size = stoul(Option(params, "0", o_chunk)); cfg.skip_flushing = Option(params, false, o_no_flush); cfg.bw_report = stoi(Option(params, "0", o_bwreport)); cfg.stats_report = stoi(Option(params, "0", o_statsrep)); @@ -681,8 +681,11 @@ int main(int argc, char** argv) // // Set global config variables // - if (cfg.chunk_size != SRT_LIVE_MAX_PLSIZE) + if (cfg.chunk_size != 0) transmit_chunk_size = cfg.chunk_size; + else + transmit_chunk_size = SRT_MAX_PLSIZE_AF_INET; + transmit_stats_writer = SrtStatsWriterFactory(cfg.stats_pf); transmit_bw_report = cfg.bw_report; transmit_stats_report = cfg.stats_report; diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index 275295173..9fd7b7bcf 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -44,7 +44,7 @@ bool g_stats_are_printed_to_stdout = false; bool transmit_total_stats = false; unsigned long transmit_bw_report = 0; unsigned long transmit_stats_report = 0; -unsigned long transmit_chunk_size = SRT_LIVE_MAX_PLSIZE; +unsigned long transmit_chunk_size = SRT_MAX_PLSIZE_AF_INET6; class FileSource: public Source { @@ -179,6 +179,40 @@ void SrtCommon::InitParameters(string host, map par) m_adapter = host; } + unsigned int max_payload_size = 0; + + // Try to interpret host and adapter first + sockaddr_any host_sa, adapter_sa; + + if (host != "") + { + host_sa = CreateAddr(host); + if (host_sa.family() == AF_UNSPEC) + Error("Failed to interpret 'host' spec: " + host); + + if (host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; + } + + if (adapter != "" && adapter != host) + { + adapter_sa = CreateAddr(adapter); + + if (adapter_sa.family() == AF_UNSPEC) + Error("Failed to interpret 'adapter' spec: " + adapter); + + if (host_sa.family() != AF_UNSPEC && host_sa.family() != adapter_sa.family()) + { + Error("Both host and adapter specified and they use different IP versions"); + } + + if (max_payload_size == 0 && host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; + } + + if (!max_payload_size) + max_payload_size = SRT_MAX_PLSIZE_AF_INET6; + if (par.count("tsbpd") && false_names.count(par.at("tsbpd"))) { m_tsbpdmode = false; @@ -195,11 +229,17 @@ void SrtCommon::InitParameters(string host, map par) if ((par.count("transtype") == 0 || par["transtype"] != "file") && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { - if (transmit_chunk_size > SRT_LIVE_MAX_PLSIZE) - throw std::runtime_error("Chunk size in live mode exceeds 1456 bytes; this is not supported"); + if (transmit_chunk_size > max_payload_size) + Error(Sprint("Chunk size in live mode exceeds ", max_payload_size, " bytes; this is not supported")); par["payloadsize"] = Sprint(transmit_chunk_size); } + else + { + // set it so without making sure that it was set to "file". + // worst case it will be rejected in settings + m_transtype = SRTT_FILE; + } // Assign the others here. m_options = par; @@ -263,6 +303,21 @@ bool SrtCommon::AcceptNewClient() Error("srt_accept"); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + // we do one client connection at a time, // so close the listener. srt_close(m_bindsock); @@ -431,6 +486,19 @@ void SrtCommon::ConnectClient(string host, int port) Error("srt_connect"); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + stat = ConfigurePost(m_sock); if ( stat == SRT_ERROR ) Error("ConfigurePost"); diff --git a/apps/transmitmedia.hpp b/apps/transmitmedia.hpp index 527f005d9..a4812d815 100644 --- a/apps/transmitmedia.hpp +++ b/apps/transmitmedia.hpp @@ -42,6 +42,7 @@ class SrtCommon string m_mode; string m_adapter; map m_options; // All other options, as provided in the URI + SRT_TRANSTYPE m_transtype = SRTT_LIVE; SRTSOCKET m_sock = SRT_INVALID_SOCK; SRTSOCKET m_bindsock = SRT_INVALID_SOCK; bool IsUsable() { SRT_SOCKSTATUS st = srt_getsockstate(m_sock); return st > SRTS_INIT && st < SRTS_BROKEN; } diff --git a/docs/API/API-functions.md b/docs/API/API-functions.md index 74fbc506f..f30a6b3d5 100644 --- a/docs/API/API-functions.md +++ b/docs/API/API-functions.md @@ -19,6 +19,7 @@ | [srt_bind_acquire](#srt_bind_acquire) | Acquires a given UDP socket instead of creating one | | [srt_getsockstate](#srt_getsockstate) | Gets the current status of the socket | | [srt_getsndbuffer](#srt_getsndbuffer) | Retrieves information about the sender buffer | +| [srt_getmaxpayloadsize](#srt_getmaxpayloadsize) | Retrieves the information about the maximum payload size in a single packet | | [srt_close](#srt_close) | Closes the socket or group and frees all used resources | | | | @@ -28,7 +29,7 @@ |:------------------------------------------------- |:-------------------------------------------------------------------------------------------------------------- | | [srt_listen](#srt_listen) | Sets up the listening state on a socket | | [srt_accept](#srt_accept) | Accepts a connection; creates/returns a new socket or group ID | -| [srt_accept_bond](#srt_accept_bond) | Accepts a connection pending on any sockets passed in the `listeners` array
of `nlisteners` size | +| [srt_accept_bond](#srt_accept_bond) | Accepts a connection pending on any sockets passed in the `listeners` array
of `nlisteners` size | | [srt_listen_callback](#srt_listen_callback) | Installs/executes a callback hook on a socket created to handle the incoming connection
on a listening socket | | [srt_connect](#srt_connect) | Connects a socket or a group to a remote party with a specified address and port | | [srt_connect_bind](#srt_connect_bind) | Same as [`srt_bind`](#srt_bind) then [`srt_connect`](#srt_connect) if called with socket [`u`](#u) | @@ -166,13 +167,14 @@ Since SRT v1.5.0. | [SRT_REJ_VERSION](#SRT_REJ_VERSION) | 1.3.4 | A party did not satisfy the minimum version requirement that had been set up for a connection | | [SRT_REJ_RDVCOOKIE](#SRT_REJ_RDVCOOKIE) | 1.3.4 | Rendezvous cookie collision | | [SRT_REJ_BADSECRET](#SRT_REJ_BADSECRET) | 1.3.4 | Both parties have defined a passphrase for connection and they differ | -| [SRT_REJ_UNSECURE](#SRT_REJ_UNSECURE) | 1.3.4 | Only one connection party has set up a password | +| [SRT_REJ_UNSECURE](#SRT_REJ_UNSECURE) | 1.3.4 | Only one connection party has set up a password | | [SRT_REJ_MESSAGEAPI](#SRT_REJ_MESSAGEAPI) | 1.3.4 | The value for [`SRTO_MESSAGEAPI`](API-socket-options.md#SRTO_MESSAGEAPI) flag is different on both connection parties | | [SRT_REJ_FILTER](#SRT_REJ_FILTER) | 1.3.4 | The [`SRTO_PACKETFILTER`](API-socket-options.md#SRTO_PACKETFILTER) option has been set differently on both connection parties | -| [SRT_REJ_GROUP](#SRT_REJ_GROUP) | 1.4.2 | The group type or some group settings are incompatible for both connection parties | -| [SRT_REJ_TIMEOUT](#SRT_REJ_TIMEOUT) | 1.4.2 | The connection wasn't rejected, but it timed out | -| [SRT_REJ_CRYPTO](#SRT_REJ_CRYPTO) | 1.5.2 | The connection was rejected due to an unsupported or mismatching encryption mode | -| | | | +| [SRT_REJ_GROUP](#SRT_REJ_GROUP) | 1.4.2 | The group type or some group settings are incompatible for both connection parties | +| [SRT_REJ_TIMEOUT](#SRT_REJ_TIMEOUT) | 1.4.2 | The connection wasn't rejected, but it timed out | +| [SRT_REJ_CRYPTO](#SRT_REJ_CRYPTO) | 1.5.2 | The connection was rejected due to an unsupported or mismatching encryption mode | +| [SRT_REJ_CONFIG](#SRT_REJ_CONFIG) | 1.6.0 | The connection was rejected because settings on both parties are in collision and cannot negotiate common values | +| | | | See the full list in [Rejection Reason Codes](./rejection-codes.md). @@ -294,6 +296,7 @@ This means that if you call [`srt_startup`](#srt_startup) multiple times, you ne * [srt_bind_acquire](#srt_bind_acquire) * [srt_getsockstate](#srt_getsockstate) * [srt_getsndbuffer](#srt_getsndbuffer) +* [srt_getmaxpayloadsize](#srt_getmaxpayloadsize) * [srt_close](#srt_close) @@ -541,6 +544,58 @@ This function can be used for diagnostics. It is especially useful when the socket needs to be closed asynchronously. +[:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) + +--- + +### srt_getmaxpayloadsize + +``` +int srt_getmaxpayloadsize(SRTSOCKET u); +``` + +Returns the maximum number of bytes that fit in a single packet. Useful only in +live mode (when `SRTO_TSBPDMODE` is true). The socket must be bound (see +[srt_bind](#srt_bind)) or connected (see [srt_connect](#srt_connect)) +to use this function. Note that in case when the socket is bound to an IPv6 +wildcard address and it is dual-stack (`SRTO_IPV6ONLY` is set to false), this +function returns the correct value only if the socket is connected, otherwise +it will return the value always as if the connection was made from an IPv6 peer +(including when you call it on a listening socket). + +This function is only useful for the application to check if it is able to use +a payload of certain size in the live mode, or after connection, if the application +can send payloads of certain size. This is useful only in assertions, as if the +[`SRTO_PAYLOADSIZE`](API_socket-options.md#SRTO_PAYLOADSIZE) option is to be +set to a non-default value (for which the one returned by this function is the +maximum value), this option should be modified before connection and on both +parties, regarding the settings applied on the socket. + +The returned value is the maximum number of bytes that can be put in a single +packet regarding: + +* The current MTU size (`SRTO_MSS`) +* The IP version (IPv4 or IPv6) +* The `SRTO_CRYPTOMODE` setting (bytes reserved for AEAD authentication tag) +* The `SRTO_PACKETFILTER` setting (bytes reserved for extra field in a FEC control packet) + +With default options this value should be 1456 for IPv4 and 1444 for IPv6. + + +| Returns | | +|:----------------------------- |:------------------------------------------------- | +| The maximum payload size (>0) | If succeeded | +| `SRT_ERROR` | Usage error | +| | | + +| Errors | | +|:--------------------------------------- |:----------------------------------------------- | +| [`SRT_EINVSOCK`](#srt_einvsock) | Socket [`u`](#u) indicates no valid socket ID | +| [`SRT_EUNBOUNDSOCK`](#srt_eunboundsock) | Socket [`u`](#u) is not bound | +| | | + + + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) --- @@ -3071,6 +3126,21 @@ and above is reserved for "predefined codes" (`SRT_REJC_PREDEFINED` value plus adopted HTTP codes). Values above `SRT_REJC_USERDEFINED` are freely defined by the application. +#### SRT_REJ_CRYPTO + +Settings for `SRTO_CRYPTOMODE` on both parties are not compatible with one another. +See [`SRTO_CRYPTOMODE`](API-socket-options.md#SRTO_CRYPTOMODE) for details. + +#### SRT_REJ_CONFIG + +Settings for various transmission parameters that are supposed to be negotiated +during the handshake (in order to agree upon a common value) are under restrictions +that make finding common values for them impossible. Cases include: + +* `SRTO_PAYLOADSIZE`, which is nonzero in live mode, is set to a value that +exceeds the free space in a single packet that results from the value of the +negotiated MSS value + [:arrow_up:   Back to List of Functions & Structures](#srt-api-functions) diff --git a/docs/API/API-socket-options.md b/docs/API/API-socket-options.md index a06f8556d..c632857fe 100644 --- a/docs/API/API-socket-options.md +++ b/docs/API/API-socket-options.md @@ -924,20 +924,86 @@ The default value is 0x010000 (SRT v1.0.0). | OptName | Since | Restrict | Type | Units | Default | Range | Dir | Entity | | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | -| `SRTO_MSS` | | pre-bind | `int32_t` | bytes | 1500 | 76.. | RW | GSD | - -Maximum Segment Size. Used for buffer allocation and rate calculation using -packet counter assuming fully filled packets. Each party can set its own MSS -value independently. During a handshake the parties exchange MSS values, and -the lowest is used. - -*Generally on the internet MSS is 1500 by default. This is the maximum -size of a UDP packet and can be only decreased, unless you have some unusual -dedicated network settings. MSS is not to be confused with the size of the UDP -payload or SRT payload - this size is the size of the IP packet, including the -UDP and SRT headers* - -THe value of `SRTO_MSS` must not exceed `SRTO_UDP_SNDBUF` or `SRTO_UDP_RCVBUF`. +| `SRTO_MSS` | | pre-bind | `int32_t` | bytes | 1500 | 116.. | RW | GSD | + +Maximum Segment Size. This value represents the maximum size of a UDP packet +sent by the system. Therefore the value of `SRTO_MSS` must not exceed the +values of `SRTO_UDP_SNDBUF` or `SRTO_UDP_RCVBUF`. It is used for buffer +allocation and rate calculation using a packet counter that assumes fully filled +packets. + +This value is a sum of: + +* IP header (20 bytes for IPv4, or 32 bytes for IPv6) +* UDP header (8 bytes) +* SRT header (16 bytes) +* remaining space (as the maximum payload size available for a packet) + +For the default 1500 the "remaining space" is effectively 1456 for IPv4 +and 1444 for IPv6. + +Note that the IP version used here is not the domain of the underlying UDP +socket, but the in-transmission IP version. This is effectively IPv4 in the +following cases: + +* when the current socket's binding address is of IPv4 domain +* when the peer's address is an IPv6-mapped-IPv4 address + +The IPv6 transmission case is assumed only if the peer's address is a true IPv6 address +(not IPv4 mapped). It is then not possible to determine the payload size limit +until the connection is established. SRT operations that must allocate any +resources according to this value prior to connecting will assume IPv4 transmission +because this way, in the worst case, they allocate more space than needed. + +This value can be set on both connection parties independently, but after +connection `SRTO_MSS` gets a negotiated value, which is the lesser +of the two. If this effective value is too small for either of the +connection peers, the connection is rejected (or late-rejected on the caller +side). + +This value then controls: + +* The maximum size of the payload in a single UDP packet ("remaining space"). + +* The size of the memory space allocated for a single packet in the sender +and receiver buffers. This value is equal to "SRT header" + "remaining space" +in the IPv4 layout case (1472 bytes per packet for MSS=1500). The reason for it +is that some buffer resources are allocated prior to the connection, so this +value must fit both IPv4 and IPv6 for buffer memory allocation. + +The default value of 1500 corresponds to the standard MTU size for network devices. It +is recommended that this value be set to the maximum MTU size of +the network device that you will use for the connection. + +The recommendations for the value of `SRTO_MSS` differ between file and live modes. + +In live mode a single call to the `srt_send*` function may only send data +that fits in one packet. This size is defined by the `SRTO_PAYLOADSIZE` +option (defult: 1316), and it is also the size of the data in a single UDP +packet. To save memory space, you may want then to set `SRTO_MSS` in live mode to +a value for which the "remaining space" matches the `SRTO_PAYLOADSIZE` value (for +the default value of 1316 this will be 1360 for IPv4 and 1372 for IPv6). For security reasons, +this is not done by default: it may potentially lead to the inability to read an incoming UDP +packet if its size is for some reason bigger than the negotiated MSS, which may in turn lead +to unpredictable behaviour and hard-to-detect errors. You should set such a value only if +the peer is trusted (that is, you can be certain that you will never receive an oversized UDP +packet over the link used for the connection). You should also consider the limitations of +`SRTO_PAYLOADSIZE`. + +In file mode `SRTO_PAYLOADSIZE` has a special value 0 that means no limit +for sending a single packet, and therefore bigger portions of data are +internally split into smaller portions, each one using the maximum available +"remaining space". The best value of `SRTO_MSS` for this case is then equal to +the current network device's MTU size. Setting a greater value is possible +(maximum for the system API is 65535), but it may lead to packet fragmentation +on the system level. This is highly unwanted in SRT because: + +* SRT also performs its own fragmentation, so it would be counter-productive +* It would use more system resources to no advantage +* SRT is unaware of it, so the resulting statistics would be slightly misleading + +System-level packet fragmentation cannot be reliably turned off, +so the safest approach is to avoid it by using appropriate parameters. [Return to list](#list-of-options) @@ -1034,7 +1100,7 @@ Cases when negotiation succeeds: | fec,cols:10 | fec,cols:10,rows:20 | fec,cols:10,rows:20,arq:onreq,layout:even | fec,layout:staircase | fec,cols:10 | fec,cols:10,rows:1,arq:onreq,layout:staircase -In these cases the configuration is rejected with SRT_REJ_FILTER code: +In these cases the configuration is rejected with `SRT_REJ_FILTER` code: | Peer A | Peer B | Error reason |-----------------------|---------------------|-------------------------- @@ -1091,12 +1157,58 @@ encrypted connection, they have to simply set the same passphrase. | -------------------- | ----- | -------- | ---------- | ------- | -------- | ------ | --- | ------ | | `SRTO_PAYLOADSIZE` | 1.3.0 | pre | `int32_t` | bytes | \* | 0.. \* | W | GSD | -Sets the maximum declared size of a single call to sending function in Live -mode. When set to 0, there's no limit for a single sending call. +Sets the mode that determines the limitations on how data is sent, including the maximum +size of payload data sent within a single UDP packet. This option can be only set prior +to connecting, but it can be read also after the connection has been established. + +The default value is 1316 in live mode (which is default) and 0 in file mode (when file +mode is set through the `SRTO_TRANSTYPE` option). + +In file mode (`SRTO_PAYLOADSIZE` = 0) the call to `srt_send*` is not limited to the size +of a single packet. If necessary, the supplied data will be split into multiple pieces, +each fitting into a single UDP packet. Every data payload (except the last one in the +stream or in the message) will use the maximum space available in a UDP packet, +as determined by `SRTO_MSS` and other settings that may influence this size +(such as [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER) and +[`SRTO_CRYPTOMODE`](#SRTO_CRYPTOMODE)). + +Also when this option is set to 0 prior to connecting, then reading this option +from a connected socket will return the maximum size of the payload that fits +in a single packet according to the current connection parameters. + +In live mode (`SRTO_PAYLOADSIZE` > 0) the value defines the maximum size of: + +* a single call to a sending function (`srt_send*`) +* the payload supplied in each data packet + +as well as the minimum size of the buffer used for the `srt_recv*` call. + +This value can be set to a greater value than the default 1316, but the maximum +possible value is limited by the following factors: + +* 1500 bytes is the default MSS (see [`SRTO_MSS`](#SRTO_MSS)), including headers, which occupy: + * 20 bytes for IPv4, or 32 bytes for IPv6 + * 8 bytes for UDP + * 16 bytes for SRT + +This alone gives a limit of 1456 for IPv4 and 1444 for IPv6. This limit may +be further decreased in the following cases: + +* 4 bytes reserved for FEC, if you use the built in FEC packet filter (see [`SRTO_PACKETFILTER`](#SRTO_PACKETFILTER)) +* 16 bytes reserved for the authentication tag, if you use AES GCM (see [`SRTO_CRYPTOMODE`](#SRTO_CRYPTOMODE)) + +**WARNING**: The party setting the options will reject a value that is too +large, but note that not every limitation can be checked prior to connection. +This includes: + +* the MSS value defined by a peer, which may override the MSS set by an agent +* the in-transmission IP version (see [SRTO_MSS](#SRTO_MSS) for details) -For Live mode: Default value is 1316, but can be increased up to 1456. Note that -with the `SRTO_PACKETFILTER` option additional header space is usually required, -which decreases the maximum possible value for `SRTO_PAYLOADSIZE`. +These values also influence the "remaining space" in the packet to be used for +payload. If during the handshake it turns out that this "remaining space" is +less than the value set for `SRTO_PAYLOADSIZE` (including when it remains with +the default value), the connection will be rejected with the `SRT_REJ_SETTINGS` +code. For File mode: Default value is 0 and it's recommended not to be changed. diff --git a/srtcore/api.cpp b/srtcore/api.cpp index bb5dd64fe..4817d6858 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -3262,7 +3262,14 @@ void srt::CUDTUnited::updateMux(CUDTSocket* s, const sockaddr_any& reqaddr, cons m.m_pSndQueue = new CSndQueue; m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer); m.m_pRcvQueue = new CRcvQueue; - m.m_pRcvQueue->init(128, s->core().maxPayloadSize(), m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer); + + // We can't use maxPayloadSize() because this value isn't valid until the connection is established. + // We need to "think big", that is, allocate a size that would fit both IPv4 and IPv6. + const size_t payload_size = s->core().m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(AF_INET); + + HLOGC(smlog.Debug, log << s->core().CONID() << "updateMux: config rcv queue qsize=" << 128 + << " plsize=" << payload_size << " hsize=" << 1024); + m.m_pRcvQueue->init(128, payload_size, m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer); // Rewrite the port here, as it might be only known upon return // from CChannel::open. @@ -4427,6 +4434,47 @@ SRT_SOCKSTATUS srt::CUDT::getsockstate(SRTSOCKET u) } } +int srt::CUDT::getMaxPayloadSize(SRTSOCKET id) +{ + return uglobal().getMaxPayloadSize(id); +} + +int srt::CUDTUnited::getMaxPayloadSize(SRTSOCKET id) +{ + CUDTSocket* s = locateSocket(id); + if (!s) + { + return CUDT::APIError(MJ_NOTSUP, MN_SIDINVAL); + } + + if (s->m_SelfAddr.family() == AF_UNSPEC) + { + return CUDT::APIError(MJ_NOTSUP, MN_ISUNBOUND); + } + + int fam = s->m_SelfAddr.family(); + CUDT& u = s->core(); + + std::string errmsg; + int extra = u.m_config.extraPayloadReserve((errmsg)); + if (extra == -1) + { + LOGP(aclog.Error, errmsg); + return CUDT::APIError(MJ_NOTSUP, MN_INVAL); + } + + // Prefer transfer IP version, if defined. This is defined after + // the connection is established. Note that the call is rejected + // if the socket isn't bound, be it explicitly or implicitly by + // calling srt_connect(). + if (u.m_TransferIPVersion != AF_UNSPEC) + fam = u.m_TransferIPVersion; + + int payload_size = u.m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(fam) - extra; + + return payload_size; +} + //////////////////////////////////////////////////////////////////////////////// namespace UDT diff --git a/srtcore/api.h b/srtcore/api.h index b5d6be915..ef531cfe1 100644 --- a/srtcore/api.h +++ b/srtcore/api.h @@ -428,6 +428,8 @@ class CUDTUnited CUDTSocket* locateSocket_LOCKED(SRTSOCKET u); CUDTSocket* locatePeer(const sockaddr_any& peer, const SRTSOCKET id, int32_t isn); + int getMaxPayloadSize(SRTSOCKET u); + #if ENABLE_BONDING CUDTGroup* locateAcquireGroup(SRTSOCKET u, ErrorHandling erh = ERH_RETURN); CUDTGroup* acquireSocketsGroup(CUDTSocket* s); diff --git a/srtcore/buffer_tools.cpp b/srtcore/buffer_tools.cpp index 7473e3824..4f2c3dcbd 100644 --- a/srtcore/buffer_tools.cpp +++ b/srtcore/buffer_tools.cpp @@ -102,12 +102,12 @@ void AvgBufSize::update(const steady_clock::time_point& now, int pkts, int bytes m_dTimespanMAvg = avg_iir_w<1000, double>(m_dTimespanMAvg, timespan_ms, elapsed_ms); } -CRateEstimator::CRateEstimator(int /*family*/) +CRateEstimator::CRateEstimator(int family) : m_iInRatePktsCount(0) , m_iInRateBytesCount(0) , m_InRatePeriod(INPUTRATE_FAST_START_US) // 0.5 sec (fast start) , m_iInRateBps(INPUTRATE_INITIAL_BYTESPS) - , m_iFullHeaderSize(CPacket::UDP_HDR_SIZE + CPacket::HDR_SIZE) + , m_iFullHeaderSize(CPacket::udpHeaderSize(family) + CPacket::HDR_SIZE) {} void CRateEstimator::setInputRateSmpPeriod(int period) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index eca2b2069..ea303a25b 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -273,6 +273,7 @@ void srt::CUDT::construct() m_pSndQueue = NULL; m_pRcvQueue = NULL; + m_TransferIPVersion = AF_UNSPEC; // Will be set after connection m_pSNode = NULL; m_pRNode = NULL; @@ -904,7 +905,7 @@ string srt::CUDT::getstreamid(SRTSOCKET u) // Initial sequence number, loss, acknowledgement, etc. void srt::CUDT::clearData() { - const size_t full_hdr_size = CPacket::UDP_HDR_SIZE - CPacket::HDR_SIZE; + const size_t full_hdr_size = CPacket::udpHeaderSize(AF_INET) + CPacket::HDR_SIZE; m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; HLOGC(cnlog.Debug, log << CONID() << "clearData: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); @@ -3080,7 +3081,7 @@ bool srt::CUDT::checkApplyFilterConfig(const std::string &confstr) // XXX Using less maximum payload size of IPv4 and IPv6; this is only about the payload size // for live. - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - cfg.extra_size; + size_t efc_max_payload_size = SRT_MAX_PLSIZE_AF_INET6 - cfg.extra_size; if (m_config.zExpPayloadSize > efc_max_payload_size) { LOGC(cnlog.Warn, @@ -4720,15 +4721,33 @@ bool srt::CUDT::applyResponseSettings(const CPacket* pHspkt /*[[nullable]]*/) AT return false; } + m_TransferIPVersion = m_PeerAddr.family(); + if (m_PeerAddr.family() == AF_INET6) + { + // Check if the m_PeerAddr's address is a mapped IPv4. If so, + // define Transfer IP version as 4 because this one will be used. + if (checkMappedIPv4(m_PeerAddr.sin6)) + m_TransferIPVersion = AF_INET; + } + // Re-configure according to the negotiated values. m_config.iMSS = m_ConnRes.m_iMSS; - const size_t full_hdr_size = CPacket::UDP_HDR_SIZE + CPacket::HDR_SIZE; + const size_t full_hdr_size = CPacket::udpHeaderSize(m_TransferIPVersion) + CPacket::HDR_SIZE; m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; - HLOGC(cnlog.Debug, log << CONID() << "applyResponseSettings: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + if (m_iMaxSRTPayloadSize < int(m_config.zExpPayloadSize)) + { + LOGC(cnlog.Error, log << CONID() << "applyResponseSettings: negotiated MSS=" << m_config.iMSS + << " leaves too little payload space " << m_iMaxSRTPayloadSize << " for configured payload size " + << m_config.zExpPayloadSize); + m_RejectReason = SRT_REJ_CONFIG; + return false; + } + HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); + m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize; - const int udpsize = m_config.iMSS - CPacket::UDP_HDR_SIZE; + const int udpsize = m_config.iMSS - CPacket::udpHeaderSize(m_TransferIPVersion); m_iMaxSRTPayloadSize = udpsize - CPacket::HDR_SIZE; m_iPeerISN = m_ConnRes.m_iISN; @@ -5707,16 +5726,31 @@ bool srt::CUDT::prepareBuffers(CUDTException* eout) try { + // XXX SND buffer may allocate more memory, but must set the size of a single + // packet that fits the transmission for the overall connection. For any mixed 4-6 + // connection it should be the less size, that is, for IPv6 + // CryptoControl has to be initialized and in case of RESPONDER the KM REQ must be processed (interpretSrtHandshake(..)) for the crypto mode to be deduced. const int authtag = getAuthTagSize(); SRT_ASSERT(m_iMaxSRTPayloadSize != 0); - - HLOGC(rslog.Debug, log << CONID() << "Creating buffers: snd-plsize=" << m_iMaxSRTPayloadSize - << " snd-bufsize=" << 32 + SRT_ASSERT(m_TransferIPVersion != AF_UNSPEC); + // IMPORTANT: + // The m_iMaxSRTPayloadSize is the size of the payload in the "SRT packet" that can be sent + // over the current connection - which means that if both parties are IPv6, then the maximum size + // is the one for IPv6 (1444). If any party is IPv4, this maximum size is 1456. + // The family as the first argument is something different - it's for the header size in order + // to calculate rate and statistics. + + int snd_payload_size = m_config.iMSS - CPacket::HDR_SIZE - CPacket::udpHeaderSize(m_TransferIPVersion); + SRT_ASSERT(m_iMaxSRTPayloadSize <= snd_payload_size); + + HLOGC(rslog.Debug, log << CONID() << "Creating buffers: snd-plsize=" << snd_payload_size + << " snd-bufsize=" << 32 << " TF-IPv" + << (m_TransferIPVersion == AF_INET6 ? "6" : m_TransferIPVersion == AF_INET ? "4" : "???") << " authtag=" << authtag); - m_pSndBuffer = new CSndBuffer(AF_INET, 32, m_iMaxSRTPayloadSize, authtag); + m_pSndBuffer = new CSndBuffer(m_TransferIPVersion, 32, snd_payload_size, authtag); SRT_ASSERT(m_iPeerISN != -1); m_pRcvBuffer = new srt::CRcvBuffer(m_iPeerISN, m_config.iRcvBufSize, m_pRcvQueue->m_pUnitQueue, m_config.bMessageAPI); // After introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice a space. @@ -5764,8 +5798,16 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& // Uses the smaller MSS between the peers m_config.iMSS = std::min(m_config.iMSS, w_hs.m_iMSS); - const size_t full_hdr_size = CPacket::UDP_HDR_SIZE + CPacket::HDR_SIZE; + const size_t full_hdr_size = CPacket::udpHeaderSize(m_TransferIPVersion) + CPacket::HDR_SIZE; m_iMaxSRTPayloadSize = m_config.iMSS - full_hdr_size; + if (m_iMaxSRTPayloadSize < int(m_config.zExpPayloadSize)) + { + LOGC(cnlog.Error, log << CONID() << "acceptAndRespond: negotiated MSS=" << m_config.iMSS + << " leaves too little payload space " << m_iMaxSRTPayloadSize << " for configured payload size " + << m_config.zExpPayloadSize); + m_RejectReason = SRT_REJ_CONFIG; + throw CUDTException(MJ_SETUP, MN_REJECTED, 0); + } HLOGC(cnlog.Debug, log << CONID() << "acceptAndRespond: PAYLOAD SIZE: " << m_iMaxSRTPayloadSize); @@ -5790,6 +5832,15 @@ void srt::CUDT::acceptAndRespond(const sockaddr_any& agent, const sockaddr_any& rewriteHandshakeData(peer, (w_hs)); + m_TransferIPVersion = peer.family(); + if (peer.family() == AF_INET6) + { + // Check if the peer's address is a mapped IPv4. If so, + // define Transfer IP version as 4 because this one will be used. + if (checkMappedIPv4(peer.sin6)) + m_TransferIPVersion = AF_INET; + } + // Prepare all structures if (!prepareConnectionObjects(w_hs, HSD_DRAW, 0)) @@ -7491,7 +7542,7 @@ void srt::CUDT::bstats(CBytePerfMon *perf, bool clear, bool instantaneous) if (m_bBroken || m_bClosing) throw CUDTException(MJ_CONNECTION, MN_CONNLOST, 0); - const int pktHdrSize = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; + const int pktHdrSize = CPacket::HDR_SIZE + CPacket::udpHeaderSize(m_TransferIPVersion == AF_UNSPEC ? AF_INET : m_TransferIPVersion); { int32_t flight_span = getFlightSpan(); diff --git a/srtcore/core.h b/srtcore/core.h index ed250c641..17e08b0cc 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -254,6 +254,7 @@ class CUDT static int rejectReason(SRTSOCKET s); static int rejectReason(SRTSOCKET s, int value); static int64_t socketStartTime(SRTSOCKET s); + static int getMaxPayloadSize(SRTSOCKET u); public: // internal API // This is public so that it can be used directly in API implementation functions. @@ -1231,6 +1232,7 @@ class CUDT sockaddr_any m_PeerAddr; // peer address sockaddr_any m_SourceAddr; // override UDP source address with this one when sending uint32_t m_piSelfIP[4]; // local UDP IP address + int m_TransferIPVersion; // AF_INET/6 that should be used to determine common payload size CSNode* m_pSNode; // node information for UDT list used in snd queue CRNode* m_pRNode; // node information for UDT list used in rcv queue diff --git a/srtcore/group.cpp b/srtcore/group.cpp index 5601cdeee..ecd9f40dc 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -236,7 +236,7 @@ CUDTGroup::SocketData* CUDTGroup::add(SocketData data) log << "CUDTGroup::add: taking MAX payload size from socket @" << data.ps->m_SocketID << ": " << plsize << " " << (plsize ? "(explicit)" : "(unspecified = fallback to 1456)")); if (plsize == 0) - plsize = SRT_LIVE_MAX_PLSIZE; + plsize = CPacket::srtPayloadSize(data.agent.family()); // It is stated that the payload size // is taken from first, and every next one // will get the same. @@ -507,8 +507,8 @@ void CUDTGroup::deriveSettings(CUDT* u) IM(SRTO_FC, iFlightFlagSize); // Nonstandard - importOption(m_config, SRTO_SNDBUF, u->m_config.iSndBufSize * (u->m_config.iMSS - CPacket::UDP_HDR_SIZE)); - importOption(m_config, SRTO_RCVBUF, u->m_config.iRcvBufSize * (u->m_config.iMSS - CPacket::UDP_HDR_SIZE)); + importOption(m_config, SRTO_SNDBUF, u->m_config.iSndBufSize * (u->m_config.iMSS - CPacket::udpHeaderSize(AF_INET))); + importOption(m_config, SRTO_RCVBUF, u->m_config.iRcvBufSize * (u->m_config.iMSS - CPacket::udpHeaderSize(AF_INET))); IM(SRTO_LINGER, Linger); IM(SRTO_UDP_SNDBUF, iUDPSndBufSize); @@ -640,7 +640,7 @@ static bool getOptDefault(SRT_SOCKOPT optname, void* pw_optval, int& w_optlen) case SRTO_SNDBUF: case SRTO_RCVBUF: - w_optlen = fillValue((pw_optval), w_optlen, CSrtConfig::DEF_BUFFER_SIZE * (CSrtConfig::DEF_MSS - CPacket::UDP_HDR_SIZE)); + w_optlen = fillValue((pw_optval), w_optlen, CSrtConfig::DEF_BUFFER_SIZE * (CSrtConfig::DEF_MSS - CPacket::udpHeaderSize(AF_INET))); break; case SRTO_LINGER: @@ -2513,7 +2513,7 @@ void CUDTGroup::bstatsSocket(CBytePerfMon* perf, bool clear) // links and sending a single packet over these two links could be different. // These stats then don't make much sense in this form, this has to be // redesigned. We use the header size as per IPv4, as it was everywhere. - const int pktHdrSize = CPacket::HDR_SIZE + CPacket::UDP_HDR_SIZE; + const int pktHdrSize = CPacket::HDR_SIZE + CPacket::udpHeaderSize(AF_INET); memset(perf, 0, sizeof *perf); @@ -3632,7 +3632,9 @@ int CUDTGroup::sendBackup(const char* buf, int len, SRT_MSGCTRL& w_mc) } // Only live streaming is supported - if (len > SRT_LIVE_MAX_PLSIZE) + // Also - as the group may use potentially IPv4 and IPv6 connections + // in the same group, use the size that fits both + if (len > SRT_MAX_PLSIZE_AF_INET6) { LOGC(gslog.Error, log << "grp/send(backup): buffer size=" << len << " exceeds maximum allowed in live mode"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); @@ -4067,7 +4069,8 @@ void CUDTGroup::internalKeepalive(SocketData* gli) } } -CUDTGroup::BufferedMessageStorage CUDTGroup::BufferedMessage::storage(SRT_LIVE_MAX_PLSIZE /*, 1000*/); +// Use the bigger size of SRT_MAX_PLSIZE to potentially fit both IPv4/6 +CUDTGroup::BufferedMessageStorage CUDTGroup::BufferedMessage::storage(SRT_MAX_PLSIZE_AF_INET /*, 1000*/); // Forwarder needed due to class definition order int32_t CUDTGroup::generateISN() diff --git a/srtcore/packet.h b/srtcore/packet.h index 5a6d6eb15..c59d660a6 100644 --- a/srtcore/packet.h +++ b/srtcore/packet.h @@ -371,16 +371,24 @@ class CPacket static const size_t HDR_SIZE = sizeof(HEADER_TYPE); // packet header size = SRT_PH_E_SIZE * sizeof(uint32_t) - // Can also be calculated as: sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct udphdr). - static const size_t UDP_HDR_SIZE = 28; // 20 bytes IPv4 + 8 bytes of UDP { u16 sport, dport, len, csum }. - - static const size_t SRT_DATA_HDR_SIZE = UDP_HDR_SIZE + HDR_SIZE; +private: // Do not disclose ingredients to the public + static const size_t UDP_HDR_SIZE = 8; // 8 bytes of UDP { u16 sport, dport, len, csum }. + static const size_t IPv4_HDR_SIZE = 20; // 20 bytes IPv4 + static const size_t IPv6_HDR_SIZE = 32; // 32 bytes IPv6 +public: + static inline size_t udpHeaderSize(int family) + { + return UDP_HDR_SIZE + (family == AF_INET ? IPv4_HDR_SIZE : IPv6_HDR_SIZE); + } + static inline size_t srtPayloadSize(int family) + { + return ETH_MAX_MTU_SIZE - (family == AF_INET ? IPv4_HDR_SIZE : IPv6_HDR_SIZE) - UDP_HDR_SIZE - HDR_SIZE; + } // Maximum transmission unit size. 1500 in case of Ethernet II (RFC 1191). static const size_t ETH_MAX_MTU_SIZE = 1500; // Maximum payload size of an SRT packet. - static const size_t SRT_MAX_PAYLOAD_SIZE = ETH_MAX_MTU_SIZE - SRT_DATA_HDR_SIZE; // Packet interface char* data() { return m_pcData; } diff --git a/srtcore/packetfilter_api.h b/srtcore/packetfilter_api.h index 3bfba7c76..ef0d8867f 100644 --- a/srtcore/packetfilter_api.h +++ b/srtcore/packetfilter_api.h @@ -66,7 +66,7 @@ struct SrtFilterInitializer struct SrtPacket { uint32_t hdr[SRT_PH_E_SIZE]; - char buffer[SRT_LIVE_MAX_PLSIZE]; + char buffer[SRT_MAX_PLSIZE_AF_INET]; // Using this as the bigger one (this for AF_INET6 is smaller) size_t length; SrtPacket(size_t size): length(size) diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 1c067d059..fa4ad48bc 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -57,7 +57,10 @@ namespace srt int RcvBufferSizeOptionToValue(int val, int flightflag, int mss) { // Mimimum recv buffer size is 32 packets - const int mssin_size = mss - CPacket::UDP_HDR_SIZE; + // We take the size per packet as maximum allowed for AF_INET, + // as we don't know which one is used, and this requires more + // space than AF_INET6. + const int mssin_size = mss - CPacket::udpHeaderSize(AF_INET); int bufsize; if (val > mssin_size * CSrtConfig::DEF_MIN_FLIGHT_PKT) @@ -90,9 +93,15 @@ struct CSrtConfigSetter { static void set(CSrtConfig& co, const void* optval, int optlen) { + using namespace srt_logging; const int ival = cast_optval(optval, optlen); - if (ival < int(CPacket::UDP_HDR_SIZE + CHandShake::m_iContentSize)) + const int handshake_size = CHandShake::m_iContentSize + (sizeof(uint32_t) * SRT_HS_E_SIZE); + const int minval = int(CPacket::udpHeaderSize(AF_INET6) + CPacket::HDR_SIZE + handshake_size); + if (ival < minval) + { + LOGC(kmlog.Error, log << "SRTO_MSS: minimum value allowed is " << minval << " = [IPv6][UDP][SRT] headers + minimum SRT handshake"); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } co.iMSS = ival; @@ -130,7 +139,7 @@ struct CSrtConfigSetter if (bs <= 0) throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); - co.iSndBufSize = bs / (co.iMSS - CPacket::UDP_HDR_SIZE); + co.iSndBufSize = bs / co.bytesPerPkt(); } }; @@ -144,6 +153,16 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); co.iRcvBufSize = srt::RcvBufferSizeOptionToValue(val, co.iFlightFlagSize, co.iMSS); + const int mssin_size = co.bytesPerPkt(); + + if (val > mssin_size * co.DEF_MIN_FLIGHT_PKT) + co.iRcvBufSize = val / mssin_size; + else + co.iRcvBufSize = co.DEF_MIN_FLIGHT_PKT; + + // recv buffer MUST not be greater than FC size + if (co.iRcvBufSize > co.iFlightFlagSize) + co.iRcvBufSize = co.iFlightFlagSize; } }; @@ -621,9 +640,13 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - if (val > SRT_LIVE_MAX_PLSIZE) + // We don't know at this point, how bit the payloadsize can be set, + // so we limit it to the biggest value of the two. + // When this payloadsize would be then too big to be used with given MSS and IPv6, + // this problem should be reported appropriately from srt_connect and srt_bind calls. + if (val > SRT_MAX_PLSIZE_AF_INET) { - LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << ", maximum payload per MTU."); + LOGC(aclog.Error, log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_MAX_PLSIZE_AF_INET << ", maximum payload per MTU."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } @@ -826,7 +849,7 @@ struct CSrtConfigSetter throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } - size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; + size_t efc_max_payload_size = SRT_MAX_PLSIZE_AF_INET - fc.extra_size; if (co.zExpPayloadSize > efc_max_payload_size) { LOGC(aclog.Warn, @@ -999,40 +1022,54 @@ int CSrtConfig::set(SRT_SOCKOPT optName, const void* optval, int optlen) return dispatchSet(optName, *this, optval, optlen); } -bool CSrtConfig::payloadSizeFits(size_t val, int /*ip_family*/, std::string& w_errmsg) ATR_NOTHROW +int CSrtConfig::extraPayloadReserve(std::string& w_info) ATR_NOTHROW { + int resv = 0; + if (!this->sPacketFilterConfig.empty()) { // This means that the filter might have been installed before, // and the fix to the maximum payload size was already applied. // This needs to be checked now. SrtFilterConfig fc; - if (!ParseFilterConfig(this->sPacketFilterConfig.str(), fc)) + if (!ParseFilterConfig(this->sPacketFilterConfig.str(), (fc))) { // Break silently. This should not happen - w_errmsg = "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"; - return false; + w_info = "SRTO_PAYLOADSIZE: IPE: failing filter configuration installed"; + return -1; } - const size_t efc_max_payload_size = SRT_LIVE_MAX_PLSIZE - fc.extra_size; - if (size_t(val) > efc_max_payload_size) - { - std::ostringstream log; - log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE << " bytes decreased by " << fc.extra_size - << " required for packet filter header"; - w_errmsg = log.str(); - return false; - } + resv += fc.extra_size; + w_info = "Packet Filter"; + } + + if (this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) + { + resv += HAICRYPT_AUTHTAG_MAX; + if (!w_info.empty()) + w_info += " and "; + w_info += "AES_GCM mode"; } - // Not checking AUTO to allow defaul 1456 bytes. - if ((this->iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM) - && (val > (SRT_LIVE_MAX_PLSIZE - HAICRYPT_AUTHTAG_MAX))) + return resv; +} + +bool CSrtConfig::payloadSizeFits(size_t val, int ip_family, std::string& w_errmsg) ATR_NOTHROW +{ + int resv = extraPayloadReserve((w_errmsg)); + if (resv == -1) + return false; + + size_t valmax = CPacket::srtPayloadSize(ip_family) - resv; + + if (val > valmax) { std::ostringstream log; - log << "SRTO_PAYLOADSIZE: value exceeds " << SRT_LIVE_MAX_PLSIZE - << " bytes decreased by " << HAICRYPT_AUTHTAG_MAX - << " required for AES-GCM."; + log << "SRTO_PAYLOADSIZE: value " << val << "exceeds " << valmax + << " bytes"; + if (!w_errmsg.empty()) + log << " as limited by " << w_errmsg; + w_errmsg = log.str(); return false; } diff --git a/srtcore/socketconfig.h b/srtcore/socketconfig.h index 8a78ea21e..f28460be2 100644 --- a/srtcore/socketconfig.h +++ b/srtcore/socketconfig.h @@ -347,7 +347,9 @@ struct CSrtConfig: CSrtMuxerConfig // This function returns the number of bytes that are allocated // for a single packet in the sender and receiver buffer. - int bytesPerPkt() const { return iMSS - int(CPacket::UDP_HDR_SIZE); } + int bytesPerPkt() const { return iMSS - int(CPacket::udpHeaderSize(AF_INET)); } + + int extraPayloadReserve(std::string& w_errmsg) ATR_NOTHROW; }; template diff --git a/srtcore/srt.h b/srtcore/srt.h index 614a85aea..b261558ae 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -288,9 +288,17 @@ typedef enum SRT_TRANSTYPE // This is for MPEG TS and it's a default SRTO_PAYLOADSIZE for SRTT_LIVE. static const int SRT_LIVE_DEF_PLSIZE = 1316; // = 188*7, recommended for MPEG TS -// This is the maximum payload size for Live mode, should you have a different -// payload type than MPEG TS. -static const int SRT_LIVE_MAX_PLSIZE = 1456; // MTU(1500) - UDP.hdr(28) - SRT.hdr(16) +// DEPRECATED. Use one of these below instead. +SRT_ATR_DEPRECATED_PX static const int SRT_LIVE_MAX_PLSIZE SRT_ATR_DEPRECATED = 1456; // MTU(1500) - UDP.hdr(28) - SRT.hdr(16) + +// These constants define the maximum size of the payload +// in a single UDP packet, depending on the IP version, and +// with the default socket options, that is: +// * default 1500 bytes of MTU (see SRTO_MSS) +// * without FEC packet filter (see SRTO_PACKETFILTER) +// * without AEAD through AES-GCM (see SRTO_CRYPTOMODE) +static const int SRT_MAX_PLSIZE_AF_INET = 1456; // MTU(1500) - IPv4.hdr(20) - UDP.hdr(8) - SRT.hdr(16) +static const int SRT_MAX_PLSIZE_AF_INET6 = 1444; // MTU(1500) - IPv6.hdr(32) - UDP.hdr(8) - SRT.hdr(16) // Latency for Live transmission: default is 120 static const int SRT_LIVE_DEF_LATENCY_MS = 120; @@ -546,10 +554,11 @@ enum SRT_REJECT_REASON SRT_REJ_CONGESTION, // incompatible congestion-controller type SRT_REJ_FILTER, // incompatible packet filter SRT_REJ_GROUP, // incompatible group - SRT_REJ_TIMEOUT, // connection timeout + SRT_REJ_TIMEOUT = 16,// connection timeout #ifdef ENABLE_AEAD_API_PREVIEW SRT_REJ_CRYPTO, // conflicting cryptographic configurations #endif + SRT_REJ_CONFIG = 18, // socket settings on both sides collide and can't be negotiated SRT_REJ_E_SIZE, }; @@ -781,6 +790,9 @@ SRT_API int srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCK SRT_API int srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen); SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen); +SRT_API int srt_getmaxpayloadsize(SRTSOCKET sock); + + typedef struct SRT_SocketGroupData_ SRT_SOCKGROUPDATA; typedef struct SRT_MsgCtrl_ diff --git a/srtcore/srt_c_api.cpp b/srtcore/srt_c_api.cpp index 031030cd7..8262b8ae0 100644 --- a/srtcore/srt_c_api.cpp +++ b/srtcore/srt_c_api.cpp @@ -169,6 +169,11 @@ int srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen) int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen) { return CUDT::setsockopt(u, 0, opt, optval, optlen); } +int srt_getmaxpayloadsize(SRTSOCKET u) +{ + return CUDT::getMaxPayloadSize(u); +} + int srt_send(SRTSOCKET u, const char * buf, int len) { return CUDT::send(u, buf, len, 0); } int srt_recv(SRTSOCKET u, char * buf, int len) { return CUDT::recv(u, buf, len, 0); } int srt_sendmsg(SRTSOCKET u, const char * buf, int len, int ttl, int inorder) { return CUDT::sendmsg(u, buf, len, ttl, 0!= inorder); } @@ -415,6 +420,9 @@ int srt_clock_type() return SRT_SYNC_CLOCK; } +// NOTE: crypto mode is defined regardless of the setting of +// ENABLE_AEAD_API_PREVIEW symbol. This can only block the symbol, +// but it doesn't change the symbol layout. const char* const srt_rejection_reason_msg [] = { "Unknown or erroneous", "Error in system calls", @@ -432,10 +440,9 @@ const char* const srt_rejection_reason_msg [] = { "Congestion controller type collision", "Packet Filter settings error", "Group settings collision", - "Connection timeout" -#ifdef ENABLE_AEAD_API_PREVIEW - ,"Crypto mode" -#endif + "Connection timeout", + "Crypto mode", + "Invalid configuration" }; // Deprecated, available in SRT API. @@ -456,14 +463,14 @@ extern const char* const srt_rejectreason_msg[] = { srt_rejection_reason_msg[13], srt_rejection_reason_msg[14], srt_rejection_reason_msg[15], - srt_rejection_reason_msg[16] -#ifdef ENABLE_AEAD_API_PREVIEW - , srt_rejection_reason_msg[17] -#endif + srt_rejection_reason_msg[16], + srt_rejection_reason_msg[17], + srt_rejection_reason_msg[18] }; const char* srt_rejectreason_str(int id) { + using namespace srt_logging; if (id >= SRT_REJC_PREDEFINED) { return "Application-defined rejection reason"; @@ -471,7 +478,10 @@ const char* srt_rejectreason_str(int id) static const size_t ra_size = Size(srt_rejection_reason_msg); if (size_t(id) >= ra_size) + { + HLOGC(gglog.Error, log << "Invalid rejection code #" << id); return srt_rejection_reason_msg[0]; + } return srt_rejection_reason_msg[id]; } diff --git a/test/test_fec_rebuilding.cpp b/test/test_fec_rebuilding.cpp index ac57e5265..951f82279 100644 --- a/test/test_fec_rebuilding.cpp +++ b/test/test_fec_rebuilding.cpp @@ -59,7 +59,7 @@ class TestFECRebuilding: public srt::Test source.emplace_back(new CPacket); CPacket& p = *source.back(); - p.allocate(SRT_LIVE_MAX_PLSIZE); + p.allocate(SRT_MAX_PLSIZE_AF_INET); uint32_t* hdr = p.getHeader(); @@ -773,7 +773,7 @@ TEST_F(TestFECRebuilding, Prepare) seq = p.getSeqNo(); } - SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); + SrtPacket fec_ctl(SRT_MAX_PLSIZE_AF_INET); // Use the sequence number of the last packet, as usual. bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); @@ -794,7 +794,7 @@ TEST_F(TestFECRebuilding, NoRebuild) seq = p.getSeqNo(); } - SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); + SrtPacket fec_ctl(SRT_MAX_PLSIZE_AF_INET); // Use the sequence number of the last packet, as usual. const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); @@ -871,7 +871,7 @@ TEST_F(TestFECRebuilding, Rebuild) seq = p.getSeqNo(); } - SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE); + SrtPacket fec_ctl(SRT_MAX_PLSIZE_AF_INET); // Use the sequence number of the last packet, as usual. const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq); diff --git a/test/test_file_transmission.cpp b/test/test_file_transmission.cpp index bfd668ac7..5937f43ce 100644 --- a/test/test_file_transmission.cpp +++ b/test/test_file_transmission.cpp @@ -18,6 +18,7 @@ #endif #include "srt.h" +#include "netinet_any.h" #include #include @@ -29,7 +30,7 @@ //#pragma comment (lib, "ws2_32.lib") -TEST(Transmission, FileUpload) +TEST(FileTransmission, Upload) { srt::TestInit srtinit; srtinit.HandlePerTestOptions(); @@ -210,3 +211,200 @@ TEST(Transmission, FileUpload) remove("file.target"); } + +TEST(FileTransmission, Setup46) +{ + using namespace srt; + SRTST_REQUIRES(IPv6); + TestInit srtinit; + + SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket(); + + const int tt = SRTT_FILE; + srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt); + srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt); + + // Setup a connection with IPv6 caller and IPv4 listener, + // then send data of 1456 size and make sure two packets were used. + + // So first configure a caller for IPv6 socket, capable of + // using IPv4. As the IP version isn't specified now when + // creating a socket, force binding explicitly. + + // This creates the "any" spec for IPv6 with port = 0 + sockaddr_any sa(AF_INET6); + + int ipv4_and_ipv6 = 0; + ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + + ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); + + int connect_port = 5555; + + // Configure listener + sockaddr_in sa_lsn = sockaddr_in(); + sa_lsn.sin_family = AF_INET; + sa_lsn.sin_addr.s_addr = INADDR_ANY; + sa_lsn.sin_port = htons(connect_port); + + // Find unused a port not used by any other service. + // Otherwise srt_connect may actually connect. + int bind_res = -1; + for (connect_port = 5000; connect_port <= 5555; ++connect_port) + { + sa_lsn.sin_port = htons(connect_port); + bind_res = srt_bind(sock_lsn, (sockaddr*)&sa_lsn, sizeof sa_lsn); + if (bind_res == 0) + { + std::cout << "Running test on port " << connect_port << "\n"; + break; + } + + ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res; + } + + ASSERT_GE(bind_res, 0); + + srt_listen(sock_lsn, 1); + + ASSERT_EQ(inet_pton(AF_INET6, "::FFFF:127.0.0.1", &sa.sin6.sin6_addr), 1); + + sa.hport(connect_port); + + ASSERT_EQ(srt_connect(sock_clr, sa.get(), sa.size()), 0); + + int sock_acp = -1; + ASSERT_NE(sock_acp = srt_accept(sock_lsn, sa.get(), &sa.len), -1); + + const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 + char buffer[SIZE]; + + std::random_device rd; + std::mt19937 mtrd(rd()); + std::uniform_int_distribution dis(0, UINT8_MAX); + + for (size_t i = 0; i < SIZE; ++i) + { + buffer[i] = dis(mtrd); + } + + EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); + + char resultbuf[SIZE]; + EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), SIZE) << srt_getlasterror_str(); + + // It should use the maximum payload size per packet reported from the option. + int payloadsize_back = 0; + int payloadsize_back_size = sizeof (payloadsize_back); + EXPECT_EQ(srt_getsockflag(sock_clr, SRTO_PAYLOADSIZE, &payloadsize_back, &payloadsize_back_size), 0); + EXPECT_EQ(payloadsize_back, SRT_MAX_PLSIZE_AF_INET); + + SRT_TRACEBSTATS snd_stats, rcv_stats; + srt_bstats(sock_acp, &snd_stats, 0); + srt_bstats(sock_clr, &rcv_stats, 0); + + EXPECT_EQ(snd_stats.pktSentUniqueTotal, 1); + EXPECT_EQ(rcv_stats.pktRecvUniqueTotal, 1); + +} + +TEST(FileTransmission, Setup66) +{ + using namespace srt; + SRTST_REQUIRES(IPv6); + TestInit srtinit; + + SRTSOCKET sock_lsn = srt_create_socket(), sock_clr = srt_create_socket(); + + const int tt = SRTT_FILE; + srt_setsockflag(sock_lsn, SRTO_TRANSTYPE, &tt, sizeof tt); + srt_setsockflag(sock_clr, SRTO_TRANSTYPE, &tt, sizeof tt); + + // Setup a connection with IPv6 caller and IPv4 listener, + // then send data of 1456 size and make sure two packets were used. + + // So first configure a caller for IPv6 socket, capable of + // using IPv4. As the IP version isn't specified now when + // creating a socket, force binding explicitly. + + // This creates the "any" spec for IPv6 with port = 0 + sockaddr_any sa(AF_INET6); + + // Require that the connection allows both IP versions. + int ipv4_and_ipv6 = 0; + ASSERT_NE(srt_setsockflag(sock_clr, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + ASSERT_NE(srt_setsockflag(sock_lsn, SRTO_IPV6ONLY, &ipv4_and_ipv6, sizeof ipv4_and_ipv6), -1); + + ASSERT_NE(srt_bind(sock_clr, sa.get(), sa.size()), -1); + + int connect_port = 5555; + + // Configure listener + sockaddr_any sa_lsn(AF_INET6); + + // Find unused a port not used by any other service. + // Otherwise srt_connect may actually connect. + int bind_res = -1; + for (connect_port = 5000; connect_port <= 5555; ++connect_port) + { + sa_lsn.hport(connect_port); + bind_res = srt_bind(sock_lsn, sa_lsn.get(), sa_lsn.size()); + if (bind_res == 0) + { + std::cout << "Running test on port " << connect_port << "\n"; + break; + } + + ASSERT_TRUE(bind_res == SRT_EINVOP) << "Bind failed not due to an occupied port. Result " << bind_res; + } + + ASSERT_GE(bind_res, 0); + + srt_listen(sock_lsn, 1); + + ASSERT_EQ(inet_pton(AF_INET6, "::1", &sa.sin6.sin6_addr), 1); + + sa.hport(connect_port); + + std::cout << "Connecting to: " << sa.str() << std::endl; + + int connect_result = srt_connect(sock_clr, sa.get(), sa.size()); + ASSERT_EQ(connect_result, 0) << srt_getlasterror_str(); + + int sock_acp = -1; + ASSERT_NE(sock_acp = srt_accept(sock_lsn, sa.get(), &sa.len), SRT_ERROR); + + const size_t SIZE = 1454; // Max payload for IPv4 minus 2 - still more than 1444 for IPv6 + char buffer[SIZE]; + + std::random_device rd; + std::mt19937 mtrd(rd()); + std::uniform_int_distribution dis(0, UINT8_MAX); + + for (size_t i = 0; i < SIZE; ++i) + { + buffer[i] = dis(mtrd); + } + + EXPECT_EQ(srt_send(sock_acp, buffer, SIZE), SIZE) << srt_getlasterror_str(); + + char resultbuf[SIZE]; + EXPECT_EQ(srt_recv(sock_clr, resultbuf, SIZE), SIZE) << srt_getlasterror_str(); + + // It should use the maximum payload size per packet reported from the option. + int payloadsize_back = 0; + int payloadsize_back_size = sizeof (payloadsize_back); + EXPECT_EQ(srt_getsockflag(sock_clr, SRTO_PAYLOADSIZE, &payloadsize_back, &payloadsize_back_size), 0); + EXPECT_EQ(payloadsize_back, SRT_MAX_PLSIZE_AF_INET6); + std::cout << "Payload size: " << payloadsize_back << std::endl; + + SRT_TRACEBSTATS snd_stats, rcv_stats; + srt_bstats(sock_acp, &snd_stats, 0); + srt_bstats(sock_clr, &rcv_stats, 0); + + // We use the same data size that fit in 1 payload IPv4, but not IPv6. + // Therefore sending should be here split into two packets. + EXPECT_EQ(snd_stats.pktSentUniqueTotal, 2); + EXPECT_EQ(rcv_stats.pktRecvUniqueTotal, 2); + +} diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index ee11292b0..9843a0303 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -1,12 +1,16 @@ #include +#include #include +#include #include "gtest/gtest.h" #include "test_env.h" #include "srt.h" +#include "sync.h" #include "netinet_any.h" using srt::sockaddr_any; +using namespace srt::sync; class TestIPv6 : public srt::Test @@ -36,6 +40,11 @@ class TestIPv6 m_listener_sock = srt_create_socket(); ASSERT_NE(m_listener_sock, SRT_ERROR); + + m_CallerStarted.reset(new std::promise); + m_ReadyCaller.reset(new std::promise); + m_ReadyAccept.reset(new std::promise); + } void teardown() override @@ -47,20 +56,71 @@ class TestIPv6 } public: + + void SetupFileMode() + { + int val = SRTT_FILE; + ASSERT_NE(srt_setsockflag(m_caller_sock, SRTO_TRANSTYPE, &val, sizeof val), -1); + ASSERT_NE(srt_setsockflag(m_listener_sock, SRTO_TRANSTYPE, &val, sizeof val), -1); + } + + int m_CallerPayloadSize = 0; + int m_AcceptedPayloadSize = 0; + + std::unique_ptr> m_CallerStarted, m_ReadyCaller, m_ReadyAccept; + + // "default parameter" version. Can't use default parameters because this goes + // against binding parameters. Nor overloading. void ClientThread(int family, const std::string& address) { + return ClientThreadFlex(family, address, true); + } + + void ClientThreadFlex(int family, const std::string& address, bool shouldwork) + { + std::future ready_accepter = m_ReadyAccept->get_future(); + sockaddr_any sa (family); sa.hport(m_listen_port); EXPECT_EQ(inet_pton(family, address.c_str(), sa.get_addr()), 1); - std::cout << "Calling: " << address << "(" << fam[family] << ")\n"; + std::cout << "Calling: " << address << "(" << fam[family] << ") [LOCK...]\n"; + + m_CallerStarted->set_value(); const int connect_res = srt_connect(m_caller_sock, (sockaddr*)&sa, sizeof sa); - EXPECT_NE(connect_res, SRT_ERROR) << "srt_connect() failed with: " << srt_getlasterror_str(); - if (connect_res == SRT_ERROR) - srt_close(m_listener_sock); - PrintAddresses(m_caller_sock, "CALLER"); + if (shouldwork) + { + // Version with expected success + EXPECT_NE(connect_res, SRT_ERROR) << "srt_connect() failed with: " << srt_getlasterror_str(); + + int size = sizeof (int); + EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_CallerPayloadSize, &size), -1); + + m_ReadyCaller->set_value(); + + PrintAddresses(m_caller_sock, "CALLER"); + + if (connect_res == SRT_ERROR) + { + std::cout << "Connect failed - [UNLOCK]\n"; + srt_close(m_listener_sock); + } + else + { + std::cout << "Connect succeeded, [FUTURE-WAIT...]\n"; + ready_accepter.wait(); + } + } + else + { + // Version with expected failure + EXPECT_EQ(connect_res, SRT_ERROR); + EXPECT_EQ(srt_getrejectreason(m_caller_sock), SRT_REJ_CONFIG); + srt_close(m_listener_sock); + } + std::cout << "Connect: exit\n"; } std::map fam = { {AF_INET, "IPv4"}, {AF_INET6, "IPv6"} }; @@ -68,24 +128,34 @@ class TestIPv6 void ShowAddress(std::string src, const sockaddr_any& w) { EXPECT_NE(fam.count(w.family()), 0U) << "INVALID FAMILY"; - std::cout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; + // Printing may happen from different threads, avoid intelining. + std::ostringstream sout; + sout << src << ": " << w.str() << " (" << fam[w.family()] << ")" << std::endl; + std::cout << sout.str(); } sockaddr_any DoAccept() { sockaddr_any sc1; + // Make sure the caller started + m_CallerStarted->get_future().wait(); + std::cout << "DoAccept: caller started, proceeding to accept\n"; + SRTSOCKET accepted_sock = srt_accept(m_listener_sock, sc1.get(), &sc1.len); EXPECT_NE(accepted_sock, SRT_INVALID_SOCK) << "accept() failed with: " << srt_getlasterror_str(); if (accepted_sock == SRT_INVALID_SOCK) { return sockaddr_any(); } - PrintAddresses(accepted_sock, "ACCEPTED"); sockaddr_any sn; EXPECT_NE(srt_getsockname(accepted_sock, sn.get(), &sn.len), SRT_ERROR); EXPECT_NE(sn.get_addr(), nullptr); + int size = sizeof (int); + EXPECT_NE(srt_getsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &m_AcceptedPayloadSize, &size), -1); + + m_ReadyCaller->get_future().wait(); if (sn.get_addr() != nullptr) { @@ -94,6 +164,9 @@ class TestIPv6 << "EMPTY address in srt_getsockname"; } + std::cout << "DoAccept: ready accept - promise SET\n"; + m_ReadyAccept->set_value(); + srt_close(accepted_sock); return sn; } @@ -197,3 +270,86 @@ TEST_F(TestIPv6, v6_calls_v4) client.join(); } +TEST_F(TestIPv6, plsize_v6) +{ + SRTST_REQUIRES(IPv6); + + SetupFileMode(); + + sockaddr_any sa (AF_INET6); + sa.hport(m_listen_port); + + // This time bind the socket exclusively to IPv6. + ASSERT_EQ(srt_setsockflag(m_listener_sock, SRTO_IPV6ONLY, &yes, sizeof yes), 0); + ASSERT_EQ(inet_pton(AF_INET6, "::1", sa.get_addr()), 1); + + ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); + + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "::1"); + + DoAccept(); + + EXPECT_EQ(m_CallerPayloadSize, 1444); // == 1500 - 32[IPv6] - 8[UDP] - 16[SRT] + EXPECT_EQ(m_AcceptedPayloadSize, 1444); + + client.join(); +} + +TEST_F(TestIPv6, plsize_v4) +{ + SetupFileMode(); + + sockaddr_any sa (AF_INET); + sa.hport(m_listen_port); + + // This time bind the socket exclusively to IPv4. + ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", sa.get_addr()), 1); + + ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); + + std::thread client(&TestIPv6::ClientThread, this, AF_INET6, "0::FFFF:127.0.0.1"); + + DoAccept(); + + EXPECT_EQ(m_CallerPayloadSize, 1456); // == 1500 - 20[IPv4] - 8[UDP] - 16[SRT] + EXPECT_EQ(m_AcceptedPayloadSize, 1456); + + client.join(); +} + +TEST_F(TestIPv6, plsize_faux_v6) +{ + SRTST_REQUIRES(IPv6); + + using namespace std::chrono; + SetupFileMode(); + + sockaddr_any sa (AF_INET6); + sa.hport(m_listen_port); + + // This time bind the socket exclusively to IPv6. + ASSERT_EQ(srt_setsockflag(m_listener_sock, SRTO_IPV6ONLY, &yes, sizeof yes), 0); + ASSERT_EQ(inet_pton(AF_INET6, "::1", sa.get_addr()), 1); + + ASSERT_NE(srt_bind(m_listener_sock, sa.get(), sa.size()), SRT_ERROR); + ASSERT_NE(srt_listen(m_listener_sock, SOMAXCONN), SRT_ERROR); + + int oversize = 1450; + ASSERT_NE(srt_setsockflag(m_caller_sock, SRTO_PAYLOADSIZE, &oversize, sizeof (int)), -1); + + std::thread client(&TestIPv6::ClientThreadFlex, this, AF_INET6, "::1", false); + + // Set on sleeping to make sure that the thread started. + // Sleeping isn't reliable so do a dampened spinlock here. + // This flag also confirms that the caller acquired the mutex and will + // unlock it for CV waiting - so we can proceed to notifying it. + m_CallerStarted->get_future().wait(); + + // Just in case of a test failure, kick CV to avoid deadlock + std::cout << "TEST: [PROMISE-SET]\n"; + m_ReadyAccept->set_value(); + + client.join(); +} diff --git a/test/test_socket_options.cpp b/test/test_socket_options.cpp index a4c90af37..9f165e5ba 100644 --- a/test/test_socket_options.cpp +++ b/test/test_socket_options.cpp @@ -198,7 +198,7 @@ const OptionTestEntry g_test_matrix_options[] = { SRTO_MESSAGEAPI, "SRTO_MESSAGEAPI", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, { SRTO_MININPUTBW, "SRTO_MININPUTBW", RestrictionType::POST, sizeof(int64_t), int64_t(0), INT64_MAX, int64_t(0), int64_t(200000), {int64_t(-1)}}, { SRTO_MINVERSION, "SRTO_MINVERSION", RestrictionType::PRE, sizeof(int), 0, INT32_MAX, 0x010000, 0x010300, {} }, - { SRTO_MSS, "SRTO_MSS", RestrictionType::PREBIND, sizeof(int), 76, 65536, 1500, 1400, {-1, 0, 75} }, + { SRTO_MSS, "SRTO_MSS", RestrictionType::PREBIND, sizeof(int), 116, 65536, 1500, 1400, {-1, 0, 75} }, { SRTO_NAKREPORT, "SRTO_NAKREPORT", RestrictionType::PRE, sizeof(bool), false, true, true, false, {} }, { SRTO_OHEADBW, "SRTO_OHEADBW", RestrictionType::POST, sizeof(int), 5, 100, 25, 20, {-1, 0, 4, 101} }, //SRTO_PACKETFILTER diff --git a/testing/srt-test-live.cpp b/testing/srt-test-live.cpp index 1811220c8..12478e461 100644 --- a/testing/srt-test-live.cpp +++ b/testing/srt-test-live.cpp @@ -301,7 +301,7 @@ extern "C" int SrtCheckGroupHook(void* , SRTSOCKET acpsock, int , const sockaddr size = sizeof gt; if (-1 != srt_getsockflag(acpsock, SRTO_GROUPTYPE, >, &size)) { - if (gt < Size(gtypes)) + if (size_t(gt) < Size(gtypes)) Verb(" type=", gtypes[gt], VerbNoEOL); else Verb(" type=", int(gt), VerbNoEOL); diff --git a/testing/srt-test-mpbond.cpp b/testing/srt-test-mpbond.cpp index 60d91df2c..599964b2e 100644 --- a/testing/srt-test-mpbond.cpp +++ b/testing/srt-test-mpbond.cpp @@ -241,7 +241,7 @@ int main( int argc, char** argv ) return 2; } - size_t chunk = SRT_LIVE_MAX_PLSIZE; + size_t chunk = SRT_MAX_PLSIZE_AF_INET; // state the bigger size // Now run the loop try diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index b9d8a0413..b97fa4986 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -392,6 +392,41 @@ void SrtCommon::InitParameters(string host, string path, map par) { m_mode = par.at("mode"); } + + size_t max_payload_size = 0; + + // Try to interpret host and adapter first + sockaddr_any host_sa, adapter_sa; + + if (host != "") + { + host_sa = CreateAddr(host); + if (host_sa.family() == AF_UNSPEC) + Error("Failed to interpret 'host' spec: " + host); + + if (host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; + } + + if (adapter != "") + { + adapter_sa = CreateAddr(adapter); + + if (adapter_sa.family() == AF_UNSPEC) + Error("Failed to interpret 'adapter' spec: " + adapter); + + if (host_sa.family() != AF_UNSPEC && host_sa.family() != adapter_sa.family()) + { + Error("Both host and adapter specified and they use different IP versions"); + } + + if (max_payload_size == 0 && host_sa.family() == AF_INET) + max_payload_size = SRT_MAX_PLSIZE_AF_INET; + } + + if (!max_payload_size) + max_payload_size = SRT_MAX_PLSIZE_AF_INET6; + SocketOption::Mode mode = SrtInterpretMode(m_mode, host, adapter); if (mode == SocketOption::FAILURE) { @@ -445,16 +480,19 @@ void SrtCommon::InitParameters(string host, string path, map par) // That's kinda clumsy, but it must rely on the defaults. // Default mode is live, so check if the file mode was enforced - if (par.count("transtype") == 0 || par["transtype"] != "file") + if ((par.count("transtype") == 0 || par["transtype"] != "file") + && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { - // If the Live chunk size was nondefault, enforce the size. - if (transmit_chunk_size != SRT_LIVE_DEF_PLSIZE) - { - if (transmit_chunk_size > SRT_LIVE_MAX_PLSIZE) - throw std::runtime_error("Chunk size in live mode exceeds 1456 bytes; this is not supported"); + if (transmit_chunk_size > max_payload_size) + throw std::runtime_error(Sprint("Chunk size in live mode exceeds ", max_payload_size, " bytes; this is not supported")); - par["payloadsize"] = Sprint(transmit_chunk_size); - } + par["payloadsize"] = Sprint(transmit_chunk_size); + } + else + { + // set it so without making sure that it was set to "file". + // worst case it will be rejected in settings + m_transtype = SRTT_FILE; } // Assigning group configuration from a special "groupconfig" attribute. @@ -569,6 +607,21 @@ void SrtCommon::AcceptNewClient() Error("srt_accept"); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_bindsock); + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + #if ENABLE_BONDING if (m_sock & SRTGROUP_MASK) { @@ -1406,6 +1459,19 @@ void SrtCommon::ConnectClient(string host, int port) transmit_error_storage.clear(); } + int maxsize = srt_getmaxpayloadsize(m_sock); + if (maxsize == SRT_ERROR) + { + srt_close(m_sock); + Error("srt_getmaxpayloadsize"); + } + + if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) + { + srt_close(m_sock); + Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + } + Verb() << " connected."; stat = ConfigurePost(m_sock); if (stat == SRT_ERROR) diff --git a/testing/testmedia.hpp b/testing/testmedia.hpp index be72471d1..f42c4b8c1 100644 --- a/testing/testmedia.hpp +++ b/testing/testmedia.hpp @@ -102,6 +102,7 @@ class SrtCommon std::string m_mode; std::string m_adapter; std::map m_options; // All other options, as provided in the URI + SRT_TRANSTYPE m_transtype = SRTT_LIVE; std::vector m_group_nodes; std::string m_group_type; std::string m_group_config;