static int procinfo_iterator(void *data, void *args, struct in6_addr *addr, unsigned long *pref) { struct procinfo_iterator_args *arg = (struct procinfo_iterator_args *)args; struct mipv6_bcache_entry *entry = (struct mipv6_bcache_entry *)data; DEBUG_FUNC(); if (entry == NULL) return ITERATOR_ERR; if (entry->type == TEMPORARY_ENTRY) return ITERATOR_CONT; if (arg->skip < arg->offset / BC_INFO_LEN) { arg->skip++; return ITERATOR_CONT; } if (arg->len >= arg->length) return ITERATOR_CONT; arg->len += sprintf(arg->buffer + arg->len, "h=%04x%04x%04x%04x%04x%04x%04x%04x " "c=%04x%04x%04x%04x%04x%04x%04x%04x " "(e=%010lu,t=%02d)\n", NIPV6ADDR(&entry->home_addr), NIPV6ADDR(&entry->coa), ((entry->callback_time) - jiffies) / HZ, (int)entry->type); return ITERATOR_CONT; }
void mipv6_check_tunneled_packet(struct sk_buff *skb) { DEBUG_FUNC(); /* If tunnel flag was set */ if (skb->security & RCV_TUNNEL) { struct in6_addr coa; __u32 lifetime; mipv6_get_care_of_address(&skb->nh.ipv6h->daddr, &coa); lifetime = mipv6_mn_get_bulifetime(&skb->nh.ipv6h->daddr, &coa, 0); DEBUG((DBG_WARNING, "packet was tunneled. Sending BU to CN" "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&skb->nh.ipv6h->saddr))); /* This should work also with home address option */ mipv6_send_upd_option( &skb->nh.ipv6h->daddr, &skb->nh.ipv6h->saddr, CN_BU_DELAY, INITIAL_BINDACK_TIMEOUT, MAX_BINDACK_TIMEOUT, 1, #ifdef CN_REQ_ACK MIPV6_BU_F_ACK, /* ack */ #else 0, /* no ack */ #endif 64, lifetime, NULL); } }
/** * mn_handoff - called for every bul entry to send BU to CN * @rawentry: bul entry * @args: handoff event * @hashkey: * @sortkey: * * Since MN can have many home addresses and home networks, every BUL entry * needs to be checked **/ static int mn_handoff(void *rawentry, void *args, struct in6_addr *hashkey, unsigned long *sortkey) { __u32 lifetime; int athome; struct mipv6_bul_entry *entry = (struct mipv6_bul_entry *)rawentry; struct handoff *ho = (struct handoff *)args; int pfixlen = 64; /* Get the prefixlength instead of fixed 64 bits */ athome = mipv6_prefix_compare(&ho->rtr_new.raddr, &entry->home_addr, pfixlen); if (!athome) { lifetime = mipv6_mn_get_bulifetime(&entry->home_addr, &ho->rtr_new.CoA, entry->flags); } else { lifetime = 0; entry->flags = entry->flags | MIPV6_BU_F_DEREG; } if (entry->flags & MIPV6_BU_F_HOME) { DEBUG((DBG_INFO, "Sending home de ? %d registration for " "home address: %x:%x:%x:%x:%x:%x:%x:%x\n" "to home agent %x:%x:%x:%x:%x:%x:%x:%x, " "with lifetime %ld, prefixlength %d", athome, NIPV6ADDR(&entry->home_addr), NIPV6ADDR(&entry->cn_addr), lifetime, entry->prefix)); mipv6_send_upd_option( &entry->home_addr, &entry->cn_addr, HA_BU_DELAY, INITIAL_BINDACK_TIMEOUT, MAX_BINDACK_TIMEOUT, 1, entry->flags, entry->prefix, lifetime, NULL); } else { DEBUG((DBG_INFO, "Sending BU for home address: %x:%x:%x:%x:%x:%x:%x:%x \n" "to CN: %x:%x:%x:%x:%x:%x:%x:%x, " "with lifetime %ld", NIPV6ADDR(&entry->home_addr), NIPV6ADDR(&entry->cn_addr), lifetime)); mipv6_send_upd_option( &entry->home_addr, &entry->cn_addr, CN_BU_DELAY, INITIAL_BINDACK_TIMEOUT, MAX_BINDACK_TIMEOUT, 1, entry->flags, entry->prefix, lifetime, NULL); } return ITERATOR_CONT; }
int mipv6_parse_opts(struct in6_addr *CoA, __u8 *ptr,__u8 *end, __u8 send) { DEBUG_FUNC(); ptr += 2; /* To skip to option type */ while (ptr < end){ DEBUG((AH_PARSE,"dst or hbh opt of type: %x", ptr[0])); /* if (opt_len > end - ptr){ DEBUG((DBG_ERROR, " opt of incorrect length ?")); return -1; }*/ if (ptr && ptr[0] & 0x20){ /* mutable */ DEBUG((AH_PARSE, "Mutable dest. or hop option")); DEBUG((AH_PARSE, "Of type: %x", ptr[0])); /* memset(ptr,0,ptr[1] + 2); Set option to zero */ } /* Swap home address and CoA */ if (ptr[0] == 0xc9 && send) { struct in6_addr coa; if(!CoA || !(ptr + 2)) { DEBUG((DBG_ERROR, "AH calc got null saddr or home address opt")); return -1; } ipv6_addr_copy(&coa, CoA); ipv6_addr_copy(CoA, ((struct in6_addr *)(&ptr[2]))); DEBUG((AH_PARSE, "Setting source address to " "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(CoA))); ipv6_addr_copy((struct in6_addr *)(&ptr[2]), &coa); DEBUG((AH_PARSE, "Setting home address to " "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR((struct in6_addr *)(&ptr[2])))); } /* length of option + length of type and length fields*/ ptr = ptr + ptr[1] + 2; } return 0; }
/** * init_home_registration - start Home Registration process * @hinfo: mn_info entry for the home address * @coa: care-of address * * Checks whether we have a Home Agent address for this home address. * If not starts Dynamic Home Agent Address Discovery. Otherwise * tries to register with home agent if not already registered. **/ int init_home_registration(struct mn_info *hinfo, struct in6_addr *coa) { __u32 lifetime; if (mipv6_prefix_compare(&hinfo->home_addr, coa, hinfo->home_plen)) { hinfo->is_at_home = 1; DEBUG((DBG_INFO, "Adding home address, MN at home")); return 0; } if (ipv6_addr_any(&hinfo->ha)) { DEBUG((DBG_INFO, "Home Agent address not set, initiating DHAAD")); mipv6_mn_dhaad_send_req(&hinfo->home_addr, hinfo->home_plen, &hinfo->dhaad_id); } else { struct mipv6_bul_entry *bul = mipv6_bul_get(&hinfo->ha); if (bul) { if (!ipv6_addr_cmp(&bul->home_addr, &hinfo->home_addr)){ DEBUG((DBG_INFO, "BU already sent to HA")); mipv6_bul_put(bul); return 0; } mipv6_bul_put(bul); } lifetime = mipv6_mn_get_bulifetime( &hinfo->home_addr, coa, MIPV6_BU_F_HOME | MIPV6_BU_F_ACK); DEBUG((DBG_INFO, "Sending initial home registration for " "home address: %x:%x:%x:%x:%x:%x:%x:%x\n" "to home agent %x:%x:%x:%x:%x:%x:%x:%x, " "with lifetime %ld, prefixlength %d", NIPV6ADDR(&hinfo->home_addr), NIPV6ADDR(&hinfo->ha), lifetime, 0)); mipv6_send_upd_option( &hinfo->home_addr, &hinfo->ha, HA_BU_DELAY, INITIAL_BINDACK_DAD_TIMEOUT, MAX_BINDACK_TIMEOUT, 1, MIPV6_BU_F_HOME | MIPV6_BU_F_ACK | MIPV6_BU_F_DAD, 0, lifetime, NULL); } return 0; }
static int procinfo_iterator(void *data, void *args, struct in6_addr *addr, unsigned long *pref) { struct procinfo_iterator_args *arg = (struct procinfo_iterator_args *)args; struct mipv6_halist_entry *entry = (struct mipv6_halist_entry *)data; int expire; DEBUG_FUNC(); if (entry == NULL) return ITERATOR_ERR; if (time_after(jiffies, entry->expire)) return ITERATOR_DELETE_ENTRY; if (arg->skip < arg->offset / HALIST_INFO_LEN) { arg->skip++; return ITERATOR_CONT; } if (arg->len >= arg->length) return ITERATOR_CONT; expire = (entry->expire - jiffies) / HZ; arg->len += sprintf(arg->buffer + arg->len, "%02d %04x%04x%04x%04x%04x%04x%04x%04x " "%04x%04x%04x%04x%04x%04x%04x%04x %05d %05d\n", entry->ifindex, NIPV6ADDR(&entry->global_addr), NIPV6ADDR(&entry->link_local_addr), -(entry->preference - PREF_BASE), expire); return ITERATOR_CONT; }
/* Used for sending, sets seq number to network byte order */ static void seq_incr(struct sec_as *sa, __u32 *seq){ unsigned long flags; spin_lock_irqsave(&seq_lock, flags); /* TODO: Handle wraparound */ sa->replay_count++; *seq = htonl(sa->replay_count); spin_unlock_irqrestore(&seq_lock, flags); if (sa->replay_count == 0) { DEBUG((DBG_ERROR, "Sequence number wraparound for" "outbound security association with address" "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&sa->addr))); /* TODO: start sa_acquire */ } }
static int mipv6_mn_tunnel_rcv_send_bu_hook( struct ipv6_tunnel *t, struct sk_buff *skb, __u32 flags) { struct ipv6hdr *inner = (struct ipv6hdr *)skb->h.raw; struct ipv6hdr *outer = skb->nh.ipv6h; struct mn_info *minfo = NULL; __u32 lifetime; DEBUG_FUNC(); if (!(flags & IPV6_T_F_MIPV6_DEV)) return IPV6_TUNNEL_ACCEPT; read_lock(&mn_info_lock); minfo = mipv6_mn_get_info(&inner->daddr); if (!minfo) { DEBUG((DBG_WARNING, "MN info missing")); read_unlock(&mn_info_lock); return IPV6_TUNNEL_ACCEPT; } /* We don't send bus in response to all tunneled packets */ if (!ipv6_addr_cmp(&minfo->ha, &inner->saddr)) { DEBUG((DBG_ERROR, "HA BUG: Received a tunneled packet " "originally sent by home agent, not sending BU")); read_unlock(&mn_info_lock); return IPV6_TUNNEL_ACCEPT; } if (ipv6_addr_cmp(&minfo->ha, &outer->saddr)) { DEBUG((DBG_WARNING, "MIPV6 MN: Received a tunneled IPv6 packet" " that was not tunneled by HA %x:%x:%x:%x:%x:%x:%x:%x," " but by %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&minfo->ha), NIPV6ADDR(&outer->saddr))); read_unlock(&mn_info_lock); return IPV6_TUNNEL_ACCEPT; } read_unlock(&mn_info_lock); DEBUG((DBG_INFO, "Sending BU to correspondent node")); if (inner->nexthdr != IPPROTO_DSTOPTS) { DEBUG((DBG_INFO, "Received tunneled packet without dst_opts")); lifetime = mipv6_mn_get_bulifetime(&inner->daddr, &outer->daddr, 0); if(lifetime) /* max wait 1000 ms before sending an empty packet with BU */ mipv6_send_upd_option(&inner->daddr,&inner->saddr, CN_BU_DELAY, INITIAL_BINDACK_TIMEOUT, MAX_BINDACK_TIMEOUT , 1, #ifdef CN_REQ_ACK MIPV6_BU_F_ACK, /* ack */ #else 0, /* no ack */ #endif 0, lifetime, NULL); } /* (Mis)use ipsec tunnel flag */ DEBUG((DBG_INFO, "setting rcv_tunnel flag in skb")); skb->security = skb->security | RCV_TUNNEL; return IPV6_TUNNEL_ACCEPT; }
/* Prepares the data for ah icv and returns pointeres both to the skb->ah and buff->ah */ struct sk_buff *mipv6_prepare_dgram(struct sk_buff **pskb, struct mipv6_ah **ah_orig, struct mipv6_ah **ah, __u8 send) { struct sk_buff *buff=NULL; __u8 *ptr,nhdr; unsigned int len, optlen; DEBUG_FUNC(); if (!(*pskb)){ DEBUG((DBG_WARNING,"mipv6_prepare_dgram: Got null skb as argument")); return NULL; } buff = skb_copy(*pskb, GFP_ATOMIC); if (!buff || !buff->nh.ipv6h){ DEBUG((DBG_ERROR, "mipv6_prepare_dgram: buff or ipv6 header was null")); return NULL; } len = ntohs(buff->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr); ptr = (__u8*)buff->nh.ipv6h + sizeof(struct ipv6hdr); if (!ptr){ DEBUG((DBG_ERROR, "mipv6_prepare_dgram: pointer to nexthdr was null")); return NULL; } nhdr = buff->nh.ipv6h->nexthdr; buff->nh.ipv6h->hop_limit = 0; buff->nh.ipv6h->tclass1 = 0; memset(buff->nh.ipv6h->tclass2_flow, 0, 3); while (nhdr != NEXTHDR_NONE) { switch (nhdr) { case NEXTHDR_HOP: case NEXTHDR_DEST: /* Check mutability and if 1 replace with zeroes */ optlen = (ptr[1] + 1) << 3; debug_print_buffer(DBG_DATADUMP, (void *)ptr, optlen); if(ptr + optlen > (__u8 *)buff->nh.ipv6h + len){ DEBUG((DBG_WARNING, "Invalid opt.len %x, ptr: %x , buff->tail %x", optlen, ptr, buff->tail)); return NULL; } DEBUG((AH_DUMP, "source address before parse opts " "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&buff->nh.ipv6h->saddr))); mipv6_parse_opts(&buff->nh.ipv6h->saddr, ptr, ptr + optlen, send); DEBUG((AH_PARSE, "AH calc set source address to " "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&buff->nh.ipv6h->saddr))); /* Print the option */ nhdr = *ptr; ptr += optlen; break; case NEXTHDR_ROUTING: /* swap addresses etc. */ optlen = ((ptr[1] +1) << 3); if(ptr + optlen > (__u8 *)buff->nh.ipv6h + len) { DEBUG((DBG_ERROR, "received rt header with incorrect length!")); return NULL; } /* We don't process incoming rt headers */ if (send && mipv6_ah_rt_header((struct rt0_hdr *)ptr, &buff->nh.ipv6h->daddr) < 0) return NULL; nhdr = *ptr; ptr += optlen; break; case NEXTHDR_FRAGMENT: /* barf */ DEBUG((DBG_ERROR, "Don't know how to handle fragmentation header")); return NULL; case NEXTHDR_AUTH: optlen = ((ptr[1]+2)<<2); if(ptr + optlen > (__u8 *)buff->nh.ipv6h + len) { DEBUG((DBG_ERROR, "received auth header with incorrect length!")); return NULL; } /* We calculate the corresponding location in the * original unmodified skb and add the AH pointer */ *ah_orig = (struct mipv6_ah*)ptr; *ah = (struct mipv6_ah*)( (__u8*)((*pskb)->nh.ipv6h) + (ptr - (__u8*)(buff->nh.ipv6h))); nhdr = *ptr; debug_print_buffer(AH_DUMP, (void*)ptr, optlen); ptr += optlen; return buff; DEBUG((AH_PARSE, "returning buff. ")); default: /* Payload or unknown ext.hdrs*/ return buff; } } DEBUG((AH_PARSE,"No next header to process\n")); return buff; }
/* * Removes all expired entries */ static void expire(void) { struct mipv6_bcache_entry *entry; unsigned long now = jiffies; unsigned long flags; struct br_addrs { struct in6_addr daddr; struct in6_addr saddr; struct br_addrs *next; }; struct br_addrs *br_info = NULL; DEBUG_FUNC(); write_lock_irqsave(&bcache->lock, flags); while ((entry = (struct mipv6_bcache_entry *) hashlist_get_first(bcache->entries)) != NULL) { if (entry->callback_time <= now) { DEBUG((DBG_INFO, "expire(): an entry expired")); if (entry->type & HOME_REGISTRATION) { mipv6_tunnel_route_del(&entry->home_addr, &entry->coa, &entry->our_addr); mipv6_tunnel_del(&entry->coa, &entry->our_addr); bcache_proxy_nd_rem(entry); } hashlist_delete(bcache->entries, &entry->home_addr); mipv6_bcache_entry_free(entry); entry = NULL; } else if (entry->br_callback_time != 0 && entry->br_callback_time <= now && entry->type & !(HOME_REGISTRATION | TEMPORARY_ENTRY)) { if (now - entry->last_used < BCACHE_BR_SEND_THRESHOLD * HZ) { struct br_addrs *tmp; tmp = br_info; DEBUG((DBG_INFO, "bcache entry recently used. Sending BR.")); /* queue for sending */ br_info = kmalloc(sizeof(struct br_addrs), GFP_ATOMIC); if (br_info) { ipv6_addr_copy(&br_info->saddr, &entry->our_addr); ipv6_addr_copy(&br_info->daddr, &entry->home_addr); br_info->next = tmp; entry->last_br = now; } else { br_info = tmp; DEBUG((DBG_ERROR, "Out of memory")); } } entry->br_callback_time = 0; } else { break; } } write_unlock_irqrestore(&bcache->lock, flags); while (br_info) { struct br_addrs *tmp = br_info->next; if (mipv6_send_rq_option(&br_info->saddr, &br_info->daddr, 0, NULL) < 0) DEBUG((DBG_WARNING, "BR send for %x:%x:%x:%x:%x:%x:%x:%x failed", NIPV6ADDR(&br_info->daddr))); kfree(br_info); br_info = tmp; } return; }