Kea 3.1.0
radius_access.cc
Go to the documentation of this file.
1// Copyright (C) 2020-2025 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>
14#include <radius_access.h>
15#include <radius_log.h>
16#include <radius_utils.h>
18#include <stdio.h>
19#include <sstream>
20
21using namespace std;
22using namespace isc;
23using namespace isc::asiolink;
24using namespace isc::data;
25using namespace isc::dhcp;
26using namespace isc::hooks;
27using namespace isc::util;
28namespace ph = std::placeholders;
29
30namespace {
31
32// From Dhcpv6Srv.
34getMAC(Pkt6& pkt) {
36 getMACSources().get();
37 HWAddrPtr hwaddr;
38 for (auto const& source : mac_sources) {
39 hwaddr = pkt.getMAC(source);
40 if (hwaddr) {
41 return (hwaddr);
42 }
43 }
44 return (hwaddr);
45}
46
47} // end of anonymous namespace.
48
49namespace isc {
50namespace radius {
51
53 const std::vector<uint8_t>& id,
54 AttributesPtr send_attrs)
55 : subnet_id_(subnet_id), id_(id), send_attrs_(send_attrs) {
56}
57
59 const CallbackAuth& callback)
60 : env_(env), auth_() {
61 auth_.reset(new RadiusAsyncAuth(env.subnet_id_, env_.send_attrs_, callback));
62 RadiusImpl::instance().registerExchange(auth_->getExchange());
63 // set the IO service when async access will be available.
64}
65
66void
68 auth_->start();
69}
70
73
74bool
76 std::vector<uint8_t>& id,
77 std::string& text) {
79 try {
80 HWAddrPtr hwaddr;
81 OptionPtr option;
82 ClientIdPtr clientid;
83 bool extracted = false;
84 switch (type) {
86 hwaddr = query.getHWAddr();
87 if (!hwaddr) {
88 isc_throw(BadValue, "no hardware address");
89 }
90 id = hwaddr->hwaddr_;
91 if (id.empty()) {
92 isc_throw(BadValue, "empty hardware address");
93 }
94 text = query.getHWAddr()->toText(false);
95 if (RadiusImpl::instance().canonical_mac_address_) {
96 text = canonize(text);
97 }
98 break;
99
100 case Host::IDENT_DUID:
102 if (!option) {
103 isc_throw(BadValue, "no client-id option");
104 }
105 clientid.reset(new ClientId(option->getData()));
106 id = clientid->getClientId();
107 if ((id.size() <= 5) || (id[0] != CLIENT_ID_OPTION_TYPE_DUID)) {
108 isc_throw(BadValue, "no DUID in client-id option");
109 }
110 id = vector<uint8_t>(id.begin() + 5, id.end());
111 if (RadiusImpl::instance().clientid_printable_) {
112 text = toPrintable(id);
113 } else {
114 text = toHex(id);
115 }
116 break;
117
119 option = query.getOption(DHO_DHCP_AGENT_OPTIONS);
120 if (!option) {
121 isc_throw(BadValue, "no relay agent options");
122 }
123 option = option->getOption(RAI_OPTION_AGENT_CIRCUIT_ID);
124 if (!option) {
125 isc_throw(BadValue, "no circuit-id option");
126 }
127 id = option->getData();
128 if (id.empty()) {
129 isc_throw(BadValue, "empty circuit-id option");
130 }
131 if (RadiusImpl::instance().clientid_printable_) {
132 text = toPrintable(id);
133 } else {
134 text = toHex(id);
135 }
136 break;
137
140 if (!option) {
141 isc_throw(BadValue, "no client-id option");
142 }
143 clientid.reset(new ClientId(option->getData()));
144 id = clientid->getClientId();
145 if (id.empty()) {
146 isc_throw(BadValue, "empty client-id option");
147 }
148 if (RadiusImpl::instance().extract_duid_) {
149 text = toHex(extractDuid(clientid, extracted));
150 }
151 if (extracted) {
152 break;
153 }
154 if (RadiusImpl::instance().clientid_pop0_) {
155 vector<uint8_t> popped = pop0(clientid);
156 if (RadiusImpl::instance().clientid_printable_) {
157 text = toPrintable(popped);
158 } else {
159 text = toHex(popped);
160 }
161 } else if (RadiusImpl::instance().clientid_printable_) {
162 text = toPrintable(id);
163 } else {
164 text = toHex(id);
165 }
166 break;
167
168 case Host::IDENT_FLEX:
169 // Relies on replace-client-id flex-id parameter to be true.
171 if (!option) {
172 isc_throw(BadValue, "no client-id option");
173 }
174 clientid.reset(new ClientId(option->getData()));
175 id = clientid->getClientId();
176 if ((id.size() <= 1) || (id[0] != 0)) {
177 isc_throw(BadValue, "no flex-id in client-id option");
178 }
179 id = vector<uint8_t>(id.begin() + 1, id.end());
180 if (RadiusImpl::instance().clientid_printable_) {
181 text = toPrintable(id);
182 } else {
183 text = toHex(id);
184 }
185 break;
186
187 default:
188 isc_throw(OutOfRange, "unsupported identifier type " << type);
189 }
190 } catch (const std::exception& ex) {
192 .arg(Host::getIdentifierName(type))
193 .arg(query.getLabel())
194 .arg(ex.what());
195 id.clear();
196 text.clear();
197 return (false);
198 }
200 .arg(toHex(id))
201 .arg(Host::getIdentifierName(type))
202 .arg(text)
203 .arg(query.getLabel());
204 return (true);
205}
206
207bool
209 std::vector<uint8_t>& id,
210 std::string& text) {
212 try {
213 HWAddrPtr hwaddr;
214 OptionPtr option;
215 DuidPtr duid;
216 switch (type) {
217 case Host::IDENT_DUID:
218 option = query.getOption(D6O_CLIENTID);
219 if (!option) {
220 isc_throw(BadValue, "no client-id option");
221 }
222 duid.reset(new DUID(option->getData()));
223 id = duid->getDuid();
224 if (id.empty()) {
225 isc_throw(BadValue, "empty client-id option");
226 }
227 if (RadiusImpl::instance().clientid_pop0_) {
228 vector<uint8_t> popped = pop0(duid);
229 if (RadiusImpl::instance().clientid_printable_) {
230 text = toPrintable(popped);
231 } else {
232 text = toHex(popped);
233 }
234 } else if (RadiusImpl::instance().clientid_printable_) {
235 text = toPrintable(id);
236 } else {
237 text = toHex(id);
238 }
239 break;
240
242 hwaddr = getMAC(query);
243 if (!hwaddr) {
244 isc_throw(BadValue, "no hardware address");
245 }
246 id = hwaddr->hwaddr_;
247 if (id.empty()) {
248 isc_throw(BadValue, "empty hardware address");
249 }
250 text = hwaddr->toText(false);
251 if (RadiusImpl::instance().canonical_mac_address_) {
252 text = canonize(text);
253 }
254 break;
255
256 case Host::IDENT_FLEX:
257 option = query.getOption(D6O_CLIENTID);
258 if (!option) {
259 isc_throw(BadValue, "no client-id option");
260 }
261 duid.reset(new DUID(option->getData()));
262 id = duid->getDuid();
263 if ((id.size() <= 2) || (id[0] != 0) || (id[1] != 0)) {
264 isc_throw(BadValue, "no flex-id in client-id option");
265 }
266 id = vector<uint8_t>(id.begin() + 2, id.end());
267 if (RadiusImpl::instance().clientid_printable_) {
268 text = toPrintable(id);
269 } else {
270 text = toHex(id);
271 }
272 break;
273
274 default:
275 isc_throw(OutOfRange, "unsupported identifier type " << type);
276 }
277 } catch (const std::exception& ex) {
279 .arg(Host::getIdentifierName(type))
280 .arg(query.getLabel())
281 .arg(ex.what());
282 id.clear();
283 text.clear();
284 return (false);
285 }
287 .arg(toHex(id))
288 .arg(Host::getIdentifierName(type))
289 .arg(text)
290 .arg(query.getLabel());
291 return (true);
292}
293
296 uint32_t subnet_id,
297 const std::vector<uint8_t>& id,
298 const std::string& text) {
299 AttributesPtr send(new Attributes());
300
301 try {
302 if (subnet_id == SUBNET_ID_UNUSED) {
303 isc_throw(BadValue, "subnet ID is reserved");
304 }
305
306 send->add(Attribute::fromString(PW_USER_NAME, text));
307
308 // Add hardware address.
309 HWAddrPtr hwaddr = query.getHWAddr();
310 if (RadiusImpl::instance().id_type4_ != Host::IDENT_HWADDR &&
311 hwaddr && !hwaddr->hwaddr_.empty()) {
312 string hw = hwaddr->toText(false);
313 if (RadiusImpl::instance().canonical_mac_address_) {
314 hw = canonize(hw);
315 }
317 }
318
319 // Add attributes from configuration.
320 send->append(RadiusImpl::instance().auth_->
321 attributes_.getEvalAll(query));
322
323 } catch (const std::exception& ex) {
325 .arg(ex.what())
326 .arg(query.getLabel());
327 return (RadiusAuthHandlerPtr());
328 }
329
330 // Return the handler.
331 RadiusAuthEnv env(subnet_id, id, send);
332 RadiusAuthHandlerPtr handler;
333 handler.reset(new RadiusAuthHandler(env,
334 std::bind(&RadiusAccess::terminate4,
335 env, ph::_1, ph::_2)));
336 return (handler);
337}
338
341 uint32_t subnet_id,
342 const std::vector<uint8_t>& id,
343 const std::string& text) {
344 AttributesPtr send(new Attributes());
345
346 try {
347 if (subnet_id == SUBNET_ID_UNUSED) {
348 isc_throw(BadValue, "subnet ID is reserved");
349 }
350
351 send->add(Attribute::fromString(PW_USER_NAME, text));
352
353 // Add hardware address.
354 HWAddrPtr hwaddr = getMAC(query);
355 if (RadiusImpl::instance().id_type6_ != Host::IDENT_HWADDR &&
356 hwaddr && !hwaddr->hwaddr_.empty()) {
357 string hw = hwaddr->toText(false);
358 if (RadiusImpl::instance().canonical_mac_address_) {
359 hw = canonize(hw);
360 }
362 }
363
364 // Add attributes from configuration.
365 send->append(RadiusImpl::instance().auth_->
366 attributes_.getEvalAll(query));
367
368 } catch (const std::exception& ex) {
370 .arg(ex.what())
371 .arg(query.getLabel());
372 return (RadiusAuthHandlerPtr());
373 }
374
375 // Return the handler.
376 RadiusAuthEnv env(subnet_id, id, send);
377 RadiusAuthHandlerPtr handler;
378 handler.reset(new RadiusAuthHandler(env,
379 std::bind(&RadiusAccess::terminate6,
380 env, ph::_1, ph::_2)));
381 return (handler);
382}
383
384bool
385RadiusAccess::reselectSubnet(const dhcp::Pkt4Ptr& query, uint32_t& subnet_id,
386 bool& both_global, const std::string& cclass) {
387 both_global = false;
388 ConstCfgSubnets4Ptr subnets4 =
389 CfgMgr::instance().getCurrentCfg()->getCfgSubnets4();
390
391 // Check selected subnet.
392 ConstSubnet4Ptr subnet = subnets4->getBySubnetId(subnet_id);
393 if (!subnet) {
394 return (false);
395 }
396 // If one of the pools accepts the client-class we're done.
397 if (subnet->clientSupported(query->classes_)) {
398 const PoolCollection& pools = subnet->getPools(Lease::TYPE_V4);
399 for (auto const& pool : pools) {
400 if (pool->clientSupported(cclass)) {
401 return (false);
402 }
403 }
404 }
405 // Check if this subnet uses global reservations.
406 bool use_global = subnet->getReservationsGlobal();
407
408 // Try other selectable subnets.
409 const Subnet4Collection* subnets = subnets4->getAll();
410 CfgSubnets4Ptr selectable(new CfgSubnets4());
411
412 for (auto const& iter : *subnets) {
413 // Check pools.
414 const PoolCollection& pools = iter->getPools(Lease::TYPE_V4);
415 for (auto const& pool : pools) {
416 if (pool->clientSupported(cclass)) {
417 selectable->add(iter);
418 break;
419 }
420 }
421 }
422
423 const SubnetSelector& selector = CfgSubnets4::initSelector(query);
424 subnet = selectable->selectSubnet(selector);
425
426 if (!subnet) {
427 subnet_id = SUBNET_ID_UNUSED;
428 } else {
429 subnet_id = subnet->getID();
430 if (use_global && subnet->getReservationsGlobal()) {
431 both_global = true;
432 }
433 }
434 return (true);
435}
436
437bool
438RadiusAccess::reselectSubnet(const dhcp::Pkt6Ptr& query, uint32_t& subnet_id,
439 bool& both_global, const std::string& cclass) {
440 both_global = false;
441 ConstCfgSubnets6Ptr subnets6 =
442 CfgMgr::instance().getCurrentCfg()->getCfgSubnets6();
443
444 // Check selected subnet.
445 ConstSubnet6Ptr subnet = subnets6->getBySubnetId(subnet_id);
446 if (!subnet) {
447 return (false);
448 }
449 // If one of the pools accepts the client-class we're done.
450 if (subnet->clientSupported(query->classes_)) {
451 const PoolCollection& pools = subnet->getPools(Lease::TYPE_NA);
452 for (auto const& pool : pools) {
453 if (pool->clientSupported(cclass)) {
454 return (false);
455 }
456 }
457 }
458 // Check if this subnet uses global reservations.
459 bool use_global = subnet->getReservationsGlobal();
460
461 // Try other selectable subnets.
462 const Subnet6Collection* subnets = subnets6->getAll();
463 CfgSubnets6Ptr selectable(new CfgSubnets6());
464
465 for (auto const& iter : *subnets) {
466 // Check pools.
467 const PoolCollection& pools = iter->getPools(Lease::TYPE_NA);
468 for (auto const& pool : pools) {
469 if (pool->clientSupported(cclass)) {
470 selectable->add(iter);
471 break;
472 }
473 }
474 }
475
476 const SubnetSelector& selector = CfgSubnets6::initSelector(query);
477 subnet = selectable->selectSubnet(selector);
478
479 if (!subnet) {
480 subnet_id = SUBNET_ID_UNUSED;
481 } else {
482 subnet_id = subnet->getID();
483 if (use_global && subnet->getReservationsGlobal()) {
484 both_global = true;
485 }
486 }
487 return (true);
488}
489
490bool
492 uint32_t& subnet_id,
493 bool& both_global,
494 const asiolink::IOAddress& address) {
495 both_global = false;
496 ConstCfgSubnets4Ptr subnets4 =
497 CfgMgr::instance().getCurrentCfg()->getCfgSubnets4();
498
499 // Check selected subnet.
500 ConstSubnet4Ptr subnet = subnets4->getBySubnetId(subnet_id);
501 if (!subnet) {
502 return (false);
503 }
504 // If the reserved address is in range we're done.
505 if (subnet->clientSupported(query->classes_) && subnet->inRange(address)) {
506 return (false);
507 }
508 // Check if this subnet uses global reservations.
509 bool use_global = subnet->getReservationsGlobal();
510
511 // Select subnet by reserved address only.
512 subnet = subnets4->selectSubnet(address, query->classes_);
513 if (!subnet) {
514 subnet_id = SUBNET_ID_UNUSED;
515 } else {
516 subnet_id = subnet->getID();
517 if (use_global && subnet->getReservationsGlobal()) {
518 both_global = true;
519 }
520 }
521 return (true);
522}
523
524bool
526 uint32_t& subnet_id,
527 bool& both_global,
528 const asiolink::IOAddress& address) {
529 both_global = false;
530 ConstCfgSubnets6Ptr subnets6 =
531 CfgMgr::instance().getCurrentCfg()->getCfgSubnets6();
532
533 // Check selected subnet.
534 ConstSubnet6Ptr subnet = subnets6->getBySubnetId(subnet_id);
535 if (!subnet) {
536 return (false);
537 }
538 // If the reserved address is in range we're done.
539 if (subnet->clientSupported(query->classes_) && subnet->inRange(address)) {
540 return (false);
541 }
542 // Check if this subnet uses global reservations.
543 bool use_global = subnet->getReservationsGlobal();
544
545 // Select subnet by reserved address only.
546 subnet = subnets6->selectSubnet(address, query->classes_);
547 if (!subnet) {
548 subnet_id = SUBNET_ID_UNUSED;
549 } else {
550 subnet_id = subnet->getID();
551 if (use_global && subnet->getReservationsGlobal()) {
552 both_global = true;
553 }
554 }
555 return (true);
556}
557
558void
560 AttributesPtr recv_attrs,
561 Pkt4Ptr& query, bool& drop) {
563 MultiThreadingLock lock(impl.auth_->requests4_.mutex_);
564
565 // Get the pending request.
567 impl.auth_->requests4_.get(env.id_);
568 if (!pending_request) {
570 .arg(toHex(env.id_));
571 drop = true;
572 return;
573 }
574 // Outside some unit tests the query is never null.
575 query = pending_request->query_;
576 impl.auth_->requests4_.remove(env.id_);
577
578 // Process response.
579 ConstAttributePtr ip_address;
580 ConstAttributePtr framed_pool;
581 ConstAttributePtr class_;
582 bool reselected = false;
583 bool both_global = false;
584 uint32_t original_subnet_id = env.subnet_id_;
585
586 if (result == REJECT_RC) {
587 // Create the host with no attributes.
588 // Should we saved them for the Class?
589 recv_attrs.reset();
590 // Reset subnet
591 env.subnet_id_ = SUBNET_ID_UNUSED;
592 reselected = true;
593 } else if (result != OK_RC) {
594 // Error case
596 .arg(result)
597 .arg(exchangeRCtoText(result));
598 // what to do? For now nothing!?
599 // drop = true;
600 return;
601 } else if (recv_attrs) {
602 // Pickup interesting things in received attributes.
603 ip_address = recv_attrs->get(PW_FRAMED_IP_ADDRESS);
604 framed_pool = recv_attrs->get(PW_FRAMED_POOL);
605 class_ = recv_attrs->get(PW_CLASS);
606 // etc
607 }
608
609 // Set pool.
610 string cclass;
611 if (framed_pool && (framed_pool->getValueType() == PW_TYPE_STRING)) {
612 cclass = framed_pool->toString();
613 if (query) {
614 query->addClass(cclass);
615 }
616 }
617
618 // Get IPv4 address.
620 if (ip_address && (ip_address->getValueType() == PW_TYPE_IPADDR)) {
621 addr4 = ip_address->toIpAddr();
622 }
623
624 // Check reselection using pool and client-class.
625 if (query && !reselected && !cclass.empty() &&
626 impl.reselect_subnet_pool_) {
627 reselected = reselectSubnet(query, env.subnet_id_, both_global, cclass);
628 }
629
630 // Check reselection using reserved address and range.
631 if (query && !reselected && !addr4.isV4Zero() &&
632 impl.reselect_subnet_address_) {
633 reselected = reselectSubnet(query, env.subnet_id_, both_global, addr4);
634 }
635
636 // Get host identifier.
637 Host::IdentifierType type = impl.id_type4_;
638 CacheHostDataSourcePtr cache = impl.cache_;
640 if (reselected) {
641 // Add subnet-id in user context.
642 map->set("subnet-id",
643 Element::create(static_cast<int>(env.subnet_id_)));
644 }
645
646 // Create and insert a reselecting entry.
647 uint32_t host_subnet_id = SUBNET_ID_UNUSED;
648 if (reselected && !both_global) {
650 getCfgSubnets4()->getBySubnetId(original_subnet_id);
651 if (!subnet) {
652 isc_throw(Unexpected, "no original subnet " << original_subnet_id);
653 }
654 host_subnet_id =
655 ((subnet && subnet->getReservationsGlobal()) ?
656 SUBNET_ID_GLOBAL : original_subnet_id);
657
658 // Create.
659 HostPtr host(new Host(&env.id_[0], env.id_.size(), type,
660 host_subnet_id, SUBNET_ID_UNUSED,
662
663 // Add reselect in the user-context and reset it.
664 host->setContext(map);
665 map = Element::createMap();
666
667 // Negative entry.
668 host->setNegative(true);
669
670 // Insert it.
671 if (!cache) {
672 return;
673 }
674 static_cast<void>(cache->insert(host, true));
675
676 ostringstream msg;
677 msg << "subnet-id := " << env.subnet_id_;
679 .arg(host->toText())
680 .arg(msg.str());
681 }
682
683 // Return if the subnet is null. Note in this case both_global
684 // is always false.
685 if (env.subnet_id_ == SUBNET_ID_UNUSED) {
686 return;
687 }
688
689 // Build a host entry to cache radius attributes from OK_RC answer.
691 getCfgSubnets4()->getBySubnetId(env.subnet_id_);
692 if (!subnet) {
693 isc_throw(Unexpected, "no subnet " << env.subnet_id_);
694 }
695 host_subnet_id =
696 ((subnet && subnet->getReservationsGlobal()) ?
697 SUBNET_ID_GLOBAL : env.subnet_id_);
698 HostPtr host(new Host(&env.id_[0], env.id_.size(), type,
699 host_subnet_id, SUBNET_ID_UNUSED, addr4));
700
701 // Save received attributes.
702 if (recv_attrs) {
703 map->set("radius", recv_attrs->toElement());
704 }
705 host->setContext(map);
706
707 if (addr4.isV4Zero()) {
708 // The entry has no reservation nor hostname so mark it as negative.
709 host->setNegative(true);
710 }
711
712 // Insert entry.
713 if (!cache) {
714 return;
715 }
716 cache->insert(host, true);
717
719 .arg(host->toText())
720 .arg(recv_attrs ? recv_attrs->toText() : "");
721
722 // Pass the subnet to the server code.
723 if (query) {
724 CalloutHandlePtr callout_handle = getCalloutHandle(query);
725 callout_handle->setContext("subnet4", subnet);
726 }
727}
728
729void
731 AttributesPtr recv_attrs) {
732 Pkt4Ptr query;
733 bool drop = false;
734 try {
735 terminate4Internal(env, result, recv_attrs, query, drop);
736 } catch (const std::exception& ex) {
737 // Unexpected error.
739 .arg(ex.what());
740 drop = true;
741 } catch (...) {
742 // Unexpected unknown error.
744 .arg("unknown error");
745 drop = true;
746 }
747 if (!query) {
748 return;
749 }
750 if (drop) {
753 .arg(query->getLabel());
754 HooksManager::drop("subnet4_select", query);
755 } else {
756 ostringstream msg;
757 if (env.subnet_id_ == SUBNET_ID_UNUSED) {
758 msg << "no subnet";
759 } else {
760 msg << "subnet " << env.subnet_id_;
761 }
764 .arg(query->getLabel())
765 .arg(msg.str());
766 HooksManager::unpark("subnet4_select", query);
767 }
768}
769
770void
772 AttributesPtr recv_attrs,
773 Pkt6Ptr& query, bool& drop) {
775 MultiThreadingLock lock(impl.auth_->requests6_.mutex_);
776
777 // Get the pending request.
779 impl.auth_->requests6_.get(env.id_);
780 if (!pending_request) {
782 .arg(toHex(env.id_));
783 drop = true;
784 return;
785 }
786 // Outside some unit tests the query is never null.
787 query = pending_request->query_;
788 impl.auth_->requests6_.remove(env.id_);
789
790 // Process response.
791 ConstAttributePtr ip6_address;
792 ConstAttributePtr prefix;
793 ConstAttributePtr framed_pool;
794 ConstAttributePtr class_;
795 bool reselected = false;
796 bool both_global = false;
797 uint32_t original_subnet_id = env.subnet_id_;
798
799 if (result == REJECT_RC) {
800 // Create the host with no attributes.
801 // Should we saved them for the Class?
802 recv_attrs.reset();
803 // Reset subnet
804 env.subnet_id_ = SUBNET_ID_UNUSED;
805 reselected = true;
806 } else if (result != OK_RC) {
807 // Error case
809 .arg(result)
810 .arg(exchangeRCtoText(result));
811 // what to do? For now nothing!?
812 // drop = true;
813 return;
814 } else if (recv_attrs) {
815 // Pickup interesting things in received attributes.
816 ip6_address = recv_attrs->get(PW_FRAMED_IPV6_ADDRESS);
817 prefix = recv_attrs->get(PW_DELEGATED_IPV6_PREFIX);
818 framed_pool = recv_attrs->get(PW_FRAMED_POOL);
819 class_ = recv_attrs->get(PW_CLASS);
820 // etc
821 }
822
823 // Set pool.
824 string cclass;
825 if (framed_pool && (framed_pool->getValueType() == PW_TYPE_STRING)) {
826 cclass = framed_pool->toString();
827 if (query) {
828 query->addClass(cclass);
829 }
830 }
831
832 // Get IPv6 address.
834 if (ip6_address && (ip6_address->getValueType() == PW_TYPE_IPV6ADDR)) {
835 addr6 = ip6_address->toIpv6Addr();
836 }
837
838 // Get prefix.
839 uint8_t pref_len = 0;
841 if (prefix && (prefix->getValueType() == PW_TYPE_IPV6PREFIX)) {
842 pref_len = prefix->toIpv6PrefixLen();
843 pref_addr = prefix->toIpv6Prefix();
844 }
845
846 // Check reselection using pool and client-class.
847 if (query && !reselected && !cclass.empty() &&
848 impl.reselect_subnet_pool_) {
849 reselected = reselectSubnet(query, env.subnet_id_, both_global, cclass);
850 }
851
852 // Check reselection using reserved address and range.
853 if (query && !reselected && !addr6.isV6Zero() &&
854 impl.reselect_subnet_address_) {
855 reselected = reselectSubnet(query, env.subnet_id_, both_global, addr6);
856 }
857
858 // No reselection using prefix.
859
860 // Get host identifier.
861 Host::IdentifierType type = impl.id_type6_;
862 CacheHostDataSourcePtr cache = impl.cache_;
864 if (reselected) {
865 // Add subnet-id in user context.
866 map->set("subnet-id",
867 Element::create(static_cast<int>(env.subnet_id_)));
868 }
869
870 // Create and insert a reselecting entry.
871 uint32_t host_subnet_id = SUBNET_ID_UNUSED;
872 if (reselected && !both_global) {
874 getCfgSubnets6()->getBySubnetId(original_subnet_id);
875 if (!subnet) {
876 isc_throw(Unexpected, "no original subnet " << original_subnet_id);
877 }
878 host_subnet_id =
879 ((subnet && subnet->getReservationsGlobal()) ?
880 SUBNET_ID_GLOBAL : original_subnet_id);
881
882 // Create.
883 HostPtr host(new Host(&env.id_[0], env.id_.size(), type,
884 SUBNET_ID_UNUSED, host_subnet_id,
886
887 // Add reselect in the user-context and reset it.
888 host->setContext(map);
889 map = Element::createMap();
890
891 // Negative entry.
892 host->setNegative(true);
893
894 // Insert it.
895 if (!cache) {
896 return;
897 }
898 static_cast<void>(cache->insert(host, true));
899
900 ostringstream msg;
901 msg << "subnet-id := " << env.subnet_id_;
903 .arg(host->toText())
904 .arg(msg.str());
905 }
906
907 // Return if the subnet is null. Note in this case both_global
908 // is always false.
909 if (env.subnet_id_ == SUBNET_ID_UNUSED) {
910 return;
911 }
912
913 // Build a host entry to cache radius attributes from OK_RC answer.
915 getCfgSubnets6()->getBySubnetId(env.subnet_id_);
916 if (!subnet) {
917 isc_throw(Unexpected, "no subnet " << env.subnet_id_);
918 }
919 host_subnet_id =
920 ((subnet && subnet->getReservationsGlobal()) ?
921 SUBNET_ID_GLOBAL : env.subnet_id_);
922 HostPtr host(new Host(&env.id_[0], env.id_.size(), type,
923 SUBNET_ID_UNUSED, host_subnet_id,
925
926 // Save received attributes.
927 if (recv_attrs) {
928 map->set("radius", recv_attrs->toElement());
929 }
930 host->setContext(map);
931
932 // Add IPv6 address.
933 bool has_reservation = false;
934 if (!addr6.isV6Zero()) {
935 try {
936 host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, addr6));
937 has_reservation = true;
938 } catch (...) {
939 }
940 }
941
942 // Add delegated prefix.
943 if (pref_len && !pref_addr.isV6Zero()) {
944 try {
945 host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, pref_addr,
946 pref_len));
947 has_reservation = true;
948 } catch (...) {
949 }
950 }
951
952 if (!has_reservation) {
953 // The entry has no reservation nor hostname so mark it as negative.
954 host->setNegative(true);
955 }
956
957 // Insert entry.
958 if (!cache) {
959 return;
960 }
961 cache->insert(host, true);
962
964 .arg(host->toText())
965 .arg(recv_attrs ? recv_attrs->toText() : "");
966
967 // Pass the subnet to the server code.
968 if (query) {
969 CalloutHandlePtr callout_handle = getCalloutHandle(query);
970 callout_handle->setContext("subnet6", subnet);
971 }
972}
973
974void
976 AttributesPtr recv_attrs) {
977 Pkt6Ptr query;
978 bool drop = false;
979 try {
980 terminate6Internal(env, result, recv_attrs, query, drop);
981 } catch (const std::exception& ex) {
982 // Unexpected error.
984 .arg(ex.what());
985 drop = true;
986 } catch (...) {
987 // Unexpected unknown error.
989 .arg("unknown error");
990 drop = true;
991 }
992 if (!query) {
993 return;
994 }
995 if (drop) {
998 .arg(query->getLabel());
999 HooksManager::drop("subnet6_select", query);
1000 } else {
1001 ostringstream msg;
1002 if (env.subnet_id_ == SUBNET_ID_UNUSED) {
1003 msg << "no subnet";
1004 } else {
1005 msg << "subnet " << env.subnet_id_;
1006 }
1009 .arg(query->getLabel())
1010 .arg(msg.str());
1011 HooksManager::unpark("subnet6_select", query);
1012 }
1013}
1014
1015} // end of namespace isc::radius
1016} // end of namespace isc
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition data.cc:249
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:304
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:327
IdentifierType
Type of the host identifier.
Definition host.h:337
@ IDENT_FLEX
Flexible host identifier.
Definition host.h:342
@ IDENT_CLIENT_ID
Definition host.h:341
@ IDENT_CIRCUIT_ID
Definition host.h:340
static std::string getIdentifierName(const IdentifierType &type)
Returns name of the identifier of a specified type.
Definition host.cc:349
IPv6 reservation for a host.
Definition host.h:163
Represents DHCPv4 packet.
Definition pkt4.h:37
std::string getLabel() const
Returns text representation of the primary packet identifiers.
Definition pkt4.cc:365
HWAddrPtr getHWAddr() const
returns hardware address information
Definition pkt4.h:324
Represents a DHCPv6 packet.
Definition pkt6.h:44
virtual std::string getLabel() const
Returns text representation of the primary packet identifiers.
Definition pkt6.cc:720
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:187
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.
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.
Radius hooks library implementation.
Definition radius.h:50
dhcp::Host::IdentifierType id_type4_
Identifier type for IPv4.
Definition radius.h:202
dhcp::Host::IdentifierType id_type6_
Identifier type for IPv6.
Definition radius.h:205
static RadiusImpl & instance()
RadiusImpl is a singleton class.
Definition radius.cc:37
CfgAttributes attributes_
Attribute configurations.
RadiusService(const std::string &name)
Constructor.
@ D6O_CLIENTID
Definition dhcp6.h:21
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#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:28
@ DHO_DHCP_CLIENT_IDENTIFIER
Definition dhcp4.h:130
@ DHO_DHCP_AGENT_OPTIONS
Definition dhcp4.h:151
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition subnet.h:623
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:837
boost::shared_ptr< const Subnet4 > ConstSubnet4Ptr
A const pointer to a Subnet4 object.
Definition subnet.h:458
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition pkt4.h:555
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:937
std::vector< PoolPtr > PoolCollection
a container for either IPv4 or IPv6 Pools
Definition pool.h:729
boost::shared_ptr< CfgSubnets6 > CfgSubnets6Ptr
Non-const pointer.
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:154
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:866
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
@ RAI_OPTION_AGENT_CIRCUIT_ID
Definition dhcp4.h:265
boost::shared_ptr< Option > OptionPtr
Definition option.h:37
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
@ PW_DELEGATED_IPV6_PREFIX
ipv6prefix.
@ PW_FRAMED_IPV6_ADDRESS
ipv6addr.
boost::shared_ptr< RadiusAuthPendingRequest< PktPtrType > > RadiusAuthPendingRequestPtr
Pointer to a pending Radius access request.
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
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
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
string toHex(const vector< uint8_t > &content)
Return hexadecimal textual representation of a vector.
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
Defines the logger used by the top-level component of kea-lfc.
@ TYPE_V4
IPv4 lease.
Definition lease.h:50
@ TYPE_NA
the lease contains non-temporary IPv6 address
Definition lease.h:47
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.