Skip to content

DHCP server fails on FreeBSD/OPNsense due to missing IPv4 interface index and limited broadcast send failure #2099

Description

@setsor

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:

IPAddress.Broadcast

which is:

255.255.255.255

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:

IPAddress.Broadcast

For a 10.0.1.0/24 scope this changes the destination from:

255.255.255.255

to:

10.0.1.255

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:

  1. provide a safe DHCP scope-selection fallback when .NET returns an IPv4 interface index of 0;
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions