]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/igmp.c
kernel: dont use atomic increment for Proc.nlocks, maintain Lock.m for lock(), use...
[plan9front.git] / sys / src / 9 / ip / igmp.c
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 }