Browse Source

[master] Merge branch 'trac5350'

Tomek Mrugalski 7 years ago
parent
commit
828ecb6dbd

+ 14 - 3
doc/examples/kea4/advanced.json

@@ -80,11 +80,22 @@
     "match-client-id": true,
 
     // The following list defines subnets. Each subnet consists of at
-    // least subnet and pool entries.
+    // least subnet and pool entries. One extra feature that requires some
+    // explanation is user-context. This is a structure that you can define
+    // in subnets and pools. It is parsed by Kea, but not used directly.
+    // It is intended to keep anything you may want to put there - comments,
+    // extra designations, floor or department names etc. These structures
+    // will be made available to Kea hooks.
     "subnet4": [
         {
-            "pools": [ { "pool":  "192.0.2.1 - 192.0.2.200" } ],
-            "subnet": "192.0.2.0/24"
+            "pools": [ {
+                "pool":  "192.0.2.1 - 192.0.2.200",
+                "user-context": { "info": "what a large pool" }
+            } ],
+            "subnet": "192.0.2.0/24",
+            "user-context": {
+                "comment": "Our first subnet!"
+            }
         },
         {
             // This particular subnet has match-client-id value changed.

+ 105 - 79
doc/examples/kea6/advanced.json

@@ -14,51 +14,51 @@
 { "Dhcp6":
 
 {
-  // Kea is told to listen on ethX network interface only.
-  "interfaces-config": {
-    "interfaces": [ "ethX" ],
+    // Kea is told to listen on ethX network interface only.
+    "interfaces-config": {
+        "interfaces": [ "ethX" ],
 
         // This makes interfaces to be re-detected at each (re-)configuration.
         // By default it is true.
         "re-detect": true
-  },
-
-  // We need to specify the the database used to store leases. As of
-  // September 2016, four database backends are supported: MySQL,
-  // PostgreSQL, Cassandra, and the in-memory database, Memfile.
-  // We will use memfile  because it doesn't require any prior set up.
-  "lease-database": {
-      "type": "memfile",
-      "lfc-interval": 3600
-  },
-
-// Kea 0.9.1 introduced MAC/hardware addresses support in DHCPv6. There is
-// no single reliable method of getting MAC address information in DHCPv6.
-// Kea supports several methods. Depending on your network set up, some
-// methods may be more preferable than others, hence the configuration
-// parameter. 'mac-sources' is a list of methods. Allowed parameters are:
-// any, raw, duid, ipv6-link-local, client-link-addr-option, rfc6939 (which
-// 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" ],
+    },
+
+    // We need to specify the the database used to store leases. As of
+    // September 2016, four database backends are supported: MySQL,
+    // PostgreSQL, Cassandra, and the in-memory database, Memfile.
+    // We will use memfile  because it doesn't require any prior set up.
+    "lease-database": {
+        "type": "memfile",
+        "lfc-interval": 3600
+    },
 
-// RFC6422 defines a mechanism called relay-supplied options option. The relay
-// agent may insert certain options that the server will echo back to the
-// client, if certain criteria are met. One condition is that the option must
-// be RSOO-enabled (i.e. allowed to be echoed back). IANA maintains a list
-// of those options here:
-// http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#options-relay-supplied
-// However, it is possible to allow the server to echo back additional options.
-// This entry marks options 110, 120 and 130 as RSOO-enabled.
-   "relay-supplied-options": [ "110", "120", "130" ],
+    // Kea 0.9.1 introduced MAC/hardware addresses support in DHCPv6. There is
+    // no single reliable method of getting MAC address information in DHCPv6.
+    // Kea supports several methods. Depending on your network set up, some
+    // methods may be more preferable than others, hence the configuration
+    // parameter. 'mac-sources' is a list of methods. Allowed parameters are:
+    // any, raw, duid, ipv6-link-local, client-link-addr-option, rfc6939 (which
+    // 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" ],
 
+    // RFC6422 defines a mechanism called relay-supplied options option. The
+    // relay agent may insert certain options that the server will echo back to
+    // the client, if certain criteria are met. One condition is that the option
+    // must be RSOO-enabled (i.e. allowed to be echoed back). IANA maintains a
+    // list of those options here:
+    // http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#options-relay-supplied
+    // However, it is possible to allow the server to echo back additional
+    // options.  This entry marks options 110, 120 and 130 as RSOO-enabled.
+    "relay-supplied-options": [ "110", "120", "130" ],
 
     // This defines a control socket. If defined, Kea will open a UNIX socket
     // and will listen for incoming commands. See section 15 of the Kea User's
@@ -68,48 +68,74 @@
         "socket-name": "/tmp/kea6-ctrl-socket"
     },
 
-// Addresses will be assigned with preferred and valid lifetimes
-// being 3000 and 4000, respectively. Client is told to start
-// renewing after 1000 seconds. If the server does not respond
-// after 2000 seconds since the lease was granted, client is supposed
-// to start REBIND procedure (emergency renewal that allows switching
-// to a different server).
-  "preferred-lifetime": 3000,
-  "valid-lifetime": 4000,
-  "renew-timer": 1000,
-  "rebind-timer": 2000,
-
-// The following list defines subnets. Each subnet consists of at
-// least subnet and pool entries.
-  "subnet6": [
+    // Addresses will be assigned with preferred and valid lifetimes
+    // being 3000 and 4000, respectively. Client is told to start
+    // renewing after 1000 seconds. If the server does not respond
+    // after 2000 seconds since the lease was granted, client is supposed
+    // to start REBIND procedure (emergency renewal that allows switching
+    // to a different server).
+    "preferred-lifetime": 3000,
+    "valid-lifetime": 4000,
+    "renew-timer": 1000,
+    "rebind-timer": 2000,
+
+    // The following list defines subnets. Each subnet consists of at
+    // least subnet and pool entries. Note the user-context being
+    // used throughout the definitions. This is something that is not
+    // being used by Kea, it's simply parsed and stored in appropriate
+    // structures. You can put anything you want in the user-context
+    // as long as it is a valid JSON and it starts with a map (i.e.
+    // is enclosed by curly brackets).
+    "subnet6": [
     {
-      "pools": [ { "pool": "2001:db8:1::/80" } ],
-
-// This defines PD (prefix delegation) pools. In this case
-// we have only one pool. That consists of /64 prefixes
-// being delegated out of large /48 pool. Each delegated
-// prefix will contain an excluded-prefix option.
-      "pd-pools": [
-      {
-          "prefix": "2001:db8:abcd::",
-          "prefix-len": 48,
-          "delegated-len": 64,
-          "excluded-prefix": "2001:db8:abcd:1234::",
-          "excluded-prefix-len": 62
-      }
-      ],
-      "subnet": "2001:db8:1::/64",
-      "interface": "ethX",
-
-      // Sometimes the relay may use an odd IPv6 address that's not matching
-      // the subnet. This is discouraged, but there are valid cases when it
-      // makes sense. One case is when the relay has only link-local address
-      // and another is when there is a shared subnet scenario.
-      "relay": {
-          "ip-address": "3000::1"
-      }
+        "pools": [
+            {
+                "pool": "2001:db8:1::/80",
+
+                // This is user context specified for this particular
+                // pool. You can use it to describe the pool in some way.
+                // Just keep in mind that the structure will not be used
+                // by Kea itself. It will be made available to hooks if
+                // they want to use it.
+                "user-context": { "department": "engineering" }
+            }],
+
+        // Here's the user-context for the whole subnet.
+        "user-context": { "comment": "Floor one, west wing" },
+
+        // This defines PD (prefix delegation) pools. In this case
+        // we have only one pool. That consists of /64 prefixes
+        // being delegated out of large /48 pool. Each delegated
+        // prefix will contain an excluded-prefix option.
+        "pd-pools": [
+        {
+            "prefix": "2001:db8:abcd::",
+            "prefix-len": 48,
+            "delegated-len": 64,
+            "excluded-prefix": "2001:db8:abcd:1234::",
+            "excluded-prefix-len": 62,
+
+            // Another user-context for this PD pool. Again, you can put
+            // anything you want in there as long as it's valid JSON and
+            // starts with a map.
+            "user-context": {
+                "purpose": "For CPE devices"
+            }
+        }
+        ], // end of pools
+
+        "subnet": "2001:db8:1::/64",
+        "interface": "ethX",
+
+        // Sometimes the relay may use an odd IPv6 address that's not matching
+        // the subnet. This is discouraged, but there are valid cases when it
+        // makes sense. One case is when the relay has only link-local address
+        // and another is when there is a shared subnet scenario.
+        "relay": {
+            "ip-address": "3000::1"
+        }
     }
-  ]
+    ]
 },
 
 // The following configures logging. It assumes that messages with at

+ 35 - 8
doc/guide/dhcp4-srv.xml

@@ -3915,7 +3915,7 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
     </section>
 
       <section>
-        <title>User context in IPv4 pools</title>
+        <title>User contexts in IPv4</title>
         <para>
           Kea allows loading hook libraries that sometimes could benefit from
           additional parameters. If such a parameter is specific to the whole
@@ -3923,11 +3923,25 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
           However, sometimes there is a need to specify parameters that are
           different for each pool.
         </para>
+
+        <para>
+          User contexts can store arbitrary data as long as it is valid JSON
+          syntax and its top level element is a map (i.e. the data must be
+          enclosed in curly brackets). Some hook libraries may expect specific
+          formatting, though.  Please consult specific hook library
+          documentation for details.
+        </para>
+
+        <para>
+          User contexts can be specified on either subnet or pool level. One
+          other useful usage is the ability to store comments or descriptions.
+        </para>
+
         <para>
           Let's consider an imaginary case of devices that have color LED
           lights. Depending on their location, they should glow red, blue or
           green. It would be easy to write a hook library that would send
-          specific values as maybe vendor option. However, the server has to
+          specific values as maybe a vendor option. However, the server has to
           have some way to specify that value for each pool. This need is
           addressed by user contexts. In essence, any user data can specified
           in the user context as long as it is a valid JSON map. For example,
@@ -3939,8 +3953,22 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
     "subnet4": [
         {
             "subnet": "192.0.2.0/24",
-            "pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ],
-            <userinput>"user-context": { "colour": "red" }</userinput>
+            "pools": [ {
+                "pool": "192.0.2.10 - 192.0.2.20",
+                // This is pool specific user context
+                <userinput>"user-context": { "colour": "red" }</userinput>
+            } ],
+
+            // This is a subnet specific user context. You can put whatever type
+            // of information you want as long as it is a valid JSON.
+            <userinput>"user-context": {
+                "comment": "network on the second floor",
+                "last-modified": "2017-09-04 13:32",
+                "description": "you can put here anything you like",
+                "phones": [ "x1234", "x2345" ],
+                "devices-registered": 42,
+                "billing": false
+            }</userinput>
         },
         ...
     ],
@@ -3951,11 +3979,10 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
         <para>
           It should be noted that Kea will not use that information, but will
           simply store and make it available to hook libraries. It is up to the
-        hook library to extract that information and make use of it.</para>
+          hook library to extract that information and make use of it.
+        </para>
         <para>
-          Currently only pools allow definition of user contexts, but this
-          concept is expected to be enhanced to other structures in the future.
-          For more background information, see <xref linkend="user-context"/>
+          For more background information, see <xref linkend="user-context"/>.
         </para>
     </section>
 

+ 29 - 7
doc/guide/dhcp6-srv.xml

@@ -4071,7 +4071,7 @@ If not specified, the default value is:
     </section>
 
       <section>
-        <title>User context in IPv6 pools</title>
+        <title>User contexts in IPv6</title>
         <para>
           Kea allows loading hook libraries that sometimes could benefit from
           additional parameters. If such a parameter is specific to the whole
@@ -4079,6 +4079,20 @@ If not specified, the default value is:
           However, sometimes there is a need to specify parameters that are
           different for each pool.
         </para>
+
+        <para>
+          User contexts can store arbitrary data as long as it is valid JSON
+          syntax and its top level element is a map (i.e. the data must be
+          enclosed in curly brackets). Some hook libraries may expect specific
+          formatting, though.  Please consult specific hook library
+          documentation for details.
+        </para>
+
+        <para>
+          User contexts can be specified on either subnet or pool level. One
+          other useful usage is the ability to store comments or descriptions.
+        </para>
+
         <para>
           Let's consider an example where certain parameters are supposed
           to be delivered to clients in form of additional options,
@@ -4093,20 +4107,31 @@ If not specified, the default value is:
           parameters used by the hook library when it is loaded.
           An example configuration could look as follows:
           <screen>
-"Dhcp4": {
+"Dhcp6": {
     "subnet6": [ {
         "pd-pools": [
         {
             "prefix":  "2001:db8::",
             "prefix-len": 56,
             "delegated-len": 64,
+
+            // This is a pool specific context.
             <userinput>"user-context": {
                 "threshold-percent": 85,
                 "v4-network": "192.168.0.0/16",
                 "v4-overflow": "10.0.0.0/16"
             }</userinput>
         } ],
-        "subnet": "2001:db8::/32"
+        "subnet": "2001:db8::/32",
+
+        // This is a subnet specific context. You can put any type of
+        // information here as long as it is a valid JSON.
+        <userinput>"user-context": {
+            "comment": "Those v4-v6 migration technologies are tricky.",
+            "experimental": true,
+            "billing-department": 42,
+            "contact-points": [ "Alice", "Bob" ]
+        }</userinput>
     } ],
     ...
 }</screen>
@@ -4119,10 +4144,7 @@ If not specified, the default value is:
           and make use of it.
         </para>
         <para>
-          Currently only address and prefix pools allow definition of user
-          contexts, but this concept is expected to be enhanced to other
-          structures in the future. For more background information, see <xref
-          linkend="user-context"/>
+          For more background information, see <xref linkend="user-context"/>.
         </para>
     </section>
 

+ 11 - 3
doc/guide/hooks.xml

@@ -1969,9 +1969,17 @@ If the subnet exists the response will be similar to this:
       and makes it available for the hook libraries.
       </para>
       <para>
-        As of Kea 1.2, the only structures that allow user contexts are address
-        and prefix pools, but it is expected to extend other structures with the
-        user context capability.
+        Another use case for user contexts may be storing comments and other
+        information that will be retained by Kea. Regular comments are discarded
+        when configuration is loaded, but user contexts are retained. This is
+        useful if you want your comments to survive config-set, config-get
+        operations for example.
+      </para>
+      <para>
+        As of Kea 1.3, the structures that allow user contexts are pools of all
+        types (addresses and prefixes) and subnets. These are supported in both
+        DHCPv4 and DHCPv6. It is expected that other structures will be extended
+        in the future to provide the user context capability.
       </para>
     </section>
 

File diff suppressed because it is too large
+ 365 - 586
src/bin/dhcp4/dhcp4_lexer.cc


+ 1 - 0
src/bin/dhcp4/dhcp4_lexer.ll

@@ -529,6 +529,7 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
 
 \"user-context\" {
     switch(driver.ctx_) {
+    case isc::dhcp::Parser4Context::SUBNET4:
     case isc::dhcp::Parser4Context::POOLS:
         return isc::dhcp::Dhcp4Parser::make_USER_CONTEXT(driver.loc_);
     default:

File diff suppressed because it is too large
+ 903 - 926
src/bin/dhcp4/dhcp4_parser.cc


+ 3 - 3
src/bin/dhcp4/dhcp4_parser.h

@@ -1385,9 +1385,9 @@ namespace isc { namespace dhcp {
     enum
     {
       yyeof_ = 0,
-      yylast_ = 755,     ///< Last index in yytable_.
-      yynnts_ = 321,  ///< Number of nonterminal symbols.
-      yyfinal_ = 26, ///< Termination state number.
+      yylast_ = 757,     ///< Last index in yytable_.
+      yynnts_ = 318,  ///< Number of nonterminal symbols.
+      yyfinal_ = 24, ///< Termination state number.
       yyterror_ = 1,
       yyerrcode_ = 256,
       yyntokens_ = 146  ///< Number of tokens.

+ 1 - 0
src/bin/dhcp4/dhcp4_parser.yy

@@ -894,6 +894,7 @@ subnet4_param: valid_lifetime
              | subnet_4o6_interface
              | subnet_4o6_interface_id
              | subnet_4o6_subnet
+             | user_context
              | unknown_map_entry
              ;
 

+ 0 - 1
src/bin/dhcp4/location.hh

@@ -1,4 +1,3 @@
-// Generated 201709021237
 // A Bison parser, made by GNU Bison 3.0.4.
 
 // Locations for Bison parsers in C++

+ 0 - 1
src/bin/dhcp4/position.hh

@@ -1,4 +1,3 @@
-// Generated 201709021237
 // A Bison parser, made by GNU Bison 3.0.4.
 
 // Positions for Bison parsers in C++

+ 0 - 1
src/bin/dhcp4/stack.hh

@@ -1,4 +1,3 @@
-// Generated 201709021237
 // A Bison parser, made by GNU Bison 3.0.4.
 
 // Stack handling for Bison parsers in C++

+ 47 - 1
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

@@ -119,7 +119,22 @@ const char* CONFIGS[] = {
     "            \"always-send\": true"
     "        }"
     "    ]"
-    "}" };
+    "}",
+
+    // Configuration 3:
+    // - one subnet, with one pool
+    // - user-contexts defined in both subnet and pool
+    "{"
+        "    \"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"10.254.226.0/25\","
+        "                   \"user-context\": { \"value\": 42 } } ],"
+        "    \"subnet\": \"10.254.226.0/24\", "
+        "    \"user-context\": {"
+        "        \"secure\": false"
+        "    }"
+        " } ],"
+    "\"valid-lifetime\": 4000 }",
+};
 
 // This test verifies that the destination address of the response
 // message is set to giaddr, when giaddr is set to non-zero address
@@ -2795,6 +2810,37 @@ TEST_F(Dhcpv4SrvTest, tooLongClientId) {
     EXPECT_NO_THROW(client.doDORA());
 }
 
+// Checks if user-contexts are parsed properly.
+TEST_F(Dhcpv4SrvTest, userContext) {
+
+    IfaceMgrTestConfig test_config(true);
+
+    NakedDhcpv4Srv srv(0);
+
+    // This config has one subnet with user-context with one
+    // pool (also with context). Make sure the configuration could be accepted.
+    cout << CONFIGS[3] << endl;
+    EXPECT_NO_THROW(configure(CONFIGS[3]));
+
+    // Now make sure the data was not lost.
+    ConstSrvConfigPtr cfg = CfgMgr::instance().getCurrentCfg();
+    const Subnet4Collection* subnets = cfg->getCfgSubnets4()->getAll();
+    ASSERT_TRUE(subnets);
+    ASSERT_EQ(1, subnets->size());
+
+    // Let's get the subnet and check its context.
+    Subnet4Ptr subnet1 = (*subnets)[0];
+    ASSERT_TRUE(subnet1);
+    ASSERT_TRUE(subnet1->getContext());
+    EXPECT_EQ("{ \"secure\": false }", subnet1->getContext()->str());
+
+    // Ok, not get the sole pool in it and check its context, too.
+    PoolCollection pools = subnet1->getPools(Lease::TYPE_V4);
+    ASSERT_EQ(1, pools.size());
+    ASSERT_TRUE(pools[0]);
+    ASSERT_TRUE(pools[0]->getContext());
+    EXPECT_EQ("{ \"value\": 42 }", pools[0]->getContext()->str());
+}
 
 /// @todo: Implement proper tests for MySQL lease/host database,
 ///        see ticket #4214.

File diff suppressed because it is too large
+ 392 - 613
src/bin/dhcp6/dhcp6_lexer.cc


+ 1 - 0
src/bin/dhcp6/dhcp6_lexer.ll

@@ -788,6 +788,7 @@ ControlCharacterFill            [^"\\]|\\{JSONEscapeSequence}
     switch(driver.ctx_) {
     case isc::dhcp::Parser6Context::POOLS:
     case isc::dhcp::Parser6Context::PD_POOLS:
+    case isc::dhcp::Parser6Context::SUBNET6:
         return isc::dhcp::Dhcp6Parser::make_USER_CONTEXT(driver.loc_);
     default:
         return isc::dhcp::Dhcp6Parser::make_STRING("user-context", driver.loc_);

File diff suppressed because it is too large
+ 966 - 977
src/bin/dhcp6/dhcp6_parser.cc


+ 3 - 3
src/bin/dhcp6/dhcp6_parser.h

@@ -1425,9 +1425,9 @@ namespace isc { namespace dhcp {
     enum
     {
       yyeof_ = 0,
-      yylast_ = 803,     ///< Last index in yytable_.
-      yynnts_ = 338,  ///< Number of nonterminal symbols.
-      yyfinal_ = 28, ///< Termination state number.
+      yylast_ = 805,     ///< Last index in yytable_.
+      yynnts_ = 335,  ///< Number of nonterminal symbols.
+      yyfinal_ = 26, ///< Termination state number.
       yyterror_ = 1,
       yyerrcode_ = 256,
       yyntokens_ = 154  ///< Number of tokens.

+ 1 - 0
src/bin/dhcp6/dhcp6_parser.yy

@@ -905,6 +905,7 @@ subnet6_param: preferred_lifetime
              | reservations
              | reservation_mode
              | relay
+             | user_context
              | unknown_map_entry
              ;
 

+ 0 - 1
src/bin/dhcp6/location.hh

@@ -1,4 +1,3 @@
-// Generated 201709021237
 // A Bison parser, made by GNU Bison 3.0.4.
 
 // Locations for Bison parsers in C++

+ 0 - 1
src/bin/dhcp6/position.hh

@@ -1,4 +1,3 @@
-// Generated 201709021237
 // A Bison parser, made by GNU Bison 3.0.4.
 
 // Positions for Bison parsers in C++

+ 0 - 1
src/bin/dhcp6/stack.hh

@@ -1,4 +1,3 @@
-// Generated 201709021237
 // A Bison parser, made by GNU Bison 3.0.4.
 
 // Stack handling for Bison parsers in C++

+ 58 - 0
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -125,6 +125,26 @@ const char* CONFIGS[] = {
     "         \"always-send\": true"
     "    }"
     "    ]"
+    "}",
+
+    // Configuration 3:
+    // - one subnet with one address pool and one prefix pool
+    // - user-contexts defined in subnet and each pool
+    "{"
+    "    \"subnet6\": [ {"
+    "       \"pools\": [ {"
+    "           \"pool\": \"2001:db8:1::/64\","
+    "           \"user-context\": { \"value\": 42 }"
+    "       } ], "
+    "       \"pd-pools\": [ {"
+    "           \"prefix\": \"2001:db8:abcd::\","
+    "           \"prefix-len\": 48,"
+    "           \"delegated-len\": 64,"
+    "           \"user-context\": { \"type\": \"prefixes\" }"
+    "        } ],"
+    "       \"subnet\": \"2001:db8:1::/48\","
+    "       \"user-context\": { \"secure\": false }"
+    "    } ] "
     "}"
 };
 
@@ -2424,6 +2444,44 @@ TEST_F(Dhcpv6SrvTest, tooLongServerId) {
     EXPECT_NO_THROW(client.doSARR());
 }
 
+// Checks if user-contexts are parsed properly.
+TEST_F(Dhcpv6SrvTest, userContext) {
+
+    IfaceMgrTestConfig test_config(true);
+
+    NakedDhcpv6Srv srv(0);
+
+    // This config has one subnet with user-context with one
+    // pool (also with context). Make sure the configuration could be accepted.
+    EXPECT_NO_THROW(configure(CONFIGS[3]));
+
+    // Now make sure the data was not lost.
+    ConstSrvConfigPtr cfg = CfgMgr::instance().getCurrentCfg();
+    const Subnet6Collection* subnets = cfg->getCfgSubnets6()->getAll();
+    ASSERT_TRUE(subnets);
+    ASSERT_EQ(1, subnets->size());
+
+    // Let's get the subnet and check its context.
+    Subnet6Ptr subnet1 = (*subnets)[0];
+    ASSERT_TRUE(subnet1);
+    ASSERT_TRUE(subnet1->getContext());
+    EXPECT_EQ("{ \"secure\": false }", subnet1->getContext()->str());
+
+    // Ok, not get the address pool in it and check its context, too.
+    PoolCollection pools = subnet1->getPools(Lease::TYPE_NA);
+    ASSERT_EQ(1, pools.size());
+    ASSERT_TRUE(pools[0]);
+    ASSERT_TRUE(pools[0]->getContext());
+    EXPECT_EQ("{ \"value\": 42 }", pools[0]->getContext()->str());
+
+    // Ok, not get the prefix pool in it and check its context, too.
+    pools = subnet1->getPools(Lease::TYPE_PD);
+    ASSERT_EQ(1, pools.size());
+    ASSERT_TRUE(pools[0]);
+    ASSERT_TRUE(pools[0]->getContext());
+    EXPECT_EQ("{ \"type\": \"prefixes\" }", pools[0]->getContext()->str());
+}
+
 
 /// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
 /// to call processX() methods.

+ 12 - 2
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -445,7 +445,7 @@ PoolParser::parse(PoolStoragePtr pools,
             isc_throw(isc::dhcp::DhcpConfigError, "User context has to be a map ("
                       << user_context->getPosition() << ")");
         }
-        pool->setUserContext(user_context);
+        pool->setContext(user_context);
     }
 
     // Parser pool specific options.
@@ -620,6 +620,16 @@ SubnetConfigParser::createSubnet(ConstElementPtr params) {
         subnet_->allowClientClass(client_class);
     }
 
+    // If there's user-context specified, store it.
+    ConstElementPtr user_context = params->get("user-context");
+    if (user_context) {
+        if (user_context->getType() != Element::map) {
+            isc_throw(isc::dhcp::DhcpConfigError, "User context has to be a map ("
+                      << user_context->getPosition() << ")");
+        }
+        subnet_->setContext(user_context);
+    }
+
     // Here globally defined options were merged to the subnet specific
     // options but this is no longer the case (they have a different
     // and not consecutive priority).
@@ -890,7 +900,7 @@ PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) {
     }
 
     if (user_context_) {
-        pool_->setUserContext(user_context_);
+        pool_->setContext(user_context_);
     }
 
     // Add the local pool to the external storage ptr.

+ 1 - 1
src/lib/dhcpsrv/pool.h

@@ -102,7 +102,7 @@ public:
 
     /// @brief Sets user context.
     /// @param ctx user context to be stored.
-    void setUserContext(const data::ConstElementPtr& ctx) {
+    void setContext(const data::ConstElementPtr& ctx) {
         user_context_ = ctx;
     }
 

+ 6 - 0
src/lib/dhcpsrv/subnet.cc

@@ -542,6 +542,12 @@ Subnet::toElement() const {
     ConstCfgOptionPtr opts = getCfgOption();
     map->set("option-data", opts->toElement());
 
+    // Add user-context, but only if defined. Omit if it was not.
+    ConstElementPtr ctx = getContext();
+    if (ctx) {
+        map->set("user-context", ctx);
+    }
+
     return (map);
 }
 

+ 14 - 0
src/lib/dhcpsrv/subnet.h

@@ -349,6 +349,17 @@ public:
         host_reservation_mode_ = mode;
     }
 
+    /// @brief Sets user context.
+    /// @param ctx user context to be stored.
+    void setContext(const data::ConstElementPtr& ctx) {
+        user_context_ = ctx;
+    }
+
+    /// @brief Returns const pointer to the user context.
+    data::ConstElementPtr getContext() const {
+        return (user_context_);
+    }
+
 protected:
     /// @brief Returns all pools (non-const variant)
     ///
@@ -518,6 +529,9 @@ protected:
     ///
     /// See @ref HRMode type for details.
     HRMode host_reservation_mode_;
+
+    /// @brief Pointer to the user context (may be NULL)
+    data::ConstElementPtr user_context_;
 private:
 
     /// @brief Pointer to the option data configuration for this subnet.

+ 34 - 0
src/lib/dhcpsrv/tests/pool_unittest.cc

@@ -7,6 +7,7 @@
 #include <config.h>
 
 #include <asiolink/io_address.h>
+#include <cc/data.h>
 #include <dhcp/option6_pdexclude.h>
 #include <dhcpsrv/pool.h>
 
@@ -21,6 +22,7 @@
 using boost::scoped_ptr;
 using namespace isc;
 using namespace isc::dhcp;
+using namespace isc::data;
 using namespace isc::asiolink;
 
 namespace {
@@ -172,6 +174,22 @@ TEST(Pool4Test, addOptions) {
     EXPECT_TRUE(options->empty());
 }
 
+// This test checks that handling for user-context is valid.
+TEST(Pool4Test, userContext) {
+    // Create a pool to add options to it.
+    Pool4Ptr pool(new Pool4(IOAddress("192.0.2.0"),
+                            IOAddress("192.0.2.255")));
+
+    // Context should be empty until explicitly set.
+    EXPECT_FALSE(pool->getContext());
+
+    // When set, should be returned properly.
+    ElementPtr ctx = Element::create("{ \"comment\": \"foo\" }");
+    EXPECT_NO_THROW(pool->setContext(ctx));
+    ASSERT_TRUE(pool->getContext());
+    EXPECT_EQ(ctx->str(), pool->getContext()->str());
+}
+
 TEST(Pool6Test, constructor_first_last) {
 
     // let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool
@@ -460,4 +478,20 @@ TEST(Pool6Test, addOptions) {
     EXPECT_TRUE(options->empty());
 }
 
+// This test checks that handling for user-context is valid.
+TEST(Pool6Test, userContext) {
+    // Create a pool to add options to it.
+    Pool6 pool(Lease::TYPE_NA, IOAddress("2001:db8::1"),
+                IOAddress("2001:db8::2"));
+
+    // Context should be empty until explicitly set.
+    EXPECT_FALSE(pool.getContext());
+
+    // When set, should be returned properly.
+    ElementPtr ctx = Element::create("{ \"comment\": \"foo\" }");
+    EXPECT_NO_THROW(pool.setContext(ctx));
+    ASSERT_TRUE(pool.getContext());
+    EXPECT_EQ(ctx->str(), pool.getContext()->str());
+}
+
 }; // end of anonymous namespace