How to bind a process to an IP address using LD_PRELOAD (on Linux)
Sometimes a piece 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 can occur in early application versions or software that is no longer maintained; historical examples include 7 Days to Die and utdns. Check the documentation for your application version for a native bind setting first.
It is possible to work around this on Linux without modifying the source (if available) by using an injected (preload) dynamic library to detour the socket functions and add the required functionality.
Compiling the LD_PRELOAD shim
The shim we are going to be using in this example was written by Daniel Ryde. With thanks to Daniel Lange for the guide to binding applications to a specific IP and locally hosted copy of the code.
Prerequisites
Use a supported operating-system release and a compiler/toolchain matching the application's architecture. On Debian or Ubuntu, install the basic build tools:
apt-get install build-essential
On Fedora, install the compiler and C library headers using the supported package manager:
dnf install gcc glibc-devel
For other distributions, use their supported development packages. The historical CentOS 4/5/6 recipes are retired. For a 32-bit x86 application on a 64-bit x86 system, install the distribution's matching multilib compiler and C development libraries before using the -m32 command below. Package availability depends on the release and architecture. For example, Debian 13 on AMD64 provides the following packages for building 32-bit x86 applications:
apt-get install gcc-multilib libc6-dev-i386
See Debian's multilib compiler and 32-bit C development libraries.
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.cCompile the shim using GCC.
For the architecture of the compiling server:
gcc -nostartfiles -fpic -shared bind.c -o bind.so -ldl -D_GNU_SOURCEFor a 32bit application, compiled on a 64bit server:
gcc -nostartfiles -fpic -shared bind.c -o bind32.so -ldl -D_GNU_SOURCE -m32Strip excess symbols from the file. If cross-compiling for a 32-bit architecture, use
bind32.soinstead ofbind.so.strip bind.soMove the shared object to
/usr/lib/cp -i bind.so /usr/lib/
Using the compiled shim
To preload the compiled shim into your application or service, set these environment variables:
LD_PRELOAD: the path to the compiled shared object.BIND_ADDR: 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
The following background explains how the shim selects a local address.
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 matching 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.
An LD_PRELOAD library modifies bind and connect to use a specific local address, regardless of the parameters supplied by the application or service. This binds both listening and connecting sockets to that address.
See the Linux dynamic-loader reference and Fedora compiler instructions for prerequisites.
