162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IPv6 Address [auto]configuration 462306a36Sopenharmony_ci * Linux INET6 implementation 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 862306a36Sopenharmony_ci * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * Changes: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Janos Farkas : delete timer on ifdown 1562306a36Sopenharmony_ci * <chexum@bankinf.banki.hu> 1662306a36Sopenharmony_ci * Andi Kleen : kill double kfree on module 1762306a36Sopenharmony_ci * unload. 1862306a36Sopenharmony_ci * Maciej W. Rozycki : FDDI support 1962306a36Sopenharmony_ci * sekiya@USAGI : Don't send too many RS 2062306a36Sopenharmony_ci * packets. 2162306a36Sopenharmony_ci * yoshfuji@USAGI : Fixed interval between DAD 2262306a36Sopenharmony_ci * packets. 2362306a36Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI : improved accuracy of 2462306a36Sopenharmony_ci * address validation timer. 2562306a36Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI : Privacy Extensions (RFC3041) 2662306a36Sopenharmony_ci * support. 2762306a36Sopenharmony_ci * Yuji SEKIYA @USAGI : Don't assign a same IPv6 2862306a36Sopenharmony_ci * address on a same interface. 2962306a36Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI : ARCnet support 3062306a36Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI : convert /proc/net/if_inet6 to 3162306a36Sopenharmony_ci * seq_file. 3262306a36Sopenharmony_ci * YOSHIFUJI Hideaki @USAGI : improved source address 3362306a36Sopenharmony_ci * selection; consider scope, 3462306a36Sopenharmony_ci * status etc. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <linux/errno.h> 4062306a36Sopenharmony_ci#include <linux/types.h> 4162306a36Sopenharmony_ci#include <linux/kernel.h> 4262306a36Sopenharmony_ci#include <linux/sched/signal.h> 4362306a36Sopenharmony_ci#include <linux/socket.h> 4462306a36Sopenharmony_ci#include <linux/sockios.h> 4562306a36Sopenharmony_ci#include <linux/net.h> 4662306a36Sopenharmony_ci#include <linux/inet.h> 4762306a36Sopenharmony_ci#include <linux/in6.h> 4862306a36Sopenharmony_ci#include <linux/netdevice.h> 4962306a36Sopenharmony_ci#include <linux/if_addr.h> 5062306a36Sopenharmony_ci#include <linux/if_arp.h> 5162306a36Sopenharmony_ci#include <linux/if_arcnet.h> 5262306a36Sopenharmony_ci#include <linux/if_infiniband.h> 5362306a36Sopenharmony_ci#include <linux/route.h> 5462306a36Sopenharmony_ci#include <linux/inetdevice.h> 5562306a36Sopenharmony_ci#include <linux/init.h> 5662306a36Sopenharmony_ci#include <linux/slab.h> 5762306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 5862306a36Sopenharmony_ci#include <linux/sysctl.h> 5962306a36Sopenharmony_ci#endif 6062306a36Sopenharmony_ci#include <linux/capability.h> 6162306a36Sopenharmony_ci#include <linux/delay.h> 6262306a36Sopenharmony_ci#include <linux/notifier.h> 6362306a36Sopenharmony_ci#include <linux/string.h> 6462306a36Sopenharmony_ci#include <linux/hash.h> 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#include <net/net_namespace.h> 6762306a36Sopenharmony_ci#include <net/sock.h> 6862306a36Sopenharmony_ci#include <net/snmp.h> 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#include <net/6lowpan.h> 7162306a36Sopenharmony_ci#include <net/firewire.h> 7262306a36Sopenharmony_ci#include <net/ipv6.h> 7362306a36Sopenharmony_ci#include <net/protocol.h> 7462306a36Sopenharmony_ci#include <net/ndisc.h> 7562306a36Sopenharmony_ci#include <net/ip6_route.h> 7662306a36Sopenharmony_ci#include <net/addrconf.h> 7762306a36Sopenharmony_ci#include <net/tcp.h> 7862306a36Sopenharmony_ci#include <net/ip.h> 7962306a36Sopenharmony_ci#include <net/netlink.h> 8062306a36Sopenharmony_ci#include <net/pkt_sched.h> 8162306a36Sopenharmony_ci#include <net/l3mdev.h> 8262306a36Sopenharmony_ci#include <linux/if_tunnel.h> 8362306a36Sopenharmony_ci#include <linux/rtnetlink.h> 8462306a36Sopenharmony_ci#include <linux/netconf.h> 8562306a36Sopenharmony_ci#include <linux/random.h> 8662306a36Sopenharmony_ci#include <linux/uaccess.h> 8762306a36Sopenharmony_ci#include <asm/unaligned.h> 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#include <linux/proc_fs.h> 9062306a36Sopenharmony_ci#include <linux/seq_file.h> 9162306a36Sopenharmony_ci#include <linux/export.h> 9262306a36Sopenharmony_ci#include <linux/ioam6.h> 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define INFINITY_LIFE_TIME 0xFFFFFFFF 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define IPV6_MAX_STRLEN \ 9762306a36Sopenharmony_ci sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic inline u32 cstamp_delta(unsigned long cstamp) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic inline s32 rfc3315_s14_backoff_init(s32 irt) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci /* multiply 'initial retransmission time' by 0.9 .. 1.1 */ 10762306a36Sopenharmony_ci u64 tmp = get_random_u32_inclusive(900000, 1100000) * (u64)irt; 10862306a36Sopenharmony_ci do_div(tmp, 1000000); 10962306a36Sopenharmony_ci return (s32)tmp; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline s32 rfc3315_s14_backoff_update(s32 rt, s32 mrt) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci /* multiply 'retransmission timeout' by 1.9 .. 2.1 */ 11562306a36Sopenharmony_ci u64 tmp = get_random_u32_inclusive(1900000, 2100000) * (u64)rt; 11662306a36Sopenharmony_ci do_div(tmp, 1000000); 11762306a36Sopenharmony_ci if ((s32)tmp > mrt) { 11862306a36Sopenharmony_ci /* multiply 'maximum retransmission time' by 0.9 .. 1.1 */ 11962306a36Sopenharmony_ci tmp = get_random_u32_inclusive(900000, 1100000) * (u64)mrt; 12062306a36Sopenharmony_ci do_div(tmp, 1000000); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci return (s32)tmp; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 12662306a36Sopenharmony_cistatic int addrconf_sysctl_register(struct inet6_dev *idev); 12762306a36Sopenharmony_cistatic void addrconf_sysctl_unregister(struct inet6_dev *idev); 12862306a36Sopenharmony_ci#else 12962306a36Sopenharmony_cistatic inline int addrconf_sysctl_register(struct inet6_dev *idev) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic inline void addrconf_sysctl_unregister(struct inet6_dev *idev) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci#endif 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void ipv6_gen_rnd_iid(struct in6_addr *addr); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int ipv6_generate_eui64(u8 *eui, struct net_device *dev); 14262306a36Sopenharmony_cistatic int ipv6_count_addresses(const struct inet6_dev *idev); 14362306a36Sopenharmony_cistatic int ipv6_generate_stable_address(struct in6_addr *addr, 14462306a36Sopenharmony_ci u8 dad_count, 14562306a36Sopenharmony_ci const struct inet6_dev *idev); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#define IN6_ADDR_HSIZE_SHIFT 8 14862306a36Sopenharmony_ci#define IN6_ADDR_HSIZE (1 << IN6_ADDR_HSIZE_SHIFT) 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void addrconf_verify(struct net *net); 15162306a36Sopenharmony_cistatic void addrconf_verify_rtnl(struct net *net); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic struct workqueue_struct *addrconf_wq; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void addrconf_join_anycast(struct inet6_ifaddr *ifp); 15662306a36Sopenharmony_cistatic void addrconf_leave_anycast(struct inet6_ifaddr *ifp); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void addrconf_type_change(struct net_device *dev, 15962306a36Sopenharmony_ci unsigned long event); 16062306a36Sopenharmony_cistatic int addrconf_ifdown(struct net_device *dev, bool unregister); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic struct fib6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, 16362306a36Sopenharmony_ci int plen, 16462306a36Sopenharmony_ci const struct net_device *dev, 16562306a36Sopenharmony_ci u32 flags, u32 noflags, 16662306a36Sopenharmony_ci bool no_gw); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void addrconf_dad_start(struct inet6_ifaddr *ifp); 16962306a36Sopenharmony_cistatic void addrconf_dad_work(struct work_struct *w); 17062306a36Sopenharmony_cistatic void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id, 17162306a36Sopenharmony_ci bool send_na); 17262306a36Sopenharmony_cistatic void addrconf_dad_run(struct inet6_dev *idev, bool restart); 17362306a36Sopenharmony_cistatic void addrconf_rs_timer(struct timer_list *t); 17462306a36Sopenharmony_cistatic void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa); 17562306a36Sopenharmony_cistatic void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void inet6_prefix_notify(int event, struct inet6_dev *idev, 17862306a36Sopenharmony_ci struct prefix_info *pinfo); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic struct ipv6_devconf ipv6_devconf __read_mostly = { 18162306a36Sopenharmony_ci .forwarding = 0, 18262306a36Sopenharmony_ci .hop_limit = IPV6_DEFAULT_HOPLIMIT, 18362306a36Sopenharmony_ci .mtu6 = IPV6_MIN_MTU, 18462306a36Sopenharmony_ci .accept_ra = 1, 18562306a36Sopenharmony_ci .accept_redirects = 1, 18662306a36Sopenharmony_ci .autoconf = 1, 18762306a36Sopenharmony_ci .force_mld_version = 0, 18862306a36Sopenharmony_ci .mldv1_unsolicited_report_interval = 10 * HZ, 18962306a36Sopenharmony_ci .mldv2_unsolicited_report_interval = HZ, 19062306a36Sopenharmony_ci .dad_transmits = 1, 19162306a36Sopenharmony_ci .rtr_solicits = MAX_RTR_SOLICITATIONS, 19262306a36Sopenharmony_ci .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, 19362306a36Sopenharmony_ci .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL, 19462306a36Sopenharmony_ci .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY, 19562306a36Sopenharmony_ci .use_tempaddr = 0, 19662306a36Sopenharmony_ci .temp_valid_lft = TEMP_VALID_LIFETIME, 19762306a36Sopenharmony_ci .temp_prefered_lft = TEMP_PREFERRED_LIFETIME, 19862306a36Sopenharmony_ci .regen_max_retry = REGEN_MAX_RETRY, 19962306a36Sopenharmony_ci .max_desync_factor = MAX_DESYNC_FACTOR, 20062306a36Sopenharmony_ci .max_addresses = IPV6_MAX_ADDRESSES, 20162306a36Sopenharmony_ci .accept_ra_defrtr = 1, 20262306a36Sopenharmony_ci .ra_defrtr_metric = IP6_RT_PRIO_USER, 20362306a36Sopenharmony_ci .accept_ra_from_local = 0, 20462306a36Sopenharmony_ci .accept_ra_min_hop_limit= 1, 20562306a36Sopenharmony_ci .accept_ra_min_lft = 0, 20662306a36Sopenharmony_ci .accept_ra_pinfo = 1, 20762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 20862306a36Sopenharmony_ci .accept_ra_rtr_pref = 1, 20962306a36Sopenharmony_ci .rtr_probe_interval = 60 * HZ, 21062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 21162306a36Sopenharmony_ci .accept_ra_rt_info_min_plen = 0, 21262306a36Sopenharmony_ci .accept_ra_rt_info_max_plen = 0, 21362306a36Sopenharmony_ci#endif 21462306a36Sopenharmony_ci#endif 21562306a36Sopenharmony_ci .proxy_ndp = 0, 21662306a36Sopenharmony_ci .accept_source_route = 0, /* we do not accept RH0 by default. */ 21762306a36Sopenharmony_ci .disable_ipv6 = 0, 21862306a36Sopenharmony_ci .accept_dad = 0, 21962306a36Sopenharmony_ci .suppress_frag_ndisc = 1, 22062306a36Sopenharmony_ci .accept_ra_mtu = 1, 22162306a36Sopenharmony_ci .stable_secret = { 22262306a36Sopenharmony_ci .initialized = false, 22362306a36Sopenharmony_ci }, 22462306a36Sopenharmony_ci .use_oif_addrs_only = 0, 22562306a36Sopenharmony_ci .ignore_routes_with_linkdown = 0, 22662306a36Sopenharmony_ci .keep_addr_on_down = 0, 22762306a36Sopenharmony_ci .seg6_enabled = 0, 22862306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 22962306a36Sopenharmony_ci .seg6_require_hmac = 0, 23062306a36Sopenharmony_ci#endif 23162306a36Sopenharmony_ci .enhanced_dad = 1, 23262306a36Sopenharmony_ci .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, 23362306a36Sopenharmony_ci .disable_policy = 0, 23462306a36Sopenharmony_ci .rpl_seg_enabled = 0, 23562306a36Sopenharmony_ci .ioam6_enabled = 0, 23662306a36Sopenharmony_ci .ioam6_id = IOAM6_DEFAULT_IF_ID, 23762306a36Sopenharmony_ci .ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE, 23862306a36Sopenharmony_ci .ndisc_evict_nocarrier = 1, 23962306a36Sopenharmony_ci}; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 24262306a36Sopenharmony_ci .forwarding = 0, 24362306a36Sopenharmony_ci .hop_limit = IPV6_DEFAULT_HOPLIMIT, 24462306a36Sopenharmony_ci .mtu6 = IPV6_MIN_MTU, 24562306a36Sopenharmony_ci .accept_ra = 1, 24662306a36Sopenharmony_ci .accept_redirects = 1, 24762306a36Sopenharmony_ci .autoconf = 1, 24862306a36Sopenharmony_ci .force_mld_version = 0, 24962306a36Sopenharmony_ci .mldv1_unsolicited_report_interval = 10 * HZ, 25062306a36Sopenharmony_ci .mldv2_unsolicited_report_interval = HZ, 25162306a36Sopenharmony_ci .dad_transmits = 1, 25262306a36Sopenharmony_ci .rtr_solicits = MAX_RTR_SOLICITATIONS, 25362306a36Sopenharmony_ci .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, 25462306a36Sopenharmony_ci .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL, 25562306a36Sopenharmony_ci .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY, 25662306a36Sopenharmony_ci .use_tempaddr = 0, 25762306a36Sopenharmony_ci .temp_valid_lft = TEMP_VALID_LIFETIME, 25862306a36Sopenharmony_ci .temp_prefered_lft = TEMP_PREFERRED_LIFETIME, 25962306a36Sopenharmony_ci .regen_max_retry = REGEN_MAX_RETRY, 26062306a36Sopenharmony_ci .max_desync_factor = MAX_DESYNC_FACTOR, 26162306a36Sopenharmony_ci .max_addresses = IPV6_MAX_ADDRESSES, 26262306a36Sopenharmony_ci .accept_ra_defrtr = 1, 26362306a36Sopenharmony_ci .ra_defrtr_metric = IP6_RT_PRIO_USER, 26462306a36Sopenharmony_ci .accept_ra_from_local = 0, 26562306a36Sopenharmony_ci .accept_ra_min_hop_limit= 1, 26662306a36Sopenharmony_ci .accept_ra_min_lft = 0, 26762306a36Sopenharmony_ci .accept_ra_pinfo = 1, 26862306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 26962306a36Sopenharmony_ci .accept_ra_rtr_pref = 1, 27062306a36Sopenharmony_ci .rtr_probe_interval = 60 * HZ, 27162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 27262306a36Sopenharmony_ci .accept_ra_rt_info_min_plen = 0, 27362306a36Sopenharmony_ci .accept_ra_rt_info_max_plen = 0, 27462306a36Sopenharmony_ci#endif 27562306a36Sopenharmony_ci#endif 27662306a36Sopenharmony_ci .proxy_ndp = 0, 27762306a36Sopenharmony_ci .accept_source_route = 0, /* we do not accept RH0 by default. */ 27862306a36Sopenharmony_ci .disable_ipv6 = 0, 27962306a36Sopenharmony_ci .accept_dad = 1, 28062306a36Sopenharmony_ci .suppress_frag_ndisc = 1, 28162306a36Sopenharmony_ci .accept_ra_mtu = 1, 28262306a36Sopenharmony_ci .stable_secret = { 28362306a36Sopenharmony_ci .initialized = false, 28462306a36Sopenharmony_ci }, 28562306a36Sopenharmony_ci .use_oif_addrs_only = 0, 28662306a36Sopenharmony_ci .ignore_routes_with_linkdown = 0, 28762306a36Sopenharmony_ci .keep_addr_on_down = 0, 28862306a36Sopenharmony_ci .seg6_enabled = 0, 28962306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 29062306a36Sopenharmony_ci .seg6_require_hmac = 0, 29162306a36Sopenharmony_ci#endif 29262306a36Sopenharmony_ci .enhanced_dad = 1, 29362306a36Sopenharmony_ci .addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64, 29462306a36Sopenharmony_ci .disable_policy = 0, 29562306a36Sopenharmony_ci .rpl_seg_enabled = 0, 29662306a36Sopenharmony_ci .ioam6_enabled = 0, 29762306a36Sopenharmony_ci .ioam6_id = IOAM6_DEFAULT_IF_ID, 29862306a36Sopenharmony_ci .ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE, 29962306a36Sopenharmony_ci .ndisc_evict_nocarrier = 1, 30062306a36Sopenharmony_ci}; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* Check if link is ready: is it up and is a valid qdisc available */ 30362306a36Sopenharmony_cistatic inline bool addrconf_link_ready(const struct net_device *dev) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci return netif_oper_up(dev) && !qdisc_tx_is_noop(dev); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic void addrconf_del_rs_timer(struct inet6_dev *idev) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci if (del_timer(&idev->rs_timer)) 31162306a36Sopenharmony_ci __in6_dev_put(idev); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void addrconf_del_dad_work(struct inet6_ifaddr *ifp) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci if (cancel_delayed_work(&ifp->dad_work)) 31762306a36Sopenharmony_ci __in6_ifa_put(ifp); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void addrconf_mod_rs_timer(struct inet6_dev *idev, 32162306a36Sopenharmony_ci unsigned long when) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci if (!mod_timer(&idev->rs_timer, jiffies + when)) 32462306a36Sopenharmony_ci in6_dev_hold(idev); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic void addrconf_mod_dad_work(struct inet6_ifaddr *ifp, 32862306a36Sopenharmony_ci unsigned long delay) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci in6_ifa_hold(ifp); 33162306a36Sopenharmony_ci if (mod_delayed_work(addrconf_wq, &ifp->dad_work, delay)) 33262306a36Sopenharmony_ci in6_ifa_put(ifp); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int snmp6_alloc_dev(struct inet6_dev *idev) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci int i; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci idev->stats.ipv6 = alloc_percpu_gfp(struct ipstats_mib, GFP_KERNEL_ACCOUNT); 34062306a36Sopenharmony_ci if (!idev->stats.ipv6) 34162306a36Sopenharmony_ci goto err_ip; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci for_each_possible_cpu(i) { 34462306a36Sopenharmony_ci struct ipstats_mib *addrconf_stats; 34562306a36Sopenharmony_ci addrconf_stats = per_cpu_ptr(idev->stats.ipv6, i); 34662306a36Sopenharmony_ci u64_stats_init(&addrconf_stats->syncp); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device), 35162306a36Sopenharmony_ci GFP_KERNEL); 35262306a36Sopenharmony_ci if (!idev->stats.icmpv6dev) 35362306a36Sopenharmony_ci goto err_icmp; 35462306a36Sopenharmony_ci idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device), 35562306a36Sopenharmony_ci GFP_KERNEL_ACCOUNT); 35662306a36Sopenharmony_ci if (!idev->stats.icmpv6msgdev) 35762306a36Sopenharmony_ci goto err_icmpmsg; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cierr_icmpmsg: 36262306a36Sopenharmony_ci kfree(idev->stats.icmpv6dev); 36362306a36Sopenharmony_cierr_icmp: 36462306a36Sopenharmony_ci free_percpu(idev->stats.ipv6); 36562306a36Sopenharmony_cierr_ip: 36662306a36Sopenharmony_ci return -ENOMEM; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic struct inet6_dev *ipv6_add_dev(struct net_device *dev) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct inet6_dev *ndev; 37262306a36Sopenharmony_ci int err = -ENOMEM; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci ASSERT_RTNL(); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (dev->mtu < IPV6_MIN_MTU && dev != blackhole_netdev) 37762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ndev = kzalloc(sizeof(*ndev), GFP_KERNEL_ACCOUNT); 38062306a36Sopenharmony_ci if (!ndev) 38162306a36Sopenharmony_ci return ERR_PTR(err); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci rwlock_init(&ndev->lock); 38462306a36Sopenharmony_ci ndev->dev = dev; 38562306a36Sopenharmony_ci INIT_LIST_HEAD(&ndev->addr_list); 38662306a36Sopenharmony_ci timer_setup(&ndev->rs_timer, addrconf_rs_timer, 0); 38762306a36Sopenharmony_ci memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf)); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (ndev->cnf.stable_secret.initialized) 39062306a36Sopenharmony_ci ndev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ndev->cnf.mtu6 = dev->mtu; 39362306a36Sopenharmony_ci ndev->ra_mtu = 0; 39462306a36Sopenharmony_ci ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl); 39562306a36Sopenharmony_ci if (!ndev->nd_parms) { 39662306a36Sopenharmony_ci kfree(ndev); 39762306a36Sopenharmony_ci return ERR_PTR(err); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci if (ndev->cnf.forwarding) 40062306a36Sopenharmony_ci dev_disable_lro(dev); 40162306a36Sopenharmony_ci /* We refer to the device */ 40262306a36Sopenharmony_ci netdev_hold(dev, &ndev->dev_tracker, GFP_KERNEL); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (snmp6_alloc_dev(ndev) < 0) { 40562306a36Sopenharmony_ci netdev_dbg(dev, "%s: cannot allocate memory for statistics\n", 40662306a36Sopenharmony_ci __func__); 40762306a36Sopenharmony_ci neigh_parms_release(&nd_tbl, ndev->nd_parms); 40862306a36Sopenharmony_ci netdev_put(dev, &ndev->dev_tracker); 40962306a36Sopenharmony_ci kfree(ndev); 41062306a36Sopenharmony_ci return ERR_PTR(err); 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (dev != blackhole_netdev) { 41462306a36Sopenharmony_ci if (snmp6_register_dev(ndev) < 0) { 41562306a36Sopenharmony_ci netdev_dbg(dev, "%s: cannot create /proc/net/dev_snmp6/%s\n", 41662306a36Sopenharmony_ci __func__, dev->name); 41762306a36Sopenharmony_ci goto err_release; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci /* One reference from device. */ 42162306a36Sopenharmony_ci refcount_set(&ndev->refcnt, 1); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (dev->flags & (IFF_NOARP | IFF_LOOPBACK)) 42462306a36Sopenharmony_ci ndev->cnf.accept_dad = -1; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT) 42762306a36Sopenharmony_ci if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) { 42862306a36Sopenharmony_ci pr_info("%s: Disabled Multicast RS\n", dev->name); 42962306a36Sopenharmony_ci ndev->cnf.rtr_solicits = 0; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci#endif 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci INIT_LIST_HEAD(&ndev->tempaddr_list); 43462306a36Sopenharmony_ci ndev->desync_factor = U32_MAX; 43562306a36Sopenharmony_ci if ((dev->flags&IFF_LOOPBACK) || 43662306a36Sopenharmony_ci dev->type == ARPHRD_TUNNEL || 43762306a36Sopenharmony_ci dev->type == ARPHRD_TUNNEL6 || 43862306a36Sopenharmony_ci dev->type == ARPHRD_SIT || 43962306a36Sopenharmony_ci dev->type == ARPHRD_NONE) { 44062306a36Sopenharmony_ci ndev->cnf.use_tempaddr = -1; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci ndev->token = in6addr_any; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (netif_running(dev) && addrconf_link_ready(dev)) 44662306a36Sopenharmony_ci ndev->if_flags |= IF_READY; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ipv6_mc_init_dev(ndev); 44962306a36Sopenharmony_ci ndev->tstamp = jiffies; 45062306a36Sopenharmony_ci if (dev != blackhole_netdev) { 45162306a36Sopenharmony_ci err = addrconf_sysctl_register(ndev); 45262306a36Sopenharmony_ci if (err) { 45362306a36Sopenharmony_ci ipv6_mc_destroy_dev(ndev); 45462306a36Sopenharmony_ci snmp6_unregister_dev(ndev); 45562306a36Sopenharmony_ci goto err_release; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci /* protected by rtnl_lock */ 45962306a36Sopenharmony_ci rcu_assign_pointer(dev->ip6_ptr, ndev); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (dev != blackhole_netdev) { 46262306a36Sopenharmony_ci /* Join interface-local all-node multicast group */ 46362306a36Sopenharmony_ci ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allnodes); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* Join all-node multicast group */ 46662306a36Sopenharmony_ci ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* Join all-router multicast group if forwarding is set */ 46962306a36Sopenharmony_ci if (ndev->cnf.forwarding && (dev->flags & IFF_MULTICAST)) 47062306a36Sopenharmony_ci ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci return ndev; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cierr_release: 47562306a36Sopenharmony_ci neigh_parms_release(&nd_tbl, ndev->nd_parms); 47662306a36Sopenharmony_ci ndev->dead = 1; 47762306a36Sopenharmony_ci in6_dev_finish_destroy(ndev); 47862306a36Sopenharmony_ci return ERR_PTR(err); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic struct inet6_dev *ipv6_find_idev(struct net_device *dev) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct inet6_dev *idev; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ASSERT_RTNL(); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci idev = __in6_dev_get(dev); 48862306a36Sopenharmony_ci if (!idev) { 48962306a36Sopenharmony_ci idev = ipv6_add_dev(dev); 49062306a36Sopenharmony_ci if (IS_ERR(idev)) 49162306a36Sopenharmony_ci return idev; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (dev->flags&IFF_UP) 49562306a36Sopenharmony_ci ipv6_mc_up(idev); 49662306a36Sopenharmony_ci return idev; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic int inet6_netconf_msgsize_devconf(int type) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 50262306a36Sopenharmony_ci + nla_total_size(4); /* NETCONFA_IFINDEX */ 50362306a36Sopenharmony_ci bool all = false; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (type == NETCONFA_ALL) 50662306a36Sopenharmony_ci all = true; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (all || type == NETCONFA_FORWARDING) 50962306a36Sopenharmony_ci size += nla_total_size(4); 51062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 51162306a36Sopenharmony_ci if (all || type == NETCONFA_MC_FORWARDING) 51262306a36Sopenharmony_ci size += nla_total_size(4); 51362306a36Sopenharmony_ci#endif 51462306a36Sopenharmony_ci if (all || type == NETCONFA_PROXY_NEIGH) 51562306a36Sopenharmony_ci size += nla_total_size(4); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) 51862306a36Sopenharmony_ci size += nla_total_size(4); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return size; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 52462306a36Sopenharmony_ci struct ipv6_devconf *devconf, u32 portid, 52562306a36Sopenharmony_ci u32 seq, int event, unsigned int flags, 52662306a36Sopenharmony_ci int type) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct nlmsghdr *nlh; 52962306a36Sopenharmony_ci struct netconfmsg *ncm; 53062306a36Sopenharmony_ci bool all = false; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 53362306a36Sopenharmony_ci flags); 53462306a36Sopenharmony_ci if (!nlh) 53562306a36Sopenharmony_ci return -EMSGSIZE; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (type == NETCONFA_ALL) 53862306a36Sopenharmony_ci all = true; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci ncm = nlmsg_data(nlh); 54162306a36Sopenharmony_ci ncm->ncm_family = AF_INET6; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 54462306a36Sopenharmony_ci goto nla_put_failure; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (!devconf) 54762306a36Sopenharmony_ci goto out; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if ((all || type == NETCONFA_FORWARDING) && 55062306a36Sopenharmony_ci nla_put_s32(skb, NETCONFA_FORWARDING, devconf->forwarding) < 0) 55162306a36Sopenharmony_ci goto nla_put_failure; 55262306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 55362306a36Sopenharmony_ci if ((all || type == NETCONFA_MC_FORWARDING) && 55462306a36Sopenharmony_ci nla_put_s32(skb, NETCONFA_MC_FORWARDING, 55562306a36Sopenharmony_ci atomic_read(&devconf->mc_forwarding)) < 0) 55662306a36Sopenharmony_ci goto nla_put_failure; 55762306a36Sopenharmony_ci#endif 55862306a36Sopenharmony_ci if ((all || type == NETCONFA_PROXY_NEIGH) && 55962306a36Sopenharmony_ci nla_put_s32(skb, NETCONFA_PROXY_NEIGH, devconf->proxy_ndp) < 0) 56062306a36Sopenharmony_ci goto nla_put_failure; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) && 56362306a36Sopenharmony_ci nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 56462306a36Sopenharmony_ci devconf->ignore_routes_with_linkdown) < 0) 56562306a36Sopenharmony_ci goto nla_put_failure; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ciout: 56862306a36Sopenharmony_ci nlmsg_end(skb, nlh); 56962306a36Sopenharmony_ci return 0; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cinla_put_failure: 57262306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 57362306a36Sopenharmony_ci return -EMSGSIZE; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_civoid inet6_netconf_notify_devconf(struct net *net, int event, int type, 57762306a36Sopenharmony_ci int ifindex, struct ipv6_devconf *devconf) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct sk_buff *skb; 58062306a36Sopenharmony_ci int err = -ENOBUFS; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci skb = nlmsg_new(inet6_netconf_msgsize_devconf(type), GFP_KERNEL); 58362306a36Sopenharmony_ci if (!skb) 58462306a36Sopenharmony_ci goto errout; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci err = inet6_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 58762306a36Sopenharmony_ci event, 0, type); 58862306a36Sopenharmony_ci if (err < 0) { 58962306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */ 59062306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 59162306a36Sopenharmony_ci kfree_skb(skb); 59262306a36Sopenharmony_ci goto errout; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV6_NETCONF, NULL, GFP_KERNEL); 59562306a36Sopenharmony_ci return; 59662306a36Sopenharmony_cierrout: 59762306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_NETCONF, err); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = { 60162306a36Sopenharmony_ci [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 60262306a36Sopenharmony_ci [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 60362306a36Sopenharmony_ci [NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) }, 60462306a36Sopenharmony_ci [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .len = sizeof(int) }, 60562306a36Sopenharmony_ci}; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int inet6_netconf_valid_get_req(struct sk_buff *skb, 60862306a36Sopenharmony_ci const struct nlmsghdr *nlh, 60962306a36Sopenharmony_ci struct nlattr **tb, 61062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci int i, err; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) { 61562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for netconf get request"); 61662306a36Sopenharmony_ci return -EINVAL; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (!netlink_strict_get_check(skb)) 62062306a36Sopenharmony_ci return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg), 62162306a36Sopenharmony_ci tb, NETCONFA_MAX, 62262306a36Sopenharmony_ci devconf_ipv6_policy, extack); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg), 62562306a36Sopenharmony_ci tb, NETCONFA_MAX, 62662306a36Sopenharmony_ci devconf_ipv6_policy, extack); 62762306a36Sopenharmony_ci if (err) 62862306a36Sopenharmony_ci return err; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci for (i = 0; i <= NETCONFA_MAX; i++) { 63162306a36Sopenharmony_ci if (!tb[i]) 63262306a36Sopenharmony_ci continue; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci switch (i) { 63562306a36Sopenharmony_ci case NETCONFA_IFINDEX: 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci default: 63862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in netconf get request"); 63962306a36Sopenharmony_ci return -EINVAL; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci return 0; 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic int inet6_netconf_get_devconf(struct sk_buff *in_skb, 64762306a36Sopenharmony_ci struct nlmsghdr *nlh, 64862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 65162306a36Sopenharmony_ci struct nlattr *tb[NETCONFA_MAX+1]; 65262306a36Sopenharmony_ci struct inet6_dev *in6_dev = NULL; 65362306a36Sopenharmony_ci struct net_device *dev = NULL; 65462306a36Sopenharmony_ci struct sk_buff *skb; 65562306a36Sopenharmony_ci struct ipv6_devconf *devconf; 65662306a36Sopenharmony_ci int ifindex; 65762306a36Sopenharmony_ci int err; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci err = inet6_netconf_valid_get_req(in_skb, nlh, tb, extack); 66062306a36Sopenharmony_ci if (err < 0) 66162306a36Sopenharmony_ci return err; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (!tb[NETCONFA_IFINDEX]) 66462306a36Sopenharmony_ci return -EINVAL; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci err = -EINVAL; 66762306a36Sopenharmony_ci ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 66862306a36Sopenharmony_ci switch (ifindex) { 66962306a36Sopenharmony_ci case NETCONFA_IFINDEX_ALL: 67062306a36Sopenharmony_ci devconf = net->ipv6.devconf_all; 67162306a36Sopenharmony_ci break; 67262306a36Sopenharmony_ci case NETCONFA_IFINDEX_DEFAULT: 67362306a36Sopenharmony_ci devconf = net->ipv6.devconf_dflt; 67462306a36Sopenharmony_ci break; 67562306a36Sopenharmony_ci default: 67662306a36Sopenharmony_ci dev = dev_get_by_index(net, ifindex); 67762306a36Sopenharmony_ci if (!dev) 67862306a36Sopenharmony_ci return -EINVAL; 67962306a36Sopenharmony_ci in6_dev = in6_dev_get(dev); 68062306a36Sopenharmony_ci if (!in6_dev) 68162306a36Sopenharmony_ci goto errout; 68262306a36Sopenharmony_ci devconf = &in6_dev->cnf; 68362306a36Sopenharmony_ci break; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci err = -ENOBUFS; 68762306a36Sopenharmony_ci skb = nlmsg_new(inet6_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL); 68862306a36Sopenharmony_ci if (!skb) 68962306a36Sopenharmony_ci goto errout; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci err = inet6_netconf_fill_devconf(skb, ifindex, devconf, 69262306a36Sopenharmony_ci NETLINK_CB(in_skb).portid, 69362306a36Sopenharmony_ci nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 69462306a36Sopenharmony_ci NETCONFA_ALL); 69562306a36Sopenharmony_ci if (err < 0) { 69662306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */ 69762306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 69862306a36Sopenharmony_ci kfree_skb(skb); 69962306a36Sopenharmony_ci goto errout; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 70262306a36Sopenharmony_cierrout: 70362306a36Sopenharmony_ci if (in6_dev) 70462306a36Sopenharmony_ci in6_dev_put(in6_dev); 70562306a36Sopenharmony_ci dev_put(dev); 70662306a36Sopenharmony_ci return err; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci/* Combine dev_addr_genid and dev_base_seq to detect changes. 71062306a36Sopenharmony_ci */ 71162306a36Sopenharmony_cistatic u32 inet6_base_seq(const struct net *net) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci u32 res = atomic_read(&net->ipv6.dev_addr_genid) + 71462306a36Sopenharmony_ci net->dev_base_seq; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* Must not return 0 (see nl_dump_check_consistent()). 71762306a36Sopenharmony_ci * Chose a value far away from 0. 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_ci if (!res) 72062306a36Sopenharmony_ci res = 0x80000000; 72162306a36Sopenharmony_ci return res; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic int inet6_netconf_dump_devconf(struct sk_buff *skb, 72662306a36Sopenharmony_ci struct netlink_callback *cb) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 72962306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 73062306a36Sopenharmony_ci int h, s_h; 73162306a36Sopenharmony_ci int idx, s_idx; 73262306a36Sopenharmony_ci struct net_device *dev; 73362306a36Sopenharmony_ci struct inet6_dev *idev; 73462306a36Sopenharmony_ci struct hlist_head *head; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (cb->strict_check) { 73762306a36Sopenharmony_ci struct netlink_ext_ack *extack = cb->extack; 73862306a36Sopenharmony_ci struct netconfmsg *ncm; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) { 74162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for netconf dump request"); 74262306a36Sopenharmony_ci return -EINVAL; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (nlmsg_attrlen(nlh, sizeof(*ncm))) { 74662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid data after header in netconf dump request"); 74762306a36Sopenharmony_ci return -EINVAL; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci s_h = cb->args[0]; 75262306a36Sopenharmony_ci s_idx = idx = cb->args[1]; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 75562306a36Sopenharmony_ci idx = 0; 75662306a36Sopenharmony_ci head = &net->dev_index_head[h]; 75762306a36Sopenharmony_ci rcu_read_lock(); 75862306a36Sopenharmony_ci cb->seq = inet6_base_seq(net); 75962306a36Sopenharmony_ci hlist_for_each_entry_rcu(dev, head, index_hlist) { 76062306a36Sopenharmony_ci if (idx < s_idx) 76162306a36Sopenharmony_ci goto cont; 76262306a36Sopenharmony_ci idev = __in6_dev_get(dev); 76362306a36Sopenharmony_ci if (!idev) 76462306a36Sopenharmony_ci goto cont; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (inet6_netconf_fill_devconf(skb, dev->ifindex, 76762306a36Sopenharmony_ci &idev->cnf, 76862306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 76962306a36Sopenharmony_ci nlh->nlmsg_seq, 77062306a36Sopenharmony_ci RTM_NEWNETCONF, 77162306a36Sopenharmony_ci NLM_F_MULTI, 77262306a36Sopenharmony_ci NETCONFA_ALL) < 0) { 77362306a36Sopenharmony_ci rcu_read_unlock(); 77462306a36Sopenharmony_ci goto done; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 77762306a36Sopenharmony_cicont: 77862306a36Sopenharmony_ci idx++; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci rcu_read_unlock(); 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci if (h == NETDEV_HASHENTRIES) { 78362306a36Sopenharmony_ci if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 78462306a36Sopenharmony_ci net->ipv6.devconf_all, 78562306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 78662306a36Sopenharmony_ci nlh->nlmsg_seq, 78762306a36Sopenharmony_ci RTM_NEWNETCONF, NLM_F_MULTI, 78862306a36Sopenharmony_ci NETCONFA_ALL) < 0) 78962306a36Sopenharmony_ci goto done; 79062306a36Sopenharmony_ci else 79162306a36Sopenharmony_ci h++; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci if (h == NETDEV_HASHENTRIES + 1) { 79462306a36Sopenharmony_ci if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 79562306a36Sopenharmony_ci net->ipv6.devconf_dflt, 79662306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 79762306a36Sopenharmony_ci nlh->nlmsg_seq, 79862306a36Sopenharmony_ci RTM_NEWNETCONF, NLM_F_MULTI, 79962306a36Sopenharmony_ci NETCONFA_ALL) < 0) 80062306a36Sopenharmony_ci goto done; 80162306a36Sopenharmony_ci else 80262306a36Sopenharmony_ci h++; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_cidone: 80562306a36Sopenharmony_ci cb->args[0] = h; 80662306a36Sopenharmony_ci cb->args[1] = idx; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return skb->len; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 81262306a36Sopenharmony_cistatic void dev_forward_change(struct inet6_dev *idev) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct net_device *dev; 81562306a36Sopenharmony_ci struct inet6_ifaddr *ifa; 81662306a36Sopenharmony_ci LIST_HEAD(tmp_addr_list); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (!idev) 81962306a36Sopenharmony_ci return; 82062306a36Sopenharmony_ci dev = idev->dev; 82162306a36Sopenharmony_ci if (idev->cnf.forwarding) 82262306a36Sopenharmony_ci dev_disable_lro(dev); 82362306a36Sopenharmony_ci if (dev->flags & IFF_MULTICAST) { 82462306a36Sopenharmony_ci if (idev->cnf.forwarding) { 82562306a36Sopenharmony_ci ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); 82662306a36Sopenharmony_ci ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allrouters); 82762306a36Sopenharmony_ci ipv6_dev_mc_inc(dev, &in6addr_sitelocal_allrouters); 82862306a36Sopenharmony_ci } else { 82962306a36Sopenharmony_ci ipv6_dev_mc_dec(dev, &in6addr_linklocal_allrouters); 83062306a36Sopenharmony_ci ipv6_dev_mc_dec(dev, &in6addr_interfacelocal_allrouters); 83162306a36Sopenharmony_ci ipv6_dev_mc_dec(dev, &in6addr_sitelocal_allrouters); 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci read_lock_bh(&idev->lock); 83662306a36Sopenharmony_ci list_for_each_entry(ifa, &idev->addr_list, if_list) { 83762306a36Sopenharmony_ci if (ifa->flags&IFA_F_TENTATIVE) 83862306a36Sopenharmony_ci continue; 83962306a36Sopenharmony_ci list_add_tail(&ifa->if_list_aux, &tmp_addr_list); 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci while (!list_empty(&tmp_addr_list)) { 84462306a36Sopenharmony_ci ifa = list_first_entry(&tmp_addr_list, 84562306a36Sopenharmony_ci struct inet6_ifaddr, if_list_aux); 84662306a36Sopenharmony_ci list_del(&ifa->if_list_aux); 84762306a36Sopenharmony_ci if (idev->cnf.forwarding) 84862306a36Sopenharmony_ci addrconf_join_anycast(ifa); 84962306a36Sopenharmony_ci else 85062306a36Sopenharmony_ci addrconf_leave_anycast(ifa); 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF, 85462306a36Sopenharmony_ci NETCONFA_FORWARDING, 85562306a36Sopenharmony_ci dev->ifindex, &idev->cnf); 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic void addrconf_forward_change(struct net *net, __s32 newf) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci struct net_device *dev; 86262306a36Sopenharmony_ci struct inet6_dev *idev; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci for_each_netdev(net, dev) { 86562306a36Sopenharmony_ci idev = __in6_dev_get(dev); 86662306a36Sopenharmony_ci if (idev) { 86762306a36Sopenharmony_ci int changed = (!idev->cnf.forwarding) ^ (!newf); 86862306a36Sopenharmony_ci idev->cnf.forwarding = newf; 86962306a36Sopenharmony_ci if (changed) 87062306a36Sopenharmony_ci dev_forward_change(idev); 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct net *net; 87862306a36Sopenharmony_ci int old; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (!rtnl_trylock()) 88162306a36Sopenharmony_ci return restart_syscall(); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci net = (struct net *)table->extra2; 88462306a36Sopenharmony_ci old = *p; 88562306a36Sopenharmony_ci *p = newf; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (p == &net->ipv6.devconf_dflt->forwarding) { 88862306a36Sopenharmony_ci if ((!newf) ^ (!old)) 88962306a36Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 89062306a36Sopenharmony_ci NETCONFA_FORWARDING, 89162306a36Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT, 89262306a36Sopenharmony_ci net->ipv6.devconf_dflt); 89362306a36Sopenharmony_ci rtnl_unlock(); 89462306a36Sopenharmony_ci return 0; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (p == &net->ipv6.devconf_all->forwarding) { 89862306a36Sopenharmony_ci int old_dflt = net->ipv6.devconf_dflt->forwarding; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci net->ipv6.devconf_dflt->forwarding = newf; 90162306a36Sopenharmony_ci if ((!newf) ^ (!old_dflt)) 90262306a36Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 90362306a36Sopenharmony_ci NETCONFA_FORWARDING, 90462306a36Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT, 90562306a36Sopenharmony_ci net->ipv6.devconf_dflt); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci addrconf_forward_change(net, newf); 90862306a36Sopenharmony_ci if ((!newf) ^ (!old)) 90962306a36Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 91062306a36Sopenharmony_ci NETCONFA_FORWARDING, 91162306a36Sopenharmony_ci NETCONFA_IFINDEX_ALL, 91262306a36Sopenharmony_ci net->ipv6.devconf_all); 91362306a36Sopenharmony_ci } else if ((!newf) ^ (!old)) 91462306a36Sopenharmony_ci dev_forward_change((struct inet6_dev *)table->extra1); 91562306a36Sopenharmony_ci rtnl_unlock(); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (newf) 91862306a36Sopenharmony_ci rt6_purge_dflt_routers(net); 91962306a36Sopenharmony_ci return 1; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic void addrconf_linkdown_change(struct net *net, __s32 newf) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci struct net_device *dev; 92562306a36Sopenharmony_ci struct inet6_dev *idev; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci for_each_netdev(net, dev) { 92862306a36Sopenharmony_ci idev = __in6_dev_get(dev); 92962306a36Sopenharmony_ci if (idev) { 93062306a36Sopenharmony_ci int changed = (!idev->cnf.ignore_routes_with_linkdown) ^ (!newf); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci idev->cnf.ignore_routes_with_linkdown = newf; 93362306a36Sopenharmony_ci if (changed) 93462306a36Sopenharmony_ci inet6_netconf_notify_devconf(dev_net(dev), 93562306a36Sopenharmony_ci RTM_NEWNETCONF, 93662306a36Sopenharmony_ci NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 93762306a36Sopenharmony_ci dev->ifindex, 93862306a36Sopenharmony_ci &idev->cnf); 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic int addrconf_fixup_linkdown(struct ctl_table *table, int *p, int newf) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct net *net; 94662306a36Sopenharmony_ci int old; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (!rtnl_trylock()) 94962306a36Sopenharmony_ci return restart_syscall(); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci net = (struct net *)table->extra2; 95262306a36Sopenharmony_ci old = *p; 95362306a36Sopenharmony_ci *p = newf; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (p == &net->ipv6.devconf_dflt->ignore_routes_with_linkdown) { 95662306a36Sopenharmony_ci if ((!newf) ^ (!old)) 95762306a36Sopenharmony_ci inet6_netconf_notify_devconf(net, 95862306a36Sopenharmony_ci RTM_NEWNETCONF, 95962306a36Sopenharmony_ci NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 96062306a36Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT, 96162306a36Sopenharmony_ci net->ipv6.devconf_dflt); 96262306a36Sopenharmony_ci rtnl_unlock(); 96362306a36Sopenharmony_ci return 0; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (p == &net->ipv6.devconf_all->ignore_routes_with_linkdown) { 96762306a36Sopenharmony_ci net->ipv6.devconf_dflt->ignore_routes_with_linkdown = newf; 96862306a36Sopenharmony_ci addrconf_linkdown_change(net, newf); 96962306a36Sopenharmony_ci if ((!newf) ^ (!old)) 97062306a36Sopenharmony_ci inet6_netconf_notify_devconf(net, 97162306a36Sopenharmony_ci RTM_NEWNETCONF, 97262306a36Sopenharmony_ci NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 97362306a36Sopenharmony_ci NETCONFA_IFINDEX_ALL, 97462306a36Sopenharmony_ci net->ipv6.devconf_all); 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci rtnl_unlock(); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci return 1; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci#endif 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci/* Nobody refers to this ifaddr, destroy it */ 98462306a36Sopenharmony_civoid inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci WARN_ON(!hlist_unhashed(&ifp->addr_lst)); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci#ifdef NET_REFCNT_DEBUG 98962306a36Sopenharmony_ci pr_debug("%s\n", __func__); 99062306a36Sopenharmony_ci#endif 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci in6_dev_put(ifp->idev); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (cancel_delayed_work(&ifp->dad_work)) 99562306a36Sopenharmony_ci pr_notice("delayed DAD work was pending while freeing ifa=%p\n", 99662306a36Sopenharmony_ci ifp); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if (ifp->state != INET6_IFADDR_STATE_DEAD) { 99962306a36Sopenharmony_ci pr_warn("Freeing alive inet6 address %p\n", ifp); 100062306a36Sopenharmony_ci return; 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci kfree_rcu(ifp, rcu); 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic void 100762306a36Sopenharmony_ciipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci struct list_head *p; 101062306a36Sopenharmony_ci int ifp_scope = ipv6_addr_src_scope(&ifp->addr); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci /* 101362306a36Sopenharmony_ci * Each device address list is sorted in order of scope - 101462306a36Sopenharmony_ci * global before linklocal. 101562306a36Sopenharmony_ci */ 101662306a36Sopenharmony_ci list_for_each(p, &idev->addr_list) { 101762306a36Sopenharmony_ci struct inet6_ifaddr *ifa 101862306a36Sopenharmony_ci = list_entry(p, struct inet6_ifaddr, if_list); 101962306a36Sopenharmony_ci if (ifp_scope >= ipv6_addr_src_scope(&ifa->addr)) 102062306a36Sopenharmony_ci break; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci list_add_tail_rcu(&ifp->if_list, p); 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic u32 inet6_addr_hash(const struct net *net, const struct in6_addr *addr) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci u32 val = ipv6_addr_hash(addr) ^ net_hash_mix(net); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci return hash_32(val, IN6_ADDR_HSIZE_SHIFT); 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, 103462306a36Sopenharmony_ci struct net_device *dev, unsigned int hash) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci hlist_for_each_entry(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) { 103962306a36Sopenharmony_ci if (ipv6_addr_equal(&ifp->addr, addr)) { 104062306a36Sopenharmony_ci if (!dev || ifp->idev->dev == dev) 104162306a36Sopenharmony_ci return true; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci return false; 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic int ipv6_add_addr_hash(struct net_device *dev, struct inet6_ifaddr *ifa) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci struct net *net = dev_net(dev); 105062306a36Sopenharmony_ci unsigned int hash = inet6_addr_hash(net, &ifa->addr); 105162306a36Sopenharmony_ci int err = 0; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci spin_lock_bh(&net->ipv6.addrconf_hash_lock); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci /* Ignore adding duplicate addresses on an interface */ 105662306a36Sopenharmony_ci if (ipv6_chk_same_addr(net, &ifa->addr, dev, hash)) { 105762306a36Sopenharmony_ci netdev_dbg(dev, "ipv6_add_addr: already assigned\n"); 105862306a36Sopenharmony_ci err = -EEXIST; 105962306a36Sopenharmony_ci } else { 106062306a36Sopenharmony_ci hlist_add_head_rcu(&ifa->addr_lst, &net->ipv6.inet6_addr_lst[hash]); 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci spin_unlock_bh(&net->ipv6.addrconf_hash_lock); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci return err; 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci/* On success it returns ifp with increased reference count */ 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_cistatic struct inet6_ifaddr * 107162306a36Sopenharmony_ciipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg, 107262306a36Sopenharmony_ci bool can_block, struct netlink_ext_ack *extack) 107362306a36Sopenharmony_ci{ 107462306a36Sopenharmony_ci gfp_t gfp_flags = can_block ? GFP_KERNEL : GFP_ATOMIC; 107562306a36Sopenharmony_ci int addr_type = ipv6_addr_type(cfg->pfx); 107662306a36Sopenharmony_ci struct net *net = dev_net(idev->dev); 107762306a36Sopenharmony_ci struct inet6_ifaddr *ifa = NULL; 107862306a36Sopenharmony_ci struct fib6_info *f6i = NULL; 107962306a36Sopenharmony_ci int err = 0; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (addr_type == IPV6_ADDR_ANY) { 108262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid address"); 108362306a36Sopenharmony_ci return ERR_PTR(-EADDRNOTAVAIL); 108462306a36Sopenharmony_ci } else if (addr_type & IPV6_ADDR_MULTICAST && 108562306a36Sopenharmony_ci !(cfg->ifa_flags & IFA_F_MCAUTOJOIN)) { 108662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Cannot assign multicast address without \"IFA_F_MCAUTOJOIN\" flag"); 108762306a36Sopenharmony_ci return ERR_PTR(-EADDRNOTAVAIL); 108862306a36Sopenharmony_ci } else if (!(idev->dev->flags & IFF_LOOPBACK) && 108962306a36Sopenharmony_ci !netif_is_l3_master(idev->dev) && 109062306a36Sopenharmony_ci addr_type & IPV6_ADDR_LOOPBACK) { 109162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Cannot assign loopback address on this device"); 109262306a36Sopenharmony_ci return ERR_PTR(-EADDRNOTAVAIL); 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci if (idev->dead) { 109662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "device is going away"); 109762306a36Sopenharmony_ci err = -ENODEV; 109862306a36Sopenharmony_ci goto out; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci if (idev->cnf.disable_ipv6) { 110262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "IPv6 is disabled on this device"); 110362306a36Sopenharmony_ci err = -EACCES; 110462306a36Sopenharmony_ci goto out; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci /* validator notifier needs to be blocking; 110862306a36Sopenharmony_ci * do not call in atomic context 110962306a36Sopenharmony_ci */ 111062306a36Sopenharmony_ci if (can_block) { 111162306a36Sopenharmony_ci struct in6_validator_info i6vi = { 111262306a36Sopenharmony_ci .i6vi_addr = *cfg->pfx, 111362306a36Sopenharmony_ci .i6vi_dev = idev, 111462306a36Sopenharmony_ci .extack = extack, 111562306a36Sopenharmony_ci }; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci err = inet6addr_validator_notifier_call_chain(NETDEV_UP, &i6vi); 111862306a36Sopenharmony_ci err = notifier_to_errno(err); 111962306a36Sopenharmony_ci if (err < 0) 112062306a36Sopenharmony_ci goto out; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci ifa = kzalloc(sizeof(*ifa), gfp_flags | __GFP_ACCOUNT); 112462306a36Sopenharmony_ci if (!ifa) { 112562306a36Sopenharmony_ci err = -ENOBUFS; 112662306a36Sopenharmony_ci goto out; 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci f6i = addrconf_f6i_alloc(net, idev, cfg->pfx, false, gfp_flags, extack); 113062306a36Sopenharmony_ci if (IS_ERR(f6i)) { 113162306a36Sopenharmony_ci err = PTR_ERR(f6i); 113262306a36Sopenharmony_ci f6i = NULL; 113362306a36Sopenharmony_ci goto out; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci neigh_parms_data_state_setall(idev->nd_parms); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci ifa->addr = *cfg->pfx; 113962306a36Sopenharmony_ci if (cfg->peer_pfx) 114062306a36Sopenharmony_ci ifa->peer_addr = *cfg->peer_pfx; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci spin_lock_init(&ifa->lock); 114362306a36Sopenharmony_ci INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work); 114462306a36Sopenharmony_ci INIT_HLIST_NODE(&ifa->addr_lst); 114562306a36Sopenharmony_ci ifa->scope = cfg->scope; 114662306a36Sopenharmony_ci ifa->prefix_len = cfg->plen; 114762306a36Sopenharmony_ci ifa->rt_priority = cfg->rt_priority; 114862306a36Sopenharmony_ci ifa->flags = cfg->ifa_flags; 114962306a36Sopenharmony_ci ifa->ifa_proto = cfg->ifa_proto; 115062306a36Sopenharmony_ci /* No need to add the TENTATIVE flag for addresses with NODAD */ 115162306a36Sopenharmony_ci if (!(cfg->ifa_flags & IFA_F_NODAD)) 115262306a36Sopenharmony_ci ifa->flags |= IFA_F_TENTATIVE; 115362306a36Sopenharmony_ci ifa->valid_lft = cfg->valid_lft; 115462306a36Sopenharmony_ci ifa->prefered_lft = cfg->preferred_lft; 115562306a36Sopenharmony_ci ifa->cstamp = ifa->tstamp = jiffies; 115662306a36Sopenharmony_ci ifa->tokenized = false; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci ifa->rt = f6i; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci ifa->idev = idev; 116162306a36Sopenharmony_ci in6_dev_hold(idev); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* For caller */ 116462306a36Sopenharmony_ci refcount_set(&ifa->refcnt, 1); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci rcu_read_lock(); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci err = ipv6_add_addr_hash(idev->dev, ifa); 116962306a36Sopenharmony_ci if (err < 0) { 117062306a36Sopenharmony_ci rcu_read_unlock(); 117162306a36Sopenharmony_ci goto out; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci write_lock_bh(&idev->lock); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci /* Add to inet6_dev unicast addr list. */ 117762306a36Sopenharmony_ci ipv6_link_dev_addr(idev, ifa); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci if (ifa->flags&IFA_F_TEMPORARY) { 118062306a36Sopenharmony_ci list_add(&ifa->tmp_list, &idev->tempaddr_list); 118162306a36Sopenharmony_ci in6_ifa_hold(ifa); 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci in6_ifa_hold(ifa); 118562306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci rcu_read_unlock(); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci inet6addr_notifier_call_chain(NETDEV_UP, ifa); 119062306a36Sopenharmony_ciout: 119162306a36Sopenharmony_ci if (unlikely(err < 0)) { 119262306a36Sopenharmony_ci fib6_info_release(f6i); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci if (ifa) { 119562306a36Sopenharmony_ci if (ifa->idev) 119662306a36Sopenharmony_ci in6_dev_put(ifa->idev); 119762306a36Sopenharmony_ci kfree(ifa); 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci ifa = ERR_PTR(err); 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci return ifa; 120362306a36Sopenharmony_ci} 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_cienum cleanup_prefix_rt_t { 120662306a36Sopenharmony_ci CLEANUP_PREFIX_RT_NOP, /* no cleanup action for prefix route */ 120762306a36Sopenharmony_ci CLEANUP_PREFIX_RT_DEL, /* delete the prefix route */ 120862306a36Sopenharmony_ci CLEANUP_PREFIX_RT_EXPIRE, /* update the lifetime of the prefix route */ 120962306a36Sopenharmony_ci}; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci/* 121262306a36Sopenharmony_ci * Check, whether the prefix for ifp would still need a prefix route 121362306a36Sopenharmony_ci * after deleting ifp. The function returns one of the CLEANUP_PREFIX_RT_* 121462306a36Sopenharmony_ci * constants. 121562306a36Sopenharmony_ci * 121662306a36Sopenharmony_ci * 1) we don't purge prefix if address was not permanent. 121762306a36Sopenharmony_ci * prefix is managed by its own lifetime. 121862306a36Sopenharmony_ci * 2) we also don't purge, if the address was IFA_F_NOPREFIXROUTE. 121962306a36Sopenharmony_ci * 3) if there are no addresses, delete prefix. 122062306a36Sopenharmony_ci * 4) if there are still other permanent address(es), 122162306a36Sopenharmony_ci * corresponding prefix is still permanent. 122262306a36Sopenharmony_ci * 5) if there are still other addresses with IFA_F_NOPREFIXROUTE, 122362306a36Sopenharmony_ci * don't purge the prefix, assume user space is managing it. 122462306a36Sopenharmony_ci * 6) otherwise, update prefix lifetime to the 122562306a36Sopenharmony_ci * longest valid lifetime among the corresponding 122662306a36Sopenharmony_ci * addresses on the device. 122762306a36Sopenharmony_ci * Note: subsequent RA will update lifetime. 122862306a36Sopenharmony_ci **/ 122962306a36Sopenharmony_cistatic enum cleanup_prefix_rt_t 123062306a36Sopenharmony_cicheck_cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long *expires) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci struct inet6_ifaddr *ifa; 123362306a36Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 123462306a36Sopenharmony_ci unsigned long lifetime; 123562306a36Sopenharmony_ci enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_DEL; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci *expires = jiffies; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci list_for_each_entry(ifa, &idev->addr_list, if_list) { 124062306a36Sopenharmony_ci if (ifa == ifp) 124162306a36Sopenharmony_ci continue; 124262306a36Sopenharmony_ci if (ifa->prefix_len != ifp->prefix_len || 124362306a36Sopenharmony_ci !ipv6_prefix_equal(&ifa->addr, &ifp->addr, 124462306a36Sopenharmony_ci ifp->prefix_len)) 124562306a36Sopenharmony_ci continue; 124662306a36Sopenharmony_ci if (ifa->flags & (IFA_F_PERMANENT | IFA_F_NOPREFIXROUTE)) 124762306a36Sopenharmony_ci return CLEANUP_PREFIX_RT_NOP; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci action = CLEANUP_PREFIX_RT_EXPIRE; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci spin_lock(&ifa->lock); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ); 125462306a36Sopenharmony_ci /* 125562306a36Sopenharmony_ci * Note: Because this address is 125662306a36Sopenharmony_ci * not permanent, lifetime < 125762306a36Sopenharmony_ci * LONG_MAX / HZ here. 125862306a36Sopenharmony_ci */ 125962306a36Sopenharmony_ci if (time_before(*expires, ifa->tstamp + lifetime * HZ)) 126062306a36Sopenharmony_ci *expires = ifa->tstamp + lifetime * HZ; 126162306a36Sopenharmony_ci spin_unlock(&ifa->lock); 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci return action; 126562306a36Sopenharmony_ci} 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_cistatic void 126862306a36Sopenharmony_cicleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires, 126962306a36Sopenharmony_ci bool del_rt, bool del_peer) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci struct fib6_info *f6i; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci f6i = addrconf_get_prefix_route(del_peer ? &ifp->peer_addr : &ifp->addr, 127462306a36Sopenharmony_ci ifp->prefix_len, 127562306a36Sopenharmony_ci ifp->idev->dev, 0, RTF_DEFAULT, true); 127662306a36Sopenharmony_ci if (f6i) { 127762306a36Sopenharmony_ci if (del_rt) 127862306a36Sopenharmony_ci ip6_del_rt(dev_net(ifp->idev->dev), f6i, false); 127962306a36Sopenharmony_ci else { 128062306a36Sopenharmony_ci if (!(f6i->fib6_flags & RTF_EXPIRES)) 128162306a36Sopenharmony_ci fib6_set_expires(f6i, expires); 128262306a36Sopenharmony_ci fib6_info_release(f6i); 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci} 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci/* This function wants to get referenced ifp and releases it before return */ 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic void ipv6_del_addr(struct inet6_ifaddr *ifp) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_NOP; 129362306a36Sopenharmony_ci struct net *net = dev_net(ifp->idev->dev); 129462306a36Sopenharmony_ci unsigned long expires; 129562306a36Sopenharmony_ci int state; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci ASSERT_RTNL(); 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 130062306a36Sopenharmony_ci state = ifp->state; 130162306a36Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_DEAD; 130262306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci if (state == INET6_IFADDR_STATE_DEAD) 130562306a36Sopenharmony_ci goto out; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci spin_lock_bh(&net->ipv6.addrconf_hash_lock); 130862306a36Sopenharmony_ci hlist_del_init_rcu(&ifp->addr_lst); 130962306a36Sopenharmony_ci spin_unlock_bh(&net->ipv6.addrconf_hash_lock); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci write_lock_bh(&ifp->idev->lock); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci if (ifp->flags&IFA_F_TEMPORARY) { 131462306a36Sopenharmony_ci list_del(&ifp->tmp_list); 131562306a36Sopenharmony_ci if (ifp->ifpub) { 131662306a36Sopenharmony_ci in6_ifa_put(ifp->ifpub); 131762306a36Sopenharmony_ci ifp->ifpub = NULL; 131862306a36Sopenharmony_ci } 131962306a36Sopenharmony_ci __in6_ifa_put(ifp); 132062306a36Sopenharmony_ci } 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci if (ifp->flags & IFA_F_PERMANENT && !(ifp->flags & IFA_F_NOPREFIXROUTE)) 132362306a36Sopenharmony_ci action = check_cleanup_prefix_route(ifp, &expires); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci list_del_rcu(&ifp->if_list); 132662306a36Sopenharmony_ci __in6_ifa_put(ifp); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci write_unlock_bh(&ifp->idev->lock); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci addrconf_del_dad_work(ifp); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci ipv6_ifa_notify(RTM_DELADDR, ifp); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci inet6addr_notifier_call_chain(NETDEV_DOWN, ifp); 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci if (action != CLEANUP_PREFIX_RT_NOP) { 133762306a36Sopenharmony_ci cleanup_prefix_route(ifp, expires, 133862306a36Sopenharmony_ci action == CLEANUP_PREFIX_RT_DEL, false); 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci /* clean up prefsrc entries */ 134262306a36Sopenharmony_ci rt6_remove_prefsrc(ifp); 134362306a36Sopenharmony_ciout: 134462306a36Sopenharmony_ci in6_ifa_put(ifp); 134562306a36Sopenharmony_ci} 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_cistatic int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 135062306a36Sopenharmony_ci unsigned long tmp_tstamp, age; 135162306a36Sopenharmony_ci unsigned long regen_advance; 135262306a36Sopenharmony_ci unsigned long now = jiffies; 135362306a36Sopenharmony_ci s32 cnf_temp_preferred_lft; 135462306a36Sopenharmony_ci struct inet6_ifaddr *ift; 135562306a36Sopenharmony_ci struct ifa6_config cfg; 135662306a36Sopenharmony_ci long max_desync_factor; 135762306a36Sopenharmony_ci struct in6_addr addr; 135862306a36Sopenharmony_ci int ret = 0; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci write_lock_bh(&idev->lock); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ciretry: 136362306a36Sopenharmony_ci in6_dev_hold(idev); 136462306a36Sopenharmony_ci if (idev->cnf.use_tempaddr <= 0) { 136562306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 136662306a36Sopenharmony_ci pr_info("%s: use_tempaddr is disabled\n", __func__); 136762306a36Sopenharmony_ci in6_dev_put(idev); 136862306a36Sopenharmony_ci ret = -1; 136962306a36Sopenharmony_ci goto out; 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 137262306a36Sopenharmony_ci if (ifp->regen_count++ >= idev->cnf.regen_max_retry) { 137362306a36Sopenharmony_ci idev->cnf.use_tempaddr = -1; /*XXX*/ 137462306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 137562306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 137662306a36Sopenharmony_ci pr_warn("%s: regeneration time exceeded - disabled temporary address support\n", 137762306a36Sopenharmony_ci __func__); 137862306a36Sopenharmony_ci in6_dev_put(idev); 137962306a36Sopenharmony_ci ret = -1; 138062306a36Sopenharmony_ci goto out; 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci in6_ifa_hold(ifp); 138362306a36Sopenharmony_ci memcpy(addr.s6_addr, ifp->addr.s6_addr, 8); 138462306a36Sopenharmony_ci ipv6_gen_rnd_iid(&addr); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci age = (now - ifp->tstamp) / HZ; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci regen_advance = idev->cnf.regen_max_retry * 138962306a36Sopenharmony_ci idev->cnf.dad_transmits * 139062306a36Sopenharmony_ci max(NEIGH_VAR(idev->nd_parms, RETRANS_TIME), HZ/100) / HZ; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci /* recalculate max_desync_factor each time and update 139362306a36Sopenharmony_ci * idev->desync_factor if it's larger 139462306a36Sopenharmony_ci */ 139562306a36Sopenharmony_ci cnf_temp_preferred_lft = READ_ONCE(idev->cnf.temp_prefered_lft); 139662306a36Sopenharmony_ci max_desync_factor = min_t(long, 139762306a36Sopenharmony_ci idev->cnf.max_desync_factor, 139862306a36Sopenharmony_ci cnf_temp_preferred_lft - regen_advance); 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci if (unlikely(idev->desync_factor > max_desync_factor)) { 140162306a36Sopenharmony_ci if (max_desync_factor > 0) { 140262306a36Sopenharmony_ci get_random_bytes(&idev->desync_factor, 140362306a36Sopenharmony_ci sizeof(idev->desync_factor)); 140462306a36Sopenharmony_ci idev->desync_factor %= max_desync_factor; 140562306a36Sopenharmony_ci } else { 140662306a36Sopenharmony_ci idev->desync_factor = 0; 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci memset(&cfg, 0, sizeof(cfg)); 141162306a36Sopenharmony_ci cfg.valid_lft = min_t(__u32, ifp->valid_lft, 141262306a36Sopenharmony_ci idev->cnf.temp_valid_lft + age); 141362306a36Sopenharmony_ci cfg.preferred_lft = cnf_temp_preferred_lft + age - idev->desync_factor; 141462306a36Sopenharmony_ci cfg.preferred_lft = min_t(__u32, ifp->prefered_lft, cfg.preferred_lft); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci cfg.plen = ifp->prefix_len; 141762306a36Sopenharmony_ci tmp_tstamp = ifp->tstamp; 141862306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci /* A temporary address is created only if this calculated Preferred 142362306a36Sopenharmony_ci * Lifetime is greater than REGEN_ADVANCE time units. In particular, 142462306a36Sopenharmony_ci * an implementation must not create a temporary address with a zero 142562306a36Sopenharmony_ci * Preferred Lifetime. 142662306a36Sopenharmony_ci * Use age calculation as in addrconf_verify to avoid unnecessary 142762306a36Sopenharmony_ci * temporary addresses being generated. 142862306a36Sopenharmony_ci */ 142962306a36Sopenharmony_ci age = (now - tmp_tstamp + ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 143062306a36Sopenharmony_ci if (cfg.preferred_lft <= regen_advance + age) { 143162306a36Sopenharmony_ci in6_ifa_put(ifp); 143262306a36Sopenharmony_ci in6_dev_put(idev); 143362306a36Sopenharmony_ci ret = -1; 143462306a36Sopenharmony_ci goto out; 143562306a36Sopenharmony_ci } 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci cfg.ifa_flags = IFA_F_TEMPORARY; 143862306a36Sopenharmony_ci /* set in addrconf_prefix_rcv() */ 143962306a36Sopenharmony_ci if (ifp->flags & IFA_F_OPTIMISTIC) 144062306a36Sopenharmony_ci cfg.ifa_flags |= IFA_F_OPTIMISTIC; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci cfg.pfx = &addr; 144362306a36Sopenharmony_ci cfg.scope = ipv6_addr_scope(cfg.pfx); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci ift = ipv6_add_addr(idev, &cfg, block, NULL); 144662306a36Sopenharmony_ci if (IS_ERR(ift)) { 144762306a36Sopenharmony_ci in6_ifa_put(ifp); 144862306a36Sopenharmony_ci in6_dev_put(idev); 144962306a36Sopenharmony_ci pr_info("%s: retry temporary address regeneration\n", __func__); 145062306a36Sopenharmony_ci write_lock_bh(&idev->lock); 145162306a36Sopenharmony_ci goto retry; 145262306a36Sopenharmony_ci } 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci spin_lock_bh(&ift->lock); 145562306a36Sopenharmony_ci ift->ifpub = ifp; 145662306a36Sopenharmony_ci ift->cstamp = now; 145762306a36Sopenharmony_ci ift->tstamp = tmp_tstamp; 145862306a36Sopenharmony_ci spin_unlock_bh(&ift->lock); 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci addrconf_dad_start(ift); 146162306a36Sopenharmony_ci in6_ifa_put(ift); 146262306a36Sopenharmony_ci in6_dev_put(idev); 146362306a36Sopenharmony_ciout: 146462306a36Sopenharmony_ci return ret; 146562306a36Sopenharmony_ci} 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci/* 146862306a36Sopenharmony_ci * Choose an appropriate source address (RFC3484) 146962306a36Sopenharmony_ci */ 147062306a36Sopenharmony_cienum { 147162306a36Sopenharmony_ci IPV6_SADDR_RULE_INIT = 0, 147262306a36Sopenharmony_ci IPV6_SADDR_RULE_LOCAL, 147362306a36Sopenharmony_ci IPV6_SADDR_RULE_SCOPE, 147462306a36Sopenharmony_ci IPV6_SADDR_RULE_PREFERRED, 147562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MIP6 147662306a36Sopenharmony_ci IPV6_SADDR_RULE_HOA, 147762306a36Sopenharmony_ci#endif 147862306a36Sopenharmony_ci IPV6_SADDR_RULE_OIF, 147962306a36Sopenharmony_ci IPV6_SADDR_RULE_LABEL, 148062306a36Sopenharmony_ci IPV6_SADDR_RULE_PRIVACY, 148162306a36Sopenharmony_ci IPV6_SADDR_RULE_ORCHID, 148262306a36Sopenharmony_ci IPV6_SADDR_RULE_PREFIX, 148362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 148462306a36Sopenharmony_ci IPV6_SADDR_RULE_NOT_OPTIMISTIC, 148562306a36Sopenharmony_ci#endif 148662306a36Sopenharmony_ci IPV6_SADDR_RULE_MAX 148762306a36Sopenharmony_ci}; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_cistruct ipv6_saddr_score { 149062306a36Sopenharmony_ci int rule; 149162306a36Sopenharmony_ci int addr_type; 149262306a36Sopenharmony_ci struct inet6_ifaddr *ifa; 149362306a36Sopenharmony_ci DECLARE_BITMAP(scorebits, IPV6_SADDR_RULE_MAX); 149462306a36Sopenharmony_ci int scopedist; 149562306a36Sopenharmony_ci int matchlen; 149662306a36Sopenharmony_ci}; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_cistruct ipv6_saddr_dst { 149962306a36Sopenharmony_ci const struct in6_addr *addr; 150062306a36Sopenharmony_ci int ifindex; 150162306a36Sopenharmony_ci int scope; 150262306a36Sopenharmony_ci int label; 150362306a36Sopenharmony_ci unsigned int prefs; 150462306a36Sopenharmony_ci}; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_cistatic inline int ipv6_saddr_preferred(int type) 150762306a36Sopenharmony_ci{ 150862306a36Sopenharmony_ci if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4|IPV6_ADDR_LOOPBACK)) 150962306a36Sopenharmony_ci return 1; 151062306a36Sopenharmony_ci return 0; 151162306a36Sopenharmony_ci} 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_cistatic bool ipv6_use_optimistic_addr(struct net *net, 151462306a36Sopenharmony_ci struct inet6_dev *idev) 151562306a36Sopenharmony_ci{ 151662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 151762306a36Sopenharmony_ci if (!idev) 151862306a36Sopenharmony_ci return false; 151962306a36Sopenharmony_ci if (!net->ipv6.devconf_all->optimistic_dad && !idev->cnf.optimistic_dad) 152062306a36Sopenharmony_ci return false; 152162306a36Sopenharmony_ci if (!net->ipv6.devconf_all->use_optimistic && !idev->cnf.use_optimistic) 152262306a36Sopenharmony_ci return false; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci return true; 152562306a36Sopenharmony_ci#else 152662306a36Sopenharmony_ci return false; 152762306a36Sopenharmony_ci#endif 152862306a36Sopenharmony_ci} 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_cistatic bool ipv6_allow_optimistic_dad(struct net *net, 153162306a36Sopenharmony_ci struct inet6_dev *idev) 153262306a36Sopenharmony_ci{ 153362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 153462306a36Sopenharmony_ci if (!idev) 153562306a36Sopenharmony_ci return false; 153662306a36Sopenharmony_ci if (!net->ipv6.devconf_all->optimistic_dad && !idev->cnf.optimistic_dad) 153762306a36Sopenharmony_ci return false; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci return true; 154062306a36Sopenharmony_ci#else 154162306a36Sopenharmony_ci return false; 154262306a36Sopenharmony_ci#endif 154362306a36Sopenharmony_ci} 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_cistatic int ipv6_get_saddr_eval(struct net *net, 154662306a36Sopenharmony_ci struct ipv6_saddr_score *score, 154762306a36Sopenharmony_ci struct ipv6_saddr_dst *dst, 154862306a36Sopenharmony_ci int i) 154962306a36Sopenharmony_ci{ 155062306a36Sopenharmony_ci int ret; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci if (i <= score->rule) { 155362306a36Sopenharmony_ci switch (i) { 155462306a36Sopenharmony_ci case IPV6_SADDR_RULE_SCOPE: 155562306a36Sopenharmony_ci ret = score->scopedist; 155662306a36Sopenharmony_ci break; 155762306a36Sopenharmony_ci case IPV6_SADDR_RULE_PREFIX: 155862306a36Sopenharmony_ci ret = score->matchlen; 155962306a36Sopenharmony_ci break; 156062306a36Sopenharmony_ci default: 156162306a36Sopenharmony_ci ret = !!test_bit(i, score->scorebits); 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci goto out; 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci switch (i) { 156762306a36Sopenharmony_ci case IPV6_SADDR_RULE_INIT: 156862306a36Sopenharmony_ci /* Rule 0: remember if hiscore is not ready yet */ 156962306a36Sopenharmony_ci ret = !!score->ifa; 157062306a36Sopenharmony_ci break; 157162306a36Sopenharmony_ci case IPV6_SADDR_RULE_LOCAL: 157262306a36Sopenharmony_ci /* Rule 1: Prefer same address */ 157362306a36Sopenharmony_ci ret = ipv6_addr_equal(&score->ifa->addr, dst->addr); 157462306a36Sopenharmony_ci break; 157562306a36Sopenharmony_ci case IPV6_SADDR_RULE_SCOPE: 157662306a36Sopenharmony_ci /* Rule 2: Prefer appropriate scope 157762306a36Sopenharmony_ci * 157862306a36Sopenharmony_ci * ret 157962306a36Sopenharmony_ci * ^ 158062306a36Sopenharmony_ci * -1 | d 15 158162306a36Sopenharmony_ci * ---+--+-+---> scope 158262306a36Sopenharmony_ci * | 158362306a36Sopenharmony_ci * | d is scope of the destination. 158462306a36Sopenharmony_ci * B-d | \ 158562306a36Sopenharmony_ci * | \ <- smaller scope is better if 158662306a36Sopenharmony_ci * B-15 | \ if scope is enough for destination. 158762306a36Sopenharmony_ci * | ret = B - scope (-1 <= scope >= d <= 15). 158862306a36Sopenharmony_ci * d-C-1 | / 158962306a36Sopenharmony_ci * |/ <- greater is better 159062306a36Sopenharmony_ci * -C / if scope is not enough for destination. 159162306a36Sopenharmony_ci * /| ret = scope - C (-1 <= d < scope <= 15). 159262306a36Sopenharmony_ci * 159362306a36Sopenharmony_ci * d - C - 1 < B -15 (for all -1 <= d <= 15). 159462306a36Sopenharmony_ci * C > d + 14 - B >= 15 + 14 - B = 29 - B. 159562306a36Sopenharmony_ci * Assume B = 0 and we get C > 29. 159662306a36Sopenharmony_ci */ 159762306a36Sopenharmony_ci ret = __ipv6_addr_src_scope(score->addr_type); 159862306a36Sopenharmony_ci if (ret >= dst->scope) 159962306a36Sopenharmony_ci ret = -ret; 160062306a36Sopenharmony_ci else 160162306a36Sopenharmony_ci ret -= 128; /* 30 is enough */ 160262306a36Sopenharmony_ci score->scopedist = ret; 160362306a36Sopenharmony_ci break; 160462306a36Sopenharmony_ci case IPV6_SADDR_RULE_PREFERRED: 160562306a36Sopenharmony_ci { 160662306a36Sopenharmony_ci /* Rule 3: Avoid deprecated and optimistic addresses */ 160762306a36Sopenharmony_ci u8 avoid = IFA_F_DEPRECATED; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci if (!ipv6_use_optimistic_addr(net, score->ifa->idev)) 161062306a36Sopenharmony_ci avoid |= IFA_F_OPTIMISTIC; 161162306a36Sopenharmony_ci ret = ipv6_saddr_preferred(score->addr_type) || 161262306a36Sopenharmony_ci !(score->ifa->flags & avoid); 161362306a36Sopenharmony_ci break; 161462306a36Sopenharmony_ci } 161562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MIP6 161662306a36Sopenharmony_ci case IPV6_SADDR_RULE_HOA: 161762306a36Sopenharmony_ci { 161862306a36Sopenharmony_ci /* Rule 4: Prefer home address */ 161962306a36Sopenharmony_ci int prefhome = !(dst->prefs & IPV6_PREFER_SRC_COA); 162062306a36Sopenharmony_ci ret = !(score->ifa->flags & IFA_F_HOMEADDRESS) ^ prefhome; 162162306a36Sopenharmony_ci break; 162262306a36Sopenharmony_ci } 162362306a36Sopenharmony_ci#endif 162462306a36Sopenharmony_ci case IPV6_SADDR_RULE_OIF: 162562306a36Sopenharmony_ci /* Rule 5: Prefer outgoing interface */ 162662306a36Sopenharmony_ci ret = (!dst->ifindex || 162762306a36Sopenharmony_ci dst->ifindex == score->ifa->idev->dev->ifindex); 162862306a36Sopenharmony_ci break; 162962306a36Sopenharmony_ci case IPV6_SADDR_RULE_LABEL: 163062306a36Sopenharmony_ci /* Rule 6: Prefer matching label */ 163162306a36Sopenharmony_ci ret = ipv6_addr_label(net, 163262306a36Sopenharmony_ci &score->ifa->addr, score->addr_type, 163362306a36Sopenharmony_ci score->ifa->idev->dev->ifindex) == dst->label; 163462306a36Sopenharmony_ci break; 163562306a36Sopenharmony_ci case IPV6_SADDR_RULE_PRIVACY: 163662306a36Sopenharmony_ci { 163762306a36Sopenharmony_ci /* Rule 7: Prefer public address 163862306a36Sopenharmony_ci * Note: prefer temporary address if use_tempaddr >= 2 163962306a36Sopenharmony_ci */ 164062306a36Sopenharmony_ci int preftmp = dst->prefs & (IPV6_PREFER_SRC_PUBLIC|IPV6_PREFER_SRC_TMP) ? 164162306a36Sopenharmony_ci !!(dst->prefs & IPV6_PREFER_SRC_TMP) : 164262306a36Sopenharmony_ci score->ifa->idev->cnf.use_tempaddr >= 2; 164362306a36Sopenharmony_ci ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ preftmp; 164462306a36Sopenharmony_ci break; 164562306a36Sopenharmony_ci } 164662306a36Sopenharmony_ci case IPV6_SADDR_RULE_ORCHID: 164762306a36Sopenharmony_ci /* Rule 8-: Prefer ORCHID vs ORCHID or 164862306a36Sopenharmony_ci * non-ORCHID vs non-ORCHID 164962306a36Sopenharmony_ci */ 165062306a36Sopenharmony_ci ret = !(ipv6_addr_orchid(&score->ifa->addr) ^ 165162306a36Sopenharmony_ci ipv6_addr_orchid(dst->addr)); 165262306a36Sopenharmony_ci break; 165362306a36Sopenharmony_ci case IPV6_SADDR_RULE_PREFIX: 165462306a36Sopenharmony_ci /* Rule 8: Use longest matching prefix */ 165562306a36Sopenharmony_ci ret = ipv6_addr_diff(&score->ifa->addr, dst->addr); 165662306a36Sopenharmony_ci if (ret > score->ifa->prefix_len) 165762306a36Sopenharmony_ci ret = score->ifa->prefix_len; 165862306a36Sopenharmony_ci score->matchlen = ret; 165962306a36Sopenharmony_ci break; 166062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 166162306a36Sopenharmony_ci case IPV6_SADDR_RULE_NOT_OPTIMISTIC: 166262306a36Sopenharmony_ci /* Optimistic addresses still have lower precedence than other 166362306a36Sopenharmony_ci * preferred addresses. 166462306a36Sopenharmony_ci */ 166562306a36Sopenharmony_ci ret = !(score->ifa->flags & IFA_F_OPTIMISTIC); 166662306a36Sopenharmony_ci break; 166762306a36Sopenharmony_ci#endif 166862306a36Sopenharmony_ci default: 166962306a36Sopenharmony_ci ret = 0; 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci if (ret) 167362306a36Sopenharmony_ci __set_bit(i, score->scorebits); 167462306a36Sopenharmony_ci score->rule = i; 167562306a36Sopenharmony_ciout: 167662306a36Sopenharmony_ci return ret; 167762306a36Sopenharmony_ci} 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_cistatic int __ipv6_dev_get_saddr(struct net *net, 168062306a36Sopenharmony_ci struct ipv6_saddr_dst *dst, 168162306a36Sopenharmony_ci struct inet6_dev *idev, 168262306a36Sopenharmony_ci struct ipv6_saddr_score *scores, 168362306a36Sopenharmony_ci int hiscore_idx) 168462306a36Sopenharmony_ci{ 168562306a36Sopenharmony_ci struct ipv6_saddr_score *score = &scores[1 - hiscore_idx], *hiscore = &scores[hiscore_idx]; 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci list_for_each_entry_rcu(score->ifa, &idev->addr_list, if_list) { 168862306a36Sopenharmony_ci int i; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci /* 169162306a36Sopenharmony_ci * - Tentative Address (RFC2462 section 5.4) 169262306a36Sopenharmony_ci * - A tentative address is not considered 169362306a36Sopenharmony_ci * "assigned to an interface" in the traditional 169462306a36Sopenharmony_ci * sense, unless it is also flagged as optimistic. 169562306a36Sopenharmony_ci * - Candidate Source Address (section 4) 169662306a36Sopenharmony_ci * - In any case, anycast addresses, multicast 169762306a36Sopenharmony_ci * addresses, and the unspecified address MUST 169862306a36Sopenharmony_ci * NOT be included in a candidate set. 169962306a36Sopenharmony_ci */ 170062306a36Sopenharmony_ci if ((score->ifa->flags & IFA_F_TENTATIVE) && 170162306a36Sopenharmony_ci (!(score->ifa->flags & IFA_F_OPTIMISTIC))) 170262306a36Sopenharmony_ci continue; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci score->addr_type = __ipv6_addr_type(&score->ifa->addr); 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci if (unlikely(score->addr_type == IPV6_ADDR_ANY || 170762306a36Sopenharmony_ci score->addr_type & IPV6_ADDR_MULTICAST)) { 170862306a36Sopenharmony_ci net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s", 170962306a36Sopenharmony_ci idev->dev->name); 171062306a36Sopenharmony_ci continue; 171162306a36Sopenharmony_ci } 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci score->rule = -1; 171462306a36Sopenharmony_ci bitmap_zero(score->scorebits, IPV6_SADDR_RULE_MAX); 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) { 171762306a36Sopenharmony_ci int minihiscore, miniscore; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci minihiscore = ipv6_get_saddr_eval(net, hiscore, dst, i); 172062306a36Sopenharmony_ci miniscore = ipv6_get_saddr_eval(net, score, dst, i); 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci if (minihiscore > miniscore) { 172362306a36Sopenharmony_ci if (i == IPV6_SADDR_RULE_SCOPE && 172462306a36Sopenharmony_ci score->scopedist > 0) { 172562306a36Sopenharmony_ci /* 172662306a36Sopenharmony_ci * special case: 172762306a36Sopenharmony_ci * each remaining entry 172862306a36Sopenharmony_ci * has too small (not enough) 172962306a36Sopenharmony_ci * scope, because ifa entries 173062306a36Sopenharmony_ci * are sorted by their scope 173162306a36Sopenharmony_ci * values. 173262306a36Sopenharmony_ci */ 173362306a36Sopenharmony_ci goto out; 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci break; 173662306a36Sopenharmony_ci } else if (minihiscore < miniscore) { 173762306a36Sopenharmony_ci swap(hiscore, score); 173862306a36Sopenharmony_ci hiscore_idx = 1 - hiscore_idx; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci /* restore our iterator */ 174162306a36Sopenharmony_ci score->ifa = hiscore->ifa; 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci break; 174462306a36Sopenharmony_ci } 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ciout: 174862306a36Sopenharmony_ci return hiscore_idx; 174962306a36Sopenharmony_ci} 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_cistatic int ipv6_get_saddr_master(struct net *net, 175262306a36Sopenharmony_ci const struct net_device *dst_dev, 175362306a36Sopenharmony_ci const struct net_device *master, 175462306a36Sopenharmony_ci struct ipv6_saddr_dst *dst, 175562306a36Sopenharmony_ci struct ipv6_saddr_score *scores, 175662306a36Sopenharmony_ci int hiscore_idx) 175762306a36Sopenharmony_ci{ 175862306a36Sopenharmony_ci struct inet6_dev *idev; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci idev = __in6_dev_get(dst_dev); 176162306a36Sopenharmony_ci if (idev) 176262306a36Sopenharmony_ci hiscore_idx = __ipv6_dev_get_saddr(net, dst, idev, 176362306a36Sopenharmony_ci scores, hiscore_idx); 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci idev = __in6_dev_get(master); 176662306a36Sopenharmony_ci if (idev) 176762306a36Sopenharmony_ci hiscore_idx = __ipv6_dev_get_saddr(net, dst, idev, 176862306a36Sopenharmony_ci scores, hiscore_idx); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci return hiscore_idx; 177162306a36Sopenharmony_ci} 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ciint ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev, 177462306a36Sopenharmony_ci const struct in6_addr *daddr, unsigned int prefs, 177562306a36Sopenharmony_ci struct in6_addr *saddr) 177662306a36Sopenharmony_ci{ 177762306a36Sopenharmony_ci struct ipv6_saddr_score scores[2], *hiscore; 177862306a36Sopenharmony_ci struct ipv6_saddr_dst dst; 177962306a36Sopenharmony_ci struct inet6_dev *idev; 178062306a36Sopenharmony_ci struct net_device *dev; 178162306a36Sopenharmony_ci int dst_type; 178262306a36Sopenharmony_ci bool use_oif_addr = false; 178362306a36Sopenharmony_ci int hiscore_idx = 0; 178462306a36Sopenharmony_ci int ret = 0; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci dst_type = __ipv6_addr_type(daddr); 178762306a36Sopenharmony_ci dst.addr = daddr; 178862306a36Sopenharmony_ci dst.ifindex = dst_dev ? dst_dev->ifindex : 0; 178962306a36Sopenharmony_ci dst.scope = __ipv6_addr_src_scope(dst_type); 179062306a36Sopenharmony_ci dst.label = ipv6_addr_label(net, daddr, dst_type, dst.ifindex); 179162306a36Sopenharmony_ci dst.prefs = prefs; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci scores[hiscore_idx].rule = -1; 179462306a36Sopenharmony_ci scores[hiscore_idx].ifa = NULL; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci rcu_read_lock(); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci /* Candidate Source Address (section 4) 179962306a36Sopenharmony_ci * - multicast and link-local destination address, 180062306a36Sopenharmony_ci * the set of candidate source address MUST only 180162306a36Sopenharmony_ci * include addresses assigned to interfaces 180262306a36Sopenharmony_ci * belonging to the same link as the outgoing 180362306a36Sopenharmony_ci * interface. 180462306a36Sopenharmony_ci * (- For site-local destination addresses, the 180562306a36Sopenharmony_ci * set of candidate source addresses MUST only 180662306a36Sopenharmony_ci * include addresses assigned to interfaces 180762306a36Sopenharmony_ci * belonging to the same site as the outgoing 180862306a36Sopenharmony_ci * interface.) 180962306a36Sopenharmony_ci * - "It is RECOMMENDED that the candidate source addresses 181062306a36Sopenharmony_ci * be the set of unicast addresses assigned to the 181162306a36Sopenharmony_ci * interface that will be used to send to the destination 181262306a36Sopenharmony_ci * (the 'outgoing' interface)." (RFC 6724) 181362306a36Sopenharmony_ci */ 181462306a36Sopenharmony_ci if (dst_dev) { 181562306a36Sopenharmony_ci idev = __in6_dev_get(dst_dev); 181662306a36Sopenharmony_ci if ((dst_type & IPV6_ADDR_MULTICAST) || 181762306a36Sopenharmony_ci dst.scope <= IPV6_ADDR_SCOPE_LINKLOCAL || 181862306a36Sopenharmony_ci (idev && idev->cnf.use_oif_addrs_only)) { 181962306a36Sopenharmony_ci use_oif_addr = true; 182062306a36Sopenharmony_ci } 182162306a36Sopenharmony_ci } 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci if (use_oif_addr) { 182462306a36Sopenharmony_ci if (idev) 182562306a36Sopenharmony_ci hiscore_idx = __ipv6_dev_get_saddr(net, &dst, idev, scores, hiscore_idx); 182662306a36Sopenharmony_ci } else { 182762306a36Sopenharmony_ci const struct net_device *master; 182862306a36Sopenharmony_ci int master_idx = 0; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci /* if dst_dev exists and is enslaved to an L3 device, then 183162306a36Sopenharmony_ci * prefer addresses from dst_dev and then the master over 183262306a36Sopenharmony_ci * any other enslaved devices in the L3 domain. 183362306a36Sopenharmony_ci */ 183462306a36Sopenharmony_ci master = l3mdev_master_dev_rcu(dst_dev); 183562306a36Sopenharmony_ci if (master) { 183662306a36Sopenharmony_ci master_idx = master->ifindex; 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci hiscore_idx = ipv6_get_saddr_master(net, dst_dev, 183962306a36Sopenharmony_ci master, &dst, 184062306a36Sopenharmony_ci scores, hiscore_idx); 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci if (scores[hiscore_idx].ifa) 184362306a36Sopenharmony_ci goto out; 184462306a36Sopenharmony_ci } 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci for_each_netdev_rcu(net, dev) { 184762306a36Sopenharmony_ci /* only consider addresses on devices in the 184862306a36Sopenharmony_ci * same L3 domain 184962306a36Sopenharmony_ci */ 185062306a36Sopenharmony_ci if (l3mdev_master_ifindex_rcu(dev) != master_idx) 185162306a36Sopenharmony_ci continue; 185262306a36Sopenharmony_ci idev = __in6_dev_get(dev); 185362306a36Sopenharmony_ci if (!idev) 185462306a36Sopenharmony_ci continue; 185562306a36Sopenharmony_ci hiscore_idx = __ipv6_dev_get_saddr(net, &dst, idev, scores, hiscore_idx); 185662306a36Sopenharmony_ci } 185762306a36Sopenharmony_ci } 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ciout: 186062306a36Sopenharmony_ci hiscore = &scores[hiscore_idx]; 186162306a36Sopenharmony_ci if (!hiscore->ifa) 186262306a36Sopenharmony_ci ret = -EADDRNOTAVAIL; 186362306a36Sopenharmony_ci else 186462306a36Sopenharmony_ci *saddr = hiscore->ifa->addr; 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci rcu_read_unlock(); 186762306a36Sopenharmony_ci return ret; 186862306a36Sopenharmony_ci} 186962306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_dev_get_saddr); 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_cistatic int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, 187262306a36Sopenharmony_ci u32 banned_flags) 187362306a36Sopenharmony_ci{ 187462306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 187562306a36Sopenharmony_ci int err = -EADDRNOTAVAIL; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci list_for_each_entry_reverse(ifp, &idev->addr_list, if_list) { 187862306a36Sopenharmony_ci if (ifp->scope > IFA_LINK) 187962306a36Sopenharmony_ci break; 188062306a36Sopenharmony_ci if (ifp->scope == IFA_LINK && 188162306a36Sopenharmony_ci !(ifp->flags & banned_flags)) { 188262306a36Sopenharmony_ci *addr = ifp->addr; 188362306a36Sopenharmony_ci err = 0; 188462306a36Sopenharmony_ci break; 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci } 188762306a36Sopenharmony_ci return err; 188862306a36Sopenharmony_ci} 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ciint ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 189162306a36Sopenharmony_ci u32 banned_flags) 189262306a36Sopenharmony_ci{ 189362306a36Sopenharmony_ci struct inet6_dev *idev; 189462306a36Sopenharmony_ci int err = -EADDRNOTAVAIL; 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci rcu_read_lock(); 189762306a36Sopenharmony_ci idev = __in6_dev_get(dev); 189862306a36Sopenharmony_ci if (idev) { 189962306a36Sopenharmony_ci read_lock_bh(&idev->lock); 190062306a36Sopenharmony_ci err = __ipv6_get_lladdr(idev, addr, banned_flags); 190162306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 190262306a36Sopenharmony_ci } 190362306a36Sopenharmony_ci rcu_read_unlock(); 190462306a36Sopenharmony_ci return err; 190562306a36Sopenharmony_ci} 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_cistatic int ipv6_count_addresses(const struct inet6_dev *idev) 190862306a36Sopenharmony_ci{ 190962306a36Sopenharmony_ci const struct inet6_ifaddr *ifp; 191062306a36Sopenharmony_ci int cnt = 0; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci rcu_read_lock(); 191362306a36Sopenharmony_ci list_for_each_entry_rcu(ifp, &idev->addr_list, if_list) 191462306a36Sopenharmony_ci cnt++; 191562306a36Sopenharmony_ci rcu_read_unlock(); 191662306a36Sopenharmony_ci return cnt; 191762306a36Sopenharmony_ci} 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ciint ipv6_chk_addr(struct net *net, const struct in6_addr *addr, 192062306a36Sopenharmony_ci const struct net_device *dev, int strict) 192162306a36Sopenharmony_ci{ 192262306a36Sopenharmony_ci return ipv6_chk_addr_and_flags(net, addr, dev, !dev, 192362306a36Sopenharmony_ci strict, IFA_F_TENTATIVE); 192462306a36Sopenharmony_ci} 192562306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_chk_addr); 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci/* device argument is used to find the L3 domain of interest. If 192862306a36Sopenharmony_ci * skip_dev_check is set, then the ifp device is not checked against 192962306a36Sopenharmony_ci * the passed in dev argument. So the 2 cases for addresses checks are: 193062306a36Sopenharmony_ci * 1. does the address exist in the L3 domain that dev is part of 193162306a36Sopenharmony_ci * (skip_dev_check = true), or 193262306a36Sopenharmony_ci * 193362306a36Sopenharmony_ci * 2. does the address exist on the specific device 193462306a36Sopenharmony_ci * (skip_dev_check = false) 193562306a36Sopenharmony_ci */ 193662306a36Sopenharmony_cistatic struct net_device * 193762306a36Sopenharmony_ci__ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, 193862306a36Sopenharmony_ci const struct net_device *dev, bool skip_dev_check, 193962306a36Sopenharmony_ci int strict, u32 banned_flags) 194062306a36Sopenharmony_ci{ 194162306a36Sopenharmony_ci unsigned int hash = inet6_addr_hash(net, addr); 194262306a36Sopenharmony_ci struct net_device *l3mdev, *ndev; 194362306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 194462306a36Sopenharmony_ci u32 ifp_flags; 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci rcu_read_lock(); 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci l3mdev = l3mdev_master_dev_rcu(dev); 194962306a36Sopenharmony_ci if (skip_dev_check) 195062306a36Sopenharmony_ci dev = NULL; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) { 195362306a36Sopenharmony_ci ndev = ifp->idev->dev; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci if (l3mdev_master_dev_rcu(ndev) != l3mdev) 195662306a36Sopenharmony_ci continue; 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci /* Decouple optimistic from tentative for evaluation here. 195962306a36Sopenharmony_ci * Ban optimistic addresses explicitly, when required. 196062306a36Sopenharmony_ci */ 196162306a36Sopenharmony_ci ifp_flags = (ifp->flags&IFA_F_OPTIMISTIC) 196262306a36Sopenharmony_ci ? (ifp->flags&~IFA_F_TENTATIVE) 196362306a36Sopenharmony_ci : ifp->flags; 196462306a36Sopenharmony_ci if (ipv6_addr_equal(&ifp->addr, addr) && 196562306a36Sopenharmony_ci !(ifp_flags&banned_flags) && 196662306a36Sopenharmony_ci (!dev || ndev == dev || 196762306a36Sopenharmony_ci !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))) { 196862306a36Sopenharmony_ci rcu_read_unlock(); 196962306a36Sopenharmony_ci return ndev; 197062306a36Sopenharmony_ci } 197162306a36Sopenharmony_ci } 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci rcu_read_unlock(); 197462306a36Sopenharmony_ci return NULL; 197562306a36Sopenharmony_ci} 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ciint ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, 197862306a36Sopenharmony_ci const struct net_device *dev, bool skip_dev_check, 197962306a36Sopenharmony_ci int strict, u32 banned_flags) 198062306a36Sopenharmony_ci{ 198162306a36Sopenharmony_ci return __ipv6_chk_addr_and_flags(net, addr, dev, skip_dev_check, 198262306a36Sopenharmony_ci strict, banned_flags) ? 1 : 0; 198362306a36Sopenharmony_ci} 198462306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_chk_addr_and_flags); 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci/* Compares an address/prefix_len with addresses on device @dev. 198862306a36Sopenharmony_ci * If one is found it returns true. 198962306a36Sopenharmony_ci */ 199062306a36Sopenharmony_cibool ipv6_chk_custom_prefix(const struct in6_addr *addr, 199162306a36Sopenharmony_ci const unsigned int prefix_len, struct net_device *dev) 199262306a36Sopenharmony_ci{ 199362306a36Sopenharmony_ci const struct inet6_ifaddr *ifa; 199462306a36Sopenharmony_ci const struct inet6_dev *idev; 199562306a36Sopenharmony_ci bool ret = false; 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci rcu_read_lock(); 199862306a36Sopenharmony_ci idev = __in6_dev_get(dev); 199962306a36Sopenharmony_ci if (idev) { 200062306a36Sopenharmony_ci list_for_each_entry_rcu(ifa, &idev->addr_list, if_list) { 200162306a36Sopenharmony_ci ret = ipv6_prefix_equal(addr, &ifa->addr, prefix_len); 200262306a36Sopenharmony_ci if (ret) 200362306a36Sopenharmony_ci break; 200462306a36Sopenharmony_ci } 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci rcu_read_unlock(); 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci return ret; 200962306a36Sopenharmony_ci} 201062306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_chk_custom_prefix); 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ciint ipv6_chk_prefix(const struct in6_addr *addr, struct net_device *dev) 201362306a36Sopenharmony_ci{ 201462306a36Sopenharmony_ci const struct inet6_ifaddr *ifa; 201562306a36Sopenharmony_ci const struct inet6_dev *idev; 201662306a36Sopenharmony_ci int onlink; 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci onlink = 0; 201962306a36Sopenharmony_ci rcu_read_lock(); 202062306a36Sopenharmony_ci idev = __in6_dev_get(dev); 202162306a36Sopenharmony_ci if (idev) { 202262306a36Sopenharmony_ci list_for_each_entry_rcu(ifa, &idev->addr_list, if_list) { 202362306a36Sopenharmony_ci onlink = ipv6_prefix_equal(addr, &ifa->addr, 202462306a36Sopenharmony_ci ifa->prefix_len); 202562306a36Sopenharmony_ci if (onlink) 202662306a36Sopenharmony_ci break; 202762306a36Sopenharmony_ci } 202862306a36Sopenharmony_ci } 202962306a36Sopenharmony_ci rcu_read_unlock(); 203062306a36Sopenharmony_ci return onlink; 203162306a36Sopenharmony_ci} 203262306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_chk_prefix); 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci/** 203562306a36Sopenharmony_ci * ipv6_dev_find - find the first device with a given source address. 203662306a36Sopenharmony_ci * @net: the net namespace 203762306a36Sopenharmony_ci * @addr: the source address 203862306a36Sopenharmony_ci * @dev: used to find the L3 domain of interest 203962306a36Sopenharmony_ci * 204062306a36Sopenharmony_ci * The caller should be protected by RCU, or RTNL. 204162306a36Sopenharmony_ci */ 204262306a36Sopenharmony_cistruct net_device *ipv6_dev_find(struct net *net, const struct in6_addr *addr, 204362306a36Sopenharmony_ci struct net_device *dev) 204462306a36Sopenharmony_ci{ 204562306a36Sopenharmony_ci return __ipv6_chk_addr_and_flags(net, addr, dev, !dev, 1, 204662306a36Sopenharmony_ci IFA_F_TENTATIVE); 204762306a36Sopenharmony_ci} 204862306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_dev_find); 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_cistruct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *addr, 205162306a36Sopenharmony_ci struct net_device *dev, int strict) 205262306a36Sopenharmony_ci{ 205362306a36Sopenharmony_ci unsigned int hash = inet6_addr_hash(net, addr); 205462306a36Sopenharmony_ci struct inet6_ifaddr *ifp, *result = NULL; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci rcu_read_lock(); 205762306a36Sopenharmony_ci hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) { 205862306a36Sopenharmony_ci if (ipv6_addr_equal(&ifp->addr, addr)) { 205962306a36Sopenharmony_ci if (!dev || ifp->idev->dev == dev || 206062306a36Sopenharmony_ci !(ifp->scope&(IFA_LINK|IFA_HOST) || strict)) { 206162306a36Sopenharmony_ci result = ifp; 206262306a36Sopenharmony_ci in6_ifa_hold(ifp); 206362306a36Sopenharmony_ci break; 206462306a36Sopenharmony_ci } 206562306a36Sopenharmony_ci } 206662306a36Sopenharmony_ci } 206762306a36Sopenharmony_ci rcu_read_unlock(); 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci return result; 207062306a36Sopenharmony_ci} 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci/* Gets referenced address, destroys ifaddr */ 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_cistatic void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed) 207562306a36Sopenharmony_ci{ 207662306a36Sopenharmony_ci if (dad_failed) 207762306a36Sopenharmony_ci ifp->flags |= IFA_F_DADFAILED; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci if (ifp->flags&IFA_F_TEMPORARY) { 208062306a36Sopenharmony_ci struct inet6_ifaddr *ifpub; 208162306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 208262306a36Sopenharmony_ci ifpub = ifp->ifpub; 208362306a36Sopenharmony_ci if (ifpub) { 208462306a36Sopenharmony_ci in6_ifa_hold(ifpub); 208562306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 208662306a36Sopenharmony_ci ipv6_create_tempaddr(ifpub, true); 208762306a36Sopenharmony_ci in6_ifa_put(ifpub); 208862306a36Sopenharmony_ci } else { 208962306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci ipv6_del_addr(ifp); 209262306a36Sopenharmony_ci } else if (ifp->flags&IFA_F_PERMANENT || !dad_failed) { 209362306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 209462306a36Sopenharmony_ci addrconf_del_dad_work(ifp); 209562306a36Sopenharmony_ci ifp->flags |= IFA_F_TENTATIVE; 209662306a36Sopenharmony_ci if (dad_failed) 209762306a36Sopenharmony_ci ifp->flags &= ~IFA_F_OPTIMISTIC; 209862306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 209962306a36Sopenharmony_ci if (dad_failed) 210062306a36Sopenharmony_ci ipv6_ifa_notify(0, ifp); 210162306a36Sopenharmony_ci in6_ifa_put(ifp); 210262306a36Sopenharmony_ci } else { 210362306a36Sopenharmony_ci ipv6_del_addr(ifp); 210462306a36Sopenharmony_ci } 210562306a36Sopenharmony_ci} 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_cistatic int addrconf_dad_end(struct inet6_ifaddr *ifp) 210862306a36Sopenharmony_ci{ 210962306a36Sopenharmony_ci int err = -ENOENT; 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 211262306a36Sopenharmony_ci if (ifp->state == INET6_IFADDR_STATE_DAD) { 211362306a36Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_POSTDAD; 211462306a36Sopenharmony_ci err = 0; 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci return err; 211962306a36Sopenharmony_ci} 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_civoid addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp) 212262306a36Sopenharmony_ci{ 212362306a36Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 212462306a36Sopenharmony_ci struct net *net = dev_net(idev->dev); 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci if (addrconf_dad_end(ifp)) { 212762306a36Sopenharmony_ci in6_ifa_put(ifp); 212862306a36Sopenharmony_ci return; 212962306a36Sopenharmony_ci } 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci net_info_ratelimited("%s: IPv6 duplicate address %pI6c used by %pM detected!\n", 213262306a36Sopenharmony_ci ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source); 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci if (ifp->flags & IFA_F_STABLE_PRIVACY) { 213762306a36Sopenharmony_ci struct in6_addr new_addr; 213862306a36Sopenharmony_ci struct inet6_ifaddr *ifp2; 213962306a36Sopenharmony_ci int retries = ifp->stable_privacy_retry + 1; 214062306a36Sopenharmony_ci struct ifa6_config cfg = { 214162306a36Sopenharmony_ci .pfx = &new_addr, 214262306a36Sopenharmony_ci .plen = ifp->prefix_len, 214362306a36Sopenharmony_ci .ifa_flags = ifp->flags, 214462306a36Sopenharmony_ci .valid_lft = ifp->valid_lft, 214562306a36Sopenharmony_ci .preferred_lft = ifp->prefered_lft, 214662306a36Sopenharmony_ci .scope = ifp->scope, 214762306a36Sopenharmony_ci }; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci if (retries > net->ipv6.sysctl.idgen_retries) { 215062306a36Sopenharmony_ci net_info_ratelimited("%s: privacy stable address generation failed because of DAD conflicts!\n", 215162306a36Sopenharmony_ci ifp->idev->dev->name); 215262306a36Sopenharmony_ci goto errdad; 215362306a36Sopenharmony_ci } 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci new_addr = ifp->addr; 215662306a36Sopenharmony_ci if (ipv6_generate_stable_address(&new_addr, retries, 215762306a36Sopenharmony_ci idev)) 215862306a36Sopenharmony_ci goto errdad; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci if (idev->cnf.max_addresses && 216362306a36Sopenharmony_ci ipv6_count_addresses(idev) >= 216462306a36Sopenharmony_ci idev->cnf.max_addresses) 216562306a36Sopenharmony_ci goto lock_errdad; 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci net_info_ratelimited("%s: generating new stable privacy address because of DAD conflict\n", 216862306a36Sopenharmony_ci ifp->idev->dev->name); 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci ifp2 = ipv6_add_addr(idev, &cfg, false, NULL); 217162306a36Sopenharmony_ci if (IS_ERR(ifp2)) 217262306a36Sopenharmony_ci goto lock_errdad; 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci spin_lock_bh(&ifp2->lock); 217562306a36Sopenharmony_ci ifp2->stable_privacy_retry = retries; 217662306a36Sopenharmony_ci ifp2->state = INET6_IFADDR_STATE_PREDAD; 217762306a36Sopenharmony_ci spin_unlock_bh(&ifp2->lock); 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay); 218062306a36Sopenharmony_ci in6_ifa_put(ifp2); 218162306a36Sopenharmony_cilock_errdad: 218262306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 218362306a36Sopenharmony_ci } 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_cierrdad: 218662306a36Sopenharmony_ci /* transition from _POSTDAD to _ERRDAD */ 218762306a36Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_ERRDAD; 218862306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci addrconf_mod_dad_work(ifp, 0); 219162306a36Sopenharmony_ci in6_ifa_put(ifp); 219262306a36Sopenharmony_ci} 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci/* Join to solicited addr multicast group. 219562306a36Sopenharmony_ci * caller must hold RTNL */ 219662306a36Sopenharmony_civoid addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr) 219762306a36Sopenharmony_ci{ 219862306a36Sopenharmony_ci struct in6_addr maddr; 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci if (dev->flags&(IFF_LOOPBACK|IFF_NOARP)) 220162306a36Sopenharmony_ci return; 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci addrconf_addr_solict_mult(addr, &maddr); 220462306a36Sopenharmony_ci ipv6_dev_mc_inc(dev, &maddr); 220562306a36Sopenharmony_ci} 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci/* caller must hold RTNL */ 220862306a36Sopenharmony_civoid addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr) 220962306a36Sopenharmony_ci{ 221062306a36Sopenharmony_ci struct in6_addr maddr; 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci if (idev->dev->flags&(IFF_LOOPBACK|IFF_NOARP)) 221362306a36Sopenharmony_ci return; 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci addrconf_addr_solict_mult(addr, &maddr); 221662306a36Sopenharmony_ci __ipv6_dev_mc_dec(idev, &maddr); 221762306a36Sopenharmony_ci} 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci/* caller must hold RTNL */ 222062306a36Sopenharmony_cistatic void addrconf_join_anycast(struct inet6_ifaddr *ifp) 222162306a36Sopenharmony_ci{ 222262306a36Sopenharmony_ci struct in6_addr addr; 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci if (ifp->prefix_len >= 127) /* RFC 6164 */ 222562306a36Sopenharmony_ci return; 222662306a36Sopenharmony_ci ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); 222762306a36Sopenharmony_ci if (ipv6_addr_any(&addr)) 222862306a36Sopenharmony_ci return; 222962306a36Sopenharmony_ci __ipv6_dev_ac_inc(ifp->idev, &addr); 223062306a36Sopenharmony_ci} 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci/* caller must hold RTNL */ 223362306a36Sopenharmony_cistatic void addrconf_leave_anycast(struct inet6_ifaddr *ifp) 223462306a36Sopenharmony_ci{ 223562306a36Sopenharmony_ci struct in6_addr addr; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci if (ifp->prefix_len >= 127) /* RFC 6164 */ 223862306a36Sopenharmony_ci return; 223962306a36Sopenharmony_ci ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); 224062306a36Sopenharmony_ci if (ipv6_addr_any(&addr)) 224162306a36Sopenharmony_ci return; 224262306a36Sopenharmony_ci __ipv6_dev_ac_dec(ifp->idev, &addr); 224362306a36Sopenharmony_ci} 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_cistatic int addrconf_ifid_6lowpan(u8 *eui, struct net_device *dev) 224662306a36Sopenharmony_ci{ 224762306a36Sopenharmony_ci switch (dev->addr_len) { 224862306a36Sopenharmony_ci case ETH_ALEN: 224962306a36Sopenharmony_ci memcpy(eui, dev->dev_addr, 3); 225062306a36Sopenharmony_ci eui[3] = 0xFF; 225162306a36Sopenharmony_ci eui[4] = 0xFE; 225262306a36Sopenharmony_ci memcpy(eui + 5, dev->dev_addr + 3, 3); 225362306a36Sopenharmony_ci break; 225462306a36Sopenharmony_ci case EUI64_ADDR_LEN: 225562306a36Sopenharmony_ci memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN); 225662306a36Sopenharmony_ci eui[0] ^= 2; 225762306a36Sopenharmony_ci break; 225862306a36Sopenharmony_ci default: 225962306a36Sopenharmony_ci return -1; 226062306a36Sopenharmony_ci } 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci return 0; 226362306a36Sopenharmony_ci} 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_cistatic int addrconf_ifid_ieee1394(u8 *eui, struct net_device *dev) 226662306a36Sopenharmony_ci{ 226762306a36Sopenharmony_ci const union fwnet_hwaddr *ha; 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci if (dev->addr_len != FWNET_ALEN) 227062306a36Sopenharmony_ci return -1; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci ha = (const union fwnet_hwaddr *)dev->dev_addr; 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci memcpy(eui, &ha->uc.uniq_id, sizeof(ha->uc.uniq_id)); 227562306a36Sopenharmony_ci eui[0] ^= 2; 227662306a36Sopenharmony_ci return 0; 227762306a36Sopenharmony_ci} 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_cistatic int addrconf_ifid_arcnet(u8 *eui, struct net_device *dev) 228062306a36Sopenharmony_ci{ 228162306a36Sopenharmony_ci /* XXX: inherit EUI-64 from other interface -- yoshfuji */ 228262306a36Sopenharmony_ci if (dev->addr_len != ARCNET_ALEN) 228362306a36Sopenharmony_ci return -1; 228462306a36Sopenharmony_ci memset(eui, 0, 7); 228562306a36Sopenharmony_ci eui[7] = *(u8 *)dev->dev_addr; 228662306a36Sopenharmony_ci return 0; 228762306a36Sopenharmony_ci} 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_cistatic int addrconf_ifid_infiniband(u8 *eui, struct net_device *dev) 229062306a36Sopenharmony_ci{ 229162306a36Sopenharmony_ci if (dev->addr_len != INFINIBAND_ALEN) 229262306a36Sopenharmony_ci return -1; 229362306a36Sopenharmony_ci memcpy(eui, dev->dev_addr + 12, 8); 229462306a36Sopenharmony_ci eui[0] |= 2; 229562306a36Sopenharmony_ci return 0; 229662306a36Sopenharmony_ci} 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_cistatic int __ipv6_isatap_ifid(u8 *eui, __be32 addr) 229962306a36Sopenharmony_ci{ 230062306a36Sopenharmony_ci if (addr == 0) 230162306a36Sopenharmony_ci return -1; 230262306a36Sopenharmony_ci eui[0] = (ipv4_is_zeronet(addr) || ipv4_is_private_10(addr) || 230362306a36Sopenharmony_ci ipv4_is_loopback(addr) || ipv4_is_linklocal_169(addr) || 230462306a36Sopenharmony_ci ipv4_is_private_172(addr) || ipv4_is_test_192(addr) || 230562306a36Sopenharmony_ci ipv4_is_anycast_6to4(addr) || ipv4_is_private_192(addr) || 230662306a36Sopenharmony_ci ipv4_is_test_198(addr) || ipv4_is_multicast(addr) || 230762306a36Sopenharmony_ci ipv4_is_lbcast(addr)) ? 0x00 : 0x02; 230862306a36Sopenharmony_ci eui[1] = 0; 230962306a36Sopenharmony_ci eui[2] = 0x5E; 231062306a36Sopenharmony_ci eui[3] = 0xFE; 231162306a36Sopenharmony_ci memcpy(eui + 4, &addr, 4); 231262306a36Sopenharmony_ci return 0; 231362306a36Sopenharmony_ci} 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_cistatic int addrconf_ifid_sit(u8 *eui, struct net_device *dev) 231662306a36Sopenharmony_ci{ 231762306a36Sopenharmony_ci if (dev->priv_flags & IFF_ISATAP) 231862306a36Sopenharmony_ci return __ipv6_isatap_ifid(eui, *(__be32 *)dev->dev_addr); 231962306a36Sopenharmony_ci return -1; 232062306a36Sopenharmony_ci} 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_cistatic int addrconf_ifid_gre(u8 *eui, struct net_device *dev) 232362306a36Sopenharmony_ci{ 232462306a36Sopenharmony_ci return __ipv6_isatap_ifid(eui, *(__be32 *)dev->dev_addr); 232562306a36Sopenharmony_ci} 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_cistatic int addrconf_ifid_ip6tnl(u8 *eui, struct net_device *dev) 232862306a36Sopenharmony_ci{ 232962306a36Sopenharmony_ci memcpy(eui, dev->perm_addr, 3); 233062306a36Sopenharmony_ci memcpy(eui + 5, dev->perm_addr + 3, 3); 233162306a36Sopenharmony_ci eui[3] = 0xFF; 233262306a36Sopenharmony_ci eui[4] = 0xFE; 233362306a36Sopenharmony_ci eui[0] ^= 2; 233462306a36Sopenharmony_ci return 0; 233562306a36Sopenharmony_ci} 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_cistatic int ipv6_generate_eui64(u8 *eui, struct net_device *dev) 233862306a36Sopenharmony_ci{ 233962306a36Sopenharmony_ci switch (dev->type) { 234062306a36Sopenharmony_ci case ARPHRD_ETHER: 234162306a36Sopenharmony_ci case ARPHRD_FDDI: 234262306a36Sopenharmony_ci return addrconf_ifid_eui48(eui, dev); 234362306a36Sopenharmony_ci case ARPHRD_ARCNET: 234462306a36Sopenharmony_ci return addrconf_ifid_arcnet(eui, dev); 234562306a36Sopenharmony_ci case ARPHRD_INFINIBAND: 234662306a36Sopenharmony_ci return addrconf_ifid_infiniband(eui, dev); 234762306a36Sopenharmony_ci case ARPHRD_SIT: 234862306a36Sopenharmony_ci return addrconf_ifid_sit(eui, dev); 234962306a36Sopenharmony_ci case ARPHRD_IPGRE: 235062306a36Sopenharmony_ci case ARPHRD_TUNNEL: 235162306a36Sopenharmony_ci return addrconf_ifid_gre(eui, dev); 235262306a36Sopenharmony_ci case ARPHRD_6LOWPAN: 235362306a36Sopenharmony_ci return addrconf_ifid_6lowpan(eui, dev); 235462306a36Sopenharmony_ci case ARPHRD_IEEE1394: 235562306a36Sopenharmony_ci return addrconf_ifid_ieee1394(eui, dev); 235662306a36Sopenharmony_ci case ARPHRD_TUNNEL6: 235762306a36Sopenharmony_ci case ARPHRD_IP6GRE: 235862306a36Sopenharmony_ci case ARPHRD_RAWIP: 235962306a36Sopenharmony_ci return addrconf_ifid_ip6tnl(eui, dev); 236062306a36Sopenharmony_ci } 236162306a36Sopenharmony_ci return -1; 236262306a36Sopenharmony_ci} 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_cistatic int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev) 236562306a36Sopenharmony_ci{ 236662306a36Sopenharmony_ci int err = -1; 236762306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci read_lock_bh(&idev->lock); 237062306a36Sopenharmony_ci list_for_each_entry_reverse(ifp, &idev->addr_list, if_list) { 237162306a36Sopenharmony_ci if (ifp->scope > IFA_LINK) 237262306a36Sopenharmony_ci break; 237362306a36Sopenharmony_ci if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) { 237462306a36Sopenharmony_ci memcpy(eui, ifp->addr.s6_addr+8, 8); 237562306a36Sopenharmony_ci err = 0; 237662306a36Sopenharmony_ci break; 237762306a36Sopenharmony_ci } 237862306a36Sopenharmony_ci } 237962306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 238062306a36Sopenharmony_ci return err; 238162306a36Sopenharmony_ci} 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci/* Generation of a randomized Interface Identifier 238462306a36Sopenharmony_ci * draft-ietf-6man-rfc4941bis, Section 3.3.1 238562306a36Sopenharmony_ci */ 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_cistatic void ipv6_gen_rnd_iid(struct in6_addr *addr) 238862306a36Sopenharmony_ci{ 238962306a36Sopenharmony_ciregen: 239062306a36Sopenharmony_ci get_random_bytes(&addr->s6_addr[8], 8); 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci /* <draft-ietf-6man-rfc4941bis-08.txt>, Section 3.3.1: 239362306a36Sopenharmony_ci * check if generated address is not inappropriate: 239462306a36Sopenharmony_ci * 239562306a36Sopenharmony_ci * - Reserved IPv6 Interface Identifiers 239662306a36Sopenharmony_ci * - XXX: already assigned to an address on the device 239762306a36Sopenharmony_ci */ 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci /* Subnet-router anycast: 0000:0000:0000:0000 */ 240062306a36Sopenharmony_ci if (!(addr->s6_addr32[2] | addr->s6_addr32[3])) 240162306a36Sopenharmony_ci goto regen; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci /* IANA Ethernet block: 0200:5EFF:FE00:0000-0200:5EFF:FE00:5212 240462306a36Sopenharmony_ci * Proxy Mobile IPv6: 0200:5EFF:FE00:5213 240562306a36Sopenharmony_ci * IANA Ethernet block: 0200:5EFF:FE00:5214-0200:5EFF:FEFF:FFFF 240662306a36Sopenharmony_ci */ 240762306a36Sopenharmony_ci if (ntohl(addr->s6_addr32[2]) == 0x02005eff && 240862306a36Sopenharmony_ci (ntohl(addr->s6_addr32[3]) & 0Xff000000) == 0xfe000000) 240962306a36Sopenharmony_ci goto regen; 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci /* Reserved subnet anycast addresses */ 241262306a36Sopenharmony_ci if (ntohl(addr->s6_addr32[2]) == 0xfdffffff && 241362306a36Sopenharmony_ci ntohl(addr->s6_addr32[3]) >= 0Xffffff80) 241462306a36Sopenharmony_ci goto regen; 241562306a36Sopenharmony_ci} 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci/* 241862306a36Sopenharmony_ci * Add prefix route. 241962306a36Sopenharmony_ci */ 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_cistatic void 242262306a36Sopenharmony_ciaddrconf_prefix_route(struct in6_addr *pfx, int plen, u32 metric, 242362306a36Sopenharmony_ci struct net_device *dev, unsigned long expires, 242462306a36Sopenharmony_ci u32 flags, gfp_t gfp_flags) 242562306a36Sopenharmony_ci{ 242662306a36Sopenharmony_ci struct fib6_config cfg = { 242762306a36Sopenharmony_ci .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX, 242862306a36Sopenharmony_ci .fc_metric = metric ? : IP6_RT_PRIO_ADDRCONF, 242962306a36Sopenharmony_ci .fc_ifindex = dev->ifindex, 243062306a36Sopenharmony_ci .fc_expires = expires, 243162306a36Sopenharmony_ci .fc_dst_len = plen, 243262306a36Sopenharmony_ci .fc_flags = RTF_UP | flags, 243362306a36Sopenharmony_ci .fc_nlinfo.nl_net = dev_net(dev), 243462306a36Sopenharmony_ci .fc_protocol = RTPROT_KERNEL, 243562306a36Sopenharmony_ci .fc_type = RTN_UNICAST, 243662306a36Sopenharmony_ci }; 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci cfg.fc_dst = *pfx; 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci /* Prevent useless cloning on PtP SIT. 244162306a36Sopenharmony_ci This thing is done here expecting that the whole 244262306a36Sopenharmony_ci class of non-broadcast devices need not cloning. 244362306a36Sopenharmony_ci */ 244462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT) 244562306a36Sopenharmony_ci if (dev->type == ARPHRD_SIT && (dev->flags & IFF_POINTOPOINT)) 244662306a36Sopenharmony_ci cfg.fc_flags |= RTF_NONEXTHOP; 244762306a36Sopenharmony_ci#endif 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci ip6_route_add(&cfg, gfp_flags, NULL); 245062306a36Sopenharmony_ci} 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_cistatic struct fib6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, 245462306a36Sopenharmony_ci int plen, 245562306a36Sopenharmony_ci const struct net_device *dev, 245662306a36Sopenharmony_ci u32 flags, u32 noflags, 245762306a36Sopenharmony_ci bool no_gw) 245862306a36Sopenharmony_ci{ 245962306a36Sopenharmony_ci struct fib6_node *fn; 246062306a36Sopenharmony_ci struct fib6_info *rt = NULL; 246162306a36Sopenharmony_ci struct fib6_table *table; 246262306a36Sopenharmony_ci u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX; 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci table = fib6_get_table(dev_net(dev), tb_id); 246562306a36Sopenharmony_ci if (!table) 246662306a36Sopenharmony_ci return NULL; 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ci rcu_read_lock(); 246962306a36Sopenharmony_ci fn = fib6_locate(&table->tb6_root, pfx, plen, NULL, 0, true); 247062306a36Sopenharmony_ci if (!fn) 247162306a36Sopenharmony_ci goto out; 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci for_each_fib6_node_rt_rcu(fn) { 247462306a36Sopenharmony_ci /* prefix routes only use builtin fib6_nh */ 247562306a36Sopenharmony_ci if (rt->nh) 247662306a36Sopenharmony_ci continue; 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci if (rt->fib6_nh->fib_nh_dev->ifindex != dev->ifindex) 247962306a36Sopenharmony_ci continue; 248062306a36Sopenharmony_ci if (no_gw && rt->fib6_nh->fib_nh_gw_family) 248162306a36Sopenharmony_ci continue; 248262306a36Sopenharmony_ci if ((rt->fib6_flags & flags) != flags) 248362306a36Sopenharmony_ci continue; 248462306a36Sopenharmony_ci if ((rt->fib6_flags & noflags) != 0) 248562306a36Sopenharmony_ci continue; 248662306a36Sopenharmony_ci if (!fib6_info_hold_safe(rt)) 248762306a36Sopenharmony_ci continue; 248862306a36Sopenharmony_ci break; 248962306a36Sopenharmony_ci } 249062306a36Sopenharmony_ciout: 249162306a36Sopenharmony_ci rcu_read_unlock(); 249262306a36Sopenharmony_ci return rt; 249362306a36Sopenharmony_ci} 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci/* Create "default" multicast route to the interface */ 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_cistatic void addrconf_add_mroute(struct net_device *dev) 249962306a36Sopenharmony_ci{ 250062306a36Sopenharmony_ci struct fib6_config cfg = { 250162306a36Sopenharmony_ci .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_LOCAL, 250262306a36Sopenharmony_ci .fc_metric = IP6_RT_PRIO_ADDRCONF, 250362306a36Sopenharmony_ci .fc_ifindex = dev->ifindex, 250462306a36Sopenharmony_ci .fc_dst_len = 8, 250562306a36Sopenharmony_ci .fc_flags = RTF_UP, 250662306a36Sopenharmony_ci .fc_type = RTN_MULTICAST, 250762306a36Sopenharmony_ci .fc_nlinfo.nl_net = dev_net(dev), 250862306a36Sopenharmony_ci .fc_protocol = RTPROT_KERNEL, 250962306a36Sopenharmony_ci }; 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci ipv6_addr_set(&cfg.fc_dst, htonl(0xFF000000), 0, 0, 0); 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_ci ip6_route_add(&cfg, GFP_KERNEL, NULL); 251462306a36Sopenharmony_ci} 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_cistatic struct inet6_dev *addrconf_add_dev(struct net_device *dev) 251762306a36Sopenharmony_ci{ 251862306a36Sopenharmony_ci struct inet6_dev *idev; 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_ci ASSERT_RTNL(); 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci idev = ipv6_find_idev(dev); 252362306a36Sopenharmony_ci if (IS_ERR(idev)) 252462306a36Sopenharmony_ci return idev; 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci if (idev->cnf.disable_ipv6) 252762306a36Sopenharmony_ci return ERR_PTR(-EACCES); 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci /* Add default multicast route */ 253062306a36Sopenharmony_ci if (!(dev->flags & IFF_LOOPBACK) && !netif_is_l3_master(dev)) 253162306a36Sopenharmony_ci addrconf_add_mroute(dev); 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci return idev; 253462306a36Sopenharmony_ci} 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_cistatic void manage_tempaddrs(struct inet6_dev *idev, 253762306a36Sopenharmony_ci struct inet6_ifaddr *ifp, 253862306a36Sopenharmony_ci __u32 valid_lft, __u32 prefered_lft, 253962306a36Sopenharmony_ci bool create, unsigned long now) 254062306a36Sopenharmony_ci{ 254162306a36Sopenharmony_ci u32 flags; 254262306a36Sopenharmony_ci struct inet6_ifaddr *ift; 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci read_lock_bh(&idev->lock); 254562306a36Sopenharmony_ci /* update all temporary addresses in the list */ 254662306a36Sopenharmony_ci list_for_each_entry(ift, &idev->tempaddr_list, tmp_list) { 254762306a36Sopenharmony_ci int age, max_valid, max_prefered; 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci if (ifp != ift->ifpub) 255062306a36Sopenharmony_ci continue; 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci /* RFC 4941 section 3.3: 255362306a36Sopenharmony_ci * If a received option will extend the lifetime of a public 255462306a36Sopenharmony_ci * address, the lifetimes of temporary addresses should 255562306a36Sopenharmony_ci * be extended, subject to the overall constraint that no 255662306a36Sopenharmony_ci * temporary addresses should ever remain "valid" or "preferred" 255762306a36Sopenharmony_ci * for a time longer than (TEMP_VALID_LIFETIME) or 255862306a36Sopenharmony_ci * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR), respectively. 255962306a36Sopenharmony_ci */ 256062306a36Sopenharmony_ci age = (now - ift->cstamp) / HZ; 256162306a36Sopenharmony_ci max_valid = idev->cnf.temp_valid_lft - age; 256262306a36Sopenharmony_ci if (max_valid < 0) 256362306a36Sopenharmony_ci max_valid = 0; 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci max_prefered = idev->cnf.temp_prefered_lft - 256662306a36Sopenharmony_ci idev->desync_factor - age; 256762306a36Sopenharmony_ci if (max_prefered < 0) 256862306a36Sopenharmony_ci max_prefered = 0; 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci if (valid_lft > max_valid) 257162306a36Sopenharmony_ci valid_lft = max_valid; 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci if (prefered_lft > max_prefered) 257462306a36Sopenharmony_ci prefered_lft = max_prefered; 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci spin_lock(&ift->lock); 257762306a36Sopenharmony_ci flags = ift->flags; 257862306a36Sopenharmony_ci ift->valid_lft = valid_lft; 257962306a36Sopenharmony_ci ift->prefered_lft = prefered_lft; 258062306a36Sopenharmony_ci ift->tstamp = now; 258162306a36Sopenharmony_ci if (prefered_lft > 0) 258262306a36Sopenharmony_ci ift->flags &= ~IFA_F_DEPRECATED; 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci spin_unlock(&ift->lock); 258562306a36Sopenharmony_ci if (!(flags&IFA_F_TENTATIVE)) 258662306a36Sopenharmony_ci ipv6_ifa_notify(0, ift); 258762306a36Sopenharmony_ci } 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci /* Also create a temporary address if it's enabled but no temporary 259062306a36Sopenharmony_ci * address currently exists. 259162306a36Sopenharmony_ci * However, we get called with valid_lft == 0, prefered_lft == 0, create == false 259262306a36Sopenharmony_ci * as part of cleanup (ie. deleting the mngtmpaddr). 259362306a36Sopenharmony_ci * We don't want that to result in creating a new temporary ip address. 259462306a36Sopenharmony_ci */ 259562306a36Sopenharmony_ci if (list_empty(&idev->tempaddr_list) && (valid_lft || prefered_lft)) 259662306a36Sopenharmony_ci create = true; 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_ci if (create && idev->cnf.use_tempaddr > 0) { 259962306a36Sopenharmony_ci /* When a new public address is created as described 260062306a36Sopenharmony_ci * in [ADDRCONF], also create a new temporary address. 260162306a36Sopenharmony_ci */ 260262306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 260362306a36Sopenharmony_ci ipv6_create_tempaddr(ifp, false); 260462306a36Sopenharmony_ci } else { 260562306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 260662306a36Sopenharmony_ci } 260762306a36Sopenharmony_ci} 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_cistatic bool is_addr_mode_generate_stable(struct inet6_dev *idev) 261062306a36Sopenharmony_ci{ 261162306a36Sopenharmony_ci return idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY || 261262306a36Sopenharmony_ci idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM; 261362306a36Sopenharmony_ci} 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_ciint addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, 261662306a36Sopenharmony_ci const struct prefix_info *pinfo, 261762306a36Sopenharmony_ci struct inet6_dev *in6_dev, 261862306a36Sopenharmony_ci const struct in6_addr *addr, int addr_type, 261962306a36Sopenharmony_ci u32 addr_flags, bool sllao, bool tokenized, 262062306a36Sopenharmony_ci __u32 valid_lft, u32 prefered_lft) 262162306a36Sopenharmony_ci{ 262262306a36Sopenharmony_ci struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1); 262362306a36Sopenharmony_ci int create = 0, update_lft = 0; 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci if (!ifp && valid_lft) { 262662306a36Sopenharmony_ci int max_addresses = in6_dev->cnf.max_addresses; 262762306a36Sopenharmony_ci struct ifa6_config cfg = { 262862306a36Sopenharmony_ci .pfx = addr, 262962306a36Sopenharmony_ci .plen = pinfo->prefix_len, 263062306a36Sopenharmony_ci .ifa_flags = addr_flags, 263162306a36Sopenharmony_ci .valid_lft = valid_lft, 263262306a36Sopenharmony_ci .preferred_lft = prefered_lft, 263362306a36Sopenharmony_ci .scope = addr_type & IPV6_ADDR_SCOPE_MASK, 263462306a36Sopenharmony_ci .ifa_proto = IFAPROT_KERNEL_RA 263562306a36Sopenharmony_ci }; 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 263862306a36Sopenharmony_ci if ((net->ipv6.devconf_all->optimistic_dad || 263962306a36Sopenharmony_ci in6_dev->cnf.optimistic_dad) && 264062306a36Sopenharmony_ci !net->ipv6.devconf_all->forwarding && sllao) 264162306a36Sopenharmony_ci cfg.ifa_flags |= IFA_F_OPTIMISTIC; 264262306a36Sopenharmony_ci#endif 264362306a36Sopenharmony_ci 264462306a36Sopenharmony_ci /* Do not allow to create too much of autoconfigured 264562306a36Sopenharmony_ci * addresses; this would be too easy way to crash kernel. 264662306a36Sopenharmony_ci */ 264762306a36Sopenharmony_ci if (!max_addresses || 264862306a36Sopenharmony_ci ipv6_count_addresses(in6_dev) < max_addresses) 264962306a36Sopenharmony_ci ifp = ipv6_add_addr(in6_dev, &cfg, false, NULL); 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci if (IS_ERR_OR_NULL(ifp)) 265262306a36Sopenharmony_ci return -1; 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci create = 1; 265562306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 265662306a36Sopenharmony_ci ifp->flags |= IFA_F_MANAGETEMPADDR; 265762306a36Sopenharmony_ci ifp->cstamp = jiffies; 265862306a36Sopenharmony_ci ifp->tokenized = tokenized; 265962306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 266062306a36Sopenharmony_ci addrconf_dad_start(ifp); 266162306a36Sopenharmony_ci } 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci if (ifp) { 266462306a36Sopenharmony_ci u32 flags; 266562306a36Sopenharmony_ci unsigned long now; 266662306a36Sopenharmony_ci u32 stored_lft; 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_ci /* update lifetime (RFC2462 5.5.3 e) */ 266962306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 267062306a36Sopenharmony_ci now = jiffies; 267162306a36Sopenharmony_ci if (ifp->valid_lft > (now - ifp->tstamp) / HZ) 267262306a36Sopenharmony_ci stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ; 267362306a36Sopenharmony_ci else 267462306a36Sopenharmony_ci stored_lft = 0; 267562306a36Sopenharmony_ci if (!create && stored_lft) { 267662306a36Sopenharmony_ci const u32 minimum_lft = min_t(u32, 267762306a36Sopenharmony_ci stored_lft, MIN_VALID_LIFETIME); 267862306a36Sopenharmony_ci valid_lft = max(valid_lft, minimum_lft); 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci /* RFC4862 Section 5.5.3e: 268162306a36Sopenharmony_ci * "Note that the preferred lifetime of the 268262306a36Sopenharmony_ci * corresponding address is always reset to 268362306a36Sopenharmony_ci * the Preferred Lifetime in the received 268462306a36Sopenharmony_ci * Prefix Information option, regardless of 268562306a36Sopenharmony_ci * whether the valid lifetime is also reset or 268662306a36Sopenharmony_ci * ignored." 268762306a36Sopenharmony_ci * 268862306a36Sopenharmony_ci * So we should always update prefered_lft here. 268962306a36Sopenharmony_ci */ 269062306a36Sopenharmony_ci update_lft = 1; 269162306a36Sopenharmony_ci } 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci if (update_lft) { 269462306a36Sopenharmony_ci ifp->valid_lft = valid_lft; 269562306a36Sopenharmony_ci ifp->prefered_lft = prefered_lft; 269662306a36Sopenharmony_ci ifp->tstamp = now; 269762306a36Sopenharmony_ci flags = ifp->flags; 269862306a36Sopenharmony_ci ifp->flags &= ~IFA_F_DEPRECATED; 269962306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci if (!(flags&IFA_F_TENTATIVE)) 270262306a36Sopenharmony_ci ipv6_ifa_notify(0, ifp); 270362306a36Sopenharmony_ci } else 270462306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft, 270762306a36Sopenharmony_ci create, now); 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_ci in6_ifa_put(ifp); 271062306a36Sopenharmony_ci addrconf_verify(net); 271162306a36Sopenharmony_ci } 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci return 0; 271462306a36Sopenharmony_ci} 271562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr); 271662306a36Sopenharmony_ci 271762306a36Sopenharmony_civoid addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) 271862306a36Sopenharmony_ci{ 271962306a36Sopenharmony_ci struct prefix_info *pinfo; 272062306a36Sopenharmony_ci __u32 valid_lft; 272162306a36Sopenharmony_ci __u32 prefered_lft; 272262306a36Sopenharmony_ci int addr_type, err; 272362306a36Sopenharmony_ci u32 addr_flags = 0; 272462306a36Sopenharmony_ci struct inet6_dev *in6_dev; 272562306a36Sopenharmony_ci struct net *net = dev_net(dev); 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci pinfo = (struct prefix_info *) opt; 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci if (len < sizeof(struct prefix_info)) { 273062306a36Sopenharmony_ci netdev_dbg(dev, "addrconf: prefix option too short\n"); 273162306a36Sopenharmony_ci return; 273262306a36Sopenharmony_ci } 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci /* 273562306a36Sopenharmony_ci * Validation checks ([ADDRCONF], page 19) 273662306a36Sopenharmony_ci */ 273762306a36Sopenharmony_ci 273862306a36Sopenharmony_ci addr_type = ipv6_addr_type(&pinfo->prefix); 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci if (addr_type & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)) 274162306a36Sopenharmony_ci return; 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci valid_lft = ntohl(pinfo->valid); 274462306a36Sopenharmony_ci prefered_lft = ntohl(pinfo->prefered); 274562306a36Sopenharmony_ci 274662306a36Sopenharmony_ci if (prefered_lft > valid_lft) { 274762306a36Sopenharmony_ci net_warn_ratelimited("addrconf: prefix option has invalid lifetime\n"); 274862306a36Sopenharmony_ci return; 274962306a36Sopenharmony_ci } 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci in6_dev = in6_dev_get(dev); 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci if (!in6_dev) { 275462306a36Sopenharmony_ci net_dbg_ratelimited("addrconf: device %s not configured\n", 275562306a36Sopenharmony_ci dev->name); 275662306a36Sopenharmony_ci return; 275762306a36Sopenharmony_ci } 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci if (valid_lft != 0 && valid_lft < in6_dev->cnf.accept_ra_min_lft) 276062306a36Sopenharmony_ci goto put; 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci /* 276362306a36Sopenharmony_ci * Two things going on here: 276462306a36Sopenharmony_ci * 1) Add routes for on-link prefixes 276562306a36Sopenharmony_ci * 2) Configure prefixes with the auto flag set 276662306a36Sopenharmony_ci */ 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci if (pinfo->onlink) { 276962306a36Sopenharmony_ci struct fib6_info *rt; 277062306a36Sopenharmony_ci unsigned long rt_expires; 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci /* Avoid arithmetic overflow. Really, we could 277362306a36Sopenharmony_ci * save rt_expires in seconds, likely valid_lft, 277462306a36Sopenharmony_ci * but it would require division in fib gc, that it 277562306a36Sopenharmony_ci * not good. 277662306a36Sopenharmony_ci */ 277762306a36Sopenharmony_ci if (HZ > USER_HZ) 277862306a36Sopenharmony_ci rt_expires = addrconf_timeout_fixup(valid_lft, HZ); 277962306a36Sopenharmony_ci else 278062306a36Sopenharmony_ci rt_expires = addrconf_timeout_fixup(valid_lft, USER_HZ); 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci if (addrconf_finite_timeout(rt_expires)) 278362306a36Sopenharmony_ci rt_expires *= HZ; 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci rt = addrconf_get_prefix_route(&pinfo->prefix, 278662306a36Sopenharmony_ci pinfo->prefix_len, 278762306a36Sopenharmony_ci dev, 278862306a36Sopenharmony_ci RTF_ADDRCONF | RTF_PREFIX_RT, 278962306a36Sopenharmony_ci RTF_DEFAULT, true); 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci if (rt) { 279262306a36Sopenharmony_ci /* Autoconf prefix route */ 279362306a36Sopenharmony_ci if (valid_lft == 0) { 279462306a36Sopenharmony_ci ip6_del_rt(net, rt, false); 279562306a36Sopenharmony_ci rt = NULL; 279662306a36Sopenharmony_ci } else if (addrconf_finite_timeout(rt_expires)) { 279762306a36Sopenharmony_ci /* not infinity */ 279862306a36Sopenharmony_ci fib6_set_expires(rt, jiffies + rt_expires); 279962306a36Sopenharmony_ci } else { 280062306a36Sopenharmony_ci fib6_clean_expires(rt); 280162306a36Sopenharmony_ci } 280262306a36Sopenharmony_ci } else if (valid_lft) { 280362306a36Sopenharmony_ci clock_t expires = 0; 280462306a36Sopenharmony_ci int flags = RTF_ADDRCONF | RTF_PREFIX_RT; 280562306a36Sopenharmony_ci if (addrconf_finite_timeout(rt_expires)) { 280662306a36Sopenharmony_ci /* not infinity */ 280762306a36Sopenharmony_ci flags |= RTF_EXPIRES; 280862306a36Sopenharmony_ci expires = jiffies_to_clock_t(rt_expires); 280962306a36Sopenharmony_ci } 281062306a36Sopenharmony_ci addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len, 281162306a36Sopenharmony_ci 0, dev, expires, flags, 281262306a36Sopenharmony_ci GFP_ATOMIC); 281362306a36Sopenharmony_ci } 281462306a36Sopenharmony_ci fib6_info_release(rt); 281562306a36Sopenharmony_ci } 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci /* Try to figure out our local address for this prefix */ 281862306a36Sopenharmony_ci 281962306a36Sopenharmony_ci if (pinfo->autoconf && in6_dev->cnf.autoconf) { 282062306a36Sopenharmony_ci struct in6_addr addr; 282162306a36Sopenharmony_ci bool tokenized = false, dev_addr_generated = false; 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci if (pinfo->prefix_len == 64) { 282462306a36Sopenharmony_ci memcpy(&addr, &pinfo->prefix, 8); 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci if (!ipv6_addr_any(&in6_dev->token)) { 282762306a36Sopenharmony_ci read_lock_bh(&in6_dev->lock); 282862306a36Sopenharmony_ci memcpy(addr.s6_addr + 8, 282962306a36Sopenharmony_ci in6_dev->token.s6_addr + 8, 8); 283062306a36Sopenharmony_ci read_unlock_bh(&in6_dev->lock); 283162306a36Sopenharmony_ci tokenized = true; 283262306a36Sopenharmony_ci } else if (is_addr_mode_generate_stable(in6_dev) && 283362306a36Sopenharmony_ci !ipv6_generate_stable_address(&addr, 0, 283462306a36Sopenharmony_ci in6_dev)) { 283562306a36Sopenharmony_ci addr_flags |= IFA_F_STABLE_PRIVACY; 283662306a36Sopenharmony_ci goto ok; 283762306a36Sopenharmony_ci } else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) && 283862306a36Sopenharmony_ci ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) { 283962306a36Sopenharmony_ci goto put; 284062306a36Sopenharmony_ci } else { 284162306a36Sopenharmony_ci dev_addr_generated = true; 284262306a36Sopenharmony_ci } 284362306a36Sopenharmony_ci goto ok; 284462306a36Sopenharmony_ci } 284562306a36Sopenharmony_ci net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n", 284662306a36Sopenharmony_ci pinfo->prefix_len); 284762306a36Sopenharmony_ci goto put; 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_ciok: 285062306a36Sopenharmony_ci err = addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, 285162306a36Sopenharmony_ci &addr, addr_type, 285262306a36Sopenharmony_ci addr_flags, sllao, 285362306a36Sopenharmony_ci tokenized, valid_lft, 285462306a36Sopenharmony_ci prefered_lft); 285562306a36Sopenharmony_ci if (err) 285662306a36Sopenharmony_ci goto put; 285762306a36Sopenharmony_ci 285862306a36Sopenharmony_ci /* Ignore error case here because previous prefix add addr was 285962306a36Sopenharmony_ci * successful which will be notified. 286062306a36Sopenharmony_ci */ 286162306a36Sopenharmony_ci ndisc_ops_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, &addr, 286262306a36Sopenharmony_ci addr_type, addr_flags, sllao, 286362306a36Sopenharmony_ci tokenized, valid_lft, 286462306a36Sopenharmony_ci prefered_lft, 286562306a36Sopenharmony_ci dev_addr_generated); 286662306a36Sopenharmony_ci } 286762306a36Sopenharmony_ci inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo); 286862306a36Sopenharmony_ciput: 286962306a36Sopenharmony_ci in6_dev_put(in6_dev); 287062306a36Sopenharmony_ci} 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_cistatic int addrconf_set_sit_dstaddr(struct net *net, struct net_device *dev, 287362306a36Sopenharmony_ci struct in6_ifreq *ireq) 287462306a36Sopenharmony_ci{ 287562306a36Sopenharmony_ci struct ip_tunnel_parm p = { }; 287662306a36Sopenharmony_ci int err; 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci if (!(ipv6_addr_type(&ireq->ifr6_addr) & IPV6_ADDR_COMPATv4)) 287962306a36Sopenharmony_ci return -EADDRNOTAVAIL; 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_ci p.iph.daddr = ireq->ifr6_addr.s6_addr32[3]; 288262306a36Sopenharmony_ci p.iph.version = 4; 288362306a36Sopenharmony_ci p.iph.ihl = 5; 288462306a36Sopenharmony_ci p.iph.protocol = IPPROTO_IPV6; 288562306a36Sopenharmony_ci p.iph.ttl = 64; 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci if (!dev->netdev_ops->ndo_tunnel_ctl) 288862306a36Sopenharmony_ci return -EOPNOTSUPP; 288962306a36Sopenharmony_ci err = dev->netdev_ops->ndo_tunnel_ctl(dev, &p, SIOCADDTUNNEL); 289062306a36Sopenharmony_ci if (err) 289162306a36Sopenharmony_ci return err; 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci dev = __dev_get_by_name(net, p.name); 289462306a36Sopenharmony_ci if (!dev) 289562306a36Sopenharmony_ci return -ENOBUFS; 289662306a36Sopenharmony_ci return dev_open(dev, NULL); 289762306a36Sopenharmony_ci} 289862306a36Sopenharmony_ci 289962306a36Sopenharmony_ci/* 290062306a36Sopenharmony_ci * Set destination address. 290162306a36Sopenharmony_ci * Special case for SIT interfaces where we create a new "virtual" 290262306a36Sopenharmony_ci * device. 290362306a36Sopenharmony_ci */ 290462306a36Sopenharmony_ciint addrconf_set_dstaddr(struct net *net, void __user *arg) 290562306a36Sopenharmony_ci{ 290662306a36Sopenharmony_ci struct net_device *dev; 290762306a36Sopenharmony_ci struct in6_ifreq ireq; 290862306a36Sopenharmony_ci int err = -ENODEV; 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_IPV6_SIT)) 291162306a36Sopenharmony_ci return -ENODEV; 291262306a36Sopenharmony_ci if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) 291362306a36Sopenharmony_ci return -EFAULT; 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_ci rtnl_lock(); 291662306a36Sopenharmony_ci dev = __dev_get_by_index(net, ireq.ifr6_ifindex); 291762306a36Sopenharmony_ci if (dev && dev->type == ARPHRD_SIT) 291862306a36Sopenharmony_ci err = addrconf_set_sit_dstaddr(net, dev, &ireq); 291962306a36Sopenharmony_ci rtnl_unlock(); 292062306a36Sopenharmony_ci return err; 292162306a36Sopenharmony_ci} 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_cistatic int ipv6_mc_config(struct sock *sk, bool join, 292462306a36Sopenharmony_ci const struct in6_addr *addr, int ifindex) 292562306a36Sopenharmony_ci{ 292662306a36Sopenharmony_ci int ret; 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci ASSERT_RTNL(); 292962306a36Sopenharmony_ci 293062306a36Sopenharmony_ci lock_sock(sk); 293162306a36Sopenharmony_ci if (join) 293262306a36Sopenharmony_ci ret = ipv6_sock_mc_join(sk, ifindex, addr); 293362306a36Sopenharmony_ci else 293462306a36Sopenharmony_ci ret = ipv6_sock_mc_drop(sk, ifindex, addr); 293562306a36Sopenharmony_ci release_sock(sk); 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci return ret; 293862306a36Sopenharmony_ci} 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci/* 294162306a36Sopenharmony_ci * Manual configuration of address on an interface 294262306a36Sopenharmony_ci */ 294362306a36Sopenharmony_cistatic int inet6_addr_add(struct net *net, int ifindex, 294462306a36Sopenharmony_ci struct ifa6_config *cfg, 294562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 294662306a36Sopenharmony_ci{ 294762306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 294862306a36Sopenharmony_ci struct inet6_dev *idev; 294962306a36Sopenharmony_ci struct net_device *dev; 295062306a36Sopenharmony_ci unsigned long timeout; 295162306a36Sopenharmony_ci clock_t expires; 295262306a36Sopenharmony_ci u32 flags; 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci ASSERT_RTNL(); 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci if (cfg->plen > 128) { 295762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid prefix length"); 295862306a36Sopenharmony_ci return -EINVAL; 295962306a36Sopenharmony_ci } 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci /* check the lifetime */ 296262306a36Sopenharmony_ci if (!cfg->valid_lft || cfg->preferred_lft > cfg->valid_lft) { 296362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "address lifetime invalid"); 296462306a36Sopenharmony_ci return -EINVAL; 296562306a36Sopenharmony_ci } 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_ci if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR && cfg->plen != 64) { 296862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "address with \"mngtmpaddr\" flag must have a prefix length of 64"); 296962306a36Sopenharmony_ci return -EINVAL; 297062306a36Sopenharmony_ci } 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci dev = __dev_get_by_index(net, ifindex); 297362306a36Sopenharmony_ci if (!dev) 297462306a36Sopenharmony_ci return -ENODEV; 297562306a36Sopenharmony_ci 297662306a36Sopenharmony_ci idev = addrconf_add_dev(dev); 297762306a36Sopenharmony_ci if (IS_ERR(idev)) { 297862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "IPv6 is disabled on this device"); 297962306a36Sopenharmony_ci return PTR_ERR(idev); 298062306a36Sopenharmony_ci } 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci if (cfg->ifa_flags & IFA_F_MCAUTOJOIN) { 298362306a36Sopenharmony_ci int ret = ipv6_mc_config(net->ipv6.mc_autojoin_sk, 298462306a36Sopenharmony_ci true, cfg->pfx, ifindex); 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci if (ret < 0) { 298762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Multicast auto join failed"); 298862306a36Sopenharmony_ci return ret; 298962306a36Sopenharmony_ci } 299062306a36Sopenharmony_ci } 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci cfg->scope = ipv6_addr_scope(cfg->pfx); 299362306a36Sopenharmony_ci 299462306a36Sopenharmony_ci timeout = addrconf_timeout_fixup(cfg->valid_lft, HZ); 299562306a36Sopenharmony_ci if (addrconf_finite_timeout(timeout)) { 299662306a36Sopenharmony_ci expires = jiffies_to_clock_t(timeout * HZ); 299762306a36Sopenharmony_ci cfg->valid_lft = timeout; 299862306a36Sopenharmony_ci flags = RTF_EXPIRES; 299962306a36Sopenharmony_ci } else { 300062306a36Sopenharmony_ci expires = 0; 300162306a36Sopenharmony_ci flags = 0; 300262306a36Sopenharmony_ci cfg->ifa_flags |= IFA_F_PERMANENT; 300362306a36Sopenharmony_ci } 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci timeout = addrconf_timeout_fixup(cfg->preferred_lft, HZ); 300662306a36Sopenharmony_ci if (addrconf_finite_timeout(timeout)) { 300762306a36Sopenharmony_ci if (timeout == 0) 300862306a36Sopenharmony_ci cfg->ifa_flags |= IFA_F_DEPRECATED; 300962306a36Sopenharmony_ci cfg->preferred_lft = timeout; 301062306a36Sopenharmony_ci } 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci ifp = ipv6_add_addr(idev, cfg, true, extack); 301362306a36Sopenharmony_ci if (!IS_ERR(ifp)) { 301462306a36Sopenharmony_ci if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) { 301562306a36Sopenharmony_ci addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 301662306a36Sopenharmony_ci ifp->rt_priority, dev, expires, 301762306a36Sopenharmony_ci flags, GFP_KERNEL); 301862306a36Sopenharmony_ci } 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci /* Send a netlink notification if DAD is enabled and 302162306a36Sopenharmony_ci * optimistic flag is not set 302262306a36Sopenharmony_ci */ 302362306a36Sopenharmony_ci if (!(ifp->flags & (IFA_F_OPTIMISTIC | IFA_F_NODAD))) 302462306a36Sopenharmony_ci ipv6_ifa_notify(0, ifp); 302562306a36Sopenharmony_ci /* 302662306a36Sopenharmony_ci * Note that section 3.1 of RFC 4429 indicates 302762306a36Sopenharmony_ci * that the Optimistic flag should not be set for 302862306a36Sopenharmony_ci * manually configured addresses 302962306a36Sopenharmony_ci */ 303062306a36Sopenharmony_ci addrconf_dad_start(ifp); 303162306a36Sopenharmony_ci if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR) 303262306a36Sopenharmony_ci manage_tempaddrs(idev, ifp, cfg->valid_lft, 303362306a36Sopenharmony_ci cfg->preferred_lft, true, jiffies); 303462306a36Sopenharmony_ci in6_ifa_put(ifp); 303562306a36Sopenharmony_ci addrconf_verify_rtnl(net); 303662306a36Sopenharmony_ci return 0; 303762306a36Sopenharmony_ci } else if (cfg->ifa_flags & IFA_F_MCAUTOJOIN) { 303862306a36Sopenharmony_ci ipv6_mc_config(net->ipv6.mc_autojoin_sk, false, 303962306a36Sopenharmony_ci cfg->pfx, ifindex); 304062306a36Sopenharmony_ci } 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci return PTR_ERR(ifp); 304362306a36Sopenharmony_ci} 304462306a36Sopenharmony_ci 304562306a36Sopenharmony_cistatic int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags, 304662306a36Sopenharmony_ci const struct in6_addr *pfx, unsigned int plen, 304762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 304862306a36Sopenharmony_ci{ 304962306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 305062306a36Sopenharmony_ci struct inet6_dev *idev; 305162306a36Sopenharmony_ci struct net_device *dev; 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci if (plen > 128) { 305462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid prefix length"); 305562306a36Sopenharmony_ci return -EINVAL; 305662306a36Sopenharmony_ci } 305762306a36Sopenharmony_ci 305862306a36Sopenharmony_ci dev = __dev_get_by_index(net, ifindex); 305962306a36Sopenharmony_ci if (!dev) { 306062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unable to find the interface"); 306162306a36Sopenharmony_ci return -ENODEV; 306262306a36Sopenharmony_ci } 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci idev = __in6_dev_get(dev); 306562306a36Sopenharmony_ci if (!idev) { 306662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "IPv6 is disabled on this device"); 306762306a36Sopenharmony_ci return -ENXIO; 306862306a36Sopenharmony_ci } 306962306a36Sopenharmony_ci 307062306a36Sopenharmony_ci read_lock_bh(&idev->lock); 307162306a36Sopenharmony_ci list_for_each_entry(ifp, &idev->addr_list, if_list) { 307262306a36Sopenharmony_ci if (ifp->prefix_len == plen && 307362306a36Sopenharmony_ci ipv6_addr_equal(pfx, &ifp->addr)) { 307462306a36Sopenharmony_ci in6_ifa_hold(ifp); 307562306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci if (!(ifp->flags & IFA_F_TEMPORARY) && 307862306a36Sopenharmony_ci (ifa_flags & IFA_F_MANAGETEMPADDR)) 307962306a36Sopenharmony_ci manage_tempaddrs(idev, ifp, 0, 0, false, 308062306a36Sopenharmony_ci jiffies); 308162306a36Sopenharmony_ci ipv6_del_addr(ifp); 308262306a36Sopenharmony_ci addrconf_verify_rtnl(net); 308362306a36Sopenharmony_ci if (ipv6_addr_is_multicast(pfx)) { 308462306a36Sopenharmony_ci ipv6_mc_config(net->ipv6.mc_autojoin_sk, 308562306a36Sopenharmony_ci false, pfx, dev->ifindex); 308662306a36Sopenharmony_ci } 308762306a36Sopenharmony_ci return 0; 308862306a36Sopenharmony_ci } 308962306a36Sopenharmony_ci } 309062306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "address not found"); 309362306a36Sopenharmony_ci return -EADDRNOTAVAIL; 309462306a36Sopenharmony_ci} 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_ciint addrconf_add_ifaddr(struct net *net, void __user *arg) 309862306a36Sopenharmony_ci{ 309962306a36Sopenharmony_ci struct ifa6_config cfg = { 310062306a36Sopenharmony_ci .ifa_flags = IFA_F_PERMANENT, 310162306a36Sopenharmony_ci .preferred_lft = INFINITY_LIFE_TIME, 310262306a36Sopenharmony_ci .valid_lft = INFINITY_LIFE_TIME, 310362306a36Sopenharmony_ci }; 310462306a36Sopenharmony_ci struct in6_ifreq ireq; 310562306a36Sopenharmony_ci int err; 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 310862306a36Sopenharmony_ci return -EPERM; 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) 311162306a36Sopenharmony_ci return -EFAULT; 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_ci cfg.pfx = &ireq.ifr6_addr; 311462306a36Sopenharmony_ci cfg.plen = ireq.ifr6_prefixlen; 311562306a36Sopenharmony_ci 311662306a36Sopenharmony_ci rtnl_lock(); 311762306a36Sopenharmony_ci err = inet6_addr_add(net, ireq.ifr6_ifindex, &cfg, NULL); 311862306a36Sopenharmony_ci rtnl_unlock(); 311962306a36Sopenharmony_ci return err; 312062306a36Sopenharmony_ci} 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_ciint addrconf_del_ifaddr(struct net *net, void __user *arg) 312362306a36Sopenharmony_ci{ 312462306a36Sopenharmony_ci struct in6_ifreq ireq; 312562306a36Sopenharmony_ci int err; 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_ci if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 312862306a36Sopenharmony_ci return -EPERM; 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) 313162306a36Sopenharmony_ci return -EFAULT; 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci rtnl_lock(); 313462306a36Sopenharmony_ci err = inet6_addr_del(net, ireq.ifr6_ifindex, 0, &ireq.ifr6_addr, 313562306a36Sopenharmony_ci ireq.ifr6_prefixlen, NULL); 313662306a36Sopenharmony_ci rtnl_unlock(); 313762306a36Sopenharmony_ci return err; 313862306a36Sopenharmony_ci} 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_cistatic void add_addr(struct inet6_dev *idev, const struct in6_addr *addr, 314162306a36Sopenharmony_ci int plen, int scope, u8 proto) 314262306a36Sopenharmony_ci{ 314362306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 314462306a36Sopenharmony_ci struct ifa6_config cfg = { 314562306a36Sopenharmony_ci .pfx = addr, 314662306a36Sopenharmony_ci .plen = plen, 314762306a36Sopenharmony_ci .ifa_flags = IFA_F_PERMANENT, 314862306a36Sopenharmony_ci .valid_lft = INFINITY_LIFE_TIME, 314962306a36Sopenharmony_ci .preferred_lft = INFINITY_LIFE_TIME, 315062306a36Sopenharmony_ci .scope = scope, 315162306a36Sopenharmony_ci .ifa_proto = proto 315262306a36Sopenharmony_ci }; 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci ifp = ipv6_add_addr(idev, &cfg, true, NULL); 315562306a36Sopenharmony_ci if (!IS_ERR(ifp)) { 315662306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 315762306a36Sopenharmony_ci ifp->flags &= ~IFA_F_TENTATIVE; 315862306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 315962306a36Sopenharmony_ci rt_genid_bump_ipv6(dev_net(idev->dev)); 316062306a36Sopenharmony_ci ipv6_ifa_notify(RTM_NEWADDR, ifp); 316162306a36Sopenharmony_ci in6_ifa_put(ifp); 316262306a36Sopenharmony_ci } 316362306a36Sopenharmony_ci} 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT) || IS_ENABLED(CONFIG_NET_IPGRE) || IS_ENABLED(CONFIG_IPV6_GRE) 316662306a36Sopenharmony_cistatic void add_v4_addrs(struct inet6_dev *idev) 316762306a36Sopenharmony_ci{ 316862306a36Sopenharmony_ci struct in6_addr addr; 316962306a36Sopenharmony_ci struct net_device *dev; 317062306a36Sopenharmony_ci struct net *net = dev_net(idev->dev); 317162306a36Sopenharmony_ci int scope, plen, offset = 0; 317262306a36Sopenharmony_ci u32 pflags = 0; 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci ASSERT_RTNL(); 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_ci memset(&addr, 0, sizeof(struct in6_addr)); 317762306a36Sopenharmony_ci /* in case of IP6GRE the dev_addr is an IPv6 and therefore we use only the last 4 bytes */ 317862306a36Sopenharmony_ci if (idev->dev->addr_len == sizeof(struct in6_addr)) 317962306a36Sopenharmony_ci offset = sizeof(struct in6_addr) - 4; 318062306a36Sopenharmony_ci memcpy(&addr.s6_addr32[3], idev->dev->dev_addr + offset, 4); 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci if (!(idev->dev->flags & IFF_POINTOPOINT) && idev->dev->type == ARPHRD_SIT) { 318362306a36Sopenharmony_ci scope = IPV6_ADDR_COMPATv4; 318462306a36Sopenharmony_ci plen = 96; 318562306a36Sopenharmony_ci pflags |= RTF_NONEXTHOP; 318662306a36Sopenharmony_ci } else { 318762306a36Sopenharmony_ci if (idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_NONE) 318862306a36Sopenharmony_ci return; 318962306a36Sopenharmony_ci 319062306a36Sopenharmony_ci addr.s6_addr32[0] = htonl(0xfe800000); 319162306a36Sopenharmony_ci scope = IFA_LINK; 319262306a36Sopenharmony_ci plen = 64; 319362306a36Sopenharmony_ci } 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_ci if (addr.s6_addr32[3]) { 319662306a36Sopenharmony_ci add_addr(idev, &addr, plen, scope, IFAPROT_UNSPEC); 319762306a36Sopenharmony_ci addrconf_prefix_route(&addr, plen, 0, idev->dev, 0, pflags, 319862306a36Sopenharmony_ci GFP_KERNEL); 319962306a36Sopenharmony_ci return; 320062306a36Sopenharmony_ci } 320162306a36Sopenharmony_ci 320262306a36Sopenharmony_ci for_each_netdev(net, dev) { 320362306a36Sopenharmony_ci struct in_device *in_dev = __in_dev_get_rtnl(dev); 320462306a36Sopenharmony_ci if (in_dev && (dev->flags & IFF_UP)) { 320562306a36Sopenharmony_ci struct in_ifaddr *ifa; 320662306a36Sopenharmony_ci int flag = scope; 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci in_dev_for_each_ifa_rtnl(ifa, in_dev) { 320962306a36Sopenharmony_ci addr.s6_addr32[3] = ifa->ifa_local; 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci if (ifa->ifa_scope == RT_SCOPE_LINK) 321262306a36Sopenharmony_ci continue; 321362306a36Sopenharmony_ci if (ifa->ifa_scope >= RT_SCOPE_HOST) { 321462306a36Sopenharmony_ci if (idev->dev->flags&IFF_POINTOPOINT) 321562306a36Sopenharmony_ci continue; 321662306a36Sopenharmony_ci flag |= IFA_HOST; 321762306a36Sopenharmony_ci } 321862306a36Sopenharmony_ci 321962306a36Sopenharmony_ci add_addr(idev, &addr, plen, flag, 322062306a36Sopenharmony_ci IFAPROT_UNSPEC); 322162306a36Sopenharmony_ci addrconf_prefix_route(&addr, plen, 0, idev->dev, 322262306a36Sopenharmony_ci 0, pflags, GFP_KERNEL); 322362306a36Sopenharmony_ci } 322462306a36Sopenharmony_ci } 322562306a36Sopenharmony_ci } 322662306a36Sopenharmony_ci} 322762306a36Sopenharmony_ci#endif 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_cistatic void init_loopback(struct net_device *dev) 323062306a36Sopenharmony_ci{ 323162306a36Sopenharmony_ci struct inet6_dev *idev; 323262306a36Sopenharmony_ci 323362306a36Sopenharmony_ci /* ::1 */ 323462306a36Sopenharmony_ci 323562306a36Sopenharmony_ci ASSERT_RTNL(); 323662306a36Sopenharmony_ci 323762306a36Sopenharmony_ci idev = ipv6_find_idev(dev); 323862306a36Sopenharmony_ci if (IS_ERR(idev)) { 323962306a36Sopenharmony_ci pr_debug("%s: add_dev failed\n", __func__); 324062306a36Sopenharmony_ci return; 324162306a36Sopenharmony_ci } 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_ci add_addr(idev, &in6addr_loopback, 128, IFA_HOST, IFAPROT_KERNEL_LO); 324462306a36Sopenharmony_ci} 324562306a36Sopenharmony_ci 324662306a36Sopenharmony_civoid addrconf_add_linklocal(struct inet6_dev *idev, 324762306a36Sopenharmony_ci const struct in6_addr *addr, u32 flags) 324862306a36Sopenharmony_ci{ 324962306a36Sopenharmony_ci struct ifa6_config cfg = { 325062306a36Sopenharmony_ci .pfx = addr, 325162306a36Sopenharmony_ci .plen = 64, 325262306a36Sopenharmony_ci .ifa_flags = flags | IFA_F_PERMANENT, 325362306a36Sopenharmony_ci .valid_lft = INFINITY_LIFE_TIME, 325462306a36Sopenharmony_ci .preferred_lft = INFINITY_LIFE_TIME, 325562306a36Sopenharmony_ci .scope = IFA_LINK, 325662306a36Sopenharmony_ci .ifa_proto = IFAPROT_KERNEL_LL 325762306a36Sopenharmony_ci }; 325862306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 326162306a36Sopenharmony_ci if ((dev_net(idev->dev)->ipv6.devconf_all->optimistic_dad || 326262306a36Sopenharmony_ci idev->cnf.optimistic_dad) && 326362306a36Sopenharmony_ci !dev_net(idev->dev)->ipv6.devconf_all->forwarding) 326462306a36Sopenharmony_ci cfg.ifa_flags |= IFA_F_OPTIMISTIC; 326562306a36Sopenharmony_ci#endif 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_ci ifp = ipv6_add_addr(idev, &cfg, true, NULL); 326862306a36Sopenharmony_ci if (!IS_ERR(ifp)) { 326962306a36Sopenharmony_ci addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 0, idev->dev, 327062306a36Sopenharmony_ci 0, 0, GFP_ATOMIC); 327162306a36Sopenharmony_ci addrconf_dad_start(ifp); 327262306a36Sopenharmony_ci in6_ifa_put(ifp); 327362306a36Sopenharmony_ci } 327462306a36Sopenharmony_ci} 327562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(addrconf_add_linklocal); 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_cistatic bool ipv6_reserved_interfaceid(struct in6_addr address) 327862306a36Sopenharmony_ci{ 327962306a36Sopenharmony_ci if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0) 328062306a36Sopenharmony_ci return true; 328162306a36Sopenharmony_ci 328262306a36Sopenharmony_ci if (address.s6_addr32[2] == htonl(0x02005eff) && 328362306a36Sopenharmony_ci ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000))) 328462306a36Sopenharmony_ci return true; 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_ci if (address.s6_addr32[2] == htonl(0xfdffffff) && 328762306a36Sopenharmony_ci ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80))) 328862306a36Sopenharmony_ci return true; 328962306a36Sopenharmony_ci 329062306a36Sopenharmony_ci return false; 329162306a36Sopenharmony_ci} 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_cistatic int ipv6_generate_stable_address(struct in6_addr *address, 329462306a36Sopenharmony_ci u8 dad_count, 329562306a36Sopenharmony_ci const struct inet6_dev *idev) 329662306a36Sopenharmony_ci{ 329762306a36Sopenharmony_ci static DEFINE_SPINLOCK(lock); 329862306a36Sopenharmony_ci static __u32 digest[SHA1_DIGEST_WORDS]; 329962306a36Sopenharmony_ci static __u32 workspace[SHA1_WORKSPACE_WORDS]; 330062306a36Sopenharmony_ci 330162306a36Sopenharmony_ci static union { 330262306a36Sopenharmony_ci char __data[SHA1_BLOCK_SIZE]; 330362306a36Sopenharmony_ci struct { 330462306a36Sopenharmony_ci struct in6_addr secret; 330562306a36Sopenharmony_ci __be32 prefix[2]; 330662306a36Sopenharmony_ci unsigned char hwaddr[MAX_ADDR_LEN]; 330762306a36Sopenharmony_ci u8 dad_count; 330862306a36Sopenharmony_ci } __packed; 330962306a36Sopenharmony_ci } data; 331062306a36Sopenharmony_ci 331162306a36Sopenharmony_ci struct in6_addr secret; 331262306a36Sopenharmony_ci struct in6_addr temp; 331362306a36Sopenharmony_ci struct net *net = dev_net(idev->dev); 331462306a36Sopenharmony_ci 331562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(data.__data) != sizeof(data)); 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_ci if (idev->cnf.stable_secret.initialized) 331862306a36Sopenharmony_ci secret = idev->cnf.stable_secret.secret; 331962306a36Sopenharmony_ci else if (net->ipv6.devconf_dflt->stable_secret.initialized) 332062306a36Sopenharmony_ci secret = net->ipv6.devconf_dflt->stable_secret.secret; 332162306a36Sopenharmony_ci else 332262306a36Sopenharmony_ci return -1; 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_ciretry: 332562306a36Sopenharmony_ci spin_lock_bh(&lock); 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_ci sha1_init(digest); 332862306a36Sopenharmony_ci memset(&data, 0, sizeof(data)); 332962306a36Sopenharmony_ci memset(workspace, 0, sizeof(workspace)); 333062306a36Sopenharmony_ci memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len); 333162306a36Sopenharmony_ci data.prefix[0] = address->s6_addr32[0]; 333262306a36Sopenharmony_ci data.prefix[1] = address->s6_addr32[1]; 333362306a36Sopenharmony_ci data.secret = secret; 333462306a36Sopenharmony_ci data.dad_count = dad_count; 333562306a36Sopenharmony_ci 333662306a36Sopenharmony_ci sha1_transform(digest, data.__data, workspace); 333762306a36Sopenharmony_ci 333862306a36Sopenharmony_ci temp = *address; 333962306a36Sopenharmony_ci temp.s6_addr32[2] = (__force __be32)digest[0]; 334062306a36Sopenharmony_ci temp.s6_addr32[3] = (__force __be32)digest[1]; 334162306a36Sopenharmony_ci 334262306a36Sopenharmony_ci spin_unlock_bh(&lock); 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci if (ipv6_reserved_interfaceid(temp)) { 334562306a36Sopenharmony_ci dad_count++; 334662306a36Sopenharmony_ci if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries) 334762306a36Sopenharmony_ci return -1; 334862306a36Sopenharmony_ci goto retry; 334962306a36Sopenharmony_ci } 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci *address = temp; 335262306a36Sopenharmony_ci return 0; 335362306a36Sopenharmony_ci} 335462306a36Sopenharmony_ci 335562306a36Sopenharmony_cistatic void ipv6_gen_mode_random_init(struct inet6_dev *idev) 335662306a36Sopenharmony_ci{ 335762306a36Sopenharmony_ci struct ipv6_stable_secret *s = &idev->cnf.stable_secret; 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ci if (s->initialized) 336062306a36Sopenharmony_ci return; 336162306a36Sopenharmony_ci s = &idev->cnf.stable_secret; 336262306a36Sopenharmony_ci get_random_bytes(&s->secret, sizeof(s->secret)); 336362306a36Sopenharmony_ci s->initialized = true; 336462306a36Sopenharmony_ci} 336562306a36Sopenharmony_ci 336662306a36Sopenharmony_cistatic void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route) 336762306a36Sopenharmony_ci{ 336862306a36Sopenharmony_ci struct in6_addr addr; 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci /* no link local addresses on L3 master devices */ 337162306a36Sopenharmony_ci if (netif_is_l3_master(idev->dev)) 337262306a36Sopenharmony_ci return; 337362306a36Sopenharmony_ci 337462306a36Sopenharmony_ci /* no link local addresses on devices flagged as slaves */ 337562306a36Sopenharmony_ci if (idev->dev->priv_flags & IFF_NO_ADDRCONF) 337662306a36Sopenharmony_ci return; 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0); 337962306a36Sopenharmony_ci 338062306a36Sopenharmony_ci switch (idev->cnf.addr_gen_mode) { 338162306a36Sopenharmony_ci case IN6_ADDR_GEN_MODE_RANDOM: 338262306a36Sopenharmony_ci ipv6_gen_mode_random_init(idev); 338362306a36Sopenharmony_ci fallthrough; 338462306a36Sopenharmony_ci case IN6_ADDR_GEN_MODE_STABLE_PRIVACY: 338562306a36Sopenharmony_ci if (!ipv6_generate_stable_address(&addr, 0, idev)) 338662306a36Sopenharmony_ci addrconf_add_linklocal(idev, &addr, 338762306a36Sopenharmony_ci IFA_F_STABLE_PRIVACY); 338862306a36Sopenharmony_ci else if (prefix_route) 338962306a36Sopenharmony_ci addrconf_prefix_route(&addr, 64, 0, idev->dev, 339062306a36Sopenharmony_ci 0, 0, GFP_KERNEL); 339162306a36Sopenharmony_ci break; 339262306a36Sopenharmony_ci case IN6_ADDR_GEN_MODE_EUI64: 339362306a36Sopenharmony_ci /* addrconf_add_linklocal also adds a prefix_route and we 339462306a36Sopenharmony_ci * only need to care about prefix routes if ipv6_generate_eui64 339562306a36Sopenharmony_ci * couldn't generate one. 339662306a36Sopenharmony_ci */ 339762306a36Sopenharmony_ci if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0) 339862306a36Sopenharmony_ci addrconf_add_linklocal(idev, &addr, 0); 339962306a36Sopenharmony_ci else if (prefix_route) 340062306a36Sopenharmony_ci addrconf_prefix_route(&addr, 64, 0, idev->dev, 340162306a36Sopenharmony_ci 0, 0, GFP_KERNEL); 340262306a36Sopenharmony_ci break; 340362306a36Sopenharmony_ci case IN6_ADDR_GEN_MODE_NONE: 340462306a36Sopenharmony_ci default: 340562306a36Sopenharmony_ci /* will not add any link local address */ 340662306a36Sopenharmony_ci break; 340762306a36Sopenharmony_ci } 340862306a36Sopenharmony_ci} 340962306a36Sopenharmony_ci 341062306a36Sopenharmony_cistatic void addrconf_dev_config(struct net_device *dev) 341162306a36Sopenharmony_ci{ 341262306a36Sopenharmony_ci struct inet6_dev *idev; 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci ASSERT_RTNL(); 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_ci if ((dev->type != ARPHRD_ETHER) && 341762306a36Sopenharmony_ci (dev->type != ARPHRD_FDDI) && 341862306a36Sopenharmony_ci (dev->type != ARPHRD_ARCNET) && 341962306a36Sopenharmony_ci (dev->type != ARPHRD_INFINIBAND) && 342062306a36Sopenharmony_ci (dev->type != ARPHRD_IEEE1394) && 342162306a36Sopenharmony_ci (dev->type != ARPHRD_TUNNEL6) && 342262306a36Sopenharmony_ci (dev->type != ARPHRD_6LOWPAN) && 342362306a36Sopenharmony_ci (dev->type != ARPHRD_TUNNEL) && 342462306a36Sopenharmony_ci (dev->type != ARPHRD_NONE) && 342562306a36Sopenharmony_ci (dev->type != ARPHRD_RAWIP)) { 342662306a36Sopenharmony_ci /* Alas, we support only Ethernet autoconfiguration. */ 342762306a36Sopenharmony_ci idev = __in6_dev_get(dev); 342862306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(idev) && dev->flags & IFF_UP && 342962306a36Sopenharmony_ci dev->flags & IFF_MULTICAST) 343062306a36Sopenharmony_ci ipv6_mc_up(idev); 343162306a36Sopenharmony_ci return; 343262306a36Sopenharmony_ci } 343362306a36Sopenharmony_ci 343462306a36Sopenharmony_ci idev = addrconf_add_dev(dev); 343562306a36Sopenharmony_ci if (IS_ERR(idev)) 343662306a36Sopenharmony_ci return; 343762306a36Sopenharmony_ci 343862306a36Sopenharmony_ci /* this device type has no EUI support */ 343962306a36Sopenharmony_ci if (dev->type == ARPHRD_NONE && 344062306a36Sopenharmony_ci idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) 344162306a36Sopenharmony_ci idev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM; 344262306a36Sopenharmony_ci 344362306a36Sopenharmony_ci addrconf_addr_gen(idev, false); 344462306a36Sopenharmony_ci} 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT) 344762306a36Sopenharmony_cistatic void addrconf_sit_config(struct net_device *dev) 344862306a36Sopenharmony_ci{ 344962306a36Sopenharmony_ci struct inet6_dev *idev; 345062306a36Sopenharmony_ci 345162306a36Sopenharmony_ci ASSERT_RTNL(); 345262306a36Sopenharmony_ci 345362306a36Sopenharmony_ci /* 345462306a36Sopenharmony_ci * Configure the tunnel with one of our IPv4 345562306a36Sopenharmony_ci * addresses... we should configure all of 345662306a36Sopenharmony_ci * our v4 addrs in the tunnel 345762306a36Sopenharmony_ci */ 345862306a36Sopenharmony_ci 345962306a36Sopenharmony_ci idev = ipv6_find_idev(dev); 346062306a36Sopenharmony_ci if (IS_ERR(idev)) { 346162306a36Sopenharmony_ci pr_debug("%s: add_dev failed\n", __func__); 346262306a36Sopenharmony_ci return; 346362306a36Sopenharmony_ci } 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ci if (dev->priv_flags & IFF_ISATAP) { 346662306a36Sopenharmony_ci addrconf_addr_gen(idev, false); 346762306a36Sopenharmony_ci return; 346862306a36Sopenharmony_ci } 346962306a36Sopenharmony_ci 347062306a36Sopenharmony_ci add_v4_addrs(idev); 347162306a36Sopenharmony_ci 347262306a36Sopenharmony_ci if (dev->flags&IFF_POINTOPOINT) 347362306a36Sopenharmony_ci addrconf_add_mroute(dev); 347462306a36Sopenharmony_ci} 347562306a36Sopenharmony_ci#endif 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IPGRE) || IS_ENABLED(CONFIG_IPV6_GRE) 347862306a36Sopenharmony_cistatic void addrconf_gre_config(struct net_device *dev) 347962306a36Sopenharmony_ci{ 348062306a36Sopenharmony_ci struct inet6_dev *idev; 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_ci ASSERT_RTNL(); 348362306a36Sopenharmony_ci 348462306a36Sopenharmony_ci idev = ipv6_find_idev(dev); 348562306a36Sopenharmony_ci if (IS_ERR(idev)) { 348662306a36Sopenharmony_ci pr_debug("%s: add_dev failed\n", __func__); 348762306a36Sopenharmony_ci return; 348862306a36Sopenharmony_ci } 348962306a36Sopenharmony_ci 349062306a36Sopenharmony_ci if (dev->type == ARPHRD_ETHER) { 349162306a36Sopenharmony_ci addrconf_addr_gen(idev, true); 349262306a36Sopenharmony_ci return; 349362306a36Sopenharmony_ci } 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_ci add_v4_addrs(idev); 349662306a36Sopenharmony_ci 349762306a36Sopenharmony_ci if (dev->flags & IFF_POINTOPOINT) 349862306a36Sopenharmony_ci addrconf_add_mroute(dev); 349962306a36Sopenharmony_ci} 350062306a36Sopenharmony_ci#endif 350162306a36Sopenharmony_ci 350262306a36Sopenharmony_cistatic void addrconf_init_auto_addrs(struct net_device *dev) 350362306a36Sopenharmony_ci{ 350462306a36Sopenharmony_ci switch (dev->type) { 350562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT) 350662306a36Sopenharmony_ci case ARPHRD_SIT: 350762306a36Sopenharmony_ci addrconf_sit_config(dev); 350862306a36Sopenharmony_ci break; 350962306a36Sopenharmony_ci#endif 351062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IPGRE) || IS_ENABLED(CONFIG_IPV6_GRE) 351162306a36Sopenharmony_ci case ARPHRD_IP6GRE: 351262306a36Sopenharmony_ci case ARPHRD_IPGRE: 351362306a36Sopenharmony_ci addrconf_gre_config(dev); 351462306a36Sopenharmony_ci break; 351562306a36Sopenharmony_ci#endif 351662306a36Sopenharmony_ci case ARPHRD_LOOPBACK: 351762306a36Sopenharmony_ci init_loopback(dev); 351862306a36Sopenharmony_ci break; 351962306a36Sopenharmony_ci 352062306a36Sopenharmony_ci default: 352162306a36Sopenharmony_ci addrconf_dev_config(dev); 352262306a36Sopenharmony_ci break; 352362306a36Sopenharmony_ci } 352462306a36Sopenharmony_ci} 352562306a36Sopenharmony_ci 352662306a36Sopenharmony_cistatic int fixup_permanent_addr(struct net *net, 352762306a36Sopenharmony_ci struct inet6_dev *idev, 352862306a36Sopenharmony_ci struct inet6_ifaddr *ifp) 352962306a36Sopenharmony_ci{ 353062306a36Sopenharmony_ci /* !fib6_node means the host route was removed from the 353162306a36Sopenharmony_ci * FIB, for example, if 'lo' device is taken down. In that 353262306a36Sopenharmony_ci * case regenerate the host route. 353362306a36Sopenharmony_ci */ 353462306a36Sopenharmony_ci if (!ifp->rt || !ifp->rt->fib6_node) { 353562306a36Sopenharmony_ci struct fib6_info *f6i, *prev; 353662306a36Sopenharmony_ci 353762306a36Sopenharmony_ci f6i = addrconf_f6i_alloc(net, idev, &ifp->addr, false, 353862306a36Sopenharmony_ci GFP_ATOMIC, NULL); 353962306a36Sopenharmony_ci if (IS_ERR(f6i)) 354062306a36Sopenharmony_ci return PTR_ERR(f6i); 354162306a36Sopenharmony_ci 354262306a36Sopenharmony_ci /* ifp->rt can be accessed outside of rtnl */ 354362306a36Sopenharmony_ci spin_lock(&ifp->lock); 354462306a36Sopenharmony_ci prev = ifp->rt; 354562306a36Sopenharmony_ci ifp->rt = f6i; 354662306a36Sopenharmony_ci spin_unlock(&ifp->lock); 354762306a36Sopenharmony_ci 354862306a36Sopenharmony_ci fib6_info_release(prev); 354962306a36Sopenharmony_ci } 355062306a36Sopenharmony_ci 355162306a36Sopenharmony_ci if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) { 355262306a36Sopenharmony_ci addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 355362306a36Sopenharmony_ci ifp->rt_priority, idev->dev, 0, 0, 355462306a36Sopenharmony_ci GFP_ATOMIC); 355562306a36Sopenharmony_ci } 355662306a36Sopenharmony_ci 355762306a36Sopenharmony_ci if (ifp->state == INET6_IFADDR_STATE_PREDAD) 355862306a36Sopenharmony_ci addrconf_dad_start(ifp); 355962306a36Sopenharmony_ci 356062306a36Sopenharmony_ci return 0; 356162306a36Sopenharmony_ci} 356262306a36Sopenharmony_ci 356362306a36Sopenharmony_cistatic void addrconf_permanent_addr(struct net *net, struct net_device *dev) 356462306a36Sopenharmony_ci{ 356562306a36Sopenharmony_ci struct inet6_ifaddr *ifp, *tmp; 356662306a36Sopenharmony_ci struct inet6_dev *idev; 356762306a36Sopenharmony_ci 356862306a36Sopenharmony_ci idev = __in6_dev_get(dev); 356962306a36Sopenharmony_ci if (!idev) 357062306a36Sopenharmony_ci return; 357162306a36Sopenharmony_ci 357262306a36Sopenharmony_ci write_lock_bh(&idev->lock); 357362306a36Sopenharmony_ci 357462306a36Sopenharmony_ci list_for_each_entry_safe(ifp, tmp, &idev->addr_list, if_list) { 357562306a36Sopenharmony_ci if ((ifp->flags & IFA_F_PERMANENT) && 357662306a36Sopenharmony_ci fixup_permanent_addr(net, idev, ifp) < 0) { 357762306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 357862306a36Sopenharmony_ci in6_ifa_hold(ifp); 357962306a36Sopenharmony_ci ipv6_del_addr(ifp); 358062306a36Sopenharmony_ci write_lock_bh(&idev->lock); 358162306a36Sopenharmony_ci 358262306a36Sopenharmony_ci net_info_ratelimited("%s: Failed to add prefix route for address %pI6c; dropping\n", 358362306a36Sopenharmony_ci idev->dev->name, &ifp->addr); 358462306a36Sopenharmony_ci } 358562306a36Sopenharmony_ci } 358662306a36Sopenharmony_ci 358762306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 358862306a36Sopenharmony_ci} 358962306a36Sopenharmony_ci 359062306a36Sopenharmony_cistatic int addrconf_notify(struct notifier_block *this, unsigned long event, 359162306a36Sopenharmony_ci void *ptr) 359262306a36Sopenharmony_ci{ 359362306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 359462306a36Sopenharmony_ci struct netdev_notifier_change_info *change_info; 359562306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info; 359662306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 359762306a36Sopenharmony_ci struct net *net = dev_net(dev); 359862306a36Sopenharmony_ci int run_pending = 0; 359962306a36Sopenharmony_ci int err; 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_ci switch (event) { 360262306a36Sopenharmony_ci case NETDEV_REGISTER: 360362306a36Sopenharmony_ci if (!idev && dev->mtu >= IPV6_MIN_MTU) { 360462306a36Sopenharmony_ci idev = ipv6_add_dev(dev); 360562306a36Sopenharmony_ci if (IS_ERR(idev)) 360662306a36Sopenharmony_ci return notifier_from_errno(PTR_ERR(idev)); 360762306a36Sopenharmony_ci } 360862306a36Sopenharmony_ci break; 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_ci case NETDEV_CHANGEMTU: 361162306a36Sopenharmony_ci /* if MTU under IPV6_MIN_MTU stop IPv6 on this interface. */ 361262306a36Sopenharmony_ci if (dev->mtu < IPV6_MIN_MTU) { 361362306a36Sopenharmony_ci addrconf_ifdown(dev, dev != net->loopback_dev); 361462306a36Sopenharmony_ci break; 361562306a36Sopenharmony_ci } 361662306a36Sopenharmony_ci 361762306a36Sopenharmony_ci if (idev) { 361862306a36Sopenharmony_ci rt6_mtu_change(dev, dev->mtu); 361962306a36Sopenharmony_ci idev->cnf.mtu6 = dev->mtu; 362062306a36Sopenharmony_ci break; 362162306a36Sopenharmony_ci } 362262306a36Sopenharmony_ci 362362306a36Sopenharmony_ci /* allocate new idev */ 362462306a36Sopenharmony_ci idev = ipv6_add_dev(dev); 362562306a36Sopenharmony_ci if (IS_ERR(idev)) 362662306a36Sopenharmony_ci break; 362762306a36Sopenharmony_ci 362862306a36Sopenharmony_ci /* device is still not ready */ 362962306a36Sopenharmony_ci if (!(idev->if_flags & IF_READY)) 363062306a36Sopenharmony_ci break; 363162306a36Sopenharmony_ci 363262306a36Sopenharmony_ci run_pending = 1; 363362306a36Sopenharmony_ci fallthrough; 363462306a36Sopenharmony_ci case NETDEV_UP: 363562306a36Sopenharmony_ci case NETDEV_CHANGE: 363662306a36Sopenharmony_ci if (idev && idev->cnf.disable_ipv6) 363762306a36Sopenharmony_ci break; 363862306a36Sopenharmony_ci 363962306a36Sopenharmony_ci if (dev->priv_flags & IFF_NO_ADDRCONF) { 364062306a36Sopenharmony_ci if (event == NETDEV_UP && !IS_ERR_OR_NULL(idev) && 364162306a36Sopenharmony_ci dev->flags & IFF_UP && dev->flags & IFF_MULTICAST) 364262306a36Sopenharmony_ci ipv6_mc_up(idev); 364362306a36Sopenharmony_ci break; 364462306a36Sopenharmony_ci } 364562306a36Sopenharmony_ci 364662306a36Sopenharmony_ci if (event == NETDEV_UP) { 364762306a36Sopenharmony_ci /* restore routes for permanent addresses */ 364862306a36Sopenharmony_ci addrconf_permanent_addr(net, dev); 364962306a36Sopenharmony_ci 365062306a36Sopenharmony_ci if (!addrconf_link_ready(dev)) { 365162306a36Sopenharmony_ci /* device is not ready yet. */ 365262306a36Sopenharmony_ci pr_debug("ADDRCONF(NETDEV_UP): %s: link is not ready\n", 365362306a36Sopenharmony_ci dev->name); 365462306a36Sopenharmony_ci break; 365562306a36Sopenharmony_ci } 365662306a36Sopenharmony_ci 365762306a36Sopenharmony_ci if (!idev && dev->mtu >= IPV6_MIN_MTU) 365862306a36Sopenharmony_ci idev = ipv6_add_dev(dev); 365962306a36Sopenharmony_ci 366062306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(idev)) { 366162306a36Sopenharmony_ci idev->if_flags |= IF_READY; 366262306a36Sopenharmony_ci run_pending = 1; 366362306a36Sopenharmony_ci } 366462306a36Sopenharmony_ci } else if (event == NETDEV_CHANGE) { 366562306a36Sopenharmony_ci if (!addrconf_link_ready(dev)) { 366662306a36Sopenharmony_ci /* device is still not ready. */ 366762306a36Sopenharmony_ci rt6_sync_down_dev(dev, event); 366862306a36Sopenharmony_ci break; 366962306a36Sopenharmony_ci } 367062306a36Sopenharmony_ci 367162306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(idev)) { 367262306a36Sopenharmony_ci if (idev->if_flags & IF_READY) { 367362306a36Sopenharmony_ci /* device is already configured - 367462306a36Sopenharmony_ci * but resend MLD reports, we might 367562306a36Sopenharmony_ci * have roamed and need to update 367662306a36Sopenharmony_ci * multicast snooping switches 367762306a36Sopenharmony_ci */ 367862306a36Sopenharmony_ci ipv6_mc_up(idev); 367962306a36Sopenharmony_ci change_info = ptr; 368062306a36Sopenharmony_ci if (change_info->flags_changed & IFF_NOARP) 368162306a36Sopenharmony_ci addrconf_dad_run(idev, true); 368262306a36Sopenharmony_ci rt6_sync_up(dev, RTNH_F_LINKDOWN); 368362306a36Sopenharmony_ci break; 368462306a36Sopenharmony_ci } 368562306a36Sopenharmony_ci idev->if_flags |= IF_READY; 368662306a36Sopenharmony_ci } 368762306a36Sopenharmony_ci 368862306a36Sopenharmony_ci pr_debug("ADDRCONF(NETDEV_CHANGE): %s: link becomes ready\n", 368962306a36Sopenharmony_ci dev->name); 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_ci run_pending = 1; 369262306a36Sopenharmony_ci } 369362306a36Sopenharmony_ci 369462306a36Sopenharmony_ci addrconf_init_auto_addrs(dev); 369562306a36Sopenharmony_ci 369662306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(idev)) { 369762306a36Sopenharmony_ci if (run_pending) 369862306a36Sopenharmony_ci addrconf_dad_run(idev, false); 369962306a36Sopenharmony_ci 370062306a36Sopenharmony_ci /* Device has an address by now */ 370162306a36Sopenharmony_ci rt6_sync_up(dev, RTNH_F_DEAD); 370262306a36Sopenharmony_ci 370362306a36Sopenharmony_ci /* 370462306a36Sopenharmony_ci * If the MTU changed during the interface down, 370562306a36Sopenharmony_ci * when the interface up, the changed MTU must be 370662306a36Sopenharmony_ci * reflected in the idev as well as routers. 370762306a36Sopenharmony_ci */ 370862306a36Sopenharmony_ci if (idev->cnf.mtu6 != dev->mtu && 370962306a36Sopenharmony_ci dev->mtu >= IPV6_MIN_MTU) { 371062306a36Sopenharmony_ci rt6_mtu_change(dev, dev->mtu); 371162306a36Sopenharmony_ci idev->cnf.mtu6 = dev->mtu; 371262306a36Sopenharmony_ci } 371362306a36Sopenharmony_ci idev->tstamp = jiffies; 371462306a36Sopenharmony_ci inet6_ifinfo_notify(RTM_NEWLINK, idev); 371562306a36Sopenharmony_ci 371662306a36Sopenharmony_ci /* 371762306a36Sopenharmony_ci * If the changed mtu during down is lower than 371862306a36Sopenharmony_ci * IPV6_MIN_MTU stop IPv6 on this interface. 371962306a36Sopenharmony_ci */ 372062306a36Sopenharmony_ci if (dev->mtu < IPV6_MIN_MTU) 372162306a36Sopenharmony_ci addrconf_ifdown(dev, dev != net->loopback_dev); 372262306a36Sopenharmony_ci } 372362306a36Sopenharmony_ci break; 372462306a36Sopenharmony_ci 372562306a36Sopenharmony_ci case NETDEV_DOWN: 372662306a36Sopenharmony_ci case NETDEV_UNREGISTER: 372762306a36Sopenharmony_ci /* 372862306a36Sopenharmony_ci * Remove all addresses from this interface. 372962306a36Sopenharmony_ci */ 373062306a36Sopenharmony_ci addrconf_ifdown(dev, event != NETDEV_DOWN); 373162306a36Sopenharmony_ci break; 373262306a36Sopenharmony_ci 373362306a36Sopenharmony_ci case NETDEV_CHANGENAME: 373462306a36Sopenharmony_ci if (idev) { 373562306a36Sopenharmony_ci snmp6_unregister_dev(idev); 373662306a36Sopenharmony_ci addrconf_sysctl_unregister(idev); 373762306a36Sopenharmony_ci err = addrconf_sysctl_register(idev); 373862306a36Sopenharmony_ci if (err) 373962306a36Sopenharmony_ci return notifier_from_errno(err); 374062306a36Sopenharmony_ci err = snmp6_register_dev(idev); 374162306a36Sopenharmony_ci if (err) { 374262306a36Sopenharmony_ci addrconf_sysctl_unregister(idev); 374362306a36Sopenharmony_ci return notifier_from_errno(err); 374462306a36Sopenharmony_ci } 374562306a36Sopenharmony_ci } 374662306a36Sopenharmony_ci break; 374762306a36Sopenharmony_ci 374862306a36Sopenharmony_ci case NETDEV_PRE_TYPE_CHANGE: 374962306a36Sopenharmony_ci case NETDEV_POST_TYPE_CHANGE: 375062306a36Sopenharmony_ci if (idev) 375162306a36Sopenharmony_ci addrconf_type_change(dev, event); 375262306a36Sopenharmony_ci break; 375362306a36Sopenharmony_ci 375462306a36Sopenharmony_ci case NETDEV_CHANGEUPPER: 375562306a36Sopenharmony_ci info = ptr; 375662306a36Sopenharmony_ci 375762306a36Sopenharmony_ci /* flush all routes if dev is linked to or unlinked from 375862306a36Sopenharmony_ci * an L3 master device (e.g., VRF) 375962306a36Sopenharmony_ci */ 376062306a36Sopenharmony_ci if (info->upper_dev && netif_is_l3_master(info->upper_dev)) 376162306a36Sopenharmony_ci addrconf_ifdown(dev, false); 376262306a36Sopenharmony_ci } 376362306a36Sopenharmony_ci 376462306a36Sopenharmony_ci return NOTIFY_OK; 376562306a36Sopenharmony_ci} 376662306a36Sopenharmony_ci 376762306a36Sopenharmony_ci/* 376862306a36Sopenharmony_ci * addrconf module should be notified of a device going up 376962306a36Sopenharmony_ci */ 377062306a36Sopenharmony_cistatic struct notifier_block ipv6_dev_notf = { 377162306a36Sopenharmony_ci .notifier_call = addrconf_notify, 377262306a36Sopenharmony_ci .priority = ADDRCONF_NOTIFY_PRIORITY, 377362306a36Sopenharmony_ci}; 377462306a36Sopenharmony_ci 377562306a36Sopenharmony_cistatic void addrconf_type_change(struct net_device *dev, unsigned long event) 377662306a36Sopenharmony_ci{ 377762306a36Sopenharmony_ci struct inet6_dev *idev; 377862306a36Sopenharmony_ci ASSERT_RTNL(); 377962306a36Sopenharmony_ci 378062306a36Sopenharmony_ci idev = __in6_dev_get(dev); 378162306a36Sopenharmony_ci 378262306a36Sopenharmony_ci if (event == NETDEV_POST_TYPE_CHANGE) 378362306a36Sopenharmony_ci ipv6_mc_remap(idev); 378462306a36Sopenharmony_ci else if (event == NETDEV_PRE_TYPE_CHANGE) 378562306a36Sopenharmony_ci ipv6_mc_unmap(idev); 378662306a36Sopenharmony_ci} 378762306a36Sopenharmony_ci 378862306a36Sopenharmony_cistatic bool addr_is_local(const struct in6_addr *addr) 378962306a36Sopenharmony_ci{ 379062306a36Sopenharmony_ci return ipv6_addr_type(addr) & 379162306a36Sopenharmony_ci (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); 379262306a36Sopenharmony_ci} 379362306a36Sopenharmony_ci 379462306a36Sopenharmony_cistatic int addrconf_ifdown(struct net_device *dev, bool unregister) 379562306a36Sopenharmony_ci{ 379662306a36Sopenharmony_ci unsigned long event = unregister ? NETDEV_UNREGISTER : NETDEV_DOWN; 379762306a36Sopenharmony_ci struct net *net = dev_net(dev); 379862306a36Sopenharmony_ci struct inet6_dev *idev; 379962306a36Sopenharmony_ci struct inet6_ifaddr *ifa; 380062306a36Sopenharmony_ci LIST_HEAD(tmp_addr_list); 380162306a36Sopenharmony_ci bool keep_addr = false; 380262306a36Sopenharmony_ci bool was_ready; 380362306a36Sopenharmony_ci int state, i; 380462306a36Sopenharmony_ci 380562306a36Sopenharmony_ci ASSERT_RTNL(); 380662306a36Sopenharmony_ci 380762306a36Sopenharmony_ci rt6_disable_ip(dev, event); 380862306a36Sopenharmony_ci 380962306a36Sopenharmony_ci idev = __in6_dev_get(dev); 381062306a36Sopenharmony_ci if (!idev) 381162306a36Sopenharmony_ci return -ENODEV; 381262306a36Sopenharmony_ci 381362306a36Sopenharmony_ci /* 381462306a36Sopenharmony_ci * Step 1: remove reference to ipv6 device from parent device. 381562306a36Sopenharmony_ci * Do not dev_put! 381662306a36Sopenharmony_ci */ 381762306a36Sopenharmony_ci if (unregister) { 381862306a36Sopenharmony_ci idev->dead = 1; 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_ci /* protected by rtnl_lock */ 382162306a36Sopenharmony_ci RCU_INIT_POINTER(dev->ip6_ptr, NULL); 382262306a36Sopenharmony_ci 382362306a36Sopenharmony_ci /* Step 1.5: remove snmp6 entry */ 382462306a36Sopenharmony_ci snmp6_unregister_dev(idev); 382562306a36Sopenharmony_ci 382662306a36Sopenharmony_ci } 382762306a36Sopenharmony_ci 382862306a36Sopenharmony_ci /* combine the user config with event to determine if permanent 382962306a36Sopenharmony_ci * addresses are to be removed from address hash table 383062306a36Sopenharmony_ci */ 383162306a36Sopenharmony_ci if (!unregister && !idev->cnf.disable_ipv6) { 383262306a36Sopenharmony_ci /* aggregate the system setting and interface setting */ 383362306a36Sopenharmony_ci int _keep_addr = net->ipv6.devconf_all->keep_addr_on_down; 383462306a36Sopenharmony_ci 383562306a36Sopenharmony_ci if (!_keep_addr) 383662306a36Sopenharmony_ci _keep_addr = idev->cnf.keep_addr_on_down; 383762306a36Sopenharmony_ci 383862306a36Sopenharmony_ci keep_addr = (_keep_addr > 0); 383962306a36Sopenharmony_ci } 384062306a36Sopenharmony_ci 384162306a36Sopenharmony_ci /* Step 2: clear hash table */ 384262306a36Sopenharmony_ci for (i = 0; i < IN6_ADDR_HSIZE; i++) { 384362306a36Sopenharmony_ci struct hlist_head *h = &net->ipv6.inet6_addr_lst[i]; 384462306a36Sopenharmony_ci 384562306a36Sopenharmony_ci spin_lock_bh(&net->ipv6.addrconf_hash_lock); 384662306a36Sopenharmony_cirestart: 384762306a36Sopenharmony_ci hlist_for_each_entry_rcu(ifa, h, addr_lst) { 384862306a36Sopenharmony_ci if (ifa->idev == idev) { 384962306a36Sopenharmony_ci addrconf_del_dad_work(ifa); 385062306a36Sopenharmony_ci /* combined flag + permanent flag decide if 385162306a36Sopenharmony_ci * address is retained on a down event 385262306a36Sopenharmony_ci */ 385362306a36Sopenharmony_ci if (!keep_addr || 385462306a36Sopenharmony_ci !(ifa->flags & IFA_F_PERMANENT) || 385562306a36Sopenharmony_ci addr_is_local(&ifa->addr)) { 385662306a36Sopenharmony_ci hlist_del_init_rcu(&ifa->addr_lst); 385762306a36Sopenharmony_ci goto restart; 385862306a36Sopenharmony_ci } 385962306a36Sopenharmony_ci } 386062306a36Sopenharmony_ci } 386162306a36Sopenharmony_ci spin_unlock_bh(&net->ipv6.addrconf_hash_lock); 386262306a36Sopenharmony_ci } 386362306a36Sopenharmony_ci 386462306a36Sopenharmony_ci write_lock_bh(&idev->lock); 386562306a36Sopenharmony_ci 386662306a36Sopenharmony_ci addrconf_del_rs_timer(idev); 386762306a36Sopenharmony_ci 386862306a36Sopenharmony_ci /* Step 2: clear flags for stateless addrconf, repeated down 386962306a36Sopenharmony_ci * detection 387062306a36Sopenharmony_ci */ 387162306a36Sopenharmony_ci was_ready = idev->if_flags & IF_READY; 387262306a36Sopenharmony_ci if (!unregister) 387362306a36Sopenharmony_ci idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY); 387462306a36Sopenharmony_ci 387562306a36Sopenharmony_ci /* Step 3: clear tempaddr list */ 387662306a36Sopenharmony_ci while (!list_empty(&idev->tempaddr_list)) { 387762306a36Sopenharmony_ci ifa = list_first_entry(&idev->tempaddr_list, 387862306a36Sopenharmony_ci struct inet6_ifaddr, tmp_list); 387962306a36Sopenharmony_ci list_del(&ifa->tmp_list); 388062306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 388162306a36Sopenharmony_ci spin_lock_bh(&ifa->lock); 388262306a36Sopenharmony_ci 388362306a36Sopenharmony_ci if (ifa->ifpub) { 388462306a36Sopenharmony_ci in6_ifa_put(ifa->ifpub); 388562306a36Sopenharmony_ci ifa->ifpub = NULL; 388662306a36Sopenharmony_ci } 388762306a36Sopenharmony_ci spin_unlock_bh(&ifa->lock); 388862306a36Sopenharmony_ci in6_ifa_put(ifa); 388962306a36Sopenharmony_ci write_lock_bh(&idev->lock); 389062306a36Sopenharmony_ci } 389162306a36Sopenharmony_ci 389262306a36Sopenharmony_ci list_for_each_entry(ifa, &idev->addr_list, if_list) 389362306a36Sopenharmony_ci list_add_tail(&ifa->if_list_aux, &tmp_addr_list); 389462306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 389562306a36Sopenharmony_ci 389662306a36Sopenharmony_ci while (!list_empty(&tmp_addr_list)) { 389762306a36Sopenharmony_ci struct fib6_info *rt = NULL; 389862306a36Sopenharmony_ci bool keep; 389962306a36Sopenharmony_ci 390062306a36Sopenharmony_ci ifa = list_first_entry(&tmp_addr_list, 390162306a36Sopenharmony_ci struct inet6_ifaddr, if_list_aux); 390262306a36Sopenharmony_ci list_del(&ifa->if_list_aux); 390362306a36Sopenharmony_ci 390462306a36Sopenharmony_ci addrconf_del_dad_work(ifa); 390562306a36Sopenharmony_ci 390662306a36Sopenharmony_ci keep = keep_addr && (ifa->flags & IFA_F_PERMANENT) && 390762306a36Sopenharmony_ci !addr_is_local(&ifa->addr); 390862306a36Sopenharmony_ci 390962306a36Sopenharmony_ci spin_lock_bh(&ifa->lock); 391062306a36Sopenharmony_ci 391162306a36Sopenharmony_ci if (keep) { 391262306a36Sopenharmony_ci /* set state to skip the notifier below */ 391362306a36Sopenharmony_ci state = INET6_IFADDR_STATE_DEAD; 391462306a36Sopenharmony_ci ifa->state = INET6_IFADDR_STATE_PREDAD; 391562306a36Sopenharmony_ci if (!(ifa->flags & IFA_F_NODAD)) 391662306a36Sopenharmony_ci ifa->flags |= IFA_F_TENTATIVE; 391762306a36Sopenharmony_ci 391862306a36Sopenharmony_ci rt = ifa->rt; 391962306a36Sopenharmony_ci ifa->rt = NULL; 392062306a36Sopenharmony_ci } else { 392162306a36Sopenharmony_ci state = ifa->state; 392262306a36Sopenharmony_ci ifa->state = INET6_IFADDR_STATE_DEAD; 392362306a36Sopenharmony_ci } 392462306a36Sopenharmony_ci 392562306a36Sopenharmony_ci spin_unlock_bh(&ifa->lock); 392662306a36Sopenharmony_ci 392762306a36Sopenharmony_ci if (rt) 392862306a36Sopenharmony_ci ip6_del_rt(net, rt, false); 392962306a36Sopenharmony_ci 393062306a36Sopenharmony_ci if (state != INET6_IFADDR_STATE_DEAD) { 393162306a36Sopenharmony_ci __ipv6_ifa_notify(RTM_DELADDR, ifa); 393262306a36Sopenharmony_ci inet6addr_notifier_call_chain(NETDEV_DOWN, ifa); 393362306a36Sopenharmony_ci } else { 393462306a36Sopenharmony_ci if (idev->cnf.forwarding) 393562306a36Sopenharmony_ci addrconf_leave_anycast(ifa); 393662306a36Sopenharmony_ci addrconf_leave_solict(ifa->idev, &ifa->addr); 393762306a36Sopenharmony_ci } 393862306a36Sopenharmony_ci 393962306a36Sopenharmony_ci if (!keep) { 394062306a36Sopenharmony_ci write_lock_bh(&idev->lock); 394162306a36Sopenharmony_ci list_del_rcu(&ifa->if_list); 394262306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 394362306a36Sopenharmony_ci in6_ifa_put(ifa); 394462306a36Sopenharmony_ci } 394562306a36Sopenharmony_ci } 394662306a36Sopenharmony_ci 394762306a36Sopenharmony_ci /* Step 5: Discard anycast and multicast list */ 394862306a36Sopenharmony_ci if (unregister) { 394962306a36Sopenharmony_ci ipv6_ac_destroy_dev(idev); 395062306a36Sopenharmony_ci ipv6_mc_destroy_dev(idev); 395162306a36Sopenharmony_ci } else if (was_ready) { 395262306a36Sopenharmony_ci ipv6_mc_down(idev); 395362306a36Sopenharmony_ci } 395462306a36Sopenharmony_ci 395562306a36Sopenharmony_ci idev->tstamp = jiffies; 395662306a36Sopenharmony_ci idev->ra_mtu = 0; 395762306a36Sopenharmony_ci 395862306a36Sopenharmony_ci /* Last: Shot the device (if unregistered) */ 395962306a36Sopenharmony_ci if (unregister) { 396062306a36Sopenharmony_ci addrconf_sysctl_unregister(idev); 396162306a36Sopenharmony_ci neigh_parms_release(&nd_tbl, idev->nd_parms); 396262306a36Sopenharmony_ci neigh_ifdown(&nd_tbl, dev); 396362306a36Sopenharmony_ci in6_dev_put(idev); 396462306a36Sopenharmony_ci } 396562306a36Sopenharmony_ci return 0; 396662306a36Sopenharmony_ci} 396762306a36Sopenharmony_ci 396862306a36Sopenharmony_cistatic void addrconf_rs_timer(struct timer_list *t) 396962306a36Sopenharmony_ci{ 397062306a36Sopenharmony_ci struct inet6_dev *idev = from_timer(idev, t, rs_timer); 397162306a36Sopenharmony_ci struct net_device *dev = idev->dev; 397262306a36Sopenharmony_ci struct in6_addr lladdr; 397362306a36Sopenharmony_ci 397462306a36Sopenharmony_ci write_lock(&idev->lock); 397562306a36Sopenharmony_ci if (idev->dead || !(idev->if_flags & IF_READY)) 397662306a36Sopenharmony_ci goto out; 397762306a36Sopenharmony_ci 397862306a36Sopenharmony_ci if (!ipv6_accept_ra(idev)) 397962306a36Sopenharmony_ci goto out; 398062306a36Sopenharmony_ci 398162306a36Sopenharmony_ci /* Announcement received after solicitation was sent */ 398262306a36Sopenharmony_ci if (idev->if_flags & IF_RA_RCVD) 398362306a36Sopenharmony_ci goto out; 398462306a36Sopenharmony_ci 398562306a36Sopenharmony_ci if (idev->rs_probes++ < idev->cnf.rtr_solicits || idev->cnf.rtr_solicits < 0) { 398662306a36Sopenharmony_ci write_unlock(&idev->lock); 398762306a36Sopenharmony_ci if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE)) 398862306a36Sopenharmony_ci ndisc_send_rs(dev, &lladdr, 398962306a36Sopenharmony_ci &in6addr_linklocal_allrouters); 399062306a36Sopenharmony_ci else 399162306a36Sopenharmony_ci goto put; 399262306a36Sopenharmony_ci 399362306a36Sopenharmony_ci write_lock(&idev->lock); 399462306a36Sopenharmony_ci idev->rs_interval = rfc3315_s14_backoff_update( 399562306a36Sopenharmony_ci idev->rs_interval, idev->cnf.rtr_solicit_max_interval); 399662306a36Sopenharmony_ci /* The wait after the last probe can be shorter */ 399762306a36Sopenharmony_ci addrconf_mod_rs_timer(idev, (idev->rs_probes == 399862306a36Sopenharmony_ci idev->cnf.rtr_solicits) ? 399962306a36Sopenharmony_ci idev->cnf.rtr_solicit_delay : 400062306a36Sopenharmony_ci idev->rs_interval); 400162306a36Sopenharmony_ci } else { 400262306a36Sopenharmony_ci /* 400362306a36Sopenharmony_ci * Note: we do not support deprecated "all on-link" 400462306a36Sopenharmony_ci * assumption any longer. 400562306a36Sopenharmony_ci */ 400662306a36Sopenharmony_ci pr_debug("%s: no IPv6 routers present\n", idev->dev->name); 400762306a36Sopenharmony_ci } 400862306a36Sopenharmony_ci 400962306a36Sopenharmony_ciout: 401062306a36Sopenharmony_ci write_unlock(&idev->lock); 401162306a36Sopenharmony_ciput: 401262306a36Sopenharmony_ci in6_dev_put(idev); 401362306a36Sopenharmony_ci} 401462306a36Sopenharmony_ci 401562306a36Sopenharmony_ci/* 401662306a36Sopenharmony_ci * Duplicate Address Detection 401762306a36Sopenharmony_ci */ 401862306a36Sopenharmony_cistatic void addrconf_dad_kick(struct inet6_ifaddr *ifp) 401962306a36Sopenharmony_ci{ 402062306a36Sopenharmony_ci unsigned long rand_num; 402162306a36Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 402262306a36Sopenharmony_ci u64 nonce; 402362306a36Sopenharmony_ci 402462306a36Sopenharmony_ci if (ifp->flags & IFA_F_OPTIMISTIC) 402562306a36Sopenharmony_ci rand_num = 0; 402662306a36Sopenharmony_ci else 402762306a36Sopenharmony_ci rand_num = get_random_u32_below(idev->cnf.rtr_solicit_delay ? : 1); 402862306a36Sopenharmony_ci 402962306a36Sopenharmony_ci nonce = 0; 403062306a36Sopenharmony_ci if (idev->cnf.enhanced_dad || 403162306a36Sopenharmony_ci dev_net(idev->dev)->ipv6.devconf_all->enhanced_dad) { 403262306a36Sopenharmony_ci do 403362306a36Sopenharmony_ci get_random_bytes(&nonce, 6); 403462306a36Sopenharmony_ci while (nonce == 0); 403562306a36Sopenharmony_ci } 403662306a36Sopenharmony_ci ifp->dad_nonce = nonce; 403762306a36Sopenharmony_ci ifp->dad_probes = idev->cnf.dad_transmits; 403862306a36Sopenharmony_ci addrconf_mod_dad_work(ifp, rand_num); 403962306a36Sopenharmony_ci} 404062306a36Sopenharmony_ci 404162306a36Sopenharmony_cistatic void addrconf_dad_begin(struct inet6_ifaddr *ifp) 404262306a36Sopenharmony_ci{ 404362306a36Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 404462306a36Sopenharmony_ci struct net_device *dev = idev->dev; 404562306a36Sopenharmony_ci bool bump_id, notify = false; 404662306a36Sopenharmony_ci struct net *net; 404762306a36Sopenharmony_ci 404862306a36Sopenharmony_ci addrconf_join_solict(dev, &ifp->addr); 404962306a36Sopenharmony_ci 405062306a36Sopenharmony_ci read_lock_bh(&idev->lock); 405162306a36Sopenharmony_ci spin_lock(&ifp->lock); 405262306a36Sopenharmony_ci if (ifp->state == INET6_IFADDR_STATE_DEAD) 405362306a36Sopenharmony_ci goto out; 405462306a36Sopenharmony_ci 405562306a36Sopenharmony_ci net = dev_net(dev); 405662306a36Sopenharmony_ci if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || 405762306a36Sopenharmony_ci (net->ipv6.devconf_all->accept_dad < 1 && 405862306a36Sopenharmony_ci idev->cnf.accept_dad < 1) || 405962306a36Sopenharmony_ci !(ifp->flags&IFA_F_TENTATIVE) || 406062306a36Sopenharmony_ci ifp->flags & IFA_F_NODAD) { 406162306a36Sopenharmony_ci bool send_na = false; 406262306a36Sopenharmony_ci 406362306a36Sopenharmony_ci if (ifp->flags & IFA_F_TENTATIVE && 406462306a36Sopenharmony_ci !(ifp->flags & IFA_F_OPTIMISTIC)) 406562306a36Sopenharmony_ci send_na = true; 406662306a36Sopenharmony_ci bump_id = ifp->flags & IFA_F_TENTATIVE; 406762306a36Sopenharmony_ci ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); 406862306a36Sopenharmony_ci spin_unlock(&ifp->lock); 406962306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 407062306a36Sopenharmony_ci 407162306a36Sopenharmony_ci addrconf_dad_completed(ifp, bump_id, send_na); 407262306a36Sopenharmony_ci return; 407362306a36Sopenharmony_ci } 407462306a36Sopenharmony_ci 407562306a36Sopenharmony_ci if (!(idev->if_flags & IF_READY)) { 407662306a36Sopenharmony_ci spin_unlock(&ifp->lock); 407762306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 407862306a36Sopenharmony_ci /* 407962306a36Sopenharmony_ci * If the device is not ready: 408062306a36Sopenharmony_ci * - keep it tentative if it is a permanent address. 408162306a36Sopenharmony_ci * - otherwise, kill it. 408262306a36Sopenharmony_ci */ 408362306a36Sopenharmony_ci in6_ifa_hold(ifp); 408462306a36Sopenharmony_ci addrconf_dad_stop(ifp, 0); 408562306a36Sopenharmony_ci return; 408662306a36Sopenharmony_ci } 408762306a36Sopenharmony_ci 408862306a36Sopenharmony_ci /* 408962306a36Sopenharmony_ci * Optimistic nodes can start receiving 409062306a36Sopenharmony_ci * Frames right away 409162306a36Sopenharmony_ci */ 409262306a36Sopenharmony_ci if (ifp->flags & IFA_F_OPTIMISTIC) { 409362306a36Sopenharmony_ci ip6_ins_rt(net, ifp->rt); 409462306a36Sopenharmony_ci if (ipv6_use_optimistic_addr(net, idev)) { 409562306a36Sopenharmony_ci /* Because optimistic nodes can use this address, 409662306a36Sopenharmony_ci * notify listeners. If DAD fails, RTM_DELADDR is sent. 409762306a36Sopenharmony_ci */ 409862306a36Sopenharmony_ci notify = true; 409962306a36Sopenharmony_ci } 410062306a36Sopenharmony_ci } 410162306a36Sopenharmony_ci 410262306a36Sopenharmony_ci addrconf_dad_kick(ifp); 410362306a36Sopenharmony_ciout: 410462306a36Sopenharmony_ci spin_unlock(&ifp->lock); 410562306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 410662306a36Sopenharmony_ci if (notify) 410762306a36Sopenharmony_ci ipv6_ifa_notify(RTM_NEWADDR, ifp); 410862306a36Sopenharmony_ci} 410962306a36Sopenharmony_ci 411062306a36Sopenharmony_cistatic void addrconf_dad_start(struct inet6_ifaddr *ifp) 411162306a36Sopenharmony_ci{ 411262306a36Sopenharmony_ci bool begin_dad = false; 411362306a36Sopenharmony_ci 411462306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 411562306a36Sopenharmony_ci if (ifp->state != INET6_IFADDR_STATE_DEAD) { 411662306a36Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_PREDAD; 411762306a36Sopenharmony_ci begin_dad = true; 411862306a36Sopenharmony_ci } 411962306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 412062306a36Sopenharmony_ci 412162306a36Sopenharmony_ci if (begin_dad) 412262306a36Sopenharmony_ci addrconf_mod_dad_work(ifp, 0); 412362306a36Sopenharmony_ci} 412462306a36Sopenharmony_ci 412562306a36Sopenharmony_cistatic void addrconf_dad_work(struct work_struct *w) 412662306a36Sopenharmony_ci{ 412762306a36Sopenharmony_ci struct inet6_ifaddr *ifp = container_of(to_delayed_work(w), 412862306a36Sopenharmony_ci struct inet6_ifaddr, 412962306a36Sopenharmony_ci dad_work); 413062306a36Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 413162306a36Sopenharmony_ci bool bump_id, disable_ipv6 = false; 413262306a36Sopenharmony_ci struct in6_addr mcaddr; 413362306a36Sopenharmony_ci 413462306a36Sopenharmony_ci enum { 413562306a36Sopenharmony_ci DAD_PROCESS, 413662306a36Sopenharmony_ci DAD_BEGIN, 413762306a36Sopenharmony_ci DAD_ABORT, 413862306a36Sopenharmony_ci } action = DAD_PROCESS; 413962306a36Sopenharmony_ci 414062306a36Sopenharmony_ci rtnl_lock(); 414162306a36Sopenharmony_ci 414262306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 414362306a36Sopenharmony_ci if (ifp->state == INET6_IFADDR_STATE_PREDAD) { 414462306a36Sopenharmony_ci action = DAD_BEGIN; 414562306a36Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_DAD; 414662306a36Sopenharmony_ci } else if (ifp->state == INET6_IFADDR_STATE_ERRDAD) { 414762306a36Sopenharmony_ci action = DAD_ABORT; 414862306a36Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_POSTDAD; 414962306a36Sopenharmony_ci 415062306a36Sopenharmony_ci if ((dev_net(idev->dev)->ipv6.devconf_all->accept_dad > 1 || 415162306a36Sopenharmony_ci idev->cnf.accept_dad > 1) && 415262306a36Sopenharmony_ci !idev->cnf.disable_ipv6 && 415362306a36Sopenharmony_ci !(ifp->flags & IFA_F_STABLE_PRIVACY)) { 415462306a36Sopenharmony_ci struct in6_addr addr; 415562306a36Sopenharmony_ci 415662306a36Sopenharmony_ci addr.s6_addr32[0] = htonl(0xfe800000); 415762306a36Sopenharmony_ci addr.s6_addr32[1] = 0; 415862306a36Sopenharmony_ci 415962306a36Sopenharmony_ci if (!ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) && 416062306a36Sopenharmony_ci ipv6_addr_equal(&ifp->addr, &addr)) { 416162306a36Sopenharmony_ci /* DAD failed for link-local based on MAC */ 416262306a36Sopenharmony_ci idev->cnf.disable_ipv6 = 1; 416362306a36Sopenharmony_ci 416462306a36Sopenharmony_ci pr_info("%s: IPv6 being disabled!\n", 416562306a36Sopenharmony_ci ifp->idev->dev->name); 416662306a36Sopenharmony_ci disable_ipv6 = true; 416762306a36Sopenharmony_ci } 416862306a36Sopenharmony_ci } 416962306a36Sopenharmony_ci } 417062306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 417162306a36Sopenharmony_ci 417262306a36Sopenharmony_ci if (action == DAD_BEGIN) { 417362306a36Sopenharmony_ci addrconf_dad_begin(ifp); 417462306a36Sopenharmony_ci goto out; 417562306a36Sopenharmony_ci } else if (action == DAD_ABORT) { 417662306a36Sopenharmony_ci in6_ifa_hold(ifp); 417762306a36Sopenharmony_ci addrconf_dad_stop(ifp, 1); 417862306a36Sopenharmony_ci if (disable_ipv6) 417962306a36Sopenharmony_ci addrconf_ifdown(idev->dev, false); 418062306a36Sopenharmony_ci goto out; 418162306a36Sopenharmony_ci } 418262306a36Sopenharmony_ci 418362306a36Sopenharmony_ci if (!ifp->dad_probes && addrconf_dad_end(ifp)) 418462306a36Sopenharmony_ci goto out; 418562306a36Sopenharmony_ci 418662306a36Sopenharmony_ci write_lock_bh(&idev->lock); 418762306a36Sopenharmony_ci if (idev->dead || !(idev->if_flags & IF_READY)) { 418862306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 418962306a36Sopenharmony_ci goto out; 419062306a36Sopenharmony_ci } 419162306a36Sopenharmony_ci 419262306a36Sopenharmony_ci spin_lock(&ifp->lock); 419362306a36Sopenharmony_ci if (ifp->state == INET6_IFADDR_STATE_DEAD) { 419462306a36Sopenharmony_ci spin_unlock(&ifp->lock); 419562306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 419662306a36Sopenharmony_ci goto out; 419762306a36Sopenharmony_ci } 419862306a36Sopenharmony_ci 419962306a36Sopenharmony_ci if (ifp->dad_probes == 0) { 420062306a36Sopenharmony_ci bool send_na = false; 420162306a36Sopenharmony_ci 420262306a36Sopenharmony_ci /* 420362306a36Sopenharmony_ci * DAD was successful 420462306a36Sopenharmony_ci */ 420562306a36Sopenharmony_ci 420662306a36Sopenharmony_ci if (ifp->flags & IFA_F_TENTATIVE && 420762306a36Sopenharmony_ci !(ifp->flags & IFA_F_OPTIMISTIC)) 420862306a36Sopenharmony_ci send_na = true; 420962306a36Sopenharmony_ci bump_id = ifp->flags & IFA_F_TENTATIVE; 421062306a36Sopenharmony_ci ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); 421162306a36Sopenharmony_ci spin_unlock(&ifp->lock); 421262306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 421362306a36Sopenharmony_ci 421462306a36Sopenharmony_ci addrconf_dad_completed(ifp, bump_id, send_na); 421562306a36Sopenharmony_ci 421662306a36Sopenharmony_ci goto out; 421762306a36Sopenharmony_ci } 421862306a36Sopenharmony_ci 421962306a36Sopenharmony_ci ifp->dad_probes--; 422062306a36Sopenharmony_ci addrconf_mod_dad_work(ifp, 422162306a36Sopenharmony_ci max(NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME), 422262306a36Sopenharmony_ci HZ/100)); 422362306a36Sopenharmony_ci spin_unlock(&ifp->lock); 422462306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 422562306a36Sopenharmony_ci 422662306a36Sopenharmony_ci /* send a neighbour solicitation for our addr */ 422762306a36Sopenharmony_ci addrconf_addr_solict_mult(&ifp->addr, &mcaddr); 422862306a36Sopenharmony_ci ndisc_send_ns(ifp->idev->dev, &ifp->addr, &mcaddr, &in6addr_any, 422962306a36Sopenharmony_ci ifp->dad_nonce); 423062306a36Sopenharmony_ciout: 423162306a36Sopenharmony_ci in6_ifa_put(ifp); 423262306a36Sopenharmony_ci rtnl_unlock(); 423362306a36Sopenharmony_ci} 423462306a36Sopenharmony_ci 423562306a36Sopenharmony_ci/* ifp->idev must be at least read locked */ 423662306a36Sopenharmony_cistatic bool ipv6_lonely_lladdr(struct inet6_ifaddr *ifp) 423762306a36Sopenharmony_ci{ 423862306a36Sopenharmony_ci struct inet6_ifaddr *ifpiter; 423962306a36Sopenharmony_ci struct inet6_dev *idev = ifp->idev; 424062306a36Sopenharmony_ci 424162306a36Sopenharmony_ci list_for_each_entry_reverse(ifpiter, &idev->addr_list, if_list) { 424262306a36Sopenharmony_ci if (ifpiter->scope > IFA_LINK) 424362306a36Sopenharmony_ci break; 424462306a36Sopenharmony_ci if (ifp != ifpiter && ifpiter->scope == IFA_LINK && 424562306a36Sopenharmony_ci (ifpiter->flags & (IFA_F_PERMANENT|IFA_F_TENTATIVE| 424662306a36Sopenharmony_ci IFA_F_OPTIMISTIC|IFA_F_DADFAILED)) == 424762306a36Sopenharmony_ci IFA_F_PERMANENT) 424862306a36Sopenharmony_ci return false; 424962306a36Sopenharmony_ci } 425062306a36Sopenharmony_ci return true; 425162306a36Sopenharmony_ci} 425262306a36Sopenharmony_ci 425362306a36Sopenharmony_cistatic void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id, 425462306a36Sopenharmony_ci bool send_na) 425562306a36Sopenharmony_ci{ 425662306a36Sopenharmony_ci struct net_device *dev = ifp->idev->dev; 425762306a36Sopenharmony_ci struct in6_addr lladdr; 425862306a36Sopenharmony_ci bool send_rs, send_mld; 425962306a36Sopenharmony_ci 426062306a36Sopenharmony_ci addrconf_del_dad_work(ifp); 426162306a36Sopenharmony_ci 426262306a36Sopenharmony_ci /* 426362306a36Sopenharmony_ci * Configure the address for reception. Now it is valid. 426462306a36Sopenharmony_ci */ 426562306a36Sopenharmony_ci 426662306a36Sopenharmony_ci ipv6_ifa_notify(RTM_NEWADDR, ifp); 426762306a36Sopenharmony_ci 426862306a36Sopenharmony_ci /* If added prefix is link local and we are prepared to process 426962306a36Sopenharmony_ci router advertisements, start sending router solicitations. 427062306a36Sopenharmony_ci */ 427162306a36Sopenharmony_ci 427262306a36Sopenharmony_ci read_lock_bh(&ifp->idev->lock); 427362306a36Sopenharmony_ci send_mld = ifp->scope == IFA_LINK && ipv6_lonely_lladdr(ifp); 427462306a36Sopenharmony_ci send_rs = send_mld && 427562306a36Sopenharmony_ci ipv6_accept_ra(ifp->idev) && 427662306a36Sopenharmony_ci ifp->idev->cnf.rtr_solicits != 0 && 427762306a36Sopenharmony_ci (dev->flags & IFF_LOOPBACK) == 0 && 427862306a36Sopenharmony_ci (dev->type != ARPHRD_TUNNEL) && 427962306a36Sopenharmony_ci !netif_is_team_port(dev); 428062306a36Sopenharmony_ci read_unlock_bh(&ifp->idev->lock); 428162306a36Sopenharmony_ci 428262306a36Sopenharmony_ci /* While dad is in progress mld report's source address is in6_addrany. 428362306a36Sopenharmony_ci * Resend with proper ll now. 428462306a36Sopenharmony_ci */ 428562306a36Sopenharmony_ci if (send_mld) 428662306a36Sopenharmony_ci ipv6_mc_dad_complete(ifp->idev); 428762306a36Sopenharmony_ci 428862306a36Sopenharmony_ci /* send unsolicited NA if enabled */ 428962306a36Sopenharmony_ci if (send_na && 429062306a36Sopenharmony_ci (ifp->idev->cnf.ndisc_notify || 429162306a36Sopenharmony_ci dev_net(dev)->ipv6.devconf_all->ndisc_notify)) { 429262306a36Sopenharmony_ci ndisc_send_na(dev, &in6addr_linklocal_allnodes, &ifp->addr, 429362306a36Sopenharmony_ci /*router=*/ !!ifp->idev->cnf.forwarding, 429462306a36Sopenharmony_ci /*solicited=*/ false, /*override=*/ true, 429562306a36Sopenharmony_ci /*inc_opt=*/ true); 429662306a36Sopenharmony_ci } 429762306a36Sopenharmony_ci 429862306a36Sopenharmony_ci if (send_rs) { 429962306a36Sopenharmony_ci /* 430062306a36Sopenharmony_ci * If a host as already performed a random delay 430162306a36Sopenharmony_ci * [...] as part of DAD [...] there is no need 430262306a36Sopenharmony_ci * to delay again before sending the first RS 430362306a36Sopenharmony_ci */ 430462306a36Sopenharmony_ci if (ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE)) 430562306a36Sopenharmony_ci return; 430662306a36Sopenharmony_ci ndisc_send_rs(dev, &lladdr, &in6addr_linklocal_allrouters); 430762306a36Sopenharmony_ci 430862306a36Sopenharmony_ci write_lock_bh(&ifp->idev->lock); 430962306a36Sopenharmony_ci spin_lock(&ifp->lock); 431062306a36Sopenharmony_ci ifp->idev->rs_interval = rfc3315_s14_backoff_init( 431162306a36Sopenharmony_ci ifp->idev->cnf.rtr_solicit_interval); 431262306a36Sopenharmony_ci ifp->idev->rs_probes = 1; 431362306a36Sopenharmony_ci ifp->idev->if_flags |= IF_RS_SENT; 431462306a36Sopenharmony_ci addrconf_mod_rs_timer(ifp->idev, ifp->idev->rs_interval); 431562306a36Sopenharmony_ci spin_unlock(&ifp->lock); 431662306a36Sopenharmony_ci write_unlock_bh(&ifp->idev->lock); 431762306a36Sopenharmony_ci } 431862306a36Sopenharmony_ci 431962306a36Sopenharmony_ci if (bump_id) 432062306a36Sopenharmony_ci rt_genid_bump_ipv6(dev_net(dev)); 432162306a36Sopenharmony_ci 432262306a36Sopenharmony_ci /* Make sure that a new temporary address will be created 432362306a36Sopenharmony_ci * before this temporary address becomes deprecated. 432462306a36Sopenharmony_ci */ 432562306a36Sopenharmony_ci if (ifp->flags & IFA_F_TEMPORARY) 432662306a36Sopenharmony_ci addrconf_verify_rtnl(dev_net(dev)); 432762306a36Sopenharmony_ci} 432862306a36Sopenharmony_ci 432962306a36Sopenharmony_cistatic void addrconf_dad_run(struct inet6_dev *idev, bool restart) 433062306a36Sopenharmony_ci{ 433162306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 433262306a36Sopenharmony_ci 433362306a36Sopenharmony_ci read_lock_bh(&idev->lock); 433462306a36Sopenharmony_ci list_for_each_entry(ifp, &idev->addr_list, if_list) { 433562306a36Sopenharmony_ci spin_lock(&ifp->lock); 433662306a36Sopenharmony_ci if ((ifp->flags & IFA_F_TENTATIVE && 433762306a36Sopenharmony_ci ifp->state == INET6_IFADDR_STATE_DAD) || restart) { 433862306a36Sopenharmony_ci if (restart) 433962306a36Sopenharmony_ci ifp->state = INET6_IFADDR_STATE_PREDAD; 434062306a36Sopenharmony_ci addrconf_dad_kick(ifp); 434162306a36Sopenharmony_ci } 434262306a36Sopenharmony_ci spin_unlock(&ifp->lock); 434362306a36Sopenharmony_ci } 434462306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 434562306a36Sopenharmony_ci} 434662306a36Sopenharmony_ci 434762306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 434862306a36Sopenharmony_cistruct if6_iter_state { 434962306a36Sopenharmony_ci struct seq_net_private p; 435062306a36Sopenharmony_ci int bucket; 435162306a36Sopenharmony_ci int offset; 435262306a36Sopenharmony_ci}; 435362306a36Sopenharmony_ci 435462306a36Sopenharmony_cistatic struct inet6_ifaddr *if6_get_first(struct seq_file *seq, loff_t pos) 435562306a36Sopenharmony_ci{ 435662306a36Sopenharmony_ci struct if6_iter_state *state = seq->private; 435762306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 435862306a36Sopenharmony_ci struct inet6_ifaddr *ifa = NULL; 435962306a36Sopenharmony_ci int p = 0; 436062306a36Sopenharmony_ci 436162306a36Sopenharmony_ci /* initial bucket if pos is 0 */ 436262306a36Sopenharmony_ci if (pos == 0) { 436362306a36Sopenharmony_ci state->bucket = 0; 436462306a36Sopenharmony_ci state->offset = 0; 436562306a36Sopenharmony_ci } 436662306a36Sopenharmony_ci 436762306a36Sopenharmony_ci for (; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) { 436862306a36Sopenharmony_ci hlist_for_each_entry_rcu(ifa, &net->ipv6.inet6_addr_lst[state->bucket], 436962306a36Sopenharmony_ci addr_lst) { 437062306a36Sopenharmony_ci /* sync with offset */ 437162306a36Sopenharmony_ci if (p < state->offset) { 437262306a36Sopenharmony_ci p++; 437362306a36Sopenharmony_ci continue; 437462306a36Sopenharmony_ci } 437562306a36Sopenharmony_ci return ifa; 437662306a36Sopenharmony_ci } 437762306a36Sopenharmony_ci 437862306a36Sopenharmony_ci /* prepare for next bucket */ 437962306a36Sopenharmony_ci state->offset = 0; 438062306a36Sopenharmony_ci p = 0; 438162306a36Sopenharmony_ci } 438262306a36Sopenharmony_ci return NULL; 438362306a36Sopenharmony_ci} 438462306a36Sopenharmony_ci 438562306a36Sopenharmony_cistatic struct inet6_ifaddr *if6_get_next(struct seq_file *seq, 438662306a36Sopenharmony_ci struct inet6_ifaddr *ifa) 438762306a36Sopenharmony_ci{ 438862306a36Sopenharmony_ci struct if6_iter_state *state = seq->private; 438962306a36Sopenharmony_ci struct net *net = seq_file_net(seq); 439062306a36Sopenharmony_ci 439162306a36Sopenharmony_ci hlist_for_each_entry_continue_rcu(ifa, addr_lst) { 439262306a36Sopenharmony_ci state->offset++; 439362306a36Sopenharmony_ci return ifa; 439462306a36Sopenharmony_ci } 439562306a36Sopenharmony_ci 439662306a36Sopenharmony_ci state->offset = 0; 439762306a36Sopenharmony_ci while (++state->bucket < IN6_ADDR_HSIZE) { 439862306a36Sopenharmony_ci hlist_for_each_entry_rcu(ifa, 439962306a36Sopenharmony_ci &net->ipv6.inet6_addr_lst[state->bucket], addr_lst) { 440062306a36Sopenharmony_ci return ifa; 440162306a36Sopenharmony_ci } 440262306a36Sopenharmony_ci } 440362306a36Sopenharmony_ci 440462306a36Sopenharmony_ci return NULL; 440562306a36Sopenharmony_ci} 440662306a36Sopenharmony_ci 440762306a36Sopenharmony_cistatic void *if6_seq_start(struct seq_file *seq, loff_t *pos) 440862306a36Sopenharmony_ci __acquires(rcu) 440962306a36Sopenharmony_ci{ 441062306a36Sopenharmony_ci rcu_read_lock(); 441162306a36Sopenharmony_ci return if6_get_first(seq, *pos); 441262306a36Sopenharmony_ci} 441362306a36Sopenharmony_ci 441462306a36Sopenharmony_cistatic void *if6_seq_next(struct seq_file *seq, void *v, loff_t *pos) 441562306a36Sopenharmony_ci{ 441662306a36Sopenharmony_ci struct inet6_ifaddr *ifa; 441762306a36Sopenharmony_ci 441862306a36Sopenharmony_ci ifa = if6_get_next(seq, v); 441962306a36Sopenharmony_ci ++*pos; 442062306a36Sopenharmony_ci return ifa; 442162306a36Sopenharmony_ci} 442262306a36Sopenharmony_ci 442362306a36Sopenharmony_cistatic void if6_seq_stop(struct seq_file *seq, void *v) 442462306a36Sopenharmony_ci __releases(rcu) 442562306a36Sopenharmony_ci{ 442662306a36Sopenharmony_ci rcu_read_unlock(); 442762306a36Sopenharmony_ci} 442862306a36Sopenharmony_ci 442962306a36Sopenharmony_cistatic int if6_seq_show(struct seq_file *seq, void *v) 443062306a36Sopenharmony_ci{ 443162306a36Sopenharmony_ci struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v; 443262306a36Sopenharmony_ci seq_printf(seq, "%pi6 %02x %02x %02x %02x %8s\n", 443362306a36Sopenharmony_ci &ifp->addr, 443462306a36Sopenharmony_ci ifp->idev->dev->ifindex, 443562306a36Sopenharmony_ci ifp->prefix_len, 443662306a36Sopenharmony_ci ifp->scope, 443762306a36Sopenharmony_ci (u8) ifp->flags, 443862306a36Sopenharmony_ci ifp->idev->dev->name); 443962306a36Sopenharmony_ci return 0; 444062306a36Sopenharmony_ci} 444162306a36Sopenharmony_ci 444262306a36Sopenharmony_cistatic const struct seq_operations if6_seq_ops = { 444362306a36Sopenharmony_ci .start = if6_seq_start, 444462306a36Sopenharmony_ci .next = if6_seq_next, 444562306a36Sopenharmony_ci .show = if6_seq_show, 444662306a36Sopenharmony_ci .stop = if6_seq_stop, 444762306a36Sopenharmony_ci}; 444862306a36Sopenharmony_ci 444962306a36Sopenharmony_cistatic int __net_init if6_proc_net_init(struct net *net) 445062306a36Sopenharmony_ci{ 445162306a36Sopenharmony_ci if (!proc_create_net("if_inet6", 0444, net->proc_net, &if6_seq_ops, 445262306a36Sopenharmony_ci sizeof(struct if6_iter_state))) 445362306a36Sopenharmony_ci return -ENOMEM; 445462306a36Sopenharmony_ci return 0; 445562306a36Sopenharmony_ci} 445662306a36Sopenharmony_ci 445762306a36Sopenharmony_cistatic void __net_exit if6_proc_net_exit(struct net *net) 445862306a36Sopenharmony_ci{ 445962306a36Sopenharmony_ci remove_proc_entry("if_inet6", net->proc_net); 446062306a36Sopenharmony_ci} 446162306a36Sopenharmony_ci 446262306a36Sopenharmony_cistatic struct pernet_operations if6_proc_net_ops = { 446362306a36Sopenharmony_ci .init = if6_proc_net_init, 446462306a36Sopenharmony_ci .exit = if6_proc_net_exit, 446562306a36Sopenharmony_ci}; 446662306a36Sopenharmony_ci 446762306a36Sopenharmony_ciint __init if6_proc_init(void) 446862306a36Sopenharmony_ci{ 446962306a36Sopenharmony_ci return register_pernet_subsys(&if6_proc_net_ops); 447062306a36Sopenharmony_ci} 447162306a36Sopenharmony_ci 447262306a36Sopenharmony_civoid if6_proc_exit(void) 447362306a36Sopenharmony_ci{ 447462306a36Sopenharmony_ci unregister_pernet_subsys(&if6_proc_net_ops); 447562306a36Sopenharmony_ci} 447662306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 447762306a36Sopenharmony_ci 447862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 447962306a36Sopenharmony_ci/* Check if address is a home address configured on any interface. */ 448062306a36Sopenharmony_ciint ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr) 448162306a36Sopenharmony_ci{ 448262306a36Sopenharmony_ci unsigned int hash = inet6_addr_hash(net, addr); 448362306a36Sopenharmony_ci struct inet6_ifaddr *ifp = NULL; 448462306a36Sopenharmony_ci int ret = 0; 448562306a36Sopenharmony_ci 448662306a36Sopenharmony_ci rcu_read_lock(); 448762306a36Sopenharmony_ci hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) { 448862306a36Sopenharmony_ci if (ipv6_addr_equal(&ifp->addr, addr) && 448962306a36Sopenharmony_ci (ifp->flags & IFA_F_HOMEADDRESS)) { 449062306a36Sopenharmony_ci ret = 1; 449162306a36Sopenharmony_ci break; 449262306a36Sopenharmony_ci } 449362306a36Sopenharmony_ci } 449462306a36Sopenharmony_ci rcu_read_unlock(); 449562306a36Sopenharmony_ci return ret; 449662306a36Sopenharmony_ci} 449762306a36Sopenharmony_ci#endif 449862306a36Sopenharmony_ci 449962306a36Sopenharmony_ci/* RFC6554 has some algorithm to avoid loops in segment routing by 450062306a36Sopenharmony_ci * checking if the segments contains any of a local interface address. 450162306a36Sopenharmony_ci * 450262306a36Sopenharmony_ci * Quote: 450362306a36Sopenharmony_ci * 450462306a36Sopenharmony_ci * To detect loops in the SRH, a router MUST determine if the SRH 450562306a36Sopenharmony_ci * includes multiple addresses assigned to any interface on that router. 450662306a36Sopenharmony_ci * If such addresses appear more than once and are separated by at least 450762306a36Sopenharmony_ci * one address not assigned to that router. 450862306a36Sopenharmony_ci */ 450962306a36Sopenharmony_ciint ipv6_chk_rpl_srh_loop(struct net *net, const struct in6_addr *segs, 451062306a36Sopenharmony_ci unsigned char nsegs) 451162306a36Sopenharmony_ci{ 451262306a36Sopenharmony_ci const struct in6_addr *addr; 451362306a36Sopenharmony_ci int i, ret = 0, found = 0; 451462306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 451562306a36Sopenharmony_ci bool separated = false; 451662306a36Sopenharmony_ci unsigned int hash; 451762306a36Sopenharmony_ci bool hash_found; 451862306a36Sopenharmony_ci 451962306a36Sopenharmony_ci rcu_read_lock(); 452062306a36Sopenharmony_ci for (i = 0; i < nsegs; i++) { 452162306a36Sopenharmony_ci addr = &segs[i]; 452262306a36Sopenharmony_ci hash = inet6_addr_hash(net, addr); 452362306a36Sopenharmony_ci 452462306a36Sopenharmony_ci hash_found = false; 452562306a36Sopenharmony_ci hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) { 452662306a36Sopenharmony_ci 452762306a36Sopenharmony_ci if (ipv6_addr_equal(&ifp->addr, addr)) { 452862306a36Sopenharmony_ci hash_found = true; 452962306a36Sopenharmony_ci break; 453062306a36Sopenharmony_ci } 453162306a36Sopenharmony_ci } 453262306a36Sopenharmony_ci 453362306a36Sopenharmony_ci if (hash_found) { 453462306a36Sopenharmony_ci if (found > 1 && separated) { 453562306a36Sopenharmony_ci ret = 1; 453662306a36Sopenharmony_ci break; 453762306a36Sopenharmony_ci } 453862306a36Sopenharmony_ci 453962306a36Sopenharmony_ci separated = false; 454062306a36Sopenharmony_ci found++; 454162306a36Sopenharmony_ci } else { 454262306a36Sopenharmony_ci separated = true; 454362306a36Sopenharmony_ci } 454462306a36Sopenharmony_ci } 454562306a36Sopenharmony_ci rcu_read_unlock(); 454662306a36Sopenharmony_ci 454762306a36Sopenharmony_ci return ret; 454862306a36Sopenharmony_ci} 454962306a36Sopenharmony_ci 455062306a36Sopenharmony_ci/* 455162306a36Sopenharmony_ci * Periodic address status verification 455262306a36Sopenharmony_ci */ 455362306a36Sopenharmony_ci 455462306a36Sopenharmony_cistatic void addrconf_verify_rtnl(struct net *net) 455562306a36Sopenharmony_ci{ 455662306a36Sopenharmony_ci unsigned long now, next, next_sec, next_sched; 455762306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 455862306a36Sopenharmony_ci int i; 455962306a36Sopenharmony_ci 456062306a36Sopenharmony_ci ASSERT_RTNL(); 456162306a36Sopenharmony_ci 456262306a36Sopenharmony_ci rcu_read_lock_bh(); 456362306a36Sopenharmony_ci now = jiffies; 456462306a36Sopenharmony_ci next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 456562306a36Sopenharmony_ci 456662306a36Sopenharmony_ci cancel_delayed_work(&net->ipv6.addr_chk_work); 456762306a36Sopenharmony_ci 456862306a36Sopenharmony_ci for (i = 0; i < IN6_ADDR_HSIZE; i++) { 456962306a36Sopenharmony_cirestart: 457062306a36Sopenharmony_ci hlist_for_each_entry_rcu_bh(ifp, &net->ipv6.inet6_addr_lst[i], addr_lst) { 457162306a36Sopenharmony_ci unsigned long age; 457262306a36Sopenharmony_ci 457362306a36Sopenharmony_ci /* When setting preferred_lft to a value not zero or 457462306a36Sopenharmony_ci * infinity, while valid_lft is infinity 457562306a36Sopenharmony_ci * IFA_F_PERMANENT has a non-infinity life time. 457662306a36Sopenharmony_ci */ 457762306a36Sopenharmony_ci if ((ifp->flags & IFA_F_PERMANENT) && 457862306a36Sopenharmony_ci (ifp->prefered_lft == INFINITY_LIFE_TIME)) 457962306a36Sopenharmony_ci continue; 458062306a36Sopenharmony_ci 458162306a36Sopenharmony_ci spin_lock(&ifp->lock); 458262306a36Sopenharmony_ci /* We try to batch several events at once. */ 458362306a36Sopenharmony_ci age = (now - ifp->tstamp + ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 458462306a36Sopenharmony_ci 458562306a36Sopenharmony_ci if ((ifp->flags&IFA_F_TEMPORARY) && 458662306a36Sopenharmony_ci !(ifp->flags&IFA_F_TENTATIVE) && 458762306a36Sopenharmony_ci ifp->prefered_lft != INFINITY_LIFE_TIME && 458862306a36Sopenharmony_ci !ifp->regen_count && ifp->ifpub) { 458962306a36Sopenharmony_ci /* This is a non-regenerated temporary addr. */ 459062306a36Sopenharmony_ci 459162306a36Sopenharmony_ci unsigned long regen_advance = ifp->idev->cnf.regen_max_retry * 459262306a36Sopenharmony_ci ifp->idev->cnf.dad_transmits * 459362306a36Sopenharmony_ci max(NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME), HZ/100) / HZ; 459462306a36Sopenharmony_ci 459562306a36Sopenharmony_ci if (age + regen_advance >= ifp->prefered_lft) { 459662306a36Sopenharmony_ci struct inet6_ifaddr *ifpub = ifp->ifpub; 459762306a36Sopenharmony_ci if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next)) 459862306a36Sopenharmony_ci next = ifp->tstamp + ifp->prefered_lft * HZ; 459962306a36Sopenharmony_ci 460062306a36Sopenharmony_ci ifp->regen_count++; 460162306a36Sopenharmony_ci in6_ifa_hold(ifp); 460262306a36Sopenharmony_ci in6_ifa_hold(ifpub); 460362306a36Sopenharmony_ci spin_unlock(&ifp->lock); 460462306a36Sopenharmony_ci 460562306a36Sopenharmony_ci spin_lock(&ifpub->lock); 460662306a36Sopenharmony_ci ifpub->regen_count = 0; 460762306a36Sopenharmony_ci spin_unlock(&ifpub->lock); 460862306a36Sopenharmony_ci rcu_read_unlock_bh(); 460962306a36Sopenharmony_ci ipv6_create_tempaddr(ifpub, true); 461062306a36Sopenharmony_ci in6_ifa_put(ifpub); 461162306a36Sopenharmony_ci in6_ifa_put(ifp); 461262306a36Sopenharmony_ci rcu_read_lock_bh(); 461362306a36Sopenharmony_ci goto restart; 461462306a36Sopenharmony_ci } else if (time_before(ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ, next)) 461562306a36Sopenharmony_ci next = ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ; 461662306a36Sopenharmony_ci } 461762306a36Sopenharmony_ci 461862306a36Sopenharmony_ci if (ifp->valid_lft != INFINITY_LIFE_TIME && 461962306a36Sopenharmony_ci age >= ifp->valid_lft) { 462062306a36Sopenharmony_ci spin_unlock(&ifp->lock); 462162306a36Sopenharmony_ci in6_ifa_hold(ifp); 462262306a36Sopenharmony_ci rcu_read_unlock_bh(); 462362306a36Sopenharmony_ci ipv6_del_addr(ifp); 462462306a36Sopenharmony_ci rcu_read_lock_bh(); 462562306a36Sopenharmony_ci goto restart; 462662306a36Sopenharmony_ci } else if (ifp->prefered_lft == INFINITY_LIFE_TIME) { 462762306a36Sopenharmony_ci spin_unlock(&ifp->lock); 462862306a36Sopenharmony_ci continue; 462962306a36Sopenharmony_ci } else if (age >= ifp->prefered_lft) { 463062306a36Sopenharmony_ci /* jiffies - ifp->tstamp > age >= ifp->prefered_lft */ 463162306a36Sopenharmony_ci int deprecate = 0; 463262306a36Sopenharmony_ci 463362306a36Sopenharmony_ci if (!(ifp->flags&IFA_F_DEPRECATED)) { 463462306a36Sopenharmony_ci deprecate = 1; 463562306a36Sopenharmony_ci ifp->flags |= IFA_F_DEPRECATED; 463662306a36Sopenharmony_ci } 463762306a36Sopenharmony_ci 463862306a36Sopenharmony_ci if ((ifp->valid_lft != INFINITY_LIFE_TIME) && 463962306a36Sopenharmony_ci (time_before(ifp->tstamp + ifp->valid_lft * HZ, next))) 464062306a36Sopenharmony_ci next = ifp->tstamp + ifp->valid_lft * HZ; 464162306a36Sopenharmony_ci 464262306a36Sopenharmony_ci spin_unlock(&ifp->lock); 464362306a36Sopenharmony_ci 464462306a36Sopenharmony_ci if (deprecate) { 464562306a36Sopenharmony_ci in6_ifa_hold(ifp); 464662306a36Sopenharmony_ci 464762306a36Sopenharmony_ci ipv6_ifa_notify(0, ifp); 464862306a36Sopenharmony_ci in6_ifa_put(ifp); 464962306a36Sopenharmony_ci goto restart; 465062306a36Sopenharmony_ci } 465162306a36Sopenharmony_ci } else { 465262306a36Sopenharmony_ci /* ifp->prefered_lft <= ifp->valid_lft */ 465362306a36Sopenharmony_ci if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next)) 465462306a36Sopenharmony_ci next = ifp->tstamp + ifp->prefered_lft * HZ; 465562306a36Sopenharmony_ci spin_unlock(&ifp->lock); 465662306a36Sopenharmony_ci } 465762306a36Sopenharmony_ci } 465862306a36Sopenharmony_ci } 465962306a36Sopenharmony_ci 466062306a36Sopenharmony_ci next_sec = round_jiffies_up(next); 466162306a36Sopenharmony_ci next_sched = next; 466262306a36Sopenharmony_ci 466362306a36Sopenharmony_ci /* If rounded timeout is accurate enough, accept it. */ 466462306a36Sopenharmony_ci if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 466562306a36Sopenharmony_ci next_sched = next_sec; 466662306a36Sopenharmony_ci 466762306a36Sopenharmony_ci /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 466862306a36Sopenharmony_ci if (time_before(next_sched, jiffies + ADDRCONF_TIMER_FUZZ_MAX)) 466962306a36Sopenharmony_ci next_sched = jiffies + ADDRCONF_TIMER_FUZZ_MAX; 467062306a36Sopenharmony_ci 467162306a36Sopenharmony_ci pr_debug("now = %lu, schedule = %lu, rounded schedule = %lu => %lu\n", 467262306a36Sopenharmony_ci now, next, next_sec, next_sched); 467362306a36Sopenharmony_ci mod_delayed_work(addrconf_wq, &net->ipv6.addr_chk_work, next_sched - now); 467462306a36Sopenharmony_ci rcu_read_unlock_bh(); 467562306a36Sopenharmony_ci} 467662306a36Sopenharmony_ci 467762306a36Sopenharmony_cistatic void addrconf_verify_work(struct work_struct *w) 467862306a36Sopenharmony_ci{ 467962306a36Sopenharmony_ci struct net *net = container_of(to_delayed_work(w), struct net, 468062306a36Sopenharmony_ci ipv6.addr_chk_work); 468162306a36Sopenharmony_ci 468262306a36Sopenharmony_ci rtnl_lock(); 468362306a36Sopenharmony_ci addrconf_verify_rtnl(net); 468462306a36Sopenharmony_ci rtnl_unlock(); 468562306a36Sopenharmony_ci} 468662306a36Sopenharmony_ci 468762306a36Sopenharmony_cistatic void addrconf_verify(struct net *net) 468862306a36Sopenharmony_ci{ 468962306a36Sopenharmony_ci mod_delayed_work(addrconf_wq, &net->ipv6.addr_chk_work, 0); 469062306a36Sopenharmony_ci} 469162306a36Sopenharmony_ci 469262306a36Sopenharmony_cistatic struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local, 469362306a36Sopenharmony_ci struct in6_addr **peer_pfx) 469462306a36Sopenharmony_ci{ 469562306a36Sopenharmony_ci struct in6_addr *pfx = NULL; 469662306a36Sopenharmony_ci 469762306a36Sopenharmony_ci *peer_pfx = NULL; 469862306a36Sopenharmony_ci 469962306a36Sopenharmony_ci if (addr) 470062306a36Sopenharmony_ci pfx = nla_data(addr); 470162306a36Sopenharmony_ci 470262306a36Sopenharmony_ci if (local) { 470362306a36Sopenharmony_ci if (pfx && nla_memcmp(local, pfx, sizeof(*pfx))) 470462306a36Sopenharmony_ci *peer_pfx = pfx; 470562306a36Sopenharmony_ci pfx = nla_data(local); 470662306a36Sopenharmony_ci } 470762306a36Sopenharmony_ci 470862306a36Sopenharmony_ci return pfx; 470962306a36Sopenharmony_ci} 471062306a36Sopenharmony_ci 471162306a36Sopenharmony_cistatic const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = { 471262306a36Sopenharmony_ci [IFA_ADDRESS] = { .len = sizeof(struct in6_addr) }, 471362306a36Sopenharmony_ci [IFA_LOCAL] = { .len = sizeof(struct in6_addr) }, 471462306a36Sopenharmony_ci [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, 471562306a36Sopenharmony_ci [IFA_FLAGS] = { .len = sizeof(u32) }, 471662306a36Sopenharmony_ci [IFA_RT_PRIORITY] = { .len = sizeof(u32) }, 471762306a36Sopenharmony_ci [IFA_TARGET_NETNSID] = { .type = NLA_S32 }, 471862306a36Sopenharmony_ci [IFA_PROTO] = { .type = NLA_U8 }, 471962306a36Sopenharmony_ci}; 472062306a36Sopenharmony_ci 472162306a36Sopenharmony_cistatic int 472262306a36Sopenharmony_ciinet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, 472362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 472462306a36Sopenharmony_ci{ 472562306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 472662306a36Sopenharmony_ci struct ifaddrmsg *ifm; 472762306a36Sopenharmony_ci struct nlattr *tb[IFA_MAX+1]; 472862306a36Sopenharmony_ci struct in6_addr *pfx, *peer_pfx; 472962306a36Sopenharmony_ci u32 ifa_flags; 473062306a36Sopenharmony_ci int err; 473162306a36Sopenharmony_ci 473262306a36Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 473362306a36Sopenharmony_ci ifa_ipv6_policy, extack); 473462306a36Sopenharmony_ci if (err < 0) 473562306a36Sopenharmony_ci return err; 473662306a36Sopenharmony_ci 473762306a36Sopenharmony_ci ifm = nlmsg_data(nlh); 473862306a36Sopenharmony_ci pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx); 473962306a36Sopenharmony_ci if (!pfx) 474062306a36Sopenharmony_ci return -EINVAL; 474162306a36Sopenharmony_ci 474262306a36Sopenharmony_ci ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags; 474362306a36Sopenharmony_ci 474462306a36Sopenharmony_ci /* We ignore other flags so far. */ 474562306a36Sopenharmony_ci ifa_flags &= IFA_F_MANAGETEMPADDR; 474662306a36Sopenharmony_ci 474762306a36Sopenharmony_ci return inet6_addr_del(net, ifm->ifa_index, ifa_flags, pfx, 474862306a36Sopenharmony_ci ifm->ifa_prefixlen, extack); 474962306a36Sopenharmony_ci} 475062306a36Sopenharmony_ci 475162306a36Sopenharmony_cistatic int modify_prefix_route(struct inet6_ifaddr *ifp, 475262306a36Sopenharmony_ci unsigned long expires, u32 flags, 475362306a36Sopenharmony_ci bool modify_peer) 475462306a36Sopenharmony_ci{ 475562306a36Sopenharmony_ci struct fib6_info *f6i; 475662306a36Sopenharmony_ci u32 prio; 475762306a36Sopenharmony_ci 475862306a36Sopenharmony_ci f6i = addrconf_get_prefix_route(modify_peer ? &ifp->peer_addr : &ifp->addr, 475962306a36Sopenharmony_ci ifp->prefix_len, 476062306a36Sopenharmony_ci ifp->idev->dev, 0, RTF_DEFAULT, true); 476162306a36Sopenharmony_ci if (!f6i) 476262306a36Sopenharmony_ci return -ENOENT; 476362306a36Sopenharmony_ci 476462306a36Sopenharmony_ci prio = ifp->rt_priority ? : IP6_RT_PRIO_ADDRCONF; 476562306a36Sopenharmony_ci if (f6i->fib6_metric != prio) { 476662306a36Sopenharmony_ci /* delete old one */ 476762306a36Sopenharmony_ci ip6_del_rt(dev_net(ifp->idev->dev), f6i, false); 476862306a36Sopenharmony_ci 476962306a36Sopenharmony_ci /* add new one */ 477062306a36Sopenharmony_ci addrconf_prefix_route(modify_peer ? &ifp->peer_addr : &ifp->addr, 477162306a36Sopenharmony_ci ifp->prefix_len, 477262306a36Sopenharmony_ci ifp->rt_priority, ifp->idev->dev, 477362306a36Sopenharmony_ci expires, flags, GFP_KERNEL); 477462306a36Sopenharmony_ci } else { 477562306a36Sopenharmony_ci if (!expires) 477662306a36Sopenharmony_ci fib6_clean_expires(f6i); 477762306a36Sopenharmony_ci else 477862306a36Sopenharmony_ci fib6_set_expires(f6i, expires); 477962306a36Sopenharmony_ci 478062306a36Sopenharmony_ci fib6_info_release(f6i); 478162306a36Sopenharmony_ci } 478262306a36Sopenharmony_ci 478362306a36Sopenharmony_ci return 0; 478462306a36Sopenharmony_ci} 478562306a36Sopenharmony_ci 478662306a36Sopenharmony_cistatic int inet6_addr_modify(struct net *net, struct inet6_ifaddr *ifp, 478762306a36Sopenharmony_ci struct ifa6_config *cfg) 478862306a36Sopenharmony_ci{ 478962306a36Sopenharmony_ci u32 flags; 479062306a36Sopenharmony_ci clock_t expires; 479162306a36Sopenharmony_ci unsigned long timeout; 479262306a36Sopenharmony_ci bool was_managetempaddr; 479362306a36Sopenharmony_ci bool had_prefixroute; 479462306a36Sopenharmony_ci bool new_peer = false; 479562306a36Sopenharmony_ci 479662306a36Sopenharmony_ci ASSERT_RTNL(); 479762306a36Sopenharmony_ci 479862306a36Sopenharmony_ci if (!cfg->valid_lft || cfg->preferred_lft > cfg->valid_lft) 479962306a36Sopenharmony_ci return -EINVAL; 480062306a36Sopenharmony_ci 480162306a36Sopenharmony_ci if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR && 480262306a36Sopenharmony_ci (ifp->flags & IFA_F_TEMPORARY || ifp->prefix_len != 64)) 480362306a36Sopenharmony_ci return -EINVAL; 480462306a36Sopenharmony_ci 480562306a36Sopenharmony_ci if (!(ifp->flags & IFA_F_TENTATIVE) || ifp->flags & IFA_F_DADFAILED) 480662306a36Sopenharmony_ci cfg->ifa_flags &= ~IFA_F_OPTIMISTIC; 480762306a36Sopenharmony_ci 480862306a36Sopenharmony_ci timeout = addrconf_timeout_fixup(cfg->valid_lft, HZ); 480962306a36Sopenharmony_ci if (addrconf_finite_timeout(timeout)) { 481062306a36Sopenharmony_ci expires = jiffies_to_clock_t(timeout * HZ); 481162306a36Sopenharmony_ci cfg->valid_lft = timeout; 481262306a36Sopenharmony_ci flags = RTF_EXPIRES; 481362306a36Sopenharmony_ci } else { 481462306a36Sopenharmony_ci expires = 0; 481562306a36Sopenharmony_ci flags = 0; 481662306a36Sopenharmony_ci cfg->ifa_flags |= IFA_F_PERMANENT; 481762306a36Sopenharmony_ci } 481862306a36Sopenharmony_ci 481962306a36Sopenharmony_ci timeout = addrconf_timeout_fixup(cfg->preferred_lft, HZ); 482062306a36Sopenharmony_ci if (addrconf_finite_timeout(timeout)) { 482162306a36Sopenharmony_ci if (timeout == 0) 482262306a36Sopenharmony_ci cfg->ifa_flags |= IFA_F_DEPRECATED; 482362306a36Sopenharmony_ci cfg->preferred_lft = timeout; 482462306a36Sopenharmony_ci } 482562306a36Sopenharmony_ci 482662306a36Sopenharmony_ci if (cfg->peer_pfx && 482762306a36Sopenharmony_ci memcmp(&ifp->peer_addr, cfg->peer_pfx, sizeof(struct in6_addr))) { 482862306a36Sopenharmony_ci if (!ipv6_addr_any(&ifp->peer_addr)) 482962306a36Sopenharmony_ci cleanup_prefix_route(ifp, expires, true, true); 483062306a36Sopenharmony_ci new_peer = true; 483162306a36Sopenharmony_ci } 483262306a36Sopenharmony_ci 483362306a36Sopenharmony_ci spin_lock_bh(&ifp->lock); 483462306a36Sopenharmony_ci was_managetempaddr = ifp->flags & IFA_F_MANAGETEMPADDR; 483562306a36Sopenharmony_ci had_prefixroute = ifp->flags & IFA_F_PERMANENT && 483662306a36Sopenharmony_ci !(ifp->flags & IFA_F_NOPREFIXROUTE); 483762306a36Sopenharmony_ci ifp->flags &= ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD | 483862306a36Sopenharmony_ci IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR | 483962306a36Sopenharmony_ci IFA_F_NOPREFIXROUTE); 484062306a36Sopenharmony_ci ifp->flags |= cfg->ifa_flags; 484162306a36Sopenharmony_ci ifp->tstamp = jiffies; 484262306a36Sopenharmony_ci ifp->valid_lft = cfg->valid_lft; 484362306a36Sopenharmony_ci ifp->prefered_lft = cfg->preferred_lft; 484462306a36Sopenharmony_ci ifp->ifa_proto = cfg->ifa_proto; 484562306a36Sopenharmony_ci 484662306a36Sopenharmony_ci if (cfg->rt_priority && cfg->rt_priority != ifp->rt_priority) 484762306a36Sopenharmony_ci ifp->rt_priority = cfg->rt_priority; 484862306a36Sopenharmony_ci 484962306a36Sopenharmony_ci if (new_peer) 485062306a36Sopenharmony_ci ifp->peer_addr = *cfg->peer_pfx; 485162306a36Sopenharmony_ci 485262306a36Sopenharmony_ci spin_unlock_bh(&ifp->lock); 485362306a36Sopenharmony_ci if (!(ifp->flags&IFA_F_TENTATIVE)) 485462306a36Sopenharmony_ci ipv6_ifa_notify(0, ifp); 485562306a36Sopenharmony_ci 485662306a36Sopenharmony_ci if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) { 485762306a36Sopenharmony_ci int rc = -ENOENT; 485862306a36Sopenharmony_ci 485962306a36Sopenharmony_ci if (had_prefixroute) 486062306a36Sopenharmony_ci rc = modify_prefix_route(ifp, expires, flags, false); 486162306a36Sopenharmony_ci 486262306a36Sopenharmony_ci /* prefix route could have been deleted; if so restore it */ 486362306a36Sopenharmony_ci if (rc == -ENOENT) { 486462306a36Sopenharmony_ci addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 486562306a36Sopenharmony_ci ifp->rt_priority, ifp->idev->dev, 486662306a36Sopenharmony_ci expires, flags, GFP_KERNEL); 486762306a36Sopenharmony_ci } 486862306a36Sopenharmony_ci 486962306a36Sopenharmony_ci if (had_prefixroute && !ipv6_addr_any(&ifp->peer_addr)) 487062306a36Sopenharmony_ci rc = modify_prefix_route(ifp, expires, flags, true); 487162306a36Sopenharmony_ci 487262306a36Sopenharmony_ci if (rc == -ENOENT && !ipv6_addr_any(&ifp->peer_addr)) { 487362306a36Sopenharmony_ci addrconf_prefix_route(&ifp->peer_addr, ifp->prefix_len, 487462306a36Sopenharmony_ci ifp->rt_priority, ifp->idev->dev, 487562306a36Sopenharmony_ci expires, flags, GFP_KERNEL); 487662306a36Sopenharmony_ci } 487762306a36Sopenharmony_ci } else if (had_prefixroute) { 487862306a36Sopenharmony_ci enum cleanup_prefix_rt_t action; 487962306a36Sopenharmony_ci unsigned long rt_expires; 488062306a36Sopenharmony_ci 488162306a36Sopenharmony_ci write_lock_bh(&ifp->idev->lock); 488262306a36Sopenharmony_ci action = check_cleanup_prefix_route(ifp, &rt_expires); 488362306a36Sopenharmony_ci write_unlock_bh(&ifp->idev->lock); 488462306a36Sopenharmony_ci 488562306a36Sopenharmony_ci if (action != CLEANUP_PREFIX_RT_NOP) { 488662306a36Sopenharmony_ci cleanup_prefix_route(ifp, rt_expires, 488762306a36Sopenharmony_ci action == CLEANUP_PREFIX_RT_DEL, false); 488862306a36Sopenharmony_ci } 488962306a36Sopenharmony_ci } 489062306a36Sopenharmony_ci 489162306a36Sopenharmony_ci if (was_managetempaddr || ifp->flags & IFA_F_MANAGETEMPADDR) { 489262306a36Sopenharmony_ci if (was_managetempaddr && 489362306a36Sopenharmony_ci !(ifp->flags & IFA_F_MANAGETEMPADDR)) { 489462306a36Sopenharmony_ci cfg->valid_lft = 0; 489562306a36Sopenharmony_ci cfg->preferred_lft = 0; 489662306a36Sopenharmony_ci } 489762306a36Sopenharmony_ci manage_tempaddrs(ifp->idev, ifp, cfg->valid_lft, 489862306a36Sopenharmony_ci cfg->preferred_lft, !was_managetempaddr, 489962306a36Sopenharmony_ci jiffies); 490062306a36Sopenharmony_ci } 490162306a36Sopenharmony_ci 490262306a36Sopenharmony_ci addrconf_verify_rtnl(net); 490362306a36Sopenharmony_ci 490462306a36Sopenharmony_ci return 0; 490562306a36Sopenharmony_ci} 490662306a36Sopenharmony_ci 490762306a36Sopenharmony_cistatic int 490862306a36Sopenharmony_ciinet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, 490962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 491062306a36Sopenharmony_ci{ 491162306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 491262306a36Sopenharmony_ci struct ifaddrmsg *ifm; 491362306a36Sopenharmony_ci struct nlattr *tb[IFA_MAX+1]; 491462306a36Sopenharmony_ci struct in6_addr *peer_pfx; 491562306a36Sopenharmony_ci struct inet6_ifaddr *ifa; 491662306a36Sopenharmony_ci struct net_device *dev; 491762306a36Sopenharmony_ci struct inet6_dev *idev; 491862306a36Sopenharmony_ci struct ifa6_config cfg; 491962306a36Sopenharmony_ci int err; 492062306a36Sopenharmony_ci 492162306a36Sopenharmony_ci err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 492262306a36Sopenharmony_ci ifa_ipv6_policy, extack); 492362306a36Sopenharmony_ci if (err < 0) 492462306a36Sopenharmony_ci return err; 492562306a36Sopenharmony_ci 492662306a36Sopenharmony_ci memset(&cfg, 0, sizeof(cfg)); 492762306a36Sopenharmony_ci 492862306a36Sopenharmony_ci ifm = nlmsg_data(nlh); 492962306a36Sopenharmony_ci cfg.pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx); 493062306a36Sopenharmony_ci if (!cfg.pfx) 493162306a36Sopenharmony_ci return -EINVAL; 493262306a36Sopenharmony_ci 493362306a36Sopenharmony_ci cfg.peer_pfx = peer_pfx; 493462306a36Sopenharmony_ci cfg.plen = ifm->ifa_prefixlen; 493562306a36Sopenharmony_ci if (tb[IFA_RT_PRIORITY]) 493662306a36Sopenharmony_ci cfg.rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]); 493762306a36Sopenharmony_ci 493862306a36Sopenharmony_ci if (tb[IFA_PROTO]) 493962306a36Sopenharmony_ci cfg.ifa_proto = nla_get_u8(tb[IFA_PROTO]); 494062306a36Sopenharmony_ci 494162306a36Sopenharmony_ci cfg.valid_lft = INFINITY_LIFE_TIME; 494262306a36Sopenharmony_ci cfg.preferred_lft = INFINITY_LIFE_TIME; 494362306a36Sopenharmony_ci 494462306a36Sopenharmony_ci if (tb[IFA_CACHEINFO]) { 494562306a36Sopenharmony_ci struct ifa_cacheinfo *ci; 494662306a36Sopenharmony_ci 494762306a36Sopenharmony_ci ci = nla_data(tb[IFA_CACHEINFO]); 494862306a36Sopenharmony_ci cfg.valid_lft = ci->ifa_valid; 494962306a36Sopenharmony_ci cfg.preferred_lft = ci->ifa_prefered; 495062306a36Sopenharmony_ci } 495162306a36Sopenharmony_ci 495262306a36Sopenharmony_ci dev = __dev_get_by_index(net, ifm->ifa_index); 495362306a36Sopenharmony_ci if (!dev) { 495462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unable to find the interface"); 495562306a36Sopenharmony_ci return -ENODEV; 495662306a36Sopenharmony_ci } 495762306a36Sopenharmony_ci 495862306a36Sopenharmony_ci if (tb[IFA_FLAGS]) 495962306a36Sopenharmony_ci cfg.ifa_flags = nla_get_u32(tb[IFA_FLAGS]); 496062306a36Sopenharmony_ci else 496162306a36Sopenharmony_ci cfg.ifa_flags = ifm->ifa_flags; 496262306a36Sopenharmony_ci 496362306a36Sopenharmony_ci /* We ignore other flags so far. */ 496462306a36Sopenharmony_ci cfg.ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | 496562306a36Sopenharmony_ci IFA_F_MANAGETEMPADDR | IFA_F_NOPREFIXROUTE | 496662306a36Sopenharmony_ci IFA_F_MCAUTOJOIN | IFA_F_OPTIMISTIC; 496762306a36Sopenharmony_ci 496862306a36Sopenharmony_ci idev = ipv6_find_idev(dev); 496962306a36Sopenharmony_ci if (IS_ERR(idev)) 497062306a36Sopenharmony_ci return PTR_ERR(idev); 497162306a36Sopenharmony_ci 497262306a36Sopenharmony_ci if (!ipv6_allow_optimistic_dad(net, idev)) 497362306a36Sopenharmony_ci cfg.ifa_flags &= ~IFA_F_OPTIMISTIC; 497462306a36Sopenharmony_ci 497562306a36Sopenharmony_ci if (cfg.ifa_flags & IFA_F_NODAD && 497662306a36Sopenharmony_ci cfg.ifa_flags & IFA_F_OPTIMISTIC) { 497762306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, "IFA_F_NODAD and IFA_F_OPTIMISTIC are mutually exclusive"); 497862306a36Sopenharmony_ci return -EINVAL; 497962306a36Sopenharmony_ci } 498062306a36Sopenharmony_ci 498162306a36Sopenharmony_ci ifa = ipv6_get_ifaddr(net, cfg.pfx, dev, 1); 498262306a36Sopenharmony_ci if (!ifa) { 498362306a36Sopenharmony_ci /* 498462306a36Sopenharmony_ci * It would be best to check for !NLM_F_CREATE here but 498562306a36Sopenharmony_ci * userspace already relies on not having to provide this. 498662306a36Sopenharmony_ci */ 498762306a36Sopenharmony_ci return inet6_addr_add(net, ifm->ifa_index, &cfg, extack); 498862306a36Sopenharmony_ci } 498962306a36Sopenharmony_ci 499062306a36Sopenharmony_ci if (nlh->nlmsg_flags & NLM_F_EXCL || 499162306a36Sopenharmony_ci !(nlh->nlmsg_flags & NLM_F_REPLACE)) { 499262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "address already assigned"); 499362306a36Sopenharmony_ci err = -EEXIST; 499462306a36Sopenharmony_ci } else { 499562306a36Sopenharmony_ci err = inet6_addr_modify(net, ifa, &cfg); 499662306a36Sopenharmony_ci } 499762306a36Sopenharmony_ci 499862306a36Sopenharmony_ci in6_ifa_put(ifa); 499962306a36Sopenharmony_ci 500062306a36Sopenharmony_ci return err; 500162306a36Sopenharmony_ci} 500262306a36Sopenharmony_ci 500362306a36Sopenharmony_cistatic void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u32 flags, 500462306a36Sopenharmony_ci u8 scope, int ifindex) 500562306a36Sopenharmony_ci{ 500662306a36Sopenharmony_ci struct ifaddrmsg *ifm; 500762306a36Sopenharmony_ci 500862306a36Sopenharmony_ci ifm = nlmsg_data(nlh); 500962306a36Sopenharmony_ci ifm->ifa_family = AF_INET6; 501062306a36Sopenharmony_ci ifm->ifa_prefixlen = prefixlen; 501162306a36Sopenharmony_ci ifm->ifa_flags = flags; 501262306a36Sopenharmony_ci ifm->ifa_scope = scope; 501362306a36Sopenharmony_ci ifm->ifa_index = ifindex; 501462306a36Sopenharmony_ci} 501562306a36Sopenharmony_ci 501662306a36Sopenharmony_cistatic int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 501762306a36Sopenharmony_ci unsigned long tstamp, u32 preferred, u32 valid) 501862306a36Sopenharmony_ci{ 501962306a36Sopenharmony_ci struct ifa_cacheinfo ci; 502062306a36Sopenharmony_ci 502162306a36Sopenharmony_ci ci.cstamp = cstamp_delta(cstamp); 502262306a36Sopenharmony_ci ci.tstamp = cstamp_delta(tstamp); 502362306a36Sopenharmony_ci ci.ifa_prefered = preferred; 502462306a36Sopenharmony_ci ci.ifa_valid = valid; 502562306a36Sopenharmony_ci 502662306a36Sopenharmony_ci return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 502762306a36Sopenharmony_ci} 502862306a36Sopenharmony_ci 502962306a36Sopenharmony_cistatic inline int rt_scope(int ifa_scope) 503062306a36Sopenharmony_ci{ 503162306a36Sopenharmony_ci if (ifa_scope & IFA_HOST) 503262306a36Sopenharmony_ci return RT_SCOPE_HOST; 503362306a36Sopenharmony_ci else if (ifa_scope & IFA_LINK) 503462306a36Sopenharmony_ci return RT_SCOPE_LINK; 503562306a36Sopenharmony_ci else if (ifa_scope & IFA_SITE) 503662306a36Sopenharmony_ci return RT_SCOPE_SITE; 503762306a36Sopenharmony_ci else 503862306a36Sopenharmony_ci return RT_SCOPE_UNIVERSE; 503962306a36Sopenharmony_ci} 504062306a36Sopenharmony_ci 504162306a36Sopenharmony_cistatic inline int inet6_ifaddr_msgsize(void) 504262306a36Sopenharmony_ci{ 504362306a36Sopenharmony_ci return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 504462306a36Sopenharmony_ci + nla_total_size(16) /* IFA_LOCAL */ 504562306a36Sopenharmony_ci + nla_total_size(16) /* IFA_ADDRESS */ 504662306a36Sopenharmony_ci + nla_total_size(sizeof(struct ifa_cacheinfo)) 504762306a36Sopenharmony_ci + nla_total_size(4) /* IFA_FLAGS */ 504862306a36Sopenharmony_ci + nla_total_size(1) /* IFA_PROTO */ 504962306a36Sopenharmony_ci + nla_total_size(4) /* IFA_RT_PRIORITY */; 505062306a36Sopenharmony_ci} 505162306a36Sopenharmony_ci 505262306a36Sopenharmony_cienum addr_type_t { 505362306a36Sopenharmony_ci UNICAST_ADDR, 505462306a36Sopenharmony_ci MULTICAST_ADDR, 505562306a36Sopenharmony_ci ANYCAST_ADDR, 505662306a36Sopenharmony_ci}; 505762306a36Sopenharmony_ci 505862306a36Sopenharmony_cistruct inet6_fill_args { 505962306a36Sopenharmony_ci u32 portid; 506062306a36Sopenharmony_ci u32 seq; 506162306a36Sopenharmony_ci int event; 506262306a36Sopenharmony_ci unsigned int flags; 506362306a36Sopenharmony_ci int netnsid; 506462306a36Sopenharmony_ci int ifindex; 506562306a36Sopenharmony_ci enum addr_type_t type; 506662306a36Sopenharmony_ci}; 506762306a36Sopenharmony_ci 506862306a36Sopenharmony_cistatic int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, 506962306a36Sopenharmony_ci struct inet6_fill_args *args) 507062306a36Sopenharmony_ci{ 507162306a36Sopenharmony_ci struct nlmsghdr *nlh; 507262306a36Sopenharmony_ci u32 preferred, valid; 507362306a36Sopenharmony_ci 507462306a36Sopenharmony_ci nlh = nlmsg_put(skb, args->portid, args->seq, args->event, 507562306a36Sopenharmony_ci sizeof(struct ifaddrmsg), args->flags); 507662306a36Sopenharmony_ci if (!nlh) 507762306a36Sopenharmony_ci return -EMSGSIZE; 507862306a36Sopenharmony_ci 507962306a36Sopenharmony_ci put_ifaddrmsg(nlh, ifa->prefix_len, ifa->flags, rt_scope(ifa->scope), 508062306a36Sopenharmony_ci ifa->idev->dev->ifindex); 508162306a36Sopenharmony_ci 508262306a36Sopenharmony_ci if (args->netnsid >= 0 && 508362306a36Sopenharmony_ci nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) 508462306a36Sopenharmony_ci goto error; 508562306a36Sopenharmony_ci 508662306a36Sopenharmony_ci spin_lock_bh(&ifa->lock); 508762306a36Sopenharmony_ci if (!((ifa->flags&IFA_F_PERMANENT) && 508862306a36Sopenharmony_ci (ifa->prefered_lft == INFINITY_LIFE_TIME))) { 508962306a36Sopenharmony_ci preferred = ifa->prefered_lft; 509062306a36Sopenharmony_ci valid = ifa->valid_lft; 509162306a36Sopenharmony_ci if (preferred != INFINITY_LIFE_TIME) { 509262306a36Sopenharmony_ci long tval = (jiffies - ifa->tstamp)/HZ; 509362306a36Sopenharmony_ci if (preferred > tval) 509462306a36Sopenharmony_ci preferred -= tval; 509562306a36Sopenharmony_ci else 509662306a36Sopenharmony_ci preferred = 0; 509762306a36Sopenharmony_ci if (valid != INFINITY_LIFE_TIME) { 509862306a36Sopenharmony_ci if (valid > tval) 509962306a36Sopenharmony_ci valid -= tval; 510062306a36Sopenharmony_ci else 510162306a36Sopenharmony_ci valid = 0; 510262306a36Sopenharmony_ci } 510362306a36Sopenharmony_ci } 510462306a36Sopenharmony_ci } else { 510562306a36Sopenharmony_ci preferred = INFINITY_LIFE_TIME; 510662306a36Sopenharmony_ci valid = INFINITY_LIFE_TIME; 510762306a36Sopenharmony_ci } 510862306a36Sopenharmony_ci spin_unlock_bh(&ifa->lock); 510962306a36Sopenharmony_ci 511062306a36Sopenharmony_ci if (!ipv6_addr_any(&ifa->peer_addr)) { 511162306a36Sopenharmony_ci if (nla_put_in6_addr(skb, IFA_LOCAL, &ifa->addr) < 0 || 511262306a36Sopenharmony_ci nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->peer_addr) < 0) 511362306a36Sopenharmony_ci goto error; 511462306a36Sopenharmony_ci } else 511562306a36Sopenharmony_ci if (nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->addr) < 0) 511662306a36Sopenharmony_ci goto error; 511762306a36Sopenharmony_ci 511862306a36Sopenharmony_ci if (ifa->rt_priority && 511962306a36Sopenharmony_ci nla_put_u32(skb, IFA_RT_PRIORITY, ifa->rt_priority)) 512062306a36Sopenharmony_ci goto error; 512162306a36Sopenharmony_ci 512262306a36Sopenharmony_ci if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) 512362306a36Sopenharmony_ci goto error; 512462306a36Sopenharmony_ci 512562306a36Sopenharmony_ci if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0) 512662306a36Sopenharmony_ci goto error; 512762306a36Sopenharmony_ci 512862306a36Sopenharmony_ci if (ifa->ifa_proto && 512962306a36Sopenharmony_ci nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) 513062306a36Sopenharmony_ci goto error; 513162306a36Sopenharmony_ci 513262306a36Sopenharmony_ci nlmsg_end(skb, nlh); 513362306a36Sopenharmony_ci return 0; 513462306a36Sopenharmony_ci 513562306a36Sopenharmony_cierror: 513662306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 513762306a36Sopenharmony_ci return -EMSGSIZE; 513862306a36Sopenharmony_ci} 513962306a36Sopenharmony_ci 514062306a36Sopenharmony_cistatic int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, 514162306a36Sopenharmony_ci struct inet6_fill_args *args) 514262306a36Sopenharmony_ci{ 514362306a36Sopenharmony_ci struct nlmsghdr *nlh; 514462306a36Sopenharmony_ci u8 scope = RT_SCOPE_UNIVERSE; 514562306a36Sopenharmony_ci int ifindex = ifmca->idev->dev->ifindex; 514662306a36Sopenharmony_ci 514762306a36Sopenharmony_ci if (ipv6_addr_scope(&ifmca->mca_addr) & IFA_SITE) 514862306a36Sopenharmony_ci scope = RT_SCOPE_SITE; 514962306a36Sopenharmony_ci 515062306a36Sopenharmony_ci nlh = nlmsg_put(skb, args->portid, args->seq, args->event, 515162306a36Sopenharmony_ci sizeof(struct ifaddrmsg), args->flags); 515262306a36Sopenharmony_ci if (!nlh) 515362306a36Sopenharmony_ci return -EMSGSIZE; 515462306a36Sopenharmony_ci 515562306a36Sopenharmony_ci if (args->netnsid >= 0 && 515662306a36Sopenharmony_ci nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) { 515762306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 515862306a36Sopenharmony_ci return -EMSGSIZE; 515962306a36Sopenharmony_ci } 516062306a36Sopenharmony_ci 516162306a36Sopenharmony_ci put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex); 516262306a36Sopenharmony_ci if (nla_put_in6_addr(skb, IFA_MULTICAST, &ifmca->mca_addr) < 0 || 516362306a36Sopenharmony_ci put_cacheinfo(skb, ifmca->mca_cstamp, ifmca->mca_tstamp, 516462306a36Sopenharmony_ci INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) { 516562306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 516662306a36Sopenharmony_ci return -EMSGSIZE; 516762306a36Sopenharmony_ci } 516862306a36Sopenharmony_ci 516962306a36Sopenharmony_ci nlmsg_end(skb, nlh); 517062306a36Sopenharmony_ci return 0; 517162306a36Sopenharmony_ci} 517262306a36Sopenharmony_ci 517362306a36Sopenharmony_cistatic int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca, 517462306a36Sopenharmony_ci struct inet6_fill_args *args) 517562306a36Sopenharmony_ci{ 517662306a36Sopenharmony_ci struct net_device *dev = fib6_info_nh_dev(ifaca->aca_rt); 517762306a36Sopenharmony_ci int ifindex = dev ? dev->ifindex : 1; 517862306a36Sopenharmony_ci struct nlmsghdr *nlh; 517962306a36Sopenharmony_ci u8 scope = RT_SCOPE_UNIVERSE; 518062306a36Sopenharmony_ci 518162306a36Sopenharmony_ci if (ipv6_addr_scope(&ifaca->aca_addr) & IFA_SITE) 518262306a36Sopenharmony_ci scope = RT_SCOPE_SITE; 518362306a36Sopenharmony_ci 518462306a36Sopenharmony_ci nlh = nlmsg_put(skb, args->portid, args->seq, args->event, 518562306a36Sopenharmony_ci sizeof(struct ifaddrmsg), args->flags); 518662306a36Sopenharmony_ci if (!nlh) 518762306a36Sopenharmony_ci return -EMSGSIZE; 518862306a36Sopenharmony_ci 518962306a36Sopenharmony_ci if (args->netnsid >= 0 && 519062306a36Sopenharmony_ci nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) { 519162306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 519262306a36Sopenharmony_ci return -EMSGSIZE; 519362306a36Sopenharmony_ci } 519462306a36Sopenharmony_ci 519562306a36Sopenharmony_ci put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex); 519662306a36Sopenharmony_ci if (nla_put_in6_addr(skb, IFA_ANYCAST, &ifaca->aca_addr) < 0 || 519762306a36Sopenharmony_ci put_cacheinfo(skb, ifaca->aca_cstamp, ifaca->aca_tstamp, 519862306a36Sopenharmony_ci INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) { 519962306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 520062306a36Sopenharmony_ci return -EMSGSIZE; 520162306a36Sopenharmony_ci } 520262306a36Sopenharmony_ci 520362306a36Sopenharmony_ci nlmsg_end(skb, nlh); 520462306a36Sopenharmony_ci return 0; 520562306a36Sopenharmony_ci} 520662306a36Sopenharmony_ci 520762306a36Sopenharmony_ci/* called with rcu_read_lock() */ 520862306a36Sopenharmony_cistatic int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, 520962306a36Sopenharmony_ci struct netlink_callback *cb, int s_ip_idx, 521062306a36Sopenharmony_ci struct inet6_fill_args *fillargs) 521162306a36Sopenharmony_ci{ 521262306a36Sopenharmony_ci struct ifmcaddr6 *ifmca; 521362306a36Sopenharmony_ci struct ifacaddr6 *ifaca; 521462306a36Sopenharmony_ci int ip_idx = 0; 521562306a36Sopenharmony_ci int err = 1; 521662306a36Sopenharmony_ci 521762306a36Sopenharmony_ci read_lock_bh(&idev->lock); 521862306a36Sopenharmony_ci switch (fillargs->type) { 521962306a36Sopenharmony_ci case UNICAST_ADDR: { 522062306a36Sopenharmony_ci struct inet6_ifaddr *ifa; 522162306a36Sopenharmony_ci fillargs->event = RTM_NEWADDR; 522262306a36Sopenharmony_ci 522362306a36Sopenharmony_ci /* unicast address incl. temp addr */ 522462306a36Sopenharmony_ci list_for_each_entry(ifa, &idev->addr_list, if_list) { 522562306a36Sopenharmony_ci if (ip_idx < s_ip_idx) 522662306a36Sopenharmony_ci goto next; 522762306a36Sopenharmony_ci err = inet6_fill_ifaddr(skb, ifa, fillargs); 522862306a36Sopenharmony_ci if (err < 0) 522962306a36Sopenharmony_ci break; 523062306a36Sopenharmony_ci nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 523162306a36Sopenharmony_cinext: 523262306a36Sopenharmony_ci ip_idx++; 523362306a36Sopenharmony_ci } 523462306a36Sopenharmony_ci break; 523562306a36Sopenharmony_ci } 523662306a36Sopenharmony_ci case MULTICAST_ADDR: 523762306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 523862306a36Sopenharmony_ci fillargs->event = RTM_GETMULTICAST; 523962306a36Sopenharmony_ci 524062306a36Sopenharmony_ci /* multicast address */ 524162306a36Sopenharmony_ci for (ifmca = rtnl_dereference(idev->mc_list); 524262306a36Sopenharmony_ci ifmca; 524362306a36Sopenharmony_ci ifmca = rtnl_dereference(ifmca->next), ip_idx++) { 524462306a36Sopenharmony_ci if (ip_idx < s_ip_idx) 524562306a36Sopenharmony_ci continue; 524662306a36Sopenharmony_ci err = inet6_fill_ifmcaddr(skb, ifmca, fillargs); 524762306a36Sopenharmony_ci if (err < 0) 524862306a36Sopenharmony_ci break; 524962306a36Sopenharmony_ci } 525062306a36Sopenharmony_ci read_lock_bh(&idev->lock); 525162306a36Sopenharmony_ci break; 525262306a36Sopenharmony_ci case ANYCAST_ADDR: 525362306a36Sopenharmony_ci fillargs->event = RTM_GETANYCAST; 525462306a36Sopenharmony_ci /* anycast address */ 525562306a36Sopenharmony_ci for (ifaca = idev->ac_list; ifaca; 525662306a36Sopenharmony_ci ifaca = ifaca->aca_next, ip_idx++) { 525762306a36Sopenharmony_ci if (ip_idx < s_ip_idx) 525862306a36Sopenharmony_ci continue; 525962306a36Sopenharmony_ci err = inet6_fill_ifacaddr(skb, ifaca, fillargs); 526062306a36Sopenharmony_ci if (err < 0) 526162306a36Sopenharmony_ci break; 526262306a36Sopenharmony_ci } 526362306a36Sopenharmony_ci break; 526462306a36Sopenharmony_ci default: 526562306a36Sopenharmony_ci break; 526662306a36Sopenharmony_ci } 526762306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 526862306a36Sopenharmony_ci cb->args[2] = ip_idx; 526962306a36Sopenharmony_ci return err; 527062306a36Sopenharmony_ci} 527162306a36Sopenharmony_ci 527262306a36Sopenharmony_cistatic int inet6_valid_dump_ifaddr_req(const struct nlmsghdr *nlh, 527362306a36Sopenharmony_ci struct inet6_fill_args *fillargs, 527462306a36Sopenharmony_ci struct net **tgt_net, struct sock *sk, 527562306a36Sopenharmony_ci struct netlink_callback *cb) 527662306a36Sopenharmony_ci{ 527762306a36Sopenharmony_ci struct netlink_ext_ack *extack = cb->extack; 527862306a36Sopenharmony_ci struct nlattr *tb[IFA_MAX+1]; 527962306a36Sopenharmony_ci struct ifaddrmsg *ifm; 528062306a36Sopenharmony_ci int err, i; 528162306a36Sopenharmony_ci 528262306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 528362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for address dump request"); 528462306a36Sopenharmony_ci return -EINVAL; 528562306a36Sopenharmony_ci } 528662306a36Sopenharmony_ci 528762306a36Sopenharmony_ci ifm = nlmsg_data(nlh); 528862306a36Sopenharmony_ci if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) { 528962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for address dump request"); 529062306a36Sopenharmony_ci return -EINVAL; 529162306a36Sopenharmony_ci } 529262306a36Sopenharmony_ci 529362306a36Sopenharmony_ci fillargs->ifindex = ifm->ifa_index; 529462306a36Sopenharmony_ci if (fillargs->ifindex) { 529562306a36Sopenharmony_ci cb->answer_flags |= NLM_F_DUMP_FILTERED; 529662306a36Sopenharmony_ci fillargs->flags |= NLM_F_DUMP_FILTERED; 529762306a36Sopenharmony_ci } 529862306a36Sopenharmony_ci 529962306a36Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX, 530062306a36Sopenharmony_ci ifa_ipv6_policy, extack); 530162306a36Sopenharmony_ci if (err < 0) 530262306a36Sopenharmony_ci return err; 530362306a36Sopenharmony_ci 530462306a36Sopenharmony_ci for (i = 0; i <= IFA_MAX; ++i) { 530562306a36Sopenharmony_ci if (!tb[i]) 530662306a36Sopenharmony_ci continue; 530762306a36Sopenharmony_ci 530862306a36Sopenharmony_ci if (i == IFA_TARGET_NETNSID) { 530962306a36Sopenharmony_ci struct net *net; 531062306a36Sopenharmony_ci 531162306a36Sopenharmony_ci fillargs->netnsid = nla_get_s32(tb[i]); 531262306a36Sopenharmony_ci net = rtnl_get_net_ns_capable(sk, fillargs->netnsid); 531362306a36Sopenharmony_ci if (IS_ERR(net)) { 531462306a36Sopenharmony_ci fillargs->netnsid = -1; 531562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid target network namespace id"); 531662306a36Sopenharmony_ci return PTR_ERR(net); 531762306a36Sopenharmony_ci } 531862306a36Sopenharmony_ci *tgt_net = net; 531962306a36Sopenharmony_ci } else { 532062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in dump request"); 532162306a36Sopenharmony_ci return -EINVAL; 532262306a36Sopenharmony_ci } 532362306a36Sopenharmony_ci } 532462306a36Sopenharmony_ci 532562306a36Sopenharmony_ci return 0; 532662306a36Sopenharmony_ci} 532762306a36Sopenharmony_ci 532862306a36Sopenharmony_cistatic int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, 532962306a36Sopenharmony_ci enum addr_type_t type) 533062306a36Sopenharmony_ci{ 533162306a36Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 533262306a36Sopenharmony_ci struct inet6_fill_args fillargs = { 533362306a36Sopenharmony_ci .portid = NETLINK_CB(cb->skb).portid, 533462306a36Sopenharmony_ci .seq = cb->nlh->nlmsg_seq, 533562306a36Sopenharmony_ci .flags = NLM_F_MULTI, 533662306a36Sopenharmony_ci .netnsid = -1, 533762306a36Sopenharmony_ci .type = type, 533862306a36Sopenharmony_ci }; 533962306a36Sopenharmony_ci struct net *tgt_net = sock_net(skb->sk); 534062306a36Sopenharmony_ci int idx, s_idx, s_ip_idx; 534162306a36Sopenharmony_ci int h, s_h; 534262306a36Sopenharmony_ci struct net_device *dev; 534362306a36Sopenharmony_ci struct inet6_dev *idev; 534462306a36Sopenharmony_ci struct hlist_head *head; 534562306a36Sopenharmony_ci int err = 0; 534662306a36Sopenharmony_ci 534762306a36Sopenharmony_ci s_h = cb->args[0]; 534862306a36Sopenharmony_ci s_idx = idx = cb->args[1]; 534962306a36Sopenharmony_ci s_ip_idx = cb->args[2]; 535062306a36Sopenharmony_ci 535162306a36Sopenharmony_ci if (cb->strict_check) { 535262306a36Sopenharmony_ci err = inet6_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net, 535362306a36Sopenharmony_ci skb->sk, cb); 535462306a36Sopenharmony_ci if (err < 0) 535562306a36Sopenharmony_ci goto put_tgt_net; 535662306a36Sopenharmony_ci 535762306a36Sopenharmony_ci err = 0; 535862306a36Sopenharmony_ci if (fillargs.ifindex) { 535962306a36Sopenharmony_ci dev = __dev_get_by_index(tgt_net, fillargs.ifindex); 536062306a36Sopenharmony_ci if (!dev) { 536162306a36Sopenharmony_ci err = -ENODEV; 536262306a36Sopenharmony_ci goto put_tgt_net; 536362306a36Sopenharmony_ci } 536462306a36Sopenharmony_ci idev = __in6_dev_get(dev); 536562306a36Sopenharmony_ci if (idev) { 536662306a36Sopenharmony_ci err = in6_dump_addrs(idev, skb, cb, s_ip_idx, 536762306a36Sopenharmony_ci &fillargs); 536862306a36Sopenharmony_ci if (err > 0) 536962306a36Sopenharmony_ci err = 0; 537062306a36Sopenharmony_ci } 537162306a36Sopenharmony_ci goto put_tgt_net; 537262306a36Sopenharmony_ci } 537362306a36Sopenharmony_ci } 537462306a36Sopenharmony_ci 537562306a36Sopenharmony_ci rcu_read_lock(); 537662306a36Sopenharmony_ci cb->seq = inet6_base_seq(tgt_net); 537762306a36Sopenharmony_ci for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 537862306a36Sopenharmony_ci idx = 0; 537962306a36Sopenharmony_ci head = &tgt_net->dev_index_head[h]; 538062306a36Sopenharmony_ci hlist_for_each_entry_rcu(dev, head, index_hlist) { 538162306a36Sopenharmony_ci if (idx < s_idx) 538262306a36Sopenharmony_ci goto cont; 538362306a36Sopenharmony_ci if (h > s_h || idx > s_idx) 538462306a36Sopenharmony_ci s_ip_idx = 0; 538562306a36Sopenharmony_ci idev = __in6_dev_get(dev); 538662306a36Sopenharmony_ci if (!idev) 538762306a36Sopenharmony_ci goto cont; 538862306a36Sopenharmony_ci 538962306a36Sopenharmony_ci if (in6_dump_addrs(idev, skb, cb, s_ip_idx, 539062306a36Sopenharmony_ci &fillargs) < 0) 539162306a36Sopenharmony_ci goto done; 539262306a36Sopenharmony_cicont: 539362306a36Sopenharmony_ci idx++; 539462306a36Sopenharmony_ci } 539562306a36Sopenharmony_ci } 539662306a36Sopenharmony_cidone: 539762306a36Sopenharmony_ci rcu_read_unlock(); 539862306a36Sopenharmony_ci cb->args[0] = h; 539962306a36Sopenharmony_ci cb->args[1] = idx; 540062306a36Sopenharmony_ciput_tgt_net: 540162306a36Sopenharmony_ci if (fillargs.netnsid >= 0) 540262306a36Sopenharmony_ci put_net(tgt_net); 540362306a36Sopenharmony_ci 540462306a36Sopenharmony_ci return skb->len ? : err; 540562306a36Sopenharmony_ci} 540662306a36Sopenharmony_ci 540762306a36Sopenharmony_cistatic int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 540862306a36Sopenharmony_ci{ 540962306a36Sopenharmony_ci enum addr_type_t type = UNICAST_ADDR; 541062306a36Sopenharmony_ci 541162306a36Sopenharmony_ci return inet6_dump_addr(skb, cb, type); 541262306a36Sopenharmony_ci} 541362306a36Sopenharmony_ci 541462306a36Sopenharmony_cistatic int inet6_dump_ifmcaddr(struct sk_buff *skb, struct netlink_callback *cb) 541562306a36Sopenharmony_ci{ 541662306a36Sopenharmony_ci enum addr_type_t type = MULTICAST_ADDR; 541762306a36Sopenharmony_ci 541862306a36Sopenharmony_ci return inet6_dump_addr(skb, cb, type); 541962306a36Sopenharmony_ci} 542062306a36Sopenharmony_ci 542162306a36Sopenharmony_ci 542262306a36Sopenharmony_cistatic int inet6_dump_ifacaddr(struct sk_buff *skb, struct netlink_callback *cb) 542362306a36Sopenharmony_ci{ 542462306a36Sopenharmony_ci enum addr_type_t type = ANYCAST_ADDR; 542562306a36Sopenharmony_ci 542662306a36Sopenharmony_ci return inet6_dump_addr(skb, cb, type); 542762306a36Sopenharmony_ci} 542862306a36Sopenharmony_ci 542962306a36Sopenharmony_cistatic int inet6_rtm_valid_getaddr_req(struct sk_buff *skb, 543062306a36Sopenharmony_ci const struct nlmsghdr *nlh, 543162306a36Sopenharmony_ci struct nlattr **tb, 543262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 543362306a36Sopenharmony_ci{ 543462306a36Sopenharmony_ci struct ifaddrmsg *ifm; 543562306a36Sopenharmony_ci int i, err; 543662306a36Sopenharmony_ci 543762306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 543862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for get address request"); 543962306a36Sopenharmony_ci return -EINVAL; 544062306a36Sopenharmony_ci } 544162306a36Sopenharmony_ci 544262306a36Sopenharmony_ci if (!netlink_strict_get_check(skb)) 544362306a36Sopenharmony_ci return nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 544462306a36Sopenharmony_ci ifa_ipv6_policy, extack); 544562306a36Sopenharmony_ci 544662306a36Sopenharmony_ci ifm = nlmsg_data(nlh); 544762306a36Sopenharmony_ci if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) { 544862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get address request"); 544962306a36Sopenharmony_ci return -EINVAL; 545062306a36Sopenharmony_ci } 545162306a36Sopenharmony_ci 545262306a36Sopenharmony_ci err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX, 545362306a36Sopenharmony_ci ifa_ipv6_policy, extack); 545462306a36Sopenharmony_ci if (err) 545562306a36Sopenharmony_ci return err; 545662306a36Sopenharmony_ci 545762306a36Sopenharmony_ci for (i = 0; i <= IFA_MAX; i++) { 545862306a36Sopenharmony_ci if (!tb[i]) 545962306a36Sopenharmony_ci continue; 546062306a36Sopenharmony_ci 546162306a36Sopenharmony_ci switch (i) { 546262306a36Sopenharmony_ci case IFA_TARGET_NETNSID: 546362306a36Sopenharmony_ci case IFA_ADDRESS: 546462306a36Sopenharmony_ci case IFA_LOCAL: 546562306a36Sopenharmony_ci break; 546662306a36Sopenharmony_ci default: 546762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get address request"); 546862306a36Sopenharmony_ci return -EINVAL; 546962306a36Sopenharmony_ci } 547062306a36Sopenharmony_ci } 547162306a36Sopenharmony_ci 547262306a36Sopenharmony_ci return 0; 547362306a36Sopenharmony_ci} 547462306a36Sopenharmony_ci 547562306a36Sopenharmony_cistatic int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh, 547662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 547762306a36Sopenharmony_ci{ 547862306a36Sopenharmony_ci struct net *tgt_net = sock_net(in_skb->sk); 547962306a36Sopenharmony_ci struct inet6_fill_args fillargs = { 548062306a36Sopenharmony_ci .portid = NETLINK_CB(in_skb).portid, 548162306a36Sopenharmony_ci .seq = nlh->nlmsg_seq, 548262306a36Sopenharmony_ci .event = RTM_NEWADDR, 548362306a36Sopenharmony_ci .flags = 0, 548462306a36Sopenharmony_ci .netnsid = -1, 548562306a36Sopenharmony_ci }; 548662306a36Sopenharmony_ci struct ifaddrmsg *ifm; 548762306a36Sopenharmony_ci struct nlattr *tb[IFA_MAX+1]; 548862306a36Sopenharmony_ci struct in6_addr *addr = NULL, *peer; 548962306a36Sopenharmony_ci struct net_device *dev = NULL; 549062306a36Sopenharmony_ci struct inet6_ifaddr *ifa; 549162306a36Sopenharmony_ci struct sk_buff *skb; 549262306a36Sopenharmony_ci int err; 549362306a36Sopenharmony_ci 549462306a36Sopenharmony_ci err = inet6_rtm_valid_getaddr_req(in_skb, nlh, tb, extack); 549562306a36Sopenharmony_ci if (err < 0) 549662306a36Sopenharmony_ci return err; 549762306a36Sopenharmony_ci 549862306a36Sopenharmony_ci if (tb[IFA_TARGET_NETNSID]) { 549962306a36Sopenharmony_ci fillargs.netnsid = nla_get_s32(tb[IFA_TARGET_NETNSID]); 550062306a36Sopenharmony_ci 550162306a36Sopenharmony_ci tgt_net = rtnl_get_net_ns_capable(NETLINK_CB(in_skb).sk, 550262306a36Sopenharmony_ci fillargs.netnsid); 550362306a36Sopenharmony_ci if (IS_ERR(tgt_net)) 550462306a36Sopenharmony_ci return PTR_ERR(tgt_net); 550562306a36Sopenharmony_ci } 550662306a36Sopenharmony_ci 550762306a36Sopenharmony_ci addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer); 550862306a36Sopenharmony_ci if (!addr) { 550962306a36Sopenharmony_ci err = -EINVAL; 551062306a36Sopenharmony_ci goto errout; 551162306a36Sopenharmony_ci } 551262306a36Sopenharmony_ci ifm = nlmsg_data(nlh); 551362306a36Sopenharmony_ci if (ifm->ifa_index) 551462306a36Sopenharmony_ci dev = dev_get_by_index(tgt_net, ifm->ifa_index); 551562306a36Sopenharmony_ci 551662306a36Sopenharmony_ci ifa = ipv6_get_ifaddr(tgt_net, addr, dev, 1); 551762306a36Sopenharmony_ci if (!ifa) { 551862306a36Sopenharmony_ci err = -EADDRNOTAVAIL; 551962306a36Sopenharmony_ci goto errout; 552062306a36Sopenharmony_ci } 552162306a36Sopenharmony_ci 552262306a36Sopenharmony_ci skb = nlmsg_new(inet6_ifaddr_msgsize(), GFP_KERNEL); 552362306a36Sopenharmony_ci if (!skb) { 552462306a36Sopenharmony_ci err = -ENOBUFS; 552562306a36Sopenharmony_ci goto errout_ifa; 552662306a36Sopenharmony_ci } 552762306a36Sopenharmony_ci 552862306a36Sopenharmony_ci err = inet6_fill_ifaddr(skb, ifa, &fillargs); 552962306a36Sopenharmony_ci if (err < 0) { 553062306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */ 553162306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 553262306a36Sopenharmony_ci kfree_skb(skb); 553362306a36Sopenharmony_ci goto errout_ifa; 553462306a36Sopenharmony_ci } 553562306a36Sopenharmony_ci err = rtnl_unicast(skb, tgt_net, NETLINK_CB(in_skb).portid); 553662306a36Sopenharmony_cierrout_ifa: 553762306a36Sopenharmony_ci in6_ifa_put(ifa); 553862306a36Sopenharmony_cierrout: 553962306a36Sopenharmony_ci dev_put(dev); 554062306a36Sopenharmony_ci if (fillargs.netnsid >= 0) 554162306a36Sopenharmony_ci put_net(tgt_net); 554262306a36Sopenharmony_ci 554362306a36Sopenharmony_ci return err; 554462306a36Sopenharmony_ci} 554562306a36Sopenharmony_ci 554662306a36Sopenharmony_cistatic void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa) 554762306a36Sopenharmony_ci{ 554862306a36Sopenharmony_ci struct sk_buff *skb; 554962306a36Sopenharmony_ci struct net *net = dev_net(ifa->idev->dev); 555062306a36Sopenharmony_ci struct inet6_fill_args fillargs = { 555162306a36Sopenharmony_ci .portid = 0, 555262306a36Sopenharmony_ci .seq = 0, 555362306a36Sopenharmony_ci .event = event, 555462306a36Sopenharmony_ci .flags = 0, 555562306a36Sopenharmony_ci .netnsid = -1, 555662306a36Sopenharmony_ci }; 555762306a36Sopenharmony_ci int err = -ENOBUFS; 555862306a36Sopenharmony_ci 555962306a36Sopenharmony_ci skb = nlmsg_new(inet6_ifaddr_msgsize(), GFP_ATOMIC); 556062306a36Sopenharmony_ci if (!skb) 556162306a36Sopenharmony_ci goto errout; 556262306a36Sopenharmony_ci 556362306a36Sopenharmony_ci err = inet6_fill_ifaddr(skb, ifa, &fillargs); 556462306a36Sopenharmony_ci if (err < 0) { 556562306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */ 556662306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 556762306a36Sopenharmony_ci kfree_skb(skb); 556862306a36Sopenharmony_ci goto errout; 556962306a36Sopenharmony_ci } 557062306a36Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC); 557162306a36Sopenharmony_ci return; 557262306a36Sopenharmony_cierrout: 557362306a36Sopenharmony_ci if (err < 0) 557462306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_IFADDR, err); 557562306a36Sopenharmony_ci} 557662306a36Sopenharmony_ci 557762306a36Sopenharmony_cistatic inline void ipv6_store_devconf(struct ipv6_devconf *cnf, 557862306a36Sopenharmony_ci __s32 *array, int bytes) 557962306a36Sopenharmony_ci{ 558062306a36Sopenharmony_ci BUG_ON(bytes < (DEVCONF_MAX * 4)); 558162306a36Sopenharmony_ci 558262306a36Sopenharmony_ci memset(array, 0, bytes); 558362306a36Sopenharmony_ci array[DEVCONF_FORWARDING] = cnf->forwarding; 558462306a36Sopenharmony_ci array[DEVCONF_HOPLIMIT] = cnf->hop_limit; 558562306a36Sopenharmony_ci array[DEVCONF_MTU6] = cnf->mtu6; 558662306a36Sopenharmony_ci array[DEVCONF_ACCEPT_RA] = cnf->accept_ra; 558762306a36Sopenharmony_ci array[DEVCONF_ACCEPT_REDIRECTS] = cnf->accept_redirects; 558862306a36Sopenharmony_ci array[DEVCONF_AUTOCONF] = cnf->autoconf; 558962306a36Sopenharmony_ci array[DEVCONF_DAD_TRANSMITS] = cnf->dad_transmits; 559062306a36Sopenharmony_ci array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits; 559162306a36Sopenharmony_ci array[DEVCONF_RTR_SOLICIT_INTERVAL] = 559262306a36Sopenharmony_ci jiffies_to_msecs(cnf->rtr_solicit_interval); 559362306a36Sopenharmony_ci array[DEVCONF_RTR_SOLICIT_MAX_INTERVAL] = 559462306a36Sopenharmony_ci jiffies_to_msecs(cnf->rtr_solicit_max_interval); 559562306a36Sopenharmony_ci array[DEVCONF_RTR_SOLICIT_DELAY] = 559662306a36Sopenharmony_ci jiffies_to_msecs(cnf->rtr_solicit_delay); 559762306a36Sopenharmony_ci array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version; 559862306a36Sopenharmony_ci array[DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL] = 559962306a36Sopenharmony_ci jiffies_to_msecs(cnf->mldv1_unsolicited_report_interval); 560062306a36Sopenharmony_ci array[DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL] = 560162306a36Sopenharmony_ci jiffies_to_msecs(cnf->mldv2_unsolicited_report_interval); 560262306a36Sopenharmony_ci array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr; 560362306a36Sopenharmony_ci array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft; 560462306a36Sopenharmony_ci array[DEVCONF_TEMP_PREFERED_LFT] = cnf->temp_prefered_lft; 560562306a36Sopenharmony_ci array[DEVCONF_REGEN_MAX_RETRY] = cnf->regen_max_retry; 560662306a36Sopenharmony_ci array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor; 560762306a36Sopenharmony_ci array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses; 560862306a36Sopenharmony_ci array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr; 560962306a36Sopenharmony_ci array[DEVCONF_RA_DEFRTR_METRIC] = cnf->ra_defrtr_metric; 561062306a36Sopenharmony_ci array[DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT] = cnf->accept_ra_min_hop_limit; 561162306a36Sopenharmony_ci array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo; 561262306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 561362306a36Sopenharmony_ci array[DEVCONF_ACCEPT_RA_RTR_PREF] = cnf->accept_ra_rtr_pref; 561462306a36Sopenharmony_ci array[DEVCONF_RTR_PROBE_INTERVAL] = 561562306a36Sopenharmony_ci jiffies_to_msecs(cnf->rtr_probe_interval); 561662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 561762306a36Sopenharmony_ci array[DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN] = cnf->accept_ra_rt_info_min_plen; 561862306a36Sopenharmony_ci array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen; 561962306a36Sopenharmony_ci#endif 562062306a36Sopenharmony_ci#endif 562162306a36Sopenharmony_ci array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp; 562262306a36Sopenharmony_ci array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route; 562362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 562462306a36Sopenharmony_ci array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad; 562562306a36Sopenharmony_ci array[DEVCONF_USE_OPTIMISTIC] = cnf->use_optimistic; 562662306a36Sopenharmony_ci#endif 562762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 562862306a36Sopenharmony_ci array[DEVCONF_MC_FORWARDING] = atomic_read(&cnf->mc_forwarding); 562962306a36Sopenharmony_ci#endif 563062306a36Sopenharmony_ci array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6; 563162306a36Sopenharmony_ci array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad; 563262306a36Sopenharmony_ci array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao; 563362306a36Sopenharmony_ci array[DEVCONF_NDISC_NOTIFY] = cnf->ndisc_notify; 563462306a36Sopenharmony_ci array[DEVCONF_SUPPRESS_FRAG_NDISC] = cnf->suppress_frag_ndisc; 563562306a36Sopenharmony_ci array[DEVCONF_ACCEPT_RA_FROM_LOCAL] = cnf->accept_ra_from_local; 563662306a36Sopenharmony_ci array[DEVCONF_ACCEPT_RA_MTU] = cnf->accept_ra_mtu; 563762306a36Sopenharmony_ci array[DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN] = cnf->ignore_routes_with_linkdown; 563862306a36Sopenharmony_ci /* we omit DEVCONF_STABLE_SECRET for now */ 563962306a36Sopenharmony_ci array[DEVCONF_USE_OIF_ADDRS_ONLY] = cnf->use_oif_addrs_only; 564062306a36Sopenharmony_ci array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = cnf->drop_unicast_in_l2_multicast; 564162306a36Sopenharmony_ci array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na; 564262306a36Sopenharmony_ci array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down; 564362306a36Sopenharmony_ci array[DEVCONF_SEG6_ENABLED] = cnf->seg6_enabled; 564462306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 564562306a36Sopenharmony_ci array[DEVCONF_SEG6_REQUIRE_HMAC] = cnf->seg6_require_hmac; 564662306a36Sopenharmony_ci#endif 564762306a36Sopenharmony_ci array[DEVCONF_ENHANCED_DAD] = cnf->enhanced_dad; 564862306a36Sopenharmony_ci array[DEVCONF_ADDR_GEN_MODE] = cnf->addr_gen_mode; 564962306a36Sopenharmony_ci array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy; 565062306a36Sopenharmony_ci array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass; 565162306a36Sopenharmony_ci array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled; 565262306a36Sopenharmony_ci array[DEVCONF_IOAM6_ENABLED] = cnf->ioam6_enabled; 565362306a36Sopenharmony_ci array[DEVCONF_IOAM6_ID] = cnf->ioam6_id; 565462306a36Sopenharmony_ci array[DEVCONF_IOAM6_ID_WIDE] = cnf->ioam6_id_wide; 565562306a36Sopenharmony_ci array[DEVCONF_NDISC_EVICT_NOCARRIER] = cnf->ndisc_evict_nocarrier; 565662306a36Sopenharmony_ci array[DEVCONF_ACCEPT_UNTRACKED_NA] = cnf->accept_untracked_na; 565762306a36Sopenharmony_ci array[DEVCONF_ACCEPT_RA_MIN_LFT] = cnf->accept_ra_min_lft; 565862306a36Sopenharmony_ci} 565962306a36Sopenharmony_ci 566062306a36Sopenharmony_cistatic inline size_t inet6_ifla6_size(void) 566162306a36Sopenharmony_ci{ 566262306a36Sopenharmony_ci return nla_total_size(4) /* IFLA_INET6_FLAGS */ 566362306a36Sopenharmony_ci + nla_total_size(sizeof(struct ifla_cacheinfo)) 566462306a36Sopenharmony_ci + nla_total_size(DEVCONF_MAX * 4) /* IFLA_INET6_CONF */ 566562306a36Sopenharmony_ci + nla_total_size(IPSTATS_MIB_MAX * 8) /* IFLA_INET6_STATS */ 566662306a36Sopenharmony_ci + nla_total_size(ICMP6_MIB_MAX * 8) /* IFLA_INET6_ICMP6STATS */ 566762306a36Sopenharmony_ci + nla_total_size(sizeof(struct in6_addr)) /* IFLA_INET6_TOKEN */ 566862306a36Sopenharmony_ci + nla_total_size(1) /* IFLA_INET6_ADDR_GEN_MODE */ 566962306a36Sopenharmony_ci + nla_total_size(4) /* IFLA_INET6_RA_MTU */ 567062306a36Sopenharmony_ci + 0; 567162306a36Sopenharmony_ci} 567262306a36Sopenharmony_ci 567362306a36Sopenharmony_cistatic inline size_t inet6_if_nlmsg_size(void) 567462306a36Sopenharmony_ci{ 567562306a36Sopenharmony_ci return NLMSG_ALIGN(sizeof(struct ifinfomsg)) 567662306a36Sopenharmony_ci + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ 567762306a36Sopenharmony_ci + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ 567862306a36Sopenharmony_ci + nla_total_size(4) /* IFLA_MTU */ 567962306a36Sopenharmony_ci + nla_total_size(4) /* IFLA_LINK */ 568062306a36Sopenharmony_ci + nla_total_size(1) /* IFLA_OPERSTATE */ 568162306a36Sopenharmony_ci + nla_total_size(inet6_ifla6_size()); /* IFLA_PROTINFO */ 568262306a36Sopenharmony_ci} 568362306a36Sopenharmony_ci 568462306a36Sopenharmony_cistatic inline void __snmp6_fill_statsdev(u64 *stats, atomic_long_t *mib, 568562306a36Sopenharmony_ci int bytes) 568662306a36Sopenharmony_ci{ 568762306a36Sopenharmony_ci int i; 568862306a36Sopenharmony_ci int pad = bytes - sizeof(u64) * ICMP6_MIB_MAX; 568962306a36Sopenharmony_ci BUG_ON(pad < 0); 569062306a36Sopenharmony_ci 569162306a36Sopenharmony_ci /* Use put_unaligned() because stats may not be aligned for u64. */ 569262306a36Sopenharmony_ci put_unaligned(ICMP6_MIB_MAX, &stats[0]); 569362306a36Sopenharmony_ci for (i = 1; i < ICMP6_MIB_MAX; i++) 569462306a36Sopenharmony_ci put_unaligned(atomic_long_read(&mib[i]), &stats[i]); 569562306a36Sopenharmony_ci 569662306a36Sopenharmony_ci memset(&stats[ICMP6_MIB_MAX], 0, pad); 569762306a36Sopenharmony_ci} 569862306a36Sopenharmony_ci 569962306a36Sopenharmony_cistatic inline void __snmp6_fill_stats64(u64 *stats, void __percpu *mib, 570062306a36Sopenharmony_ci int bytes, size_t syncpoff) 570162306a36Sopenharmony_ci{ 570262306a36Sopenharmony_ci int i, c; 570362306a36Sopenharmony_ci u64 buff[IPSTATS_MIB_MAX]; 570462306a36Sopenharmony_ci int pad = bytes - sizeof(u64) * IPSTATS_MIB_MAX; 570562306a36Sopenharmony_ci 570662306a36Sopenharmony_ci BUG_ON(pad < 0); 570762306a36Sopenharmony_ci 570862306a36Sopenharmony_ci memset(buff, 0, sizeof(buff)); 570962306a36Sopenharmony_ci buff[0] = IPSTATS_MIB_MAX; 571062306a36Sopenharmony_ci 571162306a36Sopenharmony_ci for_each_possible_cpu(c) { 571262306a36Sopenharmony_ci for (i = 1; i < IPSTATS_MIB_MAX; i++) 571362306a36Sopenharmony_ci buff[i] += snmp_get_cpu_field64(mib, c, i, syncpoff); 571462306a36Sopenharmony_ci } 571562306a36Sopenharmony_ci 571662306a36Sopenharmony_ci memcpy(stats, buff, IPSTATS_MIB_MAX * sizeof(u64)); 571762306a36Sopenharmony_ci memset(&stats[IPSTATS_MIB_MAX], 0, pad); 571862306a36Sopenharmony_ci} 571962306a36Sopenharmony_ci 572062306a36Sopenharmony_cistatic void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype, 572162306a36Sopenharmony_ci int bytes) 572262306a36Sopenharmony_ci{ 572362306a36Sopenharmony_ci switch (attrtype) { 572462306a36Sopenharmony_ci case IFLA_INET6_STATS: 572562306a36Sopenharmony_ci __snmp6_fill_stats64(stats, idev->stats.ipv6, bytes, 572662306a36Sopenharmony_ci offsetof(struct ipstats_mib, syncp)); 572762306a36Sopenharmony_ci break; 572862306a36Sopenharmony_ci case IFLA_INET6_ICMP6STATS: 572962306a36Sopenharmony_ci __snmp6_fill_statsdev(stats, idev->stats.icmpv6dev->mibs, bytes); 573062306a36Sopenharmony_ci break; 573162306a36Sopenharmony_ci } 573262306a36Sopenharmony_ci} 573362306a36Sopenharmony_ci 573462306a36Sopenharmony_cistatic int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev, 573562306a36Sopenharmony_ci u32 ext_filter_mask) 573662306a36Sopenharmony_ci{ 573762306a36Sopenharmony_ci struct nlattr *nla; 573862306a36Sopenharmony_ci struct ifla_cacheinfo ci; 573962306a36Sopenharmony_ci 574062306a36Sopenharmony_ci if (nla_put_u32(skb, IFLA_INET6_FLAGS, idev->if_flags)) 574162306a36Sopenharmony_ci goto nla_put_failure; 574262306a36Sopenharmony_ci ci.max_reasm_len = IPV6_MAXPLEN; 574362306a36Sopenharmony_ci ci.tstamp = cstamp_delta(idev->tstamp); 574462306a36Sopenharmony_ci ci.reachable_time = jiffies_to_msecs(idev->nd_parms->reachable_time); 574562306a36Sopenharmony_ci ci.retrans_time = jiffies_to_msecs(NEIGH_VAR(idev->nd_parms, RETRANS_TIME)); 574662306a36Sopenharmony_ci if (nla_put(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci)) 574762306a36Sopenharmony_ci goto nla_put_failure; 574862306a36Sopenharmony_ci nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32)); 574962306a36Sopenharmony_ci if (!nla) 575062306a36Sopenharmony_ci goto nla_put_failure; 575162306a36Sopenharmony_ci ipv6_store_devconf(&idev->cnf, nla_data(nla), nla_len(nla)); 575262306a36Sopenharmony_ci 575362306a36Sopenharmony_ci /* XXX - MC not implemented */ 575462306a36Sopenharmony_ci 575562306a36Sopenharmony_ci if (ext_filter_mask & RTEXT_FILTER_SKIP_STATS) 575662306a36Sopenharmony_ci return 0; 575762306a36Sopenharmony_ci 575862306a36Sopenharmony_ci nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64)); 575962306a36Sopenharmony_ci if (!nla) 576062306a36Sopenharmony_ci goto nla_put_failure; 576162306a36Sopenharmony_ci snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_STATS, nla_len(nla)); 576262306a36Sopenharmony_ci 576362306a36Sopenharmony_ci nla = nla_reserve(skb, IFLA_INET6_ICMP6STATS, ICMP6_MIB_MAX * sizeof(u64)); 576462306a36Sopenharmony_ci if (!nla) 576562306a36Sopenharmony_ci goto nla_put_failure; 576662306a36Sopenharmony_ci snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla)); 576762306a36Sopenharmony_ci 576862306a36Sopenharmony_ci nla = nla_reserve(skb, IFLA_INET6_TOKEN, sizeof(struct in6_addr)); 576962306a36Sopenharmony_ci if (!nla) 577062306a36Sopenharmony_ci goto nla_put_failure; 577162306a36Sopenharmony_ci read_lock_bh(&idev->lock); 577262306a36Sopenharmony_ci memcpy(nla_data(nla), idev->token.s6_addr, nla_len(nla)); 577362306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 577462306a36Sopenharmony_ci 577562306a36Sopenharmony_ci if (nla_put_u8(skb, IFLA_INET6_ADDR_GEN_MODE, idev->cnf.addr_gen_mode)) 577662306a36Sopenharmony_ci goto nla_put_failure; 577762306a36Sopenharmony_ci 577862306a36Sopenharmony_ci if (idev->ra_mtu && 577962306a36Sopenharmony_ci nla_put_u32(skb, IFLA_INET6_RA_MTU, idev->ra_mtu)) 578062306a36Sopenharmony_ci goto nla_put_failure; 578162306a36Sopenharmony_ci 578262306a36Sopenharmony_ci return 0; 578362306a36Sopenharmony_ci 578462306a36Sopenharmony_cinla_put_failure: 578562306a36Sopenharmony_ci return -EMSGSIZE; 578662306a36Sopenharmony_ci} 578762306a36Sopenharmony_ci 578862306a36Sopenharmony_cistatic size_t inet6_get_link_af_size(const struct net_device *dev, 578962306a36Sopenharmony_ci u32 ext_filter_mask) 579062306a36Sopenharmony_ci{ 579162306a36Sopenharmony_ci if (!__in6_dev_get(dev)) 579262306a36Sopenharmony_ci return 0; 579362306a36Sopenharmony_ci 579462306a36Sopenharmony_ci return inet6_ifla6_size(); 579562306a36Sopenharmony_ci} 579662306a36Sopenharmony_ci 579762306a36Sopenharmony_cistatic int inet6_fill_link_af(struct sk_buff *skb, const struct net_device *dev, 579862306a36Sopenharmony_ci u32 ext_filter_mask) 579962306a36Sopenharmony_ci{ 580062306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 580162306a36Sopenharmony_ci 580262306a36Sopenharmony_ci if (!idev) 580362306a36Sopenharmony_ci return -ENODATA; 580462306a36Sopenharmony_ci 580562306a36Sopenharmony_ci if (inet6_fill_ifla6_attrs(skb, idev, ext_filter_mask) < 0) 580662306a36Sopenharmony_ci return -EMSGSIZE; 580762306a36Sopenharmony_ci 580862306a36Sopenharmony_ci return 0; 580962306a36Sopenharmony_ci} 581062306a36Sopenharmony_ci 581162306a36Sopenharmony_cistatic int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token, 581262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 581362306a36Sopenharmony_ci{ 581462306a36Sopenharmony_ci struct inet6_ifaddr *ifp; 581562306a36Sopenharmony_ci struct net_device *dev = idev->dev; 581662306a36Sopenharmony_ci bool clear_token, update_rs = false; 581762306a36Sopenharmony_ci struct in6_addr ll_addr; 581862306a36Sopenharmony_ci 581962306a36Sopenharmony_ci ASSERT_RTNL(); 582062306a36Sopenharmony_ci 582162306a36Sopenharmony_ci if (!token) 582262306a36Sopenharmony_ci return -EINVAL; 582362306a36Sopenharmony_ci 582462306a36Sopenharmony_ci if (dev->flags & IFF_LOOPBACK) { 582562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Device is loopback"); 582662306a36Sopenharmony_ci return -EINVAL; 582762306a36Sopenharmony_ci } 582862306a36Sopenharmony_ci 582962306a36Sopenharmony_ci if (dev->flags & IFF_NOARP) { 583062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 583162306a36Sopenharmony_ci "Device does not do neighbour discovery"); 583262306a36Sopenharmony_ci return -EINVAL; 583362306a36Sopenharmony_ci } 583462306a36Sopenharmony_ci 583562306a36Sopenharmony_ci if (!ipv6_accept_ra(idev)) { 583662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 583762306a36Sopenharmony_ci "Router advertisement is disabled on device"); 583862306a36Sopenharmony_ci return -EINVAL; 583962306a36Sopenharmony_ci } 584062306a36Sopenharmony_ci 584162306a36Sopenharmony_ci if (idev->cnf.rtr_solicits == 0) { 584262306a36Sopenharmony_ci NL_SET_ERR_MSG(extack, 584362306a36Sopenharmony_ci "Router solicitation is disabled on device"); 584462306a36Sopenharmony_ci return -EINVAL; 584562306a36Sopenharmony_ci } 584662306a36Sopenharmony_ci 584762306a36Sopenharmony_ci write_lock_bh(&idev->lock); 584862306a36Sopenharmony_ci 584962306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(token->s6_addr) != 16); 585062306a36Sopenharmony_ci memcpy(idev->token.s6_addr + 8, token->s6_addr + 8, 8); 585162306a36Sopenharmony_ci 585262306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 585362306a36Sopenharmony_ci 585462306a36Sopenharmony_ci clear_token = ipv6_addr_any(token); 585562306a36Sopenharmony_ci if (clear_token) 585662306a36Sopenharmony_ci goto update_lft; 585762306a36Sopenharmony_ci 585862306a36Sopenharmony_ci if (!idev->dead && (idev->if_flags & IF_READY) && 585962306a36Sopenharmony_ci !ipv6_get_lladdr(dev, &ll_addr, IFA_F_TENTATIVE | 586062306a36Sopenharmony_ci IFA_F_OPTIMISTIC)) { 586162306a36Sopenharmony_ci /* If we're not ready, then normal ifup will take care 586262306a36Sopenharmony_ci * of this. Otherwise, we need to request our rs here. 586362306a36Sopenharmony_ci */ 586462306a36Sopenharmony_ci ndisc_send_rs(dev, &ll_addr, &in6addr_linklocal_allrouters); 586562306a36Sopenharmony_ci update_rs = true; 586662306a36Sopenharmony_ci } 586762306a36Sopenharmony_ci 586862306a36Sopenharmony_ciupdate_lft: 586962306a36Sopenharmony_ci write_lock_bh(&idev->lock); 587062306a36Sopenharmony_ci 587162306a36Sopenharmony_ci if (update_rs) { 587262306a36Sopenharmony_ci idev->if_flags |= IF_RS_SENT; 587362306a36Sopenharmony_ci idev->rs_interval = rfc3315_s14_backoff_init( 587462306a36Sopenharmony_ci idev->cnf.rtr_solicit_interval); 587562306a36Sopenharmony_ci idev->rs_probes = 1; 587662306a36Sopenharmony_ci addrconf_mod_rs_timer(idev, idev->rs_interval); 587762306a36Sopenharmony_ci } 587862306a36Sopenharmony_ci 587962306a36Sopenharmony_ci /* Well, that's kinda nasty ... */ 588062306a36Sopenharmony_ci list_for_each_entry(ifp, &idev->addr_list, if_list) { 588162306a36Sopenharmony_ci spin_lock(&ifp->lock); 588262306a36Sopenharmony_ci if (ifp->tokenized) { 588362306a36Sopenharmony_ci ifp->valid_lft = 0; 588462306a36Sopenharmony_ci ifp->prefered_lft = 0; 588562306a36Sopenharmony_ci } 588662306a36Sopenharmony_ci spin_unlock(&ifp->lock); 588762306a36Sopenharmony_ci } 588862306a36Sopenharmony_ci 588962306a36Sopenharmony_ci write_unlock_bh(&idev->lock); 589062306a36Sopenharmony_ci inet6_ifinfo_notify(RTM_NEWLINK, idev); 589162306a36Sopenharmony_ci addrconf_verify_rtnl(dev_net(dev)); 589262306a36Sopenharmony_ci return 0; 589362306a36Sopenharmony_ci} 589462306a36Sopenharmony_ci 589562306a36Sopenharmony_cistatic const struct nla_policy inet6_af_policy[IFLA_INET6_MAX + 1] = { 589662306a36Sopenharmony_ci [IFLA_INET6_ADDR_GEN_MODE] = { .type = NLA_U8 }, 589762306a36Sopenharmony_ci [IFLA_INET6_TOKEN] = { .len = sizeof(struct in6_addr) }, 589862306a36Sopenharmony_ci [IFLA_INET6_RA_MTU] = { .type = NLA_REJECT, 589962306a36Sopenharmony_ci .reject_message = 590062306a36Sopenharmony_ci "IFLA_INET6_RA_MTU can not be set" }, 590162306a36Sopenharmony_ci}; 590262306a36Sopenharmony_ci 590362306a36Sopenharmony_cistatic int check_addr_gen_mode(int mode) 590462306a36Sopenharmony_ci{ 590562306a36Sopenharmony_ci if (mode != IN6_ADDR_GEN_MODE_EUI64 && 590662306a36Sopenharmony_ci mode != IN6_ADDR_GEN_MODE_NONE && 590762306a36Sopenharmony_ci mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY && 590862306a36Sopenharmony_ci mode != IN6_ADDR_GEN_MODE_RANDOM) 590962306a36Sopenharmony_ci return -EINVAL; 591062306a36Sopenharmony_ci return 1; 591162306a36Sopenharmony_ci} 591262306a36Sopenharmony_ci 591362306a36Sopenharmony_cistatic int check_stable_privacy(struct inet6_dev *idev, struct net *net, 591462306a36Sopenharmony_ci int mode) 591562306a36Sopenharmony_ci{ 591662306a36Sopenharmony_ci if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY && 591762306a36Sopenharmony_ci !idev->cnf.stable_secret.initialized && 591862306a36Sopenharmony_ci !net->ipv6.devconf_dflt->stable_secret.initialized) 591962306a36Sopenharmony_ci return -EINVAL; 592062306a36Sopenharmony_ci return 1; 592162306a36Sopenharmony_ci} 592262306a36Sopenharmony_ci 592362306a36Sopenharmony_cistatic int inet6_validate_link_af(const struct net_device *dev, 592462306a36Sopenharmony_ci const struct nlattr *nla, 592562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 592662306a36Sopenharmony_ci{ 592762306a36Sopenharmony_ci struct nlattr *tb[IFLA_INET6_MAX + 1]; 592862306a36Sopenharmony_ci struct inet6_dev *idev = NULL; 592962306a36Sopenharmony_ci int err; 593062306a36Sopenharmony_ci 593162306a36Sopenharmony_ci if (dev) { 593262306a36Sopenharmony_ci idev = __in6_dev_get(dev); 593362306a36Sopenharmony_ci if (!idev) 593462306a36Sopenharmony_ci return -EAFNOSUPPORT; 593562306a36Sopenharmony_ci } 593662306a36Sopenharmony_ci 593762306a36Sopenharmony_ci err = nla_parse_nested_deprecated(tb, IFLA_INET6_MAX, nla, 593862306a36Sopenharmony_ci inet6_af_policy, extack); 593962306a36Sopenharmony_ci if (err) 594062306a36Sopenharmony_ci return err; 594162306a36Sopenharmony_ci 594262306a36Sopenharmony_ci if (!tb[IFLA_INET6_TOKEN] && !tb[IFLA_INET6_ADDR_GEN_MODE]) 594362306a36Sopenharmony_ci return -EINVAL; 594462306a36Sopenharmony_ci 594562306a36Sopenharmony_ci if (tb[IFLA_INET6_ADDR_GEN_MODE]) { 594662306a36Sopenharmony_ci u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]); 594762306a36Sopenharmony_ci 594862306a36Sopenharmony_ci if (check_addr_gen_mode(mode) < 0) 594962306a36Sopenharmony_ci return -EINVAL; 595062306a36Sopenharmony_ci if (dev && check_stable_privacy(idev, dev_net(dev), mode) < 0) 595162306a36Sopenharmony_ci return -EINVAL; 595262306a36Sopenharmony_ci } 595362306a36Sopenharmony_ci 595462306a36Sopenharmony_ci return 0; 595562306a36Sopenharmony_ci} 595662306a36Sopenharmony_ci 595762306a36Sopenharmony_cistatic int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla, 595862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 595962306a36Sopenharmony_ci{ 596062306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 596162306a36Sopenharmony_ci struct nlattr *tb[IFLA_INET6_MAX + 1]; 596262306a36Sopenharmony_ci int err; 596362306a36Sopenharmony_ci 596462306a36Sopenharmony_ci if (!idev) 596562306a36Sopenharmony_ci return -EAFNOSUPPORT; 596662306a36Sopenharmony_ci 596762306a36Sopenharmony_ci if (nla_parse_nested_deprecated(tb, IFLA_INET6_MAX, nla, NULL, NULL) < 0) 596862306a36Sopenharmony_ci return -EINVAL; 596962306a36Sopenharmony_ci 597062306a36Sopenharmony_ci if (tb[IFLA_INET6_TOKEN]) { 597162306a36Sopenharmony_ci err = inet6_set_iftoken(idev, nla_data(tb[IFLA_INET6_TOKEN]), 597262306a36Sopenharmony_ci extack); 597362306a36Sopenharmony_ci if (err) 597462306a36Sopenharmony_ci return err; 597562306a36Sopenharmony_ci } 597662306a36Sopenharmony_ci 597762306a36Sopenharmony_ci if (tb[IFLA_INET6_ADDR_GEN_MODE]) { 597862306a36Sopenharmony_ci u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]); 597962306a36Sopenharmony_ci 598062306a36Sopenharmony_ci idev->cnf.addr_gen_mode = mode; 598162306a36Sopenharmony_ci } 598262306a36Sopenharmony_ci 598362306a36Sopenharmony_ci return 0; 598462306a36Sopenharmony_ci} 598562306a36Sopenharmony_ci 598662306a36Sopenharmony_cistatic int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev, 598762306a36Sopenharmony_ci u32 portid, u32 seq, int event, unsigned int flags) 598862306a36Sopenharmony_ci{ 598962306a36Sopenharmony_ci struct net_device *dev = idev->dev; 599062306a36Sopenharmony_ci struct ifinfomsg *hdr; 599162306a36Sopenharmony_ci struct nlmsghdr *nlh; 599262306a36Sopenharmony_ci void *protoinfo; 599362306a36Sopenharmony_ci 599462306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(*hdr), flags); 599562306a36Sopenharmony_ci if (!nlh) 599662306a36Sopenharmony_ci return -EMSGSIZE; 599762306a36Sopenharmony_ci 599862306a36Sopenharmony_ci hdr = nlmsg_data(nlh); 599962306a36Sopenharmony_ci hdr->ifi_family = AF_INET6; 600062306a36Sopenharmony_ci hdr->__ifi_pad = 0; 600162306a36Sopenharmony_ci hdr->ifi_type = dev->type; 600262306a36Sopenharmony_ci hdr->ifi_index = dev->ifindex; 600362306a36Sopenharmony_ci hdr->ifi_flags = dev_get_flags(dev); 600462306a36Sopenharmony_ci hdr->ifi_change = 0; 600562306a36Sopenharmony_ci 600662306a36Sopenharmony_ci if (nla_put_string(skb, IFLA_IFNAME, dev->name) || 600762306a36Sopenharmony_ci (dev->addr_len && 600862306a36Sopenharmony_ci nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) || 600962306a36Sopenharmony_ci nla_put_u32(skb, IFLA_MTU, dev->mtu) || 601062306a36Sopenharmony_ci (dev->ifindex != dev_get_iflink(dev) && 601162306a36Sopenharmony_ci nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))) || 601262306a36Sopenharmony_ci nla_put_u8(skb, IFLA_OPERSTATE, 601362306a36Sopenharmony_ci netif_running(dev) ? dev->operstate : IF_OPER_DOWN)) 601462306a36Sopenharmony_ci goto nla_put_failure; 601562306a36Sopenharmony_ci protoinfo = nla_nest_start_noflag(skb, IFLA_PROTINFO); 601662306a36Sopenharmony_ci if (!protoinfo) 601762306a36Sopenharmony_ci goto nla_put_failure; 601862306a36Sopenharmony_ci 601962306a36Sopenharmony_ci if (inet6_fill_ifla6_attrs(skb, idev, 0) < 0) 602062306a36Sopenharmony_ci goto nla_put_failure; 602162306a36Sopenharmony_ci 602262306a36Sopenharmony_ci nla_nest_end(skb, protoinfo); 602362306a36Sopenharmony_ci nlmsg_end(skb, nlh); 602462306a36Sopenharmony_ci return 0; 602562306a36Sopenharmony_ci 602662306a36Sopenharmony_cinla_put_failure: 602762306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 602862306a36Sopenharmony_ci return -EMSGSIZE; 602962306a36Sopenharmony_ci} 603062306a36Sopenharmony_ci 603162306a36Sopenharmony_cistatic int inet6_valid_dump_ifinfo(const struct nlmsghdr *nlh, 603262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 603362306a36Sopenharmony_ci{ 603462306a36Sopenharmony_ci struct ifinfomsg *ifm; 603562306a36Sopenharmony_ci 603662306a36Sopenharmony_ci if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 603762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid header for link dump request"); 603862306a36Sopenharmony_ci return -EINVAL; 603962306a36Sopenharmony_ci } 604062306a36Sopenharmony_ci 604162306a36Sopenharmony_ci if (nlmsg_attrlen(nlh, sizeof(*ifm))) { 604262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid data after header"); 604362306a36Sopenharmony_ci return -EINVAL; 604462306a36Sopenharmony_ci } 604562306a36Sopenharmony_ci 604662306a36Sopenharmony_ci ifm = nlmsg_data(nlh); 604762306a36Sopenharmony_ci if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags || 604862306a36Sopenharmony_ci ifm->ifi_change || ifm->ifi_index) { 604962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for dump request"); 605062306a36Sopenharmony_ci return -EINVAL; 605162306a36Sopenharmony_ci } 605262306a36Sopenharmony_ci 605362306a36Sopenharmony_ci return 0; 605462306a36Sopenharmony_ci} 605562306a36Sopenharmony_ci 605662306a36Sopenharmony_cistatic int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) 605762306a36Sopenharmony_ci{ 605862306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 605962306a36Sopenharmony_ci int h, s_h; 606062306a36Sopenharmony_ci int idx = 0, s_idx; 606162306a36Sopenharmony_ci struct net_device *dev; 606262306a36Sopenharmony_ci struct inet6_dev *idev; 606362306a36Sopenharmony_ci struct hlist_head *head; 606462306a36Sopenharmony_ci 606562306a36Sopenharmony_ci /* only requests using strict checking can pass data to 606662306a36Sopenharmony_ci * influence the dump 606762306a36Sopenharmony_ci */ 606862306a36Sopenharmony_ci if (cb->strict_check) { 606962306a36Sopenharmony_ci int err = inet6_valid_dump_ifinfo(cb->nlh, cb->extack); 607062306a36Sopenharmony_ci 607162306a36Sopenharmony_ci if (err < 0) 607262306a36Sopenharmony_ci return err; 607362306a36Sopenharmony_ci } 607462306a36Sopenharmony_ci 607562306a36Sopenharmony_ci s_h = cb->args[0]; 607662306a36Sopenharmony_ci s_idx = cb->args[1]; 607762306a36Sopenharmony_ci 607862306a36Sopenharmony_ci rcu_read_lock(); 607962306a36Sopenharmony_ci for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 608062306a36Sopenharmony_ci idx = 0; 608162306a36Sopenharmony_ci head = &net->dev_index_head[h]; 608262306a36Sopenharmony_ci hlist_for_each_entry_rcu(dev, head, index_hlist) { 608362306a36Sopenharmony_ci if (idx < s_idx) 608462306a36Sopenharmony_ci goto cont; 608562306a36Sopenharmony_ci idev = __in6_dev_get(dev); 608662306a36Sopenharmony_ci if (!idev) 608762306a36Sopenharmony_ci goto cont; 608862306a36Sopenharmony_ci if (inet6_fill_ifinfo(skb, idev, 608962306a36Sopenharmony_ci NETLINK_CB(cb->skb).portid, 609062306a36Sopenharmony_ci cb->nlh->nlmsg_seq, 609162306a36Sopenharmony_ci RTM_NEWLINK, NLM_F_MULTI) < 0) 609262306a36Sopenharmony_ci goto out; 609362306a36Sopenharmony_cicont: 609462306a36Sopenharmony_ci idx++; 609562306a36Sopenharmony_ci } 609662306a36Sopenharmony_ci } 609762306a36Sopenharmony_ciout: 609862306a36Sopenharmony_ci rcu_read_unlock(); 609962306a36Sopenharmony_ci cb->args[1] = idx; 610062306a36Sopenharmony_ci cb->args[0] = h; 610162306a36Sopenharmony_ci 610262306a36Sopenharmony_ci return skb->len; 610362306a36Sopenharmony_ci} 610462306a36Sopenharmony_ci 610562306a36Sopenharmony_civoid inet6_ifinfo_notify(int event, struct inet6_dev *idev) 610662306a36Sopenharmony_ci{ 610762306a36Sopenharmony_ci struct sk_buff *skb; 610862306a36Sopenharmony_ci struct net *net = dev_net(idev->dev); 610962306a36Sopenharmony_ci int err = -ENOBUFS; 611062306a36Sopenharmony_ci 611162306a36Sopenharmony_ci skb = nlmsg_new(inet6_if_nlmsg_size(), GFP_ATOMIC); 611262306a36Sopenharmony_ci if (!skb) 611362306a36Sopenharmony_ci goto errout; 611462306a36Sopenharmony_ci 611562306a36Sopenharmony_ci err = inet6_fill_ifinfo(skb, idev, 0, 0, event, 0); 611662306a36Sopenharmony_ci if (err < 0) { 611762306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in inet6_if_nlmsg_size() */ 611862306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 611962306a36Sopenharmony_ci kfree_skb(skb); 612062306a36Sopenharmony_ci goto errout; 612162306a36Sopenharmony_ci } 612262306a36Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFINFO, NULL, GFP_ATOMIC); 612362306a36Sopenharmony_ci return; 612462306a36Sopenharmony_cierrout: 612562306a36Sopenharmony_ci if (err < 0) 612662306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_IFINFO, err); 612762306a36Sopenharmony_ci} 612862306a36Sopenharmony_ci 612962306a36Sopenharmony_cistatic inline size_t inet6_prefix_nlmsg_size(void) 613062306a36Sopenharmony_ci{ 613162306a36Sopenharmony_ci return NLMSG_ALIGN(sizeof(struct prefixmsg)) 613262306a36Sopenharmony_ci + nla_total_size(sizeof(struct in6_addr)) 613362306a36Sopenharmony_ci + nla_total_size(sizeof(struct prefix_cacheinfo)); 613462306a36Sopenharmony_ci} 613562306a36Sopenharmony_ci 613662306a36Sopenharmony_cistatic int inet6_fill_prefix(struct sk_buff *skb, struct inet6_dev *idev, 613762306a36Sopenharmony_ci struct prefix_info *pinfo, u32 portid, u32 seq, 613862306a36Sopenharmony_ci int event, unsigned int flags) 613962306a36Sopenharmony_ci{ 614062306a36Sopenharmony_ci struct prefixmsg *pmsg; 614162306a36Sopenharmony_ci struct nlmsghdr *nlh; 614262306a36Sopenharmony_ci struct prefix_cacheinfo ci; 614362306a36Sopenharmony_ci 614462306a36Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, event, sizeof(*pmsg), flags); 614562306a36Sopenharmony_ci if (!nlh) 614662306a36Sopenharmony_ci return -EMSGSIZE; 614762306a36Sopenharmony_ci 614862306a36Sopenharmony_ci pmsg = nlmsg_data(nlh); 614962306a36Sopenharmony_ci pmsg->prefix_family = AF_INET6; 615062306a36Sopenharmony_ci pmsg->prefix_pad1 = 0; 615162306a36Sopenharmony_ci pmsg->prefix_pad2 = 0; 615262306a36Sopenharmony_ci pmsg->prefix_ifindex = idev->dev->ifindex; 615362306a36Sopenharmony_ci pmsg->prefix_len = pinfo->prefix_len; 615462306a36Sopenharmony_ci pmsg->prefix_type = pinfo->type; 615562306a36Sopenharmony_ci pmsg->prefix_pad3 = 0; 615662306a36Sopenharmony_ci pmsg->prefix_flags = pinfo->flags; 615762306a36Sopenharmony_ci 615862306a36Sopenharmony_ci if (nla_put(skb, PREFIX_ADDRESS, sizeof(pinfo->prefix), &pinfo->prefix)) 615962306a36Sopenharmony_ci goto nla_put_failure; 616062306a36Sopenharmony_ci ci.preferred_time = ntohl(pinfo->prefered); 616162306a36Sopenharmony_ci ci.valid_time = ntohl(pinfo->valid); 616262306a36Sopenharmony_ci if (nla_put(skb, PREFIX_CACHEINFO, sizeof(ci), &ci)) 616362306a36Sopenharmony_ci goto nla_put_failure; 616462306a36Sopenharmony_ci nlmsg_end(skb, nlh); 616562306a36Sopenharmony_ci return 0; 616662306a36Sopenharmony_ci 616762306a36Sopenharmony_cinla_put_failure: 616862306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 616962306a36Sopenharmony_ci return -EMSGSIZE; 617062306a36Sopenharmony_ci} 617162306a36Sopenharmony_ci 617262306a36Sopenharmony_cistatic void inet6_prefix_notify(int event, struct inet6_dev *idev, 617362306a36Sopenharmony_ci struct prefix_info *pinfo) 617462306a36Sopenharmony_ci{ 617562306a36Sopenharmony_ci struct sk_buff *skb; 617662306a36Sopenharmony_ci struct net *net = dev_net(idev->dev); 617762306a36Sopenharmony_ci int err = -ENOBUFS; 617862306a36Sopenharmony_ci 617962306a36Sopenharmony_ci skb = nlmsg_new(inet6_prefix_nlmsg_size(), GFP_ATOMIC); 618062306a36Sopenharmony_ci if (!skb) 618162306a36Sopenharmony_ci goto errout; 618262306a36Sopenharmony_ci 618362306a36Sopenharmony_ci err = inet6_fill_prefix(skb, idev, pinfo, 0, 0, event, 0); 618462306a36Sopenharmony_ci if (err < 0) { 618562306a36Sopenharmony_ci /* -EMSGSIZE implies BUG in inet6_prefix_nlmsg_size() */ 618662306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 618762306a36Sopenharmony_ci kfree_skb(skb); 618862306a36Sopenharmony_ci goto errout; 618962306a36Sopenharmony_ci } 619062306a36Sopenharmony_ci rtnl_notify(skb, net, 0, RTNLGRP_IPV6_PREFIX, NULL, GFP_ATOMIC); 619162306a36Sopenharmony_ci return; 619262306a36Sopenharmony_cierrout: 619362306a36Sopenharmony_ci if (err < 0) 619462306a36Sopenharmony_ci rtnl_set_sk_err(net, RTNLGRP_IPV6_PREFIX, err); 619562306a36Sopenharmony_ci} 619662306a36Sopenharmony_ci 619762306a36Sopenharmony_cistatic void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) 619862306a36Sopenharmony_ci{ 619962306a36Sopenharmony_ci struct net *net = dev_net(ifp->idev->dev); 620062306a36Sopenharmony_ci 620162306a36Sopenharmony_ci if (event) 620262306a36Sopenharmony_ci ASSERT_RTNL(); 620362306a36Sopenharmony_ci 620462306a36Sopenharmony_ci inet6_ifa_notify(event ? : RTM_NEWADDR, ifp); 620562306a36Sopenharmony_ci 620662306a36Sopenharmony_ci switch (event) { 620762306a36Sopenharmony_ci case RTM_NEWADDR: 620862306a36Sopenharmony_ci /* 620962306a36Sopenharmony_ci * If the address was optimistic we inserted the route at the 621062306a36Sopenharmony_ci * start of our DAD process, so we don't need to do it again. 621162306a36Sopenharmony_ci * If the device was taken down in the middle of the DAD 621262306a36Sopenharmony_ci * cycle there is a race where we could get here without a 621362306a36Sopenharmony_ci * host route, so nothing to insert. That will be fixed when 621462306a36Sopenharmony_ci * the device is brought up. 621562306a36Sopenharmony_ci */ 621662306a36Sopenharmony_ci if (ifp->rt && !rcu_access_pointer(ifp->rt->fib6_node)) { 621762306a36Sopenharmony_ci ip6_ins_rt(net, ifp->rt); 621862306a36Sopenharmony_ci } else if (!ifp->rt && (ifp->idev->dev->flags & IFF_UP)) { 621962306a36Sopenharmony_ci pr_warn("BUG: Address %pI6c on device %s is missing its host route.\n", 622062306a36Sopenharmony_ci &ifp->addr, ifp->idev->dev->name); 622162306a36Sopenharmony_ci } 622262306a36Sopenharmony_ci 622362306a36Sopenharmony_ci if (ifp->idev->cnf.forwarding) 622462306a36Sopenharmony_ci addrconf_join_anycast(ifp); 622562306a36Sopenharmony_ci if (!ipv6_addr_any(&ifp->peer_addr)) 622662306a36Sopenharmony_ci addrconf_prefix_route(&ifp->peer_addr, 128, 622762306a36Sopenharmony_ci ifp->rt_priority, ifp->idev->dev, 622862306a36Sopenharmony_ci 0, 0, GFP_ATOMIC); 622962306a36Sopenharmony_ci break; 623062306a36Sopenharmony_ci case RTM_DELADDR: 623162306a36Sopenharmony_ci if (ifp->idev->cnf.forwarding) 623262306a36Sopenharmony_ci addrconf_leave_anycast(ifp); 623362306a36Sopenharmony_ci addrconf_leave_solict(ifp->idev, &ifp->addr); 623462306a36Sopenharmony_ci if (!ipv6_addr_any(&ifp->peer_addr)) { 623562306a36Sopenharmony_ci struct fib6_info *rt; 623662306a36Sopenharmony_ci 623762306a36Sopenharmony_ci rt = addrconf_get_prefix_route(&ifp->peer_addr, 128, 623862306a36Sopenharmony_ci ifp->idev->dev, 0, 0, 623962306a36Sopenharmony_ci false); 624062306a36Sopenharmony_ci if (rt) 624162306a36Sopenharmony_ci ip6_del_rt(net, rt, false); 624262306a36Sopenharmony_ci } 624362306a36Sopenharmony_ci if (ifp->rt) { 624462306a36Sopenharmony_ci ip6_del_rt(net, ifp->rt, false); 624562306a36Sopenharmony_ci ifp->rt = NULL; 624662306a36Sopenharmony_ci } 624762306a36Sopenharmony_ci rt_genid_bump_ipv6(net); 624862306a36Sopenharmony_ci break; 624962306a36Sopenharmony_ci } 625062306a36Sopenharmony_ci atomic_inc(&net->ipv6.dev_addr_genid); 625162306a36Sopenharmony_ci} 625262306a36Sopenharmony_ci 625362306a36Sopenharmony_cistatic void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) 625462306a36Sopenharmony_ci{ 625562306a36Sopenharmony_ci if (likely(ifp->idev->dead == 0)) 625662306a36Sopenharmony_ci __ipv6_ifa_notify(event, ifp); 625762306a36Sopenharmony_ci} 625862306a36Sopenharmony_ci 625962306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 626062306a36Sopenharmony_ci 626162306a36Sopenharmony_cistatic int addrconf_sysctl_forward(struct ctl_table *ctl, int write, 626262306a36Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 626362306a36Sopenharmony_ci{ 626462306a36Sopenharmony_ci int *valp = ctl->data; 626562306a36Sopenharmony_ci int val = *valp; 626662306a36Sopenharmony_ci loff_t pos = *ppos; 626762306a36Sopenharmony_ci struct ctl_table lctl; 626862306a36Sopenharmony_ci int ret; 626962306a36Sopenharmony_ci 627062306a36Sopenharmony_ci /* 627162306a36Sopenharmony_ci * ctl->data points to idev->cnf.forwarding, we should 627262306a36Sopenharmony_ci * not modify it until we get the rtnl lock. 627362306a36Sopenharmony_ci */ 627462306a36Sopenharmony_ci lctl = *ctl; 627562306a36Sopenharmony_ci lctl.data = &val; 627662306a36Sopenharmony_ci 627762306a36Sopenharmony_ci ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); 627862306a36Sopenharmony_ci 627962306a36Sopenharmony_ci if (write) 628062306a36Sopenharmony_ci ret = addrconf_fixup_forwarding(ctl, valp, val); 628162306a36Sopenharmony_ci if (ret) 628262306a36Sopenharmony_ci *ppos = pos; 628362306a36Sopenharmony_ci return ret; 628462306a36Sopenharmony_ci} 628562306a36Sopenharmony_ci 628662306a36Sopenharmony_cistatic int addrconf_sysctl_mtu(struct ctl_table *ctl, int write, 628762306a36Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 628862306a36Sopenharmony_ci{ 628962306a36Sopenharmony_ci struct inet6_dev *idev = ctl->extra1; 629062306a36Sopenharmony_ci int min_mtu = IPV6_MIN_MTU; 629162306a36Sopenharmony_ci struct ctl_table lctl; 629262306a36Sopenharmony_ci 629362306a36Sopenharmony_ci lctl = *ctl; 629462306a36Sopenharmony_ci lctl.extra1 = &min_mtu; 629562306a36Sopenharmony_ci lctl.extra2 = idev ? &idev->dev->mtu : NULL; 629662306a36Sopenharmony_ci 629762306a36Sopenharmony_ci return proc_dointvec_minmax(&lctl, write, buffer, lenp, ppos); 629862306a36Sopenharmony_ci} 629962306a36Sopenharmony_ci 630062306a36Sopenharmony_cistatic void dev_disable_change(struct inet6_dev *idev) 630162306a36Sopenharmony_ci{ 630262306a36Sopenharmony_ci struct netdev_notifier_info info; 630362306a36Sopenharmony_ci 630462306a36Sopenharmony_ci if (!idev || !idev->dev) 630562306a36Sopenharmony_ci return; 630662306a36Sopenharmony_ci 630762306a36Sopenharmony_ci netdev_notifier_info_init(&info, idev->dev); 630862306a36Sopenharmony_ci if (idev->cnf.disable_ipv6) 630962306a36Sopenharmony_ci addrconf_notify(NULL, NETDEV_DOWN, &info); 631062306a36Sopenharmony_ci else 631162306a36Sopenharmony_ci addrconf_notify(NULL, NETDEV_UP, &info); 631262306a36Sopenharmony_ci} 631362306a36Sopenharmony_ci 631462306a36Sopenharmony_cistatic void addrconf_disable_change(struct net *net, __s32 newf) 631562306a36Sopenharmony_ci{ 631662306a36Sopenharmony_ci struct net_device *dev; 631762306a36Sopenharmony_ci struct inet6_dev *idev; 631862306a36Sopenharmony_ci 631962306a36Sopenharmony_ci for_each_netdev(net, dev) { 632062306a36Sopenharmony_ci idev = __in6_dev_get(dev); 632162306a36Sopenharmony_ci if (idev) { 632262306a36Sopenharmony_ci int changed = (!idev->cnf.disable_ipv6) ^ (!newf); 632362306a36Sopenharmony_ci idev->cnf.disable_ipv6 = newf; 632462306a36Sopenharmony_ci if (changed) 632562306a36Sopenharmony_ci dev_disable_change(idev); 632662306a36Sopenharmony_ci } 632762306a36Sopenharmony_ci } 632862306a36Sopenharmony_ci} 632962306a36Sopenharmony_ci 633062306a36Sopenharmony_cistatic int addrconf_disable_ipv6(struct ctl_table *table, int *p, int newf) 633162306a36Sopenharmony_ci{ 633262306a36Sopenharmony_ci struct net *net; 633362306a36Sopenharmony_ci int old; 633462306a36Sopenharmony_ci 633562306a36Sopenharmony_ci if (!rtnl_trylock()) 633662306a36Sopenharmony_ci return restart_syscall(); 633762306a36Sopenharmony_ci 633862306a36Sopenharmony_ci net = (struct net *)table->extra2; 633962306a36Sopenharmony_ci old = *p; 634062306a36Sopenharmony_ci *p = newf; 634162306a36Sopenharmony_ci 634262306a36Sopenharmony_ci if (p == &net->ipv6.devconf_dflt->disable_ipv6) { 634362306a36Sopenharmony_ci rtnl_unlock(); 634462306a36Sopenharmony_ci return 0; 634562306a36Sopenharmony_ci } 634662306a36Sopenharmony_ci 634762306a36Sopenharmony_ci if (p == &net->ipv6.devconf_all->disable_ipv6) { 634862306a36Sopenharmony_ci net->ipv6.devconf_dflt->disable_ipv6 = newf; 634962306a36Sopenharmony_ci addrconf_disable_change(net, newf); 635062306a36Sopenharmony_ci } else if ((!newf) ^ (!old)) 635162306a36Sopenharmony_ci dev_disable_change((struct inet6_dev *)table->extra1); 635262306a36Sopenharmony_ci 635362306a36Sopenharmony_ci rtnl_unlock(); 635462306a36Sopenharmony_ci return 0; 635562306a36Sopenharmony_ci} 635662306a36Sopenharmony_ci 635762306a36Sopenharmony_cistatic int addrconf_sysctl_disable(struct ctl_table *ctl, int write, 635862306a36Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 635962306a36Sopenharmony_ci{ 636062306a36Sopenharmony_ci int *valp = ctl->data; 636162306a36Sopenharmony_ci int val = *valp; 636262306a36Sopenharmony_ci loff_t pos = *ppos; 636362306a36Sopenharmony_ci struct ctl_table lctl; 636462306a36Sopenharmony_ci int ret; 636562306a36Sopenharmony_ci 636662306a36Sopenharmony_ci /* 636762306a36Sopenharmony_ci * ctl->data points to idev->cnf.disable_ipv6, we should 636862306a36Sopenharmony_ci * not modify it until we get the rtnl lock. 636962306a36Sopenharmony_ci */ 637062306a36Sopenharmony_ci lctl = *ctl; 637162306a36Sopenharmony_ci lctl.data = &val; 637262306a36Sopenharmony_ci 637362306a36Sopenharmony_ci ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); 637462306a36Sopenharmony_ci 637562306a36Sopenharmony_ci if (write) 637662306a36Sopenharmony_ci ret = addrconf_disable_ipv6(ctl, valp, val); 637762306a36Sopenharmony_ci if (ret) 637862306a36Sopenharmony_ci *ppos = pos; 637962306a36Sopenharmony_ci return ret; 638062306a36Sopenharmony_ci} 638162306a36Sopenharmony_ci 638262306a36Sopenharmony_cistatic int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write, 638362306a36Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 638462306a36Sopenharmony_ci{ 638562306a36Sopenharmony_ci int *valp = ctl->data; 638662306a36Sopenharmony_ci int ret; 638762306a36Sopenharmony_ci int old, new; 638862306a36Sopenharmony_ci 638962306a36Sopenharmony_ci old = *valp; 639062306a36Sopenharmony_ci ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 639162306a36Sopenharmony_ci new = *valp; 639262306a36Sopenharmony_ci 639362306a36Sopenharmony_ci if (write && old != new) { 639462306a36Sopenharmony_ci struct net *net = ctl->extra2; 639562306a36Sopenharmony_ci 639662306a36Sopenharmony_ci if (!rtnl_trylock()) 639762306a36Sopenharmony_ci return restart_syscall(); 639862306a36Sopenharmony_ci 639962306a36Sopenharmony_ci if (valp == &net->ipv6.devconf_dflt->proxy_ndp) 640062306a36Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 640162306a36Sopenharmony_ci NETCONFA_PROXY_NEIGH, 640262306a36Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT, 640362306a36Sopenharmony_ci net->ipv6.devconf_dflt); 640462306a36Sopenharmony_ci else if (valp == &net->ipv6.devconf_all->proxy_ndp) 640562306a36Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 640662306a36Sopenharmony_ci NETCONFA_PROXY_NEIGH, 640762306a36Sopenharmony_ci NETCONFA_IFINDEX_ALL, 640862306a36Sopenharmony_ci net->ipv6.devconf_all); 640962306a36Sopenharmony_ci else { 641062306a36Sopenharmony_ci struct inet6_dev *idev = ctl->extra1; 641162306a36Sopenharmony_ci 641262306a36Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, 641362306a36Sopenharmony_ci NETCONFA_PROXY_NEIGH, 641462306a36Sopenharmony_ci idev->dev->ifindex, 641562306a36Sopenharmony_ci &idev->cnf); 641662306a36Sopenharmony_ci } 641762306a36Sopenharmony_ci rtnl_unlock(); 641862306a36Sopenharmony_ci } 641962306a36Sopenharmony_ci 642062306a36Sopenharmony_ci return ret; 642162306a36Sopenharmony_ci} 642262306a36Sopenharmony_ci 642362306a36Sopenharmony_cistatic int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write, 642462306a36Sopenharmony_ci void *buffer, size_t *lenp, 642562306a36Sopenharmony_ci loff_t *ppos) 642662306a36Sopenharmony_ci{ 642762306a36Sopenharmony_ci int ret = 0; 642862306a36Sopenharmony_ci u32 new_val; 642962306a36Sopenharmony_ci struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1; 643062306a36Sopenharmony_ci struct net *net = (struct net *)ctl->extra2; 643162306a36Sopenharmony_ci struct ctl_table tmp = { 643262306a36Sopenharmony_ci .data = &new_val, 643362306a36Sopenharmony_ci .maxlen = sizeof(new_val), 643462306a36Sopenharmony_ci .mode = ctl->mode, 643562306a36Sopenharmony_ci }; 643662306a36Sopenharmony_ci 643762306a36Sopenharmony_ci if (!rtnl_trylock()) 643862306a36Sopenharmony_ci return restart_syscall(); 643962306a36Sopenharmony_ci 644062306a36Sopenharmony_ci new_val = *((u32 *)ctl->data); 644162306a36Sopenharmony_ci 644262306a36Sopenharmony_ci ret = proc_douintvec(&tmp, write, buffer, lenp, ppos); 644362306a36Sopenharmony_ci if (ret != 0) 644462306a36Sopenharmony_ci goto out; 644562306a36Sopenharmony_ci 644662306a36Sopenharmony_ci if (write) { 644762306a36Sopenharmony_ci if (check_addr_gen_mode(new_val) < 0) { 644862306a36Sopenharmony_ci ret = -EINVAL; 644962306a36Sopenharmony_ci goto out; 645062306a36Sopenharmony_ci } 645162306a36Sopenharmony_ci 645262306a36Sopenharmony_ci if (idev) { 645362306a36Sopenharmony_ci if (check_stable_privacy(idev, net, new_val) < 0) { 645462306a36Sopenharmony_ci ret = -EINVAL; 645562306a36Sopenharmony_ci goto out; 645662306a36Sopenharmony_ci } 645762306a36Sopenharmony_ci 645862306a36Sopenharmony_ci if (idev->cnf.addr_gen_mode != new_val) { 645962306a36Sopenharmony_ci idev->cnf.addr_gen_mode = new_val; 646062306a36Sopenharmony_ci addrconf_init_auto_addrs(idev->dev); 646162306a36Sopenharmony_ci } 646262306a36Sopenharmony_ci } else if (&net->ipv6.devconf_all->addr_gen_mode == ctl->data) { 646362306a36Sopenharmony_ci struct net_device *dev; 646462306a36Sopenharmony_ci 646562306a36Sopenharmony_ci net->ipv6.devconf_dflt->addr_gen_mode = new_val; 646662306a36Sopenharmony_ci for_each_netdev(net, dev) { 646762306a36Sopenharmony_ci idev = __in6_dev_get(dev); 646862306a36Sopenharmony_ci if (idev && 646962306a36Sopenharmony_ci idev->cnf.addr_gen_mode != new_val) { 647062306a36Sopenharmony_ci idev->cnf.addr_gen_mode = new_val; 647162306a36Sopenharmony_ci addrconf_init_auto_addrs(idev->dev); 647262306a36Sopenharmony_ci } 647362306a36Sopenharmony_ci } 647462306a36Sopenharmony_ci } 647562306a36Sopenharmony_ci 647662306a36Sopenharmony_ci *((u32 *)ctl->data) = new_val; 647762306a36Sopenharmony_ci } 647862306a36Sopenharmony_ci 647962306a36Sopenharmony_ciout: 648062306a36Sopenharmony_ci rtnl_unlock(); 648162306a36Sopenharmony_ci 648262306a36Sopenharmony_ci return ret; 648362306a36Sopenharmony_ci} 648462306a36Sopenharmony_ci 648562306a36Sopenharmony_cistatic int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write, 648662306a36Sopenharmony_ci void *buffer, size_t *lenp, 648762306a36Sopenharmony_ci loff_t *ppos) 648862306a36Sopenharmony_ci{ 648962306a36Sopenharmony_ci int err; 649062306a36Sopenharmony_ci struct in6_addr addr; 649162306a36Sopenharmony_ci char str[IPV6_MAX_STRLEN]; 649262306a36Sopenharmony_ci struct ctl_table lctl = *ctl; 649362306a36Sopenharmony_ci struct net *net = ctl->extra2; 649462306a36Sopenharmony_ci struct ipv6_stable_secret *secret = ctl->data; 649562306a36Sopenharmony_ci 649662306a36Sopenharmony_ci if (&net->ipv6.devconf_all->stable_secret == ctl->data) 649762306a36Sopenharmony_ci return -EIO; 649862306a36Sopenharmony_ci 649962306a36Sopenharmony_ci lctl.maxlen = IPV6_MAX_STRLEN; 650062306a36Sopenharmony_ci lctl.data = str; 650162306a36Sopenharmony_ci 650262306a36Sopenharmony_ci if (!rtnl_trylock()) 650362306a36Sopenharmony_ci return restart_syscall(); 650462306a36Sopenharmony_ci 650562306a36Sopenharmony_ci if (!write && !secret->initialized) { 650662306a36Sopenharmony_ci err = -EIO; 650762306a36Sopenharmony_ci goto out; 650862306a36Sopenharmony_ci } 650962306a36Sopenharmony_ci 651062306a36Sopenharmony_ci err = snprintf(str, sizeof(str), "%pI6", &secret->secret); 651162306a36Sopenharmony_ci if (err >= sizeof(str)) { 651262306a36Sopenharmony_ci err = -EIO; 651362306a36Sopenharmony_ci goto out; 651462306a36Sopenharmony_ci } 651562306a36Sopenharmony_ci 651662306a36Sopenharmony_ci err = proc_dostring(&lctl, write, buffer, lenp, ppos); 651762306a36Sopenharmony_ci if (err || !write) 651862306a36Sopenharmony_ci goto out; 651962306a36Sopenharmony_ci 652062306a36Sopenharmony_ci if (in6_pton(str, -1, addr.in6_u.u6_addr8, -1, NULL) != 1) { 652162306a36Sopenharmony_ci err = -EIO; 652262306a36Sopenharmony_ci goto out; 652362306a36Sopenharmony_ci } 652462306a36Sopenharmony_ci 652562306a36Sopenharmony_ci secret->initialized = true; 652662306a36Sopenharmony_ci secret->secret = addr; 652762306a36Sopenharmony_ci 652862306a36Sopenharmony_ci if (&net->ipv6.devconf_dflt->stable_secret == ctl->data) { 652962306a36Sopenharmony_ci struct net_device *dev; 653062306a36Sopenharmony_ci 653162306a36Sopenharmony_ci for_each_netdev(net, dev) { 653262306a36Sopenharmony_ci struct inet6_dev *idev = __in6_dev_get(dev); 653362306a36Sopenharmony_ci 653462306a36Sopenharmony_ci if (idev) { 653562306a36Sopenharmony_ci idev->cnf.addr_gen_mode = 653662306a36Sopenharmony_ci IN6_ADDR_GEN_MODE_STABLE_PRIVACY; 653762306a36Sopenharmony_ci } 653862306a36Sopenharmony_ci } 653962306a36Sopenharmony_ci } else { 654062306a36Sopenharmony_ci struct inet6_dev *idev = ctl->extra1; 654162306a36Sopenharmony_ci 654262306a36Sopenharmony_ci idev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; 654362306a36Sopenharmony_ci } 654462306a36Sopenharmony_ci 654562306a36Sopenharmony_ciout: 654662306a36Sopenharmony_ci rtnl_unlock(); 654762306a36Sopenharmony_ci 654862306a36Sopenharmony_ci return err; 654962306a36Sopenharmony_ci} 655062306a36Sopenharmony_ci 655162306a36Sopenharmony_cistatic 655262306a36Sopenharmony_ciint addrconf_sysctl_ignore_routes_with_linkdown(struct ctl_table *ctl, 655362306a36Sopenharmony_ci int write, void *buffer, 655462306a36Sopenharmony_ci size_t *lenp, 655562306a36Sopenharmony_ci loff_t *ppos) 655662306a36Sopenharmony_ci{ 655762306a36Sopenharmony_ci int *valp = ctl->data; 655862306a36Sopenharmony_ci int val = *valp; 655962306a36Sopenharmony_ci loff_t pos = *ppos; 656062306a36Sopenharmony_ci struct ctl_table lctl; 656162306a36Sopenharmony_ci int ret; 656262306a36Sopenharmony_ci 656362306a36Sopenharmony_ci /* ctl->data points to idev->cnf.ignore_routes_when_linkdown 656462306a36Sopenharmony_ci * we should not modify it until we get the rtnl lock. 656562306a36Sopenharmony_ci */ 656662306a36Sopenharmony_ci lctl = *ctl; 656762306a36Sopenharmony_ci lctl.data = &val; 656862306a36Sopenharmony_ci 656962306a36Sopenharmony_ci ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); 657062306a36Sopenharmony_ci 657162306a36Sopenharmony_ci if (write) 657262306a36Sopenharmony_ci ret = addrconf_fixup_linkdown(ctl, valp, val); 657362306a36Sopenharmony_ci if (ret) 657462306a36Sopenharmony_ci *ppos = pos; 657562306a36Sopenharmony_ci return ret; 657662306a36Sopenharmony_ci} 657762306a36Sopenharmony_ci 657862306a36Sopenharmony_cistatic 657962306a36Sopenharmony_civoid addrconf_set_nopolicy(struct rt6_info *rt, int action) 658062306a36Sopenharmony_ci{ 658162306a36Sopenharmony_ci if (rt) { 658262306a36Sopenharmony_ci if (action) 658362306a36Sopenharmony_ci rt->dst.flags |= DST_NOPOLICY; 658462306a36Sopenharmony_ci else 658562306a36Sopenharmony_ci rt->dst.flags &= ~DST_NOPOLICY; 658662306a36Sopenharmony_ci } 658762306a36Sopenharmony_ci} 658862306a36Sopenharmony_ci 658962306a36Sopenharmony_cistatic 659062306a36Sopenharmony_civoid addrconf_disable_policy_idev(struct inet6_dev *idev, int val) 659162306a36Sopenharmony_ci{ 659262306a36Sopenharmony_ci struct inet6_ifaddr *ifa; 659362306a36Sopenharmony_ci 659462306a36Sopenharmony_ci read_lock_bh(&idev->lock); 659562306a36Sopenharmony_ci list_for_each_entry(ifa, &idev->addr_list, if_list) { 659662306a36Sopenharmony_ci spin_lock(&ifa->lock); 659762306a36Sopenharmony_ci if (ifa->rt) { 659862306a36Sopenharmony_ci /* host routes only use builtin fib6_nh */ 659962306a36Sopenharmony_ci struct fib6_nh *nh = ifa->rt->fib6_nh; 660062306a36Sopenharmony_ci int cpu; 660162306a36Sopenharmony_ci 660262306a36Sopenharmony_ci rcu_read_lock(); 660362306a36Sopenharmony_ci ifa->rt->dst_nopolicy = val ? true : false; 660462306a36Sopenharmony_ci if (nh->rt6i_pcpu) { 660562306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 660662306a36Sopenharmony_ci struct rt6_info **rtp; 660762306a36Sopenharmony_ci 660862306a36Sopenharmony_ci rtp = per_cpu_ptr(nh->rt6i_pcpu, cpu); 660962306a36Sopenharmony_ci addrconf_set_nopolicy(*rtp, val); 661062306a36Sopenharmony_ci } 661162306a36Sopenharmony_ci } 661262306a36Sopenharmony_ci rcu_read_unlock(); 661362306a36Sopenharmony_ci } 661462306a36Sopenharmony_ci spin_unlock(&ifa->lock); 661562306a36Sopenharmony_ci } 661662306a36Sopenharmony_ci read_unlock_bh(&idev->lock); 661762306a36Sopenharmony_ci} 661862306a36Sopenharmony_ci 661962306a36Sopenharmony_cistatic 662062306a36Sopenharmony_ciint addrconf_disable_policy(struct ctl_table *ctl, int *valp, int val) 662162306a36Sopenharmony_ci{ 662262306a36Sopenharmony_ci struct inet6_dev *idev; 662362306a36Sopenharmony_ci struct net *net; 662462306a36Sopenharmony_ci 662562306a36Sopenharmony_ci if (!rtnl_trylock()) 662662306a36Sopenharmony_ci return restart_syscall(); 662762306a36Sopenharmony_ci 662862306a36Sopenharmony_ci *valp = val; 662962306a36Sopenharmony_ci 663062306a36Sopenharmony_ci net = (struct net *)ctl->extra2; 663162306a36Sopenharmony_ci if (valp == &net->ipv6.devconf_dflt->disable_policy) { 663262306a36Sopenharmony_ci rtnl_unlock(); 663362306a36Sopenharmony_ci return 0; 663462306a36Sopenharmony_ci } 663562306a36Sopenharmony_ci 663662306a36Sopenharmony_ci if (valp == &net->ipv6.devconf_all->disable_policy) { 663762306a36Sopenharmony_ci struct net_device *dev; 663862306a36Sopenharmony_ci 663962306a36Sopenharmony_ci for_each_netdev(net, dev) { 664062306a36Sopenharmony_ci idev = __in6_dev_get(dev); 664162306a36Sopenharmony_ci if (idev) 664262306a36Sopenharmony_ci addrconf_disable_policy_idev(idev, val); 664362306a36Sopenharmony_ci } 664462306a36Sopenharmony_ci } else { 664562306a36Sopenharmony_ci idev = (struct inet6_dev *)ctl->extra1; 664662306a36Sopenharmony_ci addrconf_disable_policy_idev(idev, val); 664762306a36Sopenharmony_ci } 664862306a36Sopenharmony_ci 664962306a36Sopenharmony_ci rtnl_unlock(); 665062306a36Sopenharmony_ci return 0; 665162306a36Sopenharmony_ci} 665262306a36Sopenharmony_ci 665362306a36Sopenharmony_cistatic int addrconf_sysctl_disable_policy(struct ctl_table *ctl, int write, 665462306a36Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 665562306a36Sopenharmony_ci{ 665662306a36Sopenharmony_ci int *valp = ctl->data; 665762306a36Sopenharmony_ci int val = *valp; 665862306a36Sopenharmony_ci loff_t pos = *ppos; 665962306a36Sopenharmony_ci struct ctl_table lctl; 666062306a36Sopenharmony_ci int ret; 666162306a36Sopenharmony_ci 666262306a36Sopenharmony_ci lctl = *ctl; 666362306a36Sopenharmony_ci lctl.data = &val; 666462306a36Sopenharmony_ci ret = proc_dointvec(&lctl, write, buffer, lenp, ppos); 666562306a36Sopenharmony_ci 666662306a36Sopenharmony_ci if (write && (*valp != val)) 666762306a36Sopenharmony_ci ret = addrconf_disable_policy(ctl, valp, val); 666862306a36Sopenharmony_ci 666962306a36Sopenharmony_ci if (ret) 667062306a36Sopenharmony_ci *ppos = pos; 667162306a36Sopenharmony_ci 667262306a36Sopenharmony_ci return ret; 667362306a36Sopenharmony_ci} 667462306a36Sopenharmony_ci 667562306a36Sopenharmony_cistatic int minus_one = -1; 667662306a36Sopenharmony_cistatic const int two_five_five = 255; 667762306a36Sopenharmony_cistatic u32 ioam6_if_id_max = U16_MAX; 667862306a36Sopenharmony_ci 667962306a36Sopenharmony_cistatic const struct ctl_table addrconf_sysctl[] = { 668062306a36Sopenharmony_ci { 668162306a36Sopenharmony_ci .procname = "forwarding", 668262306a36Sopenharmony_ci .data = &ipv6_devconf.forwarding, 668362306a36Sopenharmony_ci .maxlen = sizeof(int), 668462306a36Sopenharmony_ci .mode = 0644, 668562306a36Sopenharmony_ci .proc_handler = addrconf_sysctl_forward, 668662306a36Sopenharmony_ci }, 668762306a36Sopenharmony_ci { 668862306a36Sopenharmony_ci .procname = "hop_limit", 668962306a36Sopenharmony_ci .data = &ipv6_devconf.hop_limit, 669062306a36Sopenharmony_ci .maxlen = sizeof(int), 669162306a36Sopenharmony_ci .mode = 0644, 669262306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 669362306a36Sopenharmony_ci .extra1 = (void *)SYSCTL_ONE, 669462306a36Sopenharmony_ci .extra2 = (void *)&two_five_five, 669562306a36Sopenharmony_ci }, 669662306a36Sopenharmony_ci { 669762306a36Sopenharmony_ci .procname = "mtu", 669862306a36Sopenharmony_ci .data = &ipv6_devconf.mtu6, 669962306a36Sopenharmony_ci .maxlen = sizeof(int), 670062306a36Sopenharmony_ci .mode = 0644, 670162306a36Sopenharmony_ci .proc_handler = addrconf_sysctl_mtu, 670262306a36Sopenharmony_ci }, 670362306a36Sopenharmony_ci { 670462306a36Sopenharmony_ci .procname = "accept_ra", 670562306a36Sopenharmony_ci .data = &ipv6_devconf.accept_ra, 670662306a36Sopenharmony_ci .maxlen = sizeof(int), 670762306a36Sopenharmony_ci .mode = 0644, 670862306a36Sopenharmony_ci .proc_handler = proc_dointvec, 670962306a36Sopenharmony_ci }, 671062306a36Sopenharmony_ci { 671162306a36Sopenharmony_ci .procname = "accept_redirects", 671262306a36Sopenharmony_ci .data = &ipv6_devconf.accept_redirects, 671362306a36Sopenharmony_ci .maxlen = sizeof(int), 671462306a36Sopenharmony_ci .mode = 0644, 671562306a36Sopenharmony_ci .proc_handler = proc_dointvec, 671662306a36Sopenharmony_ci }, 671762306a36Sopenharmony_ci { 671862306a36Sopenharmony_ci .procname = "autoconf", 671962306a36Sopenharmony_ci .data = &ipv6_devconf.autoconf, 672062306a36Sopenharmony_ci .maxlen = sizeof(int), 672162306a36Sopenharmony_ci .mode = 0644, 672262306a36Sopenharmony_ci .proc_handler = proc_dointvec, 672362306a36Sopenharmony_ci }, 672462306a36Sopenharmony_ci { 672562306a36Sopenharmony_ci .procname = "dad_transmits", 672662306a36Sopenharmony_ci .data = &ipv6_devconf.dad_transmits, 672762306a36Sopenharmony_ci .maxlen = sizeof(int), 672862306a36Sopenharmony_ci .mode = 0644, 672962306a36Sopenharmony_ci .proc_handler = proc_dointvec, 673062306a36Sopenharmony_ci }, 673162306a36Sopenharmony_ci { 673262306a36Sopenharmony_ci .procname = "router_solicitations", 673362306a36Sopenharmony_ci .data = &ipv6_devconf.rtr_solicits, 673462306a36Sopenharmony_ci .maxlen = sizeof(int), 673562306a36Sopenharmony_ci .mode = 0644, 673662306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 673762306a36Sopenharmony_ci .extra1 = &minus_one, 673862306a36Sopenharmony_ci }, 673962306a36Sopenharmony_ci { 674062306a36Sopenharmony_ci .procname = "router_solicitation_interval", 674162306a36Sopenharmony_ci .data = &ipv6_devconf.rtr_solicit_interval, 674262306a36Sopenharmony_ci .maxlen = sizeof(int), 674362306a36Sopenharmony_ci .mode = 0644, 674462306a36Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 674562306a36Sopenharmony_ci }, 674662306a36Sopenharmony_ci { 674762306a36Sopenharmony_ci .procname = "router_solicitation_max_interval", 674862306a36Sopenharmony_ci .data = &ipv6_devconf.rtr_solicit_max_interval, 674962306a36Sopenharmony_ci .maxlen = sizeof(int), 675062306a36Sopenharmony_ci .mode = 0644, 675162306a36Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 675262306a36Sopenharmony_ci }, 675362306a36Sopenharmony_ci { 675462306a36Sopenharmony_ci .procname = "router_solicitation_delay", 675562306a36Sopenharmony_ci .data = &ipv6_devconf.rtr_solicit_delay, 675662306a36Sopenharmony_ci .maxlen = sizeof(int), 675762306a36Sopenharmony_ci .mode = 0644, 675862306a36Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 675962306a36Sopenharmony_ci }, 676062306a36Sopenharmony_ci { 676162306a36Sopenharmony_ci .procname = "force_mld_version", 676262306a36Sopenharmony_ci .data = &ipv6_devconf.force_mld_version, 676362306a36Sopenharmony_ci .maxlen = sizeof(int), 676462306a36Sopenharmony_ci .mode = 0644, 676562306a36Sopenharmony_ci .proc_handler = proc_dointvec, 676662306a36Sopenharmony_ci }, 676762306a36Sopenharmony_ci { 676862306a36Sopenharmony_ci .procname = "mldv1_unsolicited_report_interval", 676962306a36Sopenharmony_ci .data = 677062306a36Sopenharmony_ci &ipv6_devconf.mldv1_unsolicited_report_interval, 677162306a36Sopenharmony_ci .maxlen = sizeof(int), 677262306a36Sopenharmony_ci .mode = 0644, 677362306a36Sopenharmony_ci .proc_handler = proc_dointvec_ms_jiffies, 677462306a36Sopenharmony_ci }, 677562306a36Sopenharmony_ci { 677662306a36Sopenharmony_ci .procname = "mldv2_unsolicited_report_interval", 677762306a36Sopenharmony_ci .data = 677862306a36Sopenharmony_ci &ipv6_devconf.mldv2_unsolicited_report_interval, 677962306a36Sopenharmony_ci .maxlen = sizeof(int), 678062306a36Sopenharmony_ci .mode = 0644, 678162306a36Sopenharmony_ci .proc_handler = proc_dointvec_ms_jiffies, 678262306a36Sopenharmony_ci }, 678362306a36Sopenharmony_ci { 678462306a36Sopenharmony_ci .procname = "use_tempaddr", 678562306a36Sopenharmony_ci .data = &ipv6_devconf.use_tempaddr, 678662306a36Sopenharmony_ci .maxlen = sizeof(int), 678762306a36Sopenharmony_ci .mode = 0644, 678862306a36Sopenharmony_ci .proc_handler = proc_dointvec, 678962306a36Sopenharmony_ci }, 679062306a36Sopenharmony_ci { 679162306a36Sopenharmony_ci .procname = "temp_valid_lft", 679262306a36Sopenharmony_ci .data = &ipv6_devconf.temp_valid_lft, 679362306a36Sopenharmony_ci .maxlen = sizeof(int), 679462306a36Sopenharmony_ci .mode = 0644, 679562306a36Sopenharmony_ci .proc_handler = proc_dointvec, 679662306a36Sopenharmony_ci }, 679762306a36Sopenharmony_ci { 679862306a36Sopenharmony_ci .procname = "temp_prefered_lft", 679962306a36Sopenharmony_ci .data = &ipv6_devconf.temp_prefered_lft, 680062306a36Sopenharmony_ci .maxlen = sizeof(int), 680162306a36Sopenharmony_ci .mode = 0644, 680262306a36Sopenharmony_ci .proc_handler = proc_dointvec, 680362306a36Sopenharmony_ci }, 680462306a36Sopenharmony_ci { 680562306a36Sopenharmony_ci .procname = "regen_max_retry", 680662306a36Sopenharmony_ci .data = &ipv6_devconf.regen_max_retry, 680762306a36Sopenharmony_ci .maxlen = sizeof(int), 680862306a36Sopenharmony_ci .mode = 0644, 680962306a36Sopenharmony_ci .proc_handler = proc_dointvec, 681062306a36Sopenharmony_ci }, 681162306a36Sopenharmony_ci { 681262306a36Sopenharmony_ci .procname = "max_desync_factor", 681362306a36Sopenharmony_ci .data = &ipv6_devconf.max_desync_factor, 681462306a36Sopenharmony_ci .maxlen = sizeof(int), 681562306a36Sopenharmony_ci .mode = 0644, 681662306a36Sopenharmony_ci .proc_handler = proc_dointvec, 681762306a36Sopenharmony_ci }, 681862306a36Sopenharmony_ci { 681962306a36Sopenharmony_ci .procname = "max_addresses", 682062306a36Sopenharmony_ci .data = &ipv6_devconf.max_addresses, 682162306a36Sopenharmony_ci .maxlen = sizeof(int), 682262306a36Sopenharmony_ci .mode = 0644, 682362306a36Sopenharmony_ci .proc_handler = proc_dointvec, 682462306a36Sopenharmony_ci }, 682562306a36Sopenharmony_ci { 682662306a36Sopenharmony_ci .procname = "accept_ra_defrtr", 682762306a36Sopenharmony_ci .data = &ipv6_devconf.accept_ra_defrtr, 682862306a36Sopenharmony_ci .maxlen = sizeof(int), 682962306a36Sopenharmony_ci .mode = 0644, 683062306a36Sopenharmony_ci .proc_handler = proc_dointvec, 683162306a36Sopenharmony_ci }, 683262306a36Sopenharmony_ci { 683362306a36Sopenharmony_ci .procname = "ra_defrtr_metric", 683462306a36Sopenharmony_ci .data = &ipv6_devconf.ra_defrtr_metric, 683562306a36Sopenharmony_ci .maxlen = sizeof(u32), 683662306a36Sopenharmony_ci .mode = 0644, 683762306a36Sopenharmony_ci .proc_handler = proc_douintvec_minmax, 683862306a36Sopenharmony_ci .extra1 = (void *)SYSCTL_ONE, 683962306a36Sopenharmony_ci }, 684062306a36Sopenharmony_ci { 684162306a36Sopenharmony_ci .procname = "accept_ra_min_hop_limit", 684262306a36Sopenharmony_ci .data = &ipv6_devconf.accept_ra_min_hop_limit, 684362306a36Sopenharmony_ci .maxlen = sizeof(int), 684462306a36Sopenharmony_ci .mode = 0644, 684562306a36Sopenharmony_ci .proc_handler = proc_dointvec, 684662306a36Sopenharmony_ci }, 684762306a36Sopenharmony_ci { 684862306a36Sopenharmony_ci .procname = "accept_ra_min_lft", 684962306a36Sopenharmony_ci .data = &ipv6_devconf.accept_ra_min_lft, 685062306a36Sopenharmony_ci .maxlen = sizeof(int), 685162306a36Sopenharmony_ci .mode = 0644, 685262306a36Sopenharmony_ci .proc_handler = proc_dointvec, 685362306a36Sopenharmony_ci }, 685462306a36Sopenharmony_ci { 685562306a36Sopenharmony_ci .procname = "accept_ra_pinfo", 685662306a36Sopenharmony_ci .data = &ipv6_devconf.accept_ra_pinfo, 685762306a36Sopenharmony_ci .maxlen = sizeof(int), 685862306a36Sopenharmony_ci .mode = 0644, 685962306a36Sopenharmony_ci .proc_handler = proc_dointvec, 686062306a36Sopenharmony_ci }, 686162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF 686262306a36Sopenharmony_ci { 686362306a36Sopenharmony_ci .procname = "accept_ra_rtr_pref", 686462306a36Sopenharmony_ci .data = &ipv6_devconf.accept_ra_rtr_pref, 686562306a36Sopenharmony_ci .maxlen = sizeof(int), 686662306a36Sopenharmony_ci .mode = 0644, 686762306a36Sopenharmony_ci .proc_handler = proc_dointvec, 686862306a36Sopenharmony_ci }, 686962306a36Sopenharmony_ci { 687062306a36Sopenharmony_ci .procname = "router_probe_interval", 687162306a36Sopenharmony_ci .data = &ipv6_devconf.rtr_probe_interval, 687262306a36Sopenharmony_ci .maxlen = sizeof(int), 687362306a36Sopenharmony_ci .mode = 0644, 687462306a36Sopenharmony_ci .proc_handler = proc_dointvec_jiffies, 687562306a36Sopenharmony_ci }, 687662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO 687762306a36Sopenharmony_ci { 687862306a36Sopenharmony_ci .procname = "accept_ra_rt_info_min_plen", 687962306a36Sopenharmony_ci .data = &ipv6_devconf.accept_ra_rt_info_min_plen, 688062306a36Sopenharmony_ci .maxlen = sizeof(int), 688162306a36Sopenharmony_ci .mode = 0644, 688262306a36Sopenharmony_ci .proc_handler = proc_dointvec, 688362306a36Sopenharmony_ci }, 688462306a36Sopenharmony_ci { 688562306a36Sopenharmony_ci .procname = "accept_ra_rt_info_max_plen", 688662306a36Sopenharmony_ci .data = &ipv6_devconf.accept_ra_rt_info_max_plen, 688762306a36Sopenharmony_ci .maxlen = sizeof(int), 688862306a36Sopenharmony_ci .mode = 0644, 688962306a36Sopenharmony_ci .proc_handler = proc_dointvec, 689062306a36Sopenharmony_ci }, 689162306a36Sopenharmony_ci#endif 689262306a36Sopenharmony_ci#endif 689362306a36Sopenharmony_ci { 689462306a36Sopenharmony_ci .procname = "proxy_ndp", 689562306a36Sopenharmony_ci .data = &ipv6_devconf.proxy_ndp, 689662306a36Sopenharmony_ci .maxlen = sizeof(int), 689762306a36Sopenharmony_ci .mode = 0644, 689862306a36Sopenharmony_ci .proc_handler = addrconf_sysctl_proxy_ndp, 689962306a36Sopenharmony_ci }, 690062306a36Sopenharmony_ci { 690162306a36Sopenharmony_ci .procname = "accept_source_route", 690262306a36Sopenharmony_ci .data = &ipv6_devconf.accept_source_route, 690362306a36Sopenharmony_ci .maxlen = sizeof(int), 690462306a36Sopenharmony_ci .mode = 0644, 690562306a36Sopenharmony_ci .proc_handler = proc_dointvec, 690662306a36Sopenharmony_ci }, 690762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD 690862306a36Sopenharmony_ci { 690962306a36Sopenharmony_ci .procname = "optimistic_dad", 691062306a36Sopenharmony_ci .data = &ipv6_devconf.optimistic_dad, 691162306a36Sopenharmony_ci .maxlen = sizeof(int), 691262306a36Sopenharmony_ci .mode = 0644, 691362306a36Sopenharmony_ci .proc_handler = proc_dointvec, 691462306a36Sopenharmony_ci }, 691562306a36Sopenharmony_ci { 691662306a36Sopenharmony_ci .procname = "use_optimistic", 691762306a36Sopenharmony_ci .data = &ipv6_devconf.use_optimistic, 691862306a36Sopenharmony_ci .maxlen = sizeof(int), 691962306a36Sopenharmony_ci .mode = 0644, 692062306a36Sopenharmony_ci .proc_handler = proc_dointvec, 692162306a36Sopenharmony_ci }, 692262306a36Sopenharmony_ci#endif 692362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 692462306a36Sopenharmony_ci { 692562306a36Sopenharmony_ci .procname = "mc_forwarding", 692662306a36Sopenharmony_ci .data = &ipv6_devconf.mc_forwarding, 692762306a36Sopenharmony_ci .maxlen = sizeof(int), 692862306a36Sopenharmony_ci .mode = 0444, 692962306a36Sopenharmony_ci .proc_handler = proc_dointvec, 693062306a36Sopenharmony_ci }, 693162306a36Sopenharmony_ci#endif 693262306a36Sopenharmony_ci { 693362306a36Sopenharmony_ci .procname = "disable_ipv6", 693462306a36Sopenharmony_ci .data = &ipv6_devconf.disable_ipv6, 693562306a36Sopenharmony_ci .maxlen = sizeof(int), 693662306a36Sopenharmony_ci .mode = 0644, 693762306a36Sopenharmony_ci .proc_handler = addrconf_sysctl_disable, 693862306a36Sopenharmony_ci }, 693962306a36Sopenharmony_ci { 694062306a36Sopenharmony_ci .procname = "accept_dad", 694162306a36Sopenharmony_ci .data = &ipv6_devconf.accept_dad, 694262306a36Sopenharmony_ci .maxlen = sizeof(int), 694362306a36Sopenharmony_ci .mode = 0644, 694462306a36Sopenharmony_ci .proc_handler = proc_dointvec, 694562306a36Sopenharmony_ci }, 694662306a36Sopenharmony_ci { 694762306a36Sopenharmony_ci .procname = "force_tllao", 694862306a36Sopenharmony_ci .data = &ipv6_devconf.force_tllao, 694962306a36Sopenharmony_ci .maxlen = sizeof(int), 695062306a36Sopenharmony_ci .mode = 0644, 695162306a36Sopenharmony_ci .proc_handler = proc_dointvec 695262306a36Sopenharmony_ci }, 695362306a36Sopenharmony_ci { 695462306a36Sopenharmony_ci .procname = "ndisc_notify", 695562306a36Sopenharmony_ci .data = &ipv6_devconf.ndisc_notify, 695662306a36Sopenharmony_ci .maxlen = sizeof(int), 695762306a36Sopenharmony_ci .mode = 0644, 695862306a36Sopenharmony_ci .proc_handler = proc_dointvec 695962306a36Sopenharmony_ci }, 696062306a36Sopenharmony_ci { 696162306a36Sopenharmony_ci .procname = "suppress_frag_ndisc", 696262306a36Sopenharmony_ci .data = &ipv6_devconf.suppress_frag_ndisc, 696362306a36Sopenharmony_ci .maxlen = sizeof(int), 696462306a36Sopenharmony_ci .mode = 0644, 696562306a36Sopenharmony_ci .proc_handler = proc_dointvec 696662306a36Sopenharmony_ci }, 696762306a36Sopenharmony_ci { 696862306a36Sopenharmony_ci .procname = "accept_ra_from_local", 696962306a36Sopenharmony_ci .data = &ipv6_devconf.accept_ra_from_local, 697062306a36Sopenharmony_ci .maxlen = sizeof(int), 697162306a36Sopenharmony_ci .mode = 0644, 697262306a36Sopenharmony_ci .proc_handler = proc_dointvec, 697362306a36Sopenharmony_ci }, 697462306a36Sopenharmony_ci { 697562306a36Sopenharmony_ci .procname = "accept_ra_mtu", 697662306a36Sopenharmony_ci .data = &ipv6_devconf.accept_ra_mtu, 697762306a36Sopenharmony_ci .maxlen = sizeof(int), 697862306a36Sopenharmony_ci .mode = 0644, 697962306a36Sopenharmony_ci .proc_handler = proc_dointvec, 698062306a36Sopenharmony_ci }, 698162306a36Sopenharmony_ci { 698262306a36Sopenharmony_ci .procname = "stable_secret", 698362306a36Sopenharmony_ci .data = &ipv6_devconf.stable_secret, 698462306a36Sopenharmony_ci .maxlen = IPV6_MAX_STRLEN, 698562306a36Sopenharmony_ci .mode = 0600, 698662306a36Sopenharmony_ci .proc_handler = addrconf_sysctl_stable_secret, 698762306a36Sopenharmony_ci }, 698862306a36Sopenharmony_ci { 698962306a36Sopenharmony_ci .procname = "use_oif_addrs_only", 699062306a36Sopenharmony_ci .data = &ipv6_devconf.use_oif_addrs_only, 699162306a36Sopenharmony_ci .maxlen = sizeof(int), 699262306a36Sopenharmony_ci .mode = 0644, 699362306a36Sopenharmony_ci .proc_handler = proc_dointvec, 699462306a36Sopenharmony_ci }, 699562306a36Sopenharmony_ci { 699662306a36Sopenharmony_ci .procname = "ignore_routes_with_linkdown", 699762306a36Sopenharmony_ci .data = &ipv6_devconf.ignore_routes_with_linkdown, 699862306a36Sopenharmony_ci .maxlen = sizeof(int), 699962306a36Sopenharmony_ci .mode = 0644, 700062306a36Sopenharmony_ci .proc_handler = addrconf_sysctl_ignore_routes_with_linkdown, 700162306a36Sopenharmony_ci }, 700262306a36Sopenharmony_ci { 700362306a36Sopenharmony_ci .procname = "drop_unicast_in_l2_multicast", 700462306a36Sopenharmony_ci .data = &ipv6_devconf.drop_unicast_in_l2_multicast, 700562306a36Sopenharmony_ci .maxlen = sizeof(int), 700662306a36Sopenharmony_ci .mode = 0644, 700762306a36Sopenharmony_ci .proc_handler = proc_dointvec, 700862306a36Sopenharmony_ci }, 700962306a36Sopenharmony_ci { 701062306a36Sopenharmony_ci .procname = "drop_unsolicited_na", 701162306a36Sopenharmony_ci .data = &ipv6_devconf.drop_unsolicited_na, 701262306a36Sopenharmony_ci .maxlen = sizeof(int), 701362306a36Sopenharmony_ci .mode = 0644, 701462306a36Sopenharmony_ci .proc_handler = proc_dointvec, 701562306a36Sopenharmony_ci }, 701662306a36Sopenharmony_ci { 701762306a36Sopenharmony_ci .procname = "keep_addr_on_down", 701862306a36Sopenharmony_ci .data = &ipv6_devconf.keep_addr_on_down, 701962306a36Sopenharmony_ci .maxlen = sizeof(int), 702062306a36Sopenharmony_ci .mode = 0644, 702162306a36Sopenharmony_ci .proc_handler = proc_dointvec, 702262306a36Sopenharmony_ci 702362306a36Sopenharmony_ci }, 702462306a36Sopenharmony_ci { 702562306a36Sopenharmony_ci .procname = "seg6_enabled", 702662306a36Sopenharmony_ci .data = &ipv6_devconf.seg6_enabled, 702762306a36Sopenharmony_ci .maxlen = sizeof(int), 702862306a36Sopenharmony_ci .mode = 0644, 702962306a36Sopenharmony_ci .proc_handler = proc_dointvec, 703062306a36Sopenharmony_ci }, 703162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC 703262306a36Sopenharmony_ci { 703362306a36Sopenharmony_ci .procname = "seg6_require_hmac", 703462306a36Sopenharmony_ci .data = &ipv6_devconf.seg6_require_hmac, 703562306a36Sopenharmony_ci .maxlen = sizeof(int), 703662306a36Sopenharmony_ci .mode = 0644, 703762306a36Sopenharmony_ci .proc_handler = proc_dointvec, 703862306a36Sopenharmony_ci }, 703962306a36Sopenharmony_ci#endif 704062306a36Sopenharmony_ci { 704162306a36Sopenharmony_ci .procname = "enhanced_dad", 704262306a36Sopenharmony_ci .data = &ipv6_devconf.enhanced_dad, 704362306a36Sopenharmony_ci .maxlen = sizeof(int), 704462306a36Sopenharmony_ci .mode = 0644, 704562306a36Sopenharmony_ci .proc_handler = proc_dointvec, 704662306a36Sopenharmony_ci }, 704762306a36Sopenharmony_ci { 704862306a36Sopenharmony_ci .procname = "addr_gen_mode", 704962306a36Sopenharmony_ci .data = &ipv6_devconf.addr_gen_mode, 705062306a36Sopenharmony_ci .maxlen = sizeof(int), 705162306a36Sopenharmony_ci .mode = 0644, 705262306a36Sopenharmony_ci .proc_handler = addrconf_sysctl_addr_gen_mode, 705362306a36Sopenharmony_ci }, 705462306a36Sopenharmony_ci { 705562306a36Sopenharmony_ci .procname = "disable_policy", 705662306a36Sopenharmony_ci .data = &ipv6_devconf.disable_policy, 705762306a36Sopenharmony_ci .maxlen = sizeof(int), 705862306a36Sopenharmony_ci .mode = 0644, 705962306a36Sopenharmony_ci .proc_handler = addrconf_sysctl_disable_policy, 706062306a36Sopenharmony_ci }, 706162306a36Sopenharmony_ci { 706262306a36Sopenharmony_ci .procname = "ndisc_tclass", 706362306a36Sopenharmony_ci .data = &ipv6_devconf.ndisc_tclass, 706462306a36Sopenharmony_ci .maxlen = sizeof(int), 706562306a36Sopenharmony_ci .mode = 0644, 706662306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 706762306a36Sopenharmony_ci .extra1 = (void *)SYSCTL_ZERO, 706862306a36Sopenharmony_ci .extra2 = (void *)&two_five_five, 706962306a36Sopenharmony_ci }, 707062306a36Sopenharmony_ci { 707162306a36Sopenharmony_ci .procname = "rpl_seg_enabled", 707262306a36Sopenharmony_ci .data = &ipv6_devconf.rpl_seg_enabled, 707362306a36Sopenharmony_ci .maxlen = sizeof(int), 707462306a36Sopenharmony_ci .mode = 0644, 707562306a36Sopenharmony_ci .proc_handler = proc_dointvec, 707662306a36Sopenharmony_ci }, 707762306a36Sopenharmony_ci { 707862306a36Sopenharmony_ci .procname = "ioam6_enabled", 707962306a36Sopenharmony_ci .data = &ipv6_devconf.ioam6_enabled, 708062306a36Sopenharmony_ci .maxlen = sizeof(u8), 708162306a36Sopenharmony_ci .mode = 0644, 708262306a36Sopenharmony_ci .proc_handler = proc_dou8vec_minmax, 708362306a36Sopenharmony_ci .extra1 = (void *)SYSCTL_ZERO, 708462306a36Sopenharmony_ci .extra2 = (void *)SYSCTL_ONE, 708562306a36Sopenharmony_ci }, 708662306a36Sopenharmony_ci { 708762306a36Sopenharmony_ci .procname = "ioam6_id", 708862306a36Sopenharmony_ci .data = &ipv6_devconf.ioam6_id, 708962306a36Sopenharmony_ci .maxlen = sizeof(u32), 709062306a36Sopenharmony_ci .mode = 0644, 709162306a36Sopenharmony_ci .proc_handler = proc_douintvec_minmax, 709262306a36Sopenharmony_ci .extra1 = (void *)SYSCTL_ZERO, 709362306a36Sopenharmony_ci .extra2 = (void *)&ioam6_if_id_max, 709462306a36Sopenharmony_ci }, 709562306a36Sopenharmony_ci { 709662306a36Sopenharmony_ci .procname = "ioam6_id_wide", 709762306a36Sopenharmony_ci .data = &ipv6_devconf.ioam6_id_wide, 709862306a36Sopenharmony_ci .maxlen = sizeof(u32), 709962306a36Sopenharmony_ci .mode = 0644, 710062306a36Sopenharmony_ci .proc_handler = proc_douintvec, 710162306a36Sopenharmony_ci }, 710262306a36Sopenharmony_ci { 710362306a36Sopenharmony_ci .procname = "ndisc_evict_nocarrier", 710462306a36Sopenharmony_ci .data = &ipv6_devconf.ndisc_evict_nocarrier, 710562306a36Sopenharmony_ci .maxlen = sizeof(u8), 710662306a36Sopenharmony_ci .mode = 0644, 710762306a36Sopenharmony_ci .proc_handler = proc_dou8vec_minmax, 710862306a36Sopenharmony_ci .extra1 = (void *)SYSCTL_ZERO, 710962306a36Sopenharmony_ci .extra2 = (void *)SYSCTL_ONE, 711062306a36Sopenharmony_ci }, 711162306a36Sopenharmony_ci { 711262306a36Sopenharmony_ci .procname = "accept_untracked_na", 711362306a36Sopenharmony_ci .data = &ipv6_devconf.accept_untracked_na, 711462306a36Sopenharmony_ci .maxlen = sizeof(int), 711562306a36Sopenharmony_ci .mode = 0644, 711662306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 711762306a36Sopenharmony_ci .extra1 = SYSCTL_ZERO, 711862306a36Sopenharmony_ci .extra2 = SYSCTL_TWO, 711962306a36Sopenharmony_ci }, 712062306a36Sopenharmony_ci { 712162306a36Sopenharmony_ci /* sentinel */ 712262306a36Sopenharmony_ci } 712362306a36Sopenharmony_ci}; 712462306a36Sopenharmony_ci 712562306a36Sopenharmony_cistatic int __addrconf_sysctl_register(struct net *net, char *dev_name, 712662306a36Sopenharmony_ci struct inet6_dev *idev, struct ipv6_devconf *p) 712762306a36Sopenharmony_ci{ 712862306a36Sopenharmony_ci int i, ifindex; 712962306a36Sopenharmony_ci struct ctl_table *table; 713062306a36Sopenharmony_ci char path[sizeof("net/ipv6/conf/") + IFNAMSIZ]; 713162306a36Sopenharmony_ci 713262306a36Sopenharmony_ci table = kmemdup(addrconf_sysctl, sizeof(addrconf_sysctl), GFP_KERNEL_ACCOUNT); 713362306a36Sopenharmony_ci if (!table) 713462306a36Sopenharmony_ci goto out; 713562306a36Sopenharmony_ci 713662306a36Sopenharmony_ci for (i = 0; table[i].data; i++) { 713762306a36Sopenharmony_ci table[i].data += (char *)p - (char *)&ipv6_devconf; 713862306a36Sopenharmony_ci /* If one of these is already set, then it is not safe to 713962306a36Sopenharmony_ci * overwrite either of them: this makes proc_dointvec_minmax 714062306a36Sopenharmony_ci * usable. 714162306a36Sopenharmony_ci */ 714262306a36Sopenharmony_ci if (!table[i].extra1 && !table[i].extra2) { 714362306a36Sopenharmony_ci table[i].extra1 = idev; /* embedded; no ref */ 714462306a36Sopenharmony_ci table[i].extra2 = net; 714562306a36Sopenharmony_ci } 714662306a36Sopenharmony_ci } 714762306a36Sopenharmony_ci 714862306a36Sopenharmony_ci snprintf(path, sizeof(path), "net/ipv6/conf/%s", dev_name); 714962306a36Sopenharmony_ci 715062306a36Sopenharmony_ci p->sysctl_header = register_net_sysctl_sz(net, path, table, 715162306a36Sopenharmony_ci ARRAY_SIZE(addrconf_sysctl)); 715262306a36Sopenharmony_ci if (!p->sysctl_header) 715362306a36Sopenharmony_ci goto free; 715462306a36Sopenharmony_ci 715562306a36Sopenharmony_ci if (!strcmp(dev_name, "all")) 715662306a36Sopenharmony_ci ifindex = NETCONFA_IFINDEX_ALL; 715762306a36Sopenharmony_ci else if (!strcmp(dev_name, "default")) 715862306a36Sopenharmony_ci ifindex = NETCONFA_IFINDEX_DEFAULT; 715962306a36Sopenharmony_ci else 716062306a36Sopenharmony_ci ifindex = idev->dev->ifindex; 716162306a36Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, 716262306a36Sopenharmony_ci ifindex, p); 716362306a36Sopenharmony_ci return 0; 716462306a36Sopenharmony_ci 716562306a36Sopenharmony_cifree: 716662306a36Sopenharmony_ci kfree(table); 716762306a36Sopenharmony_ciout: 716862306a36Sopenharmony_ci return -ENOBUFS; 716962306a36Sopenharmony_ci} 717062306a36Sopenharmony_ci 717162306a36Sopenharmony_cistatic void __addrconf_sysctl_unregister(struct net *net, 717262306a36Sopenharmony_ci struct ipv6_devconf *p, int ifindex) 717362306a36Sopenharmony_ci{ 717462306a36Sopenharmony_ci struct ctl_table *table; 717562306a36Sopenharmony_ci 717662306a36Sopenharmony_ci if (!p->sysctl_header) 717762306a36Sopenharmony_ci return; 717862306a36Sopenharmony_ci 717962306a36Sopenharmony_ci table = p->sysctl_header->ctl_table_arg; 718062306a36Sopenharmony_ci unregister_net_sysctl_table(p->sysctl_header); 718162306a36Sopenharmony_ci p->sysctl_header = NULL; 718262306a36Sopenharmony_ci kfree(table); 718362306a36Sopenharmony_ci 718462306a36Sopenharmony_ci inet6_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL); 718562306a36Sopenharmony_ci} 718662306a36Sopenharmony_ci 718762306a36Sopenharmony_cistatic int addrconf_sysctl_register(struct inet6_dev *idev) 718862306a36Sopenharmony_ci{ 718962306a36Sopenharmony_ci int err; 719062306a36Sopenharmony_ci 719162306a36Sopenharmony_ci if (!sysctl_dev_name_is_allowed(idev->dev->name)) 719262306a36Sopenharmony_ci return -EINVAL; 719362306a36Sopenharmony_ci 719462306a36Sopenharmony_ci err = neigh_sysctl_register(idev->dev, idev->nd_parms, 719562306a36Sopenharmony_ci &ndisc_ifinfo_sysctl_change); 719662306a36Sopenharmony_ci if (err) 719762306a36Sopenharmony_ci return err; 719862306a36Sopenharmony_ci err = __addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name, 719962306a36Sopenharmony_ci idev, &idev->cnf); 720062306a36Sopenharmony_ci if (err) 720162306a36Sopenharmony_ci neigh_sysctl_unregister(idev->nd_parms); 720262306a36Sopenharmony_ci 720362306a36Sopenharmony_ci return err; 720462306a36Sopenharmony_ci} 720562306a36Sopenharmony_ci 720662306a36Sopenharmony_cistatic void addrconf_sysctl_unregister(struct inet6_dev *idev) 720762306a36Sopenharmony_ci{ 720862306a36Sopenharmony_ci __addrconf_sysctl_unregister(dev_net(idev->dev), &idev->cnf, 720962306a36Sopenharmony_ci idev->dev->ifindex); 721062306a36Sopenharmony_ci neigh_sysctl_unregister(idev->nd_parms); 721162306a36Sopenharmony_ci} 721262306a36Sopenharmony_ci 721362306a36Sopenharmony_ci 721462306a36Sopenharmony_ci#endif 721562306a36Sopenharmony_ci 721662306a36Sopenharmony_cistatic int __net_init addrconf_init_net(struct net *net) 721762306a36Sopenharmony_ci{ 721862306a36Sopenharmony_ci int err = -ENOMEM; 721962306a36Sopenharmony_ci struct ipv6_devconf *all, *dflt; 722062306a36Sopenharmony_ci 722162306a36Sopenharmony_ci spin_lock_init(&net->ipv6.addrconf_hash_lock); 722262306a36Sopenharmony_ci INIT_DEFERRABLE_WORK(&net->ipv6.addr_chk_work, addrconf_verify_work); 722362306a36Sopenharmony_ci net->ipv6.inet6_addr_lst = kcalloc(IN6_ADDR_HSIZE, 722462306a36Sopenharmony_ci sizeof(struct hlist_head), 722562306a36Sopenharmony_ci GFP_KERNEL); 722662306a36Sopenharmony_ci if (!net->ipv6.inet6_addr_lst) 722762306a36Sopenharmony_ci goto err_alloc_addr; 722862306a36Sopenharmony_ci 722962306a36Sopenharmony_ci all = kmemdup(&ipv6_devconf, sizeof(ipv6_devconf), GFP_KERNEL); 723062306a36Sopenharmony_ci if (!all) 723162306a36Sopenharmony_ci goto err_alloc_all; 723262306a36Sopenharmony_ci 723362306a36Sopenharmony_ci dflt = kmemdup(&ipv6_devconf_dflt, sizeof(ipv6_devconf_dflt), GFP_KERNEL); 723462306a36Sopenharmony_ci if (!dflt) 723562306a36Sopenharmony_ci goto err_alloc_dflt; 723662306a36Sopenharmony_ci 723762306a36Sopenharmony_ci if (!net_eq(net, &init_net)) { 723862306a36Sopenharmony_ci switch (net_inherit_devconf()) { 723962306a36Sopenharmony_ci case 1: /* copy from init_net */ 724062306a36Sopenharmony_ci memcpy(all, init_net.ipv6.devconf_all, 724162306a36Sopenharmony_ci sizeof(ipv6_devconf)); 724262306a36Sopenharmony_ci memcpy(dflt, init_net.ipv6.devconf_dflt, 724362306a36Sopenharmony_ci sizeof(ipv6_devconf_dflt)); 724462306a36Sopenharmony_ci break; 724562306a36Sopenharmony_ci case 3: /* copy from the current netns */ 724662306a36Sopenharmony_ci memcpy(all, current->nsproxy->net_ns->ipv6.devconf_all, 724762306a36Sopenharmony_ci sizeof(ipv6_devconf)); 724862306a36Sopenharmony_ci memcpy(dflt, 724962306a36Sopenharmony_ci current->nsproxy->net_ns->ipv6.devconf_dflt, 725062306a36Sopenharmony_ci sizeof(ipv6_devconf_dflt)); 725162306a36Sopenharmony_ci break; 725262306a36Sopenharmony_ci case 0: 725362306a36Sopenharmony_ci case 2: 725462306a36Sopenharmony_ci /* use compiled values */ 725562306a36Sopenharmony_ci break; 725662306a36Sopenharmony_ci } 725762306a36Sopenharmony_ci } 725862306a36Sopenharmony_ci 725962306a36Sopenharmony_ci /* these will be inherited by all namespaces */ 726062306a36Sopenharmony_ci dflt->autoconf = ipv6_defaults.autoconf; 726162306a36Sopenharmony_ci dflt->disable_ipv6 = ipv6_defaults.disable_ipv6; 726262306a36Sopenharmony_ci 726362306a36Sopenharmony_ci dflt->stable_secret.initialized = false; 726462306a36Sopenharmony_ci all->stable_secret.initialized = false; 726562306a36Sopenharmony_ci 726662306a36Sopenharmony_ci net->ipv6.devconf_all = all; 726762306a36Sopenharmony_ci net->ipv6.devconf_dflt = dflt; 726862306a36Sopenharmony_ci 726962306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 727062306a36Sopenharmony_ci err = __addrconf_sysctl_register(net, "all", NULL, all); 727162306a36Sopenharmony_ci if (err < 0) 727262306a36Sopenharmony_ci goto err_reg_all; 727362306a36Sopenharmony_ci 727462306a36Sopenharmony_ci err = __addrconf_sysctl_register(net, "default", NULL, dflt); 727562306a36Sopenharmony_ci if (err < 0) 727662306a36Sopenharmony_ci goto err_reg_dflt; 727762306a36Sopenharmony_ci#endif 727862306a36Sopenharmony_ci return 0; 727962306a36Sopenharmony_ci 728062306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 728162306a36Sopenharmony_cierr_reg_dflt: 728262306a36Sopenharmony_ci __addrconf_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL); 728362306a36Sopenharmony_cierr_reg_all: 728462306a36Sopenharmony_ci kfree(dflt); 728562306a36Sopenharmony_ci net->ipv6.devconf_dflt = NULL; 728662306a36Sopenharmony_ci#endif 728762306a36Sopenharmony_cierr_alloc_dflt: 728862306a36Sopenharmony_ci kfree(all); 728962306a36Sopenharmony_ci net->ipv6.devconf_all = NULL; 729062306a36Sopenharmony_cierr_alloc_all: 729162306a36Sopenharmony_ci kfree(net->ipv6.inet6_addr_lst); 729262306a36Sopenharmony_cierr_alloc_addr: 729362306a36Sopenharmony_ci return err; 729462306a36Sopenharmony_ci} 729562306a36Sopenharmony_ci 729662306a36Sopenharmony_cistatic void __net_exit addrconf_exit_net(struct net *net) 729762306a36Sopenharmony_ci{ 729862306a36Sopenharmony_ci int i; 729962306a36Sopenharmony_ci 730062306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 730162306a36Sopenharmony_ci __addrconf_sysctl_unregister(net, net->ipv6.devconf_dflt, 730262306a36Sopenharmony_ci NETCONFA_IFINDEX_DEFAULT); 730362306a36Sopenharmony_ci __addrconf_sysctl_unregister(net, net->ipv6.devconf_all, 730462306a36Sopenharmony_ci NETCONFA_IFINDEX_ALL); 730562306a36Sopenharmony_ci#endif 730662306a36Sopenharmony_ci kfree(net->ipv6.devconf_dflt); 730762306a36Sopenharmony_ci net->ipv6.devconf_dflt = NULL; 730862306a36Sopenharmony_ci kfree(net->ipv6.devconf_all); 730962306a36Sopenharmony_ci net->ipv6.devconf_all = NULL; 731062306a36Sopenharmony_ci 731162306a36Sopenharmony_ci cancel_delayed_work_sync(&net->ipv6.addr_chk_work); 731262306a36Sopenharmony_ci /* 731362306a36Sopenharmony_ci * Check hash table, then free it. 731462306a36Sopenharmony_ci */ 731562306a36Sopenharmony_ci for (i = 0; i < IN6_ADDR_HSIZE; i++) 731662306a36Sopenharmony_ci WARN_ON_ONCE(!hlist_empty(&net->ipv6.inet6_addr_lst[i])); 731762306a36Sopenharmony_ci 731862306a36Sopenharmony_ci kfree(net->ipv6.inet6_addr_lst); 731962306a36Sopenharmony_ci net->ipv6.inet6_addr_lst = NULL; 732062306a36Sopenharmony_ci} 732162306a36Sopenharmony_ci 732262306a36Sopenharmony_cistatic struct pernet_operations addrconf_ops = { 732362306a36Sopenharmony_ci .init = addrconf_init_net, 732462306a36Sopenharmony_ci .exit = addrconf_exit_net, 732562306a36Sopenharmony_ci}; 732662306a36Sopenharmony_ci 732762306a36Sopenharmony_cistatic struct rtnl_af_ops inet6_ops __read_mostly = { 732862306a36Sopenharmony_ci .family = AF_INET6, 732962306a36Sopenharmony_ci .fill_link_af = inet6_fill_link_af, 733062306a36Sopenharmony_ci .get_link_af_size = inet6_get_link_af_size, 733162306a36Sopenharmony_ci .validate_link_af = inet6_validate_link_af, 733262306a36Sopenharmony_ci .set_link_af = inet6_set_link_af, 733362306a36Sopenharmony_ci}; 733462306a36Sopenharmony_ci 733562306a36Sopenharmony_ci/* 733662306a36Sopenharmony_ci * Init / cleanup code 733762306a36Sopenharmony_ci */ 733862306a36Sopenharmony_ci 733962306a36Sopenharmony_ciint __init addrconf_init(void) 734062306a36Sopenharmony_ci{ 734162306a36Sopenharmony_ci struct inet6_dev *idev; 734262306a36Sopenharmony_ci int err; 734362306a36Sopenharmony_ci 734462306a36Sopenharmony_ci err = ipv6_addr_label_init(); 734562306a36Sopenharmony_ci if (err < 0) { 734662306a36Sopenharmony_ci pr_crit("%s: cannot initialize default policy table: %d\n", 734762306a36Sopenharmony_ci __func__, err); 734862306a36Sopenharmony_ci goto out; 734962306a36Sopenharmony_ci } 735062306a36Sopenharmony_ci 735162306a36Sopenharmony_ci err = register_pernet_subsys(&addrconf_ops); 735262306a36Sopenharmony_ci if (err < 0) 735362306a36Sopenharmony_ci goto out_addrlabel; 735462306a36Sopenharmony_ci 735562306a36Sopenharmony_ci addrconf_wq = create_workqueue("ipv6_addrconf"); 735662306a36Sopenharmony_ci if (!addrconf_wq) { 735762306a36Sopenharmony_ci err = -ENOMEM; 735862306a36Sopenharmony_ci goto out_nowq; 735962306a36Sopenharmony_ci } 736062306a36Sopenharmony_ci 736162306a36Sopenharmony_ci rtnl_lock(); 736262306a36Sopenharmony_ci idev = ipv6_add_dev(blackhole_netdev); 736362306a36Sopenharmony_ci rtnl_unlock(); 736462306a36Sopenharmony_ci if (IS_ERR(idev)) { 736562306a36Sopenharmony_ci err = PTR_ERR(idev); 736662306a36Sopenharmony_ci goto errlo; 736762306a36Sopenharmony_ci } 736862306a36Sopenharmony_ci 736962306a36Sopenharmony_ci ip6_route_init_special_entries(); 737062306a36Sopenharmony_ci 737162306a36Sopenharmony_ci register_netdevice_notifier(&ipv6_dev_notf); 737262306a36Sopenharmony_ci 737362306a36Sopenharmony_ci addrconf_verify(&init_net); 737462306a36Sopenharmony_ci 737562306a36Sopenharmony_ci rtnl_af_register(&inet6_ops); 737662306a36Sopenharmony_ci 737762306a36Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETLINK, 737862306a36Sopenharmony_ci NULL, inet6_dump_ifinfo, 0); 737962306a36Sopenharmony_ci if (err < 0) 738062306a36Sopenharmony_ci goto errout; 738162306a36Sopenharmony_ci 738262306a36Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWADDR, 738362306a36Sopenharmony_ci inet6_rtm_newaddr, NULL, 0); 738462306a36Sopenharmony_ci if (err < 0) 738562306a36Sopenharmony_ci goto errout; 738662306a36Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELADDR, 738762306a36Sopenharmony_ci inet6_rtm_deladdr, NULL, 0); 738862306a36Sopenharmony_ci if (err < 0) 738962306a36Sopenharmony_ci goto errout; 739062306a36Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDR, 739162306a36Sopenharmony_ci inet6_rtm_getaddr, inet6_dump_ifaddr, 739262306a36Sopenharmony_ci RTNL_FLAG_DOIT_UNLOCKED); 739362306a36Sopenharmony_ci if (err < 0) 739462306a36Sopenharmony_ci goto errout; 739562306a36Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETMULTICAST, 739662306a36Sopenharmony_ci NULL, inet6_dump_ifmcaddr, 0); 739762306a36Sopenharmony_ci if (err < 0) 739862306a36Sopenharmony_ci goto errout; 739962306a36Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETANYCAST, 740062306a36Sopenharmony_ci NULL, inet6_dump_ifacaddr, 0); 740162306a36Sopenharmony_ci if (err < 0) 740262306a36Sopenharmony_ci goto errout; 740362306a36Sopenharmony_ci err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETNETCONF, 740462306a36Sopenharmony_ci inet6_netconf_get_devconf, 740562306a36Sopenharmony_ci inet6_netconf_dump_devconf, 740662306a36Sopenharmony_ci RTNL_FLAG_DOIT_UNLOCKED); 740762306a36Sopenharmony_ci if (err < 0) 740862306a36Sopenharmony_ci goto errout; 740962306a36Sopenharmony_ci err = ipv6_addr_label_rtnl_register(); 741062306a36Sopenharmony_ci if (err < 0) 741162306a36Sopenharmony_ci goto errout; 741262306a36Sopenharmony_ci 741362306a36Sopenharmony_ci return 0; 741462306a36Sopenharmony_cierrout: 741562306a36Sopenharmony_ci rtnl_unregister_all(PF_INET6); 741662306a36Sopenharmony_ci rtnl_af_unregister(&inet6_ops); 741762306a36Sopenharmony_ci unregister_netdevice_notifier(&ipv6_dev_notf); 741862306a36Sopenharmony_cierrlo: 741962306a36Sopenharmony_ci destroy_workqueue(addrconf_wq); 742062306a36Sopenharmony_ciout_nowq: 742162306a36Sopenharmony_ci unregister_pernet_subsys(&addrconf_ops); 742262306a36Sopenharmony_ciout_addrlabel: 742362306a36Sopenharmony_ci ipv6_addr_label_cleanup(); 742462306a36Sopenharmony_ciout: 742562306a36Sopenharmony_ci return err; 742662306a36Sopenharmony_ci} 742762306a36Sopenharmony_ci 742862306a36Sopenharmony_civoid addrconf_cleanup(void) 742962306a36Sopenharmony_ci{ 743062306a36Sopenharmony_ci struct net_device *dev; 743162306a36Sopenharmony_ci 743262306a36Sopenharmony_ci unregister_netdevice_notifier(&ipv6_dev_notf); 743362306a36Sopenharmony_ci unregister_pernet_subsys(&addrconf_ops); 743462306a36Sopenharmony_ci ipv6_addr_label_cleanup(); 743562306a36Sopenharmony_ci 743662306a36Sopenharmony_ci rtnl_af_unregister(&inet6_ops); 743762306a36Sopenharmony_ci 743862306a36Sopenharmony_ci rtnl_lock(); 743962306a36Sopenharmony_ci 744062306a36Sopenharmony_ci /* clean dev list */ 744162306a36Sopenharmony_ci for_each_netdev(&init_net, dev) { 744262306a36Sopenharmony_ci if (__in6_dev_get(dev) == NULL) 744362306a36Sopenharmony_ci continue; 744462306a36Sopenharmony_ci addrconf_ifdown(dev, true); 744562306a36Sopenharmony_ci } 744662306a36Sopenharmony_ci addrconf_ifdown(init_net.loopback_dev, true); 744762306a36Sopenharmony_ci 744862306a36Sopenharmony_ci rtnl_unlock(); 744962306a36Sopenharmony_ci 745062306a36Sopenharmony_ci destroy_workqueue(addrconf_wq); 745162306a36Sopenharmony_ci} 7452