I set up a UDP socket with the same remote address in the multicast range. One is sending to 224.2.0.6:6666, the other to 224.2.0.7:6666. I rely on the Linux routing table to determine which interface to send the packets over.
In both cases, I set up a socket at the beginning, set my sockopts, and do a sendto()
. I do not set a particular IP_MULTICAST_IF
.
However, in the first case, I also do a connect()
before the first sendto()
.
In the connect()
case, the OS ignores the loopback route and sends all packets out via the default route and interface.
In the non-connect()
case, the OS respects the loopback route, and all packets are sent via the loopback interface.
Why does a socket()
-connect()
-sendto()
with a route via lo
cause the OS to ignore it, while just doing socket()
-sendto()
with a route via lo
get routed properly?
My ip route
:
224.2.0.0/16 via 127.0.0.1 dev lo
default via 172.16.5.1 dev eth0
Note that this problem exists only if the route is to a loopback interface. If I set the route via a "real" interface - actual, macvlan, dummy, whatever - then both connect()
and sendto()
respect the route equally and work fine.
UPDATE:
Original code. Formatting was messed up as it was an edit of someone else's code, tried to clean up manually, apologies for the mess:
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <stdlib.h>
int fd, fd2;
struct in_addr interface_addr;
int addr_size;
int optval = 1, recv_len = 4;
char *remote_ip = "224.2.0.6", *remote_ip2 = "224.2.0.7";
char *remote_port = "6666";
char *buf = "conn", *buf2 = "send";
struct addrinfo *remote_address_info, *remote_address_info2;
int main() {
// create first and second socket
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket() failed");
}
if ((fd2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket2() failed");
}
// set socket options on both
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) < 0) {
perror("setsockopt(SO_REUSEADDR) failed");
}
if (setsockopt(fd2, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) < 0) {
perror("setsockopt2(SO_REUSEADDR) failed");
}
addr_size = sizeof(interface_addr);
// structure the addr
if (getaddrinfo(remote_ip, remote_port, NULL, &remote_address_info) != 0) {
perror("getaddrinfo(remote_ip) failed");
}
if (getaddrinfo(remote_ip2, remote_port, NULL, &remote_address_info2) != 0) {
perror("getaddrinfo2(remote_ip) failed");
}
// connect ON FIRST SOCKET ONLY
if (connect(fd, remote_address_info->ai_addr, remote_address_info->ai_addrlen) != 0) {
perror("connect() failed");
}
// sendto first and second
// same result if use send() or sendto() on first socket
if (sendto(fd, buf, recv_len, 0, remote_address_info->ai_addr, remote_address_info->ai_addrlen) == -1) {
perror("sendto() failed");
}
if (sendto(fd2, buf2, recv_len, 0, remote_address_info2->ai_addr, remote_address_info2->ai_addrlen) == -1) {
perror("sendto2() failed");
}
}