Problem with netfilter queue, iptables or C ++

32 views Asked by At

I have a project to bypass some firewall rules. I tried to simplify this as much as possible to find the root of the problem but there was no luck. I have three machines , Machine A and Machine B and Machine C. Machine A sends tcp packets to Machine B on port 11799 that I will do port forwarding on Machine B, in order for the packets to get to Machine C.

Here, is my rules on Machine B :


iptables -t nat -A PREROUTING -p tcp --dport 11799 -j DNAT --to-destination "MACHINE_C_IP":11799
iptables -t nat -A POSTROUTING -p tcp --dport 11799 -j SNAT --to-source "MACHINE_B_IP"



And I did enable ipv4 forwarding on Machine B.

Now, on Machine C I have set up nfqueue and a C++ application to handle and manipulate packets.

Here is my iptables rule on Machine C :


iptables -t raw -A PREROUTING -p tcp --dport 11799 -j NFQUEUE --queue-num 800


and here is the code for my C++ application to bind to queue number 800.

#include <netinet/in.h>
#include <linux/netfilter.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h> 
#include <arpa/inet.h>
#include <iostream>
#include <cstring>
using namespace std;
unsigned short calculate_ip_checksum(struct iphdr *ip_header) {
    unsigned long cksum = 0;
    unsigned short *buffer = (unsigned short *)ip_header;
    int size = ip_header->ihl * 2 * sizeof(unsigned short); 
    while(size > 1) {
        cksum += *buffer++;
        size -= sizeof(unsigned short);
    }
    if(size) {
        cksum += *(unsigned char*)buffer;
    }
    cksum = (cksum >> 16) + (cksum & 0xffff);
    cksum += (cksum >> 16);
    return (unsigned short)(~cksum);
}
void compute_tcp_checksum(struct iphdr *pIph, unsigned short *ipPayload) {
    unsigned long sum = 0;
    unsigned short tcpLen = ntohs(pIph->tot_len) - (pIph->ihl<<2);
    struct tcphdr *tcphdrp = (struct tcphdr*)(ipPayload);
    sum += (pIph->saddr>>16)&0xFFFF;
    sum += (pIph->saddr)&0xFFFF;
    sum += (pIph->daddr>>16)&0xFFFF;
    sum += (pIph->daddr)&0xFFFF;
    sum += htons(IPPROTO_TCP);
    sum += htons(tcpLen);
    tcphdrp->check = 0;
    while (tcpLen > 1) {
        sum += * ipPayload++;
        tcpLen -= 2;
    }
    if(tcpLen > 0) {
        sum += ((*ipPayload)&htons(0xFF00));
    }
      while (sum>>16) {
          sum = (sum & 0xffff) + (sum >> 16);
      }
      sum = ~sum;
    tcphdrp->check = (unsigned short)sum;
}
static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
                     struct nfq_data *nfa, void *data)
{
    unsigned char* payload;
    int payload_len = nfq_get_payload(nfa, &payload);
    if (payload_len >= 0) {
        struct iphdr *ip_header = (struct iphdr *) payload;
        ip_header->check = 0;
        ip_header->check = calculate_ip_checksum(ip_header);

        ip_header->saddr = inet_addr("MACHINE B IP");
        ip_header->daddr = inet_addr("MACHONE C IP");

        if (ip_header->protocol == IPPROTO_TCP) {
            struct tcphdr* tcp_header = (struct tcphdr*) (payload + (ip_header->ihl * 4));
            compute_tcp_checksum(ip_header, reinterpret_cast<unsigned short*>(tcp_header));
        }
        for (int i = 0; i < payload_len; ++i) {
            cout << hex << static_cast<int>(payload[i]);
        }
        cout << endl;
    }
    int verdict = nfq_set_verdict(qh, ntohl(nfq_get_msg_packet_hdr(nfa)->packet_id), NF_ACCEPT, payload_len, payload);
    if (verdict < 0) {
        cerr << "Error: nfq_set_verdict() failed" << endl;
    }
    return verdict;
}
int main(int argc, char **argv)
{
    struct nfq_handle *h;
    struct nfq_q_handle *qh;
    int fd;
    int rv;
    char buf[4096] __attribute__ ((aligned));
    h = nfq_open();
    if (!h) {
        cerr << "Error: nfq_open() failed" << endl;
        return 1;
    }
    if (nfq_unbind_pf(h, AF_INET) < 0) {
        cerr << "Error: nfq_unbind_pf() failed" << endl;
        return 1;
    }  
    if (nfq_bind_pf(h, AF_INET) < 0) {
        cerr << "Error: nfq_bind_pf() failed" << endl;
        return 1;
    }
    qh = nfq_create_queue(h,  800, &callback, NULL);
    if (!qh) {
        cerr << "Error: nfq_create_queue() failed" << endl;
        return 1;
    }
    nfq_set_queue_maxlen(qh, 999999999);

    if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
        cerr << "Error: nfq_set_mode() failed" << endl;
        return 1;
    }
    fd = nfq_fd(h);
    while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
        nfq_handle_packet(h, buf, rv);
    }
    nfq_destroy_queue(qh);
    nfq_close(h);
    return 0;
}


As you can see this program will bind to queue 800 and practically changes nothing and re-injects the packet to the kernal to continue its path. There is a simple program listening on port 11799 TCP on machine C which will accept the packets and do the rest of the job.

This set-up works absolutely fine when I use these lines :

        ip_header->saddr = inet_addr("MACHINE B IP");
        ip_header->daddr = inet_addr("MACHONE C IP");

But when I change these lines to :

        ip_header->saddr = inet_addr("MACHINE A IP"); (source ip of packet)
        ip_header->daddr = inet_addr("MACHONE C IP"); (dest ip of packet)


the packets reach to C++ program but they don't reach the final program.

Now, I'm aware that changing the source ip of the packet might not work in various scenarios regarding network security. However, the odd thing is that when we use MACHINE A IP instead of MACHINE B IP in the process of packet manipulation on Machine C, the packet will reach the C++ program and I have confirmed this using printing statements but they don't reach the final program which is listening on port 11799 TCP

Apparently, it might be something wrong with my c++ code, or even the iptables kernal characteristics. Finally, I have reached a paradoxical point in this project where, the C++ code works fine in one situation but fails in the other one.

I need to change the source IP of packets when they reach to Machine C from Machine B for this project. What do you think might be the cause? What approaches do you suggest to troubleshoot this further?

Any word of advice would be appreciated. Thank you.

This set-up works absolutely fine when I use these lines :

        ip_header->saddr = inet_addr("MACHINE B IP");
        ip_header->daddr = inet_addr("MACHONE C IP");

But when I change these lines to :

        ip_header->saddr = inet_addr("MACHINE A IP"); (source ip of packet)
        ip_header->daddr = inet_addr("MACHONE C IP"); (dest ip of packet)


the packets reach to C++ program but they don't reach the final program.

0

There are 0 answers