<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
body {
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
        padding:1em;
        margin:auto;
        background:#fefefe;
}

h1, h2, h3, h4, h5, h6 {
        font-weight: bold;
}

h1 {
        color: #000000;
        font-size: 28pt;
}

h2 {
        border-bottom: 1px solid #CCCCCC;
        color: #000000;
        font-size: 24px;
}

h3 {
        font-size: 18px;
}

h4 {
        font-size: 16px;
}

h5 {
        font-size: 14px;
}

h6 {
        color: #777777;
        background-color: inherit;
        font-size: 14px;
}

hr {
        height: 0.2em;
        border: 0;
        color: #CCCCCC;
        background-color: #CCCCCC;
    display: inherit;
}

p, blockquote, ul, ol, dl, li, table, pre {
        margin: 15px 0;
}

a, a:visited {
        color: #4183C4;
        background-color: inherit;
        text-decoration: none;
}

#message {
        border-radius: 6px;
        border: 1px solid #ccc;
        display:block;
        width:100%;
        height:60px;
        margin:6px 0px;
}

button, #ws {
        font-size: 12 pt;
        padding: 4px 6px;
        border-radius: 5px;
        border: 1px solid #bbb;
        background-color: #eee;
}

code, pre, #ws, #message {
        font-family: Monaco;
        font-size: 10pt;
        border-radius: 3px;
        background-color: #F8F8F8;
        color: inherit;
}

code {
        border: 1px solid #EAEAEA;
        margin: 0 2px;
        padding: 0 5px;
}

pre {
        border: 1px solid #CCCCCC;
        overflow: auto;
        padding: 4px 8px;
}

pre > code {
        border: 0;
        margin: 0;
        padding: 0;
}

#ws { background-color: #f8f8f8; }


.bloop_markdown table {
border-collapse: collapse;  
font-family: Helvetica, arial, freesans, clean, sans-serif;  
color: rgb(51, 51, 51);  
font-size: 15px; line-height: 25px;
padding: 0; }

.bloop_markdown table tr {
border-top: 1px solid #cccccc;
background-color: white;
margin: 0;
padding: 0; }
     
.bloop_markdown table tr:nth-child(2n) {
background-color: #f8f8f8; }

.bloop_markdown table tr th {
font-weight: bold;
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }

.bloop_markdown table tr td {
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }

.bloop_markdown table tr th :first-child, table tr td :first-child {
margin-top: 0; }

.bloop_markdown table tr th :last-child, table tr td :last-child {
margin-bottom: 0; }

.bloop_markdown blockquote{
  border-left: 4px solid #dddddd;
  padding: 0 15px;
  color: #777777; }
  blockquote > :first-child {
    margin-top: 0; }
  blockquote > :last-child {
    margin-bottom: 0; }

code, pre, #ws, #message {
    word-break: normal;
    word-wrap: normal;
}

hr {
    display: inherit;
}

.bloop_markdown :first-child {
    -webkit-margin-before: 0;
}

code, pre, #ws, #message {
    font-family: Menlo, Consolas, Liberation Mono, Courier, monospace;
}


