Reputation: 1598
I'm writing a UDP proxy, and I'm using raw sockets in order to create a "connection" to the backend.
The proxy has a range of ports that all point to a single port (where my application is listening). The idea is that when the proxy establishes a connection to the backend, it will use a raw socket in order to set the source port, so that the backend service will respond to the source port, which will then be forwarded to my application and then processed based on what port it was sent to.
However, I've hit a snag in development. My sendto function returns no errors and does not change errno, and inspecting the traffic in tcpdump on the proxy shows a valid UDP packet being sent over. However, the packet never seems to escape the network and reach the backend server. I know it's not an iptables issue because when I send a UDP packet without using SOCK_RAW, it works with no problem and reaches the backend
Here is the packet dump on the proxy
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 [email protected].
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..@[email protected]..
0x0010: c715 4d04 138c 6987 001c 0db9 ffff ffff ..M...i.........
0x0020: 7110 c540 0130 3030 3030 3030 3030 3000 [email protected].
The first bit is the packet coming in from the client, to .204, which is the UDP proxy. Then, the UDP proxy forwards it to .4, which is the backend.
However, on the backend's side, nothing ever comes through.
Here is the code I've written so far
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);
}
}
}
These are the udp helper functions
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;
}
Thanks in advance for your time!
Upvotes: 2
Views: 200
Reputation: 1598
I figured out why it was erroring out
Since I was changing fields in the packet before sending it off again, the udp header's checksum was invalid. I'm guessing that the packet got dropped somewhere along the road by some router thinking that the UDP packet was damaged, or perhaps the kernel dropped it at the end of the line.
The quick n dirty solution was just to set the csum to 0 so that the kernel would fill in the checksum for me before it was sent out across the network. In the future I'll be using a valid checksum algo
Upvotes: 1