首页 > 解决方案 > 获取 UDP 目标地址:来自 TPROXY 流量的端口

问题描述

我正在尝试从 iptables TPROXY 规则重定向的 UDP 数据包中获取原始目的地。然而,这样做的几种方法都失败了,并且使用原始套接字从 IP 标头中钓鱼似乎给了我乱码的数据包。

困惑真的是双重的。后一个小数据包是 SOCK_DGRAM 获得的 UDP 有效负载,但是将其更改为 SOCK_RAW 会返回此乱码,其中不包含有效负载的痕迹,也不包含 IP 标头。

错误的东西:

Port: 0 -- 45 00 00 48 1c ac 00 00 6e 11 7b f6 1b 46 8b 53 8b 63 82 06 69 7d 69 b4 00 34 29 5f 00 ba 03 00 47 20 01 00 30 2b ca 00 01 7d 8d 58 e8 89 88 0e 60 7f ee 00 c0 0b e3 01 00 00 00 00 00 00 00 06 0d e3 01 80 83 06 85 80 received 74 bytes

正确的有效载荷:Port: 0 -- ff ff ff ff 55 4b a1 d5 77 received 9 bytes

未受 TPROXY 影响的正确原始数据包如下所示:

45-00-00-65-17-4B-40-00-3A-11-46-F8-2D-23-01-BA-4E-9F-64-C9-69-CD-26-8F-00-51-C2-C9-FF-FF-FF-FF-52-4C-20-30-36-2F.....

FF-FF-FF-FF-5x是所需负载的开始,端口在 69CD 处。

由于 C# 似乎没有 TPROXY 工作显然需要的 IP_TRANSPARENT 标志,因此转移到 C/C++ 套接字代码后,我尝试为 IP_TRANSPARENT、IP_RECVORIGSTDADDR、IP_HDRINCL 设置套接字选项。

我从 recvfrom 更改为 recvmsg 和 msghdrs,以便尝试循环查看辅助数据以从 sockaddr_in 结果中获取 sin_port。我已经从不同的 SO 问题中拼凑了很多尝试,诚然我真的不知道在这个级别的套接字代码上发生了什么,像 cmsghdr、cmsg、msghdr、iovecs 等对我来说都是新的。这里似乎没有任何效果,无论 TPROXY 对这些数据包做什么,它们都无法使用。

这是抓取数据包,打印十六进制并尝试获取端口的内容(无论如何它都会导致 0)

recvlen = recvmsg(fd, &message, 0);
printf("received %d bytes\n", recvlen);

for (cmsghdr *cmsg = CMSG_FIRSTHDR(&message); cmsg != NULL; cmsg = CMSG_NXTHDR(&message, cmsg))
    {
        if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_ORIGDSTADDR) continue;
       printf("copying port\n");
        std::memcpy(&dstIpAddr, CMSG_DATA(cmsg), sizeof(sockaddr_in));
        dstPort = ntohs(dstIpAddr.sin_port);
    }

    printf("Port: %i -- ", dstPort);
    //std::cout << "Destination port: " << dstPort << std::endl;
    for (int i = 0; i < recvlen; i++)
    {
        printf("%02x ", buf[i]);
    }

iptables 规则将匹配十六进制字符串的数据包重定向到此套接字绑定到的端口:

TPROXY     udp  --  anywhere             anywhere             udp dpts:27000:28000 STRING match  "|ffffffff54|" ALGO name bm TO 65535 TPROXY redirect 0.0.0.0:29000 mark 0x1/0x1
TPROXY     udp  --  anywhere             anywhere             udp dpts:27000:28000 STRING match  "|ffffffff55|" ALGO name bm TO 65535 TPROXY redirect 0.0.0.0:29000 mark 0x1/0x1
TPROXY     udp  --  anywhere             anywhere             udp dpts:27000:28000 STRING match  "|ffffffff56|" ALGO name bm TO 65535 TPROXY redirect 0.0.0.0:29000 mark 0x1/0x1
TPROXY     udp  --  anywhere             anywhere             udp dpts:27000:28000 STRING match  "|ffffffff57|" ALGO name bm TO 65535 TPROXY redirect 0.0.0.0:29000 mark 0x1/0x1

我只需要获取目标地址:端口,以便转发流量以有条件地将其转发到它打算去的地方,并将结果发回。如果我可以使用 UDP 套接字执行此操作并以某种方式获取有效负载,然后从某些函数获取端口,fab。如果我必须获取原始数据包然后从 IP 标头中获取它,那也很好,但我不能,因为 TPROXY 似乎在混淆这些数据包。

标签: csocketsudpiptablestransparentproxy

解决方案


相信const char*我通过do 将 iov_len 设置为 a 的大小sizeof(buffer),因此基本上没有可写空间。在摆弄声明并结束后,我使用 cmsg 循环正确获得了 IP 和端口。我仍然不知道为什么 TPROXY 在通过原始套接字查看数据包时会扭曲数据包,这会很有趣。

iov.iov_base = cPacket;
iov.iov_len  = BUFSIZE;

mHeader.msg_name       = &sSourceAddr; //sockaddr_storage
mHeader.msg_namelen    = sizeof(struct sockaddr_in);
mHeader.msg_iov        = &iov;
mHeader.msg_iovlen     = 1;
mHeader.msg_control    = cControl;
mHeader.msg_controllen = BUFSIZE;

for (cmsghdr *cmsg = CMSG_FIRSTHDR(&mHeader); cmsg != NULL; cmsg = CMSG_NXTHDR(&mHeader, cmsg))
{
    if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_ORIGDSTADDR) continue;
    std::memcpy(&sDestIP, CMSG_DATA(cmsg), sizeof(sockaddr_in));
    iPort = ntohs(sDestIP.sin_port);
    sAddress = sDestIP.sin_addr;
}

推荐阅读