static int ip6_dst_lookup_tail(struct sock *sk, struct dst_entry **dst, struct flowi *fl) { int err; if (*dst == NULL) *dst = ip6_route_output(sk, fl); if ((err = (*dst)->error)) goto out_err_release; if (ipv6_addr_any(&fl->fl6_src)) { err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src); if (err) goto out_err_release; } #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 */ if (!((*dst)->neighbour->nud_state & NUD_VALID)) { struct inet6_ifaddr *ifp; struct flowi fl_gw; int redirect; ifp = ipv6_get_ifaddr(&fl->fl6_src, (*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_gw, fl, sizeof(struct flowi)); memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr)); *dst = ip6_route_output(sk, &fl_gw); if ((err = (*dst)->error)) goto out_err_release; } } #endif return 0; out_err_release: dst_release(*dst); *dst = NULL; return err; }
/** * mipv6_mn_get_bulifetime - Get lifetime for a binding update * @home_addr: home address for BU * @coa: care-of address for BU * @flags: flags used for BU * * Returns maximum lifetime for BUs determined by the lifetime of * care-of address and the lifetime of home address. **/ __u32 mipv6_mn_get_bulifetime(struct in6_addr *home_addr, struct in6_addr *coa, __u8 flags) { __u32 lifetime; struct inet6_ifaddr * ifp_coa, *ifp_hoa; ifp_hoa = ipv6_get_ifaddr(home_addr, NULL); if(!ifp_hoa) { DEBUG((DBG_ERROR, "home address missing")); return 0; } ifp_coa = ipv6_get_ifaddr(coa, NULL); if (!ifp_coa) { in6_ifa_put(ifp_hoa); DEBUG((DBG_ERROR, "care-of address missing")); return 0; } if (flags & MIPV6_BU_F_HOME) lifetime = HA_BU_DEF_LIFETIME; else lifetime = CN_BU_DEF_LIFETIME; if (!(ifp_hoa->flags & IFA_F_PERMANENT)){ if (ifp_hoa->valid_lft) lifetime = min_t(__u32, lifetime, ifp_hoa->valid_lft); else DEBUG((DBG_ERROR, "Zero lifetime for home address")); } if (!(ifp_coa->flags & IFA_F_PERMANENT)) { if(ifp_coa->valid_lft) lifetime = min_t(__u32, lifetime, ifp_coa->valid_lft); else DEBUG((DBG_ERROR, "Zero lifetime for home address")); } in6_ifa_put(ifp_hoa); in6_ifa_put(ifp_coa); DEBUG((DBG_INFO, "Lifetime for binding is %ld", lifetime)); return lifetime; }
} void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, const struct in6_addr *daddr, const struct in6_addr *solicited_addr, int router, int solicited, int override, int inc_opt) { struct in6_addr tmpaddr; struct inet6_ifaddr *ifp; const struct in6_addr *src_addr; struct icmp6hdr icmp6h = { .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, }; /* 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 = 0; 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; } icmp6h.icmp6_router = router;
static int ip6_dst_lookup_tail(struct sock *sk, struct dst_entry **dst, struct flowi6 *fl6) { struct net *net = sock_net(sk); #ifdef CONFIG_IPV6_OPTIMISTIC_DAD struct neighbour *n; #endif int err; if (*dst == NULL) *dst = ip6_route_output(net, sk, fl6); if ((err = (*dst)->error)) goto out_err_release; if (ipv6_addr_any(&fl6->saddr)) { struct rt6_info *rt = (struct rt6_info *) *dst; err = ip6_route_get_saddr(net, rt, &fl6->daddr, sk ? inet6_sk(sk)->srcprefs : 0, &fl6->saddr); if (err) goto out_err_release; } #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 */ rcu_read_lock(); n = dst_get_neighbour(*dst); if (n && !(n->nud_state & NUD_VALID)) { struct inet6_ifaddr *ifp; struct flowi6 fl_gw6; int redirect; rcu_read_unlock(); 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); if ((err = (*dst)->error)) goto out_err_release; } } else { rcu_read_unlock(); } #endif return 0; out_err_release: if (err == -ENETUNREACH) IP6_INC_STATS_BH(net, NULL, IPSTATS_MIB_OUTNOROUTES); dst_release(*dst); *dst = NULL; return err; }
static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk, struct dst_entry **dst, struct flowi6 *fl6) { #ifdef CONFIG_IPV6_OPTIMISTIC_DAD struct neighbour *n; struct rt6_info *rt; #endif int err; int flags = 0; /* The correct way to handle this would be to do * ip6_route_get_saddr, and then ip6_route_output; however, * the route-specific preferred source forces the * ip6_route_output call _before_ ip6_route_get_saddr. * * In source specific routing (no src=any default route), * ip6_route_output will fail given src=any saddr, though, so * that's why we try it again later. */ if (ipv6_addr_any(&fl6->saddr) && (!*dst || !(*dst)->error)) { struct rt6_info *rt; bool had_dst = *dst != NULL; if (!had_dst) *dst = ip6_route_output(net, sk, fl6); rt = (*dst)->error ? NULL : (struct rt6_info *)*dst; err = ip6_route_get_saddr(net, rt, &fl6->daddr, sk ? inet6_sk(sk)->srcprefs : 0, &fl6->saddr); if (err) goto out_err_release; /* If we had an erroneous initial result, pretend it * never existed and let the SA-enabled version take * over. */ if (!had_dst && (*dst)->error) { dst_release(*dst); *dst = NULL; } if (fl6->flowi6_oif) flags |= RT6_LOOKUP_F_IFACE; } if (!*dst) *dst = ip6_route_output_flags(net, sk, fl6, flags); err = (*dst)->error; if (err) goto out_err_release; #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; int redirect; 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 return 0; out_err_release: if (err == -ENETUNREACH) IP6_INC_STATS(net, NULL, IPSTATS_MIB_OUTNOROUTES); dst_release(*dst); *dst = NULL; return err; }
in6_dev_put(idev); } static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, struct in6_addr *daddr, struct in6_addr *solicited_addr, int router, int solicited, int override, int inc_opt) { struct in6_addr tmpaddr; struct inet6_ifaddr *ifp; struct in6_addr *src_addr; struct icmp6hdr icmp6h = { .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, }; /* for anycast or proxy, solicited_addr != src_addr */ ifp = ipv6_get_ifaddr(solicited_addr, dev, 1); if (ifp) { src_addr = solicited_addr; if (ifp->flags & IFA_F_OPTIMISTIC) override = 0; in6_ifa_put(ifp); } else { if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr)) return; src_addr = &tmpaddr; } icmp6h.icmp6_router = router; icmp6h.icmp6_solicited = solicited; icmp6h.icmp6_override = override;
void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len) { struct prefix_info *pinfo; struct rt6_info *rt; __u32 valid_lft; __u32 prefered_lft; int addr_type; unsigned long rt_expires; struct inet6_dev *in6_dev; pinfo = (struct prefix_info *) opt; if (len < sizeof(struct prefix_info)) { ADBG(("addrconf: prefix option too short\n")); return; } /* * Validation checks ([ADDRCONF], page 19) */ addr_type = ipv6_addr_type(&pinfo->prefix); if (addr_type & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)) return; valid_lft = ntohl(pinfo->valid); prefered_lft = ntohl(pinfo->prefered); if (prefered_lft > valid_lft) { if (net_ratelimit()) printk(KERN_WARNING "addrconf: prefix option has invalid lifetime\n"); return; } in6_dev = in6_dev_get(dev); if (in6_dev == NULL) { if (net_ratelimit()) printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name); return; } /* * Two things going on here: * 1) Add routes for on-link prefixes * 2) Configure prefixes with the auto flag set */ /* Avoid arithemtic overflow. Really, we could save rt_expires in seconds, likely valid_lft, but it would require division in fib gc, that it not good. */ if (valid_lft >= 0x7FFFFFFF/HZ) rt_expires = 0; else rt_expires = jiffies + valid_lft * HZ; rt = rt6_lookup(&pinfo->prefix, NULL, dev->ifindex, 1); if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) { if (rt->rt6i_flags&RTF_EXPIRES) { if (pinfo->onlink == 0 || valid_lft == 0) { ip6_del_rt(rt); rt = NULL; } else { rt->rt6i_expires = rt_expires; } } } else if (pinfo->onlink && valid_lft) { addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len, dev, rt_expires, RTF_ADDRCONF|RTF_EXPIRES); } if (rt) dst_release(&rt->u.dst); /* Try to figure out our local address for this prefix */ if (pinfo->autoconf && in6_dev->cnf.autoconf) { struct inet6_ifaddr * ifp; struct in6_addr addr; int plen; plen = pinfo->prefix_len >> 3; if (pinfo->prefix_len == 64) { memcpy(&addr, &pinfo->prefix, 8); if (ipv6_generate_eui64(addr.s6_addr + 8, dev) && ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) { in6_dev_put(in6_dev); return; } goto ok; } if (net_ratelimit()) printk(KERN_DEBUG "IPv6 addrconf: prefix with wrong length %d\n", pinfo->prefix_len); in6_dev_put(in6_dev); return; ok: ifp = ipv6_get_ifaddr(&addr, dev); if (ifp == NULL && valid_lft) { ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len, addr_type&IPV6_ADDR_SCOPE_MASK, 0); if (ifp == NULL) { in6_dev_put(in6_dev); return; } addrconf_dad_start(ifp); } if (ifp && valid_lft == 0) { ipv6_del_addr(ifp); ifp = NULL; } if (ifp) { int flags; spin_lock(&ifp->lock); ifp->valid_lft = valid_lft; ifp->prefered_lft = prefered_lft; ifp->tstamp = jiffies; flags = ifp->flags; ifp->flags &= ~IFA_F_DEPRECATED; spin_unlock(&ifp->lock); if (!(flags&IFA_F_TENTATIVE)) ipv6_ifa_notify((flags&IFA_F_DEPRECATED) ? 0 : RTM_NEWADDR, ifp); in6_ifa_put(ifp); } } in6_dev_put(in6_dev); }