18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux IPv6 multicast routing support for BSD pim6sd 48c2ecf20Sopenharmony_ci * Based on net/ipv4/ipmr.c. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * (c) 2004 Mickael Hoerdt, <hoerdt@clarinet.u-strasbg.fr> 78c2ecf20Sopenharmony_ci * LSIIT Laboratory, Strasbourg, France 88c2ecf20Sopenharmony_ci * (c) 2004 Jean-Philippe Andriot, <jean-philippe.andriot@6WIND.com> 98c2ecf20Sopenharmony_ci * 6WIND, Paris, France 108c2ecf20Sopenharmony_ci * Copyright (C)2007,2008 USAGI/WIDE Project 118c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci#include <linux/sched.h> 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/mm.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 218c2ecf20Sopenharmony_ci#include <linux/stat.h> 228c2ecf20Sopenharmony_ci#include <linux/socket.h> 238c2ecf20Sopenharmony_ci#include <linux/inet.h> 248c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 258c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 268c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 278c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 288c2ecf20Sopenharmony_ci#include <linux/init.h> 298c2ecf20Sopenharmony_ci#include <linux/compat.h> 308c2ecf20Sopenharmony_ci#include <linux/rhashtable.h> 318c2ecf20Sopenharmony_ci#include <net/protocol.h> 328c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 338c2ecf20Sopenharmony_ci#include <net/raw.h> 348c2ecf20Sopenharmony_ci#include <linux/notifier.h> 358c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 368c2ecf20Sopenharmony_ci#include <net/checksum.h> 378c2ecf20Sopenharmony_ci#include <net/netlink.h> 388c2ecf20Sopenharmony_ci#include <net/fib_rules.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include <net/ipv6.h> 418c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 428c2ecf20Sopenharmony_ci#include <linux/mroute6.h> 438c2ecf20Sopenharmony_ci#include <linux/pim.h> 448c2ecf20Sopenharmony_ci#include <net/addrconf.h> 458c2ecf20Sopenharmony_ci#include <linux/netfilter_ipv6.h> 468c2ecf20Sopenharmony_ci#include <linux/export.h> 478c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 488c2ecf20Sopenharmony_ci#include <linux/netconf.h> 498c2ecf20Sopenharmony_ci#include <net/ip_tunnels.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include <linux/nospec.h> 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct ip6mr_rule { 548c2ecf20Sopenharmony_ci struct fib_rule common; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct ip6mr_result { 588c2ecf20Sopenharmony_ci struct mr_table *mrt; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Big lock, protecting vif table, mrt cache and mroute socket state. 628c2ecf20Sopenharmony_ci Note that the changes are semaphored via rtnl_lock. 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(mrt_lock); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* Multicast router control variables */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Special spinlock for queue of unresolved entries */ 708c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(mfc_unres_lock); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* We return to original Alan's scheme. Hash table of resolved 738c2ecf20Sopenharmony_ci entries is changed only in process context and protected 748c2ecf20Sopenharmony_ci with weak lock mrt_lock. Queue of unresolved entries is protected 758c2ecf20Sopenharmony_ci with strong spinlock mfc_unres_lock. 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci In this case data path is free of exclusive locks at all. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic struct kmem_cache *mrt_cachep __read_mostly; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic struct mr_table *ip6mr_new_table(struct net *net, u32 id); 838c2ecf20Sopenharmony_cistatic void ip6mr_free_table(struct mr_table *mrt); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void ip6_mr_forward(struct net *net, struct mr_table *mrt, 868c2ecf20Sopenharmony_ci struct net_device *dev, struct sk_buff *skb, 878c2ecf20Sopenharmony_ci struct mfc6_cache *cache); 888c2ecf20Sopenharmony_cistatic int ip6mr_cache_report(struct mr_table *mrt, struct sk_buff *pkt, 898c2ecf20Sopenharmony_ci mifi_t mifi, int assert); 908c2ecf20Sopenharmony_cistatic void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc, 918c2ecf20Sopenharmony_ci int cmd); 928c2ecf20Sopenharmony_cistatic void mrt6msg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt); 938c2ecf20Sopenharmony_cistatic int ip6mr_rtm_dumproute(struct sk_buff *skb, 948c2ecf20Sopenharmony_ci struct netlink_callback *cb); 958c2ecf20Sopenharmony_cistatic void mroute_clean_tables(struct mr_table *mrt, int flags); 968c2ecf20Sopenharmony_cistatic void ipmr_expire_process(struct timer_list *t); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES 998c2ecf20Sopenharmony_ci#define ip6mr_for_each_table(mrt, net) \ 1008c2ecf20Sopenharmony_ci list_for_each_entry_rcu(mrt, &net->ipv6.mr6_tables, list, \ 1018c2ecf20Sopenharmony_ci lockdep_rtnl_is_held() || \ 1028c2ecf20Sopenharmony_ci list_empty(&net->ipv6.mr6_tables)) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic struct mr_table *ip6mr_mr_table_iter(struct net *net, 1058c2ecf20Sopenharmony_ci struct mr_table *mrt) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci struct mr_table *ret; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (!mrt) 1108c2ecf20Sopenharmony_ci ret = list_entry_rcu(net->ipv6.mr6_tables.next, 1118c2ecf20Sopenharmony_ci struct mr_table, list); 1128c2ecf20Sopenharmony_ci else 1138c2ecf20Sopenharmony_ci ret = list_entry_rcu(mrt->list.next, 1148c2ecf20Sopenharmony_ci struct mr_table, list); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (&ret->list == &net->ipv6.mr6_tables) 1178c2ecf20Sopenharmony_ci return NULL; 1188c2ecf20Sopenharmony_ci return ret; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic struct mr_table *ip6mr_get_table(struct net *net, u32 id) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct mr_table *mrt; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci ip6mr_for_each_table(mrt, net) { 1268c2ecf20Sopenharmony_ci if (mrt->id == id) 1278c2ecf20Sopenharmony_ci return mrt; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci return NULL; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6, 1338c2ecf20Sopenharmony_ci struct mr_table **mrt) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int err; 1368c2ecf20Sopenharmony_ci struct ip6mr_result res; 1378c2ecf20Sopenharmony_ci struct fib_lookup_arg arg = { 1388c2ecf20Sopenharmony_ci .result = &res, 1398c2ecf20Sopenharmony_ci .flags = FIB_LOOKUP_NOREF, 1408c2ecf20Sopenharmony_ci }; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* update flow if oif or iif point to device enslaved to l3mdev */ 1438c2ecf20Sopenharmony_ci l3mdev_update_flow(net, flowi6_to_flowi(flp6)); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci err = fib_rules_lookup(net->ipv6.mr6_rules_ops, 1468c2ecf20Sopenharmony_ci flowi6_to_flowi(flp6), 0, &arg); 1478c2ecf20Sopenharmony_ci if (err < 0) 1488c2ecf20Sopenharmony_ci return err; 1498c2ecf20Sopenharmony_ci *mrt = res.mrt; 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int ip6mr_rule_action(struct fib_rule *rule, struct flowi *flp, 1548c2ecf20Sopenharmony_ci int flags, struct fib_lookup_arg *arg) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct ip6mr_result *res = arg->result; 1578c2ecf20Sopenharmony_ci struct mr_table *mrt; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci switch (rule->action) { 1608c2ecf20Sopenharmony_ci case FR_ACT_TO_TBL: 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci case FR_ACT_UNREACHABLE: 1638c2ecf20Sopenharmony_ci return -ENETUNREACH; 1648c2ecf20Sopenharmony_ci case FR_ACT_PROHIBIT: 1658c2ecf20Sopenharmony_ci return -EACCES; 1668c2ecf20Sopenharmony_ci case FR_ACT_BLACKHOLE: 1678c2ecf20Sopenharmony_ci default: 1688c2ecf20Sopenharmony_ci return -EINVAL; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci arg->table = fib_rule_get_table(rule, arg); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci mrt = ip6mr_get_table(rule->fr_net, arg->table); 1748c2ecf20Sopenharmony_ci if (!mrt) 1758c2ecf20Sopenharmony_ci return -EAGAIN; 1768c2ecf20Sopenharmony_ci res->mrt = mrt; 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int ip6mr_rule_match(struct fib_rule *rule, struct flowi *flp, int flags) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci return 1; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic const struct nla_policy ip6mr_rule_policy[FRA_MAX + 1] = { 1868c2ecf20Sopenharmony_ci FRA_GENERIC_POLICY, 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int ip6mr_rule_configure(struct fib_rule *rule, struct sk_buff *skb, 1908c2ecf20Sopenharmony_ci struct fib_rule_hdr *frh, struct nlattr **tb, 1918c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int ip6mr_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh, 1978c2ecf20Sopenharmony_ci struct nlattr **tb) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci return 1; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int ip6mr_rule_fill(struct fib_rule *rule, struct sk_buff *skb, 2038c2ecf20Sopenharmony_ci struct fib_rule_hdr *frh) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci frh->dst_len = 0; 2068c2ecf20Sopenharmony_ci frh->src_len = 0; 2078c2ecf20Sopenharmony_ci frh->tos = 0; 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic const struct fib_rules_ops __net_initconst ip6mr_rules_ops_template = { 2128c2ecf20Sopenharmony_ci .family = RTNL_FAMILY_IP6MR, 2138c2ecf20Sopenharmony_ci .rule_size = sizeof(struct ip6mr_rule), 2148c2ecf20Sopenharmony_ci .addr_size = sizeof(struct in6_addr), 2158c2ecf20Sopenharmony_ci .action = ip6mr_rule_action, 2168c2ecf20Sopenharmony_ci .match = ip6mr_rule_match, 2178c2ecf20Sopenharmony_ci .configure = ip6mr_rule_configure, 2188c2ecf20Sopenharmony_ci .compare = ip6mr_rule_compare, 2198c2ecf20Sopenharmony_ci .fill = ip6mr_rule_fill, 2208c2ecf20Sopenharmony_ci .nlgroup = RTNLGRP_IPV6_RULE, 2218c2ecf20Sopenharmony_ci .policy = ip6mr_rule_policy, 2228c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int __net_init ip6mr_rules_init(struct net *net) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct fib_rules_ops *ops; 2288c2ecf20Sopenharmony_ci struct mr_table *mrt; 2298c2ecf20Sopenharmony_ci int err; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci ops = fib_rules_register(&ip6mr_rules_ops_template, net); 2328c2ecf20Sopenharmony_ci if (IS_ERR(ops)) 2338c2ecf20Sopenharmony_ci return PTR_ERR(ops); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&net->ipv6.mr6_tables); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci mrt = ip6mr_new_table(net, RT6_TABLE_DFLT); 2388c2ecf20Sopenharmony_ci if (IS_ERR(mrt)) { 2398c2ecf20Sopenharmony_ci err = PTR_ERR(mrt); 2408c2ecf20Sopenharmony_ci goto err1; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci err = fib_default_rule_add(ops, 0x7fff, RT6_TABLE_DFLT, 0); 2448c2ecf20Sopenharmony_ci if (err < 0) 2458c2ecf20Sopenharmony_ci goto err2; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci net->ipv6.mr6_rules_ops = ops; 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cierr2: 2518c2ecf20Sopenharmony_ci rtnl_lock(); 2528c2ecf20Sopenharmony_ci ip6mr_free_table(mrt); 2538c2ecf20Sopenharmony_ci rtnl_unlock(); 2548c2ecf20Sopenharmony_cierr1: 2558c2ecf20Sopenharmony_ci fib_rules_unregister(ops); 2568c2ecf20Sopenharmony_ci return err; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic void __net_exit ip6mr_rules_exit(struct net *net) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct mr_table *mrt, *next; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci rtnl_lock(); 2648c2ecf20Sopenharmony_ci list_for_each_entry_safe(mrt, next, &net->ipv6.mr6_tables, list) { 2658c2ecf20Sopenharmony_ci list_del(&mrt->list); 2668c2ecf20Sopenharmony_ci ip6mr_free_table(mrt); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci fib_rules_unregister(net->ipv6.mr6_rules_ops); 2698c2ecf20Sopenharmony_ci rtnl_unlock(); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int ip6mr_rules_dump(struct net *net, struct notifier_block *nb, 2738c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci return fib_rules_dump(net, nb, RTNL_FAMILY_IP6MR, extack); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic unsigned int ip6mr_rules_seq_read(struct net *net) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci return fib_rules_seq_read(net, RTNL_FAMILY_IP6MR); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cibool ip6mr_rule_default(const struct fib_rule *rule) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci return fib_rule_matchall(rule) && rule->action == FR_ACT_TO_TBL && 2868c2ecf20Sopenharmony_ci rule->table == RT6_TABLE_DFLT && !rule->l3mdev; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ip6mr_rule_default); 2898c2ecf20Sopenharmony_ci#else 2908c2ecf20Sopenharmony_ci#define ip6mr_for_each_table(mrt, net) \ 2918c2ecf20Sopenharmony_ci for (mrt = net->ipv6.mrt6; mrt; mrt = NULL) 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic struct mr_table *ip6mr_mr_table_iter(struct net *net, 2948c2ecf20Sopenharmony_ci struct mr_table *mrt) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci if (!mrt) 2978c2ecf20Sopenharmony_ci return net->ipv6.mrt6; 2988c2ecf20Sopenharmony_ci return NULL; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic struct mr_table *ip6mr_get_table(struct net *net, u32 id) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci return net->ipv6.mrt6; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int ip6mr_fib_lookup(struct net *net, struct flowi6 *flp6, 3078c2ecf20Sopenharmony_ci struct mr_table **mrt) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci *mrt = net->ipv6.mrt6; 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int __net_init ip6mr_rules_init(struct net *net) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct mr_table *mrt; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci mrt = ip6mr_new_table(net, RT6_TABLE_DFLT); 3188c2ecf20Sopenharmony_ci if (IS_ERR(mrt)) 3198c2ecf20Sopenharmony_ci return PTR_ERR(mrt); 3208c2ecf20Sopenharmony_ci net->ipv6.mrt6 = mrt; 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void __net_exit ip6mr_rules_exit(struct net *net) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci rtnl_lock(); 3278c2ecf20Sopenharmony_ci ip6mr_free_table(net->ipv6.mrt6); 3288c2ecf20Sopenharmony_ci net->ipv6.mrt6 = NULL; 3298c2ecf20Sopenharmony_ci rtnl_unlock(); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int ip6mr_rules_dump(struct net *net, struct notifier_block *nb, 3338c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic unsigned int ip6mr_rules_seq_read(struct net *net) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci return 0; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci#endif 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic int ip6mr_hash_cmp(struct rhashtable_compare_arg *arg, 3458c2ecf20Sopenharmony_ci const void *ptr) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci const struct mfc6_cache_cmp_arg *cmparg = arg->key; 3488c2ecf20Sopenharmony_ci struct mfc6_cache *c = (struct mfc6_cache *)ptr; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return !ipv6_addr_equal(&c->mf6c_mcastgrp, &cmparg->mf6c_mcastgrp) || 3518c2ecf20Sopenharmony_ci !ipv6_addr_equal(&c->mf6c_origin, &cmparg->mf6c_origin); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic const struct rhashtable_params ip6mr_rht_params = { 3558c2ecf20Sopenharmony_ci .head_offset = offsetof(struct mr_mfc, mnode), 3568c2ecf20Sopenharmony_ci .key_offset = offsetof(struct mfc6_cache, cmparg), 3578c2ecf20Sopenharmony_ci .key_len = sizeof(struct mfc6_cache_cmp_arg), 3588c2ecf20Sopenharmony_ci .nelem_hint = 3, 3598c2ecf20Sopenharmony_ci .obj_cmpfn = ip6mr_hash_cmp, 3608c2ecf20Sopenharmony_ci .automatic_shrinking = true, 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic void ip6mr_new_table_set(struct mr_table *mrt, 3648c2ecf20Sopenharmony_ci struct net *net) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES 3678c2ecf20Sopenharmony_ci list_add_tail_rcu(&mrt->list, &net->ipv6.mr6_tables); 3688c2ecf20Sopenharmony_ci#endif 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic struct mfc6_cache_cmp_arg ip6mr_mr_table_ops_cmparg_any = { 3728c2ecf20Sopenharmony_ci .mf6c_origin = IN6ADDR_ANY_INIT, 3738c2ecf20Sopenharmony_ci .mf6c_mcastgrp = IN6ADDR_ANY_INIT, 3748c2ecf20Sopenharmony_ci}; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic struct mr_table_ops ip6mr_mr_table_ops = { 3778c2ecf20Sopenharmony_ci .rht_params = &ip6mr_rht_params, 3788c2ecf20Sopenharmony_ci .cmparg_any = &ip6mr_mr_table_ops_cmparg_any, 3798c2ecf20Sopenharmony_ci}; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic struct mr_table *ip6mr_new_table(struct net *net, u32 id) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct mr_table *mrt; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci mrt = ip6mr_get_table(net, id); 3868c2ecf20Sopenharmony_ci if (mrt) 3878c2ecf20Sopenharmony_ci return mrt; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return mr_table_alloc(net, id, &ip6mr_mr_table_ops, 3908c2ecf20Sopenharmony_ci ipmr_expire_process, ip6mr_new_table_set); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic void ip6mr_free_table(struct mr_table *mrt) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci del_timer_sync(&mrt->ipmr_expire_timer); 3968c2ecf20Sopenharmony_ci mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC | 3978c2ecf20Sopenharmony_ci MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC); 3988c2ecf20Sopenharmony_ci rhltable_destroy(&mrt->mfc_hash); 3998c2ecf20Sopenharmony_ci kfree(mrt); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 4038c2ecf20Sopenharmony_ci/* The /proc interfaces to multicast routing 4048c2ecf20Sopenharmony_ci * /proc/ip6_mr_cache /proc/ip6_mr_vif 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic void *ip6mr_vif_seq_start(struct seq_file *seq, loff_t *pos) 4088c2ecf20Sopenharmony_ci __acquires(mrt_lock) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct mr_vif_iter *iter = seq->private; 4118c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 4128c2ecf20Sopenharmony_ci struct mr_table *mrt; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); 4158c2ecf20Sopenharmony_ci if (!mrt) 4168c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci iter->mrt = mrt; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci read_lock(&mrt_lock); 4218c2ecf20Sopenharmony_ci return mr_vif_seq_start(seq, pos); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic void ip6mr_vif_seq_stop(struct seq_file *seq, void *v) 4258c2ecf20Sopenharmony_ci __releases(mrt_lock) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int ip6mr_vif_seq_show(struct seq_file *seq, void *v) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct mr_vif_iter *iter = seq->private; 4338c2ecf20Sopenharmony_ci struct mr_table *mrt = iter->mrt; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) { 4368c2ecf20Sopenharmony_ci seq_puts(seq, 4378c2ecf20Sopenharmony_ci "Interface BytesIn PktsIn BytesOut PktsOut Flags\n"); 4388c2ecf20Sopenharmony_ci } else { 4398c2ecf20Sopenharmony_ci const struct vif_device *vif = v; 4408c2ecf20Sopenharmony_ci const char *name = vif->dev ? vif->dev->name : "none"; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci seq_printf(seq, 4438c2ecf20Sopenharmony_ci "%2td %-10s %8ld %7ld %8ld %7ld %05X\n", 4448c2ecf20Sopenharmony_ci vif - mrt->vif_table, 4458c2ecf20Sopenharmony_ci name, vif->bytes_in, vif->pkt_in, 4468c2ecf20Sopenharmony_ci vif->bytes_out, vif->pkt_out, 4478c2ecf20Sopenharmony_ci vif->flags); 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic const struct seq_operations ip6mr_vif_seq_ops = { 4538c2ecf20Sopenharmony_ci .start = ip6mr_vif_seq_start, 4548c2ecf20Sopenharmony_ci .next = mr_vif_seq_next, 4558c2ecf20Sopenharmony_ci .stop = ip6mr_vif_seq_stop, 4568c2ecf20Sopenharmony_ci .show = ip6mr_vif_seq_show, 4578c2ecf20Sopenharmony_ci}; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 4628c2ecf20Sopenharmony_ci struct mr_table *mrt; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); 4658c2ecf20Sopenharmony_ci if (!mrt) 4668c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return mr_mfc_seq_start(seq, pos, mrt, &mfc_unres_lock); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int ipmr_mfc_seq_show(struct seq_file *seq, void *v) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci int n; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) { 4768c2ecf20Sopenharmony_ci seq_puts(seq, 4778c2ecf20Sopenharmony_ci "Group " 4788c2ecf20Sopenharmony_ci "Origin " 4798c2ecf20Sopenharmony_ci "Iif Pkts Bytes Wrong Oifs\n"); 4808c2ecf20Sopenharmony_ci } else { 4818c2ecf20Sopenharmony_ci const struct mfc6_cache *mfc = v; 4828c2ecf20Sopenharmony_ci const struct mr_mfc_iter *it = seq->private; 4838c2ecf20Sopenharmony_ci struct mr_table *mrt = it->mrt; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci seq_printf(seq, "%pI6 %pI6 %-3hd", 4868c2ecf20Sopenharmony_ci &mfc->mf6c_mcastgrp, &mfc->mf6c_origin, 4878c2ecf20Sopenharmony_ci mfc->_c.mfc_parent); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (it->cache != &mrt->mfc_unres_queue) { 4908c2ecf20Sopenharmony_ci seq_printf(seq, " %8lu %8lu %8lu", 4918c2ecf20Sopenharmony_ci mfc->_c.mfc_un.res.pkt, 4928c2ecf20Sopenharmony_ci mfc->_c.mfc_un.res.bytes, 4938c2ecf20Sopenharmony_ci mfc->_c.mfc_un.res.wrong_if); 4948c2ecf20Sopenharmony_ci for (n = mfc->_c.mfc_un.res.minvif; 4958c2ecf20Sopenharmony_ci n < mfc->_c.mfc_un.res.maxvif; n++) { 4968c2ecf20Sopenharmony_ci if (VIF_EXISTS(mrt, n) && 4978c2ecf20Sopenharmony_ci mfc->_c.mfc_un.res.ttls[n] < 255) 4988c2ecf20Sopenharmony_ci seq_printf(seq, 4998c2ecf20Sopenharmony_ci " %2d:%-3d", n, 5008c2ecf20Sopenharmony_ci mfc->_c.mfc_un.res.ttls[n]); 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci } else { 5038c2ecf20Sopenharmony_ci /* unresolved mfc_caches don't contain 5048c2ecf20Sopenharmony_ci * pkt, bytes and wrong_if values 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_ci seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul); 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci seq_putc(seq, '\n'); 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic const struct seq_operations ipmr_mfc_seq_ops = { 5148c2ecf20Sopenharmony_ci .start = ipmr_mfc_seq_start, 5158c2ecf20Sopenharmony_ci .next = mr_mfc_seq_next, 5168c2ecf20Sopenharmony_ci .stop = mr_mfc_seq_stop, 5178c2ecf20Sopenharmony_ci .show = ipmr_mfc_seq_show, 5188c2ecf20Sopenharmony_ci}; 5198c2ecf20Sopenharmony_ci#endif 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic int pim6_rcv(struct sk_buff *skb) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct pimreghdr *pim; 5268c2ecf20Sopenharmony_ci struct ipv6hdr *encap; 5278c2ecf20Sopenharmony_ci struct net_device *reg_dev = NULL; 5288c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 5298c2ecf20Sopenharmony_ci struct mr_table *mrt; 5308c2ecf20Sopenharmony_ci struct flowi6 fl6 = { 5318c2ecf20Sopenharmony_ci .flowi6_iif = skb->dev->ifindex, 5328c2ecf20Sopenharmony_ci .flowi6_mark = skb->mark, 5338c2ecf20Sopenharmony_ci }; 5348c2ecf20Sopenharmony_ci int reg_vif_num; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap))) 5378c2ecf20Sopenharmony_ci goto drop; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci pim = (struct pimreghdr *)skb_transport_header(skb); 5408c2ecf20Sopenharmony_ci if (pim->type != ((PIM_VERSION << 4) | PIM_TYPE_REGISTER) || 5418c2ecf20Sopenharmony_ci (pim->flags & PIM_NULL_REGISTER) || 5428c2ecf20Sopenharmony_ci (csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, 5438c2ecf20Sopenharmony_ci sizeof(*pim), IPPROTO_PIM, 5448c2ecf20Sopenharmony_ci csum_partial((void *)pim, sizeof(*pim), 0)) && 5458c2ecf20Sopenharmony_ci csum_fold(skb_checksum(skb, 0, skb->len, 0)))) 5468c2ecf20Sopenharmony_ci goto drop; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* check if the inner packet is destined to mcast group */ 5498c2ecf20Sopenharmony_ci encap = (struct ipv6hdr *)(skb_transport_header(skb) + 5508c2ecf20Sopenharmony_ci sizeof(*pim)); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (!ipv6_addr_is_multicast(&encap->daddr) || 5538c2ecf20Sopenharmony_ci encap->payload_len == 0 || 5548c2ecf20Sopenharmony_ci ntohs(encap->payload_len) + sizeof(*pim) > skb->len) 5558c2ecf20Sopenharmony_ci goto drop; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0) 5588c2ecf20Sopenharmony_ci goto drop; 5598c2ecf20Sopenharmony_ci reg_vif_num = mrt->mroute_reg_vif_num; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci read_lock(&mrt_lock); 5628c2ecf20Sopenharmony_ci if (reg_vif_num >= 0) 5638c2ecf20Sopenharmony_ci reg_dev = mrt->vif_table[reg_vif_num].dev; 5648c2ecf20Sopenharmony_ci if (reg_dev) 5658c2ecf20Sopenharmony_ci dev_hold(reg_dev); 5668c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (!reg_dev) 5698c2ecf20Sopenharmony_ci goto drop; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci skb->mac_header = skb->network_header; 5728c2ecf20Sopenharmony_ci skb_pull(skb, (u8 *)encap - skb->data); 5738c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 5748c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 5758c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci skb_tunnel_rx(skb, reg_dev, dev_net(reg_dev)); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci netif_rx(skb); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci dev_put(reg_dev); 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci drop: 5848c2ecf20Sopenharmony_ci kfree_skb(skb); 5858c2ecf20Sopenharmony_ci return 0; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic const struct inet6_protocol pim6_protocol = { 5898c2ecf20Sopenharmony_ci .handler = pim6_rcv, 5908c2ecf20Sopenharmony_ci}; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci/* Service routines creating virtual interfaces: PIMREG */ 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic netdev_tx_t reg_vif_xmit(struct sk_buff *skb, 5958c2ecf20Sopenharmony_ci struct net_device *dev) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 5988c2ecf20Sopenharmony_ci struct mr_table *mrt; 5998c2ecf20Sopenharmony_ci struct flowi6 fl6 = { 6008c2ecf20Sopenharmony_ci .flowi6_oif = dev->ifindex, 6018c2ecf20Sopenharmony_ci .flowi6_iif = skb->skb_iif ? : LOOPBACK_IFINDEX, 6028c2ecf20Sopenharmony_ci .flowi6_mark = skb->mark, 6038c2ecf20Sopenharmony_ci }; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (!pskb_inet_may_pull(skb)) 6068c2ecf20Sopenharmony_ci goto tx_err; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0) 6098c2ecf20Sopenharmony_ci goto tx_err; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci read_lock(&mrt_lock); 6128c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 6138c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 6148c2ecf20Sopenharmony_ci ip6mr_cache_report(mrt, skb, mrt->mroute_reg_vif_num, MRT6MSG_WHOLEPKT); 6158c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 6168c2ecf20Sopenharmony_ci kfree_skb(skb); 6178c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_citx_err: 6208c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 6218c2ecf20Sopenharmony_ci kfree_skb(skb); 6228c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic int reg_vif_get_iflink(const struct net_device *dev) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci return 0; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic const struct net_device_ops reg_vif_netdev_ops = { 6318c2ecf20Sopenharmony_ci .ndo_start_xmit = reg_vif_xmit, 6328c2ecf20Sopenharmony_ci .ndo_get_iflink = reg_vif_get_iflink, 6338c2ecf20Sopenharmony_ci}; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic void reg_vif_setup(struct net_device *dev) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci dev->type = ARPHRD_PIMREG; 6388c2ecf20Sopenharmony_ci dev->mtu = 1500 - sizeof(struct ipv6hdr) - 8; 6398c2ecf20Sopenharmony_ci dev->flags = IFF_NOARP; 6408c2ecf20Sopenharmony_ci dev->netdev_ops = ®_vif_netdev_ops; 6418c2ecf20Sopenharmony_ci dev->needs_free_netdev = true; 6428c2ecf20Sopenharmony_ci dev->features |= NETIF_F_NETNS_LOCAL; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic struct net_device *ip6mr_reg_vif(struct net *net, struct mr_table *mrt) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci struct net_device *dev; 6488c2ecf20Sopenharmony_ci char name[IFNAMSIZ]; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (mrt->id == RT6_TABLE_DFLT) 6518c2ecf20Sopenharmony_ci sprintf(name, "pim6reg"); 6528c2ecf20Sopenharmony_ci else 6538c2ecf20Sopenharmony_ci sprintf(name, "pim6reg%u", mrt->id); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, reg_vif_setup); 6568c2ecf20Sopenharmony_ci if (!dev) 6578c2ecf20Sopenharmony_ci return NULL; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci dev_net_set(dev, net); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (register_netdevice(dev)) { 6628c2ecf20Sopenharmony_ci free_netdev(dev); 6638c2ecf20Sopenharmony_ci return NULL; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (dev_open(dev, NULL)) 6678c2ecf20Sopenharmony_ci goto failure; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci dev_hold(dev); 6708c2ecf20Sopenharmony_ci return dev; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cifailure: 6738c2ecf20Sopenharmony_ci unregister_netdevice(dev); 6748c2ecf20Sopenharmony_ci return NULL; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci#endif 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic int call_ip6mr_vif_entry_notifiers(struct net *net, 6798c2ecf20Sopenharmony_ci enum fib_event_type event_type, 6808c2ecf20Sopenharmony_ci struct vif_device *vif, 6818c2ecf20Sopenharmony_ci mifi_t vif_index, u32 tb_id) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci return mr_call_vif_notifiers(net, RTNL_FAMILY_IP6MR, event_type, 6848c2ecf20Sopenharmony_ci vif, vif_index, tb_id, 6858c2ecf20Sopenharmony_ci &net->ipv6.ipmr_seq); 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic int call_ip6mr_mfc_entry_notifiers(struct net *net, 6898c2ecf20Sopenharmony_ci enum fib_event_type event_type, 6908c2ecf20Sopenharmony_ci struct mfc6_cache *mfc, u32 tb_id) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci return mr_call_mfc_notifiers(net, RTNL_FAMILY_IP6MR, event_type, 6938c2ecf20Sopenharmony_ci &mfc->_c, tb_id, &net->ipv6.ipmr_seq); 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci/* Delete a VIF entry */ 6978c2ecf20Sopenharmony_cistatic int mif6_delete(struct mr_table *mrt, int vifi, int notify, 6988c2ecf20Sopenharmony_ci struct list_head *head) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci struct vif_device *v; 7018c2ecf20Sopenharmony_ci struct net_device *dev; 7028c2ecf20Sopenharmony_ci struct inet6_dev *in6_dev; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (vifi < 0 || vifi >= mrt->maxvif) 7058c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci v = &mrt->vif_table[vifi]; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (VIF_EXISTS(mrt, vifi)) 7108c2ecf20Sopenharmony_ci call_ip6mr_vif_entry_notifiers(read_pnet(&mrt->net), 7118c2ecf20Sopenharmony_ci FIB_EVENT_VIF_DEL, v, vifi, 7128c2ecf20Sopenharmony_ci mrt->id); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci write_lock_bh(&mrt_lock); 7158c2ecf20Sopenharmony_ci dev = v->dev; 7168c2ecf20Sopenharmony_ci v->dev = NULL; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci if (!dev) { 7198c2ecf20Sopenharmony_ci write_unlock_bh(&mrt_lock); 7208c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2 7248c2ecf20Sopenharmony_ci if (vifi == mrt->mroute_reg_vif_num) 7258c2ecf20Sopenharmony_ci mrt->mroute_reg_vif_num = -1; 7268c2ecf20Sopenharmony_ci#endif 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (vifi + 1 == mrt->maxvif) { 7298c2ecf20Sopenharmony_ci int tmp; 7308c2ecf20Sopenharmony_ci for (tmp = vifi - 1; tmp >= 0; tmp--) { 7318c2ecf20Sopenharmony_ci if (VIF_EXISTS(mrt, tmp)) 7328c2ecf20Sopenharmony_ci break; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci mrt->maxvif = tmp + 1; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci write_unlock_bh(&mrt_lock); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci dev_set_allmulti(dev, -1); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci in6_dev = __in6_dev_get(dev); 7428c2ecf20Sopenharmony_ci if (in6_dev) { 7438c2ecf20Sopenharmony_ci atomic_dec(&in6_dev->cnf.mc_forwarding); 7448c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, 7458c2ecf20Sopenharmony_ci NETCONFA_MC_FORWARDING, 7468c2ecf20Sopenharmony_ci dev->ifindex, &in6_dev->cnf); 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci if ((v->flags & MIFF_REGISTER) && !notify) 7508c2ecf20Sopenharmony_ci unregister_netdevice_queue(dev, head); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci dev_put(dev); 7538c2ecf20Sopenharmony_ci return 0; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic inline void ip6mr_cache_free_rcu(struct rcu_head *head) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct mr_mfc *c = container_of(head, struct mr_mfc, rcu); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci kmem_cache_free(mrt_cachep, (struct mfc6_cache *)c); 7618c2ecf20Sopenharmony_ci} 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cistatic inline void ip6mr_cache_free(struct mfc6_cache *c) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci call_rcu(&c->_c.rcu, ip6mr_cache_free_rcu); 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci/* Destroy an unresolved cache entry, killing queued skbs 7698c2ecf20Sopenharmony_ci and reporting error to netlink readers. 7708c2ecf20Sopenharmony_ci */ 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic void ip6mr_destroy_unres(struct mr_table *mrt, struct mfc6_cache *c) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci struct net *net = read_pnet(&mrt->net); 7758c2ecf20Sopenharmony_ci struct sk_buff *skb; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci atomic_dec(&mrt->cache_resolve_queue_len); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&c->_c.mfc_un.unres.unresolved)) != NULL) { 7808c2ecf20Sopenharmony_ci if (ipv6_hdr(skb)->version == 0) { 7818c2ecf20Sopenharmony_ci struct nlmsghdr *nlh = skb_pull(skb, 7828c2ecf20Sopenharmony_ci sizeof(struct ipv6hdr)); 7838c2ecf20Sopenharmony_ci nlh->nlmsg_type = NLMSG_ERROR; 7848c2ecf20Sopenharmony_ci nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 7858c2ecf20Sopenharmony_ci skb_trim(skb, nlh->nlmsg_len); 7868c2ecf20Sopenharmony_ci ((struct nlmsgerr *)nlmsg_data(nlh))->error = -ETIMEDOUT; 7878c2ecf20Sopenharmony_ci rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 7888c2ecf20Sopenharmony_ci } else 7898c2ecf20Sopenharmony_ci kfree_skb(skb); 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci ip6mr_cache_free(c); 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci/* Timer process for all the unresolved queue. */ 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_cistatic void ipmr_do_expire_process(struct mr_table *mrt) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci unsigned long now = jiffies; 8018c2ecf20Sopenharmony_ci unsigned long expires = 10 * HZ; 8028c2ecf20Sopenharmony_ci struct mr_mfc *c, *next; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) { 8058c2ecf20Sopenharmony_ci if (time_after(c->mfc_un.unres.expires, now)) { 8068c2ecf20Sopenharmony_ci /* not yet... */ 8078c2ecf20Sopenharmony_ci unsigned long interval = c->mfc_un.unres.expires - now; 8088c2ecf20Sopenharmony_ci if (interval < expires) 8098c2ecf20Sopenharmony_ci expires = interval; 8108c2ecf20Sopenharmony_ci continue; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci list_del(&c->list); 8148c2ecf20Sopenharmony_ci mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE); 8158c2ecf20Sopenharmony_ci ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c); 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (!list_empty(&mrt->mfc_unres_queue)) 8198c2ecf20Sopenharmony_ci mod_timer(&mrt->ipmr_expire_timer, jiffies + expires); 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic void ipmr_expire_process(struct timer_list *t) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci struct mr_table *mrt = from_timer(mrt, t, ipmr_expire_timer); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (!spin_trylock(&mfc_unres_lock)) { 8278c2ecf20Sopenharmony_ci mod_timer(&mrt->ipmr_expire_timer, jiffies + 1); 8288c2ecf20Sopenharmony_ci return; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (!list_empty(&mrt->mfc_unres_queue)) 8328c2ecf20Sopenharmony_ci ipmr_do_expire_process(mrt); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci spin_unlock(&mfc_unres_lock); 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci/* Fill oifs list. It is called under write locked mrt_lock. */ 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic void ip6mr_update_thresholds(struct mr_table *mrt, 8408c2ecf20Sopenharmony_ci struct mr_mfc *cache, 8418c2ecf20Sopenharmony_ci unsigned char *ttls) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci int vifi; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci cache->mfc_un.res.minvif = MAXMIFS; 8468c2ecf20Sopenharmony_ci cache->mfc_un.res.maxvif = 0; 8478c2ecf20Sopenharmony_ci memset(cache->mfc_un.res.ttls, 255, MAXMIFS); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci for (vifi = 0; vifi < mrt->maxvif; vifi++) { 8508c2ecf20Sopenharmony_ci if (VIF_EXISTS(mrt, vifi) && 8518c2ecf20Sopenharmony_ci ttls[vifi] && ttls[vifi] < 255) { 8528c2ecf20Sopenharmony_ci cache->mfc_un.res.ttls[vifi] = ttls[vifi]; 8538c2ecf20Sopenharmony_ci if (cache->mfc_un.res.minvif > vifi) 8548c2ecf20Sopenharmony_ci cache->mfc_un.res.minvif = vifi; 8558c2ecf20Sopenharmony_ci if (cache->mfc_un.res.maxvif <= vifi) 8568c2ecf20Sopenharmony_ci cache->mfc_un.res.maxvif = vifi + 1; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci cache->mfc_un.res.lastuse = jiffies; 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic int mif6_add(struct net *net, struct mr_table *mrt, 8638c2ecf20Sopenharmony_ci struct mif6ctl *vifc, int mrtsock) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci int vifi = vifc->mif6c_mifi; 8668c2ecf20Sopenharmony_ci struct vif_device *v = &mrt->vif_table[vifi]; 8678c2ecf20Sopenharmony_ci struct net_device *dev; 8688c2ecf20Sopenharmony_ci struct inet6_dev *in6_dev; 8698c2ecf20Sopenharmony_ci int err; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* Is vif busy ? */ 8728c2ecf20Sopenharmony_ci if (VIF_EXISTS(mrt, vifi)) 8738c2ecf20Sopenharmony_ci return -EADDRINUSE; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci switch (vifc->mif6c_flags) { 8768c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2 8778c2ecf20Sopenharmony_ci case MIFF_REGISTER: 8788c2ecf20Sopenharmony_ci /* 8798c2ecf20Sopenharmony_ci * Special Purpose VIF in PIM 8808c2ecf20Sopenharmony_ci * All the packets will be sent to the daemon 8818c2ecf20Sopenharmony_ci */ 8828c2ecf20Sopenharmony_ci if (mrt->mroute_reg_vif_num >= 0) 8838c2ecf20Sopenharmony_ci return -EADDRINUSE; 8848c2ecf20Sopenharmony_ci dev = ip6mr_reg_vif(net, mrt); 8858c2ecf20Sopenharmony_ci if (!dev) 8868c2ecf20Sopenharmony_ci return -ENOBUFS; 8878c2ecf20Sopenharmony_ci err = dev_set_allmulti(dev, 1); 8888c2ecf20Sopenharmony_ci if (err) { 8898c2ecf20Sopenharmony_ci unregister_netdevice(dev); 8908c2ecf20Sopenharmony_ci dev_put(dev); 8918c2ecf20Sopenharmony_ci return err; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci break; 8948c2ecf20Sopenharmony_ci#endif 8958c2ecf20Sopenharmony_ci case 0: 8968c2ecf20Sopenharmony_ci dev = dev_get_by_index(net, vifc->mif6c_pifi); 8978c2ecf20Sopenharmony_ci if (!dev) 8988c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 8998c2ecf20Sopenharmony_ci err = dev_set_allmulti(dev, 1); 9008c2ecf20Sopenharmony_ci if (err) { 9018c2ecf20Sopenharmony_ci dev_put(dev); 9028c2ecf20Sopenharmony_ci return err; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci break; 9058c2ecf20Sopenharmony_ci default: 9068c2ecf20Sopenharmony_ci return -EINVAL; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci in6_dev = __in6_dev_get(dev); 9108c2ecf20Sopenharmony_ci if (in6_dev) { 9118c2ecf20Sopenharmony_ci atomic_inc(&in6_dev->cnf.mc_forwarding); 9128c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, 9138c2ecf20Sopenharmony_ci NETCONFA_MC_FORWARDING, 9148c2ecf20Sopenharmony_ci dev->ifindex, &in6_dev->cnf); 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci /* Fill in the VIF structures */ 9188c2ecf20Sopenharmony_ci vif_device_init(v, dev, vifc->vifc_rate_limit, vifc->vifc_threshold, 9198c2ecf20Sopenharmony_ci vifc->mif6c_flags | (!mrtsock ? VIFF_STATIC : 0), 9208c2ecf20Sopenharmony_ci MIFF_REGISTER); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* And finish update writing critical data */ 9238c2ecf20Sopenharmony_ci write_lock_bh(&mrt_lock); 9248c2ecf20Sopenharmony_ci v->dev = dev; 9258c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2 9268c2ecf20Sopenharmony_ci if (v->flags & MIFF_REGISTER) 9278c2ecf20Sopenharmony_ci mrt->mroute_reg_vif_num = vifi; 9288c2ecf20Sopenharmony_ci#endif 9298c2ecf20Sopenharmony_ci if (vifi + 1 > mrt->maxvif) 9308c2ecf20Sopenharmony_ci mrt->maxvif = vifi + 1; 9318c2ecf20Sopenharmony_ci write_unlock_bh(&mrt_lock); 9328c2ecf20Sopenharmony_ci call_ip6mr_vif_entry_notifiers(net, FIB_EVENT_VIF_ADD, 9338c2ecf20Sopenharmony_ci v, vifi, mrt->id); 9348c2ecf20Sopenharmony_ci return 0; 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic struct mfc6_cache *ip6mr_cache_find(struct mr_table *mrt, 9388c2ecf20Sopenharmony_ci const struct in6_addr *origin, 9398c2ecf20Sopenharmony_ci const struct in6_addr *mcastgrp) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci struct mfc6_cache_cmp_arg arg = { 9428c2ecf20Sopenharmony_ci .mf6c_origin = *origin, 9438c2ecf20Sopenharmony_ci .mf6c_mcastgrp = *mcastgrp, 9448c2ecf20Sopenharmony_ci }; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci return mr_mfc_find(mrt, &arg); 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci/* Look for a (*,G) entry */ 9508c2ecf20Sopenharmony_cistatic struct mfc6_cache *ip6mr_cache_find_any(struct mr_table *mrt, 9518c2ecf20Sopenharmony_ci struct in6_addr *mcastgrp, 9528c2ecf20Sopenharmony_ci mifi_t mifi) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci struct mfc6_cache_cmp_arg arg = { 9558c2ecf20Sopenharmony_ci .mf6c_origin = in6addr_any, 9568c2ecf20Sopenharmony_ci .mf6c_mcastgrp = *mcastgrp, 9578c2ecf20Sopenharmony_ci }; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (ipv6_addr_any(mcastgrp)) 9608c2ecf20Sopenharmony_ci return mr_mfc_find_any_parent(mrt, mifi); 9618c2ecf20Sopenharmony_ci return mr_mfc_find_any(mrt, mifi, &arg); 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci/* Look for a (S,G,iif) entry if parent != -1 */ 9658c2ecf20Sopenharmony_cistatic struct mfc6_cache * 9668c2ecf20Sopenharmony_ciip6mr_cache_find_parent(struct mr_table *mrt, 9678c2ecf20Sopenharmony_ci const struct in6_addr *origin, 9688c2ecf20Sopenharmony_ci const struct in6_addr *mcastgrp, 9698c2ecf20Sopenharmony_ci int parent) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci struct mfc6_cache_cmp_arg arg = { 9728c2ecf20Sopenharmony_ci .mf6c_origin = *origin, 9738c2ecf20Sopenharmony_ci .mf6c_mcastgrp = *mcastgrp, 9748c2ecf20Sopenharmony_ci }; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci return mr_mfc_find_parent(mrt, &arg, parent); 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci/* Allocate a multicast cache entry */ 9808c2ecf20Sopenharmony_cistatic struct mfc6_cache *ip6mr_cache_alloc(void) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL); 9838c2ecf20Sopenharmony_ci if (!c) 9848c2ecf20Sopenharmony_ci return NULL; 9858c2ecf20Sopenharmony_ci c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1; 9868c2ecf20Sopenharmony_ci c->_c.mfc_un.res.minvif = MAXMIFS; 9878c2ecf20Sopenharmony_ci c->_c.free = ip6mr_cache_free_rcu; 9888c2ecf20Sopenharmony_ci refcount_set(&c->_c.mfc_un.res.refcount, 1); 9898c2ecf20Sopenharmony_ci return c; 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_cistatic struct mfc6_cache *ip6mr_cache_alloc_unres(void) 9938c2ecf20Sopenharmony_ci{ 9948c2ecf20Sopenharmony_ci struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC); 9958c2ecf20Sopenharmony_ci if (!c) 9968c2ecf20Sopenharmony_ci return NULL; 9978c2ecf20Sopenharmony_ci skb_queue_head_init(&c->_c.mfc_un.unres.unresolved); 9988c2ecf20Sopenharmony_ci c->_c.mfc_un.unres.expires = jiffies + 10 * HZ; 9998c2ecf20Sopenharmony_ci return c; 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci/* 10038c2ecf20Sopenharmony_ci * A cache entry has gone into a resolved state from queued 10048c2ecf20Sopenharmony_ci */ 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic void ip6mr_cache_resolve(struct net *net, struct mr_table *mrt, 10078c2ecf20Sopenharmony_ci struct mfc6_cache *uc, struct mfc6_cache *c) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci struct sk_buff *skb; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci /* 10128c2ecf20Sopenharmony_ci * Play the pending entries through our router 10138c2ecf20Sopenharmony_ci */ 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci while ((skb = __skb_dequeue(&uc->_c.mfc_un.unres.unresolved))) { 10168c2ecf20Sopenharmony_ci if (ipv6_hdr(skb)->version == 0) { 10178c2ecf20Sopenharmony_ci struct nlmsghdr *nlh = skb_pull(skb, 10188c2ecf20Sopenharmony_ci sizeof(struct ipv6hdr)); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if (mr_fill_mroute(mrt, skb, &c->_c, 10218c2ecf20Sopenharmony_ci nlmsg_data(nlh)) > 0) { 10228c2ecf20Sopenharmony_ci nlh->nlmsg_len = skb_tail_pointer(skb) - (u8 *)nlh; 10238c2ecf20Sopenharmony_ci } else { 10248c2ecf20Sopenharmony_ci nlh->nlmsg_type = NLMSG_ERROR; 10258c2ecf20Sopenharmony_ci nlh->nlmsg_len = nlmsg_msg_size(sizeof(struct nlmsgerr)); 10268c2ecf20Sopenharmony_ci skb_trim(skb, nlh->nlmsg_len); 10278c2ecf20Sopenharmony_ci ((struct nlmsgerr *)nlmsg_data(nlh))->error = -EMSGSIZE; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci rtnl_unicast(skb, net, NETLINK_CB(skb).portid); 10308c2ecf20Sopenharmony_ci } else 10318c2ecf20Sopenharmony_ci ip6_mr_forward(net, mrt, skb->dev, skb, c); 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci/* 10368c2ecf20Sopenharmony_ci * Bounce a cache query up to pim6sd and netlink. 10378c2ecf20Sopenharmony_ci * 10388c2ecf20Sopenharmony_ci * Called under mrt_lock. 10398c2ecf20Sopenharmony_ci */ 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistatic int ip6mr_cache_report(struct mr_table *mrt, struct sk_buff *pkt, 10428c2ecf20Sopenharmony_ci mifi_t mifi, int assert) 10438c2ecf20Sopenharmony_ci{ 10448c2ecf20Sopenharmony_ci struct sock *mroute6_sk; 10458c2ecf20Sopenharmony_ci struct sk_buff *skb; 10468c2ecf20Sopenharmony_ci struct mrt6msg *msg; 10478c2ecf20Sopenharmony_ci int ret; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2 10508c2ecf20Sopenharmony_ci if (assert == MRT6MSG_WHOLEPKT) 10518c2ecf20Sopenharmony_ci skb = skb_realloc_headroom(pkt, -skb_network_offset(pkt) 10528c2ecf20Sopenharmony_ci +sizeof(*msg)); 10538c2ecf20Sopenharmony_ci else 10548c2ecf20Sopenharmony_ci#endif 10558c2ecf20Sopenharmony_ci skb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(*msg), GFP_ATOMIC); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (!skb) 10588c2ecf20Sopenharmony_ci return -ENOBUFS; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* I suppose that internal messages 10618c2ecf20Sopenharmony_ci * do not require checksums */ 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2 10668c2ecf20Sopenharmony_ci if (assert == MRT6MSG_WHOLEPKT) { 10678c2ecf20Sopenharmony_ci /* Ugly, but we have no choice with this interface. 10688c2ecf20Sopenharmony_ci Duplicate old header, fix length etc. 10698c2ecf20Sopenharmony_ci And all this only to mangle msg->im6_msgtype and 10708c2ecf20Sopenharmony_ci to set msg->im6_mbz to "mbz" :-) 10718c2ecf20Sopenharmony_ci */ 10728c2ecf20Sopenharmony_ci __skb_pull(skb, skb_network_offset(pkt)); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci skb_push(skb, sizeof(*msg)); 10758c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); 10768c2ecf20Sopenharmony_ci msg = (struct mrt6msg *)skb_transport_header(skb); 10778c2ecf20Sopenharmony_ci msg->im6_mbz = 0; 10788c2ecf20Sopenharmony_ci msg->im6_msgtype = MRT6MSG_WHOLEPKT; 10798c2ecf20Sopenharmony_ci msg->im6_mif = mrt->mroute_reg_vif_num; 10808c2ecf20Sopenharmony_ci msg->im6_pad = 0; 10818c2ecf20Sopenharmony_ci msg->im6_src = ipv6_hdr(pkt)->saddr; 10828c2ecf20Sopenharmony_ci msg->im6_dst = ipv6_hdr(pkt)->daddr; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 10858c2ecf20Sopenharmony_ci } else 10868c2ecf20Sopenharmony_ci#endif 10878c2ecf20Sopenharmony_ci { 10888c2ecf20Sopenharmony_ci /* 10898c2ecf20Sopenharmony_ci * Copy the IP header 10908c2ecf20Sopenharmony_ci */ 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci skb_put(skb, sizeof(struct ipv6hdr)); 10938c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 10948c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skb, ipv6_hdr(pkt), sizeof(struct ipv6hdr)); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci /* 10978c2ecf20Sopenharmony_ci * Add our header 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_ci skb_put(skb, sizeof(*msg)); 11008c2ecf20Sopenharmony_ci skb_reset_transport_header(skb); 11018c2ecf20Sopenharmony_ci msg = (struct mrt6msg *)skb_transport_header(skb); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci msg->im6_mbz = 0; 11048c2ecf20Sopenharmony_ci msg->im6_msgtype = assert; 11058c2ecf20Sopenharmony_ci msg->im6_mif = mifi; 11068c2ecf20Sopenharmony_ci msg->im6_pad = 0; 11078c2ecf20Sopenharmony_ci msg->im6_src = ipv6_hdr(pkt)->saddr; 11088c2ecf20Sopenharmony_ci msg->im6_dst = ipv6_hdr(pkt)->daddr; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci skb_dst_set(skb, dst_clone(skb_dst(pkt))); 11118c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci rcu_read_lock(); 11158c2ecf20Sopenharmony_ci mroute6_sk = rcu_dereference(mrt->mroute_sk); 11168c2ecf20Sopenharmony_ci if (!mroute6_sk) { 11178c2ecf20Sopenharmony_ci rcu_read_unlock(); 11188c2ecf20Sopenharmony_ci kfree_skb(skb); 11198c2ecf20Sopenharmony_ci return -EINVAL; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci mrt6msg_netlink_event(mrt, skb); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci /* Deliver to user space multicast routing algorithms */ 11258c2ecf20Sopenharmony_ci ret = sock_queue_rcv_skb(mroute6_sk, skb); 11268c2ecf20Sopenharmony_ci rcu_read_unlock(); 11278c2ecf20Sopenharmony_ci if (ret < 0) { 11288c2ecf20Sopenharmony_ci net_warn_ratelimited("mroute6: pending queue full, dropping entries\n"); 11298c2ecf20Sopenharmony_ci kfree_skb(skb); 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci return ret; 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci/* Queue a packet for resolution. It gets locked cache entry! */ 11368c2ecf20Sopenharmony_cistatic int ip6mr_cache_unresolved(struct mr_table *mrt, mifi_t mifi, 11378c2ecf20Sopenharmony_ci struct sk_buff *skb, struct net_device *dev) 11388c2ecf20Sopenharmony_ci{ 11398c2ecf20Sopenharmony_ci struct mfc6_cache *c; 11408c2ecf20Sopenharmony_ci bool found = false; 11418c2ecf20Sopenharmony_ci int err; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci spin_lock_bh(&mfc_unres_lock); 11448c2ecf20Sopenharmony_ci list_for_each_entry(c, &mrt->mfc_unres_queue, _c.list) { 11458c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&c->mf6c_mcastgrp, &ipv6_hdr(skb)->daddr) && 11468c2ecf20Sopenharmony_ci ipv6_addr_equal(&c->mf6c_origin, &ipv6_hdr(skb)->saddr)) { 11478c2ecf20Sopenharmony_ci found = true; 11488c2ecf20Sopenharmony_ci break; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if (!found) { 11538c2ecf20Sopenharmony_ci /* 11548c2ecf20Sopenharmony_ci * Create a new entry if allowable 11558c2ecf20Sopenharmony_ci */ 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci c = ip6mr_cache_alloc_unres(); 11588c2ecf20Sopenharmony_ci if (!c) { 11598c2ecf20Sopenharmony_ci spin_unlock_bh(&mfc_unres_lock); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci kfree_skb(skb); 11628c2ecf20Sopenharmony_ci return -ENOBUFS; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci /* Fill in the new cache entry */ 11668c2ecf20Sopenharmony_ci c->_c.mfc_parent = -1; 11678c2ecf20Sopenharmony_ci c->mf6c_origin = ipv6_hdr(skb)->saddr; 11688c2ecf20Sopenharmony_ci c->mf6c_mcastgrp = ipv6_hdr(skb)->daddr; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* 11718c2ecf20Sopenharmony_ci * Reflect first query at pim6sd 11728c2ecf20Sopenharmony_ci */ 11738c2ecf20Sopenharmony_ci err = ip6mr_cache_report(mrt, skb, mifi, MRT6MSG_NOCACHE); 11748c2ecf20Sopenharmony_ci if (err < 0) { 11758c2ecf20Sopenharmony_ci /* If the report failed throw the cache entry 11768c2ecf20Sopenharmony_ci out - Brad Parker 11778c2ecf20Sopenharmony_ci */ 11788c2ecf20Sopenharmony_ci spin_unlock_bh(&mfc_unres_lock); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci ip6mr_cache_free(c); 11818c2ecf20Sopenharmony_ci kfree_skb(skb); 11828c2ecf20Sopenharmony_ci return err; 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci atomic_inc(&mrt->cache_resolve_queue_len); 11868c2ecf20Sopenharmony_ci list_add(&c->_c.list, &mrt->mfc_unres_queue); 11878c2ecf20Sopenharmony_ci mr6_netlink_event(mrt, c, RTM_NEWROUTE); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci ipmr_do_expire_process(mrt); 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci /* See if we can append the packet */ 11938c2ecf20Sopenharmony_ci if (c->_c.mfc_un.unres.unresolved.qlen > 3) { 11948c2ecf20Sopenharmony_ci kfree_skb(skb); 11958c2ecf20Sopenharmony_ci err = -ENOBUFS; 11968c2ecf20Sopenharmony_ci } else { 11978c2ecf20Sopenharmony_ci if (dev) { 11988c2ecf20Sopenharmony_ci skb->dev = dev; 11998c2ecf20Sopenharmony_ci skb->skb_iif = dev->ifindex; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci skb_queue_tail(&c->_c.mfc_un.unres.unresolved, skb); 12028c2ecf20Sopenharmony_ci err = 0; 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci spin_unlock_bh(&mfc_unres_lock); 12068c2ecf20Sopenharmony_ci return err; 12078c2ecf20Sopenharmony_ci} 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci/* 12108c2ecf20Sopenharmony_ci * MFC6 cache manipulation by user space 12118c2ecf20Sopenharmony_ci */ 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cistatic int ip6mr_mfc_delete(struct mr_table *mrt, struct mf6cctl *mfc, 12148c2ecf20Sopenharmony_ci int parent) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci struct mfc6_cache *c; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci /* The entries are added/deleted only under RTNL */ 12198c2ecf20Sopenharmony_ci rcu_read_lock(); 12208c2ecf20Sopenharmony_ci c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr, 12218c2ecf20Sopenharmony_ci &mfc->mf6cc_mcastgrp.sin6_addr, parent); 12228c2ecf20Sopenharmony_ci rcu_read_unlock(); 12238c2ecf20Sopenharmony_ci if (!c) 12248c2ecf20Sopenharmony_ci return -ENOENT; 12258c2ecf20Sopenharmony_ci rhltable_remove(&mrt->mfc_hash, &c->_c.mnode, ip6mr_rht_params); 12268c2ecf20Sopenharmony_ci list_del_rcu(&c->_c.list); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net), 12298c2ecf20Sopenharmony_ci FIB_EVENT_ENTRY_DEL, c, mrt->id); 12308c2ecf20Sopenharmony_ci mr6_netlink_event(mrt, c, RTM_DELROUTE); 12318c2ecf20Sopenharmony_ci mr_cache_put(&c->_c); 12328c2ecf20Sopenharmony_ci return 0; 12338c2ecf20Sopenharmony_ci} 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic int ip6mr_device_event(struct notifier_block *this, 12368c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 12398c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 12408c2ecf20Sopenharmony_ci struct mr_table *mrt; 12418c2ecf20Sopenharmony_ci struct vif_device *v; 12428c2ecf20Sopenharmony_ci int ct; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci if (event != NETDEV_UNREGISTER) 12458c2ecf20Sopenharmony_ci return NOTIFY_DONE; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci ip6mr_for_each_table(mrt, net) { 12488c2ecf20Sopenharmony_ci v = &mrt->vif_table[0]; 12498c2ecf20Sopenharmony_ci for (ct = 0; ct < mrt->maxvif; ct++, v++) { 12508c2ecf20Sopenharmony_ci if (v->dev == dev) 12518c2ecf20Sopenharmony_ci mif6_delete(mrt, ct, 1, NULL); 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci return NOTIFY_DONE; 12568c2ecf20Sopenharmony_ci} 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_cistatic unsigned int ip6mr_seq_read(struct net *net) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci ASSERT_RTNL(); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci return net->ipv6.ipmr_seq + ip6mr_rules_seq_read(net); 12638c2ecf20Sopenharmony_ci} 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_cistatic int ip6mr_dump(struct net *net, struct notifier_block *nb, 12668c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 12678c2ecf20Sopenharmony_ci{ 12688c2ecf20Sopenharmony_ci return mr_dump(net, nb, RTNL_FAMILY_IP6MR, ip6mr_rules_dump, 12698c2ecf20Sopenharmony_ci ip6mr_mr_table_iter, &mrt_lock, extack); 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_cistatic struct notifier_block ip6_mr_notifier = { 12738c2ecf20Sopenharmony_ci .notifier_call = ip6mr_device_event 12748c2ecf20Sopenharmony_ci}; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_cistatic const struct fib_notifier_ops ip6mr_notifier_ops_template = { 12778c2ecf20Sopenharmony_ci .family = RTNL_FAMILY_IP6MR, 12788c2ecf20Sopenharmony_ci .fib_seq_read = ip6mr_seq_read, 12798c2ecf20Sopenharmony_ci .fib_dump = ip6mr_dump, 12808c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 12818c2ecf20Sopenharmony_ci}; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_cistatic int __net_init ip6mr_notifier_init(struct net *net) 12848c2ecf20Sopenharmony_ci{ 12858c2ecf20Sopenharmony_ci struct fib_notifier_ops *ops; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci net->ipv6.ipmr_seq = 0; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci ops = fib_notifier_ops_register(&ip6mr_notifier_ops_template, net); 12908c2ecf20Sopenharmony_ci if (IS_ERR(ops)) 12918c2ecf20Sopenharmony_ci return PTR_ERR(ops); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci net->ipv6.ip6mr_notifier_ops = ops; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci return 0; 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cistatic void __net_exit ip6mr_notifier_exit(struct net *net) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci fib_notifier_ops_unregister(net->ipv6.ip6mr_notifier_ops); 13018c2ecf20Sopenharmony_ci net->ipv6.ip6mr_notifier_ops = NULL; 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci/* Setup for IP multicast routing */ 13058c2ecf20Sopenharmony_cistatic int __net_init ip6mr_net_init(struct net *net) 13068c2ecf20Sopenharmony_ci{ 13078c2ecf20Sopenharmony_ci int err; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci err = ip6mr_notifier_init(net); 13108c2ecf20Sopenharmony_ci if (err) 13118c2ecf20Sopenharmony_ci return err; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci err = ip6mr_rules_init(net); 13148c2ecf20Sopenharmony_ci if (err < 0) 13158c2ecf20Sopenharmony_ci goto ip6mr_rules_fail; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 13188c2ecf20Sopenharmony_ci err = -ENOMEM; 13198c2ecf20Sopenharmony_ci if (!proc_create_net("ip6_mr_vif", 0, net->proc_net, &ip6mr_vif_seq_ops, 13208c2ecf20Sopenharmony_ci sizeof(struct mr_vif_iter))) 13218c2ecf20Sopenharmony_ci goto proc_vif_fail; 13228c2ecf20Sopenharmony_ci if (!proc_create_net("ip6_mr_cache", 0, net->proc_net, &ipmr_mfc_seq_ops, 13238c2ecf20Sopenharmony_ci sizeof(struct mr_mfc_iter))) 13248c2ecf20Sopenharmony_ci goto proc_cache_fail; 13258c2ecf20Sopenharmony_ci#endif 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci return 0; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 13308c2ecf20Sopenharmony_ciproc_cache_fail: 13318c2ecf20Sopenharmony_ci remove_proc_entry("ip6_mr_vif", net->proc_net); 13328c2ecf20Sopenharmony_ciproc_vif_fail: 13338c2ecf20Sopenharmony_ci ip6mr_rules_exit(net); 13348c2ecf20Sopenharmony_ci#endif 13358c2ecf20Sopenharmony_ciip6mr_rules_fail: 13368c2ecf20Sopenharmony_ci ip6mr_notifier_exit(net); 13378c2ecf20Sopenharmony_ci return err; 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic void __net_exit ip6mr_net_exit(struct net *net) 13418c2ecf20Sopenharmony_ci{ 13428c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 13438c2ecf20Sopenharmony_ci remove_proc_entry("ip6_mr_cache", net->proc_net); 13448c2ecf20Sopenharmony_ci remove_proc_entry("ip6_mr_vif", net->proc_net); 13458c2ecf20Sopenharmony_ci#endif 13468c2ecf20Sopenharmony_ci ip6mr_rules_exit(net); 13478c2ecf20Sopenharmony_ci ip6mr_notifier_exit(net); 13488c2ecf20Sopenharmony_ci} 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_cistatic struct pernet_operations ip6mr_net_ops = { 13518c2ecf20Sopenharmony_ci .init = ip6mr_net_init, 13528c2ecf20Sopenharmony_ci .exit = ip6mr_net_exit, 13538c2ecf20Sopenharmony_ci}; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ciint __init ip6_mr_init(void) 13568c2ecf20Sopenharmony_ci{ 13578c2ecf20Sopenharmony_ci int err; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci mrt_cachep = kmem_cache_create("ip6_mrt_cache", 13608c2ecf20Sopenharmony_ci sizeof(struct mfc6_cache), 13618c2ecf20Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN, 13628c2ecf20Sopenharmony_ci NULL); 13638c2ecf20Sopenharmony_ci if (!mrt_cachep) 13648c2ecf20Sopenharmony_ci return -ENOMEM; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci err = register_pernet_subsys(&ip6mr_net_ops); 13678c2ecf20Sopenharmony_ci if (err) 13688c2ecf20Sopenharmony_ci goto reg_pernet_fail; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci err = register_netdevice_notifier(&ip6_mr_notifier); 13718c2ecf20Sopenharmony_ci if (err) 13728c2ecf20Sopenharmony_ci goto reg_notif_fail; 13738c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2 13748c2ecf20Sopenharmony_ci if (inet6_add_protocol(&pim6_protocol, IPPROTO_PIM) < 0) { 13758c2ecf20Sopenharmony_ci pr_err("%s: can't add PIM protocol\n", __func__); 13768c2ecf20Sopenharmony_ci err = -EAGAIN; 13778c2ecf20Sopenharmony_ci goto add_proto_fail; 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci#endif 13808c2ecf20Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, RTNL_FAMILY_IP6MR, RTM_GETROUTE, 13818c2ecf20Sopenharmony_ci NULL, ip6mr_rtm_dumproute, 0); 13828c2ecf20Sopenharmony_ci if (err == 0) 13838c2ecf20Sopenharmony_ci return 0; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2 13868c2ecf20Sopenharmony_ci inet6_del_protocol(&pim6_protocol, IPPROTO_PIM); 13878c2ecf20Sopenharmony_ciadd_proto_fail: 13888c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&ip6_mr_notifier); 13898c2ecf20Sopenharmony_ci#endif 13908c2ecf20Sopenharmony_cireg_notif_fail: 13918c2ecf20Sopenharmony_ci unregister_pernet_subsys(&ip6mr_net_ops); 13928c2ecf20Sopenharmony_cireg_pernet_fail: 13938c2ecf20Sopenharmony_ci kmem_cache_destroy(mrt_cachep); 13948c2ecf20Sopenharmony_ci return err; 13958c2ecf20Sopenharmony_ci} 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_civoid ip6_mr_cleanup(void) 13988c2ecf20Sopenharmony_ci{ 13998c2ecf20Sopenharmony_ci rtnl_unregister(RTNL_FAMILY_IP6MR, RTM_GETROUTE); 14008c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2 14018c2ecf20Sopenharmony_ci inet6_del_protocol(&pim6_protocol, IPPROTO_PIM); 14028c2ecf20Sopenharmony_ci#endif 14038c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&ip6_mr_notifier); 14048c2ecf20Sopenharmony_ci unregister_pernet_subsys(&ip6mr_net_ops); 14058c2ecf20Sopenharmony_ci kmem_cache_destroy(mrt_cachep); 14068c2ecf20Sopenharmony_ci} 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_cistatic int ip6mr_mfc_add(struct net *net, struct mr_table *mrt, 14098c2ecf20Sopenharmony_ci struct mf6cctl *mfc, int mrtsock, int parent) 14108c2ecf20Sopenharmony_ci{ 14118c2ecf20Sopenharmony_ci unsigned char ttls[MAXMIFS]; 14128c2ecf20Sopenharmony_ci struct mfc6_cache *uc, *c; 14138c2ecf20Sopenharmony_ci struct mr_mfc *_uc; 14148c2ecf20Sopenharmony_ci bool found; 14158c2ecf20Sopenharmony_ci int i, err; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci if (mfc->mf6cc_parent >= MAXMIFS) 14188c2ecf20Sopenharmony_ci return -ENFILE; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci memset(ttls, 255, MAXMIFS); 14218c2ecf20Sopenharmony_ci for (i = 0; i < MAXMIFS; i++) { 14228c2ecf20Sopenharmony_ci if (IF_ISSET(i, &mfc->mf6cc_ifset)) 14238c2ecf20Sopenharmony_ci ttls[i] = 1; 14248c2ecf20Sopenharmony_ci } 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci /* The entries are added/deleted only under RTNL */ 14278c2ecf20Sopenharmony_ci rcu_read_lock(); 14288c2ecf20Sopenharmony_ci c = ip6mr_cache_find_parent(mrt, &mfc->mf6cc_origin.sin6_addr, 14298c2ecf20Sopenharmony_ci &mfc->mf6cc_mcastgrp.sin6_addr, parent); 14308c2ecf20Sopenharmony_ci rcu_read_unlock(); 14318c2ecf20Sopenharmony_ci if (c) { 14328c2ecf20Sopenharmony_ci write_lock_bh(&mrt_lock); 14338c2ecf20Sopenharmony_ci c->_c.mfc_parent = mfc->mf6cc_parent; 14348c2ecf20Sopenharmony_ci ip6mr_update_thresholds(mrt, &c->_c, ttls); 14358c2ecf20Sopenharmony_ci if (!mrtsock) 14368c2ecf20Sopenharmony_ci c->_c.mfc_flags |= MFC_STATIC; 14378c2ecf20Sopenharmony_ci write_unlock_bh(&mrt_lock); 14388c2ecf20Sopenharmony_ci call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, 14398c2ecf20Sopenharmony_ci c, mrt->id); 14408c2ecf20Sopenharmony_ci mr6_netlink_event(mrt, c, RTM_NEWROUTE); 14418c2ecf20Sopenharmony_ci return 0; 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&mfc->mf6cc_mcastgrp.sin6_addr) && 14458c2ecf20Sopenharmony_ci !ipv6_addr_is_multicast(&mfc->mf6cc_mcastgrp.sin6_addr)) 14468c2ecf20Sopenharmony_ci return -EINVAL; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci c = ip6mr_cache_alloc(); 14498c2ecf20Sopenharmony_ci if (!c) 14508c2ecf20Sopenharmony_ci return -ENOMEM; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci c->mf6c_origin = mfc->mf6cc_origin.sin6_addr; 14538c2ecf20Sopenharmony_ci c->mf6c_mcastgrp = mfc->mf6cc_mcastgrp.sin6_addr; 14548c2ecf20Sopenharmony_ci c->_c.mfc_parent = mfc->mf6cc_parent; 14558c2ecf20Sopenharmony_ci ip6mr_update_thresholds(mrt, &c->_c, ttls); 14568c2ecf20Sopenharmony_ci if (!mrtsock) 14578c2ecf20Sopenharmony_ci c->_c.mfc_flags |= MFC_STATIC; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci err = rhltable_insert_key(&mrt->mfc_hash, &c->cmparg, &c->_c.mnode, 14608c2ecf20Sopenharmony_ci ip6mr_rht_params); 14618c2ecf20Sopenharmony_ci if (err) { 14628c2ecf20Sopenharmony_ci pr_err("ip6mr: rhtable insert error %d\n", err); 14638c2ecf20Sopenharmony_ci ip6mr_cache_free(c); 14648c2ecf20Sopenharmony_ci return err; 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci list_add_tail_rcu(&c->_c.list, &mrt->mfc_cache_list); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci /* Check to see if we resolved a queued list. If so we 14698c2ecf20Sopenharmony_ci * need to send on the frames and tidy up. 14708c2ecf20Sopenharmony_ci */ 14718c2ecf20Sopenharmony_ci found = false; 14728c2ecf20Sopenharmony_ci spin_lock_bh(&mfc_unres_lock); 14738c2ecf20Sopenharmony_ci list_for_each_entry(_uc, &mrt->mfc_unres_queue, list) { 14748c2ecf20Sopenharmony_ci uc = (struct mfc6_cache *)_uc; 14758c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&uc->mf6c_origin, &c->mf6c_origin) && 14768c2ecf20Sopenharmony_ci ipv6_addr_equal(&uc->mf6c_mcastgrp, &c->mf6c_mcastgrp)) { 14778c2ecf20Sopenharmony_ci list_del(&_uc->list); 14788c2ecf20Sopenharmony_ci atomic_dec(&mrt->cache_resolve_queue_len); 14798c2ecf20Sopenharmony_ci found = true; 14808c2ecf20Sopenharmony_ci break; 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci if (list_empty(&mrt->mfc_unres_queue)) 14848c2ecf20Sopenharmony_ci del_timer(&mrt->ipmr_expire_timer); 14858c2ecf20Sopenharmony_ci spin_unlock_bh(&mfc_unres_lock); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci if (found) { 14888c2ecf20Sopenharmony_ci ip6mr_cache_resolve(net, mrt, uc, c); 14898c2ecf20Sopenharmony_ci ip6mr_cache_free(uc); 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci call_ip6mr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_ADD, 14928c2ecf20Sopenharmony_ci c, mrt->id); 14938c2ecf20Sopenharmony_ci mr6_netlink_event(mrt, c, RTM_NEWROUTE); 14948c2ecf20Sopenharmony_ci return 0; 14958c2ecf20Sopenharmony_ci} 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci/* 14988c2ecf20Sopenharmony_ci * Close the multicast socket, and clear the vif tables etc 14998c2ecf20Sopenharmony_ci */ 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_cistatic void mroute_clean_tables(struct mr_table *mrt, int flags) 15028c2ecf20Sopenharmony_ci{ 15038c2ecf20Sopenharmony_ci struct mr_mfc *c, *tmp; 15048c2ecf20Sopenharmony_ci LIST_HEAD(list); 15058c2ecf20Sopenharmony_ci int i; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci /* Shut down all active vif entries */ 15088c2ecf20Sopenharmony_ci if (flags & (MRT6_FLUSH_MIFS | MRT6_FLUSH_MIFS_STATIC)) { 15098c2ecf20Sopenharmony_ci for (i = 0; i < mrt->maxvif; i++) { 15108c2ecf20Sopenharmony_ci if (((mrt->vif_table[i].flags & VIFF_STATIC) && 15118c2ecf20Sopenharmony_ci !(flags & MRT6_FLUSH_MIFS_STATIC)) || 15128c2ecf20Sopenharmony_ci (!(mrt->vif_table[i].flags & VIFF_STATIC) && !(flags & MRT6_FLUSH_MIFS))) 15138c2ecf20Sopenharmony_ci continue; 15148c2ecf20Sopenharmony_ci mif6_delete(mrt, i, 0, &list); 15158c2ecf20Sopenharmony_ci } 15168c2ecf20Sopenharmony_ci unregister_netdevice_many(&list); 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci /* Wipe the cache */ 15208c2ecf20Sopenharmony_ci if (flags & (MRT6_FLUSH_MFC | MRT6_FLUSH_MFC_STATIC)) { 15218c2ecf20Sopenharmony_ci list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) { 15228c2ecf20Sopenharmony_ci if (((c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC_STATIC)) || 15238c2ecf20Sopenharmony_ci (!(c->mfc_flags & MFC_STATIC) && !(flags & MRT6_FLUSH_MFC))) 15248c2ecf20Sopenharmony_ci continue; 15258c2ecf20Sopenharmony_ci rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params); 15268c2ecf20Sopenharmony_ci list_del_rcu(&c->list); 15278c2ecf20Sopenharmony_ci call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net), 15288c2ecf20Sopenharmony_ci FIB_EVENT_ENTRY_DEL, 15298c2ecf20Sopenharmony_ci (struct mfc6_cache *)c, mrt->id); 15308c2ecf20Sopenharmony_ci mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE); 15318c2ecf20Sopenharmony_ci mr_cache_put(c); 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci if (flags & MRT6_FLUSH_MFC) { 15368c2ecf20Sopenharmony_ci if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { 15378c2ecf20Sopenharmony_ci spin_lock_bh(&mfc_unres_lock); 15388c2ecf20Sopenharmony_ci list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) { 15398c2ecf20Sopenharmony_ci list_del(&c->list); 15408c2ecf20Sopenharmony_ci mr6_netlink_event(mrt, (struct mfc6_cache *)c, 15418c2ecf20Sopenharmony_ci RTM_DELROUTE); 15428c2ecf20Sopenharmony_ci ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c); 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci spin_unlock_bh(&mfc_unres_lock); 15458c2ecf20Sopenharmony_ci } 15468c2ecf20Sopenharmony_ci } 15478c2ecf20Sopenharmony_ci} 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_cistatic int ip6mr_sk_init(struct mr_table *mrt, struct sock *sk) 15508c2ecf20Sopenharmony_ci{ 15518c2ecf20Sopenharmony_ci int err = 0; 15528c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci rtnl_lock(); 15558c2ecf20Sopenharmony_ci write_lock_bh(&mrt_lock); 15568c2ecf20Sopenharmony_ci if (rtnl_dereference(mrt->mroute_sk)) { 15578c2ecf20Sopenharmony_ci err = -EADDRINUSE; 15588c2ecf20Sopenharmony_ci } else { 15598c2ecf20Sopenharmony_ci rcu_assign_pointer(mrt->mroute_sk, sk); 15608c2ecf20Sopenharmony_ci sock_set_flag(sk, SOCK_RCU_FREE); 15618c2ecf20Sopenharmony_ci atomic_inc(&net->ipv6.devconf_all->mc_forwarding); 15628c2ecf20Sopenharmony_ci } 15638c2ecf20Sopenharmony_ci write_unlock_bh(&mrt_lock); 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci if (!err) 15668c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 15678c2ecf20Sopenharmony_ci NETCONFA_MC_FORWARDING, 15688c2ecf20Sopenharmony_ci NETCONFA_IFINDEX_ALL, 15698c2ecf20Sopenharmony_ci net->ipv6.devconf_all); 15708c2ecf20Sopenharmony_ci rtnl_unlock(); 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci return err; 15738c2ecf20Sopenharmony_ci} 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ciint ip6mr_sk_done(struct sock *sk) 15768c2ecf20Sopenharmony_ci{ 15778c2ecf20Sopenharmony_ci int err = -EACCES; 15788c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 15798c2ecf20Sopenharmony_ci struct mr_table *mrt; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (sk->sk_type != SOCK_RAW || 15828c2ecf20Sopenharmony_ci inet_sk(sk)->inet_num != IPPROTO_ICMPV6) 15838c2ecf20Sopenharmony_ci return err; 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci rtnl_lock(); 15868c2ecf20Sopenharmony_ci ip6mr_for_each_table(mrt, net) { 15878c2ecf20Sopenharmony_ci if (sk == rtnl_dereference(mrt->mroute_sk)) { 15888c2ecf20Sopenharmony_ci write_lock_bh(&mrt_lock); 15898c2ecf20Sopenharmony_ci RCU_INIT_POINTER(mrt->mroute_sk, NULL); 15908c2ecf20Sopenharmony_ci /* Note that mroute_sk had SOCK_RCU_FREE set, 15918c2ecf20Sopenharmony_ci * so the RCU grace period before sk freeing 15928c2ecf20Sopenharmony_ci * is guaranteed by sk_destruct() 15938c2ecf20Sopenharmony_ci */ 15948c2ecf20Sopenharmony_ci atomic_dec(&net->ipv6.devconf_all->mc_forwarding); 15958c2ecf20Sopenharmony_ci write_unlock_bh(&mrt_lock); 15968c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 15978c2ecf20Sopenharmony_ci NETCONFA_MC_FORWARDING, 15988c2ecf20Sopenharmony_ci NETCONFA_IFINDEX_ALL, 15998c2ecf20Sopenharmony_ci net->ipv6.devconf_all); 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci mroute_clean_tables(mrt, MRT6_FLUSH_MIFS | MRT6_FLUSH_MFC); 16028c2ecf20Sopenharmony_ci err = 0; 16038c2ecf20Sopenharmony_ci break; 16048c2ecf20Sopenharmony_ci } 16058c2ecf20Sopenharmony_ci } 16068c2ecf20Sopenharmony_ci rtnl_unlock(); 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci return err; 16098c2ecf20Sopenharmony_ci} 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_cibool mroute6_is_socket(struct net *net, struct sk_buff *skb) 16128c2ecf20Sopenharmony_ci{ 16138c2ecf20Sopenharmony_ci struct mr_table *mrt; 16148c2ecf20Sopenharmony_ci struct flowi6 fl6 = { 16158c2ecf20Sopenharmony_ci .flowi6_iif = skb->skb_iif ? : LOOPBACK_IFINDEX, 16168c2ecf20Sopenharmony_ci .flowi6_oif = skb->dev->ifindex, 16178c2ecf20Sopenharmony_ci .flowi6_mark = skb->mark, 16188c2ecf20Sopenharmony_ci }; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0) 16218c2ecf20Sopenharmony_ci return NULL; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci return rcu_access_pointer(mrt->mroute_sk); 16248c2ecf20Sopenharmony_ci} 16258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mroute6_is_socket); 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci/* 16288c2ecf20Sopenharmony_ci * Socket options and virtual interface manipulation. The whole 16298c2ecf20Sopenharmony_ci * virtual interface system is a complete heap, but unfortunately 16308c2ecf20Sopenharmony_ci * that's how BSD mrouted happens to think. Maybe one day with a proper 16318c2ecf20Sopenharmony_ci * MOSPF/PIM router set up we can clean this up. 16328c2ecf20Sopenharmony_ci */ 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ciint ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, 16358c2ecf20Sopenharmony_ci unsigned int optlen) 16368c2ecf20Sopenharmony_ci{ 16378c2ecf20Sopenharmony_ci int ret, parent = 0; 16388c2ecf20Sopenharmony_ci struct mif6ctl vif; 16398c2ecf20Sopenharmony_ci struct mf6cctl mfc; 16408c2ecf20Sopenharmony_ci mifi_t mifi; 16418c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 16428c2ecf20Sopenharmony_ci struct mr_table *mrt; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci if (sk->sk_type != SOCK_RAW || 16458c2ecf20Sopenharmony_ci inet_sk(sk)->inet_num != IPPROTO_ICMPV6) 16468c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT); 16498c2ecf20Sopenharmony_ci if (!mrt) 16508c2ecf20Sopenharmony_ci return -ENOENT; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci if (optname != MRT6_INIT) { 16538c2ecf20Sopenharmony_ci if (sk != rcu_access_pointer(mrt->mroute_sk) && 16548c2ecf20Sopenharmony_ci !ns_capable(net->user_ns, CAP_NET_ADMIN)) 16558c2ecf20Sopenharmony_ci return -EACCES; 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci switch (optname) { 16598c2ecf20Sopenharmony_ci case MRT6_INIT: 16608c2ecf20Sopenharmony_ci if (optlen < sizeof(int)) 16618c2ecf20Sopenharmony_ci return -EINVAL; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci return ip6mr_sk_init(mrt, sk); 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci case MRT6_DONE: 16668c2ecf20Sopenharmony_ci return ip6mr_sk_done(sk); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci case MRT6_ADD_MIF: 16698c2ecf20Sopenharmony_ci if (optlen < sizeof(vif)) 16708c2ecf20Sopenharmony_ci return -EINVAL; 16718c2ecf20Sopenharmony_ci if (copy_from_sockptr(&vif, optval, sizeof(vif))) 16728c2ecf20Sopenharmony_ci return -EFAULT; 16738c2ecf20Sopenharmony_ci if (vif.mif6c_mifi >= MAXMIFS) 16748c2ecf20Sopenharmony_ci return -ENFILE; 16758c2ecf20Sopenharmony_ci rtnl_lock(); 16768c2ecf20Sopenharmony_ci ret = mif6_add(net, mrt, &vif, 16778c2ecf20Sopenharmony_ci sk == rtnl_dereference(mrt->mroute_sk)); 16788c2ecf20Sopenharmony_ci rtnl_unlock(); 16798c2ecf20Sopenharmony_ci return ret; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci case MRT6_DEL_MIF: 16828c2ecf20Sopenharmony_ci if (optlen < sizeof(mifi_t)) 16838c2ecf20Sopenharmony_ci return -EINVAL; 16848c2ecf20Sopenharmony_ci if (copy_from_sockptr(&mifi, optval, sizeof(mifi_t))) 16858c2ecf20Sopenharmony_ci return -EFAULT; 16868c2ecf20Sopenharmony_ci rtnl_lock(); 16878c2ecf20Sopenharmony_ci ret = mif6_delete(mrt, mifi, 0, NULL); 16888c2ecf20Sopenharmony_ci rtnl_unlock(); 16898c2ecf20Sopenharmony_ci return ret; 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci /* 16928c2ecf20Sopenharmony_ci * Manipulate the forwarding caches. These live 16938c2ecf20Sopenharmony_ci * in a sort of kernel/user symbiosis. 16948c2ecf20Sopenharmony_ci */ 16958c2ecf20Sopenharmony_ci case MRT6_ADD_MFC: 16968c2ecf20Sopenharmony_ci case MRT6_DEL_MFC: 16978c2ecf20Sopenharmony_ci parent = -1; 16988c2ecf20Sopenharmony_ci fallthrough; 16998c2ecf20Sopenharmony_ci case MRT6_ADD_MFC_PROXY: 17008c2ecf20Sopenharmony_ci case MRT6_DEL_MFC_PROXY: 17018c2ecf20Sopenharmony_ci if (optlen < sizeof(mfc)) 17028c2ecf20Sopenharmony_ci return -EINVAL; 17038c2ecf20Sopenharmony_ci if (copy_from_sockptr(&mfc, optval, sizeof(mfc))) 17048c2ecf20Sopenharmony_ci return -EFAULT; 17058c2ecf20Sopenharmony_ci if (parent == 0) 17068c2ecf20Sopenharmony_ci parent = mfc.mf6cc_parent; 17078c2ecf20Sopenharmony_ci rtnl_lock(); 17088c2ecf20Sopenharmony_ci if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY) 17098c2ecf20Sopenharmony_ci ret = ip6mr_mfc_delete(mrt, &mfc, parent); 17108c2ecf20Sopenharmony_ci else 17118c2ecf20Sopenharmony_ci ret = ip6mr_mfc_add(net, mrt, &mfc, 17128c2ecf20Sopenharmony_ci sk == 17138c2ecf20Sopenharmony_ci rtnl_dereference(mrt->mroute_sk), 17148c2ecf20Sopenharmony_ci parent); 17158c2ecf20Sopenharmony_ci rtnl_unlock(); 17168c2ecf20Sopenharmony_ci return ret; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci case MRT6_FLUSH: 17198c2ecf20Sopenharmony_ci { 17208c2ecf20Sopenharmony_ci int flags; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci if (optlen != sizeof(flags)) 17238c2ecf20Sopenharmony_ci return -EINVAL; 17248c2ecf20Sopenharmony_ci if (copy_from_sockptr(&flags, optval, sizeof(flags))) 17258c2ecf20Sopenharmony_ci return -EFAULT; 17268c2ecf20Sopenharmony_ci rtnl_lock(); 17278c2ecf20Sopenharmony_ci mroute_clean_tables(mrt, flags); 17288c2ecf20Sopenharmony_ci rtnl_unlock(); 17298c2ecf20Sopenharmony_ci return 0; 17308c2ecf20Sopenharmony_ci } 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci /* 17338c2ecf20Sopenharmony_ci * Control PIM assert (to activate pim will activate assert) 17348c2ecf20Sopenharmony_ci */ 17358c2ecf20Sopenharmony_ci case MRT6_ASSERT: 17368c2ecf20Sopenharmony_ci { 17378c2ecf20Sopenharmony_ci int v; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci if (optlen != sizeof(v)) 17408c2ecf20Sopenharmony_ci return -EINVAL; 17418c2ecf20Sopenharmony_ci if (copy_from_sockptr(&v, optval, sizeof(v))) 17428c2ecf20Sopenharmony_ci return -EFAULT; 17438c2ecf20Sopenharmony_ci mrt->mroute_do_assert = v; 17448c2ecf20Sopenharmony_ci return 0; 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2 17488c2ecf20Sopenharmony_ci case MRT6_PIM: 17498c2ecf20Sopenharmony_ci { 17508c2ecf20Sopenharmony_ci int v; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci if (optlen != sizeof(v)) 17538c2ecf20Sopenharmony_ci return -EINVAL; 17548c2ecf20Sopenharmony_ci if (copy_from_sockptr(&v, optval, sizeof(v))) 17558c2ecf20Sopenharmony_ci return -EFAULT; 17568c2ecf20Sopenharmony_ci v = !!v; 17578c2ecf20Sopenharmony_ci rtnl_lock(); 17588c2ecf20Sopenharmony_ci ret = 0; 17598c2ecf20Sopenharmony_ci if (v != mrt->mroute_do_pim) { 17608c2ecf20Sopenharmony_ci mrt->mroute_do_pim = v; 17618c2ecf20Sopenharmony_ci mrt->mroute_do_assert = v; 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci rtnl_unlock(); 17648c2ecf20Sopenharmony_ci return ret; 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci#endif 17688c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES 17698c2ecf20Sopenharmony_ci case MRT6_TABLE: 17708c2ecf20Sopenharmony_ci { 17718c2ecf20Sopenharmony_ci u32 v; 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci if (optlen != sizeof(u32)) 17748c2ecf20Sopenharmony_ci return -EINVAL; 17758c2ecf20Sopenharmony_ci if (copy_from_sockptr(&v, optval, sizeof(v))) 17768c2ecf20Sopenharmony_ci return -EFAULT; 17778c2ecf20Sopenharmony_ci /* "pim6reg%u" should not exceed 16 bytes (IFNAMSIZ) */ 17788c2ecf20Sopenharmony_ci if (v != RT_TABLE_DEFAULT && v >= 100000000) 17798c2ecf20Sopenharmony_ci return -EINVAL; 17808c2ecf20Sopenharmony_ci if (sk == rcu_access_pointer(mrt->mroute_sk)) 17818c2ecf20Sopenharmony_ci return -EBUSY; 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci rtnl_lock(); 17848c2ecf20Sopenharmony_ci ret = 0; 17858c2ecf20Sopenharmony_ci mrt = ip6mr_new_table(net, v); 17868c2ecf20Sopenharmony_ci if (IS_ERR(mrt)) 17878c2ecf20Sopenharmony_ci ret = PTR_ERR(mrt); 17888c2ecf20Sopenharmony_ci else 17898c2ecf20Sopenharmony_ci raw6_sk(sk)->ip6mr_table = v; 17908c2ecf20Sopenharmony_ci rtnl_unlock(); 17918c2ecf20Sopenharmony_ci return ret; 17928c2ecf20Sopenharmony_ci } 17938c2ecf20Sopenharmony_ci#endif 17948c2ecf20Sopenharmony_ci /* 17958c2ecf20Sopenharmony_ci * Spurious command, or MRT6_VERSION which you cannot 17968c2ecf20Sopenharmony_ci * set. 17978c2ecf20Sopenharmony_ci */ 17988c2ecf20Sopenharmony_ci default: 17998c2ecf20Sopenharmony_ci return -ENOPROTOOPT; 18008c2ecf20Sopenharmony_ci } 18018c2ecf20Sopenharmony_ci} 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci/* 18048c2ecf20Sopenharmony_ci * Getsock opt support for the multicast routing system. 18058c2ecf20Sopenharmony_ci */ 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ciint ip6_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, 18088c2ecf20Sopenharmony_ci int __user *optlen) 18098c2ecf20Sopenharmony_ci{ 18108c2ecf20Sopenharmony_ci int olr; 18118c2ecf20Sopenharmony_ci int val; 18128c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 18138c2ecf20Sopenharmony_ci struct mr_table *mrt; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci if (sk->sk_type != SOCK_RAW || 18168c2ecf20Sopenharmony_ci inet_sk(sk)->inet_num != IPPROTO_ICMPV6) 18178c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT); 18208c2ecf20Sopenharmony_ci if (!mrt) 18218c2ecf20Sopenharmony_ci return -ENOENT; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci switch (optname) { 18248c2ecf20Sopenharmony_ci case MRT6_VERSION: 18258c2ecf20Sopenharmony_ci val = 0x0305; 18268c2ecf20Sopenharmony_ci break; 18278c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2 18288c2ecf20Sopenharmony_ci case MRT6_PIM: 18298c2ecf20Sopenharmony_ci val = mrt->mroute_do_pim; 18308c2ecf20Sopenharmony_ci break; 18318c2ecf20Sopenharmony_ci#endif 18328c2ecf20Sopenharmony_ci case MRT6_ASSERT: 18338c2ecf20Sopenharmony_ci val = mrt->mroute_do_assert; 18348c2ecf20Sopenharmony_ci break; 18358c2ecf20Sopenharmony_ci default: 18368c2ecf20Sopenharmony_ci return -ENOPROTOOPT; 18378c2ecf20Sopenharmony_ci } 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci if (get_user(olr, optlen)) 18408c2ecf20Sopenharmony_ci return -EFAULT; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci olr = min_t(int, olr, sizeof(int)); 18438c2ecf20Sopenharmony_ci if (olr < 0) 18448c2ecf20Sopenharmony_ci return -EINVAL; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci if (put_user(olr, optlen)) 18478c2ecf20Sopenharmony_ci return -EFAULT; 18488c2ecf20Sopenharmony_ci if (copy_to_user(optval, &val, olr)) 18498c2ecf20Sopenharmony_ci return -EFAULT; 18508c2ecf20Sopenharmony_ci return 0; 18518c2ecf20Sopenharmony_ci} 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci/* 18548c2ecf20Sopenharmony_ci * The IP multicast ioctl support routines. 18558c2ecf20Sopenharmony_ci */ 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ciint ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg) 18588c2ecf20Sopenharmony_ci{ 18598c2ecf20Sopenharmony_ci struct sioc_sg_req6 sr; 18608c2ecf20Sopenharmony_ci struct sioc_mif_req6 vr; 18618c2ecf20Sopenharmony_ci struct vif_device *vif; 18628c2ecf20Sopenharmony_ci struct mfc6_cache *c; 18638c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 18648c2ecf20Sopenharmony_ci struct mr_table *mrt; 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT); 18678c2ecf20Sopenharmony_ci if (!mrt) 18688c2ecf20Sopenharmony_ci return -ENOENT; 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci switch (cmd) { 18718c2ecf20Sopenharmony_ci case SIOCGETMIFCNT_IN6: 18728c2ecf20Sopenharmony_ci if (copy_from_user(&vr, arg, sizeof(vr))) 18738c2ecf20Sopenharmony_ci return -EFAULT; 18748c2ecf20Sopenharmony_ci if (vr.mifi >= mrt->maxvif) 18758c2ecf20Sopenharmony_ci return -EINVAL; 18768c2ecf20Sopenharmony_ci vr.mifi = array_index_nospec(vr.mifi, mrt->maxvif); 18778c2ecf20Sopenharmony_ci read_lock(&mrt_lock); 18788c2ecf20Sopenharmony_ci vif = &mrt->vif_table[vr.mifi]; 18798c2ecf20Sopenharmony_ci if (VIF_EXISTS(mrt, vr.mifi)) { 18808c2ecf20Sopenharmony_ci vr.icount = vif->pkt_in; 18818c2ecf20Sopenharmony_ci vr.ocount = vif->pkt_out; 18828c2ecf20Sopenharmony_ci vr.ibytes = vif->bytes_in; 18838c2ecf20Sopenharmony_ci vr.obytes = vif->bytes_out; 18848c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci if (copy_to_user(arg, &vr, sizeof(vr))) 18878c2ecf20Sopenharmony_ci return -EFAULT; 18888c2ecf20Sopenharmony_ci return 0; 18898c2ecf20Sopenharmony_ci } 18908c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 18918c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 18928c2ecf20Sopenharmony_ci case SIOCGETSGCNT_IN6: 18938c2ecf20Sopenharmony_ci if (copy_from_user(&sr, arg, sizeof(sr))) 18948c2ecf20Sopenharmony_ci return -EFAULT; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci rcu_read_lock(); 18978c2ecf20Sopenharmony_ci c = ip6mr_cache_find(mrt, &sr.src.sin6_addr, &sr.grp.sin6_addr); 18988c2ecf20Sopenharmony_ci if (c) { 18998c2ecf20Sopenharmony_ci sr.pktcnt = c->_c.mfc_un.res.pkt; 19008c2ecf20Sopenharmony_ci sr.bytecnt = c->_c.mfc_un.res.bytes; 19018c2ecf20Sopenharmony_ci sr.wrong_if = c->_c.mfc_un.res.wrong_if; 19028c2ecf20Sopenharmony_ci rcu_read_unlock(); 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci if (copy_to_user(arg, &sr, sizeof(sr))) 19058c2ecf20Sopenharmony_ci return -EFAULT; 19068c2ecf20Sopenharmony_ci return 0; 19078c2ecf20Sopenharmony_ci } 19088c2ecf20Sopenharmony_ci rcu_read_unlock(); 19098c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 19108c2ecf20Sopenharmony_ci default: 19118c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 19128c2ecf20Sopenharmony_ci } 19138c2ecf20Sopenharmony_ci} 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 19168c2ecf20Sopenharmony_cistruct compat_sioc_sg_req6 { 19178c2ecf20Sopenharmony_ci struct sockaddr_in6 src; 19188c2ecf20Sopenharmony_ci struct sockaddr_in6 grp; 19198c2ecf20Sopenharmony_ci compat_ulong_t pktcnt; 19208c2ecf20Sopenharmony_ci compat_ulong_t bytecnt; 19218c2ecf20Sopenharmony_ci compat_ulong_t wrong_if; 19228c2ecf20Sopenharmony_ci}; 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_cistruct compat_sioc_mif_req6 { 19258c2ecf20Sopenharmony_ci mifi_t mifi; 19268c2ecf20Sopenharmony_ci compat_ulong_t icount; 19278c2ecf20Sopenharmony_ci compat_ulong_t ocount; 19288c2ecf20Sopenharmony_ci compat_ulong_t ibytes; 19298c2ecf20Sopenharmony_ci compat_ulong_t obytes; 19308c2ecf20Sopenharmony_ci}; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ciint ip6mr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) 19338c2ecf20Sopenharmony_ci{ 19348c2ecf20Sopenharmony_ci struct compat_sioc_sg_req6 sr; 19358c2ecf20Sopenharmony_ci struct compat_sioc_mif_req6 vr; 19368c2ecf20Sopenharmony_ci struct vif_device *vif; 19378c2ecf20Sopenharmony_ci struct mfc6_cache *c; 19388c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 19398c2ecf20Sopenharmony_ci struct mr_table *mrt; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT); 19428c2ecf20Sopenharmony_ci if (!mrt) 19438c2ecf20Sopenharmony_ci return -ENOENT; 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci switch (cmd) { 19468c2ecf20Sopenharmony_ci case SIOCGETMIFCNT_IN6: 19478c2ecf20Sopenharmony_ci if (copy_from_user(&vr, arg, sizeof(vr))) 19488c2ecf20Sopenharmony_ci return -EFAULT; 19498c2ecf20Sopenharmony_ci if (vr.mifi >= mrt->maxvif) 19508c2ecf20Sopenharmony_ci return -EINVAL; 19518c2ecf20Sopenharmony_ci vr.mifi = array_index_nospec(vr.mifi, mrt->maxvif); 19528c2ecf20Sopenharmony_ci read_lock(&mrt_lock); 19538c2ecf20Sopenharmony_ci vif = &mrt->vif_table[vr.mifi]; 19548c2ecf20Sopenharmony_ci if (VIF_EXISTS(mrt, vr.mifi)) { 19558c2ecf20Sopenharmony_ci vr.icount = vif->pkt_in; 19568c2ecf20Sopenharmony_ci vr.ocount = vif->pkt_out; 19578c2ecf20Sopenharmony_ci vr.ibytes = vif->bytes_in; 19588c2ecf20Sopenharmony_ci vr.obytes = vif->bytes_out; 19598c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci if (copy_to_user(arg, &vr, sizeof(vr))) 19628c2ecf20Sopenharmony_ci return -EFAULT; 19638c2ecf20Sopenharmony_ci return 0; 19648c2ecf20Sopenharmony_ci } 19658c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 19668c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 19678c2ecf20Sopenharmony_ci case SIOCGETSGCNT_IN6: 19688c2ecf20Sopenharmony_ci if (copy_from_user(&sr, arg, sizeof(sr))) 19698c2ecf20Sopenharmony_ci return -EFAULT; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci rcu_read_lock(); 19728c2ecf20Sopenharmony_ci c = ip6mr_cache_find(mrt, &sr.src.sin6_addr, &sr.grp.sin6_addr); 19738c2ecf20Sopenharmony_ci if (c) { 19748c2ecf20Sopenharmony_ci sr.pktcnt = c->_c.mfc_un.res.pkt; 19758c2ecf20Sopenharmony_ci sr.bytecnt = c->_c.mfc_un.res.bytes; 19768c2ecf20Sopenharmony_ci sr.wrong_if = c->_c.mfc_un.res.wrong_if; 19778c2ecf20Sopenharmony_ci rcu_read_unlock(); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci if (copy_to_user(arg, &sr, sizeof(sr))) 19808c2ecf20Sopenharmony_ci return -EFAULT; 19818c2ecf20Sopenharmony_ci return 0; 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci rcu_read_unlock(); 19848c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 19858c2ecf20Sopenharmony_ci default: 19868c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 19878c2ecf20Sopenharmony_ci } 19888c2ecf20Sopenharmony_ci} 19898c2ecf20Sopenharmony_ci#endif 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_cistatic inline int ip6mr_forward2_finish(struct net *net, struct sock *sk, struct sk_buff *skb) 19928c2ecf20Sopenharmony_ci{ 19938c2ecf20Sopenharmony_ci IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), 19948c2ecf20Sopenharmony_ci IPSTATS_MIB_OUTFORWDATAGRAMS); 19958c2ecf20Sopenharmony_ci IP6_ADD_STATS(net, ip6_dst_idev(skb_dst(skb)), 19968c2ecf20Sopenharmony_ci IPSTATS_MIB_OUTOCTETS, skb->len); 19978c2ecf20Sopenharmony_ci return dst_output(net, sk, skb); 19988c2ecf20Sopenharmony_ci} 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci/* 20018c2ecf20Sopenharmony_ci * Processing handlers for ip6mr_forward 20028c2ecf20Sopenharmony_ci */ 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_cistatic int ip6mr_forward2(struct net *net, struct mr_table *mrt, 20058c2ecf20Sopenharmony_ci struct sk_buff *skb, int vifi) 20068c2ecf20Sopenharmony_ci{ 20078c2ecf20Sopenharmony_ci struct ipv6hdr *ipv6h; 20088c2ecf20Sopenharmony_ci struct vif_device *vif = &mrt->vif_table[vifi]; 20098c2ecf20Sopenharmony_ci struct net_device *dev; 20108c2ecf20Sopenharmony_ci struct dst_entry *dst; 20118c2ecf20Sopenharmony_ci struct flowi6 fl6; 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci if (!vif->dev) 20148c2ecf20Sopenharmony_ci goto out_free; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_PIMSM_V2 20178c2ecf20Sopenharmony_ci if (vif->flags & MIFF_REGISTER) { 20188c2ecf20Sopenharmony_ci vif->pkt_out++; 20198c2ecf20Sopenharmony_ci vif->bytes_out += skb->len; 20208c2ecf20Sopenharmony_ci vif->dev->stats.tx_bytes += skb->len; 20218c2ecf20Sopenharmony_ci vif->dev->stats.tx_packets++; 20228c2ecf20Sopenharmony_ci ip6mr_cache_report(mrt, skb, vifi, MRT6MSG_WHOLEPKT); 20238c2ecf20Sopenharmony_ci goto out_free; 20248c2ecf20Sopenharmony_ci } 20258c2ecf20Sopenharmony_ci#endif 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci ipv6h = ipv6_hdr(skb); 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci fl6 = (struct flowi6) { 20308c2ecf20Sopenharmony_ci .flowi6_oif = vif->link, 20318c2ecf20Sopenharmony_ci .daddr = ipv6h->daddr, 20328c2ecf20Sopenharmony_ci }; 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci dst = ip6_route_output(net, NULL, &fl6); 20358c2ecf20Sopenharmony_ci if (dst->error) { 20368c2ecf20Sopenharmony_ci dst_release(dst); 20378c2ecf20Sopenharmony_ci goto out_free; 20388c2ecf20Sopenharmony_ci } 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci skb_dst_drop(skb); 20418c2ecf20Sopenharmony_ci skb_dst_set(skb, dst); 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci /* 20448c2ecf20Sopenharmony_ci * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally 20458c2ecf20Sopenharmony_ci * not only before forwarding, but after forwarding on all output 20468c2ecf20Sopenharmony_ci * interfaces. It is clear, if mrouter runs a multicasting 20478c2ecf20Sopenharmony_ci * program, it should receive packets not depending to what interface 20488c2ecf20Sopenharmony_ci * program is joined. 20498c2ecf20Sopenharmony_ci * If we will not make it, the program will have to join on all 20508c2ecf20Sopenharmony_ci * interfaces. On the other hand, multihoming host (or router, but 20518c2ecf20Sopenharmony_ci * not mrouter) cannot join to more than one interface - it will 20528c2ecf20Sopenharmony_ci * result in receiving multiple packets. 20538c2ecf20Sopenharmony_ci */ 20548c2ecf20Sopenharmony_ci dev = vif->dev; 20558c2ecf20Sopenharmony_ci skb->dev = dev; 20568c2ecf20Sopenharmony_ci vif->pkt_out++; 20578c2ecf20Sopenharmony_ci vif->bytes_out += skb->len; 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci /* We are about to write */ 20608c2ecf20Sopenharmony_ci /* XXX: extension headers? */ 20618c2ecf20Sopenharmony_ci if (skb_cow(skb, sizeof(*ipv6h) + LL_RESERVED_SPACE(dev))) 20628c2ecf20Sopenharmony_ci goto out_free; 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci ipv6h = ipv6_hdr(skb); 20658c2ecf20Sopenharmony_ci ipv6h->hop_limit--; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci IP6CB(skb)->flags |= IP6SKB_FORWARDED; 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci return NF_HOOK(NFPROTO_IPV6, NF_INET_FORWARD, 20708c2ecf20Sopenharmony_ci net, NULL, skb, skb->dev, dev, 20718c2ecf20Sopenharmony_ci ip6mr_forward2_finish); 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ciout_free: 20748c2ecf20Sopenharmony_ci kfree_skb(skb); 20758c2ecf20Sopenharmony_ci return 0; 20768c2ecf20Sopenharmony_ci} 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_cistatic int ip6mr_find_vif(struct mr_table *mrt, struct net_device *dev) 20798c2ecf20Sopenharmony_ci{ 20808c2ecf20Sopenharmony_ci int ct; 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci for (ct = mrt->maxvif - 1; ct >= 0; ct--) { 20838c2ecf20Sopenharmony_ci if (mrt->vif_table[ct].dev == dev) 20848c2ecf20Sopenharmony_ci break; 20858c2ecf20Sopenharmony_ci } 20868c2ecf20Sopenharmony_ci return ct; 20878c2ecf20Sopenharmony_ci} 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_cistatic void ip6_mr_forward(struct net *net, struct mr_table *mrt, 20908c2ecf20Sopenharmony_ci struct net_device *dev, struct sk_buff *skb, 20918c2ecf20Sopenharmony_ci struct mfc6_cache *c) 20928c2ecf20Sopenharmony_ci{ 20938c2ecf20Sopenharmony_ci int psend = -1; 20948c2ecf20Sopenharmony_ci int vif, ct; 20958c2ecf20Sopenharmony_ci int true_vifi = ip6mr_find_vif(mrt, dev); 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci vif = c->_c.mfc_parent; 20988c2ecf20Sopenharmony_ci c->_c.mfc_un.res.pkt++; 20998c2ecf20Sopenharmony_ci c->_c.mfc_un.res.bytes += skb->len; 21008c2ecf20Sopenharmony_ci c->_c.mfc_un.res.lastuse = jiffies; 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci if (ipv6_addr_any(&c->mf6c_origin) && true_vifi >= 0) { 21038c2ecf20Sopenharmony_ci struct mfc6_cache *cache_proxy; 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci /* For an (*,G) entry, we only check that the incoming 21068c2ecf20Sopenharmony_ci * interface is part of the static tree. 21078c2ecf20Sopenharmony_ci */ 21088c2ecf20Sopenharmony_ci rcu_read_lock(); 21098c2ecf20Sopenharmony_ci cache_proxy = mr_mfc_find_any_parent(mrt, vif); 21108c2ecf20Sopenharmony_ci if (cache_proxy && 21118c2ecf20Sopenharmony_ci cache_proxy->_c.mfc_un.res.ttls[true_vifi] < 255) { 21128c2ecf20Sopenharmony_ci rcu_read_unlock(); 21138c2ecf20Sopenharmony_ci goto forward; 21148c2ecf20Sopenharmony_ci } 21158c2ecf20Sopenharmony_ci rcu_read_unlock(); 21168c2ecf20Sopenharmony_ci } 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci /* 21198c2ecf20Sopenharmony_ci * Wrong interface: drop packet and (maybe) send PIM assert. 21208c2ecf20Sopenharmony_ci */ 21218c2ecf20Sopenharmony_ci if (mrt->vif_table[vif].dev != dev) { 21228c2ecf20Sopenharmony_ci c->_c.mfc_un.res.wrong_if++; 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci if (true_vifi >= 0 && mrt->mroute_do_assert && 21258c2ecf20Sopenharmony_ci /* pimsm uses asserts, when switching from RPT to SPT, 21268c2ecf20Sopenharmony_ci so that we cannot check that packet arrived on an oif. 21278c2ecf20Sopenharmony_ci It is bad, but otherwise we would need to move pretty 21288c2ecf20Sopenharmony_ci large chunk of pimd to kernel. Ough... --ANK 21298c2ecf20Sopenharmony_ci */ 21308c2ecf20Sopenharmony_ci (mrt->mroute_do_pim || 21318c2ecf20Sopenharmony_ci c->_c.mfc_un.res.ttls[true_vifi] < 255) && 21328c2ecf20Sopenharmony_ci time_after(jiffies, 21338c2ecf20Sopenharmony_ci c->_c.mfc_un.res.last_assert + 21348c2ecf20Sopenharmony_ci MFC_ASSERT_THRESH)) { 21358c2ecf20Sopenharmony_ci c->_c.mfc_un.res.last_assert = jiffies; 21368c2ecf20Sopenharmony_ci ip6mr_cache_report(mrt, skb, true_vifi, MRT6MSG_WRONGMIF); 21378c2ecf20Sopenharmony_ci } 21388c2ecf20Sopenharmony_ci goto dont_forward; 21398c2ecf20Sopenharmony_ci } 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ciforward: 21428c2ecf20Sopenharmony_ci mrt->vif_table[vif].pkt_in++; 21438c2ecf20Sopenharmony_ci mrt->vif_table[vif].bytes_in += skb->len; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci /* 21468c2ecf20Sopenharmony_ci * Forward the frame 21478c2ecf20Sopenharmony_ci */ 21488c2ecf20Sopenharmony_ci if (ipv6_addr_any(&c->mf6c_origin) && 21498c2ecf20Sopenharmony_ci ipv6_addr_any(&c->mf6c_mcastgrp)) { 21508c2ecf20Sopenharmony_ci if (true_vifi >= 0 && 21518c2ecf20Sopenharmony_ci true_vifi != c->_c.mfc_parent && 21528c2ecf20Sopenharmony_ci ipv6_hdr(skb)->hop_limit > 21538c2ecf20Sopenharmony_ci c->_c.mfc_un.res.ttls[c->_c.mfc_parent]) { 21548c2ecf20Sopenharmony_ci /* It's an (*,*) entry and the packet is not coming from 21558c2ecf20Sopenharmony_ci * the upstream: forward the packet to the upstream 21568c2ecf20Sopenharmony_ci * only. 21578c2ecf20Sopenharmony_ci */ 21588c2ecf20Sopenharmony_ci psend = c->_c.mfc_parent; 21598c2ecf20Sopenharmony_ci goto last_forward; 21608c2ecf20Sopenharmony_ci } 21618c2ecf20Sopenharmony_ci goto dont_forward; 21628c2ecf20Sopenharmony_ci } 21638c2ecf20Sopenharmony_ci for (ct = c->_c.mfc_un.res.maxvif - 1; 21648c2ecf20Sopenharmony_ci ct >= c->_c.mfc_un.res.minvif; ct--) { 21658c2ecf20Sopenharmony_ci /* For (*,G) entry, don't forward to the incoming interface */ 21668c2ecf20Sopenharmony_ci if ((!ipv6_addr_any(&c->mf6c_origin) || ct != true_vifi) && 21678c2ecf20Sopenharmony_ci ipv6_hdr(skb)->hop_limit > c->_c.mfc_un.res.ttls[ct]) { 21688c2ecf20Sopenharmony_ci if (psend != -1) { 21698c2ecf20Sopenharmony_ci struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); 21708c2ecf20Sopenharmony_ci if (skb2) 21718c2ecf20Sopenharmony_ci ip6mr_forward2(net, mrt, skb2, psend); 21728c2ecf20Sopenharmony_ci } 21738c2ecf20Sopenharmony_ci psend = ct; 21748c2ecf20Sopenharmony_ci } 21758c2ecf20Sopenharmony_ci } 21768c2ecf20Sopenharmony_cilast_forward: 21778c2ecf20Sopenharmony_ci if (psend != -1) { 21788c2ecf20Sopenharmony_ci ip6mr_forward2(net, mrt, skb, psend); 21798c2ecf20Sopenharmony_ci return; 21808c2ecf20Sopenharmony_ci } 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_cidont_forward: 21838c2ecf20Sopenharmony_ci kfree_skb(skb); 21848c2ecf20Sopenharmony_ci} 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci/* 21888c2ecf20Sopenharmony_ci * Multicast packets for forwarding arrive here 21898c2ecf20Sopenharmony_ci */ 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ciint ip6_mr_input(struct sk_buff *skb) 21928c2ecf20Sopenharmony_ci{ 21938c2ecf20Sopenharmony_ci struct mfc6_cache *cache; 21948c2ecf20Sopenharmony_ci struct net *net = dev_net(skb->dev); 21958c2ecf20Sopenharmony_ci struct mr_table *mrt; 21968c2ecf20Sopenharmony_ci struct flowi6 fl6 = { 21978c2ecf20Sopenharmony_ci .flowi6_iif = skb->dev->ifindex, 21988c2ecf20Sopenharmony_ci .flowi6_mark = skb->mark, 21998c2ecf20Sopenharmony_ci }; 22008c2ecf20Sopenharmony_ci int err; 22018c2ecf20Sopenharmony_ci struct net_device *dev; 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci /* skb->dev passed in is the master dev for vrfs. 22048c2ecf20Sopenharmony_ci * Get the proper interface that does have a vif associated with it. 22058c2ecf20Sopenharmony_ci */ 22068c2ecf20Sopenharmony_ci dev = skb->dev; 22078c2ecf20Sopenharmony_ci if (netif_is_l3_master(skb->dev)) { 22088c2ecf20Sopenharmony_ci dev = dev_get_by_index_rcu(net, IPCB(skb)->iif); 22098c2ecf20Sopenharmony_ci if (!dev) { 22108c2ecf20Sopenharmony_ci kfree_skb(skb); 22118c2ecf20Sopenharmony_ci return -ENODEV; 22128c2ecf20Sopenharmony_ci } 22138c2ecf20Sopenharmony_ci } 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci err = ip6mr_fib_lookup(net, &fl6, &mrt); 22168c2ecf20Sopenharmony_ci if (err < 0) { 22178c2ecf20Sopenharmony_ci kfree_skb(skb); 22188c2ecf20Sopenharmony_ci return err; 22198c2ecf20Sopenharmony_ci } 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci read_lock(&mrt_lock); 22228c2ecf20Sopenharmony_ci cache = ip6mr_cache_find(mrt, 22238c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr); 22248c2ecf20Sopenharmony_ci if (!cache) { 22258c2ecf20Sopenharmony_ci int vif = ip6mr_find_vif(mrt, dev); 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci if (vif >= 0) 22288c2ecf20Sopenharmony_ci cache = ip6mr_cache_find_any(mrt, 22298c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->daddr, 22308c2ecf20Sopenharmony_ci vif); 22318c2ecf20Sopenharmony_ci } 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci /* 22348c2ecf20Sopenharmony_ci * No usable cache entry 22358c2ecf20Sopenharmony_ci */ 22368c2ecf20Sopenharmony_ci if (!cache) { 22378c2ecf20Sopenharmony_ci int vif; 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci vif = ip6mr_find_vif(mrt, dev); 22408c2ecf20Sopenharmony_ci if (vif >= 0) { 22418c2ecf20Sopenharmony_ci int err = ip6mr_cache_unresolved(mrt, vif, skb, dev); 22428c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci return err; 22458c2ecf20Sopenharmony_ci } 22468c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 22478c2ecf20Sopenharmony_ci kfree_skb(skb); 22488c2ecf20Sopenharmony_ci return -ENODEV; 22498c2ecf20Sopenharmony_ci } 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci ip6_mr_forward(net, mrt, dev, skb, cache); 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci return 0; 22568c2ecf20Sopenharmony_ci} 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ciint ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm, 22598c2ecf20Sopenharmony_ci u32 portid) 22608c2ecf20Sopenharmony_ci{ 22618c2ecf20Sopenharmony_ci int err; 22628c2ecf20Sopenharmony_ci struct mr_table *mrt; 22638c2ecf20Sopenharmony_ci struct mfc6_cache *cache; 22648c2ecf20Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci mrt = ip6mr_get_table(net, RT6_TABLE_DFLT); 22678c2ecf20Sopenharmony_ci if (!mrt) 22688c2ecf20Sopenharmony_ci return -ENOENT; 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci read_lock(&mrt_lock); 22718c2ecf20Sopenharmony_ci cache = ip6mr_cache_find(mrt, &rt->rt6i_src.addr, &rt->rt6i_dst.addr); 22728c2ecf20Sopenharmony_ci if (!cache && skb->dev) { 22738c2ecf20Sopenharmony_ci int vif = ip6mr_find_vif(mrt, skb->dev); 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci if (vif >= 0) 22768c2ecf20Sopenharmony_ci cache = ip6mr_cache_find_any(mrt, &rt->rt6i_dst.addr, 22778c2ecf20Sopenharmony_ci vif); 22788c2ecf20Sopenharmony_ci } 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci if (!cache) { 22818c2ecf20Sopenharmony_ci struct sk_buff *skb2; 22828c2ecf20Sopenharmony_ci struct ipv6hdr *iph; 22838c2ecf20Sopenharmony_ci struct net_device *dev; 22848c2ecf20Sopenharmony_ci int vif; 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci dev = skb->dev; 22878c2ecf20Sopenharmony_ci if (!dev || (vif = ip6mr_find_vif(mrt, dev)) < 0) { 22888c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 22898c2ecf20Sopenharmony_ci return -ENODEV; 22908c2ecf20Sopenharmony_ci } 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci /* really correct? */ 22938c2ecf20Sopenharmony_ci skb2 = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC); 22948c2ecf20Sopenharmony_ci if (!skb2) { 22958c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 22968c2ecf20Sopenharmony_ci return -ENOMEM; 22978c2ecf20Sopenharmony_ci } 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci NETLINK_CB(skb2).portid = portid; 23008c2ecf20Sopenharmony_ci skb_reset_transport_header(skb2); 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci skb_put(skb2, sizeof(struct ipv6hdr)); 23038c2ecf20Sopenharmony_ci skb_reset_network_header(skb2); 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci iph = ipv6_hdr(skb2); 23068c2ecf20Sopenharmony_ci iph->version = 0; 23078c2ecf20Sopenharmony_ci iph->priority = 0; 23088c2ecf20Sopenharmony_ci iph->flow_lbl[0] = 0; 23098c2ecf20Sopenharmony_ci iph->flow_lbl[1] = 0; 23108c2ecf20Sopenharmony_ci iph->flow_lbl[2] = 0; 23118c2ecf20Sopenharmony_ci iph->payload_len = 0; 23128c2ecf20Sopenharmony_ci iph->nexthdr = IPPROTO_NONE; 23138c2ecf20Sopenharmony_ci iph->hop_limit = 0; 23148c2ecf20Sopenharmony_ci iph->saddr = rt->rt6i_src.addr; 23158c2ecf20Sopenharmony_ci iph->daddr = rt->rt6i_dst.addr; 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci err = ip6mr_cache_unresolved(mrt, vif, skb2, dev); 23188c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci return err; 23218c2ecf20Sopenharmony_ci } 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci err = mr_fill_mroute(mrt, skb, &cache->_c, rtm); 23248c2ecf20Sopenharmony_ci read_unlock(&mrt_lock); 23258c2ecf20Sopenharmony_ci return err; 23268c2ecf20Sopenharmony_ci} 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_cistatic int ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 23298c2ecf20Sopenharmony_ci u32 portid, u32 seq, struct mfc6_cache *c, int cmd, 23308c2ecf20Sopenharmony_ci int flags) 23318c2ecf20Sopenharmony_ci{ 23328c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 23338c2ecf20Sopenharmony_ci struct rtmsg *rtm; 23348c2ecf20Sopenharmony_ci int err; 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), flags); 23378c2ecf20Sopenharmony_ci if (!nlh) 23388c2ecf20Sopenharmony_ci return -EMSGSIZE; 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci rtm = nlmsg_data(nlh); 23418c2ecf20Sopenharmony_ci rtm->rtm_family = RTNL_FAMILY_IP6MR; 23428c2ecf20Sopenharmony_ci rtm->rtm_dst_len = 128; 23438c2ecf20Sopenharmony_ci rtm->rtm_src_len = 128; 23448c2ecf20Sopenharmony_ci rtm->rtm_tos = 0; 23458c2ecf20Sopenharmony_ci rtm->rtm_table = mrt->id; 23468c2ecf20Sopenharmony_ci if (nla_put_u32(skb, RTA_TABLE, mrt->id)) 23478c2ecf20Sopenharmony_ci goto nla_put_failure; 23488c2ecf20Sopenharmony_ci rtm->rtm_type = RTN_MULTICAST; 23498c2ecf20Sopenharmony_ci rtm->rtm_scope = RT_SCOPE_UNIVERSE; 23508c2ecf20Sopenharmony_ci if (c->_c.mfc_flags & MFC_STATIC) 23518c2ecf20Sopenharmony_ci rtm->rtm_protocol = RTPROT_STATIC; 23528c2ecf20Sopenharmony_ci else 23538c2ecf20Sopenharmony_ci rtm->rtm_protocol = RTPROT_MROUTED; 23548c2ecf20Sopenharmony_ci rtm->rtm_flags = 0; 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_ci if (nla_put_in6_addr(skb, RTA_SRC, &c->mf6c_origin) || 23578c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, RTA_DST, &c->mf6c_mcastgrp)) 23588c2ecf20Sopenharmony_ci goto nla_put_failure; 23598c2ecf20Sopenharmony_ci err = mr_fill_mroute(mrt, skb, &c->_c, rtm); 23608c2ecf20Sopenharmony_ci /* do not break the dump if cache is unresolved */ 23618c2ecf20Sopenharmony_ci if (err < 0 && err != -ENOENT) 23628c2ecf20Sopenharmony_ci goto nla_put_failure; 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 23658c2ecf20Sopenharmony_ci return 0; 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_cinla_put_failure: 23688c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 23698c2ecf20Sopenharmony_ci return -EMSGSIZE; 23708c2ecf20Sopenharmony_ci} 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_cistatic int _ip6mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, 23738c2ecf20Sopenharmony_ci u32 portid, u32 seq, struct mr_mfc *c, 23748c2ecf20Sopenharmony_ci int cmd, int flags) 23758c2ecf20Sopenharmony_ci{ 23768c2ecf20Sopenharmony_ci return ip6mr_fill_mroute(mrt, skb, portid, seq, (struct mfc6_cache *)c, 23778c2ecf20Sopenharmony_ci cmd, flags); 23788c2ecf20Sopenharmony_ci} 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_cistatic int mr6_msgsize(bool unresolved, int maxvif) 23818c2ecf20Sopenharmony_ci{ 23828c2ecf20Sopenharmony_ci size_t len = 23838c2ecf20Sopenharmony_ci NLMSG_ALIGN(sizeof(struct rtmsg)) 23848c2ecf20Sopenharmony_ci + nla_total_size(4) /* RTA_TABLE */ 23858c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct in6_addr)) /* RTA_SRC */ 23868c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct in6_addr)) /* RTA_DST */ 23878c2ecf20Sopenharmony_ci ; 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci if (!unresolved) 23908c2ecf20Sopenharmony_ci len = len 23918c2ecf20Sopenharmony_ci + nla_total_size(4) /* RTA_IIF */ 23928c2ecf20Sopenharmony_ci + nla_total_size(0) /* RTA_MULTIPATH */ 23938c2ecf20Sopenharmony_ci + maxvif * NLA_ALIGN(sizeof(struct rtnexthop)) 23948c2ecf20Sopenharmony_ci /* RTA_MFC_STATS */ 23958c2ecf20Sopenharmony_ci + nla_total_size_64bit(sizeof(struct rta_mfc_stats)) 23968c2ecf20Sopenharmony_ci ; 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci return len; 23998c2ecf20Sopenharmony_ci} 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_cistatic void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc, 24028c2ecf20Sopenharmony_ci int cmd) 24038c2ecf20Sopenharmony_ci{ 24048c2ecf20Sopenharmony_ci struct net *net = read_pnet(&mrt->net); 24058c2ecf20Sopenharmony_ci struct sk_buff *skb; 24068c2ecf20Sopenharmony_ci int err = -ENOBUFS; 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci skb = nlmsg_new(mr6_msgsize(mfc->_c.mfc_parent >= MAXMIFS, mrt->maxvif), 24098c2ecf20Sopenharmony_ci GFP_ATOMIC); 24108c2ecf20Sopenharmony_ci if (!skb) 24118c2ecf20Sopenharmony_ci goto errout; 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci err = ip6mr_fill_mroute(mrt, skb, 0, 0, mfc, cmd, 0); 24148c2ecf20Sopenharmony_ci if (err < 0) 24158c2ecf20Sopenharmony_ci goto errout; 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV6_MROUTE, NULL, GFP_ATOMIC); 24188c2ecf20Sopenharmony_ci return; 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_cierrout: 24218c2ecf20Sopenharmony_ci kfree_skb(skb); 24228c2ecf20Sopenharmony_ci if (err < 0) 24238c2ecf20Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE, err); 24248c2ecf20Sopenharmony_ci} 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_cistatic size_t mrt6msg_netlink_msgsize(size_t payloadlen) 24278c2ecf20Sopenharmony_ci{ 24288c2ecf20Sopenharmony_ci size_t len = 24298c2ecf20Sopenharmony_ci NLMSG_ALIGN(sizeof(struct rtgenmsg)) 24308c2ecf20Sopenharmony_ci + nla_total_size(1) /* IP6MRA_CREPORT_MSGTYPE */ 24318c2ecf20Sopenharmony_ci + nla_total_size(4) /* IP6MRA_CREPORT_MIF_ID */ 24328c2ecf20Sopenharmony_ci /* IP6MRA_CREPORT_SRC_ADDR */ 24338c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct in6_addr)) 24348c2ecf20Sopenharmony_ci /* IP6MRA_CREPORT_DST_ADDR */ 24358c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct in6_addr)) 24368c2ecf20Sopenharmony_ci /* IP6MRA_CREPORT_PKT */ 24378c2ecf20Sopenharmony_ci + nla_total_size(payloadlen) 24388c2ecf20Sopenharmony_ci ; 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci return len; 24418c2ecf20Sopenharmony_ci} 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_cistatic void mrt6msg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt) 24448c2ecf20Sopenharmony_ci{ 24458c2ecf20Sopenharmony_ci struct net *net = read_pnet(&mrt->net); 24468c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 24478c2ecf20Sopenharmony_ci struct rtgenmsg *rtgenm; 24488c2ecf20Sopenharmony_ci struct mrt6msg *msg; 24498c2ecf20Sopenharmony_ci struct sk_buff *skb; 24508c2ecf20Sopenharmony_ci struct nlattr *nla; 24518c2ecf20Sopenharmony_ci int payloadlen; 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci payloadlen = pkt->len - sizeof(struct mrt6msg); 24548c2ecf20Sopenharmony_ci msg = (struct mrt6msg *)skb_transport_header(pkt); 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci skb = nlmsg_new(mrt6msg_netlink_msgsize(payloadlen), GFP_ATOMIC); 24578c2ecf20Sopenharmony_ci if (!skb) 24588c2ecf20Sopenharmony_ci goto errout; 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, 0, 0, RTM_NEWCACHEREPORT, 24618c2ecf20Sopenharmony_ci sizeof(struct rtgenmsg), 0); 24628c2ecf20Sopenharmony_ci if (!nlh) 24638c2ecf20Sopenharmony_ci goto errout; 24648c2ecf20Sopenharmony_ci rtgenm = nlmsg_data(nlh); 24658c2ecf20Sopenharmony_ci rtgenm->rtgen_family = RTNL_FAMILY_IP6MR; 24668c2ecf20Sopenharmony_ci if (nla_put_u8(skb, IP6MRA_CREPORT_MSGTYPE, msg->im6_msgtype) || 24678c2ecf20Sopenharmony_ci nla_put_u32(skb, IP6MRA_CREPORT_MIF_ID, msg->im6_mif) || 24688c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, IP6MRA_CREPORT_SRC_ADDR, 24698c2ecf20Sopenharmony_ci &msg->im6_src) || 24708c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, IP6MRA_CREPORT_DST_ADDR, 24718c2ecf20Sopenharmony_ci &msg->im6_dst)) 24728c2ecf20Sopenharmony_ci goto nla_put_failure; 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_ci nla = nla_reserve(skb, IP6MRA_CREPORT_PKT, payloadlen); 24758c2ecf20Sopenharmony_ci if (!nla || skb_copy_bits(pkt, sizeof(struct mrt6msg), 24768c2ecf20Sopenharmony_ci nla_data(nla), payloadlen)) 24778c2ecf20Sopenharmony_ci goto nla_put_failure; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV6_MROUTE_R, NULL, GFP_ATOMIC); 24828c2ecf20Sopenharmony_ci return; 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_cinla_put_failure: 24858c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 24868c2ecf20Sopenharmony_cierrout: 24878c2ecf20Sopenharmony_ci kfree_skb(skb); 24888c2ecf20Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE_R, -ENOBUFS); 24898c2ecf20Sopenharmony_ci} 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_cistatic int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) 24928c2ecf20Sopenharmony_ci{ 24938c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 24948c2ecf20Sopenharmony_ci struct fib_dump_filter filter = {}; 24958c2ecf20Sopenharmony_ci int err; 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_ci if (cb->strict_check) { 24988c2ecf20Sopenharmony_ci err = ip_valid_fib_dump_req(sock_net(skb->sk), nlh, 24998c2ecf20Sopenharmony_ci &filter, cb); 25008c2ecf20Sopenharmony_ci if (err < 0) 25018c2ecf20Sopenharmony_ci return err; 25028c2ecf20Sopenharmony_ci } 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci if (filter.table_id) { 25058c2ecf20Sopenharmony_ci struct mr_table *mrt; 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci mrt = ip6mr_get_table(sock_net(skb->sk), filter.table_id); 25088c2ecf20Sopenharmony_ci if (!mrt) { 25098c2ecf20Sopenharmony_ci if (rtnl_msg_family(cb->nlh) != RTNL_FAMILY_IP6MR) 25108c2ecf20Sopenharmony_ci return skb->len; 25118c2ecf20Sopenharmony_ci 25128c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(cb->extack, "MR table does not exist"); 25138c2ecf20Sopenharmony_ci return -ENOENT; 25148c2ecf20Sopenharmony_ci } 25158c2ecf20Sopenharmony_ci err = mr_table_dump(mrt, skb, cb, _ip6mr_fill_mroute, 25168c2ecf20Sopenharmony_ci &mfc_unres_lock, &filter); 25178c2ecf20Sopenharmony_ci return skb->len ? : err; 25188c2ecf20Sopenharmony_ci } 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci return mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter, 25218c2ecf20Sopenharmony_ci _ip6mr_fill_mroute, &mfc_unres_lock, &filter); 25228c2ecf20Sopenharmony_ci} 2523