blob: c43ec2d51ce21dcb8666785f52bbd5417d868a0e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * IP multicast routing support for mrouted 3.6/3.8
3 *
Alan Cox113aa832008-10-13 19:01:08 -07004 * (c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk>
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Linux Consultancy and Custom Driver Development
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070012 * Fixes:
13 * Michael Chastain : Incorrect size of copying.
14 * Alan Cox : Added the cache manager code
15 * Alan Cox : Fixed the clone/copy bug and device race.
16 * Mike McLagan : Routing by source
17 * Malcolm Beattie : Buffer handling fixes.
18 * Alexey Kuznetsov : Double buffer free and other fixes.
19 * SVR Anand : Fixed several multicast bugs and problems.
20 * Alexey Kuznetsov : Status, optimisations and more.
21 * Brad Parker : Better behaviour on mrouted upcall
22 * overflow.
23 * Carlos Picoto : PIMv1 Support
24 * Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header
25 * Relax this requrement to work with older peers.
26 *
27 */
28
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include <asm/system.h>
30#include <asm/uaccess.h>
31#include <linux/types.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -080032#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/errno.h>
34#include <linux/timer.h>
35#include <linux/mm.h>
36#include <linux/kernel.h>
37#include <linux/fcntl.h>
38#include <linux/stat.h>
39#include <linux/socket.h>
40#include <linux/in.h>
41#include <linux/inet.h>
42#include <linux/netdevice.h>
43#include <linux/inetdevice.h>
44#include <linux/igmp.h>
45#include <linux/proc_fs.h>
46#include <linux/seq_file.h>
47#include <linux/mroute.h>
48#include <linux/init.h>
Kris Katterjohn46f25df2006-01-05 16:35:42 -080049#include <linux/if_ether.h>
Eric W. Biederman457c4cb2007-09-12 12:01:34 +020050#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#include <net/ip.h>
52#include <net/protocol.h>
53#include <linux/skbuff.h>
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020054#include <net/route.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#include <net/sock.h>
56#include <net/icmp.h>
57#include <net/udp.h>
58#include <net/raw.h>
59#include <linux/notifier.h>
60#include <linux/if_arp.h>
61#include <linux/netfilter_ipv4.h>
62#include <net/ipip.h>
63#include <net/checksum.h>
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -070064#include <net/netlink.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
66#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
67#define CONFIG_IP_PIMSM 1
68#endif
69
Linus Torvalds1da177e2005-04-16 15:20:36 -070070/* Big lock, protecting vif table, mrt cache and mroute socket state.
71 Note that the changes are semaphored via rtnl_lock.
72 */
73
74static DEFINE_RWLOCK(mrt_lock);
75
76/*
77 * Multicast router control variables
78 */
79
Benjamin Therycf958ae32009-01-22 04:56:16 +000080#define VIF_EXISTS(_net, _idx) ((_net)->ipv4.vif_table[_idx].dev != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
Linus Torvalds1da177e2005-04-16 15:20:36 -070082static struct mfc_cache *mfc_unres_queue; /* Queue of unresolved entries */
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
84/* Special spinlock for queue of unresolved entries */
85static DEFINE_SPINLOCK(mfc_unres_lock);
86
87/* We return to original Alan's scheme. Hash table of resolved
88 entries is changed only in process context and protected
89 with weak lock mrt_lock. Queue of unresolved entries is protected
90 with strong spinlock mfc_unres_lock.
91
92 In this case data path is free of exclusive locks at all.
93 */
94
Christoph Lametere18b8902006-12-06 20:33:20 -080095static struct kmem_cache *mrt_cachep __read_mostly;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096
97static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local);
Benjamin Thery4feb88e2009-01-22 04:56:23 +000098static int ipmr_cache_report(struct net *net,
99 struct sk_buff *pkt, vifi_t vifi, int assert);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100static int ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm);
101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102static struct timer_list ipmr_expire_timer;
103
104/* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */
105
Wang Chend6070322008-07-14 20:55:26 -0700106static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
107{
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000108 struct net *net = dev_net(dev);
109
Wang Chend6070322008-07-14 20:55:26 -0700110 dev_close(dev);
111
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000112 dev = __dev_get_by_name(net, "tunl0");
Wang Chend6070322008-07-14 20:55:26 -0700113 if (dev) {
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800114 const struct net_device_ops *ops = dev->netdev_ops;
Wang Chend6070322008-07-14 20:55:26 -0700115 struct ifreq ifr;
Wang Chend6070322008-07-14 20:55:26 -0700116 struct ip_tunnel_parm p;
117
118 memset(&p, 0, sizeof(p));
119 p.iph.daddr = v->vifc_rmt_addr.s_addr;
120 p.iph.saddr = v->vifc_lcl_addr.s_addr;
121 p.iph.version = 4;
122 p.iph.ihl = 5;
123 p.iph.protocol = IPPROTO_IPIP;
124 sprintf(p.name, "dvmrp%d", v->vifc_vifi);
125 ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
126
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800127 if (ops->ndo_do_ioctl) {
128 mm_segment_t oldfs = get_fs();
129
130 set_fs(KERNEL_DS);
131 ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL);
132 set_fs(oldfs);
133 }
Wang Chend6070322008-07-14 20:55:26 -0700134 }
135}
136
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137static
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000138struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139{
140 struct net_device *dev;
141
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000142 dev = __dev_get_by_name(net, "tunl0");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
144 if (dev) {
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800145 const struct net_device_ops *ops = dev->netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 int err;
147 struct ifreq ifr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 struct ip_tunnel_parm p;
149 struct in_device *in_dev;
150
151 memset(&p, 0, sizeof(p));
152 p.iph.daddr = v->vifc_rmt_addr.s_addr;
153 p.iph.saddr = v->vifc_lcl_addr.s_addr;
154 p.iph.version = 4;
155 p.iph.ihl = 5;
156 p.iph.protocol = IPPROTO_IPIP;
157 sprintf(p.name, "dvmrp%d", v->vifc_vifi);
Stephen Hemmingerba93ef72008-01-21 17:28:59 -0800158 ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
Stephen Hemminger5bc3eb72008-11-19 21:52:05 -0800160 if (ops->ndo_do_ioctl) {
161 mm_segment_t oldfs = get_fs();
162
163 set_fs(KERNEL_DS);
164 err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
165 set_fs(oldfs);
166 } else
167 err = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168
169 dev = NULL;
170
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000171 if (err == 0 &&
172 (dev = __dev_get_by_name(net, p.name)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 dev->flags |= IFF_MULTICAST;
174
Herbert Xue5ed6392005-10-03 14:35:55 -0700175 in_dev = __in_dev_get_rtnl(dev);
Herbert Xu71e27da2007-06-04 23:36:06 -0700176 if (in_dev == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 goto failure;
Herbert Xu71e27da2007-06-04 23:36:06 -0700178
179 ipv4_devconf_setall(in_dev);
180 IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181
182 if (dev_open(dev))
183 goto failure;
Wang Chen7dc00c82008-07-14 20:56:34 -0700184 dev_hold(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 }
186 }
187 return dev;
188
189failure:
190 /* allow the register to be completed before unregistering. */
191 rtnl_unlock();
192 rtnl_lock();
193
194 unregister_netdevice(dev);
195 return NULL;
196}
197
198#ifdef CONFIG_IP_PIMSM
199
Stephen Hemminger6fef4c02009-08-31 19:50:41 +0000200static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201{
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000202 struct net *net = dev_net(dev);
203
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 read_lock(&mrt_lock);
Pavel Emelyanovcf3677a2008-05-21 14:17:33 -0700205 dev->stats.tx_bytes += skb->len;
206 dev->stats.tx_packets++;
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000207 ipmr_cache_report(net, skb, net->ipv4.mroute_reg_vif_num,
208 IGMPMSG_WHOLEPKT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 read_unlock(&mrt_lock);
210 kfree_skb(skb);
Patrick McHardy6ed10652009-06-23 06:03:08 +0000211 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212}
213
Stephen Hemminger007c3832008-11-20 20:28:35 -0800214static const struct net_device_ops reg_vif_netdev_ops = {
215 .ndo_start_xmit = reg_vif_xmit,
216};
217
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218static void reg_vif_setup(struct net_device *dev)
219{
220 dev->type = ARPHRD_PIMREG;
Kris Katterjohn46f25df2006-01-05 16:35:42 -0800221 dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 dev->flags = IFF_NOARP;
Stephen Hemminger007c3832008-11-20 20:28:35 -0800223 dev->netdev_ops = &reg_vif_netdev_ops,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 dev->destructor = free_netdev;
Tom Goff403dbb92009-06-14 03:16:13 -0700225 dev->features |= NETIF_F_NETNS_LOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226}
227
Tom Goff403dbb92009-06-14 03:16:13 -0700228static struct net_device *ipmr_reg_vif(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229{
230 struct net_device *dev;
231 struct in_device *in_dev;
232
Pavel Emelyanovcf3677a2008-05-21 14:17:33 -0700233 dev = alloc_netdev(0, "pimreg", reg_vif_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
235 if (dev == NULL)
236 return NULL;
237
Tom Goff403dbb92009-06-14 03:16:13 -0700238 dev_net_set(dev, net);
239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 if (register_netdevice(dev)) {
241 free_netdev(dev);
242 return NULL;
243 }
244 dev->iflink = 0;
245
Herbert Xu71e27da2007-06-04 23:36:06 -0700246 rcu_read_lock();
247 if ((in_dev = __in_dev_get_rcu(dev)) == NULL) {
248 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 goto failure;
Herbert Xu71e27da2007-06-04 23:36:06 -0700250 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Herbert Xu71e27da2007-06-04 23:36:06 -0700252 ipv4_devconf_setall(in_dev);
253 IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
254 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
256 if (dev_open(dev))
257 goto failure;
258
Wang Chen7dc00c82008-07-14 20:56:34 -0700259 dev_hold(dev);
260
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 return dev;
262
263failure:
264 /* allow the register to be completed before unregistering. */
265 rtnl_unlock();
266 rtnl_lock();
267
268 unregister_netdevice(dev);
269 return NULL;
270}
271#endif
272
273/*
274 * Delete a VIF entry
Wang Chen7dc00c82008-07-14 20:56:34 -0700275 * @notify: Set to 1, if the caller is a notifier_call
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900277
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000278static int vif_delete(struct net *net, int vifi, int notify)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279{
280 struct vif_device *v;
281 struct net_device *dev;
282 struct in_device *in_dev;
283
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000284 if (vifi < 0 || vifi >= net->ipv4.maxvif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 return -EADDRNOTAVAIL;
286
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000287 v = &net->ipv4.vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
289 write_lock_bh(&mrt_lock);
290 dev = v->dev;
291 v->dev = NULL;
292
293 if (!dev) {
294 write_unlock_bh(&mrt_lock);
295 return -EADDRNOTAVAIL;
296 }
297
298#ifdef CONFIG_IP_PIMSM
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000299 if (vifi == net->ipv4.mroute_reg_vif_num)
300 net->ipv4.mroute_reg_vif_num = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301#endif
302
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000303 if (vifi+1 == net->ipv4.maxvif) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 int tmp;
305 for (tmp=vifi-1; tmp>=0; tmp--) {
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000306 if (VIF_EXISTS(net, tmp))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 break;
308 }
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000309 net->ipv4.maxvif = tmp+1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 }
311
312 write_unlock_bh(&mrt_lock);
313
314 dev_set_allmulti(dev, -1);
315
Herbert Xue5ed6392005-10-03 14:35:55 -0700316 if ((in_dev = __in_dev_get_rtnl(dev)) != NULL) {
Herbert Xu42f811b2007-06-04 23:34:44 -0700317 IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 ip_rt_multicast_event(in_dev);
319 }
320
Wang Chen7dc00c82008-07-14 20:56:34 -0700321 if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER) && !notify)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 unregister_netdevice(dev);
323
324 dev_put(dev);
325 return 0;
326}
327
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000328static inline void ipmr_cache_free(struct mfc_cache *c)
329{
330 release_net(mfc_net(c));
331 kmem_cache_free(mrt_cachep, c);
332}
333
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334/* Destroy an unresolved cache entry, killing queued skbs
335 and reporting error to netlink readers.
336 */
337
338static void ipmr_destroy_unres(struct mfc_cache *c)
339{
340 struct sk_buff *skb;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700341 struct nlmsgerr *e;
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000342 struct net *net = mfc_net(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000344 atomic_dec(&net->ipv4.cache_resolve_queue_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345
Jianjun Kongc354e122008-11-03 00:28:02 -0800346 while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700347 if (ip_hdr(skb)->version == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
349 nlh->nlmsg_type = NLMSG_ERROR;
350 nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
351 skb_trim(skb, nlh->nlmsg_len);
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700352 e = NLMSG_DATA(nlh);
353 e->error = -ETIMEDOUT;
354 memset(&e->msg, 0, sizeof(e->msg));
Thomas Graf2942e902006-08-15 00:30:25 -0700355
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000356 rtnl_unicast(skb, net, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 } else
358 kfree_skb(skb);
359 }
360
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000361 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362}
363
364
365/* Single timer process for all the unresolved queue. */
366
367static void ipmr_expire_process(unsigned long dummy)
368{
369 unsigned long now;
370 unsigned long expires;
371 struct mfc_cache *c, **cp;
372
373 if (!spin_trylock(&mfc_unres_lock)) {
374 mod_timer(&ipmr_expire_timer, jiffies+HZ/10);
375 return;
376 }
377
Benjamin Thery1e8fb3b2009-01-22 04:56:19 +0000378 if (mfc_unres_queue == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 goto out;
380
381 now = jiffies;
382 expires = 10*HZ;
383 cp = &mfc_unres_queue;
384
385 while ((c=*cp) != NULL) {
386 if (time_after(c->mfc_un.unres.expires, now)) {
387 unsigned long interval = c->mfc_un.unres.expires - now;
388 if (interval < expires)
389 expires = interval;
390 cp = &c->next;
391 continue;
392 }
393
394 *cp = c->next;
395
396 ipmr_destroy_unres(c);
397 }
398
Benjamin Thery1e8fb3b2009-01-22 04:56:19 +0000399 if (mfc_unres_queue != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 mod_timer(&ipmr_expire_timer, jiffies + expires);
401
402out:
403 spin_unlock(&mfc_unres_lock);
404}
405
406/* Fill oifs list. It is called under write locked mrt_lock. */
407
Baruch Evend1b04c02005-07-30 17:41:59 -0700408static void ipmr_update_thresholds(struct mfc_cache *cache, unsigned char *ttls)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409{
410 int vifi;
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000411 struct net *net = mfc_net(cache);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412
413 cache->mfc_un.res.minvif = MAXVIFS;
414 cache->mfc_un.res.maxvif = 0;
415 memset(cache->mfc_un.res.ttls, 255, MAXVIFS);
416
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000417 for (vifi = 0; vifi < net->ipv4.maxvif; vifi++) {
418 if (VIF_EXISTS(net, vifi) &&
Benjamin Therycf958ae32009-01-22 04:56:16 +0000419 ttls[vifi] && ttls[vifi] < 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 cache->mfc_un.res.ttls[vifi] = ttls[vifi];
421 if (cache->mfc_un.res.minvif > vifi)
422 cache->mfc_un.res.minvif = vifi;
423 if (cache->mfc_un.res.maxvif <= vifi)
424 cache->mfc_un.res.maxvif = vifi + 1;
425 }
426 }
427}
428
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000429static int vif_add(struct net *net, struct vifctl *vifc, int mrtsock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430{
431 int vifi = vifc->vifc_vifi;
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000432 struct vif_device *v = &net->ipv4.vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 struct net_device *dev;
434 struct in_device *in_dev;
Wang Chend6070322008-07-14 20:55:26 -0700435 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
437 /* Is vif busy ? */
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000438 if (VIF_EXISTS(net, vifi))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 return -EADDRINUSE;
440
441 switch (vifc->vifc_flags) {
442#ifdef CONFIG_IP_PIMSM
443 case VIFF_REGISTER:
444 /*
445 * Special Purpose VIF in PIM
446 * All the packets will be sent to the daemon
447 */
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000448 if (net->ipv4.mroute_reg_vif_num >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 return -EADDRINUSE;
Tom Goff403dbb92009-06-14 03:16:13 -0700450 dev = ipmr_reg_vif(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 if (!dev)
452 return -ENOBUFS;
Wang Chend6070322008-07-14 20:55:26 -0700453 err = dev_set_allmulti(dev, 1);
454 if (err) {
455 unregister_netdevice(dev);
Wang Chen7dc00c82008-07-14 20:56:34 -0700456 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700457 return err;
458 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 break;
460#endif
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900461 case VIFF_TUNNEL:
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000462 dev = ipmr_new_tunnel(net, vifc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 if (!dev)
464 return -ENOBUFS;
Wang Chend6070322008-07-14 20:55:26 -0700465 err = dev_set_allmulti(dev, 1);
466 if (err) {
467 ipmr_del_tunnel(dev, vifc);
Wang Chen7dc00c82008-07-14 20:56:34 -0700468 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700469 return err;
470 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 break;
472 case 0:
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000473 dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 if (!dev)
475 return -EADDRNOTAVAIL;
Wang Chend6070322008-07-14 20:55:26 -0700476 err = dev_set_allmulti(dev, 1);
Wang Chen7dc00c82008-07-14 20:56:34 -0700477 if (err) {
478 dev_put(dev);
Wang Chend6070322008-07-14 20:55:26 -0700479 return err;
Wang Chen7dc00c82008-07-14 20:56:34 -0700480 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 break;
482 default:
483 return -EINVAL;
484 }
485
Herbert Xue5ed6392005-10-03 14:35:55 -0700486 if ((in_dev = __in_dev_get_rtnl(dev)) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 return -EADDRNOTAVAIL;
Herbert Xu42f811b2007-06-04 23:34:44 -0700488 IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 ip_rt_multicast_event(in_dev);
490
491 /*
492 * Fill in the VIF structures
493 */
Jianjun Kongc354e122008-11-03 00:28:02 -0800494 v->rate_limit = vifc->vifc_rate_limit;
495 v->local = vifc->vifc_lcl_addr.s_addr;
496 v->remote = vifc->vifc_rmt_addr.s_addr;
497 v->flags = vifc->vifc_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 if (!mrtsock)
499 v->flags |= VIFF_STATIC;
Jianjun Kongc354e122008-11-03 00:28:02 -0800500 v->threshold = vifc->vifc_threshold;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 v->bytes_in = 0;
502 v->bytes_out = 0;
503 v->pkt_in = 0;
504 v->pkt_out = 0;
505 v->link = dev->ifindex;
506 if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER))
507 v->link = dev->iflink;
508
509 /* And finish update writing critical data */
510 write_lock_bh(&mrt_lock);
Jianjun Kongc354e122008-11-03 00:28:02 -0800511 v->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512#ifdef CONFIG_IP_PIMSM
513 if (v->flags&VIFF_REGISTER)
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000514 net->ipv4.mroute_reg_vif_num = vifi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515#endif
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000516 if (vifi+1 > net->ipv4.maxvif)
517 net->ipv4.maxvif = vifi+1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 write_unlock_bh(&mrt_lock);
519 return 0;
520}
521
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000522static struct mfc_cache *ipmr_cache_find(struct net *net,
523 __be32 origin,
524 __be32 mcastgrp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525{
Jianjun Kongc354e122008-11-03 00:28:02 -0800526 int line = MFC_HASH(mcastgrp, origin);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 struct mfc_cache *c;
528
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000529 for (c = net->ipv4.mfc_cache_array[line]; c; c = c->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 if (c->mfc_origin==origin && c->mfc_mcastgrp==mcastgrp)
531 break;
532 }
533 return c;
534}
535
536/*
537 * Allocate a multicast cache entry
538 */
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000539static struct mfc_cache *ipmr_cache_alloc(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540{
Jianjun Kongc354e122008-11-03 00:28:02 -0800541 struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
542 if (c == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 c->mfc_un.res.minvif = MAXVIFS;
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000545 mfc_net_set(c, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 return c;
547}
548
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000549static struct mfc_cache *ipmr_cache_alloc_unres(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550{
Jianjun Kongc354e122008-11-03 00:28:02 -0800551 struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
552 if (c == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 skb_queue_head_init(&c->mfc_un.unres.unresolved);
555 c->mfc_un.unres.expires = jiffies + 10*HZ;
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000556 mfc_net_set(c, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 return c;
558}
559
560/*
561 * A cache entry has gone into a resolved state from queued
562 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900563
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564static void ipmr_cache_resolve(struct mfc_cache *uc, struct mfc_cache *c)
565{
566 struct sk_buff *skb;
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700567 struct nlmsgerr *e;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
569 /*
570 * Play the pending entries through our router
571 */
572
Jianjun Kongc354e122008-11-03 00:28:02 -0800573 while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700574 if (ip_hdr(skb)->version == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
576
577 if (ipmr_fill_mroute(skb, c, NLMSG_DATA(nlh)) > 0) {
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700578 nlh->nlmsg_len = (skb_tail_pointer(skb) -
579 (u8 *)nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 } else {
581 nlh->nlmsg_type = NLMSG_ERROR;
582 nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
583 skb_trim(skb, nlh->nlmsg_len);
Patrick McHardy9ef1d4c2005-06-28 12:55:30 -0700584 e = NLMSG_DATA(nlh);
585 e->error = -EMSGSIZE;
586 memset(&e->msg, 0, sizeof(e->msg));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 }
Thomas Graf2942e902006-08-15 00:30:25 -0700588
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000589 rtnl_unicast(skb, mfc_net(c), NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 } else
591 ip_mr_forward(skb, c, 0);
592 }
593}
594
595/*
596 * Bounce a cache query up to mrouted. We could use netlink for this but mrouted
597 * expects the following bizarre scheme.
598 *
599 * Called under mrt_lock.
600 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900601
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000602static int ipmr_cache_report(struct net *net,
603 struct sk_buff *pkt, vifi_t vifi, int assert)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604{
605 struct sk_buff *skb;
Arnaldo Carvalho de Meloc9bdd4b2007-03-12 20:09:15 -0300606 const int ihl = ip_hdrlen(pkt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 struct igmphdr *igmp;
608 struct igmpmsg *msg;
609 int ret;
610
611#ifdef CONFIG_IP_PIMSM
612 if (assert == IGMPMSG_WHOLEPKT)
613 skb = skb_realloc_headroom(pkt, sizeof(struct iphdr));
614 else
615#endif
616 skb = alloc_skb(128, GFP_ATOMIC);
617
Stephen Hemminger132adf52007-03-08 20:44:43 -0800618 if (!skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 return -ENOBUFS;
620
621#ifdef CONFIG_IP_PIMSM
622 if (assert == IGMPMSG_WHOLEPKT) {
623 /* Ugly, but we have no choice with this interface.
624 Duplicate old header, fix ihl, length etc.
625 And all this only to mangle msg->im_msgtype and
626 to set msg->im_mbz to "mbz" :-)
627 */
Arnaldo Carvalho de Melo878c8142007-03-11 22:38:29 -0300628 skb_push(skb, sizeof(struct iphdr));
629 skb_reset_network_header(skb);
Arnaldo Carvalho de Melobadff6d2007-03-13 13:06:52 -0300630 skb_reset_transport_header(skb);
Arnaldo Carvalho de Melo0272ffc2007-03-12 20:05:39 -0300631 msg = (struct igmpmsg *)skb_network_header(skb);
Arnaldo Carvalho de Melod56f90a2007-04-10 20:50:43 -0700632 memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 msg->im_msgtype = IGMPMSG_WHOLEPKT;
634 msg->im_mbz = 0;
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000635 msg->im_vif = net->ipv4.mroute_reg_vif_num;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700636 ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
637 ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
638 sizeof(struct iphdr));
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900639 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640#endif
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900641 {
642
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 /*
644 * Copy the IP header
645 */
646
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700647 skb->network_header = skb->tail;
Arnaldo Carvalho de Meloddc7b8e2007-03-15 21:42:27 -0300648 skb_put(skb, ihl);
Arnaldo Carvalho de Melo27d7ff42007-03-31 11:55:19 -0300649 skb_copy_to_linear_data(skb, pkt->data, ihl);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700650 ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */
651 msg = (struct igmpmsg *)skb_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 msg->im_vif = vifi;
Eric Dumazetadf30902009-06-02 05:19:30 +0000653 skb_dst_set(skb, dst_clone(skb_dst(pkt)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
655 /*
656 * Add our header
657 */
658
Jianjun Kongc354e122008-11-03 00:28:02 -0800659 igmp=(struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 igmp->type =
661 msg->im_msgtype = assert;
662 igmp->code = 0;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700663 ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -0700664 skb->transport_header = skb->network_header;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900665 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000667 if (net->ipv4.mroute_sk == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 kfree_skb(skb);
669 return -EINVAL;
670 }
671
672 /*
673 * Deliver to mrouted
674 */
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000675 ret = sock_queue_rcv_skb(net->ipv4.mroute_sk, skb);
Benjamin Thery70a269e2009-01-22 04:56:15 +0000676 if (ret < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 if (net_ratelimit())
678 printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n");
679 kfree_skb(skb);
680 }
681
682 return ret;
683}
684
685/*
686 * Queue a packet for resolution. It gets locked cache entry!
687 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900688
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689static int
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000690ipmr_cache_unresolved(struct net *net, vifi_t vifi, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691{
692 int err;
693 struct mfc_cache *c;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700694 const struct iphdr *iph = ip_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
696 spin_lock_bh(&mfc_unres_lock);
697 for (c=mfc_unres_queue; c; c=c->next) {
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000698 if (net_eq(mfc_net(c), net) &&
Benjamin Thery1e8fb3b2009-01-22 04:56:19 +0000699 c->mfc_mcastgrp == iph->daddr &&
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700700 c->mfc_origin == iph->saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 break;
702 }
703
704 if (c == NULL) {
705 /*
706 * Create a new entry if allowable
707 */
708
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000709 if (atomic_read(&net->ipv4.cache_resolve_queue_len) >= 10 ||
710 (c = ipmr_cache_alloc_unres(net)) == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 spin_unlock_bh(&mfc_unres_lock);
712
713 kfree_skb(skb);
714 return -ENOBUFS;
715 }
716
717 /*
718 * Fill in the new cache entry
719 */
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -0700720 c->mfc_parent = -1;
721 c->mfc_origin = iph->saddr;
722 c->mfc_mcastgrp = iph->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
724 /*
725 * Reflect first query at mrouted.
726 */
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000727 err = ipmr_cache_report(net, skb, vifi, IGMPMSG_NOCACHE);
728 if (err < 0) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900729 /* If the report failed throw the cache entry
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 out - Brad Parker
731 */
732 spin_unlock_bh(&mfc_unres_lock);
733
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000734 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 kfree_skb(skb);
736 return err;
737 }
738
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000739 atomic_inc(&net->ipv4.cache_resolve_queue_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 c->next = mfc_unres_queue;
741 mfc_unres_queue = c;
742
743 mod_timer(&ipmr_expire_timer, c->mfc_un.unres.expires);
744 }
745
746 /*
747 * See if we can append the packet
748 */
749 if (c->mfc_un.unres.unresolved.qlen>3) {
750 kfree_skb(skb);
751 err = -ENOBUFS;
752 } else {
Jianjun Kongc354e122008-11-03 00:28:02 -0800753 skb_queue_tail(&c->mfc_un.unres.unresolved, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 err = 0;
755 }
756
757 spin_unlock_bh(&mfc_unres_lock);
758 return err;
759}
760
761/*
762 * MFC cache manipulation by user space mroute daemon
763 */
764
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000765static int ipmr_mfc_delete(struct net *net, struct mfcctl *mfc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766{
767 int line;
768 struct mfc_cache *c, **cp;
769
Jianjun Kongc354e122008-11-03 00:28:02 -0800770 line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000772 for (cp = &net->ipv4.mfc_cache_array[line];
Benjamin Thery2bb8b262009-01-22 04:56:18 +0000773 (c = *cp) != NULL; cp = &c->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
775 c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
776 write_lock_bh(&mrt_lock);
777 *cp = c->next;
778 write_unlock_bh(&mrt_lock);
779
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000780 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 return 0;
782 }
783 }
784 return -ENOENT;
785}
786
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000787static int ipmr_mfc_add(struct net *net, struct mfcctl *mfc, int mrtsock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788{
789 int line;
790 struct mfc_cache *uc, *c, **cp;
791
Jianjun Kongc354e122008-11-03 00:28:02 -0800792 line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000794 for (cp = &net->ipv4.mfc_cache_array[line];
Benjamin Thery2bb8b262009-01-22 04:56:18 +0000795 (c = *cp) != NULL; cp = &c->next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
797 c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr)
798 break;
799 }
800
801 if (c != NULL) {
802 write_lock_bh(&mrt_lock);
803 c->mfc_parent = mfc->mfcc_parent;
Baruch Evend1b04c02005-07-30 17:41:59 -0700804 ipmr_update_thresholds(c, mfc->mfcc_ttls);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 if (!mrtsock)
806 c->mfc_flags |= MFC_STATIC;
807 write_unlock_bh(&mrt_lock);
808 return 0;
809 }
810
Joe Perchesf97c1e02007-12-16 13:45:43 -0800811 if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 return -EINVAL;
813
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000814 c = ipmr_cache_alloc(net);
Jianjun Kongc354e122008-11-03 00:28:02 -0800815 if (c == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 return -ENOMEM;
817
Jianjun Kongc354e122008-11-03 00:28:02 -0800818 c->mfc_origin = mfc->mfcc_origin.s_addr;
819 c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr;
820 c->mfc_parent = mfc->mfcc_parent;
Baruch Evend1b04c02005-07-30 17:41:59 -0700821 ipmr_update_thresholds(c, mfc->mfcc_ttls);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 if (!mrtsock)
823 c->mfc_flags |= MFC_STATIC;
824
825 write_lock_bh(&mrt_lock);
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000826 c->next = net->ipv4.mfc_cache_array[line];
827 net->ipv4.mfc_cache_array[line] = c;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 write_unlock_bh(&mrt_lock);
829
830 /*
831 * Check to see if we resolved a queued list. If so we
832 * need to send on the frames and tidy up.
833 */
834 spin_lock_bh(&mfc_unres_lock);
835 for (cp = &mfc_unres_queue; (uc=*cp) != NULL;
836 cp = &uc->next) {
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000837 if (net_eq(mfc_net(uc), net) &&
Benjamin Thery1e8fb3b2009-01-22 04:56:19 +0000838 uc->mfc_origin == c->mfc_origin &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 uc->mfc_mcastgrp == c->mfc_mcastgrp) {
840 *cp = uc->next;
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000841 atomic_dec(&net->ipv4.cache_resolve_queue_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 break;
843 }
844 }
Benjamin Thery1e8fb3b2009-01-22 04:56:19 +0000845 if (mfc_unres_queue == NULL)
846 del_timer(&ipmr_expire_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 spin_unlock_bh(&mfc_unres_lock);
848
849 if (uc) {
850 ipmr_cache_resolve(uc, c);
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000851 ipmr_cache_free(uc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 }
853 return 0;
854}
855
856/*
857 * Close the multicast socket, and clear the vif tables etc
858 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900859
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000860static void mroute_clean_tables(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861{
862 int i;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900863
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 /*
865 * Shut down all active vif entries
866 */
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000867 for (i = 0; i < net->ipv4.maxvif; i++) {
868 if (!(net->ipv4.vif_table[i].flags&VIFF_STATIC))
869 vif_delete(net, i, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 }
871
872 /*
873 * Wipe the cache
874 */
Jianjun Kongc354e122008-11-03 00:28:02 -0800875 for (i=0; i<MFC_LINES; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 struct mfc_cache *c, **cp;
877
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000878 cp = &net->ipv4.mfc_cache_array[i];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 while ((c = *cp) != NULL) {
880 if (c->mfc_flags&MFC_STATIC) {
881 cp = &c->next;
882 continue;
883 }
884 write_lock_bh(&mrt_lock);
885 *cp = c->next;
886 write_unlock_bh(&mrt_lock);
887
Benjamin Thery5c0a66f2009-01-22 04:56:17 +0000888 ipmr_cache_free(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 }
890 }
891
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000892 if (atomic_read(&net->ipv4.cache_resolve_queue_len) != 0) {
Benjamin Thery1e8fb3b2009-01-22 04:56:19 +0000893 struct mfc_cache *c, **cp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
895 spin_lock_bh(&mfc_unres_lock);
Benjamin Thery1e8fb3b2009-01-22 04:56:19 +0000896 cp = &mfc_unres_queue;
897 while ((c = *cp) != NULL) {
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000898 if (!net_eq(mfc_net(c), net)) {
Benjamin Thery1e8fb3b2009-01-22 04:56:19 +0000899 cp = &c->next;
900 continue;
901 }
902 *cp = c->next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
904 ipmr_destroy_unres(c);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 }
906 spin_unlock_bh(&mfc_unres_lock);
907 }
908}
909
910static void mrtsock_destruct(struct sock *sk)
911{
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000912 struct net *net = sock_net(sk);
913
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 rtnl_lock();
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000915 if (sk == net->ipv4.mroute_sk) {
916 IPV4_DEVCONF_ALL(net, MC_FORWARDING)--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
918 write_lock_bh(&mrt_lock);
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000919 net->ipv4.mroute_sk = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 write_unlock_bh(&mrt_lock);
921
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000922 mroute_clean_tables(net);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 }
924 rtnl_unlock();
925}
926
927/*
928 * Socket options and virtual interface manipulation. The whole
929 * virtual interface system is a complete heap, but unfortunately
930 * that's how BSD mrouted happens to think. Maybe one day with a proper
931 * MOSPF/PIM router set up we can clean this up.
932 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900933
Jianjun Kongc354e122008-11-03 00:28:02 -0800934int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int optlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935{
936 int ret;
937 struct vifctl vif;
938 struct mfcctl mfc;
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000939 struct net *net = sock_net(sk);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900940
Stephen Hemminger132adf52007-03-08 20:44:43 -0800941 if (optname != MRT_INIT) {
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000942 if (sk != net->ipv4.mroute_sk && !capable(CAP_NET_ADMIN))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 return -EACCES;
944 }
945
Stephen Hemminger132adf52007-03-08 20:44:43 -0800946 switch (optname) {
947 case MRT_INIT:
948 if (sk->sk_type != SOCK_RAW ||
949 inet_sk(sk)->num != IPPROTO_IGMP)
950 return -EOPNOTSUPP;
Jianjun Kongc354e122008-11-03 00:28:02 -0800951 if (optlen != sizeof(int))
Stephen Hemminger132adf52007-03-08 20:44:43 -0800952 return -ENOPROTOOPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953
Stephen Hemminger132adf52007-03-08 20:44:43 -0800954 rtnl_lock();
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000955 if (net->ipv4.mroute_sk) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 rtnl_unlock();
Stephen Hemminger132adf52007-03-08 20:44:43 -0800957 return -EADDRINUSE;
958 }
959
960 ret = ip_ra_control(sk, 1, mrtsock_destruct);
961 if (ret == 0) {
962 write_lock_bh(&mrt_lock);
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000963 net->ipv4.mroute_sk = sk;
Stephen Hemminger132adf52007-03-08 20:44:43 -0800964 write_unlock_bh(&mrt_lock);
965
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000966 IPV4_DEVCONF_ALL(net, MC_FORWARDING)++;
Stephen Hemminger132adf52007-03-08 20:44:43 -0800967 }
968 rtnl_unlock();
969 return ret;
970 case MRT_DONE:
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000971 if (sk != net->ipv4.mroute_sk)
Stephen Hemminger132adf52007-03-08 20:44:43 -0800972 return -EACCES;
973 return ip_ra_control(sk, 0, NULL);
974 case MRT_ADD_VIF:
975 case MRT_DEL_VIF:
Jianjun Kongc354e122008-11-03 00:28:02 -0800976 if (optlen != sizeof(vif))
Stephen Hemminger132adf52007-03-08 20:44:43 -0800977 return -EINVAL;
Jianjun Kongc354e122008-11-03 00:28:02 -0800978 if (copy_from_user(&vif, optval, sizeof(vif)))
Stephen Hemminger132adf52007-03-08 20:44:43 -0800979 return -EFAULT;
980 if (vif.vifc_vifi >= MAXVIFS)
981 return -ENFILE;
982 rtnl_lock();
Jianjun Kongc354e122008-11-03 00:28:02 -0800983 if (optname == MRT_ADD_VIF) {
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000984 ret = vif_add(net, &vif, sk == net->ipv4.mroute_sk);
Stephen Hemminger132adf52007-03-08 20:44:43 -0800985 } else {
Benjamin Thery4feb88e2009-01-22 04:56:23 +0000986 ret = vif_delete(net, vif.vifc_vifi, 0);
Stephen Hemminger132adf52007-03-08 20:44:43 -0800987 }
988 rtnl_unlock();
989 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
991 /*
992 * Manipulate the forwarding caches. These live
993 * in a sort of kernel/user symbiosis.
994 */
Stephen Hemminger132adf52007-03-08 20:44:43 -0800995 case MRT_ADD_MFC:
996 case MRT_DEL_MFC:
Jianjun Kongc354e122008-11-03 00:28:02 -0800997 if (optlen != sizeof(mfc))
Stephen Hemminger132adf52007-03-08 20:44:43 -0800998 return -EINVAL;
Jianjun Kongc354e122008-11-03 00:28:02 -0800999 if (copy_from_user(&mfc, optval, sizeof(mfc)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001000 return -EFAULT;
1001 rtnl_lock();
Jianjun Kongc354e122008-11-03 00:28:02 -08001002 if (optname == MRT_DEL_MFC)
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001003 ret = ipmr_mfc_delete(net, &mfc);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001004 else
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001005 ret = ipmr_mfc_add(net, &mfc, sk == net->ipv4.mroute_sk);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001006 rtnl_unlock();
1007 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 /*
1009 * Control PIM assert.
1010 */
Stephen Hemminger132adf52007-03-08 20:44:43 -08001011 case MRT_ASSERT:
1012 {
1013 int v;
1014 if (get_user(v,(int __user *)optval))
1015 return -EFAULT;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001016 net->ipv4.mroute_do_assert = (v) ? 1 : 0;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001017 return 0;
1018 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019#ifdef CONFIG_IP_PIMSM
Stephen Hemminger132adf52007-03-08 20:44:43 -08001020 case MRT_PIM:
1021 {
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08001022 int v;
1023
Stephen Hemminger132adf52007-03-08 20:44:43 -08001024 if (get_user(v,(int __user *)optval))
1025 return -EFAULT;
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08001026 v = (v) ? 1 : 0;
1027
Stephen Hemminger132adf52007-03-08 20:44:43 -08001028 rtnl_lock();
1029 ret = 0;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001030 if (v != net->ipv4.mroute_do_pim) {
1031 net->ipv4.mroute_do_pim = v;
1032 net->ipv4.mroute_do_assert = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 }
Stephen Hemminger132adf52007-03-08 20:44:43 -08001034 rtnl_unlock();
1035 return ret;
1036 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037#endif
Stephen Hemminger132adf52007-03-08 20:44:43 -08001038 /*
1039 * Spurious command, or MRT_VERSION which you cannot
1040 * set.
1041 */
1042 default:
1043 return -ENOPROTOOPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 }
1045}
1046
1047/*
1048 * Getsock opt support for the multicast routing system.
1049 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001050
Jianjun Kongc354e122008-11-03 00:28:02 -08001051int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052{
1053 int olr;
1054 int val;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001055 struct net *net = sock_net(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056
Jianjun Kongc354e122008-11-03 00:28:02 -08001057 if (optname != MRT_VERSION &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058#ifdef CONFIG_IP_PIMSM
1059 optname!=MRT_PIM &&
1060#endif
1061 optname!=MRT_ASSERT)
1062 return -ENOPROTOOPT;
1063
1064 if (get_user(olr, optlen))
1065 return -EFAULT;
1066
1067 olr = min_t(unsigned int, olr, sizeof(int));
1068 if (olr < 0)
1069 return -EINVAL;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001070
Jianjun Kongc354e122008-11-03 00:28:02 -08001071 if (put_user(olr, optlen))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 return -EFAULT;
Jianjun Kongc354e122008-11-03 00:28:02 -08001073 if (optname == MRT_VERSION)
1074 val = 0x0305;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075#ifdef CONFIG_IP_PIMSM
Jianjun Kongc354e122008-11-03 00:28:02 -08001076 else if (optname == MRT_PIM)
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001077 val = net->ipv4.mroute_do_pim;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078#endif
1079 else
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001080 val = net->ipv4.mroute_do_assert;
Jianjun Kongc354e122008-11-03 00:28:02 -08001081 if (copy_to_user(optval, &val, olr))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 return -EFAULT;
1083 return 0;
1084}
1085
1086/*
1087 * The IP multicast ioctl support routines.
1088 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001089
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
1091{
1092 struct sioc_sg_req sr;
1093 struct sioc_vif_req vr;
1094 struct vif_device *vif;
1095 struct mfc_cache *c;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001096 struct net *net = sock_net(sk);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001097
Stephen Hemminger132adf52007-03-08 20:44:43 -08001098 switch (cmd) {
1099 case SIOCGETVIFCNT:
Jianjun Kongc354e122008-11-03 00:28:02 -08001100 if (copy_from_user(&vr, arg, sizeof(vr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001101 return -EFAULT;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001102 if (vr.vifi >= net->ipv4.maxvif)
Stephen Hemminger132adf52007-03-08 20:44:43 -08001103 return -EINVAL;
1104 read_lock(&mrt_lock);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001105 vif = &net->ipv4.vif_table[vr.vifi];
1106 if (VIF_EXISTS(net, vr.vifi)) {
Jianjun Kongc354e122008-11-03 00:28:02 -08001107 vr.icount = vif->pkt_in;
1108 vr.ocount = vif->pkt_out;
1109 vr.ibytes = vif->bytes_in;
1110 vr.obytes = vif->bytes_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 read_unlock(&mrt_lock);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001112
Jianjun Kongc354e122008-11-03 00:28:02 -08001113 if (copy_to_user(arg, &vr, sizeof(vr)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 return -EFAULT;
Stephen Hemminger132adf52007-03-08 20:44:43 -08001115 return 0;
1116 }
1117 read_unlock(&mrt_lock);
1118 return -EADDRNOTAVAIL;
1119 case SIOCGETSGCNT:
Jianjun Kongc354e122008-11-03 00:28:02 -08001120 if (copy_from_user(&sr, arg, sizeof(sr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001121 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122
Stephen Hemminger132adf52007-03-08 20:44:43 -08001123 read_lock(&mrt_lock);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001124 c = ipmr_cache_find(net, sr.src.s_addr, sr.grp.s_addr);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001125 if (c) {
1126 sr.pktcnt = c->mfc_un.res.pkt;
1127 sr.bytecnt = c->mfc_un.res.bytes;
1128 sr.wrong_if = c->mfc_un.res.wrong_if;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 read_unlock(&mrt_lock);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001130
Jianjun Kongc354e122008-11-03 00:28:02 -08001131 if (copy_to_user(arg, &sr, sizeof(sr)))
Stephen Hemminger132adf52007-03-08 20:44:43 -08001132 return -EFAULT;
1133 return 0;
1134 }
1135 read_unlock(&mrt_lock);
1136 return -EADDRNOTAVAIL;
1137 default:
1138 return -ENOIOCTLCMD;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 }
1140}
1141
1142
1143static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
1144{
Eric W. Biedermane9dc8652007-09-12 13:02:17 +02001145 struct net_device *dev = ptr;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001146 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 struct vif_device *v;
1148 int ct;
Eric W. Biedermane9dc8652007-09-12 13:02:17 +02001149
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001150 if (!net_eq(dev_net(dev), net))
Eric W. Biedermane9dc8652007-09-12 13:02:17 +02001151 return NOTIFY_DONE;
1152
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 if (event != NETDEV_UNREGISTER)
1154 return NOTIFY_DONE;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001155 v = &net->ipv4.vif_table[0];
1156 for (ct = 0; ct < net->ipv4.maxvif; ct++, v++) {
Jianjun Kongc354e122008-11-03 00:28:02 -08001157 if (v->dev == dev)
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001158 vif_delete(net, ct, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 }
1160 return NOTIFY_DONE;
1161}
1162
1163
Jianjun Kongc354e122008-11-03 00:28:02 -08001164static struct notifier_block ip_mr_notifier = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 .notifier_call = ipmr_device_event,
1166};
1167
1168/*
1169 * Encapsulate a packet by attaching a valid IPIP header to it.
1170 * This avoids tunnel drivers and other mess and gives us the speed so
1171 * important for multicast video.
1172 */
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001173
Al Viro114c7842006-09-27 18:39:29 -07001174static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175{
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001176 struct iphdr *iph;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001177 struct iphdr *old_iph = ip_hdr(skb);
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001178
1179 skb_push(skb, sizeof(struct iphdr));
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07001180 skb->transport_header = skb->network_header;
Arnaldo Carvalho de Melo8856dfa2007-03-10 19:40:39 -03001181 skb_reset_network_header(skb);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001182 iph = ip_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183
1184 iph->version = 4;
Arnaldo Carvalho de Meloe023dd62007-03-12 20:09:36 -03001185 iph->tos = old_iph->tos;
1186 iph->ttl = old_iph->ttl;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 iph->frag_off = 0;
1188 iph->daddr = daddr;
1189 iph->saddr = saddr;
1190 iph->protocol = IPPROTO_IPIP;
1191 iph->ihl = 5;
1192 iph->tot_len = htons(skb->len);
Eric Dumazetadf30902009-06-02 05:19:30 +00001193 ip_select_ident(iph, skb_dst(skb), NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 ip_send_check(iph);
1195
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
1197 nf_reset(skb);
1198}
1199
1200static inline int ipmr_forward_finish(struct sk_buff *skb)
1201{
1202 struct ip_options * opt = &(IPCB(skb)->opt);
1203
Eric Dumazetadf30902009-06-02 05:19:30 +00001204 IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205
1206 if (unlikely(opt->optlen))
1207 ip_forward_options(skb);
1208
1209 return dst_output(skb);
1210}
1211
1212/*
1213 * Processing handlers for ipmr_forward
1214 */
1215
1216static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi)
1217{
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001218 struct net *net = mfc_net(c);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001219 const struct iphdr *iph = ip_hdr(skb);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001220 struct vif_device *vif = &net->ipv4.vif_table[vifi];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 struct net_device *dev;
1222 struct rtable *rt;
1223 int encap = 0;
1224
1225 if (vif->dev == NULL)
1226 goto out_free;
1227
1228#ifdef CONFIG_IP_PIMSM
1229 if (vif->flags & VIFF_REGISTER) {
1230 vif->pkt_out++;
Jianjun Kongc354e122008-11-03 00:28:02 -08001231 vif->bytes_out += skb->len;
Pavel Emelyanovcf3677a2008-05-21 14:17:33 -07001232 vif->dev->stats.tx_bytes += skb->len;
1233 vif->dev->stats.tx_packets++;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001234 ipmr_cache_report(net, skb, vifi, IGMPMSG_WHOLEPKT);
Ilpo Järvinen69ebbf52009-02-06 23:46:51 -08001235 goto out_free;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 }
1237#endif
1238
1239 if (vif->flags&VIFF_TUNNEL) {
1240 struct flowi fl = { .oif = vif->link,
1241 .nl_u = { .ip4_u =
1242 { .daddr = vif->remote,
1243 .saddr = vif->local,
1244 .tos = RT_TOS(iph->tos) } },
1245 .proto = IPPROTO_IPIP };
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001246 if (ip_route_output_key(net, &rt, &fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 goto out_free;
1248 encap = sizeof(struct iphdr);
1249 } else {
1250 struct flowi fl = { .oif = vif->link,
1251 .nl_u = { .ip4_u =
1252 { .daddr = iph->daddr,
1253 .tos = RT_TOS(iph->tos) } },
1254 .proto = IPPROTO_IPIP };
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001255 if (ip_route_output_key(net, &rt, &fl))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 goto out_free;
1257 }
1258
1259 dev = rt->u.dst.dev;
1260
1261 if (skb->len+encap > dst_mtu(&rt->u.dst) && (ntohs(iph->frag_off) & IP_DF)) {
1262 /* Do not fragment multicasts. Alas, IPv4 does not
1263 allow to send ICMP, so that packets will disappear
1264 to blackhole.
1265 */
1266
Pavel Emelyanov7c73a6f2008-07-16 20:20:11 -07001267 IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 ip_rt_put(rt);
1269 goto out_free;
1270 }
1271
1272 encap += LL_RESERVED_SPACE(dev) + rt->u.dst.header_len;
1273
1274 if (skb_cow(skb, encap)) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001275 ip_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 goto out_free;
1277 }
1278
1279 vif->pkt_out++;
Jianjun Kongc354e122008-11-03 00:28:02 -08001280 vif->bytes_out += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281
Eric Dumazetadf30902009-06-02 05:19:30 +00001282 skb_dst_drop(skb);
1283 skb_dst_set(skb, &rt->u.dst);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001284 ip_decrease_ttl(ip_hdr(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285
1286 /* FIXME: forward and output firewalls used to be called here.
1287 * What do we do with netfilter? -- RR */
1288 if (vif->flags & VIFF_TUNNEL) {
1289 ip_encap(skb, vif->local, vif->remote);
1290 /* FIXME: extra output firewall step used to be here. --RR */
Pavel Emelyanov2f4c02d2008-05-21 14:16:14 -07001291 vif->dev->stats.tx_packets++;
1292 vif->dev->stats.tx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 }
1294
1295 IPCB(skb)->flags |= IPSKB_FORWARDED;
1296
1297 /*
1298 * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
1299 * not only before forwarding, but after forwarding on all output
1300 * interfaces. It is clear, if mrouter runs a multicasting
1301 * program, it should receive packets not depending to what interface
1302 * program is joined.
1303 * If we will not make it, the program will have to join on all
1304 * interfaces. On the other hand, multihoming host (or router, but
1305 * not mrouter) cannot join to more than one interface - it will
1306 * result in receiving multiple packets.
1307 */
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001308 NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, dev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 ipmr_forward_finish);
1310 return;
1311
1312out_free:
1313 kfree_skb(skb);
1314 return;
1315}
1316
1317static int ipmr_find_vif(struct net_device *dev)
1318{
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001319 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 int ct;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001321 for (ct = net->ipv4.maxvif-1; ct >= 0; ct--) {
1322 if (net->ipv4.vif_table[ct].dev == dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 break;
1324 }
1325 return ct;
1326}
1327
1328/* "local" means that we should preserve one skb (for local delivery) */
1329
1330static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local)
1331{
1332 int psend = -1;
1333 int vif, ct;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001334 struct net *net = mfc_net(cache);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335
1336 vif = cache->mfc_parent;
1337 cache->mfc_un.res.pkt++;
1338 cache->mfc_un.res.bytes += skb->len;
1339
1340 /*
1341 * Wrong interface: drop packet and (maybe) send PIM assert.
1342 */
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001343 if (net->ipv4.vif_table[vif].dev != skb->dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 int true_vifi;
1345
Eric Dumazet511c3f92009-06-02 05:14:27 +00001346 if (skb_rtable(skb)->fl.iif == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 /* It is our own packet, looped back.
1348 Very complicated situation...
1349
1350 The best workaround until routing daemons will be
1351 fixed is not to redistribute packet, if it was
1352 send through wrong interface. It means, that
1353 multicast applications WILL NOT work for
1354 (S,G), which have default multicast route pointing
1355 to wrong oif. In any case, it is not a good
1356 idea to use multicasting applications on router.
1357 */
1358 goto dont_forward;
1359 }
1360
1361 cache->mfc_un.res.wrong_if++;
1362 true_vifi = ipmr_find_vif(skb->dev);
1363
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001364 if (true_vifi >= 0 && net->ipv4.mroute_do_assert &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 /* pimsm uses asserts, when switching from RPT to SPT,
1366 so that we cannot check that packet arrived on an oif.
1367 It is bad, but otherwise we would need to move pretty
1368 large chunk of pimd to kernel. Ough... --ANK
1369 */
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001370 (net->ipv4.mroute_do_pim ||
Benjamin Thery6f9374a2009-01-22 04:56:20 +00001371 cache->mfc_un.res.ttls[true_vifi] < 255) &&
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001372 time_after(jiffies,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
1374 cache->mfc_un.res.last_assert = jiffies;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001375 ipmr_cache_report(net, skb, true_vifi, IGMPMSG_WRONGVIF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 }
1377 goto dont_forward;
1378 }
1379
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001380 net->ipv4.vif_table[vif].pkt_in++;
1381 net->ipv4.vif_table[vif].bytes_in += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382
1383 /*
1384 * Forward the frame
1385 */
1386 for (ct = cache->mfc_un.res.maxvif-1; ct >= cache->mfc_un.res.minvif; ct--) {
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001387 if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 if (psend != -1) {
1389 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
1390 if (skb2)
1391 ipmr_queue_xmit(skb2, cache, psend);
1392 }
Jianjun Kongc354e122008-11-03 00:28:02 -08001393 psend = ct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 }
1395 }
1396 if (psend != -1) {
1397 if (local) {
1398 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
1399 if (skb2)
1400 ipmr_queue_xmit(skb2, cache, psend);
1401 } else {
1402 ipmr_queue_xmit(skb, cache, psend);
1403 return 0;
1404 }
1405 }
1406
1407dont_forward:
1408 if (!local)
1409 kfree_skb(skb);
1410 return 0;
1411}
1412
1413
1414/*
1415 * Multicast packets for forwarding arrive here
1416 */
1417
1418int ip_mr_input(struct sk_buff *skb)
1419{
1420 struct mfc_cache *cache;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001421 struct net *net = dev_net(skb->dev);
Eric Dumazet511c3f92009-06-02 05:14:27 +00001422 int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423
1424 /* Packet is looped back after forward, it should not be
1425 forwarded second time, but still can be delivered locally.
1426 */
1427 if (IPCB(skb)->flags&IPSKB_FORWARDED)
1428 goto dont_forward;
1429
1430 if (!local) {
1431 if (IPCB(skb)->opt.router_alert) {
1432 if (ip_call_ra_chain(skb))
1433 return 0;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001434 } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP){
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 /* IGMPv1 (and broken IGMPv2 implementations sort of
1436 Cisco IOS <= 11.2(8)) do not put router alert
1437 option to IGMP packets destined to routable
1438 groups. It is very bad, because it means
1439 that we can forward NO IGMP messages.
1440 */
1441 read_lock(&mrt_lock);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001442 if (net->ipv4.mroute_sk) {
Patrick McHardy2715bcf2005-06-21 14:06:24 -07001443 nf_reset(skb);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001444 raw_rcv(net->ipv4.mroute_sk, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 read_unlock(&mrt_lock);
1446 return 0;
1447 }
1448 read_unlock(&mrt_lock);
1449 }
1450 }
1451
1452 read_lock(&mrt_lock);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001453 cache = ipmr_cache_find(net, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454
1455 /*
1456 * No usable cache entry
1457 */
Jianjun Kongc354e122008-11-03 00:28:02 -08001458 if (cache == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 int vif;
1460
1461 if (local) {
1462 struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
1463 ip_local_deliver(skb);
1464 if (skb2 == NULL) {
1465 read_unlock(&mrt_lock);
1466 return -ENOBUFS;
1467 }
1468 skb = skb2;
1469 }
1470
1471 vif = ipmr_find_vif(skb->dev);
1472 if (vif >= 0) {
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001473 int err = ipmr_cache_unresolved(net, vif, skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 read_unlock(&mrt_lock);
1475
1476 return err;
1477 }
1478 read_unlock(&mrt_lock);
1479 kfree_skb(skb);
1480 return -ENODEV;
1481 }
1482
1483 ip_mr_forward(skb, cache, local);
1484
1485 read_unlock(&mrt_lock);
1486
1487 if (local)
1488 return ip_local_deliver(skb);
1489
1490 return 0;
1491
1492dont_forward:
1493 if (local)
1494 return ip_local_deliver(skb);
1495 kfree_skb(skb);
1496 return 0;
1497}
1498
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001499#ifdef CONFIG_IP_PIMSM
1500static int __pim_rcv(struct sk_buff *skb, unsigned int pimlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501{
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001502 struct net_device *reg_dev = NULL;
1503 struct iphdr *encap;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001504 struct net *net = dev_net(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001506 encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 /*
1508 Check that:
1509 a. packet is really destinted to a multicast group
1510 b. packet is not a NULL-REGISTER
1511 c. packet is not truncated
1512 */
Joe Perchesf97c1e02007-12-16 13:45:43 -08001513 if (!ipv4_is_multicast(encap->daddr) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 encap->tot_len == 0 ||
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001515 ntohs(encap->tot_len) + pimlen > skb->len)
1516 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517
1518 read_lock(&mrt_lock);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001519 if (net->ipv4.mroute_reg_vif_num >= 0)
1520 reg_dev = net->ipv4.vif_table[net->ipv4.mroute_reg_vif_num].dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 if (reg_dev)
1522 dev_hold(reg_dev);
1523 read_unlock(&mrt_lock);
1524
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001525 if (reg_dev == NULL)
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001526 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527
Arnaldo Carvalho de Melob0e380b2007-04-10 21:21:55 -07001528 skb->mac_header = skb->network_header;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 skb_pull(skb, (u8*)encap - skb->data);
Arnaldo Carvalho de Melo31c77112007-03-10 19:04:55 -03001530 skb_reset_network_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 skb->dev = reg_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 skb->protocol = htons(ETH_P_IP);
1533 skb->ip_summed = 0;
1534 skb->pkt_type = PACKET_HOST;
Eric Dumazetadf30902009-06-02 05:19:30 +00001535 skb_dst_drop(skb);
Pavel Emelyanovcf3677a2008-05-21 14:17:33 -07001536 reg_dev->stats.rx_bytes += skb->len;
1537 reg_dev->stats.rx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 nf_reset(skb);
1539 netif_rx(skb);
1540 dev_put(reg_dev);
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001541
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 return 0;
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001543}
1544#endif
1545
1546#ifdef CONFIG_IP_PIMSM_V1
1547/*
1548 * Handle IGMP messages of PIMv1
1549 */
1550
1551int pim_rcv_v1(struct sk_buff * skb)
1552{
1553 struct igmphdr *pim;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001554 struct net *net = dev_net(skb->dev);
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001555
1556 if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
1557 goto drop;
1558
1559 pim = igmp_hdr(skb);
1560
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001561 if (!net->ipv4.mroute_do_pim ||
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001562 pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
1563 goto drop;
1564
1565 if (__pim_rcv(skb, sizeof(*pim))) {
1566drop:
1567 kfree_skb(skb);
1568 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 return 0;
1570}
1571#endif
1572
1573#ifdef CONFIG_IP_PIMSM_V2
1574static int pim_rcv(struct sk_buff * skb)
1575{
1576 struct pimreghdr *pim;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001578 if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 goto drop;
1580
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001581 pim = (struct pimreghdr *)skb_transport_header(skb);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001582 if (pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 (pim->flags&PIM_NULL_REGISTER) ||
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001584 (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
Al Virod3bc23e2006-11-14 21:24:49 -08001585 csum_fold(skb_checksum(skb, 0, skb->len, 0))))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586 goto drop;
1587
Ilpo Järvinenb1879202008-12-16 01:15:11 -08001588 if (__pim_rcv(skb, sizeof(*pim))) {
1589drop:
1590 kfree_skb(skb);
1591 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592 return 0;
1593}
1594#endif
1595
1596static int
1597ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm)
1598{
1599 int ct;
1600 struct rtnexthop *nhp;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001601 struct net *net = mfc_net(c);
1602 struct net_device *dev = net->ipv4.vif_table[c->mfc_parent].dev;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001603 u8 *b = skb_tail_pointer(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604 struct rtattr *mp_head;
1605
1606 if (dev)
1607 RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex);
1608
Jianjun Kongc354e122008-11-03 00:28:02 -08001609 mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610
1611 for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
1612 if (c->mfc_un.res.ttls[ct] < 255) {
1613 if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
1614 goto rtattr_failure;
Jianjun Kongc354e122008-11-03 00:28:02 -08001615 nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616 nhp->rtnh_flags = 0;
1617 nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001618 nhp->rtnh_ifindex = net->ipv4.vif_table[ct].dev->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 nhp->rtnh_len = sizeof(*nhp);
1620 }
1621 }
1622 mp_head->rta_type = RTA_MULTIPATH;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001623 mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 rtm->rtm_type = RTN_MULTICAST;
1625 return 1;
1626
1627rtattr_failure:
Arnaldo Carvalho de Melodc5fc572007-03-25 23:06:12 -07001628 nlmsg_trim(skb, b);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 return -EMSGSIZE;
1630}
1631
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001632int ipmr_get_route(struct net *net,
1633 struct sk_buff *skb, struct rtmsg *rtm, int nowait)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634{
1635 int err;
1636 struct mfc_cache *cache;
Eric Dumazet511c3f92009-06-02 05:14:27 +00001637 struct rtable *rt = skb_rtable(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638
1639 read_lock(&mrt_lock);
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001640 cache = ipmr_cache_find(net, rt->rt_src, rt->rt_dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
Jianjun Kongc354e122008-11-03 00:28:02 -08001642 if (cache == NULL) {
Alexey Kuznetsov72287492006-07-25 16:45:12 -07001643 struct sk_buff *skb2;
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001644 struct iphdr *iph;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645 struct net_device *dev;
1646 int vif;
1647
1648 if (nowait) {
1649 read_unlock(&mrt_lock);
1650 return -EAGAIN;
1651 }
1652
1653 dev = skb->dev;
1654 if (dev == NULL || (vif = ipmr_find_vif(dev)) < 0) {
1655 read_unlock(&mrt_lock);
1656 return -ENODEV;
1657 }
Alexey Kuznetsov72287492006-07-25 16:45:12 -07001658 skb2 = skb_clone(skb, GFP_ATOMIC);
1659 if (!skb2) {
1660 read_unlock(&mrt_lock);
1661 return -ENOMEM;
1662 }
1663
Arnaldo Carvalho de Meloe2d1bca2007-04-10 20:46:21 -07001664 skb_push(skb2, sizeof(struct iphdr));
1665 skb_reset_network_header(skb2);
Arnaldo Carvalho de Meloeddc9ec2007-04-20 22:47:35 -07001666 iph = ip_hdr(skb2);
1667 iph->ihl = sizeof(struct iphdr) >> 2;
1668 iph->saddr = rt->rt_src;
1669 iph->daddr = rt->rt_dst;
1670 iph->version = 0;
Benjamin Thery4feb88e2009-01-22 04:56:23 +00001671 err = ipmr_cache_unresolved(net, vif, skb2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 read_unlock(&mrt_lock);
1673 return err;
1674 }
1675
1676 if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY))
1677 cache->mfc_flags |= MFC_NOTIFY;
1678 err = ipmr_fill_mroute(skb, cache, rtm);
1679 read_unlock(&mrt_lock);
1680 return err;
1681}
1682
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001683#ifdef CONFIG_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684/*
1685 * The /proc interfaces to multicast routing /proc/ip_mr_cache /proc/ip_mr_vif
1686 */
1687struct ipmr_vif_iter {
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001688 struct seq_net_private p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689 int ct;
1690};
1691
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001692static struct vif_device *ipmr_vif_seq_idx(struct net *net,
1693 struct ipmr_vif_iter *iter,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 loff_t pos)
1695{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001696 for (iter->ct = 0; iter->ct < net->ipv4.maxvif; ++iter->ct) {
1697 if (!VIF_EXISTS(net, iter->ct))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 continue;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001699 if (pos-- == 0)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001700 return &net->ipv4.vif_table[iter->ct];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 }
1702 return NULL;
1703}
1704
1705static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos)
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08001706 __acquires(mrt_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001708 struct net *net = seq_file_net(seq);
1709
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 read_lock(&mrt_lock);
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001711 return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 : SEQ_START_TOKEN;
1713}
1714
1715static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos)
1716{
1717 struct ipmr_vif_iter *iter = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001718 struct net *net = seq_file_net(seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719
1720 ++*pos;
1721 if (v == SEQ_START_TOKEN)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001722 return ipmr_vif_seq_idx(net, iter, 0);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001723
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001724 while (++iter->ct < net->ipv4.maxvif) {
1725 if (!VIF_EXISTS(net, iter->ct))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726 continue;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001727 return &net->ipv4.vif_table[iter->ct];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 }
1729 return NULL;
1730}
1731
1732static void ipmr_vif_seq_stop(struct seq_file *seq, void *v)
Stephen Hemmingerba93ef72008-01-21 17:28:59 -08001733 __releases(mrt_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734{
1735 read_unlock(&mrt_lock);
1736}
1737
1738static int ipmr_vif_seq_show(struct seq_file *seq, void *v)
1739{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001740 struct net *net = seq_file_net(seq);
1741
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 if (v == SEQ_START_TOKEN) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001743 seq_puts(seq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n");
1745 } else {
1746 const struct vif_device *vif = v;
1747 const char *name = vif->dev ? vif->dev->name : "none";
1748
1749 seq_printf(seq,
1750 "%2Zd %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n",
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001751 vif - net->ipv4.vif_table,
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001752 name, vif->bytes_in, vif->pkt_in,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 vif->bytes_out, vif->pkt_out,
1754 vif->flags, vif->local, vif->remote);
1755 }
1756 return 0;
1757}
1758
Stephen Hemmingerf6908082007-03-12 14:34:29 -07001759static const struct seq_operations ipmr_vif_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760 .start = ipmr_vif_seq_start,
1761 .next = ipmr_vif_seq_next,
1762 .stop = ipmr_vif_seq_stop,
1763 .show = ipmr_vif_seq_show,
1764};
1765
1766static int ipmr_vif_open(struct inode *inode, struct file *file)
1767{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001768 return seq_open_net(inode, file, &ipmr_vif_seq_ops,
1769 sizeof(struct ipmr_vif_iter));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770}
1771
Arjan van de Ven9a321442007-02-12 00:55:35 -08001772static const struct file_operations ipmr_vif_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 .owner = THIS_MODULE,
1774 .open = ipmr_vif_open,
1775 .read = seq_read,
1776 .llseek = seq_lseek,
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001777 .release = seq_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778};
1779
1780struct ipmr_mfc_iter {
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001781 struct seq_net_private p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782 struct mfc_cache **cache;
1783 int ct;
1784};
1785
1786
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001787static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net,
1788 struct ipmr_mfc_iter *it, loff_t pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789{
1790 struct mfc_cache *mfc;
1791
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001792 it->cache = net->ipv4.mfc_cache_array;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793 read_lock(&mrt_lock);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001794 for (it->ct = 0; it->ct < MFC_LINES; it->ct++)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001795 for (mfc = net->ipv4.mfc_cache_array[it->ct];
Benjamin Thery2bb8b262009-01-22 04:56:18 +00001796 mfc; mfc = mfc->next)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001797 if (pos-- == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798 return mfc;
1799 read_unlock(&mrt_lock);
1800
1801 it->cache = &mfc_unres_queue;
1802 spin_lock_bh(&mfc_unres_lock);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001803 for (mfc = mfc_unres_queue; mfc; mfc = mfc->next)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001804 if (net_eq(mfc_net(mfc), net) &&
1805 pos-- == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 return mfc;
1807 spin_unlock_bh(&mfc_unres_lock);
1808
1809 it->cache = NULL;
1810 return NULL;
1811}
1812
1813
1814static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
1815{
1816 struct ipmr_mfc_iter *it = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001817 struct net *net = seq_file_net(seq);
1818
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 it->cache = NULL;
1820 it->ct = 0;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001821 return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822 : SEQ_START_TOKEN;
1823}
1824
1825static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
1826{
1827 struct mfc_cache *mfc = v;
1828 struct ipmr_mfc_iter *it = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001829 struct net *net = seq_file_net(seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830
1831 ++*pos;
1832
1833 if (v == SEQ_START_TOKEN)
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001834 return ipmr_mfc_seq_idx(net, seq->private, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835
1836 if (mfc->next)
1837 return mfc->next;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001838
1839 if (it->cache == &mfc_unres_queue)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840 goto end_of_list;
1841
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001842 BUG_ON(it->cache != net->ipv4.mfc_cache_array);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843
1844 while (++it->ct < MFC_LINES) {
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001845 mfc = net->ipv4.mfc_cache_array[it->ct];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846 if (mfc)
1847 return mfc;
1848 }
1849
1850 /* exhausted cache_array, show unresolved */
1851 read_unlock(&mrt_lock);
1852 it->cache = &mfc_unres_queue;
1853 it->ct = 0;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001854
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855 spin_lock_bh(&mfc_unres_lock);
1856 mfc = mfc_unres_queue;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001857 while (mfc && !net_eq(mfc_net(mfc), net))
1858 mfc = mfc->next;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001859 if (mfc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860 return mfc;
1861
1862 end_of_list:
1863 spin_unlock_bh(&mfc_unres_lock);
1864 it->cache = NULL;
1865
1866 return NULL;
1867}
1868
1869static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v)
1870{
1871 struct ipmr_mfc_iter *it = seq->private;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001872 struct net *net = seq_file_net(seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873
1874 if (it->cache == &mfc_unres_queue)
1875 spin_unlock_bh(&mfc_unres_lock);
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001876 else if (it->cache == net->ipv4.mfc_cache_array)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877 read_unlock(&mrt_lock);
1878}
1879
1880static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
1881{
1882 int n;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001883 struct net *net = seq_file_net(seq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884
1885 if (v == SEQ_START_TOKEN) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001886 seq_puts(seq,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 "Group Origin Iif Pkts Bytes Wrong Oifs\n");
1888 } else {
1889 const struct mfc_cache *mfc = v;
1890 const struct ipmr_mfc_iter *it = seq->private;
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001891
Benjamin Thery999890b2008-12-03 22:22:16 -08001892 seq_printf(seq, "%08lX %08lX %-3hd",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 (unsigned long) mfc->mfc_mcastgrp,
1894 (unsigned long) mfc->mfc_origin,
Benjamin Thery1ea472e2008-12-03 22:21:47 -08001895 mfc->mfc_parent);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896
1897 if (it->cache != &mfc_unres_queue) {
Benjamin Thery1ea472e2008-12-03 22:21:47 -08001898 seq_printf(seq, " %8lu %8lu %8lu",
1899 mfc->mfc_un.res.pkt,
1900 mfc->mfc_un.res.bytes,
1901 mfc->mfc_un.res.wrong_if);
Stephen Hemminger132adf52007-03-08 20:44:43 -08001902 for (n = mfc->mfc_un.res.minvif;
1903 n < mfc->mfc_un.res.maxvif; n++ ) {
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001904 if (VIF_EXISTS(net, n) &&
Benjamin Therycf958ae32009-01-22 04:56:16 +00001905 mfc->mfc_un.res.ttls[n] < 255)
1906 seq_printf(seq,
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001907 " %2d:%-3d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 n, mfc->mfc_un.res.ttls[n]);
1909 }
Benjamin Thery1ea472e2008-12-03 22:21:47 -08001910 } else {
1911 /* unresolved mfc_caches don't contain
1912 * pkt, bytes and wrong_if values
1913 */
1914 seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915 }
1916 seq_putc(seq, '\n');
1917 }
1918 return 0;
1919}
1920
Stephen Hemmingerf6908082007-03-12 14:34:29 -07001921static const struct seq_operations ipmr_mfc_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922 .start = ipmr_mfc_seq_start,
1923 .next = ipmr_mfc_seq_next,
1924 .stop = ipmr_mfc_seq_stop,
1925 .show = ipmr_mfc_seq_show,
1926};
1927
1928static int ipmr_mfc_open(struct inode *inode, struct file *file)
1929{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001930 return seq_open_net(inode, file, &ipmr_mfc_seq_ops,
1931 sizeof(struct ipmr_mfc_iter));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932}
1933
Arjan van de Ven9a321442007-02-12 00:55:35 -08001934static const struct file_operations ipmr_mfc_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 .owner = THIS_MODULE,
1936 .open = ipmr_mfc_open,
1937 .read = seq_read,
1938 .llseek = seq_lseek,
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001939 .release = seq_release_net,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940};
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001941#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942
1943#ifdef CONFIG_IP_PIMSM_V2
Alexey Dobriyan32613092009-09-14 12:21:47 +00001944static const struct net_protocol pim_protocol = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945 .handler = pim_rcv,
Tom Goff403dbb92009-06-14 03:16:13 -07001946 .netns_ok = 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947};
1948#endif
1949
1950
1951/*
1952 * Setup for IP multicast routing
1953 */
Benjamin Therycf958ae32009-01-22 04:56:16 +00001954static int __net_init ipmr_net_init(struct net *net)
1955{
1956 int err = 0;
1957
1958 net->ipv4.vif_table = kcalloc(MAXVIFS, sizeof(struct vif_device),
1959 GFP_KERNEL);
1960 if (!net->ipv4.vif_table) {
1961 err = -ENOMEM;
1962 goto fail;
1963 }
Benjamin Thery2bb8b262009-01-22 04:56:18 +00001964
1965 /* Forwarding cache */
1966 net->ipv4.mfc_cache_array = kcalloc(MFC_LINES,
1967 sizeof(struct mfc_cache *),
1968 GFP_KERNEL);
1969 if (!net->ipv4.mfc_cache_array) {
1970 err = -ENOMEM;
1971 goto fail_mfc_cache;
1972 }
Benjamin Thery6c5143d2009-01-22 04:56:21 +00001973
1974#ifdef CONFIG_IP_PIMSM
1975 net->ipv4.mroute_reg_vif_num = -1;
1976#endif
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001977
1978#ifdef CONFIG_PROC_FS
1979 err = -ENOMEM;
1980 if (!proc_net_fops_create(net, "ip_mr_vif", 0, &ipmr_vif_fops))
1981 goto proc_vif_fail;
1982 if (!proc_net_fops_create(net, "ip_mr_cache", 0, &ipmr_mfc_fops))
1983 goto proc_cache_fail;
1984#endif
Benjamin Thery2bb8b262009-01-22 04:56:18 +00001985 return 0;
1986
Benjamin Theryf6bb4512009-01-22 04:56:22 +00001987#ifdef CONFIG_PROC_FS
1988proc_cache_fail:
1989 proc_net_remove(net, "ip_mr_vif");
1990proc_vif_fail:
1991 kfree(net->ipv4.mfc_cache_array);
1992#endif
Benjamin Thery2bb8b262009-01-22 04:56:18 +00001993fail_mfc_cache:
1994 kfree(net->ipv4.vif_table);
Benjamin Therycf958ae32009-01-22 04:56:16 +00001995fail:
1996 return err;
1997}
1998
1999static void __net_exit ipmr_net_exit(struct net *net)
2000{
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002001#ifdef CONFIG_PROC_FS
2002 proc_net_remove(net, "ip_mr_cache");
2003 proc_net_remove(net, "ip_mr_vif");
2004#endif
Benjamin Thery2bb8b262009-01-22 04:56:18 +00002005 kfree(net->ipv4.mfc_cache_array);
Benjamin Therycf958ae32009-01-22 04:56:16 +00002006 kfree(net->ipv4.vif_table);
2007}
2008
2009static struct pernet_operations ipmr_net_ops = {
2010 .init = ipmr_net_init,
2011 .exit = ipmr_net_exit,
2012};
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09002013
Wang Chen03d2f892008-07-03 12:13:36 +08002014int __init ip_mr_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015{
Wang Chen03d2f892008-07-03 12:13:36 +08002016 int err;
2017
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018 mrt_cachep = kmem_cache_create("ip_mrt_cache",
2019 sizeof(struct mfc_cache),
Alexey Dobriyane5d679f332006-08-26 19:25:52 -07002020 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
Paul Mundt20c2df82007-07-20 10:11:58 +09002021 NULL);
Wang Chen03d2f892008-07-03 12:13:36 +08002022 if (!mrt_cachep)
2023 return -ENOMEM;
2024
Benjamin Therycf958ae32009-01-22 04:56:16 +00002025 err = register_pernet_subsys(&ipmr_net_ops);
2026 if (err)
2027 goto reg_pernet_fail;
2028
Pavel Emelyanovb24b8a22008-01-23 21:20:07 -08002029 setup_timer(&ipmr_expire_timer, ipmr_expire_process, 0);
Wang Chen03d2f892008-07-03 12:13:36 +08002030 err = register_netdevice_notifier(&ip_mr_notifier);
2031 if (err)
2032 goto reg_notif_fail;
Tom Goff403dbb92009-06-14 03:16:13 -07002033#ifdef CONFIG_IP_PIMSM_V2
2034 if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) {
2035 printk(KERN_ERR "ip_mr_init: can't add PIM protocol\n");
2036 err = -EAGAIN;
2037 goto add_proto_fail;
2038 }
2039#endif
Wang Chen03d2f892008-07-03 12:13:36 +08002040 return 0;
Benjamin Theryf6bb4512009-01-22 04:56:22 +00002041
Tom Goff403dbb92009-06-14 03:16:13 -07002042#ifdef CONFIG_IP_PIMSM_V2
2043add_proto_fail:
2044 unregister_netdevice_notifier(&ip_mr_notifier);
2045#endif
Benjamin Theryc3e38892008-11-19 14:07:41 -08002046reg_notif_fail:
2047 del_timer(&ipmr_expire_timer);
Benjamin Therycf958ae32009-01-22 04:56:16 +00002048 unregister_pernet_subsys(&ipmr_net_ops);
2049reg_pernet_fail:
Benjamin Theryc3e38892008-11-19 14:07:41 -08002050 kmem_cache_destroy(mrt_cachep);
Wang Chen03d2f892008-07-03 12:13:36 +08002051 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052}