Environment
- Technitium DNS Server: 15.4.0
- OPNsense: 26.7.x
- FreeBSD: 15.1
- .NET Runtime: 10.0.10
- DHCP interface: IPv4 LAN (
10.0.1.1/24 in the test environment)
I have been testing the Technitium DHCPv4 server on OPNsense/FreeBSD and identified two independent FreeBSD/.NET-specific issues which prevent the DHCP exchange from completing.
Both issues were reproduced independently from Technitium with minimal socket tests.
Issue 1: IPv4 interface index is returned as 0 for DHCP broadcast packets
Technitium uses Socket.ReceiveMessageFromAsync() and the resulting IPPacketInformation.Interface to identify the DHCP scope associated with an incoming packet.
For a DHCP broadcast received as:
0.0.0.0:68 -> 255.255.255.255:67
.NET 10 on FreeBSD reports:
Remote endpoint : 0.0.0.0:68
Destination : 255.255.255.255
Interface index : 0
while the actual receiving interface in the test was:
Interface index : 2
Interface : igc1
A native FreeBSD recvmsg() test using IP_RECVIF receives the same DHCP broadcast and correctly reports:
destination : 255.255.255.255
ifindex : 2
ifname : igc1
Because IPPacketInformation.Interface == 0, the current Technitium DHCP scope lookup does not match the enabled LAN scope, whose interface index is 2.
Tested workaround
For FreeBSD only, when:
ipPacketInformation.Interface == 0
the DHCP server performs a safe fallback scope lookup, returning a matching reserved scope where possible, or the single unambiguous enabled dynamic scope.
With this change, Technitium successfully identifies the LAN scope and generates a DHCPOFFER.
Issue 2: sending DHCP responses to 255.255.255.255 fails on FreeBSD
After solving the scope-selection issue, Technitium generated the DHCP offer correctly but failed when transmitting it:
DHCP Server offered IP address [10.0.1.104] ...
System.Net.Sockets.SocketException (51): Network is unreachable
The current send path uses:
which is:
I reproduced this independently with a minimal .NET 10 UDP socket test bound to the LAN interface address.
Results:
Destination Flags Result
---------------------------------------------------------
255.255.255.255 DontRoute error 51 - NetworkUnreachable
255.255.255.255 None error 65 - HostUnreachable
10.0.1.255 DontRoute SUCCESS
10.0.1.255 None SUCCESS
Thus, on this FreeBSD system, the socket cannot transmit the DHCP response using the IPv4 limited broadcast address, while the subnet-directed broadcast address works correctly.
Tested workaround
For FreeBSD only, the DHCP response is sent to the broadcast address of the uniquely matching DHCP scope:
broadcastScope.BroadcastAddress
instead of:
For a 10.0.1.0/24 scope this changes the destination from:
to:
Result after applying both changes
The complete DHCP exchange works correctly:
DHCPDISCOVER
|
v
DHCPOFFER
10.0.1.1:67 -> 10.0.1.255:68
|
v
DHCPREQUEST
|
v
DHCPACK
10.0.1.1:67 -> 10.0.1.255:68
Technitium logs confirm successful allocation:
DHCP Server offered IP address [...]
DHCP Server leased IP address [...]
and there are no further:
SocketException (51): Network is unreachable
errors.
The patched server has been tested successfully with multiple real DHCP clients and after an OPNsense reboot.
Proposed scope of the fix
The proposed changes are intentionally limited to FreeBSD:
- provide a safe DHCP scope-selection fallback when
.NET returns an IPv4 interface index of 0;
- use the matching DHCP scope's directed broadcast address for broadcast responses on FreeBSD.
No change is proposed to DHCP behavior on Windows, Linux, or macOS.
I have a working source patch against Technitium DNS Server 15.4.0 and can prepare a pull request against the current master branch if this approach is acceptable.
I can also provide the two minimal .NET/FreeBSD reproducer programs used to isolate both socket behaviors.
OPNsense-specific deployment notes
There are two additional OPNsense requirements which are separate from the Technitium bugs described above:
- when Technitium runs as an unprivileged service account, FreeBSD
mac_portacl can be used to allow that account to bind UDP port 67;
- when the built-in OPNsense DHCPv4 server is disabled, its automatically generated PF rules disappear, so equivalent DHCPv4 firewall rules must be configured for a local third-party DHCP daemon.
These deployment requirements are not part of the proposed Technitium source changes.
Environment
10.0.1.1/24in the test environment)I have been testing the Technitium DHCPv4 server on OPNsense/FreeBSD and identified two independent FreeBSD/.NET-specific issues which prevent the DHCP exchange from completing.
Both issues were reproduced independently from Technitium with minimal socket tests.
Issue 1: IPv4 interface index is returned as 0 for DHCP broadcast packets
Technitium uses
Socket.ReceiveMessageFromAsync()and the resultingIPPacketInformation.Interfaceto identify the DHCP scope associated with an incoming packet.For a DHCP broadcast received as:
.NET 10 on FreeBSD reports:
while the actual receiving interface in the test was:
A native FreeBSD
recvmsg()test usingIP_RECVIFreceives the same DHCP broadcast and correctly reports:Because
IPPacketInformation.Interface == 0, the current Technitium DHCP scope lookup does not match the enabled LAN scope, whose interface index is 2.Tested workaround
For FreeBSD only, when:
the DHCP server performs a safe fallback scope lookup, returning a matching reserved scope where possible, or the single unambiguous enabled dynamic scope.
With this change, Technitium successfully identifies the LAN scope and generates a DHCPOFFER.
Issue 2: sending DHCP responses to 255.255.255.255 fails on FreeBSD
After solving the scope-selection issue, Technitium generated the DHCP offer correctly but failed when transmitting it:
The current send path uses:
which is:
I reproduced this independently with a minimal .NET 10 UDP socket test bound to the LAN interface address.
Results:
Thus, on this FreeBSD system, the socket cannot transmit the DHCP response using the IPv4 limited broadcast address, while the subnet-directed broadcast address works correctly.
Tested workaround
For FreeBSD only, the DHCP response is sent to the broadcast address of the uniquely matching DHCP scope:
instead of:
For a
10.0.1.0/24scope this changes the destination from:to:
Result after applying both changes
The complete DHCP exchange works correctly:
Technitium logs confirm successful allocation:
and there are no further:
errors.
The patched server has been tested successfully with multiple real DHCP clients and after an OPNsense reboot.
Proposed scope of the fix
The proposed changes are intentionally limited to FreeBSD:
.NETreturns an IPv4 interface index of0;No change is proposed to DHCP behavior on Windows, Linux, or macOS.
I have a working source patch against Technitium DNS Server 15.4.0 and can prepare a pull request against the current
masterbranch if this approach is acceptable.I can also provide the two minimal .NET/FreeBSD reproducer programs used to isolate both socket behaviors.
OPNsense-specific deployment notes
There are two additional OPNsense requirements which are separate from the Technitium bugs described above:
mac_portaclcan be used to allow that account to bind UDP port 67;These deployment requirements are not part of the proposed Technitium source changes.