Skip to content

Add non-blocking DNS functionality to network interface #6751

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 73 additions & 65 deletions features/FEATURE_LWIP/lwip-interface/LWIPStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "lwip/dns.h"
#include "lwip/udp.h"
#include "lwip/lwip_errno.h"
#include "lwip-sys/arch/sys_arch.h"

#include "LWIPStack.h"

Expand All @@ -47,10 +48,10 @@ void LWIP::socket_callback(struct netconn *nc, enum netconn_evt eh, u16_t len)
return;
}

sys_prot_t prot = sys_arch_protect();

LWIP &lwip = LWIP::get_instance();

lwip.adaptation.lock();

for (int i = 0; i < MEMP_NUM_NETCONN; i++) {
if (lwip.arena[i].in_use
&& lwip.arena[i].conn == nc
Expand All @@ -59,7 +60,7 @@ void LWIP::socket_callback(struct netconn *nc, enum netconn_evt eh, u16_t len)
}
}

sys_arch_unprotect(prot);
lwip.adaptation.unlock();
}

#if !LWIP_IPV4 || !LWIP_IPV6
Expand Down Expand Up @@ -149,6 +150,7 @@ void LWIP::tcpip_init_irq(void *eh)
{
LWIP *lwip = static_cast<LWIP *>(eh);
lwip->tcpip_inited.release();
sys_tcpip_thread_set();
}

/* LWIP network stack implementation */
Expand All @@ -173,80 +175,85 @@ LWIP::LWIP()
arena_init();
}

