Force any binary to use a specific network interface.
Build: $ make
Use: LD_PRELOAD=$(pwd)/setnif.so NIF=eth1 program args...
Linux only.
*.so |
all: setnif.so | |
.PHONY: all | |
setnif.so: setnif.c | |
gcc -O2 -o $@ -fpic -shared $< -ldl -D_GNU_SOURCE | |
clean: | |
rm -f setnif.so |
#include <dlfcn.h> | |
#include <errno.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/socket.h> | |
#include <unistd.h> | |
#define CONST_STR_LEN(s) (s), (sizeof(s) - 1) | |
static int (*std_socket)(int, int, int); | |
static const char *nif; | |
static int nif_len; | |
static void __attribute__ ((constructor)) setnif_init(void) { | |
const char *err; | |
std_socket = dlsym(RTLD_NEXT, "socket"); | |
nif = getenv("NIF"); | |
nif_len = strlen(nif); | |
} | |
static int set_nif(int fd) { | |
if (NULL == nif) return fd; | |
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, nif, strlen(nif)) < 0) { | |
int e = errno; | |
close(fd); | |
errno = e; | |
return -1; | |
} | |
return fd; | |
} | |
int socket(int domain, int type, int protocol) { | |
int fd; | |
if (NULL == std_socket) { | |
errno = ENOSYS; | |
return -1; | |
} | |
fd = std_socket(domain, type, protocol); | |
if (fd < 0) return fd; | |
switch (domain) { | |
case AF_INET: | |
case AF_INET6: | |
return set_nif(fd); | |
default: | |
return fd; | |
} | |
} |