我正在编写UDP代理,我正在使用原始套接字来创建与后端的“连接”。
代理有一系列端口,都指向一个端口(我的应用程序正在侦听的位置)。这个想法是当代理建立到后端的连接时,它将使用原始套接字来设置源端口,以便后端服务将响应源端口,然后将其转发到我的应用程序然后根据发送到的端口进行处理。
然而,我在开发方面遇到了障碍。我的sendto函数不返回任何错误并且不更改errno,并且检查代理上tcpdump中的流量显示正在发送的有效UDP数据包。但是,数据包似乎永远不会逃离网络并到达后端服务器。我知道这不是iptables问题,因为当我在不使用SOCK_RAW的情况下发送UDP数据包时,它没有问题并且到达后端
以下是代理
上的数据包转储19:09:57.379502 IP 192.12.88.14.63208 > 192.99.144.204.27015: UDP, length 20
0x0000: 4500 0030 24f7 0000 7011 bc7b c00c 580e E..0$...p..{..X.
0x0010: c063 90cc f6e8 6987 001c 0db9 ffff ffff .c....i.........
0x0020: 7110 c540 0130 3030 3030 3030 3030 3000 q..@.0000000000.
19:09:57.379653 IP 192.99.144.204.5004 > 199.21.77.4.27015: UDP, length 20
0x0000: 4500 0030 0000 4000 4011 d573 c063 90cc E..0..@.@..s.c..
0x0010: c715 4d04 138c 6987 001c 0db9 ffff ffff ..M...i.........
0x0020: 7110 c540 0130 3030 3030 3030 3030 3000 q..@.0000000000.
第一位是从客户端进入的数据包到.204,它是UDP代理。然后,UDP代理将其转发到.4,即后端。
然而,在后端方面,没有任何事情发生。
这是我到目前为止编写的代码
void relay_traffic(void)
{
int n, ret, t;
struct sockaddr_in cli_addr;
char *buf = (char *)malloc(2048), *pck;
unsigned short buf_seq;
struct raw_udp_hdr *udp_hdr;
struct estab_conn *conn;
len = sizeof (struct sockaddr_in);
while (1)
{
errno = 0;
t = time(NULL);
n = recvfrom(serv_fd, buf, 2048, 0, (struct sockaddr *)&cli_addr, &len);
if (n < 44)
{
bad_packets++;
continue;
}
pck = buf + 20;
udp_hdr = parse_udp_hdr(pck);
memcpy(&buf_seq, buf + 4, 2);
if (cli_addr.sin_addr.s_addr != backend_addr.sin_addr.s_addr)
{
if (udp_hdr->dst_port != htons(SERVER_PORT))
continue;
if (inspect_packet(n, buf) != 1)
{
bad_packets++;
continue;
}
conn = conn_list_recv_addr(cli_addr);
if (conn == NULL)
{
printf("Incoming connection\n");
conn = (struct estab_conn *)malloc(sizeof (struct estab_conn));
conn->cli_addr = cli_addr;
conn->cli_addr.sin_family = AF_INET;
conn->cli_addr.sin_port = udp_hdr->src_port;
conn->used_port = pop_available_port();
conn->next = NULL;
conn_list_push(conn);
}
conn->last_packet = t;
udp_hdr->src_port = htons(conn->used_port->port);
udp_hdr->dst_port = htons(SERVER_PORT);
ret = sendto(back_fd, pck, n - 20, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *)&backend_addr, sizeof (struct sockaddr_in));
if (errno != 0)
conn_list_pop(conn);
printf("(C) Sendto result: %d, errno: %d\n", ret, errno);
}
else
{
conn = conn_list_recv_port(udp_hdr->dst_port);
if (conn == NULL)
{
printf("Response from server, don't know where to route\n");
continue;
}
udp_hdr->src_port = htons(SERVER_PORT);
udp_hdr->dst_port = conn->cli_addr.sin_port;
ret = sendto(back_fd, pck, n - 20, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *)&conn->cli_addr, sizeof (struct sockaddr_in));
if (errno != 0)
conn_list_pop(conn);
printf("Sendto result: %d, errno: %d\n", ret, errno);
}
}
}
这些是udp辅助函数
struct raw_udp_hdr {
unsigned short src_port, dst_port, len, csum;
};
struct raw_udp_hdr form_udp_hdr(unsigned short, unsigned short, unsigned short);
struct raw_udp_hdr *parse_udp_hdr(char *);
struct raw_udp_hdr form_udp_hdr(unsigned short src_port, unsigned short dst_port, unsigned short len)
{
struct raw_udp_hdr udp_hdr;
udp_hdr.src_port = src_port;
udp_hdr.dst_port = dst_port;
udp_hdr.len = len;
udp_hdr.csum = 0;
return udp_hdr;
}
inline struct raw_udp_hdr *parse_udp_hdr(char *pck)
{
return (struct raw_udp_hdr *)pck;
}
提前感谢您的时间!
答案 0 :(得分:1)
我弄明白为什么错误
由于我在再次发送之前更改了数据包中的字段,因此udp标头的校验和无效。我猜测某个路由器认为UDP数据包已损坏,或者内核可能会在线路末端丢弃该数据包时,数据包在某处丢弃了。
快速n脏解决方案只是将csum设置为0,以便内核在通过网络发送之前为我填写校验和。将来我将使用有效的校验和算法