O'Reilly logo

Linux Device Drivers, Second Edition by Alessandro Rubini, Jonathan Corbet

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Multicasting

A multicast packet is a network packet meant to be received by more than one host, but not by all hosts. This functionality is obtained by assigning special hardware addresses to groups of hosts. Packets directed to one of the special addresses should be received by all the hosts in that group. In the case of Ethernet, a multicast address has the least significant bit of the first address octet set in the destination address, while every device board has that bit clear in its own hardware address.

The tricky part of dealing with host groups and hardware addresses is performed by applications and the kernel, and the interface driver doesn’t need to deal with these problems.

Transmission of multicast packets is a simple problem because they look exactly like any other packets. The interface transmits them over the communication medium without looking at the destination address. It’s the kernel that has to assign a correct hardware destination address; the hard_header device method, if defined, doesn’t need to look in the data it arranges.

The kernel handles the job of tracking which multicast addresses are of interest at any given time. The list can change frequently, since it is a function of the applications that are running at any given time and the user’s interest. It is the driver’s job to accept the list of interesting multicast addresses and deliver to the kernel any packets sent to those addresses. How the driver implements the multicast list is somewhat dependent on how the underlying hardware works. Typically, hardware belongs to one of three classes, as far as multicast is concerned:

  • Interfaces that cannot deal with multicast. These interfaces either receive packets directed specifically to their hardware address (plus broadcast packets), or they receive every packet. They can receive multicast packets only by receiving every packet, thus potentially overwhelming the operating system with a huge number of “uninteresting” packets. You don’t usually count these interfaces as multicast capable, and the driver won’t set IFF_MULTICAST in dev->flags.

    Point-to-point interfaces are a special case, because they always receive every packet without performing any hardware filtering.

  • Interfaces that can tell multicast packets from other packets (host-to-host or broadcast). These interfaces can be instructed to receive every multicast packet and let the software determine if this host is a valid recipient. The overhead introduced in this case is acceptable, because the number of multicast packets on a typical network is low.

  • Interfaces that can perform hardware detection of multicast addresses. These interfaces can be passed a list of multicast addresses for which packets are to be received, and they will ignore other multicast packets. This is the optimum case for the kernel, because it doesn’t waste processor time dropping “uninteresting” packets received by the interface.

The kernel tries to exploit the capabilities of high-level interfaces by supporting at its best the third device class, which is the most versatile. Therefore, the kernel notifies the driver whenever the list of valid multicast addresses is changed, and it passes the new list to the driver so it can update the hardware filter according to the new information.

Kernel Support for Multicasting

Support for multicast packets is made up of several items: a device method, a data structure and device flags.

void (*dev->set_multicast_list)(struct net_device *dev);

This device method is called whenever the list of machine addresses associated with the device changes. It is also called when dev->flags is modified, because some flags (e.g., IFF_PROMISC) may also require you to reprogram the hardware filter. The method receives a pointer to struct net_device as an argument and returns void. A driver not interested in implementing this method can leave the field set to NULL.

struct dev_mc_list *dev->mc_list;

This is a linked list of all the multicast addresses associated with the device. The actual definition of the structure is introduced at the end of this section.

int dev->mc_count;

The number of items in the linked list. This information is somewhat redundant, but checking mc_count against 0 is a useful shortcut for checking the list.

IFF_MULTICAST

Unless the driver sets this flag in dev->flags, the interface won’t be asked to handle multicast packets. The set_multicast_list method will nonetheless be called when dev->flags changes, because the multicast list may have changed while the interface was not active.

IFF_ALLMULTI

This flag is set in dev->flags by the networking software to tell the driver to retrieve all multicast packets from the network. This happens when multicast routing is enabled. If the flag is set, dev->mc_list shouldn’t be used to filter multicast packets.

IFF_PROMISC

This flag is set in dev->flags when the interface is put into promiscuous mode. Every packet should be received by the interface, independent of dev->mc_list.

The last bit of information needed by the driver developer is the definition of struct dev_mc_list, which lives in <linux/netdevice.h>.

struct dev_mc_list {    
    struct dev_mc_list   *next;          /* Next address in list */
    __u8                 dmi_addr[MAX_ADDR_LEN]; /* Hardware address */
    unsigned char        dmi_addrlen;    /* Address length */
    int                  dmi_users;      /* Number of users */
    int                  dmi_gusers;     /* Number of groups */
};

Because multicasting and hardware addresses are independent of the actual transmission of packets, this structure is portable across network implementations, and each address is identified by a string of octets and a length, just like dev->dev_addr.

A Typical Implementation

The best way to describe the design of set_multicast_list is to show you some pseudocode.

The following function is a typical implementation of the function in a full-featured (ff) driver. The driver is full featured in that the interface it controls has a complex hardware packet filter, which can hold a table of multicast addresses to be received by this host. The maximum size of the table is FF_TABLE_SIZE.

All the functions prefixed with ff_ are placeholders for hardware-specific operations.

void ff_set_multicast_list(struct net_device *dev)
{
    struct dev_mc_list *mcptr;

    if (dev->flags & IFF_PROMISC) {
        ff_get_all_packets();
        return;
    }
    /* If there's more addresses than we handle, get all multicast
    packets and sort them out in software. */
    if (dev->flags & IFF_ALLMULTI || dev->mc_count > FF_TABLE_SIZE) {
        ff_get_all_multicast_packets();
        return;
    }
    /* No multicast?  Just get our own stuff */
    if (dev->mc_count == 0) {
        ff_get_only_own_packets();
        return;
    }
    /* Store all of the multicast addresses in the hardware filter */
    ff_clear_mc_list();
    for (mc_ptr = dev->mc_list; mc_ptr; mc_ptr = mc_ptr->next)
        ff_store_mc_address(mc_ptr->dmi_addr);
    ff_get_packets_in_multicast_list();
}

This implementation can be simplified if the interface cannot store a multicast table in the hardware filter for incoming packets. In that case, FF_TABLE_SIZE reduces to 0 and the last four lines of code are not needed.

As was mentioned earlier, even interfaces that can’t deal with multicast packets need to implement the set_multicast_list method to be notified about changes in dev->flags. This approach could be called a “nonfeatured” (nf) implementation. The implementation is very simple, as shown by the following code:

void nf_set_multicast_list(struct net_device *dev)
{
    if (dev->flags & IFF_PROMISC)
        nf_get_all_packets();
    else
        nf_get_only_own_packets();
}

Implementing IFF_PROMISC is important, because otherwise the user won’t be able to run tcpdump or any other network analyzers. If the interface runs a point-to-point link, on the other hand, there’s no need to implement set_multicast_list at all, because users receive every packet anyway.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required