diff --git a/esphome/components/wireguard/__init__.py b/esphome/components/wireguard/__init__.py index 069b291574..e9641a5957 100644 --- a/esphome/components/wireguard/__init__.py +++ b/esphome/components/wireguard/__init__.py @@ -121,16 +121,19 @@ async def to_code(config): ) ) + # Shared flash table ended by an empty entry, so the component stores only a pointer. + entries = [ + cg.StructInitializer( + AllowedIP, + ("ip", str(ip.network_address)), + ("netmask", str(ip.netmask)), + ) + for ip in allowed_ips + ] + end = cg.StructInitializer(AllowedIP, ("ip", cg.nullptr), ("netmask", cg.nullptr)) cg.add( var.set_allowed_ips( - [ - cg.StructInitializer( - AllowedIP, - ("ip", str(ip.network_address)), - ("netmask", str(ip.netmask)), - ) - for ip in allowed_ips - ] + cg.shared_progmem_array("wireguard_allowed_ips", AllowedIP, [*entries, end]) ) ) diff --git a/esphome/components/wireguard/wireguard.cpp b/esphome/components/wireguard/wireguard.cpp index 73e34b6592..01921f17e8 100644 --- a/esphome/components/wireguard/wireguard.cpp +++ b/esphome/components/wireguard/wireguard.cpp @@ -154,8 +154,8 @@ void Wireguard::dump_config() { (this->preshared_key_ != nullptr ? preshared_key_masked : LOG_STR_LITERAL("NOT IN USE"))); // clang-format on ESP_LOGCONFIG(TAG, " Peer Allowed IPs:"); - for (const AllowedIP &allowed_ip : this->allowed_ips_) { - ESP_LOGCONFIG(TAG, " - %s/%s", allowed_ip.ip, allowed_ip.netmask); + for (const AllowedIP *it = this->allowed_ips_; it != nullptr && it->ip != nullptr; it++) { + ESP_LOGCONFIG(TAG, " - %s/%s", it->ip, it->netmask); } ESP_LOGCONFIG(TAG, " Peer Persistent Keepalive: %d%s", this->keepalive_, (this->keepalive_ > 0 ? LOG_STR_LITERAL("s") : LOG_STR_LITERAL(" (DISABLED)"))); @@ -249,8 +249,8 @@ void Wireguard::start_connection_() { ESP_LOGD(TAG, "Configuring allowed IPs list"); bool allowed_ips_ok = true; - for (const AllowedIP &ip : this->allowed_ips_) { - allowed_ips_ok &= (esp_wireguard_add_allowed_ip(&(this->wg_ctx_), ip.ip, ip.netmask) == ESP_OK); + for (const AllowedIP *it = this->allowed_ips_; it != nullptr && it->ip != nullptr; it++) { + allowed_ips_ok &= (esp_wireguard_add_allowed_ip(&(this->wg_ctx_), it->ip, it->netmask) == ESP_OK); } if (allowed_ips_ok) { diff --git a/esphome/components/wireguard/wireguard.h b/esphome/components/wireguard/wireguard.h index faf63cd444..6125d2f52c 100644 --- a/esphome/components/wireguard/wireguard.h +++ b/esphome/components/wireguard/wireguard.h @@ -2,7 +2,6 @@ #include "esphome/core/defines.h" #ifdef USE_WIREGUARD #include -#include #include "esphome/core/component.h" #include "esphome/core/helpers.h" @@ -58,9 +57,8 @@ class Wireguard final : public PollingComponent { void set_peer_public_key(const std::string &key) = delete; void set_preshared_key(const std::string &key) = delete; - void set_allowed_ips(std::initializer_list ips) { this->allowed_ips_ = ips; } - /// Prevent accidental use of std::string which would dangle - void set_allowed_ips(std::initializer_list> ips) = delete; + /// Table ended by an entry with a null ip; must outlive the component. + void set_allowed_ips(const AllowedIP *ips) { this->allowed_ips_ = ips; } void set_keepalive(const uint16_t seconds) { this->keepalive_ = seconds; } void set_reboot_timeout(const uint32_t seconds) { this->reboot_timeout_ = seconds; } @@ -105,7 +103,7 @@ class Wireguard final : public PollingComponent { const char *peer_public_key_{nullptr}; const char *preshared_key_{nullptr}; - FixedVector allowed_ips_; + const AllowedIP *allowed_ips_{nullptr}; uint16_t peer_port_; uint16_t keepalive_; diff --git a/tests/component_tests/wireguard/config/allowed_ips.yaml b/tests/component_tests/wireguard/config/allowed_ips.yaml new file mode 100644 index 0000000000..7f191f90fb --- /dev/null +++ b/tests/component_tests/wireguard/config/allowed_ips.yaml @@ -0,0 +1,26 @@ +esphome: + name: test + +esp32: + board: esp32dev + framework: + type: esp-idf + +wifi: + ssid: test + password: password1 + +time: + - platform: sntp + id: sntp_time + +wireguard: + time_id: sntp_time + address: 172.16.34.100 + netmask: 255.255.255.0 + private_key: wPBMxtNYH3mChicrbpsRpZIasIdPq3yZuthn23FbGG8= + peer_public_key: Hs2JfikvYU03/Kv3YoAs1hrUIPPTEkpsZKSPUljE9yc= + peer_endpoint: wg.server.example + peer_allowed_ips: + - 172.16.34.0/24 + - 192.168.0.0/16 diff --git a/tests/component_tests/wireguard/test_allowed_ips.py b/tests/component_tests/wireguard/test_allowed_ips.py new file mode 100644 index 0000000000..ff490c516e --- /dev/null +++ b/tests/component_tests/wireguard/test_allowed_ips.py @@ -0,0 +1,21 @@ +"""Tests for the wireguard allowed IPs codegen.""" + +from collections.abc import Callable +from pathlib import Path + + +def test_allowed_ips_are_a_progmem_table( + generate_main: Callable[[str | Path], str], + component_config_path: Callable[[str], Path], +) -> None: + """Allowed IPs live in a flash table ended by an empty entry instead of a heap vector.""" + main_cpp = generate_main(component_config_path("allowed_ips.yaml")) + + table = main_cpp.split( + "wireguard::AllowedIP wireguard_allowed_ips[] PROGMEM = ", 1 + )[1] + table = table.split(";", 1)[0] + assert '.ip = "172.16.34.0"' in table + assert '.netmask = "255.255.0.0"' in table + assert ".ip = nullptr" in table + assert "->set_allowed_ips(wireguard_allowed_ips);" in main_cpp