17.14. Multicast
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 users' 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 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 the
address is interesting for this host. 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 ignore other
multicast packets. This is the optimal 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 the third device class, which is the most versatile, at
its best. 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.
17.14.1. 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);
-
Device
method 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;
-
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. Nonetheless, the kernel calls the driver's
set_multicast_list method when
dev->flags changes, because the multicast list
may have changed while the interface was not active.
- IFF_ALLMULTI
-
Flag 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
-
Flag 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.
17.14.2. 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.
|