Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/net/ipv6/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 188 kB image not shown  

Quelle  addrconf.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * IPv6 Address [auto]configuration
 * Linux INET6 implementation
 *
 * Authors:
 * Pedro Roque <roque@di.fc.ul.pt>
 * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
 */


/*
 * Changes:
 *
 * Janos Farkas : delete timer on ifdown
 * <chexum@bankinf.banki.hu>
 * Andi Kleen : kill double kfree on module
 * unload.
 * Maciej W. Rozycki : FDDI support
 * sekiya@USAGI : Don't send too many RS
 * packets.
 * yoshfuji@USAGI :       Fixed interval between DAD
 * packets.
 * YOSHIFUJI Hideaki @USAGI : improved accuracy of
 * address validation timer.
 * YOSHIFUJI Hideaki @USAGI : Privacy Extensions (RFC3041)
 * support.
 * Yuji SEKIYA @USAGI : Don't assign a same IPv6
 * address on a same interface.
 * YOSHIFUJI Hideaki @USAGI : ARCnet support
 * YOSHIFUJI Hideaki @USAGI : convert /proc/net/if_inet6 to
 * seq_file.
 * YOSHIFUJI Hideaki @USAGI : improved source address
 * selection; consider scope,
 * status etc.
 */


#define pr_fmt(fmt) "IPv6: " fmt

#include <linux/errno.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched/signal.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/inet.h>
#include <linux/in6.h>
#include <linux/netdevice.h>
#include <linux/if_addr.h>
#include <linux/if_arp.h>
#include <linux/if_arcnet.h>
#include <linux/if_infiniband.h>
#include <linux/route.h>
#include <linux/inetdevice.h>
#include <linux/init.h>
#include <linux/slab.h>
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif
#include <linux/capability.h>
#include <linux/delay.h>
#include <linux/notifier.h>
#include <linux/string.h>
#include <linux/hash.h>

#include <net/ip_tunnels.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/snmp.h>

#include <net/6lowpan.h>
#include <net/firewire.h>
#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/ndisc.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/tcp.h>
#include <net/ip.h>
#include <net/netlink.h>
#include <net/pkt_sched.h>
#include <net/l3mdev.h>
#include <net/netdev_lock.h>
#include <linux/if_tunnel.h>
#include <linux/rtnetlink.h>
#include <linux/netconf.h>
#include <linux/random.h>
#include <linux/uaccess.h>
#include <linux/unaligned.h>

#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/export.h>
#include <linux/ioam6.h>

#define IPV6_MAX_STRLEN \
 sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")

static inline u32 cstamp_delta(unsigned long cstamp)
{
 return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
}

static inline s32 rfc3315_s14_backoff_init(s32 irt)
{
 /* multiply 'initial retransmission time' by 0.9 .. 1.1 */
 u64 tmp = get_random_u32_inclusive(900000, 1100000) * (u64)irt;
 do_div(tmp, 1000000);
 return (s32)tmp;
}

static inline s32 rfc3315_s14_backoff_update(s32 rt, s32 mrt)
{
 /* multiply 'retransmission timeout' by 1.9 .. 2.1 */
 u64 tmp = get_random_u32_inclusive(1900000, 2100000) * (u64)rt;
 do_div(tmp, 1000000);
 if ((s32)tmp > mrt) {
  /* multiply 'maximum retransmission time' by 0.9 .. 1.1 */
  tmp = get_random_u32_inclusive(900000, 1100000) * (u64)mrt;
  do_div(tmp, 1000000);
 }
 return (s32)tmp;
}

#ifdef CONFIG_SYSCTL
static int addrconf_sysctl_register(struct inet6_dev *idev);
static void addrconf_sysctl_unregister(struct inet6_dev *idev);
#else
static inline int addrconf_sysctl_register(struct inet6_dev *idev)
{
 return 0;
}

static inline void addrconf_sysctl_unregister(struct inet6_dev *idev)
{
}
#endif

static void ipv6_gen_rnd_iid(struct in6_addr *addr);

static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
static int ipv6_count_addresses(const struct inet6_dev *idev);
static int ipv6_generate_stable_address(struct in6_addr *addr,
     u8 dad_count,
     const struct inet6_dev *idev);

#define IN6_ADDR_HSIZE_SHIFT 8
#define IN6_ADDR_HSIZE  (1 << IN6_ADDR_HSIZE_SHIFT)

static void addrconf_verify(struct net *net);
static void addrconf_verify_rtnl(struct net *net);

static struct workqueue_struct *addrconf_wq;

static void addrconf_join_anycast(struct inet6_ifaddr *ifp);
static void addrconf_leave_anycast(struct inet6_ifaddr *ifp);

static void addrconf_type_change(struct net_device *dev,
     unsigned long event);
static int addrconf_ifdown(struct net_device *dev, bool unregister);

static struct fib6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
        int plen,
        const struct net_device *dev,
        u32 flags, u32 noflags,
        bool no_gw);

static void addrconf_dad_start(struct inet6_ifaddr *ifp);
static void addrconf_dad_work(struct work_struct *w);
static void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id,
       bool send_na);
static void addrconf_dad_run(struct inet6_dev *idev, bool restart);
static void addrconf_rs_timer(struct timer_list *t);
static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);

static void inet6_prefix_notify(int event, struct inet6_dev *idev,
    struct prefix_info *pinfo);

static struct ipv6_devconf ipv6_devconf __read_mostly = {
 .forwarding  = 0,
 .hop_limit  = IPV6_DEFAULT_HOPLIMIT,
 .mtu6   = IPV6_MIN_MTU,
 .accept_ra  = 1,
 .accept_redirects = 1,
 .autoconf  = 1,
 .force_mld_version = 0,
 .mldv1_unsolicited_report_interval = 10 * HZ,
 .mldv2_unsolicited_report_interval = HZ,
 .dad_transmits  = 1,
 .rtr_solicits  = MAX_RTR_SOLICITATIONS,
 .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL,
 .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL,
 .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY,
 .use_tempaddr  = 0,
 .temp_valid_lft  = TEMP_VALID_LIFETIME,
 .temp_prefered_lft = TEMP_PREFERRED_LIFETIME,
 .regen_min_advance = REGEN_MIN_ADVANCE,
 .regen_max_retry = REGEN_MAX_RETRY,
 .max_desync_factor = MAX_DESYNC_FACTOR,
 .max_addresses  = IPV6_MAX_ADDRESSES,
 .accept_ra_defrtr = 1,
 .ra_defrtr_metric = IP6_RT_PRIO_USER,
 .accept_ra_from_local = 0,
 .accept_ra_min_hop_limit= 1,
 .accept_ra_min_lft = 0,
 .accept_ra_pinfo = 1,
#ifdef CONFIG_IPV6_ROUTER_PREF
 .accept_ra_rtr_pref = 1,
 .rtr_probe_interval = 60 * HZ,
#ifdef CONFIG_IPV6_ROUTE_INFO
 .accept_ra_rt_info_min_plen = 0,
 .accept_ra_rt_info_max_plen = 0,
#endif
#endif
 .proxy_ndp  = 0,
 .accept_source_route = 0, /* we do not accept RH0 by default. */
 .disable_ipv6  = 0,
 .accept_dad  = 0,
 .suppress_frag_ndisc = 1,
 .accept_ra_mtu  = 1,
 .stable_secret  = {
  .initialized = false,
 },
 .use_oif_addrs_only = 0,
 .ignore_routes_with_linkdown = 0,
 .keep_addr_on_down = 0,
 .seg6_enabled  = 0,
#ifdef CONFIG_IPV6_SEG6_HMAC
 .seg6_require_hmac = 0,
#endif
 .enhanced_dad           = 1,
 .addr_gen_mode  = IN6_ADDR_GEN_MODE_EUI64,
 .disable_policy  = 0,
 .rpl_seg_enabled = 0,
 .ioam6_enabled  = 0,
 .ioam6_id               = IOAM6_DEFAULT_IF_ID,
 .ioam6_id_wide  = IOAM6_DEFAULT_IF_ID_WIDE,
 .ndisc_evict_nocarrier = 1,
 .ra_honor_pio_life = 0,
 .ra_honor_pio_pflag = 0,
 .force_forwarding = 0,
};

static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
 .forwarding  = 0,
 .hop_limit  = IPV6_DEFAULT_HOPLIMIT,
 .mtu6   = IPV6_MIN_MTU,
 .accept_ra  = 1,
 .accept_redirects = 1,
 .autoconf  = 1,
 .force_mld_version = 0,
 .mldv1_unsolicited_report_interval = 10 * HZ,
 .mldv2_unsolicited_report_interval = HZ,
 .dad_transmits  = 1,
 .rtr_solicits  = MAX_RTR_SOLICITATIONS,
 .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL,
 .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL,
 .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY,
 .use_tempaddr  = 0,
 .temp_valid_lft  = TEMP_VALID_LIFETIME,
 .temp_prefered_lft = TEMP_PREFERRED_LIFETIME,
 .regen_min_advance = REGEN_MIN_ADVANCE,
 .regen_max_retry = REGEN_MAX_RETRY,
 .max_desync_factor = MAX_DESYNC_FACTOR,
 .max_addresses  = IPV6_MAX_ADDRESSES,
 .accept_ra_defrtr = 1,
 .ra_defrtr_metric = IP6_RT_PRIO_USER,
 .accept_ra_from_local = 0,
 .accept_ra_min_hop_limit= 1,
 .accept_ra_min_lft = 0,
 .accept_ra_pinfo = 1,
#ifdef CONFIG_IPV6_ROUTER_PREF
 .accept_ra_rtr_pref = 1,
 .rtr_probe_interval = 60 * HZ,
#ifdef CONFIG_IPV6_ROUTE_INFO
 .accept_ra_rt_info_min_plen = 0,
 .accept_ra_rt_info_max_plen = 0,
#endif
#endif
 .proxy_ndp  = 0,
 .accept_source_route = 0, /* we do not accept RH0 by default. */
 .disable_ipv6  = 0,
 .accept_dad  = 1,
 .suppress_frag_ndisc = 1,
 .accept_ra_mtu  = 1,
 .stable_secret  = {
  .initialized = false,
 },
 .use_oif_addrs_only = 0,
 .ignore_routes_with_linkdown = 0,
 .keep_addr_on_down = 0,
 .seg6_enabled  = 0,
