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 the 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 packet. 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 rebuild_header device method, if defined, doesn’t need to look in the data it arranges.

Receiving multicast packets, on the other hand, needs some cooperation from the device. The hardware should notify the operating system whenever an ``interesting'' multicast packet is received, i.e., a packet whose destination address identifies a group of hosts that includes this interface. This means that the hardware filter should be programmed to tell some multicast destination addresses from the others. The filter is the unit that matches the destination address of the network packets against its own hardware address during normal operation of the interface.

Typically, hardware belongs to one of three classes, as far as multicasting is concerned:

  • Interfaces that cannot deal with multicasting. 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 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, as 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, as 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 and to support 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

Here is a summary of the data structures and functions related to driver multicast capabilities:

void (*dev->set_multicast_list)(struct 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 also require you to reprogram the hardware filter. The method receives a pointer to struct 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 over 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.

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 the list */
    char dmi_addr[MAX_ADDR_LEN];   /* hardware address */
    unsigned short dmi_addrlen;    /* len in octets of the address */
    unsigned short dmi_users;      /* usage count of the structure */
};

Since 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 device *dev)
{
    struct dev_mc_list *mcptr;

    if (dev->flags & IFF_PROMISC) {
        ff_get_all_packets();
        return;
    }
    if (dev->flags & IFF_ALLMULTI || dev->mc_count > FF_TABLE_SIZE) {
        ff_get_all_multicast_packets();
        return;
    }
    if (dev->mc_count == 0) {
        ff_get_only_own_packets();
        return;
    }
    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.

Nowadays, interface boards often can’t store a multicast list. This is not a big problem, though, because the upper layers of networking code will take care of dropping unwanted packets.

As I suggested 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. I call this a ``non-featured'' (nf) implementation. The implementation is very simple, as shown by the following code:

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

Dealing with 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 they receive every packet anyway.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.186.164