"comment": "DdnsDomain example",
"dns-servers":
[
- { // This server has an entry in gss/servers and
+ {
+ // This server has an entry in gss/servers and
// thus will use GSS-TSIG.
"ip-address": "192.0.2.1"
},
- { // This server also has an entry there, so will
+ {
+ // This server also has an entry there, so will
// use GSS-TSIG, too.
"ip-address": "192.0.2.2",
"port": 5300
// store client keys. As credentials cache is more flexible,
// it is recommended to use it. Typically, using both at the
// same time may cause problems.
- //
// "client-keytab": "FILE:/etc/dhcp.keytab", // toplevel only
"credentials-cache": "FILE:/etc/ccache", // toplevel only
{
// ------------------ DHCP-DDNS ---------------------
-//
"DhcpDdns":
{
// -------------- Global Parameters ----------------
-//
// D2 will listen for update requests for Kea DHCP servers at 127.0.0.1
// on port 53001. Maximum time to we will wait for a DNS server to
// respond to us is 1000 ms.
"user-context": { "version": 1 },
-//
// ----------------- Control Socket -----------------
-//
"control-socket":
{
"socket-name": "/tmp/kea-ddns-ctrl-socket"
},
-//
// ----------------- Hooks Libraries -----------------
-//
"hooks-libraries":
[
}
],
-//
// ----------------- Forward DDNS ------------------
-//
// 1. Zone - "four.example.com.
// It uses TSIG, key name is "d2.md5.key"
// It is served by one DNS server which listens for DDNS requests at
// 172.16.1.1 on the default port 53 (standard DNS port)
-//
// 2. Zone - "six.example.com."
// It does not use TSIG.
// It is server by one DNS server at "2001:db8:1::10" on port 7802
},
// ----------------- Reverse DDNS ------------------
-//
// We will update Reverse DNS for one zone "2.0.192.in-addr-arpa". It
// uses TSIG with key "d2.sha1.key" and is served by two DNS servers:
// one listening at "172.16.1.1" on 53001 and the other at "192.168.2.10".
-//
"reverse-ddns":
{
"ddns-domains":
},
// ------------------ TSIG keys ---------------------
-//
// Each key has a name, an algorithm (HMAC-MD5, HMAC-SHA1, HMAC-SHA224...)
// and a base-64 encoded shared secret.
-//
"tsig-keys":
[
{
// This file may be used a template for constructing DHCP-DDNS JSON
// configuration.
-//
// It must start with a left-curly-bracket.
{
"DhcpDdns" :
{
-//
// -------------- Global Parameters ----------------
-//
// All of the global parameters have default values as shown. If these
// are satisfactory you may omit them.
-//
// "ip-address" : "127.0.0.1",
// "port" : 53001,
// "dns-server-timeout" : 100,
// "ncr-protocol" : "UDP"
// "ncr-format" : "JSON"
-//
// ----------------- Control Socket -----------------
-//
// "control-socket":
// {
// "socket-name": "/tmp/kea-ddns-ctrl-socket"
// },
-//
// ----------------- Forward DDNS ------------------
-//
"forward-ddns" :
{
"ddns-domains" :
]
},
-//
// ----------------- Reverse DDNS ------------------
-//
"reverse-ddns" :
{
"ddns-domains" :
// :
]
},
-//
// ------------------ TSIG keys ---------------------
-//
"tsig-keys" :
[
// {
// PostgreSQL backends do support this mode.
"ip-reservations-unique": true,
- /// Boolean parameter which controls whether host reservations lookup
- /// should be performed before lease lookup. This parameter has effect
- /// only when multi-threading is disabled. When multi-threading is
- /// enabled, host reservations lookup is always performed first to avoid
- /// lease-lookup resource locking.
+ // Boolean parameter which controls whether host reservations lookup
+ // should be performed before lease lookup. This parameter has effect
+ // only when multi-threading is disabled. When multi-threading is
+ // enabled, host reservations lookup is always performed first to avoid
+ // lease-lookup resource locking.
"reservations-lookup-first": true,
// Specifies credentials to access lease database.
// PostgreSQL backends do support this mode.
"ip-reservations-unique": true,
- /// Boolean parameter which controls whether host reservations lookup
- /// should be performed before lease lookup. This parameter has effect
- /// only when multi-threading is disabled. When multi-threading is
- /// enabled, host reservations lookup is always performed first to avoid
- /// lease-lookup resource locking.
+ // Boolean parameter which controls whether host reservations lookup
+ // should be performed before lease lookup. This parameter has effect
+ // only when multi-threading is disabled. When multi-threading is
+ // enabled, host reservations lookup is always performed first to avoid
+ // lease-lookup resource locking.
"reservations-lookup-first": true,
// Specifies credentials to access lease database.
{
"Dhcp4": {
- /* Data for all standard option definitions */
+ /*
+ Data for all standard option definitions
+ */
// Option data defined globally
"option-data": [
/*
"name": "vivso-suboptions"
},
- // Option codes 126-127 are unassigned.
- // Option codes 128-135 are not defined in Kea.
+ // Option codes 126-127 are unassigned.
+ // Option codes 128-135 are not defined in Kea.
/*
0 1
// Option codes 220-221 are not defined in Kea.
// Option codes 222-254 are unassigned
- /* Custom option data */
+ /*
+ Custom option data
+ */
// See "option-def" below for the definitions.
{
"code": 1,
}
],
- /* Custom option definitions */
+ /*
+ Custom option definitions
+ */
// For kea-dhcp4, custom option definitions can be global or in a client
// class.
"option-def": [
],
"subnet4": [
- /* DOCSIS3 option data */
+ /*
+ DOCSIS3 option data
+ */
// Headers are as defined in CL-SP-CANN-DHCP-Reg-I16-200715.
// "space" is required to be explicitly defined as "docsis3-v4"
{
// This is an example configuration file for the DHCPv4 server in Kea.
// The purpose of this example is to showcase how clients can be classified.
-{ "Dhcp4": {
+{ "Dhcp4":
+
+{
// Kea is told to listen on eth0 interface only.
"interfaces-config": {
// The purpose of this example is to showcase how clients can be classified
// with advanced features.
-{ "Dhcp4": {
+{ "Dhcp4":
+
+{
// Kea is told to listen on eth0 interface only.
"interfaces-config": {
// configuration in the database. If this library is not loaded,
// the configuration can be managed directly using available
// tools that work directly with the MySQL database.
- //,{
- // "library": "/usr/local/lib/kea/hooks/libdhcp_cb_cmds.so"
- //}
+ // ,{
+ // "library": "/usr/local/lib/kea/hooks/libdhcp_cb_cmds.so"
+ // }
],
// The following configures logging. It assumes that messages with at
// It is likely that in your network you'll have a mix of regular,
// "plain" subnets and shared networks. It is perfectly valid to mix
// them in the same config file.
- //
+
// This is regular subnet. It's not part of any shared-network.
"subnet4": [
{
// is an alias for client-link-addr-option), remote-id, rfc4649 (which is an
// alias for remote-id, subscriber-id, rfc4580 (which is an alias for
// subscriber-id) and docsis.
- //
+
// Note that the order matters. Methods are attempted one by one in the
// order specified until hardware address is obtained. If you don't care
// which method is used, using 'any' is marginally faster than enumerating
// them all.
- //
+
// If mac-sources are not specified, a default value of 'any' is used.
"mac-sources": [ "client-link-addr-option", "duid", "ipv6-link-local" ],
// support this mode.
"ip-reservations-unique": true,
- /// Boolean parameter which controls whether host reservations lookup
- /// should be performed before lease lookup. This parameter has effect
- /// only when multi-threading is disabled. When multi-threading is
- /// enabled, host reservations lookup is always performed first to avoid
- /// lease-lookup resource locking.
+ // Boolean parameter which controls whether host reservations lookup
+ // should be performed before lease lookup. This parameter has effect
+ // only when multi-threading is disabled. When multi-threading is
+ // enabled, host reservations lookup is always performed first to avoid
+ // lease-lookup resource locking.
"reservations-lookup-first": true,
// Specifies credentials to access lease database.
// support this mode.
"ip-reservations-unique": true,
- /// Boolean parameter which controls whether host reservations lookup
- /// should be performed before lease lookup. This parameter has effect
- /// only when multi-threading is disabled. When multi-threading is
- /// enabled, host reservations lookup is always performed first to avoid
- /// lease-lookup resource locking.
+ // Boolean parameter which controls whether host reservations lookup
+ // should be performed before lease lookup. This parameter has effect
+ // only when multi-threading is disabled. When multi-threading is
+ // enabled, host reservations lookup is always performed first to avoid
+ // lease-lookup resource locking.
"reservations-lookup-first": true,
// Specifies credentials to access lease database.
{
"Dhcp6": {
- /* Data for all standard option definitions */
+ /*
+ Data for all standard option definitions
+ */
// Option data defined globally
"option-data": [
/*
// Option codes 145-65535 are unassigned.
- /* Custom option data */
+ /*
+ Custom option data
+ */
// See "option-def" below for the definitions.
{
"code": 111,
}
],
- /* Custom option definitions */
+ /*
+ Custom option definitions
+ */
// For kea-dhcp6, custom option definitions are always global. Even when
// data for said options is then configured at subnet level.
"option-def": [
],
"subnet6": [
- /* DOCSIS3 option data */
+ /*
+ DOCSIS3 option data
+ */
// Headers are as defined in CL-SP-CANN-DHCP-Reg-I16-200715.
// "space" is required to be explicitly defined as "docsis3-v6"
{
// configuration in the database. If this library is not loaded,
// the configuration can be managed directly using available
// tools that work directly with the MySQL database.
- //,{
- // "library": "/usr/local/lib/kea/hooks/libdhcp_cb_cmds.so"
- //}
+ // ,{
+ // "library": "/usr/local/lib/kea/hooks/libdhcp_cb_cmds.so"
+ // }
],
// The following configures logging. It assumes that messages with at
// substring(option dhcp6.user-class, 2, 4) = "iPXE" {
// option dhcp6.bootfile-url "http://[2001:db8::1]/ubuntu.cfg";
// }
-//
+
// In example shown below incoming packet will receive value
// http://[2001:db8::1]/ubuntu.cfg if incoming packet will include user
// class option with "iPXE" in it and value http://[2001:db8::1]/ipxe.efi
// Expression can be specified either as hex or plain text using single
// quotes.
// Note: flexible identifier requires flex_id hook library to be
-//loaded to work.
+// loaded to work.
{
"flex-id": "'somevalue'",
"ip-addresses": [ "2001:db8:1:cafe::2" ]
// It is likely that in your network you'll have a mix of regular,
// "plain" subnets and shared networks. It is perfectly valid to mix
// them in the same config file.
- //
+
// This is regular subnet. It's not part of any shared-network.
"subnet6": [
{
// values inserted by relays, which are only used for
// remote traffic. A shared network cannot be both direct
// and relayed.
- //"interface-id": "content of the option",
+ // "interface-id": "content of the option",
// Other parameters defined here will be inherited by the
// subnets.
// Netconf is able to load hook libraries that augment its operation.
// The primary functionality is the ability to add new commands.
- //
+
// Uncomment this section to load a hook library.
- //
+
// "hooks-libraries": [
// // Hook libraries list may contain more than one library.
// {
// // The only necessary parameter is the library filename.
// "library": "/opt/local/netconf-commands.so",
- //
+
// // Some libraries may support parameters. Make sure you
// // type this section carefully, as the CA does not validate
// // it (because the format is library-specific).
// "param1": "foo"
// }
// }
- //]
+ // ]
// Similar to other Kea components, Netconf also uses logging.
"loggers": [
"name": "kea-netconf",
"output_options": [
{
- //"output": "/var/log/kea-netconf.log",
+ // "output": "/var/log/kea-netconf.log",
"output": "stdout",
// Several additional parameters are possible in addition
// to the typical output. Flush determines whether logger
// Eventually, the kea-netconf will be able to handle multiple
// models. However, for the time being the choices for
// DHCPv6 server are kea-dhcp6-server and
- /// ietf-dhcpv6-server models but only the first is usable.
+ // ietf-dhcpv6-server models but only the first is usable.
"model": "kea-dhcp6-server",
// The three control flags can be defined in this scope too
// Netconf is able to load hook libraries that augment its operation.
// The primary functionality is the ability to add new commands.
- //
+
// Uncomment this section to load a hook library.
- //
+
// "hooks-libraries": [
// // Hook libraries list may contain more than one library.
// {
// // The only necessary parameter is the library filename.
// "library": "/opt/local/netconf-commands.so",
- //
+
// // Some libraries may support parameters. Make sure you
// // type this section carefully, as the CA does not validate
// // it (because the format is library-specific).
// "param1": "foo"
// }
// }
- //]
+ // ]
// Similar to other Kea components, Netconf also uses logging.
"loggers": [
"name": "kea-netconf",
"output_options": [
{
- //"output": "/var/log/kea-netconf.log",
+ // "output": "/var/log/kea-netconf.log",
"output": "stdout",
// Several additional parameters are possible in addition
// to the typical output. Flush determines whether logger
"client-class": "Client_foo"
},
...
- ],,
+ ],
...
}
]
},
...
- ],,
-
+ ],
+ ...
}
The following example shows how to restrict access to an address pool. This
::
+ {
"subnet4": [ {
"id": 1,
"subnet": "10.20.30.0/24",
"user-context": {
- "building": "Main"
+ "building": "Main",
"floor": 1
}
} ]
+ }
The same can be done with many other commands like lease6-add etc.
"ip-address": "192.0.2.1",
"state": 0,
"subnet-id": 44,
- "valid-lft": 3600
+ "valid-lft": 3600,
"user-context": {
"ISC": {
"relays": [
}
}
}
+ }
User context can store configuration for multiple hooks and comments at once.
::
+ {
"subnet4": [
{
"id": 123,
"subnet": "10.0.0.0/8"
}
],
+ ...
+ }
::
"dhcp-queue-control": {
- "enable-queue": true|false,
+ "enable-queue": true, // true|false
"queue-type": "queue type",
- "capacity" : n
+ "capacity" : 256 // n packets
}
where:
"Dhcp4":
{
- ...
- "dhcp-queue-control": {
+ "dhcp-queue-control": {
"enable-queue": true,
"queue-type": "kea-ring4",
"capacity" : 250
"Dhcp6":
{
- ...
- "dhcp-queue-control": {
+ "dhcp-queue-control": {
"enable-queue": true,
"queue-type": "kea-ring6",
"capacity" : 300
{
"command": "foo",
- "service": [ "dhcp4" ]
+ "service": [ "dhcp4" ],
"arguments": {
"param1": "value1",
"param2": "value2",
Content-Length: 147\r\n\r\n
{
"command": "foo",
- "service": [ "dhcp4" ]
+ "service": [ "dhcp4" ],
"arguments": {
"param1": "value1",
"param2": "value2",
}
}
-``command`` is the name of the command to execute and is mandatory.
-``arguments`` is a map of the parameters required to carry out the given
+The ``command`` is the name of the command to execute and is mandatory.
+The ``arguments`` is a map of the parameters required to carry out the given
command. The exact content and format of the map are command-specific.
``service`` is a list of the servers at which the control command is
::
{
- "result": 0|1|2|3|4,
+ "result": 0, // 0|1|2|3|4
"text": "textual description",
"arguments": {
"argument1": "value1",
{
"command": "foo",
// service is a list
- "service": [ "dhcp4" ]
+ "service": [ "dhcp4" ],
# command arguments are here.
"arguments": {
- "param1": "value1"/*,
+ "param1": "value1",
+ ...
+ /*
"param2": "value2",
- ...*/
+ ...
+ */
}
}
[
{
- "result": 0|1|2|3|4,
+ "result": 0, // 0|1|2|3|4
"text": "textual description",
"arguments": {
"argument1": "value1",
"argument2": "value2",
- ...
+ ...
+ }
}
]
[
{
- "result": 0|1|2|3|4,
+ "result": 0, // 0|1|2|3|4
"text": "textual description",
"arguments": {
"argument1": "value1",
"argument2": "value2",
- ...
+ ...
+ }
},
{
- "result": 0|1|2|3|4,
+ "result": 0, // 0|1|2|3|4
"text": "textual description",
"arguments": {
"argument1": "value1",
"argument2": "value2",
- ...
+ ...
+ }
},
...
]
"command": "config-test",
"arguments": {
"Dhcp6": {
- :
+ ...
}
}
}
"command": "config-set",
"arguments": {
"Dhcp6": {
- :
+ ...
}
}
}
::
{
- "command": "shutdown"
+ "command": "shutdown",
"arguments": {
"exit-value": 3
}
::
{
- "command": "shutdown"
+ "command": "shutdown",
"arguments": {
"exit-value": 3,
"type": "drain_first"
"ip-address": "192.168.1.10",
"port": 900,
...
- }
}
.. warning::
::
{
- "command": "shutdown"
+ "command": "shutdown",
"arguments": {
"exit-value": 3,
"type": "drain_first"
"DhcpDdns": {
"reverse-ddns": {
"ddns-domains": [ ]
- }
+ },
...
}
],
"user-context": { "backup": false }
},
-
+ ...
]
}
}
{ "ip-address": "172.16.1.5" },
{ "ip-address": "172.16.2.5" }
]
- }
+ },
{
"name": "1.0.0.0.8.B.D.0.1.0.0.2.ip6.arpa.",
"key-name": "",
"dns-servers": [
{ "ip-address": "2001:db8::1" }
]
- }
+ },
{
"name": "0.192.in-addr.arpa.",
"key-name": "",
"dns-servers": [
{ "ip-address": "172.16.2.5" }
]
- }
+ },
+ ...
]
}
}
::
+ {
"interfaces-config": {
"interfaces": [ "eth0", "eth1" ]
},
+ ...
+ }
The next lines define the lease database, the place where the
server stores its lease information. This particular example tells the
::
+ {
"subnet4": [
{
"pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
"pools": [ { "pool": "192.0.4.1 - 192.0.4.254" } ],
"subnet": "192.0.4.0/24"
}
- ]
+ ],
+ ...
+ }
Note that indentation is optional and is used for aesthetic purposes
only. In some cases it may be preferable to use more compact notation.
::
- "Dhcp4": { "lease-database": { "user": "user-name",
- "password": "password",
- ... },
- ... }
+ "Dhcp4": {
+ "lease-database": {
+ "user": "user-name",
+ "password": "password",
+ ...
+ },
+ ...
+ }
If there is no password to the account, set the password to the empty
string ``""``. (This is the default.)
::
- "Dhcp4": { "hosts-database": { "user": "user-name",
- "password": "password",
- ... },
- ... }
+ "Dhcp4": {
+ "hosts-database": {
+ "user": "user-name",
+ "password": "password",
+ ...
+ },
+ ...
+ }
If there is no password to the account, set the password to the empty
string ``""``. (This is the default.)
"Dhcp4": {
"interfaces-config": {
"interfaces": [ "*" ]
- }
+ },
...
}
"record-types": "",
"space": "dhcp4",
"encapsulate": ""
- }, ...
+ },
+ ...
],
...
}
"space": "dhcp4",
"csv-format": true,
"data": "12345"
- }, ...
+ },
+ ...
],
...
}
"array": false,
"record-types": "ipv4-address, uint16, boolean, string",
"encapsulate": ""
- }, ...
+ },
+ ...
],
...
}
...
}
-``csv-format`` is set to ``true`` to indicate that the ``data`` field
+The ``csv-format`` is set to ``true`` to indicate that the ``data`` field
comprises a comma-separated list of values. The values in ``data``
must correspond to the types set in the ``record-types`` field of the
option definition.
"array": true,
"record-types": "ipv4-address, uint16",
"encapsulate": ""
- }, ...
+ },
+ ...
],
...
}
}
],
...
- }, ...
+ },
+ ...
],
...
}
...
}
-(Note that the option space is set to
-``"vendor-encapsulated-options-space"``.) Once the option format is defined,
-the next step is to define actual values for that option:
+Note that the option space is set to ``"vendor-encapsulated-options-space"``.
+Once the option format is defined, the next step is to define actual values
+for that option:
::
"Dhcp4": {
"option-def": [
- ...,
{
"name": "container",
"code": 222,
"array": false,
"record-types": "",
"encapsulate": "isc"
- }
+ },
+ ...
],
...
}
::
+ {
"option-def": [
{
"array": false,
}
]
}
- ]
+ ],
+ ...
+ }
.. note::
"Dhcp4": {
"subnet4": [
{
- "subnet": "192.0.2.0/24"
+ "subnet": "192.0.2.0/24",
"option-data": [ {
"name": "domain-name-servers",
"code": 6,
...
],
...
- }
+ }
If an incoming packet is matched to multiple classes, then the
value used for each field will come from the first class that
"ddns-update-on-renew": false,
"ddns-use-conflict-resolution": true,
"hostname-char-set": "",
- "hostname-char-replacement": ""
+ "hostname-char-replacement": "",
...
}
to generate DNS removal requests to D2.
The DNS entries Kea creates contain a value for TTL (time to live).
-``kea-dhcp4`` calculates that value based on
+The ``kea-dhcp4`` calculates that value based on
`RFC 4702, Section 5 <https://tools.ietf.org/html/rfc4702#section-5>`__,
which suggests that the TTL value be 1/3 of the lease's lifetime, with
a minimum value of 10 minutes.
When Does the ``kea-dhcp4`` Server Generate a DDNS Request?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-``kea-dhcp4`` follows the behavior prescribed for DHCP servers in `RFC
+The ``kea-dhcp4`` follows the behavior prescribed for DHCP servers in `RFC
4702 <https://tools.ietf.org/html/rfc4702>`__. It is important to keep in
mind that ``kea-dhcp4`` makes the initial decision of when and what to
update and forwards that information to D2 in the form of NCRs. Carrying
::
"Dhcp4": {
- ...
"ddns-override-client-update": true,
...
}
::
"Dhcp4": {
- ...
"ddns-override-no-update": true,
...
}
-``kea-dhcp4`` always generates DDNS update requests if the client
+The ``kea-dhcp4`` always generates DDNS update requests if the client
request only contains the Host Name option. In addition, it includes
an FQDN option in the response to the client with the FQDN N-S-O flags
set to 0-1-0, respectively. The domain name portion of the FQDN option
::
"Dhcp4": {
- ...
"ddns-replace-client-name": "always",
...
}
::
"Dhcp4": {
- ...
"ddns-generated-prefix": "another.host",
...
}
::
"Dhcp4": {
- ...
"ddns-qualifying-suffix": "foo.example.org",
...
}
::
"Dhcp4": {
- ...
"hostname-char-set": "[^A-Za-z0-9.-]",
"hostname-char-replacement": "x",
...
"Dhcp4": {
"next-server": "192.0.2.123",
"boot-file-name": "/dev/null",
- ...,
"subnet4": [
{
"next-server": "192.0.2.234",
"boot-file-name": "bootfile.efi",
...
}
- ]
+ ],
+ ...
}
.. _dhcp4-echo-client-id:
::
- { "ISC": { "relay-agent-info": {
- "sub-options": "0x02030102030C03AABBCC",
- "remote-id": "03010203",
- "relay-id": "AABBCC"
- } } }
+ {
+ "ISC": {
+ "relay-agent-info": {
+ "sub-options": "0x02030102030C03AABBCC",
+ "remote-id": "03010203",
+ "relay-id": "AABBCC"
+ }
+ }
+ }
.. note::
"enable-multi-threading": true,
"thread-pool-size": 4,
"packet-queue-size": 16
- }
+ },
...
}
::
+ {
"subnet4": [
{
"pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
]
}
],
+ ...
+ }
Lease Caching
-------------
::
+ {
"subnet4": [
{
"pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
...
}
],
+ ...
+ }
When an already-assigned lease can fulfill a client query:
::
+ {
"subnet4": [
{
"pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
...
}
],
+ ...
+ }
Here ``offer-lifetime`` has been configured to be 60 seconds, with a ``valid-lifetime``
of 2000 seconds. This instructs ``kea-dhcp4`` to persist leases for 60 seconds when
::
+ {
"subnet4": [
{
"pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
}
]
}
- ]
+ ],
+ ...
+ }
The first entry reserves the 192.0.2.202 address for the client that
uses a MAC address of 1a:1b:1c:1d:1e:1f. The second entry reserves the
::
+ {
"host-reservation-identifiers": [ "circuit-id", "hw-address", "duid", "client-id" ],
"subnet4": [
{
"subnet": "192.0.2.0/24",
...
}
- ]
+ ],
+ ...
+ }
If not specified, the default value is:
"subnet4": [
{
"subnet": "192.0.2.0/24",
- "reservations": [{"
+ "reservations": [{
"hw-address": "aa:bb:cc:dd:ee:fe",
"client-classes": [ "reserved_class" ]
}],
"name": "reserved_class"
},
{
- "name: "unreserved_class",
+ "name": "unreserved_class",
"test": "not member('reserved_class')"
}
],
- "reservations": [{"
+ "reservations": [{
"hw-address": "aa:bb:cc:dd:ee:fe",
"client-classes": [ "reserved_class" ]
}],
"data": "192.0.2.251"
}
]
- },
+ }
],
"reservations": [
// Clients on this list will be added to the KNOWN class. Some
::
- {
"Dhcp4": {
- "shared-networks": [
- {
+ "shared-networks": [ {
# Name of the shared network. It may be an arbitrary string
# and it must be unique among all shared networks.
"name": "my-secret-lair-level-1",
"pools": [ { "pool": "192.0.2.100 - 192.0.2.199" } ]
}
]
- } ], # end of shared-networks
+ } ],
+ # end of shared-networks
# It is likely that in the network there will be a mix of regular,
# "plain" subnets and shared networks. It is perfectly valid to mix
"interface": "eth1"
}
]
-
- } # end of Dhcp4
}
As demonstrated in the example, it is possible to mix shared and regular
::
+ {
"shared-networks": [
{
"name": "lab-network3",
} ]
}
]
- } ]
+ }
+ ],
+ ...
+ }
In this example, there is a ``log-servers`` option defined that is available
to clients in both subnets in this shared network. Also, the valid
::
+ {
"shared-networks": [
{
"name": "office-floor-2",
"interface": "eth1"
}
]
- } ]
+ }
+ ],
+ ...
+ }
To minimize the chance of configuration errors, it is often more convenient
to simply specify the interface name once, at the shared-network level, as
::
+ {
"shared-networks": [
{
"name": "office-floor-2",
"pools": [ { "pool": "192.0.2.100 - 192.0.2.199" } ]
}
]
- } ]
+ }
+ ],
+ ...
+ }
With relayed traffic, subnets are typically selected using
::
+ {
"shared-networks": [
{
"name": "kakapo",
}
]
}
- ]
+ ],
+ ...
+ }
Again, it is better to specify the relay address at the shared-network
level; this value will be inherited by all subnets belonging to the
::
+ {
"shared-networks": [
{
"name": "kakapo",
}
]
}
- ]
+ ],
+ ...
+ }
Even though it is technically possible to configure two (or more) subnets
within the shared network to use different relay addresses, this will almost
::
+ {
"subnet4": [
{
"subnet": "192.0.2.0/24",
],
...
}
- ]
+ ],
+ ...
+ }
.. _dhcp4-subnet-selection:
{
"subnet": "10.1.1.0/24",
"pools": [ { "pool": "10.1.1.2 - 10.1.1.20" } ],
- "client-class" "docsis3.0",
+ "client-class": "docsis3.0",
"relay": {
- "ip-addresses": [ "10.1.1.1 ]"
+ "ip-addresses": [ "10.1.1.1" ]
}
},
{
::
- "Dhcp4": {
+ "Dhcp4": {
"decline-probation-period": 3600,
- "subnet4": [ ... ],
+ "subnet4": [
+ {
+ ...
+ },
+ ...
+ ],
...
}
::
- "Dhcp4": {
+ "Dhcp4": {
"statistic-default-sample-count": 1,
- "subnet4": [ ... ],
+ "subnet4": [
+ {
+ ...
+ },
+ ...
+ ],
...
}
},
"subnet4": [
+ {
+ ...
+ },
...
],
...
},
{
"id": 2,
- "subnet": "192.0.2.0/24",
+ "subnet": "192.0.2.0/24"
}
]
}
::
+ {
"interfaces-config": {
"interfaces": [ "eth0", "eth1" ]
},
+ ...
+ }
The next lines define the lease database, the place where the
server stores its lease information. This particular example tells the
::
+ {
"subnet6": [
{
"pools": [ { "pool": "2001:db8:1::/112" } ],
"pools": [ { "pool": "2001:db8:2::1-2001:db8:2::ffff" } ],
"subnet": "2001:db8:2::/64"
}
- ]
+ ],
+ ...
+ }
Note that indentation is optional and is used for aesthetic purposes
only. In some cases it may be preferable to use more compact notation.
::
- "Dhcp6": { "lease-database": { "user": "user-name",
- "password": "password",
- ... },
- ... }
+ "Dhcp6": {
+ "lease-database": {
+ "user": "user-name",
+ "password": "password",
+ ...
+ },
+ ...
+ }
If there is no password to the account, set the password to the empty
string ``""``. (This is the default.)
::
- "Dhcp6": { "hosts-database": { "user": "user-name",
- "password": "password",
- ... },
- ... }
+ "Dhcp6": {
+ "hosts-database": {
+ "user": "user-name",
+ "password": "password",
+ ...
+ },
+ ...
+ }
If there is no password to the account, set the password to the empty
string ``""``. (This is the default.)
"Dhcp6": {
"interfaces-config": {
"interfaces": [ "*" ]
- }
+ },
...
}
"interfaces-config": {
"interfaces": [ "eth1/2001:db8::1" ]
},
- ...
"option-data": [
{
"name": "unicast",
{ "pool": "2001:db8:2::/64" }
]
},
-
- ...
+ ...
]
}
::
"Dhcp6": {
- ...
"option-data": [
{
"name": "s46-cont-mape"
"record-types": "",
"space": "dhcp6",
"encapsulate": ""
- }, ...
+ },
+ ...
],
...
}
"space": "dhcp6",
"csv-format": true,
"data": "12345"
- }, ...
+ },
+ ...
],
...
}
"array": false,
"record-types": "ipv6-address, uint16, boolean, string",
"encapsulate": ""
- }, ...
+ },
+ ...
],
...
}
...
}
-``csv-format`` is set to ``true`` to indicate that the ``data`` field
+The ``csv-format`` is set to ``true`` to indicate that the ``data`` field
comprises a comma-separated list of values. The values in ``data``
must correspond to the types set in the ``record-types`` field of the
option definition.
"array": true,
"record-types": "ipv6-address, uint16",
"encapsulate": ""
- }, ...
+ },
+ ...
],
...
}
...
}
-(Note that the option space is set to ``"vendor-12345"``.) Once the
-option format is defined, the next step is to define actual values for
-that option:
+Note that the option space is set to ``"vendor-12345"``.
+Once the option format is defined, the next step is to define actual values
+for that option:
::
"Dhcp6": {
"option-data": [
- ...,
{
"name": "vendor-opts",
"data": "12345"
- }
+ },
+ ...
],
...
}
"Dhcp6": {
"option-data": [
- ...,
{
"code": 17,
"data": "12345"
- }
+ },
+ ...
],
...
}
"space": "isc",
"type": "string",
"record-types": "",
- "array": false
+ "array": false,
"encapsulate": ""
}
],
"Dhcp6": {
"option-def": [
- ...,
{
"name": "container",
"code": 102,
"array": false,
"record-types": "",
"encapsulate": "isc"
- }
+ },
+ ...
],
...
}
],
"subnet6": [
{
- "subnet": "2001:db8:1::/64"
+ "subnet": "2001:db8:1::/64",
"pools": [
{
"pool": "2001:db8:1::-2001:db8:1::ffff"
}
- ],
+ ],
"require-client-classes": [ "Client_foo" ],
...
},
"ddns-update-on-renew": false,
"ddns-use-conflict-resolution": true,
"hostname-char-set": "",
- "hostname-char-replacement": ""
+ "hostname-char-replacement": "",
...
}
When Does the ``kea-dhcp6`` Server Generate a DDNS Request?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-``kea-dhcp6`` follows the behavior prescribed for DHCP servers in `RFC
+The ``kea-dhcp6`` follows the behavior prescribed for DHCP servers in `RFC
4704 <https://tools.ietf.org/html/rfc4704>`__. It is important to keep in
mind that ``kea-dhcp6`` makes the initial decision of when and what to
update and forwards that information to D2 in the form of NCRs. Carrying
::
"Dhcp6": {
- ...
"ddns-override-client-update": true,
...
}
::
"Dhcp6": {
- ...
"ddns-override-no-update": true,
...
}
+The ``kea-dhcp6`` always generates DDNS update requests if the client
+request only contains the Host Name option. In addition, it includes
+an FQDN option in the response to the client with the FQDN N-S-O flags
+set to 0-1-0, respectively. The domain name portion of the FQDN option
+is the name submitted to D2 in the DDNS update request.
+
.. _dhcpv6-fqdn-name-generation:
``kea-dhcp6`` Name Generation for DDNS Update Requests
::
"Dhcp6": {
- ...
"ddns-replace-client-name": "always",
...
}
::
"Dhcp6": {
- ...
"ddns-generated-prefix": "another.host",
...
}
::
"Dhcp6": {
- ...
"ddns-qualifying-suffix": "foo.example.org",
...
}
::
"Dhcp6": {
- ...
"hostname-char-set": "[^A-Za-z0-9.-]",
"hostname-char-replacement": "x",
...
"peer": "2001:db8::4"
},
{
- "hop": 1",
+ "hop": 1,
"link": "2001:db8::5",
"options": "0x00250006010203040506003500086464646464646464",
"remote-id": "010203040506",
"enable-multi-threading": true,
"thread-pool-size": 4,
"packet-queue-size": 16
- }
+ },
...
}
::
+ {
"subnet6": [
{
"subnet": "2001:db8:1:1::/64",
...
}
],
+ ...
+ }
When an already-assigned lease can fulfill a client query:
::
+ {
"subnet6": [
{
"subnet": "2001:db8:1::/48",
}
]
}
- ]
+ ],
+ ...
+ }
This example includes reservations for three different clients. The
first reservation is for the address 2001:db8:1::100, for a client using
::
+ {
"subnet6": [
{
"subnet": "2001:db8:1::/48",
],
"dhcp-ddns": {
"enable-updates": true
+ },
+ ...
}
will result the "alice-laptop.example.isc.org." hostname being assigned to
::
+ {
"reservations": [
{
- "duid": "01:02:03:05:06:07:08",
- "ip-addresses": [ "2001:db8:1::2" ],
+ "duid": "01:02:03:05:06:07:08",
+ "ip-addresses": [ "2001:db8:1::2" ],
"option-data": [
{
- "option-data": [ {
- "name": "dns-servers",
- "data": "3000:1::234"
- },
- {
- "name": "nis-servers",
- "data": "3000:1::234"
- }
- } ]
- } ]
+ "name": "dns-servers",
+ "data": "3000:1::234"
+ },
+ {
+ "name": "nis-servers",
+ "data": "3000:1::234"
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
Vendor-specific options can be reserved in a similar manner:
::
+ {
"reservations": [
{
"duid": "aa:bb:cc:dd:ee:ff",
"name": "tftp-servers",
"space": "vendor-4491",
"data": "3000:1::234"
- } ]
- } ]
+ },
+ ...
+ ],
+ ...
+ },
+ ...
+ ],
+ ...
+ }
Options defined at the host level have the highest priority. In other words,
if there are options defined with the same type on global, subnet,
::
+ {
"host-reservation-identifiers": [ "duid", "hw-address" ],
"subnet6": [
{
"subnet": "2001:db8:1::/64",
...
}
- ]
+ ],
+ ...
+ }
If not specified, the default value is:
"subnet6": [
{
"subnet": "2001:db8:1::/64",
- "reservations": [{"
+ "reservations": [{
"hw-address": "aa:bb:cc:dd:ee:fe",
"client-classes": [ "reserved_class" ]
}],
"name": "reserved_class"
},
{
- "name: "unreserved_class",
+ "name": "unreserved_class",
"test": "not member('reserved_class')"
}
],
- "reservations": [{"
+ "reservations": [{
"hw-address": "aa:bb:cc:dd:ee:fe",
"client-classes": [ "reserved_class" ]
}],
"data": "2001:db8::2"
}
]
- },
+ }
],
"reservations": [
// Clients on this list will be added to the KNOWN class. Some
::
"Dhcp6": {
- "shared-networks": [{
+ "shared-networks": [ {
# Name of the shared network. It may be an arbitrary string
# and it must be unique among all shared networks.
"name": "ipv6-lab-1",
# This starts a list of subnets in this shared network.
# There are two subnets in this example.
- "subnet6": [{
- "subnet": "2001:db8::/48",
- "pools": [{ "pool": "2001:db8::1 - 2001:db8::ffff" }]
- }, {
- "subnet": "3ffe:ffe::/64",
- "pools": [{ "pool": "3ffe:ffe::/64" }]
- }]
- }], # end of shared-networks
+ "subnet6": [
+ {
+ "subnet": "2001:db8::/48",
+ "pools": [{ "pool": "2001:db8::1 - 2001:db8::ffff" }]
+ },
+ {
+ "subnet": "3ffe:ffe::/64",
+ "pools": [{ "pool": "3ffe:ffe::/64" }]
+ }
+ ]
+ } ],
+ # end of shared-networks
# It is likely that in the network there will be a mix of regular,
# "plain" subnets and shared networks. It is perfectly valid
# to mix them in the same configuration file.
#
# This is a regular subnet. It is not part of any shared-network.
- "subnet6": [{
- "subnet": "2001:db9::/48",
- "pools": [{ "pool": "2001:db9::/64" }],
- "relay": {
- "ip-addresses": [ "2001:db8:1:2::1" ]
+ "subnet6": [
+ {
+ "subnet": "2001:db9::/48",
+ "pools": [{ "pool": "2001:db9::/64" }],
+ "relay": {
+ "ip-addresses": [ "2001:db8:1:2::1" ]
+ }
}
- }]
- } # end of Dhcp6
+ ]
+ }
As demonstrated in the example, it is possible to mix shared and regular
("plain") subnets. Each shared network must have a unique name. This is
::
+ {
"shared-networks": [
{
"name": "lab-network3",
} ]
}
]
- } ]
+ }
+ ],
+ ...
+ }
In this example, there is a ``dns-servers`` option defined that is available
to clients in both subnets in this shared network. Also, the valid
::
+ {
"shared-networks": [
{
"name": "office-floor-2",
{
"subnet": "3ffe:abcd::/64",
"pools": [ { "pool": "3ffe:abcd::1 - 3ffe:abcd::ffff" } ],
-
- # Specifying the different interface name is a configuration
+ ...
+ # Specifying a different interface name is a configuration
# error. This value should rather be "eth0" or the interface
# name in the other subnet should be "eth1".
# "interface": "eth1"
}
]
- } ]
+ }
+ ],
+ ...
+ }
To minimize the chance of configuration errors, it is often more convenient
to simply specify the interface name once, at the shared-network level, as
::
+ {
"shared-networks": [
{
"name": "office-floor-2",
"pools": [ { "pool": "3ffe:abcd::1 - 3ffe:abcd::ffff" } ]
}
]
- } ]
+ }
+ ],
+ ...
+ }
With relayed traffic, subnets are typically selected using
::
+ {
"shared-networks": [
{
"name": "kakapo",
}
]
}
- ]
+ ],
+ ...
+ }
Again, it is better to specify the relay address at the shared-network
level; this value will be inherited by all subnets belonging to the
::
+ {
"shared-networks": [
{
"name": "kakapo",
}
]
}
- ]
+ ],
+ ...
+ }
Even though it is technically possible to configure two (or more) subnets
within the shared network to use different relay addresses, this will almost
{
"subnet": "2001:db8:1::/64",
"id": 100,
- "pools": [ { "2001:db8:1::1 - 2001:db8:1::64" } ],
+ "pools": [ { "pool": "2001:db8:1::1 - 2001:db8:1::64" } ],
"reservations": [
{
"duid": "00:03:00:01:11:22:33:44:55:66",
::
"Dhcp6": {
- "mac-sources": [ "method1", "method2", "method3", ... ],
-
- "subnet6": [ ... ],
+ "mac-sources": [
+ "method1",
+ "method2",
+ "method3",
+ ...
+ ],
+ "subnet6": [
+ {
+ ...
+ },
+ ...
+ ],
...
}
::
- "Dhcp6": {
+ "Dhcp6": {
"decline-probation-period": 3600,
- "subnet6": [ ... ],
+ "subnet6": [
+ {
+ ...
+ },
+ ...
+ ],
...
}
::
- "Dhcp6": {
+ "Dhcp6": {
"statistic-default-sample-count": 1,
- "subnet6": [ ... ],
+ "subnet6": [
+ {
+ ...
+ },
+ ...
+ ],
...
}
},
"subnet6": [
+ {
+ ...
+ },
...
],
...
// store client keys. As credentials cache is more flexible,
// it is recommended to use it. Typically, using both at the
// same time may cause problems.
- //
// "client-keytab": "FILE:/etc/dhcp.keytab", // toplevel only
"credentials-cache": "FILE:/etc/ccache", // toplevel only
"gss-replay-flag": true, // GSS anti replay service
// This map specifies how each server is managed. For each server there
// is a name of the YANG model to be used and the control channel.
- //
// Currently three control channel types are supported:
// "stdout" which outputs the configuration on the standard output,
// "unix" which uses the local control channel supported by the
{
"result": 0,
- "text": "1 DHCPv4 server(s) deleted."
+ "text": "1 DHCPv4 server(s) deleted.",
"arguments": {
"count": 1
}
.. code-block:: json
{
- "command": "remote-server6-get"
+ "command": "remote-server6-get",
"arguments": {
"servers": [
{
.. code-block:: json
{
- "command": "remote-server4-get-all"
+ "command": "remote-server4-get-all",
"arguments": {
"remote": {
"type": "mysql"
.. code-block:: json
{
- "command": "remote-server6-set"
+ "command": "remote-server6-set",
"arguments": {
"servers": [
{
.. code-block:: json
{
- "command": "remote-global-parameter4-set"
+ "command": "remote-global-parameter4-set",
"arguments": {
"parameters": {
"boot-file-name": "/dev/null",
.. code-block:: json
{
- "command": "remote-network4-list"
+ "command": "remote-network4-list",
"arguments": {
"remote": {
"type": "mysql"
.. code-block:: json
{
- "command": "remote-network4-list"
+ "command": "remote-network4-list",
"arguments": {
"remote": {
"type": "mysql"
.. code-block:: json
{
- "command": "remote-option-def4-get"
+ "command": "remote-option-def4-get",
"arguments": {
"option-defs": [
{
.. code-block:: json
{
- "command": "remote-option-def6-get-all"
+ "command": "remote-option-def6-get-all",
"arguments": {
"remote": {
"type": "mysql"
"arguments": {
"options": [
{
- "code": 5
+ "code": 5,
"space": "dhcp4"
}
],
}
}
-"dhcp4" is the top-level option space where the standard DHCPv4 options
+The "dhcp4" is the top-level option space where the standard DHCPv4 options
belong. The ``server-tags`` parameter is mandatory and must include a
single option tag or the keyword "all". If the explicit server tag is specified,
this command attempts to delete a global option associated with this
}
}
-"dhcp4" is the top-level option space where the standard DHCPv4 options
+The "dhcp4" is the top-level option space where the standard DHCPv4 options
belong. The ``server-tags`` parameter cannot be specified for this command.
.. _command-remote-option4-network-set:
}
}
-"dhcp6" is the top-level option space where the standard DHCPv6 options
+The "dhcp6" is the top-level option space where the standard DHCPv6 options
belong. The ``server-tags`` parameter cannot be specified for this command.
.. _command-remote-option6-pd-pool-set:
}
}
-"dhcp4" is the top-level option space where the standard DHCPv4 options
+The "dhcp4" is the top-level option space where the standard DHCPv4 options
belong. The ``server-tags`` parameter cannot be specified for this command.
.. _command-remote-option4-pool-set:
}
}
-"dhcp4" is the top-level option space where the standard DHCPv4 options
+The "dhcp4" is the top-level option space where the standard DHCPv4 options
belong. The ``server-tags`` parameter cannot be specified for this command.
.. _command-remote-option4-subnet-set:
.. code-block:: json
{
- "command": "remote-subnet4-list"
+ "command": "remote-subnet4-list",
"arguments": {
"remote": {
"type": "mysql"
.. code-block:: json
{
- "command": "remote-subnet4-list"
+ "command": "remote-subnet4-list",
"arguments": {
"remote": {
"type": "mysql"
{
"command": "class-del",
"arguments": {
- {
- "name": "ipxe_efi_x64"
- }
+ "name": "ipxe_efi_x64"
}
}
{
"hooks-libraries": [
- :
- ,
{
"library": "/usr/local/lib/libdhcp_ddns_tuning.so",
"parameters": {
- :
+ ...
}
},
- :
- ]
+ ...
+ ],
+ ...
}
Procedural Host-Name Generation
{
"hooks-libraries": [
- :
- ,
{
"library": "/usr/local/lib/libdhcp_ddns_tuning.so",
"parameters": {
- :
- "hostname-expr": "'host-'+hexstring(pkt4.mac,'-')"
+ "hostname-expr": "'host-'+hexstring(pkt4.mac,'-')",
+ ...
}
},
- :
- ]
+ ...
+ ],
+ ...
}
It is also possible to define this parameter in a subnet, using the user-context mechanism.
.. code-block:: javascript
+ {
"subnet4": [{
"subnet": "192.0.2.0/24",
"pools": [{
"devices-registered": 42,
"billing": false
}
- }]
+ }],
+ ...
+ }
.. note::
::
"Dhcp6": {
- "subnet6": [{ ..., # subnet definition starts here
- "reservations": [
- "flex-id": "'port1234'", # value of the first 8 bytes of the interface-id
+ "subnet6": [{
+ # subnet definition starts here
+ "reservations": [{
+ "flex-id": "'port1234'",
+ # value of the first 8 bytes of the interface-id
"ip-addresses": [ "2001:db8::1" ]
+ },
+ ...
],
- }], # end of subnet definitions
- "host-reservation-identifiers": ["duid", "flex-id"], # add "flex-id" to reservation identifiers
+ ...
+ },
+ ...
+ ],
+ # end of subnet definitions
+ "host-reservation-identifiers": ["duid", "flex-id"],
+ # add "flex-id" to reservation identifiers
"hooks-libraries": [
{
"library": "/path/libdhcp_flex_id.so",
}
},
...
- ]
+ ],
+ ...
}
.. note::
::
"Dhcp6": {
- "subnet6": [{ ..., # subnet definition starts here
- "reservations": [
- "flex-id": "01:02:03:04:05:06", # value of the first 8 bytes of the interface-id
+ "subnet6": [{
+ # subnet definition starts here
+ "reservations": [{
+ "flex-id": "01:02:03:04:05:06",
+ # value of the first 8 bytes of the interface-id
"ip-addresses": [ "2001:db8::1" ]
+ },
+ ...
],
- }], # end of subnet definitions
- "host-reservation-identifiers": ["duid", "flex-id"], # add "flex-id" to reservation identifiers
+ ...
+ },
+ ...
+ ],
+ # end of subnet definitions
+ "host-reservation-identifiers": ["duid", "flex-id"],
+ # add "flex-id" to reservation identifiers
"hooks-libraries": [
{
"library": "/path/libdhcp_flex_id.so",
}
},
...
- ]
+ ],
+ ...
}
The ``replace-client-id`` Flag
::
- {
"Dhcp4": {
-
- ...
-
"hooks-libraries": [
{
"library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
}
}
],
-
...
-
}
In the most typical use case, both parameters are set to the same value, i.e.
::
- {
"Dhcp4": {
-
- ...
-
"hooks-libraries": [
{
"library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
}
}
],
-
...
-
}
It is important to note that extending this ``sync-timeout`` value may sometimes
::
"Dhcp4": {
-
- ...
-
"hooks-libraries": [
{
"library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
}
}
],
-
...
-
}
The ``pause`` parameter value ``once`` denotes that the state machine should be
::
"Dhcp4": {
-
- ...
-
"hooks-libraries": [
{
"library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
}
}
],
-
...
-
}
This configuration instructs the server to pause the state machine every time it
::
"Dhcp4": {
-
- ...
"hooks-libraries": [
{
"library": "/usr/lib/kea/hooks/libdhcp_lease_cmds.so",
"parameters": {
"high-availability": [ {
"this-server-name": "server1",
- ...
"multi-threading": {
"enable-multi-threading": true,
"http-dedicated-listener": true,
"http-listener-threads": 4,
"http-client-threads": 4
},
- ...
"peers": [
// This is the configuration of this server instance.
{
// primary as specified in the previous "peers"
// entry and in "this-server-name" before that.
"role": "secondary"
- }
+ },
+ ...
+ ],
...
+ },
+ ...
+ ]
+ }
+ },
+ ...
+ ],
+ ...
+ }
In the example above, HA+MT is enabled with four threads for the listener and
::
"Dhcp6": {
-
- ...
// Limit the number of concurrently parked packets to 128.
"parked-packet-limit": 128,
"hooks-libraries": [
"high-availability": [ {
"this-server-name": "server1",
...
+ } ]
+ }
+ },
+ ...
+ ],
+ ...
+ }
.. note::
"maximum": 1000
}
- } ]
+ } ],
+ ...
+ }
Once loaded, the Host Cache hook library provides a number of new
commands which can be used either over the control channel (see
"hooks-libraries": [
{
"library": "/path/libdhcp_host_cmds.so"
- }
+ },
...
]
}
}
}
-``reservation-get`` typically returns the result 0 when a query was
+The ``reservation-get`` typically returns the result 0 when a query was
conducted properly. In particular, 0 is returned when the host was not
found. If the query was successful, the host parameters are
returned. An example of a query that did not find the host looks as
::
- { "result": 1, "text": "No 'ip-address' provided and 'identifier-type'
- is either missing or not a string." }
+ { "result": 1, "text": "No 'ip-address' provided and 'identifier-type' is either missing or not a string." }
.. _command-reservation-get-all:
"server-hostname": "server-hostname.example.org",
"subnet-id": 1
},
- ...
{
"boot-file-name": "bootfile.efi",
"client-classes": [ ],
"option-data": [ ],
"server-hostname": "server-hostname.example.org",
"subnet-id": 1
- }
+ },
+ ...
]
},
"result": 0,
"option-data": [ ],
"server-hostname": "server-hostname.example.org"
},
- ...
{
"boot-file-name": "bootfile.efi",
"client-classes": [ ],
"next-server": "192.0.0.2",
"option-data": [ ],
"server-hostname": "server-hostname.example.org"
- }
+ },
+ ...
],
"next": {
"from": 1234567,
"hosts": [ ]
},
"result": 3,
- "0 IPv4 host(s) found."
+ "text": "0 IPv4 host(s) found."
}
This command is more complex than ``reservation-get-all``, but lets
"option-data": [ ],
"server-hostname": "server-hostname.example.org"
},
- ...
{
"boot-file-name": "bootfile.efi",
"client-classes": [ ],
"next-server": "192.0.0.2",
"option-data": [ ],
"server-hostname": "server-hostname.example.org"
- }
+ },
+ ...
]
},
"result": 0,
"server-hostname": "server-hostname.example.org",
"subnet-id": 123
},
- ...
{
"boot-file-name": "bootfile.efi",
"client-classes": [ ],
"option-data": [ ],
"server-hostname": "server-hostname.example.org",
"subnet-id": 345
- }
+ },
+ ...
]
},
"result": 0,
{
"command": "reservation-del",
- "arguments":
+ "arguments": {
"subnet-id": 4,
"identifier-type": "hw-address",
"identifier": "01:02:03:04:05:06"
}
}
-``reservation-del`` returns a result of 0 when the host deletion was
+The ``reservation-del`` returns a result of 0 when the host deletion was
successful, or 1 if it failed. Descriptive text is provided in the event of
an error. Here are some examples of possible results:
"text": "Host not deleted (not found)."
}
+or
+
::
{
"text": "Host deleted."
}
+or
+
::
{
"result": 1,
- "text": "Unable to delete a host because there is no hosts-database
- configured."
+ "text": "Unable to delete a host because there is no hosts-database configured."
}
.. _command-reservation-update:
"hooks-libraries": [
{
"library": "/path/libdhcp_lease_cmds.so"
- }
+ },
...
]
}
}
}
-``lease6-add`` can also be used to add leases for IPv6 prefixes. In this
+The ``lease6-add`` can also be used to add leases for IPv6 prefixes. In this
case there are three additional parameters that must be specified:
``subnet-id``, ``type`` (set to "IA_PD"), and prefix length. The actual
prefix is set using the ``ip-address`` field. Note that Kea cannot guess
{
"result": 0,
- "text": "Bulk apply of 2 IPv6 leases completed".
+ "text": "Bulk apply of 2 IPv6 leases completed",
"arguments": {
"failed-deleted-leases": [
{
{
"ip-address": "2001:db8:5::3",
...
- }
+ },
{
"ip-address": "2001:db8:4::1",
...
{
"ip-address": "2001:db8:2::7",
...
- }
-
+ },
+ ...
],
"count": 6
},
}
-``lease4-del`` and ``lease6-del`` return a result that indicates the outcome of the
-operation. It has one of the following values: 0 (success), 1 (error),
-or 3 (empty). The empty result means that a query has been completed
-properly, but the object (a lease, in this case) has not been found.
+The ``lease4-del`` and ``lease6-del`` return a result that indicates the outcome
+of the operation. It has one of the following values: 0 (success), 1 (error),
+or 3 (empty). The empty result means that a query has been completed properly,
+but the object (a lease, in this case) has not been found.
.. _command-lease4-update:
}
}
-``lease4-resend-ddns`` and ``lease6-resend-ddns`` return an indication of the result of the operation.
-It has one of the following values: 0 (success), 1 (error), or 3 (empty). An empty
+The ``lease4-resend-ddns`` and ``lease6-resend-ddns`` return an indication of the
+result of the operation.
+it has one of the following values: 0 (success), 1 (error), or 3 (empty). An empty
result means that a query has been completed properly, but the object (a lease in
this case) has not been found.
::
- :
+ {
"hooks-libraries": [
{
"library": "lib/kea/hooks/libdhcp_lease_query.so",
}
}
],
- :
+ ...
+ }
.. note::
::
- :
+ {
"hooks-libraries": [
{
"library": "lib/kea/hooks/libdhcp_lease_query.so",
}
}
],
- :
+ ...
+ }
.. note::
::
- :
+ {
"hooks-libraries": [
{
"library": "lib/kea/hooks/libdhcp_lease_query.so",
}
}
],
- :
+ ...
+ }
or for DHCPv6:
::
- :
+ {
"hooks-libraries": [
{
"library": "lib/kea/hooks/libdhcp_lease_query.so",
}
}
],
- :
+ ...
+ }
"response-parser-format": "ifelse(pkt4.msgtype == 5, 'Address: ' + addrtotext(pkt4.yiaddr) + ' has been assigned for ' + uint32totext(option[51].hex) + ' seconds to a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + ifelse(option[61].exists, ', client-id: ' + hexstring(option[61].hex, ':'), '') + ifelse(pkt4.giaddr == 0.0.0.0, '', ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + ifelse(option[82].option[1].exists, ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), '') + ifelse(option[82].option[2].exists, ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), '') + ifelse(option[82].option[6].exists, ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), '')), '')"
}
+Details:
+
.. raw:: html
<details><summary>Expand here!</summary>
"request-parser-format": "ifelse(pkt4.msgtype == 3, 'Address: ' + ifelse(option[50].exists, addrtotext(option[50].hex), addrtotext(pkt4.ciaddr)) + ' has been assigned' + ifelse(option[51].exists, ' for ' + uint32totext(option[51].hex) + ' seconds', '') + ' to a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + ifelse(option[61].exists, ', client-id: ' + hexstring(option[61].hex, ':'), '') + ifelse(pkt4.giaddr == 0.0.0.0, '', ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + ifelse(option[82].option[1].exists, ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), '') + ifelse(option[82].option[2].exists, ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), '') + ifelse(option[82].option[6].exists, ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), '')), ifelse(pkt4.msgtype == 4 or pkt4.msgtype == 7, 'Address: ' + ifelse(option[50].exists, addrtotext(option[50].hex), addrtotext(pkt4.ciaddr)) + ' has been released from a device with hardware address: hwtype=' + substring(hexstring(pkt4.htype, ''), 7, 1) + ' ' + hexstring(pkt4.mac, ':') + ifelse(option[61].exists, ', client-id: ' + hexstring(option[61].hex, ':'), '') + ifelse(pkt4.giaddr == 0.0.0.0, '', ' connected via relay at address: ' + addrtotext(pkt4.giaddr) + ifelse(option[82].option[1].exists, ', circuit-id: ' + hexstring(option[82].option[1].hex, ':'), '') + ifelse(option[82].option[2].exists, ', remote-id: ' + hexstring(option[82].option[2].hex, ':'), '') + ifelse(option[82].option[6].exists, ', subscriber-id: ' + hexstring(option[82].option[6].hex, ':'), '')), ''))"
}
+Details:
+
.. raw:: html
<details><summary>Expand here!</summary>
"response-parser-format": "ifelse(pkt6.msgtype == 7, ifelse(option[3].option[5].exists and not (substring(option[3].option[5].hex, 20, 4) == 0), 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been assigned for ' + uint32totext(substring(option[3].option[5].hex, 20, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), '') + ifelse(option[25].option[26].exists and not (substring(option[25].option[26].hex, 4, 4) == 0), 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been assigned for ' + uint32totext(substring(option[25].option[26].hex, 4, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), ''), '')"
}
+Details:
+
.. raw:: html
<details><summary>Expand here!</summary>
"request-parser-format": "ifelse(pkt6.msgtype == 3 or pkt6.msgtype == 5 or pkt6.msgtype == 6, ifelse(option[3].option[5].exists, 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been assigned for ' + uint32totext(substring(option[3].option[5].hex, 20, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), '') + ifelse(option[25].option[26].exists, 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been assigned for ' + uint32totext(substring(option[25].option[26].hex, 4, 4)) + ' seconds to a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), ''), ifelse(pkt6.msgtype == 8 or pkt6.msgtype == 9, ifelse(option[3].option[5].exists, 'Address: ' + addrtotext(substring(option[3].option[5].hex, 0, 16)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), '') + ifelse(option[25].option[26].exists, 'Prefix: ' + addrtotext(substring(option[25].option[26].hex, 9, 16)) + '/' + uint8totext(substring(option[25].option[26].hex, 8, 1)) + ' has been released from a device with DUID: ' + hexstring(option[1].hex, ':') + ifelse(relay6[0].peeraddr == '', '', ' connected via relay at address: ' + addrtotext(relay6[0].peeraddr) + ' for client on link address: ' + addrtotext(relay6[0].linkaddr) + ifelse(relay6[0].option[37].exists, ', remote-id: ' + hexstring(relay6[0].option[37].hex, ':'), '') + ifelse(relay6[0].option[38].exists, ', subscriber-id: ' + hexstring(relay6[0].option[38].hex, ':'), '') + ifelse(relay6[0].option[18].exists, ', connected at location interface-id: ' + hexstring(relay6[0].option[18].hex, ':'), '')), ''), ''))"
}
+Details:
+
.. raw:: html
<details><summary>Expand here!</summary>
# Specify which address to use to communicate with RADIUS servers
"bindaddr": "*",
-
+ ...
# more RADIUS parameters here
}
- } ]
+ } ],
+ ...
+ }
RADIUS is a complicated environment. As such, it is not feasible
to provide a default configuration that works for everyone.
.. code-block:: javascript
- ...
+ {
"commands": [
{
"name": "my-new-command",
"access": "write",
"hook": "my-custom-hook"
}
- ]
+ ],
+ ...
+ }
The new command can then be specified in ``roles``:
.. code-block:: javascript
- ...
+ {
"roles": [
{
"name": "user1",
},
{
"name": "user2",
- "accept-commands": { "hook": "my-custom-hook" }
+ "accept-commands": { "hook": "my-custom-hook" },
"reject-commands": "ALL",
"list-match-first": "accept"
}
- ]
+ ],
+ ...
+ }
The second method is to create a custom file in ``.../share/kea/api`` and define
the access type of the custom command(s).
.. code-block:: javascript
- ...
+ {
"commands": [
{
"name": "dhcp-disable",
"hook": "my-custom-hook-3"
}
]
+ }
With this approach, an administrator can put the configurations of all existing
commands inside the Control Agent's configuration file.
.. code-block:: javascript
- ...
+ {
"roles": [
{
"name": "user1",
// This is the default but as the config relies on it
// it is explicitly set.
"list-match-first": "accept"
- },
- ...
- ],
- ...
+ },
+ ...
+ ],
+ ...
+ }
A common alternative is not to set the "reject-commands" list, i.e. leave
it empty and rely on "other-commands" to reject anything else.
.. code-block:: javascript
- ...
+ {
"roles": [
{
"name": "user2",
// This is the default but as the config relies on it
// it is explicitly set.
"other-commands": "reject"
- },
- ...
- ],
- ...
+ },
+ ...
+ ],
+ ...
+ }
It is also possible to do the opposite, i.e. to set only the "reject-commands" list:
.. code-block:: javascript
- ...
+ {
"roles": [
{
"name": "user3",
]
},
"other-commands": "accept"
- },
- ...
- ],
- ...
+ },
+ ...
+ ],
+ ...
+ }
Or use both lists with the exception in the "reject-commands" list,
which must be checked first as "config-get" has the read-access right.
.. code-block:: javascript
- ...
+ {
"roles": [
{
"name": "user4",
"accept-commands": "READ",
"reject-commands": { "commands": [ "config-get" ] },
"list-match-first": "reject"
- },
- ...
- ],
- ...
+ },
+ ...
+ ],
+ ...
+ }
To check any configuration, it is a good idea to use the "list-commands"
response filter, which shows errors such as missing (rejected) commands
.. code-block:: javascript
- ...
+ {
"access-control-lists":[
{
"my-list-one":{
"unknown-role":{
"accept-commands":"my-list-three",
"reject-commands":"ALL"
+ },
+ ...
}
"hooks-libraries": [
{
"library": "/path/libdhcp_stat_cmds.so"
- }
+ },
...
]
}
{
"command": "stat-lease4-get",
"arguments": {
- "subnet-range" {
+ "subnet-range": {
"first-subnet-id": 10,
"last-subnet-id": 50
}
"text": "stat-lease4-get: 2 rows found",
"arguments": {
"result-set": {
- "columns": [ "subnet-id", "total-addresses", "cumulative-assigned-addresses", "assigned-addresses", "declined-addresses" ]
+ "columns": [ "subnet-id", "total-addresses", "cumulative-assigned-addresses", "assigned-addresses", "declined-addresses" ],
"rows": [
[ 10, 256, 300, 111, 0 ],
[ 20, 4098, 2034, 2034, 4 ]
],
- "timestamp": "2018-05-04 15:03:37.000000"
+ "timestamp": "2018-05-04 15:03:37.000000"
}
}
}
"text": "stat-lease6-get: 2 rows found",
"arguments": {
"result-set": {
- "columns": [ "subnet-id", "total-nas", "cumulative-assigned-nas", "assigned-nas", "declined-nas", "total-pds", "cumulative-assigned-pds", "assigned-pds" ]
+ "columns": [ "subnet-id", "total-nas", "cumulative-assigned-nas", "assigned-nas", "declined-nas", "total-pds", "cumulative-assigned-pds", "assigned-pds" ],
"rows": [
[ 10, 4096, 5000, 2400, 3, 0, 0, 0],
- [ 20, 0, 0, 0, 0, 1048, 300, 233 ]
+ [ 20, 0, 0, 0, 0, 1048, 300, 233 ],
[ 30, 256, 60, 60, 0, 1048, 15, 15 ]
],
- "timestamp": "2018-05-04 15:03:37.000000"
+ "timestamp": "2018-05-04 15:03:37.000000"
}
}
}
"subnet": "192.0.2.0/24"
}
]
+ }
}
If no IPv4 subnets are found, an error code is returned along with the
"subnet": "3000::/16"
}
]
+ }
}
If no IPv6 subnets are found, an error code is returned along with the
"subnet": "10.0.0.0/8",
"id": 1,
"option-data": [
- ....
- ]
+ {
+ ...
+ },
+ ...
+ ],
...
}
]
"subnet": "2001:db8:1::/64",
"id": 1,
"option-data": [
+ {
+ ...
+ },
...
- ]
- ....
+ ],
+ ...
}
]
}
"valid-lifetime": 0,
"id": 123,
"subnet": "10.20.30.0/24",
- "option-data" [
+ "option-data": [
{ "name": "routers" }
- ]
+ ],
"pools": [
{
"option-data": [
{ "code": 4 }
- ]
+ ],
"pool": "10.20.30.11-10.20.30.20"
},
{
"valid-lifetime": 0,
"id": 234,
"subnet": "2001:db8:1::/64",
- "option-data" [
+ "option-data": [
{ "name": "dns-servers" }
- ]
+ ],
"pd-pools": [
{
"prefix": "2001:db8:3::",
{
"option-data": [
{ "code": 31 }
- ]
+ ],
"pool": "2001:db8:1::11-2001:db8:1::20"
},
{
{
"subnet": "192.0.2.0/24",
"id": 5,
+ ...
# many other subnet-specific details here
},
{
"id": 6,
"subnet": "192.0.3.0/31",
+ ...
# many other subnet-specific details here
}
],
::
"Dhcp4": {
- ...
-
"expired-leases-processing": {
"reclaim-timer-wait-time": 3,
"hold-reclaimed-time": 1800,
"flush-reclaimed-timer-wait-time": 5
},
-
...
}
"output": "/var/log/kea-debug.log",
"maxver": 8,
"maxsize": 204800,
- "flush": true
+ "flush": true,
"pattern": "%d{%j %H:%M:%S.%q} %c %m\n"
}
],
"description": "See <xref linkend=\"command-config-test\"/>",
"name": "config-test",
"resp-syntax": [
- "{ \"result\": 0, \"text\": \"Configuration seems sane...\" }",
+ "{ \"result\": 0, \"text\": \"Configuration seems sane.\" }",
"",
"or",
"",
" {",
" \"subnet\": \"192.0.2.0/24\",",
" \"id\": 5,",
- " // many other subnet specific details here",
+ " ...",
" },",
" {",
" \"subnet\": \"192.0.3.0/31\",",
" \"id\": 6,",
- " // many other subnet specific details here",
+ " ...",
" }",
" ],",
" \"valid-lifetime\": 120",
" {",
" \"subnet\": \"2003:db8:1::/64\",",
" \"id\": 5,",
- " // many other subnet specific details here",
+ " ...",
" },",
" {",
" \"subnet\": \"2003:db8:2::/71\",",
" \"id\": 6,",
- " // many other subnet specific details here",
+ " ...",
" }",
" ],",
" \"valid-lifetime\": 120",
--- /dev/null
+#!/bin/bash
+work_file=`mktemp`
+for file in `find ./ | grep -v "\.git" | grep -v "_build" | grep -v "\/man\/" | grep "\.rst\|\.json" | grep -v "api\.rst" | sort`; do
+ json=0
+ comment=0
+ line_num=0
+ echo "processing: $file"
+ while read line; do
+ line_num=$((line_num+1))
+ if [ $comment -eq 0 -a $json -eq 0 -a `echo "$line" | grep -e "^\[A-Za-z]+" | wc -l` -eq 1 ]; then
+ continue
+ elif [ $comment -eq 0 -a `echo "$line" | grep -e "\/\*" | grep -v -e "\*\/" | wc -l` -eq 1 ]; then
+ comment=1
+ echo "" >> $work_file
+ continue
+ elif [ $comment -eq 1 -a `echo "$line" | grep "\*\/" | wc -l` -eq 1 ]; then
+ comment=0
+ echo "" >> $work_file
+ continue
+ elif [ $comment -eq 0 -a $json -eq 0 -a `echo "$line" | grep "^\s*{\|^\s*\".*{" | grep -v "}" | grep -v "key\|pre" | wc -l` -eq 1 ]; then
+ json=1
+ # ignore any map name before top level map
+ line=`echo "$line" | sed "s/.*{/{/g"`
+ echo "" > $work_file
+ elif [ $comment -eq 0 -a $json -eq 1 -a `echo "$line" | grep -e "^\s*[A-Za-z]" | wc -l` -eq 1 ]; then
+ json=0
+ cat $work_file | jq . > /dev/null
+ if [ $? -ne 0 ]; then
+ echo "file $file contains invalid JSON near line $line_num"
+ echo "===start of JSON block==="
+ cat $work_file
+ echo "====end of JSON block===="
+ fi
+ fi
+ if [ $comment -eq 0 -a $json -eq 1 ]; then
+ if [ `echo "$line" | grep -e "^\s*\.\.\s" | wc -l` -eq 1 ]; then
+ echo "" >> $work_file
+ else
+ if [ `echo "$file" | grep "\.json" | wc -l` -eq 0 ]; then
+ echo "$line" | cut -d "#" -f 1 | sed "s/\.\.\./\"placeholder\": 0/g" | sed "s/\/\/ .*//g" | sed "s/<?.*?>//g" >> $work_file
+ else
+ echo "$line" | cut -d "#" -f 1 | sed "s/\/\/ .*//g" | sed "s/<?.*?>//g" >> $work_file
+ fi
+ fi
+ fi
+ done <<< $(cat $file | sed ':a;N;$!ba;s/,\s*\n\s*\.\.\.//g' | sed 's/\\\"/\\\\\"/g' | sed 's/\\\\,/\\\\\\\\,/g')
+ if [ $comment -eq 0 -a $json -eq 1 ]; then
+ cat $work_file | jq . > /dev/null
+ if [ $? -ne 0 ]; then
+ echo "file $file contains invalid JSON near line $line_num"
+ echo "===start of JSON block==="
+ cat $work_file
+ echo "====end of JSON block===="
+ fi
+ fi
+done
+rm $work_file