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 : }
|