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