diff --git a/examples/protocols/asio/ssl_client_server/CMakeLists.txt b/examples/protocols/asio/ssl_client_server/CMakeLists.txt new file mode 100644 index 000000000..fcdac1488 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/CMakeLists.txt @@ -0,0 +1,10 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +# (Not part of the boilerplate) +# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(asio_ssl_client_server) diff --git a/examples/protocols/asio/ssl_client_server/Makefile b/examples/protocols/asio/ssl_client_server/Makefile new file mode 100644 index 000000000..af59f284f --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# +PROJECT_NAME := asio_ssl_client_server + +EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common + +include $(IDF_PATH)/make/project.mk diff --git a/examples/protocols/asio/ssl_client_server/README.md b/examples/protocols/asio/ssl_client_server/README.md new file mode 100644 index 000000000..e99ccfe11 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/README.md @@ -0,0 +1,85 @@ +# Asio SSL client/server example + +Simple Asio client and server with SSL/TLS transport + +## How to Use Example + +### Hardware Required + +This example can be executed on any ESP platform board. No external connection is required, it is recommended though +to connect to internet or a local network via WiFi or Ethernet to easily exercise features of this example. + +### Configure the project + +* Open the project configuration menu (`idf.py menuconfig`) +* Configure Wi-Fi or Ethernet under "Example Connection Configuration" menu. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](../../README.md) for more details. +* Enable the ASIO client and set server's host name to examine client's functionality. +The ASIO client connects to the configured server and sends default payload string "GET / HTTP/1.1" +* Enable the ASIO server to examine server's functionality. The ASIO server listens to connection and echos back what was received. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +### Client connecting to public server + +The below output illustrates the client connecting to a public https server. + +``` +I (1267) example_connect: Waiting for IP(s) +I (2587) wifi:new:<11,0>, old:<1,0>, ap:<255,255>, sta:<11,0>, prof:1 +I (3367) wifi:state: init -> auth (b0) +I (3377) wifi:state: auth -> assoc (0) +I (3387) wifi:state: assoc -> run (10) +I (3397) wifi:security type: 3, phy: bgn, rssi: -49 +I (3397) wifi:pm start, type: 1 +I (3457) wifi:AP's beacon interval = 102400 us, DTIM period = 1 +I (4747) example_connect: Got IPv6 event: Interface "example_connect: sta" address: fe80:0000:0000:0000:260a:xxxx:xxxx:xxxx, type: ESP_IP6_ADDR_IS_LINK_LOCAL +I (5247) esp_netif_handlers: example_connect: sta ip: 192.168.32.69, mask: 255.255.252.0, gw: 192.168.32.3 +I (5247) example_connect: Got IPv4 event: Interface "example_connect: sta" address: 192.168.32.69 +I (5257) example_connect: Connected to example_connect: sta +I (5257) example_connect: - IPv4 address: 192.168.32.69 +I (5267) example_connect: - IPv6 address: fe80:0000:0000:0000:260a:xxxx:xxxx:xxxx, type: ESP_IP6_ADDR_IS_LINK_LOCAL +W (5277) esp32_asio_pthread: pthread_condattr_setclock: not yet supported! +W (5297) esp32_asio_pthread: pthread_condattr_setclock: not yet supported! +Reply: HTTP/1.1 200 OK +D +``` +### Both server and client enabled + +The below output demonstrates the client connecting to the ASIO server via loopback interface, so no WiFi, nor Ethernet connection +was established. +``` +I (0) cpu_start: App cpu up. +I (495) heap_init: Initializing. RAM available for dynamic allocation: +I (502) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM +I (508) heap_init: At 3FFB5400 len 0002AC00 (171 KiB): DRAM +I (515) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM +I (521) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM +I (527) heap_init: At 4008BB80 len 00014480 (81 KiB): IRAM +I (534) cpu_start: Pro cpu start user code +I (556) spi_flash: detected chip: gd +I (556) spi_flash: flash io: dio +W (556) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header. +I (566) cpu_start: Starting scheduler on PRO CPU. +I (0) cpu_start: Starting scheduler on APP CPU. +I (600) example_connect: Waiting for IP(s) +W (600) esp32_asio_pthread: pthread_condattr_setclock: not yet supported! +W (1610) esp32_asio_pthread: pthread_condattr_setclock: not yet supported! +W (1610) esp32_asio_pthread: pthread_condattr_setclock: not yet supported! +Server received: GET / HTTP/1.1 + + +Reply: GET / HTTP/1.1 +``` +See the README.md file in the upper level 'examples' directory for more information about examples. diff --git a/examples/protocols/asio/ssl_client_server/example_test.py b/examples/protocols/asio/ssl_client_server/example_test.py new file mode 100644 index 000000000..876eb01b6 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/example_test.py @@ -0,0 +1,15 @@ +from __future__ import unicode_literals +import ttfw_idf + + +@ttfw_idf.idf_example_test(env_tag='Example_GENERIC') +def test_examples_asio_ssl(env, extra_data): + + dut = env.get_dut('asio_ssl_client_server', 'examples/protocols/asio/ssl_client_server') + dut.start_app() + + dut.expect('Reply: GET / HTTP/1.1') + + +if __name__ == '__main__': + test_examples_asio_ssl() diff --git a/examples/protocols/asio/ssl_client_server/main/CMakeLists.txt b/examples/protocols/asio/ssl_client_server/main/CMakeLists.txt new file mode 100644 index 000000000..5fd4fe15e --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "asio_ssl_main.cpp" + INCLUDE_DIRS "." + EMBED_TXTFILES cacert.pem prvtkey.pem) diff --git a/examples/protocols/asio/ssl_client_server/main/Kconfig.projbuild b/examples/protocols/asio/ssl_client_server/main/Kconfig.projbuild new file mode 100644 index 000000000..a5996f708 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/Kconfig.projbuild @@ -0,0 +1,28 @@ +menu "Example Configuration" + + config EXAMPLE_CLIENT + bool "Enable TLS client" + default y + help + Choose this option to use ASIO TLS/SSL client functionality + + config EXAMPLE_PORT + string "ASIO port number" + default "443" + help + Port number used by ASIO example. + + config EXAMPLE_SERVER + bool "Enable TLS server" + default n + help + Choose this option to use ASIO TLS/SSL server functionality + + config EXAMPLE_SERVER_NAME + string "ASIO server name or IP" + default "www.google.com" + depends on EXAMPLE_CLIENT + help + Asio example server ip for the ASIO client to connect to. + +endmenu diff --git a/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp b/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp new file mode 100644 index 000000000..8209b680d --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/asio_ssl_main.cpp @@ -0,0 +1,283 @@ +#include +#include "protocol_examples_common.h" +#include "esp_event.h" +#include "nvs_flash.h" +#include +#include +#include +#include +#include "asio.hpp" +#include "asio/ssl.hpp" +#include "asio/buffer.hpp" +#include "esp_pthread.h" + +extern const unsigned char cacert_pem_start[] asm("_binary_cacert_pem_start"); +extern const unsigned char cacert_pem_end[] asm("_binary_cacert_pem_end"); + +extern const unsigned char prvtkey_pem_start[] asm("_binary_prvtkey_pem_start"); +extern const unsigned char prvtkey_pem_end[] asm("_binary_prvtkey_pem_end"); + +const asio::const_buffer cert_chain(cacert_pem_start, cacert_pem_end - cacert_pem_start); +const asio::const_buffer privkey(prvtkey_pem_start, prvtkey_pem_end - prvtkey_pem_start); + +using asio::ip::tcp; + +using asio::ip::tcp; + +enum { max_length = 1024 }; + +class client +{ +public: + client(asio::io_context& io_context, + asio::ssl::context& context, + const tcp::resolver::results_type& endpoints) + : socket_(io_context, context) + { + socket_.set_verify_mode(asio::ssl::verify_peer); + + connect(endpoints); + } + +private: + void connect(const tcp::resolver::results_type& endpoints) + { + asio::async_connect(socket_.lowest_layer(), endpoints, + [this](const std::error_code& error, + const tcp::endpoint& /*endpoint*/) + { + if (!error) + { + handshake(); + } + else + { + std::cout << "Connect failed: " << error.message() << "\n"; + } + }); + } + + void handshake() + { + socket_.async_handshake(asio::ssl::stream_base::client, + [this](const std::error_code& error) + { + if (!error) + { + send_request(); + } + else + { + std::cout << "Handshake failed: " << error.message() << "\n"; + } + }); + } + + void send_request() + { + size_t request_length = std::strlen(request_); + + asio::async_write(socket_, + asio::buffer(request_, request_length), + [this](const std::error_code& error, std::size_t length) + { + if (!error) + { + receive_response(length); + } + else + { + std::cout << "Write failed: " << error.message() << "\n"; + } + }); + } + + void receive_response(std::size_t length) + { + asio::async_read(socket_, + asio::buffer(reply_, length), + [this](const std::error_code& error, std::size_t length) + { + if (!error) + { + std::cout << "Reply: "; + std::cout.write(reply_, length); + std::cout << "\n"; + } + else + { + std::cout << "Read failed: " << error.message() << "\n"; + } + }); + + } + + asio::ssl::stream socket_; + char request_[max_length] = "GET / HTTP/1.1\r\n\r\n"; + char reply_[max_length]; +}; + +class session : public std::enable_shared_from_this +{ +public: + session(tcp::socket socket, asio::ssl::context& context) + : socket_(std::move(socket), context) + { + } + + void start() + { + do_handshake(); + } + +private: + void do_handshake() + { + auto self(shared_from_this()); + socket_.async_handshake(asio::ssl::stream_base::server, + [this, self](const std::error_code& error) + { + if (!error) + { + do_read(); + } + }); + } + + void do_read() + { + auto self(shared_from_this()); + socket_.async_read_some(asio::buffer(data_), + [this, self](const std::error_code& ec, std::size_t length) + { + if (!ec) + { + data_[length] = 0; + std::cout << "Server received: " << data_ << std::endl; + do_write(length); + } + }); + } + + void do_write(std::size_t length) + { + auto self(shared_from_this()); + asio::async_write(socket_, asio::buffer(data_, length), + [this, self](const std::error_code& ec, + std::size_t /*length*/) + { + if (!ec) + { + do_read(); + } + }); + } + + asio::ssl::stream socket_; + char data_[1024]; +}; + +class server +{ +public: + server(asio::io_context& io_context, unsigned short port) + : acceptor_(io_context, tcp::endpoint(tcp::v4(), port)), + context_(asio::ssl::context::tls_server) + { + context_.set_options( + asio::ssl::context::default_workarounds + | asio::ssl::context::no_sslv2); + context_.use_certificate_chain(cert_chain); + context_.use_private_key(privkey, asio::ssl::context::pem); + + do_accept(); + } + +private: + void do_accept() + { + acceptor_.async_accept( + [this](const std::error_code& error, tcp::socket socket) + { + if (!error) + { + std::make_shared(std::move(socket), context_)->start(); + } + + do_accept(); + }); + } + + tcp::acceptor acceptor_; + asio::ssl::context context_; +}; + +void set_thread_config(const char *name, int stack, int prio) +{ + auto cfg = esp_pthread_get_default_config(); + cfg.thread_name = name; + cfg.stack_size = stack; + cfg.prio = prio; + esp_pthread_set_cfg(&cfg); +} + +void ssl_server_thread() +{ + asio::io_context io_context; + + server s(io_context, 443); + + io_context.run(); +} + +void ssl_client_thread() +{ + asio::io_context io_context; + + tcp::resolver resolver(io_context); + std::string server_ip = CONFIG_EXAMPLE_SERVER_NAME; + std::string server_port = CONFIG_EXAMPLE_PORT; + auto endpoints = resolver.resolve(server_ip, server_port); + + asio::ssl::context ctx(asio::ssl::context::tls_client); + ctx.use_certificate_chain(cert_chain); + + client c(io_context, ctx, endpoints); + + io_context.run(); + +} + + +extern "C" void app_main(void) +{ + ESP_ERROR_CHECK(nvs_flash_init()); + esp_netif_init(); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + */ + ESP_ERROR_CHECK(example_connect()); + + /* This helper function configures blocking UART I/O */ + ESP_ERROR_CHECK(example_configure_stdin_stdout()); + std::vector work_threads; + +#if CONFIG_EXAMPLE_SERVER + set_thread_config("Server", 16 * 1024, 5); + work_threads.emplace_back(std::thread(ssl_server_thread)); + std::this_thread::sleep_for(std::chrono::seconds(1)); +#endif // CONFIG_EXAMPLE_SERVER + +#if CONFIG_EXAMPLE_CLIENT + set_thread_config("Client", 16 * 1024, 5); + work_threads.emplace_back(ssl_client_thread); +#endif // CONFIG_EXAMPLE_CLIENT + + for (auto & t : work_threads) { + t.join(); + } + +} diff --git a/examples/protocols/asio/ssl_client_server/main/cacert.pem b/examples/protocols/asio/ssl_client_server/main/cacert.pem new file mode 100644 index 000000000..e09c3989c --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/cacert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIJAPMMNobNczaUMA0GCSqGSIb3DQEBBAUAMHQxEzARBgNV +BAMTCk15IFRlc3QgQ0ExCzAJBgNVBAgTAkhaMQswCQYDVQQGEwJDTjEcMBoGCSqG +SIb3DQEJARYNdGVzdEBjZXJ0LmNvbTElMCMGA1UEChMcUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNjExMTUwNTA0MThaFw0xOTExMTUwNTA0MThaMHQx +EzARBgNVBAMTCk15IFRlc3QgQ0ExCzAJBgNVBAgTAkhaMQswCQYDVQQGEwJDTjEc +MBoGCSqGSIb3DQEJARYNdGVzdEBjZXJ0LmNvbTElMCMGA1UEChMcUm9vdCBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALDjSPDlomepHCzbw4MUrquQAU0xTV4/Npb27k9I5TRVTjIoOs/5hNI2LPFW +e4CREx09ZrT8K3NFOBoSy7bhPAsjGaFxCYYWc9tiX1m5gq3ToVRSmbZ65fE3kvnI +8E/d5VyzA0OMmWbfaolBSTMoWgqRynEaT+z1Eh2yDTzVFy9eov1DdQFUqGDqbH5b +QYvTY5Fyem7UcKWAe2yS0j3H4dVtVBKNY7qV3Px08yGAs5fQFgUwhyB5+qwhvkeL +JdgapGaSTwLgoQKWHbe/lA3NiBIB9hznFUGKo3hmniAvYZbrQcn3tc0l/J4I39v2 +Pm29FAyjWvQyBkGktz2q4elOZYkCAwEAAaMQMA4wDAYDVR0TBAUwAwEB/zANBgkq +hkiG9w0BAQQFAAOCAQEAJCJ+97oae/FcOLbPpjCpUQnWqYydgSChgalkZNvr4fVp +TnuNg471l0Y2oTJLoWn2YcbPSFVOEeKkU47mpjMzucHHp0zGaW9SdzhZalWwmbgK +q2ijecIbuFHFNedYTk/03K7eaAcjVhD8e0oOJImeLOL6DAFivA1LUnSgXsdGPDtD +zhISsCPTu+cL1j0yP6HBvLeAyb8kaCWJ05RtiVLRANNHQn/keHajJYpMwnEEbJdG +cqN3whfJoGVbZ6isEf2RQJ0pYRnP7uGLW3wGkLWxfdto8uER8HVDx7fZpevLIqGd +1OoSEi3cIJXWBAjx0TLzzhtb6aeIxBJWQqHThtkKdg== +-----END CERTIFICATE----- diff --git a/examples/protocols/asio/ssl_client_server/main/component.mk b/examples/protocols/asio/ssl_client_server/main/component.mk new file mode 100644 index 000000000..656f72d7b --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/component.mk @@ -0,0 +1,11 @@ +# +# Main component makefile. +# +# This Makefile can be left empty. By default, it will take the sources in the +# src/ directory, compile them and link them into lib(subdirectory_name).a +# in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +COMPONENT_EMBED_TXTFILES := cacert.pem +COMPONENT_EMBED_TXTFILES += prvtkey.pem diff --git a/examples/protocols/asio/ssl_client_server/main/prvtkey.pem b/examples/protocols/asio/ssl_client_server/main/prvtkey.pem new file mode 100644 index 000000000..4ead61f6f --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/main/prvtkey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAsONI8OWiZ6kcLNvDgxSuq5ABTTFNXj82lvbuT0jlNFVOMig6 +z/mE0jYs8VZ7gJETHT1mtPwrc0U4GhLLtuE8CyMZoXEJhhZz22JfWbmCrdOhVFKZ +tnrl8TeS+cjwT93lXLMDQ4yZZt9qiUFJMyhaCpHKcRpP7PUSHbINPNUXL16i/UN1 +AVSoYOpsfltBi9NjkXJ6btRwpYB7bJLSPcfh1W1UEo1jupXc/HTzIYCzl9AWBTCH +IHn6rCG+R4sl2BqkZpJPAuChApYdt7+UDc2IEgH2HOcVQYqjeGaeIC9hlutByfe1 +zSX8ngjf2/Y+bb0UDKNa9DIGQaS3Parh6U5liQIDAQABAoIBAB9K9jp3xXVlO3DM +KBhmbkg3n6NSV4eW00d9w8cO9E1/0eeZql3knJS7tNO1IwApqiIAHM1j1yP7WONz +88oUqpSlzwD6iF7KVhC3pHqxEOdDi0Tpn/viXg+Ab2X1IF5guRTfLnKiyviiCazi +edqtBtDb3d6Icx9Oc7gBKcpbQFDGt++wSOb5L+xhRm9B5B4l/6byikiPeKqIK5tC +SoP9Zr1mvpNoGm1P4LvEunFJcRBqVI010VNwfO9P98oVyzJu9/FZZrQxXoY9JdXF +OM6nbl+hMDM3TkEOda9NvBhImozEAvuc97CaaXyR3XivxMqNqNIb4+syUPa2PCS3 +ZztI5qECgYEA1gbVG6ifpvpbBkDPi3Im8fM3F7FLLrQc48FdFjdMvDhHD9lVKucD +Uaa8PF9dbbvlu2cwMyfBOKSuWaXxRxRsiqiPmTunS1MvPzQcSrGwUrL2AogGucn6 ++NrLQf5P4H5IpkDQ9ih3zwjO6xKFK1WeYnYpHM8qUBtl6q0YFyVBPu0CgYEA05Pn +StWA4D7VSbNnVi6lvFyEOUsTrK3v419598TFiq4eXLq6aV8/CQYzKsSzoG+aOZhX +Li+0uyT5cNzUcXYhTsW1hA/pNhMfxMrYiB1x14zlLp2WRGg4vd/+SxX6d9Yd3acX +7QzPKgdDicXs9QN8ozJOICKvNbUI53AJdATVEY0CgYEAwvpGeoQLrdq1weSZLrg3 +soOX1QW3MDz1dKdbXjnStkWut0mOxR7fbysuoPFf8/ARQcCnsHKvHCMqkpESVWbN +2yPkbfxiU8Tcbf/TJljqAOz4ISY6ula/RKZONTixHBrvpEW4GAiV3Q5xMsYUe33s +ZFaw7YXtTj0ng7tdDvjpj6ECgYEApHdUU9ejVq2BHslWiqe4LbO9FMxHfvO2hgix +xugupp6y+2Irhb2EQn+PRq+g8hXOzPaezkhHNTKItDL08T3iplkJwJ6dqmszRsZn +i2dYFzZu8M2PAZ4CfZahFbz/9id7D9HTx3EtmH4NAgvZJpyPRkzUbiaIDDettDpj +Hsyi1AECgYAPLvjBzQj4kPF8Zo9pQEUcz4pmupRVfv3aRfjnahDK4qZHEePDRj+J +W7pzayrs1dyN9QLB8pTc424z7f8MB3llCICN+ohs8CR/eW0NEobE9ldDOeoCr1Vh +NhNSbrN1iZ8U4oLkRTMaDKkVngGffvjGi/q0tOU7hJdZOqNlk2Iahg== +-----END RSA PRIVATE KEY----- diff --git a/examples/protocols/asio/ssl_client_server/partitions.csv b/examples/protocols/asio/ssl_client_server/partitions.csv new file mode 100644 index 000000000..d01414b8a --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/partitions.csv @@ -0,0 +1,5 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1200000, diff --git a/examples/protocols/asio/ssl_client_server/sdkconfig.ci b/examples/protocols/asio/ssl_client_server/sdkconfig.ci new file mode 100644 index 000000000..958aa45d4 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/sdkconfig.ci @@ -0,0 +1,6 @@ +CONFIG_EXAMPLE_CLIENT=y +CONFIG_EXAMPLE_SERVER=y +CONFIG_EXAMPLE_SERVER_NAME="localhost" +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_EXAMPLE_CONNECT_ETHERNET=n + diff --git a/examples/protocols/asio/ssl_client_server/sdkconfig.defaults b/examples/protocols/asio/ssl_client_server/sdkconfig.defaults new file mode 100644 index 000000000..8ff365b19 --- /dev/null +++ b/examples/protocols/asio/ssl_client_server/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"