17ec5746aSDavid du Colombier /*
27ec5746aSDavid du Colombier * igmp - internet group management protocol
37ec5746aSDavid du Colombier * unfinished.
47ec5746aSDavid du Colombier */
57dd7cddfSDavid du Colombier #include "u.h"
67dd7cddfSDavid du Colombier #include "../port/lib.h"
77dd7cddfSDavid du Colombier #include "mem.h"
87dd7cddfSDavid du Colombier #include "dat.h"
97dd7cddfSDavid du Colombier #include "fns.h"
107dd7cddfSDavid du Colombier #include "../port/error.h"
117dd7cddfSDavid du Colombier
127dd7cddfSDavid du Colombier #include "ip.h"
137dd7cddfSDavid du Colombier
147dd7cddfSDavid du Colombier enum
157dd7cddfSDavid du Colombier {
167dd7cddfSDavid du Colombier IGMP_IPHDRSIZE = 20, /* size of ip header */
177dd7cddfSDavid du Colombier IGMP_HDRSIZE = 8, /* size of IGMP header */
187dd7cddfSDavid du Colombier IP_IGMPPROTO = 2,
197dd7cddfSDavid du Colombier
207dd7cddfSDavid du Colombier IGMPquery = 1,
217dd7cddfSDavid du Colombier IGMPreport = 2,
227dd7cddfSDavid du Colombier
237dd7cddfSDavid du Colombier MSPTICK = 100,
247dd7cddfSDavid du Colombier MAXTIMEOUT = 10000/MSPTICK, /* at most 10 secs for a response */
257dd7cddfSDavid du Colombier };
267dd7cddfSDavid du Colombier
277dd7cddfSDavid du Colombier typedef struct IGMPpkt IGMPpkt;
287dd7cddfSDavid du Colombier struct IGMPpkt
297dd7cddfSDavid du Colombier {
307dd7cddfSDavid du Colombier /* ip header */
317ec5746aSDavid du Colombier uchar vihl; /* Version and header length */
327ec5746aSDavid du Colombier uchar tos; /* Type of service */
337ec5746aSDavid du Colombier uchar len[2]; /* packet length (including headers) */
347ec5746aSDavid du Colombier uchar id[2]; /* Identification */
357ec5746aSDavid du Colombier uchar frag[2]; /* Fragment information */
367ec5746aSDavid du Colombier uchar Unused;
377ec5746aSDavid du Colombier uchar proto; /* Protocol */
387ec5746aSDavid du Colombier uchar cksum[2]; /* checksum of ip portion */
397ec5746aSDavid du Colombier uchar src[IPaddrlen]; /* Ip source */
407ec5746aSDavid du Colombier uchar dst[IPaddrlen]; /* Ip destination */
417dd7cddfSDavid du Colombier
427dd7cddfSDavid du Colombier /* igmp header */
437ec5746aSDavid du Colombier uchar vertype; /* version and type */
447ec5746aSDavid du Colombier uchar unused;
457ec5746aSDavid du Colombier uchar igmpcksum[2]; /* checksum of igmp portion */
467ec5746aSDavid du Colombier uchar group[IPaddrlen]; /* multicast group */
477ec5746aSDavid du Colombier
487ec5746aSDavid du Colombier uchar payload[];
497dd7cddfSDavid du Colombier };
507dd7cddfSDavid du Colombier
517ec5746aSDavid du Colombier #define IGMPPKTSZ offsetof(IGMPpkt, payload[0])
527ec5746aSDavid du Colombier
537dd7cddfSDavid du Colombier /*
547dd7cddfSDavid du Colombier * lists for group reports
557dd7cddfSDavid du Colombier */
567dd7cddfSDavid du Colombier typedef struct IGMPrep IGMPrep;
577dd7cddfSDavid du Colombier struct IGMPrep
587dd7cddfSDavid du Colombier {
597dd7cddfSDavid du Colombier IGMPrep *next;
607ec5746aSDavid du Colombier Medium *m;
617dd7cddfSDavid du Colombier int ticks;
627dd7cddfSDavid du Colombier Multicast *multi;
637dd7cddfSDavid du Colombier };
647dd7cddfSDavid du Colombier
657dd7cddfSDavid du Colombier typedef struct IGMP IGMP;
667dd7cddfSDavid du Colombier struct IGMP
677dd7cddfSDavid du Colombier {
687dd7cddfSDavid du Colombier Lock;
697dd7cddfSDavid du Colombier Rendez r;
707dd7cddfSDavid du Colombier IGMPrep *reports;
717dd7cddfSDavid du Colombier };
727dd7cddfSDavid du Colombier
737dd7cddfSDavid du Colombier IGMP igmpalloc;
747dd7cddfSDavid du Colombier
757dd7cddfSDavid du Colombier Proto igmp;
767dd7cddfSDavid du Colombier extern Fs fs;
777dd7cddfSDavid du Colombier
787dd7cddfSDavid du Colombier static struct Stats
797dd7cddfSDavid du Colombier {
807dd7cddfSDavid du Colombier ulong inqueries;
817dd7cddfSDavid du Colombier ulong outqueries;
827dd7cddfSDavid du Colombier ulong inreports;
837dd7cddfSDavid du Colombier ulong outreports;
847dd7cddfSDavid du Colombier } stats;
857dd7cddfSDavid du Colombier
867dd7cddfSDavid du Colombier void
igmpsendreport(Medium * m,uchar * addr)877ec5746aSDavid du Colombier igmpsendreport(Medium *m, uchar *addr)
887dd7cddfSDavid du Colombier {
897dd7cddfSDavid du Colombier IGMPpkt *p;
907dd7cddfSDavid du Colombier Block *bp;
917dd7cddfSDavid du Colombier
927dd7cddfSDavid du Colombier bp = allocb(sizeof(IGMPpkt));
937dd7cddfSDavid du Colombier if(bp == nil)
947dd7cddfSDavid du Colombier return;
957dd7cddfSDavid du Colombier p = (IGMPpkt*)bp->wp;
963ff48bf5SDavid du Colombier p->vihl = IP_VER4;
977ec5746aSDavid du Colombier bp->wp += IGMPPKTSZ;
987ec5746aSDavid du Colombier memset(bp->rp, 0, IGMPPKTSZ);
997ec5746aSDavid du Colombier hnputl(p->src, Mediumgetaddr(m));
1007dd7cddfSDavid du Colombier hnputl(p->dst, Ipallsys);
1017dd7cddfSDavid du Colombier p->vertype = (1<<4) | IGMPreport;
1027dd7cddfSDavid du Colombier p->proto = IP_IGMPPROTO;
1037dd7cddfSDavid du Colombier memmove(p->group, addr, IPaddrlen);
1047dd7cddfSDavid du Colombier hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE));
1057dd7cddfSDavid du Colombier netlog(Logigmp, "igmpreport %I\n", p->group);
1067dd7cddfSDavid du Colombier stats.outreports++;
107a6a9e072SDavid du Colombier ipoput4(bp, 0, 1, DFLTTOS, nil); /* TTL of 1 */
1087dd7cddfSDavid du Colombier }
1097dd7cddfSDavid du Colombier
1107dd7cddfSDavid du Colombier static int
isreport(void * a)1117dd7cddfSDavid du Colombier isreport(void *a)
1127dd7cddfSDavid du Colombier {
1137dd7cddfSDavid du Colombier USED(a);
1147dd7cddfSDavid du Colombier return igmpalloc.reports != 0;
1157dd7cddfSDavid du Colombier }
1167dd7cddfSDavid du Colombier
1177dd7cddfSDavid du Colombier
1187dd7cddfSDavid du Colombier void
igmpproc(void * a)1197dd7cddfSDavid du Colombier igmpproc(void *a)
1207dd7cddfSDavid du Colombier {
1217dd7cddfSDavid du Colombier IGMPrep *rp, **lrp;
1227dd7cddfSDavid du Colombier Multicast *mp, **lmp;
1237ec5746aSDavid du Colombier uchar ip[IPaddrlen];
1247dd7cddfSDavid du Colombier
1257dd7cddfSDavid du Colombier USED(a);
1267dd7cddfSDavid du Colombier
1277dd7cddfSDavid du Colombier for(;;){
1287dd7cddfSDavid du Colombier sleep(&igmpalloc.r, isreport, 0);
1297dd7cddfSDavid du Colombier for(;;){
1307dd7cddfSDavid du Colombier lock(&igmpalloc);
1317dd7cddfSDavid du Colombier
1327dd7cddfSDavid du Colombier if(igmpalloc.reports == nil)
1337dd7cddfSDavid du Colombier break;
1347dd7cddfSDavid du Colombier
1357dd7cddfSDavid du Colombier /* look for a single report */
1367dd7cddfSDavid du Colombier lrp = &igmpalloc.reports;
1377dd7cddfSDavid du Colombier mp = nil;
1387dd7cddfSDavid du Colombier for(rp = *lrp; rp; rp = *lrp){
1397dd7cddfSDavid du Colombier rp->ticks++;
1407dd7cddfSDavid du Colombier lmp = &rp->multi;
1417dd7cddfSDavid du Colombier for(mp = *lmp; mp; mp = *lmp){
1427dd7cddfSDavid du Colombier if(rp->ticks >= mp->timeout){
1437dd7cddfSDavid du Colombier *lmp = mp->next;
1447dd7cddfSDavid du Colombier break;
1457dd7cddfSDavid du Colombier }
1467dd7cddfSDavid du Colombier lmp = &mp->next;
1477dd7cddfSDavid du Colombier }
1487dd7cddfSDavid du Colombier if(mp != nil)
1497dd7cddfSDavid du Colombier break;
1507dd7cddfSDavid du Colombier
1517dd7cddfSDavid du Colombier if(rp->multi != nil){
1527dd7cddfSDavid du Colombier lrp = &rp->next;
1537dd7cddfSDavid du Colombier continue;
1547dd7cddfSDavid du Colombier } else {
1557dd7cddfSDavid du Colombier *lrp = rp->next;
1567dd7cddfSDavid du Colombier free(rp);
1577dd7cddfSDavid du Colombier }
1587dd7cddfSDavid du Colombier }
1597dd7cddfSDavid du Colombier unlock(&igmpalloc);
1607dd7cddfSDavid du Colombier
1617dd7cddfSDavid du Colombier if(mp){
1627dd7cddfSDavid du Colombier /* do a single report and try again */
1637dd7cddfSDavid du Colombier hnputl(ip, mp->addr);
164*cc057a51SDavid du Colombier igmpsendreport(rp->medium, ip);
1657dd7cddfSDavid du Colombier free(mp);
1667dd7cddfSDavid du Colombier continue;
1677dd7cddfSDavid du Colombier }
1687dd7cddfSDavid du Colombier
169dc5a79c1SDavid du Colombier tsleep(&up->sleep, return0, 0, MSPTICK);
1707dd7cddfSDavid du Colombier }
1717dd7cddfSDavid du Colombier unlock(&igmpalloc);
1727dd7cddfSDavid du Colombier }
1737dd7cddfSDavid du Colombier
1747dd7cddfSDavid du Colombier }
1757dd7cddfSDavid du Colombier
1767dd7cddfSDavid du Colombier void
igmpiput(Medium * m,Ipifc *,Block * bp)1777ec5746aSDavid du Colombier igmpiput(Medium *m, Ipifc *, Block *bp)
1787dd7cddfSDavid du Colombier {
1797dd7cddfSDavid du Colombier int n;
1807dd7cddfSDavid du Colombier IGMPpkt *ghp;
1817dd7cddfSDavid du Colombier Ipaddr group;
1827dd7cddfSDavid du Colombier IGMPrep *rp, **lrp;
1837dd7cddfSDavid du Colombier Multicast *mp, **lmp;
1847dd7cddfSDavid du Colombier
1857dd7cddfSDavid du Colombier ghp = (IGMPpkt*)(bp->rp);
1867dd7cddfSDavid du Colombier netlog(Logigmp, "igmpiput: %d %I\n", ghp->vertype, ghp->group);
1877dd7cddfSDavid du Colombier
1887dd7cddfSDavid du Colombier n = blocklen(bp);
1897dd7cddfSDavid du Colombier if(n < IGMP_IPHDRSIZE+IGMP_HDRSIZE){
1907dd7cddfSDavid du Colombier netlog(Logigmp, "igmpiput: bad len\n");
1917dd7cddfSDavid du Colombier goto error;
1927dd7cddfSDavid du Colombier }
1937dd7cddfSDavid du Colombier if((ghp->vertype>>4) != 1){
1947dd7cddfSDavid du Colombier netlog(Logigmp, "igmpiput: bad igmp type\n");
1957dd7cddfSDavid du Colombier goto error;
1967dd7cddfSDavid du Colombier }
1977dd7cddfSDavid du Colombier if(ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)){
1987dd7cddfSDavid du Colombier netlog(Logigmp, "igmpiput: checksum error %I\n", ghp->src);
1997dd7cddfSDavid du Colombier goto error;
2007dd7cddfSDavid du Colombier }
2017dd7cddfSDavid du Colombier
2027dd7cddfSDavid du Colombier group = nhgetl(ghp->group);
2037dd7cddfSDavid du Colombier
2047dd7cddfSDavid du Colombier lock(&igmpalloc);
2057dd7cddfSDavid du Colombier switch(ghp->vertype & 0xf){
2067dd7cddfSDavid du Colombier case IGMPquery:
2077dd7cddfSDavid du Colombier /*
2087dd7cddfSDavid du Colombier * start reporting groups that we're a member of.
2097dd7cddfSDavid du Colombier */
2107dd7cddfSDavid du Colombier stats.inqueries++;
2117dd7cddfSDavid du Colombier for(rp = igmpalloc.reports; rp; rp = rp->next)
212*cc057a51SDavid du Colombier if(rp->medium == m)
2137dd7cddfSDavid du Colombier break;
2147dd7cddfSDavid du Colombier if(rp != nil)
2157dd7cddfSDavid du Colombier break; /* already reporting */
2167dd7cddfSDavid du Colombier
2177ec5746aSDavid du Colombier mp = Mediumcopymulti(m);
2187dd7cddfSDavid du Colombier if(mp == nil)
2197dd7cddfSDavid du Colombier break;
2207dd7cddfSDavid du Colombier
2217dd7cddfSDavid du Colombier rp = malloc(sizeof(*rp));
2227dd7cddfSDavid du Colombier if(rp == nil)
2237dd7cddfSDavid du Colombier break;
2247dd7cddfSDavid du Colombier
225*cc057a51SDavid du Colombier rp->medium = m;
2267dd7cddfSDavid du Colombier rp->multi = mp;
2277dd7cddfSDavid du Colombier rp->ticks = 0;
2287dd7cddfSDavid du Colombier for(; mp; mp = mp->next)
2297dd7cddfSDavid du Colombier mp->timeout = nrand(MAXTIMEOUT);
2307dd7cddfSDavid du Colombier rp->next = igmpalloc.reports;
2317dd7cddfSDavid du Colombier igmpalloc.reports = rp;
2327dd7cddfSDavid du Colombier
2337dd7cddfSDavid du Colombier wakeup(&igmpalloc.r);
2347dd7cddfSDavid du Colombier
2357dd7cddfSDavid du Colombier break;
2367dd7cddfSDavid du Colombier case IGMPreport:
2377dd7cddfSDavid du Colombier /*
2387dd7cddfSDavid du Colombier * find report list for this medium
2397dd7cddfSDavid du Colombier */
2407dd7cddfSDavid du Colombier stats.inreports++;
2417dd7cddfSDavid du Colombier lrp = &igmpalloc.reports;
2427dd7cddfSDavid du Colombier for(rp = *lrp; rp; rp = *lrp){
243*cc057a51SDavid du Colombier if(rp->medium == m)
2447dd7cddfSDavid du Colombier break;
2457dd7cddfSDavid du Colombier lrp = &rp->next;
2467dd7cddfSDavid du Colombier }
2477dd7cddfSDavid du Colombier if(rp == nil)
2487dd7cddfSDavid du Colombier break;
2497dd7cddfSDavid du Colombier
2507dd7cddfSDavid du Colombier /*
2517dd7cddfSDavid du Colombier * if someone else has reported a group,
2527dd7cddfSDavid du Colombier * we don't have to.
2537dd7cddfSDavid du Colombier */
2547dd7cddfSDavid du Colombier lmp = &rp->multi;
2557dd7cddfSDavid du Colombier for(mp = *lmp; mp; mp = *lmp){
2567dd7cddfSDavid du Colombier if(mp->addr == group){
2577dd7cddfSDavid du Colombier *lmp = mp->next;
2587dd7cddfSDavid du Colombier free(mp);
2597dd7cddfSDavid du Colombier break;
2607dd7cddfSDavid du Colombier }
2617dd7cddfSDavid du Colombier lmp = &mp->next;
2627dd7cddfSDavid du Colombier }
2637dd7cddfSDavid du Colombier
2647dd7cddfSDavid du Colombier break;
2657dd7cddfSDavid du Colombier }
2667dd7cddfSDavid du Colombier unlock(&igmpalloc);
2677dd7cddfSDavid du Colombier
2687dd7cddfSDavid du Colombier error:
2697dd7cddfSDavid du Colombier freeb(bp);
2707dd7cddfSDavid du Colombier }
2717dd7cddfSDavid du Colombier
2727dd7cddfSDavid du Colombier int
igmpstats(char * buf,int len)2737dd7cddfSDavid du Colombier igmpstats(char *buf, int len)
2747dd7cddfSDavid du Colombier {
2757dd7cddfSDavid du Colombier return snprint(buf, len, "\trcvd %d %d\n\tsent %d %d\n",
2767dd7cddfSDavid du Colombier stats.inqueries, stats.inreports,
2777dd7cddfSDavid du Colombier stats.outqueries, stats.outreports);
2787dd7cddfSDavid du Colombier }
2797dd7cddfSDavid du Colombier
2807dd7cddfSDavid du Colombier void
igmpinit(Fs * fs)2817dd7cddfSDavid du Colombier igmpinit(Fs *fs)
2827dd7cddfSDavid du Colombier {
2837dd7cddfSDavid du Colombier igmp.name = "igmp";
2847dd7cddfSDavid du Colombier igmp.connect = nil;
2857dd7cddfSDavid du Colombier igmp.announce = nil;
2867dd7cddfSDavid du Colombier igmp.ctl = nil;
2877dd7cddfSDavid du Colombier igmp.state = nil;
2887dd7cddfSDavid du Colombier igmp.close = nil;
2897dd7cddfSDavid du Colombier igmp.rcv = igmpiput;
2907dd7cddfSDavid du Colombier igmp.stats = igmpstats;
2917dd7cddfSDavid du Colombier igmp.ipproto = IP_IGMPPROTO;
2927dd7cddfSDavid du Colombier igmp.nc = 0;
2937dd7cddfSDavid du Colombier igmp.ptclsize = 0;
2947dd7cddfSDavid du Colombier
2957dd7cddfSDavid du Colombier igmpreportfn = igmpsendreport;
2967dd7cddfSDavid du Colombier kproc("igmpproc", igmpproc, 0);
2977dd7cddfSDavid du Colombier
2987dd7cddfSDavid du Colombier Fsproto(fs, &igmp);
2997dd7cddfSDavid du Colombier }
300