Mangled and ACCEPTed NFQUEUE packets getting dropped

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



As part of a packet processor I am working on in my research, I often
want to turn a packet around, by swapping its source and destination
addresses and ports.  I have reduced what I want to accomplish down to a
small test program, which I have attached, that is more or less a UDP
echo service.  Packets that go through NFQUEUE 1 should be sent right
back unchanged save for direction.

I set up a rule to match all UDP packets destined for port 20000, then
connected with `socat udp:localhost:20000 -'.  Whatever I sent came back
correctly.  However, when I went to another machine and used `socat
udp:192.168.1.103:20000 0', everything dropped into the ether.  I know
the network and basic setup is correct, because the rule shown by
`iptables -L -v' still increments correctly, and the usual UDP echo
service works---my mangled packets simply aren't arriving back.

Because I see the expected behavior over the loopback device but not the
local network, I hypothesize that the problem is either that the MAC
addresses should also be reversed and aren't, or that the egress
interface needs to be recomputed and isn't, or something like that.
Basically, I suspect the kernel has made some sort of routing decision
before I change the addresses, and needs to update it based on the new
values but does not.  My question, then, is how do I set up my rules so
that my mangling is done in the right place and my packets are delivered
correctly?

I currently use the following rule:

$ iptables -t mangle -A INPUT -p udp --dport 20000 -j NFQUEUE
--queue-num 1

I had previously tried the usual filter table, but read somewhere that
one shouldn't mangle packets in that table, so I switched to the mangle
table.  I have also tried the PREROUTING chain rather than INPUT.  All
such variations produce the exact same behavior; it works on the
loopback but packets disappear when I introduce a second machine.
#include "rtcp.h"

#ifndef __FAVOR_BSD
#define __FAVOR_BSD
#endif

#ifndef __USE_BSD
#define __USE_BSD
#endif

#include <netinet/ip.h>
#include <netinet/udp.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <libnetfilter_queue/libnetfilter_queue.h>
#include <linux/netfilter.h>

static int bounce(struct nfq_q_handle *q, struct nfgenmsg *m,
                  struct nfq_data *d, void *a)
{
	int id;
	int ret;
	struct ip *ip;
	struct udphdr *udp;
	uint32_t addr;
	uint16_t port;

	(void)m;
	(void)a;

	id = ntohl((nfq_get_msg_packet_hdr(d))->packet_id);
	nfq_get_payload(d, (char **)&ip);
	udp = (struct udphdr *)((uint32_t *) ip + ip->ip_hl);

	addr = ip->ip_src.s_addr;
	ip->ip_src.s_addr = ip->ip_dst.s_addr;
	ip->ip_dst.s_addr = addr;

	port = udp->uh_sport;
	udp->uh_sport = udp->uh_dport;
	udp->uh_dport = port;
	
	ret = nfq_set_verdict(q, id, NF_ACCEPT, ntohs(ip->ip_len),
			      (unsigned char *)ip);
	if (ret < 0) {
		fputs("Error setting verdict.\n", stderr);
		exit(EXIT_FAILURE);
	}
	return ret;
}

int main(int argc, char **argv)
{
	char buf[1500];
	int fd;
	int ret;
	ssize_t size;
	struct nfq_handle *h;
	struct nfq_q_handle *q;

	(void)argc;
	(void)argv;

	h = nfq_open();
	if (!h) {
		fputs("Error opening the queue interface.\n", stderr);
		exit(EXIT_FAILURE);
	}
	/*
	 * From the netfilter mailinglist: "The entire unregistration
	 * stuff is a horrible hack, the only reason why it (still)
	 * exists is because registration of the same handler returns
	 * EEXIST instead of silently ignoring it. The best fix for now
	 * is to ignore the return value of nfq_unbind_pf()."
	 */
	nfq_unbind_pf(h, AF_INET);
	ret = nfq_bind_pf(h, AF_INET);
	if (ret < 0) {
		fputs("Error binding the queue handler.\n", stderr);
		exit(EXIT_FAILURE);
	}

	q = nfq_create_queue(h, 1, bounce, NULL);
	if (!q) {
		fputs("Error creating the incoming queue.\n", stderr);
		exit(EXIT_FAILURE);
	}
	ret = nfq_set_mode(q, NFQNL_COPY_PACKET, sizeof(buf));
	if (ret < 0) {
		fputs("Error setting up the incoming queue.\n", stderr);
		exit(EXIT_FAILURE);
	}

	fd = nfq_fd(h);
	while ((size = recv(fd, buf, sizeof(buf), 0)) > 0)
		nfq_handle_packet(h, buf, size);

	nfq_destroy_queue(q);
	nfq_close(h);
	return EXIT_SUCCESS;
}

[Index of Archives]     [Linux Netfilter Development]     [Linux Kernel Networking Development]     [Netem]     [Berkeley Packet Filter]     [Linux Kernel Development]     [Advanced Routing & Traffice Control]     [Bugtraq]

  Powered by Linux