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> 641da177e4SLinus Torvalds #include <net/ipip.h> 651da177e4SLinus Torvalds #include <net/checksum.h> 66dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 67f0ad0860SPatrick McHardy #include <net/fib_rules.h> 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) 701da177e4SLinus Torvalds #define CONFIG_IP_PIMSM 1 711da177e4SLinus Torvalds #endif 721da177e4SLinus Torvalds 730c12295aSPatrick McHardy struct mr_table { 74f0ad0860SPatrick McHardy struct list_head list; 758de53dfbSPatrick McHardy #ifdef CONFIG_NET_NS 768de53dfbSPatrick McHardy struct net *net; 778de53dfbSPatrick McHardy #endif 78f0ad0860SPatrick McHardy u32 id; 794c968709SEric Dumazet struct sock __rcu *mroute_sk; 800c12295aSPatrick McHardy struct timer_list ipmr_expire_timer; 810c12295aSPatrick McHardy struct list_head mfc_unres_queue; 820c12295aSPatrick McHardy struct list_head mfc_cache_array[MFC_LINES]; 830c12295aSPatrick McHardy struct vif_device vif_table[MAXVIFS]; 840c12295aSPatrick McHardy int maxvif; 850c12295aSPatrick McHardy atomic_t cache_resolve_queue_len; 860c12295aSPatrick McHardy int mroute_do_assert; 870c12295aSPatrick McHardy int mroute_do_pim; 880c12295aSPatrick McHardy #if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) 890c12295aSPatrick McHardy int mroute_reg_vif_num; 900c12295aSPatrick McHardy #endif 910c12295aSPatrick McHardy }; 920c12295aSPatrick McHardy 93f0ad0860SPatrick McHardy struct ipmr_rule { 94f0ad0860SPatrick McHardy struct fib_rule common; 95f0ad0860SPatrick McHardy }; 96f0ad0860SPatrick McHardy 97f0ad0860SPatrick McHardy struct ipmr_result { 98f0ad0860SPatrick McHardy struct mr_table *mrt; 99f0ad0860SPatrick McHardy }; 100f0ad0860SPatrick McHardy 1011da177e4SLinus Torvalds /* Big lock, protecting vif table, mrt cache and mroute socket state. 102a8cb16ddSEric Dumazet * Note that the changes are semaphored via rtnl_lock. 1031da177e4SLinus Torvalds */ 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds static DEFINE_RWLOCK(mrt_lock); 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds /* 1081da177e4SLinus Torvalds * Multicast router control variables 1091da177e4SLinus Torvalds */ 1101da177e4SLinus Torvalds 1110c12295aSPatrick McHardy #define VIF_EXISTS(_mrt, _idx) ((_mrt)->vif_table[_idx].dev != NULL) 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds /* Special spinlock for queue of unresolved entries */ 1141da177e4SLinus Torvalds static DEFINE_SPINLOCK(mfc_unres_lock); 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds /* We return to original Alan's scheme. Hash table of resolved 117a8cb16ddSEric Dumazet * entries is changed only in process context and protected 118a8cb16ddSEric Dumazet * with weak lock mrt_lock. Queue of unresolved entries is protected 119a8cb16ddSEric Dumazet * with strong spinlock mfc_unres_lock. 120a8cb16ddSEric Dumazet * 121a8cb16ddSEric Dumazet * In this case data path is free of exclusive locks at all. 1221da177e4SLinus Torvalds */ 1231da177e4SLinus Torvalds 124e18b890bSChristoph Lameter static struct kmem_cache *mrt_cachep __read_mostly; 1251da177e4SLinus Torvalds 126f0ad0860SPatrick McHardy static struct mr_table *ipmr_new_table(struct net *net, u32 id); 127acbb219dSFrancesco Ruggeri static void ipmr_free_table(struct mr_table *mrt); 128acbb219dSFrancesco Ruggeri 1290c12295aSPatrick McHardy static int ip_mr_forward(struct net *net, struct mr_table *mrt, 1300c12295aSPatrick McHardy struct sk_buff *skb, struct mfc_cache *cache, 1310c12295aSPatrick McHardy int local); 1320c12295aSPatrick McHardy static int ipmr_cache_report(struct mr_table *mrt, 1334feb88e5SBenjamin Thery struct sk_buff *pkt, vifi_t vifi, int assert); 134cb6a4e46SPatrick McHardy static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 135d658f8a0SPatrick McHardy struct mfc_cache *c, struct rtmsg *rtm); 136acbb219dSFrancesco Ruggeri static void mroute_clean_tables(struct mr_table *mrt); 137f0ad0860SPatrick McHardy static void ipmr_expire_process(unsigned long arg); 1381da177e4SLinus Torvalds 139f0ad0860SPatrick McHardy #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 140f0ad0860SPatrick McHardy #define ipmr_for_each_table(mrt, net) \ 141f0ad0860SPatrick McHardy list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list) 142f0ad0860SPatrick McHardy 143f0ad0860SPatrick McHardy static struct mr_table *ipmr_get_table(struct net *net, u32 id) 144f0ad0860SPatrick McHardy { 145f0ad0860SPatrick McHardy struct mr_table *mrt; 146f0ad0860SPatrick McHardy 147f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 148f0ad0860SPatrick McHardy if (mrt->id == id) 149f0ad0860SPatrick McHardy return mrt; 150f0ad0860SPatrick McHardy } 151f0ad0860SPatrick McHardy return NULL; 152f0ad0860SPatrick McHardy } 153f0ad0860SPatrick McHardy 154da91981bSDavid S. Miller static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 155f0ad0860SPatrick McHardy struct mr_table **mrt) 156f0ad0860SPatrick McHardy { 157f0ad0860SPatrick McHardy struct ipmr_result res; 158f0ad0860SPatrick McHardy struct fib_lookup_arg arg = { .result = &res, }; 159f0ad0860SPatrick McHardy int err; 160f0ad0860SPatrick McHardy 161da91981bSDavid S. Miller err = fib_rules_lookup(net->ipv4.mr_rules_ops, 162da91981bSDavid S. Miller flowi4_to_flowi(flp4), 0, &arg); 163f0ad0860SPatrick McHardy if (err < 0) 164f0ad0860SPatrick McHardy return err; 165f0ad0860SPatrick McHardy *mrt = res.mrt; 166f0ad0860SPatrick McHardy return 0; 167f0ad0860SPatrick McHardy } 168f0ad0860SPatrick McHardy 169f0ad0860SPatrick McHardy static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp, 170f0ad0860SPatrick McHardy int flags, struct fib_lookup_arg *arg) 171f0ad0860SPatrick McHardy { 172f0ad0860SPatrick McHardy struct ipmr_result *res = arg->result; 173f0ad0860SPatrick McHardy struct mr_table *mrt; 174f0ad0860SPatrick McHardy 175f0ad0860SPatrick McHardy switch (rule->action) { 176f0ad0860SPatrick McHardy case FR_ACT_TO_TBL: 177f0ad0860SPatrick McHardy break; 178f0ad0860SPatrick McHardy case FR_ACT_UNREACHABLE: 179f0ad0860SPatrick McHardy return -ENETUNREACH; 180f0ad0860SPatrick McHardy case FR_ACT_PROHIBIT: 181f0ad0860SPatrick McHardy return -EACCES; 182f0ad0860SPatrick McHardy case FR_ACT_BLACKHOLE: 183f0ad0860SPatrick McHardy default: 184f0ad0860SPatrick McHardy return -EINVAL; 185f0ad0860SPatrick McHardy } 186f0ad0860SPatrick McHardy 187f0ad0860SPatrick McHardy mrt = ipmr_get_table(rule->fr_net, rule->table); 188f0ad0860SPatrick McHardy if (mrt == NULL) 189f0ad0860SPatrick McHardy return -EAGAIN; 190f0ad0860SPatrick McHardy res->mrt = mrt; 191f0ad0860SPatrick McHardy return 0; 192f0ad0860SPatrick McHardy } 193f0ad0860SPatrick McHardy 194f0ad0860SPatrick McHardy static int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) 195f0ad0860SPatrick McHardy { 196f0ad0860SPatrick McHardy return 1; 197f0ad0860SPatrick McHardy } 198f0ad0860SPatrick McHardy 199f0ad0860SPatrick McHardy static const struct nla_policy ipmr_rule_policy[FRA_MAX + 1] = { 200f0ad0860SPatrick McHardy FRA_GENERIC_POLICY, 201f0ad0860SPatrick McHardy }; 202f0ad0860SPatrick McHardy 203f0ad0860SPatrick McHardy static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb, 204f0ad0860SPatrick McHardy struct fib_rule_hdr *frh, struct nlattr **tb) 205f0ad0860SPatrick McHardy { 206f0ad0860SPatrick McHardy return 0; 207f0ad0860SPatrick McHardy } 208f0ad0860SPatrick McHardy 209f0ad0860SPatrick McHardy static int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 210f0ad0860SPatrick McHardy struct nlattr **tb) 211f0ad0860SPatrick McHardy { 212f0ad0860SPatrick McHardy return 1; 213f0ad0860SPatrick McHardy } 214f0ad0860SPatrick McHardy 215f0ad0860SPatrick McHardy static int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb, 216f0ad0860SPatrick McHardy struct fib_rule_hdr *frh) 217f0ad0860SPatrick McHardy { 218f0ad0860SPatrick McHardy frh->dst_len = 0; 219f0ad0860SPatrick McHardy frh->src_len = 0; 220f0ad0860SPatrick McHardy frh->tos = 0; 221f0ad0860SPatrick McHardy return 0; 222f0ad0860SPatrick McHardy } 223f0ad0860SPatrick McHardy 22404a6f82cSAndi Kleen static const struct fib_rules_ops __net_initconst ipmr_rules_ops_template = { 22525239ceeSPatrick McHardy .family = RTNL_FAMILY_IPMR, 226f0ad0860SPatrick McHardy .rule_size = sizeof(struct ipmr_rule), 227f0ad0860SPatrick McHardy .addr_size = sizeof(u32), 228f0ad0860SPatrick McHardy .action = ipmr_rule_action, 229f0ad0860SPatrick McHardy .match = ipmr_rule_match, 230f0ad0860SPatrick McHardy .configure = ipmr_rule_configure, 231f0ad0860SPatrick McHardy .compare = ipmr_rule_compare, 232f0ad0860SPatrick McHardy .default_pref = fib_default_rule_pref, 233f0ad0860SPatrick McHardy .fill = ipmr_rule_fill, 234f0ad0860SPatrick McHardy .nlgroup = RTNLGRP_IPV4_RULE, 235f0ad0860SPatrick McHardy .policy = ipmr_rule_policy, 236f0ad0860SPatrick McHardy .owner = THIS_MODULE, 237f0ad0860SPatrick McHardy }; 238f0ad0860SPatrick McHardy 239f0ad0860SPatrick McHardy static int __net_init ipmr_rules_init(struct net *net) 240f0ad0860SPatrick McHardy { 241f0ad0860SPatrick McHardy struct fib_rules_ops *ops; 242f0ad0860SPatrick McHardy struct mr_table *mrt; 243f0ad0860SPatrick McHardy int err; 244f0ad0860SPatrick McHardy 245f0ad0860SPatrick McHardy ops = fib_rules_register(&ipmr_rules_ops_template, net); 246f0ad0860SPatrick McHardy if (IS_ERR(ops)) 247f0ad0860SPatrick McHardy return PTR_ERR(ops); 248f0ad0860SPatrick McHardy 249f0ad0860SPatrick McHardy INIT_LIST_HEAD(&net->ipv4.mr_tables); 250f0ad0860SPatrick McHardy 251f0ad0860SPatrick McHardy mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 252f0ad0860SPatrick McHardy if (mrt == NULL) { 253f0ad0860SPatrick McHardy err = -ENOMEM; 254f0ad0860SPatrick McHardy goto err1; 255f0ad0860SPatrick McHardy } 256f0ad0860SPatrick McHardy 257f0ad0860SPatrick McHardy err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0); 258f0ad0860SPatrick McHardy if (err < 0) 259f0ad0860SPatrick McHardy goto err2; 260f0ad0860SPatrick McHardy 261f0ad0860SPatrick McHardy net->ipv4.mr_rules_ops = ops; 262f0ad0860SPatrick McHardy return 0; 263f0ad0860SPatrick McHardy 264f0ad0860SPatrick McHardy err2: 265f0ad0860SPatrick McHardy kfree(mrt); 266f0ad0860SPatrick McHardy err1: 267f0ad0860SPatrick McHardy fib_rules_unregister(ops); 268f0ad0860SPatrick McHardy return err; 269f0ad0860SPatrick McHardy } 270f0ad0860SPatrick McHardy 271f0ad0860SPatrick McHardy static void __net_exit ipmr_rules_exit(struct net *net) 272f0ad0860SPatrick McHardy { 273f0ad0860SPatrick McHardy struct mr_table *mrt, *next; 274f0ad0860SPatrick McHardy 275035320d5SEric Dumazet list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) { 276035320d5SEric Dumazet list_del(&mrt->list); 277acbb219dSFrancesco Ruggeri ipmr_free_table(mrt); 278035320d5SEric Dumazet } 279f0ad0860SPatrick McHardy fib_rules_unregister(net->ipv4.mr_rules_ops); 280f0ad0860SPatrick McHardy } 281f0ad0860SPatrick McHardy #else 282f0ad0860SPatrick McHardy #define ipmr_for_each_table(mrt, net) \ 283f0ad0860SPatrick McHardy for (mrt = net->ipv4.mrt; mrt; mrt = NULL) 284f0ad0860SPatrick McHardy 285f0ad0860SPatrick McHardy static struct mr_table *ipmr_get_table(struct net *net, u32 id) 286f0ad0860SPatrick McHardy { 287f0ad0860SPatrick McHardy return net->ipv4.mrt; 288f0ad0860SPatrick McHardy } 289f0ad0860SPatrick McHardy 290da91981bSDavid S. Miller static int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 291f0ad0860SPatrick McHardy struct mr_table **mrt) 292f0ad0860SPatrick McHardy { 293f0ad0860SPatrick McHardy *mrt = net->ipv4.mrt; 294f0ad0860SPatrick McHardy return 0; 295f0ad0860SPatrick McHardy } 296f0ad0860SPatrick McHardy 297f0ad0860SPatrick McHardy static int __net_init ipmr_rules_init(struct net *net) 298f0ad0860SPatrick McHardy { 299f0ad0860SPatrick McHardy net->ipv4.mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 300f0ad0860SPatrick McHardy return net->ipv4.mrt ? 0 : -ENOMEM; 301f0ad0860SPatrick McHardy } 302f0ad0860SPatrick McHardy 303f0ad0860SPatrick McHardy static void __net_exit ipmr_rules_exit(struct net *net) 304f0ad0860SPatrick McHardy { 305acbb219dSFrancesco Ruggeri ipmr_free_table(net->ipv4.mrt); 306f0ad0860SPatrick McHardy } 307f0ad0860SPatrick McHardy #endif 308f0ad0860SPatrick McHardy 309f0ad0860SPatrick McHardy static struct mr_table *ipmr_new_table(struct net *net, u32 id) 310f0ad0860SPatrick McHardy { 311f0ad0860SPatrick McHardy struct mr_table *mrt; 312f0ad0860SPatrick McHardy unsigned int i; 313f0ad0860SPatrick McHardy 314f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, id); 315f0ad0860SPatrick McHardy if (mrt != NULL) 316f0ad0860SPatrick McHardy return mrt; 317f0ad0860SPatrick McHardy 318f0ad0860SPatrick McHardy mrt = kzalloc(sizeof(*mrt), GFP_KERNEL); 319f0ad0860SPatrick McHardy if (mrt == NULL) 320f0ad0860SPatrick McHardy return NULL; 3218de53dfbSPatrick McHardy write_pnet(&mrt->net, net); 322f0ad0860SPatrick McHardy mrt->id = id; 323f0ad0860SPatrick McHardy 324f0ad0860SPatrick McHardy /* Forwarding cache */ 325f0ad0860SPatrick McHardy for (i = 0; i < MFC_LINES; i++) 326f0ad0860SPatrick McHardy INIT_LIST_HEAD(&mrt->mfc_cache_array[i]); 327f0ad0860SPatrick McHardy 328f0ad0860SPatrick McHardy INIT_LIST_HEAD(&mrt->mfc_unres_queue); 329f0ad0860SPatrick McHardy 330f0ad0860SPatrick McHardy setup_timer(&mrt->ipmr_expire_timer, ipmr_expire_process, 331f0ad0860SPatrick McHardy (unsigned long)mrt); 332f0ad0860SPatrick McHardy 333f0ad0860SPatrick McHardy #ifdef CONFIG_IP_PIMSM 334f0ad0860SPatrick McHardy mrt->mroute_reg_vif_num = -1; 335f0ad0860SPatrick McHardy #endif 336f0ad0860SPatrick McHardy #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 337f0ad0860SPatrick McHardy list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables); 338f0ad0860SPatrick McHardy #endif 339f0ad0860SPatrick McHardy return mrt; 340f0ad0860SPatrick McHardy } 3411da177e4SLinus Torvalds 342acbb219dSFrancesco Ruggeri static void ipmr_free_table(struct mr_table *mrt) 343acbb219dSFrancesco Ruggeri { 344acbb219dSFrancesco Ruggeri del_timer_sync(&mrt->ipmr_expire_timer); 345acbb219dSFrancesco Ruggeri mroute_clean_tables(mrt); 346acbb219dSFrancesco Ruggeri kfree(mrt); 347acbb219dSFrancesco Ruggeri } 348acbb219dSFrancesco Ruggeri 3491da177e4SLinus Torvalds /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */ 3501da177e4SLinus Torvalds 351d607032dSWang Chen static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v) 352d607032dSWang Chen { 3534feb88e5SBenjamin Thery struct net *net = dev_net(dev); 3544feb88e5SBenjamin Thery 355d607032dSWang Chen dev_close(dev); 356d607032dSWang Chen 3574feb88e5SBenjamin Thery dev = __dev_get_by_name(net, "tunl0"); 358d607032dSWang Chen if (dev) { 3595bc3eb7eSStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 360d607032dSWang Chen struct ifreq ifr; 361d607032dSWang Chen struct ip_tunnel_parm p; 362d607032dSWang Chen 363d607032dSWang Chen memset(&p, 0, sizeof(p)); 364d607032dSWang Chen p.iph.daddr = v->vifc_rmt_addr.s_addr; 365d607032dSWang Chen p.iph.saddr = v->vifc_lcl_addr.s_addr; 366d607032dSWang Chen p.iph.version = 4; 367d607032dSWang Chen p.iph.ihl = 5; 368d607032dSWang Chen p.iph.protocol = IPPROTO_IPIP; 369d607032dSWang Chen sprintf(p.name, "dvmrp%d", v->vifc_vifi); 370d607032dSWang Chen ifr.ifr_ifru.ifru_data = (__force void __user *)&p; 371d607032dSWang Chen 3725bc3eb7eSStephen Hemminger if (ops->ndo_do_ioctl) { 3735bc3eb7eSStephen Hemminger mm_segment_t oldfs = get_fs(); 3745bc3eb7eSStephen Hemminger 3755bc3eb7eSStephen Hemminger set_fs(KERNEL_DS); 3765bc3eb7eSStephen Hemminger ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL); 377d607032dSWang Chen set_fs(oldfs); 378d607032dSWang Chen } 379d607032dSWang Chen } 3805bc3eb7eSStephen Hemminger } 381d607032dSWang Chen 3821da177e4SLinus Torvalds static 3834feb88e5SBenjamin Thery struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v) 3841da177e4SLinus Torvalds { 3851da177e4SLinus Torvalds struct net_device *dev; 3861da177e4SLinus Torvalds 3874feb88e5SBenjamin Thery dev = __dev_get_by_name(net, "tunl0"); 3881da177e4SLinus Torvalds 3891da177e4SLinus Torvalds if (dev) { 3905bc3eb7eSStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 3911da177e4SLinus Torvalds int err; 3921da177e4SLinus Torvalds struct ifreq ifr; 3931da177e4SLinus Torvalds struct ip_tunnel_parm p; 3941da177e4SLinus Torvalds struct in_device *in_dev; 3951da177e4SLinus Torvalds 3961da177e4SLinus Torvalds memset(&p, 0, sizeof(p)); 3971da177e4SLinus Torvalds p.iph.daddr = v->vifc_rmt_addr.s_addr; 3981da177e4SLinus Torvalds p.iph.saddr = v->vifc_lcl_addr.s_addr; 3991da177e4SLinus Torvalds p.iph.version = 4; 4001da177e4SLinus Torvalds p.iph.ihl = 5; 4011da177e4SLinus Torvalds p.iph.protocol = IPPROTO_IPIP; 4021da177e4SLinus Torvalds sprintf(p.name, "dvmrp%d", v->vifc_vifi); 403ba93ef74SStephen Hemminger ifr.ifr_ifru.ifru_data = (__force void __user *)&p; 4041da177e4SLinus Torvalds 4055bc3eb7eSStephen Hemminger if (ops->ndo_do_ioctl) { 4065bc3eb7eSStephen Hemminger mm_segment_t oldfs = get_fs(); 4075bc3eb7eSStephen Hemminger 4085bc3eb7eSStephen Hemminger set_fs(KERNEL_DS); 4095bc3eb7eSStephen Hemminger err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL); 4101da177e4SLinus Torvalds set_fs(oldfs); 411a8cb16ddSEric Dumazet } else { 4125bc3eb7eSStephen Hemminger err = -EOPNOTSUPP; 413a8cb16ddSEric Dumazet } 4141da177e4SLinus Torvalds dev = NULL; 4151da177e4SLinus Torvalds 4164feb88e5SBenjamin Thery if (err == 0 && 4174feb88e5SBenjamin Thery (dev = __dev_get_by_name(net, p.name)) != NULL) { 4181da177e4SLinus Torvalds dev->flags |= IFF_MULTICAST; 4191da177e4SLinus Torvalds 420e5ed6399SHerbert Xu in_dev = __in_dev_get_rtnl(dev); 42171e27da9SHerbert Xu if (in_dev == NULL) 4221da177e4SLinus Torvalds goto failure; 42371e27da9SHerbert Xu 42471e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 42571e27da9SHerbert Xu IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; 4261da177e4SLinus Torvalds 4271da177e4SLinus Torvalds if (dev_open(dev)) 4281da177e4SLinus Torvalds goto failure; 4297dc00c82SWang Chen dev_hold(dev); 4301da177e4SLinus Torvalds } 4311da177e4SLinus Torvalds } 4321da177e4SLinus Torvalds return dev; 4331da177e4SLinus Torvalds 4341da177e4SLinus Torvalds failure: 4351da177e4SLinus Torvalds /* allow the register to be completed before unregistering. */ 4361da177e4SLinus Torvalds rtnl_unlock(); 4371da177e4SLinus Torvalds rtnl_lock(); 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds unregister_netdevice(dev); 4401da177e4SLinus Torvalds return NULL; 4411da177e4SLinus Torvalds } 4421da177e4SLinus Torvalds 4431da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 4441da177e4SLinus Torvalds 4456fef4c0cSStephen Hemminger static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) 4461da177e4SLinus Torvalds { 4474feb88e5SBenjamin Thery struct net *net = dev_net(dev); 448f0ad0860SPatrick McHardy struct mr_table *mrt; 449da91981bSDavid S. Miller struct flowi4 fl4 = { 450da91981bSDavid S. Miller .flowi4_oif = dev->ifindex, 451da91981bSDavid S. Miller .flowi4_iif = skb->skb_iif, 452da91981bSDavid S. Miller .flowi4_mark = skb->mark, 453f0ad0860SPatrick McHardy }; 454f0ad0860SPatrick McHardy int err; 455f0ad0860SPatrick McHardy 456da91981bSDavid S. Miller err = ipmr_fib_lookup(net, &fl4, &mrt); 457e40dbc51SBen Greear if (err < 0) { 458e40dbc51SBen Greear kfree_skb(skb); 459f0ad0860SPatrick McHardy return err; 460e40dbc51SBen Greear } 4614feb88e5SBenjamin Thery 4621da177e4SLinus Torvalds read_lock(&mrt_lock); 463cf3677aeSPavel Emelyanov dev->stats.tx_bytes += skb->len; 464cf3677aeSPavel Emelyanov dev->stats.tx_packets++; 4650c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, IGMPMSG_WHOLEPKT); 4661da177e4SLinus Torvalds read_unlock(&mrt_lock); 4671da177e4SLinus Torvalds kfree_skb(skb); 4686ed10654SPatrick McHardy return NETDEV_TX_OK; 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds 471007c3838SStephen Hemminger static const struct net_device_ops reg_vif_netdev_ops = { 472007c3838SStephen Hemminger .ndo_start_xmit = reg_vif_xmit, 473007c3838SStephen Hemminger }; 474007c3838SStephen Hemminger 4751da177e4SLinus Torvalds static void reg_vif_setup(struct net_device *dev) 4761da177e4SLinus Torvalds { 4771da177e4SLinus Torvalds dev->type = ARPHRD_PIMREG; 47846f25dffSKris Katterjohn dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8; 4791da177e4SLinus Torvalds dev->flags = IFF_NOARP; 480007c3838SStephen Hemminger dev->netdev_ops = ®_vif_netdev_ops, 4811da177e4SLinus Torvalds dev->destructor = free_netdev; 482403dbb97STom Goff dev->features |= NETIF_F_NETNS_LOCAL; 4831da177e4SLinus Torvalds } 4841da177e4SLinus Torvalds 485f0ad0860SPatrick McHardy static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) 4861da177e4SLinus Torvalds { 4871da177e4SLinus Torvalds struct net_device *dev; 4881da177e4SLinus Torvalds struct in_device *in_dev; 489f0ad0860SPatrick McHardy char name[IFNAMSIZ]; 4901da177e4SLinus Torvalds 491f0ad0860SPatrick McHardy if (mrt->id == RT_TABLE_DEFAULT) 492f0ad0860SPatrick McHardy sprintf(name, "pimreg"); 493f0ad0860SPatrick McHardy else 494f0ad0860SPatrick McHardy sprintf(name, "pimreg%u", mrt->id); 495f0ad0860SPatrick McHardy 496f0ad0860SPatrick McHardy dev = alloc_netdev(0, name, reg_vif_setup); 4971da177e4SLinus Torvalds 4981da177e4SLinus Torvalds if (dev == NULL) 4991da177e4SLinus Torvalds return NULL; 5001da177e4SLinus Torvalds 501403dbb97STom Goff dev_net_set(dev, net); 502403dbb97STom Goff 5031da177e4SLinus Torvalds if (register_netdevice(dev)) { 5041da177e4SLinus Torvalds free_netdev(dev); 5051da177e4SLinus Torvalds return NULL; 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds dev->iflink = 0; 5081da177e4SLinus Torvalds 50971e27da9SHerbert Xu rcu_read_lock(); 510a8cb16ddSEric Dumazet in_dev = __in_dev_get_rcu(dev); 511a8cb16ddSEric Dumazet if (!in_dev) { 51271e27da9SHerbert Xu rcu_read_unlock(); 5131da177e4SLinus Torvalds goto failure; 51471e27da9SHerbert Xu } 5151da177e4SLinus Torvalds 51671e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 51771e27da9SHerbert Xu IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; 51871e27da9SHerbert Xu rcu_read_unlock(); 5191da177e4SLinus Torvalds 5201da177e4SLinus Torvalds if (dev_open(dev)) 5211da177e4SLinus Torvalds goto failure; 5221da177e4SLinus Torvalds 5237dc00c82SWang Chen dev_hold(dev); 5247dc00c82SWang Chen 5251da177e4SLinus Torvalds return dev; 5261da177e4SLinus Torvalds 5271da177e4SLinus Torvalds failure: 5281da177e4SLinus Torvalds /* allow the register to be completed before unregistering. */ 5291da177e4SLinus Torvalds rtnl_unlock(); 5301da177e4SLinus Torvalds rtnl_lock(); 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvalds unregister_netdevice(dev); 5331da177e4SLinus Torvalds return NULL; 5341da177e4SLinus Torvalds } 5351da177e4SLinus Torvalds #endif 5361da177e4SLinus Torvalds 5372c53040fSBen Hutchings /** 5382c53040fSBen Hutchings * vif_delete - Delete a VIF entry 5397dc00c82SWang Chen * @notify: Set to 1, if the caller is a notifier_call 5401da177e4SLinus Torvalds */ 5411da177e4SLinus Torvalds 5420c12295aSPatrick McHardy static int vif_delete(struct mr_table *mrt, int vifi, int notify, 543d17fa6faSEric Dumazet struct list_head *head) 5441da177e4SLinus Torvalds { 5451da177e4SLinus Torvalds struct vif_device *v; 5461da177e4SLinus Torvalds struct net_device *dev; 5471da177e4SLinus Torvalds struct in_device *in_dev; 5481da177e4SLinus Torvalds 5490c12295aSPatrick McHardy if (vifi < 0 || vifi >= mrt->maxvif) 5501da177e4SLinus Torvalds return -EADDRNOTAVAIL; 5511da177e4SLinus Torvalds 5520c12295aSPatrick McHardy v = &mrt->vif_table[vifi]; 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 5551da177e4SLinus Torvalds dev = v->dev; 5561da177e4SLinus Torvalds v->dev = NULL; 5571da177e4SLinus Torvalds 5581da177e4SLinus Torvalds if (!dev) { 5591da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 5601da177e4SLinus Torvalds return -EADDRNOTAVAIL; 5611da177e4SLinus Torvalds } 5621da177e4SLinus Torvalds 5631da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 5640c12295aSPatrick McHardy if (vifi == mrt->mroute_reg_vif_num) 5650c12295aSPatrick McHardy mrt->mroute_reg_vif_num = -1; 5661da177e4SLinus Torvalds #endif 5671da177e4SLinus Torvalds 5680c12295aSPatrick McHardy if (vifi + 1 == mrt->maxvif) { 5691da177e4SLinus Torvalds int tmp; 570a8cb16ddSEric Dumazet 5711da177e4SLinus Torvalds for (tmp = vifi - 1; tmp >= 0; tmp--) { 5720c12295aSPatrick McHardy if (VIF_EXISTS(mrt, tmp)) 5731da177e4SLinus Torvalds break; 5741da177e4SLinus Torvalds } 5750c12295aSPatrick McHardy mrt->maxvif = tmp+1; 5761da177e4SLinus Torvalds } 5771da177e4SLinus Torvalds 5781da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 5791da177e4SLinus Torvalds 5801da177e4SLinus Torvalds dev_set_allmulti(dev, -1); 5811da177e4SLinus Torvalds 582a8cb16ddSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 583a8cb16ddSEric Dumazet if (in_dev) { 58442f811b8SHerbert Xu IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--; 5851da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 5861da177e4SLinus Torvalds } 5871da177e4SLinus Torvalds 5887dc00c82SWang Chen if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER) && !notify) 589d17fa6faSEric Dumazet unregister_netdevice_queue(dev, head); 5901da177e4SLinus Torvalds 5911da177e4SLinus Torvalds dev_put(dev); 5921da177e4SLinus Torvalds return 0; 5931da177e4SLinus Torvalds } 5941da177e4SLinus Torvalds 595a8c9486bSEric Dumazet static void ipmr_cache_free_rcu(struct rcu_head *head) 596a8c9486bSEric Dumazet { 597a8c9486bSEric Dumazet struct mfc_cache *c = container_of(head, struct mfc_cache, rcu); 598a8c9486bSEric Dumazet 599a8c9486bSEric Dumazet kmem_cache_free(mrt_cachep, c); 600a8c9486bSEric Dumazet } 601a8c9486bSEric Dumazet 6025c0a66f5SBenjamin Thery static inline void ipmr_cache_free(struct mfc_cache *c) 6035c0a66f5SBenjamin Thery { 604a8c9486bSEric Dumazet call_rcu(&c->rcu, ipmr_cache_free_rcu); 6055c0a66f5SBenjamin Thery } 6065c0a66f5SBenjamin Thery 6071da177e4SLinus Torvalds /* Destroy an unresolved cache entry, killing queued skbs 608a8cb16ddSEric Dumazet * and reporting error to netlink readers. 6091da177e4SLinus Torvalds */ 6101da177e4SLinus Torvalds 6110c12295aSPatrick McHardy static void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c) 6121da177e4SLinus Torvalds { 6138de53dfbSPatrick McHardy struct net *net = read_pnet(&mrt->net); 6141da177e4SLinus Torvalds struct sk_buff *skb; 6159ef1d4c7SPatrick McHardy struct nlmsgerr *e; 6161da177e4SLinus Torvalds 6170c12295aSPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len); 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) { 620eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->version == 0) { 6211da177e4SLinus Torvalds struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr)); 6221da177e4SLinus Torvalds nlh->nlmsg_type = NLMSG_ERROR; 6231da177e4SLinus Torvalds nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr)); 6241da177e4SLinus Torvalds skb_trim(skb, nlh->nlmsg_len); 6259ef1d4c7SPatrick McHardy e = NLMSG_DATA(nlh); 6269ef1d4c7SPatrick McHardy e->error = -ETIMEDOUT; 6279ef1d4c7SPatrick McHardy memset(&e->msg, 0, sizeof(e->msg)); 6282942e900SThomas Graf 62915e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 630a8cb16ddSEric Dumazet } else { 6311da177e4SLinus Torvalds kfree_skb(skb); 6321da177e4SLinus Torvalds } 633a8cb16ddSEric Dumazet } 6341da177e4SLinus Torvalds 6355c0a66f5SBenjamin Thery ipmr_cache_free(c); 6361da177e4SLinus Torvalds } 6371da177e4SLinus Torvalds 6381da177e4SLinus Torvalds 639e258beb2SPatrick McHardy /* Timer process for the unresolved queue. */ 6401da177e4SLinus Torvalds 641e258beb2SPatrick McHardy static void ipmr_expire_process(unsigned long arg) 6421da177e4SLinus Torvalds { 6430c12295aSPatrick McHardy struct mr_table *mrt = (struct mr_table *)arg; 6441da177e4SLinus Torvalds unsigned long now; 6451da177e4SLinus Torvalds unsigned long expires; 646862465f2SPatrick McHardy struct mfc_cache *c, *next; 6471da177e4SLinus Torvalds 6481da177e4SLinus Torvalds if (!spin_trylock(&mfc_unres_lock)) { 6490c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10); 6501da177e4SLinus Torvalds return; 6511da177e4SLinus Torvalds } 6521da177e4SLinus Torvalds 6530c12295aSPatrick McHardy if (list_empty(&mrt->mfc_unres_queue)) 6541da177e4SLinus Torvalds goto out; 6551da177e4SLinus Torvalds 6561da177e4SLinus Torvalds now = jiffies; 6571da177e4SLinus Torvalds expires = 10*HZ; 6581da177e4SLinus Torvalds 6590c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) { 6601da177e4SLinus Torvalds if (time_after(c->mfc_un.unres.expires, now)) { 6611da177e4SLinus Torvalds unsigned long interval = c->mfc_un.unres.expires - now; 6621da177e4SLinus Torvalds if (interval < expires) 6631da177e4SLinus Torvalds expires = interval; 6641da177e4SLinus Torvalds continue; 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds 667862465f2SPatrick McHardy list_del(&c->list); 6680c12295aSPatrick McHardy ipmr_destroy_unres(mrt, c); 6691da177e4SLinus Torvalds } 6701da177e4SLinus Torvalds 6710c12295aSPatrick McHardy if (!list_empty(&mrt->mfc_unres_queue)) 6720c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, jiffies + expires); 6731da177e4SLinus Torvalds 6741da177e4SLinus Torvalds out: 6751da177e4SLinus Torvalds spin_unlock(&mfc_unres_lock); 6761da177e4SLinus Torvalds } 6771da177e4SLinus Torvalds 6781da177e4SLinus Torvalds /* Fill oifs list. It is called under write locked mrt_lock. */ 6791da177e4SLinus Torvalds 6800c12295aSPatrick McHardy static void ipmr_update_thresholds(struct mr_table *mrt, struct mfc_cache *cache, 681d658f8a0SPatrick McHardy unsigned char *ttls) 6821da177e4SLinus Torvalds { 6831da177e4SLinus Torvalds int vifi; 6841da177e4SLinus Torvalds 6851da177e4SLinus Torvalds cache->mfc_un.res.minvif = MAXVIFS; 6861da177e4SLinus Torvalds cache->mfc_un.res.maxvif = 0; 6871da177e4SLinus Torvalds memset(cache->mfc_un.res.ttls, 255, MAXVIFS); 6881da177e4SLinus Torvalds 6890c12295aSPatrick McHardy for (vifi = 0; vifi < mrt->maxvif; vifi++) { 6900c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vifi) && 691cf958ae3SBenjamin Thery ttls[vifi] && ttls[vifi] < 255) { 6921da177e4SLinus Torvalds cache->mfc_un.res.ttls[vifi] = ttls[vifi]; 6931da177e4SLinus Torvalds if (cache->mfc_un.res.minvif > vifi) 6941da177e4SLinus Torvalds cache->mfc_un.res.minvif = vifi; 6951da177e4SLinus Torvalds if (cache->mfc_un.res.maxvif <= vifi) 6961da177e4SLinus Torvalds cache->mfc_un.res.maxvif = vifi + 1; 6971da177e4SLinus Torvalds } 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds } 7001da177e4SLinus Torvalds 7010c12295aSPatrick McHardy static int vif_add(struct net *net, struct mr_table *mrt, 7020c12295aSPatrick McHardy struct vifctl *vifc, int mrtsock) 7031da177e4SLinus Torvalds { 7041da177e4SLinus Torvalds int vifi = vifc->vifc_vifi; 7050c12295aSPatrick McHardy struct vif_device *v = &mrt->vif_table[vifi]; 7061da177e4SLinus Torvalds struct net_device *dev; 7071da177e4SLinus Torvalds struct in_device *in_dev; 708d607032dSWang Chen int err; 7091da177e4SLinus Torvalds 7101da177e4SLinus Torvalds /* Is vif busy ? */ 7110c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vifi)) 7121da177e4SLinus Torvalds return -EADDRINUSE; 7131da177e4SLinus Torvalds 7141da177e4SLinus Torvalds switch (vifc->vifc_flags) { 7151da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 7161da177e4SLinus Torvalds case VIFF_REGISTER: 7171da177e4SLinus Torvalds /* 7181da177e4SLinus Torvalds * Special Purpose VIF in PIM 7191da177e4SLinus Torvalds * All the packets will be sent to the daemon 7201da177e4SLinus Torvalds */ 7210c12295aSPatrick McHardy if (mrt->mroute_reg_vif_num >= 0) 7221da177e4SLinus Torvalds return -EADDRINUSE; 723f0ad0860SPatrick McHardy dev = ipmr_reg_vif(net, mrt); 7241da177e4SLinus Torvalds if (!dev) 7251da177e4SLinus Torvalds return -ENOBUFS; 726d607032dSWang Chen err = dev_set_allmulti(dev, 1); 727d607032dSWang Chen if (err) { 728d607032dSWang Chen unregister_netdevice(dev); 7297dc00c82SWang Chen dev_put(dev); 730d607032dSWang Chen return err; 731d607032dSWang Chen } 7321da177e4SLinus Torvalds break; 7331da177e4SLinus Torvalds #endif 7341da177e4SLinus Torvalds case VIFF_TUNNEL: 7354feb88e5SBenjamin Thery dev = ipmr_new_tunnel(net, vifc); 7361da177e4SLinus Torvalds if (!dev) 7371da177e4SLinus Torvalds return -ENOBUFS; 738d607032dSWang Chen err = dev_set_allmulti(dev, 1); 739d607032dSWang Chen if (err) { 740d607032dSWang Chen ipmr_del_tunnel(dev, vifc); 7417dc00c82SWang Chen dev_put(dev); 742d607032dSWang Chen return err; 743d607032dSWang Chen } 7441da177e4SLinus Torvalds break; 745ee5e81f0SIlia K 746ee5e81f0SIlia K case VIFF_USE_IFINDEX: 7471da177e4SLinus Torvalds case 0: 748ee5e81f0SIlia K if (vifc->vifc_flags == VIFF_USE_IFINDEX) { 749ee5e81f0SIlia K dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex); 75095ae6b22SEric Dumazet if (dev && __in_dev_get_rtnl(dev) == NULL) { 751ee5e81f0SIlia K dev_put(dev); 752ee5e81f0SIlia K return -EADDRNOTAVAIL; 753ee5e81f0SIlia K } 754a8cb16ddSEric Dumazet } else { 7554feb88e5SBenjamin Thery dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr); 756a8cb16ddSEric Dumazet } 7571da177e4SLinus Torvalds if (!dev) 7581da177e4SLinus Torvalds return -EADDRNOTAVAIL; 759d607032dSWang Chen err = dev_set_allmulti(dev, 1); 7607dc00c82SWang Chen if (err) { 7617dc00c82SWang Chen dev_put(dev); 762d607032dSWang Chen return err; 7637dc00c82SWang Chen } 7641da177e4SLinus Torvalds break; 7651da177e4SLinus Torvalds default: 7661da177e4SLinus Torvalds return -EINVAL; 7671da177e4SLinus Torvalds } 7681da177e4SLinus Torvalds 769a8cb16ddSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 770a8cb16ddSEric Dumazet if (!in_dev) { 771d0490cfdSDan Carpenter dev_put(dev); 7721da177e4SLinus Torvalds return -EADDRNOTAVAIL; 773d0490cfdSDan Carpenter } 77442f811b8SHerbert Xu IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++; 7751da177e4SLinus Torvalds ip_rt_multicast_event(in_dev); 7761da177e4SLinus Torvalds 777a8cb16ddSEric Dumazet /* Fill in the VIF structures */ 778a8cb16ddSEric Dumazet 7791da177e4SLinus Torvalds v->rate_limit = vifc->vifc_rate_limit; 7801da177e4SLinus Torvalds v->local = vifc->vifc_lcl_addr.s_addr; 7811da177e4SLinus Torvalds v->remote = vifc->vifc_rmt_addr.s_addr; 7821da177e4SLinus Torvalds v->flags = vifc->vifc_flags; 7831da177e4SLinus Torvalds if (!mrtsock) 7841da177e4SLinus Torvalds v->flags |= VIFF_STATIC; 7851da177e4SLinus Torvalds v->threshold = vifc->vifc_threshold; 7861da177e4SLinus Torvalds v->bytes_in = 0; 7871da177e4SLinus Torvalds v->bytes_out = 0; 7881da177e4SLinus Torvalds v->pkt_in = 0; 7891da177e4SLinus Torvalds v->pkt_out = 0; 7901da177e4SLinus Torvalds v->link = dev->ifindex; 7911da177e4SLinus Torvalds if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER)) 7921da177e4SLinus Torvalds v->link = dev->iflink; 7931da177e4SLinus Torvalds 7941da177e4SLinus Torvalds /* And finish update writing critical data */ 7951da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 7961da177e4SLinus Torvalds v->dev = dev; 7971da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 7981da177e4SLinus Torvalds if (v->flags & VIFF_REGISTER) 7990c12295aSPatrick McHardy mrt->mroute_reg_vif_num = vifi; 8001da177e4SLinus Torvalds #endif 8010c12295aSPatrick McHardy if (vifi+1 > mrt->maxvif) 8020c12295aSPatrick McHardy mrt->maxvif = vifi+1; 8031da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 8041da177e4SLinus Torvalds return 0; 8051da177e4SLinus Torvalds } 8061da177e4SLinus Torvalds 807a8c9486bSEric Dumazet /* called with rcu_read_lock() */ 8080c12295aSPatrick McHardy static struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, 8094feb88e5SBenjamin Thery __be32 origin, 8104feb88e5SBenjamin Thery __be32 mcastgrp) 8111da177e4SLinus Torvalds { 8121da177e4SLinus Torvalds int line = MFC_HASH(mcastgrp, origin); 8131da177e4SLinus Torvalds struct mfc_cache *c; 8141da177e4SLinus Torvalds 815a8c9486bSEric Dumazet list_for_each_entry_rcu(c, &mrt->mfc_cache_array[line], list) { 8161da177e4SLinus Torvalds if (c->mfc_origin == origin && c->mfc_mcastgrp == mcastgrp) 8171da177e4SLinus Torvalds return c; 8181da177e4SLinus Torvalds } 819862465f2SPatrick McHardy return NULL; 820862465f2SPatrick McHardy } 8211da177e4SLinus Torvalds 8221da177e4SLinus Torvalds /* 8231da177e4SLinus Torvalds * Allocate a multicast cache entry 8241da177e4SLinus Torvalds */ 825d658f8a0SPatrick McHardy static struct mfc_cache *ipmr_cache_alloc(void) 8261da177e4SLinus Torvalds { 827c3762229SRobert P. J. Day struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL); 828a8c9486bSEric Dumazet 829a8c9486bSEric Dumazet if (c) 8301da177e4SLinus Torvalds c->mfc_un.res.minvif = MAXVIFS; 8311da177e4SLinus Torvalds return c; 8321da177e4SLinus Torvalds } 8331da177e4SLinus Torvalds 834d658f8a0SPatrick McHardy static struct mfc_cache *ipmr_cache_alloc_unres(void) 8351da177e4SLinus Torvalds { 836c3762229SRobert P. J. Day struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC); 837a8c9486bSEric Dumazet 838a8c9486bSEric Dumazet if (c) { 8391da177e4SLinus Torvalds skb_queue_head_init(&c->mfc_un.unres.unresolved); 8401da177e4SLinus Torvalds c->mfc_un.unres.expires = jiffies + 10*HZ; 841a8c9486bSEric Dumazet } 8421da177e4SLinus Torvalds return c; 8431da177e4SLinus Torvalds } 8441da177e4SLinus Torvalds 8451da177e4SLinus Torvalds /* 8461da177e4SLinus Torvalds * A cache entry has gone into a resolved state from queued 8471da177e4SLinus Torvalds */ 8481da177e4SLinus Torvalds 8490c12295aSPatrick McHardy static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt, 8500c12295aSPatrick McHardy struct mfc_cache *uc, struct mfc_cache *c) 8511da177e4SLinus Torvalds { 8521da177e4SLinus Torvalds struct sk_buff *skb; 8539ef1d4c7SPatrick McHardy struct nlmsgerr *e; 8541da177e4SLinus Torvalds 855a8cb16ddSEric Dumazet /* Play the pending entries through our router */ 8561da177e4SLinus Torvalds 8571da177e4SLinus Torvalds while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) { 858eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->version == 0) { 8591da177e4SLinus Torvalds struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr)); 8601da177e4SLinus Torvalds 861cb6a4e46SPatrick McHardy if (__ipmr_fill_mroute(mrt, skb, c, NLMSG_DATA(nlh)) > 0) { 862a8cb16ddSEric Dumazet nlh->nlmsg_len = skb_tail_pointer(skb) - 863a8cb16ddSEric Dumazet (u8 *)nlh; 8641da177e4SLinus Torvalds } else { 8651da177e4SLinus Torvalds nlh->nlmsg_type = NLMSG_ERROR; 8661da177e4SLinus Torvalds nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr)); 8671da177e4SLinus Torvalds skb_trim(skb, nlh->nlmsg_len); 8689ef1d4c7SPatrick McHardy e = NLMSG_DATA(nlh); 8699ef1d4c7SPatrick McHardy e->error = -EMSGSIZE; 8709ef1d4c7SPatrick McHardy memset(&e->msg, 0, sizeof(e->msg)); 8711da177e4SLinus Torvalds } 8722942e900SThomas Graf 87315e47304SEric W. Biederman rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 874a8cb16ddSEric Dumazet } else { 8750c12295aSPatrick McHardy ip_mr_forward(net, mrt, skb, c, 0); 8761da177e4SLinus Torvalds } 8771da177e4SLinus Torvalds } 878a8cb16ddSEric Dumazet } 8791da177e4SLinus Torvalds 8801da177e4SLinus Torvalds /* 8811da177e4SLinus Torvalds * Bounce a cache query up to mrouted. We could use netlink for this but mrouted 8821da177e4SLinus Torvalds * expects the following bizarre scheme. 8831da177e4SLinus Torvalds * 8841da177e4SLinus Torvalds * Called under mrt_lock. 8851da177e4SLinus Torvalds */ 8861da177e4SLinus Torvalds 8870c12295aSPatrick McHardy static int ipmr_cache_report(struct mr_table *mrt, 8884feb88e5SBenjamin Thery struct sk_buff *pkt, vifi_t vifi, int assert) 8891da177e4SLinus Torvalds { 8901da177e4SLinus Torvalds struct sk_buff *skb; 891c9bdd4b5SArnaldo Carvalho de Melo const int ihl = ip_hdrlen(pkt); 8921da177e4SLinus Torvalds struct igmphdr *igmp; 8931da177e4SLinus Torvalds struct igmpmsg *msg; 8944c968709SEric Dumazet struct sock *mroute_sk; 8951da177e4SLinus Torvalds int ret; 8961da177e4SLinus Torvalds 8971da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 8981da177e4SLinus Torvalds if (assert == IGMPMSG_WHOLEPKT) 8991da177e4SLinus Torvalds skb = skb_realloc_headroom(pkt, sizeof(struct iphdr)); 9001da177e4SLinus Torvalds else 9011da177e4SLinus Torvalds #endif 9021da177e4SLinus Torvalds skb = alloc_skb(128, GFP_ATOMIC); 9031da177e4SLinus Torvalds 9041da177e4SLinus Torvalds if (!skb) 9051da177e4SLinus Torvalds return -ENOBUFS; 9061da177e4SLinus Torvalds 9071da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 9081da177e4SLinus Torvalds if (assert == IGMPMSG_WHOLEPKT) { 9091da177e4SLinus Torvalds /* Ugly, but we have no choice with this interface. 910a8cb16ddSEric Dumazet * Duplicate old header, fix ihl, length etc. 911a8cb16ddSEric Dumazet * And all this only to mangle msg->im_msgtype and 912a8cb16ddSEric Dumazet * to set msg->im_mbz to "mbz" :-) 9131da177e4SLinus Torvalds */ 914878c8145SArnaldo Carvalho de Melo skb_push(skb, sizeof(struct iphdr)); 915878c8145SArnaldo Carvalho de Melo skb_reset_network_header(skb); 916badff6d0SArnaldo Carvalho de Melo skb_reset_transport_header(skb); 9170272ffc4SArnaldo Carvalho de Melo msg = (struct igmpmsg *)skb_network_header(skb); 918d56f90a7SArnaldo Carvalho de Melo memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr)); 9191da177e4SLinus Torvalds msg->im_msgtype = IGMPMSG_WHOLEPKT; 9201da177e4SLinus Torvalds msg->im_mbz = 0; 9210c12295aSPatrick McHardy msg->im_vif = mrt->mroute_reg_vif_num; 922eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2; 923eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) + 924eddc9ec5SArnaldo Carvalho de Melo sizeof(struct iphdr)); 9251da177e4SLinus Torvalds } else 9261da177e4SLinus Torvalds #endif 9271da177e4SLinus Torvalds { 9281da177e4SLinus Torvalds 929a8cb16ddSEric Dumazet /* Copy the IP header */ 9301da177e4SLinus Torvalds 93127a884dcSArnaldo Carvalho de Melo skb->network_header = skb->tail; 932ddc7b8e3SArnaldo Carvalho de Melo skb_put(skb, ihl); 93327d7ff46SArnaldo Carvalho de Melo skb_copy_to_linear_data(skb, pkt->data, ihl); 934eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */ 935eddc9ec5SArnaldo Carvalho de Melo msg = (struct igmpmsg *)skb_network_header(skb); 9361da177e4SLinus Torvalds msg->im_vif = vifi; 937adf30907SEric Dumazet skb_dst_set(skb, dst_clone(skb_dst(pkt))); 9381da177e4SLinus Torvalds 939a8cb16ddSEric Dumazet /* Add our header */ 9401da177e4SLinus Torvalds 9411da177e4SLinus Torvalds igmp = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr)); 9421da177e4SLinus Torvalds igmp->type = 9431da177e4SLinus Torvalds msg->im_msgtype = assert; 9441da177e4SLinus Torvalds igmp->code = 0; 945eddc9ec5SArnaldo Carvalho de Melo ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */ 946b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 9471da177e4SLinus Torvalds } 9481da177e4SLinus Torvalds 9494c968709SEric Dumazet rcu_read_lock(); 9504c968709SEric Dumazet mroute_sk = rcu_dereference(mrt->mroute_sk); 9514c968709SEric Dumazet if (mroute_sk == NULL) { 9524c968709SEric Dumazet rcu_read_unlock(); 9531da177e4SLinus Torvalds kfree_skb(skb); 9541da177e4SLinus Torvalds return -EINVAL; 9551da177e4SLinus Torvalds } 9561da177e4SLinus Torvalds 957a8cb16ddSEric Dumazet /* Deliver to mrouted */ 958a8cb16ddSEric Dumazet 9594c968709SEric Dumazet ret = sock_queue_rcv_skb(mroute_sk, skb); 9604c968709SEric Dumazet rcu_read_unlock(); 96170a269e6SBenjamin Thery if (ret < 0) { 962e87cc472SJoe Perches net_warn_ratelimited("mroute: pending queue full, dropping entries\n"); 9631da177e4SLinus Torvalds kfree_skb(skb); 9641da177e4SLinus Torvalds } 9651da177e4SLinus Torvalds 9661da177e4SLinus Torvalds return ret; 9671da177e4SLinus Torvalds } 9681da177e4SLinus Torvalds 9691da177e4SLinus Torvalds /* 9701da177e4SLinus Torvalds * Queue a packet for resolution. It gets locked cache entry! 9711da177e4SLinus Torvalds */ 9721da177e4SLinus Torvalds 9731da177e4SLinus Torvalds static int 9740c12295aSPatrick McHardy ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb) 9751da177e4SLinus Torvalds { 976862465f2SPatrick McHardy bool found = false; 9771da177e4SLinus Torvalds int err; 9781da177e4SLinus Torvalds struct mfc_cache *c; 979eddc9ec5SArnaldo Carvalho de Melo const struct iphdr *iph = ip_hdr(skb); 9801da177e4SLinus Torvalds 9811da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 9820c12295aSPatrick McHardy list_for_each_entry(c, &mrt->mfc_unres_queue, list) { 983e258beb2SPatrick McHardy if (c->mfc_mcastgrp == iph->daddr && 984862465f2SPatrick McHardy c->mfc_origin == iph->saddr) { 985862465f2SPatrick McHardy found = true; 9861da177e4SLinus Torvalds break; 9871da177e4SLinus Torvalds } 988862465f2SPatrick McHardy } 9891da177e4SLinus Torvalds 990862465f2SPatrick McHardy if (!found) { 991a8cb16ddSEric Dumazet /* Create a new entry if allowable */ 9921da177e4SLinus Torvalds 9930c12295aSPatrick McHardy if (atomic_read(&mrt->cache_resolve_queue_len) >= 10 || 994d658f8a0SPatrick McHardy (c = ipmr_cache_alloc_unres()) == NULL) { 9951da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 9961da177e4SLinus Torvalds 9971da177e4SLinus Torvalds kfree_skb(skb); 9981da177e4SLinus Torvalds return -ENOBUFS; 9991da177e4SLinus Torvalds } 10001da177e4SLinus Torvalds 1001a8cb16ddSEric Dumazet /* Fill in the new cache entry */ 1002a8cb16ddSEric Dumazet 10031da177e4SLinus Torvalds c->mfc_parent = -1; 1004eddc9ec5SArnaldo Carvalho de Melo c->mfc_origin = iph->saddr; 1005eddc9ec5SArnaldo Carvalho de Melo c->mfc_mcastgrp = iph->daddr; 10061da177e4SLinus Torvalds 1007a8cb16ddSEric Dumazet /* Reflect first query at mrouted. */ 1008a8cb16ddSEric Dumazet 10090c12295aSPatrick McHardy err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE); 10104feb88e5SBenjamin Thery if (err < 0) { 10111da177e4SLinus Torvalds /* If the report failed throw the cache entry 10121da177e4SLinus Torvalds out - Brad Parker 10131da177e4SLinus Torvalds */ 10141da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 10151da177e4SLinus Torvalds 10165c0a66f5SBenjamin Thery ipmr_cache_free(c); 10171da177e4SLinus Torvalds kfree_skb(skb); 10181da177e4SLinus Torvalds return err; 10191da177e4SLinus Torvalds } 10201da177e4SLinus Torvalds 10210c12295aSPatrick McHardy atomic_inc(&mrt->cache_resolve_queue_len); 10220c12295aSPatrick McHardy list_add(&c->list, &mrt->mfc_unres_queue); 10231da177e4SLinus Torvalds 1024278554bdSDavid S. Miller if (atomic_read(&mrt->cache_resolve_queue_len) == 1) 10250c12295aSPatrick McHardy mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires); 10261da177e4SLinus Torvalds } 10271da177e4SLinus Torvalds 1028a8cb16ddSEric Dumazet /* See if we can append the packet */ 1029a8cb16ddSEric Dumazet 10301da177e4SLinus Torvalds if (c->mfc_un.unres.unresolved.qlen > 3) { 10311da177e4SLinus Torvalds kfree_skb(skb); 10321da177e4SLinus Torvalds err = -ENOBUFS; 10331da177e4SLinus Torvalds } else { 10341da177e4SLinus Torvalds skb_queue_tail(&c->mfc_un.unres.unresolved, skb); 10351da177e4SLinus Torvalds err = 0; 10361da177e4SLinus Torvalds } 10371da177e4SLinus Torvalds 10381da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 10391da177e4SLinus Torvalds return err; 10401da177e4SLinus Torvalds } 10411da177e4SLinus Torvalds 10421da177e4SLinus Torvalds /* 10431da177e4SLinus Torvalds * MFC cache manipulation by user space mroute daemon 10441da177e4SLinus Torvalds */ 10451da177e4SLinus Torvalds 10460c12295aSPatrick McHardy static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc) 10471da177e4SLinus Torvalds { 10481da177e4SLinus Torvalds int line; 1049862465f2SPatrick McHardy struct mfc_cache *c, *next; 10501da177e4SLinus Torvalds 10511da177e4SLinus Torvalds line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr); 10521da177e4SLinus Torvalds 10530c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[line], list) { 10541da177e4SLinus Torvalds if (c->mfc_origin == mfc->mfcc_origin.s_addr && 10551da177e4SLinus Torvalds c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) { 1056a8c9486bSEric Dumazet list_del_rcu(&c->list); 10571da177e4SLinus Torvalds 10585c0a66f5SBenjamin Thery ipmr_cache_free(c); 10591da177e4SLinus Torvalds return 0; 10601da177e4SLinus Torvalds } 10611da177e4SLinus Torvalds } 10621da177e4SLinus Torvalds return -ENOENT; 10631da177e4SLinus Torvalds } 10641da177e4SLinus Torvalds 10650c12295aSPatrick McHardy static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, 10660c12295aSPatrick McHardy struct mfcctl *mfc, int mrtsock) 10671da177e4SLinus Torvalds { 1068862465f2SPatrick McHardy bool found = false; 10691da177e4SLinus Torvalds int line; 1070862465f2SPatrick McHardy struct mfc_cache *uc, *c; 10711da177e4SLinus Torvalds 1072a50436f2SPatrick McHardy if (mfc->mfcc_parent >= MAXVIFS) 1073a50436f2SPatrick McHardy return -ENFILE; 1074a50436f2SPatrick McHardy 10751da177e4SLinus Torvalds line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr); 10761da177e4SLinus Torvalds 10770c12295aSPatrick McHardy list_for_each_entry(c, &mrt->mfc_cache_array[line], list) { 10781da177e4SLinus Torvalds if (c->mfc_origin == mfc->mfcc_origin.s_addr && 1079862465f2SPatrick McHardy c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) { 1080862465f2SPatrick McHardy found = true; 10811da177e4SLinus Torvalds break; 10821da177e4SLinus Torvalds } 1083862465f2SPatrick McHardy } 10841da177e4SLinus Torvalds 1085862465f2SPatrick McHardy if (found) { 10861da177e4SLinus Torvalds write_lock_bh(&mrt_lock); 10871da177e4SLinus Torvalds c->mfc_parent = mfc->mfcc_parent; 10880c12295aSPatrick McHardy ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls); 10891da177e4SLinus Torvalds if (!mrtsock) 10901da177e4SLinus Torvalds c->mfc_flags |= MFC_STATIC; 10911da177e4SLinus Torvalds write_unlock_bh(&mrt_lock); 10921da177e4SLinus Torvalds return 0; 10931da177e4SLinus Torvalds } 10941da177e4SLinus Torvalds 1095f97c1e0cSJoe Perches if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) 10961da177e4SLinus Torvalds return -EINVAL; 10971da177e4SLinus Torvalds 1098d658f8a0SPatrick McHardy c = ipmr_cache_alloc(); 10991da177e4SLinus Torvalds if (c == NULL) 11001da177e4SLinus Torvalds return -ENOMEM; 11011da177e4SLinus Torvalds 11021da177e4SLinus Torvalds c->mfc_origin = mfc->mfcc_origin.s_addr; 11031da177e4SLinus Torvalds c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr; 11041da177e4SLinus Torvalds c->mfc_parent = mfc->mfcc_parent; 11050c12295aSPatrick McHardy ipmr_update_thresholds(mrt, c, mfc->mfcc_ttls); 11061da177e4SLinus Torvalds if (!mrtsock) 11071da177e4SLinus Torvalds c->mfc_flags |= MFC_STATIC; 11081da177e4SLinus Torvalds 1109a8c9486bSEric Dumazet list_add_rcu(&c->list, &mrt->mfc_cache_array[line]); 11101da177e4SLinus Torvalds 11111da177e4SLinus Torvalds /* 11121da177e4SLinus Torvalds * Check to see if we resolved a queued list. If so we 11131da177e4SLinus Torvalds * need to send on the frames and tidy up. 11141da177e4SLinus Torvalds */ 1115b0ebb739SPatrick McHardy found = false; 11161da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 11170c12295aSPatrick McHardy list_for_each_entry(uc, &mrt->mfc_unres_queue, list) { 1118e258beb2SPatrick McHardy if (uc->mfc_origin == c->mfc_origin && 11191da177e4SLinus Torvalds uc->mfc_mcastgrp == c->mfc_mcastgrp) { 1120862465f2SPatrick McHardy list_del(&uc->list); 11210c12295aSPatrick McHardy atomic_dec(&mrt->cache_resolve_queue_len); 1122b0ebb739SPatrick McHardy found = true; 11231da177e4SLinus Torvalds break; 11241da177e4SLinus Torvalds } 11251da177e4SLinus Torvalds } 11260c12295aSPatrick McHardy if (list_empty(&mrt->mfc_unres_queue)) 11270c12295aSPatrick McHardy del_timer(&mrt->ipmr_expire_timer); 11281da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11291da177e4SLinus Torvalds 1130b0ebb739SPatrick McHardy if (found) { 11310c12295aSPatrick McHardy ipmr_cache_resolve(net, mrt, uc, c); 11325c0a66f5SBenjamin Thery ipmr_cache_free(uc); 11331da177e4SLinus Torvalds } 11341da177e4SLinus Torvalds return 0; 11351da177e4SLinus Torvalds } 11361da177e4SLinus Torvalds 11371da177e4SLinus Torvalds /* 11381da177e4SLinus Torvalds * Close the multicast socket, and clear the vif tables etc 11391da177e4SLinus Torvalds */ 11401da177e4SLinus Torvalds 11410c12295aSPatrick McHardy static void mroute_clean_tables(struct mr_table *mrt) 11421da177e4SLinus Torvalds { 11431da177e4SLinus Torvalds int i; 1144d17fa6faSEric Dumazet LIST_HEAD(list); 1145862465f2SPatrick McHardy struct mfc_cache *c, *next; 11461da177e4SLinus Torvalds 1147a8cb16ddSEric Dumazet /* Shut down all active vif entries */ 1148a8cb16ddSEric Dumazet 11490c12295aSPatrick McHardy for (i = 0; i < mrt->maxvif; i++) { 11500c12295aSPatrick McHardy if (!(mrt->vif_table[i].flags & VIFF_STATIC)) 11510c12295aSPatrick McHardy vif_delete(mrt, i, 0, &list); 11521da177e4SLinus Torvalds } 1153d17fa6faSEric Dumazet unregister_netdevice_many(&list); 11541da177e4SLinus Torvalds 1155a8cb16ddSEric Dumazet /* Wipe the cache */ 1156a8cb16ddSEric Dumazet 11571da177e4SLinus Torvalds for (i = 0; i < MFC_LINES; i++) { 11580c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_cache_array[i], list) { 1159862465f2SPatrick McHardy if (c->mfc_flags & MFC_STATIC) 11601da177e4SLinus Torvalds continue; 1161a8c9486bSEric Dumazet list_del_rcu(&c->list); 11625c0a66f5SBenjamin Thery ipmr_cache_free(c); 11631da177e4SLinus Torvalds } 11641da177e4SLinus Torvalds } 11651da177e4SLinus Torvalds 11660c12295aSPatrick McHardy if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { 11671da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 11680c12295aSPatrick McHardy list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) { 1169862465f2SPatrick McHardy list_del(&c->list); 11700c12295aSPatrick McHardy ipmr_destroy_unres(mrt, c); 11711da177e4SLinus Torvalds } 11721da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 11731da177e4SLinus Torvalds } 11741da177e4SLinus Torvalds } 11751da177e4SLinus Torvalds 11764c968709SEric Dumazet /* called from ip_ra_control(), before an RCU grace period, 11774c968709SEric Dumazet * we dont need to call synchronize_rcu() here 11784c968709SEric Dumazet */ 11791da177e4SLinus Torvalds static void mrtsock_destruct(struct sock *sk) 11801da177e4SLinus Torvalds { 11814feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1182f0ad0860SPatrick McHardy struct mr_table *mrt; 11834feb88e5SBenjamin Thery 11841da177e4SLinus Torvalds rtnl_lock(); 1185f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 11864c968709SEric Dumazet if (sk == rtnl_dereference(mrt->mroute_sk)) { 11874feb88e5SBenjamin Thery IPV4_DEVCONF_ALL(net, MC_FORWARDING)--; 1188a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(mrt->mroute_sk, NULL); 11890c12295aSPatrick McHardy mroute_clean_tables(mrt); 11901da177e4SLinus Torvalds } 11911da177e4SLinus Torvalds } 11921da177e4SLinus Torvalds rtnl_unlock(); 11931da177e4SLinus Torvalds } 11941da177e4SLinus Torvalds 11951da177e4SLinus Torvalds /* 11961da177e4SLinus Torvalds * Socket options and virtual interface manipulation. The whole 11971da177e4SLinus Torvalds * virtual interface system is a complete heap, but unfortunately 11981da177e4SLinus Torvalds * that's how BSD mrouted happens to think. Maybe one day with a proper 11991da177e4SLinus Torvalds * MOSPF/PIM router set up we can clean this up. 12001da177e4SLinus Torvalds */ 12011da177e4SLinus Torvalds 1202b7058842SDavid S. Miller int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsigned int optlen) 12031da177e4SLinus Torvalds { 12041da177e4SLinus Torvalds int ret; 12051da177e4SLinus Torvalds struct vifctl vif; 12061da177e4SLinus Torvalds struct mfcctl mfc; 12074feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1208f0ad0860SPatrick McHardy struct mr_table *mrt; 1209f0ad0860SPatrick McHardy 1210*5e1859fbSEric Dumazet if (sk->sk_type != SOCK_RAW || 1211*5e1859fbSEric Dumazet inet_sk(sk)->inet_num != IPPROTO_IGMP) 1212*5e1859fbSEric Dumazet return -EOPNOTSUPP; 1213*5e1859fbSEric Dumazet 1214f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 1215f0ad0860SPatrick McHardy if (mrt == NULL) 1216f0ad0860SPatrick McHardy return -ENOENT; 12171da177e4SLinus Torvalds 1218132adf54SStephen Hemminger if (optname != MRT_INIT) { 121933d480ceSEric Dumazet if (sk != rcu_access_pointer(mrt->mroute_sk) && 122052e804c6SEric W. Biederman !ns_capable(net->user_ns, CAP_NET_ADMIN)) 12211da177e4SLinus Torvalds return -EACCES; 12221da177e4SLinus Torvalds } 12231da177e4SLinus Torvalds 1224132adf54SStephen Hemminger switch (optname) { 12251da177e4SLinus Torvalds case MRT_INIT: 12261da177e4SLinus Torvalds if (optlen != sizeof(int)) 1227*5e1859fbSEric Dumazet return -EINVAL; 12281da177e4SLinus Torvalds 12291da177e4SLinus Torvalds rtnl_lock(); 12304c968709SEric Dumazet if (rtnl_dereference(mrt->mroute_sk)) { 12311da177e4SLinus Torvalds rtnl_unlock(); 12321da177e4SLinus Torvalds return -EADDRINUSE; 12331da177e4SLinus Torvalds } 12341da177e4SLinus Torvalds 12351da177e4SLinus Torvalds ret = ip_ra_control(sk, 1, mrtsock_destruct); 12361da177e4SLinus Torvalds if (ret == 0) { 1237cf778b00SEric Dumazet rcu_assign_pointer(mrt->mroute_sk, sk); 12384feb88e5SBenjamin Thery IPV4_DEVCONF_ALL(net, MC_FORWARDING)++; 12391da177e4SLinus Torvalds } 12401da177e4SLinus Torvalds rtnl_unlock(); 12411da177e4SLinus Torvalds return ret; 12421da177e4SLinus Torvalds case MRT_DONE: 124333d480ceSEric Dumazet if (sk != rcu_access_pointer(mrt->mroute_sk)) 12441da177e4SLinus Torvalds return -EACCES; 12451da177e4SLinus Torvalds return ip_ra_control(sk, 0, NULL); 12461da177e4SLinus Torvalds case MRT_ADD_VIF: 12471da177e4SLinus Torvalds case MRT_DEL_VIF: 12481da177e4SLinus Torvalds if (optlen != sizeof(vif)) 12491da177e4SLinus Torvalds return -EINVAL; 12501da177e4SLinus Torvalds if (copy_from_user(&vif, optval, sizeof(vif))) 12511da177e4SLinus Torvalds return -EFAULT; 12521da177e4SLinus Torvalds if (vif.vifc_vifi >= MAXVIFS) 12531da177e4SLinus Torvalds return -ENFILE; 12541da177e4SLinus Torvalds rtnl_lock(); 12551da177e4SLinus Torvalds if (optname == MRT_ADD_VIF) { 12564c968709SEric Dumazet ret = vif_add(net, mrt, &vif, 12574c968709SEric Dumazet sk == rtnl_dereference(mrt->mroute_sk)); 12581da177e4SLinus Torvalds } else { 12590c12295aSPatrick McHardy ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL); 12601da177e4SLinus Torvalds } 12611da177e4SLinus Torvalds rtnl_unlock(); 12621da177e4SLinus Torvalds return ret; 12631da177e4SLinus Torvalds 12641da177e4SLinus Torvalds /* 12651da177e4SLinus Torvalds * Manipulate the forwarding caches. These live 12661da177e4SLinus Torvalds * in a sort of kernel/user symbiosis. 12671da177e4SLinus Torvalds */ 12681da177e4SLinus Torvalds case MRT_ADD_MFC: 12691da177e4SLinus Torvalds case MRT_DEL_MFC: 12701da177e4SLinus Torvalds if (optlen != sizeof(mfc)) 12711da177e4SLinus Torvalds return -EINVAL; 12721da177e4SLinus Torvalds if (copy_from_user(&mfc, optval, sizeof(mfc))) 12731da177e4SLinus Torvalds return -EFAULT; 12741da177e4SLinus Torvalds rtnl_lock(); 12751da177e4SLinus Torvalds if (optname == MRT_DEL_MFC) 12760c12295aSPatrick McHardy ret = ipmr_mfc_delete(mrt, &mfc); 12771da177e4SLinus Torvalds else 12784c968709SEric Dumazet ret = ipmr_mfc_add(net, mrt, &mfc, 12794c968709SEric Dumazet sk == rtnl_dereference(mrt->mroute_sk)); 12801da177e4SLinus Torvalds rtnl_unlock(); 12811da177e4SLinus Torvalds return ret; 12821da177e4SLinus Torvalds /* 12831da177e4SLinus Torvalds * Control PIM assert. 12841da177e4SLinus Torvalds */ 12851da177e4SLinus Torvalds case MRT_ASSERT: 12861da177e4SLinus Torvalds { 12871da177e4SLinus Torvalds int v; 1288*5e1859fbSEric Dumazet if (optlen != sizeof(v)) 1289*5e1859fbSEric Dumazet return -EINVAL; 12901da177e4SLinus Torvalds if (get_user(v, (int __user *)optval)) 12911da177e4SLinus Torvalds return -EFAULT; 1292*5e1859fbSEric Dumazet mrt->mroute_do_assert = !!v; 12931da177e4SLinus Torvalds return 0; 12941da177e4SLinus Torvalds } 12951da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 12961da177e4SLinus Torvalds case MRT_PIM: 12971da177e4SLinus Torvalds { 1298ba93ef74SStephen Hemminger int v; 1299ba93ef74SStephen Hemminger 1300*5e1859fbSEric Dumazet if (optlen != sizeof(v)) 1301*5e1859fbSEric Dumazet return -EINVAL; 13021da177e4SLinus Torvalds if (get_user(v, (int __user *)optval)) 13031da177e4SLinus Torvalds return -EFAULT; 1304*5e1859fbSEric Dumazet v = !!v; 1305ba93ef74SStephen Hemminger 13061da177e4SLinus Torvalds rtnl_lock(); 13071da177e4SLinus Torvalds ret = 0; 13080c12295aSPatrick McHardy if (v != mrt->mroute_do_pim) { 13090c12295aSPatrick McHardy mrt->mroute_do_pim = v; 13100c12295aSPatrick McHardy mrt->mroute_do_assert = v; 13111da177e4SLinus Torvalds } 13121da177e4SLinus Torvalds rtnl_unlock(); 13131da177e4SLinus Torvalds return ret; 13141da177e4SLinus Torvalds } 13151da177e4SLinus Torvalds #endif 1316f0ad0860SPatrick McHardy #ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 1317f0ad0860SPatrick McHardy case MRT_TABLE: 1318f0ad0860SPatrick McHardy { 1319f0ad0860SPatrick McHardy u32 v; 1320f0ad0860SPatrick McHardy 1321f0ad0860SPatrick McHardy if (optlen != sizeof(u32)) 1322f0ad0860SPatrick McHardy return -EINVAL; 1323f0ad0860SPatrick McHardy if (get_user(v, (u32 __user *)optval)) 1324f0ad0860SPatrick McHardy return -EFAULT; 1325f0ad0860SPatrick McHardy 1326f0ad0860SPatrick McHardy rtnl_lock(); 1327f0ad0860SPatrick McHardy ret = 0; 13284c968709SEric Dumazet if (sk == rtnl_dereference(mrt->mroute_sk)) { 13294c968709SEric Dumazet ret = -EBUSY; 13304c968709SEric Dumazet } else { 1331f0ad0860SPatrick McHardy if (!ipmr_new_table(net, v)) 1332f0ad0860SPatrick McHardy ret = -ENOMEM; 1333*5e1859fbSEric Dumazet else 1334f0ad0860SPatrick McHardy raw_sk(sk)->ipmr_table = v; 13354c968709SEric Dumazet } 1336f0ad0860SPatrick McHardy rtnl_unlock(); 1337f0ad0860SPatrick McHardy return ret; 1338f0ad0860SPatrick McHardy } 1339f0ad0860SPatrick McHardy #endif 13401da177e4SLinus Torvalds /* 13411da177e4SLinus Torvalds * Spurious command, or MRT_VERSION which you cannot 13421da177e4SLinus Torvalds * set. 13431da177e4SLinus Torvalds */ 13441da177e4SLinus Torvalds default: 13451da177e4SLinus Torvalds return -ENOPROTOOPT; 13461da177e4SLinus Torvalds } 13471da177e4SLinus Torvalds } 13481da177e4SLinus Torvalds 13491da177e4SLinus Torvalds /* 13501da177e4SLinus Torvalds * Getsock opt support for the multicast routing system. 13511da177e4SLinus Torvalds */ 13521da177e4SLinus Torvalds 13531da177e4SLinus Torvalds int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen) 13541da177e4SLinus Torvalds { 13551da177e4SLinus Torvalds int olr; 13561da177e4SLinus Torvalds int val; 13574feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1358f0ad0860SPatrick McHardy struct mr_table *mrt; 1359f0ad0860SPatrick McHardy 1360*5e1859fbSEric Dumazet if (sk->sk_type != SOCK_RAW || 1361*5e1859fbSEric Dumazet inet_sk(sk)->inet_num != IPPROTO_IGMP) 1362*5e1859fbSEric Dumazet return -EOPNOTSUPP; 1363*5e1859fbSEric Dumazet 1364f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 1365f0ad0860SPatrick McHardy if (mrt == NULL) 1366f0ad0860SPatrick McHardy return -ENOENT; 13671da177e4SLinus Torvalds 13681da177e4SLinus Torvalds if (optname != MRT_VERSION && 13691da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 13701da177e4SLinus Torvalds optname != MRT_PIM && 13711da177e4SLinus Torvalds #endif 13721da177e4SLinus Torvalds optname != MRT_ASSERT) 13731da177e4SLinus Torvalds return -ENOPROTOOPT; 13741da177e4SLinus Torvalds 13751da177e4SLinus Torvalds if (get_user(olr, optlen)) 13761da177e4SLinus Torvalds return -EFAULT; 13771da177e4SLinus Torvalds 13781da177e4SLinus Torvalds olr = min_t(unsigned int, olr, sizeof(int)); 13791da177e4SLinus Torvalds if (olr < 0) 13801da177e4SLinus Torvalds return -EINVAL; 13811da177e4SLinus Torvalds 13821da177e4SLinus Torvalds if (put_user(olr, optlen)) 13831da177e4SLinus Torvalds return -EFAULT; 13841da177e4SLinus Torvalds if (optname == MRT_VERSION) 13851da177e4SLinus Torvalds val = 0x0305; 13861da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 13871da177e4SLinus Torvalds else if (optname == MRT_PIM) 13880c12295aSPatrick McHardy val = mrt->mroute_do_pim; 13891da177e4SLinus Torvalds #endif 13901da177e4SLinus Torvalds else 13910c12295aSPatrick McHardy val = mrt->mroute_do_assert; 13921da177e4SLinus Torvalds if (copy_to_user(optval, &val, olr)) 13931da177e4SLinus Torvalds return -EFAULT; 13941da177e4SLinus Torvalds return 0; 13951da177e4SLinus Torvalds } 13961da177e4SLinus Torvalds 13971da177e4SLinus Torvalds /* 13981da177e4SLinus Torvalds * The IP multicast ioctl support routines. 13991da177e4SLinus Torvalds */ 14001da177e4SLinus Torvalds 14011da177e4SLinus Torvalds int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) 14021da177e4SLinus Torvalds { 14031da177e4SLinus Torvalds struct sioc_sg_req sr; 14041da177e4SLinus Torvalds struct sioc_vif_req vr; 14051da177e4SLinus Torvalds struct vif_device *vif; 14061da177e4SLinus Torvalds struct mfc_cache *c; 14074feb88e5SBenjamin Thery struct net *net = sock_net(sk); 1408f0ad0860SPatrick McHardy struct mr_table *mrt; 1409f0ad0860SPatrick McHardy 1410f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 1411f0ad0860SPatrick McHardy if (mrt == NULL) 1412f0ad0860SPatrick McHardy return -ENOENT; 14131da177e4SLinus Torvalds 1414132adf54SStephen Hemminger switch (cmd) { 14151da177e4SLinus Torvalds case SIOCGETVIFCNT: 14161da177e4SLinus Torvalds if (copy_from_user(&vr, arg, sizeof(vr))) 14171da177e4SLinus Torvalds return -EFAULT; 14180c12295aSPatrick McHardy if (vr.vifi >= mrt->maxvif) 14191da177e4SLinus Torvalds return -EINVAL; 14201da177e4SLinus Torvalds read_lock(&mrt_lock); 14210c12295aSPatrick McHardy vif = &mrt->vif_table[vr.vifi]; 14220c12295aSPatrick McHardy if (VIF_EXISTS(mrt, vr.vifi)) { 14231da177e4SLinus Torvalds vr.icount = vif->pkt_in; 14241da177e4SLinus Torvalds vr.ocount = vif->pkt_out; 14251da177e4SLinus Torvalds vr.ibytes = vif->bytes_in; 14261da177e4SLinus Torvalds vr.obytes = vif->bytes_out; 14271da177e4SLinus Torvalds read_unlock(&mrt_lock); 14281da177e4SLinus Torvalds 14291da177e4SLinus Torvalds if (copy_to_user(arg, &vr, sizeof(vr))) 14301da177e4SLinus Torvalds return -EFAULT; 14311da177e4SLinus Torvalds return 0; 14321da177e4SLinus Torvalds } 14331da177e4SLinus Torvalds read_unlock(&mrt_lock); 14341da177e4SLinus Torvalds return -EADDRNOTAVAIL; 14351da177e4SLinus Torvalds case SIOCGETSGCNT: 14361da177e4SLinus Torvalds if (copy_from_user(&sr, arg, sizeof(sr))) 14371da177e4SLinus Torvalds return -EFAULT; 14381da177e4SLinus Torvalds 1439a8c9486bSEric Dumazet rcu_read_lock(); 14400c12295aSPatrick McHardy c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); 14411da177e4SLinus Torvalds if (c) { 14421da177e4SLinus Torvalds sr.pktcnt = c->mfc_un.res.pkt; 14431da177e4SLinus Torvalds sr.bytecnt = c->mfc_un.res.bytes; 14441da177e4SLinus Torvalds sr.wrong_if = c->mfc_un.res.wrong_if; 1445a8c9486bSEric Dumazet rcu_read_unlock(); 14461da177e4SLinus Torvalds 14471da177e4SLinus Torvalds if (copy_to_user(arg, &sr, sizeof(sr))) 14481da177e4SLinus Torvalds return -EFAULT; 14491da177e4SLinus Torvalds return 0; 14501da177e4SLinus Torvalds } 1451a8c9486bSEric Dumazet rcu_read_unlock(); 14521da177e4SLinus Torvalds return -EADDRNOTAVAIL; 14531da177e4SLinus Torvalds default: 14541da177e4SLinus Torvalds return -ENOIOCTLCMD; 14551da177e4SLinus Torvalds } 14561da177e4SLinus Torvalds } 14571da177e4SLinus Torvalds 1458709b46e8SEric W. Biederman #ifdef CONFIG_COMPAT 1459709b46e8SEric W. Biederman struct compat_sioc_sg_req { 1460709b46e8SEric W. Biederman struct in_addr src; 1461709b46e8SEric W. Biederman struct in_addr grp; 1462709b46e8SEric W. Biederman compat_ulong_t pktcnt; 1463709b46e8SEric W. Biederman compat_ulong_t bytecnt; 1464709b46e8SEric W. Biederman compat_ulong_t wrong_if; 1465709b46e8SEric W. Biederman }; 1466709b46e8SEric W. Biederman 1467ca6b8bb0SDavid S. Miller struct compat_sioc_vif_req { 1468ca6b8bb0SDavid S. Miller vifi_t vifi; /* Which iface */ 1469ca6b8bb0SDavid S. Miller compat_ulong_t icount; 1470ca6b8bb0SDavid S. Miller compat_ulong_t ocount; 1471ca6b8bb0SDavid S. Miller compat_ulong_t ibytes; 1472ca6b8bb0SDavid S. Miller compat_ulong_t obytes; 1473ca6b8bb0SDavid S. Miller }; 1474ca6b8bb0SDavid S. Miller 1475709b46e8SEric W. Biederman int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) 1476709b46e8SEric W. Biederman { 14770033d5adSDavid S. Miller struct compat_sioc_sg_req sr; 1478ca6b8bb0SDavid S. Miller struct compat_sioc_vif_req vr; 1479ca6b8bb0SDavid S. Miller struct vif_device *vif; 1480709b46e8SEric W. Biederman struct mfc_cache *c; 1481709b46e8SEric W. Biederman struct net *net = sock_net(sk); 1482709b46e8SEric W. Biederman struct mr_table *mrt; 1483709b46e8SEric W. Biederman 1484709b46e8SEric W. Biederman mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 1485709b46e8SEric W. Biederman if (mrt == NULL) 1486709b46e8SEric W. Biederman return -ENOENT; 1487709b46e8SEric W. Biederman 1488709b46e8SEric W. Biederman switch (cmd) { 1489ca6b8bb0SDavid S. Miller case SIOCGETVIFCNT: 1490ca6b8bb0SDavid S. Miller if (copy_from_user(&vr, arg, sizeof(vr))) 1491ca6b8bb0SDavid S. Miller return -EFAULT; 1492ca6b8bb0SDavid S. Miller if (vr.vifi >= mrt->maxvif) 1493ca6b8bb0SDavid S. Miller return -EINVAL; 1494ca6b8bb0SDavid S. Miller read_lock(&mrt_lock); 1495ca6b8bb0SDavid S. Miller vif = &mrt->vif_table[vr.vifi]; 1496ca6b8bb0SDavid S. Miller if (VIF_EXISTS(mrt, vr.vifi)) { 1497ca6b8bb0SDavid S. Miller vr.icount = vif->pkt_in; 1498ca6b8bb0SDavid S. Miller vr.ocount = vif->pkt_out; 1499ca6b8bb0SDavid S. Miller vr.ibytes = vif->bytes_in; 1500ca6b8bb0SDavid S. Miller vr.obytes = vif->bytes_out; 1501ca6b8bb0SDavid S. Miller read_unlock(&mrt_lock); 1502ca6b8bb0SDavid S. Miller 1503ca6b8bb0SDavid S. Miller if (copy_to_user(arg, &vr, sizeof(vr))) 1504ca6b8bb0SDavid S. Miller return -EFAULT; 1505ca6b8bb0SDavid S. Miller return 0; 1506ca6b8bb0SDavid S. Miller } 1507ca6b8bb0SDavid S. Miller read_unlock(&mrt_lock); 1508ca6b8bb0SDavid S. Miller return -EADDRNOTAVAIL; 1509709b46e8SEric W. Biederman case SIOCGETSGCNT: 1510709b46e8SEric W. Biederman if (copy_from_user(&sr, arg, sizeof(sr))) 1511709b46e8SEric W. Biederman return -EFAULT; 1512709b46e8SEric W. Biederman 1513709b46e8SEric W. Biederman rcu_read_lock(); 1514709b46e8SEric W. Biederman c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); 1515709b46e8SEric W. Biederman if (c) { 1516709b46e8SEric W. Biederman sr.pktcnt = c->mfc_un.res.pkt; 1517709b46e8SEric W. Biederman sr.bytecnt = c->mfc_un.res.bytes; 1518709b46e8SEric W. Biederman sr.wrong_if = c->mfc_un.res.wrong_if; 1519709b46e8SEric W. Biederman rcu_read_unlock(); 1520709b46e8SEric W. Biederman 1521709b46e8SEric W. Biederman if (copy_to_user(arg, &sr, sizeof(sr))) 1522709b46e8SEric W. Biederman return -EFAULT; 1523709b46e8SEric W. Biederman return 0; 1524709b46e8SEric W. Biederman } 1525709b46e8SEric W. Biederman rcu_read_unlock(); 1526709b46e8SEric W. Biederman return -EADDRNOTAVAIL; 1527709b46e8SEric W. Biederman default: 1528709b46e8SEric W. Biederman return -ENOIOCTLCMD; 1529709b46e8SEric W. Biederman } 1530709b46e8SEric W. Biederman } 1531709b46e8SEric W. Biederman #endif 1532709b46e8SEric W. Biederman 15331da177e4SLinus Torvalds 15341da177e4SLinus Torvalds static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr) 15351da177e4SLinus Torvalds { 1536e9dc8653SEric W. Biederman struct net_device *dev = ptr; 15374feb88e5SBenjamin Thery struct net *net = dev_net(dev); 1538f0ad0860SPatrick McHardy struct mr_table *mrt; 15391da177e4SLinus Torvalds struct vif_device *v; 15401da177e4SLinus Torvalds int ct; 1541e9dc8653SEric W. Biederman 15421da177e4SLinus Torvalds if (event != NETDEV_UNREGISTER) 15431da177e4SLinus Torvalds return NOTIFY_DONE; 1544f0ad0860SPatrick McHardy 1545f0ad0860SPatrick McHardy ipmr_for_each_table(mrt, net) { 15460c12295aSPatrick McHardy v = &mrt->vif_table[0]; 15470c12295aSPatrick McHardy for (ct = 0; ct < mrt->maxvif; ct++, v++) { 1548e9dc8653SEric W. Biederman if (v->dev == dev) 1549e92036a6SRongQing.Li vif_delete(mrt, ct, 1, NULL); 15501da177e4SLinus Torvalds } 15511da177e4SLinus Torvalds } 15521da177e4SLinus Torvalds return NOTIFY_DONE; 15531da177e4SLinus Torvalds } 15541da177e4SLinus Torvalds 15551da177e4SLinus Torvalds 15561da177e4SLinus Torvalds static struct notifier_block ip_mr_notifier = { 15571da177e4SLinus Torvalds .notifier_call = ipmr_device_event, 15581da177e4SLinus Torvalds }; 15591da177e4SLinus Torvalds 15601da177e4SLinus Torvalds /* 15611da177e4SLinus Torvalds * Encapsulate a packet by attaching a valid IPIP header to it. 15621da177e4SLinus Torvalds * This avoids tunnel drivers and other mess and gives us the speed so 15631da177e4SLinus Torvalds * important for multicast video. 15641da177e4SLinus Torvalds */ 15651da177e4SLinus Torvalds 1566114c7844SAl Viro static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr) 15671da177e4SLinus Torvalds { 15688856dfa3SArnaldo Carvalho de Melo struct iphdr *iph; 1569b71d1d42SEric Dumazet const struct iphdr *old_iph = ip_hdr(skb); 15708856dfa3SArnaldo Carvalho de Melo 15718856dfa3SArnaldo Carvalho de Melo skb_push(skb, sizeof(struct iphdr)); 1572b0e380b1SArnaldo Carvalho de Melo skb->transport_header = skb->network_header; 15738856dfa3SArnaldo Carvalho de Melo skb_reset_network_header(skb); 1574eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb); 15751da177e4SLinus Torvalds 15761da177e4SLinus Torvalds iph->version = 4; 1577e023dd64SArnaldo Carvalho de Melo iph->tos = old_iph->tos; 1578e023dd64SArnaldo Carvalho de Melo iph->ttl = old_iph->ttl; 15791da177e4SLinus Torvalds iph->frag_off = 0; 15801da177e4SLinus Torvalds iph->daddr = daddr; 15811da177e4SLinus Torvalds iph->saddr = saddr; 15821da177e4SLinus Torvalds iph->protocol = IPPROTO_IPIP; 15831da177e4SLinus Torvalds iph->ihl = 5; 15841da177e4SLinus Torvalds iph->tot_len = htons(skb->len); 1585adf30907SEric Dumazet ip_select_ident(iph, skb_dst(skb), NULL); 15861da177e4SLinus Torvalds ip_send_check(iph); 15871da177e4SLinus Torvalds 15881da177e4SLinus Torvalds memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 15891da177e4SLinus Torvalds nf_reset(skb); 15901da177e4SLinus Torvalds } 15911da177e4SLinus Torvalds 15921da177e4SLinus Torvalds static inline int ipmr_forward_finish(struct sk_buff *skb) 15931da177e4SLinus Torvalds { 15941da177e4SLinus Torvalds struct ip_options *opt = &(IPCB(skb)->opt); 15951da177e4SLinus Torvalds 1596adf30907SEric Dumazet IP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTFORWDATAGRAMS); 15972d8dbb04SVincent Bernat IP_ADD_STATS_BH(dev_net(skb_dst(skb)->dev), IPSTATS_MIB_OUTOCTETS, skb->len); 15981da177e4SLinus Torvalds 15991da177e4SLinus Torvalds if (unlikely(opt->optlen)) 16001da177e4SLinus Torvalds ip_forward_options(skb); 16011da177e4SLinus Torvalds 16021da177e4SLinus Torvalds return dst_output(skb); 16031da177e4SLinus Torvalds } 16041da177e4SLinus Torvalds 16051da177e4SLinus Torvalds /* 16061da177e4SLinus Torvalds * Processing handlers for ipmr_forward 16071da177e4SLinus Torvalds */ 16081da177e4SLinus Torvalds 16090c12295aSPatrick McHardy static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, 16100c12295aSPatrick McHardy struct sk_buff *skb, struct mfc_cache *c, int vifi) 16111da177e4SLinus Torvalds { 1612eddc9ec5SArnaldo Carvalho de Melo const struct iphdr *iph = ip_hdr(skb); 16130c12295aSPatrick McHardy struct vif_device *vif = &mrt->vif_table[vifi]; 16141da177e4SLinus Torvalds struct net_device *dev; 16151da177e4SLinus Torvalds struct rtable *rt; 161631e4543dSDavid S. Miller struct flowi4 fl4; 16171da177e4SLinus Torvalds int encap = 0; 16181da177e4SLinus Torvalds 16191da177e4SLinus Torvalds if (vif->dev == NULL) 16201da177e4SLinus Torvalds goto out_free; 16211da177e4SLinus Torvalds 16221da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM 16231da177e4SLinus Torvalds if (vif->flags & VIFF_REGISTER) { 16241da177e4SLinus Torvalds vif->pkt_out++; 16251da177e4SLinus Torvalds vif->bytes_out += skb->len; 1626cf3677aeSPavel Emelyanov vif->dev->stats.tx_bytes += skb->len; 1627cf3677aeSPavel Emelyanov vif->dev->stats.tx_packets++; 16280c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT); 162969ebbf58SIlpo Järvinen goto out_free; 16301da177e4SLinus Torvalds } 16311da177e4SLinus Torvalds #endif 16321da177e4SLinus Torvalds 16331da177e4SLinus Torvalds if (vif->flags & VIFF_TUNNEL) { 163431e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, 163578fbfd8aSDavid S. Miller vif->remote, vif->local, 163678fbfd8aSDavid S. Miller 0, 0, 163778fbfd8aSDavid S. Miller IPPROTO_IPIP, 163878fbfd8aSDavid S. Miller RT_TOS(iph->tos), vif->link); 1639b23dd4feSDavid S. Miller if (IS_ERR(rt)) 16401da177e4SLinus Torvalds goto out_free; 16411da177e4SLinus Torvalds encap = sizeof(struct iphdr); 16421da177e4SLinus Torvalds } else { 164331e4543dSDavid S. Miller rt = ip_route_output_ports(net, &fl4, NULL, iph->daddr, 0, 164478fbfd8aSDavid S. Miller 0, 0, 164578fbfd8aSDavid S. Miller IPPROTO_IPIP, 164678fbfd8aSDavid S. Miller RT_TOS(iph->tos), vif->link); 1647b23dd4feSDavid S. Miller if (IS_ERR(rt)) 16481da177e4SLinus Torvalds goto out_free; 16491da177e4SLinus Torvalds } 16501da177e4SLinus Torvalds 1651d8d1f30bSChangli Gao dev = rt->dst.dev; 16521da177e4SLinus Torvalds 1653d8d1f30bSChangli Gao if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) { 16541da177e4SLinus Torvalds /* Do not fragment multicasts. Alas, IPv4 does not 1655a8cb16ddSEric Dumazet * allow to send ICMP, so that packets will disappear 1656a8cb16ddSEric Dumazet * to blackhole. 16571da177e4SLinus Torvalds */ 16581da177e4SLinus Torvalds 16597c73a6faSPavel Emelyanov IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_FRAGFAILS); 16601da177e4SLinus Torvalds ip_rt_put(rt); 16611da177e4SLinus Torvalds goto out_free; 16621da177e4SLinus Torvalds } 16631da177e4SLinus Torvalds 1664d8d1f30bSChangli Gao encap += LL_RESERVED_SPACE(dev) + rt->dst.header_len; 16651da177e4SLinus Torvalds 16661da177e4SLinus Torvalds if (skb_cow(skb, encap)) { 16671da177e4SLinus Torvalds ip_rt_put(rt); 16681da177e4SLinus Torvalds goto out_free; 16691da177e4SLinus Torvalds } 16701da177e4SLinus Torvalds 16711da177e4SLinus Torvalds vif->pkt_out++; 16721da177e4SLinus Torvalds vif->bytes_out += skb->len; 16731da177e4SLinus Torvalds 1674adf30907SEric Dumazet skb_dst_drop(skb); 1675d8d1f30bSChangli Gao skb_dst_set(skb, &rt->dst); 1676eddc9ec5SArnaldo Carvalho de Melo ip_decrease_ttl(ip_hdr(skb)); 16771da177e4SLinus Torvalds 16781da177e4SLinus Torvalds /* FIXME: forward and output firewalls used to be called here. 1679a8cb16ddSEric Dumazet * What do we do with netfilter? -- RR 1680a8cb16ddSEric Dumazet */ 16811da177e4SLinus Torvalds if (vif->flags & VIFF_TUNNEL) { 16821da177e4SLinus Torvalds ip_encap(skb, vif->local, vif->remote); 16831da177e4SLinus Torvalds /* FIXME: extra output firewall step used to be here. --RR */ 16842f4c02d4SPavel Emelyanov vif->dev->stats.tx_packets++; 16852f4c02d4SPavel Emelyanov vif->dev->stats.tx_bytes += skb->len; 16861da177e4SLinus Torvalds } 16871da177e4SLinus Torvalds 16881da177e4SLinus Torvalds IPCB(skb)->flags |= IPSKB_FORWARDED; 16891da177e4SLinus Torvalds 16901da177e4SLinus Torvalds /* 16911da177e4SLinus Torvalds * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally 16921da177e4SLinus Torvalds * not only before forwarding, but after forwarding on all output 16931da177e4SLinus Torvalds * interfaces. It is clear, if mrouter runs a multicasting 16941da177e4SLinus Torvalds * program, it should receive packets not depending to what interface 16951da177e4SLinus Torvalds * program is joined. 16961da177e4SLinus Torvalds * If we will not make it, the program will have to join on all 16971da177e4SLinus Torvalds * interfaces. On the other hand, multihoming host (or router, but 16981da177e4SLinus Torvalds * not mrouter) cannot join to more than one interface - it will 16991da177e4SLinus Torvalds * result in receiving multiple packets. 17001da177e4SLinus Torvalds */ 17019bbc768aSJan Engelhardt NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, skb, skb->dev, dev, 17021da177e4SLinus Torvalds ipmr_forward_finish); 17031da177e4SLinus Torvalds return; 17041da177e4SLinus Torvalds 17051da177e4SLinus Torvalds out_free: 17061da177e4SLinus Torvalds kfree_skb(skb); 17071da177e4SLinus Torvalds } 17081da177e4SLinus Torvalds 17090c12295aSPatrick McHardy static int ipmr_find_vif(struct mr_table *mrt, struct net_device *dev) 17101da177e4SLinus Torvalds { 17111da177e4SLinus Torvalds int ct; 17120c12295aSPatrick McHardy 17130c12295aSPatrick McHardy for (ct = mrt->maxvif-1; ct >= 0; ct--) { 17140c12295aSPatrick McHardy if (mrt->vif_table[ct].dev == dev) 17151da177e4SLinus Torvalds break; 17161da177e4SLinus Torvalds } 17171da177e4SLinus Torvalds return ct; 17181da177e4SLinus Torvalds } 17191da177e4SLinus Torvalds 17201da177e4SLinus Torvalds /* "local" means that we should preserve one skb (for local delivery) */ 17211da177e4SLinus Torvalds 17220c12295aSPatrick McHardy static int ip_mr_forward(struct net *net, struct mr_table *mrt, 17230c12295aSPatrick McHardy struct sk_buff *skb, struct mfc_cache *cache, 17240c12295aSPatrick McHardy int local) 17251da177e4SLinus Torvalds { 17261da177e4SLinus Torvalds int psend = -1; 17271da177e4SLinus Torvalds int vif, ct; 17281da177e4SLinus Torvalds 17291da177e4SLinus Torvalds vif = cache->mfc_parent; 17301da177e4SLinus Torvalds cache->mfc_un.res.pkt++; 17311da177e4SLinus Torvalds cache->mfc_un.res.bytes += skb->len; 17321da177e4SLinus Torvalds 17331da177e4SLinus Torvalds /* 17341da177e4SLinus Torvalds * Wrong interface: drop packet and (maybe) send PIM assert. 17351da177e4SLinus Torvalds */ 17360c12295aSPatrick McHardy if (mrt->vif_table[vif].dev != skb->dev) { 17371da177e4SLinus Torvalds int true_vifi; 17381da177e4SLinus Torvalds 1739c7537967SDavid S. Miller if (rt_is_output_route(skb_rtable(skb))) { 17401da177e4SLinus Torvalds /* It is our own packet, looped back. 1741a8cb16ddSEric Dumazet * Very complicated situation... 1742a8cb16ddSEric Dumazet * 1743a8cb16ddSEric Dumazet * The best workaround until routing daemons will be 1744a8cb16ddSEric Dumazet * fixed is not to redistribute packet, if it was 1745a8cb16ddSEric Dumazet * send through wrong interface. It means, that 1746a8cb16ddSEric Dumazet * multicast applications WILL NOT work for 1747a8cb16ddSEric Dumazet * (S,G), which have default multicast route pointing 1748a8cb16ddSEric Dumazet * to wrong oif. In any case, it is not a good 1749a8cb16ddSEric Dumazet * idea to use multicasting applications on router. 17501da177e4SLinus Torvalds */ 17511da177e4SLinus Torvalds goto dont_forward; 17521da177e4SLinus Torvalds } 17531da177e4SLinus Torvalds 17541da177e4SLinus Torvalds cache->mfc_un.res.wrong_if++; 17550c12295aSPatrick McHardy true_vifi = ipmr_find_vif(mrt, skb->dev); 17561da177e4SLinus Torvalds 17570c12295aSPatrick McHardy if (true_vifi >= 0 && mrt->mroute_do_assert && 17581da177e4SLinus Torvalds /* pimsm uses asserts, when switching from RPT to SPT, 1759a8cb16ddSEric Dumazet * so that we cannot check that packet arrived on an oif. 1760a8cb16ddSEric Dumazet * It is bad, but otherwise we would need to move pretty 1761a8cb16ddSEric Dumazet * large chunk of pimd to kernel. Ough... --ANK 17621da177e4SLinus Torvalds */ 17630c12295aSPatrick McHardy (mrt->mroute_do_pim || 17646f9374a9SBenjamin Thery cache->mfc_un.res.ttls[true_vifi] < 255) && 17651da177e4SLinus Torvalds time_after(jiffies, 17661da177e4SLinus Torvalds cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) { 17671da177e4SLinus Torvalds cache->mfc_un.res.last_assert = jiffies; 17680c12295aSPatrick McHardy ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF); 17691da177e4SLinus Torvalds } 17701da177e4SLinus Torvalds goto dont_forward; 17711da177e4SLinus Torvalds } 17721da177e4SLinus Torvalds 17730c12295aSPatrick McHardy mrt->vif_table[vif].pkt_in++; 17740c12295aSPatrick McHardy mrt->vif_table[vif].bytes_in += skb->len; 17751da177e4SLinus Torvalds 17761da177e4SLinus Torvalds /* 17771da177e4SLinus Torvalds * Forward the frame 17781da177e4SLinus Torvalds */ 1779a8cb16ddSEric Dumazet for (ct = cache->mfc_un.res.maxvif - 1; 1780a8cb16ddSEric Dumazet ct >= cache->mfc_un.res.minvif; ct--) { 1781eddc9ec5SArnaldo Carvalho de Melo if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) { 17821da177e4SLinus Torvalds if (psend != -1) { 17831da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 1784a8cb16ddSEric Dumazet 17851da177e4SLinus Torvalds if (skb2) 17860c12295aSPatrick McHardy ipmr_queue_xmit(net, mrt, skb2, cache, 17870c12295aSPatrick McHardy psend); 17881da177e4SLinus Torvalds } 17891da177e4SLinus Torvalds psend = ct; 17901da177e4SLinus Torvalds } 17911da177e4SLinus Torvalds } 17921da177e4SLinus Torvalds if (psend != -1) { 17931da177e4SLinus Torvalds if (local) { 17941da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 1795a8cb16ddSEric Dumazet 17961da177e4SLinus Torvalds if (skb2) 17970c12295aSPatrick McHardy ipmr_queue_xmit(net, mrt, skb2, cache, psend); 17981da177e4SLinus Torvalds } else { 17990c12295aSPatrick McHardy ipmr_queue_xmit(net, mrt, skb, cache, psend); 18001da177e4SLinus Torvalds return 0; 18011da177e4SLinus Torvalds } 18021da177e4SLinus Torvalds } 18031da177e4SLinus Torvalds 18041da177e4SLinus Torvalds dont_forward: 18051da177e4SLinus Torvalds if (!local) 18061da177e4SLinus Torvalds kfree_skb(skb); 18071da177e4SLinus Torvalds return 0; 18081da177e4SLinus Torvalds } 18091da177e4SLinus Torvalds 1810417da66fSDavid S. Miller static struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb) 1811ee3f1aafSDavid S. Miller { 1812417da66fSDavid S. Miller struct rtable *rt = skb_rtable(skb); 1813417da66fSDavid S. Miller struct iphdr *iph = ip_hdr(skb); 1814da91981bSDavid S. Miller struct flowi4 fl4 = { 1815417da66fSDavid S. Miller .daddr = iph->daddr, 1816417da66fSDavid S. Miller .saddr = iph->saddr, 1817b0fe4a31SJulian Anastasov .flowi4_tos = RT_TOS(iph->tos), 18184fd551d7SDavid S. Miller .flowi4_oif = (rt_is_output_route(rt) ? 18194fd551d7SDavid S. Miller skb->dev->ifindex : 0), 18204fd551d7SDavid S. Miller .flowi4_iif = (rt_is_output_route(rt) ? 18211fb9489bSPavel Emelyanov LOOPBACK_IFINDEX : 18224fd551d7SDavid S. Miller skb->dev->ifindex), 1823b4869889SDavid Miller .flowi4_mark = skb->mark, 1824ee3f1aafSDavid S. Miller }; 1825ee3f1aafSDavid S. Miller struct mr_table *mrt; 1826ee3f1aafSDavid S. Miller int err; 1827ee3f1aafSDavid S. Miller 1828da91981bSDavid S. Miller err = ipmr_fib_lookup(net, &fl4, &mrt); 1829ee3f1aafSDavid S. Miller if (err) 1830ee3f1aafSDavid S. Miller return ERR_PTR(err); 1831ee3f1aafSDavid S. Miller return mrt; 1832ee3f1aafSDavid S. Miller } 18331da177e4SLinus Torvalds 18341da177e4SLinus Torvalds /* 18351da177e4SLinus Torvalds * Multicast packets for forwarding arrive here 18364c968709SEric Dumazet * Called with rcu_read_lock(); 18371da177e4SLinus Torvalds */ 18381da177e4SLinus Torvalds 18391da177e4SLinus Torvalds int ip_mr_input(struct sk_buff *skb) 18401da177e4SLinus Torvalds { 18411da177e4SLinus Torvalds struct mfc_cache *cache; 18424feb88e5SBenjamin Thery struct net *net = dev_net(skb->dev); 1843511c3f92SEric Dumazet int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL; 1844f0ad0860SPatrick McHardy struct mr_table *mrt; 18451da177e4SLinus Torvalds 18461da177e4SLinus Torvalds /* Packet is looped back after forward, it should not be 1847a8cb16ddSEric Dumazet * forwarded second time, but still can be delivered locally. 18481da177e4SLinus Torvalds */ 18491da177e4SLinus Torvalds if (IPCB(skb)->flags & IPSKB_FORWARDED) 18501da177e4SLinus Torvalds goto dont_forward; 18511da177e4SLinus Torvalds 1852417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 1853ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) { 1854e40dbc51SBen Greear kfree_skb(skb); 1855ee3f1aafSDavid S. Miller return PTR_ERR(mrt); 18565e2b61f7SDavid S. Miller } 18571da177e4SLinus Torvalds if (!local) { 18581da177e4SLinus Torvalds if (IPCB(skb)->opt.router_alert) { 18591da177e4SLinus Torvalds if (ip_call_ra_chain(skb)) 18601da177e4SLinus Torvalds return 0; 1861eddc9ec5SArnaldo Carvalho de Melo } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP) { 18621da177e4SLinus Torvalds /* IGMPv1 (and broken IGMPv2 implementations sort of 18634c968709SEric Dumazet * Cisco IOS <= 11.2(8)) do not put router alert 18644c968709SEric Dumazet * option to IGMP packets destined to routable 18654c968709SEric Dumazet * groups. It is very bad, because it means 18664c968709SEric Dumazet * that we can forward NO IGMP messages. 18671da177e4SLinus Torvalds */ 18684c968709SEric Dumazet struct sock *mroute_sk; 18694c968709SEric Dumazet 18704c968709SEric Dumazet mroute_sk = rcu_dereference(mrt->mroute_sk); 18714c968709SEric Dumazet if (mroute_sk) { 18722715bcf9SPatrick McHardy nf_reset(skb); 18734c968709SEric Dumazet raw_rcv(mroute_sk, skb); 18741da177e4SLinus Torvalds return 0; 18751da177e4SLinus Torvalds } 18761da177e4SLinus Torvalds } 18771da177e4SLinus Torvalds } 18781da177e4SLinus Torvalds 1879a8c9486bSEric Dumazet /* already under rcu_read_lock() */ 18800c12295aSPatrick McHardy cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); 18811da177e4SLinus Torvalds 18821da177e4SLinus Torvalds /* 18831da177e4SLinus Torvalds * No usable cache entry 18841da177e4SLinus Torvalds */ 18851da177e4SLinus Torvalds if (cache == NULL) { 18861da177e4SLinus Torvalds int vif; 18871da177e4SLinus Torvalds 18881da177e4SLinus Torvalds if (local) { 18891da177e4SLinus Torvalds struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 18901da177e4SLinus Torvalds ip_local_deliver(skb); 1891a8c9486bSEric Dumazet if (skb2 == NULL) 18921da177e4SLinus Torvalds return -ENOBUFS; 18931da177e4SLinus Torvalds skb = skb2; 18941da177e4SLinus Torvalds } 18951da177e4SLinus Torvalds 1896a8c9486bSEric Dumazet read_lock(&mrt_lock); 18970c12295aSPatrick McHardy vif = ipmr_find_vif(mrt, skb->dev); 18981da177e4SLinus Torvalds if (vif >= 0) { 18990eae88f3SEric Dumazet int err2 = ipmr_cache_unresolved(mrt, vif, skb); 19001da177e4SLinus Torvalds read_unlock(&mrt_lock); 19011da177e4SLinus Torvalds 19020eae88f3SEric Dumazet return err2; 19031da177e4SLinus Torvalds } 19041da177e4SLinus Torvalds read_unlock(&mrt_lock); 19051da177e4SLinus Torvalds kfree_skb(skb); 19061da177e4SLinus Torvalds return -ENODEV; 19071da177e4SLinus Torvalds } 19081da177e4SLinus Torvalds 1909a8c9486bSEric Dumazet read_lock(&mrt_lock); 19100c12295aSPatrick McHardy ip_mr_forward(net, mrt, skb, cache, local); 19111da177e4SLinus Torvalds read_unlock(&mrt_lock); 19121da177e4SLinus Torvalds 19131da177e4SLinus Torvalds if (local) 19141da177e4SLinus Torvalds return ip_local_deliver(skb); 19151da177e4SLinus Torvalds 19161da177e4SLinus Torvalds return 0; 19171da177e4SLinus Torvalds 19181da177e4SLinus Torvalds dont_forward: 19191da177e4SLinus Torvalds if (local) 19201da177e4SLinus Torvalds return ip_local_deliver(skb); 19211da177e4SLinus Torvalds kfree_skb(skb); 19221da177e4SLinus Torvalds return 0; 19231da177e4SLinus Torvalds } 19241da177e4SLinus Torvalds 1925b1879204SIlpo Järvinen #ifdef CONFIG_IP_PIMSM 192655747a0aSEric Dumazet /* called with rcu_read_lock() */ 1927f0ad0860SPatrick McHardy static int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb, 1928f0ad0860SPatrick McHardy unsigned int pimlen) 19291da177e4SLinus Torvalds { 19301da177e4SLinus Torvalds struct net_device *reg_dev = NULL; 1931b1879204SIlpo Järvinen struct iphdr *encap; 19321da177e4SLinus Torvalds 1933b1879204SIlpo Järvinen encap = (struct iphdr *)(skb_transport_header(skb) + pimlen); 19341da177e4SLinus Torvalds /* 1935a8cb16ddSEric Dumazet * Check that: 1936a8cb16ddSEric Dumazet * a. packet is really sent to a multicast group 1937a8cb16ddSEric Dumazet * b. packet is not a NULL-REGISTER 1938a8cb16ddSEric Dumazet * c. packet is not truncated 19391da177e4SLinus Torvalds */ 1940f97c1e0cSJoe Perches if (!ipv4_is_multicast(encap->daddr) || 19411da177e4SLinus Torvalds encap->tot_len == 0 || 1942b1879204SIlpo Järvinen ntohs(encap->tot_len) + pimlen > skb->len) 1943b1879204SIlpo Järvinen return 1; 19441da177e4SLinus Torvalds 19451da177e4SLinus Torvalds read_lock(&mrt_lock); 19460c12295aSPatrick McHardy if (mrt->mroute_reg_vif_num >= 0) 19470c12295aSPatrick McHardy reg_dev = mrt->vif_table[mrt->mroute_reg_vif_num].dev; 19481da177e4SLinus Torvalds read_unlock(&mrt_lock); 19491da177e4SLinus Torvalds 19501da177e4SLinus Torvalds if (reg_dev == NULL) 1951b1879204SIlpo Järvinen return 1; 19521da177e4SLinus Torvalds 1953b0e380b1SArnaldo Carvalho de Melo skb->mac_header = skb->network_header; 19541da177e4SLinus Torvalds skb_pull(skb, (u8 *)encap - skb->data); 195531c7711bSArnaldo Carvalho de Melo skb_reset_network_header(skb); 19561da177e4SLinus Torvalds skb->protocol = htons(ETH_P_IP); 195755747a0aSEric Dumazet skb->ip_summed = CHECKSUM_NONE; 19581da177e4SLinus Torvalds skb->pkt_type = PACKET_HOST; 1959d19d56ddSEric Dumazet 1960d19d56ddSEric Dumazet skb_tunnel_rx(skb, reg_dev); 1961d19d56ddSEric Dumazet 19621da177e4SLinus Torvalds netif_rx(skb); 1963b1879204SIlpo Järvinen 196455747a0aSEric Dumazet return NET_RX_SUCCESS; 1965b1879204SIlpo Järvinen } 1966b1879204SIlpo Järvinen #endif 1967b1879204SIlpo Järvinen 1968b1879204SIlpo Järvinen #ifdef CONFIG_IP_PIMSM_V1 1969b1879204SIlpo Järvinen /* 1970b1879204SIlpo Järvinen * Handle IGMP messages of PIMv1 1971b1879204SIlpo Järvinen */ 1972b1879204SIlpo Järvinen 1973b1879204SIlpo Järvinen int pim_rcv_v1(struct sk_buff *skb) 1974b1879204SIlpo Järvinen { 1975b1879204SIlpo Järvinen struct igmphdr *pim; 19764feb88e5SBenjamin Thery struct net *net = dev_net(skb->dev); 1977f0ad0860SPatrick McHardy struct mr_table *mrt; 1978b1879204SIlpo Järvinen 1979b1879204SIlpo Järvinen if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 1980b1879204SIlpo Järvinen goto drop; 1981b1879204SIlpo Järvinen 1982b1879204SIlpo Järvinen pim = igmp_hdr(skb); 1983b1879204SIlpo Järvinen 1984417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 1985ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) 1986f0ad0860SPatrick McHardy goto drop; 19870c12295aSPatrick McHardy if (!mrt->mroute_do_pim || 1988b1879204SIlpo Järvinen pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) 1989b1879204SIlpo Järvinen goto drop; 1990b1879204SIlpo Järvinen 1991f0ad0860SPatrick McHardy if (__pim_rcv(mrt, skb, sizeof(*pim))) { 19921da177e4SLinus Torvalds drop: 19931da177e4SLinus Torvalds kfree_skb(skb); 1994b1879204SIlpo Järvinen } 19951da177e4SLinus Torvalds return 0; 19961da177e4SLinus Torvalds } 19971da177e4SLinus Torvalds #endif 19981da177e4SLinus Torvalds 19991da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V2 20001da177e4SLinus Torvalds static int pim_rcv(struct sk_buff *skb) 20011da177e4SLinus Torvalds { 20021da177e4SLinus Torvalds struct pimreghdr *pim; 2003f0ad0860SPatrick McHardy struct net *net = dev_net(skb->dev); 2004f0ad0860SPatrick McHardy struct mr_table *mrt; 20051da177e4SLinus Torvalds 2006b1879204SIlpo Järvinen if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 20071da177e4SLinus Torvalds goto drop; 20081da177e4SLinus Torvalds 20099c70220bSArnaldo Carvalho de Melo pim = (struct pimreghdr *)skb_transport_header(skb); 20101da177e4SLinus Torvalds if (pim->type != ((PIM_VERSION << 4) | (PIM_REGISTER)) || 20111da177e4SLinus Torvalds (pim->flags & PIM_NULL_REGISTER) || 20121da177e4SLinus Torvalds (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && 2013d3bc23e7SAl Viro csum_fold(skb_checksum(skb, 0, skb->len, 0)))) 20141da177e4SLinus Torvalds goto drop; 20151da177e4SLinus Torvalds 2016417da66fSDavid S. Miller mrt = ipmr_rt_fib_lookup(net, skb); 2017ee3f1aafSDavid S. Miller if (IS_ERR(mrt)) 2018f0ad0860SPatrick McHardy goto drop; 2019f0ad0860SPatrick McHardy if (__pim_rcv(mrt, skb, sizeof(*pim))) { 20201da177e4SLinus Torvalds drop: 20211da177e4SLinus Torvalds kfree_skb(skb); 2022b1879204SIlpo Järvinen } 20231da177e4SLinus Torvalds return 0; 20241da177e4SLinus Torvalds } 20251da177e4SLinus Torvalds #endif 20261da177e4SLinus Torvalds 2027cb6a4e46SPatrick McHardy static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 2028cb6a4e46SPatrick McHardy struct mfc_cache *c, struct rtmsg *rtm) 20291da177e4SLinus Torvalds { 20301da177e4SLinus Torvalds int ct; 20311da177e4SLinus Torvalds struct rtnexthop *nhp; 203292a395e5SThomas Graf struct nlattr *mp_attr; 20331da177e4SLinus Torvalds 20347438189bSNicolas Dichtel /* If cache is unresolved, don't try to parse IIF and OIF */ 2035ed0f160aSDan Carpenter if (c->mfc_parent >= MAXVIFS) 20367438189bSNicolas Dichtel return -ENOENT; 20377438189bSNicolas Dichtel 203892a395e5SThomas Graf if (VIF_EXISTS(mrt, c->mfc_parent) && 203992a395e5SThomas Graf nla_put_u32(skb, RTA_IIF, mrt->vif_table[c->mfc_parent].dev->ifindex) < 0) 204092a395e5SThomas Graf return -EMSGSIZE; 20411da177e4SLinus Torvalds 204292a395e5SThomas Graf if (!(mp_attr = nla_nest_start(skb, RTA_MULTIPATH))) 204392a395e5SThomas Graf return -EMSGSIZE; 20441da177e4SLinus Torvalds 20451da177e4SLinus Torvalds for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) { 20460c12295aSPatrick McHardy if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) { 204792a395e5SThomas Graf if (!(nhp = nla_reserve_nohdr(skb, sizeof(*nhp)))) { 204892a395e5SThomas Graf nla_nest_cancel(skb, mp_attr); 204992a395e5SThomas Graf return -EMSGSIZE; 205092a395e5SThomas Graf } 205192a395e5SThomas Graf 20521da177e4SLinus Torvalds nhp->rtnh_flags = 0; 20531da177e4SLinus Torvalds nhp->rtnh_hops = c->mfc_un.res.ttls[ct]; 20540c12295aSPatrick McHardy nhp->rtnh_ifindex = mrt->vif_table[ct].dev->ifindex; 20551da177e4SLinus Torvalds nhp->rtnh_len = sizeof(*nhp); 20561da177e4SLinus Torvalds } 20571da177e4SLinus Torvalds } 205892a395e5SThomas Graf 205992a395e5SThomas Graf nla_nest_end(skb, mp_attr); 206092a395e5SThomas Graf 20611da177e4SLinus Torvalds rtm->rtm_type = RTN_MULTICAST; 20621da177e4SLinus Torvalds return 1; 20631da177e4SLinus Torvalds } 20641da177e4SLinus Torvalds 20659a1b9496SDavid S. Miller int ipmr_get_route(struct net *net, struct sk_buff *skb, 20669a1b9496SDavid S. Miller __be32 saddr, __be32 daddr, 20679a1b9496SDavid S. Miller struct rtmsg *rtm, int nowait) 20681da177e4SLinus Torvalds { 20691da177e4SLinus Torvalds struct mfc_cache *cache; 20709a1b9496SDavid S. Miller struct mr_table *mrt; 20719a1b9496SDavid S. Miller int err; 20721da177e4SLinus Torvalds 2073f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 2074f0ad0860SPatrick McHardy if (mrt == NULL) 2075f0ad0860SPatrick McHardy return -ENOENT; 2076f0ad0860SPatrick McHardy 2077a8c9486bSEric Dumazet rcu_read_lock(); 20789a1b9496SDavid S. Miller cache = ipmr_cache_find(mrt, saddr, daddr); 20791da177e4SLinus Torvalds 20801da177e4SLinus Torvalds if (cache == NULL) { 208172287490SAlexey Kuznetsov struct sk_buff *skb2; 2082eddc9ec5SArnaldo Carvalho de Melo struct iphdr *iph; 20831da177e4SLinus Torvalds struct net_device *dev; 2084a8cb16ddSEric Dumazet int vif = -1; 20851da177e4SLinus Torvalds 20861da177e4SLinus Torvalds if (nowait) { 2087a8c9486bSEric Dumazet rcu_read_unlock(); 20881da177e4SLinus Torvalds return -EAGAIN; 20891da177e4SLinus Torvalds } 20901da177e4SLinus Torvalds 20911da177e4SLinus Torvalds dev = skb->dev; 2092a8c9486bSEric Dumazet read_lock(&mrt_lock); 2093a8cb16ddSEric Dumazet if (dev) 2094a8cb16ddSEric Dumazet vif = ipmr_find_vif(mrt, dev); 2095a8cb16ddSEric Dumazet if (vif < 0) { 20961da177e4SLinus Torvalds read_unlock(&mrt_lock); 2097a8c9486bSEric Dumazet rcu_read_unlock(); 20981da177e4SLinus Torvalds return -ENODEV; 20991da177e4SLinus Torvalds } 210072287490SAlexey Kuznetsov skb2 = skb_clone(skb, GFP_ATOMIC); 210172287490SAlexey Kuznetsov if (!skb2) { 210272287490SAlexey Kuznetsov read_unlock(&mrt_lock); 2103a8c9486bSEric Dumazet rcu_read_unlock(); 210472287490SAlexey Kuznetsov return -ENOMEM; 210572287490SAlexey Kuznetsov } 210672287490SAlexey Kuznetsov 2107e2d1bca7SArnaldo Carvalho de Melo skb_push(skb2, sizeof(struct iphdr)); 2108e2d1bca7SArnaldo Carvalho de Melo skb_reset_network_header(skb2); 2109eddc9ec5SArnaldo Carvalho de Melo iph = ip_hdr(skb2); 2110eddc9ec5SArnaldo Carvalho de Melo iph->ihl = sizeof(struct iphdr) >> 2; 21119a1b9496SDavid S. Miller iph->saddr = saddr; 21129a1b9496SDavid S. Miller iph->daddr = daddr; 2113eddc9ec5SArnaldo Carvalho de Melo iph->version = 0; 21140c12295aSPatrick McHardy err = ipmr_cache_unresolved(mrt, vif, skb2); 21151da177e4SLinus Torvalds read_unlock(&mrt_lock); 2116a8c9486bSEric Dumazet rcu_read_unlock(); 21171da177e4SLinus Torvalds return err; 21181da177e4SLinus Torvalds } 21191da177e4SLinus Torvalds 2120a8c9486bSEric Dumazet read_lock(&mrt_lock); 21211da177e4SLinus Torvalds if (!nowait && (rtm->rtm_flags & RTM_F_NOTIFY)) 21221da177e4SLinus Torvalds cache->mfc_flags |= MFC_NOTIFY; 2123cb6a4e46SPatrick McHardy err = __ipmr_fill_mroute(mrt, skb, cache, rtm); 21241da177e4SLinus Torvalds read_unlock(&mrt_lock); 2125a8c9486bSEric Dumazet rcu_read_unlock(); 21261da177e4SLinus Torvalds return err; 21271da177e4SLinus Torvalds } 21281da177e4SLinus Torvalds 2129cb6a4e46SPatrick McHardy static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 213015e47304SEric W. Biederman u32 portid, u32 seq, struct mfc_cache *c) 2131cb6a4e46SPatrick McHardy { 2132cb6a4e46SPatrick McHardy struct nlmsghdr *nlh; 2133cb6a4e46SPatrick McHardy struct rtmsg *rtm; 2134cb6a4e46SPatrick McHardy 213515e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI); 2136cb6a4e46SPatrick McHardy if (nlh == NULL) 2137cb6a4e46SPatrick McHardy return -EMSGSIZE; 2138cb6a4e46SPatrick McHardy 2139cb6a4e46SPatrick McHardy rtm = nlmsg_data(nlh); 2140cb6a4e46SPatrick McHardy rtm->rtm_family = RTNL_FAMILY_IPMR; 2141cb6a4e46SPatrick McHardy rtm->rtm_dst_len = 32; 2142cb6a4e46SPatrick McHardy rtm->rtm_src_len = 32; 2143cb6a4e46SPatrick McHardy rtm->rtm_tos = 0; 2144cb6a4e46SPatrick McHardy rtm->rtm_table = mrt->id; 2145f3756b79SDavid S. Miller if (nla_put_u32(skb, RTA_TABLE, mrt->id)) 2146f3756b79SDavid S. Miller goto nla_put_failure; 2147cb6a4e46SPatrick McHardy rtm->rtm_type = RTN_MULTICAST; 2148cb6a4e46SPatrick McHardy rtm->rtm_scope = RT_SCOPE_UNIVERSE; 2149cb6a4e46SPatrick McHardy rtm->rtm_protocol = RTPROT_UNSPEC; 2150cb6a4e46SPatrick McHardy rtm->rtm_flags = 0; 2151cb6a4e46SPatrick McHardy 2152f3756b79SDavid S. Miller if (nla_put_be32(skb, RTA_SRC, c->mfc_origin) || 2153f3756b79SDavid S. Miller nla_put_be32(skb, RTA_DST, c->mfc_mcastgrp)) 2154f3756b79SDavid S. Miller goto nla_put_failure; 2155cb6a4e46SPatrick McHardy if (__ipmr_fill_mroute(mrt, skb, c, rtm) < 0) 2156cb6a4e46SPatrick McHardy goto nla_put_failure; 2157cb6a4e46SPatrick McHardy 2158cb6a4e46SPatrick McHardy return nlmsg_end(skb, nlh); 2159cb6a4e46SPatrick McHardy 2160cb6a4e46SPatrick McHardy nla_put_failure: 2161cb6a4e46SPatrick McHardy nlmsg_cancel(skb, nlh); 2162cb6a4e46SPatrick McHardy return -EMSGSIZE; 2163cb6a4e46SPatrick McHardy } 2164cb6a4e46SPatrick McHardy 2165cb6a4e46SPatrick McHardy static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) 2166cb6a4e46SPatrick McHardy { 2167cb6a4e46SPatrick McHardy struct net *net = sock_net(skb->sk); 2168cb6a4e46SPatrick McHardy struct mr_table *mrt; 2169cb6a4e46SPatrick McHardy struct mfc_cache *mfc; 2170cb6a4e46SPatrick McHardy unsigned int t = 0, s_t; 2171cb6a4e46SPatrick McHardy unsigned int h = 0, s_h; 2172cb6a4e46SPatrick McHardy unsigned int e = 0, s_e; 2173cb6a4e46SPatrick McHardy 2174cb6a4e46SPatrick McHardy s_t = cb->args[0]; 2175cb6a4e46SPatrick McHardy s_h = cb->args[1]; 2176cb6a4e46SPatrick McHardy s_e = cb->args[2]; 2177cb6a4e46SPatrick McHardy 2178a8c9486bSEric Dumazet rcu_read_lock(); 2179cb6a4e46SPatrick McHardy ipmr_for_each_table(mrt, net) { 2180cb6a4e46SPatrick McHardy if (t < s_t) 2181cb6a4e46SPatrick McHardy goto next_table; 2182cb6a4e46SPatrick McHardy if (t > s_t) 2183cb6a4e46SPatrick McHardy s_h = 0; 2184cb6a4e46SPatrick McHardy for (h = s_h; h < MFC_LINES; h++) { 2185a8c9486bSEric Dumazet list_for_each_entry_rcu(mfc, &mrt->mfc_cache_array[h], list) { 2186cb6a4e46SPatrick McHardy if (e < s_e) 2187cb6a4e46SPatrick McHardy goto next_entry; 2188cb6a4e46SPatrick McHardy if (ipmr_fill_mroute(mrt, skb, 218915e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 2190cb6a4e46SPatrick McHardy cb->nlh->nlmsg_seq, 2191cb6a4e46SPatrick McHardy mfc) < 0) 2192cb6a4e46SPatrick McHardy goto done; 2193cb6a4e46SPatrick McHardy next_entry: 2194cb6a4e46SPatrick McHardy e++; 2195cb6a4e46SPatrick McHardy } 2196cb6a4e46SPatrick McHardy e = s_e = 0; 2197cb6a4e46SPatrick McHardy } 2198cb6a4e46SPatrick McHardy s_h = 0; 2199cb6a4e46SPatrick McHardy next_table: 2200cb6a4e46SPatrick McHardy t++; 2201cb6a4e46SPatrick McHardy } 2202cb6a4e46SPatrick McHardy done: 2203a8c9486bSEric Dumazet rcu_read_unlock(); 2204cb6a4e46SPatrick McHardy 2205cb6a4e46SPatrick McHardy cb->args[2] = e; 2206cb6a4e46SPatrick McHardy cb->args[1] = h; 2207cb6a4e46SPatrick McHardy cb->args[0] = t; 2208cb6a4e46SPatrick McHardy 2209cb6a4e46SPatrick McHardy return skb->len; 2210cb6a4e46SPatrick McHardy } 2211cb6a4e46SPatrick McHardy 22121da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 22131da177e4SLinus Torvalds /* 2214a8cb16ddSEric Dumazet * The /proc interfaces to multicast routing : 2215a8cb16ddSEric Dumazet * /proc/net/ip_mr_cache & /proc/net/ip_mr_vif 22161da177e4SLinus Torvalds */ 22171da177e4SLinus Torvalds struct ipmr_vif_iter { 2218f6bb4514SBenjamin Thery struct seq_net_private p; 2219f0ad0860SPatrick McHardy struct mr_table *mrt; 22201da177e4SLinus Torvalds int ct; 22211da177e4SLinus Torvalds }; 22221da177e4SLinus Torvalds 2223f6bb4514SBenjamin Thery static struct vif_device *ipmr_vif_seq_idx(struct net *net, 2224f6bb4514SBenjamin Thery struct ipmr_vif_iter *iter, 22251da177e4SLinus Torvalds loff_t pos) 22261da177e4SLinus Torvalds { 2227f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 22280c12295aSPatrick McHardy 22290c12295aSPatrick McHardy for (iter->ct = 0; iter->ct < mrt->maxvif; ++iter->ct) { 22300c12295aSPatrick McHardy if (!VIF_EXISTS(mrt, iter->ct)) 22311da177e4SLinus Torvalds continue; 22321da177e4SLinus Torvalds if (pos-- == 0) 22330c12295aSPatrick McHardy return &mrt->vif_table[iter->ct]; 22341da177e4SLinus Torvalds } 22351da177e4SLinus Torvalds return NULL; 22361da177e4SLinus Torvalds } 22371da177e4SLinus Torvalds 22381da177e4SLinus Torvalds static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos) 2239ba93ef74SStephen Hemminger __acquires(mrt_lock) 22401da177e4SLinus Torvalds { 2241f0ad0860SPatrick McHardy struct ipmr_vif_iter *iter = seq->private; 2242f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2243f0ad0860SPatrick McHardy struct mr_table *mrt; 2244f0ad0860SPatrick McHardy 2245f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 2246f0ad0860SPatrick McHardy if (mrt == NULL) 2247f0ad0860SPatrick McHardy return ERR_PTR(-ENOENT); 2248f0ad0860SPatrick McHardy 2249f0ad0860SPatrick McHardy iter->mrt = mrt; 2250f6bb4514SBenjamin Thery 22511da177e4SLinus Torvalds read_lock(&mrt_lock); 2252f6bb4514SBenjamin Thery return *pos ? ipmr_vif_seq_idx(net, seq->private, *pos - 1) 22531da177e4SLinus Torvalds : SEQ_START_TOKEN; 22541da177e4SLinus Torvalds } 22551da177e4SLinus Torvalds 22561da177e4SLinus Torvalds static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos) 22571da177e4SLinus Torvalds { 22581da177e4SLinus Torvalds struct ipmr_vif_iter *iter = seq->private; 2259f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2260f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 22611da177e4SLinus Torvalds 22621da177e4SLinus Torvalds ++*pos; 22631da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 2264f6bb4514SBenjamin Thery return ipmr_vif_seq_idx(net, iter, 0); 22651da177e4SLinus Torvalds 22660c12295aSPatrick McHardy while (++iter->ct < mrt->maxvif) { 22670c12295aSPatrick McHardy if (!VIF_EXISTS(mrt, iter->ct)) 22681da177e4SLinus Torvalds continue; 22690c12295aSPatrick McHardy return &mrt->vif_table[iter->ct]; 22701da177e4SLinus Torvalds } 22711da177e4SLinus Torvalds return NULL; 22721da177e4SLinus Torvalds } 22731da177e4SLinus Torvalds 22741da177e4SLinus Torvalds static void ipmr_vif_seq_stop(struct seq_file *seq, void *v) 2275ba93ef74SStephen Hemminger __releases(mrt_lock) 22761da177e4SLinus Torvalds { 22771da177e4SLinus Torvalds read_unlock(&mrt_lock); 22781da177e4SLinus Torvalds } 22791da177e4SLinus Torvalds 22801da177e4SLinus Torvalds static int ipmr_vif_seq_show(struct seq_file *seq, void *v) 22811da177e4SLinus Torvalds { 2282f0ad0860SPatrick McHardy struct ipmr_vif_iter *iter = seq->private; 2283f0ad0860SPatrick McHardy struct mr_table *mrt = iter->mrt; 2284f6bb4514SBenjamin Thery 22851da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 22861da177e4SLinus Torvalds seq_puts(seq, 22871da177e4SLinus Torvalds "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n"); 22881da177e4SLinus Torvalds } else { 22891da177e4SLinus Torvalds const struct vif_device *vif = v; 22901da177e4SLinus Torvalds const char *name = vif->dev ? vif->dev->name : "none"; 22911da177e4SLinus Torvalds 22921da177e4SLinus Torvalds seq_printf(seq, 22931da177e4SLinus Torvalds "%2Zd %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n", 22940c12295aSPatrick McHardy vif - mrt->vif_table, 22951da177e4SLinus Torvalds name, vif->bytes_in, vif->pkt_in, 22961da177e4SLinus Torvalds vif->bytes_out, vif->pkt_out, 22971da177e4SLinus Torvalds vif->flags, vif->local, vif->remote); 22981da177e4SLinus Torvalds } 22991da177e4SLinus Torvalds return 0; 23001da177e4SLinus Torvalds } 23011da177e4SLinus Torvalds 2302f690808eSStephen Hemminger static const struct seq_operations ipmr_vif_seq_ops = { 23031da177e4SLinus Torvalds .start = ipmr_vif_seq_start, 23041da177e4SLinus Torvalds .next = ipmr_vif_seq_next, 23051da177e4SLinus Torvalds .stop = ipmr_vif_seq_stop, 23061da177e4SLinus Torvalds .show = ipmr_vif_seq_show, 23071da177e4SLinus Torvalds }; 23081da177e4SLinus Torvalds 23091da177e4SLinus Torvalds static int ipmr_vif_open(struct inode *inode, struct file *file) 23101da177e4SLinus Torvalds { 2311f6bb4514SBenjamin Thery return seq_open_net(inode, file, &ipmr_vif_seq_ops, 2312cf7732e4SPavel Emelyanov sizeof(struct ipmr_vif_iter)); 23131da177e4SLinus Torvalds } 23141da177e4SLinus Torvalds 23159a32144eSArjan van de Ven static const struct file_operations ipmr_vif_fops = { 23161da177e4SLinus Torvalds .owner = THIS_MODULE, 23171da177e4SLinus Torvalds .open = ipmr_vif_open, 23181da177e4SLinus Torvalds .read = seq_read, 23191da177e4SLinus Torvalds .llseek = seq_lseek, 2320f6bb4514SBenjamin Thery .release = seq_release_net, 23211da177e4SLinus Torvalds }; 23221da177e4SLinus Torvalds 23231da177e4SLinus Torvalds struct ipmr_mfc_iter { 2324f6bb4514SBenjamin Thery struct seq_net_private p; 2325f0ad0860SPatrick McHardy struct mr_table *mrt; 2326862465f2SPatrick McHardy struct list_head *cache; 23271da177e4SLinus Torvalds int ct; 23281da177e4SLinus Torvalds }; 23291da177e4SLinus Torvalds 23301da177e4SLinus Torvalds 2331f6bb4514SBenjamin Thery static struct mfc_cache *ipmr_mfc_seq_idx(struct net *net, 2332f6bb4514SBenjamin Thery struct ipmr_mfc_iter *it, loff_t pos) 23331da177e4SLinus Torvalds { 2334f0ad0860SPatrick McHardy struct mr_table *mrt = it->mrt; 23351da177e4SLinus Torvalds struct mfc_cache *mfc; 23361da177e4SLinus Torvalds 2337a8c9486bSEric Dumazet rcu_read_lock(); 2338862465f2SPatrick McHardy for (it->ct = 0; it->ct < MFC_LINES; it->ct++) { 23390c12295aSPatrick McHardy it->cache = &mrt->mfc_cache_array[it->ct]; 2340a8c9486bSEric Dumazet list_for_each_entry_rcu(mfc, it->cache, list) 23411da177e4SLinus Torvalds if (pos-- == 0) 23421da177e4SLinus Torvalds return mfc; 2343862465f2SPatrick McHardy } 2344a8c9486bSEric Dumazet rcu_read_unlock(); 23451da177e4SLinus Torvalds 23461da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 23470c12295aSPatrick McHardy it->cache = &mrt->mfc_unres_queue; 2348862465f2SPatrick McHardy list_for_each_entry(mfc, it->cache, list) 2349e258beb2SPatrick McHardy if (pos-- == 0) 23501da177e4SLinus Torvalds return mfc; 23511da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 23521da177e4SLinus Torvalds 23531da177e4SLinus Torvalds it->cache = NULL; 23541da177e4SLinus Torvalds return NULL; 23551da177e4SLinus Torvalds } 23561da177e4SLinus Torvalds 23571da177e4SLinus Torvalds 23581da177e4SLinus Torvalds static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos) 23591da177e4SLinus Torvalds { 23601da177e4SLinus Torvalds struct ipmr_mfc_iter *it = seq->private; 2361f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2362f0ad0860SPatrick McHardy struct mr_table *mrt; 2363f6bb4514SBenjamin Thery 2364f0ad0860SPatrick McHardy mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 2365f0ad0860SPatrick McHardy if (mrt == NULL) 2366f0ad0860SPatrick McHardy return ERR_PTR(-ENOENT); 2367f0ad0860SPatrick McHardy 2368f0ad0860SPatrick McHardy it->mrt = mrt; 23691da177e4SLinus Torvalds it->cache = NULL; 23701da177e4SLinus Torvalds it->ct = 0; 2371f6bb4514SBenjamin Thery return *pos ? ipmr_mfc_seq_idx(net, seq->private, *pos - 1) 23721da177e4SLinus Torvalds : SEQ_START_TOKEN; 23731da177e4SLinus Torvalds } 23741da177e4SLinus Torvalds 23751da177e4SLinus Torvalds static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos) 23761da177e4SLinus Torvalds { 23771da177e4SLinus Torvalds struct mfc_cache *mfc = v; 23781da177e4SLinus Torvalds struct ipmr_mfc_iter *it = seq->private; 2379f6bb4514SBenjamin Thery struct net *net = seq_file_net(seq); 2380f0ad0860SPatrick McHardy struct mr_table *mrt = it->mrt; 23811da177e4SLinus Torvalds 23821da177e4SLinus Torvalds ++*pos; 23831da177e4SLinus Torvalds 23841da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) 2385f6bb4514SBenjamin Thery return ipmr_mfc_seq_idx(net, seq->private, 0); 23861da177e4SLinus Torvalds 2387862465f2SPatrick McHardy if (mfc->list.next != it->cache) 2388862465f2SPatrick McHardy return list_entry(mfc->list.next, struct mfc_cache, list); 23891da177e4SLinus Torvalds 23900c12295aSPatrick McHardy if (it->cache == &mrt->mfc_unres_queue) 23911da177e4SLinus Torvalds goto end_of_list; 23921da177e4SLinus Torvalds 23930c12295aSPatrick McHardy BUG_ON(it->cache != &mrt->mfc_cache_array[it->ct]); 23941da177e4SLinus Torvalds 23951da177e4SLinus Torvalds while (++it->ct < MFC_LINES) { 23960c12295aSPatrick McHardy it->cache = &mrt->mfc_cache_array[it->ct]; 2397862465f2SPatrick McHardy if (list_empty(it->cache)) 2398862465f2SPatrick McHardy continue; 2399862465f2SPatrick McHardy return list_first_entry(it->cache, struct mfc_cache, list); 24001da177e4SLinus Torvalds } 24011da177e4SLinus Torvalds 24021da177e4SLinus Torvalds /* exhausted cache_array, show unresolved */ 2403a8c9486bSEric Dumazet rcu_read_unlock(); 24040c12295aSPatrick McHardy it->cache = &mrt->mfc_unres_queue; 24051da177e4SLinus Torvalds it->ct = 0; 24061da177e4SLinus Torvalds 24071da177e4SLinus Torvalds spin_lock_bh(&mfc_unres_lock); 2408862465f2SPatrick McHardy if (!list_empty(it->cache)) 2409862465f2SPatrick McHardy return list_first_entry(it->cache, struct mfc_cache, list); 24101da177e4SLinus Torvalds 24111da177e4SLinus Torvalds end_of_list: 24121da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 24131da177e4SLinus Torvalds it->cache = NULL; 24141da177e4SLinus Torvalds 24151da177e4SLinus Torvalds return NULL; 24161da177e4SLinus Torvalds } 24171da177e4SLinus Torvalds 24181da177e4SLinus Torvalds static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) 24191da177e4SLinus Torvalds { 24201da177e4SLinus Torvalds struct ipmr_mfc_iter *it = seq->private; 2421f0ad0860SPatrick McHardy struct mr_table *mrt = it->mrt; 24221da177e4SLinus Torvalds 24230c12295aSPatrick McHardy if (it->cache == &mrt->mfc_unres_queue) 24241da177e4SLinus Torvalds spin_unlock_bh(&mfc_unres_lock); 24250c12295aSPatrick McHardy else if (it->cache == &mrt->mfc_cache_array[it->ct]) 2426a8c9486bSEric Dumazet rcu_read_unlock(); 24271da177e4SLinus Torvalds } 24281da177e4SLinus Torvalds 24291da177e4SLinus Torvalds static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) 24301da177e4SLinus Torvalds { 24311da177e4SLinus Torvalds int n; 24321da177e4SLinus Torvalds 24331da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 24341da177e4SLinus Torvalds seq_puts(seq, 24351da177e4SLinus Torvalds "Group Origin Iif Pkts Bytes Wrong Oifs\n"); 24361da177e4SLinus Torvalds } else { 24371da177e4SLinus Torvalds const struct mfc_cache *mfc = v; 24381da177e4SLinus Torvalds const struct ipmr_mfc_iter *it = seq->private; 2439f0ad0860SPatrick McHardy const struct mr_table *mrt = it->mrt; 24401da177e4SLinus Torvalds 24410eae88f3SEric Dumazet seq_printf(seq, "%08X %08X %-3hd", 24420eae88f3SEric Dumazet (__force u32) mfc->mfc_mcastgrp, 24430eae88f3SEric Dumazet (__force u32) mfc->mfc_origin, 24441ea472e2SBenjamin Thery mfc->mfc_parent); 24451ea472e2SBenjamin Thery 24460c12295aSPatrick McHardy if (it->cache != &mrt->mfc_unres_queue) { 24471ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 24481da177e4SLinus Torvalds mfc->mfc_un.res.pkt, 24491da177e4SLinus Torvalds mfc->mfc_un.res.bytes, 24501da177e4SLinus Torvalds mfc->mfc_un.res.wrong_if); 24511da177e4SLinus Torvalds for (n = mfc->mfc_un.res.minvif; 24521da177e4SLinus Torvalds n < mfc->mfc_un.res.maxvif; n++) { 24530c12295aSPatrick McHardy if (VIF_EXISTS(mrt, n) && 2454cf958ae3SBenjamin Thery mfc->mfc_un.res.ttls[n] < 255) 24551da177e4SLinus Torvalds seq_printf(seq, 24561da177e4SLinus Torvalds " %2d:%-3d", 24571da177e4SLinus Torvalds n, mfc->mfc_un.res.ttls[n]); 24581da177e4SLinus Torvalds } 24591ea472e2SBenjamin Thery } else { 24601ea472e2SBenjamin Thery /* unresolved mfc_caches don't contain 24611ea472e2SBenjamin Thery * pkt, bytes and wrong_if values 24621ea472e2SBenjamin Thery */ 24631ea472e2SBenjamin Thery seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul); 24641da177e4SLinus Torvalds } 24651da177e4SLinus Torvalds seq_putc(seq, '\n'); 24661da177e4SLinus Torvalds } 24671da177e4SLinus Torvalds return 0; 24681da177e4SLinus Torvalds } 24691da177e4SLinus Torvalds 2470f690808eSStephen Hemminger static const struct seq_operations ipmr_mfc_seq_ops = { 24711da177e4SLinus Torvalds .start = ipmr_mfc_seq_start, 24721da177e4SLinus Torvalds .next = ipmr_mfc_seq_next, 24731da177e4SLinus Torvalds .stop = ipmr_mfc_seq_stop, 24741da177e4SLinus Torvalds .show = ipmr_mfc_seq_show, 24751da177e4SLinus Torvalds }; 24761da177e4SLinus Torvalds 24771da177e4SLinus Torvalds static int ipmr_mfc_open(struct inode *inode, struct file *file) 24781da177e4SLinus Torvalds { 2479f6bb4514SBenjamin Thery return seq_open_net(inode, file, &ipmr_mfc_seq_ops, 2480cf7732e4SPavel Emelyanov sizeof(struct ipmr_mfc_iter)); 24811da177e4SLinus Torvalds } 24821da177e4SLinus Torvalds 24839a32144eSArjan van de Ven static const struct file_operations ipmr_mfc_fops = { 24841da177e4SLinus Torvalds .owner = THIS_MODULE, 24851da177e4SLinus Torvalds .open = ipmr_mfc_open, 24861da177e4SLinus Torvalds .read = seq_read, 24871da177e4SLinus Torvalds .llseek = seq_lseek, 2488f6bb4514SBenjamin Thery .release = seq_release_net, 24891da177e4SLinus Torvalds }; 24901da177e4SLinus Torvalds #endif 24911da177e4SLinus Torvalds 24921da177e4SLinus Torvalds #ifdef CONFIG_IP_PIMSM_V2 249332613090SAlexey Dobriyan static const struct net_protocol pim_protocol = { 24941da177e4SLinus Torvalds .handler = pim_rcv, 2495403dbb97STom Goff .netns_ok = 1, 24961da177e4SLinus Torvalds }; 24971da177e4SLinus Torvalds #endif 24981da177e4SLinus Torvalds 24991da177e4SLinus Torvalds 25001da177e4SLinus Torvalds /* 25011da177e4SLinus Torvalds * Setup for IP multicast routing 25021da177e4SLinus Torvalds */ 2503cf958ae3SBenjamin Thery static int __net_init ipmr_net_init(struct net *net) 2504cf958ae3SBenjamin Thery { 2505f0ad0860SPatrick McHardy int err; 2506cf958ae3SBenjamin Thery 2507f0ad0860SPatrick McHardy err = ipmr_rules_init(net); 2508f0ad0860SPatrick McHardy if (err < 0) 2509cf958ae3SBenjamin Thery goto fail; 2510f6bb4514SBenjamin Thery 2511f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 2512f6bb4514SBenjamin Thery err = -ENOMEM; 2513f6bb4514SBenjamin Thery if (!proc_net_fops_create(net, "ip_mr_vif", 0, &ipmr_vif_fops)) 2514f6bb4514SBenjamin Thery goto proc_vif_fail; 2515f6bb4514SBenjamin Thery if (!proc_net_fops_create(net, "ip_mr_cache", 0, &ipmr_mfc_fops)) 2516f6bb4514SBenjamin Thery goto proc_cache_fail; 2517f6bb4514SBenjamin Thery #endif 25182bb8b26cSBenjamin Thery return 0; 25192bb8b26cSBenjamin Thery 2520f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 2521f6bb4514SBenjamin Thery proc_cache_fail: 2522f6bb4514SBenjamin Thery proc_net_remove(net, "ip_mr_vif"); 2523f6bb4514SBenjamin Thery proc_vif_fail: 2524f0ad0860SPatrick McHardy ipmr_rules_exit(net); 2525f6bb4514SBenjamin Thery #endif 2526cf958ae3SBenjamin Thery fail: 2527cf958ae3SBenjamin Thery return err; 2528cf958ae3SBenjamin Thery } 2529cf958ae3SBenjamin Thery 2530cf958ae3SBenjamin Thery static void __net_exit ipmr_net_exit(struct net *net) 2531cf958ae3SBenjamin Thery { 2532f6bb4514SBenjamin Thery #ifdef CONFIG_PROC_FS 2533f6bb4514SBenjamin Thery proc_net_remove(net, "ip_mr_cache"); 2534f6bb4514SBenjamin Thery proc_net_remove(net, "ip_mr_vif"); 2535f6bb4514SBenjamin Thery #endif 2536f0ad0860SPatrick McHardy ipmr_rules_exit(net); 2537cf958ae3SBenjamin Thery } 2538cf958ae3SBenjamin Thery 2539cf958ae3SBenjamin Thery static struct pernet_operations ipmr_net_ops = { 2540cf958ae3SBenjamin Thery .init = ipmr_net_init, 2541cf958ae3SBenjamin Thery .exit = ipmr_net_exit, 2542cf958ae3SBenjamin Thery }; 25431da177e4SLinus Torvalds 254403d2f897SWang Chen int __init ip_mr_init(void) 25451da177e4SLinus Torvalds { 254603d2f897SWang Chen int err; 254703d2f897SWang Chen 25481da177e4SLinus Torvalds mrt_cachep = kmem_cache_create("ip_mrt_cache", 25491da177e4SLinus Torvalds sizeof(struct mfc_cache), 2550e5d679f3SAlexey Dobriyan 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, 255120c2df83SPaul Mundt NULL); 255203d2f897SWang Chen if (!mrt_cachep) 255303d2f897SWang Chen return -ENOMEM; 255403d2f897SWang Chen 2555cf958ae3SBenjamin Thery err = register_pernet_subsys(&ipmr_net_ops); 2556cf958ae3SBenjamin Thery if (err) 2557cf958ae3SBenjamin Thery goto reg_pernet_fail; 2558cf958ae3SBenjamin Thery 255903d2f897SWang Chen err = register_netdevice_notifier(&ip_mr_notifier); 256003d2f897SWang Chen if (err) 256103d2f897SWang Chen goto reg_notif_fail; 2562403dbb97STom Goff #ifdef CONFIG_IP_PIMSM_V2 2563403dbb97STom Goff if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) { 2564058bd4d2SJoe Perches pr_err("%s: can't add PIM protocol\n", __func__); 2565403dbb97STom Goff err = -EAGAIN; 2566403dbb97STom Goff goto add_proto_fail; 2567403dbb97STom Goff } 2568403dbb97STom Goff #endif 2569c7ac8679SGreg Rose rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, 2570c7ac8679SGreg Rose NULL, ipmr_rtm_dumproute, NULL); 257103d2f897SWang Chen return 0; 2572f6bb4514SBenjamin Thery 2573403dbb97STom Goff #ifdef CONFIG_IP_PIMSM_V2 2574403dbb97STom Goff add_proto_fail: 2575403dbb97STom Goff unregister_netdevice_notifier(&ip_mr_notifier); 2576403dbb97STom Goff #endif 2577c3e38896SBenjamin Thery reg_notif_fail: 2578cf958ae3SBenjamin Thery unregister_pernet_subsys(&ipmr_net_ops); 2579cf958ae3SBenjamin Thery reg_pernet_fail: 2580c3e38896SBenjamin Thery kmem_cache_destroy(mrt_cachep); 258103d2f897SWang Chen return err; 25821da177e4SLinus Torvalds } 2583