I am trying to write an iptables
rule that will redirect all outgoing UDP packets to a local socket, but I also need the destination information. I started out with
sudo iptables -t nat -A sshuttle-12300 -j RETURN --dest 127.0.0.0/8 -p udp
sudo iptables -t nat -A sshuttle-12300 -j REDIRECT --dest 0.0.0.0/0 -p udp --to-ports 15000
And that's great, now I can get all outgoing UDP packets by using a socket on port 15000.
Now, I need the destination information (target host and port number) so a simple UDP socket isn't enough; need a raw socket so that it gets the full IP header.
However, as it turns out, the packets received seem to be addressed for localhost:15000
. This makes sense because that's where the socket is, but that's not what I want; I want the host/port before the packet was redirected by iptables
.
Googling led to this question, with the answer suggesting two approaches: TPROXY
and SO_ORIGINAL_DST
, recommending the former, so that's what I tried to go with.
Added the iptables
rule for TPROXY
:
sudo iptables -t mangle -A PREROUTING -j TPROXY --dest 0.0.0.0/0 -p udp --on-port 15000
Reading from tproxy.txt, we need to create a listening socket with the IP_TRANSPARENT
option (this is done as root):
from socket import *
s = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)
# The IP_TRANSPARENT option isn't defined in the socket module.
# Took the value (19) from the patch in http://bugs.python.org/issue12809
s.setsockopt(SOL_IP, 19, 1)
s.bind(('0.0.0.0', 15000))
s.recv(4096) # Will hang until it receives a packet
Alright, now let's write another script to generate a test packet to see if anything happens:
from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.connect(('192.168.1.1', 9001))
s.send('hello')
But then nothing happens on the receiving side. The recv
call seems to hang, not receiving any data.
So, the overall question is either:
TPROXY
rule?or
Edit: I should insist that I'd like to redirect (therefore intercept) the packets, not just inspect them as they go through.
I found your question interesting.
The following solution is based on marking the UDP traffic generated by the host and re-routing it back to the local host application. At the application, a UDP socket should be used to read the data, even one that is not destined for the host itself (see below how).
iptables -A OUTPUT -t mangle -p udp -j MARK --set-mark 1 ip rule add fwmark 1 lookup 100 ip route add local 0.0.0.0/0 dev lo table 100
#ifndef IP_TRANSPARENT #define IP_TRANSPARENT 19 #endif int val = 1; setsockopt(sockfd, SOL_IP, IP_TRANSPARENT, &val, sizeof(val));
You should be able now to read from the socket. Tip form Etienne Perot: For accepting all UDP traffic, bind to 0.0.0.0.
What I found here very interesting, is that locally generated traffic (and not routed one) may be classified and re-routed using iptables and route rules.
Hope this helps.