csocketsudpiptablestransparentproxy

Getting UDP Destination Address:Port from TPROXY'd Traffic


I'm trying to get the original destination from UDP packets that have been redirected by an iptables TPROXY rule. However several ways of doing this have failed, and using a raw socket to fish from the IP header seems to give me garbled packets.

The confusion is two fold really. The latter small packet is the UDP payload obtained by SOCK_DGRAM, however changing it to SOCK_RAW returns this garbled mess that contains no traces of the payload, nor IP header.

Wrong stuff:

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

Correct payload: Port: 0 -- ff ff ff ff 55 4b a1 d5 77 received 9 bytes

A correct raw packet that hasn't been affected by TPROXY looks like:

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.....

the FF-FF-FF-FF-5x is the start of the payload that is needed, and the port is there at 69CD.

Having moved to C/C++ socket code because C# didn't seem to have IP_TRANSPARENT flags that were apparently needed for TPROXY to work, I've tried setting socket options for IP_TRANSPARENT, IP_RECVORIGSTDADDR, IP_HDRINCL.

I changed from recvfrom to recvmsg and msghdrs, in order to try cycling through ancillary data to get the sin_port from the sockaddr_in result. I've pieced together lots of attempts from different SO questions, admittedly I don't really know what's going on at this level of socket code, things like cmsghdr, cmsg, msghdr, iovecs etc are all new to me. Nothing seems to work here, and whatever TPROXY is doing to those packets leaves them unusable anyway.

Here's what's grabbing the packet, printing the hex, and trying to get the port (it results in 0 no matter what)

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 rules are redirecting packets that match a hex string to the port this socket is bound to:

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

I just need to get the destination address:port, in order to forward traffic to conditionally forward it to where it was meant to be going, and send the result back. If I can do this using a UDP socket and somehow just get the payload, then get the port from some functions, fab. If I have to get the raw packet then fish it from the IP header, that's fine too, except I can't, because TPROXY seems to be garbling those packets.


Solution

  • I believe I was setting the iov_len to the size of a const char* by doing sizeof(buffer), thus getting essentially no writable space. Upon fiddling with declarations and ending up at this, I obtained the IP and port correctly with the cmsg loop. I still don't know why TPROXY warps the packets when viewing them through a raw socket, which would be interesting to know.

    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;
    }