Skip to content

Instantly share code, notes, and snippets.

@markusfisch
Last active July 22, 2024 00:35
Show Gist options
  • Save markusfisch/51b1ce6c3ca9ce67e081 to your computer and use it in GitHub Desktop.
Save markusfisch/51b1ce6c3ca9ce67e081 to your computer and use it in GitHub Desktop.
Force any binary to use a specific network interface in Linux

setnif

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 $@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment