DHCPY6D.CONF(5)
π NAME π
dhcpy6d.conf β the configuration file for the DHCPv6 server daemon dhcpy6d. That's the whole name section, bestie, we move. π
β¨ DESCRIPTION β¨
okay so β this file holds the general settings for the DHCPv6 server daemon dhcpy6d. It follows RFC 822 style, and it gets parsed by the Python ConfigParser module. It's got several sections and we're gonna talk about every single one in detail here, no skimming, no scrolling past. π
There's also an online documentation available at https://dhcpy6d.de/documentation/config if you want the web version, iykyk.
Boolean settings can be set with 1|0, on|off or yes|no values. Pick your fighter, they all work. β¨
Some options allow multiple values. Those have to be separated by spaces. Spaces, bestie, not vibes.
There are 5 types of sections:
[dhcpy6d]
This section contains general options like interfaces, storage and logging. Only one [dhcpy6d] section is allowed. One. Periodt.
[address_<address_name>]
There can be various [address_<address_name>] sections. In several address sections several address ranges and types can be defined. Addresses are organized in classes. For details read further down. π
[prefix_<prefix_name>]
There can be various [prefix_<prefix_name>] sections. In several prefix sections several prefix ranges and types can be defined. Prefixes are organized in classes. For details read further down. πΈ
[class_<class_name>]
Class definitions allow one to apply different addresses, time limits et al. to different types of clients. Very main character energy, honestly.
[bootfile_<bootfile_name>]
There can be various [bootfile_<bootfile_name>] sections. In several bootfile sections several tftp bootfile urls with restrictions to CPU architecture and user class supplied by the PXE client can be defined. π¦
π GENERAL CONFIGURATION IN SECTION [DHCPY6D] π
This section contains the important general options. Heads up though: values are sometimes examples and not meant to be used in production environments. Do not copy-paste the example values into prod, I'm begging. π
really_do_it = yes|noβ letdhcpy6dreally do it and respond to client requests. Disabling this might be of use for debugging and testing. Default:nointerface = <interface> [<interface> ...]β the interfaces the server listens on, defined with the keywordinterface. Multiple interfaces have to be separated by spaces.serverduid = <longlongserverduid>β the server DUID should be configured withserverduid. If there is none,dhcpy6dcreates a new one at every startup. Windows clients might run a little bit wild when server DUID changed π. You are free to compose your own as long as it follows RFC 3315. Please note that it has to be in hexadecimal format β no octals, no-, just like in the example below. The example here is a DUID-LLT (Link-layer Address Plus Time) even if it should be a DUID-TLL as timestamp comes first. It is composed of DUID-type(LLT=1) + Hardware-type(Ethernet=1) + Unixtime-in-hexadecimal + MAC-address what makes a0001+0001+11fb5dc9+01023472a6c5=0001000111fb5dc901023472a6c5.server_preference = <0-255>β the server preference determines the priority of the server. The maximum value is255which means highest priority. Default:255user = <user>β for security reasonsdhcpy6dcan and should be run as non-root user. Default:rootgroup = <group>β for security reasonsdhcpy6dcan and should be run as non-root group. Default:rootnameserver = <nameserver-address> [<nameserver-address> ...]β if an address type is of categorydnsat least one nameserver has to be given here. If more than one is needed they have to be separated by spaces.domain = <domain-name>β the domain to be used with FQDN hostnames for option 39.domain_search_list = <domain-name> [<domain-name> ...]β domain search lists to be used with option 24. If none is given the value ofdomainabove is used. Multiple domains have to be separated by space or comma.ntp_server = <ntp_server> [<ntp_server> ...]β NTP servers to be used.<ntp_server>can be unicast addresses, multicast addresses or FQDNs following RFC 5908 for DHCPv6 option 56.log = yes|noβ enable logging. Default:nolog_console = yes|noβ log to the console wheredhcpy6dhas been started. Default:nolog_file = </path/to/dhcpy6d/log/file>β defines the file used for logging. Will be created if it does not yet exist. π«Άlog_syslog = yes|noβ if logs should go to syslog it is set here. Default:nolog_syslog_destination = syslogserverβ an UDP syslog server may be used iflog_syslog_destinationpoints to it. Optionally a port other than default514can be set when adding":<port>"to the destination.log_syslog_facility = <log-facility>β the default syslog facility isdaemonbut can be changed here. Default:daemonlog_mac_llip = yes|noβ log discovered MAC/LLIP pairs of clients. Might be pretty verbose in larger setups and with disabled MAC/LLIP pair caching. Default:nostore_config = file|sqlite|mysql|postgresql|noneβ configuration of clients can be stored in a file or in a database. Databases MySQL, PostgreSQL and SQLite are supported at the moment, thus possible values arefile,mysql,postgresqlorsqlite. To disable any client configuration source it has to benone. Default:nonestore_file_config = </path/to/client/conf/file>β file which contains the clients configuration. For details seedhcpy6d-clients.conf(5). Default:/etc/dhcpy6d-clients.confstore_sqlite_config = /path/to/sqlite/config/fileβ SQLite database file which contains the clients configuration. Default:config.sqlitestore_volatile = sqlite|mysql|postgresqlβ volatile data like leases and the mapping between Link Local addresses and MAC addresses can be stored in MySQL, PostgreSQL or SQLite database, so the possible values aremysql,postgresqlandsqlite.store_sqlite_volatile = /path/to/sqlite/volatile/fileβ if set tosqlitea SQLite database file must be defined. Default:/var/lib/dhcpy6d/volatile.sqlitestore_db_host = <database-host>store_db_db = <database-name>store_db_user = <database-user>store_db_password = <database-password>β ifstore_configand/orstore_volatileuse a database to store information it has to be set with these self-explanatory options. The same database is used for config and volatile data.cache_mac_llip = yes|noβ cache discovered MAC/LLIP pairs in database. If enabled it reduces response time and opens dhcpy6d to possible MAC/LLIP poisoning. If disabled might increase system load. Default:noidentification = <mac> <duid> <hostname>β clients can be set to be identified by several attributes β MAC address, DUID or hostname. At least one ofmac,duidorhostnameis necessary. Hostname is the one sent in client request with DHCPv6 option 39. Identification is used to get the correct settings for the client from config file or database. Same MAC and different DUIDs might be interesting for clients with multiple OS. Default:macidentification_mode = match_all|match_someβ if more than one identification attribute has been set, the identification mode can be one ofmatch_allormatch_some. The first means that all attributes have to match to identify a client and the latter is more tolerant. Default:match_allignore_mac = yes|noβ if serving only for delivering addresses regardless of classes (e.g. on PPP interface) MACs do not need to be investigated.dns_update = yes|noβ dynamically update DNS. This works at the moment only with Bind DNS, but might be extended to others, maybe via call of an external command. Default:nodns_update_nameserver = <nameserver-address> [<nameserver-address> ...]dns_use_rndc = yes|noβ DNS updates might be able without RNDC key but this is not advised. Default:yesdns_rndc_key = <rndc-key_like_in_rndc.conf>dns_rndc_secret = <secret_key_like_in_rndc.confβ (yes bestie, that>is missing in the original, I am merely the messenger π)- When connecting to a Bind DNS server for dynamic DNS updates its address and the necessary RNDC data must be set. That covers
dns_update_nameserver,dns_use_rndc,dns_rndc_keyanddns_rndc_secretabove. π dns_ignore_client = yes|noβ clients may request that they update the DNS record theirself. If their wishes shall be ignored this option has to be true. Default:yesdns_use_client_hostname = yes|noβ the client hostname either comes from configuration ofdhcpy6dor in the client request. Default:nopreferred_lifetime = <seconds>β Default:5400valid_lifetime = <seconds>β Default:7200t1 = <seconds>β Default:2700t2 = <seconds>β preferred lifetime, valid lifetime, T1 and T2 in seconds are configured with the corresponding options. Default:4050information_refresh_time = <seconds>β the lifetime of information given to clients as response to an information-request message. Default:6000ignore_iaid = yes|noβ ignore IAID when looking for leases in database. Might be of use in case some clients are changing their IAD for some unknown reason. Default:noignore_unknown_clients = yes|noβ ignore clients if no trace of them can be found in the neighbor cache. Default:yesrequest_limit = yes|noβ enables request limits for clients which can be controlled byrequest_limit_timeandrequest_limit_count. Default:norequest_limit_identification = mac|llipβ identifies clients either by MAC address or Link Local IP. Default:lliprequest_limit_time = <seconds>β Default:60request_limit_count = <max_number_of_requests>β requests can be limited to avoid server to be flooded by buggy clients. Set number of request during a certain time in seconds. Default:20request_limit_release_time = <seconds>β duration in seconds for brute force clients to stay on the blacklist. Default:7200manage_routes_at_start = yes|noβ check prefixes at startup and call commands for adding and deleting routes respectively. Default:no
β¨ ADDRESS DEFINITIONS IN MULTIPLE [ADDRESS_<ADDRESS_NAME>] SECTIONS β¨
ok so the <address_name> bit in an [address_<address_name>] section? literally whatever name you want bestie, it's an arbitrarily chosen identifier β like clients_global or invalid_clients_local. You can have so many address definitions and your classes will use them. Every single address definition can hold a bunch of properties:
category = mac|eui64|id|range|random|fixed|dnsβ categories are the moment when you're defining patterns for addresses, fr fr. An address belongs to one category:macβ uses the MAC address from the client request as part of the address π«eui64β also uses the MAC address from the client as part of the address, but converts it to a 64-bit extended unique identifier (EUI-64) π¦idβ uses the ID given to the client in the configuration file or database as one octet of the address, and it should be in range0-ffffrangeβ generates addresses of a given range, like0-ffffrandomβ randomly created 64 bit values used as the host part in the address π²fixedβ use addresses from the client configuration only.dnsβ ask the DNS server for the IPv6 address of the client host πΈ
range = <from>-<to>β sets the range for addresses of categoryrange.fromβ starting hex number of the range, minimum is0toβ maximum hex limit of the range, highest isffff.
pattern = 2001:db8::$mac$|$id$|$range$|$random$pattern= $prefix$|$mac$|$eui64$|$id$|$range$|$random$β patterns let you design the addresses according to their category. Peep the examples section below if you want it spelled out clearer π$mac$β the MAC address from the DHCPv6 request's Link Local Address found in the neighbor cache gets inserted instead of the placeholder. It gets stretched over 3 octets so like00:11:22:33:44:55becomes0011:2233:4455β¨$eui64$β the MAC address converted to a modified 64-bit extended unique identifier (EUI-64) from the DHCPv6 request's Link Local Address found in the neighbor cache gets inserted instead of the placeholder. It gets converted according to RFC 4291 so like52:54:00:e5:b4:64becomes5054:ff:fee5:b464$id$β if clients get an ID in the client configuration file or in the client configuration database, this ID fills one octet. So the ID has to be in the range of0000-ffff.$range$β if the address is of categoryrange, the range defined with the extra keywordrangegets used here in place of one octet. This is why the range can span from0000-ffff. Clients will get an address out of the given range.$random64$β a 64 bit random address gets generated in place of this variable. Clients get a random address just like they would if privacy extensions were used. The random part spans over 4 octets π$prefixβ this placeholder can be used instead of a literal prefix and uses the prefix given when callingdhcpy6dvia the--prefixargument, like$prefix$::$id$.
ia_type = na|taβ IA (Identity Association) types can be one of non-temporary addressnaor temporary addressta. Default and probably most used isna. Default:napreferred_lifetime = <seconds>valid_lifetime = <seconds>β by default preferred and valid lifetime are set in the general settings, but it's configurable individually for every address setting πdns_update = yes|noβ Default:nodns_zone = <dnszone>dns_rev_zone = <reverse_dnszone>β if these addresses should be synchronized with Bind DNS, these three settings have to be set accordingly. The nameserver for updates is set in the general settings.
π Default Address π
The address scheme used for the default class class_default is by default named address_default. It should be enough if address_default is defined β only if unknown clients should get extra nameservers etc. does a class_default have to be set.
[address_default]β address scheme used as default for clients which do not match any other class thanclass_default.
π PREFIX DEFINITIONS IN MULTIPLE [PREFIX_<PREFIX_NAME>] SECTIONS π
The <prefix_name> part of an [prefix_<prefix_name>] section is an arbitrarily chosen identifier, like customers. A prefix definition can contain several properties:
category = rangeβ like addresses, prefixes have a category. Right now onlyrangeseems to make sense, similar to ranges in addresses being like0-ffff.range = <from>-<to>β sets the range for a prefix of categoryrange.fromβ starting hex number of the range, minimum is0toβ maximum hex limit of the range, highest isffff.
pattern = 2001:db8:$range$::pattern= $prefix$:$range$::β patterns let you design the addresses according to their category. Check the examples section below if you want it made clearer π$range$β if the address is of categoryrange, the range defined with the extra keywordrangegets used here in place of one octet. This is why the range can span from0000-ffff. Clients will get an address out of the given range.
length = <prefix_length>β length of the prefix given out to clients πpreferred_lifetime = <seconds>valid_lifetime = <seconds>β by default preferred and valid lifetime are set in the general settings, but it's configurable individually for every prefixk setting.route_link_lokal = yes|noβ by default the Link Local Address of the requesting client is not used as the router address for an external call. Instead the client should be able to retrieve exactly 1 address from the server to be used as router for the delegated prefix. Alternatively the client Link Local Address might be used by enabling this option. Default:no
ngl bestie that last one is a whole vibe check on your routing setup, so don't just flip it on without thinking π«Ά
π¦ CLASS DEFINITIONS IN MULTIPLE [CLASS_<CLASS_NAME>] SECTIONS π¦
okay bestie so the <class_name> bit of an [class_<class_name>] section is just... whatever name you feel like, fr fr β like clients or invalid_clients, it's arbitrarily chosen, nobody's judging. Clients can be grouped into classes, and that's the whole point: different classes can have different properties, different address sets and different numbers of addresses. Classes might also rock different name servers, time intervals, filters and interfaces. it's giving customizable π
A client gets the addresses, nameserver and T1/T2 values of the class which it is configured for in client configuration database or file. periodt.
addresses = <address_name> [<address_name> ...]β a class can contain as many addresses as needed, no cap. Their names have to be separated by spaces. "Name" here means the name-part of an address section, like[address_name]. Important: if a class does not contain any addresses, clients won't get any address β except they have one fixed defined in client configuration file or database. so like, don't forget it πprefixes = <prefix_name> [<address_name> ...]β a class can contain prefixes β even most probably only one prefix will be useful, ngl. "Name" means the name-part of a prefix section.answer = normal|noaddress|noneβ normally a client will get an answer, but if for whatever reason there's a need to give it an NoAddrAvail message back, or to completely ignore the client, it can be set here. Default:normalnameserver = <nameserver-address> [<nameserver-address> ...]β each class can have its own nameservers. If this option is used it replaces the nameservers from general settings. no merge, bestie, it's a takeover πt1 = <seconds>t2 = <seconds>β each class can have its ownt1andt2values. The ones from general settings will be overridden. Might be of use for some invalid-but-about-to-become-valid-somehow-soon class. we love a glow-up arc β¨filter_hostname = <regular_expression>filter_mac = <regular_expression>filter_duid = <regular_expression>β Filters allow one to apply a class to a client not by configuration but by a matching regular expression filter. Most useful might be the filtering by hostname, but maybe there is some use for DUID and MAC address based filtering too. The regular expressions are meant to be Python Regular Expressions. Seehttps://docs.python.org/2/howto/regex.htmland examples section below for details. iykyk πinterface = <interface> [<interface> ...]β it is possible to let a class only apply on specific interfaces. These have to be separated by spaces.advertise = addresses|prefixesβ a class per default allows one to advertise addresses as well as prefixes if requested. This option allows one to narrow the answers down to either addresses or prefixes. Default:addressescall_up = <executable> [$prefix$] [$length$] [$router$]β when a route is requested and accepted the custom executable will be called, and the optional but senseful variables will be filled with their appropriate values. π₯$prefix$β Contains the prefix advertised to the client.$length$β The prefix length.$router$β The host which routes into the advertised prefix β of course the requesting client IPv6.
call_down = <executable> [$prefix$] [$length$] [$router$]β when a route is released the custom executable will be called, and the optional but senseful variables will be filled with their appropriate values. same trio, same energy π$prefix$β Contains the prefix advertised to the client.$length$β The prefix length.$router$β The host which routes into the advertised prefix β of course the requesting client IPv6.
bootfiles = <bootfile> [<bootfile> ...]β List of PXE bootfiles to evaluate for clients in this client. Each value must refer a bootfile section (see below). Each bootfile is evaluated by the filter defined in the bootfile section, the first matching bootfile is chosen.
Example:
bootfiles = eth1_ipxe eth1_efi64 eth1_efi32 eth1_efibc
π Default Class π
okay so at the moment, every client which does not match any other class by client configuration or filter automatically matches the class "default". This class could get an address scheme too, bestie. It should be enough if address_default is defined β only if unknown clients should get extra nameservers etc. does a class_default have to be set.
[class_default]β Default class for all clients that do not match any other class. Like any other class it might contain all options that apply to a class. the people's champ π«Ά[class_default_<interface>]β If dhcpy6d listens at multiple interfaces, one can define a default class for every'interface'. one for each, no favs π
π BOOTFILE DEFINITIONS IN MULTIPLE [BOOTFILE_<BOOTFILE_NAME>] SECTIONS π
The <bootfile_name> part of an [bootfile_<bootfile_name>] section is an arbitrarily chosen identifier like efi32, bios or efi64. Each bootfile can be restricted to an architecture and/or an user class which is sent by the PXE client. restrictive queen behavior, love that for us β¨
bootfile_url = <url>β the bootfile URL in a format liketftp://[2001:db8:85a3::8a2e:370:7334]/pxe.efi. The possible protocols are dependent on the PXE client, but TFTP should be supported by almost every client. so like, tftp is the safe bet πclient_architecture = <architecture>β Optionally restrict the bootfile to a specific CPU architecture. If the client doesn't match the requirement, the next bootfile assigned to the class definition is chosen, or no bootfile is provided, if there are no further alternatives. no match β next one up, and if there's nothing left, tough luck π
Either the integer identifier for an architecture is possible (e.g. 0009 for EFI x86-64). The integer must consist of four numeric digits, empty digits must be written as zero (e.g. 9 => 0009). For a full list of possible integer identifier see https://tools.ietf.org/html/rfc4578#section-2.1. Alternatively the well-known names of registered CPU architectures defined in RF4578 can be used:
- Intel x86PC
- NEC/PC98
- EFI Itanium
- DEC Alpha
- Arc x86
- Intel Lean Client
- EFI IA32
- EFI BC
- EFI Xscale
- EFI x86-64
pick your fighter, bestie π
user_class = <user_class>β Optionally restrict this bootfile to PXE clients sending this user class. Theuser_classis matched against the value of the client with simple comparison (no regular expression β keep it simple okay).
Example:
user_class = iPXE
This restricts the bootfile to the iPXE boot firmware. obsessed with a filter that actually filters π
β EXAMPLES β
The following paragraphs contain some hopefully helpful examples: yes bestie, we're doing show-and-tell π
πΈ Minimal configuration πΈ
Here in this minimalistic example the server daemon listens on interface eth0. It does not use any client configuration source but answers requests with default addresses. These are made of the pattern fd01:db8:dead:bad:beef:$mac$ and result in addresses like fd01:db8:deaf:bad:beef:1020:3040:5060 if the MAC address of the requesting client was 10:20:30:40:50:60. like, it literally just builds the address out of the MAC. iconic behavior β¨
[dhcpy6d]
# Set to yes to really answer to clients.
really_do_it = yes
# Interface to listen to multicast ff02::1:2.
interface = eth0
# Some server DUID.
serverduid = 0001000134824528134567366121
# Do not identify and configure clients.
store_config = none
# SQLite DB for leases and LLIP-MAC-mapping.
store_volatile = sqlite
store_sqlite_volatile = /var/lib/dhcpy6d/volatile.sqlite
# Special address type which applies to all not specially.
# configured clients.
[address_default]
# Choosing MAC-based addresses.
category = mac
# ULA-type address pattern.
pattern = fd01:db8:dead:bad:beef:$mac$
π¦ Configuration with valid and unknown clients π¦
This example shows some more complexity. Here only valid hosts will get a random global address from 2001:db8::/64. Unknown clients get a default ULA range address from fc00::/7. two tiers of treatment, main character vs. mystery guest π
[dhcpy6d]
# Set to yes to really answer to clients.
really_do_it = yes
# Interface to listen to multicast ff02::1:2.
interface = eth0
# Server DUID - if not set there will be one generated every time dhcpy6d starts.
# This might cause trouble for Windows clients because they go crazy about the
# changed server DUID.
serverduid = 0001000134824528134567366121
# Non-privileged user/group.
user = dhcpy6d
group = dhcpy6d
# Nameservers for option 23 - there can be several specified separated by spaces.
nameserver = fd00:db8::53
# Domain to be used for option 39 - host FQDN.
domain = example.com
# Domain search list for option 24 - domain search list.
# If omitted the value of option "domain" above is taken as default.
domain_search_list = example.com
# Do logging.
log = yes
# Log to console.
log_console = no
# Path to logfile.
log_file = /var/log/dhcpy6d.log
# Use SQLite for client configuration.
store_config = sqlite
# Use SQLite for volatile data.
store_volatile = sqlite
# Paths to SQLite database files.
store_sqlite_config = /var/lib/dhcpy6d/config.sqlite
store_sqlite_volatile = /var/lib/dhcpy6d/volatile.sqlite
# Declare which attributes of a requesting client should be checked
# to prove its identity. It is possible to mix them, separated by spaces.
identification = mac
# Declare if all checked attributes have to match or is it enough if
# some do. Kind of senseless with just one attribute.
identification_mode = match_all
# These lifetimes are also used as default for addresses which
# have no extra defined lifetimes.
preferred_lifetime = 43200
valid_lifetime = 64800
t1 = 21600
t2 = 32400
# ADDRESS DEFINITION
# Addresses for proper valid clients.
[address_valid_clients]
# Better privacy for global addresses with category random.
category = random
# The following pattern will result in addresses like 2001:0db8::d3f6:834a:03d5:139c.
pattern = 2001:db8::$random64$
# Default addresses for unknown invalid clients.
[address_default]
# Unknown clients will get an internal ULA range-based address.
category = range
# The keyword "range" sets the range used in pattern.
range = 1000-1fff
# This pattern results in addresses like fd00::1234.
pattern = fd00::$range$
# CLASS DEFINITION
# Class for proper valid client.
[class_valid_clients]
# At least one of the above address schemes has to be set.
addresses = valid_clients
# Valid clients get a different nameserver.
nameserver = 2001:db8::53
# Default class for unknown hosts - only necessary here because of time interval settings.
[class_default]
addresses = default
# Short interval of address refresh attempts so that a client's status
# change will be reflected in IPv6 address soon.
t1 = 600
t2 = 900
okay so that's the whole thing wrapping up π unknown clients land in [address_default] with a ULA-style address like fd00::1234, and [class_default] keeps t1 = 600 and t2 = 900 so a client whose status changes gets noticed fast. no cap, the short timers are the whole point there.
π¦ Configuration with 2 network segments, servers, valid and unknown clients π¦
bestie this one is giving two-network-segment main character energy β one segment for servers, one for clients. Servers only get local ULA addresses. Valid clients get two addresses, one local ULA and one global GUA. and like, the GUA thing? DHCPv6 does support it, but at the moment it's only well supported by Windows clients β everyone else is lowkey flaky about it π. Unknown clients get a local ULA address. and the nameserver info? only valid clients and servers get that. periodt.
[dhcpy6d]
# Set to yes to really answer to clients.
really_do_it = yes
# Interfaces to listen to multicast ff02::1:2.
# eth1 - client network
# eth2 - server network
interface = eth1 eth2
# Server DUID - if not set there will be one generated every time dhcpy6d starts.
# This might cause trouble for Windows clients because they go crazy about the
# changed server DUID.
serverduid = 0001000134824528134567366121
# Non-privileged user/group.
user = dhcpy6d
group = dhcpy6d
# Domain to be used for option 39 - host FQDN.
domain = example.com
# Domain search list for option 24 - domain search list.
# If omited the value of option "domain" above is taken as default.
domain_search_list = example.com
# Do logging.
log = yes
# Log to console.
log_console = no
# Path to logfile.
log_file = /var/log/dhcpy6d.log
# Use MySQL for client configuration.
store_config = mysql
# Use MySQL for volatile data.
store_volatile = mysql
# Data used for MySQL storage.
store_db_host = localhost
store_db_db = dhcpy6d
store_db_user = dhcpy6d
store_db_password = dhcpy6d
# Declare which attributes of a requesting client should be checked
# to prove its identity. It is possible to mix them, separated by spaces.
identification = mac
# Declare if all checked attributes have to match or is it enough if
# some do. Kind of senseless with just one attribute.
identification_mode = match_all
# These lifetimes are also used as default for addresses which
# have no extra defined lifetimes.
preferred_lifetime = 43200
valid_lifetime = 64800
t1 = 21600
t2 = 32400
# ADDRESS DEFINITION
# Global addresses for proper valid clients (GUA).
[address_valid_clients_global]
# Better privacy for global addresses with category random.
category = random
# The following pattern will result in addresses like 2001:0db8::d3f6:834a:03d5:139c.
pattern = 2001:db8::$random64$
# Local addresses for proper valid clients (ULA).
[address_valid_clients_local]
# Local addresses need no privacy, so they will be based of range.
category = range
range = 2000-2FFF
# Valid clients will get local ULA addresses from fd01::/64.
pattern = fd01::$range$
# Servers in servers network will get local addresses based on IDs from client configuration.
[address_servers]
# IDs are set in client configuration database in range of 0-FFFF.
category = id
# Servers will get local ULA addresses from fd02::/64.
pattern = fd02::$id$
# Default addresses for unknown invalid clients
[address_default]
# Unknown clients will get an internal ULA range-based address.
category = range
# The keyword "range" sets the range used in pattern.
range = 1000-1FFF
# This pattern results in addresses like fd00::1234.
pattern = fd00::$range$
# CLASS DEFINITION
# Class for proper valid client.
[class_valid_clients]
# Clients only exist in network linked with eth1.
interface = eth1
# Valid clients get 2 addresses, one local ULA and one global GUA
# (only works reliably with Windows clients).
addresses = valid_clients_global valid_clients_local
# Only valid clients get a nameserver from server network.
nameserver = fd02::53
# Class for servers in network on eth2
[class_servers]
# Servers only exist in network linked with eth2.
interface = eth2
# Only local addresses for servers.
addresses = servers
# Nameserver from server network.
nameserver = fd02::53
# Default class for unknown hosts - only necessary here because of time interval settings
[class_default]
addresses = default
# Short interval of address refresh attempts so that a client's status
# change will be reflected in IPv6 address soon.
t1 = 600
t2 = 900
the way [class_valid_clients] is scoped to eth1 and [class_servers] is scoped to eth2β¦ iconic separation honestly. and [address_servers] pulls its IDs straight from the client configuration database in the range 0-FFFF π
.
π Configuration with dynamic DNS Updates π
okay so in this one the hostnames of valid clients get registered in the Bind DNS server π. the zones you're updating? configured per address definition. here only the global GUA addresses for valid clients get updated in DNS. and the hostnames come from client configuration data β whatever the clients themselves supply is ignored, bye π.
[dhcpy6d]
# Set to yes to really answer to clients.
really_do_it = yes
# Interface to listen to multicast ff02::1:2.
interface = eth0
# Server DUID - if not set there will be one generated every time dhcpy6d starts.
# This might cause trouble for Windows clients because they go crazy about the
# changed server DUID.
serverduid = 0001000134824528134567366121
# Non-privileged user/group.
user = dhcpy6d
group = dhcpy6d
# Nameservers for option 23 - there can be several specified separated by spaces.
nameserver = fd00:db8::53
# Domain to be used for option 39 - host FQDN.
domain = example.com
# Domain search list for option 24 - domain search list.
# If omited the value of option "domain" above is taken as default.
domain_search_list = example.com
# This works at the moment only for ISC Bind nameservers.
dns_update = yes
# RNDC key name for DNS Update.
dns_rndc_key = rndc-key
# RNDC secret - mostly some MD5-hash. Take it from
# nameservers' /etc/rndc.key.
dns_rndc_secret = 0123456789012345679
# Nameserver to talk to.
dns_update_nameserver = ::1
# Regarding RFC 4704 5. there are 3 kinds of client behaviour
# for N O S bits:
# - client wants to update DNS itself -> sends 0 0 0
# - client wants server to update DNS -> sends 0 0 1
# - client wants no server DNS update -> sends 1 0 0
# Ignore client ideas about DNS (if at all, what name to use, self-updating...)
# Here client hostname is taken from client configuration
dns_ignore_client = yes
# Do logging.
log = yes
# Log to console.
log_console = no
# Path to logfile.
log_file = /var/log/dhcpy6d.log
# Use SQLite for client configuration.
store_config = sqlite
# Use SQLite for volatile data.
store_volatile = sqlite
# Paths to SQLite database files.
store_sqlite_config = config.sqlite
store_sqlite_volatile = volatile.sqlite
# Declare which attributes of a requesting client should be checked
# to prove its identity. It is possible to mix them, separated by spaces.
identification = mac
# ADDRESS DEFINITION
# Addresses for proper valid clients.
[address_valid_clients]
# Better privacy for global addresses with category random.
category = random
# The following pattern will result in addresses like 2001:0db8::d3f6:834a:03d5:139c.
pattern = 2001:db8::$random64$
# Update these addresses in Bind DNS
dns_update = yes
# Zone to update.
dns_zone = example.com
# Reverse zone to update
dns_rev_zone = 8.b.d.0.1.0.0.2.ip6.arpa
# Default addresses for unknown invalid clients.
[address_default]
# Unknown clients will get an internal ULA range-based address.
category = range
# The keyword "range" sets the range used in pattern.
range = 1000-1FFF
# This pattern results in addresses like fd00::1234.
pattern = fd00::$range$
# CLASS DEFINITION
# Class for proper valid client.
[class_valid_clients]
# At least one of the above address schemes has to be set.
addresses = valid_clients
# Valid clients get a different nameserver.
nameserver = 2001:db8::53
ngl the dns_ignore_client = yes bit is doing a lot of heavy lifting here π«Ά β RFC 4704 section 5 says clients can send 0 0 0 (they wanna update DNS themselves), 0 0 1 (they want the server to do it), or 1 0 0 (no server DNS update please), and this setting means you justβ¦ ignore all of that and take the hostname from your own client configuration. also heads up: dns_update at the moment only works with ISC Bind nameservers, and that RNDC secret is mostly some MD5-hash you grab from the nameserver's /etc/rndc.key π.
β¨ Configuration with filter β¨
so here's the vibe: class membership gets decided by a filter on hostnames πΈ. all the Windows machines have win* names, and when they request an address that hostname gets filtered. iykyk.
[dhcpy6d]
# Set to yes to really answer to clients.
really_do_it = yes
# Interface to listen to multicast ff02::1:2.
interface = eth0
# Server DUID - if not set there will be one generated every time dhcpy6d starts.
# This might cause trouble for Windows clients because they go crazy about the
# changed server DUID.
serverduid = 0001000134824528134567366121
# Use no client configuration.
store_config = none
# Use SQLite for volatile data.
store_volatile = sqlite
# Paths to SQLite database file.
store_sqlite_volatile = volatile.sqlite
# ADDRESS DEFINITION
[address_local]
category = range
range = 1000-1FFF
pattern = fd00::$range$
[address_global]
category = random
pattern = 2001:638::$random64$
# CLASS DEFINITION
and yeah β store_config = none over here because the filtering does the work instead of a client config database, with just store_sqlite_volatile = volatile.sqlite hanging onto the volatile data π. and we are so not done, the class part of this one is coming up next π
[class_windows]
addresses = local
# Python regular expressions to be used here
filter_hostname = win.*
[class_default]
addresses = global
π Only use fixed addresses
okay so what if you want dhcpy6d to hand out literally nothing generated? Then your clients need an address that's already written down somewhere β either in their own configuration file or in the database. It's giving very "hand-picked, no surprises, main character client" energy. The shape of it looks like this:
[example-client]
hostname = example-client
mac = 01:02:03:04:05:06
class = fixed_address
address = 2001:db8::1234
And the class that client belongs to? Bestie, it simply must not have any address definition at all. It can stay totally empty and that's the whole point:
[dhcpy6d]
# Set to yes to really answer to clients.
really_do_it = yes
# Interface to listen to multicast ff02::1:2.
interface = eth0
# Some server DUID.
serverduid = 0001000134824528134567366121
# Do not identify and configure clients.
store_config = none
# SQLite DB for leases and LLIP-MAC-mapping.
store_volatile = sqlite
store_sqlite_volatile = /var/lib/dhcpy6d/volatile.sqlite
# Special address type which applies to all not specially.
# configured clients.
[address_default]
# Choosing MAC-based addresses.
category = mac
# ULA-type address pattern.
pattern = fd01:db8:dead:bad:beef:$mac$
# To use the EUI-64 instead of the plain MAC address:
#category = eui64
#pattern = fd01:db8:dead:bad:$eui64$
[class_fixed_address]
# just no address definiton here
π¦ Supply a PXE bootfile for different CPU architectures and user classes
This example shows how to assign PXE bootfiles depending on CPU architecture and user class at the same time β yes, both, iconic:
[class_default_eth1]
bootfiles = eth1_ipxe eth1_efi64 eth1_efi32 eth1_efibc
addresses = eth1
interface = eth1
nameserver = fdff:cc21:56df:8bc8:5054:00ff:fec2:c5dd 2001:0470:76aa:00f5:5054:00ff:fec2:c5dd
filter_mac = .*
[address_eth1]
# Choosing EUI-64-based addresses.
category = eui64
# ULA-type address pattern.
pattern = fdff:cc21:56df:8bc8::$eui64$
[bootfile_eth1_ipxe]
user_class = iPXE
bootfile_url = tftp://[fdff:cc21:56df:8bc8:5054:00ff:fec2:c5dd]/default.ipxe
[bootfile_eth1_efi32]
client_architecture = 0006
bootfile_url = tftp://[fdff:cc21:56df:8bc8:5054:00ff:fec2:c5dd]/efi32/ipxe.efi
[bootfile_eth1_efibc]
client_architecture = 0007
bootfile_url = tftp://[fdff:cc21:56df:8bc8:5054:00ff:fec2:c5dd]/efi64/ipxe.efi
[bootfile_eth1_efi64]
client_architecture = 0009
bootfile_url = tftp://[fdff:cc21:56df:8bc8:5054:00ff:fec2:c5dd]/efi32/ipxe.efi
[bootfile_eth2_ipxe]
user_class = iPXE
bootfile_url = tftp://[fdff:cc21:56df:fe1d:5054:00ff:fe3f:5da0]/default.ipxe
[bootfile_eth2_efi32]
client_architecture = 0006
bootfile_url = tftp://[fdff:cc21:56df:fe1d:5054:00ff:fe3f:5da0]/efi32/ipxe.efi
[bootfile_eth2_efibc]
client_architecture = 0007
bootfile_url = tftp://[fdff:cc21:56df:fe1d:5054:00ff:fe3f:5da0]/efi64/ipxe.efi
[bootfile_eth2_efi64]
client_architecture = 0009
bootfile_url = tftp://[fdff:cc21:56df:fe1d:5054:00ff:fe3f:5da0]/efi32/ipxe.efi
First thing that happens is a check for the iPXE boot firmware β and if that hits, it delivers an iPXE script. π― Otherwise, the iPXE binary that matches the architecture gets served instead. Slay.
π LICENSE
okay legal bestie moment, but make it cute π«Ά
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation β either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
On Debian systems, the full text of the GNU General Public License version 2 can be found in the file /usr/share/common-licenses/GPL-2.
π SEE ALSO
βοΈ AUTHOR
Copyright (C) 2012-2022 Henri Wahl <henri@dhcpy6d.de> π
Β©οΈ COPYRIGHT
This manual page is licensed under the GPL-2 license.
and that's a wrap, bestie!! πβ¨ this whole thing was dhcpy6d.conf(5) version 1.0.3, dated 2020-12-21 β go forth and configure your IPv6 like the icon you are ππ
π¦
π¦ where this page comes from
- debian bookworm: dhcpy6d 1.2.3-1 Β· GPL-2.0-or-later
- debian trixie: dhcpy6d 1.2.3-1.1 Β· GPL-2.0-or-later
- ubuntu noble: dhcpy6d 1.2.3-1 Β· GPL-2.0-or-later
- ubuntu questing: dhcpy6d 1.2.3-1.1
- ubuntu resolute: dhcpy6d 1.2.3-1.1build1 Β· GPL-2.0-or-later
- ubuntu stonking: dhcpy6d 1.2.3-1.1build1 Β· GPL-2.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms