wiki:SubnetCommands

Subnet Commands Hook Library

This page describes requirements and a design for Subnet commands (subnet_cmds) hook library. That library is planned a a premium hook library for upcoming Kea 1.3 release. For overview of the commands, see Commands, Section 2.4.

NOTE: This document is a work in progress, it is expected to be updated in the next future.

Use Cases

  1. One of the potential users shared a story with us. Once in a while, they boot up a new datacenter. Each datacenter has up to a 1000 of new subnets. Therefore it is desired to be able to add a significant number of subnets with as little interruptions as possible.
  1. When troubleshooting network problems, it is essential to retrieve configuration for a given subnet being debugged.
  1. When GUI/IPAM system manages a network, it must be possible to retrieve a list of subnets, details for a given subnet, and then push changes for a given subnet. It must be possible to add new subnets, edit and remove existing ones.
  1. Once shared subnets support is developed, the library should able to provide management interface for it. As the shared subnets feature is not designed yet (see SharedSubnets), the details are TBD.

Commands

The subnet_cmds library will provide a number of additional commands. For a overview of those commands, see Commands, section 2.4. Below are the details of each commands.

subnet4-list command

This command takes no parameters and retrieves an abbreviated list of IPv4 subnets. Each subnet is represented by a single value of subnet-id. If you need to get more information about the subnet, please use subnet4-get command. Example command:

{ "command": "subnet4-list" }

Example response:

{
  "arguments": {
    "subnet-ids": [ 1, 2, 3, 4, 5, 8, 12 ]
  },
  "result": 0,
  "text": "7 IPv4 subnet(s) found."
}

This should be interpreted as: There are 7 IPv4 subnets currently configured. They are using the following subnet-ids: 1, 2, 3, 4, 5, 8 and 12. Note that subnet-ids typically start from one and grow by one, but that is not guaranteed. Deleting first subnet being configured will cause the number start from 2 for example. Sysadmin can use his own numbering scheme, so numbers starting from 100 may designate a floor 1 for example. Therefore it is essential to not assume that subnet-ids are continuous or that they are strictly increasing.

Alternative approach: Returning just a subnet-id for each subnet seems rather terse. It has the advantage of being able to return large number of subnets in compact way. On the other hand, retrieving more information about them would require more commands to be issued. Therefore the alternative approach would be to return subnet-id and a subnet prefix for each subnet. If this approach is chosen, the response would look like this. Note this example response corresponds to the same case as above. The size of this response is definitely bigger, but the data becomes possibly useful on its own, without any extra commands.

{
  "arguments": {
    "subnet-ids": [
    {
        "subnet-id": 1,
        "subnet": "192.0.1.0/24"
    },
    {
        "subnet-id": 2,
        "subnet": "192.0.2.0/28",
    },
    {
        "subnet-id": 3,
        "subnet": "192.0.3.0/24",
    },
    {
        "subnet-id": 4,
        "subnet": "192.0.4.0/24",
    },
    {
        "subnet-id": 5,
        "subnet": "192.0.5.0/24",
    },
    {
        "subnet-id": 8,
        "subnet": "10.20.30.0/30",
    },
    {
        "subnet-id": 12,
        "subnet": "10.30.0.0/16"
    }
    ] 
  },
  "result": 0,
  "text": "7 IPv4 subnet(s) found."
}

subnet6-list command

This command takes no parameters and retrieves an abbreviated list of IPv6 subnets. Each subnet is represented by a single value of subnet-id. If you need to get more information but the subnet, please use subnet6-get command. Example call:

{ "command": "subnet6-list" }

Example response:

{
  "arguments": {
    "subnet-ids": [ 1, 2, 3, 4, 5, 8, 12 ]
  },
  "result": 0,
  "text": "7 IPv6 subnet(s) found."
}

See subnet4-list for explanation how to interpret the response.

Alternative: returning just a subnet-id for each subnet seems rather terse. It has the advantage of being able to return large number of subnets in compact way. On the other hand, retrieving more information about them would require more calls. Therefore the alternative approach would be to return subnet-id and a subnet prefix for each subnet. If this approach is chosen, the response would look like this:

{
  "arguments": {
    "subnet-ids": [
    {
        "subnet-id": 1,
        "subnet": "2001:db8:1::/64"
    },
    {
        "subnet-id": 2,
        "subnet": "2001:db8:2::/64",
    },
    {
        "subnet-id": 3,
        "subnet": "2001:db8:3::/64",
    },
    {
        "subnet-id": 4,
        "subnet": "2001:db8:4::/64",
    },
    {
        "subnet-id": 5,
        "subnet": "2001:db8:5::/48",
    },
    {
        "subnet-id": 8,
        "subnet": "3ffe:dead:beef:1000::/56",
    },
    {
        "subnet-id": 12,
        "subnet": "2d01:abcd:1234:5678::/64"
    }
    ] 
  },
  "result": 0,
  "text": "7 IPv6 subnet(s) found."
}

subnet4-get command

This command retrieves information about a single subnet. There are several ways how such a subnet could be identified. In the initial implementation we expect to implement two of them: by subnet-id, or by using CIDR (subnet/length) notation. Those two parameter sets are mutually exclusive.

Example call for query by subnet-id:

{ 
    "command": "subnet4-get",
    "arguments": {
        "subnet-id": 1234
    }
}

Example call for query by subnet/length:

{ 
    "command": "subnet4-get",
    "arguments": {
        "subnet": "192.0.2.0/24"
    }
}

Example response:

{
    "arguments": {
        "subnet4": [
        {
            "subnet": "192.0.2.0/24",
            "id": 1024,
            "pools": [
                { "pool": "192.0.2.10-192.0.2.20" },
                { "pool": "192.0.2.64/26" }
            ],
            "option-data": [
                {
                    "name": "domain-name-servers",
                    "code": 6,
                    "space": "dhcp4",
                    "csv-format": true,
                    "data": "192.0.2.3"
                }
            ]
        }]
    },
    "result": 0,
    "text": "Info about IPv4 subnet 192.0.2.0/24 (subnet-id 1024) returned."
}

Actual subnets will likely to feature much more information. Anything that you could configure in a subnet except host reservations (options, pools, options in pools, many other parameters, etc) could be returned here, if configured. To retrieve host reservations from a subnet, use reservation-get call from host_cmds library.

subnet6-get command

This command retrieves information about a single IPv6 subnet. There are several ways how such a subnet could be identified. The initial implementation will provide support for two of them: by subnet-id, or by using CIDR (subnet/length) notation. Those two parameter sets are mutually exclusive.

Example call for query by subnet-id:

{ 
    "command": "subnet6-get",
    "arguments": {
        "subnet-id": 1234
    }
}

Example call for query by subnet/length:

{ 
    "command": "subnet6-get",
    "arguments": {
        "subnet": "2001:db8:1::/64"
    }
}

Example response:

{
    "arguments": {
        "subnet6": [
        {
            "subnet": "2001:db8:1::/64",
            "id": 1024,
            "pools": [
                { "pool": "2001:db8:1::-2001:db8:1::ffff" }
            ],
            "pd-pools": [
                {
                    "prefix": "3000:1::",
                    "prefix-len": 48,
                    "delegated-len": 56
                }
            ],
            "option-data": [
                {
                    "name": "unicast",
                    "data": "2001:db8:1::beef"
                }
            ]
        }]
    },
    "result": 0,
    "text": "Info about IPv6 subnet 2001:db8:1::/64 (subnet-id 1024) returned."
}

Actual subnets will likely to feature much more information. Anything that you could configure in a subnet except host reservations (options, pools, options in pools, many other parameters, etc) could be returned here, if configured. To retrieve host reservations from a subnet, use reservation-get call from host_cmds library.

subnet4-add command

This command adds a new IPv4 subnet to the current configuration. Several sanity checks are conducted before the subnet is actually added. In particular, there's a check whether the subnet does not overlap with existing subnets, whether the parameters are correct and the subnet-id is not currently used. Also, this updates the currently running configuration. The change will be lost if the server is shut down. To retain this information, please use config-get to retrieve full configuration or config-write to write it to disk.

In general, any parameters allowed in subnet4 scope in the configuration file are allowed as part of this command. Specifying subnet-id ("id" parameter) is not mandatory, but recommended. If not specified, the server will assign unique-id on its own. The actual value is reported in the response.

A example usage of this command could look like this:

