Зависит от механизма перенаправления. Если вы используете REDIRECT (который является NAT под капотом), то вам нужно использовать SO_ORIGINAL_DST или libnetfilter_conntrack, чтобы запросить исходное назначение соединения до применения NAT. Однако, поскольку вы можете обслуживать несколько соединений с одним и тем же сокетом слушателя, этот поиск должен быть выполнен для каждого пакета.
Вы можете поэкспериментировать с libnetfilter_conntrack и сервисами, которые он предоставляет, используя инструмент командной строки conntrack.
Альтернативой является использование TPROXY для перенаправления, которое предназначалось для использования в подобных случаях. Там вы можете получить исходный пункт назначения пакета, используя вспомогательное сообщение, используя recvmsg (). Ключ, который нужно искать, это IP_RECVORIGDST setsockopt.
Дополнительную информацию о TPROXY можно найти в каталоге документации ядра, в файле с именем tproxy.txt. Его немного сложно использовать, но он работает более надежно, так как он реализован стеком, а не подсистемой фильтрации пакетов.
Отредактировано: , чтобы добавить способ запроса адресов назначения UDP с помощью TProxy.
- открыть сокет UDP, привязать его к 0.0.0.0 или к более конкретному IP
- вы включаете IP_RECVORIGDST через setsockopt (fd, SOL_IP, IP_RECVORIGDSTADDR, ...)
- вы используете recvmsg () вместо recvfrom () / recv () для получения кадров
- recvmsg () вернет пакет и серию вспомогательных сообщений,
- переберите вспомогательные сообщения и найдите блок CMSG с уровнем SOL_IP, индекс IP_ORIGDSTADDR
- этот блок CMSG будет содержать struct sockaddr_in с информацией об IP и порте.
Отредактировано: SO_ORIGINAL_DST против udp
SO_ORIGINAL_DST должен работать с сокетами udp, однако ядро не позволяет указывать конечные точки подключения, оно будет использовать сокет, для которого вы вызываете SO_ORIGINAL_DST, для получения этой информации об адресе.
Это означает, что он будет работать, только если UDP-сокет правильно связан (с перенаправленным адресом / портом) и подключен (к рассматриваемому клиенту). Ваш сокет слушателя, вероятно, связан с 0.0.0.0 и обслуживает не один, а несколько клиентов.
Однако вам не нужно использовать фактический сокет прослушивателя для запроса адреса назначения. Поскольку UDP не передает дейтаграммы при установлении соединения, вы можете создать новый сокет UDP, связать его с адресом перенаправления и подключить его к клиенту (чей адрес вы знаете с тех пор, как он отправил первый пакет вашему слушателю в любом случае). Затем вы можете использовать этот сокет для запуска SO_ORIGINAL_DST, однако есть и недостатки:
- как только вы откроете такой сокет, ядро предпочтет, что если клиент отправит дополнительные пакеты после первого, а не ваш сокет слушателя
- это по своей сути очень неестественно, поскольку к тому времени, когда у вашего приложения появится возможность вызвать SO_ORIGINAL_DST, срок действия записи conntrack может истечь.
- это медленно и много накладных расходов
Метод на основе TProxy явно лучше.