我正在尝试编写一个透明代理,将任意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盒子。)谢谢!
答案 0 :(得分:1)
好的,所以我得到了半个答案。
事实证明,如果我只使用RAW IP套接字,打开IP_HDRINCL选项,并在用户空间中使用完整IP标头构建传出UDP数据包,内核将遵循非本地源地址并发送数据包那样。
我现在正在使用第三个套接字sock_output,并且解码的UDP数据包正确输出。 (有趣的是:UDP校验和字段必须为零或正确的校验和值。任何其他原因都会导致内核静默丢弃数据包,并且您永远不会看到它出去。内核不会出现问题。如果你将其清零,请为你填写适当的校验和,但如果你指定它,它将验证它是否正确。不会以这种方式发送故意错误校验和的UDP。)
所以问题的前半部分仍然存在:当我从sock_clear读取带有MSG_PROXY标志的数据包到recvfrom()时,为什么我没有在sdaddr的后半部分获得实际的目标地址?