C原始套接字包看起来很好,但不会逃脱网络

时间:2014-09-24 23:27:02

标签: c sockets networking

我正在编写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;
}

提前感谢您的时间!

1 个答案:

答案 0 :(得分:1)

我弄明白为什么错误

由于我在再次发送之前更改了数据包中的字段,因此udp标头的校验和无效。我猜测某个路由器认为UDP数据包已损坏,或者内核可能会在线路末端丢弃该数据包时,数据包在某处丢弃了。

快速n脏解决方案只是将csum设置为0,以便内核在通过网络发送之前为我填写校验和。将来我将使用有效的校验和算法