Reputation: 732
Is it possible to bind a udp socket to a specific interface so it sends data through that interface? I have an application that uses several Udp Sockets to send data and it is running on a machine with several interfaces. I know it's possible to do this by specifying the interface name by using this code:
int UdpSocket::open(const char *interface)
{
send_fd_ = ::socket(AF_INET, SOCK_DGRAM, 0);
if (send_fd_ < 0)
{
perror("socket");
return -1;
}
int val = 1;
int rc = ::setsockopt(send_fd_, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
if (rc < 0)
{
perror("sesockopt");
close();
return -1;
}
unsigned char ttl = 16;
rc = ::setsockopt(send_fd_, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
if (rc < 0)
{
perror("sesockopt_ttl");
close();
return -1;
}
if (interface != NULL)
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), interface);
rc = ::setsockopt(send_fd_, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(ifr));
if (rc < 0)
{
perror("sesockopt");
close();
return -1;
}
}
const int flags = ::fcntl(send_fd_, F_GETFL, 0);
::fcntl(send_fd_, F_SETFL, flags | O_NONBLOCK);
return 0;
}
But this requires that the app is run with root privileges, otherwise it it will throw an error saying the "operation not permitted."
Upvotes: 2
Views: 6291
Reputation: 16046
From the manpage:
SO_BINDTODEVICE
Bind this socket to a particular device like “eth0”, as specified in the passed interface name. If the name is an empty string or the option length is zero, the socket device binding is removed. The passed option is a variable-length null-terminated interface name string with the maximum size of IFNAMSIZ. If a socket is bound to an interface, only packets received from that particular interface are processed by the socket. Note that this only works for some socket types, particularly AF_INET sockets. It is not supported for packet sockets (use normal bind(2) there).
This means you have to get the interface from the name yourself, possibly using getifaddrs
, and then bind to that address.
Upvotes: 2
Reputation: 84239
The easiest, and by far the most sane, approach is to add route
(s) matching your multicast destinations:
~# route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0
since OS network stack selects outbound interface for multicast packets based on the routing table. This also works for listening - you just bind to group address and kernel would pick correct interface for you. You still have to join the group as usual.
Upvotes: 2