308 lines · 5.6 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * igmp - internet group management protocol * unfinished. */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../port/error.h" #include "ip.h" enum{	IGMP_IPHDRSIZE	= 20,		/* size of ip header */	IGMP_HDRSIZE	= 8,		/* size of IGMP header */	IP_IGMPPROTO	= 2, 	IGMPquery	= 1,	IGMPreport	= 2, 	MSPTICK		= 100,	MAXTIMEOUT	= 10000/MSPTICK,	/* at most 10 secs for a response */}; typedef struct IGMPpkt IGMPpkt;struct IGMPpkt{	/* ip header */	uchar	vihl;		/* Version and header length */	uchar	tos;		/* Type of service */	uchar	len[2];		/* packet length (including headers) */	uchar	id[2];		/* Identification */	uchar	frag[2];	/* Fragment information */	uchar	Unused;	uchar	proto;		/* Protocol */	uchar	cksum[2];	/* checksum of ip portion */	uchar	src[IPaddrlen];		/* Ip source */	uchar	dst[IPaddrlen];		/* Ip destination */ 	/* igmp header */	uchar	vertype;	/* version and type */	uchar	unused;	uchar	igmpcksum[2];		/* checksum of igmp portion */	uchar	group[IPaddrlen];	/* multicast group */ 	uchar	payload[];}; #define IGMPPKTSZ offsetof(IGMPpkt, payload[0]) /* *  lists for group reports */typedef struct IGMPrep IGMPrep;struct IGMPrep{	IGMPrep		*next;	Medium		*m;	int		ticks;	Multicast	*multi;}; typedef struct IGMP IGMP;struct IGMP{	Lock;	Rendez	r;	IGMPrep	*reports;}; IGMP igmpalloc; 	Proto	igmp;extern	Fs	fs; static struct Stats{	ulong 	inqueries;	ulong	outqueries;	ulong	inreports;	ulong	outreports;} stats; voidigmpsendreport(Medium *m, uchar *addr){	IGMPpkt *p;	Block *bp; 	bp = allocb(sizeof(IGMPpkt));	if(bp == nil)		return;	p = (IGMPpkt*)bp->wp;	p->vihl = IP_VER4;	bp->wp += IGMPPKTSZ;	memset(bp->rp, 0, IGMPPKTSZ);	hnputl(p->src, Mediumgetaddr(m));	hnputl(p->dst, Ipallsys);	p->vertype = (1<<4) | IGMPreport;	p->proto = IP_IGMPPROTO;	memmove(p->group, addr, IPaddrlen);	hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE));	netlog(Logigmp, "igmpreport %I\n", p->group);	stats.outreports++;	ipoput4(bp, 0, 1, DFLTTOS, nil);	/* TTL of 1 */} static intisreport(void *a){	USED(a);	return igmpalloc.reports != 0;}  voidigmpproc(void *a){	IGMPrep *rp, **lrp;	Multicast *mp, **lmp;	uchar ip[IPaddrlen]; 	USED(a); 	for(;;){		sleep(&igmpalloc.r, isreport, 0);		for(;;){			lock(&igmpalloc); 			if(igmpalloc.reports == nil)				break; 			/* look for a single report */			lrp = &igmpalloc.reports;			mp = nil;			for(rp = *lrp; rp; rp = *lrp){				rp->ticks++;				lmp = &rp->multi;				for(mp = *lmp; mp; mp = *lmp){					if(rp->ticks >= mp->timeout){						*lmp = mp->next;						break;					}					lmp = &mp->next;				}				if(mp != nil)					break; 				if(rp->multi != nil){					lrp = &rp->next;					continue;				} else {					*lrp = rp->next;					free(rp);				}			}			unlock(&igmpalloc); 			if(mp){				/* do a single report and try again */				hnputl(ip, mp->addr);				igmpsendreport(rp->m, ip);				free(mp);				continue;			} 			tsleep(&up->sleep, return0, 0, MSPTICK);		}		unlock(&igmpalloc);	} } voidigmpiput(Medium *m, Ipifc *, Block *bp){	int n;	IGMPpkt *ghp;	Ipaddr group;	IGMPrep *rp, **lrp;	Multicast *mp, **lmp; 	ghp = (IGMPpkt*)(bp->rp);	netlog(Logigmp, "igmpiput: %d %I\n", ghp->vertype, ghp->group); 	n = blocklen(bp);	if(n < IGMP_IPHDRSIZE+IGMP_HDRSIZE){		netlog(Logigmp, "igmpiput: bad len\n");		goto error;	}	if((ghp->vertype>>4) != 1){		netlog(Logigmp, "igmpiput: bad igmp type\n");		goto error;	}	if(ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)){		netlog(Logigmp, "igmpiput: checksum error %I\n", ghp->src);		goto error;	} 	group = nhgetl(ghp->group); 	lock(&igmpalloc);	switch(ghp->vertype & 0xf){	case IGMPquery:		/*		 *  start reporting groups that we're a member of.		 */		stats.inqueries++;		for(rp = igmpalloc.reports; rp; rp = rp->next)			if(rp->m == m)				break;		if(rp != nil)			break;	/* already reporting */ 		mp = Mediumcopymulti(m);		if(mp == nil)			break; 		rp = malloc(sizeof(*rp));		if(rp == nil)			break; 		rp->m = m;		rp->multi = mp;		rp->ticks = 0;		for(; mp; mp = mp->next)			mp->timeout = nrand(MAXTIMEOUT);		rp->next = igmpalloc.reports;		igmpalloc.reports = rp; 		wakeup(&igmpalloc.r); 		break;	case IGMPreport:		/*		 *  find report list for this medium		 */		stats.inreports++;		lrp = &igmpalloc.reports;		for(rp = *lrp; rp; rp = *lrp){			if(rp->m == m)				break;			lrp = &rp->next;		}		if(rp == nil)			break; 		/*		 *  if someone else has reported a group,		 *  we don't have to.		 */		lmp = &rp->multi;		for(mp = *lmp; mp; mp = *lmp){			if(mp->addr == group){				*lmp = mp->next;				free(mp);				break;			}			lmp = &mp->next;		} 		break;	}	unlock(&igmpalloc); error:	freeb(bp);} intigmpstats(char *buf, int len){	return snprint(buf, len, "\trcvd %d %d\n\tsent %d %d\n",		stats.inqueries, stats.inreports,		stats.outqueries, stats.outreports);} voidigmpinit(Fs *fs){	igmp.name = "igmp";	igmp.connect = nil;	igmp.announce = nil;	igmp.ctl = nil;	igmp.state = nil;	igmp.close = nil;	igmp.rcv = igmpiput;	igmp.stats = igmpstats;	igmp.ipproto = IP_IGMPPROTO;	igmp.nc = 0;	igmp.ptclsize = 0; 	igmpreportfn = igmpsendreport;	kproc("igmpproc", igmpproc, 0); 	Fsproto(fs, &igmp);}