Dit artikel is beschikbaar in: English Castellano Deutsch Francais Nederlands Russian Turkce Arabic |
door Over de auteur: Angel is aan het afstuderen in Computer Engineering. Op dit moment geeft hij les als docent bij Sun Microsystems in Solaris en netwerkbeheer. Hij heeft net als co-auteur met Ra-Ma een boek uitgebracht met als titel Internet design and implementation on Unix systems. Het meest interessant vindt hij netwerken, beveiliging, programmeren onder Unix en, sinds kort, houdt het hacken in de Linux kernel hem wakker ;) Inhoud: |
Kort:
Dit artikel gaat in op de multicast technologie in een TCP/IP netwerk. Het gaat in op de theorie over het gebruik van Multicast communicatie en de Linuxkoppeling die we hiervoor kunnen gebruiken. Om het geheel te completeren wordt ook de kernel-code bekeken die dit implementeert onder Linux. Het wordt afgerond met een voorbeeldprogramma in C dat het gebruik van Multicast in een applicatie illustreert.
De vier meest-significante bits van het adres bepalen de waarde tussen 224 en 239. De andere 28 bits vormen de groepsidentificatie, zoals hieronder weergegeven:
Op netwerkniveau moeten de Multicast adressen naar echte adressen worden vertaald, afhankelijk van het soort netwerk. Voor unicast kan hiervoor het ARP protocol worden gebruikt. Dit kan echter niet voor Multicast adressen en dus zal dit op een andere manier moeten. Een aantal RFC documenten gaan in op het vertalen van een Multicast adres naar echte adressen:
Er zijn een aantal bijzondere Multicast adressen:
Onderstaande tabel geeft een overzicht van het complete Multicast adresbereik met de gebruikelijke benoeming van ieder bereik en de bijbehorende TTL teller (Time To Live teller in ieder IP-pakket). In IP versie 4 heeft deze teller een dubbele betekenis. Zoals waarschijnlijk bekend bepaalt deze teller de levensduur van een datagram in het netwerk om oneindige lussen door verkeerde configuratie van routing-tabellen tegen te gaan. Binnen Multicast bepaalt de teller ook het bereik van het datagram (oftewel hoe ver het pakket in het netwerk mag gaan). Hiermee wordt het mogelijk een bereik te definiëren op basis van de categorie van een datagram.
Bereik | TTL | Adres bereik | Beschrijving |
Machine | 0 | Het verkeer is beperkt tot de lokale machine, het zal niet via een koppeling het netwerk op gaan. | |
Link | 1 | 224.0.0.0 - 224.0.0.255 | Dit verkeer blijft in het lokale netwerk en zal niet langs een router komen. |
Afdeling | < 32 | 239.255.0.0 - 239.255.255.255 | Verkeer beperkt tot de afdeling van een organisatie. |
Organisatie | < 64 | 239.192.0.0 - 239.195.255.255 | Beperkt tot een bepaalde organisatie. |
Globaal | < 255 | 224.0.1.0 - 238.255.255.255 | Geen beperking. |
Aldus zal de koppeling met fysiek adres 80:C0:F6:A0:4A:B1 die zich bij Multicast groep 224.0.1.10 heeft aangesloten, alle pakketten doorgeven, gericht aan één van de volgende adressen:
Routers zenden ook IGMP-pakketten naar groep 224.0.0.1 om machines te vragen op welke groepen ze geabonneerd zijn. Machines zullen niet direct antwoorden maar een teller op een willekeurige waarde zetten. Pas als deze teller op nul staat zal een machine antwoorden. Dit om te voorkomen dat alle machines tegelijk antwoorden en zo teveel verkeer op het netwerk genereren. Een machine die aldus antwoordt zal de adressen van de groep uitzenden waarop hij is geabonneerd. Andere machines met hetzelfde abonnement vangen dit op en zullen dit niet meer antwoorden. Eén antwoord van een machine is voor een router voldoende om te weten welk Multicast-verkeer hij door moet sturen.
Wanneer alle machines van de groep zijn afgekoppeld zal er geen antwoord meer komen op dit soort verkeer en zullen routers dus concluderen dat niemand meer op de groep is geabonneerd. Het doorgeven van Multicast verkeer voor dit adres zal dan worden gestopt. Een andere mogelijkheid in IGMP versie 2 is het versturen van een pakket vanuit de machine waarmee men expliciet het abonnement opzegt via adres 224.0.0.2.
setsockopt()
en getsockopt()
verzorgen deze extra mogelijkheden. Onderstaande tabel geeft details van de vijf mogelijkheden voor Multicast, tezamen met de datatypen en een korte omschrijving.
IPv4 Optie | Data type | Beschrijving |
IP_ADD_MEMBERSHIP | struct ip_mreq | Abonneer je op de Multicast groep. |
IP_DROP_MEMBERSHIP | struct ip_mreq | Zeg het abonnement op. |
IP_MULTICAST_IF | struct ip_mreq | Geef een bepaalde koppeling op voor het versturen van Multicast berichten. |
IP_MULTICAST_TTL | u_char | Geef een TTL (Time To Live) op voor de berichten. |
IP_MULTICAST_LOOP | u_char | Activeren/Deactiveren van de test-loopback voor Multicast berichten. |
ip_mreq
is gedefinieerd in het bestand <linux/in.h>
, zoals hieronder weergegeven:
struct ip_mreq { struct in_addr imr_multiaddr; /* IP multicast address of group */ struct in_addr imr_interface; /* local IP address of interface */ };De volgende opties gelden voor Multicast:
#define IP_MULTICAST_IF 32 #define IP_MULTICAST_TTL 33 #define IP_MULTICAST_LOOP 34 #define IP_ADD_MEMBERSHIP 35 #define IP_DROP_MEMBERSHIP 36
setsockopt()
. Als parameter wordt een ip_mreq
meegegeven. Het eerste veld, imr_multiaddr
, geeft de groep waarop we ons willen abonneren. Het tweede veld, imr_interface
, geeft het IP-adres van de koppeling die we hiervoor zullen gebruiken.
ip_mreq
worden op dezelfde manier gebruikt als hierboven omschreven.
Het volgende programma is een server die alles wat aan de invoer wordt aangeboden (stdin
) naar de Multicast groep 224.0.1.1 uitzend. Zoals te zien hoeven er geen bijzondere handelingen te worden verricht. Het opgeven van het adres voldoet.
Opties aangaande loopback en TTL kunnen worden veranderd mochten de standaard waarden niet voldoen.
stdin
) wordt doorgestuurd naar Multicast groep 224.0.1.1:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> #define MAXBUF 256 #define PUERTO 5000 #define GRUPO "224.0.1.1" int main(void) { int s; struct sockaddr_in srv; char buf[MAXBUF]; bzero(&srv, sizeof(srv)); srv.sin_family = AF_INET; srv.sin_port = htons(PUERTO); if (inet_aton(GRUPO, &srv.sin_addr) < 0) { perror("inet_aton"); return 1; } if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); return 1; } while (fgets(buf, MAXBUF, stdin)) { if (sendto(s, buf, strlen(buf), 0, (struct sockaddr *)&srv, sizeof(srv)) < 0) { perror("recvfrom"); } else { fprintf(stdout, "Enviado a %s: %s\n", GRUPO, buf); } } }
stdout
) doorgestuurd. Het enige nieuwe aan deze code is het gebruik van de optie IP_ADD_MEMBERSHIP
. De rest van de code is de gebruikelijke voor ontvangst van udp pakketjes.
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #define MAXBUF 256 #define PUERTO 5000 #define GRUPO "224.0.1.1" int main(void) { int s, n, r; struct sockaddr_in srv, cli; struct ip_mreq mreq; char buf[MAXBUF]; bzero(&srv, sizeof(srv)); srv.sin_family = AF_INET; srv.sin_port = htons(PUERTO); if (inet_aton(GRUPO, &srv.sin_addr) < 0) { perror("inet_aton"); return 1; } if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); return 1; } if (bind(s, (struct sockaddr *)&srv, sizeof(srv)) < 0) { perror("bind"); return 1; } if (inet_aton(GRUPO, &mreq.imr_multiaddr) < 0) { perror("inet_aton"); return 1; } mreq.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { perror("setsockopt"); return 1; } n = sizeof(cli); while (1) { if ((r = recvfrom(s, buf, MAXBUF, 0, (struct sockaddr *) &cli, &n)) < 0) { perror("recvfrom"); } else { buf[r] = 0; fprintf(stdout, "Mensaje desde %s: %s\n", inet_ntoa(cli.sin_addr), buf); } } }
setsockopt()
functie gebruiken met de optie IP_ADD_MEMBERSHIP
. De code voor het uitvoeren van deze functie kun je vinden in /usr/src/linux/net/ipv4/ip_sockglue.c. De code om deze optie te activeren (of te deactiveren met IP_DROP_MEMBERSHIP
) is als volgt:
struct ip_mreqn mreq; if (optlen < sizeof(struct ip_mreq)) return -EINVAL; if (optlen >= sizeof(struct ip_mreqn)) { if(copy_from_user(&mreq,optval,sizeof(mreq))) return -EFAULT; } else { memset(&mreq, 0, sizeof(mreq)); if (copy_from_user(&mreq,optval,sizeof(struct ip_mreq))) return -EFAULT; } if (optname == IP_ADD_MEMBERSHIP) return ip_mc_join_group(sk,&mreq); else return ip_mc_leave_group(sk,&mreq);De eerste regels code controleren of de parameter
ip_mreq
de juiste lengte heeft om te kunnen kopiëren naar kernelgeheugen. Afhankelijk van de gegeven optie wordt dan ip_mc_join_group()
of ip_mc_leave_group()
aangeroepen als we ons op een groep willen abonneren respectievelijk op willen zeggen.
De code voor deze functies kun je vinden in /usr/src/linux/net/ipv4/igmp.c. Voor het abonneren op een groep is de code als volgt:
int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr) { int err; u32 addr = imr->imr_multiaddr.s_addr; struct ip_mc_socklist, *iml, *i; struct in_device *in_dev; int count = 0;Allereerst wordt met de
MULTICAST
macro gecontroleerd of het adres wel een echt Multicast adres is. Controle op de waarde 224 in het hoogste byte van het adres volstaat.
if (!MULTICAST(addr)) return -EINVAL; rtnl_shlock();Vervolgens wordt een netwerkkoppeling aangemaakt die de groep moet afhandelen. Als dit niet via een index kan, zoals onder IP versie 6 mogelijk moet zijn, dan wordt
ip_mc_find_dev()
aangeroepen voor het vinden van de juiste koppeling. Voor dit artikel zullen we aannemen dat we met IP versie 4 werken. Als het adres de waarde INADDR_ANY
heeft dan gaat de kernel zelf op zoek naar de beste koppeling om dit verkeer af te handelen, wat afhankelijk is van het groepsadres zelf en de inhoud van de routing-tabellen.
if (!imr->imr_ifindex) in_dev = ip_mc_find_dev(imr); else in_dev = inetdev_by_index(imr->imr_ifindex); if (!in_dev) { iml = NULL; err = -ENODEV; goto done; }Vervolgens wordt geheugen gereserveerd voor een
struct ip_mc_socklist
en ieder groepsadres wordt vergeleken met de koppeling. Als we er reeds een vinden dan verlaten we de functie omdat het geen zin heeft een dubbele verbinding op te zetten naar de koppeling. Als de optie INADDR_ANY
niet wordt gebruikt zal de betreffende teller worden opgehoogd voordat de functie beëindigt.
iml = (struct ip_mc_socklist *)sock_kmalloc(sk, sizeof(*iml), GFP_KERNEL); err = -EADDRINUSE; for (i=sk->ip_mc_list; i; i=i->next) { if (memcmp(&i->multi, imr, sizeof(*imr)) == 0) { /* New style additions are reference counted */ if (imr->imr_address.s_addr == 0) { i->count++; err = 0; } goto done; } count++; } err = -ENOBUFS; if (iml == NULL || count >= sysctl_igmp_max_memberships) goto done;Op dit punt aangekomen moeten we een nieuwe socket met een nieuwe groep aanmaken. Er moet dus een nieuwe inschrijving worden aangemaakt en in de lijst van groepen worden gezet die met deze socket worden geassocieerd. Het geheugen hiervoor was al gereserveerd en dus moeten we alleen de desbetreffende velden op correcte wijze invullen.
memcpy(&iml->multi,imr, sizeof(*imr)); iml->next = sk->ip_mc_list; iml->count = 1; sk->ip_mc_list = iml; ip_mc_inc_group(in_dev,addr); iml = NULL; err = 0; done: rtnl_shunlock(); if (iml) sock_kfree_s(sk, iml, sizeof(*iml)); return err; }De functie
ip_mc_leave_group()
gaat over het afmelden van een groep en is een stuk eenvoudiger dan de vorige functie. Deze zoekt de lijsten af op de juiste inschrijving en verlaagt de betreffende groepsteller. Als deze op nul staat wordt de inschrijving zélf weggegooid.
int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr) { struct ip_mc_socklist *iml, **imlp; for (imlp=&sk->ip_mc_list;(iml=*imlp)!=NULL; imlp=&iml->next) { if (iml->multi.imr_multiaddr.s_addr==imr->imr_multiaddr.s_addr && iml->multi.imr_address.s_addr==imr->imr_address.s_addr && (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) { struct in_device *in_dev; if (--iml->count) return 0; *imlp = iml->next; synchronize_bh(); in_dev = inetdev_by_index(iml->multi.imr_ifindex); if (in_dev) ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr); sock_kfree_s(sk, iml, sizeof(*iml)); return 0; } } return -EADDRNOTAVAIL; }De andere Multicast opties die we boven hebben besproken zijn heel eenvoudig omdat ze alleen maar wat waarden veranderen in de velden die bij de groepsstructuur horen. Deze worden direct uitgevoerd door de functie
ip_setsockopt()
.
|
Site onderhouden door het LinuxFocus editors team © Angel Lopez, FDL LinuxFocus.org Klik hier om een fout te melden of commentaar te geven |
Vertaling info:
|
2001-02-02, generated by lfparser version 2.8