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; | |
| } | |
| } |