OpenBSD Journal

Introducing dhcpleased(8)

Contributed by Pamela Mosiejczuk on from the please dhcpleased me dept.

Now enabled by default on OpenBSD -current is dhcpleased(8), a dynamic host configuration protocol daemon written by florian@ (Florian Obser), who spoke with us about his work:

I suppose this is either the KAME project's fault, or if we don't want to go that far back, Theo's fault. At g2k16 he floated the idea of a network configuration daemon. It would collect "proposals" for IP addresses, default routes and DNS configuration from various sources (DHCP, IPv6 router advertisements, umb(4), etc.), make some policy decisions, configure the network, and set resolv.conf(5)

Curiously, I was not actually around when this happened. I think I was out for lunch, enjoying a pint of the black stuff.

But when I came back I immediately started to work on part of this project: IPv6 router advertisements. At the time, handling was split between the kernel and rtsold(8) in userland. And it was all pretty old and crufty code, not really suitable for what we wanted to do. slaacd(8) was born.

Since not too many people were using IPv6 at the time we could freely experiment. krw had ripped all the ospf specifics out of ospfd(8) and ended up with a bare bones three process privsep daemon that didn't actually do anything. But we could fill it with a thing that understood IPv6 stateless address autoconfiguration. And it was tightly pledged. The process handling untrusted data off the network can't talk to the outside world. It can't open network connections or write to disk.

Now this magical overarching network configuration daemon never went anywhere. I think it was just too ambitious as a single big project. But that's not how we run, anyway. We make small incremental progress, chopping off bits at the edges. It turned out we don't collect conflicting IP configurations that need to be negotiated. dhclient(8), slaacd(8) and umb(4) can just configure the network and route priorities take care of multiple default routes.

That left /etc/resolv.conf. This is a shared resource and we need to negotiate access to it. dhclient(8) owned it and nothing else could write to it. But we also get nameservers from slaacd(8) and umb(4).

unwind(8) and resolvd(8) help with this. unwind(8) is very clever and does a lot of work behind the scenes to make DNS just work. We are probably going to run it by default soon, too.
resolvd(8) is clever in a simple kind of way. It just negotiates access to resolv.conf. It receives nameservers from various sources and just puts them into /etc/resolv.conf. It also understands when unwind(8) is running and points DNS at it.

So with those in place we are done. There is also phessler's "join" feature for wireless networks. Now with all those pieces in place you can wander around with your laptop, switch networks at will and things just magically work. Took us only 5 years.

Hang on, what was the question?

Ah yes, dhcpleased…

So with all the things in place I got bored one evening…
Ever since about 2017 or 2018 I had been thinking: Wouldn't it be neat to transmogrify slaacd(8) into a DHCP speaker? Have a clean slate, use everything we learned and have a go at it. dhclient(8) is this crufty code from the 90s. If I understand the history correctly we got some privsep due to some heroic efforts by Henning in the early 2000s.

It used to be a single process. A few years later krw picked it up and shined the turd for years. I'm sure it has as high a gleam as possible by now, but it's still a turd.

The one thing that stands out to me is the packet parsing process: it's pledged "stdio inet dns route". That's just too powerful and shows that the privsep is weak compared to our current standards. slaacd(8)'s parser on the other hand is pledged "stdio". And so is dhcpleased's parsing process.

How does this behave differently from the user's perspective?

The first thing people noticed that it is much snappier. dhclient(8) is intentionally slow during boot. It stays in the foreground for some time until it acquires a lease or a timeout fires after 10 seconds, I think. This holds up the boot process. I'm not holding this against dhclient(8), as said, it does this intentionally, we just decided to not continue with that behaviour in dhcpleased(8). There is a bit of fallout to mop up.

When bringing down and up the network, moving between networks, etc., dhcpleased(8) also feels snappier. I don't know why that is. Maybe dhcpleased(8) is much more aggressive with timeouts when sending new DHCP requests. Or maybe there is a bug in dhclient(8) which holds it back.

Users will also find exciting new bugs, maybe things that have not been implemented yet. I have a todo list for less common cases. What I'm more worried about is weird CPEs or other embedded DHCP servers that will only give us a lease if we ask in just the right way. This stuff has been around since the early 90s, nearly 30 years now. Cruft accumulates. Only time will tell.

To begin using dhcpleased, upgrade to a current snapshot. You can edit hostname.if(5) to replace "dhcp" with "inet autoconf". Readers interested in learning how privilege-separated daemons are constructed may enjoy Florian's talk about slaacd(8) from BSDCan 2018.

We thank Florian for his time speaking with us and his ongoing hard work in the project.

(Comments are closed)


Comments
  1. By Mike. (maple) on

    I've loaded up current to take dhcpleased out for a spin.

    One thing I noticed is that the info file

    ( /var/db/dhcpleased/if )

    doesn't contain the router, lease start, renew and expire times.

    That omission will probably banish me to continue using dhclient instead of dhcpleased.

    Are there plans to add these other data to the info file?

    thx.

    Comments
    1. By Otto Moerbeek (ottom) otto@drijf.net on

      I don't think that will be added to the lease file. But you can use dhcpleasectl to get the info:
      $ dhcpleasectl show interface                                             
      ure0 [Bound]:
      	server: 192.168.178.3
      	    IP: 192.168.178.13/255.255.255.0
      	routes:	0.0.0.0/0.0.0.0 - 192.168.178.20
      	   DNS: 192.168.178.28 192.168.178.13
      	 lease: 7h 45m 31s 
      

      Comments
      1. By Mike. (maple) on

        > you can use dhcpleasectl

        That works for me.

        thx.

  2. By Aaron (ariekenb) aaron.riekenberg@gmail.com on

    This is really interesting to read about. Thanks and congratulations for all the hard work on dhcpleased and resolvd! Just curious are there thoughts to support dhcpv6 and prefix delegation one day in the base system? Now one has to use dhcpcd for this as I understand: https://github.com/openbsd/ports/blob/master/net/dhcpcd/pkg/README

  3. By John McCue (jmcunx) jmcq66@comcast.net on Nice Intro

    Nice intro and I am really hoping I can use it. But one question I could not figure out when searching.

    Is it still possible to override the name server with dhcpleased(8) ?

    For example, I have this in /etc/dhclient.conf and curious how this can be set with dhcpleased(8)

    send host-name "THE-NAME";
    supersede domain-name-servers 10.4.0.1;

    Thanks

    Comments
    1. By fish (fish) on

      Yes, you can disable resolvd(8) and then add "nameserver 10.4.0.1" to /etc/resolv.conf.

      Comments
      1. By John McCue (jmcunx) jmcq66@comcast.net on great

        Thanks, will give it a shot when released

Credits

Copyright © - Daniel Hartmeier. All rights reserved. Articles and comments are copyright their respective authors, submission implies license to publish on this web site. Contents of the archive prior to as well as images and HTML templates were copied from the fabulous original deadly.org with Jose's and Jim's kind permission. This journal runs as CGI with httpd(8) on OpenBSD, the source code is BSD licensed. undeadly \Un*dead"ly\, a. Not subject to death; immortal. [Obs.]