1 // Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 #include <config.h>
8
9 #include <exceptions/exceptions.h>
10 #include <dhcpsrv/network_state.h>
11 #include <dhcpsrv/timer_mgr.h>
12 #include <util/multi_threading_mgr.h>
13 #include <boost/enable_shared_from_this.hpp>
14 #include <functional>
15 #include <string>
16
17 using namespace isc::util;
18
19 namespace {
20
21 /// @brief Name of the timer used by the @c NetworkState class.
22 const std::string NETWORK_STATE_TIMER_NAME_USER_CMD = "network-state-timer-user-cmd";
23 const std::string NETWORK_STATE_TIMER_NAME_HA_CMD = "network-state-timer-ha-cmd";
24
25 } // end of anonymous namespace
26
27 namespace isc {
28 namespace dhcp {
29
30 /// @brief Implementation of the @c NetworkState class.
31 class NetworkStateImpl : public boost::enable_shared_from_this<NetworkStateImpl> {
32 public:
33
34 /// @brief Constructor.
NetworkStateImpl(const NetworkState::ServerType & server_type)35 NetworkStateImpl(const NetworkState::ServerType& server_type)
36 : server_type_(server_type), globally_disabled_(false),
37 disabled_subnets_(), disabled_networks_(),
38 timer_mgr_(TimerMgr::instance()), disabled_by_user_command_(false),
39 disabled_by_ha_command_(false), disabled_by_db_connection_(0) {
40 }
41
42 /// @brief Destructor.
~NetworkStateImpl()43 ~NetworkStateImpl() {
44 destroyTimer(NetworkState::Origin::USER_COMMAND);
45 destroyTimer(NetworkState::Origin::HA_COMMAND);
46 }
47
48 /// @brief Sets appropriate disabled or enabled DHCP service state for the
49 /// respective origin.
50 ///
51 /// @note If any of the user commands, HA internal commands or connection
52 /// recovery processes disable the dhcp service, the service will remain
53 /// disabled until all flags are cleared.
54 /// In the case of the connection recovery a reference count is used
55 /// internally, so that all connections must be restored before enabling
56 /// the network state.
57 ///
58 /// @param disable The value of the flag used to perform the transition.
59 /// @param origin The origin of the state transition.
setDisableService(const bool disable,const NetworkState::Origin & origin)60 void setDisableService(const bool disable,
61 const NetworkState::Origin& origin) {
62 if (disable) {
63 // Disable the service for any flag.
64 globally_disabled_ = true;
65 switch (origin) {
66 case NetworkState::Origin::USER_COMMAND:
67 disabled_by_user_command_ = true;
68 break;
69 case NetworkState::Origin::HA_COMMAND:
70 disabled_by_ha_command_ = true;
71 break;
72 case NetworkState::Origin::DB_CONNECTION:
73 ++disabled_by_db_connection_;
74 break;
75 default:
76 isc_throw(NotImplemented, "origin value not handled when "
77 "disabling the network state");
78 break;
79 }
80 } else {
81 switch (origin) {
82 case NetworkState::Origin::USER_COMMAND:
83 disabled_by_user_command_ = false;
84 break;
85 case NetworkState::Origin::HA_COMMAND:
86 disabled_by_ha_command_ = false;
87 break;
88 case NetworkState::Origin::DB_CONNECTION:
89 // Never go below 0 (using unsigned type).
90 // This should never happen anyway.
91 if (disabled_by_db_connection_) {
92 --disabled_by_db_connection_;
93 }
94 break;
95 default:
96 isc_throw(NotImplemented, "origin value not handled when "
97 "enabling the network state");
98 break;
99 }
100 // Enable the service only if all flags have been cleared.
101 if (!disabled_by_user_command_ && !disabled_by_ha_command_ &&
102 disabled_by_db_connection_ == 0) {
103 globally_disabled_ = false;
104 }
105 }
106 }
107
108 /// @brief Reset internal counters for a specific origin.
109 ///
110 /// @note The dhcp service will remain disabled until all flags are cleared.
111 ///
112 /// @param origin The origin of the state transition.
reset(const NetworkState::Origin & origin)113 void reset(const NetworkState::Origin& origin) {
114 switch (origin) {
115 case NetworkState::Origin::USER_COMMAND:
116 disabled_by_user_command_ = false;
117 break;
118 case NetworkState::Origin::HA_COMMAND:
119 disabled_by_ha_command_ = false;
120 break;
121 case NetworkState::Origin::DB_CONNECTION:
122 disabled_by_db_connection_ = 0;
123 break;
124 default:
125 isc_throw(NotImplemented, "origin value not handled when "
126 "resetting the network state");
127 break;
128 }
129 // Enable the service only if all flags have been cleared.
130 if (!disabled_by_user_command_ && !disabled_by_ha_command_ &&
131 disabled_by_db_connection_ == 0) {
132 globally_disabled_ = false;
133 }
134 }
135
136 /// @brief Enables DHCP service globally and per scopes.
137 ///
138 /// If delayed enabling DHCP service has been scheduled, it cancels it.
139 ///
140 /// @param origin The origin of the state transition.
enableAll(const NetworkState::Origin & origin)141 void enableAll(const NetworkState::Origin& origin) {
142 setDisableService(false, origin);
143
144 /// @todo Enable service for all subnets and networks here.
145
146 destroyTimer(origin);
147 }
148
149 /// @brief Creates a timer counting the time when @c enableAll should be
150 /// automatically called.
151 ///
152 /// If the timer has been already scheduled, it is destroyed and replaced
153 /// with a new timer.
154 ///
155 /// @param seconds Number of seconds to elapse before the @c enableAll is
156 /// called.
157 /// @param origin The origin of the state transition.
createTimer(const unsigned int seconds,const NetworkState::Origin & origin)158 void createTimer(const unsigned int seconds,
159 const NetworkState::Origin& origin) {
160 destroyTimer(origin);
161 std::string timer_name = NETWORK_STATE_TIMER_NAME_USER_CMD;
162 switch (origin) {
163 case NetworkState::Origin::USER_COMMAND:
164 timer_name = NETWORK_STATE_TIMER_NAME_USER_CMD;
165 break;
166 case NetworkState::Origin::HA_COMMAND:
167 timer_name = NETWORK_STATE_TIMER_NAME_HA_CMD;
168 break;
169 case NetworkState::Origin::DB_CONNECTION:
170 isc_throw(BadValue, "DB connection does not support delayed enable");
171 break;
172 default:
173 isc_throw(NotImplemented, "origin value not handled when creating "
174 "a timer for delayed enable");
175 break;
176 }
177 timer_mgr_->registerTimer(timer_name,
178 std::bind(&NetworkStateImpl::enableAll,
179 shared_from_this(), origin),
180 seconds * 1000,
181 asiolink::IntervalTimer::ONE_SHOT);
182 timer_mgr_->setup(timer_name);
183 }
184
185 /// @brief Destroys a timer if present.
186 ///
187 /// @param origin The origin of the state transition.
destroyTimer(const NetworkState::Origin & origin)188 void destroyTimer(const NetworkState::Origin& origin) {
189 std::string timer_name = NETWORK_STATE_TIMER_NAME_USER_CMD;
190 switch (origin) {
191 case NetworkState::Origin::USER_COMMAND:
192 timer_name = NETWORK_STATE_TIMER_NAME_USER_CMD;
193 break;
194 case NetworkState::Origin::HA_COMMAND:
195 timer_name = NETWORK_STATE_TIMER_NAME_HA_CMD;
196 break;
197 case NetworkState::Origin::DB_CONNECTION:
198 return;
199 default:
200 isc_throw(NotImplemented, "origin value not handled when creating "
201 "a timer for delayed enable");
202 break;
203 }
204 if (timer_mgr_->isTimerRegistered(timer_name)) {
205 timer_mgr_->unregisterTimer(timer_name);
206 }
207 }
208
209 /// @brief Server type.
210 NetworkState::ServerType server_type_;
211
212 /// @brief A flag indicating if DHCP service is globally disabled.
213 bool globally_disabled_;
214
215 /// @brief A list of subnets for which the DHCP service has been disabled.
216 NetworkState::Subnets disabled_subnets_;
217
218 /// @brief A list of networks for which the DHCP service has been disabled.
219 NetworkState::Networks disabled_networks_;
220
221 /// @brief A pointer to the common timer manager.
222 ///
223 /// This pointer is held here to make sure that the timer manager is not
224 /// destroyed before an instance of this class is destroyed.
225 TimerMgrPtr timer_mgr_;
226
227 /// @brief Flag which indicates the state has been disabled by an user
228 /// command.
229 bool disabled_by_user_command_;
230
231 /// @brief Flag which indicates the state has been disabled by the HA
232 /// command.
233 bool disabled_by_ha_command_;
234
235 /// @brief Flag which indicates the state has been disabled by a DB
236 /// connection loss.
237 uint32_t disabled_by_db_connection_;
238 };
239
NetworkState(const NetworkState::ServerType & server_type)240 NetworkState::NetworkState(const NetworkState::ServerType& server_type)
241 : impl_(new NetworkStateImpl(server_type)), mutex_(new std::mutex()) {
242 }
243
244 void
disableService(const Origin & origin)245 NetworkState::disableService(const Origin& origin) {
246 if (MultiThreadingMgr::instance().getMode()) {
247 std::lock_guard<std::mutex> lk(*mutex_);
248 impl_->setDisableService(true, origin);
249 } else {
250 impl_->setDisableService(true, origin);
251 }
252 }
253
254 void
enableService(const Origin & origin)255 NetworkState::enableService(const Origin& origin) {
256 if (MultiThreadingMgr::instance().getMode()) {
257 std::lock_guard<std::mutex> lk(*mutex_);
258 impl_->setDisableService(false, origin);
259 } else {
260 impl_->setDisableService(false, origin);
261 }
262 }
263
264 void
reset(const NetworkState::Origin & origin)265 NetworkState::reset(const NetworkState::Origin& origin) {
266 if (MultiThreadingMgr::instance().getMode()) {
267 std::lock_guard<std::mutex> lk(*mutex_);
268 impl_->reset(origin);
269 } else {
270 impl_->reset(origin);
271 }
272 }
273
274 void
enableAll(const NetworkState::Origin & origin)275 NetworkState::enableAll(const NetworkState::Origin& origin) {
276 if (MultiThreadingMgr::instance().getMode()) {
277 std::lock_guard<std::mutex> lk(*mutex_);
278 impl_->enableAll(origin);
279 } else {
280 impl_->enableAll(origin);
281 }
282 }
283
284 void
delayedEnableAll(const unsigned int seconds,const NetworkState::Origin & origin)285 NetworkState::delayedEnableAll(const unsigned int seconds,
286 const NetworkState::Origin& origin) {
287 if (MultiThreadingMgr::instance().getMode()) {
288 std::lock_guard<std::mutex> lk(*mutex_);
289 impl_->createTimer(seconds, origin);
290 } else {
291 impl_->createTimer(seconds, origin);
292 }
293 }
294
295 bool
isServiceEnabled() const296 NetworkState::isServiceEnabled() const {
297 if (MultiThreadingMgr::instance().getMode()) {
298 std::lock_guard<std::mutex> lk(*mutex_);
299 return (!impl_->globally_disabled_);
300 } else {
301 return (!impl_->globally_disabled_);
302 }
303 }
304
305 bool
isDelayedEnableAll() const306 NetworkState::isDelayedEnableAll() const {
307 return (TimerMgr::instance()->isTimerRegistered(NETWORK_STATE_TIMER_NAME_USER_CMD) ||
308 TimerMgr::instance()->isTimerRegistered(NETWORK_STATE_TIMER_NAME_HA_CMD));
309 }
310
311 void
selectiveDisable(const NetworkState::Subnets &)312 NetworkState::selectiveDisable(const NetworkState::Subnets&) {
313 isc_throw(NotImplemented, "selectiveDisableService is not implemented");
314 }
315
316 void
selectiveDisable(const NetworkState::Networks &)317 NetworkState::selectiveDisable(const NetworkState::Networks&) {
318 isc_throw(NotImplemented, "selectiveDisableService is not implemented");
319 }
320
321 void
selectiveEnable(const NetworkState::Subnets &)322 NetworkState::selectiveEnable(const NetworkState::Subnets&) {
323 isc_throw(NotImplemented, "selectiveEnableService is not implemented");
324 }
325
326 void
selectiveEnable(const NetworkState::Networks &)327 NetworkState::selectiveEnable(const NetworkState::Networks&) {
328 isc_throw(NotImplemented, "selectiveEnableService is not implemented");
329 }
330
331 } // end of namespace isc::dhcp
332 } // end of namespace isc
333