18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IPv6 Address [auto]configuration 48c2ecf20Sopenharmony_ci * Linux INET6 implementation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 88c2ecf20Sopenharmony_ci * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * Changes: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Janos Farkas : delete timer on ifdown 158c2ecf20Sopenharmony_ci * <chexum@bankinf.banki.hu> 168c2ecf20Sopenharmony_ci * Andi Kleen : kill double kfree on module 178c2ecf20Sopenharmony_ci * unload. 188c2ecf20Sopenharmony_ci * Maciej W. Rozycki : FDDI support 198c2ecf20Sopenharmony_ci * sekiya@USAGI : Don't send too many RS 208c2ecf20Sopenharmony_ci * packets. 218c2ecf20Sopenharmony_ci * yoshfuji@USAGI : Fixed interval between DAD 228c2ecf20Sopenharmony_ci * packets. 238c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI : improved accuracy of 248c2ecf20Sopenharmony_ci * address validation timer. 258c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI : Privacy Extensions (RFC3041) 268c2ecf20Sopenharmony_ci * support. 278c2ecf20Sopenharmony_ci * Yuji SEKIYA @USAGI : Don't assign a same IPv6 288c2ecf20Sopenharmony_ci * address on a same interface. 298c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI : ARCnet support 308c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI : convert /proc/net/if_inet6 to 318c2ecf20Sopenharmony_ci * seq_file. 328c2ecf20Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI : improved source address 338c2ecf20Sopenharmony_ci * selection; consider scope, 348c2ecf20Sopenharmony_ci * status etc. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include <linux/errno.h> 408c2ecf20Sopenharmony_ci#include <linux/types.h> 418c2ecf20Sopenharmony_ci#include <linux/kernel.h> 428c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 438c2ecf20Sopenharmony_ci#include <linux/socket.h> 448c2ecf20Sopenharmony_ci#include <linux/sockios.h> 458c2ecf20Sopenharmony_ci#include <linux/net.h> 468c2ecf20Sopenharmony_ci#include <linux/inet.h> 478c2ecf20Sopenharmony_ci#include <linux/in6.h> 488c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 498c2ecf20Sopenharmony_ci#include <linux/if_addr.h> 508c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 518c2ecf20Sopenharmony_ci#include <linux/if_arcnet.h> 528c2ecf20Sopenharmony_ci#include <linux/if_infiniband.h> 538c2ecf20Sopenharmony_ci#include <linux/route.h> 548c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 558c2ecf20Sopenharmony_ci#include <linux/init.h> 568c2ecf20Sopenharmony_ci#include <linux/slab.h> 578c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 588c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 598c2ecf20Sopenharmony_ci#endif 608c2ecf20Sopenharmony_ci#include <linux/capability.h> 618c2ecf20Sopenharmony_ci#include <linux/delay.h> 628c2ecf20Sopenharmony_ci#include <linux/notifier.h> 638c2ecf20Sopenharmony_ci#include <linux/string.h> 648c2ecf20Sopenharmony_ci#include <linux/hash.h> 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 678c2ecf20Sopenharmony_ci#include <net/sock.h> 688c2ecf20Sopenharmony_ci#include <net/snmp.h> 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#include <net/6lowpan.h> 718c2ecf20Sopenharmony_ci#include <net/firewire.h> 728c2ecf20Sopenharmony_ci#include <net/ipv6.h> 738c2ecf20Sopenharmony_ci#include <net/protocol.h> 748c2ecf20Sopenharmony_ci#include <net/ndisc.h> 758c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 768c2ecf20Sopenharmony_ci#include <net/addrconf.h> 778c2ecf20Sopenharmony_ci#include <net/tcp.h> 788c2ecf20Sopenharmony_ci#include <net/ip.h> 798c2ecf20Sopenharmony_ci#include <net/netlink.h> 808c2ecf20Sopenharmony_ci#include <net/pkt_sched.h> 818c2ecf20Sopenharmony_ci#include <net/l3mdev.h> 828c2ecf20Sopenharmony_ci#include <linux/if_tunnel.h> 838c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 848c2ecf20Sopenharmony_ci#include <linux/netconf.h> 858c2ecf20Sopenharmony_ci#include <linux/random.h> 868c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 878c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 908c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 918c2ecf20Sopenharmony_ci#include <linux/export.h> 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define INFINITY_LIFE_TIME 0xFFFFFFFF 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define IPV6_MAX_STRLEN \ 968c2ecf20Sopenharmony_ci sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic inline u32 cstamp_delta(unsigned long cstamp) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic inline s32 rfc3315_s14_backoff_init(s32 irt) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci /* multiply 'initial retransmission time' by 0.9 .. 1.1 */ 1068c2ecf20Sopenharmony_ci u64 tmp = (900000 + prandom_u32() % 200001) * (u64)irt; 1078c2ecf20Sopenharmony_ci do_div(tmp, 1000000); 1088c2ecf20Sopenharmony_ci return (s32)tmp; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic inline s32 rfc3315_s14_backoff_update(s32 rt, s32 mrt) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci /* multiply 'retransmission timeout' by 1.9 .. 2.1 */ 1148c2ecf20Sopenharmony_ci u64 tmp = (1900000 + prandom_u32() % 200001) * (u64)rt; 1158c2ecf20Sopenharmony_ci do_div(tmp, 1000000); 1168c2ecf20Sopenharmony_ci if ((s32)tmp > mrt) { 1178c2ecf20Sopenharmony_ci /* multiply 'maximum retransmission time' by 0.9 .. 1.1 */ 1188c2ecf20Sopenharmony_ci tmp = (900000 + prandom_u32() % 200001) * (u64)mrt; 1198c2ecf20Sopenharmony_ci do_div(tmp, 1000000); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci return (s32)tmp; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 1258c2ecf20Sopenharmony_cistatic int addrconf_sysctl_register(struct inet6_dev *idev); 1268c2ecf20Sopenharmony_cistatic void addrconf_sysctl_unregister(struct inet6_dev *idev); 1278c2ecf20Sopenharmony_ci#else 1288c2ecf20Sopenharmony_cistatic inline int addrconf_sysctl_register(struct inet6_dev *idev) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic inline void addrconf_sysctl_unregister(struct inet6_dev *idev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci#endif 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void ipv6_gen_rnd_iid(struct in6_addr *addr); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int ipv6_generate_eui64(u8 *eui, struct net_device *dev); 1418c2ecf20Sopenharmony_cistatic int ipv6_count_addresses(const struct inet6_dev *idev); 1428c2ecf20Sopenharmony_cistatic int ipv6_generate_stable_address(struct in6_addr *addr, 1438c2ecf20Sopenharmony_ci u8 dad_count, 1448c2ecf20Sopenharmony_ci const struct inet6_dev *idev); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci#define IN6_ADDR_HSIZE_SHIFT 8 1478c2ecf20Sopenharmony_ci#define IN6_ADDR_HSIZE (1 << IN6_ADDR_HSIZE_SHIFT) 1488c2ecf20Sopenharmony_ci/* 1498c2ecf20Sopenharmony_ci * Configured unicast address hash table 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE]; 1528c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(addrconf_hash_lock); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void addrconf_verify(void); 1558c2ecf20Sopenharmony_cistatic void addrconf_verify_rtnl(void); 1568c2ecf20Sopenharmony_cistatic void addrconf_verify_work(struct work_struct *); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic struct workqueue_struct *addrconf_wq; 1598c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(addr_chk_work, addrconf_verify_work); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void addrconf_join_anycast(struct inet6_ifaddr *ifp); 1628c2ecf20Sopenharmony_cistatic void addrconf_leave_anycast(struct inet6_ifaddr *ifp); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void addrconf_type_change(struct net_device *dev, 1658c2ecf20Sopenharmony_ci unsigned long event); 1668c2ecf20Sopenharmony_cistatic int addrconf_ifdown(struct net_device *dev, bool unregister); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic struct fib6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, 1698c2ecf20Sopenharmony_ci int plen, 1708c2ecf20Sopenharmony_ci const struct net_device *dev, 1718c2ecf20Sopenharmony_ci u32 flags, u32 noflags, 1728c2ecf20Sopenharmony_ci bool no_gw); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void addrconf_dad_start(struct inet6_ifaddr *ifp); 1758c2ecf20Sopenharmony_cistatic void addrconf_dad_work(struct work_struct *w); 1768c2ecf20Sopenharmony_cistatic void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id, 1778c2ecf20Sopenharmony_ci bool send_na); 1788c2ecf20Sopenharmony_cistatic void addrconf_dad_run(struct inet6_dev *idev, bool restart); 1798c2ecf20Sopenharmony_cistatic void addrconf_rs_timer(struct timer_list *t); 1808c2ecf20Sopenharmony_cistatic void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa); 1818c2ecf20Sopenharmony_cistatic void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void inet6_prefix_notify(int event, struct inet6_dev *idev, 1848c2ecf20Sopenharmony_ci struct prefix_info *pinfo); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic struct ipv6_devconf ipv6_devconf __read_mostly = { 1878c2ecf20Sopenharmony_ci .forwarding = 0, 1888c2ecf20Sopenharmony_ci .hop_limit = IPV6_DEFAULT_HOPLIMIT, 1898c2ecf20Sopenharmony_ci .mtu6 = IPV6_MIN_MTU, 1908c2ecf20Sopenharmony_ci .accept_ra = 1, 1918c2ecf20Sopenharmony_ci .accept_redirects = 1, 1928c2ecf20Sopenharmony_ci .autoconf = 1, 1938c2ecf20Sopenharmony_ci .force_mld_version = 0, 1948c2ecf20Sopenharmony_ci .mldv1_unsolicited_report_interval = 10 * HZ, 1958c2ecf20Sopenharmony_ci .mldv2_unsolicited_report_interval = HZ, 1968c2ecf20Sopenharmony_ci .dad_transmits = 1, 1978c2ecf20Sopenharmony_ci .rtr_solicits = MAX_RTR_SOLICITATIONS, 1988c2ecf20Sopenharmony_ci .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, 1998c2ecf20Sopenharmony_ci .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL, 2008c2ecf20Sopenharmony_ci .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY, 2018c2ecf20Sopenharmony_ci .use_tempaddr = 0, 2028c2ecf20Sopenharmony_ci .temp_valid_lft = TEMP_VALID_LIFETIME, 2038c2ecf20Sopenharmony_ci .temp_prefered_lft = TEMP_PREFERRED_LIFETIME, 2048c2ecf20Sopenharmony_ci .regen_max_retry = REGEN_MAX_RETRY, 2058c2ecf20Sopenharmony_ci .max_desync_factor = MAX_DESYNC_FACTOR, 2068c2ecf20Sopenharmony_ci .max_addresses = IPV6_MAX_ADDRESSES, 2078c2ecf20Sopenharmony_ci .accept_ra_defrtr = 1, 2088c2ecf20Sopenharmony_ci .accept_ra_from_local = 0, 2098c2ecf20Sopenharmony_ci .accept_ra_min_hop_limit= 1, 2108c2ecf20Sopenharmony_ci .accept_ra_min_lft = 0, 2118c2ecf20Sopenharmony_ci .accept_ra_pinfo = 1, 2128c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 2138c2ecf20Sopenharmony_ci .accept_ra_rtr_pref = 1, 2148c2ecf20Sopenharmony_ci .rtr_probe_interval = 60 * HZ, 2158c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 2168c2ecf20Sopenharmony_ci .accept_ra_rt_info_min_plen = 0, 2178c2ecf20Sopenharmony_ci .accept_ra_rt_info_max_plen = 0, 2188c2ecf20Sopenharmony_ci#endif 2198c2ecf20Sopenharmony_ci#endif 2208c2ecf20Sopenharmony_ci .proxy_ndp = 0, 2218c2ecf20Sopenharmony_ci .accept_source_route = 0, /* we do not accept RH0 by default. */ 2228c2ecf20Sopenharmony_ci .disable_ipv6 = 0, 2238c2ecf20Sopenharmony_ci .accept_dad = 0, 2248c2ecf20Sopenharmony_ci .suppress_frag_ndisc = 1, 2258c2ecf20Sopenharmony_ci .accept_ra_mtu = 1, 2268c2ecf20Sopenharmony_ci .stable_secret = { 2278c2ecf20Sopenharmony_ci .initialized = false, 2288c2ecf20Sopenharmony_ci }, 2298c2ecf20Sopenharmony_ci .use_oif_addrs_only = 0, 2308c2ecf20Sopenharmony_ci .ignore_routes_with_linkdown = 0, 2318c2ecf20Sopenharmony_ci .keep_addr_on_down = 0, 2328c2ecf20Sopenharmony_ci .seg6_enabled = 0, 2338c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 2348c2ecf20Sopenharmony_ci .seg6_require_hmac = 0, 2358c2ecf20Sopenharmony_ci#endif 2368c2ecf20Sopenharmony_ci .enhanced_dad = 1, 2378c2ecf20Sopenharmony_ci .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, 2388c2ecf20Sopenharmony_ci .disable_policy = 0, 2398c2ecf20Sopenharmony_ci .rpl_seg_enabled = 0, 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 2438c2ecf20Sopenharmony_ci .forwarding = 0, 2448c2ecf20Sopenharmony_ci .hop_limit = IPV6_DEFAULT_HOPLIMIT, 2458c2ecf20Sopenharmony_ci .mtu6 = IPV6_MIN_MTU, 2468c2ecf20Sopenharmony_ci .accept_ra = 1, 2478c2ecf20Sopenharmony_ci .accept_redirects = 1, 2488c2ecf20Sopenharmony_ci .autoconf = 1, 2498c2ecf20Sopenharmony_ci .force_mld_version = 0, 2508c2ecf20Sopenharmony_ci .mldv1_unsolicited_report_interval = 10 * HZ, 2518c2ecf20Sopenharmony_ci .mldv2_unsolicited_report_interval = HZ, 2528c2ecf20Sopenharmony_ci .dad_transmits = 1, 2538c2ecf20Sopenharmony_ci .rtr_solicits = MAX_RTR_SOLICITATIONS, 2548c2ecf20Sopenharmony_ci .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, 2558c2ecf20Sopenharmony_ci .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL, 2568c2ecf20Sopenharmony_ci .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY, 2578c2ecf20Sopenharmony_ci .use_tempaddr = 0, 2588c2ecf20Sopenharmony_ci .temp_valid_lft = TEMP_VALID_LIFETIME, 2598c2ecf20Sopenharmony_ci .temp_prefered_lft = TEMP_PREFERRED_LIFETIME, 2608c2ecf20Sopenharmony_ci .regen_max_retry = REGEN_MAX_RETRY, 2618c2ecf20Sopenharmony_ci .max_desync_factor = MAX_DESYNC_FACTOR, 2628c2ecf20Sopenharmony_ci .max_addresses = IPV6_MAX_ADDRESSES, 2638c2ecf20Sopenharmony_ci .accept_ra_defrtr = 1, 2648c2ecf20Sopenharmony_ci .accept_ra_from_local = 0, 2658c2ecf20Sopenharmony_ci .accept_ra_min_hop_limit= 1, 2668c2ecf20Sopenharmony_ci .accept_ra_min_lft = 0, 2678c2ecf20Sopenharmony_ci .accept_ra_pinfo = 1, 2688c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 2698c2ecf20Sopenharmony_ci .accept_ra_rtr_pref = 1, 2708c2ecf20Sopenharmony_ci .rtr_probe_interval = 60 * HZ, 2718c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 2728c2ecf20Sopenharmony_ci .accept_ra_rt_info_min_plen = 0, 2738c2ecf20Sopenharmony_ci .accept_ra_rt_info_max_plen = 0, 2748c2ecf20Sopenharmony_ci#endif 2758c2ecf20Sopenharmony_ci#endif 2768c2ecf20Sopenharmony_ci .proxy_ndp = 0, 2778c2ecf20Sopenharmony_ci .accept_source_route = 0, /* we do not accept RH0 by default. */ 2788c2ecf20Sopenharmony_ci .disable_ipv6 = 0, 2798c2ecf20Sopenharmony_ci .accept_dad = 1, 2808c2ecf20Sopenharmony_ci .suppress_frag_ndisc = 1, 2818c2ecf20Sopenharmony_ci .accept_ra_mtu = 1, 2828c2ecf20Sopenharmony_ci .stable_secret = { 2838c2ecf20Sopenharmony_ci .initialized = false, 2848c2ecf20Sopenharmony_ci }, 2858c2ecf20Sopenharmony_ci .use_oif_addrs_only = 0, 2868c2ecf20Sopenharmony_ci .ignore_routes_with_linkdown = 0, 2878c2ecf20Sopenharmony_ci .keep_addr_on_down = 0, 2888c2ecf20Sopenharmony_ci .seg6_enabled = 0, 2898c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 2908c2ecf20Sopenharmony_ci .seg6_require_hmac = 0, 2918c2ecf20Sopenharmony_ci#endif 2928c2ecf20Sopenharmony_ci .enhanced_dad = 1, 2938c2ecf20Sopenharmony_ci .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, 2948c2ecf20Sopenharmony_ci .disable_policy = 0, 2958c2ecf20Sopenharmony_ci .rpl_seg_enabled = 0, 2968c2ecf20Sopenharmony_ci}; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci/* Check if link is ready: is it up and is a valid qdisc available */ 2998c2ecf20Sopenharmony_cistatic inline bool addrconf_link_ready(const struct net_device *dev) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci return netif_oper_up(dev) && !qdisc_tx_is_noop(dev); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic void addrconf_del_rs_timer(struct inet6_dev *idev) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci if (del_timer(&idev->rs_timer)) 3078c2ecf20Sopenharmony_ci __in6_dev_put(idev); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic void addrconf_del_dad_work(struct inet6_ifaddr *ifp) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci if (cancel_delayed_work(&ifp->dad_work)) 3138c2ecf20Sopenharmony_ci __in6_ifa_put(ifp); 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic void addrconf_mod_rs_timer(struct inet6_dev *idev, 3178c2ecf20Sopenharmony_ci unsigned long when) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci if (!mod_timer(&idev->rs_timer, jiffies + when)) 3208c2ecf20Sopenharmony_ci in6_dev_hold(idev); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic void addrconf_mod_dad_work(struct inet6_ifaddr *ifp, 3248c2ecf20Sopenharmony_ci unsigned long delay) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci in6_ifa_hold(ifp); 3278c2ecf20Sopenharmony_ci if (mod_delayed_work(addrconf_wq, &ifp->dad_work, delay)) 3288c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int snmp6_alloc_dev(struct inet6_dev *idev) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci int i; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci idev->stats.ipv6 = alloc_percpu(struct ipstats_mib); 3368c2ecf20Sopenharmony_ci if (!idev->stats.ipv6) 3378c2ecf20Sopenharmony_ci goto err_ip; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 3408c2ecf20Sopenharmony_ci struct ipstats_mib *addrconf_stats; 3418c2ecf20Sopenharmony_ci addrconf_stats = per_cpu_ptr(idev->stats.ipv6, i); 3428c2ecf20Sopenharmony_ci u64_stats_init(&addrconf_stats->syncp); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device), 3478c2ecf20Sopenharmony_ci GFP_KERNEL); 3488c2ecf20Sopenharmony_ci if (!idev->stats.icmpv6dev) 3498c2ecf20Sopenharmony_ci goto err_icmp; 3508c2ecf20Sopenharmony_ci idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device), 3518c2ecf20Sopenharmony_ci GFP_KERNEL); 3528c2ecf20Sopenharmony_ci if (!idev->stats.icmpv6msgdev) 3538c2ecf20Sopenharmony_ci goto err_icmpmsg; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cierr_icmpmsg: 3588c2ecf20Sopenharmony_ci kfree(idev->stats.icmpv6dev); 3598c2ecf20Sopenharmony_cierr_icmp: 3608c2ecf20Sopenharmony_ci free_percpu(idev->stats.ipv6); 3618c2ecf20Sopenharmony_cierr_ip: 3628c2ecf20Sopenharmony_ci return -ENOMEM; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic struct inet6_dev *ipv6_add_dev(struct net_device *dev) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct inet6_dev *ndev; 3688c2ecf20Sopenharmony_ci int err = -ENOMEM; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci ASSERT_RTNL(); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (dev->mtu < IPV6_MIN_MTU) 3738c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL); 3768c2ecf20Sopenharmony_ci if (!ndev) 3778c2ecf20Sopenharmony_ci return ERR_PTR(err); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci rwlock_init(&ndev->lock); 3808c2ecf20Sopenharmony_ci ndev->dev = dev; 3818c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ndev->addr_list); 3828c2ecf20Sopenharmony_ci timer_setup(&ndev->rs_timer, addrconf_rs_timer, 0); 3838c2ecf20Sopenharmony_ci memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf)); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (ndev->cnf.stable_secret.initialized) 3868c2ecf20Sopenharmony_ci ndev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci ndev->cnf.mtu6 = dev->mtu; 3898c2ecf20Sopenharmony_ci ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl); 3908c2ecf20Sopenharmony_ci if (!ndev->nd_parms) { 3918c2ecf20Sopenharmony_ci kfree(ndev); 3928c2ecf20Sopenharmony_ci return ERR_PTR(err); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci if (ndev->cnf.forwarding) 3958c2ecf20Sopenharmony_ci dev_disable_lro(dev); 3968c2ecf20Sopenharmony_ci /* We refer to the device */ 3978c2ecf20Sopenharmony_ci dev_hold(dev); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (snmp6_alloc_dev(ndev) < 0) { 4008c2ecf20Sopenharmony_ci netdev_dbg(dev, "%s: cannot allocate memory for statistics\n", 4018c2ecf20Sopenharmony_ci __func__); 4028c2ecf20Sopenharmony_ci neigh_parms_release(&nd_tbl, ndev->nd_parms); 4038c2ecf20Sopenharmony_ci dev_put(dev); 4048c2ecf20Sopenharmony_ci kfree(ndev); 4058c2ecf20Sopenharmony_ci return ERR_PTR(err); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (snmp6_register_dev(ndev) < 0) { 4098c2ecf20Sopenharmony_ci netdev_dbg(dev, "%s: cannot create /proc/net/dev_snmp6/%s\n", 4108c2ecf20Sopenharmony_ci __func__, dev->name); 4118c2ecf20Sopenharmony_ci goto err_release; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* One reference from device. */ 4158c2ecf20Sopenharmony_ci refcount_set(&ndev->refcnt, 1); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (dev->flags & (IFF_NOARP | IFF_LOOPBACK)) 4188c2ecf20Sopenharmony_ci ndev->cnf.accept_dad = -1; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT) 4218c2ecf20Sopenharmony_ci if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) { 4228c2ecf20Sopenharmony_ci pr_info("%s: Disabled Multicast RS\n", dev->name); 4238c2ecf20Sopenharmony_ci ndev->cnf.rtr_solicits = 0; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci#endif 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ndev->tempaddr_list); 4288c2ecf20Sopenharmony_ci ndev->desync_factor = U32_MAX; 4298c2ecf20Sopenharmony_ci if ((dev->flags&IFF_LOOPBACK) || 4308c2ecf20Sopenharmony_ci dev->type == ARPHRD_TUNNEL || 4318c2ecf20Sopenharmony_ci dev->type == ARPHRD_TUNNEL6 || 4328c2ecf20Sopenharmony_ci dev->type == ARPHRD_SIT || 4338c2ecf20Sopenharmony_ci dev->type == ARPHRD_NONE) { 4348c2ecf20Sopenharmony_ci ndev->cnf.use_tempaddr = -1; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci ndev->token = in6addr_any; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (netif_running(dev) && addrconf_link_ready(dev)) 4408c2ecf20Sopenharmony_ci ndev->if_flags |= IF_READY; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci ipv6_mc_init_dev(ndev); 4438c2ecf20Sopenharmony_ci ndev->tstamp = jiffies; 4448c2ecf20Sopenharmony_ci err = addrconf_sysctl_register(ndev); 4458c2ecf20Sopenharmony_ci if (err) { 4468c2ecf20Sopenharmony_ci ipv6_mc_destroy_dev(ndev); 4478c2ecf20Sopenharmony_ci snmp6_unregister_dev(ndev); 4488c2ecf20Sopenharmony_ci goto err_release; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci /* protected by rtnl_lock */ 4518c2ecf20Sopenharmony_ci rcu_assign_pointer(dev->ip6_ptr, ndev); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* Join interface-local all-node multicast group */ 4548c2ecf20Sopenharmony_ci ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allnodes); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Join all-node multicast group */ 4578c2ecf20Sopenharmony_ci ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* Join all-router multicast group if forwarding is set */ 4608c2ecf20Sopenharmony_ci if (ndev->cnf.forwarding && (dev->flags & IFF_MULTICAST)) 4618c2ecf20Sopenharmony_ci ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return ndev; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cierr_release: 4668c2ecf20Sopenharmony_ci neigh_parms_release(&nd_tbl, ndev->nd_parms); 4678c2ecf20Sopenharmony_ci ndev->dead = 1; 4688c2ecf20Sopenharmony_ci in6_dev_finish_destroy(ndev); 4698c2ecf20Sopenharmony_ci return ERR_PTR(err); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic struct inet6_dev *ipv6_find_idev(struct net_device *dev) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct inet6_dev *idev; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci ASSERT_RTNL(); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 4798c2ecf20Sopenharmony_ci if (!idev) { 4808c2ecf20Sopenharmony_ci idev = ipv6_add_dev(dev); 4818c2ecf20Sopenharmony_ci if (IS_ERR(idev)) 4828c2ecf20Sopenharmony_ci return idev; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (dev->flags&IFF_UP) 4868c2ecf20Sopenharmony_ci ipv6_mc_up(idev); 4878c2ecf20Sopenharmony_ci return idev; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic int inet6_netconf_msgsize_devconf(int type) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 4938c2ecf20Sopenharmony_ci + nla_total_size(4); /* NETCONFA_IFINDEX */ 4948c2ecf20Sopenharmony_ci bool all = false; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (type == NETCONFA_ALL) 4978c2ecf20Sopenharmony_ci all = true; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (all || type == NETCONFA_FORWARDING) 5008c2ecf20Sopenharmony_ci size += nla_total_size(4); 5018c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 5028c2ecf20Sopenharmony_ci if (all || type == NETCONFA_MC_FORWARDING) 5038c2ecf20Sopenharmony_ci size += nla_total_size(4); 5048c2ecf20Sopenharmony_ci#endif 5058c2ecf20Sopenharmony_ci if (all || type == NETCONFA_PROXY_NEIGH) 5068c2ecf20Sopenharmony_ci size += nla_total_size(4); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) 5098c2ecf20Sopenharmony_ci size += nla_total_size(4); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return size; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 5158c2ecf20Sopenharmony_ci struct ipv6_devconf *devconf, u32 portid, 5168c2ecf20Sopenharmony_ci u32 seq, int event, unsigned int flags, 5178c2ecf20Sopenharmony_ci int type) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 5208c2ecf20Sopenharmony_ci struct netconfmsg *ncm; 5218c2ecf20Sopenharmony_ci bool all = false; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 5248c2ecf20Sopenharmony_ci flags); 5258c2ecf20Sopenharmony_ci if (!nlh) 5268c2ecf20Sopenharmony_ci return -EMSGSIZE; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (type == NETCONFA_ALL) 5298c2ecf20Sopenharmony_ci all = true; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci ncm = nlmsg_data(nlh); 5328c2ecf20Sopenharmony_ci ncm->ncm_family = AF_INET6; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 5358c2ecf20Sopenharmony_ci goto nla_put_failure; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (!devconf) 5388c2ecf20Sopenharmony_ci goto out; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if ((all || type == NETCONFA_FORWARDING) && 5418c2ecf20Sopenharmony_ci nla_put_s32(skb, NETCONFA_FORWARDING, devconf->forwarding) < 0) 5428c2ecf20Sopenharmony_ci goto nla_put_failure; 5438c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 5448c2ecf20Sopenharmony_ci if ((all || type == NETCONFA_MC_FORWARDING) && 5458c2ecf20Sopenharmony_ci nla_put_s32(skb, NETCONFA_MC_FORWARDING, 5468c2ecf20Sopenharmony_ci atomic_read(&devconf->mc_forwarding)) < 0) 5478c2ecf20Sopenharmony_ci goto nla_put_failure; 5488c2ecf20Sopenharmony_ci#endif 5498c2ecf20Sopenharmony_ci if ((all || type == NETCONFA_PROXY_NEIGH) && 5508c2ecf20Sopenharmony_ci nla_put_s32(skb, NETCONFA_PROXY_NEIGH, devconf->proxy_ndp) < 0) 5518c2ecf20Sopenharmony_ci goto nla_put_failure; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) && 5548c2ecf20Sopenharmony_ci nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 5558c2ecf20Sopenharmony_ci devconf->ignore_routes_with_linkdown) < 0) 5568c2ecf20Sopenharmony_ci goto nla_put_failure; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ciout: 5598c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 5608c2ecf20Sopenharmony_ci return 0; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cinla_put_failure: 5638c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 5648c2ecf20Sopenharmony_ci return -EMSGSIZE; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_civoid inet6_netconf_notify_devconf(struct net *net, int event, int type, 5688c2ecf20Sopenharmony_ci int ifindex, struct ipv6_devconf *devconf) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci struct sk_buff *skb; 5718c2ecf20Sopenharmony_ci int err = -ENOBUFS; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci skb = nlmsg_new(inet6_netconf_msgsize_devconf(type), GFP_KERNEL); 5748c2ecf20Sopenharmony_ci if (!skb) 5758c2ecf20Sopenharmony_ci goto errout; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci err = inet6_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 5788c2ecf20Sopenharmony_ci event, 0, type); 5798c2ecf20Sopenharmony_ci if (err < 0) { 5808c2ecf20Sopenharmony_ci /* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */ 5818c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 5828c2ecf20Sopenharmony_ci kfree_skb(skb); 5838c2ecf20Sopenharmony_ci goto errout; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV6_NETCONF, NULL, GFP_KERNEL); 5868c2ecf20Sopenharmony_ci return; 5878c2ecf20Sopenharmony_cierrout: 5888c2ecf20Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_NETCONF, err); 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = { 5928c2ecf20Sopenharmony_ci [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 5938c2ecf20Sopenharmony_ci [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 5948c2ecf20Sopenharmony_ci [NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) }, 5958c2ecf20Sopenharmony_ci [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .len = sizeof(int) }, 5968c2ecf20Sopenharmony_ci}; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int inet6_netconf_valid_get_req(struct sk_buff *skb, 5998c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh, 6008c2ecf20Sopenharmony_ci struct nlattr **tb, 6018c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci int i, err; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) { 6068c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for netconf get request"); 6078c2ecf20Sopenharmony_ci return -EINVAL; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (!netlink_strict_get_check(skb)) 6118c2ecf20Sopenharmony_ci return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg), 6128c2ecf20Sopenharmony_ci tb, NETCONFA_MAX, 6138c2ecf20Sopenharmony_ci devconf_ipv6_policy, extack); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg), 6168c2ecf20Sopenharmony_ci tb, NETCONFA_MAX, 6178c2ecf20Sopenharmony_ci devconf_ipv6_policy, extack); 6188c2ecf20Sopenharmony_ci if (err) 6198c2ecf20Sopenharmony_ci return err; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci for (i = 0; i <= NETCONFA_MAX; i++) { 6228c2ecf20Sopenharmony_ci if (!tb[i]) 6238c2ecf20Sopenharmony_ci continue; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci switch (i) { 6268c2ecf20Sopenharmony_ci case NETCONFA_IFINDEX: 6278c2ecf20Sopenharmony_ci break; 6288c2ecf20Sopenharmony_ci default: 6298c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in netconf get request"); 6308c2ecf20Sopenharmony_ci return -EINVAL; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci return 0; 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic int inet6_netconf_get_devconf(struct sk_buff *in_skb, 6388c2ecf20Sopenharmony_ci struct nlmsghdr *nlh, 6398c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 6428c2ecf20Sopenharmony_ci struct nlattr *tb[NETCONFA_MAX+1]; 6438c2ecf20Sopenharmony_ci struct inet6_dev *in6_dev = NULL; 6448c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 6458c2ecf20Sopenharmony_ci struct sk_buff *skb; 6468c2ecf20Sopenharmony_ci struct ipv6_devconf *devconf; 6478c2ecf20Sopenharmony_ci int ifindex; 6488c2ecf20Sopenharmony_ci int err; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci err = inet6_netconf_valid_get_req(in_skb, nlh, tb, extack); 6518c2ecf20Sopenharmony_ci if (err < 0) 6528c2ecf20Sopenharmony_ci return err; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (!tb[NETCONFA_IFINDEX]) 6558c2ecf20Sopenharmony_ci return -EINVAL; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci err = -EINVAL; 6588c2ecf20Sopenharmony_ci ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 6598c2ecf20Sopenharmony_ci switch (ifindex) { 6608c2ecf20Sopenharmony_ci case NETCONFA_IFINDEX_ALL: 6618c2ecf20Sopenharmony_ci devconf = net->ipv6.devconf_all; 6628c2ecf20Sopenharmony_ci break; 6638c2ecf20Sopenharmony_ci case NETCONFA_IFINDEX_DEFAULT: 6648c2ecf20Sopenharmony_ci devconf = net->ipv6.devconf_dflt; 6658c2ecf20Sopenharmony_ci break; 6668c2ecf20Sopenharmony_ci default: 6678c2ecf20Sopenharmony_ci dev = dev_get_by_index(net, ifindex); 6688c2ecf20Sopenharmony_ci if (!dev) 6698c2ecf20Sopenharmony_ci return -EINVAL; 6708c2ecf20Sopenharmony_ci in6_dev = in6_dev_get(dev); 6718c2ecf20Sopenharmony_ci if (!in6_dev) 6728c2ecf20Sopenharmony_ci goto errout; 6738c2ecf20Sopenharmony_ci devconf = &in6_dev->cnf; 6748c2ecf20Sopenharmony_ci break; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci err = -ENOBUFS; 6788c2ecf20Sopenharmony_ci skb = nlmsg_new(inet6_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL); 6798c2ecf20Sopenharmony_ci if (!skb) 6808c2ecf20Sopenharmony_ci goto errout; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci err = inet6_netconf_fill_devconf(skb, ifindex, devconf, 6838c2ecf20Sopenharmony_ci NETLINK_CB(in_skb).portid, 6848c2ecf20Sopenharmony_ci nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 6858c2ecf20Sopenharmony_ci NETCONFA_ALL); 6868c2ecf20Sopenharmony_ci if (err < 0) { 6878c2ecf20Sopenharmony_ci /* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */ 6888c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 6898c2ecf20Sopenharmony_ci kfree_skb(skb); 6908c2ecf20Sopenharmony_ci goto errout; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 6938c2ecf20Sopenharmony_cierrout: 6948c2ecf20Sopenharmony_ci if (in6_dev) 6958c2ecf20Sopenharmony_ci in6_dev_put(in6_dev); 6968c2ecf20Sopenharmony_ci if (dev) 6978c2ecf20Sopenharmony_ci dev_put(dev); 6988c2ecf20Sopenharmony_ci return err; 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cistatic int inet6_netconf_dump_devconf(struct sk_buff *skb, 7028c2ecf20Sopenharmony_ci struct netlink_callback *cb) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 7058c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 7068c2ecf20Sopenharmony_ci int h, s_h; 7078c2ecf20Sopenharmony_ci int idx, s_idx; 7088c2ecf20Sopenharmony_ci struct net_device *dev; 7098c2ecf20Sopenharmony_ci struct inet6_dev *idev; 7108c2ecf20Sopenharmony_ci struct hlist_head *head; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci if (cb->strict_check) { 7138c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack = cb->extack; 7148c2ecf20Sopenharmony_ci struct netconfmsg *ncm; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) { 7178c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for netconf dump request"); 7188c2ecf20Sopenharmony_ci return -EINVAL; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (nlmsg_attrlen(nlh, sizeof(*ncm))) { 7228c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid data after header in netconf dump request"); 7238c2ecf20Sopenharmony_ci return -EINVAL; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci s_h = cb->args[0]; 7288c2ecf20Sopenharmony_ci s_idx = idx = cb->args[1]; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 7318c2ecf20Sopenharmony_ci idx = 0; 7328c2ecf20Sopenharmony_ci head = &net->dev_index_head[h]; 7338c2ecf20Sopenharmony_ci rcu_read_lock(); 7348c2ecf20Sopenharmony_ci cb->seq = atomic_read(&net->ipv6.dev_addr_genid) ^ 7358c2ecf20Sopenharmony_ci net->dev_base_seq; 7368c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(dev, head, index_hlist) { 7378c2ecf20Sopenharmony_ci if (idx < s_idx) 7388c2ecf20Sopenharmony_ci goto cont; 7398c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 7408c2ecf20Sopenharmony_ci if (!idev) 7418c2ecf20Sopenharmony_ci goto cont; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (inet6_netconf_fill_devconf(skb, dev->ifindex, 7448c2ecf20Sopenharmony_ci &idev->cnf, 7458c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 7468c2ecf20Sopenharmony_ci nlh->nlmsg_seq, 7478c2ecf20Sopenharmony_ci RTM_NEWNETCONF, 7488c2ecf20Sopenharmony_ci NLM_F_MULTI, 7498c2ecf20Sopenharmony_ci NETCONFA_ALL) < 0) { 7508c2ecf20Sopenharmony_ci rcu_read_unlock(); 7518c2ecf20Sopenharmony_ci goto done; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 7548c2ecf20Sopenharmony_cicont: 7558c2ecf20Sopenharmony_ci idx++; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci rcu_read_unlock(); 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci if (h == NETDEV_HASHENTRIES) { 7608c2ecf20Sopenharmony_ci if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 7618c2ecf20Sopenharmony_ci net->ipv6.devconf_all, 7628c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 7638c2ecf20Sopenharmony_ci nlh->nlmsg_seq, 7648c2ecf20Sopenharmony_ci RTM_NEWNETCONF, NLM_F_MULTI, 7658c2ecf20Sopenharmony_ci NETCONFA_ALL) < 0) 7668c2ecf20Sopenharmony_ci goto done; 7678c2ecf20Sopenharmony_ci else 7688c2ecf20Sopenharmony_ci h++; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci if (h == NETDEV_HASHENTRIES + 1) { 7718c2ecf20Sopenharmony_ci if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 7728c2ecf20Sopenharmony_ci net->ipv6.devconf_dflt, 7738c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 7748c2ecf20Sopenharmony_ci nlh->nlmsg_seq, 7758c2ecf20Sopenharmony_ci RTM_NEWNETCONF, NLM_F_MULTI, 7768c2ecf20Sopenharmony_ci NETCONFA_ALL) < 0) 7778c2ecf20Sopenharmony_ci goto done; 7788c2ecf20Sopenharmony_ci else 7798c2ecf20Sopenharmony_ci h++; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_cidone: 7828c2ecf20Sopenharmony_ci cb->args[0] = h; 7838c2ecf20Sopenharmony_ci cb->args[1] = idx; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci return skb->len; 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 7898c2ecf20Sopenharmony_cistatic void dev_forward_change(struct inet6_dev *idev) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct net_device *dev; 7928c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa; 7938c2ecf20Sopenharmony_ci LIST_HEAD(tmp_addr_list); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (!idev) 7968c2ecf20Sopenharmony_ci return; 7978c2ecf20Sopenharmony_ci dev = idev->dev; 7988c2ecf20Sopenharmony_ci if (idev->cnf.forwarding) 7998c2ecf20Sopenharmony_ci dev_disable_lro(dev); 8008c2ecf20Sopenharmony_ci if (dev->flags & IFF_MULTICAST) { 8018c2ecf20Sopenharmony_ci if (idev->cnf.forwarding) { 8028c2ecf20Sopenharmony_ci ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); 8038c2ecf20Sopenharmony_ci ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allrouters); 8048c2ecf20Sopenharmony_ci ipv6_dev_mc_inc(dev, &in6addr_sitelocal_allrouters); 8058c2ecf20Sopenharmony_ci } else { 8068c2ecf20Sopenharmony_ci ipv6_dev_mc_dec(dev, &in6addr_linklocal_allrouters); 8078c2ecf20Sopenharmony_ci ipv6_dev_mc_dec(dev, &in6addr_interfacelocal_allrouters); 8088c2ecf20Sopenharmony_ci ipv6_dev_mc_dec(dev, &in6addr_sitelocal_allrouters); 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 8138c2ecf20Sopenharmony_ci list_for_each_entry(ifa, &idev->addr_list, if_list) { 8148c2ecf20Sopenharmony_ci if (ifa->flags&IFA_F_TENTATIVE) 8158c2ecf20Sopenharmony_ci continue; 8168c2ecf20Sopenharmony_ci list_add_tail(&ifa->if_list_aux, &tmp_addr_list); 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci while (!list_empty(&tmp_addr_list)) { 8218c2ecf20Sopenharmony_ci ifa = list_first_entry(&tmp_addr_list, 8228c2ecf20Sopenharmony_ci struct inet6_ifaddr, if_list_aux); 8238c2ecf20Sopenharmony_ci list_del(&ifa->if_list_aux); 8248c2ecf20Sopenharmony_ci if (idev->cnf.forwarding) 8258c2ecf20Sopenharmony_ci addrconf_join_anycast(ifa); 8268c2ecf20Sopenharmony_ci else 8278c2ecf20Sopenharmony_ci addrconf_leave_anycast(ifa); 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, 8318c2ecf20Sopenharmony_ci NETCONFA_FORWARDING, 8328c2ecf20Sopenharmony_ci dev->ifindex, &idev->cnf); 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cistatic void addrconf_forward_change(struct net *net, __s32 newf) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct net_device *dev; 8398c2ecf20Sopenharmony_ci struct inet6_dev *idev; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci for_each_netdev(net, dev) { 8428c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 8438c2ecf20Sopenharmony_ci if (idev) { 8448c2ecf20Sopenharmony_ci int changed = (!idev->cnf.forwarding) ^ (!newf); 8458c2ecf20Sopenharmony_ci idev->cnf.forwarding = newf; 8468c2ecf20Sopenharmony_ci if (changed) 8478c2ecf20Sopenharmony_ci dev_forward_change(idev); 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci struct net *net; 8558c2ecf20Sopenharmony_ci int old; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (!rtnl_trylock()) 8588c2ecf20Sopenharmony_ci return restart_syscall(); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci net = (struct net *)table->extra2; 8618c2ecf20Sopenharmony_ci old = *p; 8628c2ecf20Sopenharmony_ci *p = newf; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (p == &net->ipv6.devconf_dflt->forwarding) { 8658c2ecf20Sopenharmony_ci if ((!newf) ^ (!old)) 8668c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 8678c2ecf20Sopenharmony_ci NETCONFA_FORWARDING, 8688c2ecf20Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT, 8698c2ecf20Sopenharmony_ci net->ipv6.devconf_dflt); 8708c2ecf20Sopenharmony_ci rtnl_unlock(); 8718c2ecf20Sopenharmony_ci return 0; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (p == &net->ipv6.devconf_all->forwarding) { 8758c2ecf20Sopenharmony_ci int old_dflt = net->ipv6.devconf_dflt->forwarding; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci net->ipv6.devconf_dflt->forwarding = newf; 8788c2ecf20Sopenharmony_ci if ((!newf) ^ (!old_dflt)) 8798c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 8808c2ecf20Sopenharmony_ci NETCONFA_FORWARDING, 8818c2ecf20Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT, 8828c2ecf20Sopenharmony_ci net->ipv6.devconf_dflt); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci addrconf_forward_change(net, newf); 8858c2ecf20Sopenharmony_ci if ((!newf) ^ (!old)) 8868c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 8878c2ecf20Sopenharmony_ci NETCONFA_FORWARDING, 8888c2ecf20Sopenharmony_ci NETCONFA_IFINDEX_ALL, 8898c2ecf20Sopenharmony_ci net->ipv6.devconf_all); 8908c2ecf20Sopenharmony_ci } else if ((!newf) ^ (!old)) 8918c2ecf20Sopenharmony_ci dev_forward_change((struct inet6_dev *)table->extra1); 8928c2ecf20Sopenharmony_ci rtnl_unlock(); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (newf) 8958c2ecf20Sopenharmony_ci rt6_purge_dflt_routers(net); 8968c2ecf20Sopenharmony_ci return 1; 8978c2ecf20Sopenharmony_ci} 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic void addrconf_linkdown_change(struct net *net, __s32 newf) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci struct net_device *dev; 9028c2ecf20Sopenharmony_ci struct inet6_dev *idev; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci for_each_netdev(net, dev) { 9058c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 9068c2ecf20Sopenharmony_ci if (idev) { 9078c2ecf20Sopenharmony_ci int changed = (!idev->cnf.ignore_routes_with_linkdown) ^ (!newf); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci idev->cnf.ignore_routes_with_linkdown = newf; 9108c2ecf20Sopenharmony_ci if (changed) 9118c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(dev_net(dev), 9128c2ecf20Sopenharmony_ci RTM_NEWNETCONF, 9138c2ecf20Sopenharmony_ci NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 9148c2ecf20Sopenharmony_ci dev->ifindex, 9158c2ecf20Sopenharmony_ci &idev->cnf); 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistatic int addrconf_fixup_linkdown(struct ctl_table *table, int *p, int newf) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci struct net *net; 9238c2ecf20Sopenharmony_ci int old; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci if (!rtnl_trylock()) 9268c2ecf20Sopenharmony_ci return restart_syscall(); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci net = (struct net *)table->extra2; 9298c2ecf20Sopenharmony_ci old = *p; 9308c2ecf20Sopenharmony_ci *p = newf; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci if (p == &net->ipv6.devconf_dflt->ignore_routes_with_linkdown) { 9338c2ecf20Sopenharmony_ci if ((!newf) ^ (!old)) 9348c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(net, 9358c2ecf20Sopenharmony_ci RTM_NEWNETCONF, 9368c2ecf20Sopenharmony_ci NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 9378c2ecf20Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT, 9388c2ecf20Sopenharmony_ci net->ipv6.devconf_dflt); 9398c2ecf20Sopenharmony_ci rtnl_unlock(); 9408c2ecf20Sopenharmony_ci return 0; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (p == &net->ipv6.devconf_all->ignore_routes_with_linkdown) { 9448c2ecf20Sopenharmony_ci net->ipv6.devconf_dflt->ignore_routes_with_linkdown = newf; 9458c2ecf20Sopenharmony_ci addrconf_linkdown_change(net, newf); 9468c2ecf20Sopenharmony_ci if ((!newf) ^ (!old)) 9478c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(net, 9488c2ecf20Sopenharmony_ci RTM_NEWNETCONF, 9498c2ecf20Sopenharmony_ci NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 9508c2ecf20Sopenharmony_ci NETCONFA_IFINDEX_ALL, 9518c2ecf20Sopenharmony_ci net->ipv6.devconf_all); 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci rtnl_unlock(); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci return 1; 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci#endif 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci/* Nobody refers to this ifaddr, destroy it */ 9618c2ecf20Sopenharmony_civoid inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci WARN_ON(!hlist_unhashed(&ifp->addr_lst)); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci#ifdef NET_REFCNT_DEBUG 9668c2ecf20Sopenharmony_ci pr_debug("%s\n", __func__); 9678c2ecf20Sopenharmony_ci#endif 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci in6_dev_put(ifp->idev); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci if (cancel_delayed_work(&ifp->dad_work)) 9728c2ecf20Sopenharmony_ci pr_notice("delayed DAD work was pending while freeing ifa=%p\n", 9738c2ecf20Sopenharmony_ci ifp); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci if (ifp->state != INET6_IFADDR_STATE_DEAD) { 9768c2ecf20Sopenharmony_ci pr_warn("Freeing alive inet6 address %p\n", ifp); 9778c2ecf20Sopenharmony_ci return; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci kfree_rcu(ifp, rcu); 9818c2ecf20Sopenharmony_ci} 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic void 9848c2ecf20Sopenharmony_ciipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) 9858c2ecf20Sopenharmony_ci{ 9868c2ecf20Sopenharmony_ci struct list_head *p; 9878c2ecf20Sopenharmony_ci int ifp_scope = ipv6_addr_src_scope(&ifp->addr); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci /* 9908c2ecf20Sopenharmony_ci * Each device address list is sorted in order of scope - 9918c2ecf20Sopenharmony_ci * global before linklocal. 9928c2ecf20Sopenharmony_ci */ 9938c2ecf20Sopenharmony_ci list_for_each(p, &idev->addr_list) { 9948c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa 9958c2ecf20Sopenharmony_ci = list_entry(p, struct inet6_ifaddr, if_list); 9968c2ecf20Sopenharmony_ci if (ifp_scope >= ipv6_addr_src_scope(&ifa->addr)) 9978c2ecf20Sopenharmony_ci break; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci list_add_tail_rcu(&ifp->if_list, p); 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic u32 inet6_addr_hash(const struct net *net, const struct in6_addr *addr) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci u32 val = ipv6_addr_hash(addr) ^ net_hash_mix(net); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci return hash_32(val, IN6_ADDR_HSIZE_SHIFT); 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_cistatic bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, 10118c2ecf20Sopenharmony_ci struct net_device *dev, unsigned int hash) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci hlist_for_each_entry(ifp, &inet6_addr_lst[hash], addr_lst) { 10168c2ecf20Sopenharmony_ci if (!net_eq(dev_net(ifp->idev->dev), net)) 10178c2ecf20Sopenharmony_ci continue; 10188c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&ifp->addr, addr)) { 10198c2ecf20Sopenharmony_ci if (!dev || ifp->idev->dev == dev) 10208c2ecf20Sopenharmony_ci return true; 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci return false; 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic int ipv6_add_addr_hash(struct net_device *dev, struct inet6_ifaddr *ifa) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci unsigned int hash = inet6_addr_hash(dev_net(dev), &ifa->addr); 10298c2ecf20Sopenharmony_ci int err = 0; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci spin_lock(&addrconf_hash_lock); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci /* Ignore adding duplicate addresses on an interface */ 10348c2ecf20Sopenharmony_ci if (ipv6_chk_same_addr(dev_net(dev), &ifa->addr, dev, hash)) { 10358c2ecf20Sopenharmony_ci netdev_dbg(dev, "ipv6_add_addr: already assigned\n"); 10368c2ecf20Sopenharmony_ci err = -EEXIST; 10378c2ecf20Sopenharmony_ci } else { 10388c2ecf20Sopenharmony_ci hlist_add_head_rcu(&ifa->addr_lst, &inet6_addr_lst[hash]); 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci spin_unlock(&addrconf_hash_lock); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci return err; 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci/* On success it returns ifp with increased reference count */ 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_cistatic struct inet6_ifaddr * 10498c2ecf20Sopenharmony_ciipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg, 10508c2ecf20Sopenharmony_ci bool can_block, struct netlink_ext_ack *extack) 10518c2ecf20Sopenharmony_ci{ 10528c2ecf20Sopenharmony_ci gfp_t gfp_flags = can_block ? GFP_KERNEL : GFP_ATOMIC; 10538c2ecf20Sopenharmony_ci int addr_type = ipv6_addr_type(cfg->pfx); 10548c2ecf20Sopenharmony_ci struct net *net = dev_net(idev->dev); 10558c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa = NULL; 10568c2ecf20Sopenharmony_ci struct fib6_info *f6i = NULL; 10578c2ecf20Sopenharmony_ci int err = 0; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (addr_type == IPV6_ADDR_ANY || 10608c2ecf20Sopenharmony_ci (addr_type & IPV6_ADDR_MULTICAST && 10618c2ecf20Sopenharmony_ci !(cfg->ifa_flags & IFA_F_MCAUTOJOIN)) || 10628c2ecf20Sopenharmony_ci (!(idev->dev->flags & IFF_LOOPBACK) && 10638c2ecf20Sopenharmony_ci !netif_is_l3_master(idev->dev) && 10648c2ecf20Sopenharmony_ci addr_type & IPV6_ADDR_LOOPBACK)) 10658c2ecf20Sopenharmony_ci return ERR_PTR(-EADDRNOTAVAIL); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (idev->dead) { 10688c2ecf20Sopenharmony_ci err = -ENODEV; /*XXX*/ 10698c2ecf20Sopenharmony_ci goto out; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci if (idev->cnf.disable_ipv6) { 10738c2ecf20Sopenharmony_ci err = -EACCES; 10748c2ecf20Sopenharmony_ci goto out; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci /* validator notifier needs to be blocking; 10788c2ecf20Sopenharmony_ci * do not call in atomic context 10798c2ecf20Sopenharmony_ci */ 10808c2ecf20Sopenharmony_ci if (can_block) { 10818c2ecf20Sopenharmony_ci struct in6_validator_info i6vi = { 10828c2ecf20Sopenharmony_ci .i6vi_addr = *cfg->pfx, 10838c2ecf20Sopenharmony_ci .i6vi_dev = idev, 10848c2ecf20Sopenharmony_ci .extack = extack, 10858c2ecf20Sopenharmony_ci }; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci err = inet6addr_validator_notifier_call_chain(NETDEV_UP, &i6vi); 10888c2ecf20Sopenharmony_ci err = notifier_to_errno(err); 10898c2ecf20Sopenharmony_ci if (err < 0) 10908c2ecf20Sopenharmony_ci goto out; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci ifa = kzalloc(sizeof(*ifa), gfp_flags); 10948c2ecf20Sopenharmony_ci if (!ifa) { 10958c2ecf20Sopenharmony_ci err = -ENOBUFS; 10968c2ecf20Sopenharmony_ci goto out; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci f6i = addrconf_f6i_alloc(net, idev, cfg->pfx, false, gfp_flags); 11008c2ecf20Sopenharmony_ci if (IS_ERR(f6i)) { 11018c2ecf20Sopenharmony_ci err = PTR_ERR(f6i); 11028c2ecf20Sopenharmony_ci f6i = NULL; 11038c2ecf20Sopenharmony_ci goto out; 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci neigh_parms_data_state_setall(idev->nd_parms); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci ifa->addr = *cfg->pfx; 11098c2ecf20Sopenharmony_ci if (cfg->peer_pfx) 11108c2ecf20Sopenharmony_ci ifa->peer_addr = *cfg->peer_pfx; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci spin_lock_init(&ifa->lock); 11138c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work); 11148c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&ifa->addr_lst); 11158c2ecf20Sopenharmony_ci ifa->scope = cfg->scope; 11168c2ecf20Sopenharmony_ci ifa->prefix_len = cfg->plen; 11178c2ecf20Sopenharmony_ci ifa->rt_priority = cfg->rt_priority; 11188c2ecf20Sopenharmony_ci ifa->flags = cfg->ifa_flags; 11198c2ecf20Sopenharmony_ci /* No need to add the TENTATIVE flag for addresses with NODAD */ 11208c2ecf20Sopenharmony_ci if (!(cfg->ifa_flags & IFA_F_NODAD)) 11218c2ecf20Sopenharmony_ci ifa->flags |= IFA_F_TENTATIVE; 11228c2ecf20Sopenharmony_ci ifa->valid_lft = cfg->valid_lft; 11238c2ecf20Sopenharmony_ci ifa->prefered_lft = cfg->preferred_lft; 11248c2ecf20Sopenharmony_ci ifa->cstamp = ifa->tstamp = jiffies; 11258c2ecf20Sopenharmony_ci ifa->tokenized = false; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci ifa->rt = f6i; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci ifa->idev = idev; 11308c2ecf20Sopenharmony_ci in6_dev_hold(idev); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci /* For caller */ 11338c2ecf20Sopenharmony_ci refcount_set(&ifa->refcnt, 1); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci rcu_read_lock_bh(); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci err = ipv6_add_addr_hash(idev->dev, ifa); 11388c2ecf20Sopenharmony_ci if (err < 0) { 11398c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 11408c2ecf20Sopenharmony_ci goto out; 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci write_lock(&idev->lock); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci /* Add to inet6_dev unicast addr list. */ 11468c2ecf20Sopenharmony_ci ipv6_link_dev_addr(idev, ifa); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (ifa->flags&IFA_F_TEMPORARY) { 11498c2ecf20Sopenharmony_ci list_add(&ifa->tmp_list, &idev->tempaddr_list); 11508c2ecf20Sopenharmony_ci in6_ifa_hold(ifa); 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci in6_ifa_hold(ifa); 11548c2ecf20Sopenharmony_ci write_unlock(&idev->lock); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci inet6addr_notifier_call_chain(NETDEV_UP, ifa); 11598c2ecf20Sopenharmony_ciout: 11608c2ecf20Sopenharmony_ci if (unlikely(err < 0)) { 11618c2ecf20Sopenharmony_ci fib6_info_release(f6i); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (ifa) { 11648c2ecf20Sopenharmony_ci if (ifa->idev) 11658c2ecf20Sopenharmony_ci in6_dev_put(ifa->idev); 11668c2ecf20Sopenharmony_ci kfree(ifa); 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci ifa = ERR_PTR(err); 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci return ifa; 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_cienum cleanup_prefix_rt_t { 11758c2ecf20Sopenharmony_ci CLEANUP_PREFIX_RT_NOP, /* no cleanup action for prefix route */ 11768c2ecf20Sopenharmony_ci CLEANUP_PREFIX_RT_DEL, /* delete the prefix route */ 11778c2ecf20Sopenharmony_ci CLEANUP_PREFIX_RT_EXPIRE, /* update the lifetime of the prefix route */ 11788c2ecf20Sopenharmony_ci}; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci/* 11818c2ecf20Sopenharmony_ci * Check, whether the prefix for ifp would still need a prefix route 11828c2ecf20Sopenharmony_ci * after deleting ifp. The function returns one of the CLEANUP_PREFIX_RT_* 11838c2ecf20Sopenharmony_ci * constants. 11848c2ecf20Sopenharmony_ci * 11858c2ecf20Sopenharmony_ci * 1) we don't purge prefix if address was not permanent. 11868c2ecf20Sopenharmony_ci * prefix is managed by its own lifetime. 11878c2ecf20Sopenharmony_ci * 2) we also don't purge, if the address was IFA_F_NOPREFIXROUTE. 11888c2ecf20Sopenharmony_ci * 3) if there are no addresses, delete prefix. 11898c2ecf20Sopenharmony_ci * 4) if there are still other permanent address(es), 11908c2ecf20Sopenharmony_ci * corresponding prefix is still permanent. 11918c2ecf20Sopenharmony_ci * 5) if there are still other addresses with IFA_F_NOPREFIXROUTE, 11928c2ecf20Sopenharmony_ci * don't purge the prefix, assume user space is managing it. 11938c2ecf20Sopenharmony_ci * 6) otherwise, update prefix lifetime to the 11948c2ecf20Sopenharmony_ci * longest valid lifetime among the corresponding 11958c2ecf20Sopenharmony_ci * addresses on the device. 11968c2ecf20Sopenharmony_ci * Note: subsequent RA will update lifetime. 11978c2ecf20Sopenharmony_ci **/ 11988c2ecf20Sopenharmony_cistatic enum cleanup_prefix_rt_t 11998c2ecf20Sopenharmony_cicheck_cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long *expires) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa; 12028c2ecf20Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 12038c2ecf20Sopenharmony_ci unsigned long lifetime; 12048c2ecf20Sopenharmony_ci enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_DEL; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci *expires = jiffies; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci list_for_each_entry(ifa, &idev->addr_list, if_list) { 12098c2ecf20Sopenharmony_ci if (ifa == ifp) 12108c2ecf20Sopenharmony_ci continue; 12118c2ecf20Sopenharmony_ci if (ifa->prefix_len != ifp->prefix_len || 12128c2ecf20Sopenharmony_ci !ipv6_prefix_equal(&ifa->addr, &ifp->addr, 12138c2ecf20Sopenharmony_ci ifp->prefix_len)) 12148c2ecf20Sopenharmony_ci continue; 12158c2ecf20Sopenharmony_ci if (ifa->flags & (IFA_F_PERMANENT | IFA_F_NOPREFIXROUTE)) 12168c2ecf20Sopenharmony_ci return CLEANUP_PREFIX_RT_NOP; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci action = CLEANUP_PREFIX_RT_EXPIRE; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci spin_lock(&ifa->lock); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ); 12238c2ecf20Sopenharmony_ci /* 12248c2ecf20Sopenharmony_ci * Note: Because this address is 12258c2ecf20Sopenharmony_ci * not permanent, lifetime < 12268c2ecf20Sopenharmony_ci * LONG_MAX / HZ here. 12278c2ecf20Sopenharmony_ci */ 12288c2ecf20Sopenharmony_ci if (time_before(*expires, ifa->tstamp + lifetime * HZ)) 12298c2ecf20Sopenharmony_ci *expires = ifa->tstamp + lifetime * HZ; 12308c2ecf20Sopenharmony_ci spin_unlock(&ifa->lock); 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci return action; 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic void 12378c2ecf20Sopenharmony_cicleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires, 12388c2ecf20Sopenharmony_ci bool del_rt, bool del_peer) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci struct fib6_info *f6i; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci f6i = addrconf_get_prefix_route(del_peer ? &ifp->peer_addr : &ifp->addr, 12438c2ecf20Sopenharmony_ci ifp->prefix_len, 12448c2ecf20Sopenharmony_ci ifp->idev->dev, 0, RTF_DEFAULT, true); 12458c2ecf20Sopenharmony_ci if (f6i) { 12468c2ecf20Sopenharmony_ci if (del_rt) 12478c2ecf20Sopenharmony_ci ip6_del_rt(dev_net(ifp->idev->dev), f6i, false); 12488c2ecf20Sopenharmony_ci else { 12498c2ecf20Sopenharmony_ci if (!(f6i->fib6_flags & RTF_EXPIRES)) 12508c2ecf20Sopenharmony_ci fib6_set_expires(f6i, expires); 12518c2ecf20Sopenharmony_ci fib6_info_release(f6i); 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci/* This function wants to get referenced ifp and releases it before return */ 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_cistatic void ipv6_del_addr(struct inet6_ifaddr *ifp) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci int state; 12628c2ecf20Sopenharmony_ci enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_NOP; 12638c2ecf20Sopenharmony_ci unsigned long expires; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci ASSERT_RTNL(); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 12688c2ecf20Sopenharmony_ci state = ifp->state; 12698c2ecf20Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_DEAD; 12708c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci if (state == INET6_IFADDR_STATE_DEAD) 12738c2ecf20Sopenharmony_ci goto out; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci spin_lock_bh(&addrconf_hash_lock); 12768c2ecf20Sopenharmony_ci hlist_del_init_rcu(&ifp->addr_lst); 12778c2ecf20Sopenharmony_ci spin_unlock_bh(&addrconf_hash_lock); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci write_lock_bh(&ifp->idev->lock); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci if (ifp->flags&IFA_F_TEMPORARY) { 12828c2ecf20Sopenharmony_ci list_del(&ifp->tmp_list); 12838c2ecf20Sopenharmony_ci if (ifp->ifpub) { 12848c2ecf20Sopenharmony_ci in6_ifa_put(ifp->ifpub); 12858c2ecf20Sopenharmony_ci ifp->ifpub = NULL; 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci __in6_ifa_put(ifp); 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci if (ifp->flags & IFA_F_PERMANENT && !(ifp->flags & IFA_F_NOPREFIXROUTE)) 12918c2ecf20Sopenharmony_ci action = check_cleanup_prefix_route(ifp, &expires); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci list_del_rcu(&ifp->if_list); 12948c2ecf20Sopenharmony_ci __in6_ifa_put(ifp); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci write_unlock_bh(&ifp->idev->lock); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci addrconf_del_dad_work(ifp); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci ipv6_ifa_notify(RTM_DELADDR, ifp); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci inet6addr_notifier_call_chain(NETDEV_DOWN, ifp); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci if (action != CLEANUP_PREFIX_RT_NOP) { 13058c2ecf20Sopenharmony_ci cleanup_prefix_route(ifp, expires, 13068c2ecf20Sopenharmony_ci action == CLEANUP_PREFIX_RT_DEL, false); 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci /* clean up prefsrc entries */ 13108c2ecf20Sopenharmony_ci rt6_remove_prefsrc(ifp); 13118c2ecf20Sopenharmony_ciout: 13128c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 13138c2ecf20Sopenharmony_ci} 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_cistatic int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block) 13168c2ecf20Sopenharmony_ci{ 13178c2ecf20Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 13188c2ecf20Sopenharmony_ci unsigned long tmp_tstamp, age; 13198c2ecf20Sopenharmony_ci unsigned long regen_advance; 13208c2ecf20Sopenharmony_ci unsigned long now = jiffies; 13218c2ecf20Sopenharmony_ci s32 cnf_temp_preferred_lft; 13228c2ecf20Sopenharmony_ci struct inet6_ifaddr *ift; 13238c2ecf20Sopenharmony_ci struct ifa6_config cfg; 13248c2ecf20Sopenharmony_ci long max_desync_factor; 13258c2ecf20Sopenharmony_ci struct in6_addr addr; 13268c2ecf20Sopenharmony_ci int ret = 0; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ciretry: 13318c2ecf20Sopenharmony_ci in6_dev_hold(idev); 13328c2ecf20Sopenharmony_ci if (idev->cnf.use_tempaddr <= 0) { 13338c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 13348c2ecf20Sopenharmony_ci pr_info("%s: use_tempaddr is disabled\n", __func__); 13358c2ecf20Sopenharmony_ci in6_dev_put(idev); 13368c2ecf20Sopenharmony_ci ret = -1; 13378c2ecf20Sopenharmony_ci goto out; 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 13408c2ecf20Sopenharmony_ci if (ifp->regen_count++ >= idev->cnf.regen_max_retry) { 13418c2ecf20Sopenharmony_ci idev->cnf.use_tempaddr = -1; /*XXX*/ 13428c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 13438c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 13448c2ecf20Sopenharmony_ci pr_warn("%s: regeneration time exceeded - disabled temporary address support\n", 13458c2ecf20Sopenharmony_ci __func__); 13468c2ecf20Sopenharmony_ci in6_dev_put(idev); 13478c2ecf20Sopenharmony_ci ret = -1; 13488c2ecf20Sopenharmony_ci goto out; 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci in6_ifa_hold(ifp); 13518c2ecf20Sopenharmony_ci memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 13528c2ecf20Sopenharmony_ci ipv6_gen_rnd_iid(&addr); 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci age = (now - ifp->tstamp) / HZ; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci regen_advance = idev->cnf.regen_max_retry * 13578c2ecf20Sopenharmony_ci idev->cnf.dad_transmits * 13588c2ecf20Sopenharmony_ci max(NEIGH_VAR(idev->nd_parms, RETRANS_TIME), HZ/100) / HZ; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci /* recalculate max_desync_factor each time and update 13618c2ecf20Sopenharmony_ci * idev->desync_factor if it's larger 13628c2ecf20Sopenharmony_ci */ 13638c2ecf20Sopenharmony_ci cnf_temp_preferred_lft = READ_ONCE(idev->cnf.temp_prefered_lft); 13648c2ecf20Sopenharmony_ci max_desync_factor = min_t(long, 13658c2ecf20Sopenharmony_ci idev->cnf.max_desync_factor, 13668c2ecf20Sopenharmony_ci cnf_temp_preferred_lft - regen_advance); 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci if (unlikely(idev->desync_factor > max_desync_factor)) { 13698c2ecf20Sopenharmony_ci if (max_desync_factor > 0) { 13708c2ecf20Sopenharmony_ci get_random_bytes(&idev->desync_factor, 13718c2ecf20Sopenharmony_ci sizeof(idev->desync_factor)); 13728c2ecf20Sopenharmony_ci idev->desync_factor %= max_desync_factor; 13738c2ecf20Sopenharmony_ci } else { 13748c2ecf20Sopenharmony_ci idev->desync_factor = 0; 13758c2ecf20Sopenharmony_ci } 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci memset(&cfg, 0, sizeof(cfg)); 13798c2ecf20Sopenharmony_ci cfg.valid_lft = min_t(__u32, ifp->valid_lft, 13808c2ecf20Sopenharmony_ci idev->cnf.temp_valid_lft + age); 13818c2ecf20Sopenharmony_ci cfg.preferred_lft = cnf_temp_preferred_lft + age - idev->desync_factor; 13828c2ecf20Sopenharmony_ci cfg.preferred_lft = min_t(__u32, ifp->prefered_lft, cfg.preferred_lft); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci cfg.plen = ifp->prefix_len; 13858c2ecf20Sopenharmony_ci tmp_tstamp = ifp->tstamp; 13868c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci /* A temporary address is created only if this calculated Preferred 13918c2ecf20Sopenharmony_ci * Lifetime is greater than REGEN_ADVANCE time units. In particular, 13928c2ecf20Sopenharmony_ci * an implementation must not create a temporary address with a zero 13938c2ecf20Sopenharmony_ci * Preferred Lifetime. 13948c2ecf20Sopenharmony_ci * Use age calculation as in addrconf_verify to avoid unnecessary 13958c2ecf20Sopenharmony_ci * temporary addresses being generated. 13968c2ecf20Sopenharmony_ci */ 13978c2ecf20Sopenharmony_ci age = (now - tmp_tstamp + ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 13988c2ecf20Sopenharmony_ci if (cfg.preferred_lft <= regen_advance + age) { 13998c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 14008c2ecf20Sopenharmony_ci in6_dev_put(idev); 14018c2ecf20Sopenharmony_ci ret = -1; 14028c2ecf20Sopenharmony_ci goto out; 14038c2ecf20Sopenharmony_ci } 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci cfg.ifa_flags = IFA_F_TEMPORARY; 14068c2ecf20Sopenharmony_ci /* set in addrconf_prefix_rcv() */ 14078c2ecf20Sopenharmony_ci if (ifp->flags & IFA_F_OPTIMISTIC) 14088c2ecf20Sopenharmony_ci cfg.ifa_flags |= IFA_F_OPTIMISTIC; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci cfg.pfx = &addr; 14118c2ecf20Sopenharmony_ci cfg.scope = ipv6_addr_scope(cfg.pfx); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci ift = ipv6_add_addr(idev, &cfg, block, NULL); 14148c2ecf20Sopenharmony_ci if (IS_ERR(ift)) { 14158c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 14168c2ecf20Sopenharmony_ci in6_dev_put(idev); 14178c2ecf20Sopenharmony_ci pr_info("%s: retry temporary address regeneration\n", __func__); 14188c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 14198c2ecf20Sopenharmony_ci goto retry; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci spin_lock_bh(&ift->lock); 14238c2ecf20Sopenharmony_ci ift->ifpub = ifp; 14248c2ecf20Sopenharmony_ci ift->cstamp = now; 14258c2ecf20Sopenharmony_ci ift->tstamp = tmp_tstamp; 14268c2ecf20Sopenharmony_ci spin_unlock_bh(&ift->lock); 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci addrconf_dad_start(ift); 14298c2ecf20Sopenharmony_ci in6_ifa_put(ift); 14308c2ecf20Sopenharmony_ci in6_dev_put(idev); 14318c2ecf20Sopenharmony_ciout: 14328c2ecf20Sopenharmony_ci return ret; 14338c2ecf20Sopenharmony_ci} 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci/* 14368c2ecf20Sopenharmony_ci * Choose an appropriate source address (RFC3484) 14378c2ecf20Sopenharmony_ci */ 14388c2ecf20Sopenharmony_cienum { 14398c2ecf20Sopenharmony_ci IPV6_SADDR_RULE_INIT = 0, 14408c2ecf20Sopenharmony_ci IPV6_SADDR_RULE_LOCAL, 14418c2ecf20Sopenharmony_ci IPV6_SADDR_RULE_SCOPE, 14428c2ecf20Sopenharmony_ci IPV6_SADDR_RULE_PREFERRED, 14438c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MIP6 14448c2ecf20Sopenharmony_ci IPV6_SADDR_RULE_HOA, 14458c2ecf20Sopenharmony_ci#endif 14468c2ecf20Sopenharmony_ci IPV6_SADDR_RULE_OIF, 14478c2ecf20Sopenharmony_ci IPV6_SADDR_RULE_LABEL, 14488c2ecf20Sopenharmony_ci IPV6_SADDR_RULE_PRIVACY, 14498c2ecf20Sopenharmony_ci IPV6_SADDR_RULE_ORCHID, 14508c2ecf20Sopenharmony_ci IPV6_SADDR_RULE_PREFIX, 14518c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 14528c2ecf20Sopenharmony_ci IPV6_SADDR_RULE_NOT_OPTIMISTIC, 14538c2ecf20Sopenharmony_ci#endif 14548c2ecf20Sopenharmony_ci IPV6_SADDR_RULE_MAX 14558c2ecf20Sopenharmony_ci}; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_cistruct ipv6_saddr_score { 14588c2ecf20Sopenharmony_ci int rule; 14598c2ecf20Sopenharmony_ci int addr_type; 14608c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa; 14618c2ecf20Sopenharmony_ci DECLARE_BITMAP(scorebits, IPV6_SADDR_RULE_MAX); 14628c2ecf20Sopenharmony_ci int scopedist; 14638c2ecf20Sopenharmony_ci int matchlen; 14648c2ecf20Sopenharmony_ci}; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_cistruct ipv6_saddr_dst { 14678c2ecf20Sopenharmony_ci const struct in6_addr *addr; 14688c2ecf20Sopenharmony_ci int ifindex; 14698c2ecf20Sopenharmony_ci int scope; 14708c2ecf20Sopenharmony_ci int label; 14718c2ecf20Sopenharmony_ci unsigned int prefs; 14728c2ecf20Sopenharmony_ci}; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_cistatic inline int ipv6_saddr_preferred(int type) 14758c2ecf20Sopenharmony_ci{ 14768c2ecf20Sopenharmony_ci if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4|IPV6_ADDR_LOOPBACK)) 14778c2ecf20Sopenharmony_ci return 1; 14788c2ecf20Sopenharmony_ci return 0; 14798c2ecf20Sopenharmony_ci} 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_cistatic bool ipv6_use_optimistic_addr(struct net *net, 14828c2ecf20Sopenharmony_ci struct inet6_dev *idev) 14838c2ecf20Sopenharmony_ci{ 14848c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 14858c2ecf20Sopenharmony_ci if (!idev) 14868c2ecf20Sopenharmony_ci return false; 14878c2ecf20Sopenharmony_ci if (!net->ipv6.devconf_all->optimistic_dad && !idev->cnf.optimistic_dad) 14888c2ecf20Sopenharmony_ci return false; 14898c2ecf20Sopenharmony_ci if (!net->ipv6.devconf_all->use_optimistic && !idev->cnf.use_optimistic) 14908c2ecf20Sopenharmony_ci return false; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci return true; 14938c2ecf20Sopenharmony_ci#else 14948c2ecf20Sopenharmony_ci return false; 14958c2ecf20Sopenharmony_ci#endif 14968c2ecf20Sopenharmony_ci} 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_cistatic bool ipv6_allow_optimistic_dad(struct net *net, 14998c2ecf20Sopenharmony_ci struct inet6_dev *idev) 15008c2ecf20Sopenharmony_ci{ 15018c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 15028c2ecf20Sopenharmony_ci if (!idev) 15038c2ecf20Sopenharmony_ci return false; 15048c2ecf20Sopenharmony_ci if (!net->ipv6.devconf_all->optimistic_dad && !idev->cnf.optimistic_dad) 15058c2ecf20Sopenharmony_ci return false; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci return true; 15088c2ecf20Sopenharmony_ci#else 15098c2ecf20Sopenharmony_ci return false; 15108c2ecf20Sopenharmony_ci#endif 15118c2ecf20Sopenharmony_ci} 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_cistatic int ipv6_get_saddr_eval(struct net *net, 15148c2ecf20Sopenharmony_ci struct ipv6_saddr_score *score, 15158c2ecf20Sopenharmony_ci struct ipv6_saddr_dst *dst, 15168c2ecf20Sopenharmony_ci int i) 15178c2ecf20Sopenharmony_ci{ 15188c2ecf20Sopenharmony_ci int ret; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci if (i <= score->rule) { 15218c2ecf20Sopenharmony_ci switch (i) { 15228c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_SCOPE: 15238c2ecf20Sopenharmony_ci ret = score->scopedist; 15248c2ecf20Sopenharmony_ci break; 15258c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_PREFIX: 15268c2ecf20Sopenharmony_ci ret = score->matchlen; 15278c2ecf20Sopenharmony_ci break; 15288c2ecf20Sopenharmony_ci default: 15298c2ecf20Sopenharmony_ci ret = !!test_bit(i, score->scorebits); 15308c2ecf20Sopenharmony_ci } 15318c2ecf20Sopenharmony_ci goto out; 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci switch (i) { 15358c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_INIT: 15368c2ecf20Sopenharmony_ci /* Rule 0: remember if hiscore is not ready yet */ 15378c2ecf20Sopenharmony_ci ret = !!score->ifa; 15388c2ecf20Sopenharmony_ci break; 15398c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_LOCAL: 15408c2ecf20Sopenharmony_ci /* Rule 1: Prefer same address */ 15418c2ecf20Sopenharmony_ci ret = ipv6_addr_equal(&score->ifa->addr, dst->addr); 15428c2ecf20Sopenharmony_ci break; 15438c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_SCOPE: 15448c2ecf20Sopenharmony_ci /* Rule 2: Prefer appropriate scope 15458c2ecf20Sopenharmony_ci * 15468c2ecf20Sopenharmony_ci * ret 15478c2ecf20Sopenharmony_ci * ^ 15488c2ecf20Sopenharmony_ci * -1 | d 15 15498c2ecf20Sopenharmony_ci * ---+--+-+---> scope 15508c2ecf20Sopenharmony_ci * | 15518c2ecf20Sopenharmony_ci * | d is scope of the destination. 15528c2ecf20Sopenharmony_ci * B-d | \ 15538c2ecf20Sopenharmony_ci * | \ <- smaller scope is better if 15548c2ecf20Sopenharmony_ci * B-15 | \ if scope is enough for destination. 15558c2ecf20Sopenharmony_ci * | ret = B - scope (-1 <= scope >= d <= 15). 15568c2ecf20Sopenharmony_ci * d-C-1 | / 15578c2ecf20Sopenharmony_ci * |/ <- greater is better 15588c2ecf20Sopenharmony_ci * -C / if scope is not enough for destination. 15598c2ecf20Sopenharmony_ci * /| ret = scope - C (-1 <= d < scope <= 15). 15608c2ecf20Sopenharmony_ci * 15618c2ecf20Sopenharmony_ci * d - C - 1 < B -15 (for all -1 <= d <= 15). 15628c2ecf20Sopenharmony_ci * C > d + 14 - B >= 15 + 14 - B = 29 - B. 15638c2ecf20Sopenharmony_ci * Assume B = 0 and we get C > 29. 15648c2ecf20Sopenharmony_ci */ 15658c2ecf20Sopenharmony_ci ret = __ipv6_addr_src_scope(score->addr_type); 15668c2ecf20Sopenharmony_ci if (ret >= dst->scope) 15678c2ecf20Sopenharmony_ci ret = -ret; 15688c2ecf20Sopenharmony_ci else 15698c2ecf20Sopenharmony_ci ret -= 128; /* 30 is enough */ 15708c2ecf20Sopenharmony_ci score->scopedist = ret; 15718c2ecf20Sopenharmony_ci break; 15728c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_PREFERRED: 15738c2ecf20Sopenharmony_ci { 15748c2ecf20Sopenharmony_ci /* Rule 3: Avoid deprecated and optimistic addresses */ 15758c2ecf20Sopenharmony_ci u8 avoid = IFA_F_DEPRECATED; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci if (!ipv6_use_optimistic_addr(net, score->ifa->idev)) 15788c2ecf20Sopenharmony_ci avoid |= IFA_F_OPTIMISTIC; 15798c2ecf20Sopenharmony_ci ret = ipv6_saddr_preferred(score->addr_type) || 15808c2ecf20Sopenharmony_ci !(score->ifa->flags & avoid); 15818c2ecf20Sopenharmony_ci break; 15828c2ecf20Sopenharmony_ci } 15838c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MIP6 15848c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_HOA: 15858c2ecf20Sopenharmony_ci { 15868c2ecf20Sopenharmony_ci /* Rule 4: Prefer home address */ 15878c2ecf20Sopenharmony_ci int prefhome = !(dst->prefs & IPV6_PREFER_SRC_COA); 15888c2ecf20Sopenharmony_ci ret = !(score->ifa->flags & IFA_F_HOMEADDRESS) ^ prefhome; 15898c2ecf20Sopenharmony_ci break; 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci#endif 15928c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_OIF: 15938c2ecf20Sopenharmony_ci /* Rule 5: Prefer outgoing interface */ 15948c2ecf20Sopenharmony_ci ret = (!dst->ifindex || 15958c2ecf20Sopenharmony_ci dst->ifindex == score->ifa->idev->dev->ifindex); 15968c2ecf20Sopenharmony_ci break; 15978c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_LABEL: 15988c2ecf20Sopenharmony_ci /* Rule 6: Prefer matching label */ 15998c2ecf20Sopenharmony_ci ret = ipv6_addr_label(net, 16008c2ecf20Sopenharmony_ci &score->ifa->addr, score->addr_type, 16018c2ecf20Sopenharmony_ci score->ifa->idev->dev->ifindex) == dst->label; 16028c2ecf20Sopenharmony_ci break; 16038c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_PRIVACY: 16048c2ecf20Sopenharmony_ci { 16058c2ecf20Sopenharmony_ci /* Rule 7: Prefer public address 16068c2ecf20Sopenharmony_ci * Note: prefer temporary address if use_tempaddr >= 2 16078c2ecf20Sopenharmony_ci */ 16088c2ecf20Sopenharmony_ci int preftmp = dst->prefs & (IPV6_PREFER_SRC_PUBLIC|IPV6_PREFER_SRC_TMP) ? 16098c2ecf20Sopenharmony_ci !!(dst->prefs & IPV6_PREFER_SRC_TMP) : 16108c2ecf20Sopenharmony_ci score->ifa->idev->cnf.use_tempaddr >= 2; 16118c2ecf20Sopenharmony_ci ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ preftmp; 16128c2ecf20Sopenharmony_ci break; 16138c2ecf20Sopenharmony_ci } 16148c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_ORCHID: 16158c2ecf20Sopenharmony_ci /* Rule 8-: Prefer ORCHID vs ORCHID or 16168c2ecf20Sopenharmony_ci * non-ORCHID vs non-ORCHID 16178c2ecf20Sopenharmony_ci */ 16188c2ecf20Sopenharmony_ci ret = !(ipv6_addr_orchid(&score->ifa->addr) ^ 16198c2ecf20Sopenharmony_ci ipv6_addr_orchid(dst->addr)); 16208c2ecf20Sopenharmony_ci break; 16218c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_PREFIX: 16228c2ecf20Sopenharmony_ci /* Rule 8: Use longest matching prefix */ 16238c2ecf20Sopenharmony_ci ret = ipv6_addr_diff(&score->ifa->addr, dst->addr); 16248c2ecf20Sopenharmony_ci if (ret > score->ifa->prefix_len) 16258c2ecf20Sopenharmony_ci ret = score->ifa->prefix_len; 16268c2ecf20Sopenharmony_ci score->matchlen = ret; 16278c2ecf20Sopenharmony_ci break; 16288c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 16298c2ecf20Sopenharmony_ci case IPV6_SADDR_RULE_NOT_OPTIMISTIC: 16308c2ecf20Sopenharmony_ci /* Optimistic addresses still have lower precedence than other 16318c2ecf20Sopenharmony_ci * preferred addresses. 16328c2ecf20Sopenharmony_ci */ 16338c2ecf20Sopenharmony_ci ret = !(score->ifa->flags & IFA_F_OPTIMISTIC); 16348c2ecf20Sopenharmony_ci break; 16358c2ecf20Sopenharmony_ci#endif 16368c2ecf20Sopenharmony_ci default: 16378c2ecf20Sopenharmony_ci ret = 0; 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci if (ret) 16418c2ecf20Sopenharmony_ci __set_bit(i, score->scorebits); 16428c2ecf20Sopenharmony_ci score->rule = i; 16438c2ecf20Sopenharmony_ciout: 16448c2ecf20Sopenharmony_ci return ret; 16458c2ecf20Sopenharmony_ci} 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_cistatic int __ipv6_dev_get_saddr(struct net *net, 16488c2ecf20Sopenharmony_ci struct ipv6_saddr_dst *dst, 16498c2ecf20Sopenharmony_ci struct inet6_dev *idev, 16508c2ecf20Sopenharmony_ci struct ipv6_saddr_score *scores, 16518c2ecf20Sopenharmony_ci int hiscore_idx) 16528c2ecf20Sopenharmony_ci{ 16538c2ecf20Sopenharmony_ci struct ipv6_saddr_score *score = &scores[1 - hiscore_idx], *hiscore = &scores[hiscore_idx]; 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci list_for_each_entry_rcu(score->ifa, &idev->addr_list, if_list) { 16568c2ecf20Sopenharmony_ci int i; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci /* 16598c2ecf20Sopenharmony_ci * - Tentative Address (RFC2462 section 5.4) 16608c2ecf20Sopenharmony_ci * - A tentative address is not considered 16618c2ecf20Sopenharmony_ci * "assigned to an interface" in the traditional 16628c2ecf20Sopenharmony_ci * sense, unless it is also flagged as optimistic. 16638c2ecf20Sopenharmony_ci * - Candidate Source Address (section 4) 16648c2ecf20Sopenharmony_ci * - In any case, anycast addresses, multicast 16658c2ecf20Sopenharmony_ci * addresses, and the unspecified address MUST 16668c2ecf20Sopenharmony_ci * NOT be included in a candidate set. 16678c2ecf20Sopenharmony_ci */ 16688c2ecf20Sopenharmony_ci if ((score->ifa->flags & IFA_F_TENTATIVE) && 16698c2ecf20Sopenharmony_ci (!(score->ifa->flags & IFA_F_OPTIMISTIC))) 16708c2ecf20Sopenharmony_ci continue; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci score->addr_type = __ipv6_addr_type(&score->ifa->addr); 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci if (unlikely(score->addr_type == IPV6_ADDR_ANY || 16758c2ecf20Sopenharmony_ci score->addr_type & IPV6_ADDR_MULTICAST)) { 16768c2ecf20Sopenharmony_ci net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s", 16778c2ecf20Sopenharmony_ci idev->dev->name); 16788c2ecf20Sopenharmony_ci continue; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci score->rule = -1; 16828c2ecf20Sopenharmony_ci bitmap_zero(score->scorebits, IPV6_SADDR_RULE_MAX); 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) { 16858c2ecf20Sopenharmony_ci int minihiscore, miniscore; 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci minihiscore = ipv6_get_saddr_eval(net, hiscore, dst, i); 16888c2ecf20Sopenharmony_ci miniscore = ipv6_get_saddr_eval(net, score, dst, i); 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci if (minihiscore > miniscore) { 16918c2ecf20Sopenharmony_ci if (i == IPV6_SADDR_RULE_SCOPE && 16928c2ecf20Sopenharmony_ci score->scopedist > 0) { 16938c2ecf20Sopenharmony_ci /* 16948c2ecf20Sopenharmony_ci * special case: 16958c2ecf20Sopenharmony_ci * each remaining entry 16968c2ecf20Sopenharmony_ci * has too small (not enough) 16978c2ecf20Sopenharmony_ci * scope, because ifa entries 16988c2ecf20Sopenharmony_ci * are sorted by their scope 16998c2ecf20Sopenharmony_ci * values. 17008c2ecf20Sopenharmony_ci */ 17018c2ecf20Sopenharmony_ci goto out; 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci break; 17048c2ecf20Sopenharmony_ci } else if (minihiscore < miniscore) { 17058c2ecf20Sopenharmony_ci swap(hiscore, score); 17068c2ecf20Sopenharmony_ci hiscore_idx = 1 - hiscore_idx; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci /* restore our iterator */ 17098c2ecf20Sopenharmony_ci score->ifa = hiscore->ifa; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci break; 17128c2ecf20Sopenharmony_ci } 17138c2ecf20Sopenharmony_ci } 17148c2ecf20Sopenharmony_ci } 17158c2ecf20Sopenharmony_ciout: 17168c2ecf20Sopenharmony_ci return hiscore_idx; 17178c2ecf20Sopenharmony_ci} 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_cistatic int ipv6_get_saddr_master(struct net *net, 17208c2ecf20Sopenharmony_ci const struct net_device *dst_dev, 17218c2ecf20Sopenharmony_ci const struct net_device *master, 17228c2ecf20Sopenharmony_ci struct ipv6_saddr_dst *dst, 17238c2ecf20Sopenharmony_ci struct ipv6_saddr_score *scores, 17248c2ecf20Sopenharmony_ci int hiscore_idx) 17258c2ecf20Sopenharmony_ci{ 17268c2ecf20Sopenharmony_ci struct inet6_dev *idev; 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci idev = __in6_dev_get(dst_dev); 17298c2ecf20Sopenharmony_ci if (idev) 17308c2ecf20Sopenharmony_ci hiscore_idx = __ipv6_dev_get_saddr(net, dst, idev, 17318c2ecf20Sopenharmony_ci scores, hiscore_idx); 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci idev = __in6_dev_get(master); 17348c2ecf20Sopenharmony_ci if (idev) 17358c2ecf20Sopenharmony_ci hiscore_idx = __ipv6_dev_get_saddr(net, dst, idev, 17368c2ecf20Sopenharmony_ci scores, hiscore_idx); 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci return hiscore_idx; 17398c2ecf20Sopenharmony_ci} 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ciint ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, 17428c2ecf20Sopenharmony_ci const struct in6_addr *daddr, unsigned int prefs, 17438c2ecf20Sopenharmony_ci struct in6_addr *saddr) 17448c2ecf20Sopenharmony_ci{ 17458c2ecf20Sopenharmony_ci struct ipv6_saddr_score scores[2], *hiscore; 17468c2ecf20Sopenharmony_ci struct ipv6_saddr_dst dst; 17478c2ecf20Sopenharmony_ci struct inet6_dev *idev; 17488c2ecf20Sopenharmony_ci struct net_device *dev; 17498c2ecf20Sopenharmony_ci int dst_type; 17508c2ecf20Sopenharmony_ci bool use_oif_addr = false; 17518c2ecf20Sopenharmony_ci int hiscore_idx = 0; 17528c2ecf20Sopenharmony_ci int ret = 0; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci dst_type = __ipv6_addr_type(daddr); 17558c2ecf20Sopenharmony_ci dst.addr = daddr; 17568c2ecf20Sopenharmony_ci dst.ifindex = dst_dev ? dst_dev->ifindex : 0; 17578c2ecf20Sopenharmony_ci dst.scope = __ipv6_addr_src_scope(dst_type); 17588c2ecf20Sopenharmony_ci dst.label = ipv6_addr_label(net, daddr, dst_type, dst.ifindex); 17598c2ecf20Sopenharmony_ci dst.prefs = prefs; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci scores[hiscore_idx].rule = -1; 17628c2ecf20Sopenharmony_ci scores[hiscore_idx].ifa = NULL; 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci rcu_read_lock(); 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci /* Candidate Source Address (section 4) 17678c2ecf20Sopenharmony_ci * - multicast and link-local destination address, 17688c2ecf20Sopenharmony_ci * the set of candidate source address MUST only 17698c2ecf20Sopenharmony_ci * include addresses assigned to interfaces 17708c2ecf20Sopenharmony_ci * belonging to the same link as the outgoing 17718c2ecf20Sopenharmony_ci * interface. 17728c2ecf20Sopenharmony_ci * (- For site-local destination addresses, the 17738c2ecf20Sopenharmony_ci * set of candidate source addresses MUST only 17748c2ecf20Sopenharmony_ci * include addresses assigned to interfaces 17758c2ecf20Sopenharmony_ci * belonging to the same site as the outgoing 17768c2ecf20Sopenharmony_ci * interface.) 17778c2ecf20Sopenharmony_ci * - "It is RECOMMENDED that the candidate source addresses 17788c2ecf20Sopenharmony_ci * be the set of unicast addresses assigned to the 17798c2ecf20Sopenharmony_ci * interface that will be used to send to the destination 17808c2ecf20Sopenharmony_ci * (the 'outgoing' interface)." (RFC 6724) 17818c2ecf20Sopenharmony_ci */ 17828c2ecf20Sopenharmony_ci if (dst_dev) { 17838c2ecf20Sopenharmony_ci idev = __in6_dev_get(dst_dev); 17848c2ecf20Sopenharmony_ci if ((dst_type & IPV6_ADDR_MULTICAST) || 17858c2ecf20Sopenharmony_ci dst.scope <= IPV6_ADDR_SCOPE_LINKLOCAL || 17868c2ecf20Sopenharmony_ci (idev && idev->cnf.use_oif_addrs_only)) { 17878c2ecf20Sopenharmony_ci use_oif_addr = true; 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ci } 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci if (use_oif_addr) { 17928c2ecf20Sopenharmony_ci if (idev) 17938c2ecf20Sopenharmony_ci hiscore_idx = __ipv6_dev_get_saddr(net, &dst, idev, scores, hiscore_idx); 17948c2ecf20Sopenharmony_ci } else { 17958c2ecf20Sopenharmony_ci const struct net_device *master; 17968c2ecf20Sopenharmony_ci int master_idx = 0; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci /* if dst_dev exists and is enslaved to an L3 device, then 17998c2ecf20Sopenharmony_ci * prefer addresses from dst_dev and then the master over 18008c2ecf20Sopenharmony_ci * any other enslaved devices in the L3 domain. 18018c2ecf20Sopenharmony_ci */ 18028c2ecf20Sopenharmony_ci master = l3mdev_master_dev_rcu(dst_dev); 18038c2ecf20Sopenharmony_ci if (master) { 18048c2ecf20Sopenharmony_ci master_idx = master->ifindex; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci hiscore_idx = ipv6_get_saddr_master(net, dst_dev, 18078c2ecf20Sopenharmony_ci master, &dst, 18088c2ecf20Sopenharmony_ci scores, hiscore_idx); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci if (scores[hiscore_idx].ifa) 18118c2ecf20Sopenharmony_ci goto out; 18128c2ecf20Sopenharmony_ci } 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci for_each_netdev_rcu(net, dev) { 18158c2ecf20Sopenharmony_ci /* only consider addresses on devices in the 18168c2ecf20Sopenharmony_ci * same L3 domain 18178c2ecf20Sopenharmony_ci */ 18188c2ecf20Sopenharmony_ci if (l3mdev_master_ifindex_rcu(dev) != master_idx) 18198c2ecf20Sopenharmony_ci continue; 18208c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 18218c2ecf20Sopenharmony_ci if (!idev) 18228c2ecf20Sopenharmony_ci continue; 18238c2ecf20Sopenharmony_ci hiscore_idx = __ipv6_dev_get_saddr(net, &dst, idev, scores, hiscore_idx); 18248c2ecf20Sopenharmony_ci } 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ciout: 18288c2ecf20Sopenharmony_ci hiscore = &scores[hiscore_idx]; 18298c2ecf20Sopenharmony_ci if (!hiscore->ifa) 18308c2ecf20Sopenharmony_ci ret = -EADDRNOTAVAIL; 18318c2ecf20Sopenharmony_ci else 18328c2ecf20Sopenharmony_ci *saddr = hiscore->ifa->addr; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci rcu_read_unlock(); 18358c2ecf20Sopenharmony_ci return ret; 18368c2ecf20Sopenharmony_ci} 18378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipv6_dev_get_saddr); 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ciint __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, 18408c2ecf20Sopenharmony_ci u32 banned_flags) 18418c2ecf20Sopenharmony_ci{ 18428c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 18438c2ecf20Sopenharmony_ci int err = -EADDRNOTAVAIL; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci list_for_each_entry_reverse(ifp, &idev->addr_list, if_list) { 18468c2ecf20Sopenharmony_ci if (ifp->scope > IFA_LINK) 18478c2ecf20Sopenharmony_ci break; 18488c2ecf20Sopenharmony_ci if (ifp->scope == IFA_LINK && 18498c2ecf20Sopenharmony_ci !(ifp->flags & banned_flags)) { 18508c2ecf20Sopenharmony_ci *addr = ifp->addr; 18518c2ecf20Sopenharmony_ci err = 0; 18528c2ecf20Sopenharmony_ci break; 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci return err; 18568c2ecf20Sopenharmony_ci} 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ciint ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 18598c2ecf20Sopenharmony_ci u32 banned_flags) 18608c2ecf20Sopenharmony_ci{ 18618c2ecf20Sopenharmony_ci struct inet6_dev *idev; 18628c2ecf20Sopenharmony_ci int err = -EADDRNOTAVAIL; 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci rcu_read_lock(); 18658c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 18668c2ecf20Sopenharmony_ci if (idev) { 18678c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 18688c2ecf20Sopenharmony_ci err = __ipv6_get_lladdr(idev, addr, banned_flags); 18698c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 18708c2ecf20Sopenharmony_ci } 18718c2ecf20Sopenharmony_ci rcu_read_unlock(); 18728c2ecf20Sopenharmony_ci return err; 18738c2ecf20Sopenharmony_ci} 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_cistatic int ipv6_count_addresses(const struct inet6_dev *idev) 18768c2ecf20Sopenharmony_ci{ 18778c2ecf20Sopenharmony_ci const struct inet6_ifaddr *ifp; 18788c2ecf20Sopenharmony_ci int cnt = 0; 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci rcu_read_lock(); 18818c2ecf20Sopenharmony_ci list_for_each_entry_rcu(ifp, &idev->addr_list, if_list) 18828c2ecf20Sopenharmony_ci cnt++; 18838c2ecf20Sopenharmony_ci rcu_read_unlock(); 18848c2ecf20Sopenharmony_ci return cnt; 18858c2ecf20Sopenharmony_ci} 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ciint ipv6_chk_addr(struct net *net, const struct in6_addr *addr, 18888c2ecf20Sopenharmony_ci const struct net_device *dev, int strict) 18898c2ecf20Sopenharmony_ci{ 18908c2ecf20Sopenharmony_ci return ipv6_chk_addr_and_flags(net, addr, dev, !dev, 18918c2ecf20Sopenharmony_ci strict, IFA_F_TENTATIVE); 18928c2ecf20Sopenharmony_ci} 18938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipv6_chk_addr); 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci/* device argument is used to find the L3 domain of interest. If 18968c2ecf20Sopenharmony_ci * skip_dev_check is set, then the ifp device is not checked against 18978c2ecf20Sopenharmony_ci * the passed in dev argument. So the 2 cases for addresses checks are: 18988c2ecf20Sopenharmony_ci * 1. does the address exist in the L3 domain that dev is part of 18998c2ecf20Sopenharmony_ci * (skip_dev_check = true), or 19008c2ecf20Sopenharmony_ci * 19018c2ecf20Sopenharmony_ci * 2. does the address exist on the specific device 19028c2ecf20Sopenharmony_ci * (skip_dev_check = false) 19038c2ecf20Sopenharmony_ci */ 19048c2ecf20Sopenharmony_cistatic struct net_device * 19058c2ecf20Sopenharmony_ci__ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, 19068c2ecf20Sopenharmony_ci const struct net_device *dev, bool skip_dev_check, 19078c2ecf20Sopenharmony_ci int strict, u32 banned_flags) 19088c2ecf20Sopenharmony_ci{ 19098c2ecf20Sopenharmony_ci unsigned int hash = inet6_addr_hash(net, addr); 19108c2ecf20Sopenharmony_ci struct net_device *l3mdev, *ndev; 19118c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 19128c2ecf20Sopenharmony_ci u32 ifp_flags; 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci rcu_read_lock(); 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci l3mdev = l3mdev_master_dev_rcu(dev); 19178c2ecf20Sopenharmony_ci if (skip_dev_check) 19188c2ecf20Sopenharmony_ci dev = NULL; 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { 19218c2ecf20Sopenharmony_ci ndev = ifp->idev->dev; 19228c2ecf20Sopenharmony_ci if (!net_eq(dev_net(ndev), net)) 19238c2ecf20Sopenharmony_ci continue; 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci if (l3mdev_master_dev_rcu(ndev) != l3mdev) 19268c2ecf20Sopenharmony_ci continue; 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci /* Decouple optimistic from tentative for evaluation here. 19298c2ecf20Sopenharmony_ci * Ban optimistic addresses explicitly, when required. 19308c2ecf20Sopenharmony_ci */ 19318c2ecf20Sopenharmony_ci ifp_flags = (ifp->flags&IFA_F_OPTIMISTIC) 19328c2ecf20Sopenharmony_ci ? (ifp->flags&~IFA_F_TENTATIVE) 19338c2ecf20Sopenharmony_ci : ifp->flags; 19348c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&ifp->addr, addr) && 19358c2ecf20Sopenharmony_ci !(ifp_flags&banned_flags) && 19368c2ecf20Sopenharmony_ci (!dev || ndev == dev || 19378c2ecf20Sopenharmony_ci !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))) { 19388c2ecf20Sopenharmony_ci rcu_read_unlock(); 19398c2ecf20Sopenharmony_ci return ndev; 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci rcu_read_unlock(); 19448c2ecf20Sopenharmony_ci return NULL; 19458c2ecf20Sopenharmony_ci} 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ciint ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, 19488c2ecf20Sopenharmony_ci const struct net_device *dev, bool skip_dev_check, 19498c2ecf20Sopenharmony_ci int strict, u32 banned_flags) 19508c2ecf20Sopenharmony_ci{ 19518c2ecf20Sopenharmony_ci return __ipv6_chk_addr_and_flags(net, addr, dev, skip_dev_check, 19528c2ecf20Sopenharmony_ci strict, banned_flags) ? 1 : 0; 19538c2ecf20Sopenharmony_ci} 19548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipv6_chk_addr_and_flags); 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci/* Compares an address/prefix_len with addresses on device @dev. 19588c2ecf20Sopenharmony_ci * If one is found it returns true. 19598c2ecf20Sopenharmony_ci */ 19608c2ecf20Sopenharmony_cibool ipv6_chk_custom_prefix(const struct in6_addr *addr, 19618c2ecf20Sopenharmony_ci const unsigned int prefix_len, struct net_device *dev) 19628c2ecf20Sopenharmony_ci{ 19638c2ecf20Sopenharmony_ci const struct inet6_ifaddr *ifa; 19648c2ecf20Sopenharmony_ci const struct inet6_dev *idev; 19658c2ecf20Sopenharmony_ci bool ret = false; 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci rcu_read_lock(); 19688c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 19698c2ecf20Sopenharmony_ci if (idev) { 19708c2ecf20Sopenharmony_ci list_for_each_entry_rcu(ifa, &idev->addr_list, if_list) { 19718c2ecf20Sopenharmony_ci ret = ipv6_prefix_equal(addr, &ifa->addr, prefix_len); 19728c2ecf20Sopenharmony_ci if (ret) 19738c2ecf20Sopenharmony_ci break; 19748c2ecf20Sopenharmony_ci } 19758c2ecf20Sopenharmony_ci } 19768c2ecf20Sopenharmony_ci rcu_read_unlock(); 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci return ret; 19798c2ecf20Sopenharmony_ci} 19808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipv6_chk_custom_prefix); 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ciint ipv6_chk_prefix(const struct in6_addr *addr, struct net_device *dev) 19838c2ecf20Sopenharmony_ci{ 19848c2ecf20Sopenharmony_ci const struct inet6_ifaddr *ifa; 19858c2ecf20Sopenharmony_ci const struct inet6_dev *idev; 19868c2ecf20Sopenharmony_ci int onlink; 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci onlink = 0; 19898c2ecf20Sopenharmony_ci rcu_read_lock(); 19908c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 19918c2ecf20Sopenharmony_ci if (idev) { 19928c2ecf20Sopenharmony_ci list_for_each_entry_rcu(ifa, &idev->addr_list, if_list) { 19938c2ecf20Sopenharmony_ci onlink = ipv6_prefix_equal(addr, &ifa->addr, 19948c2ecf20Sopenharmony_ci ifa->prefix_len); 19958c2ecf20Sopenharmony_ci if (onlink) 19968c2ecf20Sopenharmony_ci break; 19978c2ecf20Sopenharmony_ci } 19988c2ecf20Sopenharmony_ci } 19998c2ecf20Sopenharmony_ci rcu_read_unlock(); 20008c2ecf20Sopenharmony_ci return onlink; 20018c2ecf20Sopenharmony_ci} 20028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipv6_chk_prefix); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci/** 20058c2ecf20Sopenharmony_ci * ipv6_dev_find - find the first device with a given source address. 20068c2ecf20Sopenharmony_ci * @net: the net namespace 20078c2ecf20Sopenharmony_ci * @addr: the source address 20088c2ecf20Sopenharmony_ci * 20098c2ecf20Sopenharmony_ci * The caller should be protected by RCU, or RTNL. 20108c2ecf20Sopenharmony_ci */ 20118c2ecf20Sopenharmony_cistruct net_device *ipv6_dev_find(struct net *net, const struct in6_addr *addr, 20128c2ecf20Sopenharmony_ci struct net_device *dev) 20138c2ecf20Sopenharmony_ci{ 20148c2ecf20Sopenharmony_ci return __ipv6_chk_addr_and_flags(net, addr, dev, !dev, 1, 20158c2ecf20Sopenharmony_ci IFA_F_TENTATIVE); 20168c2ecf20Sopenharmony_ci} 20178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipv6_dev_find); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_cistruct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *addr, 20208c2ecf20Sopenharmony_ci struct net_device *dev, int strict) 20218c2ecf20Sopenharmony_ci{ 20228c2ecf20Sopenharmony_ci unsigned int hash = inet6_addr_hash(net, addr); 20238c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp, *result = NULL; 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci rcu_read_lock(); 20268c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { 20278c2ecf20Sopenharmony_ci if (!net_eq(dev_net(ifp->idev->dev), net)) 20288c2ecf20Sopenharmony_ci continue; 20298c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&ifp->addr, addr)) { 20308c2ecf20Sopenharmony_ci if (!dev || ifp->idev->dev == dev || 20318c2ecf20Sopenharmony_ci !(ifp->scope&(IFA_LINK|IFA_HOST) || strict)) { 20328c2ecf20Sopenharmony_ci if (in6_ifa_hold_safe(ifp)) { 20338c2ecf20Sopenharmony_ci result = ifp; 20348c2ecf20Sopenharmony_ci break; 20358c2ecf20Sopenharmony_ci } 20368c2ecf20Sopenharmony_ci } 20378c2ecf20Sopenharmony_ci } 20388c2ecf20Sopenharmony_ci } 20398c2ecf20Sopenharmony_ci rcu_read_unlock(); 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci return result; 20428c2ecf20Sopenharmony_ci} 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci/* Gets referenced address, destroys ifaddr */ 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_cistatic void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed) 20478c2ecf20Sopenharmony_ci{ 20488c2ecf20Sopenharmony_ci if (dad_failed) 20498c2ecf20Sopenharmony_ci ifp->flags |= IFA_F_DADFAILED; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci if (ifp->flags&IFA_F_TEMPORARY) { 20528c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifpub; 20538c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 20548c2ecf20Sopenharmony_ci ifpub = ifp->ifpub; 20558c2ecf20Sopenharmony_ci if (ifpub) { 20568c2ecf20Sopenharmony_ci in6_ifa_hold(ifpub); 20578c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 20588c2ecf20Sopenharmony_ci ipv6_create_tempaddr(ifpub, true); 20598c2ecf20Sopenharmony_ci in6_ifa_put(ifpub); 20608c2ecf20Sopenharmony_ci } else { 20618c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 20628c2ecf20Sopenharmony_ci } 20638c2ecf20Sopenharmony_ci ipv6_del_addr(ifp); 20648c2ecf20Sopenharmony_ci } else if (ifp->flags&IFA_F_PERMANENT || !dad_failed) { 20658c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 20668c2ecf20Sopenharmony_ci addrconf_del_dad_work(ifp); 20678c2ecf20Sopenharmony_ci ifp->flags |= IFA_F_TENTATIVE; 20688c2ecf20Sopenharmony_ci if (dad_failed) 20698c2ecf20Sopenharmony_ci ifp->flags &= ~IFA_F_OPTIMISTIC; 20708c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 20718c2ecf20Sopenharmony_ci if (dad_failed) 20728c2ecf20Sopenharmony_ci ipv6_ifa_notify(0, ifp); 20738c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 20748c2ecf20Sopenharmony_ci } else { 20758c2ecf20Sopenharmony_ci ipv6_del_addr(ifp); 20768c2ecf20Sopenharmony_ci } 20778c2ecf20Sopenharmony_ci} 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_cistatic int addrconf_dad_end(struct inet6_ifaddr *ifp) 20808c2ecf20Sopenharmony_ci{ 20818c2ecf20Sopenharmony_ci int err = -ENOENT; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 20848c2ecf20Sopenharmony_ci if (ifp->state == INET6_IFADDR_STATE_DAD) { 20858c2ecf20Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_POSTDAD; 20868c2ecf20Sopenharmony_ci err = 0; 20878c2ecf20Sopenharmony_ci } 20888c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci return err; 20918c2ecf20Sopenharmony_ci} 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_civoid addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp) 20948c2ecf20Sopenharmony_ci{ 20958c2ecf20Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 20968c2ecf20Sopenharmony_ci struct net *net = dev_net(ifp->idev->dev); 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci if (addrconf_dad_end(ifp)) { 20998c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 21008c2ecf20Sopenharmony_ci return; 21018c2ecf20Sopenharmony_ci } 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci net_info_ratelimited("%s: IPv6 duplicate address %pI6c used by %pM detected!\n", 21048c2ecf20Sopenharmony_ci ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source); 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci if (ifp->flags & IFA_F_STABLE_PRIVACY) { 21098c2ecf20Sopenharmony_ci struct in6_addr new_addr; 21108c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp2; 21118c2ecf20Sopenharmony_ci int retries = ifp->stable_privacy_retry + 1; 21128c2ecf20Sopenharmony_ci struct ifa6_config cfg = { 21138c2ecf20Sopenharmony_ci .pfx = &new_addr, 21148c2ecf20Sopenharmony_ci .plen = ifp->prefix_len, 21158c2ecf20Sopenharmony_ci .ifa_flags = ifp->flags, 21168c2ecf20Sopenharmony_ci .valid_lft = ifp->valid_lft, 21178c2ecf20Sopenharmony_ci .preferred_lft = ifp->prefered_lft, 21188c2ecf20Sopenharmony_ci .scope = ifp->scope, 21198c2ecf20Sopenharmony_ci }; 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci if (retries > net->ipv6.sysctl.idgen_retries) { 21228c2ecf20Sopenharmony_ci net_info_ratelimited("%s: privacy stable address generation failed because of DAD conflicts!\n", 21238c2ecf20Sopenharmony_ci ifp->idev->dev->name); 21248c2ecf20Sopenharmony_ci goto errdad; 21258c2ecf20Sopenharmony_ci } 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci new_addr = ifp->addr; 21288c2ecf20Sopenharmony_ci if (ipv6_generate_stable_address(&new_addr, retries, 21298c2ecf20Sopenharmony_ci idev)) 21308c2ecf20Sopenharmony_ci goto errdad; 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci if (idev->cnf.max_addresses && 21358c2ecf20Sopenharmony_ci ipv6_count_addresses(idev) >= 21368c2ecf20Sopenharmony_ci idev->cnf.max_addresses) 21378c2ecf20Sopenharmony_ci goto lock_errdad; 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci net_info_ratelimited("%s: generating new stable privacy address because of DAD conflict\n", 21408c2ecf20Sopenharmony_ci ifp->idev->dev->name); 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci ifp2 = ipv6_add_addr(idev, &cfg, false, NULL); 21438c2ecf20Sopenharmony_ci if (IS_ERR(ifp2)) 21448c2ecf20Sopenharmony_ci goto lock_errdad; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci spin_lock_bh(&ifp2->lock); 21478c2ecf20Sopenharmony_ci ifp2->stable_privacy_retry = retries; 21488c2ecf20Sopenharmony_ci ifp2->state = INET6_IFADDR_STATE_PREDAD; 21498c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp2->lock); 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay); 21528c2ecf20Sopenharmony_ci in6_ifa_put(ifp2); 21538c2ecf20Sopenharmony_cilock_errdad: 21548c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 21558c2ecf20Sopenharmony_ci } 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_cierrdad: 21588c2ecf20Sopenharmony_ci /* transition from _POSTDAD to _ERRDAD */ 21598c2ecf20Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_ERRDAD; 21608c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci addrconf_mod_dad_work(ifp, 0); 21638c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 21648c2ecf20Sopenharmony_ci} 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci/* Join to solicited addr multicast group. 21678c2ecf20Sopenharmony_ci * caller must hold RTNL */ 21688c2ecf20Sopenharmony_civoid addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr) 21698c2ecf20Sopenharmony_ci{ 21708c2ecf20Sopenharmony_ci struct in6_addr maddr; 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci if (dev->flags&(IFF_LOOPBACK|IFF_NOARP)) 21738c2ecf20Sopenharmony_ci return; 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci addrconf_addr_solict_mult(addr, &maddr); 21768c2ecf20Sopenharmony_ci ipv6_dev_mc_inc(dev, &maddr); 21778c2ecf20Sopenharmony_ci} 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci/* caller must hold RTNL */ 21808c2ecf20Sopenharmony_civoid addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr) 21818c2ecf20Sopenharmony_ci{ 21828c2ecf20Sopenharmony_ci struct in6_addr maddr; 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci if (idev->dev->flags&(IFF_LOOPBACK|IFF_NOARP)) 21858c2ecf20Sopenharmony_ci return; 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci addrconf_addr_solict_mult(addr, &maddr); 21888c2ecf20Sopenharmony_ci __ipv6_dev_mc_dec(idev, &maddr); 21898c2ecf20Sopenharmony_ci} 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci/* caller must hold RTNL */ 21928c2ecf20Sopenharmony_cistatic void addrconf_join_anycast(struct inet6_ifaddr *ifp) 21938c2ecf20Sopenharmony_ci{ 21948c2ecf20Sopenharmony_ci struct in6_addr addr; 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci if (ifp->prefix_len >= 127) /* RFC 6164 */ 21978c2ecf20Sopenharmony_ci return; 21988c2ecf20Sopenharmony_ci ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); 21998c2ecf20Sopenharmony_ci if (ipv6_addr_any(&addr)) 22008c2ecf20Sopenharmony_ci return; 22018c2ecf20Sopenharmony_ci __ipv6_dev_ac_inc(ifp->idev, &addr); 22028c2ecf20Sopenharmony_ci} 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci/* caller must hold RTNL */ 22058c2ecf20Sopenharmony_cistatic void addrconf_leave_anycast(struct inet6_ifaddr *ifp) 22068c2ecf20Sopenharmony_ci{ 22078c2ecf20Sopenharmony_ci struct in6_addr addr; 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci if (ifp->prefix_len >= 127) /* RFC 6164 */ 22108c2ecf20Sopenharmony_ci return; 22118c2ecf20Sopenharmony_ci ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); 22128c2ecf20Sopenharmony_ci if (ipv6_addr_any(&addr)) 22138c2ecf20Sopenharmony_ci return; 22148c2ecf20Sopenharmony_ci __ipv6_dev_ac_dec(ifp->idev, &addr); 22158c2ecf20Sopenharmony_ci} 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_cistatic int addrconf_ifid_6lowpan(u8 *eui, struct net_device *dev) 22188c2ecf20Sopenharmony_ci{ 22198c2ecf20Sopenharmony_ci switch (dev->addr_len) { 22208c2ecf20Sopenharmony_ci case ETH_ALEN: 22218c2ecf20Sopenharmony_ci memcpy(eui, dev->dev_addr, 3); 22228c2ecf20Sopenharmony_ci eui[3] = 0xFF; 22238c2ecf20Sopenharmony_ci eui[4] = 0xFE; 22248c2ecf20Sopenharmony_ci memcpy(eui + 5, dev->dev_addr + 3, 3); 22258c2ecf20Sopenharmony_ci break; 22268c2ecf20Sopenharmony_ci case EUI64_ADDR_LEN: 22278c2ecf20Sopenharmony_ci memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN); 22288c2ecf20Sopenharmony_ci eui[0] ^= 2; 22298c2ecf20Sopenharmony_ci break; 22308c2ecf20Sopenharmony_ci default: 22318c2ecf20Sopenharmony_ci return -1; 22328c2ecf20Sopenharmony_ci } 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci return 0; 22358c2ecf20Sopenharmony_ci} 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_cistatic int addrconf_ifid_ieee1394(u8 *eui, struct net_device *dev) 22388c2ecf20Sopenharmony_ci{ 22398c2ecf20Sopenharmony_ci union fwnet_hwaddr *ha; 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci if (dev->addr_len != FWNET_ALEN) 22428c2ecf20Sopenharmony_ci return -1; 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci ha = (union fwnet_hwaddr *)dev->dev_addr; 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci memcpy(eui, &ha->uc.uniq_id, sizeof(ha->uc.uniq_id)); 22478c2ecf20Sopenharmony_ci eui[0] ^= 2; 22488c2ecf20Sopenharmony_ci return 0; 22498c2ecf20Sopenharmony_ci} 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_cistatic int addrconf_ifid_arcnet(u8 *eui, struct net_device *dev) 22528c2ecf20Sopenharmony_ci{ 22538c2ecf20Sopenharmony_ci /* XXX: inherit EUI-64 from other interface -- yoshfuji */ 22548c2ecf20Sopenharmony_ci if (dev->addr_len != ARCNET_ALEN) 22558c2ecf20Sopenharmony_ci return -1; 22568c2ecf20Sopenharmony_ci memset(eui, 0, 7); 22578c2ecf20Sopenharmony_ci eui[7] = *(u8 *)dev->dev_addr; 22588c2ecf20Sopenharmony_ci return 0; 22598c2ecf20Sopenharmony_ci} 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_cistatic int addrconf_ifid_infiniband(u8 *eui, struct net_device *dev) 22628c2ecf20Sopenharmony_ci{ 22638c2ecf20Sopenharmony_ci if (dev->addr_len != INFINIBAND_ALEN) 22648c2ecf20Sopenharmony_ci return -1; 22658c2ecf20Sopenharmony_ci memcpy(eui, dev->dev_addr + 12, 8); 22668c2ecf20Sopenharmony_ci eui[0] |= 2; 22678c2ecf20Sopenharmony_ci return 0; 22688c2ecf20Sopenharmony_ci} 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_cistatic int __ipv6_isatap_ifid(u8 *eui, __be32 addr) 22718c2ecf20Sopenharmony_ci{ 22728c2ecf20Sopenharmony_ci if (addr == 0) 22738c2ecf20Sopenharmony_ci return -1; 22748c2ecf20Sopenharmony_ci eui[0] = (ipv4_is_zeronet(addr) || ipv4_is_private_10(addr) || 22758c2ecf20Sopenharmony_ci ipv4_is_loopback(addr) || ipv4_is_linklocal_169(addr) || 22768c2ecf20Sopenharmony_ci ipv4_is_private_172(addr) || ipv4_is_test_192(addr) || 22778c2ecf20Sopenharmony_ci ipv4_is_anycast_6to4(addr) || ipv4_is_private_192(addr) || 22788c2ecf20Sopenharmony_ci ipv4_is_test_198(addr) || ipv4_is_multicast(addr) || 22798c2ecf20Sopenharmony_ci ipv4_is_lbcast(addr)) ? 0x00 : 0x02; 22808c2ecf20Sopenharmony_ci eui[1] = 0; 22818c2ecf20Sopenharmony_ci eui[2] = 0x5E; 22828c2ecf20Sopenharmony_ci eui[3] = 0xFE; 22838c2ecf20Sopenharmony_ci memcpy(eui + 4, &addr, 4); 22848c2ecf20Sopenharmony_ci return 0; 22858c2ecf20Sopenharmony_ci} 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_cistatic int addrconf_ifid_sit(u8 *eui, struct net_device *dev) 22888c2ecf20Sopenharmony_ci{ 22898c2ecf20Sopenharmony_ci if (dev->priv_flags & IFF_ISATAP) 22908c2ecf20Sopenharmony_ci return __ipv6_isatap_ifid(eui, *(__be32 *)dev->dev_addr); 22918c2ecf20Sopenharmony_ci return -1; 22928c2ecf20Sopenharmony_ci} 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_cistatic int addrconf_ifid_gre(u8 *eui, struct net_device *dev) 22958c2ecf20Sopenharmony_ci{ 22968c2ecf20Sopenharmony_ci return __ipv6_isatap_ifid(eui, *(__be32 *)dev->dev_addr); 22978c2ecf20Sopenharmony_ci} 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_cistatic int addrconf_ifid_ip6tnl(u8 *eui, struct net_device *dev) 23008c2ecf20Sopenharmony_ci{ 23018c2ecf20Sopenharmony_ci memcpy(eui, dev->perm_addr, 3); 23028c2ecf20Sopenharmony_ci memcpy(eui + 5, dev->perm_addr + 3, 3); 23038c2ecf20Sopenharmony_ci eui[3] = 0xFF; 23048c2ecf20Sopenharmony_ci eui[4] = 0xFE; 23058c2ecf20Sopenharmony_ci eui[0] ^= 2; 23068c2ecf20Sopenharmony_ci return 0; 23078c2ecf20Sopenharmony_ci} 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_cistatic int ipv6_generate_eui64(u8 *eui, struct net_device *dev) 23108c2ecf20Sopenharmony_ci{ 23118c2ecf20Sopenharmony_ci switch (dev->type) { 23128c2ecf20Sopenharmony_ci case ARPHRD_ETHER: 23138c2ecf20Sopenharmony_ci case ARPHRD_FDDI: 23148c2ecf20Sopenharmony_ci return addrconf_ifid_eui48(eui, dev); 23158c2ecf20Sopenharmony_ci case ARPHRD_ARCNET: 23168c2ecf20Sopenharmony_ci return addrconf_ifid_arcnet(eui, dev); 23178c2ecf20Sopenharmony_ci case ARPHRD_INFINIBAND: 23188c2ecf20Sopenharmony_ci return addrconf_ifid_infiniband(eui, dev); 23198c2ecf20Sopenharmony_ci case ARPHRD_SIT: 23208c2ecf20Sopenharmony_ci return addrconf_ifid_sit(eui, dev); 23218c2ecf20Sopenharmony_ci case ARPHRD_IPGRE: 23228c2ecf20Sopenharmony_ci case ARPHRD_TUNNEL: 23238c2ecf20Sopenharmony_ci return addrconf_ifid_gre(eui, dev); 23248c2ecf20Sopenharmony_ci case ARPHRD_6LOWPAN: 23258c2ecf20Sopenharmony_ci return addrconf_ifid_6lowpan(eui, dev); 23268c2ecf20Sopenharmony_ci case ARPHRD_IEEE1394: 23278c2ecf20Sopenharmony_ci return addrconf_ifid_ieee1394(eui, dev); 23288c2ecf20Sopenharmony_ci case ARPHRD_TUNNEL6: 23298c2ecf20Sopenharmony_ci case ARPHRD_IP6GRE: 23308c2ecf20Sopenharmony_ci case ARPHRD_RAWIP: 23318c2ecf20Sopenharmony_ci return addrconf_ifid_ip6tnl(eui, dev); 23328c2ecf20Sopenharmony_ci } 23338c2ecf20Sopenharmony_ci return -1; 23348c2ecf20Sopenharmony_ci} 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_cistatic int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev) 23378c2ecf20Sopenharmony_ci{ 23388c2ecf20Sopenharmony_ci int err = -1; 23398c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 23428c2ecf20Sopenharmony_ci list_for_each_entry_reverse(ifp, &idev->addr_list, if_list) { 23438c2ecf20Sopenharmony_ci if (ifp->scope > IFA_LINK) 23448c2ecf20Sopenharmony_ci break; 23458c2ecf20Sopenharmony_ci if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) { 23468c2ecf20Sopenharmony_ci memcpy(eui, ifp->addr.s6_addr+8, 8); 23478c2ecf20Sopenharmony_ci err = 0; 23488c2ecf20Sopenharmony_ci break; 23498c2ecf20Sopenharmony_ci } 23508c2ecf20Sopenharmony_ci } 23518c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 23528c2ecf20Sopenharmony_ci return err; 23538c2ecf20Sopenharmony_ci} 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci/* Generation of a randomized Interface Identifier 23568c2ecf20Sopenharmony_ci * draft-ietf-6man-rfc4941bis, Section 3.3.1 23578c2ecf20Sopenharmony_ci */ 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_cistatic void ipv6_gen_rnd_iid(struct in6_addr *addr) 23608c2ecf20Sopenharmony_ci{ 23618c2ecf20Sopenharmony_ciregen: 23628c2ecf20Sopenharmony_ci get_random_bytes(&addr->s6_addr[8], 8); 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci /* <draft-ietf-6man-rfc4941bis-08.txt>, Section 3.3.1: 23658c2ecf20Sopenharmony_ci * check if generated address is not inappropriate: 23668c2ecf20Sopenharmony_ci * 23678c2ecf20Sopenharmony_ci * - Reserved IPv6 Interface Identifers 23688c2ecf20Sopenharmony_ci * - XXX: already assigned to an address on the device 23698c2ecf20Sopenharmony_ci */ 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci /* Subnet-router anycast: 0000:0000:0000:0000 */ 23728c2ecf20Sopenharmony_ci if (!(addr->s6_addr32[2] | addr->s6_addr32[3])) 23738c2ecf20Sopenharmony_ci goto regen; 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci /* IANA Ethernet block: 0200:5EFF:FE00:0000-0200:5EFF:FE00:5212 23768c2ecf20Sopenharmony_ci * Proxy Mobile IPv6: 0200:5EFF:FE00:5213 23778c2ecf20Sopenharmony_ci * IANA Ethernet block: 0200:5EFF:FE00:5214-0200:5EFF:FEFF:FFFF 23788c2ecf20Sopenharmony_ci */ 23798c2ecf20Sopenharmony_ci if (ntohl(addr->s6_addr32[2]) == 0x02005eff && 23808c2ecf20Sopenharmony_ci (ntohl(addr->s6_addr32[3]) & 0Xff000000) == 0xfe000000) 23818c2ecf20Sopenharmony_ci goto regen; 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci /* Reserved subnet anycast addresses */ 23848c2ecf20Sopenharmony_ci if (ntohl(addr->s6_addr32[2]) == 0xfdffffff && 23858c2ecf20Sopenharmony_ci ntohl(addr->s6_addr32[3]) >= 0Xffffff80) 23868c2ecf20Sopenharmony_ci goto regen; 23878c2ecf20Sopenharmony_ci} 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci/* 23908c2ecf20Sopenharmony_ci * Add prefix route. 23918c2ecf20Sopenharmony_ci */ 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_cistatic void 23948c2ecf20Sopenharmony_ciaddrconf_prefix_route(struct in6_addr *pfx, int plen, u32 metric, 23958c2ecf20Sopenharmony_ci struct net_device *dev, unsigned long expires, 23968c2ecf20Sopenharmony_ci u32 flags, gfp_t gfp_flags) 23978c2ecf20Sopenharmony_ci{ 23988c2ecf20Sopenharmony_ci struct fib6_config cfg = { 23998c2ecf20Sopenharmony_ci .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX, 24008c2ecf20Sopenharmony_ci .fc_metric = metric ? : IP6_RT_PRIO_ADDRCONF, 24018c2ecf20Sopenharmony_ci .fc_ifindex = dev->ifindex, 24028c2ecf20Sopenharmony_ci .fc_expires = expires, 24038c2ecf20Sopenharmony_ci .fc_dst_len = plen, 24048c2ecf20Sopenharmony_ci .fc_flags = RTF_UP | flags, 24058c2ecf20Sopenharmony_ci .fc_nlinfo.nl_net = dev_net(dev), 24068c2ecf20Sopenharmony_ci .fc_protocol = RTPROT_KERNEL, 24078c2ecf20Sopenharmony_ci .fc_type = RTN_UNICAST, 24088c2ecf20Sopenharmony_ci }; 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci cfg.fc_dst = *pfx; 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci /* Prevent useless cloning on PtP SIT. 24138c2ecf20Sopenharmony_ci This thing is done here expecting that the whole 24148c2ecf20Sopenharmony_ci class of non-broadcast devices need not cloning. 24158c2ecf20Sopenharmony_ci */ 24168c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT) 24178c2ecf20Sopenharmony_ci if (dev->type == ARPHRD_SIT && (dev->flags & IFF_POINTOPOINT)) 24188c2ecf20Sopenharmony_ci cfg.fc_flags |= RTF_NONEXTHOP; 24198c2ecf20Sopenharmony_ci#endif 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci ip6_route_add(&cfg, gfp_flags, NULL); 24228c2ecf20Sopenharmony_ci} 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_cistatic struct fib6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, 24268c2ecf20Sopenharmony_ci int plen, 24278c2ecf20Sopenharmony_ci const struct net_device *dev, 24288c2ecf20Sopenharmony_ci u32 flags, u32 noflags, 24298c2ecf20Sopenharmony_ci bool no_gw) 24308c2ecf20Sopenharmony_ci{ 24318c2ecf20Sopenharmony_ci struct fib6_node *fn; 24328c2ecf20Sopenharmony_ci struct fib6_info *rt = NULL; 24338c2ecf20Sopenharmony_ci struct fib6_table *table; 24348c2ecf20Sopenharmony_ci u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX; 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci table = fib6_get_table(dev_net(dev), tb_id); 24378c2ecf20Sopenharmony_ci if (!table) 24388c2ecf20Sopenharmony_ci return NULL; 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci rcu_read_lock(); 24418c2ecf20Sopenharmony_ci fn = fib6_locate(&table->tb6_root, pfx, plen, NULL, 0, true); 24428c2ecf20Sopenharmony_ci if (!fn) 24438c2ecf20Sopenharmony_ci goto out; 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci for_each_fib6_node_rt_rcu(fn) { 24468c2ecf20Sopenharmony_ci /* prefix routes only use builtin fib6_nh */ 24478c2ecf20Sopenharmony_ci if (rt->nh) 24488c2ecf20Sopenharmony_ci continue; 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci if (rt->fib6_nh->fib_nh_dev->ifindex != dev->ifindex) 24518c2ecf20Sopenharmony_ci continue; 24528c2ecf20Sopenharmony_ci if (no_gw && rt->fib6_nh->fib_nh_gw_family) 24538c2ecf20Sopenharmony_ci continue; 24548c2ecf20Sopenharmony_ci if ((rt->fib6_flags & flags) != flags) 24558c2ecf20Sopenharmony_ci continue; 24568c2ecf20Sopenharmony_ci if ((rt->fib6_flags & noflags) != 0) 24578c2ecf20Sopenharmony_ci continue; 24588c2ecf20Sopenharmony_ci if (!fib6_info_hold_safe(rt)) 24598c2ecf20Sopenharmony_ci continue; 24608c2ecf20Sopenharmony_ci break; 24618c2ecf20Sopenharmony_ci } 24628c2ecf20Sopenharmony_ciout: 24638c2ecf20Sopenharmony_ci rcu_read_unlock(); 24648c2ecf20Sopenharmony_ci return rt; 24658c2ecf20Sopenharmony_ci} 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci/* Create "default" multicast route to the interface */ 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_cistatic void addrconf_add_mroute(struct net_device *dev) 24718c2ecf20Sopenharmony_ci{ 24728c2ecf20Sopenharmony_ci struct fib6_config cfg = { 24738c2ecf20Sopenharmony_ci .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_LOCAL, 24748c2ecf20Sopenharmony_ci .fc_metric = IP6_RT_PRIO_ADDRCONF, 24758c2ecf20Sopenharmony_ci .fc_ifindex = dev->ifindex, 24768c2ecf20Sopenharmony_ci .fc_dst_len = 8, 24778c2ecf20Sopenharmony_ci .fc_flags = RTF_UP, 24788c2ecf20Sopenharmony_ci .fc_type = RTN_MULTICAST, 24798c2ecf20Sopenharmony_ci .fc_nlinfo.nl_net = dev_net(dev), 24808c2ecf20Sopenharmony_ci .fc_protocol = RTPROT_KERNEL, 24818c2ecf20Sopenharmony_ci }; 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci ipv6_addr_set(&cfg.fc_dst, htonl(0xFF000000), 0, 0, 0); 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci ip6_route_add(&cfg, GFP_KERNEL, NULL); 24868c2ecf20Sopenharmony_ci} 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_cistatic struct inet6_dev *addrconf_add_dev(struct net_device *dev) 24898c2ecf20Sopenharmony_ci{ 24908c2ecf20Sopenharmony_ci struct inet6_dev *idev; 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci ASSERT_RTNL(); 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci idev = ipv6_find_idev(dev); 24958c2ecf20Sopenharmony_ci if (IS_ERR(idev)) 24968c2ecf20Sopenharmony_ci return idev; 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci if (idev->cnf.disable_ipv6) 24998c2ecf20Sopenharmony_ci return ERR_PTR(-EACCES); 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_ci /* Add default multicast route */ 25028c2ecf20Sopenharmony_ci if (!(dev->flags & IFF_LOOPBACK) && !netif_is_l3_master(dev)) 25038c2ecf20Sopenharmony_ci addrconf_add_mroute(dev); 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci return idev; 25068c2ecf20Sopenharmony_ci} 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_cistatic void manage_tempaddrs(struct inet6_dev *idev, 25098c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp, 25108c2ecf20Sopenharmony_ci __u32 valid_lft, __u32 prefered_lft, 25118c2ecf20Sopenharmony_ci bool create, unsigned long now) 25128c2ecf20Sopenharmony_ci{ 25138c2ecf20Sopenharmony_ci u32 flags; 25148c2ecf20Sopenharmony_ci struct inet6_ifaddr *ift; 25158c2ecf20Sopenharmony_ci 25168c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 25178c2ecf20Sopenharmony_ci /* update all temporary addresses in the list */ 25188c2ecf20Sopenharmony_ci list_for_each_entry(ift, &idev->tempaddr_list, tmp_list) { 25198c2ecf20Sopenharmony_ci int age, max_valid, max_prefered; 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci if (ifp != ift->ifpub) 25228c2ecf20Sopenharmony_ci continue; 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci /* RFC 4941 section 3.3: 25258c2ecf20Sopenharmony_ci * If a received option will extend the lifetime of a public 25268c2ecf20Sopenharmony_ci * address, the lifetimes of temporary addresses should 25278c2ecf20Sopenharmony_ci * be extended, subject to the overall constraint that no 25288c2ecf20Sopenharmony_ci * temporary addresses should ever remain "valid" or "preferred" 25298c2ecf20Sopenharmony_ci * for a time longer than (TEMP_VALID_LIFETIME) or 25308c2ecf20Sopenharmony_ci * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR), respectively. 25318c2ecf20Sopenharmony_ci */ 25328c2ecf20Sopenharmony_ci age = (now - ift->cstamp) / HZ; 25338c2ecf20Sopenharmony_ci max_valid = idev->cnf.temp_valid_lft - age; 25348c2ecf20Sopenharmony_ci if (max_valid < 0) 25358c2ecf20Sopenharmony_ci max_valid = 0; 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci max_prefered = idev->cnf.temp_prefered_lft - 25388c2ecf20Sopenharmony_ci idev->desync_factor - age; 25398c2ecf20Sopenharmony_ci if (max_prefered < 0) 25408c2ecf20Sopenharmony_ci max_prefered = 0; 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_ci if (valid_lft > max_valid) 25438c2ecf20Sopenharmony_ci valid_lft = max_valid; 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci if (prefered_lft > max_prefered) 25468c2ecf20Sopenharmony_ci prefered_lft = max_prefered; 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci spin_lock(&ift->lock); 25498c2ecf20Sopenharmony_ci flags = ift->flags; 25508c2ecf20Sopenharmony_ci ift->valid_lft = valid_lft; 25518c2ecf20Sopenharmony_ci ift->prefered_lft = prefered_lft; 25528c2ecf20Sopenharmony_ci ift->tstamp = now; 25538c2ecf20Sopenharmony_ci if (prefered_lft > 0) 25548c2ecf20Sopenharmony_ci ift->flags &= ~IFA_F_DEPRECATED; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci spin_unlock(&ift->lock); 25578c2ecf20Sopenharmony_ci if (!(flags&IFA_F_TENTATIVE)) 25588c2ecf20Sopenharmony_ci ipv6_ifa_notify(0, ift); 25598c2ecf20Sopenharmony_ci } 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci /* Also create a temporary address if it's enabled but no temporary 25628c2ecf20Sopenharmony_ci * address currently exists. 25638c2ecf20Sopenharmony_ci * However, we get called with valid_lft == 0, prefered_lft == 0, create == false 25648c2ecf20Sopenharmony_ci * as part of cleanup (ie. deleting the mngtmpaddr). 25658c2ecf20Sopenharmony_ci * We don't want that to result in creating a new temporary ip address. 25668c2ecf20Sopenharmony_ci */ 25678c2ecf20Sopenharmony_ci if (list_empty(&idev->tempaddr_list) && (valid_lft || prefered_lft)) 25688c2ecf20Sopenharmony_ci create = true; 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci if (create && idev->cnf.use_tempaddr > 0) { 25718c2ecf20Sopenharmony_ci /* When a new public address is created as described 25728c2ecf20Sopenharmony_ci * in [ADDRCONF], also create a new temporary address. 25738c2ecf20Sopenharmony_ci */ 25748c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 25758c2ecf20Sopenharmony_ci ipv6_create_tempaddr(ifp, false); 25768c2ecf20Sopenharmony_ci } else { 25778c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 25788c2ecf20Sopenharmony_ci } 25798c2ecf20Sopenharmony_ci} 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_cistatic bool is_addr_mode_generate_stable(struct inet6_dev *idev) 25828c2ecf20Sopenharmony_ci{ 25838c2ecf20Sopenharmony_ci return idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY || 25848c2ecf20Sopenharmony_ci idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM; 25858c2ecf20Sopenharmony_ci} 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ciint addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, 25888c2ecf20Sopenharmony_ci const struct prefix_info *pinfo, 25898c2ecf20Sopenharmony_ci struct inet6_dev *in6_dev, 25908c2ecf20Sopenharmony_ci const struct in6_addr *addr, int addr_type, 25918c2ecf20Sopenharmony_ci u32 addr_flags, bool sllao, bool tokenized, 25928c2ecf20Sopenharmony_ci __u32 valid_lft, u32 prefered_lft) 25938c2ecf20Sopenharmony_ci{ 25948c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1); 25958c2ecf20Sopenharmony_ci int create = 0, update_lft = 0; 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci if (!ifp && valid_lft) { 25988c2ecf20Sopenharmony_ci int max_addresses = in6_dev->cnf.max_addresses; 25998c2ecf20Sopenharmony_ci struct ifa6_config cfg = { 26008c2ecf20Sopenharmony_ci .pfx = addr, 26018c2ecf20Sopenharmony_ci .plen = pinfo->prefix_len, 26028c2ecf20Sopenharmony_ci .ifa_flags = addr_flags, 26038c2ecf20Sopenharmony_ci .valid_lft = valid_lft, 26048c2ecf20Sopenharmony_ci .preferred_lft = prefered_lft, 26058c2ecf20Sopenharmony_ci .scope = addr_type & IPV6_ADDR_SCOPE_MASK, 26068c2ecf20Sopenharmony_ci }; 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 26098c2ecf20Sopenharmony_ci if ((net->ipv6.devconf_all->optimistic_dad || 26108c2ecf20Sopenharmony_ci in6_dev->cnf.optimistic_dad) && 26118c2ecf20Sopenharmony_ci !net->ipv6.devconf_all->forwarding && sllao) 26128c2ecf20Sopenharmony_ci cfg.ifa_flags |= IFA_F_OPTIMISTIC; 26138c2ecf20Sopenharmony_ci#endif 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci /* Do not allow to create too much of autoconfigured 26168c2ecf20Sopenharmony_ci * addresses; this would be too easy way to crash kernel. 26178c2ecf20Sopenharmony_ci */ 26188c2ecf20Sopenharmony_ci if (!max_addresses || 26198c2ecf20Sopenharmony_ci ipv6_count_addresses(in6_dev) < max_addresses) 26208c2ecf20Sopenharmony_ci ifp = ipv6_add_addr(in6_dev, &cfg, false, NULL); 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(ifp)) 26238c2ecf20Sopenharmony_ci return -1; 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci create = 1; 26268c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 26278c2ecf20Sopenharmony_ci ifp->flags |= IFA_F_MANAGETEMPADDR; 26288c2ecf20Sopenharmony_ci ifp->cstamp = jiffies; 26298c2ecf20Sopenharmony_ci ifp->tokenized = tokenized; 26308c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 26318c2ecf20Sopenharmony_ci addrconf_dad_start(ifp); 26328c2ecf20Sopenharmony_ci } 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ci if (ifp) { 26358c2ecf20Sopenharmony_ci u32 flags; 26368c2ecf20Sopenharmony_ci unsigned long now; 26378c2ecf20Sopenharmony_ci u32 stored_lft; 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci /* update lifetime (RFC2462 5.5.3 e) */ 26408c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 26418c2ecf20Sopenharmony_ci now = jiffies; 26428c2ecf20Sopenharmony_ci if (ifp->valid_lft > (now - ifp->tstamp) / HZ) 26438c2ecf20Sopenharmony_ci stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ; 26448c2ecf20Sopenharmony_ci else 26458c2ecf20Sopenharmony_ci stored_lft = 0; 26468c2ecf20Sopenharmony_ci if (!create && stored_lft) { 26478c2ecf20Sopenharmony_ci const u32 minimum_lft = min_t(u32, 26488c2ecf20Sopenharmony_ci stored_lft, MIN_VALID_LIFETIME); 26498c2ecf20Sopenharmony_ci valid_lft = max(valid_lft, minimum_lft); 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_ci /* RFC4862 Section 5.5.3e: 26528c2ecf20Sopenharmony_ci * "Note that the preferred lifetime of the 26538c2ecf20Sopenharmony_ci * corresponding address is always reset to 26548c2ecf20Sopenharmony_ci * the Preferred Lifetime in the received 26558c2ecf20Sopenharmony_ci * Prefix Information option, regardless of 26568c2ecf20Sopenharmony_ci * whether the valid lifetime is also reset or 26578c2ecf20Sopenharmony_ci * ignored." 26588c2ecf20Sopenharmony_ci * 26598c2ecf20Sopenharmony_ci * So we should always update prefered_lft here. 26608c2ecf20Sopenharmony_ci */ 26618c2ecf20Sopenharmony_ci update_lft = 1; 26628c2ecf20Sopenharmony_ci } 26638c2ecf20Sopenharmony_ci 26648c2ecf20Sopenharmony_ci if (update_lft) { 26658c2ecf20Sopenharmony_ci ifp->valid_lft = valid_lft; 26668c2ecf20Sopenharmony_ci ifp->prefered_lft = prefered_lft; 26678c2ecf20Sopenharmony_ci ifp->tstamp = now; 26688c2ecf20Sopenharmony_ci flags = ifp->flags; 26698c2ecf20Sopenharmony_ci ifp->flags &= ~IFA_F_DEPRECATED; 26708c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_ci if (!(flags&IFA_F_TENTATIVE)) 26738c2ecf20Sopenharmony_ci ipv6_ifa_notify(0, ifp); 26748c2ecf20Sopenharmony_ci } else 26758c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft, 26788c2ecf20Sopenharmony_ci create, now); 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 26818c2ecf20Sopenharmony_ci addrconf_verify(); 26828c2ecf20Sopenharmony_ci } 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_ci return 0; 26858c2ecf20Sopenharmony_ci} 26868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr); 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_civoid addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) 26898c2ecf20Sopenharmony_ci{ 26908c2ecf20Sopenharmony_ci struct prefix_info *pinfo; 26918c2ecf20Sopenharmony_ci __u32 valid_lft; 26928c2ecf20Sopenharmony_ci __u32 prefered_lft; 26938c2ecf20Sopenharmony_ci int addr_type, err; 26948c2ecf20Sopenharmony_ci u32 addr_flags = 0; 26958c2ecf20Sopenharmony_ci struct inet6_dev *in6_dev; 26968c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci pinfo = (struct prefix_info *) opt; 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci if (len < sizeof(struct prefix_info)) { 27018c2ecf20Sopenharmony_ci netdev_dbg(dev, "addrconf: prefix option too short\n"); 27028c2ecf20Sopenharmony_ci return; 27038c2ecf20Sopenharmony_ci } 27048c2ecf20Sopenharmony_ci 27058c2ecf20Sopenharmony_ci /* 27068c2ecf20Sopenharmony_ci * Validation checks ([ADDRCONF], page 19) 27078c2ecf20Sopenharmony_ci */ 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci addr_type = ipv6_addr_type(&pinfo->prefix); 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci if (addr_type & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)) 27128c2ecf20Sopenharmony_ci return; 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci valid_lft = ntohl(pinfo->valid); 27158c2ecf20Sopenharmony_ci prefered_lft = ntohl(pinfo->prefered); 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci if (prefered_lft > valid_lft) { 27188c2ecf20Sopenharmony_ci net_warn_ratelimited("addrconf: prefix option has invalid lifetime\n"); 27198c2ecf20Sopenharmony_ci return; 27208c2ecf20Sopenharmony_ci } 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci in6_dev = in6_dev_get(dev); 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_ci if (!in6_dev) { 27258c2ecf20Sopenharmony_ci net_dbg_ratelimited("addrconf: device %s not configured\n", 27268c2ecf20Sopenharmony_ci dev->name); 27278c2ecf20Sopenharmony_ci return; 27288c2ecf20Sopenharmony_ci } 27298c2ecf20Sopenharmony_ci 27308c2ecf20Sopenharmony_ci if (valid_lft != 0 && valid_lft < in6_dev->cnf.accept_ra_min_lft) 27318c2ecf20Sopenharmony_ci goto put; 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci /* 27348c2ecf20Sopenharmony_ci * Two things going on here: 27358c2ecf20Sopenharmony_ci * 1) Add routes for on-link prefixes 27368c2ecf20Sopenharmony_ci * 2) Configure prefixes with the auto flag set 27378c2ecf20Sopenharmony_ci */ 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci if (pinfo->onlink) { 27408c2ecf20Sopenharmony_ci struct fib6_info *rt; 27418c2ecf20Sopenharmony_ci unsigned long rt_expires; 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci /* Avoid arithmetic overflow. Really, we could 27448c2ecf20Sopenharmony_ci * save rt_expires in seconds, likely valid_lft, 27458c2ecf20Sopenharmony_ci * but it would require division in fib gc, that it 27468c2ecf20Sopenharmony_ci * not good. 27478c2ecf20Sopenharmony_ci */ 27488c2ecf20Sopenharmony_ci if (HZ > USER_HZ) 27498c2ecf20Sopenharmony_ci rt_expires = addrconf_timeout_fixup(valid_lft, HZ); 27508c2ecf20Sopenharmony_ci else 27518c2ecf20Sopenharmony_ci rt_expires = addrconf_timeout_fixup(valid_lft, USER_HZ); 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci if (addrconf_finite_timeout(rt_expires)) 27548c2ecf20Sopenharmony_ci rt_expires *= HZ; 27558c2ecf20Sopenharmony_ci 27568c2ecf20Sopenharmony_ci rt = addrconf_get_prefix_route(&pinfo->prefix, 27578c2ecf20Sopenharmony_ci pinfo->prefix_len, 27588c2ecf20Sopenharmony_ci dev, 27598c2ecf20Sopenharmony_ci RTF_ADDRCONF | RTF_PREFIX_RT, 27608c2ecf20Sopenharmony_ci RTF_DEFAULT, true); 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci if (rt) { 27638c2ecf20Sopenharmony_ci /* Autoconf prefix route */ 27648c2ecf20Sopenharmony_ci if (valid_lft == 0) { 27658c2ecf20Sopenharmony_ci ip6_del_rt(net, rt, false); 27668c2ecf20Sopenharmony_ci rt = NULL; 27678c2ecf20Sopenharmony_ci } else if (addrconf_finite_timeout(rt_expires)) { 27688c2ecf20Sopenharmony_ci /* not infinity */ 27698c2ecf20Sopenharmony_ci fib6_set_expires(rt, jiffies + rt_expires); 27708c2ecf20Sopenharmony_ci } else { 27718c2ecf20Sopenharmony_ci fib6_clean_expires(rt); 27728c2ecf20Sopenharmony_ci } 27738c2ecf20Sopenharmony_ci } else if (valid_lft) { 27748c2ecf20Sopenharmony_ci clock_t expires = 0; 27758c2ecf20Sopenharmony_ci int flags = RTF_ADDRCONF | RTF_PREFIX_RT; 27768c2ecf20Sopenharmony_ci if (addrconf_finite_timeout(rt_expires)) { 27778c2ecf20Sopenharmony_ci /* not infinity */ 27788c2ecf20Sopenharmony_ci flags |= RTF_EXPIRES; 27798c2ecf20Sopenharmony_ci expires = jiffies_to_clock_t(rt_expires); 27808c2ecf20Sopenharmony_ci } 27818c2ecf20Sopenharmony_ci addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len, 27828c2ecf20Sopenharmony_ci 0, dev, expires, flags, 27838c2ecf20Sopenharmony_ci GFP_ATOMIC); 27848c2ecf20Sopenharmony_ci } 27858c2ecf20Sopenharmony_ci fib6_info_release(rt); 27868c2ecf20Sopenharmony_ci } 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci /* Try to figure out our local address for this prefix */ 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci if (pinfo->autoconf && in6_dev->cnf.autoconf) { 27918c2ecf20Sopenharmony_ci struct in6_addr addr; 27928c2ecf20Sopenharmony_ci bool tokenized = false, dev_addr_generated = false; 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci if (pinfo->prefix_len == 64) { 27958c2ecf20Sopenharmony_ci memcpy(&addr, &pinfo->prefix, 8); 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&in6_dev->token)) { 27988c2ecf20Sopenharmony_ci read_lock_bh(&in6_dev->lock); 27998c2ecf20Sopenharmony_ci memcpy(addr.s6_addr + 8, 28008c2ecf20Sopenharmony_ci in6_dev->token.s6_addr + 8, 8); 28018c2ecf20Sopenharmony_ci read_unlock_bh(&in6_dev->lock); 28028c2ecf20Sopenharmony_ci tokenized = true; 28038c2ecf20Sopenharmony_ci } else if (is_addr_mode_generate_stable(in6_dev) && 28048c2ecf20Sopenharmony_ci !ipv6_generate_stable_address(&addr, 0, 28058c2ecf20Sopenharmony_ci in6_dev)) { 28068c2ecf20Sopenharmony_ci addr_flags |= IFA_F_STABLE_PRIVACY; 28078c2ecf20Sopenharmony_ci goto ok; 28088c2ecf20Sopenharmony_ci } else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) && 28098c2ecf20Sopenharmony_ci ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) { 28108c2ecf20Sopenharmony_ci goto put; 28118c2ecf20Sopenharmony_ci } else { 28128c2ecf20Sopenharmony_ci dev_addr_generated = true; 28138c2ecf20Sopenharmony_ci } 28148c2ecf20Sopenharmony_ci goto ok; 28158c2ecf20Sopenharmony_ci } 28168c2ecf20Sopenharmony_ci net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n", 28178c2ecf20Sopenharmony_ci pinfo->prefix_len); 28188c2ecf20Sopenharmony_ci goto put; 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ciok: 28218c2ecf20Sopenharmony_ci err = addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, 28228c2ecf20Sopenharmony_ci &addr, addr_type, 28238c2ecf20Sopenharmony_ci addr_flags, sllao, 28248c2ecf20Sopenharmony_ci tokenized, valid_lft, 28258c2ecf20Sopenharmony_ci prefered_lft); 28268c2ecf20Sopenharmony_ci if (err) 28278c2ecf20Sopenharmony_ci goto put; 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_ci /* Ignore error case here because previous prefix add addr was 28308c2ecf20Sopenharmony_ci * successful which will be notified. 28318c2ecf20Sopenharmony_ci */ 28328c2ecf20Sopenharmony_ci ndisc_ops_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, &addr, 28338c2ecf20Sopenharmony_ci addr_type, addr_flags, sllao, 28348c2ecf20Sopenharmony_ci tokenized, valid_lft, 28358c2ecf20Sopenharmony_ci prefered_lft, 28368c2ecf20Sopenharmony_ci dev_addr_generated); 28378c2ecf20Sopenharmony_ci } 28388c2ecf20Sopenharmony_ci inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo); 28398c2ecf20Sopenharmony_ciput: 28408c2ecf20Sopenharmony_ci in6_dev_put(in6_dev); 28418c2ecf20Sopenharmony_ci} 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_cistatic int addrconf_set_sit_dstaddr(struct net *net, struct net_device *dev, 28448c2ecf20Sopenharmony_ci struct in6_ifreq *ireq) 28458c2ecf20Sopenharmony_ci{ 28468c2ecf20Sopenharmony_ci struct ip_tunnel_parm p = { }; 28478c2ecf20Sopenharmony_ci int err; 28488c2ecf20Sopenharmony_ci 28498c2ecf20Sopenharmony_ci if (!(ipv6_addr_type(&ireq->ifr6_addr) & IPV6_ADDR_COMPATv4)) 28508c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci p.iph.daddr = ireq->ifr6_addr.s6_addr32[3]; 28538c2ecf20Sopenharmony_ci p.iph.version = 4; 28548c2ecf20Sopenharmony_ci p.iph.ihl = 5; 28558c2ecf20Sopenharmony_ci p.iph.protocol = IPPROTO_IPV6; 28568c2ecf20Sopenharmony_ci p.iph.ttl = 64; 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci if (!dev->netdev_ops->ndo_tunnel_ctl) 28598c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 28608c2ecf20Sopenharmony_ci err = dev->netdev_ops->ndo_tunnel_ctl(dev, &p, SIOCADDTUNNEL); 28618c2ecf20Sopenharmony_ci if (err) 28628c2ecf20Sopenharmony_ci return err; 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci dev = __dev_get_by_name(net, p.name); 28658c2ecf20Sopenharmony_ci if (!dev) 28668c2ecf20Sopenharmony_ci return -ENOBUFS; 28678c2ecf20Sopenharmony_ci return dev_open(dev, NULL); 28688c2ecf20Sopenharmony_ci} 28698c2ecf20Sopenharmony_ci 28708c2ecf20Sopenharmony_ci/* 28718c2ecf20Sopenharmony_ci * Set destination address. 28728c2ecf20Sopenharmony_ci * Special case for SIT interfaces where we create a new "virtual" 28738c2ecf20Sopenharmony_ci * device. 28748c2ecf20Sopenharmony_ci */ 28758c2ecf20Sopenharmony_ciint addrconf_set_dstaddr(struct net *net, void __user *arg) 28768c2ecf20Sopenharmony_ci{ 28778c2ecf20Sopenharmony_ci struct net_device *dev; 28788c2ecf20Sopenharmony_ci struct in6_ifreq ireq; 28798c2ecf20Sopenharmony_ci int err = -ENODEV; 28808c2ecf20Sopenharmony_ci 28818c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_IPV6_SIT)) 28828c2ecf20Sopenharmony_ci return -ENODEV; 28838c2ecf20Sopenharmony_ci if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) 28848c2ecf20Sopenharmony_ci return -EFAULT; 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_ci rtnl_lock(); 28878c2ecf20Sopenharmony_ci dev = __dev_get_by_index(net, ireq.ifr6_ifindex); 28888c2ecf20Sopenharmony_ci if (dev && dev->type == ARPHRD_SIT) 28898c2ecf20Sopenharmony_ci err = addrconf_set_sit_dstaddr(net, dev, &ireq); 28908c2ecf20Sopenharmony_ci rtnl_unlock(); 28918c2ecf20Sopenharmony_ci return err; 28928c2ecf20Sopenharmony_ci} 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_cistatic int ipv6_mc_config(struct sock *sk, bool join, 28958c2ecf20Sopenharmony_ci const struct in6_addr *addr, int ifindex) 28968c2ecf20Sopenharmony_ci{ 28978c2ecf20Sopenharmony_ci int ret; 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_ci ASSERT_RTNL(); 29008c2ecf20Sopenharmony_ci 29018c2ecf20Sopenharmony_ci lock_sock(sk); 29028c2ecf20Sopenharmony_ci if (join) 29038c2ecf20Sopenharmony_ci ret = ipv6_sock_mc_join(sk, ifindex, addr); 29048c2ecf20Sopenharmony_ci else 29058c2ecf20Sopenharmony_ci ret = ipv6_sock_mc_drop(sk, ifindex, addr); 29068c2ecf20Sopenharmony_ci release_sock(sk); 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci return ret; 29098c2ecf20Sopenharmony_ci} 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci/* 29128c2ecf20Sopenharmony_ci * Manual configuration of address on an interface 29138c2ecf20Sopenharmony_ci */ 29148c2ecf20Sopenharmony_cistatic int inet6_addr_add(struct net *net, int ifindex, 29158c2ecf20Sopenharmony_ci struct ifa6_config *cfg, 29168c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 29178c2ecf20Sopenharmony_ci{ 29188c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 29198c2ecf20Sopenharmony_ci struct inet6_dev *idev; 29208c2ecf20Sopenharmony_ci struct net_device *dev; 29218c2ecf20Sopenharmony_ci unsigned long timeout; 29228c2ecf20Sopenharmony_ci clock_t expires; 29238c2ecf20Sopenharmony_ci u32 flags; 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci ASSERT_RTNL(); 29268c2ecf20Sopenharmony_ci 29278c2ecf20Sopenharmony_ci if (cfg->plen > 128) 29288c2ecf20Sopenharmony_ci return -EINVAL; 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_ci /* check the lifetime */ 29318c2ecf20Sopenharmony_ci if (!cfg->valid_lft || cfg->preferred_lft > cfg->valid_lft) 29328c2ecf20Sopenharmony_ci return -EINVAL; 29338c2ecf20Sopenharmony_ci 29348c2ecf20Sopenharmony_ci if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR && cfg->plen != 64) 29358c2ecf20Sopenharmony_ci return -EINVAL; 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_ci dev = __dev_get_by_index(net, ifindex); 29388c2ecf20Sopenharmony_ci if (!dev) 29398c2ecf20Sopenharmony_ci return -ENODEV; 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_ci idev = addrconf_add_dev(dev); 29428c2ecf20Sopenharmony_ci if (IS_ERR(idev)) 29438c2ecf20Sopenharmony_ci return PTR_ERR(idev); 29448c2ecf20Sopenharmony_ci 29458c2ecf20Sopenharmony_ci if (cfg->ifa_flags & IFA_F_MCAUTOJOIN) { 29468c2ecf20Sopenharmony_ci int ret = ipv6_mc_config(net->ipv6.mc_autojoin_sk, 29478c2ecf20Sopenharmony_ci true, cfg->pfx, ifindex); 29488c2ecf20Sopenharmony_ci 29498c2ecf20Sopenharmony_ci if (ret < 0) 29508c2ecf20Sopenharmony_ci return ret; 29518c2ecf20Sopenharmony_ci } 29528c2ecf20Sopenharmony_ci 29538c2ecf20Sopenharmony_ci cfg->scope = ipv6_addr_scope(cfg->pfx); 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_ci timeout = addrconf_timeout_fixup(cfg->valid_lft, HZ); 29568c2ecf20Sopenharmony_ci if (addrconf_finite_timeout(timeout)) { 29578c2ecf20Sopenharmony_ci expires = jiffies_to_clock_t(timeout * HZ); 29588c2ecf20Sopenharmony_ci cfg->valid_lft = timeout; 29598c2ecf20Sopenharmony_ci flags = RTF_EXPIRES; 29608c2ecf20Sopenharmony_ci } else { 29618c2ecf20Sopenharmony_ci expires = 0; 29628c2ecf20Sopenharmony_ci flags = 0; 29638c2ecf20Sopenharmony_ci cfg->ifa_flags |= IFA_F_PERMANENT; 29648c2ecf20Sopenharmony_ci } 29658c2ecf20Sopenharmony_ci 29668c2ecf20Sopenharmony_ci timeout = addrconf_timeout_fixup(cfg->preferred_lft, HZ); 29678c2ecf20Sopenharmony_ci if (addrconf_finite_timeout(timeout)) { 29688c2ecf20Sopenharmony_ci if (timeout == 0) 29698c2ecf20Sopenharmony_ci cfg->ifa_flags |= IFA_F_DEPRECATED; 29708c2ecf20Sopenharmony_ci cfg->preferred_lft = timeout; 29718c2ecf20Sopenharmony_ci } 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_ci ifp = ipv6_add_addr(idev, cfg, true, extack); 29748c2ecf20Sopenharmony_ci if (!IS_ERR(ifp)) { 29758c2ecf20Sopenharmony_ci if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) { 29768c2ecf20Sopenharmony_ci addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 29778c2ecf20Sopenharmony_ci ifp->rt_priority, dev, expires, 29788c2ecf20Sopenharmony_ci flags, GFP_KERNEL); 29798c2ecf20Sopenharmony_ci } 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci /* Send a netlink notification if DAD is enabled and 29828c2ecf20Sopenharmony_ci * optimistic flag is not set 29838c2ecf20Sopenharmony_ci */ 29848c2ecf20Sopenharmony_ci if (!(ifp->flags & (IFA_F_OPTIMISTIC | IFA_F_NODAD))) 29858c2ecf20Sopenharmony_ci ipv6_ifa_notify(0, ifp); 29868c2ecf20Sopenharmony_ci /* 29878c2ecf20Sopenharmony_ci * Note that section 3.1 of RFC 4429 indicates 29888c2ecf20Sopenharmony_ci * that the Optimistic flag should not be set for 29898c2ecf20Sopenharmony_ci * manually configured addresses 29908c2ecf20Sopenharmony_ci */ 29918c2ecf20Sopenharmony_ci addrconf_dad_start(ifp); 29928c2ecf20Sopenharmony_ci if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR) 29938c2ecf20Sopenharmony_ci manage_tempaddrs(idev, ifp, cfg->valid_lft, 29948c2ecf20Sopenharmony_ci cfg->preferred_lft, true, jiffies); 29958c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 29968c2ecf20Sopenharmony_ci addrconf_verify_rtnl(); 29978c2ecf20Sopenharmony_ci return 0; 29988c2ecf20Sopenharmony_ci } else if (cfg->ifa_flags & IFA_F_MCAUTOJOIN) { 29998c2ecf20Sopenharmony_ci ipv6_mc_config(net->ipv6.mc_autojoin_sk, false, 30008c2ecf20Sopenharmony_ci cfg->pfx, ifindex); 30018c2ecf20Sopenharmony_ci } 30028c2ecf20Sopenharmony_ci 30038c2ecf20Sopenharmony_ci return PTR_ERR(ifp); 30048c2ecf20Sopenharmony_ci} 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_cistatic int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags, 30078c2ecf20Sopenharmony_ci const struct in6_addr *pfx, unsigned int plen) 30088c2ecf20Sopenharmony_ci{ 30098c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 30108c2ecf20Sopenharmony_ci struct inet6_dev *idev; 30118c2ecf20Sopenharmony_ci struct net_device *dev; 30128c2ecf20Sopenharmony_ci 30138c2ecf20Sopenharmony_ci if (plen > 128) 30148c2ecf20Sopenharmony_ci return -EINVAL; 30158c2ecf20Sopenharmony_ci 30168c2ecf20Sopenharmony_ci dev = __dev_get_by_index(net, ifindex); 30178c2ecf20Sopenharmony_ci if (!dev) 30188c2ecf20Sopenharmony_ci return -ENODEV; 30198c2ecf20Sopenharmony_ci 30208c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 30218c2ecf20Sopenharmony_ci if (!idev) 30228c2ecf20Sopenharmony_ci return -ENXIO; 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 30258c2ecf20Sopenharmony_ci list_for_each_entry(ifp, &idev->addr_list, if_list) { 30268c2ecf20Sopenharmony_ci if (ifp->prefix_len == plen && 30278c2ecf20Sopenharmony_ci ipv6_addr_equal(pfx, &ifp->addr)) { 30288c2ecf20Sopenharmony_ci in6_ifa_hold(ifp); 30298c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci if (!(ifp->flags & IFA_F_TEMPORARY) && 30328c2ecf20Sopenharmony_ci (ifa_flags & IFA_F_MANAGETEMPADDR)) 30338c2ecf20Sopenharmony_ci manage_tempaddrs(idev, ifp, 0, 0, false, 30348c2ecf20Sopenharmony_ci jiffies); 30358c2ecf20Sopenharmony_ci ipv6_del_addr(ifp); 30368c2ecf20Sopenharmony_ci addrconf_verify_rtnl(); 30378c2ecf20Sopenharmony_ci if (ipv6_addr_is_multicast(pfx)) { 30388c2ecf20Sopenharmony_ci ipv6_mc_config(net->ipv6.mc_autojoin_sk, 30398c2ecf20Sopenharmony_ci false, pfx, dev->ifindex); 30408c2ecf20Sopenharmony_ci } 30418c2ecf20Sopenharmony_ci return 0; 30428c2ecf20Sopenharmony_ci } 30438c2ecf20Sopenharmony_ci } 30448c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 30458c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 30468c2ecf20Sopenharmony_ci} 30478c2ecf20Sopenharmony_ci 30488c2ecf20Sopenharmony_ci 30498c2ecf20Sopenharmony_ciint addrconf_add_ifaddr(struct net *net, void __user *arg) 30508c2ecf20Sopenharmony_ci{ 30518c2ecf20Sopenharmony_ci struct ifa6_config cfg = { 30528c2ecf20Sopenharmony_ci .ifa_flags = IFA_F_PERMANENT, 30538c2ecf20Sopenharmony_ci .preferred_lft = INFINITY_LIFE_TIME, 30548c2ecf20Sopenharmony_ci .valid_lft = INFINITY_LIFE_TIME, 30558c2ecf20Sopenharmony_ci }; 30568c2ecf20Sopenharmony_ci struct in6_ifreq ireq; 30578c2ecf20Sopenharmony_ci int err; 30588c2ecf20Sopenharmony_ci 30598c2ecf20Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 30608c2ecf20Sopenharmony_ci return -EPERM; 30618c2ecf20Sopenharmony_ci 30628c2ecf20Sopenharmony_ci if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) 30638c2ecf20Sopenharmony_ci return -EFAULT; 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_ci cfg.pfx = &ireq.ifr6_addr; 30668c2ecf20Sopenharmony_ci cfg.plen = ireq.ifr6_prefixlen; 30678c2ecf20Sopenharmony_ci 30688c2ecf20Sopenharmony_ci rtnl_lock(); 30698c2ecf20Sopenharmony_ci err = inet6_addr_add(net, ireq.ifr6_ifindex, &cfg, NULL); 30708c2ecf20Sopenharmony_ci rtnl_unlock(); 30718c2ecf20Sopenharmony_ci return err; 30728c2ecf20Sopenharmony_ci} 30738c2ecf20Sopenharmony_ci 30748c2ecf20Sopenharmony_ciint addrconf_del_ifaddr(struct net *net, void __user *arg) 30758c2ecf20Sopenharmony_ci{ 30768c2ecf20Sopenharmony_ci struct in6_ifreq ireq; 30778c2ecf20Sopenharmony_ci int err; 30788c2ecf20Sopenharmony_ci 30798c2ecf20Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 30808c2ecf20Sopenharmony_ci return -EPERM; 30818c2ecf20Sopenharmony_ci 30828c2ecf20Sopenharmony_ci if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) 30838c2ecf20Sopenharmony_ci return -EFAULT; 30848c2ecf20Sopenharmony_ci 30858c2ecf20Sopenharmony_ci rtnl_lock(); 30868c2ecf20Sopenharmony_ci err = inet6_addr_del(net, ireq.ifr6_ifindex, 0, &ireq.ifr6_addr, 30878c2ecf20Sopenharmony_ci ireq.ifr6_prefixlen); 30888c2ecf20Sopenharmony_ci rtnl_unlock(); 30898c2ecf20Sopenharmony_ci return err; 30908c2ecf20Sopenharmony_ci} 30918c2ecf20Sopenharmony_ci 30928c2ecf20Sopenharmony_cistatic void add_addr(struct inet6_dev *idev, const struct in6_addr *addr, 30938c2ecf20Sopenharmony_ci int plen, int scope) 30948c2ecf20Sopenharmony_ci{ 30958c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 30968c2ecf20Sopenharmony_ci struct ifa6_config cfg = { 30978c2ecf20Sopenharmony_ci .pfx = addr, 30988c2ecf20Sopenharmony_ci .plen = plen, 30998c2ecf20Sopenharmony_ci .ifa_flags = IFA_F_PERMANENT, 31008c2ecf20Sopenharmony_ci .valid_lft = INFINITY_LIFE_TIME, 31018c2ecf20Sopenharmony_ci .preferred_lft = INFINITY_LIFE_TIME, 31028c2ecf20Sopenharmony_ci .scope = scope 31038c2ecf20Sopenharmony_ci }; 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ci ifp = ipv6_add_addr(idev, &cfg, true, NULL); 31068c2ecf20Sopenharmony_ci if (!IS_ERR(ifp)) { 31078c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 31088c2ecf20Sopenharmony_ci ifp->flags &= ~IFA_F_TENTATIVE; 31098c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 31108c2ecf20Sopenharmony_ci rt_genid_bump_ipv6(dev_net(idev->dev)); 31118c2ecf20Sopenharmony_ci ipv6_ifa_notify(RTM_NEWADDR, ifp); 31128c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 31138c2ecf20Sopenharmony_ci } 31148c2ecf20Sopenharmony_ci} 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT) 31178c2ecf20Sopenharmony_cistatic void sit_add_v4_addrs(struct inet6_dev *idev) 31188c2ecf20Sopenharmony_ci{ 31198c2ecf20Sopenharmony_ci struct in6_addr addr; 31208c2ecf20Sopenharmony_ci struct net_device *dev; 31218c2ecf20Sopenharmony_ci struct net *net = dev_net(idev->dev); 31228c2ecf20Sopenharmony_ci int scope, plen; 31238c2ecf20Sopenharmony_ci u32 pflags = 0; 31248c2ecf20Sopenharmony_ci 31258c2ecf20Sopenharmony_ci ASSERT_RTNL(); 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_ci memset(&addr, 0, sizeof(struct in6_addr)); 31288c2ecf20Sopenharmony_ci memcpy(&addr.s6_addr32[3], idev->dev->dev_addr, 4); 31298c2ecf20Sopenharmony_ci 31308c2ecf20Sopenharmony_ci if (idev->dev->flags&IFF_POINTOPOINT) { 31318c2ecf20Sopenharmony_ci if (idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_NONE) 31328c2ecf20Sopenharmony_ci return; 31338c2ecf20Sopenharmony_ci 31348c2ecf20Sopenharmony_ci addr.s6_addr32[0] = htonl(0xfe800000); 31358c2ecf20Sopenharmony_ci scope = IFA_LINK; 31368c2ecf20Sopenharmony_ci plen = 64; 31378c2ecf20Sopenharmony_ci } else { 31388c2ecf20Sopenharmony_ci scope = IPV6_ADDR_COMPATv4; 31398c2ecf20Sopenharmony_ci plen = 96; 31408c2ecf20Sopenharmony_ci pflags |= RTF_NONEXTHOP; 31418c2ecf20Sopenharmony_ci } 31428c2ecf20Sopenharmony_ci 31438c2ecf20Sopenharmony_ci if (addr.s6_addr32[3]) { 31448c2ecf20Sopenharmony_ci add_addr(idev, &addr, plen, scope); 31458c2ecf20Sopenharmony_ci addrconf_prefix_route(&addr, plen, 0, idev->dev, 0, pflags, 31468c2ecf20Sopenharmony_ci GFP_KERNEL); 31478c2ecf20Sopenharmony_ci return; 31488c2ecf20Sopenharmony_ci } 31498c2ecf20Sopenharmony_ci 31508c2ecf20Sopenharmony_ci for_each_netdev(net, dev) { 31518c2ecf20Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rtnl(dev); 31528c2ecf20Sopenharmony_ci if (in_dev && (dev->flags & IFF_UP)) { 31538c2ecf20Sopenharmony_ci struct in_ifaddr *ifa; 31548c2ecf20Sopenharmony_ci int flag = scope; 31558c2ecf20Sopenharmony_ci 31568c2ecf20Sopenharmony_ci in_dev_for_each_ifa_rtnl(ifa, in_dev) { 31578c2ecf20Sopenharmony_ci addr.s6_addr32[3] = ifa->ifa_local; 31588c2ecf20Sopenharmony_ci 31598c2ecf20Sopenharmony_ci if (ifa->ifa_scope == RT_SCOPE_LINK) 31608c2ecf20Sopenharmony_ci continue; 31618c2ecf20Sopenharmony_ci if (ifa->ifa_scope >= RT_SCOPE_HOST) { 31628c2ecf20Sopenharmony_ci if (idev->dev->flags&IFF_POINTOPOINT) 31638c2ecf20Sopenharmony_ci continue; 31648c2ecf20Sopenharmony_ci flag |= IFA_HOST; 31658c2ecf20Sopenharmony_ci } 31668c2ecf20Sopenharmony_ci 31678c2ecf20Sopenharmony_ci add_addr(idev, &addr, plen, flag); 31688c2ecf20Sopenharmony_ci addrconf_prefix_route(&addr, plen, 0, idev->dev, 31698c2ecf20Sopenharmony_ci 0, pflags, GFP_KERNEL); 31708c2ecf20Sopenharmony_ci } 31718c2ecf20Sopenharmony_ci } 31728c2ecf20Sopenharmony_ci } 31738c2ecf20Sopenharmony_ci} 31748c2ecf20Sopenharmony_ci#endif 31758c2ecf20Sopenharmony_ci 31768c2ecf20Sopenharmony_cistatic void init_loopback(struct net_device *dev) 31778c2ecf20Sopenharmony_ci{ 31788c2ecf20Sopenharmony_ci struct inet6_dev *idev; 31798c2ecf20Sopenharmony_ci 31808c2ecf20Sopenharmony_ci /* ::1 */ 31818c2ecf20Sopenharmony_ci 31828c2ecf20Sopenharmony_ci ASSERT_RTNL(); 31838c2ecf20Sopenharmony_ci 31848c2ecf20Sopenharmony_ci idev = ipv6_find_idev(dev); 31858c2ecf20Sopenharmony_ci if (IS_ERR(idev)) { 31868c2ecf20Sopenharmony_ci pr_debug("%s: add_dev failed\n", __func__); 31878c2ecf20Sopenharmony_ci return; 31888c2ecf20Sopenharmony_ci } 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci add_addr(idev, &in6addr_loopback, 128, IFA_HOST); 31918c2ecf20Sopenharmony_ci} 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_civoid addrconf_add_linklocal(struct inet6_dev *idev, 31948c2ecf20Sopenharmony_ci const struct in6_addr *addr, u32 flags) 31958c2ecf20Sopenharmony_ci{ 31968c2ecf20Sopenharmony_ci struct ifa6_config cfg = { 31978c2ecf20Sopenharmony_ci .pfx = addr, 31988c2ecf20Sopenharmony_ci .plen = 64, 31998c2ecf20Sopenharmony_ci .ifa_flags = flags | IFA_F_PERMANENT, 32008c2ecf20Sopenharmony_ci .valid_lft = INFINITY_LIFE_TIME, 32018c2ecf20Sopenharmony_ci .preferred_lft = INFINITY_LIFE_TIME, 32028c2ecf20Sopenharmony_ci .scope = IFA_LINK 32038c2ecf20Sopenharmony_ci }; 32048c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 32078c2ecf20Sopenharmony_ci if ((dev_net(idev->dev)->ipv6.devconf_all->optimistic_dad || 32088c2ecf20Sopenharmony_ci idev->cnf.optimistic_dad) && 32098c2ecf20Sopenharmony_ci !dev_net(idev->dev)->ipv6.devconf_all->forwarding) 32108c2ecf20Sopenharmony_ci cfg.ifa_flags |= IFA_F_OPTIMISTIC; 32118c2ecf20Sopenharmony_ci#endif 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_ci ifp = ipv6_add_addr(idev, &cfg, true, NULL); 32148c2ecf20Sopenharmony_ci if (!IS_ERR(ifp)) { 32158c2ecf20Sopenharmony_ci addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 0, idev->dev, 32168c2ecf20Sopenharmony_ci 0, 0, GFP_ATOMIC); 32178c2ecf20Sopenharmony_ci addrconf_dad_start(ifp); 32188c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 32198c2ecf20Sopenharmony_ci } 32208c2ecf20Sopenharmony_ci} 32218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(addrconf_add_linklocal); 32228c2ecf20Sopenharmony_ci 32238c2ecf20Sopenharmony_cistatic bool ipv6_reserved_interfaceid(struct in6_addr address) 32248c2ecf20Sopenharmony_ci{ 32258c2ecf20Sopenharmony_ci if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0) 32268c2ecf20Sopenharmony_ci return true; 32278c2ecf20Sopenharmony_ci 32288c2ecf20Sopenharmony_ci if (address.s6_addr32[2] == htonl(0x02005eff) && 32298c2ecf20Sopenharmony_ci ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000))) 32308c2ecf20Sopenharmony_ci return true; 32318c2ecf20Sopenharmony_ci 32328c2ecf20Sopenharmony_ci if (address.s6_addr32[2] == htonl(0xfdffffff) && 32338c2ecf20Sopenharmony_ci ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80))) 32348c2ecf20Sopenharmony_ci return true; 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_ci return false; 32378c2ecf20Sopenharmony_ci} 32388c2ecf20Sopenharmony_ci 32398c2ecf20Sopenharmony_cistatic int ipv6_generate_stable_address(struct in6_addr *address, 32408c2ecf20Sopenharmony_ci u8 dad_count, 32418c2ecf20Sopenharmony_ci const struct inet6_dev *idev) 32428c2ecf20Sopenharmony_ci{ 32438c2ecf20Sopenharmony_ci static DEFINE_SPINLOCK(lock); 32448c2ecf20Sopenharmony_ci static __u32 digest[SHA1_DIGEST_WORDS]; 32458c2ecf20Sopenharmony_ci static __u32 workspace[SHA1_WORKSPACE_WORDS]; 32468c2ecf20Sopenharmony_ci 32478c2ecf20Sopenharmony_ci static union { 32488c2ecf20Sopenharmony_ci char __data[SHA1_BLOCK_SIZE]; 32498c2ecf20Sopenharmony_ci struct { 32508c2ecf20Sopenharmony_ci struct in6_addr secret; 32518c2ecf20Sopenharmony_ci __be32 prefix[2]; 32528c2ecf20Sopenharmony_ci unsigned char hwaddr[MAX_ADDR_LEN]; 32538c2ecf20Sopenharmony_ci u8 dad_count; 32548c2ecf20Sopenharmony_ci } __packed; 32558c2ecf20Sopenharmony_ci } data; 32568c2ecf20Sopenharmony_ci 32578c2ecf20Sopenharmony_ci struct in6_addr secret; 32588c2ecf20Sopenharmony_ci struct in6_addr temp; 32598c2ecf20Sopenharmony_ci struct net *net = dev_net(idev->dev); 32608c2ecf20Sopenharmony_ci 32618c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(data.__data) != sizeof(data)); 32628c2ecf20Sopenharmony_ci 32638c2ecf20Sopenharmony_ci if (idev->cnf.stable_secret.initialized) 32648c2ecf20Sopenharmony_ci secret = idev->cnf.stable_secret.secret; 32658c2ecf20Sopenharmony_ci else if (net->ipv6.devconf_dflt->stable_secret.initialized) 32668c2ecf20Sopenharmony_ci secret = net->ipv6.devconf_dflt->stable_secret.secret; 32678c2ecf20Sopenharmony_ci else 32688c2ecf20Sopenharmony_ci return -1; 32698c2ecf20Sopenharmony_ci 32708c2ecf20Sopenharmony_ciretry: 32718c2ecf20Sopenharmony_ci spin_lock_bh(&lock); 32728c2ecf20Sopenharmony_ci 32738c2ecf20Sopenharmony_ci sha1_init(digest); 32748c2ecf20Sopenharmony_ci memset(&data, 0, sizeof(data)); 32758c2ecf20Sopenharmony_ci memset(workspace, 0, sizeof(workspace)); 32768c2ecf20Sopenharmony_ci memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len); 32778c2ecf20Sopenharmony_ci data.prefix[0] = address->s6_addr32[0]; 32788c2ecf20Sopenharmony_ci data.prefix[1] = address->s6_addr32[1]; 32798c2ecf20Sopenharmony_ci data.secret = secret; 32808c2ecf20Sopenharmony_ci data.dad_count = dad_count; 32818c2ecf20Sopenharmony_ci 32828c2ecf20Sopenharmony_ci sha1_transform(digest, data.__data, workspace); 32838c2ecf20Sopenharmony_ci 32848c2ecf20Sopenharmony_ci temp = *address; 32858c2ecf20Sopenharmony_ci temp.s6_addr32[2] = (__force __be32)digest[0]; 32868c2ecf20Sopenharmony_ci temp.s6_addr32[3] = (__force __be32)digest[1]; 32878c2ecf20Sopenharmony_ci 32888c2ecf20Sopenharmony_ci spin_unlock_bh(&lock); 32898c2ecf20Sopenharmony_ci 32908c2ecf20Sopenharmony_ci if (ipv6_reserved_interfaceid(temp)) { 32918c2ecf20Sopenharmony_ci dad_count++; 32928c2ecf20Sopenharmony_ci if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries) 32938c2ecf20Sopenharmony_ci return -1; 32948c2ecf20Sopenharmony_ci goto retry; 32958c2ecf20Sopenharmony_ci } 32968c2ecf20Sopenharmony_ci 32978c2ecf20Sopenharmony_ci *address = temp; 32988c2ecf20Sopenharmony_ci return 0; 32998c2ecf20Sopenharmony_ci} 33008c2ecf20Sopenharmony_ci 33018c2ecf20Sopenharmony_cistatic void ipv6_gen_mode_random_init(struct inet6_dev *idev) 33028c2ecf20Sopenharmony_ci{ 33038c2ecf20Sopenharmony_ci struct ipv6_stable_secret *s = &idev->cnf.stable_secret; 33048c2ecf20Sopenharmony_ci 33058c2ecf20Sopenharmony_ci if (s->initialized) 33068c2ecf20Sopenharmony_ci return; 33078c2ecf20Sopenharmony_ci s = &idev->cnf.stable_secret; 33088c2ecf20Sopenharmony_ci get_random_bytes(&s->secret, sizeof(s->secret)); 33098c2ecf20Sopenharmony_ci s->initialized = true; 33108c2ecf20Sopenharmony_ci} 33118c2ecf20Sopenharmony_ci 33128c2ecf20Sopenharmony_cistatic void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) 33138c2ecf20Sopenharmony_ci{ 33148c2ecf20Sopenharmony_ci struct in6_addr addr; 33158c2ecf20Sopenharmony_ci 33168c2ecf20Sopenharmony_ci /* no link local addresses on L3 master devices */ 33178c2ecf20Sopenharmony_ci if (netif_is_l3_master(idev->dev)) 33188c2ecf20Sopenharmony_ci return; 33198c2ecf20Sopenharmony_ci 33208c2ecf20Sopenharmony_ci /* no link local addresses on devices flagged as slaves */ 33218c2ecf20Sopenharmony_ci if (idev->dev->flags & IFF_SLAVE) 33228c2ecf20Sopenharmony_ci return; 33238c2ecf20Sopenharmony_ci 33248c2ecf20Sopenharmony_ci ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); 33258c2ecf20Sopenharmony_ci 33268c2ecf20Sopenharmony_ci switch (idev->cnf.addr_gen_mode) { 33278c2ecf20Sopenharmony_ci case IN6_ADDR_GEN_MODE_RANDOM: 33288c2ecf20Sopenharmony_ci ipv6_gen_mode_random_init(idev); 33298c2ecf20Sopenharmony_ci fallthrough; 33308c2ecf20Sopenharmony_ci case IN6_ADDR_GEN_MODE_STABLE_PRIVACY: 33318c2ecf20Sopenharmony_ci if (!ipv6_generate_stable_address(&addr, 0, idev)) 33328c2ecf20Sopenharmony_ci addrconf_add_linklocal(idev, &addr, 33338c2ecf20Sopenharmony_ci IFA_F_STABLE_PRIVACY); 33348c2ecf20Sopenharmony_ci else if (prefix_route) 33358c2ecf20Sopenharmony_ci addrconf_prefix_route(&addr, 64, 0, idev->dev, 33368c2ecf20Sopenharmony_ci 0, 0, GFP_KERNEL); 33378c2ecf20Sopenharmony_ci break; 33388c2ecf20Sopenharmony_ci case IN6_ADDR_GEN_MODE_EUI64: 33398c2ecf20Sopenharmony_ci /* addrconf_add_linklocal also adds a prefix_route and we 33408c2ecf20Sopenharmony_ci * only need to care about prefix routes if ipv6_generate_eui64 33418c2ecf20Sopenharmony_ci * couldn't generate one. 33428c2ecf20Sopenharmony_ci */ 33438c2ecf20Sopenharmony_ci if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0) 33448c2ecf20Sopenharmony_ci addrconf_add_linklocal(idev, &addr, 0); 33458c2ecf20Sopenharmony_ci else if (prefix_route) 33468c2ecf20Sopenharmony_ci addrconf_prefix_route(&addr, 64, 0, idev->dev, 33478c2ecf20Sopenharmony_ci 0, 0, GFP_KERNEL); 33488c2ecf20Sopenharmony_ci break; 33498c2ecf20Sopenharmony_ci case IN6_ADDR_GEN_MODE_NONE: 33508c2ecf20Sopenharmony_ci default: 33518c2ecf20Sopenharmony_ci /* will not add any link local address */ 33528c2ecf20Sopenharmony_ci break; 33538c2ecf20Sopenharmony_ci } 33548c2ecf20Sopenharmony_ci} 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_cistatic void addrconf_dev_config(struct net_device *dev) 33578c2ecf20Sopenharmony_ci{ 33588c2ecf20Sopenharmony_ci struct inet6_dev *idev; 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_ci ASSERT_RTNL(); 33618c2ecf20Sopenharmony_ci 33628c2ecf20Sopenharmony_ci if ((dev->type != ARPHRD_ETHER) && 33638c2ecf20Sopenharmony_ci (dev->type != ARPHRD_FDDI) && 33648c2ecf20Sopenharmony_ci (dev->type != ARPHRD_ARCNET) && 33658c2ecf20Sopenharmony_ci (dev->type != ARPHRD_INFINIBAND) && 33668c2ecf20Sopenharmony_ci (dev->type != ARPHRD_IEEE1394) && 33678c2ecf20Sopenharmony_ci (dev->type != ARPHRD_TUNNEL6) && 33688c2ecf20Sopenharmony_ci (dev->type != ARPHRD_6LOWPAN) && 33698c2ecf20Sopenharmony_ci (dev->type != ARPHRD_IP6GRE) && 33708c2ecf20Sopenharmony_ci (dev->type != ARPHRD_IPGRE) && 33718c2ecf20Sopenharmony_ci (dev->type != ARPHRD_TUNNEL) && 33728c2ecf20Sopenharmony_ci (dev->type != ARPHRD_NONE) && 33738c2ecf20Sopenharmony_ci (dev->type != ARPHRD_RAWIP)) { 33748c2ecf20Sopenharmony_ci /* Alas, we support only Ethernet autoconfiguration. */ 33758c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 33768c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(idev) && dev->flags & IFF_UP && 33778c2ecf20Sopenharmony_ci dev->flags & IFF_MULTICAST) 33788c2ecf20Sopenharmony_ci ipv6_mc_up(idev); 33798c2ecf20Sopenharmony_ci return; 33808c2ecf20Sopenharmony_ci } 33818c2ecf20Sopenharmony_ci 33828c2ecf20Sopenharmony_ci idev = addrconf_add_dev(dev); 33838c2ecf20Sopenharmony_ci if (IS_ERR(idev)) 33848c2ecf20Sopenharmony_ci return; 33858c2ecf20Sopenharmony_ci 33868c2ecf20Sopenharmony_ci /* this device type has no EUI support */ 33878c2ecf20Sopenharmony_ci if (dev->type == ARPHRD_NONE && 33888c2ecf20Sopenharmony_ci idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) 33898c2ecf20Sopenharmony_ci idev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM; 33908c2ecf20Sopenharmony_ci 33918c2ecf20Sopenharmony_ci addrconf_addr_gen(idev, false); 33928c2ecf20Sopenharmony_ci} 33938c2ecf20Sopenharmony_ci 33948c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT) 33958c2ecf20Sopenharmony_cistatic void addrconf_sit_config(struct net_device *dev) 33968c2ecf20Sopenharmony_ci{ 33978c2ecf20Sopenharmony_ci struct inet6_dev *idev; 33988c2ecf20Sopenharmony_ci 33998c2ecf20Sopenharmony_ci ASSERT_RTNL(); 34008c2ecf20Sopenharmony_ci 34018c2ecf20Sopenharmony_ci /* 34028c2ecf20Sopenharmony_ci * Configure the tunnel with one of our IPv4 34038c2ecf20Sopenharmony_ci * addresses... we should configure all of 34048c2ecf20Sopenharmony_ci * our v4 addrs in the tunnel 34058c2ecf20Sopenharmony_ci */ 34068c2ecf20Sopenharmony_ci 34078c2ecf20Sopenharmony_ci idev = ipv6_find_idev(dev); 34088c2ecf20Sopenharmony_ci if (IS_ERR(idev)) { 34098c2ecf20Sopenharmony_ci pr_debug("%s: add_dev failed\n", __func__); 34108c2ecf20Sopenharmony_ci return; 34118c2ecf20Sopenharmony_ci } 34128c2ecf20Sopenharmony_ci 34138c2ecf20Sopenharmony_ci if (dev->priv_flags & IFF_ISATAP) { 34148c2ecf20Sopenharmony_ci addrconf_addr_gen(idev, false); 34158c2ecf20Sopenharmony_ci return; 34168c2ecf20Sopenharmony_ci } 34178c2ecf20Sopenharmony_ci 34188c2ecf20Sopenharmony_ci sit_add_v4_addrs(idev); 34198c2ecf20Sopenharmony_ci 34208c2ecf20Sopenharmony_ci if (dev->flags&IFF_POINTOPOINT) 34218c2ecf20Sopenharmony_ci addrconf_add_mroute(dev); 34228c2ecf20Sopenharmony_ci} 34238c2ecf20Sopenharmony_ci#endif 34248c2ecf20Sopenharmony_ci 34258c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IPGRE) 34268c2ecf20Sopenharmony_cistatic void addrconf_gre_config(struct net_device *dev) 34278c2ecf20Sopenharmony_ci{ 34288c2ecf20Sopenharmony_ci struct inet6_dev *idev; 34298c2ecf20Sopenharmony_ci 34308c2ecf20Sopenharmony_ci ASSERT_RTNL(); 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci idev = ipv6_find_idev(dev); 34338c2ecf20Sopenharmony_ci if (IS_ERR(idev)) { 34348c2ecf20Sopenharmony_ci pr_debug("%s: add_dev failed\n", __func__); 34358c2ecf20Sopenharmony_ci return; 34368c2ecf20Sopenharmony_ci } 34378c2ecf20Sopenharmony_ci 34388c2ecf20Sopenharmony_ci addrconf_addr_gen(idev, true); 34398c2ecf20Sopenharmony_ci if (dev->flags & IFF_POINTOPOINT) 34408c2ecf20Sopenharmony_ci addrconf_add_mroute(dev); 34418c2ecf20Sopenharmony_ci} 34428c2ecf20Sopenharmony_ci#endif 34438c2ecf20Sopenharmony_ci 34448c2ecf20Sopenharmony_cistatic int fixup_permanent_addr(struct net *net, 34458c2ecf20Sopenharmony_ci struct inet6_dev *idev, 34468c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp) 34478c2ecf20Sopenharmony_ci{ 34488c2ecf20Sopenharmony_ci /* !fib6_node means the host route was removed from the 34498c2ecf20Sopenharmony_ci * FIB, for example, if 'lo' device is taken down. In that 34508c2ecf20Sopenharmony_ci * case regenerate the host route. 34518c2ecf20Sopenharmony_ci */ 34528c2ecf20Sopenharmony_ci if (!ifp->rt || !ifp->rt->fib6_node) { 34538c2ecf20Sopenharmony_ci struct fib6_info *f6i, *prev; 34548c2ecf20Sopenharmony_ci 34558c2ecf20Sopenharmony_ci f6i = addrconf_f6i_alloc(net, idev, &ifp->addr, false, 34568c2ecf20Sopenharmony_ci GFP_ATOMIC); 34578c2ecf20Sopenharmony_ci if (IS_ERR(f6i)) 34588c2ecf20Sopenharmony_ci return PTR_ERR(f6i); 34598c2ecf20Sopenharmony_ci 34608c2ecf20Sopenharmony_ci /* ifp->rt can be accessed outside of rtnl */ 34618c2ecf20Sopenharmony_ci spin_lock(&ifp->lock); 34628c2ecf20Sopenharmony_ci prev = ifp->rt; 34638c2ecf20Sopenharmony_ci ifp->rt = f6i; 34648c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 34658c2ecf20Sopenharmony_ci 34668c2ecf20Sopenharmony_ci fib6_info_release(prev); 34678c2ecf20Sopenharmony_ci } 34688c2ecf20Sopenharmony_ci 34698c2ecf20Sopenharmony_ci if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) { 34708c2ecf20Sopenharmony_ci addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 34718c2ecf20Sopenharmony_ci ifp->rt_priority, idev->dev, 0, 0, 34728c2ecf20Sopenharmony_ci GFP_ATOMIC); 34738c2ecf20Sopenharmony_ci } 34748c2ecf20Sopenharmony_ci 34758c2ecf20Sopenharmony_ci if (ifp->state == INET6_IFADDR_STATE_PREDAD) 34768c2ecf20Sopenharmony_ci addrconf_dad_start(ifp); 34778c2ecf20Sopenharmony_ci 34788c2ecf20Sopenharmony_ci return 0; 34798c2ecf20Sopenharmony_ci} 34808c2ecf20Sopenharmony_ci 34818c2ecf20Sopenharmony_cistatic void addrconf_permanent_addr(struct net *net, struct net_device *dev) 34828c2ecf20Sopenharmony_ci{ 34838c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp, *tmp; 34848c2ecf20Sopenharmony_ci struct inet6_dev *idev; 34858c2ecf20Sopenharmony_ci 34868c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 34878c2ecf20Sopenharmony_ci if (!idev) 34888c2ecf20Sopenharmony_ci return; 34898c2ecf20Sopenharmony_ci 34908c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 34918c2ecf20Sopenharmony_ci 34928c2ecf20Sopenharmony_ci list_for_each_entry_safe(ifp, tmp, &idev->addr_list, if_list) { 34938c2ecf20Sopenharmony_ci if ((ifp->flags & IFA_F_PERMANENT) && 34948c2ecf20Sopenharmony_ci fixup_permanent_addr(net, idev, ifp) < 0) { 34958c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 34968c2ecf20Sopenharmony_ci in6_ifa_hold(ifp); 34978c2ecf20Sopenharmony_ci ipv6_del_addr(ifp); 34988c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 34998c2ecf20Sopenharmony_ci 35008c2ecf20Sopenharmony_ci net_info_ratelimited("%s: Failed to add prefix route for address %pI6c; dropping\n", 35018c2ecf20Sopenharmony_ci idev->dev->name, &ifp->addr); 35028c2ecf20Sopenharmony_ci } 35038c2ecf20Sopenharmony_ci } 35048c2ecf20Sopenharmony_ci 35058c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 35068c2ecf20Sopenharmony_ci} 35078c2ecf20Sopenharmony_ci 35088c2ecf20Sopenharmony_cistatic int addrconf_notify(struct notifier_block *this, unsigned long event, 35098c2ecf20Sopenharmony_ci void *ptr) 35108c2ecf20Sopenharmony_ci{ 35118c2ecf20Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 35128c2ecf20Sopenharmony_ci struct netdev_notifier_change_info *change_info; 35138c2ecf20Sopenharmony_ci struct netdev_notifier_changeupper_info *info; 35148c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 35158c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 35168c2ecf20Sopenharmony_ci int run_pending = 0; 35178c2ecf20Sopenharmony_ci int err; 35188c2ecf20Sopenharmony_ci 35198c2ecf20Sopenharmony_ci switch (event) { 35208c2ecf20Sopenharmony_ci case NETDEV_REGISTER: 35218c2ecf20Sopenharmony_ci if (!idev && dev->mtu >= IPV6_MIN_MTU) { 35228c2ecf20Sopenharmony_ci idev = ipv6_add_dev(dev); 35238c2ecf20Sopenharmony_ci if (IS_ERR(idev)) 35248c2ecf20Sopenharmony_ci return notifier_from_errno(PTR_ERR(idev)); 35258c2ecf20Sopenharmony_ci } 35268c2ecf20Sopenharmony_ci break; 35278c2ecf20Sopenharmony_ci 35288c2ecf20Sopenharmony_ci case NETDEV_CHANGEMTU: 35298c2ecf20Sopenharmony_ci /* if MTU under IPV6_MIN_MTU stop IPv6 on this interface. */ 35308c2ecf20Sopenharmony_ci if (dev->mtu < IPV6_MIN_MTU) { 35318c2ecf20Sopenharmony_ci addrconf_ifdown(dev, dev != net->loopback_dev); 35328c2ecf20Sopenharmony_ci break; 35338c2ecf20Sopenharmony_ci } 35348c2ecf20Sopenharmony_ci 35358c2ecf20Sopenharmony_ci if (idev) { 35368c2ecf20Sopenharmony_ci rt6_mtu_change(dev, dev->mtu); 35378c2ecf20Sopenharmony_ci idev->cnf.mtu6 = dev->mtu; 35388c2ecf20Sopenharmony_ci break; 35398c2ecf20Sopenharmony_ci } 35408c2ecf20Sopenharmony_ci 35418c2ecf20Sopenharmony_ci /* allocate new idev */ 35428c2ecf20Sopenharmony_ci idev = ipv6_add_dev(dev); 35438c2ecf20Sopenharmony_ci if (IS_ERR(idev)) 35448c2ecf20Sopenharmony_ci break; 35458c2ecf20Sopenharmony_ci 35468c2ecf20Sopenharmony_ci /* device is still not ready */ 35478c2ecf20Sopenharmony_ci if (!(idev->if_flags & IF_READY)) 35488c2ecf20Sopenharmony_ci break; 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci run_pending = 1; 35518c2ecf20Sopenharmony_ci fallthrough; 35528c2ecf20Sopenharmony_ci case NETDEV_UP: 35538c2ecf20Sopenharmony_ci case NETDEV_CHANGE: 35548c2ecf20Sopenharmony_ci if (dev->flags & IFF_SLAVE) 35558c2ecf20Sopenharmony_ci break; 35568c2ecf20Sopenharmony_ci 35578c2ecf20Sopenharmony_ci if (idev && idev->cnf.disable_ipv6) 35588c2ecf20Sopenharmony_ci break; 35598c2ecf20Sopenharmony_ci 35608c2ecf20Sopenharmony_ci if (event == NETDEV_UP) { 35618c2ecf20Sopenharmony_ci /* restore routes for permanent addresses */ 35628c2ecf20Sopenharmony_ci addrconf_permanent_addr(net, dev); 35638c2ecf20Sopenharmony_ci 35648c2ecf20Sopenharmony_ci if (!addrconf_link_ready(dev)) { 35658c2ecf20Sopenharmony_ci /* device is not ready yet. */ 35668c2ecf20Sopenharmony_ci pr_debug("ADDRCONF(NETDEV_UP): %s: link is not ready\n", 35678c2ecf20Sopenharmony_ci dev->name); 35688c2ecf20Sopenharmony_ci break; 35698c2ecf20Sopenharmony_ci } 35708c2ecf20Sopenharmony_ci 35718c2ecf20Sopenharmony_ci if (!idev && dev->mtu >= IPV6_MIN_MTU) 35728c2ecf20Sopenharmony_ci idev = ipv6_add_dev(dev); 35738c2ecf20Sopenharmony_ci 35748c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(idev)) { 35758c2ecf20Sopenharmony_ci idev->if_flags |= IF_READY; 35768c2ecf20Sopenharmony_ci run_pending = 1; 35778c2ecf20Sopenharmony_ci } 35788c2ecf20Sopenharmony_ci } else if (event == NETDEV_CHANGE) { 35798c2ecf20Sopenharmony_ci if (!addrconf_link_ready(dev)) { 35808c2ecf20Sopenharmony_ci /* device is still not ready. */ 35818c2ecf20Sopenharmony_ci rt6_sync_down_dev(dev, event); 35828c2ecf20Sopenharmony_ci break; 35838c2ecf20Sopenharmony_ci } 35848c2ecf20Sopenharmony_ci 35858c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(idev)) { 35868c2ecf20Sopenharmony_ci if (idev->if_flags & IF_READY) { 35878c2ecf20Sopenharmony_ci /* device is already configured - 35888c2ecf20Sopenharmony_ci * but resend MLD reports, we might 35898c2ecf20Sopenharmony_ci * have roamed and need to update 35908c2ecf20Sopenharmony_ci * multicast snooping switches 35918c2ecf20Sopenharmony_ci */ 35928c2ecf20Sopenharmony_ci ipv6_mc_up(idev); 35938c2ecf20Sopenharmony_ci change_info = ptr; 35948c2ecf20Sopenharmony_ci if (change_info->flags_changed & IFF_NOARP) 35958c2ecf20Sopenharmony_ci addrconf_dad_run(idev, true); 35968c2ecf20Sopenharmony_ci rt6_sync_up(dev, RTNH_F_LINKDOWN); 35978c2ecf20Sopenharmony_ci break; 35988c2ecf20Sopenharmony_ci } 35998c2ecf20Sopenharmony_ci idev->if_flags |= IF_READY; 36008c2ecf20Sopenharmony_ci } 36018c2ecf20Sopenharmony_ci 36028c2ecf20Sopenharmony_ci pr_info("ADDRCONF(NETDEV_CHANGE): %s: link becomes ready\n", 36038c2ecf20Sopenharmony_ci dev->name); 36048c2ecf20Sopenharmony_ci 36058c2ecf20Sopenharmony_ci run_pending = 1; 36068c2ecf20Sopenharmony_ci } 36078c2ecf20Sopenharmony_ci 36088c2ecf20Sopenharmony_ci switch (dev->type) { 36098c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT) 36108c2ecf20Sopenharmony_ci case ARPHRD_SIT: 36118c2ecf20Sopenharmony_ci addrconf_sit_config(dev); 36128c2ecf20Sopenharmony_ci break; 36138c2ecf20Sopenharmony_ci#endif 36148c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IPGRE) 36158c2ecf20Sopenharmony_ci case ARPHRD_IPGRE: 36168c2ecf20Sopenharmony_ci addrconf_gre_config(dev); 36178c2ecf20Sopenharmony_ci break; 36188c2ecf20Sopenharmony_ci#endif 36198c2ecf20Sopenharmony_ci case ARPHRD_LOOPBACK: 36208c2ecf20Sopenharmony_ci init_loopback(dev); 36218c2ecf20Sopenharmony_ci break; 36228c2ecf20Sopenharmony_ci 36238c2ecf20Sopenharmony_ci default: 36248c2ecf20Sopenharmony_ci addrconf_dev_config(dev); 36258c2ecf20Sopenharmony_ci break; 36268c2ecf20Sopenharmony_ci } 36278c2ecf20Sopenharmony_ci 36288c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(idev)) { 36298c2ecf20Sopenharmony_ci if (run_pending) 36308c2ecf20Sopenharmony_ci addrconf_dad_run(idev, false); 36318c2ecf20Sopenharmony_ci 36328c2ecf20Sopenharmony_ci /* Device has an address by now */ 36338c2ecf20Sopenharmony_ci rt6_sync_up(dev, RTNH_F_DEAD); 36348c2ecf20Sopenharmony_ci 36358c2ecf20Sopenharmony_ci /* 36368c2ecf20Sopenharmony_ci * If the MTU changed during the interface down, 36378c2ecf20Sopenharmony_ci * when the interface up, the changed MTU must be 36388c2ecf20Sopenharmony_ci * reflected in the idev as well as routers. 36398c2ecf20Sopenharmony_ci */ 36408c2ecf20Sopenharmony_ci if (idev->cnf.mtu6 != dev->mtu && 36418c2ecf20Sopenharmony_ci dev->mtu >= IPV6_MIN_MTU) { 36428c2ecf20Sopenharmony_ci rt6_mtu_change(dev, dev->mtu); 36438c2ecf20Sopenharmony_ci idev->cnf.mtu6 = dev->mtu; 36448c2ecf20Sopenharmony_ci } 36458c2ecf20Sopenharmony_ci idev->tstamp = jiffies; 36468c2ecf20Sopenharmony_ci inet6_ifinfo_notify(RTM_NEWLINK, idev); 36478c2ecf20Sopenharmony_ci 36488c2ecf20Sopenharmony_ci /* 36498c2ecf20Sopenharmony_ci * If the changed mtu during down is lower than 36508c2ecf20Sopenharmony_ci * IPV6_MIN_MTU stop IPv6 on this interface. 36518c2ecf20Sopenharmony_ci */ 36528c2ecf20Sopenharmony_ci if (dev->mtu < IPV6_MIN_MTU) 36538c2ecf20Sopenharmony_ci addrconf_ifdown(dev, dev != net->loopback_dev); 36548c2ecf20Sopenharmony_ci } 36558c2ecf20Sopenharmony_ci break; 36568c2ecf20Sopenharmony_ci 36578c2ecf20Sopenharmony_ci case NETDEV_DOWN: 36588c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 36598c2ecf20Sopenharmony_ci /* 36608c2ecf20Sopenharmony_ci * Remove all addresses from this interface. 36618c2ecf20Sopenharmony_ci */ 36628c2ecf20Sopenharmony_ci addrconf_ifdown(dev, event != NETDEV_DOWN); 36638c2ecf20Sopenharmony_ci break; 36648c2ecf20Sopenharmony_ci 36658c2ecf20Sopenharmony_ci case NETDEV_CHANGENAME: 36668c2ecf20Sopenharmony_ci if (idev) { 36678c2ecf20Sopenharmony_ci snmp6_unregister_dev(idev); 36688c2ecf20Sopenharmony_ci addrconf_sysctl_unregister(idev); 36698c2ecf20Sopenharmony_ci err = addrconf_sysctl_register(idev); 36708c2ecf20Sopenharmony_ci if (err) 36718c2ecf20Sopenharmony_ci return notifier_from_errno(err); 36728c2ecf20Sopenharmony_ci err = snmp6_register_dev(idev); 36738c2ecf20Sopenharmony_ci if (err) { 36748c2ecf20Sopenharmony_ci addrconf_sysctl_unregister(idev); 36758c2ecf20Sopenharmony_ci return notifier_from_errno(err); 36768c2ecf20Sopenharmony_ci } 36778c2ecf20Sopenharmony_ci } 36788c2ecf20Sopenharmony_ci break; 36798c2ecf20Sopenharmony_ci 36808c2ecf20Sopenharmony_ci case NETDEV_PRE_TYPE_CHANGE: 36818c2ecf20Sopenharmony_ci case NETDEV_POST_TYPE_CHANGE: 36828c2ecf20Sopenharmony_ci if (idev) 36838c2ecf20Sopenharmony_ci addrconf_type_change(dev, event); 36848c2ecf20Sopenharmony_ci break; 36858c2ecf20Sopenharmony_ci 36868c2ecf20Sopenharmony_ci case NETDEV_CHANGEUPPER: 36878c2ecf20Sopenharmony_ci info = ptr; 36888c2ecf20Sopenharmony_ci 36898c2ecf20Sopenharmony_ci /* flush all routes if dev is linked to or unlinked from 36908c2ecf20Sopenharmony_ci * an L3 master device (e.g., VRF) 36918c2ecf20Sopenharmony_ci */ 36928c2ecf20Sopenharmony_ci if (info->upper_dev && netif_is_l3_master(info->upper_dev)) 36938c2ecf20Sopenharmony_ci addrconf_ifdown(dev, false); 36948c2ecf20Sopenharmony_ci } 36958c2ecf20Sopenharmony_ci 36968c2ecf20Sopenharmony_ci return NOTIFY_OK; 36978c2ecf20Sopenharmony_ci} 36988c2ecf20Sopenharmony_ci 36998c2ecf20Sopenharmony_ci/* 37008c2ecf20Sopenharmony_ci * addrconf module should be notified of a device going up 37018c2ecf20Sopenharmony_ci */ 37028c2ecf20Sopenharmony_cistatic struct notifier_block ipv6_dev_notf = { 37038c2ecf20Sopenharmony_ci .notifier_call = addrconf_notify, 37048c2ecf20Sopenharmony_ci .priority = ADDRCONF_NOTIFY_PRIORITY, 37058c2ecf20Sopenharmony_ci}; 37068c2ecf20Sopenharmony_ci 37078c2ecf20Sopenharmony_cistatic void addrconf_type_change(struct net_device *dev, unsigned long event) 37088c2ecf20Sopenharmony_ci{ 37098c2ecf20Sopenharmony_ci struct inet6_dev *idev; 37108c2ecf20Sopenharmony_ci ASSERT_RTNL(); 37118c2ecf20Sopenharmony_ci 37128c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 37138c2ecf20Sopenharmony_ci 37148c2ecf20Sopenharmony_ci if (event == NETDEV_POST_TYPE_CHANGE) 37158c2ecf20Sopenharmony_ci ipv6_mc_remap(idev); 37168c2ecf20Sopenharmony_ci else if (event == NETDEV_PRE_TYPE_CHANGE) 37178c2ecf20Sopenharmony_ci ipv6_mc_unmap(idev); 37188c2ecf20Sopenharmony_ci} 37198c2ecf20Sopenharmony_ci 37208c2ecf20Sopenharmony_cistatic bool addr_is_local(const struct in6_addr *addr) 37218c2ecf20Sopenharmony_ci{ 37228c2ecf20Sopenharmony_ci return ipv6_addr_type(addr) & 37238c2ecf20Sopenharmony_ci (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); 37248c2ecf20Sopenharmony_ci} 37258c2ecf20Sopenharmony_ci 37268c2ecf20Sopenharmony_cistatic int addrconf_ifdown(struct net_device *dev, bool unregister) 37278c2ecf20Sopenharmony_ci{ 37288c2ecf20Sopenharmony_ci unsigned long event = unregister ? NETDEV_UNREGISTER : NETDEV_DOWN; 37298c2ecf20Sopenharmony_ci struct net *net = dev_net(dev); 37308c2ecf20Sopenharmony_ci struct inet6_dev *idev; 37318c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa; 37328c2ecf20Sopenharmony_ci LIST_HEAD(tmp_addr_list); 37338c2ecf20Sopenharmony_ci bool keep_addr = false; 37348c2ecf20Sopenharmony_ci bool was_ready; 37358c2ecf20Sopenharmony_ci int state, i; 37368c2ecf20Sopenharmony_ci 37378c2ecf20Sopenharmony_ci ASSERT_RTNL(); 37388c2ecf20Sopenharmony_ci 37398c2ecf20Sopenharmony_ci rt6_disable_ip(dev, event); 37408c2ecf20Sopenharmony_ci 37418c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 37428c2ecf20Sopenharmony_ci if (!idev) 37438c2ecf20Sopenharmony_ci return -ENODEV; 37448c2ecf20Sopenharmony_ci 37458c2ecf20Sopenharmony_ci /* 37468c2ecf20Sopenharmony_ci * Step 1: remove reference to ipv6 device from parent device. 37478c2ecf20Sopenharmony_ci * Do not dev_put! 37488c2ecf20Sopenharmony_ci */ 37498c2ecf20Sopenharmony_ci if (unregister) { 37508c2ecf20Sopenharmony_ci idev->dead = 1; 37518c2ecf20Sopenharmony_ci 37528c2ecf20Sopenharmony_ci /* protected by rtnl_lock */ 37538c2ecf20Sopenharmony_ci RCU_INIT_POINTER(dev->ip6_ptr, NULL); 37548c2ecf20Sopenharmony_ci 37558c2ecf20Sopenharmony_ci /* Step 1.5: remove snmp6 entry */ 37568c2ecf20Sopenharmony_ci snmp6_unregister_dev(idev); 37578c2ecf20Sopenharmony_ci 37588c2ecf20Sopenharmony_ci } 37598c2ecf20Sopenharmony_ci 37608c2ecf20Sopenharmony_ci /* combine the user config with event to determine if permanent 37618c2ecf20Sopenharmony_ci * addresses are to be removed from address hash table 37628c2ecf20Sopenharmony_ci */ 37638c2ecf20Sopenharmony_ci if (!unregister && !idev->cnf.disable_ipv6) { 37648c2ecf20Sopenharmony_ci /* aggregate the system setting and interface setting */ 37658c2ecf20Sopenharmony_ci int _keep_addr = net->ipv6.devconf_all->keep_addr_on_down; 37668c2ecf20Sopenharmony_ci 37678c2ecf20Sopenharmony_ci if (!_keep_addr) 37688c2ecf20Sopenharmony_ci _keep_addr = idev->cnf.keep_addr_on_down; 37698c2ecf20Sopenharmony_ci 37708c2ecf20Sopenharmony_ci keep_addr = (_keep_addr > 0); 37718c2ecf20Sopenharmony_ci } 37728c2ecf20Sopenharmony_ci 37738c2ecf20Sopenharmony_ci /* Step 2: clear hash table */ 37748c2ecf20Sopenharmony_ci for (i = 0; i < IN6_ADDR_HSIZE; i++) { 37758c2ecf20Sopenharmony_ci struct hlist_head *h = &inet6_addr_lst[i]; 37768c2ecf20Sopenharmony_ci 37778c2ecf20Sopenharmony_ci spin_lock_bh(&addrconf_hash_lock); 37788c2ecf20Sopenharmony_cirestart: 37798c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(ifa, h, addr_lst) { 37808c2ecf20Sopenharmony_ci if (ifa->idev == idev) { 37818c2ecf20Sopenharmony_ci addrconf_del_dad_work(ifa); 37828c2ecf20Sopenharmony_ci /* combined flag + permanent flag decide if 37838c2ecf20Sopenharmony_ci * address is retained on a down event 37848c2ecf20Sopenharmony_ci */ 37858c2ecf20Sopenharmony_ci if (!keep_addr || 37868c2ecf20Sopenharmony_ci !(ifa->flags & IFA_F_PERMANENT) || 37878c2ecf20Sopenharmony_ci addr_is_local(&ifa->addr)) { 37888c2ecf20Sopenharmony_ci hlist_del_init_rcu(&ifa->addr_lst); 37898c2ecf20Sopenharmony_ci goto restart; 37908c2ecf20Sopenharmony_ci } 37918c2ecf20Sopenharmony_ci } 37928c2ecf20Sopenharmony_ci } 37938c2ecf20Sopenharmony_ci spin_unlock_bh(&addrconf_hash_lock); 37948c2ecf20Sopenharmony_ci } 37958c2ecf20Sopenharmony_ci 37968c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 37978c2ecf20Sopenharmony_ci 37988c2ecf20Sopenharmony_ci addrconf_del_rs_timer(idev); 37998c2ecf20Sopenharmony_ci 38008c2ecf20Sopenharmony_ci /* Step 2: clear flags for stateless addrconf, repeated down 38018c2ecf20Sopenharmony_ci * detection 38028c2ecf20Sopenharmony_ci */ 38038c2ecf20Sopenharmony_ci was_ready = idev->if_flags & IF_READY; 38048c2ecf20Sopenharmony_ci if (!unregister) 38058c2ecf20Sopenharmony_ci idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY); 38068c2ecf20Sopenharmony_ci 38078c2ecf20Sopenharmony_ci /* Step 3: clear tempaddr list */ 38088c2ecf20Sopenharmony_ci while (!list_empty(&idev->tempaddr_list)) { 38098c2ecf20Sopenharmony_ci ifa = list_first_entry(&idev->tempaddr_list, 38108c2ecf20Sopenharmony_ci struct inet6_ifaddr, tmp_list); 38118c2ecf20Sopenharmony_ci list_del(&ifa->tmp_list); 38128c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 38138c2ecf20Sopenharmony_ci spin_lock_bh(&ifa->lock); 38148c2ecf20Sopenharmony_ci 38158c2ecf20Sopenharmony_ci if (ifa->ifpub) { 38168c2ecf20Sopenharmony_ci in6_ifa_put(ifa->ifpub); 38178c2ecf20Sopenharmony_ci ifa->ifpub = NULL; 38188c2ecf20Sopenharmony_ci } 38198c2ecf20Sopenharmony_ci spin_unlock_bh(&ifa->lock); 38208c2ecf20Sopenharmony_ci in6_ifa_put(ifa); 38218c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 38228c2ecf20Sopenharmony_ci } 38238c2ecf20Sopenharmony_ci 38248c2ecf20Sopenharmony_ci list_for_each_entry(ifa, &idev->addr_list, if_list) 38258c2ecf20Sopenharmony_ci list_add_tail(&ifa->if_list_aux, &tmp_addr_list); 38268c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 38278c2ecf20Sopenharmony_ci 38288c2ecf20Sopenharmony_ci while (!list_empty(&tmp_addr_list)) { 38298c2ecf20Sopenharmony_ci struct fib6_info *rt = NULL; 38308c2ecf20Sopenharmony_ci bool keep; 38318c2ecf20Sopenharmony_ci 38328c2ecf20Sopenharmony_ci ifa = list_first_entry(&tmp_addr_list, 38338c2ecf20Sopenharmony_ci struct inet6_ifaddr, if_list_aux); 38348c2ecf20Sopenharmony_ci list_del(&ifa->if_list_aux); 38358c2ecf20Sopenharmony_ci 38368c2ecf20Sopenharmony_ci addrconf_del_dad_work(ifa); 38378c2ecf20Sopenharmony_ci 38388c2ecf20Sopenharmony_ci keep = keep_addr && (ifa->flags & IFA_F_PERMANENT) && 38398c2ecf20Sopenharmony_ci !addr_is_local(&ifa->addr); 38408c2ecf20Sopenharmony_ci 38418c2ecf20Sopenharmony_ci spin_lock_bh(&ifa->lock); 38428c2ecf20Sopenharmony_ci 38438c2ecf20Sopenharmony_ci if (keep) { 38448c2ecf20Sopenharmony_ci /* set state to skip the notifier below */ 38458c2ecf20Sopenharmony_ci state = INET6_IFADDR_STATE_DEAD; 38468c2ecf20Sopenharmony_ci ifa->state = INET6_IFADDR_STATE_PREDAD; 38478c2ecf20Sopenharmony_ci if (!(ifa->flags & IFA_F_NODAD)) 38488c2ecf20Sopenharmony_ci ifa->flags |= IFA_F_TENTATIVE; 38498c2ecf20Sopenharmony_ci 38508c2ecf20Sopenharmony_ci rt = ifa->rt; 38518c2ecf20Sopenharmony_ci ifa->rt = NULL; 38528c2ecf20Sopenharmony_ci } else { 38538c2ecf20Sopenharmony_ci state = ifa->state; 38548c2ecf20Sopenharmony_ci ifa->state = INET6_IFADDR_STATE_DEAD; 38558c2ecf20Sopenharmony_ci } 38568c2ecf20Sopenharmony_ci 38578c2ecf20Sopenharmony_ci spin_unlock_bh(&ifa->lock); 38588c2ecf20Sopenharmony_ci 38598c2ecf20Sopenharmony_ci if (rt) 38608c2ecf20Sopenharmony_ci ip6_del_rt(net, rt, false); 38618c2ecf20Sopenharmony_ci 38628c2ecf20Sopenharmony_ci if (state != INET6_IFADDR_STATE_DEAD) { 38638c2ecf20Sopenharmony_ci __ipv6_ifa_notify(RTM_DELADDR, ifa); 38648c2ecf20Sopenharmony_ci inet6addr_notifier_call_chain(NETDEV_DOWN, ifa); 38658c2ecf20Sopenharmony_ci } else { 38668c2ecf20Sopenharmony_ci if (idev->cnf.forwarding) 38678c2ecf20Sopenharmony_ci addrconf_leave_anycast(ifa); 38688c2ecf20Sopenharmony_ci addrconf_leave_solict(ifa->idev, &ifa->addr); 38698c2ecf20Sopenharmony_ci } 38708c2ecf20Sopenharmony_ci 38718c2ecf20Sopenharmony_ci if (!keep) { 38728c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 38738c2ecf20Sopenharmony_ci list_del_rcu(&ifa->if_list); 38748c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 38758c2ecf20Sopenharmony_ci in6_ifa_put(ifa); 38768c2ecf20Sopenharmony_ci } 38778c2ecf20Sopenharmony_ci } 38788c2ecf20Sopenharmony_ci 38798c2ecf20Sopenharmony_ci /* Step 5: Discard anycast and multicast list */ 38808c2ecf20Sopenharmony_ci if (unregister) { 38818c2ecf20Sopenharmony_ci ipv6_ac_destroy_dev(idev); 38828c2ecf20Sopenharmony_ci ipv6_mc_destroy_dev(idev); 38838c2ecf20Sopenharmony_ci } else if (was_ready) { 38848c2ecf20Sopenharmony_ci ipv6_mc_down(idev); 38858c2ecf20Sopenharmony_ci } 38868c2ecf20Sopenharmony_ci 38878c2ecf20Sopenharmony_ci idev->tstamp = jiffies; 38888c2ecf20Sopenharmony_ci 38898c2ecf20Sopenharmony_ci /* Last: Shot the device (if unregistered) */ 38908c2ecf20Sopenharmony_ci if (unregister) { 38918c2ecf20Sopenharmony_ci addrconf_sysctl_unregister(idev); 38928c2ecf20Sopenharmony_ci neigh_parms_release(&nd_tbl, idev->nd_parms); 38938c2ecf20Sopenharmony_ci neigh_ifdown(&nd_tbl, dev); 38948c2ecf20Sopenharmony_ci in6_dev_put(idev); 38958c2ecf20Sopenharmony_ci } 38968c2ecf20Sopenharmony_ci return 0; 38978c2ecf20Sopenharmony_ci} 38988c2ecf20Sopenharmony_ci 38998c2ecf20Sopenharmony_cistatic void addrconf_rs_timer(struct timer_list *t) 39008c2ecf20Sopenharmony_ci{ 39018c2ecf20Sopenharmony_ci struct inet6_dev *idev = from_timer(idev, t, rs_timer); 39028c2ecf20Sopenharmony_ci struct net_device *dev = idev->dev; 39038c2ecf20Sopenharmony_ci struct in6_addr lladdr; 39048c2ecf20Sopenharmony_ci 39058c2ecf20Sopenharmony_ci write_lock(&idev->lock); 39068c2ecf20Sopenharmony_ci if (idev->dead || !(idev->if_flags & IF_READY)) 39078c2ecf20Sopenharmony_ci goto out; 39088c2ecf20Sopenharmony_ci 39098c2ecf20Sopenharmony_ci if (!ipv6_accept_ra(idev)) 39108c2ecf20Sopenharmony_ci goto out; 39118c2ecf20Sopenharmony_ci 39128c2ecf20Sopenharmony_ci /* Announcement received after solicitation was sent */ 39138c2ecf20Sopenharmony_ci if (idev->if_flags & IF_RA_RCVD) 39148c2ecf20Sopenharmony_ci goto out; 39158c2ecf20Sopenharmony_ci 39168c2ecf20Sopenharmony_ci if (idev->rs_probes++ < idev->cnf.rtr_solicits || idev->cnf.rtr_solicits < 0) { 39178c2ecf20Sopenharmony_ci write_unlock(&idev->lock); 39188c2ecf20Sopenharmony_ci if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE)) 39198c2ecf20Sopenharmony_ci ndisc_send_rs(dev, &lladdr, 39208c2ecf20Sopenharmony_ci &in6addr_linklocal_allrouters); 39218c2ecf20Sopenharmony_ci else 39228c2ecf20Sopenharmony_ci goto put; 39238c2ecf20Sopenharmony_ci 39248c2ecf20Sopenharmony_ci write_lock(&idev->lock); 39258c2ecf20Sopenharmony_ci idev->rs_interval = rfc3315_s14_backoff_update( 39268c2ecf20Sopenharmony_ci idev->rs_interval, idev->cnf.rtr_solicit_max_interval); 39278c2ecf20Sopenharmony_ci /* The wait after the last probe can be shorter */ 39288c2ecf20Sopenharmony_ci addrconf_mod_rs_timer(idev, (idev->rs_probes == 39298c2ecf20Sopenharmony_ci idev->cnf.rtr_solicits) ? 39308c2ecf20Sopenharmony_ci idev->cnf.rtr_solicit_delay : 39318c2ecf20Sopenharmony_ci idev->rs_interval); 39328c2ecf20Sopenharmony_ci } else { 39338c2ecf20Sopenharmony_ci /* 39348c2ecf20Sopenharmony_ci * Note: we do not support deprecated "all on-link" 39358c2ecf20Sopenharmony_ci * assumption any longer. 39368c2ecf20Sopenharmony_ci */ 39378c2ecf20Sopenharmony_ci pr_debug("%s: no IPv6 routers present\n", idev->dev->name); 39388c2ecf20Sopenharmony_ci } 39398c2ecf20Sopenharmony_ci 39408c2ecf20Sopenharmony_ciout: 39418c2ecf20Sopenharmony_ci write_unlock(&idev->lock); 39428c2ecf20Sopenharmony_ciput: 39438c2ecf20Sopenharmony_ci in6_dev_put(idev); 39448c2ecf20Sopenharmony_ci} 39458c2ecf20Sopenharmony_ci 39468c2ecf20Sopenharmony_ci/* 39478c2ecf20Sopenharmony_ci * Duplicate Address Detection 39488c2ecf20Sopenharmony_ci */ 39498c2ecf20Sopenharmony_cistatic void addrconf_dad_kick(struct inet6_ifaddr *ifp) 39508c2ecf20Sopenharmony_ci{ 39518c2ecf20Sopenharmony_ci unsigned long rand_num; 39528c2ecf20Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 39538c2ecf20Sopenharmony_ci u64 nonce; 39548c2ecf20Sopenharmony_ci 39558c2ecf20Sopenharmony_ci if (ifp->flags & IFA_F_OPTIMISTIC) 39568c2ecf20Sopenharmony_ci rand_num = 0; 39578c2ecf20Sopenharmony_ci else 39588c2ecf20Sopenharmony_ci rand_num = prandom_u32() % (idev->cnf.rtr_solicit_delay ? : 1); 39598c2ecf20Sopenharmony_ci 39608c2ecf20Sopenharmony_ci nonce = 0; 39618c2ecf20Sopenharmony_ci if (idev->cnf.enhanced_dad || 39628c2ecf20Sopenharmony_ci dev_net(idev->dev)->ipv6.devconf_all->enhanced_dad) { 39638c2ecf20Sopenharmony_ci do 39648c2ecf20Sopenharmony_ci get_random_bytes(&nonce, 6); 39658c2ecf20Sopenharmony_ci while (nonce == 0); 39668c2ecf20Sopenharmony_ci } 39678c2ecf20Sopenharmony_ci ifp->dad_nonce = nonce; 39688c2ecf20Sopenharmony_ci ifp->dad_probes = idev->cnf.dad_transmits; 39698c2ecf20Sopenharmony_ci addrconf_mod_dad_work(ifp, rand_num); 39708c2ecf20Sopenharmony_ci} 39718c2ecf20Sopenharmony_ci 39728c2ecf20Sopenharmony_cistatic void addrconf_dad_begin(struct inet6_ifaddr *ifp) 39738c2ecf20Sopenharmony_ci{ 39748c2ecf20Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 39758c2ecf20Sopenharmony_ci struct net_device *dev = idev->dev; 39768c2ecf20Sopenharmony_ci bool bump_id, notify = false; 39778c2ecf20Sopenharmony_ci struct net *net; 39788c2ecf20Sopenharmony_ci 39798c2ecf20Sopenharmony_ci addrconf_join_solict(dev, &ifp->addr); 39808c2ecf20Sopenharmony_ci 39818c2ecf20Sopenharmony_ci prandom_seed((__force u32) ifp->addr.s6_addr32[3]); 39828c2ecf20Sopenharmony_ci 39838c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 39848c2ecf20Sopenharmony_ci spin_lock(&ifp->lock); 39858c2ecf20Sopenharmony_ci if (ifp->state == INET6_IFADDR_STATE_DEAD) 39868c2ecf20Sopenharmony_ci goto out; 39878c2ecf20Sopenharmony_ci 39888c2ecf20Sopenharmony_ci net = dev_net(dev); 39898c2ecf20Sopenharmony_ci if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || 39908c2ecf20Sopenharmony_ci (net->ipv6.devconf_all->accept_dad < 1 && 39918c2ecf20Sopenharmony_ci idev->cnf.accept_dad < 1) || 39928c2ecf20Sopenharmony_ci !(ifp->flags&IFA_F_TENTATIVE) || 39938c2ecf20Sopenharmony_ci ifp->flags & IFA_F_NODAD) { 39948c2ecf20Sopenharmony_ci bool send_na = false; 39958c2ecf20Sopenharmony_ci 39968c2ecf20Sopenharmony_ci if (ifp->flags & IFA_F_TENTATIVE && 39978c2ecf20Sopenharmony_ci !(ifp->flags & IFA_F_OPTIMISTIC)) 39988c2ecf20Sopenharmony_ci send_na = true; 39998c2ecf20Sopenharmony_ci bump_id = ifp->flags & IFA_F_TENTATIVE; 40008c2ecf20Sopenharmony_ci ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); 40018c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 40028c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 40038c2ecf20Sopenharmony_ci 40048c2ecf20Sopenharmony_ci addrconf_dad_completed(ifp, bump_id, send_na); 40058c2ecf20Sopenharmony_ci return; 40068c2ecf20Sopenharmony_ci } 40078c2ecf20Sopenharmony_ci 40088c2ecf20Sopenharmony_ci if (!(idev->if_flags & IF_READY)) { 40098c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 40108c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 40118c2ecf20Sopenharmony_ci /* 40128c2ecf20Sopenharmony_ci * If the device is not ready: 40138c2ecf20Sopenharmony_ci * - keep it tentative if it is a permanent address. 40148c2ecf20Sopenharmony_ci * - otherwise, kill it. 40158c2ecf20Sopenharmony_ci */ 40168c2ecf20Sopenharmony_ci in6_ifa_hold(ifp); 40178c2ecf20Sopenharmony_ci addrconf_dad_stop(ifp, 0); 40188c2ecf20Sopenharmony_ci return; 40198c2ecf20Sopenharmony_ci } 40208c2ecf20Sopenharmony_ci 40218c2ecf20Sopenharmony_ci /* 40228c2ecf20Sopenharmony_ci * Optimistic nodes can start receiving 40238c2ecf20Sopenharmony_ci * Frames right away 40248c2ecf20Sopenharmony_ci */ 40258c2ecf20Sopenharmony_ci if (ifp->flags & IFA_F_OPTIMISTIC) { 40268c2ecf20Sopenharmony_ci ip6_ins_rt(net, ifp->rt); 40278c2ecf20Sopenharmony_ci if (ipv6_use_optimistic_addr(net, idev)) { 40288c2ecf20Sopenharmony_ci /* Because optimistic nodes can use this address, 40298c2ecf20Sopenharmony_ci * notify listeners. If DAD fails, RTM_DELADDR is sent. 40308c2ecf20Sopenharmony_ci */ 40318c2ecf20Sopenharmony_ci notify = true; 40328c2ecf20Sopenharmony_ci } 40338c2ecf20Sopenharmony_ci } 40348c2ecf20Sopenharmony_ci 40358c2ecf20Sopenharmony_ci addrconf_dad_kick(ifp); 40368c2ecf20Sopenharmony_ciout: 40378c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 40388c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 40398c2ecf20Sopenharmony_ci if (notify) 40408c2ecf20Sopenharmony_ci ipv6_ifa_notify(RTM_NEWADDR, ifp); 40418c2ecf20Sopenharmony_ci} 40428c2ecf20Sopenharmony_ci 40438c2ecf20Sopenharmony_cistatic void addrconf_dad_start(struct inet6_ifaddr *ifp) 40448c2ecf20Sopenharmony_ci{ 40458c2ecf20Sopenharmony_ci bool begin_dad = false; 40468c2ecf20Sopenharmony_ci 40478c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 40488c2ecf20Sopenharmony_ci if (ifp->state != INET6_IFADDR_STATE_DEAD) { 40498c2ecf20Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_PREDAD; 40508c2ecf20Sopenharmony_ci begin_dad = true; 40518c2ecf20Sopenharmony_ci } 40528c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 40538c2ecf20Sopenharmony_ci 40548c2ecf20Sopenharmony_ci if (begin_dad) 40558c2ecf20Sopenharmony_ci addrconf_mod_dad_work(ifp, 0); 40568c2ecf20Sopenharmony_ci} 40578c2ecf20Sopenharmony_ci 40588c2ecf20Sopenharmony_cistatic void addrconf_dad_work(struct work_struct *w) 40598c2ecf20Sopenharmony_ci{ 40608c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp = container_of(to_delayed_work(w), 40618c2ecf20Sopenharmony_ci struct inet6_ifaddr, 40628c2ecf20Sopenharmony_ci dad_work); 40638c2ecf20Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 40648c2ecf20Sopenharmony_ci bool bump_id, disable_ipv6 = false; 40658c2ecf20Sopenharmony_ci struct in6_addr mcaddr; 40668c2ecf20Sopenharmony_ci 40678c2ecf20Sopenharmony_ci enum { 40688c2ecf20Sopenharmony_ci DAD_PROCESS, 40698c2ecf20Sopenharmony_ci DAD_BEGIN, 40708c2ecf20Sopenharmony_ci DAD_ABORT, 40718c2ecf20Sopenharmony_ci } action = DAD_PROCESS; 40728c2ecf20Sopenharmony_ci 40738c2ecf20Sopenharmony_ci rtnl_lock(); 40748c2ecf20Sopenharmony_ci 40758c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 40768c2ecf20Sopenharmony_ci if (ifp->state == INET6_IFADDR_STATE_PREDAD) { 40778c2ecf20Sopenharmony_ci action = DAD_BEGIN; 40788c2ecf20Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_DAD; 40798c2ecf20Sopenharmony_ci } else if (ifp->state == INET6_IFADDR_STATE_ERRDAD) { 40808c2ecf20Sopenharmony_ci action = DAD_ABORT; 40818c2ecf20Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_POSTDAD; 40828c2ecf20Sopenharmony_ci 40838c2ecf20Sopenharmony_ci if ((dev_net(idev->dev)->ipv6.devconf_all->accept_dad > 1 || 40848c2ecf20Sopenharmony_ci idev->cnf.accept_dad > 1) && 40858c2ecf20Sopenharmony_ci !idev->cnf.disable_ipv6 && 40868c2ecf20Sopenharmony_ci !(ifp->flags & IFA_F_STABLE_PRIVACY)) { 40878c2ecf20Sopenharmony_ci struct in6_addr addr; 40888c2ecf20Sopenharmony_ci 40898c2ecf20Sopenharmony_ci addr.s6_addr32[0] = htonl(0xfe800000); 40908c2ecf20Sopenharmony_ci addr.s6_addr32[1] = 0; 40918c2ecf20Sopenharmony_ci 40928c2ecf20Sopenharmony_ci if (!ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) && 40938c2ecf20Sopenharmony_ci ipv6_addr_equal(&ifp->addr, &addr)) { 40948c2ecf20Sopenharmony_ci /* DAD failed for link-local based on MAC */ 40958c2ecf20Sopenharmony_ci idev->cnf.disable_ipv6 = 1; 40968c2ecf20Sopenharmony_ci 40978c2ecf20Sopenharmony_ci pr_info("%s: IPv6 being disabled!\n", 40988c2ecf20Sopenharmony_ci ifp->idev->dev->name); 40998c2ecf20Sopenharmony_ci disable_ipv6 = true; 41008c2ecf20Sopenharmony_ci } 41018c2ecf20Sopenharmony_ci } 41028c2ecf20Sopenharmony_ci } 41038c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 41048c2ecf20Sopenharmony_ci 41058c2ecf20Sopenharmony_ci if (action == DAD_BEGIN) { 41068c2ecf20Sopenharmony_ci addrconf_dad_begin(ifp); 41078c2ecf20Sopenharmony_ci goto out; 41088c2ecf20Sopenharmony_ci } else if (action == DAD_ABORT) { 41098c2ecf20Sopenharmony_ci in6_ifa_hold(ifp); 41108c2ecf20Sopenharmony_ci addrconf_dad_stop(ifp, 1); 41118c2ecf20Sopenharmony_ci if (disable_ipv6) 41128c2ecf20Sopenharmony_ci addrconf_ifdown(idev->dev, false); 41138c2ecf20Sopenharmony_ci goto out; 41148c2ecf20Sopenharmony_ci } 41158c2ecf20Sopenharmony_ci 41168c2ecf20Sopenharmony_ci if (!ifp->dad_probes && addrconf_dad_end(ifp)) 41178c2ecf20Sopenharmony_ci goto out; 41188c2ecf20Sopenharmony_ci 41198c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 41208c2ecf20Sopenharmony_ci if (idev->dead || !(idev->if_flags & IF_READY)) { 41218c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 41228c2ecf20Sopenharmony_ci goto out; 41238c2ecf20Sopenharmony_ci } 41248c2ecf20Sopenharmony_ci 41258c2ecf20Sopenharmony_ci spin_lock(&ifp->lock); 41268c2ecf20Sopenharmony_ci if (ifp->state == INET6_IFADDR_STATE_DEAD) { 41278c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 41288c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 41298c2ecf20Sopenharmony_ci goto out; 41308c2ecf20Sopenharmony_ci } 41318c2ecf20Sopenharmony_ci 41328c2ecf20Sopenharmony_ci if (ifp->dad_probes == 0) { 41338c2ecf20Sopenharmony_ci bool send_na = false; 41348c2ecf20Sopenharmony_ci 41358c2ecf20Sopenharmony_ci /* 41368c2ecf20Sopenharmony_ci * DAD was successful 41378c2ecf20Sopenharmony_ci */ 41388c2ecf20Sopenharmony_ci 41398c2ecf20Sopenharmony_ci if (ifp->flags & IFA_F_TENTATIVE && 41408c2ecf20Sopenharmony_ci !(ifp->flags & IFA_F_OPTIMISTIC)) 41418c2ecf20Sopenharmony_ci send_na = true; 41428c2ecf20Sopenharmony_ci bump_id = ifp->flags & IFA_F_TENTATIVE; 41438c2ecf20Sopenharmony_ci ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); 41448c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 41458c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 41468c2ecf20Sopenharmony_ci 41478c2ecf20Sopenharmony_ci addrconf_dad_completed(ifp, bump_id, send_na); 41488c2ecf20Sopenharmony_ci 41498c2ecf20Sopenharmony_ci goto out; 41508c2ecf20Sopenharmony_ci } 41518c2ecf20Sopenharmony_ci 41528c2ecf20Sopenharmony_ci ifp->dad_probes--; 41538c2ecf20Sopenharmony_ci addrconf_mod_dad_work(ifp, 41548c2ecf20Sopenharmony_ci max(NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME), 41558c2ecf20Sopenharmony_ci HZ/100)); 41568c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 41578c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 41588c2ecf20Sopenharmony_ci 41598c2ecf20Sopenharmony_ci /* send a neighbour solicitation for our addr */ 41608c2ecf20Sopenharmony_ci addrconf_addr_solict_mult(&ifp->addr, &mcaddr); 41618c2ecf20Sopenharmony_ci ndisc_send_ns(ifp->idev->dev, &ifp->addr, &mcaddr, &in6addr_any, 41628c2ecf20Sopenharmony_ci ifp->dad_nonce); 41638c2ecf20Sopenharmony_ciout: 41648c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 41658c2ecf20Sopenharmony_ci rtnl_unlock(); 41668c2ecf20Sopenharmony_ci} 41678c2ecf20Sopenharmony_ci 41688c2ecf20Sopenharmony_ci/* ifp->idev must be at least read locked */ 41698c2ecf20Sopenharmony_cistatic bool ipv6_lonely_lladdr(struct inet6_ifaddr *ifp) 41708c2ecf20Sopenharmony_ci{ 41718c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifpiter; 41728c2ecf20Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 41738c2ecf20Sopenharmony_ci 41748c2ecf20Sopenharmony_ci list_for_each_entry_reverse(ifpiter, &idev->addr_list, if_list) { 41758c2ecf20Sopenharmony_ci if (ifpiter->scope > IFA_LINK) 41768c2ecf20Sopenharmony_ci break; 41778c2ecf20Sopenharmony_ci if (ifp != ifpiter && ifpiter->scope == IFA_LINK && 41788c2ecf20Sopenharmony_ci (ifpiter->flags & (IFA_F_PERMANENT|IFA_F_TENTATIVE| 41798c2ecf20Sopenharmony_ci IFA_F_OPTIMISTIC|IFA_F_DADFAILED)) == 41808c2ecf20Sopenharmony_ci IFA_F_PERMANENT) 41818c2ecf20Sopenharmony_ci return false; 41828c2ecf20Sopenharmony_ci } 41838c2ecf20Sopenharmony_ci return true; 41848c2ecf20Sopenharmony_ci} 41858c2ecf20Sopenharmony_ci 41868c2ecf20Sopenharmony_cistatic void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id, 41878c2ecf20Sopenharmony_ci bool send_na) 41888c2ecf20Sopenharmony_ci{ 41898c2ecf20Sopenharmony_ci struct net_device *dev = ifp->idev->dev; 41908c2ecf20Sopenharmony_ci struct in6_addr lladdr; 41918c2ecf20Sopenharmony_ci bool send_rs, send_mld; 41928c2ecf20Sopenharmony_ci 41938c2ecf20Sopenharmony_ci addrconf_del_dad_work(ifp); 41948c2ecf20Sopenharmony_ci 41958c2ecf20Sopenharmony_ci /* 41968c2ecf20Sopenharmony_ci * Configure the address for reception. Now it is valid. 41978c2ecf20Sopenharmony_ci */ 41988c2ecf20Sopenharmony_ci 41998c2ecf20Sopenharmony_ci ipv6_ifa_notify(RTM_NEWADDR, ifp); 42008c2ecf20Sopenharmony_ci 42018c2ecf20Sopenharmony_ci /* If added prefix is link local and we are prepared to process 42028c2ecf20Sopenharmony_ci router advertisements, start sending router solicitations. 42038c2ecf20Sopenharmony_ci */ 42048c2ecf20Sopenharmony_ci 42058c2ecf20Sopenharmony_ci read_lock_bh(&ifp->idev->lock); 42068c2ecf20Sopenharmony_ci send_mld = ifp->scope == IFA_LINK && ipv6_lonely_lladdr(ifp); 42078c2ecf20Sopenharmony_ci send_rs = send_mld && 42088c2ecf20Sopenharmony_ci ipv6_accept_ra(ifp->idev) && 42098c2ecf20Sopenharmony_ci ifp->idev->cnf.rtr_solicits != 0 && 42108c2ecf20Sopenharmony_ci (dev->flags & IFF_LOOPBACK) == 0 && 42118c2ecf20Sopenharmony_ci (dev->type != ARPHRD_TUNNEL); 42128c2ecf20Sopenharmony_ci read_unlock_bh(&ifp->idev->lock); 42138c2ecf20Sopenharmony_ci 42148c2ecf20Sopenharmony_ci /* While dad is in progress mld report's source address is in6_addrany. 42158c2ecf20Sopenharmony_ci * Resend with proper ll now. 42168c2ecf20Sopenharmony_ci */ 42178c2ecf20Sopenharmony_ci if (send_mld) 42188c2ecf20Sopenharmony_ci ipv6_mc_dad_complete(ifp->idev); 42198c2ecf20Sopenharmony_ci 42208c2ecf20Sopenharmony_ci /* send unsolicited NA if enabled */ 42218c2ecf20Sopenharmony_ci if (send_na && 42228c2ecf20Sopenharmony_ci (ifp->idev->cnf.ndisc_notify || 42238c2ecf20Sopenharmony_ci dev_net(dev)->ipv6.devconf_all->ndisc_notify)) { 42248c2ecf20Sopenharmony_ci ndisc_send_na(dev, &in6addr_linklocal_allnodes, &ifp->addr, 42258c2ecf20Sopenharmony_ci /*router=*/ !!ifp->idev->cnf.forwarding, 42268c2ecf20Sopenharmony_ci /*solicited=*/ false, /*override=*/ true, 42278c2ecf20Sopenharmony_ci /*inc_opt=*/ true); 42288c2ecf20Sopenharmony_ci } 42298c2ecf20Sopenharmony_ci 42308c2ecf20Sopenharmony_ci if (send_rs) { 42318c2ecf20Sopenharmony_ci /* 42328c2ecf20Sopenharmony_ci * If a host as already performed a random delay 42338c2ecf20Sopenharmony_ci * [...] as part of DAD [...] there is no need 42348c2ecf20Sopenharmony_ci * to delay again before sending the first RS 42358c2ecf20Sopenharmony_ci */ 42368c2ecf20Sopenharmony_ci if (ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE)) 42378c2ecf20Sopenharmony_ci return; 42388c2ecf20Sopenharmony_ci ndisc_send_rs(dev, &lladdr, &in6addr_linklocal_allrouters); 42398c2ecf20Sopenharmony_ci 42408c2ecf20Sopenharmony_ci write_lock_bh(&ifp->idev->lock); 42418c2ecf20Sopenharmony_ci spin_lock(&ifp->lock); 42428c2ecf20Sopenharmony_ci ifp->idev->rs_interval = rfc3315_s14_backoff_init( 42438c2ecf20Sopenharmony_ci ifp->idev->cnf.rtr_solicit_interval); 42448c2ecf20Sopenharmony_ci ifp->idev->rs_probes = 1; 42458c2ecf20Sopenharmony_ci ifp->idev->if_flags |= IF_RS_SENT; 42468c2ecf20Sopenharmony_ci addrconf_mod_rs_timer(ifp->idev, ifp->idev->rs_interval); 42478c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 42488c2ecf20Sopenharmony_ci write_unlock_bh(&ifp->idev->lock); 42498c2ecf20Sopenharmony_ci } 42508c2ecf20Sopenharmony_ci 42518c2ecf20Sopenharmony_ci if (bump_id) 42528c2ecf20Sopenharmony_ci rt_genid_bump_ipv6(dev_net(dev)); 42538c2ecf20Sopenharmony_ci 42548c2ecf20Sopenharmony_ci /* Make sure that a new temporary address will be created 42558c2ecf20Sopenharmony_ci * before this temporary address becomes deprecated. 42568c2ecf20Sopenharmony_ci */ 42578c2ecf20Sopenharmony_ci if (ifp->flags & IFA_F_TEMPORARY) 42588c2ecf20Sopenharmony_ci addrconf_verify_rtnl(); 42598c2ecf20Sopenharmony_ci} 42608c2ecf20Sopenharmony_ci 42618c2ecf20Sopenharmony_cistatic void addrconf_dad_run(struct inet6_dev *idev, bool restart) 42628c2ecf20Sopenharmony_ci{ 42638c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 42648c2ecf20Sopenharmony_ci 42658c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 42668c2ecf20Sopenharmony_ci list_for_each_entry(ifp, &idev->addr_list, if_list) { 42678c2ecf20Sopenharmony_ci spin_lock(&ifp->lock); 42688c2ecf20Sopenharmony_ci if ((ifp->flags & IFA_F_TENTATIVE && 42698c2ecf20Sopenharmony_ci ifp->state == INET6_IFADDR_STATE_DAD) || restart) { 42708c2ecf20Sopenharmony_ci if (restart) 42718c2ecf20Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_PREDAD; 42728c2ecf20Sopenharmony_ci addrconf_dad_kick(ifp); 42738c2ecf20Sopenharmony_ci } 42748c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 42758c2ecf20Sopenharmony_ci } 42768c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 42778c2ecf20Sopenharmony_ci} 42788c2ecf20Sopenharmony_ci 42798c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 42808c2ecf20Sopenharmony_cistruct if6_iter_state { 42818c2ecf20Sopenharmony_ci struct seq_net_private p; 42828c2ecf20Sopenharmony_ci int bucket; 42838c2ecf20Sopenharmony_ci int offset; 42848c2ecf20Sopenharmony_ci}; 42858c2ecf20Sopenharmony_ci 42868c2ecf20Sopenharmony_cistatic struct inet6_ifaddr *if6_get_first(struct seq_file *seq, loff_t pos) 42878c2ecf20Sopenharmony_ci{ 42888c2ecf20Sopenharmony_ci struct if6_iter_state *state = seq->private; 42898c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 42908c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa = NULL; 42918c2ecf20Sopenharmony_ci int p = 0; 42928c2ecf20Sopenharmony_ci 42938c2ecf20Sopenharmony_ci /* initial bucket if pos is 0 */ 42948c2ecf20Sopenharmony_ci if (pos == 0) { 42958c2ecf20Sopenharmony_ci state->bucket = 0; 42968c2ecf20Sopenharmony_ci state->offset = 0; 42978c2ecf20Sopenharmony_ci } 42988c2ecf20Sopenharmony_ci 42998c2ecf20Sopenharmony_ci for (; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) { 43008c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(ifa, &inet6_addr_lst[state->bucket], 43018c2ecf20Sopenharmony_ci addr_lst) { 43028c2ecf20Sopenharmony_ci if (!net_eq(dev_net(ifa->idev->dev), net)) 43038c2ecf20Sopenharmony_ci continue; 43048c2ecf20Sopenharmony_ci /* sync with offset */ 43058c2ecf20Sopenharmony_ci if (p < state->offset) { 43068c2ecf20Sopenharmony_ci p++; 43078c2ecf20Sopenharmony_ci continue; 43088c2ecf20Sopenharmony_ci } 43098c2ecf20Sopenharmony_ci return ifa; 43108c2ecf20Sopenharmony_ci } 43118c2ecf20Sopenharmony_ci 43128c2ecf20Sopenharmony_ci /* prepare for next bucket */ 43138c2ecf20Sopenharmony_ci state->offset = 0; 43148c2ecf20Sopenharmony_ci p = 0; 43158c2ecf20Sopenharmony_ci } 43168c2ecf20Sopenharmony_ci return NULL; 43178c2ecf20Sopenharmony_ci} 43188c2ecf20Sopenharmony_ci 43198c2ecf20Sopenharmony_cistatic struct inet6_ifaddr *if6_get_next(struct seq_file *seq, 43208c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa) 43218c2ecf20Sopenharmony_ci{ 43228c2ecf20Sopenharmony_ci struct if6_iter_state *state = seq->private; 43238c2ecf20Sopenharmony_ci struct net *net = seq_file_net(seq); 43248c2ecf20Sopenharmony_ci 43258c2ecf20Sopenharmony_ci hlist_for_each_entry_continue_rcu(ifa, addr_lst) { 43268c2ecf20Sopenharmony_ci if (!net_eq(dev_net(ifa->idev->dev), net)) 43278c2ecf20Sopenharmony_ci continue; 43288c2ecf20Sopenharmony_ci state->offset++; 43298c2ecf20Sopenharmony_ci return ifa; 43308c2ecf20Sopenharmony_ci } 43318c2ecf20Sopenharmony_ci 43328c2ecf20Sopenharmony_ci state->offset = 0; 43338c2ecf20Sopenharmony_ci while (++state->bucket < IN6_ADDR_HSIZE) { 43348c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(ifa, 43358c2ecf20Sopenharmony_ci &inet6_addr_lst[state->bucket], addr_lst) { 43368c2ecf20Sopenharmony_ci if (!net_eq(dev_net(ifa->idev->dev), net)) 43378c2ecf20Sopenharmony_ci continue; 43388c2ecf20Sopenharmony_ci return ifa; 43398c2ecf20Sopenharmony_ci } 43408c2ecf20Sopenharmony_ci } 43418c2ecf20Sopenharmony_ci 43428c2ecf20Sopenharmony_ci return NULL; 43438c2ecf20Sopenharmony_ci} 43448c2ecf20Sopenharmony_ci 43458c2ecf20Sopenharmony_cistatic void *if6_seq_start(struct seq_file *seq, loff_t *pos) 43468c2ecf20Sopenharmony_ci __acquires(rcu) 43478c2ecf20Sopenharmony_ci{ 43488c2ecf20Sopenharmony_ci rcu_read_lock(); 43498c2ecf20Sopenharmony_ci return if6_get_first(seq, *pos); 43508c2ecf20Sopenharmony_ci} 43518c2ecf20Sopenharmony_ci 43528c2ecf20Sopenharmony_cistatic void *if6_seq_next(struct seq_file *seq, void *v, loff_t *pos) 43538c2ecf20Sopenharmony_ci{ 43548c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa; 43558c2ecf20Sopenharmony_ci 43568c2ecf20Sopenharmony_ci ifa = if6_get_next(seq, v); 43578c2ecf20Sopenharmony_ci ++*pos; 43588c2ecf20Sopenharmony_ci return ifa; 43598c2ecf20Sopenharmony_ci} 43608c2ecf20Sopenharmony_ci 43618c2ecf20Sopenharmony_cistatic void if6_seq_stop(struct seq_file *seq, void *v) 43628c2ecf20Sopenharmony_ci __releases(rcu) 43638c2ecf20Sopenharmony_ci{ 43648c2ecf20Sopenharmony_ci rcu_read_unlock(); 43658c2ecf20Sopenharmony_ci} 43668c2ecf20Sopenharmony_ci 43678c2ecf20Sopenharmony_cistatic int if6_seq_show(struct seq_file *seq, void *v) 43688c2ecf20Sopenharmony_ci{ 43698c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v; 43708c2ecf20Sopenharmony_ci seq_printf(seq, "%pi6 %02x %02x %02x %02x %8s\n", 43718c2ecf20Sopenharmony_ci &ifp->addr, 43728c2ecf20Sopenharmony_ci ifp->idev->dev->ifindex, 43738c2ecf20Sopenharmony_ci ifp->prefix_len, 43748c2ecf20Sopenharmony_ci ifp->scope, 43758c2ecf20Sopenharmony_ci (u8) ifp->flags, 43768c2ecf20Sopenharmony_ci ifp->idev->dev->name); 43778c2ecf20Sopenharmony_ci return 0; 43788c2ecf20Sopenharmony_ci} 43798c2ecf20Sopenharmony_ci 43808c2ecf20Sopenharmony_cistatic const struct seq_operations if6_seq_ops = { 43818c2ecf20Sopenharmony_ci .start = if6_seq_start, 43828c2ecf20Sopenharmony_ci .next = if6_seq_next, 43838c2ecf20Sopenharmony_ci .show = if6_seq_show, 43848c2ecf20Sopenharmony_ci .stop = if6_seq_stop, 43858c2ecf20Sopenharmony_ci}; 43868c2ecf20Sopenharmony_ci 43878c2ecf20Sopenharmony_cistatic int __net_init if6_proc_net_init(struct net *net) 43888c2ecf20Sopenharmony_ci{ 43898c2ecf20Sopenharmony_ci if (!proc_create_net("if_inet6", 0444, net->proc_net, &if6_seq_ops, 43908c2ecf20Sopenharmony_ci sizeof(struct if6_iter_state))) 43918c2ecf20Sopenharmony_ci return -ENOMEM; 43928c2ecf20Sopenharmony_ci return 0; 43938c2ecf20Sopenharmony_ci} 43948c2ecf20Sopenharmony_ci 43958c2ecf20Sopenharmony_cistatic void __net_exit if6_proc_net_exit(struct net *net) 43968c2ecf20Sopenharmony_ci{ 43978c2ecf20Sopenharmony_ci remove_proc_entry("if_inet6", net->proc_net); 43988c2ecf20Sopenharmony_ci} 43998c2ecf20Sopenharmony_ci 44008c2ecf20Sopenharmony_cistatic struct pernet_operations if6_proc_net_ops = { 44018c2ecf20Sopenharmony_ci .init = if6_proc_net_init, 44028c2ecf20Sopenharmony_ci .exit = if6_proc_net_exit, 44038c2ecf20Sopenharmony_ci}; 44048c2ecf20Sopenharmony_ci 44058c2ecf20Sopenharmony_ciint __init if6_proc_init(void) 44068c2ecf20Sopenharmony_ci{ 44078c2ecf20Sopenharmony_ci return register_pernet_subsys(&if6_proc_net_ops); 44088c2ecf20Sopenharmony_ci} 44098c2ecf20Sopenharmony_ci 44108c2ecf20Sopenharmony_civoid if6_proc_exit(void) 44118c2ecf20Sopenharmony_ci{ 44128c2ecf20Sopenharmony_ci unregister_pernet_subsys(&if6_proc_net_ops); 44138c2ecf20Sopenharmony_ci} 44148c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 44158c2ecf20Sopenharmony_ci 44168c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 44178c2ecf20Sopenharmony_ci/* Check if address is a home address configured on any interface. */ 44188c2ecf20Sopenharmony_ciint ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr) 44198c2ecf20Sopenharmony_ci{ 44208c2ecf20Sopenharmony_ci unsigned int hash = inet6_addr_hash(net, addr); 44218c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp = NULL; 44228c2ecf20Sopenharmony_ci int ret = 0; 44238c2ecf20Sopenharmony_ci 44248c2ecf20Sopenharmony_ci rcu_read_lock(); 44258c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { 44268c2ecf20Sopenharmony_ci if (!net_eq(dev_net(ifp->idev->dev), net)) 44278c2ecf20Sopenharmony_ci continue; 44288c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&ifp->addr, addr) && 44298c2ecf20Sopenharmony_ci (ifp->flags & IFA_F_HOMEADDRESS)) { 44308c2ecf20Sopenharmony_ci ret = 1; 44318c2ecf20Sopenharmony_ci break; 44328c2ecf20Sopenharmony_ci } 44338c2ecf20Sopenharmony_ci } 44348c2ecf20Sopenharmony_ci rcu_read_unlock(); 44358c2ecf20Sopenharmony_ci return ret; 44368c2ecf20Sopenharmony_ci} 44378c2ecf20Sopenharmony_ci#endif 44388c2ecf20Sopenharmony_ci 44398c2ecf20Sopenharmony_ci/* RFC6554 has some algorithm to avoid loops in segment routing by 44408c2ecf20Sopenharmony_ci * checking if the segments contains any of a local interface address. 44418c2ecf20Sopenharmony_ci * 44428c2ecf20Sopenharmony_ci * Quote: 44438c2ecf20Sopenharmony_ci * 44448c2ecf20Sopenharmony_ci * To detect loops in the SRH, a router MUST determine if the SRH 44458c2ecf20Sopenharmony_ci * includes multiple addresses assigned to any interface on that router. 44468c2ecf20Sopenharmony_ci * If such addresses appear more than once and are separated by at least 44478c2ecf20Sopenharmony_ci * one address not assigned to that router. 44488c2ecf20Sopenharmony_ci */ 44498c2ecf20Sopenharmony_ciint ipv6_chk_rpl_srh_loop(struct net *net, const struct in6_addr *segs, 44508c2ecf20Sopenharmony_ci unsigned char nsegs) 44518c2ecf20Sopenharmony_ci{ 44528c2ecf20Sopenharmony_ci const struct in6_addr *addr; 44538c2ecf20Sopenharmony_ci int i, ret = 0, found = 0; 44548c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 44558c2ecf20Sopenharmony_ci bool separated = false; 44568c2ecf20Sopenharmony_ci unsigned int hash; 44578c2ecf20Sopenharmony_ci bool hash_found; 44588c2ecf20Sopenharmony_ci 44598c2ecf20Sopenharmony_ci rcu_read_lock(); 44608c2ecf20Sopenharmony_ci for (i = 0; i < nsegs; i++) { 44618c2ecf20Sopenharmony_ci addr = &segs[i]; 44628c2ecf20Sopenharmony_ci hash = inet6_addr_hash(net, addr); 44638c2ecf20Sopenharmony_ci 44648c2ecf20Sopenharmony_ci hash_found = false; 44658c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { 44668c2ecf20Sopenharmony_ci if (!net_eq(dev_net(ifp->idev->dev), net)) 44678c2ecf20Sopenharmony_ci continue; 44688c2ecf20Sopenharmony_ci 44698c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&ifp->addr, addr)) { 44708c2ecf20Sopenharmony_ci hash_found = true; 44718c2ecf20Sopenharmony_ci break; 44728c2ecf20Sopenharmony_ci } 44738c2ecf20Sopenharmony_ci } 44748c2ecf20Sopenharmony_ci 44758c2ecf20Sopenharmony_ci if (hash_found) { 44768c2ecf20Sopenharmony_ci if (found > 1 && separated) { 44778c2ecf20Sopenharmony_ci ret = 1; 44788c2ecf20Sopenharmony_ci break; 44798c2ecf20Sopenharmony_ci } 44808c2ecf20Sopenharmony_ci 44818c2ecf20Sopenharmony_ci separated = false; 44828c2ecf20Sopenharmony_ci found++; 44838c2ecf20Sopenharmony_ci } else { 44848c2ecf20Sopenharmony_ci separated = true; 44858c2ecf20Sopenharmony_ci } 44868c2ecf20Sopenharmony_ci } 44878c2ecf20Sopenharmony_ci rcu_read_unlock(); 44888c2ecf20Sopenharmony_ci 44898c2ecf20Sopenharmony_ci return ret; 44908c2ecf20Sopenharmony_ci} 44918c2ecf20Sopenharmony_ci 44928c2ecf20Sopenharmony_ci/* 44938c2ecf20Sopenharmony_ci * Periodic address status verification 44948c2ecf20Sopenharmony_ci */ 44958c2ecf20Sopenharmony_ci 44968c2ecf20Sopenharmony_cistatic void addrconf_verify_rtnl(void) 44978c2ecf20Sopenharmony_ci{ 44988c2ecf20Sopenharmony_ci unsigned long now, next, next_sec, next_sched; 44998c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 45008c2ecf20Sopenharmony_ci int i; 45018c2ecf20Sopenharmony_ci 45028c2ecf20Sopenharmony_ci ASSERT_RTNL(); 45038c2ecf20Sopenharmony_ci 45048c2ecf20Sopenharmony_ci rcu_read_lock_bh(); 45058c2ecf20Sopenharmony_ci now = jiffies; 45068c2ecf20Sopenharmony_ci next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 45078c2ecf20Sopenharmony_ci 45088c2ecf20Sopenharmony_ci cancel_delayed_work(&addr_chk_work); 45098c2ecf20Sopenharmony_ci 45108c2ecf20Sopenharmony_ci for (i = 0; i < IN6_ADDR_HSIZE; i++) { 45118c2ecf20Sopenharmony_cirestart: 45128c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu_bh(ifp, &inet6_addr_lst[i], addr_lst) { 45138c2ecf20Sopenharmony_ci unsigned long age; 45148c2ecf20Sopenharmony_ci 45158c2ecf20Sopenharmony_ci /* When setting preferred_lft to a value not zero or 45168c2ecf20Sopenharmony_ci * infinity, while valid_lft is infinity 45178c2ecf20Sopenharmony_ci * IFA_F_PERMANENT has a non-infinity life time. 45188c2ecf20Sopenharmony_ci */ 45198c2ecf20Sopenharmony_ci if ((ifp->flags & IFA_F_PERMANENT) && 45208c2ecf20Sopenharmony_ci (ifp->prefered_lft == INFINITY_LIFE_TIME)) 45218c2ecf20Sopenharmony_ci continue; 45228c2ecf20Sopenharmony_ci 45238c2ecf20Sopenharmony_ci spin_lock(&ifp->lock); 45248c2ecf20Sopenharmony_ci /* We try to batch several events at once. */ 45258c2ecf20Sopenharmony_ci age = (now - ifp->tstamp + ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 45268c2ecf20Sopenharmony_ci 45278c2ecf20Sopenharmony_ci if (ifp->valid_lft != INFINITY_LIFE_TIME && 45288c2ecf20Sopenharmony_ci age >= ifp->valid_lft) { 45298c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 45308c2ecf20Sopenharmony_ci in6_ifa_hold(ifp); 45318c2ecf20Sopenharmony_ci ipv6_del_addr(ifp); 45328c2ecf20Sopenharmony_ci goto restart; 45338c2ecf20Sopenharmony_ci } else if (ifp->prefered_lft == INFINITY_LIFE_TIME) { 45348c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 45358c2ecf20Sopenharmony_ci continue; 45368c2ecf20Sopenharmony_ci } else if (age >= ifp->prefered_lft) { 45378c2ecf20Sopenharmony_ci /* jiffies - ifp->tstamp > age >= ifp->prefered_lft */ 45388c2ecf20Sopenharmony_ci int deprecate = 0; 45398c2ecf20Sopenharmony_ci 45408c2ecf20Sopenharmony_ci if (!(ifp->flags&IFA_F_DEPRECATED)) { 45418c2ecf20Sopenharmony_ci deprecate = 1; 45428c2ecf20Sopenharmony_ci ifp->flags |= IFA_F_DEPRECATED; 45438c2ecf20Sopenharmony_ci } 45448c2ecf20Sopenharmony_ci 45458c2ecf20Sopenharmony_ci if ((ifp->valid_lft != INFINITY_LIFE_TIME) && 45468c2ecf20Sopenharmony_ci (time_before(ifp->tstamp + ifp->valid_lft * HZ, next))) 45478c2ecf20Sopenharmony_ci next = ifp->tstamp + ifp->valid_lft * HZ; 45488c2ecf20Sopenharmony_ci 45498c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 45508c2ecf20Sopenharmony_ci 45518c2ecf20Sopenharmony_ci if (deprecate) { 45528c2ecf20Sopenharmony_ci in6_ifa_hold(ifp); 45538c2ecf20Sopenharmony_ci 45548c2ecf20Sopenharmony_ci ipv6_ifa_notify(0, ifp); 45558c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 45568c2ecf20Sopenharmony_ci goto restart; 45578c2ecf20Sopenharmony_ci } 45588c2ecf20Sopenharmony_ci } else if ((ifp->flags&IFA_F_TEMPORARY) && 45598c2ecf20Sopenharmony_ci !(ifp->flags&IFA_F_TENTATIVE)) { 45608c2ecf20Sopenharmony_ci unsigned long regen_advance = ifp->idev->cnf.regen_max_retry * 45618c2ecf20Sopenharmony_ci ifp->idev->cnf.dad_transmits * 45628c2ecf20Sopenharmony_ci max(NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME), HZ/100) / HZ; 45638c2ecf20Sopenharmony_ci 45648c2ecf20Sopenharmony_ci if (age >= ifp->prefered_lft - regen_advance) { 45658c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifpub = ifp->ifpub; 45668c2ecf20Sopenharmony_ci if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next)) 45678c2ecf20Sopenharmony_ci next = ifp->tstamp + ifp->prefered_lft * HZ; 45688c2ecf20Sopenharmony_ci if (!ifp->regen_count && ifpub) { 45698c2ecf20Sopenharmony_ci ifp->regen_count++; 45708c2ecf20Sopenharmony_ci in6_ifa_hold(ifp); 45718c2ecf20Sopenharmony_ci in6_ifa_hold(ifpub); 45728c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 45738c2ecf20Sopenharmony_ci 45748c2ecf20Sopenharmony_ci spin_lock(&ifpub->lock); 45758c2ecf20Sopenharmony_ci ifpub->regen_count = 0; 45768c2ecf20Sopenharmony_ci spin_unlock(&ifpub->lock); 45778c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 45788c2ecf20Sopenharmony_ci ipv6_create_tempaddr(ifpub, true); 45798c2ecf20Sopenharmony_ci in6_ifa_put(ifpub); 45808c2ecf20Sopenharmony_ci in6_ifa_put(ifp); 45818c2ecf20Sopenharmony_ci rcu_read_lock_bh(); 45828c2ecf20Sopenharmony_ci goto restart; 45838c2ecf20Sopenharmony_ci } 45848c2ecf20Sopenharmony_ci } else if (time_before(ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ, next)) 45858c2ecf20Sopenharmony_ci next = ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ; 45868c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 45878c2ecf20Sopenharmony_ci } else { 45888c2ecf20Sopenharmony_ci /* ifp->prefered_lft <= ifp->valid_lft */ 45898c2ecf20Sopenharmony_ci if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next)) 45908c2ecf20Sopenharmony_ci next = ifp->tstamp + ifp->prefered_lft * HZ; 45918c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 45928c2ecf20Sopenharmony_ci } 45938c2ecf20Sopenharmony_ci } 45948c2ecf20Sopenharmony_ci } 45958c2ecf20Sopenharmony_ci 45968c2ecf20Sopenharmony_ci next_sec = round_jiffies_up(next); 45978c2ecf20Sopenharmony_ci next_sched = next; 45988c2ecf20Sopenharmony_ci 45998c2ecf20Sopenharmony_ci /* If rounded timeout is accurate enough, accept it. */ 46008c2ecf20Sopenharmony_ci if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 46018c2ecf20Sopenharmony_ci next_sched = next_sec; 46028c2ecf20Sopenharmony_ci 46038c2ecf20Sopenharmony_ci /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 46048c2ecf20Sopenharmony_ci if (time_before(next_sched, jiffies + ADDRCONF_TIMER_FUZZ_MAX)) 46058c2ecf20Sopenharmony_ci next_sched = jiffies + ADDRCONF_TIMER_FUZZ_MAX; 46068c2ecf20Sopenharmony_ci 46078c2ecf20Sopenharmony_ci pr_debug("now = %lu, schedule = %lu, rounded schedule = %lu => %lu\n", 46088c2ecf20Sopenharmony_ci now, next, next_sec, next_sched); 46098c2ecf20Sopenharmony_ci mod_delayed_work(addrconf_wq, &addr_chk_work, next_sched - now); 46108c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 46118c2ecf20Sopenharmony_ci} 46128c2ecf20Sopenharmony_ci 46138c2ecf20Sopenharmony_cistatic void addrconf_verify_work(struct work_struct *w) 46148c2ecf20Sopenharmony_ci{ 46158c2ecf20Sopenharmony_ci rtnl_lock(); 46168c2ecf20Sopenharmony_ci addrconf_verify_rtnl(); 46178c2ecf20Sopenharmony_ci rtnl_unlock(); 46188c2ecf20Sopenharmony_ci} 46198c2ecf20Sopenharmony_ci 46208c2ecf20Sopenharmony_cistatic void addrconf_verify(void) 46218c2ecf20Sopenharmony_ci{ 46228c2ecf20Sopenharmony_ci mod_delayed_work(addrconf_wq, &addr_chk_work, 0); 46238c2ecf20Sopenharmony_ci} 46248c2ecf20Sopenharmony_ci 46258c2ecf20Sopenharmony_cistatic struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local, 46268c2ecf20Sopenharmony_ci struct in6_addr **peer_pfx) 46278c2ecf20Sopenharmony_ci{ 46288c2ecf20Sopenharmony_ci struct in6_addr *pfx = NULL; 46298c2ecf20Sopenharmony_ci 46308c2ecf20Sopenharmony_ci *peer_pfx = NULL; 46318c2ecf20Sopenharmony_ci 46328c2ecf20Sopenharmony_ci if (addr) 46338c2ecf20Sopenharmony_ci pfx = nla_data(addr); 46348c2ecf20Sopenharmony_ci 46358c2ecf20Sopenharmony_ci if (local) { 46368c2ecf20Sopenharmony_ci if (pfx && nla_memcmp(local, pfx, sizeof(*pfx))) 46378c2ecf20Sopenharmony_ci *peer_pfx = pfx; 46388c2ecf20Sopenharmony_ci pfx = nla_data(local); 46398c2ecf20Sopenharmony_ci } 46408c2ecf20Sopenharmony_ci 46418c2ecf20Sopenharmony_ci return pfx; 46428c2ecf20Sopenharmony_ci} 46438c2ecf20Sopenharmony_ci 46448c2ecf20Sopenharmony_cistatic const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = { 46458c2ecf20Sopenharmony_ci [IFA_ADDRESS] = { .len = sizeof(struct in6_addr) }, 46468c2ecf20Sopenharmony_ci [IFA_LOCAL] = { .len = sizeof(struct in6_addr) }, 46478c2ecf20Sopenharmony_ci [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, 46488c2ecf20Sopenharmony_ci [IFA_FLAGS] = { .len = sizeof(u32) }, 46498c2ecf20Sopenharmony_ci [IFA_RT_PRIORITY] = { .len = sizeof(u32) }, 46508c2ecf20Sopenharmony_ci [IFA_TARGET_NETNSID] = { .type = NLA_S32 }, 46518c2ecf20Sopenharmony_ci}; 46528c2ecf20Sopenharmony_ci 46538c2ecf20Sopenharmony_cistatic int 46548c2ecf20Sopenharmony_ciinet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, 46558c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 46568c2ecf20Sopenharmony_ci{ 46578c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 46588c2ecf20Sopenharmony_ci struct ifaddrmsg *ifm; 46598c2ecf20Sopenharmony_ci struct nlattr *tb[IFA_MAX+1]; 46608c2ecf20Sopenharmony_ci struct in6_addr *pfx, *peer_pfx; 46618c2ecf20Sopenharmony_ci u32 ifa_flags; 46628c2ecf20Sopenharmony_ci int err; 46638c2ecf20Sopenharmony_ci 46648c2ecf20Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 46658c2ecf20Sopenharmony_ci ifa_ipv6_policy, extack); 46668c2ecf20Sopenharmony_ci if (err < 0) 46678c2ecf20Sopenharmony_ci return err; 46688c2ecf20Sopenharmony_ci 46698c2ecf20Sopenharmony_ci ifm = nlmsg_data(nlh); 46708c2ecf20Sopenharmony_ci pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx); 46718c2ecf20Sopenharmony_ci if (!pfx) 46728c2ecf20Sopenharmony_ci return -EINVAL; 46738c2ecf20Sopenharmony_ci 46748c2ecf20Sopenharmony_ci ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags; 46758c2ecf20Sopenharmony_ci 46768c2ecf20Sopenharmony_ci /* We ignore other flags so far. */ 46778c2ecf20Sopenharmony_ci ifa_flags &= IFA_F_MANAGETEMPADDR; 46788c2ecf20Sopenharmony_ci 46798c2ecf20Sopenharmony_ci return inet6_addr_del(net, ifm->ifa_index, ifa_flags, pfx, 46808c2ecf20Sopenharmony_ci ifm->ifa_prefixlen); 46818c2ecf20Sopenharmony_ci} 46828c2ecf20Sopenharmony_ci 46838c2ecf20Sopenharmony_cistatic int modify_prefix_route(struct inet6_ifaddr *ifp, 46848c2ecf20Sopenharmony_ci unsigned long expires, u32 flags, 46858c2ecf20Sopenharmony_ci bool modify_peer) 46868c2ecf20Sopenharmony_ci{ 46878c2ecf20Sopenharmony_ci struct fib6_info *f6i; 46888c2ecf20Sopenharmony_ci u32 prio; 46898c2ecf20Sopenharmony_ci 46908c2ecf20Sopenharmony_ci f6i = addrconf_get_prefix_route(modify_peer ? &ifp->peer_addr : &ifp->addr, 46918c2ecf20Sopenharmony_ci ifp->prefix_len, 46928c2ecf20Sopenharmony_ci ifp->idev->dev, 0, RTF_DEFAULT, true); 46938c2ecf20Sopenharmony_ci if (!f6i) 46948c2ecf20Sopenharmony_ci return -ENOENT; 46958c2ecf20Sopenharmony_ci 46968c2ecf20Sopenharmony_ci prio = ifp->rt_priority ? : IP6_RT_PRIO_ADDRCONF; 46978c2ecf20Sopenharmony_ci if (f6i->fib6_metric != prio) { 46988c2ecf20Sopenharmony_ci /* delete old one */ 46998c2ecf20Sopenharmony_ci ip6_del_rt(dev_net(ifp->idev->dev), f6i, false); 47008c2ecf20Sopenharmony_ci 47018c2ecf20Sopenharmony_ci /* add new one */ 47028c2ecf20Sopenharmony_ci addrconf_prefix_route(modify_peer ? &ifp->peer_addr : &ifp->addr, 47038c2ecf20Sopenharmony_ci ifp->prefix_len, 47048c2ecf20Sopenharmony_ci ifp->rt_priority, ifp->idev->dev, 47058c2ecf20Sopenharmony_ci expires, flags, GFP_KERNEL); 47068c2ecf20Sopenharmony_ci } else { 47078c2ecf20Sopenharmony_ci if (!expires) 47088c2ecf20Sopenharmony_ci fib6_clean_expires(f6i); 47098c2ecf20Sopenharmony_ci else 47108c2ecf20Sopenharmony_ci fib6_set_expires(f6i, expires); 47118c2ecf20Sopenharmony_ci 47128c2ecf20Sopenharmony_ci fib6_info_release(f6i); 47138c2ecf20Sopenharmony_ci } 47148c2ecf20Sopenharmony_ci 47158c2ecf20Sopenharmony_ci return 0; 47168c2ecf20Sopenharmony_ci} 47178c2ecf20Sopenharmony_ci 47188c2ecf20Sopenharmony_cistatic int inet6_addr_modify(struct inet6_ifaddr *ifp, struct ifa6_config *cfg) 47198c2ecf20Sopenharmony_ci{ 47208c2ecf20Sopenharmony_ci u32 flags; 47218c2ecf20Sopenharmony_ci clock_t expires; 47228c2ecf20Sopenharmony_ci unsigned long timeout; 47238c2ecf20Sopenharmony_ci bool was_managetempaddr; 47248c2ecf20Sopenharmony_ci bool had_prefixroute; 47258c2ecf20Sopenharmony_ci bool new_peer = false; 47268c2ecf20Sopenharmony_ci 47278c2ecf20Sopenharmony_ci ASSERT_RTNL(); 47288c2ecf20Sopenharmony_ci 47298c2ecf20Sopenharmony_ci if (!cfg->valid_lft || cfg->preferred_lft > cfg->valid_lft) 47308c2ecf20Sopenharmony_ci return -EINVAL; 47318c2ecf20Sopenharmony_ci 47328c2ecf20Sopenharmony_ci if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR && 47338c2ecf20Sopenharmony_ci (ifp->flags & IFA_F_TEMPORARY || ifp->prefix_len != 64)) 47348c2ecf20Sopenharmony_ci return -EINVAL; 47358c2ecf20Sopenharmony_ci 47368c2ecf20Sopenharmony_ci if (!(ifp->flags & IFA_F_TENTATIVE) || ifp->flags & IFA_F_DADFAILED) 47378c2ecf20Sopenharmony_ci cfg->ifa_flags &= ~IFA_F_OPTIMISTIC; 47388c2ecf20Sopenharmony_ci 47398c2ecf20Sopenharmony_ci timeout = addrconf_timeout_fixup(cfg->valid_lft, HZ); 47408c2ecf20Sopenharmony_ci if (addrconf_finite_timeout(timeout)) { 47418c2ecf20Sopenharmony_ci expires = jiffies_to_clock_t(timeout * HZ); 47428c2ecf20Sopenharmony_ci cfg->valid_lft = timeout; 47438c2ecf20Sopenharmony_ci flags = RTF_EXPIRES; 47448c2ecf20Sopenharmony_ci } else { 47458c2ecf20Sopenharmony_ci expires = 0; 47468c2ecf20Sopenharmony_ci flags = 0; 47478c2ecf20Sopenharmony_ci cfg->ifa_flags |= IFA_F_PERMANENT; 47488c2ecf20Sopenharmony_ci } 47498c2ecf20Sopenharmony_ci 47508c2ecf20Sopenharmony_ci timeout = addrconf_timeout_fixup(cfg->preferred_lft, HZ); 47518c2ecf20Sopenharmony_ci if (addrconf_finite_timeout(timeout)) { 47528c2ecf20Sopenharmony_ci if (timeout == 0) 47538c2ecf20Sopenharmony_ci cfg->ifa_flags |= IFA_F_DEPRECATED; 47548c2ecf20Sopenharmony_ci cfg->preferred_lft = timeout; 47558c2ecf20Sopenharmony_ci } 47568c2ecf20Sopenharmony_ci 47578c2ecf20Sopenharmony_ci if (cfg->peer_pfx && 47588c2ecf20Sopenharmony_ci memcmp(&ifp->peer_addr, cfg->peer_pfx, sizeof(struct in6_addr))) { 47598c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&ifp->peer_addr)) 47608c2ecf20Sopenharmony_ci cleanup_prefix_route(ifp, expires, true, true); 47618c2ecf20Sopenharmony_ci new_peer = true; 47628c2ecf20Sopenharmony_ci } 47638c2ecf20Sopenharmony_ci 47648c2ecf20Sopenharmony_ci spin_lock_bh(&ifp->lock); 47658c2ecf20Sopenharmony_ci was_managetempaddr = ifp->flags & IFA_F_MANAGETEMPADDR; 47668c2ecf20Sopenharmony_ci had_prefixroute = ifp->flags & IFA_F_PERMANENT && 47678c2ecf20Sopenharmony_ci !(ifp->flags & IFA_F_NOPREFIXROUTE); 47688c2ecf20Sopenharmony_ci ifp->flags &= ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD | 47698c2ecf20Sopenharmony_ci IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR | 47708c2ecf20Sopenharmony_ci IFA_F_NOPREFIXROUTE); 47718c2ecf20Sopenharmony_ci ifp->flags |= cfg->ifa_flags; 47728c2ecf20Sopenharmony_ci ifp->tstamp = jiffies; 47738c2ecf20Sopenharmony_ci ifp->valid_lft = cfg->valid_lft; 47748c2ecf20Sopenharmony_ci ifp->prefered_lft = cfg->preferred_lft; 47758c2ecf20Sopenharmony_ci 47768c2ecf20Sopenharmony_ci if (cfg->rt_priority && cfg->rt_priority != ifp->rt_priority) 47778c2ecf20Sopenharmony_ci ifp->rt_priority = cfg->rt_priority; 47788c2ecf20Sopenharmony_ci 47798c2ecf20Sopenharmony_ci if (new_peer) 47808c2ecf20Sopenharmony_ci ifp->peer_addr = *cfg->peer_pfx; 47818c2ecf20Sopenharmony_ci 47828c2ecf20Sopenharmony_ci spin_unlock_bh(&ifp->lock); 47838c2ecf20Sopenharmony_ci if (!(ifp->flags&IFA_F_TENTATIVE)) 47848c2ecf20Sopenharmony_ci ipv6_ifa_notify(0, ifp); 47858c2ecf20Sopenharmony_ci 47868c2ecf20Sopenharmony_ci if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) { 47878c2ecf20Sopenharmony_ci int rc = -ENOENT; 47888c2ecf20Sopenharmony_ci 47898c2ecf20Sopenharmony_ci if (had_prefixroute) 47908c2ecf20Sopenharmony_ci rc = modify_prefix_route(ifp, expires, flags, false); 47918c2ecf20Sopenharmony_ci 47928c2ecf20Sopenharmony_ci /* prefix route could have been deleted; if so restore it */ 47938c2ecf20Sopenharmony_ci if (rc == -ENOENT) { 47948c2ecf20Sopenharmony_ci addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 47958c2ecf20Sopenharmony_ci ifp->rt_priority, ifp->idev->dev, 47968c2ecf20Sopenharmony_ci expires, flags, GFP_KERNEL); 47978c2ecf20Sopenharmony_ci } 47988c2ecf20Sopenharmony_ci 47998c2ecf20Sopenharmony_ci if (had_prefixroute && !ipv6_addr_any(&ifp->peer_addr)) 48008c2ecf20Sopenharmony_ci rc = modify_prefix_route(ifp, expires, flags, true); 48018c2ecf20Sopenharmony_ci 48028c2ecf20Sopenharmony_ci if (rc == -ENOENT && !ipv6_addr_any(&ifp->peer_addr)) { 48038c2ecf20Sopenharmony_ci addrconf_prefix_route(&ifp->peer_addr, ifp->prefix_len, 48048c2ecf20Sopenharmony_ci ifp->rt_priority, ifp->idev->dev, 48058c2ecf20Sopenharmony_ci expires, flags, GFP_KERNEL); 48068c2ecf20Sopenharmony_ci } 48078c2ecf20Sopenharmony_ci } else if (had_prefixroute) { 48088c2ecf20Sopenharmony_ci enum cleanup_prefix_rt_t action; 48098c2ecf20Sopenharmony_ci unsigned long rt_expires; 48108c2ecf20Sopenharmony_ci 48118c2ecf20Sopenharmony_ci write_lock_bh(&ifp->idev->lock); 48128c2ecf20Sopenharmony_ci action = check_cleanup_prefix_route(ifp, &rt_expires); 48138c2ecf20Sopenharmony_ci write_unlock_bh(&ifp->idev->lock); 48148c2ecf20Sopenharmony_ci 48158c2ecf20Sopenharmony_ci if (action != CLEANUP_PREFIX_RT_NOP) { 48168c2ecf20Sopenharmony_ci cleanup_prefix_route(ifp, rt_expires, 48178c2ecf20Sopenharmony_ci action == CLEANUP_PREFIX_RT_DEL, false); 48188c2ecf20Sopenharmony_ci } 48198c2ecf20Sopenharmony_ci } 48208c2ecf20Sopenharmony_ci 48218c2ecf20Sopenharmony_ci if (was_managetempaddr || ifp->flags & IFA_F_MANAGETEMPADDR) { 48228c2ecf20Sopenharmony_ci if (was_managetempaddr && 48238c2ecf20Sopenharmony_ci !(ifp->flags & IFA_F_MANAGETEMPADDR)) { 48248c2ecf20Sopenharmony_ci cfg->valid_lft = 0; 48258c2ecf20Sopenharmony_ci cfg->preferred_lft = 0; 48268c2ecf20Sopenharmony_ci } 48278c2ecf20Sopenharmony_ci manage_tempaddrs(ifp->idev, ifp, cfg->valid_lft, 48288c2ecf20Sopenharmony_ci cfg->preferred_lft, !was_managetempaddr, 48298c2ecf20Sopenharmony_ci jiffies); 48308c2ecf20Sopenharmony_ci } 48318c2ecf20Sopenharmony_ci 48328c2ecf20Sopenharmony_ci addrconf_verify_rtnl(); 48338c2ecf20Sopenharmony_ci 48348c2ecf20Sopenharmony_ci return 0; 48358c2ecf20Sopenharmony_ci} 48368c2ecf20Sopenharmony_ci 48378c2ecf20Sopenharmony_cistatic int 48388c2ecf20Sopenharmony_ciinet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, 48398c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 48408c2ecf20Sopenharmony_ci{ 48418c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 48428c2ecf20Sopenharmony_ci struct ifaddrmsg *ifm; 48438c2ecf20Sopenharmony_ci struct nlattr *tb[IFA_MAX+1]; 48448c2ecf20Sopenharmony_ci struct in6_addr *peer_pfx; 48458c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa; 48468c2ecf20Sopenharmony_ci struct net_device *dev; 48478c2ecf20Sopenharmony_ci struct inet6_dev *idev; 48488c2ecf20Sopenharmony_ci struct ifa6_config cfg; 48498c2ecf20Sopenharmony_ci int err; 48508c2ecf20Sopenharmony_ci 48518c2ecf20Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 48528c2ecf20Sopenharmony_ci ifa_ipv6_policy, extack); 48538c2ecf20Sopenharmony_ci if (err < 0) 48548c2ecf20Sopenharmony_ci return err; 48558c2ecf20Sopenharmony_ci 48568c2ecf20Sopenharmony_ci memset(&cfg, 0, sizeof(cfg)); 48578c2ecf20Sopenharmony_ci 48588c2ecf20Sopenharmony_ci ifm = nlmsg_data(nlh); 48598c2ecf20Sopenharmony_ci cfg.pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx); 48608c2ecf20Sopenharmony_ci if (!cfg.pfx) 48618c2ecf20Sopenharmony_ci return -EINVAL; 48628c2ecf20Sopenharmony_ci 48638c2ecf20Sopenharmony_ci cfg.peer_pfx = peer_pfx; 48648c2ecf20Sopenharmony_ci cfg.plen = ifm->ifa_prefixlen; 48658c2ecf20Sopenharmony_ci if (tb[IFA_RT_PRIORITY]) 48668c2ecf20Sopenharmony_ci cfg.rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]); 48678c2ecf20Sopenharmony_ci 48688c2ecf20Sopenharmony_ci cfg.valid_lft = INFINITY_LIFE_TIME; 48698c2ecf20Sopenharmony_ci cfg.preferred_lft = INFINITY_LIFE_TIME; 48708c2ecf20Sopenharmony_ci 48718c2ecf20Sopenharmony_ci if (tb[IFA_CACHEINFO]) { 48728c2ecf20Sopenharmony_ci struct ifa_cacheinfo *ci; 48738c2ecf20Sopenharmony_ci 48748c2ecf20Sopenharmony_ci ci = nla_data(tb[IFA_CACHEINFO]); 48758c2ecf20Sopenharmony_ci cfg.valid_lft = ci->ifa_valid; 48768c2ecf20Sopenharmony_ci cfg.preferred_lft = ci->ifa_prefered; 48778c2ecf20Sopenharmony_ci } 48788c2ecf20Sopenharmony_ci 48798c2ecf20Sopenharmony_ci dev = __dev_get_by_index(net, ifm->ifa_index); 48808c2ecf20Sopenharmony_ci if (!dev) 48818c2ecf20Sopenharmony_ci return -ENODEV; 48828c2ecf20Sopenharmony_ci 48838c2ecf20Sopenharmony_ci if (tb[IFA_FLAGS]) 48848c2ecf20Sopenharmony_ci cfg.ifa_flags = nla_get_u32(tb[IFA_FLAGS]); 48858c2ecf20Sopenharmony_ci else 48868c2ecf20Sopenharmony_ci cfg.ifa_flags = ifm->ifa_flags; 48878c2ecf20Sopenharmony_ci 48888c2ecf20Sopenharmony_ci /* We ignore other flags so far. */ 48898c2ecf20Sopenharmony_ci cfg.ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | 48908c2ecf20Sopenharmony_ci IFA_F_MANAGETEMPADDR | IFA_F_NOPREFIXROUTE | 48918c2ecf20Sopenharmony_ci IFA_F_MCAUTOJOIN | IFA_F_OPTIMISTIC; 48928c2ecf20Sopenharmony_ci 48938c2ecf20Sopenharmony_ci idev = ipv6_find_idev(dev); 48948c2ecf20Sopenharmony_ci if (IS_ERR(idev)) 48958c2ecf20Sopenharmony_ci return PTR_ERR(idev); 48968c2ecf20Sopenharmony_ci 48978c2ecf20Sopenharmony_ci if (!ipv6_allow_optimistic_dad(net, idev)) 48988c2ecf20Sopenharmony_ci cfg.ifa_flags &= ~IFA_F_OPTIMISTIC; 48998c2ecf20Sopenharmony_ci 49008c2ecf20Sopenharmony_ci if (cfg.ifa_flags & IFA_F_NODAD && 49018c2ecf20Sopenharmony_ci cfg.ifa_flags & IFA_F_OPTIMISTIC) { 49028c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "IFA_F_NODAD and IFA_F_OPTIMISTIC are mutually exclusive"); 49038c2ecf20Sopenharmony_ci return -EINVAL; 49048c2ecf20Sopenharmony_ci } 49058c2ecf20Sopenharmony_ci 49068c2ecf20Sopenharmony_ci ifa = ipv6_get_ifaddr(net, cfg.pfx, dev, 1); 49078c2ecf20Sopenharmony_ci if (!ifa) { 49088c2ecf20Sopenharmony_ci /* 49098c2ecf20Sopenharmony_ci * It would be best to check for !NLM_F_CREATE here but 49108c2ecf20Sopenharmony_ci * userspace already relies on not having to provide this. 49118c2ecf20Sopenharmony_ci */ 49128c2ecf20Sopenharmony_ci return inet6_addr_add(net, ifm->ifa_index, &cfg, extack); 49138c2ecf20Sopenharmony_ci } 49148c2ecf20Sopenharmony_ci 49158c2ecf20Sopenharmony_ci if (nlh->nlmsg_flags & NLM_F_EXCL || 49168c2ecf20Sopenharmony_ci !(nlh->nlmsg_flags & NLM_F_REPLACE)) 49178c2ecf20Sopenharmony_ci err = -EEXIST; 49188c2ecf20Sopenharmony_ci else 49198c2ecf20Sopenharmony_ci err = inet6_addr_modify(ifa, &cfg); 49208c2ecf20Sopenharmony_ci 49218c2ecf20Sopenharmony_ci in6_ifa_put(ifa); 49228c2ecf20Sopenharmony_ci 49238c2ecf20Sopenharmony_ci return err; 49248c2ecf20Sopenharmony_ci} 49258c2ecf20Sopenharmony_ci 49268c2ecf20Sopenharmony_cistatic void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u32 flags, 49278c2ecf20Sopenharmony_ci u8 scope, int ifindex) 49288c2ecf20Sopenharmony_ci{ 49298c2ecf20Sopenharmony_ci struct ifaddrmsg *ifm; 49308c2ecf20Sopenharmony_ci 49318c2ecf20Sopenharmony_ci ifm = nlmsg_data(nlh); 49328c2ecf20Sopenharmony_ci ifm->ifa_family = AF_INET6; 49338c2ecf20Sopenharmony_ci ifm->ifa_prefixlen = prefixlen; 49348c2ecf20Sopenharmony_ci ifm->ifa_flags = flags; 49358c2ecf20Sopenharmony_ci ifm->ifa_scope = scope; 49368c2ecf20Sopenharmony_ci ifm->ifa_index = ifindex; 49378c2ecf20Sopenharmony_ci} 49388c2ecf20Sopenharmony_ci 49398c2ecf20Sopenharmony_cistatic int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 49408c2ecf20Sopenharmony_ci unsigned long tstamp, u32 preferred, u32 valid) 49418c2ecf20Sopenharmony_ci{ 49428c2ecf20Sopenharmony_ci struct ifa_cacheinfo ci; 49438c2ecf20Sopenharmony_ci 49448c2ecf20Sopenharmony_ci ci.cstamp = cstamp_delta(cstamp); 49458c2ecf20Sopenharmony_ci ci.tstamp = cstamp_delta(tstamp); 49468c2ecf20Sopenharmony_ci ci.ifa_prefered = preferred; 49478c2ecf20Sopenharmony_ci ci.ifa_valid = valid; 49488c2ecf20Sopenharmony_ci 49498c2ecf20Sopenharmony_ci return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 49508c2ecf20Sopenharmony_ci} 49518c2ecf20Sopenharmony_ci 49528c2ecf20Sopenharmony_cistatic inline int rt_scope(int ifa_scope) 49538c2ecf20Sopenharmony_ci{ 49548c2ecf20Sopenharmony_ci if (ifa_scope & IFA_HOST) 49558c2ecf20Sopenharmony_ci return RT_SCOPE_HOST; 49568c2ecf20Sopenharmony_ci else if (ifa_scope & IFA_LINK) 49578c2ecf20Sopenharmony_ci return RT_SCOPE_LINK; 49588c2ecf20Sopenharmony_ci else if (ifa_scope & IFA_SITE) 49598c2ecf20Sopenharmony_ci return RT_SCOPE_SITE; 49608c2ecf20Sopenharmony_ci else 49618c2ecf20Sopenharmony_ci return RT_SCOPE_UNIVERSE; 49628c2ecf20Sopenharmony_ci} 49638c2ecf20Sopenharmony_ci 49648c2ecf20Sopenharmony_cistatic inline int inet6_ifaddr_msgsize(void) 49658c2ecf20Sopenharmony_ci{ 49668c2ecf20Sopenharmony_ci return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 49678c2ecf20Sopenharmony_ci + nla_total_size(16) /* IFA_LOCAL */ 49688c2ecf20Sopenharmony_ci + nla_total_size(16) /* IFA_ADDRESS */ 49698c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct ifa_cacheinfo)) 49708c2ecf20Sopenharmony_ci + nla_total_size(4) /* IFA_FLAGS */ 49718c2ecf20Sopenharmony_ci + nla_total_size(4) /* IFA_RT_PRIORITY */; 49728c2ecf20Sopenharmony_ci} 49738c2ecf20Sopenharmony_ci 49748c2ecf20Sopenharmony_cienum addr_type_t { 49758c2ecf20Sopenharmony_ci UNICAST_ADDR, 49768c2ecf20Sopenharmony_ci MULTICAST_ADDR, 49778c2ecf20Sopenharmony_ci ANYCAST_ADDR, 49788c2ecf20Sopenharmony_ci}; 49798c2ecf20Sopenharmony_ci 49808c2ecf20Sopenharmony_cistruct inet6_fill_args { 49818c2ecf20Sopenharmony_ci u32 portid; 49828c2ecf20Sopenharmony_ci u32 seq; 49838c2ecf20Sopenharmony_ci int event; 49848c2ecf20Sopenharmony_ci unsigned int flags; 49858c2ecf20Sopenharmony_ci int netnsid; 49868c2ecf20Sopenharmony_ci int ifindex; 49878c2ecf20Sopenharmony_ci enum addr_type_t type; 49888c2ecf20Sopenharmony_ci}; 49898c2ecf20Sopenharmony_ci 49908c2ecf20Sopenharmony_cistatic int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, 49918c2ecf20Sopenharmony_ci struct inet6_fill_args *args) 49928c2ecf20Sopenharmony_ci{ 49938c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 49948c2ecf20Sopenharmony_ci u32 preferred, valid; 49958c2ecf20Sopenharmony_ci 49968c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, args->portid, args->seq, args->event, 49978c2ecf20Sopenharmony_ci sizeof(struct ifaddrmsg), args->flags); 49988c2ecf20Sopenharmony_ci if (!nlh) 49998c2ecf20Sopenharmony_ci return -EMSGSIZE; 50008c2ecf20Sopenharmony_ci 50018c2ecf20Sopenharmony_ci put_ifaddrmsg(nlh, ifa->prefix_len, ifa->flags, rt_scope(ifa->scope), 50028c2ecf20Sopenharmony_ci ifa->idev->dev->ifindex); 50038c2ecf20Sopenharmony_ci 50048c2ecf20Sopenharmony_ci if (args->netnsid >= 0 && 50058c2ecf20Sopenharmony_ci nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) 50068c2ecf20Sopenharmony_ci goto error; 50078c2ecf20Sopenharmony_ci 50088c2ecf20Sopenharmony_ci spin_lock_bh(&ifa->lock); 50098c2ecf20Sopenharmony_ci if (!((ifa->flags&IFA_F_PERMANENT) && 50108c2ecf20Sopenharmony_ci (ifa->prefered_lft == INFINITY_LIFE_TIME))) { 50118c2ecf20Sopenharmony_ci preferred = ifa->prefered_lft; 50128c2ecf20Sopenharmony_ci valid = ifa->valid_lft; 50138c2ecf20Sopenharmony_ci if (preferred != INFINITY_LIFE_TIME) { 50148c2ecf20Sopenharmony_ci long tval = (jiffies - ifa->tstamp)/HZ; 50158c2ecf20Sopenharmony_ci if (preferred > tval) 50168c2ecf20Sopenharmony_ci preferred -= tval; 50178c2ecf20Sopenharmony_ci else 50188c2ecf20Sopenharmony_ci preferred = 0; 50198c2ecf20Sopenharmony_ci if (valid != INFINITY_LIFE_TIME) { 50208c2ecf20Sopenharmony_ci if (valid > tval) 50218c2ecf20Sopenharmony_ci valid -= tval; 50228c2ecf20Sopenharmony_ci else 50238c2ecf20Sopenharmony_ci valid = 0; 50248c2ecf20Sopenharmony_ci } 50258c2ecf20Sopenharmony_ci } 50268c2ecf20Sopenharmony_ci } else { 50278c2ecf20Sopenharmony_ci preferred = INFINITY_LIFE_TIME; 50288c2ecf20Sopenharmony_ci valid = INFINITY_LIFE_TIME; 50298c2ecf20Sopenharmony_ci } 50308c2ecf20Sopenharmony_ci spin_unlock_bh(&ifa->lock); 50318c2ecf20Sopenharmony_ci 50328c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&ifa->peer_addr)) { 50338c2ecf20Sopenharmony_ci if (nla_put_in6_addr(skb, IFA_LOCAL, &ifa->addr) < 0 || 50348c2ecf20Sopenharmony_ci nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->peer_addr) < 0) 50358c2ecf20Sopenharmony_ci goto error; 50368c2ecf20Sopenharmony_ci } else 50378c2ecf20Sopenharmony_ci if (nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->addr) < 0) 50388c2ecf20Sopenharmony_ci goto error; 50398c2ecf20Sopenharmony_ci 50408c2ecf20Sopenharmony_ci if (ifa->rt_priority && 50418c2ecf20Sopenharmony_ci nla_put_u32(skb, IFA_RT_PRIORITY, ifa->rt_priority)) 50428c2ecf20Sopenharmony_ci goto error; 50438c2ecf20Sopenharmony_ci 50448c2ecf20Sopenharmony_ci if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) 50458c2ecf20Sopenharmony_ci goto error; 50468c2ecf20Sopenharmony_ci 50478c2ecf20Sopenharmony_ci if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0) 50488c2ecf20Sopenharmony_ci goto error; 50498c2ecf20Sopenharmony_ci 50508c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 50518c2ecf20Sopenharmony_ci return 0; 50528c2ecf20Sopenharmony_ci 50538c2ecf20Sopenharmony_cierror: 50548c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 50558c2ecf20Sopenharmony_ci return -EMSGSIZE; 50568c2ecf20Sopenharmony_ci} 50578c2ecf20Sopenharmony_ci 50588c2ecf20Sopenharmony_cistatic int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, 50598c2ecf20Sopenharmony_ci struct inet6_fill_args *args) 50608c2ecf20Sopenharmony_ci{ 50618c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 50628c2ecf20Sopenharmony_ci u8 scope = RT_SCOPE_UNIVERSE; 50638c2ecf20Sopenharmony_ci int ifindex = ifmca->idev->dev->ifindex; 50648c2ecf20Sopenharmony_ci 50658c2ecf20Sopenharmony_ci if (ipv6_addr_scope(&ifmca->mca_addr) & IFA_SITE) 50668c2ecf20Sopenharmony_ci scope = RT_SCOPE_SITE; 50678c2ecf20Sopenharmony_ci 50688c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, args->portid, args->seq, args->event, 50698c2ecf20Sopenharmony_ci sizeof(struct ifaddrmsg), args->flags); 50708c2ecf20Sopenharmony_ci if (!nlh) 50718c2ecf20Sopenharmony_ci return -EMSGSIZE; 50728c2ecf20Sopenharmony_ci 50738c2ecf20Sopenharmony_ci if (args->netnsid >= 0 && 50748c2ecf20Sopenharmony_ci nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) { 50758c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 50768c2ecf20Sopenharmony_ci return -EMSGSIZE; 50778c2ecf20Sopenharmony_ci } 50788c2ecf20Sopenharmony_ci 50798c2ecf20Sopenharmony_ci put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex); 50808c2ecf20Sopenharmony_ci if (nla_put_in6_addr(skb, IFA_MULTICAST, &ifmca->mca_addr) < 0 || 50818c2ecf20Sopenharmony_ci put_cacheinfo(skb, ifmca->mca_cstamp, ifmca->mca_tstamp, 50828c2ecf20Sopenharmony_ci INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) { 50838c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 50848c2ecf20Sopenharmony_ci return -EMSGSIZE; 50858c2ecf20Sopenharmony_ci } 50868c2ecf20Sopenharmony_ci 50878c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 50888c2ecf20Sopenharmony_ci return 0; 50898c2ecf20Sopenharmony_ci} 50908c2ecf20Sopenharmony_ci 50918c2ecf20Sopenharmony_cistatic int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca, 50928c2ecf20Sopenharmony_ci struct inet6_fill_args *args) 50938c2ecf20Sopenharmony_ci{ 50948c2ecf20Sopenharmony_ci struct net_device *dev = fib6_info_nh_dev(ifaca->aca_rt); 50958c2ecf20Sopenharmony_ci int ifindex = dev ? dev->ifindex : 1; 50968c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 50978c2ecf20Sopenharmony_ci u8 scope = RT_SCOPE_UNIVERSE; 50988c2ecf20Sopenharmony_ci 50998c2ecf20Sopenharmony_ci if (ipv6_addr_scope(&ifaca->aca_addr) & IFA_SITE) 51008c2ecf20Sopenharmony_ci scope = RT_SCOPE_SITE; 51018c2ecf20Sopenharmony_ci 51028c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, args->portid, args->seq, args->event, 51038c2ecf20Sopenharmony_ci sizeof(struct ifaddrmsg), args->flags); 51048c2ecf20Sopenharmony_ci if (!nlh) 51058c2ecf20Sopenharmony_ci return -EMSGSIZE; 51068c2ecf20Sopenharmony_ci 51078c2ecf20Sopenharmony_ci if (args->netnsid >= 0 && 51088c2ecf20Sopenharmony_ci nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) { 51098c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 51108c2ecf20Sopenharmony_ci return -EMSGSIZE; 51118c2ecf20Sopenharmony_ci } 51128c2ecf20Sopenharmony_ci 51138c2ecf20Sopenharmony_ci put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex); 51148c2ecf20Sopenharmony_ci if (nla_put_in6_addr(skb, IFA_ANYCAST, &ifaca->aca_addr) < 0 || 51158c2ecf20Sopenharmony_ci put_cacheinfo(skb, ifaca->aca_cstamp, ifaca->aca_tstamp, 51168c2ecf20Sopenharmony_ci INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) { 51178c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 51188c2ecf20Sopenharmony_ci return -EMSGSIZE; 51198c2ecf20Sopenharmony_ci } 51208c2ecf20Sopenharmony_ci 51218c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 51228c2ecf20Sopenharmony_ci return 0; 51238c2ecf20Sopenharmony_ci} 51248c2ecf20Sopenharmony_ci 51258c2ecf20Sopenharmony_ci/* called with rcu_read_lock() */ 51268c2ecf20Sopenharmony_cistatic int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, 51278c2ecf20Sopenharmony_ci struct netlink_callback *cb, int s_ip_idx, 51288c2ecf20Sopenharmony_ci struct inet6_fill_args *fillargs) 51298c2ecf20Sopenharmony_ci{ 51308c2ecf20Sopenharmony_ci struct ifmcaddr6 *ifmca; 51318c2ecf20Sopenharmony_ci struct ifacaddr6 *ifaca; 51328c2ecf20Sopenharmony_ci int ip_idx = 0; 51338c2ecf20Sopenharmony_ci int err = 1; 51348c2ecf20Sopenharmony_ci 51358c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 51368c2ecf20Sopenharmony_ci switch (fillargs->type) { 51378c2ecf20Sopenharmony_ci case UNICAST_ADDR: { 51388c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa; 51398c2ecf20Sopenharmony_ci fillargs->event = RTM_NEWADDR; 51408c2ecf20Sopenharmony_ci 51418c2ecf20Sopenharmony_ci /* unicast address incl. temp addr */ 51428c2ecf20Sopenharmony_ci list_for_each_entry(ifa, &idev->addr_list, if_list) { 51438c2ecf20Sopenharmony_ci if (ip_idx < s_ip_idx) 51448c2ecf20Sopenharmony_ci goto next; 51458c2ecf20Sopenharmony_ci err = inet6_fill_ifaddr(skb, ifa, fillargs); 51468c2ecf20Sopenharmony_ci if (err < 0) 51478c2ecf20Sopenharmony_ci break; 51488c2ecf20Sopenharmony_ci nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 51498c2ecf20Sopenharmony_cinext: 51508c2ecf20Sopenharmony_ci ip_idx++; 51518c2ecf20Sopenharmony_ci } 51528c2ecf20Sopenharmony_ci break; 51538c2ecf20Sopenharmony_ci } 51548c2ecf20Sopenharmony_ci case MULTICAST_ADDR: 51558c2ecf20Sopenharmony_ci fillargs->event = RTM_GETMULTICAST; 51568c2ecf20Sopenharmony_ci 51578c2ecf20Sopenharmony_ci /* multicast address */ 51588c2ecf20Sopenharmony_ci for (ifmca = idev->mc_list; ifmca; 51598c2ecf20Sopenharmony_ci ifmca = ifmca->next, ip_idx++) { 51608c2ecf20Sopenharmony_ci if (ip_idx < s_ip_idx) 51618c2ecf20Sopenharmony_ci continue; 51628c2ecf20Sopenharmony_ci err = inet6_fill_ifmcaddr(skb, ifmca, fillargs); 51638c2ecf20Sopenharmony_ci if (err < 0) 51648c2ecf20Sopenharmony_ci break; 51658c2ecf20Sopenharmony_ci } 51668c2ecf20Sopenharmony_ci break; 51678c2ecf20Sopenharmony_ci case ANYCAST_ADDR: 51688c2ecf20Sopenharmony_ci fillargs->event = RTM_GETANYCAST; 51698c2ecf20Sopenharmony_ci /* anycast address */ 51708c2ecf20Sopenharmony_ci for (ifaca = idev->ac_list; ifaca; 51718c2ecf20Sopenharmony_ci ifaca = ifaca->aca_next, ip_idx++) { 51728c2ecf20Sopenharmony_ci if (ip_idx < s_ip_idx) 51738c2ecf20Sopenharmony_ci continue; 51748c2ecf20Sopenharmony_ci err = inet6_fill_ifacaddr(skb, ifaca, fillargs); 51758c2ecf20Sopenharmony_ci if (err < 0) 51768c2ecf20Sopenharmony_ci break; 51778c2ecf20Sopenharmony_ci } 51788c2ecf20Sopenharmony_ci break; 51798c2ecf20Sopenharmony_ci default: 51808c2ecf20Sopenharmony_ci break; 51818c2ecf20Sopenharmony_ci } 51828c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 51838c2ecf20Sopenharmony_ci cb->args[2] = ip_idx; 51848c2ecf20Sopenharmony_ci return err; 51858c2ecf20Sopenharmony_ci} 51868c2ecf20Sopenharmony_ci 51878c2ecf20Sopenharmony_cistatic int inet6_valid_dump_ifaddr_req(const struct nlmsghdr *nlh, 51888c2ecf20Sopenharmony_ci struct inet6_fill_args *fillargs, 51898c2ecf20Sopenharmony_ci struct net **tgt_net, struct sock *sk, 51908c2ecf20Sopenharmony_ci struct netlink_callback *cb) 51918c2ecf20Sopenharmony_ci{ 51928c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack = cb->extack; 51938c2ecf20Sopenharmony_ci struct nlattr *tb[IFA_MAX+1]; 51948c2ecf20Sopenharmony_ci struct ifaddrmsg *ifm; 51958c2ecf20Sopenharmony_ci int err, i; 51968c2ecf20Sopenharmony_ci 51978c2ecf20Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 51988c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for address dump request"); 51998c2ecf20Sopenharmony_ci return -EINVAL; 52008c2ecf20Sopenharmony_ci } 52018c2ecf20Sopenharmony_ci 52028c2ecf20Sopenharmony_ci ifm = nlmsg_data(nlh); 52038c2ecf20Sopenharmony_ci if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) { 52048c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for address dump request"); 52058c2ecf20Sopenharmony_ci return -EINVAL; 52068c2ecf20Sopenharmony_ci } 52078c2ecf20Sopenharmony_ci 52088c2ecf20Sopenharmony_ci fillargs->ifindex = ifm->ifa_index; 52098c2ecf20Sopenharmony_ci if (fillargs->ifindex) { 52108c2ecf20Sopenharmony_ci cb->answer_flags |= NLM_F_DUMP_FILTERED; 52118c2ecf20Sopenharmony_ci fillargs->flags |= NLM_F_DUMP_FILTERED; 52128c2ecf20Sopenharmony_ci } 52138c2ecf20Sopenharmony_ci 52148c2ecf20Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX, 52158c2ecf20Sopenharmony_ci ifa_ipv6_policy, extack); 52168c2ecf20Sopenharmony_ci if (err < 0) 52178c2ecf20Sopenharmony_ci return err; 52188c2ecf20Sopenharmony_ci 52198c2ecf20Sopenharmony_ci for (i = 0; i <= IFA_MAX; ++i) { 52208c2ecf20Sopenharmony_ci if (!tb[i]) 52218c2ecf20Sopenharmony_ci continue; 52228c2ecf20Sopenharmony_ci 52238c2ecf20Sopenharmony_ci if (i == IFA_TARGET_NETNSID) { 52248c2ecf20Sopenharmony_ci struct net *net; 52258c2ecf20Sopenharmony_ci 52268c2ecf20Sopenharmony_ci fillargs->netnsid = nla_get_s32(tb[i]); 52278c2ecf20Sopenharmony_ci net = rtnl_get_net_ns_capable(sk, fillargs->netnsid); 52288c2ecf20Sopenharmony_ci if (IS_ERR(net)) { 52298c2ecf20Sopenharmony_ci fillargs->netnsid = -1; 52308c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid target network namespace id"); 52318c2ecf20Sopenharmony_ci return PTR_ERR(net); 52328c2ecf20Sopenharmony_ci } 52338c2ecf20Sopenharmony_ci *tgt_net = net; 52348c2ecf20Sopenharmony_ci } else { 52358c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in dump request"); 52368c2ecf20Sopenharmony_ci return -EINVAL; 52378c2ecf20Sopenharmony_ci } 52388c2ecf20Sopenharmony_ci } 52398c2ecf20Sopenharmony_ci 52408c2ecf20Sopenharmony_ci return 0; 52418c2ecf20Sopenharmony_ci} 52428c2ecf20Sopenharmony_ci 52438c2ecf20Sopenharmony_cistatic int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, 52448c2ecf20Sopenharmony_ci enum addr_type_t type) 52458c2ecf20Sopenharmony_ci{ 52468c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 52478c2ecf20Sopenharmony_ci struct inet6_fill_args fillargs = { 52488c2ecf20Sopenharmony_ci .portid = NETLINK_CB(cb->skb).portid, 52498c2ecf20Sopenharmony_ci .seq = cb->nlh->nlmsg_seq, 52508c2ecf20Sopenharmony_ci .flags = NLM_F_MULTI, 52518c2ecf20Sopenharmony_ci .netnsid = -1, 52528c2ecf20Sopenharmony_ci .type = type, 52538c2ecf20Sopenharmony_ci }; 52548c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 52558c2ecf20Sopenharmony_ci struct net *tgt_net = net; 52568c2ecf20Sopenharmony_ci int idx, s_idx, s_ip_idx; 52578c2ecf20Sopenharmony_ci int h, s_h; 52588c2ecf20Sopenharmony_ci struct net_device *dev; 52598c2ecf20Sopenharmony_ci struct inet6_dev *idev; 52608c2ecf20Sopenharmony_ci struct hlist_head *head; 52618c2ecf20Sopenharmony_ci int err = 0; 52628c2ecf20Sopenharmony_ci 52638c2ecf20Sopenharmony_ci s_h = cb->args[0]; 52648c2ecf20Sopenharmony_ci s_idx = idx = cb->args[1]; 52658c2ecf20Sopenharmony_ci s_ip_idx = cb->args[2]; 52668c2ecf20Sopenharmony_ci 52678c2ecf20Sopenharmony_ci if (cb->strict_check) { 52688c2ecf20Sopenharmony_ci err = inet6_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net, 52698c2ecf20Sopenharmony_ci skb->sk, cb); 52708c2ecf20Sopenharmony_ci if (err < 0) 52718c2ecf20Sopenharmony_ci goto put_tgt_net; 52728c2ecf20Sopenharmony_ci 52738c2ecf20Sopenharmony_ci err = 0; 52748c2ecf20Sopenharmony_ci if (fillargs.ifindex) { 52758c2ecf20Sopenharmony_ci dev = __dev_get_by_index(tgt_net, fillargs.ifindex); 52768c2ecf20Sopenharmony_ci if (!dev) { 52778c2ecf20Sopenharmony_ci err = -ENODEV; 52788c2ecf20Sopenharmony_ci goto put_tgt_net; 52798c2ecf20Sopenharmony_ci } 52808c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 52818c2ecf20Sopenharmony_ci if (idev) { 52828c2ecf20Sopenharmony_ci err = in6_dump_addrs(idev, skb, cb, s_ip_idx, 52838c2ecf20Sopenharmony_ci &fillargs); 52848c2ecf20Sopenharmony_ci if (err > 0) 52858c2ecf20Sopenharmony_ci err = 0; 52868c2ecf20Sopenharmony_ci } 52878c2ecf20Sopenharmony_ci goto put_tgt_net; 52888c2ecf20Sopenharmony_ci } 52898c2ecf20Sopenharmony_ci } 52908c2ecf20Sopenharmony_ci 52918c2ecf20Sopenharmony_ci rcu_read_lock(); 52928c2ecf20Sopenharmony_ci cb->seq = atomic_read(&tgt_net->ipv6.dev_addr_genid) ^ tgt_net->dev_base_seq; 52938c2ecf20Sopenharmony_ci for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 52948c2ecf20Sopenharmony_ci idx = 0; 52958c2ecf20Sopenharmony_ci head = &tgt_net->dev_index_head[h]; 52968c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(dev, head, index_hlist) { 52978c2ecf20Sopenharmony_ci if (idx < s_idx) 52988c2ecf20Sopenharmony_ci goto cont; 52998c2ecf20Sopenharmony_ci if (h > s_h || idx > s_idx) 53008c2ecf20Sopenharmony_ci s_ip_idx = 0; 53018c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 53028c2ecf20Sopenharmony_ci if (!idev) 53038c2ecf20Sopenharmony_ci goto cont; 53048c2ecf20Sopenharmony_ci 53058c2ecf20Sopenharmony_ci if (in6_dump_addrs(idev, skb, cb, s_ip_idx, 53068c2ecf20Sopenharmony_ci &fillargs) < 0) 53078c2ecf20Sopenharmony_ci goto done; 53088c2ecf20Sopenharmony_cicont: 53098c2ecf20Sopenharmony_ci idx++; 53108c2ecf20Sopenharmony_ci } 53118c2ecf20Sopenharmony_ci } 53128c2ecf20Sopenharmony_cidone: 53138c2ecf20Sopenharmony_ci rcu_read_unlock(); 53148c2ecf20Sopenharmony_ci cb->args[0] = h; 53158c2ecf20Sopenharmony_ci cb->args[1] = idx; 53168c2ecf20Sopenharmony_ciput_tgt_net: 53178c2ecf20Sopenharmony_ci if (fillargs.netnsid >= 0) 53188c2ecf20Sopenharmony_ci put_net(tgt_net); 53198c2ecf20Sopenharmony_ci 53208c2ecf20Sopenharmony_ci return skb->len ? : err; 53218c2ecf20Sopenharmony_ci} 53228c2ecf20Sopenharmony_ci 53238c2ecf20Sopenharmony_cistatic int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 53248c2ecf20Sopenharmony_ci{ 53258c2ecf20Sopenharmony_ci enum addr_type_t type = UNICAST_ADDR; 53268c2ecf20Sopenharmony_ci 53278c2ecf20Sopenharmony_ci return inet6_dump_addr(skb, cb, type); 53288c2ecf20Sopenharmony_ci} 53298c2ecf20Sopenharmony_ci 53308c2ecf20Sopenharmony_cistatic int inet6_dump_ifmcaddr(struct sk_buff *skb, struct netlink_callback *cb) 53318c2ecf20Sopenharmony_ci{ 53328c2ecf20Sopenharmony_ci enum addr_type_t type = MULTICAST_ADDR; 53338c2ecf20Sopenharmony_ci 53348c2ecf20Sopenharmony_ci return inet6_dump_addr(skb, cb, type); 53358c2ecf20Sopenharmony_ci} 53368c2ecf20Sopenharmony_ci 53378c2ecf20Sopenharmony_ci 53388c2ecf20Sopenharmony_cistatic int inet6_dump_ifacaddr(struct sk_buff *skb, struct netlink_callback *cb) 53398c2ecf20Sopenharmony_ci{ 53408c2ecf20Sopenharmony_ci enum addr_type_t type = ANYCAST_ADDR; 53418c2ecf20Sopenharmony_ci 53428c2ecf20Sopenharmony_ci return inet6_dump_addr(skb, cb, type); 53438c2ecf20Sopenharmony_ci} 53448c2ecf20Sopenharmony_ci 53458c2ecf20Sopenharmony_cistatic int inet6_rtm_valid_getaddr_req(struct sk_buff *skb, 53468c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh, 53478c2ecf20Sopenharmony_ci struct nlattr **tb, 53488c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 53498c2ecf20Sopenharmony_ci{ 53508c2ecf20Sopenharmony_ci struct ifaddrmsg *ifm; 53518c2ecf20Sopenharmony_ci int i, err; 53528c2ecf20Sopenharmony_ci 53538c2ecf20Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 53548c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for get address request"); 53558c2ecf20Sopenharmony_ci return -EINVAL; 53568c2ecf20Sopenharmony_ci } 53578c2ecf20Sopenharmony_ci 53588c2ecf20Sopenharmony_ci if (!netlink_strict_get_check(skb)) 53598c2ecf20Sopenharmony_ci return nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 53608c2ecf20Sopenharmony_ci ifa_ipv6_policy, extack); 53618c2ecf20Sopenharmony_ci 53628c2ecf20Sopenharmony_ci ifm = nlmsg_data(nlh); 53638c2ecf20Sopenharmony_ci if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) { 53648c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get address request"); 53658c2ecf20Sopenharmony_ci return -EINVAL; 53668c2ecf20Sopenharmony_ci } 53678c2ecf20Sopenharmony_ci 53688c2ecf20Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX, 53698c2ecf20Sopenharmony_ci ifa_ipv6_policy, extack); 53708c2ecf20Sopenharmony_ci if (err) 53718c2ecf20Sopenharmony_ci return err; 53728c2ecf20Sopenharmony_ci 53738c2ecf20Sopenharmony_ci for (i = 0; i <= IFA_MAX; i++) { 53748c2ecf20Sopenharmony_ci if (!tb[i]) 53758c2ecf20Sopenharmony_ci continue; 53768c2ecf20Sopenharmony_ci 53778c2ecf20Sopenharmony_ci switch (i) { 53788c2ecf20Sopenharmony_ci case IFA_TARGET_NETNSID: 53798c2ecf20Sopenharmony_ci case IFA_ADDRESS: 53808c2ecf20Sopenharmony_ci case IFA_LOCAL: 53818c2ecf20Sopenharmony_ci break; 53828c2ecf20Sopenharmony_ci default: 53838c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get address request"); 53848c2ecf20Sopenharmony_ci return -EINVAL; 53858c2ecf20Sopenharmony_ci } 53868c2ecf20Sopenharmony_ci } 53878c2ecf20Sopenharmony_ci 53888c2ecf20Sopenharmony_ci return 0; 53898c2ecf20Sopenharmony_ci} 53908c2ecf20Sopenharmony_ci 53918c2ecf20Sopenharmony_cistatic int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh, 53928c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 53938c2ecf20Sopenharmony_ci{ 53948c2ecf20Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 53958c2ecf20Sopenharmony_ci struct inet6_fill_args fillargs = { 53968c2ecf20Sopenharmony_ci .portid = NETLINK_CB(in_skb).portid, 53978c2ecf20Sopenharmony_ci .seq = nlh->nlmsg_seq, 53988c2ecf20Sopenharmony_ci .event = RTM_NEWADDR, 53998c2ecf20Sopenharmony_ci .flags = 0, 54008c2ecf20Sopenharmony_ci .netnsid = -1, 54018c2ecf20Sopenharmony_ci }; 54028c2ecf20Sopenharmony_ci struct net *tgt_net = net; 54038c2ecf20Sopenharmony_ci struct ifaddrmsg *ifm; 54048c2ecf20Sopenharmony_ci struct nlattr *tb[IFA_MAX+1]; 54058c2ecf20Sopenharmony_ci struct in6_addr *addr = NULL, *peer; 54068c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 54078c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa; 54088c2ecf20Sopenharmony_ci struct sk_buff *skb; 54098c2ecf20Sopenharmony_ci int err; 54108c2ecf20Sopenharmony_ci 54118c2ecf20Sopenharmony_ci err = inet6_rtm_valid_getaddr_req(in_skb, nlh, tb, extack); 54128c2ecf20Sopenharmony_ci if (err < 0) 54138c2ecf20Sopenharmony_ci return err; 54148c2ecf20Sopenharmony_ci 54158c2ecf20Sopenharmony_ci if (tb[IFA_TARGET_NETNSID]) { 54168c2ecf20Sopenharmony_ci fillargs.netnsid = nla_get_s32(tb[IFA_TARGET_NETNSID]); 54178c2ecf20Sopenharmony_ci 54188c2ecf20Sopenharmony_ci tgt_net = rtnl_get_net_ns_capable(NETLINK_CB(in_skb).sk, 54198c2ecf20Sopenharmony_ci fillargs.netnsid); 54208c2ecf20Sopenharmony_ci if (IS_ERR(tgt_net)) 54218c2ecf20Sopenharmony_ci return PTR_ERR(tgt_net); 54228c2ecf20Sopenharmony_ci } 54238c2ecf20Sopenharmony_ci 54248c2ecf20Sopenharmony_ci addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer); 54258c2ecf20Sopenharmony_ci if (!addr) { 54268c2ecf20Sopenharmony_ci err = -EINVAL; 54278c2ecf20Sopenharmony_ci goto errout; 54288c2ecf20Sopenharmony_ci } 54298c2ecf20Sopenharmony_ci ifm = nlmsg_data(nlh); 54308c2ecf20Sopenharmony_ci if (ifm->ifa_index) 54318c2ecf20Sopenharmony_ci dev = dev_get_by_index(tgt_net, ifm->ifa_index); 54328c2ecf20Sopenharmony_ci 54338c2ecf20Sopenharmony_ci ifa = ipv6_get_ifaddr(tgt_net, addr, dev, 1); 54348c2ecf20Sopenharmony_ci if (!ifa) { 54358c2ecf20Sopenharmony_ci err = -EADDRNOTAVAIL; 54368c2ecf20Sopenharmony_ci goto errout; 54378c2ecf20Sopenharmony_ci } 54388c2ecf20Sopenharmony_ci 54398c2ecf20Sopenharmony_ci skb = nlmsg_new(inet6_ifaddr_msgsize(), GFP_KERNEL); 54408c2ecf20Sopenharmony_ci if (!skb) { 54418c2ecf20Sopenharmony_ci err = -ENOBUFS; 54428c2ecf20Sopenharmony_ci goto errout_ifa; 54438c2ecf20Sopenharmony_ci } 54448c2ecf20Sopenharmony_ci 54458c2ecf20Sopenharmony_ci err = inet6_fill_ifaddr(skb, ifa, &fillargs); 54468c2ecf20Sopenharmony_ci if (err < 0) { 54478c2ecf20Sopenharmony_ci /* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */ 54488c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 54498c2ecf20Sopenharmony_ci kfree_skb(skb); 54508c2ecf20Sopenharmony_ci goto errout_ifa; 54518c2ecf20Sopenharmony_ci } 54528c2ecf20Sopenharmony_ci err = rtnl_unicast(skb, tgt_net, NETLINK_CB(in_skb).portid); 54538c2ecf20Sopenharmony_cierrout_ifa: 54548c2ecf20Sopenharmony_ci in6_ifa_put(ifa); 54558c2ecf20Sopenharmony_cierrout: 54568c2ecf20Sopenharmony_ci if (dev) 54578c2ecf20Sopenharmony_ci dev_put(dev); 54588c2ecf20Sopenharmony_ci if (fillargs.netnsid >= 0) 54598c2ecf20Sopenharmony_ci put_net(tgt_net); 54608c2ecf20Sopenharmony_ci 54618c2ecf20Sopenharmony_ci return err; 54628c2ecf20Sopenharmony_ci} 54638c2ecf20Sopenharmony_ci 54648c2ecf20Sopenharmony_cistatic void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa) 54658c2ecf20Sopenharmony_ci{ 54668c2ecf20Sopenharmony_ci struct sk_buff *skb; 54678c2ecf20Sopenharmony_ci struct net *net = dev_net(ifa->idev->dev); 54688c2ecf20Sopenharmony_ci struct inet6_fill_args fillargs = { 54698c2ecf20Sopenharmony_ci .portid = 0, 54708c2ecf20Sopenharmony_ci .seq = 0, 54718c2ecf20Sopenharmony_ci .event = event, 54728c2ecf20Sopenharmony_ci .flags = 0, 54738c2ecf20Sopenharmony_ci .netnsid = -1, 54748c2ecf20Sopenharmony_ci }; 54758c2ecf20Sopenharmony_ci int err = -ENOBUFS; 54768c2ecf20Sopenharmony_ci 54778c2ecf20Sopenharmony_ci skb = nlmsg_new(inet6_ifaddr_msgsize(), GFP_ATOMIC); 54788c2ecf20Sopenharmony_ci if (!skb) 54798c2ecf20Sopenharmony_ci goto errout; 54808c2ecf20Sopenharmony_ci 54818c2ecf20Sopenharmony_ci err = inet6_fill_ifaddr(skb, ifa, &fillargs); 54828c2ecf20Sopenharmony_ci if (err < 0) { 54838c2ecf20Sopenharmony_ci /* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */ 54848c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 54858c2ecf20Sopenharmony_ci kfree_skb(skb); 54868c2ecf20Sopenharmony_ci goto errout; 54878c2ecf20Sopenharmony_ci } 54888c2ecf20Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC); 54898c2ecf20Sopenharmony_ci return; 54908c2ecf20Sopenharmony_cierrout: 54918c2ecf20Sopenharmony_ci if (err < 0) 54928c2ecf20Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_IFADDR, err); 54938c2ecf20Sopenharmony_ci} 54948c2ecf20Sopenharmony_ci 54958c2ecf20Sopenharmony_cistatic inline void ipv6_store_devconf(struct ipv6_devconf *cnf, 54968c2ecf20Sopenharmony_ci __s32 *array, int bytes) 54978c2ecf20Sopenharmony_ci{ 54988c2ecf20Sopenharmony_ci BUG_ON(bytes < (DEVCONF_MAX * 4)); 54998c2ecf20Sopenharmony_ci 55008c2ecf20Sopenharmony_ci memset(array, 0, bytes); 55018c2ecf20Sopenharmony_ci array[DEVCONF_FORWARDING] = cnf->forwarding; 55028c2ecf20Sopenharmony_ci array[DEVCONF_HOPLIMIT] = cnf->hop_limit; 55038c2ecf20Sopenharmony_ci array[DEVCONF_MTU6] = cnf->mtu6; 55048c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_RA] = cnf->accept_ra; 55058c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_REDIRECTS] = cnf->accept_redirects; 55068c2ecf20Sopenharmony_ci array[DEVCONF_AUTOCONF] = cnf->autoconf; 55078c2ecf20Sopenharmony_ci array[DEVCONF_DAD_TRANSMITS] = cnf->dad_transmits; 55088c2ecf20Sopenharmony_ci array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits; 55098c2ecf20Sopenharmony_ci array[DEVCONF_RTR_SOLICIT_INTERVAL] = 55108c2ecf20Sopenharmony_ci jiffies_to_msecs(cnf->rtr_solicit_interval); 55118c2ecf20Sopenharmony_ci array[DEVCONF_RTR_SOLICIT_MAX_INTERVAL] = 55128c2ecf20Sopenharmony_ci jiffies_to_msecs(cnf->rtr_solicit_max_interval); 55138c2ecf20Sopenharmony_ci array[DEVCONF_RTR_SOLICIT_DELAY] = 55148c2ecf20Sopenharmony_ci jiffies_to_msecs(cnf->rtr_solicit_delay); 55158c2ecf20Sopenharmony_ci array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version; 55168c2ecf20Sopenharmony_ci array[DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL] = 55178c2ecf20Sopenharmony_ci jiffies_to_msecs(cnf->mldv1_unsolicited_report_interval); 55188c2ecf20Sopenharmony_ci array[DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL] = 55198c2ecf20Sopenharmony_ci jiffies_to_msecs(cnf->mldv2_unsolicited_report_interval); 55208c2ecf20Sopenharmony_ci array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr; 55218c2ecf20Sopenharmony_ci array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft; 55228c2ecf20Sopenharmony_ci array[DEVCONF_TEMP_PREFERED_LFT] = cnf->temp_prefered_lft; 55238c2ecf20Sopenharmony_ci array[DEVCONF_REGEN_MAX_RETRY] = cnf->regen_max_retry; 55248c2ecf20Sopenharmony_ci array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor; 55258c2ecf20Sopenharmony_ci array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses; 55268c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr; 55278c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT] = cnf->accept_ra_min_hop_limit; 55288c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo; 55298c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 55308c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_RA_RTR_PREF] = cnf->accept_ra_rtr_pref; 55318c2ecf20Sopenharmony_ci array[DEVCONF_RTR_PROBE_INTERVAL] = 55328c2ecf20Sopenharmony_ci jiffies_to_msecs(cnf->rtr_probe_interval); 55338c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 55348c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN] = cnf->accept_ra_rt_info_min_plen; 55358c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen; 55368c2ecf20Sopenharmony_ci#endif 55378c2ecf20Sopenharmony_ci#endif 55388c2ecf20Sopenharmony_ci array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp; 55398c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route; 55408c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 55418c2ecf20Sopenharmony_ci array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad; 55428c2ecf20Sopenharmony_ci array[DEVCONF_USE_OPTIMISTIC] = cnf->use_optimistic; 55438c2ecf20Sopenharmony_ci#endif 55448c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 55458c2ecf20Sopenharmony_ci array[DEVCONF_MC_FORWARDING] = atomic_read(&cnf->mc_forwarding); 55468c2ecf20Sopenharmony_ci#endif 55478c2ecf20Sopenharmony_ci array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6; 55488c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad; 55498c2ecf20Sopenharmony_ci array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao; 55508c2ecf20Sopenharmony_ci array[DEVCONF_NDISC_NOTIFY] = cnf->ndisc_notify; 55518c2ecf20Sopenharmony_ci array[DEVCONF_SUPPRESS_FRAG_NDISC] = cnf->suppress_frag_ndisc; 55528c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_RA_FROM_LOCAL] = cnf->accept_ra_from_local; 55538c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_RA_MTU] = cnf->accept_ra_mtu; 55548c2ecf20Sopenharmony_ci array[DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN] = cnf->ignore_routes_with_linkdown; 55558c2ecf20Sopenharmony_ci /* we omit DEVCONF_STABLE_SECRET for now */ 55568c2ecf20Sopenharmony_ci array[DEVCONF_USE_OIF_ADDRS_ONLY] = cnf->use_oif_addrs_only; 55578c2ecf20Sopenharmony_ci array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = cnf->drop_unicast_in_l2_multicast; 55588c2ecf20Sopenharmony_ci array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na; 55598c2ecf20Sopenharmony_ci array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down; 55608c2ecf20Sopenharmony_ci array[DEVCONF_SEG6_ENABLED] = cnf->seg6_enabled; 55618c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 55628c2ecf20Sopenharmony_ci array[DEVCONF_SEG6_REQUIRE_HMAC] = cnf->seg6_require_hmac; 55638c2ecf20Sopenharmony_ci#endif 55648c2ecf20Sopenharmony_ci array[DEVCONF_ENHANCED_DAD] = cnf->enhanced_dad; 55658c2ecf20Sopenharmony_ci array[DEVCONF_ADDR_GEN_MODE] = cnf->addr_gen_mode; 55668c2ecf20Sopenharmony_ci array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy; 55678c2ecf20Sopenharmony_ci array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass; 55688c2ecf20Sopenharmony_ci array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled; 55698c2ecf20Sopenharmony_ci array[DEVCONF_ACCEPT_RA_MIN_LFT] = cnf->accept_ra_min_lft; 55708c2ecf20Sopenharmony_ci} 55718c2ecf20Sopenharmony_ci 55728c2ecf20Sopenharmony_cistatic inline size_t inet6_ifla6_size(void) 55738c2ecf20Sopenharmony_ci{ 55748c2ecf20Sopenharmony_ci return nla_total_size(4) /* IFLA_INET6_FLAGS */ 55758c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct ifla_cacheinfo)) 55768c2ecf20Sopenharmony_ci + nla_total_size(DEVCONF_MAX * 4) /* IFLA_INET6_CONF */ 55778c2ecf20Sopenharmony_ci + nla_total_size(IPSTATS_MIB_MAX * 8) /* IFLA_INET6_STATS */ 55788c2ecf20Sopenharmony_ci + nla_total_size(ICMP6_MIB_MAX * 8) /* IFLA_INET6_ICMP6STATS */ 55798c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct in6_addr)) /* IFLA_INET6_TOKEN */ 55808c2ecf20Sopenharmony_ci + nla_total_size(1) /* IFLA_INET6_ADDR_GEN_MODE */ 55818c2ecf20Sopenharmony_ci + 0; 55828c2ecf20Sopenharmony_ci} 55838c2ecf20Sopenharmony_ci 55848c2ecf20Sopenharmony_cistatic inline size_t inet6_if_nlmsg_size(void) 55858c2ecf20Sopenharmony_ci{ 55868c2ecf20Sopenharmony_ci return NLMSG_ALIGN(sizeof(struct ifinfomsg)) 55878c2ecf20Sopenharmony_ci + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ 55888c2ecf20Sopenharmony_ci + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ 55898c2ecf20Sopenharmony_ci + nla_total_size(4) /* IFLA_MTU */ 55908c2ecf20Sopenharmony_ci + nla_total_size(4) /* IFLA_LINK */ 55918c2ecf20Sopenharmony_ci + nla_total_size(1) /* IFLA_OPERSTATE */ 55928c2ecf20Sopenharmony_ci + nla_total_size(inet6_ifla6_size()); /* IFLA_PROTINFO */ 55938c2ecf20Sopenharmony_ci} 55948c2ecf20Sopenharmony_ci 55958c2ecf20Sopenharmony_cistatic inline void __snmp6_fill_statsdev(u64 *stats, atomic_long_t *mib, 55968c2ecf20Sopenharmony_ci int bytes) 55978c2ecf20Sopenharmony_ci{ 55988c2ecf20Sopenharmony_ci int i; 55998c2ecf20Sopenharmony_ci int pad = bytes - sizeof(u64) * ICMP6_MIB_MAX; 56008c2ecf20Sopenharmony_ci BUG_ON(pad < 0); 56018c2ecf20Sopenharmony_ci 56028c2ecf20Sopenharmony_ci /* Use put_unaligned() because stats may not be aligned for u64. */ 56038c2ecf20Sopenharmony_ci put_unaligned(ICMP6_MIB_MAX, &stats[0]); 56048c2ecf20Sopenharmony_ci for (i = 1; i < ICMP6_MIB_MAX; i++) 56058c2ecf20Sopenharmony_ci put_unaligned(atomic_long_read(&mib[i]), &stats[i]); 56068c2ecf20Sopenharmony_ci 56078c2ecf20Sopenharmony_ci memset(&stats[ICMP6_MIB_MAX], 0, pad); 56088c2ecf20Sopenharmony_ci} 56098c2ecf20Sopenharmony_ci 56108c2ecf20Sopenharmony_cistatic inline void __snmp6_fill_stats64(u64 *stats, void __percpu *mib, 56118c2ecf20Sopenharmony_ci int bytes, size_t syncpoff) 56128c2ecf20Sopenharmony_ci{ 56138c2ecf20Sopenharmony_ci int i, c; 56148c2ecf20Sopenharmony_ci u64 buff[IPSTATS_MIB_MAX]; 56158c2ecf20Sopenharmony_ci int pad = bytes - sizeof(u64) * IPSTATS_MIB_MAX; 56168c2ecf20Sopenharmony_ci 56178c2ecf20Sopenharmony_ci BUG_ON(pad < 0); 56188c2ecf20Sopenharmony_ci 56198c2ecf20Sopenharmony_ci memset(buff, 0, sizeof(buff)); 56208c2ecf20Sopenharmony_ci buff[0] = IPSTATS_MIB_MAX; 56218c2ecf20Sopenharmony_ci 56228c2ecf20Sopenharmony_ci for_each_possible_cpu(c) { 56238c2ecf20Sopenharmony_ci for (i = 1; i < IPSTATS_MIB_MAX; i++) 56248c2ecf20Sopenharmony_ci buff[i] += snmp_get_cpu_field64(mib, c, i, syncpoff); 56258c2ecf20Sopenharmony_ci } 56268c2ecf20Sopenharmony_ci 56278c2ecf20Sopenharmony_ci memcpy(stats, buff, IPSTATS_MIB_MAX * sizeof(u64)); 56288c2ecf20Sopenharmony_ci memset(&stats[IPSTATS_MIB_MAX], 0, pad); 56298c2ecf20Sopenharmony_ci} 56308c2ecf20Sopenharmony_ci 56318c2ecf20Sopenharmony_cistatic void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype, 56328c2ecf20Sopenharmony_ci int bytes) 56338c2ecf20Sopenharmony_ci{ 56348c2ecf20Sopenharmony_ci switch (attrtype) { 56358c2ecf20Sopenharmony_ci case IFLA_INET6_STATS: 56368c2ecf20Sopenharmony_ci __snmp6_fill_stats64(stats, idev->stats.ipv6, bytes, 56378c2ecf20Sopenharmony_ci offsetof(struct ipstats_mib, syncp)); 56388c2ecf20Sopenharmony_ci break; 56398c2ecf20Sopenharmony_ci case IFLA_INET6_ICMP6STATS: 56408c2ecf20Sopenharmony_ci __snmp6_fill_statsdev(stats, idev->stats.icmpv6dev->mibs, bytes); 56418c2ecf20Sopenharmony_ci break; 56428c2ecf20Sopenharmony_ci } 56438c2ecf20Sopenharmony_ci} 56448c2ecf20Sopenharmony_ci 56458c2ecf20Sopenharmony_cistatic int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev, 56468c2ecf20Sopenharmony_ci u32 ext_filter_mask) 56478c2ecf20Sopenharmony_ci{ 56488c2ecf20Sopenharmony_ci struct nlattr *nla; 56498c2ecf20Sopenharmony_ci struct ifla_cacheinfo ci; 56508c2ecf20Sopenharmony_ci 56518c2ecf20Sopenharmony_ci if (nla_put_u32(skb, IFLA_INET6_FLAGS, idev->if_flags)) 56528c2ecf20Sopenharmony_ci goto nla_put_failure; 56538c2ecf20Sopenharmony_ci ci.max_reasm_len = IPV6_MAXPLEN; 56548c2ecf20Sopenharmony_ci ci.tstamp = cstamp_delta(idev->tstamp); 56558c2ecf20Sopenharmony_ci ci.reachable_time = jiffies_to_msecs(idev->nd_parms->reachable_time); 56568c2ecf20Sopenharmony_ci ci.retrans_time = jiffies_to_msecs(NEIGH_VAR(idev->nd_parms, RETRANS_TIME)); 56578c2ecf20Sopenharmony_ci if (nla_put(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci)) 56588c2ecf20Sopenharmony_ci goto nla_put_failure; 56598c2ecf20Sopenharmony_ci nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32)); 56608c2ecf20Sopenharmony_ci if (!nla) 56618c2ecf20Sopenharmony_ci goto nla_put_failure; 56628c2ecf20Sopenharmony_ci ipv6_store_devconf(&idev->cnf, nla_data(nla), nla_len(nla)); 56638c2ecf20Sopenharmony_ci 56648c2ecf20Sopenharmony_ci /* XXX - MC not implemented */ 56658c2ecf20Sopenharmony_ci 56668c2ecf20Sopenharmony_ci if (ext_filter_mask & RTEXT_FILTER_SKIP_STATS) 56678c2ecf20Sopenharmony_ci return 0; 56688c2ecf20Sopenharmony_ci 56698c2ecf20Sopenharmony_ci nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64)); 56708c2ecf20Sopenharmony_ci if (!nla) 56718c2ecf20Sopenharmony_ci goto nla_put_failure; 56728c2ecf20Sopenharmony_ci snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_STATS, nla_len(nla)); 56738c2ecf20Sopenharmony_ci 56748c2ecf20Sopenharmony_ci nla = nla_reserve(skb, IFLA_INET6_ICMP6STATS, ICMP6_MIB_MAX * sizeof(u64)); 56758c2ecf20Sopenharmony_ci if (!nla) 56768c2ecf20Sopenharmony_ci goto nla_put_failure; 56778c2ecf20Sopenharmony_ci snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla)); 56788c2ecf20Sopenharmony_ci 56798c2ecf20Sopenharmony_ci nla = nla_reserve(skb, IFLA_INET6_TOKEN, sizeof(struct in6_addr)); 56808c2ecf20Sopenharmony_ci if (!nla) 56818c2ecf20Sopenharmony_ci goto nla_put_failure; 56828c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 56838c2ecf20Sopenharmony_ci memcpy(nla_data(nla), idev->token.s6_addr, nla_len(nla)); 56848c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 56858c2ecf20Sopenharmony_ci 56868c2ecf20Sopenharmony_ci if (nla_put_u8(skb, IFLA_INET6_ADDR_GEN_MODE, idev->cnf.addr_gen_mode)) 56878c2ecf20Sopenharmony_ci goto nla_put_failure; 56888c2ecf20Sopenharmony_ci 56898c2ecf20Sopenharmony_ci return 0; 56908c2ecf20Sopenharmony_ci 56918c2ecf20Sopenharmony_cinla_put_failure: 56928c2ecf20Sopenharmony_ci return -EMSGSIZE; 56938c2ecf20Sopenharmony_ci} 56948c2ecf20Sopenharmony_ci 56958c2ecf20Sopenharmony_cistatic size_t inet6_get_link_af_size(const struct net_device *dev, 56968c2ecf20Sopenharmony_ci u32 ext_filter_mask) 56978c2ecf20Sopenharmony_ci{ 56988c2ecf20Sopenharmony_ci if (!__in6_dev_get(dev)) 56998c2ecf20Sopenharmony_ci return 0; 57008c2ecf20Sopenharmony_ci 57018c2ecf20Sopenharmony_ci return inet6_ifla6_size(); 57028c2ecf20Sopenharmony_ci} 57038c2ecf20Sopenharmony_ci 57048c2ecf20Sopenharmony_cistatic int inet6_fill_link_af(struct sk_buff *skb, const struct net_device *dev, 57058c2ecf20Sopenharmony_ci u32 ext_filter_mask) 57068c2ecf20Sopenharmony_ci{ 57078c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 57088c2ecf20Sopenharmony_ci 57098c2ecf20Sopenharmony_ci if (!idev) 57108c2ecf20Sopenharmony_ci return -ENODATA; 57118c2ecf20Sopenharmony_ci 57128c2ecf20Sopenharmony_ci if (inet6_fill_ifla6_attrs(skb, idev, ext_filter_mask) < 0) 57138c2ecf20Sopenharmony_ci return -EMSGSIZE; 57148c2ecf20Sopenharmony_ci 57158c2ecf20Sopenharmony_ci return 0; 57168c2ecf20Sopenharmony_ci} 57178c2ecf20Sopenharmony_ci 57188c2ecf20Sopenharmony_cistatic int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token) 57198c2ecf20Sopenharmony_ci{ 57208c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifp; 57218c2ecf20Sopenharmony_ci struct net_device *dev = idev->dev; 57228c2ecf20Sopenharmony_ci bool clear_token, update_rs = false; 57238c2ecf20Sopenharmony_ci struct in6_addr ll_addr; 57248c2ecf20Sopenharmony_ci 57258c2ecf20Sopenharmony_ci ASSERT_RTNL(); 57268c2ecf20Sopenharmony_ci 57278c2ecf20Sopenharmony_ci if (!token) 57288c2ecf20Sopenharmony_ci return -EINVAL; 57298c2ecf20Sopenharmony_ci if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) 57308c2ecf20Sopenharmony_ci return -EINVAL; 57318c2ecf20Sopenharmony_ci if (!ipv6_accept_ra(idev)) 57328c2ecf20Sopenharmony_ci return -EINVAL; 57338c2ecf20Sopenharmony_ci if (idev->cnf.rtr_solicits == 0) 57348c2ecf20Sopenharmony_ci return -EINVAL; 57358c2ecf20Sopenharmony_ci 57368c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 57378c2ecf20Sopenharmony_ci 57388c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(token->s6_addr) != 16); 57398c2ecf20Sopenharmony_ci memcpy(idev->token.s6_addr + 8, token->s6_addr + 8, 8); 57408c2ecf20Sopenharmony_ci 57418c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 57428c2ecf20Sopenharmony_ci 57438c2ecf20Sopenharmony_ci clear_token = ipv6_addr_any(token); 57448c2ecf20Sopenharmony_ci if (clear_token) 57458c2ecf20Sopenharmony_ci goto update_lft; 57468c2ecf20Sopenharmony_ci 57478c2ecf20Sopenharmony_ci if (!idev->dead && (idev->if_flags & IF_READY) && 57488c2ecf20Sopenharmony_ci !ipv6_get_lladdr(dev, &ll_addr, IFA_F_TENTATIVE | 57498c2ecf20Sopenharmony_ci IFA_F_OPTIMISTIC)) { 57508c2ecf20Sopenharmony_ci /* If we're not ready, then normal ifup will take care 57518c2ecf20Sopenharmony_ci * of this. Otherwise, we need to request our rs here. 57528c2ecf20Sopenharmony_ci */ 57538c2ecf20Sopenharmony_ci ndisc_send_rs(dev, &ll_addr, &in6addr_linklocal_allrouters); 57548c2ecf20Sopenharmony_ci update_rs = true; 57558c2ecf20Sopenharmony_ci } 57568c2ecf20Sopenharmony_ci 57578c2ecf20Sopenharmony_ciupdate_lft: 57588c2ecf20Sopenharmony_ci write_lock_bh(&idev->lock); 57598c2ecf20Sopenharmony_ci 57608c2ecf20Sopenharmony_ci if (update_rs) { 57618c2ecf20Sopenharmony_ci idev->if_flags |= IF_RS_SENT; 57628c2ecf20Sopenharmony_ci idev->rs_interval = rfc3315_s14_backoff_init( 57638c2ecf20Sopenharmony_ci idev->cnf.rtr_solicit_interval); 57648c2ecf20Sopenharmony_ci idev->rs_probes = 1; 57658c2ecf20Sopenharmony_ci addrconf_mod_rs_timer(idev, idev->rs_interval); 57668c2ecf20Sopenharmony_ci } 57678c2ecf20Sopenharmony_ci 57688c2ecf20Sopenharmony_ci /* Well, that's kinda nasty ... */ 57698c2ecf20Sopenharmony_ci list_for_each_entry(ifp, &idev->addr_list, if_list) { 57708c2ecf20Sopenharmony_ci spin_lock(&ifp->lock); 57718c2ecf20Sopenharmony_ci if (ifp->tokenized) { 57728c2ecf20Sopenharmony_ci ifp->valid_lft = 0; 57738c2ecf20Sopenharmony_ci ifp->prefered_lft = 0; 57748c2ecf20Sopenharmony_ci } 57758c2ecf20Sopenharmony_ci spin_unlock(&ifp->lock); 57768c2ecf20Sopenharmony_ci } 57778c2ecf20Sopenharmony_ci 57788c2ecf20Sopenharmony_ci write_unlock_bh(&idev->lock); 57798c2ecf20Sopenharmony_ci inet6_ifinfo_notify(RTM_NEWLINK, idev); 57808c2ecf20Sopenharmony_ci addrconf_verify_rtnl(); 57818c2ecf20Sopenharmony_ci return 0; 57828c2ecf20Sopenharmony_ci} 57838c2ecf20Sopenharmony_ci 57848c2ecf20Sopenharmony_cistatic const struct nla_policy inet6_af_policy[IFLA_INET6_MAX + 1] = { 57858c2ecf20Sopenharmony_ci [IFLA_INET6_ADDR_GEN_MODE] = { .type = NLA_U8 }, 57868c2ecf20Sopenharmony_ci [IFLA_INET6_TOKEN] = { .len = sizeof(struct in6_addr) }, 57878c2ecf20Sopenharmony_ci}; 57888c2ecf20Sopenharmony_ci 57898c2ecf20Sopenharmony_cistatic int check_addr_gen_mode(int mode) 57908c2ecf20Sopenharmony_ci{ 57918c2ecf20Sopenharmony_ci if (mode != IN6_ADDR_GEN_MODE_EUI64 && 57928c2ecf20Sopenharmony_ci mode != IN6_ADDR_GEN_MODE_NONE && 57938c2ecf20Sopenharmony_ci mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY && 57948c2ecf20Sopenharmony_ci mode != IN6_ADDR_GEN_MODE_RANDOM) 57958c2ecf20Sopenharmony_ci return -EINVAL; 57968c2ecf20Sopenharmony_ci return 1; 57978c2ecf20Sopenharmony_ci} 57988c2ecf20Sopenharmony_ci 57998c2ecf20Sopenharmony_cistatic int check_stable_privacy(struct inet6_dev *idev, struct net *net, 58008c2ecf20Sopenharmony_ci int mode) 58018c2ecf20Sopenharmony_ci{ 58028c2ecf20Sopenharmony_ci if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY && 58038c2ecf20Sopenharmony_ci !idev->cnf.stable_secret.initialized && 58048c2ecf20Sopenharmony_ci !net->ipv6.devconf_dflt->stable_secret.initialized) 58058c2ecf20Sopenharmony_ci return -EINVAL; 58068c2ecf20Sopenharmony_ci return 1; 58078c2ecf20Sopenharmony_ci} 58088c2ecf20Sopenharmony_ci 58098c2ecf20Sopenharmony_cistatic int inet6_validate_link_af(const struct net_device *dev, 58108c2ecf20Sopenharmony_ci const struct nlattr *nla) 58118c2ecf20Sopenharmony_ci{ 58128c2ecf20Sopenharmony_ci struct nlattr *tb[IFLA_INET6_MAX + 1]; 58138c2ecf20Sopenharmony_ci struct inet6_dev *idev = NULL; 58148c2ecf20Sopenharmony_ci int err; 58158c2ecf20Sopenharmony_ci 58168c2ecf20Sopenharmony_ci if (dev) { 58178c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 58188c2ecf20Sopenharmony_ci if (!idev) 58198c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 58208c2ecf20Sopenharmony_ci } 58218c2ecf20Sopenharmony_ci 58228c2ecf20Sopenharmony_ci err = nla_parse_nested_deprecated(tb, IFLA_INET6_MAX, nla, 58238c2ecf20Sopenharmony_ci inet6_af_policy, NULL); 58248c2ecf20Sopenharmony_ci if (err) 58258c2ecf20Sopenharmony_ci return err; 58268c2ecf20Sopenharmony_ci 58278c2ecf20Sopenharmony_ci if (!tb[IFLA_INET6_TOKEN] && !tb[IFLA_INET6_ADDR_GEN_MODE]) 58288c2ecf20Sopenharmony_ci return -EINVAL; 58298c2ecf20Sopenharmony_ci 58308c2ecf20Sopenharmony_ci if (tb[IFLA_INET6_ADDR_GEN_MODE]) { 58318c2ecf20Sopenharmony_ci u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]); 58328c2ecf20Sopenharmony_ci 58338c2ecf20Sopenharmony_ci if (check_addr_gen_mode(mode) < 0) 58348c2ecf20Sopenharmony_ci return -EINVAL; 58358c2ecf20Sopenharmony_ci if (dev && check_stable_privacy(idev, dev_net(dev), mode) < 0) 58368c2ecf20Sopenharmony_ci return -EINVAL; 58378c2ecf20Sopenharmony_ci } 58388c2ecf20Sopenharmony_ci 58398c2ecf20Sopenharmony_ci return 0; 58408c2ecf20Sopenharmony_ci} 58418c2ecf20Sopenharmony_ci 58428c2ecf20Sopenharmony_cistatic int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla) 58438c2ecf20Sopenharmony_ci{ 58448c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 58458c2ecf20Sopenharmony_ci struct nlattr *tb[IFLA_INET6_MAX + 1]; 58468c2ecf20Sopenharmony_ci int err; 58478c2ecf20Sopenharmony_ci 58488c2ecf20Sopenharmony_ci if (!idev) 58498c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 58508c2ecf20Sopenharmony_ci 58518c2ecf20Sopenharmony_ci if (nla_parse_nested_deprecated(tb, IFLA_INET6_MAX, nla, NULL, NULL) < 0) 58528c2ecf20Sopenharmony_ci return -EINVAL; 58538c2ecf20Sopenharmony_ci 58548c2ecf20Sopenharmony_ci if (tb[IFLA_INET6_TOKEN]) { 58558c2ecf20Sopenharmony_ci err = inet6_set_iftoken(idev, nla_data(tb[IFLA_INET6_TOKEN])); 58568c2ecf20Sopenharmony_ci if (err) 58578c2ecf20Sopenharmony_ci return err; 58588c2ecf20Sopenharmony_ci } 58598c2ecf20Sopenharmony_ci 58608c2ecf20Sopenharmony_ci if (tb[IFLA_INET6_ADDR_GEN_MODE]) { 58618c2ecf20Sopenharmony_ci u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]); 58628c2ecf20Sopenharmony_ci 58638c2ecf20Sopenharmony_ci idev->cnf.addr_gen_mode = mode; 58648c2ecf20Sopenharmony_ci } 58658c2ecf20Sopenharmony_ci 58668c2ecf20Sopenharmony_ci return 0; 58678c2ecf20Sopenharmony_ci} 58688c2ecf20Sopenharmony_ci 58698c2ecf20Sopenharmony_cistatic int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev, 58708c2ecf20Sopenharmony_ci u32 portid, u32 seq, int event, unsigned int flags) 58718c2ecf20Sopenharmony_ci{ 58728c2ecf20Sopenharmony_ci struct net_device *dev = idev->dev; 58738c2ecf20Sopenharmony_ci struct ifinfomsg *hdr; 58748c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 58758c2ecf20Sopenharmony_ci void *protoinfo; 58768c2ecf20Sopenharmony_ci 58778c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(*hdr), flags); 58788c2ecf20Sopenharmony_ci if (!nlh) 58798c2ecf20Sopenharmony_ci return -EMSGSIZE; 58808c2ecf20Sopenharmony_ci 58818c2ecf20Sopenharmony_ci hdr = nlmsg_data(nlh); 58828c2ecf20Sopenharmony_ci hdr->ifi_family = AF_INET6; 58838c2ecf20Sopenharmony_ci hdr->__ifi_pad = 0; 58848c2ecf20Sopenharmony_ci hdr->ifi_type = dev->type; 58858c2ecf20Sopenharmony_ci hdr->ifi_index = dev->ifindex; 58868c2ecf20Sopenharmony_ci hdr->ifi_flags = dev_get_flags(dev); 58878c2ecf20Sopenharmony_ci hdr->ifi_change = 0; 58888c2ecf20Sopenharmony_ci 58898c2ecf20Sopenharmony_ci if (nla_put_string(skb, IFLA_IFNAME, dev->name) || 58908c2ecf20Sopenharmony_ci (dev->addr_len && 58918c2ecf20Sopenharmony_ci nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) || 58928c2ecf20Sopenharmony_ci nla_put_u32(skb, IFLA_MTU, dev->mtu) || 58938c2ecf20Sopenharmony_ci (dev->ifindex != dev_get_iflink(dev) && 58948c2ecf20Sopenharmony_ci nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))) || 58958c2ecf20Sopenharmony_ci nla_put_u8(skb, IFLA_OPERSTATE, 58968c2ecf20Sopenharmony_ci netif_running(dev) ? dev->operstate : IF_OPER_DOWN)) 58978c2ecf20Sopenharmony_ci goto nla_put_failure; 58988c2ecf20Sopenharmony_ci protoinfo = nla_nest_start_noflag(skb, IFLA_PROTINFO); 58998c2ecf20Sopenharmony_ci if (!protoinfo) 59008c2ecf20Sopenharmony_ci goto nla_put_failure; 59018c2ecf20Sopenharmony_ci 59028c2ecf20Sopenharmony_ci if (inet6_fill_ifla6_attrs(skb, idev, 0) < 0) 59038c2ecf20Sopenharmony_ci goto nla_put_failure; 59048c2ecf20Sopenharmony_ci 59058c2ecf20Sopenharmony_ci nla_nest_end(skb, protoinfo); 59068c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 59078c2ecf20Sopenharmony_ci return 0; 59088c2ecf20Sopenharmony_ci 59098c2ecf20Sopenharmony_cinla_put_failure: 59108c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 59118c2ecf20Sopenharmony_ci return -EMSGSIZE; 59128c2ecf20Sopenharmony_ci} 59138c2ecf20Sopenharmony_ci 59148c2ecf20Sopenharmony_cistatic int inet6_valid_dump_ifinfo(const struct nlmsghdr *nlh, 59158c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 59168c2ecf20Sopenharmony_ci{ 59178c2ecf20Sopenharmony_ci struct ifinfomsg *ifm; 59188c2ecf20Sopenharmony_ci 59198c2ecf20Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 59208c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for link dump request"); 59218c2ecf20Sopenharmony_ci return -EINVAL; 59228c2ecf20Sopenharmony_ci } 59238c2ecf20Sopenharmony_ci 59248c2ecf20Sopenharmony_ci if (nlmsg_attrlen(nlh, sizeof(*ifm))) { 59258c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid data after header"); 59268c2ecf20Sopenharmony_ci return -EINVAL; 59278c2ecf20Sopenharmony_ci } 59288c2ecf20Sopenharmony_ci 59298c2ecf20Sopenharmony_ci ifm = nlmsg_data(nlh); 59308c2ecf20Sopenharmony_ci if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags || 59318c2ecf20Sopenharmony_ci ifm->ifi_change || ifm->ifi_index) { 59328c2ecf20Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for dump request"); 59338c2ecf20Sopenharmony_ci return -EINVAL; 59348c2ecf20Sopenharmony_ci } 59358c2ecf20Sopenharmony_ci 59368c2ecf20Sopenharmony_ci return 0; 59378c2ecf20Sopenharmony_ci} 59388c2ecf20Sopenharmony_ci 59398c2ecf20Sopenharmony_cistatic int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) 59408c2ecf20Sopenharmony_ci{ 59418c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 59428c2ecf20Sopenharmony_ci int h, s_h; 59438c2ecf20Sopenharmony_ci int idx = 0, s_idx; 59448c2ecf20Sopenharmony_ci struct net_device *dev; 59458c2ecf20Sopenharmony_ci struct inet6_dev *idev; 59468c2ecf20Sopenharmony_ci struct hlist_head *head; 59478c2ecf20Sopenharmony_ci 59488c2ecf20Sopenharmony_ci /* only requests using strict checking can pass data to 59498c2ecf20Sopenharmony_ci * influence the dump 59508c2ecf20Sopenharmony_ci */ 59518c2ecf20Sopenharmony_ci if (cb->strict_check) { 59528c2ecf20Sopenharmony_ci int err = inet6_valid_dump_ifinfo(cb->nlh, cb->extack); 59538c2ecf20Sopenharmony_ci 59548c2ecf20Sopenharmony_ci if (err < 0) 59558c2ecf20Sopenharmony_ci return err; 59568c2ecf20Sopenharmony_ci } 59578c2ecf20Sopenharmony_ci 59588c2ecf20Sopenharmony_ci s_h = cb->args[0]; 59598c2ecf20Sopenharmony_ci s_idx = cb->args[1]; 59608c2ecf20Sopenharmony_ci 59618c2ecf20Sopenharmony_ci rcu_read_lock(); 59628c2ecf20Sopenharmony_ci for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 59638c2ecf20Sopenharmony_ci idx = 0; 59648c2ecf20Sopenharmony_ci head = &net->dev_index_head[h]; 59658c2ecf20Sopenharmony_ci hlist_for_each_entry_rcu(dev, head, index_hlist) { 59668c2ecf20Sopenharmony_ci if (idx < s_idx) 59678c2ecf20Sopenharmony_ci goto cont; 59688c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 59698c2ecf20Sopenharmony_ci if (!idev) 59708c2ecf20Sopenharmony_ci goto cont; 59718c2ecf20Sopenharmony_ci if (inet6_fill_ifinfo(skb, idev, 59728c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 59738c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, 59748c2ecf20Sopenharmony_ci RTM_NEWLINK, NLM_F_MULTI) < 0) 59758c2ecf20Sopenharmony_ci goto out; 59768c2ecf20Sopenharmony_cicont: 59778c2ecf20Sopenharmony_ci idx++; 59788c2ecf20Sopenharmony_ci } 59798c2ecf20Sopenharmony_ci } 59808c2ecf20Sopenharmony_ciout: 59818c2ecf20Sopenharmony_ci rcu_read_unlock(); 59828c2ecf20Sopenharmony_ci cb->args[1] = idx; 59838c2ecf20Sopenharmony_ci cb->args[0] = h; 59848c2ecf20Sopenharmony_ci 59858c2ecf20Sopenharmony_ci return skb->len; 59868c2ecf20Sopenharmony_ci} 59878c2ecf20Sopenharmony_ci 59888c2ecf20Sopenharmony_civoid inet6_ifinfo_notify(int event, struct inet6_dev *idev) 59898c2ecf20Sopenharmony_ci{ 59908c2ecf20Sopenharmony_ci struct sk_buff *skb; 59918c2ecf20Sopenharmony_ci struct net *net = dev_net(idev->dev); 59928c2ecf20Sopenharmony_ci int err = -ENOBUFS; 59938c2ecf20Sopenharmony_ci 59948c2ecf20Sopenharmony_ci skb = nlmsg_new(inet6_if_nlmsg_size(), GFP_ATOMIC); 59958c2ecf20Sopenharmony_ci if (!skb) 59968c2ecf20Sopenharmony_ci goto errout; 59978c2ecf20Sopenharmony_ci 59988c2ecf20Sopenharmony_ci err = inet6_fill_ifinfo(skb, idev, 0, 0, event, 0); 59998c2ecf20Sopenharmony_ci if (err < 0) { 60008c2ecf20Sopenharmony_ci /* -EMSGSIZE implies BUG in inet6_if_nlmsg_size() */ 60018c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 60028c2ecf20Sopenharmony_ci kfree_skb(skb); 60038c2ecf20Sopenharmony_ci goto errout; 60048c2ecf20Sopenharmony_ci } 60058c2ecf20Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFINFO, NULL, GFP_ATOMIC); 60068c2ecf20Sopenharmony_ci return; 60078c2ecf20Sopenharmony_cierrout: 60088c2ecf20Sopenharmony_ci if (err < 0) 60098c2ecf20Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_IFINFO, err); 60108c2ecf20Sopenharmony_ci} 60118c2ecf20Sopenharmony_ci 60128c2ecf20Sopenharmony_cistatic inline size_t inet6_prefix_nlmsg_size(void) 60138c2ecf20Sopenharmony_ci{ 60148c2ecf20Sopenharmony_ci return NLMSG_ALIGN(sizeof(struct prefixmsg)) 60158c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct in6_addr)) 60168c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct prefix_cacheinfo)); 60178c2ecf20Sopenharmony_ci} 60188c2ecf20Sopenharmony_ci 60198c2ecf20Sopenharmony_cistatic int inet6_fill_prefix(struct sk_buff *skb, struct inet6_dev *idev, 60208c2ecf20Sopenharmony_ci struct prefix_info *pinfo, u32 portid, u32 seq, 60218c2ecf20Sopenharmony_ci int event, unsigned int flags) 60228c2ecf20Sopenharmony_ci{ 60238c2ecf20Sopenharmony_ci struct prefixmsg *pmsg; 60248c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 60258c2ecf20Sopenharmony_ci struct prefix_cacheinfo ci; 60268c2ecf20Sopenharmony_ci 60278c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(*pmsg), flags); 60288c2ecf20Sopenharmony_ci if (!nlh) 60298c2ecf20Sopenharmony_ci return -EMSGSIZE; 60308c2ecf20Sopenharmony_ci 60318c2ecf20Sopenharmony_ci pmsg = nlmsg_data(nlh); 60328c2ecf20Sopenharmony_ci pmsg->prefix_family = AF_INET6; 60338c2ecf20Sopenharmony_ci pmsg->prefix_pad1 = 0; 60348c2ecf20Sopenharmony_ci pmsg->prefix_pad2 = 0; 60358c2ecf20Sopenharmony_ci pmsg->prefix_ifindex = idev->dev->ifindex; 60368c2ecf20Sopenharmony_ci pmsg->prefix_len = pinfo->prefix_len; 60378c2ecf20Sopenharmony_ci pmsg->prefix_type = pinfo->type; 60388c2ecf20Sopenharmony_ci pmsg->prefix_pad3 = 0; 60398c2ecf20Sopenharmony_ci pmsg->prefix_flags = pinfo->flags; 60408c2ecf20Sopenharmony_ci 60418c2ecf20Sopenharmony_ci if (nla_put(skb, PREFIX_ADDRESS, sizeof(pinfo->prefix), &pinfo->prefix)) 60428c2ecf20Sopenharmony_ci goto nla_put_failure; 60438c2ecf20Sopenharmony_ci ci.preferred_time = ntohl(pinfo->prefered); 60448c2ecf20Sopenharmony_ci ci.valid_time = ntohl(pinfo->valid); 60458c2ecf20Sopenharmony_ci if (nla_put(skb, PREFIX_CACHEINFO, sizeof(ci), &ci)) 60468c2ecf20Sopenharmony_ci goto nla_put_failure; 60478c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 60488c2ecf20Sopenharmony_ci return 0; 60498c2ecf20Sopenharmony_ci 60508c2ecf20Sopenharmony_cinla_put_failure: 60518c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 60528c2ecf20Sopenharmony_ci return -EMSGSIZE; 60538c2ecf20Sopenharmony_ci} 60548c2ecf20Sopenharmony_ci 60558c2ecf20Sopenharmony_cistatic void inet6_prefix_notify(int event, struct inet6_dev *idev, 60568c2ecf20Sopenharmony_ci struct prefix_info *pinfo) 60578c2ecf20Sopenharmony_ci{ 60588c2ecf20Sopenharmony_ci struct sk_buff *skb; 60598c2ecf20Sopenharmony_ci struct net *net = dev_net(idev->dev); 60608c2ecf20Sopenharmony_ci int err = -ENOBUFS; 60618c2ecf20Sopenharmony_ci 60628c2ecf20Sopenharmony_ci skb = nlmsg_new(inet6_prefix_nlmsg_size(), GFP_ATOMIC); 60638c2ecf20Sopenharmony_ci if (!skb) 60648c2ecf20Sopenharmony_ci goto errout; 60658c2ecf20Sopenharmony_ci 60668c2ecf20Sopenharmony_ci err = inet6_fill_prefix(skb, idev, pinfo, 0, 0, event, 0); 60678c2ecf20Sopenharmony_ci if (err < 0) { 60688c2ecf20Sopenharmony_ci /* -EMSGSIZE implies BUG in inet6_prefix_nlmsg_size() */ 60698c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 60708c2ecf20Sopenharmony_ci kfree_skb(skb); 60718c2ecf20Sopenharmony_ci goto errout; 60728c2ecf20Sopenharmony_ci } 60738c2ecf20Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV6_PREFIX, NULL, GFP_ATOMIC); 60748c2ecf20Sopenharmony_ci return; 60758c2ecf20Sopenharmony_cierrout: 60768c2ecf20Sopenharmony_ci if (err < 0) 60778c2ecf20Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_PREFIX, err); 60788c2ecf20Sopenharmony_ci} 60798c2ecf20Sopenharmony_ci 60808c2ecf20Sopenharmony_cistatic void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) 60818c2ecf20Sopenharmony_ci{ 60828c2ecf20Sopenharmony_ci struct net *net = dev_net(ifp->idev->dev); 60838c2ecf20Sopenharmony_ci 60848c2ecf20Sopenharmony_ci if (event) 60858c2ecf20Sopenharmony_ci ASSERT_RTNL(); 60868c2ecf20Sopenharmony_ci 60878c2ecf20Sopenharmony_ci inet6_ifa_notify(event ? : RTM_NEWADDR, ifp); 60888c2ecf20Sopenharmony_ci 60898c2ecf20Sopenharmony_ci switch (event) { 60908c2ecf20Sopenharmony_ci case RTM_NEWADDR: 60918c2ecf20Sopenharmony_ci /* 60928c2ecf20Sopenharmony_ci * If the address was optimistic we inserted the route at the 60938c2ecf20Sopenharmony_ci * start of our DAD process, so we don't need to do it again. 60948c2ecf20Sopenharmony_ci * If the device was taken down in the middle of the DAD 60958c2ecf20Sopenharmony_ci * cycle there is a race where we could get here without a 60968c2ecf20Sopenharmony_ci * host route, so nothing to insert. That will be fixed when 60978c2ecf20Sopenharmony_ci * the device is brought up. 60988c2ecf20Sopenharmony_ci */ 60998c2ecf20Sopenharmony_ci if (ifp->rt && !rcu_access_pointer(ifp->rt->fib6_node)) { 61008c2ecf20Sopenharmony_ci ip6_ins_rt(net, ifp->rt); 61018c2ecf20Sopenharmony_ci } else if (!ifp->rt && (ifp->idev->dev->flags & IFF_UP)) { 61028c2ecf20Sopenharmony_ci pr_warn("BUG: Address %pI6c on device %s is missing its host route.\n", 61038c2ecf20Sopenharmony_ci &ifp->addr, ifp->idev->dev->name); 61048c2ecf20Sopenharmony_ci } 61058c2ecf20Sopenharmony_ci 61068c2ecf20Sopenharmony_ci if (ifp->idev->cnf.forwarding) 61078c2ecf20Sopenharmony_ci addrconf_join_anycast(ifp); 61088c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&ifp->peer_addr)) 61098c2ecf20Sopenharmony_ci addrconf_prefix_route(&ifp->peer_addr, 128, 61108c2ecf20Sopenharmony_ci ifp->rt_priority, ifp->idev->dev, 61118c2ecf20Sopenharmony_ci 0, 0, GFP_ATOMIC); 61128c2ecf20Sopenharmony_ci break; 61138c2ecf20Sopenharmony_ci case RTM_DELADDR: 61148c2ecf20Sopenharmony_ci if (ifp->idev->cnf.forwarding) 61158c2ecf20Sopenharmony_ci addrconf_leave_anycast(ifp); 61168c2ecf20Sopenharmony_ci addrconf_leave_solict(ifp->idev, &ifp->addr); 61178c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&ifp->peer_addr)) { 61188c2ecf20Sopenharmony_ci struct fib6_info *rt; 61198c2ecf20Sopenharmony_ci 61208c2ecf20Sopenharmony_ci rt = addrconf_get_prefix_route(&ifp->peer_addr, 128, 61218c2ecf20Sopenharmony_ci ifp->idev->dev, 0, 0, 61228c2ecf20Sopenharmony_ci false); 61238c2ecf20Sopenharmony_ci if (rt) 61248c2ecf20Sopenharmony_ci ip6_del_rt(net, rt, false); 61258c2ecf20Sopenharmony_ci } 61268c2ecf20Sopenharmony_ci if (ifp->rt) { 61278c2ecf20Sopenharmony_ci ip6_del_rt(net, ifp->rt, false); 61288c2ecf20Sopenharmony_ci ifp->rt = NULL; 61298c2ecf20Sopenharmony_ci } 61308c2ecf20Sopenharmony_ci rt_genid_bump_ipv6(net); 61318c2ecf20Sopenharmony_ci break; 61328c2ecf20Sopenharmony_ci } 61338c2ecf20Sopenharmony_ci atomic_inc(&net->ipv6.dev_addr_genid); 61348c2ecf20Sopenharmony_ci} 61358c2ecf20Sopenharmony_ci 61368c2ecf20Sopenharmony_cistatic void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) 61378c2ecf20Sopenharmony_ci{ 61388c2ecf20Sopenharmony_ci rcu_read_lock_bh(); 61398c2ecf20Sopenharmony_ci if (likely(ifp->idev->dead == 0)) 61408c2ecf20Sopenharmony_ci __ipv6_ifa_notify(event, ifp); 61418c2ecf20Sopenharmony_ci rcu_read_unlock_bh(); 61428c2ecf20Sopenharmony_ci} 61438c2ecf20Sopenharmony_ci 61448c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 61458c2ecf20Sopenharmony_ci 61468c2ecf20Sopenharmony_cistatic int addrconf_sysctl_forward(struct ctl_table *ctl, int write, 61478c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 61488c2ecf20Sopenharmony_ci{ 61498c2ecf20Sopenharmony_ci int *valp = ctl->data; 61508c2ecf20Sopenharmony_ci int val = *valp; 61518c2ecf20Sopenharmony_ci loff_t pos = *ppos; 61528c2ecf20Sopenharmony_ci struct ctl_table lctl; 61538c2ecf20Sopenharmony_ci int ret; 61548c2ecf20Sopenharmony_ci 61558c2ecf20Sopenharmony_ci /* 61568c2ecf20Sopenharmony_ci * ctl->data points to idev->cnf.forwarding, we should 61578c2ecf20Sopenharmony_ci * not modify it until we get the rtnl lock. 61588c2ecf20Sopenharmony_ci */ 61598c2ecf20Sopenharmony_ci lctl = *ctl; 61608c2ecf20Sopenharmony_ci lctl.data = &val; 61618c2ecf20Sopenharmony_ci 61628c2ecf20Sopenharmony_ci ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); 61638c2ecf20Sopenharmony_ci 61648c2ecf20Sopenharmony_ci if (write) 61658c2ecf20Sopenharmony_ci ret = addrconf_fixup_forwarding(ctl, valp, val); 61668c2ecf20Sopenharmony_ci if (ret) 61678c2ecf20Sopenharmony_ci *ppos = pos; 61688c2ecf20Sopenharmony_ci return ret; 61698c2ecf20Sopenharmony_ci} 61708c2ecf20Sopenharmony_ci 61718c2ecf20Sopenharmony_cistatic int addrconf_sysctl_mtu(struct ctl_table *ctl, int write, 61728c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 61738c2ecf20Sopenharmony_ci{ 61748c2ecf20Sopenharmony_ci struct inet6_dev *idev = ctl->extra1; 61758c2ecf20Sopenharmony_ci int min_mtu = IPV6_MIN_MTU; 61768c2ecf20Sopenharmony_ci struct ctl_table lctl; 61778c2ecf20Sopenharmony_ci 61788c2ecf20Sopenharmony_ci lctl = *ctl; 61798c2ecf20Sopenharmony_ci lctl.extra1 = &min_mtu; 61808c2ecf20Sopenharmony_ci lctl.extra2 = idev ? &idev->dev->mtu : NULL; 61818c2ecf20Sopenharmony_ci 61828c2ecf20Sopenharmony_ci return proc_dointvec_minmax(&lctl, write, buffer, lenp, ppos); 61838c2ecf20Sopenharmony_ci} 61848c2ecf20Sopenharmony_ci 61858c2ecf20Sopenharmony_cistatic void dev_disable_change(struct inet6_dev *idev) 61868c2ecf20Sopenharmony_ci{ 61878c2ecf20Sopenharmony_ci struct netdev_notifier_info info; 61888c2ecf20Sopenharmony_ci 61898c2ecf20Sopenharmony_ci if (!idev || !idev->dev) 61908c2ecf20Sopenharmony_ci return; 61918c2ecf20Sopenharmony_ci 61928c2ecf20Sopenharmony_ci netdev_notifier_info_init(&info, idev->dev); 61938c2ecf20Sopenharmony_ci if (idev->cnf.disable_ipv6) 61948c2ecf20Sopenharmony_ci addrconf_notify(NULL, NETDEV_DOWN, &info); 61958c2ecf20Sopenharmony_ci else 61968c2ecf20Sopenharmony_ci addrconf_notify(NULL, NETDEV_UP, &info); 61978c2ecf20Sopenharmony_ci} 61988c2ecf20Sopenharmony_ci 61998c2ecf20Sopenharmony_cistatic void addrconf_disable_change(struct net *net, __s32 newf) 62008c2ecf20Sopenharmony_ci{ 62018c2ecf20Sopenharmony_ci struct net_device *dev; 62028c2ecf20Sopenharmony_ci struct inet6_dev *idev; 62038c2ecf20Sopenharmony_ci 62048c2ecf20Sopenharmony_ci for_each_netdev(net, dev) { 62058c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 62068c2ecf20Sopenharmony_ci if (idev) { 62078c2ecf20Sopenharmony_ci int changed = (!idev->cnf.disable_ipv6) ^ (!newf); 62088c2ecf20Sopenharmony_ci idev->cnf.disable_ipv6 = newf; 62098c2ecf20Sopenharmony_ci if (changed) 62108c2ecf20Sopenharmony_ci dev_disable_change(idev); 62118c2ecf20Sopenharmony_ci } 62128c2ecf20Sopenharmony_ci } 62138c2ecf20Sopenharmony_ci} 62148c2ecf20Sopenharmony_ci 62158c2ecf20Sopenharmony_cistatic int addrconf_disable_ipv6(struct ctl_table *table, int *p, int newf) 62168c2ecf20Sopenharmony_ci{ 62178c2ecf20Sopenharmony_ci struct net *net; 62188c2ecf20Sopenharmony_ci int old; 62198c2ecf20Sopenharmony_ci 62208c2ecf20Sopenharmony_ci if (!rtnl_trylock()) 62218c2ecf20Sopenharmony_ci return restart_syscall(); 62228c2ecf20Sopenharmony_ci 62238c2ecf20Sopenharmony_ci net = (struct net *)table->extra2; 62248c2ecf20Sopenharmony_ci old = *p; 62258c2ecf20Sopenharmony_ci *p = newf; 62268c2ecf20Sopenharmony_ci 62278c2ecf20Sopenharmony_ci if (p == &net->ipv6.devconf_dflt->disable_ipv6) { 62288c2ecf20Sopenharmony_ci rtnl_unlock(); 62298c2ecf20Sopenharmony_ci return 0; 62308c2ecf20Sopenharmony_ci } 62318c2ecf20Sopenharmony_ci 62328c2ecf20Sopenharmony_ci if (p == &net->ipv6.devconf_all->disable_ipv6) { 62338c2ecf20Sopenharmony_ci net->ipv6.devconf_dflt->disable_ipv6 = newf; 62348c2ecf20Sopenharmony_ci addrconf_disable_change(net, newf); 62358c2ecf20Sopenharmony_ci } else if ((!newf) ^ (!old)) 62368c2ecf20Sopenharmony_ci dev_disable_change((struct inet6_dev *)table->extra1); 62378c2ecf20Sopenharmony_ci 62388c2ecf20Sopenharmony_ci rtnl_unlock(); 62398c2ecf20Sopenharmony_ci return 0; 62408c2ecf20Sopenharmony_ci} 62418c2ecf20Sopenharmony_ci 62428c2ecf20Sopenharmony_cistatic int addrconf_sysctl_disable(struct ctl_table *ctl, int write, 62438c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 62448c2ecf20Sopenharmony_ci{ 62458c2ecf20Sopenharmony_ci int *valp = ctl->data; 62468c2ecf20Sopenharmony_ci int val = *valp; 62478c2ecf20Sopenharmony_ci loff_t pos = *ppos; 62488c2ecf20Sopenharmony_ci struct ctl_table lctl; 62498c2ecf20Sopenharmony_ci int ret; 62508c2ecf20Sopenharmony_ci 62518c2ecf20Sopenharmony_ci /* 62528c2ecf20Sopenharmony_ci * ctl->data points to idev->cnf.disable_ipv6, we should 62538c2ecf20Sopenharmony_ci * not modify it until we get the rtnl lock. 62548c2ecf20Sopenharmony_ci */ 62558c2ecf20Sopenharmony_ci lctl = *ctl; 62568c2ecf20Sopenharmony_ci lctl.data = &val; 62578c2ecf20Sopenharmony_ci 62588c2ecf20Sopenharmony_ci ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); 62598c2ecf20Sopenharmony_ci 62608c2ecf20Sopenharmony_ci if (write) 62618c2ecf20Sopenharmony_ci ret = addrconf_disable_ipv6(ctl, valp, val); 62628c2ecf20Sopenharmony_ci if (ret) 62638c2ecf20Sopenharmony_ci *ppos = pos; 62648c2ecf20Sopenharmony_ci return ret; 62658c2ecf20Sopenharmony_ci} 62668c2ecf20Sopenharmony_ci 62678c2ecf20Sopenharmony_cistatic int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write, 62688c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 62698c2ecf20Sopenharmony_ci{ 62708c2ecf20Sopenharmony_ci int *valp = ctl->data; 62718c2ecf20Sopenharmony_ci int ret; 62728c2ecf20Sopenharmony_ci int old, new; 62738c2ecf20Sopenharmony_ci 62748c2ecf20Sopenharmony_ci old = *valp; 62758c2ecf20Sopenharmony_ci ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 62768c2ecf20Sopenharmony_ci new = *valp; 62778c2ecf20Sopenharmony_ci 62788c2ecf20Sopenharmony_ci if (write && old != new) { 62798c2ecf20Sopenharmony_ci struct net *net = ctl->extra2; 62808c2ecf20Sopenharmony_ci 62818c2ecf20Sopenharmony_ci if (!rtnl_trylock()) 62828c2ecf20Sopenharmony_ci return restart_syscall(); 62838c2ecf20Sopenharmony_ci 62848c2ecf20Sopenharmony_ci if (valp == &net->ipv6.devconf_dflt->proxy_ndp) 62858c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 62868c2ecf20Sopenharmony_ci NETCONFA_PROXY_NEIGH, 62878c2ecf20Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT, 62888c2ecf20Sopenharmony_ci net->ipv6.devconf_dflt); 62898c2ecf20Sopenharmony_ci else if (valp == &net->ipv6.devconf_all->proxy_ndp) 62908c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 62918c2ecf20Sopenharmony_ci NETCONFA_PROXY_NEIGH, 62928c2ecf20Sopenharmony_ci NETCONFA_IFINDEX_ALL, 62938c2ecf20Sopenharmony_ci net->ipv6.devconf_all); 62948c2ecf20Sopenharmony_ci else { 62958c2ecf20Sopenharmony_ci struct inet6_dev *idev = ctl->extra1; 62968c2ecf20Sopenharmony_ci 62978c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 62988c2ecf20Sopenharmony_ci NETCONFA_PROXY_NEIGH, 62998c2ecf20Sopenharmony_ci idev->dev->ifindex, 63008c2ecf20Sopenharmony_ci &idev->cnf); 63018c2ecf20Sopenharmony_ci } 63028c2ecf20Sopenharmony_ci rtnl_unlock(); 63038c2ecf20Sopenharmony_ci } 63048c2ecf20Sopenharmony_ci 63058c2ecf20Sopenharmony_ci return ret; 63068c2ecf20Sopenharmony_ci} 63078c2ecf20Sopenharmony_ci 63088c2ecf20Sopenharmony_cistatic int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write, 63098c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, 63108c2ecf20Sopenharmony_ci loff_t *ppos) 63118c2ecf20Sopenharmony_ci{ 63128c2ecf20Sopenharmony_ci int ret = 0; 63138c2ecf20Sopenharmony_ci u32 new_val; 63148c2ecf20Sopenharmony_ci struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1; 63158c2ecf20Sopenharmony_ci struct net *net = (struct net *)ctl->extra2; 63168c2ecf20Sopenharmony_ci struct ctl_table tmp = { 63178c2ecf20Sopenharmony_ci .data = &new_val, 63188c2ecf20Sopenharmony_ci .maxlen = sizeof(new_val), 63198c2ecf20Sopenharmony_ci .mode = ctl->mode, 63208c2ecf20Sopenharmony_ci }; 63218c2ecf20Sopenharmony_ci 63228c2ecf20Sopenharmony_ci if (!rtnl_trylock()) 63238c2ecf20Sopenharmony_ci return restart_syscall(); 63248c2ecf20Sopenharmony_ci 63258c2ecf20Sopenharmony_ci new_val = *((u32 *)ctl->data); 63268c2ecf20Sopenharmony_ci 63278c2ecf20Sopenharmony_ci ret = proc_douintvec(&tmp, write, buffer, lenp, ppos); 63288c2ecf20Sopenharmony_ci if (ret != 0) 63298c2ecf20Sopenharmony_ci goto out; 63308c2ecf20Sopenharmony_ci 63318c2ecf20Sopenharmony_ci if (write) { 63328c2ecf20Sopenharmony_ci if (check_addr_gen_mode(new_val) < 0) { 63338c2ecf20Sopenharmony_ci ret = -EINVAL; 63348c2ecf20Sopenharmony_ci goto out; 63358c2ecf20Sopenharmony_ci } 63368c2ecf20Sopenharmony_ci 63378c2ecf20Sopenharmony_ci if (idev) { 63388c2ecf20Sopenharmony_ci if (check_stable_privacy(idev, net, new_val) < 0) { 63398c2ecf20Sopenharmony_ci ret = -EINVAL; 63408c2ecf20Sopenharmony_ci goto out; 63418c2ecf20Sopenharmony_ci } 63428c2ecf20Sopenharmony_ci 63438c2ecf20Sopenharmony_ci if (idev->cnf.addr_gen_mode != new_val) { 63448c2ecf20Sopenharmony_ci idev->cnf.addr_gen_mode = new_val; 63458c2ecf20Sopenharmony_ci addrconf_dev_config(idev->dev); 63468c2ecf20Sopenharmony_ci } 63478c2ecf20Sopenharmony_ci } else if (&net->ipv6.devconf_all->addr_gen_mode == ctl->data) { 63488c2ecf20Sopenharmony_ci struct net_device *dev; 63498c2ecf20Sopenharmony_ci 63508c2ecf20Sopenharmony_ci net->ipv6.devconf_dflt->addr_gen_mode = new_val; 63518c2ecf20Sopenharmony_ci for_each_netdev(net, dev) { 63528c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 63538c2ecf20Sopenharmony_ci if (idev && 63548c2ecf20Sopenharmony_ci idev->cnf.addr_gen_mode != new_val) { 63558c2ecf20Sopenharmony_ci idev->cnf.addr_gen_mode = new_val; 63568c2ecf20Sopenharmony_ci addrconf_dev_config(idev->dev); 63578c2ecf20Sopenharmony_ci } 63588c2ecf20Sopenharmony_ci } 63598c2ecf20Sopenharmony_ci } 63608c2ecf20Sopenharmony_ci 63618c2ecf20Sopenharmony_ci *((u32 *)ctl->data) = new_val; 63628c2ecf20Sopenharmony_ci } 63638c2ecf20Sopenharmony_ci 63648c2ecf20Sopenharmony_ciout: 63658c2ecf20Sopenharmony_ci rtnl_unlock(); 63668c2ecf20Sopenharmony_ci 63678c2ecf20Sopenharmony_ci return ret; 63688c2ecf20Sopenharmony_ci} 63698c2ecf20Sopenharmony_ci 63708c2ecf20Sopenharmony_cistatic int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write, 63718c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, 63728c2ecf20Sopenharmony_ci loff_t *ppos) 63738c2ecf20Sopenharmony_ci{ 63748c2ecf20Sopenharmony_ci int err; 63758c2ecf20Sopenharmony_ci struct in6_addr addr; 63768c2ecf20Sopenharmony_ci char str[IPV6_MAX_STRLEN]; 63778c2ecf20Sopenharmony_ci struct ctl_table lctl = *ctl; 63788c2ecf20Sopenharmony_ci struct net *net = ctl->extra2; 63798c2ecf20Sopenharmony_ci struct ipv6_stable_secret *secret = ctl->data; 63808c2ecf20Sopenharmony_ci 63818c2ecf20Sopenharmony_ci if (&net->ipv6.devconf_all->stable_secret == ctl->data) 63828c2ecf20Sopenharmony_ci return -EIO; 63838c2ecf20Sopenharmony_ci 63848c2ecf20Sopenharmony_ci lctl.maxlen = IPV6_MAX_STRLEN; 63858c2ecf20Sopenharmony_ci lctl.data = str; 63868c2ecf20Sopenharmony_ci 63878c2ecf20Sopenharmony_ci if (!rtnl_trylock()) 63888c2ecf20Sopenharmony_ci return restart_syscall(); 63898c2ecf20Sopenharmony_ci 63908c2ecf20Sopenharmony_ci if (!write && !secret->initialized) { 63918c2ecf20Sopenharmony_ci err = -EIO; 63928c2ecf20Sopenharmony_ci goto out; 63938c2ecf20Sopenharmony_ci } 63948c2ecf20Sopenharmony_ci 63958c2ecf20Sopenharmony_ci err = snprintf(str, sizeof(str), "%pI6", &secret->secret); 63968c2ecf20Sopenharmony_ci if (err >= sizeof(str)) { 63978c2ecf20Sopenharmony_ci err = -EIO; 63988c2ecf20Sopenharmony_ci goto out; 63998c2ecf20Sopenharmony_ci } 64008c2ecf20Sopenharmony_ci 64018c2ecf20Sopenharmony_ci err = proc_dostring(&lctl, write, buffer, lenp, ppos); 64028c2ecf20Sopenharmony_ci if (err || !write) 64038c2ecf20Sopenharmony_ci goto out; 64048c2ecf20Sopenharmony_ci 64058c2ecf20Sopenharmony_ci if (in6_pton(str, -1, addr.in6_u.u6_addr8, -1, NULL) != 1) { 64068c2ecf20Sopenharmony_ci err = -EIO; 64078c2ecf20Sopenharmony_ci goto out; 64088c2ecf20Sopenharmony_ci } 64098c2ecf20Sopenharmony_ci 64108c2ecf20Sopenharmony_ci secret->initialized = true; 64118c2ecf20Sopenharmony_ci secret->secret = addr; 64128c2ecf20Sopenharmony_ci 64138c2ecf20Sopenharmony_ci if (&net->ipv6.devconf_dflt->stable_secret == ctl->data) { 64148c2ecf20Sopenharmony_ci struct net_device *dev; 64158c2ecf20Sopenharmony_ci 64168c2ecf20Sopenharmony_ci for_each_netdev(net, dev) { 64178c2ecf20Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 64188c2ecf20Sopenharmony_ci 64198c2ecf20Sopenharmony_ci if (idev) { 64208c2ecf20Sopenharmony_ci idev->cnf.addr_gen_mode = 64218c2ecf20Sopenharmony_ci IN6_ADDR_GEN_MODE_STABLE_PRIVACY; 64228c2ecf20Sopenharmony_ci } 64238c2ecf20Sopenharmony_ci } 64248c2ecf20Sopenharmony_ci } else { 64258c2ecf20Sopenharmony_ci struct inet6_dev *idev = ctl->extra1; 64268c2ecf20Sopenharmony_ci 64278c2ecf20Sopenharmony_ci idev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; 64288c2ecf20Sopenharmony_ci } 64298c2ecf20Sopenharmony_ci 64308c2ecf20Sopenharmony_ciout: 64318c2ecf20Sopenharmony_ci rtnl_unlock(); 64328c2ecf20Sopenharmony_ci 64338c2ecf20Sopenharmony_ci return err; 64348c2ecf20Sopenharmony_ci} 64358c2ecf20Sopenharmony_ci 64368c2ecf20Sopenharmony_cistatic 64378c2ecf20Sopenharmony_ciint addrconf_sysctl_ignore_routes_with_linkdown(struct ctl_table *ctl, 64388c2ecf20Sopenharmony_ci int write, void *buffer, 64398c2ecf20Sopenharmony_ci size_t *lenp, 64408c2ecf20Sopenharmony_ci loff_t *ppos) 64418c2ecf20Sopenharmony_ci{ 64428c2ecf20Sopenharmony_ci int *valp = ctl->data; 64438c2ecf20Sopenharmony_ci int val = *valp; 64448c2ecf20Sopenharmony_ci loff_t pos = *ppos; 64458c2ecf20Sopenharmony_ci struct ctl_table lctl; 64468c2ecf20Sopenharmony_ci int ret; 64478c2ecf20Sopenharmony_ci 64488c2ecf20Sopenharmony_ci /* ctl->data points to idev->cnf.ignore_routes_when_linkdown 64498c2ecf20Sopenharmony_ci * we should not modify it until we get the rtnl lock. 64508c2ecf20Sopenharmony_ci */ 64518c2ecf20Sopenharmony_ci lctl = *ctl; 64528c2ecf20Sopenharmony_ci lctl.data = &val; 64538c2ecf20Sopenharmony_ci 64548c2ecf20Sopenharmony_ci ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); 64558c2ecf20Sopenharmony_ci 64568c2ecf20Sopenharmony_ci if (write) 64578c2ecf20Sopenharmony_ci ret = addrconf_fixup_linkdown(ctl, valp, val); 64588c2ecf20Sopenharmony_ci if (ret) 64598c2ecf20Sopenharmony_ci *ppos = pos; 64608c2ecf20Sopenharmony_ci return ret; 64618c2ecf20Sopenharmony_ci} 64628c2ecf20Sopenharmony_ci 64638c2ecf20Sopenharmony_cistatic 64648c2ecf20Sopenharmony_civoid addrconf_set_nopolicy(struct rt6_info *rt, int action) 64658c2ecf20Sopenharmony_ci{ 64668c2ecf20Sopenharmony_ci if (rt) { 64678c2ecf20Sopenharmony_ci if (action) 64688c2ecf20Sopenharmony_ci rt->dst.flags |= DST_NOPOLICY; 64698c2ecf20Sopenharmony_ci else 64708c2ecf20Sopenharmony_ci rt->dst.flags &= ~DST_NOPOLICY; 64718c2ecf20Sopenharmony_ci } 64728c2ecf20Sopenharmony_ci} 64738c2ecf20Sopenharmony_ci 64748c2ecf20Sopenharmony_cistatic 64758c2ecf20Sopenharmony_civoid addrconf_disable_policy_idev(struct inet6_dev *idev, int val) 64768c2ecf20Sopenharmony_ci{ 64778c2ecf20Sopenharmony_ci struct inet6_ifaddr *ifa; 64788c2ecf20Sopenharmony_ci 64798c2ecf20Sopenharmony_ci read_lock_bh(&idev->lock); 64808c2ecf20Sopenharmony_ci list_for_each_entry(ifa, &idev->addr_list, if_list) { 64818c2ecf20Sopenharmony_ci spin_lock(&ifa->lock); 64828c2ecf20Sopenharmony_ci if (ifa->rt) { 64838c2ecf20Sopenharmony_ci /* host routes only use builtin fib6_nh */ 64848c2ecf20Sopenharmony_ci struct fib6_nh *nh = ifa->rt->fib6_nh; 64858c2ecf20Sopenharmony_ci int cpu; 64868c2ecf20Sopenharmony_ci 64878c2ecf20Sopenharmony_ci rcu_read_lock(); 64888c2ecf20Sopenharmony_ci ifa->rt->dst_nopolicy = val ? true : false; 64898c2ecf20Sopenharmony_ci if (nh->rt6i_pcpu) { 64908c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 64918c2ecf20Sopenharmony_ci struct rt6_info **rtp; 64928c2ecf20Sopenharmony_ci 64938c2ecf20Sopenharmony_ci rtp = per_cpu_ptr(nh->rt6i_pcpu, cpu); 64948c2ecf20Sopenharmony_ci addrconf_set_nopolicy(*rtp, val); 64958c2ecf20Sopenharmony_ci } 64968c2ecf20Sopenharmony_ci } 64978c2ecf20Sopenharmony_ci rcu_read_unlock(); 64988c2ecf20Sopenharmony_ci } 64998c2ecf20Sopenharmony_ci spin_unlock(&ifa->lock); 65008c2ecf20Sopenharmony_ci } 65018c2ecf20Sopenharmony_ci read_unlock_bh(&idev->lock); 65028c2ecf20Sopenharmony_ci} 65038c2ecf20Sopenharmony_ci 65048c2ecf20Sopenharmony_cistatic 65058c2ecf20Sopenharmony_ciint addrconf_disable_policy(struct ctl_table *ctl, int *valp, int val) 65068c2ecf20Sopenharmony_ci{ 65078c2ecf20Sopenharmony_ci struct inet6_dev *idev; 65088c2ecf20Sopenharmony_ci struct net *net; 65098c2ecf20Sopenharmony_ci 65108c2ecf20Sopenharmony_ci if (!rtnl_trylock()) 65118c2ecf20Sopenharmony_ci return restart_syscall(); 65128c2ecf20Sopenharmony_ci 65138c2ecf20Sopenharmony_ci *valp = val; 65148c2ecf20Sopenharmony_ci 65158c2ecf20Sopenharmony_ci net = (struct net *)ctl->extra2; 65168c2ecf20Sopenharmony_ci if (valp == &net->ipv6.devconf_dflt->disable_policy) { 65178c2ecf20Sopenharmony_ci rtnl_unlock(); 65188c2ecf20Sopenharmony_ci return 0; 65198c2ecf20Sopenharmony_ci } 65208c2ecf20Sopenharmony_ci 65218c2ecf20Sopenharmony_ci if (valp == &net->ipv6.devconf_all->disable_policy) { 65228c2ecf20Sopenharmony_ci struct net_device *dev; 65238c2ecf20Sopenharmony_ci 65248c2ecf20Sopenharmony_ci for_each_netdev(net, dev) { 65258c2ecf20Sopenharmony_ci idev = __in6_dev_get(dev); 65268c2ecf20Sopenharmony_ci if (idev) 65278c2ecf20Sopenharmony_ci addrconf_disable_policy_idev(idev, val); 65288c2ecf20Sopenharmony_ci } 65298c2ecf20Sopenharmony_ci } else { 65308c2ecf20Sopenharmony_ci idev = (struct inet6_dev *)ctl->extra1; 65318c2ecf20Sopenharmony_ci addrconf_disable_policy_idev(idev, val); 65328c2ecf20Sopenharmony_ci } 65338c2ecf20Sopenharmony_ci 65348c2ecf20Sopenharmony_ci rtnl_unlock(); 65358c2ecf20Sopenharmony_ci return 0; 65368c2ecf20Sopenharmony_ci} 65378c2ecf20Sopenharmony_ci 65388c2ecf20Sopenharmony_cistatic int addrconf_sysctl_disable_policy(struct ctl_table *ctl, int write, 65398c2ecf20Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 65408c2ecf20Sopenharmony_ci{ 65418c2ecf20Sopenharmony_ci int *valp = ctl->data; 65428c2ecf20Sopenharmony_ci int val = *valp; 65438c2ecf20Sopenharmony_ci loff_t pos = *ppos; 65448c2ecf20Sopenharmony_ci struct ctl_table lctl; 65458c2ecf20Sopenharmony_ci int ret; 65468c2ecf20Sopenharmony_ci 65478c2ecf20Sopenharmony_ci lctl = *ctl; 65488c2ecf20Sopenharmony_ci lctl.data = &val; 65498c2ecf20Sopenharmony_ci ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); 65508c2ecf20Sopenharmony_ci 65518c2ecf20Sopenharmony_ci if (write && (*valp != val)) 65528c2ecf20Sopenharmony_ci ret = addrconf_disable_policy(ctl, valp, val); 65538c2ecf20Sopenharmony_ci 65548c2ecf20Sopenharmony_ci if (ret) 65558c2ecf20Sopenharmony_ci *ppos = pos; 65568c2ecf20Sopenharmony_ci 65578c2ecf20Sopenharmony_ci return ret; 65588c2ecf20Sopenharmony_ci} 65598c2ecf20Sopenharmony_ci 65608c2ecf20Sopenharmony_cistatic int minus_one = -1; 65618c2ecf20Sopenharmony_cistatic const int two_five_five = 255; 65628c2ecf20Sopenharmony_ci 65638c2ecf20Sopenharmony_cistatic const struct ctl_table addrconf_sysctl[] = { 65648c2ecf20Sopenharmony_ci { 65658c2ecf20Sopenharmony_ci .procname = "forwarding", 65668c2ecf20Sopenharmony_ci .data = &ipv6_devconf.forwarding, 65678c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 65688c2ecf20Sopenharmony_ci .mode = 0644, 65698c2ecf20Sopenharmony_ci .proc_handler = addrconf_sysctl_forward, 65708c2ecf20Sopenharmony_ci }, 65718c2ecf20Sopenharmony_ci { 65728c2ecf20Sopenharmony_ci .procname = "hop_limit", 65738c2ecf20Sopenharmony_ci .data = &ipv6_devconf.hop_limit, 65748c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 65758c2ecf20Sopenharmony_ci .mode = 0644, 65768c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 65778c2ecf20Sopenharmony_ci .extra1 = (void *)SYSCTL_ONE, 65788c2ecf20Sopenharmony_ci .extra2 = (void *)&two_five_five, 65798c2ecf20Sopenharmony_ci }, 65808c2ecf20Sopenharmony_ci { 65818c2ecf20Sopenharmony_ci .procname = "mtu", 65828c2ecf20Sopenharmony_ci .data = &ipv6_devconf.mtu6, 65838c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 65848c2ecf20Sopenharmony_ci .mode = 0644, 65858c2ecf20Sopenharmony_ci .proc_handler = addrconf_sysctl_mtu, 65868c2ecf20Sopenharmony_ci }, 65878c2ecf20Sopenharmony_ci { 65888c2ecf20Sopenharmony_ci .procname = "accept_ra", 65898c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_ra, 65908c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 65918c2ecf20Sopenharmony_ci .mode = 0644, 65928c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 65938c2ecf20Sopenharmony_ci }, 65948c2ecf20Sopenharmony_ci { 65958c2ecf20Sopenharmony_ci .procname = "accept_redirects", 65968c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_redirects, 65978c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 65988c2ecf20Sopenharmony_ci .mode = 0644, 65998c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 66008c2ecf20Sopenharmony_ci }, 66018c2ecf20Sopenharmony_ci { 66028c2ecf20Sopenharmony_ci .procname = "autoconf", 66038c2ecf20Sopenharmony_ci .data = &ipv6_devconf.autoconf, 66048c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66058c2ecf20Sopenharmony_ci .mode = 0644, 66068c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 66078c2ecf20Sopenharmony_ci }, 66088c2ecf20Sopenharmony_ci { 66098c2ecf20Sopenharmony_ci .procname = "dad_transmits", 66108c2ecf20Sopenharmony_ci .data = &ipv6_devconf.dad_transmits, 66118c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66128c2ecf20Sopenharmony_ci .mode = 0644, 66138c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 66148c2ecf20Sopenharmony_ci }, 66158c2ecf20Sopenharmony_ci { 66168c2ecf20Sopenharmony_ci .procname = "router_solicitations", 66178c2ecf20Sopenharmony_ci .data = &ipv6_devconf.rtr_solicits, 66188c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66198c2ecf20Sopenharmony_ci .mode = 0644, 66208c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 66218c2ecf20Sopenharmony_ci .extra1 = &minus_one, 66228c2ecf20Sopenharmony_ci }, 66238c2ecf20Sopenharmony_ci { 66248c2ecf20Sopenharmony_ci .procname = "router_solicitation_interval", 66258c2ecf20Sopenharmony_ci .data = &ipv6_devconf.rtr_solicit_interval, 66268c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66278c2ecf20Sopenharmony_ci .mode = 0644, 66288c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 66298c2ecf20Sopenharmony_ci }, 66308c2ecf20Sopenharmony_ci { 66318c2ecf20Sopenharmony_ci .procname = "router_solicitation_max_interval", 66328c2ecf20Sopenharmony_ci .data = &ipv6_devconf.rtr_solicit_max_interval, 66338c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66348c2ecf20Sopenharmony_ci .mode = 0644, 66358c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 66368c2ecf20Sopenharmony_ci }, 66378c2ecf20Sopenharmony_ci { 66388c2ecf20Sopenharmony_ci .procname = "router_solicitation_delay", 66398c2ecf20Sopenharmony_ci .data = &ipv6_devconf.rtr_solicit_delay, 66408c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66418c2ecf20Sopenharmony_ci .mode = 0644, 66428c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 66438c2ecf20Sopenharmony_ci }, 66448c2ecf20Sopenharmony_ci { 66458c2ecf20Sopenharmony_ci .procname = "force_mld_version", 66468c2ecf20Sopenharmony_ci .data = &ipv6_devconf.force_mld_version, 66478c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66488c2ecf20Sopenharmony_ci .mode = 0644, 66498c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 66508c2ecf20Sopenharmony_ci }, 66518c2ecf20Sopenharmony_ci { 66528c2ecf20Sopenharmony_ci .procname = "mldv1_unsolicited_report_interval", 66538c2ecf20Sopenharmony_ci .data = 66548c2ecf20Sopenharmony_ci &ipv6_devconf.mldv1_unsolicited_report_interval, 66558c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66568c2ecf20Sopenharmony_ci .mode = 0644, 66578c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_ms_jiffies, 66588c2ecf20Sopenharmony_ci }, 66598c2ecf20Sopenharmony_ci { 66608c2ecf20Sopenharmony_ci .procname = "mldv2_unsolicited_report_interval", 66618c2ecf20Sopenharmony_ci .data = 66628c2ecf20Sopenharmony_ci &ipv6_devconf.mldv2_unsolicited_report_interval, 66638c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66648c2ecf20Sopenharmony_ci .mode = 0644, 66658c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_ms_jiffies, 66668c2ecf20Sopenharmony_ci }, 66678c2ecf20Sopenharmony_ci { 66688c2ecf20Sopenharmony_ci .procname = "use_tempaddr", 66698c2ecf20Sopenharmony_ci .data = &ipv6_devconf.use_tempaddr, 66708c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66718c2ecf20Sopenharmony_ci .mode = 0644, 66728c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 66738c2ecf20Sopenharmony_ci }, 66748c2ecf20Sopenharmony_ci { 66758c2ecf20Sopenharmony_ci .procname = "temp_valid_lft", 66768c2ecf20Sopenharmony_ci .data = &ipv6_devconf.temp_valid_lft, 66778c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66788c2ecf20Sopenharmony_ci .mode = 0644, 66798c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 66808c2ecf20Sopenharmony_ci }, 66818c2ecf20Sopenharmony_ci { 66828c2ecf20Sopenharmony_ci .procname = "temp_prefered_lft", 66838c2ecf20Sopenharmony_ci .data = &ipv6_devconf.temp_prefered_lft, 66848c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66858c2ecf20Sopenharmony_ci .mode = 0644, 66868c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 66878c2ecf20Sopenharmony_ci }, 66888c2ecf20Sopenharmony_ci { 66898c2ecf20Sopenharmony_ci .procname = "regen_max_retry", 66908c2ecf20Sopenharmony_ci .data = &ipv6_devconf.regen_max_retry, 66918c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66928c2ecf20Sopenharmony_ci .mode = 0644, 66938c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 66948c2ecf20Sopenharmony_ci }, 66958c2ecf20Sopenharmony_ci { 66968c2ecf20Sopenharmony_ci .procname = "max_desync_factor", 66978c2ecf20Sopenharmony_ci .data = &ipv6_devconf.max_desync_factor, 66988c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 66998c2ecf20Sopenharmony_ci .mode = 0644, 67008c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 67018c2ecf20Sopenharmony_ci }, 67028c2ecf20Sopenharmony_ci { 67038c2ecf20Sopenharmony_ci .procname = "max_addresses", 67048c2ecf20Sopenharmony_ci .data = &ipv6_devconf.max_addresses, 67058c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67068c2ecf20Sopenharmony_ci .mode = 0644, 67078c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 67088c2ecf20Sopenharmony_ci }, 67098c2ecf20Sopenharmony_ci { 67108c2ecf20Sopenharmony_ci .procname = "accept_ra_defrtr", 67118c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_ra_defrtr, 67128c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67138c2ecf20Sopenharmony_ci .mode = 0644, 67148c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 67158c2ecf20Sopenharmony_ci }, 67168c2ecf20Sopenharmony_ci { 67178c2ecf20Sopenharmony_ci .procname = "accept_ra_min_hop_limit", 67188c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_ra_min_hop_limit, 67198c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67208c2ecf20Sopenharmony_ci .mode = 0644, 67218c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 67228c2ecf20Sopenharmony_ci }, 67238c2ecf20Sopenharmony_ci { 67248c2ecf20Sopenharmony_ci .procname = "accept_ra_min_lft", 67258c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_ra_min_lft, 67268c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67278c2ecf20Sopenharmony_ci .mode = 0644, 67288c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 67298c2ecf20Sopenharmony_ci }, 67308c2ecf20Sopenharmony_ci { 67318c2ecf20Sopenharmony_ci .procname = "accept_ra_pinfo", 67328c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_ra_pinfo, 67338c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67348c2ecf20Sopenharmony_ci .mode = 0644, 67358c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 67368c2ecf20Sopenharmony_ci }, 67378c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 67388c2ecf20Sopenharmony_ci { 67398c2ecf20Sopenharmony_ci .procname = "accept_ra_rtr_pref", 67408c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_ra_rtr_pref, 67418c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67428c2ecf20Sopenharmony_ci .mode = 0644, 67438c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 67448c2ecf20Sopenharmony_ci }, 67458c2ecf20Sopenharmony_ci { 67468c2ecf20Sopenharmony_ci .procname = "router_probe_interval", 67478c2ecf20Sopenharmony_ci .data = &ipv6_devconf.rtr_probe_interval, 67488c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67498c2ecf20Sopenharmony_ci .mode = 0644, 67508c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 67518c2ecf20Sopenharmony_ci }, 67528c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 67538c2ecf20Sopenharmony_ci { 67548c2ecf20Sopenharmony_ci .procname = "accept_ra_rt_info_min_plen", 67558c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_ra_rt_info_min_plen, 67568c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67578c2ecf20Sopenharmony_ci .mode = 0644, 67588c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 67598c2ecf20Sopenharmony_ci }, 67608c2ecf20Sopenharmony_ci { 67618c2ecf20Sopenharmony_ci .procname = "accept_ra_rt_info_max_plen", 67628c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_ra_rt_info_max_plen, 67638c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67648c2ecf20Sopenharmony_ci .mode = 0644, 67658c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 67668c2ecf20Sopenharmony_ci }, 67678c2ecf20Sopenharmony_ci#endif 67688c2ecf20Sopenharmony_ci#endif 67698c2ecf20Sopenharmony_ci { 67708c2ecf20Sopenharmony_ci .procname = "proxy_ndp", 67718c2ecf20Sopenharmony_ci .data = &ipv6_devconf.proxy_ndp, 67728c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67738c2ecf20Sopenharmony_ci .mode = 0644, 67748c2ecf20Sopenharmony_ci .proc_handler = addrconf_sysctl_proxy_ndp, 67758c2ecf20Sopenharmony_ci }, 67768c2ecf20Sopenharmony_ci { 67778c2ecf20Sopenharmony_ci .procname = "accept_source_route", 67788c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_source_route, 67798c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67808c2ecf20Sopenharmony_ci .mode = 0644, 67818c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 67828c2ecf20Sopenharmony_ci }, 67838c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 67848c2ecf20Sopenharmony_ci { 67858c2ecf20Sopenharmony_ci .procname = "optimistic_dad", 67868c2ecf20Sopenharmony_ci .data = &ipv6_devconf.optimistic_dad, 67878c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67888c2ecf20Sopenharmony_ci .mode = 0644, 67898c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 67908c2ecf20Sopenharmony_ci }, 67918c2ecf20Sopenharmony_ci { 67928c2ecf20Sopenharmony_ci .procname = "use_optimistic", 67938c2ecf20Sopenharmony_ci .data = &ipv6_devconf.use_optimistic, 67948c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 67958c2ecf20Sopenharmony_ci .mode = 0644, 67968c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 67978c2ecf20Sopenharmony_ci }, 67988c2ecf20Sopenharmony_ci#endif 67998c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 68008c2ecf20Sopenharmony_ci { 68018c2ecf20Sopenharmony_ci .procname = "mc_forwarding", 68028c2ecf20Sopenharmony_ci .data = &ipv6_devconf.mc_forwarding, 68038c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68048c2ecf20Sopenharmony_ci .mode = 0444, 68058c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 68068c2ecf20Sopenharmony_ci }, 68078c2ecf20Sopenharmony_ci#endif 68088c2ecf20Sopenharmony_ci { 68098c2ecf20Sopenharmony_ci .procname = "disable_ipv6", 68108c2ecf20Sopenharmony_ci .data = &ipv6_devconf.disable_ipv6, 68118c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68128c2ecf20Sopenharmony_ci .mode = 0644, 68138c2ecf20Sopenharmony_ci .proc_handler = addrconf_sysctl_disable, 68148c2ecf20Sopenharmony_ci }, 68158c2ecf20Sopenharmony_ci { 68168c2ecf20Sopenharmony_ci .procname = "accept_dad", 68178c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_dad, 68188c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68198c2ecf20Sopenharmony_ci .mode = 0644, 68208c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 68218c2ecf20Sopenharmony_ci }, 68228c2ecf20Sopenharmony_ci { 68238c2ecf20Sopenharmony_ci .procname = "force_tllao", 68248c2ecf20Sopenharmony_ci .data = &ipv6_devconf.force_tllao, 68258c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68268c2ecf20Sopenharmony_ci .mode = 0644, 68278c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec 68288c2ecf20Sopenharmony_ci }, 68298c2ecf20Sopenharmony_ci { 68308c2ecf20Sopenharmony_ci .procname = "ndisc_notify", 68318c2ecf20Sopenharmony_ci .data = &ipv6_devconf.ndisc_notify, 68328c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68338c2ecf20Sopenharmony_ci .mode = 0644, 68348c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec 68358c2ecf20Sopenharmony_ci }, 68368c2ecf20Sopenharmony_ci { 68378c2ecf20Sopenharmony_ci .procname = "suppress_frag_ndisc", 68388c2ecf20Sopenharmony_ci .data = &ipv6_devconf.suppress_frag_ndisc, 68398c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68408c2ecf20Sopenharmony_ci .mode = 0644, 68418c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec 68428c2ecf20Sopenharmony_ci }, 68438c2ecf20Sopenharmony_ci { 68448c2ecf20Sopenharmony_ci .procname = "accept_ra_from_local", 68458c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_ra_from_local, 68468c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68478c2ecf20Sopenharmony_ci .mode = 0644, 68488c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 68498c2ecf20Sopenharmony_ci }, 68508c2ecf20Sopenharmony_ci { 68518c2ecf20Sopenharmony_ci .procname = "accept_ra_mtu", 68528c2ecf20Sopenharmony_ci .data = &ipv6_devconf.accept_ra_mtu, 68538c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68548c2ecf20Sopenharmony_ci .mode = 0644, 68558c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 68568c2ecf20Sopenharmony_ci }, 68578c2ecf20Sopenharmony_ci { 68588c2ecf20Sopenharmony_ci .procname = "stable_secret", 68598c2ecf20Sopenharmony_ci .data = &ipv6_devconf.stable_secret, 68608c2ecf20Sopenharmony_ci .maxlen = IPV6_MAX_STRLEN, 68618c2ecf20Sopenharmony_ci .mode = 0600, 68628c2ecf20Sopenharmony_ci .proc_handler = addrconf_sysctl_stable_secret, 68638c2ecf20Sopenharmony_ci }, 68648c2ecf20Sopenharmony_ci { 68658c2ecf20Sopenharmony_ci .procname = "use_oif_addrs_only", 68668c2ecf20Sopenharmony_ci .data = &ipv6_devconf.use_oif_addrs_only, 68678c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68688c2ecf20Sopenharmony_ci .mode = 0644, 68698c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 68708c2ecf20Sopenharmony_ci }, 68718c2ecf20Sopenharmony_ci { 68728c2ecf20Sopenharmony_ci .procname = "ignore_routes_with_linkdown", 68738c2ecf20Sopenharmony_ci .data = &ipv6_devconf.ignore_routes_with_linkdown, 68748c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68758c2ecf20Sopenharmony_ci .mode = 0644, 68768c2ecf20Sopenharmony_ci .proc_handler = addrconf_sysctl_ignore_routes_with_linkdown, 68778c2ecf20Sopenharmony_ci }, 68788c2ecf20Sopenharmony_ci { 68798c2ecf20Sopenharmony_ci .procname = "drop_unicast_in_l2_multicast", 68808c2ecf20Sopenharmony_ci .data = &ipv6_devconf.drop_unicast_in_l2_multicast, 68818c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68828c2ecf20Sopenharmony_ci .mode = 0644, 68838c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 68848c2ecf20Sopenharmony_ci }, 68858c2ecf20Sopenharmony_ci { 68868c2ecf20Sopenharmony_ci .procname = "drop_unsolicited_na", 68878c2ecf20Sopenharmony_ci .data = &ipv6_devconf.drop_unsolicited_na, 68888c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68898c2ecf20Sopenharmony_ci .mode = 0644, 68908c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 68918c2ecf20Sopenharmony_ci }, 68928c2ecf20Sopenharmony_ci { 68938c2ecf20Sopenharmony_ci .procname = "keep_addr_on_down", 68948c2ecf20Sopenharmony_ci .data = &ipv6_devconf.keep_addr_on_down, 68958c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 68968c2ecf20Sopenharmony_ci .mode = 0644, 68978c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 68988c2ecf20Sopenharmony_ci 68998c2ecf20Sopenharmony_ci }, 69008c2ecf20Sopenharmony_ci { 69018c2ecf20Sopenharmony_ci .procname = "seg6_enabled", 69028c2ecf20Sopenharmony_ci .data = &ipv6_devconf.seg6_enabled, 69038c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 69048c2ecf20Sopenharmony_ci .mode = 0644, 69058c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 69068c2ecf20Sopenharmony_ci }, 69078c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 69088c2ecf20Sopenharmony_ci { 69098c2ecf20Sopenharmony_ci .procname = "seg6_require_hmac", 69108c2ecf20Sopenharmony_ci .data = &ipv6_devconf.seg6_require_hmac, 69118c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 69128c2ecf20Sopenharmony_ci .mode = 0644, 69138c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 69148c2ecf20Sopenharmony_ci }, 69158c2ecf20Sopenharmony_ci#endif 69168c2ecf20Sopenharmony_ci { 69178c2ecf20Sopenharmony_ci .procname = "enhanced_dad", 69188c2ecf20Sopenharmony_ci .data = &ipv6_devconf.enhanced_dad, 69198c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 69208c2ecf20Sopenharmony_ci .mode = 0644, 69218c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 69228c2ecf20Sopenharmony_ci }, 69238c2ecf20Sopenharmony_ci { 69248c2ecf20Sopenharmony_ci .procname = "addr_gen_mode", 69258c2ecf20Sopenharmony_ci .data = &ipv6_devconf.addr_gen_mode, 69268c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 69278c2ecf20Sopenharmony_ci .mode = 0644, 69288c2ecf20Sopenharmony_ci .proc_handler = addrconf_sysctl_addr_gen_mode, 69298c2ecf20Sopenharmony_ci }, 69308c2ecf20Sopenharmony_ci { 69318c2ecf20Sopenharmony_ci .procname = "disable_policy", 69328c2ecf20Sopenharmony_ci .data = &ipv6_devconf.disable_policy, 69338c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 69348c2ecf20Sopenharmony_ci .mode = 0644, 69358c2ecf20Sopenharmony_ci .proc_handler = addrconf_sysctl_disable_policy, 69368c2ecf20Sopenharmony_ci }, 69378c2ecf20Sopenharmony_ci { 69388c2ecf20Sopenharmony_ci .procname = "ndisc_tclass", 69398c2ecf20Sopenharmony_ci .data = &ipv6_devconf.ndisc_tclass, 69408c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 69418c2ecf20Sopenharmony_ci .mode = 0644, 69428c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 69438c2ecf20Sopenharmony_ci .extra1 = (void *)SYSCTL_ZERO, 69448c2ecf20Sopenharmony_ci .extra2 = (void *)&two_five_five, 69458c2ecf20Sopenharmony_ci }, 69468c2ecf20Sopenharmony_ci { 69478c2ecf20Sopenharmony_ci .procname = "rpl_seg_enabled", 69488c2ecf20Sopenharmony_ci .data = &ipv6_devconf.rpl_seg_enabled, 69498c2ecf20Sopenharmony_ci .maxlen = sizeof(int), 69508c2ecf20Sopenharmony_ci .mode = 0644, 69518c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 69528c2ecf20Sopenharmony_ci }, 69538c2ecf20Sopenharmony_ci { 69548c2ecf20Sopenharmony_ci /* sentinel */ 69558c2ecf20Sopenharmony_ci } 69568c2ecf20Sopenharmony_ci}; 69578c2ecf20Sopenharmony_ci 69588c2ecf20Sopenharmony_cistatic int __addrconf_sysctl_register(struct net *net, char *dev_name, 69598c2ecf20Sopenharmony_ci struct inet6_dev *idev, struct ipv6_devconf *p) 69608c2ecf20Sopenharmony_ci{ 69618c2ecf20Sopenharmony_ci int i, ifindex; 69628c2ecf20Sopenharmony_ci struct ctl_table *table; 69638c2ecf20Sopenharmony_ci char path[sizeof("net/ipv6/conf/") + IFNAMSIZ]; 69648c2ecf20Sopenharmony_ci 69658c2ecf20Sopenharmony_ci table = kmemdup(addrconf_sysctl, sizeof(addrconf_sysctl), GFP_KERNEL); 69668c2ecf20Sopenharmony_ci if (!table) 69678c2ecf20Sopenharmony_ci goto out; 69688c2ecf20Sopenharmony_ci 69698c2ecf20Sopenharmony_ci for (i = 0; table[i].data; i++) { 69708c2ecf20Sopenharmony_ci table[i].data += (char *)p - (char *)&ipv6_devconf; 69718c2ecf20Sopenharmony_ci /* If one of these is already set, then it is not safe to 69728c2ecf20Sopenharmony_ci * overwrite either of them: this makes proc_dointvec_minmax 69738c2ecf20Sopenharmony_ci * usable. 69748c2ecf20Sopenharmony_ci */ 69758c2ecf20Sopenharmony_ci if (!table[i].extra1 && !table[i].extra2) { 69768c2ecf20Sopenharmony_ci table[i].extra1 = idev; /* embedded; no ref */ 69778c2ecf20Sopenharmony_ci table[i].extra2 = net; 69788c2ecf20Sopenharmony_ci } 69798c2ecf20Sopenharmony_ci } 69808c2ecf20Sopenharmony_ci 69818c2ecf20Sopenharmony_ci snprintf(path, sizeof(path), "net/ipv6/conf/%s", dev_name); 69828c2ecf20Sopenharmony_ci 69838c2ecf20Sopenharmony_ci p->sysctl_header = register_net_sysctl(net, path, table); 69848c2ecf20Sopenharmony_ci if (!p->sysctl_header) 69858c2ecf20Sopenharmony_ci goto free; 69868c2ecf20Sopenharmony_ci 69878c2ecf20Sopenharmony_ci if (!strcmp(dev_name, "all")) 69888c2ecf20Sopenharmony_ci ifindex = NETCONFA_IFINDEX_ALL; 69898c2ecf20Sopenharmony_ci else if (!strcmp(dev_name, "default")) 69908c2ecf20Sopenharmony_ci ifindex = NETCONFA_IFINDEX_DEFAULT; 69918c2ecf20Sopenharmony_ci else 69928c2ecf20Sopenharmony_ci ifindex = idev->dev->ifindex; 69938c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, 69948c2ecf20Sopenharmony_ci ifindex, p); 69958c2ecf20Sopenharmony_ci return 0; 69968c2ecf20Sopenharmony_ci 69978c2ecf20Sopenharmony_cifree: 69988c2ecf20Sopenharmony_ci kfree(table); 69998c2ecf20Sopenharmony_ciout: 70008c2ecf20Sopenharmony_ci return -ENOBUFS; 70018c2ecf20Sopenharmony_ci} 70028c2ecf20Sopenharmony_ci 70038c2ecf20Sopenharmony_cistatic void __addrconf_sysctl_unregister(struct net *net, 70048c2ecf20Sopenharmony_ci struct ipv6_devconf *p, int ifindex) 70058c2ecf20Sopenharmony_ci{ 70068c2ecf20Sopenharmony_ci struct ctl_table *table; 70078c2ecf20Sopenharmony_ci 70088c2ecf20Sopenharmony_ci if (!p->sysctl_header) 70098c2ecf20Sopenharmony_ci return; 70108c2ecf20Sopenharmony_ci 70118c2ecf20Sopenharmony_ci table = p->sysctl_header->ctl_table_arg; 70128c2ecf20Sopenharmony_ci unregister_net_sysctl_table(p->sysctl_header); 70138c2ecf20Sopenharmony_ci p->sysctl_header = NULL; 70148c2ecf20Sopenharmony_ci kfree(table); 70158c2ecf20Sopenharmony_ci 70168c2ecf20Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL); 70178c2ecf20Sopenharmony_ci} 70188c2ecf20Sopenharmony_ci 70198c2ecf20Sopenharmony_cistatic int addrconf_sysctl_register(struct inet6_dev *idev) 70208c2ecf20Sopenharmony_ci{ 70218c2ecf20Sopenharmony_ci int err; 70228c2ecf20Sopenharmony_ci 70238c2ecf20Sopenharmony_ci if (!sysctl_dev_name_is_allowed(idev->dev->name)) 70248c2ecf20Sopenharmony_ci return -EINVAL; 70258c2ecf20Sopenharmony_ci 70268c2ecf20Sopenharmony_ci err = neigh_sysctl_register(idev->dev, idev->nd_parms, 70278c2ecf20Sopenharmony_ci &ndisc_ifinfo_sysctl_change); 70288c2ecf20Sopenharmony_ci if (err) 70298c2ecf20Sopenharmony_ci return err; 70308c2ecf20Sopenharmony_ci err = __addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name, 70318c2ecf20Sopenharmony_ci idev, &idev->cnf); 70328c2ecf20Sopenharmony_ci if (err) 70338c2ecf20Sopenharmony_ci neigh_sysctl_unregister(idev->nd_parms); 70348c2ecf20Sopenharmony_ci 70358c2ecf20Sopenharmony_ci return err; 70368c2ecf20Sopenharmony_ci} 70378c2ecf20Sopenharmony_ci 70388c2ecf20Sopenharmony_cistatic void addrconf_sysctl_unregister(struct inet6_dev *idev) 70398c2ecf20Sopenharmony_ci{ 70408c2ecf20Sopenharmony_ci __addrconf_sysctl_unregister(dev_net(idev->dev), &idev->cnf, 70418c2ecf20Sopenharmony_ci idev->dev->ifindex); 70428c2ecf20Sopenharmony_ci neigh_sysctl_unregister(idev->nd_parms); 70438c2ecf20Sopenharmony_ci} 70448c2ecf20Sopenharmony_ci 70458c2ecf20Sopenharmony_ci 70468c2ecf20Sopenharmony_ci#endif 70478c2ecf20Sopenharmony_ci 70488c2ecf20Sopenharmony_cistatic int __net_init addrconf_init_net(struct net *net) 70498c2ecf20Sopenharmony_ci{ 70508c2ecf20Sopenharmony_ci int err = -ENOMEM; 70518c2ecf20Sopenharmony_ci struct ipv6_devconf *all, *dflt; 70528c2ecf20Sopenharmony_ci 70538c2ecf20Sopenharmony_ci all = kmemdup(&ipv6_devconf, sizeof(ipv6_devconf), GFP_KERNEL); 70548c2ecf20Sopenharmony_ci if (!all) 70558c2ecf20Sopenharmony_ci goto err_alloc_all; 70568c2ecf20Sopenharmony_ci 70578c2ecf20Sopenharmony_ci dflt = kmemdup(&ipv6_devconf_dflt, sizeof(ipv6_devconf_dflt), GFP_KERNEL); 70588c2ecf20Sopenharmony_ci if (!dflt) 70598c2ecf20Sopenharmony_ci goto err_alloc_dflt; 70608c2ecf20Sopenharmony_ci 70618c2ecf20Sopenharmony_ci if (!net_eq(net, &init_net)) { 70628c2ecf20Sopenharmony_ci switch (net_inherit_devconf()) { 70638c2ecf20Sopenharmony_ci case 1: /* copy from init_net */ 70648c2ecf20Sopenharmony_ci memcpy(all, init_net.ipv6.devconf_all, 70658c2ecf20Sopenharmony_ci sizeof(ipv6_devconf)); 70668c2ecf20Sopenharmony_ci memcpy(dflt, init_net.ipv6.devconf_dflt, 70678c2ecf20Sopenharmony_ci sizeof(ipv6_devconf_dflt)); 70688c2ecf20Sopenharmony_ci break; 70698c2ecf20Sopenharmony_ci case 3: /* copy from the current netns */ 70708c2ecf20Sopenharmony_ci memcpy(all, current->nsproxy->net_ns->ipv6.devconf_all, 70718c2ecf20Sopenharmony_ci sizeof(ipv6_devconf)); 70728c2ecf20Sopenharmony_ci memcpy(dflt, 70738c2ecf20Sopenharmony_ci current->nsproxy->net_ns->ipv6.devconf_dflt, 70748c2ecf20Sopenharmony_ci sizeof(ipv6_devconf_dflt)); 70758c2ecf20Sopenharmony_ci break; 70768c2ecf20Sopenharmony_ci case 0: 70778c2ecf20Sopenharmony_ci case 2: 70788c2ecf20Sopenharmony_ci /* use compiled values */ 70798c2ecf20Sopenharmony_ci break; 70808c2ecf20Sopenharmony_ci } 70818c2ecf20Sopenharmony_ci } 70828c2ecf20Sopenharmony_ci 70838c2ecf20Sopenharmony_ci /* these will be inherited by all namespaces */ 70848c2ecf20Sopenharmony_ci dflt->autoconf = ipv6_defaults.autoconf; 70858c2ecf20Sopenharmony_ci dflt->disable_ipv6 = ipv6_defaults.disable_ipv6; 70868c2ecf20Sopenharmony_ci 70878c2ecf20Sopenharmony_ci dflt->stable_secret.initialized = false; 70888c2ecf20Sopenharmony_ci all->stable_secret.initialized = false; 70898c2ecf20Sopenharmony_ci 70908c2ecf20Sopenharmony_ci net->ipv6.devconf_all = all; 70918c2ecf20Sopenharmony_ci net->ipv6.devconf_dflt = dflt; 70928c2ecf20Sopenharmony_ci 70938c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 70948c2ecf20Sopenharmony_ci err = __addrconf_sysctl_register(net, "all", NULL, all); 70958c2ecf20Sopenharmony_ci if (err < 0) 70968c2ecf20Sopenharmony_ci goto err_reg_all; 70978c2ecf20Sopenharmony_ci 70988c2ecf20Sopenharmony_ci err = __addrconf_sysctl_register(net, "default", NULL, dflt); 70998c2ecf20Sopenharmony_ci if (err < 0) 71008c2ecf20Sopenharmony_ci goto err_reg_dflt; 71018c2ecf20Sopenharmony_ci#endif 71028c2ecf20Sopenharmony_ci return 0; 71038c2ecf20Sopenharmony_ci 71048c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 71058c2ecf20Sopenharmony_cierr_reg_dflt: 71068c2ecf20Sopenharmony_ci __addrconf_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL); 71078c2ecf20Sopenharmony_cierr_reg_all: 71088c2ecf20Sopenharmony_ci kfree(dflt); 71098c2ecf20Sopenharmony_ci#endif 71108c2ecf20Sopenharmony_cierr_alloc_dflt: 71118c2ecf20Sopenharmony_ci kfree(all); 71128c2ecf20Sopenharmony_cierr_alloc_all: 71138c2ecf20Sopenharmony_ci return err; 71148c2ecf20Sopenharmony_ci} 71158c2ecf20Sopenharmony_ci 71168c2ecf20Sopenharmony_cistatic void __net_exit addrconf_exit_net(struct net *net) 71178c2ecf20Sopenharmony_ci{ 71188c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL 71198c2ecf20Sopenharmony_ci __addrconf_sysctl_unregister(net, net->ipv6.devconf_dflt, 71208c2ecf20Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT); 71218c2ecf20Sopenharmony_ci __addrconf_sysctl_unregister(net, net->ipv6.devconf_all, 71228c2ecf20Sopenharmony_ci NETCONFA_IFINDEX_ALL); 71238c2ecf20Sopenharmony_ci#endif 71248c2ecf20Sopenharmony_ci kfree(net->ipv6.devconf_dflt); 71258c2ecf20Sopenharmony_ci kfree(net->ipv6.devconf_all); 71268c2ecf20Sopenharmony_ci} 71278c2ecf20Sopenharmony_ci 71288c2ecf20Sopenharmony_cistatic struct pernet_operations addrconf_ops = { 71298c2ecf20Sopenharmony_ci .init = addrconf_init_net, 71308c2ecf20Sopenharmony_ci .exit = addrconf_exit_net, 71318c2ecf20Sopenharmony_ci}; 71328c2ecf20Sopenharmony_ci 71338c2ecf20Sopenharmony_cistatic struct rtnl_af_ops inet6_ops __read_mostly = { 71348c2ecf20Sopenharmony_ci .family = AF_INET6, 71358c2ecf20Sopenharmony_ci .fill_link_af = inet6_fill_link_af, 71368c2ecf20Sopenharmony_ci .get_link_af_size = inet6_get_link_af_size, 71378c2ecf20Sopenharmony_ci .validate_link_af = inet6_validate_link_af, 71388c2ecf20Sopenharmony_ci .set_link_af = inet6_set_link_af, 71398c2ecf20Sopenharmony_ci}; 71408c2ecf20Sopenharmony_ci 71418c2ecf20Sopenharmony_ci/* 71428c2ecf20Sopenharmony_ci * Init / cleanup code 71438c2ecf20Sopenharmony_ci */ 71448c2ecf20Sopenharmony_ci 71458c2ecf20Sopenharmony_ciint __init addrconf_init(void) 71468c2ecf20Sopenharmony_ci{ 71478c2ecf20Sopenharmony_ci struct inet6_dev *idev; 71488c2ecf20Sopenharmony_ci int i, err; 71498c2ecf20Sopenharmony_ci 71508c2ecf20Sopenharmony_ci err = ipv6_addr_label_init(); 71518c2ecf20Sopenharmony_ci if (err < 0) { 71528c2ecf20Sopenharmony_ci pr_crit("%s: cannot initialize default policy table: %d\n", 71538c2ecf20Sopenharmony_ci __func__, err); 71548c2ecf20Sopenharmony_ci goto out; 71558c2ecf20Sopenharmony_ci } 71568c2ecf20Sopenharmony_ci 71578c2ecf20Sopenharmony_ci err = register_pernet_subsys(&addrconf_ops); 71588c2ecf20Sopenharmony_ci if (err < 0) 71598c2ecf20Sopenharmony_ci goto out_addrlabel; 71608c2ecf20Sopenharmony_ci 71618c2ecf20Sopenharmony_ci addrconf_wq = create_workqueue("ipv6_addrconf"); 71628c2ecf20Sopenharmony_ci if (!addrconf_wq) { 71638c2ecf20Sopenharmony_ci err = -ENOMEM; 71648c2ecf20Sopenharmony_ci goto out_nowq; 71658c2ecf20Sopenharmony_ci } 71668c2ecf20Sopenharmony_ci 71678c2ecf20Sopenharmony_ci /* The addrconf netdev notifier requires that loopback_dev 71688c2ecf20Sopenharmony_ci * has it's ipv6 private information allocated and setup 71698c2ecf20Sopenharmony_ci * before it can bring up and give link-local addresses 71708c2ecf20Sopenharmony_ci * to other devices which are up. 71718c2ecf20Sopenharmony_ci * 71728c2ecf20Sopenharmony_ci * Unfortunately, loopback_dev is not necessarily the first 71738c2ecf20Sopenharmony_ci * entry in the global dev_base list of net devices. In fact, 71748c2ecf20Sopenharmony_ci * it is likely to be the very last entry on that list. 71758c2ecf20Sopenharmony_ci * So this causes the notifier registry below to try and 71768c2ecf20Sopenharmony_ci * give link-local addresses to all devices besides loopback_dev 71778c2ecf20Sopenharmony_ci * first, then loopback_dev, which cases all the non-loopback_dev 71788c2ecf20Sopenharmony_ci * devices to fail to get a link-local address. 71798c2ecf20Sopenharmony_ci * 71808c2ecf20Sopenharmony_ci * So, as a temporary fix, allocate the ipv6 structure for 71818c2ecf20Sopenharmony_ci * loopback_dev first by hand. 71828c2ecf20Sopenharmony_ci * Longer term, all of the dependencies ipv6 has upon the loopback 71838c2ecf20Sopenharmony_ci * device and it being up should be removed. 71848c2ecf20Sopenharmony_ci */ 71858c2ecf20Sopenharmony_ci rtnl_lock(); 71868c2ecf20Sopenharmony_ci idev = ipv6_add_dev(init_net.loopback_dev); 71878c2ecf20Sopenharmony_ci rtnl_unlock(); 71888c2ecf20Sopenharmony_ci if (IS_ERR(idev)) { 71898c2ecf20Sopenharmony_ci err = PTR_ERR(idev); 71908c2ecf20Sopenharmony_ci goto errlo; 71918c2ecf20Sopenharmony_ci } 71928c2ecf20Sopenharmony_ci 71938c2ecf20Sopenharmony_ci ip6_route_init_special_entries(); 71948c2ecf20Sopenharmony_ci 71958c2ecf20Sopenharmony_ci for (i = 0; i < IN6_ADDR_HSIZE; i++) 71968c2ecf20Sopenharmony_ci INIT_HLIST_HEAD(&inet6_addr_lst[i]); 71978c2ecf20Sopenharmony_ci 71988c2ecf20Sopenharmony_ci register_netdevice_notifier(&ipv6_dev_notf); 71998c2ecf20Sopenharmony_ci 72008c2ecf20Sopenharmony_ci addrconf_verify(); 72018c2ecf20Sopenharmony_ci 72028c2ecf20Sopenharmony_ci rtnl_af_register(&inet6_ops); 72038c2ecf20Sopenharmony_ci 72048c2ecf20Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETLINK, 72058c2ecf20Sopenharmony_ci NULL, inet6_dump_ifinfo, 0); 72068c2ecf20Sopenharmony_ci if (err < 0) 72078c2ecf20Sopenharmony_ci goto errout; 72088c2ecf20Sopenharmony_ci 72098c2ecf20Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWADDR, 72108c2ecf20Sopenharmony_ci inet6_rtm_newaddr, NULL, 0); 72118c2ecf20Sopenharmony_ci if (err < 0) 72128c2ecf20Sopenharmony_ci goto errout; 72138c2ecf20Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELADDR, 72148c2ecf20Sopenharmony_ci inet6_rtm_deladdr, NULL, 0); 72158c2ecf20Sopenharmony_ci if (err < 0) 72168c2ecf20Sopenharmony_ci goto errout; 72178c2ecf20Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDR, 72188c2ecf20Sopenharmony_ci inet6_rtm_getaddr, inet6_dump_ifaddr, 72198c2ecf20Sopenharmony_ci RTNL_FLAG_DOIT_UNLOCKED); 72208c2ecf20Sopenharmony_ci if (err < 0) 72218c2ecf20Sopenharmony_ci goto errout; 72228c2ecf20Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETMULTICAST, 72238c2ecf20Sopenharmony_ci NULL, inet6_dump_ifmcaddr, 0); 72248c2ecf20Sopenharmony_ci if (err < 0) 72258c2ecf20Sopenharmony_ci goto errout; 72268c2ecf20Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETANYCAST, 72278c2ecf20Sopenharmony_ci NULL, inet6_dump_ifacaddr, 0); 72288c2ecf20Sopenharmony_ci if (err < 0) 72298c2ecf20Sopenharmony_ci goto errout; 72308c2ecf20Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETNETCONF, 72318c2ecf20Sopenharmony_ci inet6_netconf_get_devconf, 72328c2ecf20Sopenharmony_ci inet6_netconf_dump_devconf, 72338c2ecf20Sopenharmony_ci RTNL_FLAG_DOIT_UNLOCKED); 72348c2ecf20Sopenharmony_ci if (err < 0) 72358c2ecf20Sopenharmony_ci goto errout; 72368c2ecf20Sopenharmony_ci err = ipv6_addr_label_rtnl_register(); 72378c2ecf20Sopenharmony_ci if (err < 0) 72388c2ecf20Sopenharmony_ci goto errout; 72398c2ecf20Sopenharmony_ci 72408c2ecf20Sopenharmony_ci return 0; 72418c2ecf20Sopenharmony_cierrout: 72428c2ecf20Sopenharmony_ci rtnl_unregister_all(PF_INET6); 72438c2ecf20Sopenharmony_ci rtnl_af_unregister(&inet6_ops); 72448c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&ipv6_dev_notf); 72458c2ecf20Sopenharmony_cierrlo: 72468c2ecf20Sopenharmony_ci destroy_workqueue(addrconf_wq); 72478c2ecf20Sopenharmony_ciout_nowq: 72488c2ecf20Sopenharmony_ci unregister_pernet_subsys(&addrconf_ops); 72498c2ecf20Sopenharmony_ciout_addrlabel: 72508c2ecf20Sopenharmony_ci ipv6_addr_label_cleanup(); 72518c2ecf20Sopenharmony_ciout: 72528c2ecf20Sopenharmony_ci return err; 72538c2ecf20Sopenharmony_ci} 72548c2ecf20Sopenharmony_ci 72558c2ecf20Sopenharmony_civoid addrconf_cleanup(void) 72568c2ecf20Sopenharmony_ci{ 72578c2ecf20Sopenharmony_ci struct net_device *dev; 72588c2ecf20Sopenharmony_ci int i; 72598c2ecf20Sopenharmony_ci 72608c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&ipv6_dev_notf); 72618c2ecf20Sopenharmony_ci unregister_pernet_subsys(&addrconf_ops); 72628c2ecf20Sopenharmony_ci ipv6_addr_label_cleanup(); 72638c2ecf20Sopenharmony_ci 72648c2ecf20Sopenharmony_ci rtnl_af_unregister(&inet6_ops); 72658c2ecf20Sopenharmony_ci 72668c2ecf20Sopenharmony_ci rtnl_lock(); 72678c2ecf20Sopenharmony_ci 72688c2ecf20Sopenharmony_ci /* clean dev list */ 72698c2ecf20Sopenharmony_ci for_each_netdev(&init_net, dev) { 72708c2ecf20Sopenharmony_ci if (__in6_dev_get(dev) == NULL) 72718c2ecf20Sopenharmony_ci continue; 72728c2ecf20Sopenharmony_ci addrconf_ifdown(dev, true); 72738c2ecf20Sopenharmony_ci } 72748c2ecf20Sopenharmony_ci addrconf_ifdown(init_net.loopback_dev, true); 72758c2ecf20Sopenharmony_ci 72768c2ecf20Sopenharmony_ci /* 72778c2ecf20Sopenharmony_ci * Check hash table. 72788c2ecf20Sopenharmony_ci */ 72798c2ecf20Sopenharmony_ci spin_lock_bh(&addrconf_hash_lock); 72808c2ecf20Sopenharmony_ci for (i = 0; i < IN6_ADDR_HSIZE; i++) 72818c2ecf20Sopenharmony_ci WARN_ON(!hlist_empty(&inet6_addr_lst[i])); 72828c2ecf20Sopenharmony_ci spin_unlock_bh(&addrconf_hash_lock); 72838c2ecf20Sopenharmony_ci cancel_delayed_work(&addr_chk_work); 72848c2ecf20Sopenharmony_ci rtnl_unlock(); 72858c2ecf20Sopenharmony_ci 72868c2ecf20Sopenharmony_ci destroy_workqueue(addrconf_wq); 72878c2ecf20Sopenharmony_ci} 7288