IP Multicasting

IP Multicasting

IP multicasting relies on a special group of addresses known as multicast addresses. It is this group address that names a given group. For example, if five nodes all want to communicate with one another via IP multicast, they all join the same group address. Once they are joined, any data that one node sends is replicated to every member of the group, including the node that sent the data. A multicast IPv4 address is a class D IP address in the range 224.0.0.0 through 239.255.255.255 and IPv6 multicast addresses begin with the prefix FF (1111 1111), as we discussed in Chapter 3. A number of these addresses are reserved for special purposes. For a comprehensive list of reserved addresses, take a look at RFCs 1700 and 2375. The IANA maintains this list. Table 9-1 lists a few of the IPv4 addresses currently marked as reserved. Actually, you can use any address except for the first three reserved multicast addresses because they are used by routers on the network. Refer to RFC 1700 for the exact multicast address assignments.

Table 9-1 Reserved IPv4 Multicast Addresses

Multicast Address

Use

224.0.0.0

Base address (reserved)

224.0.0.1

All nodes on this subnet

224.0.0.2

All routers on this subnet

224.0.1.1

Network time protocol

224.0.0.9

RIP version 2 group address

224.0.1.24

WINS server group address

IPv6 multicasting also sets aside certain multicast addresses for specific purposes, some of which are listed in Table 9-2. One difference between IPv4- and IPv6-reserved multicast addresses is that IPv6 offers different addresses depending on the desired scope, such as link-local and site-local.

Table 9-2 IPv6 Multicast Addresses

Scope

Multicast Address

Use

Node-local

FF01::1

All nodes address

Node-local

FF01::2

All routers address

Link-local

FF02::1

All nodes address

Link-local

FF02::2

All routers address

Link-local

FF02::9

RIP routers

Site-local

FF05::2

All routers address

Site-local

FF05::1:3

All DHCP servers

Site-local

FF05::1:4

All DHCP relays

Because multicasting had not been envisioned when TCP/IP was developed, a number of accommodations had to be made to allow IP to support it. For example, we have already discovered that IP requires a set of special addresses to be set aside for multicast traffic. In addition, for IPv4, a special protocol was introduced to manage multicast clients and their membership in a group. Imagine if two workstations on separate subnets want to join a single multicast group. How is this implemented over IP? You can't simply broadcast the data to the multicast address everywhere because the network would become flooded with broadcast data in no time. IGMP was developed to signal routers that a machine on the network is interested in data destined for a given group. The latest revision of IGMP (version 3) adds support for limiting what sources data is accepted from. Note that the reliable multicast provider still relies on IGMP to communicate group membership to network elements.

For IPv6, the ICMPv6 protocol rolls up the various support protocols, such as ICMP, ARP, and IGMP, into one. Multicast membership is managed by MLD messages, which are a type of message sent over the ICMPv6 protocol.

Support Protocols

Multicasting hosts use IGMP and MLD to notify routers that a computer on the router's subnet wants to join a particular multicast group. IGMP is the backbone of IPv4 multicasting and MLD is its counterpart for IPv6. For multicasting to work correctly, all routers between two multicasting nodes must support the appropriate multicast support protocol. For example, in the case of IPv4, if machines A and B join the multicast group 224.1.2.3, and there are three routers between the two, all three routers must be IGMP-enabled for successful communication to occur. Any non-multicast-enabled router simply drops received multicast data. When an application joins a multicast group, a “join” (or report) message is sent to the all-routers address on the interface the group was joined. This command notifies the router that it has clients interested in a particular multicast address. Thus, if the router receives data destined for that multicast address, it forwards it to the network with the multicast client.

In addition, when an endpoint joins a multicast group, it specifies a TTL parameter that indicates how many routers the endpoint's multicast application is willing to traverse to send and receive data. For example, if you write an IP multicast application that joins group X with a TTL of 2, a join command is sent to the all-routers group on the local subnet. The routers on that subnet pick up the command, indicating that it should forward multicast data destined for that address. The router decrements the TTL by 1 and passes the join command on to its neighboring networks. The routers on those networks do the same upon receipt of the command. At this point, those routers decrement the TTL again, which now makes the TTL value 0, and the command is no longer propagated. Because of this, TTL limits how far multicast data will be replicated.

