« Previous | Next » 

Revision 8a76e3a6

ID8a76e3a69d2228ede432a268fa852e08cb4ea145
Parent f08d590a
Child ff8e65e0

Added by Bao Nguyen over 2 years ago

Streaming plugin UDP multicast socket bind option to `SO_REUSEADDR`

In multicast, SO_REUSEPORT and SO_REUSEADDR behaviors are identical.

SO_REUSEPORT was added1 to help unicast behave exactly like what SO_REUSEADDR
already done for multicast. Using SO_REUSEPORT2 instead of SO_REUSEADDR on
multicast can cause incompatability with other software, such as ffmpeg3. When
attempting to receive on the same set of address:port ffmpeg uses SO_REUSEADDR
instead which cause binding to fail. Even though it's socket option is set with
SO_REUSEADDR.

```
host:~$ netstat -paun | grep 239.2.0.2
udp 0 0 239.2.0.2:10000 0.0.0.0:* 17051/janus
udp 0 0 239.2.0.2:10000 0.0.0.0:* 4102/janus
udp 0 0 239.2.0.2:10000 0.0.0.0:* 28619/janus
udp 0 0 239.2.0.2:10000 0.0.0.0:* 14662/janus
udp 0 0 239.2.0.2:10001 0.0.0.0:* 17051/janus
udp 0 0 239.2.0.2:10001 0.0.0.0:* 4102/janus
udp 0 0 239.2.0.2:10001 0.0.0.0:* 28619/janus
udp 0 0 239.2.0.2:10001 0.0.0.0:* 14662/janus

setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(10000), sin_addr=inet_addr("239.2.0.2")}, 16) = -1 EADDRINUSE (Address already in use)
dup(2) = 4
fcntl(4, F_GETFL) = 0x8402 (flags O_RDWR|O_APPEND|O_LARGEFILE)
```

For SO_REUSEPORT, when 2 processes want to share the same address/port part,
their socket options must be identical during setup before bind()
(plus the user ID, to prevent listening by another user [1]). Since these two
sockets options are functionally identical for multicast, their option name is
not for multicast. Changing `SO_REUSEADDR` for multicast and `SO_REUSEPORT` for
Unicast fixes this incompatible issue.

This fix is to address the issue pointed out in [4]

```
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(10000), sin_addr=inet_addr("239.2.0.2")}, 16) = 0
setsockopt(3, SOL_IP, IP_ADD_MEMBERSHIP, {imr_multiaddr=inet_addr("239.2.0.2"),

int one = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != 0)
//if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != 0) {
perror("Error setting reuse flag");
return 2;
}
// Bind to listening address.
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = in->endpoints[i].port;
if (in->endpoints[i].bind_any) {
bind_addr.sin_addr.s_addr = INADDR_ANY;
}
else {
memcpy(&(bind_addr.sin_addr),
&(in->endpoints[i].multicast),
sizeof(in->endpoints[i].multicast));
}
if (bind(s, (const struct sockaddr *)(&bind_addr),
sizeof(bind_addr)) != 0) {
perror("Error binding to listening address");
return 3;
}
```

[1] https://lwn.net/Articles/542629/
[2] https://github.com/meetecho/janus-gateway/issues/617
[3] https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/udp.c#L833
[4] https://github.com/meetecho/janus-gateway/issues/1065

Files

  • added
  • modified
  • copied
  • renamed
  • deleted

View differences