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 {
               hardware ethernet 00:e0:4c:a7:ca:de;
               fixed-address 192.168.0.6;
        }
Unfortunately, you can only assign a static address based on the mac-address of the client this way. You’d expect to be able to say something like:
        host client {
               option agent.circuit-id “dslam42.port22”;
               fixed-address 192.168.0.6;
        }
but that is not implemented. The workaround 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”;
               }
        }