DHCPOPTINJ(8)

πŸ’– DHCPOPTINJ(8) β€” dhcpoptinj πŸ’–

System Manager's Manual Β· dhcpoptinj

okay bestie, sit down, hydrate, because this one is a whole moment. it's called dhcpoptinj and it's a DHCP option injector β€” a tool to manipulate DHCP packet options. yes literally. let's get into it ✨


🌸 NAME 🌸

dhcpoptinj β€” DHCP option injector, a tool to manipulate DHCP packet options πŸŽ€


πŸ¦‹ SYNOPSIS πŸ¦‹

dhcpoptinj [-c [conf_file]] [-df] [--forward-on-fail] [-i|-r]
           [-p [pid_file]] -q queue_num -o dhcp_option
           [(-o dhcp_option) ...]
dhcpoptinj -h|-v

πŸ’œ DESCRIPTION πŸ’œ

so. dhcpoptinj is a fairly simple tool for manipulating DHCP options in a BOOTP/DHCP packet. it's giving "small but does exactly what it says on the tin". it relies on netfilter queue and nftables/iptables rules, and you are the one who specifies in those rules which packets get sent to dhcpoptinj. you're the main character, you set the vibe check. πŸ’…

dhcpoptinj just sits there waiting for packets to arrive in a netfilter queue, like a bestie waiting by the phone. when one shows up, it makes sure the packet is actually a BOOTP/DHCP packet, and if it is, it proceeds to inject or replace options. then it recalculates the IPv4 header checksum, disables the UDP checksum (for a simpler implementation, no cap), and hands the packet back to netfilter. so polite of it. 🫢

based on the options --ignore-existing-opt and --remove-existing-opt, dhcpoptinj can be configured to replace matching options or leave them in place. and --forward-on-fail lets you accept the packet (leaving it unaltered) if the package mangling should fail for any reason. like a lil safety net. 🧚

✨ Invocation ✨

dhcpoptinj (incorrectly) requires flags to run. yes, "incorrectly", the manpage said it, not me. the netfilter queue number (--queue) is necessary, and you also need at least one DHCP option (--option). see β€œEXAMPLES” for the receipts.

note that dhcpoptinj must be run by a user with the CAP_NET_ADMIN capability. the recommended way is not to run dhcpoptinj as root. instead, you can for instance grant the CAP_NET_ADMIN capability to the binary (using setcap) and limit execution rights to only a specific user or group. so much more elegant, honestly. πŸ‘‘

πŸŽ€ Argument list processing πŸŽ€

dhcpoptinj is invoked only with options and no arguments:

  • -c, --conf-file [conf_file] β€” specify a different configuration file than /etc/dhcpoptinj.conf. if empty, no file will be loaded. pick your own adventure bestie πŸ’–
  • -d, --debug β€” make dhcpoptinj tell you as much as possible about what it does and tries to do. it's an over-sharer and we love that for it.
  • -f, --foreground β€” prevent dhcpoptinj from running in the background. it stays right here with you. πŸ₯Ί
  • --forward-on-fail β€” if the process of injecting options should fail, let the unaltered DHCP packet pass through. the default behaviour is to drop the packet if options could not be injected. dramatic but fair πŸ’€
  • -h, --help β€” print a help text. classic.
  • -i, --ignore-existing-opt β€” proceed if an injected option already exists in the original packet. unless --remove-existing-opt is provided, the default behaviour is to drop the packet. so -i is the "we move" option ✨
  • -o, --option dhcp_option β€” DHCP option to inject as a hex string, where the first byte indicates the option code. the option length field is automatically calculated and must be omitted. several options may be injected. okay so listen: the hex string may be delimited by non-hexadecimal characters for readability, and the following hex strings all produce the same result: '01 02 03', '01:02:03', '010203', '01 <-> 02 <-> 03'. and remember to quote these arguments if they contain spaces or other characters that have special meaning to your shell. protect your arguments, protect your peace. 🫢
  • -p, --pid-file [pid_file] β€” write PID to file (/var/run/dhcpoptinj.pid) or to a specified location. lil paper trail πŸ“
  • -q, --queue queue_num β€” the netfilter queue number to use. this one is required, no skipping 🌟
  • -r, --remove-existing-opt β€” remove existing DHCP options of the same kind as those to be injected. out with the old, in with the new πŸ’…
  • -v, --version β€” display version. gotta know what era of the tool you're living in.

🌈 DHCP options 🌈

all the DHCP options passed with the -o/--option flag will be added before the terminating option (end option, 255). the packet is padded if necessary and sent back to netfilter. and ngl, none of the added options are checked for whether they are valid, or whether the option codes are valid. it trusts you completely. terrifying. iconic.

options are not (automatically) padded individually, but they can be manually padded by adding options with code 0 (one pad byte per option). this special option is the only option that does not have any payload (the end option, 255, is inserted automatically and cannot be manually added). padding individual options should not be necessary, so like, don't stress about it πŸ“

