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