Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
111
isreport(void *a)
112
{
113
	USED(a);
114
	return igmpalloc.reports != 0;
115
}
116
 
117
 
118
void
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->m, 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
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->m == 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->m = 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->m == 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
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
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
}