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
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
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
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
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
Gby
Thanks your help,I will try it as your methord.
contacting later
hong
Gby
I’d like to try multiink,but,how to configure chat and pppd scripts?
Thanks
Hong