Newer
Older
/*
* IP multicast routing support for mrouted 3.6/3.8
*
* (c) 1995 Alan Cox, <alan@redhat.com>
* Linux Consultancy and Custom Driver Development
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Version: $Id: ipmr.c,v 1.65 2001/10/31 21:55:54 davem Exp $
*
* Fixes:
* Michael Chastain : Incorrect size of copying.
* Alan Cox : Added the cache manager code
* Alan Cox : Fixed the clone/copy bug and device race.
* Mike McLagan : Routing by source
* Malcolm Beattie : Buffer handling fixes.
* Alexey Kuznetsov : Double buffer free and other fixes.
* SVR Anand : Fixed several multicast bugs and problems.
* Alexey Kuznetsov : Status, optimisations and more.
* Brad Parker : Better behaviour on mrouted upcall
* overflow.
* Carlos Picoto : PIMv1 Support
* Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header
* Relax this requrement to work with older peers.
*
*/
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/kernel.h>
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/igmp.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/if_ether.h>
#include <net/net_namespace.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <linux/skbuff.h>

Arnaldo Carvalho de Melo
committed
#include <net/route.h>
#include <net/sock.h>
#include <net/icmp.h>
#include <net/udp.h>
#include <net/raw.h>
#include <linux/notifier.h>
#include <linux/if_arp.h>
#include <linux/netfilter_ipv4.h>
#include <net/ipip.h>
#include <net/checksum.h>
#include <net/netlink.h>
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
#define CONFIG_IP_PIMSM 1
#endif
static struct sock *mroute_socket;
/* Big lock, protecting vif table, mrt cache and mroute socket state.
Note that the changes are semaphored via rtnl_lock.
*/
static DEFINE_RWLOCK(mrt_lock);
/*
* Multicast router control variables
*/
static struct vif_device vif_table[MAXVIFS]; /* Devices */
static int maxvif;
#define VIF_EXISTS(idx) (vif_table[idx].dev != NULL)
static int mroute_do_assert; /* Set in PIM assert */
static int mroute_do_pim;
static struct mfc_cache *mfc_cache_array[MFC_LINES]; /* Forwarding cache */
static struct mfc_cache *mfc_unres_queue; /* Queue of unresolved entries */
static atomic_t cache_resolve_queue_len; /* Size of unresolved */
/* Special spinlock for queue of unresolved entries */
static DEFINE_SPINLOCK(mfc_unres_lock);
/* We return to original Alan's scheme. Hash table of resolved
entries is changed only in process context and protected
with weak lock mrt_lock. Queue of unresolved entries is protected
with strong spinlock mfc_unres_lock.
In this case data path is free of exclusive locks at all.
*/
static struct kmem_cache *mrt_cachep __read_mostly;
static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local);
static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert);
static int ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm);
#ifdef CONFIG_IP_PIMSM_V2
static struct net_protocol pim_protocol;
#endif
static struct timer_list ipmr_expire_timer;
/* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */
static
struct net_device *ipmr_new_tunnel(struct vifctl *v)
{
struct net_device *dev;
dev = __dev_get_by_name(&init_net, "tunl0");
if (dev) {
int err;
struct ifreq ifr;
mm_segment_t oldfs;
struct ip_tunnel_parm p;
struct in_device *in_dev;
memset(&p, 0, sizeof(p));
p.iph.daddr = v->vifc_rmt_addr.s_addr;
p.iph.saddr = v->vifc_lcl_addr.s_addr;
p.iph.version = 4;
p.iph.ihl = 5;
p.iph.protocol = IPPROTO_IPIP;
sprintf(p.name, "dvmrp%d", v->vifc_vifi);
ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
oldfs = get_fs(); set_fs(KERNEL_DS);
err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL);
set_fs(oldfs);
dev = NULL;
if (err == 0 && (dev = __dev_get_by_name(&init_net, p.name)) != NULL) {
in_dev = __in_dev_get_rtnl(dev);
ipv4_devconf_setall(in_dev);
IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
if (dev_open(dev))
goto failure;
}
}
return dev;
failure:
/* allow the register to be completed before unregistering. */
rtnl_unlock();
rtnl_lock();
unregister_netdevice(dev);
return NULL;
}
#ifdef CONFIG_IP_PIMSM
static int reg_vif_num = -1;
static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
{
read_lock(&mrt_lock);
((struct net_device_stats*)netdev_priv(dev))->tx_bytes += skb->len;
((struct net_device_stats*)netdev_priv(dev))->tx_packets++;
ipmr_cache_report(skb, reg_vif_num, IGMPMSG_WHOLEPKT);
read_unlock(&mrt_lock);
kfree_skb(skb);
return 0;
}
static struct net_device_stats *reg_vif_get_stats(struct net_device *dev)
{
return (struct net_device_stats*)netdev_priv(dev);
}
static void reg_vif_setup(struct net_device *dev)
{
dev->type = ARPHRD_PIMREG;
dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8;
dev->flags = IFF_NOARP;
dev->hard_start_xmit = reg_vif_xmit;
dev->get_stats = reg_vif_get_stats;
dev->destructor = free_netdev;
}
static struct net_device *ipmr_reg_vif(void)
{
struct net_device *dev;
struct in_device *in_dev;
dev = alloc_netdev(sizeof(struct net_device_stats), "pimreg",
reg_vif_setup);
if (dev == NULL)
return NULL;
if (register_netdevice(dev)) {
free_netdev(dev);
return NULL;
}
dev->iflink = 0;
rcu_read_lock();
if ((in_dev = __in_dev_get_rcu(dev)) == NULL) {
rcu_read_unlock();
ipv4_devconf_setall(in_dev);
IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
rcu_read_unlock();
if (dev_open(dev))
goto failure;
return dev;
failure:
/* allow the register to be completed before unregistering. */
rtnl_unlock();
rtnl_lock();
unregister_netdevice(dev);
return NULL;
}
#endif
/*
* Delete a VIF entry
*/
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
static int vif_delete(int vifi)
{
struct vif_device *v;
struct net_device *dev;
struct in_device *in_dev;
if (vifi < 0 || vifi >= maxvif)
return -EADDRNOTAVAIL;
v = &vif_table[vifi];
write_lock_bh(&mrt_lock);
dev = v->dev;
v->dev = NULL;
if (!dev) {
write_unlock_bh(&mrt_lock);
return -EADDRNOTAVAIL;
}
#ifdef CONFIG_IP_PIMSM
if (vifi == reg_vif_num)
reg_vif_num = -1;
#endif
if (vifi+1 == maxvif) {
int tmp;
for (tmp=vifi-1; tmp>=0; tmp--) {
if (VIF_EXISTS(tmp))
break;
}
maxvif = tmp+1;
}
write_unlock_bh(&mrt_lock);
dev_set_allmulti(dev, -1);
if ((in_dev = __in_dev_get_rtnl(dev)) != NULL) {
IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--;
ip_rt_multicast_event(in_dev);
}
if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER))
unregister_netdevice(dev);
dev_put(dev);
return 0;
}
/* Destroy an unresolved cache entry, killing queued skbs
and reporting error to netlink readers.
*/
static void ipmr_destroy_unres(struct mfc_cache *c)
{
struct sk_buff *skb;
while ((skb=skb_dequeue(&c->mfc_un.unres.unresolved))) {
if (ip_hdr(skb)->version == 0) {
struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
nlh->nlmsg_type = NLMSG_ERROR;
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
skb_trim(skb, nlh->nlmsg_len);
e = NLMSG_DATA(nlh);
e->error = -ETIMEDOUT;
memset(&e->msg, 0, sizeof(e->msg));
rtnl_unicast(skb, &init_net, NETLINK_CB(skb).pid);
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
} else
kfree_skb(skb);
}
kmem_cache_free(mrt_cachep, c);
}
/* Single timer process for all the unresolved queue. */
static void ipmr_expire_process(unsigned long dummy)
{
unsigned long now;
unsigned long expires;
struct mfc_cache *c, **cp;
if (!spin_trylock(&mfc_unres_lock)) {
mod_timer(&ipmr_expire_timer, jiffies+HZ/10);
return;
}
if (atomic_read(&cache_resolve_queue_len) == 0)
goto out;
now = jiffies;
expires = 10*HZ;
cp = &mfc_unres_queue;
while ((c=*cp) != NULL) {
if (time_after(c->mfc_un.unres.expires, now)) {
unsigned long interval = c->mfc_un.unres.expires - now;
if (interval < expires)
expires = interval;
cp = &c->next;
continue;
}
*cp = c->next;
ipmr_destroy_unres(c);
}
if (atomic_read(&cache_resolve_queue_len))
mod_timer(&ipmr_expire_timer, jiffies + expires);
out:
spin_unlock(&mfc_unres_lock);
}
/* Fill oifs list. It is called under write locked mrt_lock. */
static void ipmr_update_thresholds(struct mfc_cache *cache, unsigned char *ttls)
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
{
int vifi;
cache->mfc_un.res.minvif = MAXVIFS;
cache->mfc_un.res.maxvif = 0;
memset(cache->mfc_un.res.ttls, 255, MAXVIFS);
for (vifi=0; vifi<maxvif; vifi++) {
if (VIF_EXISTS(vifi) && ttls[vifi] && ttls[vifi] < 255) {
cache->mfc_un.res.ttls[vifi] = ttls[vifi];
if (cache->mfc_un.res.minvif > vifi)
cache->mfc_un.res.minvif = vifi;
if (cache->mfc_un.res.maxvif <= vifi)
cache->mfc_un.res.maxvif = vifi + 1;
}
}
}
static int vif_add(struct vifctl *vifc, int mrtsock)
{
int vifi = vifc->vifc_vifi;
struct vif_device *v = &vif_table[vifi];
struct net_device *dev;
struct in_device *in_dev;
/* Is vif busy ? */
if (VIF_EXISTS(vifi))
return -EADDRINUSE;
switch (vifc->vifc_flags) {
#ifdef CONFIG_IP_PIMSM
case VIFF_REGISTER:
/*
* Special Purpose VIF in PIM
* All the packets will be sent to the daemon
*/
if (reg_vif_num >= 0)
return -EADDRINUSE;
dev = ipmr_reg_vif();
if (!dev)
return -ENOBUFS;
break;
#endif
dev = ipmr_new_tunnel(vifc);
if (!dev)
return -ENOBUFS;
break;
case 0:
dev = ip_dev_find(&init_net, vifc->vifc_lcl_addr.s_addr);
if ((in_dev = __in_dev_get_rtnl(dev)) == NULL)
IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++;
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
dev_set_allmulti(dev, +1);
ip_rt_multicast_event(in_dev);
/*
* Fill in the VIF structures
*/
v->rate_limit=vifc->vifc_rate_limit;
v->local=vifc->vifc_lcl_addr.s_addr;
v->remote=vifc->vifc_rmt_addr.s_addr;
v->flags=vifc->vifc_flags;
if (!mrtsock)
v->flags |= VIFF_STATIC;
v->threshold=vifc->vifc_threshold;
v->bytes_in = 0;
v->bytes_out = 0;
v->pkt_in = 0;
v->pkt_out = 0;
v->link = dev->ifindex;
if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER))
v->link = dev->iflink;
/* And finish update writing critical data */
write_lock_bh(&mrt_lock);
dev_hold(dev);
v->dev=dev;
#ifdef CONFIG_IP_PIMSM
if (v->flags&VIFF_REGISTER)
reg_vif_num = vifi;
#endif
if (vifi+1 > maxvif)
maxvif = vifi+1;
write_unlock_bh(&mrt_lock);
return 0;
}
static struct mfc_cache *ipmr_cache_find(__be32 origin, __be32 mcastgrp)
{
int line=MFC_HASH(mcastgrp,origin);
struct mfc_cache *c;
for (c=mfc_cache_array[line]; c; c = c->next) {
if (c->mfc_origin==origin && c->mfc_mcastgrp==mcastgrp)
break;
}
return c;
}
/*
* Allocate a multicast cache entry
*/
static struct mfc_cache *ipmr_cache_alloc(void)
{
struct mfc_cache *c=kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
return NULL;
c->mfc_un.res.minvif = MAXVIFS;
return c;
}
static struct mfc_cache *ipmr_cache_alloc_unres(void)
{
struct mfc_cache *c=kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
return NULL;
skb_queue_head_init(&c->mfc_un.unres.unresolved);
c->mfc_un.unres.expires = jiffies + 10*HZ;
return c;
}
/*
* A cache entry has gone into a resolved state from queued
*/
static void ipmr_cache_resolve(struct mfc_cache *uc, struct mfc_cache *c)
{
struct sk_buff *skb;
/*
* Play the pending entries through our router
*/
while ((skb=__skb_dequeue(&uc->mfc_un.unres.unresolved))) {
if (ip_hdr(skb)->version == 0) {
struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
if (ipmr_fill_mroute(skb, c, NLMSG_DATA(nlh)) > 0) {
nlh->nlmsg_len = (skb_tail_pointer(skb) -
(u8 *)nlh);
} else {
nlh->nlmsg_type = NLMSG_ERROR;
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
skb_trim(skb, nlh->nlmsg_len);
e = NLMSG_DATA(nlh);
e->error = -EMSGSIZE;
memset(&e->msg, 0, sizeof(e->msg));
rtnl_unicast(skb, &init_net, NETLINK_CB(skb).pid);
} else
ip_mr_forward(skb, c, 0);
}
}
/*
* Bounce a cache query up to mrouted. We could use netlink for this but mrouted
* expects the following bizarre scheme.
*
* Called under mrt_lock.
*/
static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert)
{
struct sk_buff *skb;
struct igmphdr *igmp;
struct igmpmsg *msg;
int ret;
#ifdef CONFIG_IP_PIMSM
if (assert == IGMPMSG_WHOLEPKT)
skb = skb_realloc_headroom(pkt, sizeof(struct iphdr));
else
#endif
skb = alloc_skb(128, GFP_ATOMIC);
return -ENOBUFS;
#ifdef CONFIG_IP_PIMSM
if (assert == IGMPMSG_WHOLEPKT) {
/* Ugly, but we have no choice with this interface.
Duplicate old header, fix ihl, length etc.
And all this only to mangle msg->im_msgtype and
to set msg->im_mbz to "mbz" :-)
*/

Arnaldo Carvalho de Melo
committed
skb_push(skb, sizeof(struct iphdr));
skb_reset_network_header(skb);
skb_reset_transport_header(skb);
msg = (struct igmpmsg *)skb_network_header(skb);
memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr));
msg->im_msgtype = IGMPMSG_WHOLEPKT;
msg->im_mbz = 0;
ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
sizeof(struct iphdr));
skb->network_header = skb->tail;
skb_put(skb, ihl);
skb_copy_to_linear_data(skb, pkt->data, ihl);
ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */
msg = (struct igmpmsg *)skb_network_header(skb);
msg->im_vif = vifi;
skb->dst = dst_clone(pkt->dst);
/*
* Add our header
*/
igmp=(struct igmphdr *)skb_put(skb,sizeof(struct igmphdr));
igmp->type =
msg->im_msgtype = assert;
igmp->code = 0;
ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */

