FreeBSD/Linux Kernel Cross Reference
sys/ip/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 byte vihl; /* Version and header length */
28 byte tos; /* Type of service */
29 byte len[2]; /* packet length (including headers) */
30 byte id[2]; /* Identification */
31 byte frag[2]; /* Fragment information */
32 byte Unused;
33 byte proto; /* Protocol */
34 byte cksum[2]; /* checksum of ip portion */
35 byte src[IPaddrlen]; /* Ip source */
36 byte dst[IPaddrlen]; /* Ip destination */
37
38 /* igmp header */
39 byte vertype; /* version and type */
40 byte unused;
41 byte igmpcksum[2]; /* checksum of igmp portion */
42 byte 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, byte *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 byte ip[IPaddrlen];
116
117 USED(a);
118
119 for(;;){
120 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 tsleep(&up->sleep, return0, 0, MSPTICK);
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 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 kproc("igmpproc", igmpproc, 0);
289
290 Fsproto(fs, &igmp);
291 }
Cache object: 02b19d43ec53fc29374737eb0934368c
|