Recently, I noticed that when watching multicast IPTV over Wi-Fi, some of the traffic is lost. After a detailed study of the problem, it was found out that this behavior is explained by the nature of multicast traffic, namely, the MAC address of the packet recipient. It does not depend on the recipient and is formed from the address of the multicast group. Accordingly, all clients connected to a wireless access point claim to such packets. As a result, we get only a part of the packages and we see a steep picture.
By standard means, the problem is solved either by creating a separate access point for the client, or by creating a static route for certain multicast groups, or by moving the client to a separate VLAN. All the “power” of such decisions will manifest itself when there are several IPTV set-top boxes on the network wishing to watch the same channel, plus the need for them on the Internet will add complexity to the configuration of the router. I propose a solution to this problem below. Programs like udpxy are not suitable here, as they change the complete package structure. And we just need to set the required MAC address, while maintaining the network and transport parts so that the client software does not notice any changes.
This solution, let's call it MUT ( M ulticast to U nicast T ranslation), is as follows:
Find out the IP address of the client who wants to connect to the group
Report this to the OS kernel
Learn by IP address the client’s MAC address
Create and send a copy of the package to the appropriate interface.
The execution of steps 1 and 2 lies on the multicast routing program, 3 and 4 - on the core. Both require little change in their work. All work will take place in the GNU / Linux OS. ')
Some theory
Network routing IP version 4 in Linux is based on the following structures:
sk_buff is the most frequently used structure and is the entire network packet. It is passed from function to function along the way, changing its contents.
rtable + dst_entry - two structures storing the result of route caching, obtained from the routing table. Depending on the recipient's address, source address and the TOS of the packet, further policy is determined in relation to him. These two structures store important information for us: the interface through which the sending will take place, and the gateway field — the future L2-neighbor to whom you can send a packet without changing the L3 header. A cache search for each frame is performed twice: once at the entrance (incoming traffic) and at the second time at the exit (outgoing). We are interested in the second.
neighbor - each instance of this structure represents an L2 neighbor for a specific IP address of the recipient. It contains the MAC address of the recipient received after the ARP response; the queue of sk_buff that needs to be sent after determining the MAC address; timers and more. For multicast groups, neighbors are also created, only the MAC address is generated by the function. We should avoid this.
In Linux, multicast traffic routing is completely controlled from the user's area, namely the router program. The key element in multicast routing is the mfc_cache structure. This is a linked list that stores all the information about each route: the source address of the stream, statistics, further route, etc. Adding and removing mfc_cache structures is done by a user program.
Schematic representation of the mfc_cache list:
Image taken from the book “Linux Networking Architecture”
Development
It was based on the Linux 3.18 kernel. To store client IP addresses for each multicast group, expand the mfc_cache with a linked list:
We introduce the new function ipmr_unicast_xmit . A unicast rtable will be generated in it , but at the same time we will transmit multicast sk_buff . Thus, we select the necessary interface for future dispatch.
Now, in order to further create a neighbor for our recipient, and not for a multicast group, we specify the gateway in the rtable . The rt_gateway field is responsible for this:
More details with all the changes can be found in the repository. Link at the end of the article.
Visual representation of the work (changes in the column with the MAC address):
Router
Based on the program IGMPProxy . You could take any other, the same mrouted . It is very important that all IGMP messages are sent from the IP address of the requesting interface, and nothing prevents us from using it. Details of the changes describe it makes no sense, they can also be found in the corresponding repository. The main thing is that in the management of the kernel there are two new commands that the program should support:
MRT_MUT_ADD_DST (212) - add recipient
MRT_MUT_DEL_DST (213) - delete recipient
Together with them is passed the structure of the form:
It is worth noting that such an approach does not allow disconnecting clients from groups for the absence of Membership Report requests, since, based on IGMP, a client who received such a request from another client with the same group does not send a similar one. Therefore, disabling is possible only after receiving an explicit Leave Group-package.
Using
To enable a new feature, you must compile the kernel with the CONFIG_IP_MUT = y option For the full work of the modified IGMPProxy, you also need to enable CONFIG_SYSCTL_SYSCALL = y
Rami Rosen “Linux Kernel Networking. Implementation and Theory » Christian Benvenuti "Understanding Linux Network Internals" Klaus Wehrle and Frank Pahlke "Linux Networking Architecture"
If anyone has a different way to solve the problem, please share in the comments.