11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * IP multicast routing support for mrouted 3.6/3.8 31da177e4SLinus Torvalds * 4113aa838SAlan Cox * (c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk> 51da177e4SLinus Torvalds * Linux Consultancy and Custom Driver Development 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 81da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 91da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 101da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * Fixes: 131da177e4SLinus Torvalds * Michael Chastain : Incorrect size of copying. 141da177e4SLinus Torvalds * Alan Cox : Added the cache manager code 151da177e4SLinus Torvalds * Alan Cox : Fixed the clone/copy bug and device race. 161da177e4SLinus Torvalds * Mike McLagan : Routing by source 171da177e4SLinus Torvalds * Malcolm Beattie : Buffer handling fixes. 181da177e4SLinus Torvalds * Alexey Kuznetsov : Double buffer free and other fixes. 191da177e4SLinus Torvalds * SVR Anand : Fixed several multicast bugs and problems. 201da177e4SLinus Torvalds * Alexey Kuznetsov : Status, optimisations and more. 211da177e4SLinus Torvalds * Brad Parker : Better behaviour on mrouted upcall 221da177e4SLinus Torvalds * overflow. 231da177e4SLinus Torvalds * Carlos Picoto : PIMv1 Support 241da177e4SLinus Torvalds * Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header 25f77f13e2SGilles Espinasse * Relax this requirement to work with older peers. 261da177e4SLinus Torvalds * 271da177e4SLinus Torvalds */ 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds #include <asm/uaccess.h> 301da177e4SLinus Torvalds #include <linux/types.h> 314fc268d2SRandy Dunlap #include <linux/capability.h> 321da177e4SLinus Torvalds #include <linux/errno.h> 331da177e4SLinus Torvalds #include <linux/timer.h> 341da177e4SLinus Torvalds #include <linux/mm.h> 351da177e4SLinus Torvalds #include <linux/kernel.h> 361da177e4SLinus Torvalds #include <linux/fcntl.h> 371da177e4SLinus Torvalds #include <linux/stat.h> 381da177e4SLinus Torvalds #include <linux/socket.h> 391da177e4SLinus Torvalds #include <linux/in.h> 401da177e4SLinus Torvalds #include <linux/inet.h> 411da177e4SLinus Torvalds #include <linux/netdevice.h> 421da177e4SLinus Torvalds #include <linux/inetdevice.h> 431da177e4SLinus Torvalds #include <linux/igmp.h> 441da177e4SLinus Torvalds #include <linux/proc_fs.h> 451da177e4SLinus Torvalds #include <linux/seq_file.h> 461da177e4SLinus Torvalds #include <linux/mroute.h> 471da177e4SLinus Torvalds #include <linux/init.h> 4846f25dffSKris Katterjohn #include <linux/if_ether.h> 495a0e3ad6STejun Heo #include <linux/slab.h> 50457c4cbcSEric W. Biederman #include <net/net_namespace.h> 511da177e4SLinus Torvalds #include <net/ip.h> 521da177e4SLinus Torvalds #include <net/protocol.h> 531da177e4SLinus Torvalds #include <linux/skbuff.h> 5414c85021SArnaldo Carvalho de Melo #include <net/route.h> 551da177e4SLinus Torvalds #include <net/sock.h> 561da177e4SLinus Torvalds #include <net/icmp.h> 571da177e4SLinus Torvalds #include <net/udp.h> 581da177e4SLinus Torvalds #include <net/raw.h> 591da177e4SLinus Torvalds #include <linux/notifier.h> 601da177e4SLinus Torvalds #include <linux/if_arp.h> 611da177e4SLinus Torvalds #include <linux/netfilter_ipv4.h> 62709b46e8SEric W. Biederman #include <linux/compat.h> 63bc3b2d7fSPaul Gortmaker #include <linux/export.h> 64c5441932SPravin B Shelar #include <net/ip_tunnels.h> 651da177e4SLinus Torvalds #include <net/checksum.h> 66dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 67f0ad0860SPatrick McHardy #include <net/fib_rules.h> 68d67b8c61SNicolas Dichtel #include <linux/netconf.h> 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) 711da177e4SLinus Torvalds #define CONFIG_IP_PIMSM 1 721da177e4SLinus Torvalds #endif 731da177e4SLinus Torvalds 740c12295aSPatrick McHardy struct mr_table { 75f0ad0860SPatrick McHardy struct list_head list; 760c5c9fb5SEric W. Biederman possible_net_t net; 77f0ad0860SPatrick McHardy u32 id; 784c968709SEric Dumazet struct sock __rcu *mroute_sk; 790c12295aSPatrick McHardy struct timer_list ipmr_expire_timer; 800c12295aSPatrick McHardy struct list_head mfc_unres_queue; 810c12295aSPatrick McHardy struct list_head mfc_cache_array[MFC_LINES]; 820c12295aSPatrick McHardy struct vif_device vif_table[MAXVIFS]; 830c12295aSPatrick McHardy int maxvif; 840c12295aSPatrick McHardy atomic_t cache_resolve_queue_len; 8553d6841dSJoe Perches bool mroute_do_assert; 8653d6841dSJoe Perches bool mroute_do_pim; 870c12295aSPatrick McHardy #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) 880c12295aSPatrick McHardy int mroute_reg_vif_num; 890c12295aSPatrick McHardy #endif 900c12295aSPatrick McHardy }; 910c12295aSPatrick McHardy 92f0ad0860SPatrick McHardy struct ipmr_rule { 93f0ad0860SPatrick McHardy struct fib_rule common; 94f0ad0860SPatrick McHardy }; 95f0ad0860SPatrick McHardy 96f0ad0860SPatrick McHardy struct ipmr_result { 97f0ad0860SPatrick McHardy struct mr_table *mrt; 98f0ad0860SPatrick McHardy }; 99f0ad0860SPatrick McHardy 1001da177e4SLinus Torvalds /* Big lock, protecting vif table, mrt cache and mroute socket state. 101a8cb16ddSEric Dumazet * Note that the changes are semaphored via rtnl_lock. 1021da177e4SLinus Torvalds */ 1031da177e4SLinus Torvalds 1041da177e4SLinus Torvalds static DEFINE_RWLOCK(mrt_lock); 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds /* 1071da177e4SLinus Torvalds * Multicast router control variables 1081da177e4SLinus Torvalds */ 1091da177e4SLinus Torvalds 1100c12295aSPatrick McHardy #define VIF_EXISTS(_mrt, _idx) ((_mrt)->vif_table[_idx].dev != NULL) 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds /* Special spinlock for queue of unresolved entries */ 1131da177e4SLinus Torvalds static DEFINE_SPINLOCK(mfc_unres_lock); 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds /* We return to original Alan's scheme. Hash table of resolved 116a8cb16ddSEric Dumazet * entries is changed only in process context and protected 117a8cb16ddSEric Dumazet * with weak lock mrt_lock. Queue of unresolved entries is protected 118a8cb16ddSEric Dumazet * with strong spinlock mfc_unres_lock. 119a8cb16ddSEric Dumazet * 120a8cb16ddSEric Dumazet * In this case data path is free of exclusive locks at all. 1211da177e4SLinus Torvalds */ 1221da177e4SLinus Torvalds 123e18b890bSChristoph Lameter static struct kmem_cache *mrt_cachep __read_mostly; 1241da177e4SLinus Torvalds 125f0ad0860SPatrick McHardy static struct mr_table *ipmr_new_table(struct net *net, u32 id); 126acbb219dSFrancesco Ruggeri static void ipmr_free_table(struct mr_table *mrt); 127acbb219dSFrancesco Ruggeri 128c4854ec8SRami Rosen static void ip_mr_forward(struct net *net, struct mr_table *mrt, 1290c12295aSPatrick McHardy struct sk_buff *skb, struct mfc_cache *cache, 1300c12295aSPatrick McHardy int local); 1310c12295aSPatrick McHardy static int ipmr_cache_report(struct mr_table *mrt, 1324feb88e5SBenjamin Thery struct sk_buff *pkt, vifi_t vifi, int assert); 133cb6a4e46SPatrick McHardy static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 134d658f8a0SPatrick McHardy struct mfc_cache *c, struct rtmsg *rtm); 1358cd3ac9fSNicolas Dichtel static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, 1368cd3ac9fSNicolas Dichtel int cmd); 137*0e615e96SNikolay Aleksandrov static void mroute_clean_tables(struct mr_table *mrt, bool all); 138f0ad0860SPatrick McHardy static void ipmr_expire_process(unsigned long arg); 1391da177e4SLinus Torvalds 140f0ad0860SPatrick McHardy #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 141f0ad0860SPatrick McHardy #define ipmr_for_each_table(mrt, net) \ 142f0ad0860SPatrick McHardy list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list) 143f0ad0860SPatrick McHardy 144f0ad0860SPatrick McHardy static struct mr_table *ipmr_get_table(struct net *net, u32 id) 145f0ad0860SPatrick McHardy { 146f0ad0860SPatrick McHardy struct mr_table *mrt; 147f0ad0860SPatrick McHardy 148f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 149f0ad0860SPatrick McHardy if (mrt->id == id) 150f0ad0860SPatrick McHardy return mrt; 151f0ad0860SPatrick McHardy } 152f0ad0860SPatrick McHardy return NULL; 153f0ad0860SPatrick McHardy } 154f0ad0860SPatrick McHardy 155da91981bSDavid S. Miller static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 156f0ad0860SPatrick McHardy struct mr_table **mrt) 157f0ad0860SPatrick McHardy { 158f0ad0860SPatrick McHardy int err; 15995f4a45dSHannes Frederic Sowa struct ipmr_result res; 16095f4a45dSHannes Frederic Sowa struct fib_lookup_arg arg = { 16195f4a45dSHannes Frederic Sowa .result = &res, 16295f4a45dSHannes Frederic Sowa .flags = FIB_LOOKUP_NOREF, 16395f4a45dSHannes Frederic Sowa }; 164f0ad0860SPatrick McHardy 165da91981bSDavid S. Miller err = fib_rules_lookup(net->ipv4.mr_rules_ops, 166da91981bSDavid S. Miller flowi4_to_flowi(flp4), 0, &arg); 167f0ad0860SPatrick McHardy if (err < 0) 168f0ad0860SPatrick McHardy return err; 169f0ad0860SPatrick McHardy *mrt = res.mrt; 170f0ad0860SPatrick McHardy return 0; 171f0ad0860SPatrick McHardy } 172f0ad0860SPatrick McHardy 173f0ad0860SPatrick McHardy static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp, 174f0ad0860SPatrick McHardy int flags, struct fib_lookup_arg *arg) 175f0ad0860SPatrick McHardy { 176f0ad0860SPatrick McHardy struct ipmr_result *res = arg->result; 177f0ad0860SPatrick McHardy struct mr_table *mrt; 178f0ad0860SPatrick McHardy 179f0ad0860SPatrick McHardy switch (rule->action) { 180f0ad0860SPatrick McHardy case FR_ACT_TO_TBL: 181f0ad0860SPatrick McHardy break; 182f0ad0860SPatrick McHardy case FR_ACT_UNREACHABLE: 183f0ad0860SPatrick McHardy return -ENETUNREACH; 184f0ad0860SPatrick McHardy case FR_ACT_PROHIBIT: 185f0ad0860SPatrick McHardy return -EACCES; 186f0ad0860SPatrick McHardy case FR_ACT_BLACKHOLE: 187f0ad0860SPatrick McHardy default: 188f0ad0860SPatrick McHardy return -EINVAL; 189f0ad0860SPatrick McHardy } 190f0ad0860SPatrick McHardy 191f0ad0860SPatrick McHardy mrt = ipmr_get_table(rule->fr_net, rule->table); 19251456b29SIan Morris if (!mrt) 193f0ad0860SPatrick McHardy return -EAGAIN; 194f0ad0860SPatrick McHardy res->mrt = mrt; 195f0ad0860SPatrick McHardy return 0; 196f0ad0860SPatrick McHardy } 197f0ad0860SPatrick McHardy 198f0ad0860SPatrick McHardy static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) 199f0ad0860SPatrick McHardy { 200f0ad0860SPatrick McHardy return 1; 201f0ad0860SPatrick McHardy } 202f0ad0860SPatrick McHardy 203f0ad0860SPatrick McHardy static const struct nla_policy ipmr_rule_policy[FRA_MAX + 1] = { 204f0ad0860SPatrick McHardy FRA_GENERIC_POLICY, 205f0ad0860SPatrick McHardy }; 206f0ad0860SPatrick McHardy 207f0ad0860SPatrick McHardy static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb, 208f0ad0860SPatrick McHardy struct fib_rule_hdr *frh, struct nlattr **tb) 209f0ad0860SPatrick McHardy { 210f0ad0860SPatrick McHardy return 0; 211f0ad0860SPatrick McHardy } 212f0ad0860SPatrick McHardy 213f0ad0860SPatrick McHardy static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 214f0ad0860SPatrick McHardy struct nlattr **tb) 215f0ad0860SPatrick McHardy { 216f0ad0860SPatrick McHardy return 1; 217f0ad0860SPatrick McHardy } 218f0ad0860SPatrick McHardy 219f0ad0860SPatrick McHardy static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb, 220f0ad0860SPatrick McHardy struct fib_rule_hdr *frh) 221f0ad0860SPatrick McHardy { 222f0ad0860SPatrick McHardy frh->dst_len = 0; 223f0ad0860SPatrick McHardy frh->src_len = 0; 224f0ad0860SPatrick McHardy frh->tos = 0; 225f0ad0860SPatrick McHardy return 0; 226f0ad0860SPatrick McHardy } 227f0ad0860SPatrick McHardy 22804a6f82cSAndi Kleen static const struct fib_rules_ops __net_initconst ipmr_rules_ops_template = { 22925239ceeSPatrick McHardy .family = RTNL_FAMILY_IPMR, 230f0ad0860SPatrick McHardy .rule_size = sizeof(struct ipmr_rule), 231f0ad0860SPatrick McHardy .addr_size = sizeof(u32), 232f0ad0860SPatrick McHardy .action = ipmr_rule_action, 233f0ad0860SPatrick McHardy .match = ipmr_rule_match, 234f0ad0860SPatrick McHardy .configure = ipmr_rule_configure, 235f0ad0860SPatrick McHardy .compare = ipmr_rule_compare, 236f0ad0860SPatrick McHardy .fill = ipmr_rule_fill, 237f0ad0860SPatrick McHardy .nlgroup = RTNLGRP_IPV4_RULE, 238f0ad0860SPatrick McHardy .policy = ipmr_rule_policy, 239f0ad0860SPatrick McHardy .owner = THIS_MODULE, 240f0ad0860SPatrick McHardy }; 241f0ad0860SPatrick McHardy 242f0ad0860SPatrick McHardy static int __net_init ipmr_rules_init(struct net *net) 243f0ad0860SPatrick McHardy { 244f0ad0860SPatrick McHardy struct fib_rules_ops *ops; 245f0ad0860SPatrick McHardy struct mr_table *mrt; 246f0ad0860SPatrick McHardy int err; 247f0ad0860SPatrick McHardy 248f0ad0860SPatrick McHardy ops = fib_rules_register(&ipmr_rules_ops_template, net); 249f0ad0860SPatrick McHardy if (IS_ERR(ops)) 250f0ad0860SPatrick McHardy return PTR_ERR(ops); 251f0ad0860SPatrick McHardy 252f0ad0860SPatrick McHardy INIT_LIST_HEAD(&net->ipv4.mr_tables); 253f0ad0860SPatrick McHardy 254f0ad0860SPatrick McHardy mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 25551456b29SIan Morris if (!mrt) { 256f0ad0860SPatrick McHardy err = -ENOMEM; 257f0ad0860SPatrick McHardy goto err1; 258f0ad0860SPatrick McHardy } 259f0ad0860SPatrick McHardy 260f0ad0860SPatrick McHardy err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0); 261f0ad0860SPatrick McHardy if (err < 0) 262f0ad0860SPatrick McHardy goto err2; 263f0ad0860SPatrick McHardy 264f0ad0860SPatrick McHardy net->ipv4.mr_rules_ops = ops; 265f0ad0860SPatrick McHardy return 0; 266f0ad0860SPatrick McHardy 267f0ad0860SPatrick McHardy err2: 268f243e5a7SWANG Cong ipmr_free_table(mrt); 269f0ad0860SPatrick McHardy err1: 270f0ad0860SPatrick McHardy fib_rules_unregister(ops); 271f0ad0860SPatrick McHardy return err; 272f0ad0860SPatrick McHardy } 273f0ad0860SPatrick McHardy 274f0ad0860SPatrick McHardy static void __net_exit ipmr_rules_exit(struct net *net) 275f0ad0860SPatrick McHardy { 276f0ad0860SPatrick McHardy struct mr_table *mrt, *next; 277f0ad0860SPatrick McHardy 278ed785309SWANG Cong rtnl_lock(); 279035320d5SEric Dumazet list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) { 280035320d5SEric Dumazet list_del(&mrt->list); 281acbb219dSFrancesco Ruggeri ipmr_free_table(mrt); 282035320d5SEric Dumazet } 283f0ad0860SPatrick McHardy fib_rules_unregister(net->ipv4.mr_rules_ops); 284419df12fSWANG Cong rtnl_unlock(); 285f0ad0860SPatrick McHardy } 286f0ad0860SPatrick McHardy #else 287f0ad0860SPatrick McHardy #define ipmr_for_each_table(mrt, net) \ 288f0ad0860SPatrick McHardy for (mrt = net->ipv4.mrt; mrt; mrt = NULL) 289f0ad0860SPatrick McHardy 290f0ad0860SPatrick McHardy static struct mr_table *ipmr_get_table(struct net *net, u32 id) 291f0ad0860SPatrick McHardy { 292f0ad0860SPatrick McHardy return net->ipv4.mrt; 293f0ad0860SPatrick McHardy } 294f0ad0860SPatrick McHardy 295da91981bSDavid S. Miller static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 296f0ad0860SPatrick McHardy struct mr_table **mrt) 297f0ad0860SPatrick McHardy { 298f0ad0860SPatrick McHardy *mrt = net->ipv4.mrt; 299f0ad0860SPatrick McHardy return 0; 300f0ad0860SPatrick McHardy } 301f0ad0860SPatrick McHardy 302f0ad0860SPatrick McHardy static int __net_init ipmr_rules_init(struct net *net) 303f0ad0860SPatrick McHardy { 304f0ad0860SPatrick McHardy net->ipv4.mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 305f0ad0860SPatrick McHardy return net->ipv4.mrt ? 0 : -ENOMEM; 306f0ad0860SPatrick McHardy } 307f0ad0860SPatrick McHardy 308f0ad0860SPatrick McHardy static void __net_exit ipmr_rules_exit(struct net *net) 309f0ad0860SPatrick McHardy { 310ed785309SWANG Cong rtnl_lock(); 311acbb219dSFrancesco Ruggeri ipmr_free_table(net->ipv4.mrt); 312ed785309SWANG Cong net->ipv4.mrt = NULL; 313ed785309SWANG Cong rtnl_unlock(); 314f0ad0860SPatrick McHardy } 315f0ad0860SPatrick McHardy #endif 316f0ad0860SPatrick McHardy 317f0ad0860SPatrick McHardy static struct mr_table *ipmr_new_table(struct net *net, u32 id) 318f0ad0860SPatrick McHardy { 319f0ad0860SPatrick McHardy struct mr_table *mrt; 320f0ad0860SPatrick McHardy unsigned int i; 321f0ad0860SPatrick McHardy 322f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, id); 32300db4124SIan Morris if (mrt) 324f0ad0860SPatrick McHardy return mrt; 325f0ad0860SPatrick McHardy 326f0ad0860SPatrick McHardy mrt = kzalloc(sizeof(*mrt), GFP_KERNEL); 32751456b29SIan Morris if (!mrt) 328f0ad0860SPatrick McHardy return NULL; 3298de53dfbSPatrick McHardy write_pnet(&mrt->net, net); 330f0ad0860SPatrick McHardy mrt->id = id; 331f0ad0860SPatrick McHardy 332f0ad0860SPatrick McHardy /* Forwarding cache */ 333f0ad0860SPatrick McHardy for (i = 0; i < MFC_LINES; i++) 334f0ad0860SPatrick McHardy INIT_LIST_HEAD(&mrt->mfc_cache_array[i]); 335f0ad0860SPatrick McHardy 336f0ad0860SPatrick McHardy INIT_LIST_HEAD(&mrt->mfc_unres_queue); 337f0ad0860SPatrick McHardy 338f0ad0860SPatrick McHardy setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process, 339f0ad0860SPatrick McHardy (unsigned long)mrt); 340f0ad0860SPatrick McHardy 341f0ad0860SPatrick McHardy #ifdef CONFIG_IP_PIMSM 342f0ad0860SPatrick McHardy mrt->mroute_reg_vif_num = -1; 343f0ad0860SPatrick McHardy #endif 344f0ad0860SPatrick McHardy #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 345f0ad0860SPatrick McHardy list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables); 346f0ad0860SPatrick McHardy #endif 347f0ad0860SPatrick McHardy return mrt; 348f0ad0860SPatrick McHardy } 3491da177e4SLinus Torvalds 350acbb219dSFrancesco Ruggeri static void ipmr_free_table(struct mr_table *mrt) 351acbb219dSFrancesco Ruggeri { 352acbb219dSFrancesco Ruggeri del_timer_sync(&mrt->ipmr_expire_timer); 353*0e615e96SNikolay Aleksandrov mroute_clean_tables(mrt, true); 354acbb219dSFrancesco Ruggeri kfree(mrt); 355acbb219dSFrancesco Ruggeri } 356acbb219dSFrancesco Ruggeri 3571da177e4SLinus Torvalds /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */ 3581da177e4SLinus Torvalds 359d607032dSWang Chen static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v) 360d607032dSWang Chen { 3614feb88e5SBenjamin Thery struct net *net = dev_net(dev); 3624feb88e5SBenjamin Thery 363d607032dSWang Chen dev_close(dev); 364d607032dSWang Chen 3654feb88e5SBenjamin Thery dev = __dev_get_by_name(net, "tunl0"); 366d607032dSWang Chen if (dev) { 3675bc3eb7eSStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 368d607032dSWang Chen struct ifreq ifr; 369d607032dSWang Chen struct ip_tunnel_parm p; 370d607032dSWang Chen 371d607032dSWang Chen memset(&p, 0, sizeof(p)); 372d607032dSWang Chen p.iph.daddr = v->vifc_rmt_addr.s_addr; 373d607032dSWang Chen p.iph.saddr = v->vifc_lcl_addr.s_addr; 374d607032dSWang Chen p.iph.version = 4; 375d607032dSWang Chen p.iph.ihl = 5; 376d607032dSWang Chen p.iph.protocol = IPPROTO_IPIP; 377d607032dSWang Chen sprintf(p.name, "dvmrp%d", v->vifc_vifi); 378d607032dSWang Chen ifr.ifr_ifru.ifru_data = (__force void __user *)&p; 379d607032dSWang Chen 3805bc3eb7eSStephen Hemminger if (ops->ndo_do_ioctl) { 3815bc3eb7eSStephen Hemminger mm_segment_t oldfs = get_fs(); 3825bc3eb7eSStephen Hemminger 3835bc3eb7eSStephen Hemminger set_fs(KERNEL_DS); 3845bc3eb7eSStephen Hemminger ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL); 385d607032dSWang Chen set_fs(oldfs); 386d607032dSWang Chen } 387d607032dSWang Chen } 3885bc3eb7eSStephen Hemminger } 389d607032dSWang Chen 3901da177e4SLinus Torvalds static 3914feb88e5SBenjamin Thery struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v) 3921da177e4SLinus Torvalds { 3931da177e4SLinus Torvalds struct net_device *dev; 3941da177e4SLinus Torvalds 3954feb88e5SBenjamin Thery dev = __dev_get_by_name(net, "tunl0"); 3961da177e4SLinus Torvalds 3971da177e4SLinus Torvalds if (dev) { 3985bc3eb7eSStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 3991da177e4SLinus Torvalds int err; 4001da177e4SLinus Torvalds struct ifreq ifr; 4011da177e4SLinus Torvalds struct ip_tunnel_parm p; 4021da177e4SLinus Torvalds struct in_device *in_dev; 4031da177e4SLinus Torvalds 4041da177e4SLinus Torvalds memset(&p, 0, sizeof(p)); 4051da177e4SLinus Torvalds p.iph.daddr = v->vifc_rmt_addr.s_addr; 4061da177e4SLinus Torvalds p.iph.saddr = v->vifc_lcl_addr.s_addr; 4071da177e4SLinus Torvalds p.iph.version = 4; 4081da177e4SLinus Torvalds p.iph.ihl = 5; 4091da177e4SLinus Torvalds p.iph.protocol = IPPROTO_IPIP; 4101da177e4SLinus Torvalds sprintf(p.name, "dvmrp%d", v->vifc_vifi); 411ba93ef74SStephen Hemminger ifr.ifr_ifru.ifru_data = (__force void __user *)&p; 4121da177e4SLinus Torvalds 4135bc3eb7eSStephen Hemminger if (ops->ndo_do_ioctl) { 4145bc3eb7eSStephen Hemminger mm_segment_t oldfs = get_fs(); 4155bc3eb7eSStephen Hemminger 4165bc3eb7eSStephen Hemminger set_fs(KERNEL_DS); 4175bc3eb7eSStephen Hemminger err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL); 4181da177e4SLinus Torvalds set_fs(oldfs); 419a8cb16ddSEric Dumazet } else { 4205bc3eb7eSStephen Hemminger err = -EOPNOTSUPP; 421a8cb16ddSEric Dumazet } 4221da177e4SLinus Torvalds dev = NULL; 4231da177e4SLinus Torvalds 4244feb88e5SBenjamin Thery if (err == 0 && 4254feb88e5SBenjamin Thery (dev = __dev_get_by_name(net, p.name)) != NULL) { 4261da177e4SLinus Torvalds dev->flags |= IFF_MULTICAST; 4271da177e4SLinus Torvalds 428e5ed6399SHerbert Xu in_dev = __in_dev_get_rtnl(dev); 42951456b29SIan Morris if (!in_dev) 4301da177e4SLinus Torvalds goto failure; 43171e27da9SHerbert Xu 43271e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 4331d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 43471e27da9SHerbert Xu IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; 4351da177e4SLinus Torvalds 4361da177e4SLinus Torvalds if (dev_open(dev)) 4371da177e4SLinus Torvalds goto failure; 4387dc00c82SWang Chen dev_hold(dev); 4391da177e4SLinus Torvalds } 4401da177e4SLinus Torvalds } 4411da177e4SLinus Torvalds return dev; 4421da177e4SLinus Torvalds 4431da177e4SLinus Torvalds failure: 4441da177e4SLinus Torvalds /* allow the register to be completed before unregistering. */ 4451da177e4SLinus Torvalds rtnl_unlock(); 4461da177e4SLinus Torvalds rtnl_lock(); 4471da177e4SLinus Torvalds 4481da177e4SLinus Torvalds unregister_netdevice(dev); 4491da177e4SLinus Torvalds return NULL; 4501da177e4SLinus Torvalds } 4511da177e4SLinus Torvalds 4521da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 4531da177e4SLinus Torvalds 4546fef4c0cSStephen Hemminger static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) 4551da177e4SLinus Torvalds { 4564feb88e5SBenjamin Thery struct net *net = dev_net(dev); 457f0ad0860SPatrick McHardy struct mr_table *mrt; 458da91981bSDavid S. Miller struct flowi4 fl4 = { 459da91981bSDavid S. Miller .flowi4_oif = dev->ifindex, 4606a662719SCong Wang .flowi4_iif = skb->skb_iif ? : LOOPBACK_IFINDEX, 461da91981bSDavid S. Miller .flowi4_mark = skb->mark, 462f0ad0860SPatrick McHardy }; 463f0ad0860SPatrick McHardy int err; 464f0ad0860SPatrick McHardy 465da91981bSDavid S. Miller err = ipmr_fib_lookup(net, &fl4, &mrt); 466e40dbc51SBen Greear if (err < 0) { 467e40dbc51SBen Greear kfree_skb(skb); 468f0ad0860SPatrick McHardy return err; 469e40dbc51SBen Greear } 4704feb88e5SBenjamin Thery 4711da177e4SLinus Torvalds read_lock(&mrt_lock); 472cf3677aeSPavel Emelyanov dev->stats.tx_bytes += skb->len; 473cf3677aeSPavel Emelyanov dev->stats.tx_packets++; 4740c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, IGMPMSG_WHOLEPKT); 4751da177e4SLinus Torvalds read_unlock(&mrt_lock); 4761da177e4SLinus Torvalds kfree_skb(skb); 4776ed10654SPatrick McHardy return NETDEV_TX_OK; 4781da177e4SLinus Torvalds } 4791da177e4SLinus Torvalds 480ee9b9596SNicolas Dichtel static int reg_vif_get_iflink(const struct net_device *dev) 481ee9b9596SNicolas Dichtel { 482ee9b9596SNicolas Dichtel return 0; 483ee9b9596SNicolas Dichtel } 484ee9b9596SNicolas Dichtel 485007c3838SStephen Hemminger static const struct net_device_ops reg_vif_netdev_ops = { 486007c3838SStephen Hemminger .ndo_start_xmit = reg_vif_xmit, 487ee9b9596SNicolas Dichtel .ndo_get_iflink = reg_vif_get_iflink, 488007c3838SStephen Hemminger }; 489007c3838SStephen Hemminger 4901da177e4SLinus Torvalds static void reg_vif_setup(struct net_device *dev) 4911da177e4SLinus Torvalds { 4921da177e4SLinus Torvalds dev->type = ARPHRD_PIMREG; 49346f25dffSKris Katterjohn dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8; 4941da177e4SLinus Torvalds dev->flags = IFF_NOARP; 49570cb4a45SHimangi Saraogi dev->netdev_ops = ®_vif_netdev_ops; 4961da177e4SLinus Torvalds dev->destructor = free_netdev; 497403dbb97STom Goff dev->features |= NETIF_F_NETNS_LOCAL; 4981da177e4SLinus Torvalds } 4991da177e4SLinus Torvalds 500f0ad0860SPatrick McHardy static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) 5011da177e4SLinus Torvalds { 5021da177e4SLinus Torvalds struct net_device *dev; 5031da177e4SLinus Torvalds struct in_device *in_dev; 504f0ad0860SPatrick McHardy char name[IFNAMSIZ]; 5051da177e4SLinus Torvalds 506f0ad0860SPatrick McHardy if (mrt->id == RT_TABLE_DEFAULT) 507f0ad0860SPatrick McHardy sprintf(name, "pimreg"); 508f0ad0860SPatrick McHardy else 509f0ad0860SPatrick McHardy sprintf(name, "pimreg%u", mrt->id); 510f0ad0860SPatrick McHardy 511c835a677STom Gundersen dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup); 5121da177e4SLinus Torvalds 51351456b29SIan Morris if (!dev) 5141da177e4SLinus Torvalds return NULL; 5151da177e4SLinus Torvalds 516403dbb97STom Goff dev_net_set(dev, net); 517403dbb97STom Goff 5181da177e4SLinus Torvalds if (register_netdevice(dev)) { 5191da177e4SLinus Torvalds free_netdev(dev); 5201da177e4SLinus Torvalds return NULL; 5211da177e4SLinus Torvalds } 5221da177e4SLinus Torvalds 52371e27da9SHerbert Xu rcu_read_lock(); 524a8cb16ddSEric Dumazet in_dev = __in_dev_get_rcu(dev); 525a8cb16ddSEric Dumazet if (!in_dev) { 52671e27da9SHerbert Xu rcu_read_unlock(); 5271da177e4SLinus Torvalds goto failure; 52871e27da9SHerbert Xu } 5291da177e4SLinus Torvalds 53071e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 5311d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 53271e27da9SHerbert Xu IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; 53371e27da9SHerbert Xu rcu_read_unlock(); 5341da177e4SLinus Torvalds 5351da177e4SLinus Torvalds if (dev_open(dev)) 5361da177e4SLinus Torvalds goto failure; 5371da177e4SLinus Torvalds 5387dc00c82SWang Chen dev_hold(dev); 5397dc00c82SWang Chen 5401da177e4SLinus Torvalds return dev; 5411da177e4SLinus Torvalds 5421da177e4SLinus Torvalds failure: 5431da177e4SLinus Torvalds /* allow the register to be completed before unregistering. */ 5441da177e4SLinus Torvalds rtnl_unlock(); 5451da177e4SLinus Torvalds rtnl_lock(); 5461da177e4SLinus Torvalds 5471da177e4SLinus Torvalds unregister_netdevice(dev); 5481da177e4SLinus Torvalds return NULL; 5491da177e4SLinus Torvalds } 5501da177e4SLinus Torvalds #endif 5511da177e4SLinus Torvalds 5522c53040fSBen Hutchings /** 5532c53040fSBen Hutchings * vif_delete - Delete a VIF entry 5547dc00c82SWang Chen * @notify: Set to 1, if the caller is a notifier_call 5551da177e4SLinus Torvalds */ 5561da177e4SLinus Torvalds 5570c12295aSPatrick McHardy static int vif_delete(struct mr_table *mrt, int vifi, int notify, 558d17fa6faSEric Dumazet struct list_head *head) 5591da177e4SLinus Torvalds { 5601da177e4SLinus Torvalds struct vif_device *v; 5611da177e4SLinus Torvalds struct net_device *dev; 5621da177e4SLinus Torvalds struct in_device *in_dev; 5631da177e4SLinus Torvalds 5640c12295aSPatrick McHardy if (vifi < 0 || vifi >= mrt->maxvif) 5651da177e4SLinus Torvalds return -EADDRNOTAVAIL; 5661da177e4SLinus Torvalds 5670c12295aSPatrick McHardy v = &mrt->vif_table[vifi]; 5681da177e4SLinus Torvalds 5691da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 5701da177e4SLinus Torvalds dev = v->dev; 5711da177e4SLinus Torvalds v->dev = NULL; 5721da177e4SLinus Torvalds 5731da177e4SLinus Torvalds if (!dev) { 5741da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 5751da177e4SLinus Torvalds return -EADDRNOTAVAIL; 5761da177e4SLinus Torvalds } 5771da177e4SLinus Torvalds 5781da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 5790c12295aSPatrick McHardy if (vifi == mrt->mroute_reg_vif_num) 5800c12295aSPatrick McHardy mrt->mroute_reg_vif_num = -1; 5811da177e4SLinus Torvalds #endif 5821da177e4SLinus Torvalds 5830c12295aSPatrick McHardy if (vifi + 1 == mrt->maxvif) { 5841da177e4SLinus Torvalds int tmp; 585a8cb16ddSEric Dumazet 5861da177e4SLinus Torvalds for (tmp = vifi - 1; tmp >= 0; tmp--) { 5870c12295aSPatrick McHardy if (VIF_EXISTS(mrt, tmp)) 5881da177e4SLinus Torvalds break; 5891da177e4SLinus Torvalds } 5900c12295aSPatrick McHardy mrt->maxvif = tmp+1; 5911da177e4SLinus Torvalds } 5921da177e4SLinus Torvalds 5931da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 5941da177e4SLinus Torvalds 5951da177e4SLinus Torvalds dev_set_allmulti(dev, -1); 5961da177e4SLinus Torvalds 597a8cb16ddSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 598a8cb16ddSEric Dumazet if (in_dev) { 59942f811b8SHerbert Xu IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--; 600d67b8c61SNicolas Dichtel inet_netconf_notify_devconf(dev_net(dev), 601d67b8c61SNicolas Dichtel NETCONFA_MC_FORWARDING, 602d67b8c61SNicolas Dichtel dev->ifindex, &in_dev->cnf); 6031da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 6041da177e4SLinus Torvalds } 6051da177e4SLinus Torvalds 6067dc00c82SWang Chen if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER) && !notify) 607d17fa6faSEric Dumazet unregister_netdevice_queue(dev, head); 6081da177e4SLinus Torvalds 6091da177e4SLinus Torvalds dev_put(dev); 6101da177e4SLinus Torvalds return 0; 6111da177e4SLinus Torvalds } 6121da177e4SLinus Torvalds 613a8c9486bSEric Dumazet static void ipmr_cache_free_rcu(struct rcu_head *head) 614a8c9486bSEric Dumazet { 615a8c9486bSEric Dumazet struct mfc_cache *c = container_of(head, struct mfc_cache, rcu); 616a8c9486bSEric Dumazet 617a8c9486bSEric Dumazet kmem_cache_free(mrt_cachep, c); 618a8c9486bSEric Dumazet } 619a8c9486bSEric Dumazet 6205c0a66f5SBenjamin Thery static inline void ipmr_cache_free(struct mfc_cache *c) 6215c0a66f5SBenjamin Thery { 622a8c9486bSEric Dumazet call_rcu(&c->rcu, ipmr_cache_free_rcu); 6235c0a66f5SBenjamin Thery } 6245c0a66f5SBenjamin Thery 6251da177e4SLinus Torvalds /* Destroy an unresolved cache entry, killing queued skbs 626a8cb16ddSEric Dumazet * and reporting error to netlink readers. 6271da177e4SLinus Torvalds */ 6281da177e4SLinus Torvalds 6290c12295aSPatrick McHardy static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c) 6301da177e4SLinus Torvalds { 6318de53dfbSPatrick McHardy struct net *net = read_pnet(&mrt->net); 6321da177e4SLinus Torvalds struct sk_buff *skb; 6339ef1d4c7SPatrick McHardy struct nlmsgerr *e; 6341da177e4SLinus Torvalds 6350c12295aSPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len); 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) { 638eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->version == 0) { 6391da177e4SLinus Torvalds struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr)); 6401da177e4SLinus Torvalds nlh->nlmsg_type = NLMSG_ERROR; 641573ce260SHong zhi guo nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 6421da177e4SLinus Torvalds skb_trim(skb, nlh->nlmsg_len); 643573ce260SHong zhi guo e = nlmsg_data(nlh); 6449ef1d4c7SPatrick McHardy e->error = -ETIMEDOUT; 6459ef1d4c7SPatrick McHardy memset(&e->msg, 0, sizeof(e->msg)); 6462942e900SThomas Graf 64715e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 648a8cb16ddSEric Dumazet } else { 6491da177e4SLinus Torvalds kfree_skb(skb); 6501da177e4SLinus Torvalds } 651a8cb16ddSEric Dumazet } 6521da177e4SLinus Torvalds 6535c0a66f5SBenjamin Thery ipmr_cache_free(c); 6541da177e4SLinus Torvalds } 6551da177e4SLinus Torvalds 6561da177e4SLinus Torvalds 657e258beb2SPatrick McHardy /* Timer process for the unresolved queue. */ 6581da177e4SLinus Torvalds 659e258beb2SPatrick McHardy static void ipmr_expire_process(unsigned long arg) 6601da177e4SLinus Torvalds { 6610c12295aSPatrick McHardy struct mr_table *mrt = (struct mr_table *)arg; 6621da177e4SLinus Torvalds unsigned long now; 6631da177e4SLinus Torvalds unsigned long expires; 664862465f2SPatrick McHardy struct mfc_cache *c, *next; 6651da177e4SLinus Torvalds 6661da177e4SLinus Torvalds if (!spin_trylock(&mfc_unres_lock)) { 6670c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10); 6681da177e4SLinus Torvalds return; 6691da177e4SLinus Torvalds } 6701da177e4SLinus Torvalds 6710c12295aSPatrick McHardy if (list_empty(&mrt->mfc_unres_queue)) 6721da177e4SLinus Torvalds goto out; 6731da177e4SLinus Torvalds 6741da177e4SLinus Torvalds now = jiffies; 6751da177e4SLinus Torvalds expires = 10*HZ; 6761da177e4SLinus Torvalds 6770c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) { 6781da177e4SLinus Torvalds if (time_after(c->mfc_un.unres.expires, now)) { 6791da177e4SLinus Torvalds unsigned long interval = c->mfc_un.unres.expires - now; 6801da177e4SLinus Torvalds if (interval < expires) 6811da177e4SLinus Torvalds expires = interval; 6821da177e4SLinus Torvalds continue; 6831da177e4SLinus Torvalds } 6841da177e4SLinus Torvalds 685862465f2SPatrick McHardy list_del(&c->list); 6868cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 6870c12295aSPatrick McHardy ipmr_destroy_unres(mrt, c); 6881da177e4SLinus Torvalds } 6891da177e4SLinus Torvalds 6900c12295aSPatrick McHardy if (!list_empty(&mrt->mfc_unres_queue)) 6910c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies + expires); 6921da177e4SLinus Torvalds 6931da177e4SLinus Torvalds out: 6941da177e4SLinus Torvalds spin_unlock(&mfc_unres_lock); 6951da177e4SLinus Torvalds } 6961da177e4SLinus Torvalds 6971da177e4SLinus Torvalds /* Fill oifs list. It is called under write locked mrt_lock. */ 6981da177e4SLinus Torvalds 6990c12295aSPatrick McHardy static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache, 700d658f8a0SPatrick McHardy unsigned char *ttls) 7011da177e4SLinus Torvalds { 7021da177e4SLinus Torvalds int vifi; 7031da177e4SLinus Torvalds 7041da177e4SLinus Torvalds cache->mfc_un.res.minvif = MAXVIFS; 7051da177e4SLinus Torvalds cache->mfc_un.res.maxvif = 0; 7061da177e4SLinus Torvalds memset(cache->mfc_un.res.ttls, 255, MAXVIFS); 7071da177e4SLinus Torvalds 7080c12295aSPatrick McHardy for (vifi = 0; vifi < mrt->maxvif; vifi++) { 7090c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vifi) && 710cf958ae3SBenjamin Thery ttls[vifi] && ttls[vifi] < 255) { 7111da177e4SLinus Torvalds cache->mfc_un.res.ttls[vifi] = ttls[vifi]; 7121da177e4SLinus Torvalds if (cache->mfc_un.res.minvif > vifi) 7131da177e4SLinus Torvalds cache->mfc_un.res.minvif = vifi; 7141da177e4SLinus Torvalds if (cache->mfc_un.res.maxvif <= vifi) 7151da177e4SLinus Torvalds cache->mfc_un.res.maxvif = vifi + 1; 7161da177e4SLinus Torvalds } 7171da177e4SLinus Torvalds } 7181da177e4SLinus Torvalds } 7191da177e4SLinus Torvalds 7200c12295aSPatrick McHardy static int vif_add(struct net *net, struct mr_table *mrt, 7210c12295aSPatrick McHardy struct vifctl *vifc, int mrtsock) 7221da177e4SLinus Torvalds { 7231da177e4SLinus Torvalds int vifi = vifc->vifc_vifi; 7240c12295aSPatrick McHardy struct vif_device *v = &mrt->vif_table[vifi]; 7251da177e4SLinus Torvalds struct net_device *dev; 7261da177e4SLinus Torvalds struct in_device *in_dev; 727d607032dSWang Chen int err; 7281da177e4SLinus Torvalds 7291da177e4SLinus Torvalds /* Is vif busy ? */ 7300c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vifi)) 7311da177e4SLinus Torvalds return -EADDRINUSE; 7321da177e4SLinus Torvalds 7331da177e4SLinus Torvalds switch (vifc->vifc_flags) { 7341da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 7351da177e4SLinus Torvalds case VIFF_REGISTER: 7361da177e4SLinus Torvalds /* 7371da177e4SLinus Torvalds * Special Purpose VIF in PIM 7381da177e4SLinus Torvalds * All the packets will be sent to the daemon 7391da177e4SLinus Torvalds */ 7400c12295aSPatrick McHardy if (mrt->mroute_reg_vif_num >= 0) 7411da177e4SLinus Torvalds return -EADDRINUSE; 742f0ad0860SPatrick McHardy dev = ipmr_reg_vif(net, mrt); 7431da177e4SLinus Torvalds if (!dev) 7441da177e4SLinus Torvalds return -ENOBUFS; 745d607032dSWang Chen err = dev_set_allmulti(dev, 1); 746d607032dSWang Chen if (err) { 747d607032dSWang Chen unregister_netdevice(dev); 7487dc00c82SWang Chen dev_put(dev); 749d607032dSWang Chen return err; 750d607032dSWang Chen } 7511da177e4SLinus Torvalds break; 7521da177e4SLinus Torvalds #endif 7531da177e4SLinus Torvalds case VIFF_TUNNEL: 7544feb88e5SBenjamin Thery dev = ipmr_new_tunnel(net, vifc); 7551da177e4SLinus Torvalds if (!dev) 7561da177e4SLinus Torvalds return -ENOBUFS; 757d607032dSWang Chen err = dev_set_allmulti(dev, 1); 758d607032dSWang Chen if (err) { 759d607032dSWang Chen ipmr_del_tunnel(dev, vifc); 7607dc00c82SWang Chen dev_put(dev); 761d607032dSWang Chen return err; 762d607032dSWang Chen } 7631da177e4SLinus Torvalds break; 764ee5e81f0SIlia K 765ee5e81f0SIlia K case VIFF_USE_IFINDEX: 7661da177e4SLinus Torvalds case 0: 767ee5e81f0SIlia K if (vifc->vifc_flags == VIFF_USE_IFINDEX) { 768ee5e81f0SIlia K dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex); 76951456b29SIan Morris if (dev && !__in_dev_get_rtnl(dev)) { 770ee5e81f0SIlia K dev_put(dev); 771ee5e81f0SIlia K return -EADDRNOTAVAIL; 772ee5e81f0SIlia K } 773a8cb16ddSEric Dumazet } else { 7744feb88e5SBenjamin Thery dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr); 775a8cb16ddSEric Dumazet } 7761da177e4SLinus Torvalds if (!dev) 7771da177e4SLinus Torvalds return -EADDRNOTAVAIL; 778d607032dSWang Chen err = dev_set_allmulti(dev, 1); 7797dc00c82SWang Chen if (err) { 7807dc00c82SWang Chen dev_put(dev); 781d607032dSWang Chen return err; 7827dc00c82SWang Chen } 7831da177e4SLinus Torvalds break; 7841da177e4SLinus Torvalds default: 7851da177e4SLinus Torvalds return -EINVAL; 7861da177e4SLinus Torvalds } 7871da177e4SLinus Torvalds 788a8cb16ddSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 789a8cb16ddSEric Dumazet if (!in_dev) { 790d0490cfdSDan Carpenter dev_put(dev); 7911da177e4SLinus Torvalds return -EADDRNOTAVAIL; 792d0490cfdSDan Carpenter } 79342f811b8SHerbert Xu IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++; 794d67b8c61SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING, dev->ifindex, 795d67b8c61SNicolas Dichtel &in_dev->cnf); 7961da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 7971da177e4SLinus Torvalds 798a8cb16ddSEric Dumazet /* Fill in the VIF structures */ 799a8cb16ddSEric Dumazet 8001da177e4SLinus Torvalds v->rate_limit = vifc->vifc_rate_limit; 8011da177e4SLinus Torvalds v->local = vifc->vifc_lcl_addr.s_addr; 8021da177e4SLinus Torvalds v->remote = vifc->vifc_rmt_addr.s_addr; 8031da177e4SLinus Torvalds v->flags = vifc->vifc_flags; 8041da177e4SLinus Torvalds if (!mrtsock) 8051da177e4SLinus Torvalds v->flags |= VIFF_STATIC; 8061da177e4SLinus Torvalds v->threshold = vifc->vifc_threshold; 8071da177e4SLinus Torvalds v->bytes_in = 0; 8081da177e4SLinus Torvalds v->bytes_out = 0; 8091da177e4SLinus Torvalds v->pkt_in = 0; 8101da177e4SLinus Torvalds v->pkt_out = 0; 8111da177e4SLinus Torvalds v->link = dev->ifindex; 8121da177e4SLinus Torvalds if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER)) 813a54acb3aSNicolas Dichtel v->link = dev_get_iflink(dev); 8141da177e4SLinus Torvalds 8151da177e4SLinus Torvalds /* And finish update writing critical data */ 8161da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 8171da177e4SLinus Torvalds v->dev = dev; 8181da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 8191da177e4SLinus Torvalds if (v->flags & VIFF_REGISTER) 8200c12295aSPatrick McHardy mrt->mroute_reg_vif_num = vifi; 8211da177e4SLinus Torvalds #endif 8220c12295aSPatrick McHardy if (vifi+1 > mrt->maxvif) 8230c12295aSPatrick McHardy mrt->maxvif = vifi+1; 8241da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 8251da177e4SLinus Torvalds return 0; 8261da177e4SLinus Torvalds } 8271da177e4SLinus Torvalds 828a8c9486bSEric Dumazet /* called with rcu_read_lock() */ 8290c12295aSPatrick McHardy static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, 8304feb88e5SBenjamin Thery __be32 origin, 8314feb88e5SBenjamin Thery __be32 mcastgrp) 8321da177e4SLinus Torvalds { 8331da177e4SLinus Torvalds int line = MFC_HASH(mcastgrp, origin); 8341da177e4SLinus Torvalds struct mfc_cache *c; 8351da177e4SLinus Torvalds 836a8c9486bSEric Dumazet list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) { 8371da177e4SLinus Torvalds if (c->mfc_origin == origin && c->mfc_mcastgrp == mcastgrp) 8381da177e4SLinus Torvalds return c; 8391da177e4SLinus Torvalds } 840862465f2SPatrick McHardy return NULL; 841862465f2SPatrick McHardy } 8421da177e4SLinus Torvalds 843660b26dcSNicolas Dichtel /* Look for a (*,*,oif) entry */ 844660b26dcSNicolas Dichtel static struct mfc_cache *ipmr_cache_find_any_parent(struct mr_table *mrt, 845660b26dcSNicolas Dichtel int vifi) 846660b26dcSNicolas Dichtel { 847360eb5daSNicolas Dichtel int line = MFC_HASH(htonl(INADDR_ANY), htonl(INADDR_ANY)); 848660b26dcSNicolas Dichtel struct mfc_cache *c; 849660b26dcSNicolas Dichtel 850660b26dcSNicolas Dichtel list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) 851360eb5daSNicolas Dichtel if (c->mfc_origin == htonl(INADDR_ANY) && 852360eb5daSNicolas Dichtel c->mfc_mcastgrp == htonl(INADDR_ANY) && 853660b26dcSNicolas Dichtel c->mfc_un.res.ttls[vifi] < 255) 854660b26dcSNicolas Dichtel return c; 855660b26dcSNicolas Dichtel 856660b26dcSNicolas Dichtel return NULL; 857660b26dcSNicolas Dichtel } 858660b26dcSNicolas Dichtel 859660b26dcSNicolas Dichtel /* Look for a (*,G) entry */ 860660b26dcSNicolas Dichtel static struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt, 861660b26dcSNicolas Dichtel __be32 mcastgrp, int vifi) 862660b26dcSNicolas Dichtel { 863360eb5daSNicolas Dichtel int line = MFC_HASH(mcastgrp, htonl(INADDR_ANY)); 864660b26dcSNicolas Dichtel struct mfc_cache *c, *proxy; 865660b26dcSNicolas Dichtel 866360eb5daSNicolas Dichtel if (mcastgrp == htonl(INADDR_ANY)) 867660b26dcSNicolas Dichtel goto skip; 868660b26dcSNicolas Dichtel 869660b26dcSNicolas Dichtel list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) 870360eb5daSNicolas Dichtel if (c->mfc_origin == htonl(INADDR_ANY) && 871660b26dcSNicolas Dichtel c->mfc_mcastgrp == mcastgrp) { 872660b26dcSNicolas Dichtel if (c->mfc_un.res.ttls[vifi] < 255) 873660b26dcSNicolas Dichtel return c; 874660b26dcSNicolas Dichtel 875660b26dcSNicolas Dichtel /* It's ok if the vifi is part of the static tree */ 876660b26dcSNicolas Dichtel proxy = ipmr_cache_find_any_parent(mrt, 877660b26dcSNicolas Dichtel c->mfc_parent); 878660b26dcSNicolas Dichtel if (proxy && proxy->mfc_un.res.ttls[vifi] < 255) 879660b26dcSNicolas Dichtel return c; 880660b26dcSNicolas Dichtel } 881660b26dcSNicolas Dichtel 882660b26dcSNicolas Dichtel skip: 883660b26dcSNicolas Dichtel return ipmr_cache_find_any_parent(mrt, vifi); 884660b26dcSNicolas Dichtel } 885660b26dcSNicolas Dichtel 8861da177e4SLinus Torvalds /* 8871da177e4SLinus Torvalds * Allocate a multicast cache entry 8881da177e4SLinus Torvalds */ 889d658f8a0SPatrick McHardy static struct mfc_cache *ipmr_cache_alloc(void) 8901da177e4SLinus Torvalds { 891c3762229SRobert P. J. Day struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL); 892a8c9486bSEric Dumazet 893a8c9486bSEric Dumazet if (c) 8941da177e4SLinus Torvalds c->mfc_un.res.minvif = MAXVIFS; 8951da177e4SLinus Torvalds return c; 8961da177e4SLinus Torvalds } 8971da177e4SLinus Torvalds 898d658f8a0SPatrick McHardy static struct mfc_cache *ipmr_cache_alloc_unres(void) 8991da177e4SLinus Torvalds { 900c3762229SRobert P. J. Day struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC); 901a8c9486bSEric Dumazet 902a8c9486bSEric Dumazet if (c) { 9031da177e4SLinus Torvalds skb_queue_head_init(&c->mfc_un.unres.unresolved); 9041da177e4SLinus Torvalds c->mfc_un.unres.expires = jiffies + 10*HZ; 905a8c9486bSEric Dumazet } 9061da177e4SLinus Torvalds return c; 9071da177e4SLinus Torvalds } 9081da177e4SLinus Torvalds 9091da177e4SLinus Torvalds /* 9101da177e4SLinus Torvalds * A cache entry has gone into a resolved state from queued 9111da177e4SLinus Torvalds */ 9121da177e4SLinus Torvalds 9130c12295aSPatrick McHardy static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt, 9140c12295aSPatrick McHardy struct mfc_cache *uc, struct mfc_cache *c) 9151da177e4SLinus Torvalds { 9161da177e4SLinus Torvalds struct sk_buff *skb; 9179ef1d4c7SPatrick McHardy struct nlmsgerr *e; 9181da177e4SLinus Torvalds 919a8cb16ddSEric Dumazet /* Play the pending entries through our router */ 9201da177e4SLinus Torvalds 9211da177e4SLinus Torvalds while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) { 922eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->version == 0) { 9231da177e4SLinus Torvalds struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr)); 9241da177e4SLinus Torvalds 925573ce260SHong zhi guo if (__ipmr_fill_mroute(mrt, skb, c, nlmsg_data(nlh)) > 0) { 926a8cb16ddSEric Dumazet nlh->nlmsg_len = skb_tail_pointer(skb) - 927a8cb16ddSEric Dumazet (u8 *)nlh; 9281da177e4SLinus Torvalds } else { 9291da177e4SLinus Torvalds nlh->nlmsg_type = NLMSG_ERROR; 930573ce260SHong zhi guo nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 9311da177e4SLinus Torvalds skb_trim(skb, nlh->nlmsg_len); 932573ce260SHong zhi guo e = nlmsg_data(nlh); 9339ef1d4c7SPatrick McHardy e->error = -EMSGSIZE; 9349ef1d4c7SPatrick McHardy memset(&e->msg, 0, sizeof(e->msg)); 9351da177e4SLinus Torvalds } 9362942e900SThomas Graf 93715e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 938a8cb16ddSEric Dumazet } else { 9390c12295aSPatrick McHardy ip_mr_forward(net, mrt, skb, c, 0); 9401da177e4SLinus Torvalds } 9411da177e4SLinus Torvalds } 942a8cb16ddSEric Dumazet } 9431da177e4SLinus Torvalds 9441da177e4SLinus Torvalds /* 9451da177e4SLinus Torvalds * Bounce a cache query up to mrouted. We could use netlink for this but mrouted 9461da177e4SLinus Torvalds * expects the following bizarre scheme. 9471da177e4SLinus Torvalds * 9481da177e4SLinus Torvalds * Called under mrt_lock. 9491da177e4SLinus Torvalds */ 9501da177e4SLinus Torvalds 9510c12295aSPatrick McHardy static int ipmr_cache_report(struct mr_table *mrt, 9524feb88e5SBenjamin Thery struct sk_buff *pkt, vifi_t vifi, int assert) 9531da177e4SLinus Torvalds { 9541da177e4SLinus Torvalds struct sk_buff *skb; 955c9bdd4b5SArnaldo Carvalho de Melo const int ihl = ip_hdrlen(pkt); 9561da177e4SLinus Torvalds struct igmphdr *igmp; 9571da177e4SLinus Torvalds struct igmpmsg *msg; 9584c968709SEric Dumazet struct sock *mroute_sk; 9591da177e4SLinus Torvalds int ret; 9601da177e4SLinus Torvalds 9611da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 9621da177e4SLinus Torvalds if (assert == IGMPMSG_WHOLEPKT) 9631da177e4SLinus Torvalds skb = skb_realloc_headroom(pkt, sizeof(struct iphdr)); 9641da177e4SLinus Torvalds else 9651da177e4SLinus Torvalds #endif 9661da177e4SLinus Torvalds skb = alloc_skb(128, GFP_ATOMIC); 9671da177e4SLinus Torvalds 9681da177e4SLinus Torvalds if (!skb) 9691da177e4SLinus Torvalds return -ENOBUFS; 9701da177e4SLinus Torvalds 9711da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 9721da177e4SLinus Torvalds if (assert == IGMPMSG_WHOLEPKT) { 9731da177e4SLinus Torvalds /* Ugly, but we have no choice with this interface. 974a8cb16ddSEric Dumazet * Duplicate old header, fix ihl, length etc. 975a8cb16ddSEric Dumazet * And all this only to mangle msg->im_msgtype and 976a8cb16ddSEric Dumazet * to set msg->im_mbz to "mbz" :-) 9771da177e4SLinus Torvalds */ 978878c8145SArnaldo Carvalho de Melo skb_push(skb, sizeof(struct iphdr)); 979878c8145SArnaldo Carvalho de Melo skb_reset_network_header(skb); 980badff6d0SArnaldo Carvalho de Melo skb_reset_transport_header(skb); 9810272ffc4SArnaldo Carvalho de Melo msg = (struct igmpmsg *)skb_network_header(skb); 982d56f90a7SArnaldo Carvalho de Melo memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr)); 9831da177e4SLinus Torvalds msg->im_msgtype = IGMPMSG_WHOLEPKT; 9841da177e4SLinus Torvalds msg->im_mbz = 0; 9850c12295aSPatrick McHardy msg->im_vif = mrt->mroute_reg_vif_num; 986eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2; 987eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) + 988eddc9ec5SArnaldo Carvalho de Melo sizeof(struct iphdr)); 9891da177e4SLinus Torvalds } else 9901da177e4SLinus Torvalds #endif 9911da177e4SLinus Torvalds { 9921da177e4SLinus Torvalds 993a8cb16ddSEric Dumazet /* Copy the IP header */ 9941da177e4SLinus Torvalds 99530f3a40fSCong Wang skb_set_network_header(skb, skb->len); 996ddc7b8e3SArnaldo Carvalho de Melo skb_put(skb, ihl); 99727d7ff46SArnaldo Carvalho de Melo skb_copy_to_linear_data(skb, pkt->data, ihl); 998eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */ 999eddc9ec5SArnaldo Carvalho de Melo msg = (struct igmpmsg *)skb_network_header(skb); 10001da177e4SLinus Torvalds msg->im_vif = vifi; 1001adf30907SEric Dumazet skb_dst_set(skb, dst_clone(skb_dst(pkt))); 10021da177e4SLinus Torvalds 1003a8cb16ddSEric Dumazet /* Add our header */ 10041da177e4SLinus Torvalds 10051da177e4SLinus Torvalds igmp = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr)); 10061da177e4SLinus Torvalds igmp->type = 10071da177e4SLinus Torvalds msg->im_msgtype = assert; 10081da177e4SLinus Torvalds igmp->code = 0; 1009eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */ 1010b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 10111da177e4SLinus Torvalds } 10121da177e4SLinus Torvalds 10134c968709SEric Dumazet rcu_read_lock(); 10144c968709SEric Dumazet mroute_sk = rcu_dereference(mrt->mroute_sk); 101551456b29SIan Morris if (!mroute_sk) { 10164c968709SEric Dumazet rcu_read_unlock(); 10171da177e4SLinus Torvalds kfree_skb(skb); 10181da177e4SLinus Torvalds return -EINVAL; 10191da177e4SLinus Torvalds } 10201da177e4SLinus Torvalds 1021a8cb16ddSEric Dumazet /* Deliver to mrouted */ 1022a8cb16ddSEric Dumazet 10234c968709SEric Dumazet ret = sock_queue_rcv_skb(mroute_sk, skb); 10244c968709SEric Dumazet rcu_read_unlock(); 102570a269e6SBenjamin Thery if (ret < 0) { 1026e87cc472SJoe Perches net_warn_ratelimited("mroute: pending queue full, dropping entries\n"); 10271da177e4SLinus Torvalds kfree_skb(skb); 10281da177e4SLinus Torvalds } 10291da177e4SLinus Torvalds 10301da177e4SLinus Torvalds return ret; 10311da177e4SLinus Torvalds } 10321da177e4SLinus Torvalds 10331da177e4SLinus Torvalds /* 10341da177e4SLinus Torvalds * Queue a packet for resolution. It gets locked cache entry! 10351da177e4SLinus Torvalds */ 10361da177e4SLinus Torvalds 10371da177e4SLinus Torvalds static int 10380c12295aSPatrick McHardy ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb) 10391da177e4SLinus Torvalds { 1040862465f2SPatrick McHardy bool found = false; 10411da177e4SLinus Torvalds int err; 10421da177e4SLinus Torvalds struct mfc_cache *c; 1043eddc9ec5SArnaldo Carvalho de Melo const struct iphdr *iph = ip_hdr(skb); 10441da177e4SLinus Torvalds 10451da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 10460c12295aSPatrick McHardy list_for_each_entry(c, &mrt->mfc_unres_queue, list) { 1047e258beb2SPatrick McHardy if (c->mfc_mcastgrp == iph->daddr && 1048862465f2SPatrick McHardy c->mfc_origin == iph->saddr) { 1049862465f2SPatrick McHardy found = true; 10501da177e4SLinus Torvalds break; 10511da177e4SLinus Torvalds } 1052862465f2SPatrick McHardy } 10531da177e4SLinus Torvalds 1054862465f2SPatrick McHardy if (!found) { 1055a8cb16ddSEric Dumazet /* Create a new entry if allowable */ 10561da177e4SLinus Torvalds 10570c12295aSPatrick McHardy if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 || 1058d658f8a0SPatrick McHardy (c = ipmr_cache_alloc_unres()) == NULL) { 10591da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 10601da177e4SLinus Torvalds 10611da177e4SLinus Torvalds kfree_skb(skb); 10621da177e4SLinus Torvalds return -ENOBUFS; 10631da177e4SLinus Torvalds } 10641da177e4SLinus Torvalds 1065a8cb16ddSEric Dumazet /* Fill in the new cache entry */ 1066a8cb16ddSEric Dumazet 10671da177e4SLinus Torvalds c->mfc_parent = -1; 1068eddc9ec5SArnaldo Carvalho de Melo c->mfc_origin = iph->saddr; 1069eddc9ec5SArnaldo Carvalho de Melo c->mfc_mcastgrp = iph->daddr; 10701da177e4SLinus Torvalds 1071a8cb16ddSEric Dumazet /* Reflect first query at mrouted. */ 1072a8cb16ddSEric Dumazet 10730c12295aSPatrick McHardy err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE); 10744feb88e5SBenjamin Thery if (err < 0) { 10751da177e4SLinus Torvalds /* If the report failed throw the cache entry 10761da177e4SLinus Torvalds out - Brad Parker 10771da177e4SLinus Torvalds */ 10781da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 10791da177e4SLinus Torvalds 10805c0a66f5SBenjamin Thery ipmr_cache_free(c); 10811da177e4SLinus Torvalds kfree_skb(skb); 10821da177e4SLinus Torvalds return err; 10831da177e4SLinus Torvalds } 10841da177e4SLinus Torvalds 10850c12295aSPatrick McHardy atomic_inc(&mrt->cache_resolve_queue_len); 10860c12295aSPatrick McHardy list_add(&c->list, &mrt->mfc_unres_queue); 10878cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 10881da177e4SLinus Torvalds 1089278554bdSDavid S. Miller if (atomic_read(&mrt->cache_resolve_queue_len) == 1) 10900c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires); 10911da177e4SLinus Torvalds } 10921da177e4SLinus Torvalds 1093a8cb16ddSEric Dumazet /* See if we can append the packet */ 1094a8cb16ddSEric Dumazet 10951da177e4SLinus Torvalds if (c->mfc_un.unres.unresolved.qlen > 3) { 10961da177e4SLinus Torvalds kfree_skb(skb); 10971da177e4SLinus Torvalds err = -ENOBUFS; 10981da177e4SLinus Torvalds } else { 10991da177e4SLinus Torvalds skb_queue_tail(&c->mfc_un.unres.unresolved, skb); 11001da177e4SLinus Torvalds err = 0; 11011da177e4SLinus Torvalds } 11021da177e4SLinus Torvalds 11031da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11041da177e4SLinus Torvalds return err; 11051da177e4SLinus Torvalds } 11061da177e4SLinus Torvalds 11071da177e4SLinus Torvalds /* 11081da177e4SLinus Torvalds * MFC cache manipulation by user space mroute daemon 11091da177e4SLinus Torvalds */ 11101da177e4SLinus Torvalds 1111660b26dcSNicolas Dichtel static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent) 11121da177e4SLinus Torvalds { 11131da177e4SLinus Torvalds int line; 1114862465f2SPatrick McHardy struct mfc_cache *c, *next; 11151da177e4SLinus Torvalds 11161da177e4SLinus Torvalds line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr); 11171da177e4SLinus Torvalds 11180c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) { 11191da177e4SLinus Torvalds if (c->mfc_origin == mfc->mfcc_origin.s_addr && 1120660b26dcSNicolas Dichtel c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr && 1121660b26dcSNicolas Dichtel (parent == -1 || parent == c->mfc_parent)) { 1122a8c9486bSEric Dumazet list_del_rcu(&c->list); 11238cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 11245c0a66f5SBenjamin Thery ipmr_cache_free(c); 11251da177e4SLinus Torvalds return 0; 11261da177e4SLinus Torvalds } 11271da177e4SLinus Torvalds } 11281da177e4SLinus Torvalds return -ENOENT; 11291da177e4SLinus Torvalds } 11301da177e4SLinus Torvalds 11310c12295aSPatrick McHardy static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, 1132660b26dcSNicolas Dichtel struct mfcctl *mfc, int mrtsock, int parent) 11331da177e4SLinus Torvalds { 1134862465f2SPatrick McHardy bool found = false; 11351da177e4SLinus Torvalds int line; 1136862465f2SPatrick McHardy struct mfc_cache *uc, *c; 11371da177e4SLinus Torvalds 1138a50436f2SPatrick McHardy if (mfc->mfcc_parent >= MAXVIFS) 1139a50436f2SPatrick McHardy return -ENFILE; 1140a50436f2SPatrick McHardy 11411da177e4SLinus Torvalds line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr); 11421da177e4SLinus Torvalds 11430c12295aSPatrick McHardy list_for_each_entry(c, &mrt->mfc_cache_array[line], list) { 11441da177e4SLinus Torvalds if (c->mfc_origin == mfc->mfcc_origin.s_addr && 1145660b26dcSNicolas Dichtel c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr && 1146660b26dcSNicolas Dichtel (parent == -1 || parent == c->mfc_parent)) { 1147862465f2SPatrick McHardy found = true; 11481da177e4SLinus Torvalds break; 11491da177e4SLinus Torvalds } 1150862465f2SPatrick McHardy } 11511da177e4SLinus Torvalds 1152862465f2SPatrick McHardy if (found) { 11531da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 11541da177e4SLinus Torvalds c->mfc_parent = mfc->mfcc_parent; 11550c12295aSPatrick McHardy ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls); 11561da177e4SLinus Torvalds if (!mrtsock) 11571da177e4SLinus Torvalds c->mfc_flags |= MFC_STATIC; 11581da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 11598cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 11601da177e4SLinus Torvalds return 0; 11611da177e4SLinus Torvalds } 11621da177e4SLinus Torvalds 1163360eb5daSNicolas Dichtel if (mfc->mfcc_mcastgrp.s_addr != htonl(INADDR_ANY) && 1164660b26dcSNicolas Dichtel !ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) 11651da177e4SLinus Torvalds return -EINVAL; 11661da177e4SLinus Torvalds 1167d658f8a0SPatrick McHardy c = ipmr_cache_alloc(); 116851456b29SIan Morris if (!c) 11691da177e4SLinus Torvalds return -ENOMEM; 11701da177e4SLinus Torvalds 11711da177e4SLinus Torvalds c->mfc_origin = mfc->mfcc_origin.s_addr; 11721da177e4SLinus Torvalds c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr; 11731da177e4SLinus Torvalds c->mfc_parent = mfc->mfcc_parent; 11740c12295aSPatrick McHardy ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls); 11751da177e4SLinus Torvalds if (!mrtsock) 11761da177e4SLinus Torvalds c->mfc_flags |= MFC_STATIC; 11771da177e4SLinus Torvalds 1178a8c9486bSEric Dumazet list_add_rcu(&c->list, &mrt->mfc_cache_array[line]); 11791da177e4SLinus Torvalds 11801da177e4SLinus Torvalds /* 11811da177e4SLinus Torvalds * Check to see if we resolved a queued list. If so we 11821da177e4SLinus Torvalds * need to send on the frames and tidy up. 11831da177e4SLinus Torvalds */ 1184b0ebb739SPatrick McHardy found = false; 11851da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 11860c12295aSPatrick McHardy list_for_each_entry(uc, &mrt->mfc_unres_queue, list) { 1187e258beb2SPatrick McHardy if (uc->mfc_origin == c->mfc_origin && 11881da177e4SLinus Torvalds uc->mfc_mcastgrp == c->mfc_mcastgrp) { 1189862465f2SPatrick McHardy list_del(&uc->list); 11900c12295aSPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len); 1191b0ebb739SPatrick McHardy found = true; 11921da177e4SLinus Torvalds break; 11931da177e4SLinus Torvalds } 11941da177e4SLinus Torvalds } 11950c12295aSPatrick McHardy if (list_empty(&mrt->mfc_unres_queue)) 11960c12295aSPatrick McHardy del_timer(&mrt->ipmr_expire_timer); 11971da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11981da177e4SLinus Torvalds 1199b0ebb739SPatrick McHardy if (found) { 12000c12295aSPatrick McHardy ipmr_cache_resolve(net, mrt, uc, c); 12015c0a66f5SBenjamin Thery ipmr_cache_free(uc); 12021da177e4SLinus Torvalds } 12038cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_NEWROUTE); 12041da177e4SLinus Torvalds return 0; 12051da177e4SLinus Torvalds } 12061da177e4SLinus Torvalds 12071da177e4SLinus Torvalds /* 12081da177e4SLinus Torvalds * Close the multicast socket, and clear the vif tables etc 12091da177e4SLinus Torvalds */ 12101da177e4SLinus Torvalds 1211*0e615e96SNikolay Aleksandrov static void mroute_clean_tables(struct mr_table *mrt, bool all) 12121da177e4SLinus Torvalds { 12131da177e4SLinus Torvalds int i; 1214d17fa6faSEric Dumazet LIST_HEAD(list); 1215862465f2SPatrick McHardy struct mfc_cache *c, *next; 12161da177e4SLinus Torvalds 1217a8cb16ddSEric Dumazet /* Shut down all active vif entries */ 1218a8cb16ddSEric Dumazet 12190c12295aSPatrick McHardy for (i = 0; i < mrt->maxvif; i++) { 1220*0e615e96SNikolay Aleksandrov if (!all && (mrt->vif_table[i].flags & VIFF_STATIC)) 1221*0e615e96SNikolay Aleksandrov continue; 12220c12295aSPatrick McHardy vif_delete(mrt, i, 0, &list); 12231da177e4SLinus Torvalds } 1224d17fa6faSEric Dumazet unregister_netdevice_many(&list); 12251da177e4SLinus Torvalds 1226a8cb16ddSEric Dumazet /* Wipe the cache */ 1227a8cb16ddSEric Dumazet 12281da177e4SLinus Torvalds for (i = 0; i < MFC_LINES; i++) { 12290c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[i], list) { 1230*0e615e96SNikolay Aleksandrov if (!all && (c->mfc_flags & MFC_STATIC)) 12311da177e4SLinus Torvalds continue; 1232a8c9486bSEric Dumazet list_del_rcu(&c->list); 12338cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 12345c0a66f5SBenjamin Thery ipmr_cache_free(c); 12351da177e4SLinus Torvalds } 12361da177e4SLinus Torvalds } 12371da177e4SLinus Torvalds 12380c12295aSPatrick McHardy if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { 12391da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 12400c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) { 1241862465f2SPatrick McHardy list_del(&c->list); 12428cd3ac9fSNicolas Dichtel mroute_netlink_event(mrt, c, RTM_DELROUTE); 12430c12295aSPatrick McHardy ipmr_destroy_unres(mrt, c); 12441da177e4SLinus Torvalds } 12451da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 12461da177e4SLinus Torvalds } 12471da177e4SLinus Torvalds } 12481da177e4SLinus Torvalds 12494c968709SEric Dumazet /* called from ip_ra_control(), before an RCU grace period, 12504c968709SEric Dumazet * we dont need to call synchronize_rcu() here 12514c968709SEric Dumazet */ 12521da177e4SLinus Torvalds static void mrtsock_destruct(struct sock *sk) 12531da177e4SLinus Torvalds { 12544feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1255f0ad0860SPatrick McHardy struct mr_table *mrt; 12564feb88e5SBenjamin Thery 12571da177e4SLinus Torvalds rtnl_lock(); 1258f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 12594c968709SEric Dumazet if (sk == rtnl_dereference(mrt->mroute_sk)) { 12604feb88e5SBenjamin Thery IPV4_DEVCONF_ALL(net, MC_FORWARDING)--; 1261d67b8c61SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING, 1262d67b8c61SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1263d67b8c61SNicolas Dichtel net->ipv4.devconf_all); 1264a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(mrt->mroute_sk, NULL); 1265*0e615e96SNikolay Aleksandrov mroute_clean_tables(mrt, false); 12661da177e4SLinus Torvalds } 12671da177e4SLinus Torvalds } 12681da177e4SLinus Torvalds rtnl_unlock(); 12691da177e4SLinus Torvalds } 12701da177e4SLinus Torvalds 12711da177e4SLinus Torvalds /* 12721da177e4SLinus Torvalds * Socket options and virtual interface manipulation. The whole 12731da177e4SLinus Torvalds * virtual interface system is a complete heap, but unfortunately 12741da177e4SLinus Torvalds * that's how BSD mrouted happens to think. Maybe one day with a proper 12751da177e4SLinus Torvalds * MOSPF/PIM router set up we can clean this up. 12761da177e4SLinus Torvalds */ 12771da177e4SLinus Torvalds 1278b7058842SDavid S. Miller int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen) 12791da177e4SLinus Torvalds { 1280660b26dcSNicolas Dichtel int ret, parent = 0; 12811da177e4SLinus Torvalds struct vifctl vif; 12821da177e4SLinus Torvalds struct mfcctl mfc; 12834feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1284f0ad0860SPatrick McHardy struct mr_table *mrt; 1285f0ad0860SPatrick McHardy 12865e1859fbSEric Dumazet if (sk->sk_type != SOCK_RAW || 12875e1859fbSEric Dumazet inet_sk(sk)->inet_num != IPPROTO_IGMP) 12885e1859fbSEric Dumazet return -EOPNOTSUPP; 12895e1859fbSEric Dumazet 1290f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 129151456b29SIan Morris if (!mrt) 1292f0ad0860SPatrick McHardy return -ENOENT; 12931da177e4SLinus Torvalds 1294132adf54SStephen Hemminger if (optname != MRT_INIT) { 129533d480ceSEric Dumazet if (sk != rcu_access_pointer(mrt->mroute_sk) && 129652e804c6SEric W. Biederman !ns_capable(net->user_ns, CAP_NET_ADMIN)) 12971da177e4SLinus Torvalds return -EACCES; 12981da177e4SLinus Torvalds } 12991da177e4SLinus Torvalds 1300132adf54SStephen Hemminger switch (optname) { 13011da177e4SLinus Torvalds case MRT_INIT: 13021da177e4SLinus Torvalds if (optlen != sizeof(int)) 13035e1859fbSEric Dumazet return -EINVAL; 13041da177e4SLinus Torvalds 13051da177e4SLinus Torvalds rtnl_lock(); 13064c968709SEric Dumazet if (rtnl_dereference(mrt->mroute_sk)) { 13071da177e4SLinus Torvalds rtnl_unlock(); 13081da177e4SLinus Torvalds return -EADDRINUSE; 13091da177e4SLinus Torvalds } 13101da177e4SLinus Torvalds 13111da177e4SLinus Torvalds ret = ip_ra_control(sk, 1, mrtsock_destruct); 13121da177e4SLinus Torvalds if (ret == 0) { 1313cf778b00SEric Dumazet rcu_assign_pointer(mrt->mroute_sk, sk); 13144feb88e5SBenjamin Thery IPV4_DEVCONF_ALL(net, MC_FORWARDING)++; 1315d67b8c61SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING, 1316d67b8c61SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1317d67b8c61SNicolas Dichtel net->ipv4.devconf_all); 13181da177e4SLinus Torvalds } 13191da177e4SLinus Torvalds rtnl_unlock(); 13201da177e4SLinus Torvalds return ret; 13211da177e4SLinus Torvalds case MRT_DONE: 132233d480ceSEric Dumazet if (sk != rcu_access_pointer(mrt->mroute_sk)) 13231da177e4SLinus Torvalds return -EACCES; 13241da177e4SLinus Torvalds return ip_ra_control(sk, 0, NULL); 13251da177e4SLinus Torvalds case MRT_ADD_VIF: 13261da177e4SLinus Torvalds case MRT_DEL_VIF: 13271da177e4SLinus Torvalds if (optlen != sizeof(vif)) 13281da177e4SLinus Torvalds return -EINVAL; 13291da177e4SLinus Torvalds if (copy_from_user(&vif, optval, sizeof(vif))) 13301da177e4SLinus Torvalds return -EFAULT; 13311da177e4SLinus Torvalds if (vif.vifc_vifi >= MAXVIFS) 13321da177e4SLinus Torvalds return -ENFILE; 13331da177e4SLinus Torvalds rtnl_lock(); 13341da177e4SLinus Torvalds if (optname == MRT_ADD_VIF) { 13354c968709SEric Dumazet ret = vif_add(net, mrt, &vif, 13364c968709SEric Dumazet sk == rtnl_dereference(mrt->mroute_sk)); 13371da177e4SLinus Torvalds } else { 13380c12295aSPatrick McHardy ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL); 13391da177e4SLinus Torvalds } 13401da177e4SLinus Torvalds rtnl_unlock(); 13411da177e4SLinus Torvalds return ret; 13421da177e4SLinus Torvalds 13431da177e4SLinus Torvalds /* 13441da177e4SLinus Torvalds * Manipulate the forwarding caches. These live 13451da177e4SLinus Torvalds * in a sort of kernel/user symbiosis. 13461da177e4SLinus Torvalds */ 13471da177e4SLinus Torvalds case MRT_ADD_MFC: 13481da177e4SLinus Torvalds case MRT_DEL_MFC: 1349660b26dcSNicolas Dichtel parent = -1; 1350660b26dcSNicolas Dichtel case MRT_ADD_MFC_PROXY: 1351660b26dcSNicolas Dichtel case MRT_DEL_MFC_PROXY: 13521da177e4SLinus Torvalds if (optlen != sizeof(mfc)) 13531da177e4SLinus Torvalds return -EINVAL; 13541da177e4SLinus Torvalds if (copy_from_user(&mfc, optval, sizeof(mfc))) 13551da177e4SLinus Torvalds return -EFAULT; 1356660b26dcSNicolas Dichtel if (parent == 0) 1357660b26dcSNicolas Dichtel parent = mfc.mfcc_parent; 13581da177e4SLinus Torvalds rtnl_lock(); 1359660b26dcSNicolas Dichtel if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY) 1360660b26dcSNicolas Dichtel ret = ipmr_mfc_delete(mrt, &mfc, parent); 13611da177e4SLinus Torvalds else 13624c968709SEric Dumazet ret = ipmr_mfc_add(net, mrt, &mfc, 1363660b26dcSNicolas Dichtel sk == rtnl_dereference(mrt->mroute_sk), 1364660b26dcSNicolas Dichtel parent); 13651da177e4SLinus Torvalds rtnl_unlock(); 13661da177e4SLinus Torvalds return ret; 13671da177e4SLinus Torvalds /* 13681da177e4SLinus Torvalds * Control PIM assert. 13691da177e4SLinus Torvalds */ 13701da177e4SLinus Torvalds case MRT_ASSERT: 13711da177e4SLinus Torvalds { 13721da177e4SLinus Torvalds int v; 13735e1859fbSEric Dumazet if (optlen != sizeof(v)) 13745e1859fbSEric Dumazet return -EINVAL; 13751da177e4SLinus Torvalds if (get_user(v, (int __user *)optval)) 13761da177e4SLinus Torvalds return -EFAULT; 137753d6841dSJoe Perches mrt->mroute_do_assert = v; 13781da177e4SLinus Torvalds return 0; 13791da177e4SLinus Torvalds } 13801da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 13811da177e4SLinus Torvalds case MRT_PIM: 13821da177e4SLinus Torvalds { 1383ba93ef74SStephen Hemminger int v; 1384ba93ef74SStephen Hemminger 13855e1859fbSEric Dumazet if (optlen != sizeof(v)) 13865e1859fbSEric Dumazet return -EINVAL; 13871da177e4SLinus Torvalds if (get_user(v, (int __user *)optval)) 13881da177e4SLinus Torvalds return -EFAULT; 13895e1859fbSEric Dumazet v = !!v; 1390ba93ef74SStephen Hemminger 13911da177e4SLinus Torvalds rtnl_lock(); 13921da177e4SLinus Torvalds ret = 0; 13930c12295aSPatrick McHardy if (v != mrt->mroute_do_pim) { 13940c12295aSPatrick McHardy mrt->mroute_do_pim = v; 13950c12295aSPatrick McHardy mrt->mroute_do_assert = v; 13961da177e4SLinus Torvalds } 13971da177e4SLinus Torvalds rtnl_unlock(); 13981da177e4SLinus Torvalds return ret; 13991da177e4SLinus Torvalds } 14001da177e4SLinus Torvalds #endif 1401f0ad0860SPatrick McHardy #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 1402f0ad0860SPatrick McHardy case MRT_TABLE: 1403f0ad0860SPatrick McHardy { 1404f0ad0860SPatrick McHardy u32 v; 1405f0ad0860SPatrick McHardy 1406f0ad0860SPatrick McHardy if (optlen != sizeof(u32)) 1407f0ad0860SPatrick McHardy return -EINVAL; 1408f0ad0860SPatrick McHardy if (get_user(v, (u32 __user *)optval)) 1409f0ad0860SPatrick McHardy return -EFAULT; 1410f0ad0860SPatrick McHardy 1411b49d3c1eSEric Dumazet /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */ 1412b49d3c1eSEric Dumazet if (v != RT_TABLE_DEFAULT && v >= 1000000000) 1413b49d3c1eSEric Dumazet return -EINVAL; 1414b49d3c1eSEric Dumazet 1415f0ad0860SPatrick McHardy rtnl_lock(); 1416f0ad0860SPatrick McHardy ret = 0; 14174c968709SEric Dumazet if (sk == rtnl_dereference(mrt->mroute_sk)) { 14184c968709SEric Dumazet ret = -EBUSY; 14194c968709SEric Dumazet } else { 1420f0ad0860SPatrick McHardy if (!ipmr_new_table(net, v)) 1421f0ad0860SPatrick McHardy ret = -ENOMEM; 14225e1859fbSEric Dumazet else 1423f0ad0860SPatrick McHardy raw_sk(sk)->ipmr_table = v; 14244c968709SEric Dumazet } 1425f0ad0860SPatrick McHardy rtnl_unlock(); 1426f0ad0860SPatrick McHardy return ret; 1427f0ad0860SPatrick McHardy } 1428f0ad0860SPatrick McHardy #endif 14291da177e4SLinus Torvalds /* 14301da177e4SLinus Torvalds * Spurious command, or MRT_VERSION which you cannot 14311da177e4SLinus Torvalds * set. 14321da177e4SLinus Torvalds */ 14331da177e4SLinus Torvalds default: 14341da177e4SLinus Torvalds return -ENOPROTOOPT; 14351da177e4SLinus Torvalds } 14361da177e4SLinus Torvalds } 14371da177e4SLinus Torvalds 14381da177e4SLinus Torvalds /* 14391da177e4SLinus Torvalds * Getsock opt support for the multicast routing system. 14401da177e4SLinus Torvalds */ 14411da177e4SLinus Torvalds 14421da177e4SLinus Torvalds int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen) 14431da177e4SLinus Torvalds { 14441da177e4SLinus Torvalds int olr; 14451da177e4SLinus Torvalds int val; 14464feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1447f0ad0860SPatrick McHardy struct mr_table *mrt; 1448f0ad0860SPatrick McHardy 14495e1859fbSEric Dumazet if (sk->sk_type != SOCK_RAW || 14505e1859fbSEric Dumazet inet_sk(sk)->inet_num != IPPROTO_IGMP) 14515e1859fbSEric Dumazet return -EOPNOTSUPP; 14525e1859fbSEric Dumazet 1453f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 145451456b29SIan Morris if (!mrt) 1455f0ad0860SPatrick McHardy return -ENOENT; 14561da177e4SLinus Torvalds 14571da177e4SLinus Torvalds if (optname != MRT_VERSION && 14581da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 14591da177e4SLinus Torvalds optname != MRT_PIM && 14601da177e4SLinus Torvalds #endif 14611da177e4SLinus Torvalds optname != MRT_ASSERT) 14621da177e4SLinus Torvalds return -ENOPROTOOPT; 14631da177e4SLinus Torvalds 14641da177e4SLinus Torvalds if (get_user(olr, optlen)) 14651da177e4SLinus Torvalds return -EFAULT; 14661da177e4SLinus Torvalds 14671da177e4SLinus Torvalds olr = min_t(unsigned int, olr, sizeof(int)); 14681da177e4SLinus Torvalds if (olr < 0) 14691da177e4SLinus Torvalds return -EINVAL; 14701da177e4SLinus Torvalds 14711da177e4SLinus Torvalds if (put_user(olr, optlen)) 14721da177e4SLinus Torvalds return -EFAULT; 14731da177e4SLinus Torvalds if (optname == MRT_VERSION) 14741da177e4SLinus Torvalds val = 0x0305; 14751da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 14761da177e4SLinus Torvalds else if (optname == MRT_PIM) 14770c12295aSPatrick McHardy val = mrt->mroute_do_pim; 14781da177e4SLinus Torvalds #endif 14791da177e4SLinus Torvalds else 14800c12295aSPatrick McHardy val = mrt->mroute_do_assert; 14811da177e4SLinus Torvalds if (copy_to_user(optval, &val, olr)) 14821da177e4SLinus Torvalds return -EFAULT; 14831da177e4SLinus Torvalds return 0; 14841da177e4SLinus Torvalds } 14851da177e4SLinus Torvalds 14861da177e4SLinus Torvalds /* 14871da177e4SLinus Torvalds * The IP multicast ioctl support routines. 14881da177e4SLinus Torvalds */ 14891da177e4SLinus Torvalds 14901da177e4SLinus Torvalds int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) 14911da177e4SLinus Torvalds { 14921da177e4SLinus Torvalds struct sioc_sg_req sr; 14931da177e4SLinus Torvalds struct sioc_vif_req vr; 14941da177e4SLinus Torvalds struct vif_device *vif; 14951da177e4SLinus Torvalds struct mfc_cache *c; 14964feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1497f0ad0860SPatrick McHardy struct mr_table *mrt; 1498f0ad0860SPatrick McHardy 1499f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 150051456b29SIan Morris if (!mrt) 1501f0ad0860SPatrick McHardy return -ENOENT; 15021da177e4SLinus Torvalds 1503132adf54SStephen Hemminger switch (cmd) { 15041da177e4SLinus Torvalds case SIOCGETVIFCNT: 15051da177e4SLinus Torvalds if (copy_from_user(&vr, arg, sizeof(vr))) 15061da177e4SLinus Torvalds return -EFAULT; 15070c12295aSPatrick McHardy if (vr.vifi >= mrt->maxvif) 15081da177e4SLinus Torvalds return -EINVAL; 15091da177e4SLinus Torvalds read_lock(&mrt_lock); 15100c12295aSPatrick McHardy vif = &mrt->vif_table[vr.vifi]; 15110c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vr.vifi)) { 15121da177e4SLinus Torvalds vr.icount = vif->pkt_in; 15131da177e4SLinus Torvalds vr.ocount = vif->pkt_out; 15141da177e4SLinus Torvalds vr.ibytes = vif->bytes_in; 15151da177e4SLinus Torvalds vr.obytes = vif->bytes_out; 15161da177e4SLinus Torvalds read_unlock(&mrt_lock); 15171da177e4SLinus Torvalds 15181da177e4SLinus Torvalds if (copy_to_user(arg, &vr, sizeof(vr))) 15191da177e4SLinus Torvalds return -EFAULT; 15201da177e4SLinus Torvalds return 0; 15211da177e4SLinus Torvalds } 15221da177e4SLinus Torvalds read_unlock(&mrt_lock); 15231da177e4SLinus Torvalds return -EADDRNOTAVAIL; 15241da177e4SLinus Torvalds case SIOCGETSGCNT: 15251da177e4SLinus Torvalds if (copy_from_user(&sr, arg, sizeof(sr))) 15261da177e4SLinus Torvalds return -EFAULT; 15271da177e4SLinus Torvalds 1528a8c9486bSEric Dumazet rcu_read_lock(); 15290c12295aSPatrick McHardy c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); 15301da177e4SLinus Torvalds if (c) { 15311da177e4SLinus Torvalds sr.pktcnt = c->mfc_un.res.pkt; 15321da177e4SLinus Torvalds sr.bytecnt = c->mfc_un.res.bytes; 15331da177e4SLinus Torvalds sr.wrong_if = c->mfc_un.res.wrong_if; 1534a8c9486bSEric Dumazet rcu_read_unlock(); 15351da177e4SLinus Torvalds 15361da177e4SLinus Torvalds if (copy_to_user(arg, &sr, sizeof(sr))) 15371da177e4SLinus Torvalds return -EFAULT; 15381da177e4SLinus Torvalds return 0; 15391da177e4SLinus Torvalds } 1540a8c9486bSEric Dumazet rcu_read_unlock(); 15411da177e4SLinus Torvalds return -EADDRNOTAVAIL; 15421da177e4SLinus Torvalds default: 15431da177e4SLinus Torvalds return -ENOIOCTLCMD; 15441da177e4SLinus Torvalds } 15451da177e4SLinus Torvalds } 15461da177e4SLinus Torvalds 1547709b46e8SEric W. Biederman #ifdef CONFIG_COMPAT 1548709b46e8SEric W. Biederman struct compat_sioc_sg_req { 1549709b46e8SEric W. Biederman struct in_addr src; 1550709b46e8SEric W. Biederman struct in_addr grp; 1551709b46e8SEric W. Biederman compat_ulong_t pktcnt; 1552709b46e8SEric W. Biederman compat_ulong_t bytecnt; 1553709b46e8SEric W. Biederman compat_ulong_t wrong_if; 1554709b46e8SEric W. Biederman }; 1555709b46e8SEric W. Biederman 1556ca6b8bb0SDavid S. Miller struct compat_sioc_vif_req { 1557ca6b8bb0SDavid S. Miller vifi_t vifi; /* Which iface */ 1558ca6b8bb0SDavid S. Miller compat_ulong_t icount; 1559ca6b8bb0SDavid S. Miller compat_ulong_t ocount; 1560ca6b8bb0SDavid S. Miller compat_ulong_t ibytes; 1561ca6b8bb0SDavid S. Miller compat_ulong_t obytes; 1562ca6b8bb0SDavid S. Miller }; 1563ca6b8bb0SDavid S. Miller 1564709b46e8SEric W. Biederman int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) 1565709b46e8SEric W. Biederman { 15660033d5adSDavid S. Miller struct compat_sioc_sg_req sr; 1567ca6b8bb0SDavid S. Miller struct compat_sioc_vif_req vr; 1568ca6b8bb0SDavid S. Miller struct vif_device *vif; 1569709b46e8SEric W. Biederman struct mfc_cache *c; 1570709b46e8SEric W. Biederman struct net *net = sock_net(sk); 1571709b46e8SEric W. Biederman struct mr_table *mrt; 1572709b46e8SEric W. Biederman 1573709b46e8SEric W. Biederman mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 157451456b29SIan Morris if (!mrt) 1575709b46e8SEric W. Biederman return -ENOENT; 1576709b46e8SEric W. Biederman 1577709b46e8SEric W. Biederman switch (cmd) { 1578ca6b8bb0SDavid S. Miller case SIOCGETVIFCNT: 1579ca6b8bb0SDavid S. Miller if (copy_from_user(&vr, arg, sizeof(vr))) 1580ca6b8bb0SDavid S. Miller return -EFAULT; 1581ca6b8bb0SDavid S. Miller if (vr.vifi >= mrt->maxvif) 1582ca6b8bb0SDavid S. Miller return -EINVAL; 1583ca6b8bb0SDavid S. Miller read_lock(&mrt_lock); 1584ca6b8bb0SDavid S. Miller vif = &mrt->vif_table[vr.vifi]; 1585ca6b8bb0SDavid S. Miller if (VIF_EXISTS(mrt, vr.vifi)) { 1586ca6b8bb0SDavid S. Miller vr.icount = vif->pkt_in; 1587ca6b8bb0SDavid S. Miller vr.ocount = vif->pkt_out; 1588ca6b8bb0SDavid S. Miller vr.ibytes = vif->bytes_in; 1589ca6b8bb0SDavid S. Miller vr.obytes = vif->bytes_out; 1590ca6b8bb0SDavid S. Miller read_unlock(&mrt_lock); 1591ca6b8bb0SDavid S. Miller 1592ca6b8bb0SDavid S. Miller if (copy_to_user(arg, &vr, sizeof(vr))) 1593ca6b8bb0SDavid S. Miller return -EFAULT; 1594ca6b8bb0SDavid S. Miller return 0; 1595ca6b8bb0SDavid S. Miller } 1596ca6b8bb0SDavid S. Miller read_unlock(&mrt_lock); 1597ca6b8bb0SDavid S. Miller return -EADDRNOTAVAIL; 1598709b46e8SEric W. Biederman case SIOCGETSGCNT: 1599709b46e8SEric W. Biederman if (copy_from_user(&sr, arg, sizeof(sr))) 1600709b46e8SEric W. Biederman return -EFAULT; 1601709b46e8SEric W. Biederman 1602709b46e8SEric W. Biederman rcu_read_lock(); 1603709b46e8SEric W. Biederman c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); 1604709b46e8SEric W. Biederman if (c) { 1605709b46e8SEric W. Biederman sr.pktcnt = c->mfc_un.res.pkt; 1606709b46e8SEric W. Biederman sr.bytecnt = c->mfc_un.res.bytes; 1607709b46e8SEric W. Biederman sr.wrong_if = c->mfc_un.res.wrong_if; 1608709b46e8SEric W. Biederman rcu_read_unlock(); 1609709b46e8SEric W. Biederman 1610709b46e8SEric W. Biederman if (copy_to_user(arg, &sr, sizeof(sr))) 1611709b46e8SEric W. Biederman return -EFAULT; 1612709b46e8SEric W. Biederman return 0; 1613709b46e8SEric W. Biederman } 1614709b46e8SEric W. Biederman rcu_read_unlock(); 1615709b46e8SEric W. Biederman return -EADDRNOTAVAIL; 1616709b46e8SEric W. Biederman default: 1617709b46e8SEric W. Biederman return -ENOIOCTLCMD; 1618709b46e8SEric W. Biederman } 1619709b46e8SEric W. Biederman } 1620709b46e8SEric W. Biederman #endif 1621709b46e8SEric W. Biederman 16221da177e4SLinus Torvalds 16231da177e4SLinus Torvalds static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr) 16241da177e4SLinus Torvalds { 1625351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 16264feb88e5SBenjamin Thery struct net *net = dev_net(dev); 1627f0ad0860SPatrick McHardy struct mr_table *mrt; 16281da177e4SLinus Torvalds struct vif_device *v; 16291da177e4SLinus Torvalds int ct; 1630e9dc8653SEric W. Biederman 16311da177e4SLinus Torvalds if (event != NETDEV_UNREGISTER) 16321da177e4SLinus Torvalds return NOTIFY_DONE; 1633f0ad0860SPatrick McHardy 1634f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 16350c12295aSPatrick McHardy v = &mrt->vif_table[0]; 16360c12295aSPatrick McHardy for (ct = 0; ct < mrt->maxvif; ct++, v++) { 1637e9dc8653SEric W. Biederman if (v->dev == dev) 1638e92036a6SRongQing.Li vif_delete(mrt, ct, 1, NULL); 16391da177e4SLinus Torvalds } 16401da177e4SLinus Torvalds } 16411da177e4SLinus Torvalds return NOTIFY_DONE; 16421da177e4SLinus Torvalds } 16431da177e4SLinus Torvalds 16441da177e4SLinus Torvalds 16451da177e4SLinus Torvalds static struct notifier_block ip_mr_notifier = { 16461da177e4SLinus Torvalds .notifier_call = ipmr_device_event, 16471da177e4SLinus Torvalds }; 16481da177e4SLinus Torvalds 16491da177e4SLinus Torvalds /* 16501da177e4SLinus Torvalds * Encapsulate a packet by attaching a valid IPIP header to it. 16511da177e4SLinus Torvalds * This avoids tunnel drivers and other mess and gives us the speed so 16521da177e4SLinus Torvalds * important for multicast video. 16531da177e4SLinus Torvalds */ 16541da177e4SLinus Torvalds 1655b6a7719aSHannes Frederic Sowa static void ip_encap(struct net *net, struct sk_buff *skb, 1656b6a7719aSHannes Frederic Sowa __be32 saddr, __be32 daddr) 16571da177e4SLinus Torvalds { 16588856dfa3SArnaldo Carvalho de Melo struct iphdr *iph; 1659b71d1d42SEric Dumazet const struct iphdr *old_iph = ip_hdr(skb); 16608856dfa3SArnaldo Carvalho de Melo 16618856dfa3SArnaldo Carvalho de Melo skb_push(skb, sizeof(struct iphdr)); 1662b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 16638856dfa3SArnaldo Carvalho de Melo skb_reset_network_header(skb); 1664eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 16651da177e4SLinus Torvalds 16661da177e4SLinus Torvalds iph->version = 4; 1667e023dd64SArnaldo Carvalho de Melo iph->tos = old_iph->tos; 1668e023dd64SArnaldo Carvalho de Melo iph->ttl = old_iph->ttl; 16691da177e4SLinus Torvalds iph->frag_off = 0; 16701da177e4SLinus Torvalds iph->daddr = daddr; 16711da177e4SLinus Torvalds iph->saddr = saddr; 16721da177e4SLinus Torvalds iph->protocol = IPPROTO_IPIP; 16731da177e4SLinus Torvalds iph->ihl = 5; 16741da177e4SLinus Torvalds iph->tot_len = htons(skb->len); 1675b6a7719aSHannes Frederic Sowa ip_select_ident(net, skb, NULL); 16761da177e4SLinus Torvalds ip_send_check(iph); 16771da177e4SLinus Torvalds 16781da177e4SLinus Torvalds memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 16791da177e4SLinus Torvalds nf_reset(skb); 16801da177e4SLinus Torvalds } 16811da177e4SLinus Torvalds 16820c4b51f0SEric W. Biederman static inline int ipmr_forward_finish(struct net *net, struct sock *sk, 16830c4b51f0SEric W. Biederman struct sk_buff *skb) 16841da177e4SLinus Torvalds { 16851da177e4SLinus Torvalds struct ip_options *opt = &(IPCB(skb)->opt); 16861da177e4SLinus Torvalds 168773186df8SDavid S. Miller IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS); 168873186df8SDavid S. Miller IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len); 16891da177e4SLinus Torvalds 16901da177e4SLinus Torvalds if (unlikely(opt->optlen)) 16911da177e4SLinus Torvalds ip_forward_options(skb); 16921da177e4SLinus Torvalds 169313206b6bSEric W. Biederman return dst_output(net, sk, skb); 16941da177e4SLinus Torvalds } 16951da177e4SLinus Torvalds 16961da177e4SLinus Torvalds /* 16971da177e4SLinus Torvalds * Processing handlers for ipmr_forward 16981da177e4SLinus Torvalds */ 16991da177e4SLinus Torvalds 17000c12295aSPatrick McHardy static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, 17010c12295aSPatrick McHardy struct sk_buff *skb, struct mfc_cache *c, int vifi) 17021da177e4SLinus Torvalds { 1703eddc9ec5SArnaldo Carvalho de Melo const struct iphdr *iph = ip_hdr(skb); 17040c12295aSPatrick McHardy struct vif_device *vif = &mrt->vif_table[vifi]; 17051da177e4SLinus Torvalds struct net_device *dev; 17061da177e4SLinus Torvalds struct rtable *rt; 170731e4543dSDavid S. Miller struct flowi4 fl4; 17081da177e4SLinus Torvalds int encap = 0; 17091da177e4SLinus Torvalds 171051456b29SIan Morris if (!vif->dev) 17111da177e4SLinus Torvalds goto out_free; 17121da177e4SLinus Torvalds 17131da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 17141da177e4SLinus Torvalds if (vif->flags & VIFF_REGISTER) { 17151da177e4SLinus Torvalds vif->pkt_out++; 17161da177e4SLinus Torvalds vif->bytes_out += skb->len; 1717cf3677aeSPavel Emelyanov vif->dev->stats.tx_bytes += skb->len; 1718cf3677aeSPavel Emelyanov vif->dev->stats.tx_packets++; 17190c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT); 172069ebbf58SIlpo Järvinen goto out_free; 17211da177e4SLinus Torvalds } 17221da177e4SLinus Torvalds #endif 17231da177e4SLinus Torvalds 17241da177e4SLinus Torvalds if (vif->flags & VIFF_TUNNEL) { 172531e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, 172678fbfd8aSDavid S. Miller vif->remote, vif->local, 172778fbfd8aSDavid S. Miller 0, 0, 172878fbfd8aSDavid S. Miller IPPROTO_IPIP, 172978fbfd8aSDavid S. Miller RT_TOS(iph->tos), vif->link); 1730b23dd4feSDavid S. Miller if (IS_ERR(rt)) 17311da177e4SLinus Torvalds goto out_free; 17321da177e4SLinus Torvalds encap = sizeof(struct iphdr); 17331da177e4SLinus Torvalds } else { 173431e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, iph->daddr, 0, 173578fbfd8aSDavid S. Miller 0, 0, 173678fbfd8aSDavid S. Miller IPPROTO_IPIP, 173778fbfd8aSDavid S. Miller RT_TOS(iph->tos), vif->link); 1738b23dd4feSDavid S. Miller if (IS_ERR(rt)) 17391da177e4SLinus Torvalds goto out_free; 17401da177e4SLinus Torvalds } 17411da177e4SLinus Torvalds 1742d8d1f30bSChangli Gao dev = rt->dst.dev; 17431da177e4SLinus Torvalds 1744d8d1f30bSChangli Gao if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) { 17451da177e4SLinus Torvalds /* Do not fragment multicasts. Alas, IPv4 does not 1746a8cb16ddSEric Dumazet * allow to send ICMP, so that packets will disappear 1747a8cb16ddSEric Dumazet * to blackhole. 17481da177e4SLinus Torvalds */ 17491da177e4SLinus Torvalds 175073186df8SDavid S. Miller IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); 17511da177e4SLinus Torvalds ip_rt_put(rt); 17521da177e4SLinus Torvalds goto out_free; 17531da177e4SLinus Torvalds } 17541da177e4SLinus Torvalds 1755d8d1f30bSChangli Gao encap += LL_RESERVED_SPACE(dev) + rt->dst.header_len; 17561da177e4SLinus Torvalds 17571da177e4SLinus Torvalds if (skb_cow(skb, encap)) { 17581da177e4SLinus Torvalds ip_rt_put(rt); 17591da177e4SLinus Torvalds goto out_free; 17601da177e4SLinus Torvalds } 17611da177e4SLinus Torvalds 17621da177e4SLinus Torvalds vif->pkt_out++; 17631da177e4SLinus Torvalds vif->bytes_out += skb->len; 17641da177e4SLinus Torvalds 1765adf30907SEric Dumazet skb_dst_drop(skb); 1766d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 1767eddc9ec5SArnaldo Carvalho de Melo ip_decrease_ttl(ip_hdr(skb)); 17681da177e4SLinus Torvalds 17691da177e4SLinus Torvalds /* FIXME: forward and output firewalls used to be called here. 1770a8cb16ddSEric Dumazet * What do we do with netfilter? -- RR 1771a8cb16ddSEric Dumazet */ 17721da177e4SLinus Torvalds if (vif->flags & VIFF_TUNNEL) { 1773b6a7719aSHannes Frederic Sowa ip_encap(net, skb, vif->local, vif->remote); 17741da177e4SLinus Torvalds /* FIXME: extra output firewall step used to be here. --RR */ 17752f4c02d4SPavel Emelyanov vif->dev->stats.tx_packets++; 17762f4c02d4SPavel Emelyanov vif->dev->stats.tx_bytes += skb->len; 17771da177e4SLinus Torvalds } 17781da177e4SLinus Torvalds 17791da177e4SLinus Torvalds IPCB(skb)->flags |= IPSKB_FORWARDED; 17801da177e4SLinus Torvalds 17811da177e4SLinus Torvalds /* 17821da177e4SLinus Torvalds * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally 17831da177e4SLinus Torvalds * not only before forwarding, but after forwarding on all output 17841da177e4SLinus Torvalds * interfaces. It is clear, if mrouter runs a multicasting 17851da177e4SLinus Torvalds * program, it should receive packets not depending to what interface 17861da177e4SLinus Torvalds * program is joined. 17871da177e4SLinus Torvalds * If we will not make it, the program will have to join on all 17881da177e4SLinus Torvalds * interfaces. On the other hand, multihoming host (or router, but 17891da177e4SLinus Torvalds * not mrouter) cannot join to more than one interface - it will 17901da177e4SLinus Torvalds * result in receiving multiple packets. 17911da177e4SLinus Torvalds */ 179229a26a56SEric W. Biederman NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, 179329a26a56SEric W. Biederman net, NULL, skb, skb->dev, dev, 17941da177e4SLinus Torvalds ipmr_forward_finish); 17951da177e4SLinus Torvalds return; 17961da177e4SLinus Torvalds 17971da177e4SLinus Torvalds out_free: 17981da177e4SLinus Torvalds kfree_skb(skb); 17991da177e4SLinus Torvalds } 18001da177e4SLinus Torvalds 18010c12295aSPatrick McHardy static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev) 18021da177e4SLinus Torvalds { 18031da177e4SLinus Torvalds int ct; 18040c12295aSPatrick McHardy 18050c12295aSPatrick McHardy for (ct = mrt->maxvif-1; ct >= 0; ct--) { 18060c12295aSPatrick McHardy if (mrt->vif_table[ct].dev == dev) 18071da177e4SLinus Torvalds break; 18081da177e4SLinus Torvalds } 18091da177e4SLinus Torvalds return ct; 18101da177e4SLinus Torvalds } 18111da177e4SLinus Torvalds 18121da177e4SLinus Torvalds /* "local" means that we should preserve one skb (for local delivery) */ 18131da177e4SLinus Torvalds 1814c4854ec8SRami Rosen static void ip_mr_forward(struct net *net, struct mr_table *mrt, 18150c12295aSPatrick McHardy struct sk_buff *skb, struct mfc_cache *cache, 18160c12295aSPatrick McHardy int local) 18171da177e4SLinus Torvalds { 18181da177e4SLinus Torvalds int psend = -1; 18191da177e4SLinus Torvalds int vif, ct; 1820660b26dcSNicolas Dichtel int true_vifi = ipmr_find_vif(mrt, skb->dev); 18211da177e4SLinus Torvalds 18221da177e4SLinus Torvalds vif = cache->mfc_parent; 18231da177e4SLinus Torvalds cache->mfc_un.res.pkt++; 18241da177e4SLinus Torvalds cache->mfc_un.res.bytes += skb->len; 18251da177e4SLinus Torvalds 1826360eb5daSNicolas Dichtel if (cache->mfc_origin == htonl(INADDR_ANY) && true_vifi >= 0) { 1827660b26dcSNicolas Dichtel struct mfc_cache *cache_proxy; 1828660b26dcSNicolas Dichtel 1829660b26dcSNicolas Dichtel /* For an (*,G) entry, we only check that the incomming 1830660b26dcSNicolas Dichtel * interface is part of the static tree. 1831660b26dcSNicolas Dichtel */ 1832660b26dcSNicolas Dichtel cache_proxy = ipmr_cache_find_any_parent(mrt, vif); 1833660b26dcSNicolas Dichtel if (cache_proxy && 1834660b26dcSNicolas Dichtel cache_proxy->mfc_un.res.ttls[true_vifi] < 255) 1835660b26dcSNicolas Dichtel goto forward; 1836660b26dcSNicolas Dichtel } 1837660b26dcSNicolas Dichtel 18381da177e4SLinus Torvalds /* 18391da177e4SLinus Torvalds * Wrong interface: drop packet and (maybe) send PIM assert. 18401da177e4SLinus Torvalds */ 18410c12295aSPatrick McHardy if (mrt->vif_table[vif].dev != skb->dev) { 1842c7537967SDavid S. Miller if (rt_is_output_route(skb_rtable(skb))) { 18431da177e4SLinus Torvalds /* It is our own packet, looped back. 1844a8cb16ddSEric Dumazet * Very complicated situation... 1845a8cb16ddSEric Dumazet * 1846a8cb16ddSEric Dumazet * The best workaround until routing daemons will be 1847a8cb16ddSEric Dumazet * fixed is not to redistribute packet, if it was 1848a8cb16ddSEric Dumazet * send through wrong interface. It means, that 1849a8cb16ddSEric Dumazet * multicast applications WILL NOT work for 1850a8cb16ddSEric Dumazet * (S,G), which have default multicast route pointing 1851a8cb16ddSEric Dumazet * to wrong oif. In any case, it is not a good 1852a8cb16ddSEric Dumazet * idea to use multicasting applications on router. 18531da177e4SLinus Torvalds */ 18541da177e4SLinus Torvalds goto dont_forward; 18551da177e4SLinus Torvalds } 18561da177e4SLinus Torvalds 18571da177e4SLinus Torvalds cache->mfc_un.res.wrong_if++; 18581da177e4SLinus Torvalds 18590c12295aSPatrick McHardy if (true_vifi >= 0 && mrt->mroute_do_assert && 18601da177e4SLinus Torvalds /* pimsm uses asserts, when switching from RPT to SPT, 1861a8cb16ddSEric Dumazet * so that we cannot check that packet arrived on an oif. 1862a8cb16ddSEric Dumazet * It is bad, but otherwise we would need to move pretty 1863a8cb16ddSEric Dumazet * large chunk of pimd to kernel. Ough... --ANK 18641da177e4SLinus Torvalds */ 18650c12295aSPatrick McHardy (mrt->mroute_do_pim || 18666f9374a9SBenjamin Thery cache->mfc_un.res.ttls[true_vifi] < 255) && 18671da177e4SLinus Torvalds time_after(jiffies, 18681da177e4SLinus Torvalds cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) { 18691da177e4SLinus Torvalds cache->mfc_un.res.last_assert = jiffies; 18700c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF); 18711da177e4SLinus Torvalds } 18721da177e4SLinus Torvalds goto dont_forward; 18731da177e4SLinus Torvalds } 18741da177e4SLinus Torvalds 1875660b26dcSNicolas Dichtel forward: 18760c12295aSPatrick McHardy mrt->vif_table[vif].pkt_in++; 18770c12295aSPatrick McHardy mrt->vif_table[vif].bytes_in += skb->len; 18781da177e4SLinus Torvalds 18791da177e4SLinus Torvalds /* 18801da177e4SLinus Torvalds * Forward the frame 18811da177e4SLinus Torvalds */ 1882360eb5daSNicolas Dichtel if (cache->mfc_origin == htonl(INADDR_ANY) && 1883360eb5daSNicolas Dichtel cache->mfc_mcastgrp == htonl(INADDR_ANY)) { 1884660b26dcSNicolas Dichtel if (true_vifi >= 0 && 1885660b26dcSNicolas Dichtel true_vifi != cache->mfc_parent && 1886660b26dcSNicolas Dichtel ip_hdr(skb)->ttl > 1887660b26dcSNicolas Dichtel cache->mfc_un.res.ttls[cache->mfc_parent]) { 1888660b26dcSNicolas Dichtel /* It's an (*,*) entry and the packet is not coming from 1889660b26dcSNicolas Dichtel * the upstream: forward the packet to the upstream 1890660b26dcSNicolas Dichtel * only. 1891660b26dcSNicolas Dichtel */ 1892660b26dcSNicolas Dichtel psend = cache->mfc_parent; 1893660b26dcSNicolas Dichtel goto last_forward; 1894660b26dcSNicolas Dichtel } 1895660b26dcSNicolas Dichtel goto dont_forward; 1896660b26dcSNicolas Dichtel } 1897a8cb16ddSEric Dumazet for (ct = cache->mfc_un.res.maxvif - 1; 1898a8cb16ddSEric Dumazet ct >= cache->mfc_un.res.minvif; ct--) { 1899660b26dcSNicolas Dichtel /* For (*,G) entry, don't forward to the incoming interface */ 1900360eb5daSNicolas Dichtel if ((cache->mfc_origin != htonl(INADDR_ANY) || 1901360eb5daSNicolas Dichtel ct != true_vifi) && 1902660b26dcSNicolas Dichtel ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) { 19031da177e4SLinus Torvalds if (psend != -1) { 19041da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 1905a8cb16ddSEric Dumazet 19061da177e4SLinus Torvalds if (skb2) 19070c12295aSPatrick McHardy ipmr_queue_xmit(net, mrt, skb2, cache, 19080c12295aSPatrick McHardy psend); 19091da177e4SLinus Torvalds } 19101da177e4SLinus Torvalds psend = ct; 19111da177e4SLinus Torvalds } 19121da177e4SLinus Torvalds } 1913660b26dcSNicolas Dichtel last_forward: 19141da177e4SLinus Torvalds if (psend != -1) { 19151da177e4SLinus Torvalds if (local) { 19161da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 1917a8cb16ddSEric Dumazet 19181da177e4SLinus Torvalds if (skb2) 19190c12295aSPatrick McHardy ipmr_queue_xmit(net, mrt, skb2, cache, psend); 19201da177e4SLinus Torvalds } else { 19210c12295aSPatrick McHardy ipmr_queue_xmit(net, mrt, skb, cache, psend); 1922c4854ec8SRami Rosen return; 19231da177e4SLinus Torvalds } 19241da177e4SLinus Torvalds } 19251da177e4SLinus Torvalds 19261da177e4SLinus Torvalds dont_forward: 19271da177e4SLinus Torvalds if (!local) 19281da177e4SLinus Torvalds kfree_skb(skb); 19291da177e4SLinus Torvalds } 19301da177e4SLinus Torvalds 1931417da66fSDavid S. Miller static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb) 1932ee3f1aafSDavid S. Miller { 1933417da66fSDavid S. Miller struct rtable *rt = skb_rtable(skb); 1934417da66fSDavid S. Miller struct iphdr *iph = ip_hdr(skb); 1935da91981bSDavid S. Miller struct flowi4 fl4 = { 1936417da66fSDavid S. Miller .daddr = iph->daddr, 1937417da66fSDavid S. Miller .saddr = iph->saddr, 1938b0fe4a31SJulian Anastasov .flowi4_tos = RT_TOS(iph->tos), 19394fd551d7SDavid S. Miller .flowi4_oif = (rt_is_output_route(rt) ? 19404fd551d7SDavid S. Miller skb->dev->ifindex : 0), 19414fd551d7SDavid S. Miller .flowi4_iif = (rt_is_output_route(rt) ? 19421fb9489bSPavel Emelyanov LOOPBACK_IFINDEX : 19434fd551d7SDavid S. Miller skb->dev->ifindex), 1944b4869889SDavid Miller .flowi4_mark = skb->mark, 1945ee3f1aafSDavid S. Miller }; 1946ee3f1aafSDavid S. Miller struct mr_table *mrt; 1947ee3f1aafSDavid S. Miller int err; 1948ee3f1aafSDavid S. Miller 1949da91981bSDavid S. Miller err = ipmr_fib_lookup(net, &fl4, &mrt); 1950ee3f1aafSDavid S. Miller if (err) 1951ee3f1aafSDavid S. Miller return ERR_PTR(err); 1952ee3f1aafSDavid S. Miller return mrt; 1953ee3f1aafSDavid S. Miller } 19541da177e4SLinus Torvalds 19551da177e4SLinus Torvalds /* 19561da177e4SLinus Torvalds * Multicast packets for forwarding arrive here 19574c968709SEric Dumazet * Called with rcu_read_lock(); 19581da177e4SLinus Torvalds */ 19591da177e4SLinus Torvalds 19601da177e4SLinus Torvalds int ip_mr_input(struct sk_buff *skb) 19611da177e4SLinus Torvalds { 19621da177e4SLinus Torvalds struct mfc_cache *cache; 19634feb88e5SBenjamin Thery struct net *net = dev_net(skb->dev); 1964511c3f92SEric Dumazet int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL; 1965f0ad0860SPatrick McHardy struct mr_table *mrt; 19661da177e4SLinus Torvalds 19671da177e4SLinus Torvalds /* Packet is looped back after forward, it should not be 1968a8cb16ddSEric Dumazet * forwarded second time, but still can be delivered locally. 19691da177e4SLinus Torvalds */ 19701da177e4SLinus Torvalds if (IPCB(skb)->flags & IPSKB_FORWARDED) 19711da177e4SLinus Torvalds goto dont_forward; 19721da177e4SLinus Torvalds 1973417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 1974ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) { 1975e40dbc51SBen Greear kfree_skb(skb); 1976ee3f1aafSDavid S. Miller return PTR_ERR(mrt); 19775e2b61f7SDavid S. Miller } 19781da177e4SLinus Torvalds if (!local) { 19791da177e4SLinus Torvalds if (IPCB(skb)->opt.router_alert) { 19801da177e4SLinus Torvalds if (ip_call_ra_chain(skb)) 19811da177e4SLinus Torvalds return 0; 1982eddc9ec5SArnaldo Carvalho de Melo } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP) { 19831da177e4SLinus Torvalds /* IGMPv1 (and broken IGMPv2 implementations sort of 19844c968709SEric Dumazet * Cisco IOS <= 11.2(8)) do not put router alert 19854c968709SEric Dumazet * option to IGMP packets destined to routable 19864c968709SEric Dumazet * groups. It is very bad, because it means 19874c968709SEric Dumazet * that we can forward NO IGMP messages. 19881da177e4SLinus Torvalds */ 19894c968709SEric Dumazet struct sock *mroute_sk; 19904c968709SEric Dumazet 19914c968709SEric Dumazet mroute_sk = rcu_dereference(mrt->mroute_sk); 19924c968709SEric Dumazet if (mroute_sk) { 19932715bcf9SPatrick McHardy nf_reset(skb); 19944c968709SEric Dumazet raw_rcv(mroute_sk, skb); 19951da177e4SLinus Torvalds return 0; 19961da177e4SLinus Torvalds } 19971da177e4SLinus Torvalds } 19981da177e4SLinus Torvalds } 19991da177e4SLinus Torvalds 2000a8c9486bSEric Dumazet /* already under rcu_read_lock() */ 20010c12295aSPatrick McHardy cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); 200251456b29SIan Morris if (!cache) { 2003660b26dcSNicolas Dichtel int vif = ipmr_find_vif(mrt, skb->dev); 2004660b26dcSNicolas Dichtel 2005660b26dcSNicolas Dichtel if (vif >= 0) 2006660b26dcSNicolas Dichtel cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr, 2007660b26dcSNicolas Dichtel vif); 2008660b26dcSNicolas Dichtel } 20091da177e4SLinus Torvalds 20101da177e4SLinus Torvalds /* 20111da177e4SLinus Torvalds * No usable cache entry 20121da177e4SLinus Torvalds */ 201351456b29SIan Morris if (!cache) { 20141da177e4SLinus Torvalds int vif; 20151da177e4SLinus Torvalds 20161da177e4SLinus Torvalds if (local) { 20171da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 20181da177e4SLinus Torvalds ip_local_deliver(skb); 201951456b29SIan Morris if (!skb2) 20201da177e4SLinus Torvalds return -ENOBUFS; 20211da177e4SLinus Torvalds skb = skb2; 20221da177e4SLinus Torvalds } 20231da177e4SLinus Torvalds 2024a8c9486bSEric Dumazet read_lock(&mrt_lock); 20250c12295aSPatrick McHardy vif = ipmr_find_vif(mrt, skb->dev); 20261da177e4SLinus Torvalds if (vif >= 0) { 20270eae88f3SEric Dumazet int err2 = ipmr_cache_unresolved(mrt, vif, skb); 20281da177e4SLinus Torvalds read_unlock(&mrt_lock); 20291da177e4SLinus Torvalds 20300eae88f3SEric Dumazet return err2; 20311da177e4SLinus Torvalds } 20321da177e4SLinus Torvalds read_unlock(&mrt_lock); 20331da177e4SLinus Torvalds kfree_skb(skb); 20341da177e4SLinus Torvalds return -ENODEV; 20351da177e4SLinus Torvalds } 20361da177e4SLinus Torvalds 2037a8c9486bSEric Dumazet read_lock(&mrt_lock); 20380c12295aSPatrick McHardy ip_mr_forward(net, mrt, skb, cache, local); 20391da177e4SLinus Torvalds read_unlock(&mrt_lock); 20401da177e4SLinus Torvalds 20411da177e4SLinus Torvalds if (local) 20421da177e4SLinus Torvalds return ip_local_deliver(skb); 20431da177e4SLinus Torvalds 20441da177e4SLinus Torvalds return 0; 20451da177e4SLinus Torvalds 20461da177e4SLinus Torvalds dont_forward: 20471da177e4SLinus Torvalds if (local) 20481da177e4SLinus Torvalds return ip_local_deliver(skb); 20491da177e4SLinus Torvalds kfree_skb(skb); 20501da177e4SLinus Torvalds return 0; 20511da177e4SLinus Torvalds } 20521da177e4SLinus Torvalds 2053b1879204SIlpo Järvinen #ifdef CONFIG_IP_PIMSM 205455747a0aSEric Dumazet /* called with rcu_read_lock() */ 2055f0ad0860SPatrick McHardy static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb, 2056f0ad0860SPatrick McHardy unsigned int pimlen) 20571da177e4SLinus Torvalds { 20581da177e4SLinus Torvalds struct net_device *reg_dev = NULL; 2059b1879204SIlpo Järvinen struct iphdr *encap; 20601da177e4SLinus Torvalds 2061b1879204SIlpo Järvinen encap = (struct iphdr *)(skb_transport_header(skb) + pimlen); 20621da177e4SLinus Torvalds /* 2063a8cb16ddSEric Dumazet * Check that: 2064a8cb16ddSEric Dumazet * a. packet is really sent to a multicast group 2065a8cb16ddSEric Dumazet * b. packet is not a NULL-REGISTER 2066a8cb16ddSEric Dumazet * c. packet is not truncated 20671da177e4SLinus Torvalds */ 2068f97c1e0cSJoe Perches if (!ipv4_is_multicast(encap->daddr) || 20691da177e4SLinus Torvalds encap->tot_len == 0 || 2070b1879204SIlpo Järvinen ntohs(encap->tot_len) + pimlen > skb->len) 2071b1879204SIlpo Järvinen return 1; 20721da177e4SLinus Torvalds 20731da177e4SLinus Torvalds read_lock(&mrt_lock); 20740c12295aSPatrick McHardy if (mrt->mroute_reg_vif_num >= 0) 20750c12295aSPatrick McHardy reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev; 20761da177e4SLinus Torvalds read_unlock(&mrt_lock); 20771da177e4SLinus Torvalds 207851456b29SIan Morris if (!reg_dev) 2079b1879204SIlpo Järvinen return 1; 20801da177e4SLinus Torvalds 2081b0e380b1SArnaldo Carvalho de Melo skb->mac_header = skb->network_header; 20821da177e4SLinus Torvalds skb_pull(skb, (u8 *)encap - skb->data); 208331c7711bSArnaldo Carvalho de Melo skb_reset_network_header(skb); 20841da177e4SLinus Torvalds skb->protocol = htons(ETH_P_IP); 208555747a0aSEric Dumazet skb->ip_summed = CHECKSUM_NONE; 2086d19d56ddSEric Dumazet 2087ea23192eSNicolas Dichtel skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev)); 2088d19d56ddSEric Dumazet 20891da177e4SLinus Torvalds netif_rx(skb); 2090b1879204SIlpo Järvinen 209155747a0aSEric Dumazet return NET_RX_SUCCESS; 2092b1879204SIlpo Järvinen } 2093b1879204SIlpo Järvinen #endif 2094b1879204SIlpo Järvinen 2095b1879204SIlpo Järvinen #ifdef CONFIG_IP_PIMSM_V1 2096b1879204SIlpo Järvinen /* 2097b1879204SIlpo Järvinen * Handle IGMP messages of PIMv1 2098b1879204SIlpo Järvinen */ 2099b1879204SIlpo Järvinen 2100b1879204SIlpo Järvinen int pim_rcv_v1(struct sk_buff *skb) 2101b1879204SIlpo Järvinen { 2102b1879204SIlpo Järvinen struct igmphdr *pim; 21034feb88e5SBenjamin Thery struct net *net = dev_net(skb->dev); 2104f0ad0860SPatrick McHardy struct mr_table *mrt; 2105b1879204SIlpo Järvinen 2106b1879204SIlpo Järvinen if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 2107b1879204SIlpo Järvinen goto drop; 2108b1879204SIlpo Järvinen 2109b1879204SIlpo Järvinen pim = igmp_hdr(skb); 2110b1879204SIlpo Järvinen 2111417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2112ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) 2113f0ad0860SPatrick McHardy goto drop; 21140c12295aSPatrick McHardy if (!mrt->mroute_do_pim || 2115b1879204SIlpo Järvinen pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) 2116b1879204SIlpo Järvinen goto drop; 2117b1879204SIlpo Järvinen 2118f0ad0860SPatrick McHardy if (__pim_rcv(mrt, skb, sizeof(*pim))) { 21191da177e4SLinus Torvalds drop: 21201da177e4SLinus Torvalds kfree_skb(skb); 2121b1879204SIlpo Järvinen } 21221da177e4SLinus Torvalds return 0; 21231da177e4SLinus Torvalds } 21241da177e4SLinus Torvalds #endif 21251da177e4SLinus Torvalds 21261da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V2 21271da177e4SLinus Torvalds static int pim_rcv(struct sk_buff *skb) 21281da177e4SLinus Torvalds { 21291da177e4SLinus Torvalds struct pimreghdr *pim; 2130f0ad0860SPatrick McHardy struct net *net = dev_net(skb->dev); 2131f0ad0860SPatrick McHardy struct mr_table *mrt; 21321da177e4SLinus Torvalds 2133b1879204SIlpo Järvinen if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 21341da177e4SLinus Torvalds goto drop; 21351da177e4SLinus Torvalds 21369c70220bSArnaldo Carvalho de Melo pim = (struct pimreghdr *)skb_transport_header(skb); 21371da177e4SLinus Torvalds if (pim->type != ((PIM_VERSION << 4) | (PIM_REGISTER)) || 21381da177e4SLinus Torvalds (pim->flags & PIM_NULL_REGISTER) || 21391da177e4SLinus Torvalds (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && 2140d3bc23e7SAl Viro csum_fold(skb_checksum(skb, 0, skb->len, 0)))) 21411da177e4SLinus Torvalds goto drop; 21421da177e4SLinus Torvalds 2143417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2144ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) 2145f0ad0860SPatrick McHardy goto drop; 2146f0ad0860SPatrick McHardy if (__pim_rcv(mrt, skb, sizeof(*pim))) { 21471da177e4SLinus Torvalds drop: 21481da177e4SLinus Torvalds kfree_skb(skb); 2149b1879204SIlpo Järvinen } 21501da177e4SLinus Torvalds return 0; 21511da177e4SLinus Torvalds } 21521da177e4SLinus Torvalds #endif 21531da177e4SLinus Torvalds 2154cb6a4e46SPatrick McHardy static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 2155cb6a4e46SPatrick McHardy struct mfc_cache *c, struct rtmsg *rtm) 21561da177e4SLinus Torvalds { 21571da177e4SLinus Torvalds int ct; 21581da177e4SLinus Torvalds struct rtnexthop *nhp; 215992a395e5SThomas Graf struct nlattr *mp_attr; 2160adfa85e4SNicolas Dichtel struct rta_mfc_stats mfcs; 21611da177e4SLinus Torvalds 21627438189bSNicolas Dichtel /* If cache is unresolved, don't try to parse IIF and OIF */ 2163ed0f160aSDan Carpenter if (c->mfc_parent >= MAXVIFS) 21647438189bSNicolas Dichtel return -ENOENT; 21657438189bSNicolas Dichtel 216692a395e5SThomas Graf if (VIF_EXISTS(mrt, c->mfc_parent) && 216792a395e5SThomas Graf nla_put_u32(skb, RTA_IIF, mrt->vif_table[c->mfc_parent].dev->ifindex) < 0) 216892a395e5SThomas Graf return -EMSGSIZE; 21691da177e4SLinus Torvalds 217092a395e5SThomas Graf if (!(mp_attr = nla_nest_start(skb, RTA_MULTIPATH))) 217192a395e5SThomas Graf return -EMSGSIZE; 21721da177e4SLinus Torvalds 21731da177e4SLinus Torvalds for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) { 21740c12295aSPatrick McHardy if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) { 217592a395e5SThomas Graf if (!(nhp = nla_reserve_nohdr(skb, sizeof(*nhp)))) { 217692a395e5SThomas Graf nla_nest_cancel(skb, mp_attr); 217792a395e5SThomas Graf return -EMSGSIZE; 217892a395e5SThomas Graf } 217992a395e5SThomas Graf 21801da177e4SLinus Torvalds nhp->rtnh_flags = 0; 21811da177e4SLinus Torvalds nhp->rtnh_hops = c->mfc_un.res.ttls[ct]; 21820c12295aSPatrick McHardy nhp->rtnh_ifindex = mrt->vif_table[ct].dev->ifindex; 21831da177e4SLinus Torvalds nhp->rtnh_len = sizeof(*nhp); 21841da177e4SLinus Torvalds } 21851da177e4SLinus Torvalds } 218692a395e5SThomas Graf 218792a395e5SThomas Graf nla_nest_end(skb, mp_attr); 218892a395e5SThomas Graf 2189adfa85e4SNicolas Dichtel mfcs.mfcs_packets = c->mfc_un.res.pkt; 2190adfa85e4SNicolas Dichtel mfcs.mfcs_bytes = c->mfc_un.res.bytes; 2191adfa85e4SNicolas Dichtel mfcs.mfcs_wrong_if = c->mfc_un.res.wrong_if; 2192adfa85e4SNicolas Dichtel if (nla_put(skb, RTA_MFC_STATS, sizeof(mfcs), &mfcs) < 0) 2193adfa85e4SNicolas Dichtel return -EMSGSIZE; 2194adfa85e4SNicolas Dichtel 21951da177e4SLinus Torvalds rtm->rtm_type = RTN_MULTICAST; 21961da177e4SLinus Torvalds return 1; 21971da177e4SLinus Torvalds } 21981da177e4SLinus Torvalds 21999a1b9496SDavid S. Miller int ipmr_get_route(struct net *net, struct sk_buff *skb, 22009a1b9496SDavid S. Miller __be32 saddr, __be32 daddr, 22019a1b9496SDavid S. Miller struct rtmsg *rtm, int nowait) 22021da177e4SLinus Torvalds { 22031da177e4SLinus Torvalds struct mfc_cache *cache; 22049a1b9496SDavid S. Miller struct mr_table *mrt; 22059a1b9496SDavid S. Miller int err; 22061da177e4SLinus Torvalds 2207f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 220851456b29SIan Morris if (!mrt) 2209f0ad0860SPatrick McHardy return -ENOENT; 2210f0ad0860SPatrick McHardy 2211a8c9486bSEric Dumazet rcu_read_lock(); 22129a1b9496SDavid S. Miller cache = ipmr_cache_find(mrt, saddr, daddr); 221351456b29SIan Morris if (!cache && skb->dev) { 2214660b26dcSNicolas Dichtel int vif = ipmr_find_vif(mrt, skb->dev); 22151da177e4SLinus Torvalds 2216660b26dcSNicolas Dichtel if (vif >= 0) 2217660b26dcSNicolas Dichtel cache = ipmr_cache_find_any(mrt, daddr, vif); 2218660b26dcSNicolas Dichtel } 221951456b29SIan Morris if (!cache) { 222072287490SAlexey Kuznetsov struct sk_buff *skb2; 2221eddc9ec5SArnaldo Carvalho de Melo struct iphdr *iph; 22221da177e4SLinus Torvalds struct net_device *dev; 2223a8cb16ddSEric Dumazet int vif = -1; 22241da177e4SLinus Torvalds 22251da177e4SLinus Torvalds if (nowait) { 2226a8c9486bSEric Dumazet rcu_read_unlock(); 22271da177e4SLinus Torvalds return -EAGAIN; 22281da177e4SLinus Torvalds } 22291da177e4SLinus Torvalds 22301da177e4SLinus Torvalds dev = skb->dev; 2231a8c9486bSEric Dumazet read_lock(&mrt_lock); 2232a8cb16ddSEric Dumazet if (dev) 2233a8cb16ddSEric Dumazet vif = ipmr_find_vif(mrt, dev); 2234a8cb16ddSEric Dumazet if (vif < 0) { 22351da177e4SLinus Torvalds read_unlock(&mrt_lock); 2236a8c9486bSEric Dumazet rcu_read_unlock(); 22371da177e4SLinus Torvalds return -ENODEV; 22381da177e4SLinus Torvalds } 223972287490SAlexey Kuznetsov skb2 = skb_clone(skb, GFP_ATOMIC); 224072287490SAlexey Kuznetsov if (!skb2) { 224172287490SAlexey Kuznetsov read_unlock(&mrt_lock); 2242a8c9486bSEric Dumazet rcu_read_unlock(); 224372287490SAlexey Kuznetsov return -ENOMEM; 224472287490SAlexey Kuznetsov } 224572287490SAlexey Kuznetsov 2246e2d1bca7SArnaldo Carvalho de Melo skb_push(skb2, sizeof(struct iphdr)); 2247e2d1bca7SArnaldo Carvalho de Melo skb_reset_network_header(skb2); 2248eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb2); 2249eddc9ec5SArnaldo Carvalho de Melo iph->ihl = sizeof(struct iphdr) >> 2; 22509a1b9496SDavid S. Miller iph->saddr = saddr; 22519a1b9496SDavid S. Miller iph->daddr = daddr; 2252eddc9ec5SArnaldo Carvalho de Melo iph->version = 0; 22530c12295aSPatrick McHardy err = ipmr_cache_unresolved(mrt, vif, skb2); 22541da177e4SLinus Torvalds read_unlock(&mrt_lock); 2255a8c9486bSEric Dumazet rcu_read_unlock(); 22561da177e4SLinus Torvalds return err; 22571da177e4SLinus Torvalds } 22581da177e4SLinus Torvalds 2259a8c9486bSEric Dumazet read_lock(&mrt_lock); 22601da177e4SLinus Torvalds if (!nowait && (rtm->rtm_flags & RTM_F_NOTIFY)) 22611da177e4SLinus Torvalds cache->mfc_flags |= MFC_NOTIFY; 2262cb6a4e46SPatrick McHardy err = __ipmr_fill_mroute(mrt, skb, cache, rtm); 22631da177e4SLinus Torvalds read_unlock(&mrt_lock); 2264a8c9486bSEric Dumazet rcu_read_unlock(); 22651da177e4SLinus Torvalds return err; 22661da177e4SLinus Torvalds } 22671da177e4SLinus Torvalds 2268cb6a4e46SPatrick McHardy static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 226965886f43SNicolas Dichtel u32 portid, u32 seq, struct mfc_cache *c, int cmd, 227065886f43SNicolas Dichtel int flags) 2271cb6a4e46SPatrick McHardy { 2272cb6a4e46SPatrick McHardy struct nlmsghdr *nlh; 2273cb6a4e46SPatrick McHardy struct rtmsg *rtm; 22741eb99af5SNicolas Dichtel int err; 2275cb6a4e46SPatrick McHardy 227665886f43SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags); 227751456b29SIan Morris if (!nlh) 2278cb6a4e46SPatrick McHardy return -EMSGSIZE; 2279cb6a4e46SPatrick McHardy 2280cb6a4e46SPatrick McHardy rtm = nlmsg_data(nlh); 2281cb6a4e46SPatrick McHardy rtm->rtm_family = RTNL_FAMILY_IPMR; 2282cb6a4e46SPatrick McHardy rtm->rtm_dst_len = 32; 2283cb6a4e46SPatrick McHardy rtm->rtm_src_len = 32; 2284cb6a4e46SPatrick McHardy rtm->rtm_tos = 0; 2285cb6a4e46SPatrick McHardy rtm->rtm_table = mrt->id; 2286f3756b79SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, mrt->id)) 2287f3756b79SDavid S. Miller goto nla_put_failure; 2288cb6a4e46SPatrick McHardy rtm->rtm_type = RTN_MULTICAST; 2289cb6a4e46SPatrick McHardy rtm->rtm_scope = RT_SCOPE_UNIVERSE; 22909a68ac72SNicolas Dichtel if (c->mfc_flags & MFC_STATIC) 22919a68ac72SNicolas Dichtel rtm->rtm_protocol = RTPROT_STATIC; 22929a68ac72SNicolas Dichtel else 22939a68ac72SNicolas Dichtel rtm->rtm_protocol = RTPROT_MROUTED; 2294cb6a4e46SPatrick McHardy rtm->rtm_flags = 0; 2295cb6a4e46SPatrick McHardy 2296930345eaSJiri Benc if (nla_put_in_addr(skb, RTA_SRC, c->mfc_origin) || 2297930345eaSJiri Benc nla_put_in_addr(skb, RTA_DST, c->mfc_mcastgrp)) 2298f3756b79SDavid S. Miller goto nla_put_failure; 22991eb99af5SNicolas Dichtel err = __ipmr_fill_mroute(mrt, skb, c, rtm); 23001eb99af5SNicolas Dichtel /* do not break the dump if cache is unresolved */ 23011eb99af5SNicolas Dichtel if (err < 0 && err != -ENOENT) 2302cb6a4e46SPatrick McHardy goto nla_put_failure; 2303cb6a4e46SPatrick McHardy 2304053c095aSJohannes Berg nlmsg_end(skb, nlh); 2305053c095aSJohannes Berg return 0; 2306cb6a4e46SPatrick McHardy 2307cb6a4e46SPatrick McHardy nla_put_failure: 2308cb6a4e46SPatrick McHardy nlmsg_cancel(skb, nlh); 2309cb6a4e46SPatrick McHardy return -EMSGSIZE; 2310cb6a4e46SPatrick McHardy } 2311cb6a4e46SPatrick McHardy 23128cd3ac9fSNicolas Dichtel static size_t mroute_msgsize(bool unresolved, int maxvif) 23138cd3ac9fSNicolas Dichtel { 23148cd3ac9fSNicolas Dichtel size_t len = 23158cd3ac9fSNicolas Dichtel NLMSG_ALIGN(sizeof(struct rtmsg)) 23168cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_TABLE */ 23178cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_SRC */ 23188cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_DST */ 23198cd3ac9fSNicolas Dichtel ; 23208cd3ac9fSNicolas Dichtel 23218cd3ac9fSNicolas Dichtel if (!unresolved) 23228cd3ac9fSNicolas Dichtel len = len 23238cd3ac9fSNicolas Dichtel + nla_total_size(4) /* RTA_IIF */ 23248cd3ac9fSNicolas Dichtel + nla_total_size(0) /* RTA_MULTIPATH */ 23258cd3ac9fSNicolas Dichtel + maxvif * NLA_ALIGN(sizeof(struct rtnexthop)) 23268cd3ac9fSNicolas Dichtel /* RTA_MFC_STATS */ 23278cd3ac9fSNicolas Dichtel + nla_total_size(sizeof(struct rta_mfc_stats)) 23288cd3ac9fSNicolas Dichtel ; 23298cd3ac9fSNicolas Dichtel 23308cd3ac9fSNicolas Dichtel return len; 23318cd3ac9fSNicolas Dichtel } 23328cd3ac9fSNicolas Dichtel 23338cd3ac9fSNicolas Dichtel static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, 23348cd3ac9fSNicolas Dichtel int cmd) 23358cd3ac9fSNicolas Dichtel { 23368cd3ac9fSNicolas Dichtel struct net *net = read_pnet(&mrt->net); 23378cd3ac9fSNicolas Dichtel struct sk_buff *skb; 23388cd3ac9fSNicolas Dichtel int err = -ENOBUFS; 23398cd3ac9fSNicolas Dichtel 23408cd3ac9fSNicolas Dichtel skb = nlmsg_new(mroute_msgsize(mfc->mfc_parent >= MAXVIFS, mrt->maxvif), 23418cd3ac9fSNicolas Dichtel GFP_ATOMIC); 234251456b29SIan Morris if (!skb) 23438cd3ac9fSNicolas Dichtel goto errout; 23448cd3ac9fSNicolas Dichtel 234565886f43SNicolas Dichtel err = ipmr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0); 23468cd3ac9fSNicolas Dichtel if (err < 0) 23478cd3ac9fSNicolas Dichtel goto errout; 23488cd3ac9fSNicolas Dichtel 23498cd3ac9fSNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE, NULL, GFP_ATOMIC); 23508cd3ac9fSNicolas Dichtel return; 23518cd3ac9fSNicolas Dichtel 23528cd3ac9fSNicolas Dichtel errout: 23538cd3ac9fSNicolas Dichtel kfree_skb(skb); 23548cd3ac9fSNicolas Dichtel if (err < 0) 23558cd3ac9fSNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE, err); 23568cd3ac9fSNicolas Dichtel } 23578cd3ac9fSNicolas Dichtel 2358cb6a4e46SPatrick McHardy static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) 2359cb6a4e46SPatrick McHardy { 2360cb6a4e46SPatrick McHardy struct net *net = sock_net(skb->sk); 2361cb6a4e46SPatrick McHardy struct mr_table *mrt; 2362cb6a4e46SPatrick McHardy struct mfc_cache *mfc; 2363cb6a4e46SPatrick McHardy unsigned int t = 0, s_t; 2364cb6a4e46SPatrick McHardy unsigned int h = 0, s_h; 2365cb6a4e46SPatrick McHardy unsigned int e = 0, s_e; 2366cb6a4e46SPatrick McHardy 2367cb6a4e46SPatrick McHardy s_t = cb->args[0]; 2368cb6a4e46SPatrick McHardy s_h = cb->args[1]; 2369cb6a4e46SPatrick McHardy s_e = cb->args[2]; 2370cb6a4e46SPatrick McHardy 2371a8c9486bSEric Dumazet rcu_read_lock(); 2372cb6a4e46SPatrick McHardy ipmr_for_each_table(mrt, net) { 2373cb6a4e46SPatrick McHardy if (t < s_t) 2374cb6a4e46SPatrick McHardy goto next_table; 2375cb6a4e46SPatrick McHardy if (t > s_t) 2376cb6a4e46SPatrick McHardy s_h = 0; 2377cb6a4e46SPatrick McHardy for (h = s_h; h < MFC_LINES; h++) { 2378a8c9486bSEric Dumazet list_for_each_entry_rcu(mfc, &mrt->mfc_cache_array[h], list) { 2379cb6a4e46SPatrick McHardy if (e < s_e) 2380cb6a4e46SPatrick McHardy goto next_entry; 2381cb6a4e46SPatrick McHardy if (ipmr_fill_mroute(mrt, skb, 238215e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 2383cb6a4e46SPatrick McHardy cb->nlh->nlmsg_seq, 238465886f43SNicolas Dichtel mfc, RTM_NEWROUTE, 238565886f43SNicolas Dichtel NLM_F_MULTI) < 0) 2386cb6a4e46SPatrick McHardy goto done; 2387cb6a4e46SPatrick McHardy next_entry: 2388cb6a4e46SPatrick McHardy e++; 2389cb6a4e46SPatrick McHardy } 2390cb6a4e46SPatrick McHardy e = s_e = 0; 2391cb6a4e46SPatrick McHardy } 23921eb99af5SNicolas Dichtel spin_lock_bh(&mfc_unres_lock); 23931eb99af5SNicolas Dichtel list_for_each_entry(mfc, &mrt->mfc_unres_queue, list) { 23941eb99af5SNicolas Dichtel if (e < s_e) 23951eb99af5SNicolas Dichtel goto next_entry2; 23961eb99af5SNicolas Dichtel if (ipmr_fill_mroute(mrt, skb, 23971eb99af5SNicolas Dichtel NETLINK_CB(cb->skb).portid, 23981eb99af5SNicolas Dichtel cb->nlh->nlmsg_seq, 239965886f43SNicolas Dichtel mfc, RTM_NEWROUTE, 240065886f43SNicolas Dichtel NLM_F_MULTI) < 0) { 24011eb99af5SNicolas Dichtel spin_unlock_bh(&mfc_unres_lock); 24021eb99af5SNicolas Dichtel goto done; 24031eb99af5SNicolas Dichtel } 24041eb99af5SNicolas Dichtel next_entry2: 24051eb99af5SNicolas Dichtel e++; 24061eb99af5SNicolas Dichtel } 24071eb99af5SNicolas Dichtel spin_unlock_bh(&mfc_unres_lock); 24081eb99af5SNicolas Dichtel e = s_e = 0; 2409cb6a4e46SPatrick McHardy s_h = 0; 2410cb6a4e46SPatrick McHardy next_table: 2411cb6a4e46SPatrick McHardy t++; 2412cb6a4e46SPatrick McHardy } 2413cb6a4e46SPatrick McHardy done: 2414a8c9486bSEric Dumazet rcu_read_unlock(); 2415cb6a4e46SPatrick McHardy 2416cb6a4e46SPatrick McHardy cb->args[2] = e; 2417cb6a4e46SPatrick McHardy cb->args[1] = h; 2418cb6a4e46SPatrick McHardy cb->args[0] = t; 2419cb6a4e46SPatrick McHardy 2420cb6a4e46SPatrick McHardy return skb->len; 2421cb6a4e46SPatrick McHardy } 2422cb6a4e46SPatrick McHardy 24231da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 24241da177e4SLinus Torvalds /* 2425a8cb16ddSEric Dumazet * The /proc interfaces to multicast routing : 2426a8cb16ddSEric Dumazet * /proc/net/ip_mr_cache & /proc/net/ip_mr_vif 24271da177e4SLinus Torvalds */ 24281da177e4SLinus Torvalds struct ipmr_vif_iter { 2429f6bb4514SBenjamin Thery struct seq_net_private p; 2430f0ad0860SPatrick McHardy struct mr_table *mrt; 24311da177e4SLinus Torvalds int ct; 24321da177e4SLinus Torvalds }; 24331da177e4SLinus Torvalds 2434f6bb4514SBenjamin Thery static struct vif_device *ipmr_vif_seq_idx(struct net *net, 2435f6bb4514SBenjamin Thery struct ipmr_vif_iter *iter, 24361da177e4SLinus Torvalds loff_t pos) 24371da177e4SLinus Torvalds { 2438f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 24390c12295aSPatrick McHardy 24400c12295aSPatrick McHardy for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) { 24410c12295aSPatrick McHardy if (!VIF_EXISTS(mrt, iter->ct)) 24421da177e4SLinus Torvalds continue; 24431da177e4SLinus Torvalds if (pos-- == 0) 24440c12295aSPatrick McHardy return &mrt->vif_table[iter->ct]; 24451da177e4SLinus Torvalds } 24461da177e4SLinus Torvalds return NULL; 24471da177e4SLinus Torvalds } 24481da177e4SLinus Torvalds 24491da177e4SLinus Torvalds static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos) 2450ba93ef74SStephen Hemminger __acquires(mrt_lock) 24511da177e4SLinus Torvalds { 2452f0ad0860SPatrick McHardy struct ipmr_vif_iter *iter = seq->private; 2453f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2454f0ad0860SPatrick McHardy struct mr_table *mrt; 2455f0ad0860SPatrick McHardy 2456f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 245751456b29SIan Morris if (!mrt) 2458f0ad0860SPatrick McHardy return ERR_PTR(-ENOENT); 2459f0ad0860SPatrick McHardy 2460f0ad0860SPatrick McHardy iter->mrt = mrt; 2461f6bb4514SBenjamin Thery 24621da177e4SLinus Torvalds read_lock(&mrt_lock); 2463f6bb4514SBenjamin Thery return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1) 24641da177e4SLinus Torvalds : SEQ_START_TOKEN; 24651da177e4SLinus Torvalds } 24661da177e4SLinus Torvalds 24671da177e4SLinus Torvalds static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos) 24681da177e4SLinus Torvalds { 24691da177e4SLinus Torvalds struct ipmr_vif_iter *iter = seq->private; 2470f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2471f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 24721da177e4SLinus Torvalds 24731da177e4SLinus Torvalds ++*pos; 24741da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 2475f6bb4514SBenjamin Thery return ipmr_vif_seq_idx(net, iter, 0); 24761da177e4SLinus Torvalds 24770c12295aSPatrick McHardy while (++iter->ct < mrt->maxvif) { 24780c12295aSPatrick McHardy if (!VIF_EXISTS(mrt, iter->ct)) 24791da177e4SLinus Torvalds continue; 24800c12295aSPatrick McHardy return &mrt->vif_table[iter->ct]; 24811da177e4SLinus Torvalds } 24821da177e4SLinus Torvalds return NULL; 24831da177e4SLinus Torvalds } 24841da177e4SLinus Torvalds 24851da177e4SLinus Torvalds static void ipmr_vif_seq_stop(struct seq_file *seq, void *v) 2486ba93ef74SStephen Hemminger __releases(mrt_lock) 24871da177e4SLinus Torvalds { 24881da177e4SLinus Torvalds read_unlock(&mrt_lock); 24891da177e4SLinus Torvalds } 24901da177e4SLinus Torvalds 24911da177e4SLinus Torvalds static int ipmr_vif_seq_show(struct seq_file *seq, void *v) 24921da177e4SLinus Torvalds { 2493f0ad0860SPatrick McHardy struct ipmr_vif_iter *iter = seq->private; 2494f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 2495f6bb4514SBenjamin Thery 24961da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 24971da177e4SLinus Torvalds seq_puts(seq, 24981da177e4SLinus Torvalds "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n"); 24991da177e4SLinus Torvalds } else { 25001da177e4SLinus Torvalds const struct vif_device *vif = v; 25011da177e4SLinus Torvalds const char *name = vif->dev ? vif->dev->name : "none"; 25021da177e4SLinus Torvalds 25031da177e4SLinus Torvalds seq_printf(seq, 25041da177e4SLinus Torvalds "%2Zd %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n", 25050c12295aSPatrick McHardy vif - mrt->vif_table, 25061da177e4SLinus Torvalds name, vif->bytes_in, vif->pkt_in, 25071da177e4SLinus Torvalds vif->bytes_out, vif->pkt_out, 25081da177e4SLinus Torvalds vif->flags, vif->local, vif->remote); 25091da177e4SLinus Torvalds } 25101da177e4SLinus Torvalds return 0; 25111da177e4SLinus Torvalds } 25121da177e4SLinus Torvalds 2513f690808eSStephen Hemminger static const struct seq_operations ipmr_vif_seq_ops = { 25141da177e4SLinus Torvalds .start = ipmr_vif_seq_start, 25151da177e4SLinus Torvalds .next = ipmr_vif_seq_next, 25161da177e4SLinus Torvalds .stop = ipmr_vif_seq_stop, 25171da177e4SLinus Torvalds .show = ipmr_vif_seq_show, 25181da177e4SLinus Torvalds }; 25191da177e4SLinus Torvalds 25201da177e4SLinus Torvalds static int ipmr_vif_open(struct inode *inode, struct file *file) 25211da177e4SLinus Torvalds { 2522f6bb4514SBenjamin Thery return seq_open_net(inode, file, &ipmr_vif_seq_ops, 2523cf7732e4SPavel Emelyanov sizeof(struct ipmr_vif_iter)); 25241da177e4SLinus Torvalds } 25251da177e4SLinus Torvalds 25269a32144eSArjan van de Ven static const struct file_operations ipmr_vif_fops = { 25271da177e4SLinus Torvalds .owner = THIS_MODULE, 25281da177e4SLinus Torvalds .open = ipmr_vif_open, 25291da177e4SLinus Torvalds .read = seq_read, 25301da177e4SLinus Torvalds .llseek = seq_lseek, 2531f6bb4514SBenjamin Thery .release = seq_release_net, 25321da177e4SLinus Torvalds }; 25331da177e4SLinus Torvalds 25341da177e4SLinus Torvalds struct ipmr_mfc_iter { 2535f6bb4514SBenjamin Thery struct seq_net_private p; 2536f0ad0860SPatrick McHardy struct mr_table *mrt; 2537862465f2SPatrick McHardy struct list_head *cache; 25381da177e4SLinus Torvalds int ct; 25391da177e4SLinus Torvalds }; 25401da177e4SLinus Torvalds 25411da177e4SLinus Torvalds 2542f6bb4514SBenjamin Thery static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net, 2543f6bb4514SBenjamin Thery struct ipmr_mfc_iter *it, loff_t pos) 25441da177e4SLinus Torvalds { 2545f0ad0860SPatrick McHardy struct mr_table *mrt = it->mrt; 25461da177e4SLinus Torvalds struct mfc_cache *mfc; 25471da177e4SLinus Torvalds 2548a8c9486bSEric Dumazet rcu_read_lock(); 2549862465f2SPatrick McHardy for (it->ct = 0; it->ct < MFC_LINES; it->ct++) { 25500c12295aSPatrick McHardy it->cache = &mrt->mfc_cache_array[it->ct]; 2551a8c9486bSEric Dumazet list_for_each_entry_rcu(mfc, it->cache, list) 25521da177e4SLinus Torvalds if (pos-- == 0) 25531da177e4SLinus Torvalds return mfc; 2554862465f2SPatrick McHardy } 2555a8c9486bSEric Dumazet rcu_read_unlock(); 25561da177e4SLinus Torvalds 25571da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 25580c12295aSPatrick McHardy it->cache = &mrt->mfc_unres_queue; 2559862465f2SPatrick McHardy list_for_each_entry(mfc, it->cache, list) 2560e258beb2SPatrick McHardy if (pos-- == 0) 25611da177e4SLinus Torvalds return mfc; 25621da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 25631da177e4SLinus Torvalds 25641da177e4SLinus Torvalds it->cache = NULL; 25651da177e4SLinus Torvalds return NULL; 25661da177e4SLinus Torvalds } 25671da177e4SLinus Torvalds 25681da177e4SLinus Torvalds 25691da177e4SLinus Torvalds static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos) 25701da177e4SLinus Torvalds { 25711da177e4SLinus Torvalds struct ipmr_mfc_iter *it = seq->private; 2572f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2573f0ad0860SPatrick McHardy struct mr_table *mrt; 2574f6bb4514SBenjamin Thery 2575f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 257651456b29SIan Morris if (!mrt) 2577f0ad0860SPatrick McHardy return ERR_PTR(-ENOENT); 2578f0ad0860SPatrick McHardy 2579f0ad0860SPatrick McHardy it->mrt = mrt; 25801da177e4SLinus Torvalds it->cache = NULL; 25811da177e4SLinus Torvalds it->ct = 0; 2582f6bb4514SBenjamin Thery return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1) 25831da177e4SLinus Torvalds : SEQ_START_TOKEN; 25841da177e4SLinus Torvalds } 25851da177e4SLinus Torvalds 25861da177e4SLinus Torvalds static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 25871da177e4SLinus Torvalds { 25881da177e4SLinus Torvalds struct mfc_cache *mfc = v; 25891da177e4SLinus Torvalds struct ipmr_mfc_iter *it = seq->private; 2590f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2591f0ad0860SPatrick McHardy struct mr_table *mrt = it->mrt; 25921da177e4SLinus Torvalds 25931da177e4SLinus Torvalds ++*pos; 25941da177e4SLinus Torvalds 25951da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 2596f6bb4514SBenjamin Thery return ipmr_mfc_seq_idx(net, seq->private, 0); 25971da177e4SLinus Torvalds 2598862465f2SPatrick McHardy if (mfc->list.next != it->cache) 2599862465f2SPatrick McHardy return list_entry(mfc->list.next, struct mfc_cache, list); 26001da177e4SLinus Torvalds 26010c12295aSPatrick McHardy if (it->cache == &mrt->mfc_unres_queue) 26021da177e4SLinus Torvalds goto end_of_list; 26031da177e4SLinus Torvalds 26040c12295aSPatrick McHardy BUG_ON(it->cache != &mrt->mfc_cache_array[it->ct]); 26051da177e4SLinus Torvalds 26061da177e4SLinus Torvalds while (++it->ct < MFC_LINES) { 26070c12295aSPatrick McHardy it->cache = &mrt->mfc_cache_array[it->ct]; 2608862465f2SPatrick McHardy if (list_empty(it->cache)) 2609862465f2SPatrick McHardy continue; 2610862465f2SPatrick McHardy return list_first_entry(it->cache, struct mfc_cache, list); 26111da177e4SLinus Torvalds } 26121da177e4SLinus Torvalds 26131da177e4SLinus Torvalds /* exhausted cache_array, show unresolved */ 2614a8c9486bSEric Dumazet rcu_read_unlock(); 26150c12295aSPatrick McHardy it->cache = &mrt->mfc_unres_queue; 26161da177e4SLinus Torvalds it->ct = 0; 26171da177e4SLinus Torvalds 26181da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 2619862465f2SPatrick McHardy if (!list_empty(it->cache)) 2620862465f2SPatrick McHardy return list_first_entry(it->cache, struct mfc_cache, list); 26211da177e4SLinus Torvalds 26221da177e4SLinus Torvalds end_of_list: 26231da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 26241da177e4SLinus Torvalds it->cache = NULL; 26251da177e4SLinus Torvalds 26261da177e4SLinus Torvalds return NULL; 26271da177e4SLinus Torvalds } 26281da177e4SLinus Torvalds 26291da177e4SLinus Torvalds static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) 26301da177e4SLinus Torvalds { 26311da177e4SLinus Torvalds struct ipmr_mfc_iter *it = seq->private; 2632f0ad0860SPatrick McHardy struct mr_table *mrt = it->mrt; 26331da177e4SLinus Torvalds 26340c12295aSPatrick McHardy if (it->cache == &mrt->mfc_unres_queue) 26351da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 26360c12295aSPatrick McHardy else if (it->cache == &mrt->mfc_cache_array[it->ct]) 2637a8c9486bSEric Dumazet rcu_read_unlock(); 26381da177e4SLinus Torvalds } 26391da177e4SLinus Torvalds 26401da177e4SLinus Torvalds static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) 26411da177e4SLinus Torvalds { 26421da177e4SLinus Torvalds int n; 26431da177e4SLinus Torvalds 26441da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 26451da177e4SLinus Torvalds seq_puts(seq, 26461da177e4SLinus Torvalds "Group Origin Iif Pkts Bytes Wrong Oifs\n"); 26471da177e4SLinus Torvalds } else { 26481da177e4SLinus Torvalds const struct mfc_cache *mfc = v; 26491da177e4SLinus Torvalds const struct ipmr_mfc_iter *it = seq->private; 2650f0ad0860SPatrick McHardy const struct mr_table *mrt = it->mrt; 26511da177e4SLinus Torvalds 26520eae88f3SEric Dumazet seq_printf(seq, "%08X %08X %-3hd", 26530eae88f3SEric Dumazet (__force u32) mfc->mfc_mcastgrp, 26540eae88f3SEric Dumazet (__force u32) mfc->mfc_origin, 26551ea472e2SBenjamin Thery mfc->mfc_parent); 26561ea472e2SBenjamin Thery 26570c12295aSPatrick McHardy if (it->cache != &mrt->mfc_unres_queue) { 26581ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 26591da177e4SLinus Torvalds mfc->mfc_un.res.pkt, 26601da177e4SLinus Torvalds mfc->mfc_un.res.bytes, 26611da177e4SLinus Torvalds mfc->mfc_un.res.wrong_if); 26621da177e4SLinus Torvalds for (n = mfc->mfc_un.res.minvif; 26631da177e4SLinus Torvalds n < mfc->mfc_un.res.maxvif; n++) { 26640c12295aSPatrick McHardy if (VIF_EXISTS(mrt, n) && 2665cf958ae3SBenjamin Thery mfc->mfc_un.res.ttls[n] < 255) 26661da177e4SLinus Torvalds seq_printf(seq, 26671da177e4SLinus Torvalds " %2d:%-3d", 26681da177e4SLinus Torvalds n, mfc->mfc_un.res.ttls[n]); 26691da177e4SLinus Torvalds } 26701ea472e2SBenjamin Thery } else { 26711ea472e2SBenjamin Thery /* unresolved mfc_caches don't contain 26721ea472e2SBenjamin Thery * pkt, bytes and wrong_if values 26731ea472e2SBenjamin Thery */ 26741ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul); 26751da177e4SLinus Torvalds } 26761da177e4SLinus Torvalds seq_putc(seq, '\n'); 26771da177e4SLinus Torvalds } 26781da177e4SLinus Torvalds return 0; 26791da177e4SLinus Torvalds } 26801da177e4SLinus Torvalds 2681f690808eSStephen Hemminger static const struct seq_operations ipmr_mfc_seq_ops = { 26821da177e4SLinus Torvalds .start = ipmr_mfc_seq_start, 26831da177e4SLinus Torvalds .next = ipmr_mfc_seq_next, 26841da177e4SLinus Torvalds .stop = ipmr_mfc_seq_stop, 26851da177e4SLinus Torvalds .show = ipmr_mfc_seq_show, 26861da177e4SLinus Torvalds }; 26871da177e4SLinus Torvalds 26881da177e4SLinus Torvalds static int ipmr_mfc_open(struct inode *inode, struct file *file) 26891da177e4SLinus Torvalds { 2690f6bb4514SBenjamin Thery return seq_open_net(inode, file, &ipmr_mfc_seq_ops, 2691cf7732e4SPavel Emelyanov sizeof(struct ipmr_mfc_iter)); 26921da177e4SLinus Torvalds } 26931da177e4SLinus Torvalds 26949a32144eSArjan van de Ven static const struct file_operations ipmr_mfc_fops = { 26951da177e4SLinus Torvalds .owner = THIS_MODULE, 26961da177e4SLinus Torvalds .open = ipmr_mfc_open, 26971da177e4SLinus Torvalds .read = seq_read, 26981da177e4SLinus Torvalds .llseek = seq_lseek, 2699f6bb4514SBenjamin Thery .release = seq_release_net, 27001da177e4SLinus Torvalds }; 27011da177e4SLinus Torvalds #endif 27021da177e4SLinus Torvalds 27031da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V2 270432613090SAlexey Dobriyan static const struct net_protocol pim_protocol = { 27051da177e4SLinus Torvalds .handler = pim_rcv, 2706403dbb97STom Goff .netns_ok = 1, 27071da177e4SLinus Torvalds }; 27081da177e4SLinus Torvalds #endif 27091da177e4SLinus Torvalds 27101da177e4SLinus Torvalds 27111da177e4SLinus Torvalds /* 27121da177e4SLinus Torvalds * Setup for IP multicast routing 27131da177e4SLinus Torvalds */ 2714cf958ae3SBenjamin Thery static int __net_init ipmr_net_init(struct net *net) 2715cf958ae3SBenjamin Thery { 2716f0ad0860SPatrick McHardy int err; 2717cf958ae3SBenjamin Thery 2718f0ad0860SPatrick McHardy err = ipmr_rules_init(net); 2719f0ad0860SPatrick McHardy if (err < 0) 2720cf958ae3SBenjamin Thery goto fail; 2721f6bb4514SBenjamin Thery 2722f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 2723f6bb4514SBenjamin Thery err = -ENOMEM; 2724d4beaa66SGao feng if (!proc_create("ip_mr_vif", 0, net->proc_net, &ipmr_vif_fops)) 2725f6bb4514SBenjamin Thery goto proc_vif_fail; 2726d4beaa66SGao feng if (!proc_create("ip_mr_cache", 0, net->proc_net, &ipmr_mfc_fops)) 2727f6bb4514SBenjamin Thery goto proc_cache_fail; 2728f6bb4514SBenjamin Thery #endif 27292bb8b26cSBenjamin Thery return 0; 27302bb8b26cSBenjamin Thery 2731f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 2732f6bb4514SBenjamin Thery proc_cache_fail: 2733ece31ffdSGao feng remove_proc_entry("ip_mr_vif", net->proc_net); 2734f6bb4514SBenjamin Thery proc_vif_fail: 2735f0ad0860SPatrick McHardy ipmr_rules_exit(net); 2736f6bb4514SBenjamin Thery #endif 2737cf958ae3SBenjamin Thery fail: 2738cf958ae3SBenjamin Thery return err; 2739cf958ae3SBenjamin Thery } 2740cf958ae3SBenjamin Thery 2741cf958ae3SBenjamin Thery static void __net_exit ipmr_net_exit(struct net *net) 2742cf958ae3SBenjamin Thery { 2743f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 2744ece31ffdSGao feng remove_proc_entry("ip_mr_cache", net->proc_net); 2745ece31ffdSGao feng remove_proc_entry("ip_mr_vif", net->proc_net); 2746f6bb4514SBenjamin Thery #endif 2747f0ad0860SPatrick McHardy ipmr_rules_exit(net); 2748cf958ae3SBenjamin Thery } 2749cf958ae3SBenjamin Thery 2750cf958ae3SBenjamin Thery static struct pernet_operations ipmr_net_ops = { 2751cf958ae3SBenjamin Thery .init = ipmr_net_init, 2752cf958ae3SBenjamin Thery .exit = ipmr_net_exit, 2753cf958ae3SBenjamin Thery }; 27541da177e4SLinus Torvalds 275503d2f897SWang Chen int __init ip_mr_init(void) 27561da177e4SLinus Torvalds { 275703d2f897SWang Chen int err; 275803d2f897SWang Chen 27591da177e4SLinus Torvalds mrt_cachep = kmem_cache_create("ip_mrt_cache", 27601da177e4SLinus Torvalds sizeof(struct mfc_cache), 2761e5d679f3SAlexey Dobriyan 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, 276220c2df83SPaul Mundt NULL); 276303d2f897SWang Chen if (!mrt_cachep) 276403d2f897SWang Chen return -ENOMEM; 276503d2f897SWang Chen 2766cf958ae3SBenjamin Thery err = register_pernet_subsys(&ipmr_net_ops); 2767cf958ae3SBenjamin Thery if (err) 2768cf958ae3SBenjamin Thery goto reg_pernet_fail; 2769cf958ae3SBenjamin Thery 277003d2f897SWang Chen err = register_netdevice_notifier(&ip_mr_notifier); 277103d2f897SWang Chen if (err) 277203d2f897SWang Chen goto reg_notif_fail; 2773403dbb97STom Goff #ifdef CONFIG_IP_PIMSM_V2 2774403dbb97STom Goff if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) { 2775058bd4d2SJoe Perches pr_err("%s: can't add PIM protocol\n", __func__); 2776403dbb97STom Goff err = -EAGAIN; 2777403dbb97STom Goff goto add_proto_fail; 2778403dbb97STom Goff } 2779403dbb97STom Goff #endif 2780c7ac8679SGreg Rose rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, 2781c7ac8679SGreg Rose NULL, ipmr_rtm_dumproute, NULL); 278203d2f897SWang Chen return 0; 2783f6bb4514SBenjamin Thery 2784403dbb97STom Goff #ifdef CONFIG_IP_PIMSM_V2 2785403dbb97STom Goff add_proto_fail: 2786403dbb97STom Goff unregister_netdevice_notifier(&ip_mr_notifier); 2787403dbb97STom Goff #endif 2788c3e38896SBenjamin Thery reg_notif_fail: 2789cf958ae3SBenjamin Thery unregister_pernet_subsys(&ipmr_net_ops); 2790cf958ae3SBenjamin Thery reg_pernet_fail: 2791c3e38896SBenjamin Thery kmem_cache_destroy(mrt_cachep); 279203d2f897SWang Chen return err; 27931da177e4SLinus Torvalds } 2794