#ifdef CONFIG_IPV6_SEG6_HMAC
 .seg6_require_hmac = 0,
#endif
 .enhanced_dad           = 1,
 .addr_gen_mode  = IN6_ADDR_GEN_MODE_EUI64,
 .disable_policy  = 0,
 .rpl_seg_enabled = 0,
 .ioam6_enabled  = 0,
 .ioam6_id               = IOAM6_DEFAULT_IF_ID,
 .ioam6_id_wide  = IOAM6_DEFAULT_IF_ID_WIDE,
 .ndisc_evict_nocarrier = 1,
 .ra_honor_pio_life = 0,
 .ra_honor_pio_pflag = 0,
 .force_forwarding = 0,
};

/* Check if link is ready: is it up and is a valid qdisc available */
static inline bool addrconf_link_ready(const struct net_device *dev)
{
 return netif_oper_up(dev) && !qdisc_tx_is_noop(dev);
}

static void addrconf_del_rs_timer(struct inet6_dev *idev)
{
 if (timer_delete(&idev->rs_timer))
  __in6_dev_put(idev);
}

static void addrconf_del_dad_work(struct inet6_ifaddr *ifp)
{
 if (cancel_delayed_work(&ifp->dad_work))
  __in6_ifa_put(ifp);
}

static void addrconf_mod_rs_timer(struct inet6_dev *idev,
      unsigned long when)
{
 if (!mod_timer(&idev->rs_timer, jiffies + when))
  in6_dev_hold(idev);
}

static void addrconf_mod_dad_work(struct inet6_ifaddr *ifp,
       unsigned long delay)
{
 in6_ifa_hold(ifp);
 if (mod_delayed_work(addrconf_wq, &ifp->dad_work, delay))
  in6_ifa_put(ifp);
}

static int snmp6_alloc_dev(struct inet6_dev *idev)
{
 int i;

 idev->stats.ipv6 = alloc_percpu_gfp(struct ipstats_mib, GFP_KERNEL_ACCOUNT);
 if (!idev->stats.ipv6)
  goto err_ip;

 for_each_possible_cpu(i) {
  struct ipstats_mib *addrconf_stats;
  addrconf_stats = per_cpu_ptr(idev->stats.ipv6, i);
  u64_stats_init(&addrconf_stats->syncp);
 }


 idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device),
     GFP_KERNEL);
 if (!idev->stats.icmpv6dev)
  goto err_icmp;
 idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device),
        GFP_KERNEL_ACCOUNT);
 if (!idev->stats.icmpv6msgdev)
  goto err_icmpmsg;

 return 0;

err_icmpmsg:
 kfree(idev->stats.icmpv6dev);
err_icmp:
 free_percpu(idev->stats.ipv6);
err_ip:
 return -ENOMEM;
}

static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
{
 struct inet6_dev *ndev;
 int err = -ENOMEM;

 ASSERT_RTNL();
 netdev_ops_assert_locked(dev);

 if (dev->mtu < IPV6_MIN_MTU && dev != blackhole_netdev)
  return ERR_PTR(-EINVAL);

 ndev = kzalloc(sizeof(*ndev), GFP_KERNEL_ACCOUNT);
 if (!ndev)
  return ERR_PTR(err);

 rwlock_init(&ndev->lock);
 ndev->dev = dev;
 INIT_LIST_HEAD(&ndev->addr_list);
 timer_setup(&ndev->rs_timer, addrconf_rs_timer, 0);
 memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));

 if (ndev->cnf.stable_secret.initialized)
  ndev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;

 ndev->cnf.mtu6 = dev->mtu;
 ndev->ra_mtu = 0;
 ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
 if (!ndev->nd_parms) {
  kfree(ndev);
  return ERR_PTR(err);
 }
 if (ndev->cnf.forwarding)
  netif_disable_lro(dev);
 /* We refer to the device */
 netdev_hold(dev, &ndev->dev_tracker, GFP_KERNEL);

 if (snmp6_alloc_dev(ndev) < 0) {
  netdev_dbg(dev, "%s: cannot allocate memory for statistics\n",
      __func__);
  neigh_parms_release(&nd_tbl, ndev->nd_parms);
  netdev_put(dev, &ndev->dev_tracker);
  kfree(ndev);
  return ERR_PTR(err);
 }

 if (dev != blackhole_netdev) {
  if (snmp6_register_dev(ndev) < 0) {
   netdev_dbg(dev, "%s: cannot create /proc/net/dev_snmp6/%s\n",
       __func__, dev->name);
   goto err_release;
  }
 }
 /* One reference from device. */
 refcount_set(&ndev->refcnt, 1);

 if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
  ndev->cnf.accept_dad = -1;

#if IS_ENABLED(CONFIG_IPV6_SIT)
 if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) {
  pr_info("%s: Disabled Multicast RS\n", dev->name);
  ndev->cnf.rtr_solicits = 0;
 }
#endif

 INIT_LIST_HEAD(&ndev->tempaddr_list);
 ndev->desync_factor = U32_MAX;
 if ((dev->flags&IFF_LOOPBACK) ||
     dev->type == ARPHRD_TUNNEL ||
     dev->type == ARPHRD_TUNNEL6 ||
     dev->type == ARPHRD_SIT ||
     dev->type == ARPHRD_NONE) {
  ndev->cnf.use_tempaddr = -1;
 }

 ndev->token = in6addr_any;

 if (netif_running(dev) && addrconf_link_ready(dev))
  ndev->if_flags |= IF_READY;

 ipv6_mc_init_dev(ndev);
 ndev->tstamp = jiffies;
 if (dev != blackhole_netdev) {
  err = addrconf_sysctl_register(ndev);
  if (err) {
   ipv6_mc_destroy_dev(ndev);
   snmp6_unregister_dev(ndev);
   goto err_release;
  }
 }
 /* protected by rtnl_lock */
 rcu_assign_pointer(dev->ip6_ptr, ndev);

 if (dev != blackhole_netdev) {
  /* Join interface-local all-node multicast group */
  ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allnodes);

  /* Join all-node multicast group */
  ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes);

  /* Join all-router multicast group if forwarding is set */
  if (ndev->cnf.forwarding && (dev->flags & IFF_MULTICAST))
   ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
 }
 return ndev;

err_release:
 neigh_parms_release(&nd_tbl, ndev->nd_parms);
 ndev->dead = 1;
 in6_dev_finish_destroy(ndev);
 return ERR_PTR(err);
}

static struct inet6_dev *ipv6_find_idev(struct net_device *dev)
{
 struct inet6_dev *idev;

 ASSERT_RTNL();

 idev = __in6_dev_get(dev);
 if (!idev) {
  idev = ipv6_add_dev(dev);
  if (IS_ERR(idev))
   return idev;
 }

 if (dev->flags&IFF_UP)
  ipv6_mc_up(idev);
 return idev;
}

static int inet6_netconf_msgsize_devconf(int type)
{
 int size =  NLMSG_ALIGN(sizeof(struct netconfmsg))
      + nla_total_size(4); /* NETCONFA_IFINDEX */
 bool all = false;

 if (type == NETCONFA_ALL)
  all = true;

 if (all || type == NETCONFA_FORWARDING)
  size += nla_total_size(4);
#ifdef CONFIG_IPV6_MROUTE
 if (all || type == NETCONFA_MC_FORWARDING)
  size += nla_total_size(4);
#endif
 if (all || type == NETCONFA_PROXY_NEIGH)
  size += nla_total_size(4);

 if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
  size += nla_total_size(4);

 return size;
}

static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
          struct ipv6_devconf *devconf, u32 portid,
          u32 seq, int event, unsigned int flags,
          int type)
{
 struct nlmsghdr  *nlh;
 struct netconfmsg *ncm;
 bool all = false;

 nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
   flags);
 if (!nlh)
  return -EMSGSIZE;

 if (type == NETCONFA_ALL)
  all = true;

 ncm = nlmsg_data(nlh);
 ncm->ncm_family = AF_INET6;

 if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
  goto nla_put_failure;

 if (!devconf)
  goto out;

 if ((all || type == NETCONFA_FORWARDING) &&
     nla_put_s32(skb, NETCONFA_FORWARDING,
   READ_ONCE(devconf->forwarding)) < 0)
  goto nla_put_failure;
#ifdef CONFIG_IPV6_MROUTE
 if ((all || type == NETCONFA_MC_FORWARDING) &&
     nla_put_s32(skb, NETCONFA_MC_FORWARDING,
   atomic_read(&devconf->mc_forwarding)) < 0)
  goto nla_put_failure;
#endif
 if ((all || type == NETCONFA_PROXY_NEIGH) &&
     nla_put_s32(skb, NETCONFA_PROXY_NEIGH,
   READ_ONCE(devconf->proxy_ndp)) < 0)
  goto nla_put_failure;

 if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
     nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
   READ_ONCE(devconf->ignore_routes_with_linkdown)) < 0)
  goto nla_put_failure;

out:
 nlmsg_end(skb, nlh);
 return 0;

nla_put_failure:
 nlmsg_cancel(skb, nlh);
 return -EMSGSIZE;
}

void inet6_netconf_notify_devconf(struct net *net, int event, int type,
      int ifindex, struct ipv6_devconf *devconf)
{
 struct sk_buff *skb;
 int err = -ENOBUFS;

 skb = nlmsg_new(inet6_netconf_msgsize_devconf(type), GFP_KERNEL);
 if (!skb)
  goto errout;

 err = inet6_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
      event, 0, type);
 if (err < 0) {
  /* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */
  WARN_ON(err == -EMSGSIZE);
  kfree_skb(skb);
  goto errout;
 }
 rtnl_notify(skb, net, 0, RTNLGRP_IPV6_NETCONF, NULL, GFP_KERNEL);
 return;
errout:
 rtnl_set_sk_err(net, RTNLGRP_IPV6_NETCONF, err);
}

static const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
 [NETCONFA_IFINDEX] = { .len = sizeof(int) },
 [NETCONFA_FORWARDING] = { .len = sizeof(int) },
 [NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) },
 [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .len = sizeof(int) },
};

static int inet6_netconf_valid_get_req(struct sk_buff *skb,
           const struct nlmsghdr *nlh,
           struct nlattr **tb,
           struct netlink_ext_ack *extack)
{
 int i, err;

