/* * 6lowpan neighbor discovery * * Copyright (C) 2013 INRIA. * * This file is subject to the terms and conditions of the GNU Lesser General * Public License. See the file LICENSE in the top level directory for more * details. * * @ingroup sixlowpan * @{ * @file sixlownd.c * @brief 6lowpan neighbor discovery functions * @author Stephan Zeisberg * @author Martin Lenders * @author Oliver Gesch * @author Oliver Hahm * @} */ #include #include #include #include "vtimer.h" #include "mutex.h" #include "net_if.h" #include "sixlowpan/error.h" #include "ip.h" #include "icmp.h" #include "serialnumber.h" #include "net_help.h" #define ENABLE_DEBUG (0) #if ENABLE_DEBUG #define DEBUG_ENABLED #endif #include "debug.h" #define LLHDR_ICMPV6HDR_LEN (LL_HDR_LEN + IPV6_HDR_LEN + ICMPV6_HDR_LEN) #define ND_HOPLIMIT (0xFF) /* parameter problem [rfc4443] */ #define PARA_PROB_LEN (8) /* echo request */ #define ECHO_REQ_LEN (4) /* echo reply */ #define ECHO_REPL_LEN (4) /* router solicitation */ #define RTR_SOL_LEN (4) #define RTR_SOL_INTERVAL (4) #define RTR_SOL_MAX (3) /* router advertisment */ #define RTR_ADV_MAX (3) #define RTR_ADV_MAX_INTERVAL (600) #define RTR_ADV_LEN (12) /* neighbour solicitation */ #define NBR_SOL_LEN (20) /* neighbour advertisement */ #define NBR_ADV_LEN (20) /* stllao option rfc4861 4.6.1 */ #define OPT_STLLAO_MIN_LEN (8) #define OPT_STLLAO_MAX_LEN (16) /* prefix info option rfc 4.6.2 */ #define OPT_PI_LIST_LEN (5) //TODO: initalwert suchen #define OPT_PI_TYPE (3) #define OPT_PI_LEN (4) #define OPT_PI_HDR_LEN (32) /* mtu option rfc4861 4.6.4 */ #define OPT_MTU_TYPE (5) #define OPT_MTU_LEN (1) #define OPT_MTU_HDR_LEN (8) /* aro - address registration option rfc6775 4.1 */ #define OPT_ARO_TYPE (33) #define OPT_ARO_LEN (2) #define OPT_ARO_HDR_LEN (16) #define OPT_ARO_LTIME (300) /* TODO: geeigneten wert finden */ /* 6lowpan context option */ #define OPT_6CO_TYPE (34) #define OPT_6CO_MIN_LEN (2) #define OPT_6CO_MAX_LEN (3) #define OPT_6CO_HDR_LEN (8) #define OPT_6CO_LTIME (5) /* TODO geeigneten Wert finden */ /* authoritative border router option */ #define OPT_ABRO_TYPE (35) #define OPT_ABRO_LEN (3) #define OPT_ABRO_HDR_LEN (24) /* authoritive border router cache size */ #define ABR_CACHE_SIZE (2) /* neighbor cache size */ #define NBR_CACHE_SIZE (8) #define NBR_CACHE_LTIME_TEN (20) /* default router list size */ #define DEF_RTR_LST_SIZE (3) /* geeigneten wert finden */ /* extern variables */ uint8_t ipv6_ext_hdr_len = 0; /* counter */ uint8_t abr_count = 0; uint8_t nbr_count = 0; uint8_t def_rtr_count = 0; uint8_t rtr_sol_count = 0; uint8_t prefix_count = 0; /* datastructures */ ndp_a6br_cache_t abr_cache[ABR_CACHE_SIZE]; ndp_neighbor_cache_t nbr_cache[NBR_CACHE_SIZE]; ndp_default_router_list_t def_rtr_lst[DEF_RTR_LST_SIZE]; ndp_prefix_list_t plist[OPT_PI_LIST_LEN]; /* pointer */ static uint8_t *llao; addr_list_t *addr_list_ptr; static ipv6_hdr_t *ipv6_buf; static icmpv6_hdr_t *icmp_buf; static icmpv6_router_adv_hdr_t *rtr_adv_buf; static icmpv6_neighbor_sol_hdr_t *nbr_sol_buf; static icmpv6_neighbor_adv_hdr_t *nbr_adv_buf; static icmpv6_ndp_opt_hdr_t *opt_buf; static icmpv6_ndp_opt_stllao_t *opt_stllao_buf; static icmpv6_ndp_opt_mtu_t *opt_mtu_buf; static icmpv6_ndp_opt_abro_t *opt_abro_buf; static icmpv6_ndp_opt_6co_hdr_t *opt_6co_hdr_buf; static uint8_t *opt_6co_prefix_buf; static icmpv6_ndp_opt_pi_t *opt_pi_buf; static icmpv6_ndp_opt_aro_t *opt_aro_buf; ndp_neighbor_cache_t *nbr_entry; ndp_default_router_list_t *def_rtr_entry; /* elements */ //ipv6_addr_t tmpaddr; uint8_t recvd_cids[NDP_6LOWPAN_CONTEXT_MAX]; uint8_t icmpv6_opt_hdr_len = 0; uint8_t recvd_cids_len = 0; ndp_prefix_list_t *recvd_prefixes[OPT_PI_LIST_LEN]; uint8_t recvd_pref_len = 0; void def_rtr_lst_add(ipv6_addr_t *ipaddr, uint32_t rtr_ltime); void def_rtr_lst_rem(ndp_default_router_list_t *entry); uint8_t nbr_cache_add(ipv6_addr_t *ipaddr, ieee_802154_long_t *laddr, uint8_t isrouter, ndp_nce_state_t state, ndp_nce_type_t type, uint16_t ltime, ieee_802154_short_t *saddr); void nbr_cache_rem(ipv6_addr_t *addr); /** * @brief Set Source link-layer address option according to interface * configuration. * * @param[out] sllao The SLLAO to set. * @param[in] type The value for the type field of the SLLAO. * @param[in] length The value for the length field of the SLLAO */ void icmpv6_ndp_set_sllao(icmpv6_ndp_opt_stllao_t *sllao, uint8_t type, uint8_t length); int min(int a, int b) { if (a < b) { return a; } else { return b; } } static icmpv6_parameter_prob_hdr_t *get_para_prob_buf(uint8_t ext_len) { return ((icmpv6_parameter_prob_hdr_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len]); } static icmpv6_echo_request_hdr_t *get_echo_req_buf(uint8_t ext_len) { return ((icmpv6_echo_request_hdr_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len]); } static icmpv6_echo_reply_hdr_t *get_echo_repl_buf(uint8_t ext_len) { return ((icmpv6_echo_reply_hdr_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len]); } static icmpv6_router_adv_hdr_t *get_rtr_adv_buf(uint8_t ext_len) { return ((icmpv6_router_adv_hdr_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len]); } static icmpv6_neighbor_sol_hdr_t *get_nbr_sol_buf(uint8_t ext_len) { return ((icmpv6_neighbor_sol_hdr_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len]); } static icmpv6_neighbor_adv_hdr_t *get_nbr_adv_buf(uint8_t ext_len) { return ((icmpv6_neighbor_adv_hdr_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len]); } static icmpv6_ndp_opt_hdr_t *get_opt_buf(uint8_t ext_len, uint8_t opt_len) { return ((icmpv6_ndp_opt_hdr_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len + opt_len]); } static icmpv6_ndp_opt_stllao_t *get_opt_stllao_buf(uint8_t ext_len, uint8_t opt_len) { return ((icmpv6_ndp_opt_stllao_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len + opt_len]); } static icmpv6_ndp_opt_mtu_t *get_opt_mtu_buf(uint8_t ext_len, uint8_t opt_len) { return ((icmpv6_ndp_opt_mtu_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len + opt_len]); } static icmpv6_ndp_opt_abro_t *get_opt_abro_buf(uint8_t ext_len, uint8_t opt_len) { return ((icmpv6_ndp_opt_abro_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len + opt_len]); } static icmpv6_ndp_opt_6co_hdr_t *get_opt_6co_hdr_buf(uint8_t ext_len, uint8_t opt_len) { return ((icmpv6_ndp_opt_6co_hdr_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len + opt_len]); } static uint8_t *get_opt_6co_prefix_buf(uint8_t ext_len, uint8_t opt_len) { return ((uint8_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len + opt_len]); } static icmpv6_ndp_opt_pi_t *get_opt_pi_buf(uint8_t ext_len, uint8_t opt_len) { return ((icmpv6_ndp_opt_pi_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len + opt_len]); } static icmpv6_ndp_opt_aro_t *get_opt_aro_buf(uint8_t ext_len, uint8_t opt_len) { return ((icmpv6_ndp_opt_aro_t *) &buffer[LLHDR_ICMPV6HDR_LEN + ext_len + opt_len]); } void icmpv6_send_echo_request(ipv6_addr_t *destaddr, uint16_t id, uint16_t seq, char *data, size_t data_len) { uint16_t packet_length; ipv6_buf = ipv6_get_buf(); icmp_buf = get_icmpv6_buf(ipv6_ext_hdr_len); icmpv6_echo_request_hdr_t *echo_buf = get_echo_req_buf(ipv6_ext_hdr_len); char *echo_data_buf = ((char *)echo_buf) + sizeof(icmpv6_echo_request_hdr_t); icmp_buf->type = ICMPV6_TYPE_ECHO_REQUEST; icmp_buf->code = 0; ipv6_buf->version_trafficclass = IPV6_VER; ipv6_buf->trafficclass_flowlabel = 0; ipv6_buf->flowlabel = 0; ipv6_buf->nextheader = IPV6_PROTO_NUM_ICMPV6; ipv6_buf->hoplimit = ipv6_get_default_hop_limit(); memcpy(&ipv6_buf->destaddr, destaddr, sizeof(ipv6_addr_t)); ipv6_iface_get_best_src_addr(&ipv6_buf->srcaddr, &ipv6_buf->destaddr); echo_buf->id = id; echo_buf->seq = seq; memcpy(echo_data_buf, data, data_len); packet_length = IPV6_HDR_LEN + ICMPV6_HDR_LEN + ipv6_ext_hdr_len + ECHO_REQ_LEN + data_len; ipv6_buf->length = packet_length - IPV6_HDR_LEN; icmp_buf->checksum = 0; icmp_buf->checksum = ~icmpv6_csum(IPV6_PROTO_NUM_ICMPV6); #ifdef DEBUG_ENABLED char addr_str[IPV6_MAX_ADDR_STR_LEN]; printf("INFO: send echo request to: %s\n", ipv6_addr_to_str(addr_str, &ipv6_buf->destaddr)); #endif ipv6_send_packet(ipv6_buf); } void icmpv6_send_echo_reply(ipv6_addr_t *destaddr, uint16_t id, uint16_t seq, char *data, size_t data_len) { uint16_t packet_length; ipv6_buf = ipv6_get_buf(); icmp_buf = get_icmpv6_buf(ipv6_ext_hdr_len); icmpv6_echo_reply_hdr_t *echo_buf = get_echo_repl_buf(ipv6_ext_hdr_len); char *echo_data_buf = ((char *)echo_buf) + sizeof(icmpv6_echo_reply_hdr_t); icmp_buf->type = ICMPV6_TYPE_ECHO_REPLY; icmp_buf->code = 0; ipv6_buf->version_trafficclass = IPV6_VER; ipv6_buf->trafficclass_flowlabel = 0; ipv6_buf->flowlabel = 0; ipv6_buf->nextheader = IPV6_PROTO_NUM_ICMPV6; ipv6_buf->hoplimit = ipv6_get_default_hop_limit(); memcpy(&ipv6_buf->destaddr, destaddr, sizeof(ipv6_addr_t)); ipv6_iface_get_best_src_addr(&ipv6_buf->srcaddr, &ipv6_buf->destaddr); echo_buf->id = id; echo_buf->seq = seq; memcpy(echo_data_buf, data, data_len); packet_length = IPV6_HDR_LEN + ICMPV6_HDR_LEN + ipv6_ext_hdr_len + ECHO_REPL_LEN + data_len; ipv6_buf->length = packet_length - IPV6_HDR_LEN; icmp_buf->checksum = 0; icmp_buf->checksum = ~icmpv6_csum(IPV6_PROTO_NUM_ICMPV6); #ifdef DEBUG_ENABLED char addr_str[IPV6_MAX_ADDR_STR_LEN]; printf("INFO: send echo request to: %s\n", ipv6_addr_to_str(addr_str, &ipv6_buf->destaddr)); #endif ipv6_send_packet(ipv6_buf); } /* send router solicitation message - RFC4861 section 4.1 */ void icmpv6_send_router_sol(uint8_t sllao) { uint16_t packet_length; ipv6_buf = ipv6_get_buf(); icmp_buf = get_icmpv6_buf(ipv6_ext_hdr_len); icmp_buf->type = ICMPV6_TYPE_ROUTER_SOL; icmp_buf->code = 0; ipv6_buf->version_trafficclass = IPV6_VER; ipv6_buf->trafficclass_flowlabel = 0; ipv6_buf->flowlabel = 0; ipv6_buf->nextheader = IPV6_PROTO_NUM_ICMPV6; ipv6_buf->hoplimit = ND_HOPLIMIT; ipv6_addr_set_all_routers_addr(&ipv6_buf->destaddr); //iface_find_src_ipaddr(&ipv6_buf->srcaddr, NDP_ADDR_STATE_PREFERRED, /* IPV6_ADDR_TYPE_MULTICAST); */ ipv6_iface_get_best_src_addr(&(ipv6_buf->srcaddr), &(ipv6_buf->destaddr)); icmpv6_opt_hdr_len = RTR_SOL_LEN; ipv6_buf->length = HTONS(ICMPV6_HDR_LEN + RTR_SOL_LEN + OPT_STLLAO_MAX_LEN); if (sllao == OPT_SLLAO) { opt_stllao_buf = get_opt_stllao_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); icmpv6_ndp_set_sllao(opt_stllao_buf, NDP_OPT_SLLAO_TYPE, 2); packet_length = IPV6_HDR_LEN + ICMPV6_HDR_LEN + ipv6_ext_hdr_len + RTR_SOL_LEN + OPT_STLLAO_MAX_LEN; } else { packet_length = IPV6_HDR_LEN + ICMPV6_HDR_LEN + ipv6_ext_hdr_len + RTR_SOL_LEN; } icmp_buf->checksum = 0; icmp_buf->checksum = ~icmpv6_csum(IPV6_PROTO_NUM_ICMPV6); #ifdef DEBUG_ENABLED char addr_str[IPV6_MAX_ADDR_STR_LEN]; printf("INFO: send router solicitation to: %s\n", ipv6_addr_to_str(addr_str, &ipv6_buf->destaddr)); #endif ipv6_send_packet(ipv6_buf); } void recv_echo_req(void) { ipv6_buf = ipv6_get_buf(); icmpv6_echo_request_hdr_t *echo_buf = get_echo_req_buf(ipv6_ext_hdr_len); char *echo_data_buf = ((char *)echo_buf) + sizeof(icmpv6_echo_reply_hdr_t); size_t data_len = ipv6_buf->length - (IPV6_HDR_LEN + ICMPV6_HDR_LEN + ipv6_ext_hdr_len + ECHO_REQ_LEN); #ifdef DEBUG_ENABLED char addr_str[IPV6_MAX_ADDR_STR_LEN]; printf("INFO: received echo request from: %s\n", ipv6_addr_to_str(addr_str, &ipv6_buf->srcaddr)); printf("\n"); printf("id = 0x%04x, seq = %d\n", echo_buf->id, echo_buf->seq); for (size_t i = 0; i < data_len; i++) { printf("%02x ", echo_data_buf[i]); if (!((i + 1) % 16) || i == data_len - 1) { printf("\n"); } } #endif icmpv6_send_echo_reply(&ipv6_buf->srcaddr, echo_buf->id, echo_buf->seq, echo_data_buf, data_len); } void recv_echo_repl(void) { #ifdef DEBUG_ENABLED ipv6_buf = ipv6_get_buf(); icmpv6_echo_reply_hdr_t *echo_buf = get_echo_repl_buf(ipv6_ext_hdr_len); char *echo_data_buf = ((char *)echo_buf) + sizeof(icmpv6_echo_reply_hdr_t); size_t data_len = ipv6_buf->length - ICMPV6_HDR_LEN - ECHO_REPL_LEN; char addr_str[IPV6_MAX_ADDR_STR_LEN]; printf("INFO: received echo reply from: %s\n", ipv6_addr_to_str(addr_str, &ipv6_buf->srcaddr)); printf("\n"); printf("id = 0x%04x, seq = %d\n", echo_buf->id, echo_buf->seq); for (size_t i = 0; i < data_len; i++) { printf("%02x ", echo_data_buf[i]); if (!((i + 1) % 16) || i == data_len - 1) { printf("\n"); } } #else return; #endif } void recv_rtr_sol(void) { icmpv6_opt_hdr_len = RTR_SOL_LEN; ipv6_buf = ipv6_get_buf(); /* check if source option is set*/ if (opt_stllao_buf->type == NDP_OPT_SLLAO_TYPE) { opt_stllao_buf = get_opt_stllao_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); llao = (uint8_t *)opt_stllao_buf; icmpv6_opt_hdr_len += (opt_stllao_buf->length) << 3; } if (llao != NULL) { nbr_entry = ndp_neighbor_cache_search(&ipv6_buf->srcaddr); if (nbr_entry != NULL) { /* found neighbor in cache, update values and check long addr */ if (memcmp(&llao[2], &nbr_entry->laddr, 8) == 0) { nbr_entry->isrouter = 0; } else { /* new long addr found, update */ memcpy(&nbr_entry->laddr, &llao[2], 8); nbr_entry->state = NDP_NCE_STATUS_STALE; nbr_entry->isrouter = 0; } } else { /* nothing found, add neigbor into cache*/ nbr_cache_add(&ipv6_buf->srcaddr, (ieee_802154_long_t *)&llao[2], 0, NDP_NCE_STATUS_STALE, NDP_NCE_TYPE_TENTATIVE, NBR_CACHE_LTIME_TEN, NULL); } } /* send solicited router advertisment */ if (abr_count > 0) { icmpv6_send_router_adv(&ipv6_buf->srcaddr, 0, 0, OPT_PI, OPT_6CO, OPT_ABRO); } else { icmpv6_send_router_adv(&ipv6_buf->srcaddr, 0, 0, OPT_PI, 0, 0); } #ifdef DEBUG_ENABLED char addr_str[IPV6_MAX_ADDR_STR_LEN]; printf("INFO: send router advertisment to: %s\n", ipv6_addr_to_str(addr_str, &ipv6_buf->destaddr)); #endif } uint8_t set_opt_6co_flags(uint8_t compression_flag, uint8_t cid) { uint8_t flags; if (compression_flag) { flags = ICMPV6_NDP_OPT_6CO_FLAG_CCOMPR; } else { flags = 0; } flags |= cid & ICMPV6_NDP_OPT_6CO_FLAG_CID; return flags; } void get_opt_6co_flags(uint8_t *compression_flag, uint8_t *cid, uint8_t flags) { compression_flag[0] = flags & ICMPV6_NDP_OPT_6CO_FLAG_CID; compression_flag[0] = compression_flag[0] != 0; cid[0] = flags & ICMPV6_NDP_OPT_6CO_FLAG_CID; } lowpan_context_t *abr_get_context(ndp_a6br_cache_t *abr, uint8_t cid); void icmpv6_send_router_adv(ipv6_addr_t *addr, uint8_t sllao, uint8_t mtu, uint8_t pi, uint8_t sixco, uint8_t abro) { uint16_t packet_length; lowpan_context_t *contexts = NULL; ndp_a6br_cache_t *msg_abr = NULL; ipv6_buf = ipv6_get_buf(); icmp_buf = get_icmpv6_buf(ipv6_ext_hdr_len); ipv6_buf->version_trafficclass = IPV6_VER; ipv6_buf->trafficclass_flowlabel = 0; ipv6_buf->flowlabel = 0; ipv6_buf->nextheader = IPV6_PROTO_NUM_ICMPV6; ipv6_buf->hoplimit = ND_HOPLIMIT; if (addr == NULL) { /* not solicited */ ipv6_addr_set_all_nodes_addr(&ipv6_buf->destaddr); } else { memcpy(&ipv6_buf->destaddr, addr, 16); } ipv6_iface_get_best_src_addr(&(ipv6_buf->srcaddr), &(ipv6_buf->destaddr)); icmp_buf->type = ICMPV6_TYPE_ROUTER_ADV; icmp_buf->code = 0; //TODO: gethoplimit func, set current ttl rtr_adv_buf = get_rtr_adv_buf(ipv6_ext_hdr_len); rtr_adv_buf->hoplimit = MULTIHOP_HOPLIMIT; /* set M and O flag, last 6 bits are zero */ rtr_adv_buf->autoconfig_flags = ICMPV6_ROUTER_ADV_FLAG_MANAGED | ICMPV6_ROUTER_ADV_FLAG_OTHER; rtr_adv_buf->router_lifetime = HTONS(RTR_ADV_MAX_INTERVAL * RTR_ADV_MAX); rtr_adv_buf->reachable_time = 0; rtr_adv_buf->retrans_timer = 0; icmpv6_opt_hdr_len = RTR_ADV_LEN; packet_length = IPV6_HDR_LEN + ICMPV6_HDR_LEN + RTR_ADV_LEN; if (sllao == OPT_SLLAO) { /* set link layer address option */ opt_stllao_buf = get_opt_stllao_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); icmpv6_ndp_set_sllao(opt_stllao_buf, NDP_OPT_SLLAO_TYPE, 2); icmpv6_opt_hdr_len += OPT_STLLAO_MAX_LEN; packet_length += OPT_STLLAO_MAX_LEN; } if (mtu == OPT_MTU) { /* set MTU options */ opt_mtu_buf = get_opt_mtu_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); opt_mtu_buf->type = OPT_MTU_TYPE; opt_mtu_buf->length = OPT_MTU_LEN; opt_mtu_buf->reserved = 0; opt_mtu_buf->mtu = HTONL(1500); icmpv6_opt_hdr_len += OPT_MTU_HDR_LEN; packet_length += OPT_MTU_HDR_LEN; } /* set payload length field */ if (abro == OPT_ABRO) { /* set authoritive border router option */ if (abr_count > 0) { msg_abr = ndp_a6br_cache_get_most_current(); opt_abro_buf = get_opt_abro_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); opt_abro_buf->type = OPT_ABRO_TYPE; opt_abro_buf->length = OPT_ABRO_LEN; opt_abro_buf->version = HTONS(msg_abr->version); opt_abro_buf->reserved = 0; memcpy(&(opt_abro_buf->addr), &(msg_abr->abr_addr), sizeof(ipv6_addr_t)); } } if (sixco == OPT_6CO) { /* set 6lowpan context option */ int contexts_len = 0; mutex_lock(&lowpan_context_mutex); if (msg_abr == NULL) { contexts = lowpan_context_get(); contexts_len = lowpan_context_len(); } else { lowpan_context_t c_tmp[NDP_6LOWPAN_CONTEXT_MAX]; contexts_len = 0; for (int i = 0; i < NDP_6LOWPAN_CONTEXT_MAX; i++) { lowpan_context_t *ctx = abr_get_context(msg_abr, i); if (ctx != NULL) { memcpy(&(c_tmp[contexts_len++]), ctx, sizeof(lowpan_context_t)); } } contexts = (lowpan_context_t *)calloc(contexts_len, sizeof(lowpan_context_t)); memcpy(contexts, c_tmp, contexts_len); } for (int i = 0; i < contexts_len; i++) { opt_6co_hdr_buf = get_opt_6co_hdr_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); opt_6co_hdr_buf->type = OPT_6CO_TYPE; if (contexts[i].length > 64) { opt_6co_hdr_buf->length = OPT_6CO_MAX_LEN; } else { opt_6co_hdr_buf->length = OPT_6CO_MIN_LEN; } opt_6co_hdr_buf->c_length = contexts[i].length; opt_6co_hdr_buf->c_flags = set_opt_6co_flags(contexts[i].comp, contexts[i].num); opt_6co_hdr_buf->reserved = 0; opt_6co_hdr_buf->val_ltime = HTONS(contexts[i].lifetime); icmpv6_opt_hdr_len += OPT_6CO_HDR_LEN; packet_length += OPT_6CO_HDR_LEN; /* attach prefixes */ opt_6co_prefix_buf = get_opt_6co_prefix_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); if (opt_6co_hdr_buf->c_length > 64) { memset((void *)opt_6co_prefix_buf, 0, 16); memcpy((void *)opt_6co_prefix_buf, (void *) &contexts[i].prefix.uint8[0], opt_6co_hdr_buf->c_length / 8); icmpv6_opt_hdr_len += 16; packet_length += 16; } else { memset((void *)opt_6co_prefix_buf, 0, 8); memcpy((void *)opt_6co_prefix_buf, (void *) &contexts[i].prefix.uint8[0], opt_6co_hdr_buf->c_length / 8); icmpv6_opt_hdr_len += 8; packet_length += 8; } } if (msg_abr != NULL && contexts != NULL) { free(contexts); } mutex_unlock(&lowpan_context_mutex); } if (pi == OPT_PI) { /* set prefix option */ for (int i = 0; i < OPT_PI_LIST_LEN; i++) { if (plist[i].inuse && plist[i].adv) { opt_pi_buf = get_opt_pi_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); memcpy(&(opt_pi_buf->addr.uint8[0]), &(plist[i].addr.uint8[0]), 16); opt_pi_buf->type = OPT_PI_TYPE; opt_pi_buf->length = OPT_PI_LEN; opt_pi_buf->prefix_length = plist[i].length; opt_pi_buf->l_a_reserved1 = plist[i].l_a_reserved1; opt_pi_buf->val_ltime = HTONL(plist[i].val_ltime); opt_pi_buf->pref_ltime = HTONL(plist[i].pref_ltime); opt_pi_buf->reserved2 = 0; packet_length += OPT_PI_HDR_LEN; icmpv6_opt_hdr_len += OPT_PI_HDR_LEN; } } } ipv6_buf->length = HTONS(packet_length - IPV6_HDR_LEN); /* calculate checksum */ icmp_buf->checksum = 0; icmp_buf->checksum = ~icmpv6_csum(IPV6_PROTO_NUM_ICMPV6); } void recv_rtr_adv(void) { int8_t trigger_ns = -1; int8_t abro_found = 0; int16_t abro_version = 0; /* later replaced, just to supress warnings */ uint16_t packet_length; ipv6_addr_t abro_addr; ipv6_buf = ipv6_get_buf(); packet_length = IPV6_HDR_LEN + ipv6_buf->length; icmpv6_opt_hdr_len = RTR_ADV_LEN; rtr_adv_buf = get_rtr_adv_buf(ipv6_ext_hdr_len); ipv6_addr_t newaddr; recvd_cids_len = 0; /* update interface reachable time and retrans timer */ if (rtr_adv_buf->reachable_time != 0) { iface.adv_reachable_time = HTONL(rtr_adv_buf->reachable_time); } if (rtr_adv_buf->retrans_timer != 0) { iface.adv_retrans_timer = HTONL(rtr_adv_buf->retrans_timer); } def_rtr_entry = ndp_default_router_list_search(&ipv6_buf->srcaddr); if (rtr_adv_buf->router_lifetime != 0) { if (def_rtr_entry != NULL) { set_remaining_time(&(def_rtr_entry->inval_time), HTONL(rtr_adv_buf->router_lifetime)); } else { def_rtr_lst_add(&(ipv6_buf->srcaddr), HTONL(rtr_adv_buf->router_lifetime)); trigger_ns = 1; } } else { /* remove router from default router list */ if (def_rtr_entry != NULL) { def_rtr_lst_rem(def_rtr_entry); } } mutex_lock(&lowpan_context_mutex); /* read options */ while (packet_length > IPV6_HDR_LEN + ICMPV6_HDR_LEN + icmpv6_opt_hdr_len) { opt_buf = get_opt_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); switch (opt_buf->type) { case (NDP_OPT_SLLAO_TYPE): { break; } case (OPT_MTU_TYPE): { break; } /* rfc 4862 section 5.5.3 */ case (OPT_PI_TYPE): { opt_pi_buf = get_opt_pi_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); /* crazy condition, read 5.5.3a-b-c for further information */ if (ipv6_addr_is_link_local(&opt_pi_buf->addr) || (HTONL(opt_pi_buf->pref_ltime) > HTONL(opt_pi_buf->val_ltime))) { break; } else { /* check if on-link flag is set */ if (opt_pi_buf->l_a_reserved1 & ICMPV6_NDP_OPT_PI_FLAG_ON_LINK) { /* TODO: do on-link pi handling */ } if (opt_pi_buf->l_a_reserved1 & ICMPV6_NDP_OPT_PI_FLAG_AUTONOM) { addr_list_ptr = ipv6_iface_addr_prefix_eq(&opt_pi_buf->addr); if (addr_list_ptr == NULL) { /* 5.5.3d */ if (opt_pi_buf->val_ltime != 0) { /* iid will also be added here */ ipv6_addr_set_by_eui64(&newaddr, &opt_pi_buf->addr); /* add into address list * TODO: duplicate address detection is not * implementet yet, so all new addresse will * be added with state PREFFERED */ ipv6_iface_add_addr(&newaddr, IPV6_ADDR_TYPE_UNICAST, NDP_ADDR_STATE_PREFERRED, opt_pi_buf->val_ltime, opt_pi_buf->pref_ltime); DEBUG("INFO: added address to interface\n"); trigger_ns = 1; } } else { /* 5.5.3e */ set_remaining_time(&(addr_list_ptr->pref_ltime), opt_pi_buf->pref_ltime); /* 7200 = 2hours in seconds */ if (HTONL(opt_pi_buf->val_ltime) > 7200 || HTONL(opt_pi_buf->val_ltime) > get_remaining_time(&(addr_list_ptr->val_ltime))) { set_remaining_time(&(addr_list_ptr->val_ltime), HTONL(opt_pi_buf->val_ltime)); } else { /* reset valid lifetime to 2 hours */ set_remaining_time(&(addr_list_ptr->val_ltime), 7200); } } } } /* TODO: save found prefixes */ break; } case (OPT_6CO_TYPE): { uint8_t comp; uint8_t num; opt_6co_hdr_buf = get_opt_6co_hdr_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); get_opt_6co_flags(&comp, &num, opt_6co_hdr_buf->c_flags); ipv6_addr_t prefix; memset(&prefix, 0, 16); opt_6co_prefix_buf = get_opt_6co_prefix_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len + OPT_6CO_HDR_LEN); memcpy(&prefix, opt_6co_prefix_buf, opt_6co_hdr_buf->c_length); lowpan_context_update( num, &prefix, opt_6co_hdr_buf->c_length, comp, HTONS(opt_6co_hdr_buf->val_ltime) ); recvd_cids[recvd_cids_len] = num; recvd_cids_len = (recvd_cids_len + 1) % NDP_6LOWPAN_CONTEXT_MAX; break; } case (OPT_ABRO_TYPE): { opt_abro_buf = get_opt_abro_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); abro_found = 1; abro_version = HTONS(opt_abro_buf->version); memcpy(&(abro_addr), &(opt_abro_buf->addr), sizeof(ipv6_addr_t)); break; } default: break; } /* multiplied with 8 because options length is in units of 8 bytes */ icmpv6_opt_hdr_len += (opt_buf->length * 8); } if (abro_found) { int i; for (i = 0; i < recvd_cids_len; i++) { abr_add_context(abro_version, &abro_addr, recvd_cids[i]); } } mutex_unlock(&lowpan_context_mutex); if (trigger_ns >= 0) { /* send ns - draft-ietf-6lowpan-nd-15#section-5.5.1 * * section-10.2.4 * "Next the 6LN registers that address with one or more of its * default routers by sending a unicast NS message with an ARO * containing its tentative global IPv6 address to register * * if new address was configured, set src to newaddr(gp16) */ icmpv6_send_neighbor_sol(&newaddr, &(ipv6_buf->srcaddr), &(ipv6_buf->srcaddr), OPT_SLLAO, OPT_ARO); #ifdef DEBUG_ENABLED char addr_str[IPV6_MAX_ADDR_STR_LEN]; printf("INFO: send neighbor solicitation to: %s\n", ipv6_addr_to_str(addr_str, &(ipv6_buf->destaddr))); #endif } } void icmpv6_send_neighbor_sol(ipv6_addr_t *src, ipv6_addr_t *dest, ipv6_addr_t *targ, uint8_t sllao, uint8_t aro) { uint16_t packet_length; int if_id = 0; // TODO: get this somehow ipv6_buf = ipv6_get_buf(); ipv6_buf->version_trafficclass = IPV6_VER; ipv6_buf->trafficclass_flowlabel = 0; ipv6_buf->flowlabel = 0; ipv6_buf->nextheader = IPV6_PROTO_NUM_ICMPV6; ipv6_buf->hoplimit = ND_HOPLIMIT; if (dest == NULL) { ipv6_addr_set_solicited_node_addr(&(ipv6_buf->destaddr), targ); } else { memcpy(&(ipv6_buf->destaddr.uint8[0]), &(dest->uint8[0]), 16); } ipv6_ext_hdr_len = 0; icmp_buf = get_icmpv6_buf(ipv6_ext_hdr_len); icmp_buf->type = ICMPV6_TYPE_NEIGHBOR_SOL; icmp_buf->code = 0; nbr_sol_buf = get_nbr_sol_buf(ipv6_ext_hdr_len); nbr_sol_buf->reserved = 0; memcpy(&(nbr_sol_buf->target_addr), targ, 16); icmpv6_opt_hdr_len = NBR_SOL_LEN; packet_length = IPV6_HDR_LEN + ICMPV6_HDR_LEN + NBR_SOL_LEN; if (ipv6_iface_addr_match(targ) == NULL) { if (src == NULL) { ipv6_iface_get_best_src_addr(&(ipv6_buf->srcaddr), &(ipv6_buf->destaddr)); } else { memcpy(&(ipv6_buf->srcaddr), src, 16); } if (sllao == OPT_SLLAO) { /* set sllao option */ opt_stllao_buf = get_opt_stllao_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); icmpv6_ndp_set_sllao(opt_stllao_buf, NDP_OPT_SLLAO_TYPE, 1); icmpv6_opt_hdr_len += OPT_STLLAO_MIN_LEN; packet_length += OPT_STLLAO_MIN_LEN; } } if (aro == OPT_ARO) { /* set aro option */ opt_aro_buf = get_opt_aro_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); opt_aro_buf->type = OPT_ARO_TYPE; opt_aro_buf->length = OPT_ARO_LEN; opt_aro_buf->status = 0; opt_aro_buf->reserved1 = 0; opt_aro_buf->reserved2 = 0; if (net_if_get_src_address_mode(if_id) == NET_IF_TRANS_ADDR_M_SHORT) { net_if_get_eui64((net_if_eui64_t *) &opt_aro_buf->eui64, if_id, 1); } else if (net_if_get_src_address_mode(if_id) == NET_IF_TRANS_ADDR_M_LONG) { net_if_get_eui64((net_if_eui64_t *) &opt_aro_buf->eui64, if_id, 0); } icmpv6_opt_hdr_len += OPT_ARO_HDR_LEN; packet_length += OPT_ARO_HDR_LEN; } ipv6_buf->length = HTONS(packet_length - IPV6_HDR_LEN); icmp_buf->checksum = 0; icmp_buf->checksum = ~icmpv6_csum(IPV6_PROTO_NUM_ICMPV6); } void recv_nbr_sol(void) { ipv6_buf = ipv6_get_buf(); llao = NULL; icmpv6_opt_hdr_len = NBR_SOL_LEN; uint8_t send_na = 0; uint8_t sllao_set = 0; uint8_t aro_state = NDP_OPT_ARO_STATE_SUCCESS; uint16_t packet_length = IPV6_HDR_LEN + ipv6_buf->length; /* check whick options are set, we need that because an aro * option condition is that a sllao option is set. thus that we don't * know which option comes first we need to this here */ while (packet_length > IPV6_HDR_LEN + ICMPV6_HDR_LEN + icmpv6_opt_hdr_len) { opt_buf = get_opt_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); if (opt_buf->type == NDP_OPT_SLLAO_TYPE) { sllao_set = 1; } icmpv6_opt_hdr_len += (opt_buf->length * 8); } icmpv6_opt_hdr_len = NBR_SOL_LEN; while (packet_length > IPV6_HDR_LEN + ICMPV6_HDR_LEN + icmpv6_opt_hdr_len) { opt_buf = get_opt_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); switch (opt_buf->type) { case (NDP_OPT_SLLAO_TYPE): { opt_stllao_buf = get_opt_stllao_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); llao = (uint8_t *)opt_stllao_buf; if (llao != NULL && !(ipv6_addr_is_unspecified(&ipv6_buf->srcaddr))) { nbr_entry = ndp_neighbor_cache_search(&(ipv6_buf->srcaddr)); if (nbr_entry != NULL) { switch (opt_stllao_buf->length) { case (1): { if (memcmp(&llao[2], &(nbr_entry->saddr), 2) == 0) { nbr_entry->isrouter = 0; } else { memcpy(&nbr_entry->saddr, &llao[2], 2); nbr_entry->state = NDP_NCE_STATUS_STALE; nbr_entry->isrouter = 0; } break; } case (2): { if (memcmp(&llao[2], &(nbr_entry->laddr), 8) == 0) { nbr_entry->isrouter = 0; } else { memcpy(&nbr_entry->laddr, &llao[2], 8); nbr_entry->state = NDP_NCE_STATUS_STALE; nbr_entry->isrouter = 0; } break; } default: break; } } else { switch (opt_stllao_buf->length) { case (1): { nbr_cache_add(&ipv6_buf->srcaddr, NULL , 0, NDP_NCE_STATUS_STALE, NDP_NCE_TYPE_TENTATIVE, NBR_CACHE_LTIME_TEN, (ieee_802154_short_t *)&llao[2]); break; } case (2): { nbr_cache_add(&ipv6_buf->srcaddr, (ieee_802154_long_t *)&llao[2], 0, NDP_NCE_STATUS_STALE, NDP_NCE_TYPE_TENTATIVE, NBR_CACHE_LTIME_TEN, NULL); break; } default: break; } } } break; } case (OPT_ARO_TYPE): { /* check if sllao option is set, and if address src address * isn't unspecified - draft-ietf-6lowpan-nd-15#section-6.5 */ if (!(ipv6_addr_is_unspecified(&ipv6_buf->srcaddr)) && sllao_set == 1) { opt_aro_buf = get_opt_aro_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); if ((opt_aro_buf->length == 2) && (opt_aro_buf->status == 0)) { /* check neighbor cache for duplicates */ nbr_entry = ndp_neighbor_cache_search(&(ipv6_buf->srcaddr)); if (nbr_entry == NULL) { /* create neighbor cache */ aro_state = nbr_cache_add(&ipv6_buf->srcaddr, &(opt_aro_buf->eui64), 0, NDP_NCE_STATUS_STALE, NDP_NCE_TYPE_TENTATIVE, opt_aro_buf->reg_ltime, NULL); } else { if (memcmp(&(nbr_entry->addr.uint16[4]), &(opt_aro_buf->eui64.uint16[0]), 8) == 0) { /* update neighbor cache entry */ if (opt_aro_buf->reg_ltime == 0) { /* delete neighbor cache entry */ nbr_cache_rem(&nbr_entry->addr); } else { set_remaining_time(&(nbr_entry->ltime), (uint32_t)opt_aro_buf->reg_ltime); nbr_entry->state = NDP_NCE_STATUS_STALE; nbr_entry->isrouter = 0; memcpy(&(nbr_entry->addr.uint8[0]), &(ipv6_buf->srcaddr.uint8[0]), 16); } aro_state = NDP_OPT_ARO_STATE_SUCCESS; } else { /* duplicate found */ aro_state = NDP_OPT_ARO_STATE_DUP_ADDR; } } } } break; } default: break; } icmpv6_opt_hdr_len += (opt_buf->length * 8); } addr_list_t *alist_targ, *alist_dest; nbr_sol_buf = get_nbr_sol_buf(ipv6_ext_hdr_len); alist_targ = ipv6_iface_addr_match(&(nbr_sol_buf->target_addr)); if (alist_targ != NULL) { alist_dest = ipv6_iface_addr_match(&(ipv6_buf->destaddr)); if ((memcmp(&(alist_targ->addr), &(alist_dest->addr), 16) == 0) || ipv6_addr_is_solicited_node(&ipv6_buf->destaddr)) { memcpy(&(ipv6_buf->destaddr.uint8[0]), &(ipv6_buf->srcaddr.uint8[0]), 16); memcpy(&(ipv6_buf->srcaddr.uint8[0]), &(nbr_sol_buf->target_addr.uint8[0]), 16); send_na = 1; } } if (send_na) { /* solicited na */ uint8_t flags = (ICMPV6_NEIGHBOR_ADV_FLAG_OVERRIDE | ICMPV6_NEIGHBOR_ADV_FLAG_SOLICITED); icmpv6_send_neighbor_adv(&(ipv6_buf->srcaddr), &(ipv6_buf->destaddr), &(alist_targ->addr), flags, 0, OPT_ARO); #ifdef DEBUG_ENABLED char addr_str[IPV6_MAX_ADDR_STR_LEN]; printf("INFO: send neighbor advertisment to: %s\n", ipv6_addr_to_str(addr_str, &ipv6_buf->destaddr)); #endif } } void icmpv6_send_neighbor_adv(ipv6_addr_t *src, ipv6_addr_t *dst, ipv6_addr_t *tgt, uint8_t rso, uint8_t sllao, uint8_t aro) { int if_id = 0; // TODO: get this somehow uint16_t packet_length; ipv6_buf = ipv6_get_buf(); ipv6_buf->version_trafficclass = IPV6_VER; ipv6_buf->trafficclass_flowlabel = 0; ipv6_buf->flowlabel = 0; ipv6_buf->nextheader = IPV6_PROTO_NUM_ICMPV6; ipv6_buf->hoplimit = ND_HOPLIMIT; ipv6_ext_hdr_len = 0; icmp_buf = get_icmpv6_buf(ipv6_ext_hdr_len); icmp_buf->type = ICMPV6_TYPE_NEIGHBOR_ADV; icmp_buf->code = 0; memcpy(&(ipv6_buf->destaddr.uint8[0]), &(dst->uint8[0]), 16); memcpy(&(ipv6_buf->srcaddr.uint8[0]), &(src->uint8[0]), 16); nbr_adv_buf = get_nbr_adv_buf(ipv6_ext_hdr_len); nbr_adv_buf->rso = rso; memset(&(nbr_adv_buf->reserved[0]), 0, 3); memcpy(&(nbr_adv_buf->target_addr.uint8[0]), &(tgt->uint8[0]), 16); packet_length = IPV6_HDR_LEN + ICMPV6_HDR_LEN + NBR_ADV_LEN; if (sllao == OPT_SLLAO) { /* set sllao option */ opt_stllao_buf = get_opt_stllao_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); icmpv6_ndp_set_sllao(opt_stllao_buf, NDP_OPT_SLLAO_TYPE, 1); icmpv6_opt_hdr_len += OPT_STLLAO_MIN_LEN; packet_length += OPT_STLLAO_MIN_LEN; } if (aro == OPT_ARO) { /* set aro option */ opt_aro_buf = get_opt_aro_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); opt_aro_buf->type = OPT_ARO_TYPE; opt_aro_buf->length = OPT_ARO_LEN; opt_aro_buf->status = 0; /* TODO */ opt_aro_buf->reserved1 = 0; opt_aro_buf->reserved2 = 0; if (net_if_get_src_address_mode(if_id) == NET_IF_TRANS_ADDR_M_SHORT) { net_if_get_eui64((net_if_eui64_t *) &opt_aro_buf->eui64, if_id, 1); } else if (net_if_get_src_address_mode(if_id) == NET_IF_TRANS_ADDR_M_LONG) { net_if_get_eui64((net_if_eui64_t *) &opt_aro_buf->eui64, if_id, 0); } icmpv6_opt_hdr_len += OPT_ARO_HDR_LEN; packet_length += OPT_ARO_HDR_LEN; } ipv6_buf->length = HTONS(packet_length - IPV6_HDR_LEN); icmp_buf->checksum = 0; icmp_buf->checksum = ~icmpv6_csum(IPV6_PROTO_NUM_ICMPV6); } void recv_nbr_adv(void) { ipv6_buf = ipv6_get_buf(); uint16_t packet_length = IPV6_HDR_LEN + ipv6_buf->length; icmpv6_opt_hdr_len = NBR_ADV_LEN; llao = NULL; nbr_entry = NULL; int8_t new_ll = -1; nbr_adv_buf = get_nbr_adv_buf(ipv6_ext_hdr_len); /* check if options are present */ while (packet_length > IPV6_HDR_LEN + ICMPV6_HDR_LEN + icmpv6_opt_hdr_len) { opt_buf = get_opt_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); switch (opt_buf->type) { case (NDP_OPT_TLLAO_TYPE): { llao = (uint8_t *)get_opt_stllao_buf(ipv6_ext_hdr_len, icmpv6_opt_hdr_len); break; } case (OPT_ARO_TYPE): { break; } } icmpv6_opt_hdr_len += (opt_buf->length * 8); } addr_list_t *addr; addr = ipv6_iface_addr_match(&nbr_adv_buf->target_addr); if (addr == NULL) { nbr_entry = ndp_neighbor_cache_search(&nbr_adv_buf->target_addr); if (nbr_entry != NULL) { if (llao != 0) { /* TODO: untersheiden zwischen short und long stllao option */ new_ll = memcmp(&llao[2], &(nbr_entry->laddr), 8); } if (nbr_entry->state == NDP_NCE_STATUS_INCOMPLETE) { if (llao == NULL) { return; } /* TODO: untersheiden zwischen short und long stllao option */ memcpy(&nbr_entry->laddr, &llao[2], 8); if (nbr_adv_buf->rso & ICMPV6_NEIGHBOR_ADV_FLAG_SOLICITED) { nbr_entry->state = NDP_NCE_STATUS_REACHABLE; /* TODO: set rechability */ } else { nbr_entry->state = NDP_NCE_STATUS_STALE; } nbr_entry->isrouter = nbr_adv_buf->rso & ICMPV6_NEIGHBOR_ADV_FLAG_ROUTER; } else { if (new_ll && !(nbr_adv_buf->rso & ICMPV6_NEIGHBOR_ADV_FLAG_OVERRIDE)) { if (nbr_entry->state == NDP_NCE_STATUS_REACHABLE) { nbr_entry->state = NDP_NCE_STATUS_STALE; } return; } else { if ((nbr_adv_buf->rso & ICMPV6_NEIGHBOR_ADV_FLAG_OVERRIDE) || (!(nbr_adv_buf->rso & ICMPV6_NEIGHBOR_ADV_FLAG_OVERRIDE) && llao != 0 && !new_ll)) { if (llao != 0) { memcpy(&nbr_entry->laddr, &llao[2], 8); } if (nbr_adv_buf->rso & ICMPV6_NEIGHBOR_ADV_FLAG_SOLICITED) { nbr_entry->state = NDP_NCE_STATUS_REACHABLE; /* TODO: set rechablility */ } else { if (llao != 0 && new_ll) { nbr_entry->state = NDP_NCE_STATUS_STALE; } } } } } } } } /* link-layer address option - RFC4861 section 4.6.1/ RFC4944 8. */ void icmpv6_ndp_set_sllao(icmpv6_ndp_opt_stllao_t *sllao, uint8_t type, uint8_t length) { sllao->type = type; sllao->length = length; uint8_t *llao = (uint8_t *)sllao; /* get link layer address */ switch (length) { case (1): { memcpy(&llao[2], &(iface.saddr), 2); memset(&llao[4], 0, 4); break; } case (2): { memcpy(&llao[2], &(iface.laddr), 8); memset(&llao[10], 0, 6); break; } default: { printf("ERROR: llao not set\n"); break; } } } uint16_t icmpv6_csum(uint8_t proto) { ipv6_buf = ipv6_get_buf(); uint16_t sum; uint16_t len = NTOHS(ipv6_buf->length); sum = len + proto; sum = csum(sum, (uint8_t *)&ipv6_buf->srcaddr, 2 * sizeof(ipv6_addr_t)); sum = csum(sum, (uint8_t *)get_icmpv6_buf(0), len); return (sum == 0) ? 0xffff : HTONS(sum); } void icmpv6_send_parameter_prob(ipv6_addr_t *src, ipv6_addr_t *dest, uint8_t code, uint32_t pointer, uint8_t *packet, uint8_t packet_len) { uint16_t packet_length = IPV6_HDR_LEN + ICMPV6_HDR_LEN + PARA_PROB_LEN; icmpv6_parameter_prob_hdr_t *para_prob_buf; memcpy(&(ipv6_buf[packet_length]), packet, min(IPV6_MTU - packet_length, packet_len)); ipv6_buf = ipv6_get_buf(); ipv6_buf->version_trafficclass = IPV6_VER; ipv6_buf->trafficclass_flowlabel = 0; ipv6_buf->flowlabel = 0; ipv6_buf->nextheader = IPV6_PROTO_NUM_ICMPV6; ipv6_buf->hoplimit = ND_HOPLIMIT; ipv6_ext_hdr_len = 0; icmp_buf = get_icmpv6_buf(ipv6_ext_hdr_len); icmp_buf->type = ICMPV6_TYPE_PARAMETER_PROB; icmp_buf->code = code; memcpy(&(ipv6_buf->destaddr.uint8[0]), &(dest->uint8[0]), 16); memcpy(&(ipv6_buf->srcaddr.uint8[0]), &(src->uint8[0]), 16); para_prob_buf = get_para_prob_buf(ipv6_ext_hdr_len); para_prob_buf->pointer = pointer; packet_length += min(IPV6_MTU - packet_length, packet_len); ipv6_buf->length = HTONS(packet_length - IPV6_HDR_LEN); icmp_buf->checksum = 0; icmp_buf->checksum = ~icmpv6_csum(IPV6_PROTO_NUM_ICMPV6); } //------------------------------------------------------------------------------ /* neighbor cache functions */ ndp_neighbor_cache_t *ndp_neighbor_cache_search(ipv6_addr_t *ipaddr) { int i; for (i = 0; i < NBR_CACHE_SIZE; i++) { if (memcmp(&(nbr_cache[i].addr.uint8[0]), &(ipaddr->uint8[0]), 16) == 0) { return &nbr_cache[i]; } } return NULL; } uint8_t nbr_cache_add(ipv6_addr_t *ipaddr, ieee_802154_long_t *laddr, uint8_t isrouter, ndp_nce_state_t state, ndp_nce_type_t type, uint16_t ltime, ieee_802154_short_t *saddr) { (void) ltime; (void) saddr; if (nbr_count == NBR_CACHE_SIZE) { printf("ERROR: neighbor cache full\n"); return NDP_OPT_ARO_STATE_NBR_CACHE_FULL; } memcpy(&(nbr_cache[nbr_count].addr), ipaddr, 16); memcpy(&(nbr_cache[nbr_count].laddr), laddr, 8); nbr_cache[nbr_count].isrouter = isrouter; nbr_cache[nbr_count].state = state; nbr_cache[nbr_count].type = type; //vtimer_set_wakeup(&(nbr_cache[nbr_count].ltime), t, /* nd_nbr_cache_rem_pid); */ nbr_count++; return NDP_OPT_ARO_STATE_SUCCESS; } void nbr_cache_auto_rem(void) { int i; for (i = 0; i < NBR_CACHE_SIZE; i++) { if (get_remaining_time(&(nbr_cache[i].ltime)) == 0 && nbr_cache[i].type == NDP_NCE_TYPE_TENTATIVE) { memmove(&(nbr_cache[i]), &(nbr_cache[nbr_count]), sizeof(ndp_neighbor_cache_t)); memset(&(nbr_cache[nbr_count]), 0, sizeof(ndp_neighbor_cache_t)); nbr_count--; } } } void nbr_cache_rem(ipv6_addr_t *addr) { int i; for (i = 0; i < NBR_CACHE_SIZE; i++) { if (memcmp(&(nbr_cache[i].addr.uint8[0]), &(addr->uint8[0]), 16) == 0) { memmove(&(nbr_cache[i]), &(nbr_cache[nbr_count]), sizeof(ndp_neighbor_cache_t)); memset(&(nbr_cache[nbr_count]), 0, sizeof(ndp_neighbor_cache_t)); nbr_count--; } } } //------------------------------------------------------------------------------ /* authoritive border router list functions */ /** * @brief Finds the most current (by version number) authoritive border * router information. * @pre assumes that abro versions are centrally managed * @return The most current authoritive border router information, NULL * if no such information is given. */ ndp_a6br_cache_t *ndp_a6br_cache_get_most_current(void) { ndp_a6br_cache_t *abr = NULL; int i; int version = abr_cache[0].version; for (i = 0; i < abr_count; i++) { if (serial_comp16(version, abr_cache[i].version) == GREATER) { abr = &(abr_cache[i]); version = abr_cache[i].version; } } return abr; } ndp_a6br_cache_t *ndp_a6br_cache_get_oldest(void) { ndp_a6br_cache_t *abr = NULL; int i; int version = abr_cache[0].version; for (i = 0; i < abr_count; i++) { if (serial_comp16(version, abr_cache[i].version) == LESS) { abr = &(abr_cache[i]); version = abr_cache[i].version; } } return abr; } ndp_a6br_cache_t *abr_get_version(uint16_t version, ipv6_addr_t *abr_addr) { int i = 0; for (i = 0; i < ABR_CACHE_SIZE; i++) { if (abr_cache[i].version == version && memcmp(&(abr_cache[i].abr_addr.uint8[0]), &(abr_addr->uint8[0]), 16 ) == 0) { return &(abr_cache[i]); } } return NULL; } lowpan_context_t *abr_get_context(ndp_a6br_cache_t *abr, uint8_t cid) { if (abr->cids[cid] != cid) { return NULL; } return lowpan_context_num_lookup(abr->cids[cid]); } ndp_a6br_cache_t *abr_add_context(uint16_t version, ipv6_addr_t *abr_addr, uint8_t cid) { ndp_a6br_cache_t *abr = abr_get_version(version, abr_addr); if (abr == NULL) { if (abr_count == ABR_CACHE_SIZE) { abr = ndp_a6br_cache_get_oldest(); } else { abr = &(abr_cache[abr_count++]); } abr->version = version; memcpy(&(abr->abr_addr), abr_addr, sizeof(ipv6_addr_t)); memset(abr->cids, 0xFF, NDP_6LOWPAN_CONTEXT_MAX); } abr->cids[cid] = cid; return abr; } void abr_remove_context(uint8_t cid) { int i; for (i = 0; i < abr_count; i++) { abr_cache[i].cids[cid] = 0xFF; } } //------------------------------------------------------------------------------ /* default router list functions */ ndp_default_router_list_t *ndp_default_router_list_search(ipv6_addr_t *ipaddr) { int i; for (i = 0; i < DEF_RTR_LST_SIZE; i++) { if (memcmp(&def_rtr_lst[i].addr.uint8[0], &(ipaddr->uint8[0]), 16) == 0) { return &def_rtr_lst[i]; } } return NULL; } void def_rtr_lst_add(ipv6_addr_t *ipaddr, uint32_t rtr_ltime) { if (def_rtr_count == DEF_RTR_LST_SIZE) { DEBUG("ERROR: default router list full\n"); } else { memcpy(&(def_rtr_lst[def_rtr_count].addr), ipaddr, 16); timex_t rltime = {rtr_ltime, 0}; timex_t now; vtimer_now(&now); def_rtr_lst[def_rtr_count].inval_time = timex_add(now, rltime); def_rtr_count++; } } void def_rtr_lst_rem(ndp_default_router_list_t *entry) { int i; for (i = 0; i < DEF_RTR_LST_SIZE; i++) { if (&def_rtr_lst[i] == entry) { /* search the to deleted item, then memmove the last item to its * position, and decrement array count */ memmove(entry, &def_rtr_lst[def_rtr_count], sizeof(ndp_default_router_list_t)); memset(&def_rtr_lst[def_rtr_count], 0, sizeof(ndp_default_router_list_t)); def_rtr_count--; } } } //------------------------------------------------------------------------------ /* prefix list functions */ int plist_add(ipv6_addr_t *addr, uint8_t size, uint32_t val_ltime, uint32_t pref_ltime, uint8_t adv_opt, uint8_t l_a_reserved1) { if (prefix_count == OPT_PI_LIST_LEN) { return SIXLOWERROR_ARRAYFULL; } else { plist[prefix_count].inuse = 1; plist[prefix_count].length = size; plist[prefix_count].adv = adv_opt; plist[prefix_count].l_a_reserved1 = l_a_reserved1; plist[prefix_count].val_ltime = HTONL(val_ltime); plist[prefix_count].pref_ltime = HTONL(pref_ltime); memcpy(&(plist[prefix_count].addr.uint8[0]), &(addr->uint8[0]), 16); return SIXLOWERROR_SUCCESS; } }