Redefining Standard Options
  • 12 Feb 2024
  • 3 Minutes to read
  • Contributors
  • Dark
    Light
  • PDF

Redefining Standard Options

  • Dark
    Light
  • PDF

Article Summary

DHCPv4 has been around a long time. There have been many manufacturers creating many devices and using custom DHCP options, at times, to allow passing of operational details to the device from the DHCP server. This is often a matter of convenience for administrators so that it isn't necessary to manually configure what can sometimes be a large number of the same device in service on the local network.

Since DHCPv4 has been around for so long, some of the option numbers that were unused in the past, have later been defined by an RFC for some purpose. One such example was discovered recently where a, now defunct, manufacturer of IP phones had used DHCPv4 option 156 (string) to pass settings for their phones. Option 156 (integer) had since been used in RFC 6926. Kea defines Option 156 for use by the Lease Query hook. Typically speaking, in Kea it is not possible to redefine an option that is defined by Kea itself. We were able to, not exactly redefine the option, but set the option to a certain value prior to sending. This value (string), of course, did not match the type of data (integer) that the option SHOULD contain as defined in the RFC and in Kea.

Defining option 156 if it were possible

Normally, the option would be defined in this way, if it didn't already exist.

"Dhcp4": {
    "option-def": [
        {
            "name": "foo",
            "code": 156,
            "type": "string",
        }
    ]
}

but attempting to redefine this option causes Kea to emit the error: DHCP4_PARSER_FAIL failed to create or run parser for configuration element option-def: unable to override definition of option '156' in standard option space 'dhcp4'

This particular instance was complicated by the need to restrict the option to only certain clients as defined by certain option 60 content, and only provide this data in a certain subnet. These requirements were all possible to accomodate using client classing restricted by "only-if-required" and the "flex_option" hook. First, the class had to be defined using "only-if-required" to prevent global evaluation.

{
  "Dhcp4": {
    "client-classes": [
      {
        "name": "ACMEphone",
        "test": "option[60].hex == 'ACME IP Phone'",
        "only-if-required": true
      }
    ]
  }
}

The next step is to only evaluate the client class in the desired subnet using "require-client-classes".

{
  "Dhcp4": {
    "subnet4": [
      {
        "id": 1,
        "subnet": "192.0.2.0/24",
        "require-client-classes": [
          "ACMEphone"
        ],
        "pools": [
          {
            "pool": "192.0.2.2 - 192.0.2.254"
          }
        ]
      } 
    ]
  }
}

The final step is to actually set the option's value to the desired string data but only if the client is a member of the class. Said membership was not evaluated unless Kea selected subnet 192.0.2.0/24 for the client. This allows the response packet to only contain the option 156 data if all of these conditions are met by restricting the "flex_option" action to only the desired class.

{
  "Dhcp4": {
    "hooks-libraries": [
      {
        "library": "/usr/local/kea/2.4.0/lib/kea/hooks/libdhcp_flex_option.so",
        "parameters": {
          "options": [
            {
              "code": 156,
              "add": "'param1=something,param2=42,param3=somethingelse'",
              "client-class": "ACMEphone"
            }
          ]
        }
      }
    ]
  }
}

In Summary: what happens here is DHCP clients that have Option 60 (Vendor Class Identifier) set to "ACME IP Phone" will be added to the class called "ACMEphone" but only if Kea selects the subnet 192.0.2.0/24. Both of these things, being true, will cause option 156 to be appended to the response packet with the string value param1=something,param2=42,param3=somethingelse. This is how it is possible to send arbitrary data in a standard option that cannot be redefined.

For reference, the complete configuration is shown below.

{
  "Dhcp4": {
    "client-classes": [
      {
        "name": "ACMEphone",
        "test": "option[60].hex == 'ACME IP Phone'",
        "only-if-required": true
      }
    ],
    "subnet4": [
      {
        "id": 1,
        "subnet": "192.0.2.0/24",
        "require-client-classes": [
          "ACMEphone"
        ],
        "pools": [
          {
            "pool": "192.0.2.2 - 192.0.2.254"
          }
        ]
      } 
    ],
    "hooks-libraries": [
      {
        "library": "/usr/local/kea/2.4.0/lib/kea/hooks/libdhcp_flex_option.so",
        "parameters": {
          "options": [
            {
              "code": 156,
              "add": "'param1=something,param2=42,param3=somethingelse'",
              "client-class": "ACMEphone"
            }
          ]
        }
      }
    ]
  }
}