static struct inet6_dev * ipv6_add_dev(struct device *dev) { struct inet6_dev *ndev, **bptr, *iter; int hash; if (dev->mtu < IPV6_MIN_MTU) return NULL; ndev = kmalloc(sizeof(struct inet6_dev), GFP_KERNEL); if (ndev) { char name[64]; memset(ndev, 0, sizeof(struct inet6_dev)); ndev->dev = dev; memcpy(&ndev->cnf, &ipv6_devconf_dflt, sizeof(ndev->cnf)); ndev->cnf.mtu6 = dev->mtu; ndev->cnf.sysctl = NULL; ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl); if (ndev->nd_parms == NULL) { kfree(ndev); return NULL; } ipv6_statistics.Ip6LastChange = timeticks(jiffies); ndev->stats.ipv6.Ip6LastChange = timeticks(jiffies); #ifdef CONFIG_PROC_FS sprintf(name, "%d", dev->ifindex); ndev->stats.proc_dir_entry = create_proc_entry(name, 0, proc_net_devsnmp6); if (!ndev->stats.proc_dir_entry) printk(KERN_WARNING "addrconf_notify(): cannot create /proc/net/dev_snmp6/%s\n",name); ndev->stats.proc_dir_entry->read_proc = afinet6_read_devsnmp; ndev->stats.proc_dir_entry->data = ndev; #endif #ifdef CONFIG_SYSCTL neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6"); addrconf_sysctl_register(ndev, &ndev->cnf); #endif hash = ipv6_devindex_hash(dev->ifindex); bptr = &inet6_dev_lst[hash]; iter = *bptr; for (; iter; iter = iter->next) bptr = &iter->next; *bptr = ndev; } return ndev; }
struct inet6_dev * ipv6_get_idev(struct device *dev) { struct inet6_dev *idev; int hash; hash = ipv6_devindex_hash(dev->ifindex); for (idev = inet6_dev_lst[hash]; idev; idev = idev->next) { if (idev->dev == dev) return idev; } return NULL; }
struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev) { struct inet6_ifaddr *ifp; struct inet6_dev *idev; int hash; 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 == IFA_LINK) return ifp; } break; } } return NULL; }
static struct inet6_dev * ipv6_add_dev(struct device *dev) { struct inet6_dev *ndev, **bptr, *iter; int hash; if (dev->mtu < IPV6_MIN_MTU) return NULL; ndev = kmalloc(sizeof(struct inet6_dev), gfp_any()); if (ndev) { memset(ndev, 0, sizeof(struct inet6_dev)); ndev->dev = dev; memcpy(&ndev->cnf, &ipv6_devconf_dflt, sizeof(ndev->cnf)); ndev->cnf.mtu6 = dev->mtu; ndev->cnf.sysctl = NULL; ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl); if (ndev->nd_parms == NULL) { kfree(ndev); return NULL; } #ifdef CONFIG_SYSCTL neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6"); addrconf_sysctl_register(ndev, &ndev->cnf); #endif hash = ipv6_devindex_hash(dev->ifindex); bptr = &inet6_dev_lst[hash]; iter = *bptr; for (; iter; iter = iter->next) bptr = &iter->next; *bptr = ndev; } return ndev; }
/* * 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; }