kea-custom-hooks
FeM custom hooks libraries for Kea DHCP
Exporter.cpp
Go to the documentation of this file.
1#include <exception>
2
3#include <xmlrpc-c/girerr.hpp>
4
5#include <log/macros.h>
6
7#include "Exporter.hpp"
10
11#include "log_admindb_arpwatch_exporter.h"
12
13namespace
14{
15constexpr uint32_t SUBNET_ID_TO_VLAN_MODULUS{10000};
16
17std::string get_ip2mac_key(const arpwatch::Exporter::ip4_addr_t& ip)
18{
19 return "ip2mac-" + ip.toText();
20}
21
22std::string get_mac2ip_key(const arpwatch::Exporter::mac_addr_t& mac)
23{
24 return "mac2ip-" + mac.toText(false);
25}
26
27std::string get_ip2macWithVLAN_key(const arpwatch::Exporter::ip4_addr_t& ip,
28 const arpwatch::Exporter::subnet_id_t subnet_id)
29{
30 const uint16_t vlan = subnet_id % SUBNET_ID_TO_VLAN_MODULUS;
31 return "ip2macWithVLAN-" + std::to_string(vlan) + '-' + ip.toText();
32}
33} // namespace
34
35namespace arpwatch
36{
38{
39 if (worker_keep_running) {
41 }
42}
43
45{
46 LOG_DEBUG(logger, 1, EXPORTER_REDIS_WORKER_START);
47 worker_keep_running = true;
48 worker = std::make_unique<std::thread>([this]() { run_worker(); });
49}
50
52{
53 LOG_DEBUG(logger, 1, EXPORTER_REDIS_WORKER_STOP);
54 worker_keep_running = false;
55 worker_notify_cv.notify_all();
56 if (worker->joinable()) {
57 worker->join();
58 }
59}
60
62 const Exporter::subnet_id_t subnet_id,
63 const std::chrono::seconds valid_lft)
64{
65 LOG_DEBUG(logger, 10, EXPORTER_WRITE_REDIS)
66 .arg(mac.toText(false).c_str())
67 .arg(ip4.toText().c_str());
68 std::unique_lock<std::mutex> lck{lease_queue_mtx};
69 lease_queue.emplace(mac, ip4, subnet_id, valid_lft);
70 lck.unlock();
71 worker_notify_cv.notify_all();
72}
73
75{
76 LOG_DEBUG(logger, 20, EXPORTER_FLUSH_LEASE_QUEUE);
77
78 std::unique_lock<std::mutex> lck{lease_queue_mtx};
79 while (not lease_queue.empty()) {
80 lck.unlock();
81 worker_notify_cv.notify_all();
82 std::this_thread::sleep_for(LEASE_QUEUE_FLUSH_WAIT);
83 lck.lock();
84 }
85}
86
87void Exporter::run_worker()
88{
89 while (worker_keep_running) {
90 std::unique_lock<std::mutex> cv_lk{lease_queue_mtx};
91 worker_notify_cv.wait(
92 cv_lk, [this]() { return not lease_queue.empty() || not worker_keep_running; });
93
94 if (!redis_adapter.is_connected()) {
95 try {
96 redis_adapter.connect();
97 }
98 catch (HiredisException& e) {
99 LOG_WARN(logger, HIREDIS_ERROR).arg(e.what());
100 cv_lk.unlock();
101 std::this_thread::sleep_for(REDIS_RECONNECT_WAIT);
102 continue;
103 }
104 }
105
106 while (not lease_queue.empty()) {
107 assert(cv_lk.owns_lock());
108
109 const auto& lease = lease_queue.front();
110 cv_lk.unlock();
111
112 try {
113 worker_send_lease_to_redis(lease);
114 worker_send_lease_to_arpwatch(lease);
115 }
116 catch (HiredisException& ex) {
117 LOG_WARN(logger, HIREDIS_ERROR).arg(ex.what());
118 // Start from the top of the outer loop, thus forcing a reconnect attempt
119 break;
120 }
121 catch (girerr::error& ex) {
122 LOG_WARN(logger, EXPORTER_RPC_ERROR).arg(ex.what());
123 }
124 catch (std::exception& ex) {
125 LOG_WARN(logger, EXPORTER_PUSH_ERROR).arg(ex.what());
126 }
127
128 cv_lk.lock();
129 lease_queue.pop();
130 }
131 }
132}
133
134void Exporter::worker_send_lease_to_redis(const lease4_t& lease)
135{
136 const auto& [mac, ip, subnet_id, valid_lft] = lease;
137 std::string ip2mac_key = get_ip2mac_key(ip);
138 std::string mac2ip_key = get_mac2ip_key(mac);
139 std::string ip2macWithVLAN_key = get_ip2macWithVLAN_key(ip, subnet_id);
140 std::string mac_value = mac.toText(false);
141 std::string ip4_value = ip.toText();
142
143 const auto expire =
144 static_cast<std::uint32_t>(static_cast<double>(valid_lft.count()) * REDIS_EXPIRE_FACT);
145
146 LOG_DEBUG(logger, 10, EXPORTER_SEND_REDIS)
147 .arg(ip2mac_key.c_str())
148 .arg(mac_value.c_str())
149 .arg(expire);
150 redis_adapter.set(ip2mac_key, mac_value, expire);
151
152 LOG_DEBUG(logger, 10, EXPORTER_SEND_REDIS)
153 .arg(mac2ip_key.c_str())
154 .arg(ip4_value.c_str())
155 .arg(expire);
156 redis_adapter.sadd(mac2ip_key, ip4_value, expire);
157
158 if (subnet_id != 0) {
159 LOG_DEBUG(logger, 10, EXPORTER_SEND_REDIS)
160 .arg(ip2macWithVLAN_key)
161 .arg(mac_value)
162 .arg(expire);
163 redis_adapter.set(ip2macWithVLAN_key, mac_value, expire);
164 }
165}
166
167void Exporter::worker_send_lease_to_arpwatch(const lease4_t& lease)
168{
169 const auto& [mac, ip4, subnet_id, valid_lft] = lease;
170 const auto vlan_id = subnet_id % SUBNET_ID_TO_VLAN_MODULUS;
171 arpwatch_rpc.push_seen_entry(vlan_id, mac, ip4);
172}
173
174} // namespace arpwatch
virtual void push_seen_entry(const isc::dhcp::SubnetID &subnet_id, const isc::dhcp::HWAddr &mac, const isc::asiolink::IOAddress &ip4)
RPC method callers.
Definition: ArpwatchRPC.cpp:26
virtual sadd_reply_t sadd(const std::string &key, const std::string &value, const timeout_t expire=0)
virtual void connect()
Connect to the Redis server.
virtual set_reply_t set(const std::string &key, const std::string &value, const timeout_t expire=0)
virtual bool is_connected() const noexcept
A problem has occured whilst interacting with Redis.
const char * what() const noexcept
static constexpr double REDIS_EXPIRE_FACT
Definition: Exporter.hpp:45
isc::dhcp::HWAddr mac_addr_t
Definition: Exporter.hpp:38
static constexpr std::chrono::seconds REDIS_RECONNECT_WAIT
Definition: Exporter.hpp:43
static constexpr std::chrono::milliseconds LEASE_QUEUE_FLUSH_WAIT
Definition: Exporter.hpp:46
void flush_lease_queue()
Definition: Exporter.cpp:74
isc::dhcp::SubnetID subnet_id_t
Definition: Exporter.hpp:40
void push_lease(const mac_addr_t &mac, const ip4_addr_t &ip4, subnet_id_t subnet_id=0, std::chrono::seconds valid_lft=DEFAULT_VALID_LFT)
Definition: Exporter.cpp:61
isc::asiolink::IOAddress ip4_addr_t
Definition: Exporter.hpp:39