1 #include "config_parser.hpp"
2 #include "ipaddress.hpp"
3 #include "mock_network_manager.hpp"
4 #include "mock_syscall.hpp"
5 #include "util.hpp"
6 
7 #include <arpa/inet.h>
8 #include <net/if.h>
9 #include <netinet/in.h>
10 #include <stdlib.h>
11 
12 #include <charconv>
13 #include <exception>
14 #include <sdbusplus/bus.hpp>
15 #include <stdplus/gtest/tmp.hpp>
16 #include <string_view>
17 #include <xyz/openbmc_project/Common/error.hpp>
18 
19 #include <gtest/gtest.h>
20 
21 namespace phosphor
22 {
23 namespace network
24 {
25 
26 using std::literals::string_view_literals::operator""sv;
27 
28 class TestEthernetInterface : public stdplus::gtest::TestWithTmp
29 {
30   public:
31     sdbusplus::bus_t bus;
32     std::string confDir;
33     MockManager manager;
34     MockEthernetInterface interface;
35     TestEthernetInterface() :
36         bus(sdbusplus::bus::new_default()), confDir(CaseTmpDir()),
37         manager(bus, "/xyz/openbmc_test/network", confDir),
38         interface(makeInterface(bus, manager))
39 
40     {
41     }
42 
43     static MockEthernetInterface makeInterface(sdbusplus::bus_t& bus,
44                                                MockManager& manager)
45     {
46         mock_clear();
47         mock_addIF("test0", /*idx=*/1);
48         return {bus, "/xyz/openbmc_test/network/test0", config::Parser(),
49                 manager};
50     }
51 
52     int countIPObjects()
53     {
54         return interface.getAddresses().size();
55     }
56 
57     bool isIPObjectExist(const std::string& ipaddress)
58     {
59         auto address = interface.getAddresses().find(ipaddress);
60         if (address == interface.getAddresses().end())
61         {
62             return false;
63         }
64         return true;
65     }
66 
67     bool deleteIPObject(const std::string& ipaddress)
68     {
69         auto address = interface.getAddresses().find(ipaddress);
70         if (address == interface.getAddresses().end())
71         {
72             return false;
73         }
74         address->second->delete_();
75         return true;
76     }
77 
78     std::string getObjectPath(const std::string& ipaddress, uint8_t subnetMask,
79                               IP::AddressOrigin origin)
80     {
81         IP::Protocol addressType = IP::Protocol::IPv4;
82 
83         return interface.generateObjectPath(addressType, ipaddress, subnetMask,
84                                             origin);
85     }
86 
87     void createIPObject(IP::Protocol addressType, const std::string& ipaddress,
88                         uint8_t subnetMask)
89     {
90         interface.ip(addressType, ipaddress, subnetMask, "");
91     }
92 
93     void setNtpServers()
94     {
95         ServerList ntpServers = {"10.1.1.1", "10.2.2.2", "10.3.3.3"};
96         interface.EthernetInterfaceIntf::ntpServers(ntpServers);
97     }
98 
99     ServerList getNtpServers()
100     {
101         return interface.EthernetInterfaceIntf::ntpServers();
102     }
103 };
104 
105 TEST_F(TestEthernetInterface, Fields)
106 {
107     EXPECT_EQ(0, interface.mtu());
108     EXPECT_EQ("", interface.macAddress());
109     EXPECT_FALSE(interface.linkUp());
110 
111     constexpr unsigned idx = 2;
112     constexpr ether_addr mac{0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
113     constexpr unsigned mtu = 150;
114 
115     mock_addIF("test1", idx, IFF_RUNNING, mac, mtu);
116     MockEthernetInterface intf(bus, "/xyz/openbmc_test/network/test1",
117                                config::Parser(), manager);
118 
119     EXPECT_EQ(mtu, intf.mtu());
120     EXPECT_EQ(mac_address::toString(mac), intf.macAddress());
121     EXPECT_TRUE(intf.linkUp());
122 }
123 
124 TEST_F(TestEthernetInterface, NoIPaddress)
125 {
126     EXPECT_EQ(countIPObjects(), 0);
127 }
128 
129 TEST_F(TestEthernetInterface, AddIPAddress)
130 {
131     IP::Protocol addressType = IP::Protocol::IPv4;
132     createIPObject(addressType, "10.10.10.10", 16);
133     EXPECT_EQ(true, isIPObjectExist("10.10.10.10"));
134 }
135 
136 TEST_F(TestEthernetInterface, AddMultipleAddress)
137 {
138     IP::Protocol addressType = IP::Protocol::IPv4;
139     createIPObject(addressType, "10.10.10.10", 16);
140     createIPObject(addressType, "20.20.20.20", 16);
141     EXPECT_EQ(true, isIPObjectExist("10.10.10.10"));
142     EXPECT_EQ(true, isIPObjectExist("20.20.20.20"));
143 }
144 
145 TEST_F(TestEthernetInterface, DeleteIPAddress)
146 {
147     IP::Protocol addressType = IP::Protocol::IPv4;
148     createIPObject(addressType, "10.10.10.10", 16);
149     createIPObject(addressType, "20.20.20.20", 16);
150     deleteIPObject("10.10.10.10");
151     EXPECT_EQ(false, isIPObjectExist("10.10.10.10"));
152     EXPECT_EQ(true, isIPObjectExist("20.20.20.20"));
153 }
154 
155 TEST_F(TestEthernetInterface, DeleteInvalidIPAddress)
156 {
157     EXPECT_EQ(false, deleteIPObject("10.10.10.10"));
158 }
159 
160 TEST_F(TestEthernetInterface, CheckObjectPath)
161 {
162     std::string ipaddress = "10.10.10.10";
163     uint8_t prefix = 16;
164     IP::AddressOrigin origin = IP::AddressOrigin::Static;
165 
166     auto path = getObjectPath(ipaddress, prefix, origin);
167     auto pathsv = std::string_view(path);
168     constexpr auto expectedPrefix = "/xyz/openbmc_test/network/test0/ipv4/"sv;
169     EXPECT_TRUE(pathsv.starts_with(expectedPrefix));
170     pathsv.remove_prefix(expectedPrefix.size());
171     uint32_t val;
172     auto [ptr, res] = std::from_chars(pathsv.begin(), pathsv.end(), val, 16);
173     EXPECT_EQ(res, std::errc());
174     EXPECT_EQ(ptr, pathsv.end());
175 
176     EXPECT_EQ(path, getObjectPath(ipaddress, prefix, origin));
177     origin = IP::AddressOrigin::DHCP;
178     EXPECT_NE(path, getObjectPath(ipaddress, prefix, origin));
179 }
180 
181 TEST_F(TestEthernetInterface, addStaticNameServers)
182 {
183     ServerList servers = {"9.1.1.1", "9.2.2.2", "9.3.3.3"};
184     EXPECT_CALL(manager, reloadConfigs());
185     interface.staticNameServers(servers);
186     fs::path filePath = confDir;
187     filePath /= "00-bmc-test0.network";
188     config::Parser parser(filePath.string());
189     EXPECT_EQ(servers, parser.map.getValueStrings("Network", "DNS"));
190 }
191 
192 TEST_F(TestEthernetInterface, getDynamicNameServers)
193 {
194     ServerList servers = {"9.1.1.1", "9.2.2.2", "9.3.3.3"};
195     EXPECT_CALL(interface, getNameServerFromResolvd())
196         .WillRepeatedly(testing::Return(servers));
197     EXPECT_EQ(interface.getNameServerFromResolvd(), servers);
198 }
199 
200 TEST_F(TestEthernetInterface, addStaticNTPServers)
201 {
202     ServerList servers = {"10.1.1.1", "10.2.2.2", "10.3.3.3"};
203     EXPECT_CALL(manager, reloadConfigs());
204     interface.staticNTPServers(servers);
205     fs::path filePath = confDir;
206     filePath /= "00-bmc-test0.network";
207     config::Parser parser(filePath.string());
208     EXPECT_EQ(servers, parser.map.getValueStrings("Network", "NTP"));
209 }
210 
211 TEST_F(TestEthernetInterface, addNTPServers)
212 {
213     using namespace sdbusplus::xyz::openbmc_project::Common::Error;
214     ServerList servers = {"10.1.1.1", "10.2.2.2", "10.3.3.3"};
215     EXPECT_THROW(interface.ntpServers(servers), NotAllowed);
216 }
217 
218 TEST_F(TestEthernetInterface, getNTPServers)
219 {
220     ServerList servers = {"10.1.1.1", "10.2.2.2", "10.3.3.3"};
221     setNtpServers();
222     EXPECT_EQ(getNtpServers(), servers);
223 }
224 
225 TEST_F(TestEthernetInterface, addGateway)
226 {
227     std::string gateway = "10.3.3.3";
228     interface.defaultGateway(gateway);
229     EXPECT_EQ(interface.defaultGateway(), gateway);
230     interface.defaultGateway("");
231     EXPECT_EQ(interface.defaultGateway(), "");
232 }
233 
234 TEST_F(TestEthernetInterface, addGateway6)
235 {
236     std::string gateway6 = "ffff:ffff:ffff:fe80::1";
237     interface.defaultGateway6(gateway6);
238     EXPECT_EQ(interface.defaultGateway6(), gateway6);
239     interface.defaultGateway6("");
240     EXPECT_EQ(interface.defaultGateway6(), "");
241 }
242 
243 TEST_F(TestEthernetInterface, DHCPEnabled)
244 {
245     EXPECT_CALL(manager, reloadConfigs()).WillRepeatedly(testing::Return());
246 
247     using DHCPConf = EthernetInterfaceIntf::DHCPConf;
248     auto test = [&](DHCPConf conf, bool dhcp4, bool dhcp6, bool ra) {
249         EXPECT_EQ(conf, interface.dhcpEnabled());
250         EXPECT_EQ(dhcp4, interface.dhcp4());
251         EXPECT_EQ(dhcp6, interface.dhcp6());
252         EXPECT_EQ(ra, interface.ipv6AcceptRA());
253     };
254     test(DHCPConf::both, /*dhcp4=*/true, /*dhcp6=*/true, /*ra=*/true);
255 
256     auto set_test = [&](DHCPConf conf, bool dhcp4, bool dhcp6, bool ra) {
257         EXPECT_EQ(conf, interface.dhcpEnabled(conf));
258         test(conf, dhcp4, dhcp6, ra);
259     };
260     set_test(DHCPConf::none, /*dhcp4=*/false, /*dhcp6=*/false, /*ra=*/false);
261     set_test(DHCPConf::v4, /*dhcp4=*/true, /*dhcp6=*/false, /*ra=*/false);
262     set_test(DHCPConf::v6stateless, /*dhcp4=*/false, /*dhcp6=*/false,
263              /*ra=*/true);
264     set_test(DHCPConf::v6, /*dhcp4=*/false, /*dhcp6=*/true, /*ra=*/true);
265     set_test(DHCPConf::v4v6stateless, /*dhcp4=*/true, /*dhcp6=*/false,
266              /*ra=*/true);
267     set_test(DHCPConf::both, /*dhcp4=*/true, /*dhcp6=*/true, /*ra=*/true);
268 
269     auto ind_test = [&](DHCPConf conf, bool dhcp4, bool dhcp6, bool ra) {
270         EXPECT_EQ(dhcp4, interface.dhcp4(dhcp4));
271         EXPECT_EQ(dhcp6, interface.dhcp6(dhcp6));
272         EXPECT_EQ(ra, interface.ipv6AcceptRA(ra));
273         test(conf, dhcp4, dhcp6, ra);
274     };
275     ind_test(DHCPConf::none, /*dhcp4=*/false, /*dhcp6=*/false, /*ra=*/false);
276     ind_test(DHCPConf::v4, /*dhcp4=*/true, /*dhcp6=*/false, /*ra=*/false);
277     ind_test(DHCPConf::v6stateless, /*dhcp4=*/false, /*dhcp6=*/false,
278              /*ra=*/true);
279     ind_test(DHCPConf::v6, /*dhcp4=*/false, /*dhcp6=*/true, /*ra=*/false);
280     set_test(DHCPConf::v6, /*dhcp4=*/false, /*dhcp6=*/true, /*ra=*/true);
281     ind_test(DHCPConf::v4v6stateless, /*dhcp4=*/true, /*dhcp6=*/false,
282              /*ra=*/true);
283     ind_test(DHCPConf::both, /*dhcp4=*/true, /*dhcp6=*/true, /*ra=*/false);
284     set_test(DHCPConf::both, /*dhcp4=*/true, /*dhcp6=*/true, /*ra=*/true);
285 }
286 
287 } // namespace network
288 } // namespace phosphor
289