Messages sent by datagram sockets can be broadcast to reach all of the hosts on an attached network. The network must support broadcast; the system provides no simulation of broadcast in software. Broadcast messages can place a high load on a network since they force every host on the network to service them. Broadcasting is usually used for either of two reasons: to find a resource on a local network without having its address, or functions like routing require that information be sent to all accessible neighbors.
To send a broadcast message, create an Internet datagram socket:
s = socket(AF_INET, SOCK_DGRAM, 0); |
and bind a port number to the socket:
sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = htons(MYPORT); bind(s, (struct sockaddr *) &sin, sizeof sin); |
The datagram can be broadcast on only one network by sending to the network's broadcast address. A datagram can also be broadcast on all attached networks by sending to the special address INADDR_BROADCAST, defined in netinet/in.h.
The system provides a mechanism to determine a number of pieces of information (including the IP address and broadcast address) about the network interfaces on the system. The SIOCGIFCONF ioctl() call returns the interface configuration of a host in a single ifconf structure. This structure contains an array of ifreq structures, one for each address domain supported by each network interface to which the host is connected. Example 2-19 shows these structures defined in net/if.h.
struct ifreq { #define IFNAMSIZ 16 char ifr_name[IFNAMSIZ]; /* if name, e.g., "en0" */ union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; char ifru_oname[IFNAMSIZ]; /* other if name */ struct sockaddr ifru_broadaddr; short ifru_flags; int ifru_metric; char ifru_data[1]; /* interface dependent data */ char ifru_enaddr[6]; } ifr_ifru; #define ifr_addr ifr_ifru.ifru_addr #define ifr_dstaddr ifr_ifru.ifru_dstaddr #define ifr_oname ifr_ifru.ifru_oname #define ifr_broadaddr ifr_ifru.ifru_broadaddr #define ifr_flags ifr_ifru.ifru_flags #define ifr_metric ifr_ifru.ifru_metric #define ifr_data ifr_ifru.ifru_data #define ifr_enaddr ifr_ifru.ifru_enaddr }; |
The call that obtains the interface configuration is:
/* * Do SIOCGIFNUM ioctl to find the number of interfaces * * Allocate space for number of interfaces found * * Do SIOCGIFCONF with allocated buffer * */ if (ioctl(s, SIOCGIFNUM, (char *)&numifs) == -1) { numifs = MAXIFS; } bufsize = numifs * sizeof(struct ifreq); reqbuf = (struct ifreq *)malloc(bufsize); if (reqbuf == NULL) { fprintf(stderr, "out of memory\n"); exit(1); } ifc.ifc_buf = (caddr_t)&reqbuf[0]; ifc.ifc_len = bufsize; if (ioctl(s, SIOCGIFCONF, (char *)&ifc) == -1) { perror("ioctl(SIOCGIFCONF)"); exit(1); } ... } |
After this call, buf contains an array of ifreq structures, one for each network to which the host is connected. These structures are ordered first by interface name, then by supported address families. ifc.ifc_len is set to the number of bytes used by the ifreq structures.
Each structure has a set of interface flags that tell whether the corresponding network is up or down, point-to-point or broadcast, and so on. Example 2-20 shows the SIOCGIFFLAGS ioctl() returning these flags for an interface specified by an ifreq structure.
struct ifreq *ifr; ifr = ifc.ifc_req; for (n = ifc.ifc_len/sizeof (struct ifreq); --n >= 0; ifr++) { /* * Be careful not to use an interface devoted to an address * domain other than those intended. */ if (ifr->ifr_addr.sa_family != AF_INET) continue; if (ioctl(s, SIOCGIFFLAGS, (char *) ifr) < 0) { ... } /* Skip boring cases */ if ((ifr->ifr_flags & IFF_UP) == 0 || (ifr->ifr_flags & IFF_LOOPBACK) || (ifr->ifr_flags & (IFF_BROADCAST | IFF_POINTOPOINT)) == 0) continue; } |
Example 2-21 shows the broadcast of an interface can be obtained with the SIOGGIFBRDADDR ioctl().
if (ioctl(s, SIOCGIFBRDADDR, (char *) ifr) < 0) { ... } memcpy((char *) &dst, (char *) &ifr->ifr_broadaddr, sizeof ifr->ifr_broadaddr); |
The SIOGGIFBRDADDR ioctl() can also be used to get the destination address of a point-to-point interface.
After the interface broadcast address is obtained, transmit the broadcast datagram with sendto():
sendto(s, buf, buflen, 0, (struct sockaddr *)&dst, sizeof dst); |
Use one sendto() for each interface to which the host is connected that supports the broadcast or point-to-point addressing.