How to send multicast packets via a specfic interface in Linux
I'm not entirely convinced my solution is correct, but I can at least shed a little more light on what is going on.
Background
Linux actually has multiple routing tables, and they are searched one at a time in a specific priority order until a table with a matching route is found. You can optionally search some of the routing tables based on source address or protocol; see the ip-rule(8)
man page.
The trouble is the "local" routing table, which has priority 0, the highest possible. The "local" table is populated automatically by the kernel and holds "obvious" interface and broadcast routes. For IPv6 under Linux, this apparently includes the entire multicast block.
The Problem
I'm going to be using the iproute2 tool rather than the more traditional route
, because it will show me everything I need to know.
On my Linux box:
$ ip -6 route show table local
local ::1 via :: dev lo proto none metric 0
local fe80::213:a9ff:fe91:5bcb via :: dev lo proto none metric 0
local fe80::250:b6ff:fe44:37d1 via :: dev lo proto none metric 0
ff00::/8 dev eth0 metric 256
ff00::/8 dev eth1 metric 256
$ ip -6 route show table main
fe80::/64 dev eth0 proto kernel metric 256
fe80::/64 dev eth1 proto kernel metric 256
ff15::/16 dev eth1 metric 1024
ff00::/8 dev eth1 metric 1024
$ ip -6 rule show
0: from all lookup local
32766: from all lookup main
...And my multicast packets for ff15::1 (5==site-local, >link-local) end up on eth0, because the "local" routing table matches first and overrides the "main" table, even though the "main" table has a more specific route. This overriding behavior is correct in the greater scheme of policy routing, but the choice of auto-adding ff00::/8 to the local table is questionable to me.
My Solution
I don't have enough experience to know if this is a good idea, but:
# ip -6 route add ff15::/16 dev eth1 table local
and now my ff15::1 packets are routed through eth1.
This agrees somewhat with the semantics of the local table, in that it's routed directly through a device. It doesn't feel exactly right (considering automatic management and "you shouldn't have to look at this table"), but it's the best solution I've found.
How to specify the multicast SEND interface in Python?
The question that you've linked How to Multicast (send) to first NIC? mentions that you could use IP_MULTICAST_IF
:
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(addr))
Sending IPv6 multicast packets through a specific network interface
I didn't really like the previous solution, so I kept looking for others.
The first option that worked for me is to bind the sender socket to the specific network interface address. The network interfaces addresses can be found using netifaces
module, and I used this helpful answer to specify the Ethernet address.
Another option that might work is the IPV6_MULTICAST_IF option-
#x is the relevant interface index
sock.setsockopt(socket.IPPROTO_IPV6,socket.IPV6_MULTICAST_IF,x)
In Windows, python 2.7, one should add the line
socket.IPPROTO_IPV6=41
before this code (since the relevant enum is not well defined).
Additional information can be found here (Windows) or here (Linux).
Although it seems like a simpler solution, I didn't completely managed to make it work, and not sure what is the proper way to find the right interface index (on Windows, Linux has several options).
How to filter a multicast receiving socket by interface?
Yes, you can do what you want on Linux, without root privileges:
Bind to INADDR_ANY
and set the IP_PKTINFO
socket option. You then have to use recvmsg()
to receive your multicast UDP packets and to scan for the IP_PKTINFO
control message. This gives you some side band information of the received UDP packet:
struct in_pktinfo {
unsigned int ipi_ifindex; /* Interface index */
struct in_addr ipi_spec_dst; /* Local address */
struct in_addr ipi_addr; /* Header Destination address */
};
The ipi_ifindex
is the interface index the packet was received on. (You can turn this into an interface name using if_indextoname()
or the other way round with if_nametoindex()
.
As you said on Windows the same network functions have different semantics, especially for UDP and even more for multicast.
The Linux bind()
semantics for the IP address for UDP sockets are mostly useless. It is essentially just a destination address filter. You will almost always want to bind to INADDR_ANY
for UDP sockets since you either do not care to which address a packet was sent or you want to receive packets for multiple addresses (e.g. receiving unicast and multicast).
Multicast route to all interfaces
You can use route to add mutlicast routes to multiple devices.
sudo route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0
sudo route add -net 224.0.0.0 netmask 240.0.0.0 dev wlan0
This was reflected in the routing table and allowed our services operating on the separate NICs to work.
However, its not clear to me that you should do this.
The reason I am not sure if the is "valid" is that I tried to port from using route which is apparently deprecated/obsolete to using "ip route", eg
ip route add 224.0.0.0/4 dev eth0
ip route add 224.0.0.0/4 dev wlan0
However ip route won't let you add the second route. I have asked about how to properly use ip route without having to use explicit multicast group addresses, but if you are comfortable using route, you should be able to add the route to each device you need to use.
Related Topics
Delete All Files Except the Newest 3 in Bash Script
How to Read Just a Single Character in Shell Script
How to Disable Socket Creation for a Linux Process, for Sandboxing
Find String Inside a Gzipped File in a Folder
Linux Script That Monitors File Changes Within Folders (Like Autospec Does!)
How to Connect to Postgresql Server: Could Not Connect to Server: Permission Denied
Replace a Text with a Variable
Print on Terminal and into File Simultaneously
Multiple Option Arguments Using Getopts (Bash)
Decrypt Obfuscated Perl Script
Sort Logs by Date Field in Bash
How to Increase the /Proc/Pid/Cmdline 4096 Byte Limit
How Does Execve Call Dynamic Linker/Loader (Ld-Linux.So.2)
Getting "I Won't Open a Connection To" When Connecting to Ftp Server from Google Compute Engine
Why Is Pr_Debug of the Linux Kernel Not Giving Any Output
Find and Remove Files with Space Using Find Command on Linux
How to Find Out What Linux Capabilities a Process Requires to Work