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> 3108009a76SAlexey Dobriyan #include <linux/cache.h> 324fc268d2SRandy Dunlap #include <linux/capability.h> 331da177e4SLinus Torvalds #include <linux/errno.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/icmp.h> 561da177e4SLinus Torvalds #include <net/udp.h> 571da177e4SLinus Torvalds #include <net/raw.h> 581da177e4SLinus Torvalds #include <linux/notifier.h> 591da177e4SLinus Torvalds #include <linux/if_arp.h> 601da177e4SLinus Torvalds #include <linux/netfilter_ipv4.h> 61709b46e8SEric W. Biederman #include <linux/compat.h> 62bc3b2d7fSPaul Gortmaker #include <linux/export.h> 630eb71a9dSNeilBrown #include <linux/rhashtable.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> 705d8b3e69SYotam Gigi #include <net/switchdev.h> 711da177e4SLinus Torvalds 72f0ad0860SPatrick McHardy struct ipmr_rule { 73f0ad0860SPatrick McHardy struct fib_rule common; 74f0ad0860SPatrick McHardy }; 75f0ad0860SPatrick McHardy 76f0ad0860SPatrick McHardy struct ipmr_result { 77f0ad0860SPatrick McHardy struct mr_table *mrt; 78f0ad0860SPatrick McHardy }; 79f0ad0860SPatrick McHardy 801da177e4SLinus Torvalds /* Big lock, protecting vif table, mrt cache and mroute socket state. 81a8cb16ddSEric Dumazet * Note that the changes are semaphored via rtnl_lock. 821da177e4SLinus Torvalds */ 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds static DEFINE_RWLOCK(mrt_lock); 851da177e4SLinus Torvalds 867ef8f65dSNikolay Aleksandrov /* Multicast router control variables */ 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds /* Special spinlock for queue of unresolved entries */ 891da177e4SLinus Torvalds static DEFINE_SPINLOCK(mfc_unres_lock); 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds /* We return to original Alan's scheme. Hash table of resolved 92a8cb16ddSEric Dumazet * entries is changed only in process context and protected 93a8cb16ddSEric Dumazet * with weak lock mrt_lock. Queue of unresolved entries is protected 94a8cb16ddSEric Dumazet * with strong spinlock mfc_unres_lock. 95a8cb16ddSEric Dumazet * 96a8cb16ddSEric Dumazet * In this case data path is free of exclusive locks at all. 971da177e4SLinus Torvalds */ 981da177e4SLinus Torvalds 9908009a76SAlexey Dobriyan static struct kmem_cache *mrt_cachep __ro_after_init; 1001da177e4SLinus Torvalds 101f0ad0860SPatrick McHardy static struct mr_table *ipmr_new_table(struct net *net, u32 id); 102acbb219dSFrancesco Ruggeri static void ipmr_free_table(struct mr_table *mrt); 103acbb219dSFrancesco Ruggeri 104c4854ec8SRami Rosen static void ip_mr_forward(struct net *net, struct mr_table *mrt, 1054b1f0d33SDonald Sharp struct net_device *dev, struct sk_buff *skb, 1064b1f0d33SDonald Sharp struct mfc_cache *cache, int local); 1070c12295aSPatrick McHardy static int ipmr_cache_report(struct mr_table *mrt, 1084feb88e5SBenjamin Thery struct sk_buff *pkt, vifi_t vifi, int assert); 1098cd3ac9fSNicolas Dichtel static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, 1108cd3ac9fSNicolas Dichtel int cmd); 1115a645dd8SJulien Gomes static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt); 1120e615e96SNikolay Aleksandrov static void mroute_clean_tables(struct mr_table *mrt, bool all); 113e99e88a9SKees Cook static void ipmr_expire_process(struct timer_list *t); 1141da177e4SLinus Torvalds 115f0ad0860SPatrick McHardy #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 116f0ad0860SPatrick McHardy #define ipmr_for_each_table(mrt, net) \ 117f0ad0860SPatrick McHardy list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list) 118f0ad0860SPatrick McHardy 1197b0db857SYuval Mintz static struct mr_table *ipmr_mr_table_iter(struct net *net, 1207b0db857SYuval Mintz struct mr_table *mrt) 1217b0db857SYuval Mintz { 1227b0db857SYuval Mintz struct mr_table *ret; 1237b0db857SYuval Mintz 1247b0db857SYuval Mintz if (!mrt) 1257b0db857SYuval Mintz ret = list_entry_rcu(net->ipv4.mr_tables.next, 1267b0db857SYuval Mintz struct mr_table, list); 1277b0db857SYuval Mintz else 1287b0db857SYuval Mintz ret = list_entry_rcu(mrt->list.next, 1297b0db857SYuval Mintz struct mr_table, list); 1307b0db857SYuval Mintz 1317b0db857SYuval Mintz if (&ret->list == &net->ipv4.mr_tables) 1327b0db857SYuval Mintz return NULL; 1337b0db857SYuval Mintz return ret; 1347b0db857SYuval Mintz } 1357b0db857SYuval Mintz 136f0ad0860SPatrick McHardy static struct mr_table *ipmr_get_table(struct net *net, u32 id) 137f0ad0860SPatrick McHardy { 138f0ad0860SPatrick McHardy struct mr_table *mrt; 139f0ad0860SPatrick McHardy 140f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 141f0ad0860SPatrick McHardy if (mrt->id == id) 142f0ad0860SPatrick McHardy return mrt; 143f0ad0860SPatrick McHardy } 144f0ad0860SPatrick McHardy return NULL; 145f0ad0860SPatrick McHardy } 146f0ad0860SPatrick McHardy 147da91981bSDavid S. Miller static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 148f0ad0860SPatrick McHardy struct mr_table **mrt) 149f0ad0860SPatrick McHardy { 150f0ad0860SPatrick McHardy int err; 15195f4a45dSHannes Frederic Sowa struct ipmr_result res; 15295f4a45dSHannes Frederic Sowa struct fib_lookup_arg arg = { 15395f4a45dSHannes Frederic Sowa .result = &res, 15495f4a45dSHannes Frederic Sowa .flags = FIB_LOOKUP_NOREF, 15595f4a45dSHannes Frederic Sowa }; 156f0ad0860SPatrick McHardy 157e58e4159SDavid Ahern /* update flow if oif or iif point to device enslaved to l3mdev */ 158e58e4159SDavid Ahern l3mdev_update_flow(net, flowi4_to_flowi(flp4)); 159e58e4159SDavid Ahern 160da91981bSDavid S. Miller err = fib_rules_lookup(net->ipv4.mr_rules_ops, 161da91981bSDavid S. Miller flowi4_to_flowi(flp4), 0, &arg); 162f0ad0860SPatrick McHardy if (err < 0) 163f0ad0860SPatrick McHardy return err; 164f0ad0860SPatrick McHardy *mrt = res.mrt; 165f0ad0860SPatrick McHardy return 0; 166f0ad0860SPatrick McHardy } 167f0ad0860SPatrick McHardy 168f0ad0860SPatrick McHardy static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp, 169f0ad0860SPatrick McHardy int flags, struct fib_lookup_arg *arg) 170f0ad0860SPatrick McHardy { 171f0ad0860SPatrick McHardy struct ipmr_result *res = arg->result; 172f0ad0860SPatrick McHardy struct mr_table *mrt; 173f0ad0860SPatrick McHardy 174f0ad0860SPatrick McHardy switch (rule->action) { 175f0ad0860SPatrick McHardy case FR_ACT_TO_TBL: 176f0ad0860SPatrick McHardy break; 177f0ad0860SPatrick McHardy case FR_ACT_UNREACHABLE: 178f0ad0860SPatrick McHardy return -ENETUNREACH; 179f0ad0860SPatrick McHardy case FR_ACT_PROHIBIT: 180f0ad0860SPatrick McHardy return -EACCES; 181f0ad0860SPatrick McHardy case FR_ACT_BLACKHOLE: 182f0ad0860SPatrick McHardy default: 183f0ad0860SPatrick McHardy return -EINVAL; 184f0ad0860SPatrick McHardy } 185f0ad0860SPatrick McHardy 186e58e4159SDavid Ahern arg->table = fib_rule_get_table(rule, arg); 187e58e4159SDavid Ahern 188e58e4159SDavid Ahern mrt = ipmr_get_table(rule->fr_net, arg->table); 18951456b29SIan Morris if (!mrt) 190f0ad0860SPatrick McHardy return -EAGAIN; 191f0ad0860SPatrick McHardy res->mrt = mrt; 192f0ad0860SPatrick McHardy return 0; 193f0ad0860SPatrick McHardy } 194f0ad0860SPatrick McHardy 195f0ad0860SPatrick McHardy static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) 196f0ad0860SPatrick McHardy { 197f0ad0860SPatrick McHardy return 1; 198f0ad0860SPatrick McHardy } 199f0ad0860SPatrick McHardy 200f0ad0860SPatrick McHardy static const struct nla_policy ipmr_rule_policy[FRA_MAX + 1] = { 201f0ad0860SPatrick McHardy FRA_GENERIC_POLICY, 202f0ad0860SPatrick McHardy }; 203f0ad0860SPatrick McHardy 204f0ad0860SPatrick McHardy static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb, 205b16fb418SRoopa Prabhu struct fib_rule_hdr *frh, struct nlattr **tb, 206b16fb418SRoopa Prabhu struct netlink_ext_ack *extack) 207f0ad0860SPatrick McHardy { 208f0ad0860SPatrick McHardy return 0; 209f0ad0860SPatrick McHardy } 210f0ad0860SPatrick McHardy 211f0ad0860SPatrick McHardy static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 212f0ad0860SPatrick McHardy struct nlattr **tb) 213f0ad0860SPatrick McHardy { 214f0ad0860SPatrick McHardy return 1; 215f0ad0860SPatrick McHardy } 216f0ad0860SPatrick McHardy 217f0ad0860SPatrick McHardy static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb, 218f0ad0860SPatrick McHardy struct fib_rule_hdr *frh) 219f0ad0860SPatrick McHardy { 220f0ad0860SPatrick McHardy frh->dst_len = 0; 221f0ad0860SPatrick McHardy frh->src_len = 0; 222f0ad0860SPatrick McHardy frh->tos = 0; 223f0ad0860SPatrick McHardy return 0; 224f0ad0860SPatrick McHardy } 225f0ad0860SPatrick McHardy 22604a6f82cSAndi Kleen static const struct fib_rules_ops __net_initconst ipmr_rules_ops_template = { 22725239ceeSPatrick McHardy .family = RTNL_FAMILY_IPMR, 228f0ad0860SPatrick McHardy .rule_size = sizeof(struct ipmr_rule), 229f0ad0860SPatrick McHardy .addr_size = sizeof(u32), 230f0ad0860SPatrick McHardy .action = ipmr_rule_action, 231f0ad0860SPatrick McHardy .match = ipmr_rule_match, 232f0ad0860SPatrick McHardy .configure = ipmr_rule_configure, 233f0ad0860SPatrick McHardy .compare = ipmr_rule_compare, 234f0ad0860SPatrick McHardy .fill = ipmr_rule_fill, 235f0ad0860SPatrick McHardy .nlgroup = RTNLGRP_IPV4_RULE, 236f0ad0860SPatrick McHardy .policy = ipmr_rule_policy, 237f0ad0860SPatrick McHardy .owner = THIS_MODULE, 238f0ad0860SPatrick McHardy }; 239f0ad0860SPatrick McHardy 240f0ad0860SPatrick McHardy static int __net_init ipmr_rules_init(struct net *net) 241f0ad0860SPatrick McHardy { 242f0ad0860SPatrick McHardy struct fib_rules_ops *ops; 243f0ad0860SPatrick McHardy struct mr_table *mrt; 244f0ad0860SPatrick McHardy int err; 245f0ad0860SPatrick McHardy 246f0ad0860SPatrick McHardy ops = fib_rules_register(&ipmr_rules_ops_template, net); 247f0ad0860SPatrick McHardy if (IS_ERR(ops)) 248f0ad0860SPatrick McHardy return PTR_ERR(ops); 249f0ad0860SPatrick McHardy 250f0ad0860SPatrick McHardy INIT_LIST_HEAD(&net->ipv4.mr_tables); 251f0ad0860SPatrick McHardy 252f0ad0860SPatrick McHardy mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 2531113ebbcSNikolay Aleksandrov if (IS_ERR(mrt)) { 2541113ebbcSNikolay Aleksandrov err = PTR_ERR(mrt); 255f0ad0860SPatrick McHardy goto err1; 256f0ad0860SPatrick McHardy } 257f0ad0860SPatrick McHardy 258f0ad0860SPatrick McHardy err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0); 259f0ad0860SPatrick McHardy if (err < 0) 260f0ad0860SPatrick McHardy goto err2; 261f0ad0860SPatrick McHardy 262f0ad0860SPatrick McHardy net->ipv4.mr_rules_ops = ops; 263f0ad0860SPatrick McHardy return 0; 264f0ad0860SPatrick McHardy 265f0ad0860SPatrick McHardy err2: 266f243e5a7SWANG Cong ipmr_free_table(mrt); 267f0ad0860SPatrick McHardy err1: 268f0ad0860SPatrick McHardy fib_rules_unregister(ops); 269f0ad0860SPatrick McHardy return err; 270f0ad0860SPatrick McHardy } 271f0ad0860SPatrick McHardy 272f0ad0860SPatrick McHardy static void __net_exit ipmr_rules_exit(struct net *net) 273f0ad0860SPatrick McHardy { 274f0ad0860SPatrick McHardy struct mr_table *mrt, *next; 275f0ad0860SPatrick McHardy 276ed785309SWANG Cong rtnl_lock(); 277035320d5SEric Dumazet list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) { 278035320d5SEric Dumazet list_del(&mrt->list); 279acbb219dSFrancesco Ruggeri ipmr_free_table(mrt); 280035320d5SEric Dumazet } 281f0ad0860SPatrick McHardy fib_rules_unregister(net->ipv4.mr_rules_ops); 282419df12fSWANG Cong rtnl_unlock(); 283f0ad0860SPatrick McHardy } 2844d65b948SYotam Gigi 2854d65b948SYotam Gigi static int ipmr_rules_dump(struct net *net, struct notifier_block *nb) 2864d65b948SYotam Gigi { 2874d65b948SYotam Gigi return fib_rules_dump(net, nb, RTNL_FAMILY_IPMR); 2884d65b948SYotam Gigi } 2894d65b948SYotam Gigi 2904d65b948SYotam Gigi static unsigned int ipmr_rules_seq_read(struct net *net) 2914d65b948SYotam Gigi { 2924d65b948SYotam Gigi return fib_rules_seq_read(net, RTNL_FAMILY_IPMR); 2934d65b948SYotam Gigi } 294478e4c2fSYotam Gigi 295478e4c2fSYotam Gigi bool ipmr_rule_default(const struct fib_rule *rule) 296478e4c2fSYotam Gigi { 297478e4c2fSYotam Gigi return fib_rule_matchall(rule) && rule->table == RT_TABLE_DEFAULT; 298478e4c2fSYotam Gigi } 299478e4c2fSYotam Gigi EXPORT_SYMBOL(ipmr_rule_default); 300f0ad0860SPatrick McHardy #else 301f0ad0860SPatrick McHardy #define ipmr_for_each_table(mrt, net) \ 302f0ad0860SPatrick McHardy for (mrt = net->ipv4.mrt; mrt; mrt = NULL) 303f0ad0860SPatrick McHardy 3047b0db857SYuval Mintz static struct mr_table *ipmr_mr_table_iter(struct net *net, 3057b0db857SYuval Mintz struct mr_table *mrt) 3067b0db857SYuval Mintz { 3077b0db857SYuval Mintz if (!mrt) 3087b0db857SYuval Mintz return net->ipv4.mrt; 3097b0db857SYuval Mintz return NULL; 3107b0db857SYuval Mintz } 3117b0db857SYuval Mintz 312f0ad0860SPatrick McHardy static struct mr_table *ipmr_get_table(struct net *net, u32 id) 313f0ad0860SPatrick McHardy { 314f0ad0860SPatrick McHardy return net->ipv4.mrt; 315f0ad0860SPatrick McHardy } 316f0ad0860SPatrick McHardy 317da91981bSDavid S. Miller static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 318f0ad0860SPatrick McHardy struct mr_table **mrt) 319f0ad0860SPatrick McHardy { 320f0ad0860SPatrick McHardy *mrt = net->ipv4.mrt; 321f0ad0860SPatrick McHardy return 0; 322f0ad0860SPatrick McHardy } 323f0ad0860SPatrick McHardy 324f0ad0860SPatrick McHardy static int __net_init ipmr_rules_init(struct net *net) 325f0ad0860SPatrick McHardy { 3261113ebbcSNikolay Aleksandrov struct mr_table *mrt; 3271113ebbcSNikolay Aleksandrov 3281113ebbcSNikolay Aleksandrov mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 3291113ebbcSNikolay Aleksandrov if (IS_ERR(mrt)) 3301113ebbcSNikolay Aleksandrov return PTR_ERR(mrt); 3311113ebbcSNikolay Aleksandrov net->ipv4.mrt = mrt; 3321113ebbcSNikolay Aleksandrov return 0; 333f0ad0860SPatrick McHardy } 334f0ad0860SPatrick McHardy 335f0ad0860SPatrick McHardy static void __net_exit ipmr_rules_exit(struct net *net) 336f0ad0860SPatrick McHardy { 337ed785309SWANG Cong rtnl_lock(); 338acbb219dSFrancesco Ruggeri ipmr_free_table(net->ipv4.mrt); 339ed785309SWANG Cong net->ipv4.mrt = NULL; 340ed785309SWANG Cong rtnl_unlock(); 341f0ad0860SPatrick McHardy } 3424d65b948SYotam Gigi 3434d65b948SYotam Gigi static int ipmr_rules_dump(struct net *net, struct notifier_block *nb) 3444d65b948SYotam Gigi { 3454d65b948SYotam Gigi return 0; 3464d65b948SYotam Gigi } 3474d65b948SYotam Gigi 3484d65b948SYotam Gigi static unsigned int ipmr_rules_seq_read(struct net *net) 3494d65b948SYotam Gigi { 3504d65b948SYotam Gigi return 0; 3514d65b948SYotam Gigi } 352478e4c2fSYotam Gigi 353478e4c2fSYotam Gigi bool ipmr_rule_default(const struct fib_rule *rule) 354478e4c2fSYotam Gigi { 355478e4c2fSYotam Gigi return true; 356478e4c2fSYotam Gigi } 357478e4c2fSYotam Gigi EXPORT_SYMBOL(ipmr_rule_default); 358f0ad0860SPatrick McHardy #endif 359f0ad0860SPatrick McHardy 3608fb472c0SNikolay Aleksandrov static inline int ipmr_hash_cmp(struct rhashtable_compare_arg *arg, 3618fb472c0SNikolay Aleksandrov const void *ptr) 3628fb472c0SNikolay Aleksandrov { 3638fb472c0SNikolay Aleksandrov const struct mfc_cache_cmp_arg *cmparg = arg->key; 3648fb472c0SNikolay Aleksandrov struct mfc_cache *c = (struct mfc_cache *)ptr; 3658fb472c0SNikolay Aleksandrov 3668fb472c0SNikolay Aleksandrov return cmparg->mfc_mcastgrp != c->mfc_mcastgrp || 3678fb472c0SNikolay Aleksandrov cmparg->mfc_origin != c->mfc_origin; 3688fb472c0SNikolay Aleksandrov } 3698fb472c0SNikolay Aleksandrov 3708fb472c0SNikolay Aleksandrov static const struct rhashtable_params ipmr_rht_params = { 371494fff56SYuval Mintz .head_offset = offsetof(struct mr_mfc, mnode), 3728fb472c0SNikolay Aleksandrov .key_offset = offsetof(struct mfc_cache, cmparg), 3738fb472c0SNikolay Aleksandrov .key_len = sizeof(struct mfc_cache_cmp_arg), 3748fb472c0SNikolay Aleksandrov .nelem_hint = 3, 3758fb472c0SNikolay Aleksandrov .locks_mul = 1, 3768fb472c0SNikolay Aleksandrov .obj_cmpfn = ipmr_hash_cmp, 3778fb472c0SNikolay Aleksandrov .automatic_shrinking = true, 3788fb472c0SNikolay Aleksandrov }; 3798fb472c0SNikolay Aleksandrov 3800bbbf0e7SYuval Mintz static void ipmr_new_table_set(struct mr_table *mrt, 3810bbbf0e7SYuval Mintz struct net *net) 3820bbbf0e7SYuval Mintz { 3830bbbf0e7SYuval Mintz #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 3840bbbf0e7SYuval Mintz list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables); 3850bbbf0e7SYuval Mintz #endif 3860bbbf0e7SYuval Mintz } 3870bbbf0e7SYuval Mintz 388845c9a7aSYuval Mintz static struct mfc_cache_cmp_arg ipmr_mr_table_ops_cmparg_any = { 389845c9a7aSYuval Mintz .mfc_mcastgrp = htonl(INADDR_ANY), 390845c9a7aSYuval Mintz .mfc_origin = htonl(INADDR_ANY), 391845c9a7aSYuval Mintz }; 392845c9a7aSYuval Mintz 393845c9a7aSYuval Mintz static struct mr_table_ops ipmr_mr_table_ops = { 394845c9a7aSYuval Mintz .rht_params = &ipmr_rht_params, 395845c9a7aSYuval Mintz .cmparg_any = &ipmr_mr_table_ops_cmparg_any, 396845c9a7aSYuval Mintz }; 397845c9a7aSYuval Mintz 398f0ad0860SPatrick McHardy static struct mr_table *ipmr_new_table(struct net *net, u32 id) 399f0ad0860SPatrick McHardy { 400f0ad0860SPatrick McHardy struct mr_table *mrt; 401f0ad0860SPatrick McHardy 4021113ebbcSNikolay Aleksandrov /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */ 4031113ebbcSNikolay Aleksandrov if (id != RT_TABLE_DEFAULT && id >= 1000000000) 4041113ebbcSNikolay Aleksandrov return ERR_PTR(-EINVAL); 4051113ebbcSNikolay Aleksandrov 406f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, id); 40700db4124SIan Morris if (mrt) 408f0ad0860SPatrick McHardy return mrt; 409f0ad0860SPatrick McHardy 410845c9a7aSYuval Mintz return mr_table_alloc(net, id, &ipmr_mr_table_ops, 4110bbbf0e7SYuval Mintz ipmr_expire_process, ipmr_new_table_set); 412f0ad0860SPatrick McHardy } 4131da177e4SLinus Torvalds 414acbb219dSFrancesco Ruggeri static void ipmr_free_table(struct mr_table *mrt) 415acbb219dSFrancesco Ruggeri { 416acbb219dSFrancesco Ruggeri del_timer_sync(&mrt->ipmr_expire_timer); 4170e615e96SNikolay Aleksandrov mroute_clean_tables(mrt, true); 4188fb472c0SNikolay Aleksandrov rhltable_destroy(&mrt->mfc_hash); 419acbb219dSFrancesco Ruggeri kfree(mrt); 420acbb219dSFrancesco Ruggeri } 421acbb219dSFrancesco Ruggeri 4221da177e4SLinus Torvalds /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */ 4231da177e4SLinus Torvalds 424d607032dSWang Chen static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v) 425d607032dSWang Chen { 4264feb88e5SBenjamin Thery struct net *net = dev_net(dev); 4274feb88e5SBenjamin Thery 428d607032dSWang Chen dev_close(dev); 429d607032dSWang Chen 4304feb88e5SBenjamin Thery dev = __dev_get_by_name(net, "tunl0"); 431d607032dSWang Chen if (dev) { 4325bc3eb7eSStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 433d607032dSWang Chen struct ifreq ifr; 434d607032dSWang Chen struct ip_tunnel_parm p; 435d607032dSWang Chen 436d607032dSWang Chen memset(&p, 0, sizeof(p)); 437d607032dSWang Chen p.iph.daddr = v->vifc_rmt_addr.s_addr; 438d607032dSWang Chen p.iph.saddr = v->vifc_lcl_addr.s_addr; 439d607032dSWang Chen p.iph.version = 4; 440d607032dSWang Chen p.iph.ihl = 5; 441d607032dSWang Chen p.iph.protocol = IPPROTO_IPIP; 442d607032dSWang Chen sprintf(p.name, "dvmrp%d", v->vifc_vifi); 443d607032dSWang Chen ifr.ifr_ifru.ifru_data = (__force void __user *)&p; 444d607032dSWang Chen 4455bc3eb7eSStephen Hemminger if (ops->ndo_do_ioctl) { 4465bc3eb7eSStephen Hemminger mm_segment_t oldfs = get_fs(); 4475bc3eb7eSStephen Hemminger 4485bc3eb7eSStephen Hemminger set_fs(KERNEL_DS); 4495bc3eb7eSStephen Hemminger ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL); 450d607032dSWang Chen set_fs(oldfs); 451d607032dSWang Chen } 452d607032dSWang Chen } 4535bc3eb7eSStephen Hemminger } 454d607032dSWang Chen 455a0b47736SNikolay Aleksandrov /* Initialize ipmr pimreg/tunnel in_device */ 456a0b47736SNikolay Aleksandrov static bool ipmr_init_vif_indev(const struct net_device *dev) 457a0b47736SNikolay Aleksandrov { 458a0b47736SNikolay Aleksandrov struct in_device *in_dev; 459a0b47736SNikolay Aleksandrov 460a0b47736SNikolay Aleksandrov ASSERT_RTNL(); 461a0b47736SNikolay Aleksandrov 462a0b47736SNikolay Aleksandrov in_dev = __in_dev_get_rtnl(dev); 463a0b47736SNikolay Aleksandrov if (!in_dev) 464a0b47736SNikolay Aleksandrov return false; 465a0b47736SNikolay Aleksandrov ipv4_devconf_setall(in_dev); 466a0b47736SNikolay Aleksandrov neigh_parms_data_state_setall(in_dev->arp_parms); 467a0b47736SNikolay Aleksandrov IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; 468a0b47736SNikolay Aleksandrov 469a0b47736SNikolay Aleksandrov return true; 470a0b47736SNikolay Aleksandrov } 471a0b47736SNikolay Aleksandrov 4727ef8f65dSNikolay Aleksandrov static struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v) 4731da177e4SLinus Torvalds { 4741da177e4SLinus Torvalds struct net_device *dev; 4751da177e4SLinus Torvalds 4764feb88e5SBenjamin Thery dev = __dev_get_by_name(net, "tunl0"); 4771da177e4SLinus Torvalds 4781da177e4SLinus Torvalds if (dev) { 4795bc3eb7eSStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 4801da177e4SLinus Torvalds int err; 4811da177e4SLinus Torvalds struct ifreq ifr; 4821da177e4SLinus Torvalds struct ip_tunnel_parm p; 4831da177e4SLinus Torvalds 4841da177e4SLinus Torvalds memset(&p, 0, sizeof(p)); 4851da177e4SLinus Torvalds p.iph.daddr = v->vifc_rmt_addr.s_addr; 4861da177e4SLinus Torvalds p.iph.saddr = v->vifc_lcl_addr.s_addr; 4871da177e4SLinus Torvalds p.iph.version = 4; 4881da177e4SLinus Torvalds p.iph.ihl = 5; 4891da177e4SLinus Torvalds p.iph.protocol = IPPROTO_IPIP; 4901da177e4SLinus Torvalds sprintf(p.name, "dvmrp%d", v->vifc_vifi); 491ba93ef74SStephen Hemminger ifr.ifr_ifru.ifru_data = (__force void __user *)&p; 4921da177e4SLinus Torvalds 4935bc3eb7eSStephen Hemminger if (ops->ndo_do_ioctl) { 4945bc3eb7eSStephen Hemminger mm_segment_t oldfs = get_fs(); 4955bc3eb7eSStephen Hemminger 4965bc3eb7eSStephen Hemminger set_fs(KERNEL_DS); 4975bc3eb7eSStephen Hemminger err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL); 4981da177e4SLinus Torvalds set_fs(oldfs); 499a8cb16ddSEric Dumazet } else { 5005bc3eb7eSStephen Hemminger err = -EOPNOTSUPP; 501a8cb16ddSEric Dumazet } 5021da177e4SLinus Torvalds dev = NULL; 5031da177e4SLinus Torvalds 5044feb88e5SBenjamin Thery if (err == 0 && 5054feb88e5SBenjamin Thery (dev = __dev_get_by_name(net, p.name)) != NULL) { 5061da177e4SLinus Torvalds dev->flags |= IFF_MULTICAST; 507a0b47736SNikolay Aleksandrov if (!ipmr_init_vif_indev(dev)) 5081da177e4SLinus Torvalds goto failure; 5091da177e4SLinus Torvalds if (dev_open(dev)) 5101da177e4SLinus Torvalds goto failure; 5117dc00c82SWang Chen dev_hold(dev); 5121da177e4SLinus Torvalds } 5131da177e4SLinus Torvalds } 5141da177e4SLinus Torvalds return dev; 5151da177e4SLinus Torvalds 5161da177e4SLinus Torvalds failure: 5171da177e4SLinus Torvalds unregister_netdevice(dev); 5181da177e4SLinus Torvalds return NULL; 5191da177e4SLinus Torvalds } 5201da177e4SLinus Torvalds 521c316c629SNikolay Aleksandrov #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) 5226fef4c0cSStephen Hemminger static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) 5231da177e4SLinus Torvalds { 5244feb88e5SBenjamin Thery struct net *net = dev_net(dev); 525f0ad0860SPatrick McHardy struct mr_table *mrt; 526da91981bSDavid S. Miller struct flowi4 fl4 = { 527da91981bSDavid S. Miller .flowi4_oif = dev->ifindex, 5286a662719SCong Wang .flowi4_iif = skb->skb_iif ? : LOOPBACK_IFINDEX, 529da91981bSDavid S. Miller .flowi4_mark = skb->mark, 530f0ad0860SPatrick McHardy }; 531f0ad0860SPatrick McHardy int err; 532f0ad0860SPatrick McHardy 533da91981bSDavid S. Miller err = ipmr_fib_lookup(net, &fl4, &mrt); 534e40dbc51SBen Greear if (err < 0) { 535e40dbc51SBen Greear kfree_skb(skb); 536f0ad0860SPatrick McHardy return err; 537e40dbc51SBen Greear } 5384feb88e5SBenjamin Thery 5391da177e4SLinus Torvalds read_lock(&mrt_lock); 540cf3677aeSPavel Emelyanov dev->stats.tx_bytes += skb->len; 541cf3677aeSPavel Emelyanov dev->stats.tx_packets++; 5420c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, IGMPMSG_WHOLEPKT); 5431da177e4SLinus Torvalds read_unlock(&mrt_lock); 5441da177e4SLinus Torvalds kfree_skb(skb); 5456ed10654SPatrick McHardy return NETDEV_TX_OK; 5461da177e4SLinus Torvalds } 5471da177e4SLinus Torvalds 548ee9b9596SNicolas Dichtel static int reg_vif_get_iflink(const struct net_device *dev) 549ee9b9596SNicolas Dichtel { 550ee9b9596SNicolas Dichtel return 0; 551ee9b9596SNicolas Dichtel } 552ee9b9596SNicolas Dichtel 553007c3838SStephen Hemminger static const struct net_device_ops reg_vif_netdev_ops = { 554007c3838SStephen Hemminger .ndo_start_xmit = reg_vif_xmit, 555ee9b9596SNicolas Dichtel .ndo_get_iflink = reg_vif_get_iflink, 556007c3838SStephen Hemminger }; 557007c3838SStephen Hemminger 5581da177e4SLinus Torvalds static void reg_vif_setup(struct net_device *dev) 5591da177e4SLinus Torvalds { 5601da177e4SLinus Torvalds dev->type = ARPHRD_PIMREG; 56146f25dffSKris Katterjohn dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8; 5621da177e4SLinus Torvalds dev->flags = IFF_NOARP; 56370cb4a45SHimangi Saraogi dev->netdev_ops = ®_vif_netdev_ops; 564cf124db5SDavid S. Miller dev->needs_free_netdev = true; 565403dbb97STom Goff dev->features |= NETIF_F_NETNS_LOCAL; 5661da177e4SLinus Torvalds } 5671da177e4SLinus Torvalds 568f0ad0860SPatrick McHardy static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) 5691da177e4SLinus Torvalds { 5701da177e4SLinus Torvalds struct net_device *dev; 571f0ad0860SPatrick McHardy char name[IFNAMSIZ]; 5721da177e4SLinus Torvalds 573f0ad0860SPatrick McHardy if (mrt->id == RT_TABLE_DEFAULT) 574f0ad0860SPatrick McHardy sprintf(name, "pimreg"); 575f0ad0860SPatrick McHardy else 576f0ad0860SPatrick McHardy sprintf(name, "pimreg%u", mrt->id); 577f0ad0860SPatrick McHardy 578c835a677STom Gundersen dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup); 5791da177e4SLinus Torvalds 58051456b29SIan Morris if (!dev) 5811da177e4SLinus Torvalds return NULL; 5821da177e4SLinus Torvalds 583403dbb97STom Goff dev_net_set(dev, net); 584403dbb97STom Goff 5851da177e4SLinus Torvalds if (register_netdevice(dev)) { 5861da177e4SLinus Torvalds free_netdev(dev); 5871da177e4SLinus Torvalds return NULL; 5881da177e4SLinus Torvalds } 5891da177e4SLinus Torvalds 590a0b47736SNikolay Aleksandrov if (!ipmr_init_vif_indev(dev)) 5911da177e4SLinus Torvalds goto failure; 5921da177e4SLinus Torvalds if (dev_open(dev)) 5931da177e4SLinus Torvalds goto failure; 5941da177e4SLinus Torvalds 5957dc00c82SWang Chen dev_hold(dev); 5967dc00c82SWang Chen 5971da177e4SLinus Torvalds return dev; 5981da177e4SLinus Torvalds 5991da177e4SLinus Torvalds failure: 6001da177e4SLinus Torvalds unregister_netdevice(dev); 6011da177e4SLinus Torvalds return NULL; 6021da177e4SLinus Torvalds } 603c316c629SNikolay Aleksandrov 604c316c629SNikolay Aleksandrov /* called with rcu_read_lock() */ 605c316c629SNikolay Aleksandrov static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb, 606c316c629SNikolay Aleksandrov unsigned int pimlen) 607c316c629SNikolay Aleksandrov { 608c316c629SNikolay Aleksandrov struct net_device *reg_dev = NULL; 609c316c629SNikolay Aleksandrov struct iphdr *encap; 610c316c629SNikolay Aleksandrov 611c316c629SNikolay Aleksandrov encap = (struct iphdr *)(skb_transport_header(skb) + pimlen); 6127ef8f65dSNikolay Aleksandrov /* Check that: 613c316c629SNikolay Aleksandrov * a. packet is really sent to a multicast group 614c316c629SNikolay Aleksandrov * b. packet is not a NULL-REGISTER 615c316c629SNikolay Aleksandrov * c. packet is not truncated 616c316c629SNikolay Aleksandrov */ 617c316c629SNikolay Aleksandrov if (!ipv4_is_multicast(encap->daddr) || 618c316c629SNikolay Aleksandrov encap->tot_len == 0 || 619c316c629SNikolay Aleksandrov ntohs(encap->tot_len) + pimlen > skb->len) 620c316c629SNikolay Aleksandrov return 1; 621c316c629SNikolay Aleksandrov 622c316c629SNikolay Aleksandrov read_lock(&mrt_lock); 623c316c629SNikolay Aleksandrov if (mrt->mroute_reg_vif_num >= 0) 624c316c629SNikolay Aleksandrov reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev; 625c316c629SNikolay Aleksandrov read_unlock(&mrt_lock); 626c316c629SNikolay Aleksandrov 627c316c629SNikolay Aleksandrov if (!reg_dev) 628c316c629SNikolay Aleksandrov return 1; 629c316c629SNikolay Aleksandrov 630c316c629SNikolay Aleksandrov skb->mac_header = skb->network_header; 631c316c629SNikolay Aleksandrov skb_pull(skb, (u8 *)encap - skb->data); 632c316c629SNikolay Aleksandrov skb_reset_network_header(skb); 633c316c629SNikolay Aleksandrov skb->protocol = htons(ETH_P_IP); 634c316c629SNikolay Aleksandrov skb->ip_summed = CHECKSUM_NONE; 635c316c629SNikolay Aleksandrov 636c316c629SNikolay Aleksandrov skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev)); 637c316c629SNikolay Aleksandrov 638c316c629SNikolay Aleksandrov netif_rx(skb); 639c316c629SNikolay Aleksandrov 640c316c629SNikolay Aleksandrov return NET_RX_SUCCESS; 641c316c629SNikolay Aleksandrov } 642c316c629SNikolay Aleksandrov #else 643c316c629SNikolay Aleksandrov static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) 644c316c629SNikolay Aleksandrov { 645c316c629SNikolay Aleksandrov return NULL; 646c316c629SNikolay Aleksandrov } 6471da177e4SLinus Torvalds #endif 6481da177e4SLinus Torvalds 649b362053aSYotam Gigi static int call_ipmr_vif_entry_notifiers(struct net *net, 650b362053aSYotam Gigi enum fib_event_type event_type, 651b362053aSYotam Gigi struct vif_device *vif, 652b362053aSYotam Gigi vifi_t vif_index, u32 tb_id) 653b362053aSYotam Gigi { 654bc67a0daSYuval Mintz return mr_call_vif_notifiers(net, RTNL_FAMILY_IPMR, event_type, 655bc67a0daSYuval Mintz vif, vif_index, tb_id, 656bc67a0daSYuval Mintz &net->ipv4.ipmr_seq); 657b362053aSYotam Gigi } 658b362053aSYotam Gigi 659b362053aSYotam Gigi static int call_ipmr_mfc_entry_notifiers(struct net *net, 660b362053aSYotam Gigi enum fib_event_type event_type, 661b362053aSYotam Gigi struct mfc_cache *mfc, u32 tb_id) 662b362053aSYotam Gigi { 66354c4cad9SYuval Mintz return mr_call_mfc_notifiers(net, RTNL_FAMILY_IPMR, event_type, 66454c4cad9SYuval Mintz &mfc->_c, tb_id, &net->ipv4.ipmr_seq); 665b362053aSYotam Gigi } 666b362053aSYotam Gigi 6672c53040fSBen Hutchings /** 6682c53040fSBen Hutchings * vif_delete - Delete a VIF entry 6697dc00c82SWang Chen * @notify: Set to 1, if the caller is a notifier_call 6701da177e4SLinus Torvalds */ 6710c12295aSPatrick McHardy static int vif_delete(struct mr_table *mrt, int vifi, int notify, 672d17fa6faSEric Dumazet struct list_head *head) 6731da177e4SLinus Torvalds { 674b362053aSYotam Gigi struct net *net = read_pnet(&mrt->net); 6751da177e4SLinus Torvalds struct vif_device *v; 6761da177e4SLinus Torvalds struct net_device *dev; 6771da177e4SLinus Torvalds struct in_device *in_dev; 6781da177e4SLinus Torvalds 6790c12295aSPatrick McHardy if (vifi < 0 || vifi >= mrt->maxvif) 6801da177e4SLinus Torvalds return -EADDRNOTAVAIL; 6811da177e4SLinus Torvalds 6820c12295aSPatrick McHardy v = &mrt->vif_table[vifi]; 6831da177e4SLinus Torvalds 684b362053aSYotam Gigi if (VIF_EXISTS(mrt, vifi)) 685b362053aSYotam Gigi call_ipmr_vif_entry_notifiers(net, FIB_EVENT_VIF_DEL, v, vifi, 686b362053aSYotam Gigi mrt->id); 687b362053aSYotam Gigi 6881da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 6891da177e4SLinus Torvalds dev = v->dev; 6901da177e4SLinus Torvalds v->dev = NULL; 6911da177e4SLinus Torvalds 6921da177e4SLinus Torvalds if (!dev) { 6931da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 6941da177e4SLinus Torvalds return -EADDRNOTAVAIL; 6951da177e4SLinus Torvalds } 6961da177e4SLinus Torvalds 6970c12295aSPatrick McHardy if (vifi == mrt->mroute_reg_vif_num) 6980c12295aSPatrick McHardy mrt->mroute_reg_vif_num = -1; 6991da177e4SLinus Torvalds 7000c12295aSPatrick McHardy if (vifi + 1 == mrt->maxvif) { 7011da177e4SLinus Torvalds int tmp; 702a8cb16ddSEric Dumazet 7031da177e4SLinus Torvalds for (tmp = vifi - 1; tmp >= 0; tmp--) { 7040c12295aSPatrick McHardy if (VIF_EXISTS(mrt, tmp)) 7051da177e4SLinus Torvalds break; 7061da177e4SLinus Torvalds } 7070c12295aSPatrick McHardy mrt->maxvif = tmp+1; 7081da177e4SLinus Torvalds } 7091da177e4SLinus Torvalds 7101da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 7111da177e4SLinus Torvalds 7121da177e4SLinus Torvalds dev_set_allmulti(dev, -1); 7131da177e4SLinus Torvalds 714a8cb16ddSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 715a8cb16ddSEric Dumazet if (in_dev) { 71642f811b8SHerbert Xu IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--; 7173b022865SDavid Ahern inet_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, 718d67b8c61SNicolas Dichtel NETCONFA_MC_FORWARDING, 719d67b8c61SNicolas Dichtel dev->ifindex, &in_dev->cnf); 7201da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 7211da177e4SLinus Torvalds } 7221da177e4SLinus Torvalds 7237dc00c82SWang Chen if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER) && !notify) 724d17fa6faSEric Dumazet unregister_netdevice_queue(dev, head); 7251da177e4SLinus Torvalds 7261da177e4SLinus Torvalds dev_put(dev); 7271da177e4SLinus Torvalds return 0; 7281da177e4SLinus Torvalds } 7291da177e4SLinus Torvalds 730a8c9486bSEric Dumazet static void ipmr_cache_free_rcu(struct rcu_head *head) 731a8c9486bSEric Dumazet { 732494fff56SYuval Mintz struct mr_mfc *c = container_of(head, struct mr_mfc, rcu); 733a8c9486bSEric Dumazet 734494fff56SYuval Mintz kmem_cache_free(mrt_cachep, (struct mfc_cache *)c); 735a8c9486bSEric Dumazet } 736a8c9486bSEric Dumazet 7378c13af2aSYuval Mintz static void ipmr_cache_free(struct mfc_cache *c) 7385c0a66f5SBenjamin Thery { 739494fff56SYuval Mintz call_rcu(&c->_c.rcu, ipmr_cache_free_rcu); 7405c0a66f5SBenjamin Thery } 7415c0a66f5SBenjamin Thery 7421da177e4SLinus Torvalds /* Destroy an unresolved cache entry, killing queued skbs 743a8cb16ddSEric Dumazet * and reporting error to netlink readers. 7441da177e4SLinus Torvalds */ 7450c12295aSPatrick McHardy static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c) 7461da177e4SLinus Torvalds { 7478de53dfbSPatrick McHardy struct net *net = read_pnet(&mrt->net); 7481da177e4SLinus Torvalds struct sk_buff *skb; 7499ef1d4c7SPatrick McHardy struct nlmsgerr *e; 7501da177e4SLinus Torvalds 7510c12295aSPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len); 7521da177e4SLinus Torvalds 753494fff56SYuval Mintz while ((skb = skb_dequeue(&c->_c.mfc_un.unres.unresolved))) { 754eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->version == 0) { 755af72868bSJohannes Berg struct nlmsghdr *nlh = skb_pull(skb, 756af72868bSJohannes Berg sizeof(struct iphdr)); 7571da177e4SLinus Torvalds nlh->nlmsg_type = NLMSG_ERROR; 758573ce260SHong zhi guo nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 7591da177e4SLinus Torvalds skb_trim(skb, nlh->nlmsg_len); 760573ce260SHong zhi guo e = nlmsg_data(nlh); 7619ef1d4c7SPatrick McHardy e->error = -ETIMEDOUT; 7629ef1d4c7SPatrick McHardy memset(&e->msg, 0, sizeof(e->msg)); 7632942e900SThomas Graf 76415e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 765a8cb16ddSEric Dumazet } else { 7661da177e4SLinus Torvalds kfree_skb(skb); 7671da177e4SLinus Torvalds } 768a8cb16ddSEric Dumazet } 7691da177e4SLinus Torvalds 7705c0a66f5SBenjamin Thery ipmr_cache_free(c); 7711da177e4SLinus Torvalds } 7721da177e4SLinus Torvalds 773e258beb2SPatrick McHardy /* Timer process for the unresolved queue. */ 774e99e88a9SKees Cook static void ipmr_expire_process(struct timer_list *t) 7751da177e4SLinus Torvalds { 776e99e88a9SKees Cook struct mr_table *mrt = from_timer(mrt, t, ipmr_expire_timer); 777494fff56SYuval Mintz struct mr_mfc *c, *next; 7781da177e4SLinus Torvalds unsigned long expires; 779494fff56SYuval Mintz unsigned long now; 7801da177e4SLinus Torvalds 7811da177e4SLinus Torvalds if (!spin_trylock(&mfc_unres_lock)) { 7820c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10); 7831da177e4SLinus Torvalds return; 7841da177e4SLinus Torvalds } 7851da177e4SLinus Torvalds 7860c12295aSPatrick McHardy if (list_empty(&mrt->mfc_unres_queue)) 7871da177e4SLinus Torvalds goto out; 7881da177e4SLinus Torvalds 7891da177e4SLinus Torvalds now = jiffies; 7901da177e4SLinus Torvalds expires = 10*HZ; 7911da177e4SLinus Torvalds 7920c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) { 7931da177e4SLinus Torvalds if (time_after(c->mfc_un.unres.expires, now)) { 7941da177e4SLinus Torvalds unsigned long interval = c->mfc_un.unres.expires - now; 7951da177e4SLinus Torvalds if (interval < expires) 7961da177e4SLinus Torvalds expires = interval; 7971da177e4SLinus Torvalds continue; 7981da177e4SLinus Torvalds } 7991da177e4SLinus Torvalds 800862465f2SPatrick McHardy list_del(&c->list); 801494fff56SYuval Mintz mroute_netlink_event(mrt, (struct mfc_cache *)c, RTM_DELROUTE); 802494fff56SYuval Mintz ipmr_destroy_unres(mrt, (struct mfc_cache *)c); 8031da177e4SLinus Torvalds } 8041da177e4SLinus Torvalds 8050c12295aSPatrick McHardy if (!list_empty(&mrt->mfc_unres_queue)) 8060c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies + expires); 8071da177e4SLinus Torvalds 8081da177e4SLinus Torvalds out: 8091da177e4SLinus Torvalds spin_unlock(&mfc_unres_lock); 8101da177e4SLinus Torvalds } 8111da177e4SLinus Torvalds 8121da177e4SLinus Torvalds /* Fill oifs list. It is called under write locked mrt_lock. */ 813494fff56SYuval Mintz static void ipmr_update_thresholds(struct mr_table *mrt, struct mr_mfc *cache, 814d658f8a0SPatrick McHardy unsigned char *ttls) 8151da177e4SLinus Torvalds { 8161da177e4SLinus Torvalds int vifi; 8171da177e4SLinus Torvalds 8181da177e4SLinus Torvalds cache->mfc_un.res.minvif = MAXVIFS; 8191da177e4SLinus Torvalds cache->mfc_un.res.maxvif = 0; 8201da177e4SLinus Torvalds memset(cache->mfc_un.res.ttls, 255, MAXVIFS); 8211da177e4SLinus Torvalds 8220c12295aSPatrick McHardy for (vifi = 0; vifi < mrt->maxvif; vifi++) { 8230c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vifi) && 824cf958ae3SBenjamin Thery ttls[vifi] && ttls[vifi] < 255) { 8251da177e4SLinus Torvalds cache->mfc_un.res.ttls[vifi] = ttls[vifi]; 8261da177e4SLinus Torvalds if (cache->mfc_un.res.minvif > vifi) 8271da177e4SLinus Torvalds cache->mfc_un.res.minvif = vifi; 8281da177e4SLinus Torvalds if (cache->mfc_un.res.maxvif <= vifi) 8291da177e4SLinus Torvalds cache->mfc_un.res.maxvif = vifi + 1; 8301da177e4SLinus Torvalds } 8311da177e4SLinus Torvalds } 83290b5ca17SNikolay Aleksandrov cache->mfc_un.res.lastuse = jiffies; 8331da177e4SLinus Torvalds } 8341da177e4SLinus Torvalds 8350c12295aSPatrick McHardy static int vif_add(struct net *net, struct mr_table *mrt, 8360c12295aSPatrick McHardy struct vifctl *vifc, int mrtsock) 8371da177e4SLinus Torvalds { 8381da177e4SLinus Torvalds int vifi = vifc->vifc_vifi; 8395d8b3e69SYotam Gigi struct switchdev_attr attr = { 8405d8b3e69SYotam Gigi .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID, 8415d8b3e69SYotam Gigi }; 8420c12295aSPatrick McHardy struct vif_device *v = &mrt->vif_table[vifi]; 8431da177e4SLinus Torvalds struct net_device *dev; 8441da177e4SLinus Torvalds struct in_device *in_dev; 845d607032dSWang Chen int err; 8461da177e4SLinus Torvalds 8471da177e4SLinus Torvalds /* Is vif busy ? */ 8480c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vifi)) 8491da177e4SLinus Torvalds return -EADDRINUSE; 8501da177e4SLinus Torvalds 8511da177e4SLinus Torvalds switch (vifc->vifc_flags) { 8521da177e4SLinus Torvalds case VIFF_REGISTER: 8531973a4eaSNikolay Aleksandrov if (!ipmr_pimsm_enabled()) 854c316c629SNikolay Aleksandrov return -EINVAL; 855c316c629SNikolay Aleksandrov /* Special Purpose VIF in PIM 8561da177e4SLinus Torvalds * All the packets will be sent to the daemon 8571da177e4SLinus Torvalds */ 8580c12295aSPatrick McHardy if (mrt->mroute_reg_vif_num >= 0) 8591da177e4SLinus Torvalds return -EADDRINUSE; 860f0ad0860SPatrick McHardy dev = ipmr_reg_vif(net, mrt); 8611da177e4SLinus Torvalds if (!dev) 8621da177e4SLinus Torvalds return -ENOBUFS; 863d607032dSWang Chen err = dev_set_allmulti(dev, 1); 864d607032dSWang Chen if (err) { 865d607032dSWang Chen unregister_netdevice(dev); 8667dc00c82SWang Chen dev_put(dev); 867d607032dSWang Chen return err; 868d607032dSWang Chen } 8691da177e4SLinus Torvalds break; 8701da177e4SLinus Torvalds case VIFF_TUNNEL: 8714feb88e5SBenjamin Thery dev = ipmr_new_tunnel(net, vifc); 8721da177e4SLinus Torvalds if (!dev) 8731da177e4SLinus Torvalds return -ENOBUFS; 874d607032dSWang Chen err = dev_set_allmulti(dev, 1); 875d607032dSWang Chen if (err) { 876d607032dSWang Chen ipmr_del_tunnel(dev, vifc); 8777dc00c82SWang Chen dev_put(dev); 878d607032dSWang Chen return err; 879d607032dSWang Chen } 8801da177e4SLinus Torvalds break; 881ee5e81f0SIlia K case VIFF_USE_IFINDEX: 8821da177e4SLinus Torvalds case 0: 883ee5e81f0SIlia K if (vifc->vifc_flags == VIFF_USE_IFINDEX) { 884ee5e81f0SIlia K dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex); 88551456b29SIan Morris if (dev && !__in_dev_get_rtnl(dev)) { 886ee5e81f0SIlia K dev_put(dev); 887ee5e81f0SIlia K return -EADDRNOTAVAIL; 888ee5e81f0SIlia K } 889a8cb16ddSEric Dumazet } else { 8904feb88e5SBenjamin Thery dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr); 891a8cb16ddSEric Dumazet } 8921da177e4SLinus Torvalds if (!dev) 8931da177e4SLinus Torvalds return -EADDRNOTAVAIL; 894d607032dSWang Chen err = dev_set_allmulti(dev, 1); 8957dc00c82SWang Chen if (err) { 8967dc00c82SWang Chen dev_put(dev); 897d607032dSWang Chen return err; 8987dc00c82SWang Chen } 8991da177e4SLinus Torvalds break; 9001da177e4SLinus Torvalds default: 9011da177e4SLinus Torvalds return -EINVAL; 9021da177e4SLinus Torvalds } 9031da177e4SLinus Torvalds 904a8cb16ddSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 905a8cb16ddSEric Dumazet if (!in_dev) { 906d0490cfdSDan Carpenter dev_put(dev); 9071da177e4SLinus Torvalds return -EADDRNOTAVAIL; 908d0490cfdSDan Carpenter } 90942f811b8SHerbert Xu IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++; 9103b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_MC_FORWARDING, 9113b022865SDavid Ahern dev->ifindex, &in_dev->cnf); 9121da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 9131da177e4SLinus Torvalds 914a8cb16ddSEric Dumazet /* Fill in the VIF structures */ 9156853f21fSYuval Mintz vif_device_init(v, dev, vifc->vifc_rate_limit, 9166853f21fSYuval Mintz vifc->vifc_threshold, 9176853f21fSYuval Mintz vifc->vifc_flags | (!mrtsock ? VIFF_STATIC : 0), 9186853f21fSYuval Mintz (VIFF_TUNNEL | VIFF_REGISTER)); 919a8cb16ddSEric Dumazet 9205d8b3e69SYotam Gigi attr.orig_dev = dev; 9215d8b3e69SYotam Gigi if (!switchdev_port_attr_get(dev, &attr)) { 9225d8b3e69SYotam Gigi memcpy(v->dev_parent_id.id, attr.u.ppid.id, attr.u.ppid.id_len); 9235d8b3e69SYotam Gigi v->dev_parent_id.id_len = attr.u.ppid.id_len; 9245d8b3e69SYotam Gigi } else { 9255d8b3e69SYotam Gigi v->dev_parent_id.id_len = 0; 9265d8b3e69SYotam Gigi } 9276853f21fSYuval Mintz 9281da177e4SLinus Torvalds v->local = vifc->vifc_lcl_addr.s_addr; 9291da177e4SLinus Torvalds v->remote = vifc->vifc_rmt_addr.s_addr; 9301da177e4SLinus Torvalds 9311da177e4SLinus Torvalds /* And finish update writing critical data */ 9321da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 9331da177e4SLinus Torvalds v->dev = dev; 9341da177e4SLinus Torvalds if (v->flags & VIFF_REGISTER) 9350c12295aSPatrick McHardy mrt->mroute_reg_vif_num = vifi; 9360c12295aSPatrick McHardy if (vifi+1 > mrt->maxvif) 9370c12295aSPatrick McHardy mrt->maxvif = vifi+1; 9381da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 939b362053aSYotam Gigi call_ipmr_vif_entry_notifiers(net, FIB_EVENT_VIF_ADD, v, vifi, mrt->id); 9401da177e4SLinus Torvalds return 0; 9411da177e4SLinus Torvalds } 9421da177e4SLinus Torvalds 943a8c9486bSEric Dumazet /* called with rcu_read_lock() */ 9440c12295aSPatrick McHardy static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, 9454feb88e5SBenjamin Thery __be32 origin, 9464feb88e5SBenjamin Thery __be32 mcastgrp) 9471da177e4SLinus Torvalds { 9488fb472c0SNikolay Aleksandrov struct mfc_cache_cmp_arg arg = { 9498fb472c0SNikolay Aleksandrov .mfc_mcastgrp = mcastgrp, 9508fb472c0SNikolay Aleksandrov .mfc_origin = origin 9518fb472c0SNikolay Aleksandrov }; 9521da177e4SLinus Torvalds 953845c9a7aSYuval Mintz return mr_mfc_find(mrt, &arg); 954660b26dcSNicolas Dichtel } 955660b26dcSNicolas Dichtel 956660b26dcSNicolas Dichtel /* Look for a (*,G) entry */ 957660b26dcSNicolas Dichtel static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt, 958660b26dcSNicolas Dichtel __be32 mcastgrp, int vifi) 959660b26dcSNicolas Dichtel { 9608fb472c0SNikolay Aleksandrov struct mfc_cache_cmp_arg arg = { 9618fb472c0SNikolay Aleksandrov .mfc_mcastgrp = mcastgrp, 9628fb472c0SNikolay Aleksandrov .mfc_origin = htonl(INADDR_ANY) 9638fb472c0SNikolay Aleksandrov }; 964660b26dcSNicolas Dichtel 965360eb5daSNicolas Dichtel if (mcastgrp == htonl(INADDR_ANY)) 966845c9a7aSYuval Mintz return mr_mfc_find_any_parent(mrt, vifi); 967845c9a7aSYuval Mintz return mr_mfc_find_any(mrt, vifi, &arg); 968660b26dcSNicolas Dichtel } 969660b26dcSNicolas Dichtel 9708fb472c0SNikolay Aleksandrov /* Look for a (S,G,iif) entry if parent != -1 */ 9718fb472c0SNikolay Aleksandrov static struct mfc_cache *ipmr_cache_find_parent(struct mr_table *mrt, 9728fb472c0SNikolay Aleksandrov __be32 origin, __be32 mcastgrp, 9738fb472c0SNikolay Aleksandrov int parent) 9748fb472c0SNikolay Aleksandrov { 9758fb472c0SNikolay Aleksandrov struct mfc_cache_cmp_arg arg = { 9768fb472c0SNikolay Aleksandrov .mfc_mcastgrp = mcastgrp, 9778fb472c0SNikolay Aleksandrov .mfc_origin = origin, 9788fb472c0SNikolay Aleksandrov }; 9798fb472c0SNikolay Aleksandrov 980845c9a7aSYuval Mintz return mr_mfc_find_parent(mrt, &arg, parent); 9818fb472c0SNikolay Aleksandrov } 9828fb472c0SNikolay Aleksandrov 9837ef8f65dSNikolay Aleksandrov /* Allocate a multicast cache entry */ 984d658f8a0SPatrick McHardy static struct mfc_cache *ipmr_cache_alloc(void) 9851da177e4SLinus Torvalds { 986c3762229SRobert P. J. Day struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL); 987a8c9486bSEric Dumazet 98870a0dec4STom Goff if (c) { 989494fff56SYuval Mintz c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1; 990494fff56SYuval Mintz c->_c.mfc_un.res.minvif = MAXVIFS; 9918c13af2aSYuval Mintz c->_c.free = ipmr_cache_free_rcu; 992494fff56SYuval Mintz refcount_set(&c->_c.mfc_un.res.refcount, 1); 99370a0dec4STom Goff } 9941da177e4SLinus Torvalds return c; 9951da177e4SLinus Torvalds } 9961da177e4SLinus Torvalds 997d658f8a0SPatrick McHardy static struct mfc_cache *ipmr_cache_alloc_unres(void) 9981da177e4SLinus Torvalds { 999c3762229SRobert P. J. Day struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC); 1000a8c9486bSEric Dumazet 1001a8c9486bSEric Dumazet if (c) { 1002494fff56SYuval Mintz skb_queue_head_init(&c->_c.mfc_un.unres.unresolved); 1003494fff56SYuval Mintz c->_c.mfc_un.unres.expires = jiffies + 10 * HZ; 1004a8c9486bSEric Dumazet } 10051da177e4SLinus Torvalds return c; 10061da177e4SLinus Torvalds } 10071da177e4SLinus Torvalds 10087ef8f65dSNikolay Aleksandrov /* A cache entry has gone into a resolved state from queued */ 10090c12295aSPatrick McHardy static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt, 10100c12295aSPatrick McHardy struct mfc_cache *uc, struct mfc_cache *c) 10111da177e4SLinus Torvalds { 10121da177e4SLinus Torvalds struct sk_buff *skb; 10139ef1d4c7SPatrick McHardy struct nlmsgerr *e; 10141da177e4SLinus Torvalds 1015a8cb16ddSEric Dumazet /* Play the pending entries through our router */ 1016494fff56SYuval Mintz while ((skb = __skb_dequeue(&uc->_c.mfc_un.unres.unresolved))) { 1017eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->version == 0) { 1018af72868bSJohannes Berg struct nlmsghdr *nlh = skb_pull(skb, 1019af72868bSJohannes Berg sizeof(struct iphdr)); 10201da177e4SLinus Torvalds 10217b0db857SYuval Mintz if (mr_fill_mroute(mrt, skb, &c->_c, 1022494fff56SYuval Mintz nlmsg_data(nlh)) > 0) { 1023a8cb16ddSEric Dumazet nlh->nlmsg_len = skb_tail_pointer(skb) - 1024a8cb16ddSEric Dumazet (u8 *)nlh; 10251da177e4SLinus Torvalds } else { 10261da177e4SLinus Torvalds nlh->nlmsg_type = NLMSG_ERROR; 1027573ce260SHong zhi guo nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 10281da177e4SLinus Torvalds skb_trim(skb, nlh->nlmsg_len); 1029573ce260SHong zhi guo e = nlmsg_data(nlh); 10309ef1d4c7SPatrick McHardy e->error = -EMSGSIZE; 10319ef1d4c7SPatrick McHardy memset(&e->msg, 0, sizeof(e->msg)); 10321da177e4SLinus Torvalds } 10332942e900SThomas Graf 103415e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 1035a8cb16ddSEric Dumazet } else { 10364b1f0d33SDonald Sharp ip_mr_forward(net, mrt, skb->dev, skb, c, 0); 10371da177e4SLinus Torvalds } 10381da177e4SLinus Torvalds } 1039a8cb16ddSEric Dumazet } 10401da177e4SLinus Torvalds 10415a645dd8SJulien Gomes /* Bounce a cache query up to mrouted and netlink. 10421da177e4SLinus Torvalds * 10431da177e4SLinus Torvalds * Called under mrt_lock. 10441da177e4SLinus Torvalds */ 10450c12295aSPatrick McHardy static int ipmr_cache_report(struct mr_table *mrt, 10464feb88e5SBenjamin Thery struct sk_buff *pkt, vifi_t vifi, int assert) 10471da177e4SLinus Torvalds { 1048c9bdd4b5SArnaldo Carvalho de Melo const int ihl = ip_hdrlen(pkt); 1049c316c629SNikolay Aleksandrov struct sock *mroute_sk; 10501da177e4SLinus Torvalds struct igmphdr *igmp; 10511da177e4SLinus Torvalds struct igmpmsg *msg; 1052c316c629SNikolay Aleksandrov struct sk_buff *skb; 10531da177e4SLinus Torvalds int ret; 10541da177e4SLinus Torvalds 1055c921c207SNikolay Aleksandrov if (assert == IGMPMSG_WHOLEPKT || assert == IGMPMSG_WRVIFWHOLE) 10561da177e4SLinus Torvalds skb = skb_realloc_headroom(pkt, sizeof(struct iphdr)); 10571da177e4SLinus Torvalds else 10581da177e4SLinus Torvalds skb = alloc_skb(128, GFP_ATOMIC); 10591da177e4SLinus Torvalds 10601da177e4SLinus Torvalds if (!skb) 10611da177e4SLinus Torvalds return -ENOBUFS; 10621da177e4SLinus Torvalds 1063c921c207SNikolay Aleksandrov if (assert == IGMPMSG_WHOLEPKT || assert == IGMPMSG_WRVIFWHOLE) { 10641da177e4SLinus Torvalds /* Ugly, but we have no choice with this interface. 1065a8cb16ddSEric Dumazet * Duplicate old header, fix ihl, length etc. 1066a8cb16ddSEric Dumazet * And all this only to mangle msg->im_msgtype and 1067a8cb16ddSEric Dumazet * to set msg->im_mbz to "mbz" :-) 10681da177e4SLinus Torvalds */ 1069878c8145SArnaldo Carvalho de Melo skb_push(skb, sizeof(struct iphdr)); 1070878c8145SArnaldo Carvalho de Melo skb_reset_network_header(skb); 1071badff6d0SArnaldo Carvalho de Melo skb_reset_transport_header(skb); 10720272ffc4SArnaldo Carvalho de Melo msg = (struct igmpmsg *)skb_network_header(skb); 1073d56f90a7SArnaldo Carvalho de Melo memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr)); 1074c921c207SNikolay Aleksandrov msg->im_msgtype = assert; 10751da177e4SLinus Torvalds msg->im_mbz = 0; 1076c921c207SNikolay Aleksandrov if (assert == IGMPMSG_WRVIFWHOLE) 1077c921c207SNikolay Aleksandrov msg->im_vif = vifi; 1078c921c207SNikolay Aleksandrov else 10790c12295aSPatrick McHardy msg->im_vif = mrt->mroute_reg_vif_num; 1080eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2; 1081eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) + 1082eddc9ec5SArnaldo Carvalho de Melo sizeof(struct iphdr)); 1083c316c629SNikolay Aleksandrov } else { 1084a8cb16ddSEric Dumazet /* Copy the IP header */ 108530f3a40fSCong Wang skb_set_network_header(skb, skb->len); 1086ddc7b8e3SArnaldo Carvalho de Melo skb_put(skb, ihl); 108727d7ff46SArnaldo Carvalho de Melo skb_copy_to_linear_data(skb, pkt->data, ihl); 1088c316c629SNikolay Aleksandrov /* Flag to the kernel this is a route add */ 1089c316c629SNikolay Aleksandrov ip_hdr(skb)->protocol = 0; 1090eddc9ec5SArnaldo Carvalho de Melo msg = (struct igmpmsg *)skb_network_header(skb); 10911da177e4SLinus Torvalds msg->im_vif = vifi; 1092adf30907SEric Dumazet skb_dst_set(skb, dst_clone(skb_dst(pkt))); 1093a8cb16ddSEric Dumazet /* Add our header */ 10944df864c1SJohannes Berg igmp = skb_put(skb, sizeof(struct igmphdr)); 1095c316c629SNikolay Aleksandrov igmp->type = assert; 10961da177e4SLinus Torvalds msg->im_msgtype = assert; 10971da177e4SLinus Torvalds igmp->code = 0; 1098eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */ 1099b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 11001da177e4SLinus Torvalds } 11011da177e4SLinus Torvalds 11024c968709SEric Dumazet rcu_read_lock(); 11034c968709SEric Dumazet mroute_sk = rcu_dereference(mrt->mroute_sk); 110451456b29SIan Morris if (!mroute_sk) { 11054c968709SEric Dumazet rcu_read_unlock(); 11061da177e4SLinus Torvalds kfree_skb(skb); 11071da177e4SLinus Torvalds return -EINVAL; 11081da177e4SLinus Torvalds } 11091da177e4SLinus Torvalds 11105a645dd8SJulien Gomes igmpmsg_netlink_event(mrt, skb); 11115a645dd8SJulien Gomes 1112a8cb16ddSEric Dumazet /* Deliver to mrouted */ 11134c968709SEric Dumazet ret = sock_queue_rcv_skb(mroute_sk, skb); 11144c968709SEric Dumazet rcu_read_unlock(); 111570a269e6SBenjamin Thery if (ret < 0) { 1116e87cc472SJoe Perches net_warn_ratelimited("mroute: pending queue full, dropping entries\n"); 11171da177e4SLinus Torvalds kfree_skb(skb); 11181da177e4SLinus Torvalds } 11191da177e4SLinus Torvalds 11201da177e4SLinus Torvalds return ret; 11211da177e4SLinus Torvalds } 11221da177e4SLinus Torvalds 11237ef8f65dSNikolay Aleksandrov /* Queue a packet for resolution. It gets locked cache entry! */ 11247ef8f65dSNikolay Aleksandrov static int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, 11254b1f0d33SDonald Sharp struct sk_buff *skb, struct net_device *dev) 11261da177e4SLinus Torvalds { 11278fb472c0SNikolay Aleksandrov const struct iphdr *iph = ip_hdr(skb); 11288fb472c0SNikolay Aleksandrov struct mfc_cache *c; 1129862465f2SPatrick McHardy bool found = false; 11301da177e4SLinus Torvalds int err; 11311da177e4SLinus Torvalds 11321da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 1133494fff56SYuval Mintz list_for_each_entry(c, &mrt->mfc_unres_queue, _c.list) { 1134e258beb2SPatrick McHardy if (c->mfc_mcastgrp == iph->daddr && 1135862465f2SPatrick McHardy c->mfc_origin == iph->saddr) { 1136862465f2SPatrick McHardy found = true; 11371da177e4SLinus Torvalds break; 11381da177e4SLinus Torvalds } 1139862465f2SPatrick McHardy } 11401da177e4SLinus Torvalds 1141862465f2SPatrick McHardy if (!found) { 1142a8cb16ddSEric Dumazet /* Create a new entry if allowable */ 11430c12295aSPatrick McHardy if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 || 1144d658f8a0SPatrick McHardy (c = ipmr_cache_alloc_unres()) == NULL) { 11451da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11461da177e4SLinus Torvalds 11471da177e4SLinus Torvalds kfree_skb(skb); 11481da177e4SLinus Torvalds return -ENOBUFS; 11491da177e4SLinus Torvalds } 11501da177e4SLinus Torvalds 1151a8cb16ddSEric Dumazet /* Fill in the new cache entry */ 1152494fff56SYuval Mintz c->_c.mfc_parent = -1; 1153eddc9ec5SArnaldo Carvalho de Melo c->mfc_origin = iph->saddr; 1154eddc9ec5SArnaldo Carvalho de Melo c->mfc_mcastgrp = iph->daddr; 11551da177e4SLinus Torvalds 1156a8cb16ddSEric Dumazet /* Reflect first query at mrouted. */ 11570c12295aSPatrick McHardy err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE); 1158494fff56SYuval Mintz 11594feb88e5SBenjamin Thery if (err < 0) { 11601da177e4SLinus Torvalds /* If the report failed throw the cache entry 11611da177e4SLinus Torvalds out - Brad Parker 11621da177e4SLinus Torvalds */ 11631da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11641da177e4SLinus Torvalds 11655c0a66f5SBenjamin Thery ipmr_cache_free(c); 11661da177e4SLinus Torvalds kfree_skb(skb); 11671da177e4SLinus Torvalds return err; 11681da177e4SLinus Torvalds } 11691da177e4SLinus Torvalds 11700c12295aSPatrick McHardy atomic_inc(&mrt->cache_resolve_queue_len); 1171494fff56SYuval Mintz list_add(&c->_c.list, &mrt->mfc_unres_queue); 11728cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 11731da177e4SLinus Torvalds 1174278554bdSDavid S. Miller if (atomic_read(&mrt->cache_resolve_queue_len) == 1) 1175494fff56SYuval Mintz mod_timer(&mrt->ipmr_expire_timer, 1176494fff56SYuval Mintz c->_c.mfc_un.unres.expires); 11771da177e4SLinus Torvalds } 11781da177e4SLinus Torvalds 1179a8cb16ddSEric Dumazet /* See if we can append the packet */ 1180494fff56SYuval Mintz if (c->_c.mfc_un.unres.unresolved.qlen > 3) { 11811da177e4SLinus Torvalds kfree_skb(skb); 11821da177e4SLinus Torvalds err = -ENOBUFS; 11831da177e4SLinus Torvalds } else { 11844b1f0d33SDonald Sharp if (dev) { 11854b1f0d33SDonald Sharp skb->dev = dev; 11864b1f0d33SDonald Sharp skb->skb_iif = dev->ifindex; 11874b1f0d33SDonald Sharp } 1188494fff56SYuval Mintz skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb); 11891da177e4SLinus Torvalds err = 0; 11901da177e4SLinus Torvalds } 11911da177e4SLinus Torvalds 11921da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11931da177e4SLinus Torvalds return err; 11941da177e4SLinus Torvalds } 11951da177e4SLinus Torvalds 11967ef8f65dSNikolay Aleksandrov /* MFC cache manipulation by user space mroute daemon */ 11971da177e4SLinus Torvalds 1198660b26dcSNicolas Dichtel static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent) 11991da177e4SLinus Torvalds { 1200b362053aSYotam Gigi struct net *net = read_pnet(&mrt->net); 12018fb472c0SNikolay Aleksandrov struct mfc_cache *c; 12021da177e4SLinus Torvalds 12038fb472c0SNikolay Aleksandrov /* The entries are added/deleted only under RTNL */ 12048fb472c0SNikolay Aleksandrov rcu_read_lock(); 12058fb472c0SNikolay Aleksandrov c = ipmr_cache_find_parent(mrt, mfc->mfcc_origin.s_addr, 12068fb472c0SNikolay Aleksandrov mfc->mfcc_mcastgrp.s_addr, parent); 12078fb472c0SNikolay Aleksandrov rcu_read_unlock(); 12088fb472c0SNikolay Aleksandrov if (!c) 12098fb472c0SNikolay Aleksandrov return -ENOENT; 1210494fff56SYuval Mintz rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ipmr_rht_params); 1211494fff56SYuval Mintz list_del_rcu(&c->_c.list); 1212b362053aSYotam Gigi call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt->id); 12138cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 12148c13af2aSYuval Mintz mr_cache_put(&c->_c); 12158fb472c0SNikolay Aleksandrov 12161da177e4SLinus Torvalds return 0; 12171da177e4SLinus Torvalds } 12181da177e4SLinus Torvalds 12190c12295aSPatrick McHardy static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, 1220660b26dcSNicolas Dichtel struct mfcctl *mfc, int mrtsock, int parent) 12211da177e4SLinus Torvalds { 1222862465f2SPatrick McHardy struct mfc_cache *uc, *c; 1223494fff56SYuval Mintz struct mr_mfc *_uc; 12248fb472c0SNikolay Aleksandrov bool found; 12258fb472c0SNikolay Aleksandrov int ret; 12261da177e4SLinus Torvalds 1227a50436f2SPatrick McHardy if (mfc->mfcc_parent >= MAXVIFS) 1228a50436f2SPatrick McHardy return -ENFILE; 1229a50436f2SPatrick McHardy 12308fb472c0SNikolay Aleksandrov /* The entries are added/deleted only under RTNL */ 12318fb472c0SNikolay Aleksandrov rcu_read_lock(); 12328fb472c0SNikolay Aleksandrov c = ipmr_cache_find_parent(mrt, mfc->mfcc_origin.s_addr, 12338fb472c0SNikolay Aleksandrov mfc->mfcc_mcastgrp.s_addr, parent); 12348fb472c0SNikolay Aleksandrov rcu_read_unlock(); 12358fb472c0SNikolay Aleksandrov if (c) { 12361da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 1237494fff56SYuval Mintz c->_c.mfc_parent = mfc->mfcc_parent; 1238494fff56SYuval Mintz ipmr_update_thresholds(mrt, &c->_c, mfc->mfcc_ttls); 12391da177e4SLinus Torvalds if (!mrtsock) 1240494fff56SYuval Mintz c->_c.mfc_flags |= MFC_STATIC; 12411da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 1242b362053aSYotam Gigi call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, c, 1243b362053aSYotam Gigi mrt->id); 12448cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 12451da177e4SLinus Torvalds return 0; 12461da177e4SLinus Torvalds } 12471da177e4SLinus Torvalds 1248360eb5daSNicolas Dichtel if (mfc->mfcc_mcastgrp.s_addr != htonl(INADDR_ANY) && 1249660b26dcSNicolas Dichtel !ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) 12501da177e4SLinus Torvalds return -EINVAL; 12511da177e4SLinus Torvalds 1252d658f8a0SPatrick McHardy c = ipmr_cache_alloc(); 125351456b29SIan Morris if (!c) 12541da177e4SLinus Torvalds return -ENOMEM; 12551da177e4SLinus Torvalds 12561da177e4SLinus Torvalds c->mfc_origin = mfc->mfcc_origin.s_addr; 12571da177e4SLinus Torvalds c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr; 1258494fff56SYuval Mintz c->_c.mfc_parent = mfc->mfcc_parent; 1259494fff56SYuval Mintz ipmr_update_thresholds(mrt, &c->_c, mfc->mfcc_ttls); 12601da177e4SLinus Torvalds if (!mrtsock) 1261494fff56SYuval Mintz c->_c.mfc_flags |= MFC_STATIC; 12621da177e4SLinus Torvalds 1263494fff56SYuval Mintz ret = rhltable_insert_key(&mrt->mfc_hash, &c->cmparg, &c->_c.mnode, 12648fb472c0SNikolay Aleksandrov ipmr_rht_params); 12658fb472c0SNikolay Aleksandrov if (ret) { 12668fb472c0SNikolay Aleksandrov pr_err("ipmr: rhtable insert error %d\n", ret); 12678fb472c0SNikolay Aleksandrov ipmr_cache_free(c); 12688fb472c0SNikolay Aleksandrov return ret; 12698fb472c0SNikolay Aleksandrov } 1270494fff56SYuval Mintz list_add_tail_rcu(&c->_c.list, &mrt->mfc_cache_list); 12717ef8f65dSNikolay Aleksandrov /* Check to see if we resolved a queued list. If so we 12721da177e4SLinus Torvalds * need to send on the frames and tidy up. 12731da177e4SLinus Torvalds */ 1274b0ebb739SPatrick McHardy found = false; 12751da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 1276494fff56SYuval Mintz list_for_each_entry(_uc, &mrt->mfc_unres_queue, list) { 1277494fff56SYuval Mintz uc = (struct mfc_cache *)_uc; 1278e258beb2SPatrick McHardy if (uc->mfc_origin == c->mfc_origin && 12791da177e4SLinus Torvalds uc->mfc_mcastgrp == c->mfc_mcastgrp) { 1280494fff56SYuval Mintz list_del(&_uc->list); 12810c12295aSPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len); 1282b0ebb739SPatrick McHardy found = true; 12831da177e4SLinus Torvalds break; 12841da177e4SLinus Torvalds } 12851da177e4SLinus Torvalds } 12860c12295aSPatrick McHardy if (list_empty(&mrt->mfc_unres_queue)) 12870c12295aSPatrick McHardy del_timer(&mrt->ipmr_expire_timer); 12881da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 12891da177e4SLinus Torvalds 1290b0ebb739SPatrick McHardy if (found) { 12910c12295aSPatrick McHardy ipmr_cache_resolve(net, mrt, uc, c); 12925c0a66f5SBenjamin Thery ipmr_cache_free(uc); 12931da177e4SLinus Torvalds } 1294b362053aSYotam Gigi call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_ADD, c, mrt->id); 12958cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 12961da177e4SLinus Torvalds return 0; 12971da177e4SLinus Torvalds } 12981da177e4SLinus Torvalds 12997ef8f65dSNikolay Aleksandrov /* Close the multicast socket, and clear the vif tables etc */ 13000e615e96SNikolay Aleksandrov static void mroute_clean_tables(struct mr_table *mrt, bool all) 13011da177e4SLinus Torvalds { 1302b362053aSYotam Gigi struct net *net = read_pnet(&mrt->net); 1303494fff56SYuval Mintz struct mr_mfc *c, *tmp; 1304494fff56SYuval Mintz struct mfc_cache *cache; 1305d17fa6faSEric Dumazet LIST_HEAD(list); 13068fb472c0SNikolay Aleksandrov int i; 13071da177e4SLinus Torvalds 1308a8cb16ddSEric Dumazet /* Shut down all active vif entries */ 13090c12295aSPatrick McHardy for (i = 0; i < mrt->maxvif; i++) { 13100e615e96SNikolay Aleksandrov if (!all && (mrt->vif_table[i].flags & VIFF_STATIC)) 13110e615e96SNikolay Aleksandrov continue; 13120c12295aSPatrick McHardy vif_delete(mrt, i, 0, &list); 13131da177e4SLinus Torvalds } 1314d17fa6faSEric Dumazet unregister_netdevice_many(&list); 13151da177e4SLinus Torvalds 1316a8cb16ddSEric Dumazet /* Wipe the cache */ 13178fb472c0SNikolay Aleksandrov list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) { 13180e615e96SNikolay Aleksandrov if (!all && (c->mfc_flags & MFC_STATIC)) 13191da177e4SLinus Torvalds continue; 13208fb472c0SNikolay Aleksandrov rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params); 1321a8c9486bSEric Dumazet list_del_rcu(&c->list); 1322494fff56SYuval Mintz cache = (struct mfc_cache *)c; 1323494fff56SYuval Mintz call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, cache, 1324b362053aSYotam Gigi mrt->id); 1325494fff56SYuval Mintz mroute_netlink_event(mrt, cache, RTM_DELROUTE); 13268c13af2aSYuval Mintz mr_cache_put(c); 13271da177e4SLinus Torvalds } 13281da177e4SLinus Torvalds 13290c12295aSPatrick McHardy if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { 13301da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 13318fb472c0SNikolay Aleksandrov list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { 1332862465f2SPatrick McHardy list_del(&c->list); 1333494fff56SYuval Mintz cache = (struct mfc_cache *)c; 1334494fff56SYuval Mintz mroute_netlink_event(mrt, cache, RTM_DELROUTE); 1335494fff56SYuval Mintz ipmr_destroy_unres(mrt, cache); 13361da177e4SLinus Torvalds } 13371da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 13381da177e4SLinus Torvalds } 13391da177e4SLinus Torvalds } 13401da177e4SLinus Torvalds 13414c968709SEric Dumazet /* called from ip_ra_control(), before an RCU grace period, 13424c968709SEric Dumazet * we dont need to call synchronize_rcu() here 13434c968709SEric Dumazet */ 13441da177e4SLinus Torvalds static void mrtsock_destruct(struct sock *sk) 13451da177e4SLinus Torvalds { 13464feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1347f0ad0860SPatrick McHardy struct mr_table *mrt; 13484feb88e5SBenjamin Thery 1349128aaa98SKirill Tkhai rtnl_lock(); 1350f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 13514c968709SEric Dumazet if (sk == rtnl_dereference(mrt->mroute_sk)) { 13524feb88e5SBenjamin Thery IPV4_DEVCONF_ALL(net, MC_FORWARDING)--; 13533b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 13543b022865SDavid Ahern NETCONFA_MC_FORWARDING, 1355d67b8c61SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1356d67b8c61SNicolas Dichtel net->ipv4.devconf_all); 1357a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(mrt->mroute_sk, NULL); 13580e615e96SNikolay Aleksandrov mroute_clean_tables(mrt, false); 13591da177e4SLinus Torvalds } 13601da177e4SLinus Torvalds } 1361128aaa98SKirill Tkhai rtnl_unlock(); 13621da177e4SLinus Torvalds } 13631da177e4SLinus Torvalds 13647ef8f65dSNikolay Aleksandrov /* Socket options and virtual interface manipulation. The whole 13651da177e4SLinus Torvalds * virtual interface system is a complete heap, but unfortunately 13661da177e4SLinus Torvalds * that's how BSD mrouted happens to think. Maybe one day with a proper 13671da177e4SLinus Torvalds * MOSPF/PIM router set up we can clean this up. 13681da177e4SLinus Torvalds */ 13691da177e4SLinus Torvalds 137029e97d21SNikolay Aleksandrov int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, 137129e97d21SNikolay Aleksandrov unsigned int optlen) 13721da177e4SLinus Torvalds { 137329e97d21SNikolay Aleksandrov struct net *net = sock_net(sk); 137429e97d21SNikolay Aleksandrov int val, ret = 0, parent = 0; 137529e97d21SNikolay Aleksandrov struct mr_table *mrt; 13761da177e4SLinus Torvalds struct vifctl vif; 13771da177e4SLinus Torvalds struct mfcctl mfc; 1378c921c207SNikolay Aleksandrov bool do_wrvifwhole; 137929e97d21SNikolay Aleksandrov u32 uval; 1380f0ad0860SPatrick McHardy 138129e97d21SNikolay Aleksandrov /* There's one exception to the lock - MRT_DONE which needs to unlock */ 138229e97d21SNikolay Aleksandrov rtnl_lock(); 13835e1859fbSEric Dumazet if (sk->sk_type != SOCK_RAW || 138429e97d21SNikolay Aleksandrov inet_sk(sk)->inet_num != IPPROTO_IGMP) { 138529e97d21SNikolay Aleksandrov ret = -EOPNOTSUPP; 138629e97d21SNikolay Aleksandrov goto out_unlock; 138729e97d21SNikolay Aleksandrov } 13885e1859fbSEric Dumazet 1389f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 139029e97d21SNikolay Aleksandrov if (!mrt) { 139129e97d21SNikolay Aleksandrov ret = -ENOENT; 139229e97d21SNikolay Aleksandrov goto out_unlock; 139329e97d21SNikolay Aleksandrov } 1394132adf54SStephen Hemminger if (optname != MRT_INIT) { 139533d480ceSEric Dumazet if (sk != rcu_access_pointer(mrt->mroute_sk) && 139629e97d21SNikolay Aleksandrov !ns_capable(net->user_ns, CAP_NET_ADMIN)) { 139729e97d21SNikolay Aleksandrov ret = -EACCES; 139829e97d21SNikolay Aleksandrov goto out_unlock; 139929e97d21SNikolay Aleksandrov } 14001da177e4SLinus Torvalds } 14011da177e4SLinus Torvalds 1402132adf54SStephen Hemminger switch (optname) { 14031da177e4SLinus Torvalds case MRT_INIT: 140442e6b89cSNikolay Aleksandrov if (optlen != sizeof(int)) { 140529e97d21SNikolay Aleksandrov ret = -EINVAL; 140629e97d21SNikolay Aleksandrov break; 140742e6b89cSNikolay Aleksandrov } 140842e6b89cSNikolay Aleksandrov if (rtnl_dereference(mrt->mroute_sk)) { 140942e6b89cSNikolay Aleksandrov ret = -EADDRINUSE; 141042e6b89cSNikolay Aleksandrov break; 141142e6b89cSNikolay Aleksandrov } 14121da177e4SLinus Torvalds 14131da177e4SLinus Torvalds ret = ip_ra_control(sk, 1, mrtsock_destruct); 14141da177e4SLinus Torvalds if (ret == 0) { 1415cf778b00SEric Dumazet rcu_assign_pointer(mrt->mroute_sk, sk); 14164feb88e5SBenjamin Thery IPV4_DEVCONF_ALL(net, MC_FORWARDING)++; 14173b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 14183b022865SDavid Ahern NETCONFA_MC_FORWARDING, 1419d67b8c61SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1420d67b8c61SNicolas Dichtel net->ipv4.devconf_all); 14211da177e4SLinus Torvalds } 142229e97d21SNikolay Aleksandrov break; 14231da177e4SLinus Torvalds case MRT_DONE: 142429e97d21SNikolay Aleksandrov if (sk != rcu_access_pointer(mrt->mroute_sk)) { 142529e97d21SNikolay Aleksandrov ret = -EACCES; 142629e97d21SNikolay Aleksandrov } else { 1427128aaa98SKirill Tkhai /* We need to unlock here because mrtsock_destruct takes 1428128aaa98SKirill Tkhai * care of rtnl itself and we can't change that due to 1429128aaa98SKirill Tkhai * the IP_ROUTER_ALERT setsockopt which runs without it. 1430128aaa98SKirill Tkhai */ 1431128aaa98SKirill Tkhai rtnl_unlock(); 143229e97d21SNikolay Aleksandrov ret = ip_ra_control(sk, 0, NULL); 1433128aaa98SKirill Tkhai goto out; 143429e97d21SNikolay Aleksandrov } 143529e97d21SNikolay Aleksandrov break; 14361da177e4SLinus Torvalds case MRT_ADD_VIF: 14371da177e4SLinus Torvalds case MRT_DEL_VIF: 143829e97d21SNikolay Aleksandrov if (optlen != sizeof(vif)) { 143929e97d21SNikolay Aleksandrov ret = -EINVAL; 144029e97d21SNikolay Aleksandrov break; 144129e97d21SNikolay Aleksandrov } 144229e97d21SNikolay Aleksandrov if (copy_from_user(&vif, optval, sizeof(vif))) { 144329e97d21SNikolay Aleksandrov ret = -EFAULT; 144429e97d21SNikolay Aleksandrov break; 144529e97d21SNikolay Aleksandrov } 144629e97d21SNikolay Aleksandrov if (vif.vifc_vifi >= MAXVIFS) { 144729e97d21SNikolay Aleksandrov ret = -ENFILE; 144829e97d21SNikolay Aleksandrov break; 144929e97d21SNikolay Aleksandrov } 14501da177e4SLinus Torvalds if (optname == MRT_ADD_VIF) { 14514c968709SEric Dumazet ret = vif_add(net, mrt, &vif, 14524c968709SEric Dumazet sk == rtnl_dereference(mrt->mroute_sk)); 14531da177e4SLinus Torvalds } else { 14540c12295aSPatrick McHardy ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL); 14551da177e4SLinus Torvalds } 145629e97d21SNikolay Aleksandrov break; 14577ef8f65dSNikolay Aleksandrov /* Manipulate the forwarding caches. These live 14581da177e4SLinus Torvalds * in a sort of kernel/user symbiosis. 14591da177e4SLinus Torvalds */ 14601da177e4SLinus Torvalds case MRT_ADD_MFC: 14611da177e4SLinus Torvalds case MRT_DEL_MFC: 1462660b26dcSNicolas Dichtel parent = -1; 1463fcfd6dfaSGustavo A. R. Silva /* fall through */ 1464660b26dcSNicolas Dichtel case MRT_ADD_MFC_PROXY: 1465660b26dcSNicolas Dichtel case MRT_DEL_MFC_PROXY: 146629e97d21SNikolay Aleksandrov if (optlen != sizeof(mfc)) { 146729e97d21SNikolay Aleksandrov ret = -EINVAL; 146829e97d21SNikolay Aleksandrov break; 146929e97d21SNikolay Aleksandrov } 147029e97d21SNikolay Aleksandrov if (copy_from_user(&mfc, optval, sizeof(mfc))) { 147129e97d21SNikolay Aleksandrov ret = -EFAULT; 147229e97d21SNikolay Aleksandrov break; 147329e97d21SNikolay Aleksandrov } 1474660b26dcSNicolas Dichtel if (parent == 0) 1475660b26dcSNicolas Dichtel parent = mfc.mfcc_parent; 1476660b26dcSNicolas Dichtel if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY) 1477660b26dcSNicolas Dichtel ret = ipmr_mfc_delete(mrt, &mfc, parent); 14781da177e4SLinus Torvalds else 14794c968709SEric Dumazet ret = ipmr_mfc_add(net, mrt, &mfc, 1480660b26dcSNicolas Dichtel sk == rtnl_dereference(mrt->mroute_sk), 1481660b26dcSNicolas Dichtel parent); 148229e97d21SNikolay Aleksandrov break; 14837ef8f65dSNikolay Aleksandrov /* Control PIM assert. */ 14841da177e4SLinus Torvalds case MRT_ASSERT: 148529e97d21SNikolay Aleksandrov if (optlen != sizeof(val)) { 148629e97d21SNikolay Aleksandrov ret = -EINVAL; 148729e97d21SNikolay Aleksandrov break; 14881da177e4SLinus Torvalds } 148929e97d21SNikolay Aleksandrov if (get_user(val, (int __user *)optval)) { 149029e97d21SNikolay Aleksandrov ret = -EFAULT; 149129e97d21SNikolay Aleksandrov break; 149229e97d21SNikolay Aleksandrov } 149329e97d21SNikolay Aleksandrov mrt->mroute_do_assert = val; 149429e97d21SNikolay Aleksandrov break; 14951da177e4SLinus Torvalds case MRT_PIM: 14961973a4eaSNikolay Aleksandrov if (!ipmr_pimsm_enabled()) { 149729e97d21SNikolay Aleksandrov ret = -ENOPROTOOPT; 149829e97d21SNikolay Aleksandrov break; 14991da177e4SLinus Torvalds } 150029e97d21SNikolay Aleksandrov if (optlen != sizeof(val)) { 150129e97d21SNikolay Aleksandrov ret = -EINVAL; 150229e97d21SNikolay Aleksandrov break; 15031da177e4SLinus Torvalds } 150429e97d21SNikolay Aleksandrov if (get_user(val, (int __user *)optval)) { 150529e97d21SNikolay Aleksandrov ret = -EFAULT; 150629e97d21SNikolay Aleksandrov break; 150729e97d21SNikolay Aleksandrov } 150829e97d21SNikolay Aleksandrov 1509c921c207SNikolay Aleksandrov do_wrvifwhole = (val == IGMPMSG_WRVIFWHOLE); 151029e97d21SNikolay Aleksandrov val = !!val; 151129e97d21SNikolay Aleksandrov if (val != mrt->mroute_do_pim) { 151229e97d21SNikolay Aleksandrov mrt->mroute_do_pim = val; 151329e97d21SNikolay Aleksandrov mrt->mroute_do_assert = val; 1514c921c207SNikolay Aleksandrov mrt->mroute_do_wrvifwhole = do_wrvifwhole; 151529e97d21SNikolay Aleksandrov } 151629e97d21SNikolay Aleksandrov break; 1517f0ad0860SPatrick McHardy case MRT_TABLE: 151829e97d21SNikolay Aleksandrov if (!IS_BUILTIN(CONFIG_IP_MROUTE_MULTIPLE_TABLES)) { 151929e97d21SNikolay Aleksandrov ret = -ENOPROTOOPT; 152029e97d21SNikolay Aleksandrov break; 152129e97d21SNikolay Aleksandrov } 152229e97d21SNikolay Aleksandrov if (optlen != sizeof(uval)) { 152329e97d21SNikolay Aleksandrov ret = -EINVAL; 152429e97d21SNikolay Aleksandrov break; 152529e97d21SNikolay Aleksandrov } 152629e97d21SNikolay Aleksandrov if (get_user(uval, (u32 __user *)optval)) { 152729e97d21SNikolay Aleksandrov ret = -EFAULT; 152829e97d21SNikolay Aleksandrov break; 152929e97d21SNikolay Aleksandrov } 1530f0ad0860SPatrick McHardy 15314c968709SEric Dumazet if (sk == rtnl_dereference(mrt->mroute_sk)) { 15324c968709SEric Dumazet ret = -EBUSY; 15334c968709SEric Dumazet } else { 153429e97d21SNikolay Aleksandrov mrt = ipmr_new_table(net, uval); 15351113ebbcSNikolay Aleksandrov if (IS_ERR(mrt)) 15361113ebbcSNikolay Aleksandrov ret = PTR_ERR(mrt); 15375e1859fbSEric Dumazet else 153829e97d21SNikolay Aleksandrov raw_sk(sk)->ipmr_table = uval; 15394c968709SEric Dumazet } 154029e97d21SNikolay Aleksandrov break; 15417ef8f65dSNikolay Aleksandrov /* Spurious command, or MRT_VERSION which you cannot set. */ 15421da177e4SLinus Torvalds default: 154329e97d21SNikolay Aleksandrov ret = -ENOPROTOOPT; 15441da177e4SLinus Torvalds } 154529e97d21SNikolay Aleksandrov out_unlock: 154629e97d21SNikolay Aleksandrov rtnl_unlock(); 1547128aaa98SKirill Tkhai out: 154829e97d21SNikolay Aleksandrov return ret; 15491da177e4SLinus Torvalds } 15501da177e4SLinus Torvalds 15517ef8f65dSNikolay Aleksandrov /* Getsock opt support for the multicast routing system. */ 15521da177e4SLinus Torvalds int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen) 15531da177e4SLinus Torvalds { 15541da177e4SLinus Torvalds int olr; 15551da177e4SLinus Torvalds int val; 15564feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1557f0ad0860SPatrick McHardy struct mr_table *mrt; 1558f0ad0860SPatrick McHardy 15595e1859fbSEric Dumazet if (sk->sk_type != SOCK_RAW || 15605e1859fbSEric Dumazet inet_sk(sk)->inet_num != IPPROTO_IGMP) 15615e1859fbSEric Dumazet return -EOPNOTSUPP; 15625e1859fbSEric Dumazet 1563f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 156451456b29SIan Morris if (!mrt) 1565f0ad0860SPatrick McHardy return -ENOENT; 15661da177e4SLinus Torvalds 1567fe9ef3ceSNikolay Aleksandrov switch (optname) { 1568fe9ef3ceSNikolay Aleksandrov case MRT_VERSION: 15691da177e4SLinus Torvalds val = 0x0305; 1570fe9ef3ceSNikolay Aleksandrov break; 1571fe9ef3ceSNikolay Aleksandrov case MRT_PIM: 15721973a4eaSNikolay Aleksandrov if (!ipmr_pimsm_enabled()) 1573c316c629SNikolay Aleksandrov return -ENOPROTOOPT; 15740c12295aSPatrick McHardy val = mrt->mroute_do_pim; 1575fe9ef3ceSNikolay Aleksandrov break; 1576fe9ef3ceSNikolay Aleksandrov case MRT_ASSERT: 15770c12295aSPatrick McHardy val = mrt->mroute_do_assert; 1578fe9ef3ceSNikolay Aleksandrov break; 1579fe9ef3ceSNikolay Aleksandrov default: 1580fe9ef3ceSNikolay Aleksandrov return -ENOPROTOOPT; 1581c316c629SNikolay Aleksandrov } 1582fe9ef3ceSNikolay Aleksandrov 1583fe9ef3ceSNikolay Aleksandrov if (get_user(olr, optlen)) 1584fe9ef3ceSNikolay Aleksandrov return -EFAULT; 1585fe9ef3ceSNikolay Aleksandrov olr = min_t(unsigned int, olr, sizeof(int)); 1586fe9ef3ceSNikolay Aleksandrov if (olr < 0) 1587fe9ef3ceSNikolay Aleksandrov return -EINVAL; 1588fe9ef3ceSNikolay Aleksandrov if (put_user(olr, optlen)) 1589fe9ef3ceSNikolay Aleksandrov return -EFAULT; 15901da177e4SLinus Torvalds if (copy_to_user(optval, &val, olr)) 15911da177e4SLinus Torvalds return -EFAULT; 15921da177e4SLinus Torvalds return 0; 15931da177e4SLinus Torvalds } 15941da177e4SLinus Torvalds 15957ef8f65dSNikolay Aleksandrov /* The IP multicast ioctl support routines. */ 15961da177e4SLinus Torvalds int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) 15971da177e4SLinus Torvalds { 15981da177e4SLinus Torvalds struct sioc_sg_req sr; 15991da177e4SLinus Torvalds struct sioc_vif_req vr; 16001da177e4SLinus Torvalds struct vif_device *vif; 16011da177e4SLinus Torvalds struct mfc_cache *c; 16024feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1603f0ad0860SPatrick McHardy struct mr_table *mrt; 1604f0ad0860SPatrick McHardy 1605f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 160651456b29SIan Morris if (!mrt) 1607f0ad0860SPatrick McHardy return -ENOENT; 16081da177e4SLinus Torvalds 1609132adf54SStephen Hemminger switch (cmd) { 16101da177e4SLinus Torvalds case SIOCGETVIFCNT: 16111da177e4SLinus Torvalds if (copy_from_user(&vr, arg, sizeof(vr))) 16121da177e4SLinus Torvalds return -EFAULT; 16130c12295aSPatrick McHardy if (vr.vifi >= mrt->maxvif) 16141da177e4SLinus Torvalds return -EINVAL; 16151da177e4SLinus Torvalds read_lock(&mrt_lock); 16160c12295aSPatrick McHardy vif = &mrt->vif_table[vr.vifi]; 16170c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vr.vifi)) { 16181da177e4SLinus Torvalds vr.icount = vif->pkt_in; 16191da177e4SLinus Torvalds vr.ocount = vif->pkt_out; 16201da177e4SLinus Torvalds vr.ibytes = vif->bytes_in; 16211da177e4SLinus Torvalds vr.obytes = vif->bytes_out; 16221da177e4SLinus Torvalds read_unlock(&mrt_lock); 16231da177e4SLinus Torvalds 16241da177e4SLinus Torvalds if (copy_to_user(arg, &vr, sizeof(vr))) 16251da177e4SLinus Torvalds return -EFAULT; 16261da177e4SLinus Torvalds return 0; 16271da177e4SLinus Torvalds } 16281da177e4SLinus Torvalds read_unlock(&mrt_lock); 16291da177e4SLinus Torvalds return -EADDRNOTAVAIL; 16301da177e4SLinus Torvalds case SIOCGETSGCNT: 16311da177e4SLinus Torvalds if (copy_from_user(&sr, arg, sizeof(sr))) 16321da177e4SLinus Torvalds return -EFAULT; 16331da177e4SLinus Torvalds 1634a8c9486bSEric Dumazet rcu_read_lock(); 16350c12295aSPatrick McHardy c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); 16361da177e4SLinus Torvalds if (c) { 1637494fff56SYuval Mintz sr.pktcnt = c->_c.mfc_un.res.pkt; 1638494fff56SYuval Mintz sr.bytecnt = c->_c.mfc_un.res.bytes; 1639494fff56SYuval Mintz sr.wrong_if = c->_c.mfc_un.res.wrong_if; 1640a8c9486bSEric Dumazet rcu_read_unlock(); 16411da177e4SLinus Torvalds 16421da177e4SLinus Torvalds if (copy_to_user(arg, &sr, sizeof(sr))) 16431da177e4SLinus Torvalds return -EFAULT; 16441da177e4SLinus Torvalds return 0; 16451da177e4SLinus Torvalds } 1646a8c9486bSEric Dumazet rcu_read_unlock(); 16471da177e4SLinus Torvalds return -EADDRNOTAVAIL; 16481da177e4SLinus Torvalds default: 16491da177e4SLinus Torvalds return -ENOIOCTLCMD; 16501da177e4SLinus Torvalds } 16511da177e4SLinus Torvalds } 16521da177e4SLinus Torvalds 1653709b46e8SEric W. Biederman #ifdef CONFIG_COMPAT 1654709b46e8SEric W. Biederman struct compat_sioc_sg_req { 1655709b46e8SEric W. Biederman struct in_addr src; 1656709b46e8SEric W. Biederman struct in_addr grp; 1657709b46e8SEric W. Biederman compat_ulong_t pktcnt; 1658709b46e8SEric W. Biederman compat_ulong_t bytecnt; 1659709b46e8SEric W. Biederman compat_ulong_t wrong_if; 1660709b46e8SEric W. Biederman }; 1661709b46e8SEric W. Biederman 1662ca6b8bb0SDavid S. Miller struct compat_sioc_vif_req { 1663ca6b8bb0SDavid S. Miller vifi_t vifi; /* Which iface */ 1664ca6b8bb0SDavid S. Miller compat_ulong_t icount; 1665ca6b8bb0SDavid S. Miller compat_ulong_t ocount; 1666ca6b8bb0SDavid S. Miller compat_ulong_t ibytes; 1667ca6b8bb0SDavid S. Miller compat_ulong_t obytes; 1668ca6b8bb0SDavid S. Miller }; 1669ca6b8bb0SDavid S. Miller 1670709b46e8SEric W. Biederman int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) 1671709b46e8SEric W. Biederman { 16720033d5adSDavid S. Miller struct compat_sioc_sg_req sr; 1673ca6b8bb0SDavid S. Miller struct compat_sioc_vif_req vr; 1674ca6b8bb0SDavid S. Miller struct vif_device *vif; 1675709b46e8SEric W. Biederman struct mfc_cache *c; 1676709b46e8SEric W. Biederman struct net *net = sock_net(sk); 1677709b46e8SEric W. Biederman struct mr_table *mrt; 1678709b46e8SEric W. Biederman 1679709b46e8SEric W. Biederman mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 168051456b29SIan Morris if (!mrt) 1681709b46e8SEric W. Biederman return -ENOENT; 1682709b46e8SEric W. Biederman 1683709b46e8SEric W. Biederman switch (cmd) { 1684ca6b8bb0SDavid S. Miller case SIOCGETVIFCNT: 1685ca6b8bb0SDavid S. Miller if (copy_from_user(&vr, arg, sizeof(vr))) 1686ca6b8bb0SDavid S. Miller return -EFAULT; 1687ca6b8bb0SDavid S. Miller if (vr.vifi >= mrt->maxvif) 1688ca6b8bb0SDavid S. Miller return -EINVAL; 1689ca6b8bb0SDavid S. Miller read_lock(&mrt_lock); 1690ca6b8bb0SDavid S. Miller vif = &mrt->vif_table[vr.vifi]; 1691ca6b8bb0SDavid S. Miller if (VIF_EXISTS(mrt, vr.vifi)) { 1692ca6b8bb0SDavid S. Miller vr.icount = vif->pkt_in; 1693ca6b8bb0SDavid S. Miller vr.ocount = vif->pkt_out; 1694ca6b8bb0SDavid S. Miller vr.ibytes = vif->bytes_in; 1695ca6b8bb0SDavid S. Miller vr.obytes = vif->bytes_out; 1696ca6b8bb0SDavid S. Miller read_unlock(&mrt_lock); 1697ca6b8bb0SDavid S. Miller 1698ca6b8bb0SDavid S. Miller if (copy_to_user(arg, &vr, sizeof(vr))) 1699ca6b8bb0SDavid S. Miller return -EFAULT; 1700ca6b8bb0SDavid S. Miller return 0; 1701ca6b8bb0SDavid S. Miller } 1702ca6b8bb0SDavid S. Miller read_unlock(&mrt_lock); 1703ca6b8bb0SDavid S. Miller return -EADDRNOTAVAIL; 1704709b46e8SEric W. Biederman case SIOCGETSGCNT: 1705709b46e8SEric W. Biederman if (copy_from_user(&sr, arg, sizeof(sr))) 1706709b46e8SEric W. Biederman return -EFAULT; 1707709b46e8SEric W. Biederman 1708709b46e8SEric W. Biederman rcu_read_lock(); 1709709b46e8SEric W. Biederman c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); 1710709b46e8SEric W. Biederman if (c) { 1711494fff56SYuval Mintz sr.pktcnt = c->_c.mfc_un.res.pkt; 1712494fff56SYuval Mintz sr.bytecnt = c->_c.mfc_un.res.bytes; 1713494fff56SYuval Mintz sr.wrong_if = c->_c.mfc_un.res.wrong_if; 1714709b46e8SEric W. Biederman rcu_read_unlock(); 1715709b46e8SEric W. Biederman 1716709b46e8SEric W. Biederman if (copy_to_user(arg, &sr, sizeof(sr))) 1717709b46e8SEric W. Biederman return -EFAULT; 1718709b46e8SEric W. Biederman return 0; 1719709b46e8SEric W. Biederman } 1720709b46e8SEric W. Biederman rcu_read_unlock(); 1721709b46e8SEric W. Biederman return -EADDRNOTAVAIL; 1722709b46e8SEric W. Biederman default: 1723709b46e8SEric W. Biederman return -ENOIOCTLCMD; 1724709b46e8SEric W. Biederman } 1725709b46e8SEric W. Biederman } 1726709b46e8SEric W. Biederman #endif 1727709b46e8SEric W. Biederman 17281da177e4SLinus Torvalds static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr) 17291da177e4SLinus Torvalds { 1730351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 17314feb88e5SBenjamin Thery struct net *net = dev_net(dev); 1732f0ad0860SPatrick McHardy struct mr_table *mrt; 17331da177e4SLinus Torvalds struct vif_device *v; 17341da177e4SLinus Torvalds int ct; 1735e9dc8653SEric W. Biederman 17361da177e4SLinus Torvalds if (event != NETDEV_UNREGISTER) 17371da177e4SLinus Torvalds return NOTIFY_DONE; 1738f0ad0860SPatrick McHardy 1739f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 17400c12295aSPatrick McHardy v = &mrt->vif_table[0]; 17410c12295aSPatrick McHardy for (ct = 0; ct < mrt->maxvif; ct++, v++) { 1742e9dc8653SEric W. Biederman if (v->dev == dev) 1743e92036a6SRongQing.Li vif_delete(mrt, ct, 1, NULL); 17441da177e4SLinus Torvalds } 17451da177e4SLinus Torvalds } 17461da177e4SLinus Torvalds return NOTIFY_DONE; 17471da177e4SLinus Torvalds } 17481da177e4SLinus Torvalds 17491da177e4SLinus Torvalds static struct notifier_block ip_mr_notifier = { 17501da177e4SLinus Torvalds .notifier_call = ipmr_device_event, 17511da177e4SLinus Torvalds }; 17521da177e4SLinus Torvalds 17537ef8f65dSNikolay Aleksandrov /* Encapsulate a packet by attaching a valid IPIP header to it. 17541da177e4SLinus Torvalds * This avoids tunnel drivers and other mess and gives us the speed so 17551da177e4SLinus Torvalds * important for multicast video. 17561da177e4SLinus Torvalds */ 1757b6a7719aSHannes Frederic Sowa static void ip_encap(struct net *net, struct sk_buff *skb, 1758b6a7719aSHannes Frederic Sowa __be32 saddr, __be32 daddr) 17591da177e4SLinus Torvalds { 17608856dfa3SArnaldo Carvalho de Melo struct iphdr *iph; 1761b71d1d42SEric Dumazet const struct iphdr *old_iph = ip_hdr(skb); 17628856dfa3SArnaldo Carvalho de Melo 17638856dfa3SArnaldo Carvalho de Melo skb_push(skb, sizeof(struct iphdr)); 1764b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 17658856dfa3SArnaldo Carvalho de Melo skb_reset_network_header(skb); 1766eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 17671da177e4SLinus Torvalds 17681da177e4SLinus Torvalds iph->version = 4; 1769e023dd64SArnaldo Carvalho de Melo iph->tos = old_iph->tos; 1770e023dd64SArnaldo Carvalho de Melo iph->ttl = old_iph->ttl; 17711da177e4SLinus Torvalds iph->frag_off = 0; 17721da177e4SLinus Torvalds iph->daddr = daddr; 17731da177e4SLinus Torvalds iph->saddr = saddr; 17741da177e4SLinus Torvalds iph->protocol = IPPROTO_IPIP; 17751da177e4SLinus Torvalds iph->ihl = 5; 17761da177e4SLinus Torvalds iph->tot_len = htons(skb->len); 1777b6a7719aSHannes Frederic Sowa ip_select_ident(net, skb, NULL); 17781da177e4SLinus Torvalds ip_send_check(iph); 17791da177e4SLinus Torvalds 17801da177e4SLinus Torvalds memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 17811da177e4SLinus Torvalds nf_reset(skb); 17821da177e4SLinus Torvalds } 17831da177e4SLinus Torvalds 17840c4b51f0SEric W. Biederman static inline int ipmr_forward_finish(struct net *net, struct sock *sk, 17850c4b51f0SEric W. Biederman struct sk_buff *skb) 17861da177e4SLinus Torvalds { 17871da177e4SLinus Torvalds struct ip_options *opt = &(IPCB(skb)->opt); 17881da177e4SLinus Torvalds 178973186df8SDavid S. Miller IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS); 179073186df8SDavid S. Miller IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len); 17911da177e4SLinus Torvalds 17921da177e4SLinus Torvalds if (unlikely(opt->optlen)) 17931da177e4SLinus Torvalds ip_forward_options(skb); 17941da177e4SLinus Torvalds 179513206b6bSEric W. Biederman return dst_output(net, sk, skb); 17961da177e4SLinus Torvalds } 17971da177e4SLinus Torvalds 1798a5bc9294SYotam Gigi #ifdef CONFIG_NET_SWITCHDEV 1799a5bc9294SYotam Gigi static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt, 1800a5bc9294SYotam Gigi int in_vifi, int out_vifi) 1801a5bc9294SYotam Gigi { 1802a5bc9294SYotam Gigi struct vif_device *out_vif = &mrt->vif_table[out_vifi]; 1803a5bc9294SYotam Gigi struct vif_device *in_vif = &mrt->vif_table[in_vifi]; 1804a5bc9294SYotam Gigi 1805a5bc9294SYotam Gigi if (!skb->offload_mr_fwd_mark) 1806a5bc9294SYotam Gigi return false; 1807a5bc9294SYotam Gigi if (!out_vif->dev_parent_id.id_len || !in_vif->dev_parent_id.id_len) 1808a5bc9294SYotam Gigi return false; 1809a5bc9294SYotam Gigi return netdev_phys_item_id_same(&out_vif->dev_parent_id, 1810a5bc9294SYotam Gigi &in_vif->dev_parent_id); 1811a5bc9294SYotam Gigi } 1812a5bc9294SYotam Gigi #else 1813a5bc9294SYotam Gigi static bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt, 1814a5bc9294SYotam Gigi int in_vifi, int out_vifi) 1815a5bc9294SYotam Gigi { 1816a5bc9294SYotam Gigi return false; 1817a5bc9294SYotam Gigi } 1818a5bc9294SYotam Gigi #endif 1819a5bc9294SYotam Gigi 18207ef8f65dSNikolay Aleksandrov /* Processing handlers for ipmr_forward */ 18211da177e4SLinus Torvalds 18220c12295aSPatrick McHardy static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, 1823a5bc9294SYotam Gigi int in_vifi, struct sk_buff *skb, 1824a5bc9294SYotam Gigi struct mfc_cache *c, int vifi) 18251da177e4SLinus Torvalds { 1826eddc9ec5SArnaldo Carvalho de Melo const struct iphdr *iph = ip_hdr(skb); 18270c12295aSPatrick McHardy struct vif_device *vif = &mrt->vif_table[vifi]; 18281da177e4SLinus Torvalds struct net_device *dev; 18291da177e4SLinus Torvalds struct rtable *rt; 183031e4543dSDavid S. Miller struct flowi4 fl4; 18311da177e4SLinus Torvalds int encap = 0; 18321da177e4SLinus Torvalds 183351456b29SIan Morris if (!vif->dev) 18341da177e4SLinus Torvalds goto out_free; 18351da177e4SLinus Torvalds 18361da177e4SLinus Torvalds if (vif->flags & VIFF_REGISTER) { 18371da177e4SLinus Torvalds vif->pkt_out++; 18381da177e4SLinus Torvalds vif->bytes_out += skb->len; 1839cf3677aeSPavel Emelyanov vif->dev->stats.tx_bytes += skb->len; 1840cf3677aeSPavel Emelyanov vif->dev->stats.tx_packets++; 18410c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT); 184269ebbf58SIlpo Järvinen goto out_free; 18431da177e4SLinus Torvalds } 18441da177e4SLinus Torvalds 1845a5bc9294SYotam Gigi if (ipmr_forward_offloaded(skb, mrt, in_vifi, vifi)) 1846a5bc9294SYotam Gigi goto out_free; 1847a5bc9294SYotam Gigi 18481da177e4SLinus Torvalds if (vif->flags & VIFF_TUNNEL) { 184931e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, 185078fbfd8aSDavid S. Miller vif->remote, vif->local, 185178fbfd8aSDavid S. Miller 0, 0, 185278fbfd8aSDavid S. Miller IPPROTO_IPIP, 185378fbfd8aSDavid S. Miller RT_TOS(iph->tos), vif->link); 1854b23dd4feSDavid S. Miller if (IS_ERR(rt)) 18551da177e4SLinus Torvalds goto out_free; 18561da177e4SLinus Torvalds encap = sizeof(struct iphdr); 18571da177e4SLinus Torvalds } else { 185831e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, iph->daddr, 0, 185978fbfd8aSDavid S. Miller 0, 0, 186078fbfd8aSDavid S. Miller IPPROTO_IPIP, 186178fbfd8aSDavid S. Miller RT_TOS(iph->tos), vif->link); 1862b23dd4feSDavid S. Miller if (IS_ERR(rt)) 18631da177e4SLinus Torvalds goto out_free; 18641da177e4SLinus Torvalds } 18651da177e4SLinus Torvalds 1866d8d1f30bSChangli Gao dev = rt->dst.dev; 18671da177e4SLinus Torvalds 1868d8d1f30bSChangli Gao if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) { 18691da177e4SLinus Torvalds /* Do not fragment multicasts. Alas, IPv4 does not 1870a8cb16ddSEric Dumazet * allow to send ICMP, so that packets will disappear 1871a8cb16ddSEric Dumazet * to blackhole. 18721da177e4SLinus Torvalds */ 187373186df8SDavid S. Miller IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); 18741da177e4SLinus Torvalds ip_rt_put(rt); 18751da177e4SLinus Torvalds goto out_free; 18761da177e4SLinus Torvalds } 18771da177e4SLinus Torvalds 1878d8d1f30bSChangli Gao encap += LL_RESERVED_SPACE(dev) + rt->dst.header_len; 18791da177e4SLinus Torvalds 18801da177e4SLinus Torvalds if (skb_cow(skb, encap)) { 18811da177e4SLinus Torvalds ip_rt_put(rt); 18821da177e4SLinus Torvalds goto out_free; 18831da177e4SLinus Torvalds } 18841da177e4SLinus Torvalds 18851da177e4SLinus Torvalds vif->pkt_out++; 18861da177e4SLinus Torvalds vif->bytes_out += skb->len; 18871da177e4SLinus Torvalds 1888adf30907SEric Dumazet skb_dst_drop(skb); 1889d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 1890eddc9ec5SArnaldo Carvalho de Melo ip_decrease_ttl(ip_hdr(skb)); 18911da177e4SLinus Torvalds 18921da177e4SLinus Torvalds /* FIXME: forward and output firewalls used to be called here. 1893a8cb16ddSEric Dumazet * What do we do with netfilter? -- RR 1894a8cb16ddSEric Dumazet */ 18951da177e4SLinus Torvalds if (vif->flags & VIFF_TUNNEL) { 1896b6a7719aSHannes Frederic Sowa ip_encap(net, skb, vif->local, vif->remote); 18971da177e4SLinus Torvalds /* FIXME: extra output firewall step used to be here. --RR */ 18982f4c02d4SPavel Emelyanov vif->dev->stats.tx_packets++; 18992f4c02d4SPavel Emelyanov vif->dev->stats.tx_bytes += skb->len; 19001da177e4SLinus Torvalds } 19011da177e4SLinus Torvalds 19029ee6c5dcSLance Richardson IPCB(skb)->flags |= IPSKB_FORWARDED; 19031da177e4SLinus Torvalds 19047ef8f65dSNikolay Aleksandrov /* RFC1584 teaches, that DVMRP/PIM router must deliver packets locally 19051da177e4SLinus Torvalds * not only before forwarding, but after forwarding on all output 19061da177e4SLinus Torvalds * interfaces. It is clear, if mrouter runs a multicasting 19071da177e4SLinus Torvalds * program, it should receive packets not depending to what interface 19081da177e4SLinus Torvalds * program is joined. 19091da177e4SLinus Torvalds * If we will not make it, the program will have to join on all 19101da177e4SLinus Torvalds * interfaces. On the other hand, multihoming host (or router, but 19111da177e4SLinus Torvalds * not mrouter) cannot join to more than one interface - it will 19121da177e4SLinus Torvalds * result in receiving multiple packets. 19131da177e4SLinus Torvalds */ 191429a26a56SEric W. Biederman NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, 191529a26a56SEric W. Biederman net, NULL, skb, skb->dev, dev, 19161da177e4SLinus Torvalds ipmr_forward_finish); 19171da177e4SLinus Torvalds return; 19181da177e4SLinus Torvalds 19191da177e4SLinus Torvalds out_free: 19201da177e4SLinus Torvalds kfree_skb(skb); 19211da177e4SLinus Torvalds } 19221da177e4SLinus Torvalds 19230c12295aSPatrick McHardy static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev) 19241da177e4SLinus Torvalds { 19251da177e4SLinus Torvalds int ct; 19260c12295aSPatrick McHardy 19270c12295aSPatrick McHardy for (ct = mrt->maxvif-1; ct >= 0; ct--) { 19280c12295aSPatrick McHardy if (mrt->vif_table[ct].dev == dev) 19291da177e4SLinus Torvalds break; 19301da177e4SLinus Torvalds } 19311da177e4SLinus Torvalds return ct; 19321da177e4SLinus Torvalds } 19331da177e4SLinus Torvalds 19341da177e4SLinus Torvalds /* "local" means that we should preserve one skb (for local delivery) */ 1935c4854ec8SRami Rosen static void ip_mr_forward(struct net *net, struct mr_table *mrt, 19364b1f0d33SDonald Sharp struct net_device *dev, struct sk_buff *skb, 1937494fff56SYuval Mintz struct mfc_cache *c, int local) 19381da177e4SLinus Torvalds { 19394b1f0d33SDonald Sharp int true_vifi = ipmr_find_vif(mrt, dev); 19401da177e4SLinus Torvalds int psend = -1; 19411da177e4SLinus Torvalds int vif, ct; 19421da177e4SLinus Torvalds 1943494fff56SYuval Mintz vif = c->_c.mfc_parent; 1944494fff56SYuval Mintz c->_c.mfc_un.res.pkt++; 1945494fff56SYuval Mintz c->_c.mfc_un.res.bytes += skb->len; 1946494fff56SYuval Mintz c->_c.mfc_un.res.lastuse = jiffies; 19471da177e4SLinus Torvalds 1948494fff56SYuval Mintz if (c->mfc_origin == htonl(INADDR_ANY) && true_vifi >= 0) { 1949660b26dcSNicolas Dichtel struct mfc_cache *cache_proxy; 1950660b26dcSNicolas Dichtel 1951660b26dcSNicolas Dichtel /* For an (*,G) entry, we only check that the incomming 1952660b26dcSNicolas Dichtel * interface is part of the static tree. 1953660b26dcSNicolas Dichtel */ 1954845c9a7aSYuval Mintz cache_proxy = mr_mfc_find_any_parent(mrt, vif); 1955660b26dcSNicolas Dichtel if (cache_proxy && 1956494fff56SYuval Mintz cache_proxy->_c.mfc_un.res.ttls[true_vifi] < 255) 1957660b26dcSNicolas Dichtel goto forward; 1958660b26dcSNicolas Dichtel } 1959660b26dcSNicolas Dichtel 19607ef8f65dSNikolay Aleksandrov /* Wrong interface: drop packet and (maybe) send PIM assert. */ 19614b1f0d33SDonald Sharp if (mrt->vif_table[vif].dev != dev) { 1962c7537967SDavid S. Miller if (rt_is_output_route(skb_rtable(skb))) { 19631da177e4SLinus Torvalds /* It is our own packet, looped back. 1964a8cb16ddSEric Dumazet * Very complicated situation... 1965a8cb16ddSEric Dumazet * 1966a8cb16ddSEric Dumazet * The best workaround until routing daemons will be 1967a8cb16ddSEric Dumazet * fixed is not to redistribute packet, if it was 1968a8cb16ddSEric Dumazet * send through wrong interface. It means, that 1969a8cb16ddSEric Dumazet * multicast applications WILL NOT work for 1970a8cb16ddSEric Dumazet * (S,G), which have default multicast route pointing 1971a8cb16ddSEric Dumazet * to wrong oif. In any case, it is not a good 1972a8cb16ddSEric Dumazet * idea to use multicasting applications on router. 19731da177e4SLinus Torvalds */ 19741da177e4SLinus Torvalds goto dont_forward; 19751da177e4SLinus Torvalds } 19761da177e4SLinus Torvalds 1977494fff56SYuval Mintz c->_c.mfc_un.res.wrong_if++; 19781da177e4SLinus Torvalds 19790c12295aSPatrick McHardy if (true_vifi >= 0 && mrt->mroute_do_assert && 19801da177e4SLinus Torvalds /* pimsm uses asserts, when switching from RPT to SPT, 1981a8cb16ddSEric Dumazet * so that we cannot check that packet arrived on an oif. 1982a8cb16ddSEric Dumazet * It is bad, but otherwise we would need to move pretty 1983a8cb16ddSEric Dumazet * large chunk of pimd to kernel. Ough... --ANK 19841da177e4SLinus Torvalds */ 19850c12295aSPatrick McHardy (mrt->mroute_do_pim || 1986494fff56SYuval Mintz c->_c.mfc_un.res.ttls[true_vifi] < 255) && 19871da177e4SLinus Torvalds time_after(jiffies, 1988494fff56SYuval Mintz c->_c.mfc_un.res.last_assert + 1989494fff56SYuval Mintz MFC_ASSERT_THRESH)) { 1990494fff56SYuval Mintz c->_c.mfc_un.res.last_assert = jiffies; 19910c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF); 1992c921c207SNikolay Aleksandrov if (mrt->mroute_do_wrvifwhole) 1993c921c207SNikolay Aleksandrov ipmr_cache_report(mrt, skb, true_vifi, 1994c921c207SNikolay Aleksandrov IGMPMSG_WRVIFWHOLE); 19951da177e4SLinus Torvalds } 19961da177e4SLinus Torvalds goto dont_forward; 19971da177e4SLinus Torvalds } 19981da177e4SLinus Torvalds 1999660b26dcSNicolas Dichtel forward: 20000c12295aSPatrick McHardy mrt->vif_table[vif].pkt_in++; 20010c12295aSPatrick McHardy mrt->vif_table[vif].bytes_in += skb->len; 20021da177e4SLinus Torvalds 20037ef8f65dSNikolay Aleksandrov /* Forward the frame */ 2004494fff56SYuval Mintz if (c->mfc_origin == htonl(INADDR_ANY) && 2005494fff56SYuval Mintz c->mfc_mcastgrp == htonl(INADDR_ANY)) { 2006660b26dcSNicolas Dichtel if (true_vifi >= 0 && 2007494fff56SYuval Mintz true_vifi != c->_c.mfc_parent && 2008660b26dcSNicolas Dichtel ip_hdr(skb)->ttl > 2009494fff56SYuval Mintz c->_c.mfc_un.res.ttls[c->_c.mfc_parent]) { 2010660b26dcSNicolas Dichtel /* It's an (*,*) entry and the packet is not coming from 2011660b26dcSNicolas Dichtel * the upstream: forward the packet to the upstream 2012660b26dcSNicolas Dichtel * only. 2013660b26dcSNicolas Dichtel */ 2014494fff56SYuval Mintz psend = c->_c.mfc_parent; 2015660b26dcSNicolas Dichtel goto last_forward; 2016660b26dcSNicolas Dichtel } 2017660b26dcSNicolas Dichtel goto dont_forward; 2018660b26dcSNicolas Dichtel } 2019494fff56SYuval Mintz for (ct = c->_c.mfc_un.res.maxvif - 1; 2020494fff56SYuval Mintz ct >= c->_c.mfc_un.res.minvif; ct--) { 2021660b26dcSNicolas Dichtel /* For (*,G) entry, don't forward to the incoming interface */ 2022494fff56SYuval Mintz if ((c->mfc_origin != htonl(INADDR_ANY) || 2023360eb5daSNicolas Dichtel ct != true_vifi) && 2024494fff56SYuval Mintz ip_hdr(skb)->ttl > c->_c.mfc_un.res.ttls[ct]) { 20251da177e4SLinus Torvalds if (psend != -1) { 20261da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 2027a8cb16ddSEric Dumazet 20281da177e4SLinus Torvalds if (skb2) 2029a5bc9294SYotam Gigi ipmr_queue_xmit(net, mrt, true_vifi, 2030494fff56SYuval Mintz skb2, c, psend); 20311da177e4SLinus Torvalds } 20321da177e4SLinus Torvalds psend = ct; 20331da177e4SLinus Torvalds } 20341da177e4SLinus Torvalds } 2035660b26dcSNicolas Dichtel last_forward: 20361da177e4SLinus Torvalds if (psend != -1) { 20371da177e4SLinus Torvalds if (local) { 20381da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 2039a8cb16ddSEric Dumazet 20401da177e4SLinus Torvalds if (skb2) 2041a5bc9294SYotam Gigi ipmr_queue_xmit(net, mrt, true_vifi, skb2, 2042494fff56SYuval Mintz c, psend); 20431da177e4SLinus Torvalds } else { 2044494fff56SYuval Mintz ipmr_queue_xmit(net, mrt, true_vifi, skb, c, psend); 2045c4854ec8SRami Rosen return; 20461da177e4SLinus Torvalds } 20471da177e4SLinus Torvalds } 20481da177e4SLinus Torvalds 20491da177e4SLinus Torvalds dont_forward: 20501da177e4SLinus Torvalds if (!local) 20511da177e4SLinus Torvalds kfree_skb(skb); 20521da177e4SLinus Torvalds } 20531da177e4SLinus Torvalds 2054417da66fSDavid S. Miller static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb) 2055ee3f1aafSDavid S. Miller { 2056417da66fSDavid S. Miller struct rtable *rt = skb_rtable(skb); 2057417da66fSDavid S. Miller struct iphdr *iph = ip_hdr(skb); 2058da91981bSDavid S. Miller struct flowi4 fl4 = { 2059417da66fSDavid S. Miller .daddr = iph->daddr, 2060417da66fSDavid S. Miller .saddr = iph->saddr, 2061b0fe4a31SJulian Anastasov .flowi4_tos = RT_TOS(iph->tos), 20624fd551d7SDavid S. Miller .flowi4_oif = (rt_is_output_route(rt) ? 20634fd551d7SDavid S. Miller skb->dev->ifindex : 0), 20644fd551d7SDavid S. Miller .flowi4_iif = (rt_is_output_route(rt) ? 20651fb9489bSPavel Emelyanov LOOPBACK_IFINDEX : 20664fd551d7SDavid S. Miller skb->dev->ifindex), 2067b4869889SDavid Miller .flowi4_mark = skb->mark, 2068ee3f1aafSDavid S. Miller }; 2069ee3f1aafSDavid S. Miller struct mr_table *mrt; 2070ee3f1aafSDavid S. Miller int err; 2071ee3f1aafSDavid S. Miller 2072da91981bSDavid S. Miller err = ipmr_fib_lookup(net, &fl4, &mrt); 2073ee3f1aafSDavid S. Miller if (err) 2074ee3f1aafSDavid S. Miller return ERR_PTR(err); 2075ee3f1aafSDavid S. Miller return mrt; 2076ee3f1aafSDavid S. Miller } 20771da177e4SLinus Torvalds 20787ef8f65dSNikolay Aleksandrov /* Multicast packets for forwarding arrive here 20794c968709SEric Dumazet * Called with rcu_read_lock(); 20801da177e4SLinus Torvalds */ 20811da177e4SLinus Torvalds int ip_mr_input(struct sk_buff *skb) 20821da177e4SLinus Torvalds { 20831da177e4SLinus Torvalds struct mfc_cache *cache; 20844feb88e5SBenjamin Thery struct net *net = dev_net(skb->dev); 2085511c3f92SEric Dumazet int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL; 2086f0ad0860SPatrick McHardy struct mr_table *mrt; 2087bcfc7d33SThomas Winter struct net_device *dev; 2088bcfc7d33SThomas Winter 2089bcfc7d33SThomas Winter /* skb->dev passed in is the loX master dev for vrfs. 2090bcfc7d33SThomas Winter * As there are no vifs associated with loopback devices, 2091bcfc7d33SThomas Winter * get the proper interface that does have a vif associated with it. 2092bcfc7d33SThomas Winter */ 2093bcfc7d33SThomas Winter dev = skb->dev; 2094bcfc7d33SThomas Winter if (netif_is_l3_master(skb->dev)) { 2095bcfc7d33SThomas Winter dev = dev_get_by_index_rcu(net, IPCB(skb)->iif); 2096bcfc7d33SThomas Winter if (!dev) { 2097bcfc7d33SThomas Winter kfree_skb(skb); 2098bcfc7d33SThomas Winter return -ENODEV; 2099bcfc7d33SThomas Winter } 2100bcfc7d33SThomas Winter } 21011da177e4SLinus Torvalds 21021da177e4SLinus Torvalds /* Packet is looped back after forward, it should not be 2103a8cb16ddSEric Dumazet * forwarded second time, but still can be delivered locally. 21041da177e4SLinus Torvalds */ 21051da177e4SLinus Torvalds if (IPCB(skb)->flags & IPSKB_FORWARDED) 21061da177e4SLinus Torvalds goto dont_forward; 21071da177e4SLinus Torvalds 2108417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2109ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) { 2110e40dbc51SBen Greear kfree_skb(skb); 2111ee3f1aafSDavid S. Miller return PTR_ERR(mrt); 21125e2b61f7SDavid S. Miller } 21131da177e4SLinus Torvalds if (!local) { 21141da177e4SLinus Torvalds if (IPCB(skb)->opt.router_alert) { 21151da177e4SLinus Torvalds if (ip_call_ra_chain(skb)) 21161da177e4SLinus Torvalds return 0; 2117eddc9ec5SArnaldo Carvalho de Melo } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP) { 21181da177e4SLinus Torvalds /* IGMPv1 (and broken IGMPv2 implementations sort of 21194c968709SEric Dumazet * Cisco IOS <= 11.2(8)) do not put router alert 21204c968709SEric Dumazet * option to IGMP packets destined to routable 21214c968709SEric Dumazet * groups. It is very bad, because it means 21224c968709SEric Dumazet * that we can forward NO IGMP messages. 21231da177e4SLinus Torvalds */ 21244c968709SEric Dumazet struct sock *mroute_sk; 21254c968709SEric Dumazet 21264c968709SEric Dumazet mroute_sk = rcu_dereference(mrt->mroute_sk); 21274c968709SEric Dumazet if (mroute_sk) { 21282715bcf9SPatrick McHardy nf_reset(skb); 21294c968709SEric Dumazet raw_rcv(mroute_sk, skb); 21301da177e4SLinus Torvalds return 0; 21311da177e4SLinus Torvalds } 21321da177e4SLinus Torvalds } 21331da177e4SLinus Torvalds } 21341da177e4SLinus Torvalds 2135a8c9486bSEric Dumazet /* already under rcu_read_lock() */ 21360c12295aSPatrick McHardy cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); 213751456b29SIan Morris if (!cache) { 2138bcfc7d33SThomas Winter int vif = ipmr_find_vif(mrt, dev); 2139660b26dcSNicolas Dichtel 2140660b26dcSNicolas Dichtel if (vif >= 0) 2141660b26dcSNicolas Dichtel cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr, 2142660b26dcSNicolas Dichtel vif); 2143660b26dcSNicolas Dichtel } 21441da177e4SLinus Torvalds 21457ef8f65dSNikolay Aleksandrov /* No usable cache entry */ 214651456b29SIan Morris if (!cache) { 21471da177e4SLinus Torvalds int vif; 21481da177e4SLinus Torvalds 21491da177e4SLinus Torvalds if (local) { 21501da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 21511da177e4SLinus Torvalds ip_local_deliver(skb); 215251456b29SIan Morris if (!skb2) 21531da177e4SLinus Torvalds return -ENOBUFS; 21541da177e4SLinus Torvalds skb = skb2; 21551da177e4SLinus Torvalds } 21561da177e4SLinus Torvalds 2157a8c9486bSEric Dumazet read_lock(&mrt_lock); 2158bcfc7d33SThomas Winter vif = ipmr_find_vif(mrt, dev); 21591da177e4SLinus Torvalds if (vif >= 0) { 21604b1f0d33SDonald Sharp int err2 = ipmr_cache_unresolved(mrt, vif, skb, dev); 21611da177e4SLinus Torvalds read_unlock(&mrt_lock); 21621da177e4SLinus Torvalds 21630eae88f3SEric Dumazet return err2; 21641da177e4SLinus Torvalds } 21651da177e4SLinus Torvalds read_unlock(&mrt_lock); 21661da177e4SLinus Torvalds kfree_skb(skb); 21671da177e4SLinus Torvalds return -ENODEV; 21681da177e4SLinus Torvalds } 21691da177e4SLinus Torvalds 2170a8c9486bSEric Dumazet read_lock(&mrt_lock); 21714b1f0d33SDonald Sharp ip_mr_forward(net, mrt, dev, skb, cache, local); 21721da177e4SLinus Torvalds read_unlock(&mrt_lock); 21731da177e4SLinus Torvalds 21741da177e4SLinus Torvalds if (local) 21751da177e4SLinus Torvalds return ip_local_deliver(skb); 21761da177e4SLinus Torvalds 21771da177e4SLinus Torvalds return 0; 21781da177e4SLinus Torvalds 21791da177e4SLinus Torvalds dont_forward: 21801da177e4SLinus Torvalds if (local) 21811da177e4SLinus Torvalds return ip_local_deliver(skb); 21821da177e4SLinus Torvalds kfree_skb(skb); 21831da177e4SLinus Torvalds return 0; 21841da177e4SLinus Torvalds } 21851da177e4SLinus Torvalds 2186b1879204SIlpo Järvinen #ifdef CONFIG_IP_PIMSM_V1 21877ef8f65dSNikolay Aleksandrov /* Handle IGMP messages of PIMv1 */ 2188b1879204SIlpo Järvinen int pim_rcv_v1(struct sk_buff *skb) 2189b1879204SIlpo Järvinen { 2190b1879204SIlpo Järvinen struct igmphdr *pim; 21914feb88e5SBenjamin Thery struct net *net = dev_net(skb->dev); 2192f0ad0860SPatrick McHardy struct mr_table *mrt; 2193b1879204SIlpo Järvinen 2194b1879204SIlpo Järvinen if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 2195b1879204SIlpo Järvinen goto drop; 2196b1879204SIlpo Järvinen 2197b1879204SIlpo Järvinen pim = igmp_hdr(skb); 2198b1879204SIlpo Järvinen 2199417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2200ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) 2201f0ad0860SPatrick McHardy goto drop; 22020c12295aSPatrick McHardy if (!mrt->mroute_do_pim || 2203b1879204SIlpo Järvinen pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) 2204b1879204SIlpo Järvinen goto drop; 2205b1879204SIlpo Järvinen 2206f0ad0860SPatrick McHardy if (__pim_rcv(mrt, skb, sizeof(*pim))) { 22071da177e4SLinus Torvalds drop: 22081da177e4SLinus Torvalds kfree_skb(skb); 2209b1879204SIlpo Järvinen } 22101da177e4SLinus Torvalds return 0; 22111da177e4SLinus Torvalds } 22121da177e4SLinus Torvalds #endif 22131da177e4SLinus Torvalds 22141da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V2 22151da177e4SLinus Torvalds static int pim_rcv(struct sk_buff *skb) 22161da177e4SLinus Torvalds { 22171da177e4SLinus Torvalds struct pimreghdr *pim; 2218f0ad0860SPatrick McHardy struct net *net = dev_net(skb->dev); 2219f0ad0860SPatrick McHardy struct mr_table *mrt; 22201da177e4SLinus Torvalds 2221b1879204SIlpo Järvinen if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 22221da177e4SLinus Torvalds goto drop; 22231da177e4SLinus Torvalds 22249c70220bSArnaldo Carvalho de Melo pim = (struct pimreghdr *)skb_transport_header(skb); 222556245caeSNikolay Aleksandrov if (pim->type != ((PIM_VERSION << 4) | (PIM_TYPE_REGISTER)) || 22261da177e4SLinus Torvalds (pim->flags & PIM_NULL_REGISTER) || 22271da177e4SLinus Torvalds (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && 2228d3bc23e7SAl Viro csum_fold(skb_checksum(skb, 0, skb->len, 0)))) 22291da177e4SLinus Torvalds goto drop; 22301da177e4SLinus Torvalds 2231417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2232ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) 2233f0ad0860SPatrick McHardy goto drop; 2234f0ad0860SPatrick McHardy if (__pim_rcv(mrt, skb, sizeof(*pim))) { 22351da177e4SLinus Torvalds drop: 22361da177e4SLinus Torvalds kfree_skb(skb); 2237b1879204SIlpo Järvinen } 22381da177e4SLinus Torvalds return 0; 22391da177e4SLinus Torvalds } 22401da177e4SLinus Torvalds #endif 22411da177e4SLinus Torvalds 22429a1b9496SDavid S. Miller int ipmr_get_route(struct net *net, struct sk_buff *skb, 22439a1b9496SDavid S. Miller __be32 saddr, __be32 daddr, 22449f09eaeaSDavid Ahern struct rtmsg *rtm, u32 portid) 22451da177e4SLinus Torvalds { 22461da177e4SLinus Torvalds struct mfc_cache *cache; 22479a1b9496SDavid S. Miller struct mr_table *mrt; 22489a1b9496SDavid S. Miller int err; 22491da177e4SLinus Torvalds 2250f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 225151456b29SIan Morris if (!mrt) 2252f0ad0860SPatrick McHardy return -ENOENT; 2253f0ad0860SPatrick McHardy 2254a8c9486bSEric Dumazet rcu_read_lock(); 22559a1b9496SDavid S. Miller cache = ipmr_cache_find(mrt, saddr, daddr); 225651456b29SIan Morris if (!cache && skb->dev) { 2257660b26dcSNicolas Dichtel int vif = ipmr_find_vif(mrt, skb->dev); 22581da177e4SLinus Torvalds 2259660b26dcSNicolas Dichtel if (vif >= 0) 2260660b26dcSNicolas Dichtel cache = ipmr_cache_find_any(mrt, daddr, vif); 2261660b26dcSNicolas Dichtel } 226251456b29SIan Morris if (!cache) { 226372287490SAlexey Kuznetsov struct sk_buff *skb2; 2264eddc9ec5SArnaldo Carvalho de Melo struct iphdr *iph; 22651da177e4SLinus Torvalds struct net_device *dev; 2266a8cb16ddSEric Dumazet int vif = -1; 22671da177e4SLinus Torvalds 22681da177e4SLinus Torvalds dev = skb->dev; 2269a8c9486bSEric Dumazet read_lock(&mrt_lock); 2270a8cb16ddSEric Dumazet if (dev) 2271a8cb16ddSEric Dumazet vif = ipmr_find_vif(mrt, dev); 2272a8cb16ddSEric Dumazet if (vif < 0) { 22731da177e4SLinus Torvalds read_unlock(&mrt_lock); 2274a8c9486bSEric Dumazet rcu_read_unlock(); 22751da177e4SLinus Torvalds return -ENODEV; 22761da177e4SLinus Torvalds } 227772287490SAlexey Kuznetsov skb2 = skb_clone(skb, GFP_ATOMIC); 227872287490SAlexey Kuznetsov if (!skb2) { 227972287490SAlexey Kuznetsov read_unlock(&mrt_lock); 2280a8c9486bSEric Dumazet rcu_read_unlock(); 228172287490SAlexey Kuznetsov return -ENOMEM; 228272287490SAlexey Kuznetsov } 228372287490SAlexey Kuznetsov 22842cf75070SNikolay Aleksandrov NETLINK_CB(skb2).portid = portid; 2285e2d1bca7SArnaldo Carvalho de Melo skb_push(skb2, sizeof(struct iphdr)); 2286e2d1bca7SArnaldo Carvalho de Melo skb_reset_network_header(skb2); 2287eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb2); 2288eddc9ec5SArnaldo Carvalho de Melo iph->ihl = sizeof(struct iphdr) >> 2; 22899a1b9496SDavid S. Miller iph->saddr = saddr; 22909a1b9496SDavid S. Miller iph->daddr = daddr; 2291eddc9ec5SArnaldo Carvalho de Melo iph->version = 0; 22924b1f0d33SDonald Sharp err = ipmr_cache_unresolved(mrt, vif, skb2, dev); 22931da177e4SLinus Torvalds read_unlock(&mrt_lock); 2294a8c9486bSEric Dumazet rcu_read_unlock(); 22951da177e4SLinus Torvalds return err; 22961da177e4SLinus Torvalds } 22971da177e4SLinus Torvalds 2298a8c9486bSEric Dumazet read_lock(&mrt_lock); 22997b0db857SYuval Mintz err = mr_fill_mroute(mrt, skb, &cache->_c, rtm); 23001da177e4SLinus Torvalds read_unlock(&mrt_lock); 2301a8c9486bSEric Dumazet rcu_read_unlock(); 23021da177e4SLinus Torvalds return err; 23031da177e4SLinus Torvalds } 23041da177e4SLinus Torvalds 2305cb6a4e46SPatrick McHardy static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 230665886f43SNicolas Dichtel u32 portid, u32 seq, struct mfc_cache *c, int cmd, 230765886f43SNicolas Dichtel int flags) 2308cb6a4e46SPatrick McHardy { 2309cb6a4e46SPatrick McHardy struct nlmsghdr *nlh; 2310cb6a4e46SPatrick McHardy struct rtmsg *rtm; 23111eb99af5SNicolas Dichtel int err; 2312cb6a4e46SPatrick McHardy 231365886f43SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags); 231451456b29SIan Morris if (!nlh) 2315cb6a4e46SPatrick McHardy return -EMSGSIZE; 2316cb6a4e46SPatrick McHardy 2317cb6a4e46SPatrick McHardy rtm = nlmsg_data(nlh); 2318cb6a4e46SPatrick McHardy rtm->rtm_family = RTNL_FAMILY_IPMR; 2319cb6a4e46SPatrick McHardy rtm->rtm_dst_len = 32; 2320cb6a4e46SPatrick McHardy rtm->rtm_src_len = 32; 2321cb6a4e46SPatrick McHardy rtm->rtm_tos = 0; 2322cb6a4e46SPatrick McHardy rtm->rtm_table = mrt->id; 2323f3756b79SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, mrt->id)) 2324f3756b79SDavid S. Miller goto nla_put_failure; 2325cb6a4e46SPatrick McHardy rtm->rtm_type = RTN_MULTICAST; 2326cb6a4e46SPatrick McHardy rtm->rtm_scope = RT_SCOPE_UNIVERSE; 2327494fff56SYuval Mintz if (c->_c.mfc_flags & MFC_STATIC) 23289a68ac72SNicolas Dichtel rtm->rtm_protocol = RTPROT_STATIC; 23299a68ac72SNicolas Dichtel else 23309a68ac72SNicolas Dichtel rtm->rtm_protocol = RTPROT_MROUTED; 2331cb6a4e46SPatrick McHardy rtm->rtm_flags = 0; 2332cb6a4e46SPatrick McHardy 2333930345eaSJiri Benc if (nla_put_in_addr(skb, RTA_SRC, c->mfc_origin) || 2334930345eaSJiri Benc nla_put_in_addr(skb, RTA_DST, c->mfc_mcastgrp)) 2335f3756b79SDavid S. Miller goto nla_put_failure; 23367b0db857SYuval Mintz err = mr_fill_mroute(mrt, skb, &c->_c, rtm); 23371eb99af5SNicolas Dichtel /* do not break the dump if cache is unresolved */ 23381eb99af5SNicolas Dichtel if (err < 0 && err != -ENOENT) 2339cb6a4e46SPatrick McHardy goto nla_put_failure; 2340cb6a4e46SPatrick McHardy 2341053c095aSJohannes Berg nlmsg_end(skb, nlh); 2342053c095aSJohannes Berg return 0; 2343cb6a4e46SPatrick McHardy 2344cb6a4e46SPatrick McHardy nla_put_failure: 2345cb6a4e46SPatrick McHardy nlmsg_cancel(skb, nlh); 2346cb6a4e46SPatrick McHardy return -EMSGSIZE; 2347cb6a4e46SPatrick McHardy } 2348cb6a4e46SPatrick McHardy 23497b0db857SYuval Mintz static int _ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 23507b0db857SYuval Mintz u32 portid, u32 seq, struct mr_mfc *c, int cmd, 23517b0db857SYuval Mintz int flags) 23527b0db857SYuval Mintz { 23537b0db857SYuval Mintz return ipmr_fill_mroute(mrt, skb, portid, seq, (struct mfc_cache *)c, 23547b0db857SYuval Mintz cmd, flags); 23557b0db857SYuval Mintz } 23567b0db857SYuval Mintz 23578cd3ac9fSNicolas Dichtel static size_t mroute_msgsize(bool unresolved, int maxvif) 23588cd3ac9fSNicolas Dichtel { 23598cd3ac9fSNicolas Dichtel size_t len = 23608cd3ac9fSNicolas Dichtel NLMSG_ALIGN(sizeof(struct rtmsg)) 23618cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_TABLE */ 23628cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_SRC */ 23638cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_DST */ 23648cd3ac9fSNicolas Dichtel ; 23658cd3ac9fSNicolas Dichtel 23668cd3ac9fSNicolas Dichtel if (!unresolved) 23678cd3ac9fSNicolas Dichtel len = len 23688cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_IIF */ 23698cd3ac9fSNicolas Dichtel + nla_total_size(0) /* RTA_MULTIPATH */ 23708cd3ac9fSNicolas Dichtel + maxvif * NLA_ALIGN(sizeof(struct rtnexthop)) 23718cd3ac9fSNicolas Dichtel /* RTA_MFC_STATS */ 2372a9a08042SNicolas Dichtel + nla_total_size_64bit(sizeof(struct rta_mfc_stats)) 23738cd3ac9fSNicolas Dichtel ; 23748cd3ac9fSNicolas Dichtel 23758cd3ac9fSNicolas Dichtel return len; 23768cd3ac9fSNicolas Dichtel } 23778cd3ac9fSNicolas Dichtel 23788cd3ac9fSNicolas Dichtel static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, 23798cd3ac9fSNicolas Dichtel int cmd) 23808cd3ac9fSNicolas Dichtel { 23818cd3ac9fSNicolas Dichtel struct net *net = read_pnet(&mrt->net); 23828cd3ac9fSNicolas Dichtel struct sk_buff *skb; 23838cd3ac9fSNicolas Dichtel int err = -ENOBUFS; 23848cd3ac9fSNicolas Dichtel 2385494fff56SYuval Mintz skb = nlmsg_new(mroute_msgsize(mfc->_c.mfc_parent >= MAXVIFS, 2386494fff56SYuval Mintz mrt->maxvif), 23878cd3ac9fSNicolas Dichtel GFP_ATOMIC); 238851456b29SIan Morris if (!skb) 23898cd3ac9fSNicolas Dichtel goto errout; 23908cd3ac9fSNicolas Dichtel 239165886f43SNicolas Dichtel err = ipmr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0); 23928cd3ac9fSNicolas Dichtel if (err < 0) 23938cd3ac9fSNicolas Dichtel goto errout; 23948cd3ac9fSNicolas Dichtel 23958cd3ac9fSNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE, NULL, GFP_ATOMIC); 23968cd3ac9fSNicolas Dichtel return; 23978cd3ac9fSNicolas Dichtel 23988cd3ac9fSNicolas Dichtel errout: 23998cd3ac9fSNicolas Dichtel kfree_skb(skb); 24008cd3ac9fSNicolas Dichtel if (err < 0) 24018cd3ac9fSNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE, err); 24028cd3ac9fSNicolas Dichtel } 24038cd3ac9fSNicolas Dichtel 24045a645dd8SJulien Gomes static size_t igmpmsg_netlink_msgsize(size_t payloadlen) 24055a645dd8SJulien Gomes { 24065a645dd8SJulien Gomes size_t len = 24075a645dd8SJulien Gomes NLMSG_ALIGN(sizeof(struct rtgenmsg)) 24085a645dd8SJulien Gomes + nla_total_size(1) /* IPMRA_CREPORT_MSGTYPE */ 24095a645dd8SJulien Gomes + nla_total_size(4) /* IPMRA_CREPORT_VIF_ID */ 24105a645dd8SJulien Gomes + nla_total_size(4) /* IPMRA_CREPORT_SRC_ADDR */ 24115a645dd8SJulien Gomes + nla_total_size(4) /* IPMRA_CREPORT_DST_ADDR */ 24125a645dd8SJulien Gomes /* IPMRA_CREPORT_PKT */ 24135a645dd8SJulien Gomes + nla_total_size(payloadlen) 24145a645dd8SJulien Gomes ; 24155a645dd8SJulien Gomes 24165a645dd8SJulien Gomes return len; 24175a645dd8SJulien Gomes } 24185a645dd8SJulien Gomes 24195a645dd8SJulien Gomes static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt) 24205a645dd8SJulien Gomes { 24215a645dd8SJulien Gomes struct net *net = read_pnet(&mrt->net); 24225a645dd8SJulien Gomes struct nlmsghdr *nlh; 24235a645dd8SJulien Gomes struct rtgenmsg *rtgenm; 24245a645dd8SJulien Gomes struct igmpmsg *msg; 24255a645dd8SJulien Gomes struct sk_buff *skb; 24265a645dd8SJulien Gomes struct nlattr *nla; 24275a645dd8SJulien Gomes int payloadlen; 24285a645dd8SJulien Gomes 24295a645dd8SJulien Gomes payloadlen = pkt->len - sizeof(struct igmpmsg); 24305a645dd8SJulien Gomes msg = (struct igmpmsg *)skb_network_header(pkt); 24315a645dd8SJulien Gomes 24325a645dd8SJulien Gomes skb = nlmsg_new(igmpmsg_netlink_msgsize(payloadlen), GFP_ATOMIC); 24335a645dd8SJulien Gomes if (!skb) 24345a645dd8SJulien Gomes goto errout; 24355a645dd8SJulien Gomes 24365a645dd8SJulien Gomes nlh = nlmsg_put(skb, 0, 0, RTM_NEWCACHEREPORT, 24375a645dd8SJulien Gomes sizeof(struct rtgenmsg), 0); 24385a645dd8SJulien Gomes if (!nlh) 24395a645dd8SJulien Gomes goto errout; 24405a645dd8SJulien Gomes rtgenm = nlmsg_data(nlh); 24415a645dd8SJulien Gomes rtgenm->rtgen_family = RTNL_FAMILY_IPMR; 24425a645dd8SJulien Gomes if (nla_put_u8(skb, IPMRA_CREPORT_MSGTYPE, msg->im_msgtype) || 24435a645dd8SJulien Gomes nla_put_u32(skb, IPMRA_CREPORT_VIF_ID, msg->im_vif) || 24445a645dd8SJulien Gomes nla_put_in_addr(skb, IPMRA_CREPORT_SRC_ADDR, 24455a645dd8SJulien Gomes msg->im_src.s_addr) || 24465a645dd8SJulien Gomes nla_put_in_addr(skb, IPMRA_CREPORT_DST_ADDR, 24475a645dd8SJulien Gomes msg->im_dst.s_addr)) 24485a645dd8SJulien Gomes goto nla_put_failure; 24495a645dd8SJulien Gomes 24505a645dd8SJulien Gomes nla = nla_reserve(skb, IPMRA_CREPORT_PKT, payloadlen); 24515a645dd8SJulien Gomes if (!nla || skb_copy_bits(pkt, sizeof(struct igmpmsg), 24525a645dd8SJulien Gomes nla_data(nla), payloadlen)) 24535a645dd8SJulien Gomes goto nla_put_failure; 24545a645dd8SJulien Gomes 24555a645dd8SJulien Gomes nlmsg_end(skb, nlh); 24565a645dd8SJulien Gomes 24575a645dd8SJulien Gomes rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE_R, NULL, GFP_ATOMIC); 24585a645dd8SJulien Gomes return; 24595a645dd8SJulien Gomes 24605a645dd8SJulien Gomes nla_put_failure: 24615a645dd8SJulien Gomes nlmsg_cancel(skb, nlh); 24625a645dd8SJulien Gomes errout: 24635a645dd8SJulien Gomes kfree_skb(skb); 24645a645dd8SJulien Gomes rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE_R, -ENOBUFS); 24655a645dd8SJulien Gomes } 24665a645dd8SJulien Gomes 24674f75ba69SDonald Sharp static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, 24684f75ba69SDonald Sharp struct netlink_ext_ack *extack) 24694f75ba69SDonald Sharp { 24704f75ba69SDonald Sharp struct net *net = sock_net(in_skb->sk); 24714f75ba69SDonald Sharp struct nlattr *tb[RTA_MAX + 1]; 24724f75ba69SDonald Sharp struct sk_buff *skb = NULL; 24734f75ba69SDonald Sharp struct mfc_cache *cache; 24744f75ba69SDonald Sharp struct mr_table *mrt; 24754f75ba69SDonald Sharp struct rtmsg *rtm; 24764f75ba69SDonald Sharp __be32 src, grp; 24774f75ba69SDonald Sharp u32 tableid; 24784f75ba69SDonald Sharp int err; 24794f75ba69SDonald Sharp 24804f75ba69SDonald Sharp err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, 24814f75ba69SDonald Sharp rtm_ipv4_policy, extack); 24824f75ba69SDonald Sharp if (err < 0) 24834f75ba69SDonald Sharp goto errout; 24844f75ba69SDonald Sharp 24854f75ba69SDonald Sharp rtm = nlmsg_data(nlh); 24864f75ba69SDonald Sharp 24874f75ba69SDonald Sharp src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0; 24884f75ba69SDonald Sharp grp = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0; 24894f75ba69SDonald Sharp tableid = tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : 0; 24904f75ba69SDonald Sharp 24914f75ba69SDonald Sharp mrt = ipmr_get_table(net, tableid ? tableid : RT_TABLE_DEFAULT); 24922e3d232eSDan Carpenter if (!mrt) { 24932e3d232eSDan Carpenter err = -ENOENT; 24944f75ba69SDonald Sharp goto errout_free; 24954f75ba69SDonald Sharp } 24964f75ba69SDonald Sharp 24974f75ba69SDonald Sharp /* entries are added/deleted only under RTNL */ 24984f75ba69SDonald Sharp rcu_read_lock(); 24994f75ba69SDonald Sharp cache = ipmr_cache_find(mrt, src, grp); 25004f75ba69SDonald Sharp rcu_read_unlock(); 25014f75ba69SDonald Sharp if (!cache) { 25024f75ba69SDonald Sharp err = -ENOENT; 25034f75ba69SDonald Sharp goto errout_free; 25044f75ba69SDonald Sharp } 25054f75ba69SDonald Sharp 25064f75ba69SDonald Sharp skb = nlmsg_new(mroute_msgsize(false, mrt->maxvif), GFP_KERNEL); 25074f75ba69SDonald Sharp if (!skb) { 25084f75ba69SDonald Sharp err = -ENOBUFS; 25094f75ba69SDonald Sharp goto errout_free; 25104f75ba69SDonald Sharp } 25114f75ba69SDonald Sharp 25124f75ba69SDonald Sharp err = ipmr_fill_mroute(mrt, skb, NETLINK_CB(in_skb).portid, 25134f75ba69SDonald Sharp nlh->nlmsg_seq, cache, 25144f75ba69SDonald Sharp RTM_NEWROUTE, 0); 25154f75ba69SDonald Sharp if (err < 0) 25164f75ba69SDonald Sharp goto errout_free; 25174f75ba69SDonald Sharp 25184f75ba69SDonald Sharp err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 25194f75ba69SDonald Sharp 25204f75ba69SDonald Sharp errout: 25214f75ba69SDonald Sharp return err; 25224f75ba69SDonald Sharp 25234f75ba69SDonald Sharp errout_free: 25244f75ba69SDonald Sharp kfree_skb(skb); 25254f75ba69SDonald Sharp goto errout; 25264f75ba69SDonald Sharp } 25274f75ba69SDonald Sharp 2528cb6a4e46SPatrick McHardy static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) 2529cb6a4e46SPatrick McHardy { 25307b0db857SYuval Mintz return mr_rtm_dumproute(skb, cb, ipmr_mr_table_iter, 25317b0db857SYuval Mintz _ipmr_fill_mroute, &mfc_unres_lock); 2532cb6a4e46SPatrick McHardy } 2533cb6a4e46SPatrick McHardy 2534ccbb0aa6SNikolay Aleksandrov static const struct nla_policy rtm_ipmr_policy[RTA_MAX + 1] = { 2535ccbb0aa6SNikolay Aleksandrov [RTA_SRC] = { .type = NLA_U32 }, 2536ccbb0aa6SNikolay Aleksandrov [RTA_DST] = { .type = NLA_U32 }, 2537ccbb0aa6SNikolay Aleksandrov [RTA_IIF] = { .type = NLA_U32 }, 2538ccbb0aa6SNikolay Aleksandrov [RTA_TABLE] = { .type = NLA_U32 }, 2539ccbb0aa6SNikolay Aleksandrov [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 2540ccbb0aa6SNikolay Aleksandrov }; 2541ccbb0aa6SNikolay Aleksandrov 2542ccbb0aa6SNikolay Aleksandrov static bool ipmr_rtm_validate_proto(unsigned char rtm_protocol) 2543ccbb0aa6SNikolay Aleksandrov { 2544ccbb0aa6SNikolay Aleksandrov switch (rtm_protocol) { 2545ccbb0aa6SNikolay Aleksandrov case RTPROT_STATIC: 2546ccbb0aa6SNikolay Aleksandrov case RTPROT_MROUTED: 2547ccbb0aa6SNikolay Aleksandrov return true; 2548ccbb0aa6SNikolay Aleksandrov } 2549ccbb0aa6SNikolay Aleksandrov return false; 2550ccbb0aa6SNikolay Aleksandrov } 2551ccbb0aa6SNikolay Aleksandrov 2552ccbb0aa6SNikolay Aleksandrov static int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl *mfcc) 2553ccbb0aa6SNikolay Aleksandrov { 2554ccbb0aa6SNikolay Aleksandrov struct rtnexthop *rtnh = nla_data(nla); 2555ccbb0aa6SNikolay Aleksandrov int remaining = nla_len(nla), vifi = 0; 2556ccbb0aa6SNikolay Aleksandrov 2557ccbb0aa6SNikolay Aleksandrov while (rtnh_ok(rtnh, remaining)) { 2558ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_ttls[vifi] = rtnh->rtnh_hops; 2559ccbb0aa6SNikolay Aleksandrov if (++vifi == MAXVIFS) 2560ccbb0aa6SNikolay Aleksandrov break; 2561ccbb0aa6SNikolay Aleksandrov rtnh = rtnh_next(rtnh, &remaining); 2562ccbb0aa6SNikolay Aleksandrov } 2563ccbb0aa6SNikolay Aleksandrov 2564ccbb0aa6SNikolay Aleksandrov return remaining > 0 ? -EINVAL : vifi; 2565ccbb0aa6SNikolay Aleksandrov } 2566ccbb0aa6SNikolay Aleksandrov 2567ccbb0aa6SNikolay Aleksandrov /* returns < 0 on error, 0 for ADD_MFC and 1 for ADD_MFC_PROXY */ 2568ccbb0aa6SNikolay Aleksandrov static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh, 2569ccbb0aa6SNikolay Aleksandrov struct mfcctl *mfcc, int *mrtsock, 2570c21ef3e3SDavid Ahern struct mr_table **mrtret, 2571c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 2572ccbb0aa6SNikolay Aleksandrov { 2573ccbb0aa6SNikolay Aleksandrov struct net_device *dev = NULL; 2574ccbb0aa6SNikolay Aleksandrov u32 tblid = RT_TABLE_DEFAULT; 2575ccbb0aa6SNikolay Aleksandrov struct mr_table *mrt; 2576ccbb0aa6SNikolay Aleksandrov struct nlattr *attr; 2577ccbb0aa6SNikolay Aleksandrov struct rtmsg *rtm; 2578ccbb0aa6SNikolay Aleksandrov int ret, rem; 2579ccbb0aa6SNikolay Aleksandrov 2580fceb6435SJohannes Berg ret = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipmr_policy, 2581c21ef3e3SDavid Ahern extack); 2582ccbb0aa6SNikolay Aleksandrov if (ret < 0) 2583ccbb0aa6SNikolay Aleksandrov goto out; 2584ccbb0aa6SNikolay Aleksandrov rtm = nlmsg_data(nlh); 2585ccbb0aa6SNikolay Aleksandrov 2586ccbb0aa6SNikolay Aleksandrov ret = -EINVAL; 2587ccbb0aa6SNikolay Aleksandrov if (rtm->rtm_family != RTNL_FAMILY_IPMR || rtm->rtm_dst_len != 32 || 2588ccbb0aa6SNikolay Aleksandrov rtm->rtm_type != RTN_MULTICAST || 2589ccbb0aa6SNikolay Aleksandrov rtm->rtm_scope != RT_SCOPE_UNIVERSE || 2590ccbb0aa6SNikolay Aleksandrov !ipmr_rtm_validate_proto(rtm->rtm_protocol)) 2591ccbb0aa6SNikolay Aleksandrov goto out; 2592ccbb0aa6SNikolay Aleksandrov 2593ccbb0aa6SNikolay Aleksandrov memset(mfcc, 0, sizeof(*mfcc)); 2594ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_parent = -1; 2595ccbb0aa6SNikolay Aleksandrov ret = 0; 2596ccbb0aa6SNikolay Aleksandrov nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), rem) { 2597ccbb0aa6SNikolay Aleksandrov switch (nla_type(attr)) { 2598ccbb0aa6SNikolay Aleksandrov case RTA_SRC: 2599ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_origin.s_addr = nla_get_be32(attr); 2600ccbb0aa6SNikolay Aleksandrov break; 2601ccbb0aa6SNikolay Aleksandrov case RTA_DST: 2602ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_mcastgrp.s_addr = nla_get_be32(attr); 2603ccbb0aa6SNikolay Aleksandrov break; 2604ccbb0aa6SNikolay Aleksandrov case RTA_IIF: 2605ccbb0aa6SNikolay Aleksandrov dev = __dev_get_by_index(net, nla_get_u32(attr)); 2606ccbb0aa6SNikolay Aleksandrov if (!dev) { 2607ccbb0aa6SNikolay Aleksandrov ret = -ENODEV; 2608ccbb0aa6SNikolay Aleksandrov goto out; 2609ccbb0aa6SNikolay Aleksandrov } 2610ccbb0aa6SNikolay Aleksandrov break; 2611ccbb0aa6SNikolay Aleksandrov case RTA_MULTIPATH: 2612ccbb0aa6SNikolay Aleksandrov if (ipmr_nla_get_ttls(attr, mfcc) < 0) { 2613ccbb0aa6SNikolay Aleksandrov ret = -EINVAL; 2614ccbb0aa6SNikolay Aleksandrov goto out; 2615ccbb0aa6SNikolay Aleksandrov } 2616ccbb0aa6SNikolay Aleksandrov break; 2617ccbb0aa6SNikolay Aleksandrov case RTA_PREFSRC: 2618ccbb0aa6SNikolay Aleksandrov ret = 1; 2619ccbb0aa6SNikolay Aleksandrov break; 2620ccbb0aa6SNikolay Aleksandrov case RTA_TABLE: 2621ccbb0aa6SNikolay Aleksandrov tblid = nla_get_u32(attr); 2622ccbb0aa6SNikolay Aleksandrov break; 2623ccbb0aa6SNikolay Aleksandrov } 2624ccbb0aa6SNikolay Aleksandrov } 2625ccbb0aa6SNikolay Aleksandrov mrt = ipmr_get_table(net, tblid); 2626ccbb0aa6SNikolay Aleksandrov if (!mrt) { 2627ccbb0aa6SNikolay Aleksandrov ret = -ENOENT; 2628ccbb0aa6SNikolay Aleksandrov goto out; 2629ccbb0aa6SNikolay Aleksandrov } 2630ccbb0aa6SNikolay Aleksandrov *mrtret = mrt; 2631ccbb0aa6SNikolay Aleksandrov *mrtsock = rtm->rtm_protocol == RTPROT_MROUTED ? 1 : 0; 2632ccbb0aa6SNikolay Aleksandrov if (dev) 2633ccbb0aa6SNikolay Aleksandrov mfcc->mfcc_parent = ipmr_find_vif(mrt, dev); 2634ccbb0aa6SNikolay Aleksandrov 2635ccbb0aa6SNikolay Aleksandrov out: 2636ccbb0aa6SNikolay Aleksandrov return ret; 2637ccbb0aa6SNikolay Aleksandrov } 2638ccbb0aa6SNikolay Aleksandrov 2639ccbb0aa6SNikolay Aleksandrov /* takes care of both newroute and delroute */ 2640c21ef3e3SDavid Ahern static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh, 2641c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 2642ccbb0aa6SNikolay Aleksandrov { 2643ccbb0aa6SNikolay Aleksandrov struct net *net = sock_net(skb->sk); 2644ccbb0aa6SNikolay Aleksandrov int ret, mrtsock, parent; 2645ccbb0aa6SNikolay Aleksandrov struct mr_table *tbl; 2646ccbb0aa6SNikolay Aleksandrov struct mfcctl mfcc; 2647ccbb0aa6SNikolay Aleksandrov 2648ccbb0aa6SNikolay Aleksandrov mrtsock = 0; 2649ccbb0aa6SNikolay Aleksandrov tbl = NULL; 2650c21ef3e3SDavid Ahern ret = rtm_to_ipmr_mfcc(net, nlh, &mfcc, &mrtsock, &tbl, extack); 2651ccbb0aa6SNikolay Aleksandrov if (ret < 0) 2652ccbb0aa6SNikolay Aleksandrov return ret; 2653ccbb0aa6SNikolay Aleksandrov 2654ccbb0aa6SNikolay Aleksandrov parent = ret ? mfcc.mfcc_parent : -1; 2655ccbb0aa6SNikolay Aleksandrov if (nlh->nlmsg_type == RTM_NEWROUTE) 2656ccbb0aa6SNikolay Aleksandrov return ipmr_mfc_add(net, tbl, &mfcc, mrtsock, parent); 2657ccbb0aa6SNikolay Aleksandrov else 2658ccbb0aa6SNikolay Aleksandrov return ipmr_mfc_delete(tbl, &mfcc, parent); 2659ccbb0aa6SNikolay Aleksandrov } 2660ccbb0aa6SNikolay Aleksandrov 2661772c344dSNikolay Aleksandrov static bool ipmr_fill_table(struct mr_table *mrt, struct sk_buff *skb) 2662772c344dSNikolay Aleksandrov { 2663772c344dSNikolay Aleksandrov u32 queue_len = atomic_read(&mrt->cache_resolve_queue_len); 2664772c344dSNikolay Aleksandrov 2665772c344dSNikolay Aleksandrov if (nla_put_u32(skb, IPMRA_TABLE_ID, mrt->id) || 2666772c344dSNikolay Aleksandrov nla_put_u32(skb, IPMRA_TABLE_CACHE_RES_QUEUE_LEN, queue_len) || 2667772c344dSNikolay Aleksandrov nla_put_s32(skb, IPMRA_TABLE_MROUTE_REG_VIF_NUM, 2668772c344dSNikolay Aleksandrov mrt->mroute_reg_vif_num) || 2669772c344dSNikolay Aleksandrov nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_ASSERT, 2670772c344dSNikolay Aleksandrov mrt->mroute_do_assert) || 2671c921c207SNikolay Aleksandrov nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_PIM, mrt->mroute_do_pim) || 2672c921c207SNikolay Aleksandrov nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_WRVIFWHOLE, 2673c921c207SNikolay Aleksandrov mrt->mroute_do_wrvifwhole)) 2674772c344dSNikolay Aleksandrov return false; 2675772c344dSNikolay Aleksandrov 2676772c344dSNikolay Aleksandrov return true; 2677772c344dSNikolay Aleksandrov } 2678772c344dSNikolay Aleksandrov 2679772c344dSNikolay Aleksandrov static bool ipmr_fill_vif(struct mr_table *mrt, u32 vifid, struct sk_buff *skb) 2680772c344dSNikolay Aleksandrov { 2681772c344dSNikolay Aleksandrov struct nlattr *vif_nest; 2682772c344dSNikolay Aleksandrov struct vif_device *vif; 2683772c344dSNikolay Aleksandrov 2684772c344dSNikolay Aleksandrov /* if the VIF doesn't exist just continue */ 2685772c344dSNikolay Aleksandrov if (!VIF_EXISTS(mrt, vifid)) 2686772c344dSNikolay Aleksandrov return true; 2687772c344dSNikolay Aleksandrov 2688772c344dSNikolay Aleksandrov vif = &mrt->vif_table[vifid]; 2689772c344dSNikolay Aleksandrov vif_nest = nla_nest_start(skb, IPMRA_VIF); 2690772c344dSNikolay Aleksandrov if (!vif_nest) 2691772c344dSNikolay Aleksandrov return false; 2692772c344dSNikolay Aleksandrov if (nla_put_u32(skb, IPMRA_VIFA_IFINDEX, vif->dev->ifindex) || 2693772c344dSNikolay Aleksandrov nla_put_u32(skb, IPMRA_VIFA_VIF_ID, vifid) || 2694772c344dSNikolay Aleksandrov nla_put_u16(skb, IPMRA_VIFA_FLAGS, vif->flags) || 2695772c344dSNikolay Aleksandrov nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_IN, vif->bytes_in, 2696772c344dSNikolay Aleksandrov IPMRA_VIFA_PAD) || 2697772c344dSNikolay Aleksandrov nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_OUT, vif->bytes_out, 2698772c344dSNikolay Aleksandrov IPMRA_VIFA_PAD) || 2699772c344dSNikolay Aleksandrov nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_IN, vif->pkt_in, 2700772c344dSNikolay Aleksandrov IPMRA_VIFA_PAD) || 2701772c344dSNikolay Aleksandrov nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_OUT, vif->pkt_out, 2702772c344dSNikolay Aleksandrov IPMRA_VIFA_PAD) || 2703772c344dSNikolay Aleksandrov nla_put_be32(skb, IPMRA_VIFA_LOCAL_ADDR, vif->local) || 2704772c344dSNikolay Aleksandrov nla_put_be32(skb, IPMRA_VIFA_REMOTE_ADDR, vif->remote)) { 2705772c344dSNikolay Aleksandrov nla_nest_cancel(skb, vif_nest); 2706772c344dSNikolay Aleksandrov return false; 2707772c344dSNikolay Aleksandrov } 2708772c344dSNikolay Aleksandrov nla_nest_end(skb, vif_nest); 2709772c344dSNikolay Aleksandrov 2710772c344dSNikolay Aleksandrov return true; 2711772c344dSNikolay Aleksandrov } 2712772c344dSNikolay Aleksandrov 2713*14fc5bb2SDavid Ahern static int ipmr_valid_dumplink(const struct nlmsghdr *nlh, 2714*14fc5bb2SDavid Ahern struct netlink_ext_ack *extack) 2715*14fc5bb2SDavid Ahern { 2716*14fc5bb2SDavid Ahern struct ifinfomsg *ifm; 2717*14fc5bb2SDavid Ahern 2718*14fc5bb2SDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 2719*14fc5bb2SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid header for ipmr link dump"); 2720*14fc5bb2SDavid Ahern return -EINVAL; 2721*14fc5bb2SDavid Ahern } 2722*14fc5bb2SDavid Ahern 2723*14fc5bb2SDavid Ahern if (nlmsg_attrlen(nlh, sizeof(*ifm))) { 2724*14fc5bb2SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid data after header in ipmr link dump"); 2725*14fc5bb2SDavid Ahern return -EINVAL; 2726*14fc5bb2SDavid Ahern } 2727*14fc5bb2SDavid Ahern 2728*14fc5bb2SDavid Ahern ifm = nlmsg_data(nlh); 2729*14fc5bb2SDavid Ahern if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags || 2730*14fc5bb2SDavid Ahern ifm->ifi_change || ifm->ifi_index) { 2731*14fc5bb2SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in header for ipmr link dump request"); 2732*14fc5bb2SDavid Ahern return -EINVAL; 2733*14fc5bb2SDavid Ahern } 2734*14fc5bb2SDavid Ahern 2735*14fc5bb2SDavid Ahern return 0; 2736*14fc5bb2SDavid Ahern } 2737*14fc5bb2SDavid Ahern 2738772c344dSNikolay Aleksandrov static int ipmr_rtm_dumplink(struct sk_buff *skb, struct netlink_callback *cb) 2739772c344dSNikolay Aleksandrov { 2740772c344dSNikolay Aleksandrov struct net *net = sock_net(skb->sk); 2741772c344dSNikolay Aleksandrov struct nlmsghdr *nlh = NULL; 2742772c344dSNikolay Aleksandrov unsigned int t = 0, s_t; 2743772c344dSNikolay Aleksandrov unsigned int e = 0, s_e; 2744772c344dSNikolay Aleksandrov struct mr_table *mrt; 2745772c344dSNikolay Aleksandrov 2746*14fc5bb2SDavid Ahern if (cb->strict_check) { 2747*14fc5bb2SDavid Ahern int err = ipmr_valid_dumplink(cb->nlh, cb->extack); 2748*14fc5bb2SDavid Ahern 2749*14fc5bb2SDavid Ahern if (err < 0) 2750*14fc5bb2SDavid Ahern return err; 2751*14fc5bb2SDavid Ahern } 2752*14fc5bb2SDavid Ahern 2753772c344dSNikolay Aleksandrov s_t = cb->args[0]; 2754772c344dSNikolay Aleksandrov s_e = cb->args[1]; 2755772c344dSNikolay Aleksandrov 2756772c344dSNikolay Aleksandrov ipmr_for_each_table(mrt, net) { 2757772c344dSNikolay Aleksandrov struct nlattr *vifs, *af; 2758772c344dSNikolay Aleksandrov struct ifinfomsg *hdr; 2759772c344dSNikolay Aleksandrov u32 i; 2760772c344dSNikolay Aleksandrov 2761772c344dSNikolay Aleksandrov if (t < s_t) 2762772c344dSNikolay Aleksandrov goto skip_table; 2763772c344dSNikolay Aleksandrov nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, 2764772c344dSNikolay Aleksandrov cb->nlh->nlmsg_seq, RTM_NEWLINK, 2765772c344dSNikolay Aleksandrov sizeof(*hdr), NLM_F_MULTI); 2766772c344dSNikolay Aleksandrov if (!nlh) 2767772c344dSNikolay Aleksandrov break; 2768772c344dSNikolay Aleksandrov 2769772c344dSNikolay Aleksandrov hdr = nlmsg_data(nlh); 2770772c344dSNikolay Aleksandrov memset(hdr, 0, sizeof(*hdr)); 2771772c344dSNikolay Aleksandrov hdr->ifi_family = RTNL_FAMILY_IPMR; 2772772c344dSNikolay Aleksandrov 2773772c344dSNikolay Aleksandrov af = nla_nest_start(skb, IFLA_AF_SPEC); 2774772c344dSNikolay Aleksandrov if (!af) { 2775772c344dSNikolay Aleksandrov nlmsg_cancel(skb, nlh); 2776772c344dSNikolay Aleksandrov goto out; 2777772c344dSNikolay Aleksandrov } 2778772c344dSNikolay Aleksandrov 2779772c344dSNikolay Aleksandrov if (!ipmr_fill_table(mrt, skb)) { 2780772c344dSNikolay Aleksandrov nlmsg_cancel(skb, nlh); 2781772c344dSNikolay Aleksandrov goto out; 2782772c344dSNikolay Aleksandrov } 2783772c344dSNikolay Aleksandrov 2784772c344dSNikolay Aleksandrov vifs = nla_nest_start(skb, IPMRA_TABLE_VIFS); 2785772c344dSNikolay Aleksandrov if (!vifs) { 2786772c344dSNikolay Aleksandrov nla_nest_end(skb, af); 2787772c344dSNikolay Aleksandrov nlmsg_end(skb, nlh); 2788772c344dSNikolay Aleksandrov goto out; 2789772c344dSNikolay Aleksandrov } 2790772c344dSNikolay Aleksandrov for (i = 0; i < mrt->maxvif; i++) { 2791772c344dSNikolay Aleksandrov if (e < s_e) 2792772c344dSNikolay Aleksandrov goto skip_entry; 2793772c344dSNikolay Aleksandrov if (!ipmr_fill_vif(mrt, i, skb)) { 2794772c344dSNikolay Aleksandrov nla_nest_end(skb, vifs); 2795772c344dSNikolay Aleksandrov nla_nest_end(skb, af); 2796772c344dSNikolay Aleksandrov nlmsg_end(skb, nlh); 2797772c344dSNikolay Aleksandrov goto out; 2798772c344dSNikolay Aleksandrov } 2799772c344dSNikolay Aleksandrov skip_entry: 2800772c344dSNikolay Aleksandrov e++; 2801772c344dSNikolay Aleksandrov } 2802772c344dSNikolay Aleksandrov s_e = 0; 2803772c344dSNikolay Aleksandrov e = 0; 2804772c344dSNikolay Aleksandrov nla_nest_end(skb, vifs); 2805772c344dSNikolay Aleksandrov nla_nest_end(skb, af); 2806772c344dSNikolay Aleksandrov nlmsg_end(skb, nlh); 2807772c344dSNikolay Aleksandrov skip_table: 2808772c344dSNikolay Aleksandrov t++; 2809772c344dSNikolay Aleksandrov } 2810772c344dSNikolay Aleksandrov 2811772c344dSNikolay Aleksandrov out: 2812772c344dSNikolay Aleksandrov cb->args[1] = e; 2813772c344dSNikolay Aleksandrov cb->args[0] = t; 2814772c344dSNikolay Aleksandrov 2815772c344dSNikolay Aleksandrov return skb->len; 2816772c344dSNikolay Aleksandrov } 2817772c344dSNikolay Aleksandrov 28181da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 28197ef8f65dSNikolay Aleksandrov /* The /proc interfaces to multicast routing : 2820a8cb16ddSEric Dumazet * /proc/net/ip_mr_cache & /proc/net/ip_mr_vif 28211da177e4SLinus Torvalds */ 28221da177e4SLinus Torvalds 28231da177e4SLinus Torvalds static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos) 2824ba93ef74SStephen Hemminger __acquires(mrt_lock) 28251da177e4SLinus Torvalds { 28263feda6b4SYuval Mintz struct mr_vif_iter *iter = seq->private; 2827f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2828f0ad0860SPatrick McHardy struct mr_table *mrt; 2829f0ad0860SPatrick McHardy 2830f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 283151456b29SIan Morris if (!mrt) 2832f0ad0860SPatrick McHardy return ERR_PTR(-ENOENT); 2833f0ad0860SPatrick McHardy 2834f0ad0860SPatrick McHardy iter->mrt = mrt; 2835f6bb4514SBenjamin Thery 28361da177e4SLinus Torvalds read_lock(&mrt_lock); 28373feda6b4SYuval Mintz return mr_vif_seq_start(seq, pos); 28381da177e4SLinus Torvalds } 28391da177e4SLinus Torvalds 28401da177e4SLinus Torvalds static void ipmr_vif_seq_stop(struct seq_file *seq, void *v) 2841ba93ef74SStephen Hemminger __releases(mrt_lock) 28421da177e4SLinus Torvalds { 28431da177e4SLinus Torvalds read_unlock(&mrt_lock); 28441da177e4SLinus Torvalds } 28451da177e4SLinus Torvalds 28461da177e4SLinus Torvalds static int ipmr_vif_seq_show(struct seq_file *seq, void *v) 28471da177e4SLinus Torvalds { 28483feda6b4SYuval Mintz struct mr_vif_iter *iter = seq->private; 2849f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 2850f6bb4514SBenjamin Thery 28511da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 28521da177e4SLinus Torvalds seq_puts(seq, 28531da177e4SLinus Torvalds "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n"); 28541da177e4SLinus Torvalds } else { 28551da177e4SLinus Torvalds const struct vif_device *vif = v; 28566853f21fSYuval Mintz const char *name = vif->dev ? 28576853f21fSYuval Mintz vif->dev->name : "none"; 28581da177e4SLinus Torvalds 28591da177e4SLinus Torvalds seq_printf(seq, 286091e6dd82SJames Hogan "%2td %-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, 28713feda6b4SYuval Mintz .next = mr_vif_seq_next, 28721da177e4SLinus Torvalds .stop = ipmr_vif_seq_stop, 28731da177e4SLinus Torvalds .show = ipmr_vif_seq_show, 28741da177e4SLinus Torvalds }; 28751da177e4SLinus Torvalds 28761da177e4SLinus Torvalds static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos) 28771da177e4SLinus Torvalds { 2878f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2879f0ad0860SPatrick McHardy struct mr_table *mrt; 2880f6bb4514SBenjamin Thery 2881f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 288251456b29SIan Morris if (!mrt) 2883f0ad0860SPatrick McHardy return ERR_PTR(-ENOENT); 2884f0ad0860SPatrick McHardy 2885c8d61968SYuval Mintz return mr_mfc_seq_start(seq, pos, mrt, &mfc_unres_lock); 28861da177e4SLinus Torvalds } 28871da177e4SLinus Torvalds 28881da177e4SLinus Torvalds static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) 28891da177e4SLinus Torvalds { 28901da177e4SLinus Torvalds int n; 28911da177e4SLinus Torvalds 28921da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 28931da177e4SLinus Torvalds seq_puts(seq, 28941da177e4SLinus Torvalds "Group Origin Iif Pkts Bytes Wrong Oifs\n"); 28951da177e4SLinus Torvalds } else { 28961da177e4SLinus Torvalds const struct mfc_cache *mfc = v; 2897c8d61968SYuval Mintz const struct mr_mfc_iter *it = seq->private; 2898f0ad0860SPatrick McHardy const struct mr_table *mrt = it->mrt; 28991da177e4SLinus Torvalds 29000eae88f3SEric Dumazet seq_printf(seq, "%08X %08X %-3hd", 29010eae88f3SEric Dumazet (__force u32) mfc->mfc_mcastgrp, 29020eae88f3SEric Dumazet (__force u32) mfc->mfc_origin, 2903494fff56SYuval Mintz mfc->_c.mfc_parent); 29041ea472e2SBenjamin Thery 29050c12295aSPatrick McHardy if (it->cache != &mrt->mfc_unres_queue) { 29061ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 2907494fff56SYuval Mintz mfc->_c.mfc_un.res.pkt, 2908494fff56SYuval Mintz mfc->_c.mfc_un.res.bytes, 2909494fff56SYuval Mintz mfc->_c.mfc_un.res.wrong_if); 2910494fff56SYuval Mintz for (n = mfc->_c.mfc_un.res.minvif; 2911494fff56SYuval Mintz n < mfc->_c.mfc_un.res.maxvif; n++) { 29120c12295aSPatrick McHardy if (VIF_EXISTS(mrt, n) && 2913494fff56SYuval Mintz mfc->_c.mfc_un.res.ttls[n] < 255) 29141da177e4SLinus Torvalds seq_printf(seq, 29151da177e4SLinus Torvalds " %2d:%-3d", 2916494fff56SYuval Mintz n, mfc->_c.mfc_un.res.ttls[n]); 29171da177e4SLinus Torvalds } 29181ea472e2SBenjamin Thery } else { 29191ea472e2SBenjamin Thery /* unresolved mfc_caches don't contain 29201ea472e2SBenjamin Thery * pkt, bytes and wrong_if values 29211ea472e2SBenjamin Thery */ 29221ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul); 29231da177e4SLinus Torvalds } 29241da177e4SLinus Torvalds seq_putc(seq, '\n'); 29251da177e4SLinus Torvalds } 29261da177e4SLinus Torvalds return 0; 29271da177e4SLinus Torvalds } 29281da177e4SLinus Torvalds 2929f690808eSStephen Hemminger static const struct seq_operations ipmr_mfc_seq_ops = { 29301da177e4SLinus Torvalds .start = ipmr_mfc_seq_start, 2931c8d61968SYuval Mintz .next = mr_mfc_seq_next, 2932c8d61968SYuval Mintz .stop = mr_mfc_seq_stop, 29331da177e4SLinus Torvalds .show = ipmr_mfc_seq_show, 29341da177e4SLinus Torvalds }; 29351da177e4SLinus Torvalds #endif 29361da177e4SLinus Torvalds 29371da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V2 293832613090SAlexey Dobriyan static const struct net_protocol pim_protocol = { 29391da177e4SLinus Torvalds .handler = pim_rcv, 2940403dbb97STom Goff .netns_ok = 1, 29411da177e4SLinus Torvalds }; 29421da177e4SLinus Torvalds #endif 29431da177e4SLinus Torvalds 29444d65b948SYotam Gigi static unsigned int ipmr_seq_read(struct net *net) 29454d65b948SYotam Gigi { 29464d65b948SYotam Gigi ASSERT_RTNL(); 29474d65b948SYotam Gigi 29484d65b948SYotam Gigi return net->ipv4.ipmr_seq + ipmr_rules_seq_read(net); 29494d65b948SYotam Gigi } 29504d65b948SYotam Gigi 29514d65b948SYotam Gigi static int ipmr_dump(struct net *net, struct notifier_block *nb) 29524d65b948SYotam Gigi { 2953cdc9f944SYuval Mintz return mr_dump(net, nb, RTNL_FAMILY_IPMR, ipmr_rules_dump, 2954cdc9f944SYuval Mintz ipmr_mr_table_iter, &mrt_lock); 29554d65b948SYotam Gigi } 29564d65b948SYotam Gigi 29574d65b948SYotam Gigi static const struct fib_notifier_ops ipmr_notifier_ops_template = { 29584d65b948SYotam Gigi .family = RTNL_FAMILY_IPMR, 29594d65b948SYotam Gigi .fib_seq_read = ipmr_seq_read, 29604d65b948SYotam Gigi .fib_dump = ipmr_dump, 29614d65b948SYotam Gigi .owner = THIS_MODULE, 29624d65b948SYotam Gigi }; 29634d65b948SYotam Gigi 2964ef739d8aSColin Ian King static int __net_init ipmr_notifier_init(struct net *net) 29654d65b948SYotam Gigi { 29664d65b948SYotam Gigi struct fib_notifier_ops *ops; 29674d65b948SYotam Gigi 29684d65b948SYotam Gigi net->ipv4.ipmr_seq = 0; 29694d65b948SYotam Gigi 29704d65b948SYotam Gigi ops = fib_notifier_ops_register(&ipmr_notifier_ops_template, net); 29714d65b948SYotam Gigi if (IS_ERR(ops)) 29724d65b948SYotam Gigi return PTR_ERR(ops); 29734d65b948SYotam Gigi net->ipv4.ipmr_notifier_ops = ops; 29744d65b948SYotam Gigi 29754d65b948SYotam Gigi return 0; 29764d65b948SYotam Gigi } 29774d65b948SYotam Gigi 29784d65b948SYotam Gigi static void __net_exit ipmr_notifier_exit(struct net *net) 29794d65b948SYotam Gigi { 29804d65b948SYotam Gigi fib_notifier_ops_unregister(net->ipv4.ipmr_notifier_ops); 29814d65b948SYotam Gigi net->ipv4.ipmr_notifier_ops = NULL; 29824d65b948SYotam Gigi } 29834d65b948SYotam Gigi 29847ef8f65dSNikolay Aleksandrov /* Setup for IP multicast routing */ 2985cf958ae3SBenjamin Thery static int __net_init ipmr_net_init(struct net *net) 2986cf958ae3SBenjamin Thery { 2987f0ad0860SPatrick McHardy int err; 2988cf958ae3SBenjamin Thery 29894d65b948SYotam Gigi err = ipmr_notifier_init(net); 29904d65b948SYotam Gigi if (err) 29914d65b948SYotam Gigi goto ipmr_notifier_fail; 29924d65b948SYotam Gigi 2993f0ad0860SPatrick McHardy err = ipmr_rules_init(net); 2994f0ad0860SPatrick McHardy if (err < 0) 29954d65b948SYotam Gigi goto ipmr_rules_fail; 2996f6bb4514SBenjamin Thery 2997f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 2998f6bb4514SBenjamin Thery err = -ENOMEM; 2999c3506372SChristoph Hellwig if (!proc_create_net("ip_mr_vif", 0, net->proc_net, &ipmr_vif_seq_ops, 3000c3506372SChristoph Hellwig sizeof(struct mr_vif_iter))) 3001f6bb4514SBenjamin Thery goto proc_vif_fail; 3002c3506372SChristoph Hellwig if (!proc_create_net("ip_mr_cache", 0, net->proc_net, &ipmr_mfc_seq_ops, 3003c3506372SChristoph Hellwig sizeof(struct mr_mfc_iter))) 3004f6bb4514SBenjamin Thery goto proc_cache_fail; 3005f6bb4514SBenjamin Thery #endif 30062bb8b26cSBenjamin Thery return 0; 30072bb8b26cSBenjamin Thery 3008f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 3009f6bb4514SBenjamin Thery proc_cache_fail: 3010ece31ffdSGao feng remove_proc_entry("ip_mr_vif", net->proc_net); 3011f6bb4514SBenjamin Thery proc_vif_fail: 3012f0ad0860SPatrick McHardy ipmr_rules_exit(net); 3013f6bb4514SBenjamin Thery #endif 30144d65b948SYotam Gigi ipmr_rules_fail: 30154d65b948SYotam Gigi ipmr_notifier_exit(net); 30164d65b948SYotam Gigi ipmr_notifier_fail: 3017cf958ae3SBenjamin Thery return err; 3018cf958ae3SBenjamin Thery } 3019cf958ae3SBenjamin Thery 3020cf958ae3SBenjamin Thery static void __net_exit ipmr_net_exit(struct net *net) 3021cf958ae3SBenjamin Thery { 3022f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 3023ece31ffdSGao feng remove_proc_entry("ip_mr_cache", net->proc_net); 3024ece31ffdSGao feng remove_proc_entry("ip_mr_vif", net->proc_net); 3025f6bb4514SBenjamin Thery #endif 30264d65b948SYotam Gigi ipmr_notifier_exit(net); 3027f0ad0860SPatrick McHardy ipmr_rules_exit(net); 3028cf958ae3SBenjamin Thery } 3029cf958ae3SBenjamin Thery 3030cf958ae3SBenjamin Thery static struct pernet_operations ipmr_net_ops = { 3031cf958ae3SBenjamin Thery .init = ipmr_net_init, 3032cf958ae3SBenjamin Thery .exit = ipmr_net_exit, 3033cf958ae3SBenjamin Thery }; 30341da177e4SLinus Torvalds 303503d2f897SWang Chen int __init ip_mr_init(void) 30361da177e4SLinus Torvalds { 303703d2f897SWang Chen int err; 303803d2f897SWang Chen 30391da177e4SLinus Torvalds mrt_cachep = kmem_cache_create("ip_mrt_cache", 30401da177e4SLinus Torvalds sizeof(struct mfc_cache), 3041e5d679f3SAlexey Dobriyan 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, 304220c2df83SPaul Mundt NULL); 304303d2f897SWang Chen 3044cf958ae3SBenjamin Thery err = register_pernet_subsys(&ipmr_net_ops); 3045cf958ae3SBenjamin Thery if (err) 3046cf958ae3SBenjamin Thery goto reg_pernet_fail; 3047cf958ae3SBenjamin Thery 304803d2f897SWang Chen err = register_netdevice_notifier(&ip_mr_notifier); 304903d2f897SWang Chen if (err) 305003d2f897SWang Chen goto reg_notif_fail; 3051403dbb97STom Goff #ifdef CONFIG_IP_PIMSM_V2 3052403dbb97STom Goff if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) { 3053058bd4d2SJoe Perches pr_err("%s: can't add PIM protocol\n", __func__); 3054403dbb97STom Goff err = -EAGAIN; 3055403dbb97STom Goff goto add_proto_fail; 3056403dbb97STom Goff } 3057403dbb97STom Goff #endif 3058c7ac8679SGreg Rose rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, 3059b97bac64SFlorian Westphal ipmr_rtm_getroute, ipmr_rtm_dumproute, 0); 3060ccbb0aa6SNikolay Aleksandrov rtnl_register(RTNL_FAMILY_IPMR, RTM_NEWROUTE, 3061b97bac64SFlorian Westphal ipmr_rtm_route, NULL, 0); 3062ccbb0aa6SNikolay Aleksandrov rtnl_register(RTNL_FAMILY_IPMR, RTM_DELROUTE, 3063b97bac64SFlorian Westphal ipmr_rtm_route, NULL, 0); 3064772c344dSNikolay Aleksandrov 3065772c344dSNikolay Aleksandrov rtnl_register(RTNL_FAMILY_IPMR, RTM_GETLINK, 3066b97bac64SFlorian Westphal NULL, ipmr_rtm_dumplink, 0); 306703d2f897SWang Chen return 0; 3068f6bb4514SBenjamin Thery 3069403dbb97STom Goff #ifdef CONFIG_IP_PIMSM_V2 3070403dbb97STom Goff add_proto_fail: 3071403dbb97STom Goff unregister_netdevice_notifier(&ip_mr_notifier); 3072403dbb97STom Goff #endif 3073c3e38896SBenjamin Thery reg_notif_fail: 3074cf958ae3SBenjamin Thery unregister_pernet_subsys(&ipmr_net_ops); 3075cf958ae3SBenjamin Thery reg_pernet_fail: 3076c3e38896SBenjamin Thery kmem_cache_destroy(mrt_cachep); 307703d2f897SWang Chen return err; 30781da177e4SLinus Torvalds } 3079