配置项optimistic_dad用于控制是否执行优化的DAD检查;配置项use_optimistic控制在源地址选择时,可使用optimistic地址,但是其优先级低于Preferred地址。如下函数ipv6_allow_optimistic_dad,命名空间和设备的optimistic_dad配置项有一个为真,就启用此功能。

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

配置项use_optimistic仅在optimistic_dad开启后有效,也是只有命名空间或者设备其中一个的use_optimistic为真,即开启此功能。

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

SLAAC自动配置地址

根据RA报文中的前缀信息配置接口地址,如果命名空间中conf/{all,interface}/optimistic_dad的值有一个不为零,则认为开启了优化DAD功能,此时如果转发功能未启用,并且RA报文中带有sllao(Source Link-Layer Address Options)的情况下,设置IFA_F_OPTIMISTIC标记,将地址标识为优化地址。

如果报文RA中没有携带sllao,将得不到路由器的链路地址。由于不能使用Optimistic地址发送NS报文,需要将数据报文(子网内)发送给路由器中转,或者路由器回复重定向报文,告知报文目的IP的链路地址,所以,如果没有sllao,Optimistic功能将不可用。

int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,const struct prefix_info *pinfo, struct inet6_dev *in6_dev,const struct in6_addr *addr, int addr_type,u32 addr_flags, bool sllao, bool tokenized, __u32 valid_lft, u32 prefered_lft)
{struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);int create = 0;if (!ifp && valid_lft) {int max_addresses = in6_dev->cnf.max_addresses;struct ifa6_config cfg = {.pfx = addr,.plen = pinfo->prefix_len,.ifa_flags = addr_flags,.valid_lft = valid_lft,.preferred_lft = prefered_lft,.scope = addr_type & IPV6_ADDR_SCOPE_MASK,};#ifdef CONFIG_IPV6_OPTIMISTIC_DADif ((net->ipv6.devconf_all->optimistic_dad ||in6_dev->cnf.optimistic_dad) &&!net->ipv6.devconf_all->forwarding && sllao)cfg.ifa_flags |= IFA_F_OPTIMISTIC;
#endif/* Do not allow to create too much of autoconfigured* addresses; this would be too easy way to crash kernel.*/if (!max_addresses || ipv6_count_addresses(in6_dev) < max_addresses)ifp = ipv6_add_addr(in6_dev, &cfg, false, NULL);if (IS_ERR_OR_NULL(ifp)) return -1;create = 1;spin_lock_bh(&ifp->lock);ifp->flags |= IFA_F_MANAGETEMPADDR;ifp->cstamp = jiffies;ifp->tokenized = tokenized;spin_unlock_bh(&ifp->lock);addrconf_dad_start(ifp);}

链路本地地址

与上节相同,内核打开了IPV6_OPTIMISTIC_DAD编译选项之后,在添加链路本地地址时,如果开启了优化dad配置,并且命名空间的forwarding为0,即工作在主机模式,设置IFA_F_OPTIMISTIC标志。对于路由器,不能设置optimistic地址,参考上一节说明的原因。

void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr *addr, u32 flags)
{struct ifa6_config cfg = {.pfx = addr,.plen = 64,.ifa_flags = flags | IFA_F_PERMANENT,.valid_lft = INFINITY_LIFE_TIME,.preferred_lft = INFINITY_LIFE_TIME,.scope = IFA_LINK};struct inet6_ifaddr *ifp;#ifdef CONFIG_IPV6_OPTIMISTIC_DADif ((dev_net(idev->dev)->ipv6.devconf_all->optimistic_dad ||idev->cnf.optimistic_dad) &&!dev_net(idev->dev)->ipv6.devconf_all->forwarding)cfg.ifa_flags |= IFA_F_OPTIMISTIC;
#endififp = ipv6_add_addr(idev, &cfg, true, NULL);if (!IS_ERR(ifp)) {addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 0, idev->dev,0, 0, GFP_ATOMIC);addrconf_dad_start(ifp);in6_ifa_put(ifp);}
}

应用层配置地址

用户层通过IP命令设置IPv6地址时,IFA_F_NODAD 和 IFA_F_OPTIMISTIC是互斥的,不能同时设置。