 if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
  NL_SET_ERR_MSG_MOD(extack, "Invalid header for netconf get request");
  return -EINVAL;
 }

 if (!netlink_strict_get_check(skb))
  return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
           tb, NETCONFA_MAX,
           devconf_ipv6_policy, extack);

 err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
         tb, NETCONFA_MAX,
         devconf_ipv6_policy, extack);
 if (err)
  return err;

 for (i = 0; i <= NETCONFA_MAX; i++) {
  if (!tb[i])
   continue;

  switch (i) {
  case NETCONFA_IFINDEX:
   break;
  default:
   NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in netconf get request");
   return -EINVAL;
  }
 }

 return 0;
}

static int inet6_netconf_get_devconf(struct sk_buff *in_skb,
         struct nlmsghdr *nlh,
         struct netlink_ext_ack *extack)
{
 struct net *net = sock_net(in_skb->sk);
 struct nlattr *tb[NETCONFA_MAX+1];
 struct inet6_dev *in6_dev = NULL;
 struct net_device *dev = NULL;
 struct sk_buff *skb;
 struct ipv6_devconf *devconf;
 int ifindex;
 int err;

 err = inet6_netconf_valid_get_req(in_skb, nlh, tb, extack);
 if (err < 0)
  return err;

 if (!tb[NETCONFA_IFINDEX])
  return -EINVAL;

 err = -EINVAL;
 ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
 switch (ifindex) {
 case NETCONFA_IFINDEX_ALL:
  devconf = net->ipv6.devconf_all;
  break;
 case NETCONFA_IFINDEX_DEFAULT:
  devconf = net->ipv6.devconf_dflt;
  break;
 default:
  dev = dev_get_by_index(net, ifindex);
  if (!dev)
   return -EINVAL;
  in6_dev = in6_dev_get(dev);
  if (!in6_dev)
   goto errout;
  devconf = &in6_dev->cnf;
  break;
 }

 err = -ENOBUFS;
 skb = nlmsg_new(inet6_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
 if (!skb)
  goto errout;

 err = inet6_netconf_fill_devconf(skb, ifindex, devconf,
      NETLINK_CB(in_skb).portid,
      nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
      NETCONFA_ALL);
 if (err < 0) {
  /* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */
  WARN_ON(err == -EMSGSIZE);
  kfree_skb(skb);
  goto errout;
 }
 err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
errout:
 if (in6_dev)
  in6_dev_put(in6_dev);
 dev_put(dev);
 return err;
}

/* Combine dev_addr_genid and dev_base_seq to detect changes.
 */

static u32 inet6_base_seq(const struct net *net)
{
 u32 res = atomic_read(&net->ipv6.dev_addr_genid) +
    READ_ONCE(net->dev_base_seq);

 /* Must not return 0 (see nl_dump_check_consistent()).
 * Chose a value far away from 0.
 */

 if (!res)
  res = 0x80000000;
 return res;
}

static int inet6_netconf_dump_devconf(struct sk_buff *skb,
          struct netlink_callback *cb)
{
 const struct nlmsghdr *nlh = cb->nlh;
 struct net *net = sock_net(skb->sk);
 struct {
  unsigned long ifindex;
  unsigned int all_default;
 } *ctx = (void *)cb->ctx;
 struct net_device *dev;
 struct inet6_dev *idev;
 int err = 0;

 if (cb->strict_check) {
  struct netlink_ext_ack *extack = cb->extack;
  struct netconfmsg *ncm;

  if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
   NL_SET_ERR_MSG_MOD(extack, "Invalid header for netconf dump request");
   return -EINVAL;
  }

  if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
   NL_SET_ERR_MSG_MOD(extack, "Invalid data after header in netconf dump request");
   return -EINVAL;
  }
 }

 rcu_read_lock();
 for_each_netdev_dump(net, dev, ctx->ifindex) {
  idev = __in6_dev_get(dev);
  if (!idev)
   continue;
  err = inet6_netconf_fill_devconf(skb, dev->ifindex,
              &idev->cnf,
       NETLINK_CB(cb->skb).portid,
       nlh->nlmsg_seq,
       RTM_NEWNETCONF,
       NLM_F_MULTI,
       NETCONFA_ALL);
  if (err < 0)
   goto done;
 }
 if (ctx->all_default == 0) {
  err = inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
       net->ipv6.devconf_all,
       NETLINK_CB(cb->skb).portid,
       nlh->nlmsg_seq,
       RTM_NEWNETCONF, NLM_F_MULTI,
       NETCONFA_ALL);
  if (err < 0)
   goto done;
  ctx->all_default++;
 }
 if (ctx->all_default == 1) {
  err = inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
       net->ipv6.devconf_dflt,
       NETLINK_CB(cb->skb).portid,
       nlh->nlmsg_seq,
       RTM_NEWNETCONF, NLM_F_MULTI,
       NETCONFA_ALL);
  if (err < 0)
   goto done;
  ctx->all_default++;
 }
done:
 rcu_read_unlock();
 return err;
}

#ifdef CONFIG_SYSCTL
static void dev_forward_change(struct inet6_dev *idev)
{
 struct net_device *dev;
 struct inet6_ifaddr *ifa;
 LIST_HEAD(tmp_addr_list);

 if (!idev)
  return;
 dev = idev->dev;
 if (idev->cnf.forwarding)
  dev_disable_lro(dev);
 if (dev->flags & IFF_MULTICAST) {
  if (idev->cnf.forwarding) {
   ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
   ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allrouters);
   ipv6_dev_mc_inc(dev, &in6addr_sitelocal_allrouters);
  } else {
   ipv6_dev_mc_dec(dev, &in6addr_linklocal_allrouters);
   ipv6_dev_mc_dec(dev, &in6addr_interfacelocal_allrouters);
   ipv6_dev_mc_dec(dev, &in6addr_sitelocal_allrouters);
  }
 }

 read_lock_bh(&idev->lock);
 list_for_each_entry(ifa, &idev->addr_list, if_list) {
  if (ifa->flags&IFA_F_TENTATIVE)
   continue;
  list_add_tail(&ifa->if_list_aux, &tmp_addr_list);
 }
 read_unlock_bh(&idev->lock);

 while (!list_empty(&tmp_addr_list)) {
  ifa = list_first_entry(&tmp_addr_list,
           struct inet6_ifaddr, if_list_aux);
  list_del(&ifa->if_list_aux);
  if (idev->cnf.forwarding)
   addrconf_join_anycast(ifa);
  else
   addrconf_leave_anycast(ifa);
 }

 inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,
         NETCONFA_FORWARDING,
         dev->ifindex, &idev->cnf);
}


static void addrconf_forward_change(struct net *net, __s32 newf)
{
 struct net_device *dev;
 struct inet6_dev *idev;

 for_each_netdev(net, dev) {
  idev = __in6_dev_get_rtnl_net(dev);
  if (idev) {
   int changed = (!idev->cnf.forwarding) ^ (!newf);
   /* Disabling all.forwarding sets 0 to force_forwarding for all interfaces */
   if (newf == 0)
    WRITE_ONCE(idev->cnf.force_forwarding, 0);

   WRITE_ONCE(idev->cnf.forwarding, newf);
   if (changed)
    dev_forward_change(idev);
  }
 }
}

static int addrconf_fixup_forwarding(const struct ctl_table *table, int *p, int newf)
{
 struct net *net = (struct net *)table->extra2;
 int old;

 if (!rtnl_net_trylock(net))
  return restart_syscall();

 old = *p;
 WRITE_ONCE(*p, newf);

 if (p == &net->ipv6.devconf_dflt->forwarding) {
  if ((!newf) ^ (!old))
   inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
           NETCONFA_FORWARDING,
           NETCONFA_IFINDEX_DEFAULT,
           net->ipv6.devconf_dflt);
  rtnl_net_unlock(net);
  return 0;
 }

 if (p == &net->ipv6.devconf_all->forwarding) {
  int old_dflt = net->ipv6.devconf_dflt->forwarding;

  WRITE_ONCE(net->ipv6.devconf_dflt->forwarding, newf);
  if ((!newf) ^ (!old_dflt))
   inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
           NETCONFA_FORWARDING,
           NETCONFA_IFINDEX_DEFAULT,
           net->ipv6.devconf_dflt);

  addrconf_forward_change(net, newf);
  if ((!newf) ^ (!old))
   inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
           NETCONFA_FORWARDING,
           NETCONFA_IFINDEX_ALL,
           net->ipv6.devconf_all);
 } else if ((!newf) ^ (!old))
  dev_forward_change((struct inet6_dev *)table->extra1);
 rtnl_net_unlock(net);

 if (newf)
  rt6_purge_dflt_routers(net);
 return 1;
}

static void addrconf_linkdown_change(struct net *net, __s32 newf)
{
 struct net_device *dev;
 struct inet6_dev *idev;

 for_each_netdev(net, dev) {
  idev = __in6_dev_get_rtnl_net(dev);
  if (idev) {
   int changed = (!idev->cnf.ignore_routes_with_linkdown) ^ (!newf);

   WRITE_ONCE(idev->cnf.ignore_routes_with_linkdown, newf);
   if (changed)
    inet6_netconf_notify_devconf(dev_net(dev),
            RTM_NEWNETCONF,
            NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
            dev->ifindex,
            &idev->cnf);
  }
 }
}

static int addrconf_fixup_linkdown(const struct ctl_table *table, int *p, int newf)
{
 struct net *net = (struct net *)table->extra2;
 int old;

 if (!rtnl_net_trylock(net))
  return restart_syscall();

 old = *p;
 WRITE_ONCE(*p, newf);

 if (p == &net->ipv6.devconf_dflt->ignore_routes_with_linkdown) {
  if ((!newf) ^ (!old))
   inet6_netconf_notify_devconf(net,
           RTM_NEWNETCONF,
           NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
           NETCONFA_IFINDEX_DEFAULT,
           net->ipv6.devconf_dflt);
  rtnl_net_unlock(net);
  return 0;
 }

 if (p == &net->ipv6.devconf_all->ignore_routes_with_linkdown) {
  WRITE_ONCE(net->ipv6.devconf_dflt->ignore_routes_with_linkdown, newf);
  addrconf_linkdown_change(net, newf);
  if ((!newf) ^ (!old))
   inet6_netconf_notify_devconf(net,
           RTM_NEWNETCONF,
           NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
           NETCONFA_IFINDEX_ALL,
           net->ipv6.devconf_all);
 }

 rtnl_net_unlock(net);

 return 1;
}

