11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * IP multicast routing support for mrouted 3.6/3.8 31da177e4SLinus Torvalds * 4113aa838SAlan Cox * (c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk> 51da177e4SLinus Torvalds * Linux Consultancy and Custom Driver Development 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 81da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 91da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 101da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * Fixes: 131da177e4SLinus Torvalds * Michael Chastain : Incorrect size of copying. 141da177e4SLinus Torvalds * Alan Cox : Added the cache manager code 151da177e4SLinus Torvalds * Alan Cox : Fixed the clone/copy bug and device race. 161da177e4SLinus Torvalds * Mike McLagan : Routing by source 171da177e4SLinus Torvalds * Malcolm Beattie : Buffer handling fixes. 181da177e4SLinus Torvalds * Alexey Kuznetsov : Double buffer free and other fixes. 191da177e4SLinus Torvalds * SVR Anand : Fixed several multicast bugs and problems. 201da177e4SLinus Torvalds * Alexey Kuznetsov : Status, optimisations and more. 211da177e4SLinus Torvalds * Brad Parker : Better behaviour on mrouted upcall 221da177e4SLinus Torvalds * overflow. 231da177e4SLinus Torvalds * Carlos Picoto : PIMv1 Support 241da177e4SLinus Torvalds * Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header 25f77f13e2SGilles Espinasse * Relax this requirement to work with older peers. 261da177e4SLinus Torvalds * 271da177e4SLinus Torvalds */ 281da177e4SLinus Torvalds 297c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 301da177e4SLinus Torvalds #include <linux/types.h> 314fc268d2SRandy Dunlap #include <linux/capability.h> 321da177e4SLinus Torvalds #include <linux/errno.h> 331da177e4SLinus Torvalds #include <linux/timer.h> 341da177e4SLinus Torvalds #include <linux/mm.h> 351da177e4SLinus Torvalds #include <linux/kernel.h> 361da177e4SLinus Torvalds #include <linux/fcntl.h> 371da177e4SLinus Torvalds #include <linux/stat.h> 381da177e4SLinus Torvalds #include <linux/socket.h> 391da177e4SLinus Torvalds #include <linux/in.h> 401da177e4SLinus Torvalds #include <linux/inet.h> 411da177e4SLinus Torvalds #include <linux/netdevice.h> 421da177e4SLinus Torvalds #include <linux/inetdevice.h> 431da177e4SLinus Torvalds #include <linux/igmp.h> 441da177e4SLinus Torvalds #include <linux/proc_fs.h> 451da177e4SLinus Torvalds #include <linux/seq_file.h> 461da177e4SLinus Torvalds #include <linux/mroute.h> 471da177e4SLinus Torvalds #include <linux/init.h> 4846f25dffSKris Katterjohn #include <linux/if_ether.h> 495a0e3ad6STejun Heo #include <linux/slab.h> 50457c4cbcSEric W. Biederman #include <net/net_namespace.h> 511da177e4SLinus Torvalds #include <net/ip.h> 521da177e4SLinus Torvalds #include <net/protocol.h> 531da177e4SLinus Torvalds #include <linux/skbuff.h> 5414c85021SArnaldo Carvalho de Melo #include <net/route.h> 551da177e4SLinus Torvalds #include <net/sock.h> 561da177e4SLinus Torvalds #include <net/icmp.h> 571da177e4SLinus Torvalds #include <net/udp.h> 581da177e4SLinus Torvalds #include <net/raw.h> 591da177e4SLinus Torvalds #include <linux/notifier.h> 601da177e4SLinus Torvalds #include <linux/if_arp.h> 611da177e4SLinus Torvalds #include <linux/netfilter_ipv4.h> 62709b46e8SEric W. Biederman #include <linux/compat.h> 63bc3b2d7fSPaul Gortmaker #include <linux/export.h> 64c5441932SPravin B Shelar #include <net/ip_tunnels.h> 651da177e4SLinus Torvalds #include <net/checksum.h> 66dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 67f0ad0860SPatrick McHardy #include <net/fib_rules.h> 68d67b8c61SNicolas Dichtel #include <linux/netconf.h> 69ccbb0aa6SNikolay Aleksandrov #include <net/nexthop.h> 701da177e4SLinus Torvalds 71f0ad0860SPatrick McHardy struct ipmr_rule { 72f0ad0860SPatrick McHardy struct fib_rule common; 73f0ad0860SPatrick McHardy }; 74f0ad0860SPatrick McHardy 75f0ad0860SPatrick McHardy struct ipmr_result { 76f0ad0860SPatrick McHardy struct mr_table *mrt; 77f0ad0860SPatrick McHardy }; 78f0ad0860SPatrick McHardy 791da177e4SLinus Torvalds /* Big lock, protecting vif table, mrt cache and mroute socket state. 80a8cb16ddSEric Dumazet * Note that the changes are semaphored via rtnl_lock. 811da177e4SLinus Torvalds */ 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds static DEFINE_RWLOCK(mrt_lock); 841da177e4SLinus Torvalds 857ef8f65dSNikolay Aleksandrov /* Multicast router control variables */ 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds /* Special spinlock for queue of unresolved entries */ 881da177e4SLinus Torvalds static DEFINE_SPINLOCK(mfc_unres_lock); 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds /* We return to original Alan's scheme. Hash table of resolved 91a8cb16ddSEric Dumazet * entries is changed only in process context and protected 92a8cb16ddSEric Dumazet * with weak lock mrt_lock. Queue of unresolved entries is protected 93a8cb16ddSEric Dumazet * with strong spinlock mfc_unres_lock. 94a8cb16ddSEric Dumazet * 95a8cb16ddSEric Dumazet * In this case data path is free of exclusive locks at all. 961da177e4SLinus Torvalds */ 971da177e4SLinus Torvalds 98e18b890bSChristoph Lameter static struct kmem_cache *mrt_cachep __read_mostly; 991da177e4SLinus Torvalds 100f0ad0860SPatrick McHardy static struct mr_table *ipmr_new_table(struct net *net, u32 id); 101acbb219dSFrancesco Ruggeri static void ipmr_free_table(struct mr_table *mrt); 102acbb219dSFrancesco Ruggeri 103c4854ec8SRami Rosen static void ip_mr_forward(struct net *net, struct mr_table *mrt, 1044b1f0d33SDonald Sharp struct net_device *dev, struct sk_buff *skb, 1054b1f0d33SDonald Sharp struct mfc_cache *cache, int local); 1060c12295aSPatrick McHardy static int ipmr_cache_report(struct mr_table *mrt, 1074feb88e5SBenjamin Thery struct sk_buff *pkt, vifi_t vifi, int assert); 108cb6a4e46SPatrick McHardy static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 109d658f8a0SPatrick McHardy struct mfc_cache *c, struct rtmsg *rtm); 1108cd3ac9fSNicolas Dichtel static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, 1118cd3ac9fSNicolas Dichtel int cmd); 1125a645dd8SJulien Gomes static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt); 1130e615e96SNikolay Aleksandrov static void mroute_clean_tables(struct mr_table *mrt, bool all); 114f0ad0860SPatrick McHardy static void ipmr_expire_process(unsigned long arg); 1151da177e4SLinus Torvalds 116f0ad0860SPatrick McHardy #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 117f0ad0860SPatrick McHardy #define ipmr_for_each_table(mrt, net) \ 118f0ad0860SPatrick McHardy list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list) 119f0ad0860SPatrick McHardy 120f0ad0860SPatrick McHardy static struct mr_table *ipmr_get_table(struct net *net, u32 id) 121f0ad0860SPatrick McHardy { 122f0ad0860SPatrick McHardy struct mr_table *mrt; 123f0ad0860SPatrick McHardy 124f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 125f0ad0860SPatrick McHardy if (mrt->id == id) 126f0ad0860SPatrick McHardy return mrt; 127f0ad0860SPatrick McHardy } 128f0ad0860SPatrick McHardy return NULL; 129f0ad0860SPatrick McHardy } 130f0ad0860SPatrick McHardy 131da91981bSDavid S. Miller static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 132f0ad0860SPatrick McHardy struct mr_table **mrt) 133f0ad0860SPatrick McHardy { 134f0ad0860SPatrick McHardy int err; 13595f4a45dSHannes Frederic Sowa struct ipmr_result res; 13695f4a45dSHannes Frederic Sowa struct fib_lookup_arg arg = { 13795f4a45dSHannes Frederic Sowa .result = &res, 13895f4a45dSHannes Frederic Sowa .flags = FIB_LOOKUP_NOREF, 13995f4a45dSHannes Frederic Sowa }; 140f0ad0860SPatrick McHardy 141e58e4159SDavid Ahern /* update flow if oif or iif point to device enslaved to l3mdev */ 142e58e4159SDavid Ahern l3mdev_update_flow(net, flowi4_to_flowi(flp4)); 143e58e4159SDavid Ahern 144da91981bSDavid S. Miller err = fib_rules_lookup(net->ipv4.mr_rules_ops, 145da91981bSDavid S. Miller flowi4_to_flowi(flp4), 0, &arg); 146f0ad0860SPatrick McHardy if (err < 0) 147f0ad0860SPatrick McHardy return err; 148f0ad0860SPatrick McHardy *mrt = res.mrt; 149f0ad0860SPatrick McHardy return 0; 150f0ad0860SPatrick McHardy } 151f0ad0860SPatrick McHardy 152f0ad0860SPatrick McHardy static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp, 153f0ad0860SPatrick McHardy int flags, struct fib_lookup_arg *arg) 154f0ad0860SPatrick McHardy { 155f0ad0860SPatrick McHardy struct ipmr_result *res = arg->result; 156f0ad0860SPatrick McHardy struct mr_table *mrt; 157f0ad0860SPatrick McHardy 158f0ad0860SPatrick McHardy switch (rule->action) { 159f0ad0860SPatrick McHardy case FR_ACT_TO_TBL: 160f0ad0860SPatrick McHardy break; 161f0ad0860SPatrick McHardy case FR_ACT_UNREACHABLE: 162f0ad0860SPatrick McHardy return -ENETUNREACH; 163f0ad0860SPatrick McHardy case FR_ACT_PROHIBIT: 164f0ad0860SPatrick McHardy return -EACCES; 165f0ad0860SPatrick McHardy case FR_ACT_BLACKHOLE: 166f0ad0860SPatrick McHardy default: 167f0ad0860SPatrick McHardy return -EINVAL; 168f0ad0860SPatrick McHardy } 169f0ad0860SPatrick McHardy 170e58e4159SDavid Ahern arg->table = fib_rule_get_table(rule, arg); 171e58e4159SDavid Ahern 172e58e4159SDavid Ahern mrt = ipmr_get_table(rule->fr_net, arg->table); 17351456b29SIan Morris if (!mrt) 174f0ad0860SPatrick McHardy return -EAGAIN; 175f0ad0860SPatrick McHardy res->mrt = mrt; 176f0ad0860SPatrick McHardy return 0; 177f0ad0860SPatrick McHardy } 178f0ad0860SPatrick McHardy 179f0ad0860SPatrick McHardy static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) 180f0ad0860SPatrick McHardy { 181f0ad0860SPatrick McHardy return 1; 182f0ad0860SPatrick McHardy } 183f0ad0860SPatrick McHardy 184f0ad0860SPatrick McHardy static const struct nla_policy ipmr_rule_policy[FRA_MAX + 1] = { 185f0ad0860SPatrick McHardy FRA_GENERIC_POLICY, 186f0ad0860SPatrick McHardy }; 187f0ad0860SPatrick McHardy 188f0ad0860SPatrick McHardy static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb, 189f0ad0860SPatrick McHardy struct fib_rule_hdr *frh, struct nlattr **tb) 190f0ad0860SPatrick McHardy { 191f0ad0860SPatrick McHardy return 0; 192f0ad0860SPatrick McHardy } 193f0ad0860SPatrick McHardy 194f0ad0860SPatrick McHardy static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 195f0ad0860SPatrick McHardy struct nlattr **tb) 196f0ad0860SPatrick McHardy { 197f0ad0860SPatrick McHardy return 1; 198f0ad0860SPatrick McHardy } 199f0ad0860SPatrick McHardy 200f0ad0860SPatrick McHardy static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb, 201f0ad0860SPatrick McHardy struct fib_rule_hdr *frh) 202f0ad0860SPatrick McHardy { 203f0ad0860SPatrick McHardy frh->dst_len = 0; 204f0ad0860SPatrick McHardy frh->src_len = 0; 205f0ad0860SPatrick McHardy frh->tos = 0; 206f0ad0860SPatrick McHardy return 0; 207f0ad0860SPatrick McHardy } 208f0ad0860SPatrick McHardy 20904a6f82cSAndi Kleen static const struct fib_rules_ops __net_initconst ipmr_rules_ops_template = { 21025239ceeSPatrick McHardy .family = RTNL_FAMILY_IPMR, 211f0ad0860SPatrick McHardy .rule_size = sizeof(struct ipmr_rule), 212f0ad0860SPatrick McHardy .addr_size = sizeof(u32), 213f0ad0860SPatrick McHardy .action = ipmr_rule_action, 214f0ad0860SPatrick McHardy .match = ipmr_rule_match, 215f0ad0860SPatrick McHardy .configure = ipmr_rule_configure, 216f0ad0860SPatrick McHardy .compare = ipmr_rule_compare, 217f0ad0860SPatrick McHardy .fill = ipmr_rule_fill, 218f0ad0860SPatrick McHardy .nlgroup = RTNLGRP_IPV4_RULE, 219f0ad0860SPatrick McHardy .policy = ipmr_rule_policy, 220f0ad0860SPatrick McHardy .owner = THIS_MODULE, 221f0ad0860SPatrick McHardy }; 222f0ad0860SPatrick McHardy 223f0ad0860SPatrick McHardy static int __net_init ipmr_rules_init(struct net *net) 224f0ad0860SPatrick McHardy { 225f0ad0860SPatrick McHardy struct fib_rules_ops *ops; 226f0ad0860SPatrick McHardy struct mr_table *mrt; 227f0ad0860SPatrick McHardy int err; 228f0ad0860SPatrick McHardy 229f0ad0860SPatrick McHardy ops = fib_rules_register(&ipmr_rules_ops_template, net); 230f0ad0860SPatrick McHardy if (IS_ERR(ops)) 231f0ad0860SPatrick McHardy return PTR_ERR(ops); 232f0ad0860SPatrick McHardy 233f0ad0860SPatrick McHardy INIT_LIST_HEAD(&net->ipv4.mr_tables); 234f0ad0860SPatrick McHardy 235f0ad0860SPatrick McHardy mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 2361113ebbcSNikolay Aleksandrov if (IS_ERR(mrt)) { 2371113ebbcSNikolay Aleksandrov err = PTR_ERR(mrt); 238f0ad0860SPatrick McHardy goto err1; 239f0ad0860SPatrick McHardy } 240f0ad0860SPatrick McHardy 241f0ad0860SPatrick McHardy err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0); 242f0ad0860SPatrick McHardy if (err < 0) 243f0ad0860SPatrick McHardy goto err2; 244f0ad0860SPatrick McHardy 245f0ad0860SPatrick McHardy net->ipv4.mr_rules_ops = ops; 246f0ad0860SPatrick McHardy return 0; 247f0ad0860SPatrick McHardy 248f0ad0860SPatrick McHardy err2: 249f243e5a7SWANG Cong ipmr_free_table(mrt); 250f0ad0860SPatrick McHardy err1: 251f0ad0860SPatrick McHardy fib_rules_unregister(ops); 252f0ad0860SPatrick McHardy return err; 253f0ad0860SPatrick McHardy } 254f0ad0860SPatrick McHardy 255f0ad0860SPatrick McHardy static void __net_exit ipmr_rules_exit(struct net *net) 256f0ad0860SPatrick McHardy { 257f0ad0860SPatrick McHardy struct mr_table *mrt, *next; 258f0ad0860SPatrick McHardy 259ed785309SWANG Cong rtnl_lock(); 260035320d5SEric Dumazet list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) { 261035320d5SEric Dumazet list_del(&mrt->list); 262acbb219dSFrancesco Ruggeri ipmr_free_table(mrt); 263035320d5SEric Dumazet } 264f0ad0860SPatrick McHardy fib_rules_unregister(net->ipv4.mr_rules_ops); 265419df12fSWANG Cong rtnl_unlock(); 266f0ad0860SPatrick McHardy } 267f0ad0860SPatrick McHardy #else 268f0ad0860SPatrick McHardy #define ipmr_for_each_table(mrt, net) \ 269f0ad0860SPatrick McHardy for (mrt = net->ipv4.mrt; mrt; mrt = NULL) 270f0ad0860SPatrick McHardy 271f0ad0860SPatrick McHardy static struct mr_table *ipmr_get_table(struct net *net, u32 id) 272f0ad0860SPatrick McHardy { 273f0ad0860SPatrick McHardy return net->ipv4.mrt; 274f0ad0860SPatrick McHardy } 275f0ad0860SPatrick McHardy 276da91981bSDavid S. Miller static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 277f0ad0860SPatrick McHardy struct mr_table **mrt) 278f0ad0860SPatrick McHardy { 279f0ad0860SPatrick McHardy *mrt = net->ipv4.mrt; 280f0ad0860SPatrick McHardy return 0; 281f0ad0860SPatrick McHardy } 282f0ad0860SPatrick McHardy 283f0ad0860SPatrick McHardy static int __net_init ipmr_rules_init(struct net *net) 284f0ad0860SPatrick McHardy { 2851113ebbcSNikolay Aleksandrov struct mr_table *mrt; 2861113ebbcSNikolay Aleksandrov 2871113ebbcSNikolay Aleksandrov mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 2881113ebbcSNikolay Aleksandrov if (IS_ERR(mrt)) 2891113ebbcSNikolay Aleksandrov return PTR_ERR(mrt); 2901113ebbcSNikolay Aleksandrov net->ipv4.mrt = mrt; 2911113ebbcSNikolay Aleksandrov return 0; 292f0ad0860SPatrick McHardy } 293f0ad0860SPatrick McHardy 294f0ad0860SPatrick McHardy static void __net_exit ipmr_rules_exit(struct net *net) 295f0ad0860SPatrick McHardy { 296ed785309SWANG Cong rtnl_lock(); 297acbb219dSFrancesco Ruggeri ipmr_free_table(net->ipv4.mrt); 298ed785309SWANG Cong net->ipv4.mrt = NULL; 299ed785309SWANG Cong rtnl_unlock(); 300f0ad0860SPatrick McHardy } 301f0ad0860SPatrick McHardy #endif 302f0ad0860SPatrick McHardy 3038fb472c0SNikolay Aleksandrov static inline int ipmr_hash_cmp(struct rhashtable_compare_arg *arg, 3048fb472c0SNikolay Aleksandrov const void *ptr) 3058fb472c0SNikolay Aleksandrov { 3068fb472c0SNikolay Aleksandrov const struct mfc_cache_cmp_arg *cmparg = arg->key; 3078fb472c0SNikolay Aleksandrov struct mfc_cache *c = (struct mfc_cache *)ptr; 3088fb472c0SNikolay Aleksandrov 3098fb472c0SNikolay Aleksandrov return cmparg->mfc_mcastgrp != c->mfc_mcastgrp || 3108fb472c0SNikolay Aleksandrov cmparg->mfc_origin != c->mfc_origin; 3118fb472c0SNikolay Aleksandrov } 3128fb472c0SNikolay Aleksandrov 3138fb472c0SNikolay Aleksandrov static const struct rhashtable_params ipmr_rht_params = { 3148fb472c0SNikolay Aleksandrov .head_offset = offsetof(struct mfc_cache, mnode), 3158fb472c0SNikolay Aleksandrov .key_offset = offsetof(struct mfc_cache, cmparg), 3168fb472c0SNikolay Aleksandrov .key_len = sizeof(struct mfc_cache_cmp_arg), 3178fb472c0SNikolay Aleksandrov .nelem_hint = 3, 3188fb472c0SNikolay Aleksandrov .locks_mul = 1, 3198fb472c0SNikolay Aleksandrov .obj_cmpfn = ipmr_hash_cmp, 3208fb472c0SNikolay Aleksandrov .automatic_shrinking = true, 3218fb472c0SNikolay Aleksandrov }; 3228fb472c0SNikolay Aleksandrov 323f0ad0860SPatrick McHardy static struct mr_table *ipmr_new_table(struct net *net, u32 id) 324f0ad0860SPatrick McHardy { 325f0ad0860SPatrick McHardy struct mr_table *mrt; 326f0ad0860SPatrick McHardy 3271113ebbcSNikolay Aleksandrov /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */ 3281113ebbcSNikolay Aleksandrov if (id != RT_TABLE_DEFAULT && id >= 1000000000) 3291113ebbcSNikolay Aleksandrov return ERR_PTR(-EINVAL); 3301113ebbcSNikolay Aleksandrov 331f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, id); 33200db4124SIan Morris if (mrt) 333f0ad0860SPatrick McHardy return mrt; 334f0ad0860SPatrick McHardy 335f0ad0860SPatrick McHardy mrt = kzalloc(sizeof(*mrt), GFP_KERNEL); 33651456b29SIan Morris if (!mrt) 3371113ebbcSNikolay Aleksandrov return ERR_PTR(-ENOMEM); 3388de53dfbSPatrick McHardy write_pnet(&mrt->net, net); 339f0ad0860SPatrick McHardy mrt->id = id; 340f0ad0860SPatrick McHardy 3418fb472c0SNikolay Aleksandrov rhltable_init(&mrt->mfc_hash, &ipmr_rht_params); 3428fb472c0SNikolay Aleksandrov INIT_LIST_HEAD(&mrt->mfc_cache_list); 343f0ad0860SPatrick McHardy INIT_LIST_HEAD(&mrt->mfc_unres_queue); 344f0ad0860SPatrick McHardy 345f0ad0860SPatrick McHardy setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process, 346f0ad0860SPatrick McHardy (unsigned long)mrt); 347f0ad0860SPatrick McHardy 348f0ad0860SPatrick McHardy mrt->mroute_reg_vif_num = -1; 349f0ad0860SPatrick McHardy #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 350f0ad0860SPatrick McHardy list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables); 351f0ad0860SPatrick McHardy #endif 352f0ad0860SPatrick McHardy return mrt; 353f0ad0860SPatrick McHardy } 3541da177e4SLinus Torvalds 355acbb219dSFrancesco Ruggeri static void ipmr_free_table(struct mr_table *mrt) 356acbb219dSFrancesco Ruggeri { 357acbb219dSFrancesco Ruggeri del_timer_sync(&mrt->ipmr_expire_timer); 3580e615e96SNikolay Aleksandrov mroute_clean_tables(mrt, true); 3598fb472c0SNikolay Aleksandrov rhltable_destroy(&mrt->mfc_hash); 360acbb219dSFrancesco Ruggeri kfree(mrt); 361acbb219dSFrancesco Ruggeri } 362acbb219dSFrancesco Ruggeri 3631da177e4SLinus Torvalds /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */ 3641da177e4SLinus Torvalds 365d607032dSWang Chen static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v) 366d607032dSWang Chen { 3674feb88e5SBenjamin Thery struct net *net = dev_net(dev); 3684feb88e5SBenjamin Thery 369d607032dSWang Chen dev_close(dev); 370d607032dSWang Chen 3714feb88e5SBenjamin Thery dev = __dev_get_by_name(net, "tunl0"); 372d607032dSWang Chen if (dev) { 3735bc3eb7eSStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 374d607032dSWang Chen struct ifreq ifr; 375d607032dSWang Chen struct ip_tunnel_parm p; 376d607032dSWang Chen 377d607032dSWang Chen memset(&p, 0, sizeof(p)); 378d607032dSWang Chen p.iph.daddr = v->vifc_rmt_addr.s_addr; 379d607032dSWang Chen p.iph.saddr = v->vifc_lcl_addr.s_addr; 380d607032dSWang Chen p.iph.version = 4; 381d607032dSWang Chen p.iph.ihl = 5; 382d607032dSWang Chen p.iph.protocol = IPPROTO_IPIP; 383d607032dSWang Chen sprintf(p.name, "dvmrp%d", v->vifc_vifi); 384d607032dSWang Chen ifr.ifr_ifru.ifru_data = (__force void __user *)&p; 385d607032dSWang Chen 3865bc3eb7eSStephen Hemminger if (ops->ndo_do_ioctl) { 3875bc3eb7eSStephen Hemminger mm_segment_t oldfs = get_fs(); 3885bc3eb7eSStephen Hemminger 3895bc3eb7eSStephen Hemminger set_fs(KERNEL_DS); 3905bc3eb7eSStephen Hemminger ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL); 391d607032dSWang Chen set_fs(oldfs); 392d607032dSWang Chen } 393d607032dSWang Chen } 3945bc3eb7eSStephen Hemminger } 395d607032dSWang Chen 396a0b47736SNikolay Aleksandrov /* Initialize ipmr pimreg/tunnel in_device */ 397a0b47736SNikolay Aleksandrov static bool ipmr_init_vif_indev(const struct net_device *dev) 398a0b47736SNikolay Aleksandrov { 399a0b47736SNikolay Aleksandrov struct in_device *in_dev; 400a0b47736SNikolay Aleksandrov 401a0b47736SNikolay Aleksandrov ASSERT_RTNL(); 402a0b47736SNikolay Aleksandrov 403a0b47736SNikolay Aleksandrov in_dev = __in_dev_get_rtnl(dev); 404a0b47736SNikolay Aleksandrov if (!in_dev) 405a0b47736SNikolay Aleksandrov return false; 406a0b47736SNikolay Aleksandrov ipv4_devconf_setall(in_dev); 407a0b47736SNikolay Aleksandrov neigh_parms_data_state_setall(in_dev->arp_parms); 408a0b47736SNikolay Aleksandrov IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; 409a0b47736SNikolay Aleksandrov 410a0b47736SNikolay Aleksandrov return true; 411a0b47736SNikolay Aleksandrov } 412a0b47736SNikolay Aleksandrov 4137ef8f65dSNikolay Aleksandrov static struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v) 4141da177e4SLinus Torvalds { 4151da177e4SLinus Torvalds struct net_device *dev; 4161da177e4SLinus Torvalds 4174feb88e5SBenjamin Thery dev = __dev_get_by_name(net, "tunl0"); 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds if (dev) { 4205bc3eb7eSStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 4211da177e4SLinus Torvalds int err; 4221da177e4SLinus Torvalds struct ifreq ifr; 4231da177e4SLinus Torvalds struct ip_tunnel_parm p; 4241da177e4SLinus Torvalds 4251da177e4SLinus Torvalds memset(&p, 0, sizeof(p)); 4261da177e4SLinus Torvalds p.iph.daddr = v->vifc_rmt_addr.s_addr; 4271da177e4SLinus Torvalds p.iph.saddr = v->vifc_lcl_addr.s_addr; 4281da177e4SLinus Torvalds p.iph.version = 4; 4291da177e4SLinus Torvalds p.iph.ihl = 5; 4301da177e4SLinus Torvalds p.iph.protocol = IPPROTO_IPIP; 4311da177e4SLinus Torvalds sprintf(p.name, "dvmrp%d", v->vifc_vifi); 432ba93ef74SStephen Hemminger ifr.ifr_ifru.ifru_data = (__force void __user *)&p; 4331da177e4SLinus Torvalds 4345bc3eb7eSStephen Hemminger if (ops->ndo_do_ioctl) { 4355bc3eb7eSStephen Hemminger mm_segment_t oldfs = get_fs(); 4365bc3eb7eSStephen Hemminger 4375bc3eb7eSStephen Hemminger set_fs(KERNEL_DS); 4385bc3eb7eSStephen Hemminger err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL); 4391da177e4SLinus Torvalds set_fs(oldfs); 440a8cb16ddSEric Dumazet } else { 4415bc3eb7eSStephen Hemminger err = -EOPNOTSUPP; 442a8cb16ddSEric Dumazet } 4431da177e4SLinus Torvalds dev = NULL; 4441da177e4SLinus Torvalds 4454feb88e5SBenjamin Thery if (err == 0 && 4464feb88e5SBenjamin Thery (dev = __dev_get_by_name(net, p.name)) != NULL) { 4471da177e4SLinus Torvalds dev->flags |= IFF_MULTICAST; 448a0b47736SNikolay Aleksandrov if (!ipmr_init_vif_indev(dev)) 4491da177e4SLinus Torvalds goto failure; 4501da177e4SLinus Torvalds if (dev_open(dev)) 4511da177e4SLinus Torvalds goto failure; 4527dc00c82SWang Chen dev_hold(dev); 4531da177e4SLinus Torvalds } 4541da177e4SLinus Torvalds } 4551da177e4SLinus Torvalds return dev; 4561da177e4SLinus Torvalds 4571da177e4SLinus Torvalds failure: 4581da177e4SLinus Torvalds unregister_netdevice(dev); 4591da177e4SLinus Torvalds return NULL; 4601da177e4SLinus Torvalds } 4611da177e4SLinus Torvalds 462c316c629SNikolay Aleksandrov #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) 4636fef4c0cSStephen Hemminger static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) 4641da177e4SLinus Torvalds { 4654feb88e5SBenjamin Thery struct net *net = dev_net(dev); 466f0ad0860SPatrick McHardy struct mr_table *mrt; 467da91981bSDavid S. Miller struct flowi4 fl4 = { 468da91981bSDavid S. Miller .flowi4_oif = dev->ifindex, 4696a662719SCong Wang .flowi4_iif = skb->skb_iif ? : LOOPBACK_IFINDEX, 470da91981bSDavid S. Miller .flowi4_mark = skb->mark, 471f0ad0860SPatrick McHardy }; 472f0ad0860SPatrick McHardy int err; 473f0ad0860SPatrick McHardy 474da91981bSDavid S. Miller err = ipmr_fib_lookup(net, &fl4, &mrt); 475e40dbc51SBen Greear if (err < 0) { 476e40dbc51SBen Greear kfree_skb(skb); 477f0ad0860SPatrick McHardy return err; 478e40dbc51SBen Greear } 4794feb88e5SBenjamin Thery 4801da177e4SLinus Torvalds read_lock(&mrt_lock); 481cf3677aeSPavel Emelyanov dev->stats.tx_bytes += skb->len; 482cf3677aeSPavel Emelyanov dev->stats.tx_packets++; 4830c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, IGMPMSG_WHOLEPKT); 4841da177e4SLinus Torvalds read_unlock(&mrt_lock); 4851da177e4SLinus Torvalds kfree_skb(skb); 4866ed10654SPatrick McHardy return NETDEV_TX_OK; 4871da177e4SLinus Torvalds } 4881da177e4SLinus Torvalds 489ee9b9596SNicolas Dichtel static int reg_vif_get_iflink(const struct net_device *dev) 490ee9b9596SNicolas Dichtel { 491ee9b9596SNicolas Dichtel return 0; 492ee9b9596SNicolas Dichtel } 493ee9b9596SNicolas Dichtel 494007c3838SStephen Hemminger static const struct net_device_ops reg_vif_netdev_ops = { 495007c3838SStephen Hemminger .ndo_start_xmit = reg_vif_xmit, 496ee9b9596SNicolas Dichtel .ndo_get_iflink = reg_vif_get_iflink, 497007c3838SStephen Hemminger }; 498007c3838SStephen Hemminger 4991da177e4SLinus Torvalds static void reg_vif_setup(struct net_device *dev) 5001da177e4SLinus Torvalds { 5011da177e4SLinus Torvalds dev->type = ARPHRD_PIMREG; 50246f25dffSKris Katterjohn dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8; 5031da177e4SLinus Torvalds dev->flags = IFF_NOARP; 50470cb4a45SHimangi Saraogi dev->netdev_ops = ®_vif_netdev_ops; 505cf124db5SDavid S. Miller dev->needs_free_netdev = true; 506403dbb97STom Goff dev->features |= NETIF_F_NETNS_LOCAL; 5071da177e4SLinus Torvalds } 5081da177e4SLinus Torvalds 509f0ad0860SPatrick McHardy static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) 5101da177e4SLinus Torvalds { 5111da177e4SLinus Torvalds struct net_device *dev; 512f0ad0860SPatrick McHardy char name[IFNAMSIZ]; 5131da177e4SLinus Torvalds 514f0ad0860SPatrick McHardy if (mrt->id == RT_TABLE_DEFAULT) 515f0ad0860SPatrick McHardy sprintf(name, "pimreg"); 516f0ad0860SPatrick McHardy else 517f0ad0860SPatrick McHardy sprintf(name, "pimreg%u", mrt->id); 518f0ad0860SPatrick McHardy 519c835a677STom Gundersen dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup); 5201da177e4SLinus Torvalds 52151456b29SIan Morris if (!dev) 5221da177e4SLinus Torvalds return NULL; 5231da177e4SLinus Torvalds 524403dbb97STom Goff dev_net_set(dev, net); 525403dbb97STom Goff 5261da177e4SLinus Torvalds if (register_netdevice(dev)) { 5271da177e4SLinus Torvalds free_netdev(dev); 5281da177e4SLinus Torvalds return NULL; 5291da177e4SLinus Torvalds } 5301da177e4SLinus Torvalds 531a0b47736SNikolay Aleksandrov if (!ipmr_init_vif_indev(dev)) 5321da177e4SLinus Torvalds goto failure; 5331da177e4SLinus Torvalds if (dev_open(dev)) 5341da177e4SLinus Torvalds goto failure; 5351da177e4SLinus Torvalds 5367dc00c82SWang Chen dev_hold(dev); 5377dc00c82SWang Chen 5381da177e4SLinus Torvalds return dev; 5391da177e4SLinus Torvalds 5401da177e4SLinus Torvalds failure: 5411da177e4SLinus Torvalds unregister_netdevice(dev); 5421da177e4SLinus Torvalds return NULL; 5431da177e4SLinus Torvalds } 544c316c629SNikolay Aleksandrov 545c316c629SNikolay Aleksandrov /* called with rcu_read_lock() */ 546c316c629SNikolay Aleksandrov static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb, 547c316c629SNikolay Aleksandrov unsigned int pimlen) 548c316c629SNikolay Aleksandrov { 549c316c629SNikolay Aleksandrov struct net_device *reg_dev = NULL; 550c316c629SNikolay Aleksandrov struct iphdr *encap; 551c316c629SNikolay Aleksandrov 552c316c629SNikolay Aleksandrov encap = (struct iphdr *)(skb_transport_header(skb) + pimlen); 5537ef8f65dSNikolay Aleksandrov /* Check that: 554c316c629SNikolay Aleksandrov * a. packet is really sent to a multicast group 555c316c629SNikolay Aleksandrov * b. packet is not a NULL-REGISTER 556c316c629SNikolay Aleksandrov * c. packet is not truncated 557c316c629SNikolay Aleksandrov */ 558c316c629SNikolay Aleksandrov if (!ipv4_is_multicast(encap->daddr) || 559c316c629SNikolay Aleksandrov encap->tot_len == 0 || 560c316c629SNikolay Aleksandrov ntohs(encap->tot_len) + pimlen > skb->len) 561c316c629SNikolay Aleksandrov return 1; 562c316c629SNikolay Aleksandrov 563c316c629SNikolay Aleksandrov read_lock(&mrt_lock); 564c316c629SNikolay Aleksandrov if (mrt->mroute_reg_vif_num >= 0) 565c316c629SNikolay Aleksandrov reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev; 566c316c629SNikolay Aleksandrov read_unlock(&mrt_lock); 567c316c629SNikolay Aleksandrov 568c316c629SNikolay Aleksandrov if (!reg_dev) 569c316c629SNikolay Aleksandrov return 1; 570c316c629SNikolay Aleksandrov 571c316c629SNikolay Aleksandrov skb->mac_header = skb->network_header; 572c316c629SNikolay Aleksandrov skb_pull(skb, (u8 *)encap - skb->data); 573c316c629SNikolay Aleksandrov skb_reset_network_header(skb); 574c316c629SNikolay Aleksandrov skb->protocol = htons(ETH_P_IP); 575c316c629SNikolay Aleksandrov skb->ip_summed = CHECKSUM_NONE; 576c316c629SNikolay Aleksandrov 577c316c629SNikolay Aleksandrov skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev)); 578c316c629SNikolay Aleksandrov 579c316c629SNikolay Aleksandrov netif_rx(skb); 580c316c629SNikolay Aleksandrov 581c316c629SNikolay Aleksandrov return NET_RX_SUCCESS; 582c316c629SNikolay Aleksandrov } 583c316c629SNikolay Aleksandrov #else 584c316c629SNikolay Aleksandrov static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) 585c316c629SNikolay Aleksandrov { 586c316c629SNikolay Aleksandrov return NULL; 587c316c629SNikolay Aleksandrov } 5881da177e4SLinus Torvalds #endif 5891da177e4SLinus Torvalds 5902c53040fSBen Hutchings /** 5912c53040fSBen Hutchings * vif_delete - Delete a VIF entry 5927dc00c82SWang Chen * @notify: Set to 1, if the caller is a notifier_call 5931da177e4SLinus Torvalds */ 5940c12295aSPatrick McHardy static int vif_delete(struct mr_table *mrt, int vifi, int notify, 595d17fa6faSEric Dumazet struct list_head *head) 5961da177e4SLinus Torvalds { 5971da177e4SLinus Torvalds struct vif_device *v; 5981da177e4SLinus Torvalds struct net_device *dev; 5991da177e4SLinus Torvalds struct in_device *in_dev; 6001da177e4SLinus Torvalds 6010c12295aSPatrick McHardy if (vifi < 0 || vifi >= mrt->maxvif) 6021da177e4SLinus Torvalds return -EADDRNOTAVAIL; 6031da177e4SLinus Torvalds 6040c12295aSPatrick McHardy v = &mrt->vif_table[vifi]; 6051da177e4SLinus Torvalds 6061da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 6071da177e4SLinus Torvalds dev = v->dev; 6081da177e4SLinus Torvalds v->dev = NULL; 6091da177e4SLinus Torvalds 6101da177e4SLinus Torvalds if (!dev) { 6111da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 6121da177e4SLinus Torvalds return -EADDRNOTAVAIL; 6131da177e4SLinus Torvalds } 6141da177e4SLinus Torvalds 6150c12295aSPatrick McHardy if (vifi == mrt->mroute_reg_vif_num) 6160c12295aSPatrick McHardy mrt->mroute_reg_vif_num = -1; 6171da177e4SLinus Torvalds 6180c12295aSPatrick McHardy if (vifi + 1 == mrt->maxvif) { 6191da177e4SLinus Torvalds int tmp; 620a8cb16ddSEric Dumazet 6211da177e4SLinus Torvalds for (tmp = vifi - 1; tmp >= 0; tmp--) { 6220c12295aSPatrick McHardy if (VIF_EXISTS(mrt, tmp)) 6231da177e4SLinus Torvalds break; 6241da177e4SLinus Torvalds } 6250c12295aSPatrick McHardy mrt->maxvif = tmp+1; 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds 6281da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 6291da177e4SLinus Torvalds 6301da177e4SLinus Torvalds dev_set_allmulti(dev, -1); 6311da177e4SLinus Torvalds 632a8cb16ddSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 633a8cb16ddSEric Dumazet if (in_dev) { 63442f811b8SHerbert Xu IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--; 6353b022865SDavid Ahern inet_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, 636d67b8c61SNicolas Dichtel NETCONFA_MC_FORWARDING, 637d67b8c61SNicolas Dichtel dev->ifindex, &in_dev->cnf); 6381da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 6391da177e4SLinus Torvalds } 6401da177e4SLinus Torvalds 6417dc00c82SWang Chen if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER) && !notify) 642d17fa6faSEric Dumazet unregister_netdevice_queue(dev, head); 6431da177e4SLinus Torvalds 6441da177e4SLinus Torvalds dev_put(dev); 6451da177e4SLinus Torvalds return 0; 6461da177e4SLinus Torvalds } 6471da177e4SLinus Torvalds 648a8c9486bSEric Dumazet static void ipmr_cache_free_rcu(struct rcu_head *head) 649a8c9486bSEric Dumazet { 650a8c9486bSEric Dumazet struct mfc_cache *c = container_of(head, struct mfc_cache, rcu); 651a8c9486bSEric Dumazet 652a8c9486bSEric Dumazet kmem_cache_free(mrt_cachep, c); 653a8c9486bSEric Dumazet } 654a8c9486bSEric Dumazet 6555c0a66f5SBenjamin Thery static inline void ipmr_cache_free(struct mfc_cache *c) 6565c0a66f5SBenjamin Thery { 657a8c9486bSEric Dumazet call_rcu(&c->rcu, ipmr_cache_free_rcu); 6585c0a66f5SBenjamin Thery } 6595c0a66f5SBenjamin Thery 6601da177e4SLinus Torvalds /* Destroy an unresolved cache entry, killing queued skbs 661a8cb16ddSEric Dumazet * and reporting error to netlink readers. 6621da177e4SLinus Torvalds */ 6630c12295aSPatrick McHardy static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c) 6641da177e4SLinus Torvalds { 6658de53dfbSPatrick McHardy struct net *net = read_pnet(&mrt->net); 6661da177e4SLinus Torvalds struct sk_buff *skb; 6679ef1d4c7SPatrick McHardy struct nlmsgerr *e; 6681da177e4SLinus Torvalds 6690c12295aSPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len); 6701da177e4SLinus Torvalds 6711da177e4SLinus Torvalds while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) { 672eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->version == 0) { 673af72868bSJohannes Berg struct nlmsghdr *nlh = skb_pull(skb, 674af72868bSJohannes Berg sizeof(struct iphdr)); 6751da177e4SLinus Torvalds nlh->nlmsg_type = NLMSG_ERROR; 676573ce260SHong zhi guo nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 6771da177e4SLinus Torvalds skb_trim(skb, nlh->nlmsg_len); 678573ce260SHong zhi guo e = nlmsg_data(nlh); 6799ef1d4c7SPatrick McHardy e->error = -ETIMEDOUT; 6809ef1d4c7SPatrick McHardy memset(&e->msg, 0, sizeof(e->msg)); 6812942e900SThomas Graf 68215e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 683a8cb16ddSEric Dumazet } else { 6841da177e4SLinus Torvalds kfree_skb(skb); 6851da177e4SLinus Torvalds } 686a8cb16ddSEric Dumazet } 6871da177e4SLinus Torvalds 6885c0a66f5SBenjamin Thery ipmr_cache_free(c); 6891da177e4SLinus Torvalds } 6901da177e4SLinus Torvalds 691e258beb2SPatrick McHardy /* Timer process for the unresolved queue. */ 692e258beb2SPatrick McHardy static void ipmr_expire_process(unsigned long arg) 6931da177e4SLinus Torvalds { 6940c12295aSPatrick McHardy struct mr_table *mrt = (struct mr_table *)arg; 6951da177e4SLinus Torvalds unsigned long now; 6961da177e4SLinus Torvalds unsigned long expires; 697862465f2SPatrick McHardy struct mfc_cache *c, *next; 6981da177e4SLinus Torvalds 6991da177e4SLinus Torvalds if (!spin_trylock(&mfc_unres_lock)) { 7000c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10); 7011da177e4SLinus Torvalds return; 7021da177e4SLinus Torvalds } 7031da177e4SLinus Torvalds 7040c12295aSPatrick McHardy if (list_empty(&mrt->mfc_unres_queue)) 7051da177e4SLinus Torvalds goto out; 7061da177e4SLinus Torvalds 7071da177e4SLinus Torvalds now = jiffies; 7081da177e4SLinus Torvalds expires = 10*HZ; 7091da177e4SLinus Torvalds 7100c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) { 7111da177e4SLinus Torvalds if (time_after(c->mfc_un.unres.expires, now)) { 7121da177e4SLinus Torvalds unsigned long interval = c->mfc_un.unres.expires - now; 7131da177e4SLinus Torvalds if (interval < expires) 7141da177e4SLinus Torvalds expires = interval; 7151da177e4SLinus Torvalds continue; 7161da177e4SLinus Torvalds } 7171da177e4SLinus Torvalds 718862465f2SPatrick McHardy list_del(&c->list); 7198cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 7200c12295aSPatrick McHardy ipmr_destroy_unres(mrt, c); 7211da177e4SLinus Torvalds } 7221da177e4SLinus Torvalds 7230c12295aSPatrick McHardy if (!list_empty(&mrt->mfc_unres_queue)) 7240c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies + expires); 7251da177e4SLinus Torvalds 7261da177e4SLinus Torvalds out: 7271da177e4SLinus Torvalds spin_unlock(&mfc_unres_lock); 7281da177e4SLinus Torvalds } 7291da177e4SLinus Torvalds 7301da177e4SLinus Torvalds /* Fill oifs list. It is called under write locked mrt_lock. */ 7310c12295aSPatrick McHardy static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache, 732d658f8a0SPatrick McHardy unsigned char *ttls) 7331da177e4SLinus Torvalds { 7341da177e4SLinus Torvalds int vifi; 7351da177e4SLinus Torvalds 7361da177e4SLinus Torvalds cache->mfc_un.res.minvif = MAXVIFS; 7371da177e4SLinus Torvalds cache->mfc_un.res.maxvif = 0; 7381da177e4SLinus Torvalds memset(cache->mfc_un.res.ttls, 255, MAXVIFS); 7391da177e4SLinus Torvalds 7400c12295aSPatrick McHardy for (vifi = 0; vifi < mrt->maxvif; vifi++) { 7410c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vifi) && 742cf958ae3SBenjamin Thery ttls[vifi] && ttls[vifi] < 255) { 7431da177e4SLinus Torvalds cache->mfc_un.res.ttls[vifi] = ttls[vifi]; 7441da177e4SLinus Torvalds if (cache->mfc_un.res.minvif > vifi) 7451da177e4SLinus Torvalds cache->mfc_un.res.minvif = vifi; 7461da177e4SLinus Torvalds if (cache->mfc_un.res.maxvif <= vifi) 7471da177e4SLinus Torvalds cache->mfc_un.res.maxvif = vifi + 1; 7481da177e4SLinus Torvalds } 7491da177e4SLinus Torvalds } 75090b5ca17SNikolay Aleksandrov cache->mfc_un.res.lastuse = jiffies; 7511da177e4SLinus Torvalds } 7521da177e4SLinus Torvalds 7530c12295aSPatrick McHardy static int vif_add(struct net *net, struct mr_table *mrt, 7540c12295aSPatrick McHardy struct vifctl *vifc, int mrtsock) 7551da177e4SLinus Torvalds { 7561da177e4SLinus Torvalds int vifi = vifc->vifc_vifi; 7570c12295aSPatrick McHardy struct vif_device *v = &mrt->vif_table[vifi]; 7581da177e4SLinus Torvalds struct net_device *dev; 7591da177e4SLinus Torvalds struct in_device *in_dev; 760d607032dSWang Chen int err; 7611da177e4SLinus Torvalds 7621da177e4SLinus Torvalds /* Is vif busy ? */ 7630c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vifi)) 7641da177e4SLinus Torvalds return -EADDRINUSE; 7651da177e4SLinus Torvalds 7661da177e4SLinus Torvalds switch (vifc->vifc_flags) { 7671da177e4SLinus Torvalds case VIFF_REGISTER: 7681973a4eaSNikolay Aleksandrov if (!ipmr_pimsm_enabled()) 769c316c629SNikolay Aleksandrov return -EINVAL; 770c316c629SNikolay Aleksandrov /* Special Purpose VIF in PIM 7711da177e4SLinus Torvalds * All the packets will be sent to the daemon 7721da177e4SLinus Torvalds */ 7730c12295aSPatrick McHardy if (mrt->mroute_reg_vif_num >= 0) 7741da177e4SLinus Torvalds return -EADDRINUSE; 775f0ad0860SPatrick McHardy dev = ipmr_reg_vif(net, mrt); 7761da177e4SLinus Torvalds if (!dev) 7771da177e4SLinus Torvalds return -ENOBUFS; 778d607032dSWang Chen err = dev_set_allmulti(dev, 1); 779d607032dSWang Chen if (err) { 780d607032dSWang Chen unregister_netdevice(dev); 7817dc00c82SWang Chen dev_put(dev); 782d607032dSWang Chen return err; 783d607032dSWang Chen } 7841da177e4SLinus Torvalds break; 7851da177e4SLinus Torvalds case VIFF_TUNNEL: 7864feb88e5SBenjamin Thery dev = ipmr_new_tunnel(net, vifc); 7871da177e4SLinus Torvalds if (!dev) 7881da177e4SLinus Torvalds return -ENOBUFS; 789d607032dSWang Chen err = dev_set_allmulti(dev, 1); 790d607032dSWang Chen if (err) { 791d607032dSWang Chen ipmr_del_tunnel(dev, vifc); 7927dc00c82SWang Chen dev_put(dev); 793d607032dSWang Chen return err; 794d607032dSWang Chen } 7951da177e4SLinus Torvalds break; 796ee5e81f0SIlia K case VIFF_USE_IFINDEX: 7971da177e4SLinus Torvalds case 0: 798ee5e81f0SIlia K if (vifc->vifc_flags == VIFF_USE_IFINDEX) { 799ee5e81f0SIlia K dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex); 80051456b29SIan Morris if (dev && !__in_dev_get_rtnl(dev)) { 801ee5e81f0SIlia K dev_put(dev); 802ee5e81f0SIlia K return -EADDRNOTAVAIL; 803ee5e81f0SIlia K } 804a8cb16ddSEric Dumazet } else { 8054feb88e5SBenjamin Thery dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr); 806a8cb16ddSEric Dumazet } 8071da177e4SLinus Torvalds if (!dev) 8081da177e4SLinus Torvalds return -EADDRNOTAVAIL; 809d607032dSWang Chen err = dev_set_allmulti(dev, 1); 8107dc00c82SWang Chen if (err) { 8117dc00c82SWang Chen dev_put(dev); 812d607032dSWang Chen return err; 8137dc00c82SWang Chen } 8141da177e4SLinus Torvalds break; 8151da177e4SLinus Torvalds default: 8161da177e4SLinus Torvalds return -EINVAL; 8171da177e4SLinus Torvalds } 8181da177e4SLinus Torvalds 819a8cb16ddSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 820a8cb16ddSEric Dumazet if (!in_dev) { 821d0490cfdSDan Carpenter dev_put(dev); 8221da177e4SLinus Torvalds return -EADDRNOTAVAIL; 823d0490cfdSDan Carpenter } 82442f811b8SHerbert Xu IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++; 8253b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_MC_FORWARDING, 8263b022865SDavid Ahern dev->ifindex, &in_dev->cnf); 8271da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 8281da177e4SLinus Torvalds 829a8cb16ddSEric Dumazet /* Fill in the VIF structures */ 830a8cb16ddSEric Dumazet 8311da177e4SLinus Torvalds v->rate_limit = vifc->vifc_rate_limit; 8321da177e4SLinus Torvalds v->local = vifc->vifc_lcl_addr.s_addr; 8331da177e4SLinus Torvalds v->remote = vifc->vifc_rmt_addr.s_addr; 8341da177e4SLinus Torvalds v->flags = vifc->vifc_flags; 8351da177e4SLinus Torvalds if (!mrtsock) 8361da177e4SLinus Torvalds v->flags |= VIFF_STATIC; 8371da177e4SLinus Torvalds v->threshold = vifc->vifc_threshold; 8381da177e4SLinus Torvalds v->bytes_in = 0; 8391da177e4SLinus Torvalds v->bytes_out = 0; 8401da177e4SLinus Torvalds v->pkt_in = 0; 8411da177e4SLinus Torvalds v->pkt_out = 0; 8421da177e4SLinus Torvalds v->link = dev->ifindex; 8431da177e4SLinus Torvalds if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER)) 844a54acb3aSNicolas Dichtel v->link = dev_get_iflink(dev); 8451da177e4SLinus Torvalds 8461da177e4SLinus Torvalds /* And finish update writing critical data */ 8471da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 8481da177e4SLinus Torvalds v->dev = dev; 8491da177e4SLinus Torvalds if (v->flags & VIFF_REGISTER) 8500c12295aSPatrick McHardy mrt->mroute_reg_vif_num = vifi; 8510c12295aSPatrick McHardy if (vifi+1 > mrt->maxvif) 8520c12295aSPatrick McHardy mrt->maxvif = vifi+1; 8531da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 8541da177e4SLinus Torvalds return 0; 8551da177e4SLinus Torvalds } 8561da177e4SLinus Torvalds 857a8c9486bSEric Dumazet /* called with rcu_read_lock() */ 8580c12295aSPatrick McHardy static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, 8594feb88e5SBenjamin Thery __be32 origin, 8604feb88e5SBenjamin Thery __be32 mcastgrp) 8611da177e4SLinus Torvalds { 8628fb472c0SNikolay Aleksandrov struct mfc_cache_cmp_arg arg = { 8638fb472c0SNikolay Aleksandrov .mfc_mcastgrp = mcastgrp, 8648fb472c0SNikolay Aleksandrov .mfc_origin = origin 8658fb472c0SNikolay Aleksandrov }; 8668fb472c0SNikolay Aleksandrov struct rhlist_head *tmp, *list; 8671da177e4SLinus Torvalds struct mfc_cache *c; 8681da177e4SLinus Torvalds 8698fb472c0SNikolay Aleksandrov list = rhltable_lookup(&mrt->mfc_hash, &arg, ipmr_rht_params); 8708fb472c0SNikolay Aleksandrov rhl_for_each_entry_rcu(c, tmp, list, mnode) 8711da177e4SLinus Torvalds return c; 8728fb472c0SNikolay Aleksandrov 873862465f2SPatrick McHardy return NULL; 874862465f2SPatrick McHardy } 8751da177e4SLinus Torvalds 876660b26dcSNicolas Dichtel /* Look for a (*,*,oif) entry */ 877660b26dcSNicolas Dichtel static struct mfc_cache *ipmr_cache_find_any_parent(struct mr_table *mrt, 878660b26dcSNicolas Dichtel int vifi) 879660b26dcSNicolas Dichtel { 8808fb472c0SNikolay Aleksandrov struct mfc_cache_cmp_arg arg = { 8818fb472c0SNikolay Aleksandrov .mfc_mcastgrp = htonl(INADDR_ANY), 8828fb472c0SNikolay Aleksandrov .mfc_origin = htonl(INADDR_ANY) 8838fb472c0SNikolay Aleksandrov }; 8848fb472c0SNikolay Aleksandrov struct rhlist_head *tmp, *list; 885660b26dcSNicolas Dichtel struct mfc_cache *c; 886660b26dcSNicolas Dichtel 8878fb472c0SNikolay Aleksandrov list = rhltable_lookup(&mrt->mfc_hash, &arg, ipmr_rht_params); 8888fb472c0SNikolay Aleksandrov rhl_for_each_entry_rcu(c, tmp, list, mnode) 8898fb472c0SNikolay Aleksandrov if (c->mfc_un.res.ttls[vifi] < 255) 890660b26dcSNicolas Dichtel return c; 891660b26dcSNicolas Dichtel 892660b26dcSNicolas Dichtel return NULL; 893660b26dcSNicolas Dichtel } 894660b26dcSNicolas Dichtel 895660b26dcSNicolas Dichtel /* Look for a (*,G) entry */ 896660b26dcSNicolas Dichtel static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt, 897660b26dcSNicolas Dichtel __be32 mcastgrp, int vifi) 898660b26dcSNicolas Dichtel { 8998fb472c0SNikolay Aleksandrov struct mfc_cache_cmp_arg arg = { 9008fb472c0SNikolay Aleksandrov .mfc_mcastgrp = mcastgrp, 9018fb472c0SNikolay Aleksandrov .mfc_origin = htonl(INADDR_ANY) 9028fb472c0SNikolay Aleksandrov }; 9038fb472c0SNikolay Aleksandrov struct rhlist_head *tmp, *list; 904660b26dcSNicolas Dichtel struct mfc_cache *c, *proxy; 905660b26dcSNicolas Dichtel 906360eb5daSNicolas Dichtel if (mcastgrp == htonl(INADDR_ANY)) 907660b26dcSNicolas Dichtel goto skip; 908660b26dcSNicolas Dichtel 9098fb472c0SNikolay Aleksandrov list = rhltable_lookup(&mrt->mfc_hash, &arg, ipmr_rht_params); 9108fb472c0SNikolay Aleksandrov rhl_for_each_entry_rcu(c, tmp, list, mnode) { 911660b26dcSNicolas Dichtel if (c->mfc_un.res.ttls[vifi] < 255) 912660b26dcSNicolas Dichtel return c; 913660b26dcSNicolas Dichtel 914660b26dcSNicolas Dichtel /* It's ok if the vifi is part of the static tree */ 9158fb472c0SNikolay Aleksandrov proxy = ipmr_cache_find_any_parent(mrt, c->mfc_parent); 916660b26dcSNicolas Dichtel if (proxy && proxy->mfc_un.res.ttls[vifi] < 255) 917660b26dcSNicolas Dichtel return c; 918660b26dcSNicolas Dichtel } 919660b26dcSNicolas Dichtel 920660b26dcSNicolas Dichtel skip: 921660b26dcSNicolas Dichtel return ipmr_cache_find_any_parent(mrt, vifi); 922660b26dcSNicolas Dichtel } 923660b26dcSNicolas Dichtel 9248fb472c0SNikolay Aleksandrov /* Look for a (S,G,iif) entry if parent != -1 */ 9258fb472c0SNikolay Aleksandrov static struct mfc_cache *ipmr_cache_find_parent(struct mr_table *mrt, 9268fb472c0SNikolay Aleksandrov __be32 origin, __be32 mcastgrp, 9278fb472c0SNikolay Aleksandrov int parent) 9288fb472c0SNikolay Aleksandrov { 9298fb472c0SNikolay Aleksandrov struct mfc_cache_cmp_arg arg = { 9308fb472c0SNikolay Aleksandrov .mfc_mcastgrp = mcastgrp, 9318fb472c0SNikolay Aleksandrov .mfc_origin = origin, 9328fb472c0SNikolay Aleksandrov }; 9338fb472c0SNikolay Aleksandrov struct rhlist_head *tmp, *list; 9348fb472c0SNikolay Aleksandrov struct mfc_cache *c; 9358fb472c0SNikolay Aleksandrov 9368fb472c0SNikolay Aleksandrov list = rhltable_lookup(&mrt->mfc_hash, &arg, ipmr_rht_params); 9378fb472c0SNikolay Aleksandrov rhl_for_each_entry_rcu(c, tmp, list, mnode) 9388fb472c0SNikolay Aleksandrov if (parent == -1 || parent == c->mfc_parent) 9398fb472c0SNikolay Aleksandrov return c; 9408fb472c0SNikolay Aleksandrov 9418fb472c0SNikolay Aleksandrov return NULL; 9428fb472c0SNikolay Aleksandrov } 9438fb472c0SNikolay Aleksandrov 9447ef8f65dSNikolay Aleksandrov /* Allocate a multicast cache entry */ 945d658f8a0SPatrick McHardy static struct mfc_cache *ipmr_cache_alloc(void) 9461da177e4SLinus Torvalds { 947c3762229SRobert P. J. Day struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL); 948a8c9486bSEric Dumazet 94970a0dec4STom Goff if (c) { 95070a0dec4STom Goff c->mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1; 9511da177e4SLinus Torvalds c->mfc_un.res.minvif = MAXVIFS; 95270a0dec4STom Goff } 9531da177e4SLinus Torvalds return c; 9541da177e4SLinus Torvalds } 9551da177e4SLinus Torvalds 956d658f8a0SPatrick McHardy static struct mfc_cache *ipmr_cache_alloc_unres(void) 9571da177e4SLinus Torvalds { 958c3762229SRobert P. J. Day struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC); 959a8c9486bSEric Dumazet 960a8c9486bSEric Dumazet if (c) { 9611da177e4SLinus Torvalds skb_queue_head_init(&c->mfc_un.unres.unresolved); 9621da177e4SLinus Torvalds c->mfc_un.unres.expires = jiffies + 10*HZ; 963a8c9486bSEric Dumazet } 9641da177e4SLinus Torvalds return c; 9651da177e4SLinus Torvalds } 9661da177e4SLinus Torvalds 9677ef8f65dSNikolay Aleksandrov /* A cache entry has gone into a resolved state from queued */ 9680c12295aSPatrick McHardy static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt, 9690c12295aSPatrick McHardy struct mfc_cache *uc, struct mfc_cache *c) 9701da177e4SLinus Torvalds { 9711da177e4SLinus Torvalds struct sk_buff *skb; 9729ef1d4c7SPatrick McHardy struct nlmsgerr *e; 9731da177e4SLinus Torvalds 974a8cb16ddSEric Dumazet /* Play the pending entries through our router */ 9751da177e4SLinus Torvalds while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) { 976eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->version == 0) { 977af72868bSJohannes Berg struct nlmsghdr *nlh = skb_pull(skb, 978af72868bSJohannes Berg sizeof(struct iphdr)); 9791da177e4SLinus Torvalds 980573ce260SHong zhi guo if (__ipmr_fill_mroute(mrt, skb, c, nlmsg_data(nlh)) > 0) { 981a8cb16ddSEric Dumazet nlh->nlmsg_len = skb_tail_pointer(skb) - 982a8cb16ddSEric Dumazet (u8 *)nlh; 9831da177e4SLinus Torvalds } else { 9841da177e4SLinus Torvalds nlh->nlmsg_type = NLMSG_ERROR; 985573ce260SHong zhi guo nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 9861da177e4SLinus Torvalds skb_trim(skb, nlh->nlmsg_len); 987573ce260SHong zhi guo e = nlmsg_data(nlh); 9889ef1d4c7SPatrick McHardy e->error = -EMSGSIZE; 9899ef1d4c7SPatrick McHardy memset(&e->msg, 0, sizeof(e->msg)); 9901da177e4SLinus Torvalds } 9912942e900SThomas Graf 99215e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 993a8cb16ddSEric Dumazet } else { 9944b1f0d33SDonald Sharp ip_mr_forward(net, mrt, skb->dev, skb, c, 0); 9951da177e4SLinus Torvalds } 9961da177e4SLinus Torvalds } 997a8cb16ddSEric Dumazet } 9981da177e4SLinus Torvalds 9995a645dd8SJulien Gomes /* Bounce a cache query up to mrouted and netlink. 10001da177e4SLinus Torvalds * 10011da177e4SLinus Torvalds * Called under mrt_lock. 10021da177e4SLinus Torvalds */ 10030c12295aSPatrick McHardy static int ipmr_cache_report(struct mr_table *mrt, 10044feb88e5SBenjamin Thery struct sk_buff *pkt, vifi_t vifi, int assert) 10051da177e4SLinus Torvalds { 1006c9bdd4b5SArnaldo Carvalho de Melo const int ihl = ip_hdrlen(pkt); 1007c316c629SNikolay Aleksandrov struct sock *mroute_sk; 10081da177e4SLinus Torvalds struct igmphdr *igmp; 10091da177e4SLinus Torvalds struct igmpmsg *msg; 1010c316c629SNikolay Aleksandrov struct sk_buff *skb; 10111da177e4SLinus Torvalds int ret; 10121da177e4SLinus Torvalds 10131da177e4SLinus Torvalds if (assert == IGMPMSG_WHOLEPKT) 10141da177e4SLinus Torvalds skb = skb_realloc_headroom(pkt, sizeof(struct iphdr)); 10151da177e4SLinus Torvalds else 10161da177e4SLinus Torvalds skb = alloc_skb(128, GFP_ATOMIC); 10171da177e4SLinus Torvalds 10181da177e4SLinus Torvalds if (!skb) 10191da177e4SLinus Torvalds return -ENOBUFS; 10201da177e4SLinus Torvalds 10211da177e4SLinus Torvalds if (assert == IGMPMSG_WHOLEPKT) { 10221da177e4SLinus Torvalds /* Ugly, but we have no choice with this interface. 1023a8cb16ddSEric Dumazet * Duplicate old header, fix ihl, length etc. 1024a8cb16ddSEric Dumazet * And all this only to mangle msg->im_msgtype and 1025a8cb16ddSEric Dumazet * to set msg->im_mbz to "mbz" :-) 10261da177e4SLinus Torvalds */ 1027878c8145SArnaldo Carvalho de Melo skb_push(skb, sizeof(struct iphdr)); 1028878c8145SArnaldo Carvalho de Melo skb_reset_network_header(skb); 1029badff6d0SArnaldo Carvalho de Melo skb_reset_transport_header(skb); 10300272ffc4SArnaldo Carvalho de Melo msg = (struct igmpmsg *)skb_network_header(skb); 1031d56f90a7SArnaldo Carvalho de Melo memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr)); 10321da177e4SLinus Torvalds msg->im_msgtype = IGMPMSG_WHOLEPKT; 10331da177e4SLinus Torvalds msg->im_mbz = 0; 10340c12295aSPatrick McHardy msg->im_vif = mrt->mroute_reg_vif_num; 1035eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2; 1036eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) + 1037eddc9ec5SArnaldo Carvalho de Melo sizeof(struct iphdr)); 1038c316c629SNikolay Aleksandrov } else { 1039a8cb16ddSEric Dumazet /* Copy the IP header */ 104030f3a40fSCong Wang skb_set_network_header(skb, skb->len); 1041ddc7b8e3SArnaldo Carvalho de Melo skb_put(skb, ihl); 104227d7ff46SArnaldo Carvalho de Melo skb_copy_to_linear_data(skb, pkt->data, ihl); 1043c316c629SNikolay Aleksandrov /* Flag to the kernel this is a route add */ 1044c316c629SNikolay Aleksandrov ip_hdr(skb)->protocol = 0; 1045eddc9ec5SArnaldo Carvalho de Melo msg = (struct igmpmsg *)skb_network_header(skb); 10461da177e4SLinus Torvalds msg->im_vif = vifi; 1047adf30907SEric Dumazet skb_dst_set(skb, dst_clone(skb_dst(pkt))); 1048a8cb16ddSEric Dumazet /* Add our header */ 10494df864c1SJohannes Berg igmp = skb_put(skb, sizeof(struct igmphdr)); 1050c316c629SNikolay Aleksandrov igmp->type = assert; 10511da177e4SLinus Torvalds msg->im_msgtype = assert; 10521da177e4SLinus Torvalds igmp->code = 0; 1053eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */ 1054b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 10551da177e4SLinus Torvalds } 10561da177e4SLinus Torvalds 10574c968709SEric Dumazet rcu_read_lock(); 10584c968709SEric Dumazet mroute_sk = rcu_dereference(mrt->mroute_sk); 105951456b29SIan Morris if (!mroute_sk) { 10604c968709SEric Dumazet rcu_read_unlock(); 10611da177e4SLinus Torvalds kfree_skb(skb); 10621da177e4SLinus Torvalds return -EINVAL; 10631da177e4SLinus Torvalds } 10641da177e4SLinus Torvalds 10655a645dd8SJulien Gomes igmpmsg_netlink_event(mrt, skb); 10665a645dd8SJulien Gomes 1067a8cb16ddSEric Dumazet /* Deliver to mrouted */ 10684c968709SEric Dumazet ret = sock_queue_rcv_skb(mroute_sk, skb); 10694c968709SEric Dumazet rcu_read_unlock(); 107070a269e6SBenjamin Thery if (ret < 0) { 1071e87cc472SJoe Perches net_warn_ratelimited("mroute: pending queue full, dropping entries\n"); 10721da177e4SLinus Torvalds kfree_skb(skb); 10731da177e4SLinus Torvalds } 10741da177e4SLinus Torvalds 10751da177e4SLinus Torvalds return ret; 10761da177e4SLinus Torvalds } 10771da177e4SLinus Torvalds 10787ef8f65dSNikolay Aleksandrov /* Queue a packet for resolution. It gets locked cache entry! */ 10797ef8f65dSNikolay Aleksandrov static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, 10804b1f0d33SDonald Sharp struct sk_buff *skb, struct net_device *dev) 10811da177e4SLinus Torvalds { 10828fb472c0SNikolay Aleksandrov const struct iphdr *iph = ip_hdr(skb); 10838fb472c0SNikolay Aleksandrov struct mfc_cache *c; 1084862465f2SPatrick McHardy bool found = false; 10851da177e4SLinus Torvalds int err; 10861da177e4SLinus Torvalds 10871da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 10880c12295aSPatrick McHardy list_for_each_entry(c, &mrt->mfc_unres_queue, list) { 1089e258beb2SPatrick McHardy if (c->mfc_mcastgrp == iph->daddr && 1090862465f2SPatrick McHardy c->mfc_origin == iph->saddr) { 1091862465f2SPatrick McHardy found = true; 10921da177e4SLinus Torvalds break; 10931da177e4SLinus Torvalds } 1094862465f2SPatrick McHardy } 10951da177e4SLinus Torvalds 1096862465f2SPatrick McHardy if (!found) { 1097a8cb16ddSEric Dumazet /* Create a new entry if allowable */ 10980c12295aSPatrick McHardy if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 || 1099d658f8a0SPatrick McHardy (c = ipmr_cache_alloc_unres()) == NULL) { 11001da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11011da177e4SLinus Torvalds 11021da177e4SLinus Torvalds kfree_skb(skb); 11031da177e4SLinus Torvalds return -ENOBUFS; 11041da177e4SLinus Torvalds } 11051da177e4SLinus Torvalds 1106a8cb16ddSEric Dumazet /* Fill in the new cache entry */ 11071da177e4SLinus Torvalds c->mfc_parent = -1; 1108eddc9ec5SArnaldo Carvalho de Melo c->mfc_origin = iph->saddr; 1109eddc9ec5SArnaldo Carvalho de Melo c->mfc_mcastgrp = iph->daddr; 11101da177e4SLinus Torvalds 1111a8cb16ddSEric Dumazet /* Reflect first query at mrouted. */ 11120c12295aSPatrick McHardy err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE); 11134feb88e5SBenjamin Thery if (err < 0) { 11141da177e4SLinus Torvalds /* If the report failed throw the cache entry 11151da177e4SLinus Torvalds out - Brad Parker 11161da177e4SLinus Torvalds */ 11171da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11181da177e4SLinus Torvalds 11195c0a66f5SBenjamin Thery ipmr_cache_free(c); 11201da177e4SLinus Torvalds kfree_skb(skb); 11211da177e4SLinus Torvalds return err; 11221da177e4SLinus Torvalds } 11231da177e4SLinus Torvalds 11240c12295aSPatrick McHardy atomic_inc(&mrt->cache_resolve_queue_len); 11250c12295aSPatrick McHardy list_add(&c->list, &mrt->mfc_unres_queue); 11268cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 11271da177e4SLinus Torvalds 1128278554bdSDavid S. Miller if (atomic_read(&mrt->cache_resolve_queue_len) == 1) 11290c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires); 11301da177e4SLinus Torvalds } 11311da177e4SLinus Torvalds 1132a8cb16ddSEric Dumazet /* See if we can append the packet */ 11331da177e4SLinus Torvalds if (c->mfc_un.unres.unresolved.qlen > 3) { 11341da177e4SLinus Torvalds kfree_skb(skb); 11351da177e4SLinus Torvalds err = -ENOBUFS; 11361da177e4SLinus Torvalds } else { 11374b1f0d33SDonald Sharp if (dev) { 11384b1f0d33SDonald Sharp skb->dev = dev; 11394b1f0d33SDonald Sharp skb->skb_iif = dev->ifindex; 11404b1f0d33SDonald Sharp } 11411da177e4SLinus Torvalds skb_queue_tail(&c->mfc_un.unres.unresolved, skb); 11421da177e4SLinus Torvalds err = 0; 11431da177e4SLinus Torvalds } 11441da177e4SLinus Torvalds 11451da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11461da177e4SLinus Torvalds return err; 11471da177e4SLinus Torvalds } 11481da177e4SLinus Torvalds 11497ef8f65dSNikolay Aleksandrov /* MFC cache manipulation by user space mroute daemon */ 11501da177e4SLinus Torvalds 1151660b26dcSNicolas Dichtel static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent) 11521da177e4SLinus Torvalds { 11538fb472c0SNikolay Aleksandrov struct mfc_cache *c; 11541da177e4SLinus Torvalds 11558fb472c0SNikolay Aleksandrov /* The entries are added/deleted only under RTNL */ 11568fb472c0SNikolay Aleksandrov rcu_read_lock(); 11578fb472c0SNikolay Aleksandrov c = ipmr_cache_find_parent(mrt, mfc->mfcc_origin.s_addr, 11588fb472c0SNikolay Aleksandrov mfc->mfcc_mcastgrp.s_addr, parent); 11598fb472c0SNikolay Aleksandrov rcu_read_unlock(); 11608fb472c0SNikolay Aleksandrov if (!c) 11618fb472c0SNikolay Aleksandrov return -ENOENT; 11628fb472c0SNikolay Aleksandrov rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params); 1163a8c9486bSEric Dumazet list_del_rcu(&c->list); 11648cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 11655c0a66f5SBenjamin Thery ipmr_cache_free(c); 11668fb472c0SNikolay Aleksandrov 11671da177e4SLinus Torvalds return 0; 11681da177e4SLinus Torvalds } 11691da177e4SLinus Torvalds 11700c12295aSPatrick McHardy static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, 1171660b26dcSNicolas Dichtel struct mfcctl *mfc, int mrtsock, int parent) 11721da177e4SLinus Torvalds { 1173862465f2SPatrick McHardy struct mfc_cache *uc, *c; 11748fb472c0SNikolay Aleksandrov bool found; 11758fb472c0SNikolay Aleksandrov int ret; 11761da177e4SLinus Torvalds 1177a50436f2SPatrick McHardy if (mfc->mfcc_parent >= MAXVIFS) 1178a50436f2SPatrick McHardy return -ENFILE; 1179a50436f2SPatrick McHardy 11808fb472c0SNikolay Aleksandrov /* The entries are added/deleted only under RTNL */ 11818fb472c0SNikolay Aleksandrov rcu_read_lock(); 11828fb472c0SNikolay Aleksandrov c = ipmr_cache_find_parent(mrt, mfc->mfcc_origin.s_addr, 11838fb472c0SNikolay Aleksandrov mfc->mfcc_mcastgrp.s_addr, parent); 11848fb472c0SNikolay Aleksandrov rcu_read_unlock(); 11858fb472c0SNikolay Aleksandrov if (c) { 11861da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 11871da177e4SLinus Torvalds c->mfc_parent = mfc->mfcc_parent; 11880c12295aSPatrick McHardy ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls); 11891da177e4SLinus Torvalds if (!mrtsock) 11901da177e4SLinus Torvalds c->mfc_flags |= MFC_STATIC; 11911da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 11928cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 11931da177e4SLinus Torvalds return 0; 11941da177e4SLinus Torvalds } 11951da177e4SLinus Torvalds 1196360eb5daSNicolas Dichtel if (mfc->mfcc_mcastgrp.s_addr != htonl(INADDR_ANY) && 1197660b26dcSNicolas Dichtel !ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) 11981da177e4SLinus Torvalds return -EINVAL; 11991da177e4SLinus Torvalds 1200d658f8a0SPatrick McHardy c = ipmr_cache_alloc(); 120151456b29SIan Morris if (!c) 12021da177e4SLinus Torvalds return -ENOMEM; 12031da177e4SLinus Torvalds 12041da177e4SLinus Torvalds c->mfc_origin = mfc->mfcc_origin.s_addr; 12051da177e4SLinus Torvalds c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr; 12061da177e4SLinus Torvalds c->mfc_parent = mfc->mfcc_parent; 12070c12295aSPatrick McHardy ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls); 12081da177e4SLinus Torvalds if (!mrtsock) 12091da177e4SLinus Torvalds c->mfc_flags |= MFC_STATIC; 12101da177e4SLinus Torvalds 12118fb472c0SNikolay Aleksandrov ret = rhltable_insert_key(&mrt->mfc_hash, &c->cmparg, &c->mnode, 12128fb472c0SNikolay Aleksandrov ipmr_rht_params); 12138fb472c0SNikolay Aleksandrov if (ret) { 12148fb472c0SNikolay Aleksandrov pr_err("ipmr: rhtable insert error %d\n", ret); 12158fb472c0SNikolay Aleksandrov ipmr_cache_free(c); 12168fb472c0SNikolay Aleksandrov return ret; 12178fb472c0SNikolay Aleksandrov } 12188fb472c0SNikolay Aleksandrov list_add_tail_rcu(&c->list, &mrt->mfc_cache_list); 12197ef8f65dSNikolay Aleksandrov /* Check to see if we resolved a queued list. If so we 12201da177e4SLinus Torvalds * need to send on the frames and tidy up. 12211da177e4SLinus Torvalds */ 1222b0ebb739SPatrick McHardy found = false; 12231da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 12240c12295aSPatrick McHardy list_for_each_entry(uc, &mrt->mfc_unres_queue, list) { 1225e258beb2SPatrick McHardy if (uc->mfc_origin == c->mfc_origin && 12261da177e4SLinus Torvalds uc->mfc_mcastgrp == c->mfc_mcastgrp) { 1227862465f2SPatrick McHardy list_del(&uc->list); 12280c12295aSPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len); 1229b0ebb739SPatrick McHardy found = true; 12301da177e4SLinus Torvalds break; 12311da177e4SLinus Torvalds } 12321da177e4SLinus Torvalds } 12330c12295aSPatrick McHardy if (list_empty(&mrt->mfc_unres_queue)) 12340c12295aSPatrick McHardy del_timer(&mrt->ipmr_expire_timer); 12351da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 12361da177e4SLinus Torvalds 1237b0ebb739SPatrick McHardy if (found) { 12380c12295aSPatrick McHardy ipmr_cache_resolve(net, mrt, uc, c); 12395c0a66f5SBenjamin Thery ipmr_cache_free(uc); 12401da177e4SLinus Torvalds } 12418cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 12421da177e4SLinus Torvalds return 0; 12431da177e4SLinus Torvalds } 12441da177e4SLinus Torvalds 12457ef8f65dSNikolay Aleksandrov /* Close the multicast socket, and clear the vif tables etc */ 12460e615e96SNikolay Aleksandrov static void mroute_clean_tables(struct mr_table *mrt, bool all) 12471da177e4SLinus Torvalds { 12488fb472c0SNikolay Aleksandrov struct mfc_cache *c, *tmp; 1249d17fa6faSEric Dumazet LIST_HEAD(list); 12508fb472c0SNikolay Aleksandrov int i; 12511da177e4SLinus Torvalds 1252a8cb16ddSEric Dumazet /* Shut down all active vif entries */ 12530c12295aSPatrick McHardy for (i = 0; i < mrt->maxvif; i++) { 12540e615e96SNikolay Aleksandrov if (!all && (mrt->vif_table[i].flags & VIFF_STATIC)) 12550e615e96SNikolay Aleksandrov continue; 12560c12295aSPatrick McHardy vif_delete(mrt, i, 0, &list); 12571da177e4SLinus Torvalds } 1258d17fa6faSEric Dumazet unregister_netdevice_many(&list); 12591da177e4SLinus Torvalds 1260a8cb16ddSEric Dumazet /* Wipe the cache */ 12618fb472c0SNikolay Aleksandrov list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) { 12620e615e96SNikolay Aleksandrov if (!all && (c->mfc_flags & MFC_STATIC)) 12631da177e4SLinus Torvalds continue; 12648fb472c0SNikolay Aleksandrov rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params); 1265a8c9486bSEric Dumazet list_del_rcu(&c->list); 12668cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 12675c0a66f5SBenjamin Thery ipmr_cache_free(c); 12681da177e4SLinus Torvalds } 12691da177e4SLinus Torvalds 12700c12295aSPatrick McHardy if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { 12711da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 12728fb472c0SNikolay Aleksandrov list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { 1273862465f2SPatrick McHardy list_del(&c->list); 12748cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 12750c12295aSPatrick McHardy ipmr_destroy_unres(mrt, c); 12761da177e4SLinus Torvalds } 12771da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 12781da177e4SLinus Torvalds } 12791da177e4SLinus Torvalds } 12801da177e4SLinus Torvalds 12814c968709SEric Dumazet /* called from ip_ra_control(), before an RCU grace period, 12824c968709SEric Dumazet * we dont need to call synchronize_rcu() here 12834c968709SEric Dumazet */ 12841da177e4SLinus Torvalds static void mrtsock_destruct(struct sock *sk) 12851da177e4SLinus Torvalds { 12864feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1287f0ad0860SPatrick McHardy struct mr_table *mrt; 12884feb88e5SBenjamin Thery 12891215e51eSWANG Cong ASSERT_RTNL(); 1290f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 12914c968709SEric Dumazet if (sk == rtnl_dereference(mrt->mroute_sk)) { 12924feb88e5SBenjamin Thery IPV4_DEVCONF_ALL(net, MC_FORWARDING)--; 12933b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 12943b022865SDavid Ahern NETCONFA_MC_FORWARDING, 1295d67b8c61SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1296d67b8c61SNicolas Dichtel net->ipv4.devconf_all); 1297a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(mrt->mroute_sk, NULL); 12980e615e96SNikolay Aleksandrov mroute_clean_tables(mrt, false); 12991da177e4SLinus Torvalds } 13001da177e4SLinus Torvalds } 13011da177e4SLinus Torvalds } 13021da177e4SLinus Torvalds 13037ef8f65dSNikolay Aleksandrov /* Socket options and virtual interface manipulation. The whole 13041da177e4SLinus Torvalds * virtual interface system is a complete heap, but unfortunately 13051da177e4SLinus Torvalds * that's how BSD mrouted happens to think. Maybe one day with a proper 13061da177e4SLinus Torvalds * MOSPF/PIM router set up we can clean this up. 13071da177e4SLinus Torvalds */ 13081da177e4SLinus Torvalds 130929e97d21SNikolay Aleksandrov int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, 131029e97d21SNikolay Aleksandrov unsigned int optlen) 13111da177e4SLinus Torvalds { 131229e97d21SNikolay Aleksandrov struct net *net = sock_net(sk); 131329e97d21SNikolay Aleksandrov int val, ret = 0, parent = 0; 131429e97d21SNikolay Aleksandrov struct mr_table *mrt; 13151da177e4SLinus Torvalds struct vifctl vif; 13161da177e4SLinus Torvalds struct mfcctl mfc; 131729e97d21SNikolay Aleksandrov u32 uval; 1318f0ad0860SPatrick McHardy 131929e97d21SNikolay Aleksandrov /* There's one exception to the lock - MRT_DONE which needs to unlock */ 132029e97d21SNikolay Aleksandrov rtnl_lock(); 13215e1859fbSEric Dumazet if (sk->sk_type != SOCK_RAW || 132229e97d21SNikolay Aleksandrov inet_sk(sk)->inet_num != IPPROTO_IGMP) { 132329e97d21SNikolay Aleksandrov ret = -EOPNOTSUPP; 132429e97d21SNikolay Aleksandrov goto out_unlock; 132529e97d21SNikolay Aleksandrov } 13265e1859fbSEric Dumazet 1327f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 132829e97d21SNikolay Aleksandrov if (!mrt) { 132929e97d21SNikolay Aleksandrov ret = -ENOENT; 133029e97d21SNikolay Aleksandrov goto out_unlock; 133129e97d21SNikolay Aleksandrov } 1332132adf54SStephen Hemminger if (optname != MRT_INIT) { 133333d480ceSEric Dumazet if (sk != rcu_access_pointer(mrt->mroute_sk) && 133429e97d21SNikolay Aleksandrov !ns_capable(net->user_ns, CAP_NET_ADMIN)) { 133529e97d21SNikolay Aleksandrov ret = -EACCES; 133629e97d21SNikolay Aleksandrov goto out_unlock; 133729e97d21SNikolay Aleksandrov } 13381da177e4SLinus Torvalds } 13391da177e4SLinus Torvalds 1340132adf54SStephen Hemminger switch (optname) { 13411da177e4SLinus Torvalds case MRT_INIT: 134242e6b89cSNikolay Aleksandrov if (optlen != sizeof(int)) { 134329e97d21SNikolay Aleksandrov ret = -EINVAL; 134429e97d21SNikolay Aleksandrov break; 134542e6b89cSNikolay Aleksandrov } 134642e6b89cSNikolay Aleksandrov if (rtnl_dereference(mrt->mroute_sk)) { 134742e6b89cSNikolay Aleksandrov ret = -EADDRINUSE; 134842e6b89cSNikolay Aleksandrov break; 134942e6b89cSNikolay Aleksandrov } 13501da177e4SLinus Torvalds 13511da177e4SLinus Torvalds ret = ip_ra_control(sk, 1, mrtsock_destruct); 13521da177e4SLinus Torvalds if (ret == 0) { 1353cf778b00SEric Dumazet rcu_assign_pointer(mrt->mroute_sk, sk); 13544feb88e5SBenjamin Thery IPV4_DEVCONF_ALL(net, MC_FORWARDING)++; 13553b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 13563b022865SDavid Ahern NETCONFA_MC_FORWARDING, 1357d67b8c61SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1358d67b8c61SNicolas Dichtel net->ipv4.devconf_all); 13591da177e4SLinus Torvalds } 136029e97d21SNikolay Aleksandrov break; 13611da177e4SLinus Torvalds case MRT_DONE: 136229e97d21SNikolay Aleksandrov if (sk != rcu_access_pointer(mrt->mroute_sk)) { 136329e97d21SNikolay Aleksandrov ret = -EACCES; 136429e97d21SNikolay Aleksandrov } else { 136529e97d21SNikolay Aleksandrov ret = ip_ra_control(sk, 0, NULL); 13661215e51eSWANG Cong goto out_unlock; 136729e97d21SNikolay Aleksandrov } 136829e97d21SNikolay Aleksandrov break; 13691da177e4SLinus Torvalds case MRT_ADD_VIF: 13701da177e4SLinus Torvalds case MRT_DEL_VIF: 137129e97d21SNikolay Aleksandrov if (optlen != sizeof(vif)) { 137229e97d21SNikolay Aleksandrov ret = -EINVAL; 137329e97d21SNikolay Aleksandrov break; 137429e97d21SNikolay Aleksandrov } 137529e97d21SNikolay Aleksandrov if (copy_from_user(&vif, optval, sizeof(vif))) { 137629e97d21SNikolay Aleksandrov ret = -EFAULT; 137729e97d21SNikolay Aleksandrov break; 137829e97d21SNikolay Aleksandrov } 137929e97d21SNikolay Aleksandrov if (vif.vifc_vifi >= MAXVIFS) { 138029e97d21SNikolay Aleksandrov ret = -ENFILE; 138129e97d21SNikolay Aleksandrov break; 138229e97d21SNikolay Aleksandrov } 13831da177e4SLinus Torvalds if (optname == MRT_ADD_VIF) { 13844c968709SEric Dumazet ret = vif_add(net, mrt, &vif, 13854c968709SEric Dumazet sk == rtnl_dereference(mrt->mroute_sk)); 13861da177e4SLinus Torvalds } else { 13870c12295aSPatrick McHardy ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL); 13881da177e4SLinus Torvalds } 138929e97d21SNikolay Aleksandrov break; 13907ef8f65dSNikolay Aleksandrov /* Manipulate the forwarding caches. These live 13911da177e4SLinus Torvalds * in a sort of kernel/user symbiosis. 13921da177e4SLinus Torvalds */ 13931da177e4SLinus Torvalds case MRT_ADD_MFC: 13941da177e4SLinus Torvalds case MRT_DEL_MFC: 1395660b26dcSNicolas Dichtel parent = -1; 1396660b26dcSNicolas Dichtel case MRT_ADD_MFC_PROXY: 1397660b26dcSNicolas Dichtel case MRT_DEL_MFC_PROXY: 139829e97d21SNikolay Aleksandrov if (optlen != sizeof(mfc)) { 139929e97d21SNikolay Aleksandrov ret = -EINVAL; 140029e97d21SNikolay Aleksandrov break; 140129e97d21SNikolay Aleksandrov } 140229e97d21SNikolay Aleksandrov if (copy_from_user(&mfc, optval, sizeof(mfc))) { 140329e97d21SNikolay Aleksandrov ret = -EFAULT; 140429e97d21SNikolay Aleksandrov break; 140529e97d21SNikolay Aleksandrov } 1406660b26dcSNicolas Dichtel if (parent == 0) 1407660b26dcSNicolas Dichtel parent = mfc.mfcc_parent; 1408660b26dcSNicolas Dichtel if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY) 1409660b26dcSNicolas Dichtel ret = ipmr_mfc_delete(mrt, &mfc, parent); 14101da177e4SLinus Torvalds else 14114c968709SEric Dumazet ret = ipmr_mfc_add(net, mrt, &mfc, 1412660b26dcSNicolas Dichtel sk == rtnl_dereference(mrt->mroute_sk), 1413660b26dcSNicolas Dichtel parent); 141429e97d21SNikolay Aleksandrov break; 14157ef8f65dSNikolay Aleksandrov /* Control PIM assert. */ 14161da177e4SLinus Torvalds case MRT_ASSERT: 141729e97d21SNikolay Aleksandrov if (optlen != sizeof(val)) { 141829e97d21SNikolay Aleksandrov ret = -EINVAL; 141929e97d21SNikolay Aleksandrov break; 14201da177e4SLinus Torvalds } 142129e97d21SNikolay Aleksandrov if (get_user(val, (int __user *)optval)) { 142229e97d21SNikolay Aleksandrov ret = -EFAULT; 142329e97d21SNikolay Aleksandrov break; 142429e97d21SNikolay Aleksandrov } 142529e97d21SNikolay Aleksandrov mrt->mroute_do_assert = val; 142629e97d21SNikolay Aleksandrov break; 14271da177e4SLinus Torvalds case MRT_PIM: 14281973a4eaSNikolay Aleksandrov if (!ipmr_pimsm_enabled()) { 142929e97d21SNikolay Aleksandrov ret = -ENOPROTOOPT; 143029e97d21SNikolay Aleksandrov break; 14311da177e4SLinus Torvalds } 143229e97d21SNikolay Aleksandrov if (optlen != sizeof(val)) { 143329e97d21SNikolay Aleksandrov ret = -EINVAL; 143429e97d21SNikolay Aleksandrov break; 14351da177e4SLinus Torvalds } 143629e97d21SNikolay Aleksandrov if (get_user(val, (int __user *)optval)) { 143729e97d21SNikolay Aleksandrov ret = -EFAULT; 143829e97d21SNikolay Aleksandrov break; 143929e97d21SNikolay Aleksandrov } 144029e97d21SNikolay Aleksandrov 144129e97d21SNikolay Aleksandrov val = !!val; 144229e97d21SNikolay Aleksandrov if (val != mrt->mroute_do_pim) { 144329e97d21SNikolay Aleksandrov mrt->mroute_do_pim = val; 144429e97d21SNikolay Aleksandrov mrt->mroute_do_assert = val; 144529e97d21SNikolay Aleksandrov } 144629e97d21SNikolay Aleksandrov break; 1447f0ad0860SPatrick McHardy case MRT_TABLE: 144829e97d21SNikolay Aleksandrov if (!IS_BUILTIN(CONFIG_IP_MROUTE_MULTIPLE_TABLES)) { 144929e97d21SNikolay Aleksandrov ret = -ENOPROTOOPT; 145029e97d21SNikolay Aleksandrov break; 145129e97d21SNikolay Aleksandrov } 145229e97d21SNikolay Aleksandrov if (optlen != sizeof(uval)) { 145329e97d21SNikolay Aleksandrov ret = -EINVAL; 145429e97d21SNikolay Aleksandrov break; 145529e97d21SNikolay Aleksandrov } 145629e97d21SNikolay Aleksandrov if (get_user(uval, (u32 __user *)optval)) { 145729e97d21SNikolay Aleksandrov ret = -EFAULT; 145829e97d21SNikolay Aleksandrov break; 145929e97d21SNikolay Aleksandrov } 1460f0ad0860SPatrick McHardy 14614c968709SEric Dumazet if (sk == rtnl_dereference(mrt->mroute_sk)) { 14624c968709SEric Dumazet ret = -EBUSY; 14634c968709SEric Dumazet } else { 146429e97d21SNikolay Aleksandrov mrt = ipmr_new_table(net, uval); 14651113ebbcSNikolay Aleksandrov if (IS_ERR(mrt)) 14661113ebbcSNikolay Aleksandrov ret = PTR_ERR(mrt); 14675e1859fbSEric Dumazet else 146829e97d21SNikolay Aleksandrov raw_sk(sk)->ipmr_table = uval; 14694c968709SEric Dumazet } 147029e97d21SNikolay Aleksandrov break; 14717ef8f65dSNikolay Aleksandrov /* Spurious command, or MRT_VERSION which you cannot set. */ 14721da177e4SLinus Torvalds default: 147329e97d21SNikolay Aleksandrov ret = -ENOPROTOOPT; 14741da177e4SLinus Torvalds } 147529e97d21SNikolay Aleksandrov out_unlock: 147629e97d21SNikolay Aleksandrov rtnl_unlock(); 147729e97d21SNikolay Aleksandrov return ret; 14781da177e4SLinus Torvalds } 14791da177e4SLinus Torvalds 14807ef8f65dSNikolay Aleksandrov /* Getsock opt support for the multicast routing system. */ 14811da177e4SLinus Torvalds int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen) 14821da177e4SLinus Torvalds { 14831da177e4SLinus Torvalds int olr; 14841da177e4SLinus Torvalds int val; 14854feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1486f0ad0860SPatrick McHardy struct mr_table *mrt; 1487f0ad0860SPatrick McHardy 14885e1859fbSEric Dumazet if (sk->sk_type != SOCK_RAW || 14895e1859fbSEric Dumazet inet_sk(sk)->inet_num != IPPROTO_IGMP) 14905e1859fbSEric Dumazet return -EOPNOTSUPP; 14915e1859fbSEric Dumazet 1492f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 149351456b29SIan Morris if (!mrt) 1494f0ad0860SPatrick McHardy return -ENOENT; 14951da177e4SLinus Torvalds 1496fe9ef3ceSNikolay Aleksandrov switch (optname) { 1497fe9ef3ceSNikolay Aleksandrov case MRT_VERSION: 14981da177e4SLinus Torvalds val = 0x0305; 1499fe9ef3ceSNikolay Aleksandrov break; 1500fe9ef3ceSNikolay Aleksandrov case MRT_PIM: 15011973a4eaSNikolay Aleksandrov if (!ipmr_pimsm_enabled()) 1502c316c629SNikolay Aleksandrov return -ENOPROTOOPT; 15030c12295aSPatrick McHardy val = mrt->mroute_do_pim; 1504fe9ef3ceSNikolay Aleksandrov break; 1505fe9ef3ceSNikolay Aleksandrov case MRT_ASSERT: 15060c12295aSPatrick McHardy val = mrt->mroute_do_assert; 1507fe9ef3ceSNikolay Aleksandrov break; 1508fe9ef3ceSNikolay Aleksandrov default: 1509fe9ef3ceSNikolay Aleksandrov return -ENOPROTOOPT; 1510c316c629SNikolay Aleksandrov } 1511fe9ef3ceSNikolay Aleksandrov 1512fe9ef3ceSNikolay Aleksandrov if (get_user(olr, optlen)) 1513fe9ef3ceSNikolay Aleksandrov return -EFAULT; 1514fe9ef3ceSNikolay Aleksandrov olr = min_t(unsigned int, olr, sizeof(int)); 1515fe9ef3ceSNikolay Aleksandrov if (olr < 0) 1516fe9ef3ceSNikolay Aleksandrov return -EINVAL; 1517fe9ef3ceSNikolay Aleksandrov if (put_user(olr, optlen)) 1518fe9ef3ceSNikolay Aleksandrov return -EFAULT; 15191da177e4SLinus Torvalds if (copy_to_user(optval, &val, olr)) 15201da177e4SLinus Torvalds return -EFAULT; 15211da177e4SLinus Torvalds return 0; 15221da177e4SLinus Torvalds } 15231da177e4SLinus Torvalds 15247ef8f65dSNikolay Aleksandrov /* The IP multicast ioctl support routines. */ 15251da177e4SLinus Torvalds int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) 15261da177e4SLinus Torvalds { 15271da177e4SLinus Torvalds struct sioc_sg_req sr; 15281da177e4SLinus Torvalds struct sioc_vif_req vr; 15291da177e4SLinus Torvalds struct vif_device *vif; 15301da177e4SLinus Torvalds struct mfc_cache *c; 15314feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1532f0ad0860SPatrick McHardy struct mr_table *mrt; 1533f0ad0860SPatrick McHardy 1534f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 153551456b29SIan Morris if (!mrt) 1536f0ad0860SPatrick McHardy return -ENOENT; 15371da177e4SLinus Torvalds 1538132adf54SStephen Hemminger switch (cmd) { 15391da177e4SLinus Torvalds case SIOCGETVIFCNT: 15401da177e4SLinus Torvalds if (copy_from_user(&vr, arg, sizeof(vr))) 15411da177e4SLinus Torvalds return -EFAULT; 15420c12295aSPatrick McHardy if (vr.vifi >= mrt->maxvif) 15431da177e4SLinus Torvalds return -EINVAL; 15441da177e4SLinus Torvalds read_lock(&mrt_lock); 15450c12295aSPatrick McHardy vif = &mrt->vif_table[vr.vifi]; 15460c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vr.vifi)) { 15471da177e4SLinus Torvalds vr.icount = vif->pkt_in; 15481da177e4SLinus Torvalds vr.ocount = vif->pkt_out; 15491da177e4SLinus Torvalds vr.ibytes = vif->bytes_in; 15501da177e4SLinus Torvalds vr.obytes = vif->bytes_out; 15511da177e4SLinus Torvalds read_unlock(&mrt_lock); 15521da177e4SLinus Torvalds 15531da177e4SLinus Torvalds if (copy_to_user(arg, &vr, sizeof(vr))) 15541da177e4SLinus Torvalds return -EFAULT; 15551da177e4SLinus Torvalds return 0; 15561da177e4SLinus Torvalds } 15571da177e4SLinus Torvalds read_unlock(&mrt_lock); 15581da177e4SLinus Torvalds return -EADDRNOTAVAIL; 15591da177e4SLinus Torvalds case SIOCGETSGCNT: 15601da177e4SLinus Torvalds if (copy_from_user(&sr, arg, sizeof(sr))) 15611da177e4SLinus Torvalds return -EFAULT; 15621da177e4SLinus Torvalds 1563a8c9486bSEric Dumazet rcu_read_lock(); 15640c12295aSPatrick McHardy c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); 15651da177e4SLinus Torvalds if (c) { 15661da177e4SLinus Torvalds sr.pktcnt = c->mfc_un.res.pkt; 15671da177e4SLinus Torvalds sr.bytecnt = c->mfc_un.res.bytes; 15681da177e4SLinus Torvalds sr.wrong_if = c->mfc_un.res.wrong_if; 1569a8c9486bSEric Dumazet rcu_read_unlock(); 15701da177e4SLinus Torvalds 15711da177e4SLinus Torvalds if (copy_to_user(arg, &sr, sizeof(sr))) 15721da177e4SLinus Torvalds return -EFAULT; 15731da177e4SLinus Torvalds return 0; 15741da177e4SLinus Torvalds } 1575a8c9486bSEric Dumazet rcu_read_unlock(); 15761da177e4SLinus Torvalds return -EADDRNOTAVAIL; 15771da177e4SLinus Torvalds default: 15781da177e4SLinus Torvalds return -ENOIOCTLCMD; 15791da177e4SLinus Torvalds } 15801da177e4SLinus Torvalds } 15811da177e4SLinus Torvalds 1582709b46e8SEric W. Biederman #ifdef CONFIG_COMPAT 1583709b46e8SEric W. Biederman struct compat_sioc_sg_req { 1584709b46e8SEric W. Biederman struct in_addr src; 1585709b46e8SEric W. Biederman struct in_addr grp; 1586709b46e8SEric W. Biederman compat_ulong_t pktcnt; 1587709b46e8SEric W. Biederman compat_ulong_t bytecnt; 1588709b46e8SEric W. Biederman compat_ulong_t wrong_if; 1589709b46e8SEric W. Biederman }; 1590709b46e8SEric W. Biederman 1591ca6b8bb0SDavid S. Miller struct compat_sioc_vif_req { 1592ca6b8bb0SDavid S. Miller vifi_t vifi; /* Which iface */ 1593ca6b8bb0SDavid S. Miller compat_ulong_t icount; 1594ca6b8bb0SDavid S. Miller compat_ulong_t ocount; 1595ca6b8bb0SDavid S. Miller compat_ulong_t ibytes; 1596ca6b8bb0SDavid S. Miller compat_ulong_t obytes; 1597ca6b8bb0SDavid S. Miller }; 1598ca6b8bb0SDavid S. Miller 1599709b46e8SEric W. Biederman int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) 1600709b46e8SEric W. Biederman { 16010033d5adSDavid S. Miller struct compat_sioc_sg_req sr; 1602ca6b8bb0SDavid S. Miller struct compat_sioc_vif_req vr; 1603ca6b8bb0SDavid S. Miller struct vif_device *vif; 1604709b46e8SEric W. Biederman struct mfc_cache *c; 1605709b46e8SEric W. Biederman struct net *net = sock_net(sk); 1606709b46e8SEric W. Biederman struct mr_table *mrt; 1607709b46e8SEric W. Biederman 1608709b46e8SEric W. Biederman mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 160951456b29SIan Morris if (!mrt) 1610709b46e8SEric W. Biederman return -ENOENT; 1611709b46e8SEric W. Biederman 1612709b46e8SEric W. Biederman switch (cmd) { 1613ca6b8bb0SDavid S. Miller case SIOCGETVIFCNT: 1614ca6b8bb0SDavid S. Miller if (copy_from_user(&vr, arg, sizeof(vr))) 1615ca6b8bb0SDavid S. Miller return -EFAULT; 1616ca6b8bb0SDavid S. Miller if (vr.vifi >= mrt->maxvif) 1617ca6b8bb0SDavid S. Miller return -EINVAL; 1618ca6b8bb0SDavid S. Miller read_lock(&mrt_lock); 1619ca6b8bb0SDavid S. Miller vif = &mrt->vif_table[vr.vifi]; 1620ca6b8bb0SDavid S. Miller if (VIF_EXISTS(mrt, vr.vifi)) { 1621ca6b8bb0SDavid S. Miller vr.icount = vif->pkt_in; 1622ca6b8bb0SDavid S. Miller vr.ocount = vif->pkt_out; 1623ca6b8bb0SDavid S. Miller vr.ibytes = vif->bytes_in; 1624ca6b8bb0SDavid S. Miller vr.obytes = vif->bytes_out; 1625ca6b8bb0SDavid S. Miller read_unlock(&mrt_lock); 1626ca6b8bb0SDavid S. Miller 1627ca6b8bb0SDavid S. Miller if (copy_to_user(arg, &vr, sizeof(vr))) 1628ca6b8bb0SDavid S. Miller return -EFAULT; 1629ca6b8bb0SDavid S. Miller return 0; 1630ca6b8bb0SDavid S. Miller } 1631ca6b8bb0SDavid S. Miller read_unlock(&mrt_lock); 1632ca6b8bb0SDavid S. Miller return -EADDRNOTAVAIL; 1633709b46e8SEric W. Biederman case SIOCGETSGCNT: 1634709b46e8SEric W. Biederman if (copy_from_user(&sr, arg, sizeof(sr))) 1635709b46e8SEric W. Biederman return -EFAULT; 1636709b46e8SEric W. Biederman 1637709b46e8SEric W. Biederman rcu_read_lock(); 1638709b46e8SEric W. Biederman c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); 1639709b46e8SEric W. Biederman if (c) { 1640709b46e8SEric W. Biederman sr.pktcnt = c->mfc_un.res.pkt; 1641709b46e8SEric W. Biederman sr.bytecnt = c->mfc_un.res.bytes; 1642709b46e8SEric W. Biederman sr.wrong_if = c->mfc_un.res.wrong_if; 1643709b46e8SEric W. Biederman rcu_read_unlock(); 1644709b46e8SEric W. Biederman 1645709b46e8SEric W. Biederman if (copy_to_user(arg, &sr, sizeof(sr))) 1646709b46e8SEric W. Biederman return -EFAULT; 1647709b46e8SEric W. Biederman return 0; 1648709b46e8SEric W. Biederman } 1649709b46e8SEric W. Biederman rcu_read_unlock(); 1650709b46e8SEric W. Biederman return -EADDRNOTAVAIL; 1651709b46e8SEric W. Biederman default: 1652709b46e8SEric W. Biederman return -ENOIOCTLCMD; 1653709b46e8SEric W. Biederman } 1654709b46e8SEric W. Biederman } 1655709b46e8SEric W. Biederman #endif 1656709b46e8SEric W. Biederman 16571da177e4SLinus Torvalds static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr) 16581da177e4SLinus Torvalds { 1659351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 16604feb88e5SBenjamin Thery struct net *net = dev_net(dev); 1661f0ad0860SPatrick McHardy struct mr_table *mrt; 16621da177e4SLinus Torvalds struct vif_device *v; 16631da177e4SLinus Torvalds int ct; 1664e9dc8653SEric W. Biederman 16651da177e4SLinus Torvalds if (event != NETDEV_UNREGISTER) 16661da177e4SLinus Torvalds return NOTIFY_DONE; 1667f0ad0860SPatrick McHardy 1668f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 16690c12295aSPatrick McHardy v = &mrt->vif_table[0]; 16700c12295aSPatrick McHardy for (ct = 0; ct < mrt->maxvif; ct++, v++) { 1671e9dc8653SEric W. Biederman if (v->dev == dev) 1672e92036a6SRongQing.Li vif_delete(mrt, ct, 1, NULL); 16731da177e4SLinus Torvalds } 16741da177e4SLinus Torvalds } 16751da177e4SLinus Torvalds return NOTIFY_DONE; 16761da177e4SLinus Torvalds } 16771da177e4SLinus Torvalds 16781da177e4SLinus Torvalds static struct notifier_block ip_mr_notifier = { 16791da177e4SLinus Torvalds .notifier_call = ipmr_device_event, 16801da177e4SLinus Torvalds }; 16811da177e4SLinus Torvalds 16827ef8f65dSNikolay Aleksandrov /* Encapsulate a packet by attaching a valid IPIP header to it. 16831da177e4SLinus Torvalds * This avoids tunnel drivers and other mess and gives us the speed so 16841da177e4SLinus Torvalds * important for multicast video. 16851da177e4SLinus Torvalds */ 1686b6a7719aSHannes Frederic Sowa static void ip_encap(struct net *net, struct sk_buff *skb, 1687b6a7719aSHannes Frederic Sowa __be32 saddr, __be32 daddr) 16881da177e4SLinus Torvalds { 16898856dfa3SArnaldo Carvalho de Melo struct iphdr *iph; 1690b71d1d42SEric Dumazet const struct iphdr *old_iph = ip_hdr(skb); 16918856dfa3SArnaldo Carvalho de Melo 16928856dfa3SArnaldo Carvalho de Melo skb_push(skb, sizeof(struct iphdr)); 1693b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 16948856dfa3SArnaldo Carvalho de Melo skb_reset_network_header(skb); 1695eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 16961da177e4SLinus Torvalds 16971da177e4SLinus Torvalds iph->version = 4; 1698e023dd64SArnaldo Carvalho de Melo iph->tos = old_iph->tos; 1699e023dd64SArnaldo Carvalho de Melo iph->ttl = old_iph->ttl; 17001da177e4SLinus Torvalds iph->frag_off = 0; 17011da177e4SLinus Torvalds iph->daddr = daddr; 17021da177e4SLinus Torvalds iph->saddr = saddr; 17031da177e4SLinus Torvalds iph->protocol = IPPROTO_IPIP; 17041da177e4SLinus Torvalds iph->ihl = 5; 17051da177e4SLinus Torvalds iph->tot_len = htons(skb->len); 1706b6a7719aSHannes Frederic Sowa ip_select_ident(net, skb, NULL); 17071da177e4SLinus Torvalds ip_send_check(iph); 17081da177e4SLinus Torvalds 17091da177e4SLinus Torvalds memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 17101da177e4SLinus Torvalds nf_reset(skb); 17111da177e4SLinus Torvalds } 17121da177e4SLinus Torvalds 17130c4b51f0SEric W. Biederman static inline int ipmr_forward_finish(struct net *net, struct sock *sk, 17140c4b51f0SEric W. Biederman struct sk_buff *skb) 17151da177e4SLinus Torvalds { 17161da177e4SLinus Torvalds struct ip_options *opt = &(IPCB(skb)->opt); 17171da177e4SLinus Torvalds 171873186df8SDavid S. Miller IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS); 171973186df8SDavid S. Miller IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len); 17201da177e4SLinus Torvalds 17211da177e4SLinus Torvalds if (unlikely(opt->optlen)) 17221da177e4SLinus Torvalds ip_forward_options(skb); 17231da177e4SLinus Torvalds 172413206b6bSEric W. Biederman return dst_output(net, sk, skb); 17251da177e4SLinus Torvalds } 17261da177e4SLinus Torvalds 17277ef8f65dSNikolay Aleksandrov /* Processing handlers for ipmr_forward */ 17281da177e4SLinus Torvalds 17290c12295aSPatrick McHardy static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, 17300c12295aSPatrick McHardy struct sk_buff *skb, struct mfc_cache *c, int vifi) 17311da177e4SLinus Torvalds { 1732eddc9ec5SArnaldo Carvalho de Melo const struct iphdr *iph = ip_hdr(skb); 17330c12295aSPatrick McHardy struct vif_device *vif = &mrt->vif_table[vifi]; 17341da177e4SLinus Torvalds struct net_device *dev; 17351da177e4SLinus Torvalds struct rtable *rt; 173631e4543dSDavid S. Miller struct flowi4 fl4; 17371da177e4SLinus Torvalds int encap = 0; 17381da177e4SLinus Torvalds 173951456b29SIan Morris if (!vif->dev) 17401da177e4SLinus Torvalds goto out_free; 17411da177e4SLinus Torvalds 17421da177e4SLinus Torvalds if (vif->flags & VIFF_REGISTER) { 17431da177e4SLinus Torvalds vif->pkt_out++; 17441da177e4SLinus Torvalds vif->bytes_out += skb->len; 1745cf3677aeSPavel Emelyanov vif->dev->stats.tx_bytes += skb->len; 1746cf3677aeSPavel Emelyanov vif->dev->stats.tx_packets++; 17470c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT); 174869ebbf58SIlpo Järvinen goto out_free; 17491da177e4SLinus Torvalds } 17501da177e4SLinus Torvalds 17511da177e4SLinus Torvalds if (vif->flags & VIFF_TUNNEL) { 175231e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, 175378fbfd8aSDavid S. Miller vif->remote, vif->local, 175478fbfd8aSDavid S. Miller 0, 0, 175578fbfd8aSDavid S. Miller IPPROTO_IPIP, 175678fbfd8aSDavid S. Miller RT_TOS(iph->tos), vif->link); 1757b23dd4feSDavid S. Miller if (IS_ERR(rt)) 17581da177e4SLinus Torvalds goto out_free; 17591da177e4SLinus Torvalds encap = sizeof(struct iphdr); 17601da177e4SLinus Torvalds } else { 176131e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, iph->daddr, 0, 176278fbfd8aSDavid S. Miller 0, 0, 176378fbfd8aSDavid S. Miller IPPROTO_IPIP, 176478fbfd8aSDavid S. Miller RT_TOS(iph->tos), vif->link); 1765b23dd4feSDavid S. Miller if (IS_ERR(rt)) 17661da177e4SLinus Torvalds goto out_free; 17671da177e4SLinus Torvalds } 17681da177e4SLinus Torvalds 1769d8d1f30bSChangli Gao dev = rt->dst.dev; 17701da177e4SLinus Torvalds 1771d8d1f30bSChangli Gao if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) { 17721da177e4SLinus Torvalds /* Do not fragment multicasts. Alas, IPv4 does not 1773a8cb16ddSEric Dumazet * allow to send ICMP, so that packets will disappear 1774a8cb16ddSEric Dumazet * to blackhole. 17751da177e4SLinus Torvalds */ 177673186df8SDavid S. Miller IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); 17771da177e4SLinus Torvalds ip_rt_put(rt); 17781da177e4SLinus Torvalds goto out_free; 17791da177e4SLinus Torvalds } 17801da177e4SLinus Torvalds 1781d8d1f30bSChangli Gao encap += LL_RESERVED_SPACE(dev) + rt->dst.header_len; 17821da177e4SLinus Torvalds 17831da177e4SLinus Torvalds if (skb_cow(skb, encap)) { 17841da177e4SLinus Torvalds ip_rt_put(rt); 17851da177e4SLinus Torvalds goto out_free; 17861da177e4SLinus Torvalds } 17871da177e4SLinus Torvalds 17881da177e4SLinus Torvalds vif->pkt_out++; 17891da177e4SLinus Torvalds vif->bytes_out += skb->len; 17901da177e4SLinus Torvalds 1791adf30907SEric Dumazet skb_dst_drop(skb); 1792d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 1793eddc9ec5SArnaldo Carvalho de Melo ip_decrease_ttl(ip_hdr(skb)); 17941da177e4SLinus Torvalds 17951da177e4SLinus Torvalds /* FIXME: forward and output firewalls used to be called here. 1796a8cb16ddSEric Dumazet * What do we do with netfilter? -- RR 1797a8cb16ddSEric Dumazet */ 17981da177e4SLinus Torvalds if (vif->flags & VIFF_TUNNEL) { 1799b6a7719aSHannes Frederic Sowa ip_encap(net, skb, vif->local, vif->remote); 18001da177e4SLinus Torvalds /* FIXME: extra output firewall step used to be here. --RR */ 18012f4c02d4SPavel Emelyanov vif->dev->stats.tx_packets++; 18022f4c02d4SPavel Emelyanov vif->dev->stats.tx_bytes += skb->len; 18031da177e4SLinus Torvalds } 18041da177e4SLinus Torvalds 18059ee6c5dcSLance Richardson IPCB(skb)->flags |= IPSKB_FORWARDED; 18061da177e4SLinus Torvalds 18077ef8f65dSNikolay Aleksandrov /* RFC1584 teaches, that DVMRP/PIM router must deliver packets locally 18081da177e4SLinus Torvalds * not only before forwarding, but after forwarding on all output 18091da177e4SLinus Torvalds * interfaces. It is clear, if mrouter runs a multicasting 18101da177e4SLinus Torvalds * program, it should receive packets not depending to what interface 18111da177e4SLinus Torvalds * program is joined. 18121da177e4SLinus Torvalds * If we will not make it, the program will have to join on all 18131da177e4SLinus Torvalds * interfaces. On the other hand, multihoming host (or router, but 18141da177e4SLinus Torvalds * not mrouter) cannot join to more than one interface - it will 18151da177e4SLinus Torvalds * result in receiving multiple packets. 18161da177e4SLinus Torvalds */ 181729a26a56SEric W. Biederman NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, 181829a26a56SEric W. Biederman net, NULL, skb, skb->dev, dev, 18191da177e4SLinus Torvalds ipmr_forward_finish); 18201da177e4SLinus Torvalds return; 18211da177e4SLinus Torvalds 18221da177e4SLinus Torvalds out_free: 18231da177e4SLinus Torvalds kfree_skb(skb); 18241da177e4SLinus Torvalds } 18251da177e4SLinus Torvalds 18260c12295aSPatrick McHardy static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev) 18271da177e4SLinus Torvalds { 18281da177e4SLinus Torvalds int ct; 18290c12295aSPatrick McHardy 18300c12295aSPatrick McHardy for (ct = mrt->maxvif-1; ct >= 0; ct--) { 18310c12295aSPatrick McHardy if (mrt->vif_table[ct].dev == dev) 18321da177e4SLinus Torvalds break; 18331da177e4SLinus Torvalds } 18341da177e4SLinus Torvalds return ct; 18351da177e4SLinus Torvalds } 18361da177e4SLinus Torvalds 18371da177e4SLinus Torvalds /* "local" means that we should preserve one skb (for local delivery) */ 1838c4854ec8SRami Rosen static void ip_mr_forward(struct net *net, struct mr_table *mrt, 18394b1f0d33SDonald Sharp struct net_device *dev, struct sk_buff *skb, 18404b1f0d33SDonald Sharp struct mfc_cache *cache, int local) 18411da177e4SLinus Torvalds { 18424b1f0d33SDonald Sharp int true_vifi = ipmr_find_vif(mrt, dev); 18431da177e4SLinus Torvalds int psend = -1; 18441da177e4SLinus Torvalds int vif, ct; 18451da177e4SLinus Torvalds 18461da177e4SLinus Torvalds vif = cache->mfc_parent; 18471da177e4SLinus Torvalds cache->mfc_un.res.pkt++; 18481da177e4SLinus Torvalds cache->mfc_un.res.bytes += skb->len; 184943b9e127SNikolay Aleksandrov cache->mfc_un.res.lastuse = jiffies; 18501da177e4SLinus Torvalds 1851360eb5daSNicolas Dichtel if (cache->mfc_origin == htonl(INADDR_ANY) && true_vifi >= 0) { 1852660b26dcSNicolas Dichtel struct mfc_cache *cache_proxy; 1853660b26dcSNicolas Dichtel 1854660b26dcSNicolas Dichtel /* For an (*,G) entry, we only check that the incomming 1855660b26dcSNicolas Dichtel * interface is part of the static tree. 1856660b26dcSNicolas Dichtel */ 1857660b26dcSNicolas Dichtel cache_proxy = ipmr_cache_find_any_parent(mrt, vif); 1858660b26dcSNicolas Dichtel if (cache_proxy && 1859660b26dcSNicolas Dichtel cache_proxy->mfc_un.res.ttls[true_vifi] < 255) 1860660b26dcSNicolas Dichtel goto forward; 1861660b26dcSNicolas Dichtel } 1862660b26dcSNicolas Dichtel 18637ef8f65dSNikolay Aleksandrov /* Wrong interface: drop packet and (maybe) send PIM assert. */ 18644b1f0d33SDonald Sharp if (mrt->vif_table[vif].dev != dev) { 1865c7537967SDavid S. Miller if (rt_is_output_route(skb_rtable(skb))) { 18661da177e4SLinus Torvalds /* It is our own packet, looped back. 1867a8cb16ddSEric Dumazet * Very complicated situation... 1868a8cb16ddSEric Dumazet * 1869a8cb16ddSEric Dumazet * The best workaround until routing daemons will be 1870a8cb16ddSEric Dumazet * fixed is not to redistribute packet, if it was 1871a8cb16ddSEric Dumazet * send through wrong interface. It means, that 1872a8cb16ddSEric Dumazet * multicast applications WILL NOT work for 1873a8cb16ddSEric Dumazet * (S,G), which have default multicast route pointing 1874a8cb16ddSEric Dumazet * to wrong oif. In any case, it is not a good 1875a8cb16ddSEric Dumazet * idea to use multicasting applications on router. 18761da177e4SLinus Torvalds */ 18771da177e4SLinus Torvalds goto dont_forward; 18781da177e4SLinus Torvalds } 18791da177e4SLinus Torvalds 18801da177e4SLinus Torvalds cache->mfc_un.res.wrong_if++; 18811da177e4SLinus Torvalds 18820c12295aSPatrick McHardy if (true_vifi >= 0 && mrt->mroute_do_assert && 18831da177e4SLinus Torvalds /* pimsm uses asserts, when switching from RPT to SPT, 1884a8cb16ddSEric Dumazet * so that we cannot check that packet arrived on an oif. 1885a8cb16ddSEric Dumazet * It is bad, but otherwise we would need to move pretty 1886a8cb16ddSEric Dumazet * large chunk of pimd to kernel. Ough... --ANK 18871da177e4SLinus Torvalds */ 18880c12295aSPatrick McHardy (mrt->mroute_do_pim || 18896f9374a9SBenjamin Thery cache->mfc_un.res.ttls[true_vifi] < 255) && 18901da177e4SLinus Torvalds time_after(jiffies, 18911da177e4SLinus Torvalds cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) { 18921da177e4SLinus Torvalds cache->mfc_un.res.last_assert = jiffies; 18930c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF); 18941da177e4SLinus Torvalds } 18951da177e4SLinus Torvalds goto dont_forward; 18961da177e4SLinus Torvalds } 18971da177e4SLinus Torvalds 1898660b26dcSNicolas Dichtel forward: 18990c12295aSPatrick McHardy mrt->vif_table[vif].pkt_in++; 19000c12295aSPatrick McHardy mrt->vif_table[vif].bytes_in += skb->len; 19011da177e4SLinus Torvalds 19027ef8f65dSNikolay Aleksandrov /* Forward the frame */ 1903360eb5daSNicolas Dichtel if (cache->mfc_origin == htonl(INADDR_ANY) && 1904360eb5daSNicolas Dichtel cache->mfc_mcastgrp == htonl(INADDR_ANY)) { 1905660b26dcSNicolas Dichtel if (true_vifi >= 0 && 1906660b26dcSNicolas Dichtel true_vifi != cache->mfc_parent && 1907660b26dcSNicolas Dichtel ip_hdr(skb)->ttl > 1908660b26dcSNicolas Dichtel cache->mfc_un.res.ttls[cache->mfc_parent]) { 1909660b26dcSNicolas Dichtel /* It's an (*,*) entry and the packet is not coming from 1910660b26dcSNicolas Dichtel * the upstream: forward the packet to the upstream 1911660b26dcSNicolas Dichtel * only. 1912660b26dcSNicolas Dichtel */ 1913660b26dcSNicolas Dichtel psend = cache->mfc_parent; 1914660b26dcSNicolas Dichtel goto last_forward; 1915660b26dcSNicolas Dichtel } 1916660b26dcSNicolas Dichtel goto dont_forward; 1917660b26dcSNicolas Dichtel } 1918a8cb16ddSEric Dumazet for (ct = cache->mfc_un.res.maxvif - 1; 1919a8cb16ddSEric Dumazet ct >= cache->mfc_un.res.minvif; ct--) { 1920660b26dcSNicolas Dichtel /* For (*,G) entry, don't forward to the incoming interface */ 1921360eb5daSNicolas Dichtel if ((cache->mfc_origin != htonl(INADDR_ANY) || 1922360eb5daSNicolas Dichtel ct != true_vifi) && 1923660b26dcSNicolas Dichtel ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) { 19241da177e4SLinus Torvalds if (psend != -1) { 19251da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 1926a8cb16ddSEric Dumazet 19271da177e4SLinus Torvalds if (skb2) 19280c12295aSPatrick McHardy ipmr_queue_xmit(net, mrt, skb2, cache, 19290c12295aSPatrick McHardy psend); 19301da177e4SLinus Torvalds } 19311da177e4SLinus Torvalds psend = ct; 19321da177e4SLinus Torvalds } 19331da177e4SLinus Torvalds } 1934660b26dcSNicolas Dichtel last_forward: 19351da177e4SLinus Torvalds if (psend != -1) { 19361da177e4SLinus Torvalds if (local) { 19371da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 1938a8cb16ddSEric Dumazet 19391da177e4SLinus Torvalds if (skb2) 19400c12295aSPatrick McHardy ipmr_queue_xmit(net, mrt, skb2, cache, psend); 19411da177e4SLinus Torvalds } else { 19420c12295aSPatrick McHardy ipmr_queue_xmit(net, mrt, skb, cache, psend); 1943c4854ec8SRami Rosen return; 19441da177e4SLinus Torvalds } 19451da177e4SLinus Torvalds } 19461da177e4SLinus Torvalds 19471da177e4SLinus Torvalds dont_forward: 19481da177e4SLinus Torvalds if (!local) 19491da177e4SLinus Torvalds kfree_skb(skb); 19501da177e4SLinus Torvalds } 19511da177e4SLinus Torvalds 1952417da66fSDavid S. Miller static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb) 1953ee3f1aafSDavid S. Miller { 1954417da66fSDavid S. Miller struct rtable *rt = skb_rtable(skb); 1955417da66fSDavid S. Miller struct iphdr *iph = ip_hdr(skb); 1956da91981bSDavid S. Miller struct flowi4 fl4 = { 1957417da66fSDavid S. Miller .daddr = iph->daddr, 1958417da66fSDavid S. Miller .saddr = iph->saddr, 1959b0fe4a31SJulian Anastasov .flowi4_tos = RT_TOS(iph->tos), 19604fd551d7SDavid S. Miller .flowi4_oif = (rt_is_output_route(rt) ? 19614fd551d7SDavid S. Miller skb->dev->ifindex : 0), 19624fd551d7SDavid S. Miller .flowi4_iif = (rt_is_output_route(rt) ? 19631fb9489bSPavel Emelyanov LOOPBACK_IFINDEX : 19644fd551d7SDavid S. Miller skb->dev->ifindex), 1965b4869889SDavid Miller .flowi4_mark = skb->mark, 1966ee3f1aafSDavid S. Miller }; 1967ee3f1aafSDavid S. Miller struct mr_table *mrt; 1968ee3f1aafSDavid S. Miller int err; 1969ee3f1aafSDavid S. Miller 1970da91981bSDavid S. Miller err = ipmr_fib_lookup(net, &fl4, &mrt); 1971ee3f1aafSDavid S. Miller if (err) 1972ee3f1aafSDavid S. Miller return ERR_PTR(err); 1973ee3f1aafSDavid S. Miller return mrt; 1974ee3f1aafSDavid S. Miller } 19751da177e4SLinus Torvalds 19767ef8f65dSNikolay Aleksandrov /* Multicast packets for forwarding arrive here 19774c968709SEric Dumazet * Called with rcu_read_lock(); 19781da177e4SLinus Torvalds */ 19791da177e4SLinus Torvalds int ip_mr_input(struct sk_buff *skb) 19801da177e4SLinus Torvalds { 19811da177e4SLinus Torvalds struct mfc_cache *cache; 19824feb88e5SBenjamin Thery struct net *net = dev_net(skb->dev); 1983511c3f92SEric Dumazet int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL; 1984f0ad0860SPatrick McHardy struct mr_table *mrt; 1985bcfc7d33SThomas Winter struct net_device *dev; 1986bcfc7d33SThomas Winter 1987bcfc7d33SThomas Winter /* skb->dev passed in is the loX master dev for vrfs. 1988bcfc7d33SThomas Winter * As there are no vifs associated with loopback devices, 1989bcfc7d33SThomas Winter * get the proper interface that does have a vif associated with it. 1990bcfc7d33SThomas Winter */ 1991bcfc7d33SThomas Winter dev = skb->dev; 1992bcfc7d33SThomas Winter if (netif_is_l3_master(skb->dev)) { 1993bcfc7d33SThomas Winter dev = dev_get_by_index_rcu(net, IPCB(skb)->iif); 1994bcfc7d33SThomas Winter if (!dev) { 1995bcfc7d33SThomas Winter kfree_skb(skb); 1996bcfc7d33SThomas Winter return -ENODEV; 1997bcfc7d33SThomas Winter } 1998bcfc7d33SThomas Winter } 19991da177e4SLinus Torvalds 20001da177e4SLinus Torvalds /* Packet is looped back after forward, it should not be 2001a8cb16ddSEric Dumazet * forwarded second time, but still can be delivered locally. 20021da177e4SLinus Torvalds */ 20031da177e4SLinus Torvalds if (IPCB(skb)->flags & IPSKB_FORWARDED) 20041da177e4SLinus Torvalds goto dont_forward; 20051da177e4SLinus Torvalds 2006417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2007ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) { 2008e40dbc51SBen Greear kfree_skb(skb); 2009ee3f1aafSDavid S. Miller return PTR_ERR(mrt); 20105e2b61f7SDavid S. Miller } 20111da177e4SLinus Torvalds if (!local) { 20121da177e4SLinus Torvalds if (IPCB(skb)->opt.router_alert) { 20131da177e4SLinus Torvalds if (ip_call_ra_chain(skb)) 20141da177e4SLinus Torvalds return 0; 2015eddc9ec5SArnaldo Carvalho de Melo } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP) { 20161da177e4SLinus Torvalds /* IGMPv1 (and broken IGMPv2 implementations sort of 20174c968709SEric Dumazet * Cisco IOS <= 11.2(8)) do not put router alert 20184c968709SEric Dumazet * option to IGMP packets destined to routable 20194c968709SEric Dumazet * groups. It is very bad, because it means 20204c968709SEric Dumazet * that we can forward NO IGMP messages. 20211da177e4SLinus Torvalds */ 20224c968709SEric Dumazet struct sock *mroute_sk; 20234c968709SEric Dumazet 20244c968709SEric Dumazet mroute_sk = rcu_dereference(mrt->mroute_sk); 20254c968709SEric Dumazet if (mroute_sk) { 20262715bcf9SPatrick McHardy nf_reset(skb); 20274c968709SEric Dumazet raw_rcv(mroute_sk, skb); 20281da177e4SLinus Torvalds return 0; 20291da177e4SLinus Torvalds } 20301da177e4SLinus Torvalds } 20311da177e4SLinus Torvalds } 20321da177e4SLinus Torvalds 2033a8c9486bSEric Dumazet /* already under rcu_read_lock() */ 20340c12295aSPatrick McHardy cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); 203551456b29SIan Morris if (!cache) { 2036bcfc7d33SThomas Winter int vif = ipmr_find_vif(mrt, dev); 2037660b26dcSNicolas Dichtel 2038660b26dcSNicolas Dichtel if (vif >= 0) 2039660b26dcSNicolas Dichtel cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr, 2040660b26dcSNicolas Dichtel vif); 2041660b26dcSNicolas Dichtel } 20421da177e4SLinus Torvalds 20437ef8f65dSNikolay Aleksandrov /* No usable cache entry */ 204451456b29SIan Morris if (!cache) { 20451da177e4SLinus Torvalds int vif; 20461da177e4SLinus Torvalds 20471da177e4SLinus Torvalds if (local) { 20481da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 20491da177e4SLinus Torvalds ip_local_deliver(skb); 205051456b29SIan Morris if (!skb2) 20511da177e4SLinus Torvalds return -ENOBUFS; 20521da177e4SLinus Torvalds skb = skb2; 20531da177e4SLinus Torvalds } 20541da177e4SLinus Torvalds 2055a8c9486bSEric Dumazet read_lock(&mrt_lock); 2056bcfc7d33SThomas Winter vif = ipmr_find_vif(mrt, dev); 20571da177e4SLinus Torvalds if (vif >= 0) { 20584b1f0d33SDonald Sharp int err2 = ipmr_cache_unresolved(mrt, vif, skb, dev); 20591da177e4SLinus Torvalds read_unlock(&mrt_lock); 20601da177e4SLinus Torvalds 20610eae88f3SEric Dumazet return err2; 20621da177e4SLinus Torvalds } 20631da177e4SLinus Torvalds read_unlock(&mrt_lock); 20641da177e4SLinus Torvalds kfree_skb(skb); 20651da177e4SLinus Torvalds return -ENODEV; 20661da177e4SLinus Torvalds } 20671da177e4SLinus Torvalds 2068a8c9486bSEric Dumazet read_lock(&mrt_lock); 20694b1f0d33SDonald Sharp ip_mr_forward(net, mrt, dev, skb, cache, local); 20701da177e4SLinus Torvalds read_unlock(&mrt_lock); 20711da177e4SLinus Torvalds 20721da177e4SLinus Torvalds if (local) 20731da177e4SLinus Torvalds return ip_local_deliver(skb); 20741da177e4SLinus Torvalds 20751da177e4SLinus Torvalds return 0; 20761da177e4SLinus Torvalds 20771da177e4SLinus Torvalds dont_forward: 20781da177e4SLinus Torvalds if (local) 20791da177e4SLinus Torvalds return ip_local_deliver(skb); 20801da177e4SLinus Torvalds kfree_skb(skb); 20811da177e4SLinus Torvalds return 0; 20821da177e4SLinus Torvalds } 20831da177e4SLinus Torvalds 2084b1879204SIlpo Järvinen #ifdef CONFIG_IP_PIMSM_V1 20857ef8f65dSNikolay Aleksandrov /* Handle IGMP messages of PIMv1 */ 2086b1879204SIlpo Järvinen int pim_rcv_v1(struct sk_buff *skb) 2087b1879204SIlpo Järvinen { 2088b1879204SIlpo Järvinen struct igmphdr *pim; 20894feb88e5SBenjamin Thery struct net *net = dev_net(skb->dev); 2090f0ad0860SPatrick McHardy struct mr_table *mrt; 2091b1879204SIlpo Järvinen 2092b1879204SIlpo Järvinen if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 2093b1879204SIlpo Järvinen goto drop; 2094b1879204SIlpo Järvinen 2095b1879204SIlpo Järvinen pim = igmp_hdr(skb); 2096b1879204SIlpo Järvinen 2097417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2098ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) 2099f0ad0860SPatrick McHardy goto drop; 21000c12295aSPatrick McHardy if (!mrt->mroute_do_pim || 2101b1879204SIlpo Järvinen pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) 2102b1879204SIlpo Järvinen goto drop; 2103b1879204SIlpo Järvinen 2104f0ad0860SPatrick McHardy if (__pim_rcv(mrt, skb, sizeof(*pim))) { 21051da177e4SLinus Torvalds drop: 21061da177e4SLinus Torvalds kfree_skb(skb); 2107b1879204SIlpo Järvinen } 21081da177e4SLinus Torvalds return 0; 21091da177e4SLinus Torvalds } 21101da177e4SLinus Torvalds #endif 21111da177e4SLinus Torvalds 21121da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V2 21131da177e4SLinus Torvalds static int pim_rcv(struct sk_buff *skb) 21141da177e4SLinus Torvalds { 21151da177e4SLinus Torvalds struct pimreghdr *pim; 2116f0ad0860SPatrick McHardy struct net *net = dev_net(skb->dev); 2117f0ad0860SPatrick McHardy struct mr_table *mrt; 21181da177e4SLinus Torvalds 2119b1879204SIlpo Järvinen if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 21201da177e4SLinus Torvalds goto drop; 21211da177e4SLinus Torvalds 21229c70220bSArnaldo Carvalho de Melo pim = (struct pimreghdr *)skb_transport_header(skb); 212356245caeSNikolay Aleksandrov if (pim->type != ((PIM_VERSION << 4) | (PIM_TYPE_REGISTER)) || 21241da177e4SLinus Torvalds (pim->flags & PIM_NULL_REGISTER) || 21251da177e4SLinus Torvalds (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && 2126d3bc23e7SAl Viro csum_fold(skb_checksum(skb, 0, skb->len, 0)))) 21271da177e4SLinus Torvalds goto drop; 21281da177e4SLinus Torvalds 2129417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2130ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) 2131f0ad0860SPatrick McHardy goto drop; 2132f0ad0860SPatrick McHardy if (__pim_rcv(mrt, skb, sizeof(*pim))) { 21331da177e4SLinus Torvalds drop: 21341da177e4SLinus Torvalds kfree_skb(skb); 2135b1879204SIlpo Järvinen } 21361da177e4SLinus Torvalds return 0; 21371da177e4SLinus Torvalds } 21381da177e4SLinus Torvalds #endif 21391da177e4SLinus Torvalds 2140cb6a4e46SPatrick McHardy static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 2141cb6a4e46SPatrick McHardy struct mfc_cache *c, struct rtmsg *rtm) 21421da177e4SLinus Torvalds { 2143adfa85e4SNicolas Dichtel struct rta_mfc_stats mfcs; 214443b9e127SNikolay Aleksandrov struct nlattr *mp_attr; 214543b9e127SNikolay Aleksandrov struct rtnexthop *nhp; 2146b5036cd4SNikolay Aleksandrov unsigned long lastuse; 214743b9e127SNikolay Aleksandrov int ct; 21481da177e4SLinus Torvalds 21497438189bSNicolas Dichtel /* If cache is unresolved, don't try to parse IIF and OIF */ 21501708ebc9SNikolay Aleksandrov if (c->mfc_parent >= MAXVIFS) { 21511708ebc9SNikolay Aleksandrov rtm->rtm_flags |= RTNH_F_UNRESOLVED; 21527438189bSNicolas Dichtel return -ENOENT; 21531708ebc9SNikolay Aleksandrov } 21547438189bSNicolas Dichtel 215592a395e5SThomas Graf if (VIF_EXISTS(mrt, c->mfc_parent) && 215692a395e5SThomas Graf nla_put_u32(skb, RTA_IIF, mrt->vif_table[c->mfc_parent].dev->ifindex) < 0) 215792a395e5SThomas Graf return -EMSGSIZE; 21581da177e4SLinus Torvalds 215992a395e5SThomas Graf if (!(mp_attr = nla_nest_start(skb, RTA_MULTIPATH))) 216092a395e5SThomas Graf return -EMSGSIZE; 21611da177e4SLinus Torvalds 21621da177e4SLinus Torvalds for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) { 21630c12295aSPatrick McHardy if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) { 216492a395e5SThomas Graf if (!(nhp = nla_reserve_nohdr(skb, sizeof(*nhp)))) { 216592a395e5SThomas Graf nla_nest_cancel(skb, mp_attr); 216692a395e5SThomas Graf return -EMSGSIZE; 216792a395e5SThomas Graf } 216892a395e5SThomas Graf 21691da177e4SLinus Torvalds nhp->rtnh_flags = 0; 21701da177e4SLinus Torvalds nhp->rtnh_hops = c->mfc_un.res.ttls[ct]; 21710c12295aSPatrick McHardy nhp->rtnh_ifindex = mrt->vif_table[ct].dev->ifindex; 21721da177e4SLinus Torvalds nhp->rtnh_len = sizeof(*nhp); 21731da177e4SLinus Torvalds } 21741da177e4SLinus Torvalds } 217592a395e5SThomas Graf 217692a395e5SThomas Graf nla_nest_end(skb, mp_attr); 217792a395e5SThomas Graf 2178b5036cd4SNikolay Aleksandrov lastuse = READ_ONCE(c->mfc_un.res.lastuse); 2179b5036cd4SNikolay Aleksandrov lastuse = time_after_eq(jiffies, lastuse) ? jiffies - lastuse : 0; 2180b5036cd4SNikolay Aleksandrov 2181adfa85e4SNicolas Dichtel mfcs.mfcs_packets = c->mfc_un.res.pkt; 2182adfa85e4SNicolas Dichtel mfcs.mfcs_bytes = c->mfc_un.res.bytes; 2183adfa85e4SNicolas Dichtel mfcs.mfcs_wrong_if = c->mfc_un.res.wrong_if; 218443b9e127SNikolay Aleksandrov if (nla_put_64bit(skb, RTA_MFC_STATS, sizeof(mfcs), &mfcs, RTA_PAD) || 2185b5036cd4SNikolay Aleksandrov nla_put_u64_64bit(skb, RTA_EXPIRES, jiffies_to_clock_t(lastuse), 218643b9e127SNikolay Aleksandrov RTA_PAD)) 2187adfa85e4SNicolas Dichtel return -EMSGSIZE; 2188adfa85e4SNicolas Dichtel 21891da177e4SLinus Torvalds rtm->rtm_type = RTN_MULTICAST; 21901da177e4SLinus Torvalds return 1; 21911da177e4SLinus Torvalds } 21921da177e4SLinus Torvalds 21939a1b9496SDavid S. Miller int ipmr_get_route(struct net *net, struct sk_buff *skb, 21949a1b9496SDavid S. Miller __be32 saddr, __be32 daddr, 21959f09eaeaSDavid Ahern struct rtmsg *rtm, u32 portid) 21961da177e4SLinus Torvalds { 21971da177e4SLinus Torvalds struct mfc_cache *cache; 21989a1b9496SDavid S. Miller struct mr_table *mrt; 21999a1b9496SDavid S. Miller int err; 22001da177e4SLinus Torvalds 2201f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 220251456b29SIan Morris if (!mrt) 2203f0ad0860SPatrick McHardy return -ENOENT; 2204f0ad0860SPatrick McHardy 2205a8c9486bSEric Dumazet rcu_read_lock(); 22069a1b9496SDavid S. Miller cache = ipmr_cache_find(mrt, saddr, daddr); 220751456b29SIan Morris if (!cache && skb->dev) { 2208660b26dcSNicolas Dichtel int vif = ipmr_find_vif(mrt, skb->dev); 22091da177e4SLinus Torvalds 2210660b26dcSNicolas Dichtel if (vif >= 0) 2211660b26dcSNicolas Dichtel cache = ipmr_cache_find_any(mrt, daddr, vif); 2212660b26dcSNicolas Dichtel } 221351456b29SIan Morris if (!cache) { 221472287490SAlexey Kuznetsov struct sk_buff *skb2; 2215eddc9ec5SArnaldo Carvalho de Melo struct iphdr *iph; 22161da177e4SLinus Torvalds struct net_device *dev; 2217a8cb16ddSEric Dumazet int vif = -1; 22181da177e4SLinus Torvalds 22191da177e4SLinus Torvalds dev = skb->dev; 2220a8c9486bSEric Dumazet read_lock(&mrt_lock); 2221a8cb16ddSEric Dumazet if (dev) 2222a8cb16ddSEric Dumazet vif = ipmr_find_vif(mrt, dev); 2223a8cb16ddSEric Dumazet if (vif < 0) { 22241da177e4SLinus Torvalds read_unlock(&mrt_lock); 2225a8c9486bSEric Dumazet rcu_read_unlock(); 22261da177e4SLinus Torvalds return -ENODEV; 22271da177e4SLinus Torvalds } 222872287490SAlexey Kuznetsov skb2 = skb_clone(skb, GFP_ATOMIC); 222972287490SAlexey Kuznetsov if (!skb2) { 223072287490SAlexey Kuznetsov read_unlock(&mrt_lock); 2231a8c9486bSEric Dumazet rcu_read_unlock(); 223272287490SAlexey Kuznetsov return -ENOMEM; 223372287490SAlexey Kuznetsov } 223472287490SAlexey Kuznetsov 22352cf75070SNikolay Aleksandrov NETLINK_CB(skb2).portid = portid; 2236e2d1bca7SArnaldo Carvalho de Melo skb_push(skb2, sizeof(struct iphdr)); 2237e2d1bca7SArnaldo Carvalho de Melo skb_reset_network_header(skb2); 2238eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb2); 2239eddc9ec5SArnaldo Carvalho de Melo iph->ihl = sizeof(struct iphdr) >> 2; 22409a1b9496SDavid S. Miller iph->saddr = saddr; 22419a1b9496SDavid S. Miller iph->daddr = daddr; 2242eddc9ec5SArnaldo Carvalho de Melo iph->version = 0; 22434b1f0d33SDonald Sharp err = ipmr_cache_unresolved(mrt, vif, skb2, dev); 22441da177e4SLinus Torvalds read_unlock(&mrt_lock); 2245a8c9486bSEric Dumazet rcu_read_unlock(); 22461da177e4SLinus Torvalds return err; 22471da177e4SLinus Torvalds } 22481da177e4SLinus Torvalds 2249a8c9486bSEric Dumazet read_lock(&mrt_lock); 2250cb6a4e46SPatrick McHardy err = __ipmr_fill_mroute(mrt, skb, cache, rtm); 22511da177e4SLinus Torvalds read_unlock(&mrt_lock); 2252a8c9486bSEric Dumazet rcu_read_unlock(); 22531da177e4SLinus Torvalds return err; 22541da177e4SLinus Torvalds } 22551da177e4SLinus Torvalds 2256cb6a4e46SPatrick McHardy static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 225765886f43SNicolas Dichtel u32 portid, u32 seq, struct mfc_cache *c, int cmd, 225865886f43SNicolas Dichtel int flags) 2259cb6a4e46SPatrick McHardy { 2260cb6a4e46SPatrick McHardy struct nlmsghdr *nlh; 2261cb6a4e46SPatrick McHardy struct rtmsg *rtm; 22621eb99af5SNicolas Dichtel int err; 2263cb6a4e46SPatrick McHardy 226465886f43SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags); 226551456b29SIan Morris if (!nlh) 2266cb6a4e46SPatrick McHardy return -EMSGSIZE; 2267cb6a4e46SPatrick McHardy 2268cb6a4e46SPatrick McHardy rtm = nlmsg_data(nlh); 2269cb6a4e46SPatrick McHardy rtm->rtm_family = RTNL_FAMILY_IPMR; 2270cb6a4e46SPatrick McHardy rtm->rtm_dst_len = 32; 2271cb6a4e46SPatrick McHardy rtm->rtm_src_len = 32; 2272cb6a4e46SPatrick McHardy rtm->rtm_tos = 0; 2273cb6a4e46SPatrick McHardy rtm->rtm_table = mrt->id; 2274f3756b79SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, mrt->id)) 2275f3756b79SDavid S. Miller goto nla_put_failure; 2276cb6a4e46SPatrick McHardy rtm->rtm_type = RTN_MULTICAST; 2277cb6a4e46SPatrick McHardy rtm->rtm_scope = RT_SCOPE_UNIVERSE; 22789a68ac72SNicolas Dichtel if (c->mfc_flags & MFC_STATIC) 22799a68ac72SNicolas Dichtel rtm->rtm_protocol = RTPROT_STATIC; 22809a68ac72SNicolas Dichtel else 22819a68ac72SNicolas Dichtel rtm->rtm_protocol = RTPROT_MROUTED; 2282cb6a4e46SPatrick McHardy rtm->rtm_flags = 0; 2283cb6a4e46SPatrick McHardy 2284930345eaSJiri Benc if (nla_put_in_addr(skb, RTA_SRC, c->mfc_origin) || 2285930345eaSJiri Benc nla_put_in_addr(skb, RTA_DST, c->mfc_mcastgrp)) 2286f3756b79SDavid S. Miller goto nla_put_failure; 22871eb99af5SNicolas Dichtel err = __ipmr_fill_mroute(mrt, skb, c, rtm); 22881eb99af5SNicolas Dichtel /* do not break the dump if cache is unresolved */ 22891eb99af5SNicolas Dichtel if (err < 0 && err != -ENOENT) 2290cb6a4e46SPatrick McHardy goto nla_put_failure; 2291cb6a4e46SPatrick McHardy 2292053c095aSJohannes Berg nlmsg_end(skb, nlh); 2293053c095aSJohannes Berg return 0; 2294cb6a4e46SPatrick McHardy 2295cb6a4e46SPatrick McHardy nla_put_failure: 2296cb6a4e46SPatrick McHardy nlmsg_cancel(skb, nlh); 2297cb6a4e46SPatrick McHardy return -EMSGSIZE; 2298cb6a4e46SPatrick McHardy } 2299cb6a4e46SPatrick McHardy 23008cd3ac9fSNicolas Dichtel static size_t mroute_msgsize(bool unresolved, int maxvif) 23018cd3ac9fSNicolas Dichtel { 23028cd3ac9fSNicolas Dichtel size_t len = 23038cd3ac9fSNicolas Dichtel NLMSG_ALIGN(sizeof(struct rtmsg)) 23048cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_TABLE */ 23058cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_SRC */ 23068cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_DST */ 23078cd3ac9fSNicolas Dichtel ; 23088cd3ac9fSNicolas Dichtel 23098cd3ac9fSNicolas Dichtel if (!unresolved) 23108cd3ac9fSNicolas Dichtel len = len 23118cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_IIF */ 23128cd3ac9fSNicolas Dichtel + nla_total_size(0) /* RTA_MULTIPATH */ 23138cd3ac9fSNicolas Dichtel + maxvif * NLA_ALIGN(sizeof(struct rtnexthop)) 23148cd3ac9fSNicolas Dichtel /* RTA_MFC_STATS */ 2315a9a08042SNicolas Dichtel + nla_total_size_64bit(sizeof(struct rta_mfc_stats)) 23168cd3ac9fSNicolas Dichtel ; 23178cd3ac9fSNicolas Dichtel 23188cd3ac9fSNicolas Dichtel return len; 23198cd3ac9fSNicolas Dichtel } 23208cd3ac9fSNicolas Dichtel 23218cd3ac9fSNicolas Dichtel static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, 23228cd3ac9fSNicolas Dichtel int cmd) 23238cd3ac9fSNicolas Dichtel { 23248cd3ac9fSNicolas Dichtel struct net *net = read_pnet(&mrt->net); 23258cd3ac9fSNicolas Dichtel struct sk_buff *skb; 23268cd3ac9fSNicolas Dichtel int err = -ENOBUFS; 23278cd3ac9fSNicolas Dichtel 23288cd3ac9fSNicolas Dichtel skb = nlmsg_new(mroute_msgsize(mfc->mfc_parent >= MAXVIFS, mrt->maxvif), 23298cd3ac9fSNicolas Dichtel GFP_ATOMIC); 233051456b29SIan Morris if (!skb) 23318cd3ac9fSNicolas Dichtel goto errout; 23328cd3ac9fSNicolas Dichtel 233365886f43SNicolas Dichtel err = ipmr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0); 23348cd3ac9fSNicolas Dichtel if (err < 0) 23358cd3ac9fSNicolas Dichtel goto errout; 23368cd3ac9fSNicolas Dichtel 23378cd3ac9fSNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE, NULL, GFP_ATOMIC); 23388cd3ac9fSNicolas Dichtel return; 23398cd3ac9fSNicolas Dichtel 23408cd3ac9fSNicolas Dichtel errout: 23418cd3ac9fSNicolas Dichtel kfree_skb(skb); 23428cd3ac9fSNicolas Dichtel if (err < 0) 23438cd3ac9fSNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE, err); 23448cd3ac9fSNicolas Dichtel } 23458cd3ac9fSNicolas Dichtel 23465a645dd8SJulien Gomes static size_t igmpmsg_netlink_msgsize(size_t payloadlen) 23475a645dd8SJulien Gomes { 23485a645dd8SJulien Gomes size_t len = 23495a645dd8SJulien Gomes NLMSG_ALIGN(sizeof(struct rtgenmsg)) 23505a645dd8SJulien Gomes + nla_total_size(1) /* IPMRA_CREPORT_MSGTYPE */ 23515a645dd8SJulien Gomes + nla_total_size(4) /* IPMRA_CREPORT_VIF_ID */ 23525a645dd8SJulien Gomes + nla_total_size(4) /* IPMRA_CREPORT_SRC_ADDR */ 23535a645dd8SJulien Gomes + nla_total_size(4) /* IPMRA_CREPORT_DST_ADDR */ 23545a645dd8SJulien Gomes /* IPMRA_CREPORT_PKT */ 23555a645dd8SJulien Gomes + nla_total_size(payloadlen) 23565a645dd8SJulien Gomes ; 23575a645dd8SJulien Gomes 23585a645dd8SJulien Gomes return len; 23595a645dd8SJulien Gomes } 23605a645dd8SJulien Gomes 23615a645dd8SJulien Gomes static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt) 23625a645dd8SJulien Gomes { 23635a645dd8SJulien Gomes struct net *net = read_pnet(&mrt->net); 23645a645dd8SJulien Gomes struct nlmsghdr *nlh; 23655a645dd8SJulien Gomes struct rtgenmsg *rtgenm; 23665a645dd8SJulien Gomes struct igmpmsg *msg; 23675a645dd8SJulien Gomes struct sk_buff *skb; 23685a645dd8SJulien Gomes struct nlattr *nla; 23695a645dd8SJulien Gomes int payloadlen; 23705a645dd8SJulien Gomes 23715a645dd8SJulien Gomes payloadlen = pkt->len - sizeof(struct igmpmsg); 23725a645dd8SJulien Gomes msg = (struct igmpmsg *)skb_network_header(pkt); 23735a645dd8SJulien Gomes 23745a645dd8SJulien Gomes skb = nlmsg_new(igmpmsg_netlink_msgsize(payloadlen), GFP_ATOMIC); 23755a645dd8SJulien Gomes if (!skb) 23765a645dd8SJulien Gomes goto errout; 23775a645dd8SJulien Gomes 23785a645dd8SJulien Gomes nlh = nlmsg_put(skb, 0, 0, RTM_NEWCACHEREPORT, 23795a645dd8SJulien Gomes sizeof(struct rtgenmsg), 0); 23805a645dd8SJulien Gomes if (!nlh) 23815a645dd8SJulien Gomes goto errout; 23825a645dd8SJulien Gomes rtgenm = nlmsg_data(nlh); 23835a645dd8SJulien Gomes rtgenm->rtgen_family = RTNL_FAMILY_IPMR; 23845a645dd8SJulien Gomes if (nla_put_u8(skb, IPMRA_CREPORT_MSGTYPE, msg->im_msgtype) || 23855a645dd8SJulien Gomes nla_put_u32(skb, IPMRA_CREPORT_VIF_ID, msg->im_vif) || 23865a645dd8SJulien Gomes nla_put_in_addr(skb, IPMRA_CREPORT_SRC_ADDR, 23875a645dd8SJulien Gomes msg->im_src.s_addr) || 23885a645dd8SJulien Gomes nla_put_in_addr(skb, IPMRA_CREPORT_DST_ADDR, 23895a645dd8SJulien Gomes msg->im_dst.s_addr)) 23905a645dd8SJulien Gomes goto nla_put_failure; 23915a645dd8SJulien Gomes 23925a645dd8SJulien Gomes nla = nla_reserve(skb, IPMRA_CREPORT_PKT, payloadlen); 23935a645dd8SJulien Gomes if (!nla || skb_copy_bits(pkt, sizeof(struct igmpmsg), 23945a645dd8SJulien Gomes nla_data(nla), payloadlen)) 23955a645dd8SJulien Gomes goto nla_put_failure; 23965a645dd8SJulien Gomes 23975a645dd8SJulien Gomes nlmsg_end(skb, nlh); 23985a645dd8SJulien Gomes 23995a645dd8SJulien Gomes rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE_R, NULL, GFP_ATOMIC); 24005a645dd8SJulien Gomes return; 24015a645dd8SJulien Gomes 24025a645dd8SJulien Gomes nla_put_failure: 24035a645dd8SJulien Gomes nlmsg_cancel(skb, nlh); 24045a645dd8SJulien Gomes errout: 24055a645dd8SJulien Gomes kfree_skb(skb); 24065a645dd8SJulien Gomes rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE_R, -ENOBUFS); 24075a645dd8SJulien Gomes } 24085a645dd8SJulien Gomes 24094f75ba69SDonald Sharp static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, 24104f75ba69SDonald Sharp struct netlink_ext_ack *extack) 24114f75ba69SDonald Sharp { 24124f75ba69SDonald Sharp struct net *net = sock_net(in_skb->sk); 24134f75ba69SDonald Sharp struct nlattr *tb[RTA_MAX + 1]; 24144f75ba69SDonald Sharp struct sk_buff *skb = NULL; 24154f75ba69SDonald Sharp struct mfc_cache *cache; 24164f75ba69SDonald Sharp struct mr_table *mrt; 24174f75ba69SDonald Sharp struct rtmsg *rtm; 24184f75ba69SDonald Sharp __be32 src, grp; 24194f75ba69SDonald Sharp u32 tableid; 24204f75ba69SDonald Sharp int err; 24214f75ba69SDonald Sharp 24224f75ba69SDonald Sharp err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, 24234f75ba69SDonald Sharp rtm_ipv4_policy, extack); 24244f75ba69SDonald Sharp if (err < 0) 24254f75ba69SDonald Sharp goto errout; 24264f75ba69SDonald Sharp 24274f75ba69SDonald Sharp rtm = nlmsg_data(nlh); 24284f75ba69SDonald Sharp 24294f75ba69SDonald Sharp src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0; 24304f75ba69SDonald Sharp grp = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0; 24314f75ba69SDonald Sharp tableid = tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : 0; 24324f75ba69SDonald Sharp 24334f75ba69SDonald Sharp mrt = ipmr_get_table(net, tableid ? tableid : RT_TABLE_DEFAULT); 2434*2e3d232eSDan Carpenter if (!mrt) { 2435*2e3d232eSDan Carpenter err = -ENOENT; 24364f75ba69SDonald Sharp goto errout_free; 24374f75ba69SDonald Sharp } 24384f75ba69SDonald Sharp 24394f75ba69SDonald Sharp /* entries are added/deleted only under RTNL */ 24404f75ba69SDonald Sharp rcu_read_lock(); 24414f75ba69SDonald Sharp cache = ipmr_cache_find(mrt, src, grp); 24424f75ba69SDonald Sharp rcu_read_unlock(); 24434f75ba69SDonald Sharp if (!cache) { 24444f75ba69SDonald Sharp err = -ENOENT; 24454f75ba69SDonald Sharp goto errout_free; 24464f75ba69SDonald Sharp } 24474f75ba69SDonald Sharp 24484f75ba69SDonald Sharp skb = nlmsg_new(mroute_msgsize(false, mrt->maxvif), GFP_KERNEL); 24494f75ba69SDonald Sharp if (!skb) { 24504f75ba69SDonald Sharp err = -ENOBUFS; 24514f75ba69SDonald Sharp goto errout_free; 24524f75ba69SDonald Sharp } 24534f75ba69SDonald Sharp 24544f75ba69SDonald Sharp err = ipmr_fill_mroute(mrt, skb, NETLINK_CB(in_skb).portid, 24554f75ba69SDonald Sharp nlh->nlmsg_seq, cache, 24564f75ba69SDonald Sharp RTM_NEWROUTE, 0); 24574f75ba69SDonald Sharp if (err < 0) 24584f75ba69SDonald Sharp goto errout_free; 24594f75ba69SDonald Sharp 24604f75ba69SDonald Sharp err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 24614f75ba69SDonald Sharp 24624f75ba69SDonald Sharp errout: 24634f75ba69SDonald Sharp return err; 24644f75ba69SDonald Sharp 24654f75ba69SDonald Sharp errout_free: 24664f75ba69SDonald Sharp kfree_skb(skb); 24674f75ba69SDonald Sharp goto errout; 24684f75ba69SDonald Sharp } 24694f75ba69SDonald Sharp 2470cb6a4e46SPatrick McHardy static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) 2471cb6a4e46SPatrick McHardy { 2472cb6a4e46SPatrick McHardy struct net *net = sock_net(skb->sk); 2473cb6a4e46SPatrick McHardy struct mr_table *mrt; 2474cb6a4e46SPatrick McHardy struct mfc_cache *mfc; 2475cb6a4e46SPatrick McHardy unsigned int t = 0, s_t; 2476cb6a4e46SPatrick McHardy unsigned int e = 0, s_e; 2477cb6a4e46SPatrick McHardy 2478cb6a4e46SPatrick McHardy s_t = cb->args[0]; 24798fb472c0SNikolay Aleksandrov s_e = cb->args[1]; 2480cb6a4e46SPatrick McHardy 2481a8c9486bSEric Dumazet rcu_read_lock(); 2482cb6a4e46SPatrick McHardy ipmr_for_each_table(mrt, net) { 2483cb6a4e46SPatrick McHardy if (t < s_t) 2484cb6a4e46SPatrick McHardy goto next_table; 24858fb472c0SNikolay Aleksandrov list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list) { 2486cb6a4e46SPatrick McHardy if (e < s_e) 2487cb6a4e46SPatrick McHardy goto next_entry; 2488cb6a4e46SPatrick McHardy if (ipmr_fill_mroute(mrt, skb, 248915e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 2490cb6a4e46SPatrick McHardy cb->nlh->nlmsg_seq, 249165886f43SNicolas Dichtel mfc, RTM_NEWROUTE, 249265886f43SNicolas Dichtel NLM_F_MULTI) < 0) 2493cb6a4e46SPatrick McHardy goto done; 2494cb6a4e46SPatrick McHardy next_entry: 2495cb6a4e46SPatrick McHardy e++; 2496cb6a4e46SPatrick McHardy } 24978fb472c0SNikolay Aleksandrov e = 0; 24988fb472c0SNikolay Aleksandrov s_e = 0; 24998fb472c0SNikolay Aleksandrov 25001eb99af5SNicolas Dichtel spin_lock_bh(&mfc_unres_lock); 25011eb99af5SNicolas Dichtel list_for_each_entry(mfc, &mrt->mfc_unres_queue, list) { 25021eb99af5SNicolas Dichtel if (e < s_e) 25031eb99af5SNicolas Dichtel goto next_entry2; 25041eb99af5SNicolas Dichtel if (ipmr_fill_mroute(mrt, skb, 25051eb99af5SNicolas Dichtel NETLINK_CB(cb->skb).portid, 25061eb99af5SNicolas Dichtel cb->nlh->nlmsg_seq, 250765886f43SNicolas Dichtel mfc, RTM_NEWROUTE, 250865886f43SNicolas Dichtel NLM_F_MULTI) < 0) { 25091eb99af5SNicolas Dichtel spin_unlock_bh(&mfc_unres_lock); 25101eb99af5SNicolas Dichtel goto done; 25111eb99af5SNicolas Dichtel } 25121eb99af5SNicolas Dichtel next_entry2: 25131eb99af5SNicolas Dichtel e++; 25141eb99af5SNicolas Dichtel } 25151eb99af5SNicolas Dichtel spin_unlock_bh(&mfc_unres_lock); 25168fb472c0SNikolay Aleksandrov e = 0; 25178fb472c0SNikolay Aleksandrov s_e = 0; 2518cb6a4e46SPatrick McHardy next_table: 2519cb6a4e46SPatrick McHardy t++; 2520cb6a4e46SPatrick McHardy } 2521cb6a4e46SPatrick McHardy done: 2522a8c9486bSEric Dumazet rcu_read_unlock(); 2523cb6a4e46SPatrick McHardy 25248fb472c0SNikolay Aleksandrov cb->args[1] = e; 2525cb6a4e46SPatrick McHardy cb->args[0] = t; 2526cb6a4e46SPatrick McHardy 2527cb6a4e46SPatrick McHardy return skb->len; 2528cb6a4e46SPatrick McHardy } 2529cb6a4e46SPatrick McHardy 2530ccbb0aa6SNikolay Aleksandrov static const struct nla_policy rtm_ipmr_policy[RTA_MAX + 1] = { 2531ccbb0aa6SNikolay Aleksandrov [RTA_SRC] = { .type = NLA_U32 }, 2532ccbb0aa6SNikolay Aleksandrov [RTA_DST] = { .type = NLA_U32 }, 2533ccbb0aa6SNikolay Aleksandrov [RTA_IIF] = { .type = NLA_U32 }, 2534ccbb0aa6SNikolay Aleksandrov [RTA_TABLE] = { .type = NLA_U32 }, 2535ccbb0aa6SNikolay Aleksandrov [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2536ccbb0aa6SNikolay Aleksandrov }; 2537ccbb0aa6SNikolay Aleksandrov 2538ccbb0aa6SNikolay Aleksandrov static bool ipmr_rtm_validate_proto(unsigned char rtm_protocol) 2539ccbb0aa6SNikolay Aleksandrov { 2540ccbb0aa6SNikolay Aleksandrov switch (rtm_protocol) { 2541ccbb0aa6SNikolay Aleksandrov case RTPROT_STATIC: 2542ccbb0aa6SNikolay Aleksandrov case RTPROT_MROUTED: 2543ccbb0aa6SNikolay Aleksandrov return true; 2544ccbb0aa6SNikolay Aleksandrov } 2545ccbb0aa6SNikolay Aleksandrov return false; 2546ccbb0aa6SNikolay Aleksandrov } 2547ccbb0aa6SNikolay Aleksandrov 2548ccbb0aa6SNikolay Aleksandrov static int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl *mfcc) 2549ccbb0aa6SNikolay Aleksandrov { 2550ccbb0aa6SNikolay Aleksandrov struct rtnexthop *rtnh = nla_data(nla); 2551ccbb0aa6SNikolay Aleksandrov int remaining = nla_len(nla), vifi = 0; 2552ccbb0aa6SNikolay Aleksandrov 2553ccbb0aa6SNikolay Aleksandrov while (rtnh_ok(rtnh, remaining)) { 2554ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_ttls[vifi] = rtnh->rtnh_hops; 2555ccbb0aa6SNikolay Aleksandrov if (++vifi == MAXVIFS) 2556ccbb0aa6SNikolay Aleksandrov break; 2557ccbb0aa6SNikolay Aleksandrov rtnh = rtnh_next(rtnh, &remaining); 2558ccbb0aa6SNikolay Aleksandrov } 2559ccbb0aa6SNikolay Aleksandrov 2560ccbb0aa6SNikolay Aleksandrov return remaining > 0 ? -EINVAL : vifi; 2561ccbb0aa6SNikolay Aleksandrov } 2562ccbb0aa6SNikolay Aleksandrov 2563ccbb0aa6SNikolay Aleksandrov /* returns < 0 on error, 0 for ADD_MFC and 1 for ADD_MFC_PROXY */ 2564ccbb0aa6SNikolay Aleksandrov static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh, 2565ccbb0aa6SNikolay Aleksandrov struct mfcctl *mfcc, int *mrtsock, 2566c21ef3e3SDavid Ahern struct mr_table **mrtret, 2567c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 2568ccbb0aa6SNikolay Aleksandrov { 2569ccbb0aa6SNikolay Aleksandrov struct net_device *dev = NULL; 2570ccbb0aa6SNikolay Aleksandrov u32 tblid = RT_TABLE_DEFAULT; 2571ccbb0aa6SNikolay Aleksandrov struct mr_table *mrt; 2572ccbb0aa6SNikolay Aleksandrov struct nlattr *attr; 2573ccbb0aa6SNikolay Aleksandrov struct rtmsg *rtm; 2574ccbb0aa6SNikolay Aleksandrov int ret, rem; 2575ccbb0aa6SNikolay Aleksandrov 2576fceb6435SJohannes Berg ret = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipmr_policy, 2577c21ef3e3SDavid Ahern extack); 2578ccbb0aa6SNikolay Aleksandrov if (ret < 0) 2579ccbb0aa6SNikolay Aleksandrov goto out; 2580ccbb0aa6SNikolay Aleksandrov rtm = nlmsg_data(nlh); 2581ccbb0aa6SNikolay Aleksandrov 2582ccbb0aa6SNikolay Aleksandrov ret = -EINVAL; 2583ccbb0aa6SNikolay Aleksandrov if (rtm->rtm_family != RTNL_FAMILY_IPMR || rtm->rtm_dst_len != 32 || 2584ccbb0aa6SNikolay Aleksandrov rtm->rtm_type != RTN_MULTICAST || 2585ccbb0aa6SNikolay Aleksandrov rtm->rtm_scope != RT_SCOPE_UNIVERSE || 2586ccbb0aa6SNikolay Aleksandrov !ipmr_rtm_validate_proto(rtm->rtm_protocol)) 2587ccbb0aa6SNikolay Aleksandrov goto out; 2588ccbb0aa6SNikolay Aleksandrov 2589ccbb0aa6SNikolay Aleksandrov memset(mfcc, 0, sizeof(*mfcc)); 2590ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_parent = -1; 2591ccbb0aa6SNikolay Aleksandrov ret = 0; 2592ccbb0aa6SNikolay Aleksandrov nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), rem) { 2593ccbb0aa6SNikolay Aleksandrov switch (nla_type(attr)) { 2594ccbb0aa6SNikolay Aleksandrov case RTA_SRC: 2595ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_origin.s_addr = nla_get_be32(attr); 2596ccbb0aa6SNikolay Aleksandrov break; 2597ccbb0aa6SNikolay Aleksandrov case RTA_DST: 2598ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_mcastgrp.s_addr = nla_get_be32(attr); 2599ccbb0aa6SNikolay Aleksandrov break; 2600ccbb0aa6SNikolay Aleksandrov case RTA_IIF: 2601ccbb0aa6SNikolay Aleksandrov dev = __dev_get_by_index(net, nla_get_u32(attr)); 2602ccbb0aa6SNikolay Aleksandrov if (!dev) { 2603ccbb0aa6SNikolay Aleksandrov ret = -ENODEV; 2604ccbb0aa6SNikolay Aleksandrov goto out; 2605ccbb0aa6SNikolay Aleksandrov } 2606ccbb0aa6SNikolay Aleksandrov break; 2607ccbb0aa6SNikolay Aleksandrov case RTA_MULTIPATH: 2608ccbb0aa6SNikolay Aleksandrov if (ipmr_nla_get_ttls(attr, mfcc) < 0) { 2609ccbb0aa6SNikolay Aleksandrov ret = -EINVAL; 2610ccbb0aa6SNikolay Aleksandrov goto out; 2611ccbb0aa6SNikolay Aleksandrov } 2612ccbb0aa6SNikolay Aleksandrov break; 2613ccbb0aa6SNikolay Aleksandrov case RTA_PREFSRC: 2614ccbb0aa6SNikolay Aleksandrov ret = 1; 2615ccbb0aa6SNikolay Aleksandrov break; 2616ccbb0aa6SNikolay Aleksandrov case RTA_TABLE: 2617ccbb0aa6SNikolay Aleksandrov tblid = nla_get_u32(attr); 2618ccbb0aa6SNikolay Aleksandrov break; 2619ccbb0aa6SNikolay Aleksandrov } 2620ccbb0aa6SNikolay Aleksandrov } 2621ccbb0aa6SNikolay Aleksandrov mrt = ipmr_get_table(net, tblid); 2622ccbb0aa6SNikolay Aleksandrov if (!mrt) { 2623ccbb0aa6SNikolay Aleksandrov ret = -ENOENT; 2624ccbb0aa6SNikolay Aleksandrov goto out; 2625ccbb0aa6SNikolay Aleksandrov } 2626ccbb0aa6SNikolay Aleksandrov *mrtret = mrt; 2627ccbb0aa6SNikolay Aleksandrov *mrtsock = rtm->rtm_protocol == RTPROT_MROUTED ? 1 : 0; 2628ccbb0aa6SNikolay Aleksandrov if (dev) 2629ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_parent = ipmr_find_vif(mrt, dev); 2630ccbb0aa6SNikolay Aleksandrov 2631ccbb0aa6SNikolay Aleksandrov out: 2632ccbb0aa6SNikolay Aleksandrov return ret; 2633ccbb0aa6SNikolay Aleksandrov } 2634ccbb0aa6SNikolay Aleksandrov 2635ccbb0aa6SNikolay Aleksandrov /* takes care of both newroute and delroute */ 2636c21ef3e3SDavid Ahern static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh, 2637c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 2638ccbb0aa6SNikolay Aleksandrov { 2639ccbb0aa6SNikolay Aleksandrov struct net *net = sock_net(skb->sk); 2640ccbb0aa6SNikolay Aleksandrov int ret, mrtsock, parent; 2641ccbb0aa6SNikolay Aleksandrov struct mr_table *tbl; 2642ccbb0aa6SNikolay Aleksandrov struct mfcctl mfcc; 2643ccbb0aa6SNikolay Aleksandrov 2644ccbb0aa6SNikolay Aleksandrov mrtsock = 0; 2645ccbb0aa6SNikolay Aleksandrov tbl = NULL; 2646c21ef3e3SDavid Ahern ret = rtm_to_ipmr_mfcc(net, nlh, &mfcc, &mrtsock, &tbl, extack); 2647ccbb0aa6SNikolay Aleksandrov if (ret < 0) 2648ccbb0aa6SNikolay Aleksandrov return ret; 2649ccbb0aa6SNikolay Aleksandrov 2650ccbb0aa6SNikolay Aleksandrov parent = ret ? mfcc.mfcc_parent : -1; 2651ccbb0aa6SNikolay Aleksandrov if (nlh->nlmsg_type == RTM_NEWROUTE) 2652ccbb0aa6SNikolay Aleksandrov return ipmr_mfc_add(net, tbl, &mfcc, mrtsock, parent); 2653ccbb0aa6SNikolay Aleksandrov else 2654ccbb0aa6SNikolay Aleksandrov return ipmr_mfc_delete(tbl, &mfcc, parent); 2655ccbb0aa6SNikolay Aleksandrov } 2656ccbb0aa6SNikolay Aleksandrov 2657772c344dSNikolay Aleksandrov static bool ipmr_fill_table(struct mr_table *mrt, struct sk_buff *skb) 2658772c344dSNikolay Aleksandrov { 2659772c344dSNikolay Aleksandrov u32 queue_len = atomic_read(&mrt->cache_resolve_queue_len); 2660772c344dSNikolay Aleksandrov 2661772c344dSNikolay Aleksandrov if (nla_put_u32(skb, IPMRA_TABLE_ID, mrt->id) || 2662772c344dSNikolay Aleksandrov nla_put_u32(skb, IPMRA_TABLE_CACHE_RES_QUEUE_LEN, queue_len) || 2663772c344dSNikolay Aleksandrov nla_put_s32(skb, IPMRA_TABLE_MROUTE_REG_VIF_NUM, 2664772c344dSNikolay Aleksandrov mrt->mroute_reg_vif_num) || 2665772c344dSNikolay Aleksandrov nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_ASSERT, 2666772c344dSNikolay Aleksandrov mrt->mroute_do_assert) || 2667772c344dSNikolay Aleksandrov nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_PIM, mrt->mroute_do_pim)) 2668772c344dSNikolay Aleksandrov return false; 2669772c344dSNikolay Aleksandrov 2670772c344dSNikolay Aleksandrov return true; 2671772c344dSNikolay Aleksandrov } 2672772c344dSNikolay Aleksandrov 2673772c344dSNikolay Aleksandrov static bool ipmr_fill_vif(struct mr_table *mrt, u32 vifid, struct sk_buff *skb) 2674772c344dSNikolay Aleksandrov { 2675772c344dSNikolay Aleksandrov struct nlattr *vif_nest; 2676772c344dSNikolay Aleksandrov struct vif_device *vif; 2677772c344dSNikolay Aleksandrov 2678772c344dSNikolay Aleksandrov /* if the VIF doesn't exist just continue */ 2679772c344dSNikolay Aleksandrov if (!VIF_EXISTS(mrt, vifid)) 2680772c344dSNikolay Aleksandrov return true; 2681772c344dSNikolay Aleksandrov 2682772c344dSNikolay Aleksandrov vif = &mrt->vif_table[vifid]; 2683772c344dSNikolay Aleksandrov vif_nest = nla_nest_start(skb, IPMRA_VIF); 2684772c344dSNikolay Aleksandrov if (!vif_nest) 2685772c344dSNikolay Aleksandrov return false; 2686772c344dSNikolay Aleksandrov if (nla_put_u32(skb, IPMRA_VIFA_IFINDEX, vif->dev->ifindex) || 2687772c344dSNikolay Aleksandrov nla_put_u32(skb, IPMRA_VIFA_VIF_ID, vifid) || 2688772c344dSNikolay Aleksandrov nla_put_u16(skb, IPMRA_VIFA_FLAGS, vif->flags) || 2689772c344dSNikolay Aleksandrov nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_IN, vif->bytes_in, 2690772c344dSNikolay Aleksandrov IPMRA_VIFA_PAD) || 2691772c344dSNikolay Aleksandrov nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_OUT, vif->bytes_out, 2692772c344dSNikolay Aleksandrov IPMRA_VIFA_PAD) || 2693772c344dSNikolay Aleksandrov nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_IN, vif->pkt_in, 2694772c344dSNikolay Aleksandrov IPMRA_VIFA_PAD) || 2695772c344dSNikolay Aleksandrov nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_OUT, vif->pkt_out, 2696772c344dSNikolay Aleksandrov IPMRA_VIFA_PAD) || 2697772c344dSNikolay Aleksandrov nla_put_be32(skb, IPMRA_VIFA_LOCAL_ADDR, vif->local) || 2698772c344dSNikolay Aleksandrov nla_put_be32(skb, IPMRA_VIFA_REMOTE_ADDR, vif->remote)) { 2699772c344dSNikolay Aleksandrov nla_nest_cancel(skb, vif_nest); 2700772c344dSNikolay Aleksandrov return false; 2701772c344dSNikolay Aleksandrov } 2702772c344dSNikolay Aleksandrov nla_nest_end(skb, vif_nest); 2703772c344dSNikolay Aleksandrov 2704772c344dSNikolay Aleksandrov return true; 2705772c344dSNikolay Aleksandrov } 2706772c344dSNikolay Aleksandrov 2707772c344dSNikolay Aleksandrov static int ipmr_rtm_dumplink(struct sk_buff *skb, struct netlink_callback *cb) 2708772c344dSNikolay Aleksandrov { 2709772c344dSNikolay Aleksandrov struct net *net = sock_net(skb->sk); 2710772c344dSNikolay Aleksandrov struct nlmsghdr *nlh = NULL; 2711772c344dSNikolay Aleksandrov unsigned int t = 0, s_t; 2712772c344dSNikolay Aleksandrov unsigned int e = 0, s_e; 2713772c344dSNikolay Aleksandrov struct mr_table *mrt; 2714772c344dSNikolay Aleksandrov 2715772c344dSNikolay Aleksandrov s_t = cb->args[0]; 2716772c344dSNikolay Aleksandrov s_e = cb->args[1]; 2717772c344dSNikolay Aleksandrov 2718772c344dSNikolay Aleksandrov ipmr_for_each_table(mrt, net) { 2719772c344dSNikolay Aleksandrov struct nlattr *vifs, *af; 2720772c344dSNikolay Aleksandrov struct ifinfomsg *hdr; 2721772c344dSNikolay Aleksandrov u32 i; 2722772c344dSNikolay Aleksandrov 2723772c344dSNikolay Aleksandrov if (t < s_t) 2724772c344dSNikolay Aleksandrov goto skip_table; 2725772c344dSNikolay Aleksandrov nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, 2726772c344dSNikolay Aleksandrov cb->nlh->nlmsg_seq, RTM_NEWLINK, 2727772c344dSNikolay Aleksandrov sizeof(*hdr), NLM_F_MULTI); 2728772c344dSNikolay Aleksandrov if (!nlh) 2729772c344dSNikolay Aleksandrov break; 2730772c344dSNikolay Aleksandrov 2731772c344dSNikolay Aleksandrov hdr = nlmsg_data(nlh); 2732772c344dSNikolay Aleksandrov memset(hdr, 0, sizeof(*hdr)); 2733772c344dSNikolay Aleksandrov hdr->ifi_family = RTNL_FAMILY_IPMR; 2734772c344dSNikolay Aleksandrov 2735772c344dSNikolay Aleksandrov af = nla_nest_start(skb, IFLA_AF_SPEC); 2736772c344dSNikolay Aleksandrov if (!af) { 2737772c344dSNikolay Aleksandrov nlmsg_cancel(skb, nlh); 2738772c344dSNikolay Aleksandrov goto out; 2739772c344dSNikolay Aleksandrov } 2740772c344dSNikolay Aleksandrov 2741772c344dSNikolay Aleksandrov if (!ipmr_fill_table(mrt, skb)) { 2742772c344dSNikolay Aleksandrov nlmsg_cancel(skb, nlh); 2743772c344dSNikolay Aleksandrov goto out; 2744772c344dSNikolay Aleksandrov } 2745772c344dSNikolay Aleksandrov 2746772c344dSNikolay Aleksandrov vifs = nla_nest_start(skb, IPMRA_TABLE_VIFS); 2747772c344dSNikolay Aleksandrov if (!vifs) { 2748772c344dSNikolay Aleksandrov nla_nest_end(skb, af); 2749772c344dSNikolay Aleksandrov nlmsg_end(skb, nlh); 2750772c344dSNikolay Aleksandrov goto out; 2751772c344dSNikolay Aleksandrov } 2752772c344dSNikolay Aleksandrov for (i = 0; i < mrt->maxvif; i++) { 2753772c344dSNikolay Aleksandrov if (e < s_e) 2754772c344dSNikolay Aleksandrov goto skip_entry; 2755772c344dSNikolay Aleksandrov if (!ipmr_fill_vif(mrt, i, skb)) { 2756772c344dSNikolay Aleksandrov nla_nest_end(skb, vifs); 2757772c344dSNikolay Aleksandrov nla_nest_end(skb, af); 2758772c344dSNikolay Aleksandrov nlmsg_end(skb, nlh); 2759772c344dSNikolay Aleksandrov goto out; 2760772c344dSNikolay Aleksandrov } 2761772c344dSNikolay Aleksandrov skip_entry: 2762772c344dSNikolay Aleksandrov e++; 2763772c344dSNikolay Aleksandrov } 2764772c344dSNikolay Aleksandrov s_e = 0; 2765772c344dSNikolay Aleksandrov e = 0; 2766772c344dSNikolay Aleksandrov nla_nest_end(skb, vifs); 2767772c344dSNikolay Aleksandrov nla_nest_end(skb, af); 2768772c344dSNikolay Aleksandrov nlmsg_end(skb, nlh); 2769772c344dSNikolay Aleksandrov skip_table: 2770772c344dSNikolay Aleksandrov t++; 2771772c344dSNikolay Aleksandrov } 2772772c344dSNikolay Aleksandrov 2773772c344dSNikolay Aleksandrov out: 2774772c344dSNikolay Aleksandrov cb->args[1] = e; 2775772c344dSNikolay Aleksandrov cb->args[0] = t; 2776772c344dSNikolay Aleksandrov 2777772c344dSNikolay Aleksandrov return skb->len; 2778772c344dSNikolay Aleksandrov } 2779772c344dSNikolay Aleksandrov 27801da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 27817ef8f65dSNikolay Aleksandrov /* The /proc interfaces to multicast routing : 2782a8cb16ddSEric Dumazet * /proc/net/ip_mr_cache & /proc/net/ip_mr_vif 27831da177e4SLinus Torvalds */ 27841da177e4SLinus Torvalds struct ipmr_vif_iter { 2785f6bb4514SBenjamin Thery struct seq_net_private p; 2786f0ad0860SPatrick McHardy struct mr_table *mrt; 27871da177e4SLinus Torvalds int ct; 27881da177e4SLinus Torvalds }; 27891da177e4SLinus Torvalds 2790f6bb4514SBenjamin Thery static struct vif_device *ipmr_vif_seq_idx(struct net *net, 2791f6bb4514SBenjamin Thery struct ipmr_vif_iter *iter, 27921da177e4SLinus Torvalds loff_t pos) 27931da177e4SLinus Torvalds { 2794f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 27950c12295aSPatrick McHardy 27960c12295aSPatrick McHardy for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) { 27970c12295aSPatrick McHardy if (!VIF_EXISTS(mrt, iter->ct)) 27981da177e4SLinus Torvalds continue; 27991da177e4SLinus Torvalds if (pos-- == 0) 28000c12295aSPatrick McHardy return &mrt->vif_table[iter->ct]; 28011da177e4SLinus Torvalds } 28021da177e4SLinus Torvalds return NULL; 28031da177e4SLinus Torvalds } 28041da177e4SLinus Torvalds 28051da177e4SLinus Torvalds static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos) 2806ba93ef74SStephen Hemminger __acquires(mrt_lock) 28071da177e4SLinus Torvalds { 2808f0ad0860SPatrick McHardy struct ipmr_vif_iter *iter = seq->private; 2809f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2810f0ad0860SPatrick McHardy struct mr_table *mrt; 2811f0ad0860SPatrick McHardy 2812f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 281351456b29SIan Morris if (!mrt) 2814f0ad0860SPatrick McHardy return ERR_PTR(-ENOENT); 2815f0ad0860SPatrick McHardy 2816f0ad0860SPatrick McHardy iter->mrt = mrt; 2817f6bb4514SBenjamin Thery 28181da177e4SLinus Torvalds read_lock(&mrt_lock); 2819f6bb4514SBenjamin Thery return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1) 28201da177e4SLinus Torvalds : SEQ_START_TOKEN; 28211da177e4SLinus Torvalds } 28221da177e4SLinus Torvalds 28231da177e4SLinus Torvalds static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos) 28241da177e4SLinus Torvalds { 28251da177e4SLinus Torvalds struct ipmr_vif_iter *iter = seq->private; 2826f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2827f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 28281da177e4SLinus Torvalds 28291da177e4SLinus Torvalds ++*pos; 28301da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 2831f6bb4514SBenjamin Thery return ipmr_vif_seq_idx(net, iter, 0); 28321da177e4SLinus Torvalds 28330c12295aSPatrick McHardy while (++iter->ct < mrt->maxvif) { 28340c12295aSPatrick McHardy if (!VIF_EXISTS(mrt, iter->ct)) 28351da177e4SLinus Torvalds continue; 28360c12295aSPatrick McHardy return &mrt->vif_table[iter->ct]; 28371da177e4SLinus Torvalds } 28381da177e4SLinus Torvalds return NULL; 28391da177e4SLinus Torvalds } 28401da177e4SLinus Torvalds 28411da177e4SLinus Torvalds static void ipmr_vif_seq_stop(struct seq_file *seq, void *v) 2842ba93ef74SStephen Hemminger __releases(mrt_lock) 28431da177e4SLinus Torvalds { 28441da177e4SLinus Torvalds read_unlock(&mrt_lock); 28451da177e4SLinus Torvalds } 28461da177e4SLinus Torvalds 28471da177e4SLinus Torvalds static int ipmr_vif_seq_show(struct seq_file *seq, void *v) 28481da177e4SLinus Torvalds { 2849f0ad0860SPatrick McHardy struct ipmr_vif_iter *iter = seq->private; 2850f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 2851f6bb4514SBenjamin Thery 28521da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 28531da177e4SLinus Torvalds seq_puts(seq, 28541da177e4SLinus Torvalds "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n"); 28551da177e4SLinus Torvalds } else { 28561da177e4SLinus Torvalds const struct vif_device *vif = v; 28571da177e4SLinus Torvalds const char *name = vif->dev ? vif->dev->name : "none"; 28581da177e4SLinus Torvalds 28591da177e4SLinus Torvalds seq_printf(seq, 28605b5e0928SAlexey Dobriyan "%2zd %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n", 28610c12295aSPatrick McHardy vif - mrt->vif_table, 28621da177e4SLinus Torvalds name, vif->bytes_in, vif->pkt_in, 28631da177e4SLinus Torvalds vif->bytes_out, vif->pkt_out, 28641da177e4SLinus Torvalds vif->flags, vif->local, vif->remote); 28651da177e4SLinus Torvalds } 28661da177e4SLinus Torvalds return 0; 28671da177e4SLinus Torvalds } 28681da177e4SLinus Torvalds 2869f690808eSStephen Hemminger static const struct seq_operations ipmr_vif_seq_ops = { 28701da177e4SLinus Torvalds .start = ipmr_vif_seq_start, 28711da177e4SLinus Torvalds .next = ipmr_vif_seq_next, 28721da177e4SLinus Torvalds .stop = ipmr_vif_seq_stop, 28731da177e4SLinus Torvalds .show = ipmr_vif_seq_show, 28741da177e4SLinus Torvalds }; 28751da177e4SLinus Torvalds 28761da177e4SLinus Torvalds static int ipmr_vif_open(struct inode *inode, struct file *file) 28771da177e4SLinus Torvalds { 2878f6bb4514SBenjamin Thery return seq_open_net(inode, file, &ipmr_vif_seq_ops, 2879cf7732e4SPavel Emelyanov sizeof(struct ipmr_vif_iter)); 28801da177e4SLinus Torvalds } 28811da177e4SLinus Torvalds 28829a32144eSArjan van de Ven static const struct file_operations ipmr_vif_fops = { 28831da177e4SLinus Torvalds .owner = THIS_MODULE, 28841da177e4SLinus Torvalds .open = ipmr_vif_open, 28851da177e4SLinus Torvalds .read = seq_read, 28861da177e4SLinus Torvalds .llseek = seq_lseek, 2887f6bb4514SBenjamin Thery .release = seq_release_net, 28881da177e4SLinus Torvalds }; 28891da177e4SLinus Torvalds 28901da177e4SLinus Torvalds struct ipmr_mfc_iter { 2891f6bb4514SBenjamin Thery struct seq_net_private p; 2892f0ad0860SPatrick McHardy struct mr_table *mrt; 2893862465f2SPatrick McHardy struct list_head *cache; 28941da177e4SLinus Torvalds }; 28951da177e4SLinus Torvalds 2896f6bb4514SBenjamin Thery static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net, 2897f6bb4514SBenjamin Thery struct ipmr_mfc_iter *it, loff_t pos) 28981da177e4SLinus Torvalds { 2899f0ad0860SPatrick McHardy struct mr_table *mrt = it->mrt; 29001da177e4SLinus Torvalds struct mfc_cache *mfc; 29011da177e4SLinus Torvalds 2902a8c9486bSEric Dumazet rcu_read_lock(); 29038fb472c0SNikolay Aleksandrov it->cache = &mrt->mfc_cache_list; 29048fb472c0SNikolay Aleksandrov list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list) 29051da177e4SLinus Torvalds if (pos-- == 0) 29061da177e4SLinus Torvalds return mfc; 2907a8c9486bSEric Dumazet rcu_read_unlock(); 29081da177e4SLinus Torvalds 29091da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 29100c12295aSPatrick McHardy it->cache = &mrt->mfc_unres_queue; 2911862465f2SPatrick McHardy list_for_each_entry(mfc, it->cache, list) 2912e258beb2SPatrick McHardy if (pos-- == 0) 29131da177e4SLinus Torvalds return mfc; 29141da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 29151da177e4SLinus Torvalds 29161da177e4SLinus Torvalds it->cache = NULL; 29171da177e4SLinus Torvalds return NULL; 29181da177e4SLinus Torvalds } 29191da177e4SLinus Torvalds 29201da177e4SLinus Torvalds 29211da177e4SLinus Torvalds static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos) 29221da177e4SLinus Torvalds { 29231da177e4SLinus Torvalds struct ipmr_mfc_iter *it = seq->private; 2924f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2925f0ad0860SPatrick McHardy struct mr_table *mrt; 2926f6bb4514SBenjamin Thery 2927f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 292851456b29SIan Morris if (!mrt) 2929f0ad0860SPatrick McHardy return ERR_PTR(-ENOENT); 2930f0ad0860SPatrick McHardy 2931f0ad0860SPatrick McHardy it->mrt = mrt; 29321da177e4SLinus Torvalds it->cache = NULL; 2933f6bb4514SBenjamin Thery return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1) 29341da177e4SLinus Torvalds : SEQ_START_TOKEN; 29351da177e4SLinus Torvalds } 29361da177e4SLinus Torvalds 29371da177e4SLinus Torvalds static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 29381da177e4SLinus Torvalds { 29391da177e4SLinus Torvalds struct ipmr_mfc_iter *it = seq->private; 2940f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2941f0ad0860SPatrick McHardy struct mr_table *mrt = it->mrt; 29428fb472c0SNikolay Aleksandrov struct mfc_cache *mfc = v; 29431da177e4SLinus Torvalds 29441da177e4SLinus Torvalds ++*pos; 29451da177e4SLinus Torvalds 29461da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 2947f6bb4514SBenjamin Thery return ipmr_mfc_seq_idx(net, seq->private, 0); 29481da177e4SLinus Torvalds 2949862465f2SPatrick McHardy if (mfc->list.next != it->cache) 2950862465f2SPatrick McHardy return list_entry(mfc->list.next, struct mfc_cache, list); 29511da177e4SLinus Torvalds 29520c12295aSPatrick McHardy if (it->cache == &mrt->mfc_unres_queue) 29531da177e4SLinus Torvalds goto end_of_list; 29541da177e4SLinus Torvalds 29551da177e4SLinus Torvalds /* exhausted cache_array, show unresolved */ 2956a8c9486bSEric Dumazet rcu_read_unlock(); 29570c12295aSPatrick McHardy it->cache = &mrt->mfc_unres_queue; 29581da177e4SLinus Torvalds 29591da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 2960862465f2SPatrick McHardy if (!list_empty(it->cache)) 2961862465f2SPatrick McHardy return list_first_entry(it->cache, struct mfc_cache, list); 29621da177e4SLinus Torvalds 29631da177e4SLinus Torvalds end_of_list: 29641da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 29651da177e4SLinus Torvalds it->cache = NULL; 29661da177e4SLinus Torvalds 29671da177e4SLinus Torvalds return NULL; 29681da177e4SLinus Torvalds } 29691da177e4SLinus Torvalds 29701da177e4SLinus Torvalds static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) 29711da177e4SLinus Torvalds { 29721da177e4SLinus Torvalds struct ipmr_mfc_iter *it = seq->private; 2973f0ad0860SPatrick McHardy struct mr_table *mrt = it->mrt; 29741da177e4SLinus Torvalds 29750c12295aSPatrick McHardy if (it->cache == &mrt->mfc_unres_queue) 29761da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 29778fb472c0SNikolay Aleksandrov else if (it->cache == &mrt->mfc_cache_list) 2978a8c9486bSEric Dumazet rcu_read_unlock(); 29791da177e4SLinus Torvalds } 29801da177e4SLinus Torvalds 29811da177e4SLinus Torvalds static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) 29821da177e4SLinus Torvalds { 29831da177e4SLinus Torvalds int n; 29841da177e4SLinus Torvalds 29851da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 29861da177e4SLinus Torvalds seq_puts(seq, 29871da177e4SLinus Torvalds "Group Origin Iif Pkts Bytes Wrong Oifs\n"); 29881da177e4SLinus Torvalds } else { 29891da177e4SLinus Torvalds const struct mfc_cache *mfc = v; 29901da177e4SLinus Torvalds const struct ipmr_mfc_iter *it = seq->private; 2991f0ad0860SPatrick McHardy const struct mr_table *mrt = it->mrt; 29921da177e4SLinus Torvalds 29930eae88f3SEric Dumazet seq_printf(seq, "%08X %08X %-3hd", 29940eae88f3SEric Dumazet (__force u32) mfc->mfc_mcastgrp, 29950eae88f3SEric Dumazet (__force u32) mfc->mfc_origin, 29961ea472e2SBenjamin Thery mfc->mfc_parent); 29971ea472e2SBenjamin Thery 29980c12295aSPatrick McHardy if (it->cache != &mrt->mfc_unres_queue) { 29991ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 30001da177e4SLinus Torvalds mfc->mfc_un.res.pkt, 30011da177e4SLinus Torvalds mfc->mfc_un.res.bytes, 30021da177e4SLinus Torvalds mfc->mfc_un.res.wrong_if); 30031da177e4SLinus Torvalds for (n = mfc->mfc_un.res.minvif; 30041da177e4SLinus Torvalds n < mfc->mfc_un.res.maxvif; n++) { 30050c12295aSPatrick McHardy if (VIF_EXISTS(mrt, n) && 3006cf958ae3SBenjamin Thery mfc->mfc_un.res.ttls[n] < 255) 30071da177e4SLinus Torvalds seq_printf(seq, 30081da177e4SLinus Torvalds " %2d:%-3d", 30091da177e4SLinus Torvalds n, mfc->mfc_un.res.ttls[n]); 30101da177e4SLinus Torvalds } 30111ea472e2SBenjamin Thery } else { 30121ea472e2SBenjamin Thery /* unresolved mfc_caches don't contain 30131ea472e2SBenjamin Thery * pkt, bytes and wrong_if values 30141ea472e2SBenjamin Thery */ 30151ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul); 30161da177e4SLinus Torvalds } 30171da177e4SLinus Torvalds seq_putc(seq, '\n'); 30181da177e4SLinus Torvalds } 30191da177e4SLinus Torvalds return 0; 30201da177e4SLinus Torvalds } 30211da177e4SLinus Torvalds 3022f690808eSStephen Hemminger static const struct seq_operations ipmr_mfc_seq_ops = { 30231da177e4SLinus Torvalds .start = ipmr_mfc_seq_start, 30241da177e4SLinus Torvalds .next = ipmr_mfc_seq_next, 30251da177e4SLinus Torvalds .stop = ipmr_mfc_seq_stop, 30261da177e4SLinus Torvalds .show = ipmr_mfc_seq_show, 30271da177e4SLinus Torvalds }; 30281da177e4SLinus Torvalds 30291da177e4SLinus Torvalds static int ipmr_mfc_open(struct inode *inode, struct file *file) 30301da177e4SLinus Torvalds { 3031f6bb4514SBenjamin Thery return seq_open_net(inode, file, &ipmr_mfc_seq_ops, 3032cf7732e4SPavel Emelyanov sizeof(struct ipmr_mfc_iter)); 30331da177e4SLinus Torvalds } 30341da177e4SLinus Torvalds 30359a32144eSArjan van de Ven static const struct file_operations ipmr_mfc_fops = { 30361da177e4SLinus Torvalds .owner = THIS_MODULE, 30371da177e4SLinus Torvalds .open = ipmr_mfc_open, 30381da177e4SLinus Torvalds .read = seq_read, 30391da177e4SLinus Torvalds .llseek = seq_lseek, 3040f6bb4514SBenjamin Thery .release = seq_release_net, 30411da177e4SLinus Torvalds }; 30421da177e4SLinus Torvalds #endif 30431da177e4SLinus Torvalds 30441da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V2 304532613090SAlexey Dobriyan static const struct net_protocol pim_protocol = { 30461da177e4SLinus Torvalds .handler = pim_rcv, 3047403dbb97STom Goff .netns_ok = 1, 30481da177e4SLinus Torvalds }; 30491da177e4SLinus Torvalds #endif 30501da177e4SLinus Torvalds 30517ef8f65dSNikolay Aleksandrov /* Setup for IP multicast routing */ 3052cf958ae3SBenjamin Thery static int __net_init ipmr_net_init(struct net *net) 3053cf958ae3SBenjamin Thery { 3054f0ad0860SPatrick McHardy int err; 3055cf958ae3SBenjamin Thery 3056f0ad0860SPatrick McHardy err = ipmr_rules_init(net); 3057f0ad0860SPatrick McHardy if (err < 0) 3058cf958ae3SBenjamin Thery goto fail; 3059f6bb4514SBenjamin Thery 3060f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 3061f6bb4514SBenjamin Thery err = -ENOMEM; 3062d4beaa66SGao feng if (!proc_create("ip_mr_vif", 0, net->proc_net, &ipmr_vif_fops)) 3063f6bb4514SBenjamin Thery goto proc_vif_fail; 3064d4beaa66SGao feng if (!proc_create("ip_mr_cache", 0, net->proc_net, &ipmr_mfc_fops)) 3065f6bb4514SBenjamin Thery goto proc_cache_fail; 3066f6bb4514SBenjamin Thery #endif 30672bb8b26cSBenjamin Thery return 0; 30682bb8b26cSBenjamin Thery 3069f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 3070f6bb4514SBenjamin Thery proc_cache_fail: 3071ece31ffdSGao feng remove_proc_entry("ip_mr_vif", net->proc_net); 3072f6bb4514SBenjamin Thery proc_vif_fail: 3073f0ad0860SPatrick McHardy ipmr_rules_exit(net); 3074f6bb4514SBenjamin Thery #endif 3075cf958ae3SBenjamin Thery fail: 3076cf958ae3SBenjamin Thery return err; 3077cf958ae3SBenjamin Thery } 3078cf958ae3SBenjamin Thery 3079cf958ae3SBenjamin Thery static void __net_exit ipmr_net_exit(struct net *net) 3080cf958ae3SBenjamin Thery { 3081f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 3082ece31ffdSGao feng remove_proc_entry("ip_mr_cache", net->proc_net); 3083ece31ffdSGao feng remove_proc_entry("ip_mr_vif", net->proc_net); 3084f6bb4514SBenjamin Thery #endif 3085f0ad0860SPatrick McHardy ipmr_rules_exit(net); 3086cf958ae3SBenjamin Thery } 3087cf958ae3SBenjamin Thery 3088cf958ae3SBenjamin Thery static struct pernet_operations ipmr_net_ops = { 3089cf958ae3SBenjamin Thery .init = ipmr_net_init, 3090cf958ae3SBenjamin Thery .exit = ipmr_net_exit, 3091cf958ae3SBenjamin Thery }; 30921da177e4SLinus Torvalds 309303d2f897SWang Chen int __init ip_mr_init(void) 30941da177e4SLinus Torvalds { 309503d2f897SWang Chen int err; 309603d2f897SWang Chen 30971da177e4SLinus Torvalds mrt_cachep = kmem_cache_create("ip_mrt_cache", 30981da177e4SLinus Torvalds sizeof(struct mfc_cache), 3099e5d679f3SAlexey Dobriyan 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, 310020c2df83SPaul Mundt NULL); 310103d2f897SWang Chen 3102cf958ae3SBenjamin Thery err = register_pernet_subsys(&ipmr_net_ops); 3103cf958ae3SBenjamin Thery if (err) 3104cf958ae3SBenjamin Thery goto reg_pernet_fail; 3105cf958ae3SBenjamin Thery 310603d2f897SWang Chen err = register_netdevice_notifier(&ip_mr_notifier); 310703d2f897SWang Chen if (err) 310803d2f897SWang Chen goto reg_notif_fail; 3109403dbb97STom Goff #ifdef CONFIG_IP_PIMSM_V2 3110403dbb97STom Goff if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) { 3111058bd4d2SJoe Perches pr_err("%s: can't add PIM protocol\n", __func__); 3112403dbb97STom Goff err = -EAGAIN; 3113403dbb97STom Goff goto add_proto_fail; 3114403dbb97STom Goff } 3115403dbb97STom Goff #endif 3116c7ac8679SGreg Rose rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, 31174f75ba69SDonald Sharp ipmr_rtm_getroute, ipmr_rtm_dumproute, NULL); 3118ccbb0aa6SNikolay Aleksandrov rtnl_register(RTNL_FAMILY_IPMR, RTM_NEWROUTE, 3119ccbb0aa6SNikolay Aleksandrov ipmr_rtm_route, NULL, NULL); 3120ccbb0aa6SNikolay Aleksandrov rtnl_register(RTNL_FAMILY_IPMR, RTM_DELROUTE, 3121ccbb0aa6SNikolay Aleksandrov ipmr_rtm_route, NULL, NULL); 3122772c344dSNikolay Aleksandrov 3123772c344dSNikolay Aleksandrov rtnl_register(RTNL_FAMILY_IPMR, RTM_GETLINK, 3124772c344dSNikolay Aleksandrov NULL, ipmr_rtm_dumplink, NULL); 312503d2f897SWang Chen return 0; 3126f6bb4514SBenjamin Thery 3127403dbb97STom Goff #ifdef CONFIG_IP_PIMSM_V2 3128403dbb97STom Goff add_proto_fail: 3129403dbb97STom Goff unregister_netdevice_notifier(&ip_mr_notifier); 3130403dbb97STom Goff #endif 3131c3e38896SBenjamin Thery reg_notif_fail: 3132cf958ae3SBenjamin Thery unregister_pernet_subsys(&ipmr_net_ops); 3133cf958ae3SBenjamin Thery reg_pernet_fail: 3134c3e38896SBenjamin Thery kmem_cache_destroy(mrt_cachep); 313503d2f897SWang Chen return err; 31361da177e4SLinus Torvalds } 3137