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