static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen) { struct inet6_ifaddr *ifp; struct inet6_dev *idev; struct device *dev; int scope; if ((dev = dev_get_by_index(ifindex)) == NULL) return -ENODEV; if ((idev = ipv6_get_idev(dev)) == NULL) return -ENXIO; scope = ipv6_addr_scope(pfx); for (ifp = idev->addr_list; ifp; ifp=ifp->if_next) { if (ifp->scope == scope && ifp->prefix_len == plen && (!memcmp(pfx, &ifp->addr, sizeof(struct in6_addr)))) { ipv6_del_addr(ifp); /* If the last address is deleted administratively, disable IPv6 on this interface. */ if (idev->addr_list == NULL) addrconf_ifdown(idev->dev, 1); return 0; } } return -EADDRNOTAVAIL; }
/* * Manual configuration of address on an interface */ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen) { struct inet6_ifaddr *ifp; struct inet6_dev *idev; struct net_device *dev; int scope; ASSERT_RTNL(); if ((dev = __dev_get_by_index(ifindex)) == NULL) return -ENODEV; if (!(dev->flags&IFF_UP)) return -ENETDOWN; if ((idev = addrconf_add_dev(dev)) == NULL) return -ENOBUFS; scope = ipv6_addr_scope(pfx); if ((ifp = ipv6_add_addr(idev, pfx, plen, scope, IFA_F_PERMANENT)) != NULL) { addrconf_dad_start(ifp); in6_ifa_put(ifp); return 0; } return -ENOBUFS; }
/* * Manual configuration of address on an interface */ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen) { struct inet6_ifaddr *ifp; struct inet6_dev *idev; struct device *dev; int scope; if ((dev = dev_get_by_index(ifindex)) == NULL) return -ENODEV; if (!(dev->flags&IFF_UP)) return -ENETDOWN; if ((idev = addrconf_add_dev(dev)) == NULL) return -ENOBUFS; scope = ipv6_addr_scope(pfx); if ((ifp = ipv6_add_addr(idev, pfx, scope)) == NULL) return -ENOMEM; ifp->prefix_len = plen; ifp->flags |= ADDR_PERMANENT; addrconf_dad_start(ifp); return 0; }
/* * Choose an apropriate source address * should do: * i) get an address with an apropriate scope * ii) see if there is a specific route for the destination and use * an address of the attached interface * iii) don't use deprecated addresses */ int ipv6_get_saddr(struct dst_entry *dst, struct in6_addr *daddr, struct in6_addr *saddr) { int scope; struct inet6_ifaddr *ifp = NULL; struct inet6_ifaddr *match = NULL; struct net_device *dev = NULL; struct inet6_dev *idev; struct rt6_info *rt; int err; rt = (struct rt6_info *) dst; if (rt) dev = rt->rt6i_dev; scope = ipv6_addr_scope(daddr); if (rt && (rt->rt6i_flags & RTF_ALLONLINK)) { /* * route for the "all destinations on link" rule * when no routers are present */ scope = IFA_LINK; } /* * known dev * search dev and walk through dev addresses */ if (dev) { if (dev->flags & IFF_LOOPBACK) scope = IFA_HOST; read_lock(&addrconf_lock); idev = __in6_dev_get(dev); if (idev) { read_lock_bh(&idev->lock); for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) { if (ifp->scope == scope) { if (!(ifp->flags & (IFA_F_DEPRECATED|IFA_F_TENTATIVE))) { in6_ifa_hold(ifp); read_unlock_bh(&idev->lock); read_unlock(&addrconf_lock); goto out; } if (!match && !(ifp->flags & IFA_F_TENTATIVE)) { match = ifp; in6_ifa_hold(ifp); } } } read_unlock_bh(&idev->lock); } read_unlock(&addrconf_lock); } if (scope == IFA_LINK) goto out; /* * dev == NULL or search failed for specified dev */ read_lock(&dev_base_lock); read_lock(&addrconf_lock); for (dev = dev_base; dev; dev=dev->next) { idev = __in6_dev_get(dev); if (idev) { read_lock_bh(&idev->lock); for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) { if (ifp->scope == scope) { if (!(ifp->flags&(IFA_F_DEPRECATED|IFA_F_TENTATIVE))) { in6_ifa_hold(ifp); read_unlock_bh(&idev->lock); goto out_unlock_base; } if (!match && !(ifp->flags&IFA_F_TENTATIVE)) { match = ifp; in6_ifa_hold(ifp); } } } read_unlock_bh(&idev->lock); } } out_unlock_base: read_unlock(&addrconf_lock); read_unlock(&dev_base_lock); out: if (ifp == NULL) { ifp = match; match = NULL; } err = -EADDRNOTAVAIL; if (ifp) { ipv6_addr_copy(saddr, &ifp->addr); err = 0; in6_ifa_put(ifp); } if (match) in6_ifa_put(match); return err; }
int getsock(char *tname, struct sockaddr_in6 *psin6_arg, int scope) { #ifdef HAVE_IFADDRS_H static struct ifaddrs *pifa_head; struct ifaddrs *pifa; struct sockaddr_in6 *psin6; char strbuf[128]; int ifindex = 0; int s; if (!pifa_head && getifaddrs(&pifa_head)) { tst_resm(TBROK, "%s: getifaddrs failed", tname); return -1; } if (psin6_arg) ifindex = psin6_arg->sin6_scope_id; /* first, find a global address */ for (pifa = pifa_head; pifa; pifa = pifa->ifa_next) { int this_scope; if (!(pifa->ifa_flags & IFF_UP)) continue; if (pifa->ifa_addr->sa_family != AF_INET6) continue; psin6 = (struct sockaddr_in6 *)pifa->ifa_addr; this_scope = ipv6_addr_scope(&psin6->sin6_addr); if (this_scope && ((this_scope < 0 && -this_scope == scope) || (this_scope > 0 && this_scope != scope))) continue; psin6->sin6_scope_id = if_nametoindex(pifa->ifa_name); if ((ifindex < 0 && -ifindex == psin6->sin6_scope_id) || (ifindex > 0 && ifindex != psin6->sin6_scope_id)) continue; s = socket(PF_INET6, SOCK_DGRAM, 0); if (s < 0) { tst_resm(TBROK, "%s: socket %s", tname, strerror(errno)); return -1; } if (bind(s, pifa->ifa_addr, sizeof(struct sockaddr_in6)) < 0) { tst_resm(TBROK, "%s: bind \"%s\": %s", tname, inet_ntop(AF_INET6, &psin6->sin6_addr, strbuf, sizeof(strbuf)), strerror(errno)); return -1; } if (psin6_arg) { *psin6_arg = *psin6; psin6_arg->sin6_scope_id = if_nametoindex(pifa->ifa_name); } return s; } { char *scopestr, *intfstr; switch (scope) { case IPV6_ADDR_NODE: scopestr = " node-local"; break; case IPV6_ADDR_LINK: scopestr = " link-local"; break; case IPV6_ADDR_GLOBAL: scopestr = " global"; break; default: scopestr = ""; break; } if (ifindex < 0) { intfstr = " not on ifindex"; ifindex = -ifindex; } else if (ifindex) intfstr = " on ifindex"; else intfstr = 0; if (intfstr) tst_resm(TBROK, "%s: getsock : no%s addresses%s %d", tname, scopestr, intfstr, ifindex); else tst_resm(TBROK, "%s: getsock : no%s addresses", tname, scopestr); } return -1; #else /* HAVE_IFADDRS_H */ return -1; #endif }
/* * Choose an apropriate source address * should do: * i) get an address with an apropriate scope * ii) see if there is a specific route for the destination and use * an address of the attached interface * iii) don't use deprecated addresses * * at the moment I believe only iii) is missing. */ struct inet6_ifaddr * ipv6_get_saddr(struct dst_entry *dst, struct in6_addr *daddr) { int scope; struct inet6_ifaddr *ifp = NULL; struct inet6_ifaddr *match = NULL; struct device *dev = NULL; struct rt6_info *rt; int i; rt = (struct rt6_info *) dst; if (rt) dev = rt->rt6i_dev; atomic_inc(&addr_list_lock); scope = ipv6_addr_scope(daddr); if (rt && (rt->rt6i_flags & RTF_ALLONLINK)) { /* * route for the "all destinations on link" rule * when no routers are present */ scope = IFA_LINK; } /* * known dev * search dev and walk through dev addresses */ if (dev) { struct inet6_dev *idev; int hash; if (dev->flags & IFF_LOOPBACK) scope = IFA_HOST; hash = ipv6_devindex_hash(dev->ifindex); for (idev = inet6_dev_lst[hash]; idev; idev=idev->next) { if (idev->dev == dev) { for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) { if (ifp->scope == scope) { if (!(ifp->flags & ADDR_STATUS)) goto out; if (!(ifp->flags & ADDR_INVALID)) match = ifp; } } break; } } } if (scope == IFA_LINK) goto out; /* * dev == NULL or search failed for specified dev */ for (i=0; i < IN6_ADDR_HSIZE; i++) { for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) { if (ifp->scope == scope) { if (!(ifp->flags & ADDR_STATUS)) goto out; if (!(ifp->flags & ADDR_INVALID)) match = ifp; } } } out: if (ifp == NULL) ifp = match; atomic_dec(&addr_list_lock); return ifp; }
/* * Choose an apropriate source address * should do: * i) get an address with an apropriate scope * ii) see if there is a specific route for the destination and use * an address of the attached interface * iii) don't use deprecated addresses */ int ipv6_get_saddr(struct dst_entry *dst, struct in6_addr *daddr, struct in6_addr *saddr) { int scope; struct inet6_ifaddr *ifp = NULL; struct inet6_ifaddr *match = NULL; struct device *dev = NULL; struct rt6_info *rt; int deprecated = 1; int matchlen = 0; int err; int i; rt = (struct rt6_info *) dst; if (rt) dev = rt->rt6i_dev; addrconf_lock(); scope = ipv6_addr_scope(daddr); if (rt && (rt->rt6i_flags & RTF_ALLONLINK)) { /* * route for the "all destinations on link" rule * when no routers are present */ scope = IFA_LINK; } /* * known dev * search dev and walk through dev addresses */ if (dev) { struct inet6_dev *idev; int hash; if (dev->flags & IFF_LOOPBACK) scope = IFA_HOST; hash = ipv6_devindex_hash(dev->ifindex); for (idev = inet6_dev_lst[hash]; idev; idev=idev->next) { if (idev->dev == dev) { for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) { if (ifp->scope == scope) { int newlen = inet6_addr_diff(daddr, &ifp->addr); if (newlen < matchlen) continue; if (!(ifp->flags & (ADDR_STATUS|DAD_STATUS))) { match = ifp; matchlen = newlen; deprecated = 0; continue; } if(!deprecated) continue; if (!(ifp->flags & (ADDR_INVALID|DAD_STATUS))) { match = ifp; matchlen = newlen; continue; } } } break; } } } if (match != NULL && !deprecated) goto out; if (scope == IFA_LINK) goto out; /* * dev == NULL or search failed for specified dev */ for (i=0; i < IN6_ADDR_HSIZE; i++) { for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) { if (ifp->scope == scope) { int newlen = inet6_addr_diff(daddr, &ifp->addr); if (newlen < matchlen) continue; if (!(ifp->flags & (ADDR_STATUS|DAD_STATUS))) { match = ifp; matchlen = newlen; deprecated = 0; continue; } if (!deprecated) continue; if (!(ifp->flags & (ADDR_INVALID|DAD_STATUS))) { match = ifp; matchlen = newlen; continue; } } } } out: if (ifp == NULL) ifp = match; err = -ENETUNREACH; if (ifp) { memcpy(saddr, &ifp->addr, sizeof(struct in6_addr)); err = 0; } addrconf_unlock(); return err; }