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