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.
you can use a tun/tap device, can simply read it from python, for example:
tunnel.py
import os
from fcntl import ioctl
from select import select
import struct
import subprocess
TUNSETIFF = 0x400454ca
TUNMODE = 0x0001
tunFile = os.open("/dev/net/tun", os.O_RDWR)
ifs = ioctl(f, TUNSETIFF, struct.pack("16sH", "tun%d", TUNMODE))
ifname = ifs[:16].strip("\x00")
print "Allocated interface %s. Configure it and use it" % ifname
subprocess.call("ifconfig %s 192.168.13.1" % ifname,shell=True)
# Reading
def read():
r = select([tunFile], [], [])[0][0]
if r == tunFile:
return os.read(tunFile, 1600)
return None
# Writing
def write(buf):
os.write(tunFile, buf)
the full example can be found here
and route your packets to interface "tun0" or printed inetrface name.
linux distributions does not require to install anything but if you are on windows use this, and for mac os use this
EDIT 1 Note from here:
The difference between a tap interface and a tun interface is that a tap interface outputs (and must be given) full ethernet frames, while a tun interface outputs (and must be given) raw IP packets (and no ethernet headers are added by the kernel). Whether an interface functions like a tun interface or like a tap interface is specified with a flag when the interface is created.
for finding packet header information(like src, dst & etc ...) you may use dpkt
from dpkt import ip
from tunnel import read,write # tunnel.py
while 1:
data = read()
if data:
ipObj = ip.IP(data[4:])
print ipObj.src, ipObj.dst
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With