.send { color:#77bb77; }
.server { color:#7799bb; }
.error { color:#AA0000; }</style>
</head>
<body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;">
<div class="bloop_markdown">
<p>Hmm. CentOS 6 or 7? If 7, systemd (ugh) should be respawning it when it dies.</p>
<p></p>
</div>
<div class="bloop_original_html"><style>body{font-family:Helvetica,Arial;font-size:13px}</style>
<div id="bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px; color: rgba(0,0,0,1.0); margin: 0px; line-height: auto;">
<br>
</div>
<br>
<div id="bloop_sign_1486567150111900928" class="bloop_sign"></div>
<div class="airmail_ext_on" style="color:black"><br>
From: <span style="color:black">Bryan Perry</span> <a href="mailto:bryan@sfcn.org">
<bryan@sfcn.org></a><br>
Date: <span style="color:black">February 8, 2017 at 10:18:20 AM</span><br>
To: <span style="color:black">kea-users@lists.isc.org</span> <a href="mailto:kea-users@lists.isc.org">
<kea-users@lists.isc.org></a><br>
Subject: <span style="color:black"> Re: [Kea-users] Select subnet based on reservation?
<br>
</span></div>
<br>
<blockquote type="cite" class="clean_bq"><span>
<div>
<div></div>
<div>James,<br>
<br>
Since getting this library functional in May I have had roughly 6 to 8 <br>
instances where the Kea process just dies and goes away. I am not sure <br>
why or if it is an issue with my library, kea itself or the CentOS <br>
machine it's running on. After the first two instances of this happening <br>
I wrote a simple script that is run as a cron job that checks Kea every <br>
minute and if it has died restarts it. That was the stability problem.<br>
<br>
On the shared subnet handling I have another scalability challenge in my <br>
network. The upstream router that is acting as my DHCP relay will always <br>
send the client request from its primary IP address. This even killed <br>
regular DHCP lease responses from a second subnet since the request came <br>
from the first subnet ID. In order to get around this I had to enable a <br>
feature of that router that would try sending the DHCP requests from <br>
each secondary IP address after a DHCP request failure on the primary <br>
address. This works, but takes several seconds for the failure on the <br>
primary and then the request on the secondary. The time delay gets <br>
longer and longer the more subnets it has to get failures on while its <br>
going down the list. That's a painful delay trying to get an IP address <br>
on the network if you are in a subnet further down the list.<br>
<br>
The last requirement I have that has also been handled via the library I <br>
wrote for Kea has been logging lease assignments to a database for <br>
historical purposes (summons, subpoenas, etc.).<br>
<br>
Basically, until the shared subnet functionality works in Kea the way it <br>
does in DHCPD I don't really have a choice but to use DHCPD and put <br>
lease reservations for my static customers in the config file. Once upon <br>
a time I was also able to use DHCPD's 'on commit' functionality to write <br>
lease information to a database for historical tracking. I'll have to <br>
resurrect that as well.<br>
<br>
Good luck,<br>
Bryan<br>
<br>
On 2/8/2017 7:46 AM, James Sumners wrote:<br>
> Thank you Bryan for chiming in. That is exactly what I am thinking of<br>
> doing, and your code is a big help. I was just hoping to have a Kea API<br>
> for accessing the database instead of doing it directly.<br>
><br>
> What issues have you found that have lead you to conclude it is too<br>
> unstable?<br>
><br>
><br>
><br>
><br>
> On February 8, 2017 at 9:38:18 AM, Bryan Perry (bryan@sfcn.org<br>
> <mailto:bryan@sfcn.org>) wrote:<br>
><br>
>> Hi James,<br>
>><br>
>> At the risk of re-hashing this topic for everyone again... What you are<br>
>> facing is a very unfortunate shortcoming of Kea's ability to deal with<br>
>> shared subnets that I hope they solve in the future. I faced the same<br>
>> issue and took the approach of writing a custom library that uses Kea's<br>
>> hooks and intercepted the DHCP Discover, queried the database for lease<br>
>> reservations matching the MAC address and if found, changed the subnet<br>
>> ID to the correct ID and handed it back to Kea for processing.<br>
>><br>
>> So yes, it can be done, but it is not trivial and very specific to your<br>
>> own implementation. I am running Kea this way now, but this hasn't been<br>
>> the most stable approach. This along with a few other issues related to<br>
>> shared subnet scenarios are unfortunately going to require that I move<br>
>> back to the standard ISC DHCPD.<br>
>><br>
>> I am pasting below some of my final correspondence with a couple of the<br>
>> more knowledgeable Kea folks back in May when I got this running. The<br>
>> code I used to compile my library is also pasted in case it helps you<br>
>> see just how cumbersome of a task this was, or otherwise helps you in<br>
>> your project. It is certainly not anything I am an expert on or can<br>
>> provide technical support for, sorry, but I hope it helps.<br>
>><br>
>> -Bryan<br>
>><br>
>><br>
>><br>
>><br>
>><br>
>><br>
>><br>
>><br>
>> Thomas, Marcin,<br>
>><br>
>> With your help I have been able to get this working exactly as I needed<br>
>> it to. The subnet4_select callout checks my hosts table for a static<br>
>> reservation and, if found, updates the assigned subnet ID. Kea then<br>
>> picks up processing with the new subnet ID and can successfully assign<br>
>> the correct static IP address to the client.<br>
>><br>
>> Although it's surely not the most elegant, I will paste in my code below<br>
>> in case it is helpful for anyone else dealing with shared subnets and<br>
>> static reservations. If you see anything wrong with the code or problems<br>
>> I may cause, I'm always open to feedback, but it seems to be working<br>
>> great right now.<br>
>><br>
>> I also had the requirements to write some basic info out to a more human<br>
>> readable log as well as writing all IP address assignments out to a<br>
>> completely different database for required lease history tracking. I<br>
>> used the lease4_select callout for the lease history storage. I'll paste<br>
>> that code as well in case it's of any use to anyone else.<br>
>><br>
>> Hopefully later versions of Kea will have some of this functionality<br>
>> built in.<br>
>><br>
>> Thanks again for all your help. I couldn't have got this working without<br>
>> you guys.<br>
>><br>
>> -Bryan<br>
>><br>
>><br>
>><br>
>><br>
>><br>
>><br>
>><br>
>> // subnet4_select.cc<br>
>><br>
>> #include <hooks/hooks.h><br>
>> #include <dhcp/pkt4.h><br>
>> #include <dhcpsrv/subnet.h><br>
>> #include <dhcpsrv/mysql_connection.h><br>
>> #include <algorithm/string.hpp><br>
>> #include "library_common.h"<br>
>> #include <string><br>
>><br>
>> using namespace isc::dhcp;<br>
>> using namespace isc::hooks;<br>
>> using namespace std;<br>
>><br>
>> // Define a structure for the mysql connection instance<br>
>> struct connection_details {<br>
>> const char *server;<br>
>> const char *user;<br>
>> const char *password;<br>
>> const char *database;<br>
>> };<br>
>><br>
>> MYSQL_RES *Result; // The mysql query results set variable<br>
>><br>
>> // A function to establish the mysql connection<br>
>> MYSQL* mysql_connection_setup2(struct connection_details<br>
>> mysql_details) {<br>
>> // Create a mysql instance and initialize the variables<br>
>> MYSQL *connection = mysql_init(NULL);<br>
>><br>
>> // Connect to the database with the details in the connection<br>
>> instance<br>
>> if (!mysql_real_connect(connection,mysql_details.server,<br>
>> mysql_details.user, mysql_details.password, mysql_details.database, 0,<br>
>> NULL, 0)) {<br>
>> printf("Conection error : %s\n", mysql_error(connection));<br>
>> //exit(1);<br>
>> }<br>
>> return connection;<br>
>> }<br>
>><br>
>> // A function to run the query and return the results set<br>
>> MYSQL_RES* mysql_perform_query2(MYSQL *connection, const char<br>
>> *sql_query) {<br>
>> // send the query to the database<br>
>> // cout << sql_query << " in function mysql_perform_query2" << endl;<br>
>> if (mysql_query(connection, sql_query))<br>
>> {<br>
>> printf("MySQL query error : %s\n", mysql_error(connection));<br>
>> // exit(1);<br>
>> }<br>
>> Result = mysql_store_result(connection);<br>
>> int RowsReturned = mysql_num_rows( Result );<br>
>> // cout << "NumRows in the function: " << RowsReturned << endl;<br>
>> // return mysql_use_result(connection);<br>
>> return Result;<br>
>> }<br>
>><br>
>> extern "C" {<br>
>><br>
>> // This callout is called at the "subnet4_select" hook.<br>
>> // We will intercept the request, check the MySQL hosts table<br>
>> // for reservations, and if found, set the correct subnet ID<br>
>><br>
>> int subnet4_select(CalloutHandle& handle) {<br>
>><br>
>> // A pointer to the packet is passed to the callout via a "boost" smart<br>
>> // pointer. The include file "pkt4.h" typedefs a pointer to the Pkt4<br>
>> // object as Pkt4Ptr. Retrieve a pointer to the object.<br>
>> Pkt4Ptr query4_ptr;<br>
>> handle.getArgument("query4", query4_ptr);<br>
>><br>
>> // Get a pointer to the subnet4 object<br>
>> Subnet4Ptr subnet4_ptr;<br>
>> handle.getArgument("subnet4", subnet4_ptr);<br>
>><br>
>> // Get the collection of subnets from the callout argument set<br>
>> const isc::dhcp::Subnet4Collection* subnets;<br>
>> handle.getArgument("subnet4collection", subnets);<br>
>><br>
>> // Get a pointer to the hardware address.<br>
>> HWAddrPtr hwaddr_ptr = query4_ptr->getHWAddr();<br>
>><br>
>> // Get the subnet ID from the initial request<br>
>> SubnetID subnetId = subnet4_ptr->getID();<br>
>><br>
>> // Exclude hardware type from hardware address string<br>
>> bool include_htype=false;<br>
>> string colonized_hwaddr = hwaddr_ptr->toText(include_htype);<br>
>><br>
>> // Strip colons from colonized_hwaddr to get hwaddr<br>
>> string hwaddr = colonized_hwaddr;<br>
>> boost::erase_all(hwaddr, ":");<br>
>><br>
>> // Define some variables we will use<br>
>> int newSubnetId = 0;<br>
>> string ipaddr = "BLANK";<br>
>> long RowsReturned;<br>
>><br>
>> // cout << hwaddr << " wants subnet " << subnetId << "\n";<br>
>> // Use a try...catch here to help prevent MySQL errors from killing<br>
>> the server.<br>
>> try {<br>
>> // Here is where we query the database and set new subnet ID if<br>
>> necessary<br>
>> MYSQL *conn2; // the connection<br>
>> MYSQL_RES *res2; // the results<br>
>> MYSQL_ROW row2; // the results row (line by line)<br>
>><br>
>> struct connection_details mysqlD2;<br>
>> mysqlD2.server = "localhost"; // where the mysql database is<br>
>> mysqlD2.user = "********"; // the root user of mysql<br>
>> mysqlD2.password = "********"; // the password of the root user<br>
>> in mysql<br>
>> mysqlD2.database = "********"; // the databse to pick<br>
>><br>
>> // Build the query string<br>
>> ostringstream ss2;<br>
>> ss2 << "select hex(dhcp_identifier), dhcp4_subnet_id,<br>
>> INET_NTOA(ipv4_address), host_id from hosts where hex(dhcp_identifier) =<br>
>> '" << hwaddr << "' limit 1";<br>
>> string ss2_str = ss2.str();<br>
>> const char *full_query2 = ss2_str.c_str();<br>
>><br>
>> // Connect to the mysql database<br>
>> conn2 = mysql_connection_setup2(mysqlD2);<br>
>><br>
>> // Assign the results returned to res2 if valid<br>
>> if (res2 = mysql_perform_query2(conn2, full_query2)) {<br>
>> // Get the number of rows in the results set. Should be 0 or 1<br>
>> int rCount = mysql_num_rows(res2);<br>
>> // If we got more than 0 rows in the results then we found a<br>
>> reservation for that client<br>
>> if (rCount > 0) {<br>
>> while ((row2 = mysql_fetch_row(res2)) !=NULL) {<br>
>> // Convert the subnet ID to an integer for use further on<br>
>> string input(row2[1]); stringstream SS(input); SS >><br>
>> newSubnetId;<br>
>> // Handle a NULL ipv4_address field<br>
>> if (row2[2]) {<br>
>> ipaddr = row2[2];<br>
>> } // end if<br>
>> // Log that we found a reservation<br>
>> leaselog << "Static reservation " << ipaddr << " found for<br>
>> " << row2[0] << " on row " << row2[3] << ", subnet " << row2[1] << endl;<br>
>> }<br>
>> } // end if<br>
>> // cout << "New subnet ID: " << newSubnetId << endl;<br>
>><br>
>> // Clean up the database result set<br>
>> mysql_free_result(res2);<br>
>><br>
>> // Clean up the database connectio<br>
>> mysql_close(conn2);<br>
>> } // end if<br>
>> else {<br>
>> // Throw an error if something went wrong in the mysql lookup<br>
>> leaselog << "MySQL query failed in lookup for " << hwaddr << " --<br>
>> Results processing skipped." << endl;<br>
>> } // end else<br>
>><br>
>> } catch (...) {<br>
>> // Throw an error if something more serious happened<br>
>> leaselog << "# ERR: Caught an error in subnet4_select.cc" << endl;<br>
>> }<br>
>><br>
>><br>
>><br>
>> // Next, if we have to change the subnet ID, we iterate through the<br>
>> collection<br>
>> // of subnets, looking for the ID we want and then set it.<br>
>> if ((newSubnetId != 0) && (subnetId != newSubnetId)) {<br>
>> for (int i = 0; i < subnets->size(); ++i) {<br>
>> Subnet4Ptr new_subnet = (*subnets)[i];<br>
>> if (new_subnet->getID() == newSubnetId) {<br>
>> // id matched so replace the selected subnet by<br>
>> // setting the "subnet4" callout argument with<br>
>> // the new subnet<br>
>> handle.setArgument("subnet4", new_subnet);<br>
>> break;<br>
>> }<br>
>> }<br>
>> }<br>
>><br>
>> flush(leaselog);<br>
>> return (0);<br>
>><br>
>> };<br>
>><br>
>> }<br>
>><br>
>><br>
>><br>
>><br>
>><br>
>><br>
>><br>
>><br>
>> // lease4_select.cc<br>
>><br>
>> #include <hooks/hooks.h><br>
>> #include <dhcp/pkt4.h><br>
>> #include <dhcpsrv/lease.h><br>
>> #include <dhcpsrv/mysql_connection.h><br>
>> #include "library_common.h"<br>
>> #include <string><br>
>><br>
>> using namespace isc::dhcp;<br>
>> using namespace isc::hooks;<br>
>> using namespace std;<br>
>><br>
>> // Define a structure for the mysql connection instance<br>
>> struct connection_details {<br>
>> const char *server;<br>
>> const char *user;<br>
>> const char *password;<br>
>> const char *database;<br>
>> };<br>
>><br>
>> // A function to establish the mysql connection<br>
>> MYSQL* mysql_connection_setup(struct connection_details<br>
>> mysql_details) {<br>
>> // first of all create a mysql instance and initialize the<br>
>> variables within<br>
>> MYSQL *connection = mysql_init(NULL);<br>
>><br>
>> // connect to the database with the details attached.<br>
>> if (!mysql_real_connect(connection,mysql_details.server,<br>
>> mysql_details.user, mysql_details.password, mysql_details.database, 0,<br>
>> NULL, 0)) {<br>
>> printf("Conection error : %s\n", mysql_error(connection));<br>
>> // exit(1);<br>
>> }<br>
>> return connection;<br>
>> }<br>
>><br>
>> // A function to run the query and return the results set<br>
>> MYSQL_RES* mysql_perform_query(MYSQL *connection, const char<br>
>> *sql_query) {<br>
>> // send the query to the database<br>
>> // cout << sql_query << " in function mysql_perform_query" << endl;<br>
>> if (mysql_query(connection, sql_query))<br>
>> {<br>
>> printf("MySQL query error : %s\n", mysql_error(connection));<br>
>> // exit(1);<br>
>> }<br>
>> return mysql_use_result(connection);<br>
>> }<br>
>><br>
>><br>
>> extern "C" {<br>
>><br>
>> // This callout is called at the "lease4_select" hook.<br>
>> int lease4_select(CalloutHandle& handle) {<br>
>><br>
>> // A pointer to the object is passed to the callout via a "boost" smart<br>
>> // pointer. The include file "lease.h" typedefs a pointer to the Lease4<br>
>> // object as Lease4Ptr. Retrieve a pointer to the object.<br>
>> Lease4Ptr lease4_ptr;<br>
>> handle.getArgument("lease4", lease4_ptr);<br>
>> bool isFake;<br>
>> handle.getArgument("fake_allocation", isFake);<br>
>><br>
>> // Get a pointer to the hardware address.<br>
>> HWAddrPtr hwaddr_ptr = lease4_ptr->hwaddr_;<br>
>><br>
>> string ipaddr = lease4_ptr->addr_.toText();<br>
>> int subnetId = lease4_ptr->subnet_id_;<br>
>><br>
>> bool include_htype=false;<br>
>> string hwaddr = hwaddr_ptr->toText(include_htype);<br>
>><br>
>> // If it is DHCPACK and not just DHCPOFFER, write to log and database<br>
>> if (!isFake) {<br>
>><br>
>> leaselog << "ASSIGNED: " << ipaddr << " to " << hwaddr << "<br>
>> subnetID " << subnetId << "\n";<br>
>><br>
>> // Use a try...catch here to help prevent MySQL errors from killing<br>
>> the server.<br>
>> try {<br>
>> // Here is where we query the database and set new subnet ID if<br>
>> necessary<br>
>> MYSQL *conn; // the connection<br>
>> MYSQL_RES *res; // the results<br>
>> MYSQL_ROW row; // the results row (line by line)<br>
>><br>
>> struct connection_details mysqlD;<br>
>> mysqlD.server = "localhost"; // where the mysql database is<br>
>> mysqlD.user = "********"; // the root user of mysql<br>
>> mysqlD.password = "********"; // the password of the root user in<br>
>> mysql<br>
>> mysqlD.database = "********"; // the databse to pick<br>
>><br>
>> // connect to the mysql database<br>
>> conn = mysql_connection_setup(mysqlD);<br>
>><br>
>> // Build the query<br>
>> std::stringstream ss;<br>
>> ss << "INSERT INTO lease_history VALUES ('" << ipaddr << "', '" <<<br>
>> hwaddr << "', '" << subnetId << "')";<br>
>> const char *full_query = ss.str().c_str();<br>
>> // leaselog << full_query << endl;<br>
>><br>
>> // assign the results return to the MYSQL_RES pointer<br>
>> res = mysql_perform_query(conn, full_query);<br>
>><br>
>> // printf("MySQL Tables in mysql database:\n");<br>
>> // while ((row = mysql_fetch_row(res)) !=NULL)<br>
>> // printf("%s\n", row[0]);<br>
>><br>
>> /* clean up the database result set */<br>
>> mysql_free_result(res);<br>
>> /* clean up the database link */<br>
>> mysql_close(conn);<br>
>> } catch (...) {<br>
>> // Throw an error if something bad happened<br>
>> leaselog << "# ERR: Caught an error in lease4_select.cc" << endl;<br>
>> }<br>
>><br>
>><br>
>> } //End if (!isFake)<br>
>><br>
>> flush(leaselog);<br>
>> return (0);<br>
>> };<br>
>> }<br>
>><br>
>><br>
>><br>
>> On 2/8/2017 6:22 AM, James Sumners wrote:<br>
>> > Before I move on, would the following be available to a hook?:<br>
>> ><br>
>> > 1. The subnet identifiers for each configured subnet<br>
>> > 2. The database of host reservations<br>
>> ><br>
>> > Get Outlook for iOS <https://aka.ms/o0ukef><br>
>> ><br>
>> > ------------------------------------------------------------------------<br>
>> > *From:* Tomek Mrugalski <tomasz@isc.org><br>
>> > *Sent:* Tuesday, February 7, 2017 3:39:01 PM<br>
>> > *To:* James Sumners; kea-users@lists.isc.org<br>
>> > *Subject:* Re: [Kea-users] Select subnet based on reservation?<br>
>> ><br>
>> > W dniu 07.02.2017 o 21:31, James Sumners pisze:<br>
>> >> 1.3? 1.2 is too far off. Sadly, I’m just going to have to go back to<br>
>> >> looking for alternatives. That’s a shame,<br>
>> > Sorry to hear that. We already have our plans for 1.2 and we can't add<br>
>> > new features less than 3 months before the release. The shared subnets<br>
>> > scenario is not a trivial feature to implement, as it affects the logic<br>
>> > in many places.<br>
>> ><br>
>> >> because I was really liking Kea.<br>
>> > Thanks for your kind words. I hope you'll take a look at Kea again some<br>
>> > time in the future.<br>
>> ><br>
>> > Tomek<br>
>> ><br>
>> ><br>
>> ><br>
>> ><br>
>> ><br>
>> > _______________________________________________<br>
>> > Kea-users mailing list<br>
>> > Kea-users@lists.isc.org<br>
>> > https://lists.isc.org/mailman/listinfo/kea-users<br>
>> ><br>
>> _______________________________________________<br>
>> Kea-users mailing list<br>
>> Kea-users@lists.isc.org<br>
>> https://lists.isc.org/mailman/listinfo/kea-users<br>
><br>
_______________________________________________<br>
Kea-users mailing list<br>
Kea-users@lists.isc.org<br>
https://lists.isc.org/mailman/listinfo/kea-users<br>
</div>
</div>
</span></blockquote>
</div>
<div class="bloop_markdown">
<p></p>
</div>
</body>
</html>