1 // Copyright (C) 2014-2020 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 #ifndef IFACE_MGR_TEST_CONFIG_H
8 #define IFACE_MGR_TEST_CONFIG_H
9 
10 #include <asiolink/io_address.h>
11 #include <dhcp/iface_mgr.h>
12 #include <boost/noncopyable.hpp>
13 
14 namespace isc {
15 namespace dhcp {
16 namespace test {
17 
18 //@{
19 /// @brief Index of the lo fake interface.
20 const uint32_t LO_INDEX = 0;
21 
22 /// @brief Index of the eth0 fake interface.
23 const uint32_t ETH0_INDEX = 1;
24 
25 /// @brief Index of the eth1 fake interface.
26 const uint32_t ETH1_INDEX = 2;
27 
28 /// @brief Index of the eth1961 fake interface.
29 const uint32_t ETH1961_INDEX = 1962;
30 //@}
31 
32 ///
33 /// @name Set of structures describing interface flags.
34 ///
35 /// These flags encapsulate the boolean type to pass the flags values
36 /// to @c IfaceMgrTestConfig methods. If the values passed to these methods
37 /// were not encapsulated by the types defined here, the API would become
38 /// prone to errors like swapping parameters being passed to specific functions.
39 /// For example, in the call to @c IfaceMgrTestConfig::setIfaceFlags:
40 /// @code
41 ///     IfaceMgrTestConfig test_config(true);
42 ///     test_config.setIfaceFlags("eth1", false, false, true, false, false);
43 /// @endcode
44 ///
45 /// it is quite likely that the developer by mistake swaps the values and
46 /// assigns them to wrong flags. When the flags are encapsulated with dedicated
47 /// structs, the compiler will return an error if values are swapped. For
48 /// example:
49 /// @code
50 ///     IfaceMgrTestConfig test_config(true);
51 ///     test_config.setIfaceFlags("eth1", FlagLoopback(false), FlagUp(false),
52 ///                               FlagRunning(true), FlagInactive4(false),
53 ///                               FlagInactive6(false));
54 /// @endcode
55 /// will succeed, but the following code will result in the compilation error
56 /// and thus protect a developer from making an error:
57 /// @code
58 ///     IfaceMgrTestConfig test_config(true);
59 ///     test_config.setIfaceFlags("eth1", FlagLoopback(false),
60 ///                               FlagRunning(true), FlagUp(false),
61 ///                               FlagInactive4(false), FlagInactive6(false));
62 /// @endcode
63 ///
64 //@{
65 /// @brief Structure describing the loopback interface flag.
66 struct FlagLoopback {
FlagLoopbackFlagLoopback67     explicit FlagLoopback(bool flag) : flag_(flag) { }
68     bool flag_;
69 };
70 
71 /// @brief Structure describing the up interface flag.
72 struct FlagUp {
FlagUpFlagUp73     explicit FlagUp(bool flag) : flag_(flag) { }
74     bool flag_;
75 };
76 
77 /// @brief Structure describing the running interface flag.
78 struct FlagRunning {
FlagRunningFlagRunning79     explicit FlagRunning(bool flag) : flag_(flag) { }
80     bool flag_;
81 };
82 
83 /// @brief Structure describing the inactive4 interface flag.
84 struct FlagInactive4 {
FlagInactive4FlagInactive485     explicit FlagInactive4(bool flag) : flag_(flag) { }
86     bool flag_;
87 };
88 
89 /// @brief Structure describing the inactive6 interface flag.
90 struct FlagInactive6 {
FlagInactive6FlagInactive691     explicit FlagInactive6(bool flag) : flag_(flag) { }
92     bool flag_;
93 };
94 //@}
95 
96 /// @brief Convenience class for configuring @c IfaceMgr for unit testing.
97 ///
98 /// This class is used by various unit tests which test the code relaying
99 /// on IfaceMgr. The use of this class is not limited to libdhcp++ validation.
100 /// There are other libraries and applications (e.g. DHCP servers) which
101 /// depend on @c IfaceMgr.
102 ///
103 /// During the normal operation, the @c IfaceMgr detects interfaces present
104 /// on the machine where it is running. It also provides the means for
105 /// applications to open sockets on these interfaces and perform other
106 /// IO operations. This however creates dependency of the applications
107 /// using @c IfaceMgr on the physical properties of the system and effectively
108 /// makes it very hard to unit test the dependent code.
109 ///
110 /// Unit tests usually require that @c IfaceMgr holds a list of well known
111 /// interfaces with the well known set of IP addresses and other properties
112 /// (a.k.a. interface flags). The solution which works for many test scenarios
113 /// is to provide a set of well known fake interfaces, by bypassing the
114 /// standard interface detection procedure and manually adding @c Iface objects
115 /// which encapsulate the fake interfaces. As a consequence, it becomes
116 /// impossible to test IO operations (e.g. sending packets) because real sockets
117 /// can't be opened on these interfaces. The @c PktFilterTestStub class
118 /// is used by this class to mimic behavior of IO operations on fake sockets.
119 ///
120 /// This class provides a set of convenience functions that should be called
121 /// by unit tests to configure the @c IfaceMgr with fake interfaces.
122 ///
123 /// The class allows the caller to create custom fake interfaces (with custom
124 /// IPv4 and IPv6 addresses, flags etc.), but it also provides a default
125 /// test configuration for interfaces as follows:
126 /// - lo #0
127 ///   - 127.0.0.1
128 ///   - ::1
129 /// - eth0 #1
130 ///   - 10.0.0.1
131 ///   - fe80::3a60:77ff:fed5:cdef
132 ///   - 2001:db8:1::1
133 /// - eth1 #2
134 ///   - 192.0.2.3
135 ///   - fe80::3a60:77ff:fed5:abcd
136 /// - eth1961 #1962
137 ///   - 198.51.100.1
138 ///   - fe80::3a60:77ff:fed5:9876
139 ///
140 /// For all interfaces the following flags are set:
141 /// - multicast
142 /// - up
143 /// - running
144 class IfaceMgrTestConfig : public boost::noncopyable {
145 public:
146 
147     /// @brief Constructor.
148     ///
149     /// It closes all sockets opened by @c IfaceMgr and removes all interfaces
150     /// being used by @c IfaceMgr.
151     IfaceMgrTestConfig(const bool default_config = false);
152 
153     /// @brief Destructor.
154     ///
155     /// Closes all currently opened sockets, removes current interfaces and
156     /// sets the default packet filtering classes. The default packet filtering
157     /// classes are used for IO operations on real sockets/interfaces.
158     /// Receiver is stopped.
159     ///
160     /// Destructor also re-detects real interfaces.
161     ~IfaceMgrTestConfig();
162 
163     /// @brief Adds new IPv4 or IPv6 address to the interface.
164     ///
165     /// @param iface_name Name of the interface on which new address should
166     /// be configured.
167     /// @param address IPv4 or IPv6 address to be configured on the interface.
168     void addAddress(const std::string& iface_name,
169                     const asiolink::IOAddress& address);
170 
171     /// @brief Configures new interface for the @c IfaceMgr.
172     ///
173     /// @param iface Object encapsulating interface to be added.
174     void addIface(const IfacePtr& iface);
175 
176     /// @brief Configures new interface for the @c IfaceMgr.
177     ///
178     /// @param name Name of the new interface.
179     /// @param ifindex Index for a new interface.
180     void addIface(const std::string& name, const int ifindex);
181 
182     /// @brief Create an object representing interface.
183     ///
184     /// Apart from creating an interface, this function also sets the
185     /// interface flags:
186     /// - loopback flag if interface name is "lo"
187     /// - up always true
188     /// - running always true
189     /// - inactive4 set to false for non-loopback interface
190     /// - inactive6 set to false for non-loopback interface
191     /// - multicast always to true
192     /// - broadcast always to false
193     ///
194     /// If one needs to modify the default flag settings, the setIfaceFlags
195     /// function should be used.
196     ///
197     /// @param name A name of the interface to be created.
198     /// @param ifindex An index of the interface to be created.
199     ///
200     /// @return An object representing interface.
201     static IfacePtr createIface(const std::string& name, const int ifindex);
202 
203     /// @brief Creates a default (example) set of fake interfaces.
204     void createIfaces();
205 
206     /// @brief Returns currently used packet filter for DHCPv4.
getPacketFilter4()207     PktFilterPtr getPacketFilter4() const {
208         return (packet_filter4_);
209     }
210 
211     /// @brief Sets the direct response capability for current packet filter.
212     ///
213     /// The test uses stub implementation of packet filter object. It is
214     /// possible to configure that object to report having a capability
215     /// to directly respond to clients which don't have an address yet.
216     /// This function sets this property for packet filter object.
217     ///
218     /// @param direct_resp Value to be set.
219     ///
220     /// @throw isc::Unexpected if unable to set the property.
221     void setDirectResponse(const bool direct_resp);
222 
223     /// @brief Sets various flags on the specified interface.
224     ///
225     /// This function configures interface with new values for flags.
226     ///
227     /// @param name Interface name.
228     /// @param loopback Specifies if interface is a loopback interface.
229     /// @param up Specifies if the interface is up.
230     /// @param running Specifies if the interface is running.
231     /// @param inactive4 Specifies if the interface is inactive for V4
232     /// traffic, i.e. @c IfaceMgr opens V4 sockets on this interface.
233     /// @param inactive6 Specifies if the interface is inactive for V6
234     /// traffic, i.e. @c IfaceMgr opens V6 sockets on this interface.
235     void setIfaceFlags(const std::string& name,
236                        const FlagLoopback& loopback,
237                        const FlagUp& up,
238                        const FlagRunning& running,
239                        const FlagInactive4& inactive4,
240                        const FlagInactive6& inactive6);
241 
242     /// @brief Checks if socket of the specified family is opened on interface.
243     ///
244     /// @param iface_name Interface name.
245     /// @param family One of: AF_INET or AF_INET6
246     bool socketOpen(const std::string& iface_name, const int family) const;
247 
248     /// @brief Checks is socket is opened on the interface and bound to a
249     /// specified address.
250     ///
251     /// @param iface_name Interface name.
252     /// @param address Address to which the socket is bound.
253     bool socketOpen(const std::string& iface_name,
254                     const std::string& address) const;
255 
256     /// @brief Checks if unicast socket is opened on interface.
257     ///
258     /// @param iface_name Interface name.
259     bool unicastOpen(const std::string& iface_name) const;
260 
261 
262 private:
263     /// @brief Currently used packet filter for DHCPv4.
264     PktFilterPtr packet_filter4_;
265 
266     /// @brief Currently used packet filter for DHCPv6.
267     PktFilter6Ptr packet_filter6_;
268 };
269 
270 };
271 };
272 };
273 
274 #endif // IFACE_MGR_TEST_CONFIG_H
275