Force any binary to use a specific network interface.
Requires gcc and bash. Linux only.
Usage:
$ sudo ./setnif.sh eth1 curl www.google.com
Inspired by Daniel Ryde's Libc wrapper for bind and connect.
Force any binary to use a specific network interface.
Requires gcc and bash. Linux only.
Usage:
$ sudo ./setnif.sh eth1 curl www.google.com
Inspired by Daniel Ryde's Libc wrapper for bind and connect.
#!/usr/bin/env bash | |
# Build lib | |
build() | |
{ | |
gcc -o $SO -nostartfiles -fpic -shared -xc - -ldl -D_GNU_SOURCE <<EOF | |
#include <sys/socket.h> | |
#include <sys/ioctl.h> | |
#include <net/if.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <dlfcn.h> | |
int (*std_socket)( int, int, int ); | |
void _init( void ) | |
{ | |
const char *err; | |
std_socket = dlsym( RTLD_NEXT, "socket" ); | |
if( (err = dlerror()) != NULL ) | |
fprintf( stderr, "dlsym (socket): %s\n", err ); | |
} | |
static void set_nif( int sockfd ) | |
{ | |
struct ifreq ifr; | |
const char *nif; | |
if( !(nif = getenv( "NIF" )) ) | |
{ | |
fprintf( stderr, "NIF unset\n" ); | |
return; | |
} | |
memset( &ifr, 0, sizeof( ifr ) ); | |
strncat( ifr.ifr_name, nif, sizeof( ifr.ifr_name ) ); | |
if( ioctl( sockfd, SIOCGIFINDEX, &ifr ) ) | |
{ | |
perror( "ioctl" ); | |
return; | |
} | |
if( setsockopt( | |
sockfd, | |
SOL_SOCKET, | |
SO_BINDTODEVICE, | |
(void *)&ifr, | |
sizeof( ifr ) ) < 0 ) | |
perror( "SO_BINDTODEVICE failed" ); | |
} | |
int socket( int domain, int type, int protocol ) | |
{ | |
int sockfd; | |
if( (sockfd = std_socket( domain, type, protocol )) > 2 && | |
domain == AF_INET ) | |
set_nif( sockfd ); | |
return sockfd; | |
} | |
EOF | |
} | |
(( $# < 2 )) && { | |
echo "usage: ${0##*/} DEVICE BINARY [ARGS]" | |
exit 1 | |
} | |
readonly SO=${SO:-"${0%/*}/setnif.so"} | |
if ! [ -r $SO ] || find ${SO%/*} -newer $SO -name ${0##*/} &>/dev/null | |
then | |
build || exit 1 | |
fi | |
export NIF=$1 | |
shift | |
LD_PRELOAD=$SO $@ |