301 lines
12 KiB
Markdown
301 lines
12 KiB
Markdown
|
|
# Shelly DHCP Investigation
|
||
|
|
|
||
|
|
Date: 2026-04-17
|
||
|
|
|
||
|
|
Update: the Kea reservation workaround was applied during this pass.
|
||
|
|
|
||
|
|
## Scope
|
||
|
|
|
||
|
|
This document investigates why `Shelly-Server` is live at `192.168.100.127` even though OPNsense at `192.168.100.254` has a reservation for the device at `192.168.100.75` and is expected to be the only authoritative DHCP server on the LAN.
|
||
|
|
|
||
|
|
No destructive changes were made during this pass. All checks were read-only diagnostics.
|
||
|
|
|
||
|
|
## Executive Summary
|
||
|
|
|
||
|
|
The strongest current evidence does not point to a confirmed rogue DHCP server.
|
||
|
|
|
||
|
|
It also does not support the idea that OPNsense is holding an old reservation for a replaced Shelly unit.
|
||
|
|
|
||
|
|
The stronger explanation is:
|
||
|
|
|
||
|
|
1. `Shelly-Server` is configured for DHCP, not a static IP.
|
||
|
|
2. The device is live and reports itself as `192.168.100.127`.
|
||
|
|
3. The firewall still has a static reservation for the device's factory MAC `34:94:54:73:E2:28` at `192.168.100.75`.
|
||
|
|
4. On the LAN, `192.168.100.127` resolves to a different MAC, `02:0F:B5:73:E2:28`, not the Shelly factory MAC.
|
||
|
|
5. That observed MAC is locally administered and preserves only the last three bytes of the Shelly MAC.
|
||
|
|
|
||
|
|
This strongly suggests an intermediate bridge/router/AP behavior that rewrites the client MAC on the wire. If that is true, OPNsense may still be the DHCP server, but the reservation on `34:94:54:73:E2:28` will never match because the DHCP server sees `02:0F:B5:73:E2:28` instead.
|
||
|
|
|
||
|
|
If you had replaced the Shelly and forgotten to update the reservation, the reservation MAC and the live device's own reported MAC would differ. In this case they match, so the reservation appears tied to the correct physical Shelly, just not to the effective MAC seen on the LAN.
|
||
|
|
|
||
|
|
## Confirmed Facts
|
||
|
|
|
||
|
|
### 1. OPNsense LAN identity
|
||
|
|
|
||
|
|
- LAN interface IP in OPNsense config: `192.168.100.254/24`
|
||
|
|
- The local Windows host on the same subnet has:
|
||
|
|
- IPv4 address `192.168.100.134`
|
||
|
|
- default gateway `192.168.100.254`
|
||
|
|
- DHCP server `192.168.100.254`
|
||
|
|
- DNS server `192.168.100.254`
|
||
|
|
|
||
|
|
This confirms that normal clients on the LAN are getting DHCP from the firewall.
|
||
|
|
|
||
|
|
### 2. Shelly device runtime state
|
||
|
|
|
||
|
|
Direct HTTP inspection of the live device at `http://192.168.100.127/` returned:
|
||
|
|
|
||
|
|
- device name `Shelly-Server`
|
||
|
|
- device type `SHEM` / Shelly EM
|
||
|
|
- factory MAC `34945473E228`
|
||
|
|
- Wi-Fi SSID `Shellynet`
|
||
|
|
- IP `192.168.100.127`
|
||
|
|
- Wi-Fi mode `ipv4_method: dhcp`
|
||
|
|
- MQTT connected `true`
|
||
|
|
|
||
|
|
This rules out a device-side static IP configuration.
|
||
|
|
|
||
|
|
### 3. OPNsense reservation and DNS state
|
||
|
|
|
||
|
|
OPNsense config contains a reservation for:
|
||
|
|
|
||
|
|
- IP `192.168.100.75`
|
||
|
|
- MAC `34:94:54:73:e2:28`
|
||
|
|
- hostname `Shelly-Server`
|
||
|
|
|
||
|
|
The live Shelly web API reports the same factory MAC, `34:94:54:73:E2:28`.
|
||
|
|
|
||
|
|
That means the reservation is mapped to the correct Shelly hardware identity.
|
||
|
|
|
||
|
|
Firewall DNS answers currently conflict:
|
||
|
|
|
||
|
|
- `Shelly-Server.maddo.science -> 192.168.100.75`
|
||
|
|
- `shellyem-34945473E228.maddo.science -> 192.168.100.127`
|
||
|
|
- reverse lookup of `192.168.100.127` returns `shellyem-34945473E228.maddo.science`
|
||
|
|
|
||
|
|
That means the firewall has both:
|
||
|
|
|
||
|
|
- a static name tied to the reserved IP
|
||
|
|
- a learned live record tied to the current IP
|
||
|
|
|
||
|
|
### 4. Local ARP evidence
|
||
|
|
|
||
|
|
Known Shelly devices on the same subnet resolve normally to their vendor MACs:
|
||
|
|
|
||
|
|
- `192.168.100.62 -> 24-4C-AB-41-87-35`
|
||
|
|
- `192.168.100.76 -> 24-4C-AB-41-6D-0C`
|
||
|
|
- `192.168.100.96 -> 34-94-54-78-06-1B`
|
||
|
|
|
||
|
|
But the live `Shelly-Server` IP resolves as:
|
||
|
|
|
||
|
|
- `192.168.100.127 -> 02-0F-B5-73-E2-28`
|
||
|
|
|
||
|
|
The first octet `02` marks this as a locally administered MAC, not the Shelly factory OUI. The last three bytes `73-E2-28` match the device suffix.
|
||
|
|
|
||
|
|
That is the key anomaly in this investigation.
|
||
|
|
|
||
|
|
## Interpretation
|
||
|
|
|
||
|
|
The observed facts fit this model best:
|
||
|
|
|
||
|
|
1. `Shelly-Server` is connected through something on `Shellynet` that rewrites or virtualizes the client MAC on the LAN side.
|
||
|
|
2. OPNsense may still be handing out the lease, but it is likely handing it to `02:0F:B5:73:E2:28`, not to `34:94:54:73:E2:28`.
|
||
|
|
3. Because the reservation is keyed to the factory MAC, the reservation at `192.168.100.75` is bypassed.
|
||
|
|
4. The device then receives a dynamic address inside the general pool, which explains `192.168.100.127`.
|
||
|
|
|
||
|
|
So the most likely correction is not "replace the old reservation MAC with the new device MAC." The factory MAC already matches. The only MAC mismatch is between the factory MAC and the translated on-wire MAC observed for the live IP.
|
||
|
|
|
||
|
|
This explanation is more consistent with the evidence than a rogue DHCP server because:
|
||
|
|
|
||
|
|
- the local host is using `192.168.100.254` as DHCP correctly
|
||
|
|
- the firewall DNS has a live learned name for `192.168.100.127`
|
||
|
|
- the on-wire MAC for `192.168.100.127` does not match the reservation MAC
|
||
|
|
|
||
|
|
## Alternative Explanation
|
||
|
|
|
||
|
|
A second DHCP server is still possible, but current evidence is weaker for that theory.
|
||
|
|
|
||
|
|
For a rogue DHCP explanation to fit all observations, one of these would also need to be true:
|
||
|
|
|
||
|
|
- the firewall is somehow learning dynamic DNS records for a lease it did not issue
|
||
|
|
- or the second DHCP domain is bridged into the LAN in a way that still produces the rewritten local MAC pattern
|
||
|
|
|
||
|
|
That is possible, but it is not the simplest explanation from the data collected so far.
|
||
|
|
|
||
|
|
## Likely Root Causes
|
||
|
|
|
||
|
|
### 1. Wireless bridge or router mode on the `Shellynet` path
|
||
|
|
|
||
|
|
Examples:
|
||
|
|
|
||
|
|
- Wi-Fi extender in router mode
|
||
|
|
- client bridge with MAC NAT
|
||
|
|
- WISP mode
|
||
|
|
- AP/client isolation feature that synthesizes per-client LAN MACs
|
||
|
|
- travel router or embedded bridge in front of the Shelly
|
||
|
|
|
||
|
|
### 2. Reservation keyed to the wrong effective MAC
|
||
|
|
|
||
|
|
If the DHCP server sees `02:0F:B5:73:E2:28` on the wire, a reservation for `34:94:54:73:E2:28` will never apply.
|
||
|
|
|
||
|
|
### 3. Stale naming and reservation state on the firewall
|
||
|
|
|
||
|
|
The static host entry for `Shelly-Server.maddo.science` still points to `.75`, while the firewall also knows a live dynamic name at `.127`. That split-brain state makes troubleshooting harder even if DHCP itself is still on the firewall.
|
||
|
|
|
||
|
|
## What I Could Not Prove In This Pass
|
||
|
|
|
||
|
|
- I did not directly read the active DHCP lease database from OPNsense because the available SSH wrapper restricted several generic shell commands.
|
||
|
|
- I did not capture a DHCP exchange packet trace from the firewall.
|
||
|
|
- I did not inspect the AP/controller behind `Shellynet` because that system is not exposed through the current toolset.
|
||
|
|
|
||
|
|
So the exact point where the MAC changes is still unverified.
|
||
|
|
|
||
|
|
## Recommended Next Checks
|
||
|
|
|
||
|
|
### Highest value checks
|
||
|
|
|
||
|
|
1. Inspect the device or controller that provides `Shellynet`.
|
||
|
|
- Determine whether it is a pure bridge, router, repeater, WISP client, or extender.
|
||
|
|
- Look for features such as MAC NAT, proxy ARP, client isolation, wireless bridge translation, or MAC cloning.
|
||
|
|
|
||
|
|
2. Check the live DHCP lease table on OPNsense for `192.168.100.127`.
|
||
|
|
- Confirm which MAC the firewall associates with that lease.
|
||
|
|
- Check whether the hostname is `shellyem-34945473E228`.
|
||
|
|
|
||
|
|
3. Run a packet capture during a DHCP renew from the Shelly.
|
||
|
|
- Capture on the LAN side of the firewall for UDP `67/68`.
|
||
|
|
- Confirm whether the DHCP client identifier or source MAC is `34:94:54:73:E2:28` or `02:0F:B5:73:E2:28`.
|
||
|
|
|
||
|
|
### Good follow-up checks
|
||
|
|
|
||
|
|
4. Search the AP client list for both MACs:
|
||
|
|
- `34:94:54:73:E2:28`
|
||
|
|
- `02:0F:B5:73:E2:28`
|
||
|
|
|
||
|
|
5. Check whether any other Shelly devices on `Shellynet` show the same MAC translation pattern.
|
||
|
|
|
||
|
|
6. Verify whether `Shellynet` is bridged directly onto `192.168.100.0/24` or routed/NATed by an intermediate device.
|
||
|
|
|
||
|
|
## Proposed Fixes
|
||
|
|
|
||
|
|
## Plain-English Explanation
|
||
|
|
|
||
|
|
The simplest reading of the evidence is this:
|
||
|
|
|
||
|
|
- the Shelly itself is fine
|
||
|
|
- OPNsense is probably still the DHCP server
|
||
|
|
- the `Shellynet` extender is likely changing how the Shelly appears on the network
|
||
|
|
- because of that, OPNsense does not match the Shelly to its reservation at `192.168.100.75`
|
||
|
|
- the Shelly then gets a normal dynamic IP instead, currently `192.168.100.127`
|
||
|
|
|
||
|
|
So the real problem is probably not the Shelly and not Home Assistant.
|
||
|
|
|
||
|
|
The likely problem is the Wi-Fi extender path.
|
||
|
|
|
||
|
|
## Recommended Solution
|
||
|
|
|
||
|
|
The best solution is to stop using the extender path for this Shelly if possible.
|
||
|
|
|
||
|
|
Preferred order:
|
||
|
|
|
||
|
|
1. Move `Shelly-Server` onto a normal Wi-Fi network that is bridged directly to the main LAN.
|
||
|
|
2. If that is not possible, reconfigure the extender so it behaves as a simple bridge or access point and does not rewrite client identity.
|
||
|
|
3. Only if neither is possible, use a DHCP reservation for the effective MAC currently seen on the LAN as a workaround.
|
||
|
|
|
||
|
|
## Best Fix
|
||
|
|
|
||
|
|
Move the Shelly off `Shellynet` and onto a normal SSID served directly by the main Wi-Fi infrastructure.
|
||
|
|
|
||
|
|
Why this is the best fix:
|
||
|
|
|
||
|
|
- it should let OPNsense see the Shelly's real MAC again
|
||
|
|
- it should make the existing reservation at `192.168.100.75` work normally
|
||
|
|
- it removes the weird translation behavior from the network path
|
||
|
|
- it is simpler and more robust than working around the extender behavior
|
||
|
|
|
||
|
|
After moving the Shelly to a normal SSID:
|
||
|
|
|
||
|
|
1. reboot the Shelly or renew its network connection
|
||
|
|
2. verify that it takes `192.168.100.75`
|
||
|
|
3. verify that `Shelly-Server.maddo.science` resolves correctly
|
||
|
|
4. verify that Home Assistant still sees live MQTT updates
|
||
|
|
|
||
|
|
## Good Workaround
|
||
|
|
|
||
|
|
If the extender must stay in place, use the MAC that the LAN is actually seeing for the live IP.
|
||
|
|
|
||
|
|
Current observed effective MAC:
|
||
|
|
|
||
|
|
- `02:0F:B5:73:E2:28`
|
||
|
|
|
||
|
|
That workaround would mean:
|
||
|
|
|
||
|
|
1. update the OPNsense reservation to use `02:0F:B5:73:E2:28` instead of `34:94:54:73:E2:28`
|
||
|
|
2. keep the reserved IP as `192.168.100.75`
|
||
|
|
3. reconnect or reboot the Shelly
|
||
|
|
4. verify whether it then gets `192.168.100.75`
|
||
|
|
|
||
|
|
Current state after applying the workaround:
|
||
|
|
|
||
|
|
- reservation IP: `192.168.100.75`
|
||
|
|
- reserved effective MAC: `02:0F:B5:73:E2:28`
|
||
|
|
- hostname: `Shelly-Server`
|
||
|
|
- Kea description: `Shelly-Server via Shellynet extender workaround; physical MAC 34:94:54:73:e2:28`
|
||
|
|
- firewall config backup created before change: `/conf/config.xml.pre-shellynet-workaround-20260417`
|
||
|
|
|
||
|
|
This workaround may succeed, but it has a risk:
|
||
|
|
|
||
|
|
- if the extender changes that translated MAC later, the reservation will break again
|
||
|
|
|
||
|
|
## What I Recommend You Actually Do
|
||
|
|
|
||
|
|
If you want the most reliable outcome with the least long-term confusion:
|
||
|
|
|
||
|
|
1. first try to get `Shelly-Server` off `Shellynet`
|
||
|
|
2. if that is not realistic, reserve `192.168.100.75` for `02:0F:B5:73:E2:28` as a workaround
|
||
|
|
3. once it works, keep both MACs documented:
|
||
|
|
- physical Shelly MAC: `34:94:54:73:E2:28`
|
||
|
|
- effective LAN-side MAC through extender: `02:0F:B5:73:E2:28`
|
||
|
|
|
||
|
|
## What Not To Do
|
||
|
|
|
||
|
|
- do not assume the reservation is stale because of a replaced Shelly; the current Shelly reports the same physical MAC stored in OPNsense
|
||
|
|
- do not delete historical information about the physical MAC; it is still the real hardware identity of the device
|
||
|
|
- do not assume Home Assistant is the problem; it is working correctly because MQTT is working
|
||
|
|
|
||
|
|
### Preferred fix
|
||
|
|
|
||
|
|
Remove the MAC-rewriting hop from the Shelly path.
|
||
|
|
|
||
|
|
That means:
|
||
|
|
|
||
|
|
- put the Shelly on a normal bridged SSID
|
||
|
|
- or reconfigure the `Shellynet` infrastructure so the original client MAC reaches the firewall unchanged
|
||
|
|
|
||
|
|
If the original MAC reaches OPNsense, the existing reservation at `192.168.100.75` should be able to work as intended.
|
||
|
|
|
||
|
|
### Acceptable workaround
|
||
|
|
|
||
|
|
If the `Shellynet` path must stay as-is, create the reservation for the effective on-wire MAC actually seen by DHCP, after confirming it is stable.
|
||
|
|
|
||
|
|
Based on current evidence, that candidate MAC is:
|
||
|
|
|
||
|
|
- `02:0F:B5:73:E2:28`
|
||
|
|
|
||
|
|
This is only a workaround. If that translated MAC is generated dynamically by the bridge and can change, it is not a robust long-term fix.
|
||
|
|
|
||
|
|
It should not be documented as an "old Shelly MAC." It is better treated as an observed effective LAN-side MAC associated with the current path to `Shelly-Server`.
|
||
|
|
|
||
|
|
### Cleanup after the root issue is fixed
|
||
|
|
|
||
|
|
1. Align firewall DNS so there is only one canonical name/IP for this device.
|
||
|
|
2. Remove or update stale static entries that still point `Shelly-Server` at `.75` if the intended address changes.
|
||
|
|
3. Re-test Home Assistant entity availability and direct web access after the network path is corrected.
|
||
|
|
|
||
|
|
## Working Conclusion
|
||
|
|
|
||
|
|
The current evidence supports this working conclusion:
|
||
|
|
|
||
|
|
OPNsense at `192.168.100.254` is still likely the real DHCP authority, but `Shelly-Server` is probably not presenting its factory MAC to that DHCP server. Instead, something on the `Shellynet` path appears to be rewriting the MAC to `02:0F:B5:73:E2:28`, causing the firewall to hand out a dynamic lease at `192.168.100.127` and bypass the reservation for `192.168.100.75`.
|
||
|
|
|
||
|
|
That makes this look less like a rogue DHCP server problem and more like a MAC translation or bridging mode problem that breaks DHCP reservations.
|