Forcing connections through a specific interface

Gilad Ben-Yossef May 15th, 2008

It can sometime be very useful to force an application to force to use a specific network interface for routing traffic, the regular routing tables not withstanding.

An example for such cases is where an embedded Linux system has a management network interface and a separate regular network interface where regular traffic should go:

So long as the two interfaces use two different IP addresses from non overlapping network it is very easy - simply bind the management application socket to the management IP and let the routing table do the rest.

What to do, however, if product definition calls for supporting two different and separate, default gateway settings for management and media traffic? Although we can assign multiple default gateway routing table entries, how to make sure the management applications use one and all other traffic use another?

This is where the SO_BINDTODEVICE socket option comes to our rescue. We will install two default gateway entries: a normal one for the media traffic and another one for the management traffic, for which we will define a high metric so it will normally will not be used.

Then, we will set the SO_BINDTODEVICE socket option on sockets opened in management application, thus forcing the Linux networking stack to disregard any routing table entries not going through the specific device, but only for those specific sockets.

It looks something like this:

#include <sys/socket.h>
#include <net/if.h>
struct ifreq interface;
struct socket sock;
/* Management net interface name */
#define IFNAME "eth3"
 
/* Acquire socket here ... */
 
strncpy(interface.ifr_ifrn.ifrn_name, IFNAME, \
  IFNAMSIZ);
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, \
  (char *)&interface, sizeof(interface))  < 0) {
       perror("SO_BINDTODEVICE failed");
      /* Deal with error... */
}

But wait! this is all well and good when we have the management application source code. This however is not always the case. Fear not! this too can be dealt with by hijacking the socket library call:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#define __USE_GNU // Get RTLD_NEXT definition
#include <dlfcn.h>
 
/* Management interface */
#define IFNAME "eth3"
 
int socket(int domain, int type, int protocol) {
 
int (*origsock)(int domain, int type, int protocol);
char * error;
int sock;
 
origsock = dlsym(RTLD_NEXT, "socket");
if ((error = dlerror()) != NULL) {
fprintf (stderr, "%s\n", error);
exit(1);
}
 
sock = origsock(domain, type, protocol);
if(sock != -1) {
  struct ifreq interface;
  strncpy(interface.ifr_ifrn.ifrn_name, IFNAME, \
  IFNAMSIZ);
  if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, \
    (char *)&interface, sizeof(interface)) < 0) {
      perror("sendpacket: setting SO_BINDTODEVICE");
      exit(1);
   }
}
 
return sock;
}

Compile this small library with (or create a proper Makefile)

$ gcc sendto.c -fPIC -o sendto.so -ldl -shared

To use:

LD_PRELOAD=sendto.so ./management_app

This post originally appeared in the Codefidence Technoblog

6 Responses to “Forcing connections through a specific interface”

  1. kristrevon 24 Jul 2008 at 9:23 am

    Hello,

    Very nice example. Would it be possible to post a fully functional, simple client/server using SO_BINDTODEVICE, or at least a bit more complete example of how to use it? I manage to connect using one interface, but when I try to use my other it simply freezes (I use BINDTODEVICE on the client, as I want to have two connections to a remote server on separate interfaces).

    Thanks in advance,
    Kristian

  2. Tuxology teamon 25 Jul 2008 at 8:12 am

    Hi Kristian,

    Glad you liked our example.

    A fuller example of a client/server application is available in many locations, such as Beej’s Guide to Network Programming, available on the Internet.

    The only change from that generic example with regard to BINDTODEVICE is that the little code snippet in the example should be added after the socket is allocated and before it is used. Other then that it’s exactly the same.

    Based on what you wrote, I guess the trouble you’ve run into has more to do with the routing table of the machine rather then the code itself.

    Remember that BINDTODEVICE simply instruct the kernel to send the packets via the specified interface but it is the routing table that determine how they are sent (using a gateway or locally to the LAN and which gateway).

    Also, bare in mind that BINDTODEVICE only affects the machine on which you are running and the other side (server or client) are not obliged by it - you must use BINDTODEVICE on the other side as well to get a fully symmetric scheme.

    If you still can’ get it to work, how about posting the code to our network support forum and we’ll have a look at it?

    Hope this helps,
    Gilad

  3. lihongon 19 Sep 2008 at 1:11 am

    Hi
    I am very interestring about your example,recently, I meet a question about using the ppp, which is similar your example. the problem as follow:
    we expand four serial ports by the TLV16C754B, every serial port connect one CDMA module, they are CDMA0, CDMA1,CDMA2,CDMA3. then , we dummy ppp0,ppp1,ppp2,ppp3 by using the pppd. ppp0 corresponds CDMA0,ppp1 corresponds CDMA1,ppp2 corresponds CDMA2,ppp3 corresponds CDMA3. we write data package to ppp0 ppp1 ppp2 ppp3 by turns , the time interval is 25ms and the order of data package is data0, data1, data2,data3. However we receive the data package in disorder, for example : data0 data0 data0 data0 data3. data3. data3. data3. data2, data2, data2, data2, data1, data1, data1, data1, or other case.
    According to you ,how to slove this problem ? in order to increase bandwidth , how to combine the four CDMA paths into one path?

    Thanks

    Hong

  4. Tuxology teamon 19 Sep 2008 at 2:03 am

    Hi lihong,

    As you’ve seen, there are no order guarantees between different devices.

    To solve your problem, a different approach is needed. I recommend using PPP Multilink, sometime referred to as MP, to “bond” all the 4 CDMA circuits into a single virtual channel.

    Look for “MULTILINK” section in the pppd man page: http://www.manpagez.com/man/8/pppd/

    Good luck!
    Gby

  5. lihongon 19 Sep 2008 at 2:20 am

    Gby
    Thanks your help,I will try it as your methord.
    contacting later

    hong

  6. lihongon 19 Sep 2008 at 3:34 am

    Gby
    I’d like to try multiink,but,how to configure chat and pppd scripts?
    Thanks
    Hong