fix(network): rebuild the DHCP hostname when the node is renamed

The hostname was built once in begin() from the node name, so `set name` left
the node advertising its old name to DHCP until the next reflash. It is now
rebuilt on rename through a new CommonCLICallbacks::onNodeNameChanged() hook,
and begin() shares the same helper.

What this cannot do is rename a live lease. IDF 4.4 states that a hostname
changed after the interface is up "would only be reflected once the interface
restarts/reconnects", and Arduino 2.x's WiFi.setHostname() does not touch a
running netif at all — it writes a static string that the netif reads at
bring-up. Forcing the issue would mean bouncing the link, which costs every
MQTT slot a reconnect; that is not a reasonable price for a rename, so the new
name lands at the next reconnect or boot instead.

Only automatic (Ethernet-preferred) links carry a hostname today, exactly as
before. Giving plain Wi-Fi observers one would change the DHCP identity of the
existing fleet and is a separate decision.
This commit is contained in:
agessaman committed 2026-09-19 15:57:45 -07:00
1 parent c781862362
commit da6406e92f
6 files changed
+75 -10

No files matched your search

+22 -5
View File
@@ -1123,6 +1123,27 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
memset(default_scope.key, 0, sizeof(default_scope.key));
}
#if defined(WITH_MQTT_BRIDGE) && defined(ESP_PLATFORM)
// Only an automatic link carries a hostname today (see the header note). The
// value reaches DHCP when the link next starts or reconnects: IDF 4.4 states
// plainly that a hostname changed after the interface is up "would only be
// reflected once the interface restarts/reconnects", and Arduino 2.x's
// WiFi.setHostname() does not even touch a running netif — it writes a static
// string the netif reads at bring-up. So this cannot rename a live lease, and
// deliberately does not bounce the link to force one: dropping an observer's
// route mid-operation costs every MQTT slot a reconnect, which is not a
// reasonable price for a cosmetic rename.
void MyMesh::applyNetworkHostname(const char* when) {
NetworkLink& link = activeNetworkLink();
if (!link.isAutomatic()) return;
char network_hostname[NetworkHostname::kBufferSize];
NetworkHostname::build(network_hostname, sizeof(network_hostname),
_prefs.node_name, self_id.pub_key, PUB_KEY_SIZE);
link.setHostname(network_hostname);
Serial.printf("Network: hostname %s%s\n", network_hostname, when);
}
#endif
void MyMesh::begin(FILESYSTEM *fs) {
mesh::Mesh::begin();
_fs = fs;
@@ -1151,11 +1172,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
#if defined(WITH_MQTT_BRIDGE) && defined(ESP_PLATFORM)
NetworkLink& boot_network = activeNetworkLink();
if (boot_network.isAutomatic()) {
char network_hostname[NetworkHostname::kBufferSize];
NetworkHostname::build(network_hostname, sizeof(network_hostname),
_prefs.node_name, self_id.pub_key, PUB_KEY_SIZE);
boot_network.setHostname(network_hostname);
Serial.printf("Network: hostname %s\n", network_hostname);
applyNetworkHostname("");
MQTTPrefs* obs = _cli.getObserverPrefs();
Serial.printf("Network: probing Ethernet for up to %lums\n",
(unsigned long)NETWORK_ETHERNET_BOOT_WAIT_MS);
+13
View File
@@ -274,10 +274,23 @@ protected:
void sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size);
// Build the DHCP hostname from the current node name and hand it to the
// link. Only automatic links carry one: a plain Wi-Fi observer keeps the
// framework default, and changing that for the existing fleet is a separate
// decision.
void applyNetworkHostname(const char* when);
public:
MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables);
void begin(FILESYSTEM* fs);
// CommonCLICallbacks: `set name` has just rewritten node_name.
void onNodeNameChanged() override {
#if defined(WITH_MQTT_BRIDGE) && defined(ESP_PLATFORM)
applyNetworkHostname(" (renamed)");
#endif
}
void sendNodeDiscoverReq();
const char* getFirmwareVer() override { return FIRMWARE_VERSION; }
const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; }
+22 -5
View File
@@ -943,6 +943,27 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
memset(default_scope.key, 0, sizeof(default_scope.key));
}
#if defined(WITH_MQTT_BRIDGE) && defined(ESP_PLATFORM)
// Only an automatic link carries a hostname today (see the header note). The
// value reaches DHCP when the link next starts or reconnects: IDF 4.4 states
// plainly that a hostname changed after the interface is up "would only be
// reflected once the interface restarts/reconnects", and Arduino 2.x's
// WiFi.setHostname() does not even touch a running netif — it writes a static
// string the netif reads at bring-up. So this cannot rename a live lease, and
// deliberately does not bounce the link to force one: dropping an observer's
// route mid-operation costs every MQTT slot a reconnect, which is not a
// reasonable price for a cosmetic rename.
void MyMesh::applyNetworkHostname(const char* when) {
NetworkLink& link = activeNetworkLink();
if (!link.isAutomatic()) return;
char network_hostname[NetworkHostname::kBufferSize];
NetworkHostname::build(network_hostname, sizeof(network_hostname),
_prefs.node_name, self_id.pub_key, PUB_KEY_SIZE);
link.setHostname(network_hostname);
Serial.printf("Network: hostname %s%s\n", network_hostname, when);
}
#endif
void MyMesh::begin(FILESYSTEM *fs) {
mesh::Mesh::begin();
_fs = fs;
@@ -952,11 +973,7 @@ void MyMesh::begin(FILESYSTEM *fs) {
#if defined(WITH_MQTT_BRIDGE) && defined(ESP_PLATFORM)
NetworkLink& boot_network = activeNetworkLink();
if (boot_network.isAutomatic()) {
char network_hostname[NetworkHostname::kBufferSize];
NetworkHostname::build(network_hostname, sizeof(network_hostname),
_prefs.node_name, self_id.pub_key, PUB_KEY_SIZE);
boot_network.setHostname(network_hostname);
Serial.printf("Network: hostname %s\n", network_hostname);
applyNetworkHostname("");
MQTTPrefs* obs = _cli.getObserverPrefs();
Serial.printf("Network: probing Ethernet for up to %lums\n",
(unsigned long)NETWORK_ETHERNET_BOOT_WAIT_MS);
+13
View File
@@ -281,10 +281,23 @@ protected:
void sendFloodReply(mesh::Packet* packet, unsigned long delay_millis, uint8_t path_hash_size);
// Build the DHCP hostname from the current node name and hand it to the
// link. Only automatic links carry one: a plain Wi-Fi observer keeps the
// framework default, and changing that for the existing fleet is a separate
// decision.
void applyNetworkHostname(const char* when);
public:
MyMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables);
void begin(FILESYSTEM* fs);
// CommonCLICallbacks: `set name` has just rewritten node_name.
void onNodeNameChanged() override {
#if defined(WITH_MQTT_BRIDGE) && defined(ESP_PLATFORM)
applyNetworkHostname(" (renamed)");
#endif
}
void addSystemPost(const char* postData);
const char* getFirmwareVer() override { return FIRMWARE_VERSION; }
+1
View File
@@ -1583,6 +1583,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
if (isValidName(&config[5])) {
StrHelper::strncpy(_prefs->node_name, &config[5], sizeof(_prefs->node_name));
savePrefs();
_callbacks->onNodeNameChanged();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, bad chars");
+4
View File
@@ -274,6 +274,10 @@ struct LegacyObserverTail {
class CommonCLICallbacks {
public:
virtual void savePrefs() = 0;
// The node name feeds the DHCP hostname, which is built once when the link
// starts. Renaming a running node has to rebuild it or the node keeps
// advertising the old name to DHCP until the next reflash.
virtual void onNodeNameChanged() {}
#ifdef WITH_MQTT_BRIDGE
virtual bool saveObserverPrefs() = 0;
#else