static int inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack)
{if (tb[IFA_FLAGS])cfg.ifa_flags = nla_get_u32(tb[IFA_FLAGS]);elsecfg.ifa_flags = ifm->ifa_flags;/* We ignore other flags so far. */cfg.ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS |IFA_F_MANAGETEMPADDR | IFA_F_NOPREFIXROUTE |IFA_F_MCAUTOJOIN | IFA_F_OPTIMISTIC;idev = ipv6_find_idev(dev);if (IS_ERR(idev))return PTR_ERR(idev);if (!ipv6_allow_optimistic_dad(net, idev))cfg.ifa_flags &= ~IFA_F_OPTIMISTIC;if (cfg.ifa_flags & IFA_F_NODAD &&cfg.ifa_flags & IFA_F_OPTIMISTIC) {NL_SET_ERR_MSG(extack, "IFA_F_NODAD and IFA_F_OPTIMISTIC are mutually exclusive");return -EINVAL;}ifa = ipv6_get_ifaddr(net, cfg.pfx, dev, 1);if (!ifa) {/** It would be best to check for !NLM_F_CREATE here but* userspace already relies on not having to provide this.*/return inet6_addr_add(net, ifm->ifa_index, &cfg, extack);}

源地址选择

在选择源地址时,不考虑暂定tentative地址,除非其设置了IFA_F_OPTIMISTIC标志。

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;

不能使用DEPRECATED废弃地址,如果没有打开使用use_optimistic地址配置,也不能使用IFA_F_OPTIMISTIC地址。即使使用OPTIMISTIC优化地址,其优先级也是低于preferred地址。

static int ipv6_get_saddr_eval(struct net *net, struct ipv6_saddr_score *score,struct ipv6_saddr_dst *dst, int i)
{switch (i) {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_OPTIMISTIC_DADcase IPV6_SADDR_RULE_NOT_OPTIMISTIC:/* Optimistic addresses still have lower precedence than other* preferred addresses.*/ret = !(score->ifa->flags & IFA_F_OPTIMISTIC);break;
#endifdefault:ret = 0;}

优化DAD

对于不使用DAD的情况,如果仅为tentative地址,没有同时设置optimistic标志,发送NA报文通知邻居。否则,如果设置了optimistic标志,不能发送NA,以免遇到地址冲突的情况下,破坏邻居设备中的地址表项。

函数addrconf_dad_completed中发送NA时,将设置Override标志。

static void addrconf_dad_begin(struct inet6_ifaddr *ifp)
{net = dev_net(dev);if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||(net->ipv6.devconf_all->accept_dad < 1 &&idev->cnf.accept_dad < 1) ||!(ifp->flags&IFA_F_TENTATIVE) ||ifp->flags & IFA_F_NODAD) {bool send_na = false;if (ifp->flags & IFA_F_TENTATIVE &&!(ifp->flags & IFA_F_OPTIMISTIC))send_na = true;bump_id = ifp->flags & IFA_F_TENTATIVE;ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);spin_unlock(&ifp->lock);read_unlock_bh(&idev->lock);addrconf_dad_completed(ifp, bump_id, send_na);return;}

对于使用DAD的情况,优化地址在DAD完成之前已经可以使用,这里插入路由表项,并且,向上层发送新地址通知。

    /* Optimistic nodes can start receiving Frames right away*/if (ifp->flags & IFA_F_OPTIMISTIC) {ip6_ins_rt(net, ifp->rt);if (ipv6_use_optimistic_addr(net, idev)) {/* Because optimistic nodes can use this address,* notify listeners. If DAD fails, RTM_DELADDR is sent.*/notify = true;}}addrconf_dad_kick(ifp);

在DAD成功结束之后,对于OPTIMISTIC优化地址,不需要发送NA报文,与以上函数addrconf_dad_begin中处理相同。

static void addrconf_dad_work(struct work_struct *w)
{if (ifp->dad_probes == 0) {bool send_na = false;/* DAD was successful*/if (ifp->flags & IFA_F_TENTATIVE &&!(ifp->flags & IFA_F_OPTIMISTIC))send_na = true;bump_id = ifp->flags & IFA_F_TENTATIVE;ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);spin_unlock(&ifp->lock);write_unlock_bh(&idev->lock);addrconf_dad_completed(ifp, bump_id, send_na);goto out;}

另外,对于优化Optimistic地址,不需要延时,立即调用DAD处理函数,发送NS报文,源地址使用未指定地址in6addr_any,报文在不包括SLLAO。

/*  Duplicate Address Detection*/
static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
{unsigned long rand_num;struct inet6_dev *idev = ifp->idev;u64 nonce;if (ifp->flags & IFA_F_OPTIMISTIC)rand_num = 0;elserand_num = prandom_u32() % (idev->cnf.rtr_solicit_delay ? : 1);nonce = 0;if (idev->cnf.enhanced_dad ||dev_net(idev->dev)->ipv6.devconf_all->enhanced_dad) {doget_random_bytes(&nonce, 6);while (nonce == 0);}ifp->dad_nonce = nonce;ifp->dad_probes = idev->cnf.dad_transmits;addrconf_mod_dad_work(ifp, rand_num);
}

对于PERMANENT地址,如果dad失败,将其设置为tentative地址,清除optimistic标志位,地址不可用。

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) {...} 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);

