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