Instead of always allocating both IPv6 and IPv4 address information to every network interface, allow more fine grained address configuration. So it is possible to have IPv6 or IPv4 only network interfaces. This commit introduces two new config options: CONFIG_NET_IF_MAX_IPV4_COUNT and CONFIG_NET_IF_MAX_IPV6_COUNT which tell how many IP address information structs are allocated statically. At runtime when network interface is setup, it is then possible to attach this IP address info struct to a specific network interface. This can save considerable amount of memory as the IP address information struct can be quite large (depends on how many IP addresses user configures in the system). Note that the value of CONFIG_NET_IF_MAX_IPV4_COUNT and CONFIG_NET_IF_MAX_IPV6_COUNT should reflect the estimated number of network interfaces in the system. So if if CONFIG_NET_IF_MAX_IPV6_COUNT is set to 1 and there are two network interfaces that need IPv6 addresses, then the system will not be able to setup IPv6 addresses to the second network interface in this case. This scenario might be just fine if the second network interface is IPv4 only. The net_if.c will print a warning during startup if mismatch about the counts and the actual number of network interface is detected. Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
399 lines
9.4 KiB
C
399 lines
9.4 KiB
C
/* init.c */
|
|
|
|
/*
|
|
* Copyright (c) 2017 Intel Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#if defined(CONFIG_NET_DEBUG_APP)
|
|
#define SYS_LOG_DOMAIN "net/app"
|
|
#define NET_SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
|
|
#define NET_LOG_ENABLED 1
|
|
#endif
|
|
|
|
#include <zephyr.h>
|
|
#include <init.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <net/net_core.h>
|
|
#include <net/net_ip.h>
|
|
#include <net/net_if.h>
|
|
#include <net/dhcpv4.h>
|
|
#include <net/net_mgmt.h>
|
|
#include <net/dns_resolve.h>
|
|
|
|
#include <net/net_app.h>
|
|
|
|
#include "ieee802154_settings.h"
|
|
#include "bt_settings.h"
|
|
|
|
static K_SEM_DEFINE(waiter, 0, 1);
|
|
static struct k_sem counter;
|
|
|
|
#if defined(CONFIG_NET_DHCPV4)
|
|
static struct net_mgmt_event_callback mgmt4_cb;
|
|
|
|
static void ipv4_addr_add_handler(struct net_mgmt_event_callback *cb,
|
|
u32_t mgmt_event,
|
|
struct net_if *iface)
|
|
{
|
|
#if defined(CONFIG_NET_DEBUG_APP) && CONFIG_SYS_LOG_NET_LEVEL > 2
|
|
char hr_addr[NET_IPV4_ADDR_LEN];
|
|
#endif
|
|
int i;
|
|
|
|
if (mgmt_event != NET_EVENT_IPV4_ADDR_ADD) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) {
|
|
struct net_if_addr *if_addr =
|
|
&iface->config.ip.ipv4->unicast[i];
|
|
|
|
if (if_addr->addr_type != NET_ADDR_DHCP || !if_addr->is_used) {
|
|
continue;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_DEBUG_APP) && CONFIG_SYS_LOG_NET_LEVEL > 2
|
|
NET_INFO("IPv4 address: %s",
|
|
net_addr_ntop(AF_INET, &if_addr->address.in_addr,
|
|
hr_addr, NET_IPV4_ADDR_LEN));
|
|
NET_INFO("Lease time: %u seconds",
|
|
iface->config.dhcpv4.lease_time);
|
|
NET_INFO("Subnet: %s",
|
|
net_addr_ntop(AF_INET,
|
|
&iface->config.ip.ipv4->netmask,
|
|
hr_addr, NET_IPV4_ADDR_LEN));
|
|
NET_INFO("Router: %s",
|
|
net_addr_ntop(AF_INET, &iface->config.ip.ipv4->gw,
|
|
hr_addr, NET_IPV4_ADDR_LEN));
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
k_sem_take(&counter, K_NO_WAIT);
|
|
k_sem_give(&waiter);
|
|
}
|
|
|
|
static void setup_dhcpv4(struct net_if *iface)
|
|
{
|
|
NET_INFO("Running dhcpv4 client...");
|
|
|
|
net_mgmt_init_event_callback(&mgmt4_cb, ipv4_addr_add_handler,
|
|
NET_EVENT_IPV4_ADDR_ADD);
|
|
net_mgmt_add_event_callback(&mgmt4_cb);
|
|
|
|
net_dhcpv4_start(iface);
|
|
}
|
|
|
|
#else
|
|
#define setup_dhcpv4(...)
|
|
#endif /* CONFIG_NET_DHCPV4 */
|
|
|
|
#if defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_DHCPV4) && \
|
|
!defined(CONFIG_NET_APP_MY_IPV4_ADDR)
|
|
#error "You need to define an IPv4 address or enable DHCPv4!"
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_APP_MY_IPV4_ADDR)
|
|
|
|
static void setup_ipv4(struct net_if *iface)
|
|
{
|
|
#if defined(CONFIG_NET_DEBUG_APP) && CONFIG_SYS_LOG_NET_LEVEL > 2
|
|
char hr_addr[NET_IPV4_ADDR_LEN];
|
|
#endif
|
|
struct in_addr addr;
|
|
|
|
if (sizeof(CONFIG_NET_APP_MY_IPV4_ADDR) == 1) {
|
|
/* Empty address, skip setting ANY address in this case */
|
|
return;
|
|
}
|
|
|
|
if (net_addr_pton(AF_INET, CONFIG_NET_APP_MY_IPV4_ADDR, &addr)) {
|
|
NET_ERR("Invalid address: %s", CONFIG_NET_APP_MY_IPV4_ADDR);
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_DHCPV4)
|
|
/* In case DHCP is enabled, make the static address tentative,
|
|
* to allow DHCP address to override it. This covers a usecase
|
|
* of "there should be a static IP address for DHCP-less setups",
|
|
* but DHCP should override it (to use it, NET_IF_MAX_IPV4_ADDR
|
|
* should be set to 1). There is another usecase: "there should
|
|
* always be static IP address, and optionally, DHCP address".
|
|
* For that to work, NET_IF_MAX_IPV4_ADDR should be 2 (or more).
|
|
* (In this case, an app will need to bind to the needed addr
|
|
* explicitly.)
|
|
*/
|
|
net_if_ipv4_addr_add(iface, &addr, NET_ADDR_OVERRIDABLE, 0);
|
|
#else
|
|
net_if_ipv4_addr_add(iface, &addr, NET_ADDR_MANUAL, 0);
|
|
#endif
|
|
|
|
#if defined(CONFIG_NET_DEBUG_APP) && CONFIG_SYS_LOG_NET_LEVEL > 2
|
|
NET_INFO("IPv4 address: %s",
|
|
net_addr_ntop(AF_INET, &addr, hr_addr, NET_IPV4_ADDR_LEN));
|
|
#endif
|
|
|
|
if (sizeof(CONFIG_NET_APP_MY_IPV4_NETMASK) > 1) {
|
|
/* If not empty */
|
|
if (net_addr_pton(AF_INET, CONFIG_NET_APP_MY_IPV4_NETMASK,
|
|
&addr)) {
|
|
NET_ERR("Invalid netmask: %s",
|
|
CONFIG_NET_APP_MY_IPV4_NETMASK);
|
|
} else {
|
|
net_if_ipv4_set_netmask(iface, &addr);
|
|
}
|
|
}
|
|
|
|
if (sizeof(CONFIG_NET_APP_MY_IPV4_GW) > 1) {
|
|
/* If not empty */
|
|
if (net_addr_pton(AF_INET, CONFIG_NET_APP_MY_IPV4_GW,
|
|
&addr)) {
|
|
NET_ERR("Invalid gateway: %s",
|
|
CONFIG_NET_APP_MY_IPV4_GW);
|
|
} else {
|
|
net_if_ipv4_set_gw(iface, &addr);
|
|
}
|
|
}
|
|
|
|
k_sem_take(&counter, K_NO_WAIT);
|
|
k_sem_give(&waiter);
|
|
}
|
|
|
|
#else
|
|
#define setup_ipv4(...)
|
|
#endif /* CONFIG_NET_IPV4 && !CONFIG_NET_DHCPV4 */
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
#if !defined(CONFIG_NET_APP_MY_IPV6_ADDR)
|
|
#error "You need to define an IPv6 address!"
|
|
#endif
|
|
|
|
static struct net_mgmt_event_callback mgmt6_cb;
|
|
static struct in6_addr laddr;
|
|
|
|
static void ipv6_event_handler(struct net_mgmt_event_callback *cb,
|
|
u32_t mgmt_event, struct net_if *iface)
|
|
{
|
|
struct net_if_ipv6 *ipv6 = iface->config.ip.ipv6;
|
|
int i;
|
|
|
|
if (!ipv6) {
|
|
return;
|
|
}
|
|
|
|
if (mgmt_event == NET_EVENT_IPV6_ADDR_ADD) {
|
|
/* save the last added IP address for this interface */
|
|
for (i = NET_IF_MAX_IPV6_ADDR - 1; i >= 0; i--) {
|
|
if (ipv6->unicast[i].is_used) {
|
|
memcpy(&laddr,
|
|
&ipv6->unicast[i].address.in6_addr,
|
|
sizeof(laddr));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mgmt_event == NET_EVENT_IPV6_DAD_SUCCEED) {
|
|
#if defined(CONFIG_NET_DEBUG_APP) && CONFIG_SYS_LOG_NET_LEVEL > 2
|
|
char hr_addr[NET_IPV6_ADDR_LEN];
|
|
#endif
|
|
struct net_if_addr *ifaddr;
|
|
|
|
ifaddr = net_if_ipv6_addr_lookup(&laddr, &iface);
|
|
if (!ifaddr ||
|
|
!(net_ipv6_addr_cmp(&ifaddr->address.in6_addr, &laddr) &&
|
|
ifaddr->addr_state == NET_ADDR_PREFERRED)) {
|
|
/* Address is not yet properly setup */
|
|
return;
|
|
}
|
|
|
|
#if defined(CONFIG_NET_DEBUG_APP) && CONFIG_SYS_LOG_NET_LEVEL > 2
|
|
NET_INFO("IPv6 address: %s",
|
|
net_addr_ntop(AF_INET6, &laddr, hr_addr,
|
|
NET_IPV6_ADDR_LEN));
|
|
#endif
|
|
|
|
k_sem_take(&counter, K_NO_WAIT);
|
|
k_sem_give(&waiter);
|
|
}
|
|
|
|
if (mgmt_event == NET_EVENT_IPV6_ROUTER_ADD) {
|
|
k_sem_take(&counter, K_NO_WAIT);
|
|
k_sem_give(&waiter);
|
|
}
|
|
}
|
|
|
|
static void setup_ipv6(struct net_if *iface, u32_t flags)
|
|
{
|
|
struct net_if_addr *ifaddr;
|
|
u32_t mask = NET_EVENT_IPV6_DAD_SUCCEED;
|
|
|
|
if (sizeof(CONFIG_NET_APP_MY_IPV6_ADDR) == 1) {
|
|
/* Empty address, skip setting ANY address in this case */
|
|
return;
|
|
}
|
|
|
|
if (net_addr_pton(AF_INET6, CONFIG_NET_APP_MY_IPV6_ADDR, &laddr)) {
|
|
NET_ERR("Invalid address: %s", CONFIG_NET_APP_MY_IPV6_ADDR);
|
|
/* some interfaces may add IP address later */
|
|
mask |= NET_EVENT_IPV6_ADDR_ADD;
|
|
}
|
|
|
|
if (flags & NET_APP_NEED_ROUTER) {
|
|
mask |= NET_EVENT_IPV6_ROUTER_ADD;
|
|
}
|
|
|
|
net_mgmt_init_event_callback(&mgmt6_cb, ipv6_event_handler, mask);
|
|
net_mgmt_add_event_callback(&mgmt6_cb);
|
|
|
|
/*
|
|
* check for CMD_ADDR_ADD bit here, NET_EVENT_IPV6_ADDR_ADD is
|
|
* a combination of _NET_EVENT_IPV6_BASE | NET_EVENT_IPV6_CMD_ADDR_ADD
|
|
* so it will always return != NET_EVENT_IPV6_CMD_ADDR_ADD if any other
|
|
* event is set (for instance NET_EVENT_IPV6_ROUTER_ADD)
|
|
*/
|
|
if ((mask & NET_EVENT_IPV6_CMD_ADDR_ADD) ==
|
|
NET_EVENT_IPV6_CMD_ADDR_ADD) {
|
|
ifaddr = net_if_ipv6_addr_add(iface, &laddr,
|
|
NET_ADDR_MANUAL, 0);
|
|
if (!ifaddr) {
|
|
NET_ERR("Cannot add %s to interface",
|
|
CONFIG_NET_APP_MY_IPV6_ADDR);
|
|
}
|
|
}
|
|
|
|
#if !defined(CONFIG_NET_IPV6_DAD)
|
|
k_sem_take(&counter, K_NO_WAIT);
|
|
k_sem_give(&waiter);
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
#else
|
|
#define setup_ipv6(...)
|
|
#endif /* CONFIG_NET_IPV6 */
|
|
|
|
int net_app_init(const char *app_info, u32_t flags, s32_t timeout)
|
|
{
|
|
#define LOOP_DIVIDER 10
|
|
struct net_if *iface = net_if_get_default();
|
|
int loop = timeout / LOOP_DIVIDER;
|
|
int count = 0;
|
|
|
|
if (app_info) {
|
|
NET_INFO("%s", app_info);
|
|
}
|
|
|
|
if (!iface) {
|
|
NET_ERR("No network interfaces");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (flags & NET_APP_NEED_IPV6) {
|
|
count++;
|
|
}
|
|
|
|
if (flags & NET_APP_NEED_IPV4) {
|
|
count++;
|
|
}
|
|
|
|
k_sem_init(&counter, count, UINT_MAX);
|
|
|
|
setup_ipv4(iface);
|
|
|
|
setup_dhcpv4(iface);
|
|
|
|
setup_ipv6(iface, flags);
|
|
|
|
if (timeout < 0) {
|
|
count = -1;
|
|
} else if (timeout == 0) {
|
|
count = 0;
|
|
} else {
|
|
count = timeout / 1000 + 1;
|
|
}
|
|
|
|
/* Loop here until we are ready to continue. As we might need
|
|
* to wait multiple events, sleep smaller amounts of data.
|
|
*/
|
|
while (count--) {
|
|
if (k_sem_take(&waiter, loop)) {
|
|
if (!k_sem_count_get(&counter)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!count && timeout) {
|
|
NET_ERR("Timeout while waiting setup");
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* From subsys/logging/sys_log_net.c */
|
|
extern void syslog_net_hook_install(void);
|
|
static inline void syslog_net_init(void)
|
|
{
|
|
#if defined(CONFIG_SYS_LOG_BACKEND_NET)
|
|
syslog_net_hook_install();
|
|
#endif
|
|
}
|
|
|
|
#if defined(CONFIG_NET_APP_AUTO_INIT)
|
|
static int init_net_app(struct device *device)
|
|
{
|
|
u32_t flags = 0;
|
|
int ret;
|
|
|
|
ARG_UNUSED(device);
|
|
|
|
#if defined(CONFIG_NET_IPV6)
|
|
/* IEEE 802.15.4 is only usable if IPv6 is enabled */
|
|
ret = _net_app_ieee802154_setup();
|
|
if (ret < 0) {
|
|
NET_ERR("Cannot setup IEEE 802.15.4 interface (%d)", ret);
|
|
}
|
|
|
|
ret = _net_app_bt_setup();
|
|
if (ret < 0) {
|
|
NET_ERR("Cannot setup Bluetooth interface (%d)", ret);
|
|
}
|
|
#endif
|
|
|
|
if (IS_ENABLED(CONFIG_NET_APP_NEED_IPV6)) {
|
|
flags |= NET_APP_NEED_IPV6;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_APP_NEED_IPV6_ROUTER)) {
|
|
flags |= NET_APP_NEED_ROUTER;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_NET_APP_NEED_IPV4)) {
|
|
flags |= NET_APP_NEED_IPV4;
|
|
}
|
|
|
|
/* Initialize the application automatically if needed */
|
|
ret = net_app_init("Initializing network", flags,
|
|
K_SECONDS(CONFIG_NET_APP_INIT_TIMEOUT));
|
|
if (ret < 0) {
|
|
NET_ERR("Network initialization failed (%d)", ret);
|
|
}
|
|
|
|
/* This is activated late as it requires the network stack to be up
|
|
* and running before syslog messages can be sent to network.
|
|
*/
|
|
syslog_net_init();
|
|
|
|
return ret;
|
|
}
|
|
|
|
SYS_INIT(init_net_app, APPLICATION, CONFIG_NET_APP_INIT_PRIO);
|
|
#endif /* CONFIG_NET_APP_AUTO_INIT */
|