{
    "command": "subnet4-add",
    "arguments": {
        "subnet4": [
        {
            "subnet": "192.0.2.0/24",
            "id": 1024,
            "pools": [
                { "pool": "192.0.2.10-192.0.2.20" },
                { "pool": "192.0.2.64/26" }
            ],
            "option-data": [
                {
                    "name": "domain-name-servers",
                    "data": "192.0.2.3"
                }
            ]
        }]
    }
}

An example response could look like:

{
    "result": 0, 
    "text": "IPv4 subnet added." 
    "arguments": {
        "subnet": "192.0.2.0/24",
        "id": 1024
    }
}

or this:

{
    "result": 1, 
    "text": "IPv4 subnet addition failed: This subnet overlaps with subnet with subnet-id 1234." 
}

subnet6-add command

This command adds a new IPv6 subnet to the current configuration. Several sanity checks are conducted before the subnet is actually added. In particular, there's a check whether the subnet does not overlap with existing subnets. Also, this updates the currently running configuration. The change will be lost if the server is shut down. To retain this information, please use config-get to retrieve full configuration or config-write to write it to disk.

In general, any parameters allowed in subnet6 scope in the configuration file are allowed as part of this command. Specifying subnet-id ("id" parameter) is not mandatory, but recommended. If not specified, the server will assign unique-id on its own.

A example usage of this command could look like this:

{
    "command": "subnet6-add",
    "arguments": {
        "subnet6": [
        {
            "subnet": "2001:db8:1::/64",
            "id": 1024,
            "pools": [
                { "pool": "2001:db8:1::-2001:db8:1::ffff" }
            ],
            "pd-pools": [
                {
                    "prefix": "3000:1::",
                    "prefix-len": 48,
                    "delegated-len": 56
                }
            ]
            "option-data": [
                {
                    "name": "unicast",
                    "data": "2001:db8:1::beef"
                }
            ]
        }]
    },
    "result": 0,
    "text": "Info about IPv6 subnet 2001:db8:1::/64 (subnet-id 1024) returned."
}

Usually the response would look similar to this:

{ 
    "result": 0, 
    "text": "IPv6 subnet added.",
    "arguments": {
        "subnet": "2001:db8:1::/64",
        "id": 1024
    }
}

subnet4-del

This command attempts to delete an IPv4 subnet. The initial implementation takes two optional parameters that specify whether to also delete the leases and the reservations in this subnet or not. In the future additional actions will be implemented.

An example use of this command looks like this:

{
   "command": "subnet4-del",
   "arguments": {
       "id": 1024,
       "leases-action": "keep",
       "reservations-action": "delete".
   }
}

The plan for initial implementation is to support two values for leases-action and reservations-action: delete (which means that either leases or host reservations associated with a given subnet are deleted) and keep (which means that either leases or host reservations are kept).

The default is keep. If leases-action is not specified, the leases are not deleted. If reservations-action is not specified, the reservations are not deleted.

In the future, we may implement additional actions. In particular, we may implement expire, which would keep the leases until the client tries to renew them and such a request would be NAKed, forcing client to restart its state machine. Another possible action here would be reconfigure, i.e. to tell Kea to use DHCP reconfigure mechanism.

NOTE: When leases-delete is set to true, the leases are permanently deleted in lease database. The devices (DHCP clients) are not informed about this removal and will continue using the addresses until the assigned lease lifetime elapses.

And example result could look as follows:

{ "result": 0, "text": "IPv4 subnet 192.0.2.0/24 deleted." }

NOTE: In the future there will likely be a feature implemented to let the subnet be deleted gradually, i.e. mark it for removal, keep the leases for now, but not extend them any further and not grant any new leases. Once the number of active leases go down to zero, the subnet would be deleted. Such a future mechanism is currently not scheduled for implementation.

subnet6-del

This command attempts to delete an IPv6 subnet. The initial implementation takes two optional parameters that specify whether to also delete the leases and the reservations in this subnet or not. In the future there will be more flexibility in the operation.

An example use of this command looks like this:

{
   "command": "subnet6-del",
   "arguments": {
       "id": 1024,
       "leases-action": "delete",
       "reservations-action": "keep"
   }
}

The plan for initial implementation is to support two values for leases-action and reservations-action: delete (which means that either leases or host reservations associated with a given subnet are deleted) and keep (which means that either leases or host reservations are kept).

The default is keep. If leases-action is not specified, the leases are not deleted. If reservations-action is not specified, the reservations are not deleted.