#endif

/* Nobody refers to this ifaddr, destroy it */
void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
{
 WARN_ON(!hlist_unhashed(&ifp->addr_lst));

#ifdef NET_REFCNT_DEBUG
 pr_debug("%s\n", __func__);
#endif

 in6_dev_put(ifp->idev);

 if (cancel_delayed_work(&ifp->dad_work))
  pr_notice("delayed DAD work was pending while freeing ifa=%p\n",
     ifp);

 if (ifp->state != INET6_IFADDR_STATE_DEAD) {
  pr_warn("Freeing alive inet6 address %p\n", ifp);
  return;
 }

 kfree_rcu(ifp, rcu);
}

static void
ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp)
{
 struct list_head *p;
 int ifp_scope = ipv6_addr_src_scope(&ifp->addr);

 /*
 * Each device address list is sorted in order of scope -
 * global before linklocal.
 */

 list_for_each(p, &idev->addr_list) {
  struct inet6_ifaddr *ifa
   = list_entry(p, struct inet6_ifaddr, if_list);
  if (ifp_scope >= ipv6_addr_src_scope(&ifa->addr))
   break;
 }

 list_add_tail_rcu(&ifp->if_list, p);
}

static u32 inet6_addr_hash(const struct net *net, const struct in6_addr *addr)
{
 u32 val = __ipv6_addr_jhash(addr, net_hash_mix(net));

 return hash_32(val, IN6_ADDR_HSIZE_SHIFT);
}

static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr,
          struct net_device *dev, unsigned int hash)
{
 struct inet6_ifaddr *ifp;

 hlist_for_each_entry(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) {
  if (ipv6_addr_equal(&ifp->addr, addr)) {
   if (!dev || ifp->idev->dev == dev)
    return true;
  }
 }
 return false;
}

static int ipv6_add_addr_hash(struct net_device *dev, struct inet6_ifaddr *ifa)
{
 struct net *net = dev_net(dev);
 unsigned int hash = inet6_addr_hash(net, &ifa->addr);
 int err = 0;

 spin_lock_bh(&net->ipv6.addrconf_hash_lock);

 /* Ignore adding duplicate addresses on an interface */
 if (ipv6_chk_same_addr(net, &ifa->addr, dev, hash)) {
  netdev_dbg(dev, "ipv6_add_addr: already assigned\n");
  err = -EEXIST;
 } else {
  hlist_add_head_rcu(&ifa->addr_lst, &net->ipv6.inet6_addr_lst[hash]);
 }

 spin_unlock_bh(&net->ipv6.addrconf_hash_lock);

 return err;
}

/* On success it returns ifp with increased reference count */

static struct inet6_ifaddr *
ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg,
       bool can_block, struct netlink_ext_ack *extack)
{
 gfp_t gfp_flags = can_block ? GFP_KERNEL : GFP_ATOMIC;
 int addr_type = ipv6_addr_type(cfg->pfx);
 struct net *net = dev_net(idev->dev);
 struct inet6_ifaddr *ifa = NULL;
 struct fib6_info *f6i = NULL;
 int err = 0;

 if (addr_type == IPV6_ADDR_ANY) {
  NL_SET_ERR_MSG_MOD(extack, "Invalid address");
  return ERR_PTR(-EADDRNOTAVAIL);
 } else if (addr_type & IPV6_ADDR_MULTICAST &&
     !(cfg->ifa_flags & IFA_F_MCAUTOJOIN)) {
  NL_SET_ERR_MSG_MOD(extack, "Cannot assign multicast address without \"IFA_F_MCAUTOJOIN\" flag");
  return ERR_PTR(-EADDRNOTAVAIL);
 } else if (!(idev->dev->flags & IFF_LOOPBACK) &&
     !netif_is_l3_master(idev->dev) &&
     addr_type & IPV6_ADDR_LOOPBACK) {
  NL_SET_ERR_MSG_MOD(extack, "Cannot assign loopback address on this device");
  return ERR_PTR(-EADDRNOTAVAIL);
 }

 if (idev->dead) {
  NL_SET_ERR_MSG_MOD(extack, "device is going away");
  err = -ENODEV;
  goto out;
 }

 if (idev->cnf.disable_ipv6) {
  NL_SET_ERR_MSG_MOD(extack, "IPv6 is disabled on this device");
  err = -EACCES;
  goto out;
 }

 /* validator notifier needs to be blocking;
 * do not call in atomic context
 */

 if (can_block) {
  struct in6_validator_info i6vi = {
   .i6vi_addr = *cfg->pfx,
   .i6vi_dev = idev,
   .extack = extack,
  };

  err = inet6addr_validator_notifier_call_chain(NETDEV_UP, &i6vi);
  err = notifier_to_errno(err);
  if (err < 0)
   goto out;
 }

 ifa = kzalloc(sizeof(*ifa), gfp_flags | __GFP_ACCOUNT);
 if (!ifa) {
  err = -ENOBUFS;
  goto out;
 }

 f6i = addrconf_f6i_alloc(net, idev, cfg->pfx, false, gfp_flags, extack);
 if (IS_ERR(f6i)) {
  err = PTR_ERR(f6i);
  f6i = NULL;
  goto out;
 }

 neigh_parms_data_state_setall(idev->nd_parms);

 ifa->addr = *cfg->pfx;
 if (cfg->peer_pfx)
  ifa->peer_addr = *cfg->peer_pfx;

 spin_lock_init(&ifa->lock);
 INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work);
 INIT_HLIST_NODE(&ifa->addr_lst);
 ifa->scope = cfg->scope;
 ifa->prefix_len = cfg->plen;
 ifa->rt_priority = cfg->rt_priority;
 ifa->flags = cfg->ifa_flags;
 ifa->ifa_proto = cfg->ifa_proto;
 /* No need to add the TENTATIVE flag for addresses with NODAD */
 if (!(cfg->ifa_flags & IFA_F_NODAD))
  ifa->flags |= IFA_F_TENTATIVE;
 ifa->valid_lft = cfg->valid_lft;
 ifa->prefered_lft = cfg->preferred_lft;
 ifa->cstamp = ifa->tstamp = jiffies;
 ifa->tokenized = false;

 ifa->rt = f6i;

 ifa->idev = idev;
 in6_dev_hold(idev);

 /* For caller */
 refcount_set(&ifa->refcnt, 1);

 rcu_read_lock();

 err = ipv6_add_addr_hash(idev->dev, ifa);
 if (err < 0) {
  rcu_read_unlock();
  goto out;
 }

 write_lock_bh(&idev->lock);

 /* Add to inet6_dev unicast addr list. */
 ipv6_link_dev_addr(idev, ifa);

 if (ifa->flags&IFA_F_TEMPORARY) {
  list_add(&ifa->tmp_list, &idev->tempaddr_list);
  in6_ifa_hold(ifa);
 }

 in6_ifa_hold(ifa);
 write_unlock_bh(&idev->lock);

 rcu_read_unlock();

 inet6addr_notifier_call_chain(NETDEV_UP, ifa);
out:
 if (unlikely(err < 0)) {
  fib6_info_release(f6i);

  if (ifa) {
   if (ifa->idev)
    in6_dev_put(ifa->idev);
   kfree(ifa);
  }
  ifa = ERR_PTR(err);
 }

 return ifa;
}

enum cleanup_prefix_rt_t {
 CLEANUP_PREFIX_RT_NOP,    /* no cleanup action for prefix route */
 CLEANUP_PREFIX_RT_DEL,    /* delete the prefix route */
 CLEANUP_PREFIX_RT_EXPIRE, /* update the lifetime of the prefix route */
};

/*
 * Check, whether the prefix for ifp would still need a prefix route
 * after deleting ifp. The function returns one of the CLEANUP_PREFIX_RT_*
 * constants.
 *
 * 1) we don't purge prefix if address was not permanent.
 *    prefix is managed by its own lifetime.
 * 2) we also don't purge, if the address was IFA_F_NOPREFIXROUTE.
 * 3) if there are no addresses, delete prefix.
 * 4) if there are still other permanent address(es),
 *    corresponding prefix is still permanent.
 * 5) if there are still other addresses with IFA_F_NOPREFIXROUTE,
 *    don't purge the prefix, assume user space is managing it.
 * 6) otherwise, update prefix lifetime to the
 *    longest valid lifetime among the corresponding
 *    addresses on the device.
 *    Note: subsequent RA will update lifetime.
 **/

static enum cleanup_prefix_rt_t
check_cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long *expires)
{
 struct inet6_ifaddr *ifa;
 struct inet6_dev *idev = ifp->idev;
 unsigned long lifetime;
 enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_DEL;

 *expires = jiffies;

 list_for_each_entry(ifa, &idev->addr_list, if_list) {
  if (ifa == ifp)
   continue;
  if (ifa->prefix_len != ifp->prefix_len ||
      !ipv6_prefix_equal(&ifa->addr, &ifp->addr,
           ifp->prefix_len))
   continue;
  if (ifa->flags & (IFA_F_PERMANENT | IFA_F_NOPREFIXROUTE))
   return CLEANUP_PREFIX_RT_NOP;

  action = CLEANUP_PREFIX_RT_EXPIRE;

  spin_lock(&ifa->lock);

  lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ);
  /*
 * Note: Because this address is
 * not permanent, lifetime <
 * LONG_MAX / HZ here.
 */

  if (time_before(*expires, ifa->tstamp + lifetime * HZ))
   *expires = ifa->tstamp + lifetime * HZ;
  spin_unlock(&ifa->lock);
 }

 return action;
}

static void
cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires,
       bool del_rt, bool del_peer)
{
 struct fib6_table *table;
 struct fib6_info *f6i;

 f6i = addrconf_get_prefix_route(del_peer ? &ifp->peer_addr : &ifp->addr,
     ifp->prefix_len,
     ifp->idev->dev, 0, RTF_DEFAULT, true);
 if (f6i) {
  if (del_rt)
   ip6_del_rt(dev_net(ifp->idev->dev), f6i, false);
  else {
   if (!(f6i->fib6_flags & RTF_EXPIRES)) {
    table = f6i->fib6_table;
    spin_lock_bh(&table->tb6_lock);

    fib6_set_expires(f6i, expires);
    fib6_add_gc_list(f6i);

    spin_unlock_bh(&table->tb6_lock);
   }
   fib6_info_release(f6i);
  }
 }
}


