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

Generated by: LCOV version 1.14