In the future, we may implement additional actions. In particular, we may implement expire, which would keep the leases until the client tries to renew them and such a request would be rejected, forcing client to restart its state machine or send a Request message. Another possible action here would be reconfigure, i.e. to tell Kea to use DHCP reconfigure mechanism.

NOTE: When leases-delete is set to true, the leases are permanently deleted in lease database. The devices (DHCP clients) are not informed about this removal and will continue using the addresses until the assigned lease lifetime elapses.

And example result could look as follows:

{ "result": 0, "text": "IPv6 subnet 2001:db8:1::/64 deleted." }

NOTE: In the future there will likely be a feature implemented to let the subnet be deleted gradually, i.e. mark it for removal, keep the leases for now, but not extend them any further and not grant any new leases. Once the number of active leases go down to zero, the subnet would be deleted. Such a future mechanism is currently not scheduled for implementation.

Design

This functionality is planned as a hooks library. The design is expected to be completed as part of #5322 ticket.

With the increasing complexity of the configuration it becomes less and less viable to operate on complete list of all subnets. This will become more and more problematic once the configurations grow to many thousands of networks (it already does for many deployments). While there has to be a way to retrieve all subnets, this should not be the default access pattern and should be discouraged in general. Therefore the subnet4-list and subnet6-list returns the absolute minimum of information (i.e. the subnet-id values).

Also, it may look more natural from English to use plural form (subnets6-list) rather than singular. However, in the future there may be mechanisms that would take advantage of similarities between commands. Two of such mechanisms that were discussed are automatic generation of available commands (it would be good if commands related to a certain aspect, e.g. subnet4, were grouped together) and tab completion in interactive clients (it is possible to implement an interactive client that would use tab completion: you would type subnet4, hit tab and see list of things you could possibly do with IPv4 subnets).

Modifying configuration

The central problem here is how to add a new subnet. The problem is that internally the configuration is stored in SrvConfig, which is returned by CfgMgr::getCurrentCfg() as ConstSrvConfigPtr, i.e. a const pointer. The idea here was that the code must not modify the configuration while the server is running. In principle, we could implement some sort of clone mechanism (so staging config would be a clone of running config), then add new subnet and commit the configuration. This would work well for single subnets, but would be terrible for the use case we're trying to address here: adding a 1000 of subnets. It would mean the server would go through a 1000 of reconfiguration events - something we definitely want to avoid.

There are at least two possible approaches here:

  1. Therefore CfgMgr should be updated to provide an ability to retrieve current config as non-const pointer. This should be restricted somehow as it may be very dangerous if mis-used. The likely way to achieve this is to define the getCurrentNonConstCfg as private and use friendship mechanism to allow the subnet hooks class to access it. This makes the complexity of the solution to be on the developers side, keep usage of the API simpler for the users.
  1. We could add extra commands to explicitly clone configuration to staging (e.g. config-clone), and then extend subnet{4,6}-add methods with optional parameter "dont-commit". This way user would need to call config-clone, subnet4-add(first subnet), subnet4-add(2nd subnet), ... subnet4-add(nth subnet). All but the last call would have dont-commit flag set. This would allow to batch subnet additions together, but it would make the operation more complex (extra calls, extra parameters). It would also be less performant than approach 1. This would make the complexity of the solution to be on the users side, keeping the API usage more complex. On the other hand, grouping additions (and possibly removals) together may be beneficial.

Shared subnets

One of the upcoming features of Kea 1.3 will be the ability to support shared subnets, i.e. handle several logical IPv4 or IPv6 subnets on the same physical link. This library will eventually be able to handle that, but it is unlikely to be a feature from day 1. As the shared subnets feature is not designed yet, the details of the support is blurry. However, some thoughts that could be shared at this time are:

  • the difference between managing shared and regular subnet should be as minimal as possible.
  • one of the primary use case for shared subnets is people running out of IPv4 addresses and adding new IPv4 subnet on top of existing one. This means that an existing subnet must be easily convertible to shared subnet. Also, the reverse conversion (one of two shared subnets is being retired and the other subnet becomes a regular subnet) must be possible.
  • ISC DHCP provided a different structure of the configuration for single and shared subnets. This increased complexity is something we should definitely avoid in Kea.
Last modified 5 months ago Last modified on Jun 12, 2017, 4:14:36 PM