mirror of https://github.com/ARMmbed/mbed-os.git
1010 lines
35 KiB
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, ðernet_data_conf_cb, ðernet_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
|