]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/igmp.c
usbohci: use 64-bit io base address, disable interrupts before reset, remove resetlck
[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         p = (IGMPpkt*)bp->wp;
94         p->vihl = IP_VER4;
95         bp->wp += IGMPPKTSZ;
96         memset(bp->rp, 0, IGMPPKTSZ);
97         hnputl(p->src, Mediumgetaddr(m));
98         hnputl(p->dst, Ipallsys);
99         p->vertype = (1<<4) | IGMPreport;
100         p->proto = IP_IGMPPROTO;
101         memmove(p->group, addr, IPaddrlen);
102         hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE));
103         netlog(Logigmp, "igmpreport %I\n", p->group);
104         stats.outreports++;
105         ipoput4(bp, 0, 1, DFLTTOS, nil);        /* TTL of 1 */
106 }
107
108 static int
109 isreport(void *a)
110 {
111         USED(a);
112         return igmpalloc.reports != 0;
113 }
114
115
116 void
117 igmpproc(void *a)
118 {
119         IGMPrep *rp, **lrp;
120         Multicast *mp, **lmp;
121         uchar ip[IPaddrlen];
122
123         USED(a);
124
125         for(;;){
126                 sleep(&igmpalloc.r, isreport, 0);
127                 for(;;){
128                         lock(&igmpalloc);
129
130                         if(igmpalloc.reports == nil)
131                                 break;
132         
133                         /* look for a single report */
134                         lrp = &igmpalloc.reports;
135                         mp = nil;
136                         for(rp = *lrp; rp; rp = *lrp){
137                                 rp->ticks++;
138                                 lmp = &rp->multi;
139                                 for(mp = *lmp; mp; mp = *lmp){
140                                         if(rp->ticks >= mp->timeout){
141                                                 *lmp = mp->next;
142                                                 break;
143                                         }
144                                         lmp = &mp->next;
145                                 }
146                                 if(mp != nil)
147                                         break;
148
149                                 if(rp->multi != nil){
150                                         lrp = &rp->next;
151                                         continue;
152                                 } else {
153                                         *lrp = rp->next;
154                                         free(rp);
155                                 }
156                         }
157                         unlock(&igmpalloc);
158
159                         if(mp){
160                                 /* do a single report and try again */
161                                 hnputl(ip, mp->addr);
162                                 igmpsendreport(rp->m, ip);
163                                 free(mp);
164                                 continue;
165                         }
166
167                         tsleep(&up->sleep, return0, 0, MSPTICK);
168                 }
169                 unlock(&igmpalloc);
170         }
171
172 }
173
174 void
175 igmpiput(Medium *m, Ipifc *, Block *bp)
176 {
177         int n;
178         IGMPpkt *ghp;
179         Ipaddr group;
180         IGMPrep *rp, **lrp;
181         Multicast *mp, **lmp;
182
183         ghp = (IGMPpkt*)(bp->rp);
184         netlog(Logigmp, "igmpiput: %d %I\n", ghp->vertype, ghp->group);
185
186         n = blocklen(bp);
187         if(n < IGMP_IPHDRSIZE+IGMP_HDRSIZE){
188                 netlog(Logigmp, "igmpiput: bad len\n");
189                 goto error;
190         }
191         if((ghp->vertype>>4) != 1){
192                 netlog(Logigmp, "igmpiput: bad igmp type\n");
193                 goto error;
194         }
195         if(ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)){
196                 netlog(Logigmp, "igmpiput: checksum error %I\n", ghp->src);
197                 goto error;
198         }
199
200         group = nhgetl(ghp->group);
201         
202         lock(&igmpalloc);
203         switch(ghp->vertype & 0xf){
204         case IGMPquery:
205                 /*
206                  *  start reporting groups that we're a member of.
207                  */
208                 stats.inqueries++;
209                 for(rp = igmpalloc.reports; rp; rp = rp->next)
210                         if(rp->m == m)
211                                 break;
212                 if(rp != nil)
213                         break;  /* already reporting */
214
215                 mp = Mediumcopymulti(m);
216                 if(mp == nil)
217                         break;
218
219                 rp = malloc(sizeof(*rp));
220                 if(rp == nil)
221                         break;
222
223                 rp->m = m;
224                 rp->multi = mp;
225                 rp->ticks = 0;
226                 for(; mp; mp = mp->next)
227                         mp->timeout = nrand(MAXTIMEOUT);
228                 rp->next = igmpalloc.reports;
229                 igmpalloc.reports = rp;
230
231                 wakeup(&igmpalloc.r);
232
233                 break;
234         case IGMPreport:
235                 /*
236                  *  find report list for this medium
237                  */
238                 stats.inreports++;
239                 lrp = &igmpalloc.reports;
240                 for(rp = *lrp; rp; rp = *lrp){
241                         if(rp->m == m)
242                                 break;
243                         lrp = &rp->next;
244                 }
245                 if(rp == nil)
246                         break;
247
248                 /*
249                  *  if someone else has reported a group,
250                  *  we don't have to.
251                  */
252                 lmp = &rp->multi;
253                 for(mp = *lmp; mp; mp = *lmp){
254                         if(mp->addr == group){
255                                 *lmp = mp->next;
256                                 free(mp);
257                                 break;
258                         }
259                         lmp = &mp->next;
260                 }
261
262                 break;
263         }
264         unlock(&igmpalloc);
265
266 error:
267         freeb(bp);
268 }
269
270 int
271 igmpstats(char *buf, int len)
272 {
273         return snprint(buf, len, "\trcvd %d %d\n\tsent %d %d\n",
274                 stats.inqueries, stats.inreports,
275                 stats.outqueries, stats.outreports);
276 }
277
278 void
279 igmpinit(Fs *fs)
280 {
281         igmp.name = "igmp";
282         igmp.connect = nil;
283         igmp.announce = nil;
284         igmp.ctl = nil;
285         igmp.state = nil;
286         igmp.close = nil;
287         igmp.rcv = igmpiput;
288         igmp.stats = igmpstats;
289         igmp.ipproto = IP_IGMPPROTO;
290         igmp.nc = 0;
291         igmp.ptclsize = 0;
292
293         igmpreportfn = igmpsendreport;
294         kproc("igmpproc", igmpproc, 0);
295
296         Fsproto(fs, &igmp);
297 }