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