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