From: Thomas Markwalder Date: Sat, 10 Nov 2018 18:20:22 +0000 (-0500) Subject: [#260,!120] Packet queueing is now optional X-Git-Tag: 204-move-models-base~4^2^2~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eb244004882b42006159f0a33cee8c480b18ff26;p=thirdparty%2Fkea.git [#260,!120] Packet queueing is now optional src/bin/dhcp<4/6>/ctrl_dhcp<4/6>_srv.cc ControlledDhcpv<4/6>Srv::processConfig() - now calls IfaceMgr::configureDHCPPacketQueue src/bin/dhcp<4/6>/dhcp<4/6>_parser.yy dhpc-queue-control parsing updated to enforce enable-queue/queue-type rules src/bin/dhcp<4/6>/tests/config_parser_unittest.cc TEST_F(Dhcp<4/6>ParserTest, dhcpQueueControl) TEST_F(Dhcp<4/6>ParserTest, dhcpQueueControlInvalid) src/lib/dhcp/iface_mgr.* IfaceMgr - closeSockets() - now calls stopDHCPReceiver - openSockets<4/6>() - now calls startDHCPReceiver - receive<4/6>Indirect() - new function which monitors receiver thread watch sockets, reads DHCP packets from queue - receive<4/6>Direct() - new function which monitors and reads DHCP packets from interface sockets directly - receive<4/6>() - rewritten to call receive<4/6>Indirect if receiver thread is running, otherwise it calls receive<4/6>Direct - configureDHCPPacketQueue() - new function which either enables queuing by creating a new packet queue, or disables it by destroying the existing queue src/lib/dhcp/packet_queue_mgr.h PacketQueue::destroyPacketQueue() - new function src/lib/dhcp/packet_queue_mgr<4/6>.cc PacketQueueMgr<4/6>::PacketQueueMgr<4/6>() - no longer creates a default packet queue src/lib/dhcpsrv/cfg_iface.cc CfgIface::closeSockets() - removed call to stopDHCPReceiver CfgIface::openSockets() - removed call to startDHCPReceiver src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.* DHCPQueueControlParser - removed unused family_ member - parse() - added support for enable-queue src/lib/dhcpsrv/tests/dhcp_queue_control_parser_unittest.cc - new file --- diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index 20b78029b7..b78154bc55 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -634,24 +634,15 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) { return (isc::config::createAnswer(1, err.str())); } - // Configure packet queue + // Configure DHCP packet queueing try { data::ConstElementPtr qc; qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl(); - if (!qc) { - // @todo For now we're manually constructing default queue config - // This probably needs to be built into the PQM? - data::ElementPtr default_qc = data::Element::createMap(); - default_qc->set("queue-type", data::Element::create("kea-ring4")); - default_qc->set("capacity", data::Element::create(static_cast(500))); - PacketQueueMgr4::instance().createPacketQueue(default_qc); - } else { - PacketQueueMgr4::instance().createPacketQueue(qc); + if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) { + LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CONFIG_PACKET_QUEUE) + .arg(PacketQueueMgr4::instance().getPacketQueue()->getInfoStr()); } - LOG_DEBUG(dhcp4_logger, DBG_DHCP4_BASIC, DHCP4_CONFIG_PACKET_QUEUE) - .arg(PacketQueueMgr4::instance().getPacketQueue()->getInfoStr()); - } catch (const std::exception& ex) { err << "Error setting packet queue controls after server reconfiguration: " << ex.what(); diff --git a/src/bin/dhcp4/dhcp4_parser.cc b/src/bin/dhcp4/dhcp4_parser.cc index 3f883f5bcf..97942a4f9d 100644 --- a/src/bin/dhcp4/dhcp4_parser.cc +++ b/src/bin/dhcp4/dhcp4_parser.cc @@ -2980,653 +2980,680 @@ namespace isc { namespace dhcp { ElementPtr qc = yystack_[0].value.as< ElementPtr > (); ctx.stack_.back()->set("dhcp-queue-control", qc); - if (!qc->contains("queue-type")) { + // Doing this manually, because dhcp-queue-control + // content is otherwise arbitrary + if (!qc->contains("enable-queue")) { std::stringstream msg; - msg << "'queue-type' is required: "; + msg << "'enable-queue' is required: "; msg << qc->getPosition().str() << ")"; error(yystack_[3].location, msg.str()); } + ConstElementPtr enable_queue = qc->get("enable-queue"); + if (enable_queue->getType() != Element::boolean) { + std::stringstream msg; + msg << "'enable-queue' must be boolean: "; + msg << qc->getPosition().str() << ")"; + error(yystack_[3].location, msg.str()); + } + + if (enable_queue->boolValue()) { + if (!qc->contains("queue-type")) { + std::stringstream msg; + msg << "'queue-type' is required, when 'enable-queue' is true: "; + msg << qc->getPosition().str() << ")"; + error(yystack_[3].location, msg.str()); + } + + ConstElementPtr queue_type = qc->get("queue-type"); + if (queue_type->getType() != Element::string) { + std::stringstream msg; + msg << "'queue-type' must be a string: "; + msg << qc->getPosition().str() << ")"; + error(yystack_[3].location, msg.str()); + } + } + ctx.leave(); } -#line 2993 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3020 "dhcp4_parser.cc" // lalr1.cc:859 break; case 534: -#line 1859 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1886 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("dhcp-ddns", m); ctx.stack_.push_back(m); ctx.enter(ctx.DHCP_DDNS); } -#line 3004 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3031 "dhcp4_parser.cc" // lalr1.cc:859 break; case 535: -#line 1864 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1891 "dhcp4_parser.yy" // lalr1.cc:859 { // The enable updates DHCP DDNS parameter is required. ctx.require("enable-updates", ctx.loc2pos(yystack_[2].location), ctx.loc2pos(yystack_[0].location)); ctx.stack_.pop_back(); ctx.leave(); } -#line 3015 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3042 "dhcp4_parser.cc" // lalr1.cc:859 break; case 536: -#line 1871 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1898 "dhcp4_parser.yy" // lalr1.cc:859 { // Parse the dhcp-ddns map ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.push_back(m); } -#line 3025 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3052 "dhcp4_parser.cc" // lalr1.cc:859 break; case 537: -#line 1875 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1902 "dhcp4_parser.yy" // lalr1.cc:859 { // The enable updates DHCP DDNS parameter is required. ctx.require("enable-updates", ctx.loc2pos(yystack_[3].location), ctx.loc2pos(yystack_[0].location)); // parsing completed } -#line 3035 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3062 "dhcp4_parser.cc" // lalr1.cc:859 break; case 559: -#line 1906 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1933 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr b(new BoolElement(yystack_[0].value.as< bool > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("enable-updates", b); } -#line 3044 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3071 "dhcp4_parser.cc" // lalr1.cc:859 break; case 560: -#line 1911 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1938 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3052 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3079 "dhcp4_parser.cc" // lalr1.cc:859 break; case 561: -#line 1913 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1940 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("qualifying-suffix", s); ctx.leave(); } -#line 3062 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3089 "dhcp4_parser.cc" // lalr1.cc:859 break; case 562: -#line 1919 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1946 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3070 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3097 "dhcp4_parser.cc" // lalr1.cc:859 break; case 563: -#line 1921 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1948 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("server-ip", s); ctx.leave(); } -#line 3080 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3107 "dhcp4_parser.cc" // lalr1.cc:859 break; case 564: -#line 1927 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1954 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr i(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("server-port", i); } -#line 3089 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3116 "dhcp4_parser.cc" // lalr1.cc:859 break; case 565: -#line 1932 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1959 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3097 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3124 "dhcp4_parser.cc" // lalr1.cc:859 break; case 566: -#line 1934 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1961 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("sender-ip", s); ctx.leave(); } -#line 3107 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3134 "dhcp4_parser.cc" // lalr1.cc:859 break; case 567: -#line 1940 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1967 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr i(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("sender-port", i); } -#line 3116 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3143 "dhcp4_parser.cc" // lalr1.cc:859 break; case 568: -#line 1945 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1972 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr i(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("max-queue-size", i); } -#line 3125 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3152 "dhcp4_parser.cc" // lalr1.cc:859 break; case 569: -#line 1950 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1977 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NCR_PROTOCOL); } -#line 3133 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3160 "dhcp4_parser.cc" // lalr1.cc:859 break; case 570: -#line 1952 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1979 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.stack_.back()->set("ncr-protocol", yystack_[0].value.as< ElementPtr > ()); ctx.leave(); } -#line 3142 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3169 "dhcp4_parser.cc" // lalr1.cc:859 break; case 571: -#line 1958 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1985 "dhcp4_parser.yy" // lalr1.cc:859 { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("UDP", ctx.loc2pos(yystack_[0].location))); } -#line 3148 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3175 "dhcp4_parser.cc" // lalr1.cc:859 break; case 572: -#line 1959 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1986 "dhcp4_parser.yy" // lalr1.cc:859 { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("TCP", ctx.loc2pos(yystack_[0].location))); } -#line 3154 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3181 "dhcp4_parser.cc" // lalr1.cc:859 break; case 573: -#line 1962 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1989 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NCR_FORMAT); } -#line 3162 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3189 "dhcp4_parser.cc" // lalr1.cc:859 break; case 574: -#line 1964 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1991 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr json(new StringElement("JSON", ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("ncr-format", json); ctx.leave(); } -#line 3172 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3199 "dhcp4_parser.cc" // lalr1.cc:859 break; case 575: -#line 1970 "dhcp4_parser.yy" // lalr1.cc:859 +#line 1997 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr b(new BoolElement(yystack_[0].value.as< bool > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("always-include-fqdn", b); } -#line 3181 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3208 "dhcp4_parser.cc" // lalr1.cc:859 break; case 576: -#line 1975 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2002 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr b(new BoolElement(yystack_[0].value.as< bool > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("override-no-update", b); } -#line 3190 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3217 "dhcp4_parser.cc" // lalr1.cc:859 break; case 577: -#line 1980 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2007 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr b(new BoolElement(yystack_[0].value.as< bool > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("override-client-update", b); } -#line 3199 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3226 "dhcp4_parser.cc" // lalr1.cc:859 break; case 578: -#line 1985 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2012 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.REPLACE_CLIENT_NAME); } -#line 3207 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3234 "dhcp4_parser.cc" // lalr1.cc:859 break; case 579: -#line 1987 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2014 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.stack_.back()->set("replace-client-name", yystack_[0].value.as< ElementPtr > ()); ctx.leave(); } -#line 3216 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3243 "dhcp4_parser.cc" // lalr1.cc:859 break; case 580: -#line 1993 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2020 "dhcp4_parser.yy" // lalr1.cc:859 { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("when-present", ctx.loc2pos(yystack_[0].location))); } -#line 3224 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3251 "dhcp4_parser.cc" // lalr1.cc:859 break; case 581: -#line 1996 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2023 "dhcp4_parser.yy" // lalr1.cc:859 { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("never", ctx.loc2pos(yystack_[0].location))); } -#line 3232 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3259 "dhcp4_parser.cc" // lalr1.cc:859 break; case 582: -#line 1999 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2026 "dhcp4_parser.yy" // lalr1.cc:859 { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("always", ctx.loc2pos(yystack_[0].location))); } -#line 3240 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3267 "dhcp4_parser.cc" // lalr1.cc:859 break; case 583: -#line 2002 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2029 "dhcp4_parser.yy" // lalr1.cc:859 { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("when-not-present", ctx.loc2pos(yystack_[0].location))); } -#line 3248 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3275 "dhcp4_parser.cc" // lalr1.cc:859 break; case 584: -#line 2005 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2032 "dhcp4_parser.yy" // lalr1.cc:859 { error(yystack_[0].location, "boolean values for the replace-client-name are " "no longer supported"); } -#line 3257 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3284 "dhcp4_parser.cc" // lalr1.cc:859 break; case 585: -#line 2011 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2038 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3265 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3292 "dhcp4_parser.cc" // lalr1.cc:859 break; case 586: -#line 2013 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2040 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("generated-prefix", s); ctx.leave(); } -#line 3275 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3302 "dhcp4_parser.cc" // lalr1.cc:859 break; case 587: -#line 2019 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2046 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3283 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3310 "dhcp4_parser.cc" // lalr1.cc:859 break; case 588: -#line 2021 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2048 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("hostname-char-set", s); ctx.leave(); } -#line 3293 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3320 "dhcp4_parser.cc" // lalr1.cc:859 break; case 589: -#line 2027 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2054 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3301 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3328 "dhcp4_parser.cc" // lalr1.cc:859 break; case 590: -#line 2029 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2056 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("hostname-char-replacement", s); ctx.leave(); } -#line 3311 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3338 "dhcp4_parser.cc" // lalr1.cc:859 break; case 591: -#line 2038 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2065 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3319 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3346 "dhcp4_parser.cc" // lalr1.cc:859 break; case 592: -#line 2040 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2067 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.stack_.back()->set("Dhcp6", yystack_[0].value.as< ElementPtr > ()); ctx.leave(); } -#line 3328 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3355 "dhcp4_parser.cc" // lalr1.cc:859 break; case 593: -#line 2045 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2072 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3336 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3363 "dhcp4_parser.cc" // lalr1.cc:859 break; case 594: -#line 2047 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2074 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.stack_.back()->set("DhcpDdns", yystack_[0].value.as< ElementPtr > ()); ctx.leave(); } -#line 3345 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3372 "dhcp4_parser.cc" // lalr1.cc:859 break; case 595: -#line 2052 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2079 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3353 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3380 "dhcp4_parser.cc" // lalr1.cc:859 break; case 596: -#line 2054 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2081 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.stack_.back()->set("Control-agent", yystack_[0].value.as< ElementPtr > ()); ctx.leave(); } -#line 3362 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3389 "dhcp4_parser.cc" // lalr1.cc:859 break; case 597: -#line 2059 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2086 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->add(m); ctx.stack_.push_back(m); } -#line 3372 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3399 "dhcp4_parser.cc" // lalr1.cc:859 break; case 598: -#line 2063 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2090 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); } -#line 3380 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3407 "dhcp4_parser.cc" // lalr1.cc:859 break; case 599: -#line 2068 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2095 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr i(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("config-control", i); ctx.stack_.push_back(i); ctx.enter(ctx.CONFIG_CONTROL); } -#line 3391 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3418 "dhcp4_parser.cc" // lalr1.cc:859 break; case 600: -#line 2073 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2100 "dhcp4_parser.yy" // lalr1.cc:859 { // No config control params are required ctx.stack_.pop_back(); ctx.leave(); } -#line 3401 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3428 "dhcp4_parser.cc" // lalr1.cc:859 break; case 601: -#line 2079 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2106 "dhcp4_parser.yy" // lalr1.cc:859 { // Parse the config-control map ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.push_back(m); } -#line 3411 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3438 "dhcp4_parser.cc" // lalr1.cc:859 break; case 602: -#line 2083 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2110 "dhcp4_parser.yy" // lalr1.cc:859 { // No config_control params are required // parsing completed } -#line 3420 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3447 "dhcp4_parser.cc" // lalr1.cc:859 break; case 607: -#line 2098 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2125 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("config-databases", l); ctx.stack_.push_back(l); ctx.enter(ctx.CONFIG_DATABASE); } -#line 3431 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3458 "dhcp4_parser.cc" // lalr1.cc:859 break; case 608: -#line 2103 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2130 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); ctx.leave(); } -#line 3440 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3467 "dhcp4_parser.cc" // lalr1.cc:859 break; case 609: -#line 2113 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2140 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("Logging", m); ctx.stack_.push_back(m); ctx.enter(ctx.LOGGING); } -#line 3451 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3478 "dhcp4_parser.cc" // lalr1.cc:859 break; case 610: -#line 2118 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2145 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); ctx.leave(); } -#line 3460 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3487 "dhcp4_parser.cc" // lalr1.cc:859 break; case 611: -#line 2123 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2150 "dhcp4_parser.yy" // lalr1.cc:859 { // Parse the Logging map ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.push_back(m); } -#line 3470 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3497 "dhcp4_parser.cc" // lalr1.cc:859 break; case 612: -#line 2127 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2154 "dhcp4_parser.yy" // lalr1.cc:859 { // parsing completed } -#line 3478 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3505 "dhcp4_parser.cc" // lalr1.cc:859 break; case 616: -#line 2143 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2170 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("loggers", l); ctx.stack_.push_back(l); ctx.enter(ctx.LOGGERS); } -#line 3489 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3516 "dhcp4_parser.cc" // lalr1.cc:859 break; case 617: -#line 2148 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2175 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); ctx.leave(); } -#line 3498 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3525 "dhcp4_parser.cc" // lalr1.cc:859 break; case 620: -#line 2160 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2187 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr l(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->add(l); ctx.stack_.push_back(l); } -#line 3508 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3535 "dhcp4_parser.cc" // lalr1.cc:859 break; case 621: -#line 2164 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2191 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); } -#line 3516 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3543 "dhcp4_parser.cc" // lalr1.cc:859 break; case 631: -#line 2181 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2208 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr dl(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("debuglevel", dl); } -#line 3525 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3552 "dhcp4_parser.cc" // lalr1.cc:859 break; case 632: -#line 2186 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2213 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3533 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3560 "dhcp4_parser.cc" // lalr1.cc:859 break; case 633: -#line 2188 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2215 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr sev(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("severity", sev); ctx.leave(); } -#line 3543 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3570 "dhcp4_parser.cc" // lalr1.cc:859 break; case 634: -#line 2194 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2221 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("output_options", l); ctx.stack_.push_back(l); ctx.enter(ctx.OUTPUT_OPTIONS); } -#line 3554 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3581 "dhcp4_parser.cc" // lalr1.cc:859 break; case 635: -#line 2199 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2226 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); ctx.leave(); } -#line 3563 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3590 "dhcp4_parser.cc" // lalr1.cc:859 break; case 638: -#line 2208 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2235 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->add(m); ctx.stack_.push_back(m); } -#line 3573 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3600 "dhcp4_parser.cc" // lalr1.cc:859 break; case 639: -#line 2212 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2239 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); } -#line 3581 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3608 "dhcp4_parser.cc" // lalr1.cc:859 break; case 646: -#line 2226 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2253 "dhcp4_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3589 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3616 "dhcp4_parser.cc" // lalr1.cc:859 break; case 647: -#line 2228 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2255 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr sev(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("output", sev); ctx.leave(); } -#line 3599 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3626 "dhcp4_parser.cc" // lalr1.cc:859 break; case 648: -#line 2234 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2261 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr flush(new BoolElement(yystack_[0].value.as< bool > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("flush", flush); } -#line 3608 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3635 "dhcp4_parser.cc" // lalr1.cc:859 break; case 649: -#line 2239 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2266 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr maxsize(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("maxsize", maxsize); } -#line 3617 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3644 "dhcp4_parser.cc" // lalr1.cc:859 break; case 650: -#line 2244 "dhcp4_parser.yy" // lalr1.cc:859 +#line 2271 "dhcp4_parser.yy" // lalr1.cc:859 { ElementPtr maxver(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("maxver", maxver); } -#line 3626 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3653 "dhcp4_parser.cc" // lalr1.cc:859 break; -#line 3630 "dhcp4_parser.cc" // lalr1.cc:859 +#line 3657 "dhcp4_parser.cc" // lalr1.cc:859 default: break; } @@ -4857,19 +4884,19 @@ namespace isc { namespace dhcp { 1755, 1758, 1759, 1762, 1763, 1764, 1765, 1766, 1767, 1768, 1769, 1770, 1771, 1772, 1775, 1777, 1777, 1785, 1794, 1801, 1801, 1811, 1812, 1815, 1816, 1817, 1818, 1819, 1822, 1822, - 1830, 1830, 1841, 1841, 1859, 1859, 1871, 1871, 1881, 1882, - 1885, 1886, 1887, 1888, 1889, 1890, 1891, 1892, 1893, 1894, - 1895, 1896, 1897, 1898, 1899, 1900, 1901, 1902, 1903, 1906, - 1911, 1911, 1919, 1919, 1927, 1932, 1932, 1940, 1945, 1950, - 1950, 1958, 1959, 1962, 1962, 1970, 1975, 1980, 1985, 1985, - 1993, 1996, 1999, 2002, 2005, 2011, 2011, 2019, 2019, 2027, - 2027, 2038, 2038, 2045, 2045, 2052, 2052, 2059, 2059, 2068, - 2068, 2079, 2079, 2089, 2090, 2094, 2095, 2098, 2098, 2113, - 2113, 2123, 2123, 2134, 2135, 2139, 2143, 2143, 2155, 2156, - 2160, 2160, 2168, 2169, 2172, 2173, 2174, 2175, 2176, 2177, - 2178, 2181, 2186, 2186, 2194, 2194, 2204, 2205, 2208, 2208, - 2216, 2217, 2220, 2221, 2222, 2223, 2226, 2226, 2234, 2239, - 2244 + 1830, 1830, 1841, 1841, 1886, 1886, 1898, 1898, 1908, 1909, + 1912, 1913, 1914, 1915, 1916, 1917, 1918, 1919, 1920, 1921, + 1922, 1923, 1924, 1925, 1926, 1927, 1928, 1929, 1930, 1933, + 1938, 1938, 1946, 1946, 1954, 1959, 1959, 1967, 1972, 1977, + 1977, 1985, 1986, 1989, 1989, 1997, 2002, 2007, 2012, 2012, + 2020, 2023, 2026, 2029, 2032, 2038, 2038, 2046, 2046, 2054, + 2054, 2065, 2065, 2072, 2072, 2079, 2079, 2086, 2086, 2095, + 2095, 2106, 2106, 2116, 2117, 2121, 2122, 2125, 2125, 2140, + 2140, 2150, 2150, 2161, 2162, 2166, 2170, 2170, 2182, 2183, + 2187, 2187, 2195, 2196, 2199, 2200, 2201, 2202, 2203, 2204, + 2205, 2208, 2213, 2213, 2221, 2221, 2231, 2232, 2235, 2235, + 2243, 2244, 2247, 2248, 2249, 2250, 2253, 2253, 2261, 2266, + 2271 }; // Print the state stack on the debug stream. @@ -4904,8 +4931,8 @@ namespace isc { namespace dhcp { #line 14 "dhcp4_parser.yy" // lalr1.cc:1167 } } // isc::dhcp -#line 4908 "dhcp4_parser.cc" // lalr1.cc:1167 -#line 2249 "dhcp4_parser.yy" // lalr1.cc:1168 +#line 4935 "dhcp4_parser.cc" // lalr1.cc:1167 +#line 2276 "dhcp4_parser.yy" // lalr1.cc:1168 void diff --git a/src/bin/dhcp4/dhcp4_parser.yy b/src/bin/dhcp4/dhcp4_parser.yy index e18fc66584..0bf3ea9df1 100644 --- a/src/bin/dhcp4/dhcp4_parser.yy +++ b/src/bin/dhcp4/dhcp4_parser.yy @@ -1844,13 +1844,40 @@ dhcp_queue_control: DHCP_QUEUE_CONTROL { ElementPtr qc = $4; ctx.stack_.back()->set("dhcp-queue-control", qc); - if (!qc->contains("queue-type")) { + // Doing this manually, because dhcp-queue-control + // content is otherwise arbitrary + if (!qc->contains("enable-queue")) { std::stringstream msg; - msg << "'queue-type' is required: "; + msg << "'enable-queue' is required: "; msg << qc->getPosition().str() << ")"; error(@1, msg.str()); } + ConstElementPtr enable_queue = qc->get("enable-queue"); + if (enable_queue->getType() != Element::boolean) { + std::stringstream msg; + msg << "'enable-queue' must be boolean: "; + msg << qc->getPosition().str() << ")"; + error(@1, msg.str()); + } + + if (enable_queue->boolValue()) { + if (!qc->contains("queue-type")) { + std::stringstream msg; + msg << "'queue-type' is required, when 'enable-queue' is true: "; + msg << qc->getPosition().str() << ")"; + error(@1, msg.str()); + } + + ConstElementPtr queue_type = qc->get("queue-type"); + if (queue_type->getType() != Element::string) { + std::stringstream msg; + msg << "'queue-type' must be a string: "; + msg << qc->getPosition().str() << ")"; + error(@1, msg.str()); + } + } + ctx.leave(); }; diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index 913ff50a23..8fa2e1f142 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -391,7 +391,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, } if (config_pair.first == "dhcp-queue-control") { - DHCPQueueControlParser parser(AF_INET); + DHCPQueueControlParser parser; srv_cfg->setDHCPQueueControl(parser.parse(config_pair.second)); continue; } diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index 4c8ba4148d..bc66dd4eed 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -6437,20 +6437,33 @@ TEST_F(Dhcp4ParserTest, dhcpQueueControl) { "" }, { - "valid entry", + "queue disabled", "{ \n" - " \"queue-type\": \"some-type\", \n" - " \"capacity\": 75 \n" + " \"enable-queue\": false \n" + "} \n" + }, + { + "queue disabled, arbitrary content allowed", + "{ \n" + " \"enable-queue\": false, \n" + " \"foo\": \"bogus\", \n" + " \"random-int\" : 1234 \n" + "} \n" + }, + { + "queue enabled, with queue-type", + "{ \n" + " \"enable-queue\": true, \n" + " \"queue-type\": \"some-type\" \n" "} \n" }, { - "valid arbitrary content", + "queue enabled with queue-type and arbitrary content", "{ \n" - " \"queue-type\": \"some-type\", \n" - " \"capacity\": 90, \n" - " \"user-context\": { \"comment\": \"some text\" },\n" - " \"random-bool\" : false, \n" - " \"random-int\" : 1234 \n" + " \"enable-queue\": true, \n" + " \"queue-type\": \"some-type\", \n" + " \"foo\": \"bogus\", \n" + " \"random-int\" : 1234 \n" "} \n" } }; @@ -6460,9 +6473,7 @@ TEST_F(Dhcp4ParserTest, dhcpQueueControl) { control = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl(); ASSERT_FALSE(control); - // Iterate over the incorrect scenarios and verify they - // fail as expected. Note, we use parseDHCP4() directly - // as all of the errors above are enforced by the grammar. + // Iterate over the valid scenarios and verify they succeed. data::ConstElementPtr exp_elems; for (auto scenario : scenarios) { SCOPED_TRACE(scenario.description_); @@ -6507,24 +6518,43 @@ TEST_F(Dhcp4ParserTest, dhcpQueueControlInvalid) { struct Scenario { std::string description_; std::string json_; + std::string exp_error_; }; std::vector scenarios = { { - "not a map", - "{ " + genIfaceConfig() + ", \n" + - " \"subnet4\": [ ], \n" - " \"dhcp-queue-control\": 75 \n" - "} \n" + "not a map", + "75 \n", + ":2.24-25: syntax error, unexpected integer, expecting {" + }, + { + "enable-queue missing", + "{ \n" + " \"enable-type\": \"some-type\" \n" + "} \n", + ":2.2-21: 'enable-queue' is required: :2:24)" }, { - "queue type missing", - "{ " + genIfaceConfig() + ", \n" + - " \"subnet4\": [ ], \n" - " \"dhcp-queue-control\": { \n" - " \"capacity\": 100 \n" - " } \n" - "} \n" + "enable-queue not boolean", + "{ \n" + " \"enable-queue\": \"always\" \n" + "} \n", + ":2.2-21: 'enable-queue' must be boolean: :2:24)" + }, + { + "queue enabled, type missing", + "{ \n" + " \"enable-queue\": true \n" + "} \n", + ":2.2-21: 'queue-type' is required, when 'enable-queue' is true: :2:24)" + }, + { + "queue enabled, type not a string", + "{ \n" + " \"enable-queue\": true, \n" + " \"queue-type\": 7777 \n" + "} \n", + ":2.2-21: 'queue-type' must be a string: :2:24)" } }; @@ -6534,10 +6564,23 @@ TEST_F(Dhcp4ParserTest, dhcpQueueControlInvalid) { for (auto scenario : scenarios) { SCOPED_TRACE(scenario.description_); { - EXPECT_THROW(parseDHCP4(scenario.json_), Dhcp4ParseError); + // Construct the config JSON + std::stringstream os; + os << "{ " + genIfaceConfig(); + os << ",\n \"dhcp-queue-control\": " << scenario.json_; + os << "} \n"; + + std::string error_msg = ""; + try { + ASSERT_TRUE(parseDHCP4(os.str(), false)) << "parser returned empty element"; + } catch(const std::exception& ex) { + error_msg = ex.what(); + } + + ASSERT_FALSE(error_msg.empty()) << "parseDHCP4 should have thrown"; + EXPECT_EQ(scenario.exp_error_, error_msg); } } } - } diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index a7241c1170..08e6e2f65d 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -653,27 +653,18 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) { return (isc::config::createAnswer(1, err.str())); } - // Configure DHCP packet queue + // Configure DHCP packet queueing try { data::ConstElementPtr qc; qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl(); - if (!qc) { - // @todo For now we're manually constructing default queue config - // This probably needs to be built into the PQM? - data::ElementPtr default_qc = data::Element::createMap(); - default_qc->set("queue-type", data::Element::create("kea-ring6")); - default_qc->set("capacity", data::Element::create(static_cast(500))); - PacketQueueMgr6::instance().createPacketQueue(default_qc); - } else { - PacketQueueMgr6::instance().createPacketQueue(qc); + if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, qc)) { + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CONFIG_PACKET_QUEUE) + .arg(PacketQueueMgr6::instance().getPacketQueue()->getInfoStr()); } - LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_CONFIG_PACKET_QUEUE) - .arg(PacketQueueMgr6::instance().getPacketQueue()->getInfoStr()); - } catch (const std::exception& ex) { std::ostringstream err; - err << "Error setting DHCP packet queue controls after server reconfiguration: " + err << "Error setting packet queue controls after server reconfiguration: " << ex.what(); return (isc::config::createAnswer(1, err.str())); } diff --git a/src/bin/dhcp6/dhcp6_parser.cc b/src/bin/dhcp6/dhcp6_parser.cc index 3b6755872f..0d8af522cf 100644 --- a/src/bin/dhcp6/dhcp6_parser.cc +++ b/src/bin/dhcp6/dhcp6_parser.cc @@ -3058,653 +3058,681 @@ namespace isc { namespace dhcp { ElementPtr qc = yystack_[0].value.as< ElementPtr > (); ctx.stack_.back()->set("dhcp-queue-control", qc); - if (!qc->contains("queue-type")) { + // Doing this manually, because dhcp-queue-control + // content is otherwise arbitrary + if (!qc->contains("enable-queue")) { std::stringstream msg; - msg << "'queue-type' is required: "; + msg << "'enable-queue' is required: "; msg << qc->getPosition().str() << ")"; error(yystack_[3].location, msg.str()); } + ConstElementPtr enable_queue = qc->get("enable-queue"); + if (enable_queue->getType() != Element::boolean) { + std::stringstream msg; + msg << "'enable-queue' must be boolean: "; + msg << qc->getPosition().str() << ")"; + error(yystack_[3].location, msg.str()); + } + + if (enable_queue->boolValue()) { + if (!qc->contains("queue-type")) { + std::stringstream msg; + msg << "'queue-type' is required, when 'enable-queue' is true: "; + msg << qc->getPosition().str() << ")"; + error(yystack_[3].location, msg.str()); + } + + ConstElementPtr queue_type = qc->get("queue-type"); + if (queue_type->getType() != Element::string) { + std::stringstream msg; + msg << "'queue-type' must be a string: "; + msg << qc->getPosition().str() << ")"; + error(yystack_[3].location, msg.str()); + } + } + + ctx.leave(); } -#line 3071 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3099 "dhcp6_parser.cc" // lalr1.cc:859 break; case 555: -#line 1948 "dhcp6_parser.yy" // lalr1.cc:859 +#line 1976 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("dhcp-ddns", m); ctx.stack_.push_back(m); ctx.enter(ctx.DHCP_DDNS); } -#line 3082 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3110 "dhcp6_parser.cc" // lalr1.cc:859 break; case 556: -#line 1953 "dhcp6_parser.yy" // lalr1.cc:859 +#line 1981 "dhcp6_parser.yy" // lalr1.cc:859 { // The enable updates DHCP DDNS parameter is required. ctx.require("enable-updates", ctx.loc2pos(yystack_[2].location), ctx.loc2pos(yystack_[0].location)); ctx.stack_.pop_back(); ctx.leave(); } -#line 3093 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3121 "dhcp6_parser.cc" // lalr1.cc:859 break; case 557: -#line 1960 "dhcp6_parser.yy" // lalr1.cc:859 +#line 1988 "dhcp6_parser.yy" // lalr1.cc:859 { // Parse the dhcp-ddns map ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.push_back(m); } -#line 3103 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3131 "dhcp6_parser.cc" // lalr1.cc:859 break; case 558: -#line 1964 "dhcp6_parser.yy" // lalr1.cc:859 +#line 1992 "dhcp6_parser.yy" // lalr1.cc:859 { // The enable updates DHCP DDNS parameter is required. ctx.require("enable-updates", ctx.loc2pos(yystack_[3].location), ctx.loc2pos(yystack_[0].location)); // parsing completed } -#line 3113 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3141 "dhcp6_parser.cc" // lalr1.cc:859 break; case 580: -#line 1995 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2023 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr b(new BoolElement(yystack_[0].value.as< bool > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("enable-updates", b); } -#line 3122 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3150 "dhcp6_parser.cc" // lalr1.cc:859 break; case 581: -#line 2000 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2028 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3130 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3158 "dhcp6_parser.cc" // lalr1.cc:859 break; case 582: -#line 2002 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2030 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("qualifying-suffix", s); ctx.leave(); } -#line 3140 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3168 "dhcp6_parser.cc" // lalr1.cc:859 break; case 583: -#line 2008 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2036 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3148 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3176 "dhcp6_parser.cc" // lalr1.cc:859 break; case 584: -#line 2010 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2038 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("server-ip", s); ctx.leave(); } -#line 3158 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3186 "dhcp6_parser.cc" // lalr1.cc:859 break; case 585: -#line 2016 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2044 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr i(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("server-port", i); } -#line 3167 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3195 "dhcp6_parser.cc" // lalr1.cc:859 break; case 586: -#line 2021 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2049 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3175 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3203 "dhcp6_parser.cc" // lalr1.cc:859 break; case 587: -#line 2023 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2051 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("sender-ip", s); ctx.leave(); } -#line 3185 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3213 "dhcp6_parser.cc" // lalr1.cc:859 break; case 588: -#line 2029 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2057 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr i(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("sender-port", i); } -#line 3194 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3222 "dhcp6_parser.cc" // lalr1.cc:859 break; case 589: -#line 2034 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2062 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr i(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("max-queue-size", i); } -#line 3203 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3231 "dhcp6_parser.cc" // lalr1.cc:859 break; case 590: -#line 2039 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2067 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NCR_PROTOCOL); } -#line 3211 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3239 "dhcp6_parser.cc" // lalr1.cc:859 break; case 591: -#line 2041 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2069 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.stack_.back()->set("ncr-protocol", yystack_[0].value.as< ElementPtr > ()); ctx.leave(); } -#line 3220 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3248 "dhcp6_parser.cc" // lalr1.cc:859 break; case 592: -#line 2047 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2075 "dhcp6_parser.yy" // lalr1.cc:859 { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("UDP", ctx.loc2pos(yystack_[0].location))); } -#line 3226 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3254 "dhcp6_parser.cc" // lalr1.cc:859 break; case 593: -#line 2048 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2076 "dhcp6_parser.yy" // lalr1.cc:859 { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("TCP", ctx.loc2pos(yystack_[0].location))); } -#line 3232 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3260 "dhcp6_parser.cc" // lalr1.cc:859 break; case 594: -#line 2051 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2079 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NCR_FORMAT); } -#line 3240 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3268 "dhcp6_parser.cc" // lalr1.cc:859 break; case 595: -#line 2053 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2081 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr json(new StringElement("JSON", ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("ncr-format", json); ctx.leave(); } -#line 3250 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3278 "dhcp6_parser.cc" // lalr1.cc:859 break; case 596: -#line 2059 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2087 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr b(new BoolElement(yystack_[0].value.as< bool > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("always-include-fqdn", b); } -#line 3259 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3287 "dhcp6_parser.cc" // lalr1.cc:859 break; case 597: -#line 2064 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2092 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr b(new BoolElement(yystack_[0].value.as< bool > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("override-no-update", b); } -#line 3268 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3296 "dhcp6_parser.cc" // lalr1.cc:859 break; case 598: -#line 2069 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2097 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr b(new BoolElement(yystack_[0].value.as< bool > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("override-client-update", b); } -#line 3277 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3305 "dhcp6_parser.cc" // lalr1.cc:859 break; case 599: -#line 2074 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2102 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.REPLACE_CLIENT_NAME); } -#line 3285 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3313 "dhcp6_parser.cc" // lalr1.cc:859 break; case 600: -#line 2076 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2104 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.stack_.back()->set("replace-client-name", yystack_[0].value.as< ElementPtr > ()); ctx.leave(); } -#line 3294 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3322 "dhcp6_parser.cc" // lalr1.cc:859 break; case 601: -#line 2082 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2110 "dhcp6_parser.yy" // lalr1.cc:859 { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("when-present", ctx.loc2pos(yystack_[0].location))); } -#line 3302 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3330 "dhcp6_parser.cc" // lalr1.cc:859 break; case 602: -#line 2085 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2113 "dhcp6_parser.yy" // lalr1.cc:859 { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("never", ctx.loc2pos(yystack_[0].location))); } -#line 3310 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3338 "dhcp6_parser.cc" // lalr1.cc:859 break; case 603: -#line 2088 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2116 "dhcp6_parser.yy" // lalr1.cc:859 { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("always", ctx.loc2pos(yystack_[0].location))); } -#line 3318 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3346 "dhcp6_parser.cc" // lalr1.cc:859 break; case 604: -#line 2091 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2119 "dhcp6_parser.yy" // lalr1.cc:859 { yylhs.value.as< ElementPtr > () = ElementPtr(new StringElement("when-not-present", ctx.loc2pos(yystack_[0].location))); } -#line 3326 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3354 "dhcp6_parser.cc" // lalr1.cc:859 break; case 605: -#line 2094 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2122 "dhcp6_parser.yy" // lalr1.cc:859 { error(yystack_[0].location, "boolean values for the replace-client-name are " "no longer supported"); } -#line 3335 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3363 "dhcp6_parser.cc" // lalr1.cc:859 break; case 606: -#line 2100 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2128 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3343 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3371 "dhcp6_parser.cc" // lalr1.cc:859 break; case 607: -#line 2102 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2130 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("generated-prefix", s); ctx.leave(); } -#line 3353 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3381 "dhcp6_parser.cc" // lalr1.cc:859 break; case 608: -#line 2108 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2136 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3361 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3389 "dhcp6_parser.cc" // lalr1.cc:859 break; case 609: -#line 2110 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2138 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("hostname-char-set", s); ctx.leave(); } -#line 3371 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3399 "dhcp6_parser.cc" // lalr1.cc:859 break; case 610: -#line 2116 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2144 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3379 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3407 "dhcp6_parser.cc" // lalr1.cc:859 break; case 611: -#line 2118 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2146 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr s(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("hostname-char-replacement", s); ctx.leave(); } -#line 3389 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3417 "dhcp6_parser.cc" // lalr1.cc:859 break; case 612: -#line 2126 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2154 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3397 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3425 "dhcp6_parser.cc" // lalr1.cc:859 break; case 613: -#line 2128 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2156 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.stack_.back()->set("Dhcp4", yystack_[0].value.as< ElementPtr > ()); ctx.leave(); } -#line 3406 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3434 "dhcp6_parser.cc" // lalr1.cc:859 break; case 614: -#line 2133 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2161 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3414 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3442 "dhcp6_parser.cc" // lalr1.cc:859 break; case 615: -#line 2135 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2163 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.stack_.back()->set("DhcpDdns", yystack_[0].value.as< ElementPtr > ()); ctx.leave(); } -#line 3423 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3451 "dhcp6_parser.cc" // lalr1.cc:859 break; case 616: -#line 2140 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2168 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3431 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3459 "dhcp6_parser.cc" // lalr1.cc:859 break; case 617: -#line 2142 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2170 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.stack_.back()->set("Control-agent", yystack_[0].value.as< ElementPtr > ()); ctx.leave(); } -#line 3440 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3468 "dhcp6_parser.cc" // lalr1.cc:859 break; case 618: -#line 2149 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2177 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->add(m); ctx.stack_.push_back(m); } -#line 3450 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3478 "dhcp6_parser.cc" // lalr1.cc:859 break; case 619: -#line 2153 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2181 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); } -#line 3458 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3486 "dhcp6_parser.cc" // lalr1.cc:859 break; case 620: -#line 2158 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2186 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr i(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("config-control", i); ctx.stack_.push_back(i); ctx.enter(ctx.CONFIG_CONTROL); } -#line 3469 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3497 "dhcp6_parser.cc" // lalr1.cc:859 break; case 621: -#line 2163 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2191 "dhcp6_parser.yy" // lalr1.cc:859 { // No config control params are required ctx.stack_.pop_back(); ctx.leave(); } -#line 3479 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3507 "dhcp6_parser.cc" // lalr1.cc:859 break; case 622: -#line 2169 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2197 "dhcp6_parser.yy" // lalr1.cc:859 { // Parse the config-control map ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.push_back(m); } -#line 3489 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3517 "dhcp6_parser.cc" // lalr1.cc:859 break; case 623: -#line 2173 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2201 "dhcp6_parser.yy" // lalr1.cc:859 { // No config_control params are required // parsing completed } -#line 3498 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3526 "dhcp6_parser.cc" // lalr1.cc:859 break; case 628: -#line 2188 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2216 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("config-databases", l); ctx.stack_.push_back(l); ctx.enter(ctx.CONFIG_DATABASE); } -#line 3509 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3537 "dhcp6_parser.cc" // lalr1.cc:859 break; case 629: -#line 2193 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2221 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); ctx.leave(); } -#line 3518 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3546 "dhcp6_parser.cc" // lalr1.cc:859 break; case 630: -#line 2203 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2231 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("Logging", m); ctx.stack_.push_back(m); ctx.enter(ctx.LOGGING); } -#line 3529 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3557 "dhcp6_parser.cc" // lalr1.cc:859 break; case 631: -#line 2208 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2236 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); ctx.leave(); } -#line 3538 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3566 "dhcp6_parser.cc" // lalr1.cc:859 break; case 632: -#line 2213 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2241 "dhcp6_parser.yy" // lalr1.cc:859 { // Parse the Logging map ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.push_back(m); } -#line 3548 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3576 "dhcp6_parser.cc" // lalr1.cc:859 break; case 633: -#line 2217 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2245 "dhcp6_parser.yy" // lalr1.cc:859 { // parsing completed } -#line 3556 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3584 "dhcp6_parser.cc" // lalr1.cc:859 break; case 637: -#line 2233 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2261 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("loggers", l); ctx.stack_.push_back(l); ctx.enter(ctx.LOGGERS); } -#line 3567 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3595 "dhcp6_parser.cc" // lalr1.cc:859 break; case 638: -#line 2238 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2266 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); ctx.leave(); } -#line 3576 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3604 "dhcp6_parser.cc" // lalr1.cc:859 break; case 641: -#line 2250 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2278 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr l(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->add(l); ctx.stack_.push_back(l); } -#line 3586 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3614 "dhcp6_parser.cc" // lalr1.cc:859 break; case 642: -#line 2254 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2282 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); } -#line 3594 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3622 "dhcp6_parser.cc" // lalr1.cc:859 break; case 652: -#line 2271 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2299 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr dl(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("debuglevel", dl); } -#line 3603 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3631 "dhcp6_parser.cc" // lalr1.cc:859 break; case 653: -#line 2276 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2304 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3611 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3639 "dhcp6_parser.cc" // lalr1.cc:859 break; case 654: -#line 2278 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2306 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr sev(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("severity", sev); ctx.leave(); } -#line 3621 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3649 "dhcp6_parser.cc" // lalr1.cc:859 break; case 655: -#line 2284 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2312 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("output_options", l); ctx.stack_.push_back(l); ctx.enter(ctx.OUTPUT_OPTIONS); } -#line 3632 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3660 "dhcp6_parser.cc" // lalr1.cc:859 break; case 656: -#line 2289 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2317 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); ctx.leave(); } -#line 3641 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3669 "dhcp6_parser.cc" // lalr1.cc:859 break; case 659: -#line 2298 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2326 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->add(m); ctx.stack_.push_back(m); } -#line 3651 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3679 "dhcp6_parser.cc" // lalr1.cc:859 break; case 660: -#line 2302 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2330 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.stack_.pop_back(); } -#line 3659 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3687 "dhcp6_parser.cc" // lalr1.cc:859 break; case 667: -#line 2316 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2344 "dhcp6_parser.yy" // lalr1.cc:859 { ctx.enter(ctx.NO_KEYWORD); } -#line 3667 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3695 "dhcp6_parser.cc" // lalr1.cc:859 break; case 668: -#line 2318 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2346 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr sev(new StringElement(yystack_[0].value.as< std::string > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("output", sev); ctx.leave(); } -#line 3677 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3705 "dhcp6_parser.cc" // lalr1.cc:859 break; case 669: -#line 2324 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2352 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr flush(new BoolElement(yystack_[0].value.as< bool > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("flush", flush); } -#line 3686 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3714 "dhcp6_parser.cc" // lalr1.cc:859 break; case 670: -#line 2329 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2357 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr maxsize(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("maxsize", maxsize); } -#line 3695 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3723 "dhcp6_parser.cc" // lalr1.cc:859 break; case 671: -#line 2334 "dhcp6_parser.yy" // lalr1.cc:859 +#line 2362 "dhcp6_parser.yy" // lalr1.cc:859 { ElementPtr maxver(new IntElement(yystack_[0].value.as< int64_t > (), ctx.loc2pos(yystack_[0].location))); ctx.stack_.back()->set("maxver", maxver); } -#line 3704 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3732 "dhcp6_parser.cc" // lalr1.cc:859 break; -#line 3708 "dhcp6_parser.cc" // lalr1.cc:859 +#line 3736 "dhcp6_parser.cc" // lalr1.cc:859 default: break; } @@ -4970,19 +4998,19 @@ namespace isc { namespace dhcp { 1836, 1837, 1838, 1839, 1840, 1841, 1842, 1843, 1844, 1847, 1847, 1854, 1855, 1856, 1859, 1864, 1864, 1872, 1877, 1884, 1891, 1891, 1901, 1902, 1905, 1906, 1907, 1908, 1909, 1912, - 1912, 1920, 1920, 1930, 1930, 1948, 1948, 1960, 1960, 1970, - 1971, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, - 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, - 1995, 2000, 2000, 2008, 2008, 2016, 2021, 2021, 2029, 2034, - 2039, 2039, 2047, 2048, 2051, 2051, 2059, 2064, 2069, 2074, - 2074, 2082, 2085, 2088, 2091, 2094, 2100, 2100, 2108, 2108, - 2116, 2116, 2126, 2126, 2133, 2133, 2140, 2140, 2149, 2149, - 2158, 2158, 2169, 2169, 2179, 2180, 2184, 2185, 2188, 2188, - 2203, 2203, 2213, 2213, 2224, 2225, 2229, 2233, 2233, 2245, - 2246, 2250, 2250, 2258, 2259, 2262, 2263, 2264, 2265, 2266, - 2267, 2268, 2271, 2276, 2276, 2284, 2284, 2294, 2295, 2298, - 2298, 2306, 2307, 2310, 2311, 2312, 2313, 2316, 2316, 2324, - 2329, 2334 + 1912, 1920, 1920, 1930, 1930, 1976, 1976, 1988, 1988, 1998, + 1999, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, + 2023, 2028, 2028, 2036, 2036, 2044, 2049, 2049, 2057, 2062, + 2067, 2067, 2075, 2076, 2079, 2079, 2087, 2092, 2097, 2102, + 2102, 2110, 2113, 2116, 2119, 2122, 2128, 2128, 2136, 2136, + 2144, 2144, 2154, 2154, 2161, 2161, 2168, 2168, 2177, 2177, + 2186, 2186, 2197, 2197, 2207, 2208, 2212, 2213, 2216, 2216, + 2231, 2231, 2241, 2241, 2252, 2253, 2257, 2261, 2261, 2273, + 2274, 2278, 2278, 2286, 2287, 2290, 2291, 2292, 2293, 2294, + 2295, 2296, 2299, 2304, 2304, 2312, 2312, 2322, 2323, 2326, + 2326, 2334, 2335, 2338, 2339, 2340, 2341, 2344, 2344, 2352, + 2357, 2362 }; // Print the state stack on the debug stream. @@ -5017,8 +5045,8 @@ namespace isc { namespace dhcp { #line 14 "dhcp6_parser.yy" // lalr1.cc:1167 } } // isc::dhcp -#line 5021 "dhcp6_parser.cc" // lalr1.cc:1167 -#line 2339 "dhcp6_parser.yy" // lalr1.cc:1168 +#line 5049 "dhcp6_parser.cc" // lalr1.cc:1167 +#line 2367 "dhcp6_parser.yy" // lalr1.cc:1168 void diff --git a/src/bin/dhcp6/dhcp6_parser.yy b/src/bin/dhcp6/dhcp6_parser.yy index 2328636e6f..6209de3069 100644 --- a/src/bin/dhcp6/dhcp6_parser.yy +++ b/src/bin/dhcp6/dhcp6_parser.yy @@ -1933,13 +1933,41 @@ dhcp_queue_control: DHCP_QUEUE_CONTROL { ElementPtr qc = $4; ctx.stack_.back()->set("dhcp-queue-control", qc); - if (!qc->contains("queue-type")) { + // Doing this manually, because dhcp-queue-control + // content is otherwise arbitrary + if (!qc->contains("enable-queue")) { std::stringstream msg; - msg << "'queue-type' is required: "; + msg << "'enable-queue' is required: "; msg << qc->getPosition().str() << ")"; error(@1, msg.str()); } + ConstElementPtr enable_queue = qc->get("enable-queue"); + if (enable_queue->getType() != Element::boolean) { + std::stringstream msg; + msg << "'enable-queue' must be boolean: "; + msg << qc->getPosition().str() << ")"; + error(@1, msg.str()); + } + + if (enable_queue->boolValue()) { + if (!qc->contains("queue-type")) { + std::stringstream msg; + msg << "'queue-type' is required, when 'enable-queue' is true: "; + msg << qc->getPosition().str() << ")"; + error(@1, msg.str()); + } + + ConstElementPtr queue_type = qc->get("queue-type"); + if (queue_type->getType() != Element::string) { + std::stringstream msg; + msg << "'queue-type' must be a string: "; + msg << qc->getPosition().str() << ")"; + error(@1, msg.str()); + } + } + + ctx.leave(); }; diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index 3ff82c89bb..6def10c74a 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -484,7 +484,7 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, } if (config_pair.first == "dhcp-queue-control") { - DHCPQueueControlParser parser(AF_INET); + DHCPQueueControlParser parser; srv_config->setDHCPQueueControl(parser.parse(config_pair.second)); continue; } diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index 027afcd498..757c46bf1a 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -6938,20 +6938,33 @@ TEST_F(Dhcp6ParserTest, dhcpQueueControl) { "" }, { - "valid entry", + "queue disabled", "{ \n" - " \"queue-type\": \"some-type\", \n" - " \"capacity\": 75 \n" + " \"enable-queue\": false \n" + "} \n" + }, + { + "queue disabled, arbitrary content allowed", + "{ \n" + " \"enable-queue\": false, \n" + " \"foo\": \"bogus\", \n" + " \"random-int\" : 1234 \n" "} \n" }, { - "valid arbitrary content", + "queue enabled, with queue-type", "{ \n" - " \"queue-type\": \"some-type\", \n" - " \"capacity\": 90, \n" - " \"user-context\": { \"comment\": \"some text\" },\n" - " \"random-bool\" : false, \n" - " \"random-int\" : 1236 \n" + " \"enable-queue\": true, \n" + " \"queue-type\": \"some-type\" \n" + "} \n" + }, + { + "queue enabled with queue-type and arbitrary content", + "{ \n" + " \"enable-queue\": true, \n" + " \"queue-type\": \"some-type\", \n" + " \"foo\": \"bogus\", \n" + " \"random-int\" : 1234 \n" "} \n" } }; @@ -6961,9 +6974,7 @@ TEST_F(Dhcp6ParserTest, dhcpQueueControl) { control = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl(); ASSERT_FALSE(control); - // Iterate over the incorrect scenarios and verify they - // fail as expected. Note, we use parseDHCP6() directly - // as all of the errors above are enforced by the grammar. + // Iterate over the valid scenarios and verify they succeed. data::ConstElementPtr exp_elems; for (auto scenario : scenarios) { SCOPED_TRACE(scenario.description_); @@ -7008,24 +7019,43 @@ TEST_F(Dhcp6ParserTest, dhcpQueueControlInvalid) { struct Scenario { std::string description_; std::string json_; + std::string exp_error_; }; std::vector scenarios = { { - "not a map", - "{ " + genIfaceConfig() + ", \n" + - " \"subnet6\": [ ], \n" - " \"dhcp-queue-control\": 75 \n" - "} \n" + "not a map", + "75 \n", + ":2.24-25: syntax error, unexpected integer, expecting {" + }, + { + "enable-queue missing", + "{ \n" + " \"enable-type\": \"some-type\" \n" + "} \n", + ":2.2-21: 'enable-queue' is required: :2:24)" + }, + { + "enable-queue not boolean", + "{ \n" + " \"enable-queue\": \"always\" \n" + "} \n", + ":2.2-21: 'enable-queue' must be boolean: :2:24)" }, { - "queue type missing", - "{ " + genIfaceConfig() + ", \n" + - " \"subnet6\": [ ], \n" - " \"dhcp-queue-control\": { \n" - " \"capacity\": 100 \n" - " } \n" - "} \n" + "queue enabled, type missing", + "{ \n" + " \"enable-queue\": true \n" + "} \n", + ":2.2-21: 'queue-type' is required, when 'enable-queue' is true: :2:24)" + }, + { + "queue enabled, type not a string", + "{ \n" + " \"enable-queue\": true, \n" + " \"queue-type\": 7777 \n" + "} \n", + ":2.2-21: 'queue-type' must be a string: :2:24)" } }; @@ -7035,7 +7065,21 @@ TEST_F(Dhcp6ParserTest, dhcpQueueControlInvalid) { for (auto scenario : scenarios) { SCOPED_TRACE(scenario.description_); { - EXPECT_THROW(parseDHCP6(scenario.json_), Dhcp6ParseError); + // Construct the config JSON + std::stringstream os; + os << "{ " + genIfaceConfig(); + os << ",\n \"dhcp-queue-control\": " << scenario.json_; + os << "} \n"; + + std::string error_msg = ""; + try { + ASSERT_TRUE(parseDHCP6(os.str(), false)) << "parser returned empty element"; + } catch(const std::exception& ex) { + error_msg = ex.what(); + } + + ASSERT_FALSE(error_msg.empty()) << "parseDHCP6 should have thrown"; + EXPECT_EQ(scenario.exp_error_, error_msg); } } } diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc index 8792b216e6..8c6ace93fc 100644 --- a/src/lib/dhcp/iface_mgr.cc +++ b/src/lib/dhcp/iface_mgr.cc @@ -93,6 +93,7 @@ Iface::closeSockets(const uint16_t family) { << " specified when requested to close all sockets" << " which belong to this family"); } + // Search for the socket of the specific type. SocketCollection::iterator sock = sockets_.begin(); while (sock != sockets_.end()) { @@ -282,6 +283,9 @@ Iface::countActive4() const { } void IfaceMgr::closeSockets() { + // Stops the receiver thread if there is one. + stopDHCPReceiver(); + BOOST_FOREACH(IfacePtr iface, ifaces_) { iface->closeSockets(); } @@ -293,11 +297,17 @@ void IfaceMgr::stopDHCPReceiver() { receiver_thread_->wait(); receiver_thread_.reset(); error_watch_.clearReady(); + } receiver_error_ = "no error"; - getPacketQueue4()->clear(); - getPacketQueue4()->clear(); + if (getPacketQueue4()) { + getPacketQueue4()->clear(); + } + + if (getPacketQueue6()) { + getPacketQueue6()->clear(); + } } IfaceMgr::~IfaceMgr() { @@ -584,6 +594,12 @@ IfaceMgr::openSockets4(const uint16_t port, const bool use_bcast, } } + + if (count > 0) { + // starts the receiver thread (if queueing is enabled); + startDHCPReceiver(AF_INET); + } + return (count > 0); } @@ -662,6 +678,11 @@ IfaceMgr::openSockets6(const uint16_t port, } } + + if (count > 0) { + // starts the receiver thread (if queueing is enabled); + startDHCPReceiver(AF_INET6); + } return (count > 0); } @@ -674,14 +695,14 @@ IfaceMgr::startDHCPReceiver(const uint16_t family) { switch (family) { case AF_INET: if(!getPacketQueue4()) { - isc_throw(Unexpected, "startDHCPRecever - no packet queue?"); + return; } receiver_thread_.reset(new Thread(boost::bind(&IfaceMgr::receiveDHCP4Packets, this))); break; case AF_INET6: if(!getPacketQueue6()) { - isc_throw(Unexpected, "startDHCPRecever - no packet queue?"); + return; } receiver_thread_.reset(new Thread(boost::bind(&IfaceMgr::receiveDHCP6Packets, this))); @@ -942,8 +963,15 @@ IfaceMgr::send(const Pkt4Ptr& pkt) { return (packet_filter_->send(*iface, getSocket(*pkt).sockfd_, pkt)); } - Pkt4Ptr IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { + if (receiver_thread_) { + return (receive4Indirect(timeout_sec, timeout_usec)); + } + + return (receive4Direct(timeout_sec, timeout_usec)); +} + +Pkt4Ptr IfaceMgr::receive4Indirect(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { // Sanity check for microsecond timeout. if (timeout_usec >= 1000000) { isc_throw(BadValue, "fractional timeout must be shorter than" @@ -1051,7 +1079,233 @@ Pkt4Ptr IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */ return (pkt); } -Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */ ) { +Pkt4Ptr IfaceMgr::receive4Direct(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { + // Sanity check for microsecond timeout. + if (timeout_usec >= 1000000) { + isc_throw(BadValue, "fractional timeout must be shorter than" + " one million microseconds"); + } + boost::scoped_ptr candidate; + IfacePtr iface; + fd_set sockets; + int maxfd = 0; + + FD_ZERO(&sockets); + + /// @todo: marginal performance optimization. We could create the set once + /// and then use its copy for select(). Please note that select() modifies + /// provided set to indicated which sockets have something to read. + BOOST_FOREACH(iface, ifaces_) { + BOOST_FOREACH(SocketInfo s, iface->getSockets()) { + + // Only deal with IPv4 addresses. + if (s.addr_.isV4()) { + + // Add this socket to listening set + FD_SET(s.sockfd_, &sockets); + if (maxfd < s.sockfd_) { + maxfd = s.sockfd_; + } + } + } + } + + // if there are any callbacks for external sockets registered... + if (!callbacks_.empty()) { + BOOST_FOREACH(SocketCallbackInfo s, callbacks_) { + FD_SET(s.socket_, &sockets); + if (maxfd < s.socket_) { + maxfd = s.socket_; + } + } + } + + struct timeval select_timeout; + select_timeout.tv_sec = timeout_sec; + select_timeout.tv_usec = timeout_usec; + + // zero out the errno to be safe + errno = 0; + + int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout); + + if (result == 0) { + // nothing received and timeout has been reached + return (Pkt4Ptr()); // NULL + + } else if (result < 0) { + // In most cases we would like to know whether select() returned + // an error because of a signal being received or for some other + // reason. This is because DHCP servers use signals to trigger + // certain actions, like reconfiguration or graceful shutdown. + // By catching a dedicated exception the caller will know if the + // error returned by the function is due to the reception of the + // signal or for some other reason. + if (errno == EINTR) { + isc_throw(SignalInterruptOnSelect, strerror(errno)); + } else { + isc_throw(SocketReadError, strerror(errno)); + } + } + + // Let's find out which socket has the data + BOOST_FOREACH(SocketCallbackInfo s, callbacks_) { + if (!FD_ISSET(s.socket_, &sockets)) { + continue; + } + + // something received over external socket + + // Calling the external socket's callback provides its service + // layer access without integrating any specific features + // in IfaceMgr + if (s.callback_) { + s.callback_(); + } + + return (Pkt4Ptr()); + } + + // Let's find out which interface/socket has the data + BOOST_FOREACH(iface, ifaces_) { + BOOST_FOREACH(SocketInfo s, iface->getSockets()) { + if (FD_ISSET(s.sockfd_, &sockets)) { + candidate.reset(new SocketInfo(s)); + break; + } + } + if (candidate) { + break; + } + } + + if (!candidate) { + isc_throw(SocketReadError, "received data over unknown socket"); + } + + // Now we have a socket, let's get some data from it! + // Assuming that packet filter is not NULL, because its modifier checks it. + return (packet_filter_->receive(*iface, *candidate)); +} + +Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) { + if (receiver_thread_) { + return (receive6Indirect(timeout_sec, timeout_usec)); + } + + return (receive6Direct(timeout_sec, timeout_usec)); +} + +Pkt6Ptr IfaceMgr::receive6Direct(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */ ) { + // Sanity check for microsecond timeout. + if (timeout_usec >= 1000000) { + isc_throw(BadValue, "fractional timeout must be shorter than" + " one million microseconds"); + } + + boost::scoped_ptr candidate; + fd_set sockets; + int maxfd = 0; + + FD_ZERO(&sockets); + + /// @todo: marginal performance optimization. We could create the set once + /// and then use its copy for select(). Please note that select() modifies + /// provided set to indicated which sockets have something to read. + BOOST_FOREACH(IfacePtr iface, ifaces_) { + + BOOST_FOREACH(SocketInfo s, iface->getSockets()) { + // Only deal with IPv6 addresses. + if (s.addr_.isV6()) { + + // Add this socket to listening set + FD_SET(s.sockfd_, &sockets); + if (maxfd < s.sockfd_) { + maxfd = s.sockfd_; + } + } + } + } + + // if there are any callbacks for external sockets registered... + if (!callbacks_.empty()) { + BOOST_FOREACH(SocketCallbackInfo s, callbacks_) { + // Add it to the set as well + FD_SET(s.socket_, &sockets); + if (maxfd < s.socket_) { + maxfd = s.socket_; + } + } + } + + struct timeval select_timeout; + select_timeout.tv_sec = timeout_sec; + select_timeout.tv_usec = timeout_usec; + + // zero out the errno to be safe + errno = 0; + + int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout); + + if (result == 0) { + // nothing received and timeout has been reached + return (Pkt6Ptr()); // NULL + + } else if (result < 0) { + // In most cases we would like to know whether select() returned + // an error because of a signal being received or for some other + // reason. This is because DHCP servers use signals to trigger + // certain actions, like reconfiguration or graceful shutdown. + // By catching a dedicated exception the caller will know if the + // error returned by the function is due to the reception of the + // signal or for some other reason. + if (errno == EINTR) { + isc_throw(SignalInterruptOnSelect, strerror(errno)); + } else { + isc_throw(SocketReadError, strerror(errno)); + } + } + + // Let's find out which socket has the data + BOOST_FOREACH(SocketCallbackInfo s, callbacks_) { + if (!FD_ISSET(s.socket_, &sockets)) { + continue; + } + + // something received over external socket + + // Calling the external socket's callback provides its service + // layer access without integrating any specific features + // in IfaceMgr + if (s.callback_) { + s.callback_(); + } + + return (Pkt6Ptr()); + } + + // Let's find out which interface/socket has the data + BOOST_FOREACH(IfacePtr iface, ifaces_) { + BOOST_FOREACH(SocketInfo s, iface->getSockets()) { + if (FD_ISSET(s.sockfd_, &sockets)) { + candidate.reset(new SocketInfo(s)); + break; + } + } + if (candidate) { + break; + } + } + + if (!candidate) { + isc_throw(SocketReadError, "received data over unknown socket"); + } + // Assuming that packet filter is not NULL, because its modifier checks it. + return (packet_filter6_->receive(*candidate)); +} + + +Pkt6Ptr IfaceMgr::receive6Indirect(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */ ) { // Sanity check for microsecond timeout. if (timeout_usec >= 1000000) { isc_throw(BadValue, "fractional timeout must be shorter than" @@ -1485,5 +1739,42 @@ IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) { return (*candidate); } +bool +IfaceMgr::configureDHCPPacketQueue(uint16_t family, data::ConstElementPtr queue_control) { + if (receiver_thread_) { + isc_throw(InvalidOperation, "Cannot reconfigure queueing" + " while receiver thread is running"); + } + + bool enable_queue = false; + if (queue_control) { + try { + enable_queue = data::SimpleParser::getBoolean(queue_control, "enable-queue"); + } catch (...) { + // @todo - for now swallow not found errors. + // if not present we assume default + } + } + + if (enable_queue) { + // Try to create the queue as configured. + if (family == AF_INET) { + PacketQueueMgr4::instance().createPacketQueue(queue_control); + } else { + PacketQueueMgr6::instance().createPacketQueue(queue_control); + } + } else { + // Destroy the current queue (if one), this inherently disables threading. + if (family == AF_INET) { + PacketQueueMgr4::instance().destroyPacketQueue(); + } else { + PacketQueueMgr6::instance().destroyPacketQueue(); + } + } + + return(enable_queue); +} + + } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h index 979e2667a7..37c4d1ffe4 100644 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@ -699,6 +699,9 @@ public: /// @return Pkt6 object representing received packet (or NULL) Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec = 0); + Pkt6Ptr receive6Direct(uint32_t timeout_sec, uint32_t timeout_usec = 0); + Pkt6Ptr receive6Indirect(uint32_t timeout_sec, uint32_t timeout_usec = 0); + /// @brief Tries to receive IPv4 packet over open IPv4 sockets. /// /// Attempts to receive a single DHCPv4 message over any of the open @@ -721,6 +724,9 @@ public: /// @return Pkt4 object representing received packet (or NULL) Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec = 0); + Pkt4Ptr receive4Direct(uint32_t timeout_sec, uint32_t timeout_usec = 0); + Pkt4Ptr receive4Indirect(uint32_t timeout_sec, uint32_t timeout_usec = 0); + /// Opens UDP/IP socket and binds it to address, interface and port. /// /// Specific type of socket (UDP/IPv4 or UDP/IPv6) depends on passed addr @@ -808,6 +814,9 @@ public: /// but it is not running, it is down, or is a loopback interface when /// loopback is not allowed, an error is reported. /// + /// If sockets were successfully opened, it calls @ startDHCPReceiver to + /// start the receiver thread (if packet queueing is enabled). + /// /// On the systems with multiple interfaces, it is often desired that the /// failure to open a socket on a particular interface doesn't cause a /// fatal error and sockets should be opened on remaining interfaces. @@ -857,6 +866,9 @@ public: /// represented by a class derived from @c isc::dhcp::PktFilter abstract /// class. /// + /// If sockets were successfully opened, it calls @ startDHCPReceiver to + /// start the receiver thread (if packet queueing is enabled). + /// /// It is possible to specify whether sockets should be broadcast capable. /// In most of the cases, the sockets should support broadcast traffic, e.g. /// DHCPv4 server and relay need to listen to broadcast messages sent by @@ -916,6 +928,10 @@ public: IfaceMgrErrorMsgCallback error_handler = 0); /// @brief Closes all open sockets. + /// + /// It calls @c stopDHCPReceiver to stop the receiver thread and then + /// it closes all open interface sockets. + /// /// Is used in destructor, but also from Dhcpv4Srv and Dhcpv6Srv classes. void closeSockets(); @@ -1047,7 +1063,8 @@ public: /// @brief Starts DHCP packet receiver. /// /// Starts the DHCP packet receiver thread for the given. - /// protocol, AF_NET or AF_INET6 + /// protocol, AF_NET or AF_INET6, if the packet queue + /// exists, otherwise it simply returns. /// /// @param family indicates which receiver to start, /// (AF_INET or AF_INET6) @@ -1057,9 +1074,27 @@ public: /// @brief Stops the DHCP packet receiver. /// - /// Stops the receiver and deletes the dedicated thread. + /// If the thread exists, it is stopped, deleted, and + /// the packet queue is flushed. void stopDHCPReceiver(); + /// @brief Configures DHCP packet queue + /// + /// If the given configuration enables packet queueing, then the + /// appropriate queue is created. Otherwise, the existing queue is + /// destroyed. If the receiver thread is running when this function + /// is invoked, it will throw. + /// + /// @param family indicates which receiver to start, + /// (AF_INET or AF_INET6) + /// @parm queue_control configuration containing "dhcp-queue-control" + /// content + /// @return true if packet queueuing has been enabled, false otherwise + /// @throw InvalidOperation if the receiver thread is currently running. + bool configureDHCPPacketQueue(const uint16_t family, + data::ConstElementPtr queue_control); + + // don't use private, we need derived classes in tests protected: diff --git a/src/lib/dhcp/packet_queue_mgr.h b/src/lib/dhcp/packet_queue_mgr.h index e34bf261e5..5b1d4da779 100644 --- a/src/lib/dhcp/packet_queue_mgr.h +++ b/src/lib/dhcp/packet_queue_mgr.h @@ -158,6 +158,12 @@ public: return (packet_queue_); } + /// @brief Destroys the current packet queue. + /// Any queued packets will be discarded. + void destroyPacketQueue() { + packet_queue_.reset(); + } + protected: /// @brief A map holding registered backend factory functions. std::map factories_; diff --git a/src/lib/dhcp/packet_queue_mgr4.cc b/src/lib/dhcp/packet_queue_mgr4.cc index 079c067de1..82f7740fc8 100644 --- a/src/lib/dhcp/packet_queue_mgr4.cc +++ b/src/lib/dhcp/packet_queue_mgr4.cc @@ -30,11 +30,6 @@ PacketQueueMgr4::PacketQueueMgr4() { PacketQueue4Ptr queue(new PacketQueueRing4("kea-ring4", capacity)); return (queue); }); - - data::ElementPtr parameters = data::Element::createMap(); - parameters->set("queue-type", data::Element::create("kea-ring4")); - parameters->set("capacity", data::Element::create(static_cast(500))); - createPacketQueue(parameters); } boost::scoped_ptr& diff --git a/src/lib/dhcp/packet_queue_mgr4.h b/src/lib/dhcp/packet_queue_mgr4.h index dc1a26bb77..a770f51d0c 100644 --- a/src/lib/dhcp/packet_queue_mgr4.h +++ b/src/lib/dhcp/packet_queue_mgr4.h @@ -55,8 +55,7 @@ public: private: /// @brief Private constructor. /// - /// It registers a default factory for DHCPv4 queues and creates - /// an default DHCPv4 packet queue. + /// It registers a default factory for DHCPv4 queues. PacketQueueMgr4(); /// @brief Returns a pointer to the currently instance of the diff --git a/src/lib/dhcp/packet_queue_mgr6.cc b/src/lib/dhcp/packet_queue_mgr6.cc index b6c8ad9212..705daaa66c 100644 --- a/src/lib/dhcp/packet_queue_mgr6.cc +++ b/src/lib/dhcp/packet_queue_mgr6.cc @@ -30,11 +30,6 @@ PacketQueueMgr6::PacketQueueMgr6() { PacketQueue6Ptr queue(new PacketQueueRing6("kea-ring6", capacity)); return (queue); }); - - data::ElementPtr parameters = data::Element::createMap(); - parameters->set("queue-type", data::Element::create("kea-ring6")); - parameters->set("capacity", data::Element::create(static_cast(500))); - createPacketQueue(parameters); } boost::scoped_ptr& diff --git a/src/lib/dhcp/packet_queue_mgr6.h b/src/lib/dhcp/packet_queue_mgr6.h index fb9d4e5057..a4d89301ee 100644 --- a/src/lib/dhcp/packet_queue_mgr6.h +++ b/src/lib/dhcp/packet_queue_mgr6.h @@ -56,8 +56,7 @@ public: private: /// @brief Private constructor. /// - /// It registers a default factory for DHCPv6 queues and creates - /// an default DHCPv6 packet queue. + /// It registers a default factory for DHCPv6 queues. PacketQueueMgr6(); /// @brief Returns a pointer to the currently used instance of the diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc index 7975efbc1b..fb4114f586 100644 --- a/src/lib/dhcp/tests/iface_mgr_unittest.cc +++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc @@ -645,12 +645,14 @@ TEST_F(IfaceMgrTest, clearIfaces) { TEST_F(IfaceMgrTest, packetQueue4) { NakedIfaceMgr ifacemgr; - // Get the default queue. - PacketQueue4Ptr q4 = ifacemgr.getPacketQueue4(); - ASSERT_TRUE(q4); + // Should not have a queue at start up. + ASSERT_FALSE(ifacemgr.getPacketQueue4()); - // Verify that the queue is what we expect. - checkInfo(q4, "{ \"capacity\": 500, \"queue-type\": \"kea-ring4\", \"size\": 0 }"); + // Verify that we can create a queue with default factory. + data::ConstElementPtr config = makeQueueConfig("kea-ring4", 2000); + ASSERT_NO_THROW(PacketQueueMgr4::instance().createPacketQueue(config)); + checkInfo(ifacemgr.getPacketQueue4(), + "{ \"capacity\": 2000, \"queue-type\": \"kea-ring4\", \"size\": 0 }"); // Verify that fetching the queue via IfaceMgr and PacketQueueMgr // returns the same queue. @@ -661,18 +663,20 @@ TEST_F(IfaceMgrTest, packetQueue4) { TEST_F(IfaceMgrTest, packetQueue6) { NakedIfaceMgr ifacemgr; - // Get the default queue. - PacketQueue6Ptr q6 = ifacemgr.getPacketQueue6(); + // Should not have a queue at start up. + ASSERT_FALSE(ifacemgr.getPacketQueue6()); - // Verify that we have a default queue and its info is correct. - checkInfo(q6, "{ \"capacity\": 500, \"queue-type\": \"kea-ring6\", \"size\": 0 }"); + // Verify that we can create a queue with default factory. + data::ConstElementPtr config = makeQueueConfig("kea-ring6", 2000); + ASSERT_NO_THROW(PacketQueueMgr6::instance().createPacketQueue(config)); + checkInfo(ifacemgr.getPacketQueue6(), + "{ \"capacity\": 2000, \"queue-type\": \"kea-ring6\", \"size\": 0 }"); // Verify that fetching the queue via IfaceMgr and PacketQueueMgr // returns the same queue. ASSERT_EQ(ifacemgr.getPacketQueue6(), PacketQueueMgr6::instance().getPacketQueue()); } - TEST_F(IfaceMgrTest, receiveTimeout6) { using namespace boost::posix_time; std::cout << "Testing DHCPv6 packet reception timeouts." @@ -732,7 +736,7 @@ TEST_F(IfaceMgrTest, receiveTimeout6) { TEST_F(IfaceMgrTest, receiveTimeout4) { using namespace boost::posix_time; - std::cout << "Testing DHCPv6 packet reception timeouts." + std::cout << "Testing DHCPv4 packet reception timeouts." << " Test will block for a few seconds when waiting" << " for timeout to occur." << std::endl; @@ -745,6 +749,7 @@ TEST_F(IfaceMgrTest, receiveTimeout4) { ); // Socket is open if returned value is non-negative. ASSERT_GE(socket1, 0); + // Start receiver. ASSERT_NO_THROW(ifacemgr->startDHCPReceiver(AF_INET)); @@ -780,8 +785,8 @@ TEST_F(IfaceMgrTest, receiveTimeout4) { EXPECT_LE(duration.total_microseconds(), 700000); // Test with invalid fractional timeout values. - EXPECT_THROW(ifacemgr->receive6(0, 1000000), isc::BadValue); - EXPECT_THROW(ifacemgr->receive6(2, 1000005), isc::BadValue); + EXPECT_THROW(ifacemgr->receive4(0, 1000000), isc::BadValue); + EXPECT_THROW(ifacemgr->receive4(2, 1000005), isc::BadValue); // Stop receiver. EXPECT_NO_THROW(ifacemgr->stopDHCPReceiver()); @@ -1129,7 +1134,9 @@ TEST_F(IfaceMgrTest, sendReceive4) { EXPECT_GE(socket1, 0); +#if 0 ifacemgr->startDHCPReceiver(AF_INET); +#endif boost::shared_ptr sendPkt(new Pkt4(DHCPDISCOVER, 1234) ); @@ -1213,7 +1220,9 @@ TEST_F(IfaceMgrTest, sendReceive4) { EXPECT_THROW(ifacemgr->send(sendPkt), SocketWriteError); +#if 0 ifacemgr->stopDHCPReceiver(); +#endif } // Verifies that it is possible to set custom packet filter object diff --git a/src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc b/src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc index 8e3bbeaec3..4c0d8bf607 100644 --- a/src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc +++ b/src/lib/dhcp/tests/packet_queue_mgr4_unittest.cc @@ -96,23 +96,20 @@ public: }; // Verifies that DHCPv4 PQM provides a default queue factory -// and packet queue. TEST_F(PacketQueueMgr4Test, defaultQueue) { + // Should not be a queue at start-up + ASSERT_FALSE(mgr().getPacketQueue()); - // Verify that we have a default queue and its info is correct. - checkMyInfo("{ \"capacity\": 500, \"queue-type\": \"kea-ring4\", \"size\": 0 }"); - + // Verify that we can create a queue with default factory. data::ConstElementPtr config = makeQueueConfig("kea-ring4", 2000); - - // Verify that we can replace the default queue with different capacity queue ASSERT_NO_THROW(mgr().createPacketQueue(config)); checkMyInfo("{ \"capacity\": 2000, \"queue-type\": \"kea-ring4\", \"size\": 0 }"); // We should be able to recreate the manager. ASSERT_NO_THROW(PacketQueueMgr4::create()); - // And be back to having the default queue. - checkMyInfo("{ \"capacity\": 500, \"queue-type\": \"kea-ring4\", \"size\": 0 }"); + // Should not be a queue. + ASSERT_FALSE(mgr().getPacketQueue()); } // Verifies that PQM registry and creation of custome queue implementations. @@ -125,10 +122,7 @@ TEST_F(PacketQueueMgr4Test, customQueueType) { // Register our adjustable-type factory ASSERT_TRUE(addCustomQueueType("custom-queue")); - // We still have our default queue. - checkMyInfo("{ \"capacity\": 500, \"queue-type\": \"kea-ring4\", \"size\": 0 }"); - - // Verify that we can replace the default queue with a "custom-queue" queue + // Verify that we can create a custom queue. ASSERT_NO_THROW(mgr().createPacketQueue(config)); checkMyInfo("{ \"capacity\": 2000, \"queue-type\": \"custom-queue\", \"size\": 0 }"); diff --git a/src/lib/dhcp/tests/packet_queue_mgr6_unittest.cc b/src/lib/dhcp/tests/packet_queue_mgr6_unittest.cc index ad3ca73db5..ef01709222 100644 --- a/src/lib/dhcp/tests/packet_queue_mgr6_unittest.cc +++ b/src/lib/dhcp/tests/packet_queue_mgr6_unittest.cc @@ -84,23 +84,20 @@ public: }; // Verifies that DHCPv6 PQM provides a default queue factory -// and packet queue. TEST_F(PacketQueueMgr6Test, defaultQueue) { + // Should not be a queue at start-up + ASSERT_FALSE(mgr().getPacketQueue()); - // Verify that we have a default queue and its info is correct. - checkMyInfo("{ \"capacity\": 500, \"queue-type\": \"kea-ring6\", \"size\": 0 }"); - + // Verify that we can create a queue with default factory. data::ConstElementPtr config = makeQueueConfig("kea-ring6", 2000); - - // Verify that we can replace the default queue with different capacity queue ASSERT_NO_THROW(mgr().createPacketQueue(config)); checkMyInfo("{ \"capacity\": 2000, \"queue-type\": \"kea-ring6\", \"size\": 0 }"); // We should be able to recreate the manager. ASSERT_NO_THROW(PacketQueueMgr6::create()); - // And be back to having the default queue. - checkMyInfo("{ \"capacity\": 500, \"queue-type\": \"kea-ring6\", \"size\": 0 }"); + // Should not be a queue. + ASSERT_FALSE(mgr().getPacketQueue()); } // Verifies that PQM registry and creation of custome queue implementations. @@ -113,10 +110,7 @@ TEST_F(PacketQueueMgr6Test, customQueueType) { // Register our adjustable-type factory ASSERT_TRUE(addCustomQueueType("custom-queue")); - // We still have our default queue. - checkMyInfo("{ \"capacity\": 500, \"queue-type\": \"kea-ring6\", \"size\": 0 }"); - - // Verify that we can replace the default queue with a "custom-queue" queue + // Verify that we can create a custom queue. ASSERT_NO_THROW(mgr().createPacketQueue(config)); checkMyInfo("{ \"capacity\": 2000, \"queue-type\": \"custom-queue\", \"size\": 0 }"); diff --git a/src/lib/dhcpsrv/cfg_iface.cc b/src/lib/dhcpsrv/cfg_iface.cc index 31e4a48cc6..951d880ee6 100644 --- a/src/lib/dhcpsrv/cfg_iface.cc +++ b/src/lib/dhcpsrv/cfg_iface.cc @@ -28,7 +28,6 @@ CfgIface::CfgIface() void CfgIface::closeSockets() const { - IfaceMgr::instance().stopDHCPReceiver(); IfaceMgr::instance().closeSockets(); } @@ -174,11 +173,7 @@ CfgIface::openSockets(const uint16_t family, const uint16_t port, sopen = IfaceMgr::instance().openSockets6(port, error_callback); } - if (sopen) { - // @todo we may consider starting/stopping this when DHCP service is - // enable/disabled, rather then when we open sockets. - IfaceMgr::instance().startDHCPReceiver(family); - } else { + if (!sopen) { // If no socket were opened, log a warning because the server will // not respond to any queries. LOG_WARN(dhcpsrv_logger, DHCPSRV_NO_SOCKETS_OPEN); diff --git a/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.cc b/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.cc index b852683ddf..728c06338a 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.cc +++ b/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.cc @@ -18,15 +18,6 @@ using namespace isc::data; namespace isc { namespace dhcp { -DHCPQueueControlParser::DHCPQueueControlParser(const uint16_t family) - : family_(family) { - // @todo Not sure we need family but just in case. - if (family_ != AF_INET && family_ != AF_INET6) { - isc_throw(BadValue, "DHCPQueueControlParser - invalid family: " - << family_ << ", must be AF_INET or AF_INET6"); - } -} - data::ElementPtr DHCPQueueControlParser::parse(const isc::data::ConstElementPtr& control_elem) { // All we really do here is verify that it is a map that @@ -36,12 +27,17 @@ DHCPQueueControlParser::parse(const isc::data::ConstElementPtr& control_elem) { isc_throw(DhcpConfigError, "dhcp-queue-control must be a map"); } - ConstElementPtr elem = control_elem->get("queue-type"); - if (!elem) { - isc_throw(DhcpConfigError, "queue-type is required"); - } else { - if (elem->getType() != Element::string) { - isc_throw(DhcpConfigError, "queue-type must be a string"); + // enable-queue is mandatory. + bool enable_queue = getBoolean(control_elem, "enable-queue"); + + if (enable_queue) { + ConstElementPtr elem = control_elem->get("queue-type"); + if (!elem) { + isc_throw(DhcpConfigError, "when queue is enabled, queue-type is required"); + } else { + if (elem->getType() != Element::string) { + isc_throw(DhcpConfigError, "queue-type must be a string"); + } } } diff --git a/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.h b/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.h index 5d1fbe8ca0..663874903f 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.h +++ b/src/lib/dhcpsrv/parsers/dhcp_queue_control_parser.h @@ -18,9 +18,16 @@ namespace dhcp { /// /// This parser parses the "dhcp-queue-control" parameter which holds the /// the configurable parameters that tailor DHCP packet queue behavior. -/// Currently "dhcp-queue-control" is treated as a map of arbitrary values, -/// with only one required value, "queue-type". This was done to -/// provide latitude for differing queue implementations. +/// In order to provide wide latitude to packet queue implementators, +/// 'dhcp-queue-control' is mostly treated as a map of arbitrary values. +/// There is only mandatory value, 'enable-queue', which enables/disables +/// DHCP packet queueing. If this value is true, then the content must +/// also include a value for 'queue-type'. Beyond these values, the +/// map may contain any combination of valid JSON elements. +/// +/// Unlike most other parsers, this parser primarily serves to validate +/// the aforementioned rules, and rather than instantiate an object as +/// a result, it simply returns a copy original map of elements. /// /// This parser is used in both DHCPv4 and DHCPv6. Derived parsers /// are not needed. @@ -29,22 +36,19 @@ public: /// @brief Constructor /// - /// @param family AF_INET for DHCPv4 and AF_INET6 for DHCPv6. - explicit DHCPQueueControlParser(const uint16_t family); + DHCPQueueControlParser(){}; /// @brief Parses content of the "dhcp-queue-control". /// - /// @param values pointer to the content of parsed values + /// @param control_elem MapElement containing the queue control values to + /// parse /// - /// @return A pointer to a newly constructed DHCPQueueControl populated - /// with the parsed values + /// @return A copy of the of the input MapElement. /// /// @throw DhcpConfigError if any of the values are invalid. - data::ElementPtr parse(const isc::data::ConstElementPtr& values); + data::ElementPtr parse(const isc::data::ConstElementPtr& control_elem); private: - /// @brief AF_INET for DHCPv4 and AF_INET6 for DHCPv6. - int family_; }; } diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am index 644a4716c1..ea30468e7a 100644 --- a/src/lib/dhcpsrv/tests/Makefile.am +++ b/src/lib/dhcpsrv/tests/Makefile.am @@ -85,6 +85,7 @@ libdhcpsrv_unittests_SOURCES += csv_lease_file6_unittest.cc libdhcpsrv_unittests_SOURCES += d2_client_unittest.cc libdhcpsrv_unittests_SOURCES += d2_udp_unittest.cc libdhcpsrv_unittests_SOURCES += database_connection_unittest.cc +libdhcpsrv_unittests_SOURCES += dhcp_queue_control_parser_unittest.cc libdhcpsrv_unittests_SOURCES += dhcp4o6_ipc_unittest.cc libdhcpsrv_unittests_SOURCES += duid_config_parser_unittest.cc libdhcpsrv_unittests_SOURCES += expiration_config_parser_unittest.cc diff --git a/src/lib/dhcpsrv/tests/dhcp_queue_control_parser_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_queue_control_parser_unittest.cc new file mode 100644 index 0000000000..2aae12b08d --- /dev/null +++ b/src/lib/dhcpsrv/tests/dhcp_queue_control_parser_unittest.cc @@ -0,0 +1,171 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include + +using namespace isc::data; +using namespace isc::dhcp; +using namespace isc::test; + +namespace { + +/// @brief Test fixture class for @c DHCPQueueControlParser +class DHCPQueueControlParserTest : public ::testing::Test { +protected: + + /// @brief Setup for each test. + /// + /// Clears the configuration in the @c CfgMgr. + virtual void SetUp(); + + /// @brief Cleans up after each test. + /// + /// Clears the configuration in the @c CfgMgr. + virtual void TearDown(); + +}; + +void +DHCPQueueControlParserTest::SetUp() { + CfgMgr::instance().clear(); +} + +void +DHCPQueueControlParserTest::TearDown() { + CfgMgr::instance().clear(); +} + +// Verifies that DHCPQueueControlParser handles +// expected valid dhcp-queue-control contet +TEST_F(DHCPQueueControlParserTest, validContent) { + struct Scenario { + std::string description_; + std::string json_; + }; + + std::vector scenarios = { + { + "queue disabled", + "{ \n" + " \"enable-queue\": false \n" + "} \n" + }, + { + "queue disabled, arbitrary content allowed", + "{ \n" + " \"enable-queue\": false, \n" + " \"foo\": \"bogus\", \n" + " \"random-int\" : 1234 \n" + "} \n" + }, + { + "queue enabled, with queue-type", + "{ \n" + " \"enable-queue\": true, \n" + " \"queue-type\": \"some-type\" \n" + "} \n" + }, + { + "queue enabled with queue-type and arbitrary content", + "{ \n" + " \"enable-queue\": true, \n" + " \"queue-type\": \"some-type\", \n" + " \"foo\": \"bogus\", \n" + " \"random-int\" : 1234 \n" + "} \n" + } + }; + + // Iterate over the valid scenarios and verify they succeed. + ConstElementPtr config_elems; + ConstElementPtr queue_control; + for (auto scenario : scenarios) { + SCOPED_TRACE(scenario.description_); + { + // Construct the config JSON + ASSERT_NO_THROW(config_elems = Element::fromJSON(scenario.json_)) + << "invalid JSON, test is broken"; + + // Parsing config into a queue control should succeed. + DHCPQueueControlParser parser; + try { + queue_control = parser.parse(config_elems); + } catch (const std::exception& ex) { + ADD_FAILURE() << "parser threw an exception: " << ex.what(); + } + + // Verify the resultant queue control. + ASSERT_TRUE(queue_control); + + // The parser should have created a duplicate of the + // configuration elements. + ASSERT_TRUE(queue_control.get() != config_elems.get()); + EXPECT_TRUE(queue_control->equals(*config_elems)); + } + } +} + +// Verifies that DHCPQueueControlParser correctly catches +// invalid dhcp-queue-control content +TEST_F(DHCPQueueControlParserTest, invalidContent) { + struct Scenario { + std::string description_; + std::string json_; + }; + + std::vector scenarios = { + { + "enable-queue missing", + "{ \n" + " \"enable-type\": \"some-type\" \n" + "} \n" + }, + { + "enable-queue not boolean", + "{ \n" + " \"enable-queue\": \"always\" \n" + "} \n" + }, + { + "queue enabled, type missing", + "{ \n" + " \"enable-queue\": true \n" + "} \n" + }, + { + "queue enabled, type not a string", + "{ \n" + " \"enable-queue\": true, \n" + " \"queue-type\": 7777 \n" + "} \n" + } + }; + + // Iterate over the valid scenarios and verify they succeed. + ConstElementPtr config_elems; + ConstElementPtr queue_control; + for (auto scenario : scenarios) { + SCOPED_TRACE(scenario.description_); + { + // Construct the config JSON + ASSERT_NO_THROW(config_elems = Element::fromJSON(scenario.json_)) + << "invalid JSON, test is broken"; + + // Parsing config into a queue control should succeed. + DHCPQueueControlParser parser; + EXPECT_THROW(parser.parse(config_elems), DhcpConfigError); + } + } +} + + +}; // anonymous namespace