Setting source address when sending UDP multicast messages - python

How to set the source IP address for UDP multicast packages to something else than the interface IP?
I am trying to write a small router that selectively routes UDP SSDP packages from one network to another. The plan is to do it in python, although I am flexible on that.
It seems fairly easy to route SSDP NOTIFY messages: I receive them on one interface and decide which interface to re-broadcast them on. However the protocol for M-SEARCH messages require that the source IP is set to the original source of the message as any service that chooses to respond will respond with a unicast message to the source IP and port.
Example (heavily simplified):
Network A: 192.168.10.0/24
Network B: 192.168.11.0/24
My router application runs on a multihomed computer on 192.168.10.2 and 192.168.11.2.
A client on network A with IP 192.168.10.10 sends an M-SEARCH message:
Src IP/Port: 192.168.10.10 port 40000
Dst IP/Port: 239.255.255.250 port 1900
My "router application" on 192.168.10.2 receives the packet and would like to rebroadcast it on network B. However I cannot find any method in the socket API that allows me to set the source IP address. Only to pick the source interface.
Thus the rebroadcasted packet now looks like this:
Src IP/Port: 192.168.11.2 port xxxxx
Dst IP/Port: 239.255.255.250 port 1900
And now the receiving service is unable to unicast back to the client as the original IP and port are lost.

How to set the source IP address for UDP multicast packages to something else than the interface IP?
The only way I know of is to use a RAW socket and construct the IP headers manually. Note that use of RAW sockets is typically restricted to admin users on most platforms.
However I cannot find any method in the socket API that allows me to set the source IP address.
Because there isn't one.
And now the receiving service is unable to unicast back to the client as the original IP and port are lost.
Can't you just have your router remember the original source when it receives the M-SEARCH request, and when it receives the unicast reply then forward it to the original requester? That is how most routers usually work.

Related

How to capture martian packets without letting the linux kernel to drop them and then catching them using raw sockets

I'm trying to setup a network to simulate an Edge computing scenario with LTE. But the question here is more pertaining to IP tables and raw sockets on the server.
I have the following setup:
PC-A is the Tower, PC-C is the LTE Core, PC-B is acting as intermediary and routing data using proxy ARPs
Cell phone wants to connect to a TCP server on PC-B(IP: 172.17.1.3)
Cell phone(IP:192.172.0.2) sends packets to PC-A(IP: 172.17.1.1)
The data from PC-A(172.17.1.1) generally has to reach LTE-EPC on 172.17.1.4 which is connected via PC-B(172.17.1.2 - 172.17.1.3 with proxy ARPs). And the data comes in the form of UDP packets to PC-B(IP: 172.17.1.2)
I use NAT table and python script with raw sockets to send all of these UDP packets to a local port, do some filtering, decapsulate the GTP headers and send the TCP/IP packet to the TCP server on PC-B interface(172.17.1.3). I use the below iptables settings to do this
iptables -t nat -A PREROUTING -p udp -d 172.17.1.4 --dport 2152 -j DNAT --to-destination 172.17.1.2:7000
Until here everything works, the extracted TCP/IP packet also reaches the TCP server on 172.17.1.3. The server responds to these packets. For example, for the SYN packet from the cell phone, the server now sends out SYN, ACK. However, the server responds to the original source address 192.172.0.2.
I want to catch these response TCP/IP packets from the TCP server 172.17.1.3 to 192.172.0.2 and do some GTP encapsulation before sending them back to PC-A.
Can anyone tell me how I can use the iptables to tell the kernel to stop dropping these martian packets with destination address 192.172.0.2, but instead forward to a local ip and port, so I can read the same.
I can see the SYN, ACK responses from the server on wireshark. But I assume that these are dropped as I already tried to route it to local ip:port using a similar iptables rule from above.
Any help is much appreciated, Thank you.
It sounds like a tun/tap interface could be useful here. Here's the official Linux kernel documentation.
Both these interface types allow a program to create a virtual network interface. This is designed for tunnels and VPNs and it seems like that is exactly what you are creating.
According to the linked documentation, you may create an interface by opening /dev/net/tun (O_RDWR) and issuing this ioctl to initialize it:
struct ifreq req;
memset(&req, 0, sizeof(req));
req.ifr_flags = IFF_TUN; // or IFF_TAP
strncpy(req.ifr_name, "tunnel%d", IFNAMSIZ); // optional; leave it blank to get a default name; you don't have to have a %d
ioctl(fd, TUNSETIFF, &req); // error check omitted for demonstration
// req.ifr_name now contains the name that was actually selected
After the ioctl you have a virtual network interface in your system. You can configure IP addresses, routes, whatever.
Any time the kernel sends a packet out through your interface it will go into a queue and you'll be able to read it. Any time you write to the interface the kernel will process it as if it's a real packet that just arrived.
You will need to configure networking like it's a real interface. Set up a static route so that 192.172.0.0/16 (or whatever your subnet is) is reached through the tunnel interface. I'm not sure if Linux will let you do this without giving it an address; you might have to give it a dummy address like 192.172.255.254. Or a completely unrelated address like 1.2.3.4 and then let Linux think there's another router in front of your cellphone subnet. Or maybe it will just work without an address - not sure.
The difference between "tun" and "tap" is whether Ethernet processing happens or not (IP tunnel vs Ethernet tap). I expect tun is right for your application. If you choose tap then Linux will also use ARP and so on, and the interface will certainly need an address.
You might find it convenient to use the tunnel interface in both directions, or just one. IP packets aren't required to take the same route in both directions. This is the "correct" way to implement a tunnel, so you might find that a lot of mysterious bugs go away by using it.

