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

The shim we are going to be using in this example was written by Daniel Ryde. With thanks to Daniel Lange for his guide and locally hosted copy of the code.

Prerequisites

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

  1. 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
    
  2. 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
    
  3. Strip excess symbols from the file. If crosscompiling for a 32bit architecture the shared object should be bind32.so not bind.so.

    strip bind.so
    
  4. Move the shared object to /usr/lib/

    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:

Output of Route

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.