static int halist_proc_info(char *buffer, char **start, off_t offset, int length) { unsigned long flags; struct procinfo_iterator_args args; DEBUG_FUNC(); args.buffer = buffer; args.offset = offset; args.length = length; args.skip = 0; args.len = 0; read_lock_irqsave(&home_agents->lock, flags); hashlist_iterate(home_agents->entries, &args, procinfo_iterator); read_unlock_irqrestore(&home_agents->lock, flags); *start = buffer; if (offset) *start += offset % HALIST_INFO_LEN; args.len -= offset % HALIST_INFO_LEN; if (args.len > length) args.len = length; if (args.len < 0) args.len = 0; return args.len; }
/** * mipv6_ha_get_pref_list - Get list of preferred home agents * @ifindex: interface identifier * @addrs: pointer to a buffer to store the list * @max: maximum number of home agents to return * * Creates a list of @max preferred (or all known if less than @max) * home agents. Home Agents List is interface specific so you must * supply @ifindex. Stores list in addrs and returns number of home * agents stored. On failure, returns a negative value. **/ int mipv6_ha_get_pref_list(int ifindex, struct in6_addr **addrs, int max) { unsigned long flags; struct preflist_iterator_args args; if (max <= 0) { *addrs = NULL; return 0; } args.count = 0; args.requested = max; args.ifindex = ifindex; args.list = kmalloc(max * sizeof(struct in6_addr), GFP_ATOMIC); if (args.list == NULL) return -1; read_lock_irqsave(&home_agents->lock, flags); hashlist_iterate(home_agents->entries, &args, preflist_iterator); read_unlock_irqrestore(&home_agents->lock, flags); if (args.count >= 0) { *addrs = args.list; } else { kfree(args.list); *addrs = NULL; } return args.count; }
/* * Get memory for a new bcache entry. If bcache if full, a cache * entry may deleted to get space for a home registration, but not * vice versa. */ static struct mipv6_bcache_entry *mipv6_bcache_get_entry(__u8 type) { struct mipv6_bcache_entry *entry; struct cache_entry_iterator_args args; DEBUG_FUNC(); entry = (struct mipv6_bcache_entry *) mipv6_allocate_element(bcache->entry_pool); if (entry == NULL && type == HOME_REGISTRATION) { /* cache full, but need space for a home registration */ args.entry = &entry; hashlist_iterate(bcache->entries, &args, find_first_cache_entry_iterator); if (entry != NULL) { DEBUG((DBG_INFO, "cache entry: %x", entry)); if (hashlist_delete(bcache->entries, &entry->home_addr) < 0) DEBUG((DBG_ERROR, "bcache entry delete failed")); else entry = (struct mipv6_bcache_entry *) mipv6_allocate_element(bcache->entry_pool); } } return entry; }
/** * mipv6_halist_clean - remove all home agents for an interface * @ifindex: interface index * * Removes all Home Agents List entries for a given interface. **/ void mipv6_halist_clean(int ifindex) { unsigned long flags; int args; args = ifindex; write_lock_irqsave(&home_agents->lock, flags); hashlist_iterate(home_agents->entries, &args, cleaner_iterator); write_unlock_irqrestore(&home_agents->lock, flags); }
static int mipv6_halist_gc(int type) { int args; DEBUG_FUNC(); args = type; hashlist_iterate(home_agents->entries, &args, gc_iterator); return 0; }
/* * Get Home Agent Address for an interface */ int mipv6_ha_get_addr(int ifindex, struct in6_addr *addr) { unsigned long flags; struct getaddr_iterator_args args; struct net_device *dev; if (ifindex <= 0) return -1; if ((dev = dev_get_by_index(ifindex)) == NULL) return -1; memset(addr, 0, sizeof(struct in6_addr)); args.dev = dev; args.addr = addr; read_lock_irqsave(&home_agents->lock, flags); hashlist_iterate(home_agents->entries, &args, getaddr_iterator); #ifdef CONFIG_IPV6_MOBILITY_DEBUG printk(KERN_INFO "%s: interface = %s\n", __FUNCTION__, dev->name); printk(KERN_INFO "%s: home agent = %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", __FUNCTION__, ntohs(args.addr->s6_addr16[0]), ntohs(args.addr->s6_addr16[1]), ntohs(args.addr->s6_addr16[2]), ntohs(args.addr->s6_addr16[3]), ntohs(args.addr->s6_addr16[4]), ntohs(args.addr->s6_addr16[5]), ntohs(args.addr->s6_addr16[6]), ntohs(args.addr->s6_addr16[7])); #endif read_unlock_irqrestore(&home_agents->lock, flags); dev_put(dev); if (ipv6_addr_any(addr)) return -1; return 0; }
/** * mipv6_mn_get_pref_ha - get preferred home agent for prefix * @prefix: prefix * @plen: prefix length * * Is this useful? **/ struct in6_addr *mipv6_mn_get_prefha(struct in6_addr *prefix, int plen) { unsigned long flags; struct mipv6_halist_entry *entry = NULL; struct in6_addr *ha_addr; struct prefha_iterator_args args; DEBUG_FUNC(); ha_addr = kmalloc(sizeof(struct in6_addr), GFP_ATOMIC); if (ha_addr == NULL) { return NULL; } ipv6_addr_copy(&args.prefix, prefix); args.plen = plen; read_lock_irqsave(&home_agents->lock, flags); /* search for HA in home subnet with highest preference */ if (entry == NULL) { hashlist_iterate(home_agents->entries, &args, prefha_iterator); entry = args.entry; } /* no suitable HA could be found */ if (entry == NULL) { read_unlock_irqrestore(&home_agents->lock, flags); kfree(ha_addr); return NULL; } ipv6_addr_copy(ha_addr, &entry->global_addr); read_unlock_irqrestore(&home_agents->lock, flags); return ha_addr; }
/** * mipv6_bcache_add - add Binding Cache entry * @ifindex: interface index * @our_addr: own address * @home_addr: MN's home address * @coa: MN's care-of address * @lifetime: lifetime for this binding * @prefix: prefix length * @seq: sequence number * @single: single address bit * @type: type of entry * * Adds an entry for this @home_addr in the Binding Cache. If entry * already exists, old entry is updated. @type may be %CACHE_ENTRY or * %HOME_REGISTRATION. **/ int mipv6_bcache_add( int ifindex, struct in6_addr *our_addr, struct in6_addr *home_addr, struct in6_addr *coa, __u32 lifetime, __u8 prefix, __u8 seq, __u8 single, __u8 type) { unsigned long flags; struct mipv6_bcache_entry *entry; int update = 0; int create_tunnel = 0; unsigned long now = jiffies; struct cache_entry_iterator_args args; write_lock_irqsave(&bcache->lock, flags); if ((entry = (struct mipv6_bcache_entry *) hashlist_get(bcache->entries, home_addr)) != NULL) { /* if an entry for this home_addr exists (with smaller * seq than the new seq), update it by removing it * first */ if (SEQMOD(seq, entry->seq)) { DEBUG((DBG_INFO, "mipv6_bcache_add: updating an " "existing entry")); update = 1; if (entry->type == HOME_REGISTRATION) { create_tunnel = ipv6_addr_cmp(&entry->our_addr, our_addr) || ipv6_addr_cmp(&entry->coa, coa) || entry->ifindex != ifindex || entry->prefix != prefix || entry->single != single; if (create_tunnel || type != HOME_REGISTRATION) { mipv6_tunnel_route_del( &entry->home_addr, &entry->coa, &entry->our_addr); mipv6_tunnel_del(&entry->coa, &entry->our_addr); } if (type != HOME_REGISTRATION) { bcache_proxy_nd_rem(entry); } } } else { DEBUG((DBG_INFO, "mipv6_bcache_add: smaller seq " "than existing, not updating")); /* write_unlock_irqrestore(&bcache->lock, flags); return 0; */ } } else { /* no entry for this home_addr, try to create a new entry */ DEBUG((DBG_INFO, "mipv6_bcache_add: creating a new entry")); entry = mipv6_bcache_get_entry(type); if (entry == NULL) { /* delete next expiring entry of type CACHE_ENTRY */ args.entry = &entry; hashlist_iterate(bcache->entries, &args, find_first_cache_entry_iterator); if (entry == NULL) { DEBUG((DBG_INFO, "mipv6_bcache_add: cache full")); write_unlock_irqrestore(&bcache->lock, flags); return -1; } hashlist_delete(bcache->entries, &entry->home_addr); } create_tunnel = (type == HOME_REGISTRATION); } ipv6_addr_copy(&(entry->our_addr), our_addr); ipv6_addr_copy(&(entry->home_addr), home_addr); ipv6_addr_copy(&(entry->coa), coa); entry->ifindex = ifindex; entry->prefix = prefix; entry->seq = seq; entry->type = type; entry->last_used = 0; if (type == HOME_REGISTRATION) { entry->router = 0; entry->single = single; if (create_tunnel) { if (mipv6_tunnel_add(coa, our_addr, 0)) { DEBUG((DBG_INFO, "mipv6_bcache_add: no free tunnel devices!")); bcache_proxy_nd_rem(entry); if (update) hashlist_delete(bcache->entries, &entry->home_addr); mipv6_bcache_entry_free(entry); write_unlock_irqrestore(&bcache->lock, flags); return -1; } /* Todo: set the prefix length correctly */ if (mipv6_tunnel_route_add(home_addr, coa, our_addr)) { DEBUG((DBG_INFO, "mipv6_bcache_add: invalid route to home address!")); mipv6_tunnel_del(coa, our_addr); bcache_proxy_nd_rem(entry); if (update) hashlist_delete(bcache->entries, &entry->home_addr); mipv6_bcache_entry_free(entry); write_unlock_irqrestore(&bcache->lock, flags); return -1; } } } entry->last_br = 0; if (lifetime == EXPIRE_INFINITE) { entry->callback_time = EXPIRE_INFINITE; } else { entry->callback_time = now + lifetime * HZ; if (entry->type & (HOME_REGISTRATION | TEMPORARY_ENTRY)) entry->br_callback_time = 0; else entry->br_callback_time = now + (lifetime - BCACHE_BR_SEND_LEAD) * HZ; } if (update) { DEBUG((DBG_INFO, "updating entry : %x", entry)); hashlist_reschedule(bcache->entries, home_addr, entry->callback_time); } else { DEBUG((DBG_INFO, "adding entry: %x", entry)); if ((hashlist_add(bcache->entries, home_addr, entry->callback_time, entry)) < 0) { if (create_tunnel) { mipv6_tunnel_route_del(home_addr, coa, our_addr); mipv6_tunnel_del(coa, our_addr); bcache_proxy_nd_rem(entry); } mipv6_bcache_entry_free(entry); DEBUG((DBG_ERROR, "Hash add failed")); write_unlock_irqrestore(&bcache->lock, flags); return -1; } } set_timer(); write_unlock_irqrestore(&bcache->lock, flags); return 0; }