Python sockets google dns server

I need my own IP in a small script and in order not to hardcode it, I`ve found a piece of code from here(stackoverflow) that works.
This--
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(("8.8.8.8", 80))
print(s.getsockname()[0])
--
What is not clear for me is why it only works on UDP and not TCP? It has something to do with the google dns server? Thanks in advance.
This has nothing to do with Google and nothing to do with DNS.
All what this code does is to "connect" a UDP socket to an external IP, so that the OS kernel figures out which local IP address it needs to use in order to reach this external system. This is no real connection, i.e. there is no traffic involved but the OS kernel is only checking routing tables and local interfaces in order to decide which IP address to use as source in case one would actually use the socket to send data.
One could do the same with TCP. But in this case a real TCP connection would be established which means that actual traffic would be exchanged and that the connect would fail if the external system would not be reachable on this port (i.e. no listener, firewall in between etc).
With UDP instead connect will not produce any traffic and would fail only if no route to the destination IP address could be determined. This also means that an arbitrary external IP address and port could be used, i.e. ('1.1.1.1',11) would work the same as ('8.8.8.8',80).

python public ip with sockets (err:cannot assign requested addr)

After allowing my raspberry pi to access port 9999 of my router socketname.bind(96.231.140.202,9999) in python gives me a cannot assign error
To port forward I used:
myfiosgateway.com/#/firewall/portforward (the same method
worked fo my apache server) and I have verified that 96.231.140.202 is my pub ip
You cannot bind to your public IP. Your router is doing that. You instead want to bind to your private IP and port forward traffic destined to 9999 to your bound IP on your pi, this address will fall into the rfc compliant private IP ranges, so it will most likely be something like 192.168.1.12 or something similar.
For example:
socketname.bind(0.0.0.0,9999) #the use of 0.0.0.0 will automatically find your available interface on that raspberry pi.
If you let me know exactly what socket library youa re using I can craft the exact code.

Use UDP without specifc port for Multicast

IP multicast gives you the ability to send a single packet, which is picked up by multiple interfaces if they are subscribed to that multicast. If I understand it correctly.
Now if I want to use UDP, in combination with IP multicast, I am obligated to assign a port to listen to. But now I understand that I only receive UDP packets on a multicast specifically sent to that port. But I would like to intercept all UDP packets send to a certain multicast IP address, regardless of the port and receive them at my single socket.
Is something like this possible?
Preferably accompanied by a python example, if this is possible.
This isn't possible using the BSD socket API (which is roughly the API Python exposes in its socket module) - except by creating 2 ** 16 - 1 sockets and using them to bind to all ports.
It's possible using lower-level interfaces such as the TUN/TAP system offered by Linux.

Why doens't IPv6 display client IP name like IPv4 does?

Hi there sorry for disturbing you guys. But I read something in the Magazine about porting codes to IPV6 so i tried just changing this code (socket.AF_INET6, socket.SOCK_STREAM)
In python it works, but the problem is that the server does not display the client ip.
I expected it to display it like this. Got connection from ('127.0.0.1', 59815).
But for it , it messes up all thing and give me this (Got connection from ('::1', 59815, 0, 0)),
So if I ask were is the client IP here?. and what does the two last zeros(0, 0) really mean?. And what should i do to port my code to IPv6.? Thanks iam using python(geany)
So if I do this(socket.socket(socket.AF_INET6, socket.SOCK_STREAM)) will I have put my code to IPV6, o are there some other things I need to do thanks)
::1 is the IPv6 address for localhost, just like 127.0.0.1 is the IPv4 address for localhost. So you are seeing the client's IPv6 address.
The fields in the socket address are documented in the Python socket documentation. They are:
host
port
flowinfo
scope-id
Flowinfo and scope-id are new for IPv6. Flowinfo contains the Flow Label, which is specified in RFC 6437. If you don't know how to use it you can safely leave it at 0. The scope-id is used when an address is valid in multiple scopes. IPv6 link-local addresses for example are valid on every IPv6 interface, but routing them from one interface to another is not possible. So if you want to communicate with link-local addresses you have to specify which interface to use. The scope-id is the number of the interface.
When porting code to IPv6 you have to think about a few things. The socket handling you already have changed. You should also look at name resolving and use functions that support both IPv4 and IPv6 (i.e. don't use gethostbyname() but use getaddrinfo() etc.) And don't forget to allocate enough space when storing IP addresses. IPv6 addresses take up more space than IPv4 addresses.
PS: The IPy library provides very useful classes for dealing with IP addresses.

Categories

Resources