162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IP multicast routing support for mrouted 3.6/3.8 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk> 662306a36Sopenharmony_ci * Linux Consultancy and Custom Driver Development 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Fixes: 962306a36Sopenharmony_ci * Michael Chastain : Incorrect size of copying. 1062306a36Sopenharmony_ci * Alan Cox : Added the cache manager code 1162306a36Sopenharmony_ci * Alan Cox : Fixed the clone/copy bug and device race. 1262306a36Sopenharmony_ci * Mike McLagan : Routing by source 1362306a36Sopenharmony_ci * Malcolm Beattie : Buffer handling fixes. 1462306a36Sopenharmony_ci * Alexey Kuznetsov : Double buffer free and other fixes. 1562306a36Sopenharmony_ci * SVR Anand : Fixed several multicast bugs and problems. 1662306a36Sopenharmony_ci * Alexey Kuznetsov : Status, optimisations and more. 1762306a36Sopenharmony_ci * Brad Parker : Better behaviour on mrouted upcall 1862306a36Sopenharmony_ci * overflow. 1962306a36Sopenharmony_ci * Carlos Picoto : PIMv1 Support 2062306a36Sopenharmony_ci * Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header 2162306a36Sopenharmony_ci * Relax this requirement to work with older peers. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/uaccess.h> 2562306a36Sopenharmony_ci#include <linux/types.h> 2662306a36Sopenharmony_ci#include <linux/cache.h> 2762306a36Sopenharmony_ci#include <linux/capability.h> 2862306a36Sopenharmony_ci#include <linux/errno.h> 2962306a36Sopenharmony_ci#include <linux/mm.h> 3062306a36Sopenharmony_ci#include <linux/kernel.h> 3162306a36Sopenharmony_ci#include <linux/fcntl.h> 3262306a36Sopenharmony_ci#include <linux/stat.h> 3362306a36Sopenharmony_ci#include <linux/socket.h> 3462306a36Sopenharmony_ci#include <linux/in.h> 3562306a36Sopenharmony_ci#include <linux/inet.h> 3662306a36Sopenharmony_ci#include <linux/netdevice.h> 3762306a36Sopenharmony_ci#include <linux/inetdevice.h> 3862306a36Sopenharmony_ci#include <linux/igmp.h> 3962306a36Sopenharmony_ci#include <linux/proc_fs.h> 4062306a36Sopenharmony_ci#include <linux/seq_file.h> 4162306a36Sopenharmony_ci#include <linux/mroute.h> 4262306a36Sopenharmony_ci#include <linux/init.h> 4362306a36Sopenharmony_ci#include <linux/if_ether.h> 4462306a36Sopenharmony_ci#include <linux/slab.h> 4562306a36Sopenharmony_ci#include <net/net_namespace.h> 4662306a36Sopenharmony_ci#include <net/ip.h> 4762306a36Sopenharmony_ci#include <net/protocol.h> 4862306a36Sopenharmony_ci#include <linux/skbuff.h> 4962306a36Sopenharmony_ci#include <net/route.h> 5062306a36Sopenharmony_ci#include <net/icmp.h> 5162306a36Sopenharmony_ci#include <net/udp.h> 5262306a36Sopenharmony_ci#include <net/raw.h> 5362306a36Sopenharmony_ci#include <linux/notifier.h> 5462306a36Sopenharmony_ci#include <linux/if_arp.h> 5562306a36Sopenharmony_ci#include <linux/netfilter_ipv4.h> 5662306a36Sopenharmony_ci#include <linux/compat.h> 5762306a36Sopenharmony_ci#include <linux/export.h> 5862306a36Sopenharmony_ci#include <linux/rhashtable.h> 5962306a36Sopenharmony_ci#include <net/ip_tunnels.h> 6062306a36Sopenharmony_ci#include <net/checksum.h> 6162306a36Sopenharmony_ci#include <net/netlink.h> 6262306a36Sopenharmony_ci#include <net/fib_rules.h> 6362306a36Sopenharmony_ci#include <linux/netconf.h> 6462306a36Sopenharmony_ci#include <net/rtnh.h> 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#include <linux/nospec.h> 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct ipmr_rule { 6962306a36Sopenharmony_ci struct fib_rule common; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct ipmr_result { 7362306a36Sopenharmony_ci struct mr_table *mrt; 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Big lock, protecting vif table, mrt cache and mroute socket state. 7762306a36Sopenharmony_ci * Note that the changes are semaphored via rtnl_lock. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(mrt_lock); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic struct net_device *vif_dev_read(const struct vif_device *vif) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci return rcu_dereference(vif->dev); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* Multicast router control variables */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* Special spinlock for queue of unresolved entries */ 9062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(mfc_unres_lock); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* We return to original Alan's scheme. Hash table of resolved 9362306a36Sopenharmony_ci * entries is changed only in process context and protected 9462306a36Sopenharmony_ci * with weak lock mrt_lock. Queue of unresolved entries is protected 9562306a36Sopenharmony_ci * with strong spinlock mfc_unres_lock. 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * In this case data path is free of exclusive locks at all. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic struct kmem_cache *mrt_cachep __ro_after_init; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic struct mr_table *ipmr_new_table(struct net *net, u32 id); 10362306a36Sopenharmony_cistatic void ipmr_free_table(struct mr_table *mrt); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void ip_mr_forward(struct net *net, struct mr_table *mrt, 10662306a36Sopenharmony_ci struct net_device *dev, struct sk_buff *skb, 10762306a36Sopenharmony_ci struct mfc_cache *cache, int local); 10862306a36Sopenharmony_cistatic int ipmr_cache_report(const struct mr_table *mrt, 10962306a36Sopenharmony_ci struct sk_buff *pkt, vifi_t vifi, int assert); 11062306a36Sopenharmony_cistatic void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, 11162306a36Sopenharmony_ci int cmd); 11262306a36Sopenharmony_cistatic void igmpmsg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt); 11362306a36Sopenharmony_cistatic void mroute_clean_tables(struct mr_table *mrt, int flags); 11462306a36Sopenharmony_cistatic void ipmr_expire_process(struct timer_list *t); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 11762306a36Sopenharmony_ci#define ipmr_for_each_table(mrt, net) \ 11862306a36Sopenharmony_ci list_for_each_entry_rcu(mrt, &net->ipv4.mr_tables, list, \ 11962306a36Sopenharmony_ci lockdep_rtnl_is_held() || \ 12062306a36Sopenharmony_ci list_empty(&net->ipv4.mr_tables)) 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic struct mr_table *ipmr_mr_table_iter(struct net *net, 12362306a36Sopenharmony_ci struct mr_table *mrt) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct mr_table *ret; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (!mrt) 12862306a36Sopenharmony_ci ret = list_entry_rcu(net->ipv4.mr_tables.next, 12962306a36Sopenharmony_ci struct mr_table, list); 13062306a36Sopenharmony_ci else 13162306a36Sopenharmony_ci ret = list_entry_rcu(mrt->list.next, 13262306a36Sopenharmony_ci struct mr_table, list); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (&ret->list == &net->ipv4.mr_tables) 13562306a36Sopenharmony_ci return NULL; 13662306a36Sopenharmony_ci return ret; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic struct mr_table *ipmr_get_table(struct net *net, u32 id) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct mr_table *mrt; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ipmr_for_each_table(mrt, net) { 14462306a36Sopenharmony_ci if (mrt->id == id) 14562306a36Sopenharmony_ci return mrt; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci return NULL; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 15162306a36Sopenharmony_ci struct mr_table **mrt) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci int err; 15462306a36Sopenharmony_ci struct ipmr_result res; 15562306a36Sopenharmony_ci struct fib_lookup_arg arg = { 15662306a36Sopenharmony_ci .result = &res, 15762306a36Sopenharmony_ci .flags = FIB_LOOKUP_NOREF, 15862306a36Sopenharmony_ci }; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* update flow if oif or iif point to device enslaved to l3mdev */ 16162306a36Sopenharmony_ci l3mdev_update_flow(net, flowi4_to_flowi(flp4)); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci err = fib_rules_lookup(net->ipv4.mr_rules_ops, 16462306a36Sopenharmony_ci flowi4_to_flowi(flp4), 0, &arg); 16562306a36Sopenharmony_ci if (err < 0) 16662306a36Sopenharmony_ci return err; 16762306a36Sopenharmony_ci *mrt = res.mrt; 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp, 17262306a36Sopenharmony_ci int flags, struct fib_lookup_arg *arg) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct ipmr_result *res = arg->result; 17562306a36Sopenharmony_ci struct mr_table *mrt; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci switch (rule->action) { 17862306a36Sopenharmony_ci case FR_ACT_TO_TBL: 17962306a36Sopenharmony_ci break; 18062306a36Sopenharmony_ci case FR_ACT_UNREACHABLE: 18162306a36Sopenharmony_ci return -ENETUNREACH; 18262306a36Sopenharmony_ci case FR_ACT_PROHIBIT: 18362306a36Sopenharmony_ci return -EACCES; 18462306a36Sopenharmony_ci case FR_ACT_BLACKHOLE: 18562306a36Sopenharmony_ci default: 18662306a36Sopenharmony_ci return -EINVAL; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci arg->table = fib_rule_get_table(rule, arg); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci mrt = ipmr_get_table(rule->fr_net, arg->table); 19262306a36Sopenharmony_ci if (!mrt) 19362306a36Sopenharmony_ci return -EAGAIN; 19462306a36Sopenharmony_ci res->mrt = mrt; 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int ipmr_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci return 1; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb, 20462306a36Sopenharmony_ci struct fib_rule_hdr *frh, struct nlattr **tb, 20562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int ipmr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 21162306a36Sopenharmony_ci struct nlattr **tb) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci return 1; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int ipmr_rule_fill(struct fib_rule *rule, struct sk_buff *skb, 21762306a36Sopenharmony_ci struct fib_rule_hdr *frh) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci frh->dst_len = 0; 22062306a36Sopenharmony_ci frh->src_len = 0; 22162306a36Sopenharmony_ci frh->tos = 0; 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic const struct fib_rules_ops __net_initconst ipmr_rules_ops_template = { 22662306a36Sopenharmony_ci .family = RTNL_FAMILY_IPMR, 22762306a36Sopenharmony_ci .rule_size = sizeof(struct ipmr_rule), 22862306a36Sopenharmony_ci .addr_size = sizeof(u32), 22962306a36Sopenharmony_ci .action = ipmr_rule_action, 23062306a36Sopenharmony_ci .match = ipmr_rule_match, 23162306a36Sopenharmony_ci .configure = ipmr_rule_configure, 23262306a36Sopenharmony_ci .compare = ipmr_rule_compare, 23362306a36Sopenharmony_ci .fill = ipmr_rule_fill, 23462306a36Sopenharmony_ci .nlgroup = RTNLGRP_IPV4_RULE, 23562306a36Sopenharmony_ci .owner = THIS_MODULE, 23662306a36Sopenharmony_ci}; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int __net_init ipmr_rules_init(struct net *net) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct fib_rules_ops *ops; 24162306a36Sopenharmony_ci struct mr_table *mrt; 24262306a36Sopenharmony_ci int err; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci ops = fib_rules_register(&ipmr_rules_ops_template, net); 24562306a36Sopenharmony_ci if (IS_ERR(ops)) 24662306a36Sopenharmony_ci return PTR_ERR(ops); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci INIT_LIST_HEAD(&net->ipv4.mr_tables); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 25162306a36Sopenharmony_ci if (IS_ERR(mrt)) { 25262306a36Sopenharmony_ci err = PTR_ERR(mrt); 25362306a36Sopenharmony_ci goto err1; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci err = fib_default_rule_add(ops, 0x7fff, RT_TABLE_DEFAULT, 0); 25762306a36Sopenharmony_ci if (err < 0) 25862306a36Sopenharmony_ci goto err2; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci net->ipv4.mr_rules_ops = ops; 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cierr2: 26462306a36Sopenharmony_ci rtnl_lock(); 26562306a36Sopenharmony_ci ipmr_free_table(mrt); 26662306a36Sopenharmony_ci rtnl_unlock(); 26762306a36Sopenharmony_cierr1: 26862306a36Sopenharmony_ci fib_rules_unregister(ops); 26962306a36Sopenharmony_ci return err; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void __net_exit ipmr_rules_exit(struct net *net) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct mr_table *mrt, *next; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ASSERT_RTNL(); 27762306a36Sopenharmony_ci list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) { 27862306a36Sopenharmony_ci list_del(&mrt->list); 27962306a36Sopenharmony_ci ipmr_free_table(mrt); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci fib_rules_unregister(net->ipv4.mr_rules_ops); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int ipmr_rules_dump(struct net *net, struct notifier_block *nb, 28562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci return fib_rules_dump(net, nb, RTNL_FAMILY_IPMR, extack); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic unsigned int ipmr_rules_seq_read(struct net *net) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci return fib_rules_seq_read(net, RTNL_FAMILY_IPMR); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cibool ipmr_rule_default(const struct fib_rule *rule) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci return fib_rule_matchall(rule) && rule->table == RT_TABLE_DEFAULT; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ciEXPORT_SYMBOL(ipmr_rule_default); 30062306a36Sopenharmony_ci#else 30162306a36Sopenharmony_ci#define ipmr_for_each_table(mrt, net) \ 30262306a36Sopenharmony_ci for (mrt = net->ipv4.mrt; mrt; mrt = NULL) 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic struct mr_table *ipmr_mr_table_iter(struct net *net, 30562306a36Sopenharmony_ci struct mr_table *mrt) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci if (!mrt) 30862306a36Sopenharmony_ci return net->ipv4.mrt; 30962306a36Sopenharmony_ci return NULL; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic struct mr_table *ipmr_get_table(struct net *net, u32 id) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci return net->ipv4.mrt; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int ipmr_fib_lookup(struct net *net, struct flowi4 *flp4, 31862306a36Sopenharmony_ci struct mr_table **mrt) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci *mrt = net->ipv4.mrt; 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int __net_init ipmr_rules_init(struct net *net) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct mr_table *mrt; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci mrt = ipmr_new_table(net, RT_TABLE_DEFAULT); 32962306a36Sopenharmony_ci if (IS_ERR(mrt)) 33062306a36Sopenharmony_ci return PTR_ERR(mrt); 33162306a36Sopenharmony_ci net->ipv4.mrt = mrt; 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic void __net_exit ipmr_rules_exit(struct net *net) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci ASSERT_RTNL(); 33862306a36Sopenharmony_ci ipmr_free_table(net->ipv4.mrt); 33962306a36Sopenharmony_ci net->ipv4.mrt = NULL; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic int ipmr_rules_dump(struct net *net, struct notifier_block *nb, 34362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic unsigned int ipmr_rules_seq_read(struct net *net) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cibool ipmr_rule_default(const struct fib_rule *rule) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci return true; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ciEXPORT_SYMBOL(ipmr_rule_default); 35862306a36Sopenharmony_ci#endif 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic inline int ipmr_hash_cmp(struct rhashtable_compare_arg *arg, 36162306a36Sopenharmony_ci const void *ptr) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci const struct mfc_cache_cmp_arg *cmparg = arg->key; 36462306a36Sopenharmony_ci const struct mfc_cache *c = ptr; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return cmparg->mfc_mcastgrp != c->mfc_mcastgrp || 36762306a36Sopenharmony_ci cmparg->mfc_origin != c->mfc_origin; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic const struct rhashtable_params ipmr_rht_params = { 37162306a36Sopenharmony_ci .head_offset = offsetof(struct mr_mfc, mnode), 37262306a36Sopenharmony_ci .key_offset = offsetof(struct mfc_cache, cmparg), 37362306a36Sopenharmony_ci .key_len = sizeof(struct mfc_cache_cmp_arg), 37462306a36Sopenharmony_ci .nelem_hint = 3, 37562306a36Sopenharmony_ci .obj_cmpfn = ipmr_hash_cmp, 37662306a36Sopenharmony_ci .automatic_shrinking = true, 37762306a36Sopenharmony_ci}; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic void ipmr_new_table_set(struct mr_table *mrt, 38062306a36Sopenharmony_ci struct net *net) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES 38362306a36Sopenharmony_ci list_add_tail_rcu(&mrt->list, &net->ipv4.mr_tables); 38462306a36Sopenharmony_ci#endif 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic struct mfc_cache_cmp_arg ipmr_mr_table_ops_cmparg_any = { 38862306a36Sopenharmony_ci .mfc_mcastgrp = htonl(INADDR_ANY), 38962306a36Sopenharmony_ci .mfc_origin = htonl(INADDR_ANY), 39062306a36Sopenharmony_ci}; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic struct mr_table_ops ipmr_mr_table_ops = { 39362306a36Sopenharmony_ci .rht_params = &ipmr_rht_params, 39462306a36Sopenharmony_ci .cmparg_any = &ipmr_mr_table_ops_cmparg_any, 39562306a36Sopenharmony_ci}; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic struct mr_table *ipmr_new_table(struct net *net, u32 id) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct mr_table *mrt; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */ 40262306a36Sopenharmony_ci if (id != RT_TABLE_DEFAULT && id >= 1000000000) 40362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci mrt = ipmr_get_table(net, id); 40662306a36Sopenharmony_ci if (mrt) 40762306a36Sopenharmony_ci return mrt; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return mr_table_alloc(net, id, &ipmr_mr_table_ops, 41062306a36Sopenharmony_ci ipmr_expire_process, ipmr_new_table_set); 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic void ipmr_free_table(struct mr_table *mrt) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci timer_shutdown_sync(&mrt->ipmr_expire_timer); 41662306a36Sopenharmony_ci mroute_clean_tables(mrt, MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC | 41762306a36Sopenharmony_ci MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC); 41862306a36Sopenharmony_ci rhltable_destroy(&mrt->mfc_hash); 41962306a36Sopenharmony_ci kfree(mrt); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */ 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci/* Initialize ipmr pimreg/tunnel in_device */ 42562306a36Sopenharmony_cistatic bool ipmr_init_vif_indev(const struct net_device *dev) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct in_device *in_dev; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci ASSERT_RTNL(); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci in_dev = __in_dev_get_rtnl(dev); 43262306a36Sopenharmony_ci if (!in_dev) 43362306a36Sopenharmony_ci return false; 43462306a36Sopenharmony_ci ipv4_devconf_setall(in_dev); 43562306a36Sopenharmony_ci neigh_parms_data_state_setall(in_dev->arp_parms); 43662306a36Sopenharmony_ci IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return true; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct net_device *tunnel_dev, *new_dev; 44462306a36Sopenharmony_ci struct ip_tunnel_parm p = { }; 44562306a36Sopenharmony_ci int err; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci tunnel_dev = __dev_get_by_name(net, "tunl0"); 44862306a36Sopenharmony_ci if (!tunnel_dev) 44962306a36Sopenharmony_ci goto out; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci p.iph.daddr = v->vifc_rmt_addr.s_addr; 45262306a36Sopenharmony_ci p.iph.saddr = v->vifc_lcl_addr.s_addr; 45362306a36Sopenharmony_ci p.iph.version = 4; 45462306a36Sopenharmony_ci p.iph.ihl = 5; 45562306a36Sopenharmony_ci p.iph.protocol = IPPROTO_IPIP; 45662306a36Sopenharmony_ci sprintf(p.name, "dvmrp%d", v->vifc_vifi); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (!tunnel_dev->netdev_ops->ndo_tunnel_ctl) 45962306a36Sopenharmony_ci goto out; 46062306a36Sopenharmony_ci err = tunnel_dev->netdev_ops->ndo_tunnel_ctl(tunnel_dev, &p, 46162306a36Sopenharmony_ci SIOCADDTUNNEL); 46262306a36Sopenharmony_ci if (err) 46362306a36Sopenharmony_ci goto out; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci new_dev = __dev_get_by_name(net, p.name); 46662306a36Sopenharmony_ci if (!new_dev) 46762306a36Sopenharmony_ci goto out; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci new_dev->flags |= IFF_MULTICAST; 47062306a36Sopenharmony_ci if (!ipmr_init_vif_indev(new_dev)) 47162306a36Sopenharmony_ci goto out_unregister; 47262306a36Sopenharmony_ci if (dev_open(new_dev, NULL)) 47362306a36Sopenharmony_ci goto out_unregister; 47462306a36Sopenharmony_ci dev_hold(new_dev); 47562306a36Sopenharmony_ci err = dev_set_allmulti(new_dev, 1); 47662306a36Sopenharmony_ci if (err) { 47762306a36Sopenharmony_ci dev_close(new_dev); 47862306a36Sopenharmony_ci tunnel_dev->netdev_ops->ndo_tunnel_ctl(tunnel_dev, &p, 47962306a36Sopenharmony_ci SIOCDELTUNNEL); 48062306a36Sopenharmony_ci dev_put(new_dev); 48162306a36Sopenharmony_ci new_dev = ERR_PTR(err); 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci return new_dev; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ciout_unregister: 48662306a36Sopenharmony_ci unregister_netdevice(new_dev); 48762306a36Sopenharmony_ciout: 48862306a36Sopenharmony_ci return ERR_PTR(-ENOBUFS); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2) 49262306a36Sopenharmony_cistatic netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct net *net = dev_net(dev); 49562306a36Sopenharmony_ci struct mr_table *mrt; 49662306a36Sopenharmony_ci struct flowi4 fl4 = { 49762306a36Sopenharmony_ci .flowi4_oif = dev->ifindex, 49862306a36Sopenharmony_ci .flowi4_iif = skb->skb_iif ? : LOOPBACK_IFINDEX, 49962306a36Sopenharmony_ci .flowi4_mark = skb->mark, 50062306a36Sopenharmony_ci }; 50162306a36Sopenharmony_ci int err; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci err = ipmr_fib_lookup(net, &fl4, &mrt); 50462306a36Sopenharmony_ci if (err < 0) { 50562306a36Sopenharmony_ci kfree_skb(skb); 50662306a36Sopenharmony_ci return err; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci DEV_STATS_ADD(dev, tx_bytes, skb->len); 51062306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_packets); 51162306a36Sopenharmony_ci rcu_read_lock(); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci /* Pairs with WRITE_ONCE() in vif_add() and vif_delete() */ 51462306a36Sopenharmony_ci ipmr_cache_report(mrt, skb, READ_ONCE(mrt->mroute_reg_vif_num), 51562306a36Sopenharmony_ci IGMPMSG_WHOLEPKT); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci rcu_read_unlock(); 51862306a36Sopenharmony_ci kfree_skb(skb); 51962306a36Sopenharmony_ci return NETDEV_TX_OK; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic int reg_vif_get_iflink(const struct net_device *dev) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic const struct net_device_ops reg_vif_netdev_ops = { 52862306a36Sopenharmony_ci .ndo_start_xmit = reg_vif_xmit, 52962306a36Sopenharmony_ci .ndo_get_iflink = reg_vif_get_iflink, 53062306a36Sopenharmony_ci}; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic void reg_vif_setup(struct net_device *dev) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci dev->type = ARPHRD_PIMREG; 53562306a36Sopenharmony_ci dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8; 53662306a36Sopenharmony_ci dev->flags = IFF_NOARP; 53762306a36Sopenharmony_ci dev->netdev_ops = ®_vif_netdev_ops; 53862306a36Sopenharmony_ci dev->needs_free_netdev = true; 53962306a36Sopenharmony_ci dev->features |= NETIF_F_NETNS_LOCAL; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct net_device *dev; 54562306a36Sopenharmony_ci char name[IFNAMSIZ]; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (mrt->id == RT_TABLE_DEFAULT) 54862306a36Sopenharmony_ci sprintf(name, "pimreg"); 54962306a36Sopenharmony_ci else 55062306a36Sopenharmony_ci sprintf(name, "pimreg%u", mrt->id); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (!dev) 55562306a36Sopenharmony_ci return NULL; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci dev_net_set(dev, net); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (register_netdevice(dev)) { 56062306a36Sopenharmony_ci free_netdev(dev); 56162306a36Sopenharmony_ci return NULL; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (!ipmr_init_vif_indev(dev)) 56562306a36Sopenharmony_ci goto failure; 56662306a36Sopenharmony_ci if (dev_open(dev, NULL)) 56762306a36Sopenharmony_ci goto failure; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci dev_hold(dev); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return dev; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cifailure: 57462306a36Sopenharmony_ci unregister_netdevice(dev); 57562306a36Sopenharmony_ci return NULL; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci/* called with rcu_read_lock() */ 57962306a36Sopenharmony_cistatic int __pim_rcv(struct mr_table *mrt, struct sk_buff *skb, 58062306a36Sopenharmony_ci unsigned int pimlen) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct net_device *reg_dev = NULL; 58362306a36Sopenharmony_ci struct iphdr *encap; 58462306a36Sopenharmony_ci int vif_num; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci encap = (struct iphdr *)(skb_transport_header(skb) + pimlen); 58762306a36Sopenharmony_ci /* Check that: 58862306a36Sopenharmony_ci * a. packet is really sent to a multicast group 58962306a36Sopenharmony_ci * b. packet is not a NULL-REGISTER 59062306a36Sopenharmony_ci * c. packet is not truncated 59162306a36Sopenharmony_ci */ 59262306a36Sopenharmony_ci if (!ipv4_is_multicast(encap->daddr) || 59362306a36Sopenharmony_ci encap->tot_len == 0 || 59462306a36Sopenharmony_ci ntohs(encap->tot_len) + pimlen > skb->len) 59562306a36Sopenharmony_ci return 1; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Pairs with WRITE_ONCE() in vif_add()/vid_delete() */ 59862306a36Sopenharmony_ci vif_num = READ_ONCE(mrt->mroute_reg_vif_num); 59962306a36Sopenharmony_ci if (vif_num >= 0) 60062306a36Sopenharmony_ci reg_dev = vif_dev_read(&mrt->vif_table[vif_num]); 60162306a36Sopenharmony_ci if (!reg_dev) 60262306a36Sopenharmony_ci return 1; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci skb->mac_header = skb->network_header; 60562306a36Sopenharmony_ci skb_pull(skb, (u8 *)encap - skb->data); 60662306a36Sopenharmony_ci skb_reset_network_header(skb); 60762306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 60862306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev)); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci netif_rx(skb); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci return NET_RX_SUCCESS; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci#else 61762306a36Sopenharmony_cistatic struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci return NULL; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci#endif 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic int call_ipmr_vif_entry_notifiers(struct net *net, 62462306a36Sopenharmony_ci enum fib_event_type event_type, 62562306a36Sopenharmony_ci struct vif_device *vif, 62662306a36Sopenharmony_ci struct net_device *vif_dev, 62762306a36Sopenharmony_ci vifi_t vif_index, u32 tb_id) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci return mr_call_vif_notifiers(net, RTNL_FAMILY_IPMR, event_type, 63062306a36Sopenharmony_ci vif, vif_dev, vif_index, tb_id, 63162306a36Sopenharmony_ci &net->ipv4.ipmr_seq); 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic int call_ipmr_mfc_entry_notifiers(struct net *net, 63562306a36Sopenharmony_ci enum fib_event_type event_type, 63662306a36Sopenharmony_ci struct mfc_cache *mfc, u32 tb_id) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci return mr_call_mfc_notifiers(net, RTNL_FAMILY_IPMR, event_type, 63962306a36Sopenharmony_ci &mfc->_c, tb_id, &net->ipv4.ipmr_seq); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/** 64362306a36Sopenharmony_ci * vif_delete - Delete a VIF entry 64462306a36Sopenharmony_ci * @mrt: Table to delete from 64562306a36Sopenharmony_ci * @vifi: VIF identifier to delete 64662306a36Sopenharmony_ci * @notify: Set to 1, if the caller is a notifier_call 64762306a36Sopenharmony_ci * @head: if unregistering the VIF, place it on this queue 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_cistatic int vif_delete(struct mr_table *mrt, int vifi, int notify, 65062306a36Sopenharmony_ci struct list_head *head) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct net *net = read_pnet(&mrt->net); 65362306a36Sopenharmony_ci struct vif_device *v; 65462306a36Sopenharmony_ci struct net_device *dev; 65562306a36Sopenharmony_ci struct in_device *in_dev; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (vifi < 0 || vifi >= mrt->maxvif) 65862306a36Sopenharmony_ci return -EADDRNOTAVAIL; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci v = &mrt->vif_table[vifi]; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci dev = rtnl_dereference(v->dev); 66362306a36Sopenharmony_ci if (!dev) 66462306a36Sopenharmony_ci return -EADDRNOTAVAIL; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci spin_lock(&mrt_lock); 66762306a36Sopenharmony_ci call_ipmr_vif_entry_notifiers(net, FIB_EVENT_VIF_DEL, v, dev, 66862306a36Sopenharmony_ci vifi, mrt->id); 66962306a36Sopenharmony_ci RCU_INIT_POINTER(v->dev, NULL); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (vifi == mrt->mroute_reg_vif_num) { 67262306a36Sopenharmony_ci /* Pairs with READ_ONCE() in ipmr_cache_report() and reg_vif_xmit() */ 67362306a36Sopenharmony_ci WRITE_ONCE(mrt->mroute_reg_vif_num, -1); 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci if (vifi + 1 == mrt->maxvif) { 67662306a36Sopenharmony_ci int tmp; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci for (tmp = vifi - 1; tmp >= 0; tmp--) { 67962306a36Sopenharmony_ci if (VIF_EXISTS(mrt, tmp)) 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci WRITE_ONCE(mrt->maxvif, tmp + 1); 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci spin_unlock(&mrt_lock); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci dev_set_allmulti(dev, -1); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci in_dev = __in_dev_get_rtnl(dev); 69062306a36Sopenharmony_ci if (in_dev) { 69162306a36Sopenharmony_ci IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--; 69262306a36Sopenharmony_ci inet_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, 69362306a36Sopenharmony_ci NETCONFA_MC_FORWARDING, 69462306a36Sopenharmony_ci dev->ifindex, &in_dev->cnf); 69562306a36Sopenharmony_ci ip_rt_multicast_event(in_dev); 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (v->flags & (VIFF_TUNNEL | VIFF_REGISTER) && !notify) 69962306a36Sopenharmony_ci unregister_netdevice_queue(dev, head); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci netdev_put(dev, &v->dev_tracker); 70262306a36Sopenharmony_ci return 0; 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic void ipmr_cache_free_rcu(struct rcu_head *head) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci struct mr_mfc *c = container_of(head, struct mr_mfc, rcu); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci kmem_cache_free(mrt_cachep, (struct mfc_cache *)c); 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic void ipmr_cache_free(struct mfc_cache *c) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci call_rcu(&c->_c.rcu, ipmr_cache_free_rcu); 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci/* Destroy an unresolved cache entry, killing queued skbs 71862306a36Sopenharmony_ci * and reporting error to netlink readers. 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_cistatic void ipmr_destroy_unres(struct mr_table *mrt, struct mfc_cache *c) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct net *net = read_pnet(&mrt->net); 72362306a36Sopenharmony_ci struct sk_buff *skb; 72462306a36Sopenharmony_ci struct nlmsgerr *e; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci atomic_dec(&mrt->cache_resolve_queue_len); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci while ((skb = skb_dequeue(&c->_c.mfc_un.unres.unresolved))) { 72962306a36Sopenharmony_ci if (ip_hdr(skb)->version == 0) { 73062306a36Sopenharmony_ci struct nlmsghdr *nlh = skb_pull(skb, 73162306a36Sopenharmony_ci sizeof(struct iphdr)); 73262306a36Sopenharmony_ci nlh->nlmsg_type = NLMSG_ERROR; 73362306a36Sopenharmony_ci nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 73462306a36Sopenharmony_ci skb_trim(skb, nlh->nlmsg_len); 73562306a36Sopenharmony_ci e = nlmsg_data(nlh); 73662306a36Sopenharmony_ci e->error = -ETIMEDOUT; 73762306a36Sopenharmony_ci memset(&e->msg, 0, sizeof(e->msg)); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 74062306a36Sopenharmony_ci } else { 74162306a36Sopenharmony_ci kfree_skb(skb); 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci ipmr_cache_free(c); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/* Timer process for the unresolved queue. */ 74962306a36Sopenharmony_cistatic void ipmr_expire_process(struct timer_list *t) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct mr_table *mrt = from_timer(mrt, t, ipmr_expire_timer); 75262306a36Sopenharmony_ci struct mr_mfc *c, *next; 75362306a36Sopenharmony_ci unsigned long expires; 75462306a36Sopenharmony_ci unsigned long now; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (!spin_trylock(&mfc_unres_lock)) { 75762306a36Sopenharmony_ci mod_timer(&mrt->ipmr_expire_timer, jiffies+HZ/10); 75862306a36Sopenharmony_ci return; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (list_empty(&mrt->mfc_unres_queue)) 76262306a36Sopenharmony_ci goto out; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci now = jiffies; 76562306a36Sopenharmony_ci expires = 10*HZ; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) { 76862306a36Sopenharmony_ci if (time_after(c->mfc_un.unres.expires, now)) { 76962306a36Sopenharmony_ci unsigned long interval = c->mfc_un.unres.expires - now; 77062306a36Sopenharmony_ci if (interval < expires) 77162306a36Sopenharmony_ci expires = interval; 77262306a36Sopenharmony_ci continue; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci list_del(&c->list); 77662306a36Sopenharmony_ci mroute_netlink_event(mrt, (struct mfc_cache *)c, RTM_DELROUTE); 77762306a36Sopenharmony_ci ipmr_destroy_unres(mrt, (struct mfc_cache *)c); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (!list_empty(&mrt->mfc_unres_queue)) 78162306a36Sopenharmony_ci mod_timer(&mrt->ipmr_expire_timer, jiffies + expires); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ciout: 78462306a36Sopenharmony_ci spin_unlock(&mfc_unres_lock); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci/* Fill oifs list. It is called under locked mrt_lock. */ 78862306a36Sopenharmony_cistatic void ipmr_update_thresholds(struct mr_table *mrt, struct mr_mfc *cache, 78962306a36Sopenharmony_ci unsigned char *ttls) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci int vifi; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci cache->mfc_un.res.minvif = MAXVIFS; 79462306a36Sopenharmony_ci cache->mfc_un.res.maxvif = 0; 79562306a36Sopenharmony_ci memset(cache->mfc_un.res.ttls, 255, MAXVIFS); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci for (vifi = 0; vifi < mrt->maxvif; vifi++) { 79862306a36Sopenharmony_ci if (VIF_EXISTS(mrt, vifi) && 79962306a36Sopenharmony_ci ttls[vifi] && ttls[vifi] < 255) { 80062306a36Sopenharmony_ci cache->mfc_un.res.ttls[vifi] = ttls[vifi]; 80162306a36Sopenharmony_ci if (cache->mfc_un.res.minvif > vifi) 80262306a36Sopenharmony_ci cache->mfc_un.res.minvif = vifi; 80362306a36Sopenharmony_ci if (cache->mfc_un.res.maxvif <= vifi) 80462306a36Sopenharmony_ci cache->mfc_un.res.maxvif = vifi + 1; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci cache->mfc_un.res.lastuse = jiffies; 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic int vif_add(struct net *net, struct mr_table *mrt, 81162306a36Sopenharmony_ci struct vifctl *vifc, int mrtsock) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct netdev_phys_item_id ppid = { }; 81462306a36Sopenharmony_ci int vifi = vifc->vifc_vifi; 81562306a36Sopenharmony_ci struct vif_device *v = &mrt->vif_table[vifi]; 81662306a36Sopenharmony_ci struct net_device *dev; 81762306a36Sopenharmony_ci struct in_device *in_dev; 81862306a36Sopenharmony_ci int err; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* Is vif busy ? */ 82162306a36Sopenharmony_ci if (VIF_EXISTS(mrt, vifi)) 82262306a36Sopenharmony_ci return -EADDRINUSE; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci switch (vifc->vifc_flags) { 82562306a36Sopenharmony_ci case VIFF_REGISTER: 82662306a36Sopenharmony_ci if (!ipmr_pimsm_enabled()) 82762306a36Sopenharmony_ci return -EINVAL; 82862306a36Sopenharmony_ci /* Special Purpose VIF in PIM 82962306a36Sopenharmony_ci * All the packets will be sent to the daemon 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_ci if (mrt->mroute_reg_vif_num >= 0) 83262306a36Sopenharmony_ci return -EADDRINUSE; 83362306a36Sopenharmony_ci dev = ipmr_reg_vif(net, mrt); 83462306a36Sopenharmony_ci if (!dev) 83562306a36Sopenharmony_ci return -ENOBUFS; 83662306a36Sopenharmony_ci err = dev_set_allmulti(dev, 1); 83762306a36Sopenharmony_ci if (err) { 83862306a36Sopenharmony_ci unregister_netdevice(dev); 83962306a36Sopenharmony_ci dev_put(dev); 84062306a36Sopenharmony_ci return err; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci break; 84362306a36Sopenharmony_ci case VIFF_TUNNEL: 84462306a36Sopenharmony_ci dev = ipmr_new_tunnel(net, vifc); 84562306a36Sopenharmony_ci if (IS_ERR(dev)) 84662306a36Sopenharmony_ci return PTR_ERR(dev); 84762306a36Sopenharmony_ci break; 84862306a36Sopenharmony_ci case VIFF_USE_IFINDEX: 84962306a36Sopenharmony_ci case 0: 85062306a36Sopenharmony_ci if (vifc->vifc_flags == VIFF_USE_IFINDEX) { 85162306a36Sopenharmony_ci dev = dev_get_by_index(net, vifc->vifc_lcl_ifindex); 85262306a36Sopenharmony_ci if (dev && !__in_dev_get_rtnl(dev)) { 85362306a36Sopenharmony_ci dev_put(dev); 85462306a36Sopenharmony_ci return -EADDRNOTAVAIL; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci } else { 85762306a36Sopenharmony_ci dev = ip_dev_find(net, vifc->vifc_lcl_addr.s_addr); 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci if (!dev) 86062306a36Sopenharmony_ci return -EADDRNOTAVAIL; 86162306a36Sopenharmony_ci err = dev_set_allmulti(dev, 1); 86262306a36Sopenharmony_ci if (err) { 86362306a36Sopenharmony_ci dev_put(dev); 86462306a36Sopenharmony_ci return err; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci default: 86862306a36Sopenharmony_ci return -EINVAL; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci in_dev = __in_dev_get_rtnl(dev); 87262306a36Sopenharmony_ci if (!in_dev) { 87362306a36Sopenharmony_ci dev_put(dev); 87462306a36Sopenharmony_ci return -EADDRNOTAVAIL; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++; 87762306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_MC_FORWARDING, 87862306a36Sopenharmony_ci dev->ifindex, &in_dev->cnf); 87962306a36Sopenharmony_ci ip_rt_multicast_event(in_dev); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* Fill in the VIF structures */ 88262306a36Sopenharmony_ci vif_device_init(v, dev, vifc->vifc_rate_limit, 88362306a36Sopenharmony_ci vifc->vifc_threshold, 88462306a36Sopenharmony_ci vifc->vifc_flags | (!mrtsock ? VIFF_STATIC : 0), 88562306a36Sopenharmony_ci (VIFF_TUNNEL | VIFF_REGISTER)); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci err = dev_get_port_parent_id(dev, &ppid, true); 88862306a36Sopenharmony_ci if (err == 0) { 88962306a36Sopenharmony_ci memcpy(v->dev_parent_id.id, ppid.id, ppid.id_len); 89062306a36Sopenharmony_ci v->dev_parent_id.id_len = ppid.id_len; 89162306a36Sopenharmony_ci } else { 89262306a36Sopenharmony_ci v->dev_parent_id.id_len = 0; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci v->local = vifc->vifc_lcl_addr.s_addr; 89662306a36Sopenharmony_ci v->remote = vifc->vifc_rmt_addr.s_addr; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci /* And finish update writing critical data */ 89962306a36Sopenharmony_ci spin_lock(&mrt_lock); 90062306a36Sopenharmony_ci rcu_assign_pointer(v->dev, dev); 90162306a36Sopenharmony_ci netdev_tracker_alloc(dev, &v->dev_tracker, GFP_ATOMIC); 90262306a36Sopenharmony_ci if (v->flags & VIFF_REGISTER) { 90362306a36Sopenharmony_ci /* Pairs with READ_ONCE() in ipmr_cache_report() and reg_vif_xmit() */ 90462306a36Sopenharmony_ci WRITE_ONCE(mrt->mroute_reg_vif_num, vifi); 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci if (vifi+1 > mrt->maxvif) 90762306a36Sopenharmony_ci WRITE_ONCE(mrt->maxvif, vifi + 1); 90862306a36Sopenharmony_ci spin_unlock(&mrt_lock); 90962306a36Sopenharmony_ci call_ipmr_vif_entry_notifiers(net, FIB_EVENT_VIF_ADD, v, dev, 91062306a36Sopenharmony_ci vifi, mrt->id); 91162306a36Sopenharmony_ci return 0; 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci/* called with rcu_read_lock() */ 91562306a36Sopenharmony_cistatic struct mfc_cache *ipmr_cache_find(struct mr_table *mrt, 91662306a36Sopenharmony_ci __be32 origin, 91762306a36Sopenharmony_ci __be32 mcastgrp) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci struct mfc_cache_cmp_arg arg = { 92062306a36Sopenharmony_ci .mfc_mcastgrp = mcastgrp, 92162306a36Sopenharmony_ci .mfc_origin = origin 92262306a36Sopenharmony_ci }; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci return mr_mfc_find(mrt, &arg); 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci/* Look for a (*,G) entry */ 92862306a36Sopenharmony_cistatic struct mfc_cache *ipmr_cache_find_any(struct mr_table *mrt, 92962306a36Sopenharmony_ci __be32 mcastgrp, int vifi) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci struct mfc_cache_cmp_arg arg = { 93262306a36Sopenharmony_ci .mfc_mcastgrp = mcastgrp, 93362306a36Sopenharmony_ci .mfc_origin = htonl(INADDR_ANY) 93462306a36Sopenharmony_ci }; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (mcastgrp == htonl(INADDR_ANY)) 93762306a36Sopenharmony_ci return mr_mfc_find_any_parent(mrt, vifi); 93862306a36Sopenharmony_ci return mr_mfc_find_any(mrt, vifi, &arg); 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci/* Look for a (S,G,iif) entry if parent != -1 */ 94262306a36Sopenharmony_cistatic struct mfc_cache *ipmr_cache_find_parent(struct mr_table *mrt, 94362306a36Sopenharmony_ci __be32 origin, __be32 mcastgrp, 94462306a36Sopenharmony_ci int parent) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci struct mfc_cache_cmp_arg arg = { 94762306a36Sopenharmony_ci .mfc_mcastgrp = mcastgrp, 94862306a36Sopenharmony_ci .mfc_origin = origin, 94962306a36Sopenharmony_ci }; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci return mr_mfc_find_parent(mrt, &arg, parent); 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci/* Allocate a multicast cache entry */ 95562306a36Sopenharmony_cistatic struct mfc_cache *ipmr_cache_alloc(void) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci if (c) { 96062306a36Sopenharmony_ci c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1; 96162306a36Sopenharmony_ci c->_c.mfc_un.res.minvif = MAXVIFS; 96262306a36Sopenharmony_ci c->_c.free = ipmr_cache_free_rcu; 96362306a36Sopenharmony_ci refcount_set(&c->_c.mfc_un.res.refcount, 1); 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci return c; 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic struct mfc_cache *ipmr_cache_alloc_unres(void) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci if (c) { 97362306a36Sopenharmony_ci skb_queue_head_init(&c->_c.mfc_un.unres.unresolved); 97462306a36Sopenharmony_ci c->_c.mfc_un.unres.expires = jiffies + 10 * HZ; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci return c; 97762306a36Sopenharmony_ci} 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci/* A cache entry has gone into a resolved state from queued */ 98062306a36Sopenharmony_cistatic void ipmr_cache_resolve(struct net *net, struct mr_table *mrt, 98162306a36Sopenharmony_ci struct mfc_cache *uc, struct mfc_cache *c) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci struct sk_buff *skb; 98462306a36Sopenharmony_ci struct nlmsgerr *e; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci /* Play the pending entries through our router */ 98762306a36Sopenharmony_ci while ((skb = __skb_dequeue(&uc->_c.mfc_un.unres.unresolved))) { 98862306a36Sopenharmony_ci if (ip_hdr(skb)->version == 0) { 98962306a36Sopenharmony_ci struct nlmsghdr *nlh = skb_pull(skb, 99062306a36Sopenharmony_ci sizeof(struct iphdr)); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (mr_fill_mroute(mrt, skb, &c->_c, 99362306a36Sopenharmony_ci nlmsg_data(nlh)) > 0) { 99462306a36Sopenharmony_ci nlh->nlmsg_len = skb_tail_pointer(skb) - 99562306a36Sopenharmony_ci (u8 *)nlh; 99662306a36Sopenharmony_ci } else { 99762306a36Sopenharmony_ci nlh->nlmsg_type = NLMSG_ERROR; 99862306a36Sopenharmony_ci nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 99962306a36Sopenharmony_ci skb_trim(skb, nlh->nlmsg_len); 100062306a36Sopenharmony_ci e = nlmsg_data(nlh); 100162306a36Sopenharmony_ci e->error = -EMSGSIZE; 100262306a36Sopenharmony_ci memset(&e->msg, 0, sizeof(e->msg)); 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 100662306a36Sopenharmony_ci } else { 100762306a36Sopenharmony_ci rcu_read_lock(); 100862306a36Sopenharmony_ci ip_mr_forward(net, mrt, skb->dev, skb, c, 0); 100962306a36Sopenharmony_ci rcu_read_unlock(); 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci/* Bounce a cache query up to mrouted and netlink. 101562306a36Sopenharmony_ci * 101662306a36Sopenharmony_ci * Called under rcu_read_lock(). 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_cistatic int ipmr_cache_report(const struct mr_table *mrt, 101962306a36Sopenharmony_ci struct sk_buff *pkt, vifi_t vifi, int assert) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci const int ihl = ip_hdrlen(pkt); 102262306a36Sopenharmony_ci struct sock *mroute_sk; 102362306a36Sopenharmony_ci struct igmphdr *igmp; 102462306a36Sopenharmony_ci struct igmpmsg *msg; 102562306a36Sopenharmony_ci struct sk_buff *skb; 102662306a36Sopenharmony_ci int ret; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci mroute_sk = rcu_dereference(mrt->mroute_sk); 102962306a36Sopenharmony_ci if (!mroute_sk) 103062306a36Sopenharmony_ci return -EINVAL; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci if (assert == IGMPMSG_WHOLEPKT || assert == IGMPMSG_WRVIFWHOLE) 103362306a36Sopenharmony_ci skb = skb_realloc_headroom(pkt, sizeof(struct iphdr)); 103462306a36Sopenharmony_ci else 103562306a36Sopenharmony_ci skb = alloc_skb(128, GFP_ATOMIC); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci if (!skb) 103862306a36Sopenharmony_ci return -ENOBUFS; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (assert == IGMPMSG_WHOLEPKT || assert == IGMPMSG_WRVIFWHOLE) { 104162306a36Sopenharmony_ci /* Ugly, but we have no choice with this interface. 104262306a36Sopenharmony_ci * Duplicate old header, fix ihl, length etc. 104362306a36Sopenharmony_ci * And all this only to mangle msg->im_msgtype and 104462306a36Sopenharmony_ci * to set msg->im_mbz to "mbz" :-) 104562306a36Sopenharmony_ci */ 104662306a36Sopenharmony_ci skb_push(skb, sizeof(struct iphdr)); 104762306a36Sopenharmony_ci skb_reset_network_header(skb); 104862306a36Sopenharmony_ci skb_reset_transport_header(skb); 104962306a36Sopenharmony_ci msg = (struct igmpmsg *)skb_network_header(skb); 105062306a36Sopenharmony_ci memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr)); 105162306a36Sopenharmony_ci msg->im_msgtype = assert; 105262306a36Sopenharmony_ci msg->im_mbz = 0; 105362306a36Sopenharmony_ci if (assert == IGMPMSG_WRVIFWHOLE) { 105462306a36Sopenharmony_ci msg->im_vif = vifi; 105562306a36Sopenharmony_ci msg->im_vif_hi = vifi >> 8; 105662306a36Sopenharmony_ci } else { 105762306a36Sopenharmony_ci /* Pairs with WRITE_ONCE() in vif_add() and vif_delete() */ 105862306a36Sopenharmony_ci int vif_num = READ_ONCE(mrt->mroute_reg_vif_num); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci msg->im_vif = vif_num; 106162306a36Sopenharmony_ci msg->im_vif_hi = vif_num >> 8; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2; 106462306a36Sopenharmony_ci ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) + 106562306a36Sopenharmony_ci sizeof(struct iphdr)); 106662306a36Sopenharmony_ci } else { 106762306a36Sopenharmony_ci /* Copy the IP header */ 106862306a36Sopenharmony_ci skb_set_network_header(skb, skb->len); 106962306a36Sopenharmony_ci skb_put(skb, ihl); 107062306a36Sopenharmony_ci skb_copy_to_linear_data(skb, pkt->data, ihl); 107162306a36Sopenharmony_ci /* Flag to the kernel this is a route add */ 107262306a36Sopenharmony_ci ip_hdr(skb)->protocol = 0; 107362306a36Sopenharmony_ci msg = (struct igmpmsg *)skb_network_header(skb); 107462306a36Sopenharmony_ci msg->im_vif = vifi; 107562306a36Sopenharmony_ci msg->im_vif_hi = vifi >> 8; 107662306a36Sopenharmony_ci ipv4_pktinfo_prepare(mroute_sk, pkt, false); 107762306a36Sopenharmony_ci memcpy(skb->cb, pkt->cb, sizeof(skb->cb)); 107862306a36Sopenharmony_ci /* Add our header */ 107962306a36Sopenharmony_ci igmp = skb_put(skb, sizeof(struct igmphdr)); 108062306a36Sopenharmony_ci igmp->type = assert; 108162306a36Sopenharmony_ci msg->im_msgtype = assert; 108262306a36Sopenharmony_ci igmp->code = 0; 108362306a36Sopenharmony_ci ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */ 108462306a36Sopenharmony_ci skb->transport_header = skb->network_header; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci igmpmsg_netlink_event(mrt, skb); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* Deliver to mrouted */ 109062306a36Sopenharmony_ci ret = sock_queue_rcv_skb(mroute_sk, skb); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (ret < 0) { 109362306a36Sopenharmony_ci net_warn_ratelimited("mroute: pending queue full, dropping entries\n"); 109462306a36Sopenharmony_ci kfree_skb(skb); 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci return ret; 109862306a36Sopenharmony_ci} 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci/* Queue a packet for resolution. It gets locked cache entry! */ 110162306a36Sopenharmony_ci/* Called under rcu_read_lock() */ 110262306a36Sopenharmony_cistatic int ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, 110362306a36Sopenharmony_ci struct sk_buff *skb, struct net_device *dev) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci const struct iphdr *iph = ip_hdr(skb); 110662306a36Sopenharmony_ci struct mfc_cache *c; 110762306a36Sopenharmony_ci bool found = false; 110862306a36Sopenharmony_ci int err; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci spin_lock_bh(&mfc_unres_lock); 111162306a36Sopenharmony_ci list_for_each_entry(c, &mrt->mfc_unres_queue, _c.list) { 111262306a36Sopenharmony_ci if (c->mfc_mcastgrp == iph->daddr && 111362306a36Sopenharmony_ci c->mfc_origin == iph->saddr) { 111462306a36Sopenharmony_ci found = true; 111562306a36Sopenharmony_ci break; 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci if (!found) { 112062306a36Sopenharmony_ci /* Create a new entry if allowable */ 112162306a36Sopenharmony_ci c = ipmr_cache_alloc_unres(); 112262306a36Sopenharmony_ci if (!c) { 112362306a36Sopenharmony_ci spin_unlock_bh(&mfc_unres_lock); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci kfree_skb(skb); 112662306a36Sopenharmony_ci return -ENOBUFS; 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci /* Fill in the new cache entry */ 113062306a36Sopenharmony_ci c->_c.mfc_parent = -1; 113162306a36Sopenharmony_ci c->mfc_origin = iph->saddr; 113262306a36Sopenharmony_ci c->mfc_mcastgrp = iph->daddr; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* Reflect first query at mrouted. */ 113562306a36Sopenharmony_ci err = ipmr_cache_report(mrt, skb, vifi, IGMPMSG_NOCACHE); 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (err < 0) { 113862306a36Sopenharmony_ci /* If the report failed throw the cache entry 113962306a36Sopenharmony_ci out - Brad Parker 114062306a36Sopenharmony_ci */ 114162306a36Sopenharmony_ci spin_unlock_bh(&mfc_unres_lock); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci ipmr_cache_free(c); 114462306a36Sopenharmony_ci kfree_skb(skb); 114562306a36Sopenharmony_ci return err; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci atomic_inc(&mrt->cache_resolve_queue_len); 114962306a36Sopenharmony_ci list_add(&c->_c.list, &mrt->mfc_unres_queue); 115062306a36Sopenharmony_ci mroute_netlink_event(mrt, c, RTM_NEWROUTE); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (atomic_read(&mrt->cache_resolve_queue_len) == 1) 115362306a36Sopenharmony_ci mod_timer(&mrt->ipmr_expire_timer, 115462306a36Sopenharmony_ci c->_c.mfc_un.unres.expires); 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci /* See if we can append the packet */ 115862306a36Sopenharmony_ci if (c->_c.mfc_un.unres.unresolved.qlen > 3) { 115962306a36Sopenharmony_ci kfree_skb(skb); 116062306a36Sopenharmony_ci err = -ENOBUFS; 116162306a36Sopenharmony_ci } else { 116262306a36Sopenharmony_ci if (dev) { 116362306a36Sopenharmony_ci skb->dev = dev; 116462306a36Sopenharmony_ci skb->skb_iif = dev->ifindex; 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb); 116762306a36Sopenharmony_ci err = 0; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci spin_unlock_bh(&mfc_unres_lock); 117162306a36Sopenharmony_ci return err; 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci/* MFC cache manipulation by user space mroute daemon */ 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci struct net *net = read_pnet(&mrt->net); 117962306a36Sopenharmony_ci struct mfc_cache *c; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* The entries are added/deleted only under RTNL */ 118262306a36Sopenharmony_ci rcu_read_lock(); 118362306a36Sopenharmony_ci c = ipmr_cache_find_parent(mrt, mfc->mfcc_origin.s_addr, 118462306a36Sopenharmony_ci mfc->mfcc_mcastgrp.s_addr, parent); 118562306a36Sopenharmony_ci rcu_read_unlock(); 118662306a36Sopenharmony_ci if (!c) 118762306a36Sopenharmony_ci return -ENOENT; 118862306a36Sopenharmony_ci rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ipmr_rht_params); 118962306a36Sopenharmony_ci list_del_rcu(&c->_c.list); 119062306a36Sopenharmony_ci call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, c, mrt->id); 119162306a36Sopenharmony_ci mroute_netlink_event(mrt, c, RTM_DELROUTE); 119262306a36Sopenharmony_ci mr_cache_put(&c->_c); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci return 0; 119562306a36Sopenharmony_ci} 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_cistatic int ipmr_mfc_add(struct net *net, struct mr_table *mrt, 119862306a36Sopenharmony_ci struct mfcctl *mfc, int mrtsock, int parent) 119962306a36Sopenharmony_ci{ 120062306a36Sopenharmony_ci struct mfc_cache *uc, *c; 120162306a36Sopenharmony_ci struct mr_mfc *_uc; 120262306a36Sopenharmony_ci bool found; 120362306a36Sopenharmony_ci int ret; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci if (mfc->mfcc_parent >= MAXVIFS) 120662306a36Sopenharmony_ci return -ENFILE; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci /* The entries are added/deleted only under RTNL */ 120962306a36Sopenharmony_ci rcu_read_lock(); 121062306a36Sopenharmony_ci c = ipmr_cache_find_parent(mrt, mfc->mfcc_origin.s_addr, 121162306a36Sopenharmony_ci mfc->mfcc_mcastgrp.s_addr, parent); 121262306a36Sopenharmony_ci rcu_read_unlock(); 121362306a36Sopenharmony_ci if (c) { 121462306a36Sopenharmony_ci spin_lock(&mrt_lock); 121562306a36Sopenharmony_ci c->_c.mfc_parent = mfc->mfcc_parent; 121662306a36Sopenharmony_ci ipmr_update_thresholds(mrt, &c->_c, mfc->mfcc_ttls); 121762306a36Sopenharmony_ci if (!mrtsock) 121862306a36Sopenharmony_ci c->_c.mfc_flags |= MFC_STATIC; 121962306a36Sopenharmony_ci spin_unlock(&mrt_lock); 122062306a36Sopenharmony_ci call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, c, 122162306a36Sopenharmony_ci mrt->id); 122262306a36Sopenharmony_ci mroute_netlink_event(mrt, c, RTM_NEWROUTE); 122362306a36Sopenharmony_ci return 0; 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci if (mfc->mfcc_mcastgrp.s_addr != htonl(INADDR_ANY) && 122762306a36Sopenharmony_ci !ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr)) 122862306a36Sopenharmony_ci return -EINVAL; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci c = ipmr_cache_alloc(); 123162306a36Sopenharmony_ci if (!c) 123262306a36Sopenharmony_ci return -ENOMEM; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci c->mfc_origin = mfc->mfcc_origin.s_addr; 123562306a36Sopenharmony_ci c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr; 123662306a36Sopenharmony_ci c->_c.mfc_parent = mfc->mfcc_parent; 123762306a36Sopenharmony_ci ipmr_update_thresholds(mrt, &c->_c, mfc->mfcc_ttls); 123862306a36Sopenharmony_ci if (!mrtsock) 123962306a36Sopenharmony_ci c->_c.mfc_flags |= MFC_STATIC; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci ret = rhltable_insert_key(&mrt->mfc_hash, &c->cmparg, &c->_c.mnode, 124262306a36Sopenharmony_ci ipmr_rht_params); 124362306a36Sopenharmony_ci if (ret) { 124462306a36Sopenharmony_ci pr_err("ipmr: rhtable insert error %d\n", ret); 124562306a36Sopenharmony_ci ipmr_cache_free(c); 124662306a36Sopenharmony_ci return ret; 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci list_add_tail_rcu(&c->_c.list, &mrt->mfc_cache_list); 124962306a36Sopenharmony_ci /* Check to see if we resolved a queued list. If so we 125062306a36Sopenharmony_ci * need to send on the frames and tidy up. 125162306a36Sopenharmony_ci */ 125262306a36Sopenharmony_ci found = false; 125362306a36Sopenharmony_ci spin_lock_bh(&mfc_unres_lock); 125462306a36Sopenharmony_ci list_for_each_entry(_uc, &mrt->mfc_unres_queue, list) { 125562306a36Sopenharmony_ci uc = (struct mfc_cache *)_uc; 125662306a36Sopenharmony_ci if (uc->mfc_origin == c->mfc_origin && 125762306a36Sopenharmony_ci uc->mfc_mcastgrp == c->mfc_mcastgrp) { 125862306a36Sopenharmony_ci list_del(&_uc->list); 125962306a36Sopenharmony_ci atomic_dec(&mrt->cache_resolve_queue_len); 126062306a36Sopenharmony_ci found = true; 126162306a36Sopenharmony_ci break; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci if (list_empty(&mrt->mfc_unres_queue)) 126562306a36Sopenharmony_ci del_timer(&mrt->ipmr_expire_timer); 126662306a36Sopenharmony_ci spin_unlock_bh(&mfc_unres_lock); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (found) { 126962306a36Sopenharmony_ci ipmr_cache_resolve(net, mrt, uc, c); 127062306a36Sopenharmony_ci ipmr_cache_free(uc); 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_ADD, c, mrt->id); 127362306a36Sopenharmony_ci mroute_netlink_event(mrt, c, RTM_NEWROUTE); 127462306a36Sopenharmony_ci return 0; 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci/* Close the multicast socket, and clear the vif tables etc */ 127862306a36Sopenharmony_cistatic void mroute_clean_tables(struct mr_table *mrt, int flags) 127962306a36Sopenharmony_ci{ 128062306a36Sopenharmony_ci struct net *net = read_pnet(&mrt->net); 128162306a36Sopenharmony_ci struct mr_mfc *c, *tmp; 128262306a36Sopenharmony_ci struct mfc_cache *cache; 128362306a36Sopenharmony_ci LIST_HEAD(list); 128462306a36Sopenharmony_ci int i; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci /* Shut down all active vif entries */ 128762306a36Sopenharmony_ci if (flags & (MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC)) { 128862306a36Sopenharmony_ci for (i = 0; i < mrt->maxvif; i++) { 128962306a36Sopenharmony_ci if (((mrt->vif_table[i].flags & VIFF_STATIC) && 129062306a36Sopenharmony_ci !(flags & MRT_FLUSH_VIFS_STATIC)) || 129162306a36Sopenharmony_ci (!(mrt->vif_table[i].flags & VIFF_STATIC) && !(flags & MRT_FLUSH_VIFS))) 129262306a36Sopenharmony_ci continue; 129362306a36Sopenharmony_ci vif_delete(mrt, i, 0, &list); 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci unregister_netdevice_many(&list); 129662306a36Sopenharmony_ci } 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci /* Wipe the cache */ 129962306a36Sopenharmony_ci if (flags & (MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC)) { 130062306a36Sopenharmony_ci list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) { 130162306a36Sopenharmony_ci if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT_FLUSH_MFC_STATIC)) || 130262306a36Sopenharmony_ci (!(c->mfc_flags & MFC_STATIC) && !(flags & MRT_FLUSH_MFC))) 130362306a36Sopenharmony_ci continue; 130462306a36Sopenharmony_ci rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params); 130562306a36Sopenharmony_ci list_del_rcu(&c->list); 130662306a36Sopenharmony_ci cache = (struct mfc_cache *)c; 130762306a36Sopenharmony_ci call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, cache, 130862306a36Sopenharmony_ci mrt->id); 130962306a36Sopenharmony_ci mroute_netlink_event(mrt, cache, RTM_DELROUTE); 131062306a36Sopenharmony_ci mr_cache_put(c); 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci if (flags & MRT_FLUSH_MFC) { 131562306a36Sopenharmony_ci if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { 131662306a36Sopenharmony_ci spin_lock_bh(&mfc_unres_lock); 131762306a36Sopenharmony_ci list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { 131862306a36Sopenharmony_ci list_del(&c->list); 131962306a36Sopenharmony_ci cache = (struct mfc_cache *)c; 132062306a36Sopenharmony_ci mroute_netlink_event(mrt, cache, RTM_DELROUTE); 132162306a36Sopenharmony_ci ipmr_destroy_unres(mrt, cache); 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci spin_unlock_bh(&mfc_unres_lock); 132462306a36Sopenharmony_ci } 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci} 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci/* called from ip_ra_control(), before an RCU grace period, 132962306a36Sopenharmony_ci * we don't need to call synchronize_rcu() here 133062306a36Sopenharmony_ci */ 133162306a36Sopenharmony_cistatic void mrtsock_destruct(struct sock *sk) 133262306a36Sopenharmony_ci{ 133362306a36Sopenharmony_ci struct net *net = sock_net(sk); 133462306a36Sopenharmony_ci struct mr_table *mrt; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci rtnl_lock(); 133762306a36Sopenharmony_ci ipmr_for_each_table(mrt, net) { 133862306a36Sopenharmony_ci if (sk == rtnl_dereference(mrt->mroute_sk)) { 133962306a36Sopenharmony_ci IPV4_DEVCONF_ALL(net, MC_FORWARDING)--; 134062306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 134162306a36Sopenharmony_ci NETCONFA_MC_FORWARDING, 134262306a36Sopenharmony_ci NETCONFA_IFINDEX_ALL, 134362306a36Sopenharmony_ci net->ipv4.devconf_all); 134462306a36Sopenharmony_ci RCU_INIT_POINTER(mrt->mroute_sk, NULL); 134562306a36Sopenharmony_ci mroute_clean_tables(mrt, MRT_FLUSH_VIFS | MRT_FLUSH_MFC); 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci rtnl_unlock(); 134962306a36Sopenharmony_ci} 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci/* Socket options and virtual interface manipulation. The whole 135262306a36Sopenharmony_ci * virtual interface system is a complete heap, but unfortunately 135362306a36Sopenharmony_ci * that's how BSD mrouted happens to think. Maybe one day with a proper 135462306a36Sopenharmony_ci * MOSPF/PIM router set up we can clean this up. 135562306a36Sopenharmony_ci */ 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ciint ip_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, 135862306a36Sopenharmony_ci unsigned int optlen) 135962306a36Sopenharmony_ci{ 136062306a36Sopenharmony_ci struct net *net = sock_net(sk); 136162306a36Sopenharmony_ci int val, ret = 0, parent = 0; 136262306a36Sopenharmony_ci struct mr_table *mrt; 136362306a36Sopenharmony_ci struct vifctl vif; 136462306a36Sopenharmony_ci struct mfcctl mfc; 136562306a36Sopenharmony_ci bool do_wrvifwhole; 136662306a36Sopenharmony_ci u32 uval; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* There's one exception to the lock - MRT_DONE which needs to unlock */ 136962306a36Sopenharmony_ci rtnl_lock(); 137062306a36Sopenharmony_ci if (sk->sk_type != SOCK_RAW || 137162306a36Sopenharmony_ci inet_sk(sk)->inet_num != IPPROTO_IGMP) { 137262306a36Sopenharmony_ci ret = -EOPNOTSUPP; 137362306a36Sopenharmony_ci goto out_unlock; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 137762306a36Sopenharmony_ci if (!mrt) { 137862306a36Sopenharmony_ci ret = -ENOENT; 137962306a36Sopenharmony_ci goto out_unlock; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci if (optname != MRT_INIT) { 138262306a36Sopenharmony_ci if (sk != rcu_access_pointer(mrt->mroute_sk) && 138362306a36Sopenharmony_ci !ns_capable(net->user_ns, CAP_NET_ADMIN)) { 138462306a36Sopenharmony_ci ret = -EACCES; 138562306a36Sopenharmony_ci goto out_unlock; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci switch (optname) { 139062306a36Sopenharmony_ci case MRT_INIT: 139162306a36Sopenharmony_ci if (optlen != sizeof(int)) { 139262306a36Sopenharmony_ci ret = -EINVAL; 139362306a36Sopenharmony_ci break; 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci if (rtnl_dereference(mrt->mroute_sk)) { 139662306a36Sopenharmony_ci ret = -EADDRINUSE; 139762306a36Sopenharmony_ci break; 139862306a36Sopenharmony_ci } 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci ret = ip_ra_control(sk, 1, mrtsock_destruct); 140162306a36Sopenharmony_ci if (ret == 0) { 140262306a36Sopenharmony_ci rcu_assign_pointer(mrt->mroute_sk, sk); 140362306a36Sopenharmony_ci IPV4_DEVCONF_ALL(net, MC_FORWARDING)++; 140462306a36Sopenharmony_ci inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 140562306a36Sopenharmony_ci NETCONFA_MC_FORWARDING, 140662306a36Sopenharmony_ci NETCONFA_IFINDEX_ALL, 140762306a36Sopenharmony_ci net->ipv4.devconf_all); 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci break; 141062306a36Sopenharmony_ci case MRT_DONE: 141162306a36Sopenharmony_ci if (sk != rcu_access_pointer(mrt->mroute_sk)) { 141262306a36Sopenharmony_ci ret = -EACCES; 141362306a36Sopenharmony_ci } else { 141462306a36Sopenharmony_ci /* We need to unlock here because mrtsock_destruct takes 141562306a36Sopenharmony_ci * care of rtnl itself and we can't change that due to 141662306a36Sopenharmony_ci * the IP_ROUTER_ALERT setsockopt which runs without it. 141762306a36Sopenharmony_ci */ 141862306a36Sopenharmony_ci rtnl_unlock(); 141962306a36Sopenharmony_ci ret = ip_ra_control(sk, 0, NULL); 142062306a36Sopenharmony_ci goto out; 142162306a36Sopenharmony_ci } 142262306a36Sopenharmony_ci break; 142362306a36Sopenharmony_ci case MRT_ADD_VIF: 142462306a36Sopenharmony_ci case MRT_DEL_VIF: 142562306a36Sopenharmony_ci if (optlen != sizeof(vif)) { 142662306a36Sopenharmony_ci ret = -EINVAL; 142762306a36Sopenharmony_ci break; 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci if (copy_from_sockptr(&vif, optval, sizeof(vif))) { 143062306a36Sopenharmony_ci ret = -EFAULT; 143162306a36Sopenharmony_ci break; 143262306a36Sopenharmony_ci } 143362306a36Sopenharmony_ci if (vif.vifc_vifi >= MAXVIFS) { 143462306a36Sopenharmony_ci ret = -ENFILE; 143562306a36Sopenharmony_ci break; 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci if (optname == MRT_ADD_VIF) { 143862306a36Sopenharmony_ci ret = vif_add(net, mrt, &vif, 143962306a36Sopenharmony_ci sk == rtnl_dereference(mrt->mroute_sk)); 144062306a36Sopenharmony_ci } else { 144162306a36Sopenharmony_ci ret = vif_delete(mrt, vif.vifc_vifi, 0, NULL); 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci break; 144462306a36Sopenharmony_ci /* Manipulate the forwarding caches. These live 144562306a36Sopenharmony_ci * in a sort of kernel/user symbiosis. 144662306a36Sopenharmony_ci */ 144762306a36Sopenharmony_ci case MRT_ADD_MFC: 144862306a36Sopenharmony_ci case MRT_DEL_MFC: 144962306a36Sopenharmony_ci parent = -1; 145062306a36Sopenharmony_ci fallthrough; 145162306a36Sopenharmony_ci case MRT_ADD_MFC_PROXY: 145262306a36Sopenharmony_ci case MRT_DEL_MFC_PROXY: 145362306a36Sopenharmony_ci if (optlen != sizeof(mfc)) { 145462306a36Sopenharmony_ci ret = -EINVAL; 145562306a36Sopenharmony_ci break; 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci if (copy_from_sockptr(&mfc, optval, sizeof(mfc))) { 145862306a36Sopenharmony_ci ret = -EFAULT; 145962306a36Sopenharmony_ci break; 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci if (parent == 0) 146262306a36Sopenharmony_ci parent = mfc.mfcc_parent; 146362306a36Sopenharmony_ci if (optname == MRT_DEL_MFC || optname == MRT_DEL_MFC_PROXY) 146462306a36Sopenharmony_ci ret = ipmr_mfc_delete(mrt, &mfc, parent); 146562306a36Sopenharmony_ci else 146662306a36Sopenharmony_ci ret = ipmr_mfc_add(net, mrt, &mfc, 146762306a36Sopenharmony_ci sk == rtnl_dereference(mrt->mroute_sk), 146862306a36Sopenharmony_ci parent); 146962306a36Sopenharmony_ci break; 147062306a36Sopenharmony_ci case MRT_FLUSH: 147162306a36Sopenharmony_ci if (optlen != sizeof(val)) { 147262306a36Sopenharmony_ci ret = -EINVAL; 147362306a36Sopenharmony_ci break; 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci if (copy_from_sockptr(&val, optval, sizeof(val))) { 147662306a36Sopenharmony_ci ret = -EFAULT; 147762306a36Sopenharmony_ci break; 147862306a36Sopenharmony_ci } 147962306a36Sopenharmony_ci mroute_clean_tables(mrt, val); 148062306a36Sopenharmony_ci break; 148162306a36Sopenharmony_ci /* Control PIM assert. */ 148262306a36Sopenharmony_ci case MRT_ASSERT: 148362306a36Sopenharmony_ci if (optlen != sizeof(val)) { 148462306a36Sopenharmony_ci ret = -EINVAL; 148562306a36Sopenharmony_ci break; 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci if (copy_from_sockptr(&val, optval, sizeof(val))) { 148862306a36Sopenharmony_ci ret = -EFAULT; 148962306a36Sopenharmony_ci break; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci mrt->mroute_do_assert = val; 149262306a36Sopenharmony_ci break; 149362306a36Sopenharmony_ci case MRT_PIM: 149462306a36Sopenharmony_ci if (!ipmr_pimsm_enabled()) { 149562306a36Sopenharmony_ci ret = -ENOPROTOOPT; 149662306a36Sopenharmony_ci break; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci if (optlen != sizeof(val)) { 149962306a36Sopenharmony_ci ret = -EINVAL; 150062306a36Sopenharmony_ci break; 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci if (copy_from_sockptr(&val, optval, sizeof(val))) { 150362306a36Sopenharmony_ci ret = -EFAULT; 150462306a36Sopenharmony_ci break; 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci do_wrvifwhole = (val == IGMPMSG_WRVIFWHOLE); 150862306a36Sopenharmony_ci val = !!val; 150962306a36Sopenharmony_ci if (val != mrt->mroute_do_pim) { 151062306a36Sopenharmony_ci mrt->mroute_do_pim = val; 151162306a36Sopenharmony_ci mrt->mroute_do_assert = val; 151262306a36Sopenharmony_ci mrt->mroute_do_wrvifwhole = do_wrvifwhole; 151362306a36Sopenharmony_ci } 151462306a36Sopenharmony_ci break; 151562306a36Sopenharmony_ci case MRT_TABLE: 151662306a36Sopenharmony_ci if (!IS_BUILTIN(CONFIG_IP_MROUTE_MULTIPLE_TABLES)) { 151762306a36Sopenharmony_ci ret = -ENOPROTOOPT; 151862306a36Sopenharmony_ci break; 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci if (optlen != sizeof(uval)) { 152162306a36Sopenharmony_ci ret = -EINVAL; 152262306a36Sopenharmony_ci break; 152362306a36Sopenharmony_ci } 152462306a36Sopenharmony_ci if (copy_from_sockptr(&uval, optval, sizeof(uval))) { 152562306a36Sopenharmony_ci ret = -EFAULT; 152662306a36Sopenharmony_ci break; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci if (sk == rtnl_dereference(mrt->mroute_sk)) { 153062306a36Sopenharmony_ci ret = -EBUSY; 153162306a36Sopenharmony_ci } else { 153262306a36Sopenharmony_ci mrt = ipmr_new_table(net, uval); 153362306a36Sopenharmony_ci if (IS_ERR(mrt)) 153462306a36Sopenharmony_ci ret = PTR_ERR(mrt); 153562306a36Sopenharmony_ci else 153662306a36Sopenharmony_ci raw_sk(sk)->ipmr_table = uval; 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci break; 153962306a36Sopenharmony_ci /* Spurious command, or MRT_VERSION which you cannot set. */ 154062306a36Sopenharmony_ci default: 154162306a36Sopenharmony_ci ret = -ENOPROTOOPT; 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ciout_unlock: 154462306a36Sopenharmony_ci rtnl_unlock(); 154562306a36Sopenharmony_ciout: 154662306a36Sopenharmony_ci return ret; 154762306a36Sopenharmony_ci} 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci/* Execute if this ioctl is a special mroute ioctl */ 155062306a36Sopenharmony_ciint ipmr_sk_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) 155162306a36Sopenharmony_ci{ 155262306a36Sopenharmony_ci switch (cmd) { 155362306a36Sopenharmony_ci /* These userspace buffers will be consumed by ipmr_ioctl() */ 155462306a36Sopenharmony_ci case SIOCGETVIFCNT: { 155562306a36Sopenharmony_ci struct sioc_vif_req buffer; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci return sock_ioctl_inout(sk, cmd, arg, &buffer, 155862306a36Sopenharmony_ci sizeof(buffer)); 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci case SIOCGETSGCNT: { 156162306a36Sopenharmony_ci struct sioc_sg_req buffer; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci return sock_ioctl_inout(sk, cmd, arg, &buffer, 156462306a36Sopenharmony_ci sizeof(buffer)); 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci /* return code > 0 means that the ioctl was not executed */ 156862306a36Sopenharmony_ci return 1; 156962306a36Sopenharmony_ci} 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci/* Getsock opt support for the multicast routing system. */ 157262306a36Sopenharmony_ciint ip_mroute_getsockopt(struct sock *sk, int optname, sockptr_t optval, 157362306a36Sopenharmony_ci sockptr_t optlen) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci int olr; 157662306a36Sopenharmony_ci int val; 157762306a36Sopenharmony_ci struct net *net = sock_net(sk); 157862306a36Sopenharmony_ci struct mr_table *mrt; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci if (sk->sk_type != SOCK_RAW || 158162306a36Sopenharmony_ci inet_sk(sk)->inet_num != IPPROTO_IGMP) 158262306a36Sopenharmony_ci return -EOPNOTSUPP; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 158562306a36Sopenharmony_ci if (!mrt) 158662306a36Sopenharmony_ci return -ENOENT; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci switch (optname) { 158962306a36Sopenharmony_ci case MRT_VERSION: 159062306a36Sopenharmony_ci val = 0x0305; 159162306a36Sopenharmony_ci break; 159262306a36Sopenharmony_ci case MRT_PIM: 159362306a36Sopenharmony_ci if (!ipmr_pimsm_enabled()) 159462306a36Sopenharmony_ci return -ENOPROTOOPT; 159562306a36Sopenharmony_ci val = mrt->mroute_do_pim; 159662306a36Sopenharmony_ci break; 159762306a36Sopenharmony_ci case MRT_ASSERT: 159862306a36Sopenharmony_ci val = mrt->mroute_do_assert; 159962306a36Sopenharmony_ci break; 160062306a36Sopenharmony_ci default: 160162306a36Sopenharmony_ci return -ENOPROTOOPT; 160262306a36Sopenharmony_ci } 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci if (copy_from_sockptr(&olr, optlen, sizeof(int))) 160562306a36Sopenharmony_ci return -EFAULT; 160662306a36Sopenharmony_ci if (olr < 0) 160762306a36Sopenharmony_ci return -EINVAL; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci olr = min_t(unsigned int, olr, sizeof(int)); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci if (copy_to_sockptr(optlen, &olr, sizeof(int))) 161262306a36Sopenharmony_ci return -EFAULT; 161362306a36Sopenharmony_ci if (copy_to_sockptr(optval, &val, olr)) 161462306a36Sopenharmony_ci return -EFAULT; 161562306a36Sopenharmony_ci return 0; 161662306a36Sopenharmony_ci} 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci/* The IP multicast ioctl support routines. */ 161962306a36Sopenharmony_ciint ipmr_ioctl(struct sock *sk, int cmd, void *arg) 162062306a36Sopenharmony_ci{ 162162306a36Sopenharmony_ci struct vif_device *vif; 162262306a36Sopenharmony_ci struct mfc_cache *c; 162362306a36Sopenharmony_ci struct net *net = sock_net(sk); 162462306a36Sopenharmony_ci struct sioc_vif_req *vr; 162562306a36Sopenharmony_ci struct sioc_sg_req *sr; 162662306a36Sopenharmony_ci struct mr_table *mrt; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 162962306a36Sopenharmony_ci if (!mrt) 163062306a36Sopenharmony_ci return -ENOENT; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci switch (cmd) { 163362306a36Sopenharmony_ci case SIOCGETVIFCNT: 163462306a36Sopenharmony_ci vr = (struct sioc_vif_req *)arg; 163562306a36Sopenharmony_ci if (vr->vifi >= mrt->maxvif) 163662306a36Sopenharmony_ci return -EINVAL; 163762306a36Sopenharmony_ci vr->vifi = array_index_nospec(vr->vifi, mrt->maxvif); 163862306a36Sopenharmony_ci rcu_read_lock(); 163962306a36Sopenharmony_ci vif = &mrt->vif_table[vr->vifi]; 164062306a36Sopenharmony_ci if (VIF_EXISTS(mrt, vr->vifi)) { 164162306a36Sopenharmony_ci vr->icount = READ_ONCE(vif->pkt_in); 164262306a36Sopenharmony_ci vr->ocount = READ_ONCE(vif->pkt_out); 164362306a36Sopenharmony_ci vr->ibytes = READ_ONCE(vif->bytes_in); 164462306a36Sopenharmony_ci vr->obytes = READ_ONCE(vif->bytes_out); 164562306a36Sopenharmony_ci rcu_read_unlock(); 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci return 0; 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci rcu_read_unlock(); 165062306a36Sopenharmony_ci return -EADDRNOTAVAIL; 165162306a36Sopenharmony_ci case SIOCGETSGCNT: 165262306a36Sopenharmony_ci sr = (struct sioc_sg_req *)arg; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci rcu_read_lock(); 165562306a36Sopenharmony_ci c = ipmr_cache_find(mrt, sr->src.s_addr, sr->grp.s_addr); 165662306a36Sopenharmony_ci if (c) { 165762306a36Sopenharmony_ci sr->pktcnt = c->_c.mfc_un.res.pkt; 165862306a36Sopenharmony_ci sr->bytecnt = c->_c.mfc_un.res.bytes; 165962306a36Sopenharmony_ci sr->wrong_if = c->_c.mfc_un.res.wrong_if; 166062306a36Sopenharmony_ci rcu_read_unlock(); 166162306a36Sopenharmony_ci return 0; 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci rcu_read_unlock(); 166462306a36Sopenharmony_ci return -EADDRNOTAVAIL; 166562306a36Sopenharmony_ci default: 166662306a36Sopenharmony_ci return -ENOIOCTLCMD; 166762306a36Sopenharmony_ci } 166862306a36Sopenharmony_ci} 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 167162306a36Sopenharmony_cistruct compat_sioc_sg_req { 167262306a36Sopenharmony_ci struct in_addr src; 167362306a36Sopenharmony_ci struct in_addr grp; 167462306a36Sopenharmony_ci compat_ulong_t pktcnt; 167562306a36Sopenharmony_ci compat_ulong_t bytecnt; 167662306a36Sopenharmony_ci compat_ulong_t wrong_if; 167762306a36Sopenharmony_ci}; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_cistruct compat_sioc_vif_req { 168062306a36Sopenharmony_ci vifi_t vifi; /* Which iface */ 168162306a36Sopenharmony_ci compat_ulong_t icount; 168262306a36Sopenharmony_ci compat_ulong_t ocount; 168362306a36Sopenharmony_ci compat_ulong_t ibytes; 168462306a36Sopenharmony_ci compat_ulong_t obytes; 168562306a36Sopenharmony_ci}; 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ciint ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) 168862306a36Sopenharmony_ci{ 168962306a36Sopenharmony_ci struct compat_sioc_sg_req sr; 169062306a36Sopenharmony_ci struct compat_sioc_vif_req vr; 169162306a36Sopenharmony_ci struct vif_device *vif; 169262306a36Sopenharmony_ci struct mfc_cache *c; 169362306a36Sopenharmony_ci struct net *net = sock_net(sk); 169462306a36Sopenharmony_ci struct mr_table *mrt; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT); 169762306a36Sopenharmony_ci if (!mrt) 169862306a36Sopenharmony_ci return -ENOENT; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci switch (cmd) { 170162306a36Sopenharmony_ci case SIOCGETVIFCNT: 170262306a36Sopenharmony_ci if (copy_from_user(&vr, arg, sizeof(vr))) 170362306a36Sopenharmony_ci return -EFAULT; 170462306a36Sopenharmony_ci if (vr.vifi >= mrt->maxvif) 170562306a36Sopenharmony_ci return -EINVAL; 170662306a36Sopenharmony_ci vr.vifi = array_index_nospec(vr.vifi, mrt->maxvif); 170762306a36Sopenharmony_ci rcu_read_lock(); 170862306a36Sopenharmony_ci vif = &mrt->vif_table[vr.vifi]; 170962306a36Sopenharmony_ci if (VIF_EXISTS(mrt, vr.vifi)) { 171062306a36Sopenharmony_ci vr.icount = READ_ONCE(vif->pkt_in); 171162306a36Sopenharmony_ci vr.ocount = READ_ONCE(vif->pkt_out); 171262306a36Sopenharmony_ci vr.ibytes = READ_ONCE(vif->bytes_in); 171362306a36Sopenharmony_ci vr.obytes = READ_ONCE(vif->bytes_out); 171462306a36Sopenharmony_ci rcu_read_unlock(); 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci if (copy_to_user(arg, &vr, sizeof(vr))) 171762306a36Sopenharmony_ci return -EFAULT; 171862306a36Sopenharmony_ci return 0; 171962306a36Sopenharmony_ci } 172062306a36Sopenharmony_ci rcu_read_unlock(); 172162306a36Sopenharmony_ci return -EADDRNOTAVAIL; 172262306a36Sopenharmony_ci case SIOCGETSGCNT: 172362306a36Sopenharmony_ci if (copy_from_user(&sr, arg, sizeof(sr))) 172462306a36Sopenharmony_ci return -EFAULT; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci rcu_read_lock(); 172762306a36Sopenharmony_ci c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr); 172862306a36Sopenharmony_ci if (c) { 172962306a36Sopenharmony_ci sr.pktcnt = c->_c.mfc_un.res.pkt; 173062306a36Sopenharmony_ci sr.bytecnt = c->_c.mfc_un.res.bytes; 173162306a36Sopenharmony_ci sr.wrong_if = c->_c.mfc_un.res.wrong_if; 173262306a36Sopenharmony_ci rcu_read_unlock(); 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci if (copy_to_user(arg, &sr, sizeof(sr))) 173562306a36Sopenharmony_ci return -EFAULT; 173662306a36Sopenharmony_ci return 0; 173762306a36Sopenharmony_ci } 173862306a36Sopenharmony_ci rcu_read_unlock(); 173962306a36Sopenharmony_ci return -EADDRNOTAVAIL; 174062306a36Sopenharmony_ci default: 174162306a36Sopenharmony_ci return -ENOIOCTLCMD; 174262306a36Sopenharmony_ci } 174362306a36Sopenharmony_ci} 174462306a36Sopenharmony_ci#endif 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_cistatic int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr) 174762306a36Sopenharmony_ci{ 174862306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 174962306a36Sopenharmony_ci struct net *net = dev_net(dev); 175062306a36Sopenharmony_ci struct mr_table *mrt; 175162306a36Sopenharmony_ci struct vif_device *v; 175262306a36Sopenharmony_ci int ct; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci if (event != NETDEV_UNREGISTER) 175562306a36Sopenharmony_ci return NOTIFY_DONE; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci ipmr_for_each_table(mrt, net) { 175862306a36Sopenharmony_ci v = &mrt->vif_table[0]; 175962306a36Sopenharmony_ci for (ct = 0; ct < mrt->maxvif; ct++, v++) { 176062306a36Sopenharmony_ci if (rcu_access_pointer(v->dev) == dev) 176162306a36Sopenharmony_ci vif_delete(mrt, ct, 1, NULL); 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci } 176462306a36Sopenharmony_ci return NOTIFY_DONE; 176562306a36Sopenharmony_ci} 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_cistatic struct notifier_block ip_mr_notifier = { 176862306a36Sopenharmony_ci .notifier_call = ipmr_device_event, 176962306a36Sopenharmony_ci}; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci/* Encapsulate a packet by attaching a valid IPIP header to it. 177262306a36Sopenharmony_ci * This avoids tunnel drivers and other mess and gives us the speed so 177362306a36Sopenharmony_ci * important for multicast video. 177462306a36Sopenharmony_ci */ 177562306a36Sopenharmony_cistatic void ip_encap(struct net *net, struct sk_buff *skb, 177662306a36Sopenharmony_ci __be32 saddr, __be32 daddr) 177762306a36Sopenharmony_ci{ 177862306a36Sopenharmony_ci struct iphdr *iph; 177962306a36Sopenharmony_ci const struct iphdr *old_iph = ip_hdr(skb); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci skb_push(skb, sizeof(struct iphdr)); 178262306a36Sopenharmony_ci skb->transport_header = skb->network_header; 178362306a36Sopenharmony_ci skb_reset_network_header(skb); 178462306a36Sopenharmony_ci iph = ip_hdr(skb); 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci iph->version = 4; 178762306a36Sopenharmony_ci iph->tos = old_iph->tos; 178862306a36Sopenharmony_ci iph->ttl = old_iph->ttl; 178962306a36Sopenharmony_ci iph->frag_off = 0; 179062306a36Sopenharmony_ci iph->daddr = daddr; 179162306a36Sopenharmony_ci iph->saddr = saddr; 179262306a36Sopenharmony_ci iph->protocol = IPPROTO_IPIP; 179362306a36Sopenharmony_ci iph->ihl = 5; 179462306a36Sopenharmony_ci iph->tot_len = htons(skb->len); 179562306a36Sopenharmony_ci ip_select_ident(net, skb, NULL); 179662306a36Sopenharmony_ci ip_send_check(iph); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); 179962306a36Sopenharmony_ci nf_reset_ct(skb); 180062306a36Sopenharmony_ci} 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_cistatic inline int ipmr_forward_finish(struct net *net, struct sock *sk, 180362306a36Sopenharmony_ci struct sk_buff *skb) 180462306a36Sopenharmony_ci{ 180562306a36Sopenharmony_ci struct ip_options *opt = &(IPCB(skb)->opt); 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci if (unlikely(opt->optlen)) 181062306a36Sopenharmony_ci ip_forward_options(skb); 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci return dst_output(net, sk, skb); 181362306a36Sopenharmony_ci} 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci#ifdef CONFIG_NET_SWITCHDEV 181662306a36Sopenharmony_cistatic bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt, 181762306a36Sopenharmony_ci int in_vifi, int out_vifi) 181862306a36Sopenharmony_ci{ 181962306a36Sopenharmony_ci struct vif_device *out_vif = &mrt->vif_table[out_vifi]; 182062306a36Sopenharmony_ci struct vif_device *in_vif = &mrt->vif_table[in_vifi]; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci if (!skb->offload_l3_fwd_mark) 182362306a36Sopenharmony_ci return false; 182462306a36Sopenharmony_ci if (!out_vif->dev_parent_id.id_len || !in_vif->dev_parent_id.id_len) 182562306a36Sopenharmony_ci return false; 182662306a36Sopenharmony_ci return netdev_phys_item_id_same(&out_vif->dev_parent_id, 182762306a36Sopenharmony_ci &in_vif->dev_parent_id); 182862306a36Sopenharmony_ci} 182962306a36Sopenharmony_ci#else 183062306a36Sopenharmony_cistatic bool ipmr_forward_offloaded(struct sk_buff *skb, struct mr_table *mrt, 183162306a36Sopenharmony_ci int in_vifi, int out_vifi) 183262306a36Sopenharmony_ci{ 183362306a36Sopenharmony_ci return false; 183462306a36Sopenharmony_ci} 183562306a36Sopenharmony_ci#endif 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci/* Processing handlers for ipmr_forward, under rcu_read_lock() */ 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_cistatic void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, 184062306a36Sopenharmony_ci int in_vifi, struct sk_buff *skb, int vifi) 184162306a36Sopenharmony_ci{ 184262306a36Sopenharmony_ci const struct iphdr *iph = ip_hdr(skb); 184362306a36Sopenharmony_ci struct vif_device *vif = &mrt->vif_table[vifi]; 184462306a36Sopenharmony_ci struct net_device *vif_dev; 184562306a36Sopenharmony_ci struct net_device *dev; 184662306a36Sopenharmony_ci struct rtable *rt; 184762306a36Sopenharmony_ci struct flowi4 fl4; 184862306a36Sopenharmony_ci int encap = 0; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci vif_dev = vif_dev_read(vif); 185162306a36Sopenharmony_ci if (!vif_dev) 185262306a36Sopenharmony_ci goto out_free; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci if (vif->flags & VIFF_REGISTER) { 185562306a36Sopenharmony_ci WRITE_ONCE(vif->pkt_out, vif->pkt_out + 1); 185662306a36Sopenharmony_ci WRITE_ONCE(vif->bytes_out, vif->bytes_out + skb->len); 185762306a36Sopenharmony_ci DEV_STATS_ADD(vif_dev, tx_bytes, skb->len); 185862306a36Sopenharmony_ci DEV_STATS_INC(vif_dev, tx_packets); 185962306a36Sopenharmony_ci ipmr_cache_report(mrt, skb, vifi, IGMPMSG_WHOLEPKT); 186062306a36Sopenharmony_ci goto out_free; 186162306a36Sopenharmony_ci } 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci if (ipmr_forward_offloaded(skb, mrt, in_vifi, vifi)) 186462306a36Sopenharmony_ci goto out_free; 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci if (vif->flags & VIFF_TUNNEL) { 186762306a36Sopenharmony_ci rt = ip_route_output_ports(net, &fl4, NULL, 186862306a36Sopenharmony_ci vif->remote, vif->local, 186962306a36Sopenharmony_ci 0, 0, 187062306a36Sopenharmony_ci IPPROTO_IPIP, 187162306a36Sopenharmony_ci RT_TOS(iph->tos), vif->link); 187262306a36Sopenharmony_ci if (IS_ERR(rt)) 187362306a36Sopenharmony_ci goto out_free; 187462306a36Sopenharmony_ci encap = sizeof(struct iphdr); 187562306a36Sopenharmony_ci } else { 187662306a36Sopenharmony_ci rt = ip_route_output_ports(net, &fl4, NULL, iph->daddr, 0, 187762306a36Sopenharmony_ci 0, 0, 187862306a36Sopenharmony_ci IPPROTO_IPIP, 187962306a36Sopenharmony_ci RT_TOS(iph->tos), vif->link); 188062306a36Sopenharmony_ci if (IS_ERR(rt)) 188162306a36Sopenharmony_ci goto out_free; 188262306a36Sopenharmony_ci } 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci dev = rt->dst.dev; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) { 188762306a36Sopenharmony_ci /* Do not fragment multicasts. Alas, IPv4 does not 188862306a36Sopenharmony_ci * allow to send ICMP, so that packets will disappear 188962306a36Sopenharmony_ci * to blackhole. 189062306a36Sopenharmony_ci */ 189162306a36Sopenharmony_ci IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); 189262306a36Sopenharmony_ci ip_rt_put(rt); 189362306a36Sopenharmony_ci goto out_free; 189462306a36Sopenharmony_ci } 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci encap += LL_RESERVED_SPACE(dev) + rt->dst.header_len; 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci if (skb_cow(skb, encap)) { 189962306a36Sopenharmony_ci ip_rt_put(rt); 190062306a36Sopenharmony_ci goto out_free; 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci WRITE_ONCE(vif->pkt_out, vif->pkt_out + 1); 190462306a36Sopenharmony_ci WRITE_ONCE(vif->bytes_out, vif->bytes_out + skb->len); 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci skb_dst_drop(skb); 190762306a36Sopenharmony_ci skb_dst_set(skb, &rt->dst); 190862306a36Sopenharmony_ci ip_decrease_ttl(ip_hdr(skb)); 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci /* FIXME: forward and output firewalls used to be called here. 191162306a36Sopenharmony_ci * What do we do with netfilter? -- RR 191262306a36Sopenharmony_ci */ 191362306a36Sopenharmony_ci if (vif->flags & VIFF_TUNNEL) { 191462306a36Sopenharmony_ci ip_encap(net, skb, vif->local, vif->remote); 191562306a36Sopenharmony_ci /* FIXME: extra output firewall step used to be here. --RR */ 191662306a36Sopenharmony_ci DEV_STATS_INC(vif_dev, tx_packets); 191762306a36Sopenharmony_ci DEV_STATS_ADD(vif_dev, tx_bytes, skb->len); 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci IPCB(skb)->flags |= IPSKB_FORWARDED; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci /* RFC1584 teaches, that DVMRP/PIM router must deliver packets locally 192362306a36Sopenharmony_ci * not only before forwarding, but after forwarding on all output 192462306a36Sopenharmony_ci * interfaces. It is clear, if mrouter runs a multicasting 192562306a36Sopenharmony_ci * program, it should receive packets not depending to what interface 192662306a36Sopenharmony_ci * program is joined. 192762306a36Sopenharmony_ci * If we will not make it, the program will have to join on all 192862306a36Sopenharmony_ci * interfaces. On the other hand, multihoming host (or router, but 192962306a36Sopenharmony_ci * not mrouter) cannot join to more than one interface - it will 193062306a36Sopenharmony_ci * result in receiving multiple packets. 193162306a36Sopenharmony_ci */ 193262306a36Sopenharmony_ci NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD, 193362306a36Sopenharmony_ci net, NULL, skb, skb->dev, dev, 193462306a36Sopenharmony_ci ipmr_forward_finish); 193562306a36Sopenharmony_ci return; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ciout_free: 193862306a36Sopenharmony_ci kfree_skb(skb); 193962306a36Sopenharmony_ci} 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci/* Called with mrt_lock or rcu_read_lock() */ 194262306a36Sopenharmony_cistatic int ipmr_find_vif(const struct mr_table *mrt, struct net_device *dev) 194362306a36Sopenharmony_ci{ 194462306a36Sopenharmony_ci int ct; 194562306a36Sopenharmony_ci /* Pairs with WRITE_ONCE() in vif_delete()/vif_add() */ 194662306a36Sopenharmony_ci for (ct = READ_ONCE(mrt->maxvif) - 1; ct >= 0; ct--) { 194762306a36Sopenharmony_ci if (rcu_access_pointer(mrt->vif_table[ct].dev) == dev) 194862306a36Sopenharmony_ci break; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci return ct; 195162306a36Sopenharmony_ci} 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci/* "local" means that we should preserve one skb (for local delivery) */ 195462306a36Sopenharmony_ci/* Called uner rcu_read_lock() */ 195562306a36Sopenharmony_cistatic void ip_mr_forward(struct net *net, struct mr_table *mrt, 195662306a36Sopenharmony_ci struct net_device *dev, struct sk_buff *skb, 195762306a36Sopenharmony_ci struct mfc_cache *c, int local) 195862306a36Sopenharmony_ci{ 195962306a36Sopenharmony_ci int true_vifi = ipmr_find_vif(mrt, dev); 196062306a36Sopenharmony_ci int psend = -1; 196162306a36Sopenharmony_ci int vif, ct; 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci vif = c->_c.mfc_parent; 196462306a36Sopenharmony_ci c->_c.mfc_un.res.pkt++; 196562306a36Sopenharmony_ci c->_c.mfc_un.res.bytes += skb->len; 196662306a36Sopenharmony_ci c->_c.mfc_un.res.lastuse = jiffies; 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci if (c->mfc_origin == htonl(INADDR_ANY) && true_vifi >= 0) { 196962306a36Sopenharmony_ci struct mfc_cache *cache_proxy; 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci /* For an (*,G) entry, we only check that the incoming 197262306a36Sopenharmony_ci * interface is part of the static tree. 197362306a36Sopenharmony_ci */ 197462306a36Sopenharmony_ci cache_proxy = mr_mfc_find_any_parent(mrt, vif); 197562306a36Sopenharmony_ci if (cache_proxy && 197662306a36Sopenharmony_ci cache_proxy->_c.mfc_un.res.ttls[true_vifi] < 255) 197762306a36Sopenharmony_ci goto forward; 197862306a36Sopenharmony_ci } 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci /* Wrong interface: drop packet and (maybe) send PIM assert. */ 198162306a36Sopenharmony_ci if (rcu_access_pointer(mrt->vif_table[vif].dev) != dev) { 198262306a36Sopenharmony_ci if (rt_is_output_route(skb_rtable(skb))) { 198362306a36Sopenharmony_ci /* It is our own packet, looped back. 198462306a36Sopenharmony_ci * Very complicated situation... 198562306a36Sopenharmony_ci * 198662306a36Sopenharmony_ci * The best workaround until routing daemons will be 198762306a36Sopenharmony_ci * fixed is not to redistribute packet, if it was 198862306a36Sopenharmony_ci * send through wrong interface. It means, that 198962306a36Sopenharmony_ci * multicast applications WILL NOT work for 199062306a36Sopenharmony_ci * (S,G), which have default multicast route pointing 199162306a36Sopenharmony_ci * to wrong oif. In any case, it is not a good 199262306a36Sopenharmony_ci * idea to use multicasting applications on router. 199362306a36Sopenharmony_ci */ 199462306a36Sopenharmony_ci goto dont_forward; 199562306a36Sopenharmony_ci } 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci c->_c.mfc_un.res.wrong_if++; 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci if (true_vifi >= 0 && mrt->mroute_do_assert && 200062306a36Sopenharmony_ci /* pimsm uses asserts, when switching from RPT to SPT, 200162306a36Sopenharmony_ci * so that we cannot check that packet arrived on an oif. 200262306a36Sopenharmony_ci * It is bad, but otherwise we would need to move pretty 200362306a36Sopenharmony_ci * large chunk of pimd to kernel. Ough... --ANK 200462306a36Sopenharmony_ci */ 200562306a36Sopenharmony_ci (mrt->mroute_do_pim || 200662306a36Sopenharmony_ci c->_c.mfc_un.res.ttls[true_vifi] < 255) && 200762306a36Sopenharmony_ci time_after(jiffies, 200862306a36Sopenharmony_ci c->_c.mfc_un.res.last_assert + 200962306a36Sopenharmony_ci MFC_ASSERT_THRESH)) { 201062306a36Sopenharmony_ci c->_c.mfc_un.res.last_assert = jiffies; 201162306a36Sopenharmony_ci ipmr_cache_report(mrt, skb, true_vifi, IGMPMSG_WRONGVIF); 201262306a36Sopenharmony_ci if (mrt->mroute_do_wrvifwhole) 201362306a36Sopenharmony_ci ipmr_cache_report(mrt, skb, true_vifi, 201462306a36Sopenharmony_ci IGMPMSG_WRVIFWHOLE); 201562306a36Sopenharmony_ci } 201662306a36Sopenharmony_ci goto dont_forward; 201762306a36Sopenharmony_ci } 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ciforward: 202062306a36Sopenharmony_ci WRITE_ONCE(mrt->vif_table[vif].pkt_in, 202162306a36Sopenharmony_ci mrt->vif_table[vif].pkt_in + 1); 202262306a36Sopenharmony_ci WRITE_ONCE(mrt->vif_table[vif].bytes_in, 202362306a36Sopenharmony_ci mrt->vif_table[vif].bytes_in + skb->len); 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci /* Forward the frame */ 202662306a36Sopenharmony_ci if (c->mfc_origin == htonl(INADDR_ANY) && 202762306a36Sopenharmony_ci c->mfc_mcastgrp == htonl(INADDR_ANY)) { 202862306a36Sopenharmony_ci if (true_vifi >= 0 && 202962306a36Sopenharmony_ci true_vifi != c->_c.mfc_parent && 203062306a36Sopenharmony_ci ip_hdr(skb)->ttl > 203162306a36Sopenharmony_ci c->_c.mfc_un.res.ttls[c->_c.mfc_parent]) { 203262306a36Sopenharmony_ci /* It's an (*,*) entry and the packet is not coming from 203362306a36Sopenharmony_ci * the upstream: forward the packet to the upstream 203462306a36Sopenharmony_ci * only. 203562306a36Sopenharmony_ci */ 203662306a36Sopenharmony_ci psend = c->_c.mfc_parent; 203762306a36Sopenharmony_ci goto last_forward; 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci goto dont_forward; 204062306a36Sopenharmony_ci } 204162306a36Sopenharmony_ci for (ct = c->_c.mfc_un.res.maxvif - 1; 204262306a36Sopenharmony_ci ct >= c->_c.mfc_un.res.minvif; ct--) { 204362306a36Sopenharmony_ci /* For (*,G) entry, don't forward to the incoming interface */ 204462306a36Sopenharmony_ci if ((c->mfc_origin != htonl(INADDR_ANY) || 204562306a36Sopenharmony_ci ct != true_vifi) && 204662306a36Sopenharmony_ci ip_hdr(skb)->ttl > c->_c.mfc_un.res.ttls[ct]) { 204762306a36Sopenharmony_ci if (psend != -1) { 204862306a36Sopenharmony_ci struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci if (skb2) 205162306a36Sopenharmony_ci ipmr_queue_xmit(net, mrt, true_vifi, 205262306a36Sopenharmony_ci skb2, psend); 205362306a36Sopenharmony_ci } 205462306a36Sopenharmony_ci psend = ct; 205562306a36Sopenharmony_ci } 205662306a36Sopenharmony_ci } 205762306a36Sopenharmony_cilast_forward: 205862306a36Sopenharmony_ci if (psend != -1) { 205962306a36Sopenharmony_ci if (local) { 206062306a36Sopenharmony_ci struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci if (skb2) 206362306a36Sopenharmony_ci ipmr_queue_xmit(net, mrt, true_vifi, skb2, 206462306a36Sopenharmony_ci psend); 206562306a36Sopenharmony_ci } else { 206662306a36Sopenharmony_ci ipmr_queue_xmit(net, mrt, true_vifi, skb, psend); 206762306a36Sopenharmony_ci return; 206862306a36Sopenharmony_ci } 206962306a36Sopenharmony_ci } 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_cidont_forward: 207262306a36Sopenharmony_ci if (!local) 207362306a36Sopenharmony_ci kfree_skb(skb); 207462306a36Sopenharmony_ci} 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_cistatic struct mr_table *ipmr_rt_fib_lookup(struct net *net, struct sk_buff *skb) 207762306a36Sopenharmony_ci{ 207862306a36Sopenharmony_ci struct rtable *rt = skb_rtable(skb); 207962306a36Sopenharmony_ci struct iphdr *iph = ip_hdr(skb); 208062306a36Sopenharmony_ci struct flowi4 fl4 = { 208162306a36Sopenharmony_ci .daddr = iph->daddr, 208262306a36Sopenharmony_ci .saddr = iph->saddr, 208362306a36Sopenharmony_ci .flowi4_tos = RT_TOS(iph->tos), 208462306a36Sopenharmony_ci .flowi4_oif = (rt_is_output_route(rt) ? 208562306a36Sopenharmony_ci skb->dev->ifindex : 0), 208662306a36Sopenharmony_ci .flowi4_iif = (rt_is_output_route(rt) ? 208762306a36Sopenharmony_ci LOOPBACK_IFINDEX : 208862306a36Sopenharmony_ci skb->dev->ifindex), 208962306a36Sopenharmony_ci .flowi4_mark = skb->mark, 209062306a36Sopenharmony_ci }; 209162306a36Sopenharmony_ci struct mr_table *mrt; 209262306a36Sopenharmony_ci int err; 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_ci err = ipmr_fib_lookup(net, &fl4, &mrt); 209562306a36Sopenharmony_ci if (err) 209662306a36Sopenharmony_ci return ERR_PTR(err); 209762306a36Sopenharmony_ci return mrt; 209862306a36Sopenharmony_ci} 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci/* Multicast packets for forwarding arrive here 210162306a36Sopenharmony_ci * Called with rcu_read_lock(); 210262306a36Sopenharmony_ci */ 210362306a36Sopenharmony_ciint ip_mr_input(struct sk_buff *skb) 210462306a36Sopenharmony_ci{ 210562306a36Sopenharmony_ci struct mfc_cache *cache; 210662306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 210762306a36Sopenharmony_ci int local = skb_rtable(skb)->rt_flags & RTCF_LOCAL; 210862306a36Sopenharmony_ci struct mr_table *mrt; 210962306a36Sopenharmony_ci struct net_device *dev; 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci /* skb->dev passed in is the loX master dev for vrfs. 211262306a36Sopenharmony_ci * As there are no vifs associated with loopback devices, 211362306a36Sopenharmony_ci * get the proper interface that does have a vif associated with it. 211462306a36Sopenharmony_ci */ 211562306a36Sopenharmony_ci dev = skb->dev; 211662306a36Sopenharmony_ci if (netif_is_l3_master(skb->dev)) { 211762306a36Sopenharmony_ci dev = dev_get_by_index_rcu(net, IPCB(skb)->iif); 211862306a36Sopenharmony_ci if (!dev) { 211962306a36Sopenharmony_ci kfree_skb(skb); 212062306a36Sopenharmony_ci return -ENODEV; 212162306a36Sopenharmony_ci } 212262306a36Sopenharmony_ci } 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci /* Packet is looped back after forward, it should not be 212562306a36Sopenharmony_ci * forwarded second time, but still can be delivered locally. 212662306a36Sopenharmony_ci */ 212762306a36Sopenharmony_ci if (IPCB(skb)->flags & IPSKB_FORWARDED) 212862306a36Sopenharmony_ci goto dont_forward; 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci mrt = ipmr_rt_fib_lookup(net, skb); 213162306a36Sopenharmony_ci if (IS_ERR(mrt)) { 213262306a36Sopenharmony_ci kfree_skb(skb); 213362306a36Sopenharmony_ci return PTR_ERR(mrt); 213462306a36Sopenharmony_ci } 213562306a36Sopenharmony_ci if (!local) { 213662306a36Sopenharmony_ci if (IPCB(skb)->opt.router_alert) { 213762306a36Sopenharmony_ci if (ip_call_ra_chain(skb)) 213862306a36Sopenharmony_ci return 0; 213962306a36Sopenharmony_ci } else if (ip_hdr(skb)->protocol == IPPROTO_IGMP) { 214062306a36Sopenharmony_ci /* IGMPv1 (and broken IGMPv2 implementations sort of 214162306a36Sopenharmony_ci * Cisco IOS <= 11.2(8)) do not put router alert 214262306a36Sopenharmony_ci * option to IGMP packets destined to routable 214362306a36Sopenharmony_ci * groups. It is very bad, because it means 214462306a36Sopenharmony_ci * that we can forward NO IGMP messages. 214562306a36Sopenharmony_ci */ 214662306a36Sopenharmony_ci struct sock *mroute_sk; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci mroute_sk = rcu_dereference(mrt->mroute_sk); 214962306a36Sopenharmony_ci if (mroute_sk) { 215062306a36Sopenharmony_ci nf_reset_ct(skb); 215162306a36Sopenharmony_ci raw_rcv(mroute_sk, skb); 215262306a36Sopenharmony_ci return 0; 215362306a36Sopenharmony_ci } 215462306a36Sopenharmony_ci } 215562306a36Sopenharmony_ci } 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci /* already under rcu_read_lock() */ 215862306a36Sopenharmony_ci cache = ipmr_cache_find(mrt, ip_hdr(skb)->saddr, ip_hdr(skb)->daddr); 215962306a36Sopenharmony_ci if (!cache) { 216062306a36Sopenharmony_ci int vif = ipmr_find_vif(mrt, dev); 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci if (vif >= 0) 216362306a36Sopenharmony_ci cache = ipmr_cache_find_any(mrt, ip_hdr(skb)->daddr, 216462306a36Sopenharmony_ci vif); 216562306a36Sopenharmony_ci } 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci /* No usable cache entry */ 216862306a36Sopenharmony_ci if (!cache) { 216962306a36Sopenharmony_ci int vif; 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci if (local) { 217262306a36Sopenharmony_ci struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 217362306a36Sopenharmony_ci ip_local_deliver(skb); 217462306a36Sopenharmony_ci if (!skb2) 217562306a36Sopenharmony_ci return -ENOBUFS; 217662306a36Sopenharmony_ci skb = skb2; 217762306a36Sopenharmony_ci } 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci vif = ipmr_find_vif(mrt, dev); 218062306a36Sopenharmony_ci if (vif >= 0) 218162306a36Sopenharmony_ci return ipmr_cache_unresolved(mrt, vif, skb, dev); 218262306a36Sopenharmony_ci kfree_skb(skb); 218362306a36Sopenharmony_ci return -ENODEV; 218462306a36Sopenharmony_ci } 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci ip_mr_forward(net, mrt, dev, skb, cache, local); 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci if (local) 218962306a36Sopenharmony_ci return ip_local_deliver(skb); 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci return 0; 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_cidont_forward: 219462306a36Sopenharmony_ci if (local) 219562306a36Sopenharmony_ci return ip_local_deliver(skb); 219662306a36Sopenharmony_ci kfree_skb(skb); 219762306a36Sopenharmony_ci return 0; 219862306a36Sopenharmony_ci} 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci#ifdef CONFIG_IP_PIMSM_V1 220162306a36Sopenharmony_ci/* Handle IGMP messages of PIMv1 */ 220262306a36Sopenharmony_ciint pim_rcv_v1(struct sk_buff *skb) 220362306a36Sopenharmony_ci{ 220462306a36Sopenharmony_ci struct igmphdr *pim; 220562306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 220662306a36Sopenharmony_ci struct mr_table *mrt; 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 220962306a36Sopenharmony_ci goto drop; 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci pim = igmp_hdr(skb); 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci mrt = ipmr_rt_fib_lookup(net, skb); 221462306a36Sopenharmony_ci if (IS_ERR(mrt)) 221562306a36Sopenharmony_ci goto drop; 221662306a36Sopenharmony_ci if (!mrt->mroute_do_pim || 221762306a36Sopenharmony_ci pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) 221862306a36Sopenharmony_ci goto drop; 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci if (__pim_rcv(mrt, skb, sizeof(*pim))) { 222162306a36Sopenharmony_cidrop: 222262306a36Sopenharmony_ci kfree_skb(skb); 222362306a36Sopenharmony_ci } 222462306a36Sopenharmony_ci return 0; 222562306a36Sopenharmony_ci} 222662306a36Sopenharmony_ci#endif 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci#ifdef CONFIG_IP_PIMSM_V2 222962306a36Sopenharmony_cistatic int pim_rcv(struct sk_buff *skb) 223062306a36Sopenharmony_ci{ 223162306a36Sopenharmony_ci struct pimreghdr *pim; 223262306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 223362306a36Sopenharmony_ci struct mr_table *mrt; 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr))) 223662306a36Sopenharmony_ci goto drop; 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci pim = (struct pimreghdr *)skb_transport_header(skb); 223962306a36Sopenharmony_ci if (pim->type != ((PIM_VERSION << 4) | (PIM_TYPE_REGISTER)) || 224062306a36Sopenharmony_ci (pim->flags & PIM_NULL_REGISTER) || 224162306a36Sopenharmony_ci (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && 224262306a36Sopenharmony_ci csum_fold(skb_checksum(skb, 0, skb->len, 0)))) 224362306a36Sopenharmony_ci goto drop; 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci mrt = ipmr_rt_fib_lookup(net, skb); 224662306a36Sopenharmony_ci if (IS_ERR(mrt)) 224762306a36Sopenharmony_ci goto drop; 224862306a36Sopenharmony_ci if (__pim_rcv(mrt, skb, sizeof(*pim))) { 224962306a36Sopenharmony_cidrop: 225062306a36Sopenharmony_ci kfree_skb(skb); 225162306a36Sopenharmony_ci } 225262306a36Sopenharmony_ci return 0; 225362306a36Sopenharmony_ci} 225462306a36Sopenharmony_ci#endif 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ciint ipmr_get_route(struct net *net, struct sk_buff *skb, 225762306a36Sopenharmony_ci __be32 saddr, __be32 daddr, 225862306a36Sopenharmony_ci struct rtmsg *rtm, u32 portid) 225962306a36Sopenharmony_ci{ 226062306a36Sopenharmony_ci struct mfc_cache *cache; 226162306a36Sopenharmony_ci struct mr_table *mrt; 226262306a36Sopenharmony_ci int err; 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 226562306a36Sopenharmony_ci if (!mrt) 226662306a36Sopenharmony_ci return -ENOENT; 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci rcu_read_lock(); 226962306a36Sopenharmony_ci cache = ipmr_cache_find(mrt, saddr, daddr); 227062306a36Sopenharmony_ci if (!cache && skb->dev) { 227162306a36Sopenharmony_ci int vif = ipmr_find_vif(mrt, skb->dev); 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci if (vif >= 0) 227462306a36Sopenharmony_ci cache = ipmr_cache_find_any(mrt, daddr, vif); 227562306a36Sopenharmony_ci } 227662306a36Sopenharmony_ci if (!cache) { 227762306a36Sopenharmony_ci struct sk_buff *skb2; 227862306a36Sopenharmony_ci struct iphdr *iph; 227962306a36Sopenharmony_ci struct net_device *dev; 228062306a36Sopenharmony_ci int vif = -1; 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci dev = skb->dev; 228362306a36Sopenharmony_ci if (dev) 228462306a36Sopenharmony_ci vif = ipmr_find_vif(mrt, dev); 228562306a36Sopenharmony_ci if (vif < 0) { 228662306a36Sopenharmony_ci rcu_read_unlock(); 228762306a36Sopenharmony_ci return -ENODEV; 228862306a36Sopenharmony_ci } 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci skb2 = skb_realloc_headroom(skb, sizeof(struct iphdr)); 229162306a36Sopenharmony_ci if (!skb2) { 229262306a36Sopenharmony_ci rcu_read_unlock(); 229362306a36Sopenharmony_ci return -ENOMEM; 229462306a36Sopenharmony_ci } 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci NETLINK_CB(skb2).portid = portid; 229762306a36Sopenharmony_ci skb_push(skb2, sizeof(struct iphdr)); 229862306a36Sopenharmony_ci skb_reset_network_header(skb2); 229962306a36Sopenharmony_ci iph = ip_hdr(skb2); 230062306a36Sopenharmony_ci iph->ihl = sizeof(struct iphdr) >> 2; 230162306a36Sopenharmony_ci iph->saddr = saddr; 230262306a36Sopenharmony_ci iph->daddr = daddr; 230362306a36Sopenharmony_ci iph->version = 0; 230462306a36Sopenharmony_ci err = ipmr_cache_unresolved(mrt, vif, skb2, dev); 230562306a36Sopenharmony_ci rcu_read_unlock(); 230662306a36Sopenharmony_ci return err; 230762306a36Sopenharmony_ci } 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_ci err = mr_fill_mroute(mrt, skb, &cache->_c, rtm); 231062306a36Sopenharmony_ci rcu_read_unlock(); 231162306a36Sopenharmony_ci return err; 231262306a36Sopenharmony_ci} 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_cistatic int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 231562306a36Sopenharmony_ci u32 portid, u32 seq, struct mfc_cache *c, int cmd, 231662306a36Sopenharmony_ci int flags) 231762306a36Sopenharmony_ci{ 231862306a36Sopenharmony_ci struct nlmsghdr *nlh; 231962306a36Sopenharmony_ci struct rtmsg *rtm; 232062306a36Sopenharmony_ci int err; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags); 232362306a36Sopenharmony_ci if (!nlh) 232462306a36Sopenharmony_ci return -EMSGSIZE; 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci rtm = nlmsg_data(nlh); 232762306a36Sopenharmony_ci rtm->rtm_family = RTNL_FAMILY_IPMR; 232862306a36Sopenharmony_ci rtm->rtm_dst_len = 32; 232962306a36Sopenharmony_ci rtm->rtm_src_len = 32; 233062306a36Sopenharmony_ci rtm->rtm_tos = 0; 233162306a36Sopenharmony_ci rtm->rtm_table = mrt->id; 233262306a36Sopenharmony_ci if (nla_put_u32(skb, RTA_TABLE, mrt->id)) 233362306a36Sopenharmony_ci goto nla_put_failure; 233462306a36Sopenharmony_ci rtm->rtm_type = RTN_MULTICAST; 233562306a36Sopenharmony_ci rtm->rtm_scope = RT_SCOPE_UNIVERSE; 233662306a36Sopenharmony_ci if (c->_c.mfc_flags & MFC_STATIC) 233762306a36Sopenharmony_ci rtm->rtm_protocol = RTPROT_STATIC; 233862306a36Sopenharmony_ci else 233962306a36Sopenharmony_ci rtm->rtm_protocol = RTPROT_MROUTED; 234062306a36Sopenharmony_ci rtm->rtm_flags = 0; 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci if (nla_put_in_addr(skb, RTA_SRC, c->mfc_origin) || 234362306a36Sopenharmony_ci nla_put_in_addr(skb, RTA_DST, c->mfc_mcastgrp)) 234462306a36Sopenharmony_ci goto nla_put_failure; 234562306a36Sopenharmony_ci err = mr_fill_mroute(mrt, skb, &c->_c, rtm); 234662306a36Sopenharmony_ci /* do not break the dump if cache is unresolved */ 234762306a36Sopenharmony_ci if (err < 0 && err != -ENOENT) 234862306a36Sopenharmony_ci goto nla_put_failure; 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci nlmsg_end(skb, nlh); 235162306a36Sopenharmony_ci return 0; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_cinla_put_failure: 235462306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 235562306a36Sopenharmony_ci return -EMSGSIZE; 235662306a36Sopenharmony_ci} 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_cistatic int _ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 235962306a36Sopenharmony_ci u32 portid, u32 seq, struct mr_mfc *c, int cmd, 236062306a36Sopenharmony_ci int flags) 236162306a36Sopenharmony_ci{ 236262306a36Sopenharmony_ci return ipmr_fill_mroute(mrt, skb, portid, seq, (struct mfc_cache *)c, 236362306a36Sopenharmony_ci cmd, flags); 236462306a36Sopenharmony_ci} 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_cistatic size_t mroute_msgsize(bool unresolved, int maxvif) 236762306a36Sopenharmony_ci{ 236862306a36Sopenharmony_ci size_t len = 236962306a36Sopenharmony_ci NLMSG_ALIGN(sizeof(struct rtmsg)) 237062306a36Sopenharmony_ci + nla_total_size(4) /* RTA_TABLE */ 237162306a36Sopenharmony_ci + nla_total_size(4) /* RTA_SRC */ 237262306a36Sopenharmony_ci + nla_total_size(4) /* RTA_DST */ 237362306a36Sopenharmony_ci ; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci if (!unresolved) 237662306a36Sopenharmony_ci len = len 237762306a36Sopenharmony_ci + nla_total_size(4) /* RTA_IIF */ 237862306a36Sopenharmony_ci + nla_total_size(0) /* RTA_MULTIPATH */ 237962306a36Sopenharmony_ci + maxvif * NLA_ALIGN(sizeof(struct rtnexthop)) 238062306a36Sopenharmony_ci /* RTA_MFC_STATS */ 238162306a36Sopenharmony_ci + nla_total_size_64bit(sizeof(struct rta_mfc_stats)) 238262306a36Sopenharmony_ci ; 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci return len; 238562306a36Sopenharmony_ci} 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_cistatic void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, 238862306a36Sopenharmony_ci int cmd) 238962306a36Sopenharmony_ci{ 239062306a36Sopenharmony_ci struct net *net = read_pnet(&mrt->net); 239162306a36Sopenharmony_ci struct sk_buff *skb; 239262306a36Sopenharmony_ci int err = -ENOBUFS; 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci skb = nlmsg_new(mroute_msgsize(mfc->_c.mfc_parent >= MAXVIFS, 239562306a36Sopenharmony_ci mrt->maxvif), 239662306a36Sopenharmony_ci GFP_ATOMIC); 239762306a36Sopenharmony_ci if (!skb) 239862306a36Sopenharmony_ci goto errout; 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci err = ipmr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0); 240162306a36Sopenharmony_ci if (err < 0) 240262306a36Sopenharmony_ci goto errout; 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE, NULL, GFP_ATOMIC); 240562306a36Sopenharmony_ci return; 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_cierrout: 240862306a36Sopenharmony_ci kfree_skb(skb); 240962306a36Sopenharmony_ci if (err < 0) 241062306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE, err); 241162306a36Sopenharmony_ci} 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_cistatic size_t igmpmsg_netlink_msgsize(size_t payloadlen) 241462306a36Sopenharmony_ci{ 241562306a36Sopenharmony_ci size_t len = 241662306a36Sopenharmony_ci NLMSG_ALIGN(sizeof(struct rtgenmsg)) 241762306a36Sopenharmony_ci + nla_total_size(1) /* IPMRA_CREPORT_MSGTYPE */ 241862306a36Sopenharmony_ci + nla_total_size(4) /* IPMRA_CREPORT_VIF_ID */ 241962306a36Sopenharmony_ci + nla_total_size(4) /* IPMRA_CREPORT_SRC_ADDR */ 242062306a36Sopenharmony_ci + nla_total_size(4) /* IPMRA_CREPORT_DST_ADDR */ 242162306a36Sopenharmony_ci + nla_total_size(4) /* IPMRA_CREPORT_TABLE */ 242262306a36Sopenharmony_ci /* IPMRA_CREPORT_PKT */ 242362306a36Sopenharmony_ci + nla_total_size(payloadlen) 242462306a36Sopenharmony_ci ; 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci return len; 242762306a36Sopenharmony_ci} 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_cistatic void igmpmsg_netlink_event(const struct mr_table *mrt, struct sk_buff *pkt) 243062306a36Sopenharmony_ci{ 243162306a36Sopenharmony_ci struct net *net = read_pnet(&mrt->net); 243262306a36Sopenharmony_ci struct nlmsghdr *nlh; 243362306a36Sopenharmony_ci struct rtgenmsg *rtgenm; 243462306a36Sopenharmony_ci struct igmpmsg *msg; 243562306a36Sopenharmony_ci struct sk_buff *skb; 243662306a36Sopenharmony_ci struct nlattr *nla; 243762306a36Sopenharmony_ci int payloadlen; 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci payloadlen = pkt->len - sizeof(struct igmpmsg); 244062306a36Sopenharmony_ci msg = (struct igmpmsg *)skb_network_header(pkt); 244162306a36Sopenharmony_ci 244262306a36Sopenharmony_ci skb = nlmsg_new(igmpmsg_netlink_msgsize(payloadlen), GFP_ATOMIC); 244362306a36Sopenharmony_ci if (!skb) 244462306a36Sopenharmony_ci goto errout; 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci nlh = nlmsg_put(skb, 0, 0, RTM_NEWCACHEREPORT, 244762306a36Sopenharmony_ci sizeof(struct rtgenmsg), 0); 244862306a36Sopenharmony_ci if (!nlh) 244962306a36Sopenharmony_ci goto errout; 245062306a36Sopenharmony_ci rtgenm = nlmsg_data(nlh); 245162306a36Sopenharmony_ci rtgenm->rtgen_family = RTNL_FAMILY_IPMR; 245262306a36Sopenharmony_ci if (nla_put_u8(skb, IPMRA_CREPORT_MSGTYPE, msg->im_msgtype) || 245362306a36Sopenharmony_ci nla_put_u32(skb, IPMRA_CREPORT_VIF_ID, msg->im_vif | (msg->im_vif_hi << 8)) || 245462306a36Sopenharmony_ci nla_put_in_addr(skb, IPMRA_CREPORT_SRC_ADDR, 245562306a36Sopenharmony_ci msg->im_src.s_addr) || 245662306a36Sopenharmony_ci nla_put_in_addr(skb, IPMRA_CREPORT_DST_ADDR, 245762306a36Sopenharmony_ci msg->im_dst.s_addr) || 245862306a36Sopenharmony_ci nla_put_u32(skb, IPMRA_CREPORT_TABLE, mrt->id)) 245962306a36Sopenharmony_ci goto nla_put_failure; 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci nla = nla_reserve(skb, IPMRA_CREPORT_PKT, payloadlen); 246262306a36Sopenharmony_ci if (!nla || skb_copy_bits(pkt, sizeof(struct igmpmsg), 246362306a36Sopenharmony_ci nla_data(nla), payloadlen)) 246462306a36Sopenharmony_ci goto nla_put_failure; 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci nlmsg_end(skb, nlh); 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE_R, NULL, GFP_ATOMIC); 246962306a36Sopenharmony_ci return; 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_cinla_put_failure: 247262306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 247362306a36Sopenharmony_cierrout: 247462306a36Sopenharmony_ci kfree_skb(skb); 247562306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE_R, -ENOBUFS); 247662306a36Sopenharmony_ci} 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_cistatic int ipmr_rtm_valid_getroute_req(struct sk_buff *skb, 247962306a36Sopenharmony_ci const struct nlmsghdr *nlh, 248062306a36Sopenharmony_ci struct nlattr **tb, 248162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 248262306a36Sopenharmony_ci{ 248362306a36Sopenharmony_ci struct rtmsg *rtm; 248462306a36Sopenharmony_ci int i, err; 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { 248762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Invalid header for multicast route get request"); 248862306a36Sopenharmony_ci return -EINVAL; 248962306a36Sopenharmony_ci } 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci if (!netlink_strict_get_check(skb)) 249262306a36Sopenharmony_ci return nlmsg_parse_deprecated(nlh, sizeof(*rtm), tb, RTA_MAX, 249362306a36Sopenharmony_ci rtm_ipv4_policy, extack); 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci rtm = nlmsg_data(nlh); 249662306a36Sopenharmony_ci if ((rtm->rtm_src_len && rtm->rtm_src_len != 32) || 249762306a36Sopenharmony_ci (rtm->rtm_dst_len && rtm->rtm_dst_len != 32) || 249862306a36Sopenharmony_ci rtm->rtm_tos || rtm->rtm_table || rtm->rtm_protocol || 249962306a36Sopenharmony_ci rtm->rtm_scope || rtm->rtm_type || rtm->rtm_flags) { 250062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for multicast route get request"); 250162306a36Sopenharmony_ci return -EINVAL; 250262306a36Sopenharmony_ci } 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, 250562306a36Sopenharmony_ci rtm_ipv4_policy, extack); 250662306a36Sopenharmony_ci if (err) 250762306a36Sopenharmony_ci return err; 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci if ((tb[RTA_SRC] && !rtm->rtm_src_len) || 251062306a36Sopenharmony_ci (tb[RTA_DST] && !rtm->rtm_dst_len)) { 251162306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: rtm_src_len and rtm_dst_len must be 32 for IPv4"); 251262306a36Sopenharmony_ci return -EINVAL; 251362306a36Sopenharmony_ci } 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci for (i = 0; i <= RTA_MAX; i++) { 251662306a36Sopenharmony_ci if (!tb[i]) 251762306a36Sopenharmony_ci continue; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci switch (i) { 252062306a36Sopenharmony_ci case RTA_SRC: 252162306a36Sopenharmony_ci case RTA_DST: 252262306a36Sopenharmony_ci case RTA_TABLE: 252362306a36Sopenharmony_ci break; 252462306a36Sopenharmony_ci default: 252562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in multicast route get request"); 252662306a36Sopenharmony_ci return -EINVAL; 252762306a36Sopenharmony_ci } 252862306a36Sopenharmony_ci } 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci return 0; 253162306a36Sopenharmony_ci} 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_cistatic int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, 253462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 253562306a36Sopenharmony_ci{ 253662306a36Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 253762306a36Sopenharmony_ci struct nlattr *tb[RTA_MAX + 1]; 253862306a36Sopenharmony_ci struct sk_buff *skb = NULL; 253962306a36Sopenharmony_ci struct mfc_cache *cache; 254062306a36Sopenharmony_ci struct mr_table *mrt; 254162306a36Sopenharmony_ci __be32 src, grp; 254262306a36Sopenharmony_ci u32 tableid; 254362306a36Sopenharmony_ci int err; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci err = ipmr_rtm_valid_getroute_req(in_skb, nlh, tb, extack); 254662306a36Sopenharmony_ci if (err < 0) 254762306a36Sopenharmony_ci goto errout; 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci src = tb[RTA_SRC] ? nla_get_in_addr(tb[RTA_SRC]) : 0; 255062306a36Sopenharmony_ci grp = tb[RTA_DST] ? nla_get_in_addr(tb[RTA_DST]) : 0; 255162306a36Sopenharmony_ci tableid = tb[RTA_TABLE] ? nla_get_u32(tb[RTA_TABLE]) : 0; 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci mrt = ipmr_get_table(net, tableid ? tableid : RT_TABLE_DEFAULT); 255462306a36Sopenharmony_ci if (!mrt) { 255562306a36Sopenharmony_ci err = -ENOENT; 255662306a36Sopenharmony_ci goto errout_free; 255762306a36Sopenharmony_ci } 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci /* entries are added/deleted only under RTNL */ 256062306a36Sopenharmony_ci rcu_read_lock(); 256162306a36Sopenharmony_ci cache = ipmr_cache_find(mrt, src, grp); 256262306a36Sopenharmony_ci rcu_read_unlock(); 256362306a36Sopenharmony_ci if (!cache) { 256462306a36Sopenharmony_ci err = -ENOENT; 256562306a36Sopenharmony_ci goto errout_free; 256662306a36Sopenharmony_ci } 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci skb = nlmsg_new(mroute_msgsize(false, mrt->maxvif), GFP_KERNEL); 256962306a36Sopenharmony_ci if (!skb) { 257062306a36Sopenharmony_ci err = -ENOBUFS; 257162306a36Sopenharmony_ci goto errout_free; 257262306a36Sopenharmony_ci } 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci err = ipmr_fill_mroute(mrt, skb, NETLINK_CB(in_skb).portid, 257562306a36Sopenharmony_ci nlh->nlmsg_seq, cache, 257662306a36Sopenharmony_ci RTM_NEWROUTE, 0); 257762306a36Sopenharmony_ci if (err < 0) 257862306a36Sopenharmony_ci goto errout_free; 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_ci err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_cierrout: 258362306a36Sopenharmony_ci return err; 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_cierrout_free: 258662306a36Sopenharmony_ci kfree_skb(skb); 258762306a36Sopenharmony_ci goto errout; 258862306a36Sopenharmony_ci} 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_cistatic int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) 259162306a36Sopenharmony_ci{ 259262306a36Sopenharmony_ci struct fib_dump_filter filter = {}; 259362306a36Sopenharmony_ci int err; 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci if (cb->strict_check) { 259662306a36Sopenharmony_ci err = ip_valid_fib_dump_req(sock_net(skb->sk), cb->nlh, 259762306a36Sopenharmony_ci &filter, cb); 259862306a36Sopenharmony_ci if (err < 0) 259962306a36Sopenharmony_ci return err; 260062306a36Sopenharmony_ci } 260162306a36Sopenharmony_ci 260262306a36Sopenharmony_ci if (filter.table_id) { 260362306a36Sopenharmony_ci struct mr_table *mrt; 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_ci mrt = ipmr_get_table(sock_net(skb->sk), filter.table_id); 260662306a36Sopenharmony_ci if (!mrt) { 260762306a36Sopenharmony_ci if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IPMR) 260862306a36Sopenharmony_ci return skb->len; 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci NL_SET_ERR_MSG(cb->extack, "ipv4: MR table does not exist"); 261162306a36Sopenharmony_ci return -ENOENT; 261262306a36Sopenharmony_ci } 261362306a36Sopenharmony_ci err = mr_table_dump(mrt, skb, cb, _ipmr_fill_mroute, 261462306a36Sopenharmony_ci &mfc_unres_lock, &filter); 261562306a36Sopenharmony_ci return skb->len ? : err; 261662306a36Sopenharmony_ci } 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci return mr_rtm_dumproute(skb, cb, ipmr_mr_table_iter, 261962306a36Sopenharmony_ci _ipmr_fill_mroute, &mfc_unres_lock, &filter); 262062306a36Sopenharmony_ci} 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_cistatic const struct nla_policy rtm_ipmr_policy[RTA_MAX + 1] = { 262362306a36Sopenharmony_ci [RTA_SRC] = { .type = NLA_U32 }, 262462306a36Sopenharmony_ci [RTA_DST] = { .type = NLA_U32 }, 262562306a36Sopenharmony_ci [RTA_IIF] = { .type = NLA_U32 }, 262662306a36Sopenharmony_ci [RTA_TABLE] = { .type = NLA_U32 }, 262762306a36Sopenharmony_ci [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) }, 262862306a36Sopenharmony_ci}; 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_cistatic bool ipmr_rtm_validate_proto(unsigned char rtm_protocol) 263162306a36Sopenharmony_ci{ 263262306a36Sopenharmony_ci switch (rtm_protocol) { 263362306a36Sopenharmony_ci case RTPROT_STATIC: 263462306a36Sopenharmony_ci case RTPROT_MROUTED: 263562306a36Sopenharmony_ci return true; 263662306a36Sopenharmony_ci } 263762306a36Sopenharmony_ci return false; 263862306a36Sopenharmony_ci} 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_cistatic int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl *mfcc) 264162306a36Sopenharmony_ci{ 264262306a36Sopenharmony_ci struct rtnexthop *rtnh = nla_data(nla); 264362306a36Sopenharmony_ci int remaining = nla_len(nla), vifi = 0; 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci while (rtnh_ok(rtnh, remaining)) { 264662306a36Sopenharmony_ci mfcc->mfcc_ttls[vifi] = rtnh->rtnh_hops; 264762306a36Sopenharmony_ci if (++vifi == MAXVIFS) 264862306a36Sopenharmony_ci break; 264962306a36Sopenharmony_ci rtnh = rtnh_next(rtnh, &remaining); 265062306a36Sopenharmony_ci } 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci return remaining > 0 ? -EINVAL : vifi; 265362306a36Sopenharmony_ci} 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci/* returns < 0 on error, 0 for ADD_MFC and 1 for ADD_MFC_PROXY */ 265662306a36Sopenharmony_cistatic int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh, 265762306a36Sopenharmony_ci struct mfcctl *mfcc, int *mrtsock, 265862306a36Sopenharmony_ci struct mr_table **mrtret, 265962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 266062306a36Sopenharmony_ci{ 266162306a36Sopenharmony_ci struct net_device *dev = NULL; 266262306a36Sopenharmony_ci u32 tblid = RT_TABLE_DEFAULT; 266362306a36Sopenharmony_ci struct mr_table *mrt; 266462306a36Sopenharmony_ci struct nlattr *attr; 266562306a36Sopenharmony_ci struct rtmsg *rtm; 266662306a36Sopenharmony_ci int ret, rem; 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_ci ret = nlmsg_validate_deprecated(nlh, sizeof(*rtm), RTA_MAX, 266962306a36Sopenharmony_ci rtm_ipmr_policy, extack); 267062306a36Sopenharmony_ci if (ret < 0) 267162306a36Sopenharmony_ci goto out; 267262306a36Sopenharmony_ci rtm = nlmsg_data(nlh); 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_ci ret = -EINVAL; 267562306a36Sopenharmony_ci if (rtm->rtm_family != RTNL_FAMILY_IPMR || rtm->rtm_dst_len != 32 || 267662306a36Sopenharmony_ci rtm->rtm_type != RTN_MULTICAST || 267762306a36Sopenharmony_ci rtm->rtm_scope != RT_SCOPE_UNIVERSE || 267862306a36Sopenharmony_ci !ipmr_rtm_validate_proto(rtm->rtm_protocol)) 267962306a36Sopenharmony_ci goto out; 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci memset(mfcc, 0, sizeof(*mfcc)); 268262306a36Sopenharmony_ci mfcc->mfcc_parent = -1; 268362306a36Sopenharmony_ci ret = 0; 268462306a36Sopenharmony_ci nlmsg_for_each_attr(attr, nlh, sizeof(struct rtmsg), rem) { 268562306a36Sopenharmony_ci switch (nla_type(attr)) { 268662306a36Sopenharmony_ci case RTA_SRC: 268762306a36Sopenharmony_ci mfcc->mfcc_origin.s_addr = nla_get_be32(attr); 268862306a36Sopenharmony_ci break; 268962306a36Sopenharmony_ci case RTA_DST: 269062306a36Sopenharmony_ci mfcc->mfcc_mcastgrp.s_addr = nla_get_be32(attr); 269162306a36Sopenharmony_ci break; 269262306a36Sopenharmony_ci case RTA_IIF: 269362306a36Sopenharmony_ci dev = __dev_get_by_index(net, nla_get_u32(attr)); 269462306a36Sopenharmony_ci if (!dev) { 269562306a36Sopenharmony_ci ret = -ENODEV; 269662306a36Sopenharmony_ci goto out; 269762306a36Sopenharmony_ci } 269862306a36Sopenharmony_ci break; 269962306a36Sopenharmony_ci case RTA_MULTIPATH: 270062306a36Sopenharmony_ci if (ipmr_nla_get_ttls(attr, mfcc) < 0) { 270162306a36Sopenharmony_ci ret = -EINVAL; 270262306a36Sopenharmony_ci goto out; 270362306a36Sopenharmony_ci } 270462306a36Sopenharmony_ci break; 270562306a36Sopenharmony_ci case RTA_PREFSRC: 270662306a36Sopenharmony_ci ret = 1; 270762306a36Sopenharmony_ci break; 270862306a36Sopenharmony_ci case RTA_TABLE: 270962306a36Sopenharmony_ci tblid = nla_get_u32(attr); 271062306a36Sopenharmony_ci break; 271162306a36Sopenharmony_ci } 271262306a36Sopenharmony_ci } 271362306a36Sopenharmony_ci mrt = ipmr_get_table(net, tblid); 271462306a36Sopenharmony_ci if (!mrt) { 271562306a36Sopenharmony_ci ret = -ENOENT; 271662306a36Sopenharmony_ci goto out; 271762306a36Sopenharmony_ci } 271862306a36Sopenharmony_ci *mrtret = mrt; 271962306a36Sopenharmony_ci *mrtsock = rtm->rtm_protocol == RTPROT_MROUTED ? 1 : 0; 272062306a36Sopenharmony_ci if (dev) 272162306a36Sopenharmony_ci mfcc->mfcc_parent = ipmr_find_vif(mrt, dev); 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_ciout: 272462306a36Sopenharmony_ci return ret; 272562306a36Sopenharmony_ci} 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci/* takes care of both newroute and delroute */ 272862306a36Sopenharmony_cistatic int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh, 272962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 273062306a36Sopenharmony_ci{ 273162306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 273262306a36Sopenharmony_ci int ret, mrtsock, parent; 273362306a36Sopenharmony_ci struct mr_table *tbl; 273462306a36Sopenharmony_ci struct mfcctl mfcc; 273562306a36Sopenharmony_ci 273662306a36Sopenharmony_ci mrtsock = 0; 273762306a36Sopenharmony_ci tbl = NULL; 273862306a36Sopenharmony_ci ret = rtm_to_ipmr_mfcc(net, nlh, &mfcc, &mrtsock, &tbl, extack); 273962306a36Sopenharmony_ci if (ret < 0) 274062306a36Sopenharmony_ci return ret; 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci parent = ret ? mfcc.mfcc_parent : -1; 274362306a36Sopenharmony_ci if (nlh->nlmsg_type == RTM_NEWROUTE) 274462306a36Sopenharmony_ci return ipmr_mfc_add(net, tbl, &mfcc, mrtsock, parent); 274562306a36Sopenharmony_ci else 274662306a36Sopenharmony_ci return ipmr_mfc_delete(tbl, &mfcc, parent); 274762306a36Sopenharmony_ci} 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_cistatic bool ipmr_fill_table(struct mr_table *mrt, struct sk_buff *skb) 275062306a36Sopenharmony_ci{ 275162306a36Sopenharmony_ci u32 queue_len = atomic_read(&mrt->cache_resolve_queue_len); 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci if (nla_put_u32(skb, IPMRA_TABLE_ID, mrt->id) || 275462306a36Sopenharmony_ci nla_put_u32(skb, IPMRA_TABLE_CACHE_RES_QUEUE_LEN, queue_len) || 275562306a36Sopenharmony_ci nla_put_s32(skb, IPMRA_TABLE_MROUTE_REG_VIF_NUM, 275662306a36Sopenharmony_ci mrt->mroute_reg_vif_num) || 275762306a36Sopenharmony_ci nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_ASSERT, 275862306a36Sopenharmony_ci mrt->mroute_do_assert) || 275962306a36Sopenharmony_ci nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_PIM, mrt->mroute_do_pim) || 276062306a36Sopenharmony_ci nla_put_u8(skb, IPMRA_TABLE_MROUTE_DO_WRVIFWHOLE, 276162306a36Sopenharmony_ci mrt->mroute_do_wrvifwhole)) 276262306a36Sopenharmony_ci return false; 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci return true; 276562306a36Sopenharmony_ci} 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_cistatic bool ipmr_fill_vif(struct mr_table *mrt, u32 vifid, struct sk_buff *skb) 276862306a36Sopenharmony_ci{ 276962306a36Sopenharmony_ci struct net_device *vif_dev; 277062306a36Sopenharmony_ci struct nlattr *vif_nest; 277162306a36Sopenharmony_ci struct vif_device *vif; 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci vif = &mrt->vif_table[vifid]; 277462306a36Sopenharmony_ci vif_dev = rtnl_dereference(vif->dev); 277562306a36Sopenharmony_ci /* if the VIF doesn't exist just continue */ 277662306a36Sopenharmony_ci if (!vif_dev) 277762306a36Sopenharmony_ci return true; 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_ci vif_nest = nla_nest_start_noflag(skb, IPMRA_VIF); 278062306a36Sopenharmony_ci if (!vif_nest) 278162306a36Sopenharmony_ci return false; 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci if (nla_put_u32(skb, IPMRA_VIFA_IFINDEX, vif_dev->ifindex) || 278462306a36Sopenharmony_ci nla_put_u32(skb, IPMRA_VIFA_VIF_ID, vifid) || 278562306a36Sopenharmony_ci nla_put_u16(skb, IPMRA_VIFA_FLAGS, vif->flags) || 278662306a36Sopenharmony_ci nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_IN, vif->bytes_in, 278762306a36Sopenharmony_ci IPMRA_VIFA_PAD) || 278862306a36Sopenharmony_ci nla_put_u64_64bit(skb, IPMRA_VIFA_BYTES_OUT, vif->bytes_out, 278962306a36Sopenharmony_ci IPMRA_VIFA_PAD) || 279062306a36Sopenharmony_ci nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_IN, vif->pkt_in, 279162306a36Sopenharmony_ci IPMRA_VIFA_PAD) || 279262306a36Sopenharmony_ci nla_put_u64_64bit(skb, IPMRA_VIFA_PACKETS_OUT, vif->pkt_out, 279362306a36Sopenharmony_ci IPMRA_VIFA_PAD) || 279462306a36Sopenharmony_ci nla_put_be32(skb, IPMRA_VIFA_LOCAL_ADDR, vif->local) || 279562306a36Sopenharmony_ci nla_put_be32(skb, IPMRA_VIFA_REMOTE_ADDR, vif->remote)) { 279662306a36Sopenharmony_ci nla_nest_cancel(skb, vif_nest); 279762306a36Sopenharmony_ci return false; 279862306a36Sopenharmony_ci } 279962306a36Sopenharmony_ci nla_nest_end(skb, vif_nest); 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci return true; 280262306a36Sopenharmony_ci} 280362306a36Sopenharmony_ci 280462306a36Sopenharmony_cistatic int ipmr_valid_dumplink(const struct nlmsghdr *nlh, 280562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 280662306a36Sopenharmony_ci{ 280762306a36Sopenharmony_ci struct ifinfomsg *ifm; 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 281062306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "ipv4: Invalid header for ipmr link dump"); 281162306a36Sopenharmony_ci return -EINVAL; 281262306a36Sopenharmony_ci } 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_ci if (nlmsg_attrlen(nlh, sizeof(*ifm))) { 281562306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid data after header in ipmr link dump"); 281662306a36Sopenharmony_ci return -EINVAL; 281762306a36Sopenharmony_ci } 281862306a36Sopenharmony_ci 281962306a36Sopenharmony_ci ifm = nlmsg_data(nlh); 282062306a36Sopenharmony_ci if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags || 282162306a36Sopenharmony_ci ifm->ifi_change || ifm->ifi_index) { 282262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid values in header for ipmr link dump request"); 282362306a36Sopenharmony_ci return -EINVAL; 282462306a36Sopenharmony_ci } 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci return 0; 282762306a36Sopenharmony_ci} 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_cistatic int ipmr_rtm_dumplink(struct sk_buff *skb, struct netlink_callback *cb) 283062306a36Sopenharmony_ci{ 283162306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 283262306a36Sopenharmony_ci struct nlmsghdr *nlh = NULL; 283362306a36Sopenharmony_ci unsigned int t = 0, s_t; 283462306a36Sopenharmony_ci unsigned int e = 0, s_e; 283562306a36Sopenharmony_ci struct mr_table *mrt; 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci if (cb->strict_check) { 283862306a36Sopenharmony_ci int err = ipmr_valid_dumplink(cb->nlh, cb->extack); 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci if (err < 0) 284162306a36Sopenharmony_ci return err; 284262306a36Sopenharmony_ci } 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci s_t = cb->args[0]; 284562306a36Sopenharmony_ci s_e = cb->args[1]; 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_ci ipmr_for_each_table(mrt, net) { 284862306a36Sopenharmony_ci struct nlattr *vifs, *af; 284962306a36Sopenharmony_ci struct ifinfomsg *hdr; 285062306a36Sopenharmony_ci u32 i; 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci if (t < s_t) 285362306a36Sopenharmony_ci goto skip_table; 285462306a36Sopenharmony_ci nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, 285562306a36Sopenharmony_ci cb->nlh->nlmsg_seq, RTM_NEWLINK, 285662306a36Sopenharmony_ci sizeof(*hdr), NLM_F_MULTI); 285762306a36Sopenharmony_ci if (!nlh) 285862306a36Sopenharmony_ci break; 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci hdr = nlmsg_data(nlh); 286162306a36Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 286262306a36Sopenharmony_ci hdr->ifi_family = RTNL_FAMILY_IPMR; 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci af = nla_nest_start_noflag(skb, IFLA_AF_SPEC); 286562306a36Sopenharmony_ci if (!af) { 286662306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 286762306a36Sopenharmony_ci goto out; 286862306a36Sopenharmony_ci } 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_ci if (!ipmr_fill_table(mrt, skb)) { 287162306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 287262306a36Sopenharmony_ci goto out; 287362306a36Sopenharmony_ci } 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci vifs = nla_nest_start_noflag(skb, IPMRA_TABLE_VIFS); 287662306a36Sopenharmony_ci if (!vifs) { 287762306a36Sopenharmony_ci nla_nest_end(skb, af); 287862306a36Sopenharmony_ci nlmsg_end(skb, nlh); 287962306a36Sopenharmony_ci goto out; 288062306a36Sopenharmony_ci } 288162306a36Sopenharmony_ci for (i = 0; i < mrt->maxvif; i++) { 288262306a36Sopenharmony_ci if (e < s_e) 288362306a36Sopenharmony_ci goto skip_entry; 288462306a36Sopenharmony_ci if (!ipmr_fill_vif(mrt, i, skb)) { 288562306a36Sopenharmony_ci nla_nest_end(skb, vifs); 288662306a36Sopenharmony_ci nla_nest_end(skb, af); 288762306a36Sopenharmony_ci nlmsg_end(skb, nlh); 288862306a36Sopenharmony_ci goto out; 288962306a36Sopenharmony_ci } 289062306a36Sopenharmony_ciskip_entry: 289162306a36Sopenharmony_ci e++; 289262306a36Sopenharmony_ci } 289362306a36Sopenharmony_ci s_e = 0; 289462306a36Sopenharmony_ci e = 0; 289562306a36Sopenharmony_ci nla_nest_end(skb, vifs); 289662306a36Sopenharmony_ci nla_nest_end(skb, af); 289762306a36Sopenharmony_ci nlmsg_end(skb, nlh); 289862306a36Sopenharmony_ciskip_table: 289962306a36Sopenharmony_ci t++; 290062306a36Sopenharmony_ci } 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_ciout: 290362306a36Sopenharmony_ci cb->args[1] = e; 290462306a36Sopenharmony_ci cb->args[0] = t; 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_ci return skb->len; 290762306a36Sopenharmony_ci} 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 291062306a36Sopenharmony_ci/* The /proc interfaces to multicast routing : 291162306a36Sopenharmony_ci * /proc/net/ip_mr_cache & /proc/net/ip_mr_vif 291262306a36Sopenharmony_ci */ 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_cistatic void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos) 291562306a36Sopenharmony_ci __acquires(RCU) 291662306a36Sopenharmony_ci{ 291762306a36Sopenharmony_ci struct mr_vif_iter *iter = seq->private; 291862306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 291962306a36Sopenharmony_ci struct mr_table *mrt; 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 292262306a36Sopenharmony_ci if (!mrt) 292362306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 292462306a36Sopenharmony_ci 292562306a36Sopenharmony_ci iter->mrt = mrt; 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci rcu_read_lock(); 292862306a36Sopenharmony_ci return mr_vif_seq_start(seq, pos); 292962306a36Sopenharmony_ci} 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_cistatic void ipmr_vif_seq_stop(struct seq_file *seq, void *v) 293262306a36Sopenharmony_ci __releases(RCU) 293362306a36Sopenharmony_ci{ 293462306a36Sopenharmony_ci rcu_read_unlock(); 293562306a36Sopenharmony_ci} 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_cistatic int ipmr_vif_seq_show(struct seq_file *seq, void *v) 293862306a36Sopenharmony_ci{ 293962306a36Sopenharmony_ci struct mr_vif_iter *iter = seq->private; 294062306a36Sopenharmony_ci struct mr_table *mrt = iter->mrt; 294162306a36Sopenharmony_ci 294262306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) { 294362306a36Sopenharmony_ci seq_puts(seq, 294462306a36Sopenharmony_ci "Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n"); 294562306a36Sopenharmony_ci } else { 294662306a36Sopenharmony_ci const struct vif_device *vif = v; 294762306a36Sopenharmony_ci const struct net_device *vif_dev; 294862306a36Sopenharmony_ci const char *name; 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_ci vif_dev = vif_dev_read(vif); 295162306a36Sopenharmony_ci name = vif_dev ? vif_dev->name : "none"; 295262306a36Sopenharmony_ci seq_printf(seq, 295362306a36Sopenharmony_ci "%2td %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n", 295462306a36Sopenharmony_ci vif - mrt->vif_table, 295562306a36Sopenharmony_ci name, vif->bytes_in, vif->pkt_in, 295662306a36Sopenharmony_ci vif->bytes_out, vif->pkt_out, 295762306a36Sopenharmony_ci vif->flags, vif->local, vif->remote); 295862306a36Sopenharmony_ci } 295962306a36Sopenharmony_ci return 0; 296062306a36Sopenharmony_ci} 296162306a36Sopenharmony_ci 296262306a36Sopenharmony_cistatic const struct seq_operations ipmr_vif_seq_ops = { 296362306a36Sopenharmony_ci .start = ipmr_vif_seq_start, 296462306a36Sopenharmony_ci .next = mr_vif_seq_next, 296562306a36Sopenharmony_ci .stop = ipmr_vif_seq_stop, 296662306a36Sopenharmony_ci .show = ipmr_vif_seq_show, 296762306a36Sopenharmony_ci}; 296862306a36Sopenharmony_ci 296962306a36Sopenharmony_cistatic void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos) 297062306a36Sopenharmony_ci{ 297162306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 297262306a36Sopenharmony_ci struct mr_table *mrt; 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci mrt = ipmr_get_table(net, RT_TABLE_DEFAULT); 297562306a36Sopenharmony_ci if (!mrt) 297662306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 297762306a36Sopenharmony_ci 297862306a36Sopenharmony_ci return mr_mfc_seq_start(seq, pos, mrt, &mfc_unres_lock); 297962306a36Sopenharmony_ci} 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_cistatic int ipmr_mfc_seq_show(struct seq_file *seq, void *v) 298262306a36Sopenharmony_ci{ 298362306a36Sopenharmony_ci int n; 298462306a36Sopenharmony_ci 298562306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) { 298662306a36Sopenharmony_ci seq_puts(seq, 298762306a36Sopenharmony_ci "Group Origin Iif Pkts Bytes Wrong Oifs\n"); 298862306a36Sopenharmony_ci } else { 298962306a36Sopenharmony_ci const struct mfc_cache *mfc = v; 299062306a36Sopenharmony_ci const struct mr_mfc_iter *it = seq->private; 299162306a36Sopenharmony_ci const struct mr_table *mrt = it->mrt; 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci seq_printf(seq, "%08X %08X %-3hd", 299462306a36Sopenharmony_ci (__force u32) mfc->mfc_mcastgrp, 299562306a36Sopenharmony_ci (__force u32) mfc->mfc_origin, 299662306a36Sopenharmony_ci mfc->_c.mfc_parent); 299762306a36Sopenharmony_ci 299862306a36Sopenharmony_ci if (it->cache != &mrt->mfc_unres_queue) { 299962306a36Sopenharmony_ci seq_printf(seq, " %8lu %8lu %8lu", 300062306a36Sopenharmony_ci mfc->_c.mfc_un.res.pkt, 300162306a36Sopenharmony_ci mfc->_c.mfc_un.res.bytes, 300262306a36Sopenharmony_ci mfc->_c.mfc_un.res.wrong_if); 300362306a36Sopenharmony_ci for (n = mfc->_c.mfc_un.res.minvif; 300462306a36Sopenharmony_ci n < mfc->_c.mfc_un.res.maxvif; n++) { 300562306a36Sopenharmony_ci if (VIF_EXISTS(mrt, n) && 300662306a36Sopenharmony_ci mfc->_c.mfc_un.res.ttls[n] < 255) 300762306a36Sopenharmony_ci seq_printf(seq, 300862306a36Sopenharmony_ci " %2d:%-3d", 300962306a36Sopenharmony_ci n, mfc->_c.mfc_un.res.ttls[n]); 301062306a36Sopenharmony_ci } 301162306a36Sopenharmony_ci } else { 301262306a36Sopenharmony_ci /* unresolved mfc_caches don't contain 301362306a36Sopenharmony_ci * pkt, bytes and wrong_if values 301462306a36Sopenharmony_ci */ 301562306a36Sopenharmony_ci seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul); 301662306a36Sopenharmony_ci } 301762306a36Sopenharmony_ci seq_putc(seq, '\n'); 301862306a36Sopenharmony_ci } 301962306a36Sopenharmony_ci return 0; 302062306a36Sopenharmony_ci} 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_cistatic const struct seq_operations ipmr_mfc_seq_ops = { 302362306a36Sopenharmony_ci .start = ipmr_mfc_seq_start, 302462306a36Sopenharmony_ci .next = mr_mfc_seq_next, 302562306a36Sopenharmony_ci .stop = mr_mfc_seq_stop, 302662306a36Sopenharmony_ci .show = ipmr_mfc_seq_show, 302762306a36Sopenharmony_ci}; 302862306a36Sopenharmony_ci#endif 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci#ifdef CONFIG_IP_PIMSM_V2 303162306a36Sopenharmony_cistatic const struct net_protocol pim_protocol = { 303262306a36Sopenharmony_ci .handler = pim_rcv, 303362306a36Sopenharmony_ci}; 303462306a36Sopenharmony_ci#endif 303562306a36Sopenharmony_ci 303662306a36Sopenharmony_cistatic unsigned int ipmr_seq_read(struct net *net) 303762306a36Sopenharmony_ci{ 303862306a36Sopenharmony_ci ASSERT_RTNL(); 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_ci return net->ipv4.ipmr_seq + ipmr_rules_seq_read(net); 304162306a36Sopenharmony_ci} 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_cistatic int ipmr_dump(struct net *net, struct notifier_block *nb, 304462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 304562306a36Sopenharmony_ci{ 304662306a36Sopenharmony_ci return mr_dump(net, nb, RTNL_FAMILY_IPMR, ipmr_rules_dump, 304762306a36Sopenharmony_ci ipmr_mr_table_iter, extack); 304862306a36Sopenharmony_ci} 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_cistatic const struct fib_notifier_ops ipmr_notifier_ops_template = { 305162306a36Sopenharmony_ci .family = RTNL_FAMILY_IPMR, 305262306a36Sopenharmony_ci .fib_seq_read = ipmr_seq_read, 305362306a36Sopenharmony_ci .fib_dump = ipmr_dump, 305462306a36Sopenharmony_ci .owner = THIS_MODULE, 305562306a36Sopenharmony_ci}; 305662306a36Sopenharmony_ci 305762306a36Sopenharmony_cistatic int __net_init ipmr_notifier_init(struct net *net) 305862306a36Sopenharmony_ci{ 305962306a36Sopenharmony_ci struct fib_notifier_ops *ops; 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ci net->ipv4.ipmr_seq = 0; 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci ops = fib_notifier_ops_register(&ipmr_notifier_ops_template, net); 306462306a36Sopenharmony_ci if (IS_ERR(ops)) 306562306a36Sopenharmony_ci return PTR_ERR(ops); 306662306a36Sopenharmony_ci net->ipv4.ipmr_notifier_ops = ops; 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_ci return 0; 306962306a36Sopenharmony_ci} 307062306a36Sopenharmony_ci 307162306a36Sopenharmony_cistatic void __net_exit ipmr_notifier_exit(struct net *net) 307262306a36Sopenharmony_ci{ 307362306a36Sopenharmony_ci fib_notifier_ops_unregister(net->ipv4.ipmr_notifier_ops); 307462306a36Sopenharmony_ci net->ipv4.ipmr_notifier_ops = NULL; 307562306a36Sopenharmony_ci} 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci/* Setup for IP multicast routing */ 307862306a36Sopenharmony_cistatic int __net_init ipmr_net_init(struct net *net) 307962306a36Sopenharmony_ci{ 308062306a36Sopenharmony_ci int err; 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci err = ipmr_notifier_init(net); 308362306a36Sopenharmony_ci if (err) 308462306a36Sopenharmony_ci goto ipmr_notifier_fail; 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci err = ipmr_rules_init(net); 308762306a36Sopenharmony_ci if (err < 0) 308862306a36Sopenharmony_ci goto ipmr_rules_fail; 308962306a36Sopenharmony_ci 309062306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 309162306a36Sopenharmony_ci err = -ENOMEM; 309262306a36Sopenharmony_ci if (!proc_create_net("ip_mr_vif", 0, net->proc_net, &ipmr_vif_seq_ops, 309362306a36Sopenharmony_ci sizeof(struct mr_vif_iter))) 309462306a36Sopenharmony_ci goto proc_vif_fail; 309562306a36Sopenharmony_ci if (!proc_create_net("ip_mr_cache", 0, net->proc_net, &ipmr_mfc_seq_ops, 309662306a36Sopenharmony_ci sizeof(struct mr_mfc_iter))) 309762306a36Sopenharmony_ci goto proc_cache_fail; 309862306a36Sopenharmony_ci#endif 309962306a36Sopenharmony_ci return 0; 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 310262306a36Sopenharmony_ciproc_cache_fail: 310362306a36Sopenharmony_ci remove_proc_entry("ip_mr_vif", net->proc_net); 310462306a36Sopenharmony_ciproc_vif_fail: 310562306a36Sopenharmony_ci rtnl_lock(); 310662306a36Sopenharmony_ci ipmr_rules_exit(net); 310762306a36Sopenharmony_ci rtnl_unlock(); 310862306a36Sopenharmony_ci#endif 310962306a36Sopenharmony_ciipmr_rules_fail: 311062306a36Sopenharmony_ci ipmr_notifier_exit(net); 311162306a36Sopenharmony_ciipmr_notifier_fail: 311262306a36Sopenharmony_ci return err; 311362306a36Sopenharmony_ci} 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_cistatic void __net_exit ipmr_net_exit(struct net *net) 311662306a36Sopenharmony_ci{ 311762306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 311862306a36Sopenharmony_ci remove_proc_entry("ip_mr_cache", net->proc_net); 311962306a36Sopenharmony_ci remove_proc_entry("ip_mr_vif", net->proc_net); 312062306a36Sopenharmony_ci#endif 312162306a36Sopenharmony_ci ipmr_notifier_exit(net); 312262306a36Sopenharmony_ci} 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_cistatic void __net_exit ipmr_net_exit_batch(struct list_head *net_list) 312562306a36Sopenharmony_ci{ 312662306a36Sopenharmony_ci struct net *net; 312762306a36Sopenharmony_ci 312862306a36Sopenharmony_ci rtnl_lock(); 312962306a36Sopenharmony_ci list_for_each_entry(net, net_list, exit_list) 313062306a36Sopenharmony_ci ipmr_rules_exit(net); 313162306a36Sopenharmony_ci rtnl_unlock(); 313262306a36Sopenharmony_ci} 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_cistatic struct pernet_operations ipmr_net_ops = { 313562306a36Sopenharmony_ci .init = ipmr_net_init, 313662306a36Sopenharmony_ci .exit = ipmr_net_exit, 313762306a36Sopenharmony_ci .exit_batch = ipmr_net_exit_batch, 313862306a36Sopenharmony_ci}; 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_ciint __init ip_mr_init(void) 314162306a36Sopenharmony_ci{ 314262306a36Sopenharmony_ci int err; 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci mrt_cachep = kmem_cache_create("ip_mrt_cache", 314562306a36Sopenharmony_ci sizeof(struct mfc_cache), 314662306a36Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, 314762306a36Sopenharmony_ci NULL); 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_ci err = register_pernet_subsys(&ipmr_net_ops); 315062306a36Sopenharmony_ci if (err) 315162306a36Sopenharmony_ci goto reg_pernet_fail; 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ci err = register_netdevice_notifier(&ip_mr_notifier); 315462306a36Sopenharmony_ci if (err) 315562306a36Sopenharmony_ci goto reg_notif_fail; 315662306a36Sopenharmony_ci#ifdef CONFIG_IP_PIMSM_V2 315762306a36Sopenharmony_ci if (inet_add_protocol(&pim_protocol, IPPROTO_PIM) < 0) { 315862306a36Sopenharmony_ci pr_err("%s: can't add PIM protocol\n", __func__); 315962306a36Sopenharmony_ci err = -EAGAIN; 316062306a36Sopenharmony_ci goto add_proto_fail; 316162306a36Sopenharmony_ci } 316262306a36Sopenharmony_ci#endif 316362306a36Sopenharmony_ci rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, 316462306a36Sopenharmony_ci ipmr_rtm_getroute, ipmr_rtm_dumproute, 0); 316562306a36Sopenharmony_ci rtnl_register(RTNL_FAMILY_IPMR, RTM_NEWROUTE, 316662306a36Sopenharmony_ci ipmr_rtm_route, NULL, 0); 316762306a36Sopenharmony_ci rtnl_register(RTNL_FAMILY_IPMR, RTM_DELROUTE, 316862306a36Sopenharmony_ci ipmr_rtm_route, NULL, 0); 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci rtnl_register(RTNL_FAMILY_IPMR, RTM_GETLINK, 317162306a36Sopenharmony_ci NULL, ipmr_rtm_dumplink, 0); 317262306a36Sopenharmony_ci return 0; 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci#ifdef CONFIG_IP_PIMSM_V2 317562306a36Sopenharmony_ciadd_proto_fail: 317662306a36Sopenharmony_ci unregister_netdevice_notifier(&ip_mr_notifier); 317762306a36Sopenharmony_ci#endif 317862306a36Sopenharmony_cireg_notif_fail: 317962306a36Sopenharmony_ci unregister_pernet_subsys(&ipmr_net_ops); 318062306a36Sopenharmony_cireg_pernet_fail: 318162306a36Sopenharmony_ci kmem_cache_destroy(mrt_cachep); 318262306a36Sopenharmony_ci return err; 318362306a36Sopenharmony_ci} 3184