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