DHCP unused_ip issue (dnsmasq)

Problem:
Creating a new host, the “IPv4 Address” does not automatically populate, upon clicking “Suggest new” I receive;
Failed to fetch a free IP from proxy foreman-test.#### (https://foreman-test.####:8443): ERF12-8202 [ProxyAPI::ProxyException]: Unable to retrieve unused IP ([RestClient::BadRequest]: 400 Bad Request) for proxy https://foreman-test.####:8443/dhcp

Expected outcome:
An unused IP address is populated in “IPv4 Address”.

Foreman and Proxy versions:
Foreman 3.15
Proxy 3.15 (co-hosted)

Foreman and Proxy plugin versions:
smart_proxy_dhcp_dnsmasq 1.0

Distribution and version:
Ubuntu 22.04.5

Other relevant data:

2025-08-20T00:26:32  [D] Rack::Handler::WEBrick is invoked.
2025-08-20T00:26:32 4b5de642 [I] Started GET /dhcp/192.168.200.0/unused_ip from=192.168.200.100&to=192.168.200.200
2025-08-20T00:26:32 4b5de642 [D] verifying remote client 192.168.200.4 against trusted_hosts ["foreman-test.####"]
2025-08-20T00:26:32 4b5de642 [E] nil can't be coerced into Integer
2025-08-20T00:26:32 4b5de642 [W] Error details for nil can't be coerced into Integer: <TypeError>: nil can't be coerced into Integer
/usr/share/foreman-proxy/modules/dhcp_common/free_ips.rb:25:in `+'
/usr/share/foreman-proxy/modules/dhcp_common/free_ips.rb:25:in `block in mark_ip_as_allocated'
/usr/share/foreman-proxy/modules/dhcp_common/free_ips.rb:23:in `synchronize'
/usr/share/foreman-proxy/modules/dhcp_common/free_ips.rb:23:in `mark_ip_as_allocated'
/usr/share/foreman-proxy/modules/dhcp_common/free_ips.rb:65:in `block (2 levels) in find_free_ip'
/usr/share/foreman-proxy/modules/dhcp_common/free_ips.rb:61:in `synchronize'
/usr/share/foreman-proxy/modules/dhcp_common/free_ips.rb:61:in `block in find_free_ip'
/usr/share/foreman-proxy/modules/dhcp_common/free_ips.rb:101:in `block in random_index'
/usr/share/foreman-proxy/modules/dhcp_common/free_ips.rb:97:in `loop'
/usr/share/foreman-proxy/modules/dhcp_common/free_ips.rb:97:in `random_index'
/usr/share/foreman-proxy/modules/dhcp_common/free_ips.rb:59:in `find_free_ip'
/usr/share/foreman-proxy/modules/dhcp_common/server.rb:110:in `unused_ip'
/usr/share/foreman-proxy/modules/dhcp/dhcp_api.rb:36:in `block in <class:DhcpApi>'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1644:in `call'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1644:in `block in compile!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:994:in `block (3 levels) in route!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1013:in `route_eval'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:994:in `block (2 levels) in route!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1042:in `block in process_route'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1040:in `catch'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1040:in `process_route'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:992:in `block in route!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:991:in `each'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:991:in `route!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1106:in `block in dispatch!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1080:in `block in invoke'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1080:in `catch'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1080:in `invoke'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1103:in `dispatch!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:926:in `block in call!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1080:in `block in invoke'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1080:in `catch'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1080:in `invoke'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:926:in `call!'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:915:in `call'
/usr/share/rubygems-integration/all/gems/rack-2.1.4/lib/rack/method_override.rb:24:in `call'
/usr/share/foreman-proxy/lib/proxy/log.rb:102:in `call'
/usr/share/foreman-proxy/lib/proxy/request_id_middleware.rb:11:in `call'
/usr/lib/ruby/vendor_ruby/rack/protection/xss_header.rb:18:in `call'
/usr/lib/ruby/vendor_ruby/rack/protection/path_traversal.rb:16:in `call'
/usr/lib/ruby/vendor_ruby/rack/protection/json_csrf.rb:26:in `call'
/usr/lib/ruby/vendor_ruby/rack/protection/base.rb:50:in `call'
/usr/lib/ruby/vendor_ruby/rack/protection/base.rb:50:in `call'
/usr/lib/ruby/vendor_ruby/rack/protection/frame_options.rb:31:in `call'
/usr/share/rubygems-integration/all/gems/rack-2.1.4/lib/rack/null_logger.rb:11:in `call'
/usr/share/rubygems-integration/all/gems/rack-2.1.4/lib/rack/head.rb:14:in `call'
/usr/lib/ruby/vendor_ruby/sinatra/show_exceptions.rb:22:in `call'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:194:in `call'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1959:in `call'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1511:in `block in call'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1738:in `synchronize'
/usr/lib/ruby/vendor_ruby/sinatra/base.rb:1511:in `call'
/usr/share/rubygems-integration/all/gems/rack-2.1.4/lib/rack/urlmap.rb:77:in `block in call'
/usr/share/rubygems-integration/all/gems/rack-2.1.4/lib/rack/urlmap.rb:61:in `each'
/usr/share/rubygems-integration/all/gems/rack-2.1.4/lib/rack/urlmap.rb:61:in `call'
/usr/share/rubygems-integration/all/gems/rack-2.1.4/lib/rack/builder.rb:177:in `call'
/usr/share/rubygems-integration/all/gems/rack-2.1.4/lib/rack/handler/webrick.rb:88:in `service'
/usr/share/rubygems-integration/all/gems/webrick-1.7.0/lib/webrick/httpserver.rb:140:in `service'
/usr/share/rubygems-integration/all/gems/webrick-1.7.0/lib/webrick/httpserver.rb:96:in `run'
/usr/share/rubygems-integration/all/gems/webrick-1.7.0/lib/webrick/server.rb:310:in `block in start_thread'
/usr/lib/ruby/vendor_ruby/logging/diagnostic_context.rb:474:in `block in create_with_logging_context'

