Nanostack Border Router is a generic mbed border router implementation that provides the 6LoWPAN ND or Thread border router initialization logic.

source/thread_br_conn_handler.c

Committer:
mbed_official
Date:
2017-06-19
Revision:
21:3754f30e7b69
Parent:
20:918e62713e63
Child:
22:8740285d8f09

File content as of revision 21:3754f30e7b69:

/*
 * Copyright (c) 2016 ARM Limited. All rights reserved.
 */

#include <string.h>
#include "net_interface.h"
#include "mbed-trace/mbed_trace.h"
#include "thread_border_router_api.h"
#include "thread_br_conn_handler.h"
#include "thread_dhcpv6_server.h"
#include "borderrouter_helpers.h"
#include "common_functions.h"
#include "eventOS_event_timer.h"
#include "thread_bbr_ext.h"

#define TRACE_GROUP "TBRH"
#define DHCP_SERVER_SHUTDOWN_TIMEOUT (100)

typedef struct {
    
    uint8_t dhcp_prefix[16];
    timeout_t *thread_dhcp_shutdown_timer;
    uint8_t dhcp_prefix_len;
    int8_t  thread_interface_id;
    int8_t  eth_interface_id;
    bool    eth_connection_ready;
    bool    thread_connection_ready;
    bool    dhcp_server_running;
} thread_br_handler_t;

static thread_br_handler_t thread_br_handler;
static void thread_br_conn_handler_border_router_startup_attempt(void);
static bool thread_br_conn_handler_default_route_enable(void);
static void thread_br_conn_handler_dhcp_server_stop_cb(void *arg);
static void thread_br_conn_handler_border_router_shutdown_request(void);


void thread_br_conn_handler_init(void)
{
    thread_br_handler.eth_connection_ready = false;
    thread_br_handler.thread_connection_ready = false;    
    thread_br_handler.dhcp_server_running = false;
    thread_br_handler.dhcp_prefix_len = 0;
    thread_br_handler.thread_interface_id = -1;
    thread_br_handler.eth_interface_id = -1;    
    thread_br_handler.thread_dhcp_shutdown_timer = NULL;
    memset(thread_br_handler.dhcp_prefix, 0, 16);
}

static void thread_br_conn_handler_border_router_startup_attempt(void)
{
    if (thread_br_handler.thread_dhcp_shutdown_timer != NULL) {
        tr_debug("DHCP server already running, enable default_route");
        eventOS_timeout_cancel(thread_br_handler.thread_dhcp_shutdown_timer);
        thread_br_handler.thread_dhcp_shutdown_timer = NULL;
    }

    if (!thread_br_handler.eth_connection_ready) {
        tr_debug("eth0 is down");
        return;
    } else if (!thread_br_handler.thread_connection_ready) {
        tr_debug("mesh0 is down");
        return;
    }

    if (thread_br_handler.dhcp_prefix_len == 0) {
        //No prefix/prefix_len to start DHCP server
        tr_error("DHCP server prefix length = 0");
        return;
    }

    if (thread_br_handler.thread_interface_id == -1) {
        tr_error("Thread interface ID not set");
        return;
    }

    if (thread_br_handler.dhcp_server_running == true) {
        // DHCP server is already running, enable default route
        tr_debug("DHCP server already running, enable default_route");
        thread_br_conn_handler_default_route_enable();
        return;
    }

    int retcode = thread_dhcpv6_server_add(thread_br_handler.thread_interface_id, thread_br_handler.dhcp_prefix, 200, true);
    if (retcode == 0) {
        tr_debug("DHCP server started ");
        if (thread_br_conn_handler_default_route_enable()) {
            thread_br_handler.dhcp_server_running = true;
            thread_bbr_extension_start(thread_br_handler.thread_interface_id, thread_br_handler.eth_interface_id);
        } else {
            tr_error("Failed to update DHCP default route");
        }
    } else {
        tr_error("DHCP server start failed");
    }
}

void thread_br_conn_handler_thread_connection_update(bool status)
{
    thread_br_handler.thread_connection_ready = status;
    tr_debug("mesh0 connection status: %d", status);

    if (status) {
        thread_br_conn_handler_border_router_startup_attempt();
    } else {
        // Thread network down. Reset DHCP server back to original state
        thread_br_handler.dhcp_server_running = false;
        // stop mDNS responder as no thread network
        thread_border_router_mdns_responder_stop();
        if (thread_br_handler.thread_dhcp_shutdown_timer != NULL) {
            // cancel active shutdown timer
            eventOS_timeout_cancel(thread_br_handler.thread_dhcp_shutdown_timer);
            thread_br_handler.thread_dhcp_shutdown_timer = NULL;
        }
    }
}