Once a router has one or more multicast groups registered by workstations, it periodically sends a “group query” message to the all-hosts address for each multicast address that a join command notified it of. If clients on that network are still using that multicast address, they respond with another message so that the router knows to keep forwarding data related to that address; otherwise, the router stops forwarding any data for that address. Also, both IGMP (version 2 and greater) and MLD support the notion of a host explicitly leaving a group. That is, each interface maintains a reference count of how many applications are joined to a particular multicast group. When the count goes to zero, a leave (or “done”) message is sent. This notifies the routers to stop forwarding data for that group. Note that for IGMPv1, there is no explicit leave message, so the router will continue forwarding data even after the interested application(s) have exited, which can have undesired results. Only when the IGMPv1 router sends a group query will it discover that no one is listening to that particular group.

Windows XP introduces support for IGMP version 3 for IPv4 multicasting, which allows applications to join an IPv4 multicast group and list one or more sources from which to accept or deny data from. If group X is joined, which specifies sources A and B as the only valid sources, then only data originating from A or B will be propagated to the application. Likewise, if group X was joined and sources A and B were excluded, then data sent to the multicast group from everyone except A and B will be propagated to the application. For IGMPv3 to work properly, the routers on the network must also support IGMPv3. If they don't, there is no real gain because the routers will propagate all data for the joined multicast groups, not just from the source list specified. If a Windows XP host is on a network that is not IGMPv3-enabled, it will fall back to the version of IGMP present.

Windows 98, Windows Me, and Windows 2000 natively support IGMP version 2. For Windows 95, the latest Winsock 2 update also includes IGMP version 2. In Windows NT 4.0, Service Pack 4 includes support for IGMP version 2. Previous Service Packs and the base OS supported only version 1. Windows XP supports IGMPv3. If you want to read the complete specifications on IGMP version 1 or version 2, consult RFC 1112 or RFC 2236, respectively. Currently, IGMP version 3 is an IETF draft: draft-ietf-idmr-igmp-v3-07.txt.

Multicasting with Setsockopt

Originally, the only way to join or leave a multicast group was via the setsockopt API. Winsock 2 introduces a protocol-independent method of multicasting with the WSAJoinLeaf API (discussed in the next section), but as we will soon see, the setsockopt method is much more flexible even though it is more closely tied to the protocol being used.

IPv4

There are two socket options that control joining and leaving groups: IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. The socket option level is IPPROTO_IP. The input parameter is a struct ip_mreq structure, which is defined as

struct ip_mreq {    struct in_addr imr_multiaddr;   /* IP multicast address of group */    struct in_addr imr_interface;   /* local IP address of interface */ };

The imr_multiaddr field is the 32-bit IPv4 address of the multicast group in network-byte order and imr_interface is the 32-bit IPv4 address of the local interface on which to join the multicast group (also specified in network-byte order). The following code snippet illustrates joining a multicast group.

SOCKET       s; SOCKADDR_IN    localif; struct ip_mreq mreq; s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); localif.sin_family = AF_INET; localif.sin_port   = htons(5150); localif.sin_addr.s_addr = htonl(INADDR_ANY); bind(s, (SOCKADDR *)&localif, sizeof(localif)); mreq.imr_interface.s_addr = inet_addr("157.124.22.104"); mreq.imr_multiaddr.s_addr = inet_addr("234.5.6.7"); setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq,  sizeof(mreq));

Note that the socket should be bound to the wildcard address (INADDR_ANY) before joining the group. In this example, the socket is joined to the multicast group 234.5.6.7 on the local interface 157.124.22.104. Multiple groups may be joined on the same socket on the same or different interface.

Once one or more multicast groups are joined, the IP_DROP_MEMBERSHIP option is used to leave a particular group. Again, the struct ip_mreq structure is the input parameter. The local interface and multicast group to drop are the arguments of the structure. For example, given the code sample you just saw, the following code drops the multicast group previously joined:

// Join the group as shown above mreq.imr_interface.s_addr = inet_addr("157.124.22.104"); mreq.imr_multiaddr.s_addr = inet_addr("234.5.6.7"); setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&mreq,  sizeof(mreq));

Finally, if the application exits or the socket is closed, any multicast groups joined by that process or socket are cleaned up.

note

An IPv4 multicasting sample that uses setsockopt is provided on the companion CD in the directory IP-SETSOCKOPT.

IPv4 with Multicast Sourcing