Initially I’d thought that this was simply the unused_ip API not being supported in the dhcp_dnsmasq plugin but reading through the code it appears that it populates an internal list in the subnet service with the contents of the leases file and then the call to unused_ip just calls back directly to the base functionality so near as I can tell it SHOULD work.

After doing a bunch of tombstoning in the code I’ve been able to figure out that the problem was that I didn’t have a “blacklist_duration_minutes” configuration parameter explicitly configured.

The code seems to provide a default value for that argument for the initialize method of FreeIps but for some reason it was getting nulled somewhere along the line.

I’ll see if I can figure out where/why and will submit a pull request but for the moment if you happen to have this issue add the following parameter to /etc/foreman-proxy/settings.d/dhcp_dnsmasq.yml;
:blacklist_duration_minutes:

OK, fix was as simple as adding a default configuration parameter to dhcp_dnsmasq_plugin.rb for that value.

Will fork and issue a PR when I get a chance.

1 Like

It looks like the blacklist_duration_minutes setting is missing a default value, unlike other DHCP providers such as ISC and Infoblox, which already define one.

I opened a ticket for it and I’d appreciate a review once I submit the PR.

Ah, I can get to it this way but not from the “Visit Topic” button in the email.

Thanks for creating the ticket happy to do whatever you need to get it merged.

Oh, just realised you’ve created a new PR, I’d already submitted one, sorry probably should’ve dropped a link Unused_ip fix by darkglade · Pull Request #20 · theforeman/smart_proxy_dhcp_dnsmasq · GitHub

Apologies if it’s not up to the project standards, the link to the PR template in the mainline foreman docs was broken when I went looking for it.

1 Like

I’ve reviewed your PR.

Only thing that’s really different between that and mine is I explicitly set my value rather than using 30*60 and I bumped the version number to 1.1, not sure what project policy is on that though.

I’ve also closed my PR and noted that it’s been replaced by 21.

1 Like