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