IP source multicasting is available on systems that support the IGMPv3 protocol and allows a socket to join a multicast group on an interface while specifying a set of source addresses to accept data from. There are two possible modes in which a socket may join a group. The first is the INCLUDE mode, in which a socket joins a group specifying N number of valid source addresses to accept data from. The other mode is EXCLUDE, in which a socket joins a group specifying to accept data from anyone except the N source addresses listed. Depending on which mode is used, the socket options differ.

To join a multicast group while using the INCLUDE mode, the socket options are IP_ADD_SOURCE_MEMBERSHIP and IP_DROP_SOURCE_MEMBERSHIP. The first step is to add one or more sources. Both socket options take a struct ip_mreq_source structure, which is defined as

struct ip_mreq_source {    struct in_addr imr_multiaddr;   /* IP multicast address of group */    struct in_addr imr_sourceaddr;  /* IP address of source          */    struct in_addr imr_interface;   /* local IP address of interface */ };

The imr_multiaddr and imr_interface fields are the same as in the struct ip_mreq structure. The new field imr_sourceaddr specifies the 32-bit IP address of the source to accept data from. If there are multiple valid sources, then the IP_ADD_SOURCE_MEMBERSHIP is called again with the same multicast address and interface with the other valid source. The following code sample joins a multicast group on a local interface with two valid sources:

SOCKET                s; SOCKADDR_IN           localif; struct ip_mreq_source mreqsrc; s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); localif.sin_family = AF_INET; localif.sin_port   = htons(5150); localif.sin_addr.s_addr = htonl(INADDR_ANY); bind(s, (SOCKADDR *)&localif, sizeof(localif)); mreqsrc.imr_interface.s_addr = inet_addr("157.124.22.104"); mreqsrc.imr_multiaddr.s_addr = inet_addr("234.5.6.7"); mreqsrc.imr_sourceaddr.s_addr = inet_addr("172.138.104.10"); setsockopt(s, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP,   (char *)&mreqsrc, sizeof(mreqsrc)); mreqsrc.imr_sourceaddr.s_addr = inet_addr("172.141.87.101"); setsockopt(s, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *)&mreqsrc, sizeof(mreqsrc));

To remove a source from the INCLUDE set, the IP_DROP_SOURCE_ MEMBERSHIP is called with the multicast group, local interface, and source to be removed.

To join a multicast group that excludes one or more sources, the multicast group is joined with IP_ADD_MEMBERSHIP. Using IP_ADD_MEMBERSHIP to join a group is equivalent to joining a group in the EXCLUDE mode except that no one is excluded. Data sent to the joined group is accepted regardless of the source. Once the group is joined, then the IP_BLOCK_SOURCE option is called to exclude the given source. Again, the struct ip_mreq_source structure is the input parameter that specifies the source to block. The following example joins a group and then excludes a single source:

SOCKET                s; SOCKADDR_IN           localif; struct ip_mreq        mreq; struct ip_mreq_source mreqsrc; s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); localif.sin_family = AF_INET; localif.sin_port   = htons(5150); localif.sin_addr.s_addr = htonl(INADDR_ANY); bind(s, (SOCKADDR *)&localif, sizeof(localif)); // Join a group - the filter is EXCLUDE none mreq.imr_interface.s_addr = inet_addr("157.124.22.104"); mreq.imr_multiaddr.s_addr = inet_addr("234.5.6.7"); setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq,    sizeof(mreq)); mreqsrc.imr_interface = mreq.imr_interface; mreqsrc.imr_multiaddr = mreq.imr_multiaddr; mreqsrc.imr_sourceaddr.s_addr = inet_addr("172.138.104.10"); setsockopt(s, IPPROTO_IP, IP_BLOCK_SOURCE, (char *)&mreqsrc,    sizeof(mreqsrc));

If after some point, the application wishes to accept data from a source previously blocked, it may remove that source from the exclude set by calling setsockopt with IP_UNBLOCK_SOURCE. A struct ip_mreq_source is the input parameter that specifies the source to accept data from.

note

An IPv4 source multicasting sample that uses setsockopt is provided on the companion CD in the directory IP-SOURCE.

IPv6

Multicasting with IPv6 is similar to IPv4 multicasting except that the socket options are named differently and take slightly different input parameters. The options for IPv6 are IPV6_ADD_MEMBERSHIP and IPV6_DROP_MEMBERSHIP. The option level is IPPROTO_IPV6. The structure that specifies the multicast group and interface is a struct ipv6_mreq that is defined as