/* This function wants to get referenced ifp and releases it before return */

static void ipv6_del_addr(struct inet6_ifaddr *ifp)
{
 enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_NOP;
 struct net *net = dev_net(ifp->idev->dev);
 unsigned long expires;
 int state;

 ASSERT_RTNL();

 spin_lock_bh(&ifp->lock);
 state = ifp->state;
 ifp->state = INET6_IFADDR_STATE_DEAD;
 spin_unlock_bh(&ifp->lock);

 if (state == INET6_IFADDR_STATE_DEAD)
  goto out;

 spin_lock_bh(&net->ipv6.addrconf_hash_lock);
 hlist_del_init_rcu(&ifp->addr_lst);
 spin_unlock_bh(&net->ipv6.addrconf_hash_lock);

 write_lock_bh(&ifp->idev->lock);

 if (ifp->flags&IFA_F_TEMPORARY) {
  list_del(&ifp->tmp_list);
  if (ifp->ifpub) {
   in6_ifa_put(ifp->ifpub);
   ifp->ifpub = NULL;
  }
  __in6_ifa_put(ifp);
 }

 if (ifp->flags & IFA_F_PERMANENT && !(ifp->flags & IFA_F_NOPREFIXROUTE))
  action = check_cleanup_prefix_route(ifp, &expires);

 list_del_rcu(&ifp->if_list);
 __in6_ifa_put(ifp);

 write_unlock_bh(&ifp->idev->lock);

 addrconf_del_dad_work(ifp);

 ipv6_ifa_notify(RTM_DELADDR, ifp);

 inet6addr_notifier_call_chain(NETDEV_DOWN, ifp);

 if (action != CLEANUP_PREFIX_RT_NOP) {
  cleanup_prefix_route(ifp, expires,
   action == CLEANUP_PREFIX_RT_DEL, false);
 }

 /* clean up prefsrc entries */
 rt6_remove_prefsrc(ifp);
out:
 in6_ifa_put(ifp);
}

static unsigned long ipv6_get_regen_advance(const struct inet6_dev *idev)
{
 return READ_ONCE(idev->cnf.regen_min_advance) +
  READ_ONCE(idev->cnf.regen_max_retry) *
  READ_ONCE(idev->cnf.dad_transmits) *
  max(NEIGH_VAR(idev->nd_parms, RETRANS_TIME), HZ/100) / HZ;
}

static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
{
 struct inet6_dev *idev = ifp->idev;
 unsigned long tmp_tstamp, age;
 unsigned long regen_advance;
 unsigned long now = jiffies;
 u32 if_public_preferred_lft;
 s32 cnf_temp_preferred_lft;
 struct inet6_ifaddr *ift;
 struct ifa6_config cfg;
 long max_desync_factor;
 struct in6_addr addr;
 int ret = 0;

 write_lock_bh(&idev->lock);

retry:
 in6_dev_hold(idev);
 if (READ_ONCE(idev->cnf.use_tempaddr) <= 0) {
  write_unlock_bh(&idev->lock);
  pr_info("%s: use_tempaddr is disabled\n", __func__);
  in6_dev_put(idev);
  ret = -1;
  goto out;
 }
 spin_lock_bh(&ifp->lock);
 if (ifp->regen_count++ >= READ_ONCE(idev->cnf.regen_max_retry)) {
  WRITE_ONCE(idev->cnf.use_tempaddr, -1); /*XXX*/
  spin_unlock_bh(&ifp->lock);
  write_unlock_bh(&idev->lock);
  pr_warn("%s: regeneration time exceeded - disabled temporary address support\n",
   __func__);
  in6_dev_put(idev);
  ret = -1;
  goto out;
 }
 in6_ifa_hold(ifp);
 memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
 ipv6_gen_rnd_iid(&addr);

 age = (now - ifp->tstamp) / HZ;

 regen_advance = ipv6_get_regen_advance(idev);

 /* recalculate max_desync_factor each time and update
 * idev->desync_factor if it's larger
 */

 cnf_temp_preferred_lft = READ_ONCE(idev->cnf.temp_prefered_lft);
 max_desync_factor = min_t(long,
      READ_ONCE(idev->cnf.max_desync_factor),
      cnf_temp_preferred_lft - regen_advance);

 if (unlikely(idev->desync_factor > max_desync_factor)) {
  if (max_desync_factor > 0) {
   get_random_bytes(&idev->desync_factor,
      sizeof(idev->desync_factor));
   idev->desync_factor %= max_desync_factor;
  } else {
   idev->desync_factor = 0;
  }
 }

 if_public_preferred_lft = ifp->prefered_lft;

 memset(&cfg, 0, sizeof(cfg));
 cfg.valid_lft = min_t(__u32, ifp->valid_lft,
         READ_ONCE(idev->cnf.temp_valid_lft) + age);
 cfg.preferred_lft = cnf_temp_preferred_lft + age - idev->desync_factor;
 cfg.preferred_lft = min_t(__u32, if_public_preferred_lft, cfg.preferred_lft);
 cfg.preferred_lft = min_t(__u32, cfg.valid_lft, cfg.preferred_lft);

 cfg.plen = ifp->prefix_len;
 tmp_tstamp = ifp->tstamp;
 spin_unlock_bh(&ifp->lock);

 write_unlock_bh(&idev->lock);

 /* From RFC 4941:
 *
 *     A temporary address is created only if this calculated Preferred
 *     Lifetime is greater than REGEN_ADVANCE time units.  In
 *     particular, an implementation must not create a temporary address
 *     with a zero Preferred Lifetime.
 *
 *     ...
 *
 *     When creating a temporary address, the lifetime values MUST be
 *     derived from the corresponding prefix as follows:
 *
 *     ...
 *
 *     *  Its Preferred Lifetime is the lower of the Preferred Lifetime
 *        of the public address or TEMP_PREFERRED_LIFETIME -
 *        DESYNC_FACTOR.
 *
 * To comply with the RFC's requirements, clamp the preferred lifetime
 * to a minimum of regen_advance, unless that would exceed valid_lft or
 * ifp->prefered_lft.
 *
 * Use age calculation as in addrconf_verify to avoid unnecessary
 * temporary addresses being generated.
 */

 age = (now - tmp_tstamp + ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
 if (cfg.preferred_lft <= regen_advance + age) {
  cfg.preferred_lft = regen_advance + age + 1;
  if (cfg.preferred_lft > cfg.valid_lft ||
      cfg.preferred_lft > if_public_preferred_lft) {
   in6_ifa_put(ifp);
   in6_dev_put(idev);
   ret = -1;
   goto out;
  }
 }

 cfg.ifa_flags = IFA_F_TEMPORARY;
 /* set in addrconf_prefix_rcv() */
 if (ifp->flags & IFA_F_OPTIMISTIC)
  cfg.ifa_flags |= IFA_F_OPTIMISTIC;

 cfg.pfx = &addr;
 cfg.scope = ipv6_addr_scope(cfg.pfx);

 ift = ipv6_add_addr(idev, &cfg, block, NULL);
 if (IS_ERR(ift)) {
  in6_ifa_put(ifp);
  in6_dev_put(idev);
  pr_info("%s: retry temporary address regeneration\n", __func__);
  write_lock_bh(&idev->lock);
  goto retry;
 }

 spin_lock_bh(&ift->lock);
 ift->ifpub = ifp;
 ift->cstamp = now;
 ift->tstamp = tmp_tstamp;
 spin_unlock_bh(&ift->lock);

 addrconf_dad_start(ift);
 in6_ifa_put(ift);
 in6_dev_put(idev);
out:
 return ret;
}

/*
 * Choose an appropriate source address (RFC3484)
 */

enum {
 IPV6_SADDR_RULE_INIT = 0,
 IPV6_SADDR_RULE_LOCAL,
 IPV6_SADDR_RULE_SCOPE,
 IPV6_SADDR_RULE_PREFERRED,
#ifdef CONFIG_IPV6_MIP6
 IPV6_SADDR_RULE_HOA,
#endif
 IPV6_SADDR_RULE_OIF,
 IPV6_SADDR_RULE_LABEL,
 IPV6_SADDR_RULE_PRIVACY,
 IPV6_SADDR_RULE_ORCHID,
 IPV6_SADDR_RULE_PREFIX,
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
 IPV6_SADDR_RULE_NOT_OPTIMISTIC,
#endif
 IPV6_SADDR_RULE_MAX
};

struct ipv6_saddr_score {
 int   rule;
 int   addr_type;
 struct inet6_ifaddr *ifa;
 DECLARE_BITMAP(scorebits, IPV6_SADDR_RULE_MAX);
 int   scopedist;
 int   matchlen;
};

struct ipv6_saddr_dst {
 const struct in6_addr *addr;
 int ifindex;
 int scope;
 int label;
 unsigned int prefs;
};

static inline int ipv6_saddr_preferred(int type)
{
 if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4|IPV6_ADDR_LOOPBACK))
  return 1;
 return 0;
}

static bool ipv6_use_optimistic_addr(const struct net *net,
         const struct inet6_dev *idev)
{
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
 if (!idev)
  return false;
 if (!READ_ONCE(net->ipv6.devconf_all->optimistic_dad) &&
     !READ_ONCE(idev->cnf.optimistic_dad))
  return false;
 if (!READ_ONCE(net->ipv6.devconf_all->use_optimistic) &&
     !READ_ONCE(idev->cnf.use_optimistic))
  return false;

 return true;
#else
 return false;
#endif
}

static bool ipv6_allow_optimistic_dad(const struct net *net,
          const struct inet6_dev *idev)
{
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
 if (!idev)
  return false;
 if (!READ_ONCE(net->ipv6.devconf_all->optimistic_dad) &&
     !READ_ONCE(idev->cnf.optimistic_dad))
  return false;

 return true;
#else
 return false;
#endif
}

