Я пишу UDP-прозрачный прокси на Rust, использую несколько крейтов
- сокет2=> Доступ к созданию RAW-сокета
- nix::sys::сокет=> Доступ ко всем низкоуровневым вызовам API сокетов
- std::net::сокет=> Доступ к стандартному разъему
Машина, которая должна действовать как прокси-сервер, была установлена в качестве шлюза для целевых машин.
На прокси-машине включены возможности перенаправления ядра Linux, см. фрагмент ниже.
#!/bin/sh
# Redirect packets coming to the computer
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv6.conf.all.forwarding=1
# Disable ICMP redirection
sysctl -w net.ipv4.conf.all.send_redirects=0
На машине также установлено NFTABLES
правило для перенаправления всего входящего трафика на прокси-программу, см. фрагмент ниже.
#!/bin/sh
# Setting up NFTABLES
nft add table filter
nft add chain filter divert "{ type filter hook prerouting priority -150; }"
nft add rule filter divert meta l4proto udp socket transparent 1 meta mark set 1 accept
#nft add table ip nat
#nft -- add chain ip nat prerouting { type nat hook prerouting priority -100 \; }
# Creating a new rule that redirects UDP traffic from P1-P2 to port 35
nft add rule filter divert udp dport 80-65525 tproxy to :35 meta mark set 1 accept
#nft add rule ip nat prerouting udp dport 80-65525 redirect to :35
Как вы можете видеть из закомментированных частей, я экспериментировал с обоими NAT PREROUTING
и TPROXY
. После обширных исследований и тестирования я обнаружил, что когда я использую NAT PREROUTING
свой прокси (который прослушивает порт 35), он получает ВЕСЬ входящий трафик от клиентов, но когда я читаю пакеты из сокета, исходный адрес назначения перезаписывается IP-адресом прокси-машины (чего я не хочу). Но если я вместо этого использую , TPROXY
я не получаю весь входящий трафик, я получаю только то, что «должна» видеть моя машина, например, широковещательный трафик и трафик, который имеет mac-адрес прокси-машины в качестве пункта назначения, это означает, что я не могу видеть весь трафик от подключенных клиентов.
Я выполнил(а) инструкции, данные вруководство по tproxy ядра Linux.
Следует отметить, что я устанавливаю IP_TRANSPARENT
.
Это то, что я установил в своей программе.
use socket2::{Socket, Domain, Type, Protocol};
use std::os::fd::{OwnedFd, RawFd, AsRawFd};
use nix::sys::socket::{
...
sockopt::{IpTransparent, Ipv4OrigDstAddr, Ipv4PacketInfo},
setsockopt,
...
};
...
let client_to_proxy_socket = match Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::UDP)) { ... };
let socket_ref: OwnedFd = client_to_proxy_socket.into();
let active: bool = true;
match setsockopt(&socket_ref, IpTransparent, &active) { ... }; // IP_TRANSPARENT
match setsockopt(&socket_ref, Ipv4OrigDstAddr, &active) { ... }; // SO_ORIGINAL_DST
match setsockopt(&socket_ref, Ipv4PacketInfo, &active) { ... }; // IP_PKTINFO
...
Говоря о том, что я пробовал, я должен сказать, что я перепробовал множество возможных комбинаций как правил, так и кода с вариациями и различными структурами.
Я собираюсь опубликовать краткий список ссылок, которые я уже посещал и читал много раз.
- Реализовать асинхронный сокет recvmsg для временных меток RX и TX в Rust
- Получить адрес назначения полученного UDP-пакета
- Перенаправление трафика на локальный прокси-сервер с помощью iptables
- Использование iptables TPROXY вместо REDIRECT
- Конфигурация IPTables для прозрачного прокси-сервера
Что касается моих ожиданий, то я ожидаю получить весь входящий трафик и перенаправить его TPROXY
на порт 35.
Я также разместил аналогичный вопрос на stackoverflow, но мне пришло в голову, что это не только вопрос программирования, но это может быть также вопрос сети/Linux.
Хочу отметить еще одну вещь: во время вчерашнего тестирования я обнаружил, что даже несмотря на то, что я отключил ICMP redirection
(см. первый фрагмент), я видел, что машина все равно отправляла сообщения о перенаправлении ICMP, считывая их через Wireshark, и это меня очень озадачило.