typedef struct ipv6_mreq {     struct in6_addr ipv6mr_multiaddr;  /* IPv6 multicast address */     unsigned int    ipv6mr_interface;  /* Interface index */ } IPV6_MREQ;

The structure fields are equivalent to the IPv4 structure struct ip_mreq except that the local interface is the interface index instead of the full IPv6 address. The easiest way to find the interface index of a local IPv6 address is via the IP Helper API GetAdaptersAddresses covered in Chapter 16. Also, if the link-local address is used as the local interface, the scope-ID of that link is the interface index. The following code sample illustrates joining an IPv6 multicast group when the link-local address is given for the local interface:

SOCKET             s; struct ipv6_mreq   mreq6; struct addrinfo   *reslocal,                   *resmulti,                    hints; s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); // Get the local wildcard address to bind to (i.e. "::") memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; getaddrinfo(NULL, "5150", &hints, &reslocal); bind(s, reslocal->ai_addr, reslocal->ai_addrlen); freeaddrinfo(reslocal); // Resolve the link-local interface getaddrinfo("fe80::250:4ff:fe7c:7036%6", NULL, NULL, &reslocal); // Resolve the multicast address getaddrinfo("ff12::1", NULL, NULL, &resmulti); // Join the multicast group mreq6.ipv6mr_multiaddr =  ((SOCKADDR_IN6 *)resmulti->ai_addr)->sin6_addr; mreq6.ipv6mr_interface =  ((SOCKADDR_IN6 *)reslocal->ai_addr)->sin6_scope_id; setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&mreq6, sizeof(mreq6)); freeaddrinfo(resmulti); freeaddrinfo(reslocal);

As with IPv4, to leave the multicast group the same struct ipv6_mreq structure is passed into setsockopt but with the IPV6_DROP_MEMBERSHIP option.

note

An IPv6 multicasting sample that uses setsockopt is provided on the companion CD in the directory IP-SETSOCKOPT. This is the same sample as that for IPv4 multicasting because it will figure out which address family is being used from the addresses supplied on the command line (using the getaddrinfo API to make the sample IP version independent as discussed in Chapter 3).

Sending Multicast Data with IPv4

The previous sections have shown how to join and leave multicast groups to receive data sent to that group; however, an application need not join the group to send data to it. There is one issue to be aware of: multihomed computers. If an application creates a UDP socket and calls sendto with a destination address of “234.5.6.7”, which interface is the data sent on? Basically, the first interface listed in the routing table is the interface the data is sent on. To override this behavior, applications may use the IP_MULTICAST_IF socket option to specify the interface for outgoing data. The option value is simply the 32-bit IPv4 address of the local interface. The following code sample sets the outgoing interface on a socket.

SOCKET         s; SOCKADDR_IN    dest; ULONG          localif; char           buf[1024]; int            buflen=1024; s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); localif = inet_addr("157.124.22.104"); setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localif, sizeof(localif)); dest.sin_family = AF_INET; dest.sin_port   = htons(5150); dest.sin_addr.s_addr = inet_addr("234.5.6.7"); sendto(s, buf, buflen, 0, (SOCKADDR *)&dest, sizeof(dest));

note

The multicasting sample code on the companion CD also illustrates using this socket option.

Sending Multicast Data with IPv6

The same sending issue that applies to IPv4 is also present for IPv6 multicasting. The outgoing interface for data sent to an IPv6 multicast group is specified with the IPV6_MULTICAST_IF option. The input parameter is an integer that specifies the adapter interface index that outgoing data is sent on. Also, if sendto or WSASendTo is used for sending data to the multicast group, the sin6_scope_id of the SOCKADDR_IN6 structure given for the to address parameter should be zero.

note

The multicasting sample code on the companion CD also illustrates using this socket option.

Multicasting with WSAIoctl

For IPv4 source multicasting, there is an ioctl that can specify one or more source addresses to include or exclude with a single call. This is SIO_SET_MULTICAST_FILTER. The input parameter is a struct ip_msfilter structure that is defined as

struct ip_msfilter {    struct in_addr imsf_multiaddr;  /* IP multicast address of group */    struct in_addr imsf_interface;  /* local IP address of interface */    u_long         imsf_fmode;      /* filter mode - */                                    /* INCLUDE or EXCLUDE */    u_long         imsf_numsrc;    /* number of sources in src_list */     struct in_addr imsf_slist[1]; };