static int ipv6_get_saddr_eval(struct net *net,
          struct ipv6_saddr_score *score,
          struct ipv6_saddr_dst *dst,
          int i)
{
 int ret;

 if (i <= score->rule) {
  switch (i) {
  case IPV6_SADDR_RULE_SCOPE:
   ret = score->scopedist;
   break;
  case IPV6_SADDR_RULE_PREFIX:
   ret = score->matchlen;
   break;
  default:
   ret = !!test_bit(i, score->scorebits);
  }
  goto out;
 }

 switch (i) {
 case IPV6_SADDR_RULE_INIT:
  /* Rule 0: remember if hiscore is not ready yet */
  ret = !!score->ifa;
  break;
 case IPV6_SADDR_RULE_LOCAL:
  /* Rule 1: Prefer same address */
  ret = ipv6_addr_equal(&score->ifa->addr, dst->addr);
  break;
 case IPV6_SADDR_RULE_SCOPE:
  /* Rule 2: Prefer appropriate scope
 *
 *      ret
 *       ^
 *    -1 |  d 15
 *    ---+--+-+---> scope
 *       |
 *       |             d is scope of the destination.
 *  B-d  |  \
 *       |   \      <- smaller scope is better if
 *  B-15 |    \        if scope is enough for destination.
 *       |             ret = B - scope (-1 <= scope >= d <= 15).
 * d-C-1 | /
 *       |/         <- greater is better
 *   -C  /             if scope is not enough for destination.
 *      /|             ret = scope - C (-1 <= d < scope <= 15).
 *
 * d - C - 1 < B -15 (for all -1 <= d <= 15).
 * C > d + 14 - B >= 15 + 14 - B = 29 - B.
 * Assume B = 0 and we get C > 29.
 */

  ret = __ipv6_addr_src_scope(score->addr_type);
  if (ret >= dst->scope)
   ret = -ret;
  else
   ret -= 128; /* 30 is enough */
  score->scopedist = ret;
  break;
 case IPV6_SADDR_RULE_PREFERRED:
     {
  /* Rule 3: Avoid deprecated and optimistic addresses */
  u8 avoid = IFA_F_DEPRECATED;

  if (!ipv6_use_optimistic_addr(net, score->ifa->idev))
   avoid |= IFA_F_OPTIMISTIC;
  ret = ipv6_saddr_preferred(score->addr_type) ||
        !(score->ifa->flags & avoid);
  break;
     }
#ifdef CONFIG_IPV6_MIP6
 case IPV6_SADDR_RULE_HOA:
     {
  /* Rule 4: Prefer home address */
  int prefhome = !(dst->prefs & IPV6_PREFER_SRC_COA);
  ret = !(score->ifa->flags & IFA_F_HOMEADDRESS) ^ prefhome;
  break;
     }
#endif
 case IPV6_SADDR_RULE_OIF:
  /* Rule 5: Prefer outgoing interface */
  ret = (!dst->ifindex ||
         dst->ifindex == score->ifa->idev->dev->ifindex);
  break;
 case IPV6_SADDR_RULE_LABEL:
  /* Rule 6: Prefer matching label */
  ret = ipv6_addr_label(net,
          &score->ifa->addr, score->addr_type,
          score->ifa->idev->dev->ifindex) == dst->label;
  break;
 case IPV6_SADDR_RULE_PRIVACY:
     {
  /* Rule 7: Prefer public address
 * Note: prefer temporary address if use_tempaddr >= 2
 */

  int preftmp = dst->prefs & (IPV6_PREFER_SRC_PUBLIC|IPV6_PREFER_SRC_TMP) ?
    !!(dst->prefs & IPV6_PREFER_SRC_TMP) :
    READ_ONCE(score->ifa->idev->cnf.use_tempaddr) >= 2;
  ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ preftmp;
  break;
     }
 case IPV6_SADDR_RULE_ORCHID:
  /* Rule 8-: Prefer ORCHID vs ORCHID or
 *     non-ORCHID vs non-ORCHID
 */

  ret = !(ipv6_addr_orchid(&score->ifa->addr) ^
   ipv6_addr_orchid(dst->addr));
  break;
 case IPV6_SADDR_RULE_PREFIX:
  /* Rule 8: Use longest matching prefix */
  ret = ipv6_addr_diff(&score->ifa->addr, dst->addr);
  if (ret > score->ifa->prefix_len)
   ret = score->ifa->prefix_len;
  score->matchlen = ret;
  break;
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
 case IPV6_SADDR_RULE_NOT_OPTIMISTIC:
  /* Optimistic addresses still have lower precedence than other
 * preferred addresses.
 */

  ret = !(score->ifa->flags & IFA_F_OPTIMISTIC);
  break;
#endif
 default:
  ret = 0;
 }

 if (ret)
  __set_bit(i, score->scorebits);
 score->rule = i;
out:
 return ret;
}

static int __ipv6_dev_get_saddr(struct net *net,
    struct ipv6_saddr_dst *dst,
    struct inet6_dev *idev,
    struct ipv6_saddr_score *scores,
    int hiscore_idx)
{
 struct ipv6_saddr_score *score = &scores[1 - hiscore_idx], *hiscore = &scores[hiscore_idx];

 list_for_each_entry_rcu(score->ifa, &idev->addr_list, if_list) {
  int i;

  /*
 * - Tentative Address (RFC2462 section 5.4)
 *  - A tentative address is not considered
 *    "assigned to an interface" in the traditional
 *    sense, unless it is also flagged as optimistic.
 * - Candidate Source Address (section 4)
 *  - In any case, anycast addresses, multicast
 *    addresses, and the unspecified address MUST
 *    NOT be included in a candidate set.
 */

  if ((score->ifa->flags & IFA_F_TENTATIVE) &&
      (!(score->ifa->flags & IFA_F_OPTIMISTIC)))
   continue;

  score->addr_type = __ipv6_addr_type(&score->ifa->addr);

  if (unlikely(score->addr_type == IPV6_ADDR_ANY ||
        score->addr_type & IPV6_ADDR_MULTICAST)) {
   net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s",
         idev->dev->name);
   continue;
  }

  score->rule = -1;
  bitmap_zero(score->scorebits, IPV6_SADDR_RULE_MAX);

  for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) {
   int minihiscore, miniscore;

   minihiscore = ipv6_get_saddr_eval(net, hiscore, dst, i);
   miniscore = ipv6_get_saddr_eval(net, score, dst, i);

   if (minihiscore > miniscore) {
    if (i == IPV6_SADDR_RULE_SCOPE &&
        score->scopedist > 0) {
     /*
 * special case:
 * each remaining entry
 * has too small (not enough)
 * scope, because ifa entries
 * are sorted by their scope
 * values.
 */

     goto out;
    }
    break;
   } else if (minihiscore < miniscore) {
    swap(hiscore, score);
    hiscore_idx = 1 - hiscore_idx;

    /* restore our iterator */
    score->ifa = hiscore->ifa;

    break;
   }
  }
 }
out:
 return hiscore_idx;
}

static int ipv6_get_saddr_master(struct net *net,
     const struct net_device *dst_dev,
     const struct net_device *master,
     struct ipv6_saddr_dst *dst,
     struct ipv6_saddr_score *scores,
     int hiscore_idx)
{
 struct inet6_dev *idev;

 idev = __in6_dev_get(dst_dev);
 if (idev)
  hiscore_idx = __ipv6_dev_get_saddr(net, dst, idev,
         scores, hiscore_idx);

 idev = __in6_dev_get(master);
 if (idev)
  hiscore_idx = __ipv6_dev_get_saddr(net, dst, idev,
         scores, hiscore_idx);

 return hiscore_idx;
}

int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
         const struct in6_addr *daddr, unsigned int prefs,
         struct in6_addr *saddr)
{
 struct ipv6_saddr_score scores[2], *hiscore;
 struct ipv6_saddr_dst dst;
 struct inet6_dev *idev;
 struct net_device *dev;
 int dst_type;
 bool use_oif_addr = false;
 int hiscore_idx = 0;
 int ret = 0;

 dst_type = __ipv6_addr_type(daddr);
 dst.addr = daddr;
 dst.ifindex = dst_dev ? dst_dev->ifindex : 0;
 dst.scope = __ipv6_addr_src_scope(dst_type);
 dst.label = ipv6_addr_label(net, daddr, dst_type, dst.ifindex);
 dst.prefs = prefs;

 scores[hiscore_idx].rule = -1;
 scores[hiscore_idx].ifa = NULL;

 rcu_read_lock();

 /* Candidate Source Address (section 4)
 *  - multicast and link-local destination address,
 *    the set of candidate source address MUST only
 *    include addresses assigned to interfaces
 *    belonging to the same link as the outgoing
 *    interface.
 * (- For site-local destination addresses, the
 *    set of candidate source addresses MUST only
 *    include addresses assigned to interfaces
 *    belonging to the same site as the outgoing
 *    interface.)
 *  - "It is RECOMMENDED that the candidate source addresses
 *    be the set of unicast addresses assigned to the
 *    interface that will be used to send to the destination
 *    (the 'outgoing' interface)." (RFC 6724)
 */

 if (dst_dev) {
  idev = __in6_dev_get(dst_dev);
  if ((dst_type & IPV6_ADDR_MULTICAST) ||
      dst.scope <= IPV6_ADDR_SCOPE_LINKLOCAL ||
      (idev && READ_ONCE(idev->cnf.use_oif_addrs_only))) {
   use_oif_addr = true;
  }
 }

 if (use_oif_addr) {
  if (idev)
   hiscore_idx = __ipv6_dev_get_saddr(net, &dst, idev, scores, hiscore_idx);
 } else {
  const struct net_device *master;
  int master_idx = 0;

  /* if dst_dev exists and is enslaved to an L3 device, then
 * prefer addresses from dst_dev and then the master over
 * any other enslaved devices in the L3 domain.
 */

  master = l3mdev_master_dev_rcu(dst_dev);
  if (master) {
   master_idx = master->ifindex;

   hiscore_idx = ipv6_get_saddr_master(net, dst_dev,
           master, &dst,
           scores, hiscore_idx);

   if (scores[hiscore_idx].ifa &&
       scores[hiscore_idx].scopedist >= 0)
    goto out;
  }

  for_each_netdev_rcu(net, dev) {
   /* only consider addresses on devices in the
 * same L3 domain
 */

   if (l3mdev_master_ifindex_rcu(dev) != master_idx)
    continue;
   idev = __in6_dev_get(dev);
   if (!idev)
    continue;
   hiscore_idx = __ipv6_dev_get_saddr(net, &dst, idev, scores, hiscore_idx);
  }
 }

out:
 hiscore = &scores[hiscore_idx];
 if (!hiscore->ifa)
  ret = -EADDRNOTAVAIL;
 else
  *saddr = hiscore->ifa->addr;

