This source file includes following definitions.
- igmp_stop_timer
- random
- igmp_start_timer
- igmp_timer_expire
- igmp_init_timer
- igmp_heard_report
- igmp_heard_query
- ip_mc_map
- ip_mc_filter_add
- ip_mc_filter_del
- igmp_group_dropped
- igmp_group_added
- igmp_rcv
- ip_mc_inc_group
- ip_mc_dec_group
- ip_mc_drop_device
- ip_mc_allhost
- ip_mc_join_group
- ip_mc_leave_group
- ip_mc_drop_socket
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include <asm/segment.h>
30 #include <asm/system.h>
31 #include <linux/types.h>
32 #include <linux/kernel.h>
33 #include <linux/sched.h>
34 #include <linux/string.h>
35 #include <linux/config.h>
36 #include <linux/socket.h>
37 #include <linux/sockios.h>
38 #include <linux/in.h>
39 #include <linux/inet.h>
40 #include <linux/netdevice.h>
41 #include <linux/if_arp.h>
42 #include <net/ip.h>
43 #include <net/protocol.h>
44 #include <net/route.h>
45 #include <linux/skbuff.h>
46 #include <net/sock.h>
47 #include <linux/igmp.h>
48 #include <net/checksum.h>
49
50 #ifdef CONFIG_IP_MULTICAST
51
52
53
54
55
56
57
58 static void igmp_stop_timer(struct ip_mc_list *im)
59 {
60 del_timer(&im->timer);
61 im->tm_running=0;
62 }
63
64 extern __inline__ int random(void)
65 {
66 static unsigned long seed=152L;
67 seed=seed*69069L+1;
68 return seed^jiffies;
69 }
70
71
72
73
74
75 static void igmp_start_timer(struct ip_mc_list *im)
76 {
77 int tv;
78 if(im->tm_running)
79 return;
80 tv=random()%(10*HZ);
81 im->timer.expires=jiffies+tv;
82 im->tm_running=1;
83 add_timer(&im->timer);
84 }
85
86
87
88
89
90 #define MAX_IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+64)
91
92 static void igmp_send_report(struct device *dev, unsigned long address, int type)
93 {
94 struct sk_buff *skb=alloc_skb(MAX_IGMP_SIZE, GFP_ATOMIC);
95 int tmp;
96 struct igmphdr *ih;
97
98 if(skb==NULL)
99 return;
100 tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL,
101 28 , 0, 1);
102 if(tmp<0)
103 {
104 kfree_skb(skb, FREE_WRITE);
105 return;
106 }
107 ih=(struct igmphdr *)skb_put(skb,sizeof(struct igmphdr));
108 ih->type=IGMP_HOST_MEMBERSHIP_REPORT;
109 ih->code=0;
110 ih->csum=0;
111 ih->group=address;
112 ih->csum=ip_compute_csum((void *)ih,sizeof(struct igmphdr));
113 ip_queue_xmit(NULL,dev,skb,1);
114 }
115
116
117 static void igmp_timer_expire(unsigned long data)
118 {
119 struct ip_mc_list *im=(struct ip_mc_list *)data;
120 igmp_stop_timer(im);
121 igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
122 }
123
124 static void igmp_init_timer(struct ip_mc_list *im)
125 {
126 im->tm_running=0;
127 init_timer(&im->timer);
128 im->timer.data=(unsigned long)im;
129 im->timer.function=&igmp_timer_expire;
130 }
131
132
133 static void igmp_heard_report(struct device *dev, unsigned long address)
134 {
135 struct ip_mc_list *im;
136 for(im=dev->ip_mc_list;im!=NULL;im=im->next)
137 if(im->multiaddr==address)
138 igmp_stop_timer(im);
139 }
140
141 static void igmp_heard_query(struct device *dev)
142 {
143 struct ip_mc_list *im;
144 for(im=dev->ip_mc_list;im!=NULL;im=im->next)
145 {
146 if(!im->tm_running && im->multiaddr!=IGMP_ALL_HOSTS)
147 igmp_start_timer(im);
148 }
149 }
150
151
152
153
154
155 extern __inline__ void ip_mc_map(unsigned long addr, char *buf)
156 {
157 addr=ntohl(addr);
158 buf[0]=0x01;
159 buf[1]=0x00;
160 buf[2]=0x5e;
161 buf[5]=addr&0xFF;
162 addr>>=8;
163 buf[4]=addr&0xFF;
164 addr>>=8;
165 buf[3]=addr&0x7F;
166 }
167
168
169
170
171
172 void ip_mc_filter_add(struct device *dev, unsigned long addr)
173 {
174 char buf[6];
175 if(dev->type!=ARPHRD_ETHER)
176 return;
177 ip_mc_map(addr,buf);
178 dev_mc_add(dev,buf,ETH_ALEN,0);
179 }
180
181
182
183
184
185 void ip_mc_filter_del(struct device *dev, unsigned long addr)
186 {
187 char buf[6];
188 if(dev->type!=ARPHRD_ETHER)
189 return;
190 ip_mc_map(addr,buf);
191 dev_mc_delete(dev,buf,ETH_ALEN,0);
192 }
193
194 extern __inline__ void igmp_group_dropped(struct ip_mc_list *im)
195 {
196 del_timer(&im->timer);
197 igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
198 ip_mc_filter_del(im->interface, im->multiaddr);
199 }
200
201 extern __inline__ void igmp_group_added(struct ip_mc_list *im)
202 {
203 igmp_init_timer(im);
204 ip_mc_filter_add(im->interface, im->multiaddr);
205 igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
206 }
207
208 int igmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
209 unsigned long daddr, unsigned short len, unsigned long saddr, int redo,
210 struct inet_protocol *protocol)
211 {
212
213 struct igmphdr *ih;
214
215
216
217
218
219
220
221 if(dev->flags&IFF_LOOPBACK)
222 {
223 dev=ip_dev_find(saddr);
224 if(dev==NULL)
225 dev=&loopback_dev;
226 }
227 ih=(struct igmphdr *)skb->h.raw;
228
229 if(skb->len <sizeof(struct igmphdr) || skb->ip_hdr->ttl>1 || ip_compute_csum((void *)skb->h.raw,sizeof(struct igmphdr)))
230 {
231 kfree_skb(skb, FREE_READ);
232 return 0;
233 }
234
235 if(ih->type==IGMP_HOST_MEMBERSHIP_QUERY && daddr==IGMP_ALL_HOSTS)
236 igmp_heard_query(dev);
237 if(ih->type==IGMP_HOST_MEMBERSHIP_REPORT && daddr==ih->group)
238 igmp_heard_report(dev,ih->group);
239 kfree_skb(skb, FREE_READ);
240 return 0;
241 }
242
243
244
245
246
247
248
249
250
251
252 static void ip_mc_inc_group(struct device *dev, unsigned long addr)
253 {
254 struct ip_mc_list *i;
255 for(i=dev->ip_mc_list;i!=NULL;i=i->next)
256 {
257 if(i->multiaddr==addr)
258 {
259 i->users++;
260 return;
261 }
262 }
263 i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL);
264 if(!i)
265 return;
266 i->users=1;
267 i->interface=dev;
268 i->multiaddr=addr;
269 i->next=dev->ip_mc_list;
270 igmp_group_added(i);
271 dev->ip_mc_list=i;
272 }
273
274
275
276
277
278 static void ip_mc_dec_group(struct device *dev, unsigned long addr)
279 {
280 struct ip_mc_list **i;
281 for(i=&(dev->ip_mc_list);(*i)!=NULL;i=&(*i)->next)
282 {
283 if((*i)->multiaddr==addr)
284 {
285 if(--((*i)->users))
286 return;
287 else
288 {
289 struct ip_mc_list *tmp= *i;
290 igmp_group_dropped(tmp);
291 *i=(*i)->next;
292 kfree_s(tmp,sizeof(*tmp));
293 }
294 }
295 }
296 }
297
298
299
300
301
302 void ip_mc_drop_device(struct device *dev)
303 {
304 struct ip_mc_list *i;
305 struct ip_mc_list *j;
306 for(i=dev->ip_mc_list;i!=NULL;i=j)
307 {
308 j=i->next;
309 kfree_s(i,sizeof(*i));
310 }
311 dev->ip_mc_list=NULL;
312 }
313
314
315
316
317
318 void ip_mc_allhost(struct device *dev)
319 {
320 struct ip_mc_list *i;
321 for(i=dev->ip_mc_list;i!=NULL;i=i->next)
322 if(i->multiaddr==IGMP_ALL_HOSTS)
323 return;
324 i=(struct ip_mc_list *)kmalloc(sizeof(*i), GFP_KERNEL);
325 if(!i)
326 return;
327 i->users=1;
328 i->interface=dev;
329 i->multiaddr=IGMP_ALL_HOSTS;
330 i->tm_running=0;
331 i->next=dev->ip_mc_list;
332 dev->ip_mc_list=i;
333 ip_mc_filter_add(i->interface, i->multiaddr);
334
335 }
336
337
338
339
340
341 int ip_mc_join_group(struct sock *sk , struct device *dev, unsigned long addr)
342 {
343 int unused= -1;
344 int i;
345 if(!MULTICAST(addr))
346 return -EINVAL;
347 if(!(dev->flags&IFF_MULTICAST))
348 return -EADDRNOTAVAIL;
349 if(sk->ip_mc_list==NULL)
350 {
351 if((sk->ip_mc_list=(struct ip_mc_socklist *)kmalloc(sizeof(*sk->ip_mc_list), GFP_KERNEL))==NULL)
352 return -ENOMEM;
353 memset(sk->ip_mc_list,'\0',sizeof(*sk->ip_mc_list));
354 }
355 for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
356 {
357 if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev)
358 return -EADDRINUSE;
359 if(sk->ip_mc_list->multidev[i]==NULL)
360 unused=i;
361 }
362
363 if(unused==-1)
364 return -ENOBUFS;
365 sk->ip_mc_list->multiaddr[unused]=addr;
366 sk->ip_mc_list->multidev[unused]=dev;
367 ip_mc_inc_group(dev,addr);
368 return 0;
369 }
370
371
372
373
374
375 int ip_mc_leave_group(struct sock *sk, struct device *dev, unsigned long addr)
376 {
377 int i;
378 if(!MULTICAST(addr))
379 return -EINVAL;
380 if(!(dev->flags&IFF_MULTICAST))
381 return -EADDRNOTAVAIL;
382 if(sk->ip_mc_list==NULL)
383 return -EADDRNOTAVAIL;
384
385 for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
386 {
387 if(sk->ip_mc_list->multiaddr[i]==addr && sk->ip_mc_list->multidev[i]==dev)
388 {
389 sk->ip_mc_list->multidev[i]=NULL;
390 ip_mc_dec_group(dev,addr);
391 return 0;
392 }
393 }
394 return -EADDRNOTAVAIL;
395 }
396
397
398
399
400
401 void ip_mc_drop_socket(struct sock *sk)
402 {
403 int i;
404
405 if(sk->ip_mc_list==NULL)
406 return;
407
408 for(i=0;i<IP_MAX_MEMBERSHIPS;i++)
409 {
410 if(sk->ip_mc_list->multidev[i])
411 {
412 ip_mc_dec_group(sk->ip_mc_list->multidev[i], sk->ip_mc_list->multiaddr[i]);
413 sk->ip_mc_list->multidev[i]=NULL;
414 }
415 }
416 kfree_s(sk->ip_mc_list,sizeof(*sk->ip_mc_list));
417 sk->ip_mc_list=NULL;
418 }
419
420 #endif