Arnaldo Carvalho de Melo
committed
skb->transport_header = skb->network_header;
if (mroute_socket == NULL) {
kfree_skb(skb);
return -EINVAL;
}
/*
* Deliver to mrouted
*/
if ((ret=sock_queue_rcv_skb(mroute_socket,skb))<0) {
if (net_ratelimit())
printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n");
kfree_skb(skb);
}
return ret;
}
/*
* Queue a packet for resolution. It gets locked cache entry!
*/
static int
ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb)
{
int err;
struct mfc_cache *c;
const struct iphdr *iph = ip_hdr(skb);
spin_lock_bh(&mfc_unres_lock);
for (c=mfc_unres_queue; c; c=c->next) {
if (c->mfc_mcastgrp == iph->daddr &&
c->mfc_origin == iph->saddr)
break;
}
if (c == NULL) {
/*
* Create a new entry if allowable
*/
if (atomic_read(&cache_resolve_queue_len)>=10 ||
(c=ipmr_cache_alloc_unres())==NULL) {
spin_unlock_bh(&mfc_unres_lock);
kfree_skb(skb);
return -ENOBUFS;
}
/*
* Fill in the new cache entry
*/
c->mfc_parent = -1;
c->mfc_origin = iph->saddr;
c->mfc_mcastgrp = iph->daddr;
/*
* Reflect first query at mrouted.
*/
if ((err = ipmr_cache_report(skb, vifi, IGMPMSG_NOCACHE))<0) {
/* If the report failed throw the cache entry
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
out - Brad Parker
*/
spin_unlock_bh(&mfc_unres_lock);
kmem_cache_free(mrt_cachep, c);
kfree_skb(skb);
return err;
}
atomic_inc(&cache_resolve_queue_len);
c->next = mfc_unres_queue;
mfc_unres_queue = c;
mod_timer(&ipmr_expire_timer, c->mfc_un.unres.expires);
}
/*
* See if we can append the packet
*/
if (c->mfc_un.unres.unresolved.qlen>3) {
kfree_skb(skb);
err = -ENOBUFS;
} else {
skb_queue_tail(&c->mfc_un.unres.unresolved,skb);
err = 0;
}
spin_unlock_bh(&mfc_unres_lock);
return err;
}
/*
* MFC cache manipulation by user space mroute daemon
*/
static int ipmr_mfc_delete(struct mfcctl *mfc)
{
int line;
struct mfc_cache *c, **cp;
line=MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
for (cp=&mfc_cache_array[line]; (c=*cp) != NULL; cp = &c->next) {
if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
write_lock_bh(&mrt_lock);
*cp = c->next;
write_unlock_bh(&mrt_lock);
kmem_cache_free(mrt_cachep, c);
return 0;
}
}
return -ENOENT;
}
static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock)
{
int line;
struct mfc_cache *uc, *c, **cp;
line=MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
for (cp=&mfc_cache_array[line]; (c=*cp) != NULL; cp = &c->next) {
if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr)
break;
}
if (c != NULL) {
write_lock_bh(&mrt_lock);
c->mfc_parent = mfc->mfcc_parent;
ipmr_update_thresholds(c, mfc->mfcc_ttls);
if (!mrtsock)
c->mfc_flags |= MFC_STATIC;
write_unlock_bh(&mrt_lock);
return 0;
}
if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
return -EINVAL;
c=ipmr_cache_alloc();
if (c==NULL)
return -ENOMEM;
c->mfc_origin=mfc->mfcc_origin.s_addr;
c->mfc_mcastgrp=mfc->mfcc_mcastgrp.s_addr;
c->mfc_parent=mfc->mfcc_parent;
ipmr_update_thresholds(c, mfc->mfcc_ttls);
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
if (!mrtsock)
c->mfc_flags |= MFC_STATIC;
write_lock_bh(&mrt_lock);
c->next = mfc_cache_array[line];
mfc_cache_array[line] = c;
write_unlock_bh(&mrt_lock);
/*
* Check to see if we resolved a queued list. If so we
* need to send on the frames and tidy up.
*/
spin_lock_bh(&mfc_unres_lock);
for (cp = &mfc_unres_queue; (uc=*cp) != NULL;
cp = &uc->next) {
if (uc->mfc_origin == c->mfc_origin &&
uc->mfc_mcastgrp == c->mfc_mcastgrp) {
*cp = uc->next;
if (atomic_dec_and_test(&cache_resolve_queue_len))
del_timer(&ipmr_expire_timer);
break;
}
}
spin_unlock_bh(&mfc_unres_lock);
if (uc) {
ipmr_cache_resolve(uc, c);
kmem_cache_free(mrt_cachep, uc);
}
return 0;
}
/*
* Close the multicast socket, and clear the vif tables etc
*/
static void mroute_clean_tables(struct sock *sk)
{
int i;
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
if (!(vif_table[i].flags&VIFF_STATIC))
vif_delete(i);
}
/*
* Wipe the cache
*/
for (i=0;i<MFC_LINES;i++) {
struct mfc_cache *c, **cp;
cp = &mfc_cache_array[i];
while ((c = *cp) != NULL) {
if (c->mfc_flags&MFC_STATIC) {
cp = &c->next;
continue;
}
write_lock_bh(&mrt_lock);
*cp = c->next;
write_unlock_bh(&mrt_lock);
kmem_cache_free(mrt_cachep, c);
}
}
if (atomic_read(&cache_resolve_queue_len) != 0) {
struct mfc_cache *c;
spin_lock_bh(&mfc_unres_lock);
while (mfc_unres_queue != NULL) {
c = mfc_unres_queue;
mfc_unres_queue = c->next;
spin_unlock_bh(&mfc_unres_lock);
ipmr_destroy_unres(c);
spin_lock_bh(&mfc_unres_lock);
}
spin_unlock_bh(&mfc_unres_lock);
}
}
static void mrtsock_destruct(struct sock *sk)
{
rtnl_lock();
if (sk == mroute_socket) {
IPV4_DEVCONF_ALL(sk->sk_net, MC_FORWARDING)--;
write_lock_bh(&mrt_lock);
mroute_socket=NULL;
write_unlock_bh(&mrt_lock);
mroute_clean_tables(sk);
}
rtnl_unlock();
}
/*
* Socket options and virtual interface manipulation. The whole
* virtual interface system is a complete heap, but unfortunately
* that's how BSD mrouted happens to think. Maybe one day with a proper
* MOSPF/PIM router set up we can clean this up.
*/
int ip_mroute_setsockopt(struct sock *sk,int optname,char __user *optval,int optlen)
{
int ret;
struct vifctl vif;
struct mfcctl mfc;
if (optname != MRT_INIT) {
if (sk != mroute_socket && !capable(CAP_NET_ADMIN))
switch (optname) {
case MRT_INIT:
if (sk->sk_type != SOCK_RAW ||
inet_sk(sk)->num != IPPROTO_IGMP)
return -EOPNOTSUPP;
if (optlen!=sizeof(int))
return -ENOPROTOOPT;
return -EADDRINUSE;
}
ret = ip_ra_control(sk, 1, mrtsock_destruct);
if (ret == 0) {
write_lock_bh(&mrt_lock);
mroute_socket=sk;
write_unlock_bh(&mrt_lock);
IPV4_DEVCONF_ALL(sk->sk_net, MC_FORWARDING)++;
}
rtnl_unlock();
return ret;
case MRT_DONE:
if (sk!=mroute_socket)
return -EACCES;
return ip_ra_control(sk, 0, NULL);
case MRT_ADD_VIF:
case MRT_DEL_VIF:
if (optlen!=sizeof(vif))
return -EINVAL;
if (copy_from_user(&vif,optval,sizeof(vif)))
return -EFAULT;
if (vif.vifc_vifi >= MAXVIFS)
return -ENFILE;
rtnl_lock();
if (optname==MRT_ADD_VIF) {
ret = vif_add(&vif, sk==mroute_socket);
} else {
ret = vif_delete(vif.vifc_vifi);
}
rtnl_unlock();
return ret;
/*
* Manipulate the forwarding caches. These live
* in a sort of kernel/user symbiosis.
*/
case MRT_ADD_MFC:
case MRT_DEL_MFC:
if (optlen!=sizeof(mfc))
return -EINVAL;
if (copy_from_user(&mfc,optval, sizeof(mfc)))
return -EFAULT;
rtnl_lock();
if (optname==MRT_DEL_MFC)
ret = ipmr_mfc_delete(&mfc);
else
ret = ipmr_mfc_add(&mfc, sk==mroute_socket);
rtnl_unlock();
return ret;
case MRT_ASSERT:
{
int v;
if (get_user(v,(int __user *)optval))
return -EFAULT;
mroute_do_assert=(v)?1:0;
return 0;
}
if (get_user(v,(int __user *)optval))
return -EFAULT;
rtnl_lock();
ret = 0;
if (v != mroute_do_pim) {
mroute_do_pim = v;
mroute_do_assert = v;
if (mroute_do_pim)
ret = inet_add_protocol(&pim_protocol,
IPPROTO_PIM);
else
ret = inet_del_protocol(&pim_protocol,
IPPROTO_PIM);
if (ret < 0)
ret = -EAGAIN;
/*
* Spurious command, or MRT_VERSION which you cannot
* set.
*/
default:
return -ENOPROTOOPT;
}
}
/*
* Getsock opt support for the multicast routing system.
*/
int ip_mroute_getsockopt(struct sock *sk,int optname,char __user *optval,int __user *optlen)
{
int olr;
int val;