 rcu_read_unlock();
 return ret;
}
EXPORT_SYMBOL(ipv6_dev_get_saddr);

static int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
         u32 banned_flags)
{
 struct inet6_ifaddr *ifp;
 int err = -EADDRNOTAVAIL;

 list_for_each_entry_reverse(ifp, &idev->addr_list, if_list) {
  if (ifp->scope > IFA_LINK)
   break;
  if (ifp->scope == IFA_LINK &&
      !(ifp->flags & banned_flags)) {
   *addr = ifp->addr;
   err = 0;
   break;
  }
 }
 return err;
}

int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
      u32 banned_flags)
{
 struct inet6_dev *idev;
 int err = -EADDRNOTAVAIL;

 rcu_read_lock();
 idev = __in6_dev_get(dev);
 if (idev) {
  read_lock_bh(&idev->lock);
  err = __ipv6_get_lladdr(idev, addr, banned_flags);
  read_unlock_bh(&idev->lock);
 }
 rcu_read_unlock();
 return err;
}

static int ipv6_count_addresses(const struct inet6_dev *idev)
{
 const struct inet6_ifaddr *ifp;
 int cnt = 0;

 rcu_read_lock();
 list_for_each_entry_rcu(ifp, &idev->addr_list, if_list)
  cnt++;
 rcu_read_unlock();
 return cnt;
}

int ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
    const struct net_device *dev, int strict)
{
 return ipv6_chk_addr_and_flags(net, addr, dev, !dev,
           strict, IFA_F_TENTATIVE);
}
EXPORT_SYMBOL(ipv6_chk_addr);

/* device argument is used to find the L3 domain of interest. If
 * skip_dev_check is set, then the ifp device is not checked against
 * the passed in dev argument. So the 2 cases for addresses checks are:
 *   1. does the address exist in the L3 domain that dev is part of
 *      (skip_dev_check = true), or
 *
 *   2. does the address exist on the specific device
 *      (skip_dev_check = false)
 */

static struct net_device *
__ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr,
     const struct net_device *dev, bool skip_dev_check,
     int strict, u32 banned_flags)
{
 unsigned int hash = inet6_addr_hash(net, addr);
 struct net_device *l3mdev, *ndev;
 struct inet6_ifaddr *ifp;
 u32 ifp_flags;

 rcu_read_lock();

 l3mdev = l3mdev_master_dev_rcu(dev);
 if (skip_dev_check)
  dev = NULL;

 hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) {
  ndev = ifp->idev->dev;

  if (l3mdev_master_dev_rcu(ndev) != l3mdev)
   continue;

  /* Decouple optimistic from tentative for evaluation here.
 * Ban optimistic addresses explicitly, when required.
 */

  ifp_flags = (ifp->flags&IFA_F_OPTIMISTIC)
       ? (ifp->flags&~IFA_F_TENTATIVE)
       : ifp->flags;
  if (ipv6_addr_equal(&ifp->addr, addr) &&
      !(ifp_flags&banned_flags) &&
      (!dev || ndev == dev ||
       !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))) {
   rcu_read_unlock();
   return ndev;
  }
 }

 rcu_read_unlock();
 return NULL;
}

int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr,
       const struct net_device *dev, bool skip_dev_check,
       int strict, u32 banned_flags)
{
 return __ipv6_chk_addr_and_flags(net, addr, dev, skip_dev_check,
      strict, banned_flags) ? 1 : 0;
}
EXPORT_SYMBOL(ipv6_chk_addr_and_flags);


/* Compares an address/prefix_len with addresses on device @dev.
 * If one is found it returns true.
 */

bool ipv6_chk_custom_prefix(const struct in6_addr *addr,
 const unsigned int prefix_len, struct net_device *dev)
{
 const struct inet6_ifaddr *ifa;
 const struct inet6_dev *idev;
 bool ret = false;

 rcu_read_lock();
 idev = __in6_dev_get(dev);
 if (idev) {
  list_for_each_entry_rcu(ifa, &idev->addr_list, if_list) {
   ret = ipv6_prefix_equal(addr, &ifa->addr, prefix_len);
   if (ret)
    break;
  }
 }
 rcu_read_unlock();

 return ret;
}
EXPORT_SYMBOL(ipv6_chk_custom_prefix);

int ipv6_chk_prefix(const struct in6_addr *addr, struct net_device *dev)
{
 const struct inet6_ifaddr *ifa;
 const struct inet6_dev *idev;
 int onlink;

 onlink = 0;
 rcu_read_lock();
 idev = __in6_dev_get(dev);
 if (idev) {
  list_for_each_entry_rcu(ifa, &idev->addr_list, if_list) {
   onlink = ipv6_prefix_equal(addr, &ifa->addr,
         ifa->prefix_len);
   if (onlink)
    break;
  }
 }
 rcu_read_unlock();
 return onlink;
}
EXPORT_SYMBOL(ipv6_chk_prefix);

/**
 * ipv6_dev_find - find the first device with a given source address.
 * @net: the net namespace
 * @addr: the source address
 * @dev: used to find the L3 domain of interest
 *
 * The caller should be protected by RCU, or RTNL.
 */

struct net_device *ipv6_dev_find(struct net *net, const struct in6_addr *addr,
     struct net_device *dev)
{
 return __ipv6_chk_addr_and_flags(net, addr, dev, !dev, 1,
      IFA_F_TENTATIVE);
}
EXPORT_SYMBOL(ipv6_dev_find);

struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *addr,
         struct net_device *dev, int strict)
{
 unsigned int hash = inet6_addr_hash(net, addr);
 struct inet6_ifaddr *ifp, *result = NULL;

 rcu_read_lock();
 hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) {
  if (ipv6_addr_equal(&ifp->addr, addr)) {
   if (!dev || ifp->idev->dev == dev ||
       !(ifp->scope&(IFA_LINK|IFA_HOST) || strict)) {
    if (in6_ifa_hold_safe(ifp)) {
     result = ifp;
     break;
    }
   }
  }
 }
 rcu_read_unlock();

 return result;
}

/* Gets referenced address, destroys ifaddr */

static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
{
 if (dad_failed)
  ifp->flags |= IFA_F_DADFAILED;

 if (ifp->flags&IFA_F_TEMPORARY) {
  struct inet6_ifaddr *ifpub;
  spin_lock_bh(&ifp->lock);
  ifpub = ifp->ifpub;
  if (ifpub) {
   in6_ifa_hold(ifpub);
   spin_unlock_bh(&ifp->lock);
   ipv6_create_tempaddr(ifpub, true);
   in6_ifa_put(ifpub);
  } else {
   spin_unlock_bh(&ifp->lock);
  }
  ipv6_del_addr(ifp);
 } else if (ifp->flags&IFA_F_PERMANENT || !dad_failed) {
  spin_lock_bh(&ifp->lock);
  addrconf_del_dad_work(ifp);
  ifp->flags |= IFA_F_TENTATIVE;
  if (dad_failed)
   ifp->flags &= ~IFA_F_OPTIMISTIC;
  spin_unlock_bh(&ifp->lock);
  if (dad_failed)
   ipv6_ifa_notify(0, ifp);
  in6_ifa_put(ifp);
 } else {
  ipv6_del_addr(ifp);
 }
}

static int addrconf_dad_end(struct inet6_ifaddr *ifp)
{
 int err = -ENOENT;

 spin_lock_bh(&ifp->lock);
 if (ifp->state == INET6_IFADDR_STATE_DAD) {
  ifp->state = INET6_IFADDR_STATE_POSTDAD;
  err = 0;
 }
 spin_unlock_bh(&ifp->lock);

 return err;
}

void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
{
 struct inet6_dev *idev = ifp->idev;
 struct net *net = dev_net(idev->dev);
 int max_addresses;

 if (addrconf_dad_end(ifp)) {
  in6_ifa_put(ifp);
  return;
 }

 net_info_ratelimited("%s: IPv6 duplicate address %pI6c used by %pM detected!\n",
        ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source);

 spin_lock_bh(&ifp->lock);

 if (ifp->flags & IFA_F_STABLE_PRIVACY) {
  struct in6_addr new_addr;
  struct inet6_ifaddr *ifp2;
  int retries = ifp->stable_privacy_retry + 1;
  struct ifa6_config cfg = {
   .pfx = &new_addr,
   .plen = ifp->prefix_len,
   .ifa_flags = ifp->flags,
   .valid_lft = ifp->valid_lft,
   .preferred_lft = ifp->prefered_lft,
   .scope = ifp->scope,
  };

  if (retries > net->ipv6.sysctl.idgen_retries) {
   net_info_ratelimited("%s: privacy stable address generation failed because of DAD conflicts!\n",
          ifp->idev->dev->name);
   goto errdad;
  }

  new_addr = ifp->addr;
  if (ipv6_generate_stable_address(&new_addr, retries,
       idev))
   goto errdad;

  spin_unlock_bh(&ifp->lock);

  max_addresses = READ_ONCE(idev->cnf.max_addresses);
  if (max_addresses &&
      ipv6_count_addresses(idev) >= max_addresses)
   goto lock_errdad;

  net_info_ratelimited("%s: generating new stable privacy address because of DAD conflict\n",
         ifp->idev->dev->name);

  ifp2 = ipv6_add_addr(idev, &cfg, false, NULL);
  if (IS_ERR(ifp2))
   goto lock_errdad;

  spin_lock_bh(&ifp2->lock);
  ifp2->stable_privacy_retry = retries;
  ifp2->state = INET6_IFADDR_STATE_PREDAD;
  spin_unlock_bh(&ifp2->lock);

  addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay);
  in6_ifa_put(ifp2);
lock_errdad:
  spin_lock_bh(&ifp->lock);
 }

