linuxnetwork-programmingdnsdnsmasqtransparentproxy

Dnsmasq not receiving response after TPROXY intercept


I am developing a 'monitor the traffic' kind of an application on the router, where I use the TPROXY feature to intercept the DNS packet & send it to my application server listening on a port. After processing, I forward the packet to the actual destination (i.e., dnsmasq) after modifying the TTL.

JFYI, my firewall rule to TPROXY forward the DNS Response packets to my application server listening on port 2345 looks like this:

iptables -t mangle -A PREROUTING -i <WAN-INTERFACE> -p udp --sport 53 -j TPROXY --tproxy-mark 0x3 --on-port 2345

At my application server, without the error checks:

sock_fd = socket(AF_INET, SOCK_DGRAM, 0 );

setsockopt(socket_fd, SOL_IP, IP_PKTINFO, &enabled, sizeof(int));
setsockopt(socket_fd, SOL_IP, IP_TRANSPARENT, &enabled, sizeof(int));
setsockopt(socket_fd, SOL_IP, IP_RECVORIGDSTADDR, &enabled, sizeof(int));
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(int));

/* client_addr points to the source IP (i.e. upstream DNS server's IP) */
bind(sock_fd, (const struct sockaddr *)client_addr, sizeof(struct sockaddr));

/* dst_addr points to the router IP on the WAN interface */
sendto(sock_fd, dns_packet_buffer, data_len, 0,
            (const struct sockaddr *)dst_addr, sizeof(struct sockaddr));

This sendto succeeds, i.e., no error!!! But, dnsmasq does not receive the data! To be more precise, the fd on which dnsmasq is waiting for data does not become "ready."

At the dnsmasq code, inside check_dns_listeners

for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
    if (FD_ISSET(serverfdp->fd, set))
        reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now);

the FD_ISSET() returns false. If I do not intercept the DNS response flow then this FD_ISSET() returns true. What am I missing here?


Solution

  • Finally I found the answer!! Lemme put it here suppose it is helpful for anybody else.

    As I had mentioned earlier, my application was running on the router. The router manufacturers had modified the existing dnsmasq code to add an additional option to limit the interface on which they listen from the upstream server! In other words, they accept responses from the upstream server only via a given interface (like eth2). From the code perspective, they don't even listen on other interfaces other than eth2! Since my response was coming via 'lo' they weren't listening!! :)

    I restarted the dnsmasq without that option and viola it works! :)

    I wish they had documented it on a public forum! So that generic googling works and not reading through 1000s lines of code!!