void thread_br_conn_handler_ethernet_connection_update(bool status)
{
    thread_br_handler.eth_connection_ready = status;
    tr_debug("Eth0 connection status: %d", status);

    if (status) {
        thread_br_conn_handler_border_router_startup_attempt();
    } else {
        // Ethernet connection down, request DHCP server shutdown
        thread_br_conn_handler_border_router_shutdown_request();
        thread_border_router_mdns_responder_stop();
    }
}

void thread_br_conn_handler_eth_ready()
{    
    uint8_t prefix_len = 64;
    uint8_t global_address[16];
    
    if (0 == arm_net_address_get(thread_br_conn_handler_eth_interface_id_get(), ADDR_IPV6_GP, global_address)) {
        tr_info("Ethernet (eth0) bootstrap ready. IP: %s", print_ipv6(global_address));
    } else {
        tr_warn("arm_net_address_get fail");
    }
    thread_br_handler.dhcp_prefix_len = prefix_len;
    memset(thread_br_handler.dhcp_prefix, 0, 16);
    memcpy(thread_br_handler.dhcp_prefix, global_address, prefix_len / 8);
}

static bool thread_br_conn_handler_default_route_enable(void)
{
    thread_border_router_info_t thread_border_router_info;
    thread_border_router_info.Prf = 1;
    thread_border_router_info.P_preferred = false;
    thread_border_router_info.P_slaac = false;
    thread_border_router_info.P_dhcp = true;
    thread_border_router_info.P_configure = false;
    thread_border_router_info.P_default_route = true;
    thread_border_router_info.P_on_mesh = false;
    thread_border_router_info.P_nd_dns = false;
    thread_border_router_info.stableData = true;

    if (thread_border_router_prefix_add(thread_br_handler.thread_interface_id, thread_br_handler.dhcp_prefix, thread_br_handler.dhcp_prefix_len, &thread_border_router_info) == 0) {
        thread_border_router_publish(thread_br_handler.thread_interface_id);
        tr_debug("Updated %s prefix", print_ipv6_prefix(thread_br_handler.dhcp_prefix, thread_br_handler.dhcp_prefix_len));
        thread_border_router_mdns_responder_start(thread_br_handler.thread_interface_id, thread_br_handler.eth_interface_id, "ARM-BR");
        return true;
    } else {
        tr_error("Failed to enable default_route flag to prefix");
        return false;
    }
}

static void thread_br_conn_handler_dhcp_server_stop_cb(void *arg)
{
    (void)arg;

    tr_debug("DHCP server stop cb");
    thread_br_handler.thread_dhcp_shutdown_timer = NULL;
    thread_br_handler.dhcp_server_running = false;
    thread_dhcpv6_server_delete(thread_br_handler.thread_interface_id, thread_br_handler.dhcp_prefix);
    thread_border_router_publish(thread_br_handler.thread_interface_id);
    thread_br_handler.dhcp_prefix_len = 0;
    memset(thread_br_handler.dhcp_prefix, 0, 16);
}

static void thread_br_conn_handler_border_router_shutdown_request(void)
{
    if (thread_br_handler.dhcp_server_running && thread_br_handler.thread_dhcp_shutdown_timer == NULL) {
        tr_debug("DHCP server shutdown timer started");
        thread_br_handler.thread_dhcp_shutdown_timer = eventOS_timeout_ms(thread_br_conn_handler_dhcp_server_stop_cb, DHCP_SERVER_SHUTDOWN_TIMEOUT, NULL);
    }
}

void thread_br_conn_handler_thread_interface_id_set(int8_t interfaceId)
{
    thread_br_handler.thread_interface_id = interfaceId;
    thread_bbr_extension_mesh_interface_updated_ntf(thread_br_handler.thread_interface_id);
}

int8_t thread_br_conn_handler_thread_interface_id_get(void)
{
    return thread_br_handler.thread_interface_id;
}

bool thread_br_conn_handler_eth_connection_status_get(void)
{
    return thread_br_handler.eth_connection_ready;
}

bool thread_br_conn_handler_thread_connection_status_get(void)
{
    return thread_br_handler.thread_connection_ready;
}

void thread_br_conn_handler_eth_interface_id_set(int8_t interfaceId)
{
    thread_br_handler.eth_interface_id = interfaceId;
    thread_bbr_extension_bb_interface_updated_ntf(thread_br_handler.eth_interface_id);
}

int8_t thread_br_conn_handler_eth_interface_id_get(void)
{
    return thread_br_handler.eth_interface_id;
}