3

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");
  }
}
deitch
  • 14,019
  • 14
  • 68
  • 96
  • What is a UDP connect? – Martin James Aug 03 '15 at 16:11
  • 1
    What parameter is in the connect ? – Philip Stuyck Aug 03 '15 at 16:12
  • connect makes sense to me only if it is followed by a send. Connect on a udp socket basically means you are always going to send to the same destination and is similar in behaviour as sendto using the same address as in the connect. see http://stackoverflow.com/questions/9741392/can-you-bind-and-connect-both-ends-of-a-udp-connection – Philip Stuyck Aug 03 '15 at 16:15
  • @PhilipStuyck you are correct. I have always viewed it as a convenience method. `sendto` always requires the destination address and the socket `fd`. With `connect`, I can dispense with the address. But clearly it does something additional at the OS level. And the same behaviour occurs with a `send` as a `sendto` – deitch Aug 03 '15 at 19:59
  • @AndrewHenle posting now.... – deitch Aug 03 '15 at 19:59
  • @PhilipStuyck Or if you only want to receive from that peer. – user207421 Aug 04 '15 at 07:03
  • @EJP, good point. Either way, I definitely know the behaviour - `connect()` immediately sets up the socket and ignores the `224.2.0.0/8 via 127.0.0.1` route, sending via the default route, while the socket that just goes straight to `sendto()` *always* sends out using the route. But why? – deitch Aug 04 '15 at 07:07

0 Answers0