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