How to bind a process to an IP address using LD_PRELOAD (on Linux)
Sometimes a peice of software does not provide a configuration option to specify the IP address to bind to on a multi-ip server (including GRE tunnels!). This could be due to immaturity of the software (7 Days to Die) or that it is no longer under development (utdns). It is possible to work around this on Linux without modifying the source (if available) by using a injected (preload) dynamic library to detour the the socket functions and add the required functionality.
Compiling the LD_PRELOAD shim
Install the GCC compiler, essential libraries and development headers.
On Debian / Ubuntu:
apt-get install build-essential
On CentOS / Redhat / Fedora:
yum groupinstall "Development Tools"
If compiling on a 64bit operating system for a 32bit application install the 32bit libraries and development headers
On Debian / Ubuntu:
apt-get install g++-multilib libc6-dev-i386
On CentOS 6.x / Redhat / Fedora:
yum reinstall glibc.i686 glibc-devel.i686
On CentOS 4.x, 5.x / Redhat / Fedora:
yum reinstall glibc.i386 glibc-devel.i386
Compiling the Shim with GCC
Get a copy of the shim code. This code can be obtained from GitHub.
To download this onto your server using wget:
wget https://raw.githubusercontent.com/X4BNet/ld_preload_shim/main/bind.c -O bind.c
Compile the shim using GCC.
For the architecture of the compiling server:
gcc -nostartfiles -fpic -shared bind.c -o bind.so -ldl -D_GNU_SOURCE
For a 32bit application, compiled on a 64bit server:
gcc -nostartfiles -fpic -shared bind.c -o bind32.so -ldl -D_GNU_SOURCE -m32
Strip excess symbols from the file. If crosscompiling for a 32bit architecture the shared object should be bind32.so not bind.so.
Move the shared object to
cp -i bind.so /usr/lib/
Using the compiled shim
To use the compiled shim with your application or service it needs to be "preloaded" into the application. This is accomplished with the environment variable
LD_PRELOAD which should be the path to the shared object compiled. An environment variable
BIND_ADDR is provided to specify the IP address for the service to bind to.
BIND_ADDR="X.X.X.X" LD_PRELOAD=/usr/lib/bind.so [command to run]
The command to run can be the daemon of the application, or a utility such as
start-stop-daemon (providing the utility passes on environment variables).
With X4B GRE Tunnels the
BIND_ADDR is most likely the IP address of your backend's side of the GRE tunnel. This can be obtained from
ifconfig or your service panel (Tunnel Information).
# ifconfig gre2 gre2 Link encap:UNSPEC HWaddr 6C-3D-CF-72-00-00-B0-B6-00-00-00-00-00-00-00-00 inet addr:10.17.24.14 P-t-P:10.17.24.14 Mask:255.255.255.252 UP POINTOPOINT RUNNING NOARP MTU:1180 Metric:1 RX packets:30826 errors:0 dropped:0 overruns:0 frame:0 TX packets:41206 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:2782629 (2.6 MiB) TX bytes:6674435 (6.3 MiB)
For an example of this shim in use see the 7 Days to Die DDoS protection tutorial.
How this works
Warning: Technical Content ahead, feel free to skip
These days many systems are multi-homed in the sense that they have more than one IP address bound at the same time. These IP addresses could be additional IPs added to your server for additional HTTPS sites, services (where a specific port is required) or connected to different networks.
The kernel chooses an outgoing IP from the records matcing the destination from the routing table. Multiple routes are sorted based on the value of the metric:
It is possible to alter the metric and make the kernel router prefer the desired interface above others. However this will affect all applications and services on the server. This includes private services such as SSH, and makes it difficult to use the additional IP addresses on the server.
Applications / Services are able to choose to bind to a specific interface, and often provide the option via a configuration file to choose the network interface or IP address to bind to. Unfortunately not all applications and services provide this functionality, and hence it becomes necessary to add this functionality after the fact. Often this has to be done without knowledge of the underlying software's source code, either because it is closed source or where compilation is difficult.
To do this a LD_PRELOAD library is made to modify bind and connect to use a specific localaddress, regardless of the parameters given to them by the application or service. In doing such the application or service is hence bound to the localaddress for listening and connecting sockets.