The first two fields are self-explanatory. The third field, imsf_fmode, indicates whether the source addresses listed in the imsf_slist array are sources that should be included or excluded from the multicast group. To include the sources, the constant MCAST_INCLUDE is supplied; otherwise, to exclude these sources MCAST_EXCLUDE is used. imsf_numsrc indicates the number of sources supplied, and imsf_slist is the array of source addresses.

The following code sample illustrates using this ioctl:

SOCKET   s; char   buf[1024]; struct ip_msfilter *msfilter; DWORD   bytes; int   filterlen; s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // bind the socket to INADDR_ANY msfilter = (struct ip_msfilter *)buf; msfilter->imsf_multiaddr.s_addr = inet_addr("234.5.6.7"); msfilter->imsf_interface.s_addr = inet_addr("157.124.22.104"); msfilter->imsf_fmode = MCAST_INCLUDE; msfilter->imsf_numsrc = 3; msfilter->imsf_slist[0].s_addr = inet_addr("172.138.104.10"); msfilter->imsf_slist[1].s_addr = inet_addr("172.138.104.11"); msfilter->imsf_slist[2].s_addr = inet_addr("172.138.104.12"); filterlen = sizeof(struct ip_msfilter) + ((msfilter->imsf_numsrc - 1) * sizeof(struct in_addr)); WSAIoctl(s, SIO_SET_MULTICAST_FILTER, (void *)msfilter, filterlen, NULL, 0, &bytes, NULL, NULL);

This sample joins a multicast group and specifies three valid sources to accept data from. To retrieve the multicast filter set, use the ioctl SIO_GET_MULTICAST_ FILTER. The input parameter must be a struct ip_msfilter structure that contains the local interface and multicast group on which to obtain the multicast state for. Upon success, a struct ip_msfilter structure is returned via the output parameter, which contains the full set of sources for the group and interface. Note that you must specify a large enough buffer to hold the filter plus all the sources.

note

An IPv4 source multicasting sample that uses WSAIoctl is provided on the companion CD in the directory IP-SOURCE. This is the same sample that uses setsockopt for IP source multicasting. The -f' flag indicates to use SIO_SET_MULTICAST_FILTER instead of the socket options.

Multicasting with WSAJoinLeaf

Winsock 2 introduces the WSAJoinLeaf API, which is designed to be protocol independent. The API is defined as

SOCKET WSAJoinLeaf(     IN SOCKET s,     IN const struct sockaddr FAR * name,     IN int namelen,     IN LPWSABUF lpCallerData,     OUT LPWSABUF lpCalleeData,     IN LPQOS lpSQOS,     IN LPQOS lpGQOS,     IN DWORD dwFlags     );

In most respects, WSAJoinLeaf takes the same parameters as WSAConnect. The exceptions are that name indicates the multicast address to join and dwFlags indicates whether the socket will be sending or receiving data on the multicast socket. The valid flag values are JL_SENDER_ONLY, JL_RECEIVER_ONLY, and JL_BOTH.

For IP multicasting, a UDP socket must be created by using the WSASocket API, and the flags WSA_FLAG_MULTIPOINT_C_LEAF and WSA_FLAG_MULTIPOINT_D_LEAF must be specified. The socket should then be bound to the specific interface on which the group is to be joined. Then WSAJoinLeaf is invoked with the multicast address of the group to join. There are a couple of differences here. First, instead of binding to the wildcard address, you should bind to the specific local interface. Second, the outgoing (sending) interface does not have to be set. When the multicast group is joined, the outgoing interface is automatically set to the interface the socket is bound to. Finally, only a single multicast group can be joined on a socket. That is, unlike using setsockopt, WSAJoinLeaf can be invoked only once on a socket and the multicast group is joined until the socket is closed—there is no other way to leave the group.

note

An IP multicasting sample that uses WSAJoinLeaf is provided on the companion CD in the directory IP-WSAJOINLEAF. The sample works over IPv4 and IPv6.



Network Programming for Microsoft Windows
Network Programming for Microsoft Windows (Microsoft Professional Series)
ISBN: 0735605602
EAN: 2147483647
Year: 2001
Pages: 172
Authors: Anthony Jones

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net