So, iptables basically remembers the
port number that was used for the
outgoing packet (what else could it
remember for a UDP packet?),
I am pretty sure for UDP the source and destination ports and addresses are stored.
If you want to inspect the state tables install conntrack and/or netstat-nat.
(What would happen, if I accidentally
tried to start a service on that port
within the timeframe - would that
attempt be denied/blocked?)
Since you are using OUTPUT and INPUT your are talking about local services. The port is already used I don't believe your system will allow you to start up another service since something is already listening on that port. I guess you could stop the first service and start another if you really wanted to though, in that case the response would probably get to your service. What the service does with the packet depends on what the contents of the packet is, and what service it is.
You can achieve your goal using (abusing) iptables
, to be more specific: connbytes
match and NFQUEUE
target. connbytes
allows you to match the Nth packet in the connection and NFQUEUE
is a mechanism for passing packets matching an iptables rule to userspace program. Furthermore: you'll have to use some program which whill be receiving relevant packets from the kernel and processing them.
iptables
I'm assuming here that you are interested in capturing the packets server-side (that can be changed if you are interested in client-side capturing). In that case we'll need to capture the 3-rd incoming packet for each connection (i.e. the first incoming packet after the three-way handshake) and put the packet in a netfilter queue (queue #1 in this case).
iptables -I INPUT -p tcp -m tcp --dport 12345 -m connbytes --connbytes-mode packets --connbytes-dir original --connbytes 3:3 -j NFQUEUE --queue-num 1
As soon as a packet matches this rule, it will be passed to the userspace program bound to the queue #1. The program can then examine the packet and afterwards decide to accept it or drop it.
The program
You will need a program which will receive the packets in userspace using the libnetfilter_queue
library. Bindings for the library are available in different languages. The following is a sample program written in python:
import struct
from netfilterqueue import NetfilterQueue
def ip_to_string(ip):
return ".".join(map(lambda n: str(ip>>n & 0xff), [24,16,8,0]))
def print_and_accept(pkt):
pl = pkt.get_payload()
src_ip = struct.unpack('>I', pl[12:16])[0]
tcp_offset = (struct.unpack('>B', pl[0:1])[0] & 0xf) * 4
tmp = struct.unpack('>B', pl[tcp_offset+12:tcp_offset+13])[0]
data_offset = ((tmp & 0xf0) >> 4) * 4
src_port = struct.unpack('>H', pl[tcp_offset+0:tcp_offset+2])[0]
data = pl[tcp_offset + data_offset:]
print 'from {}:{}, "{}"'.format(ip_to_string(src_ip), src_port, data)
pkt.accept()
nfqueue = NetfilterQueue()
nfqueue.bind(1, print_and_accept)
try:
nfqueue.run()
except KeyboardInterrupt:
print
The program assumes that the queued packets will be IPv4 TCP packets and prints the source ip:port pair and the TCP payload of the packet.
Caveats
- You can never be sure that the first data packet will have the complete TLS client hello - TCP can fragment the stream as it likes and it is not impossible to receive a single byte in the first data packet.
- TCP Fast Open will break the logic of this approach. If it is enabled, the initial three-way handshake may already transfer data. But TFO is disabled by default on almost every device for now.
- Care must be taken when using
NFQUEUE
target: if the userspace program bound to the queue hangs, crashes or is slow to process packets, those will be dropped/stuck and the service bound to the specified port will become unreachable. You can pass the --queue-bypass
option to the NFQUEUE
target to ACCEPT
the matched packets if no userspace program is bound to the specified queue: this should help with the problem of the program crashing but won't help with a hung or slow program.
Best Answer
For the iptables solution, you'll basically be doing an destination NAT on the packets. Something like:
iptables -t nat -I PREROUTING -p udp --dport 12345 -j DNAT --to 192.168.1.128:12345
With netcat, hmm. You can use the
-k
option to keep the listen side up after you process the packets, but you'll need to do something to keep sending. Named pipes, maybe?Untested, clearly.