the option hex string is written as a series of two-digit pairs, optionally delimited by one or more non-hexadecimal characters: '466A6173','46 6A 61 73', '46:6A:61:73' etc. there is a limit of maximum 256 bytes per option, excluding the option code (the first byte) and the automatically inserted length byte. at least one option must be provided. no options, no service, sorry bestie.

if the packet already contains a DHCP option that is to be injected (matched by code), the behaviour depends on the command line options --ignore-existing-opt and --remove-existing-opt:

  • (none) β€” the packet will be dropped. 😭
  • -i β€” the existing options are ignored and the injected options are added. we simply do not perceive them ✨
  • -r β€” any existing options are removed and the injected options are added. clean slate energy 🌸
note that injected options will not be injected in the same place as those that may have been removed if using -r. however, this should not matter. it's fine. it's giving "don't worry about it".

🧚 FILES 🧚

at startup dhcpoptinj reads /etc/dhcpoptinj.conf. if the file does not exist, no error will be produced β€” it's very chill about it. another file may be specified with -c/--conf-file.

the syntax of the file is a list of key–value pairs separated by newlines. keys must match long option names and if the option takes an argument it must follow the symbol =. whitespace is accepted anywhere on the line. values may optionally be enclosed in single or double quotes.

DHCP options must be listed one-by-one, as on the command line, and not as a list. the keywords conf-file, help and version are not accepted. anything after and including the character # is considered a comment and is ignored. periodt.

the following shows an example configuration file:

# Run in foreground:
foreground
# Enable debug output:
debug
# Override hostname to "fjasehost":
option = '0C 66 6A 61 73 65 68 6F 73 74'
# Send agent ID "Fjas":
option = "52:01:04:46:6A:61:73"
# Override address request to ask for 10.20.30.40:
option=320A141E28
# Use queue 12:
queue = 12

remove-existing-opt # Remove options before inserting

any option on the command line will override a setting in the configuration file. however, note that options with arguments, like --foreground, cannot be removed/negated if it has been set in the configuration file. yeah. that one's a one-way door πŸšͺ if any DHCP option is passed on the command line, all DHCP options listed in the configuration file is ignored. all of them. every single one.


⭐ EXIT STATUS ⭐

the dhcpoptinj utility exits 0 on success, and >0 if an error occurs. simple as. πŸ’–


✨ EXAMPLES ✨

okay bestie, storytime πŸ’– let's say you've got two interfaces bridged together, eth0 and eth1. And let's say you wanna intercept all the BOOTP requests coming from eth0 and inject the relay agent information option (that's 82/0x52, iykyk). And let's make up a payload that's literally just a string we can match inside a DHCP server like dnsmasq so we can send different routes to the client: an agent circuit ID sub-option with the value "Fjas". yes we're naming it that, no notes πŸŽ€

First, add a rule to the iptables mangle table:

sudo iptables -t mangle -A PREROUTING -m physdev --physdev-in eth0
-p udp --dport 67 -j NFQUEUE --queue-num 42

Then run dhcpoptinj β€” let's run it in the foreground with extra debug output, because we're nosy like that:

sudo dhcpoptinj -d -f -q 42 -o'52 01 04 46 6A 61 73'

Now send a DHCP packet to the eth0 interface and watch it get modified when it reaches the bridged interface, using a tool like Wireshark (https://www.wireshark.org/). It should have the injected option at the end of the option list. And here's the sneaky part πŸ‘€ if you capture the incoming DHCP packet with Wireshark, it will appear unmodified β€” even though it will in fact be mangled. gaslight, gatekeep, girlboss πŸ’…

Now, the format of the argument to the -o option: it should be a hexadecimal string starting with the DHCP option code, followed by the option payload. The option length β€” that's the byte that normally comes right after the option code β€” is automatically calculated, and you must not specify it. The hex string can be delimited by non-hexadecimal characters for readability, so go off with your little spaces. And every option must have a payload, except for the special pad option (https://tools.ietf.org/html/rfc2132#section-2), which is code 0.

The layout of the nonsensical option used in this example, 52 01 04 46 6A 61 73 β€” first the DHCP option layout (https://tools.ietf.org/html/rfc2132#section-2), then the specific relay agent information option sub-option layout (https://tools.ietf.org/html/rfc3046#section-2.0) β€” is as follows:

β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Code β”‚ Length β”‚            Data            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  52  β”‚ (auto) β”‚ 01 04 46 6A 61 73 ("Fjas") β”‚
β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Sub-opt. β”‚ Lenth β”‚         Data         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    01    β”‚   4   β”‚ 46 6A 61 73 ("Fjas") β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

🌈 SEE ALSO 🌈


πŸ‘‘ AUTHORS πŸ‘‘

  • Andreas Misje <amisje@gmail.com>

and that's the whole page, bestie 🫢 it came from Debian, dated May 9, 2019, and it answers to the name DHCPOPTINJ(8) ✨ go forth and inject responsibly πŸ’–

πŸ“¦ where this page comes from