mbed-os/features/nanostack/sal-stack-nanostack/source/ipv6_stack/protocol_ipv6.c

1010 lines
35 KiB
C

/*
* Copyright (c) 2012-2018, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nsconfig.h"
#include "string.h"
#include "ns_types.h"
#include "ns_list.h"
#include "ns_trace.h"
#include "nsdynmemLIB.h"
#include "Core/include/socket.h"
#include "NWK_INTERFACE/Include/protocol.h"
#include "Common_Protocols/ipv6.h"
#include "Common_Protocols/icmpv6.h"
#include "Common_Protocols/icmpv6_prefix.h"
#include "Common_Protocols/icmpv6_radv.h"
#include "Common_Protocols/ipv6_resolution.h"
#include "Common_Protocols/udp.h"
#include "6LoWPAN/ND/nd_router_object.h" // for gp_address_ functions - better place?
#include "ipv6_stack/ipv6_routing_table.h"
#include "ipv6_stack/protocol_ipv6.h"
#include "Common_Protocols/tcp.h"
#include "Service_Libs/whiteboard/whiteboard.h"
#include "Service_Libs/nd_proxy/nd_proxy.h"
#include "platform/arm_hal_interrupt.h"
#include "common_functions.h"
#include "ethernet_mac_api.h"
#ifdef HAVE_ETHERNET
#define TRACE_GROUP "pIP6"
typedef struct ipv6_interface_prefix_on_link_t {
uint8_t prefix[16]; /*!< destination address */
uint8_t prefix_len;
uint32_t prefix_valid_ttl;
ns_list_link_t link;
} ipv6_interface_prefix_on_link_t;
typedef struct ipv6_interface_route_on_link_t {
uint8_t prefix[16]; /*!< destination address */
uint8_t prefix_len;
uint8_t routePrefer;
uint32_t prefix_valid_ttl;
ns_list_link_t link;
} ipv6_interface_route_on_link_t;
#define WB_UPDATE_PERIOD_SECONDS 23
#define ROUTER_SOL_MAX_COUNTER 4
#define TRACE_GROUP_PROTOCOL_IPv6 "ip6s"
static void ipv6_stack(buffer_t *b);
static uint8_t ipv6_nd_rs(protocol_interface_info_entry_t *cur);
static void ipv6_stack_prefix_on_link_update(protocol_interface_info_entry_t *cur, uint8_t *address);
static void ipv6_nd_bootstrap(protocol_interface_info_entry_t *cur);
static void ipv6_interface_address_cb(protocol_interface_info_entry_t *interface, if_address_entry_t *addr, if_address_callback_t reason);
int ipv6_interface_route_validate(int8_t interface_id , uint8_t *address);
/* These are the advertised on-link prefixes */
static NS_LIST_DEFINE(prefix_on_link, ipv6_interface_prefix_on_link_t, link);
/* These are the advertised Route prefixes */
static NS_LIST_DEFINE(route_on_link, ipv6_interface_route_on_link_t, link);
static prefix_list_t ipv6_prefixs = NS_LIST_INIT(ipv6_prefixs);
bool tunnel_in_use = false;
static void ethernet_data_conf_cb( const eth_mac_api_t* api, const eth_data_conf_t *data )
{
(void)data;
(void)api;
//Currently not handled
}
static void ethernet_data_ind_cb(const eth_mac_api_t* api, const eth_data_ind_t *data)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(api->parent_id);
if (!cur || !cur->if_stack_buffer_handler || !data) {
return;
}
/* Fast exit for Ethernet - we will tend to get quite a lot of broadcast
* IPv4 traffic, so filter frame type before allocating a buffer.
*/
if (data->etehernet_type != ETHERTYPE_IPV6 || data->msduLength == 0) {
return;
}
buffer_t *buffer = buffer_get_minimal(data->msduLength);
if (!buffer) {
tr_error("RX: out of memory");
return;
}
buffer->buf_ptr = buffer->buf_end = 0;
// Upward direction functions assume no headroom and are trusting that removed bytes are still valid.
// see mac.c:655
/* Set default flags */
buffer->options.dbm = data->dbm;
buffer->options.lqi = data->link_quality;
buffer->interface = cur;
buffer->info = (buffer_info_t)(B_TO_IPV6_TXRX | B_FROM_MAC | B_DIR_UP);
if (cur->eth_mac_api->address_resolution_needed) {
buffer->dst_sa.addr_type = ADDR_EUI_48;
memcpy(buffer->dst_sa.address, data->dstAddress, 6);
buffer->src_sa.addr_type = ADDR_EUI_48;
memcpy(buffer->src_sa.address, data->srcAddress, 6);
}
/* Copy packet content */
buffer_data_add(buffer, data->msdu, data->msduLength);
protocol_push(buffer);
}
void ipv6_interface_phy_sap_register(protocol_interface_info_entry_t *cur)
{
if( cur->eth_mac_api ){
cur->eth_mac_api->mac_initialize(cur->eth_mac_api, &ethernet_data_conf_cb, &ethernet_data_ind_cb, cur->id);
}
}
int ipv6_generate_static_gp_setup(protocol_interface_info_entry_t *cur, bool temporaryAddress)
{
int ret_val = -1;
if_address_entry_t *address_entry;
uint8_t static_address[16];
ns_list_foreach(prefix_entry_t, prefix_ptr, &ipv6_prefixs) {
if (prefix_ptr->options & PIO_A) {
memcpy(static_address, prefix_ptr->prefix, 8);
memcpy(static_address + 8, cur->iid_eui64, 8);
address_entry = addr_add(cur, static_address, 64, ADDR_SOURCE_STATIC, 0xffffffff, 0xffffffff, false);
if (address_entry) {
address_entry->temporary = temporaryAddress;
address_entry->cb = ipv6_interface_address_cb;
#ifdef WHITEBOARD
whiteboard_interface_register(static_address, cur->id);
#endif
if (cur->ipv6_configure->IPv6_ND_state != IPV6_GP_CONFIG) {
cur->ipv6_configure->IPv6_ND_state = IPV6_GP_CONFIG;
cur->global_address_available = true;
}
ret_val = 0;
}
}
}
return ret_val;
}
int ipv6_prefix_router_flag_activate(uint8_t *ipv6_address)
{
prefix_entry_t *entry = icmpv6_prefix_compare(&ipv6_prefixs, ipv6_address, 64);
if (entry) {
entry->options |= PIO_R;
memcpy(&entry->prefix[8], ipv6_address + 8, 8);
return 0;
}
return -1;
}
static buffer_t *ethernet_header_build_push(buffer_t *buf, uint16_t ethernet_type, int8_t *status)
{
protocol_interface_info_entry_t *cur = buf->interface;
*status = -1;
if (cur->nwk_id == IF_IPV6) {
eth_data_req_t req;
req.msduLength = buffer_data_length(buf);
req.msdu = buffer_data_pointer(buf);
req.srcAddress = buf->src_sa.address;
req.dstAddress = buf->dst_sa.address;
req.etehernet_type = ethernet_type;
if( cur->eth_mac_api ){
cur->eth_mac_api->data_req(cur->eth_mac_api, &req);
*status = 0;
}else{
*status = -1;
}
}
return buf;
}
/* Input: Final IP packet for transmission on link.
* Buffer destination = final destination
* Buffer source undefined.
* Route next hop address set.
* Output: Buffer destination+source = link-layer addresses
* Sent to MAC layer
*/
buffer_t *ethernet_down(buffer_t *buf)
{
protocol_interface_info_entry_t *cur = buf->interface;
if (!buf->route) {
tr_debug("ethernet_down route");
return buffer_free(buf);
}
/* Check if we have tunnel interface that does not need address resolution */
if (cur->eth_mac_api->address_resolution_needed) {
const uint8_t *ip_dst = buf->route->route_info.next_hop_addr;
/* First, check routing to get next hop */
if (addr_is_ipv6_multicast(ip_dst)) {
buf->dst_sa.addr_type = ADDR_EUI_48;
buf->dst_sa.address[0] = 0x33;
buf->dst_sa.address[1] = 0x33;
memcpy(&buf->dst_sa.address[2], ip_dst + 12, 4);
} else { /* unicast */
ipv6_neighbour_t *n = ipv6_interface_resolve_new(cur, buf);
if (!n) {
return NULL;
}
}
buf->src_sa.addr_type = ADDR_EUI_48;
memcpy(buf->src_sa.address, cur->mac, 6);
}
buf->info = (buffer_info_t)(B_FROM_IPV6_TXRX | B_TO_MAC | B_DIR_DOWN);
return buf;
}
/* Return length of option always, and write option if opt_out != NULL */
static uint8_t ethernet_llao_write(protocol_interface_info_entry_t *cur, uint8_t *opt_out, uint8_t opt_type, bool must, const uint8_t *ip_addr)
{
(void) must;
(void) ip_addr;
if (opt_out) {
opt_out[0] = opt_type;
opt_out[1] = 1;
memcpy(opt_out + 2, cur->mac, 6);
}
return 8;
}
/* Parse, and return actual size, or 0 if error */
static uint8_t ethernet_llao_parse(protocol_interface_info_entry_t *cur, const uint8_t *opt_in, sockaddr_t *ll_addr_out)
{
(void) cur;
if (opt_in[1] != 1) {
return 0;
}
ll_addr_out->addr_type = ADDR_EUI_48;
memcpy(ll_addr_out->address, opt_in + 2, 6);
return 6;
}
/**
* \fn ethernet_up
*
* \brief Parse IPv6 header received over the Backhaul link
*
*/
/* Input: MAC payload received from Ethernet or other link (frame type was IPv6)
* Buffer destination = MAC destination (if actually was Ethernet)
* Buffer source = MAC source
* Output: LL broadcast/multicast flags set based on addresses.
* Sent to IPv6 forwarding up.
*/
static buffer_t *ethernet_up(buffer_t *buf)
{
if (buf->dst_sa.addr_type == ADDR_EUI_48 && (buf->dst_sa.address[0] & 1)) {
if (memcmp(buf->dst_sa.address, (const uint8_t[]) { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, 6) == 0)
buf->options.ll_broadcast_rx = true;
else {
buf->options.ll_multicast_rx = true;
}
}
buf->ip_routed_up = true;
buf->info = (buffer_info_t)(B_DIR_UP | B_FROM_IPV6_TXRX | B_TO_IPV6_FWD);
return buf;
}
static void ipv6_nd_bootstrap(protocol_interface_info_entry_t *cur)
{
switch (cur->ipv6_configure->IPv6_ND_state) {
case IPV6_LL_CONFIG:
if (addr_interface_set_ll64(cur, ipv6_interface_address_cb) != 0) {
cur->ipv6_configure->ND_TIMER = 1;
}
break;
case IPV6_ROUTER_SOLICATION:
tr_debug("Waiting for ICMPv6 Router Advertisement");
if (ipv6_nd_rs(cur)) {
if (cur->ipv6_configure->routerSolicationRetryCounter != ROUTER_SOL_MAX_COUNTER) {
cur->ipv6_configure->routerSolicationRetryCounter++;
}
cur->ipv6_configure->ND_TIMER = (cur->ipv6_configure->routerSolicationRetryCounter * 25);
} else {
cur->ipv6_configure->ND_TIMER = 1;
}
break;
case IPV6_GP_GEN:
if (ipv6_generate_static_gp_setup(cur, false) != 0) {
cur->ipv6_configure->ND_TIMER = 1;
}
break;
case IPV6_GP_CONFIG:
break;
case IPV6_DHCPV6_SOLICATION:
// if(dhcpv6_non_temporal_address_solication(cur) == 0)
// {
// cur->ipv6_configure->ND_TIMER = 0;
// }
// else
// {
// cur->ipv6_configure->ND_TIMER = 1;
// }
break;
case IPV6_DHCPV6_ADDRESS_REQUEST:
break;
case IPV6_DHCPV6_ADDRESS_REQ_FAIL:
tr_warn("DHCP ADDRESS_REQ_FAIL");
/*XXX Do what here? */
break;
case IPV6_READY:
break;
}
}
void ipv6_prefix_on_link_list_add_by_interface_address_list(protocol_interface_info_entry_t *cur_Ipv6, protocol_interface_info_entry_t *curRegisteredInterface)
{
ns_list_foreach(if_address_entry_t, e, &curRegisteredInterface->ip_addresses) {
if (addr_ipv6_scope(e->address,curRegisteredInterface) == IPV6_SCOPE_GLOBAL) {
ipv6_stack_prefix_on_link_update(cur_Ipv6, e->address);
}
}
}
static void ipv6_stack_prefix_on_link_update(protocol_interface_info_entry_t *cur, uint8_t *address)
{
if (!cur->global_address_available) {
return;
}
if (addr_interface_gp_prefix_compare(cur, address) != 0) {
ipv6_interface_prefix_on_link_t *new_entry = 0;
ns_list_foreach(ipv6_interface_prefix_on_link_t, tmp_prefix, &prefix_on_link) {
if (memcmp(tmp_prefix->prefix, address, 8) == 0) {
return;
}
}
new_entry = ns_dyn_mem_alloc(sizeof(ipv6_interface_prefix_on_link_t));
if (new_entry) {
memset(new_entry->prefix, 0, 16);
memcpy(new_entry->prefix, address, 8);
new_entry->prefix_len = 64;
// Treat the prefix like a "route", so 3 * advert interval, not 30 days
new_entry->prefix_valid_ttl = (UINT32_C(3) * icmpv6_radv_max_rtr_adv_interval(cur)) / 10;
ns_list_add_to_end(&prefix_on_link, new_entry);
icmpv6_restart_router_advertisements(cur, ADDR_UNSPECIFIED);
}
}
}
void ipv6_stack_route_advert_update(uint8_t *address, uint8_t prefixLength, uint8_t routePrefer)
{
protocol_interface_info_entry_t *cur = nwk_interface_get_ipv6_ptr();
if (!cur) {
return;
}
if (cur->ipv6_configure->ipv6_stack_mode != NET_IPV6_BOOTSTRAP_STATIC) {
return;
}
if (addr_interface_gp_prefix_compare(cur, address) != 0) {
ns_list_foreach(ipv6_interface_route_on_link_t, cur_prefix, &route_on_link) {
if ((cur_prefix->prefix_len == prefixLength) && bitsequal(cur_prefix->prefix, address, prefixLength)) {
cur_prefix->routePrefer = routePrefer;
return;
}
}
}
ipv6_interface_route_on_link_t *new_entry = ns_dyn_mem_alloc(sizeof(ipv6_interface_route_on_link_t));
if (new_entry) {
memset(new_entry->prefix, 0, 16);
memcpy(new_entry->prefix, address, 16);
new_entry->prefix_len = prefixLength;
new_entry->routePrefer = routePrefer;
// Treat the prefix like a "route", so 3 * advert interval, not 30 days
new_entry->prefix_valid_ttl = (UINT32_C(3) * icmpv6_radv_max_rtr_adv_interval(cur)) / 10;
ns_list_add_to_end(&route_on_link, new_entry);
icmpv6_restart_router_advertisements(cur, ADDR_UNSPECIFIED);
}
}
void ipv6_prefix_on_link_update(uint8_t *address)
{
protocol_interface_info_entry_t *cur = nwk_interface_get_ipv6_ptr();
if (cur) {
//Call IPv6 Onlink Update
if (cur->ipv6_configure->ipv6_stack_mode == NET_IPV6_BOOTSTRAP_STATIC) {
ipv6_stack_prefix_on_link_update(cur, address);
}
}
}
static void ipv6_stack_prefix_on_link_remove(protocol_interface_info_entry_t *cur, uint8_t *address)
{
ns_list_foreach(ipv6_interface_prefix_on_link_t, cur_prefix, &prefix_on_link) {
if (memcmp(cur_prefix->prefix, address, 8) == 0) {
cur_prefix->prefix_valid_ttl = 0;
icmpv6_restart_router_advertisements(cur, ADDR_UNSPECIFIED);
break;
}
}
}
void ipv6_prefix_on_link_remove(uint8_t *address)
{
protocol_interface_info_entry_t *cur = nwk_interface_get_ipv6_ptr();
if (cur) {
if (cur->ipv6_configure->ipv6_stack_mode == NET_IPV6_BOOTSTRAP_STATIC) {
ipv6_stack_prefix_on_link_remove(cur, address);
}
}
}
void ipv6_stack_route_advert_remove(uint8_t *address, uint8_t prefixLength)
{
protocol_interface_info_entry_t *cur = nwk_interface_get_ipv6_ptr();
if (!cur) {
return;
}
if (cur->ipv6_configure->ipv6_stack_mode == NET_IPV6_BOOTSTRAP_STATIC) {
ns_list_foreach(ipv6_interface_route_on_link_t, cur_prefix, &route_on_link)
{
if ((cur_prefix->prefix_len == prefixLength) && bitsequal(cur_prefix->prefix, address, prefixLength)) {
cur_prefix->prefix_valid_ttl = 0;
icmpv6_restart_router_advertisements(cur, ADDR_UNSPECIFIED);
break;
}
}
}
}
void ipv6_prefix_online_list_free(void)
{
ns_list_foreach_safe(ipv6_interface_prefix_on_link_t, cur, &prefix_on_link) {
ns_list_remove(&prefix_on_link, cur);
ns_dyn_mem_free(cur);
}
}
void ipv6_rote_advert_list_free(void)
{
ns_list_foreach_safe(ipv6_interface_route_on_link_t, cur, &route_on_link) {
ns_list_remove(&route_on_link, cur);
ns_dyn_mem_free(cur);
}
}
void ipv6_core_slow_timer_event_handle(struct protocol_interface_info_entry *cur)
{
if (cur->ipv6_configure) {
protocol_interface_info_entry_t *curRegisteredInterface;
if (cur->ipv6_configure->wb_table_ttl-- == 1) {
//tr_debug("WB Table TTL");
whiteboard_ttl_update(WB_UPDATE_PERIOD_SECONDS);
cur->ipv6_configure->wb_table_ttl = WB_UPDATE_PERIOD_SECONDS;
}
if (cur->ipv6_configure->ipv6_stack_mode == NET_IPV6_BOOTSTRAP_STATIC) {
if (cur->global_address_available) {
curRegisteredInterface = protocol_stack_interface_info_get(IF_6LoWPAN);
if (curRegisteredInterface) {
ipv6_prefix_on_link_list_add_by_interface_address_list(cur, curRegisteredInterface);
}
}
}
}
}
void ipv6_core_timer_event_handle(protocol_interface_info_entry_t *cur, const uint8_t event)
{
(void)event;
if (cur->ipv6_configure->ND_TIMER) {
if (cur->ipv6_configure->ND_TIMER-- == 1) {
//Call ND State Machine
ipv6_nd_bootstrap(cur);
}
}
}
int ipv6_prefix_register(uint8_t *prefix_64, uint32_t lifetime, uint32_t prefer_lifetime)
{
prefix_entry_t *new_entry = icmpv6_prefix_add(&ipv6_prefixs, prefix_64, 64, lifetime, prefer_lifetime, (PIO_L | PIO_A));
if (new_entry) {
memset(&new_entry->prefix[8], 0, 8);
new_entry->options = PIO_L | PIO_A;
return 0;
}
return -1;
}
int ipv6_interface_route_validate(int8_t interface_id , uint8_t *address)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur) {
return -1;
} else if (!cur->ipv6_configure) {
return -1;
} else if (cur->ipv6_configure->IPv6_ND_state != IPV6_READY) {
return -1;
}
//Validate route OnLink
ipv6_route_t *route;
route = ipv6_route_choose_next_hop(address, interface_id, NULL);
//Respond default route only when we have other interface for route
if (route && route->on_link) {
return 0;
}
return -1;
}
int8_t ipv6_interface_up(protocol_interface_info_entry_t *cur)
{
uint8_t ipv6_interface_adr[16];
if (cur->if_stack_buffer_handler) {
return -1;
}
if (cur->ipv6_configure->ipv6_stack_mode == NET_IPV6_BOOTSTRAP_STATIC) {
uint32_t lifeTime, prefLifetime;
uint8_t pre_options;
memcpy(&ipv6_interface_adr[0], cur->ipv6_configure->static_prefix64, 8);
memcpy(&ipv6_interface_adr[8], cur->iid_eui64, 8);
prefix_entry_t *new_entry = 0;
// Treat the prefix like a "route", so 3 * advert interval, not 30 days
lifeTime = (UINT32_C(3) * icmpv6_radv_max_rtr_adv_interval(cur)) / 10;
prefLifetime = lifeTime / 2;
pre_options = PIO_L | PIO_A | PIO_R;
new_entry = icmpv6_prefix_add(&ipv6_prefixs, ipv6_interface_adr, 64, lifeTime, prefLifetime, pre_options);
if (!new_entry) {
return -1;
}
memcpy(&new_entry->prefix[8], &ipv6_interface_adr[8], 8);
new_entry->options = pre_options;
ipv6_route_add(ipv6_interface_adr, 64, cur->id, NULL, ROUTE_STATIC, 0xffffffff, 1);
// Disable RA route and prefix processing until static address has been added to interface
icmpv6_recv_ra_routes(cur, false);
icmpv6_recv_ra_prefixes(cur, false);
}
cur->ipv6_configure->IPv6_ND_state = IPV6_LL_CONFIG;
cur->ipv6_configure->ND_TIMER = 1;
cur->ipv6_configure->routerSolicationRetryCounter = 0;
cur->ipv6_configure->wb_table_ttl = WB_UPDATE_PERIOD_SECONDS;
icmpv6_radv_disable(cur);
tr_debug("IPV6 interface Base Ready");
cur->if_stack_buffer_handler = ipv6_stack;
cur->if_llao_write = ethernet_llao_write;
cur->if_llao_parse = ethernet_llao_parse;
cur->ipv6_neighbour_cache.max_ll_len = 6;
cur->ipv6_neighbour_cache.link_mtu = cur->max_link_mtu = 1500;
cur->lowpan_info |= INTERFACE_NWK_BOOTSRAP_ACTIVE;
cur->lowpan_info |= INTERFACE_NWK_ACTIVE;
// Always want fe80::/64 to be an on-link prefix.
ipv6_route_add(ADDR_LINK_LOCAL_PREFIX, 64, cur->id, NULL, ROUTE_STATIC, 0xFFFFFFFF, 0);
// Putting a multicast route to ff00::/8 makes sure we can always transmit multicast.
// Interface metric will determine which interface is actually used, if we have multiple.
ipv6_route_add(ADDR_LINK_LOCAL_ALL_NODES, 8, cur->id, NULL, ROUTE_STATIC, 0xFFFFFFFF, -1);
return 0;
}
int8_t ipv6_interface_down(protocol_interface_info_entry_t *cur)
{
if (!cur || !(cur->lowpan_info & INTERFACE_NWK_ACTIVE)) {
return -1;
}
whiteboard_interface_unregister_all_address(cur->id);
icmpv6_prefix_list_free(&ipv6_prefixs);
ipv6_prefix_online_list_free();
ipv6_rote_advert_list_free();
neighbor_cache_flush(&cur->neigh_cache);
ipv6_route_table_remove_interface(cur->id);
protocol_core_interface_info_reset(cur);
cur->ipv6_configure->wb_table_ttl = 0;
cur->lowpan_info &= ~INTERFACE_NWK_ACTIVE;
cur->global_address_available = false;
//Clear all generated proxies
nd_proxy_upstream_interface_unregister(cur->id);
return 0;
}
static uint8_t ipv6_nd_rs(protocol_interface_info_entry_t *cur)
{
buffer_t *buf = icmpv6_build_rs(cur, NULL);
if (buf) {
tr_debug("Push RS to IPV6");
arm_net_protocol_packet_handler(buf, cur);
return 1;
}
return 0;
}
#ifdef RADV_TX
void ipv6_nd_ra_advert(protocol_interface_info_entry_t *cur, const uint8_t *dest)
{
/* 12 base size, plus allow 8 for MTU and 16 for SLLA */
uint16_t length = 12 + 8 + 16;
length += 32 * ns_list_count(&ipv6_prefixs);
length += 32 * ns_list_count(&prefix_on_link);
ns_list_foreach(ipv6_interface_route_on_link_t, tmp_route, &route_on_link) {
if (tmp_route->prefix_len < 65) {
length += 16; // add upper 64-bit
} else {
length += 24; // add full address
}
}
buffer_t *buf = buffer_get(length);
if (buf) {
uint8_t *ptr = buffer_data_pointer(buf);
buf->options.hop_limit = 255;
buf->options.type = ICMPV6_TYPE_INFO_RA;
buf->options.code = 0x00;
*ptr++ = cur->adv_cur_hop_limit;
*ptr++ = cur->rtr_adv_flags;
//Do not advertise default route: set Router Lifetime to 0
ptr = common_write_16_bit(0, ptr);
ptr = common_write_32_bit(cur->adv_reachable_time, ptr);
ptr = common_write_32_bit(cur->adv_retrans_timer, ptr);
// We don't include our Source Link-Layer address - we're not
// advertising routes, so we don't expect anyone to be talking directly
// to us; we're just acting as an ND proxy. If we put in the SLLAO, we
// add ourselves to their Neighbour Cache unnecessarily. This can be
// reactivated if we start advertising actual routes
#if 0
// Set the source Link-Layer address
ptr = icmpv6_write_icmp_lla(cur, ptr, ICMPV6_OPT_SRC_LL_ADDR);
#endif
//MTU
if (cur->adv_link_mtu != 0) {
ptr = icmpv6_write_mtu_option(cur->adv_link_mtu, ptr);
}
//IPv6 Prefixes
ptr = icmpv6_write_prefix_option(&ipv6_prefixs, ptr, 0, cur);
// Update our own routing table prefixes in sync with what we're advertising above
ns_list_foreach(prefix_entry_t, pfx, &ipv6_prefixs) {
if (pfx->options & PIO_L) {
ipv6_route_add(pfx->prefix, pfx->prefix_len, cur->id, NULL, ROUTE_STATIC, pfx->lifetime, 1);
}
}
ns_list_foreach_safe(ipv6_interface_prefix_on_link_t, pfx, &prefix_on_link) {
ipv6_route_add(pfx->prefix, pfx->prefix_len, cur->id, NULL, ROUTE_STATIC, pfx->prefix_valid_ttl, 0);
*ptr++ = ICMPV6_OPT_PREFIX_INFO;
*ptr++ = 4; // length
*ptr++ = pfx->prefix_len;
*ptr++ = PIO_L;
ptr = common_write_32_bit(pfx->prefix_valid_ttl, ptr); // Valid Lifetime
ptr = common_write_32_bit(pfx->prefix_valid_ttl >> 1, ptr); // Preferred Lifetime
ptr = common_write_32_bit(0, ptr);
memcpy(ptr, pfx->prefix, 16);
ptr += 16;
if (pfx->prefix_valid_ttl == 0) {
ns_list_remove(&prefix_on_link, pfx);
ns_dyn_mem_free(pfx);
}
}
ns_list_foreach_safe(ipv6_interface_route_on_link_t, tmp_route, &route_on_link) {
uint8_t prefix_bytes;
*ptr++ = ICMPV6_OPT_ROUTE_INFO;
if (tmp_route->prefix_len < 65) {
*ptr++ = 2;
prefix_bytes = 8;
} else {
*ptr++ = 3;
prefix_bytes = 16;
}
*ptr++ = tmp_route->prefix_len;
*ptr++ = tmp_route->routePrefer;
ptr = common_write_32_bit(tmp_route->prefix_valid_ttl, ptr);
memcpy(ptr, tmp_route->prefix, prefix_bytes);
ptr += prefix_bytes;
if (tmp_route->prefix_valid_ttl == 0) {
ns_list_remove(&route_on_link, tmp_route);
ns_dyn_mem_free(tmp_route);
}
}
buffer_data_end_set(buf, ptr);
memcpy(buf->dst_sa.address, dest, 16);
/* Source must be LL address (even if non-LL dest) */
if (addr_interface_get_ll_address(cur, buf->src_sa.address, 0)) {
tr_debug("No address defined");
buffer_free(buf);
return;
}
buf->dst_sa.addr_type = ADDR_IPV6;
buf->src_sa.addr_type = ADDR_IPV6;
buf->interface = cur;
buf->info = (buffer_info_t)(B_FROM_ICMP | B_TO_ICMP | B_DIR_DOWN);
arm_net_protocol_packet_handler(buf, cur);
}
}
#endif
static void ipv6_stack(buffer_t *b)
{
/* Protocol Buffer Handle */
int8_t status;
nwk_interface_id nwk_id = IF_IPV6;
if (b) {
//Save Original Interface id
nwk_id = b->interface->nwk_id;
}
while (b) {
/* Buffer Direction Select Switch */
if ((b->info & B_DIR_MASK) == B_DIR_DOWN) {
/* Direction DOWN */
switch (b->info & B_TO_MASK) {
case B_TO_ICMP:
/* Build ICMP Header */
b = icmpv6_down(b);
break;
case B_TO_UDP:
/* Build UDP Header */
b = udp_down(b);
break;
case B_TO_IPV6:
/* Build IP Header */
b = ipv6_down(b);
break;
case B_TO_IPV6_FWD:
/* IPv6 forwarding */
b = ipv6_forwarding_down(b);
break;
case B_TO_IPV6_TXRX:
/* Resolution, preparing MAC payload */
b = ethernet_down(b);
break;
case B_TO_MAC:
b = ethernet_header_build_push(b, ETHERTYPE_IPV6, &status);
if (b) {
//GET Interface Pointer
//uint8_t *ptr = b->buf + b->buf_ptr;
//uint16_t len = b->buf_end - b->buf_ptr;
//ethernet_tx(ptr, len);
if (status == 0) {
socket_tx_buffer_event_and_free(b, SOCKET_TX_DONE);
} else {
socket_tx_buffer_event_and_free(b, SOCKET_TX_FAIL);
}
}
return;
default:
b = buffer_free(b);
break;
}
} else {
/* Direction UP */
switch (b->info & B_TO_MASK) {
case B_TO_APP:
socket_up(b);
b = NULL;
break;
case B_TO_MAC:
/* Beacon and command frames */
//b = mac_up(b);
break;
case B_TO_ICMP:
/* Parse ICMP Message */
b = icmpv6_up(b);
break;
case B_TO_UDP:
/* Parse UDP Message */
b = udp_up(b);
break;
case B_TO_IPV6_FWD:
/* Handle IP Payload */
b = ipv6_forwarding_up(b);
break;
case B_TO_IPV6_TXRX:
/* Handle MAC Payload */
b = ethernet_up(b);
break;
case B_TO_TCP:
//tr_debug("--> TCP_up()");
b = tcp_up(b);
break;
default:
tr_debug("LLL");
b = buffer_free(b);
break;
}
}
if (b) {
//Check If Stack forward packet to different interface
if (b->interface->nwk_id != nwk_id) {
protocol_push(b);
b = 0;
}
}
}
}
static void ipv6_interface_address_cb(protocol_interface_info_entry_t *interface, if_address_entry_t *addr, if_address_callback_t reason)
{
tr_debug("Interface ID: %i, ipv6: %s", interface->id, trace_ipv6(addr->address));
switch (reason) {
case ADDR_CALLBACK_DAD_COMPLETE:
tr_debug("DAD OK");
switch (interface->ipv6_configure->IPv6_ND_state) {
case IPV6_LL_CONFIG:
if (interface->ipv6_configure->ipv6_stack_mode == NET_IPV6_BOOTSTRAP_STATIC) {
interface->ipv6_configure->IPv6_ND_state = IPV6_GP_GEN;
} else {
interface->ipv6_configure->IPv6_ND_state = IPV6_ROUTER_SOLICATION;
}
interface->ipv6_configure->ND_TIMER = 1;
break;
case IPV6_GP_CONFIG:
if (addr_interface_all_address_ready(interface)) {
interface->global_address_available = true;
if (interface->lowpan_info & INTERFACE_NWK_BOOTSRAP_ACTIVE) {
nwk_bootsrap_state_update(ARM_NWK_BOOTSTRAP_READY, interface);
/* We will need proxy both mode currently future static mode should not need proxy */
nd_proxy_upstream_interface_register(interface->id, ipv6_interface_route_validate);
}
}
// Enable RA route and prefix processing for static configuration mode if RA accept always is enabled
if (interface->ipv6_configure->ipv6_stack_mode == NET_IPV6_BOOTSTRAP_STATIC &&
interface->ipv6_configure->accept_ra == NET_IPV6_RA_ACCEPT_ALWAYS) {
icmpv6_recv_ra_routes(interface, true);
icmpv6_recv_ra_prefixes(interface, true);
}
if (addr->source == ADDR_SOURCE_SLAAC) {
ipv6_prefix_router_flag_activate(addr->address);
}
break;
default:
/* No special action for subsequent addresses after initial bootstrap */
if (interface->global_address_available && interface->ipv6_configure->temporaryUlaAddressState) {
if (addr_ipv6_scope(addr->address,interface) == IPV6_SCOPE_GLOBAL) {
nwk_bootsrap_state_update(ARM_NWK_BOOTSTRAP_READY, interface);
tr_debug("Learn Real Global Scope");
interface->ipv6_configure->temporaryUlaAddressState = false;
/* We will need proxy both mode currently future static mode should not need proxy */
nd_proxy_upstream_interface_register(interface->id, ipv6_interface_route_validate);
}
}
break;
}
break;
case ADDR_CALLBACK_PARENT_FULL:
case ADDR_CALLBACK_DAD_FAILED:
switch (interface->ipv6_configure->IPv6_ND_state) {
case IPV6_LL_CONFIG:
tr_warn("No Valid LLaddress..Turn OFF Interface and Push DAD Event");
nwk_bootsrap_state_update(ARM_NWK_DUPLICATE_ADDRESS_DETECTED, interface);
break;
case IPV6_GP_CONFIG:
if (interface->lowpan_info & INTERFACE_NWK_BOOTSRAP_ACTIVE) {
}
break;
default:
/* No special action for subsequent addresses after initial bootstrap */
break;
}
break;
default:
break;
}
}
void ipv6_interface_slaac_handler(protocol_interface_info_entry_t *cur, const uint8_t *slaacPrefix, uint8_t prefixLen, uint32_t validLifeTime, uint32_t preferredLifeTime)
{
if_address_entry_t *address_entry = icmpv6_slaac_address_add(cur, slaacPrefix, prefixLen, validLifeTime, preferredLifeTime, false, SLAAC_IID_DEFAULT);
if (address_entry) {
address_entry->cb = ipv6_interface_address_cb;
if (cur->ipv6_configure && cur->ipv6_configure->IPv6_ND_state == IPV6_ROUTER_SOLICATION) {
cur->ipv6_configure->IPv6_ND_state = IPV6_GP_CONFIG;
}
}
}
int8_t ipv6_interface_configure_ipv6_bootstrap_set(int8_t interface_id, net_ipv6_mode_e bootstrap_mode, const uint8_t *ipv6_prefix_pointer)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur) {
return -1;
}
if (cur->lowpan_info & INTERFACE_NWK_ACTIVE) {
return -4;
}
if (cur->nwk_id != IF_IPV6) {
return -1;
}
switch (bootstrap_mode) {
case NET_IPV6_BOOTSTRAP_STATIC:
memcpy(cur->ipv6_configure->static_prefix64, ipv6_prefix_pointer, 8);
/* fall through */
case NET_IPV6_BOOTSTRAP_AUTONOMOUS:
cur->bootsrap_mode = ARM_NWK_BOOTSRAP_MODE_ETHERNET_ROUTER;
cur->ipv6_configure->ipv6_stack_mode = bootstrap_mode;
break;
default:
break;
}
return 0;
}
int8_t ipv6_interface_accept_ra(int8_t interface_id, net_ipv6_accept_ra_e accept_ra)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur || !cur->ipv6_configure) {
return -1;
}
cur->ipv6_configure->accept_ra = accept_ra;
return 0;
}
#endif