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