Line data Source code
1 :
2 : #include <pugixml/pugixml.hpp>
3 : #include <boost/filesystem.hpp>
4 : #include "base/address_util.h"
5 : #include "base/timer.h"
6 : #include "cmn/agent_cmn.h"
7 : #include "init/agent_param.h"
8 : #include "services/services_types.h"
9 : #include "services/services_init.h"
10 : #include "services/dhcp_lease_db.h"
11 : #include "services/dhcp_proto.h"
12 :
13 : using namespace pugi;
14 :
15 0 : DhcpLeaseDb::DhcpLeaseDb(const Ip4Address &subnet, uint8_t plen,
16 : const std::vector<Ip4Address> &reserve_addresses,
17 : const std::string &lease_filename,
18 0 : boost::asio::io_context &io) :
19 0 : subnet_(subnet), plen_(plen),
20 0 : max_lease_update_count_(0), lease_update_count_(0),
21 0 : lease_timeout_(kDhcpLeaseTimer), lease_filename_(lease_filename) {
22 0 : ReserveAddresses(reserve_addresses, true);
23 0 : LoadLeaseFile();
24 0 : timer_ = TimerManager::CreateTimer(io, "DhcpLeaseTimer",
25 : TaskScheduler::GetInstance()->
26 : GetTaskId("Agent::Services"),
27 : PktHandler::DHCP);
28 0 : timer_->Start(lease_timeout_,
29 : boost::bind(&DhcpLeaseDb::LeaseTimerExpiry, this));
30 0 : }
31 :
32 0 : DhcpLeaseDb::~DhcpLeaseDb() {
33 0 : lease_bitmap_.clear();
34 0 : released_lease_bitmap_.clear();
35 0 : timer_->Cancel();
36 0 : TimerManager::DeleteTimer(timer_);
37 : // remove(lease_filename_.c_str());
38 0 : }
39 :
40 0 : void DhcpLeaseDb::Update(const Ip4Address &subnet, uint8_t plen,
41 : const std::vector<Ip4Address> &reserve_addresses) {
42 : // TODO: in case we update after allocating addresses from earlier subnet,
43 : // they will be ignored; no trace of that in the agent after this update;
44 0 : bool subnet_change = false;
45 0 : if (subnet != subnet_ || plen != plen_) {
46 0 : subnet_ = subnet;
47 0 : plen_ = plen;
48 0 : leases_.clear();
49 0 : lease_bitmap_.clear();
50 0 : released_lease_bitmap_.clear();
51 0 : remove(lease_filename_.c_str());
52 0 : subnet_change = true;
53 : }
54 0 : ReserveAddresses(reserve_addresses, subnet_change);
55 0 : }
56 :
57 0 : bool DhcpLeaseDb::Allocate(const MacAddress &mac, Ip4Address *ip,
58 : uint64_t lease) {
59 : size_t index;
60 0 : uint64_t expiry = ClockMonotonicUsec() + (lease * 1000000);
61 :
62 : std::set<DhcpLease>::iterator it =
63 0 : leases_.find(DhcpLease(mac, Ip4Address(), 0, false));
64 0 : if (it != leases_.end()) {
65 0 : index = AddressToIndex(it->ip_);
66 0 : if (!IsReservedAddress(it->ip_) &&
67 0 : (!it->released_ || released_lease_bitmap_[index])) {
68 0 : *ip = it->ip_;
69 0 : lease_update_count_++;
70 0 : UpdateLease(mac, *ip, expiry, false);
71 0 : PersistLeaseRecord(mac, *ip, expiry, false);
72 0 : return true;
73 : } else {
74 : // A reserved address was leased earlier or the lease has been
75 : // given to a different client, cannot give the same
76 : // lease now; release it and allocate newly
77 0 : leases_.erase(it);
78 : }
79 : }
80 :
81 0 : index = lease_bitmap_.find_first();
82 0 : if (index == lease_bitmap_.npos) {
83 0 : index = released_lease_bitmap_.find_first();
84 0 : if (index == released_lease_bitmap_.npos) {
85 0 : DHCP_TRACE(Error, "DHCP Lease not available for MAC " <<
86 : mac.ToString() << " in Subnet " <<
87 : subnet_.to_string());
88 0 : return false;
89 : }
90 : }
91 :
92 0 : IndexToAddress(index, ip);
93 0 : UpdateLease(mac, *ip, expiry, false);
94 0 : PersistLeaseRecord(mac, *ip, expiry, false);
95 0 : return true;
96 : }
97 :
98 0 : bool DhcpLeaseDb::Release(const MacAddress &mac) {
99 : std::set<DhcpLease>::const_iterator it =
100 0 : leases_.find(DhcpLease(mac, Ip4Address(), 0, false));
101 0 : if (it != leases_.end()) {
102 0 : lease_update_count_++;
103 0 : UpdateLease(mac, it->ip_, it->lease_expiry_time_, true);
104 0 : PersistLeaseRecord(mac, it->ip_, it->lease_expiry_time_, true);
105 0 : return true;
106 : }
107 :
108 0 : return false;
109 : }
110 :
111 0 : bool DhcpLeaseDb::LeaseTimerExpiry() {
112 0 : uint64_t current_time = ClockMonotonicUsec();
113 :
114 0 : std::vector<DhcpLease> changed_leases;
115 0 : for (std::set<DhcpLease>::iterator it = leases_.begin();
116 0 : it != leases_.end(); ) {
117 0 : size_t index = AddressToIndex(it->ip_);
118 0 : if (it->released_ && !released_lease_bitmap_[index]) {
119 : // lease is not valid and address is re-allocated; remove the lease
120 0 : DHCP_TRACE(Trace, "DHCP Lease removed : " << it->mac_.ToString() <<
121 : it->ip_.to_string());
122 0 : leases_.erase(it++);
123 0 : continue;
124 0 : }
125 0 : if (current_time > it->lease_expiry_time_) {
126 0 : it->released_ = true;
127 0 : released_lease_bitmap_[index] = 1;
128 0 : changed_leases.push_back(*it);
129 : }
130 0 : it++;
131 : }
132 :
133 0 : lease_update_count_ += changed_leases.size();
134 0 : if (lease_update_count_ >= max_lease_update_count_) {
135 : // Re-create the lease file, so that it is compacted
136 0 : CreateLeaseFile();
137 0 : lease_update_count_ = 0;
138 0 : } else if (changed_leases.size()) {
139 0 : PersistLeaseRecords(changed_leases);
140 : }
141 :
142 0 : return true;
143 0 : }
144 :
145 0 : void DhcpLeaseDb::UpdateLease(const MacAddress &mac, const Ip4Address &ip,
146 : uint64_t expiry, bool released) {
147 0 : size_t index = AddressToIndex(ip);
148 0 : lease_bitmap_[index] = 0;
149 0 : released_lease_bitmap_[index] = (released) ? 1 : 0;
150 : std::set<DhcpLease>::iterator it =
151 0 : leases_.find(DhcpLease(mac, Ip4Address(), 0, false));
152 0 : if (it != leases_.end()) {
153 0 : it->ip_ = ip;
154 0 : it->lease_expiry_time_ = expiry;
155 0 : it->released_ = released;
156 : } else {
157 0 : leases_.insert(DhcpLease(mac, ip, expiry, released));
158 : }
159 0 : DHCP_TRACE(Trace, "DHCP Lease : " << mac.ToString() << " " <<
160 : ip.to_string() << " " << expiry << " " <<
161 : (released ? "released" : "valid"));
162 0 : }
163 :
164 : // block the reserved addresses
165 0 : void DhcpLeaseDb::ReserveAddresses(const std::vector<Ip4Address> &addresses,
166 : bool subnet_change) {
167 0 : if (subnet_change) {
168 0 : uint32_t num_bits = 1 << (32 - plen_);
169 0 : if (num_bits < (2 + addresses.size()))
170 0 : return;
171 :
172 0 : lease_bitmap_.resize(num_bits, 1);
173 0 : lease_bitmap_[0] = 0;
174 0 : lease_bitmap_[(1 << (32 - plen_)) - 1] = 0;
175 0 : released_lease_bitmap_.resize(num_bits, 0);
176 0 : max_lease_update_count_ = (num_bits < 256) ? 256 :
177 : ((num_bits > 8192) ? 8192 : num_bits);
178 : }
179 0 : for (std::vector<Ip4Address>::const_iterator it = addresses.begin();
180 0 : it != addresses.end(); ++it) {
181 0 : if (IsIp4SubnetMember(*it, subnet_, plen_)) {
182 0 : size_t index = AddressToIndex(*it);
183 0 : lease_bitmap_[index] = 0;
184 0 : released_lease_bitmap_[index] = 0;
185 : }
186 : }
187 0 : reserve_addresses_ = addresses;
188 : }
189 :
190 0 : void DhcpLeaseDb::IndexToAddress(size_t index, Ip4Address *address) const {
191 0 : Ip4Address ip(subnet_.to_ulong() + index);
192 0 : *address = ip;
193 0 : }
194 :
195 0 : size_t DhcpLeaseDb::AddressToIndex(const Ip4Address &address) const {
196 0 : return address.to_ulong() & (0xFFFFFFFF >> plen_);
197 : }
198 :
199 0 : bool DhcpLeaseDb::IsReservedAddress(const Ip4Address &address) const {
200 0 : for (std::vector<Ip4Address>::const_iterator it =
201 0 : reserve_addresses_.begin(); it != reserve_addresses_.end(); ++it) {
202 0 : if (address == *it)
203 0 : return true;
204 : }
205 0 : return false;
206 : }
207 :
208 : // Added for UT
209 0 : void DhcpLeaseDb::ClearLeases() {
210 : // clear the lease bitmaps and leases
211 0 : leases_.clear();
212 0 : lease_bitmap_.set();
213 0 : released_lease_bitmap_.reset();
214 0 : ReserveAddresses(reserve_addresses_, true);
215 0 : }
216 :
217 : // Added for UT
218 0 : void DhcpLeaseDb::set_lease_timeout(uint32_t timeout) {
219 0 : if (lease_timeout_ != timeout) {
220 0 : lease_timeout_ = timeout;
221 0 : timer_->Cancel();
222 0 : timer_->Start(lease_timeout_,
223 : boost::bind(&DhcpLeaseDb::LeaseTimerExpiry, this));
224 : }
225 0 : }
226 :
227 : // Write the complete lease file
228 0 : void DhcpLeaseDb::CreateLeaseFile() {
229 0 : std::ofstream lease_ofstream;
230 0 : lease_ofstream.open(lease_filename_.c_str(),
231 : std::ofstream::out | std::ofstream::trunc);
232 0 : if (!lease_ofstream.good()) {
233 0 : lease_ofstream.close();
234 0 : DHCP_TRACE(Error, "Cannot create DHCP Lease file: " << lease_filename_);
235 0 : return;
236 : }
237 :
238 0 : for (std::set<DhcpLease>::const_iterator it = leases_.begin();
239 0 : it != leases_.end(); ++it) {
240 0 : WriteLeaseRecord(lease_ofstream, it->mac_, it->ip_,
241 0 : it->lease_expiry_time_, it->released_);
242 : }
243 :
244 0 : lease_ofstream.flush();
245 0 : lease_ofstream.close();
246 0 : }
247 :
248 : // Append lease record
249 0 : void DhcpLeaseDb::PersistLeaseRecord(const MacAddress &mac,
250 : const Ip4Address &ip,
251 : const uint64_t &expiry,
252 : bool released) {
253 0 : std::ofstream lease_ofstream;
254 0 : lease_ofstream.open(lease_filename_.c_str(), std::ofstream::app);
255 0 : if (!lease_ofstream.good()) {
256 0 : lease_ofstream.close();
257 0 : DHCP_TRACE(Error, "Cannot open DHCP Lease file for writing : " <<
258 : lease_filename_);
259 0 : return;
260 : }
261 :
262 0 : WriteLeaseRecord(lease_ofstream, mac, ip, expiry, released);
263 :
264 0 : lease_ofstream.flush();
265 0 : lease_ofstream.close();
266 0 : }
267 :
268 0 : void DhcpLeaseDb::PersistLeaseRecords(const std::vector<DhcpLease> &leases) {
269 0 : std::ofstream lease_ofstream;
270 0 : lease_ofstream.open(lease_filename_.c_str(), std::ofstream::app);
271 0 : if (!lease_ofstream.good()) {
272 0 : lease_ofstream.close();
273 0 : DHCP_TRACE(Error, "Cannot open DHCP Lease file for writing : "
274 : << lease_filename_);
275 0 : return;
276 : }
277 :
278 0 : for (std::vector<DhcpLease>::const_iterator it = leases.begin();
279 0 : it != leases.end(); ++it) {
280 0 : WriteLeaseRecord(lease_ofstream, it->mac_, it->ip_,
281 0 : it->lease_expiry_time_, it->released_);
282 : }
283 :
284 0 : lease_ofstream.flush();
285 0 : lease_ofstream.close();
286 0 : }
287 :
288 0 : void DhcpLeaseDb::WriteLeaseRecord(std::ofstream &lease_ofstream,
289 : const MacAddress &mac, const Ip4Address &ip,
290 : const uint64_t &expiry, bool released) {
291 0 : lease_ofstream << "<lease>";
292 0 : lease_ofstream << " <mac>" << mac.ToString() << "</mac>";
293 0 : lease_ofstream << " <ip>" << ip.to_string() << "</ip>";
294 0 : lease_ofstream << " <expiry>" << expiry << "</expiry>";
295 : lease_ofstream << " <released>" <<
296 0 : (released ? "true" : "false") << "</released>";
297 0 : lease_ofstream << " </lease>" << std::endl;
298 :
299 0 : if (!lease_ofstream.good()) {
300 0 : DHCP_TRACE(Error, "Lease write to " << lease_filename_ <<
301 : " failed : " << mac.ToString() << " " << ip.to_string() <<
302 : (released ? " released" : " valid"));
303 : }
304 0 : }
305 :
306 0 : void DhcpLeaseDb::LoadLeaseFile() {
307 0 : std::string leases;
308 0 : ReadLeaseFile(leases);
309 0 : ParseLeaseFile(leases);
310 0 : }
311 :
312 0 : void DhcpLeaseDb::ReadLeaseFile(std::string &leases) {
313 0 : std::ifstream ifile(lease_filename_.c_str());
314 0 : if (!ifile.good()) {
315 0 : ifile.close();
316 0 : DHCP_TRACE(Error, "Cannot open DHCP Lease file for reading : " <<
317 : lease_filename_);
318 0 : return;
319 : }
320 :
321 0 : ifile.seekg(0, std::ios::end);
322 0 : leases.reserve(ifile.tellg());
323 0 : ifile.seekg(0, std::ios::beg);
324 :
325 0 : leases.assign((std::istreambuf_iterator<char>(ifile)),
326 : std::istreambuf_iterator<char>());
327 0 : ifile.close();
328 0 : }
329 :
330 0 : void DhcpLeaseDb::ParseLeaseFile(const std::string &leases) {
331 0 : if (leases.empty())
332 0 : return;
333 :
334 0 : std::istringstream sstream(leases);
335 0 : xml_document xdoc;
336 0 : xml_parse_result result = xdoc.load(sstream);
337 0 : if (!result) {
338 0 : DHCP_TRACE(Error, "Unable to load DHCP leases. status=" <<
339 : result.status << ", offset=" << result.offset);
340 0 : return;
341 : }
342 :
343 0 : for (xml_node root = xdoc.first_child(); root; root = root.next_sibling()) {
344 0 : if (strcmp(root.name(), "lease") == 0) {
345 0 : ParseLease(root);
346 : }
347 : }
348 0 : }
349 :
350 0 : void DhcpLeaseDb::ParseLease(const xml_node &lease) {
351 0 : MacAddress mac;
352 0 : Ip4Address ip;
353 0 : uint64_t expiry = 0;
354 0 : boost::system::error_code ec;
355 0 : bool released = false;
356 0 : bool error = false;
357 0 : for (xml_node root = lease.first_child(); root; root = root.next_sibling()) {
358 0 : if (strcmp(root.name(), "mac") == 0) {
359 0 : mac = MacAddress::FromString(root.child_value(), &ec);
360 0 : if (ec)
361 0 : error = true;
362 0 : continue;
363 : }
364 0 : if (strcmp(root.name(), "ip") == 0) {
365 0 : ip = Ip4Address::from_string(root.child_value(), ec);
366 0 : if (ec)
367 0 : error = true;
368 0 : continue;
369 : }
370 0 : if (strcmp(root.name(), "expiry") == 0) {
371 : char *endp;
372 0 : expiry = strtoull(root.child_value(), &endp, 10);
373 0 : while (isspace(*endp)) endp++;
374 0 : if (endp[0] != '\0')
375 0 : error = true;
376 0 : continue;
377 0 : }
378 0 : if (strcmp(root.name(), "released") == 0) {
379 0 : if (strcmp(root.child_value(), "true") == 0)
380 0 : released = true;
381 : }
382 : }
383 :
384 0 : if (mac.IsZero() || ip.is_unspecified() || error) {
385 0 : DHCP_TRACE(Error, "Invalid DHCP Lease record : " << mac.ToString() << " " <<
386 : ip.to_string() << " " << expiry);
387 0 : return;
388 : }
389 :
390 0 : UpdateLease(mac, ip, expiry, released);
391 : }
392 :
393 0 : void ShowGwDhcpLeases::HandleRequest() const {
394 0 : std::vector<GwDhcpLeases> lease_list;
395 : const DhcpProto::LeaseManagerMap &lease_mgr =
396 0 : Agent::GetInstance()->GetDhcpProto()->lease_manager();
397 0 : for (DhcpProto::LeaseManagerMap::const_iterator it = lease_mgr.begin();
398 0 : it != lease_mgr.end(); ++it) {
399 0 : GwDhcpLeases gw_leases;
400 0 : std::vector<DhcpLeaseData> out_lease_data;
401 : const std::set<DhcpLeaseDb::DhcpLease> &lease_data =
402 0 : it->second->leases();
403 0 : for (std::set<DhcpLeaseDb::DhcpLease>::const_iterator lit =
404 0 : lease_data.begin(); lit != lease_data.end(); ++lit) {
405 0 : uint64_t current_time = ClockMonotonicUsec();
406 0 : DhcpLeaseData data;
407 0 : data.mac = lit->mac_.ToString();
408 0 : data.ip = lit->ip_.to_string();
409 0 : data.expiry_us = (lit->lease_expiry_time_ > current_time) ?
410 0 : (lit->lease_expiry_time_ - current_time) : 0;
411 0 : data.released = lit->released_ ? "yes" : "no";
412 0 : out_lease_data.push_back(data);
413 0 : }
414 0 : VmInterface *vmi = static_cast<VmInterface *>(it->first);
415 0 : gw_leases.physical_interface = vmi->parent()->name();
416 0 : gw_leases.vm_interface = vmi->name();
417 0 : gw_leases.leases = out_lease_data;
418 0 : lease_list.push_back(gw_leases);
419 0 : }
420 :
421 0 : GwDhcpLeasesResponse *resp = new GwDhcpLeasesResponse();
422 0 : resp->set_context(context());
423 0 : resp->set_gw_leases(lease_list);
424 0 : resp->Response();
425 0 : }
|