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