xref: /plan9-contrib/sys/src/9/ip/igmp.c (revision cc057a51b3dcd6e5c4acbe143eb66067f72df965)
1 /*
2  * igmp - internet group management protocol
3  * unfinished.
4  */
5 #include "u.h"
6 #include "../port/lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "../port/error.h"
11 
12 #include "ip.h"
13 
14 enum
15 {
16 	IGMP_IPHDRSIZE	= 20,		/* size of ip header */
17 	IGMP_HDRSIZE	= 8,		/* size of IGMP header */
18 	IP_IGMPPROTO	= 2,
19 
20 	IGMPquery	= 1,
21 	IGMPreport	= 2,
22 
23 	MSPTICK		= 100,
24 	MAXTIMEOUT	= 10000/MSPTICK,	/* at most 10 secs for a response */
25 };
26 
27 typedef struct IGMPpkt IGMPpkt;
28 struct IGMPpkt
29 {
30 	/* ip header */
31 	uchar	vihl;		/* Version and header length */
32 	uchar	tos;		/* Type of service */
33 	uchar	len[2];		/* packet length (including headers) */
34 	uchar	id[2];		/* Identification */
35 	uchar	frag[2];	/* Fragment information */
36 	uchar	Unused;
37 	uchar	proto;		/* Protocol */
38 	uchar	cksum[2];	/* checksum of ip portion */
39 	uchar	src[IPaddrlen];		/* Ip source */
40 	uchar	dst[IPaddrlen];		/* Ip destination */
41 
42 	/* igmp header */
43 	uchar	vertype;	/* version and type */
44 	uchar	unused;
45 	uchar	igmpcksum[2];		/* checksum of igmp portion */
46 	uchar	group[IPaddrlen];	/* multicast group */
47 
48 	uchar	payload[];
49 };
50 
51 #define IGMPPKTSZ offsetof(IGMPpkt, payload[0])
52 
53 /*
54  *  lists for group reports
55  */
56 typedef struct IGMPrep IGMPrep;
57 struct IGMPrep
58 {
59 	IGMPrep		*next;
60 	Medium		*m;
61 	int		ticks;
62 	Multicast	*multi;
63 };
64 
65 typedef struct IGMP IGMP;
66 struct IGMP
67 {
68 	Lock;
69 	Rendez	r;
70 	IGMPrep	*reports;
71 };
72 
73 IGMP igmpalloc;
74 
75 	Proto	igmp;
76 extern	Fs	fs;
77 
78 static struct Stats
79 {
80 	ulong 	inqueries;
81 	ulong	outqueries;
82 	ulong	inreports;
83 	ulong	outreports;
84 } stats;
85 
86 void
igmpsendreport(Medium * m,uchar * addr)87 igmpsendreport(Medium *m, uchar *addr)
88 {
89 	IGMPpkt *p;
90 	Block *bp;
91 
92 	bp = allocb(sizeof(IGMPpkt));
93 	if(bp == nil)
94 		return;
95 	p = (IGMPpkt*)bp->wp;
96 	p->vihl = IP_VER4;
97 	bp->wp += IGMPPKTSZ;
98 	memset(bp->rp, 0, IGMPPKTSZ);
99 	hnputl(p->src, Mediumgetaddr(m));
100 	hnputl(p->dst, Ipallsys);
101 	p->vertype = (1<<4) | IGMPreport;
102 	p->proto = IP_IGMPPROTO;
103 	memmove(p->group, addr, IPaddrlen);
104 	hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE));
105 	netlog(Logigmp, "igmpreport %I\n", p->group);
106 	stats.outreports++;
107 	ipoput4(bp, 0, 1, DFLTTOS, nil);	/* TTL of 1 */
108 }
109 
110 static int
isreport(void * a)111 isreport(void *a)
112 {
113 	USED(a);
114 	return igmpalloc.reports != 0;
115 }
116 
117 
118 void
igmpproc(void * a)119 igmpproc(void *a)
120 {
121 	IGMPrep *rp, **lrp;
122 	Multicast *mp, **lmp;
123 	uchar ip[IPaddrlen];
124 
125 	USED(a);
126 
127 	for(;;){
128 		sleep(&igmpalloc.r, isreport, 0);
129 		for(;;){
130 			lock(&igmpalloc);
131 
132 			if(igmpalloc.reports == nil)
133 				break;
134 
135 			/* look for a single report */
136 			lrp = &igmpalloc.reports;
137 			mp = nil;
138 			for(rp = *lrp; rp; rp = *lrp){
139 				rp->ticks++;
140 				lmp = &rp->multi;
141 				for(mp = *lmp; mp; mp = *lmp){
142 					if(rp->ticks >= mp->timeout){
143 						*lmp = mp->next;
144 						break;
145 					}
146 					lmp = &mp->next;
147 				}
148 				if(mp != nil)
149 					break;
150 
151 				if(rp->multi != nil){
152 					lrp = &rp->next;
153 					continue;
154 				} else {
155 					*lrp = rp->next;
156 					free(rp);
157 				}
158 			}
159 			unlock(&igmpalloc);
160 
161 			if(mp){
162 				/* do a single report and try again */
163 				hnputl(ip, mp->addr);
164 				igmpsendreport(rp->medium, ip);
165 				free(mp);
166 				continue;
167 			}
168 
169 			tsleep(&up->sleep, return0, 0, MSPTICK);
170 		}
171 		unlock(&igmpalloc);
172 	}
173 
174 }
175 
176 void
igmpiput(Medium * m,Ipifc *,Block * bp)177 igmpiput(Medium *m, Ipifc *, Block *bp)
178 {
179 	int n;
180 	IGMPpkt *ghp;
181 	Ipaddr group;
182 	IGMPrep *rp, **lrp;
183 	Multicast *mp, **lmp;
184 
185 	ghp = (IGMPpkt*)(bp->rp);
186 	netlog(Logigmp, "igmpiput: %d %I\n", ghp->vertype, ghp->group);
187 
188 	n = blocklen(bp);
189 	if(n < IGMP_IPHDRSIZE+IGMP_HDRSIZE){
190 		netlog(Logigmp, "igmpiput: bad len\n");
191 		goto error;
192 	}
193 	if((ghp->vertype>>4) != 1){
194 		netlog(Logigmp, "igmpiput: bad igmp type\n");
195 		goto error;
196 	}
197 	if(ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)){
198 		netlog(Logigmp, "igmpiput: checksum error %I\n", ghp->src);
199 		goto error;
200 	}
201 
202 	group = nhgetl(ghp->group);
203 
204 	lock(&igmpalloc);
205 	switch(ghp->vertype & 0xf){
206 	case IGMPquery:
207 		/*
208 		 *  start reporting groups that we're a member of.
209 		 */
210 		stats.inqueries++;
211 		for(rp = igmpalloc.reports; rp; rp = rp->next)
212 			if(rp->medium == m)
213 				break;
214 		if(rp != nil)
215 			break;	/* already reporting */
216 
217 		mp = Mediumcopymulti(m);
218 		if(mp == nil)
219 			break;
220 
221 		rp = malloc(sizeof(*rp));
222 		if(rp == nil)
223 			break;
224 
225 		rp->medium = m;
226 		rp->multi = mp;
227 		rp->ticks = 0;
228 		for(; mp; mp = mp->next)
229 			mp->timeout = nrand(MAXTIMEOUT);
230 		rp->next = igmpalloc.reports;
231 		igmpalloc.reports = rp;
232 
233 		wakeup(&igmpalloc.r);
234 
235 		break;
236 	case IGMPreport:
237 		/*
238 		 *  find report list for this medium
239 		 */
240 		stats.inreports++;
241 		lrp = &igmpalloc.reports;
242 		for(rp = *lrp; rp; rp = *lrp){
243 			if(rp->medium == m)
244 				break;
245 			lrp = &rp->next;
246 		}
247 		if(rp == nil)
248 			break;
249 
250 		/*
251 		 *  if someone else has reported a group,
252 		 *  we don't have to.
253 		 */
254 		lmp = &rp->multi;
255 		for(mp = *lmp; mp; mp = *lmp){
256 			if(mp->addr == group){
257 				*lmp = mp->next;
258 				free(mp);
259 				break;
260 			}
261 			lmp = &mp->next;
262 		}
263 
264 		break;
265 	}
266 	unlock(&igmpalloc);
267 
268 error:
269 	freeb(bp);
270 }
271 
272 int
igmpstats(char * buf,int len)273 igmpstats(char *buf, int len)
274 {
275 	return snprint(buf, len, "\trcvd %d %d\n\tsent %d %d\n",
276 		stats.inqueries, stats.inreports,
277 		stats.outqueries, stats.outreports);
278 }
279 
280 void
igmpinit(Fs * fs)281 igmpinit(Fs *fs)
282 {
283 	igmp.name = "igmp";
284 	igmp.connect = nil;
285 	igmp.announce = nil;
286 	igmp.ctl = nil;
287 	igmp.state = nil;
288 	igmp.close = nil;
289 	igmp.rcv = igmpiput;
290 	igmp.stats = igmpstats;
291 	igmp.ipproto = IP_IGMPPROTO;
292 	igmp.nc = 0;
293 	igmp.ptclsize = 0;
294 
295 	igmpreportfn = igmpsendreport;
296 	kproc("igmpproc", igmpproc, 0);
297 
298 	Fsproto(fs, &igmp);
299 }
300