邻居发现

在发送RS报文时,如果使用的源地址为优化地址,报文中不应包含sllao选项。另外,在找不到相应接口地址时,也不包含sllao选项。

void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, const struct in6_addr *daddr)
{struct rs_msg *msg;int send_sllao = dev->addr_len;#ifdef CONFIG_IPV6_OPTIMISTIC_DAD/** According to section 2.2 of RFC 4429, we must not* send router solicitations with a sllao from* optimistic addresses, but we may send the solicitation* if we don't include the sllao.  So here we check* if our address is optimistic, and if so, we* suppress the inclusion of the sllao.*/if (send_sllao) {struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr, dev, 1);if (ifp) {if (ifp->flags & IFA_F_OPTIMISTIC)  {send_sllao = 0;}in6_ifa_put(ifp);} else {send_sllao = 0;}}
#endifif (send_sllao)optlen += ndisc_opt_addr_space(dev, NDISC_ROUTER_SOLICITATION);skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);if (!skb) return;

在发送NA报文时,如果源地址为优化地址,报文中的覆盖标志override为false,以免破坏接收设备的邻居表项。

void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,const struct in6_addr *solicited_addr,bool router, bool solicited, bool override, bool inc_opt)
{struct inet6_ifaddr *ifp;const struct in6_addr *src_addr;struct nd_msg *msg;/* for anycast or proxy, solicited_addr != src_addr */ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);if (ifp) {src_addr = solicited_addr;if (ifp->flags & IFA_F_OPTIMISTIC)override = false;inc_opt |= ifp->idev->cnf.force_tllao;in6_ifa_put(ifp);} else {if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,&tmpaddr))return;src_addr = &tmpaddr;}if (!dev->addr_len)inc_opt = false;if (inc_opt)optlen += ndisc_opt_addr_space(dev, NDISC_NEIGHBOUR_ADVERTISEMENT);skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);if (!skb)return;msg = skb_put(skb, sizeof(*msg));*msg = (struct nd_msg) {.icmph = {.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,.icmp6_router = router,.icmp6_solicited = solicited,.icmp6_override = override,},.target = *solicited_addr,};

在发送非请求的NA报文时,对于未设置IFA_F_OPTIMISTIC标志的TENTATIVE地址,不进行发送。需要等待其完成DAD。

static void ndisc_send_unsol_na(struct net_device *dev)
{struct inet6_dev *idev;struct inet6_ifaddr *ifa;idev = in6_dev_get(dev);if (!idev)return;read_lock_bh(&idev->lock);list_for_each_entry(ifa, &idev->addr_list, if_list) {/* skip tentative addresses until dad completes */if (ifa->flags & IFA_F_TENTATIVE &&!(ifa->flags & IFA_F_OPTIMISTIC))continue;ndisc_send_na(dev, &in6addr_linklocal_allnodes, &ifa->addr,/*router=*/ !!idev->cnf.forwarding,/*solicited=*/ false, /*override=*/ true,/*inc_opt=*/ true);}read_unlock_bh(&idev->lock);in6_dev_put(idev);
}

在发送NS报文时,如果未指定源地址,选择除TENTATIVE和OPTIMISTIC地址类型之外的其它地址做源地址。

