Saturday, 14 September 2013

DHCP Option 82, Cisco switches and routers and the ISC DHCP server

A bit of "fun" with IPv6 multicast this week which I'll come onto in another post...

We're moving into a new building beginning on Monday and our new network makes heavy use of pooled DHCP (with dynamic allocations of private addresses), and an option of fixed private or public addresses for devices needing fixed registrations and/or access from outside the University network.  The dynamic pooled allocations wouldn't need to be registered, enabling odd machines to be brought onto the network more easily, saving the fixed registrations for things that really need them.

Note: I did a follow-up article about this, to cover HP switches as well.  The details of the Cisco data are below, though.

DHCP Option 82, Cisco switches and routers


When using dynamic DHCP to unknown clients, you generally need a way to track the switch ports that hosts attach to so they can be located in the event of misbehaviour.  I think the long term way to do this is something similar to what we've done with our wireless system: using MAC-based authentication to trigger RADIUS accounting messages (or periodic MAC address table scraping).  However, for the short term, I was wondering if I could get DHCP Option 82 to help me out.

DHCP Option 82 is intended for things such as metro Ethernet or cable modems, whereby a switch providing an edge port will insert it into a DHCPREQUEST packet to indicate the switch and port where it was received, thus allowing the location of the host to be logged by the DHCP server.  It all looks promising but I've never got it to work - however, a bit of work today sorted that out.

The problem I had previously was that when Option 82 information is inserted into the DHCP packet, the DHCP server never saw the DHCPREQUEST.  This turned out to be the Cisco router: the default situation for packets received with Option 82 present, but the GIADDR (Gateway IP Address) field blank (0.0.0.0), is that the request will be discarded.  The GIADDR field is set by the relay agent (typically the router) to its address on the interface where the request was received, allowing the server to know which network the client is connected to.

In our situation, the edge switch will set Option 82 and leave GIADDR blank.  The router will relay the packet and fill in GIADDR.  I would imagine this is how most people would use it and it's odd that Cisco's default configuration doesn't do this.

Anyway, to fix this problem, the router interface must be set to trust the Option 82 information, even when GIADDR is blank, you do:

interface Vlan3008
 ip dhcp relay information trusted
 ip helper-address 172.28.208.86
 ip helper-address 172.28.208.87
 ...

... or for all VLANs, you can used the global context configuration command:

ip dhcp relay information trust-all

... then you can configure your edge switch with DHCP Snooping (and ARP Inspection, if required).

Which brings me on to part two of this - how to parse the Cisco Option 82 information in the ISC DHCP server and log it.

Cisco formats for Option 82 data


The edge switch command ip dhcp snooping information option format remote-id hostname above sets Option 82 to log the switch hostname rather than MAC address as the remote-id - I think that's more useful.

Cisco switches default to a binary-encoded vlan-mod-port for the circuit-id, although this can be overridden on a per-interface basis with something like:

interface GigabitEthernet1/0/2
 ip dhcp snooping vlan 3008 information option format-type circuit-id string Gi1/0/2:3008

... the second format is much nicer as you can just print it, but you have to manually set it on a per-port and per-VLAN basis, which is very tedious.  I don't like tedious things, so parsing the vlan-mod-port format is desirable.  There is a document called DHCP Option 82 Configurable Circuit ID and Remote ID on Cisco's website, but that just explains the ASCII variants, so I thought I'd document the default (binary) types too.

Remote ID


There are two formats for this, depending on whether it's the default (switch MAC address) or specified hostname:

012-
06 (= Length)Switch MAC address
1LengthHostname

Circuit ID


This also has two formats, depending on whether the vlan-mod-port option is used, or something custom:

0123456-
04 (= Length)VLAN ID (big endian)Module (slot)Port ID
1LengthPort and VLAN circuit-id string

Parsing Option 82 from Cisco devices in the ISC DHCP server


The following code, stuck at the top level in the dhcpd.conf file will log messages such as dhcpd: agent information 172.30.140.4 to 24:de:c6:c6:51:92 on sw-ucs-rnb-s4 port 1/2 VLAN 3008, if the agent information is present and the remote ID is in ASCII hostname format:

if ((exists agent.remote-id) and
   (substring(option agent.remote-id, 0, 1) = 1)) {

  log(
    info,
    concat(
      "agent information ",
      binary-to-ascii(10, 8, ".", leased-address),
      " to ",
      binary-to-ascii(16, 8, ":", substring(hardware, 1, 6)),
      " on ",
      substring(option agent.remote-id, 2, extract-int(substring(option agent.remote-id, 1, 1), 8)),
      " port ",
      binary-to-ascii(10, 8, "", substring(option agent.circuit-id, 4, 1)),
      "/",
      binary-to-ascii(10, 8, "", substring(option agent.circuit-id, 5, 1)),
      " VLAN ",
      binary-to-ascii(10, 16, "", substring(option agent.circuit-id, 2, 2))));
}

The ISC server's expression syntax is fairly limited and makes coping with alternatives very difficult - for example, to handle the same thing as above but with the remote ID being a MAC address, it would require an appropriate complete copy of the statement above, in addition, if both are to be handled.