nsapi_error_t LWIP::gethostbyname(const char *host, SocketAddress *address, nsapi_version_t version)
nsapi_error_t LWIP::get_dns_server(int index, SocketAddress *address)
{
ip_addr_t lwip_addr;

#if LWIP_IPV4 && LWIP_IPV6
u8_t addr_type;
if (version == NSAPI_UNSPEC) {
const ip_addr_t *ip_addr = NULL;
if (default_interface) {
ip_addr = get_ip_addr(true, &default_interface->netif);
}
// Prefer IPv6
if (IP_IS_V6(ip_addr)) {
// If IPv4 is available use it as backup
if (get_ipv4_addr(&default_interface->netif)) {
addr_type = NETCONN_DNS_IPV6_IPV4;
} else {
addr_type = NETCONN_DNS_IPV6;
}
// Prefer IPv4
} else {
// If IPv6 is available use it as backup
if (get_ipv6_addr(&default_interface->netif)) {
addr_type = NETCONN_DNS_IPV4_IPV6;
} else {
addr_type = NETCONN_DNS_IPV4;
int dns_entries = 0;

for (int i = 0; i < DNS_MAX_SERVERS; i++) {
const ip_addr_t *ip_addr = dns_getserver(i);
if (!ip_addr_isany(ip_addr)) {
if (index == dns_entries) {
nsapi_addr_t addr;
convert_lwip_addr_to_mbed(&addr, ip_addr);
address->set_addr(addr);
return NSAPI_ERROR_OK;
}
dns_entries++;
}
} else if (version == NSAPI_IPv4) {
addr_type = NETCONN_DNS_IPV4;
} else if (version == NSAPI_IPv6) {
addr_type = NETCONN_DNS_IPV6;
} else {
return NSAPI_ERROR_DNS_FAILURE;
}
err_t err = netconn_gethostbyname_addrtype(host, &lwip_addr, addr_type);
#elif LWIP_IPV4
if (version != NSAPI_IPv4 && version != NSAPI_UNSPEC) {
return NSAPI_ERROR_DNS_FAILURE;
return NSAPI_ERROR_NO_ADDRESS;
}

void LWIP::tcpip_thread_callback(void *ptr)
{
lwip_callback *cb = static_cast<lwip_callback *>(ptr);

if (cb->delay) {
sys_timeout(cb->delay, LWIP::tcpip_thread_callback, ptr);
cb->delay = 0;
} else {
cb->callback();
delete cb;
}
err_t err = netconn_gethostbyname(host, &lwip_addr);
#elif LWIP_IPV6
if (version != NSAPI_IPv6 && version != NSAPI_UNSPEC) {
return NSAPI_ERROR_DNS_FAILURE;
}

nsapi_error_t LWIP::call_in(int delay, mbed::Callback<void()> func)
{
lwip_callback *cb = new lwip_callback;
if (!cb) {
return NSAPI_ERROR_NO_MEMORY;
}
err_t err = netconn_gethostbyname(host, &lwip_addr);
#endif

if (err != ERR_OK) {
return NSAPI_ERROR_DNS_FAILURE;
cb->delay = delay;
cb->callback = func;

if (tcpip_callback_with_block(LWIP::tcpip_thread_callback, cb, 1) != ERR_OK) {
return NSAPI_ERROR_NO_MEMORY;
}

nsapi_addr_t addr;
convert_lwip_addr_to_mbed(&addr, &lwip_addr);
address->set_addr(addr);
return NSAPI_ERROR_OK;
}

return 0;
LWIP::call_in_callback_cb_t LWIP::get_call_in_callback()
{
call_in_callback_cb_t cb(this, &LWIP::call_in);
return cb;
}

nsapi_error_t LWIP::add_dns_server(const SocketAddress &address)
const char *LWIP::get_ip_address()
{
// Shift all dns servers down to give precedence to new server
for (int i = DNS_MAX_SERVERS-1; i > 0; i--) {
dns_setserver(i, dns_getserver(i-1));
if (!default_interface) {
return NULL;
}

nsapi_addr_t addr = address.get_addr();
ip_addr_t ip_addr;
if (!convert_mbed_addr_to_lwip(&ip_addr, &addr)) {
return NSAPI_ERROR_PARAMETER;
}
const ip_addr_t *addr = get_ip_addr(true, &default_interface->netif);

dns_setserver(0, &ip_addr);
return 0;
if (!addr) {
return NULL;
}
#if LWIP_IPV6
if (IP_IS_V6(addr)) {
return ip6addr_ntoa_r(ip_2_ip6(addr), ip_address, sizeof(ip_address));
}
#endif
#if LWIP_IPV4
if (IP_IS_V4(addr)) {
return ip4addr_ntoa_r(ip_2_ip4(addr), ip_address, sizeof(ip_address));
}
#endif
#if LWIP_IPV6 && LWIP_IPV4
return NULL;
#endif
}

nsapi_error_t LWIP::socket_open(nsapi_socket_t *handle, nsapi_protocol_t proto)
Expand Down Expand Up @@ -439,6 +446,7 @@ nsapi_size_or_error_t LWIP::socket_sendto(nsapi_socket_t handle, const SocketAdd
}

struct netbuf *buf = netbuf_new();

err_t err = netbuf_ref(buf, data, (u16_t)size);
if (err != ERR_OK) {
netbuf_free(buf);
Expand Down Expand Up @@ -588,7 +596,7 @@ nsapi_error_t LWIP::setsockopt(nsapi_socket_t handle, int level, int optname, co

member_pair_index = next_free_multicast_member(s, 0);

sys_prot_t prot = sys_arch_protect();
adaptation.lock();

#if LWIP_IPV4
if (IP_IS_V4(&if_addr)) {
Expand All @@ -601,7 +609,7 @@ nsapi_error_t LWIP::setsockopt(nsapi_socket_t handle, int level, int optname, co
}
#endif

sys_arch_unprotect(prot);
adaptation.unlock();

if (igmp_err == ERR_OK) {
set_multicast_member_registry_bit(s, member_pair_index);
Expand All @@ -616,7 +624,7 @@ nsapi_error_t LWIP::setsockopt(nsapi_socket_t handle, int level, int optname, co
clear_multicast_member_registry_bit(s, member_pair_index);
s->multicast_memberships_count--;

sys_prot_t prot = sys_arch_protect();
adaptation.lock();

#if LWIP_IPV4
if (IP_IS_V4(&if_addr)) {
Expand All @@ -629,7 +637,7 @@ nsapi_error_t LWIP::setsockopt(nsapi_socket_t handle, int level, int optname, co
}
#endif

sys_arch_unprotect(prot);
adaptation.unlock();
}

return err_remap(igmp_err);
Expand Down
67 changes: 51 additions & 16 deletions features/FEATURE_LWIP/lwip-interface/LWIPStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,29 +198,23 @@ class LWIP : public OnboardNetworkStack, private mbed::NonCopyable<LWIP> {
*/
nsapi_error_t _add_ppp_interface(void *pcb, bool default_if, LWIP::Interface **interface_out);

/** Translates a hostname to an IP address with specific version
/** Get a domain name server from a list of servers to query
*
* The hostname may be either a domain name or an IP address. If the
* hostname is an IP address, no network transactions will be performed.
* Returns a DNS server address for a index. If returns error no more
* DNS servers to read.
*
* If no stack-specific DNS resolution is provided, the hostname
* will be resolve using a UDP socket on the stack.
*
* @param host Hostname to resolve
* @param address Destination for the host SocketAddress
* @param version IP version of address to resolve, NSAPI_UNSPEC indicates
* version is chosen by the stack (defaults to NSAPI_UNSPEC)
* @param index Index of the DNS server, starts from zero
* @param address Destination for the host address
* @return 0 on success, negative error code on failure
*/
virtual nsapi_error_t gethostbyname(const char *host,
SocketAddress *address, nsapi_version_t version = NSAPI_UNSPEC);
virtual nsapi_error_t get_dns_server(int index, SocketAddress *address);

/** Add a domain name server to list of servers to query
/** Get the local IP address
*
* @param address Destination for the host address
* @return 0 on success, negative error code on failure
* @return Null-terminated representation of the local IP address
* or null if not yet connected
*/
virtual nsapi_error_t add_dns_server(const SocketAddress &address);
virtual const char *get_ip_address();

protected:
LWIP();
Expand Down Expand Up @@ -423,6 +417,37 @@ class LWIP : public OnboardNetworkStack, private mbed::NonCopyable<LWIP> {
int optname, void *optval, unsigned *optlen);
private:

/** Call in callback
*
* Callback is used to call the call in method of the network stack.
*/
typedef mbed::Callback<nsapi_error_t (int delay_ms, mbed::Callback<void()> user_cb)> call_in_callback_cb_t;

/** Get a call in callback
*
* Get a call in callback from the network stack context.
*
* Callback should not take more than 10ms to execute, otherwise it might
* prevent underlying thread processing. A portable user of the callback
* should not make calls to network operations due to stack size limitations.
* The callback should not perform expensive operations such as socket recv/send
* calls or blocking operations.
*
* @return Call in callback
*/
virtual call_in_callback_cb_t get_call_in_callback();

/** Call a callback after a delay
*
* Call a callback from the network stack context after a delay. If function
* returns error callback will not be called.
*
* @param delay Delay in milliseconds
* @param func Callback to be called
* @return 0 on success, negative error code on failure
*/
nsapi_error_t call_in(int delay, mbed::Callback<void()> func);

struct mbed_lwip_socket {
bool in_use;

Expand All @@ -439,6 +464,11 @@ class LWIP : public OnboardNetworkStack, private mbed::NonCopyable<LWIP> {
uint32_t multicast_memberships_registry;
};

struct lwip_callback {
unsigned int delay;
mbed::Callback<void()> callback;
};

static nsapi_error_t err_remap(err_t err);
static bool is_local_addr(const ip_addr_t *ip_addr);
static const ip_addr_t *get_ip_addr(bool any_addr, const struct netif *netif);
Expand Down Expand Up @@ -475,9 +505,14 @@ class LWIP : public OnboardNetworkStack, private mbed::NonCopyable<LWIP> {
static void socket_callback(struct netconn *nc, enum netconn_evt eh, u16_t len);

static void tcpip_init_irq(void *handle);
static void tcpip_thread_callback(void *ptr);

char ip_address[40];
rtos::Semaphore tcpip_inited;
Interface *default_interface;
LWIPMemoryManager memory_manager;
osThreadId tcpip_thread_id;
rtos::Mutex adaptation;
};

#endif /* LWIPSTACK_H_ */
12 changes: 12 additions & 0 deletions features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/lwip_sys_arch.c
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,18 @@ void sys_msleep(u32_t ms) {
osDelay(ms);
}

osThreadId_t lwip_tcpip_thread_id = 0;

void sys_tcpip_thread_set(void)
{
lwip_tcpip_thread_id = osThreadGetId();
}

bool sys_tcpip_thread_check(void)
{
return osThreadGetId() == lwip_tcpip_thread_id;
}

// Keep a pool of thread structures
static int thread_pool_index = 0;
static sys_thread_data_t thread_pool[SYS_THREAD_POOL_N];
Expand Down
3 changes: 3 additions & 0 deletions features/FEATURE_LWIP/lwip-interface/lwip-sys/arch/sys_arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ typedef sys_thread_data_t* sys_thread_t;
// === PROTECTION ===
typedef int sys_prot_t;

void sys_tcpip_thread_set(void);
bool sys_tcpip_thread_check(void);

#else
#ifdef __cplusplus
extern "C" {
Expand Down
Loading