void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,const struct in6_addr *daddr, const struct in6_addr *saddr, u64 nonce)
{struct sk_buff *skb;struct in6_addr addr_buf;int inc_opt = dev->addr_len;int optlen = 0;struct nd_msg *msg;if (!saddr) {if (ipv6_get_lladdr(dev, &addr_buf, (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))return;saddr = &addr_buf;}if (ipv6_addr_any(saddr))inc_opt = false;if (inc_opt)optlen += ndisc_opt_addr_space(dev, NDISC_NEIGHBOUR_SOLICITATION);if (nonce != 0)optlen += 8;skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);if (!skb)return;

如果没有找到非tentative/optimistic的地址,源地址saddr将为空,在以上的发送函数ndisc_send_ns中,需要查找符合的链路本地地址,找不到将不能发送。

static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
{struct in6_addr *saddr = NULL;struct net_device *dev = neigh->dev;struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;if (skb && ipv6_chk_addr_and_flags(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, false, 1,IFA_F_TENTATIVE|IFA_F_OPTIMISTIC))saddr = &ipv6_hdr(skb)->saddr;probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES);if (probes < 0) {if (!(neigh->nud_state & NUD_VALID)) {ND_PRINTK(1, dbg, "%s: trying to ucast probe in NUD_INVALID: %pI6\n", __func__, target);}ndisc_send_ns(dev, target, target, saddr, 0);} else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) {neigh_app_ns(neigh);} else {addrconf_addr_solict_mult(target, &mcaddr);ndisc_send_ns(dev, target, &mcaddr, saddr, 0);

如果接收到的NS报文不是DAD检查报文,对于Optimistic地址回复NA报文,其中Override标志设置为0。

static void ndisc_recv_ns(struct sk_buff *skb)
{ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);if (ifp) {
have_ifp:if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {if (dad) {if (nonce != 0 && ifp->dad_nonce == nonce) {u8 *np = (u8 *)&nonce;/* Matching nonce if looped back */ND_PRINTK(2, notice, "%s: IPv6 DAD loopback for address %pI6c nonce %pM ignored\n",ifp->idev->dev->name, &ifp->addr, np);goto out;}/* We are colliding with another node who is doing DAD so fail our DAD process */addrconf_dad_failure(skb, ifp);return;} else {/** This is not a dad solicitation.* If we are an optimistic node, we should respond.* Otherwise, we should ignore it.*/if (!(ifp->flags & IFA_F_OPTIMISTIC))goto out;}}idev = ifp->idev;} else {struct net *net = dev_net(dev);/* perhaps an address on the master device */if (netif_is_l3_slave(dev)) {struct net_device *mdev;mdev = netdev_master_upper_dev_get_rcu(dev);if (mdev) {ifp = ipv6_get_ifaddr(net, &msg->target, mdev, 1);if (ifp) goto have_ifp;}}

报文发送

如果当前报文目的地址的邻居表项不可用,而报文的源地址使用的是Optimistic地址,不能发送NS报文,需要将报文发给默认的网关。

static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk, struct dst_entry **dst, struct flowi6 *fl6)
{
#ifdef CONFIG_IPV6_OPTIMISTIC_DADstruct neighbour *n;struct rt6_info *rt;
#endif#ifdef CONFIG_IPV6_OPTIMISTIC_DAD/** Here if the dst entry we've looked up* has a neighbour entry that is in the INCOMPLETE* state and the src address from the flow is* marked as OPTIMISTIC, we release the found* dst entry and replace it instead with the* dst entry of the nexthop router*/rt = (struct rt6_info *) *dst;rcu_read_lock_bh();n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt, &fl6->daddr));err = n && !(n->nud_state & NUD_VALID) ? -EINVAL : 0;rcu_read_unlock_bh();if (err) {struct inet6_ifaddr *ifp;struct flowi6 fl_gw6;ifp = ipv6_get_ifaddr(net, &fl6->saddr, (*dst)->dev, 1);redirect = (ifp && ifp->flags & IFA_F_OPTIMISTIC);if (ifp) in6_ifa_put(ifp);if (redirect) {/* We need to get the dst entry for the default router instead*/dst_release(*dst);memcpy(&fl_gw6, fl6, sizeof(struct flowi6));memset(&fl_gw6.daddr, 0, sizeof(struct in6_addr));*dst = ip6_route_output(net, sk, &fl_gw6);err = (*dst)->error;if (err) goto out_err_release;}}
#endif

网关在接收到此报文之后,判断其目的地址就位于链路本地网络中,将发送ND的重定向报文,在其中携带目的地址的链路地址,如下,增加TLLAO选项。

void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
{if (dev->addr_len) {struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);if (!neigh) {ND_PRINTK(2, warn, "Redirect: no neigh for target address\n");goto release;}read_lock_bh(&neigh->lock);if (neigh->nud_state & NUD_VALID) {memcpy(ha_buf, neigh->ha, dev->addr_len);read_unlock_bh(&neigh->lock);ha = ha_buf;optlen += ndisc_redirect_opt_addr_space(dev, neigh, ops_data_buf, &ops_data);} elseread_unlock_bh(&neigh->lock);neigh_release(neigh);}buff = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);if (!buff) goto release;msg = skb_put(buff, sizeof(*msg));*msg = (struct rd_msg) {.icmph = {.icmp6_type = NDISC_REDIRECT,},.target = *target,.dest = ipv6_hdr(skb)->daddr,};/* include target_address option */if (ha)ndisc_fill_redirect_addr_option(buff, ha, ops_data);

地址处理

如果开启了DAD,并且Optimistic标志没有设置,发送上层通知。

static int inet6_addr_add(struct net *net, int ifindex, struct ifa6_config *cfg, struct netlink_ext_ack *extack)
{struct inet6_ifaddr *ifp;ifp = ipv6_add_addr(idev, cfg, true, extack);if (!IS_ERR(ifp)) {.../* Send a netlink notification if DAD is enabled and* optimistic flag is not set*/if (!(ifp->flags & (IFA_F_OPTIMISTIC | IFA_F_NODAD)))ipv6_ifa_notify(0, ifp);

在修改地址时,对于非tentative地址,或者DAD失败的地址,清除配置的Optimistic标志,不允许设置。

static int inet6_addr_modify(struct inet6_ifaddr *ifp, struct ifa6_config *cfg)
{u32 flags;if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR &&(ifp->flags & IFA_F_TEMPORARY || ifp->prefix_len != 64))return -EINVAL;if (!(ifp->flags & IFA_F_TENTATIVE) || ifp->flags & IFA_F_DADFAILED)cfg->ifa_flags &= ~IFA_F_OPTIMISTIC;

获取设备的未设置TENTATIVE和OPTIMISTIC标志的链路本地地址,作为源地址发送RS请求报文。

static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)
{struct inet6_ifaddr *ifp;struct net_device *dev = idev->dev;bool clear_token, update_rs = false;struct in6_addr ll_addr;BUILD_BUG_ON(sizeof(token->s6_addr) != 16);memcpy(idev->token.s6_addr + 8, token->s6_addr + 8, 8);write_unlock_bh(&idev->lock);clear_token = ipv6_addr_any(token);if (clear_token) goto update_lft;if (!idev->dead && (idev->if_flags & IF_READY) &&!ipv6_get_lladdr(dev, &ll_addr, IFA_F_TENTATIVE |IFA_F_OPTIMISTIC)) {/* If we're not ready, then normal ifup will take care* of this. Otherwise, we need to request our rs here.*/ndisc_send_rs(dev, &ll_addr, &in6addr_linklocal_allrouters);update_rs = true;}

临时隐私地址,继承其生成地址的IFA_F_OPTIMISTIC标志。

static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
{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;}

在检查地址函数中,如果没有明确指定禁止optimistic地址,将认为其也是合法的。由于大多数情况下,optimistic和tentative标志都是同时设置了,这里将两者分开。

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 inet6_ifaddr *ifp;hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) {ndev = ifp->idev->dev;if (!net_eq(dev_net(ndev), net)) continue;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;

如下函数ipv6_lonely_lladdr判断ifp是否为接口上唯一的链路本地地址,由于addr_list是按照地址的scope排序的,如果第一个地址的scope大于IFA_LINK,表明链表中没有任何链路本地地址。

一个合法的地址,应当设置了IFA_F_PERMANENT标志,并且没有其它的三个标志。

static bool ipv6_lonely_lladdr(struct inet6_ifaddr *ifp)
{struct inet6_ifaddr *ifpiter;struct inet6_dev *idev = ifp->idev;list_for_each_entry_reverse(ifpiter, &idev->addr_list, if_list) {if (ifpiter->scope > IFA_LINK)break;if (ifp != ifpiter && ifpiter->scope == IFA_LINK &&(ifpiter->flags & (IFA_F_PERMANENT|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED)) ==IFA_F_PERMANENT)return false;}return true;
}

内核版本 5.10

optimistic-dad相关推荐

  1. Pessimistic and Optimistic locking

    事务隔离通常通过锁定任何对事务中资源的访问来实现的.总的来说,有两种方法针对事务的锁定:乐观锁(Pessimistic locking)和悲观锁(Optimistic locking) 悲观锁(Pes ...

  2. ADO.NET - Optimistic Pessimistic Concurrency

    Optimistic currency failed, http://msdn.microsoft.com/en-us/library/aa0416cz.aspx 转载于:https://www.cn ...

  3. An Optimistic Perspective on Offline Reinforcement Learning

    An Optimistic Perspective on Offline Reinforcement Learning 摘要 1 introduction 2 Off-policy Reinforce ...

  4. Lock Mode Type 之 Optimistic 使用场景

    JPA规范中,Lock Mode的值中包含了Optimistic, 一看就知道是用于乐观锁控制, 但是我一直被这个问题困扰,原因在于,在Entity中,已经使用@Version做了乐观锁控制,Hibe ...

  5. Optimistic Contracts

    1. 引言 Optimistic Contracts:Allowing for cheaper data submission when validation is expensive. Optimi ...

  6. P14 Optimistic Concurrency Control 课程观看笔记

    FaRM 所有服务器都在一个数据中心,但性能比Spanner高许多. 其中,有一个配置管理器,来决定每个数据分片中哪台服务器是primary,哪台是backup 通过ZooKeeper来帮助实现配置管 ...

  7. An Optimistic Perspective on Offline Reinforcement Learning(ICML2020)

    Abstract \quad 该文章利用了 the DQN replay dataset 研究了Offline RL,该数据集包含了一个 DQN agent 在60款Atari 2600游戏上的 th ...

  8. 万卷书 - 向阳而生 [The Optimistic Child]

    <向阳而生> Martin E.P. Seligman 保护儿童不受情绪低潮影响,并形成长期抗压力的有效方案 内容简介 <向阳而生>(1996年)探讨了培养孩子乐观思想的好处, ...

  9. 面试高频——JUC并发工具包快速上手(超详细总结)

    目录 一.什么是JUC 二.基本知识 2.1.进程和线程 2.2.Java默认有两个进程 2.3.Java能够开启线程吗? 2.4.并发和并行 2.5.线程的状态 2.6.wait和sleep的区别 ...

  10. 特征提取,转换和选择

    特征提取,转换和选择 Extracting, transforming and selecting features This section covers algorithms for workin ...

最新文章

  1. php post数据丢失
  2. Shell - 监控某个进程的内存占用情况、主机CPU、磁盘空间等信息以及守护进程
  3. 三种权重的初始化方法
  4. python 对图片进行直方图均衡化处理(批量自动遍历文件夹图片)
  5. qt中如何模拟按钮点击_qt – 在Windows中模拟鼠标按钮单击
  6. 暑假ACM集训第一周总结
  7. 学生专用计算机abs什么意思,出现abs标志什么意思
  8. 打印机与计算机接口大多数,打印机接口-西北师范大学.PPT
  9. 编写一个程序,输入一个句子,然后统计出这个句子当中不同的单词个数。
  10. #186-[栈]法力水晶
  11. MySQL数据库常用查询(带练习示例)
  12. Redis7.0的安装步骤
  13. Android把图片保存为pdf文件(附带iTextpdf.jar)
  14. c++ 调用python错误总结
  15. 王川: 深度学习有多深, 学了究竟有几分?
  16. Android课程设计--网上购物商城
  17. matlab 关联规则挖掘,数据挖掘实验(六)Matlab实现Apriori算法【关联规则挖掘】...
  18. 如何不需要再sudo输入密码
  19. 产品综合能力AA级 Smartbi一站式大数据分析平台获得高赞
  20. 在VMWare虚机上安装集客网关X86软件

热门文章

  1. 用PHP做一个简单的搜索功能
  2. 机器人布里茨哪个皮肤好看_蒸汽机器人皮肤特效,布里茨的皮肤介绍
  3. 设计心得笔记(旧版归档)
  4. vue 调用腾讯地图 https://apis.map.qq.com/ws/place/v1/suggestion/
  5. 给气象局割接搭建短信平台,彩信平台软件
  6. ajax接收后台传来的图片
  7. 可牛真的很牛,从安装开始就没有结束.
  8. 蓝桥杯物联网基础图文教程——GPIO输出控制LD5亮灭
  9. 【机器学习】DS的基础学习笔记1:线性回归
  10. 李小龙,不死的传说。