ISC DHCP and option 82

The Relay Agent Information Option aka Option82

On DSL access networks that use DHCP to assign an IP address to the enduser, it is usual that some network element along the way acting as a DHCP relay stuffs a so-called 'relay agent information option' aka "option 82" into the DHCPDISCOVER packets. This option has several suboptions: "Agent Circuit ID" and "Agent Remote ID". One of those two usually uniquely identifies the 'circuit' the customer is connected to, such as a DSLAM port.

If you want to assign static IP addresses to your customers, it's very convenient to use this option as a unique identifier for that.

option82 with standard ISC DHCPD

With ISC DHCPD, you can assign static IP addresses to certain clients using the 'fixed-address' statement. The configuration for that looks something like this:
	host client-name-1 {
		hardware ethernet 00:e0:4c:a7:ca:de;
		fixed-address 192.168.0.6;
	}
Starting from ISC DHCPD version 4.2, you can match on agent.circuit-id as well, by using this syntax:
	host client-name-1 {
		host-identifier option agent.circuit-id "dslam42.port22";
		hardware ethernet 00:e0:4c:a7:ca:de;
		fixed-address 192.168.0.6;
	}
Unfortunately, at this time, the "host-identifier option ..." statement only works with agent.circuit-id. If you want to match on more than just agent.circuit-id. for example if agent.circuit-id is not unique and you need to match on another option as well, you are out of luck.

However, if your agent.circuit-id is unique, and you are using ISC DHCPD version 4.2 or later, you can use this method, stop reading here, and leave this page :)


 
 
 
 

Still here? Okay, you're looking for the workaround. There is one, by not using the host statement at all. That also works for ISC DHCPD versions before 4.2. It looks like this:

	stash-agent-options true;

	shared-network "networkname" {
		subnet 192.168.0.0 netmask 255.255.255.0 {

			option broadcast-address 192.168.0.255;
			option routers 192.168.0.1;
			option subnet-mask 255.255.255.0;

			# A customer.
			class "id-192.168.0.2" {
				match if option agent.circuit-id = "dslam42.port22";
			}
			pool {
				allow members of "id-192.168.0.2";
				range 192.168.0.2;
			}

			# And yet another one.
			class "id-192.168.0.5" {
				match if option agent.circuit-id = "dslam42.port29";
			}
			pool {
				allow members of "id-192.168.0.5";
				range 192.168.0.5;
			}
		}
	}
With recent hardware, this scales up to a few thousand customers. The internal implementation in ISC DHCPD of classes is such that it scales in a non-linar way - O(N^2) or something. So suddenly you'll end up with dhcpd eating 100% CPU. This is ofcourse bad.

option82 with patched ISC DHCPD

ISC DHCPD supports something called 'subclassing'. You can auto-create ("spawn") subclassed based on the agent.circuit-id or agent.remote-id. The subclasses are not stored in a linked list but in a hashtable, so lookups are fast, efficient and scalable.

The only problem is .. you cannot assign a fixed-address based on a subclass, and there is no way to say "allow members of subclass bla" in for example a pool-statement.

I chose to implement a new syntax for the "allow members of" statement. The standard syntax is:

	allow members of <class>;
This is now extended to also allow this:
	allow members of <class> <subclass>;
So, to assign a static address to customers based on the circuit-id, you would configure the server like this:
	stash-agent-options true;

	class "static-1" {
		spawn with option agent.circuit-id;
	}

	shared-network "networkname" {
		subnet 192.168.0.0 netmask 255.255.255.0 {

			option broadcast-address 192.168.0.255;
			option routers 192.168.0.1;
			option subnet-mask 255.255.255.0;

			# A customer.
			pool {
				allow members of "static-1" "dslam42.port22";
				range 192.168.0.2;
			}

			# And yet another one.
			pool {
				allow members of "static-1" "dslam42.port29";
				range 192.168.0.5;
			}
		}
	}
This has been tested on a Xeon 2.8 Ghz server, it uses just a few percent of CPU with 40.000 DHCP clients.

Ofcourse you can, as usual, define multiple classes if you like- for example, one per DSLAM. Say that your DHCP relay agent stuffs the DSLAM name into agent.remote-id and the port number in agent.circuit-id. It is not sufficient to have one class and just spawn subclasses based on the circuit-id, you will have clashes as multiple DSLAMs use the same circuit-ids.

In that case, do something like this:

	class "dslam1" {
		match if (substring(option agent.remote-id,0,6) = "dslam1");
		spawn with option agent.circuit-id;
	}
	class "dslam2" {
		match if (substring(option agent.remote-id,0,6) = "dslam2");
		spawn with option agent.circuit-id;
	}
	[....]
		pool {
			allow members of "dslam1" "port29";
			range 192.168.10.20;
		}
[....]
Another solution would be something like (syntax not tested) :
	class "static-1" {
		spawn with concat(option agent.remote-id,0,6), "_", agent.circuit-id);
	}
	[....]
		pool {
			allow members of "static-1" "dslam2_port29";
		}
	}

Notes

There is still a lease database, and the actual lease is still based on a customers MAC address. So if a customer has a lease, that lease is bound to the MAC address of his modem, router, gateway or whatever device actually asked for the lease.

This means that if the customers changes this device (places a new modem or router) the new device will not be able to get a lease, since it will get assigned the same IP address and that has already been leased.

So make sure that a) you keep the lease time short enough (I recommend 2 hours or 7200 seconds), and b) your helpdesk knows about this since customers will hit this issue. Put something like this in your config:

	default-lease-time 7200;
        max-lease-time 7200;

The patches

These patches were tested with ISC DHCP 3.0.x. They apply without rejects to version 4.0 and 4.1, and even compile -- but I have not tested it.

You apply them by going into the ISC DHCPD source directory, then run something like:

patch -p1 < ../01-subclass
patch -p1 < ../02-log-agent-options

01-subclass patch

This is the main patch for the allow members of <class> <subclass>;syntax.

01-subclass patch

02-log-agent-options patch

This patch makes the server log the agent options when it receives a DHCP DISCOVER packet. You need this patch if you want to know the agent options of a received DISCOVER packet that doesn't match anything in the config file and gets rejected.

02-log-agent-options patch

03-reply-to-client-ip patch

This is something of a local hack. Normally the ISC DHCP server sends back replies to the giaddrincluded in the DHCP packet. For certain network migration reasons we did not want this, so this patch makes the server send back replies to the actual source IP address of the DHCP request (i.e. the last relay).

You probably do not need this patch. In fact, I'm quite sure you don't. Do not use this.

03-reply-to-client-ip patch

Patches for 4.2.3

The patches do not apply cleanly against 4.2.x, so I have prepared the same patches for 4.2.3; completely untested by me, though I have a report from Golovin Pavel that they work. Anyway, if it breaks, you get to keep both halves...

01-subclass patch
02-log-agent-options patch

Support

No support. Sorry. If you port the patches to ISC DHCPD 6.0 I'd like to hear about it might and even put them on this page, but I'm not going to put this on sourceforge, run a mailinglist, and help you configure your DHCP server.

I think the best option for support is to go to the ISC DHCP users mailinglist at https://lists.isc.org/mailman/listinfo/dhcp-users and mention this webpage. Perhaps there are other people there using the same patches that can help you.


Miquel van Smoorenburg

miquels@cistron.nl