TechnitiumSoftware/DnsServer · error · ArgumentException

Ending address cannot be same as the broadcast address.

Error message

Ending address cannot be same as the broadcast address.

What it means

Thrown by Scope construction / ChangeNetwork when the ending address equals the broadcast address (network OR NOT mask). The broadcast address is reserved for the subnet and cannot be part of the offerable pool, so the library refuses it.

Source

Thrown at DnsServerCore/Dhcp/Scope.cs:1462

            uint endingAddressNumber = endingAddress.ConvertIpToNumber();

            if (startingAddressNumber >= endingAddressNumber)
                throw new ArgumentException("Ending address must be greater than starting address.");

            _startingAddress = startingAddress;
            _endingAddress = endingAddress;
            _subnetMask = subnetMask;

            //compute other parameters
            uint subnetMaskNumber = _subnetMask.ConvertIpToNumber();
            uint networkAddressNumber = startingAddressNumber & subnetMaskNumber;
            uint broadcastAddressNumber = networkAddressNumber | ~subnetMaskNumber;

            if (networkAddressNumber == startingAddressNumber)
                throw new ArgumentException("Starting address cannot be same as the network address.");

            if (broadcastAddressNumber == endingAddressNumber)
                throw new ArgumentException("Ending address cannot be same as the broadcast address.");

            _networkAddress = IPAddressExtensions.ConvertNumberToIp(networkAddressNumber);
            _broadcastAddress = IPAddressExtensions.ConvertNumberToIp(broadcastAddressNumber);

            _lastAddressOfferedLock.Wait();
            try
            {
                _lastAddressOffered = IPAddressExtensions.ConvertNumberToIp(startingAddressNumber - 1u);
            }
            finally
            {
                _lastAddressOfferedLock.Release();
            }
        }

        public bool TryAddReservedLease(Lease reservedLease)
        {
            if (_reservedLeases.TryAdd(reservedLease.ClientIdentifier, reservedLease))

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Decrement the ending address below the broadcast (broadcast - 1) before constructing the Scope.
  2. Recompute: if (end == (network | ~mask)), reduce end by one host.
  3. For tiny subnets (/31, /32) move to a larger block that has usable hosts between network and broadcast.
  4. Confirm the subnet mask is contiguous and correct so broadcast is computed as expected.

Example fix

// before
var scope = new Scope(name, true,
    IPAddress.Parse("192.168.1.1"),
    IPAddress.Parse("192.168.1.255"), // broadcast -> throws
    IPAddress.Parse("255.255.255.0"), log, dhcpServer);

// after
var scope = new Scope(name, true,
    IPAddress.Parse("192.168.1.1"),
    IPAddress.Parse("192.168.1.254"), // broadcast - 1
    IPAddress.Parse("255.255.255.0"), log, dhcpServer);
Defensive patterns

Strategy: validation

Validate before calling

static uint ToUint(IPAddress a){ var b=a.GetAddressBytes(); return (uint)((b[0]<<24)|(b[1]<<16)|(b[2]<<8)|b[3]); }
static IPAddress Decrement(IPAddress a){ var u=ToUint(a)-1u; return new IPAddress(new byte[]{(byte)(u>>24),(byte)(u>>16),(byte)(u>>8),(byte)u}); }

uint network = ToUint(start) & ToUint(mask);
uint broadcast = network | ~ToUint(mask);
if (broadcast == ToUint(end)) endingAddress = Decrement(end);

Type guard

static bool EndIsNotBroadcast(IPAddress end, IPAddress start, IPAddress mask){
    uint net = ToUint(start) & ToUint(mask);
    uint bcast = net | ~ToUint(mask);
    return bcast != ToUint(end);
}

Try / catch

try { scope.ChangeNetwork(start, end, mask); }
catch (ArgumentException ex) when (ex.Message.Contains("broadcast address"))
{ end = Decrement(end); scope.ChangeNetwork(start, end, mask); }

Prevention

When it happens

Trigger: Passing an endingAddress that is the subnet broadcast: e.g. end=192.168.1.255 with mask 255.255.255.0, or end=10.255.255.255 with mask 255.0.0.0. Also when generating end = broadcast without decrementing, or when a narrow mask pushes broadcast onto the supplied end.

Common situations: Config generated from CIDR that uses the full block including broadcast; UI defaulting the end field to x.x.x.255; tooling that sets end = broadcastAddress verbatim.

Related errors


AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13). Data as JSON: /api/errors/3dc8b2ec6f7f8d1e. Report an issue: GitHub.