Page MenuHomeVyOS Platform

[Feature] - DHCP Option 82 Support
Closed, ResolvedPublicFEATURE REQUEST

Description

For enterprise deployments where separation of different subnet pools is necessary, option 82 provides a mechanism to specify a source VLAN in DHCP requests. Currently, VyOS can in fact support this via the subnet-parameters and shared-network-parameters, but these options are less than ideal, and other parameters need to be set in the CLI even though they're not actually necessary for ISC to function in this mode. Currently, I utilize the following configurations for Option-82:
Arista EOS 4.26.1F:

ir01#show run | s relay|helper
ip dhcp relay information option
ip dhcp relay all-subnets default
interface Vlan200
   ip dhcp relay all-subnets
interface Vlan240
   ip dhcp relay all-subnets

VyOS 1.4:

set service dhcp-server failover name 'INT'
set service dhcp-server failover remote '192.168.15.4'
set service dhcp-server failover source-address '192.168.15.3'
set service dhcp-server failover status 'primary'
set service dhcp-server shared-network-name INT description 'Internal connection to ir01'
set service dhcp-server shared-network-name INT shared-network-parameters 'class "GUEST" { match if option agent.circuit-id = "Vlan240"; }'
set service dhcp-server shared-network-name INT shared-network-parameters 'class "CLIENTS" { match if option agent.circuit-id = "Vlan200"; }'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 default-router '192.168.1.1'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 domain-name 'int.trae32566.org'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 domain-search 'int.trae32566.org'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 domain-search 'ipa.trae32566.org'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 domain-search 'trae32566.org'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 name-server '192.168.255.1'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 name-server '192.168.15.10'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 name-server '192.168.31.3'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 ntp-server '192.168.255.2'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 ntp-server '192.168.15.11'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 ntp-server '192.168.31.4'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 server-identifier '192.168.15.2'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 static-mapping QUEST ip-address '192.168.1.17'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 static-mapping QUEST mac-address '80:f3:ef:11:e7:e7'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 subnet-parameters 'pool { allow members of "CLIENTS"; range 192.168.1.2 192.168.1.240; }'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 default-router '192.168.6.1'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 name-server '1.1.1.1'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 name-server '1.0.0.1'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 name-server '8.8.8.8'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 ntp-server '50.205.57.38'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 ntp-server '64.225.34.103'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 ntp-server '129.250.35.251'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 server-identifier '192.168.15.2'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 static-mapping DUMMY2 ip-address '192.168.6.254'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 static-mapping DUMMY2 mac-address '00:00:00:00:00:00'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 subnet-parameters 'pool { allow members of "GUEST"; range 192.168.6.2 192.168.6.254; }'
set service dhcp-server shared-network-name INT subnet 192.168.15.0/29 default-router '192.168.15.1'
set service dhcp-server shared-network-name INT subnet 192.168.15.0/29 enable-failover
set service dhcp-server shared-network-name INT subnet 192.168.15.0/29 range DUMMY start '192.168.15.2'
set service dhcp-server shared-network-name INT subnet 192.168.15.0/29 range DUMMY stop '192.168.15.7'

Because there is no option 82 support natively, the following things (taken from above) currently have to be "hacked" in:

  • Create the appropriate classes using shared-network-parameters:
set service dhcp-server shared-network-name INT shared-network-parameters 'class "GUEST" { match if option agent.circuit-id = "Vlan240"; }'
set service dhcp-server shared-network-name INT shared-network-parameters 'class "CLIENTS" { match if option agent.circuit-id = "Vlan200"; }'
  • Define a subnet pool allow statement with a specific range using subnet-parameters
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 subnet-parameters 'pool { allow members of "CLIENTS"; range 192.168.1.2 192.168.1.240; }'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 subnet-parameters 'pool { allow members of "GUEST"; range 192.168.6.2 192.168.6.254; }'
  • Add static mappings; at least 1 of these are needed because the range must be defined above as a subnet parameter for class mapping, as opposed to being defined in the VyOS configuration dedicated to that parameter (range):
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 static-mapping QUEST ip-address '192.168.1.17'
set service dhcp-server shared-network-name INT subnet 192.168.1.0/24 static-mapping QUEST mac-address '80:f3:ef:11:e7:e7'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 static-mapping DUMMY2 ip-address '192.168.6.254'
set service dhcp-server shared-network-name INT subnet 192.168.6.0/24 static-mapping DUMMY2 mac-address '00:00:00:00:00:00'

As well, this breaks DHCP failover, since it requires a subnet range be defined using the dedicated VyOS range configuration option.

Details

Version
-
Is it a breaking change?
Unspecified (possibly destroys the router)

Event Timeline

Any idea how you would like the CLI to look like for option 82 support?

I think this is what it would look like in service dhcp server. I left some comments to explain my thinking a bit, and I tried to make it as flexible as possible (for example the way match options are strings, so future DHCP options can be supported as soon as ISC supports them):

