LCOV - code coverage report
Current view: top level - vnsw/agent/services - dhcp_proto.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 59 182 32.4 %
Date: 2026-11-09 02:10:49 Functions: 7 16 43.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  * Copyright Contributors to the OpenSDN project
       4             :  */
       5             : 
       6             : #include <boost/filesystem.hpp>
       7             : #include "base/timer.h"
       8             : #include "cmn/agent_cmn.h"
       9             : #include "init/agent_param.h"
      10             : #include "oper/vn.h"
      11             : #include "services/dhcp_proto.h"
      12             : #include "services/dhcp_lease_db.h"
      13             : #include "services/services_types.h"
      14             : #include "services/services_init.h"
      15             : #include "pkt/pkt_init.h"
      16             : 
      17             : using namespace boost::asio;
      18             : using boost::asio::ip::udp;
      19             : 
      20           2 : DhcpProto::DhcpProto(Agent *agent, boost::asio::io_context &io,
      21           2 :                      bool run_with_vrouter) :
      22             :     Proto(agent, "Agent::Services", PktHandler::DHCP, io),
      23           2 :     run_with_vrouter_(run_with_vrouter), ip_fabric_interface_(NULL),
      24           2 :     ip_fabric_interface_index_(-1), pkt_interface_index_(-1),
      25           2 :     dhcp_server_socket_(io), dhcp_server_read_buf_(NULL),
      26           4 :     gateway_delete_seqno_(0) {
      27             :     // limit the number of entries in the workqueue
      28           2 :     work_queue_.SetSize(agent->params()->services_queue_limit());
      29           2 :     work_queue_.SetBounded(true);
      30             : 
      31           2 :     dhcp_relay_mode_ = agent->params()->dhcp_relay_mode();
      32           2 :     if (dhcp_relay_mode_) {
      33           0 :         boost::system::error_code ec;
      34           0 :         dhcp_server_socket_.open(udp::v4(), ec);
      35           0 :         assert(!ec);
      36           0 :         dhcp_server_socket_.bind(udp::endpoint(udp::v4(), DHCP_SERVER_PORT), ec);
      37           0 :         if (ec) {
      38           0 :             DHCP_TRACE(Error, "Error creating DHCP socket : " << ec);
      39             :         }
      40             : 
      41             :         // For DHCP requests coming from VMs in default VRF, when the IP received
      42             :         // from Nova is 0, DHCP module acts DHCP relay and relays the request onto
      43             :         // the fabric VRF. Vrouter sends responses for these to vhost0 interface.
      44             :         // We listen on DHCP server port to receive these responses (check option 82
      45             :         // header to decide that it is a response for a relayed request) and send
      46             :         // the response to the VM.
      47           0 :         AsyncRead();
      48             :     }
      49             : 
      50           2 :     iid_ = agent->interface_table()->Register(
      51             :                   boost::bind(&DhcpProto::ItfNotify, this, _2));
      52           2 :     vnid_ = agent->vn_table()->Register(
      53             :                   boost::bind(&DhcpProto::VnNotify, this, _2));
      54             : 
      55           2 :     lease_file_cleanup_timer_ =
      56           2 :         TimerManager::CreateTimer(io, "DhcpLeaseFileCleanupTimer",
      57             :         TaskScheduler::GetInstance()->GetTaskId("Agent::Services"),
      58             :         PktHandler::DHCP);
      59           2 : }
      60             : 
      61           4 : DhcpProto::~DhcpProto() {
      62           4 : }
      63             : 
      64           2 : void DhcpProto::Shutdown() {
      65           2 :     if (dhcp_relay_mode_) {
      66           0 :         boost::system::error_code ec;
      67           0 :         dhcp_server_socket_.shutdown(udp::socket::shutdown_both, ec);
      68           0 :         if (ec) {
      69           0 :             DHCP_TRACE(Error, "Error shutting down DHCP socket : " << ec);
      70             :         }
      71           0 :         dhcp_server_socket_.close (ec);
      72           0 :         if (ec) {
      73           0 :             DHCP_TRACE(Error, "Error closing DHCP socket : " << ec);
      74             :         }
      75             :     }
      76           2 :     agent_->interface_table()->Unregister(iid_);
      77           2 :     agent_->vn_table()->Unregister(vnid_);
      78           2 :     if (dhcp_server_read_buf_) delete [] dhcp_server_read_buf_;
      79           2 :     lease_file_cleanup_timer_->Cancel();
      80           2 :     TimerManager::DeleteTimer(lease_file_cleanup_timer_);
      81           2 : }
      82             : 
      83           0 : void DhcpProto::AsyncRead() {
      84           0 :     dhcp_server_read_buf_ = new uint8_t[kDhcpMaxPacketSize];
      85           0 :     dhcp_server_socket_.async_receive_from(
      86           0 :                 boost::asio::buffer(dhcp_server_read_buf_, kDhcpMaxPacketSize),
      87           0 :                 remote_endpoint_,
      88           0 :                 boost::bind(&DhcpProto::ReadHandler, this,
      89             :                             boost::asio::placeholders::error,
      90             :                             boost::asio::placeholders::bytes_transferred));
      91           0 : }
      92             : 
      93           0 : void DhcpProto::ReadHandler(const boost::system::error_code &error,
      94             :                             std::size_t len) {
      95           0 :     if (!error) {
      96           0 :         SendDhcpIpc(dhcp_server_read_buf_, len);
      97             :     } else  {
      98           0 :         DHCP_TRACE(Error, "Error reading packet <" + error.message() + ">");
      99           0 :         if (error == boost::system::errc::operation_canceled) {
     100           0 :             return;
     101             :         }
     102           0 :         delete [] dhcp_server_read_buf_;
     103           0 :         dhcp_server_read_buf_ = NULL;
     104             :     }
     105             : 
     106           0 :     AsyncRead();
     107             : }
     108             : 
     109           0 : void DhcpProto::SendDhcpIpc(uint8_t *dhcp, std::size_t len) {
     110           0 :     DhcpVhostMsg *ipc = new DhcpVhostMsg(dhcp, len);
     111           0 :     agent_->pkt()->pkt_handler()->SendMessage(PktHandler::DHCP, ipc);
     112           0 : }
     113             : 
     114           0 : ProtoHandler *DhcpProto::AllocProtoHandler(boost::shared_ptr<PktInfo> info,
     115             :                                            boost::asio::io_context &io) {
     116           0 :     return new DhcpHandler(agent(), info, io);
     117             : }
     118             : 
     119        1421 : void DhcpProto::ItfNotify(DBEntryBase *entry) {
     120        1421 :     Interface *itf = static_cast<Interface *>(entry);
     121        1421 :     if (entry->IsDeleted()) {
     122         179 :         if (itf->type() == Interface::PHYSICAL &&
     123           3 :             itf->name() == agent_->fabric_interface_name()) {
     124           2 :             set_ip_fabric_interface(NULL);
     125           2 :             set_ip_fabric_interface_index(-1);
     126         174 :         } else if (itf->type() == Interface::PACKET) {
     127           2 :             set_pkt_interface_index(-1);
     128         172 :         } else if (itf->type() == Interface::VM_INTERFACE) {
     129         169 :             VmInterface *vmi = static_cast<VmInterface *>(itf);
     130         169 :             if (gw_vmi_list_.erase(vmi)) {
     131           0 :                 DHCP_TRACE(Trace, "Gateway interface deleted: " << itf->name());
     132           0 :                 StartLeaseFileCleanupTimer();
     133             :             }
     134         169 :             DeleteLeaseDb(vmi);
     135             :         }
     136             :     } else {
     137        1249 :         if (itf->type() == Interface::PHYSICAL &&
     138           4 :             itf->name() == agent_->fabric_interface_name()) {
     139           2 :             set_ip_fabric_interface(itf);
     140           2 :             set_ip_fabric_interface_index(itf->id());
     141           2 :             if (run_with_vrouter_) {
     142           0 :                 set_ip_fabric_interface_mac(itf->mac());
     143             :             } else {
     144           2 :                 set_ip_fabric_interface_mac(MacAddress());
     145             :             }
     146        1243 :         } else if (itf->type() == Interface::PACKET) {
     147           2 :             set_pkt_interface_index(itf->id());
     148        1241 :         } else if (itf->type() == Interface::VM_INTERFACE) {
     149        1231 :             VmInterface *vmi = static_cast<VmInterface *>(itf);
     150        1231 :             if (vmi->vmi_type() == VmInterface::GATEWAY) {
     151           0 :                 DHCP_TRACE(Trace, "Gateway interface added: " << itf->name());
     152           0 :                 gw_vmi_list_.insert(vmi);
     153           0 :                 CreateLeaseDb(vmi);
     154             :             }
     155             :         }
     156             :     }
     157        1421 : }
     158             : 
     159           0 : void DhcpProto::CreateLeaseDb(VmInterface *vmi) {
     160           0 :     const Ip4Address &subnet = vmi->subnet();
     161           0 :     if (vmi->vn() == NULL || subnet.to_ulong() == 0) {
     162           0 :         DeleteLeaseDb(vmi);
     163           0 :         DHCP_TRACE(Trace, "DHCP Lease DB not created - config not present : " <<
     164             :                    vmi->subnet().to_string());
     165           0 :         return;
     166             :     }
     167             : 
     168           0 :     const IpAddress address(subnet);
     169           0 :     const VnIpam *vn_ipam = vmi->vn()->GetIpam(address);
     170           0 :     if (!vn_ipam) {
     171           0 :         DeleteLeaseDb(vmi);
     172           0 :         DHCP_TRACE(Trace, "DHCP Lease DB not created for subnet " <<
     173             :                    vmi->subnet().to_string() << " - IPAM not available");
     174           0 :         return;
     175             :     }
     176             : 
     177           0 :     std::string res;
     178           0 :     std::vector<Ip4Address> reserve_list;
     179           0 :     if (vmi->primary_ip_addr().to_ulong()) {
     180           0 :         reserve_list.push_back(vmi->primary_ip_addr());
     181           0 :         res = vmi->primary_ip_addr().to_string() + ", ";
     182             :     }
     183           0 :     reserve_list.push_back(vn_ipam->default_gw.to_v4());
     184           0 :     reserve_list.push_back(vn_ipam->dns_server.to_v4());
     185           0 :     res += vn_ipam->default_gw.to_v4().to_string() + ", ";
     186           0 :     res += vn_ipam->dns_server.to_v4().to_string();
     187           0 :     LeaseManagerMap::iterator it = lease_manager_.find(vmi);
     188           0 :     if (it == lease_manager_.end()) {
     189           0 :         DHCP_TRACE(Trace, "Created new DHCP Lease DB : " <<
     190             :                    vmi->name() << " " << vmi->subnet().to_string() << "/" <<
     191             :                    vmi->subnet_plen() << "; Reserved : " << res);
     192           0 :         DhcpLeaseDb *lease_db = new DhcpLeaseDb(vmi->subnet(),
     193           0 :                                                 vmi->subnet_plen(),
     194             :                                                 reserve_list,
     195           0 :                                                 GetLeaseFileName(vmi),
     196           0 :                                                 io_);
     197           0 :         lease_manager_.insert(LeaseManagerPair(vmi, lease_db));
     198             :     } else {
     199           0 :         DHCP_TRACE(Trace, "Updated DHCP Lease DB : " <<
     200             :                    vmi->subnet().to_string() << "/" <<
     201             :                    vmi->subnet_plen() << "; Reserved : " << res);
     202           0 :         it->second->Update(vmi->subnet(), vmi->subnet_plen(),
     203             :                            reserve_list);
     204             :     }
     205           0 : }
     206             : 
     207         169 : void DhcpProto::DeleteLeaseDb(VmInterface *vmi) {
     208         169 :     LeaseManagerMap::iterator it = lease_manager_.find(vmi);
     209         169 :     if (it != lease_manager_.end()) {
     210           0 :         delete it->second;
     211           0 :         lease_manager_.erase(it);
     212           0 :         DHCP_TRACE(Trace, "Deleted DHCP Lease DB : " << vmi->name());
     213             :     }
     214         169 : }
     215             : 
     216         269 : void DhcpProto::VnNotify(DBEntryBase *entry) {
     217         269 :     VnEntry *vn = static_cast<VnEntry *>(entry);
     218         269 :     for (std::set<VmInterface *>::iterator it = gw_vmi_list_.begin();
     219         269 :          it != gw_vmi_list_.end(); ++it) {
     220           0 :         VmInterface *vmi = *it;
     221           0 :         if (vmi->vn() != vn)
     222           0 :             continue;
     223           0 :         CreateLeaseDb(*it);
     224             :     }
     225         269 : }
     226             : 
     227           0 : DhcpLeaseDb *DhcpProto::GetLeaseDb(Interface *intrface) {
     228           0 :     LeaseManagerMap::iterator it = lease_manager_.find(intrface);
     229           0 :     if (it != lease_manager_.end())
     230           0 :         return it->second;
     231             : 
     232           0 :     return NULL;
     233             : }
     234             : 
     235           0 : std::string DhcpProto::GetLeaseFileName(const VmInterface *vmi) {
     236           0 :     if (!run_with_vrouter_)
     237           0 :         return "./dhcp." + UuidToString(vmi->GetUuid()) + ".leases";
     238             : 
     239           0 :     boost::filesystem::path dir(agent()->params()->agent_base_dir() + "/dhcp");
     240           0 :     boost::system::error_code ec;
     241           0 :     if (!boost::filesystem::exists(dir, ec)) {
     242           0 :         boost::filesystem::create_directory(dir, ec);
     243             :         // boost::filesystem::permissions(dir, boost::filesystem::remove_perms |
     244             :         //                                     boost::filesystem::others_all, ec);
     245           0 :         if (ec) {
     246           0 :             DHCP_TRACE(Error, "Cannot create DHCP Lease directory : " << dir);
     247             :         }
     248             :     }
     249             : 
     250           0 :     return dir.string() + "/dhcp." + UuidToString(vmi->GetUuid()) + ".leases";
     251           0 : }
     252             : 
     253           0 : void DhcpProto::StartLeaseFileCleanupTimer() {
     254           0 :     gateway_delete_seqno_++;
     255           0 :     lease_file_cleanup_timer_->Cancel();
     256           0 :     lease_file_cleanup_timer_->Start(kDhcpLeaseFileDeleteTimeout,
     257             :                                boost::bind(&DhcpProto::LeaseFileCleanupExpiry,
     258             :                                            this, gateway_delete_seqno_));
     259           0 : }
     260             : 
     261           0 : bool DhcpProto::LeaseFileCleanupExpiry(uint32_t seqno) {
     262           0 :     if (seqno == gateway_delete_seqno_ && !gw_vmi_list_.empty()) {
     263             :         // get valid file list
     264           0 :         std::set<std::string> filelist;
     265           0 :         for (std::set<VmInterface *>::const_iterator it = gw_vmi_list_.begin();
     266           0 :              it != gw_vmi_list_.end(); ++it) {
     267           0 :             filelist.insert(GetLeaseFileName(*it));
     268             :         }
     269             : 
     270             :         // delete unused files
     271           0 :         boost::system::error_code ec;
     272           0 :         boost::filesystem::path dir(agent()->params()->agent_base_dir() +
     273           0 :                                     "/dhcp");
     274           0 :         if (boost::filesystem::exists(dir, ec) &&
     275           0 :             boost::filesystem::is_directory(dir, ec)) {
     276           0 :             for (boost::filesystem::directory_iterator it(dir);
     277           0 :                  it != boost::filesystem::directory_iterator(); ++it) {
     278           0 :                 std::string filename = it->path().string();
     279           0 :                 if (boost::filesystem::is_regular_file(it->status()) &&
     280           0 :                     filelist.find(filename) == filelist.end()) {
     281           0 :                     DHCP_TRACE(Trace, "Removing DHCP Lease file : " << filename);
     282           0 :                     remove(filename.c_str()); // doesnt invalidate iterator
     283             :                 }
     284           0 :             }
     285             :         }
     286           0 :     }
     287             : 
     288           0 :     return false;
     289             : }

Generated by: LCOV version 1.14