Принудительная привязка программ к интерфейсу, а не к IP-адресу

Попытка выполнить следующую команду

for i in `cat filename| perl -pne "s/ /\n/g"| sort| uniq`; do j=`grep -c $i p.txt`; if [[ $j -eq 2 ]]; then sr=`echo $i`; else sed -n '/'$i'/p' p.txt| sed -n '/'$sr'/!p'; fi; done| awk '{if (!seen[$0]++)print $0}'

выход

apple pear
sausage egg
spoon fork
0
08.05.2021, 10:30
2 ответа

Спасибо @Andy Dalton за полезную информацию. Основываясь на этом, я сделал небольшой фрагмент кода для LD _PRELOAD, чтобы реализовать SO _BINDTODEVICE для каждой программы.

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <dlfcn.h>
#include <net/if.h>
#include <string.h>
#include <errno.h>

//Credits go to https://catonmat.net/simple-ld-preload-tutorial and https://catonmat.net/simple-ld-preload-tutorial-part-two
//And of course to https://unix.stackexchange.com/a/648721/334883

//compile with gcc -nostartfiles -fpic -shared bindInterface.c -o bindInterface.so -ldl -D_GNU_SOURCE
//Use with BIND_INTERFACE=<network interface> LD_PRELOAD=./bindInterface.so <your program> like curl ifconfig.me

int socket(int family, int type, int protocol)
{
    //printf("MySocket\n"); //"LD_PRELOAD=./bind.so wget -O- ifconfig.me 2> /dev/null" prints two times "MySocket". First is for DNS-Lookup. 
                            //If your first nameserver is not reachable via bound interface, 
                            //then it will try the next nameserver until it succeeds or stops with name resolution error. 
                            //This is why it could take significantly longer than curl --interface wlan0 ifconfig.me
    char *bind_addr_env;
    struct ifreq interface;
    int *(*original_socket)(int, int, int);
    original_socket = dlsym(RTLD_NEXT,"socket");
    int fd = (int)(*original_socket)(family,type,protocol);
    bind_addr_env = getenv("BIND_INTERFACE");
    int errorCode;
    if ( bind_addr_env!= NULL && strlen(bind_addr_env) > 0)
    {
        //printf(bind_addr_env);
        strcpy(interface.ifr_name,bind_addr_env);
        errorCode = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &interface, sizeof(interface));
        if ( errorCode < 0)
        {
            perror("setsockopt");
            errno = EINVAL;
            return -1;
        };
    }
    else
    {
        printf("Warning: Programm with LD_PRELOAD startet, but BIND_INTERFACE environment variable not set\n");
        fprintf(stderr,"Warning: Programm with LD_PRELOAD startet, but BIND_INTERFACE environment variable not set\n");
    }

    return fd;
}

Скомпилируйте его с

gcc -nostartfiles -fpic -shared bindInterface.c -o bindInterface.so -ldl -D_GNU_SOURCE

Используйте его с

BIND_INTERFACE=wlan0 LD_PRELOAD=./bindInterface.so wget -O- ifconfig.me 2>/dev/null

Примечание :Выполнение этого может занять намного больше времени, чем с curl --interface wlan0 ifconfig.meЭто связано с тем, что он также пытается получить доступ к вашему первому серверу имен из /etc/resolv.confтакже с привязанным к нему интерфейсом. Если этот сервер имен недоступен, он использует второй и так далее. Если вы отредактируете /etc/resolv.confи поместите, например, общедоступный DNS-сервер Google 8.8.8.8 на первое место, он будет таким же быстрым, как версия curl. С опцией --interfacecurl привязывается только к этому интерфейсу при установлении фактического соединения, а не при разрешении IP-адреса -. Таким образом, при использовании curl с привязанным интерфейсом и VPN-приватностью DNS-запросы будут пропускаться через обычное соединение, если они не настроены должным образом. Используйте этот код для проверки:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <dlfcn.h>
#include <net/if.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>

int connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
    int *(*original_connect)(int, const struct sockaddr*, socklen_t);
    original_connect = dlsym(RTLD_NEXT,"connect");

    static struct sockaddr_in *socketAddress;
    socketAddress = (struct sockaddr_in *)addr;

    if (socketAddress -> sin_family == AF_INET)
    {
        // inet_ntoa(socketAddress->sin_addr.s_addr); when #include <arpa/inet.h> is not included
        char *dest = inet_ntoa(socketAddress->sin_addr); //with #include <arpa/inet.h>
        printf("connecting to: %s / ",dest);
    }

    struct ifreq boundInterface = 
        {
           .ifr_name = "none",
        };
    socklen_t optionlen = sizeof(boundInterface);
    int errorCode;
    errorCode = getsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, &boundInterface, &optionlen);
    if ( errorCode < 0)
    {
        perror("getsockopt");
        return -1;
    };
    printf("Bound Interface: %s\n",boundInterface.ifr_name);

    return (int)original_connect(sockfd, addr, addrlen);    
}

Используйте ту же опцию, что и выше, для компиляции. Используйте с

LD_PRELOAD=./bindInterface.so curl --interface wlan0 ifconfig.me
connecting to: 192.168.178.1 / Bound Interface: none
connecting to: 34.117.59.81 / Bound Interface: wlan0
185.107.XX.XX

Примечание 2 :Изменение /etc/resolv.confне является постоянным. Это другая тема. Это было просто сделано для демонстрации того, почему это занимает больше времени.

0
28.07.2021, 11:33

Думаю, вы ищете (только Linux -)SO_BINDTODEVICE. Изman 7 socket:

 SO_BINDTODEVICE
      Bind this socket to a particular device like “eth0”, as specified in the passed
      interface name.  If the name is an empty string or the option length is zero,
      the socket device binding is removed. The passed option is a variable-length
      null-terminated interface name string with the maximum size of IFNAMSIZ.
      If a socket is bound to an interface, only packets received from that particular
      interface are processed by the socket.  Note that this works only for some socket
      types, particularly AF_INET sockets. It is not supported for packet sockets (use
      normal bind(2) there).

Вот пример программы, в которой он используется:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>

int main(void)
{
    const int sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if (sockfd < 0) {
        perror("socket");
        return EXIT_FAILURE;
    }

    const struct ifreq ifr = {
       .ifr_name = "enp0s3",
    };

    if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) {
        perror("setsockopt");
        return EXIT_FAILURE;
    }

    const struct sockaddr_in servaddr = {
       .sin_family      = AF_INET,
       .sin_addr.s_addr = inet_addr("142.250.73.196"),
       .sin_port        = htons(80),
    };

    if (connect(sockfd, (const struct sockaddr*) &servaddr, sizeof(servaddr)) < 0) {
        fprintf(stderr, "Connection to the server failed...\n");
        return EXIT_FAILURE;
    }

    // Make an HTTP request to Google
    dprintf(sockfd, "GET / HTTP/1.1\r\n");
    dprintf(sockfd, "HOST: www.google.com\r\n");
    dprintf(sockfd, "\r\n");

    char buffer[16] = {};
    read(sockfd, buffer, sizeof(buffer) - 1);

    printf("Response: '%s'\n", buffer);

    close(sockfd);
    return EXIT_SUCCESS;
}

Программа использует SO_BINDTODEVICEдля привязки одного из моих сетевых интерфейсов(enp0s3). Затем он подключается к одному из серверов Google, выполняет простой HTTP-запрос и печатает первые несколько байтов ответа.

Вот пример запуска:

$./a.out
Response: 'HTTP/1.1 200 OK'
2
28.07.2021, 11:33

Теги

Похожие вопросы