diff --git a/cmake/modules/configuration_files.cmake b/cmake/modules/configuration_files.cmake index fe1ff3bf16fa4..91666c2727a94 100644 --- a/cmake/modules/configuration_files.cmake +++ b/cmake/modules/configuration_files.cmake @@ -16,6 +16,7 @@ # - EXTRA_DTC_OVERLAY_FILE List of additional devicetree overlay files # - DTS_EXTRA_CPPFLAGS List of additional devicetree preprocessor defines # - APPLICATION_CONFIG_DIR: Root folder for application configuration +# - NET_INIT_CONFIG_FILE: Name of the network init configuration file # # If any of the above variables are already set when this CMake module is # loaded, then no changes to the variable will happen. @@ -95,5 +96,6 @@ zephyr_boilerplate_watch(DTC_OVERLAY_FILE) zephyr_get(EXTRA_CONF_FILE SYSBUILD LOCAL VAR EXTRA_CONF_FILE OVERLAY_CONFIG MERGE REVERSE) zephyr_get(EXTRA_DTC_OVERLAY_FILE SYSBUILD LOCAL MERGE REVERSE) zephyr_get(DTS_EXTRA_CPPFLAGS SYSBUILD LOCAL MERGE REVERSE) +zephyr_get(NET_INIT_CONFIG_FILE) build_info(application source-dir VALUE ${APPLICATION_SOURCE_DIR}) build_info(application configuration-dir VALUE ${APPLICATION_CONFIG_DIR}) diff --git a/cmake/modules/extensions.cmake b/cmake/modules/extensions.cmake index 8516188618bd2..ea81d3ed64ec1 100644 --- a/cmake/modules/extensions.cmake +++ b/cmake/modules/extensions.cmake @@ -16,6 +16,7 @@ include(CheckCXXCompilerFlag) # 1.2. zephyr_library_* # 1.2.1 zephyr_interface_library_* # 1.3. generate_inc_* +# 1.3.1 generate_config_* # 1.4. board_* # 1.5. Misc. # 2. Kconfig-aware extensions @@ -739,6 +740,79 @@ function(generate_inc_file_for_target generate_inc_file_for_gen_target(${target} ${source_file} ${generated_file} ${generated_target_name} ${ARGN}) endfunction() +# 1.3.1 generate_config_* + +# These functions are needed if a configuration file is generated +# from a user supplied yaml file. +# +function(generate_config_file + source_file # The yaml source file to be converted to config data + generated_file # The generated file + yaml_to_config_script # Script that generates the config + ) + add_custom_command( + OUTPUT ${generated_file} + COMMAND + ${PYTHON_EXECUTABLE} + ${yaml_to_config_script} + ${ARGN} # Extra arguments are passed to the script + < ${source_file} + > ${generated_file} + DEPENDS ${source_file} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) +endfunction() + +function(generate_config_file_for_gen_target + target # The cmake target that depends on the generated file + source_file # The yaml source file to be converted to config data + generated_file # The generated file + gen_target # The generated file target we depend on + yaml_to_config_script # Script that generates the config + # Any additional arguments are passed on to script + ) + + generate_config_file(${source_file} ${generated_file} ${yaml_to_config_script} ${ARGN}) + + # Ensure 'generated_file' is generated before 'target' by creating a + # dependency between the two targets + + add_dependencies(${target} ${gen_target}) +endfunction() + +function(generate_config_file_for_target + target # The cmake target that depends on the generated file + source_file # The yaml source file to be converted to config data + generated_file # The generated file + yaml_to_config_script # Script that generates the config + # Any additional arguments are passed on to script + ) + + # Ensure 'generated_file' is generated before 'target' by creating a + # 'custom_target' for it and setting up a dependency between the two + # targets + + # But first create a unique name for the custom target + generate_unique_target_name_from_filename(${generated_file} generated_target_name) + + add_custom_target(${generated_target_name} DEPENDS ${generated_file}) + generate_config_file_for_gen_target(${target} ${source_file} ${generated_file} + ${generated_target_name} ${yaml_to_config_script} ${ARGN}) +endfunction() + +function(network_generate_config_file_for_target + target # The cmake target that depends on the generated file + source_file # The yaml source file to be converted to config data + ) + + set(generated_file ${ZEPHYR_BINARY_DIR}/include/generated/net_init_config.inc) + set(yaml_to_config_script ${ZEPHYR_BASE}/scripts/net/net-yaml-config.py) + set(yaml_schema_file ${ZEPHYR_BASE}/scripts/schemas/net-configuration-schema.yml) + + generate_config_file_for_target(${target} ${source_file} ${generated_file} + ${yaml_to_config_script} ${yaml_schema_file}) +endfunction() + # 1.4. board_* # # This section is for extensions related to Zephyr board handling. diff --git a/cmake/modules/kernel.cmake b/cmake/modules/kernel.cmake index c6319611c8c35..a765aa36489dc 100644 --- a/cmake/modules/kernel.cmake +++ b/cmake/modules/kernel.cmake @@ -256,3 +256,17 @@ if("${CMAKE_EXTRA_GENERATOR}" STREQUAL "Eclipse CDT4") include(${ZEPHYR_BASE}/cmake/ide/eclipse_cdt4_generator_amendment.cmake) eclipse_cdt4_generator_amendment(1) endif() + +if(CONFIG_NET_CONFIG_SETTINGS) + # If network configuration library is enabled, check if the yaml file + # is present and use it to generate an initial configuration. Otherwise + # use the .config file to generate the initial configuration. + if(NOT NET_INIT_CONFIG_FILE) + set(NET_INIT_CONFIG_FILE ${APPLICATION_SOURCE_DIR}/net-init-config.yaml) + endif() + if(EXISTS ${APPLICATION_SOURCE_DIR}/net-init-config.yaml) + network_generate_config_file_for_target(app ${NET_INIT_CONFIG_FILE}) + else() + network_generate_config_file_for_target(app ${DOTCONFIG}) + endif() +endif() diff --git a/doc/connectivity/networking/index.rst b/doc/connectivity/networking/index.rst index 18e3032978bf3..6564107e7aaa7 100644 --- a/doc/connectivity/networking/index.rst +++ b/doc/connectivity/networking/index.rst @@ -13,6 +13,7 @@ operation of the stacks and how they were implemented. overview.rst net-stack-architecture.rst net_config_guide.rst + net_init_config.rst networking_with_host.rst network_monitoring.rst network_tracing.rst diff --git a/doc/connectivity/networking/net_init_config.rst b/doc/connectivity/networking/net_init_config.rst new file mode 100644 index 0000000000000..a5db105290bfe --- /dev/null +++ b/doc/connectivity/networking/net_init_config.rst @@ -0,0 +1,331 @@ +.. _network_initial_configuration: + +Network Stack Initial Configuration +################################### + +.. contents:: + :local: + :depth: 2 + +This document describes how the network config library can be used to do initial +network stack configuration at runtime when the device boots. The network config +library was mainly developed for testing purposes when a pre-defined setup is +applied to the device when it is starting up. The configuration data is static +and stored in ROM so the device is applied same initial network configuration at +boot. + +The configuration library can be used for example to enable DHCPv4 client at boot, +or setup VLAN tags etc. + +Network Configuration Options +***************************** + +The :kconfig:option:`CONFIG_NET_CONFIG_SETTINGS` enables the network configuration +library. If it is not set, then no network setup is done and the application must +setup network settings itself. + +If the above setting is enabled, then two configuration options can be used to setup +the network stack: + +* Normal Kconfig options from ``CONFIG_NET_CONFIG_*`` branch. + This is the legacy way and only suitable for simpler setups where there is only + one network interface that needs to be setup at boot. + See available Kconfig options in the network API documentation. + +* A yaml configuration file ``net-init-config.yaml``. + This allows user to describe the device hardware setup and what options to set + even when the device have multiple network interfaces. + +The net-init-config.yaml Syntax +******************************* + +The ``net-init-config.yaml`` can be placed to the application directory. When the +application is compiled, this file is used to generate configuration that is then +applied at runtime. The yaml file contents is verified using a schema located in +``scripts/schemas/net-configuration-schema.yaml`` file. + +User can use a different configuration yaml file by setting ``NET_INIT_CONFIG_FILE`` +when calling cmake or west. + +.. code-block:: console + + west build -p -b native_sim myapp -d build -- -DNET_INIT_CONFIG_FILE=my-net-init-config.yaml + +The yaml file contains configuration sections for each network interface in the +system, IEEE 802.15.4 configuration or SNTP configuration. + +Example: + +.. code-block:: yaml + + net_init_config: + network_interfaces: + - &main-interface + name: eth0 + set_name: main-eth0 + set_default: true + flags: + - NET_IF_NO_AUTO_START + ipv6: + status: true + ipv6_addresses: + - 2001:db8:110::1 + ipv6_multicast_addresses: + - ff05::114 + - ff15::115 + prefixes: + - address: "2001:db8::" + len: 64 + lifetime: 1024 + hop_limit: 48 + multicast_hop_limit: 2 + dhcpv6: + status: true + do_request_address: true + do_request_prefix: false + ipv4: + status: true + ipv4_addresses: + - 192.0.2.10/24 + ipv4_multicast_addresses: + - 234.0.0.10 + gateway: 192.0.2.1 + time_to_live: 128 + multicast_time_to_live: 3 + dhcpv4: + status: true + ipv4_autoconf: + status: true + + - &vlan-interface + set_name: vlan0 + bind_to: *main-interface + flags: + - ^NET_IF_PROMISC + vlan: + status: true + tag: 2432 + ipv4: + status: true + dhcpv4: + status: true + + sntp: + status: true + server: sntp.example.com + timeout: 30 + bind_to: *main-interface + +In the above example, there are two network interfaces. One with name ``eth0`` which +is changed to ``main-eth0`` and which is made the default interface. It has both +IPv6 and IPv4 supported. There is also a VLAN interface that is bound to the first +one. Its name is set to ``vlan0`` and it is enabled with tag ``2432``. The VLAN +interface does not have IPv6 enabled, but IPv4 is together with DHCPv4 client support. +Also SNTP is enabled and is using ``sntp.example.com`` server address. The SNTP is +configured to use the first network interface. + +The yaml File Configuration Options +*********************************** + +These options are available for each network configuration domain. + +.. table:: The ``network_interface`` options + :align: left + + +-------------+-------------+-----------------------------------------------------------------+ + | Option name | Type | Description | + +=============+=============+=================================================================+ + | bind_to | reference | Bind this object to another network interface. | + | | | This is useful for example for VLANs or other types of virtual | + | | | interfaces. | + +-------------+-------------+-----------------------------------------------------------------+ + | name | string | Existing name of the network interface. | + | | | This is used to find the interface so that we can apply the | + | | | subsequent configuration to it. | + | | | Either this option or the ``device_name`` option must be given. | + +-------------+-------------+-----------------------------------------------------------------+ + | device_name | string | Name of the device of the network interface. | + | | | Either this or the ``name`` option must be set in the yaml file.| + +-------------+-------------+-----------------------------------------------------------------+ + | set_name | string | New name of the network interface. | + | | | This can be used to change the name of the interface if | + | | | the default name is not suitable. | + +-------------+-------------+-----------------------------------------------------------------+ + | set_default | bool | Set this network interface as default one which will be returned| + | | | by the :c:func:`net_if_get_default` function call. | + +-------------+-------------+-----------------------------------------------------------------+ + | flags | string list | Array of network interface flags that should be applied to this | + | | | interface. See ``net_if_flag`` documentation for descriptions of| + | | | the flags. If the flag starts with ``^`` then the flag value is | + | | | cleared. | + | | | Following flags can be set/cleared: | + | | | ``NET_IF_POINTOPOINT``, ``NET_IF_PROMISC``, | + | | | ``NET_IF_NO_AUTO_START``, ``NET_IF_FORWARD_MULTICASTS``, | + | | | ``NET_IF_IPV6_NO_ND``, ``NET_IF_IPV6_NO_MLD`` | + +-------------+-------------+-----------------------------------------------------------------+ + | ipv6 | struct | IPv6 configuration options. | + +-------------+-------------+-----------------------------------------------------------------+ + | ipv4 | struct | IPv4 configuration options. | + +-------------+-------------+-----------------------------------------------------------------+ + | vlan | struct | VLAN configuration options. | + | | | Only applicable for Ethernet based interfaces. | + +-------------+-------------+-----------------------------------------------------------------+ + +.. table:: The ``ipv6`` options + :align: left + + +--------------------------+-------------+----------------------------------------------------+ + | Option name | Type | Description | + +==========================+=============+====================================================+ + | status | bool | Is the IPv6 enabled for this interface. | + | | | If set to ``false``, then these options are no-op. | + +--------------------------+-------------+----------------------------------------------------+ + | ipv6_addresses | string list | IPv6 addresses applied to this interface. | + | | | The value can contain prefix length. | + | | | Example: ``2001:db8::1/64`` | + +--------------------------+-------------+----------------------------------------------------+ + | ipv6_multicast_addresses | string list | IPv6 multicast addresses applied to this interface.| + +--------------------------+-------------+----------------------------------------------------+ + | hop_limit | int | Hop limit for the interface. | + +--------------------------+-------------+----------------------------------------------------+ + | multicast_hop_limit | int | Multicast hop limit for the interface. | + +--------------------------+-------------+----------------------------------------------------+ + | dhcpv6 | struct | DHCPv6 client options. | + +--------------------------+-------------+----------------------------------------------------+ + | prefixes | list of | IPv6 prefixes. | + | | structs | | + +--------------------------+-------------+----------------------------------------------------+ + +.. table:: The ``dhcpv6`` options + :align: left + + +--------------------+------+-----------------------------------------------------------------+ + | Option name | Type | Description | + +====================+======+=================================================================+ + | status | bool | Is DHCPv6 client enabled for this interface. | + +--------------------+------+-----------------------------------------------------------------+ + | do_request_address | bool | Request IPv6 address. | + +--------------------+------+-----------------------------------------------------------------+ + | do_request_prefix | bool | Requeest IPv6 prefix. | + +--------------------+------+-----------------------------------------------------------------+ + +.. table:: The ``prefixes`` options + :align: left + + +-------------+--------+----------------------------------------------------------------------+ + | Option name | Type | Description | + +=============+========+======================================================================+ + | address | string | IPv6 address. | + +-------------+--------+----------------------------------------------------------------------+ + | len | int | Prefix length. | + +-------------+--------+----------------------------------------------------------------------+ + | lifetime | int | Prefix lifetime. | + +-------------+--------+----------------------------------------------------------------------+ + +.. table:: The ``ipv4`` options + :align: left + + +--------------------------+-------------+----------------------------------------------------+ + | Option name | Type | Description | + +==========================+=============+====================================================+ + | status | bool | Is the IPv4 enabled for this interface. | + | | | If set to ``false``, then these options are no-op. | + +--------------------------+-------------+----------------------------------------------------+ + | ipv4_addresses | string list | IPv4 addresses applied to this interface. | + | | | The value can contain netmask length. | + | | | Example: ``192.0.2.1/24`` | + +--------------------------+-------------+----------------------------------------------------+ + | ipv4_multicast_addresses | string list | IPv4 multicast addresses applied to this interface.| + +--------------------------+-------------+----------------------------------------------------+ + | time_to_live | int | Time-to-live value for this interface. | + +--------------------------+-------------+----------------------------------------------------+ + | multicast_time_to_live | int | Multicast time-to-live value for this interface. | + +--------------------------+-------------+----------------------------------------------------+ + | gateway | string | Gateway IPv4 address. | + +--------------------------+-------------+----------------------------------------------------+ + | ipv4_autoconf | struct | IPv4 autoconfiguration options. | + +--------------------------+-------------+----------------------------------------------------+ + | dhcpv4 | struct | DHCPv4 client options. | + +--------------------------+-------------+----------------------------------------------------+ + | dhcpv4_server | struct | DHCPv4 server options. | + +--------------------------+-------------+----------------------------------------------------+ + +.. table:: The ``ipv4_autoconf`` options + :align: left + + +--------------------+--------+---------------------------------------------------------------+ + | Option name | Type | Description | + +====================+========+===============================================================+ + | status | bool | Is IPv4 auto-conf enabled for this interface. | + +--------------------+--------+---------------------------------------------------------------+ + +.. table:: The ``dhcpv4`` options + :align: left + + +--------------------+--------+---------------------------------------------------------------+ + | Option name | Type | Description | + +====================+========+===============================================================+ + | status | bool | Is DHCPv4 client enabled for this interface. | + +--------------------+--------+---------------------------------------------------------------+ + +.. table:: The ``dhcpv4_server`` options + :align: left + + +--------------------+--------+---------------------------------------------------------------+ + | Option name | Type | Description | + +====================+========+===============================================================+ + | status | bool | Is DHCPv4 server enabled for this interface. | + +--------------------+--------+---------------------------------------------------------------+ + | base_address | string | Request IPv6 address. | + +--------------------+--------+---------------------------------------------------------------+ + +.. table:: The ``vlan`` options + :align: left + + +-------------+--------+----------------------------------------------------------------------+ + | Option name | Type | Description | + +=============+========+======================================================================+ + | status | bool | Is VLAN enabled for this interface. | + +-------------+--------+----------------------------------------------------------------------+ + | tag | int | VLAN tag applied to this interface. | + +-------------+--------+----------------------------------------------------------------------+ + +.. table:: The ``sntp`` options + :align: left + + +-------------+-------------+-----------------------------------------------------------------+ + | Option name | Type | Description | + +=============+=============+=================================================================+ + | status | bool | Is SNTP enabled. | + +-------------+-------------+-----------------------------------------------------------------+ + | server | string | SNTP server address. | + +-------------+-------------+-----------------------------------------------------------------+ + | timeout | int | SNTP server connection timeout. | + +-------------+-------------+-----------------------------------------------------------------+ + | bind_to | reference | Connect to server using this network interface. | + +-------------+-------------+-----------------------------------------------------------------+ + +.. table:: The ``ieee_802_15_4`` options + :align: left + + +-------------------+-----------+-------------------------------------------------------------+ + | Option name | Type | Description | + +===================+===========+=============================================================+ + | status | bool | Is IEEE 802.15.4 enabled. | + +-------------------+-----------+-------------------------------------------------------------+ + | bind_to | reference | Apply the options to this network interface. | + +-------------------+-------------+-----------------------------------------------------------+ + | pan_id | int | PAN identifier. | + +-------------------+-----------+-------------------------------------------------------------+ + | channel | int | Channel number. | + +-------------------+-----------+-------------------------------------------------------------+ + | tx_power | int | Transmit power. | + +-------------------+-----------+-------------------------------------------------------------+ + | ack_required | bool | Require acknowledgment. | + +-------------------+-----------+-------------------------------------------------------------+ + | security_key | int array | IEEE 802.15.4 security key. Maximum length is 16. | + +-------------------+-----------+-------------------------------------------------------------+ + | security_key_mode | int | IEEE 802.15.4 security key mode. | + +-------------------+-----------+-------------------------------------------------------------+ + | security_level | int | IEEE 802.15.4 security level. | + +-------------------+-----------+-------------------------------------------------------------+ diff --git a/include/zephyr/net/net_config.h b/include/zephyr/net/net_config.h index 088ae5b558d4e..4889b02636622 100644 --- a/include/zephyr/net/net_config.h +++ b/include/zephyr/net/net_config.h @@ -14,6 +14,13 @@ #include #include #include +#include + +#if defined(CONFIG_NET_CONFIG_SETTINGS) +#include +#else +struct net_init_config; +#endif #ifdef __cplusplus extern "C" { @@ -98,6 +105,37 @@ int net_config_init_by_iface(struct net_if *iface, const char *app_info, */ int net_config_init_app(const struct device *dev, const char *app_info); +/* @brief Get network initialization configuration. + * + * @details This network configuration consists of initial read-only + * configuration and read-write configuration when the + * configuration is changed at runtime. + * + * @param cfg Caller supplied pointer to struct net_init_config where + * the configuration will be stored. + */ +int net_config_get(struct net_init_config *cfg); + +/* @brief Set network initialization configuration. + * + * @details The user supplied configuration is saved to permanent + * storage. How this works: + * - If the config option in struct net_init_config is different + * than the default one, then change the option and enable the _changed flag. + * - If the config option in struct net_init_config is the same + * as the default one. + * + * @param cfg Caller supplied pointer to struct net_init_config where + * the configuration will be read. + */ +int net_config_set(const struct net_init_config *cfg); + +/* @brief Clear all network configuration. + * + * @details This will reset all runtime configuration back to defaults. + */ +int net_config_clear(void); + /** * @} */ diff --git a/scripts/net/net-yaml-config.py b/scripts/net/net-yaml-config.py new file mode 100755 index 0000000000000..e912ee0df2cc6 --- /dev/null +++ b/scripts/net/net-yaml-config.py @@ -0,0 +1,434 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-License-Identifier: Apache-2.0 + +import hashlib +import os +import re +import sys +import yaml + +data = {} +schema = {} +normalized_output = "" + + +def read_yaml_file(input_data): + return yaml.safe_load(input_data) + + +# Allow both yaml file or .config file input +input_data = sys.stdin.read() + +lines = input_data.splitlines() +for line in lines: + if not line.strip(): # skip empty lines + continue + + if line.startswith("#"): + continue + if line.startswith("CONFIG_"): + # Construct string with two places to avoid triggering warning + # from compliance checker. + if not line.startswith("CONFIG_" + "NET_"): + continue + + (var, val) = line.split("=", 1) + if val == 'y': + val = "true" + + os.environ[var] = val + + # print("env " + var + " = " + os.getenv(var, "")) + else: + data = read_yaml_file(input_data) + break + + +# yaml env variable handler +def constructor_env_variables(loader, node): + value = loader.construct_scalar(node) + match = pattern.findall(value) # to find all env variables in line + dt = ''.join(type_tag_pattern.findall(value)) or '' + value = value.replace(dt, '') + if match: + full_value = value + for g in match: + curr_default_value = 'None' + env_var_name = g + env_var_name_with_default = g + if default_sep and isinstance(g, tuple) and len(g) > 1: + env_var_name = g[0] + env_var_name_with_default = ''.join(g) + found = False + for each in g: + if default_sep in each: + _, curr_default_value = each.split(default_sep, 1) + found = True + break + if not found: + raise ValueError(f'Could not find default value for {env_var_name}') + full_value = full_value.replace( + f'${{{env_var_name_with_default}}}', + os.environ.get(env_var_name, curr_default_value).strip('"'), + ) + if dt: + # do one more roundtrip with the dt constructor: + node.value = full_value + node.tag = dt.strip() + return loader.yaml_constructors[node.tag](loader, node) + return full_value + return value + + +# Create a yaml file from a .config variables. Treat config options as environment +# variables and substitute the variables in yaml template. +def create_yaml_file(): + if ( + "CONFIG_NET_CONFIG_MY_IPV4_NETMASK" in os.environ + and os.environ["CONFIG_NET_CONFIG_MY_IPV4_NETMASK"] != "" + ): + masklen = sum( + bin(int(x)).count('1') + for x in os.environ["CONFIG_NET_CONFIG_MY_IPV4_NETMASK"].strip('"').split('.') + ) + os.environ["IPV4_NETMASK_LEN"] = str(masklen) + + if ( + "CONFIG_NET_CONFIG_SNTP_INIT_SERVER" in os.environ + and os.environ["CONFIG_NET_CONFIG_SNTP_INIT_SERVER"] != "" + ): + os.environ["SNTP_ENABLED"] = "true" + + if "CONFIG_NET_L2_IEEE802154" in os.environ: + if ( + "CONFIG_NET_CONFIG_IEEE802154_SECURITY_KEY" in os.environ + and os.environ["CONFIG_NET_CONFIG_IEEE802154_SECURITY_KEY"] != "" + ): + IEEE802154_SECURITY_KEY = str( + [ + int(ord(x[0])) + for x in list( + os.environ["CONFIG_NET_CONFIG_IEEE802154_SECURITY_KEY"].strip('"') + ) + ] + ) + else: + IEEE802154_SECURITY_KEY = str([]) + + ieee802154 = ( + """ + ieee_802_15_4: + status: !ENV tag:yaml.org,2002:bool ${CONFIG_NET_L2_IEEE802154:false} + pan_id: !ENV tag:yaml.org,2002:int ${CONFIG_NET_CONFIG_IEEE802154_PAN_ID:0xabcd} + channel: !ENV tag:yaml.org,2002:int ${CONFIG_NET_CONFIG_IEEE802154_CHANNEL:0} + tx_power: !ENV tag:yaml.org,2002:int ${CONFIG_NET_CONFIG_IEEE802154_RADIO_TX_POWER:0} + security_key: """ + + IEEE802154_SECURITY_KEY + + """ + security_key_mode: !ENV tag:yaml.org,2002:int ${CONFIG_NET_CONFIG_IEEE802154_SECURITY_KEY_MODE:0} + security_level: !ENV tag:yaml.org,2002:int ${CONFIG_NET_CONFIG_IEEE802154_SECURITY_LEVEL:0} + ack_required: !ENV tag:yaml.org,2002:bool ${CONFIG_NET_CONFIG_IEEE802154_ACK_REQUIRED:False} + """ # noqa: E501 + ) + else: + ieee802154 = "" + + if "SNTP_ENABLED" in os.environ: + sntp = """ + sntp: + status: !ENV tag:yaml.org,2002:bool ${SNTP_ENABLED:false} + server: !ENV ${CONFIG_NET_CONFIG_SNTP_INIT_SERVER:""} + timeout: !ENV tag:yaml.org,2002:int ${CONFIG_NET_CONFIG_SNTP_INIT_TIMEOUT:0} + """ + else: + sntp = "" + + # There is only a limited number of options to set via .config file + yaml_file = ( + """ +net_init_config: + network_interfaces: + - ipv6: + status: !ENV tag:yaml.org,2002:bool ${CONFIG_NET_CONFIG_NEED_IPV6:false} + ipv6_addresses: + - !ENV ${CONFIG_NET_CONFIG_MY_IPV6_ADDR:""} + dhcpv6: + status: !ENV tag:yaml.org,2002:bool ${CONFIG_NET_DHCPV6:false} + do_request_address: !ENV tag:yaml.org,2002:bool ${CONFIG_NET_CONFIG_DHCPV6_REQUEST_ADDR:true} + do_request_prefix: !ENV tag:yaml.org,2002:bool ${CONFIG_NET_CONFIG_DHCPV6_REQUEST_PREFIX:false} + ipv4: + status: !ENV tag:yaml.org,2002:bool ${CONFIG_NET_CONFIG_NEED_IPV4:false} + ipv4_addresses: + - !ENV ${CONFIG_NET_CONFIG_MY_IPV4_ADDR:""}/${IPV4_NETMASK_LEN:32} + gateway: !ENV ${CONFIG_NET_CONFIG_MY_IPV4_GW:""} + dhcpv4: + status: !ENV tag:yaml.org,2002:bool ${CONFIG_NET_DHCPV4:false} + ipv4_autoconf: + status: !ENV tag:yaml.org,2002:bool ${CONFIG_NET_IPV4_AUTO:false} + + """ # noqa: E501 + + ieee802154 + + sntp + ) + + return yaml_file + + +# This branch handles the variables read from .config, it generates a yaml +# file and then validates (if enabled) that it is correct. +if not bool(data): + # Create a yaml using the env variables + loader = yaml.SafeLoader + tag = '!ENV' + default_sep = ':' + default_value = '' + default_sep_pattern = r'(' + default_sep + '[^}]+)?' + pattern = re.compile(r'.*?\$\{([^}{' + default_sep + r']+)' + default_sep_pattern + r'\}.*?') + type_tag = 'tag:yaml.org,2002:' + type_tag_pattern = re.compile(rf'({type_tag}\w+\s)') + + loader.add_implicit_resolver(tag, pattern, first=[tag]) + loader.add_constructor(tag, constructor_env_variables) + + data = read_yaml_file(create_yaml_file()) + +# If user has supplied an argument, treat it as a schema file +if bool(data) and len(sys.argv[1:]) > 0: + # If jsonschema is installed, then validate the yaml + try: + import jsonschema + + def yaml_validate(data, schema): + if not schema: + return + + try: + from yaml import CSafeLoader as SafeLoader + except ImportError: + from yaml import SafeLoader + + with open(schema) as f: + net_schema = yaml.load(f.read(), Loader=SafeLoader) + + validator_class = jsonschema.validators.validator_for(net_schema) + validator_class.check_schema(net_schema) + validator = validator_class(net_schema) + + errors = sorted(validator.iter_errors(data), key=lambda e: e.path) + if errors: + # Build a readable message with each error and its path + lines = [] + for e in errors: + path = ".".join(map(str, list(e.path))) or "" + lines.append(f"{path}: {e.message}") + raise jsonschema.ValidationError("\n".join(lines)) + + except ImportError as e: + sys.stderr.write("can't import jsonschema; won't validate YAML (%s)", e) + + def yaml_validate(data, schema): + pass + + yaml_validate(data, sys.argv[1]) + +with open(sys.argv[1]) as schema_file: + schema = yaml.safe_load(schema_file) + +if not bool(schema): + sys.stderr.write("Schema file needs to be supplied\n") + exit(1) + +netif = data['net_init_config']['network_interfaces'] +netif_count = len(netif) + + +# Note that all the bind-to fields are added +1 so that we can catch +# the case where the value is not set (0). When taken into use in C code, +# then -1 is added to the value. +def get_bind_to(dict): + for i in range(netif_count): + if dict == netif[i]: + return i + 1 + + +def print_bind_to(dict, indent): + print(indent + ".bind_to = " + str(get_bind_to(dict)) + ",") + + +def walk_dict(map, indent): + for key, value in map.items(): + if isinstance(value, list): + print(indent + "." + key + " = {") + walk_list(value, "\t" + indent) + print(indent + "},") + elif isinstance(value, dict): + if key == "bind_to": + print_bind_to(value, indent) + else: + print(indent + "." + key + " = {") + walk_dict(value, "\t" + indent) + print(indent + "},") + elif isinstance(value, str): + print(indent + "." + key + " = \"" + value + "\",") + elif isinstance(value, bool): + print(indent + "." + key + " = " + str(value).lower() + ",") + elif isinstance(value, int): + print(indent + "." + key + " = " + str(value) + ",") + + +def walk_list(lst, indent): + for i, v in enumerate(lst): + print(indent + "[" + str(i) + "] = {") + if isinstance(v, list): + walk_list(v, "\t" + indent) + elif isinstance(v, dict): + walk_dict(v, "\t" + indent) + elif isinstance(v, str): + print(indent + "\t" + "\"" + v + "\",") + elif isinstance(v, bool): + print(indent + "\t" + str(v).lower() + ",") + elif isinstance(v, int): + print(indent + "\t" + str(v) + ",") + print(indent + "},") + + +def walk_dict_union(cdict, prev_key, map): + for key, value in map.items(): + if isinstance(value, list): + walk_list_union(cdict, key, value) + elif isinstance(value, dict): + walk_dict_union(cdict, key, value) + + +def walk_list_union(cdict, key, lst): + for _, v in enumerate(lst): + if isinstance(v, list): + walk_list_union(cdict, key, v) + elif isinstance(v, dict): + walk_dict_union(cdict, key, v) + # Store the length of the array so that we can fetch that + # when walking the schema file. + if key in cdict: + if cdict[key] < len(lst): + cdict[key] = len(lst) + else: + cdict[key] = len(lst) + + +def output(indent, str): + global normalized_output + + normalized_output += str + print(indent + str) + + +changed = "" + + +def walk_dict_schema(level, top_level_name, cdict, key_upper, map, indent): + global changed + for key, value in map.items(): + if key == "type": + # Use value instead of items to avoid changing too many places in C code + if key_upper == "items": + key_upper = "value" + + if value == "string": + output(indent, "const char *" + key_upper + ";") + changed += indent + "bool " + "__" + key_upper + "_changed : 1;" + "\n" + elif value == "boolean": + output(indent, "bool " + key_upper + ";") + changed += indent + "bool " + "__" + key_upper + "_changed : 1;" + "\n" + elif value == "integer": + output(indent, "int " + key_upper + ";") + changed += indent + "bool " + "__" + key_upper + "_changed : 1;" + "\n" + elif value == "array": + print(changed, end="") + changed = "" + output(indent, "struct " + top_level_name + "_" + key_upper + " {") + elif value == "object": + print(changed, end="") + changed = "" + if key_upper != "value": + if level == 1: + output(indent, "struct " + key_upper + " {") + else: + output(indent, "struct " + top_level_name + "_" + key_upper + " {") + continue + + if key == "bind_to": + output(indent, "int bind_to;") + changed += indent + "bool " + "__bind_to_changed : 1;" + "\n" + continue + + if isinstance(value, dict): + walk_dict_schema(level + 1, top_level_name, cdict, key, value, "\t" + indent) + elif isinstance(value, list): + walk_list_schema(level + 1, top_level_name, cdict, key, value, "\t" + indent) + + if key == "items": + print(changed, end="") + changed = "" + if key_upper in combined_data: + output(indent, "} " + key_upper + "[" + str(combined_data[key_upper]) + "];") + else: + output(indent, "} " + key_upper + "[1];") + + elif key == "properties": + print(changed, end="") + changed = "" + if key_upper != "value": + # Avoid creating a variable at the top level because we have + # a separate variable created in the header file that is used in C file. + if level == 1: + output(indent, "};") + else: + output(indent, "} " + key_upper + ";") + + +def walk_list_schema(level, top_level_name, cdict, key, lst, indent): + for _, v in enumerate(lst): + if isinstance(v, list): + walk_list_schema(level, top_level_name, cdict, key, v, indent) + elif isinstance(v, dict): + walk_dict_schema(level, top_level_name, cdict, key, v, indent) + + +combined_data = {} +schema_data = {} + +print("#ifndef ZEPHYR_INCLUDE_NET_CONFIG_AUTO_GENERATED_H_") +print("#define ZEPHYR_INCLUDE_NET_CONFIG_AUTO_GENERATED_H_") + +# Figure out the array sizes +for key, value in data.items(): + if isinstance(value, dict): + walk_dict_union(combined_data, key, value) + +# Create C struct definition. Prefix some of the generated C struct fields with +# by the name of the struct to make them unambiguous. +for key, value in schema.items(): + if key in ('$id', '$schema', 'title', 'description'): + print("/* " + key + ": " + str(value) + " */") + if isinstance(value, dict): + walk_dict_schema(0, list(data.keys())[0], schema_data, key, value, "") + +print() +print("const struct " + list(data.keys())[0] + "* net_config_get_init_config(void);") +print() +print("#define NET_CONFIG_NETWORK_INTERFACE_COUNT " + str(netif_count)) +print("#endif /* ZEPHYR_INCLUDE_NET_CONFIG_AUTO_GENERATED_H_ */") +print() + +# Create C struct values +for key, value in data.items(): + if isinstance(value, dict): + print("#if defined(" + key.upper() + "_ENABLE_DATA)") + print("#define " + key.upper() + "_DATA " + key + "_data") + print("static const struct " + key + " " + key + "_data = {") + h = hashlib.sha1(normalized_output.encode('utf-8')).hexdigest() + print(f"\t.config_format_hash = \"{h}\",") + walk_dict(value, "\t") + print("};") + print("#endif /* " + key.upper() + "_ENABLE_DATA */") diff --git a/scripts/schemas/net-configuration-schema.yml b/scripts/schemas/net-configuration-schema.yml new file mode 100644 index 0000000000000..cfbfdf4b40a2f --- /dev/null +++ b/scripts/schemas/net-configuration-schema.yml @@ -0,0 +1,201 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# A JSON schema for basic validation of the network configuration yaml file. + +$id: "https://zephyrproject.org/schemas/zephyr/network" +$schema: "https://json-schema.org/draft/2020-12/schema" + +title: "Zephyr Network Configuration Schema" +description: Schema for validating Zephyr network configuration metadata files +type: object +properties: + net_init_config: + type: object + properties: + config_format_hash: + type: string + description: Hash for the configuration data (internal) + network_interfaces: + description: List of network interfaces + type: array + items: + type: object + properties: + bind_to: {} + name: + description: Network interface name to bind to + type: string + device_name: + description: Device name to bind to + type: string + set_name: + description: New name of the network interface + type: string + set_default: + description: Set this interface as a default one + type: boolean + flags: + description: Network interface flags to set + type: array + items: + type: string + ipv6: + description: IPv6 configuration + type: object + properties: + status: + description: Is IPv6 enabled for this interface + type: boolean + ipv6_addresses: + description: Static IPv6 addresses for this interface + type: array + items: + type: string + ipv6_multicast_addresses: + description: Multicast IPv6 addresses for this interface + type: array + items: + type: string + prefixes: + description: IPv6 prefixes for this interface + type: array + items: + type: object + properties: + address: + description: IPv6 prefix + type: string + len: + description: IPv6 prefix length + type: integer + lifetime: + description: IPv6 prefix lifetime (in sec) + type: integer + required: + - address + - len + - lifetime + hop_limit: + description: IPv6 hop limit + type: integer + multicast_hop_limit: + description: IPv6 multicast hop limit + type: integer + dhcpv6: + description: DHCPv6 configuration + type: object + properties: + status: + description: Is DHCPv6 enabled for this interface + type: boolean + do_request_address: + description: Set request address flag + type: boolean + do_request_prefix: + description: Set request prefix flag + type: boolean + ipv4: + description: IPv4 configuration + type: object + properties: + status: + description: Is IPv4 enabled for this interface + type: boolean + ipv4_addresses: + description: Static IPv4 addresses for this interface + type: array + items: + type: string + ipv4_multicast_addresses: + description: Multicast IPv4 addresses for this interface + type: array + items: + type: string + gateway: + description: IPv4 gateway to use + type: string + time_to_live: + description: IPv4 time-to-live + type: integer + multicast_time_to_live: + description: IPv4 multicast time-to-live + type: integer + dhcpv4: + description: DHCPv4 configuration + type: object + properties: + status: + description: Is DHCPv4 enabled for this interface + type: boolean + dhcpv4_server: + type: object + properties: + status: + description: Is DHCPv4 server enabled for this interface + type: boolean + base_address: + description: Set base address for the DHCPv4 server + type: string + ipv4_autoconf: + description: IPv4 autoconfiguration + type: object + properties: + status: + description: Is IPv4 auto configuration enabled for this interface + type: boolean + vlan: + description: Virtual LAN configuration + type: object + properties: + status: + description: Is VLAN enabled for this interface + type: boolean + tag: + description: VLAN tag for this interface + type: integer + required: + - tag + ieee_802_15_4: + type: object + properties: + status: + description: Is IEEE 802.15.4 enabled + type: boolean + bind_to: {} + pan_id: + type: integer + channel: + type: integer + tx_power: + type: integer + security_key_mode: + type: integer + security_level: + type: integer + ack_required: + type: boolean + security_key: + type: array + items: + type: integer + required: + - pan_id + - channel + sntp: + type: object + properties: + status: + description: Is SNTP enabled + type: boolean + server: + description: SNTP server address + type: string + timeout: + description: Request timeout in sec + type: integer + bind_to: {} + required: + - server +additionalProperties: false diff --git a/subsys/net/ip/utils.c b/subsys/net/ip/utils.c index d91b060ef1bb0..65852746a25dd 100644 --- a/subsys/net/ip/utils.c +++ b/subsys/net/ip/utils.c @@ -988,7 +988,7 @@ const char *net_ipaddr_parse_mask(const char *str, size_t str_len, int parsed_mask_len = -1; bool ret = false; - if (str == NULL || str_len == 0 || addr == NULL || mask_len == NULL) { + if (str == NULL || str_len == 0 || addr == NULL) { return NULL; } @@ -1019,7 +1019,10 @@ const char *net_ipaddr_parse_mask(const char *str, size_t str_len, } str_len = mask_ptr - str; - *mask_len = (uint8_t)parsed_mask_len; + + if (mask_len != NULL) { + *mask_len = (uint8_t)parsed_mask_len; + } } #if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_IPV6) @@ -1038,7 +1041,7 @@ const char *net_ipaddr_parse_mask(const char *str, size_t str_len, return NULL; } - if (parsed_mask_len < 0) { + if (parsed_mask_len < 0 && mask_len != NULL) { if (addr->sa_family == AF_INET) { *mask_len = 32; } else if (addr->sa_family == AF_INET6) { diff --git a/subsys/net/lib/config/CMakeLists.txt b/subsys/net/lib/config/CMakeLists.txt index 732509737396f..be24862f28490 100644 --- a/subsys/net/lib/config/CMakeLists.txt +++ b/subsys/net/lib/config/CMakeLists.txt @@ -6,6 +6,10 @@ zephyr_library_compile_definitions_ifdef( ) zephyr_library_sources_ifdef(CONFIG_NET_CONFIG_SETTINGS init.c) +zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/net/ip) +zephyr_library_include_directories_ifdef(CONFIG_FILE_SYSTEM_LITTLEFS + ${ZEPHYR_BASE}/../modules/fs/littlefs +) if(CONFIG_NET_CONFIG_SETTINGS) zephyr_library_sources_ifdef( diff --git a/subsys/net/lib/config/Kconfig b/subsys/net/lib/config/Kconfig index 3e9685643172f..95b7d26908d06 100644 --- a/subsys/net/lib/config/Kconfig +++ b/subsys/net/lib/config/Kconfig @@ -13,6 +13,7 @@ menuconfig NET_CONFIG_SETTINGS bool "Set network settings for applications" select NET_MGMT select NET_MGMT_EVENT + select SETTINGS help Allow IP addresses to be set in config file for networking client/server sample applications, or @@ -22,6 +23,12 @@ menuconfig NET_CONFIG_SETTINGS if NET_CONFIG_SETTINGS +config NET_CONFIG_SETTINGS_SHELL_ACCESS + bool "Configure options via shell" + help + If this is set, then user is able to use "net config" shell to + update the default options. This requires settings db. + config NET_CONFIG_AUTO_INIT bool "Init networking support automatically during device startup" default y if !(USB_DEVICE_NETWORK || USBD_CDC_ECM_CLASS || USBD_CDC_NCM_CLASS) diff --git a/subsys/net/lib/config/configuration-example.yaml b/subsys/net/lib/config/configuration-example.yaml new file mode 100644 index 0000000000000..c83b121441188 --- /dev/null +++ b/subsys/net/lib/config/configuration-example.yaml @@ -0,0 +1,121 @@ +# This is an example for defining network configuration data that +# can be applied automatically when the device boots. +# +net_init_config: + network_interfaces: + # Example of one interface selected by its name + - &main-interface + name: eth0 + set_name: my-eth0 + set_default: true + ipv6: + status: true + ipv6_addresses: + - 2001:db8:110::1 + ipv6_multicast_addresses: + - ff05::114 + - ff15::115 + prefixes: + - address: "2001:db8::" + len: 64 + lifetime: 1024 + hop_limit: 64 + multicast_hop_limit: 1 + dhcpv6: + status: true + do_request_address: true + do_request_prefix: false + ipv4: + status: true + ipv4_addresses: + - 192.0.2.10/24 + ipv4_multicast_addresses: + - 234.0.0.10 + gateway: 192.0.2.1 + time_to_live: 64 + multicast_time_to_live: 1 + dhcpv4: + status: true + ipv4_autoconf: + status: true + + # Example of another interface selected by its device + - &device-interface + device_name: ETH_DEVICE + set_name: my-eth1 + flags: + - NET_IF_NO_AUTO_START + ipv4: + status: true + ipv4_addresses: + - 192.168.1.2/24 + ipv4_multicast_addresses: + - 234.0.0.10 + gateway: 192.168.110.1 + time_to_live: 10 + multicast_time_to_live: 0 + dhcpv4: + status: true + dhcpv4_server: + status: true + base_address: 192.168.1.100 + + # Example of virtual interface tied to the first one + - name: virt0 + set_name: virt0 + bind_to: *device-interface + ipv6: + status: false + ipv6_addresses: + - 2001:db8:111::2 + ipv4: + status: false + + # Example of VLAN interface that attaches to eth0 + - &vlan-interface + set_name: vlan0 + bind_to: *main-interface + vlan: + status: true + tag: 1234 + ipv4: + status: true + dhcpv4: + status: true + + # The interface IPv4 configuration could be set to disabled + # in which case its IPv4 configuration is skipped + - name: eth1 + set_name: my-eth2 + flags: + - NET_IF_NO_AUTO_START + ipv4: + status: false + ipv4_addresses: + - 192.168.1.2/24 + ipv4_multicast_addresses: + - 234.0.0.10 + gateway: 192.168.110.1 + time_to_live: 10 + multicast_time_to_live: 0 + dhcpv4_server: + status: true + base_address: 192.168.2.1 + + ieee_802_15_4: + status: true + pan_id: 0xabcd + channel: 26 + tx_power: 1 + security_key: [0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, + 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf] + security_key_mode: 0 + security_level: 1 + ack_required: true + bind_to: *device-interface + + sntp: + status: true + server: sntp.foo.bar + timeout: 30 + bind_to: *vlan-interface diff --git a/subsys/net/lib/config/ieee802154_settings.c b/subsys/net/lib/config/ieee802154_settings.c index 4a142bf75bdf5..0d6752fab87a1 100644 --- a/subsys/net/lib/config/ieee802154_settings.c +++ b/subsys/net/lib/config/ieee802154_settings.c @@ -17,23 +17,14 @@ LOG_MODULE_DECLARE(net_config, CONFIG_NET_CONFIG_LOG_LEVEL); #include #include -int z_net_config_ieee802154_setup(struct net_if *iface) +int z_net_config_ieee802154_setup(struct net_if *iface, + uint16_t channel, + uint16_t pan_id, + int16_t tx_power, + struct ieee802154_security_params *sec_params) { - uint16_t channel = CONFIG_NET_CONFIG_IEEE802154_CHANNEL; - uint16_t pan_id = CONFIG_NET_CONFIG_IEEE802154_PAN_ID; const struct device *const dev = iface == NULL ? DEVICE_DT_GET(DT_CHOSEN(zephyr_ieee802154)) : net_if_get_device(iface); - int16_t tx_power = CONFIG_NET_CONFIG_IEEE802154_RADIO_TX_POWER; - -#ifdef CONFIG_NET_L2_IEEE802154_SECURITY - struct ieee802154_security_params sec_params = { - .key = CONFIG_NET_CONFIG_IEEE802154_SECURITY_KEY, - .key_len = sizeof(CONFIG_NET_CONFIG_IEEE802154_SECURITY_KEY), - .key_mode = CONFIG_NET_CONFIG_IEEE802154_SECURITY_KEY_MODE, - .level = CONFIG_NET_CONFIG_IEEE802154_SECURITY_LEVEL, - }; -#endif /* CONFIG_NET_L2_IEEE802154_SECURITY */ - if (!device_is_ready(dev)) { return -ENODEV; } @@ -62,7 +53,7 @@ int z_net_config_ieee802154_setup(struct net_if *iface) #ifdef CONFIG_NET_L2_IEEE802154_SECURITY if (net_mgmt(NET_REQUEST_IEEE802154_SET_SECURITY_SETTINGS, iface, - &sec_params, sizeof(struct ieee802154_security_params))) { + sec_params, sizeof(struct ieee802154_security_params))) { return -EINVAL; } #endif /* CONFIG_NET_L2_IEEE802154_SECURITY */ diff --git a/subsys/net/lib/config/ieee802154_settings.h b/subsys/net/lib/config/ieee802154_settings.h index 5f0bb70b2490f..d235277fda0d4 100644 --- a/subsys/net/lib/config/ieee802154_settings.h +++ b/subsys/net/lib/config/ieee802154_settings.h @@ -6,10 +6,16 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include + #if defined(CONFIG_NET_L2_IEEE802154) && defined(CONFIG_NET_CONFIG_SETTINGS) struct net_if; -int z_net_config_ieee802154_setup(struct net_if *iface); +int z_net_config_ieee802154_setup(struct net_if *iface, + uint16_t channel, + uint16_t pan_id, + int16_t tx_power, + struct ieee802154_security_params *sec_params); #else #define z_net_config_ieee802154_setup(...) 0 #endif diff --git a/subsys/net/lib/config/init.c b/subsys/net/lib/config/init.c index 22500fd8f8443..cbbeca7d3f785 100644 --- a/subsys/net/lib/config/init.c +++ b/subsys/net/lib/config/init.c @@ -15,6 +15,13 @@ LOG_MODULE_REGISTER(net_config, CONFIG_NET_CONFIG_LOG_LEVEL); #include #include +#include + +#if defined(CONFIG_FILE_SYSTEM_LITTLEFS) +#include +#include +#endif + #include #include #include @@ -22,336 +29,125 @@ LOG_MODULE_REGISTER(net_config, CONFIG_NET_CONFIG_LOG_LEVEL); #include #include #include +#include #include #include #include - +#include +#include #include #include "ieee802154_settings.h" +#include "net_private.h" -extern int net_init_clock_via_sntp(void); - -static K_SEM_DEFINE(waiter, 0, 1); -static K_SEM_DEFINE(counter, 0, UINT_MAX); -static atomic_t services_flags; - -#if defined(CONFIG_NET_NATIVE) -static struct net_mgmt_event_callback mgmt_iface_cb; -#endif - -static inline void services_notify_ready(int flags) -{ - atomic_or(&services_flags, flags); - k_sem_give(&waiter); -} - -static inline bool services_are_ready(int flags) -{ - return (atomic_get(&services_flags) & flags) == flags; -} +#include "net_init_config.inc" -#if defined(CONFIG_NET_NATIVE_IPV4) +const struct net_init_config *net_config_get_init_config(void); -#if defined(CONFIG_NET_DHCPV4) +#define STORAGE_PARTITION storage_partition +#define STORAGE_PARTITION_ID FIXED_PARTITION_ID(STORAGE_PARTITION) -static void setup_dhcpv4(struct net_if *iface) -{ - NET_INFO("Running dhcpv4 client..."); +extern int net_init_clock_via_sntp(struct net_if *iface, + const char *server, + int timeout); - net_dhcpv4_start(iface); -} +static K_SEM_DEFINE(waiter, 0, 1); +static K_SEM_DEFINE(counter, 0, UINT_MAX); -static void print_dhcpv4_info(struct net_if *iface) -{ -#if CONFIG_NET_CONFIG_LOG_LEVEL >= LOG_LEVEL_INF - char hr_addr[NET_IPV4_ADDR_LEN]; -#endif - ARRAY_FOR_EACH(iface->config.ip.ipv4->unicast, i) { - struct net_if_addr *if_addr = - &iface->config.ip.ipv4->unicast[i].ipv4; +#define SETTINGS_SUBTREE_NET_CONFIG "net/config" - if (if_addr->addr_type != NET_ADDR_DHCP || - !if_addr->is_used) { - continue; - } +static struct net_init_config net_init_config_user; -#if CONFIG_NET_CONFIG_LOG_LEVEL >= LOG_LEVEL_INF - NET_INFO("IPv4 address: %s", - net_addr_ntop(AF_INET, &if_addr->address.in_addr, - hr_addr, sizeof(hr_addr))); - NET_INFO("Lease time: %u seconds", - iface->config.dhcpv4.lease_time); - NET_INFO("Subnet: %s", - net_addr_ntop(AF_INET, - &iface->config.ip.ipv4->unicast[i].netmask, - hr_addr, sizeof(hr_addr))); - NET_INFO("Router: %s", - net_addr_ntop(AF_INET, &iface->config.ip.ipv4->gw, - hr_addr, sizeof(hr_addr))); -#endif - break; - } -} +#if defined(CONFIG_SETTINGS) +static bool settings_loaded; -#else -#define setup_dhcpv4(...) -#define print_dhcpv4_info(...) -#endif /* CONFIG_NET_DHCPV4 */ +static int net_config_settings_set(const char *name, size_t len, + settings_read_cb read_cb, void *cb_arg); +static int net_config_settings_commit(void); +static int net_config_settings_export(int (*cb)(const char *name, + const void *value, size_t val_len)); +static int net_config_settings_get(const char *name, char *val, int val_len_max); -static struct net_mgmt_event_callback mgmt4_cb; +static struct settings_handler net_config_settings_handler = { + .name = SETTINGS_SUBTREE_NET_CONFIG, + .h_get = net_config_settings_get, + .h_set = net_config_settings_set, + .h_commit = net_config_settings_commit, + .h_export = net_config_settings_export +}; -static void ipv4_addr_add_handler(struct net_mgmt_event_callback *cb, - uint64_t mgmt_event, - struct net_if *iface) +static int net_config_settings_set(const char *name, size_t len, + settings_read_cb read_cb, void *cb_arg) { - if (mgmt_event == NET_EVENT_IPV4_ADDR_ADD) { - print_dhcpv4_info(iface); + const char *next; + int ret; - if (!IS_ENABLED(CONFIG_NET_IPV4_ACD)) { - services_notify_ready(NET_CONFIG_NEED_IPV4); + if (settings_name_steq(name, "user", &next) && next == NULL) { + if (len != sizeof(struct net_init_config)) { + NET_ERR("Error net_init_config too large %zu (expecting %zu)", + len, sizeof(struct net_init_config)); + return -EINVAL; } - } - if (mgmt_event == NET_EVENT_IPV4_ACD_SUCCEED) { - services_notify_ready(NET_CONFIG_NEED_IPV4); - } -} - -#if defined(CONFIG_NET_VLAN) && (CONFIG_NET_CONFIG_MY_VLAN_ID > 0) - -static void setup_vlan(struct net_if *iface) -{ - int ret = net_eth_vlan_enable(iface, CONFIG_NET_CONFIG_MY_VLAN_ID); - - if (ret < 0) { - NET_ERR("Network interface %d (%p): cannot set VLAN tag (%d)", - net_if_get_by_iface(iface), iface, ret); - } -} - -#else -#define setup_vlan(...) -#endif /* CONFIG_NET_VLAN && (CONFIG_NET_CONFIG_MY_VLAN_ID > 0) */ - -#if defined(CONFIG_NET_NATIVE_IPV4) && !defined(CONFIG_NET_DHCPV4) && \ - !defined(CONFIG_NET_CONFIG_MY_IPV4_ADDR) -#error "You need to define an IPv4 address or enable DHCPv4!" -#endif - -static void setup_ipv4(struct net_if *iface) -{ -#if CONFIG_NET_CONFIG_LOG_LEVEL >= LOG_LEVEL_INF - char hr_addr[NET_IPV4_ADDR_LEN]; -#endif - struct in_addr addr, netmask; - - if (IS_ENABLED(CONFIG_NET_IPV4_ACD) || IS_ENABLED(CONFIG_NET_DHCPV4)) { - net_mgmt_init_event_callback(&mgmt4_cb, ipv4_addr_add_handler, - NET_EVENT_IPV4_ADDR_ADD | - NET_EVENT_IPV4_ACD_SUCCEED); - net_mgmt_add_event_callback(&mgmt4_cb); - } - - if (sizeof(CONFIG_NET_CONFIG_MY_IPV4_ADDR) == 1) { - /* Empty address, skip setting ANY address in this case */ - return; - } - - if (net_addr_pton(AF_INET, CONFIG_NET_CONFIG_MY_IPV4_ADDR, &addr)) { - NET_ERR("Invalid address: %s", CONFIG_NET_CONFIG_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 CONFIG_NET_CONFIG_LOG_LEVEL >= LOG_LEVEL_INF - NET_INFO("IPv4 address: %s", - net_addr_ntop(AF_INET, &addr, hr_addr, sizeof(hr_addr))); -#endif - - if (sizeof(CONFIG_NET_CONFIG_MY_IPV4_NETMASK) > 1) { - /* If not empty */ - if (net_addr_pton(AF_INET, CONFIG_NET_CONFIG_MY_IPV4_NETMASK, - &netmask)) { - NET_ERR("Invalid netmask: %s", - CONFIG_NET_CONFIG_MY_IPV4_NETMASK); - } else { - net_if_ipv4_set_netmask_by_addr(iface, &addr, &netmask); + ret = read_cb(cb_arg, &net_init_config_user, + sizeof(net_init_config_user)); + if (ret == 0) { + /* The key is deleted */ + memset(&net_init_config_user, 0, sizeof(net_init_config_user)); + return 0; + } else if (ret > 0) { + return 0; } - } - if (sizeof(CONFIG_NET_CONFIG_MY_IPV4_GW) > 1) { - /* If not empty */ - if (net_addr_pton(AF_INET, CONFIG_NET_CONFIG_MY_IPV4_GW, - &addr)) { - NET_ERR("Invalid gateway: %s", - CONFIG_NET_CONFIG_MY_IPV4_GW); - } else { - net_if_ipv4_set_gw(iface, &addr); - } - } + NET_ERR("Error code list read failure: %d", ret); - if (!IS_ENABLED(CONFIG_NET_IPV4_ACD)) { - services_notify_ready(NET_CONFIG_NEED_IPV4); + return ret; } -} - -#else -#define setup_ipv4(...) -#define setup_dhcpv4(...) -#define setup_vlan(...) -#endif /* CONFIG_NET_NATIVE_IPV4*/ -#if defined(CONFIG_NET_NATIVE_IPV6) + return -ENOENT; +} -#if defined(CONFIG_NET_DHCPV6) -static void setup_dhcpv6(struct net_if *iface) +static int net_config_settings_commit(void) { - struct net_dhcpv6_params params = { - .request_addr = IS_ENABLED(CONFIG_NET_CONFIG_DHCPV6_REQUEST_ADDR), - .request_prefix = IS_ENABLED(CONFIG_NET_CONFIG_DHCPV6_REQUEST_PREFIX), - }; - - NET_INFO("Running dhcpv6 client..."); - - net_dhcpv6_start(iface, ¶ms); + NET_DBG("loading all settings under <%s> handler is done", + SETTINGS_SUBTREE_NET_CONFIG); + return 0; } -#else /* CONFIG_NET_DHCPV6 */ -#define setup_dhcpv6(...) -#endif /* CONFIG_NET_DHCPV6 */ -#if !defined(CONFIG_NET_CONFIG_DHCPV6_REQUEST_ADDR) && \ - !defined(CONFIG_NET_CONFIG_MY_IPV6_ADDR) -#error "You need to define an IPv6 address or enable DHCPv6!" -#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, - uint64_t mgmt_event, struct net_if *iface) +static int net_config_settings_get(const char *name, char *val, int val_len_max) { - 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)); - break; - } - } - } - - if (mgmt_event == NET_EVENT_IPV6_DAD_SUCCEED) { -#if CONFIG_NET_CONFIG_LOG_LEVEL >= LOG_LEVEL_INF - char hr_addr[NET_IPV6_ADDR_LEN]; -#endif - struct net_if_addr *ifaddr; + const char *next; - 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 (settings_name_steq(name, "user", &next) && !next) { + if (val_len_max != sizeof(struct net_init_config)) { + NET_ERR("Error net_init_config too large %zu (expecting %zu)", + (size_t)val_len_max, sizeof(struct net_init_config)); + return -EINVAL; } -#if CONFIG_NET_CONFIG_LOG_LEVEL >= LOG_LEVEL_INF - NET_INFO("IPv6 address: %s", - net_addr_ntop(AF_INET6, &laddr, hr_addr, NET_IPV6_ADDR_LEN)); - - if (ifaddr->addr_type == NET_ADDR_DHCP) { - char remaining_str[] = "infinite"; - uint32_t remaining; - - remaining = net_timeout_remaining(&ifaddr->lifetime, - k_uptime_get_32()); - - if (!ifaddr->is_infinite) { - snprintk(remaining_str, sizeof(remaining_str), - "%u", remaining); - } - - NET_INFO("Lifetime: %s seconds", remaining_str); - } -#endif + memcpy((struct net_init_config *)val, &net_init_config_user, val_len_max); - services_notify_ready(NET_CONFIG_NEED_IPV6); + return val_len_max; } - if (mgmt_event == NET_EVENT_IPV6_ROUTER_ADD) { - services_notify_ready(NET_CONFIG_NEED_ROUTER); - } + return -ENOENT; } -static void setup_ipv6(struct net_if *iface, uint32_t flags) +static int net_config_settings_export(int (*cb)(const char *name, + const void *value, size_t val_len)) { - struct net_if_addr *ifaddr; - uint64_t mask = NET_EVENT_IPV6_DAD_SUCCEED; - - if (sizeof(CONFIG_NET_CONFIG_MY_IPV6_ADDR) == 1) { - /* Empty address, skip setting ANY address in this case */ - goto exit; - } - - if (net_addr_pton(AF_INET6, CONFIG_NET_CONFIG_MY_IPV6_ADDR, &laddr)) { - NET_ERR("Invalid address: %s", CONFIG_NET_CONFIG_MY_IPV6_ADDR); - /* some interfaces may add IP address later */ - mask |= NET_EVENT_IPV6_ADDR_ADD; - } - - if (flags & NET_CONFIG_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); - - ifaddr = net_if_ipv6_addr_add(iface, &laddr, NET_ADDR_MANUAL, 0); - if (!ifaddr) { - NET_ERR("Cannot add %s to interface", - CONFIG_NET_CONFIG_MY_IPV6_ADDR); - } + NET_DBG("export keys under <%s> handler", SETTINGS_SUBTREE_NET_CONFIG); -exit: + (void)cb(SETTINGS_SUBTREE_NET_CONFIG "/user", &net_init_config_user, + sizeof(net_init_config_user)); - if (!IS_ENABLED(CONFIG_NET_IPV6_DAD) || - net_if_flag_is_set(iface, NET_IF_IPV6_NO_ND)) { - services_notify_ready(NET_CONFIG_NEED_IPV6); - } - - return; + return 0; } - -#else -#define setup_ipv6(...) -#define setup_dhcpv6(...) -#endif /* CONFIG_NET_IPV6 */ +#endif /* CONFIG_SETTINGS */ #if defined(CONFIG_NET_NATIVE) +static struct net_mgmt_event_callback mgmt_iface_cb; + static void iface_up_handler(struct net_mgmt_event_callback *cb, uint64_t mgmt_event, struct net_if *iface) { @@ -443,123 +239,1649 @@ int net_config_init_by_iface(struct net_if *iface, const char *app_info, #endif } - setup_vlan(iface); - setup_ipv4(iface); - setup_dhcpv4(iface); - setup_ipv6(iface, flags); - setup_dhcpv6(iface); - /* Network interface did not come up. */ if (timeout > 0 && count < 0) { NET_ERR("Timeout while waiting network %s", "interface"); return -ENETDOWN; } - /* Loop here until we are ready to continue. As we might need - * to wait multiple events, sleep smaller amounts of data. - */ - while (!services_are_ready(flags) && count-- > 0) { - k_sem_take(&waiter, K_MSEC(loop)); + return 0; +} + +static struct net_if *get_interface(const struct net_init_config *config, + int config_ifindex, + const struct device *dev, + const char **iface_name) +{ + const struct net_init_config_network_interfaces *cfg; + struct net_if *iface = NULL; + const char *name; + + NET_ASSERT(IN_RANGE(config_ifindex, 0, ARRAY_SIZE(config->network_interfaces) - 1)); + + cfg = &config->network_interfaces[config_ifindex]; + + name = cfg->set_name; + if (name != NULL) { + iface = net_if_get_by_index(net_if_get_by_name(name)); + } + + if (iface == NULL) { + name = cfg->name; + + if (name != NULL) { + iface = net_if_get_by_index(net_if_get_by_name(name)); + } } - if (count == -1 && timeout > 0) { - NET_ERR("Timeout while waiting network %s", "setup"); - return -ETIMEDOUT; + if (iface == NULL) { + /* Get the interface by device */ + const struct device *iface_dev; + + name = cfg->device_name; + + iface_dev = device_get_binding(name); + if (iface_dev) { + iface = net_if_lookup_by_dev(iface_dev); + } } - return 0; + /* Use the default interface if nothing is found */ + if (iface == NULL && name == NULL) { + static char ifname[CONFIG_NET_INTERFACE_NAME_LEN + 1]; + + iface = net_if_get_default(); + (void)net_if_get_name(iface, ifname, sizeof(ifname)); + name = ifname; + } + + if (iface_name != NULL) { + *iface_name = name; + } + + return iface; } -int net_config_init(const char *app_info, uint32_t flags, - int32_t timeout) +#if defined(CONFIG_NET_IPV6) || defined(CONFIG_NET_IPV4) +static bool parse_mask(const char *str, size_t str_len, + struct sockaddr *addr, uint8_t *mask_len) { - return net_config_init_by_iface(NULL, app_info, flags, timeout); + const char *result; + + result = net_ipaddr_parse_mask(str, str_len, addr, mask_len); + if (result == NULL || result[0] != '\0') { + return false; + } + + return true; } +#endif /* CONFIG_NET_IPV6 || CONFIG_NET_IPV4 */ -static void iface_find_cb(struct net_if *iface, void *user_data) +static void ipv6_setup(struct net_if *iface, + int ifindex, + const struct net_init_config_network_interfaces *cfg) { - struct net_if **iface_to_use = user_data; +#if defined(CONFIG_NET_IPV6) + const struct net_init_config_ipv6 *ipv6 = &cfg->ipv6; + struct net_if_mcast_addr *ifmaddr; + struct net_if_addr *ifaddr; + bool ret; - if (*iface_to_use == NULL && - !net_if_flag_is_set(iface, NET_IF_NO_AUTO_START)) { - *iface_to_use = iface; + if (!ipv6->status) { + NET_DBG("Skipping IPv%c setup for iface %d", '6', ifindex); + net_if_flag_clear(iface, NET_IF_IPV6); return; } -} -int net_config_init_app(const struct device *dev, const char *app_info) -{ - struct net_if *iface = NULL; - uint32_t flags = 0U; - int ret; + /* First set all the static addresses and then enable DHCP */ + ARRAY_FOR_EACH(ipv6->ipv6_addresses, j) { + struct sockaddr_in6 addr = { 0 }; + uint8_t prefix_len; - if (dev) { - iface = net_if_lookup_by_dev(dev); - if (iface == NULL) { - NET_WARN("No interface for device %p, using default", - dev); + if (ipv6->ipv6_addresses[j].value == NULL || + ipv6->ipv6_addresses[j].value[0] == '\0') { + continue; } - } - ret = z_net_config_ieee802154_setup(iface); - if (ret < 0) { - NET_ERR("Cannot setup IEEE 802.15.4 interface (%d)", ret); + ret = parse_mask(ipv6->ipv6_addresses[j].value, + strlen(ipv6->ipv6_addresses[j].value), + (struct sockaddr *)&addr, &prefix_len); + if (!ret) { + NET_DBG("Invalid IPv%c %s address \"%s\"", '6', "unicast", + ipv6->ipv6_addresses[j].value); + continue; + } + + if (net_ipv6_is_addr_unspecified(&addr.sin6_addr)) { + continue; + } + + ifaddr = net_if_ipv6_addr_add( + iface, + &addr.sin6_addr, + /* If DHCPv6 is enabled, then allow address + * to be overridden. + */ + COND_CODE_1(CONFIG_NET_DHCPV6, + (ipv6->dhcpv6.status), + (false)) ? + NET_ADDR_OVERRIDABLE : NET_ADDR_MANUAL, + 0); + if (ifaddr == NULL) { + NET_DBG("Cannot %s %s %s to iface %d", "add", "address", + net_sprint_ipv6_addr(&addr.sin6_addr), + ifindex); + continue; + } + + NET_DBG("Added %s address %s to iface %d", "unicast", + net_sprint_ipv6_addr(&addr.sin6_addr), + ifindex); } - /* Only try to use a network interface that is auto started */ - if (iface == NULL) { - net_if_foreach(iface_find_cb, &iface); + ARRAY_FOR_EACH(ipv6->ipv6_multicast_addresses, j) { + struct sockaddr_in6 addr = { 0 }; + + if (ipv6->ipv6_multicast_addresses[j].value == NULL || + ipv6->ipv6_multicast_addresses[j].value[0] == '\0') { + continue; + } + + ret = parse_mask(ipv6->ipv6_multicast_addresses[j].value, + strlen(ipv6->ipv6_multicast_addresses[j].value), + (struct sockaddr *)&addr, NULL); + if (!ret) { + NET_DBG("Invalid IPv%c %s address \"%s\"", '6', "multicast", + ipv6->ipv6_multicast_addresses[j].value); + continue; + } + + if (net_ipv6_is_addr_unspecified(&addr.sin6_addr)) { + continue; + } + + ifmaddr = net_if_ipv6_maddr_add(iface, &addr.sin6_addr); + if (ifmaddr == NULL) { + NET_DBG("Cannot %s %s %s to iface %d", "add", "address", + net_sprint_ipv6_addr(&addr.sin6_addr), + ifindex); + continue; + } + + NET_DBG("Added %s address %s to iface %d", "multicast", + net_sprint_ipv6_addr(&addr.sin6_addr), + ifindex); } - if (!iface) { - NET_WARN("No auto-started network interface - " - "network-bound app initialization skipped."); - return 0; + ARRAY_FOR_EACH(ipv6->prefixes, j) { + struct net_if_ipv6_prefix *prefix; + struct sockaddr_in6 addr = { 0 }; + + if (ipv6->prefixes[j].address == NULL || + ipv6->prefixes[j].address[0] == '\0') { + continue; + } + + ret = parse_mask(ipv6->prefixes[j].address, + strlen(ipv6->prefixes[j].address), + (struct sockaddr *)&addr, NULL); + if (!ret) { + NET_DBG("Invalid IPv%c %s address \"%s\"", '6', "prefix", + ipv6->prefixes[j].address); + continue; + } + + if (net_ipv6_is_addr_unspecified(&addr.sin6_addr)) { + continue; + } + + prefix = net_if_ipv6_prefix_add(iface, + &addr.sin6_addr, + ipv6->prefixes[j].len, + ipv6->prefixes[j].lifetime); + if (prefix == NULL) { + NET_DBG("Cannot %s %s %s to iface %d", "add", "prefix", + net_sprint_ipv6_addr(&addr.sin6_addr), + ifindex); + continue; + } + + NET_DBG("Added %s address %s to iface %d", "prefix", + net_sprint_ipv6_addr(&addr.sin6_addr), + ifindex); } - if (IS_ENABLED(CONFIG_NET_CONFIG_NEED_IPV6)) { - flags |= NET_CONFIG_NEED_IPV6; + if (ipv6->hop_limit > 0) { + net_if_ipv6_set_hop_limit(iface, ipv6->hop_limit); } - if (IS_ENABLED(CONFIG_NET_CONFIG_NEED_IPV6_ROUTER)) { - flags |= NET_CONFIG_NEED_ROUTER; + if (ipv6->multicast_hop_limit > 0) { + net_if_ipv6_set_mcast_hop_limit(iface, ipv6->multicast_hop_limit); } - if (IS_ENABLED(CONFIG_NET_CONFIG_NEED_IPV4)) { - flags |= NET_CONFIG_NEED_IPV4; + if (COND_CODE_1(CONFIG_NET_DHCPV6, (ipv6->dhcpv6.status), (false))) { + struct net_dhcpv6_params params = { + .request_addr = COND_CODE_1(CONFIG_NET_DHCPV6, + (ipv6->dhcpv6.do_request_address), + (false)), + .request_prefix = COND_CODE_1(CONFIG_NET_DHCPV6, + (ipv6->dhcpv6.do_request_prefix), + (false)), + }; + + net_dhcpv6_start(iface, ¶ms); } +#endif +} - /* Initialize the application automatically if needed */ - ret = net_config_init_by_iface(iface, app_info, flags, - CONFIG_NET_CONFIG_INIT_TIMEOUT * MSEC_PER_SEC); - if (ret < 0) { - NET_ERR("Network initialization failed (%d)", ret); +static void ipv4_setup(struct net_if *iface, + int ifindex, + const struct net_init_config_network_interfaces *cfg) +{ +#if defined(CONFIG_NET_IPV4) + const struct net_init_config_ipv4 *ipv4 = &cfg->ipv4; + struct net_if_addr *ifaddr; + struct net_if_mcast_addr *ifmaddr; + bool ret; + + if (!ipv4->status) { + NET_DBG("Skipping IPv%c setup for iface %d", '4', ifindex); + net_if_flag_clear(iface, NET_IF_IPV4); + return; } - if (IS_ENABLED(CONFIG_NET_CONFIG_CLOCK_SNTP_INIT) && - !IS_ENABLED(CONFIG_NET_CONFIG_SNTP_INIT_USE_CONNECTION_MANAGER)) { - net_init_clock_via_sntp(); + /* First set all the static addresses and then enable DHCP */ + ARRAY_FOR_EACH(ipv4->ipv4_addresses, j) { + struct sockaddr_in addr = { 0 }; + uint8_t mask_len = 0; + + if (ipv4->ipv4_addresses[j].value == NULL || + ipv4->ipv4_addresses[j].value[0] == '\0') { + continue; + } + + ret = parse_mask(ipv4->ipv4_addresses[j].value, + strlen(ipv4->ipv4_addresses[j].value), + (struct sockaddr *)&addr, &mask_len); + if (!ret) { + NET_DBG("Invalid IPv%c %s address \"%s\"", '4', "unicast", + ipv4->ipv4_addresses[j].value); + continue; + } + + if (net_ipv4_is_addr_unspecified(&addr.sin_addr)) { + continue; + } + + ifaddr = net_if_ipv4_addr_add( + iface, + &addr.sin_addr, + /* If DHCPv4 is enabled, then allow address + * to be overridden. + */ + COND_CODE_1(CONFIG_NET_DHCPV4, + (ipv4->dhcpv4.status), + (false)) ? + NET_ADDR_OVERRIDABLE : NET_ADDR_MANUAL, + 0); + + if (ifaddr == NULL) { + NET_DBG("Cannot %s %s %s to iface %d", "add", "address", + net_sprint_ipv4_addr(&addr.sin_addr), + ifindex); + continue; + } + + /* Wait until Address Conflict Detection is ok. + * DHCPv4 server startup will fail if the address is not in + * preferred state. + */ + if (IS_ENABLED(CONFIG_NET_IPV4_ACD) && + (COND_CODE_1(CONFIG_NET_DHCPV4_SERVER, + (ipv4->dhcpv4_server.status), (false)))) { + if (WAIT_FOR(ifaddr->addr_state == NET_ADDR_PREFERRED, + USEC_PER_MSEC * MSEC_PER_SEC * 2 /* 2 sec */, + k_msleep(100)) == false) { + NET_DBG("Address %s still is not preferred", + net_sprint_ipv4_addr(&addr.sin_addr)); + } + } + + NET_DBG("Added %s address %s to iface %d", "unicast", + net_sprint_ipv4_addr(&addr.sin_addr), + ifindex); + + if (mask_len > 0) { + struct in_addr netmask = { 0 }; + + netmask.s_addr = BIT_MASK(mask_len); + + net_if_ipv4_set_netmask_by_addr(iface, + &addr.sin_addr, + &netmask); + + NET_DBG("Added %s address %s to iface %d", "netmask", + net_sprint_ipv4_addr(&netmask), + ifindex); + } } - /* This is activated late as it requires the network stack to be up - * and running before syslog messages can be sent to network. - */ - if (IS_ENABLED(CONFIG_LOG_BACKEND_NET_AUTOSTART) && - !IS_ENABLED(CONFIG_LOG_BACKEND_NET_USE_CONNECTION_MANAGER)) { - log_backend_net_start(); + ARRAY_FOR_EACH(ipv4->ipv4_multicast_addresses, j) { + struct sockaddr_in addr = { 0 }; + + if (ipv4->ipv4_multicast_addresses[j].value == NULL || + ipv4->ipv4_multicast_addresses[j].value[0] == '\0') { + continue; + } + + ret = parse_mask(ipv4->ipv4_multicast_addresses[j].value, + strlen(ipv4->ipv4_multicast_addresses[j].value), + (struct sockaddr *)&addr, NULL); + if (!ret) { + NET_DBG("Invalid IPv%c %s address \"%s\"", '4', "multicast", + ipv4->ipv4_addresses[j].value); + continue; + } + + if (net_ipv4_is_addr_unspecified(&addr.sin_addr)) { + continue; + } + + ifmaddr = net_if_ipv4_maddr_add(iface, &addr.sin_addr); + if (ifmaddr == NULL) { + NET_DBG("Cannot %s %s %s to iface %d", "add", "address", + net_sprint_ipv4_addr(&addr.sin_addr), + ifindex); + continue; + } + + NET_DBG("Added %s address %s to iface %d", "multicast", + net_sprint_ipv4_addr(&addr.sin_addr), + ifindex); } - return ret; -} + if (ipv4->time_to_live > 0) { + net_if_ipv4_set_ttl(iface, ipv4->time_to_live); + } -#if defined(CONFIG_NET_CONFIG_AUTO_INIT) -static int init_app(void) -{ + if (ipv4->multicast_time_to_live > 0) { + net_if_ipv4_set_mcast_ttl(iface, ipv4->multicast_time_to_live); + } - (void)net_config_init_app(NULL, "Initializing network"); + if (ipv4->gateway != NULL && ipv4->gateway[0] != '\0') { + struct sockaddr_in addr = { 0 }; - return 0; -} + ret = parse_mask(ipv4->gateway, + strlen(ipv4->gateway), + (struct sockaddr *)&addr, NULL); + if (!ret) { + NET_DBG("Invalid IPv%c %s address \"%s\"", '4', "geteway", + ipv4->gateway); + } else { + if (!net_ipv4_is_addr_unspecified(&addr.sin_addr)) { + net_if_ipv4_set_gw(iface, &addr.sin_addr); + + NET_DBG("Added %s address %s to iface %d", "gateway", + net_sprint_ipv4_addr(&addr.sin_addr), + ifindex); + } + } + } + + if (COND_CODE_1(CONFIG_NET_DHCPV4, (ipv4->dhcpv4.status), (false))) { + NET_DBG("DHCPv4 client started"); + net_dhcpv4_start(iface); + } + + if (COND_CODE_1(CONFIG_NET_DHCPV4_SERVER, + (ipv4->dhcpv4_server.status), (false))) { + struct sockaddr_in addr = { 0 }; + + if (ipv4->dhcpv4_server.base_address != NULL) { + ret = parse_mask(ipv4->dhcpv4_server.base_address, + strlen(ipv4->dhcpv4_server.base_address), + (struct sockaddr *)&addr, NULL); + if (!ret) { + NET_DBG("Invalid IPv%c %s address \"%s\"", '4', "DHCPv4 base", + ipv4->dhcpv4_server.base_address); + } else { + int retval; + + retval = net_dhcpv4_server_start(iface, + COND_CODE_1(CONFIG_NET_DHCPV4_SERVER, + (&addr.sin_addr), + (&((struct in_addr){ 0 })))); + if (retval < 0) { + NET_DBG("DHCPv4 server start failed (%d)", retval); + } else { + NET_DBG("DHCPv4 server started"); + } + } + } + } + + if (COND_CODE_1(CONFIG_NET_IPV4_AUTO, (ipv4->ipv4_autoconf.status), (false))) { + NET_DBG("IPv4 autoconf started"); + net_ipv4_autoconf_start(iface); + } +#endif +} + +static void vlan_setup(const struct net_init_config *config, + const struct net_init_config_network_interfaces *cfg) +{ +#if defined(CONFIG_NET_VLAN) + const struct net_init_config_vlan *vlan = &cfg->vlan; + struct net_if *bound = NULL; + int ret, ifindex; + + if (!vlan->status) { + return; + } + + bound = get_interface(config, cfg->bind_to - 1, NULL, NULL); + if (bound == NULL) { + NET_DBG("Cannot find VLAN bound interface %d", cfg->bind_to - 1); + return; + } + + ifindex = net_if_get_by_iface(bound); + + ret = net_eth_vlan_enable(bound, vlan->tag); + if (ret < 0) { + NET_DBG("Cannot %s %s %s to iface %d", "add", "VLAN", "tag", ifindex); + NET_DBG("Cannot enable %s for %s %d (%d)", "VLAN", "tag", vlan->tag, ret); + return; + } + + NET_DBG("Added %s %s %d to iface %d", "VLAN", "tag", vlan->tag, + net_if_get_by_iface(net_eth_get_vlan_iface( + net_if_get_by_index(ifindex), + vlan->tag))); + + if (cfg->set_name != NULL) { + struct net_if *iface; + + iface = net_eth_get_vlan_iface(bound, vlan->tag); + + ret = net_if_set_name(iface, cfg->set_name); + if (ret < 0) { + NET_DBG("Cannot rename interface %d to \"%s\" (%d)", + ifindex, cfg->set_name, ret); + return; + } + + NET_DBG("Changed interface %d name to \"%s\"", ifindex, + cfg->set_name); + } +#endif +} + +static void virtual_iface_setup(struct net_if *iface, + int ifindex, + const struct net_init_config *config, + const struct net_init_config_network_interfaces *cfg) +{ +#if defined(CONFIG_NET_L2_VIRTUAL) + const struct virtual_interface_api *api = net_if_get_device(iface)->api; + struct net_if *bound = NULL; + int ret; + + if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) { + return; + } + + /* VLAN interfaces are handled separately */ + if (api->get_capabilities(iface) & VIRTUAL_INTERFACE_VLAN) { + return; + } + + if (cfg->bind_to > 0) { + bound = get_interface(config, cfg->bind_to - 1, NULL, NULL); + } + + if (bound == NULL) { + NET_DBG("Cannot %s %s %s to iface %d", "add", "virtual", "interface", + cfg->bind_to - 1); + return; + } + + ret = net_virtual_interface_attach(iface, bound); + if (ret < 0) { + if (ret != -EALREADY) { + NET_DBG("Cannot %s %s %s to iface %d (%d)", "attach", + "virtual", "interface", + net_if_get_by_iface(bound), ret); + } + + return; + } + + NET_DBG("Added %s %s %d to iface %d", "virtual", "interface", ifindex, + net_if_get_by_iface(bound)); +#endif +} + +static void iface_find_cb(struct net_if *iface, void *user_data) +{ + struct net_if **iface_to_use = user_data; + + if (*iface_to_use == NULL && + !net_if_flag_is_set(iface, NET_IF_NO_AUTO_START)) { + *iface_to_use = iface; + return; + } +} + +static int wait_for_interface(const struct net_init_config_network_interfaces *ifaces, + size_t iface_count) +{ + struct net_if *iface = NULL; + + ARG_UNUSED(ifaces); + ARG_UNUSED(iface_count); + + net_if_foreach(iface_find_cb, &iface); + + if (!iface) { + NET_WARN("No auto-started network interface - " + "network-bound app initialization skipped."); + return 0; + } + + /* TODO: implement waiting of interface(s) */ + return 0; +} + +struct iface_str_flags { + enum net_if_flag flag; + const char * const str; +}; + +#define FLAG(val) { .flag = val, .str = STRINGIFY(val) } + +#if defined(CONFIG_NET_TEST) +enum net_if_flag get_iface_flag(const char *flag_str, bool *clear) +#else +static enum net_if_flag get_iface_flag(const char *flag_str, bool *clear) +#endif +{ + static const struct iface_str_flags flags[] = { + FLAG(NET_IF_POINTOPOINT), + FLAG(NET_IF_PROMISC), + FLAG(NET_IF_NO_AUTO_START), + FLAG(NET_IF_FORWARD_MULTICASTS), + FLAG(NET_IF_IPV6_NO_ND), + FLAG(NET_IF_IPV6_NO_MLD), + { 0, NULL } + }; + + if (flag_str == NULL || flag_str[0] == '\0') { + return NET_IF_NUM_FLAGS; + } + + if (flag_str[0] == '^') { + *clear = true; + } else { + *clear = false; + } + + ARRAY_FOR_EACH(flags, i) { + if (strcmp(flags[i].str, &flag_str[(*clear) ? 1 : 0]) == 0) { + return flags[i].flag; + } + } + + return NET_IF_NUM_FLAGS; +} + +static int process_iface_flag(struct net_if *iface, const char *flag_str) +{ + enum net_if_flag flag; + bool clear; + + flag = get_iface_flag(flag_str, &clear); + if (flag == NET_IF_NUM_FLAGS) { + return -ENOENT; + } + + if (clear) { + NET_DBG("%s flag %s for interface %d", "Clear", + flag_str + 1, net_if_get_by_iface(iface)); + net_if_flag_clear(iface, flag); + } else { + NET_DBG("%s flag %s for interface %d", "Set", + flag_str, net_if_get_by_iface(iface)); + net_if_flag_set(iface, flag); + } + + return 0; +} + +#define UPDATE_IFACE_VAL(x) \ + if (iface_cfg->__##x##_changed) { \ + cfg->network_interfaces[i].x = \ + net_init_config_user.network_interfaces[i].x; \ + cfg->network_interfaces[i].__##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].x = \ + config->network_interfaces[i].x; \ + cfg->network_interfaces[i].__##x##_changed = false; \ + } + +#define UPDATE_FLAGS_VAL(x) \ + if (iface_cfg->flags[j].__##x##_changed) { \ + cfg->network_interfaces[i].flags[j].x = \ + net_init_config_user.network_interfaces[i].flags[j].x; \ + cfg->network_interfaces[i].flags[j].__##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].flags[j].x = \ + config->network_interfaces[i].flags[j].x; \ + cfg->network_interfaces[i].flags[j].__##x##_changed = false; \ + } + +#define UPDATE_IPV6_VAL(x) \ + if (iface_cfg->ipv6.__##x##_changed) { \ + cfg->network_interfaces[i].ipv6.x = \ + net_init_config_user.network_interfaces[i].ipv6.x; \ + cfg->network_interfaces[i].ipv6.__##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].ipv6.x = \ + config->network_interfaces[i].ipv6.x; \ + cfg->network_interfaces[i].ipv6.__##x##_changed = false; \ + } + +#define UPDATE_IPV6_ADDR_VAL(x) \ + if (iface_cfg->ipv6.ipv6_addresses[j].__##x##_changed) { \ + cfg->network_interfaces[i].ipv6.ipv6_addresses[j].x = \ + net_init_config_user.network_interfaces[i].ipv6.ipv6_addresses[j].x; \ + cfg->network_interfaces[i].ipv6.ipv6_addresses[j].__##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].ipv6.ipv6_addresses[j].x = \ + config->network_interfaces[i].ipv6.ipv6_addresses[j].x; \ + cfg->network_interfaces[i].ipv6.ipv6_addresses[j].__##x##_changed = false; \ + } + +#define UPDATE_IPV6_MADDR_VAL(x) \ + if (iface_cfg->ipv6.ipv6_multicast_addresses[j].__##x##_changed) { \ + cfg->network_interfaces[i].ipv6.ipv6_multicast_addresses[j].x = \ + net_init_config_user.network_interfaces[i].ipv6.ipv6_multicast_addresses[j].x; \ + cfg->network_interfaces[i].ipv6.ipv6_multicast_addresses[j].\ + __##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].ipv6.ipv6_multicast_addresses[j].x = \ + config->network_interfaces[i].ipv6.ipv6_multicast_addresses[j].x; \ + cfg->network_interfaces[i].ipv6.ipv6_multicast_addresses[j].\ + __##x##_changed = false; \ + } + +#define UPDATE_IPV6_PREFIX_VAL(x) \ + if (iface_cfg->ipv6.prefixes[j].__##x##_changed) { \ + cfg->network_interfaces[i].ipv6.prefixes[j].x = \ + net_init_config_user.network_interfaces[i].ipv6.prefixes[j].x; \ + cfg->network_interfaces[i].ipv6.prefixes[j].__##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].ipv6.prefixes[j].x = \ + config->network_interfaces[i].ipv6.prefixes[j].x; \ + cfg->network_interfaces[i].ipv6.prefixes[j].__##x##_changed = false; \ + } + +#define UPDATE_IPV6_DHCPV6_VAL(x) \ + if (iface_cfg->ipv6.dhcpv6.__##x##_changed) { \ + cfg->network_interfaces[i].ipv6.dhcpv6.x = \ + net_init_config_user.network_interfaces[i].ipv6.dhcpv6.x; \ + cfg->network_interfaces[i].ipv6.dhcpv6.__##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].ipv6.dhcpv6.x = \ + config->network_interfaces[i].ipv6.dhcpv6.x; \ + cfg->network_interfaces[i].ipv6.dhcpv6.__##x##_changed = false; \ + } + +#define UPDATE_IPV4_AUTOCONF_VAL(x) \ + if (iface_cfg->ipv4.ipv4_autoconf.__##x##_changed) { \ + cfg->network_interfaces[i].ipv4.ipv4_autoconf.x = \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_autoconf.x; \ + cfg->network_interfaces[i].ipv4.ipv4_autoconf.__##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].ipv4.ipv4_autoconf.x = \ + config->network_interfaces[i].ipv4.ipv4_autoconf.x; \ + cfg->network_interfaces[i].ipv4.ipv4_autoconf.__##x##_changed = false; \ + } + +#define UPDATE_IPV4_DHCPV4_VAL(x) \ + if (iface_cfg->ipv4.dhcpv4.__##x##_changed) { \ + cfg->network_interfaces[i].ipv4.dhcpv4.x = \ + net_init_config_user.network_interfaces[i].ipv4.dhcpv4.x; \ + cfg->network_interfaces[i].ipv4.dhcpv4.__##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].ipv4.dhcpv4.x = \ + config->network_interfaces[i].ipv4.dhcpv4.x; \ + cfg->network_interfaces[i].ipv4.dhcpv4.__##x##_changed = false; \ + } + +#define UPDATE_IPV4_VAL(x) \ + if (iface_cfg->ipv4.__##x##_changed) { \ + cfg->network_interfaces[i].ipv4.x = \ + net_init_config_user.network_interfaces[i].ipv4.x; \ + cfg->network_interfaces[i].ipv4.__##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].ipv4.x = \ + config->network_interfaces[i].ipv4.x; \ + cfg->network_interfaces[i].ipv4.__##x##_changed = false; \ + } + +#define UPDATE_IPV4_ADDR_VAL(x) \ + if (iface_cfg->ipv4.ipv4_addresses[j].__##x##_changed) { \ + cfg->network_interfaces[i].ipv4.ipv4_addresses[j].x = \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_addresses[j].x; \ + cfg->network_interfaces[i].ipv4.ipv4_addresses[j].__##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].ipv4.ipv4_addresses[j].x = \ + config->network_interfaces[i].ipv4.ipv4_addresses[j].x; \ + cfg->network_interfaces[i].ipv4.ipv4_addresses[j].__##x##_changed = false; \ + } + +#define UPDATE_IPV4_MADDR_VAL(x) \ + if (iface_cfg->ipv4.ipv4_multicast_addresses[j].__##x##_changed) { \ + cfg->network_interfaces[i].ipv4.ipv4_multicast_addresses[j].x = \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_multicast_addresses[j].x; \ + cfg->network_interfaces[i].ipv4.ipv4_multicast_addresses[j].\ + __##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].ipv4.ipv4_multicast_addresses[j].x = \ + config->network_interfaces[i].ipv4.ipv4_multicast_addresses[j].x; \ + cfg->network_interfaces[i].ipv4.ipv4_multicast_addresses[j].\ + __##x##_changed = false; \ + } + +#define UPDATE_IPV4_DHCPV4_SERVER_VAL(x) \ + if (iface_cfg->ipv4.dhcpv4_server.__##x##_changed) { \ + cfg->network_interfaces[i].ipv4.dhcpv4_server.x = \ + net_init_config_user.network_interfaces[i].ipv4.dhcpv4_server.x; \ + cfg->network_interfaces[i].ipv4.dhcpv4_server.__##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].ipv4.dhcpv4_server.x = \ + config->network_interfaces[i].ipv4.dhcpv4_server.x; \ + cfg->network_interfaces[i].ipv4.dhcpv4_server.__##x##_changed = false; \ + } + +#define UPDATE_IFACE_VLAN_VAL(x) \ + if (iface_cfg->vlan.__##x##_changed) { \ + cfg->network_interfaces[i].vlan.x = \ + net_init_config_user.network_interfaces[i].vlan.x; \ + cfg->network_interfaces[i].vlan.__##x##_changed = true; \ + } else { \ + cfg->network_interfaces[i].vlan.x = \ + config->network_interfaces[i].vlan.x; \ + cfg->network_interfaces[i].vlan.__##x##_changed = false; \ + } + +#define UPDATE_IEEE802154_VAL(x) \ + if (net_init_config_user.ieee_802_15_4.__##x##_changed) { \ + cfg->ieee_802_15_4.x = net_init_config_user.ieee_802_15_4.x; \ + cfg->ieee_802_15_4.__##x##_changed = true; \ + } else { \ + cfg->ieee_802_15_4.x = config->ieee_802_15_4.x; \ + cfg->ieee_802_15_4.__##x##_changed = false; \ + } + +#define UPDATE_IEEE802154_SECURITY_KEY_VAL(x) \ + if (net_init_config_user.ieee_802_15_4.security_key[0].__##x##_changed) { \ + cfg->ieee_802_15_4.security_key[0].x = \ + net_init_config_user.ieee_802_15_4.security_key[0].x; \ + cfg->ieee_802_15_4.security_key[0].__##x##_changed = true; \ + } else { \ + cfg->ieee_802_15_4.security_key[0].x = \ + config->ieee_802_15_4.security_key[0].x; \ + cfg->ieee_802_15_4.security_key[0].__##x##_changed = false; \ + } + +#define UPDATE_SNTP_VAL(x) \ + if (net_init_config_user.sntp.__##x##_changed) { \ + cfg->sntp.x = net_init_config_user.sntp.x; \ + cfg->sntp.__##x##_changed = true; \ + } else { \ + cfg->sntp.x = config->sntp.x; \ + cfg->sntp.__##x##_changed = false; \ + } + +int net_config_get(struct net_init_config *cfg) +{ +#if defined(CONFIG_SETTINGS) && defined(CONFIG_SETTINGS_RUNTIME) + const struct net_init_config *config; + int ret; + + if (cfg == NULL) { + return -EINVAL; + } + + config = net_config_get_init_config(); + if (config == NULL) { + NET_ERR("Default network configuration not found."); + return -ENOENT; + } + + if (!settings_loaded) { + settings_load(); + + ret = settings_runtime_get(SETTINGS_SUBTREE_NET_CONFIG "/user", + &net_init_config_user, + sizeof(net_init_config_user)); + if (ret < 0) { + NET_ERR("Cannot get user network configuration (%d)", ret); + return ret; + } + + settings_loaded = true; + } + + /* Verify that the yaml schema file is not changed between what is in + * use and what was used when the user modified the configuration. + */ + if (net_init_config_user.config_format_hash != NULL && + strncmp(config->config_format_hash, + net_init_config_user.config_format_hash, + sizeof(config->config_format_hash) - 1) != 0) { + NET_ERR("User network configuration format hash mismatch"); + settings_loaded = false; + return -EINVAL; + } + + /* Make a union of the default config and the user modified one and + * return results in cfg pointer. + */ + ARRAY_FOR_EACH(net_init_config_user.network_interfaces, i) { + struct net_init_config_network_interfaces *iface_cfg; + + iface_cfg = &net_init_config_user.network_interfaces[i]; + + UPDATE_IFACE_VAL(bind_to); + UPDATE_IFACE_VAL(name); + UPDATE_IFACE_VAL(device_name); + UPDATE_IFACE_VAL(set_name); + UPDATE_IFACE_VAL(set_default); + + ARRAY_FOR_EACH(iface_cfg->flags, j) { + UPDATE_FLAGS_VAL(value); + } + + /* IPv6 config */ + if (IS_ENABLED(CONFIG_NET_IPV6)) { + UPDATE_IPV6_VAL(status); + UPDATE_IPV6_VAL(hop_limit); + UPDATE_IPV6_VAL(multicast_hop_limit); + + ARRAY_FOR_EACH(iface_cfg->ipv6.ipv6_addresses, j) { + UPDATE_IPV6_ADDR_VAL(value); + } + + ARRAY_FOR_EACH(iface_cfg->ipv6.ipv6_multicast_addresses, j) { + UPDATE_IPV6_MADDR_VAL(value); + } + + ARRAY_FOR_EACH(iface_cfg->ipv6.prefixes, j) { + UPDATE_IPV6_PREFIX_VAL(address); + UPDATE_IPV6_PREFIX_VAL(len); + UPDATE_IPV6_PREFIX_VAL(lifetime); + } + + if (IS_ENABLED(CONFIG_NET_DHCPV6)) { + UPDATE_IPV6_DHCPV6_VAL(status); + UPDATE_IPV6_DHCPV6_VAL(do_request_address); + UPDATE_IPV6_DHCPV6_VAL(do_request_prefix); + } + } + + /* IPv4 config */ + if (IS_ENABLED(CONFIG_NET_IPV4)) { + UPDATE_IPV4_VAL(status); + UPDATE_IPV4_VAL(time_to_live); + UPDATE_IPV4_VAL(multicast_time_to_live); + UPDATE_IPV4_VAL(gateway); + + if (IS_ENABLED(CONFIG_NET_DHCPV4)) { + UPDATE_IPV4_DHCPV4_VAL(status); + } + + if (IS_ENABLED(CONFIG_NET_IPV4_AUTO)) { + UPDATE_IPV4_AUTOCONF_VAL(status); + } + + ARRAY_FOR_EACH(iface_cfg->ipv4.ipv4_addresses, j) { + UPDATE_IPV4_ADDR_VAL(value); + } + + ARRAY_FOR_EACH(iface_cfg->ipv4.ipv4_multicast_addresses, j) { + UPDATE_IPV4_MADDR_VAL(value); + } + + if (IS_ENABLED(CONFIG_NET_DHCPV4_SERVER)) { + UPDATE_IPV4_DHCPV4_SERVER_VAL(status); + UPDATE_IPV4_DHCPV4_SERVER_VAL(base_address); + } + } + + /* VLAN config */ + if (IS_ENABLED(CONFIG_NET_VLAN)) { + UPDATE_IFACE_VLAN_VAL(status); + UPDATE_IFACE_VLAN_VAL(tag); + } + } + + /* IEEE 802.15.4 config */ + if (IS_ENABLED(CONFIG_NET_L2_IEEE802154)) { + UPDATE_IEEE802154_VAL(status); + UPDATE_IEEE802154_VAL(bind_to); + UPDATE_IEEE802154_VAL(pan_id); + UPDATE_IEEE802154_VAL(channel); + UPDATE_IEEE802154_VAL(tx_power); + UPDATE_IEEE802154_VAL(security_key_mode); + UPDATE_IEEE802154_VAL(security_level); + UPDATE_IEEE802154_VAL(ack_required); + UPDATE_IEEE802154_SECURITY_KEY_VAL(value); + } + + /* SNTP config */ + if (IS_ENABLED(CONFIG_NET_CONFIG_CLOCK_SNTP_INIT)) { + UPDATE_SNTP_VAL(status); + UPDATE_SNTP_VAL(bind_to); + UPDATE_SNTP_VAL(server); + UPDATE_SNTP_VAL(timeout); + } + + return 0; +#else + const struct net_init_config *config; + + config = net_config_get_init_config(); + if (config == NULL) { + NET_ERR("Default network configuration not found."); + return -ENOENT; + } + + memcpy(cfg, config, sizeof(*config)); + + return 0; +#endif /* CONFIG_SETTINGS */ +} + +#define SET_IFACE_VAL(x) \ + if (net_init_config_user.network_interfaces[i].__##x##_changed == true) { \ + if (cfg->network_interfaces[i].x != net_init_config_user. \ + network_interfaces[i].x) { \ + net_init_config_user.network_interfaces[i].x = \ + cfg->network_interfaces[i].x; \ + } \ + } else if (cfg->network_interfaces[i].__##x##_changed && \ + cfg->network_interfaces[i].x != config->network_interfaces[i].x) { \ + net_init_config_user.network_interfaces[i].x = cfg->network_interfaces[i].x; \ + net_init_config_user.network_interfaces[i].__##x##_changed = true; \ + } + +#define SET_FLAGS_VAL(x) \ + if (net_init_config_user.network_interfaces[i].flags[j].__##x##_changed == true) { \ + if (cfg->network_interfaces[i].flags[j].x != net_init_config_user. \ + network_interfaces[i].flags[j].x) { \ + net_init_config_user.network_interfaces[i].flags[j].x = \ + cfg->network_interfaces[i].flags[j].x; \ + } \ + } else if (cfg->network_interfaces[i].flags[j].__##x##_changed && \ + cfg->network_interfaces[i].flags[j].x != \ + config->network_interfaces[i].flags[j].x) { \ + net_init_config_user.network_interfaces[i].flags[j].x = \ + cfg->network_interfaces[i].flags[j].x; \ + net_init_config_user.network_interfaces[i].flags[j].__##x##_changed = true; \ + } + +#define SET_IPV6_VAL(x) \ + if (net_init_config_user.network_interfaces[i].ipv6.__##x##_changed == true) { \ + if (cfg->network_interfaces[i].ipv6.x != net_init_config_user. \ + network_interfaces[i].ipv6.x) { \ + net_init_config_user.network_interfaces[i].ipv6.x = \ + cfg->network_interfaces[i].ipv6.x; \ + } \ + } else if (cfg->network_interfaces[i].ipv6.__##x##_changed && \ + cfg->network_interfaces[i].ipv6.x != config->network_interfaces[i].ipv6.x) { \ + net_init_config_user.network_interfaces[i].ipv6.x = \ + cfg->network_interfaces[i].ipv6.x; \ + net_init_config_user.network_interfaces[i].ipv6.__##x##_changed = true; \ + } + +#define SET_IPV6_ADDR_VAL(x) \ + if (net_init_config_user.network_interfaces[i].ipv6.ipv6_addresses[j].\ + __##x##_changed == true) { \ + if (cfg->network_interfaces[i].ipv6.ipv6_addresses[j].x != \ + net_init_config_user.network_interfaces[i].ipv6.ipv6_addresses[j].x) { \ + net_init_config_user.network_interfaces[i].ipv6.ipv6_addresses[j].x = \ + cfg->network_interfaces[i].ipv6.ipv6_addresses[j].x; \ + } \ + } else if (cfg->network_interfaces[i].ipv6.ipv6_addresses[j].__##x##_changed && \ + cfg->network_interfaces[i].ipv6.ipv6_addresses[j].x != \ + config->network_interfaces[i].ipv6.ipv6_addresses[j].x) { \ + net_init_config_user.network_interfaces[i].ipv6.ipv6_addresses[j].x = \ + cfg->network_interfaces[i].ipv6.ipv6_addresses[j].x; \ + net_init_config_user.network_interfaces[i].ipv6.ipv6_addresses[j].\ + __##x##_changed = true; \ + } + +#define SET_IPV6_MADDR_VAL(x) \ + if (net_init_config_user.network_interfaces[i].ipv6.ipv6_multicast_addresses[j].\ + __##x##_changed == true) { \ + if (cfg->network_interfaces[i].ipv6.ipv6_multicast_addresses[j].x != \ + net_init_config_user.network_interfaces[i].ipv6. \ + ipv6_multicast_addresses[j].x) { \ + net_init_config_user.network_interfaces[i].ipv6.\ + ipv6_multicast_addresses[j].x = \ + cfg->network_interfaces[i].ipv6.ipv6_multicast_addresses[j].x; \ + } \ + } else if (cfg->network_interfaces[i].ipv6.ipv6_multicast_addresses[j].__##x##_changed && \ + cfg->network_interfaces[i].ipv6.ipv6_multicast_addresses[j].x != \ + config->network_interfaces[i].ipv6.ipv6_multicast_addresses[j].x) { \ + net_init_config_user.network_interfaces[i].ipv6.\ + ipv6_multicast_addresses[j].x = \ + cfg->network_interfaces[i].ipv6.ipv6_multicast_addresses[j].x; \ + net_init_config_user.network_interfaces[i].ipv6.ipv6_multicast_addresses[j].\ + __##x##_changed = true; \ + } + +#define SET_IPV6_PREFIX_VAL(x) \ + if (net_init_config_user.network_interfaces[i].ipv6.prefixes[j].\ + __##x##_changed == true) { \ + if (cfg->network_interfaces[i].ipv6.prefixes[j].x != \ + net_init_config_user.network_interfaces[i].ipv6.prefixes[j].x) { \ + net_init_config_user.network_interfaces[i].ipv6.prefixes[j].x = \ + cfg->network_interfaces[i].ipv6.prefixes[j].x; \ + } \ + } else if (cfg->network_interfaces[i].ipv6.prefixes[j].__##x##_changed && \ + cfg->network_interfaces[i].ipv6.prefixes[j].x != \ + config->network_interfaces[i].ipv6.prefixes[j].x) { \ + net_init_config_user.network_interfaces[i].ipv6.prefixes[j].x = \ + cfg->network_interfaces[i].ipv6.prefixes[j].x; \ + net_init_config_user.network_interfaces[i].ipv6.prefixes[j].\ + __##x##_changed = true; \ + } + +#define SET_IPV6_DHCPV6_VAL(x) \ + if (net_init_config_user.network_interfaces[i].ipv6.dhcpv6.\ + __##x##_changed == true) { \ + if (cfg->network_interfaces[i].ipv6.dhcpv6.x != \ + net_init_config_user.network_interfaces[i].ipv6.dhcpv6.x) { \ + net_init_config_user.network_interfaces[i].ipv6.dhcpv6.x = \ + cfg->network_interfaces[i].ipv6.dhcpv6.x; \ + } \ + } else if (cfg->network_interfaces[i].ipv6.dhcpv6.__##x##_changed && \ + cfg->network_interfaces[i].ipv6.dhcpv6.x != \ + config->network_interfaces[i].ipv6.dhcpv6.x) { \ + net_init_config_user.network_interfaces[i].ipv6.dhcpv6.x = \ + cfg->network_interfaces[i].ipv6.dhcpv6.x; \ + net_init_config_user.network_interfaces[i].ipv6.dhcpv6.\ + __##x##_changed = true; \ + } + +#define SET_IPV4_DHCPV4_VAL(x) \ + if (net_init_config_user.network_interfaces[i].ipv4.dhcpv4. \ + __##x##_changed == true) { \ + if (cfg->network_interfaces[i].ipv4.dhcpv4.x != \ + net_init_config_user.network_interfaces[i].ipv4.dhcpv4.x) { \ + net_init_config_user.network_interfaces[i].ipv4.dhcpv4.x = \ + cfg->network_interfaces[i].ipv4.dhcpv4.x; \ + } \ + } else if (cfg->network_interfaces[i].ipv4.dhcpv4.__##x##_changed && \ + cfg->network_interfaces[i].ipv4.dhcpv4.x != \ + config->network_interfaces[i].ipv4.dhcpv4.x) { \ + net_init_config_user.network_interfaces[i].ipv4.dhcpv4.x = \ + cfg->network_interfaces[i].ipv4.dhcpv4.x; \ + net_init_config_user.network_interfaces[i].ipv4.dhcpv4. \ + __##x##_changed = true; \ + } + +#define SET_IPV4_AUTOCONF_VAL(x) \ + if (net_init_config_user.network_interfaces[i].ipv4.ipv4_autoconf. \ + __##x##_changed == true) { \ + if (cfg->network_interfaces[i].ipv4.ipv4_autoconf.x != \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_autoconf.x) { \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_autoconf.x = \ + cfg->network_interfaces[i].ipv4.ipv4_autoconf.x; \ + } \ + } else if (cfg->network_interfaces[i].ipv4.ipv4_autoconf.__##x##_changed && \ + cfg->network_interfaces[i].ipv4.ipv4_autoconf.x != \ + config->network_interfaces[i].ipv4.ipv4_autoconf.x) { \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_autoconf.x = \ + cfg->network_interfaces[i].ipv4.ipv4_autoconf.x; \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_autoconf. \ + __##x##_changed = true; \ + } + +#define SET_IPV4_VAL(x) \ + if (net_init_config_user.network_interfaces[i].ipv4.__##x##_changed == true) { \ + if (cfg->network_interfaces[i].ipv4.x != \ + net_init_config_user.network_interfaces[i].ipv4.x) { \ + net_init_config_user.network_interfaces[i].ipv4.x = \ + cfg->network_interfaces[i].ipv4.x; \ + } \ + } else if (cfg->network_interfaces[i].ipv4.__##x##_changed && \ + cfg->network_interfaces[i].ipv4.x != \ + config->network_interfaces[i].ipv4.x) { \ + net_init_config_user.network_interfaces[i].ipv4.x = \ + cfg->network_interfaces[i].ipv4.x; \ + net_init_config_user.network_interfaces[i].ipv4.__##x##_changed = true; \ + } + +#define SET_IPV4_ADDR_VAL(x) \ + if (net_init_config_user.network_interfaces[i].ipv4.ipv4_addresses[j].\ + __##x##_changed == true) { \ + if (cfg->network_interfaces[i].ipv4.ipv4_addresses[j].x != \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_addresses[j].x) { \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_addresses[j].x = \ + cfg->network_interfaces[i].ipv4.ipv4_addresses[j].x; \ + } \ + } else if (cfg->network_interfaces[i].ipv4.ipv4_addresses[j].__##x##_changed && \ + cfg->network_interfaces[i].ipv4.ipv4_addresses[j].x != \ + config->network_interfaces[i].ipv4.ipv4_addresses[j].x) { \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_addresses[j].x = \ + cfg->network_interfaces[i].ipv4.ipv4_addresses[j].x; \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_addresses[j].\ + __##x##_changed = true; \ + } + +#define SET_IPV4_MADDR_VAL(x) \ + if (net_init_config_user.network_interfaces[i].ipv4.ipv4_multicast_addresses[j].\ + __##x##_changed == true) { \ + if (cfg->network_interfaces[i].ipv4.ipv4_multicast_addresses[j].x != \ + net_init_config_user.network_interfaces[i].ipv4. \ + ipv4_multicast_addresses[j].x) { \ + net_init_config_user.network_interfaces[i].ipv4.\ + ipv4_multicast_addresses[j].x = \ + cfg->network_interfaces[i].ipv4.ipv4_multicast_addresses[j].x; \ + } \ + } else if (cfg->network_interfaces[i].ipv4.ipv4_multicast_addresses[j].__##x##_changed && \ + cfg->network_interfaces[i].ipv4.ipv4_multicast_addresses[j].x != \ + config->network_interfaces[i].ipv4.ipv4_multicast_addresses[j].x) { \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_multicast_addresses[j].x = \ + cfg->network_interfaces[i].ipv4.ipv4_multicast_addresses[j].x; \ + net_init_config_user.network_interfaces[i].ipv4.ipv4_multicast_addresses[j].\ + __##x##_changed = true; \ + } + +#define SET_IPV4_DHCPV4_SERVER_VAL(x) \ + if (net_init_config_user.network_interfaces[i].ipv4.dhcpv4_server. \ + __##x##_changed == true) { \ + if (cfg->network_interfaces[i].ipv4.dhcpv4_server.x != \ + net_init_config_user.network_interfaces[i].ipv4.dhcpv4_server.x) { \ + net_init_config_user.network_interfaces[i].ipv4.dhcpv4_server.x = \ + cfg->network_interfaces[i].ipv4.dhcpv4_server.x; \ + } \ + } else if (cfg->network_interfaces[i].ipv4.dhcpv4_server.__##x##_changed && \ + cfg->network_interfaces[i].ipv4.dhcpv4_server.x != \ + config->network_interfaces[i].ipv4.dhcpv4_server.x) { \ + net_init_config_user.network_interfaces[i].ipv4.dhcpv4_server.x = \ + cfg->network_interfaces[i].ipv4.dhcpv4_server.x; \ + net_init_config_user.network_interfaces[i].ipv4.dhcpv4_server. \ + __##x##_changed = true; \ + } + +#define SET_IFACE_VLAN_VAL(x) \ + if (net_init_config_user.network_interfaces[i].vlan.__##x##_changed == true) { \ + if (cfg->network_interfaces[i].vlan.x != net_init_config_user. \ + network_interfaces[i].vlan.x) { \ + net_init_config_user.network_interfaces[i].vlan.x = \ + cfg->network_interfaces[i].vlan.x; \ + } \ + } else if (cfg->network_interfaces[i].vlan.__##x##_changed && \ + cfg->network_interfaces[i].vlan.x != config->network_interfaces[i].vlan.x) { \ + net_init_config_user.network_interfaces[i].vlan.x = \ + cfg->network_interfaces[i].vlan.x; \ + net_init_config_user.network_interfaces[i].vlan.__##x##_changed = true; \ + } + +#define SET_IEEE802154_VAL(x) \ + if (net_init_config_user.ieee_802_15_4.__##x##_changed == true) { \ + if (cfg->ieee_802_15_4.x != net_init_config_user.ieee_802_15_4.x) { \ + net_init_config_user.ieee_802_15_4.x = cfg->ieee_802_15_4.x; \ + } \ + } else if (cfg->ieee_802_15_4.__##x##_changed && \ + cfg->ieee_802_15_4.x != config->ieee_802_15_4.x) { \ + net_init_config_user.ieee_802_15_4.x = cfg->ieee_802_15_4.x; \ + net_init_config_user.ieee_802_15_4.__##x##_changed = true; \ + } + +#define SET_IEEE802154_SECURITY_KEY_VAL(x) \ + if (net_init_config_user.ieee_802_15_4.security_key[0].__##x##_changed == true) { \ + if (cfg->ieee_802_15_4.security_key[0].x != \ + net_init_config_user.ieee_802_15_4.security_key[0].x) { \ + net_init_config_user.ieee_802_15_4.security_key[0].x = \ + cfg->ieee_802_15_4.security_key[0].x; \ + } \ + } else if (cfg->ieee_802_15_4.security_key[0].__##x##_changed && \ + cfg->ieee_802_15_4.security_key[0].x != \ + config->ieee_802_15_4.security_key[0].x) { \ + net_init_config_user.ieee_802_15_4.security_key[0].x = \ + cfg->ieee_802_15_4.security_key[0].x; \ + net_init_config_user.ieee_802_15_4.security_key[0].__##x##_changed = true; \ + } + +#define SET_SNTP_VAL(x) \ + if (net_init_config_user.sntp.__##x##_changed == true) { \ + if (cfg->sntp.x != net_init_config_user.sntp.x) { \ + net_init_config_user.sntp.x = cfg->sntp.x; \ + } \ + } else if (cfg->sntp.__##x##_changed && \ + cfg->sntp.x != config->sntp.x) { \ + net_init_config_user.sntp.x = cfg->sntp.x; \ + net_init_config_user.sntp.__##x##_changed = true; \ + } + +int net_config_set(const struct net_init_config *cfg) +{ +#if defined(CONFIG_SETTINGS) && defined(CONFIG_SETTINGS_RUNTIME) + const struct net_init_config *config; + int ret; + + if (cfg == NULL) { + return -EINVAL; + } + + config = net_config_get_init_config(); + if (config == NULL) { + NET_ERR("Default network configuration not found."); + return -ENOENT; + } + + /* Save the user modified config */ + ARRAY_FOR_EACH(config->network_interfaces, i) { + const struct net_init_config_network_interfaces *iface_cfg; + + iface_cfg = &config->network_interfaces[i]; + + SET_IFACE_VAL(bind_to); + SET_IFACE_VAL(name); + SET_IFACE_VAL(device_name); + SET_IFACE_VAL(set_name); + SET_IFACE_VAL(set_default); + + ARRAY_FOR_EACH(iface_cfg->flags, j) { + SET_FLAGS_VAL(value); + } + + /* IPv6 config */ + if (IS_ENABLED(CONFIG_NET_IPV6)) { + SET_IPV6_VAL(status); + SET_IPV6_VAL(hop_limit); + SET_IPV6_VAL(multicast_hop_limit); + + ARRAY_FOR_EACH(iface_cfg->ipv6.ipv6_addresses, j) { + SET_IPV6_ADDR_VAL(value); + } + + ARRAY_FOR_EACH(iface_cfg->ipv6.ipv6_multicast_addresses, j) { + SET_IPV6_MADDR_VAL(value); + } + + ARRAY_FOR_EACH(iface_cfg->ipv6.prefixes, j) { + SET_IPV6_PREFIX_VAL(address); + SET_IPV6_PREFIX_VAL(len); + SET_IPV6_PREFIX_VAL(lifetime); + } + + if (IS_ENABLED(CONFIG_NET_DHCPV6)) { + SET_IPV6_DHCPV6_VAL(status); + SET_IPV6_DHCPV6_VAL(do_request_address); + SET_IPV6_DHCPV6_VAL(do_request_prefix); + } + } + + /* IPv4 config */ + if (IS_ENABLED(CONFIG_NET_IPV4)) { + SET_IPV4_VAL(status); + SET_IPV4_VAL(time_to_live); + SET_IPV4_VAL(multicast_time_to_live); + SET_IPV4_VAL(gateway); + + if (IS_ENABLED(CONFIG_NET_DHCPV4)) { + SET_IPV4_DHCPV4_VAL(status); + } + + if (IS_ENABLED(CONFIG_NET_IPV4_AUTO)) { + SET_IPV4_AUTOCONF_VAL(status); + } + + ARRAY_FOR_EACH(iface_cfg->ipv4.ipv4_addresses, j) { + SET_IPV4_ADDR_VAL(value); + } + + ARRAY_FOR_EACH(iface_cfg->ipv4.ipv4_multicast_addresses, j) { + SET_IPV4_MADDR_VAL(value); + } + + if (IS_ENABLED(CONFIG_NET_DHCPV4_SERVER)) { + SET_IPV4_DHCPV4_SERVER_VAL(status); + SET_IPV4_DHCPV4_SERVER_VAL(base_address); + } + } + + /* VLAN config */ + if (IS_ENABLED(CONFIG_NET_VLAN)) { + SET_IFACE_VLAN_VAL(status); + SET_IFACE_VLAN_VAL(tag); + } + } + + /* IEEE 802.15.4 config */ + if (IS_ENABLED(CONFIG_NET_L2_IEEE802154)) { + SET_IEEE802154_VAL(status); + SET_IEEE802154_VAL(bind_to); + SET_IEEE802154_VAL(pan_id); + SET_IEEE802154_VAL(channel); + SET_IEEE802154_VAL(tx_power); + SET_IEEE802154_VAL(security_key_mode); + SET_IEEE802154_VAL(security_level); + SET_IEEE802154_VAL(ack_required); + SET_IEEE802154_SECURITY_KEY_VAL(value); + } + + /* SNTP config */ + if (IS_ENABLED(CONFIG_NET_CONFIG_CLOCK_SNTP_INIT)) { + SET_SNTP_VAL(status); + SET_SNTP_VAL(bind_to); + SET_SNTP_VAL(server); + SET_SNTP_VAL(timeout); + } + + ret = settings_runtime_set(SETTINGS_SUBTREE_NET_CONFIG "/user", + &net_init_config_user, sizeof(net_init_config_user)); + if (ret < 0) { + NET_ERR("Cannot save user network configuration (%d)", ret); + return ret; + } + + ret = settings_runtime_commit(SETTINGS_SUBTREE_NET_CONFIG); + if (ret < 0) { + NET_ERR("Cannot commit user network configuration (%d)", ret); + return ret; + } + + settings_save(); + + NET_DBG("Saved user network configuration"); + + return 0; +#else + return -ENOTSUP; +#endif +} + +int net_config_clear(void) +{ +#if defined(CONFIG_SETTINGS) && defined(CONFIG_SETTINGS_RUNTIME) + int ret; + + ret = settings_delete(SETTINGS_SUBTREE_NET_CONFIG "/user"); + if (ret < 0) { + NET_ERR("Cannot clear user network configuration (%d)", ret); + return ret; + } + + memset(&net_init_config_user, 0, sizeof(net_init_config_user)); + + settings_loaded = false; + + ret = settings_runtime_commit(SETTINGS_SUBTREE_NET_CONFIG); + if (ret < 0) { + NET_ERR("Cannot commit user network configuration (%d)", ret); + return ret; + } + + settings_save(); + + NET_DBG("Cleared user network configuration"); + + return 0; +#else + return -ENOTSUP; +#endif +} + +static int generated_net_config_init_app(const struct device *dev, + const char *app_info) +{ + struct net_init_config user_config; + struct net_init_config *config = &user_config; + int ret, ifindex; + + ret = net_config_get(&user_config); + if (ret < 0) { + NET_ERR("Network configuration error (%d)", ret); + return -ENOENT; + } + + ret = wait_for_interface(config->network_interfaces, + sizeof(config->network_interfaces)); + if (ret < 0) { + NET_WARN("Timeout while waiting network interfaces (%d)", ret); + return ret; + } + + ARRAY_FOR_EACH(config->network_interfaces, i) { + const struct net_init_config_network_interfaces *cfg; + struct net_if *iface; + const char *name; + + cfg = &config->network_interfaces[i]; + + /* We first need to setup any VLAN interfaces so that other + * interfaces can use them (the interface name etc are correctly + * set so that referencing works ok). + */ + if (IS_ENABLED(CONFIG_NET_VLAN)) { + vlan_setup(config, cfg); + } + + iface = get_interface(config, i, dev, &name); + if (iface == NULL || name == NULL) { + NET_WARN("No such interface \"%s\" found.", + name == NULL ? "" : name); + continue; + } + + ifindex = net_if_get_by_iface(iface); + + NET_DBG("Configuring interface %d (%p)", ifindex, iface); + + /* Do we need to change the interface name */ + if (cfg->set_name != NULL) { + ret = net_if_set_name(iface, cfg->set_name); + if (ret < 0) { + NET_DBG("Cannot rename interface %d to \"%s\" (%d)", + ifindex, cfg->set_name, ret); + continue; + } + + NET_DBG("Changed interface %d name to \"%s\"", ifindex, + cfg->set_name); + } + + if (cfg->set_default) { + net_if_set_default(iface); + + NET_DBG("Setting interface %d as default", ifindex); + } + + ARRAY_FOR_EACH(cfg->flags, j) { + if (cfg->flags[j].value == NULL || + cfg->flags[j].value[0] == '\0') { + continue; + } + + ret = process_iface_flag(iface, cfg->flags[j].value); + if (ret < 0) { + NET_DBG("Cannot set/clear flag %s", cfg->flags[j].value); + } + } + + ipv6_setup(iface, ifindex, cfg); + ipv4_setup(iface, ifindex, cfg); + virtual_iface_setup(iface, ifindex, config, cfg); + } + + if (IS_ENABLED(CONFIG_NET_CONFIG_CLOCK_SNTP_INIT) && config->sntp.status) { +#if defined(CONFIG_SNTP) + struct net_if *iface = NULL; + + if (config->sntp.bind_to > 0) { + iface = get_interface(config, + config->sntp.bind_to - 1, + dev, + NULL); + } + + ret = net_init_clock_via_sntp(iface, config->sntp.server, + config->sntp.timeout); + if (ret < 0) { + NET_DBG("Cannot init SNTP interface %d (%d)", + net_if_get_by_iface(iface), ret); + } else { + NET_DBG("Initialized SNTP to use interface %d", + net_if_get_by_iface(iface)); + } +#endif + } + + if (IS_ENABLED(CONFIG_NET_L2_IEEE802154) && + IS_ENABLED(CONFIG_NET_CONFIG_SETTINGS) && + config->ieee_802_15_4.status) { +#ifdef CONFIG_NET_L2_IEEE802154_SECURITY + struct ieee802154_security_params sec_params = { 0 }; + struct ieee802154_security_params *generated_sec_params_ptr = &sec_params; +#else +#define generated_sec_params_ptr NULL +#endif /* CONFIG_NET_L2_IEEE802154_SECURITY */ + + struct net_if *iface = NULL; + + if (COND_CODE_1(CONFIG_NET_L2_IEEE802154, + (config->ieee_802_15_4.bind_to), (0)) > 0) { + iface = get_interface( + config, + COND_CODE_1(CONFIG_NET_L2_IEEE802154, + (config->ieee_802_15_4.bind_to - 1), (0)), + dev, + NULL); + } + +#ifdef CONFIG_NET_L2_IEEE802154_SECURITY + memcpy(sec_params.key, config->ieee_802_15_4.security_key, + MIN(sizeof(sec_params.key), + sizeof(config->ieee_802_15_4.security_key))); + sec_params.key_len = sizeof(config->ieee_802_15_4.security_key); + sec_params.key_mode = config->ieee_802_15_4.security_key_mode; + sec_params.level = config->ieee_802_15_4.security_level; +#endif + + ret = z_net_config_ieee802154_setup( + IF_ENABLED(CONFIG_NET_L2_IEEE802154, + (iface, + config->ieee_802_15_4.channel, + config->ieee_802_15_4.pan_id, + config->ieee_802_15_4.tx_power, + generated_sec_params_ptr))); + if (ret < 0) { + NET_ERR("Cannot setup IEEE 802.15.4 interface (%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. + */ + if (IS_ENABLED(CONFIG_LOG_BACKEND_NET_AUTOSTART) && + !IS_ENABLED(CONFIG_LOG_BACKEND_NET_USE_CONNECTION_MANAGER)) { + log_backend_net_start(); + } + + return 0; +} + +int net_config_init_app(const struct device *dev, const char *app_info) +{ +#if defined(CONFIG_SETTINGS) + int ret; + +#if defined(CONFIG_FILE_SYSTEM_LITTLEFS) + FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(cstorage); + + /* mounting info */ + static struct fs_mount_t littlefs_mnt = { + .type = FS_LITTLEFS, + .fs_data = &cstorage, + .storage_dev = (void *)STORAGE_PARTITION_ID, + .mnt_point = "/littlefs" + }; + + ret = fs_mount(&littlefs_mnt); + if (ret != 0) { + NET_ERR("mounting littlefs error [%d]", ret); + } else { + ret = fs_unlink(CONFIG_SETTINGS_FILE_PATH); + if ((ret != 0) && (ret != -ENOENT)) { + NET_ERR("can't delete config file (%d)", ret); + } else { + NET_DBG("FS initialized OK"); + } + } +#endif /* CONFIG_FILE_SYSTEM_LITTLEFS */ + + ret = settings_subsys_init(); + if (ret != 0) { + NET_ERR("settings subsys initialization fail (%d)", ret); + return ret; + } + + settings_loaded = false; + + ret = settings_register(&net_config_settings_handler); + if (ret == 0) { + ret = settings_load_subtree(SETTINGS_SUBTREE_NET_CONFIG); + if (ret != 0) { + NET_ERR("Settings load failed: %d", ret); + } else { + NET_DBG("Settings %s loaded", SETTINGS_SUBTREE_NET_CONFIG); + settings_loaded = true; + } + } else { + NET_ERR("Settings register failed: %d", ret); + } +#endif /* CONFIG_SETTINGS */ + + return generated_net_config_init_app(dev, app_info); +} + +#if defined(CONFIG_NET_CONFIG_AUTO_INIT) +static int init_app(void) +{ + (void)net_config_init_app(NULL, "Initializing network"); + + return 0; +} SYS_INIT(init_app, APPLICATION, CONFIG_NET_CONFIG_INIT_PRIO); + +const struct net_init_config *net_config_get_init_config(void) +{ +#define NET_INIT_CONFIG_ENABLE_DATA +#include "net_init_config.inc" + +#if defined(CONFIG_NET_DHCPV4_SERVER) +/* If we are starting DHCPv4 server, then the socket service needs to be started before + * this config lib as the server will need to use the socket service. + */ +BUILD_ASSERT(CONFIG_NET_SOCKETS_SERVICE_THREAD_PRIO < CONFIG_NET_CONFIG_INIT_PRIO); +#endif + + return &NET_INIT_CONFIG_DATA; /* defined in net_init_config.inc file */ +} + +#else /* CONFIG_NET_CONFIG_AUTO_INIT */ +const struct net_init_config *net_config_get_init_config(void) +{ + return NULL; +} #endif /* CONFIG_NET_CONFIG_AUTO_INIT */ diff --git a/subsys/net/lib/config/init_clock_sntp.c b/subsys/net/lib/config/init_clock_sntp.c index 5eeae0a8df5a1..c6a4e7375f83a 100644 --- a/subsys/net/lib/config/init_clock_sntp.c +++ b/subsys/net/lib/config/init_clock_sntp.c @@ -23,7 +23,9 @@ BUILD_ASSERT( (sizeof(CONFIG_NET_CONFIG_SNTP_INIT_SERVER) != 1), "SNTP server has to be configured, unless DHCPv4 is used to set it"); -static int sntp_init_helper(struct sntp_time *tm) +static int sntp_init_helper(struct sntp_time *tm, + const char *server, + int timeout) { #ifdef CONFIG_NET_CONFIG_SNTP_INIT_SERVER_USE_DHCPV4_OPTION struct net_if *iface = net_if_get_default(); @@ -34,7 +36,7 @@ static int sntp_init_helper(struct sntp_time *tm) sntp_addr.sin_family = AF_INET; sntp_addr.sin_addr.s_addr = iface->config.dhcpv4.ntp_addr.s_addr; return sntp_simple_addr((struct sockaddr *)&sntp_addr, sizeof(sntp_addr), - CONFIG_NET_CONFIG_SNTP_INIT_TIMEOUT, tm); + timeout, tm); } if (sizeof(CONFIG_NET_CONFIG_SNTP_INIT_SERVER) == 1) { /* Empty address, skip using SNTP via Kconfig defaults */ @@ -42,34 +44,38 @@ static int sntp_init_helper(struct sntp_time *tm) } LOG_INF("SNTP address not set by DHCPv4, using Kconfig defaults"); #endif /* NET_CONFIG_SNTP_INIT_SERVER_USE_DHCPV4_OPTION */ - return sntp_simple(CONFIG_NET_CONFIG_SNTP_INIT_SERVER, - CONFIG_NET_CONFIG_SNTP_INIT_TIMEOUT, tm); + + return sntp_simple(server, timeout, tm); } -int net_init_clock_via_sntp(void) +int net_init_clock_via_sntp(struct net_if *iface, + const char *server, + int timeout) { struct sntp_time ts; struct timespec tspec; - int res = sntp_init_helper(&ts); + int ret; - if (res < 0) { - LOG_ERR("Cannot set time using SNTP: %d", res); + ret = sntp_init_helper(&ts, server, timeout); + if (ret < 0) { + LOG_ERR("Cannot set time using SNTP (%d)", ret); goto end; } tspec.tv_sec = ts.seconds; tspec.tv_nsec = ((uint64_t)ts.fraction * (1000 * 1000 * 1000)) >> 32; - res = sys_clock_settime(SYS_CLOCK_REALTIME, &tspec); + + ret = sys_clock_settime(SYS_CLOCK_REALTIME, &tspec); LOG_DBG("Time synced using SNTP"); end: #ifdef CONFIG_NET_CONFIG_SNTP_INIT_RESYNC k_work_reschedule( &sntp_resync_work_handle, - (res < 0) ? K_SECONDS(CONFIG_NET_CONFIG_SNTP_INIT_RESYNC_ON_FAILURE_INTERVAL) + (ret < 0) ? K_SECONDS(CONFIG_NET_CONFIG_SNTP_INIT_RESYNC_ON_FAILURE_INTERVAL) : K_SECONDS(CONFIG_NET_CONFIG_SNTP_INIT_RESYNC_INTERVAL)); #endif - return res; + return ret; } #ifdef CONFIG_NET_CONFIG_SNTP_INIT_RESYNC diff --git a/subsys/net/lib/shell/CMakeLists.txt b/subsys/net/lib/shell/CMakeLists.txt index db06c84d8bb65..e3b3eedac0660 100644 --- a/subsys/net/lib/shell/CMakeLists.txt +++ b/subsys/net/lib/shell/CMakeLists.txt @@ -9,6 +9,7 @@ zephyr_library_link_libraries_ifdef(CONFIG_MBEDTLS mbedTLS) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_PKT_ALLOC_SUPPORTED allocs.c) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_ETHERNET_SUPPORTED arp.c) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_CAPTURE_SUPPORTED capture.c) +zephyr_library_sources_ifdef(CONFIG_NET_SHELL_CONFIG_SETTINGS_SUPPORTED config.c) zephyr_library_sources(conn.c) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_DHCPV4_SUPPORTED dhcpv4.c) zephyr_library_sources_ifdef(CONFIG_NET_SHELL_DHCPV6_SUPPORTED dhcpv6.c) diff --git a/subsys/net/lib/shell/Kconfig b/subsys/net/lib/shell/Kconfig index c8d7c3ed2af4b..3adbeeb4ce75c 100644 --- a/subsys/net/lib/shell/Kconfig +++ b/subsys/net/lib/shell/Kconfig @@ -35,6 +35,11 @@ config NET_SHELL_CAPTURE_SUPPORTED default y depends on NET_SHELL_SHOW_DISABLED_COMMANDS || NET_CAPTURE +config NET_SHELL_CONFIG_SETTINGS_SUPPORTED + bool "Network stack configuration" + default y + depends on NET_SHELL_SHOW_DISABLED_COMMANDS || NET_CONFIG_SETTINGS + config NET_SHELL_DHCPV4_SUPPORTED bool "DHCPv4 start / stop" default y diff --git a/subsys/net/lib/shell/config.c b/subsys/net/lib/shell/config.c new file mode 100644 index 0000000000000..567090bad157f --- /dev/null +++ b/subsys/net/lib/shell/config.c @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_DECLARE(net_shell); + +#include + +#include "net_shell_private.h" + +#include + +#if defined(CONFIG_NET_CONFIG_SETTINGS_SHELL_ACCESS) +static struct net_init_config config; +#endif + +#define CHANGED(my, t, x) ((my)->__##x##_changed ? "+ " : ((t)->__##x##_changed ? "* " : "")) + +static int cmd_net_config(const struct shell *sh, size_t argc, char *argv[]) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + /* Print current configuration */ + +#if defined(CONFIG_NET_CONFIG_SETTINGS_SHELL_ACCESS) + static struct net_init_config current_config; + struct net_init_config *cfg; + int ret; + + cfg = ¤t_config; + + ret = net_config_get(cfg); + if (ret < 0) { + PR_ERROR("Failed to %s network configuration (%d)", "get", ret); + return ret; + } + + ARRAY_FOR_EACH(cfg->network_interfaces, i) { + struct net_init_config_network_interfaces *iface_cfg, *my_cfg; + + PR("Network interface: %d\n", i + 1); + + iface_cfg = &cfg->network_interfaces[i]; + my_cfg = &config.network_interfaces[i]; + + if (iface_cfg->bind_to > 0) { + PR("\t%sbind_to %d\n", CHANGED(my_cfg, iface_cfg, bind_to), + iface_cfg->bind_to); + } + + if (iface_cfg->name != NULL) { + PR("\t%sname %s\n", CHANGED(my_cfg, iface_cfg, name), + iface_cfg->name); + } + + if (iface_cfg->device_name != NULL) { + PR("\t%sdevice_name %s\n", + CHANGED(my_cfg, iface_cfg, device_name), + iface_cfg->device_name); + } + + if (iface_cfg->set_name != NULL) { + PR("\t%sset_name %s\n", + CHANGED(my_cfg, iface_cfg, set_name), + iface_cfg->set_name); + } + + PR("\t%sset_default %s\n", CHANGED(my_cfg, iface_cfg, set_default), + iface_cfg->set_default ? "yes" : "no"); + + if (iface_cfg->flags[0].value != NULL) { + PR("\t%sflags %s\n", + CHANGED(&my_cfg->flags[0], &iface_cfg->flags[0], value), + iface_cfg->flags[0].value); + } + + if (IS_ENABLED(MY_CFG_NET_IPV6)) { + PR("\tIPv6 configuration\n"); + + if (!iface_cfg->ipv6.status) { + PR("\t\t%sipv6.status %s\n", + CHANGED(&my_cfg->ipv6, &iface_cfg->ipv6, status), + "disabled"); + goto skip_ipv6; + } + + PR("\t\t%sipv6.status %s\n", + CHANGED(&my_cfg->ipv6, &iface_cfg->ipv6, status), + "enabled"); + PR("\t\t%sipv6.hop_limit %d\n", + CHANGED(&my_cfg->ipv6, &iface_cfg->ipv6, hop_limit), + iface_cfg->ipv6.hop_limit); + PR("\t\t%sipv6.multicast_hop_limit %d\n", + CHANGED(&my_cfg->ipv6, &iface_cfg->ipv6, multicast_hop_limit), + iface_cfg->ipv6.multicast_hop_limit); + + ARRAY_FOR_EACH(iface_cfg->ipv6.ipv6_addresses, j) { + + if (iface_cfg->ipv6.ipv6_addresses[j].value == NULL) { + continue; + } + + PR("\t\t%s-j %d ipv6.ipv6_addresses %s\n", + CHANGED(&my_cfg->ipv6.ipv6_addresses[j], + &iface_cfg->ipv6.ipv6_addresses[j], value), j, + iface_cfg->ipv6.ipv6_addresses[j].value); + } + + ARRAY_FOR_EACH(iface_cfg->ipv6.ipv6_multicast_addresses, j) { + + if (iface_cfg->ipv6.ipv6_multicast_addresses[j].value == NULL) { + continue; + } + + PR("\t\t%s-j %d ipv6.ipv6_multicast_addresses %s\n", + CHANGED(&my_cfg->ipv6.ipv6_multicast_addresses[j], + &iface_cfg->ipv6.ipv6_multicast_addresses[j], + value), j, + iface_cfg->ipv6.ipv6_multicast_addresses[j].value); + } + + ARRAY_FOR_EACH(iface_cfg->ipv6.prefixes, j) { + if (iface_cfg->ipv6.prefixes[j].address == NULL) { + continue; + } + + PR("\t\t%s-j %d ipv6.prefixes.address %s\n" + "\t\t%s-j %d ipv6.prefixes.len %d\n" + "\t\t%s-j %d ipv6.prefixes.lifetime %d\n", + CHANGED(&my_cfg->ipv6.prefixes[j], + &iface_cfg->ipv6.prefixes[j], address), j, + iface_cfg->ipv6.prefixes[j].address, + CHANGED(&my_cfg->ipv6.prefixes[j], + &iface_cfg->ipv6.prefixes[j], len), j, + iface_cfg->ipv6.prefixes[j].len, + CHANGED(&my_cfg->ipv6.prefixes[j], + &iface_cfg->ipv6.prefixes[j], lifetime), j, + iface_cfg->ipv6.prefixes[j].lifetime); + } + + if (IS_ENABLED(CONFIG_NET_DHCPV6)) { + PR("\t\t%sipv6.dhcpv6 %s\n", + CHANGED(&my_cfg->ipv6.dhcpv6, &iface_cfg->ipv6.dhcpv6, status), + iface_cfg->ipv6.dhcpv6.status ? "enabled" : "disabled"); + + if (iface_cfg->ipv6.dhcpv6.status) { + PR("\t\t\t%sipv6.do_request_address %s\n", + CHANGED(&my_cfg->ipv6.dhcpv6, &iface_cfg->ipv6.dhcpv6, + do_request_address), + iface_cfg->ipv6.dhcpv6.do_request_address ? + "yes" : "no"); + PR("\t\t\t%sipv6.do_request_prefix %s\n", + CHANGED(&my_cfg->ipv6.dhcpv6, &iface_cfg->ipv6.dhcpv6, + do_request_prefix), + iface_cfg->ipv6.dhcpv6.do_request_prefix ? + "yes" : "no"); + } + } + } + +skip_ipv6: + if (IS_ENABLED(CONFIG_NET_IPV4) && iface_cfg->ipv4.status) { + PR("\tIPv4 configuration\n"); + + if (!iface_cfg->ipv4.status) { + PR("\t\t%sipv4.status %s\n", + CHANGED(&my_cfg->ipv4, &iface_cfg->ipv4, status), + "disabled"); + goto skip_ipv4; + } + + PR("\t\t%sipv4.status %s\n", + CHANGED(&my_cfg->ipv4, &iface_cfg->ipv4, status), + "enabled"); + PR("\t\t%sipv4.time_to_live %d\n", + CHANGED(&my_cfg->ipv4, &iface_cfg->ipv4, time_to_live), + iface_cfg->ipv4.time_to_live); + PR("\t\t%sipv4.multicast_time_to_live %d\n", + CHANGED(&my_cfg->ipv4, &iface_cfg->ipv4, multicast_time_to_live), + iface_cfg->ipv4.multicast_time_to_live); + PR("\t\t%sipv4.gateway %s\n", + CHANGED(&my_cfg->ipv4, &iface_cfg->ipv4, gateway), + iface_cfg->ipv4.gateway); + + if (IS_ENABLED(CONFIG_NET_DHCPV4_SERVER)) { + PR("\t\t%sipv4.dhcpv4_server.status %s\n", + CHANGED(&my_cfg->ipv4.dhcpv4_server, + &iface_cfg->ipv4.dhcpv4_server, status), + iface_cfg->ipv4.dhcpv4_server.status ? "enabled" : "disabled"); + PR("\t\t%sipv4.dhcpv4_server.base_address %s\n", + CHANGED(&my_cfg->ipv4.dhcpv4_server, + &iface_cfg->ipv4.dhcpv4_server, + base_address), + iface_cfg->ipv4.dhcpv4_server.base_address); + } + + if (IS_ENABLED(CONFIG_NET_DHCPV4)) { + PR("\t\t%sipv4.dhcpv4.status %s\n", + CHANGED(&my_cfg->ipv4.dhcpv4, + &iface_cfg->ipv4.dhcpv4, status), + iface_cfg->ipv4.dhcpv4.status ? "enabled" : "disabled"); + } + + if (IS_ENABLED(CONFIG_NET_IPV4_AUTO)) { + PR("\t\t%sipv4.ipv4_autoconf.status %s\n", + CHANGED(&my_cfg->ipv4.ipv4_autoconf, + &iface_cfg->ipv4.ipv4_autoconf, + status), + iface_cfg->ipv4.ipv4_autoconf.status ? "enabled" : "disabled"); + } + + ARRAY_FOR_EACH(iface_cfg->ipv4.ipv4_addresses, j) { + + if (iface_cfg->ipv4.ipv4_addresses[j].value == NULL) { + continue; + } + + PR("\t\t%s-j %d ipv4.ipv4_addresses %s\n", + CHANGED(&my_cfg->ipv4.ipv4_addresses[j], + &iface_cfg->ipv4.ipv4_addresses[j], value), j, + iface_cfg->ipv4.ipv4_addresses[j].value); + } + + ARRAY_FOR_EACH(iface_cfg->ipv4.ipv4_multicast_addresses, j) { + + if (iface_cfg->ipv4.ipv4_multicast_addresses[j].value == NULL) { + continue; + } + + PR("\t\t%s-j %d ipv4.ipv4_multicast_addresses %s\n", + CHANGED(&my_cfg->ipv4.ipv4_multicast_addresses[j], + &iface_cfg->ipv4.ipv4_multicast_addresses[j], + value), j, + iface_cfg->ipv4.ipv4_multicast_addresses[j].value); + } + } + +skip_ipv4: + if (IS_ENABLED(CONFIG_NET_VLAN)) { + PR("\tVLAN configuration\n"); + + if (!iface_cfg->vlan.status) { + PR("\t\t%sstatus %s\n", + CHANGED(&my_cfg->vlan, &iface_cfg->vlan, status), + "disabled"); + goto skip_vlan; + } + + PR("\t\t%sstatus %s\n", + CHANGED(&my_cfg->vlan, &iface_cfg->vlan, status), + "enabled"); + PR("\t\t%stag %d\n", + CHANGED(&my_cfg->vlan, &iface_cfg->vlan, tag), + iface_cfg->vlan.tag); + } +skip_vlan: + } + + if (IS_ENABLED(CONFIG_NET_L2_IEEE802154)) { + PR("IEEE 802.15.4 configuration\n"); + + if (!cfg->ieee_802_15_4.status) { + PR("\t%sstatus %s\n", + CHANGED(&config.ieee_802_15_4, &cfg->ieee_802_15_4, status), + "disabled"); + goto skip_ieee802154; + } + + PR("\t%sstatus %s\n", + CHANGED(&config.ieee_802_15_4, &cfg->ieee_802_15_4, status), + "enabled"); + + if (cfg->ieee_802_15_4.bind_to > 0) { + PR("\t%sbind_to %d\n", + CHANGED(&config.ieee_802_15_4, &cfg->ieee_802_15_4, bind_to), + cfg->ieee_802_15_4.bind_to); + } + + PR("\t%span_id 0x%04X\n", + CHANGED(&config.ieee_802_15_4, &cfg->ieee_802_15_4, pan_id), + cfg->ieee_802_15_4.pan_id); + PR("\t%schannel %d\n", + CHANGED(&config.ieee_802_15_4, &cfg->ieee_802_15_4, channel), + cfg->ieee_802_15_4.channel); + PR("\t%stx_power %d dBm\n", + CHANGED(&config.ieee_802_15_4, &cfg->ieee_802_15_4, tx_power), + cfg->ieee_802_15_4.tx_power); + PR("\t%sack_required %s\n", + CHANGED(&config.ieee_802_15_4, &cfg->ieee_802_15_4, ack_required), + cfg->ieee_802_15_4.ack_required ? "yes" : "no"); + PR("\t%ssecurity_key_mode %d\n", + CHANGED(&config.ieee_802_15_4, &cfg->ieee_802_15_4, security_key_mode), + cfg->ieee_802_15_4.security_key_mode); + PR("\t%ssecurity_level %d\n", + CHANGED(&config.ieee_802_15_4, &cfg->ieee_802_15_4, security_level), + cfg->ieee_802_15_4.security_level); + PR("\t%ssecurity_key %d\n", + CHANGED(&config.ieee_802_15_4.security_key[0], + &cfg->ieee_802_15_4.security_key[0], value), + cfg->ieee_802_15_4.security_key[0].value); + } + +skip_ieee802154: + + if (IS_ENABLED(CONFIG_NET_CONFIG_CLOCK_SNTP_INIT)) { + PR("SNTP configuration\n"); + + if (!cfg->sntp.status) { + PR("\t%sstatus %s\n", + CHANGED(&config.sntp, &cfg->sntp, status), + "disabled"); + goto skip_sntp; + } + + PR("\t%sstatus %s\n", + CHANGED(&config.sntp, &cfg->sntp, status), + "enabled"); + + if (cfg->sntp.bind_to > 0) { + PR("\t%sbind_to %d\n", + CHANGED(&config.sntp, &cfg->sntp, bind_to), + cfg->sntp.bind_to); + } + + PR("\t%sserver %s\n", + CHANGED(&config.sntp, &cfg->sntp, server), + cfg->sntp.server); + PR("\t%stimeout %d ms\n", + CHANGED(&config.sntp, &cfg->sntp, timeout), + cfg->sntp.timeout); + } + +skip_sntp: +#else + PR_INFO("Set %s to enable %s support.\n", + "CONFIG_NET_CONFIG_SETTINGS_SHELL_ACCESS", + "network stack settings configuration"); +#endif + return 0; +} + +static int cmd_net_config_remove(const struct shell *sh, size_t argc, char *argv[]) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + +#if defined(CONFIG_NET_CONFIG_SETTINGS_SHELL_ACCESS) + int ret; + + memset(&config, 0, sizeof(config)); + + ret = net_config_clear(); + if (ret < 0) { + PR_ERROR("Failed to %s network configuration (%d)", "remove", ret); + return ret; + } + + PR("User configured network settings removed.\n"); +#else + PR_INFO("Set %s to enable %s support.\n", + "CONFIG_NET_CONFIG_SETTINGS_SHELL_ACCESS", + "network stack settings configuration"); +#endif + + return 0; +} + +#define CHECK_BASE_OPTION(opt, val, cfg) \ + if (strcmp(option, #opt) == 0) { \ + (cfg)->opt = val; \ + (cfg)->__##opt##_changed = true; \ + goto option_found; \ + } + +#define CHECK_BASE_INT_OPTION(opt, val, cfg) \ + if (strcmp(option, #opt) == 0) { \ + err = 0; \ + (cfg)->opt = shell_strtol(val, 10, &err); \ + if (err != 0) { \ + PR_WARNING("Parse error: %s\n", val); \ + return -ENOEXEC; \ + } \ + \ + (cfg)->__##opt##_changed = true; \ + goto option_found; \ + } + +#define CHECK_BASE_BOOL_OPTION(opt, val, cfg) \ + if (strcmp(option, #opt) == 0) { \ + if (strcmp(val, "yes") == 0 || \ + strcmp(val, "enabled") == 0 || \ + strcmp(val, "1") == 0 || \ + strcmp(val, "true") == 0) { \ + (cfg)->opt = true; \ + } else if (strcmp(val, "no") == 0 || \ + strcmp(val, "disabled") == 0 || \ + strcmp(val, "0") == 0 || \ + strcmp(val, "false") == 0) { \ + (cfg)->opt = false; \ + } else { \ + PR_WARNING("Invalid boolean value: %s\n", \ + val); \ + return -ENOEXEC; \ + } \ + \ + (cfg)->__##opt##_changed = true; \ + goto option_found; \ + } + +#define CHECK_SUB_OPTION(opt, name, val, cfg) \ + if (strcmp(option, #opt "." #name) == 0) { \ + (cfg)->opt.name = val; \ + (cfg)->opt.__##name##_changed = true; \ + goto option_found; \ + } + +#define CHECK_SUB_BOOL_OPTION(opt, name, val, cfg) \ + if (strcmp(option, #opt "." #name) == 0) { \ + if (strcmp(val, "yes") == 0 || \ + strcmp(val, "enabled") == 0 || \ + strcmp(val, "1") == 0 || \ + strcmp(val, "true") == 0) { \ + (cfg)->opt . name = true; \ + } else if (strcmp(val, "no") == 0 || \ + strcmp(val, "disabled") == 0 || \ + strcmp(val, "0") == 0 || \ + strcmp(val, "false") == 0) { \ + (cfg)->opt . name = false; \ + } else { \ + PR_WARNING("Invalid boolean value: %s\n", \ + val); \ + return -ENOEXEC; \ + } \ + \ + (cfg)->opt.__##name##_changed = true; \ + goto option_found; \ + } + +#define CHECK_SUB_INT_OPTION(opt, name, val, cfg) \ + if (strcmp(option, #opt "." #name) == 0) { \ + err = 0; \ + (cfg)->opt . name = shell_strtol(val, 10, &err);\ + if (err != 0) { \ + PR_WARNING("Parse error: %s\n", val); \ + return -ENOEXEC; \ + } \ + \ + (cfg)->opt.__##name##_changed = true; \ + goto option_found; \ + } + +#define CHECK_SUB_OPTION_ARRAY(opt, name, var, val, cfg) \ + if (strcmp(option, #opt "." #name) == 0) { \ + if (array_idx < 0 || array_idx >= ARRAY_SIZE((cfg)->opt.name)) { \ + PR_WARNING("Invalid array index: %d, should be >= 0 && < %d\n", \ + array_idx, ARRAY_SIZE((cfg)->opt.name)); \ + } else { \ + (cfg)->opt.name[array_idx].var = val; \ + (cfg)->opt.name[array_idx].__##var##_changed = true; \ + goto option_found; \ + } \ + } + +static int cmd_net_config_set(const struct shell *sh, size_t argc, char *argv[]) +{ +#if defined(CONFIG_NET_CONFIG_SETTINGS_SHELL_ACCESS) + struct net_init_config_network_interfaces *iface_cfg; + char *option = NULL, *value = NULL; + int iface_idx = -1, array_idx = -1; + int err; + + for (size_t i = 1; i < argc; ++i) { + + if (*argv[i] != '-') { + /* First non-option argument is the option to set, + * second is the value. Skip others. + */ + if (option == NULL) { + option = argv[i]; + } else if (value == NULL) { + value = argv[i]; + } + + continue; + } + + switch (argv[i][1]) { + case 'i': + err = 0; + + iface_idx = shell_strtol(argv[++i], 10, &err); + if (err != 0) { + PR_WARNING("Parse error: %s\n", argv[i]); + return -ENOEXEC; + } + + if (iface_idx > NET_CONFIG_NETWORK_INTERFACE_COUNT || + iface_idx <= 0) { + PR_WARNING("Invalid interface index: %d, " + "should be > 0 && <= %d\n", + iface_idx, + NET_CONFIG_NETWORK_INTERFACE_COUNT); + return -ENOEXEC; + } + + break; + + case 'j': + err = 0; + + array_idx = shell_strtol(argv[++i], 10, &err); + if (err != 0) { + PR_WARNING("Parse error: %s\n", argv[i]); + return -ENOEXEC; + } + + if (array_idx < 0) { + PR_WARNING("Invalid array index: %d, should be >= 0\n", + array_idx); + return -ENOEXEC; + } + + break; + + default: + PR_WARNING("Unrecognized argument: %s\n", argv[i]); + return -ENOEXEC; + } + } + + if (option == NULL || value == NULL) { + PR_WARNING("Option name and value must be specified.\n"); + return -ENOEXEC; + } + + if (iface_idx < 0) { + /* If user has not set the iface index, we assume 1 (the first interface). + */ + PR("Interface index not set, assuming interface 1.\n"); + iface_idx = 1; + } + + iface_cfg = &config.network_interfaces[iface_idx - 1]; + + CHECK_BASE_OPTION(name, value, iface_cfg); + CHECK_BASE_OPTION(device_name, value, iface_cfg); + CHECK_BASE_OPTION(set_name, value, iface_cfg); + CHECK_BASE_INT_OPTION(bind_to, value, iface_cfg); + CHECK_BASE_BOOL_OPTION(set_default, value, iface_cfg); + + if (strcmp(option, "flags") == 0) { + iface_cfg->flags[0].value = value; + iface_cfg->flags[0].__value_changed = true; + goto option_found; + } + + CHECK_SUB_BOOL_OPTION(ipv6, status, value, iface_cfg); + CHECK_SUB_INT_OPTION(ipv6, hop_limit, value, iface_cfg); + CHECK_SUB_INT_OPTION(ipv6, multicast_hop_limit, value, iface_cfg); + CHECK_SUB_BOOL_OPTION(ipv6.dhcpv6, status, value, iface_cfg); + CHECK_SUB_BOOL_OPTION(ipv6.dhcpv6, do_request_address, value, iface_cfg); + CHECK_SUB_BOOL_OPTION(ipv6.dhcpv6, do_request_prefix, value, iface_cfg); + + CHECK_SUB_OPTION_ARRAY(ipv6, ipv6_addresses, value, value, iface_cfg); + CHECK_SUB_OPTION_ARRAY(ipv6, ipv6_multicast_addresses, value, value, iface_cfg); + + CHECK_SUB_BOOL_OPTION(ipv4, status, value, iface_cfg); + CHECK_SUB_INT_OPTION(ipv4, time_to_live, value, iface_cfg); + CHECK_SUB_INT_OPTION(ipv4, multicast_time_to_live, value, iface_cfg); + CHECK_SUB_OPTION(ipv4, gateway, value, iface_cfg); + CHECK_SUB_BOOL_OPTION(ipv4.dhcpv4, status, value, iface_cfg); + CHECK_SUB_BOOL_OPTION(ipv4.ipv4_autoconf, status, value, iface_cfg); + CHECK_SUB_BOOL_OPTION(ipv4.dhcpv4_server, status, value, iface_cfg); + CHECK_SUB_OPTION(ipv4.dhcpv4_server, base_address, value, iface_cfg); + + CHECK_SUB_OPTION_ARRAY(ipv4, ipv4_addresses, value, value, iface_cfg); + CHECK_SUB_OPTION_ARRAY(ipv4, ipv4_multicast_addresses, value, value, iface_cfg); + + CHECK_SUB_BOOL_OPTION(vlan, status, value, iface_cfg); + CHECK_SUB_INT_OPTION(vlan, tag, value, iface_cfg); + + CHECK_SUB_BOOL_OPTION(ieee_802_15_4, status, value, &config); + CHECK_SUB_INT_OPTION(ieee_802_15_4, bind_to, value, &config); + CHECK_SUB_INT_OPTION(ieee_802_15_4, pan_id, value, &config); + CHECK_SUB_INT_OPTION(ieee_802_15_4, channel, value, &config); + CHECK_SUB_INT_OPTION(ieee_802_15_4, tx_power, value, &config); + CHECK_SUB_BOOL_OPTION(ieee_802_15_4, ack_required, value, &config); + CHECK_SUB_INT_OPTION(ieee_802_15_4, security_key_mode, value, &config); + CHECK_SUB_INT_OPTION(ieee_802_15_4, security_level, value, &config); + + if (strcmp(option, "ieee_802_15_4.security_key") == 0) { + int val; + + err = 0; + val = shell_strtol(value, 10, &err); + if (err != 0) { + PR_WARNING("Parse error: %s\n", value); + return -ENOEXEC; + } + + config.ieee_802_15_4.security_key[0].value = val; + config.ieee_802_15_4.security_key[0].__value_changed = true; + goto option_found; + } + + CHECK_SUB_BOOL_OPTION(sntp, status, value, &config); + CHECK_SUB_INT_OPTION(sntp, bind_to, value, &config); + CHECK_SUB_OPTION(sntp, server, value, &config); + CHECK_SUB_INT_OPTION(sntp, timeout, value, &config); + + PR_WARNING("Unrecognized option: %s\n", option); + return -ENOEXEC; + +option_found: + + PR("User configured network setting set.\n"); + PR("Do 'net config commit' to save the changes to permanent storage.\n"); +#else + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + PR_INFO("Set %s to enable %s support.\n", + "CONFIG_NET_CONFIG_SETTINGS_SHELL_ACCESS", + "network stack settings configuration"); +#endif + + return 0; +} + +static int cmd_net_config_commit(const struct shell *sh, size_t argc, char *argv[]) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + +#if defined(CONFIG_NET_CONFIG_SETTINGS_SHELL_ACCESS) + int ret; + + ret = net_config_set(&config); + if (ret < 0) { + PR_ERROR("Failed to %s network configuration (%d)", "commit", ret); + return ret; + } + + PR("User configured network settings saved.\n"); + + memset(&config, 0, sizeof(config)); +#else + PR_INFO("Set %s to enable %s support.\n", + "CONFIG_NET_CONFIG_SETTINGS_SHELL_ACCESS", + "network stack settings configuration"); +#endif + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_config, + SHELL_CMD(remove, NULL, "Remove user configured network settings.", + cmd_net_config_remove), + SHELL_CMD(set, NULL, + "'net config set [-i network interface in configuration] [-j array index] " + "