12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * IP multicast routing support for mrouted 3.6/3.8 41da177e4SLinus Torvalds * 5113aa838SAlan Cox * (c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk> 61da177e4SLinus Torvalds * Linux Consultancy and Custom Driver Development 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Fixes: 91da177e4SLinus Torvalds * Michael Chastain : Incorrect size of copying. 101da177e4SLinus Torvalds * Alan Cox : Added the cache manager code 111da177e4SLinus Torvalds * Alan Cox : Fixed the clone/copy bug and device race. 121da177e4SLinus Torvalds * Mike McLagan : Routing by source 131da177e4SLinus Torvalds * Malcolm Beattie : Buffer handling fixes. 141da177e4SLinus Torvalds * Alexey Kuznetsov : Double buffer free and other fixes. 151da177e4SLinus Torvalds * SVR Anand : Fixed several multicast bugs and problems. 161da177e4SLinus Torvalds * Alexey Kuznetsov : Status, optimisations and more. 171da177e4SLinus Torvalds * Brad Parker : Better behaviour on mrouted upcall 181da177e4SLinus Torvalds * overflow. 191da177e4SLinus Torvalds * Carlos Picoto : PIMv1 Support 201da177e4SLinus Torvalds * Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header 21f77f13e2SGilles Espinasse * Relax this requirement to work with older peers. 221da177e4SLinus Torvalds */ 231da177e4SLinus Torvalds 247c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 251da177e4SLinus Torvalds #include <linux/types.h> 2608009a76SAlexey Dobriyan #include <linux/cache.h> 274fc268d2SRandy Dunlap #include <linux/capability.h> 281da177e4SLinus Torvalds #include <linux/errno.h> 291da177e4SLinus Torvalds #include <linux/mm.h> 301da177e4SLinus Torvalds #include <linux/kernel.h> 311da177e4SLinus Torvalds #include <linux/fcntl.h> 321da177e4SLinus Torvalds #include <linux/stat.h> 331da177e4SLinus Torvalds #include <linux/socket.h> 341da177e4SLinus Torvalds #include <linux/in.h> 351da177e4SLinus Torvalds #include <linux/inet.h> 361da177e4SLinus Torvalds #include <linux/netdevice.h> 371da177e4SLinus Torvalds #include <linux/inetdevice.h> 381da177e4SLinus Torvalds #include <linux/igmp.h> 391da177e4SLinus Torvalds #include <linux/proc_fs.h> 401da177e4SLinus Torvalds #include <linux/seq_file.h> 411da177e4SLinus Torvalds #include <linux/mroute.h> 421da177e4SLinus Torvalds #include <linux/init.h> 4346f25dffSKris Katterjohn #include <linux/if_ether.h> 445a0e3ad6STejun Heo #include <linux/slab.h> 45457c4cbcSEric W. Biederman #include <net/net_namespace.h> 461da177e4SLinus Torvalds #include <net/ip.h> 471da177e4SLinus Torvalds #include <net/protocol.h> 481da177e4SLinus Torvalds #include <linux/skbuff.h> 4914c85021SArnaldo Carvalho de Melo #include <net/route.h> 501da177e4SLinus Torvalds #include <net/icmp.h> 511da177e4SLinus Torvalds #include <net/udp.h> 521da177e4SLinus Torvalds #include <net/raw.h> 531da177e4SLinus Torvalds #include <linux/notifier.h> 541da177e4SLinus Torvalds #include <linux/if_arp.h> 551da177e4SLinus Torvalds #include <linux/netfilter_ipv4.h> 56709b46e8SEric W. Biederman #include <linux/compat.h> 57bc3b2d7fSPaul Gortmaker #include <linux/export.h> 580eb71a9dSNeilBrown #include <linux/rhashtable.h> 59c5441932SPravin B Shelar #include <net/ip_tunnels.h> 601da177e4SLinus Torvalds #include <net/checksum.h> 61dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 62f0ad0860SPatrick McHardy #include <net/fib_rules.h> 63d67b8c61SNicolas Dichtel #include <linux/netconf.h> 643c618c1dSDavid Ahern #include <net/rtnh.h> 651da177e4SLinus Torvalds 665648451eSGustavo A. R. Silva #include <linux/nospec.h> 675648451eSGustavo A. R. Silva 68f0ad0860SPatrick McHardy struct ipmr_rule { 69f0ad0860SPatrick McHardy struct fib_rule common; 70f0ad0860SPatrick McHardy }; 71f0ad0860SPatrick McHardy 72f0ad0860SPatrick McHardy struct ipmr_result { 73f0ad0860SPatrick McHardy struct mr_table *mrt; 74f0ad0860SPatrick McHardy }; 75f0ad0860SPatrick McHardy 761da177e4SLinus Torvalds /* Big lock, protecting vif table, mrt cache and mroute socket state. 77a8cb16ddSEric Dumazet * Note that the changes are semaphored via rtnl_lock. 781da177e4SLinus Torvalds */ 791da177e4SLinus Torvalds 803f55211eSEric Dumazet static DEFINE_SPINLOCK(mrt_lock); 811da177e4SLinus Torvalds 82ebc31979SEric Dumazet static struct net_device *vif_dev_read(const struct vif_device *vif) 83ebc31979SEric Dumazet { 843f55211eSEric Dumazet return rcu_dereference(vif->dev); 85ebc31979SEric Dumazet } 86ebc31979SEric Dumazet 877ef8f65dSNikolay Aleksandrov /* Multicast router control variables */ 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds /* Special spinlock for queue of unresolved entries */ 901da177e4SLinus Torvalds static DEFINE_SPINLOCK(mfc_unres_lock); 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds /* We return to original Alan's scheme. Hash table of resolved 93a8cb16ddSEric Dumazet * entries is changed only in process context and protected 94a8cb16ddSEric Dumazet * with weak lock mrt_lock. Queue of unresolved entries is protected 95a8cb16ddSEric Dumazet * with strong spinlock mfc_unres_lock. 96a8cb16ddSEric Dumazet * 97a8cb16ddSEric Dumazet * In this case data path is free of exclusive locks at all. 981da177e4SLinus Torvalds */ 991da177e4SLinus Torvalds 10008009a76SAlexey Dobriyan static struct kmem_cache *mrt_cachep __ro_after_init; 1011da177e4SLinus Torvalds 102f0ad0860SPatrick McHardy static struct mr_table *ipmr_new_table(struct net *net, u32 id); 103acbb219dSFrancesco Ruggeri static void ipmr_free_table(struct mr_table *mrt); 104acbb219dSFrancesco Ruggeri 105c4854ec8SRami Rosen static void ip_mr_forward(struct net *net, struct mr_table *mrt, 1064b1f0d33SDonald Sharp struct net_device *dev, struct sk_buff *skb, 1074b1f0d33SDonald Sharp struct mfc_cache *cache, int local); 10864667988SEric Dumazet static int ipmr_cache_report(const struct mr_table *mrt, 1094feb88e5SBenjamin Thery struct sk_buff *pkt, vifi_t vifi, int assert); 1108cd3ac9fSNicolas Dichtel static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, 1118cd3ac9fSNicolas Dichtel int cmd); 1120b490b51SEric Dumazet static void igmpmsg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt); 113ca8d4794SCallum Sinclair static void mroute_clean_tables(struct mr_table *mrt, int flags); 114e99e88a9SKees Cook static void ipmr_expire_process(struct timer_list *t); 1151da177e4SLinus Torvalds 116f0ad0860SPatrick McHardy #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 117f0ad0860SPatrick McHardy #define ipmr_for_each_table(mrt, net) \ 118a14fbcd4SAmol Grover list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list, \ 1197013908cSAmol Grover lockdep_rtnl_is_held() || \ 1207013908cSAmol Grover list_empty(&net->ipv4.mr_tables)) 121f0ad0860SPatrick McHardy 1227b0db857SYuval Mintz static struct mr_table *ipmr_mr_table_iter(struct net *net, 1237b0db857SYuval Mintz struct mr_table *mrt) 1247b0db857SYuval Mintz { 1257b0db857SYuval Mintz struct mr_table *ret; 1267b0db857SYuval Mintz 1277b0db857SYuval Mintz if (!mrt) 1287b0db857SYuval Mintz ret = list_entry_rcu(net->ipv4.mr_tables.next, 1297b0db857SYuval Mintz struct mr_table, list); 1307b0db857SYuval Mintz else 1317b0db857SYuval Mintz ret = list_entry_rcu(mrt->list.next, 1327b0db857SYuval Mintz struct mr_table, list); 1337b0db857SYuval Mintz 1347b0db857SYuval Mintz if (&ret->list == &net->ipv4.mr_tables) 1357b0db857SYuval Mintz return NULL; 1367b0db857SYuval Mintz return ret; 1377b0db857SYuval Mintz } 1387b0db857SYuval Mintz 139f0ad0860SPatrick McHardy static struct mr_table *ipmr_get_table(struct net *net, u32 id) 140f0ad0860SPatrick McHardy { 141f0ad0860SPatrick McHardy struct mr_table *mrt; 142f0ad0860SPatrick McHardy 143f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 144f0ad0860SPatrick McHardy if (mrt->id == id) 145f0ad0860SPatrick McHardy return mrt; 146f0ad0860SPatrick McHardy } 147f0ad0860SPatrick McHardy return NULL; 148f0ad0860SPatrick McHardy } 149f0ad0860SPatrick McHardy 150da91981bSDavid S. Miller static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 151f0ad0860SPatrick McHardy struct mr_table **mrt) 152f0ad0860SPatrick McHardy { 153f0ad0860SPatrick McHardy int err; 15495f4a45dSHannes Frederic Sowa struct ipmr_result res; 15595f4a45dSHannes Frederic Sowa struct fib_lookup_arg arg = { 15695f4a45dSHannes Frederic Sowa .result = &res, 15795f4a45dSHannes Frederic Sowa .flags = FIB_LOOKUP_NOREF, 15895f4a45dSHannes Frederic Sowa }; 159f0ad0860SPatrick McHardy 160e58e4159SDavid Ahern /* update flow if oif or iif point to device enslaved to l3mdev */ 161e58e4159SDavid Ahern l3mdev_update_flow(net, flowi4_to_flowi(flp4)); 162e58e4159SDavid Ahern 163da91981bSDavid S. Miller err = fib_rules_lookup(net->ipv4.mr_rules_ops, 164da91981bSDavid S. Miller flowi4_to_flowi(flp4), 0, &arg); 165f0ad0860SPatrick McHardy if (err < 0) 166f0ad0860SPatrick McHardy return err; 167f0ad0860SPatrick McHardy *mrt = res.mrt; 168f0ad0860SPatrick McHardy return 0; 169f0ad0860SPatrick McHardy } 170f0ad0860SPatrick McHardy 171f0ad0860SPatrick McHardy static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp, 172f0ad0860SPatrick McHardy int flags, struct fib_lookup_arg *arg) 173f0ad0860SPatrick McHardy { 174f0ad0860SPatrick McHardy struct ipmr_result *res = arg->result; 175f0ad0860SPatrick McHardy struct mr_table *mrt; 176f0ad0860SPatrick McHardy 177f0ad0860SPatrick McHardy switch (rule->action) { 178f0ad0860SPatrick McHardy case FR_ACT_TO_TBL: 179f0ad0860SPatrick McHardy break; 180f0ad0860SPatrick McHardy case FR_ACT_UNREACHABLE: 181f0ad0860SPatrick McHardy return -ENETUNREACH; 182f0ad0860SPatrick McHardy case FR_ACT_PROHIBIT: 183f0ad0860SPatrick McHardy return -EACCES; 184f0ad0860SPatrick McHardy case FR_ACT_BLACKHOLE: 185f0ad0860SPatrick McHardy default: 186f0ad0860SPatrick McHardy return -EINVAL; 187f0ad0860SPatrick McHardy } 188f0ad0860SPatrick McHardy 189e58e4159SDavid Ahern arg->table = fib_rule_get_table(rule, arg); 190e58e4159SDavid Ahern 191e58e4159SDavid Ahern mrt = ipmr_get_table(rule->fr_net, arg->table); 19251456b29SIan Morris if (!mrt) 193f0ad0860SPatrick McHardy return -EAGAIN; 194f0ad0860SPatrick McHardy res->mrt = mrt; 195f0ad0860SPatrick McHardy return 0; 196f0ad0860SPatrick McHardy } 197f0ad0860SPatrick McHardy 198f0ad0860SPatrick McHardy static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) 199f0ad0860SPatrick McHardy { 200f0ad0860SPatrick McHardy return 1; 201f0ad0860SPatrick McHardy } 202f0ad0860SPatrick McHardy 203f0ad0860SPatrick McHardy static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb, 204b16fb418SRoopa Prabhu struct fib_rule_hdr *frh, struct nlattr **tb, 205b16fb418SRoopa Prabhu struct netlink_ext_ack *extack) 206f0ad0860SPatrick McHardy { 207f0ad0860SPatrick McHardy return 0; 208f0ad0860SPatrick McHardy } 209f0ad0860SPatrick McHardy 210f0ad0860SPatrick McHardy static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 211f0ad0860SPatrick McHardy struct nlattr **tb) 212f0ad0860SPatrick McHardy { 213f0ad0860SPatrick McHardy return 1; 214f0ad0860SPatrick McHardy } 215f0ad0860SPatrick McHardy 216f0ad0860SPatrick McHardy static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb, 217f0ad0860SPatrick McHardy struct fib_rule_hdr *frh) 218f0ad0860SPatrick McHardy { 219f0ad0860SPatrick McHardy frh->dst_len = 0; 220f0ad0860SPatrick McHardy frh->src_len = 0; 221f0ad0860SPatrick McHardy frh->tos = 0; 222f0ad0860SPatrick McHardy return 0; 223f0ad0860SPatrick McHardy } 224f0ad0860SPatrick McHardy 22504a6f82cSAndi Kleen static const struct fib_rules_ops __net_initconst ipmr_rules_ops_template = { 22625239ceeSPatrick McHardy .family = RTNL_FAMILY_IPMR, 227f0ad0860SPatrick McHardy .rule_size = sizeof(struct ipmr_rule), 228f0ad0860SPatrick McHardy .addr_size = sizeof(u32), 229f0ad0860SPatrick McHardy .action = ipmr_rule_action, 230f0ad0860SPatrick McHardy .match = ipmr_rule_match, 231f0ad0860SPatrick McHardy .configure = ipmr_rule_configure, 232f0ad0860SPatrick McHardy .compare = ipmr_rule_compare, 233f0ad0860SPatrick McHardy .fill = ipmr_rule_fill, 234f0ad0860SPatrick McHardy .nlgroup = RTNLGRP_IPV4_RULE, 235f0ad0860SPatrick McHardy .owner = THIS_MODULE, 236f0ad0860SPatrick McHardy }; 237f0ad0860SPatrick McHardy 238f0ad0860SPatrick McHardy static int __net_init ipmr_rules_init(struct net *net) 239f0ad0860SPatrick McHardy { 240f0ad0860SPatrick McHardy struct fib_rules_ops *ops; 241f0ad0860SPatrick McHardy struct mr_table *mrt; 242f0ad0860SPatrick McHardy int err; 243f0ad0860SPatrick McHardy 244f0ad0860SPatrick McHardy ops = fib_rules_register(&ipmr_rules_ops_template, net); 245f0ad0860SPatrick McHardy if (IS_ERR(ops)) 246f0ad0860SPatrick McHardy return PTR_ERR(ops); 247f0ad0860SPatrick McHardy 248f0ad0860SPatrick McHardy INIT_LIST_HEAD(&net->ipv4.mr_tables); 249f0ad0860SPatrick McHardy 250f0ad0860SPatrick McHardy mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 2511113ebbcSNikolay Aleksandrov if (IS_ERR(mrt)) { 2521113ebbcSNikolay Aleksandrov err = PTR_ERR(mrt); 253f0ad0860SPatrick McHardy goto err1; 254f0ad0860SPatrick McHardy } 255f0ad0860SPatrick McHardy 256*b4c1d4d9SZhengchao Shao err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT); 257f0ad0860SPatrick McHardy if (err < 0) 258f0ad0860SPatrick McHardy goto err2; 259f0ad0860SPatrick McHardy 260f0ad0860SPatrick McHardy net->ipv4.mr_rules_ops = ops; 261f0ad0860SPatrick McHardy return 0; 262f0ad0860SPatrick McHardy 263f0ad0860SPatrick McHardy err2: 2645611a006SEric Dumazet rtnl_lock(); 265f243e5a7SWANG Cong ipmr_free_table(mrt); 2665611a006SEric Dumazet rtnl_unlock(); 267f0ad0860SPatrick McHardy err1: 268f0ad0860SPatrick McHardy fib_rules_unregister(ops); 269f0ad0860SPatrick McHardy return err; 270f0ad0860SPatrick McHardy } 271f0ad0860SPatrick McHardy 272f0ad0860SPatrick McHardy static void __net_exit ipmr_rules_exit(struct net *net) 273f0ad0860SPatrick McHardy { 274f0ad0860SPatrick McHardy struct mr_table *mrt, *next; 275f0ad0860SPatrick McHardy 276696e595fSEric Dumazet ASSERT_RTNL(); 277035320d5SEric Dumazet list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) { 278035320d5SEric Dumazet list_del(&mrt->list); 279acbb219dSFrancesco Ruggeri ipmr_free_table(mrt); 280035320d5SEric Dumazet } 281f0ad0860SPatrick McHardy fib_rules_unregister(net->ipv4.mr_rules_ops); 282f0ad0860SPatrick McHardy } 2834d65b948SYotam Gigi 284b7a59557SJiri Pirko static int ipmr_rules_dump(struct net *net, struct notifier_block *nb, 285b7a59557SJiri Pirko struct netlink_ext_ack *extack) 2864d65b948SYotam Gigi { 287b7a59557SJiri Pirko return fib_rules_dump(net, nb, RTNL_FAMILY_IPMR, extack); 2884d65b948SYotam Gigi } 2894d65b948SYotam Gigi 2904d65b948SYotam Gigi static unsigned int ipmr_rules_seq_read(struct net *net) 2914d65b948SYotam Gigi { 2924d65b948SYotam Gigi return fib_rules_seq_read(net, RTNL_FAMILY_IPMR); 2934d65b948SYotam Gigi } 294478e4c2fSYotam Gigi 295478e4c2fSYotam Gigi bool ipmr_rule_default(const struct fib_rule *rule) 296478e4c2fSYotam Gigi { 297478e4c2fSYotam Gigi return fib_rule_matchall(rule) && rule->table == RT_TABLE_DEFAULT; 298478e4c2fSYotam Gigi } 299478e4c2fSYotam Gigi EXPORT_SYMBOL(ipmr_rule_default); 300f0ad0860SPatrick McHardy #else 301f0ad0860SPatrick McHardy #define ipmr_for_each_table(mrt, net) \ 302f0ad0860SPatrick McHardy for (mrt = net->ipv4.mrt; mrt; mrt = NULL) 303f0ad0860SPatrick McHardy 3047b0db857SYuval Mintz static struct mr_table *ipmr_mr_table_iter(struct net *net, 3057b0db857SYuval Mintz struct mr_table *mrt) 3067b0db857SYuval Mintz { 3077b0db857SYuval Mintz if (!mrt) 3087b0db857SYuval Mintz return net->ipv4.mrt; 3097b0db857SYuval Mintz return NULL; 3107b0db857SYuval Mintz } 3117b0db857SYuval Mintz 312f0ad0860SPatrick McHardy static struct mr_table *ipmr_get_table(struct net *net, u32 id) 313f0ad0860SPatrick McHardy { 314f0ad0860SPatrick McHardy return net->ipv4.mrt; 315f0ad0860SPatrick McHardy } 316f0ad0860SPatrick McHardy 317da91981bSDavid S. Miller static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 318f0ad0860SPatrick McHardy struct mr_table **mrt) 319f0ad0860SPatrick McHardy { 320f0ad0860SPatrick McHardy *mrt = net->ipv4.mrt; 321f0ad0860SPatrick McHardy return 0; 322f0ad0860SPatrick McHardy } 323f0ad0860SPatrick McHardy 324f0ad0860SPatrick McHardy static int __net_init ipmr_rules_init(struct net *net) 325f0ad0860SPatrick McHardy { 3261113ebbcSNikolay Aleksandrov struct mr_table *mrt; 3271113ebbcSNikolay Aleksandrov 3281113ebbcSNikolay Aleksandrov mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 3291113ebbcSNikolay Aleksandrov if (IS_ERR(mrt)) 3301113ebbcSNikolay Aleksandrov return PTR_ERR(mrt); 3311113ebbcSNikolay Aleksandrov net->ipv4.mrt = mrt; 3321113ebbcSNikolay Aleksandrov return 0; 333f0ad0860SPatrick McHardy } 334f0ad0860SPatrick McHardy 335f0ad0860SPatrick McHardy static void __net_exit ipmr_rules_exit(struct net *net) 336f0ad0860SPatrick McHardy { 337696e595fSEric Dumazet ASSERT_RTNL(); 338acbb219dSFrancesco Ruggeri ipmr_free_table(net->ipv4.mrt); 339ed785309SWANG Cong net->ipv4.mrt = NULL; 340f0ad0860SPatrick McHardy } 3414d65b948SYotam Gigi 342b7a59557SJiri Pirko static int ipmr_rules_dump(struct net *net, struct notifier_block *nb, 343b7a59557SJiri Pirko struct netlink_ext_ack *extack) 3444d65b948SYotam Gigi { 3454d65b948SYotam Gigi return 0; 3464d65b948SYotam Gigi } 3474d65b948SYotam Gigi 3484d65b948SYotam Gigi static unsigned int ipmr_rules_seq_read(struct net *net) 3494d65b948SYotam Gigi { 3504d65b948SYotam Gigi return 0; 3514d65b948SYotam Gigi } 352478e4c2fSYotam Gigi 353478e4c2fSYotam Gigi bool ipmr_rule_default(const struct fib_rule *rule) 354478e4c2fSYotam Gigi { 355478e4c2fSYotam Gigi return true; 356478e4c2fSYotam Gigi } 357478e4c2fSYotam Gigi EXPORT_SYMBOL(ipmr_rule_default); 358f0ad0860SPatrick McHardy #endif 359f0ad0860SPatrick McHardy 3608fb472c0SNikolay Aleksandrov static inline int ipmr_hash_cmp(struct rhashtable_compare_arg *arg, 3618fb472c0SNikolay Aleksandrov const void *ptr) 3628fb472c0SNikolay Aleksandrov { 3638fb472c0SNikolay Aleksandrov const struct mfc_cache_cmp_arg *cmparg = arg->key; 3642e47eeceSYu Zhe const struct mfc_cache *c = ptr; 3658fb472c0SNikolay Aleksandrov 3668fb472c0SNikolay Aleksandrov return cmparg->mfc_mcastgrp != c->mfc_mcastgrp || 3678fb472c0SNikolay Aleksandrov cmparg->mfc_origin != c->mfc_origin; 3688fb472c0SNikolay Aleksandrov } 3698fb472c0SNikolay Aleksandrov 3708fb472c0SNikolay Aleksandrov static const struct rhashtable_params ipmr_rht_params = { 371494fff56SYuval Mintz .head_offset = offsetof(struct mr_mfc, mnode), 3728fb472c0SNikolay Aleksandrov .key_offset = offsetof(struct mfc_cache, cmparg), 3738fb472c0SNikolay Aleksandrov .key_len = sizeof(struct mfc_cache_cmp_arg), 3748fb472c0SNikolay Aleksandrov .nelem_hint = 3, 3758fb472c0SNikolay Aleksandrov .obj_cmpfn = ipmr_hash_cmp, 3768fb472c0SNikolay Aleksandrov .automatic_shrinking = true, 3778fb472c0SNikolay Aleksandrov }; 3788fb472c0SNikolay Aleksandrov 3790bbbf0e7SYuval Mintz static void ipmr_new_table_set(struct mr_table *mrt, 3800bbbf0e7SYuval Mintz struct net *net) 3810bbbf0e7SYuval Mintz { 3820bbbf0e7SYuval Mintz #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 3830bbbf0e7SYuval Mintz list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables); 3840bbbf0e7SYuval Mintz #endif 3850bbbf0e7SYuval Mintz } 3860bbbf0e7SYuval Mintz 387845c9a7aSYuval Mintz static struct mfc_cache_cmp_arg ipmr_mr_table_ops_cmparg_any = { 388845c9a7aSYuval Mintz .mfc_mcastgrp = htonl(INADDR_ANY), 389845c9a7aSYuval Mintz .mfc_origin = htonl(INADDR_ANY), 390845c9a7aSYuval Mintz }; 391845c9a7aSYuval Mintz 392845c9a7aSYuval Mintz static struct mr_table_ops ipmr_mr_table_ops = { 393845c9a7aSYuval Mintz .rht_params = &ipmr_rht_params, 394845c9a7aSYuval Mintz .cmparg_any = &ipmr_mr_table_ops_cmparg_any, 395845c9a7aSYuval Mintz }; 396845c9a7aSYuval Mintz 397f0ad0860SPatrick McHardy static struct mr_table *ipmr_new_table(struct net *net, u32 id) 398f0ad0860SPatrick McHardy { 399f0ad0860SPatrick McHardy struct mr_table *mrt; 400f0ad0860SPatrick McHardy 4011113ebbcSNikolay Aleksandrov /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */ 4021113ebbcSNikolay Aleksandrov if (id != RT_TABLE_DEFAULT && id >= 1000000000) 4031113ebbcSNikolay Aleksandrov return ERR_PTR(-EINVAL); 4041113ebbcSNikolay Aleksandrov 405f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, id); 40600db4124SIan Morris if (mrt) 407f0ad0860SPatrick McHardy return mrt; 408f0ad0860SPatrick McHardy 409845c9a7aSYuval Mintz return mr_table_alloc(net, id, &ipmr_mr_table_ops, 4100bbbf0e7SYuval Mintz ipmr_expire_process, ipmr_new_table_set); 411f0ad0860SPatrick McHardy } 4121da177e4SLinus Torvalds 413acbb219dSFrancesco Ruggeri static void ipmr_free_table(struct mr_table *mrt) 414acbb219dSFrancesco Ruggeri { 415292a089dSSteven Rostedt (Google) timer_shutdown_sync(&mrt->ipmr_expire_timer); 416ca8d4794SCallum Sinclair mroute_clean_tables(mrt, MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC | 417ca8d4794SCallum Sinclair MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC); 4188fb472c0SNikolay Aleksandrov rhltable_destroy(&mrt->mfc_hash); 419acbb219dSFrancesco Ruggeri kfree(mrt); 420acbb219dSFrancesco Ruggeri } 421acbb219dSFrancesco Ruggeri 4221da177e4SLinus Torvalds /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */ 4231da177e4SLinus Torvalds 424a0b47736SNikolay Aleksandrov /* Initialize ipmr pimreg/tunnel in_device */ 425a0b47736SNikolay Aleksandrov static bool ipmr_init_vif_indev(const struct net_device *dev) 426a0b47736SNikolay Aleksandrov { 427a0b47736SNikolay Aleksandrov struct in_device *in_dev; 428a0b47736SNikolay Aleksandrov 429a0b47736SNikolay Aleksandrov ASSERT_RTNL(); 430a0b47736SNikolay Aleksandrov 431a0b47736SNikolay Aleksandrov in_dev = __in_dev_get_rtnl(dev); 432a0b47736SNikolay Aleksandrov if (!in_dev) 433a0b47736SNikolay Aleksandrov return false; 434a0b47736SNikolay Aleksandrov ipv4_devconf_setall(in_dev); 435a0b47736SNikolay Aleksandrov neigh_parms_data_state_setall(in_dev->arp_parms); 436a0b47736SNikolay Aleksandrov IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; 437a0b47736SNikolay Aleksandrov 438a0b47736SNikolay Aleksandrov return true; 439a0b47736SNikolay Aleksandrov } 440a0b47736SNikolay Aleksandrov 4417ef8f65dSNikolay Aleksandrov static struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v) 4421da177e4SLinus Torvalds { 443c384b8a7SChristoph Hellwig struct net_device *tunnel_dev, *new_dev; 444c384b8a7SChristoph Hellwig struct ip_tunnel_parm p = { }; 445c384b8a7SChristoph Hellwig int err; 4461da177e4SLinus Torvalds 447c384b8a7SChristoph Hellwig tunnel_dev = __dev_get_by_name(net, "tunl0"); 448c384b8a7SChristoph Hellwig if (!tunnel_dev) 449c384b8a7SChristoph Hellwig goto out; 450c384b8a7SChristoph Hellwig 4511da177e4SLinus Torvalds p.iph.daddr = v->vifc_rmt_addr.s_addr; 4521da177e4SLinus Torvalds p.iph.saddr = v->vifc_lcl_addr.s_addr; 4531da177e4SLinus Torvalds p.iph.version = 4; 4541da177e4SLinus Torvalds p.iph.ihl = 5; 4551da177e4SLinus Torvalds p.iph.protocol = IPPROTO_IPIP; 4561da177e4SLinus Torvalds sprintf(p.name, "dvmrp%d", v->vifc_vifi); 4571da177e4SLinus Torvalds 458c7e36705SChristoph Hellwig if (!tunnel_dev->netdev_ops->ndo_tunnel_ctl) 459c384b8a7SChristoph Hellwig goto out; 460c7e36705SChristoph Hellwig err = tunnel_dev->netdev_ops->ndo_tunnel_ctl(tunnel_dev, &p, 461c384b8a7SChristoph Hellwig SIOCADDTUNNEL); 462c384b8a7SChristoph Hellwig if (err) 463c384b8a7SChristoph Hellwig goto out; 4641da177e4SLinus Torvalds 465c384b8a7SChristoph Hellwig new_dev = __dev_get_by_name(net, p.name); 466c384b8a7SChristoph Hellwig if (!new_dev) 467c384b8a7SChristoph Hellwig goto out; 4681da177e4SLinus Torvalds 469c384b8a7SChristoph Hellwig new_dev->flags |= IFF_MULTICAST; 470c384b8a7SChristoph Hellwig if (!ipmr_init_vif_indev(new_dev)) 471c384b8a7SChristoph Hellwig goto out_unregister; 472c384b8a7SChristoph Hellwig if (dev_open(new_dev, NULL)) 473c384b8a7SChristoph Hellwig goto out_unregister; 474c384b8a7SChristoph Hellwig dev_hold(new_dev); 475c1fd1182SChristoph Hellwig err = dev_set_allmulti(new_dev, 1); 476c1fd1182SChristoph Hellwig if (err) { 477c1fd1182SChristoph Hellwig dev_close(new_dev); 478c7e36705SChristoph Hellwig tunnel_dev->netdev_ops->ndo_tunnel_ctl(tunnel_dev, &p, 479c1fd1182SChristoph Hellwig SIOCDELTUNNEL); 480c1fd1182SChristoph Hellwig dev_put(new_dev); 481c1fd1182SChristoph Hellwig new_dev = ERR_PTR(err); 482c1fd1182SChristoph Hellwig } 483c384b8a7SChristoph Hellwig return new_dev; 484c384b8a7SChristoph Hellwig 485c384b8a7SChristoph Hellwig out_unregister: 486c384b8a7SChristoph Hellwig unregister_netdevice(new_dev); 487c384b8a7SChristoph Hellwig out: 488c1fd1182SChristoph Hellwig return ERR_PTR(-ENOBUFS); 4891da177e4SLinus Torvalds } 4901da177e4SLinus Torvalds 491c316c629SNikolay Aleksandrov #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) 4926fef4c0cSStephen Hemminger static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) 4931da177e4SLinus Torvalds { 4944feb88e5SBenjamin Thery struct net *net = dev_net(dev); 495f0ad0860SPatrick McHardy struct mr_table *mrt; 496da91981bSDavid S. Miller struct flowi4 fl4 = { 497da91981bSDavid S. Miller .flowi4_oif = dev->ifindex, 4986a662719SCong Wang .flowi4_iif = skb->skb_iif ? : LOOPBACK_IFINDEX, 499da91981bSDavid S. Miller .flowi4_mark = skb->mark, 500f0ad0860SPatrick McHardy }; 501f0ad0860SPatrick McHardy int err; 502f0ad0860SPatrick McHardy 503da91981bSDavid S. Miller err = ipmr_fib_lookup(net, &fl4, &mrt); 504e40dbc51SBen Greear if (err < 0) { 505e40dbc51SBen Greear kfree_skb(skb); 506f0ad0860SPatrick McHardy return err; 507e40dbc51SBen Greear } 5084feb88e5SBenjamin Thery 509c4794d22SEric Dumazet DEV_STATS_ADD(dev, tx_bytes, skb->len); 510c4794d22SEric Dumazet DEV_STATS_INC(dev, tx_packets); 51164667988SEric Dumazet rcu_read_lock(); 51264667988SEric Dumazet 51364667988SEric Dumazet /* Pairs with WRITE_ONCE() in vif_add() and vif_delete() */ 51464667988SEric Dumazet ipmr_cache_report(mrt, skb, READ_ONCE(mrt->mroute_reg_vif_num), 51564667988SEric Dumazet IGMPMSG_WHOLEPKT); 51664667988SEric Dumazet 51764667988SEric Dumazet rcu_read_unlock(); 5181da177e4SLinus Torvalds kfree_skb(skb); 5196ed10654SPatrick McHardy return NETDEV_TX_OK; 5201da177e4SLinus Torvalds } 5211da177e4SLinus Torvalds 522ee9b9596SNicolas Dichtel static int reg_vif_get_iflink(const struct net_device *dev) 523ee9b9596SNicolas Dichtel { 524ee9b9596SNicolas Dichtel return 0; 525ee9b9596SNicolas Dichtel } 526ee9b9596SNicolas Dichtel 527007c3838SStephen Hemminger static const struct net_device_ops reg_vif_netdev_ops = { 528007c3838SStephen Hemminger .ndo_start_xmit = reg_vif_xmit, 529ee9b9596SNicolas Dichtel .ndo_get_iflink = reg_vif_get_iflink, 530007c3838SStephen Hemminger }; 531007c3838SStephen Hemminger 5321da177e4SLinus Torvalds static void reg_vif_setup(struct net_device *dev) 5331da177e4SLinus Torvalds { 5341da177e4SLinus Torvalds dev->type = ARPHRD_PIMREG; 53546f25dffSKris Katterjohn dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8; 5361da177e4SLinus Torvalds dev->flags = IFF_NOARP; 53770cb4a45SHimangi Saraogi dev->netdev_ops = ®_vif_netdev_ops; 538cf124db5SDavid S. Miller dev->needs_free_netdev = true; 539403dbb97STom Goff dev->features |= NETIF_F_NETNS_LOCAL; 5401da177e4SLinus Torvalds } 5411da177e4SLinus Torvalds 542f0ad0860SPatrick McHardy static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) 5431da177e4SLinus Torvalds { 5441da177e4SLinus Torvalds struct net_device *dev; 545f0ad0860SPatrick McHardy char name[IFNAMSIZ]; 5461da177e4SLinus Torvalds 547f0ad0860SPatrick McHardy if (mrt->id == RT_TABLE_DEFAULT) 548f0ad0860SPatrick McHardy sprintf(name, "pimreg"); 549f0ad0860SPatrick McHardy else 550f0ad0860SPatrick McHardy sprintf(name, "pimreg%u", mrt->id); 551f0ad0860SPatrick McHardy 552c835a677STom Gundersen dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup); 5531da177e4SLinus Torvalds 55451456b29SIan Morris if (!dev) 5551da177e4SLinus Torvalds return NULL; 5561da177e4SLinus Torvalds 557403dbb97STom Goff dev_net_set(dev, net); 558403dbb97STom Goff 5591da177e4SLinus Torvalds if (register_netdevice(dev)) { 5601da177e4SLinus Torvalds free_netdev(dev); 5611da177e4SLinus Torvalds return NULL; 5621da177e4SLinus Torvalds } 5631da177e4SLinus Torvalds 564a0b47736SNikolay Aleksandrov if (!ipmr_init_vif_indev(dev)) 5651da177e4SLinus Torvalds goto failure; 56600f54e68SPetr Machata if (dev_open(dev, NULL)) 5671da177e4SLinus Torvalds goto failure; 5681da177e4SLinus Torvalds 5697dc00c82SWang Chen dev_hold(dev); 5707dc00c82SWang Chen 5711da177e4SLinus Torvalds return dev; 5721da177e4SLinus Torvalds 5731da177e4SLinus Torvalds failure: 5741da177e4SLinus Torvalds unregister_netdevice(dev); 5751da177e4SLinus Torvalds return NULL; 5761da177e4SLinus Torvalds } 577c316c629SNikolay Aleksandrov 578c316c629SNikolay Aleksandrov /* called with rcu_read_lock() */ 579c316c629SNikolay Aleksandrov static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb, 580c316c629SNikolay Aleksandrov unsigned int pimlen) 581c316c629SNikolay Aleksandrov { 582c316c629SNikolay Aleksandrov struct net_device *reg_dev = NULL; 583c316c629SNikolay Aleksandrov struct iphdr *encap; 584121fefc6SEric Dumazet int vif_num; 585c316c629SNikolay Aleksandrov 586c316c629SNikolay Aleksandrov encap = (struct iphdr *)(skb_transport_header(skb) + pimlen); 5877ef8f65dSNikolay Aleksandrov /* Check that: 588c316c629SNikolay Aleksandrov * a. packet is really sent to a multicast group 589c316c629SNikolay Aleksandrov * b. packet is not a NULL-REGISTER 590c316c629SNikolay Aleksandrov * c. packet is not truncated 591c316c629SNikolay Aleksandrov */ 592c316c629SNikolay Aleksandrov if (!ipv4_is_multicast(encap->daddr) || 593c316c629SNikolay Aleksandrov encap->tot_len == 0 || 594c316c629SNikolay Aleksandrov ntohs(encap->tot_len) + pimlen > skb->len) 595c316c629SNikolay Aleksandrov return 1; 596c316c629SNikolay Aleksandrov 597121fefc6SEric Dumazet /* Pairs with WRITE_ONCE() in vif_add()/vid_delete() */ 598121fefc6SEric Dumazet vif_num = READ_ONCE(mrt->mroute_reg_vif_num); 599121fefc6SEric Dumazet if (vif_num >= 0) 600121fefc6SEric Dumazet reg_dev = vif_dev_read(&mrt->vif_table[vif_num]); 601c316c629SNikolay Aleksandrov if (!reg_dev) 602c316c629SNikolay Aleksandrov return 1; 603c316c629SNikolay Aleksandrov 604c316c629SNikolay Aleksandrov skb->mac_header = skb->network_header; 605c316c629SNikolay Aleksandrov skb_pull(skb, (u8 *)encap - skb->data); 606c316c629SNikolay Aleksandrov skb_reset_network_header(skb); 607c316c629SNikolay Aleksandrov skb->protocol = htons(ETH_P_IP); 608c316c629SNikolay Aleksandrov skb->ip_summed = CHECKSUM_NONE; 609c316c629SNikolay Aleksandrov 610c316c629SNikolay Aleksandrov skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev)); 611c316c629SNikolay Aleksandrov 612c316c629SNikolay Aleksandrov netif_rx(skb); 613c316c629SNikolay Aleksandrov 614c316c629SNikolay Aleksandrov return NET_RX_SUCCESS; 615c316c629SNikolay Aleksandrov } 616c316c629SNikolay Aleksandrov #else 617c316c629SNikolay Aleksandrov static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) 618c316c629SNikolay Aleksandrov { 619c316c629SNikolay Aleksandrov return NULL; 620c316c629SNikolay Aleksandrov } 6211da177e4SLinus Torvalds #endif 6221da177e4SLinus Torvalds 623b362053aSYotam Gigi static int call_ipmr_vif_entry_notifiers(struct net *net, 624b362053aSYotam Gigi enum fib_event_type event_type, 625b362053aSYotam Gigi struct vif_device *vif, 626ebc31979SEric Dumazet struct net_device *vif_dev, 627b362053aSYotam Gigi vifi_t vif_index, u32 tb_id) 628b362053aSYotam Gigi { 629bc67a0daSYuval Mintz return mr_call_vif_notifiers(net, RTNL_FAMILY_IPMR, event_type, 630ebc31979SEric Dumazet vif, vif_dev, vif_index, tb_id, 631bc67a0daSYuval Mintz &net->ipv4.ipmr_seq); 632b362053aSYotam Gigi } 633b362053aSYotam Gigi 634b362053aSYotam Gigi static int call_ipmr_mfc_entry_notifiers(struct net *net, 635b362053aSYotam Gigi enum fib_event_type event_type, 636b362053aSYotam Gigi struct mfc_cache *mfc, u32 tb_id) 637b362053aSYotam Gigi { 63854c4cad9SYuval Mintz return mr_call_mfc_notifiers(net, RTNL_FAMILY_IPMR, event_type, 63954c4cad9SYuval Mintz &mfc->_c, tb_id, &net->ipv4.ipmr_seq); 640b362053aSYotam Gigi } 641b362053aSYotam Gigi 6422c53040fSBen Hutchings /** 6432c53040fSBen Hutchings * vif_delete - Delete a VIF entry 6443628e3cbSAndrew Lunn * @mrt: Table to delete from 6453628e3cbSAndrew Lunn * @vifi: VIF identifier to delete 6467dc00c82SWang Chen * @notify: Set to 1, if the caller is a notifier_call 6473628e3cbSAndrew Lunn * @head: if unregistering the VIF, place it on this queue 6481da177e4SLinus Torvalds */ 6490c12295aSPatrick McHardy static int vif_delete(struct mr_table *mrt, int vifi, int notify, 650d17fa6faSEric Dumazet struct list_head *head) 6511da177e4SLinus Torvalds { 652b362053aSYotam Gigi struct net *net = read_pnet(&mrt->net); 6531da177e4SLinus Torvalds struct vif_device *v; 6541da177e4SLinus Torvalds struct net_device *dev; 6551da177e4SLinus Torvalds struct in_device *in_dev; 6561da177e4SLinus Torvalds 6570c12295aSPatrick McHardy if (vifi < 0 || vifi >= mrt->maxvif) 6581da177e4SLinus Torvalds return -EADDRNOTAVAIL; 6591da177e4SLinus Torvalds 6600c12295aSPatrick McHardy v = &mrt->vif_table[vifi]; 6611da177e4SLinus Torvalds 662ebc31979SEric Dumazet dev = rtnl_dereference(v->dev); 663ebc31979SEric Dumazet if (!dev) 664ebc31979SEric Dumazet return -EADDRNOTAVAIL; 665b362053aSYotam Gigi 6663f55211eSEric Dumazet spin_lock(&mrt_lock); 667ebc31979SEric Dumazet call_ipmr_vif_entry_notifiers(net, FIB_EVENT_VIF_DEL, v, dev, 668ebc31979SEric Dumazet vifi, mrt->id); 669ebc31979SEric Dumazet RCU_INIT_POINTER(v->dev, NULL); 6701da177e4SLinus Torvalds 67164667988SEric Dumazet if (vifi == mrt->mroute_reg_vif_num) { 67264667988SEric Dumazet /* Pairs with READ_ONCE() in ipmr_cache_report() and reg_vif_xmit() */ 67364667988SEric Dumazet WRITE_ONCE(mrt->mroute_reg_vif_num, -1); 67464667988SEric Dumazet } 6750c12295aSPatrick McHardy if (vifi + 1 == mrt->maxvif) { 6761da177e4SLinus Torvalds int tmp; 677a8cb16ddSEric Dumazet 6781da177e4SLinus Torvalds for (tmp = vifi - 1; tmp >= 0; tmp--) { 6790c12295aSPatrick McHardy if (VIF_EXISTS(mrt, tmp)) 6801da177e4SLinus Torvalds break; 6811da177e4SLinus Torvalds } 6829094db4bSEric Dumazet WRITE_ONCE(mrt->maxvif, tmp + 1); 6831da177e4SLinus Torvalds } 6841da177e4SLinus Torvalds 6853f55211eSEric Dumazet spin_unlock(&mrt_lock); 6861da177e4SLinus Torvalds 6871da177e4SLinus Torvalds dev_set_allmulti(dev, -1); 6881da177e4SLinus Torvalds 689a8cb16ddSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 690a8cb16ddSEric Dumazet if (in_dev) { 69142f811b8SHerbert Xu IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--; 6923b022865SDavid Ahern inet_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, 693d67b8c61SNicolas Dichtel NETCONFA_MC_FORWARDING, 694d67b8c61SNicolas Dichtel dev->ifindex, &in_dev->cnf); 6951da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 6961da177e4SLinus Torvalds } 6971da177e4SLinus Torvalds 6987dc00c82SWang Chen if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER) && !notify) 699d17fa6faSEric Dumazet unregister_netdevice_queue(dev, head); 7001da177e4SLinus Torvalds 701d62607c3SJakub Kicinski netdev_put(dev, &v->dev_tracker); 7021da177e4SLinus Torvalds return 0; 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds 705a8c9486bSEric Dumazet static void ipmr_cache_free_rcu(struct rcu_head *head) 706a8c9486bSEric Dumazet { 707494fff56SYuval Mintz struct mr_mfc *c = container_of(head, struct mr_mfc, rcu); 708a8c9486bSEric Dumazet 709494fff56SYuval Mintz kmem_cache_free(mrt_cachep, (struct mfc_cache *)c); 710a8c9486bSEric Dumazet } 711a8c9486bSEric Dumazet 7128c13af2aSYuval Mintz static void ipmr_cache_free(struct mfc_cache *c) 7135c0a66f5SBenjamin Thery { 714494fff56SYuval Mintz call_rcu(&c->_c.rcu, ipmr_cache_free_rcu); 7155c0a66f5SBenjamin Thery } 7165c0a66f5SBenjamin Thery 7171da177e4SLinus Torvalds /* Destroy an unresolved cache entry, killing queued skbs 718a8cb16ddSEric Dumazet * and reporting error to netlink readers. 7191da177e4SLinus Torvalds */ 7200c12295aSPatrick McHardy static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c) 7211da177e4SLinus Torvalds { 7228de53dfbSPatrick McHardy struct net *net = read_pnet(&mrt->net); 7231da177e4SLinus Torvalds struct sk_buff *skb; 7249ef1d4c7SPatrick McHardy struct nlmsgerr *e; 7251da177e4SLinus Torvalds 7260c12295aSPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len); 7271da177e4SLinus Torvalds 728494fff56SYuval Mintz while ((skb = skb_dequeue(&c->_c.mfc_un.unres.unresolved))) { 729eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->version == 0) { 730af72868bSJohannes Berg struct nlmsghdr *nlh = skb_pull(skb, 731af72868bSJohannes Berg sizeof(struct iphdr)); 7321da177e4SLinus Torvalds nlh->nlmsg_type = NLMSG_ERROR; 733573ce260SHong zhi guo nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 7341da177e4SLinus Torvalds skb_trim(skb, nlh->nlmsg_len); 735573ce260SHong zhi guo e = nlmsg_data(nlh); 7369ef1d4c7SPatrick McHardy e->error = -ETIMEDOUT; 7379ef1d4c7SPatrick McHardy memset(&e->msg, 0, sizeof(e->msg)); 7382942e900SThomas Graf 73915e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 740a8cb16ddSEric Dumazet } else { 7411da177e4SLinus Torvalds kfree_skb(skb); 7421da177e4SLinus Torvalds } 743a8cb16ddSEric Dumazet } 7441da177e4SLinus Torvalds 7455c0a66f5SBenjamin Thery ipmr_cache_free(c); 7461da177e4SLinus Torvalds } 7471da177e4SLinus Torvalds 748e258beb2SPatrick McHardy /* Timer process for the unresolved queue. */ 749e99e88a9SKees Cook static void ipmr_expire_process(struct timer_list *t) 7501da177e4SLinus Torvalds { 751e99e88a9SKees Cook struct mr_table *mrt = from_timer(mrt, t, ipmr_expire_timer); 752494fff56SYuval Mintz struct mr_mfc *c, *next; 7531da177e4SLinus Torvalds unsigned long expires; 754494fff56SYuval Mintz unsigned long now; 7551da177e4SLinus Torvalds 7561da177e4SLinus Torvalds if (!spin_trylock(&mfc_unres_lock)) { 7570c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10); 7581da177e4SLinus Torvalds return; 7591da177e4SLinus Torvalds } 7601da177e4SLinus Torvalds 7610c12295aSPatrick McHardy if (list_empty(&mrt->mfc_unres_queue)) 7621da177e4SLinus Torvalds goto out; 7631da177e4SLinus Torvalds 7641da177e4SLinus Torvalds now = jiffies; 7651da177e4SLinus Torvalds expires = 10*HZ; 7661da177e4SLinus Torvalds 7670c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) { 7681da177e4SLinus Torvalds if (time_after(c->mfc_un.unres.expires, now)) { 7691da177e4SLinus Torvalds unsigned long interval = c->mfc_un.unres.expires - now; 7701da177e4SLinus Torvalds if (interval < expires) 7711da177e4SLinus Torvalds expires = interval; 7721da177e4SLinus Torvalds continue; 7731da177e4SLinus Torvalds } 7741da177e4SLinus Torvalds 775862465f2SPatrick McHardy list_del(&c->list); 776494fff56SYuval Mintz mroute_netlink_event(mrt, (struct mfc_cache *)c, RTM_DELROUTE); 777494fff56SYuval Mintz ipmr_destroy_unres(mrt, (struct mfc_cache *)c); 7781da177e4SLinus Torvalds } 7791da177e4SLinus Torvalds 7800c12295aSPatrick McHardy if (!list_empty(&mrt->mfc_unres_queue)) 7810c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies + expires); 7821da177e4SLinus Torvalds 7831da177e4SLinus Torvalds out: 7841da177e4SLinus Torvalds spin_unlock(&mfc_unres_lock); 7851da177e4SLinus Torvalds } 7861da177e4SLinus Torvalds 7873f55211eSEric Dumazet /* Fill oifs list. It is called under locked mrt_lock. */ 788494fff56SYuval Mintz static void ipmr_update_thresholds(struct mr_table *mrt, struct mr_mfc *cache, 789d658f8a0SPatrick McHardy unsigned char *ttls) 7901da177e4SLinus Torvalds { 7911da177e4SLinus Torvalds int vifi; 7921da177e4SLinus Torvalds 7931da177e4SLinus Torvalds cache->mfc_un.res.minvif = MAXVIFS; 7941da177e4SLinus Torvalds cache->mfc_un.res.maxvif = 0; 7951da177e4SLinus Torvalds memset(cache->mfc_un.res.ttls, 255, MAXVIFS); 7961da177e4SLinus Torvalds 7970c12295aSPatrick McHardy for (vifi = 0; vifi < mrt->maxvif; vifi++) { 7980c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vifi) && 799cf958ae3SBenjamin Thery ttls[vifi] && ttls[vifi] < 255) { 8001da177e4SLinus Torvalds cache->mfc_un.res.ttls[vifi] = ttls[vifi]; 8011da177e4SLinus Torvalds if (cache->mfc_un.res.minvif > vifi) 8021da177e4SLinus Torvalds cache->mfc_un.res.minvif = vifi; 8031da177e4SLinus Torvalds if (cache->mfc_un.res.maxvif <= vifi) 8041da177e4SLinus Torvalds cache->mfc_un.res.maxvif = vifi + 1; 8051da177e4SLinus Torvalds } 8061da177e4SLinus Torvalds } 80790b5ca17SNikolay Aleksandrov cache->mfc_un.res.lastuse = jiffies; 8081da177e4SLinus Torvalds } 8091da177e4SLinus Torvalds 8100c12295aSPatrick McHardy static int vif_add(struct net *net, struct mr_table *mrt, 8110c12295aSPatrick McHardy struct vifctl *vifc, int mrtsock) 8121da177e4SLinus Torvalds { 813bccb3025SFlorian Fainelli struct netdev_phys_item_id ppid = { }; 8141da177e4SLinus Torvalds int vifi = vifc->vifc_vifi; 8150c12295aSPatrick McHardy struct vif_device *v = &mrt->vif_table[vifi]; 8161da177e4SLinus Torvalds struct net_device *dev; 8171da177e4SLinus Torvalds struct in_device *in_dev; 818d607032dSWang Chen int err; 8191da177e4SLinus Torvalds 8201da177e4SLinus Torvalds /* Is vif busy ? */ 8210c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vifi)) 8221da177e4SLinus Torvalds return -EADDRINUSE; 8231da177e4SLinus Torvalds 8241da177e4SLinus Torvalds switch (vifc->vifc_flags) { 8251da177e4SLinus Torvalds case VIFF_REGISTER: 8261973a4eaSNikolay Aleksandrov if (!ipmr_pimsm_enabled()) 827c316c629SNikolay Aleksandrov return -EINVAL; 828c316c629SNikolay Aleksandrov /* Special Purpose VIF in PIM 8291da177e4SLinus Torvalds * All the packets will be sent to the daemon 8301da177e4SLinus Torvalds */ 8310c12295aSPatrick McHardy if (mrt->mroute_reg_vif_num >= 0) 8321da177e4SLinus Torvalds return -EADDRINUSE; 833f0ad0860SPatrick McHardy dev = ipmr_reg_vif(net, mrt); 8341da177e4SLinus Torvalds if (!dev) 8351da177e4SLinus Torvalds return -ENOBUFS; 836d607032dSWang Chen err = dev_set_allmulti(dev, 1); 837d607032dSWang Chen if (err) { 838d607032dSWang Chen unregister_netdevice(dev); 8397dc00c82SWang Chen dev_put(dev); 840d607032dSWang Chen return err; 841d607032dSWang Chen } 8421da177e4SLinus Torvalds break; 8431da177e4SLinus Torvalds case VIFF_TUNNEL: 8444feb88e5SBenjamin Thery dev = ipmr_new_tunnel(net, vifc); 845c1fd1182SChristoph Hellwig if (IS_ERR(dev)) 846c1fd1182SChristoph Hellwig return PTR_ERR(dev); 8471da177e4SLinus Torvalds break; 848ee5e81f0SIlia K case VIFF_USE_IFINDEX: 8491da177e4SLinus Torvalds case 0: 850ee5e81f0SIlia K if (vifc->vifc_flags == VIFF_USE_IFINDEX) { 851ee5e81f0SIlia K dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex); 85251456b29SIan Morris if (dev && !__in_dev_get_rtnl(dev)) { 853ee5e81f0SIlia K dev_put(dev); 854ee5e81f0SIlia K return -EADDRNOTAVAIL; 855ee5e81f0SIlia K } 856a8cb16ddSEric Dumazet } else { 8574feb88e5SBenjamin Thery dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr); 858a8cb16ddSEric Dumazet } 8591da177e4SLinus Torvalds if (!dev) 8601da177e4SLinus Torvalds return -EADDRNOTAVAIL; 861d607032dSWang Chen err = dev_set_allmulti(dev, 1); 8627dc00c82SWang Chen if (err) { 8637dc00c82SWang Chen dev_put(dev); 864d607032dSWang Chen return err; 8657dc00c82SWang Chen } 8661da177e4SLinus Torvalds break; 8671da177e4SLinus Torvalds default: 8681da177e4SLinus Torvalds return -EINVAL; 8691da177e4SLinus Torvalds } 8701da177e4SLinus Torvalds 871a8cb16ddSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 872a8cb16ddSEric Dumazet if (!in_dev) { 873d0490cfdSDan Carpenter dev_put(dev); 8741da177e4SLinus Torvalds return -EADDRNOTAVAIL; 875d0490cfdSDan Carpenter } 87642f811b8SHerbert Xu IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++; 8773b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_MC_FORWARDING, 8783b022865SDavid Ahern dev->ifindex, &in_dev->cnf); 8791da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 8801da177e4SLinus Torvalds 881a8cb16ddSEric Dumazet /* Fill in the VIF structures */ 8826853f21fSYuval Mintz vif_device_init(v, dev, vifc->vifc_rate_limit, 8836853f21fSYuval Mintz vifc->vifc_threshold, 8846853f21fSYuval Mintz vifc->vifc_flags | (!mrtsock ? VIFF_STATIC : 0), 8856853f21fSYuval Mintz (VIFF_TUNNEL | VIFF_REGISTER)); 886a8cb16ddSEric Dumazet 887bccb3025SFlorian Fainelli err = dev_get_port_parent_id(dev, &ppid, true); 888bccb3025SFlorian Fainelli if (err == 0) { 889bccb3025SFlorian Fainelli memcpy(v->dev_parent_id.id, ppid.id, ppid.id_len); 890bccb3025SFlorian Fainelli v->dev_parent_id.id_len = ppid.id_len; 8915d8b3e69SYotam Gigi } else { 8925d8b3e69SYotam Gigi v->dev_parent_id.id_len = 0; 8935d8b3e69SYotam Gigi } 8946853f21fSYuval Mintz 8951da177e4SLinus Torvalds v->local = vifc->vifc_lcl_addr.s_addr; 8961da177e4SLinus Torvalds v->remote = vifc->vifc_rmt_addr.s_addr; 8971da177e4SLinus Torvalds 8981da177e4SLinus Torvalds /* And finish update writing critical data */ 8993f55211eSEric Dumazet spin_lock(&mrt_lock); 900ebc31979SEric Dumazet rcu_assign_pointer(v->dev, dev); 90142120a86SEric Dumazet netdev_tracker_alloc(dev, &v->dev_tracker, GFP_ATOMIC); 90264667988SEric Dumazet if (v->flags & VIFF_REGISTER) { 90364667988SEric Dumazet /* Pairs with READ_ONCE() in ipmr_cache_report() and reg_vif_xmit() */ 90464667988SEric Dumazet WRITE_ONCE(mrt->mroute_reg_vif_num, vifi); 90564667988SEric Dumazet } 9060c12295aSPatrick McHardy if (vifi+1 > mrt->maxvif) 9079094db4bSEric Dumazet WRITE_ONCE(mrt->maxvif, vifi + 1); 9083f55211eSEric Dumazet spin_unlock(&mrt_lock); 909ebc31979SEric Dumazet call_ipmr_vif_entry_notifiers(net, FIB_EVENT_VIF_ADD, v, dev, 910ebc31979SEric Dumazet vifi, mrt->id); 9111da177e4SLinus Torvalds return 0; 9121da177e4SLinus Torvalds } 9131da177e4SLinus Torvalds 914a8c9486bSEric Dumazet /* called with rcu_read_lock() */ 9150c12295aSPatrick McHardy static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, 9164feb88e5SBenjamin Thery __be32 origin, 9174feb88e5SBenjamin Thery __be32 mcastgrp) 9181da177e4SLinus Torvalds { 9198fb472c0SNikolay Aleksandrov struct mfc_cache_cmp_arg arg = { 9208fb472c0SNikolay Aleksandrov .mfc_mcastgrp = mcastgrp, 9218fb472c0SNikolay Aleksandrov .mfc_origin = origin 9228fb472c0SNikolay Aleksandrov }; 9231da177e4SLinus Torvalds 924845c9a7aSYuval Mintz return mr_mfc_find(mrt, &arg); 925660b26dcSNicolas Dichtel } 926660b26dcSNicolas Dichtel 927660b26dcSNicolas Dichtel /* Look for a (*,G) entry */ 928660b26dcSNicolas Dichtel static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt, 929660b26dcSNicolas Dichtel __be32 mcastgrp, int vifi) 930660b26dcSNicolas Dichtel { 9318fb472c0SNikolay Aleksandrov struct mfc_cache_cmp_arg arg = { 9328fb472c0SNikolay Aleksandrov .mfc_mcastgrp = mcastgrp, 9338fb472c0SNikolay Aleksandrov .mfc_origin = htonl(INADDR_ANY) 9348fb472c0SNikolay Aleksandrov }; 935660b26dcSNicolas Dichtel 936360eb5daSNicolas Dichtel if (mcastgrp == htonl(INADDR_ANY)) 937845c9a7aSYuval Mintz return mr_mfc_find_any_parent(mrt, vifi); 938845c9a7aSYuval Mintz return mr_mfc_find_any(mrt, vifi, &arg); 939660b26dcSNicolas Dichtel } 940660b26dcSNicolas Dichtel 9418fb472c0SNikolay Aleksandrov /* Look for a (S,G,iif) entry if parent != -1 */ 9428fb472c0SNikolay Aleksandrov static struct mfc_cache *ipmr_cache_find_parent(struct mr_table *mrt, 9438fb472c0SNikolay Aleksandrov __be32 origin, __be32 mcastgrp, 9448fb472c0SNikolay Aleksandrov int parent) 9458fb472c0SNikolay Aleksandrov { 9468fb472c0SNikolay Aleksandrov struct mfc_cache_cmp_arg arg = { 9478fb472c0SNikolay Aleksandrov .mfc_mcastgrp = mcastgrp, 9488fb472c0SNikolay Aleksandrov .mfc_origin = origin, 9498fb472c0SNikolay Aleksandrov }; 9508fb472c0SNikolay Aleksandrov 951845c9a7aSYuval Mintz return mr_mfc_find_parent(mrt, &arg, parent); 9528fb472c0SNikolay Aleksandrov } 9538fb472c0SNikolay Aleksandrov 9547ef8f65dSNikolay Aleksandrov /* Allocate a multicast cache entry */ 955d658f8a0SPatrick McHardy static struct mfc_cache *ipmr_cache_alloc(void) 9561da177e4SLinus Torvalds { 957c3762229SRobert P. J. Day struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL); 958a8c9486bSEric Dumazet 95970a0dec4STom Goff if (c) { 960494fff56SYuval Mintz c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1; 961494fff56SYuval Mintz c->_c.mfc_un.res.minvif = MAXVIFS; 9628c13af2aSYuval Mintz c->_c.free = ipmr_cache_free_rcu; 963494fff56SYuval Mintz refcount_set(&c->_c.mfc_un.res.refcount, 1); 96470a0dec4STom Goff } 9651da177e4SLinus Torvalds return c; 9661da177e4SLinus Torvalds } 9671da177e4SLinus Torvalds 968d658f8a0SPatrick McHardy static struct mfc_cache *ipmr_cache_alloc_unres(void) 9691da177e4SLinus Torvalds { 970c3762229SRobert P. J. Day struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC); 971a8c9486bSEric Dumazet 972a8c9486bSEric Dumazet if (c) { 973494fff56SYuval Mintz skb_queue_head_init(&c->_c.mfc_un.unres.unresolved); 974494fff56SYuval Mintz c->_c.mfc_un.unres.expires = jiffies + 10 * HZ; 975a8c9486bSEric Dumazet } 9761da177e4SLinus Torvalds return c; 9771da177e4SLinus Torvalds } 9781da177e4SLinus Torvalds 9797ef8f65dSNikolay Aleksandrov /* A cache entry has gone into a resolved state from queued */ 9800c12295aSPatrick McHardy static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt, 9810c12295aSPatrick McHardy struct mfc_cache *uc, struct mfc_cache *c) 9821da177e4SLinus Torvalds { 9831da177e4SLinus Torvalds struct sk_buff *skb; 9849ef1d4c7SPatrick McHardy struct nlmsgerr *e; 9851da177e4SLinus Torvalds 986a8cb16ddSEric Dumazet /* Play the pending entries through our router */ 987494fff56SYuval Mintz while ((skb = __skb_dequeue(&uc->_c.mfc_un.unres.unresolved))) { 988eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->version == 0) { 989af72868bSJohannes Berg struct nlmsghdr *nlh = skb_pull(skb, 990af72868bSJohannes Berg sizeof(struct iphdr)); 9911da177e4SLinus Torvalds 9927b0db857SYuval Mintz if (mr_fill_mroute(mrt, skb, &c->_c, 993494fff56SYuval Mintz nlmsg_data(nlh)) > 0) { 994a8cb16ddSEric Dumazet nlh->nlmsg_len = skb_tail_pointer(skb) - 995a8cb16ddSEric Dumazet (u8 *)nlh; 9961da177e4SLinus Torvalds } else { 9971da177e4SLinus Torvalds nlh->nlmsg_type = NLMSG_ERROR; 998573ce260SHong zhi guo nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 9991da177e4SLinus Torvalds skb_trim(skb, nlh->nlmsg_len); 1000573ce260SHong zhi guo e = nlmsg_data(nlh); 10019ef1d4c7SPatrick McHardy e->error = -EMSGSIZE; 10029ef1d4c7SPatrick McHardy memset(&e->msg, 0, sizeof(e->msg)); 10031da177e4SLinus Torvalds } 10042942e900SThomas Graf 100515e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 1006a8cb16ddSEric Dumazet } else { 1007b07a9b26SIdo Schimmel rcu_read_lock(); 10084b1f0d33SDonald Sharp ip_mr_forward(net, mrt, skb->dev, skb, c, 0); 1009b07a9b26SIdo Schimmel rcu_read_unlock(); 10101da177e4SLinus Torvalds } 10111da177e4SLinus Torvalds } 1012a8cb16ddSEric Dumazet } 10131da177e4SLinus Torvalds 10145a645dd8SJulien Gomes /* Bounce a cache query up to mrouted and netlink. 10151da177e4SLinus Torvalds * 101664667988SEric Dumazet * Called under rcu_read_lock(). 10171da177e4SLinus Torvalds */ 101864667988SEric Dumazet static int ipmr_cache_report(const struct mr_table *mrt, 10194feb88e5SBenjamin Thery struct sk_buff *pkt, vifi_t vifi, int assert) 10201da177e4SLinus Torvalds { 1021c9bdd4b5SArnaldo Carvalho de Melo const int ihl = ip_hdrlen(pkt); 1022c316c629SNikolay Aleksandrov struct sock *mroute_sk; 10231da177e4SLinus Torvalds struct igmphdr *igmp; 10241da177e4SLinus Torvalds struct igmpmsg *msg; 1025c316c629SNikolay Aleksandrov struct sk_buff *skb; 10261da177e4SLinus Torvalds int ret; 10271da177e4SLinus Torvalds 1028bb740365SLeone Fernando mroute_sk = rcu_dereference(mrt->mroute_sk); 1029bb740365SLeone Fernando if (!mroute_sk) 1030bb740365SLeone Fernando return -EINVAL; 1031bb740365SLeone Fernando 1032c921c207SNikolay Aleksandrov if (assert == IGMPMSG_WHOLEPKT || assert == IGMPMSG_WRVIFWHOLE) 10331da177e4SLinus Torvalds skb = skb_realloc_headroom(pkt, sizeof(struct iphdr)); 10341da177e4SLinus Torvalds else 10351da177e4SLinus Torvalds skb = alloc_skb(128, GFP_ATOMIC); 10361da177e4SLinus Torvalds 10371da177e4SLinus Torvalds if (!skb) 10381da177e4SLinus Torvalds return -ENOBUFS; 10391da177e4SLinus Torvalds 1040c921c207SNikolay Aleksandrov if (assert == IGMPMSG_WHOLEPKT || assert == IGMPMSG_WRVIFWHOLE) { 10411da177e4SLinus Torvalds /* Ugly, but we have no choice with this interface. 1042a8cb16ddSEric Dumazet * Duplicate old header, fix ihl, length etc. 1043a8cb16ddSEric Dumazet * And all this only to mangle msg->im_msgtype and 1044a8cb16ddSEric Dumazet * to set msg->im_mbz to "mbz" :-) 10451da177e4SLinus Torvalds */ 1046878c8145SArnaldo Carvalho de Melo skb_push(skb, sizeof(struct iphdr)); 1047878c8145SArnaldo Carvalho de Melo skb_reset_network_header(skb); 1048badff6d0SArnaldo Carvalho de Melo skb_reset_transport_header(skb); 10490272ffc4SArnaldo Carvalho de Melo msg = (struct igmpmsg *)skb_network_header(skb); 1050d56f90a7SArnaldo Carvalho de Melo memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr)); 1051c921c207SNikolay Aleksandrov msg->im_msgtype = assert; 10521da177e4SLinus Torvalds msg->im_mbz = 0; 1053c8715a8eSPaul Davey if (assert == IGMPMSG_WRVIFWHOLE) { 1054c921c207SNikolay Aleksandrov msg->im_vif = vifi; 1055c8715a8eSPaul Davey msg->im_vif_hi = vifi >> 8; 1056c8715a8eSPaul Davey } else { 105764667988SEric Dumazet /* Pairs with WRITE_ONCE() in vif_add() and vif_delete() */ 105864667988SEric Dumazet int vif_num = READ_ONCE(mrt->mroute_reg_vif_num); 105964667988SEric Dumazet 106064667988SEric Dumazet msg->im_vif = vif_num; 106164667988SEric Dumazet msg->im_vif_hi = vif_num >> 8; 1062c8715a8eSPaul Davey } 1063eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2; 1064eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) + 1065eddc9ec5SArnaldo Carvalho de Melo sizeof(struct iphdr)); 1066c316c629SNikolay Aleksandrov } else { 1067a8cb16ddSEric Dumazet /* Copy the IP header */ 106830f3a40fSCong Wang skb_set_network_header(skb, skb->len); 1069ddc7b8e3SArnaldo Carvalho de Melo skb_put(skb, ihl); 107027d7ff46SArnaldo Carvalho de Melo skb_copy_to_linear_data(skb, pkt->data, ihl); 1071c316c629SNikolay Aleksandrov /* Flag to the kernel this is a route add */ 1072c316c629SNikolay Aleksandrov ip_hdr(skb)->protocol = 0; 1073eddc9ec5SArnaldo Carvalho de Melo msg = (struct igmpmsg *)skb_network_header(skb); 10741da177e4SLinus Torvalds msg->im_vif = vifi; 1075c8715a8eSPaul Davey msg->im_vif_hi = vifi >> 8; 1076bb740365SLeone Fernando ipv4_pktinfo_prepare(mroute_sk, pkt); 1077bb740365SLeone Fernando memcpy(skb->cb, pkt->cb, sizeof(skb->cb)); 1078a8cb16ddSEric Dumazet /* Add our header */ 10794df864c1SJohannes Berg igmp = skb_put(skb, sizeof(struct igmphdr)); 1080c316c629SNikolay Aleksandrov igmp->type = assert; 10811da177e4SLinus Torvalds msg->im_msgtype = assert; 10821da177e4SLinus Torvalds igmp->code = 0; 1083eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */ 1084b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 10851da177e4SLinus Torvalds } 10861da177e4SLinus Torvalds 10875a645dd8SJulien Gomes igmpmsg_netlink_event(mrt, skb); 10885a645dd8SJulien Gomes 1089a8cb16ddSEric Dumazet /* Deliver to mrouted */ 10904c968709SEric Dumazet ret = sock_queue_rcv_skb(mroute_sk, skb); 109164667988SEric Dumazet 109270a269e6SBenjamin Thery if (ret < 0) { 1093e87cc472SJoe Perches net_warn_ratelimited("mroute: pending queue full, dropping entries\n"); 10941da177e4SLinus Torvalds kfree_skb(skb); 10951da177e4SLinus Torvalds } 10961da177e4SLinus Torvalds 10971da177e4SLinus Torvalds return ret; 10981da177e4SLinus Torvalds } 10991da177e4SLinus Torvalds 11007ef8f65dSNikolay Aleksandrov /* Queue a packet for resolution. It gets locked cache entry! */ 110164667988SEric Dumazet /* Called under rcu_read_lock() */ 11027ef8f65dSNikolay Aleksandrov static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, 11034b1f0d33SDonald Sharp struct sk_buff *skb, struct net_device *dev) 11041da177e4SLinus Torvalds { 11058fb472c0SNikolay Aleksandrov const struct iphdr *iph = ip_hdr(skb); 11068fb472c0SNikolay Aleksandrov struct mfc_cache *c; 1107862465f2SPatrick McHardy bool found = false; 11081da177e4SLinus Torvalds int err; 11091da177e4SLinus Torvalds 11101da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 1111494fff56SYuval Mintz list_for_each_entry(c, &mrt->mfc_unres_queue, _c.list) { 1112e258beb2SPatrick McHardy if (c->mfc_mcastgrp == iph->daddr && 1113862465f2SPatrick McHardy c->mfc_origin == iph->saddr) { 1114862465f2SPatrick McHardy found = true; 11151da177e4SLinus Torvalds break; 11161da177e4SLinus Torvalds } 1117862465f2SPatrick McHardy } 11181da177e4SLinus Torvalds 1119862465f2SPatrick McHardy if (!found) { 1120a8cb16ddSEric Dumazet /* Create a new entry if allowable */ 11210079ad8eSHangbin Liu c = ipmr_cache_alloc_unres(); 11220079ad8eSHangbin Liu if (!c) { 11231da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11241da177e4SLinus Torvalds 11251da177e4SLinus Torvalds kfree_skb(skb); 11261da177e4SLinus Torvalds return -ENOBUFS; 11271da177e4SLinus Torvalds } 11281da177e4SLinus Torvalds 1129a8cb16ddSEric Dumazet /* Fill in the new cache entry */ 1130494fff56SYuval Mintz c->_c.mfc_parent = -1; 1131eddc9ec5SArnaldo Carvalho de Melo c->mfc_origin = iph->saddr; 1132eddc9ec5SArnaldo Carvalho de Melo c->mfc_mcastgrp = iph->daddr; 11331da177e4SLinus Torvalds 1134a8cb16ddSEric Dumazet /* Reflect first query at mrouted. */ 11350c12295aSPatrick McHardy err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE); 1136494fff56SYuval Mintz 11374feb88e5SBenjamin Thery if (err < 0) { 11381da177e4SLinus Torvalds /* If the report failed throw the cache entry 11391da177e4SLinus Torvalds out - Brad Parker 11401da177e4SLinus Torvalds */ 11411da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11421da177e4SLinus Torvalds 11435c0a66f5SBenjamin Thery ipmr_cache_free(c); 11441da177e4SLinus Torvalds kfree_skb(skb); 11451da177e4SLinus Torvalds return err; 11461da177e4SLinus Torvalds } 11471da177e4SLinus Torvalds 11480c12295aSPatrick McHardy atomic_inc(&mrt->cache_resolve_queue_len); 1149494fff56SYuval Mintz list_add(&c->_c.list, &mrt->mfc_unres_queue); 11508cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 11511da177e4SLinus Torvalds 1152278554bdSDavid S. Miller if (atomic_read(&mrt->cache_resolve_queue_len) == 1) 1153494fff56SYuval Mintz mod_timer(&mrt->ipmr_expire_timer, 1154494fff56SYuval Mintz c->_c.mfc_un.unres.expires); 11551da177e4SLinus Torvalds } 11561da177e4SLinus Torvalds 1157a8cb16ddSEric Dumazet /* See if we can append the packet */ 1158494fff56SYuval Mintz if (c->_c.mfc_un.unres.unresolved.qlen > 3) { 11591da177e4SLinus Torvalds kfree_skb(skb); 11601da177e4SLinus Torvalds err = -ENOBUFS; 11611da177e4SLinus Torvalds } else { 11624b1f0d33SDonald Sharp if (dev) { 11634b1f0d33SDonald Sharp skb->dev = dev; 11644b1f0d33SDonald Sharp skb->skb_iif = dev->ifindex; 11654b1f0d33SDonald Sharp } 1166494fff56SYuval Mintz skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb); 11671da177e4SLinus Torvalds err = 0; 11681da177e4SLinus Torvalds } 11691da177e4SLinus Torvalds 11701da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11711da177e4SLinus Torvalds return err; 11721da177e4SLinus Torvalds } 11731da177e4SLinus Torvalds 11747ef8f65dSNikolay Aleksandrov /* MFC cache manipulation by user space mroute daemon */ 11751da177e4SLinus Torvalds 1176660b26dcSNicolas Dichtel static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent) 11771da177e4SLinus Torvalds { 1178b362053aSYotam Gigi struct net *net = read_pnet(&mrt->net); 11798fb472c0SNikolay Aleksandrov struct mfc_cache *c; 11801da177e4SLinus Torvalds 11818fb472c0SNikolay Aleksandrov /* The entries are added/deleted only under RTNL */ 11828fb472c0SNikolay Aleksandrov rcu_read_lock(); 11838fb472c0SNikolay Aleksandrov c = ipmr_cache_find_parent(mrt, mfc->mfcc_origin.s_addr, 11848fb472c0SNikolay Aleksandrov mfc->mfcc_mcastgrp.s_addr, parent); 11858fb472c0SNikolay Aleksandrov rcu_read_unlock(); 11868fb472c0SNikolay Aleksandrov if (!c) 11878fb472c0SNikolay Aleksandrov return -ENOENT; 1188494fff56SYuval Mintz rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ipmr_rht_params); 1189494fff56SYuval Mintz list_del_rcu(&c->_c.list); 1190b362053aSYotam Gigi call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt->id); 11918cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 11928c13af2aSYuval Mintz mr_cache_put(&c->_c); 11938fb472c0SNikolay Aleksandrov 11941da177e4SLinus Torvalds return 0; 11951da177e4SLinus Torvalds } 11961da177e4SLinus Torvalds 11970c12295aSPatrick McHardy static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, 1198660b26dcSNicolas Dichtel struct mfcctl *mfc, int mrtsock, int parent) 11991da177e4SLinus Torvalds { 1200862465f2SPatrick McHardy struct mfc_cache *uc, *c; 1201494fff56SYuval Mintz struct mr_mfc *_uc; 12028fb472c0SNikolay Aleksandrov bool found; 12038fb472c0SNikolay Aleksandrov int ret; 12041da177e4SLinus Torvalds 1205a50436f2SPatrick McHardy if (mfc->mfcc_parent >= MAXVIFS) 1206a50436f2SPatrick McHardy return -ENFILE; 1207a50436f2SPatrick McHardy 12088fb472c0SNikolay Aleksandrov /* The entries are added/deleted only under RTNL */ 12098fb472c0SNikolay Aleksandrov rcu_read_lock(); 12108fb472c0SNikolay Aleksandrov c = ipmr_cache_find_parent(mrt, mfc->mfcc_origin.s_addr, 12118fb472c0SNikolay Aleksandrov mfc->mfcc_mcastgrp.s_addr, parent); 12128fb472c0SNikolay Aleksandrov rcu_read_unlock(); 12138fb472c0SNikolay Aleksandrov if (c) { 12143f55211eSEric Dumazet spin_lock(&mrt_lock); 1215494fff56SYuval Mintz c->_c.mfc_parent = mfc->mfcc_parent; 1216494fff56SYuval Mintz ipmr_update_thresholds(mrt, &c->_c, mfc->mfcc_ttls); 12171da177e4SLinus Torvalds if (!mrtsock) 1218494fff56SYuval Mintz c->_c.mfc_flags |= MFC_STATIC; 12193f55211eSEric Dumazet spin_unlock(&mrt_lock); 1220b362053aSYotam Gigi call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, c, 1221b362053aSYotam Gigi mrt->id); 12228cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 12231da177e4SLinus Torvalds return 0; 12241da177e4SLinus Torvalds } 12251da177e4SLinus Torvalds 1226360eb5daSNicolas Dichtel if (mfc->mfcc_mcastgrp.s_addr != htonl(INADDR_ANY) && 1227660b26dcSNicolas Dichtel !ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) 12281da177e4SLinus Torvalds return -EINVAL; 12291da177e4SLinus Torvalds 1230d658f8a0SPatrick McHardy c = ipmr_cache_alloc(); 123151456b29SIan Morris if (!c) 12321da177e4SLinus Torvalds return -ENOMEM; 12331da177e4SLinus Torvalds 12341da177e4SLinus Torvalds c->mfc_origin = mfc->mfcc_origin.s_addr; 12351da177e4SLinus Torvalds c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr; 1236494fff56SYuval Mintz c->_c.mfc_parent = mfc->mfcc_parent; 1237494fff56SYuval Mintz ipmr_update_thresholds(mrt, &c->_c, mfc->mfcc_ttls); 12381da177e4SLinus Torvalds if (!mrtsock) 1239494fff56SYuval Mintz c->_c.mfc_flags |= MFC_STATIC; 12401da177e4SLinus Torvalds 1241494fff56SYuval Mintz ret = rhltable_insert_key(&mrt->mfc_hash, &c->cmparg, &c->_c.mnode, 12428fb472c0SNikolay Aleksandrov ipmr_rht_params); 12438fb472c0SNikolay Aleksandrov if (ret) { 12448fb472c0SNikolay Aleksandrov pr_err("ipmr: rhtable insert error %d\n", ret); 12458fb472c0SNikolay Aleksandrov ipmr_cache_free(c); 12468fb472c0SNikolay Aleksandrov return ret; 12478fb472c0SNikolay Aleksandrov } 1248494fff56SYuval Mintz list_add_tail_rcu(&c->_c.list, &mrt->mfc_cache_list); 12497ef8f65dSNikolay Aleksandrov /* Check to see if we resolved a queued list. If so we 12501da177e4SLinus Torvalds * need to send on the frames and tidy up. 12511da177e4SLinus Torvalds */ 1252b0ebb739SPatrick McHardy found = false; 12531da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 1254494fff56SYuval Mintz list_for_each_entry(_uc, &mrt->mfc_unres_queue, list) { 1255494fff56SYuval Mintz uc = (struct mfc_cache *)_uc; 1256e258beb2SPatrick McHardy if (uc->mfc_origin == c->mfc_origin && 12571da177e4SLinus Torvalds uc->mfc_mcastgrp == c->mfc_mcastgrp) { 1258494fff56SYuval Mintz list_del(&_uc->list); 12590c12295aSPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len); 1260b0ebb739SPatrick McHardy found = true; 12611da177e4SLinus Torvalds break; 12621da177e4SLinus Torvalds } 12631da177e4SLinus Torvalds } 12640c12295aSPatrick McHardy if (list_empty(&mrt->mfc_unres_queue)) 12650c12295aSPatrick McHardy del_timer(&mrt->ipmr_expire_timer); 12661da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 12671da177e4SLinus Torvalds 1268b0ebb739SPatrick McHardy if (found) { 12690c12295aSPatrick McHardy ipmr_cache_resolve(net, mrt, uc, c); 12705c0a66f5SBenjamin Thery ipmr_cache_free(uc); 12711da177e4SLinus Torvalds } 1272b362053aSYotam Gigi call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_ADD, c, mrt->id); 12738cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 12741da177e4SLinus Torvalds return 0; 12751da177e4SLinus Torvalds } 12761da177e4SLinus Torvalds 12777ef8f65dSNikolay Aleksandrov /* Close the multicast socket, and clear the vif tables etc */ 1278ca8d4794SCallum Sinclair static void mroute_clean_tables(struct mr_table *mrt, int flags) 12791da177e4SLinus Torvalds { 1280b362053aSYotam Gigi struct net *net = read_pnet(&mrt->net); 1281494fff56SYuval Mintz struct mr_mfc *c, *tmp; 1282494fff56SYuval Mintz struct mfc_cache *cache; 1283d17fa6faSEric Dumazet LIST_HEAD(list); 12848fb472c0SNikolay Aleksandrov int i; 12851da177e4SLinus Torvalds 1286a8cb16ddSEric Dumazet /* Shut down all active vif entries */ 1287ca8d4794SCallum Sinclair if (flags & (MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC)) { 12880c12295aSPatrick McHardy for (i = 0; i < mrt->maxvif; i++) { 1289ca8d4794SCallum Sinclair if (((mrt->vif_table[i].flags & VIFF_STATIC) && 1290ca8d4794SCallum Sinclair !(flags & MRT_FLUSH_VIFS_STATIC)) || 1291ca8d4794SCallum Sinclair (!(mrt->vif_table[i].flags & VIFF_STATIC) && !(flags & MRT_FLUSH_VIFS))) 12920e615e96SNikolay Aleksandrov continue; 12930c12295aSPatrick McHardy vif_delete(mrt, i, 0, &list); 12941da177e4SLinus Torvalds } 1295d17fa6faSEric Dumazet unregister_netdevice_many(&list); 1296ca8d4794SCallum Sinclair } 12971da177e4SLinus Torvalds 1298a8cb16ddSEric Dumazet /* Wipe the cache */ 1299ca8d4794SCallum Sinclair if (flags & (MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC)) { 13008fb472c0SNikolay Aleksandrov list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) { 1301ca8d4794SCallum Sinclair if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT_FLUSH_MFC_STATIC)) || 1302ca8d4794SCallum Sinclair (!(c->mfc_flags & MFC_STATIC) && !(flags & MRT_FLUSH_MFC))) 13031da177e4SLinus Torvalds continue; 13048fb472c0SNikolay Aleksandrov rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params); 1305a8c9486bSEric Dumazet list_del_rcu(&c->list); 1306494fff56SYuval Mintz cache = (struct mfc_cache *)c; 1307494fff56SYuval Mintz call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, cache, 1308b362053aSYotam Gigi mrt->id); 1309494fff56SYuval Mintz mroute_netlink_event(mrt, cache, RTM_DELROUTE); 13108c13af2aSYuval Mintz mr_cache_put(c); 13111da177e4SLinus Torvalds } 1312ca8d4794SCallum Sinclair } 13131da177e4SLinus Torvalds 1314ca8d4794SCallum Sinclair if (flags & MRT_FLUSH_MFC) { 13150c12295aSPatrick McHardy if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { 13161da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 13178fb472c0SNikolay Aleksandrov list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { 1318862465f2SPatrick McHardy list_del(&c->list); 1319494fff56SYuval Mintz cache = (struct mfc_cache *)c; 1320494fff56SYuval Mintz mroute_netlink_event(mrt, cache, RTM_DELROUTE); 1321494fff56SYuval Mintz ipmr_destroy_unres(mrt, cache); 13221da177e4SLinus Torvalds } 13231da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 13241da177e4SLinus Torvalds } 13251da177e4SLinus Torvalds } 1326ca8d4794SCallum Sinclair } 13271da177e4SLinus Torvalds 13284c968709SEric Dumazet /* called from ip_ra_control(), before an RCU grace period, 1329974d8f86SZheng Yongjun * we don't need to call synchronize_rcu() here 13304c968709SEric Dumazet */ 13311da177e4SLinus Torvalds static void mrtsock_destruct(struct sock *sk) 13321da177e4SLinus Torvalds { 13334feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1334f0ad0860SPatrick McHardy struct mr_table *mrt; 13354feb88e5SBenjamin Thery 1336128aaa98SKirill Tkhai rtnl_lock(); 1337f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 13384c968709SEric Dumazet if (sk == rtnl_dereference(mrt->mroute_sk)) { 13394feb88e5SBenjamin Thery IPV4_DEVCONF_ALL(net, MC_FORWARDING)--; 13403b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 13413b022865SDavid Ahern NETCONFA_MC_FORWARDING, 1342d67b8c61SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1343d67b8c61SNicolas Dichtel net->ipv4.devconf_all); 1344a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(mrt->mroute_sk, NULL); 1345ca8d4794SCallum Sinclair mroute_clean_tables(mrt, MRT_FLUSH_VIFS | MRT_FLUSH_MFC); 13461da177e4SLinus Torvalds } 13471da177e4SLinus Torvalds } 1348128aaa98SKirill Tkhai rtnl_unlock(); 13491da177e4SLinus Torvalds } 13501da177e4SLinus Torvalds 13517ef8f65dSNikolay Aleksandrov /* Socket options and virtual interface manipulation. The whole 13521da177e4SLinus Torvalds * virtual interface system is a complete heap, but unfortunately 13531da177e4SLinus Torvalds * that's how BSD mrouted happens to think. Maybe one day with a proper 13541da177e4SLinus Torvalds * MOSPF/PIM router set up we can clean this up. 13551da177e4SLinus Torvalds */ 13561da177e4SLinus Torvalds 135701ccb5b4SChristoph Hellwig int ip_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, 135829e97d21SNikolay Aleksandrov unsigned int optlen) 13591da177e4SLinus Torvalds { 136029e97d21SNikolay Aleksandrov struct net *net = sock_net(sk); 136129e97d21SNikolay Aleksandrov int val, ret = 0, parent = 0; 136229e97d21SNikolay Aleksandrov struct mr_table *mrt; 13631da177e4SLinus Torvalds struct vifctl vif; 13641da177e4SLinus Torvalds struct mfcctl mfc; 1365c921c207SNikolay Aleksandrov bool do_wrvifwhole; 136629e97d21SNikolay Aleksandrov u32 uval; 1367f0ad0860SPatrick McHardy 136829e97d21SNikolay Aleksandrov /* There's one exception to the lock - MRT_DONE which needs to unlock */ 136929e97d21SNikolay Aleksandrov rtnl_lock(); 13705e1859fbSEric Dumazet if (sk->sk_type != SOCK_RAW || 137129e97d21SNikolay Aleksandrov inet_sk(sk)->inet_num != IPPROTO_IGMP) { 137229e97d21SNikolay Aleksandrov ret = -EOPNOTSUPP; 137329e97d21SNikolay Aleksandrov goto out_unlock; 137429e97d21SNikolay Aleksandrov } 13755e1859fbSEric Dumazet 1376f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 137729e97d21SNikolay Aleksandrov if (!mrt) { 137829e97d21SNikolay Aleksandrov ret = -ENOENT; 137929e97d21SNikolay Aleksandrov goto out_unlock; 138029e97d21SNikolay Aleksandrov } 1381132adf54SStephen Hemminger if (optname != MRT_INIT) { 138233d480ceSEric Dumazet if (sk != rcu_access_pointer(mrt->mroute_sk) && 138329e97d21SNikolay Aleksandrov !ns_capable(net->user_ns, CAP_NET_ADMIN)) { 138429e97d21SNikolay Aleksandrov ret = -EACCES; 138529e97d21SNikolay Aleksandrov goto out_unlock; 138629e97d21SNikolay Aleksandrov } 13871da177e4SLinus Torvalds } 13881da177e4SLinus Torvalds 1389132adf54SStephen Hemminger switch (optname) { 13901da177e4SLinus Torvalds case MRT_INIT: 139142e6b89cSNikolay Aleksandrov if (optlen != sizeof(int)) { 139229e97d21SNikolay Aleksandrov ret = -EINVAL; 139329e97d21SNikolay Aleksandrov break; 139442e6b89cSNikolay Aleksandrov } 139542e6b89cSNikolay Aleksandrov if (rtnl_dereference(mrt->mroute_sk)) { 139642e6b89cSNikolay Aleksandrov ret = -EADDRINUSE; 139742e6b89cSNikolay Aleksandrov break; 139842e6b89cSNikolay Aleksandrov } 13991da177e4SLinus Torvalds 14001da177e4SLinus Torvalds ret = ip_ra_control(sk, 1, mrtsock_destruct); 14011da177e4SLinus Torvalds if (ret == 0) { 1402cf778b00SEric Dumazet rcu_assign_pointer(mrt->mroute_sk, sk); 14034feb88e5SBenjamin Thery IPV4_DEVCONF_ALL(net, MC_FORWARDING)++; 14043b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 14053b022865SDavid Ahern NETCONFA_MC_FORWARDING, 1406d67b8c61SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1407d67b8c61SNicolas Dichtel net->ipv4.devconf_all); 14081da177e4SLinus Torvalds } 140929e97d21SNikolay Aleksandrov break; 14101da177e4SLinus Torvalds case MRT_DONE: 141129e97d21SNikolay Aleksandrov if (sk != rcu_access_pointer(mrt->mroute_sk)) { 141229e97d21SNikolay Aleksandrov ret = -EACCES; 141329e97d21SNikolay Aleksandrov } else { 1414128aaa98SKirill Tkhai /* We need to unlock here because mrtsock_destruct takes 1415128aaa98SKirill Tkhai * care of rtnl itself and we can't change that due to 1416128aaa98SKirill Tkhai * the IP_ROUTER_ALERT setsockopt which runs without it. 1417128aaa98SKirill Tkhai */ 1418128aaa98SKirill Tkhai rtnl_unlock(); 141929e97d21SNikolay Aleksandrov ret = ip_ra_control(sk, 0, NULL); 1420128aaa98SKirill Tkhai goto out; 142129e97d21SNikolay Aleksandrov } 142229e97d21SNikolay Aleksandrov break; 14231da177e4SLinus Torvalds case MRT_ADD_VIF: 14241da177e4SLinus Torvalds case MRT_DEL_VIF: 142529e97d21SNikolay Aleksandrov if (optlen != sizeof(vif)) { 142629e97d21SNikolay Aleksandrov ret = -EINVAL; 142729e97d21SNikolay Aleksandrov break; 142829e97d21SNikolay Aleksandrov } 142901ccb5b4SChristoph Hellwig if (copy_from_sockptr(&vif, optval, sizeof(vif))) { 143029e97d21SNikolay Aleksandrov ret = -EFAULT; 143129e97d21SNikolay Aleksandrov break; 143229e97d21SNikolay Aleksandrov } 143329e97d21SNikolay Aleksandrov if (vif.vifc_vifi >= MAXVIFS) { 143429e97d21SNikolay Aleksandrov ret = -ENFILE; 143529e97d21SNikolay Aleksandrov break; 143629e97d21SNikolay Aleksandrov } 14371da177e4SLinus Torvalds if (optname == MRT_ADD_VIF) { 14384c968709SEric Dumazet ret = vif_add(net, mrt, &vif, 14394c968709SEric Dumazet sk == rtnl_dereference(mrt->mroute_sk)); 14401da177e4SLinus Torvalds } else { 14410c12295aSPatrick McHardy ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL); 14421da177e4SLinus Torvalds } 144329e97d21SNikolay Aleksandrov break; 14447ef8f65dSNikolay Aleksandrov /* Manipulate the forwarding caches. These live 14451da177e4SLinus Torvalds * in a sort of kernel/user symbiosis. 14461da177e4SLinus Torvalds */ 14471da177e4SLinus Torvalds case MRT_ADD_MFC: 14481da177e4SLinus Torvalds case MRT_DEL_MFC: 1449660b26dcSNicolas Dichtel parent = -1; 1450a8eceea8SJoe Perches fallthrough; 1451660b26dcSNicolas Dichtel case MRT_ADD_MFC_PROXY: 1452660b26dcSNicolas Dichtel case MRT_DEL_MFC_PROXY: 145329e97d21SNikolay Aleksandrov if (optlen != sizeof(mfc)) { 145429e97d21SNikolay Aleksandrov ret = -EINVAL; 145529e97d21SNikolay Aleksandrov break; 145629e97d21SNikolay Aleksandrov } 145773cb1193SIdo Schimmel if (copy_from_sockptr(&mfc, optval, sizeof(mfc))) { 145829e97d21SNikolay Aleksandrov ret = -EFAULT; 145929e97d21SNikolay Aleksandrov break; 146029e97d21SNikolay Aleksandrov } 1461660b26dcSNicolas Dichtel if (parent == 0) 1462660b26dcSNicolas Dichtel parent = mfc.mfcc_parent; 1463660b26dcSNicolas Dichtel if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY) 1464660b26dcSNicolas Dichtel ret = ipmr_mfc_delete(mrt, &mfc, parent); 14651da177e4SLinus Torvalds else 14664c968709SEric Dumazet ret = ipmr_mfc_add(net, mrt, &mfc, 1467660b26dcSNicolas Dichtel sk == rtnl_dereference(mrt->mroute_sk), 1468660b26dcSNicolas Dichtel parent); 146929e97d21SNikolay Aleksandrov break; 1470ca8d4794SCallum Sinclair case MRT_FLUSH: 1471ca8d4794SCallum Sinclair if (optlen != sizeof(val)) { 1472ca8d4794SCallum Sinclair ret = -EINVAL; 1473ca8d4794SCallum Sinclair break; 1474ca8d4794SCallum Sinclair } 147501ccb5b4SChristoph Hellwig if (copy_from_sockptr(&val, optval, sizeof(val))) { 1476ca8d4794SCallum Sinclair ret = -EFAULT; 1477ca8d4794SCallum Sinclair break; 1478ca8d4794SCallum Sinclair } 1479ca8d4794SCallum Sinclair mroute_clean_tables(mrt, val); 1480ca8d4794SCallum Sinclair break; 14817ef8f65dSNikolay Aleksandrov /* Control PIM assert. */ 14821da177e4SLinus Torvalds case MRT_ASSERT: 148329e97d21SNikolay Aleksandrov if (optlen != sizeof(val)) { 148429e97d21SNikolay Aleksandrov ret = -EINVAL; 148529e97d21SNikolay Aleksandrov break; 14861da177e4SLinus Torvalds } 148701ccb5b4SChristoph Hellwig if (copy_from_sockptr(&val, optval, sizeof(val))) { 148829e97d21SNikolay Aleksandrov ret = -EFAULT; 148929e97d21SNikolay Aleksandrov break; 149029e97d21SNikolay Aleksandrov } 149129e97d21SNikolay Aleksandrov mrt->mroute_do_assert = val; 149229e97d21SNikolay Aleksandrov break; 14931da177e4SLinus Torvalds case MRT_PIM: 14941973a4eaSNikolay Aleksandrov if (!ipmr_pimsm_enabled()) { 149529e97d21SNikolay Aleksandrov ret = -ENOPROTOOPT; 149629e97d21SNikolay Aleksandrov break; 14971da177e4SLinus Torvalds } 149829e97d21SNikolay Aleksandrov if (optlen != sizeof(val)) { 149929e97d21SNikolay Aleksandrov ret = -EINVAL; 150029e97d21SNikolay Aleksandrov break; 15011da177e4SLinus Torvalds } 150201ccb5b4SChristoph Hellwig if (copy_from_sockptr(&val, optval, sizeof(val))) { 150329e97d21SNikolay Aleksandrov ret = -EFAULT; 150429e97d21SNikolay Aleksandrov break; 150529e97d21SNikolay Aleksandrov } 150629e97d21SNikolay Aleksandrov 1507c921c207SNikolay Aleksandrov do_wrvifwhole = (val == IGMPMSG_WRVIFWHOLE); 150829e97d21SNikolay Aleksandrov val = !!val; 150929e97d21SNikolay Aleksandrov if (val != mrt->mroute_do_pim) { 151029e97d21SNikolay Aleksandrov mrt->mroute_do_pim = val; 151129e97d21SNikolay Aleksandrov mrt->mroute_do_assert = val; 1512c921c207SNikolay Aleksandrov mrt->mroute_do_wrvifwhole = do_wrvifwhole; 151329e97d21SNikolay Aleksandrov } 151429e97d21SNikolay Aleksandrov break; 1515f0ad0860SPatrick McHardy case MRT_TABLE: 151629e97d21SNikolay Aleksandrov if (!IS_BUILTIN(CONFIG_IP_MROUTE_MULTIPLE_TABLES)) { 151729e97d21SNikolay Aleksandrov ret = -ENOPROTOOPT; 151829e97d21SNikolay Aleksandrov break; 151929e97d21SNikolay Aleksandrov } 152029e97d21SNikolay Aleksandrov if (optlen != sizeof(uval)) { 152129e97d21SNikolay Aleksandrov ret = -EINVAL; 152229e97d21SNikolay Aleksandrov break; 152329e97d21SNikolay Aleksandrov } 152401ccb5b4SChristoph Hellwig if (copy_from_sockptr(&uval, optval, sizeof(uval))) { 152529e97d21SNikolay Aleksandrov ret = -EFAULT; 152629e97d21SNikolay Aleksandrov break; 152729e97d21SNikolay Aleksandrov } 1528f0ad0860SPatrick McHardy 15294c968709SEric Dumazet if (sk == rtnl_dereference(mrt->mroute_sk)) { 15304c968709SEric Dumazet ret = -EBUSY; 15314c968709SEric Dumazet } else { 153229e97d21SNikolay Aleksandrov mrt = ipmr_new_table(net, uval); 15331113ebbcSNikolay Aleksandrov if (IS_ERR(mrt)) 15341113ebbcSNikolay Aleksandrov ret = PTR_ERR(mrt); 15355e1859fbSEric Dumazet else 153629e97d21SNikolay Aleksandrov raw_sk(sk)->ipmr_table = uval; 15374c968709SEric Dumazet } 153829e97d21SNikolay Aleksandrov break; 15397ef8f65dSNikolay Aleksandrov /* Spurious command, or MRT_VERSION which you cannot set. */ 15401da177e4SLinus Torvalds default: 154129e97d21SNikolay Aleksandrov ret = -ENOPROTOOPT; 15421da177e4SLinus Torvalds } 154329e97d21SNikolay Aleksandrov out_unlock: 154429e97d21SNikolay Aleksandrov rtnl_unlock(); 1545128aaa98SKirill Tkhai out: 154629e97d21SNikolay Aleksandrov return ret; 15471da177e4SLinus Torvalds } 15481da177e4SLinus Torvalds 1549e1d001faSBreno Leitao /* Execute if this ioctl is a special mroute ioctl */ 1550e1d001faSBreno Leitao int ipmr_sk_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) 1551e1d001faSBreno Leitao { 1552e1d001faSBreno Leitao switch (cmd) { 1553e1d001faSBreno Leitao /* These userspace buffers will be consumed by ipmr_ioctl() */ 1554e1d001faSBreno Leitao case SIOCGETVIFCNT: { 1555e1d001faSBreno Leitao struct sioc_vif_req buffer; 1556e1d001faSBreno Leitao 1557e1d001faSBreno Leitao return sock_ioctl_inout(sk, cmd, arg, &buffer, 1558e1d001faSBreno Leitao sizeof(buffer)); 1559e1d001faSBreno Leitao } 1560e1d001faSBreno Leitao case SIOCGETSGCNT: { 1561e1d001faSBreno Leitao struct sioc_sg_req buffer; 1562e1d001faSBreno Leitao 1563e1d001faSBreno Leitao return sock_ioctl_inout(sk, cmd, arg, &buffer, 1564e1d001faSBreno Leitao sizeof(buffer)); 1565e1d001faSBreno Leitao } 1566e1d001faSBreno Leitao } 1567e1d001faSBreno Leitao /* return code > 0 means that the ioctl was not executed */ 1568e1d001faSBreno Leitao return 1; 1569e1d001faSBreno Leitao } 1570e1d001faSBreno Leitao 15717ef8f65dSNikolay Aleksandrov /* Getsock opt support for the multicast routing system. */ 1572728f064cSMartin KaFai Lau int ip_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval, 1573728f064cSMartin KaFai Lau sockptr_t optlen) 15741da177e4SLinus Torvalds { 15751da177e4SLinus Torvalds int olr; 15761da177e4SLinus Torvalds int val; 15774feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1578f0ad0860SPatrick McHardy struct mr_table *mrt; 1579f0ad0860SPatrick McHardy 15805e1859fbSEric Dumazet if (sk->sk_type != SOCK_RAW || 15815e1859fbSEric Dumazet inet_sk(sk)->inet_num != IPPROTO_IGMP) 15825e1859fbSEric Dumazet return -EOPNOTSUPP; 15835e1859fbSEric Dumazet 1584f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 158551456b29SIan Morris if (!mrt) 1586f0ad0860SPatrick McHardy return -ENOENT; 15871da177e4SLinus Torvalds 1588fe9ef3ceSNikolay Aleksandrov switch (optname) { 1589fe9ef3ceSNikolay Aleksandrov case MRT_VERSION: 15901da177e4SLinus Torvalds val = 0x0305; 1591fe9ef3ceSNikolay Aleksandrov break; 1592fe9ef3ceSNikolay Aleksandrov case MRT_PIM: 15931973a4eaSNikolay Aleksandrov if (!ipmr_pimsm_enabled()) 1594c316c629SNikolay Aleksandrov return -ENOPROTOOPT; 15950c12295aSPatrick McHardy val = mrt->mroute_do_pim; 1596fe9ef3ceSNikolay Aleksandrov break; 1597fe9ef3ceSNikolay Aleksandrov case MRT_ASSERT: 15980c12295aSPatrick McHardy val = mrt->mroute_do_assert; 1599fe9ef3ceSNikolay Aleksandrov break; 1600fe9ef3ceSNikolay Aleksandrov default: 1601fe9ef3ceSNikolay Aleksandrov return -ENOPROTOOPT; 1602c316c629SNikolay Aleksandrov } 1603fe9ef3ceSNikolay Aleksandrov 1604728f064cSMartin KaFai Lau if (copy_from_sockptr(&olr, optlen, sizeof(int))) 1605fe9ef3ceSNikolay Aleksandrov return -EFAULT; 1606fe9ef3ceSNikolay Aleksandrov olr = min_t(unsigned int, olr, sizeof(int)); 1607fe9ef3ceSNikolay Aleksandrov if (olr < 0) 1608fe9ef3ceSNikolay Aleksandrov return -EINVAL; 1609728f064cSMartin KaFai Lau if (copy_to_sockptr(optlen, &olr, sizeof(int))) 1610fe9ef3ceSNikolay Aleksandrov return -EFAULT; 1611728f064cSMartin KaFai Lau if (copy_to_sockptr(optval, &val, olr)) 16121da177e4SLinus Torvalds return -EFAULT; 16131da177e4SLinus Torvalds return 0; 16141da177e4SLinus Torvalds } 16151da177e4SLinus Torvalds 16167ef8f65dSNikolay Aleksandrov /* The IP multicast ioctl support routines. */ 1617e1d001faSBreno Leitao int ipmr_ioctl(struct sock *sk, int cmd, void *arg) 16181da177e4SLinus Torvalds { 16191da177e4SLinus Torvalds struct vif_device *vif; 16201da177e4SLinus Torvalds struct mfc_cache *c; 16214feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1622e1d001faSBreno Leitao struct sioc_vif_req *vr; 1623e1d001faSBreno Leitao struct sioc_sg_req *sr; 1624f0ad0860SPatrick McHardy struct mr_table *mrt; 1625f0ad0860SPatrick McHardy 1626f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 162751456b29SIan Morris if (!mrt) 1628f0ad0860SPatrick McHardy return -ENOENT; 16291da177e4SLinus Torvalds 1630132adf54SStephen Hemminger switch (cmd) { 16311da177e4SLinus Torvalds case SIOCGETVIFCNT: 1632e1d001faSBreno Leitao vr = (struct sioc_vif_req *)arg; 1633e1d001faSBreno Leitao if (vr->vifi >= mrt->maxvif) 16341da177e4SLinus Torvalds return -EINVAL; 1635e1d001faSBreno Leitao vr->vifi = array_index_nospec(vr->vifi, mrt->maxvif); 1636559260fdSEric Dumazet rcu_read_lock(); 1637e1d001faSBreno Leitao vif = &mrt->vif_table[vr->vifi]; 1638e1d001faSBreno Leitao if (VIF_EXISTS(mrt, vr->vifi)) { 1639e1d001faSBreno Leitao vr->icount = READ_ONCE(vif->pkt_in); 1640e1d001faSBreno Leitao vr->ocount = READ_ONCE(vif->pkt_out); 1641e1d001faSBreno Leitao vr->ibytes = READ_ONCE(vif->bytes_in); 1642e1d001faSBreno Leitao vr->obytes = READ_ONCE(vif->bytes_out); 1643559260fdSEric Dumazet rcu_read_unlock(); 16441da177e4SLinus Torvalds 16451da177e4SLinus Torvalds return 0; 16461da177e4SLinus Torvalds } 1647559260fdSEric Dumazet rcu_read_unlock(); 16481da177e4SLinus Torvalds return -EADDRNOTAVAIL; 16491da177e4SLinus Torvalds case SIOCGETSGCNT: 1650e1d001faSBreno Leitao sr = (struct sioc_sg_req *)arg; 16511da177e4SLinus Torvalds 1652a8c9486bSEric Dumazet rcu_read_lock(); 1653e1d001faSBreno Leitao c = ipmr_cache_find(mrt, sr->src.s_addr, sr->grp.s_addr); 16541da177e4SLinus Torvalds if (c) { 1655e1d001faSBreno Leitao sr->pktcnt = c->_c.mfc_un.res.pkt; 1656e1d001faSBreno Leitao sr->bytecnt = c->_c.mfc_un.res.bytes; 1657e1d001faSBreno Leitao sr->wrong_if = c->_c.mfc_un.res.wrong_if; 1658a8c9486bSEric Dumazet rcu_read_unlock(); 16591da177e4SLinus Torvalds return 0; 16601da177e4SLinus Torvalds } 1661a8c9486bSEric Dumazet rcu_read_unlock(); 16621da177e4SLinus Torvalds return -EADDRNOTAVAIL; 16631da177e4SLinus Torvalds default: 16641da177e4SLinus Torvalds return -ENOIOCTLCMD; 16651da177e4SLinus Torvalds } 16661da177e4SLinus Torvalds } 16671da177e4SLinus Torvalds 1668709b46e8SEric W. Biederman #ifdef CONFIG_COMPAT 1669709b46e8SEric W. Biederman struct compat_sioc_sg_req { 1670709b46e8SEric W. Biederman struct in_addr src; 1671709b46e8SEric W. Biederman struct in_addr grp; 1672709b46e8SEric W. Biederman compat_ulong_t pktcnt; 1673709b46e8SEric W. Biederman compat_ulong_t bytecnt; 1674709b46e8SEric W. Biederman compat_ulong_t wrong_if; 1675709b46e8SEric W. Biederman }; 1676709b46e8SEric W. Biederman 1677ca6b8bb0SDavid S. Miller struct compat_sioc_vif_req { 1678ca6b8bb0SDavid S. Miller vifi_t vifi; /* Which iface */ 1679ca6b8bb0SDavid S. Miller compat_ulong_t icount; 1680ca6b8bb0SDavid S. Miller compat_ulong_t ocount; 1681ca6b8bb0SDavid S. Miller compat_ulong_t ibytes; 1682ca6b8bb0SDavid S. Miller compat_ulong_t obytes; 1683ca6b8bb0SDavid S. Miller }; 1684ca6b8bb0SDavid S. Miller 1685709b46e8SEric W. Biederman int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) 1686709b46e8SEric W. Biederman { 16870033d5adSDavid S. Miller struct compat_sioc_sg_req sr; 1688ca6b8bb0SDavid S. Miller struct compat_sioc_vif_req vr; 1689ca6b8bb0SDavid S. Miller struct vif_device *vif; 1690709b46e8SEric W. Biederman struct mfc_cache *c; 1691709b46e8SEric W. Biederman struct net *net = sock_net(sk); 1692709b46e8SEric W. Biederman struct mr_table *mrt; 1693709b46e8SEric W. Biederman 1694709b46e8SEric W. Biederman mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 169551456b29SIan Morris if (!mrt) 1696709b46e8SEric W. Biederman return -ENOENT; 1697709b46e8SEric W. Biederman 1698709b46e8SEric W. Biederman switch (cmd) { 1699ca6b8bb0SDavid S. Miller case SIOCGETVIFCNT: 1700ca6b8bb0SDavid S. Miller if (copy_from_user(&vr, arg, sizeof(vr))) 1701ca6b8bb0SDavid S. Miller return -EFAULT; 1702ca6b8bb0SDavid S. Miller if (vr.vifi >= mrt->maxvif) 1703ca6b8bb0SDavid S. Miller return -EINVAL; 17045648451eSGustavo A. R. Silva vr.vifi = array_index_nospec(vr.vifi, mrt->maxvif); 1705559260fdSEric Dumazet rcu_read_lock(); 1706ca6b8bb0SDavid S. Miller vif = &mrt->vif_table[vr.vifi]; 1707ca6b8bb0SDavid S. Miller if (VIF_EXISTS(mrt, vr.vifi)) { 1708559260fdSEric Dumazet vr.icount = READ_ONCE(vif->pkt_in); 1709559260fdSEric Dumazet vr.ocount = READ_ONCE(vif->pkt_out); 1710559260fdSEric Dumazet vr.ibytes = READ_ONCE(vif->bytes_in); 1711559260fdSEric Dumazet vr.obytes = READ_ONCE(vif->bytes_out); 1712559260fdSEric Dumazet rcu_read_unlock(); 1713ca6b8bb0SDavid S. Miller 1714ca6b8bb0SDavid S. Miller if (copy_to_user(arg, &vr, sizeof(vr))) 1715ca6b8bb0SDavid S. Miller return -EFAULT; 1716ca6b8bb0SDavid S. Miller return 0; 1717ca6b8bb0SDavid S. Miller } 1718559260fdSEric Dumazet rcu_read_unlock(); 1719ca6b8bb0SDavid S. Miller return -EADDRNOTAVAIL; 1720709b46e8SEric W. Biederman case SIOCGETSGCNT: 1721709b46e8SEric W. Biederman if (copy_from_user(&sr, arg, sizeof(sr))) 1722709b46e8SEric W. Biederman return -EFAULT; 1723709b46e8SEric W. Biederman 1724709b46e8SEric W. Biederman rcu_read_lock(); 1725709b46e8SEric W. Biederman c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); 1726709b46e8SEric W. Biederman if (c) { 1727494fff56SYuval Mintz sr.pktcnt = c->_c.mfc_un.res.pkt; 1728494fff56SYuval Mintz sr.bytecnt = c->_c.mfc_un.res.bytes; 1729494fff56SYuval Mintz sr.wrong_if = c->_c.mfc_un.res.wrong_if; 1730709b46e8SEric W. Biederman rcu_read_unlock(); 1731709b46e8SEric W. Biederman 1732709b46e8SEric W. Biederman if (copy_to_user(arg, &sr, sizeof(sr))) 1733709b46e8SEric W. Biederman return -EFAULT; 1734709b46e8SEric W. Biederman return 0; 1735709b46e8SEric W. Biederman } 1736709b46e8SEric W. Biederman rcu_read_unlock(); 1737709b46e8SEric W. Biederman return -EADDRNOTAVAIL; 1738709b46e8SEric W. Biederman default: 1739709b46e8SEric W. Biederman return -ENOIOCTLCMD; 1740709b46e8SEric W. Biederman } 1741709b46e8SEric W. Biederman } 1742709b46e8SEric W. Biederman #endif 1743709b46e8SEric W. Biederman 17441da177e4SLinus Torvalds static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr) 17451da177e4SLinus Torvalds { 1746351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 17474feb88e5SBenjamin Thery struct net *net = dev_net(dev); 1748f0ad0860SPatrick McHardy struct mr_table *mrt; 17491da177e4SLinus Torvalds struct vif_device *v; 17501da177e4SLinus Torvalds int ct; 1751e9dc8653SEric W. Biederman 17521da177e4SLinus Torvalds if (event != NETDEV_UNREGISTER) 17531da177e4SLinus Torvalds return NOTIFY_DONE; 1754f0ad0860SPatrick McHardy 1755f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 17560c12295aSPatrick McHardy v = &mrt->vif_table[0]; 17570c12295aSPatrick McHardy for (ct = 0; ct < mrt->maxvif; ct++, v++) { 1758ebc31979SEric Dumazet if (rcu_access_pointer(v->dev) == dev) 1759e92036a6SRongQing.Li vif_delete(mrt, ct, 1, NULL); 17601da177e4SLinus Torvalds } 17611da177e4SLinus Torvalds } 17621da177e4SLinus Torvalds return NOTIFY_DONE; 17631da177e4SLinus Torvalds } 17641da177e4SLinus Torvalds 17651da177e4SLinus Torvalds static struct notifier_block ip_mr_notifier = { 17661da177e4SLinus Torvalds .notifier_call = ipmr_device_event, 17671da177e4SLinus Torvalds }; 17681da177e4SLinus Torvalds 17697ef8f65dSNikolay Aleksandrov /* Encapsulate a packet by attaching a valid IPIP header to it. 17701da177e4SLinus Torvalds * This avoids tunnel drivers and other mess and gives us the speed so 17711da177e4SLinus Torvalds * important for multicast video. 17721da177e4SLinus Torvalds */ 1773b6a7719aSHannes Frederic Sowa static void ip_encap(struct net *net, struct sk_buff *skb, 1774b6a7719aSHannes Frederic Sowa __be32 saddr, __be32 daddr) 17751da177e4SLinus Torvalds { 17768856dfa3SArnaldo Carvalho de Melo struct iphdr *iph; 1777b71d1d42SEric Dumazet const struct iphdr *old_iph = ip_hdr(skb); 17788856dfa3SArnaldo Carvalho de Melo 17798856dfa3SArnaldo Carvalho de Melo skb_push(skb, sizeof(struct iphdr)); 1780b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 17818856dfa3SArnaldo Carvalho de Melo skb_reset_network_header(skb); 1782eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 17831da177e4SLinus Torvalds 17841da177e4SLinus Torvalds iph->version = 4; 1785e023dd64SArnaldo Carvalho de Melo iph->tos = old_iph->tos; 1786e023dd64SArnaldo Carvalho de Melo iph->ttl = old_iph->ttl; 17871da177e4SLinus Torvalds iph->frag_off = 0; 17881da177e4SLinus Torvalds iph->daddr = daddr; 17891da177e4SLinus Torvalds iph->saddr = saddr; 17901da177e4SLinus Torvalds iph->protocol = IPPROTO_IPIP; 17911da177e4SLinus Torvalds iph->ihl = 5; 17921da177e4SLinus Torvalds iph->tot_len = htons(skb->len); 1793b6a7719aSHannes Frederic Sowa ip_select_ident(net, skb, NULL); 17941da177e4SLinus Torvalds ip_send_check(iph); 17951da177e4SLinus Torvalds 17961da177e4SLinus Torvalds memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 1797895b5c9fSFlorian Westphal nf_reset_ct(skb); 17981da177e4SLinus Torvalds } 17991da177e4SLinus Torvalds 18000c4b51f0SEric W. Biederman static inline int ipmr_forward_finish(struct net *net, struct sock *sk, 18010c4b51f0SEric W. Biederman struct sk_buff *skb) 18021da177e4SLinus Torvalds { 18031da177e4SLinus Torvalds struct ip_options *opt = &(IPCB(skb)->opt); 18041da177e4SLinus Torvalds 180573186df8SDavid S. Miller IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS); 18061da177e4SLinus Torvalds 18071da177e4SLinus Torvalds if (unlikely(opt->optlen)) 18081da177e4SLinus Torvalds ip_forward_options(skb); 18091da177e4SLinus Torvalds 181013206b6bSEric W. Biederman return dst_output(net, sk, skb); 18111da177e4SLinus Torvalds } 18121da177e4SLinus Torvalds 1813a5bc9294SYotam Gigi #ifdef CONFIG_NET_SWITCHDEV 1814a5bc9294SYotam Gigi static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt, 1815a5bc9294SYotam Gigi int in_vifi, int out_vifi) 1816a5bc9294SYotam Gigi { 1817a5bc9294SYotam Gigi struct vif_device *out_vif = &mrt->vif_table[out_vifi]; 1818a5bc9294SYotam Gigi struct vif_device *in_vif = &mrt->vif_table[in_vifi]; 1819a5bc9294SYotam Gigi 1820875e8939SIdo Schimmel if (!skb->offload_l3_fwd_mark) 1821a5bc9294SYotam Gigi return false; 1822a5bc9294SYotam Gigi if (!out_vif->dev_parent_id.id_len || !in_vif->dev_parent_id.id_len) 1823a5bc9294SYotam Gigi return false; 1824a5bc9294SYotam Gigi return netdev_phys_item_id_same(&out_vif->dev_parent_id, 1825a5bc9294SYotam Gigi &in_vif->dev_parent_id); 1826a5bc9294SYotam Gigi } 1827a5bc9294SYotam Gigi #else 1828a5bc9294SYotam Gigi static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt, 1829a5bc9294SYotam Gigi int in_vifi, int out_vifi) 1830a5bc9294SYotam Gigi { 1831a5bc9294SYotam Gigi return false; 1832a5bc9294SYotam Gigi } 1833a5bc9294SYotam Gigi #endif 1834a5bc9294SYotam Gigi 18354eadb882SEric Dumazet /* Processing handlers for ipmr_forward, under rcu_read_lock() */ 18361da177e4SLinus Torvalds 18370c12295aSPatrick McHardy static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, 18386e0735d1SDavid Ahern int in_vifi, struct sk_buff *skb, int vifi) 18391da177e4SLinus Torvalds { 1840eddc9ec5SArnaldo Carvalho de Melo const struct iphdr *iph = ip_hdr(skb); 18410c12295aSPatrick McHardy struct vif_device *vif = &mrt->vif_table[vifi]; 1842ebc31979SEric Dumazet struct net_device *vif_dev; 18431da177e4SLinus Torvalds struct net_device *dev; 18441da177e4SLinus Torvalds struct rtable *rt; 184531e4543dSDavid S. Miller struct flowi4 fl4; 18461da177e4SLinus Torvalds int encap = 0; 18471da177e4SLinus Torvalds 1848ebc31979SEric Dumazet vif_dev = vif_dev_read(vif); 1849ebc31979SEric Dumazet if (!vif_dev) 18501da177e4SLinus Torvalds goto out_free; 18511da177e4SLinus Torvalds 18521da177e4SLinus Torvalds if (vif->flags & VIFF_REGISTER) { 1853559260fdSEric Dumazet WRITE_ONCE(vif->pkt_out, vif->pkt_out + 1); 1854559260fdSEric Dumazet WRITE_ONCE(vif->bytes_out, vif->bytes_out + skb->len); 1855c4794d22SEric Dumazet DEV_STATS_ADD(vif_dev, tx_bytes, skb->len); 1856c4794d22SEric Dumazet DEV_STATS_INC(vif_dev, tx_packets); 18570c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT); 185869ebbf58SIlpo Järvinen goto out_free; 18591da177e4SLinus Torvalds } 18601da177e4SLinus Torvalds 1861a5bc9294SYotam Gigi if (ipmr_forward_offloaded(skb, mrt, in_vifi, vifi)) 1862a5bc9294SYotam Gigi goto out_free; 1863a5bc9294SYotam Gigi 18641da177e4SLinus Torvalds if (vif->flags & VIFF_TUNNEL) { 186531e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, 186678fbfd8aSDavid S. Miller vif->remote, vif->local, 186778fbfd8aSDavid S. Miller 0, 0, 186878fbfd8aSDavid S. Miller IPPROTO_IPIP, 186978fbfd8aSDavid S. Miller RT_TOS(iph->tos), vif->link); 1870b23dd4feSDavid S. Miller if (IS_ERR(rt)) 18711da177e4SLinus Torvalds goto out_free; 18721da177e4SLinus Torvalds encap = sizeof(struct iphdr); 18731da177e4SLinus Torvalds } else { 187431e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, iph->daddr, 0, 187578fbfd8aSDavid S. Miller 0, 0, 187678fbfd8aSDavid S. Miller IPPROTO_IPIP, 187778fbfd8aSDavid S. Miller RT_TOS(iph->tos), vif->link); 1878b23dd4feSDavid S. Miller if (IS_ERR(rt)) 18791da177e4SLinus Torvalds goto out_free; 18801da177e4SLinus Torvalds } 18811da177e4SLinus Torvalds 1882d8d1f30bSChangli Gao dev = rt->dst.dev; 18831da177e4SLinus Torvalds 1884d8d1f30bSChangli Gao if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) { 18851da177e4SLinus Torvalds /* Do not fragment multicasts. Alas, IPv4 does not 1886a8cb16ddSEric Dumazet * allow to send ICMP, so that packets will disappear 1887a8cb16ddSEric Dumazet * to blackhole. 18881da177e4SLinus Torvalds */ 188973186df8SDavid S. Miller IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); 18901da177e4SLinus Torvalds ip_rt_put(rt); 18911da177e4SLinus Torvalds goto out_free; 18921da177e4SLinus Torvalds } 18931da177e4SLinus Torvalds 1894d8d1f30bSChangli Gao encap += LL_RESERVED_SPACE(dev) + rt->dst.header_len; 18951da177e4SLinus Torvalds 18961da177e4SLinus Torvalds if (skb_cow(skb, encap)) { 18971da177e4SLinus Torvalds ip_rt_put(rt); 18981da177e4SLinus Torvalds goto out_free; 18991da177e4SLinus Torvalds } 19001da177e4SLinus Torvalds 1901559260fdSEric Dumazet WRITE_ONCE(vif->pkt_out, vif->pkt_out + 1); 1902559260fdSEric Dumazet WRITE_ONCE(vif->bytes_out, vif->bytes_out + skb->len); 19031da177e4SLinus Torvalds 1904adf30907SEric Dumazet skb_dst_drop(skb); 1905d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 1906eddc9ec5SArnaldo Carvalho de Melo ip_decrease_ttl(ip_hdr(skb)); 19071da177e4SLinus Torvalds 19081da177e4SLinus Torvalds /* FIXME: forward and output firewalls used to be called here. 1909a8cb16ddSEric Dumazet * What do we do with netfilter? -- RR 1910a8cb16ddSEric Dumazet */ 19111da177e4SLinus Torvalds if (vif->flags & VIFF_TUNNEL) { 1912b6a7719aSHannes Frederic Sowa ip_encap(net, skb, vif->local, vif->remote); 19131da177e4SLinus Torvalds /* FIXME: extra output firewall step used to be here. --RR */ 1914c4794d22SEric Dumazet DEV_STATS_INC(vif_dev, tx_packets); 1915c4794d22SEric Dumazet DEV_STATS_ADD(vif_dev, tx_bytes, skb->len); 19161da177e4SLinus Torvalds } 19171da177e4SLinus Torvalds 19189ee6c5dcSLance Richardson IPCB(skb)->flags |= IPSKB_FORWARDED; 19191da177e4SLinus Torvalds 19207ef8f65dSNikolay Aleksandrov /* RFC1584 teaches, that DVMRP/PIM router must deliver packets locally 19211da177e4SLinus Torvalds * not only before forwarding, but after forwarding on all output 19221da177e4SLinus Torvalds * interfaces. It is clear, if mrouter runs a multicasting 19231da177e4SLinus Torvalds * program, it should receive packets not depending to what interface 19241da177e4SLinus Torvalds * program is joined. 19251da177e4SLinus Torvalds * If we will not make it, the program will have to join on all 19261da177e4SLinus Torvalds * interfaces. On the other hand, multihoming host (or router, but 19271da177e4SLinus Torvalds * not mrouter) cannot join to more than one interface - it will 19281da177e4SLinus Torvalds * result in receiving multiple packets. 19291da177e4SLinus Torvalds */ 193029a26a56SEric W. Biederman NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, 193129a26a56SEric W. Biederman net, NULL, skb, skb->dev, dev, 19321da177e4SLinus Torvalds ipmr_forward_finish); 19331da177e4SLinus Torvalds return; 19341da177e4SLinus Torvalds 19351da177e4SLinus Torvalds out_free: 19361da177e4SLinus Torvalds kfree_skb(skb); 19371da177e4SLinus Torvalds } 19381da177e4SLinus Torvalds 19399094db4bSEric Dumazet /* Called with mrt_lock or rcu_read_lock() */ 19409094db4bSEric Dumazet static int ipmr_find_vif(const struct mr_table *mrt, struct net_device *dev) 19411da177e4SLinus Torvalds { 19421da177e4SLinus Torvalds int ct; 19439094db4bSEric Dumazet /* Pairs with WRITE_ONCE() in vif_delete()/vif_add() */ 19449094db4bSEric Dumazet for (ct = READ_ONCE(mrt->maxvif) - 1; ct >= 0; ct--) { 1945ebc31979SEric Dumazet if (rcu_access_pointer(mrt->vif_table[ct].dev) == dev) 19461da177e4SLinus Torvalds break; 19471da177e4SLinus Torvalds } 19481da177e4SLinus Torvalds return ct; 19491da177e4SLinus Torvalds } 19501da177e4SLinus Torvalds 19511da177e4SLinus Torvalds /* "local" means that we should preserve one skb (for local delivery) */ 19524eadb882SEric Dumazet /* Called uner rcu_read_lock() */ 1953c4854ec8SRami Rosen static void ip_mr_forward(struct net *net, struct mr_table *mrt, 19544b1f0d33SDonald Sharp struct net_device *dev, struct sk_buff *skb, 1955494fff56SYuval Mintz struct mfc_cache *c, int local) 19561da177e4SLinus Torvalds { 19574b1f0d33SDonald Sharp int true_vifi = ipmr_find_vif(mrt, dev); 19581da177e4SLinus Torvalds int psend = -1; 19591da177e4SLinus Torvalds int vif, ct; 19601da177e4SLinus Torvalds 1961494fff56SYuval Mintz vif = c->_c.mfc_parent; 1962494fff56SYuval Mintz c->_c.mfc_un.res.pkt++; 1963494fff56SYuval Mintz c->_c.mfc_un.res.bytes += skb->len; 1964494fff56SYuval Mintz c->_c.mfc_un.res.lastuse = jiffies; 19651da177e4SLinus Torvalds 1966494fff56SYuval Mintz if (c->mfc_origin == htonl(INADDR_ANY) && true_vifi >= 0) { 1967660b26dcSNicolas Dichtel struct mfc_cache *cache_proxy; 1968660b26dcSNicolas Dichtel 1969974d8f86SZheng Yongjun /* For an (*,G) entry, we only check that the incoming 1970660b26dcSNicolas Dichtel * interface is part of the static tree. 1971660b26dcSNicolas Dichtel */ 1972845c9a7aSYuval Mintz cache_proxy = mr_mfc_find_any_parent(mrt, vif); 1973660b26dcSNicolas Dichtel if (cache_proxy && 1974494fff56SYuval Mintz cache_proxy->_c.mfc_un.res.ttls[true_vifi] < 255) 1975660b26dcSNicolas Dichtel goto forward; 1976660b26dcSNicolas Dichtel } 1977660b26dcSNicolas Dichtel 19787ef8f65dSNikolay Aleksandrov /* Wrong interface: drop packet and (maybe) send PIM assert. */ 1979ebc31979SEric Dumazet if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev) { 1980c7537967SDavid S. Miller if (rt_is_output_route(skb_rtable(skb))) { 19811da177e4SLinus Torvalds /* It is our own packet, looped back. 1982a8cb16ddSEric Dumazet * Very complicated situation... 1983a8cb16ddSEric Dumazet * 1984a8cb16ddSEric Dumazet * The best workaround until routing daemons will be 1985a8cb16ddSEric Dumazet * fixed is not to redistribute packet, if it was 1986a8cb16ddSEric Dumazet * send through wrong interface. It means, that 1987a8cb16ddSEric Dumazet * multicast applications WILL NOT work for 1988a8cb16ddSEric Dumazet * (S,G), which have default multicast route pointing 1989a8cb16ddSEric Dumazet * to wrong oif. In any case, it is not a good 1990a8cb16ddSEric Dumazet * idea to use multicasting applications on router. 19911da177e4SLinus Torvalds */ 19921da177e4SLinus Torvalds goto dont_forward; 19931da177e4SLinus Torvalds } 19941da177e4SLinus Torvalds 1995494fff56SYuval Mintz c->_c.mfc_un.res.wrong_if++; 19961da177e4SLinus Torvalds 19970c12295aSPatrick McHardy if (true_vifi >= 0 && mrt->mroute_do_assert && 19981da177e4SLinus Torvalds /* pimsm uses asserts, when switching from RPT to SPT, 1999a8cb16ddSEric Dumazet * so that we cannot check that packet arrived on an oif. 2000a8cb16ddSEric Dumazet * It is bad, but otherwise we would need to move pretty 2001a8cb16ddSEric Dumazet * large chunk of pimd to kernel. Ough... --ANK 20021da177e4SLinus Torvalds */ 20030c12295aSPatrick McHardy (mrt->mroute_do_pim || 2004494fff56SYuval Mintz c->_c.mfc_un.res.ttls[true_vifi] < 255) && 20051da177e4SLinus Torvalds time_after(jiffies, 2006494fff56SYuval Mintz c->_c.mfc_un.res.last_assert + 2007494fff56SYuval Mintz MFC_ASSERT_THRESH)) { 2008494fff56SYuval Mintz c->_c.mfc_un.res.last_assert = jiffies; 20090c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF); 2010c921c207SNikolay Aleksandrov if (mrt->mroute_do_wrvifwhole) 2011c921c207SNikolay Aleksandrov ipmr_cache_report(mrt, skb, true_vifi, 2012c921c207SNikolay Aleksandrov IGMPMSG_WRVIFWHOLE); 20131da177e4SLinus Torvalds } 20141da177e4SLinus Torvalds goto dont_forward; 20151da177e4SLinus Torvalds } 20161da177e4SLinus Torvalds 2017660b26dcSNicolas Dichtel forward: 2018559260fdSEric Dumazet WRITE_ONCE(mrt->vif_table[vif].pkt_in, 2019559260fdSEric Dumazet mrt->vif_table[vif].pkt_in + 1); 2020559260fdSEric Dumazet WRITE_ONCE(mrt->vif_table[vif].bytes_in, 2021559260fdSEric Dumazet mrt->vif_table[vif].bytes_in + skb->len); 20221da177e4SLinus Torvalds 20237ef8f65dSNikolay Aleksandrov /* Forward the frame */ 2024494fff56SYuval Mintz if (c->mfc_origin == htonl(INADDR_ANY) && 2025494fff56SYuval Mintz c->mfc_mcastgrp == htonl(INADDR_ANY)) { 2026660b26dcSNicolas Dichtel if (true_vifi >= 0 && 2027494fff56SYuval Mintz true_vifi != c->_c.mfc_parent && 2028660b26dcSNicolas Dichtel ip_hdr(skb)->ttl > 2029494fff56SYuval Mintz c->_c.mfc_un.res.ttls[c->_c.mfc_parent]) { 2030660b26dcSNicolas Dichtel /* It's an (*,*) entry and the packet is not coming from 2031660b26dcSNicolas Dichtel * the upstream: forward the packet to the upstream 2032660b26dcSNicolas Dichtel * only. 2033660b26dcSNicolas Dichtel */ 2034494fff56SYuval Mintz psend = c->_c.mfc_parent; 2035660b26dcSNicolas Dichtel goto last_forward; 2036660b26dcSNicolas Dichtel } 2037660b26dcSNicolas Dichtel goto dont_forward; 2038660b26dcSNicolas Dichtel } 2039494fff56SYuval Mintz for (ct = c->_c.mfc_un.res.maxvif - 1; 2040494fff56SYuval Mintz ct >= c->_c.mfc_un.res.minvif; ct--) { 2041660b26dcSNicolas Dichtel /* For (*,G) entry, don't forward to the incoming interface */ 2042494fff56SYuval Mintz if ((c->mfc_origin != htonl(INADDR_ANY) || 2043360eb5daSNicolas Dichtel ct != true_vifi) && 2044494fff56SYuval Mintz ip_hdr(skb)->ttl > c->_c.mfc_un.res.ttls[ct]) { 20451da177e4SLinus Torvalds if (psend != -1) { 20461da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 2047a8cb16ddSEric Dumazet 20481da177e4SLinus Torvalds if (skb2) 2049a5bc9294SYotam Gigi ipmr_queue_xmit(net, mrt, true_vifi, 20506e0735d1SDavid Ahern skb2, psend); 20511da177e4SLinus Torvalds } 20521da177e4SLinus Torvalds psend = ct; 20531da177e4SLinus Torvalds } 20541da177e4SLinus Torvalds } 2055660b26dcSNicolas Dichtel last_forward: 20561da177e4SLinus Torvalds if (psend != -1) { 20571da177e4SLinus Torvalds if (local) { 20581da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 2059a8cb16ddSEric Dumazet 20601da177e4SLinus Torvalds if (skb2) 2061a5bc9294SYotam Gigi ipmr_queue_xmit(net, mrt, true_vifi, skb2, 20626e0735d1SDavid Ahern psend); 20631da177e4SLinus Torvalds } else { 20646e0735d1SDavid Ahern ipmr_queue_xmit(net, mrt, true_vifi, skb, psend); 2065c4854ec8SRami Rosen return; 20661da177e4SLinus Torvalds } 20671da177e4SLinus Torvalds } 20681da177e4SLinus Torvalds 20691da177e4SLinus Torvalds dont_forward: 20701da177e4SLinus Torvalds if (!local) 20711da177e4SLinus Torvalds kfree_skb(skb); 20721da177e4SLinus Torvalds } 20731da177e4SLinus Torvalds 2074417da66fSDavid S. Miller static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb) 2075ee3f1aafSDavid S. Miller { 2076417da66fSDavid S. Miller struct rtable *rt = skb_rtable(skb); 2077417da66fSDavid S. Miller struct iphdr *iph = ip_hdr(skb); 2078da91981bSDavid S. Miller struct flowi4 fl4 = { 2079417da66fSDavid S. Miller .daddr = iph->daddr, 2080417da66fSDavid S. Miller .saddr = iph->saddr, 2081b0fe4a31SJulian Anastasov .flowi4_tos = RT_TOS(iph->tos), 20824fd551d7SDavid S. Miller .flowi4_oif = (rt_is_output_route(rt) ? 20834fd551d7SDavid S. Miller skb->dev->ifindex : 0), 20844fd551d7SDavid S. Miller .flowi4_iif = (rt_is_output_route(rt) ? 20851fb9489bSPavel Emelyanov LOOPBACK_IFINDEX : 20864fd551d7SDavid S. Miller skb->dev->ifindex), 2087b4869889SDavid Miller .flowi4_mark = skb->mark, 2088ee3f1aafSDavid S. Miller }; 2089ee3f1aafSDavid S. Miller struct mr_table *mrt; 2090ee3f1aafSDavid S. Miller int err; 2091ee3f1aafSDavid S. Miller 2092da91981bSDavid S. Miller err = ipmr_fib_lookup(net, &fl4, &mrt); 2093ee3f1aafSDavid S. Miller if (err) 2094ee3f1aafSDavid S. Miller return ERR_PTR(err); 2095ee3f1aafSDavid S. Miller return mrt; 2096ee3f1aafSDavid S. Miller } 20971da177e4SLinus Torvalds 20987ef8f65dSNikolay Aleksandrov /* Multicast packets for forwarding arrive here 20994c968709SEric Dumazet * Called with rcu_read_lock(); 21001da177e4SLinus Torvalds */ 21011da177e4SLinus Torvalds int ip_mr_input(struct sk_buff *skb) 21021da177e4SLinus Torvalds { 21031da177e4SLinus Torvalds struct mfc_cache *cache; 21044feb88e5SBenjamin Thery struct net *net = dev_net(skb->dev); 2105511c3f92SEric Dumazet int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL; 2106f0ad0860SPatrick McHardy struct mr_table *mrt; 2107bcfc7d33SThomas Winter struct net_device *dev; 2108bcfc7d33SThomas Winter 2109bcfc7d33SThomas Winter /* skb->dev passed in is the loX master dev for vrfs. 2110bcfc7d33SThomas Winter * As there are no vifs associated with loopback devices, 2111bcfc7d33SThomas Winter * get the proper interface that does have a vif associated with it. 2112bcfc7d33SThomas Winter */ 2113bcfc7d33SThomas Winter dev = skb->dev; 2114bcfc7d33SThomas Winter if (netif_is_l3_master(skb->dev)) { 2115bcfc7d33SThomas Winter dev = dev_get_by_index_rcu(net, IPCB(skb)->iif); 2116bcfc7d33SThomas Winter if (!dev) { 2117bcfc7d33SThomas Winter kfree_skb(skb); 2118bcfc7d33SThomas Winter return -ENODEV; 2119bcfc7d33SThomas Winter } 2120bcfc7d33SThomas Winter } 21211da177e4SLinus Torvalds 21221da177e4SLinus Torvalds /* Packet is looped back after forward, it should not be 2123a8cb16ddSEric Dumazet * forwarded second time, but still can be delivered locally. 21241da177e4SLinus Torvalds */ 21251da177e4SLinus Torvalds if (IPCB(skb)->flags & IPSKB_FORWARDED) 21261da177e4SLinus Torvalds goto dont_forward; 21271da177e4SLinus Torvalds 2128417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2129ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) { 2130e40dbc51SBen Greear kfree_skb(skb); 2131ee3f1aafSDavid S. Miller return PTR_ERR(mrt); 21325e2b61f7SDavid S. Miller } 21331da177e4SLinus Torvalds if (!local) { 21341da177e4SLinus Torvalds if (IPCB(skb)->opt.router_alert) { 21351da177e4SLinus Torvalds if (ip_call_ra_chain(skb)) 21361da177e4SLinus Torvalds return 0; 2137eddc9ec5SArnaldo Carvalho de Melo } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP) { 21381da177e4SLinus Torvalds /* IGMPv1 (and broken IGMPv2 implementations sort of 21394c968709SEric Dumazet * Cisco IOS <= 11.2(8)) do not put router alert 21404c968709SEric Dumazet * option to IGMP packets destined to routable 21414c968709SEric Dumazet * groups. It is very bad, because it means 21424c968709SEric Dumazet * that we can forward NO IGMP messages. 21431da177e4SLinus Torvalds */ 21444c968709SEric Dumazet struct sock *mroute_sk; 21454c968709SEric Dumazet 21464c968709SEric Dumazet mroute_sk = rcu_dereference(mrt->mroute_sk); 21474c968709SEric Dumazet if (mroute_sk) { 2148895b5c9fSFlorian Westphal nf_reset_ct(skb); 21494c968709SEric Dumazet raw_rcv(mroute_sk, skb); 21501da177e4SLinus Torvalds return 0; 21511da177e4SLinus Torvalds } 21521da177e4SLinus Torvalds } 21531da177e4SLinus Torvalds } 21541da177e4SLinus Torvalds 2155a8c9486bSEric Dumazet /* already under rcu_read_lock() */ 21560c12295aSPatrick McHardy cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); 215751456b29SIan Morris if (!cache) { 2158bcfc7d33SThomas Winter int vif = ipmr_find_vif(mrt, dev); 2159660b26dcSNicolas Dichtel 2160660b26dcSNicolas Dichtel if (vif >= 0) 2161660b26dcSNicolas Dichtel cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr, 2162660b26dcSNicolas Dichtel vif); 2163660b26dcSNicolas Dichtel } 21641da177e4SLinus Torvalds 21657ef8f65dSNikolay Aleksandrov /* No usable cache entry */ 216651456b29SIan Morris if (!cache) { 21671da177e4SLinus Torvalds int vif; 21681da177e4SLinus Torvalds 21691da177e4SLinus Torvalds if (local) { 21701da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 21711da177e4SLinus Torvalds ip_local_deliver(skb); 217251456b29SIan Morris if (!skb2) 21731da177e4SLinus Torvalds return -ENOBUFS; 21741da177e4SLinus Torvalds skb = skb2; 21751da177e4SLinus Torvalds } 21761da177e4SLinus Torvalds 2177bcfc7d33SThomas Winter vif = ipmr_find_vif(mrt, dev); 21789094db4bSEric Dumazet if (vif >= 0) 21799094db4bSEric Dumazet return ipmr_cache_unresolved(mrt, vif, skb, dev); 21801da177e4SLinus Torvalds kfree_skb(skb); 21811da177e4SLinus Torvalds return -ENODEV; 21821da177e4SLinus Torvalds } 21831da177e4SLinus Torvalds 21844b1f0d33SDonald Sharp ip_mr_forward(net, mrt, dev, skb, cache, local); 21851da177e4SLinus Torvalds 21861da177e4SLinus Torvalds if (local) 21871da177e4SLinus Torvalds return ip_local_deliver(skb); 21881da177e4SLinus Torvalds 21891da177e4SLinus Torvalds return 0; 21901da177e4SLinus Torvalds 21911da177e4SLinus Torvalds dont_forward: 21921da177e4SLinus Torvalds if (local) 21931da177e4SLinus Torvalds return ip_local_deliver(skb); 21941da177e4SLinus Torvalds kfree_skb(skb); 21951da177e4SLinus Torvalds return 0; 21961da177e4SLinus Torvalds } 21971da177e4SLinus Torvalds 2198b1879204SIlpo Järvinen #ifdef CONFIG_IP_PIMSM_V1 21997ef8f65dSNikolay Aleksandrov /* Handle IGMP messages of PIMv1 */ 2200b1879204SIlpo Järvinen int pim_rcv_v1(struct sk_buff *skb) 2201b1879204SIlpo Järvinen { 2202b1879204SIlpo Järvinen struct igmphdr *pim; 22034feb88e5SBenjamin Thery struct net *net = dev_net(skb->dev); 2204f0ad0860SPatrick McHardy struct mr_table *mrt; 2205b1879204SIlpo Järvinen 2206b1879204SIlpo Järvinen if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 2207b1879204SIlpo Järvinen goto drop; 2208b1879204SIlpo Järvinen 2209b1879204SIlpo Järvinen pim = igmp_hdr(skb); 2210b1879204SIlpo Järvinen 2211417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2212ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) 2213f0ad0860SPatrick McHardy goto drop; 22140c12295aSPatrick McHardy if (!mrt->mroute_do_pim || 2215b1879204SIlpo Järvinen pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) 2216b1879204SIlpo Järvinen goto drop; 2217b1879204SIlpo Järvinen 2218f0ad0860SPatrick McHardy if (__pim_rcv(mrt, skb, sizeof(*pim))) { 22191da177e4SLinus Torvalds drop: 22201da177e4SLinus Torvalds kfree_skb(skb); 2221b1879204SIlpo Järvinen } 22221da177e4SLinus Torvalds return 0; 22231da177e4SLinus Torvalds } 22241da177e4SLinus Torvalds #endif 22251da177e4SLinus Torvalds 22261da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V2 22271da177e4SLinus Torvalds static int pim_rcv(struct sk_buff *skb) 22281da177e4SLinus Torvalds { 22291da177e4SLinus Torvalds struct pimreghdr *pim; 2230f0ad0860SPatrick McHardy struct net *net = dev_net(skb->dev); 2231f0ad0860SPatrick McHardy struct mr_table *mrt; 22321da177e4SLinus Torvalds 2233b1879204SIlpo Järvinen if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 22341da177e4SLinus Torvalds goto drop; 22351da177e4SLinus Torvalds 22369c70220bSArnaldo Carvalho de Melo pim = (struct pimreghdr *)skb_transport_header(skb); 223756245caeSNikolay Aleksandrov if (pim->type != ((PIM_VERSION << 4) | (PIM_TYPE_REGISTER)) || 22381da177e4SLinus Torvalds (pim->flags & PIM_NULL_REGISTER) || 22391da177e4SLinus Torvalds (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && 2240d3bc23e7SAl Viro csum_fold(skb_checksum(skb, 0, skb->len, 0)))) 22411da177e4SLinus Torvalds goto drop; 22421da177e4SLinus Torvalds 2243417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2244ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) 2245f0ad0860SPatrick McHardy goto drop; 2246f0ad0860SPatrick McHardy if (__pim_rcv(mrt, skb, sizeof(*pim))) { 22471da177e4SLinus Torvalds drop: 22481da177e4SLinus Torvalds kfree_skb(skb); 2249b1879204SIlpo Järvinen } 22501da177e4SLinus Torvalds return 0; 22511da177e4SLinus Torvalds } 22521da177e4SLinus Torvalds #endif 22531da177e4SLinus Torvalds 22549a1b9496SDavid S. Miller int ipmr_get_route(struct net *net, struct sk_buff *skb, 22559a1b9496SDavid S. Miller __be32 saddr, __be32 daddr, 22569f09eaeaSDavid Ahern struct rtmsg *rtm, u32 portid) 22571da177e4SLinus Torvalds { 22581da177e4SLinus Torvalds struct mfc_cache *cache; 22599a1b9496SDavid S. Miller struct mr_table *mrt; 22609a1b9496SDavid S. Miller int err; 22611da177e4SLinus Torvalds 2262f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 226351456b29SIan Morris if (!mrt) 2264f0ad0860SPatrick McHardy return -ENOENT; 2265f0ad0860SPatrick McHardy 2266a8c9486bSEric Dumazet rcu_read_lock(); 22679a1b9496SDavid S. Miller cache = ipmr_cache_find(mrt, saddr, daddr); 226851456b29SIan Morris if (!cache && skb->dev) { 2269660b26dcSNicolas Dichtel int vif = ipmr_find_vif(mrt, skb->dev); 22701da177e4SLinus Torvalds 2271660b26dcSNicolas Dichtel if (vif >= 0) 2272660b26dcSNicolas Dichtel cache = ipmr_cache_find_any(mrt, daddr, vif); 2273660b26dcSNicolas Dichtel } 227451456b29SIan Morris if (!cache) { 227572287490SAlexey Kuznetsov struct sk_buff *skb2; 2276eddc9ec5SArnaldo Carvalho de Melo struct iphdr *iph; 22771da177e4SLinus Torvalds struct net_device *dev; 2278a8cb16ddSEric Dumazet int vif = -1; 22791da177e4SLinus Torvalds 22801da177e4SLinus Torvalds dev = skb->dev; 2281a8cb16ddSEric Dumazet if (dev) 2282a8cb16ddSEric Dumazet vif = ipmr_find_vif(mrt, dev); 2283a8cb16ddSEric Dumazet if (vif < 0) { 2284a8c9486bSEric Dumazet rcu_read_unlock(); 22851da177e4SLinus Torvalds return -ENODEV; 22861da177e4SLinus Torvalds } 22877901cd97SGuillaume Nault 22887901cd97SGuillaume Nault skb2 = skb_realloc_headroom(skb, sizeof(struct iphdr)); 228972287490SAlexey Kuznetsov if (!skb2) { 2290a8c9486bSEric Dumazet rcu_read_unlock(); 229172287490SAlexey Kuznetsov return -ENOMEM; 229272287490SAlexey Kuznetsov } 229372287490SAlexey Kuznetsov 22942cf75070SNikolay Aleksandrov NETLINK_CB(skb2).portid = portid; 2295e2d1bca7SArnaldo Carvalho de Melo skb_push(skb2, sizeof(struct iphdr)); 2296e2d1bca7SArnaldo Carvalho de Melo skb_reset_network_header(skb2); 2297eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb2); 2298eddc9ec5SArnaldo Carvalho de Melo iph->ihl = sizeof(struct iphdr) >> 2; 22999a1b9496SDavid S. Miller iph->saddr = saddr; 23009a1b9496SDavid S. Miller iph->daddr = daddr; 2301eddc9ec5SArnaldo Carvalho de Melo iph->version = 0; 23024b1f0d33SDonald Sharp err = ipmr_cache_unresolved(mrt, vif, skb2, dev); 2303a8c9486bSEric Dumazet rcu_read_unlock(); 23041da177e4SLinus Torvalds return err; 23051da177e4SLinus Torvalds } 23061da177e4SLinus Torvalds 23077b0db857SYuval Mintz err = mr_fill_mroute(mrt, skb, &cache->_c, rtm); 2308a8c9486bSEric Dumazet rcu_read_unlock(); 23091da177e4SLinus Torvalds return err; 23101da177e4SLinus Torvalds } 23111da177e4SLinus Torvalds 2312cb6a4e46SPatrick McHardy static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 231365886f43SNicolas Dichtel u32 portid, u32 seq, struct mfc_cache *c, int cmd, 231465886f43SNicolas Dichtel int flags) 2315cb6a4e46SPatrick McHardy { 2316cb6a4e46SPatrick McHardy struct nlmsghdr *nlh; 2317cb6a4e46SPatrick McHardy struct rtmsg *rtm; 23181eb99af5SNicolas Dichtel int err; 2319cb6a4e46SPatrick McHardy 232065886f43SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags); 232151456b29SIan Morris if (!nlh) 2322cb6a4e46SPatrick McHardy return -EMSGSIZE; 2323cb6a4e46SPatrick McHardy 2324cb6a4e46SPatrick McHardy rtm = nlmsg_data(nlh); 2325cb6a4e46SPatrick McHardy rtm->rtm_family = RTNL_FAMILY_IPMR; 2326cb6a4e46SPatrick McHardy rtm->rtm_dst_len = 32; 2327cb6a4e46SPatrick McHardy rtm->rtm_src_len = 32; 2328cb6a4e46SPatrick McHardy rtm->rtm_tos = 0; 2329cb6a4e46SPatrick McHardy rtm->rtm_table = mrt->id; 2330f3756b79SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, mrt->id)) 2331f3756b79SDavid S. Miller goto nla_put_failure; 2332cb6a4e46SPatrick McHardy rtm->rtm_type = RTN_MULTICAST; 2333cb6a4e46SPatrick McHardy rtm->rtm_scope = RT_SCOPE_UNIVERSE; 2334494fff56SYuval Mintz if (c->_c.mfc_flags & MFC_STATIC) 23359a68ac72SNicolas Dichtel rtm->rtm_protocol = RTPROT_STATIC; 23369a68ac72SNicolas Dichtel else 23379a68ac72SNicolas Dichtel rtm->rtm_protocol = RTPROT_MROUTED; 2338cb6a4e46SPatrick McHardy rtm->rtm_flags = 0; 2339cb6a4e46SPatrick McHardy 2340930345eaSJiri Benc if (nla_put_in_addr(skb, RTA_SRC, c->mfc_origin) || 2341930345eaSJiri Benc nla_put_in_addr(skb, RTA_DST, c->mfc_mcastgrp)) 2342f3756b79SDavid S. Miller goto nla_put_failure; 23437b0db857SYuval Mintz err = mr_fill_mroute(mrt, skb, &c->_c, rtm); 23441eb99af5SNicolas Dichtel /* do not break the dump if cache is unresolved */ 23451eb99af5SNicolas Dichtel if (err < 0 && err != -ENOENT) 2346cb6a4e46SPatrick McHardy goto nla_put_failure; 2347cb6a4e46SPatrick McHardy 2348053c095aSJohannes Berg nlmsg_end(skb, nlh); 2349053c095aSJohannes Berg return 0; 2350cb6a4e46SPatrick McHardy 2351cb6a4e46SPatrick McHardy nla_put_failure: 2352cb6a4e46SPatrick McHardy nlmsg_cancel(skb, nlh); 2353cb6a4e46SPatrick McHardy return -EMSGSIZE; 2354cb6a4e46SPatrick McHardy } 2355cb6a4e46SPatrick McHardy 23567b0db857SYuval Mintz static int _ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 23577b0db857SYuval Mintz u32 portid, u32 seq, struct mr_mfc *c, int cmd, 23587b0db857SYuval Mintz int flags) 23597b0db857SYuval Mintz { 23607b0db857SYuval Mintz return ipmr_fill_mroute(mrt, skb, portid, seq, (struct mfc_cache *)c, 23617b0db857SYuval Mintz cmd, flags); 23627b0db857SYuval Mintz } 23637b0db857SYuval Mintz 23648cd3ac9fSNicolas Dichtel static size_t mroute_msgsize(bool unresolved, int maxvif) 23658cd3ac9fSNicolas Dichtel { 23668cd3ac9fSNicolas Dichtel size_t len = 23678cd3ac9fSNicolas Dichtel NLMSG_ALIGN(sizeof(struct rtmsg)) 23688cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_TABLE */ 23698cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_SRC */ 23708cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_DST */ 23718cd3ac9fSNicolas Dichtel ; 23728cd3ac9fSNicolas Dichtel 23738cd3ac9fSNicolas Dichtel if (!unresolved) 23748cd3ac9fSNicolas Dichtel len = len 23758cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_IIF */ 23768cd3ac9fSNicolas Dichtel + nla_total_size(0) /* RTA_MULTIPATH */ 23778cd3ac9fSNicolas Dichtel + maxvif * NLA_ALIGN(sizeof(struct rtnexthop)) 23788cd3ac9fSNicolas Dichtel /* RTA_MFC_STATS */ 2379a9a08042SNicolas Dichtel + nla_total_size_64bit(sizeof(struct rta_mfc_stats)) 23808cd3ac9fSNicolas Dichtel ; 23818cd3ac9fSNicolas Dichtel 23828cd3ac9fSNicolas Dichtel return len; 23838cd3ac9fSNicolas Dichtel } 23848cd3ac9fSNicolas Dichtel 23858cd3ac9fSNicolas Dichtel static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, 23868cd3ac9fSNicolas Dichtel int cmd) 23878cd3ac9fSNicolas Dichtel { 23888cd3ac9fSNicolas Dichtel struct net *net = read_pnet(&mrt->net); 23898cd3ac9fSNicolas Dichtel struct sk_buff *skb; 23908cd3ac9fSNicolas Dichtel int err = -ENOBUFS; 23918cd3ac9fSNicolas Dichtel 2392494fff56SYuval Mintz skb = nlmsg_new(mroute_msgsize(mfc->_c.mfc_parent >= MAXVIFS, 2393494fff56SYuval Mintz mrt->maxvif), 23948cd3ac9fSNicolas Dichtel GFP_ATOMIC); 239551456b29SIan Morris if (!skb) 23968cd3ac9fSNicolas Dichtel goto errout; 23978cd3ac9fSNicolas Dichtel 239865886f43SNicolas Dichtel err = ipmr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0); 23998cd3ac9fSNicolas Dichtel if (err < 0) 24008cd3ac9fSNicolas Dichtel goto errout; 24018cd3ac9fSNicolas Dichtel 24028cd3ac9fSNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE, NULL, GFP_ATOMIC); 24038cd3ac9fSNicolas Dichtel return; 24048cd3ac9fSNicolas Dichtel 24058cd3ac9fSNicolas Dichtel errout: 24068cd3ac9fSNicolas Dichtel kfree_skb(skb); 24078cd3ac9fSNicolas Dichtel if (err < 0) 24088cd3ac9fSNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE, err); 24098cd3ac9fSNicolas Dichtel } 24108cd3ac9fSNicolas Dichtel 24115a645dd8SJulien Gomes static size_t igmpmsg_netlink_msgsize(size_t payloadlen) 24125a645dd8SJulien Gomes { 24135a645dd8SJulien Gomes size_t len = 24145a645dd8SJulien Gomes NLMSG_ALIGN(sizeof(struct rtgenmsg)) 24155a645dd8SJulien Gomes + nla_total_size(1) /* IPMRA_CREPORT_MSGTYPE */ 24165a645dd8SJulien Gomes + nla_total_size(4) /* IPMRA_CREPORT_VIF_ID */ 24175a645dd8SJulien Gomes + nla_total_size(4) /* IPMRA_CREPORT_SRC_ADDR */ 24185a645dd8SJulien Gomes + nla_total_size(4) /* IPMRA_CREPORT_DST_ADDR */ 2419501cb008SPaul Davey + nla_total_size(4) /* IPMRA_CREPORT_TABLE */ 24205a645dd8SJulien Gomes /* IPMRA_CREPORT_PKT */ 24215a645dd8SJulien Gomes + nla_total_size(payloadlen) 24225a645dd8SJulien Gomes ; 24235a645dd8SJulien Gomes 24245a645dd8SJulien Gomes return len; 24255a645dd8SJulien Gomes } 24265a645dd8SJulien Gomes 24270b490b51SEric Dumazet static void igmpmsg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt) 24285a645dd8SJulien Gomes { 24295a645dd8SJulien Gomes struct net *net = read_pnet(&mrt->net); 24305a645dd8SJulien Gomes struct nlmsghdr *nlh; 24315a645dd8SJulien Gomes struct rtgenmsg *rtgenm; 24325a645dd8SJulien Gomes struct igmpmsg *msg; 24335a645dd8SJulien Gomes struct sk_buff *skb; 24345a645dd8SJulien Gomes struct nlattr *nla; 24355a645dd8SJulien Gomes int payloadlen; 24365a645dd8SJulien Gomes 24375a645dd8SJulien Gomes payloadlen = pkt->len - sizeof(struct igmpmsg); 24385a645dd8SJulien Gomes msg = (struct igmpmsg *)skb_network_header(pkt); 24395a645dd8SJulien Gomes 24405a645dd8SJulien Gomes skb = nlmsg_new(igmpmsg_netlink_msgsize(payloadlen), GFP_ATOMIC); 24415a645dd8SJulien Gomes if (!skb) 24425a645dd8SJulien Gomes goto errout; 24435a645dd8SJulien Gomes 24445a645dd8SJulien Gomes nlh = nlmsg_put(skb, 0, 0, RTM_NEWCACHEREPORT, 24455a645dd8SJulien Gomes sizeof(struct rtgenmsg), 0); 24465a645dd8SJulien Gomes if (!nlh) 24475a645dd8SJulien Gomes goto errout; 24485a645dd8SJulien Gomes rtgenm = nlmsg_data(nlh); 24495a645dd8SJulien Gomes rtgenm->rtgen_family = RTNL_FAMILY_IPMR; 24505a645dd8SJulien Gomes if (nla_put_u8(skb, IPMRA_CREPORT_MSGTYPE, msg->im_msgtype) || 2451bb82067cSPaul Davey nla_put_u32(skb, IPMRA_CREPORT_VIF_ID, msg->im_vif | (msg->im_vif_hi << 8)) || 24525a645dd8SJulien Gomes nla_put_in_addr(skb, IPMRA_CREPORT_SRC_ADDR, 24535a645dd8SJulien Gomes msg->im_src.s_addr) || 24545a645dd8SJulien Gomes nla_put_in_addr(skb, IPMRA_CREPORT_DST_ADDR, 2455501cb008SPaul Davey msg->im_dst.s_addr) || 2456501cb008SPaul Davey nla_put_u32(skb, IPMRA_CREPORT_TABLE, mrt->id)) 24575a645dd8SJulien Gomes goto nla_put_failure; 24585a645dd8SJulien Gomes 24595a645dd8SJulien Gomes nla = nla_reserve(skb, IPMRA_CREPORT_PKT, payloadlen); 24605a645dd8SJulien Gomes if (!nla || skb_copy_bits(pkt, sizeof(struct igmpmsg), 24615a645dd8SJulien Gomes nla_data(nla), payloadlen)) 24625a645dd8SJulien Gomes goto nla_put_failure; 24635a645dd8SJulien Gomes 24645a645dd8SJulien Gomes nlmsg_end(skb, nlh); 24655a645dd8SJulien Gomes 24665a645dd8SJulien Gomes rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE_R, NULL, GFP_ATOMIC); 24675a645dd8SJulien Gomes return; 24685a645dd8SJulien Gomes 24695a645dd8SJulien Gomes nla_put_failure: 24705a645dd8SJulien Gomes nlmsg_cancel(skb, nlh); 24715a645dd8SJulien Gomes errout: 24725a645dd8SJulien Gomes kfree_skb(skb); 24735a645dd8SJulien Gomes rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE_R, -ENOBUFS); 24745a645dd8SJulien Gomes } 24755a645dd8SJulien Gomes 2476d0440029SJakub Kicinski static int ipmr_rtm_valid_getroute_req(struct sk_buff *skb, 2477d0440029SJakub Kicinski const struct nlmsghdr *nlh, 2478d0440029SJakub Kicinski struct nlattr **tb, 2479d0440029SJakub Kicinski struct netlink_ext_ack *extack) 2480d0440029SJakub Kicinski { 2481d0440029SJakub Kicinski struct rtmsg *rtm; 2482d0440029SJakub Kicinski int i, err; 2483d0440029SJakub Kicinski 2484d0440029SJakub Kicinski if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { 2485d0440029SJakub Kicinski NL_SET_ERR_MSG(extack, "ipv4: Invalid header for multicast route get request"); 2486d0440029SJakub Kicinski return -EINVAL; 2487d0440029SJakub Kicinski } 2488d0440029SJakub Kicinski 2489d0440029SJakub Kicinski if (!netlink_strict_get_check(skb)) 24908cb08174SJohannes Berg return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX, 2491d0440029SJakub Kicinski rtm_ipv4_policy, extack); 2492d0440029SJakub Kicinski 2493d0440029SJakub Kicinski rtm = nlmsg_data(nlh); 2494d0440029SJakub Kicinski if ((rtm->rtm_src_len && rtm->rtm_src_len != 32) || 2495d0440029SJakub Kicinski (rtm->rtm_dst_len && rtm->rtm_dst_len != 32) || 2496d0440029SJakub Kicinski rtm->rtm_tos || rtm->rtm_table || rtm->rtm_protocol || 2497d0440029SJakub Kicinski rtm->rtm_scope || rtm->rtm_type || rtm->rtm_flags) { 2498d0440029SJakub Kicinski NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for multicast route get request"); 2499d0440029SJakub Kicinski return -EINVAL; 2500d0440029SJakub Kicinski } 2501d0440029SJakub Kicinski 25028cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, 2503d0440029SJakub Kicinski rtm_ipv4_policy, extack); 2504d0440029SJakub Kicinski if (err) 2505d0440029SJakub Kicinski return err; 2506d0440029SJakub Kicinski 2507d0440029SJakub Kicinski if ((tb[RTA_SRC] && !rtm->rtm_src_len) || 2508d0440029SJakub Kicinski (tb[RTA_DST] && !rtm->rtm_dst_len)) { 2509d0440029SJakub Kicinski NL_SET_ERR_MSG(extack, "ipv4: rtm_src_len and rtm_dst_len must be 32 for IPv4"); 2510d0440029SJakub Kicinski return -EINVAL; 2511d0440029SJakub Kicinski } 2512d0440029SJakub Kicinski 2513d0440029SJakub Kicinski for (i = 0; i <= RTA_MAX; i++) { 2514d0440029SJakub Kicinski if (!tb[i]) 2515d0440029SJakub Kicinski continue; 2516d0440029SJakub Kicinski 2517d0440029SJakub Kicinski switch (i) { 2518d0440029SJakub Kicinski case RTA_SRC: 2519d0440029SJakub Kicinski case RTA_DST: 2520d0440029SJakub Kicinski case RTA_TABLE: 2521d0440029SJakub Kicinski break; 2522d0440029SJakub Kicinski default: 2523d0440029SJakub Kicinski NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in multicast route get request"); 2524d0440029SJakub Kicinski return -EINVAL; 2525d0440029SJakub Kicinski } 2526d0440029SJakub Kicinski } 2527d0440029SJakub Kicinski 2528d0440029SJakub Kicinski return 0; 2529d0440029SJakub Kicinski } 2530d0440029SJakub Kicinski 25314f75ba69SDonald Sharp static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, 25324f75ba69SDonald Sharp struct netlink_ext_ack *extack) 25334f75ba69SDonald Sharp { 25344f75ba69SDonald Sharp struct net *net = sock_net(in_skb->sk); 25354f75ba69SDonald Sharp struct nlattr *tb[RTA_MAX + 1]; 25364f75ba69SDonald Sharp struct sk_buff *skb = NULL; 25374f75ba69SDonald Sharp struct mfc_cache *cache; 25384f75ba69SDonald Sharp struct mr_table *mrt; 25394f75ba69SDonald Sharp __be32 src, grp; 25404f75ba69SDonald Sharp u32 tableid; 25414f75ba69SDonald Sharp int err; 25424f75ba69SDonald Sharp 2543d0440029SJakub Kicinski err = ipmr_rtm_valid_getroute_req(in_skb, nlh, tb, extack); 25444f75ba69SDonald Sharp if (err < 0) 25454f75ba69SDonald Sharp goto errout; 25464f75ba69SDonald Sharp 25474f75ba69SDonald Sharp src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0; 25484f75ba69SDonald Sharp grp = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0; 25494f75ba69SDonald Sharp tableid = tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : 0; 25504f75ba69SDonald Sharp 25514f75ba69SDonald Sharp mrt = ipmr_get_table(net, tableid ? tableid : RT_TABLE_DEFAULT); 25522e3d232eSDan Carpenter if (!mrt) { 25532e3d232eSDan Carpenter err = -ENOENT; 25544f75ba69SDonald Sharp goto errout_free; 25554f75ba69SDonald Sharp } 25564f75ba69SDonald Sharp 25574f75ba69SDonald Sharp /* entries are added/deleted only under RTNL */ 25584f75ba69SDonald Sharp rcu_read_lock(); 25594f75ba69SDonald Sharp cache = ipmr_cache_find(mrt, src, grp); 25604f75ba69SDonald Sharp rcu_read_unlock(); 25614f75ba69SDonald Sharp if (!cache) { 25624f75ba69SDonald Sharp err = -ENOENT; 25634f75ba69SDonald Sharp goto errout_free; 25644f75ba69SDonald Sharp } 25654f75ba69SDonald Sharp 25664f75ba69SDonald Sharp skb = nlmsg_new(mroute_msgsize(false, mrt->maxvif), GFP_KERNEL); 25674f75ba69SDonald Sharp if (!skb) { 25684f75ba69SDonald Sharp err = -ENOBUFS; 25694f75ba69SDonald Sharp goto errout_free; 25704f75ba69SDonald Sharp } 25714f75ba69SDonald Sharp 25724f75ba69SDonald Sharp err = ipmr_fill_mroute(mrt, skb, NETLINK_CB(in_skb).portid, 25734f75ba69SDonald Sharp nlh->nlmsg_seq, cache, 25744f75ba69SDonald Sharp RTM_NEWROUTE, 0); 25754f75ba69SDonald Sharp if (err < 0) 25764f75ba69SDonald Sharp goto errout_free; 25774f75ba69SDonald Sharp 25784f75ba69SDonald Sharp err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 25794f75ba69SDonald Sharp 25804f75ba69SDonald Sharp errout: 25814f75ba69SDonald Sharp return err; 25824f75ba69SDonald Sharp 25834f75ba69SDonald Sharp errout_free: 25844f75ba69SDonald Sharp kfree_skb(skb); 25854f75ba69SDonald Sharp goto errout; 25864f75ba69SDonald Sharp } 25874f75ba69SDonald Sharp 2588cb6a4e46SPatrick McHardy static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) 2589cb6a4e46SPatrick McHardy { 25904724676dSDavid Ahern struct fib_dump_filter filter = {}; 25914724676dSDavid Ahern int err; 25924724676dSDavid Ahern 2593cb167893SDavid Ahern if (cb->strict_check) { 25944724676dSDavid Ahern err = ip_valid_fib_dump_req(sock_net(skb->sk), cb->nlh, 2595effe6792SDavid Ahern &filter, cb); 2596e8ba330aSDavid Ahern if (err < 0) 2597e8ba330aSDavid Ahern return err; 2598e8ba330aSDavid Ahern } 2599e8ba330aSDavid Ahern 2600cb167893SDavid Ahern if (filter.table_id) { 2601cb167893SDavid Ahern struct mr_table *mrt; 2602cb167893SDavid Ahern 2603cb167893SDavid Ahern mrt = ipmr_get_table(sock_net(skb->sk), filter.table_id); 2604cb167893SDavid Ahern if (!mrt) { 260541b4bd98SSabrina Dubroca if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IPMR) 2606ae677bbbSDavid Ahern return skb->len; 2607ae677bbbSDavid Ahern 2608cb167893SDavid Ahern NL_SET_ERR_MSG(cb->extack, "ipv4: MR table does not exist"); 2609cb167893SDavid Ahern return -ENOENT; 2610cb167893SDavid Ahern } 2611cb167893SDavid Ahern err = mr_table_dump(mrt, skb, cb, _ipmr_fill_mroute, 2612cb167893SDavid Ahern &mfc_unres_lock, &filter); 2613cb167893SDavid Ahern return skb->len ? : err; 2614cb167893SDavid Ahern } 2615cb167893SDavid Ahern 26167b0db857SYuval Mintz return mr_rtm_dumproute(skb, cb, ipmr_mr_table_iter, 2617cb167893SDavid Ahern _ipmr_fill_mroute, &mfc_unres_lock, &filter); 2618cb6a4e46SPatrick McHardy } 2619cb6a4e46SPatrick McHardy 2620ccbb0aa6SNikolay Aleksandrov static const struct nla_policy rtm_ipmr_policy[RTA_MAX + 1] = { 2621ccbb0aa6SNikolay Aleksandrov [RTA_SRC] = { .type = NLA_U32 }, 2622ccbb0aa6SNikolay Aleksandrov [RTA_DST] = { .type = NLA_U32 }, 2623ccbb0aa6SNikolay Aleksandrov [RTA_IIF] = { .type = NLA_U32 }, 2624ccbb0aa6SNikolay Aleksandrov [RTA_TABLE] = { .type = NLA_U32 }, 2625ccbb0aa6SNikolay Aleksandrov [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2626ccbb0aa6SNikolay Aleksandrov }; 2627ccbb0aa6SNikolay Aleksandrov 2628ccbb0aa6SNikolay Aleksandrov static bool ipmr_rtm_validate_proto(unsigned char rtm_protocol) 2629ccbb0aa6SNikolay Aleksandrov { 2630ccbb0aa6SNikolay Aleksandrov switch (rtm_protocol) { 2631ccbb0aa6SNikolay Aleksandrov case RTPROT_STATIC: 2632ccbb0aa6SNikolay Aleksandrov case RTPROT_MROUTED: 2633ccbb0aa6SNikolay Aleksandrov return true; 2634ccbb0aa6SNikolay Aleksandrov } 2635ccbb0aa6SNikolay Aleksandrov return false; 2636ccbb0aa6SNikolay Aleksandrov } 2637ccbb0aa6SNikolay Aleksandrov 2638ccbb0aa6SNikolay Aleksandrov static int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl *mfcc) 2639ccbb0aa6SNikolay Aleksandrov { 2640ccbb0aa6SNikolay Aleksandrov struct rtnexthop *rtnh = nla_data(nla); 2641ccbb0aa6SNikolay Aleksandrov int remaining = nla_len(nla), vifi = 0; 2642ccbb0aa6SNikolay Aleksandrov 2643ccbb0aa6SNikolay Aleksandrov while (rtnh_ok(rtnh, remaining)) { 2644ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_ttls[vifi] = rtnh->rtnh_hops; 2645ccbb0aa6SNikolay Aleksandrov if (++vifi == MAXVIFS) 2646ccbb0aa6SNikolay Aleksandrov break; 2647ccbb0aa6SNikolay Aleksandrov rtnh = rtnh_next(rtnh, &remaining); 2648ccbb0aa6SNikolay Aleksandrov } 2649ccbb0aa6SNikolay Aleksandrov 2650ccbb0aa6SNikolay Aleksandrov return remaining > 0 ? -EINVAL : vifi; 2651ccbb0aa6SNikolay Aleksandrov } 2652ccbb0aa6SNikolay Aleksandrov 2653ccbb0aa6SNikolay Aleksandrov /* returns < 0 on error, 0 for ADD_MFC and 1 for ADD_MFC_PROXY */ 2654ccbb0aa6SNikolay Aleksandrov static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh, 2655ccbb0aa6SNikolay Aleksandrov struct mfcctl *mfcc, int *mrtsock, 2656c21ef3e3SDavid Ahern struct mr_table **mrtret, 2657c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 2658ccbb0aa6SNikolay Aleksandrov { 2659ccbb0aa6SNikolay Aleksandrov struct net_device *dev = NULL; 2660ccbb0aa6SNikolay Aleksandrov u32 tblid = RT_TABLE_DEFAULT; 2661ccbb0aa6SNikolay Aleksandrov struct mr_table *mrt; 2662ccbb0aa6SNikolay Aleksandrov struct nlattr *attr; 2663ccbb0aa6SNikolay Aleksandrov struct rtmsg *rtm; 2664ccbb0aa6SNikolay Aleksandrov int ret, rem; 2665ccbb0aa6SNikolay Aleksandrov 26668cb08174SJohannes Berg ret = nlmsg_validate_deprecated(nlh, sizeof(*rtm), RTA_MAX, 26678cb08174SJohannes Berg rtm_ipmr_policy, extack); 2668ccbb0aa6SNikolay Aleksandrov if (ret < 0) 2669ccbb0aa6SNikolay Aleksandrov goto out; 2670ccbb0aa6SNikolay Aleksandrov rtm = nlmsg_data(nlh); 2671ccbb0aa6SNikolay Aleksandrov 2672ccbb0aa6SNikolay Aleksandrov ret = -EINVAL; 2673ccbb0aa6SNikolay Aleksandrov if (rtm->rtm_family != RTNL_FAMILY_IPMR || rtm->rtm_dst_len != 32 || 2674ccbb0aa6SNikolay Aleksandrov rtm->rtm_type != RTN_MULTICAST || 2675ccbb0aa6SNikolay Aleksandrov rtm->rtm_scope != RT_SCOPE_UNIVERSE || 2676ccbb0aa6SNikolay Aleksandrov !ipmr_rtm_validate_proto(rtm->rtm_protocol)) 2677ccbb0aa6SNikolay Aleksandrov goto out; 2678ccbb0aa6SNikolay Aleksandrov 2679ccbb0aa6SNikolay Aleksandrov memset(mfcc, 0, sizeof(*mfcc)); 2680ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_parent = -1; 2681ccbb0aa6SNikolay Aleksandrov ret = 0; 2682ccbb0aa6SNikolay Aleksandrov nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), rem) { 2683ccbb0aa6SNikolay Aleksandrov switch (nla_type(attr)) { 2684ccbb0aa6SNikolay Aleksandrov case RTA_SRC: 2685ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_origin.s_addr = nla_get_be32(attr); 2686ccbb0aa6SNikolay Aleksandrov break; 2687ccbb0aa6SNikolay Aleksandrov case RTA_DST: 2688ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_mcastgrp.s_addr = nla_get_be32(attr); 2689ccbb0aa6SNikolay Aleksandrov break; 2690ccbb0aa6SNikolay Aleksandrov case RTA_IIF: 2691ccbb0aa6SNikolay Aleksandrov dev = __dev_get_by_index(net, nla_get_u32(attr)); 2692ccbb0aa6SNikolay Aleksandrov if (!dev) { 2693ccbb0aa6SNikolay Aleksandrov ret = -ENODEV; 2694ccbb0aa6SNikolay Aleksandrov goto out; 2695ccbb0aa6SNikolay Aleksandrov } 2696ccbb0aa6SNikolay Aleksandrov break; 2697ccbb0aa6SNikolay Aleksandrov case RTA_MULTIPATH: 2698ccbb0aa6SNikolay Aleksandrov if (ipmr_nla_get_ttls(attr, mfcc) < 0) { 2699ccbb0aa6SNikolay Aleksandrov ret = -EINVAL; 2700ccbb0aa6SNikolay Aleksandrov goto out; 2701ccbb0aa6SNikolay Aleksandrov } 2702ccbb0aa6SNikolay Aleksandrov break; 2703ccbb0aa6SNikolay Aleksandrov case RTA_PREFSRC: 2704ccbb0aa6SNikolay Aleksandrov ret = 1; 2705ccbb0aa6SNikolay Aleksandrov break; 2706ccbb0aa6SNikolay Aleksandrov case RTA_TABLE: 2707ccbb0aa6SNikolay Aleksandrov tblid = nla_get_u32(attr); 2708ccbb0aa6SNikolay Aleksandrov break; 2709ccbb0aa6SNikolay Aleksandrov } 2710ccbb0aa6SNikolay Aleksandrov } 2711ccbb0aa6SNikolay Aleksandrov mrt = ipmr_get_table(net, tblid); 2712ccbb0aa6SNikolay Aleksandrov if (!mrt) { 2713ccbb0aa6SNikolay Aleksandrov ret = -ENOENT; 2714ccbb0aa6SNikolay Aleksandrov goto out; 2715ccbb0aa6SNikolay Aleksandrov } 2716ccbb0aa6SNikolay Aleksandrov *mrtret = mrt; 2717ccbb0aa6SNikolay Aleksandrov *mrtsock = rtm->rtm_protocol == RTPROT_MROUTED ? 1 : 0; 2718ccbb0aa6SNikolay Aleksandrov if (dev) 2719ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_parent = ipmr_find_vif(mrt, dev); 2720ccbb0aa6SNikolay Aleksandrov 2721ccbb0aa6SNikolay Aleksandrov out: 2722ccbb0aa6SNikolay Aleksandrov return ret; 2723ccbb0aa6SNikolay Aleksandrov } 2724ccbb0aa6SNikolay Aleksandrov 2725ccbb0aa6SNikolay Aleksandrov /* takes care of both newroute and delroute */ 2726c21ef3e3SDavid Ahern static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh, 2727c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 2728ccbb0aa6SNikolay Aleksandrov { 2729ccbb0aa6SNikolay Aleksandrov struct net *net = sock_net(skb->sk); 2730ccbb0aa6SNikolay Aleksandrov int ret, mrtsock, parent; 2731ccbb0aa6SNikolay Aleksandrov struct mr_table *tbl; 2732ccbb0aa6SNikolay Aleksandrov struct mfcctl mfcc; 2733ccbb0aa6SNikolay Aleksandrov 2734ccbb0aa6SNikolay Aleksandrov mrtsock = 0; 2735ccbb0aa6SNikolay Aleksandrov tbl = NULL; 2736c21ef3e3SDavid Ahern ret = rtm_to_ipmr_mfcc(net, nlh, &mfcc, &mrtsock, &tbl, extack); 2737ccbb0aa6SNikolay Aleksandrov if (ret < 0) 2738ccbb0aa6SNikolay Aleksandrov return ret; 2739ccbb0aa6SNikolay Aleksandrov 2740ccbb0aa6SNikolay Aleksandrov parent = ret ? mfcc.mfcc_parent : -1; 2741ccbb0aa6SNikolay Aleksandrov if (nlh->nlmsg_type == RTM_NEWROUTE) 2742ccbb0aa6SNikolay Aleksandrov return ipmr_mfc_add(net, tbl, &mfcc, mrtsock, parent); 2743ccbb0aa6SNikolay Aleksandrov else 2744ccbb0aa6SNikolay Aleksandrov return ipmr_mfc_delete(tbl, &mfcc, parent); 2745ccbb0aa6SNikolay Aleksandrov } 2746ccbb0aa6SNikolay Aleksandrov 2747772c344dSNikolay Aleksandrov static bool ipmr_fill_table(struct mr_table *mrt, struct sk_buff *skb) 2748772c344dSNikolay Aleksandrov { 2749772c344dSNikolay Aleksandrov u32 queue_len = atomic_read(&mrt->cache_resolve_queue_len); 2750772c344dSNikolay Aleksandrov 2751772c344dSNikolay Aleksandrov if (nla_put_u32(skb, IPMRA_TABLE_ID, mrt->id) || 2752772c344dSNikolay Aleksandrov nla_put_u32(skb, IPMRA_TABLE_CACHE_RES_QUEUE_LEN, queue_len) || 2753772c344dSNikolay Aleksandrov nla_put_s32(skb, IPMRA_TABLE_MROUTE_REG_VIF_NUM, 2754772c344dSNikolay Aleksandrov mrt->mroute_reg_vif_num) || 2755772c344dSNikolay Aleksandrov nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_ASSERT, 2756772c344dSNikolay Aleksandrov mrt->mroute_do_assert) || 2757c921c207SNikolay Aleksandrov nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_PIM, mrt->mroute_do_pim) || 2758c921c207SNikolay Aleksandrov nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_WRVIFWHOLE, 2759c921c207SNikolay Aleksandrov mrt->mroute_do_wrvifwhole)) 2760772c344dSNikolay Aleksandrov return false; 2761772c344dSNikolay Aleksandrov 2762772c344dSNikolay Aleksandrov return true; 2763772c344dSNikolay Aleksandrov } 2764772c344dSNikolay Aleksandrov 2765772c344dSNikolay Aleksandrov static bool ipmr_fill_vif(struct mr_table *mrt, u32 vifid, struct sk_buff *skb) 2766772c344dSNikolay Aleksandrov { 2767ebc31979SEric Dumazet struct net_device *vif_dev; 2768772c344dSNikolay Aleksandrov struct nlattr *vif_nest; 2769772c344dSNikolay Aleksandrov struct vif_device *vif; 2770772c344dSNikolay Aleksandrov 2771ebc31979SEric Dumazet vif = &mrt->vif_table[vifid]; 27720fcae3c8SEric Dumazet vif_dev = rtnl_dereference(vif->dev); 2773772c344dSNikolay Aleksandrov /* if the VIF doesn't exist just continue */ 2774ebc31979SEric Dumazet if (!vif_dev) 2775772c344dSNikolay Aleksandrov return true; 2776772c344dSNikolay Aleksandrov 2777ae0be8deSMichal Kubecek vif_nest = nla_nest_start_noflag(skb, IPMRA_VIF); 2778772c344dSNikolay Aleksandrov if (!vif_nest) 2779772c344dSNikolay Aleksandrov return false; 2780ebc31979SEric Dumazet 2781ebc31979SEric Dumazet if (nla_put_u32(skb, IPMRA_VIFA_IFINDEX, vif_dev->ifindex) || 2782772c344dSNikolay Aleksandrov nla_put_u32(skb, IPMRA_VIFA_VIF_ID, vifid) || 2783772c344dSNikolay Aleksandrov nla_put_u16(skb, IPMRA_VIFA_FLAGS, vif->flags) || 2784772c344dSNikolay Aleksandrov nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_IN, vif->bytes_in, 2785772c344dSNikolay Aleksandrov IPMRA_VIFA_PAD) || 2786772c344dSNikolay Aleksandrov nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_OUT, vif->bytes_out, 2787772c344dSNikolay Aleksandrov IPMRA_VIFA_PAD) || 2788772c344dSNikolay Aleksandrov nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_IN, vif->pkt_in, 2789772c344dSNikolay Aleksandrov IPMRA_VIFA_PAD) || 2790772c344dSNikolay Aleksandrov nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_OUT, vif->pkt_out, 2791772c344dSNikolay Aleksandrov IPMRA_VIFA_PAD) || 2792772c344dSNikolay Aleksandrov nla_put_be32(skb, IPMRA_VIFA_LOCAL_ADDR, vif->local) || 2793772c344dSNikolay Aleksandrov nla_put_be32(skb, IPMRA_VIFA_REMOTE_ADDR, vif->remote)) { 2794772c344dSNikolay Aleksandrov nla_nest_cancel(skb, vif_nest); 2795772c344dSNikolay Aleksandrov return false; 2796772c344dSNikolay Aleksandrov } 2797772c344dSNikolay Aleksandrov nla_nest_end(skb, vif_nest); 2798772c344dSNikolay Aleksandrov 2799772c344dSNikolay Aleksandrov return true; 2800772c344dSNikolay Aleksandrov } 2801772c344dSNikolay Aleksandrov 280214fc5bb2SDavid Ahern static int ipmr_valid_dumplink(const struct nlmsghdr *nlh, 280314fc5bb2SDavid Ahern struct netlink_ext_ack *extack) 280414fc5bb2SDavid Ahern { 280514fc5bb2SDavid Ahern struct ifinfomsg *ifm; 280614fc5bb2SDavid Ahern 280714fc5bb2SDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 280814fc5bb2SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid header for ipmr link dump"); 280914fc5bb2SDavid Ahern return -EINVAL; 281014fc5bb2SDavid Ahern } 281114fc5bb2SDavid Ahern 281214fc5bb2SDavid Ahern if (nlmsg_attrlen(nlh, sizeof(*ifm))) { 281314fc5bb2SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid data after header in ipmr link dump"); 281414fc5bb2SDavid Ahern return -EINVAL; 281514fc5bb2SDavid Ahern } 281614fc5bb2SDavid Ahern 281714fc5bb2SDavid Ahern ifm = nlmsg_data(nlh); 281814fc5bb2SDavid Ahern if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags || 281914fc5bb2SDavid Ahern ifm->ifi_change || ifm->ifi_index) { 282014fc5bb2SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in header for ipmr link dump request"); 282114fc5bb2SDavid Ahern return -EINVAL; 282214fc5bb2SDavid Ahern } 282314fc5bb2SDavid Ahern 282414fc5bb2SDavid Ahern return 0; 282514fc5bb2SDavid Ahern } 282614fc5bb2SDavid Ahern 2827772c344dSNikolay Aleksandrov static int ipmr_rtm_dumplink(struct sk_buff *skb, struct netlink_callback *cb) 2828772c344dSNikolay Aleksandrov { 2829772c344dSNikolay Aleksandrov struct net *net = sock_net(skb->sk); 2830772c344dSNikolay Aleksandrov struct nlmsghdr *nlh = NULL; 2831772c344dSNikolay Aleksandrov unsigned int t = 0, s_t; 2832772c344dSNikolay Aleksandrov unsigned int e = 0, s_e; 2833772c344dSNikolay Aleksandrov struct mr_table *mrt; 2834772c344dSNikolay Aleksandrov 283514fc5bb2SDavid Ahern if (cb->strict_check) { 283614fc5bb2SDavid Ahern int err = ipmr_valid_dumplink(cb->nlh, cb->extack); 283714fc5bb2SDavid Ahern 283814fc5bb2SDavid Ahern if (err < 0) 283914fc5bb2SDavid Ahern return err; 284014fc5bb2SDavid Ahern } 284114fc5bb2SDavid Ahern 2842772c344dSNikolay Aleksandrov s_t = cb->args[0]; 2843772c344dSNikolay Aleksandrov s_e = cb->args[1]; 2844772c344dSNikolay Aleksandrov 2845772c344dSNikolay Aleksandrov ipmr_for_each_table(mrt, net) { 2846772c344dSNikolay Aleksandrov struct nlattr *vifs, *af; 2847772c344dSNikolay Aleksandrov struct ifinfomsg *hdr; 2848772c344dSNikolay Aleksandrov u32 i; 2849772c344dSNikolay Aleksandrov 2850772c344dSNikolay Aleksandrov if (t < s_t) 2851772c344dSNikolay Aleksandrov goto skip_table; 2852772c344dSNikolay Aleksandrov nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, 2853772c344dSNikolay Aleksandrov cb->nlh->nlmsg_seq, RTM_NEWLINK, 2854772c344dSNikolay Aleksandrov sizeof(*hdr), NLM_F_MULTI); 2855772c344dSNikolay Aleksandrov if (!nlh) 2856772c344dSNikolay Aleksandrov break; 2857772c344dSNikolay Aleksandrov 2858772c344dSNikolay Aleksandrov hdr = nlmsg_data(nlh); 2859772c344dSNikolay Aleksandrov memset(hdr, 0, sizeof(*hdr)); 2860772c344dSNikolay Aleksandrov hdr->ifi_family = RTNL_FAMILY_IPMR; 2861772c344dSNikolay Aleksandrov 2862ae0be8deSMichal Kubecek af = nla_nest_start_noflag(skb, IFLA_AF_SPEC); 2863772c344dSNikolay Aleksandrov if (!af) { 2864772c344dSNikolay Aleksandrov nlmsg_cancel(skb, nlh); 2865772c344dSNikolay Aleksandrov goto out; 2866772c344dSNikolay Aleksandrov } 2867772c344dSNikolay Aleksandrov 2868772c344dSNikolay Aleksandrov if (!ipmr_fill_table(mrt, skb)) { 2869772c344dSNikolay Aleksandrov nlmsg_cancel(skb, nlh); 2870772c344dSNikolay Aleksandrov goto out; 2871772c344dSNikolay Aleksandrov } 2872772c344dSNikolay Aleksandrov 2873ae0be8deSMichal Kubecek vifs = nla_nest_start_noflag(skb, IPMRA_TABLE_VIFS); 2874772c344dSNikolay Aleksandrov if (!vifs) { 2875772c344dSNikolay Aleksandrov nla_nest_end(skb, af); 2876772c344dSNikolay Aleksandrov nlmsg_end(skb, nlh); 2877772c344dSNikolay Aleksandrov goto out; 2878772c344dSNikolay Aleksandrov } 2879772c344dSNikolay Aleksandrov for (i = 0; i < mrt->maxvif; i++) { 2880772c344dSNikolay Aleksandrov if (e < s_e) 2881772c344dSNikolay Aleksandrov goto skip_entry; 2882772c344dSNikolay Aleksandrov if (!ipmr_fill_vif(mrt, i, skb)) { 2883772c344dSNikolay Aleksandrov nla_nest_end(skb, vifs); 2884772c344dSNikolay Aleksandrov nla_nest_end(skb, af); 2885772c344dSNikolay Aleksandrov nlmsg_end(skb, nlh); 2886772c344dSNikolay Aleksandrov goto out; 2887772c344dSNikolay Aleksandrov } 2888772c344dSNikolay Aleksandrov skip_entry: 2889772c344dSNikolay Aleksandrov e++; 2890772c344dSNikolay Aleksandrov } 2891772c344dSNikolay Aleksandrov s_e = 0; 2892772c344dSNikolay Aleksandrov e = 0; 2893772c344dSNikolay Aleksandrov nla_nest_end(skb, vifs); 2894772c344dSNikolay Aleksandrov nla_nest_end(skb, af); 2895772c344dSNikolay Aleksandrov nlmsg_end(skb, nlh); 2896772c344dSNikolay Aleksandrov skip_table: 2897772c344dSNikolay Aleksandrov t++; 2898772c344dSNikolay Aleksandrov } 2899772c344dSNikolay Aleksandrov 2900772c344dSNikolay Aleksandrov out: 2901772c344dSNikolay Aleksandrov cb->args[1] = e; 2902772c344dSNikolay Aleksandrov cb->args[0] = t; 2903772c344dSNikolay Aleksandrov 2904772c344dSNikolay Aleksandrov return skb->len; 2905772c344dSNikolay Aleksandrov } 2906772c344dSNikolay Aleksandrov 29071da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 29087ef8f65dSNikolay Aleksandrov /* The /proc interfaces to multicast routing : 2909a8cb16ddSEric Dumazet * /proc/net/ip_mr_cache & /proc/net/ip_mr_vif 29101da177e4SLinus Torvalds */ 29111da177e4SLinus Torvalds 29121da177e4SLinus Torvalds static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos) 2913b96ef16dSEric Dumazet __acquires(RCU) 29141da177e4SLinus Torvalds { 29153feda6b4SYuval Mintz struct mr_vif_iter *iter = seq->private; 2916f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2917f0ad0860SPatrick McHardy struct mr_table *mrt; 2918f0ad0860SPatrick McHardy 2919f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 292051456b29SIan Morris if (!mrt) 2921f0ad0860SPatrick McHardy return ERR_PTR(-ENOENT); 2922f0ad0860SPatrick McHardy 2923f0ad0860SPatrick McHardy iter->mrt = mrt; 2924f6bb4514SBenjamin Thery 2925b96ef16dSEric Dumazet rcu_read_lock(); 29263feda6b4SYuval Mintz return mr_vif_seq_start(seq, pos); 29271da177e4SLinus Torvalds } 29281da177e4SLinus Torvalds 29291da177e4SLinus Torvalds static void ipmr_vif_seq_stop(struct seq_file *seq, void *v) 2930b96ef16dSEric Dumazet __releases(RCU) 29311da177e4SLinus Torvalds { 2932b96ef16dSEric Dumazet rcu_read_unlock(); 29331da177e4SLinus Torvalds } 29341da177e4SLinus Torvalds 29351da177e4SLinus Torvalds static int ipmr_vif_seq_show(struct seq_file *seq, void *v) 29361da177e4SLinus Torvalds { 29373feda6b4SYuval Mintz struct mr_vif_iter *iter = seq->private; 2938f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 2939f6bb4514SBenjamin Thery 29401da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 29411da177e4SLinus Torvalds seq_puts(seq, 29421da177e4SLinus Torvalds "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n"); 29431da177e4SLinus Torvalds } else { 29441da177e4SLinus Torvalds const struct vif_device *vif = v; 2945ebc31979SEric Dumazet const struct net_device *vif_dev; 2946ebc31979SEric Dumazet const char *name; 29471da177e4SLinus Torvalds 2948ebc31979SEric Dumazet vif_dev = vif_dev_read(vif); 2949ebc31979SEric Dumazet name = vif_dev ? vif_dev->name : "none"; 29501da177e4SLinus Torvalds seq_printf(seq, 295191e6dd82SJames Hogan "%2td %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n", 29520c12295aSPatrick McHardy vif - mrt->vif_table, 29531da177e4SLinus Torvalds name, vif->bytes_in, vif->pkt_in, 29541da177e4SLinus Torvalds vif->bytes_out, vif->pkt_out, 29551da177e4SLinus Torvalds vif->flags, vif->local, vif->remote); 29561da177e4SLinus Torvalds } 29571da177e4SLinus Torvalds return 0; 29581da177e4SLinus Torvalds } 29591da177e4SLinus Torvalds 2960f690808eSStephen Hemminger static const struct seq_operations ipmr_vif_seq_ops = { 29611da177e4SLinus Torvalds .start = ipmr_vif_seq_start, 29623feda6b4SYuval Mintz .next = mr_vif_seq_next, 29631da177e4SLinus Torvalds .stop = ipmr_vif_seq_stop, 29641da177e4SLinus Torvalds .show = ipmr_vif_seq_show, 29651da177e4SLinus Torvalds }; 29661da177e4SLinus Torvalds 29671da177e4SLinus Torvalds static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos) 29681da177e4SLinus Torvalds { 2969f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2970f0ad0860SPatrick McHardy struct mr_table *mrt; 2971f6bb4514SBenjamin Thery 2972f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 297351456b29SIan Morris if (!mrt) 2974f0ad0860SPatrick McHardy return ERR_PTR(-ENOENT); 2975f0ad0860SPatrick McHardy 2976c8d61968SYuval Mintz return mr_mfc_seq_start(seq, pos, mrt, &mfc_unres_lock); 29771da177e4SLinus Torvalds } 29781da177e4SLinus Torvalds 29791da177e4SLinus Torvalds static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) 29801da177e4SLinus Torvalds { 29811da177e4SLinus Torvalds int n; 29821da177e4SLinus Torvalds 29831da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 29841da177e4SLinus Torvalds seq_puts(seq, 29851da177e4SLinus Torvalds "Group Origin Iif Pkts Bytes Wrong Oifs\n"); 29861da177e4SLinus Torvalds } else { 29871da177e4SLinus Torvalds const struct mfc_cache *mfc = v; 2988c8d61968SYuval Mintz const struct mr_mfc_iter *it = seq->private; 2989f0ad0860SPatrick McHardy const struct mr_table *mrt = it->mrt; 29901da177e4SLinus Torvalds 29910eae88f3SEric Dumazet seq_printf(seq, "%08X %08X %-3hd", 29920eae88f3SEric Dumazet (__force u32) mfc->mfc_mcastgrp, 29930eae88f3SEric Dumazet (__force u32) mfc->mfc_origin, 2994494fff56SYuval Mintz mfc->_c.mfc_parent); 29951ea472e2SBenjamin Thery 29960c12295aSPatrick McHardy if (it->cache != &mrt->mfc_unres_queue) { 29971ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 2998494fff56SYuval Mintz mfc->_c.mfc_un.res.pkt, 2999494fff56SYuval Mintz mfc->_c.mfc_un.res.bytes, 3000494fff56SYuval Mintz mfc->_c.mfc_un.res.wrong_if); 3001494fff56SYuval Mintz for (n = mfc->_c.mfc_un.res.minvif; 3002494fff56SYuval Mintz n < mfc->_c.mfc_un.res.maxvif; n++) { 30030c12295aSPatrick McHardy if (VIF_EXISTS(mrt, n) && 3004494fff56SYuval Mintz mfc->_c.mfc_un.res.ttls[n] < 255) 30051da177e4SLinus Torvalds seq_printf(seq, 30061da177e4SLinus Torvalds " %2d:%-3d", 3007494fff56SYuval Mintz n, mfc->_c.mfc_un.res.ttls[n]); 30081da177e4SLinus Torvalds } 30091ea472e2SBenjamin Thery } else { 30101ea472e2SBenjamin Thery /* unresolved mfc_caches don't contain 30111ea472e2SBenjamin Thery * pkt, bytes and wrong_if values 30121ea472e2SBenjamin Thery */ 30131ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul); 30141da177e4SLinus Torvalds } 30151da177e4SLinus Torvalds seq_putc(seq, '\n'); 30161da177e4SLinus Torvalds } 30171da177e4SLinus Torvalds return 0; 30181da177e4SLinus Torvalds } 30191da177e4SLinus Torvalds 3020f690808eSStephen Hemminger static const struct seq_operations ipmr_mfc_seq_ops = { 30211da177e4SLinus Torvalds .start = ipmr_mfc_seq_start, 3022c8d61968SYuval Mintz .next = mr_mfc_seq_next, 3023c8d61968SYuval Mintz .stop = mr_mfc_seq_stop, 30241da177e4SLinus Torvalds .show = ipmr_mfc_seq_show, 30251da177e4SLinus Torvalds }; 30261da177e4SLinus Torvalds #endif 30271da177e4SLinus Torvalds 30281da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V2 302932613090SAlexey Dobriyan static const struct net_protocol pim_protocol = { 30301da177e4SLinus Torvalds .handler = pim_rcv, 30311da177e4SLinus Torvalds }; 30321da177e4SLinus Torvalds #endif 30331da177e4SLinus Torvalds 30344d65b948SYotam Gigi static unsigned int ipmr_seq_read(struct net *net) 30354d65b948SYotam Gigi { 30364d65b948SYotam Gigi ASSERT_RTNL(); 30374d65b948SYotam Gigi 30384d65b948SYotam Gigi return net->ipv4.ipmr_seq + ipmr_rules_seq_read(net); 30394d65b948SYotam Gigi } 30404d65b948SYotam Gigi 3041b7a59557SJiri Pirko static int ipmr_dump(struct net *net, struct notifier_block *nb, 3042b7a59557SJiri Pirko struct netlink_ext_ack *extack) 30434d65b948SYotam Gigi { 3044cdc9f944SYuval Mintz return mr_dump(net, nb, RTNL_FAMILY_IPMR, ipmr_rules_dump, 3045194366b2SEric Dumazet ipmr_mr_table_iter, extack); 30464d65b948SYotam Gigi } 30474d65b948SYotam Gigi 30484d65b948SYotam Gigi static const struct fib_notifier_ops ipmr_notifier_ops_template = { 30494d65b948SYotam Gigi .family = RTNL_FAMILY_IPMR, 30504d65b948SYotam Gigi .fib_seq_read = ipmr_seq_read, 30514d65b948SYotam Gigi .fib_dump = ipmr_dump, 30524d65b948SYotam Gigi .owner = THIS_MODULE, 30534d65b948SYotam Gigi }; 30544d65b948SYotam Gigi 3055ef739d8aSColin Ian King static int __net_init ipmr_notifier_init(struct net *net) 30564d65b948SYotam Gigi { 30574d65b948SYotam Gigi struct fib_notifier_ops *ops; 30584d65b948SYotam Gigi 30594d65b948SYotam Gigi net->ipv4.ipmr_seq = 0; 30604d65b948SYotam Gigi 30614d65b948SYotam Gigi ops = fib_notifier_ops_register(&ipmr_notifier_ops_template, net); 30624d65b948SYotam Gigi if (IS_ERR(ops)) 30634d65b948SYotam Gigi return PTR_ERR(ops); 30644d65b948SYotam Gigi net->ipv4.ipmr_notifier_ops = ops; 30654d65b948SYotam Gigi 30664d65b948SYotam Gigi return 0; 30674d65b948SYotam Gigi } 30684d65b948SYotam Gigi 30694d65b948SYotam Gigi static void __net_exit ipmr_notifier_exit(struct net *net) 30704d65b948SYotam Gigi { 30714d65b948SYotam Gigi fib_notifier_ops_unregister(net->ipv4.ipmr_notifier_ops); 30724d65b948SYotam Gigi net->ipv4.ipmr_notifier_ops = NULL; 30734d65b948SYotam Gigi } 30744d65b948SYotam Gigi 30757ef8f65dSNikolay Aleksandrov /* Setup for IP multicast routing */ 3076cf958ae3SBenjamin Thery static int __net_init ipmr_net_init(struct net *net) 3077cf958ae3SBenjamin Thery { 3078f0ad0860SPatrick McHardy int err; 3079cf958ae3SBenjamin Thery 30804d65b948SYotam Gigi err = ipmr_notifier_init(net); 30814d65b948SYotam Gigi if (err) 30824d65b948SYotam Gigi goto ipmr_notifier_fail; 30834d65b948SYotam Gigi 3084f0ad0860SPatrick McHardy err = ipmr_rules_init(net); 3085f0ad0860SPatrick McHardy if (err < 0) 30864d65b948SYotam Gigi goto ipmr_rules_fail; 3087f6bb4514SBenjamin Thery 3088f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 3089f6bb4514SBenjamin Thery err = -ENOMEM; 3090c3506372SChristoph Hellwig if (!proc_create_net("ip_mr_vif", 0, net->proc_net, &ipmr_vif_seq_ops, 3091c3506372SChristoph Hellwig sizeof(struct mr_vif_iter))) 3092f6bb4514SBenjamin Thery goto proc_vif_fail; 3093c3506372SChristoph Hellwig if (!proc_create_net("ip_mr_cache", 0, net->proc_net, &ipmr_mfc_seq_ops, 3094c3506372SChristoph Hellwig sizeof(struct mr_mfc_iter))) 3095f6bb4514SBenjamin Thery goto proc_cache_fail; 3096f6bb4514SBenjamin Thery #endif 30972bb8b26cSBenjamin Thery return 0; 30982bb8b26cSBenjamin Thery 3099f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 3100f6bb4514SBenjamin Thery proc_cache_fail: 3101ece31ffdSGao feng remove_proc_entry("ip_mr_vif", net->proc_net); 3102f6bb4514SBenjamin Thery proc_vif_fail: 3103696e595fSEric Dumazet rtnl_lock(); 3104f0ad0860SPatrick McHardy ipmr_rules_exit(net); 3105696e595fSEric Dumazet rtnl_unlock(); 3106f6bb4514SBenjamin Thery #endif 31074d65b948SYotam Gigi ipmr_rules_fail: 31084d65b948SYotam Gigi ipmr_notifier_exit(net); 31094d65b948SYotam Gigi ipmr_notifier_fail: 3110cf958ae3SBenjamin Thery return err; 3111cf958ae3SBenjamin Thery } 3112cf958ae3SBenjamin Thery 3113cf958ae3SBenjamin Thery static void __net_exit ipmr_net_exit(struct net *net) 3114cf958ae3SBenjamin Thery { 3115f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 3116ece31ffdSGao feng remove_proc_entry("ip_mr_cache", net->proc_net); 3117ece31ffdSGao feng remove_proc_entry("ip_mr_vif", net->proc_net); 3118f6bb4514SBenjamin Thery #endif 31194d65b948SYotam Gigi ipmr_notifier_exit(net); 3120696e595fSEric Dumazet } 3121696e595fSEric Dumazet 3122696e595fSEric Dumazet static void __net_exit ipmr_net_exit_batch(struct list_head *net_list) 3123696e595fSEric Dumazet { 3124696e595fSEric Dumazet struct net *net; 3125696e595fSEric Dumazet 3126696e595fSEric Dumazet rtnl_lock(); 3127696e595fSEric Dumazet list_for_each_entry(net, net_list, exit_list) 3128f0ad0860SPatrick McHardy ipmr_rules_exit(net); 3129696e595fSEric Dumazet rtnl_unlock(); 3130cf958ae3SBenjamin Thery } 3131cf958ae3SBenjamin Thery 3132cf958ae3SBenjamin Thery static struct pernet_operations ipmr_net_ops = { 3133cf958ae3SBenjamin Thery .init = ipmr_net_init, 3134cf958ae3SBenjamin Thery .exit = ipmr_net_exit, 3135696e595fSEric Dumazet .exit_batch = ipmr_net_exit_batch, 3136cf958ae3SBenjamin Thery }; 31371da177e4SLinus Torvalds 313803d2f897SWang Chen int __init ip_mr_init(void) 31391da177e4SLinus Torvalds { 314003d2f897SWang Chen int err; 314103d2f897SWang Chen 31421da177e4SLinus Torvalds mrt_cachep = kmem_cache_create("ip_mrt_cache", 31431da177e4SLinus Torvalds sizeof(struct mfc_cache), 3144e5d679f3SAlexey Dobriyan 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, 314520c2df83SPaul Mundt NULL); 314603d2f897SWang Chen 3147cf958ae3SBenjamin Thery err = register_pernet_subsys(&ipmr_net_ops); 3148cf958ae3SBenjamin Thery if (err) 3149cf958ae3SBenjamin Thery goto reg_pernet_fail; 3150cf958ae3SBenjamin Thery 315103d2f897SWang Chen err = register_netdevice_notifier(&ip_mr_notifier); 315203d2f897SWang Chen if (err) 315303d2f897SWang Chen goto reg_notif_fail; 3154403dbb97STom Goff #ifdef CONFIG_IP_PIMSM_V2 3155403dbb97STom Goff if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) { 3156058bd4d2SJoe Perches pr_err("%s: can't add PIM protocol\n", __func__); 3157403dbb97STom Goff err = -EAGAIN; 3158403dbb97STom Goff goto add_proto_fail; 3159403dbb97STom Goff } 3160403dbb97STom Goff #endif 3161c7ac8679SGreg Rose rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, 3162b97bac64SFlorian Westphal ipmr_rtm_getroute, ipmr_rtm_dumproute, 0); 3163ccbb0aa6SNikolay Aleksandrov rtnl_register(RTNL_FAMILY_IPMR, RTM_NEWROUTE, 3164b97bac64SFlorian Westphal ipmr_rtm_route, NULL, 0); 3165ccbb0aa6SNikolay Aleksandrov rtnl_register(RTNL_FAMILY_IPMR, RTM_DELROUTE, 3166b97bac64SFlorian Westphal ipmr_rtm_route, NULL, 0); 3167772c344dSNikolay Aleksandrov 3168772c344dSNikolay Aleksandrov rtnl_register(RTNL_FAMILY_IPMR, RTM_GETLINK, 3169b97bac64SFlorian Westphal NULL, ipmr_rtm_dumplink, 0); 317003d2f897SWang Chen return 0; 3171f6bb4514SBenjamin Thery 3172403dbb97STom Goff #ifdef CONFIG_IP_PIMSM_V2 3173403dbb97STom Goff add_proto_fail: 3174403dbb97STom Goff unregister_netdevice_notifier(&ip_mr_notifier); 3175403dbb97STom Goff #endif 3176c3e38896SBenjamin Thery reg_notif_fail: 3177cf958ae3SBenjamin Thery unregister_pernet_subsys(&ipmr_net_ops); 3178cf958ae3SBenjamin Thery reg_pernet_fail: 3179c3e38896SBenjamin Thery kmem_cache_destroy(mrt_cachep); 318003d2f897SWang Chen return err; 31811da177e4SLinus Torvalds } 3182