Why it is not possible to use the ADAPTER setting in the CALLER mode? The ADAPTER setting is working for LISTENER and RENDEVOUS, but I would like to use the ADAPTER setting also in the CALLER mode to specify an interface for the outgoing datagrams. So at the moment I have to use the RENDEVOUS mode instead of the CALLER mode, but why?
I've noticed that the binding of the IP address doesn't work reliable under Linux with multiple network interfaces. I think that the socket should also be bind to the device via the SO_BINDTODEVICE option.
Theoretically this is possible to be added to the srt-live-transmit, I didn't even think about it. Here:
void SrtCommon::OpenClient(string host, int port)
{
PrepareClient();
if ( m_outgoing_port )
{
SetupAdapter("", m_outgoing_port);
}
ConnectClient(host, port);
}
It is currently only doing the adapter setup if you have specified the outgoing port. It should work as you wish (the same function is used to setup adapter for listener and rendezvous) when changed here the condition:
if ( m_outgoing_port || m_adapter != "")
{
SetupAdapter(m_adapter, m_outgoing_port);
}
The m_adapter field is earlier filled by the specification provided in the adapter attribute, or left empty if not specified.
Another thing is that there are very strict conditions when it would make sense. I've been researching last time the case when the SRT connection didn't work on a platform that has configured a virtual network interface that shares a common broadcast IP with some other local IP address. In cases like that, when you install a listener on INADDR_ANY and the connection has specified the target remote host as one of these two addresses being second in the order of route rules, the connection can't be made (it's because the listener responds from a different source IP address than the one that was directly contacted by the caller, and the caller reject this handshake response). The fix for this case is underway, just lacks some final testing. Of course, this problem can be worked around easily if you bind the listener socket directly to a given IP address - this way you will always get a listener response from the IP address that was bound.
If you have a caller, however, then I don't think you can fix anything by having an ability to specify the binding address. Truth is, even if you have a case like described above on the caller side, you will be able to make a connection to the remote address that lies in the network routing rules in the same area as two of your local IP addresses, and it doesn't matter much that it used the first one found for that case in the routing tabe. UNLESS you have some unusual firewall rules, which allow the packets sourced from the second IP address, but do not allow those from the first one.
Normally you don't specify the source address on the caller socket, and the system will simply select the correct one as per routing rules that define to which network it should send the packet after it was requested to be sent to particular target IP address. Another case that I can think of is to send packets to a multicast network - in this case usually the source address is the first found local IP address (at least in the order of routing rules).
If you can test if the above solution works for you, I'll prepare a fix.
Thank you! I've had yesterday the same idea with ;-)
if ( m_outgoing_port || m_adapter != "")
{
SetupAdapter(m_adapter, m_outgoing_port);
}
It doesn't solve the issue, but it's important for the following solution in the CChannel::Open function:
#include <ifaddrs.h>
void CChannel::open(const sockaddr* addr)
{
// construct an socket
m_iSocket = ::socket(m_iIPversion, SOCK_DGRAM, 0);
#ifdef _WIN32
if (INVALID_SOCKET == m_iSocket)
#else
if (m_iSocket < 0)
#endif
throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR);
if (NULL != addr)
{
socklen_t namelen = m_iSockAddrSize;
#ifndef _WIN32 ********** my modification starts here **********
struct ifaddrs *addrs, *tmp;
getifaddrs(&addrs);
tmp = addrs;
while (tmp)
{
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET)
{
struct sockaddr_in *net = (struct sockaddr_in *)tmp->ifa_addr;
struct sockaddr_in *own = (struct sockaddr_in *)addr;
if (net && own && (net->sin_addr.s_addr == own->sin_addr.s_addr))
{
if (setsockopt(m_iSocket, SOL_SOCKET, SO_BINDTODEVICE, tmp->ifa_name, strlen(tmp->ifa_name)) != -1)
{
HLOGC(mglog.Debug, log << "CHANNEL: Bound to local device: " << tmp->ifa_name);
}
else
{
HLOGC(mglog.Debug, log << "CHANNEL: Bound to " << tmp->ifa_name << " failed:" << strerror(errno));
}
break;
}
}
tmp = tmp->ifa_next;
}
freeifaddrs(addrs);
#endif ********** my modifications ends here **********
if (0 != ::bind(m_iSocket, addr, namelen))
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
memcpy(&m_BindAddr, addr, namelen);
m_BindAddr.len = namelen;
}
else
{……………………………..
I'm using the SO_BINDTODEVICE option to bind the socket on an interface. This is working in all our applications. Could you implement this also in the SDK? I've implemented it only for IPv4, but it's now working well. ;-)
I can make on Monday a "push request" on GitHub if you like?
Have a nice weekend,
Hans
Ok, I red this time a bit about this SO_BINDTODEVICE option and it's not so simple as you think.
This option probably will work correctly on UDP sockets used by SRT sockets, but this, albeit should work similarly and almost with the same effect (at least in most cases) as using bind(), this technically isn't the same. Users that do bind now should expect that it works the old way, or even that binding done on an SRT socket will be represented by binding on the UDP socket.
Another problem is portability. This option is not supported on many systems, and Windows isn't the only one.
By both these reasons, we can't rely on this option to set up the strict-binding procedure basing on the SO_BINDTODEVICE option. It should be rather something of a kind of UDP transparent option. We already have such examples, IP_TOS and IP_TTL (although they belong to IPPROTO_IP domain, not SOL_SOCKET). A controversial thing on passing options directly for a UDP socket is such that in cases where an SRT socket is going to share its UDP socket with another SRT socket, they can't use different values for these options. These above options can only be set prior to binding, but then it means that if two SRT sockets use two different values of these options, one wins. Therefore for this option we need to make also decisions concerning this: should this be allowed to be set up independently on sharing, or setting this option should automatically enforce SO_REUSEADDR to be cleared and no longer set as true.
Beside this, it would work through an SRT socket option, like SRTO_BINDTODEVICE, which will get the same parameter as its UDP counterpart, and then pass it to the UDP socket at the time when it's created. Of course, in this case you'll have to use it in the SRT URI as a usual option:
... srt://remote.host.com:1234?bindtodevice=p4p1
"SRTO_BINDTODEVICE" is a very good solution! Thank you! I've noticed the other UDP transparent options, but it was for me to much work to solve the issue in this way. ;-) I agree that your idea is very well! Thanks again!
We would like to change to a new SRT version soon, but a solution of this enhancement would be needed. Is there any information as to when this extension might be available?
Sorry, but is there any progress?
This, and all other things that are not related to the upcoming feature must wait until the end of January. I'm really sorry for that, but we had loads of work last time.
I see two points of discussion here:
The 1st is supported by SRT library, and I believe srt-live-transmit already provides this functionality (as of v1.4.1).
The 2nd is the remaining feature request. I agree with @ethouris (comment) that there might be some complications in exposing SO_BINDTODEVICE on an SRT socket similar to IP_TOS and IP_TTL. But if, @hhhansen, you could provide this kind of PR as you suggest, we can at least review it and decide if it is good enough of we should go with SRTO_BINDTODEVICE.
The description of SO_BINDTODEVICE option is simple enough - when you set it on a socket, then only packets from that device will be processed by this socket.
Beside this description on the net is scarce. Theoretically it should behave the same as bind done on the local IP address set on that device, although this is only in a special situation that one device has always the same and only one IP address set. The other difference is that bind allows you to additionally specify the port.
Therefore there's probably some concern for a case when this socket is set SRTO_BINDTODEVICE and then bind is used to specify the listening port. If you use 0.0.0.0:5555, for example, then according to the description the socket will occupy the port 5000 on all device, but it will only work on the specified one. If course, if you specify the right IP address, it shall then occupy only the port with this device, but then I don't think setting SO_BINDTODEVICE will change anything.
I'd keenly see an example use case when setting this option to a desired value makes a change towards what you could achieve also through bind.
Thank you very much, I'll try it asap! The IP address binding differs from the interface binding also in the routing behaviour. Binding SRT caller to a specific IP+port doesn't work in a multi network interface environment with realy independent networks, after my enhancement with SO_BINDTODEVICE mentioned above (8 Feb 2019), it works.
We'll add this because you state it's useful and required. Just mind that - at least with default system settings - setting this flag is only allowed for root-owned processes. I tested it a little bit and it seems to work as described: when I specify srt://target-addr:5000?bindtodevice=eth0 then connection times out when target-addr is not reachable over the eth0 device.
Hi @hhhansen
Have you tryied the changes proposed in PR #1203?
We've upgraded to v1.4.2 and tested the "bindtodevice" option. It's workign well! Thank you for providing this option!
Most helpful comment
We've upgraded to v1.4.2 and tested the "bindtodevice" option. It's workign well! Thank you for providing this option!