dhcp-eval(5)
π¦ dhcp-eval β ISC DHCP conditional evaluation π¦
okay so this is dhcp-eval(5), filed under File Formats Manual, and honestly? it's giving "the DHCP glow-up nobody told you about." β¨
πΈ NAME πΈ
dhcp-eval β ISC DHCP conditional evaluation π
π DESCRIPTION π
so both the Internet Systems Consortium DHCP client and the ISC DHCP server have this whole ability to do conditional behaviour β like, they can change what they do depending on what's actually inside the packets they receive. π the syntax for telling them how to do that conditional thing? that's literally what this page is about. you're welcome bestie π«Ά
β¨ REFERENCE: CONDITIONAL BEHAVIOUR β¨
you've got a few ways to spell "if this, then that": the if statement, the else and elsif statements, or the switch and case statements. no cap.
and here's the slay part β a conditional statement can show up anywhere a regular statement (like, say, an option statement) can show up, and it can wrap one or more of those statements inside it. main character energy tbh π
π CONDITIONAL BEHAVIOUR: IF π
a typical if conditional on the server side looks like this:
if option dhcp-user-class = "accounting" {
max-lease-time 17600;
option domain-name "accounting.example.org";
option domain-name-servers ns1.accounting.example.org,
ns2.accounting.example.org;
} elsif option dhcp-user-class = "sales" {
max-lease-time 17600;
option domain-name "sales.example.org";
option domain-name-servers ns1.sales.example.org,
ns2.sales.example.org;
} elsif option dhcp-user-class = "engineering" {
max-lease-time 17600;
option domain-name "engineering.example.org";
option domain-name-servers ns1.engineering.example.org,
ns2.engineering.example.org;
} else {
max-lease-time 600;
option domain-name "misc.example.org";
option domain-name-servers ns1.misc.example.org,
ns2.misc.example.org;
}
and over on the client side, conditional evaluation can look like this:
# example.org filters DNS at its firewall, so we have to use their DNS
# servers when we connect to their network. If we are not at
# example.org, prefer our own DNS server.
if not option domain-name = "example.org" {
prepend domain-name-servers 127.0.0.1;
}
okay so how does this actually work? π the if statement and the elsif continuation statement both take boolean expressions as their arguments. translation: they take expressions that, when you evaluate them, hand you back a boolean result. true or false, no in-between, periodt.
if the expression evaluates to true, then the statements chillin' inside the braces after the if get executed, and every single elsif and else clause after that gets skipped entirely. bye π
otherwise? each following elsif clause's expression gets checked, one after another, until we hit an elsif whose test evaluates to true. if we find one, the statements in the braces after it run, and then any elsif and else clauses after that get skipped.
and if every if and elsif clause got checked and none of them evaluated true β then if there's an else clause, the statements inside its braces get evaluated. π
one last thing, and it's important bestie: boolean expressions that evaluate to null get treated as false in conditionals. write that down. π
π§ CONDITIONAL BEHAVIOUR: SWITCH π§
that whole example from before? yeah, you can rewrite it with a switch construct instead. look:
switch (option dhcp-user-class) {
case "accounting":
max-lease-time 17600;
option domain-name "accounting.example.org";
option domain-name-servers ns1.accounting.example.org,
ns2.accounting.example.org;
case "sales":
max-lease-time 17600;
option domain-name "sales.example.org";
option domain-name-servers ns1.sales.example.org,
ns2.sales.example.org;
break;
case "engineering":
max-lease-time 17600;
option domain-name "engineering.example.org";
option domain-name-servers ns1.engineering.example.org,
ns2.engineering.example.org;
break;
default:
max-lease-time 600;
option domain-name "misc.example.org";
option domain-name-servers ns1.misc.example.org,
ns2.misc.example.org;
break;
}
so the switch statement and the case statements β either one can be a data expression or a numeric expression. but here's the rule: inside a single switch statement, they all have to be the same type. no mixing, bestie, it's a vibe check and they all gotta pass. β
the server evaluates the expression from the switch statement, and then it evaluates the expressions from the case statements until it finds a match. π
if it finds a match, it starts executing statements from that case and keeps going until the next break statement. if it doesn't find a match, it starts from the default statement and again proceeds to the next break statement. and if there's no match and no default? it just does nothing. π
β BOOLEAN EXPRESSIONS β
here's the current list of boolean expressions that the DHCP distribution supports, bestie. this is the whole roster. π
data-expression-1 = data-expression-2β the=operator compares the values of two data expressions and returns true if they're the same, false if they're not. and if either the left-hand side or the right-hand side is null, the result is also null. πdata-expression-1 ~= data-expression-2,data-expression-1 ~~ data-expression-2β the~=and~~operators (heads up: not available on all systems π¨) do extendedregex(7)matching on the values of two data expressions. they return true if data-expression-1 matches against the regular expression evaluated by data-expression-2, or false if it doesn't match or hits some error. and if either the left-hand side or the right-hand side is null or an empty string, the result is also false. the~~operator differs from~=in that it's case-insensitive. πboolean-expression-1 and boolean-expression-2β theandoperator evaluates to true if the boolean expression on the left-hand side and the boolean expression on the right-hand side both evaluate to true. otherwise it evaluates to false. and if either the expression on the left-hand side or the expression on the right-hand side is null, the result is null. πboolean-expression-1 or boolean-expression-2β theoroperator evaluates to true if either the boolean expression on the left-hand side or the boolean expression on the right-hand side evaluates to true. otherwise it evaluates to false. and if either the expression on the left-hand side or the expression on the right-hand side is null, the result is null. β¨not boolean-expressionβ thenotoperator evaluates to true if boolean-expression evaluates to false, and returns false if boolean-expression evaluates to true. and if boolean-expression evaluates to null, the result is also null. πexists option-nameβ theexistsexpression returns true if the specified option exists in the incoming DHCP packet being processed. πknownβ theknownexpression returns true if the client whose request is currently being processed is known β that is, if there's a host declaration for it. π«Άstaticβ thestaticexpression returns true if the lease assigned to the client whose request is currently being processed is derived from a static address assignment. π
π DATA EXPRESSIONS π
ok so bestie, some of those boolean expressions up there? they're literally just out here waiting on the results of data expressions. so here's the full roster, no gatekeeping π
substring (data-expr, offset, length)β this one evaluatesdata-exprand hands you back the chunk that startsoffsetbytes in from the beginning and keeps going forlengthbytes. bothoffsetandlengthare numeric expressions, fyi. ifdata-expr,offset, orlengthevaluate to null, then boom β the result is null too. ifoffsetis greater than or equal to the length of the evaluated data, you get a zero-length data string back. and iflengthis bigger than whatever's left afteroffset, then it just returns everything fromoffsetto the end of the evaluated data. generous queen πsuffix (data-expr, length)β returns the lastlengthbytes of whateverdata-exprevaluates to.lengthis a numeric expression. ifdata-exprorlengthevaluate to null, result is null. and if suffix evaluates to a number bigger than the length of the evaluated data? then you just get the whole evaluated data back, no drama π«Άlcase (data-expr)β returnsdata-exprconverted to lower case. ifdata-expris null, result is null. simple, iconic β¨ucase (data-expr)β same vibe but upper case. ifdata-exprevaluates to null, result is null. periodt πoption option-nameβ returns the contents of the specified option in the packet the server is responding to.config-option option-nameβ returns the value for the specified option that the DHCP client or server has been configured to send.gethostname()β returns a data string whose contents are a character string: the result of callinggethostname()on the local system, with a size limit of 255 bytes (not counting the NULL terminator). why do you care? because you can use this to configuredhclientto send the local hostname without knowing what that hostname is at the timedhclient.confis written. lowkey genius π§hardwareβ returns a data string where the first element is the type of network interface indicated in the packet being considered, and the elements after that are the client's link-layer address. if there's no packet, or the RFC2131hlenfield is invalid, then the result is null. hardware types include ethernet (1), token-ring (6), and fddi (8). these types are specified by the IETF, and the deets on how the type numbers are defined live in RFC2131 (in the ISC DHCP distribution, that's chillin' in thedoc/subdirectory).packet (offset, length)β returns the specified portion of the packet being considered, or null in contexts where no packet is being considered.offsetandlengthget applied to the packet contents exactly like in thesubstringoperator.stringβ a string wrapped in quotes can be a data expression, and it returns the text between the quotes, encoded in ASCII. the backslash ('\') character is special, just like in C programming:'\t'means TAB,'\r'means carriage return,'\n'means newline, and'\b'means bell. any octal value can be specified with'\nnn', wherennnis any positive octal number less than0400. any hexadecimal value can be specified with'\xnn', wherennis any positive hexadecimal number less than or equal to0xff. cute escapes fr fr π₯- colon-separated hexadecimal list β a list of hexadecimal octet values, separated by colons, can be specified as a data expression. that's it, that's the whole thing π
concat (data-expr1, ..., data-exprN)β the expressions get evaluated, and each result gets concatenated in the sequence the subexpressions are listed. if any subexpression evaluates to null, the whole concatenation result is null. all-or-nothing bestie πreverse (numeric-expr1, data-expr2)β both expressions get evaluated, then the data expression's result is reversed in place, using hunks of the size given by the numeric expression. example time: if the numeric expression evaluates to four, and the data expression evaluates to twelve bytes of data, thenreverseevaluates to twelve bytes β made of the last four bytes of the input, then the middle four bytes, then the first four bytes. so like, a lil shuffle, but make it mathematical β¨leased-addressβ in any context where the client whose request is being processed has been assigned an IP address, this returns that IP address. in any context where that client has not been assigned an IP address, and this data expression shows up in executable statements run on that client's behalf, a log message saying"there is no lease associated with this client"gets syslogged to the debug level (this counts asdhcpd.confdebugging information). so heads up: check your logs, bestie πbinary-to-ascii (numeric-expr1, numeric-expr2, data-expr1, data-expr2)β converts the result of evaluatingdata-expr2into a text string with one number for each element of that result. each number gets separated from the others by the result of evaluatingdata-expr1.numeric-expr1gives the base (2 through 16) the numbers convert into.numeric-expr2gives the width in bits of each number, which can be8,16, or32.
and as a lil demo of the previous three types of expressions β to produce the name of a PTR record for the IP address being assigned to a client, you could write:
concat (binary-to-ascii (10, 8, ".",
reverse (1, leased-address)),
".in-addr.arpa.");
slay π
encode-int (numeric-expr, width)βnumeric-exprgets evaluated and encoded as a data string of the specified width, in network byte order (most significant byte first). if the numeric expression evaluates to the null value, the result is null too.pick-first-value (data-expr1 [ ... exprn ] )β takes any number of data expressions as arguments. each one gets evaluated, starting with the first, until it finds one that doesn't evaluate to null. that one is returned, and none of the ones after it get evaluated. if every single expression evaluates to null, then the null value is returned. first-come-first-served, no cap πhost-decl-nameβ returns the name of the host declaration that matched the client whose request is currently being processed, if any. if no host declaration matched, the result is the null value. mystery solved π΅οΈββοΈ
π’ NUMERIC EXPRESSIONS π’
so numeric expressions are expressions that evaluate to an integer. in general, don't assume the max size of such an integer is representable in fewer than 32 bits β but the precision of these integers may be more than 32 bits. okay? okay π
on top of the operators below, several standard math functions are available too. they are:
- add β
+ - subtract β
- - divide β
/ - multiply β
* - modulus β
% - bitwise and β
& - bitwise or β
| - bitwise xor β
^
extract-int (data-expr, width)β extracts an integer value in network byte order from the result of evaluating the specified data expression.widthis the width in bits of the integer to extract. currently the only supported widths are8,16, and32. if evaluating the data expression doesn't give enough bits to extract an integer of the specified size, the null value is returned. sorry bestie, no partial credit πlease-timeβ the duration of the current lease. that is, the difference between the current time and the time the lease expires.numberβ any number between zero and the maximum representable size can be specified as a numeric expression.client-stateβ the current state of the client instance being processed. this one's only useful in DHCP client configuration files. possible values are:- Booting β DHCP client is in the INIT state, and does not yet have an IP address. the next message transmitted will be a DHCPDISCOVER, which will be broadcast.
- Reboot β DHCP client is in the INIT-REBOOT state. it has an IP address, but is not yet using it. the next message transmitted will be a DHCPREQUEST, which will be broadcast. if no response is heard, the client will bind to its address and move to the BOUND state.
- Select β DHCP client is in the SELECTING state. it has received at least one DHCPOFFER message, but is waiting to see if it might receive other DHCPOFFER messages from other servers. no messages are sent in the SELECTING state.
- Request β DHCP client is in the REQUESTING state. it has received at least one DHCPOFFER message, and has chosen which one it will request. the next message to be sent will be a DHCPREQUEST message, which will be broadcast.
- Bound β DHCP client is in the BOUND state. it has an IP address. no messages are transmitted in this state.
- Renew β DHCP client is in the RENEWING state. it has an IP address, and is trying to contact the server to renew it. the next message to be sent will be a DHCPREQUEST message, which will be unicast directly to the server.
- Rebind β DHCP client is in the REBINDING state. it has an IP address, and is trying to contact any server to renew it. the next message to be sent will be a DHCPREQUEST, which will be broadcast.
the way this whole state machine just cycles... main character energy fr fr π¦
β¨ REFERENCE: ACTION EXPRESSIONS β¨
Okay bestie, this is where the config actually does stuff. Actions, not just vibes. π
π log (priority, data-expr)
The log statement is your lil messenger to the standard logging channels. It includes an optional priority β one of fatal, error, info, or debug β plus a data expression. Obsessed.
Logging statements take only a single data expression argument, so if you wanna output multiple data values, you will need to use the concat operator to concatenate them. No cap. π«Ά
β‘ execute (command-path [, data-expr1, ... data-exprN]);
The execute statement runs an external command. The first argument is a string literal containing the name or path of the command to run. The other arguments, if present, are either string literals or data-expressions which evaluate to text strings, to be passed as command-line arguments to the command. It's giving "run this for me right now." β¨
execute (command-path [, data-expr1, ... data-exprN]);
execute is synchronous; the program will block until the external command being run has finished. Please note that lengthy program execution (for example, in an "on commit" in dhcpd.conf) may result in bad performance and timeouts. Only external applications with very short execution times are suitable for use. Keep it snappy, bestie. π
Passing user-supplied data to an external application might be dangerous. Make sure the external application checks input buffers for validity. Non-printable ASCII characters will be converted intodhcpd.conflanguage octal escapes ("\nnn"), make sure your external command handles them as such. Do not skip this warning, fr fr.
It is possible to use the execute statement in any context, not only on events. If you put it in a regular scope in the configuration file you will execute that command every time a scope is evaluated. Like, every single time. Not just once. Be aware. π
π§ parse-vendor-option;
The parse-vendor-option statement attempts to parse a vendor option (code 43). It is only useful while processing a packet on the server and requires that the administrator has already used the vendor-option-space statement to select a valid vendor space. So no valid vendor space selected? It's not gonna work, periodt. π
This functionality may be used if the server needs to take different actions depending on the values the client placed in the vendor option and the sub-options are not at fixed locations. It is handled as an action to allow an administrator to examine the incoming options and choose the correct vendor space. Cute, right? π
π REFERENCE: DYNAMIC DNS UPDATES π
See the dhcpd.conf and dhclient.conf man pages for more information about DDNS. That's the whole vibe here. Go read those for the deets. β¨
π SEE ALSO π
π«Ά AUTHOR π«Ά
Information about Internet Systems Consortium can be found at https://www.isc.org. That's the link, bestie. Go say hi. π
And that's the whole page, fr fr. This is dhcp-eval(5) from the ISC source/version, date stamped in the footer like a lil receipt. Bye bestie, go config slay πβ¨
π¦ where this page comes from
- arch rolling: dhcp 4.4.3.P1-4 Β· custom:isc-dhcp
- debian bookworm: isc-dhcp-common 4.4.3-P1-2 Β· MPL-2.0
- debian trixie: isc-dhcp-common 4.4.3-P1-8 Β· MPL-2.0
- epel 10: dhcp-common 12:4.4.3-23.el10_1 Β· ISC AND MPL-2.0
- fedora 43: dhcp-common 12:4.4.3-22.fc43 Β· ISC AND MPL-2.0
- fedora 44: dhcp-common 12:4.4.3-26.fc44 Β· ISC AND MPL-2.0
- ubuntu noble: isc-dhcp-common 4.4.3-P1-4ubuntu2 Β· MPL-2.0
- ubuntu resolute: isc-dhcp-common 4.4.3-P1-4ubuntu2 Β· MPL-2.0
- Copyright (C) 2009-2022 Internet Systems Consortium, Inc. ("ISC")
- Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
- Copyright (c) 1996-2003 by Internet Software Consortium
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms