Skip to content

Commit

Permalink
[core] Added SRTO_BINDTODEVICE option (#1203).
Browse files Browse the repository at this point in the history
Updated documentation.
  • Loading branch information
ethouris committed Aug 14, 2020
1 parent 907d1cc commit 94fb6df
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 10 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Expand Up @@ -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})
Expand Down
3 changes: 3 additions & 0 deletions apps/socketoptions.hpp
Expand Up @@ -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 }
};
Expand Down
18 changes: 18 additions & 0 deletions docs/API.md
Expand Up @@ -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 |
| ------------------ | ----- | ------- | --------- | ------ | -------- | ------ | --- | ------ |
Expand Down
9 changes: 9 additions & 0 deletions srtcore/api.cpp
Expand Up @@ -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)
{
Expand All @@ -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;
Expand All @@ -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)
Expand Down
62 changes: 55 additions & 7 deletions srtcore/channel.cpp
Expand Up @@ -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),
Expand All @@ -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);

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand All @@ -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)
{
Expand Down Expand Up @@ -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
{
Expand Down
8 changes: 8 additions & 0 deletions srtcore/channel.h
Expand Up @@ -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;

Expand All @@ -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)
Expand Down
48 changes: 48 additions & 0 deletions srtcore/core.cpp
Expand Up @@ -52,6 +52,11 @@ modified by

#include "platform_sys.h"

// Linux specific
#ifdef SRT_ENABLE_BINDTODEVICE
#include <linux/if.h>
#endif

#include <cmath>
#include <sstream>
#include <algorithm>
Expand Down Expand Up @@ -571,6 +576,28 @@ void CUDT::setOpt(SRT_SOCKOPT optName, const void* optval, int optlen)
m_iIpToS = cast_optval<int>(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<int64_t>(optval, optlen);
// (only if connected; if not, then the value
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions srtcore/core.h
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions srtcore/queue.cpp
Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions srtcore/queue.h
Expand Up @@ -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); }

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion srtcore/srt.h
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions testing/testmedia.cpp
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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();
}

/*
Expand Down

0 comments on commit 94fb6df

Please sign in to comment.