failover {
    name INT
    remote 192.168.15.4
    source-address 192.168.15.3
    status primary
}
shared-network-name INT {
    description "Internal connection to ir01"
    class CLIENT_MAP {
       rule 10 {
           action permit                                       # This is equivalent to dhcpd's allow/deny members of
           match option "agent.circuit_id" value "Vlan200"     # This could match any option (ex: dhcp-client-identifier)
       }
    }
    class GUEST_MAP {
       rule 10 {
           action permit
           match option "agent.circuit_id" value "Vlan240"
       }
    }
    subnet 192.168.1.0/24 {
        class CLIENT_MAP
        default-router 192.168.1.1
        domain-name int.trae32566.org
        domain-search int.trae32566.org
        domain-search ipa.trae32566.org
        domain-search trae32566.org
        enable-failover
        name-server 192.168.255.1
        name-server 192.168.15.10
        name-server 192.168.31.3
        ntp-server 192.168.255.2
        ntp-server 192.168.15.11
        ntp-server 192.168.31.4
        range CLIENTS {
            start 192.168.1.2
            stop 192.168.1.240
        }
        server-identifier 192.168.15.2
        static-mapping QUEST {
            ip-address 192.168.1.17
            mac-address 80:f3:ef:11:e7:e7
        }
    }
    subnet 192.168.6.0/24 {
        class GUEST_MAP
        default-router 192.168.6.1
        enable-failover
        name-server 1.1.1.1
        name-server 1.0.0.1
        name-server 8.8.8.8
        ntp-server 50.205.57.38
        ntp-server 64.225.34.103
        ntp-server 129.250.35.251
        server-identifier 192.168.15.2
        range GUESTS {
            start 192.168.6.2
            stop 192.168.6.254
        }
    }
    subnet 192.168.15.0/29 {        # This tells it indirectly to use the interface eth2, which is on this subnet (is there a better way?)
        default-router 192.168.15.1
        enable-failover
        range DUMMY {
            start 192.168.15.2
            stop 192.168.15.7
        }
    }
}

I'm also not sure how this will play into DHCP failover, but I would hope it works properly. I'm also hoping we can think of a better way to force it to use the correct interface than just specifying a dummy subnet; currently this is needed because when using option 82, the network being served is not directly connected (and therefore it has no idea what interface to go out).

Viacheslav subscribed.

Should get a new example for KEA-DHCP-server

Viacheslav triaged this task as Wishlist priority.Jan 20 2024, 2:23 AM

Happened across this one while trying to find support for defining client classes in Kea in order to pass different PXE boot files depending on client architecture. The following has an example of how this is achieved in kea.

https://lists.isc.org/pipermail/kea-users/2023-February/003870.html

Might need be a separate feature request, but would be nice to be able to pass DHCP options based upon tests performed using client classes.

There are 2 usecases of option82 when it comes to DHCP:

  1. Having the DHCP-server acknowledge the option82 and give out leases based on option82 information (instead of DUID/mac-addresses). Common usecase at ISP and enterpriselevel is to give out the same IP-address based on where the client is physically connected. That is whatever host is connected to switch4 int25 will always receive the same IP-address (and mask, gateway, dns and if needed ntp). This also gives that a leasefile no longer needs to be synchronized between routers in a redundant setup.
  1. Having the DHCP-relay (along with DHCP-snooping) acknowledge the option82 info that is it will intercept and append option82 info with lets say "SW4-INT25" before forwarding the request as a unicast towards the DHCP-server. Except for informing the DHCP-server of where this DHCP request originates it will also have the sideeffect of being effective against rogue DHCP-servers since all DHCP requests are intercepted and forwarded to the selected DHCP-server which the netadmin have decided to use.

Ran across this when trying to get a DHCP relay when doing EVPN, also on an Arista platform

I don't think the original work around would work with Kea becuase how the Jinja is formatted, but expanding on this for Kea specifically, a couple of things that would need to be implemented would be client classes that match have some type of match

"client-classes": [
     {
       "name": "vlan1099",
       "test": "option[82].option[1].hex == 0x31303939"
     },
     {
       "name": "vlan1088",
       "test": "option[82].option[1].hex == 0x31303838"
     },
     {
       "name": "vlan1033",
       "test": "option[82].option[1].hex == 0x31303333"
     }
   ],

I'm imagining something like set service dhcp-server client-class WORD match FILTER

Then there would need to be a matching client class to the shared-network

"subnet4": [
      {
        "id": 1,
        "subnet": "10.99.99.0/24",
        "pools": [
          {
            "pool": "10.99.99.10 - 10.99.99.250"
          }
        ],
        "option-data": [
          {
            "name": "routers",
            "data": "10.99.99.1"
          }
        ],
        "client-class": "vlan1099"
      },

Something like set service dhcp-server shared-network-name WORD client-class NAME

Just gonna bump this again. Would be great to see support for this as it would be super helpful for complex architecture-based netboot configurations.

A post from the forum showing a perfect example of the use case: https://forum.vyos.io/t/how-to-setup-vyos-to-let-it-support-netboot-xyz/14391/5

I have implemented this and created the following pull requests:-

Code: https://github.com/vyos/vyos-1x/pull/4665
Documentation: https://github.com/vyos/vyos-documentation/pull/1666

Viacheslav changed the task status from Open to In progress.Aug 11 2025, 9:13 AM
Viacheslav assigned this task to chris.blackburn.

Just gonna bump this again. Would be great to see support for this as it would be super helpful for complex architecture-based netboot configurations.

A post from the forum showing a perfect example of the use case: https://forum.vyos.io/t/how-to-setup-vyos-to-let-it-support-netboot-xyz/14391/5

I have specifically not implemented this exact functionality as I feel there's a lot of complexity here in what we do/don't want to support. People might want quite complex logical statements. If there is a demand for custom logic then I suggest that this is a new request which I can address separately.