Multicast from tcp replay seen by wireshark but not by application - python

I have got a multicast packet capture I'm playing with tcpreplay:
sysctl net.ipv4.conf.all.rp_filter=0
sysctl net.ipv4.conf.eth0.rp_filter=0
tcpreplay -i eth0 --loop=100 new.pcap
I watch the traffic on eth0 with wireshark and I can see the packets I'm interested in (let's say 224.0.23.60:4937).
But the following python app cannot find the packets:
import socket
import struct
MCAST_GRP = '224.0.23.60'
MCAST_PORT = 4937
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((MCAST_GRP, MCAST_PORT)) # use MCAST_GRP instead of '' to listen only
# to MCAST_GRP, not all groups on MCAST_PORT
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
print '#'
print sock.recv(64)
netstat -g is giving the following output:
lo 1 all-systems.mcast.net
eth0 1 224.0.23.60
Am I missing something here ?
[Edit] I should precise that the ip src in my packet capture is not in the same network subdomain (ip src: 192.168.1.10) whereas my ip is something like 146.186.197.164.

After reading carefully the documentation (http://tcpreplay.synfin.net/wiki/FAQ), it seems that tcpreplay sends the packets between the TCP/IP stack and the ethernet device driver, therefore the TCP/IP stack of the host system never sees the packets.
I ended up using a debian Os with virtual box configured with the host only adapter and use tcpreplay in that machine.

Now, it is mentioned clearly on the FAQ page.
https://tcpreplay.appneta.com/wiki/faq.html#can-i-send-packets-on-the-same-computer-running-tcpreplay
Q: Can I send packets on the same computer running tcpreplay?
Generally speaking no. When tcpreplay sends packets, it injects them
between the TCP/IP stack of the system and the device driver of the
network card. The result is the TCP/IP stack system running tcpreplay
never sees the packets.
One suggestion that has been made is using something like VMWare,
Parallels or Xen. Running tcpreplay in the virtual machine (guest)
would allow packets to be seen by the host operating system.

Related

How to set multicast address to receive from directly

I have a server(Running on Windows 10) and client (Running on Raspbian) communicating with each other and I'm having issues receiving multicast data from a client device.
I believe I've isolated the problem on my server. The problem is that when I set up the receive socket on my server, the socket only receives multicast data from it's only IP address only, the multicast address is not added even though I've configured it to do so. Currently the code I use to set up my receive socket is...
def _init_recv_socket(self):
self.recieve_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
self.recieve_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.recieve_socket.bind(('', MCAST_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
self.recieve_socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
I read online that using socket.INADDR_ANY is dangerous because it won't always set up to the correct interface. I called
print socket.gethostbyname_ex(socket.gethostname())
and it displayed what address I was receiving from and my multicast address was not displayed. The output was:
('MSI', [], ['192.168.99.1])
I found online how to set the multicast and interface address only getting the same output:
group = socket.inet_atons(MCAST_GRP)
iface = socket.inet_atons('192.168.99.1')
self.recieve_socket.setsocketopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, group+iface)
My multicast still isn't showing up when running the gethostbyname command.
I'm stuck and not sure how to get my server working to recieve multicast data...
Note: The same code works fine on my raspberry pi, just not on my windows.

Can't perform TCP-handshake through a NAT between two NICs with SO_BINDTODEVICE

I'm trying to connect my computer to both sides of a NAT (run by OpenWRT) and to establish a TCP connection through the NAT:
I run a DHCP server on my first NIC (eth0, ip address 129.104.0.1) and connect it to the WAN port of the router (ip address 129.104.0.198)
I connect my wifi (wlan0, ip address 192.168.1.119) to the router's SSID behind the NAT
I'm using python and the SO_BINDTODEVICE option to send packet between a server (on eth0) and a client (on wlan0) through the NAT:
For the server:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind((str(self.local_ip_addr),self.handler.port))
self.server.setsockopt(socket.SOL_SOCKET,25,self.iface.name+"\0")
self.server.listen(10)
while self.stopped() is False:
connect = self.server.accept()[0]
connect.settimeout(1)
connect.close()
self.server.close()
For the client:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, 25, self.iface.name + "\0")
sock.settimeout(1)
try:
sock.connect((self.dest,self.handler.port))
sock.close()
expect socket.timeout, socket.error as e:
return -1
My problem is that the connection times out before. I wiresharked both my interfaces and it seems the problem resides on the client's side:
wlan0 sends a TCP SYN packet to 129.104.0.1
The packet is correctly NATed by the router and is received from 129.104.0.198 by eth0
eth0 replies with a SYN,ACK packet, which is correctly NATed back to wlan0
wlan0 does not understand this SYN,ACK and tries to retransmit the first SYN packet
I'm thinking it might have something to do with the linux-kernel refusing to receive a packet from an address that belongs to the machine but if anyone has a clue it would be of great help!
EDIT: I narrowed it down: it is indeed a kernel issue, the packets sent from eth0 are perceived as "martians" by the kernel because they have a local ip address as source. Setting net.ipv4.conf.all.accept_local=1 did not help, neither did deactivating net.ipv4.conf.all.rp_filter=0.
After browsing the kernel sources and adding a lot of KERNEL_WARNING we found where it came from: the linux kernel is configured on certain mainstream distributions (Ubuntu...) to act as a router and drop packets where the source address is suspect in order to prevent spoofing (search "rp_filter" on https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt and RFC3704).
To allow such traffic you have to set some variables on your machine (as root):
sysctl -w net.ipv4.conf.all.accept_local=1
sysctl -w net.ipv4.conf.all.rp_filter=0
sysctl -w net.ipv4.conf.your_nic.rp_filter=0
where your_nic is the network interface receiving the packet. Beware to change both net.ipv4.conf.all.rp_filter and net.ipv4.conf.your_nic.rp_filter, it will not work otherwise (the kernel defaults to the most restrictive setting).

Python - UDP socket bind() to the correct address

I can't understand why if I create a socket in this way
from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.bind(("192.168.1.10",26000))
print s.recvfrom(4096)[0]
and I try to send to it a broadcast packet like this
from socket import *
s = socket(AF_INET, SOCK_DGRAM)
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
s.sendto("hey!", ("<broadcast>", 26000))
it doesn't work (it hangs on the recvfrom()) but if I try to bind it to "0.0.0.0" it receives the packet correctly.
I know that 0.0.0.0 means that every address on every interface will be listening on that port, but why binding directly to an address makes it don't receive the packet?
Operating system: OSX 10.9.2, Python version: 2.7.6
Even if I'm not running Linux, I tried binding the socket to the subnet broadcast address anyway, same results.
If the operating system is Linux then try to bind socket to the subnet broadcast address.
For example, if your ifconfig settings are inet addr:192.168.0.62 Bcast:192.168.0.255 Mask:255.255.255.0then bind your receiver socket to 192.168.0.255. On Linux you won't be able to use your regular IP address
There is a previous discussion on the topic here
In order to Listen to Broadcast packets you need to use the following.
sock.bind(("<broadcast>", port_num))
or
sock.bind(("", port_num))

Sharing UDP broadcast reception -- python example

I have a UDP broadcast of some data. I'm able to open the following client in python 2.6.1, under OSX 10.6.8, and it works. I can catch the data, all is well.
But: this code "consumes" the port, in that I can't open another one, the 2nd attempt to bind fails... and I must allow for more than one listener. Here's the code that opens the port:
import select, socket
port = 58083 # port msg is broadcast upon
# Create listening port
# ---------------------
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.bind(('', port))
except:
print 'failure to bind'
s.close()
raise
s.setblocking(0)
...Since UDP is intended for broadcast to multiple clients (among other things), I assume I'm doing something wrong. I just can't figure out what.
I found an example on activestate that suggested:
s.bind(('<broadcast>',port))
...but that simply fails every time. Binding to 0.0.0.0 works, but also suffers from the "one client" problem. Binding to the local IP (e.g. 192.168.1.100) doesn't work at all. Removing the bind doesn't work at all.
Anyone?
If you need multiple processes to listen on 58083, you need to set SO_REUSEADDR on the socket before socket.bind()
import select, socket
port = 58083 # port msg is broadcast upon
# Create listening port
# ---------------------
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allow other processes to
# bind to port
try:
s.bind(('0.0.0.0', port))
except:
print 'failure to bind'
s.close()
raise
s.setblocking(0)
After adding an infinite loop at the bottom, and running this twice on my linux server (once as root, and the other as unpriv user), I see:
root#tsunami# lsof | grep 58083
python 25908 root 3u IPv4 284835 0t0 UDP *:58083
python 25945 mpenning 3u IPv4 284850 0t0 UDP *:58083
root#tsunami#

Send and receive IPv6 link-local multicast UDP datagrams in Python?

This following is a straightforward IPv4 UDP broadcast, followed by listening on all interfaces.
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True)
sock.bind(("", 1337))
sock.sendto("hello world", ("255.255.255.255", 1337))
while True:
data, addr = sock.recvfrom(0x100)
print "received from {0}: {1!r}".format(addr, data)
I want to adjust this to send and receive both IPv4 and IPv6.
I've poked around and read as much as I can and believe the following is roughly the route I need to take:
Create an IPv6 socket.
Add the socket to a link or site local multicast group.
Send the UDP packets to the multicast address for the group in use.
Further info I have is that I may need to bind to several interfaces, and tell the socket using setsockopt() that it should also receive multicast packets. Lastly getaddrinfo() can be used across the board to gracefully "drop back" to IPv4 where IPv6 isn't available.
I have much of this implemented, but stumble primarily on the multicasting parts. A complete code example in Python, or vivid description of the constants and addresses needed are preferred.
Here's a link to python mcast demo, does both IPv4 and IPv6.
I'm currently asking a question over here that involves getting the multicast address of a received message, but the source code answers your question!
To listen:
# Initialise socket for IPv6 datagrams
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# Allows address to be reused
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Binds to all interfaces on the given port
sock.bind(('', 8080))
# Allow messages from this socket to loop back for development
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True)
# Construct message for joining multicast group
mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, "ff02::abcd:1"), (chr(0) * 16).encode('utf-8'))
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq)
data, addr = sock.recvfrom(1024)
and to send:
# Create ipv6 datagram socket
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
# Allow own messages to be sent back (for local testing)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True)
sock.sendto("hello world".encode('utf-8'), ("ff02::abcd:1", 8080))
This is for python3.6, with python 2.7 I don't think the encodes are necessary. Also in the struct.pack line, I've seen variations of "16s15s" such as "4s", but I don't know what it is and what I have worked for me!

Categories

Resources