2

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."

Alex
  • 712
  • 2
  • 13
  • 27
  • 1
    @E_net4: many reasons, depending on the kind of traffic you want to do. listen only on specific IP. subscribe to multicast traffic on that ip. Chose one of many IPs to appear as the originating IP for connections. – PlasmaHH Sep 11 '12 at 14:04
  • Can you provide a complete example that we can compile and see for ourselves? It is unclear to me what the other values in ifr and the socket are exactly. I am using that setsockopt fine to bind to interfaces, so it is probably anything of those fields that is wrong. Specifically the port is probably intresting, capabilities wise. – PlasmaHH Sep 11 '12 at 14:07
  • 1
    @E_net4 The app will be communicating with internal components on one network and with clients on a separate network. When a client connects, the socket may end up getting bound to the wrong interface. – Alex Sep 11 '12 at 14:08
  • @PlasmaHH The code is apart of a socket wrapper for a library in my app. The interface name is passed in, so it would be like "eth0" or "eth2". – Alex Sep 11 '12 at 14:15
  • The SO_BINDTODEVICE might be supported by some platforms, but isn't necessarily portable. Did the setsockopt call returned success? If this setsockopt does not work you can use raw sockets, but this requires elevated privileges too. – harper Sep 11 '12 at 14:19
  • @harper Yes it worked, but it required that i run it under the root account. I would like to be able to do this without having to use the root account. – Alex Sep 11 '12 at 14:21
  • The info you require might be in this previous answer http://stackoverflow.com/questions/1207746/problems-with-so-bindtodevice-linux-socket-option – gheese Sep 11 '12 at 14:23
  • @gheese There is nothing root-less in the article, isn't it? – harper Sep 12 '12 at 05:50

2 Answers2

2

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.

Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171
  • This will not work if incoming multicast traffic is coming over a non-default interface. – PlasmaHH Sep 11 '12 at 14:42
  • 1
    False. Add *non-default* route matching the group for that interface. Then if you have same group mcast traffic on different interfaces - you have bigger problems. – Nikolai Fetissov Sep 11 '12 at 15:21
2

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.

Trevor Boyd Smith
  • 18,164
  • 32
  • 127
  • 177
PlasmaHH
  • 15,673
  • 5
  • 44
  • 57
  • SO_BINDTODEVICE is perfect but only works as root... (you get an 'operation not permitted' otherwise) – 2072 Oct 09 '14 at 05:33
  • @2072: That is the whole point of the question and the conclusion in this answer is "This means you have to get the interface from the name yourself, possibly using getifaddrs, and then bind to that address. " – PlasmaHH Oct 09 '14 at 08:19