Kea 3.3.3
radius_accounting.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
10#include <cc/simple_parser.h>
11#include <dhcpsrv/cfgmgr.h>
12#include <dhcpsrv/host_mgr.h>
13#include <dhcpsrv/subnet.h>
16#include <radius/radius_utils.h>
18
19#include <radius_log.h>
20#include <stdio.h>
21
22using namespace std;
23using namespace isc;
24using namespace isc::asiolink;
25using namespace isc::data;
26using namespace isc::dhcp;
27using namespace isc::util;
28using namespace isc::util::str;
29using namespace boost::gregorian;
30using namespace boost::posix_time;
31namespace ph = std::placeholders;
32
33namespace isc {
34namespace radius {
35
36string
38 switch (event) {
39 case EVENT_CREATE:
40 return ("create");
41 case EVENT_RENEW:
42 return ("renew");
43 case EVENT_REBIND:
44 return ("rebind");
45 case EVENT_EXPIRE:
46 return ("expire");
47 case EVENT_RELEASE:
48 return ("release");
49 case EVENT_DECLINE:
50 return ("decline");
51 case EVENT_ADD:
52 return ("add");
53 case EVENT_UPDATE:
54 return ("update");
55 case EVENT_DEL:
56 return ("delete");
57 default:
58 return ("unknown");
59 }
60}
61
62RadiusAcctEnv::RadiusAcctEnv(std::string session_id, Event event,
63 uint32_t subnet_id,
64 AttributesPtr send_attrs)
65 : session_id_(session_id), event_(event), subnet_id_(subnet_id),
66 send_attrs_(send_attrs), finished_(false) {
67}
68
70 const CallbackAcct& callback)
71 : env_(env), acct_() {
72 acct_.reset(new RadiusAsyncAcct(env_.subnet_id_, env_.send_attrs_, callback));
73 RadiusImpl::instance().registerExchange(acct_->getExchange());
74 MultiThreadingLock lock(mutex_);
75 ++counter_;
76}
77
79 MultiThreadingLock lock(mutex_);
80 if (counter_ > 0) {
81 --counter_;
82 }
83}
84
85void
87 acct_->start();
88}
89
90size_t
92 MultiThreadingLock lock(mutex_);
93 return (counter_);
94}
95
96size_t RadiusAcctHandler::counter_ = 0;
97
98mutex RadiusAcctHandler::mutex_;
99
101 : RadiusService("accounting"), epoch_(date(2018, 3, 7), hours(11)), record_count_(0) {
102}
103
104void
105RadiusAccounting::init(const std::string& filename) {
106 filename_ = filename;
107 if (!filename_.empty()) {
108 file_.reset(new CSVFile(filename_));
109
110 file_->addColumn("address");
111 file_->addColumn("seconds");
112 file_->addColumn("milliseconds");
113
114 if (file_->exists()) {
115 if (loadFromFile() && (container_.size() > 0)) {
116 storeToFile();
117 }
118 }
119 try {
120 file_->open(true);
122 .arg(filename_);
123 } catch (const std::exception& ex) {
125 .arg(filename_)
126 .arg(ex.what());
127 file_.reset();
128 }
129 }
130}
131
134 AttributesPtr send(new Attributes());
135
136 // Add client-id and hardware address.
137 string hwaddr = lease->hwaddr_->toText(false);
138 if (RadiusImpl::instance().canonical_mac_address_) {
139 hwaddr = canonize(hwaddr);
140 }
141 if (lease->client_id_) {
142 vector<uint8_t> vec;
143 bool extracted = false;
144 if (RadiusImpl::instance().extract_duid_) {
145 vec = extractDuid(lease->client_id_, extracted);
146 }
147 // If case the DUID was not extracted, we will try to get the
148 // identifier using client-id.
149 if (!extracted) {
150 if (RadiusImpl::instance().clientid_pop0_) {
151 vec = pop0(lease->client_id_);
152 } else {
153 vec = lease->client_id_->getClientId();
154 }
155 }
156 string text;
157 if (RadiusImpl::instance().clientid_printable_) {
158 text = toPrintable(vec);
159 } else {
160 text = dumpAsHex(vec);
161 }
162
163 send->add(Attribute::fromString(PW_USER_NAME, text));
165 } else {
166 send->add(Attribute::fromString(PW_USER_NAME, hwaddr));
167 }
168
169 // Add address.
170 send->add(Attribute::fromIpAddr(PW_FRAMED_IP_ADDRESS, lease->addr_));
171
172 // Get the create timestamp.
173 bool generate = false;
174 if ((event == EVENT_CREATE) || (event == EVENT_ADD)) {
175 generate = true;
176 }
177 ptime tm = getCreateTimestamp(lease->addr_, generate);
178
179 // Build the session Id.
180 ostringstream stream;
181 stream << lease->addr_.toText() << '@';
182 time_duration rtm(tm - epoch_);
183 stream << rtm.total_seconds() << ".";
184 rtm -= seconds(rtm.total_seconds());
185 stream << rtm.total_milliseconds();
186 send->add(Attribute::fromString(PW_ACCT_SESSION_ID, stream.str()));
187
188 // Accounting status type.
189 uint32_t status = PW_STATUS_ALIVE;
190 if (generate) {
191 status = PW_STATUS_START;
192 } else if ((event == EVENT_EXPIRE) ||
193 (event == EVENT_RELEASE) ||
194 (event == EVENT_DECLINE) ||
195 (event == EVENT_DEL)) {
196 status = PW_STATUS_STOP;
197 }
198 send->add(Attribute::fromInt(PW_ACCT_STATUS_TYPE, status));
199
200 // Add class: get host identifier.
202 vector<uint8_t> host_id;
203 switch (id_type) {
205 host_id = lease->hwaddr_->hwaddr_;
206 break;
207 case Host::IDENT_DUID:
208 if (lease->client_id_) {
209 host_id = lease->client_id_->getClientId();
210 if ((host_id.size() <= 5) ||
211 (host_id[0] != CLIENT_ID_OPTION_TYPE_DUID)) {
212 host_id.clear();
213 } else {
214 host_id = vector<uint8_t>(host_id.begin() + 5, host_id.end());
215 }
216 }
217 break;
219 if (lease->client_id_) {
220 host_id = lease->client_id_->getClientId();
221 }
222 break;
223 case Host::IDENT_FLEX:
224 if (lease->client_id_) {
225 host_id = lease->client_id_->getClientId();
226 if ((host_id.size() <= 1) || (host_id[0] != 0)) {
227 host_id.clear();
228 } else {
229 host_id = vector<uint8_t>(host_id.begin() + 1, host_id.end());
230 }
231 }
232 break;
233 default:
234 // not supported.
235 break;
236 }
237
238 // Add class: get host entry.
239 ConstHostPtr host;
240 if (!host_id.empty()) {
241 uint32_t subnet_id = lease->subnet_id_;
243 getCfgSubnets4()->getBySubnetId(subnet_id);
244 if (subnet && subnet->getReservationsGlobal()) {
245 subnet_id = SUBNET_ID_GLOBAL;
246 }
247 host = HostMgr::instance().get4Any(subnet_id, id_type,
248 &host_id[0], host_id.size());
249 }
250 // Add class: get cached Class attribute.
251 // @todo: extend and make configurable with a list of attributes.
252 if (host && host->getContext() &&
253 (host->getContext()->getType() == Element::map)) {
254 Attributes cached_attrs =
255 Attributes::fromElement(host->getContext()->get("radius"));
256 send->add(cached_attrs.get(PW_CLASS));
257 }
258
259 // Add attributes from configuration.
260 send->append(RadiusImpl::instance().acct_->attributes_.getAll());
261
262 // Return the handler.
263 RadiusAcctEnv env(stream.str(), event, lease->subnet_id_, send);
264 RadiusAcctHandlerPtr handler;
265 if (RadiusImpl::instance().checkExchangeListRoom()) {
266 handler.reset(new RadiusAcctHandler(
267 env, std::bind(&RadiusAccounting::terminate, env, ph::_1)));
268 } else {
271 }
272
273 // Erase create timestamp on stop.
274 if (status == PW_STATUS_STOP) {
275 eraseCreateTimestamp(lease->addr_);
276 }
277
278 return (handler);
279}
280
283 AttributesPtr send(new Attributes());
284
285 // Add duid.
286 vector<uint8_t> vec;
287 if (RadiusImpl::instance().clientid_pop0_) {
288 vec = pop0(lease->duid_);
289 } else {
290 vec = lease->duid_->getDuid();
291 }
292 string text;
293 if (RadiusImpl::instance().clientid_printable_) {
294 text = toPrintable(vec);
295 } else {
296 text = dumpAsHex(vec);
297 }
298 send->add(Attribute::fromString(PW_USER_NAME, text));
299
300 // Add hardware address.
301 if (lease->hwaddr_) {
302 string hwaddr = lease->hwaddr_->toText(false);
303 if (RadiusImpl::instance().canonical_mac_address_) {
304 hwaddr = canonize(hwaddr);
305 }
307 }
308
309 // Add address or prefix.
310 if (lease->type_ != Lease::TYPE_PD) {
311 send->add(Attribute::fromIpv6Addr(PW_FRAMED_IPV6_ADDRESS, lease->addr_));
312 } else {
314 lease->prefixlen_, lease->addr_));
315 }
316
317 // Get the create timestamp.
318 bool generate = false;
319 if ((event == EVENT_CREATE) || (event == EVENT_ADD)) {
320 generate = true;
321 }
322 ptime tm = getCreateTimestamp(lease->addr_, generate);
323
324 // Build the session Id.
325 ostringstream stream;
326 stream << lease->addr_.toText() << '@';
327 time_duration rtm(tm - epoch_);
328 stream << rtm.total_seconds() << ".";
329 rtm -= seconds(rtm.total_seconds());
330 stream << rtm.total_milliseconds();
331 send->add(Attribute::fromString(PW_ACCT_SESSION_ID, stream.str()));
332
333 // Accounting status type.
334 uint32_t status = PW_STATUS_ALIVE;
335 if (generate) {
336 status = PW_STATUS_START;
337 } else if ((event == EVENT_EXPIRE) ||
338 (event == EVENT_RELEASE) ||
339 (event == EVENT_DECLINE) ||
340 (event == EVENT_DEL)) {
341 status = PW_STATUS_STOP;
342 }
343 send->add(Attribute::fromInt(PW_ACCT_STATUS_TYPE, status));
344
345 // Add class: get host identifier.
347 vector<uint8_t> host_id;
348 switch (id_type) {
349 case Host::IDENT_DUID:
350 host_id = lease->duid_->getDuid();
351 break;
353 if (lease->hwaddr_) {
354 host_id = lease->hwaddr_->hwaddr_;
355 }
356 break;
357 case Host::IDENT_FLEX:
358 host_id = lease->duid_->getDuid();
359 if ((host_id.size() <= 2) ||
360 (host_id[0] != 0) || (host_id[1] != 0)) {
361 host_id.clear();
362 } else {
363 host_id = vector<uint8_t>(host_id.begin() + 2, host_id.end());
364 }
365 break;
366 default:
367 // not supported.
368 break;
369 }
370
371 // Add class: get host entry.
372 ConstHostPtr host;
373 if (!host_id.empty()) {
374 uint32_t subnet_id = lease->subnet_id_;
376 getCfgSubnets6()->getBySubnetId(subnet_id);
377 if (subnet && subnet->getReservationsGlobal()) {
378 subnet_id = SUBNET_ID_GLOBAL;
379 }
380 host = HostMgr::instance().get6Any(subnet_id, id_type,
381 &host_id[0], host_id.size());
382 }
383 // Add class: get cached Class attribute.
384 // @todo: extend and make configurable with a list of attributes.
385 if (host && host->getContext() &&
386 (host->getContext()->getType() == Element::map)) {
387 Attributes cached_attrs =
388 Attributes::fromElement(host->getContext()->get("radius"));
389 send->add(cached_attrs.get(PW_CLASS));
390 }
391
392 // Add attributes from configuration.
393 send->append(RadiusImpl::instance().acct_->attributes_.getAll());
394
395 // Return the handler.
396 RadiusAcctEnv env(stream.str(), event, lease->subnet_id_, send);
397 RadiusAcctHandlerPtr handler;
398 if (RadiusImpl::instance().checkExchangeListRoom()) {
399 handler.reset(new RadiusAcctHandler(
400 env, std::bind(&RadiusAccounting::terminate, env, ph::_1)));
401 } else {
404 }
405
406 // Erase create timestamp on stop.
407 if (status == PW_STATUS_STOP) {
408 eraseCreateTimestamp(lease->addr_);
409 }
410
411 return (handler);
412}
413
416 IOAddress addr(0);
417 uint32_t subnet_id = SUBNET_ID_UNUSED;
418 HWAddrPtr hwaddr;
419 ClientIdPtr client_id;
420 bool force = false;
421 try {
422 // Parse arguments.
423 addr = SimpleParser::getAddress(arguments, "ip-address");
424 subnet_id = SimpleParser::getInteger(arguments, "subnet-id");
425 string hwaddr_txt = SimpleParser::getString(arguments, "hw-address");
426 hwaddr.reset(new HWAddr(HWAddr::fromText(hwaddr_txt)));
427
428 if (arguments->contains("client-id")) {
429 string txt = SimpleParser::getString(arguments, "client-id");
430 client_id = ClientId::fromText(txt);
431 }
432
433 if (arguments->contains("force-create")) {
434 force = SimpleParser::getBoolean(arguments, "force-create");
435 }
436 } catch (const std::exception&) {
437 return (RadiusAcctHandlerPtr());
438 }
439 if (!addr.isV4()) {
440 return (RadiusAcctHandlerPtr());
441 }
442
443 // Fill attributes.
444 AttributesPtr send(new Attributes());
445
446 // Add client-id and hardware address.
447 string mac = hwaddr->toText(false);
448 if (RadiusImpl::instance().canonical_mac_address_) {
449 mac = canonize(mac);
450 }
451 if (client_id) {
452 vector<uint8_t> vec;
453 bool extracted = false;
454 if (RadiusImpl::instance().extract_duid_) {
455 vec = extractDuid(client_id, extracted);
456 }
457 // If case the DUID was not extracted, we will try to get the
458 // identifier using client-id.
459 if (!extracted) {
460 if (RadiusImpl::instance().clientid_pop0_) {
461 vec = pop0(client_id);
462 } else {
463 vec = client_id->getClientId();
464 }
465 }
466 string text;
467 if (RadiusImpl::instance().clientid_printable_) {
468 text = toPrintable(vec);
469 } else {
470 text = dumpAsHex(vec);
471 }
472
473 send->add(Attribute::fromString(PW_USER_NAME, text));
475 } else {
476 send->add(Attribute::fromString(PW_USER_NAME, mac));
477 }
478
479 // Add address.
481
482 // Get the create timestamp.
483 bool generate = false;
484 if ((event == EVENT_ADD) && force) {
485 generate = true;
486 }
487 ptime tm = getCreateTimestamp(addr, generate);
488
489 // Build the session Id.
490 ostringstream stream;
491 stream << addr << '@';
492 time_duration rtm(tm - epoch_);
493 stream << rtm.total_seconds() << ".";
494 rtm -= seconds(rtm.total_seconds());
495 stream << rtm.total_milliseconds();
496 send->add(Attribute::fromString(PW_ACCT_SESSION_ID, stream.str()));
497
498 // Accounting status type.
499 uint32_t status = PW_STATUS_ALIVE;
500 if (generate) {
501 status = PW_STATUS_START;
502 } else if (event == EVENT_DEL) {
503 status = PW_STATUS_STOP;
504 }
505 send->add(Attribute::fromInt(PW_ACCT_STATUS_TYPE, status));
506
507 // Add class: get host identifier.
509 vector<uint8_t> host_id;
510 switch (id_type) {
512 host_id = hwaddr->hwaddr_;
513 break;
514 case Host::IDENT_DUID:
515 if (client_id) {
516 host_id = client_id->getClientId();
517 if ((host_id.size() <= 5) ||
518 (host_id[0] != CLIENT_ID_OPTION_TYPE_DUID)) {
519 host_id.clear();
520 } else {
521 host_id = vector<uint8_t>(host_id.begin() + 5, host_id.end());
522 }
523 }
524 break;
526 if (client_id) {
527 host_id = client_id->getClientId();
528 }
529 break;
530 case Host::IDENT_FLEX:
531 if (client_id) {
532 host_id = client_id->getClientId();
533 if ((host_id.size() <= 1) || (host_id[0] != 0)) {
534 host_id.clear();
535 } else {
536 host_id = vector<uint8_t>(host_id.begin() + 1, host_id.end());
537 }
538 }
539 break;
540 default:
541 // not supported.
542 break;
543 }
544
545 // Add class: get host entry.
546 ConstHostPtr host;
547 if (!host_id.empty()) {
549 getCfgSubnets4()->getBySubnetId(subnet_id);
550 uint32_t host_subnet_id =
551 ((subnet && subnet->getReservationsGlobal()) ?
552 SUBNET_ID_GLOBAL : subnet_id);
553 host = HostMgr::instance().get4Any(host_subnet_id, id_type,
554 &host_id[0], host_id.size());
555 }
556 // Add class: get cached Class attribute.
557 // @todo: extend and make configurable with a list of attributes.
558 if (host && host->getContext() &&
559 (host->getContext()->getType() == Element::map)) {
560 Attributes cached_attrs =
561 Attributes::fromElement(host->getContext()->get("radius"));
562 send->add(cached_attrs.get(PW_CLASS));
563 }
564
565 // Add attributes from configuration.
566 send->append(RadiusImpl::instance().acct_->attributes_.getAll());
567
568 // Return the handler.
569 RadiusAcctEnv env(stream.str(), event, subnet_id, send);
570 RadiusAcctHandlerPtr handler;
571 if (RadiusImpl::instance().checkExchangeListRoom()) {
572 handler.reset(new RadiusAcctHandler(
573 env, std::bind(&RadiusAccounting::terminate, env, ph::_1)));
574 } else {
577 }
578
579 // Erase create timestamp on stop.
580 if (status == PW_STATUS_STOP) {
582 }
583
584 return (handler);
585}
586
589 IOAddress addr("::");
590 uint32_t subnet_id = SUBNET_ID_UNUSED;
591 DuidPtr duid;
592 HWAddrPtr hwaddr;
594 uint8_t prefix_len = 128;
595 bool force = false;
596 try {
597 // Parse arguments.
598 addr = SimpleParser::getAddress(arguments, "ip-address");
599 subnet_id = SimpleParser::getInteger(arguments, "subnet-id");
600 string txt = SimpleParser::getString(arguments, "duid");
601 duid.reset(new DUID(DUID::fromText(txt)));
602
603 if (arguments->contains("hw-address")) {
604 string hwaddr_txt = SimpleParser::getString(arguments, "hw-address");
605 hwaddr.reset(new HWAddr(HWAddr::fromText(hwaddr_txt)));
606 }
607
608 if (arguments->contains("type")) {
609 txt = SimpleParser::getString(arguments, "type");
610 if (txt == "IA_NA") {
611 type = Lease::TYPE_NA;
612 } else if (txt == "IA_PD") {
613 type = Lease::TYPE_PD;
614
615 prefix_len = SimpleParser::getInteger(arguments, "prefix-len");
616
617 if ((prefix_len <= 0) || (prefix_len > 128)) {
619 "'prefix-len' value must be in range of [1..128]");
620 }
621
622 if (prefix_len != 128) {
623 IOAddress first_address = firstAddrInPrefix(addr, prefix_len);
624 if (first_address != addr) {
625 isc_throw(BadValue, "Prefix address: " << addr
626 << " exceeds prefix/prefix-len pair: " << first_address
627 << "/" << static_cast<uint32_t>(prefix_len));
628 }
629 }
630 } else {
631 isc_throw(BadValue, "bad type" << txt);
632 }
633 }
634
635 if (arguments->contains("force-create")) {
636 force = SimpleParser::getBoolean(arguments, "force-create");
637 }
638 } catch (const std::exception&) {
639 return (RadiusAcctHandlerPtr());
640 }
641 if (!addr.isV6()) {
642 return (RadiusAcctHandlerPtr());
643 }
644
645 // Fill attributes.
646 AttributesPtr send(new Attributes());
647
648 // Add duid.
649 vector<uint8_t> vec;
650 if (RadiusImpl::instance().clientid_pop0_) {
651 vec = pop0(duid);
652 } else {
653 vec = duid->getDuid();
654 }
655 string text;
656 if (RadiusImpl::instance().clientid_printable_) {
657 text = toPrintable(vec);
658 } else {
659 text = dumpAsHex(vec);
660 }
661 send->add(Attribute::fromString(PW_USER_NAME, text));
662
663 // Add hardware address.
664 if (hwaddr) {
665 string mac = hwaddr->toText(false);
666 if (RadiusImpl::instance().canonical_mac_address_) {
667 mac = canonize(mac);
668 }
670 }
671
672 // Add address or prefix.
673 if (type != Lease::TYPE_PD) {
675 } else {
677 prefix_len, addr));
678 }
679
680 // Get the create timestamp.
681 bool generate = false;
682 if ((event == EVENT_ADD) && force) {
683 generate = true;
684 }
685 ptime tm = getCreateTimestamp(addr, generate);
686
687 // Build the session Id.
688 ostringstream stream;
689 stream << addr << '@';
690 time_duration rtm(tm - epoch_);
691 stream << rtm.total_seconds() << ".";
692 rtm -= seconds(rtm.total_seconds());
693 stream << rtm.total_milliseconds();
694 send->add(Attribute::fromString(PW_ACCT_SESSION_ID, stream.str()));
695
696 // Accounting status type.
697 uint32_t status = PW_STATUS_ALIVE;
698 if (generate) {
699 status = PW_STATUS_START;
700 } else if (event == EVENT_DEL) {
701 status = PW_STATUS_STOP;
702 }
703 send->add(Attribute::fromInt(PW_ACCT_STATUS_TYPE, status));
704
705 // Add class: get host identifier.
707 vector<uint8_t> host_id;
708 switch (id_type) {
709 case Host::IDENT_DUID:
710 host_id = duid->getDuid();
711 break;
713 if (hwaddr) {
714 host_id = hwaddr->hwaddr_;
715 }
716 break;
717 case Host::IDENT_FLEX:
718 host_id = duid->getDuid();
719 if ((host_id.size() <= 2) ||
720 (host_id[0] != 0) || (host_id[1] != 0)) {
721 host_id.clear();
722 } else {
723 host_id = vector<uint8_t>(host_id.begin() + 2, host_id.end());
724 }
725 break;
726 default:
727 // not supported.
728 break;
729 }
730
731 // Add class: get host entry.
732 ConstHostPtr host;
733 if (!host_id.empty()) {
735 getCfgSubnets6()->getBySubnetId(subnet_id);
736 uint32_t host_subnet_id =
737 ((subnet && subnet->getReservationsGlobal()) ?
738 SUBNET_ID_GLOBAL : subnet_id);
739 host = HostMgr::instance().get6Any(host_subnet_id, id_type,
740 &host_id[0], host_id.size());
741 }
742 // Add class: get cached Class attribute.
743 // @todo: extend and make configurable with a list of attributes.
744 if (host && host->getContext() &&
745 (host->getContext()->getType() == Element::map)) {
746 Attributes cached_attrs =
747 Attributes::fromElement(host->getContext()->get("radius"));
748 send->add(cached_attrs.get(PW_CLASS));
749 }
750
751 // Add attributes from configuration.
752 send->append(RadiusImpl::instance().acct_->attributes_.getAll());
753
754 // Return the handler.
755 RadiusAcctEnv env(stream.str(), event, subnet_id, send);
756 RadiusAcctHandlerPtr handler;
757 if (RadiusImpl::instance().checkExchangeListRoom()) {
758 handler.reset(new RadiusAcctHandler(
759 env, std::bind(&RadiusAccounting::terminate, env, ph::_1)));
760 } else {
763 }
764
765 // Erase create timestamp on stop.
766 if (status == PW_STATUS_STOP) {
768 }
769
770 return (handler);
771}
772
773void
775 handler->start();
776}
777
778void
780 env.finished_ = true;
781 if (result != OK_RC) {
783 .arg(env.session_id_)
784 .arg(env.event_)
785 .arg(eventToText(env.event_))
786 .arg(result)
787 .arg(exchangeRCtoText(result));
788 }
789}
790
791ptime
795 TMContainerAddressIndex::iterator it = idx.find(addr);
796 if (!generate) {
797 if (it == idx.end()) {
800 .arg(addr.toText());
801 } else {
802 return (it->timestamp_);
803 }
804 }
805
806 // generate is true or no address was found.
807 while (it != idx.end()) {
808 idx.erase(it);
809 // One round should be enough as the address is unique.
810 it = idx.find(addr);
811 }
812 ptime tm = microsec_clock::universal_time();
813 auto ret = container_.insert(LeaseTS(addr, tm));
814 if (!ret.second) {
816 .arg(addr.toText());
817 }
818
819 // Append to create timestamp file.
820 if (file_) {
821 try {
822 CSVRow row(file_->getColumnCount());
823
824 row.writeAt(file_->getColumnIndex("address"), addr);
825 time_duration rtm(tm - epoch_);
826 row.writeAt(file_->getColumnIndex("seconds"), rtm.total_seconds());
827 rtm -= seconds(rtm.total_seconds());
828 row.writeAt(file_->getColumnIndex("milliseconds"),
829 rtm.total_milliseconds());
830
831 file_->append(row);
833 } catch (const std::exception& ex) {
835 .arg(addr.toText())
836 .arg(ex.what());
837 file_.reset();
838 }
839 }
840
841 return (tm);
842}
843
844void
848 TMContainerAddressIndex::iterator it = idx.find(addr);
849 while (it != idx.end()) {
850 idx.erase(it);
851 // One round should be enough as the address is unique.
852 it = idx.find(addr);
853 }
854 // Append delete marker to create timestamp file.
855 if (file_) {
856 try {
857 CSVRow row(file_->getColumnCount());
858
859 row.writeAt(file_->getColumnIndex("address"), addr);
860 row.writeAt(file_->getColumnIndex("seconds"), 0L);
861 row.writeAt(file_->getColumnIndex("milliseconds"), 0L);
862
863 file_->append(row);
865 } catch (const std::exception& ex) {
867 .arg(addr.toText())
868 .arg(ex.what());
869 file_.reset();
870 }
871 }
872}
873
874bool
877 bool ok = true;
878 size_t adds = 0;
879 size_t fails = 0;
880 try {
881 file_->open(false);
882
883 for (;;) {
884 CSVRow row;
885 file_->next(row, true);
886 // Check if done.
887 if (row == CSVFile::EMPTY_ROW()) {
888 if (ok) {
890 .arg(adds)
891 .arg(container_.size());
892 break;
893 } else {
894 isc_throw(CSVFileError, "partial load");
895 }
896 }
897
898 // Get fields.
899 string addr_txt = row.readAt(file_->getColumnIndex("address"));
900 IOAddress addr(addr_txt);
901 long secs =
902 row.readAndConvertAt<long>(file_->getColumnIndex("seconds"));
903 long msecs =
904 row.readAndConvertAt<long>(file_->getColumnIndex("milliseconds"));
905
906 // Get an iterator to a possible entry.
907 TMContainerAddressIndex::iterator it = idx.find(addr);
908 while (it != idx.end()) {
909 idx.erase(it);
910 // One round should be enough as the address is unique.
911 it = idx.find(addr);
912 }
913
914 // Zero seconds means deleted record.
915 if (secs == 0) {
916 continue;
917 }
918
919 time_duration rtm (seconds(secs) + milliseconds(msecs));
920 ptime tm(epoch_ + rtm);
921 auto ret = container_.insert(LeaseTS(addr, tm));
922 if (!ret.second) {
923 ok = false;
924 ++fails;
925 } else {
926 ++adds;
927 }
928 }
929
930 } catch (const std::exception& ex) {
932 .arg(adds)
933 .arg(fails)
934 .arg(container_.size());
935 ok = false;
936 }
937
938 file_->close();
939
940 return (ok);
941}
942
943void
946 string newfilename = filename_ + ".new";
947 CSVFile newfile(newfilename);
948 size_t stored = 0;
949 if (container_.empty()) {
950 return;
951 }
952 try {
953 // Add columns
954 newfile.addColumn("address");
955 newfile.addColumn("seconds");
956 newfile.addColumn("milliseconds");
957
958 // Rename file if it exists.
959 if (newfile.exists()) {
960 string backfilename = filename_ + ".new~";
961 rename(newfilename.c_str(), backfilename.c_str());
962 }
963
964 // Open file (seed to end in case the rename failed).
965 newfile.open(true);
966
967 // Dump container content.
968 for (auto const& it : idx) {
969 CSVRow row(newfile.getColumnCount());
970
971 row.writeAt(newfile.getColumnIndex("address"), it.addr_);
972 time_duration rtm(it.timestamp_ - epoch_);
973 row.writeAt(newfile.getColumnIndex("seconds"),
974 rtm.total_seconds());
975 rtm -= seconds(rtm.total_seconds());
976 row.writeAt(newfile.getColumnIndex("milliseconds"),
977 rtm.total_milliseconds());
978
979 newfile.append(row);
980 ++stored;
981 }
982
983 // Close file.
984 newfile.close();
985
986 // Rename files.
987 string backfilename = filename_ + ".bak";
988 bool moved = true;
989 if (rename(filename_.c_str(), backfilename.c_str()) != 0) {
990 moved = false;
991 }
992 if (rename(newfilename.c_str(), filename_.c_str()) != 0) {
993 if (moved) {
994 // Try to restore current file.
995 rename(backfilename.c_str(), filename_.c_str());
996 }
997 }
998
1000 .arg(stored);
1001
1002 } catch (const std::exception& ex) {
1004 .arg(newfilename)
1005 .arg(ex.what())
1006 .arg(stored)
1007 .arg(container_.size());
1008 }
1009 record_count_ = 0;
1010}
1011
1012void
1016 if (idle_timer_interval_ <= 0) {
1017 return;
1018 }
1019 // Cope to one day.
1020 long secs = idle_timer_interval_;
1021 if (secs > 24*60*60) {
1022 secs = 24*60*60;
1023 }
1024 idle_timer_.reset(new IntervalTimer(RadiusImpl::instance().getIOContext()));
1026 secs * 1000, IntervalTimer::REPEATING);
1027}
1028
1029void
1031 AttributesPtr send_attrs;
1032 RadiusAcctStatusPtr handler(new RadiusAcctStatus(send_attrs, 0));
1033 RadiusImpl::instance().registerExchange(handler->getExchange());
1034 handler->start();
1035}
1036
1037} // end of namespace isc::radius
1038} // end of namespace isc
static DUID fromText(const std::string &text)
Create DUID from the textual format.
Definition duid.cc:50
@ map
Definition data.h:160
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...
static isc::asiolink::IOAddress getAddress(const ConstElementPtr &scope, const std::string &name)
Returns a IOAddress parameter from a scope.
static std::string getString(isc::data::ConstElementPtr scope, const std::string &name)
Returns a string parameter from a scope.
static bool getBoolean(isc::data::ConstElementPtr scope, const std::string &name)
Returns a boolean parameter from a scope.
static int64_t getInteger(isc::data::ConstElementPtr scope, const std::string &name)
Returns an integer parameter from a scope.
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
static ClientIdPtr fromText(const std::string &text)
Create client identifier from the textual format.
Definition duid.cc:73
Holds DUID (DHCPv6 Unique Identifier).
Definition duid.h:142
ConstHostPtr get6Any(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len, const HostMgrOperationTarget target) const
Returns any host connected to the IPv6 subnet.
Definition host_mgr.cc:590
ConstHostPtr get4Any(const SubnetID &subnet_id, const Host::IdentifierType &identifier_type, const uint8_t *identifier_begin, const size_t identifier_len, const HostMgrOperationTarget target) const
Returns any host connected to the IPv4 subnet.
Definition host_mgr.cc:401
static HostMgr & instance()
Returns a sole instance of the HostMgr.
Definition host_mgr.cc:114
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
static AttributePtr fromInt(const uint8_t type, const uint32_t value)
From integer with type.
static AttributePtr fromIpAddr(const uint8_t type, const asiolink::IOAddress &value)
From IPv4 address with type.
static AttributePtr fromString(const uint8_t type, const std::string &value)
From type specific factories.
static AttributePtr fromIpv6Prefix(const uint8_t type, const uint8_t len, const asiolink::IOAddress &value)
From IPv6 prefix with type.
static AttributePtr fromIpv6Addr(const uint8_t type, const asiolink::IOAddress &value)
From IPv6 address with type.
Collection of attributes.
ConstAttributePtr get(const uint8_t type) const
Get instance of the attribute in the collection.
static Attributes fromElement(const data::ConstElementPtr &attr_list)
Parse collection.
Create timestamp entry.
RadiusAcctHandlerPtr buildAcct6(const data::ConstElementPtr &arguments, Event event)
Build RadiusAcct handler for Accounting-Request.
TMContainer container_
The Create timestamp container which holds session history.
RadiusAcctHandlerPtr buildAcct4(const data::ConstElementPtr &arguments, Event event)
Build RadiusAcct handler for Accounting-Request.
void eraseCreateTimestamp(const asiolink::IOAddress &addr)
Erase create-timestamp entry to session history.
CSVFilePtr file_
Pointer to the CSVFile.
void init(const std::string &filename)
Initialize.
bool loadFromFile()
Load create-timestamp entries from file.
static void runAsync(RadiusAcctHandlerPtr handler)
Run asynchronously.
std::mutex mutex_
Mutex to protect access to container_ and file_.
void storeToFile()
Store create-timestamp entries to a file.
size_t record_count_
New record counter.
RadiusAcctHandlerPtr buildAcct(const dhcp::Lease4Ptr &lease, Event event)
Build RadiusAcct handler for Accounting-Request - IPv4.
static void terminate(RadiusAcctEnv env, int result)
Termination callback.
const boost::posix_time::ptime epoch_
Epoch to avoid too long values.
static void IdleTimerCallback()
Idle timer callback.
boost::posix_time::ptime getCreateTimestamp(const asiolink::IOAddress &addr, bool generate)
Get lease create-timestamp entry from session history.
std::string filename_
Create timestamps file name.
Class of Radius accounting environments.
RadiusAcctEnv(std::string session_id, Event event, uint32_t subnet_id, AttributesPtr send_attrs)
Constructor.
std::string session_id_
Session Id.
bool finished_
Termination flag.
AttributesPtr send_attrs_
Attributes to send.
uint32_t subnet_id_
Subnet Id (aka client/NAS port).
Class of Radius accounting communication handler.
RadiusAcctHandler(RadiusAcctEnv env, const CallbackAcct &callback)
Constructor.
static size_t getCounter()
Get instance counter.
RadiusAcctEnv env_
Environment.
void start()
Start communication.
virtual ~RadiusAcctHandler()
Destructor.
RadiusAsyncAcctPtr acct_
Pointer to the communication class.
Class for communication with accounting servers.
class for asynchronous accounting communication with servers.
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
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 size_t exchangeListMaxSize
Exchange List Maximum Size (currently the max(200,soft_limit/2)).
Definition radius.h:108
Exception thrown when an error occurs during CSV file processing.
Definition csv_file.h:22
Provides input/output access to CSV files.
Definition csv_file.h:366
void close()
Closes the CSV file.
Definition csv_file.cc:124
size_t getColumnCount() const
Returns the number of columns in the file.
Definition csv_file.h:419
bool exists() const
Checks if the CSV file exists and can be opened for reading.
Definition csv_file.cc:134
static CSVRow EMPTY_ROW()
Represents empty row.
Definition csv_file.h:507
void append(const CSVRow &row) const
Writes the CSV row into the file.
Definition csv_file.cc:173
void addColumn(const std::string &col_name)
Adds new column name.
Definition csv_file.cc:153
size_t getColumnIndex(const std::string &col_name) const
Returns the index of the column having specified name.
Definition csv_file.cc:257
virtual void open(const bool seek_to_end=false)
Opens existing file or creates a new one.
Definition csv_file.cc:322
Represents a single row of the CSV file.
Definition csv_file.h:59
T readAndConvertAt(const size_t at) const
Retrieves a value from the internal container.
Definition csv_file.h:164
void writeAt(const size_t at, const char *value)
Replaces the value at specified index.
Definition csv_file.cc:85
std::string readAt(const size_t at) const
Retrieves a value from the internal container.
Definition csv_file.cc:61
#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_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition macros.h:26
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:30
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition subnet.h:620
boost::shared_ptr< const Subnet4 > ConstSubnet4Ptr
A const pointer to a Subnet4 object.
Definition subnet.h:455
boost::shared_ptr< DUID > DuidPtr
Definition duid.h:136
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
Definition lease.h:530
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:166
boost::shared_ptr< ClientId > ClientIdPtr
Shared pointer to a Client ID.
Definition duid.h:216
boost::shared_ptr< const Host > ConstHostPtr
Const pointer to the Host object.
Definition host.h:843
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition lease.h:317
boost::shared_ptr< RadiusAcctHandler > RadiusAcctHandlerPtr
Type of pointers to Radius accounting communication handler.
@ 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.
const isc::log::MessageID RADIUS_ACCOUNTING_ERROR
string canonize(const string &hexdump)
Canonize hardware address textual representation.
const isc::log::MessageID RADIUS_SESSION_HISTORY_STORE_FAILED
TMContainer::index< TMAddressIndexTag >::type TMContainerAddressIndex
First index type in the TMContainer.
boost::shared_ptr< Attributes > AttributesPtr
Shared pointers to attribute collection.
const isc::log::MessageID RADIUS_SESSION_HISTORY_OPEN_FAILED
std::function< void(int)> CallbackAcct
Type of callback for accounting termination.
TMContainer::index< TMTimestampIndexTag >::type TMContainerTimestampIndex
Second index type in the TMContainer.
const isc::log::MessageID RADIUS_SESSION_HISTORY_LOADED
string exchangeRCtoText(const int rc)
ExchangeRC value -> name function.
Event
Type of accounting events.
const int RADIUS_DBG_TRACE
Radius logging levels.
Definition radius_log.h:26
const isc::log::MessageID RADIUS_ACCOUNTING_HISTORY_UPDATE_FAILED
const isc::log::MessageID RADIUS_SESSION_HISTORY_APPEND_FAILED
string toPrintable(const vector< uint8_t > &content)
Return printable textual representation of a vector.
const isc::log::MessageID RADIUS_UDP_EXCHANGE_MAX_PENDING
const isc::log::MessageID RADIUS_SESSION_HISTORY_STORED
isc::log::Logger radius_logger("radius-hooks")
Radius Logger.
Definition radius_log.h:35
const isc::log::MessageID RADIUS_SESSION_HISTORY_OPENED
boost::shared_ptr< RadiusAcctStatus > RadiusAcctStatusPtr
Pointer to accounting status.
vector< uint8_t > pop0(const ClientIdPtr &client_id)
Pop leading zero in a DHCPv4 client-id.
const isc::log::MessageID RADIUS_SESSION_HISTORY_LOAD_FAILED
const isc::log::MessageID RADIUS_ACCOUNTING_NO_HISTORY
string eventToText(Event event)
Translate an event to text.
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.
Hardware type that represents information from DHCPv4 packet.
Definition hwaddr.h:20
static HWAddr fromText(const std::string &text, const uint16_t htype=HTYPE_ETHER)
Creates instance of the hardware address from textual format.
Definition hwaddr.cc:62
Type
Type of lease or pool.
Definition lease.h:48
@ TYPE_PD
the lease contains IPv6 prefix (for prefix delegation)
Definition lease.h:51
@ TYPE_NA
the lease contains non-temporary IPv6 address
Definition lease.h:49
Tag for the index for searching by address.
Tag for the index for searching by timestamp.
RAII lock object to protect the code in the same scope with a mutex.