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