/* Copyright (c) 2017 ARM Limited * * 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. */ #define __STDC_LIMIT_MACROS #include "nsapi.h" #include "mbed_interface.h" #include "mbed_assert.h" #include #include #include #include #include #include "lwip/opt.h" #include "lwip/api.h" #include "lwip/inet.h" #include "lwip/netif.h" #include "lwip/dhcp.h" #include "lwip/tcpip.h" #include "lwip/tcp.h" #include "lwip/ip.h" #include "lwip/mld6.h" #include "lwip/dns.h" #include "lwip/udp.h" #include "ppp_lwip.h" #include "LWIPStack.h" LWIP::Interface *LWIP::Interface::list; LWIP::Interface *LWIP::Interface::our_if_from_netif(struct netif *netif) { for (Interface *interface = list; interface; interface = interface->next) { if (netif == &interface->netif) { return interface; } } return NULL; } static void add_dns_addr_to_dns_list_index(const u8_t addr_type, const u8_t index, struct netif *netif) { #if LWIP_IPV6 if (addr_type == IPADDR_TYPE_V6) { /* 2001:4860:4860::8888 google */ ip_addr_t ipv6_dns_addr = IPADDR6_INIT( PP_HTONL(0x20014860UL), PP_HTONL(0x48600000UL), PP_HTONL(0x00000000UL), PP_HTONL(0x00008888UL)); dns_setserver(index, &ipv6_dns_addr, netif); } #endif #if LWIP_IPV4 if (addr_type == IPADDR_TYPE_V4) { /* 8.8.8.8 google */ ip_addr_t ipv4_dns_addr = IPADDR4_INIT(0x08080808); dns_setserver(index, &ipv4_dns_addr, netif); } #endif } static int get_ip_addr_type(const ip_addr_t *ip_addr) { #if LWIP_IPV6 if (IP_IS_V6(ip_addr)) { return IPADDR_TYPE_V6; } #endif #if LWIP_IPV4 if (IP_IS_V4(ip_addr)) { return IPADDR_TYPE_V4; } #endif #if LWIP_IPV6 && LWIP_IPV4 return IPADDR_TYPE_ANY; #endif } void LWIP::add_dns_addr(struct netif *lwip_netif, const char *interface_name) { if (!netif_check_default(lwip_netif)) { interface_name = NULL; } // Check for existing dns address for (char numdns = 0; numdns < DNS_MAX_SERVERS; numdns++) { const ip_addr_t *dns_ip_addr = dns_getserver(numdns, interface_name); if (!ip_addr_isany(dns_ip_addr)) { return; } } // Get preferred ip version const ip_addr_t *ip_addr = get_ip_addr(false, lwip_netif); u8_t addr_type = IPADDR_TYPE_ANY; // Add preferred ip version dns address to index 0 if (ip_addr) { addr_type = get_ip_addr_type(ip_addr); add_dns_addr_to_dns_list_index(addr_type, 0, lwip_netif); } #if LWIP_IPV4 && LWIP_IPV6 if (!ip_addr) { // Get address for any ip version ip_addr = get_ip_addr(true, lwip_netif); if (!ip_addr) { return; } addr_type = get_ip_addr_type(ip_addr); // Add the dns address to index 0 add_dns_addr_to_dns_list_index(addr_type, 0, lwip_netif); } if (addr_type == IPADDR_TYPE_V4) { // If ipv4 is preferred and ipv6 is available add ipv6 dns address to index 1 ip_addr = get_ipv6_addr(lwip_netif); } else if (addr_type == IPADDR_TYPE_V6) { // If ipv6 is preferred and ipv4 is available add ipv4 dns address to index 1 ip_addr = get_ipv4_addr(lwip_netif); } else { ip_addr = NULL; } if (ip_addr) { addr_type = get_ip_addr_type(ip_addr); add_dns_addr_to_dns_list_index(addr_type, 1, lwip_netif); } #endif } nsapi_error_t LWIP::Interface::set_dhcp() { netif_set_up(&netif); #if LWIP_DHCP if (dhcp_has_to_be_set) { err_t err = dhcp_start(&netif); dhcp_has_to_be_set = false; if (err) { connected = NSAPI_STATUS_DISCONNECTED; if (client_callback) { client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED); } return NSAPI_ERROR_DHCP_FAILURE; } dhcp_started = true; } #endif return NSAPI_ERROR_OK; } void LWIP::Interface::netif_link_irq(struct netif *netif) { LWIP::Interface *interface = our_if_from_netif(netif); nsapi_connection_status_t connectedStatusPrev = interface->connected; if (netif_is_link_up(&interface->netif) && interface->connected == NSAPI_STATUS_CONNECTING) { nsapi_error_t dhcp_status = interface->set_dhcp(); if (interface->blocking && dhcp_status == NSAPI_ERROR_OK) { osSemaphoreRelease(interface->linked); } else if (dhcp_status != NSAPI_ERROR_OK) { netif_set_down(&interface->netif); } } else { osSemaphoreRelease(interface->unlinked); if (netif_is_up(&interface->netif)) { interface->connected = NSAPI_STATUS_CONNECTING; } netif_set_down(&interface->netif); } if (interface->client_callback && connectedStatusPrev != interface->connected && interface->connected != NSAPI_STATUS_GLOBAL_UP /* advertised by netif_status_irq */ && interface->connected != NSAPI_STATUS_DISCONNECTED) { /* advertised by bring_down */ interface->client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, interface->connected); } } void LWIP::Interface::netif_status_irq(struct netif *netif) { LWIP::Interface *interface = our_if_from_netif(netif); nsapi_connection_status_t connectedStatusPrev = interface->connected; if (netif_is_up(&interface->netif) && netif_is_link_up(&interface->netif)) { bool dns_addr_has_to_be_added = false; if (!(interface->has_addr_state & HAS_ANY_ADDR) && LWIP::get_ip_addr(true, netif)) { if (interface->blocking) { osSemaphoreRelease(interface->has_any_addr); } interface->has_addr_state |= HAS_ANY_ADDR; dns_addr_has_to_be_added = true; } #if PREF_ADDR_TIMEOUT if (!(interface->has_addr_state & HAS_PREF_ADDR) && LWIP::get_ip_addr(false, netif)) { if (interface->blocking) { osSemaphoreRelease(interface->has_pref_addr); } interface->has_addr_state |= HAS_PREF_ADDR; dns_addr_has_to_be_added = true; } #endif #if BOTH_ADDR_TIMEOUT if (!(interface->has_addr_state & HAS_BOTH_ADDR) && LWIP::get_ipv4_addr(netif) && LWIP::get_ipv6_addr(netif)) { if (interface->blocking) { osSemaphoreRelease(interface->has_both_addr); } interface->has_addr_state |= HAS_BOTH_ADDR; dns_addr_has_to_be_added = true; } #endif if (dns_addr_has_to_be_added && !interface->blocking) { add_dns_addr(&interface->netif, interface->get_interface_name(interface->_interface_name)); } if (interface->has_addr_state & HAS_ANY_ADDR) { interface->connected = NSAPI_STATUS_GLOBAL_UP; } } else if (!netif_is_up(&interface->netif) && netif_is_link_up(&interface->netif)) { interface->connected = NSAPI_STATUS_DISCONNECTED; } if (interface->client_callback && (connectedStatusPrev != interface->connected) && interface->connected != NSAPI_STATUS_DISCONNECTED) { /* advertised by bring_down */ interface->client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, interface->connected); } } void LWIP::Interface::attach(mbed::Callback status_cb) { client_callback = status_cb; } nsapi_connection_status_t LWIP::Interface::get_connection_status() const { return connected; } #if LWIP_IPV6 static void mbed_lwip_clear_ipv6_addresses(struct netif *netif) { for (u8_t i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID); } } #endif char *LWIP::Interface::get_mac_address(char *buf, nsapi_size_t buflen) { (void) snprintf(buf, buflen, "%02x:%02x:%02x:%02x:%02x:%02x", netif.hwaddr[0], netif.hwaddr[1], netif.hwaddr[2], netif.hwaddr[3], netif.hwaddr[4], netif.hwaddr[5]); return buf; } char *LWIP::Interface::get_interface_name(char *buf) { sprintf(buf, "%c%c%d", netif.name[0], netif.name[1], netif.num); return buf; } char *LWIP::Interface::get_ip_address(char *buf, nsapi_size_t buflen) { const ip_addr_t *addr = LWIP::get_ip_addr(true, &netif); if (!addr) { return NULL; } #if LWIP_IPV6 if (IP_IS_V6(addr)) { return ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen); } #endif #if LWIP_IPV4 if (IP_IS_V4(addr)) { return ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen); } #endif #if LWIP_IPV6 && LWIP_IPV4 return NULL; #endif } char *LWIP::Interface::get_ip_address_if(char *buf, nsapi_size_t buflen, const char *interface_name) { const ip_addr_t *addr; if (interface_name == NULL) { addr = LWIP::get_ip_addr(true, &netif); } else { addr = LWIP::get_ip_addr(true, netif_find(interface_name)); } if (!addr) { return NULL; } #if LWIP_IPV6 if (IP_IS_V6(addr)) { return ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen); } #endif #if LWIP_IPV4 if (IP_IS_V4(addr)) { return ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen); } #endif #if LWIP_IPV6 && LWIP_IPV4 return NULL; #endif } char *LWIP::Interface::get_netmask(char *buf, nsapi_size_t buflen) { #if LWIP_IPV4 const ip4_addr_t *addr = netif_ip4_netmask(&netif); if (!ip4_addr_isany(addr)) { return ip4addr_ntoa_r(addr, buf, buflen); } else { return NULL; } #else return NULL; #endif } char *LWIP::Interface::get_gateway(char *buf, nsapi_size_t buflen) { #if LWIP_IPV4 const ip4_addr_t *addr = netif_ip4_gw(&netif); if (!ip4_addr_isany(addr)) { return ip4addr_ntoa_r(addr, buf, buflen); } else { return NULL; } #else return NULL; #endif } LWIP::Interface::Interface() : hw(NULL), has_addr_state(0), connected(NSAPI_STATUS_DISCONNECTED), dhcp_started(false), dhcp_has_to_be_set(false), blocking(true), ppp(false) { memset(&netif, 0, sizeof netif); osSemaphoreAttr_t attr; attr.name = NULL; attr.attr_bits = 0; attr.cb_mem = &linked_sem; attr.cb_size = sizeof linked_sem; linked = osSemaphoreNew(UINT16_MAX, 0, &attr); attr.cb_mem = &unlinked_sem; attr.cb_size = sizeof unlinked_sem; unlinked = osSemaphoreNew(UINT16_MAX, 0, &attr); attr.cb_mem = &has_any_addr_sem; attr.cb_size = sizeof has_any_addr_sem; has_any_addr = osSemaphoreNew(UINT16_MAX, 0, &attr); #if PREF_ADDR_TIMEOUT attr.cb_mem = &has_pref_addr_sem; attr.cb_size = sizeof has_pref_addr_sem; has_pref_addr = osSemaphoreNew(UINT16_MAX, 0, &attr); #endif #if BOTH_ADDR_TIMEOUT attr.cb_mem = &has_both_addr_sem; attr.cb_size = sizeof has_both_addr_sem; has_both_addr = osSemaphoreNew(UINT16_MAX, 0, &attr); #endif next = list; list = this; } nsapi_error_t LWIP::add_ethernet_interface(EMAC &emac, bool default_if, OnboardNetworkStack::Interface **interface_out) { #if LWIP_ETHERNET Interface *interface = new (std::nothrow) Interface(); if (!interface) { return NSAPI_ERROR_NO_MEMORY; } interface->emac = &emac; interface->memory_manager = &memory_manager; interface->ppp = false; #if (MBED_MAC_ADDRESS_SUM != MBED_MAC_ADDR_INTERFACE) netif->interface.hwaddr[0] = MBED_MAC_ADDR_0; netif->interface.hwaddr[1] = MBED_MAC_ADDR_1; netif->interface.hwaddr[2] = MBED_MAC_ADDR_2; netif->interface.hwaddr[3] = MBED_MAC_ADDR_3; netif->interface.hwaddr[4] = MBED_MAC_ADDR_4; netif->interface.hwaddr[5] = MBED_MAC_ADDR_5; #else mbed_mac_address((char *) interface->netif.hwaddr); #endif interface->netif.hwaddr_len = 6; if (!netif_add(&interface->netif, #if LWIP_IPV4 0, 0, 0, #endif interface, &LWIP::Interface::emac_if_init, tcpip_input)) { return NSAPI_ERROR_DEVICE_ERROR; } if (default_if) { netif_set_default(&interface->netif); default_interface = interface; } netif_set_link_callback(&interface->netif, &LWIP::Interface::netif_link_irq); netif_set_status_callback(&interface->netif, &LWIP::Interface::netif_status_irq); *interface_out = interface; /* Use mac address as additional seed to random number generator */ uint64_t seed = interface->netif.hwaddr[0]; for (uint8_t i = 1; i < 8; i++) { seed <<= 8; seed |= interface->netif.hwaddr[i % 6]; } lwip_add_random_seed(seed); return NSAPI_ERROR_OK; #else return NSAPI_ERROR_UNSUPPORTED; #endif //LWIP_ETHERNET } nsapi_error_t LWIP::add_l3ip_interface(L3IP &l3ip, bool default_if, OnboardNetworkStack::Interface **interface_out) { #if LWIP_L3IP Interface *interface = new (std::nothrow) Interface(); if (!interface) { return NSAPI_ERROR_NO_MEMORY; } interface->l3ip = &l3ip; interface->memory_manager = &memory_manager; interface->ppp = false; // interface->netif.hwaddr_len = 0; should we set? if (!netif_add(&interface->netif, #if LWIP_IPV4 0, 0, 0, #endif interface, &LWIP::Interface::l3ip_if_init, ip_input)) { return NSAPI_ERROR_DEVICE_ERROR; } if (default_if) { netif_set_default(&interface->netif); default_interface = interface; } netif_set_link_callback(&interface->netif, &LWIP::Interface::netif_link_irq); netif_set_status_callback(&interface->netif, &LWIP::Interface::netif_status_irq); *interface_out = interface; //lwip_add_random_seed(seed); to do? return NSAPI_ERROR_OK; #else return NSAPI_ERROR_UNSUPPORTED; #endif //LWIP_L3IP } nsapi_error_t LWIP::remove_l3ip_interface(OnboardNetworkStack::Interface **interface_out) { #if LWIP_L3IP if ((interface_out != NULL) && (*interface_out != NULL)) { Interface *lwip = static_cast(*interface_out); Interface *node = lwip->list; if (lwip->list != NULL) { if (lwip->list == lwip) { lwip->list = lwip->list->next; netif_remove(&node->netif); delete node; } else { while (node->next != NULL && node->next != lwip) { node = node->next; } if (node->next != NULL && node->next == lwip) { Interface *remove = node->next; node->next = node->next->next; remove->l3ip->power_down(); netif_remove(&remove->netif); delete remove; } } } } return NSAPI_ERROR_OK; #else return NSAPI_ERROR_UNSUPPORTED; #endif //LWIP_L3IP } /* Internal API to preserve existing PPP functionality - revise to better match mbed_ipstak_add_ethernet_interface later */ nsapi_error_t LWIP::_add_ppp_interface(void *hw, bool default_if, nsapi_ip_stack_t stack, LWIP::Interface **interface_out) { #if LWIP_PPP_API Interface *interface = new (std::nothrow) Interface(); if (!interface) { return NSAPI_ERROR_NO_MEMORY; } interface->hw = hw; interface->ppp = true; nsapi_error_t ret = ppp_lwip_if_init(hw, &interface->netif, stack); if (ret != NSAPI_ERROR_OK) { free(interface); return ret; } if (default_if) { netif_set_default(&interface->netif); default_interface = interface; } netif_set_link_callback(&interface->netif, &LWIP::Interface::netif_link_irq); netif_set_status_callback(&interface->netif, &LWIP::Interface::netif_status_irq); *interface_out = interface; return NSAPI_ERROR_OK; #else return NSAPI_ERROR_UNSUPPORTED; #endif //LWIP_PPP_API } void LWIP::set_default_interface(OnboardNetworkStack::Interface *interface) { if (interface) { default_interface = static_cast(interface); netif_set_default(&default_interface->netif); } } nsapi_error_t LWIP::Interface::bringup(bool dhcp, const char *ip, const char *netmask, const char *gw, const nsapi_ip_stack_t stack, bool block) { // Check if we've already connected if (connected == NSAPI_STATUS_GLOBAL_UP) { return NSAPI_ERROR_IS_CONNECTED; } else if (connected == NSAPI_STATUS_CONNECTING) { return NSAPI_ERROR_BUSY; } connected = NSAPI_STATUS_CONNECTING; blocking = block; #if LWIP_DHCP if (stack != IPV6_STACK && dhcp) { dhcp_has_to_be_set = true; } #endif #if LWIP_IPV6 if (stack != IPV4_STACK) { if (netif.hwaddr_len == 6) { netif_create_ip6_linklocal_address(&netif, 1/*from MAC*/); } #if LWIP_IPV6_MLD /* * For hardware/netifs that implement MAC filtering. * All-nodes link-local is handled by default, so we must let the hardware know * to allow multicast packets in. * Should set mld_mac_filter previously. */ if (netif.mld_mac_filter != NULL) { ip6_addr_t ip6_allnodes_ll; ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll); netif.mld_mac_filter(&netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER); } #endif /* LWIP_IPV6_MLD */ #if LWIP_IPV6_AUTOCONFIG /* IPv6 address autoconfiguration not enabled by default */ netif.ip6_autoconfig_enabled = 1; #endif /* LWIP_IPV6_AUTOCONFIG */ } else { // Disable rourter solicitations netif.rs_count = 0; } #endif /* LWIP_IPV6 */ #if LWIP_IPV4 if (stack != IPV6_STACK) { if (!dhcp && !ppp) { ip4_addr_t ip_addr; ip4_addr_t netmask_addr; ip4_addr_t gw_addr; if (!inet_aton(ip, &ip_addr) || !inet_aton(netmask, &netmask_addr) || !inet_aton(gw, &gw_addr)) { return NSAPI_ERROR_PARAMETER; } netif_set_addr(&netif, &ip_addr, &netmask_addr, &gw_addr); } } #endif if (client_callback) { client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_CONNECTING); } if (ppp) { err_t err = ppp_lwip_connect(hw); if (err) { connected = NSAPI_STATUS_DISCONNECTED; if (client_callback) { client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED); } return err_remap(err); } } if (!netif_is_link_up(&netif)) { if (blocking) { if (osSemaphoreAcquire(linked, 15000) != osOK) { if (ppp) { (void) ppp_lwip_disconnect(hw); } return NSAPI_ERROR_NO_CONNECTION; } } } else { nsapi_error_t ret = set_dhcp(); if (ret != NSAPI_ERROR_OK) { return ret; } } if (!blocking) { // Done enough - as addresses are acquired, there will be // connected callbacks. return NSAPI_ERROR_OK; } // If doesn't have address if (!LWIP::get_ip_addr(true, &netif)) { if (osSemaphoreAcquire(has_any_addr, DHCP_TIMEOUT * 1000) != osOK) { if (ppp) { (void) ppp_lwip_disconnect(hw); } return NSAPI_ERROR_DHCP_FAILURE; } } #if PREF_ADDR_TIMEOUT if (stack != IPV4_STACK && stack != IPV6_STACK) { // If address is not for preferred stack waits a while to see // if preferred stack address is acquired if (!LWIP::get_ip_addr(false, &netif)) { osSemaphoreAcquire(has_pref_addr, PREF_ADDR_TIMEOUT * 1000); } } #endif #if BOTH_ADDR_TIMEOUT if (stack != IPV4_STACK && stack != IPV6_STACK) { // If addresses for both stacks are not available waits a while to // see if address for both stacks are acquired if (!(LWIP::get_ipv4_addr(&netif) && LWIP::get_ipv6_addr(&netif))) { osSemaphoreAcquire(has_both_addr, BOTH_ADDR_TIMEOUT * 1000); } } #endif add_dns_addr(&netif, get_interface_name(_interface_name)); return NSAPI_ERROR_OK; } nsapi_error_t LWIP::Interface::bringdown() { // Check if we've connected if (connected == NSAPI_STATUS_DISCONNECTED) { return NSAPI_ERROR_NO_CONNECTION; } #if LWIP_DHCP // Disconnect from the network if (dhcp_started) { dhcp_release(&netif); dhcp_stop(&netif); dhcp_started = false; dhcp_has_to_be_set = false; } #endif if (ppp) { /* this is a blocking call, returns when PPP is properly closed */ err_t err = ppp_lwip_disconnect(hw); if (err) { return err_remap(err); } MBED_ASSERT(!netif_is_link_up(&netif)); /*if (netif_is_link_up(&netif)) { if (sys_arch_sem_wait(&unlinked, 15000) == SYS_ARCH_TIMEOUT) { return NSAPI_ERROR_DEVICE_ERROR; } }*/ } else { netif_set_down(&netif); } #if LWIP_IPV6 mbed_lwip_clear_ipv6_addresses(&netif); #endif #if LWIP_IPV4 ip_addr_set_zero(&(netif.ip_addr)); ip_addr_set_zero(&(netif.netmask)); ip_addr_set_zero(&(netif.gw)); #endif osSemaphoreDelete(has_any_addr); osSemaphoreAttr_t attr; attr.name = NULL; attr.attr_bits = 0; attr.cb_mem = &has_any_addr_sem; attr.cb_size = sizeof has_any_addr_sem; has_any_addr = osSemaphoreNew(UINT16_MAX, 0, &attr); #if PREF_ADDR_TIMEOUT osSemaphoreDelete(has_pref_addr); attr.cb_mem = &has_pref_addr_sem; attr.cb_size = sizeof has_pref_addr_sem; has_pref_addr = osSemaphoreNew(UINT16_MAX, 0, &attr); #endif #if BOTH_ADDR_TIMEOUT osSemaphoreDelete(has_both_addr); attr.cb_mem = &has_both_addr_sem; attr.cb_size = sizeof has_both_addr_sem; has_both_addr = osSemaphoreNew(UINT16_MAX, 0, &attr); #endif has_addr_state = 0; connected = NSAPI_STATUS_DISCONNECTED; if (client_callback) { client_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, connected); } return 0; }