MSG_PROXY не работает для предоставления / указания альтернативных адресов для прозрачного прокси
Я пытаюсь написать прозрачный прокси, который переводит произвольные пакеты UDP в пользовательский протокол и обратно. Я пытаюсь использовать прозрачное проксирование для чтения входящих пакетов UDP, которые нуждаются в преобразовании, и для записи исходящих пакетов UDP, которые только что были подвергнуты обратному преобразованию.
Моя настройка сокета, который я использую для обоих разновидностей сокетов UDP, выглядит следующим образом:
static int
setup_clear_sock(uint16_t proxy_port)
{
struct sockaddr_in saddr;
int sock;
int val = 1;
socklen_t ttllen = sizeof(std_ttl);
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0)
{
perror("Failed to create clear proxy socket");
return -1;
}
if (getsockopt(sock, IPPROTO_IP, IP_TTL, &std_ttl, &ttllen) < 0)
{
perror("Failed to read IP TTL option on clear proxy socket");
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
{
perror("Failed to set reuse address option on clear socket");
return -1;
}
if (setsockopt(sock, IPPROTO_IP, IP_TRANSPARENT, &val, sizeof(val)) < 0)
{
perror("Failed to set transparent proxy option on clear socket");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_port = htons(proxy_port);
saddr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
{
perror("Failed to bind local address to clear proxy socket");
return -1;
}
return sock;
}
У меня есть две разные, но, возможно, связанные проблемы. Во-первых, когда я читаю входящий пакет UDP из этого сокета, используя этот код:
struct sock_double_addr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port_a;
struct in_addr sin_addr_a;
sa_family_t sin_family_b;
in_port_t sin_port_b;
struct in_addr sin_addr_b;
unsigned char sin_zero[sizeof(struct sockaddr) - __SOCKADDR_COMMON_SIZE - 8
- sizeof(struct in_addr) - sizeof(in_port_t)];
};
void
handle_clear_sock(void)
{
ssize_t rcvlen;
uint16_t nbo_udp_len, coded_len;
struct sockaddr_in saddr;
struct sock_double_addr_in sdaddr;
bch_coding_context_t ctx;
socklen_t addrlen = sizeof(sdaddr);
rcvlen = recvfrom(sock_clear, &clear_buf, sizeof(clear_buf),
MSG_DONTWAIT | MSG_PROXY,
(struct sockaddr *) &sdaddr, &addrlen);
if (rcvlen < 0)
{
perror("Failed to receive a packet from clear socket");
return;
}
....
Я не вижу адрес назначения вернуться в sdaddr. Поля sin_family_b, sin_addr_b и sin_port_b равны нулю. Я сделал дамп памяти блока структуры в gdb, и, действительно, байты возвращаются из ядра в ноль (это неплохое размещение поля в моем определении структуры).
Временно работая над этим путем жесткого кодирования фиксированного IP-адреса и порта для целей тестирования, я могу отлаживать оставшуюся часть моего прокси-приложения до тех пор, пока не доберусь до точки отправки исходящего пакета UDP, который был только что подвергнут обратному преобразованию. Это происходит с этим кодом:
....
udp_len = ntohs(clear_buf.u16[2]);
if (udp_len + 6 > decoded_len)
fprintf(stderr, "Decoded fewer bytes (%u) than outputting in clear "
"(6 + %u)!\n", decoded_len, udp_len);
sdaddr.sin_family = AF_INET;
sdaddr.sin_port_a = clear_buf.u16[0];
sdaddr.sin_addr_a.s_addr = coded_buf.u32[4];
sdaddr.sin_family_b = AF_INET;
sdaddr.sin_port_b = clear_buf.u16[1];
sdaddr.sin_addr_b.s_addr = coded_buf.u32[3];
if (sendto(sock_clear, &(clear_buf.u16[3]), udp_len, MSG_PROXY,
(struct sockaddr *) &sdaddr, sizeof(sdaddr)) < 0)
perror("Failed to send a packet on clear socket");
}
и пакет никогда не обнаруживается. Я проверил все содержимое созданной структуры sdaddr, и все поля выглядят хорошо. Данные полезной нагрузки UDP выглядят хорошо. Нет возврата от системного вызова sendto() - действительно, он возвращает ноль. И пакет никогда не обнаруживается в wireshark.
Так что же происходит с моим прозрачным прокси? Как мне заставить это работать? (FWIW: узел разработки - это стандартная версия x86_64 Ubuntu 14.04 LTS.) Спасибо!
1 ответ
Хорошо, у меня есть половина ответа.
Оказывается, если я просто использую сокет RAW IP с включенной опцией IP_HDRINCL и создаю исходящий пакет UDP в пользовательском пространстве с полным заголовком IP, ядро будет учитывать нелокальный адрес источника и отправлять пакет таким образом.
Сейчас я использую третий сокет sock_output для этой цели, и декодированные UDP-пакеты выводятся правильно. (Интересное примечание: поле контрольной суммы UDP должно быть либо нулем, либо правильным значением контрольной суммы. Все остальное заставляет ядро молча отбрасывать пакет, и вы никогда не увидите, как он выходит. Ядро не заполнит правильное контрольная сумма для вас, если вы обнуляете ее, но если вы укажете ее, она проверит, что она правильная. Никакой отправки UDP с намеренно неверными контрольными суммами таким образом не будет.)
Итак, первая половина вопроса остается: когда я читаю пакет из sock_clear с флагом MSG_PROXY в recvfrom(), почему я не получаю фактический адрес назначения во второй половине sdaddr?