Thema: IGMP (Internet Group Management Protocol) zum Lesen von Multicast (MC) Paketen.
Aufgabe: T-Home-Multicast-Streams sollen empfangen werden.
Stichworte: UDP Multicast IGMP sockets IP_ADD_MEMBERSHIP recvmsg
Es gelten folgende Multicast-Quellen:
- 239.35.10.4:10000 ARD
- 239.35.10.5:10000 ZDF
- 239.35.10.18:10000 Arte
Was ist zu tun?
- Erzeugen eines socket auf dem lokalen Rechner; wichtig dabei:
- Die Adresse, auf der gelauscht wird, muss INADDR_ANY sein, damit der lokale kernel die Pakete nicht dropped.
- Der Port, auf dem gelauscht wird, muss der Port des Mutlicats-Ziels sein; im Beipiel also immer 10000.
- Falls mehrere Streams empfangen werden sollen, muessen also mehrere sockets mit gleicher IP/Port-Kombination geoeffnet werden;
damit dies funktioniert, muss die socket-Option SO_REUSEADDR gesetzt werden
- Die socket-Option IP_ADD_MEMBERSHIP muss gesetzt werden, dabei gibt die Struktur ip_mreq die Ziel-Multicast-Adresse
sowie die lokale Interface_Adresse an; (damit weiss der adressierte Router, auf dem die MCs anstehen, wohin die Pakete
weitergeleitet werden sollen.)
Durch diese Option wird ein IGMP-Paket an den Router des Providers gesendet.
Erhält der Router des Providers nun Pakete für die angegebene MC-Adresse, leitet er diese an alle Interfaces (Rechner) weiter,
die ein IP_ADD_MEMBERSHIP ausgesprochen haben.
Nachfolgend die essentiellen Systemcalls zur Lösung der Aufgabe.
// Socket anlegen:
int fd = socket(AF_INET, SOCK_DGRAM, 0);
// REUSEADDR fuer den socket setzen:
int reuse = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) {
// Fehlerbehandlung
}
// 'bind' auf ip=INADDR_ANY und Port=10000
struct sockaddr_in localSock;
localSock.sin_family = AF_INET;
localSock.sin_port = htons(10000);
localSock.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (struct sockaddr*)&localSock, sizeof(localSock))) {
// Fehlerbehandlung
}
// Struktur ip_mreq mit Ziel-MC und lokalem Interface fuellen, ADD_MEMBERSHIP f. MC aussprechen:
struct ip_mreq group;
group.imr_multiaddr.s_addr = inet_addr("239.35.10.4"); // Ziel-Multicast-Adresse
group.imr_interface.s_addr = inet_addr("192.168.2.104"); // lokales Interface
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0) {
// Fehlerbehandlung
}
// Daten vom Socket lesen (i.d.R. in einer loop):
int datalen;
char databuf[1024];
int bytes_read = read(fd, databuf, datalen);
Mit den o.a. Systemcalls kann man einfach einen MC empfangen, Moechte man gleichzeitig mehrerer MCs empfangen,
hat man mehrere Socket mit gleicher IP/Port-Kombination.
Ein einfache Loesung des Problems ist es, an einem socket nur die Pakete zu lesen, die zum gew. MC gehoeren.
Hier muss das Lesen modifiziert werden, um bei jedem empfangenen Paket die Ziel-Adresse ermitteln zu koennen.
(Statt 'read' oder 'recv' muss 'recvmsg' verwendet werden. Zusätzlich muss zuvor die Socket-Option IP_PKTINFO gesetzt werden;
man lese ggf. 'man ip', 'man recvmsg' u.a.)
Hier wieder auf die essentiellen Systemcalls beschraenkte Loesung:
// Weitere socket-Option setzen: Paket-Infos gefordert:
int on = 1;
setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
// recvmsg statt read, dafuer iovec und msghdr fuellen:
int bytes_received;
struct sockaddr_in6 from;
struct iovec iovec[1];
struct msghdr msg;
char msg_control[1024];
char udp_packet[1500];
iovec[0].iov_base = udp_packet;
iovec[0].iov_len = sizeof(udp_packet);
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = iovec;
msg.msg_iovlen = sizeof(iovec) / sizeof(*iovec);
msg.msg_control = msg_control;
msg.msg_controllen = sizeof(msg_control);
msg.msg_flags = 0;
bytes_received = recvmsg(soc, &msg, 0);
// Durch Control-Msgs iterieren, IP-Addr (IPPROTO_IP) suchen und lesen:
struct in_pktinfo in_pktinfo;
struct cmsghdr* cmsg;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != 0; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
in_pktinfo = *(struct in_pktinfo*)CMSG_DATA(cmsg);
// in_pktinfo_.ipi_addr hat jetzt die MC-Zeileadresse, vergleiche, ggf. verwerfen
}
}
Weiterfuehrende Links:
Wikipedia, IGMP
NETWORK PROGRAMMING LINUX SOCKET PART 13: MULTICAST
Get dst-adr of a udp-packet
|