From 94fb6df50a08afff23c32aff42939b6e7772c47c Mon Sep 17 00:00:00 2001 From: Sektor van Skijlen Date: Fri, 14 Aug 2020 17:17:21 +0200 Subject: [PATCH] [core] Added SRTO_BINDTODEVICE option (#1203). Updated documentation. --- CMakeLists.txt | 5 ++++ apps/socketoptions.hpp | 3 ++ docs/API.md | 18 ++++++++++++ srtcore/api.cpp | 9 ++++++ srtcore/channel.cpp | 62 +++++++++++++++++++++++++++++++++++++----- srtcore/channel.h | 8 ++++++ srtcore/core.cpp | 48 ++++++++++++++++++++++++++++++++ srtcore/core.h | 3 ++ srtcore/queue.cpp | 4 +++ srtcore/queue.h | 7 +++++ srtcore/srt.h | 2 +- testing/testmedia.cpp | 4 +-- 12 files changed, 163 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b921d5e5..96de8f28b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -578,6 +578,11 @@ add_definitions( -DSRT_VERSION="${SRT_VERSION}" ) +if (LINUX) +# This is an option supported only on Linux + add_definitions(-DSRT_ENABLE_BINDTODEVICE) +endif() + # This is obligatory include directory for all targets. This is only # for private headers. Installable headers should be exclusively used DIRECTLY. include_directories(${SRT_SRC_COMMON_DIR} ${SRT_SRC_SRTCORE_DIR} ${SRT_SRC_HAICRYPT_DIR}) diff --git a/apps/socketoptions.hpp b/apps/socketoptions.hpp index 0dd73f1a6..2bd7ceb5a 100644 --- a/apps/socketoptions.hpp +++ b/apps/socketoptions.hpp @@ -245,6 +245,9 @@ const SocketOption srt_options [] { { "peeridletimeo", 0, SRTO_PEERIDLETIMEO, SocketOption::PRE, SocketOption::INT, nullptr }, { "packetfilter", 0, SRTO_PACKETFILTER, SocketOption::PRE, SocketOption::STRING, nullptr }, { "groupconnect", 0, SRTO_GROUPCONNECT, SocketOption::PRE, SocketOption::INT, nullptr}, +#ifdef SRT_ENABLE_BINDTODEVICE + { "bindtodevice", 0, SRTO_BINDTODEVICE, SocketOption::PRE, SocketOption::STRING, nullptr}, +#endif { "groupstabtimeo", 0, SRTO_GROUPSTABTIMEO, SocketOption::PRE, SocketOption::INT, nullptr}, { "retransmitalgo", 0, SRTO_RETRANSMITALGO, SocketOption::PRE, SocketOption::INT, nullptr } }; diff --git a/docs/API.md b/docs/API.md index 6896095ae..ce7059b99 100644 --- a/docs/API.md +++ b/docs/API.md @@ -608,6 +608,24 @@ from the group. This option list is sorted alphabetically. +| OptName | Since | Binding | Type | Units | Default | Range | Dir |Entity| +| --------------------- | ----- | ------- | -------- | ------ | -------- | ------ |-----|------| +| `SRTO_BINDTODEVICE` | 1.5.0 | pre | `string` | | | | RW | GSD+ | + +- Refers to the `SO_BINDTODEVICE` system socket option for `SOL_SOCKET` level. +This effectively limits the packets received by this socket to only those +that are targeted to that device. The device is specified by name passed as +string. The setting becomes effective after binding the socket (including +default-binding when connecting). + +- NOTE: This option is only available on Linux and available there by default. +On all other platforms setting this option will always fail. + +- NOTE: With the default system configuration, this option is only available +for a process that runs as root. Otherwise the function that applies the setting +(`srt_bind`, `srt_connect` etc.) will fail. + +--- | OptName | Since | Binding | Type | Units | Default | Range | Dir | Entity | | ------------------ | ----- | ------- | --------- | ------ | -------- | ------ | --- | ------ | diff --git a/srtcore/api.cpp b/srtcore/api.cpp index fb3f6cf0f..6d4ed0330 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -2528,6 +2528,9 @@ void CUDTUnited::updateMux( && (i->second.m_iMSS == s->m_pUDT->m_iMSS) && (i->second.m_iIpTTL == s->m_pUDT->m_iIpTTL) && (i->second.m_iIpToS == s->m_pUDT->m_iIpToS) +#ifdef SRT_ENABLE_BINDTODEVICE + && (i->second.m_BindToDevice == s->m_pUDT->m_BindToDevice) +#endif && (i->second.m_iIpV6Only == s->m_pUDT->m_iIpV6Only) && i->second.m_bReusable) { @@ -2553,6 +2556,9 @@ void CUDTUnited::updateMux( m.m_iIPversion = addr.family(); m.m_iIpTTL = s->m_pUDT->m_iIpTTL; m.m_iIpToS = s->m_pUDT->m_iIpToS; +#ifdef SRT_ENABLE_BINDTODEVICE + m.m_BindToDevice = s->m_pUDT->m_BindToDevice; +#endif m.m_iRefCount = 1; m.m_iIpV6Only = s->m_pUDT->m_iIpV6Only; m.m_bReusable = s->m_pUDT->m_bReuseAddr; @@ -2563,6 +2569,9 @@ void CUDTUnited::updateMux( m.m_pChannel = new CChannel(); m.m_pChannel->setIpTTL(s->m_pUDT->m_iIpTTL); m.m_pChannel->setIpToS(s->m_pUDT->m_iIpToS); +#ifdef SRT_ENABLE_BINDTODEVICE + m.m_pChannel->setBind(m.m_BindToDevice); +#endif m.m_pChannel->setSndBufSize(s->m_pUDT->m_iUDPSndBufSize); m.m_pChannel->setRcvBufSize(s->m_pUDT->m_iUDPRcvBufSize); if (s->m_pUDT->m_iIpV6Only != -1) diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 45554cd5b..f4080423e 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -71,8 +71,15 @@ modified by using namespace std; using namespace srt_logging; +#ifdef _WIN32 + // use INVALID_SOCKET, as provided +#else + static const int INVALID_SOCKET = -1; +#endif + + CChannel::CChannel(): -m_iSocket(), +m_iSocket(INVALID_SOCKET), m_iIpTTL(-1), /* IPv4 TTL or IPv6 HOPs [1..255] (-1:undefined) */ m_iIpToS(-1), /* IPv4 Type of Service or IPv6 Traffic Class [0x00..0xff] (-1:undefined) */ m_iSndBufSize(65536), @@ -90,12 +97,6 @@ void CChannel::createSocket(int family) // construct an socket m_iSocket = ::socket(family, SOCK_DGRAM, IPPROTO_UDP); -#ifdef _WIN32 - // use INVALID_SOCKET, as provided -#else - static const int INVALID_SOCKET = -1; -#endif - if (m_iSocket == INVALID_SOCKET) throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR); @@ -257,6 +258,24 @@ void CChannel::setUDPSockOpt() } } +#ifdef SRT_ENABLE_BINDTODEVICE + if (m_BindAddr.family() != AF_INET) + { + LOGC(mglog.Error, log << "SRTO_BINDTODEVICE can only be set with AF_INET connections"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + if (!m_BindToDevice.empty()) + { + if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, m_BindToDevice.c_str(), m_BindToDevice.size())) + { + char buf[255]; + const char* err = SysStrError(NET_ERROR, buf, 255); + LOGC(mglog.Error, log << "setsockopt(SRTO_BINDTODEVICE): " << err); + throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR); + } + } +#endif #ifdef UNIX // Set non-blocking I/O @@ -324,6 +343,9 @@ void CChannel::setIpV6Only(int ipV6Only) int CChannel::getIpTTL() const { + if (m_iSocket == INVALID_SOCKET) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + socklen_t size = sizeof(m_iIpTTL); if (m_BindAddr.family() == AF_INET) { @@ -344,6 +366,9 @@ int CChannel::getIpTTL() const int CChannel::getIpToS() const { + if (m_iSocket == INVALID_SOCKET) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + socklen_t size = sizeof(m_iIpToS); if (m_BindAddr.family() == AF_INET) { @@ -374,6 +399,29 @@ void CChannel::setIpToS(int tos) m_iIpToS = tos; } +#ifdef SRT_ENABLE_BINDTODEVICE +void CChannel::setBind(const string& name) +{ + m_BindToDevice = name; +} + +bool CChannel::getBind(char* dst, size_t len) +{ + if (m_iSocket == INVALID_SOCKET) + return false; // No socket to get data from + + // Try to obtain it directly from the function. If not possible, + // then return from internal data. + socklen_t length = len; + int res = ::getsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, dst, &length); + if (res == -1) + return false; // Happens on Linux v < 3.8 + + // For any case + dst[length] = 0; + return true; +} +#endif int CChannel::ioctlQuery(int type SRT_ATR_UNUSED) const { diff --git a/srtcore/channel.h b/srtcore/channel.h index 812786f12..52508e47f 100644 --- a/srtcore/channel.h +++ b/srtcore/channel.h @@ -159,6 +159,11 @@ class CChannel int getIpToS() const; +#ifdef SRT_ENABLE_BINDTODEVICE + void setBind(const std::string& name); + bool getBind(char* dst, size_t len); +#endif + int ioctlQuery(int type) const; int sockoptQuery(int level, int option) const; @@ -173,6 +178,9 @@ class CChannel UDPSOCKET m_iSocket; // socket descriptor int m_iIpTTL; int m_iIpToS; +#ifdef SRT_ENABLE_BINDTODEVICE + std::string m_BindToDevice; +#endif int m_iSndBufSize; // UDP sending buffer size int m_iRcvBufSize; // UDP receiving buffer size int m_iIpV6Only; // IPV6_V6ONLY option (-1 if not set) diff --git a/srtcore/core.cpp b/srtcore/core.cpp index 8d1076d92..afa6e6971 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -52,6 +52,11 @@ modified by #include "platform_sys.h" +// Linux specific +#ifdef SRT_ENABLE_BINDTODEVICE +#include +#endif + #include #include #include @@ -571,6 +576,28 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen) m_iIpToS = cast_optval(optval, optlen); break; + case SRTO_BINDTODEVICE: +#ifdef SRT_ENABLE_BINDTODEVICE + { + string val; + if (optlen == -1) + val = (const char *)optval; + else + val.assign((const char *)optval, optlen); + if (val.size() >= IFNAMSIZ) + { + LOGC(mglog.Error, log << "SRTO_BINDTODEVICE: device name too long (max: IFNAMSIZ=" << IFNAMSIZ << ")"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + } + + m_BindToDevice = val; + } +#else + LOGC(mglog.Error, log << "SRTO_BINDTODEVICE is not supported on that platform"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + break; + case SRTO_INPUTBW: m_llInputBW = cast_optval(optval, optlen); // (only if connected; if not, then the value @@ -1159,6 +1186,26 @@ void CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen) optlen = sizeof(int32_t); break; + case SRTO_BINDTODEVICE: +#ifdef SRT_ENABLE_BINDTODEVICE + if (optlen < IFNAMSIZ) + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); + + if (m_bOpened && m_pSndQueue->getBind(((char*)optval), optlen)) + { + optlen = strlen((char*)optval); + break; + } + + // Fallback: return from internal data + strcpy(((char*)optval), m_BindToDevice.c_str()); + optlen = m_BindToDevice.size(); +#else + LOGC(mglog.Error, log << "SRTO_BINDTODEVICE is not supported on that platform"); + throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); +#endif + break; + case SRTO_SENDER: *(int32_t *)optval = m_bDataSender; optlen = sizeof(int32_t); @@ -1330,6 +1377,7 @@ bool SRT_SocketOptionObject::add(SRT_SOCKOPT optname, const void* optval, size_t switch (optname) { + case SRTO_BINDTODEVICE: case SRTO_CONNTIMEO: case SRTO_DRIFTTRACER: //SRTO_FC - not allowed to be different among group members diff --git a/srtcore/core.h b/srtcore/core.h index 615c6ff5a..9397ee31d 100644 --- a/srtcore/core.h +++ b/srtcore/core.h @@ -1425,6 +1425,9 @@ class CUDT int64_t m_llMaxBW; // maximum data transfer rate (threshold) int m_iIpTTL; int m_iIpToS; +#ifdef SRT_ENABLE_BINDTODEVICE + std::string m_BindToDevice; +#endif // These fields keep the options for encryption // (SRTO_PASSPHRASE, SRTO_PBKEYLEN). Crypto object is // created later and takes values from these. diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 0045f3ae1..20f25f3b4 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -521,6 +521,10 @@ int CSndQueue::getIpTTL() const { return m_pChannel ? m_pChannel->getIpTTL() : - int CSndQueue::getIpToS() const { return m_pChannel ? m_pChannel->getIpToS() : -1; } +#ifdef SRT_ENABLE_BINDTODEVICE +bool CSndQueue::getBind(char* dst, size_t len) const { return m_pChannel ? m_pChannel->getBind(dst, len) : false; } +#endif + void *CSndQueue::worker(void *param) { CSndQueue *self = (CSndQueue *)param; diff --git a/srtcore/queue.h b/srtcore/queue.h index 3284c6def..d14b8728a 100644 --- a/srtcore/queue.h +++ b/srtcore/queue.h @@ -392,6 +392,10 @@ friend class CUDTUnited; int getIpToS() const; +#ifdef SRT_ENABLE_BINDTODEVICE + bool getBind(char* dst, size_t len) const; +#endif + int ioctlQuery(int type) const { return m_pChannel->ioctlQuery(type); } int sockoptQuery(int level, int type) const { return m_pChannel->sockoptQuery(level, type); } @@ -541,6 +545,9 @@ struct CMultiplexer int m_iIPversion; // Address family (AF_INET or AF_INET6) int m_iIpTTL; int m_iIpToS; +#ifdef SRT_ENABLE_BINDTODEVICE + std::string m_BindToDevice; +#endif int m_iMSS; // Maximum Segment Size int m_iRefCount; // number of UDT instances that are associated with this multiplexer int m_iIpV6Only; // IPV6_V6ONLY option diff --git a/srtcore/srt.h b/srtcore/srt.h index 2da1e180e..f9dff664a 100644 --- a/srtcore/srt.h +++ b/srtcore/srt.h @@ -238,7 +238,7 @@ typedef enum SRT_SOCKOPT { SRTO_GROUPCONNECT, // Set on a listener to allow group connection SRTO_GROUPSTABTIMEO, // Stability timeout (backup groups) in [us] SRTO_GROUPTYPE, // Group type to which an accepted socket is about to be added, available in the handshake - // (some space left) + SRTO_BINDTODEVICE, // Forward the SOL_SOCKET/SO_BINDTODEVICE option on socket (pass packets only from that device) SRTO_PACKETFILTER = 60, // Add and configure a packet filter SRTO_RETRANSMITALGO = 61 // An option to select packet retransmission algorithm } SRT_SOCKOPT; diff --git a/testing/testmedia.cpp b/testing/testmedia.cpp index 29b313ec0..e538e55fe 100755 --- a/testing/testmedia.cpp +++ b/testing/testmedia.cpp @@ -937,7 +937,7 @@ void SrtCommon::OpenGroupClient() SRTSOCKET insock = m_group_nodes[i].socket; if (insock == -1) { - Verb() << "TARGET '" << targets[i].peeraddr.str() << "' connection failed."; + Verb() << "TARGET '" << sockaddr_any(targets[i].peeraddr).str() << "' connection failed."; continue; } @@ -971,7 +971,7 @@ void SrtCommon::OpenGroupClient() { // id, status, result, peeraddr Verb() << "@" << d.id << " <" << SockStatusStr(d.sockstate) << "> (=" << d.result << ") PEER:" - << sockaddr_any((sockaddr*)&d.peeraddr, sizeof d.peeraddr).str()); + << sockaddr_any((sockaddr*)&d.peeraddr, sizeof d.peeraddr).str(); } /*