Unicast multicast traffic routing


Recently, I noticed that when watching a multicast IPTV via Wi-Fi, part of the traffic is lost. After a detailed study of the problem, it was found 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 a multicast group. Accordingly, all clients connected to a wireless access point apply for such packets. As a result of this, we get only a part of the packages and we see a steep picture.

By regular means, the problem can be solved either by creating a separate access point for the client, or by creating a static route for certain multicast groups, or by putting the client in a separate VLAN. All the “power” of such decisions will manifest itself when there are several IPTV set-top boxes on the network who want to watch the same channel, plus the need for them on the Internet will add complexity to configuring the router. I offer my own solution to this problem below.

Programs like udpxy are not suitable here, since they change the complete structure of the package. And we only need to set the necessary MAC address, while preserving 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:
  1. Find out the IP address of a client who wants to connect to a group
  2. Report this to the OS kernel
  3. Get IP Address of Client MAC Address
  4. Create and send a copy of the package to the appropriate interface

The implementation of steps 1 and 2 lies on the multicast routing program, 3 and 4 - on the core. Both require small changes in their work. All work will take place on the GNU / Linux OS.

Bit of theory

Linux IP IP 4 routing is based on the following structures:
  • sk_buff is the most commonly 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 that store the route caching result obtained from the routing table. Depending on the address of the recipient, the source address and the TOS field of the packet, further policy is defined with respect to it. These two structures store important information for us: the interface through which the sending will go, and the gateway field is the future L2 neighbor to which the packet can be sent without changing the L3 header. The cache search for each frame is performed two times: once at the input (incoming traffic) and a second time at the output (outgoing). We are interested in the second.
  • neighbor - each instance of this structure represents an L2 neighbor for a specific recipient IP address. It contains the destination MAC address received after the ARP response; queue from sk_buff, which must 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.

On Linux, the routing of multicast traffic is completely controlled from the user's area, namely the router program. A key element in multicast routing is the mfc_cache structure . This is a linked list that stores all the information about each route: address of the stream source, statistics, further route, etc. Adding and removing mfc_cache structures is done by the user program.

Schematic representation of the mfc_cache list:

Image taken from the book “Linux Networking Architecture”


The kernel was taken the Linux 3.18 kernel. To store client IP addresses for each multicast group, we expand mfc_cache with a linked list:

structmut_dst {structlist_headlist;
    __be32 ip;

Introducing the new ipmr_unicast_xmit function . A unicast rtable will be generated in it , but at the same time we will transmit a multicast sk_buff . Thus, we select the necessary interface for future sending.

Now, so that in the future a neighbor is created for our recipient, and not for a multicast group, we specify the gateway in rtable . The rt_gateway field is responsible for this :

structrtable *rt;
rt = ip_route_output_ports(net, &fl4, NULL, m_dst->ip, 0, 0, 0, IPPROTO_IPIP, RT_TOS(iph->tos), 0);
if (IS_ERR(rt))
    goto out_free;
rt->rt_gateway = m_dst->ip;
dev = rt->dst.dev;

We introduce the sysctl variable / proc / sys / net / ipv4 / mut . It will make it possible to change the operating mode of the kernel on the fly.

sysctl net.ipv4.mut = 1 - Enables the new mode
sysctl net.ipv4.mut = 0 - Returns the standard routing mode

As before, you can see the list of routes, now also unicast: More details on all 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):

root@multicast:~# cat /proc/net/ip_mr_cache
Group     Origin Iif Pkts  Bytes    Wrong Dsts
0520C3EF           2 18842 25323648     0 01000A0A


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. There is no sense in describing the details of the changes; they can also be found in the corresponding repository. The main thing is that in the kernel management there are two new commands that the program should support:

  • MRT_MUT_ADD_DST (212) - add recipient
  • MRT_MUT_DEL_DST (213) - delete the recipient

Together with them, the structure of the form is transmitted:

struct <name> {structin_addrgroup;// Адрес группыstructin_addrorigin;// Адрес источникаstructin_addrdestination;// Адрес клиента

A warning

It is worth noting that this approach does not make it possible to disconnect clients from groups for the lack of Membership Report requests, since, based on the IGMP protocol, a client that receives such a request from another client with the same group does not send a similar one. Therefore, disconnection is possible only after receiving an explicit Leave Group package.


To enable the new feature, you need to compile the kernel with the CONFIG_IP_MUT = y option.
For the changed IGMPProxy to work properly, you also need to enable CONFIG_SYSCTL_SYSCALL = y


Modified Kernel
Modified IGMPProxy


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.

Also popular now: