1 #include "config_parser.hpp"
2 #include "ipaddress.hpp"
3 #include "mock_ethernet_interface.hpp"
4 #include "test_network_manager.hpp"
5 
6 #include <net/if.h>
7 #include <net/if_arp.h>
8 
9 #include <sdbusplus/bus.hpp>
10 #include <stdplus/gtest/tmp.hpp>
11 #include <xyz/openbmc_project/Common/error.hpp>
12 
13 #include <string_view>
14 
15 #include <gtest/gtest.h>
16 
17 namespace phosphor
18 {
19 namespace network
20 {
21 
22 using sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
23 using std::literals::string_view_literals::operator""sv;
24 using testing::Key;
25 using testing::UnorderedElementsAre;
26 
27 class TestEthernetInterface : public stdplus::gtest::TestWithTmp
28 {
29   public:
30     stdplus::Pinned<sdbusplus::bus_t> bus;
31     std::filesystem::path confDir;
32     TestManager manager;
33     MockEthernetInterface interface;
34     TestEthernetInterface() :
35         bus(sdbusplus::bus::new_default()), confDir(CaseTmpDir()),
36         manager(bus, "/xyz/openbmc_test/network", confDir),
37         interface(makeInterface(bus, manager))
38 
39     {}
40 
41     static MockEthernetInterface
42         makeInterface(stdplus::PinnedRef<sdbusplus::bus_t> bus,
43                       TestManager& manager)
44     {
45         AllIntfInfo info{InterfaceInfo{
46             .type = ARPHRD_ETHER, .idx = 1, .flags = 0, .name = "test0"}};
47         return {bus, manager, info, "/xyz/openbmc_test/network"sv,
48                 config::Parser()};
49     }
50 
51     auto createIPObject(IP::Protocol addressType, const std::string& ipaddress,
52                         uint8_t subnetMask)
53     {
54         return interface.ip(addressType, ipaddress, subnetMask, "");
55     }
56 
57     void setNtpServers()
58     {
59         ServerList ntpServers = {"10.1.1.1", "10.2.2.2", "10.3.3.3"};
60         interface.EthernetInterfaceIntf::ntpServers(ntpServers);
61     }
62 
63     ServerList getNtpServers()
64     {
65         return interface.EthernetInterfaceIntf::ntpServers();
66     }
67 };
68 
69 TEST_F(TestEthernetInterface, Fields)
70 {
71     EXPECT_EQ(0, interface.mtu());
72     EXPECT_EQ("", interface.macAddress());
73     EXPECT_FALSE(interface.linkUp());
74 
75     constexpr stdplus::EtherAddr mac{0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
76     constexpr unsigned mtu = 150;
77 
78     AllIntfInfo info{InterfaceInfo{.type = ARPHRD_ETHER,
79                                    .idx = 2,
80                                    .flags = IFF_RUNNING,
81                                    .name = "test1",
82                                    .mac = mac,
83                                    .mtu = mtu}};
84     MockEthernetInterface intf(bus, manager, info,
85                                "/xyz/openbmc_test/network"sv, config::Parser());
86 
87     EXPECT_EQ(mtu, intf.mtu());
88     EXPECT_EQ(stdplus::toStr(mac), intf.macAddress());
89     EXPECT_TRUE(intf.linkUp());
90 }
91 
92 TEST_F(TestEthernetInterface, NoIPaddress)
93 {
94     EXPECT_TRUE(interface.addrs.empty());
95 }
96 
97 TEST_F(TestEthernetInterface, AddIPAddress)
98 {
99     createIPObject(IP::Protocol::IPv4, "10.10.10.10", 16);
100     EXPECT_THAT(interface.addrs, UnorderedElementsAre(Key(stdplus::SubnetAny(
101                                      stdplus::In4Addr{10, 10, 10, 10}, 16))));
102 }
103 
104 TEST_F(TestEthernetInterface, AddMultipleAddress)
105 {
106     createIPObject(IP::Protocol::IPv4, "10.10.10.10", 16);
107     createIPObject(IP::Protocol::IPv4, "20.20.20.20", 16);
108     EXPECT_THAT(
109         interface.addrs,
110         UnorderedElementsAre(
111             Key(stdplus::SubnetAny(stdplus::In4Addr{10, 10, 10, 10}, 16)),
112             Key(stdplus::SubnetAny(stdplus::In4Addr{20, 20, 20, 20}, 16))));
113 }
114 
115 TEST_F(TestEthernetInterface, DeleteIPAddress)
116 {
117     createIPObject(IP::Protocol::IPv4, "10.10.10.10", 16);
118     createIPObject(IP::Protocol::IPv4, "20.20.20.20", 16);
119     interface.addrs
120         .at(stdplus::SubnetAny(stdplus::In4Addr{10, 10, 10, 10}, 16))
121         ->delete_();
122     EXPECT_THAT(interface.addrs, UnorderedElementsAre(Key(stdplus::SubnetAny(
123                                      stdplus::In4Addr{20, 20, 20, 20}, 16))));
124 }
125 
126 TEST_F(TestEthernetInterface, CheckObjectPath)
127 {
128     auto path = createIPObject(IP::Protocol::IPv4, "10.10.10.10", 16);
129     EXPECT_EQ(path.parent_path(), "/xyz/openbmc_test/network/test0");
130     EXPECT_EQ(path.filename(), "10.10.10.10/16");
131 }
132 
133 TEST_F(TestEthernetInterface, addStaticNameServers)
134 {
135     ServerList servers = {"9.1.1.1", "9.2.2.2", "9.3.3.3"};
136     EXPECT_CALL(manager.mockReload, schedule());
137     interface.staticNameServers(servers);
138     config::Parser parser((confDir / "00-bmc-test0.network").native());
139     EXPECT_EQ(servers, parser.map.getValueStrings("Network", "DNS"));
140 }
141 
142 TEST_F(TestEthernetInterface, getDynamicNameServers)
143 {
144     ServerList servers = {"9.1.1.1", "9.2.2.2", "9.3.3.3"};
145     EXPECT_CALL(interface, getNameServerFromResolvd())
146         .WillRepeatedly(testing::Return(servers));
147     EXPECT_EQ(interface.getNameServerFromResolvd(), servers);
148 }
149 
150 TEST_F(TestEthernetInterface, addStaticNTPServers)
151 {
152     ServerList servers = {"10.1.1.1", "10.2.2.2", "10.3.3.3"};
153     EXPECT_CALL(manager.mockReload, schedule());
154     interface.staticNTPServers(servers);
155     config::Parser parser((confDir / "00-bmc-test0.network").native());
156     EXPECT_EQ(servers, parser.map.getValueStrings("Network", "NTP"));
157 }
158 
159 TEST_F(TestEthernetInterface, addNTPServers)
160 {
161     using namespace sdbusplus::xyz::openbmc_project::Common::Error;
162     ServerList servers = {"10.1.1.1", "10.2.2.2", "10.3.3.3"};
163     EXPECT_THROW(interface.ntpServers(servers), NotAllowed);
164 }
165 
166 TEST_F(TestEthernetInterface, getNTPServers)
167 {
168     ServerList servers = {"10.1.1.1", "10.2.2.2", "10.3.3.3"};
169     setNtpServers();
170     EXPECT_EQ(getNtpServers(), servers);
171 }
172 
173 TEST_F(TestEthernetInterface, addGateway)
174 {
175     std::string gateway = "10.3.3.3";
176     interface.defaultGateway(gateway);
177     EXPECT_EQ(interface.defaultGateway(), gateway);
178     EXPECT_THROW(interface.defaultGateway6("127.0.0.10"), InvalidArgument);
179     EXPECT_THROW(interface.defaultGateway6("0.0.0.0"), InvalidArgument);
180     EXPECT_THROW(interface.defaultGateway6("224.1.0.0"), InvalidArgument);
181     EXPECT_EQ(interface.defaultGateway(), gateway);
182     interface.defaultGateway("");
183     EXPECT_EQ(interface.defaultGateway(), "");
184     interface.defaultGateway("0.0.0.0");
185     EXPECT_EQ(interface.defaultGateway(), "");
186 }
187 
188 TEST_F(TestEthernetInterface, addGateway6)
189 {
190     std::string gateway6 = "fe80::1";
191     interface.defaultGateway6(gateway6);
192     EXPECT_EQ(interface.defaultGateway6(), gateway6);
193     EXPECT_THROW(interface.defaultGateway6("::1"), InvalidArgument);
194     EXPECT_EQ(interface.defaultGateway6(), gateway6);
195     interface.defaultGateway6("");
196     EXPECT_EQ(interface.defaultGateway6(), "");
197     interface.defaultGateway6("::");
198     EXPECT_EQ(interface.defaultGateway6(), "");
199 }
200 
201 TEST_F(TestEthernetInterface, DHCPEnabled)
202 {
203     EXPECT_CALL(manager.mockReload, schedule())
204         .WillRepeatedly(testing::Return());
205 
206     using DHCPConf = EthernetInterfaceIntf::DHCPConf;
207     auto test = [&](DHCPConf conf, bool dhcp4, bool dhcp6, bool ra) {
208         EXPECT_EQ(conf, interface.dhcpEnabled());
209         EXPECT_EQ(dhcp4, interface.dhcp4());
210         EXPECT_EQ(dhcp6, interface.dhcp6());
211         EXPECT_EQ(ra, interface.ipv6AcceptRA());
212     };
213     test(DHCPConf::both, /*dhcp4=*/true, /*dhcp6=*/true, /*ra=*/true);
214 
215     auto set_test = [&](DHCPConf conf, bool dhcp4, bool dhcp6, bool ra) {
216         EXPECT_EQ(conf, interface.dhcpEnabled(conf));
217         test(conf, dhcp4, dhcp6, ra);
218     };
219     set_test(DHCPConf::none, /*dhcp4=*/false, /*dhcp6=*/false, /*ra=*/false);
220     set_test(DHCPConf::v4, /*dhcp4=*/true, /*dhcp6=*/false, /*ra=*/false);
221     set_test(DHCPConf::v6stateless, /*dhcp4=*/false, /*dhcp6=*/false,
222              /*ra=*/true);
223     set_test(DHCPConf::v6, /*dhcp4=*/false, /*dhcp6=*/true, /*ra=*/true);
224     set_test(DHCPConf::v4v6stateless, /*dhcp4=*/true, /*dhcp6=*/false,
225              /*ra=*/true);
226     set_test(DHCPConf::both, /*dhcp4=*/true, /*dhcp6=*/true, /*ra=*/true);
227 
228     auto ind_test = [&](DHCPConf conf, bool dhcp4, bool dhcp6, bool ra) {
229         EXPECT_EQ(dhcp4, interface.dhcp4(dhcp4));
230         EXPECT_EQ(dhcp6, interface.dhcp6(dhcp6));
231         EXPECT_EQ(ra, interface.ipv6AcceptRA(ra));
232         test(conf, dhcp4, dhcp6, ra);
233     };
234     ind_test(DHCPConf::none, /*dhcp4=*/false, /*dhcp6=*/false, /*ra=*/false);
235     ind_test(DHCPConf::v4, /*dhcp4=*/true, /*dhcp6=*/false, /*ra=*/false);
236     ind_test(DHCPConf::v6stateless, /*dhcp4=*/false, /*dhcp6=*/false,
237              /*ra=*/true);
238     ind_test(DHCPConf::v6, /*dhcp4=*/false, /*dhcp6=*/true, /*ra=*/false);
239     set_test(DHCPConf::v6, /*dhcp4=*/false, /*dhcp6=*/true, /*ra=*/true);
240     ind_test(DHCPConf::v4v6stateless, /*dhcp4=*/true, /*dhcp6=*/false,
241              /*ra=*/true);
242     ind_test(DHCPConf::both, /*dhcp4=*/true, /*dhcp6=*/true, /*ra=*/false);
243     set_test(DHCPConf::both, /*dhcp4=*/true, /*dhcp6=*/true, /*ra=*/true);
244 }
245 
246 } // namespace network
247 } // namespace phosphor
248