errdad:
 /* transition from _POSTDAD to _ERRDAD */
 ifp->state = INET6_IFADDR_STATE_ERRDAD;
 spin_unlock_bh(&ifp->lock);

 addrconf_mod_dad_work(ifp, 0);
 in6_ifa_put(ifp);
}

/* Join to solicited addr multicast group. */
void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr)
{
 struct in6_addr maddr;

 if (READ_ONCE(dev->flags) & (IFF_LOOPBACK | IFF_NOARP))
  return;

 addrconf_addr_solict_mult(addr, &maddr);
 ipv6_dev_mc_inc(dev, &maddr);
}

void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr)
{
 struct in6_addr maddr;

 if (READ_ONCE(idev->dev->flags) & (IFF_LOOPBACK | IFF_NOARP))
  return;

 addrconf_addr_solict_mult(addr, &maddr);
 __ipv6_dev_mc_dec(idev, &maddr);
}

static void addrconf_join_anycast(struct inet6_ifaddr *ifp)
{
 struct in6_addr addr;

 if (ifp->prefix_len >= 127) /* RFC 6164 */
  return;
 ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
 if (ipv6_addr_any(&addr))
  return;
 __ipv6_dev_ac_inc(ifp->idev, &addr);
}

static void addrconf_leave_anycast(struct inet6_ifaddr *ifp)
{
 struct in6_addr addr;

 if (ifp->prefix_len >= 127) /* RFC 6164 */
  return;
 ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
 if (ipv6_addr_any(&addr))
  return;
 __ipv6_dev_ac_dec(ifp->idev, &addr);
}

static int addrconf_ifid_6lowpan(u8 *eui, struct net_device *dev)
{
 switch (dev->addr_len) {
 case ETH_ALEN:
  memcpy(eui, dev->dev_addr, 3);
  eui[3] = 0xFF;
  eui[4] = 0xFE;
  memcpy(eui + 5, dev->dev_addr + 3, 3);
  break;
 case EUI64_ADDR_LEN:
  memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN);
  eui[0] ^= 2;
  break;
 default:
  return -1;
 }

 return 0;
}

static int addrconf_ifid_ieee1394(u8 *eui, struct net_device *dev)
{
 const union fwnet_hwaddr *ha;

 if (dev->addr_len != FWNET_ALEN)
  return -1;

 ha = (const union fwnet_hwaddr *)dev->dev_addr;

 memcpy(eui, &ha->uc.uniq_id, sizeof(ha->uc.uniq_id));
 eui[0] ^= 2;
 return 0;
}

static int addrconf_ifid_arcnet(u8 *eui, struct net_device *dev)
{
 /* XXX: inherit EUI-64 from other interface -- yoshfuji */
 if (dev->addr_len != ARCNET_ALEN)
  return -1;
 memset(eui, 0, 7);
 eui[7] = *(u8 *)dev->dev_addr;
 return 0;
}

static int addrconf_ifid_infiniband(u8 *eui, struct net_device *dev)
{
 if (dev->addr_len != INFINIBAND_ALEN)
  return -1;
 memcpy(eui, dev->dev_addr + 12, 8);
 eui[0] |= 2;
 return 0;
}

static int __ipv6_isatap_ifid(u8 *eui, __be32 addr)
{
 if (addr == 0)
  return -1;
 eui[0] = (ipv4_is_zeronet(addr) || ipv4_is_private_10(addr) ||
    ipv4_is_loopback(addr) || ipv4_is_linklocal_169(addr) ||
    ipv4_is_private_172(addr) || ipv4_is_test_192(addr) ||
    ipv4_is_anycast_6to4(addr) || ipv4_is_private_192(addr) ||
    ipv4_is_test_198(addr) || ipv4_is_multicast(addr) ||
    ipv4_is_lbcast(addr)) ? 0x00 : 0x02;
 eui[1] = 0;
 eui[2] = 0x5E;
 eui[3] = 0xFE;
 memcpy(eui + 4, &addr, 4);
 return 0;
}

static int addrconf_ifid_sit(u8 *eui, struct net_device *dev)
{
 if (dev->priv_flags & IFF_ISATAP)
  return __ipv6_isatap_ifid(eui, *(__be32 *)dev->dev_addr);
 return -1;
}

static int addrconf_ifid_gre(u8 *eui, struct net_device *dev)
{
 return __ipv6_isatap_ifid(eui, *(__be32 *)dev->dev_addr);
}

static int addrconf_ifid_ip6tnl(u8 *eui, struct net_device *dev)
{
 memcpy(eui, dev->perm_addr, 3);
 memcpy(eui + 5, dev->perm_addr + 3, 3);
 eui[3] = 0xFF;
 eui[4] = 0xFE;
 eui[0] ^= 2;
 return 0;
}

static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
{
 switch (dev->type) {
 case ARPHRD_ETHER:
 case ARPHRD_FDDI:
  return addrconf_ifid_eui48(eui, dev);
 case ARPHRD_ARCNET:
  return addrconf_ifid_arcnet(eui, dev);
 case ARPHRD_INFINIBAND:
  return addrconf_ifid_infiniband(eui, dev);
 case ARPHRD_SIT:
  return addrconf_ifid_sit(eui, dev);
 case ARPHRD_IPGRE:
 case ARPHRD_TUNNEL:
  return addrconf_ifid_gre(eui, dev);
 case ARPHRD_6LOWPAN:
  return addrconf_ifid_6lowpan(eui, dev);
 case ARPHRD_IEEE1394:
  return addrconf_ifid_ieee1394(eui, dev);
 case ARPHRD_TUNNEL6:
 case ARPHRD_IP6GRE:
 case ARPHRD_RAWIP:
  return addrconf_ifid_ip6tnl(eui, dev);
 }
 return -1;
}

static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)
{
 int err = -1;
 struct inet6_ifaddr *ifp;

 read_lock_bh(&idev->lock);
 list_for_each_entry_reverse(ifp, &idev->addr_list, if_list) {
  if (ifp->scope > IFA_LINK)
   break;
  if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) {
   memcpy(eui, ifp->addr.s6_addr+8, 8);
   err = 0;
   break;
  }
 }
 read_unlock_bh(&idev->lock);
 return err;
}

/* Generation of a randomized Interface Identifier
 * draft-ietf-6man-rfc4941bis, Section 3.3.1
 */


static void ipv6_gen_rnd_iid(struct in6_addr *addr)
{
regen:
 get_random_bytes(&addr->s6_addr[8], 8);

 /* <draft-ietf-6man-rfc4941bis-08.txt>, Section 3.3.1:
 * check if generated address is not inappropriate:
 *
 * - Reserved IPv6 Interface Identifiers
 * - XXX: already assigned to an address on the device
 */


 /* Subnet-router anycast: 0000:0000:0000:0000 */
 if (!(addr->s6_addr32[2] | addr->s6_addr32[3]))
  goto regen;

 /* IANA Ethernet block: 0200:5EFF:FE00:0000-0200:5EFF:FE00:5212
 * Proxy Mobile IPv6:   0200:5EFF:FE00:5213
 * IANA Ethernet block: 0200:5EFF:FE00:5214-0200:5EFF:FEFF:FFFF
 */

 if (ntohl(addr->s6_addr32[2]) == 0x02005eff &&
     (ntohl(addr->s6_addr32[3]) & 0Xff000000) == 0xfe000000)
  goto regen;

 /* Reserved subnet anycast addresses */
 if (ntohl(addr->s6_addr32[2]) == 0xfdffffff &&
     ntohl(addr->s6_addr32[3]) >= 0Xffffff80)
  goto regen;
}

/*
 * Add prefix route.
 */


static void
addrconf_prefix_route(struct in6_addr *pfx, int plen, u32 metric,
        struct net_device *dev, unsigned long expires,
        u32 flags, gfp_t gfp_flags)
{
 struct fib6_config cfg = {
  .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX,
  .fc_metric = metric ? : IP6_RT_PRIO_ADDRCONF,
  .fc_ifindex = dev->ifindex,
  .fc_expires = expires,
  .fc_dst_len = plen,
  .fc_flags = RTF_UP | flags,
  .fc_nlinfo.nl_net = dev_net(dev),
  .fc_protocol = RTPROT_KERNEL,
  .fc_type = RTN_UNICAST,
 };

 cfg.fc_dst = *pfx;

 /* Prevent useless cloning on PtP SIT.
   This thing is done here expecting that the whole
   class of non-broadcast devices need not cloning.
 */

#if IS_ENABLED(CONFIG_IPV6_SIT)
 if (dev->type == ARPHRD_SIT && (dev->flags & IFF_POINTOPOINT))
  cfg.fc_flags |= RTF_NONEXTHOP;
#endif

 ip6_route_add(&cfg, gfp_flags, NULL);
}


static struct fib6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
        int plen,
        const struct net_device *dev,
        u32 flags, u32 noflags,
        bool no_gw)
{
 struct fib6_node *fn;
 struct fib6_info *rt = NULL;
 struct fib6_table *table;
 u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX;

 table = fib6_get_table(dev_net(dev), tb_id);
 if (!table)
  return NULL;

 rcu_read_lock();
 fn = fib6_locate(&table->tb6_root, pfx, plen, NULL, 0, true);
 if (!fn)
  goto out;

 for_each_fib6_node_rt_rcu(fn) {
  /* prefix routes only use builtin fib6_nh */
  if (rt->nh)
   continue;

  if (rt->fib6_nh->fib_nh_dev->ifindex != dev->ifindex)
   continue;
  if (no_gw && rt->fib6_nh->fib_nh_gw_family)
   continue;
  if ((rt->fib6_flags & flags) != flags)
   continue;
  if ((rt->fib6_flags & noflags) != 0)
   continue;
  if (!fib6_info_hold_safe(rt))
   continue;
  break;
 }
out:
 rcu_read_unlock();
 return rt;
}


/* Create "default" multicast route to the interface */

static void addrconf_add_mroute(struct net_device *dev)
{
 struct fib6_config cfg = {
  .fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_LOCAL,
  .fc_metric = IP6_RT_PRIO_ADDRCONF,
  .fc_ifindex = dev->ifindex,
  .fc_dst_len = 8,
  .fc_flags = RTF_UP,
  .fc_type = RTN_MULTICAST,
  .fc_nlinfo.nl_net = dev_net(dev),
  .fc_protocol = RTPROT_KERNEL,
 };

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=92 G=94

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.20Angebot  ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.