openNET Server
Using a datagram socket, it is possible to send broadcast packets to many networks connected to the system. The network itself must support broadcasts, since the system does not support simulation of broadcasts in the software. Broadcast messages can create a high network load because they force every machine on the network to serve them.
Broadcasting is only offered in the AF_INET address family since there is no broadcast mechanism in IPv6.
Broadcasting is typically used for one of the two reasons:
A resource is to be found in a local network whose address is initially unknown.
- Important functions, such as the routing function, want to send information to all accessible computers.
To send a broadcast message,
- first a datagram socket is generated,
- then the socket is marked as allowed to send broadcast messages and
- then a port number is assigned to the socket:
int on = 1; int s = socket(AF_INET, SOCK_DGRAM, 0); ... setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof on); 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 destination address of the message to be sent as a broadcast message depends on the network or networks on which the message is being sent. There is a short name for broadcast on the local network, namely the address INADDR_BROADCAST (defined in <netinet/in.h>).
BS2000 supports a method with which information about the network interfaces of the own computer can be queried. The ioctl() call SIOCGIFCONF or SIOCGLIFCONF returns the IPv4 network configuration of the computer as an ifconf or lifconf structure. Among other things, this contains a list of ifreq or lifreq structures. In this list, there is an ifreq or lifreq structure for each network interface on the computer.
The lifreq structure is declared in <net/if.h> as follows:
struct lifreq { char lifr_name[LIFNAMSIZ]; ... union { struct sockaddr_storage lifru_addr; struct sockaddr_storage lifru_broadaddr; unsigned long long lifru_flags; ... } lifr_lifru; #define lifr_addr lifr_lifru.lifru_addr #define lifr_broadaddr lifr_lifru.lifru_broadaddr #define lifr_flags lifr_lifru.lifru_flags ... };
The following program code provides the IPv4 interface configuration:
- Calling ioctl() with SIOCGLIFNUM returns the number of IPv4 network interfaces.
- Calling ioctl() with SIOCGLIFCONF returns the list of IPv4 network interfaces.
- The ioctl() calls with SIOCGLIFFLAGS return the flags of the respective network interface, e.g. whether the network interface is active and broadcast-capable.
- The ioctl() calls with SIOCGLIFBRDADDR return the broadcast address of the respective network interface.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <sys/sockio.h> #include <net/if.h> #include <unistd.h> #include <arpa/inet.h> #include <netdb.h> int main(int argc, char **argv) { int af=AF_INET, intaf; int s, n; struct lifconf lifc; struct lifreq * plifreq; struct lifreq lifr; # define NAMLEN sizeof(lifr.lifr_name) struct lifnum lif_num; int lifreq_num; void * addrptr; char addrstr[INET6_ADDRSTRLEN + 1]; s = socket(AF_INET, SOCK_DGRAM, 0); /*-------------- SIOCGLIFNUM --------------*/ lif_num.lifn_family = af; lif_num.lifn_flags = 0; if (ioctl(s, SIOCGLIFNUM, &lif_num) < 0) { perror("ioctl(SIOCGLIFNUM)"); return 1; } lifc.lifc_len = lif_num.lifn_count * sizeof(struct lifreq); lifc.lifc_buf = malloc(lifc.lifc_len); /*-------------- SIOCGLIFCONF --------------*/ lifc.lifc_family = af; lifc.lifc_flags = 0; if (ioctl(s, SIOCGLIFCONF, &lifc) < 0) { perror("ioctl(SIOCGLIFCONF)"); return 1; } lifreq_num = lifc.lifc_len / sizeof (struct lifreq); printf ("IPv4 interfaces: %d\n", lifreq_num); for (plifreq=lifc.lifc_req,n=0; n<lifreq_num; n++, plifreq++) { intaf = plifreq->lifr_addr.ss_family; if (intaf == AF_INET6) { addrptr = &((struct sockaddr_in6 *)&plifreq->lifr_addr)->sin6_addr; } else { addrptr = &((struct sockaddr_in *)&plifreq->lifr_addr)->sin_addr; } inet_ntop(intaf, addrptr, addrstr, INET6_ADDRSTRLEN); if (plifreq->lifr_addr.ss_family != af) { continue; } printf ("%-16s %-*s ", plifreq->lifr_name, INET_ADDRSTRLEN, addrstr); /*-------------- SIOCGLIFFLAGS --------------*/ memset(&lifr, 0, sizeof(lifr)); memcpy(&(lifr.lifr_name), plifreq->lifr_name, NAMLEN); if (ioctl(s, SIOCGLIFFLAGS, &lifr) < 0) { printf("UP=? BC=?\n"); } else { printf("UP=%s BC=%s ", lifr.lifr_flags & IFF_UP ? "yes" : "no ", lifr.lifr_flags & IFF_BROADCAST ? "yes" : "no "); if (lifr.lifr_flags & IFF_BROADCAST) { /*-------------- SIOCGLIFBRDADDR --------------*/ memset(&lifr, 0, sizeof(lifr)); memcpy(&(lifr.lifr_name), plifreq->lifr_name, NAMLEN); if (ioctl(s, SIOCGLIFBRDADDR, &lifr) < 0) { printf("BCADDR=?\n"); } else{ addrptr = &((struct sockaddr_in *)&(lifr.lifr_broadaddr))->sin_addr; inet_ntop(AF_INET, addrptr, addrstr, INET6_ADDRSTRLEN); printf("BCADDR=%s\n", addrstr); } } else { printf("\n"); } } } free(lifc.lifc_buf); return 0; }
After a suitable broadcast address has been selected, you can now use this in the sendto() function:
struct sockaddr_in *dst; ... dst = (struct sockaddr_in *)&(lifr.lifr_broadaddr); ... sendto(s, buf, buflen, 0, (struct sockaddr *)&dst, sizeof dst);
A name is always assigned to the sending datagram socket, either by a call to the bind() function or implicitly by the system. Therefore, received broadcast messages always contain the address and port number of the sender.
The BCAM command BCOPTION can be used to control whether a network interface can receive broadcast messages (see the "BCAM" manual). This setting can neither be influenced nor determined with the socket functions setsockopt() and getsockopt(). Therefore, when using the broadcast mechanism, it must be ensured that this option is activated on the computers concerned.