11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * IP multicast routing support for mrouted 3.6/3.8 31da177e4SLinus Torvalds * 4113aa838SAlan Cox * (c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk> 51da177e4SLinus Torvalds * Linux Consultancy and Custom Driver Development 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 81da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 91da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 101da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * Fixes: 131da177e4SLinus Torvalds * Michael Chastain : Incorrect size of copying. 141da177e4SLinus Torvalds * Alan Cox : Added the cache manager code 151da177e4SLinus Torvalds * Alan Cox : Fixed the clone/copy bug and device race. 161da177e4SLinus Torvalds * Mike McLagan : Routing by source 171da177e4SLinus Torvalds * Malcolm Beattie : Buffer handling fixes. 181da177e4SLinus Torvalds * Alexey Kuznetsov : Double buffer free and other fixes. 191da177e4SLinus Torvalds * SVR Anand : Fixed several multicast bugs and problems. 201da177e4SLinus Torvalds * Alexey Kuznetsov : Status, optimisations and more. 211da177e4SLinus Torvalds * Brad Parker : Better behaviour on mrouted upcall 221da177e4SLinus Torvalds * overflow. 231da177e4SLinus Torvalds * Carlos Picoto : PIMv1 Support 241da177e4SLinus Torvalds * Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header 25f77f13e2SGilles Espinasse * Relax this requirement to work with older peers. 261da177e4SLinus Torvalds * 271da177e4SLinus Torvalds */ 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds #include <asm/uaccess.h> 301da177e4SLinus Torvalds #include <linux/types.h> 314fc268d2SRandy Dunlap #include <linux/capability.h> 321da177e4SLinus Torvalds #include <linux/errno.h> 331da177e4SLinus Torvalds #include <linux/timer.h> 341da177e4SLinus Torvalds #include <linux/mm.h> 351da177e4SLinus Torvalds #include <linux/kernel.h> 361da177e4SLinus Torvalds #include <linux/fcntl.h> 371da177e4SLinus Torvalds #include <linux/stat.h> 381da177e4SLinus Torvalds #include <linux/socket.h> 391da177e4SLinus Torvalds #include <linux/in.h> 401da177e4SLinus Torvalds #include <linux/inet.h> 411da177e4SLinus Torvalds #include <linux/netdevice.h> 421da177e4SLinus Torvalds #include <linux/inetdevice.h> 431da177e4SLinus Torvalds #include <linux/igmp.h> 441da177e4SLinus Torvalds #include <linux/proc_fs.h> 451da177e4SLinus Torvalds #include <linux/seq_file.h> 461da177e4SLinus Torvalds #include <linux/mroute.h> 471da177e4SLinus Torvalds #include <linux/init.h> 4846f25dffSKris Katterjohn #include <linux/if_ether.h> 495a0e3ad6STejun Heo #include <linux/slab.h> 50457c4cbcSEric W. Biederman #include <net/net_namespace.h> 511da177e4SLinus Torvalds #include <net/ip.h> 521da177e4SLinus Torvalds #include <net/protocol.h> 531da177e4SLinus Torvalds #include <linux/skbuff.h> 5414c85021SArnaldo Carvalho de Melo #include <net/route.h> 551da177e4SLinus Torvalds #include <net/sock.h> 561da177e4SLinus Torvalds #include <net/icmp.h> 571da177e4SLinus Torvalds #include <net/udp.h> 581da177e4SLinus Torvalds #include <net/raw.h> 591da177e4SLinus Torvalds #include <linux/notifier.h> 601da177e4SLinus Torvalds #include <linux/if_arp.h> 611da177e4SLinus Torvalds #include <linux/netfilter_ipv4.h> 62709b46e8SEric W. Biederman #include <linux/compat.h> 63bc3b2d7fSPaul Gortmaker #include <linux/export.h> 64c5441932SPravin B Shelar #include <net/ip_tunnels.h> 651da177e4SLinus Torvalds #include <net/checksum.h> 66dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 67f0ad0860SPatrick McHardy #include <net/fib_rules.h> 68d67b8c61SNicolas Dichtel #include <linux/netconf.h> 69ccbb0aa6SNikolay Aleksandrov #include <net/nexthop.h> 701da177e4SLinus Torvalds 71f0ad0860SPatrick McHardy struct ipmr_rule { 72f0ad0860SPatrick McHardy struct fib_rule common; 73f0ad0860SPatrick McHardy }; 74f0ad0860SPatrick McHardy 75f0ad0860SPatrick McHardy struct ipmr_result { 76f0ad0860SPatrick McHardy struct mr_table *mrt; 77f0ad0860SPatrick McHardy }; 78f0ad0860SPatrick McHardy 791da177e4SLinus Torvalds /* Big lock, protecting vif table, mrt cache and mroute socket state. 80a8cb16ddSEric Dumazet * Note that the changes are semaphored via rtnl_lock. 811da177e4SLinus Torvalds */ 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds static DEFINE_RWLOCK(mrt_lock); 841da177e4SLinus Torvalds 857ef8f65dSNikolay Aleksandrov /* Multicast router control variables */ 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds /* Special spinlock for queue of unresolved entries */ 881da177e4SLinus Torvalds static DEFINE_SPINLOCK(mfc_unres_lock); 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds /* We return to original Alan's scheme. Hash table of resolved 91a8cb16ddSEric Dumazet * entries is changed only in process context and protected 92a8cb16ddSEric Dumazet * with weak lock mrt_lock. Queue of unresolved entries is protected 93a8cb16ddSEric Dumazet * with strong spinlock mfc_unres_lock. 94a8cb16ddSEric Dumazet * 95a8cb16ddSEric Dumazet * In this case data path is free of exclusive locks at all. 961da177e4SLinus Torvalds */ 971da177e4SLinus Torvalds 98e18b890bSChristoph Lameter static struct kmem_cache *mrt_cachep __read_mostly; 991da177e4SLinus Torvalds 100f0ad0860SPatrick McHardy static struct mr_table *ipmr_new_table(struct net *net, u32 id); 101acbb219dSFrancesco Ruggeri static void ipmr_free_table(struct mr_table *mrt); 102acbb219dSFrancesco Ruggeri 103c4854ec8SRami Rosen static void ip_mr_forward(struct net *net, struct mr_table *mrt, 1040c12295aSPatrick McHardy struct sk_buff *skb, struct mfc_cache *cache, 1050c12295aSPatrick McHardy int local); 1060c12295aSPatrick McHardy static int ipmr_cache_report(struct mr_table *mrt, 1074feb88e5SBenjamin Thery struct sk_buff *pkt, vifi_t vifi, int assert); 108cb6a4e46SPatrick McHardy static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 109d658f8a0SPatrick McHardy struct mfc_cache *c, struct rtmsg *rtm); 1108cd3ac9fSNicolas Dichtel static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, 1118cd3ac9fSNicolas Dichtel int cmd); 1120e615e96SNikolay Aleksandrov static void mroute_clean_tables(struct mr_table *mrt, bool all); 113f0ad0860SPatrick McHardy static void ipmr_expire_process(unsigned long arg); 1141da177e4SLinus Torvalds 115f0ad0860SPatrick McHardy #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 116f0ad0860SPatrick McHardy #define ipmr_for_each_table(mrt, net) \ 117f0ad0860SPatrick McHardy list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list) 118f0ad0860SPatrick McHardy 119f0ad0860SPatrick McHardy static struct mr_table *ipmr_get_table(struct net *net, u32 id) 120f0ad0860SPatrick McHardy { 121f0ad0860SPatrick McHardy struct mr_table *mrt; 122f0ad0860SPatrick McHardy 123f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 124f0ad0860SPatrick McHardy if (mrt->id == id) 125f0ad0860SPatrick McHardy return mrt; 126f0ad0860SPatrick McHardy } 127f0ad0860SPatrick McHardy return NULL; 128f0ad0860SPatrick McHardy } 129f0ad0860SPatrick McHardy 130da91981bSDavid S. Miller static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 131f0ad0860SPatrick McHardy struct mr_table **mrt) 132f0ad0860SPatrick McHardy { 133f0ad0860SPatrick McHardy int err; 13495f4a45dSHannes Frederic Sowa struct ipmr_result res; 13595f4a45dSHannes Frederic Sowa struct fib_lookup_arg arg = { 13695f4a45dSHannes Frederic Sowa .result = &res, 13795f4a45dSHannes Frederic Sowa .flags = FIB_LOOKUP_NOREF, 13895f4a45dSHannes Frederic Sowa }; 139f0ad0860SPatrick McHardy 140*e58e4159SDavid Ahern /* update flow if oif or iif point to device enslaved to l3mdev */ 141*e58e4159SDavid Ahern l3mdev_update_flow(net, flowi4_to_flowi(flp4)); 142*e58e4159SDavid Ahern 143da91981bSDavid S. Miller err = fib_rules_lookup(net->ipv4.mr_rules_ops, 144da91981bSDavid S. Miller flowi4_to_flowi(flp4), 0, &arg); 145f0ad0860SPatrick McHardy if (err < 0) 146f0ad0860SPatrick McHardy return err; 147f0ad0860SPatrick McHardy *mrt = res.mrt; 148f0ad0860SPatrick McHardy return 0; 149f0ad0860SPatrick McHardy } 150f0ad0860SPatrick McHardy 151f0ad0860SPatrick McHardy static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp, 152f0ad0860SPatrick McHardy int flags, struct fib_lookup_arg *arg) 153f0ad0860SPatrick McHardy { 154f0ad0860SPatrick McHardy struct ipmr_result *res = arg->result; 155f0ad0860SPatrick McHardy struct mr_table *mrt; 156f0ad0860SPatrick McHardy 157f0ad0860SPatrick McHardy switch (rule->action) { 158f0ad0860SPatrick McHardy case FR_ACT_TO_TBL: 159f0ad0860SPatrick McHardy break; 160f0ad0860SPatrick McHardy case FR_ACT_UNREACHABLE: 161f0ad0860SPatrick McHardy return -ENETUNREACH; 162f0ad0860SPatrick McHardy case FR_ACT_PROHIBIT: 163f0ad0860SPatrick McHardy return -EACCES; 164f0ad0860SPatrick McHardy case FR_ACT_BLACKHOLE: 165f0ad0860SPatrick McHardy default: 166f0ad0860SPatrick McHardy return -EINVAL; 167f0ad0860SPatrick McHardy } 168f0ad0860SPatrick McHardy 169*e58e4159SDavid Ahern arg->table = fib_rule_get_table(rule, arg); 170*e58e4159SDavid Ahern 171*e58e4159SDavid Ahern mrt = ipmr_get_table(rule->fr_net, arg->table); 17251456b29SIan Morris if (!mrt) 173f0ad0860SPatrick McHardy return -EAGAIN; 174f0ad0860SPatrick McHardy res->mrt = mrt; 175f0ad0860SPatrick McHardy return 0; 176f0ad0860SPatrick McHardy } 177f0ad0860SPatrick McHardy 178f0ad0860SPatrick McHardy static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) 179f0ad0860SPatrick McHardy { 180f0ad0860SPatrick McHardy return 1; 181f0ad0860SPatrick McHardy } 182f0ad0860SPatrick McHardy 183f0ad0860SPatrick McHardy static const struct nla_policy ipmr_rule_policy[FRA_MAX + 1] = { 184f0ad0860SPatrick McHardy FRA_GENERIC_POLICY, 185f0ad0860SPatrick McHardy }; 186f0ad0860SPatrick McHardy 187f0ad0860SPatrick McHardy static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb, 188f0ad0860SPatrick McHardy struct fib_rule_hdr *frh, struct nlattr **tb) 189f0ad0860SPatrick McHardy { 190f0ad0860SPatrick McHardy return 0; 191f0ad0860SPatrick McHardy } 192f0ad0860SPatrick McHardy 193f0ad0860SPatrick McHardy static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 194f0ad0860SPatrick McHardy struct nlattr **tb) 195f0ad0860SPatrick McHardy { 196f0ad0860SPatrick McHardy return 1; 197f0ad0860SPatrick McHardy } 198f0ad0860SPatrick McHardy 199f0ad0860SPatrick McHardy static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb, 200f0ad0860SPatrick McHardy struct fib_rule_hdr *frh) 201f0ad0860SPatrick McHardy { 202f0ad0860SPatrick McHardy frh->dst_len = 0; 203f0ad0860SPatrick McHardy frh->src_len = 0; 204f0ad0860SPatrick McHardy frh->tos = 0; 205f0ad0860SPatrick McHardy return 0; 206f0ad0860SPatrick McHardy } 207f0ad0860SPatrick McHardy 20804a6f82cSAndi Kleen static const struct fib_rules_ops __net_initconst ipmr_rules_ops_template = { 20925239ceeSPatrick McHardy .family = RTNL_FAMILY_IPMR, 210f0ad0860SPatrick McHardy .rule_size = sizeof(struct ipmr_rule), 211f0ad0860SPatrick McHardy .addr_size = sizeof(u32), 212f0ad0860SPatrick McHardy .action = ipmr_rule_action, 213f0ad0860SPatrick McHardy .match = ipmr_rule_match, 214f0ad0860SPatrick McHardy .configure = ipmr_rule_configure, 215f0ad0860SPatrick McHardy .compare = ipmr_rule_compare, 216f0ad0860SPatrick McHardy .fill = ipmr_rule_fill, 217f0ad0860SPatrick McHardy .nlgroup = RTNLGRP_IPV4_RULE, 218f0ad0860SPatrick McHardy .policy = ipmr_rule_policy, 219f0ad0860SPatrick McHardy .owner = THIS_MODULE, 220f0ad0860SPatrick McHardy }; 221f0ad0860SPatrick McHardy 222f0ad0860SPatrick McHardy static int __net_init ipmr_rules_init(struct net *net) 223f0ad0860SPatrick McHardy { 224f0ad0860SPatrick McHardy struct fib_rules_ops *ops; 225f0ad0860SPatrick McHardy struct mr_table *mrt; 226f0ad0860SPatrick McHardy int err; 227f0ad0860SPatrick McHardy 228f0ad0860SPatrick McHardy ops = fib_rules_register(&ipmr_rules_ops_template, net); 229f0ad0860SPatrick McHardy if (IS_ERR(ops)) 230f0ad0860SPatrick McHardy return PTR_ERR(ops); 231f0ad0860SPatrick McHardy 232f0ad0860SPatrick McHardy INIT_LIST_HEAD(&net->ipv4.mr_tables); 233f0ad0860SPatrick McHardy 234f0ad0860SPatrick McHardy mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 2351113ebbcSNikolay Aleksandrov if (IS_ERR(mrt)) { 2361113ebbcSNikolay Aleksandrov err = PTR_ERR(mrt); 237f0ad0860SPatrick McHardy goto err1; 238f0ad0860SPatrick McHardy } 239f0ad0860SPatrick McHardy 240f0ad0860SPatrick McHardy err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0); 241f0ad0860SPatrick McHardy if (err < 0) 242f0ad0860SPatrick McHardy goto err2; 243f0ad0860SPatrick McHardy 244f0ad0860SPatrick McHardy net->ipv4.mr_rules_ops = ops; 245f0ad0860SPatrick McHardy return 0; 246f0ad0860SPatrick McHardy 247f0ad0860SPatrick McHardy err2: 248f243e5a7SWANG Cong ipmr_free_table(mrt); 249f0ad0860SPatrick McHardy err1: 250f0ad0860SPatrick McHardy fib_rules_unregister(ops); 251f0ad0860SPatrick McHardy return err; 252f0ad0860SPatrick McHardy } 253f0ad0860SPatrick McHardy 254f0ad0860SPatrick McHardy static void __net_exit ipmr_rules_exit(struct net *net) 255f0ad0860SPatrick McHardy { 256f0ad0860SPatrick McHardy struct mr_table *mrt, *next; 257f0ad0860SPatrick McHardy 258ed785309SWANG Cong rtnl_lock(); 259035320d5SEric Dumazet list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) { 260035320d5SEric Dumazet list_del(&mrt->list); 261acbb219dSFrancesco Ruggeri ipmr_free_table(mrt); 262035320d5SEric Dumazet } 263f0ad0860SPatrick McHardy fib_rules_unregister(net->ipv4.mr_rules_ops); 264419df12fSWANG Cong rtnl_unlock(); 265f0ad0860SPatrick McHardy } 266f0ad0860SPatrick McHardy #else 267f0ad0860SPatrick McHardy #define ipmr_for_each_table(mrt, net) \ 268f0ad0860SPatrick McHardy for (mrt = net->ipv4.mrt; mrt; mrt = NULL) 269f0ad0860SPatrick McHardy 270f0ad0860SPatrick McHardy static struct mr_table *ipmr_get_table(struct net *net, u32 id) 271f0ad0860SPatrick McHardy { 272f0ad0860SPatrick McHardy return net->ipv4.mrt; 273f0ad0860SPatrick McHardy } 274f0ad0860SPatrick McHardy 275da91981bSDavid S. Miller static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 276f0ad0860SPatrick McHardy struct mr_table **mrt) 277f0ad0860SPatrick McHardy { 278f0ad0860SPatrick McHardy *mrt = net->ipv4.mrt; 279f0ad0860SPatrick McHardy return 0; 280f0ad0860SPatrick McHardy } 281f0ad0860SPatrick McHardy 282f0ad0860SPatrick McHardy static int __net_init ipmr_rules_init(struct net *net) 283f0ad0860SPatrick McHardy { 2841113ebbcSNikolay Aleksandrov struct mr_table *mrt; 2851113ebbcSNikolay Aleksandrov 2861113ebbcSNikolay Aleksandrov mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 2871113ebbcSNikolay Aleksandrov if (IS_ERR(mrt)) 2881113ebbcSNikolay Aleksandrov return PTR_ERR(mrt); 2891113ebbcSNikolay Aleksandrov net->ipv4.mrt = mrt; 2901113ebbcSNikolay Aleksandrov return 0; 291f0ad0860SPatrick McHardy } 292f0ad0860SPatrick McHardy 293f0ad0860SPatrick McHardy static void __net_exit ipmr_rules_exit(struct net *net) 294f0ad0860SPatrick McHardy { 295ed785309SWANG Cong rtnl_lock(); 296acbb219dSFrancesco Ruggeri ipmr_free_table(net->ipv4.mrt); 297ed785309SWANG Cong net->ipv4.mrt = NULL; 298ed785309SWANG Cong rtnl_unlock(); 299f0ad0860SPatrick McHardy } 300f0ad0860SPatrick McHardy #endif 301f0ad0860SPatrick McHardy 302f0ad0860SPatrick McHardy static struct mr_table *ipmr_new_table(struct net *net, u32 id) 303f0ad0860SPatrick McHardy { 304f0ad0860SPatrick McHardy struct mr_table *mrt; 305f0ad0860SPatrick McHardy unsigned int i; 306f0ad0860SPatrick McHardy 3071113ebbcSNikolay Aleksandrov /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */ 3081113ebbcSNikolay Aleksandrov if (id != RT_TABLE_DEFAULT && id >= 1000000000) 3091113ebbcSNikolay Aleksandrov return ERR_PTR(-EINVAL); 3101113ebbcSNikolay Aleksandrov 311f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, id); 31200db4124SIan Morris if (mrt) 313f0ad0860SPatrick McHardy return mrt; 314f0ad0860SPatrick McHardy 315f0ad0860SPatrick McHardy mrt = kzalloc(sizeof(*mrt), GFP_KERNEL); 31651456b29SIan Morris if (!mrt) 3171113ebbcSNikolay Aleksandrov return ERR_PTR(-ENOMEM); 3188de53dfbSPatrick McHardy write_pnet(&mrt->net, net); 319f0ad0860SPatrick McHardy mrt->id = id; 320f0ad0860SPatrick McHardy 321f0ad0860SPatrick McHardy /* Forwarding cache */ 322f0ad0860SPatrick McHardy for (i = 0; i < MFC_LINES; i++) 323f0ad0860SPatrick McHardy INIT_LIST_HEAD(&mrt->mfc_cache_array[i]); 324f0ad0860SPatrick McHardy 325f0ad0860SPatrick McHardy INIT_LIST_HEAD(&mrt->mfc_unres_queue); 326f0ad0860SPatrick McHardy 327f0ad0860SPatrick McHardy setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process, 328f0ad0860SPatrick McHardy (unsigned long)mrt); 329f0ad0860SPatrick McHardy 330f0ad0860SPatrick McHardy mrt->mroute_reg_vif_num = -1; 331f0ad0860SPatrick McHardy #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 332f0ad0860SPatrick McHardy list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables); 333f0ad0860SPatrick McHardy #endif 334f0ad0860SPatrick McHardy return mrt; 335f0ad0860SPatrick McHardy } 3361da177e4SLinus Torvalds 337acbb219dSFrancesco Ruggeri static void ipmr_free_table(struct mr_table *mrt) 338acbb219dSFrancesco Ruggeri { 339acbb219dSFrancesco Ruggeri del_timer_sync(&mrt->ipmr_expire_timer); 3400e615e96SNikolay Aleksandrov mroute_clean_tables(mrt, true); 341acbb219dSFrancesco Ruggeri kfree(mrt); 342acbb219dSFrancesco Ruggeri } 343acbb219dSFrancesco Ruggeri 3441da177e4SLinus Torvalds /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */ 3451da177e4SLinus Torvalds 346d607032dSWang Chen static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v) 347d607032dSWang Chen { 3484feb88e5SBenjamin Thery struct net *net = dev_net(dev); 3494feb88e5SBenjamin Thery 350d607032dSWang Chen dev_close(dev); 351d607032dSWang Chen 3524feb88e5SBenjamin Thery dev = __dev_get_by_name(net, "tunl0"); 353d607032dSWang Chen if (dev) { 3545bc3eb7eSStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 355d607032dSWang Chen struct ifreq ifr; 356d607032dSWang Chen struct ip_tunnel_parm p; 357d607032dSWang Chen 358d607032dSWang Chen memset(&p, 0, sizeof(p)); 359d607032dSWang Chen p.iph.daddr = v->vifc_rmt_addr.s_addr; 360d607032dSWang Chen p.iph.saddr = v->vifc_lcl_addr.s_addr; 361d607032dSWang Chen p.iph.version = 4; 362d607032dSWang Chen p.iph.ihl = 5; 363d607032dSWang Chen p.iph.protocol = IPPROTO_IPIP; 364d607032dSWang Chen sprintf(p.name, "dvmrp%d", v->vifc_vifi); 365d607032dSWang Chen ifr.ifr_ifru.ifru_data = (__force void __user *)&p; 366d607032dSWang Chen 3675bc3eb7eSStephen Hemminger if (ops->ndo_do_ioctl) { 3685bc3eb7eSStephen Hemminger mm_segment_t oldfs = get_fs(); 3695bc3eb7eSStephen Hemminger 3705bc3eb7eSStephen Hemminger set_fs(KERNEL_DS); 3715bc3eb7eSStephen Hemminger ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL); 372d607032dSWang Chen set_fs(oldfs); 373d607032dSWang Chen } 374d607032dSWang Chen } 3755bc3eb7eSStephen Hemminger } 376d607032dSWang Chen 377a0b47736SNikolay Aleksandrov /* Initialize ipmr pimreg/tunnel in_device */ 378a0b47736SNikolay Aleksandrov static bool ipmr_init_vif_indev(const struct net_device *dev) 379a0b47736SNikolay Aleksandrov { 380a0b47736SNikolay Aleksandrov struct in_device *in_dev; 381a0b47736SNikolay Aleksandrov 382a0b47736SNikolay Aleksandrov ASSERT_RTNL(); 383a0b47736SNikolay Aleksandrov 384a0b47736SNikolay Aleksandrov in_dev = __in_dev_get_rtnl(dev); 385a0b47736SNikolay Aleksandrov if (!in_dev) 386a0b47736SNikolay Aleksandrov return false; 387a0b47736SNikolay Aleksandrov ipv4_devconf_setall(in_dev); 388a0b47736SNikolay Aleksandrov neigh_parms_data_state_setall(in_dev->arp_parms); 389a0b47736SNikolay Aleksandrov IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; 390a0b47736SNikolay Aleksandrov 391a0b47736SNikolay Aleksandrov return true; 392a0b47736SNikolay Aleksandrov } 393a0b47736SNikolay Aleksandrov 3947ef8f65dSNikolay Aleksandrov static struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v) 3951da177e4SLinus Torvalds { 3961da177e4SLinus Torvalds struct net_device *dev; 3971da177e4SLinus Torvalds 3984feb88e5SBenjamin Thery dev = __dev_get_by_name(net, "tunl0"); 3991da177e4SLinus Torvalds 4001da177e4SLinus Torvalds if (dev) { 4015bc3eb7eSStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 4021da177e4SLinus Torvalds int err; 4031da177e4SLinus Torvalds struct ifreq ifr; 4041da177e4SLinus Torvalds struct ip_tunnel_parm p; 4051da177e4SLinus Torvalds 4061da177e4SLinus Torvalds memset(&p, 0, sizeof(p)); 4071da177e4SLinus Torvalds p.iph.daddr = v->vifc_rmt_addr.s_addr; 4081da177e4SLinus Torvalds p.iph.saddr = v->vifc_lcl_addr.s_addr; 4091da177e4SLinus Torvalds p.iph.version = 4; 4101da177e4SLinus Torvalds p.iph.ihl = 5; 4111da177e4SLinus Torvalds p.iph.protocol = IPPROTO_IPIP; 4121da177e4SLinus Torvalds sprintf(p.name, "dvmrp%d", v->vifc_vifi); 413ba93ef74SStephen Hemminger ifr.ifr_ifru.ifru_data = (__force void __user *)&p; 4141da177e4SLinus Torvalds 4155bc3eb7eSStephen Hemminger if (ops->ndo_do_ioctl) { 4165bc3eb7eSStephen Hemminger mm_segment_t oldfs = get_fs(); 4175bc3eb7eSStephen Hemminger 4185bc3eb7eSStephen Hemminger set_fs(KERNEL_DS); 4195bc3eb7eSStephen Hemminger err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL); 4201da177e4SLinus Torvalds set_fs(oldfs); 421a8cb16ddSEric Dumazet } else { 4225bc3eb7eSStephen Hemminger err = -EOPNOTSUPP; 423a8cb16ddSEric Dumazet } 4241da177e4SLinus Torvalds dev = NULL; 4251da177e4SLinus Torvalds 4264feb88e5SBenjamin Thery if (err == 0 && 4274feb88e5SBenjamin Thery (dev = __dev_get_by_name(net, p.name)) != NULL) { 4281da177e4SLinus Torvalds dev->flags |= IFF_MULTICAST; 429a0b47736SNikolay Aleksandrov if (!ipmr_init_vif_indev(dev)) 4301da177e4SLinus Torvalds goto failure; 4311da177e4SLinus Torvalds if (dev_open(dev)) 4321da177e4SLinus Torvalds goto failure; 4337dc00c82SWang Chen dev_hold(dev); 4341da177e4SLinus Torvalds } 4351da177e4SLinus Torvalds } 4361da177e4SLinus Torvalds return dev; 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds failure: 4391da177e4SLinus Torvalds unregister_netdevice(dev); 4401da177e4SLinus Torvalds return NULL; 4411da177e4SLinus Torvalds } 4421da177e4SLinus Torvalds 443c316c629SNikolay Aleksandrov #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) 4446fef4c0cSStephen Hemminger static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) 4451da177e4SLinus Torvalds { 4464feb88e5SBenjamin Thery struct net *net = dev_net(dev); 447f0ad0860SPatrick McHardy struct mr_table *mrt; 448da91981bSDavid S. Miller struct flowi4 fl4 = { 449da91981bSDavid S. Miller .flowi4_oif = dev->ifindex, 4506a662719SCong Wang .flowi4_iif = skb->skb_iif ? : LOOPBACK_IFINDEX, 451da91981bSDavid S. Miller .flowi4_mark = skb->mark, 452f0ad0860SPatrick McHardy }; 453f0ad0860SPatrick McHardy int err; 454f0ad0860SPatrick McHardy 455da91981bSDavid S. Miller err = ipmr_fib_lookup(net, &fl4, &mrt); 456e40dbc51SBen Greear if (err < 0) { 457e40dbc51SBen Greear kfree_skb(skb); 458f0ad0860SPatrick McHardy return err; 459e40dbc51SBen Greear } 4604feb88e5SBenjamin Thery 4611da177e4SLinus Torvalds read_lock(&mrt_lock); 462cf3677aeSPavel Emelyanov dev->stats.tx_bytes += skb->len; 463cf3677aeSPavel Emelyanov dev->stats.tx_packets++; 4640c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, IGMPMSG_WHOLEPKT); 4651da177e4SLinus Torvalds read_unlock(&mrt_lock); 4661da177e4SLinus Torvalds kfree_skb(skb); 4676ed10654SPatrick McHardy return NETDEV_TX_OK; 4681da177e4SLinus Torvalds } 4691da177e4SLinus Torvalds 470ee9b9596SNicolas Dichtel static int reg_vif_get_iflink(const struct net_device *dev) 471ee9b9596SNicolas Dichtel { 472ee9b9596SNicolas Dichtel return 0; 473ee9b9596SNicolas Dichtel } 474ee9b9596SNicolas Dichtel 475007c3838SStephen Hemminger static const struct net_device_ops reg_vif_netdev_ops = { 476007c3838SStephen Hemminger .ndo_start_xmit = reg_vif_xmit, 477ee9b9596SNicolas Dichtel .ndo_get_iflink = reg_vif_get_iflink, 478007c3838SStephen Hemminger }; 479007c3838SStephen Hemminger 4801da177e4SLinus Torvalds static void reg_vif_setup(struct net_device *dev) 4811da177e4SLinus Torvalds { 4821da177e4SLinus Torvalds dev->type = ARPHRD_PIMREG; 48346f25dffSKris Katterjohn dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8; 4841da177e4SLinus Torvalds dev->flags = IFF_NOARP; 48570cb4a45SHimangi Saraogi dev->netdev_ops = ®_vif_netdev_ops; 4861da177e4SLinus Torvalds dev->destructor = free_netdev; 487403dbb97STom Goff dev->features |= NETIF_F_NETNS_LOCAL; 4881da177e4SLinus Torvalds } 4891da177e4SLinus Torvalds 490f0ad0860SPatrick McHardy static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) 4911da177e4SLinus Torvalds { 4921da177e4SLinus Torvalds struct net_device *dev; 493f0ad0860SPatrick McHardy char name[IFNAMSIZ]; 4941da177e4SLinus Torvalds 495f0ad0860SPatrick McHardy if (mrt->id == RT_TABLE_DEFAULT) 496f0ad0860SPatrick McHardy sprintf(name, "pimreg"); 497f0ad0860SPatrick McHardy else 498f0ad0860SPatrick McHardy sprintf(name, "pimreg%u", mrt->id); 499f0ad0860SPatrick McHardy 500c835a677STom Gundersen dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup); 5011da177e4SLinus Torvalds 50251456b29SIan Morris if (!dev) 5031da177e4SLinus Torvalds return NULL; 5041da177e4SLinus Torvalds 505403dbb97STom Goff dev_net_set(dev, net); 506403dbb97STom Goff 5071da177e4SLinus Torvalds if (register_netdevice(dev)) { 5081da177e4SLinus Torvalds free_netdev(dev); 5091da177e4SLinus Torvalds return NULL; 5101da177e4SLinus Torvalds } 5111da177e4SLinus Torvalds 512a0b47736SNikolay Aleksandrov if (!ipmr_init_vif_indev(dev)) 5131da177e4SLinus Torvalds goto failure; 5141da177e4SLinus Torvalds if (dev_open(dev)) 5151da177e4SLinus Torvalds goto failure; 5161da177e4SLinus Torvalds 5177dc00c82SWang Chen dev_hold(dev); 5187dc00c82SWang Chen 5191da177e4SLinus Torvalds return dev; 5201da177e4SLinus Torvalds 5211da177e4SLinus Torvalds failure: 5221da177e4SLinus Torvalds unregister_netdevice(dev); 5231da177e4SLinus Torvalds return NULL; 5241da177e4SLinus Torvalds } 525c316c629SNikolay Aleksandrov 526c316c629SNikolay Aleksandrov /* called with rcu_read_lock() */ 527c316c629SNikolay Aleksandrov static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb, 528c316c629SNikolay Aleksandrov unsigned int pimlen) 529c316c629SNikolay Aleksandrov { 530c316c629SNikolay Aleksandrov struct net_device *reg_dev = NULL; 531c316c629SNikolay Aleksandrov struct iphdr *encap; 532c316c629SNikolay Aleksandrov 533c316c629SNikolay Aleksandrov encap = (struct iphdr *)(skb_transport_header(skb) + pimlen); 5347ef8f65dSNikolay Aleksandrov /* Check that: 535c316c629SNikolay Aleksandrov * a. packet is really sent to a multicast group 536c316c629SNikolay Aleksandrov * b. packet is not a NULL-REGISTER 537c316c629SNikolay Aleksandrov * c. packet is not truncated 538c316c629SNikolay Aleksandrov */ 539c316c629SNikolay Aleksandrov if (!ipv4_is_multicast(encap->daddr) || 540c316c629SNikolay Aleksandrov encap->tot_len == 0 || 541c316c629SNikolay Aleksandrov ntohs(encap->tot_len) + pimlen > skb->len) 542c316c629SNikolay Aleksandrov return 1; 543c316c629SNikolay Aleksandrov 544c316c629SNikolay Aleksandrov read_lock(&mrt_lock); 545c316c629SNikolay Aleksandrov if (mrt->mroute_reg_vif_num >= 0) 546c316c629SNikolay Aleksandrov reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev; 547c316c629SNikolay Aleksandrov read_unlock(&mrt_lock); 548c316c629SNikolay Aleksandrov 549c316c629SNikolay Aleksandrov if (!reg_dev) 550c316c629SNikolay Aleksandrov return 1; 551c316c629SNikolay Aleksandrov 552c316c629SNikolay Aleksandrov skb->mac_header = skb->network_header; 553c316c629SNikolay Aleksandrov skb_pull(skb, (u8 *)encap - skb->data); 554c316c629SNikolay Aleksandrov skb_reset_network_header(skb); 555c316c629SNikolay Aleksandrov skb->protocol = htons(ETH_P_IP); 556c316c629SNikolay Aleksandrov skb->ip_summed = CHECKSUM_NONE; 557c316c629SNikolay Aleksandrov 558c316c629SNikolay Aleksandrov skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev)); 559c316c629SNikolay Aleksandrov 560c316c629SNikolay Aleksandrov netif_rx(skb); 561c316c629SNikolay Aleksandrov 562c316c629SNikolay Aleksandrov return NET_RX_SUCCESS; 563c316c629SNikolay Aleksandrov } 564c316c629SNikolay Aleksandrov #else 565c316c629SNikolay Aleksandrov static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) 566c316c629SNikolay Aleksandrov { 567c316c629SNikolay Aleksandrov return NULL; 568c316c629SNikolay Aleksandrov } 5691da177e4SLinus Torvalds #endif 5701da177e4SLinus Torvalds 5712c53040fSBen Hutchings /** 5722c53040fSBen Hutchings * vif_delete - Delete a VIF entry 5737dc00c82SWang Chen * @notify: Set to 1, if the caller is a notifier_call 5741da177e4SLinus Torvalds */ 5750c12295aSPatrick McHardy static int vif_delete(struct mr_table *mrt, int vifi, int notify, 576d17fa6faSEric Dumazet struct list_head *head) 5771da177e4SLinus Torvalds { 5781da177e4SLinus Torvalds struct vif_device *v; 5791da177e4SLinus Torvalds struct net_device *dev; 5801da177e4SLinus Torvalds struct in_device *in_dev; 5811da177e4SLinus Torvalds 5820c12295aSPatrick McHardy if (vifi < 0 || vifi >= mrt->maxvif) 5831da177e4SLinus Torvalds return -EADDRNOTAVAIL; 5841da177e4SLinus Torvalds 5850c12295aSPatrick McHardy v = &mrt->vif_table[vifi]; 5861da177e4SLinus Torvalds 5871da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 5881da177e4SLinus Torvalds dev = v->dev; 5891da177e4SLinus Torvalds v->dev = NULL; 5901da177e4SLinus Torvalds 5911da177e4SLinus Torvalds if (!dev) { 5921da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 5931da177e4SLinus Torvalds return -EADDRNOTAVAIL; 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds 5960c12295aSPatrick McHardy if (vifi == mrt->mroute_reg_vif_num) 5970c12295aSPatrick McHardy mrt->mroute_reg_vif_num = -1; 5981da177e4SLinus Torvalds 5990c12295aSPatrick McHardy if (vifi + 1 == mrt->maxvif) { 6001da177e4SLinus Torvalds int tmp; 601a8cb16ddSEric Dumazet 6021da177e4SLinus Torvalds for (tmp = vifi - 1; tmp >= 0; tmp--) { 6030c12295aSPatrick McHardy if (VIF_EXISTS(mrt, tmp)) 6041da177e4SLinus Torvalds break; 6051da177e4SLinus Torvalds } 6060c12295aSPatrick McHardy mrt->maxvif = tmp+1; 6071da177e4SLinus Torvalds } 6081da177e4SLinus Torvalds 6091da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 6101da177e4SLinus Torvalds 6111da177e4SLinus Torvalds dev_set_allmulti(dev, -1); 6121da177e4SLinus Torvalds 613a8cb16ddSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 614a8cb16ddSEric Dumazet if (in_dev) { 61542f811b8SHerbert Xu IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--; 616d67b8c61SNicolas Dichtel inet_netconf_notify_devconf(dev_net(dev), 617d67b8c61SNicolas Dichtel NETCONFA_MC_FORWARDING, 618d67b8c61SNicolas Dichtel dev->ifindex, &in_dev->cnf); 6191da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 6201da177e4SLinus Torvalds } 6211da177e4SLinus Torvalds 6227dc00c82SWang Chen if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER) && !notify) 623d17fa6faSEric Dumazet unregister_netdevice_queue(dev, head); 6241da177e4SLinus Torvalds 6251da177e4SLinus Torvalds dev_put(dev); 6261da177e4SLinus Torvalds return 0; 6271da177e4SLinus Torvalds } 6281da177e4SLinus Torvalds 629a8c9486bSEric Dumazet static void ipmr_cache_free_rcu(struct rcu_head *head) 630a8c9486bSEric Dumazet { 631a8c9486bSEric Dumazet struct mfc_cache *c = container_of(head, struct mfc_cache, rcu); 632a8c9486bSEric Dumazet 633a8c9486bSEric Dumazet kmem_cache_free(mrt_cachep, c); 634a8c9486bSEric Dumazet } 635a8c9486bSEric Dumazet 6365c0a66f5SBenjamin Thery static inline void ipmr_cache_free(struct mfc_cache *c) 6375c0a66f5SBenjamin Thery { 638a8c9486bSEric Dumazet call_rcu(&c->rcu, ipmr_cache_free_rcu); 6395c0a66f5SBenjamin Thery } 6405c0a66f5SBenjamin Thery 6411da177e4SLinus Torvalds /* Destroy an unresolved cache entry, killing queued skbs 642a8cb16ddSEric Dumazet * and reporting error to netlink readers. 6431da177e4SLinus Torvalds */ 6440c12295aSPatrick McHardy static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c) 6451da177e4SLinus Torvalds { 6468de53dfbSPatrick McHardy struct net *net = read_pnet(&mrt->net); 6471da177e4SLinus Torvalds struct sk_buff *skb; 6489ef1d4c7SPatrick McHardy struct nlmsgerr *e; 6491da177e4SLinus Torvalds 6500c12295aSPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len); 6511da177e4SLinus Torvalds 6521da177e4SLinus Torvalds while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) { 653eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->version == 0) { 6541da177e4SLinus Torvalds struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr)); 6551da177e4SLinus Torvalds nlh->nlmsg_type = NLMSG_ERROR; 656573ce260SHong zhi guo nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 6571da177e4SLinus Torvalds skb_trim(skb, nlh->nlmsg_len); 658573ce260SHong zhi guo e = nlmsg_data(nlh); 6599ef1d4c7SPatrick McHardy e->error = -ETIMEDOUT; 6609ef1d4c7SPatrick McHardy memset(&e->msg, 0, sizeof(e->msg)); 6612942e900SThomas Graf 66215e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 663a8cb16ddSEric Dumazet } else { 6641da177e4SLinus Torvalds kfree_skb(skb); 6651da177e4SLinus Torvalds } 666a8cb16ddSEric Dumazet } 6671da177e4SLinus Torvalds 6685c0a66f5SBenjamin Thery ipmr_cache_free(c); 6691da177e4SLinus Torvalds } 6701da177e4SLinus Torvalds 671e258beb2SPatrick McHardy /* Timer process for the unresolved queue. */ 672e258beb2SPatrick McHardy static void ipmr_expire_process(unsigned long arg) 6731da177e4SLinus Torvalds { 6740c12295aSPatrick McHardy struct mr_table *mrt = (struct mr_table *)arg; 6751da177e4SLinus Torvalds unsigned long now; 6761da177e4SLinus Torvalds unsigned long expires; 677862465f2SPatrick McHardy struct mfc_cache *c, *next; 6781da177e4SLinus Torvalds 6791da177e4SLinus Torvalds if (!spin_trylock(&mfc_unres_lock)) { 6800c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10); 6811da177e4SLinus Torvalds return; 6821da177e4SLinus Torvalds } 6831da177e4SLinus Torvalds 6840c12295aSPatrick McHardy if (list_empty(&mrt->mfc_unres_queue)) 6851da177e4SLinus Torvalds goto out; 6861da177e4SLinus Torvalds 6871da177e4SLinus Torvalds now = jiffies; 6881da177e4SLinus Torvalds expires = 10*HZ; 6891da177e4SLinus Torvalds 6900c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) { 6911da177e4SLinus Torvalds if (time_after(c->mfc_un.unres.expires, now)) { 6921da177e4SLinus Torvalds unsigned long interval = c->mfc_un.unres.expires - now; 6931da177e4SLinus Torvalds if (interval < expires) 6941da177e4SLinus Torvalds expires = interval; 6951da177e4SLinus Torvalds continue; 6961da177e4SLinus Torvalds } 6971da177e4SLinus Torvalds 698862465f2SPatrick McHardy list_del(&c->list); 6998cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 7000c12295aSPatrick McHardy ipmr_destroy_unres(mrt, c); 7011da177e4SLinus Torvalds } 7021da177e4SLinus Torvalds 7030c12295aSPatrick McHardy if (!list_empty(&mrt->mfc_unres_queue)) 7040c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies + expires); 7051da177e4SLinus Torvalds 7061da177e4SLinus Torvalds out: 7071da177e4SLinus Torvalds spin_unlock(&mfc_unres_lock); 7081da177e4SLinus Torvalds } 7091da177e4SLinus Torvalds 7101da177e4SLinus Torvalds /* Fill oifs list. It is called under write locked mrt_lock. */ 7110c12295aSPatrick McHardy static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache, 712d658f8a0SPatrick McHardy unsigned char *ttls) 7131da177e4SLinus Torvalds { 7141da177e4SLinus Torvalds int vifi; 7151da177e4SLinus Torvalds 7161da177e4SLinus Torvalds cache->mfc_un.res.minvif = MAXVIFS; 7171da177e4SLinus Torvalds cache->mfc_un.res.maxvif = 0; 7181da177e4SLinus Torvalds memset(cache->mfc_un.res.ttls, 255, MAXVIFS); 7191da177e4SLinus Torvalds 7200c12295aSPatrick McHardy for (vifi = 0; vifi < mrt->maxvif; vifi++) { 7210c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vifi) && 722cf958ae3SBenjamin Thery ttls[vifi] && ttls[vifi] < 255) { 7231da177e4SLinus Torvalds cache->mfc_un.res.ttls[vifi] = ttls[vifi]; 7241da177e4SLinus Torvalds if (cache->mfc_un.res.minvif > vifi) 7251da177e4SLinus Torvalds cache->mfc_un.res.minvif = vifi; 7261da177e4SLinus Torvalds if (cache->mfc_un.res.maxvif <= vifi) 7271da177e4SLinus Torvalds cache->mfc_un.res.maxvif = vifi + 1; 7281da177e4SLinus Torvalds } 7291da177e4SLinus Torvalds } 73090b5ca17SNikolay Aleksandrov cache->mfc_un.res.lastuse = jiffies; 7311da177e4SLinus Torvalds } 7321da177e4SLinus Torvalds 7330c12295aSPatrick McHardy static int vif_add(struct net *net, struct mr_table *mrt, 7340c12295aSPatrick McHardy struct vifctl *vifc, int mrtsock) 7351da177e4SLinus Torvalds { 7361da177e4SLinus Torvalds int vifi = vifc->vifc_vifi; 7370c12295aSPatrick McHardy struct vif_device *v = &mrt->vif_table[vifi]; 7381da177e4SLinus Torvalds struct net_device *dev; 7391da177e4SLinus Torvalds struct in_device *in_dev; 740d607032dSWang Chen int err; 7411da177e4SLinus Torvalds 7421da177e4SLinus Torvalds /* Is vif busy ? */ 7430c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vifi)) 7441da177e4SLinus Torvalds return -EADDRINUSE; 7451da177e4SLinus Torvalds 7461da177e4SLinus Torvalds switch (vifc->vifc_flags) { 7471da177e4SLinus Torvalds case VIFF_REGISTER: 7481973a4eaSNikolay Aleksandrov if (!ipmr_pimsm_enabled()) 749c316c629SNikolay Aleksandrov return -EINVAL; 750c316c629SNikolay Aleksandrov /* Special Purpose VIF in PIM 7511da177e4SLinus Torvalds * All the packets will be sent to the daemon 7521da177e4SLinus Torvalds */ 7530c12295aSPatrick McHardy if (mrt->mroute_reg_vif_num >= 0) 7541da177e4SLinus Torvalds return -EADDRINUSE; 755f0ad0860SPatrick McHardy dev = ipmr_reg_vif(net, mrt); 7561da177e4SLinus Torvalds if (!dev) 7571da177e4SLinus Torvalds return -ENOBUFS; 758d607032dSWang Chen err = dev_set_allmulti(dev, 1); 759d607032dSWang Chen if (err) { 760d607032dSWang Chen unregister_netdevice(dev); 7617dc00c82SWang Chen dev_put(dev); 762d607032dSWang Chen return err; 763d607032dSWang Chen } 7641da177e4SLinus Torvalds break; 7651da177e4SLinus Torvalds case VIFF_TUNNEL: 7664feb88e5SBenjamin Thery dev = ipmr_new_tunnel(net, vifc); 7671da177e4SLinus Torvalds if (!dev) 7681da177e4SLinus Torvalds return -ENOBUFS; 769d607032dSWang Chen err = dev_set_allmulti(dev, 1); 770d607032dSWang Chen if (err) { 771d607032dSWang Chen ipmr_del_tunnel(dev, vifc); 7727dc00c82SWang Chen dev_put(dev); 773d607032dSWang Chen return err; 774d607032dSWang Chen } 7751da177e4SLinus Torvalds break; 776ee5e81f0SIlia K case VIFF_USE_IFINDEX: 7771da177e4SLinus Torvalds case 0: 778ee5e81f0SIlia K if (vifc->vifc_flags == VIFF_USE_IFINDEX) { 779ee5e81f0SIlia K dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex); 78051456b29SIan Morris if (dev && !__in_dev_get_rtnl(dev)) { 781ee5e81f0SIlia K dev_put(dev); 782ee5e81f0SIlia K return -EADDRNOTAVAIL; 783ee5e81f0SIlia K } 784a8cb16ddSEric Dumazet } else { 7854feb88e5SBenjamin Thery dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr); 786a8cb16ddSEric Dumazet } 7871da177e4SLinus Torvalds if (!dev) 7881da177e4SLinus Torvalds return -EADDRNOTAVAIL; 789d607032dSWang Chen err = dev_set_allmulti(dev, 1); 7907dc00c82SWang Chen if (err) { 7917dc00c82SWang Chen dev_put(dev); 792d607032dSWang Chen return err; 7937dc00c82SWang Chen } 7941da177e4SLinus Torvalds break; 7951da177e4SLinus Torvalds default: 7961da177e4SLinus Torvalds return -EINVAL; 7971da177e4SLinus Torvalds } 7981da177e4SLinus Torvalds 799a8cb16ddSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 800a8cb16ddSEric Dumazet if (!in_dev) { 801d0490cfdSDan Carpenter dev_put(dev); 8021da177e4SLinus Torvalds return -EADDRNOTAVAIL; 803d0490cfdSDan Carpenter } 80442f811b8SHerbert Xu IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++; 805d67b8c61SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING, dev->ifindex, 806d67b8c61SNicolas Dichtel &in_dev->cnf); 8071da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 8081da177e4SLinus Torvalds 809a8cb16ddSEric Dumazet /* Fill in the VIF structures */ 810a8cb16ddSEric Dumazet 8111da177e4SLinus Torvalds v->rate_limit = vifc->vifc_rate_limit; 8121da177e4SLinus Torvalds v->local = vifc->vifc_lcl_addr.s_addr; 8131da177e4SLinus Torvalds v->remote = vifc->vifc_rmt_addr.s_addr; 8141da177e4SLinus Torvalds v->flags = vifc->vifc_flags; 8151da177e4SLinus Torvalds if (!mrtsock) 8161da177e4SLinus Torvalds v->flags |= VIFF_STATIC; 8171da177e4SLinus Torvalds v->threshold = vifc->vifc_threshold; 8181da177e4SLinus Torvalds v->bytes_in = 0; 8191da177e4SLinus Torvalds v->bytes_out = 0; 8201da177e4SLinus Torvalds v->pkt_in = 0; 8211da177e4SLinus Torvalds v->pkt_out = 0; 8221da177e4SLinus Torvalds v->link = dev->ifindex; 8231da177e4SLinus Torvalds if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER)) 824a54acb3aSNicolas Dichtel v->link = dev_get_iflink(dev); 8251da177e4SLinus Torvalds 8261da177e4SLinus Torvalds /* And finish update writing critical data */ 8271da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 8281da177e4SLinus Torvalds v->dev = dev; 8291da177e4SLinus Torvalds if (v->flags & VIFF_REGISTER) 8300c12295aSPatrick McHardy mrt->mroute_reg_vif_num = vifi; 8310c12295aSPatrick McHardy if (vifi+1 > mrt->maxvif) 8320c12295aSPatrick McHardy mrt->maxvif = vifi+1; 8331da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 8341da177e4SLinus Torvalds return 0; 8351da177e4SLinus Torvalds } 8361da177e4SLinus Torvalds 837a8c9486bSEric Dumazet /* called with rcu_read_lock() */ 8380c12295aSPatrick McHardy static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, 8394feb88e5SBenjamin Thery __be32 origin, 8404feb88e5SBenjamin Thery __be32 mcastgrp) 8411da177e4SLinus Torvalds { 8421da177e4SLinus Torvalds int line = MFC_HASH(mcastgrp, origin); 8431da177e4SLinus Torvalds struct mfc_cache *c; 8441da177e4SLinus Torvalds 845a8c9486bSEric Dumazet list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) { 8461da177e4SLinus Torvalds if (c->mfc_origin == origin && c->mfc_mcastgrp == mcastgrp) 8471da177e4SLinus Torvalds return c; 8481da177e4SLinus Torvalds } 849862465f2SPatrick McHardy return NULL; 850862465f2SPatrick McHardy } 8511da177e4SLinus Torvalds 852660b26dcSNicolas Dichtel /* Look for a (*,*,oif) entry */ 853660b26dcSNicolas Dichtel static struct mfc_cache *ipmr_cache_find_any_parent(struct mr_table *mrt, 854660b26dcSNicolas Dichtel int vifi) 855660b26dcSNicolas Dichtel { 856360eb5daSNicolas Dichtel int line = MFC_HASH(htonl(INADDR_ANY), htonl(INADDR_ANY)); 857660b26dcSNicolas Dichtel struct mfc_cache *c; 858660b26dcSNicolas Dichtel 859660b26dcSNicolas Dichtel list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) 860360eb5daSNicolas Dichtel if (c->mfc_origin == htonl(INADDR_ANY) && 861360eb5daSNicolas Dichtel c->mfc_mcastgrp == htonl(INADDR_ANY) && 862660b26dcSNicolas Dichtel c->mfc_un.res.ttls[vifi] < 255) 863660b26dcSNicolas Dichtel return c; 864660b26dcSNicolas Dichtel 865660b26dcSNicolas Dichtel return NULL; 866660b26dcSNicolas Dichtel } 867660b26dcSNicolas Dichtel 868660b26dcSNicolas Dichtel /* Look for a (*,G) entry */ 869660b26dcSNicolas Dichtel static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt, 870660b26dcSNicolas Dichtel __be32 mcastgrp, int vifi) 871660b26dcSNicolas Dichtel { 872360eb5daSNicolas Dichtel int line = MFC_HASH(mcastgrp, htonl(INADDR_ANY)); 873660b26dcSNicolas Dichtel struct mfc_cache *c, *proxy; 874660b26dcSNicolas Dichtel 875360eb5daSNicolas Dichtel if (mcastgrp == htonl(INADDR_ANY)) 876660b26dcSNicolas Dichtel goto skip; 877660b26dcSNicolas Dichtel 878660b26dcSNicolas Dichtel list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) 879360eb5daSNicolas Dichtel if (c->mfc_origin == htonl(INADDR_ANY) && 880660b26dcSNicolas Dichtel c->mfc_mcastgrp == mcastgrp) { 881660b26dcSNicolas Dichtel if (c->mfc_un.res.ttls[vifi] < 255) 882660b26dcSNicolas Dichtel return c; 883660b26dcSNicolas Dichtel 884660b26dcSNicolas Dichtel /* It's ok if the vifi is part of the static tree */ 885660b26dcSNicolas Dichtel proxy = ipmr_cache_find_any_parent(mrt, 886660b26dcSNicolas Dichtel c->mfc_parent); 887660b26dcSNicolas Dichtel if (proxy && proxy->mfc_un.res.ttls[vifi] < 255) 888660b26dcSNicolas Dichtel return c; 889660b26dcSNicolas Dichtel } 890660b26dcSNicolas Dichtel 891660b26dcSNicolas Dichtel skip: 892660b26dcSNicolas Dichtel return ipmr_cache_find_any_parent(mrt, vifi); 893660b26dcSNicolas Dichtel } 894660b26dcSNicolas Dichtel 8957ef8f65dSNikolay Aleksandrov /* Allocate a multicast cache entry */ 896d658f8a0SPatrick McHardy static struct mfc_cache *ipmr_cache_alloc(void) 8971da177e4SLinus Torvalds { 898c3762229SRobert P. J. Day struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL); 899a8c9486bSEric Dumazet 90070a0dec4STom Goff if (c) { 90170a0dec4STom Goff c->mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1; 9021da177e4SLinus Torvalds c->mfc_un.res.minvif = MAXVIFS; 90370a0dec4STom Goff } 9041da177e4SLinus Torvalds return c; 9051da177e4SLinus Torvalds } 9061da177e4SLinus Torvalds 907d658f8a0SPatrick McHardy static struct mfc_cache *ipmr_cache_alloc_unres(void) 9081da177e4SLinus Torvalds { 909c3762229SRobert P. J. Day struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC); 910a8c9486bSEric Dumazet 911a8c9486bSEric Dumazet if (c) { 9121da177e4SLinus Torvalds skb_queue_head_init(&c->mfc_un.unres.unresolved); 9131da177e4SLinus Torvalds c->mfc_un.unres.expires = jiffies + 10*HZ; 914a8c9486bSEric Dumazet } 9151da177e4SLinus Torvalds return c; 9161da177e4SLinus Torvalds } 9171da177e4SLinus Torvalds 9187ef8f65dSNikolay Aleksandrov /* A cache entry has gone into a resolved state from queued */ 9190c12295aSPatrick McHardy static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt, 9200c12295aSPatrick McHardy struct mfc_cache *uc, struct mfc_cache *c) 9211da177e4SLinus Torvalds { 9221da177e4SLinus Torvalds struct sk_buff *skb; 9239ef1d4c7SPatrick McHardy struct nlmsgerr *e; 9241da177e4SLinus Torvalds 925a8cb16ddSEric Dumazet /* Play the pending entries through our router */ 9261da177e4SLinus Torvalds while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) { 927eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->version == 0) { 9281da177e4SLinus Torvalds struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr)); 9291da177e4SLinus Torvalds 930573ce260SHong zhi guo if (__ipmr_fill_mroute(mrt, skb, c, nlmsg_data(nlh)) > 0) { 931a8cb16ddSEric Dumazet nlh->nlmsg_len = skb_tail_pointer(skb) - 932a8cb16ddSEric Dumazet (u8 *)nlh; 9331da177e4SLinus Torvalds } else { 9341da177e4SLinus Torvalds nlh->nlmsg_type = NLMSG_ERROR; 935573ce260SHong zhi guo nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 9361da177e4SLinus Torvalds skb_trim(skb, nlh->nlmsg_len); 937573ce260SHong zhi guo e = nlmsg_data(nlh); 9389ef1d4c7SPatrick McHardy e->error = -EMSGSIZE; 9399ef1d4c7SPatrick McHardy memset(&e->msg, 0, sizeof(e->msg)); 9401da177e4SLinus Torvalds } 9412942e900SThomas Graf 94215e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 943a8cb16ddSEric Dumazet } else { 9440c12295aSPatrick McHardy ip_mr_forward(net, mrt, skb, c, 0); 9451da177e4SLinus Torvalds } 9461da177e4SLinus Torvalds } 947a8cb16ddSEric Dumazet } 9481da177e4SLinus Torvalds 949c316c629SNikolay Aleksandrov /* Bounce a cache query up to mrouted. We could use netlink for this but mrouted 9501da177e4SLinus Torvalds * expects the following bizarre scheme. 9511da177e4SLinus Torvalds * 9521da177e4SLinus Torvalds * Called under mrt_lock. 9531da177e4SLinus Torvalds */ 9540c12295aSPatrick McHardy static int ipmr_cache_report(struct mr_table *mrt, 9554feb88e5SBenjamin Thery struct sk_buff *pkt, vifi_t vifi, int assert) 9561da177e4SLinus Torvalds { 957c9bdd4b5SArnaldo Carvalho de Melo const int ihl = ip_hdrlen(pkt); 958c316c629SNikolay Aleksandrov struct sock *mroute_sk; 9591da177e4SLinus Torvalds struct igmphdr *igmp; 9601da177e4SLinus Torvalds struct igmpmsg *msg; 961c316c629SNikolay Aleksandrov struct sk_buff *skb; 9621da177e4SLinus Torvalds int ret; 9631da177e4SLinus Torvalds 9641da177e4SLinus Torvalds if (assert == IGMPMSG_WHOLEPKT) 9651da177e4SLinus Torvalds skb = skb_realloc_headroom(pkt, sizeof(struct iphdr)); 9661da177e4SLinus Torvalds else 9671da177e4SLinus Torvalds skb = alloc_skb(128, GFP_ATOMIC); 9681da177e4SLinus Torvalds 9691da177e4SLinus Torvalds if (!skb) 9701da177e4SLinus Torvalds return -ENOBUFS; 9711da177e4SLinus Torvalds 9721da177e4SLinus Torvalds if (assert == IGMPMSG_WHOLEPKT) { 9731da177e4SLinus Torvalds /* Ugly, but we have no choice with this interface. 974a8cb16ddSEric Dumazet * Duplicate old header, fix ihl, length etc. 975a8cb16ddSEric Dumazet * And all this only to mangle msg->im_msgtype and 976a8cb16ddSEric Dumazet * to set msg->im_mbz to "mbz" :-) 9771da177e4SLinus Torvalds */ 978878c8145SArnaldo Carvalho de Melo skb_push(skb, sizeof(struct iphdr)); 979878c8145SArnaldo Carvalho de Melo skb_reset_network_header(skb); 980badff6d0SArnaldo Carvalho de Melo skb_reset_transport_header(skb); 9810272ffc4SArnaldo Carvalho de Melo msg = (struct igmpmsg *)skb_network_header(skb); 982d56f90a7SArnaldo Carvalho de Melo memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr)); 9831da177e4SLinus Torvalds msg->im_msgtype = IGMPMSG_WHOLEPKT; 9841da177e4SLinus Torvalds msg->im_mbz = 0; 9850c12295aSPatrick McHardy msg->im_vif = mrt->mroute_reg_vif_num; 986eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2; 987eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) + 988eddc9ec5SArnaldo Carvalho de Melo sizeof(struct iphdr)); 989c316c629SNikolay Aleksandrov } else { 990a8cb16ddSEric Dumazet /* Copy the IP header */ 99130f3a40fSCong Wang skb_set_network_header(skb, skb->len); 992ddc7b8e3SArnaldo Carvalho de Melo skb_put(skb, ihl); 99327d7ff46SArnaldo Carvalho de Melo skb_copy_to_linear_data(skb, pkt->data, ihl); 994c316c629SNikolay Aleksandrov /* Flag to the kernel this is a route add */ 995c316c629SNikolay Aleksandrov ip_hdr(skb)->protocol = 0; 996eddc9ec5SArnaldo Carvalho de Melo msg = (struct igmpmsg *)skb_network_header(skb); 9971da177e4SLinus Torvalds msg->im_vif = vifi; 998adf30907SEric Dumazet skb_dst_set(skb, dst_clone(skb_dst(pkt))); 999a8cb16ddSEric Dumazet /* Add our header */ 10001da177e4SLinus Torvalds igmp = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr)); 1001c316c629SNikolay Aleksandrov igmp->type = assert; 10021da177e4SLinus Torvalds msg->im_msgtype = assert; 10031da177e4SLinus Torvalds igmp->code = 0; 1004eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */ 1005b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 10061da177e4SLinus Torvalds } 10071da177e4SLinus Torvalds 10084c968709SEric Dumazet rcu_read_lock(); 10094c968709SEric Dumazet mroute_sk = rcu_dereference(mrt->mroute_sk); 101051456b29SIan Morris if (!mroute_sk) { 10114c968709SEric Dumazet rcu_read_unlock(); 10121da177e4SLinus Torvalds kfree_skb(skb); 10131da177e4SLinus Torvalds return -EINVAL; 10141da177e4SLinus Torvalds } 10151da177e4SLinus Torvalds 1016a8cb16ddSEric Dumazet /* Deliver to mrouted */ 10174c968709SEric Dumazet ret = sock_queue_rcv_skb(mroute_sk, skb); 10184c968709SEric Dumazet rcu_read_unlock(); 101970a269e6SBenjamin Thery if (ret < 0) { 1020e87cc472SJoe Perches net_warn_ratelimited("mroute: pending queue full, dropping entries\n"); 10211da177e4SLinus Torvalds kfree_skb(skb); 10221da177e4SLinus Torvalds } 10231da177e4SLinus Torvalds 10241da177e4SLinus Torvalds return ret; 10251da177e4SLinus Torvalds } 10261da177e4SLinus Torvalds 10277ef8f65dSNikolay Aleksandrov /* Queue a packet for resolution. It gets locked cache entry! */ 10287ef8f65dSNikolay Aleksandrov static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, 10297ef8f65dSNikolay Aleksandrov struct sk_buff *skb) 10301da177e4SLinus Torvalds { 1031862465f2SPatrick McHardy bool found = false; 10321da177e4SLinus Torvalds int err; 10331da177e4SLinus Torvalds struct mfc_cache *c; 1034eddc9ec5SArnaldo Carvalho de Melo const struct iphdr *iph = ip_hdr(skb); 10351da177e4SLinus Torvalds 10361da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 10370c12295aSPatrick McHardy list_for_each_entry(c, &mrt->mfc_unres_queue, list) { 1038e258beb2SPatrick McHardy if (c->mfc_mcastgrp == iph->daddr && 1039862465f2SPatrick McHardy c->mfc_origin == iph->saddr) { 1040862465f2SPatrick McHardy found = true; 10411da177e4SLinus Torvalds break; 10421da177e4SLinus Torvalds } 1043862465f2SPatrick McHardy } 10441da177e4SLinus Torvalds 1045862465f2SPatrick McHardy if (!found) { 1046a8cb16ddSEric Dumazet /* Create a new entry if allowable */ 10470c12295aSPatrick McHardy if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 || 1048d658f8a0SPatrick McHardy (c = ipmr_cache_alloc_unres()) == NULL) { 10491da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 10501da177e4SLinus Torvalds 10511da177e4SLinus Torvalds kfree_skb(skb); 10521da177e4SLinus Torvalds return -ENOBUFS; 10531da177e4SLinus Torvalds } 10541da177e4SLinus Torvalds 1055a8cb16ddSEric Dumazet /* Fill in the new cache entry */ 10561da177e4SLinus Torvalds c->mfc_parent = -1; 1057eddc9ec5SArnaldo Carvalho de Melo c->mfc_origin = iph->saddr; 1058eddc9ec5SArnaldo Carvalho de Melo c->mfc_mcastgrp = iph->daddr; 10591da177e4SLinus Torvalds 1060a8cb16ddSEric Dumazet /* Reflect first query at mrouted. */ 10610c12295aSPatrick McHardy err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE); 10624feb88e5SBenjamin Thery if (err < 0) { 10631da177e4SLinus Torvalds /* If the report failed throw the cache entry 10641da177e4SLinus Torvalds out - Brad Parker 10651da177e4SLinus Torvalds */ 10661da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 10671da177e4SLinus Torvalds 10685c0a66f5SBenjamin Thery ipmr_cache_free(c); 10691da177e4SLinus Torvalds kfree_skb(skb); 10701da177e4SLinus Torvalds return err; 10711da177e4SLinus Torvalds } 10721da177e4SLinus Torvalds 10730c12295aSPatrick McHardy atomic_inc(&mrt->cache_resolve_queue_len); 10740c12295aSPatrick McHardy list_add(&c->list, &mrt->mfc_unres_queue); 10758cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 10761da177e4SLinus Torvalds 1077278554bdSDavid S. Miller if (atomic_read(&mrt->cache_resolve_queue_len) == 1) 10780c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires); 10791da177e4SLinus Torvalds } 10801da177e4SLinus Torvalds 1081a8cb16ddSEric Dumazet /* See if we can append the packet */ 10821da177e4SLinus Torvalds if (c->mfc_un.unres.unresolved.qlen > 3) { 10831da177e4SLinus Torvalds kfree_skb(skb); 10841da177e4SLinus Torvalds err = -ENOBUFS; 10851da177e4SLinus Torvalds } else { 10861da177e4SLinus Torvalds skb_queue_tail(&c->mfc_un.unres.unresolved, skb); 10871da177e4SLinus Torvalds err = 0; 10881da177e4SLinus Torvalds } 10891da177e4SLinus Torvalds 10901da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 10911da177e4SLinus Torvalds return err; 10921da177e4SLinus Torvalds } 10931da177e4SLinus Torvalds 10947ef8f65dSNikolay Aleksandrov /* MFC cache manipulation by user space mroute daemon */ 10951da177e4SLinus Torvalds 1096660b26dcSNicolas Dichtel static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent) 10971da177e4SLinus Torvalds { 10981da177e4SLinus Torvalds int line; 1099862465f2SPatrick McHardy struct mfc_cache *c, *next; 11001da177e4SLinus Torvalds 11011da177e4SLinus Torvalds line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr); 11021da177e4SLinus Torvalds 11030c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) { 11041da177e4SLinus Torvalds if (c->mfc_origin == mfc->mfcc_origin.s_addr && 1105660b26dcSNicolas Dichtel c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr && 1106660b26dcSNicolas Dichtel (parent == -1 || parent == c->mfc_parent)) { 1107a8c9486bSEric Dumazet list_del_rcu(&c->list); 11088cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 11095c0a66f5SBenjamin Thery ipmr_cache_free(c); 11101da177e4SLinus Torvalds return 0; 11111da177e4SLinus Torvalds } 11121da177e4SLinus Torvalds } 11131da177e4SLinus Torvalds return -ENOENT; 11141da177e4SLinus Torvalds } 11151da177e4SLinus Torvalds 11160c12295aSPatrick McHardy static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, 1117660b26dcSNicolas Dichtel struct mfcctl *mfc, int mrtsock, int parent) 11181da177e4SLinus Torvalds { 1119862465f2SPatrick McHardy bool found = false; 11201da177e4SLinus Torvalds int line; 1121862465f2SPatrick McHardy struct mfc_cache *uc, *c; 11221da177e4SLinus Torvalds 1123a50436f2SPatrick McHardy if (mfc->mfcc_parent >= MAXVIFS) 1124a50436f2SPatrick McHardy return -ENFILE; 1125a50436f2SPatrick McHardy 11261da177e4SLinus Torvalds line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr); 11271da177e4SLinus Torvalds 11280c12295aSPatrick McHardy list_for_each_entry(c, &mrt->mfc_cache_array[line], list) { 11291da177e4SLinus Torvalds if (c->mfc_origin == mfc->mfcc_origin.s_addr && 1130660b26dcSNicolas Dichtel c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr && 1131660b26dcSNicolas Dichtel (parent == -1 || parent == c->mfc_parent)) { 1132862465f2SPatrick McHardy found = true; 11331da177e4SLinus Torvalds break; 11341da177e4SLinus Torvalds } 1135862465f2SPatrick McHardy } 11361da177e4SLinus Torvalds 1137862465f2SPatrick McHardy if (found) { 11381da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 11391da177e4SLinus Torvalds c->mfc_parent = mfc->mfcc_parent; 11400c12295aSPatrick McHardy ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls); 11411da177e4SLinus Torvalds if (!mrtsock) 11421da177e4SLinus Torvalds c->mfc_flags |= MFC_STATIC; 11431da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 11448cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 11451da177e4SLinus Torvalds return 0; 11461da177e4SLinus Torvalds } 11471da177e4SLinus Torvalds 1148360eb5daSNicolas Dichtel if (mfc->mfcc_mcastgrp.s_addr != htonl(INADDR_ANY) && 1149660b26dcSNicolas Dichtel !ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) 11501da177e4SLinus Torvalds return -EINVAL; 11511da177e4SLinus Torvalds 1152d658f8a0SPatrick McHardy c = ipmr_cache_alloc(); 115351456b29SIan Morris if (!c) 11541da177e4SLinus Torvalds return -ENOMEM; 11551da177e4SLinus Torvalds 11561da177e4SLinus Torvalds c->mfc_origin = mfc->mfcc_origin.s_addr; 11571da177e4SLinus Torvalds c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr; 11581da177e4SLinus Torvalds c->mfc_parent = mfc->mfcc_parent; 11590c12295aSPatrick McHardy ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls); 11601da177e4SLinus Torvalds if (!mrtsock) 11611da177e4SLinus Torvalds c->mfc_flags |= MFC_STATIC; 11621da177e4SLinus Torvalds 1163a8c9486bSEric Dumazet list_add_rcu(&c->list, &mrt->mfc_cache_array[line]); 11641da177e4SLinus Torvalds 11657ef8f65dSNikolay Aleksandrov /* Check to see if we resolved a queued list. If so we 11661da177e4SLinus Torvalds * need to send on the frames and tidy up. 11671da177e4SLinus Torvalds */ 1168b0ebb739SPatrick McHardy found = false; 11691da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 11700c12295aSPatrick McHardy list_for_each_entry(uc, &mrt->mfc_unres_queue, list) { 1171e258beb2SPatrick McHardy if (uc->mfc_origin == c->mfc_origin && 11721da177e4SLinus Torvalds uc->mfc_mcastgrp == c->mfc_mcastgrp) { 1173862465f2SPatrick McHardy list_del(&uc->list); 11740c12295aSPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len); 1175b0ebb739SPatrick McHardy found = true; 11761da177e4SLinus Torvalds break; 11771da177e4SLinus Torvalds } 11781da177e4SLinus Torvalds } 11790c12295aSPatrick McHardy if (list_empty(&mrt->mfc_unres_queue)) 11800c12295aSPatrick McHardy del_timer(&mrt->ipmr_expire_timer); 11811da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11821da177e4SLinus Torvalds 1183b0ebb739SPatrick McHardy if (found) { 11840c12295aSPatrick McHardy ipmr_cache_resolve(net, mrt, uc, c); 11855c0a66f5SBenjamin Thery ipmr_cache_free(uc); 11861da177e4SLinus Torvalds } 11878cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 11881da177e4SLinus Torvalds return 0; 11891da177e4SLinus Torvalds } 11901da177e4SLinus Torvalds 11917ef8f65dSNikolay Aleksandrov /* Close the multicast socket, and clear the vif tables etc */ 11920e615e96SNikolay Aleksandrov static void mroute_clean_tables(struct mr_table *mrt, bool all) 11931da177e4SLinus Torvalds { 11941da177e4SLinus Torvalds int i; 1195d17fa6faSEric Dumazet LIST_HEAD(list); 1196862465f2SPatrick McHardy struct mfc_cache *c, *next; 11971da177e4SLinus Torvalds 1198a8cb16ddSEric Dumazet /* Shut down all active vif entries */ 11990c12295aSPatrick McHardy for (i = 0; i < mrt->maxvif; i++) { 12000e615e96SNikolay Aleksandrov if (!all && (mrt->vif_table[i].flags & VIFF_STATIC)) 12010e615e96SNikolay Aleksandrov continue; 12020c12295aSPatrick McHardy vif_delete(mrt, i, 0, &list); 12031da177e4SLinus Torvalds } 1204d17fa6faSEric Dumazet unregister_netdevice_many(&list); 12051da177e4SLinus Torvalds 1206a8cb16ddSEric Dumazet /* Wipe the cache */ 12071da177e4SLinus Torvalds for (i = 0; i < MFC_LINES; i++) { 12080c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[i], list) { 12090e615e96SNikolay Aleksandrov if (!all && (c->mfc_flags & MFC_STATIC)) 12101da177e4SLinus Torvalds continue; 1211a8c9486bSEric Dumazet list_del_rcu(&c->list); 12128cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 12135c0a66f5SBenjamin Thery ipmr_cache_free(c); 12141da177e4SLinus Torvalds } 12151da177e4SLinus Torvalds } 12161da177e4SLinus Torvalds 12170c12295aSPatrick McHardy if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { 12181da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 12190c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) { 1220862465f2SPatrick McHardy list_del(&c->list); 12218cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 12220c12295aSPatrick McHardy ipmr_destroy_unres(mrt, c); 12231da177e4SLinus Torvalds } 12241da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 12251da177e4SLinus Torvalds } 12261da177e4SLinus Torvalds } 12271da177e4SLinus Torvalds 12284c968709SEric Dumazet /* called from ip_ra_control(), before an RCU grace period, 12294c968709SEric Dumazet * we dont need to call synchronize_rcu() here 12304c968709SEric Dumazet */ 12311da177e4SLinus Torvalds static void mrtsock_destruct(struct sock *sk) 12321da177e4SLinus Torvalds { 12334feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1234f0ad0860SPatrick McHardy struct mr_table *mrt; 12354feb88e5SBenjamin Thery 12361da177e4SLinus Torvalds rtnl_lock(); 1237f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 12384c968709SEric Dumazet if (sk == rtnl_dereference(mrt->mroute_sk)) { 12394feb88e5SBenjamin Thery IPV4_DEVCONF_ALL(net, MC_FORWARDING)--; 1240d67b8c61SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING, 1241d67b8c61SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1242d67b8c61SNicolas Dichtel net->ipv4.devconf_all); 1243a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(mrt->mroute_sk, NULL); 12440e615e96SNikolay Aleksandrov mroute_clean_tables(mrt, false); 12451da177e4SLinus Torvalds } 12461da177e4SLinus Torvalds } 12471da177e4SLinus Torvalds rtnl_unlock(); 12481da177e4SLinus Torvalds } 12491da177e4SLinus Torvalds 12507ef8f65dSNikolay Aleksandrov /* Socket options and virtual interface manipulation. The whole 12511da177e4SLinus Torvalds * virtual interface system is a complete heap, but unfortunately 12521da177e4SLinus Torvalds * that's how BSD mrouted happens to think. Maybe one day with a proper 12531da177e4SLinus Torvalds * MOSPF/PIM router set up we can clean this up. 12541da177e4SLinus Torvalds */ 12551da177e4SLinus Torvalds 125629e97d21SNikolay Aleksandrov int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, 125729e97d21SNikolay Aleksandrov unsigned int optlen) 12581da177e4SLinus Torvalds { 125929e97d21SNikolay Aleksandrov struct net *net = sock_net(sk); 126029e97d21SNikolay Aleksandrov int val, ret = 0, parent = 0; 126129e97d21SNikolay Aleksandrov struct mr_table *mrt; 12621da177e4SLinus Torvalds struct vifctl vif; 12631da177e4SLinus Torvalds struct mfcctl mfc; 126429e97d21SNikolay Aleksandrov u32 uval; 1265f0ad0860SPatrick McHardy 126629e97d21SNikolay Aleksandrov /* There's one exception to the lock - MRT_DONE which needs to unlock */ 126729e97d21SNikolay Aleksandrov rtnl_lock(); 12685e1859fbSEric Dumazet if (sk->sk_type != SOCK_RAW || 126929e97d21SNikolay Aleksandrov inet_sk(sk)->inet_num != IPPROTO_IGMP) { 127029e97d21SNikolay Aleksandrov ret = -EOPNOTSUPP; 127129e97d21SNikolay Aleksandrov goto out_unlock; 127229e97d21SNikolay Aleksandrov } 12735e1859fbSEric Dumazet 1274f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 127529e97d21SNikolay Aleksandrov if (!mrt) { 127629e97d21SNikolay Aleksandrov ret = -ENOENT; 127729e97d21SNikolay Aleksandrov goto out_unlock; 127829e97d21SNikolay Aleksandrov } 1279132adf54SStephen Hemminger if (optname != MRT_INIT) { 128033d480ceSEric Dumazet if (sk != rcu_access_pointer(mrt->mroute_sk) && 128129e97d21SNikolay Aleksandrov !ns_capable(net->user_ns, CAP_NET_ADMIN)) { 128229e97d21SNikolay Aleksandrov ret = -EACCES; 128329e97d21SNikolay Aleksandrov goto out_unlock; 128429e97d21SNikolay Aleksandrov } 12851da177e4SLinus Torvalds } 12861da177e4SLinus Torvalds 1287132adf54SStephen Hemminger switch (optname) { 12881da177e4SLinus Torvalds case MRT_INIT: 128942e6b89cSNikolay Aleksandrov if (optlen != sizeof(int)) { 129029e97d21SNikolay Aleksandrov ret = -EINVAL; 129129e97d21SNikolay Aleksandrov break; 129242e6b89cSNikolay Aleksandrov } 129342e6b89cSNikolay Aleksandrov if (rtnl_dereference(mrt->mroute_sk)) { 129442e6b89cSNikolay Aleksandrov ret = -EADDRINUSE; 129542e6b89cSNikolay Aleksandrov break; 129642e6b89cSNikolay Aleksandrov } 12971da177e4SLinus Torvalds 12981da177e4SLinus Torvalds ret = ip_ra_control(sk, 1, mrtsock_destruct); 12991da177e4SLinus Torvalds if (ret == 0) { 1300cf778b00SEric Dumazet rcu_assign_pointer(mrt->mroute_sk, sk); 13014feb88e5SBenjamin Thery IPV4_DEVCONF_ALL(net, MC_FORWARDING)++; 1302d67b8c61SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING, 1303d67b8c61SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1304d67b8c61SNicolas Dichtel net->ipv4.devconf_all); 13051da177e4SLinus Torvalds } 130629e97d21SNikolay Aleksandrov break; 13071da177e4SLinus Torvalds case MRT_DONE: 130829e97d21SNikolay Aleksandrov if (sk != rcu_access_pointer(mrt->mroute_sk)) { 130929e97d21SNikolay Aleksandrov ret = -EACCES; 131029e97d21SNikolay Aleksandrov } else { 131129e97d21SNikolay Aleksandrov /* We need to unlock here because mrtsock_destruct takes 131229e97d21SNikolay Aleksandrov * care of rtnl itself and we can't change that due to 131329e97d21SNikolay Aleksandrov * the IP_ROUTER_ALERT setsockopt which runs without it. 131429e97d21SNikolay Aleksandrov */ 131529e97d21SNikolay Aleksandrov rtnl_unlock(); 131629e97d21SNikolay Aleksandrov ret = ip_ra_control(sk, 0, NULL); 131729e97d21SNikolay Aleksandrov goto out; 131829e97d21SNikolay Aleksandrov } 131929e97d21SNikolay Aleksandrov break; 13201da177e4SLinus Torvalds case MRT_ADD_VIF: 13211da177e4SLinus Torvalds case MRT_DEL_VIF: 132229e97d21SNikolay Aleksandrov if (optlen != sizeof(vif)) { 132329e97d21SNikolay Aleksandrov ret = -EINVAL; 132429e97d21SNikolay Aleksandrov break; 132529e97d21SNikolay Aleksandrov } 132629e97d21SNikolay Aleksandrov if (copy_from_user(&vif, optval, sizeof(vif))) { 132729e97d21SNikolay Aleksandrov ret = -EFAULT; 132829e97d21SNikolay Aleksandrov break; 132929e97d21SNikolay Aleksandrov } 133029e97d21SNikolay Aleksandrov if (vif.vifc_vifi >= MAXVIFS) { 133129e97d21SNikolay Aleksandrov ret = -ENFILE; 133229e97d21SNikolay Aleksandrov break; 133329e97d21SNikolay Aleksandrov } 13341da177e4SLinus Torvalds if (optname == MRT_ADD_VIF) { 13354c968709SEric Dumazet ret = vif_add(net, mrt, &vif, 13364c968709SEric Dumazet sk == rtnl_dereference(mrt->mroute_sk)); 13371da177e4SLinus Torvalds } else { 13380c12295aSPatrick McHardy ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL); 13391da177e4SLinus Torvalds } 134029e97d21SNikolay Aleksandrov break; 13417ef8f65dSNikolay Aleksandrov /* Manipulate the forwarding caches. These live 13421da177e4SLinus Torvalds * in a sort of kernel/user symbiosis. 13431da177e4SLinus Torvalds */ 13441da177e4SLinus Torvalds case MRT_ADD_MFC: 13451da177e4SLinus Torvalds case MRT_DEL_MFC: 1346660b26dcSNicolas Dichtel parent = -1; 1347660b26dcSNicolas Dichtel case MRT_ADD_MFC_PROXY: 1348660b26dcSNicolas Dichtel case MRT_DEL_MFC_PROXY: 134929e97d21SNikolay Aleksandrov if (optlen != sizeof(mfc)) { 135029e97d21SNikolay Aleksandrov ret = -EINVAL; 135129e97d21SNikolay Aleksandrov break; 135229e97d21SNikolay Aleksandrov } 135329e97d21SNikolay Aleksandrov if (copy_from_user(&mfc, optval, sizeof(mfc))) { 135429e97d21SNikolay Aleksandrov ret = -EFAULT; 135529e97d21SNikolay Aleksandrov break; 135629e97d21SNikolay Aleksandrov } 1357660b26dcSNicolas Dichtel if (parent == 0) 1358660b26dcSNicolas Dichtel parent = mfc.mfcc_parent; 1359660b26dcSNicolas Dichtel if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY) 1360660b26dcSNicolas Dichtel ret = ipmr_mfc_delete(mrt, &mfc, parent); 13611da177e4SLinus Torvalds else 13624c968709SEric Dumazet ret = ipmr_mfc_add(net, mrt, &mfc, 1363660b26dcSNicolas Dichtel sk == rtnl_dereference(mrt->mroute_sk), 1364660b26dcSNicolas Dichtel parent); 136529e97d21SNikolay Aleksandrov break; 13667ef8f65dSNikolay Aleksandrov /* Control PIM assert. */ 13671da177e4SLinus Torvalds case MRT_ASSERT: 136829e97d21SNikolay Aleksandrov if (optlen != sizeof(val)) { 136929e97d21SNikolay Aleksandrov ret = -EINVAL; 137029e97d21SNikolay Aleksandrov break; 13711da177e4SLinus Torvalds } 137229e97d21SNikolay Aleksandrov if (get_user(val, (int __user *)optval)) { 137329e97d21SNikolay Aleksandrov ret = -EFAULT; 137429e97d21SNikolay Aleksandrov break; 137529e97d21SNikolay Aleksandrov } 137629e97d21SNikolay Aleksandrov mrt->mroute_do_assert = val; 137729e97d21SNikolay Aleksandrov break; 13781da177e4SLinus Torvalds case MRT_PIM: 13791973a4eaSNikolay Aleksandrov if (!ipmr_pimsm_enabled()) { 138029e97d21SNikolay Aleksandrov ret = -ENOPROTOOPT; 138129e97d21SNikolay Aleksandrov break; 13821da177e4SLinus Torvalds } 138329e97d21SNikolay Aleksandrov if (optlen != sizeof(val)) { 138429e97d21SNikolay Aleksandrov ret = -EINVAL; 138529e97d21SNikolay Aleksandrov break; 13861da177e4SLinus Torvalds } 138729e97d21SNikolay Aleksandrov if (get_user(val, (int __user *)optval)) { 138829e97d21SNikolay Aleksandrov ret = -EFAULT; 138929e97d21SNikolay Aleksandrov break; 139029e97d21SNikolay Aleksandrov } 139129e97d21SNikolay Aleksandrov 139229e97d21SNikolay Aleksandrov val = !!val; 139329e97d21SNikolay Aleksandrov if (val != mrt->mroute_do_pim) { 139429e97d21SNikolay Aleksandrov mrt->mroute_do_pim = val; 139529e97d21SNikolay Aleksandrov mrt->mroute_do_assert = val; 139629e97d21SNikolay Aleksandrov } 139729e97d21SNikolay Aleksandrov break; 1398f0ad0860SPatrick McHardy case MRT_TABLE: 139929e97d21SNikolay Aleksandrov if (!IS_BUILTIN(CONFIG_IP_MROUTE_MULTIPLE_TABLES)) { 140029e97d21SNikolay Aleksandrov ret = -ENOPROTOOPT; 140129e97d21SNikolay Aleksandrov break; 140229e97d21SNikolay Aleksandrov } 140329e97d21SNikolay Aleksandrov if (optlen != sizeof(uval)) { 140429e97d21SNikolay Aleksandrov ret = -EINVAL; 140529e97d21SNikolay Aleksandrov break; 140629e97d21SNikolay Aleksandrov } 140729e97d21SNikolay Aleksandrov if (get_user(uval, (u32 __user *)optval)) { 140829e97d21SNikolay Aleksandrov ret = -EFAULT; 140929e97d21SNikolay Aleksandrov break; 141029e97d21SNikolay Aleksandrov } 1411f0ad0860SPatrick McHardy 14124c968709SEric Dumazet if (sk == rtnl_dereference(mrt->mroute_sk)) { 14134c968709SEric Dumazet ret = -EBUSY; 14144c968709SEric Dumazet } else { 141529e97d21SNikolay Aleksandrov mrt = ipmr_new_table(net, uval); 14161113ebbcSNikolay Aleksandrov if (IS_ERR(mrt)) 14171113ebbcSNikolay Aleksandrov ret = PTR_ERR(mrt); 14185e1859fbSEric Dumazet else 141929e97d21SNikolay Aleksandrov raw_sk(sk)->ipmr_table = uval; 14204c968709SEric Dumazet } 142129e97d21SNikolay Aleksandrov break; 14227ef8f65dSNikolay Aleksandrov /* Spurious command, or MRT_VERSION which you cannot set. */ 14231da177e4SLinus Torvalds default: 142429e97d21SNikolay Aleksandrov ret = -ENOPROTOOPT; 14251da177e4SLinus Torvalds } 142629e97d21SNikolay Aleksandrov out_unlock: 142729e97d21SNikolay Aleksandrov rtnl_unlock(); 142829e97d21SNikolay Aleksandrov out: 142929e97d21SNikolay Aleksandrov return ret; 14301da177e4SLinus Torvalds } 14311da177e4SLinus Torvalds 14327ef8f65dSNikolay Aleksandrov /* Getsock opt support for the multicast routing system. */ 14331da177e4SLinus Torvalds int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen) 14341da177e4SLinus Torvalds { 14351da177e4SLinus Torvalds int olr; 14361da177e4SLinus Torvalds int val; 14374feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1438f0ad0860SPatrick McHardy struct mr_table *mrt; 1439f0ad0860SPatrick McHardy 14405e1859fbSEric Dumazet if (sk->sk_type != SOCK_RAW || 14415e1859fbSEric Dumazet inet_sk(sk)->inet_num != IPPROTO_IGMP) 14425e1859fbSEric Dumazet return -EOPNOTSUPP; 14435e1859fbSEric Dumazet 1444f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 144551456b29SIan Morris if (!mrt) 1446f0ad0860SPatrick McHardy return -ENOENT; 14471da177e4SLinus Torvalds 1448fe9ef3ceSNikolay Aleksandrov switch (optname) { 1449fe9ef3ceSNikolay Aleksandrov case MRT_VERSION: 14501da177e4SLinus Torvalds val = 0x0305; 1451fe9ef3ceSNikolay Aleksandrov break; 1452fe9ef3ceSNikolay Aleksandrov case MRT_PIM: 14531973a4eaSNikolay Aleksandrov if (!ipmr_pimsm_enabled()) 1454c316c629SNikolay Aleksandrov return -ENOPROTOOPT; 14550c12295aSPatrick McHardy val = mrt->mroute_do_pim; 1456fe9ef3ceSNikolay Aleksandrov break; 1457fe9ef3ceSNikolay Aleksandrov case MRT_ASSERT: 14580c12295aSPatrick McHardy val = mrt->mroute_do_assert; 1459fe9ef3ceSNikolay Aleksandrov break; 1460fe9ef3ceSNikolay Aleksandrov default: 1461fe9ef3ceSNikolay Aleksandrov return -ENOPROTOOPT; 1462c316c629SNikolay Aleksandrov } 1463fe9ef3ceSNikolay Aleksandrov 1464fe9ef3ceSNikolay Aleksandrov if (get_user(olr, optlen)) 1465fe9ef3ceSNikolay Aleksandrov return -EFAULT; 1466fe9ef3ceSNikolay Aleksandrov olr = min_t(unsigned int, olr, sizeof(int)); 1467fe9ef3ceSNikolay Aleksandrov if (olr < 0) 1468fe9ef3ceSNikolay Aleksandrov return -EINVAL; 1469fe9ef3ceSNikolay Aleksandrov if (put_user(olr, optlen)) 1470fe9ef3ceSNikolay Aleksandrov return -EFAULT; 14711da177e4SLinus Torvalds if (copy_to_user(optval, &val, olr)) 14721da177e4SLinus Torvalds return -EFAULT; 14731da177e4SLinus Torvalds return 0; 14741da177e4SLinus Torvalds } 14751da177e4SLinus Torvalds 14767ef8f65dSNikolay Aleksandrov /* The IP multicast ioctl support routines. */ 14771da177e4SLinus Torvalds int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) 14781da177e4SLinus Torvalds { 14791da177e4SLinus Torvalds struct sioc_sg_req sr; 14801da177e4SLinus Torvalds struct sioc_vif_req vr; 14811da177e4SLinus Torvalds struct vif_device *vif; 14821da177e4SLinus Torvalds struct mfc_cache *c; 14834feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1484f0ad0860SPatrick McHardy struct mr_table *mrt; 1485f0ad0860SPatrick McHardy 1486f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 148751456b29SIan Morris if (!mrt) 1488f0ad0860SPatrick McHardy return -ENOENT; 14891da177e4SLinus Torvalds 1490132adf54SStephen Hemminger switch (cmd) { 14911da177e4SLinus Torvalds case SIOCGETVIFCNT: 14921da177e4SLinus Torvalds if (copy_from_user(&vr, arg, sizeof(vr))) 14931da177e4SLinus Torvalds return -EFAULT; 14940c12295aSPatrick McHardy if (vr.vifi >= mrt->maxvif) 14951da177e4SLinus Torvalds return -EINVAL; 14961da177e4SLinus Torvalds read_lock(&mrt_lock); 14970c12295aSPatrick McHardy vif = &mrt->vif_table[vr.vifi]; 14980c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vr.vifi)) { 14991da177e4SLinus Torvalds vr.icount = vif->pkt_in; 15001da177e4SLinus Torvalds vr.ocount = vif->pkt_out; 15011da177e4SLinus Torvalds vr.ibytes = vif->bytes_in; 15021da177e4SLinus Torvalds vr.obytes = vif->bytes_out; 15031da177e4SLinus Torvalds read_unlock(&mrt_lock); 15041da177e4SLinus Torvalds 15051da177e4SLinus Torvalds if (copy_to_user(arg, &vr, sizeof(vr))) 15061da177e4SLinus Torvalds return -EFAULT; 15071da177e4SLinus Torvalds return 0; 15081da177e4SLinus Torvalds } 15091da177e4SLinus Torvalds read_unlock(&mrt_lock); 15101da177e4SLinus Torvalds return -EADDRNOTAVAIL; 15111da177e4SLinus Torvalds case SIOCGETSGCNT: 15121da177e4SLinus Torvalds if (copy_from_user(&sr, arg, sizeof(sr))) 15131da177e4SLinus Torvalds return -EFAULT; 15141da177e4SLinus Torvalds 1515a8c9486bSEric Dumazet rcu_read_lock(); 15160c12295aSPatrick McHardy c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); 15171da177e4SLinus Torvalds if (c) { 15181da177e4SLinus Torvalds sr.pktcnt = c->mfc_un.res.pkt; 15191da177e4SLinus Torvalds sr.bytecnt = c->mfc_un.res.bytes; 15201da177e4SLinus Torvalds sr.wrong_if = c->mfc_un.res.wrong_if; 1521a8c9486bSEric Dumazet rcu_read_unlock(); 15221da177e4SLinus Torvalds 15231da177e4SLinus Torvalds if (copy_to_user(arg, &sr, sizeof(sr))) 15241da177e4SLinus Torvalds return -EFAULT; 15251da177e4SLinus Torvalds return 0; 15261da177e4SLinus Torvalds } 1527a8c9486bSEric Dumazet rcu_read_unlock(); 15281da177e4SLinus Torvalds return -EADDRNOTAVAIL; 15291da177e4SLinus Torvalds default: 15301da177e4SLinus Torvalds return -ENOIOCTLCMD; 15311da177e4SLinus Torvalds } 15321da177e4SLinus Torvalds } 15331da177e4SLinus Torvalds 1534709b46e8SEric W. Biederman #ifdef CONFIG_COMPAT 1535709b46e8SEric W. Biederman struct compat_sioc_sg_req { 1536709b46e8SEric W. Biederman struct in_addr src; 1537709b46e8SEric W. Biederman struct in_addr grp; 1538709b46e8SEric W. Biederman compat_ulong_t pktcnt; 1539709b46e8SEric W. Biederman compat_ulong_t bytecnt; 1540709b46e8SEric W. Biederman compat_ulong_t wrong_if; 1541709b46e8SEric W. Biederman }; 1542709b46e8SEric W. Biederman 1543ca6b8bb0SDavid S. Miller struct compat_sioc_vif_req { 1544ca6b8bb0SDavid S. Miller vifi_t vifi; /* Which iface */ 1545ca6b8bb0SDavid S. Miller compat_ulong_t icount; 1546ca6b8bb0SDavid S. Miller compat_ulong_t ocount; 1547ca6b8bb0SDavid S. Miller compat_ulong_t ibytes; 1548ca6b8bb0SDavid S. Miller compat_ulong_t obytes; 1549ca6b8bb0SDavid S. Miller }; 1550ca6b8bb0SDavid S. Miller 1551709b46e8SEric W. Biederman int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) 1552709b46e8SEric W. Biederman { 15530033d5adSDavid S. Miller struct compat_sioc_sg_req sr; 1554ca6b8bb0SDavid S. Miller struct compat_sioc_vif_req vr; 1555ca6b8bb0SDavid S. Miller struct vif_device *vif; 1556709b46e8SEric W. Biederman struct mfc_cache *c; 1557709b46e8SEric W. Biederman struct net *net = sock_net(sk); 1558709b46e8SEric W. Biederman struct mr_table *mrt; 1559709b46e8SEric W. Biederman 1560709b46e8SEric W. Biederman mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 156151456b29SIan Morris if (!mrt) 1562709b46e8SEric W. Biederman return -ENOENT; 1563709b46e8SEric W. Biederman 1564709b46e8SEric W. Biederman switch (cmd) { 1565ca6b8bb0SDavid S. Miller case SIOCGETVIFCNT: 1566ca6b8bb0SDavid S. Miller if (copy_from_user(&vr, arg, sizeof(vr))) 1567ca6b8bb0SDavid S. Miller return -EFAULT; 1568ca6b8bb0SDavid S. Miller if (vr.vifi >= mrt->maxvif) 1569ca6b8bb0SDavid S. Miller return -EINVAL; 1570ca6b8bb0SDavid S. Miller read_lock(&mrt_lock); 1571ca6b8bb0SDavid S. Miller vif = &mrt->vif_table[vr.vifi]; 1572ca6b8bb0SDavid S. Miller if (VIF_EXISTS(mrt, vr.vifi)) { 1573ca6b8bb0SDavid S. Miller vr.icount = vif->pkt_in; 1574ca6b8bb0SDavid S. Miller vr.ocount = vif->pkt_out; 1575ca6b8bb0SDavid S. Miller vr.ibytes = vif->bytes_in; 1576ca6b8bb0SDavid S. Miller vr.obytes = vif->bytes_out; 1577ca6b8bb0SDavid S. Miller read_unlock(&mrt_lock); 1578ca6b8bb0SDavid S. Miller 1579ca6b8bb0SDavid S. Miller if (copy_to_user(arg, &vr, sizeof(vr))) 1580ca6b8bb0SDavid S. Miller return -EFAULT; 1581ca6b8bb0SDavid S. Miller return 0; 1582ca6b8bb0SDavid S. Miller } 1583ca6b8bb0SDavid S. Miller read_unlock(&mrt_lock); 1584ca6b8bb0SDavid S. Miller return -EADDRNOTAVAIL; 1585709b46e8SEric W. Biederman case SIOCGETSGCNT: 1586709b46e8SEric W. Biederman if (copy_from_user(&sr, arg, sizeof(sr))) 1587709b46e8SEric W. Biederman return -EFAULT; 1588709b46e8SEric W. Biederman 1589709b46e8SEric W. Biederman rcu_read_lock(); 1590709b46e8SEric W. Biederman c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); 1591709b46e8SEric W. Biederman if (c) { 1592709b46e8SEric W. Biederman sr.pktcnt = c->mfc_un.res.pkt; 1593709b46e8SEric W. Biederman sr.bytecnt = c->mfc_un.res.bytes; 1594709b46e8SEric W. Biederman sr.wrong_if = c->mfc_un.res.wrong_if; 1595709b46e8SEric W. Biederman rcu_read_unlock(); 1596709b46e8SEric W. Biederman 1597709b46e8SEric W. Biederman if (copy_to_user(arg, &sr, sizeof(sr))) 1598709b46e8SEric W. Biederman return -EFAULT; 1599709b46e8SEric W. Biederman return 0; 1600709b46e8SEric W. Biederman } 1601709b46e8SEric W. Biederman rcu_read_unlock(); 1602709b46e8SEric W. Biederman return -EADDRNOTAVAIL; 1603709b46e8SEric W. Biederman default: 1604709b46e8SEric W. Biederman return -ENOIOCTLCMD; 1605709b46e8SEric W. Biederman } 1606709b46e8SEric W. Biederman } 1607709b46e8SEric W. Biederman #endif 1608709b46e8SEric W. Biederman 16091da177e4SLinus Torvalds static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr) 16101da177e4SLinus Torvalds { 1611351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 16124feb88e5SBenjamin Thery struct net *net = dev_net(dev); 1613f0ad0860SPatrick McHardy struct mr_table *mrt; 16141da177e4SLinus Torvalds struct vif_device *v; 16151da177e4SLinus Torvalds int ct; 1616e9dc8653SEric W. Biederman 16171da177e4SLinus Torvalds if (event != NETDEV_UNREGISTER) 16181da177e4SLinus Torvalds return NOTIFY_DONE; 1619f0ad0860SPatrick McHardy 1620f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 16210c12295aSPatrick McHardy v = &mrt->vif_table[0]; 16220c12295aSPatrick McHardy for (ct = 0; ct < mrt->maxvif; ct++, v++) { 1623e9dc8653SEric W. Biederman if (v->dev == dev) 1624e92036a6SRongQing.Li vif_delete(mrt, ct, 1, NULL); 16251da177e4SLinus Torvalds } 16261da177e4SLinus Torvalds } 16271da177e4SLinus Torvalds return NOTIFY_DONE; 16281da177e4SLinus Torvalds } 16291da177e4SLinus Torvalds 16301da177e4SLinus Torvalds static struct notifier_block ip_mr_notifier = { 16311da177e4SLinus Torvalds .notifier_call = ipmr_device_event, 16321da177e4SLinus Torvalds }; 16331da177e4SLinus Torvalds 16347ef8f65dSNikolay Aleksandrov /* Encapsulate a packet by attaching a valid IPIP header to it. 16351da177e4SLinus Torvalds * This avoids tunnel drivers and other mess and gives us the speed so 16361da177e4SLinus Torvalds * important for multicast video. 16371da177e4SLinus Torvalds */ 1638b6a7719aSHannes Frederic Sowa static void ip_encap(struct net *net, struct sk_buff *skb, 1639b6a7719aSHannes Frederic Sowa __be32 saddr, __be32 daddr) 16401da177e4SLinus Torvalds { 16418856dfa3SArnaldo Carvalho de Melo struct iphdr *iph; 1642b71d1d42SEric Dumazet const struct iphdr *old_iph = ip_hdr(skb); 16438856dfa3SArnaldo Carvalho de Melo 16448856dfa3SArnaldo Carvalho de Melo skb_push(skb, sizeof(struct iphdr)); 1645b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 16468856dfa3SArnaldo Carvalho de Melo skb_reset_network_header(skb); 1647eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 16481da177e4SLinus Torvalds 16491da177e4SLinus Torvalds iph->version = 4; 1650e023dd64SArnaldo Carvalho de Melo iph->tos = old_iph->tos; 1651e023dd64SArnaldo Carvalho de Melo iph->ttl = old_iph->ttl; 16521da177e4SLinus Torvalds iph->frag_off = 0; 16531da177e4SLinus Torvalds iph->daddr = daddr; 16541da177e4SLinus Torvalds iph->saddr = saddr; 16551da177e4SLinus Torvalds iph->protocol = IPPROTO_IPIP; 16561da177e4SLinus Torvalds iph->ihl = 5; 16571da177e4SLinus Torvalds iph->tot_len = htons(skb->len); 1658b6a7719aSHannes Frederic Sowa ip_select_ident(net, skb, NULL); 16591da177e4SLinus Torvalds ip_send_check(iph); 16601da177e4SLinus Torvalds 16611da177e4SLinus Torvalds memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 16621da177e4SLinus Torvalds nf_reset(skb); 16631da177e4SLinus Torvalds } 16641da177e4SLinus Torvalds 16650c4b51f0SEric W. Biederman static inline int ipmr_forward_finish(struct net *net, struct sock *sk, 16660c4b51f0SEric W. Biederman struct sk_buff *skb) 16671da177e4SLinus Torvalds { 16681da177e4SLinus Torvalds struct ip_options *opt = &(IPCB(skb)->opt); 16691da177e4SLinus Torvalds 167073186df8SDavid S. Miller IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS); 167173186df8SDavid S. Miller IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len); 16721da177e4SLinus Torvalds 16731da177e4SLinus Torvalds if (unlikely(opt->optlen)) 16741da177e4SLinus Torvalds ip_forward_options(skb); 16751da177e4SLinus Torvalds 167613206b6bSEric W. Biederman return dst_output(net, sk, skb); 16771da177e4SLinus Torvalds } 16781da177e4SLinus Torvalds 16797ef8f65dSNikolay Aleksandrov /* Processing handlers for ipmr_forward */ 16801da177e4SLinus Torvalds 16810c12295aSPatrick McHardy static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, 16820c12295aSPatrick McHardy struct sk_buff *skb, struct mfc_cache *c, int vifi) 16831da177e4SLinus Torvalds { 1684eddc9ec5SArnaldo Carvalho de Melo const struct iphdr *iph = ip_hdr(skb); 16850c12295aSPatrick McHardy struct vif_device *vif = &mrt->vif_table[vifi]; 16861da177e4SLinus Torvalds struct net_device *dev; 16871da177e4SLinus Torvalds struct rtable *rt; 168831e4543dSDavid S. Miller struct flowi4 fl4; 16891da177e4SLinus Torvalds int encap = 0; 16901da177e4SLinus Torvalds 169151456b29SIan Morris if (!vif->dev) 16921da177e4SLinus Torvalds goto out_free; 16931da177e4SLinus Torvalds 16941da177e4SLinus Torvalds if (vif->flags & VIFF_REGISTER) { 16951da177e4SLinus Torvalds vif->pkt_out++; 16961da177e4SLinus Torvalds vif->bytes_out += skb->len; 1697cf3677aeSPavel Emelyanov vif->dev->stats.tx_bytes += skb->len; 1698cf3677aeSPavel Emelyanov vif->dev->stats.tx_packets++; 16990c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT); 170069ebbf58SIlpo Järvinen goto out_free; 17011da177e4SLinus Torvalds } 17021da177e4SLinus Torvalds 17031da177e4SLinus Torvalds if (vif->flags & VIFF_TUNNEL) { 170431e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, 170578fbfd8aSDavid S. Miller vif->remote, vif->local, 170678fbfd8aSDavid S. Miller 0, 0, 170778fbfd8aSDavid S. Miller IPPROTO_IPIP, 170878fbfd8aSDavid S. Miller RT_TOS(iph->tos), vif->link); 1709b23dd4feSDavid S. Miller if (IS_ERR(rt)) 17101da177e4SLinus Torvalds goto out_free; 17111da177e4SLinus Torvalds encap = sizeof(struct iphdr); 17121da177e4SLinus Torvalds } else { 171331e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, iph->daddr, 0, 171478fbfd8aSDavid S. Miller 0, 0, 171578fbfd8aSDavid S. Miller IPPROTO_IPIP, 171678fbfd8aSDavid S. Miller RT_TOS(iph->tos), vif->link); 1717b23dd4feSDavid S. Miller if (IS_ERR(rt)) 17181da177e4SLinus Torvalds goto out_free; 17191da177e4SLinus Torvalds } 17201da177e4SLinus Torvalds 1721d8d1f30bSChangli Gao dev = rt->dst.dev; 17221da177e4SLinus Torvalds 1723d8d1f30bSChangli Gao if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) { 17241da177e4SLinus Torvalds /* Do not fragment multicasts. Alas, IPv4 does not 1725a8cb16ddSEric Dumazet * allow to send ICMP, so that packets will disappear 1726a8cb16ddSEric Dumazet * to blackhole. 17271da177e4SLinus Torvalds */ 172873186df8SDavid S. Miller IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); 17291da177e4SLinus Torvalds ip_rt_put(rt); 17301da177e4SLinus Torvalds goto out_free; 17311da177e4SLinus Torvalds } 17321da177e4SLinus Torvalds 1733d8d1f30bSChangli Gao encap += LL_RESERVED_SPACE(dev) + rt->dst.header_len; 17341da177e4SLinus Torvalds 17351da177e4SLinus Torvalds if (skb_cow(skb, encap)) { 17361da177e4SLinus Torvalds ip_rt_put(rt); 17371da177e4SLinus Torvalds goto out_free; 17381da177e4SLinus Torvalds } 17391da177e4SLinus Torvalds 17401da177e4SLinus Torvalds vif->pkt_out++; 17411da177e4SLinus Torvalds vif->bytes_out += skb->len; 17421da177e4SLinus Torvalds 1743adf30907SEric Dumazet skb_dst_drop(skb); 1744d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 1745eddc9ec5SArnaldo Carvalho de Melo ip_decrease_ttl(ip_hdr(skb)); 17461da177e4SLinus Torvalds 17471da177e4SLinus Torvalds /* FIXME: forward and output firewalls used to be called here. 1748a8cb16ddSEric Dumazet * What do we do with netfilter? -- RR 1749a8cb16ddSEric Dumazet */ 17501da177e4SLinus Torvalds if (vif->flags & VIFF_TUNNEL) { 1751b6a7719aSHannes Frederic Sowa ip_encap(net, skb, vif->local, vif->remote); 17521da177e4SLinus Torvalds /* FIXME: extra output firewall step used to be here. --RR */ 17532f4c02d4SPavel Emelyanov vif->dev->stats.tx_packets++; 17542f4c02d4SPavel Emelyanov vif->dev->stats.tx_bytes += skb->len; 17551da177e4SLinus Torvalds } 17561da177e4SLinus Torvalds 1757359ebda2SShmulik Ladkani IPCB(skb)->flags |= IPSKB_FORWARDED | IPSKB_FRAG_SEGS; 17581da177e4SLinus Torvalds 17597ef8f65dSNikolay Aleksandrov /* RFC1584 teaches, that DVMRP/PIM router must deliver packets locally 17601da177e4SLinus Torvalds * not only before forwarding, but after forwarding on all output 17611da177e4SLinus Torvalds * interfaces. It is clear, if mrouter runs a multicasting 17621da177e4SLinus Torvalds * program, it should receive packets not depending to what interface 17631da177e4SLinus Torvalds * program is joined. 17641da177e4SLinus Torvalds * If we will not make it, the program will have to join on all 17651da177e4SLinus Torvalds * interfaces. On the other hand, multihoming host (or router, but 17661da177e4SLinus Torvalds * not mrouter) cannot join to more than one interface - it will 17671da177e4SLinus Torvalds * result in receiving multiple packets. 17681da177e4SLinus Torvalds */ 176929a26a56SEric W. Biederman NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, 177029a26a56SEric W. Biederman net, NULL, skb, skb->dev, dev, 17711da177e4SLinus Torvalds ipmr_forward_finish); 17721da177e4SLinus Torvalds return; 17731da177e4SLinus Torvalds 17741da177e4SLinus Torvalds out_free: 17751da177e4SLinus Torvalds kfree_skb(skb); 17761da177e4SLinus Torvalds } 17771da177e4SLinus Torvalds 17780c12295aSPatrick McHardy static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev) 17791da177e4SLinus Torvalds { 17801da177e4SLinus Torvalds int ct; 17810c12295aSPatrick McHardy 17820c12295aSPatrick McHardy for (ct = mrt->maxvif-1; ct >= 0; ct--) { 17830c12295aSPatrick McHardy if (mrt->vif_table[ct].dev == dev) 17841da177e4SLinus Torvalds break; 17851da177e4SLinus Torvalds } 17861da177e4SLinus Torvalds return ct; 17871da177e4SLinus Torvalds } 17881da177e4SLinus Torvalds 17891da177e4SLinus Torvalds /* "local" means that we should preserve one skb (for local delivery) */ 1790c4854ec8SRami Rosen static void ip_mr_forward(struct net *net, struct mr_table *mrt, 17910c12295aSPatrick McHardy struct sk_buff *skb, struct mfc_cache *cache, 17920c12295aSPatrick McHardy int local) 17931da177e4SLinus Torvalds { 17941da177e4SLinus Torvalds int psend = -1; 17951da177e4SLinus Torvalds int vif, ct; 1796660b26dcSNicolas Dichtel int true_vifi = ipmr_find_vif(mrt, skb->dev); 17971da177e4SLinus Torvalds 17981da177e4SLinus Torvalds vif = cache->mfc_parent; 17991da177e4SLinus Torvalds cache->mfc_un.res.pkt++; 18001da177e4SLinus Torvalds cache->mfc_un.res.bytes += skb->len; 180143b9e127SNikolay Aleksandrov cache->mfc_un.res.lastuse = jiffies; 18021da177e4SLinus Torvalds 1803360eb5daSNicolas Dichtel if (cache->mfc_origin == htonl(INADDR_ANY) && true_vifi >= 0) { 1804660b26dcSNicolas Dichtel struct mfc_cache *cache_proxy; 1805660b26dcSNicolas Dichtel 1806660b26dcSNicolas Dichtel /* For an (*,G) entry, we only check that the incomming 1807660b26dcSNicolas Dichtel * interface is part of the static tree. 1808660b26dcSNicolas Dichtel */ 1809660b26dcSNicolas Dichtel cache_proxy = ipmr_cache_find_any_parent(mrt, vif); 1810660b26dcSNicolas Dichtel if (cache_proxy && 1811660b26dcSNicolas Dichtel cache_proxy->mfc_un.res.ttls[true_vifi] < 255) 1812660b26dcSNicolas Dichtel goto forward; 1813660b26dcSNicolas Dichtel } 1814660b26dcSNicolas Dichtel 18157ef8f65dSNikolay Aleksandrov /* Wrong interface: drop packet and (maybe) send PIM assert. */ 18160c12295aSPatrick McHardy if (mrt->vif_table[vif].dev != skb->dev) { 1817*e58e4159SDavid Ahern struct net_device *mdev; 1818*e58e4159SDavid Ahern 1819*e58e4159SDavid Ahern mdev = l3mdev_master_dev_rcu(mrt->vif_table[vif].dev); 1820*e58e4159SDavid Ahern if (mdev == skb->dev) 1821*e58e4159SDavid Ahern goto forward; 1822*e58e4159SDavid Ahern 1823c7537967SDavid S. Miller if (rt_is_output_route(skb_rtable(skb))) { 18241da177e4SLinus Torvalds /* It is our own packet, looped back. 1825a8cb16ddSEric Dumazet * Very complicated situation... 1826a8cb16ddSEric Dumazet * 1827a8cb16ddSEric Dumazet * The best workaround until routing daemons will be 1828a8cb16ddSEric Dumazet * fixed is not to redistribute packet, if it was 1829a8cb16ddSEric Dumazet * send through wrong interface. It means, that 1830a8cb16ddSEric Dumazet * multicast applications WILL NOT work for 1831a8cb16ddSEric Dumazet * (S,G), which have default multicast route pointing 1832a8cb16ddSEric Dumazet * to wrong oif. In any case, it is not a good 1833a8cb16ddSEric Dumazet * idea to use multicasting applications on router. 18341da177e4SLinus Torvalds */ 18351da177e4SLinus Torvalds goto dont_forward; 18361da177e4SLinus Torvalds } 18371da177e4SLinus Torvalds 18381da177e4SLinus Torvalds cache->mfc_un.res.wrong_if++; 18391da177e4SLinus Torvalds 18400c12295aSPatrick McHardy if (true_vifi >= 0 && mrt->mroute_do_assert && 18411da177e4SLinus Torvalds /* pimsm uses asserts, when switching from RPT to SPT, 1842a8cb16ddSEric Dumazet * so that we cannot check that packet arrived on an oif. 1843a8cb16ddSEric Dumazet * It is bad, but otherwise we would need to move pretty 1844a8cb16ddSEric Dumazet * large chunk of pimd to kernel. Ough... --ANK 18451da177e4SLinus Torvalds */ 18460c12295aSPatrick McHardy (mrt->mroute_do_pim || 18476f9374a9SBenjamin Thery cache->mfc_un.res.ttls[true_vifi] < 255) && 18481da177e4SLinus Torvalds time_after(jiffies, 18491da177e4SLinus Torvalds cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) { 18501da177e4SLinus Torvalds cache->mfc_un.res.last_assert = jiffies; 18510c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF); 18521da177e4SLinus Torvalds } 18531da177e4SLinus Torvalds goto dont_forward; 18541da177e4SLinus Torvalds } 18551da177e4SLinus Torvalds 1856660b26dcSNicolas Dichtel forward: 18570c12295aSPatrick McHardy mrt->vif_table[vif].pkt_in++; 18580c12295aSPatrick McHardy mrt->vif_table[vif].bytes_in += skb->len; 18591da177e4SLinus Torvalds 18607ef8f65dSNikolay Aleksandrov /* Forward the frame */ 1861360eb5daSNicolas Dichtel if (cache->mfc_origin == htonl(INADDR_ANY) && 1862360eb5daSNicolas Dichtel cache->mfc_mcastgrp == htonl(INADDR_ANY)) { 1863660b26dcSNicolas Dichtel if (true_vifi >= 0 && 1864660b26dcSNicolas Dichtel true_vifi != cache->mfc_parent && 1865660b26dcSNicolas Dichtel ip_hdr(skb)->ttl > 1866660b26dcSNicolas Dichtel cache->mfc_un.res.ttls[cache->mfc_parent]) { 1867660b26dcSNicolas Dichtel /* It's an (*,*) entry and the packet is not coming from 1868660b26dcSNicolas Dichtel * the upstream: forward the packet to the upstream 1869660b26dcSNicolas Dichtel * only. 1870660b26dcSNicolas Dichtel */ 1871660b26dcSNicolas Dichtel psend = cache->mfc_parent; 1872660b26dcSNicolas Dichtel goto last_forward; 1873660b26dcSNicolas Dichtel } 1874660b26dcSNicolas Dichtel goto dont_forward; 1875660b26dcSNicolas Dichtel } 1876a8cb16ddSEric Dumazet for (ct = cache->mfc_un.res.maxvif - 1; 1877a8cb16ddSEric Dumazet ct >= cache->mfc_un.res.minvif; ct--) { 1878660b26dcSNicolas Dichtel /* For (*,G) entry, don't forward to the incoming interface */ 1879360eb5daSNicolas Dichtel if ((cache->mfc_origin != htonl(INADDR_ANY) || 1880360eb5daSNicolas Dichtel ct != true_vifi) && 1881660b26dcSNicolas Dichtel ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) { 18821da177e4SLinus Torvalds if (psend != -1) { 18831da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 1884a8cb16ddSEric Dumazet 18851da177e4SLinus Torvalds if (skb2) 18860c12295aSPatrick McHardy ipmr_queue_xmit(net, mrt, skb2, cache, 18870c12295aSPatrick McHardy psend); 18881da177e4SLinus Torvalds } 18891da177e4SLinus Torvalds psend = ct; 18901da177e4SLinus Torvalds } 18911da177e4SLinus Torvalds } 1892660b26dcSNicolas Dichtel last_forward: 18931da177e4SLinus Torvalds if (psend != -1) { 18941da177e4SLinus Torvalds if (local) { 18951da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 1896a8cb16ddSEric Dumazet 18971da177e4SLinus Torvalds if (skb2) 18980c12295aSPatrick McHardy ipmr_queue_xmit(net, mrt, skb2, cache, psend); 18991da177e4SLinus Torvalds } else { 19000c12295aSPatrick McHardy ipmr_queue_xmit(net, mrt, skb, cache, psend); 1901c4854ec8SRami Rosen return; 19021da177e4SLinus Torvalds } 19031da177e4SLinus Torvalds } 19041da177e4SLinus Torvalds 19051da177e4SLinus Torvalds dont_forward: 19061da177e4SLinus Torvalds if (!local) 19071da177e4SLinus Torvalds kfree_skb(skb); 19081da177e4SLinus Torvalds } 19091da177e4SLinus Torvalds 1910417da66fSDavid S. Miller static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb) 1911ee3f1aafSDavid S. Miller { 1912417da66fSDavid S. Miller struct rtable *rt = skb_rtable(skb); 1913417da66fSDavid S. Miller struct iphdr *iph = ip_hdr(skb); 1914da91981bSDavid S. Miller struct flowi4 fl4 = { 1915417da66fSDavid S. Miller .daddr = iph->daddr, 1916417da66fSDavid S. Miller .saddr = iph->saddr, 1917b0fe4a31SJulian Anastasov .flowi4_tos = RT_TOS(iph->tos), 19184fd551d7SDavid S. Miller .flowi4_oif = (rt_is_output_route(rt) ? 19194fd551d7SDavid S. Miller skb->dev->ifindex : 0), 19204fd551d7SDavid S. Miller .flowi4_iif = (rt_is_output_route(rt) ? 19211fb9489bSPavel Emelyanov LOOPBACK_IFINDEX : 19224fd551d7SDavid S. Miller skb->dev->ifindex), 1923b4869889SDavid Miller .flowi4_mark = skb->mark, 1924ee3f1aafSDavid S. Miller }; 1925ee3f1aafSDavid S. Miller struct mr_table *mrt; 1926ee3f1aafSDavid S. Miller int err; 1927ee3f1aafSDavid S. Miller 1928da91981bSDavid S. Miller err = ipmr_fib_lookup(net, &fl4, &mrt); 1929ee3f1aafSDavid S. Miller if (err) 1930ee3f1aafSDavid S. Miller return ERR_PTR(err); 1931ee3f1aafSDavid S. Miller return mrt; 1932ee3f1aafSDavid S. Miller } 19331da177e4SLinus Torvalds 19347ef8f65dSNikolay Aleksandrov /* Multicast packets for forwarding arrive here 19354c968709SEric Dumazet * Called with rcu_read_lock(); 19361da177e4SLinus Torvalds */ 19371da177e4SLinus Torvalds int ip_mr_input(struct sk_buff *skb) 19381da177e4SLinus Torvalds { 19391da177e4SLinus Torvalds struct mfc_cache *cache; 19404feb88e5SBenjamin Thery struct net *net = dev_net(skb->dev); 1941511c3f92SEric Dumazet int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL; 1942f0ad0860SPatrick McHardy struct mr_table *mrt; 19431da177e4SLinus Torvalds 19441da177e4SLinus Torvalds /* Packet is looped back after forward, it should not be 1945a8cb16ddSEric Dumazet * forwarded second time, but still can be delivered locally. 19461da177e4SLinus Torvalds */ 19471da177e4SLinus Torvalds if (IPCB(skb)->flags & IPSKB_FORWARDED) 19481da177e4SLinus Torvalds goto dont_forward; 19491da177e4SLinus Torvalds 1950417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 1951ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) { 1952e40dbc51SBen Greear kfree_skb(skb); 1953ee3f1aafSDavid S. Miller return PTR_ERR(mrt); 19545e2b61f7SDavid S. Miller } 19551da177e4SLinus Torvalds if (!local) { 19561da177e4SLinus Torvalds if (IPCB(skb)->opt.router_alert) { 19571da177e4SLinus Torvalds if (ip_call_ra_chain(skb)) 19581da177e4SLinus Torvalds return 0; 1959eddc9ec5SArnaldo Carvalho de Melo } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP) { 19601da177e4SLinus Torvalds /* IGMPv1 (and broken IGMPv2 implementations sort of 19614c968709SEric Dumazet * Cisco IOS <= 11.2(8)) do not put router alert 19624c968709SEric Dumazet * option to IGMP packets destined to routable 19634c968709SEric Dumazet * groups. It is very bad, because it means 19644c968709SEric Dumazet * that we can forward NO IGMP messages. 19651da177e4SLinus Torvalds */ 19664c968709SEric Dumazet struct sock *mroute_sk; 19674c968709SEric Dumazet 19684c968709SEric Dumazet mroute_sk = rcu_dereference(mrt->mroute_sk); 19694c968709SEric Dumazet if (mroute_sk) { 19702715bcf9SPatrick McHardy nf_reset(skb); 19714c968709SEric Dumazet raw_rcv(mroute_sk, skb); 19721da177e4SLinus Torvalds return 0; 19731da177e4SLinus Torvalds } 19741da177e4SLinus Torvalds } 19751da177e4SLinus Torvalds } 19761da177e4SLinus Torvalds 1977a8c9486bSEric Dumazet /* already under rcu_read_lock() */ 19780c12295aSPatrick McHardy cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); 197951456b29SIan Morris if (!cache) { 1980660b26dcSNicolas Dichtel int vif = ipmr_find_vif(mrt, skb->dev); 1981660b26dcSNicolas Dichtel 1982660b26dcSNicolas Dichtel if (vif >= 0) 1983660b26dcSNicolas Dichtel cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr, 1984660b26dcSNicolas Dichtel vif); 1985660b26dcSNicolas Dichtel } 19861da177e4SLinus Torvalds 19877ef8f65dSNikolay Aleksandrov /* No usable cache entry */ 198851456b29SIan Morris if (!cache) { 19891da177e4SLinus Torvalds int vif; 19901da177e4SLinus Torvalds 19911da177e4SLinus Torvalds if (local) { 19921da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 19931da177e4SLinus Torvalds ip_local_deliver(skb); 199451456b29SIan Morris if (!skb2) 19951da177e4SLinus Torvalds return -ENOBUFS; 19961da177e4SLinus Torvalds skb = skb2; 19971da177e4SLinus Torvalds } 19981da177e4SLinus Torvalds 1999a8c9486bSEric Dumazet read_lock(&mrt_lock); 20000c12295aSPatrick McHardy vif = ipmr_find_vif(mrt, skb->dev); 20011da177e4SLinus Torvalds if (vif >= 0) { 20020eae88f3SEric Dumazet int err2 = ipmr_cache_unresolved(mrt, vif, skb); 20031da177e4SLinus Torvalds read_unlock(&mrt_lock); 20041da177e4SLinus Torvalds 20050eae88f3SEric Dumazet return err2; 20061da177e4SLinus Torvalds } 20071da177e4SLinus Torvalds read_unlock(&mrt_lock); 20081da177e4SLinus Torvalds kfree_skb(skb); 20091da177e4SLinus Torvalds return -ENODEV; 20101da177e4SLinus Torvalds } 20111da177e4SLinus Torvalds 2012a8c9486bSEric Dumazet read_lock(&mrt_lock); 20130c12295aSPatrick McHardy ip_mr_forward(net, mrt, skb, cache, local); 20141da177e4SLinus Torvalds read_unlock(&mrt_lock); 20151da177e4SLinus Torvalds 20161da177e4SLinus Torvalds if (local) 20171da177e4SLinus Torvalds return ip_local_deliver(skb); 20181da177e4SLinus Torvalds 20191da177e4SLinus Torvalds return 0; 20201da177e4SLinus Torvalds 20211da177e4SLinus Torvalds dont_forward: 20221da177e4SLinus Torvalds if (local) 20231da177e4SLinus Torvalds return ip_local_deliver(skb); 20241da177e4SLinus Torvalds kfree_skb(skb); 20251da177e4SLinus Torvalds return 0; 20261da177e4SLinus Torvalds } 20271da177e4SLinus Torvalds 2028b1879204SIlpo Järvinen #ifdef CONFIG_IP_PIMSM_V1 20297ef8f65dSNikolay Aleksandrov /* Handle IGMP messages of PIMv1 */ 2030b1879204SIlpo Järvinen int pim_rcv_v1(struct sk_buff *skb) 2031b1879204SIlpo Järvinen { 2032b1879204SIlpo Järvinen struct igmphdr *pim; 20334feb88e5SBenjamin Thery struct net *net = dev_net(skb->dev); 2034f0ad0860SPatrick McHardy struct mr_table *mrt; 2035b1879204SIlpo Järvinen 2036b1879204SIlpo Järvinen if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 2037b1879204SIlpo Järvinen goto drop; 2038b1879204SIlpo Järvinen 2039b1879204SIlpo Järvinen pim = igmp_hdr(skb); 2040b1879204SIlpo Järvinen 2041417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2042ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) 2043f0ad0860SPatrick McHardy goto drop; 20440c12295aSPatrick McHardy if (!mrt->mroute_do_pim || 2045b1879204SIlpo Järvinen pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) 2046b1879204SIlpo Järvinen goto drop; 2047b1879204SIlpo Järvinen 2048f0ad0860SPatrick McHardy if (__pim_rcv(mrt, skb, sizeof(*pim))) { 20491da177e4SLinus Torvalds drop: 20501da177e4SLinus Torvalds kfree_skb(skb); 2051b1879204SIlpo Järvinen } 20521da177e4SLinus Torvalds return 0; 20531da177e4SLinus Torvalds } 20541da177e4SLinus Torvalds #endif 20551da177e4SLinus Torvalds 20561da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V2 20571da177e4SLinus Torvalds static int pim_rcv(struct sk_buff *skb) 20581da177e4SLinus Torvalds { 20591da177e4SLinus Torvalds struct pimreghdr *pim; 2060f0ad0860SPatrick McHardy struct net *net = dev_net(skb->dev); 2061f0ad0860SPatrick McHardy struct mr_table *mrt; 20621da177e4SLinus Torvalds 2063b1879204SIlpo Järvinen if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 20641da177e4SLinus Torvalds goto drop; 20651da177e4SLinus Torvalds 20669c70220bSArnaldo Carvalho de Melo pim = (struct pimreghdr *)skb_transport_header(skb); 206756245caeSNikolay Aleksandrov if (pim->type != ((PIM_VERSION << 4) | (PIM_TYPE_REGISTER)) || 20681da177e4SLinus Torvalds (pim->flags & PIM_NULL_REGISTER) || 20691da177e4SLinus Torvalds (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && 2070d3bc23e7SAl Viro csum_fold(skb_checksum(skb, 0, skb->len, 0)))) 20711da177e4SLinus Torvalds goto drop; 20721da177e4SLinus Torvalds 2073417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2074ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) 2075f0ad0860SPatrick McHardy goto drop; 2076f0ad0860SPatrick McHardy if (__pim_rcv(mrt, skb, sizeof(*pim))) { 20771da177e4SLinus Torvalds drop: 20781da177e4SLinus Torvalds kfree_skb(skb); 2079b1879204SIlpo Järvinen } 20801da177e4SLinus Torvalds return 0; 20811da177e4SLinus Torvalds } 20821da177e4SLinus Torvalds #endif 20831da177e4SLinus Torvalds 2084cb6a4e46SPatrick McHardy static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 2085cb6a4e46SPatrick McHardy struct mfc_cache *c, struct rtmsg *rtm) 20861da177e4SLinus Torvalds { 2087adfa85e4SNicolas Dichtel struct rta_mfc_stats mfcs; 208843b9e127SNikolay Aleksandrov struct nlattr *mp_attr; 208943b9e127SNikolay Aleksandrov struct rtnexthop *nhp; 2090b5036cd4SNikolay Aleksandrov unsigned long lastuse; 209143b9e127SNikolay Aleksandrov int ct; 20921da177e4SLinus Torvalds 20937438189bSNicolas Dichtel /* If cache is unresolved, don't try to parse IIF and OIF */ 2094ed0f160aSDan Carpenter if (c->mfc_parent >= MAXVIFS) 20957438189bSNicolas Dichtel return -ENOENT; 20967438189bSNicolas Dichtel 209792a395e5SThomas Graf if (VIF_EXISTS(mrt, c->mfc_parent) && 209892a395e5SThomas Graf nla_put_u32(skb, RTA_IIF, mrt->vif_table[c->mfc_parent].dev->ifindex) < 0) 209992a395e5SThomas Graf return -EMSGSIZE; 21001da177e4SLinus Torvalds 210192a395e5SThomas Graf if (!(mp_attr = nla_nest_start(skb, RTA_MULTIPATH))) 210292a395e5SThomas Graf return -EMSGSIZE; 21031da177e4SLinus Torvalds 21041da177e4SLinus Torvalds for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) { 21050c12295aSPatrick McHardy if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) { 210692a395e5SThomas Graf if (!(nhp = nla_reserve_nohdr(skb, sizeof(*nhp)))) { 210792a395e5SThomas Graf nla_nest_cancel(skb, mp_attr); 210892a395e5SThomas Graf return -EMSGSIZE; 210992a395e5SThomas Graf } 211092a395e5SThomas Graf 21111da177e4SLinus Torvalds nhp->rtnh_flags = 0; 21121da177e4SLinus Torvalds nhp->rtnh_hops = c->mfc_un.res.ttls[ct]; 21130c12295aSPatrick McHardy nhp->rtnh_ifindex = mrt->vif_table[ct].dev->ifindex; 21141da177e4SLinus Torvalds nhp->rtnh_len = sizeof(*nhp); 21151da177e4SLinus Torvalds } 21161da177e4SLinus Torvalds } 211792a395e5SThomas Graf 211892a395e5SThomas Graf nla_nest_end(skb, mp_attr); 211992a395e5SThomas Graf 2120b5036cd4SNikolay Aleksandrov lastuse = READ_ONCE(c->mfc_un.res.lastuse); 2121b5036cd4SNikolay Aleksandrov lastuse = time_after_eq(jiffies, lastuse) ? jiffies - lastuse : 0; 2122b5036cd4SNikolay Aleksandrov 2123adfa85e4SNicolas Dichtel mfcs.mfcs_packets = c->mfc_un.res.pkt; 2124adfa85e4SNicolas Dichtel mfcs.mfcs_bytes = c->mfc_un.res.bytes; 2125adfa85e4SNicolas Dichtel mfcs.mfcs_wrong_if = c->mfc_un.res.wrong_if; 212643b9e127SNikolay Aleksandrov if (nla_put_64bit(skb, RTA_MFC_STATS, sizeof(mfcs), &mfcs, RTA_PAD) || 2127b5036cd4SNikolay Aleksandrov nla_put_u64_64bit(skb, RTA_EXPIRES, jiffies_to_clock_t(lastuse), 212843b9e127SNikolay Aleksandrov RTA_PAD)) 2129adfa85e4SNicolas Dichtel return -EMSGSIZE; 2130adfa85e4SNicolas Dichtel 21311da177e4SLinus Torvalds rtm->rtm_type = RTN_MULTICAST; 21321da177e4SLinus Torvalds return 1; 21331da177e4SLinus Torvalds } 21341da177e4SLinus Torvalds 21359a1b9496SDavid S. Miller int ipmr_get_route(struct net *net, struct sk_buff *skb, 21369a1b9496SDavid S. Miller __be32 saddr, __be32 daddr, 21372cf75070SNikolay Aleksandrov struct rtmsg *rtm, int nowait, u32 portid) 21381da177e4SLinus Torvalds { 21391da177e4SLinus Torvalds struct mfc_cache *cache; 21409a1b9496SDavid S. Miller struct mr_table *mrt; 21419a1b9496SDavid S. Miller int err; 21421da177e4SLinus Torvalds 2143f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 214451456b29SIan Morris if (!mrt) 2145f0ad0860SPatrick McHardy return -ENOENT; 2146f0ad0860SPatrick McHardy 2147a8c9486bSEric Dumazet rcu_read_lock(); 21489a1b9496SDavid S. Miller cache = ipmr_cache_find(mrt, saddr, daddr); 214951456b29SIan Morris if (!cache && skb->dev) { 2150660b26dcSNicolas Dichtel int vif = ipmr_find_vif(mrt, skb->dev); 21511da177e4SLinus Torvalds 2152660b26dcSNicolas Dichtel if (vif >= 0) 2153660b26dcSNicolas Dichtel cache = ipmr_cache_find_any(mrt, daddr, vif); 2154660b26dcSNicolas Dichtel } 215551456b29SIan Morris if (!cache) { 215672287490SAlexey Kuznetsov struct sk_buff *skb2; 2157eddc9ec5SArnaldo Carvalho de Melo struct iphdr *iph; 21581da177e4SLinus Torvalds struct net_device *dev; 2159a8cb16ddSEric Dumazet int vif = -1; 21601da177e4SLinus Torvalds 21611da177e4SLinus Torvalds if (nowait) { 2162a8c9486bSEric Dumazet rcu_read_unlock(); 21631da177e4SLinus Torvalds return -EAGAIN; 21641da177e4SLinus Torvalds } 21651da177e4SLinus Torvalds 21661da177e4SLinus Torvalds dev = skb->dev; 2167a8c9486bSEric Dumazet read_lock(&mrt_lock); 2168a8cb16ddSEric Dumazet if (dev) 2169a8cb16ddSEric Dumazet vif = ipmr_find_vif(mrt, dev); 2170a8cb16ddSEric Dumazet if (vif < 0) { 21711da177e4SLinus Torvalds read_unlock(&mrt_lock); 2172a8c9486bSEric Dumazet rcu_read_unlock(); 21731da177e4SLinus Torvalds return -ENODEV; 21741da177e4SLinus Torvalds } 217572287490SAlexey Kuznetsov skb2 = skb_clone(skb, GFP_ATOMIC); 217672287490SAlexey Kuznetsov if (!skb2) { 217772287490SAlexey Kuznetsov read_unlock(&mrt_lock); 2178a8c9486bSEric Dumazet rcu_read_unlock(); 217972287490SAlexey Kuznetsov return -ENOMEM; 218072287490SAlexey Kuznetsov } 218172287490SAlexey Kuznetsov 21822cf75070SNikolay Aleksandrov NETLINK_CB(skb2).portid = portid; 2183e2d1bca7SArnaldo Carvalho de Melo skb_push(skb2, sizeof(struct iphdr)); 2184e2d1bca7SArnaldo Carvalho de Melo skb_reset_network_header(skb2); 2185eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb2); 2186eddc9ec5SArnaldo Carvalho de Melo iph->ihl = sizeof(struct iphdr) >> 2; 21879a1b9496SDavid S. Miller iph->saddr = saddr; 21889a1b9496SDavid S. Miller iph->daddr = daddr; 2189eddc9ec5SArnaldo Carvalho de Melo iph->version = 0; 21900c12295aSPatrick McHardy err = ipmr_cache_unresolved(mrt, vif, skb2); 21911da177e4SLinus Torvalds read_unlock(&mrt_lock); 2192a8c9486bSEric Dumazet rcu_read_unlock(); 21931da177e4SLinus Torvalds return err; 21941da177e4SLinus Torvalds } 21951da177e4SLinus Torvalds 2196a8c9486bSEric Dumazet read_lock(&mrt_lock); 2197cb6a4e46SPatrick McHardy err = __ipmr_fill_mroute(mrt, skb, cache, rtm); 21981da177e4SLinus Torvalds read_unlock(&mrt_lock); 2199a8c9486bSEric Dumazet rcu_read_unlock(); 22001da177e4SLinus Torvalds return err; 22011da177e4SLinus Torvalds } 22021da177e4SLinus Torvalds 2203cb6a4e46SPatrick McHardy static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 220465886f43SNicolas Dichtel u32 portid, u32 seq, struct mfc_cache *c, int cmd, 220565886f43SNicolas Dichtel int flags) 2206cb6a4e46SPatrick McHardy { 2207cb6a4e46SPatrick McHardy struct nlmsghdr *nlh; 2208cb6a4e46SPatrick McHardy struct rtmsg *rtm; 22091eb99af5SNicolas Dichtel int err; 2210cb6a4e46SPatrick McHardy 221165886f43SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags); 221251456b29SIan Morris if (!nlh) 2213cb6a4e46SPatrick McHardy return -EMSGSIZE; 2214cb6a4e46SPatrick McHardy 2215cb6a4e46SPatrick McHardy rtm = nlmsg_data(nlh); 2216cb6a4e46SPatrick McHardy rtm->rtm_family = RTNL_FAMILY_IPMR; 2217cb6a4e46SPatrick McHardy rtm->rtm_dst_len = 32; 2218cb6a4e46SPatrick McHardy rtm->rtm_src_len = 32; 2219cb6a4e46SPatrick McHardy rtm->rtm_tos = 0; 2220cb6a4e46SPatrick McHardy rtm->rtm_table = mrt->id; 2221f3756b79SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, mrt->id)) 2222f3756b79SDavid S. Miller goto nla_put_failure; 2223cb6a4e46SPatrick McHardy rtm->rtm_type = RTN_MULTICAST; 2224cb6a4e46SPatrick McHardy rtm->rtm_scope = RT_SCOPE_UNIVERSE; 22259a68ac72SNicolas Dichtel if (c->mfc_flags & MFC_STATIC) 22269a68ac72SNicolas Dichtel rtm->rtm_protocol = RTPROT_STATIC; 22279a68ac72SNicolas Dichtel else 22289a68ac72SNicolas Dichtel rtm->rtm_protocol = RTPROT_MROUTED; 2229cb6a4e46SPatrick McHardy rtm->rtm_flags = 0; 2230cb6a4e46SPatrick McHardy 2231930345eaSJiri Benc if (nla_put_in_addr(skb, RTA_SRC, c->mfc_origin) || 2232930345eaSJiri Benc nla_put_in_addr(skb, RTA_DST, c->mfc_mcastgrp)) 2233f3756b79SDavid S. Miller goto nla_put_failure; 22341eb99af5SNicolas Dichtel err = __ipmr_fill_mroute(mrt, skb, c, rtm); 22351eb99af5SNicolas Dichtel /* do not break the dump if cache is unresolved */ 22361eb99af5SNicolas Dichtel if (err < 0 && err != -ENOENT) 2237cb6a4e46SPatrick McHardy goto nla_put_failure; 2238cb6a4e46SPatrick McHardy 2239053c095aSJohannes Berg nlmsg_end(skb, nlh); 2240053c095aSJohannes Berg return 0; 2241cb6a4e46SPatrick McHardy 2242cb6a4e46SPatrick McHardy nla_put_failure: 2243cb6a4e46SPatrick McHardy nlmsg_cancel(skb, nlh); 2244cb6a4e46SPatrick McHardy return -EMSGSIZE; 2245cb6a4e46SPatrick McHardy } 2246cb6a4e46SPatrick McHardy 22478cd3ac9fSNicolas Dichtel static size_t mroute_msgsize(bool unresolved, int maxvif) 22488cd3ac9fSNicolas Dichtel { 22498cd3ac9fSNicolas Dichtel size_t len = 22508cd3ac9fSNicolas Dichtel NLMSG_ALIGN(sizeof(struct rtmsg)) 22518cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_TABLE */ 22528cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_SRC */ 22538cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_DST */ 22548cd3ac9fSNicolas Dichtel ; 22558cd3ac9fSNicolas Dichtel 22568cd3ac9fSNicolas Dichtel if (!unresolved) 22578cd3ac9fSNicolas Dichtel len = len 22588cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_IIF */ 22598cd3ac9fSNicolas Dichtel + nla_total_size(0) /* RTA_MULTIPATH */ 22608cd3ac9fSNicolas Dichtel + maxvif * NLA_ALIGN(sizeof(struct rtnexthop)) 22618cd3ac9fSNicolas Dichtel /* RTA_MFC_STATS */ 2262a9a08042SNicolas Dichtel + nla_total_size_64bit(sizeof(struct rta_mfc_stats)) 22638cd3ac9fSNicolas Dichtel ; 22648cd3ac9fSNicolas Dichtel 22658cd3ac9fSNicolas Dichtel return len; 22668cd3ac9fSNicolas Dichtel } 22678cd3ac9fSNicolas Dichtel 22688cd3ac9fSNicolas Dichtel static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, 22698cd3ac9fSNicolas Dichtel int cmd) 22708cd3ac9fSNicolas Dichtel { 22718cd3ac9fSNicolas Dichtel struct net *net = read_pnet(&mrt->net); 22728cd3ac9fSNicolas Dichtel struct sk_buff *skb; 22738cd3ac9fSNicolas Dichtel int err = -ENOBUFS; 22748cd3ac9fSNicolas Dichtel 22758cd3ac9fSNicolas Dichtel skb = nlmsg_new(mroute_msgsize(mfc->mfc_parent >= MAXVIFS, mrt->maxvif), 22768cd3ac9fSNicolas Dichtel GFP_ATOMIC); 227751456b29SIan Morris if (!skb) 22788cd3ac9fSNicolas Dichtel goto errout; 22798cd3ac9fSNicolas Dichtel 228065886f43SNicolas Dichtel err = ipmr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0); 22818cd3ac9fSNicolas Dichtel if (err < 0) 22828cd3ac9fSNicolas Dichtel goto errout; 22838cd3ac9fSNicolas Dichtel 22848cd3ac9fSNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE, NULL, GFP_ATOMIC); 22858cd3ac9fSNicolas Dichtel return; 22868cd3ac9fSNicolas Dichtel 22878cd3ac9fSNicolas Dichtel errout: 22888cd3ac9fSNicolas Dichtel kfree_skb(skb); 22898cd3ac9fSNicolas Dichtel if (err < 0) 22908cd3ac9fSNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE, err); 22918cd3ac9fSNicolas Dichtel } 22928cd3ac9fSNicolas Dichtel 2293cb6a4e46SPatrick McHardy static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) 2294cb6a4e46SPatrick McHardy { 2295cb6a4e46SPatrick McHardy struct net *net = sock_net(skb->sk); 2296cb6a4e46SPatrick McHardy struct mr_table *mrt; 2297cb6a4e46SPatrick McHardy struct mfc_cache *mfc; 2298cb6a4e46SPatrick McHardy unsigned int t = 0, s_t; 2299cb6a4e46SPatrick McHardy unsigned int h = 0, s_h; 2300cb6a4e46SPatrick McHardy unsigned int e = 0, s_e; 2301cb6a4e46SPatrick McHardy 2302cb6a4e46SPatrick McHardy s_t = cb->args[0]; 2303cb6a4e46SPatrick McHardy s_h = cb->args[1]; 2304cb6a4e46SPatrick McHardy s_e = cb->args[2]; 2305cb6a4e46SPatrick McHardy 2306a8c9486bSEric Dumazet rcu_read_lock(); 2307cb6a4e46SPatrick McHardy ipmr_for_each_table(mrt, net) { 2308cb6a4e46SPatrick McHardy if (t < s_t) 2309cb6a4e46SPatrick McHardy goto next_table; 2310cb6a4e46SPatrick McHardy if (t > s_t) 2311cb6a4e46SPatrick McHardy s_h = 0; 2312cb6a4e46SPatrick McHardy for (h = s_h; h < MFC_LINES; h++) { 2313a8c9486bSEric Dumazet list_for_each_entry_rcu(mfc, &mrt->mfc_cache_array[h], list) { 2314cb6a4e46SPatrick McHardy if (e < s_e) 2315cb6a4e46SPatrick McHardy goto next_entry; 2316cb6a4e46SPatrick McHardy if (ipmr_fill_mroute(mrt, skb, 231715e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 2318cb6a4e46SPatrick McHardy cb->nlh->nlmsg_seq, 231965886f43SNicolas Dichtel mfc, RTM_NEWROUTE, 232065886f43SNicolas Dichtel NLM_F_MULTI) < 0) 2321cb6a4e46SPatrick McHardy goto done; 2322cb6a4e46SPatrick McHardy next_entry: 2323cb6a4e46SPatrick McHardy e++; 2324cb6a4e46SPatrick McHardy } 2325cb6a4e46SPatrick McHardy e = s_e = 0; 2326cb6a4e46SPatrick McHardy } 23271eb99af5SNicolas Dichtel spin_lock_bh(&mfc_unres_lock); 23281eb99af5SNicolas Dichtel list_for_each_entry(mfc, &mrt->mfc_unres_queue, list) { 23291eb99af5SNicolas Dichtel if (e < s_e) 23301eb99af5SNicolas Dichtel goto next_entry2; 23311eb99af5SNicolas Dichtel if (ipmr_fill_mroute(mrt, skb, 23321eb99af5SNicolas Dichtel NETLINK_CB(cb->skb).portid, 23331eb99af5SNicolas Dichtel cb->nlh->nlmsg_seq, 233465886f43SNicolas Dichtel mfc, RTM_NEWROUTE, 233565886f43SNicolas Dichtel NLM_F_MULTI) < 0) { 23361eb99af5SNicolas Dichtel spin_unlock_bh(&mfc_unres_lock); 23371eb99af5SNicolas Dichtel goto done; 23381eb99af5SNicolas Dichtel } 23391eb99af5SNicolas Dichtel next_entry2: 23401eb99af5SNicolas Dichtel e++; 23411eb99af5SNicolas Dichtel } 23421eb99af5SNicolas Dichtel spin_unlock_bh(&mfc_unres_lock); 23431eb99af5SNicolas Dichtel e = s_e = 0; 2344cb6a4e46SPatrick McHardy s_h = 0; 2345cb6a4e46SPatrick McHardy next_table: 2346cb6a4e46SPatrick McHardy t++; 2347cb6a4e46SPatrick McHardy } 2348cb6a4e46SPatrick McHardy done: 2349a8c9486bSEric Dumazet rcu_read_unlock(); 2350cb6a4e46SPatrick McHardy 2351cb6a4e46SPatrick McHardy cb->args[2] = e; 2352cb6a4e46SPatrick McHardy cb->args[1] = h; 2353cb6a4e46SPatrick McHardy cb->args[0] = t; 2354cb6a4e46SPatrick McHardy 2355cb6a4e46SPatrick McHardy return skb->len; 2356cb6a4e46SPatrick McHardy } 2357cb6a4e46SPatrick McHardy 2358ccbb0aa6SNikolay Aleksandrov static const struct nla_policy rtm_ipmr_policy[RTA_MAX + 1] = { 2359ccbb0aa6SNikolay Aleksandrov [RTA_SRC] = { .type = NLA_U32 }, 2360ccbb0aa6SNikolay Aleksandrov [RTA_DST] = { .type = NLA_U32 }, 2361ccbb0aa6SNikolay Aleksandrov [RTA_IIF] = { .type = NLA_U32 }, 2362ccbb0aa6SNikolay Aleksandrov [RTA_TABLE] = { .type = NLA_U32 }, 2363ccbb0aa6SNikolay Aleksandrov [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2364ccbb0aa6SNikolay Aleksandrov }; 2365ccbb0aa6SNikolay Aleksandrov 2366ccbb0aa6SNikolay Aleksandrov static bool ipmr_rtm_validate_proto(unsigned char rtm_protocol) 2367ccbb0aa6SNikolay Aleksandrov { 2368ccbb0aa6SNikolay Aleksandrov switch (rtm_protocol) { 2369ccbb0aa6SNikolay Aleksandrov case RTPROT_STATIC: 2370ccbb0aa6SNikolay Aleksandrov case RTPROT_MROUTED: 2371ccbb0aa6SNikolay Aleksandrov return true; 2372ccbb0aa6SNikolay Aleksandrov } 2373ccbb0aa6SNikolay Aleksandrov return false; 2374ccbb0aa6SNikolay Aleksandrov } 2375ccbb0aa6SNikolay Aleksandrov 2376ccbb0aa6SNikolay Aleksandrov static int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl *mfcc) 2377ccbb0aa6SNikolay Aleksandrov { 2378ccbb0aa6SNikolay Aleksandrov struct rtnexthop *rtnh = nla_data(nla); 2379ccbb0aa6SNikolay Aleksandrov int remaining = nla_len(nla), vifi = 0; 2380ccbb0aa6SNikolay Aleksandrov 2381ccbb0aa6SNikolay Aleksandrov while (rtnh_ok(rtnh, remaining)) { 2382ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_ttls[vifi] = rtnh->rtnh_hops; 2383ccbb0aa6SNikolay Aleksandrov if (++vifi == MAXVIFS) 2384ccbb0aa6SNikolay Aleksandrov break; 2385ccbb0aa6SNikolay Aleksandrov rtnh = rtnh_next(rtnh, &remaining); 2386ccbb0aa6SNikolay Aleksandrov } 2387ccbb0aa6SNikolay Aleksandrov 2388ccbb0aa6SNikolay Aleksandrov return remaining > 0 ? -EINVAL : vifi; 2389ccbb0aa6SNikolay Aleksandrov } 2390ccbb0aa6SNikolay Aleksandrov 2391ccbb0aa6SNikolay Aleksandrov /* returns < 0 on error, 0 for ADD_MFC and 1 for ADD_MFC_PROXY */ 2392ccbb0aa6SNikolay Aleksandrov static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh, 2393ccbb0aa6SNikolay Aleksandrov struct mfcctl *mfcc, int *mrtsock, 2394ccbb0aa6SNikolay Aleksandrov struct mr_table **mrtret) 2395ccbb0aa6SNikolay Aleksandrov { 2396ccbb0aa6SNikolay Aleksandrov struct net_device *dev = NULL; 2397ccbb0aa6SNikolay Aleksandrov u32 tblid = RT_TABLE_DEFAULT; 2398ccbb0aa6SNikolay Aleksandrov struct mr_table *mrt; 2399ccbb0aa6SNikolay Aleksandrov struct nlattr *attr; 2400ccbb0aa6SNikolay Aleksandrov struct rtmsg *rtm; 2401ccbb0aa6SNikolay Aleksandrov int ret, rem; 2402ccbb0aa6SNikolay Aleksandrov 2403ccbb0aa6SNikolay Aleksandrov ret = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipmr_policy); 2404ccbb0aa6SNikolay Aleksandrov if (ret < 0) 2405ccbb0aa6SNikolay Aleksandrov goto out; 2406ccbb0aa6SNikolay Aleksandrov rtm = nlmsg_data(nlh); 2407ccbb0aa6SNikolay Aleksandrov 2408ccbb0aa6SNikolay Aleksandrov ret = -EINVAL; 2409ccbb0aa6SNikolay Aleksandrov if (rtm->rtm_family != RTNL_FAMILY_IPMR || rtm->rtm_dst_len != 32 || 2410ccbb0aa6SNikolay Aleksandrov rtm->rtm_type != RTN_MULTICAST || 2411ccbb0aa6SNikolay Aleksandrov rtm->rtm_scope != RT_SCOPE_UNIVERSE || 2412ccbb0aa6SNikolay Aleksandrov !ipmr_rtm_validate_proto(rtm->rtm_protocol)) 2413ccbb0aa6SNikolay Aleksandrov goto out; 2414ccbb0aa6SNikolay Aleksandrov 2415ccbb0aa6SNikolay Aleksandrov memset(mfcc, 0, sizeof(*mfcc)); 2416ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_parent = -1; 2417ccbb0aa6SNikolay Aleksandrov ret = 0; 2418ccbb0aa6SNikolay Aleksandrov nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), rem) { 2419ccbb0aa6SNikolay Aleksandrov switch (nla_type(attr)) { 2420ccbb0aa6SNikolay Aleksandrov case RTA_SRC: 2421ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_origin.s_addr = nla_get_be32(attr); 2422ccbb0aa6SNikolay Aleksandrov break; 2423ccbb0aa6SNikolay Aleksandrov case RTA_DST: 2424ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_mcastgrp.s_addr = nla_get_be32(attr); 2425ccbb0aa6SNikolay Aleksandrov break; 2426ccbb0aa6SNikolay Aleksandrov case RTA_IIF: 2427ccbb0aa6SNikolay Aleksandrov dev = __dev_get_by_index(net, nla_get_u32(attr)); 2428ccbb0aa6SNikolay Aleksandrov if (!dev) { 2429ccbb0aa6SNikolay Aleksandrov ret = -ENODEV; 2430ccbb0aa6SNikolay Aleksandrov goto out; 2431ccbb0aa6SNikolay Aleksandrov } 2432ccbb0aa6SNikolay Aleksandrov break; 2433ccbb0aa6SNikolay Aleksandrov case RTA_MULTIPATH: 2434ccbb0aa6SNikolay Aleksandrov if (ipmr_nla_get_ttls(attr, mfcc) < 0) { 2435ccbb0aa6SNikolay Aleksandrov ret = -EINVAL; 2436ccbb0aa6SNikolay Aleksandrov goto out; 2437ccbb0aa6SNikolay Aleksandrov } 2438ccbb0aa6SNikolay Aleksandrov break; 2439ccbb0aa6SNikolay Aleksandrov case RTA_PREFSRC: 2440ccbb0aa6SNikolay Aleksandrov ret = 1; 2441ccbb0aa6SNikolay Aleksandrov break; 2442ccbb0aa6SNikolay Aleksandrov case RTA_TABLE: 2443ccbb0aa6SNikolay Aleksandrov tblid = nla_get_u32(attr); 2444ccbb0aa6SNikolay Aleksandrov break; 2445ccbb0aa6SNikolay Aleksandrov } 2446ccbb0aa6SNikolay Aleksandrov } 2447ccbb0aa6SNikolay Aleksandrov mrt = ipmr_get_table(net, tblid); 2448ccbb0aa6SNikolay Aleksandrov if (!mrt) { 2449ccbb0aa6SNikolay Aleksandrov ret = -ENOENT; 2450ccbb0aa6SNikolay Aleksandrov goto out; 2451ccbb0aa6SNikolay Aleksandrov } 2452ccbb0aa6SNikolay Aleksandrov *mrtret = mrt; 2453ccbb0aa6SNikolay Aleksandrov *mrtsock = rtm->rtm_protocol == RTPROT_MROUTED ? 1 : 0; 2454ccbb0aa6SNikolay Aleksandrov if (dev) 2455ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_parent = ipmr_find_vif(mrt, dev); 2456ccbb0aa6SNikolay Aleksandrov 2457ccbb0aa6SNikolay Aleksandrov out: 2458ccbb0aa6SNikolay Aleksandrov return ret; 2459ccbb0aa6SNikolay Aleksandrov } 2460ccbb0aa6SNikolay Aleksandrov 2461ccbb0aa6SNikolay Aleksandrov /* takes care of both newroute and delroute */ 2462ccbb0aa6SNikolay Aleksandrov static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh) 2463ccbb0aa6SNikolay Aleksandrov { 2464ccbb0aa6SNikolay Aleksandrov struct net *net = sock_net(skb->sk); 2465ccbb0aa6SNikolay Aleksandrov int ret, mrtsock, parent; 2466ccbb0aa6SNikolay Aleksandrov struct mr_table *tbl; 2467ccbb0aa6SNikolay Aleksandrov struct mfcctl mfcc; 2468ccbb0aa6SNikolay Aleksandrov 2469ccbb0aa6SNikolay Aleksandrov mrtsock = 0; 2470ccbb0aa6SNikolay Aleksandrov tbl = NULL; 2471ccbb0aa6SNikolay Aleksandrov ret = rtm_to_ipmr_mfcc(net, nlh, &mfcc, &mrtsock, &tbl); 2472ccbb0aa6SNikolay Aleksandrov if (ret < 0) 2473ccbb0aa6SNikolay Aleksandrov return ret; 2474ccbb0aa6SNikolay Aleksandrov 2475ccbb0aa6SNikolay Aleksandrov parent = ret ? mfcc.mfcc_parent : -1; 2476ccbb0aa6SNikolay Aleksandrov if (nlh->nlmsg_type == RTM_NEWROUTE) 2477ccbb0aa6SNikolay Aleksandrov return ipmr_mfc_add(net, tbl, &mfcc, mrtsock, parent); 2478ccbb0aa6SNikolay Aleksandrov else 2479ccbb0aa6SNikolay Aleksandrov return ipmr_mfc_delete(tbl, &mfcc, parent); 2480ccbb0aa6SNikolay Aleksandrov } 2481ccbb0aa6SNikolay Aleksandrov 24821da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 24837ef8f65dSNikolay Aleksandrov /* The /proc interfaces to multicast routing : 2484a8cb16ddSEric Dumazet * /proc/net/ip_mr_cache & /proc/net/ip_mr_vif 24851da177e4SLinus Torvalds */ 24861da177e4SLinus Torvalds struct ipmr_vif_iter { 2487f6bb4514SBenjamin Thery struct seq_net_private p; 2488f0ad0860SPatrick McHardy struct mr_table *mrt; 24891da177e4SLinus Torvalds int ct; 24901da177e4SLinus Torvalds }; 24911da177e4SLinus Torvalds 2492f6bb4514SBenjamin Thery static struct vif_device *ipmr_vif_seq_idx(struct net *net, 2493f6bb4514SBenjamin Thery struct ipmr_vif_iter *iter, 24941da177e4SLinus Torvalds loff_t pos) 24951da177e4SLinus Torvalds { 2496f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 24970c12295aSPatrick McHardy 24980c12295aSPatrick McHardy for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) { 24990c12295aSPatrick McHardy if (!VIF_EXISTS(mrt, iter->ct)) 25001da177e4SLinus Torvalds continue; 25011da177e4SLinus Torvalds if (pos-- == 0) 25020c12295aSPatrick McHardy return &mrt->vif_table[iter->ct]; 25031da177e4SLinus Torvalds } 25041da177e4SLinus Torvalds return NULL; 25051da177e4SLinus Torvalds } 25061da177e4SLinus Torvalds 25071da177e4SLinus Torvalds static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos) 2508ba93ef74SStephen Hemminger __acquires(mrt_lock) 25091da177e4SLinus Torvalds { 2510f0ad0860SPatrick McHardy struct ipmr_vif_iter *iter = seq->private; 2511f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2512f0ad0860SPatrick McHardy struct mr_table *mrt; 2513f0ad0860SPatrick McHardy 2514f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 251551456b29SIan Morris if (!mrt) 2516f0ad0860SPatrick McHardy return ERR_PTR(-ENOENT); 2517f0ad0860SPatrick McHardy 2518f0ad0860SPatrick McHardy iter->mrt = mrt; 2519f6bb4514SBenjamin Thery 25201da177e4SLinus Torvalds read_lock(&mrt_lock); 2521f6bb4514SBenjamin Thery return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1) 25221da177e4SLinus Torvalds : SEQ_START_TOKEN; 25231da177e4SLinus Torvalds } 25241da177e4SLinus Torvalds 25251da177e4SLinus Torvalds static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos) 25261da177e4SLinus Torvalds { 25271da177e4SLinus Torvalds struct ipmr_vif_iter *iter = seq->private; 2528f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2529f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 25301da177e4SLinus Torvalds 25311da177e4SLinus Torvalds ++*pos; 25321da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 2533f6bb4514SBenjamin Thery return ipmr_vif_seq_idx(net, iter, 0); 25341da177e4SLinus Torvalds 25350c12295aSPatrick McHardy while (++iter->ct < mrt->maxvif) { 25360c12295aSPatrick McHardy if (!VIF_EXISTS(mrt, iter->ct)) 25371da177e4SLinus Torvalds continue; 25380c12295aSPatrick McHardy return &mrt->vif_table[iter->ct]; 25391da177e4SLinus Torvalds } 25401da177e4SLinus Torvalds return NULL; 25411da177e4SLinus Torvalds } 25421da177e4SLinus Torvalds 25431da177e4SLinus Torvalds static void ipmr_vif_seq_stop(struct seq_file *seq, void *v) 2544ba93ef74SStephen Hemminger __releases(mrt_lock) 25451da177e4SLinus Torvalds { 25461da177e4SLinus Torvalds read_unlock(&mrt_lock); 25471da177e4SLinus Torvalds } 25481da177e4SLinus Torvalds 25491da177e4SLinus Torvalds static int ipmr_vif_seq_show(struct seq_file *seq, void *v) 25501da177e4SLinus Torvalds { 2551f0ad0860SPatrick McHardy struct ipmr_vif_iter *iter = seq->private; 2552f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 2553f6bb4514SBenjamin Thery 25541da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 25551da177e4SLinus Torvalds seq_puts(seq, 25561da177e4SLinus Torvalds "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n"); 25571da177e4SLinus Torvalds } else { 25581da177e4SLinus Torvalds const struct vif_device *vif = v; 25591da177e4SLinus Torvalds const char *name = vif->dev ? vif->dev->name : "none"; 25601da177e4SLinus Torvalds 25611da177e4SLinus Torvalds seq_printf(seq, 25621da177e4SLinus Torvalds "%2Zd %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n", 25630c12295aSPatrick McHardy vif - mrt->vif_table, 25641da177e4SLinus Torvalds name, vif->bytes_in, vif->pkt_in, 25651da177e4SLinus Torvalds vif->bytes_out, vif->pkt_out, 25661da177e4SLinus Torvalds vif->flags, vif->local, vif->remote); 25671da177e4SLinus Torvalds } 25681da177e4SLinus Torvalds return 0; 25691da177e4SLinus Torvalds } 25701da177e4SLinus Torvalds 2571f690808eSStephen Hemminger static const struct seq_operations ipmr_vif_seq_ops = { 25721da177e4SLinus Torvalds .start = ipmr_vif_seq_start, 25731da177e4SLinus Torvalds .next = ipmr_vif_seq_next, 25741da177e4SLinus Torvalds .stop = ipmr_vif_seq_stop, 25751da177e4SLinus Torvalds .show = ipmr_vif_seq_show, 25761da177e4SLinus Torvalds }; 25771da177e4SLinus Torvalds 25781da177e4SLinus Torvalds static int ipmr_vif_open(struct inode *inode, struct file *file) 25791da177e4SLinus Torvalds { 2580f6bb4514SBenjamin Thery return seq_open_net(inode, file, &ipmr_vif_seq_ops, 2581cf7732e4SPavel Emelyanov sizeof(struct ipmr_vif_iter)); 25821da177e4SLinus Torvalds } 25831da177e4SLinus Torvalds 25849a32144eSArjan van de Ven static const struct file_operations ipmr_vif_fops = { 25851da177e4SLinus Torvalds .owner = THIS_MODULE, 25861da177e4SLinus Torvalds .open = ipmr_vif_open, 25871da177e4SLinus Torvalds .read = seq_read, 25881da177e4SLinus Torvalds .llseek = seq_lseek, 2589f6bb4514SBenjamin Thery .release = seq_release_net, 25901da177e4SLinus Torvalds }; 25911da177e4SLinus Torvalds 25921da177e4SLinus Torvalds struct ipmr_mfc_iter { 2593f6bb4514SBenjamin Thery struct seq_net_private p; 2594f0ad0860SPatrick McHardy struct mr_table *mrt; 2595862465f2SPatrick McHardy struct list_head *cache; 25961da177e4SLinus Torvalds int ct; 25971da177e4SLinus Torvalds }; 25981da177e4SLinus Torvalds 25991da177e4SLinus Torvalds 2600f6bb4514SBenjamin Thery static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net, 2601f6bb4514SBenjamin Thery struct ipmr_mfc_iter *it, loff_t pos) 26021da177e4SLinus Torvalds { 2603f0ad0860SPatrick McHardy struct mr_table *mrt = it->mrt; 26041da177e4SLinus Torvalds struct mfc_cache *mfc; 26051da177e4SLinus Torvalds 2606a8c9486bSEric Dumazet rcu_read_lock(); 2607862465f2SPatrick McHardy for (it->ct = 0; it->ct < MFC_LINES; it->ct++) { 26080c12295aSPatrick McHardy it->cache = &mrt->mfc_cache_array[it->ct]; 2609a8c9486bSEric Dumazet list_for_each_entry_rcu(mfc, it->cache, list) 26101da177e4SLinus Torvalds if (pos-- == 0) 26111da177e4SLinus Torvalds return mfc; 2612862465f2SPatrick McHardy } 2613a8c9486bSEric Dumazet rcu_read_unlock(); 26141da177e4SLinus Torvalds 26151da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 26160c12295aSPatrick McHardy it->cache = &mrt->mfc_unres_queue; 2617862465f2SPatrick McHardy list_for_each_entry(mfc, it->cache, list) 2618e258beb2SPatrick McHardy if (pos-- == 0) 26191da177e4SLinus Torvalds return mfc; 26201da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 26211da177e4SLinus Torvalds 26221da177e4SLinus Torvalds it->cache = NULL; 26231da177e4SLinus Torvalds return NULL; 26241da177e4SLinus Torvalds } 26251da177e4SLinus Torvalds 26261da177e4SLinus Torvalds 26271da177e4SLinus Torvalds static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos) 26281da177e4SLinus Torvalds { 26291da177e4SLinus Torvalds struct ipmr_mfc_iter *it = seq->private; 2630f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2631f0ad0860SPatrick McHardy struct mr_table *mrt; 2632f6bb4514SBenjamin Thery 2633f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 263451456b29SIan Morris if (!mrt) 2635f0ad0860SPatrick McHardy return ERR_PTR(-ENOENT); 2636f0ad0860SPatrick McHardy 2637f0ad0860SPatrick McHardy it->mrt = mrt; 26381da177e4SLinus Torvalds it->cache = NULL; 26391da177e4SLinus Torvalds it->ct = 0; 2640f6bb4514SBenjamin Thery return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1) 26411da177e4SLinus Torvalds : SEQ_START_TOKEN; 26421da177e4SLinus Torvalds } 26431da177e4SLinus Torvalds 26441da177e4SLinus Torvalds static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 26451da177e4SLinus Torvalds { 26461da177e4SLinus Torvalds struct mfc_cache *mfc = v; 26471da177e4SLinus Torvalds struct ipmr_mfc_iter *it = seq->private; 2648f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2649f0ad0860SPatrick McHardy struct mr_table *mrt = it->mrt; 26501da177e4SLinus Torvalds 26511da177e4SLinus Torvalds ++*pos; 26521da177e4SLinus Torvalds 26531da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 2654f6bb4514SBenjamin Thery return ipmr_mfc_seq_idx(net, seq->private, 0); 26551da177e4SLinus Torvalds 2656862465f2SPatrick McHardy if (mfc->list.next != it->cache) 2657862465f2SPatrick McHardy return list_entry(mfc->list.next, struct mfc_cache, list); 26581da177e4SLinus Torvalds 26590c12295aSPatrick McHardy if (it->cache == &mrt->mfc_unres_queue) 26601da177e4SLinus Torvalds goto end_of_list; 26611da177e4SLinus Torvalds 26620c12295aSPatrick McHardy BUG_ON(it->cache != &mrt->mfc_cache_array[it->ct]); 26631da177e4SLinus Torvalds 26641da177e4SLinus Torvalds while (++it->ct < MFC_LINES) { 26650c12295aSPatrick McHardy it->cache = &mrt->mfc_cache_array[it->ct]; 2666862465f2SPatrick McHardy if (list_empty(it->cache)) 2667862465f2SPatrick McHardy continue; 2668862465f2SPatrick McHardy return list_first_entry(it->cache, struct mfc_cache, list); 26691da177e4SLinus Torvalds } 26701da177e4SLinus Torvalds 26711da177e4SLinus Torvalds /* exhausted cache_array, show unresolved */ 2672a8c9486bSEric Dumazet rcu_read_unlock(); 26730c12295aSPatrick McHardy it->cache = &mrt->mfc_unres_queue; 26741da177e4SLinus Torvalds it->ct = 0; 26751da177e4SLinus Torvalds 26761da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 2677862465f2SPatrick McHardy if (!list_empty(it->cache)) 2678862465f2SPatrick McHardy return list_first_entry(it->cache, struct mfc_cache, list); 26791da177e4SLinus Torvalds 26801da177e4SLinus Torvalds end_of_list: 26811da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 26821da177e4SLinus Torvalds it->cache = NULL; 26831da177e4SLinus Torvalds 26841da177e4SLinus Torvalds return NULL; 26851da177e4SLinus Torvalds } 26861da177e4SLinus Torvalds 26871da177e4SLinus Torvalds static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) 26881da177e4SLinus Torvalds { 26891da177e4SLinus Torvalds struct ipmr_mfc_iter *it = seq->private; 2690f0ad0860SPatrick McHardy struct mr_table *mrt = it->mrt; 26911da177e4SLinus Torvalds 26920c12295aSPatrick McHardy if (it->cache == &mrt->mfc_unres_queue) 26931da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 26940c12295aSPatrick McHardy else if (it->cache == &mrt->mfc_cache_array[it->ct]) 2695a8c9486bSEric Dumazet rcu_read_unlock(); 26961da177e4SLinus Torvalds } 26971da177e4SLinus Torvalds 26981da177e4SLinus Torvalds static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) 26991da177e4SLinus Torvalds { 27001da177e4SLinus Torvalds int n; 27011da177e4SLinus Torvalds 27021da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 27031da177e4SLinus Torvalds seq_puts(seq, 27041da177e4SLinus Torvalds "Group Origin Iif Pkts Bytes Wrong Oifs\n"); 27051da177e4SLinus Torvalds } else { 27061da177e4SLinus Torvalds const struct mfc_cache *mfc = v; 27071da177e4SLinus Torvalds const struct ipmr_mfc_iter *it = seq->private; 2708f0ad0860SPatrick McHardy const struct mr_table *mrt = it->mrt; 27091da177e4SLinus Torvalds 27100eae88f3SEric Dumazet seq_printf(seq, "%08X %08X %-3hd", 27110eae88f3SEric Dumazet (__force u32) mfc->mfc_mcastgrp, 27120eae88f3SEric Dumazet (__force u32) mfc->mfc_origin, 27131ea472e2SBenjamin Thery mfc->mfc_parent); 27141ea472e2SBenjamin Thery 27150c12295aSPatrick McHardy if (it->cache != &mrt->mfc_unres_queue) { 27161ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 27171da177e4SLinus Torvalds mfc->mfc_un.res.pkt, 27181da177e4SLinus Torvalds mfc->mfc_un.res.bytes, 27191da177e4SLinus Torvalds mfc->mfc_un.res.wrong_if); 27201da177e4SLinus Torvalds for (n = mfc->mfc_un.res.minvif; 27211da177e4SLinus Torvalds n < mfc->mfc_un.res.maxvif; n++) { 27220c12295aSPatrick McHardy if (VIF_EXISTS(mrt, n) && 2723cf958ae3SBenjamin Thery mfc->mfc_un.res.ttls[n] < 255) 27241da177e4SLinus Torvalds seq_printf(seq, 27251da177e4SLinus Torvalds " %2d:%-3d", 27261da177e4SLinus Torvalds n, mfc->mfc_un.res.ttls[n]); 27271da177e4SLinus Torvalds } 27281ea472e2SBenjamin Thery } else { 27291ea472e2SBenjamin Thery /* unresolved mfc_caches don't contain 27301ea472e2SBenjamin Thery * pkt, bytes and wrong_if values 27311ea472e2SBenjamin Thery */ 27321ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul); 27331da177e4SLinus Torvalds } 27341da177e4SLinus Torvalds seq_putc(seq, '\n'); 27351da177e4SLinus Torvalds } 27361da177e4SLinus Torvalds return 0; 27371da177e4SLinus Torvalds } 27381da177e4SLinus Torvalds 2739f690808eSStephen Hemminger static const struct seq_operations ipmr_mfc_seq_ops = { 27401da177e4SLinus Torvalds .start = ipmr_mfc_seq_start, 27411da177e4SLinus Torvalds .next = ipmr_mfc_seq_next, 27421da177e4SLinus Torvalds .stop = ipmr_mfc_seq_stop, 27431da177e4SLinus Torvalds .show = ipmr_mfc_seq_show, 27441da177e4SLinus Torvalds }; 27451da177e4SLinus Torvalds 27461da177e4SLinus Torvalds static int ipmr_mfc_open(struct inode *inode, struct file *file) 27471da177e4SLinus Torvalds { 2748f6bb4514SBenjamin Thery return seq_open_net(inode, file, &ipmr_mfc_seq_ops, 2749cf7732e4SPavel Emelyanov sizeof(struct ipmr_mfc_iter)); 27501da177e4SLinus Torvalds } 27511da177e4SLinus Torvalds 27529a32144eSArjan van de Ven static const struct file_operations ipmr_mfc_fops = { 27531da177e4SLinus Torvalds .owner = THIS_MODULE, 27541da177e4SLinus Torvalds .open = ipmr_mfc_open, 27551da177e4SLinus Torvalds .read = seq_read, 27561da177e4SLinus Torvalds .llseek = seq_lseek, 2757f6bb4514SBenjamin Thery .release = seq_release_net, 27581da177e4SLinus Torvalds }; 27591da177e4SLinus Torvalds #endif 27601da177e4SLinus Torvalds 27611da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V2 276232613090SAlexey Dobriyan static const struct net_protocol pim_protocol = { 27631da177e4SLinus Torvalds .handler = pim_rcv, 2764403dbb97STom Goff .netns_ok = 1, 27651da177e4SLinus Torvalds }; 27661da177e4SLinus Torvalds #endif 27671da177e4SLinus Torvalds 27687ef8f65dSNikolay Aleksandrov /* Setup for IP multicast routing */ 2769cf958ae3SBenjamin Thery static int __net_init ipmr_net_init(struct net *net) 2770cf958ae3SBenjamin Thery { 2771f0ad0860SPatrick McHardy int err; 2772cf958ae3SBenjamin Thery 2773f0ad0860SPatrick McHardy err = ipmr_rules_init(net); 2774f0ad0860SPatrick McHardy if (err < 0) 2775cf958ae3SBenjamin Thery goto fail; 2776f6bb4514SBenjamin Thery 2777f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 2778f6bb4514SBenjamin Thery err = -ENOMEM; 2779d4beaa66SGao feng if (!proc_create("ip_mr_vif", 0, net->proc_net, &ipmr_vif_fops)) 2780f6bb4514SBenjamin Thery goto proc_vif_fail; 2781d4beaa66SGao feng if (!proc_create("ip_mr_cache", 0, net->proc_net, &ipmr_mfc_fops)) 2782f6bb4514SBenjamin Thery goto proc_cache_fail; 2783f6bb4514SBenjamin Thery #endif 27842bb8b26cSBenjamin Thery return 0; 27852bb8b26cSBenjamin Thery 2786f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 2787f6bb4514SBenjamin Thery proc_cache_fail: 2788ece31ffdSGao feng remove_proc_entry("ip_mr_vif", net->proc_net); 2789f6bb4514SBenjamin Thery proc_vif_fail: 2790f0ad0860SPatrick McHardy ipmr_rules_exit(net); 2791f6bb4514SBenjamin Thery #endif 2792cf958ae3SBenjamin Thery fail: 2793cf958ae3SBenjamin Thery return err; 2794cf958ae3SBenjamin Thery } 2795cf958ae3SBenjamin Thery 2796cf958ae3SBenjamin Thery static void __net_exit ipmr_net_exit(struct net *net) 2797cf958ae3SBenjamin Thery { 2798f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 2799ece31ffdSGao feng remove_proc_entry("ip_mr_cache", net->proc_net); 2800ece31ffdSGao feng remove_proc_entry("ip_mr_vif", net->proc_net); 2801f6bb4514SBenjamin Thery #endif 2802f0ad0860SPatrick McHardy ipmr_rules_exit(net); 2803cf958ae3SBenjamin Thery } 2804cf958ae3SBenjamin Thery 2805cf958ae3SBenjamin Thery static struct pernet_operations ipmr_net_ops = { 2806cf958ae3SBenjamin Thery .init = ipmr_net_init, 2807cf958ae3SBenjamin Thery .exit = ipmr_net_exit, 2808cf958ae3SBenjamin Thery }; 28091da177e4SLinus Torvalds 281003d2f897SWang Chen int __init ip_mr_init(void) 28111da177e4SLinus Torvalds { 281203d2f897SWang Chen int err; 281303d2f897SWang Chen 28141da177e4SLinus Torvalds mrt_cachep = kmem_cache_create("ip_mrt_cache", 28151da177e4SLinus Torvalds sizeof(struct mfc_cache), 2816e5d679f3SAlexey Dobriyan 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, 281720c2df83SPaul Mundt NULL); 281803d2f897SWang Chen 2819cf958ae3SBenjamin Thery err = register_pernet_subsys(&ipmr_net_ops); 2820cf958ae3SBenjamin Thery if (err) 2821cf958ae3SBenjamin Thery goto reg_pernet_fail; 2822cf958ae3SBenjamin Thery 282303d2f897SWang Chen err = register_netdevice_notifier(&ip_mr_notifier); 282403d2f897SWang Chen if (err) 282503d2f897SWang Chen goto reg_notif_fail; 2826403dbb97STom Goff #ifdef CONFIG_IP_PIMSM_V2 2827403dbb97STom Goff if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) { 2828058bd4d2SJoe Perches pr_err("%s: can't add PIM protocol\n", __func__); 2829403dbb97STom Goff err = -EAGAIN; 2830403dbb97STom Goff goto add_proto_fail; 2831403dbb97STom Goff } 2832403dbb97STom Goff #endif 2833c7ac8679SGreg Rose rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, 2834c7ac8679SGreg Rose NULL, ipmr_rtm_dumproute, NULL); 2835ccbb0aa6SNikolay Aleksandrov rtnl_register(RTNL_FAMILY_IPMR, RTM_NEWROUTE, 2836ccbb0aa6SNikolay Aleksandrov ipmr_rtm_route, NULL, NULL); 2837ccbb0aa6SNikolay Aleksandrov rtnl_register(RTNL_FAMILY_IPMR, RTM_DELROUTE, 2838ccbb0aa6SNikolay Aleksandrov ipmr_rtm_route, NULL, NULL); 283903d2f897SWang Chen return 0; 2840f6bb4514SBenjamin Thery 2841403dbb97STom Goff #ifdef CONFIG_IP_PIMSM_V2 2842403dbb97STom Goff add_proto_fail: 2843403dbb97STom Goff unregister_netdevice_notifier(&ip_mr_notifier); 2844403dbb97STom Goff #endif 2845c3e38896SBenjamin Thery reg_notif_fail: 2846cf958ae3SBenjamin Thery unregister_pernet_subsys(&ipmr_net_ops); 2847cf958ae3SBenjamin Thery reg_pernet_fail: 2848c3e38896SBenjamin Thery kmem_cache_destroy(mrt_cachep); 284903d2f897SWang Chen return err; 28501da177e4SLinus Torvalds } 2851