Bei der Verwendung eines Datagramm-Sockets ist es möglich, Broadcast-Pakete an viele mit dem System verbundene Netzwerke zu senden. Das Netzwerk selbst muss Broadcasts unterstützen, da das System keine Simulation von Broadcasts in der Software unterstützt. Broadcast-Nachrichten können eine hohe Netzwerklast erzeugen, da sie jeden Rechner im Netzwerk zwingen, sie zu bedienen.
Broadcasting wird nur in der AF_INET-Adressfamilie angeboten, da es in IPv6 keinen Broadcast-Mechanismus gibt.
Broadcasting wird in der Regel aus einem der beiden folgenden Gründe verwendet:
In einem lokalen Netzwerk soll eine Ressource gefunden werden, deren Adresse zunächst unbekannt ist.
Wichtige Funktionen, wie z.B. die Routing-Funktion, wollen Informationen an alle erreichbaren Rechner senden.
Um eine Broadcast-Nachricht zu senden, muss
- zunächst ein Datagramm-Socket erzeugt werden,
- dann erhält der Socket die Markierung, dass er Broadcast-Nachrichten senden darf und
- dann wird dem Socket eine Portnummer zugeordnet:
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);
Die Zieladresse der Nachricht, die als Broadcast-Nachricht gesendet werden soll, hängt vom Netzwerk oder den Netzwerken ab, auf dem oder denen die Nachricht gesendet wird. Es gibt eine Kurzbezeichnung für Broadcast im lokalen Netzwerk, nämlich die Adresse INADDR_BROADCAST (definiert in <netinet/in.h>).
BS2000 unterstützt eine Methode, mit der Informationen über die Netzwerkschnittstellen des eigenen Rechners erfragt werden können. Der ioctl()-Aufruf SIOCGIFCONF oder SIOCGLIFCONF liefert die IPv4-Netzwerk-Konfiguration des Rechners als Struktur ifconf oder lifconf. Diese enthält einen unter anderem eine Liste von ifreq- oder lifreq-Strukturen. In dieser Liste gibt es eine ifreq- oder lifreq-Struktur für jede Netzwerkschnittstellen des Rechners.
Die Struktur lifreq ist in <net/if.h> wie folgt deklariert:
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 ... };
Der folgende Programmcode liefert die IPv4-Interface-Konfiguration:
- Der Aufruf von ioctl() mit SIOCGLIFNUM liefert die Anzahl der IPv4-Netzwerk-Interfaces.
- Der Aufruf von ioctl() mit SIOCGLIFCONF liefert die Liste der IPv4-Netzwerk-Interfaces.
- Die Aufrufe von ioctl() mit SIOCGLIFFLAGS liefern die Flags des jeweiligen Netzwerk-Interfaces, z.B. ob das Netzwerk-Interface aktiv und Broadcast-fähig ist.
- Die Aufrufe von ioctl() mit SIOCGLIFBRDADDR liefern die Broadcast-Adresse des jeweiligen Netzwerk-Interfaces.
#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; }
Nachdem eine passende Broadcast-Adresse ausgewählt ist, können Sie diese nun in der Funktion sendto() verwenden:
struct sockaddr_in *dst; ... dst = (struct sockaddr_in *)&(lifr.lifr_broadaddr); ... sendto(s, buf, buflen, 0, (struct sockaddr *)&dst, sizeof dst);
Dem sendenden Datagramm-Socket wird immer ein Name zugeordnet, entweder durch einen Aufruf der Funktion bind() oder implizit durch das System. Daher enthalten empfangene Broadcast-Nachrichten immer Adresse und Portnummer des Senders.
Mit dem BCAM-Kommando BCOPTION kann gesteuert werden, ob ein Netzwerk-Interface Broadcast-Nachrichten empfangen darf (siehe Handbuch "BCAM"). Mit den Socket-Funktionen setsockopt() und getsockopt() kann diese Einstellung weder beeinflusst noch ermittelt werden. Deshalb ist bei Nutzung des Broadcast-Mechanismus sicherzustellen, dass bei den betreffenden Rechnern diese Option eingeschaltet ist.