xref: /plan9-contrib/sys/src/9/ip/igmp.c (revision cc057a51b3dcd6e5c4acbe143eb66067f72df965)
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