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; }
static struct inet6_dev * ipv6_find_idev(struct device *dev) { struct inet6_dev *idev; if ((idev = ipv6_get_idev(dev)) == NULL) { idev = ipv6_add_dev(dev); if (idev == NULL) return NULL; } return idev; }
static struct inet6_dev * ipv6_find_idev(struct device *dev) { struct inet6_dev *idev; if ((idev = ipv6_get_idev(dev)) == NULL) { idev = ipv6_add_dev(dev); if (idev == NULL) return NULL; } if (dev->flags&IFF_UP) ipv6_mc_up(idev); return idev; }
struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev) { struct inet6_ifaddr *ifp = NULL; struct inet6_dev *idev; if ((idev = ipv6_get_idev(dev)) != NULL) { addrconf_lock(); for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) { if (ifp->scope == IFA_LINK) break; } addrconf_unlock(); } return ifp; }
static int ndisc_constructor(struct neighbour *neigh) { struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key; struct device *dev = neigh->dev; struct inet6_dev *in6_dev = ipv6_get_idev(dev); int addr_type; if (in6_dev == NULL) return -EINVAL; addr_type = ipv6_addr_type(addr); if (in6_dev->nd_parms) neigh->parms = in6_dev->nd_parms; if (addr_type&IPV6_ADDR_MULTICAST) neigh->type = RTN_MULTICAST; else neigh->type = RTN_UNICAST; if (dev->hard_header == NULL) { neigh->nud_state = NUD_NOARP; neigh->ops = &ndisc_direct_ops; neigh->output = neigh->ops->queue_xmit; } else { if (addr_type&IPV6_ADDR_MULTICAST) { neigh->nud_state = NUD_NOARP; ndisc_mc_map(addr, neigh->ha, dev, 1); } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) { neigh->nud_state = NUD_NOARP; memcpy(neigh->ha, dev->dev_addr, dev->addr_len); if (dev->flags&IFF_LOOPBACK) neigh->type = RTN_LOCAL; } else if (dev->flags&IFF_POINTOPOINT) { neigh->nud_state = NUD_NOARP; memcpy(neigh->ha, dev->broadcast, dev->addr_len); } if (dev->hard_header_cache) neigh->ops = &ndisc_hh_ops; else neigh->ops = &ndisc_generic_ops; if (neigh->nud_state&NUD_VALID) neigh->output = neigh->ops->connected_output; else neigh->output = neigh->ops->output; } return 0; }
/* * device multicast group inc (add if not found) */ int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr) { struct ifmcaddr6 *mc; struct inet6_dev *idev; int hash; idev = ipv6_get_idev(dev); if (idev == NULL) return -EINVAL; hash = ipv6_addr_hash(addr); for (mc = inet6_mcast_lst[hash]; mc; mc = mc->next) { if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0 && mc->dev == dev) { atomic_inc(&mc->mca_users); return 0; } } /* * not found: create a new one. */ mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC); if (mc == NULL) return -ENOMEM; memset(mc, 0, sizeof(struct ifmcaddr6)); mc->mca_timer.function = igmp6_timer_handler; mc->mca_timer.data = (unsigned long) mc; memcpy(&mc->mca_addr, addr, sizeof(struct in6_addr)); mc->dev = dev; atomic_set(&mc->mca_users, 1); mc->next = inet6_mcast_lst[hash]; inet6_mcast_lst[hash] = mc; mc->if_next = idev->mc_list; idev->mc_list = mc; igmp6_group_added(mc); return 0; }
static void pndisc_destructor(struct pneigh_entry *n) { struct in6_addr *addr = (struct in6_addr*)&n->key; struct in6_addr maddr; struct device *dev = n->dev; if (dev == NULL || ipv6_get_idev(dev) == NULL) return; #ifndef CONFIG_IPV6_NO_PB addrconf_addr_solict_mult_old(addr, &maddr); ipv6_dev_mc_dec(dev, &maddr); #endif #ifdef CONFIG_IPV6_EUI64 addrconf_addr_solict_mult_new(addr, &maddr); ipv6_dev_mc_dec(dev, &maddr); #endif }
static int igmp6_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data) { off_t pos=0, begin=0; struct ifmcaddr6 *im; int len=0; struct device *dev; for (dev = dev_base; dev; dev = dev->next) { struct inet6_dev *idev; if ((idev = ipv6_get_idev(dev)) == NULL) continue; for (im = idev->mc_list; im; im = im->if_next) { int i; len += sprintf(buffer+len,"%-4d %-15s ", dev->ifindex, dev->name); for (i=0; i<16; i++) len += sprintf(buffer+len, "%02x", im->mca_addr.s6_addr[i]); len+=sprintf(buffer+len, " %5d %08X %ld\n", atomic_read(&im->mca_users), im->mca_flags, (im->mca_flags&MAF_TIMER_RUNNING) ? im->mca_timer.expires-jiffies : 0); pos=begin+len; if (pos < offset) { len=0; begin=pos; } if (pos > offset+length) goto done; } } *eof = 1; done: *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) len=length; return len; }
static void ipv6_mca_remove(struct device *dev, struct ifmcaddr6 *ma) { struct inet6_dev *idev; idev = ipv6_get_idev(dev); if (idev) { struct ifmcaddr6 *iter, **lnk; for (lnk = &idev->mc_list; (iter = *lnk) != NULL; lnk = &iter->if_next) { if (iter == ma) { *lnk = iter->if_next; return; } } } }
int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len) { struct ifmcaddr6 *ma; struct in6_addr *addrp; unsigned long resptime; if (len < sizeof(struct icmp6hdr) + sizeof(struct in6_addr)) return -EINVAL; /* Drop queries with not link local source */ if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL)) return -EINVAL; resptime = ntohs(hdr->icmp6_maxdelay); /* Translate milliseconds to jiffies */ resptime = (resptime<<10)/(1024000/HZ); addrp = (struct in6_addr *) (hdr + 1); if (ipv6_addr_any(addrp)) { struct inet6_dev *idev; idev = ipv6_get_idev(skb->dev); if (idev == NULL) return 0; for (ma = idev->mc_list; ma; ma=ma->if_next) igmp6_group_queried(ma, resptime); } else { int hash = ipv6_addr_hash(addrp); for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) { if (ma->dev == skb->dev && ipv6_addr_cmp(addrp, &ma->mca_addr) == 0) { igmp6_group_queried(ma, resptime); break; } } } return 0; }
void addrconf_prefix_rcv(struct 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 = ipv6_get_idev(dev); if (in6_dev == NULL) { printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name); return; } 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) { printk(KERN_WARNING "addrconf: prefix option has invalid lifetime\n"); return; } /* * If we where using an "all destinations on link" route * delete it */ rt6_purge_dflt_routers(RTF_ALLONLINK); /* * 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); } 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; #ifdef CONFIG_IPV6_EUI64 if (pinfo->prefix_len == 64) { memcpy(&addr, &pinfo->prefix, 8); if (ipv6_generate_eui64(addr.s6_addr + 8, dev)) return; goto ok; } #endif #ifndef CONFIG_IPV6_NO_PB if (pinfo->prefix_len == ((sizeof(struct in6_addr) - dev->addr_len)<<3)) { memcpy(&addr, &pinfo->prefix, plen); memcpy(addr.s6_addr + plen, dev->dev_addr, dev->addr_len); goto ok; } #endif printk(KERN_DEBUG "IPv6 addrconf: prefix with wrong length %d\n", pinfo->prefix_len); return; ok: ifp = ipv6_chk_addr(&addr, dev, 1); if ((ifp == NULL || (ifp->flags&ADDR_INVALID)) && valid_lft) { if (ifp == NULL) ifp = ipv6_add_addr(in6_dev, &addr, addr_type & IPV6_ADDR_SCOPE_MASK); if (ifp == NULL) return; ifp->prefix_len = pinfo->prefix_len; addrconf_dad_start(ifp); } if (ifp && valid_lft == 0) { ipv6_del_addr(ifp); ifp = NULL; } if (ifp) { int event = 0; ifp->valid_lft = valid_lft; ifp->prefered_lft = prefered_lft; ifp->tstamp = jiffies; if (ifp->flags & ADDR_INVALID) event = RTM_NEWADDR; ifp->flags &= ~(ADDR_DEPRECATED|ADDR_INVALID); ipv6_ifa_notify(event, ifp); } } }