Kea 3.3.3
radius_access.cc
Go to the documentation of this file.
1// Copyright (C) 2020-2026 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <dhcp/dhcp4.h>
10#include <dhcp/dhcp6.h>
12#include <dhcpsrv/cfgmgr.h>
13#include <dhcpsrv/host_mgr.h>
16#include <radius/radius_utils.h>
17#include <stats/stats_mgr.h>
19#include <util/str.h>
20
21#include <sstream>
22
23#include <radius_log.h>
24#include <stdio.h>
25
26using namespace std;
27using namespace isc;
28using namespace isc::asiolink;
29using namespace isc::data;
30using namespace isc::dhcp;
31using namespace isc::hooks;
32using namespace isc::stats;
33using namespace isc::util;
34using namespace isc::util::str;
35namespace ph = std::placeholders;
36
37namespace {
38
39// From Dhcpv6Srv.
41getMAC(Pkt6& pkt) {
43 getMACSources().get();
44 HWAddrPtr hwaddr;
45 for (auto const& source : mac_sources) {
46 hwaddr = pkt.getMAC(source);
47 if (hwaddr) {
48 return (hwaddr);
49 }
50 }
51 return (hwaddr);
52}
53
54} // end of anonymous namespace.
55
56namespace isc {
57namespace radius {
58
60 const std::vector<uint8_t>& id,
61 AttributesPtr send_attrs)
62 : subnet_id_(subnet_id), id_(id), send_attrs_(send_attrs) {
63}
64
66 const CallbackAuth& callback)
67 : env_(env), auth_() {
68 auth_.reset(new RadiusAsyncAuth(env.subnet_id_, env_.send_attrs_, callback));
69 RadiusImpl::instance().registerExchange(auth_->getExchange());
70 // set the IO service when async access will be available.
71}
72
73void
75 auth_->start();
76}
77
80
81bool
83 std::vector<uint8_t>& id,
84 std::string& text) {
86 try {
87 HWAddrPtr hwaddr;
88 OptionPtr option;
89 ClientIdPtr clientid;
90 bool extracted = false;
91 switch (type) {
93 hwaddr = query.getHWAddr();
94 if (!hwaddr) {
95 isc_throw(BadValue, "no hardware address");
96 }
97 id = hwaddr->hwaddr_;
98 if (id.empty()) {
99 isc_throw(BadValue, "empty hardware address");
100 }
101 text = query.getHWAddr()->toText(false);
102 if (RadiusImpl::instance().canonical_mac_address_) {
103 text = canonize(text);
104 }
105 break;
106
107 case Host::IDENT_DUID:
109 if (!option) {
110 isc_throw(BadValue, "no client-id option");
111 }
112 clientid.reset(new ClientId(option->getData()));
113 id = clientid->getClientId();
114 if ((id.size() <= 5) || (id[0] != CLIENT_ID_OPTION_TYPE_DUID)) {
115 isc_throw(BadValue, "no DUID in client-id option");
116 }
117 id = vector<uint8_t>(id.begin() + 5, id.end());
118 if (RadiusImpl::instance().clientid_printable_) {
119 text = toPrintable(id);
120 } else {
121 text = dumpAsHex(id);
122 }
123 break;
124
126 option = query.getOption(DHO_DHCP_AGENT_OPTIONS);
127 if (!option) {
128 isc_throw(BadValue, "no relay agent options");
129 }
130 option = option->getOption(RAI_OPTION_AGENT_CIRCUIT_ID);
131 if (!option) {
132 isc_throw(BadValue, "no circuit-id option");
133 }
134 id = option->getData();
135 if (id.empty()) {
136 isc_throw(BadValue, "empty circuit-id option");
137 }
138 if (RadiusImpl::instance().clientid_printable_) {
139 text = toPrintable(id);
140 } else {
141 text = dumpAsHex(id);
142 }
143 break;
144
147 if (!option) {
148 isc_throw(BadValue, "no client-id option");
149 }
150 clientid.reset(new ClientId(option->getData()));
151 id = clientid->getClientId();
152 if (id.empty()) {
153 isc_throw(BadValue, "empty client-id option");
154 }
155 if (RadiusImpl::instance().extract_duid_) {
156 text = dumpAsHex(extractDuid(clientid, extracted));
157 }
158 if (extracted) {
159 break;
160 }
161 if (RadiusImpl::instance().clientid_pop0_) {
162 vector<uint8_t> popped = pop0(clientid);
163 if (RadiusImpl::instance().clientid_printable_) {
164 text = toPrintable(popped);
165 } else {
166 text = dumpAsHex(popped);
167 }
168 } else if (RadiusImpl::instance().clientid_printable_) {
169 text = toPrintable(id);
170 } else {
171 text = dumpAsHex(id);
172 }
173 break;
174
175 case Host::IDENT_FLEX:
176 // Relies on replace-client-id flex-id parameter to be true.
178 if (!option) {
179 isc_throw(BadValue, "no client-id option");
180 }
181 clientid.reset(new ClientId(option->getData()));
182 id = clientid->getClientId();
183 if ((id.size() <= 1) || (id[0] != 0)) {
184 isc_throw(BadValue, "no flex-id in client-id option");
185 }
186 id = vector<uint8_t>(id.begin() + 1, id.end());
187 if (RadiusImpl::instance().clientid_printable_) {
188 text = toPrintable(id);
189 } else {
190 text = dumpAsHex(id);
191 }
192 break;
193
194 default:
195 isc_throw(OutOfRange, "unsupported identifier type " << type);
196 }
197 } catch (const std::exception& ex) {
199 .arg(Host::getIdentifierName(type))
200 .arg(query.getLabel())
201 .arg(ex.what());
202 id.clear();
203 text.clear();
204 return (false);
205 }
207 .arg(dumpAsHex(id))
208 .arg(Host::getIdentifierName(type))
209 .arg(text)
210 .arg(query.getLabel());
211 return (true);
212}
213
214bool
216 std::vector<uint8_t>& id,
217 std::string& text) {
219 try {
220 HWAddrPtr hwaddr;
221 OptionPtr option;
222 DuidPtr duid;
223 switch (type) {
224 case Host::IDENT_DUID:
225 option = query.getOption(D6O_CLIENTID);
226 if (!option) {
227 isc_throw(BadValue, "no client-id option");
228 }
229 duid.reset(new DUID(option->getData()));
230 id = duid->getDuid();
231 if (id.empty()) {
232 isc_throw(BadValue, "empty client-id option");
233 }
234 if (RadiusImpl::instance().clientid_pop0_) {
235 vector<uint8_t> popped = pop0(duid);
236 if (RadiusImpl::instance().clientid_printable_) {
237 text = toPrintable(popped);
238 } else {
239 text = dumpAsHex(popped);
240 }
241 } else if (RadiusImpl::instance().clientid_printable_) {
242 text = toPrintable(id);
243 } else {
244 text = dumpAsHex(id);
245 }
246 break;
247
249 hwaddr = getMAC(query);
250 if (!hwaddr) {
251 isc_throw(BadValue, "no hardware address");
252 }
253 id = hwaddr->hwaddr_;
254 if (id.empty()) {
255 isc_throw(BadValue, "empty hardware address");
256 }
257 text = hwaddr->toText(false);
258 if (RadiusImpl::instance().canonical_mac_address_) {
259 text = canonize(text);
260 }
261 break;
262
263 case Host::IDENT_FLEX:
264 option = query.getOption(D6O_CLIENTID);
265 if (!option) {
266 isc_throw(BadValue, "no client-id option");
267 }
268 duid.reset(new DUID(option->getData()));
269 id = duid->getDuid();
270 if ((id.size() <= 2) || (id[0] != 0) || (id[1] != 0)) {
271 isc_throw(BadValue, "no flex-id in client-id option");
272 }
273 id = vector<uint8_t>(id.begin() + 2, id.end());
274 if (RadiusImpl::instance().clientid_printable_) {
275 text = toPrintable(id);
276 } else {
277 text = dumpAsHex(id);
278 }
279 break;
280
281 default:
282 isc_throw(OutOfRange, "unsupported identifier type " << type);
283 }
284 } catch (const std::exception& ex) {
286 .arg(Host::getIdentifierName(type))
287 .arg(query.getLabel())
288 .arg(ex.what());
289 id.clear();
290 text.clear();
291 return (false);
292 }
294 .arg(dumpAsHex(id))
295 .arg(Host::getIdentifierName(type))
296 .arg(text)
297 .arg(query.getLabel());
298 return (true);
299}
300
303 uint32_t subnet_id,
304 const std::vector<uint8_t>& id,
305 const std::string& text) {
306 AttributesPtr send(new Attributes());
307
308 try {
309 if (subnet_id == SUBNET_ID_UNUSED) {
310 isc_throw(BadValue, "subnet ID is reserved");
311 }
312
313 send->add(Attribute::fromString(PW_USER_NAME, text));
314
315 // Add hardware address.
316 HWAddrPtr hwaddr = query.getHWAddr();
317 if (RadiusImpl::instance().id_type4_ != Host::IDENT_HWADDR &&
318 hwaddr && !hwaddr->hwaddr_.empty()) {
319 string hw = hwaddr->toText(false);
320 if (RadiusImpl::instance().canonical_mac_address_) {
321 hw = canonize(hw);
322 }
324 }
325
326 // Add attributes from configuration.
327 send->append(RadiusImpl::instance().auth_->
328 attributes_.getEvalAll(query));
329
330 } catch (const std::exception& ex) {
332 .arg(ex.what())
333 .arg(query.getLabel());
334 return (RadiusAuthHandlerPtr());
335 }
336
337 // Return the handler.
338 RadiusAuthEnv env(subnet_id, id, send);
339 RadiusAuthHandlerPtr handler;
340 handler.reset(new RadiusAuthHandler(env,
341 std::bind(&RadiusAccess::terminate4,
342 env, ph::_1, ph::_2)));
343 return (handler);
344}
345
348 uint32_t subnet_id,
349 const std::vector<uint8_t>& id,
350 const std::string& text) {
351 AttributesPtr send(new Attributes());
352
353 try {
354 if (subnet_id == SUBNET_ID_UNUSED) {
355 isc_throw(BadValue, "subnet ID is reserved");
356 }
357
358 send->add(Attribute::fromString(PW_USER_NAME, text));
359
360 // Add hardware address.
361 HWAddrPtr hwaddr = getMAC(query);
362 if (RadiusImpl::instance().id_type6_ != Host::IDENT_HWADDR &&
363 hwaddr && !hwaddr->hwaddr_.empty()) {
364 string hw = hwaddr->toText(false);
365 if (RadiusImpl::instance().canonical_mac_address_) {
366 hw = canonize(hw);
367 }
369 }
370
371 // Add attributes from configuration.
372 send->append(RadiusImpl::instance().auth_->
373 attributes_.getEvalAll(query));
374
375 } catch (const std::exception& ex) {
377 .arg(ex.what())
378 .arg(query.getLabel());
379 return (RadiusAuthHandlerPtr());
380 }
381
382 // Return the handler.
383 RadiusAuthEnv env(subnet_id, id, send);
384 RadiusAuthHandlerPtr handler;
385 handler.reset(new RadiusAuthHandler(env,
386 std::bind(&RadiusAccess::terminate6,
387 env, ph::_1, ph::_2)));
388 return (handler);
389}
390
391bool
392RadiusAccess::reselectSubnet(const dhcp::Pkt4Ptr& query, uint32_t& subnet_id,
393 bool& both_global, const std::string& cclass) {
394 both_global = false;
395 ConstCfgSubnets4Ptr subnets4 =
396 CfgMgr::instance().getCurrentCfg()->getCfgSubnets4();
397
398 // Check selected subnet.
399 ConstSubnet4Ptr subnet = subnets4->getBySubnetId(subnet_id);
400 if (!subnet) {
401 return (false);
402 }
403 // If one of the pools accepts the client-class we're done.
404 if (subnet->clientSupported(query->classes_)) {
405 const PoolCollection& pools = subnet->getPools(Lease::TYPE_V4);
406 for (auto const& pool : pools) {
407 if (pool->clientSupported(cclass)) {
408 return (false);
409 }
410 }
411 }
412 // Check if this subnet uses global reservations.
413 bool use_global = subnet->getReservationsGlobal();
414
415 // Try other selectable subnets.
416 const Subnet4Collection* subnets = subnets4->getAll();
417 CfgSubnets4Ptr selectable(new CfgSubnets4());
418
419 for (auto const& iter : *subnets) {
420 // Check pools.
421 const PoolCollection& pools = iter->getPools(Lease::TYPE_V4);
422 for (auto const& pool : pools) {
423 if (pool->clientSupported(cclass)) {
424 selectable->add(iter);
425 break;
426 }
427 }
428 }
429
430 const SubnetSelector& selector = CfgSubnets4::initSelector(query);
431 subnet = selectable->selectSubnet(selector);
432
433 if (!subnet) {
434 subnet_id = SUBNET_ID_UNUSED;
435 } else {
436 subnet_id = subnet->getID();
437 if (use_global && subnet->getReservationsGlobal()) {
438 both_global = true;
439 }
440 }
441 return (true);
442}
443
444bool
445RadiusAccess::reselectSubnet(const dhcp::Pkt6Ptr& query, uint32_t& subnet_id,
446 bool& both_global, const std::string& cclass) {
447 both_global = false;
448 ConstCfgSubnets6Ptr subnets6 =
449 CfgMgr::instance().getCurrentCfg()->getCfgSubnets6();
450
451 // Check selected subnet.
452 ConstSubnet6Ptr subnet = subnets6->getBySubnetId(subnet_id);
453 if (!subnet) {
454 return (false);
455 }
456 // If one of the pools accepts the client-class we're done.
457 if (subnet->clientSupported(query->classes_)) {
458 const PoolCollection& pools = subnet->getPools(Lease::TYPE_NA);
459 for (auto const& pool : pools) {
460 if (pool->clientSupported(cclass)) {
461 return (false);
462 }
463 }
464 }
465 // Check if this subnet uses global reservations.
466 bool use_global = subnet->getReservationsGlobal();
467
468 // Try other selectable subnets.
469 const Subnet6Collection* subnets = subnets6->getAll();
470 CfgSubnets6Ptr selectable(new CfgSubnets6());
471
472 for (auto const& iter : *subnets) {
473 // Check pools.
474 const PoolCollection& pools = iter->getPools(Lease::TYPE_NA);
475 for (auto const& pool : pools) {
476 if (pool->clientSupported(cclass)) {
477 selectable->add(iter);
478 break;
479 }
480 }
481 }
482
483 const SubnetSelector& selector = CfgSubnets6::initSelector(query);
484 subnet = selectable->selectSubnet(selector);
485
486 if (!subnet) {
487 subnet_id = SUBNET_ID_UNUSED;
488 } else {
489 subnet_id = subnet->getID();
490 if (use_global && subnet->getReservationsGlobal()) {
491 both_global = true;
492 }
493 }
494 return (true);
495}
496
497bool
499 uint32_t& subnet_id,
500 bool& both_global,
501 const asiolink::IOAddress& address) {
502 both_global = false;
503 ConstCfgSubnets4Ptr subnets4 =
504 CfgMgr::instance().getCurrentCfg()->getCfgSubnets4();
505
506 // Check selected subnet.
507 ConstSubnet4Ptr subnet = subnets4->getBySubnetId(subnet_id);
508 if (!subnet) {
509 return (false);
510 }
511 // If the reserved address is in range we're done.
512 if (subnet->clientSupported(query->classes_) && subnet->inRange(address)) {
513 return (false);
514 }
515 // Check if this subnet uses global reservations.
516 bool use_global = subnet->getReservationsGlobal();
517
518 // Select subnet by reserved address only.
519 subnet = subnets4->selectSubnet(address, query->classes_);
520 if (!subnet) {
521 subnet_id = SUBNET_ID_UNUSED;
522 } else {
523 subnet_id = subnet->getID();
524 if (use_global && subnet->getReservationsGlobal()) {
525 both_global = true;
526 }
527 }
528 return (true);
529}
530
531bool
533 uint32_t& subnet_id,
534 bool& both_global,
535 const asiolink::IOAddress& address) {
536 both_global = false;
537 ConstCfgSubnets6Ptr subnets6 =
538 CfgMgr::instance().getCurrentCfg()->getCfgSubnets6();
539
540 // Check selected subnet.
541 ConstSubnet6Ptr subnet = subnets6->getBySubnetId(subnet_id);
542 if (!subnet) {
543 return (false);
544 }
545 // If the reserved address is in range we're done.
546 if (subnet->clientSupported(query->classes_) && subnet->inRange(address)) {
547 return (false);
548 }
549 // Check if this subnet uses global reservations.
550 bool use_global = subnet->getReservationsGlobal();
551
552 // Select subnet by reserved address only.
553 subnet = subnets6->selectSubnet(address, query->classes_);
554 if (!subnet) {
555 subnet_id = SUBNET_ID_UNUSED;
556 } else {
557 subnet_id = subnet->getID();
558 if (use_global && subnet->getReservationsGlobal()) {
559 both_global = true;
560 }
561 }
562 return (true);
563}
564
565void
567 AttributesPtr recv_attrs,
568 Pkt4Ptr& query, bool& drop) {
570 MultiThreadingLock lock(impl.auth_->requests4_.mutex_);
571
572 // Get the pending request.
574 impl.auth_->requests4_.get(env.id_);
575 if (!pending_request) {
577 .arg(dumpAsHex(env.id_));
578 drop = true;
579 return;
580 }
581 // Outside some unit tests the query is never null.
582 query = pending_request->query_;
583 impl.auth_->requests4_.remove(env.id_);
584
585 // Process response.
586 ConstAttributePtr ip_address;
587 ConstAttributePtr framed_pool;
588 ConstAttributePtr class_;
589 bool reselected = false;
590 bool both_global = false;
591 uint32_t original_subnet_id = env.subnet_id_;
592
593 if (result == REJECT_RC) {
594 // Create the host with no attributes.
595 // Should we saved them for the Class?
596 recv_attrs.reset();
597 // Reset subnet
598 env.subnet_id_ = SUBNET_ID_UNUSED;
599 reselected = true;
600 } else if (result != OK_RC) {
601 // Error case
603 .arg(result)
604 .arg(exchangeRCtoText(result));
605 // what to do? For now nothing!?
606 // drop = true;
607 return;
608 } else if (recv_attrs) {
609 // Pickup interesting things in received attributes.
610 ip_address = recv_attrs->get(PW_FRAMED_IP_ADDRESS);
611 framed_pool = recv_attrs->get(PW_FRAMED_POOL);
612 class_ = recv_attrs->get(PW_CLASS);
613 // etc
614 }
615
616 // Set pool.
617 string cclass;
618 if (framed_pool && (framed_pool->getValueType() == PW_TYPE_STRING)) {
619 cclass = framed_pool->toString();
620 if (query) {
621 query->addClass(cclass);
622 }
623 }
624
625 // Get IPv4 address.
627 if (ip_address && (ip_address->getValueType() == PW_TYPE_IPADDR)) {
628 addr4 = ip_address->toIpAddr();
629 }
630
631 // Check reselection using pool and client-class.
632 if (query && !reselected && !cclass.empty() &&
633 impl.reselect_subnet_pool_) {
634 reselected = reselectSubnet(query, env.subnet_id_, both_global, cclass);
635 }
636
637 // Check reselection using reserved address and range.
638 if (query && !reselected && !addr4.isV4Zero() &&
639 impl.reselect_subnet_address_) {
640 reselected = reselectSubnet(query, env.subnet_id_, both_global, addr4);
641 }
642
643 // Get host identifier.
644 Host::IdentifierType type = impl.id_type4_;
645 CacheHostDataSourcePtr cache = impl.cache_;
647 if (reselected) {
648 // Add subnet-id in user context.
649 map->set("subnet-id",
650 Element::create(static_cast<int>(env.subnet_id_)));
651 }
652
653 // Create and insert a reselecting entry.
654 uint32_t host_subnet_id = SUBNET_ID_UNUSED;
655 if (reselected && !both_global) {
657 getCfgSubnets4()->getBySubnetId(original_subnet_id);
658 if (!subnet) {
659 isc_throw(Unexpected, "no original subnet " << original_subnet_id);
660 }
661 host_subnet_id =
662 ((subnet && subnet->getReservationsGlobal()) ?
663 SUBNET_ID_GLOBAL : original_subnet_id);
664
665 // Create.
666 HostPtr host(new Host(&env.id_[0], env.id_.size(), type,
667 host_subnet_id, SUBNET_ID_UNUSED,
669
670 // Add reselect in the user-context and reset it.
671 host->setContext(map);
672 map = Element::createMap();
673
674 // Negative entry.
675 host->setNegative(true);
676
677 // Insert it.
678 if (!cache) {
679 return;
680 }
681 static_cast<void>(cache->insert(host, true));
682
683 ostringstream msg;
684 msg << "subnet-id := " << env.subnet_id_;
686 .arg(host->toText())
687 .arg(msg.str());
688 }
689
690 // Return if the subnet is null. Note in this case both_global
691 // is always false.
692 if (env.subnet_id_ == SUBNET_ID_UNUSED) {
693 return;
694 }
695
696 // Build a host entry to cache radius attributes from OK_RC answer.
698 getCfgSubnets4()->getBySubnetId(env.subnet_id_);
699 if (!subnet) {
700 isc_throw(Unexpected, "no subnet " << env.subnet_id_);
701 }
702 host_subnet_id =
703 ((subnet && subnet->getReservationsGlobal()) ?
704 SUBNET_ID_GLOBAL : env.subnet_id_);
705 HostPtr host(new Host(&env.id_[0], env.id_.size(), type,
706 host_subnet_id, SUBNET_ID_UNUSED, addr4));
707
708 // Save received attributes.
709 if (recv_attrs) {
710 map->set("radius", recv_attrs->toElement());
711 }
712 host->setContext(map);
713
714 if (addr4.isV4Zero()) {
715 // The entry has no reservation nor hostname so mark it as negative.
716 host->setNegative(true);
717 }
718
719 // Insert entry.
720 if (!cache) {
721 return;
722 }
723 cache->insert(host, true);
724
726 .arg(host->toText())
727 .arg(recv_attrs ? recv_attrs->toText() : "");
728
729 // Pass the subnet to the server code.
730 if (query) {
731 CalloutHandlePtr callout_handle = getCalloutHandle(query);
732 callout_handle->setContext("subnet4", subnet);
733 }
734}
735
736void
738 AttributesPtr recv_attrs) {
739 Pkt4Ptr query;
740 bool drop = false;
741 try {
742 terminate4Internal(env, result, recv_attrs, query, drop);
743 } catch (const std::exception& ex) {
744 // Unexpected error.
746 .arg(ex.what());
747 drop = true;
748 } catch (...) {
749 // Unexpected unknown error.
751 .arg("unknown error");
752 drop = true;
753 }
754 if (!query) {
755 return;
756 }
757 if (drop) {
760 .arg(query->getLabel());
761 StatsMgr::instance().addValue("pkt4-processing-failed",
762 static_cast<int64_t>(1));
763 StatsMgr::instance().addValue("pkt4-receive-drop",
764 static_cast<int64_t>(1));
765 HooksManager::drop("subnet4_select", query);
766 } else {
767 ostringstream msg;
768 if (env.subnet_id_ == SUBNET_ID_UNUSED) {
769 msg << "no subnet";
770 } else {
771 msg << "subnet " << env.subnet_id_;
772 }
775 .arg(query->getLabel())
776 .arg(msg.str());
777 HooksManager::unpark("subnet4_select", query);
778 }
779}
780
781void
783 AttributesPtr recv_attrs,
784 Pkt6Ptr& query, bool& drop) {
786 MultiThreadingLock lock(impl.auth_->requests6_.mutex_);
787
788 // Get the pending request.
790 impl.auth_->requests6_.get(env.id_);
791 if (!pending_request) {
793 .arg(dumpAsHex(env.id_));
794 drop = true;
795 return;
796 }
797 // Outside some unit tests the query is never null.
798 query = pending_request->query_;
799 impl.auth_->requests6_.remove(env.id_);
800
801 // Process response.
802 ConstAttributePtr ip6_address;
803 ConstAttributePtr prefix;
804 ConstAttributePtr framed_pool;
805 ConstAttributePtr class_;
806 bool reselected = false;
807 bool both_global = false;
808 uint32_t original_subnet_id = env.subnet_id_;
809
810 if (result == REJECT_RC) {
811 // Create the host with no attributes.
812 // Should we saved them for the Class?
813 recv_attrs.reset();
814 // Reset subnet
815 env.subnet_id_ = SUBNET_ID_UNUSED;
816 reselected = true;
817 } else if (result != OK_RC) {
818 // Error case
820 .arg(result)
821 .arg(exchangeRCtoText(result));
822 // what to do? For now nothing!?
823 // drop = true;
824 return;
825 } else if (recv_attrs) {
826 // Pickup interesting things in received attributes.
827 ip6_address = recv_attrs->get(PW_FRAMED_IPV6_ADDRESS);
828 prefix = recv_attrs->get(PW_DELEGATED_IPV6_PREFIX);
829 framed_pool = recv_attrs->get(PW_FRAMED_POOL);
830 class_ = recv_attrs->get(PW_CLASS);
831 // etc
832 }
833
834 // Set pool.
835 string cclass;
836 if (framed_pool && (framed_pool->getValueType() == PW_TYPE_STRING)) {
837 cclass = framed_pool->toString();
838 if (query) {
839 query->addClass(cclass);
840 }
841 }
842
843 // Get IPv6 address.
845 if (ip6_address && (ip6_address->getValueType() == PW_TYPE_IPV6ADDR)) {
846 addr6 = ip6_address->toIpv6Addr();
847 }
848
849 // Get prefix.
850 uint8_t pref_len = 0;
852 if (prefix && (prefix->getValueType() == PW_TYPE_IPV6PREFIX)) {
853 pref_len = prefix->toIpv6PrefixLen();
854 pref_addr = prefix->toIpv6Prefix();
855 }
856
857 // Check reselection using pool and client-class.
858 if (query && !reselected && !cclass.empty() &&
859 impl.reselect_subnet_pool_) {
860 reselected = reselectSubnet(query, env.subnet_id_, both_global, cclass);
861 }
862
863 // Check reselection using reserved address and range.
864 if (query && !reselected && !addr6.isV6Zero() &&
865 impl.reselect_subnet_address_) {
866 reselected = reselectSubnet(query, env.subnet_id_, both_global, addr6);
867 }
868
869 // No reselection using prefix.
870
871 // Get host identifier.
872 Host::IdentifierType type = impl.id_type6_;
873 CacheHostDataSourcePtr cache = impl.cache_;
875 if (reselected) {
876 // Add subnet-id in user context.
877 map->set("subnet-id",
878 Element::create(static_cast<int>(env.subnet_id_)));
879 }
880
881 // Create and insert a reselecting entry.
882 uint32_t host_subnet_id = SUBNET_ID_UNUSED;
883 if (reselected && !both_global) {
885 getCfgSubnets6()->getBySubnetId(original_subnet_id);
886 if (!subnet) {
887 isc_throw(Unexpected, "no original subnet " << original_subnet_id);
888 }
889 host_subnet_id =
890 ((subnet && subnet->getReservationsGlobal()) ?
891 SUBNET_ID_GLOBAL : original_subnet_id);
892
893 // Create.
894 HostPtr host(new Host(&env.id_[0], env.id_.size(), type,
895 SUBNET_ID_UNUSED, host_subnet_id,
897
898 // Add reselect in the user-context and reset it.
899 host->setContext(map);
900 map = Element::createMap();
901
902 // Negative entry.
903 host->setNegative(true);
904
905 // Insert it.
906 if (!cache) {
907 return;
908 }
909 static_cast<void>(cache->insert(host, true));
910
911 ostringstream msg;
912 msg << "subnet-id := " << env.subnet_id_;
914 .arg(host->toText())
915 .arg(msg.str());
916 }
917
918 // Return if the subnet is null. Note in this case both_global
919 // is always false.
920 if (env.subnet_id_ == SUBNET_ID_UNUSED) {
921 return;
922 }
923
924 // Build a host entry to cache radius attributes from OK_RC answer.
926 getCfgSubnets6()->getBySubnetId(env.subnet_id_);
927 if (!subnet) {
928 isc_throw(Unexpected, "no subnet " << env.subnet_id_);
929 }
930 host_subnet_id =
931 ((subnet && subnet->getReservationsGlobal()) ?
932 SUBNET_ID_GLOBAL : env.subnet_id_);
933 HostPtr host(new Host(&env.id_[0], env.id_.size(), type,
934 SUBNET_ID_UNUSED, host_subnet_id,
936
937 // Save received attributes.
938 if (recv_attrs) {
939 map->set("radius", recv_attrs->toElement());
940 }
941 host->setContext(map);
942
943 // Add IPv6 address.
944 bool has_reservation = false;
945 if (!addr6.isV6Zero()) {
946 try {
947 host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, addr6));
948 has_reservation = true;
949 } catch (...) {
950 }
951 }
952
953 // Add delegated prefix.
954 if (pref_len && !pref_addr.isV6Zero()) {
955 try {
956 host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, pref_addr,
957 pref_len));
958 has_reservation = true;
959 } catch (...) {
960 }
961 }
962
963 if (!has_reservation) {
964 // The entry has no reservation nor hostname so mark it as negative.
965 host->setNegative(true);
966 }
967
968 // Insert entry.
969 if (!cache) {
970 return;
971 }
972 cache->insert(host, true);
973
975 .arg(host->toText())
976 .arg(recv_attrs ? recv_attrs->toText() : "");
977
978 // Pass the subnet to the server code.
979 if (query) {
980 CalloutHandlePtr callout_handle = getCalloutHandle(query);
981 callout_handle->setContext("subnet6", subnet);
982 }
983}
984
985void
987 AttributesPtr recv_attrs) {
988 Pkt6Ptr query;
989 bool drop = false;
990 try {
991 terminate6Internal(env, result, recv_attrs, query, drop);
992 } catch (const std::exception& ex) {
993 // Unexpected error.
995 .arg(ex.what());
996 drop = true;
997 } catch (...) {
998 // Unexpected unknown error.
1000 .arg("unknown error");
1001 drop = true;
1002 }
1003 if (!query) {
1004 return;
1005 }
1006 if (drop) {
1009 .arg(query->getLabel());
1010 StatsMgr::instance().addValue("pkt6-processing-failed",
1011 static_cast<int64_t>(1));
1012 StatsMgr::instance().addValue("pkt6-receive-drop",
1013 static_cast<int64_t>(1));
1014 HooksManager::drop("subnet6_select", query);
1015 } else {
1016 ostringstream msg;
1017 if (env.subnet_id_ == SUBNET_ID_UNUSED) {
1018 msg << "no subnet";
1019 } else {
1020 msg << "subnet " << env.subnet_id_;
1021 }
1024 .arg(query->getLabel())
1025 .arg(msg.str());
1026 HooksManager::unpark("subnet6_select", query);
1027 }
1028}
1029
1030void
1034 if (idle_timer_interval_ <= 0) {
1035 return;
1036 }
1037 // Cope to one day.
1038 long secs = idle_timer_interval_;
1039 if (secs > 24*60*60) {
1040 secs = 24*60*60;
1041 }
1042 idle_timer_.reset(new IntervalTimer(RadiusImpl::instance().getIOContext()));
1044 secs * 1000, IntervalTimer::REPEATING);
1045}
1046
1047void
1049 AttributesPtr send_attrs;
1050 RadiusAuthStatusPtr handler(new RadiusAuthStatus(send_attrs, 0));
1051 RadiusImpl::instance().registerExchange(handler->getExchange());
1052 handler->start();
1053}
1054
1055} // end of namespace isc::radius
1056} // end of namespace isc
static ElementPtr create(const Position &pos=ZERO_POSITION())
Create a NullElement.
Definition data.cc:300
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:355
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
A generic exception that is thrown when an unexpected error condition occurs.
static CfgMgr & instance()
returns a single instance of Configuration Manager
Definition cfgmgr.cc:29
SrvConfigPtr getCurrentCfg()
Returns a pointer to the current configuration.
Definition cfgmgr.cc:116
Holds subnets configured for the DHCPv4 server.
static SubnetSelector initSelector(const Pkt4Ptr &query)
Build selector from a client's message.
Holds subnets configured for the DHCPv6 server.
static SubnetSelector initSelector(const Pkt6Ptr &query)
Build selector from a client's message.
Holds Client identifier or client IPv4 address.
Definition duid.h:222
Holds DUID (DHCPv6 Unique Identifier).
Definition duid.h:142
Represents a device with IPv4 and/or IPv6 reservations.
Definition host.h:330
static std::string getIdentifierName(const IdentifierType &type)
Returns name of the identifier of a specified type.
Definition host.cc:356
IdentifierType
Type of the host identifier.
Definition host.h:340
@ IDENT_FLEX
Flexible host identifier.
Definition host.h:345
@ IDENT_CLIENT_ID
Definition host.h:344
@ IDENT_CIRCUIT_ID
Definition host.h:343
IPv6 reservation for a host.
Definition host.h:166
Represents DHCPv4 packet.
Definition pkt4.h:37
std::string getLabel() const
Returns text representation of the primary packet identifiers.
Definition pkt4.cc:367
HWAddrPtr getHWAddr() const
returns hardware address information
Definition pkt4.h:325
Represents a DHCPv6 packet.
Definition pkt6.h:44
virtual std::string getLabel() const
Returns text representation of the primary packet identifiers.
Definition pkt6.cc:730
OptionPtr getOption(const uint16_t type)
Returns the first option of specified type.
Definition pkt.cc:71
HWAddrPtr getMAC(uint32_t hw_addr_src)
Returns MAC address.
Definition pkt.cc:196
static bool unpark(const std::string &hook_name, T parked_object)
Forces unparking the object (packet).
static bool drop(const std::string &hook_name, T parked_object)
Removes parked object without calling a callback.
static AttributePtr fromString(const uint8_t type, const std::string &value)
From type specific factories.
Collection of attributes.
static void terminate6Internal(RadiusAuthEnv &env, int result, AttributesPtr recv_attrs, dhcp::Pkt6Ptr &query, bool &drop)
Termination callback body - IPv6.
static void terminate4Internal(RadiusAuthEnv &env, int result, AttributesPtr recv_attrs, dhcp::Pkt4Ptr &query, bool &drop)
Termination callback body - IPv4.
static void terminate4(RadiusAuthEnv env, int result, AttributesPtr recv_attrs)
Termination callback - IPv4.
static bool reselectSubnet(const dhcp::Pkt4Ptr &query, uint32_t &subnet_id, bool &both_global, const std::string &cclass)
Subnet reselect - class/pool IPv4.
static void terminate6(RadiusAuthEnv env, int result, AttributesPtr recv_attrs)
Termination callback - IPv6.
bool getIdentifier(dhcp::Pkt4 &query, std::vector< uint8_t > &id, std::string &text)
Get Identifier – IPv4.
void setIdleTimer()
Set idle timer.
static void IdleTimerCallback()
Idle timer callback.
RadiusAuthHandlerPtr buildAuth(dhcp::Pkt4 &query, uint32_t subnet_id, const std::vector< uint8_t > &id, const std::string &text)
Build RadiusAuth handler for Access-Request - IPv4.
class for asynchronous authentication communication with servers.
Class of Radius access environments.
RadiusAuthEnv(uint32_t subnet_id, const std::vector< uint8_t > &id, AttributesPtr send_attrs)
Constructor.
const std::vector< uint8_t > id_
Identifier.
uint32_t subnet_id_
Subnet Id (aka client/NAS port).
AttributesPtr send_attrs_
Attributes to send.
Class of Radius access communication handler.
RadiusAuthHandler(RadiusAuthEnv env, const CallbackAuth &callback)
Constructor.
RadiusAuthEnv env_
Environment.
void start()
Start communication.
RadiusAsyncAuthPtr auth_
Pointer to the communication class.
Class for communication with access servers.
Radius hooks library implementation.
Definition radius.h:151
dhcp::Host::IdentifierType id_type4_
Identifier type for IPv4.
Definition radius.h:347
void registerExchange(ExchangePtr exchange)
Register Exchange.
Definition radius.cc:227
dhcp::Host::IdentifierType id_type6_
Identifier type for IPv6.
Definition radius.h:350
static RadiusImpl & instance()
RadiusImpl is a singleton class.
Definition radius.cc:194
CfgAttributes attributes_
Attribute configurations.
RadiusService(const std::string &name)
Constructor.
asiolink::IntervalTimerPtr idle_timer_
Idle timer.
long idle_timer_interval_
Idle timer interval in seconds.
void cancelIdleTimer()
Cancel idle timer.
static std::mutex idle_timer_mutex_
Idle timer mutex.
static StatsMgr & instance()
Statistics Manager accessor method.
@ D6O_CLIENTID
Definition dhcp6.h:21
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
void addValue(const std::string &name, const int64_t value)
Records incremental integer observation.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
boost::shared_ptr< Element > ElementPtr
Definition data.h:29
@ RAI_OPTION_AGENT_CIRCUIT_ID
Definition dhcp4.h:265
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition subnet.h:620
std::vector< uint32_t > CfgMACSources
Container for defined MAC/hardware address sources.
boost::shared_ptr< Host > HostPtr
Pointer to the Host object.
Definition host.h:840
@ DHO_DHCP_CLIENT_IDENTIFIER
Definition dhcp4.h:130
@ DHO_DHCP_AGENT_OPTIONS
Definition dhcp4.h:151
boost::shared_ptr< const Subnet4 > ConstSubnet4Ptr
A const pointer to a Subnet4 object.
Definition subnet.h:455
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition pkt4.h:556
boost::shared_ptr< DUID > DuidPtr
Definition duid.h:136
boost::multi_index_container< Subnet6Ptr, boost::multi_index::indexed_by< boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetSubnetIdIndexTag >, boost::multi_index::const_mem_fun< Subnet, SubnetID, &Subnet::getID > >, boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetPrefixIndexTag >, boost::multi_index::const_mem_fun< Subnet, std::string, &Subnet::toText > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SubnetModificationTimeIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, boost::posix_time::ptime, &data::BaseStampedElement::getModificationTime > > > > Subnet6Collection
A collection of Subnet6 objects.
Definition subnet.h:934
std::vector< PoolPtr > PoolCollection
a container for either IPv4 or IPv6 Pools
Definition pool.h:745
boost::shared_ptr< CfgSubnets6 > CfgSubnets6Ptr
Non-const pointer.
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:166
isc::hooks::CalloutHandlePtr getCalloutHandle(const T &pktptr)
CalloutHandle Store.
boost::multi_index_container< Subnet4Ptr, boost::multi_index::indexed_by< boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetSubnetIdIndexTag >, boost::multi_index::const_mem_fun< Subnet, SubnetID, &Subnet::getID > >, boost::multi_index::ordered_unique< boost::multi_index::tag< SubnetPrefixIndexTag >, boost::multi_index::const_mem_fun< Subnet, std::string, &Subnet::toText > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SubnetServerIdIndexTag >, boost::multi_index::const_mem_fun< Network4, asiolink::IOAddress, &Network4::getServerId > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag< SubnetModificationTimeIndexTag >, boost::multi_index::const_mem_fun< data::BaseStampedElement, boost::posix_time::ptime, &data::BaseStampedElement::getModificationTime > > > > Subnet4Collection
A collection of Subnet4 objects.
Definition subnet.h:863
boost::shared_ptr< CacheHostDataSource > CacheHostDataSourcePtr
CacheHostDataSource pointer.
boost::shared_ptr< ClientId > ClientIdPtr
Shared pointer to a Client ID.
Definition duid.h:216
boost::shared_ptr< CfgSubnets4 > CfgSubnets4Ptr
Non-const pointer.
boost::shared_ptr< const CfgSubnets4 > ConstCfgSubnets4Ptr
Const pointer.
boost::shared_ptr< const CfgSubnets6 > ConstCfgSubnets6Ptr
Const pointer.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition pkt6.h:31
boost::shared_ptr< Option > OptionPtr
Definition option.h:38
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
@ PW_DELEGATED_IPV6_PREFIX
ipv6prefix.
@ PW_FRAMED_IPV6_ADDRESS
ipv6addr.
vector< uint8_t > extractDuid(const ClientIdPtr &client_id, bool &extracted)
Extract the duid from a RFC 4361 compliant DHCPv4 client ID.
string canonize(const string &hexdump)
Canonize hardware address textual representation.
const isc::log::MessageID RADIUS_ACCESS_BUILD_FAILED
boost::shared_ptr< Attributes > AttributesPtr
Shared pointers to attribute collection.
const isc::log::MessageID RADIUS_ACCESS_RESUME_PARKED_QUERY
boost::shared_ptr< const Attribute > ConstAttributePtr
const isc::log::MessageID RADIUS_ACCESS_TERMINATE_ERROR
boost::shared_ptr< RadiusAuthPendingRequest< PktPtrType > > RadiusAuthPendingRequestPtr
Pointer to a pending Radius access request.
string exchangeRCtoText(const int rc)
ExchangeRC value -> name function.
const isc::log::MessageID RADIUS_ACCESS_CACHE_INSERT
const isc::log::MessageID RADIUS_ACCESS_GET_IDENTIFIER_FAILED
const int RADIUS_DBG_TRACE
Radius logging levels.
Definition radius_log.h:26
boost::shared_ptr< RadiusAuthStatus > RadiusAuthStatusPtr
Pointer to access status.
string toPrintable(const vector< uint8_t > &content)
Return printable textual representation of a vector.
const isc::log::MessageID RADIUS_ACCESS_ORPHAN
const isc::log::MessageID RADIUS_ACCESS_ERROR
const isc::log::MessageID RADIUS_ACCESS_DROP_PARKED_QUERY
std::function< void(int, AttributesPtr)> CallbackAuth
Type of callback for authentication termination.
boost::shared_ptr< RadiusAuthHandler > RadiusAuthHandlerPtr
Type of pointers to Radius access communication handler.
isc::log::Logger radius_logger("radius-hooks")
Radius Logger.
Definition radius_log.h:35
vector< uint8_t > pop0(const ClientIdPtr &client_id)
Pop leading zero in a DHCPv4 client-id.
const isc::log::MessageID RADIUS_ACCESS_GET_IDENTIFIER
string dumpAsHex(const uint8_t *data, size_t length)
Dumps a buffer of bytes as a string of hexadecimal digits.
Definition str.cc:330
Defines the logger used by the top-level component of kea-lfc.
@ TYPE_V4
IPv4 lease.
Definition lease.h:52
@ TYPE_NA
the lease contains non-temporary IPv6 address
Definition lease.h:49
Subnet selector used to specify parameters used to select a subnet.
RAII lock object to protect the code in the same scope with a mutex.