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