Отправьте UDP-данные с одного сетевого интерфейса и получите их на другом сетевом интерфейсе на том же компьютере.

Вопрос или проблема

У меня несколько сетевых интерфейсов на моем компьютере с Ubuntu.

======================

ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:caff:fe8e:4751  prefixlen 64  scopeid 0x20<link>
        ether 02:42:ca:8e:47:51  txqueuelen 0  (Ethernet)
        RX пакеты 765284  байты 41062277 (41.0 MB)
        RX ошибки 0  потеряно 0  превышение 0  фрейм 0
        TX пакеты 1184197  байты 5763037228 (5.7 GB)
        TX ошибки 0  потеряно 0 превышение 0  носитель 0  коллизии 0

eno1np0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.10.100.105  netmask 255.255.255.0  broadcast 10.10.100.255
        ether 3c:ec:ef:74:3b:16  txqueuelen 1000  (Ethernet)
        RX пакеты 31185342  байты 31349645842 (31.3 GB)
        RX ошибки 0  потеряно 0  превышение 0  фрейм 0
        TX пакеты 7674137  байты 1643490885 (1.6 GB)
        TX ошибки 0  потеряно 0 превышение 0  носитель 0  коллизии 0

eno2np1: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether 3c:ec:ef:74:3b:17  txqueuelen 1000  (Ethernet)
        RX пакеты 0  байты 0 (0.0 B)
        RX ошибки 0  потеряно 0  превышение 0  фрейм 0
        TX пакеты 0  байты 0 (0.0 B)
        TX ошибки 0  потеряно 0 превышение 0  носитель 0  коллизии 0

enp175s0f0np0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.0.10  netmask 255.255.0.0  broadcast 172.16.255.255
        ether 1c:34:da:7e:65:de  txqueuelen 1000  (Ethernet)
        RX пакеты 214905696  байты 268542904796 (268.5 GB)
        RX ошибки 0  потеряно 0  превышение 0  фрейм 0
        TX пакеты 75363  байты 11878888 (11.8 MB)
        TX ошибки 0  потеряно 0 превышение 0  носитель 0  коллизии 0

enp175s0f1np1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.16.0.11  netmask 255.255.0.0  broadcast 172.16.255.255
        ether 1c:34:da:7e:65:df  txqueuelen 1000  (Ethernet)
        RX пакеты 36352647  байты 40206354758 (40.2 GB)
        RX ошибки 0  потеряно 283164  превышение 0  фрейм 0
        TX пакеты 998  байты 114081 (114.0 KB)
        TX ошибки 0  потеряно 0 превышение 0  носитель 0  коллизии 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Локальный цикл)
        RX пакеты 3819602  байты 764575236 (764.5 MB)
        RX ошибки 0  потеряно 0  превышение 0  фрейм 0
        TX пакеты 3819602  байты 764575236 (764.5 MB)
        TX ошибки 0  потеряно 0 превышение 0  носитель 0  коллизии 0

======================

Я могу отправлять и получать датаграммы через сокеты UDP. Я использую код подобный этому
https://en.wikipedia.org/wiki/Berkeley_sockets в примерах клиент-сервер UDP.

Мой сервер работает на enp175s0f1np1 172.16.0.11

Я вижу, что мой RX увеличивается на enp175s0f1np1, когда я выполняю ‘watch -n,1 ifconfig’

TX (на любом сетевом интерфейсе) не увеличивается. Не знаю почему, я получаю данные без проблем.

Я думаю, что используется один и тот же сетевой интерфейс enp175s0f1np1 для TX и RX.

Как я могу сделать так, чтобы TX исходил от другого сетевого интерфейса enp175s0f0np0 172.16.0.10 ?

SO_BINDTODEVICE, похоже, предназначен для серверных сокетов, как показано здесь https://unix.stackexchange.com/a/648721/583986

Будет ли это работать и для клиентских сокетов?

Также есть идеи, почему TX не увеличивается, когда я принимаю данные?

Ответ или решение

Для отправки UDP-данных с одного сетевого интерфейса (NIC) и их приема на другом интерфейсе на одном и том же компьютере, необходимо учесть несколько аспектов, включая настройки сокетов и поведение операционной системы с сетевыми интерфейсами.

Настройка окружения

Ваше окружение с различными сетевыми интерфейсами на Ubuntu (eno1np0, eno2np1, enp175s0f0np0, enp175s0f1np1) представлено следующим образом:

  • eno1np0: IP – 10.10.100.105
  • enp175s0f0np0: IP – 172.16.0.10
  • enp175s0f1np1: IP – 172.16.0.11

Судя по вашему описанию, сервер работает на enp175s0f1np1 (IP: 172.16.0.11), и вы хотите отправлять данные с клиента через другой интерфейс enp175s0f0np0 (IP: 172.16.0.10).

Проблема и её анализ

Проблема с TX: Учитывая, что вы отправляете и получаете данные, но TX на интерфейсах не увеличивается, это может означать, что ваш клиент использует ту же NIC (enp175s0f1np1) для отправки, что и сервер. Чтобы переслать данные через интерфейс enp175s0f0np0, стоит явным образом указать, какой интерфейс использовать.

Как отправить UDP-данные через другой NIC

Для этого вы можете использовать SO_BINDTODEVICE для клиентского сокета, как и для серверного. Этот параметр позволяет связать сокет с конкретным сетевым интерфейсом.

Вот пример кода, который показывает, как это сделать:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <linux/if.h>
#include <sys/ioctl.h>

int main() {
    int sock;
    struct sockaddr_in server_addr;
    char *device = "enp175s0f0np0"; // Название интерфейса
    int send_result;

    // Создаем UDP сокет
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    // Привязываем сокет к интерфейсу
    if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)) < 0) {
        perror("SO_BINDTODEVICE failed");
        close(sock);
        exit(EXIT_FAILURE);
    }

    // Настраиваем адрес сервера
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080); // Порт сервера
    inet_pton(AF_INET, "172.16.0.11", &server_addr.sin_addr); // IP сервера

    // Отправляем данные
    const char *msg = "Hello, UDP server!";
    send_result = sendto(sock, msg, strlen(msg), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));
    if (send_result < 0) {
        perror("Send failed");
        close(sock);
        exit(EXIT_FAILURE);
    }

    printf("Message sent through %s\n", device);
    close(sock);
    return 0;
}

Объяснения по коду:

  • Создание сокета: Мы начинаем с создания UDP сокета.
  • Привязка к интерфейсу: С помощью setsockopt и SO_BINDTODEVICE мы указываем, что этот сокет будет использоваться через интерфейс enp175s0f0np0.
  • Настройка адреса сервера: Указываем IP-адрес сервера и порт, на который будем отправлять данные.
  • Отправка данных: Используем sendto, чтобы отправить сообщения серверу.

Заключение

Используя параметры SO_BINDTODEVICE, вы сможете успешно отправлять UDP-данные из одного интерфейса, а получать их через другой. Это должно помочь решить вашу текущую проблему и позволить вам управлять маршрутизацией UDP-пакетов на вашем сервере.

Если у вас есть дополнительные вопросы или проблемы, не стесняйтесь обращаться за помощью.

Оцените материал
Добавить комментарий

Капча загружается...