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