Kea 3.1.1
radius_accounting.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
10#include <cc/simple_parser.h>
11#include <dhcpsrv/cfgmgr.h>
12#include <dhcpsrv/host_mgr.h>
13#include <dhcpsrv/subnet.h>
14#include <radius_accounting.h>
15#include <radius_log.h>
16#include <radius_utils.h>
18#include <stdio.h>
19
20using namespace std;
21using namespace isc;
22using namespace isc::asiolink;
23using namespace isc::data;
24using namespace isc::dhcp;
25using namespace isc::util;
26using namespace boost::gregorian;
27using namespace boost::posix_time;
28namespace ph = std::placeholders;
29
30namespace isc {
31namespace radius {
32
33string
35 switch (event) {
36 case EVENT_CREATE:
37 return ("create");
38 case EVENT_RENEW:
39 return ("renew");
40 case EVENT_REBIND:
41 return ("rebind");
42 case EVENT_EXPIRE:
43 return ("expire");
44 case EVENT_RELEASE:
45 return ("release");
46 case EVENT_DECLINE:
47 return ("decline");
48 case EVENT_ADD:
49 return ("add");
50 case EVENT_UPDATE:
51 return ("update");
52 case EVENT_DEL:
53 return ("delete");
54 default:
55 return ("unknown");
56 }
57}
58
59RadiusAcctEnv::RadiusAcctEnv(std::string session_id, Event event,
60 uint32_t subnet_id,
61 AttributesPtr send_attrs)
62 : session_id_(session_id), event_(event), subnet_id_(subnet_id),
63 send_attrs_(send_attrs), finished_(false) {
64}
65
67 const CallbackAcct& callback)
68 : env_(env), acct_() {
69 acct_.reset(new RadiusAsyncAcct(env_.subnet_id_, env_.send_attrs_, callback));
70 RadiusImpl::instance().registerExchange(acct_->getExchange());
71 MultiThreadingLock lock(mutex_);
72 ++counter_;
73}
74
76 MultiThreadingLock lock(mutex_);
77 if (counter_ > 0) {
78 --counter_;
79 }
80}
81
82void
84 acct_->start();
85}
86
87size_t
89 MultiThreadingLock lock(mutex_);
90 return (counter_);
91}
92
93size_t RadiusAcctHandler::counter_ = 0;
94
95mutex RadiusAcctHandler::mutex_;
96
98 : RadiusService("accounting"), epoch_(date(2018, 3, 7), hours(11)), record_count_(0) {
99}
100
101void
102RadiusAccounting::init(const std::string& filename) {
103 filename_ = filename;
104 if (!filename_.empty()) {
105 file_.reset(new CSVFile(filename_));
106
107 file_->addColumn("address");
108 file_->addColumn("seconds");
109 file_->addColumn("milliseconds");
110
111 if (file_->exists()) {
112 if (loadFromFile() && (container_.size() > 0)) {
113 storeToFile();
114 }
115 }
116 try {
117 file_->open(true);
119 .arg(filename_);
120 } catch (const std::exception& ex) {
122 .arg(filename_)
123 .arg(ex.what());
124 file_.reset();
125 }
126 }
127}
128
131 AttributesPtr send(new Attributes());
132
133 // Add client-id and hardware address.
134 string hwaddr = lease->hwaddr_->toText(false);
135 if (RadiusImpl::instance().canonical_mac_address_) {
136 hwaddr = canonize(hwaddr);
137 }
138 if (lease->client_id_) {
139 vector<uint8_t> vec;
140 bool extracted = false;
141 if (RadiusImpl::instance().extract_duid_) {
142 vec = extractDuid(lease->client_id_, extracted);
143 }
144 // If case the DUID was not extracted, we will try to get the
145 // identifier using client-id.
146 if (!extracted) {
147 if (RadiusImpl::instance().clientid_pop0_) {
148 vec = pop0(lease->client_id_);
149 } else {
150 vec = lease->client_id_->getClientId();
151 }
152 }
153 string text;
154 if (RadiusImpl::instance().clientid_printable_) {
155 text = toPrintable(vec);
156 } else {
157 text = toHex(vec);
158 }
159
160 send->add(Attribute::fromString(PW_USER_NAME, text));
162 } else {
163 send->add(Attribute::fromString(PW_USER_NAME, hwaddr));
164 }
165
166 // Add address.
167 send->add(Attribute::fromIpAddr(PW_FRAMED_IP_ADDRESS, lease->addr_));
168
169 // Get the create timestamp.
170 bool generate = false;
171 if ((event == EVENT_CREATE) || (event == EVENT_ADD)) {
172 generate = true;
173 }
174 ptime tm = getCreateTimestamp(lease->addr_, generate);
175
176 // Build the session Id.
177 ostringstream stream;
178 stream << lease->addr_.toText() << '@';
179 time_duration rtm(tm - epoch_);
180 stream << rtm.total_seconds() << ".";
181 rtm -= seconds(rtm.total_seconds());
182 stream << rtm.total_milliseconds();
183 send->add(Attribute::fromString(PW_ACCT_SESSION_ID, stream.str()));
184
185 // Accounting status type.
186 uint32_t status = PW_STATUS_ALIVE;
187 if (generate) {
188 status = PW_STATUS_START;
189 } else if ((event == EVENT_EXPIRE) ||
190 (event == EVENT_RELEASE) ||
191 (event == EVENT_DECLINE) ||
192 (event == EVENT_DEL)) {
193 status = PW_STATUS_STOP;
194 }
195 send->add(Attribute::fromInt(PW_ACCT_STATUS_TYPE, status));
196
197 // Add class: get host identifier.
199 vector<uint8_t> host_id;
200 switch (id_type) {
202 host_id = lease->hwaddr_->hwaddr_;
203 break;
204 case Host::IDENT_DUID:
205 if (lease->client_id_) {
206 host_id = lease->client_id_->getClientId();
207 if ((host_id.size() <= 5) ||
208 (host_id[0] != CLIENT_ID_OPTION_TYPE_DUID)) {
209 host_id.clear();
210 } else {
211 host_id = vector<uint8_t>(host_id.begin() + 5, host_id.end());
212 }
213 }
214 break;
216 if (lease->client_id_) {
217 host_id = lease->client_id_->getClientId();
218 }
219 break;
220 case Host::IDENT_FLEX:
221 if (lease->client_id_) {
222 host_id = lease->client_id_->getClientId();
223 if ((host_id.size() <= 1) || (host_id[0] != 0)) {
224 host_id.clear();
225 } else {
226 host_id = vector<uint8_t>(host_id.begin() + 1, host_id.end());
227 }
228 }
229 break;
230 default:
231 // not supported.
232 break;
233 }
234
235 // Add class: get host entry.
236 ConstHostPtr host;
237 if (!host_id.empty()) {
238 uint32_t subnet_id = lease->subnet_id_;
240 getCfgSubnets4()->getBySubnetId(subnet_id);
241 if (subnet && subnet->getReservationsGlobal()) {
242 subnet_id = SUBNET_ID_GLOBAL;
243 }
244 host = HostMgr::instance().get4Any(subnet_id, id_type,
245 &host_id[0], host_id.size());
246 }
247 // Add class: get cached Class attribute.
248 // @todo: extend and make configurable with a list of attributes.
249 if (host && host->getContext() &&
250 (host->getContext()->getType() == Element::map)) {
251 Attributes cached_attrs =
252 Attributes::fromElement(host->getContext()->get("radius"));
253 send->add(cached_attrs.get(PW_CLASS));
254 }
255
256 // Add attributes from configuration.
257 send->append(RadiusImpl::instance().acct_->attributes_.getAll());
258
259 // Return the handler.
260 RadiusAcctEnv env(stream.str(), event, lease->subnet_id_, send);
261 RadiusAcctHandlerPtr handler;
262 handler.reset(new RadiusAcctHandler(
263 env, std::bind(&RadiusAccounting::terminate, env, ph::_1)));
264
265 // Erase create timestamp on stop.
266 if (status == PW_STATUS_STOP) {
267 eraseCreateTimestamp(lease->addr_);
268 }
269
270 return (handler);
271}
272
275 AttributesPtr send(new Attributes());
276
277 // Add duid.
278 vector<uint8_t> vec;
279 if (RadiusImpl::instance().clientid_pop0_) {
280 vec = pop0(lease->duid_);
281 } else {
282 vec = lease->duid_->getDuid();
283 }
284 string text;
285 if (RadiusImpl::instance().clientid_printable_) {
286 text = toPrintable(vec);
287 } else {
288 text = toHex(vec);
289 }
290 send->add(Attribute::fromString(PW_USER_NAME, text));
291
292 // Add hardware address.
293 if (lease->hwaddr_) {
294 string hwaddr = lease->hwaddr_->toText(false);
295 if (RadiusImpl::instance().canonical_mac_address_) {
296 hwaddr = canonize(hwaddr);
297 }
299 }
300
301 // Add address or prefix.
302 if (lease->type_ != Lease::TYPE_PD) {
303 send->add(Attribute::fromIpv6Addr(PW_FRAMED_IPV6_ADDRESS, lease->addr_));
304 } else {
306 lease->prefixlen_, lease->addr_));
307 }
308
309 // Get the create timestamp.
310 bool generate = false;
311 if ((event == EVENT_CREATE) || (event == EVENT_ADD)) {
312 generate = true;
313 }
314 ptime tm = getCreateTimestamp(lease->addr_, generate);
315
316 // Build the session Id.
317 ostringstream stream;
318 stream << lease->addr_.toText() << '@';
319 time_duration rtm(tm - epoch_);
320 stream << rtm.total_seconds() << ".";
321 rtm -= seconds(rtm.total_seconds());
322 stream << rtm.total_milliseconds();
323 send->add(Attribute::fromString(PW_ACCT_SESSION_ID, stream.str()));
324
325 // Accounting status type.
326 uint32_t status = PW_STATUS_ALIVE;
327 if (generate) {
328 status = PW_STATUS_START;
329 } else if ((event == EVENT_EXPIRE) ||
330 (event == EVENT_RELEASE) ||
331 (event == EVENT_DECLINE) ||
332 (event == EVENT_DEL)) {
333 status = PW_STATUS_STOP;
334 }
335 send->add(Attribute::fromInt(PW_ACCT_STATUS_TYPE, status));
336
337 // Add class: get host identifier.
339 vector<uint8_t> host_id;
340 switch (id_type) {
341 case Host::IDENT_DUID:
342 host_id = lease->duid_->getDuid();
343 break;
345 if (lease->hwaddr_) {
346 host_id = lease->hwaddr_->hwaddr_;
347 }
348 break;
349 case Host::IDENT_FLEX:
350 host_id = lease->duid_->getDuid();
351 if ((host_id.size() <= 2) ||
352 (host_id[0] != 0) || (host_id[1] != 0)) {
353 host_id.clear();
354 } else {
355 host_id = vector<uint8_t>(host_id.begin() + 2, host_id.end());
356 }
357 break;
358 default:
359 // not supported.
360 break;
361 }
362
363 // Add class: get host entry.
364 ConstHostPtr host;
365 if (!host_id.empty()) {
366 uint32_t subnet_id = lease->subnet_id_;
368 getCfgSubnets6()->getBySubnetId(subnet_id);
369 if (subnet && subnet->getReservationsGlobal()) {
370 subnet_id = SUBNET_ID_GLOBAL;
371 }
372 host = HostMgr::instance().get6Any(subnet_id, id_type,
373 &host_id[0], host_id.size());
374 }
375 // Add class: get cached Class attribute.
376 // @todo: extend and make configurable with a list of attributes.
377 if (host && host->getContext() &&
378 (host->getContext()->getType() == Element::map)) {
379 Attributes cached_attrs =
380 Attributes::fromElement(host->getContext()->get("radius"));
381 send->add(cached_attrs.get(PW_CLASS));
382 }
383
384 // Add attributes from configuration.
385 send->append(RadiusImpl::instance().acct_->attributes_.getAll());
386
387 // Return the handler.
388 RadiusAcctEnv env(stream.str(), event, lease->subnet_id_, send);
389 RadiusAcctHandlerPtr handler;
390 handler.reset(new RadiusAcctHandler(
391 env, std::bind(&RadiusAccounting::terminate, env, ph::_1)));
392
393 // Erase create timestamp on stop.
394 if (status == PW_STATUS_STOP) {
395 eraseCreateTimestamp(lease->addr_);
396 }
397
398 return (handler);
399}
400
403 IOAddress addr(0);
404 uint32_t subnet_id = SUBNET_ID_UNUSED;
405 HWAddrPtr hwaddr;
406 ClientIdPtr client_id;
407 bool force = false;
408 try {
409 // Parse arguments.
410 addr = SimpleParser::getAddress(arguments, "ip-address");
411 subnet_id = SimpleParser::getInteger(arguments, "subnet-id");
412 string hwaddr_txt = SimpleParser::getString(arguments, "hw-address");
413 hwaddr.reset(new HWAddr(HWAddr::fromText(hwaddr_txt)));
414
415 if (arguments->contains("client-id")) {
416 string txt = SimpleParser::getString(arguments, "client-id");
417 client_id = ClientId::fromText(txt);
418 }
419
420 if (arguments->contains("force-create")) {
421 force = SimpleParser::getBoolean(arguments, "force-create");
422 }
423 } catch (const std::exception&) {
424 return (RadiusAcctHandlerPtr());
425 }
426 if (!addr.isV4()) {
427 return (RadiusAcctHandlerPtr());
428 }
429
430 // Fill attributes.
431 AttributesPtr send(new Attributes());
432
433 // Add client-id and hardware address.
434 string mac = hwaddr->toText(false);
435 if (RadiusImpl::instance().canonical_mac_address_) {
436 mac = canonize(mac);
437 }
438 if (client_id) {
439 vector<uint8_t> vec;
440 bool extracted = false;
441 if (RadiusImpl::instance().extract_duid_) {
442 vec = extractDuid(client_id, extracted);
443 }
444 // If case the DUID was not extracted, we will try to get the
445 // identifier using client-id.
446 if (!extracted) {
447 if (RadiusImpl::instance().clientid_pop0_) {
448 vec = pop0(client_id);
449 } else {
450 vec = client_id->getClientId();
451 }
452 }
453 string text;
454 if (RadiusImpl::instance().clientid_printable_) {
455 text = toPrintable(vec);
456 } else {
457 text = toHex(vec);
458 }
459
460 send->add(Attribute::fromString(PW_USER_NAME, text));
462 } else {
463 send->add(Attribute::fromString(PW_USER_NAME, mac));
464 }
465
466 // Add address.
468
469 // Get the create timestamp.
470 bool generate = false;
471 if ((event == EVENT_ADD) && force) {
472 generate = true;
473 }
474 ptime tm = getCreateTimestamp(addr, generate);
475
476 // Build the session Id.
477 ostringstream stream;
478 stream << addr << '@';
479 time_duration rtm(tm - epoch_);
480 stream << rtm.total_seconds() << ".";
481 rtm -= seconds(rtm.total_seconds());
482 stream << rtm.total_milliseconds();
483 send->add(Attribute::fromString(PW_ACCT_SESSION_ID, stream.str()));
484
485 // Accounting status type.
486 uint32_t status = PW_STATUS_ALIVE;
487 if (generate) {
488 status = PW_STATUS_START;
489 } else if (event == EVENT_DEL) {
490 status = PW_STATUS_STOP;
491 }
492 send->add(Attribute::fromInt(PW_ACCT_STATUS_TYPE, status));
493
494 // Add class: get host identifier.
496 vector<uint8_t> host_id;
497 switch (id_type) {
499 host_id = hwaddr->hwaddr_;
500 break;
501 case Host::IDENT_DUID:
502 if (client_id) {
503 host_id = client_id->getClientId();
504 if ((host_id.size() <= 5) ||
505 (host_id[0] != CLIENT_ID_OPTION_TYPE_DUID)) {
506 host_id.clear();
507 } else {
508 host_id = vector<uint8_t>(host_id.begin() + 5, host_id.end());
509 }
510 }
511 break;
513 if (client_id) {
514 host_id = client_id->getClientId();
515 }
516 break;
517 case Host::IDENT_FLEX:
518 if (client_id) {
519 host_id = client_id->getClientId();
520 if ((host_id.size() <= 1) || (host_id[0] != 0)) {
521 host_id.clear();
522 } else {
523 host_id = vector<uint8_t>(host_id.begin() + 1, host_id.end());
524 }
525 }
526 break;
527 default:
528 // not supported.
529 break;
530 }
531
532 // Add class: get host entry.
533 ConstHostPtr host;
534 if (!host_id.empty()) {
536 getCfgSubnets4()->getBySubnetId(subnet_id);
537 uint32_t host_subnet_id =
538 ((subnet && subnet->getReservationsGlobal()) ?
539 SUBNET_ID_GLOBAL : subnet_id);
540 host = HostMgr::instance().get4Any(host_subnet_id, id_type,
541 &host_id[0], host_id.size());
542 }
543 // Add class: get cached Class attribute.
544 // @todo: extend and make configurable with a list of attributes.
545 if (host && host->getContext() &&
546 (host->getContext()->getType() == Element::map)) {
547 Attributes cached_attrs =
548 Attributes::fromElement(host->getContext()->get("radius"));
549 send->add(cached_attrs.get(PW_CLASS));
550 }
551
552 // Add attributes from configuration.
553 send->append(RadiusImpl::instance().acct_->attributes_.getAll());
554
555 // Return the handler.
556 RadiusAcctEnv env(stream.str(), event, subnet_id, send);
557 RadiusAcctHandlerPtr handler;
558 handler.reset(new RadiusAcctHandler(
559 env, std::bind(&RadiusAccounting::terminate, env, ph::_1)));
560
561 // Erase create timestamp on stop.
562 if (status == PW_STATUS_STOP) {
564 }
565
566 return (handler);
567}
568
571 IOAddress addr("::");
572 uint32_t subnet_id = SUBNET_ID_UNUSED;
573 DuidPtr duid;
574 HWAddrPtr hwaddr;
576 uint8_t prefix_len = 128;
577 bool force = false;
578 try {
579 // Parse arguments.
580 addr = SimpleParser::getAddress(arguments, "ip-address");
581 subnet_id = SimpleParser::getInteger(arguments, "subnet-id");
582 string txt = SimpleParser::getString(arguments, "duid");
583 duid.reset(new DUID(DUID::fromText(txt)));
584
585 if (arguments->contains("hw-address")) {
586 string hwaddr_txt = SimpleParser::getString(arguments, "hw-address");
587 hwaddr.reset(new HWAddr(HWAddr::fromText(hwaddr_txt)));
588 }
589
590 if (arguments->contains("type")) {
591 txt = SimpleParser::getString(arguments, "type");
592 if (txt == "IA_NA") {
593 type = Lease::TYPE_NA;
594 } else if (txt == "IA_TA") {
595 type = Lease::TYPE_TA;
596 } else if (txt == "IA_PD") {
597 type = Lease::TYPE_PD;
598
599 prefix_len = SimpleParser::getInteger(arguments, "prefix-len");
600
601 if ((prefix_len <= 0) || (prefix_len > 128)) {
603 "'prefix-len' value must be in range of [1..128]");
604 }
605
606 if (prefix_len != 128) {
607 IOAddress first_address = firstAddrInPrefix(addr, prefix_len);
608 if (first_address != addr) {
609 isc_throw(BadValue, "Prefix address: " << addr
610 << " exceeds prefix/prefix-len pair: " << first_address
611 << "/" << static_cast<uint32_t>(prefix_len));
612 }
613 }
614 } else {
615 isc_throw(BadValue, "bad type" << txt);
616 }
617 }
618
619 if (arguments->contains("force-create")) {
620 force = SimpleParser::getBoolean(arguments, "force-create");
621 }
622 } catch (const std::exception&) {
623 return (RadiusAcctHandlerPtr());
624 }
625 if (!addr.isV6()) {
626 return (RadiusAcctHandlerPtr());
627 }
628
629 // Fill attributes.
630 AttributesPtr send(new Attributes());
631
632 // Add duid.
633 vector<uint8_t> vec;
634 if (RadiusImpl::instance().clientid_pop0_) {
635 vec = pop0(duid);
636 } else {
637 vec = duid->getDuid();
638 }
639 string text;
640 if (RadiusImpl::instance().clientid_printable_) {
641 text = toPrintable(vec);
642 } else {
643 text = toHex(vec);
644 }
645 send->add(Attribute::fromString(PW_USER_NAME, text));
646
647 // Add hardware address.
648 if (hwaddr) {
649 string mac = hwaddr->toText(false);
650 if (RadiusImpl::instance().canonical_mac_address_) {
651 mac = canonize(mac);
652 }
654 }
655
656 // Add address or prefix.
657 if (type != Lease::TYPE_PD) {
659 } else {
661 prefix_len, addr));
662 }
663
664 // Get the create timestamp.
665 bool generate = false;
666 if ((event == EVENT_ADD) && force) {
667 generate = true;
668 }
669 ptime tm = getCreateTimestamp(addr, generate);
670
671 // Build the session Id.
672 ostringstream stream;
673 stream << addr << '@';
674 time_duration rtm(tm - epoch_);
675 stream << rtm.total_seconds() << ".";
676 rtm -= seconds(rtm.total_seconds());
677 stream << rtm.total_milliseconds();
678 send->add(Attribute::fromString(PW_ACCT_SESSION_ID, stream.str()));
679
680 // Accounting status type.
681 uint32_t status = PW_STATUS_ALIVE;
682 if (generate) {
683 status = PW_STATUS_START;
684 } else if (event == EVENT_DEL) {
685 status = PW_STATUS_STOP;
686 }
687 send->add(Attribute::fromInt(PW_ACCT_STATUS_TYPE, status));
688
689 // Add class: get host identifier.
691 vector<uint8_t> host_id;
692 switch (id_type) {
693 case Host::IDENT_DUID:
694 host_id = duid->getDuid();
695 break;
697 if (hwaddr) {
698 host_id = hwaddr->hwaddr_;
699 }
700 break;
701 case Host::IDENT_FLEX:
702 host_id = duid->getDuid();
703 if ((host_id.size() <= 2) ||
704 (host_id[0] != 0) || (host_id[1] != 0)) {
705 host_id.clear();
706 } else {
707 host_id = vector<uint8_t>(host_id.begin() + 2, host_id.end());
708 }
709 break;
710 default:
711 // not supported.
712 break;
713 }
714
715 // Add class: get host entry.
716 ConstHostPtr host;
717 if (!host_id.empty()) {
719 getCfgSubnets6()->getBySubnetId(subnet_id);
720 uint32_t host_subnet_id =
721 ((subnet && subnet->getReservationsGlobal()) ?
722 SUBNET_ID_GLOBAL : subnet_id);
723 host = HostMgr::instance().get6Any(host_subnet_id, id_type,
724 &host_id[0], host_id.size());
725 }
726 // Add class: get cached Class attribute.
727 // @todo: extend and make configurable with a list of attributes.
728 if (host && host->getContext() &&
729 (host->getContext()->getType() == Element::map)) {
730 Attributes cached_attrs =
731 Attributes::fromElement(host->getContext()->get("radius"));
732 send->add(cached_attrs.get(PW_CLASS));
733 }
734
735 // Add attributes from configuration.
736 send->append(RadiusImpl::instance().acct_->attributes_.getAll());
737
738 // Return the handler.
739 RadiusAcctEnv env(stream.str(), event, subnet_id, send);
740 RadiusAcctHandlerPtr handler;
741 handler.reset(new RadiusAcctHandler(
742 env, std::bind(&RadiusAccounting::terminate, env, ph::_1)));
743
744 // Erase create timestamp on stop.
745 if (status == PW_STATUS_STOP) {
747 }
748
749 return (handler);
750}
751
752void
754 handler->start();
755}
756
757void
759 env.finished_ = true;
760 if (result != OK_RC) {
762 .arg(env.session_id_)
763 .arg(env.event_)
764 .arg(eventToText(env.event_))
765 .arg(result)
766 .arg(exchangeRCtoText(result));
767 }
768}
769
770ptime
774 TMContainerAddressIndex::iterator it = idx.find(addr);
775 if (!generate) {
776 if (it == idx.end()) {
779 .arg(addr.toText());
780 } else {
781 return (it->timestamp_);
782 }
783 }
784
785 // generate is true or no address was found.
786 while (it != idx.end()) {
787 idx.erase(it);
788 // One round should be enough as the address is unique.
789 it = idx.find(addr);
790 }
791 ptime tm = microsec_clock::universal_time();
792 auto ret = container_.insert(LeaseTS(addr, tm));
793 if (!ret.second) {
795 .arg(addr.toText());
796 }
797
798 // Append to create timestamp file.
799 if (file_) {
800 try {
801 CSVRow row(file_->getColumnCount());
802
803 row.writeAt(file_->getColumnIndex("address"), addr);
804 time_duration rtm(tm - epoch_);
805 row.writeAt(file_->getColumnIndex("seconds"), rtm.total_seconds());
806 rtm -= seconds(rtm.total_seconds());
807 row.writeAt(file_->getColumnIndex("milliseconds"),
808 rtm.total_milliseconds());
809
810 file_->append(row);
812 } catch (const std::exception& ex) {
814 .arg(addr.toText())
815 .arg(ex.what());
816 file_.reset();
817 }
818 }
819
820 return (tm);
821}
822
823void
827 TMContainerAddressIndex::iterator it = idx.find(addr);
828 while (it != idx.end()) {
829 idx.erase(it);
830 // One round should be enough as the address is unique.
831 it = idx.find(addr);
832 }
833 // Append delete marker to create timestamp file.
834 if (file_) {
835 try {
836 CSVRow row(file_->getColumnCount());
837
838 row.writeAt(file_->getColumnIndex("address"), addr);
839 row.writeAt(file_->getColumnIndex("seconds"), 0L);
840 row.writeAt(file_->getColumnIndex("milliseconds"), 0L);
841
842 file_->append(row);
844 } catch (const std::exception& ex) {
846 .arg(addr.toText())
847 .arg(ex.what());
848 file_.reset();
849 }
850 }
851}
852
853bool
856 bool ok = true;
857 size_t adds = 0;
858 size_t fails = 0;
859 try {
860 file_->open(false);
861
862 for (;;) {
863 CSVRow row;
864 file_->next(row, true);
865 // Check if done.
866 if (row == CSVFile::EMPTY_ROW()) {
867 if (ok) {
869 .arg(adds)
870 .arg(container_.size());
871 break;
872 } else {
873 isc_throw(CSVFileError, "partial load");
874 }
875 }
876
877 // Get fields.
878 string addr_txt = row.readAt(file_->getColumnIndex("address"));
879 IOAddress addr(addr_txt);
880 long secs =
881 row.readAndConvertAt<long>(file_->getColumnIndex("seconds"));
882 long msecs =
883 row.readAndConvertAt<long>(file_->getColumnIndex("milliseconds"));
884
885 // Get an iterator to a possible entry.
886 TMContainerAddressIndex::iterator it = idx.find(addr);
887 while (it != idx.end()) {
888 idx.erase(it);
889 // One round should be enough as the address is unique.
890 it = idx.find(addr);
891 }
892
893 // Zero seconds means deleted record.
894 if (secs == 0) {
895 continue;
896 }
897
898 time_duration rtm (seconds(secs) + milliseconds(msecs));
899 ptime tm(epoch_ + rtm);
900 auto ret = container_.insert(LeaseTS(addr, tm));
901 if (!ret.second) {
902 ok = false;
903 ++fails;
904 } else {
905 ++adds;
906 }
907 }
908
909 } catch (const std::exception& ex) {
911 .arg(adds)
912 .arg(fails)
913 .arg(container_.size());
914 ok = false;
915 }
916
917 file_->close();
918
919 return (ok);
920}
921
922void
925 string newfilename = filename_ + ".new";
926 CSVFile newfile(newfilename);
927 size_t stored = 0;
928 if (container_.empty()) {
929 return;
930 }
931 try {
932 // Add columns
933 newfile.addColumn("address");
934 newfile.addColumn("seconds");
935 newfile.addColumn("milliseconds");
936
937 // Rename file if it exists.
938 if (newfile.exists()) {
939 string backfilename = filename_ + ".new~";
940 rename(newfilename.c_str(), backfilename.c_str());
941 }
942
943 // Open file (seed to end in case the rename failed).
944 newfile.open(true);
945
946 // Dump container content.
947 for (auto const& it : idx) {
948 CSVRow row(newfile.getColumnCount());
949
950 row.writeAt(newfile.getColumnIndex("address"), it.addr_);
951 time_duration rtm(it.timestamp_ - epoch_);
952 row.writeAt(newfile.getColumnIndex("seconds"),
953 rtm.total_seconds());
954 rtm -= seconds(rtm.total_seconds());
955 row.writeAt(newfile.getColumnIndex("milliseconds"),
956 rtm.total_milliseconds());
957
958 newfile.append(row);
959 ++stored;
960 }
961
962 // Close file.
963 newfile.close();
964
965 // Rename files.
966 string backfilename = filename_ + ".bak";
967 bool moved = true;
968 if (rename(filename_.c_str(), backfilename.c_str()) != 0) {
969 moved = false;
970 }
971 if (rename(newfilename.c_str(), filename_.c_str()) != 0) {
972 if (moved) {
973 // Try to restore current file.
974 rename(backfilename.c_str(), filename_.c_str());
975 }
976 }
977
979 .arg(stored);
980
981 } catch (const std::exception& ex) {
983 .arg(newfilename)
984 .arg(ex.what())
985 .arg(stored)
986 .arg(container_.size());
987 }
988 record_count_ = 0;
989}
990
991} // end of namespace isc::radius
992} // 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:147
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:337
@ IDENT_FLEX
Flexible host identifier.
Definition host.h:342
@ IDENT_CLIENT_ID
Definition host.h:341
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.
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 asynchronous accounting communication with servers.
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
RadiusService(const std::string &name)
Constructor.
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:358
void close()
Closes the CSV file.
Definition csv_file.cc:123
size_t getColumnCount() const
Returns the number of columns in the file.
Definition csv_file.h:403
bool exists() const
Checks if the CSV file exists and can be opened for reading.
Definition csv_file.cc:133
static CSVRow EMPTY_ROW()
Represents empty row.
Definition csv_file.h:491
void append(const CSVRow &row) const
Writes the CSV row into the file.
Definition csv_file.cc:167
void addColumn(const std::string &col_name)
Adds new column name.
Definition csv_file.cc:147
size_t getColumnIndex(const std::string &col_name) const
Returns the index of the column having specified name.
Definition csv_file.cc:237
virtual void open(const bool seek_to_end=false)
Opens existing file or creates a new one.
Definition csv_file.cc:302
Represents a single row of the CSV file.
Definition csv_file.h:51
T readAndConvertAt(const size_t at) const
Retrieves a value from the internal container.
Definition csv_file.h:156
void writeAt(const size_t at, const char *value)
Replaces the value at specified index.
Definition csv_file.cc:84
std::string readAt(const size_t at) const
Retrieves a value from the internal container.
Definition csv_file.cc:60
#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:29
boost::shared_ptr< const Subnet6 > ConstSubnet6Ptr
A const pointer to a Subnet6 object.
Definition subnet.h:623
boost::shared_ptr< const Subnet4 > ConstSubnet4Ptr
A const pointer to a Subnet4 object.
Definition subnet.h:458
boost::shared_ptr< DUID > DuidPtr
Definition duid.h:136
boost::shared_ptr< Lease6 > Lease6Ptr
Pointer to a Lease6 structure.
Definition lease.h:528
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition hwaddr.h:154
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:840
boost::shared_ptr< Lease4 > Lease4Ptr
Pointer to a Lease4 structure.
Definition lease.h:315
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
Event
Type of accounting events.
string exchangeRCtoText(const int rc)
ExchangeRC value -> name function.
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.
string toHex(const vector< uint8_t > &content)
Return hexadecimal textual representation of a vector.
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
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.
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:69
Type
Type of lease or pool.
Definition lease.h:46
@ TYPE_TA
the lease contains temporary IPv6 address
Definition lease.h:48
@ TYPE_PD
the lease contains IPv6 prefix (for prefix delegation)
Definition lease.h:49
@ TYPE_NA
the lease contains non-temporary IPv6 address
Definition lease.h:47
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.