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 #include <config.h>
8 
9 #include <dhcp/pkt_filter.h>
10 #include <dhcp/pkt_filter_inet.h>
11 #include <dhcp/pkt_filter_inet6.h>
12 #include <dhcp/tests/iface_mgr_test_config.h>
13 #include <dhcp/tests/pkt_filter_test_stub.h>
14 #include <dhcp/tests/pkt_filter6_test_stub.h>
15 
16 #include <boost/foreach.hpp>
17 
18 using namespace isc::asiolink;
19 
20 namespace isc {
21 namespace dhcp {
22 namespace test {
23 
IfaceMgrTestConfig(const bool default_config)24 IfaceMgrTestConfig::IfaceMgrTestConfig(const bool default_config) {
25     IfaceMgr::instance().setTestMode(true);
26     IfaceMgr::instance().closeSockets();
27     IfaceMgr::instance().clearIfaces();
28     IfaceMgr::instance().getPacketQueueMgr4()->destroyPacketQueue();
29     IfaceMgr::instance().getPacketQueueMgr6()->destroyPacketQueue();
30     packet_filter4_ = PktFilterPtr(new PktFilterTestStub());
31     packet_filter6_ = PktFilter6Ptr(new PktFilter6TestStub());
32     IfaceMgr::instance().setPacketFilter(packet_filter4_);
33     IfaceMgr::instance().setPacketFilter(packet_filter6_);
34 
35     // Create default set of fake interfaces: lo, eth0, eth1 and eth1961.
36     if (default_config) {
37         createIfaces();
38     }
39 }
40 
~IfaceMgrTestConfig()41 IfaceMgrTestConfig::~IfaceMgrTestConfig() {
42     IfaceMgr::instance().stopDHCPReceiver();
43     IfaceMgr::instance().closeSockets();
44     IfaceMgr::instance().getPacketQueueMgr4()->destroyPacketQueue();
45     IfaceMgr::instance().getPacketQueueMgr6()->destroyPacketQueue();
46     IfaceMgr::instance().clearIfaces();
47     IfaceMgr::instance().setPacketFilter(PktFilterPtr(new PktFilterInet()));
48     IfaceMgr::instance().setPacketFilter(PktFilter6Ptr(new PktFilterInet6()));
49     IfaceMgr::instance().setTestMode(false);
50     IfaceMgr::instance().detectIfaces();
51 }
52 
53 void
addAddress(const std::string & iface_name,const IOAddress & address)54 IfaceMgrTestConfig::addAddress(const std::string& iface_name,
55                                const IOAddress& address) {
56     IfacePtr iface = IfaceMgr::instance().getIface(iface_name);
57     if (!iface) {
58         isc_throw(isc::BadValue, "interface '" << iface_name
59                   << "' doesn't exist");
60     }
61     iface->addAddress(address);
62 }
63 
64 void
addIface(const IfacePtr & iface)65 IfaceMgrTestConfig::addIface(const IfacePtr& iface) {
66     IfaceMgr::instance().addInterface(iface);
67 }
68 
69 void
addIface(const std::string & name,const int ifindex)70 IfaceMgrTestConfig::addIface(const std::string& name, const int ifindex) {
71     IfaceMgr::instance().addInterface(createIface(name, ifindex));
72 }
73 
74 IfacePtr
createIface(const std::string & name,const int ifindex)75 IfaceMgrTestConfig::createIface(const std::string &name, const int ifindex) {
76     IfacePtr iface(new Iface(name, ifindex));
77     if (name == "lo") {
78         iface->flag_loopback_ = true;
79         // Don't open sockets on the loopback interface.
80         iface->inactive4_ = true;
81         iface->inactive6_ = true;
82     } else {
83         iface->inactive4_ = false;
84         iface->inactive6_ = false;
85     }
86     iface->flag_multicast_ = true;
87     // On BSD systems, the SO_BINDTODEVICE option is not supported.
88     // Therefore the IfaceMgr will throw an exception on attempt to
89     // open sockets on more than one broadcast-capable interface at
90     // the same time. In order to prevent this error, we mark all
91     // interfaces broadcast-incapable for unit testing.
92     iface->flag_broadcast_ = false;
93     iface->flag_up_ = true;
94     iface->flag_running_ = true;
95 
96     // Set MAC address to 08:08:08:08:08:08.
97     std::vector<uint8_t> mac_vec(6, 8);
98     iface->setMac(&mac_vec[0], mac_vec.size());
99     iface->setHWType(HTYPE_ETHER);
100 
101     return (iface);
102 }
103 
104 void
createIfaces()105 IfaceMgrTestConfig::createIfaces() {
106     // local loopback
107     addIface("lo", LO_INDEX);
108     addAddress("lo", IOAddress("127.0.0.1"));
109     addAddress("lo", IOAddress("::1"));
110     // eth0
111     addIface("eth0", ETH0_INDEX);
112     addAddress("eth0", IOAddress("10.0.0.1"));
113     addAddress("eth0", IOAddress("fe80::3a60:77ff:fed5:cdef"));
114     addAddress("eth0", IOAddress("2001:db8:1::1"));
115     // eth1
116     addIface("eth1", ETH1_INDEX);
117     addAddress("eth1", IOAddress("192.0.2.3"));
118     addAddress("eth1", IOAddress("192.0.2.5"));
119     addAddress("eth1", IOAddress("fe80::3a60:77ff:fed5:abcd"));
120     // eth1961
121     addIface("eth1961", ETH1961_INDEX);
122     addAddress("eth1961", IOAddress("198.51.100.1"));
123     addAddress("eth1961", IOAddress("fe80::3a60:77ff:fed5:9876"));
124 
125 }
126 
127 void
setDirectResponse(const bool direct_resp)128 IfaceMgrTestConfig::setDirectResponse(const bool direct_resp) {
129     boost::shared_ptr<PktFilterTestStub> stub =
130         boost::dynamic_pointer_cast<PktFilterTestStub>(getPacketFilter4());
131     if (!stub) {
132         isc_throw(isc::Unexpected, "unable to set direct response capability for"
133                   " test packet filter - current packet filter is not"
134                   " of a PktFilterTestStub");
135     }
136     stub->direct_response_supported_ = direct_resp;
137 }
138 
139 void
setIfaceFlags(const std::string & name,const FlagLoopback & loopback,const FlagUp & up,const FlagRunning & running,const FlagInactive4 & inactive4,const FlagInactive6 & inactive6)140 IfaceMgrTestConfig::setIfaceFlags(const std::string& name,
141                                   const FlagLoopback& loopback,
142                                   const FlagUp& up,
143                                   const FlagRunning& running,
144                                   const FlagInactive4& inactive4,
145                                   const FlagInactive6& inactive6) {
146     IfacePtr iface = IfaceMgr::instance().getIface(name);
147     if (iface == NULL) {
148         isc_throw(isc::BadValue, "interface '" << name << "' doesn't exist");
149     }
150     iface->flag_loopback_ = loopback.flag_;
151     iface->flag_up_ = up.flag_;
152     iface->flag_running_ = running.flag_;
153     iface->inactive4_ = inactive4.flag_;
154     iface->inactive6_ = inactive6.flag_;
155 }
156 
157 bool
socketOpen(const std::string & iface_name,const int family) const158 IfaceMgrTestConfig::socketOpen(const std::string& iface_name,
159                                const int family) const {
160     IfacePtr iface = IfaceMgr::instance().getIface(iface_name);
161     if (iface == NULL) {
162         isc_throw(Unexpected, "No such interface '" << iface_name << "'");
163     }
164 
165     BOOST_FOREACH(SocketInfo sock, iface->getSockets()) {
166         if (sock.family_ == family) {
167             return (true);
168         }
169     }
170     return (false);
171 }
172 
173 bool
socketOpen(const std::string & iface_name,const std::string & address) const174 IfaceMgrTestConfig::socketOpen(const std::string& iface_name,
175                                const std::string& address) const {
176     IfacePtr iface = IfaceMgr::instance().getIface(iface_name);
177     if (!iface) {
178         isc_throw(Unexpected, "No such interface '" << iface_name << "'");
179     }
180 
181     BOOST_FOREACH(SocketInfo sock, iface->getSockets()) {
182         if ((sock.family_ == AF_INET) &&
183             (sock.addr_ == IOAddress(address))) {
184             return (true);
185         }
186     }
187     return (false);
188 }
189 
190 bool
unicastOpen(const std::string & iface_name) const191 IfaceMgrTestConfig::unicastOpen(const std::string& iface_name) const {
192     IfacePtr iface = IfaceMgr::instance().getIface(iface_name);
193     if (!iface) {
194         isc_throw(Unexpected, "No such interface '" << iface_name << "'");
195     }
196 
197     BOOST_FOREACH(SocketInfo sock, iface->getSockets()) {
198         if ((!sock.addr_.isV6LinkLocal()) &&
199             (!sock.addr_.isV6Multicast())) {
200             return (true);
201         }
202     }
203     return (false);
204 }
205 
206 }
207 }
208 }
209