xref: /openbmc/phosphor-networkd/src/network_manager.cpp (revision 4b604171f5bbcfc97db33908f46cfe30cf30e95d)
1 #include "config.h"
2 
3 #include "network_manager.hpp"
4 
5 #include "ipaddress.hpp"
6 #include "network_config.hpp"
7 #include "types.hpp"
8 #include "util.hpp"
9 
10 #include <arpa/inet.h>
11 #include <dirent.h>
12 #include <net/if.h>
13 
14 #include <algorithm>
15 #include <bitset>
16 #include <filesystem>
17 #include <fstream>
18 #include <map>
19 #include <phosphor-logging/elog-errors.hpp>
20 #include <phosphor-logging/log.hpp>
21 #include <string>
22 #include <xyz/openbmc_project/Common/error.hpp>
23 
24 constexpr char SYSTEMD_BUSNAME[] = "org.freedesktop.systemd1";
25 constexpr char SYSTEMD_PATH[] = "/org/freedesktop/systemd1";
26 constexpr char SYSTEMD_INTERFACE[] = "org.freedesktop.systemd1.Manager";
27 constexpr auto FirstBootFile = "/var/lib/network/firstBoot_";
28 
29 constexpr char NETWORKD_BUSNAME[] = "org.freedesktop.network1";
30 constexpr char NETWORKD_PATH[] = "/org/freedesktop/network1";
31 constexpr char NETWORKD_INTERFACE[] = "org.freedesktop.network1.Manager";
32 
33 namespace phosphor
34 {
35 namespace network
36 {
37 
38 extern std::unique_ptr<Timer> refreshObjectTimer;
39 extern std::unique_ptr<Timer> reloadTimer;
40 using namespace phosphor::logging;
41 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
42 using Argument = xyz::openbmc_project::Common::InvalidArgument;
43 
44 Manager::Manager(sdbusplus::bus_t& bus, const char* objPath,
45                  const std::string& path) :
46     details::VLANCreateIface(bus, objPath,
47                              details::VLANCreateIface::action::defer_emit),
48     bus(bus), objectPath(objPath)
49 {
50     fs::path confDir(path);
51     setConfDir(confDir);
52 }
53 
54 bool Manager::createDefaultNetworkFiles()
55 {
56     auto isCreated = false;
57     try
58     {
59         auto interfaceStrList = getInterfaces();
60         for (const auto& interface : interfaceStrList)
61         {
62             // if the interface has '.' in the name, it means that this is a
63             // VLAN - don't create the network file.
64             if (interface.find(".") != std::string::npos)
65             {
66                 continue;
67             }
68 
69             auto fileName = systemd::config::networkFilePrefix + interface +
70                             systemd::config::networkFileSuffix;
71 
72             fs::path filePath = confDir;
73             filePath /= fileName;
74 
75             // create the interface specific network file
76             // if not existing.
77             if (!fs::is_regular_file(filePath.string()))
78             {
79                 bmc::writeDHCPDefault(filePath.string(), interface);
80                 log<level::INFO>("Created the default network file.",
81                                  entry("INTERFACE=%s", interface.c_str()));
82                 isCreated = true;
83             }
84         }
85     }
86     catch (const std::exception& e)
87     {
88         log<level::ERR>("Unable to create the default network file");
89     }
90 
91     return isCreated;
92 }
93 
94 void Manager::setConfDir(const fs::path& dir)
95 {
96     confDir = dir;
97 
98     if (!fs::exists(confDir))
99     {
100         if (!fs::create_directories(confDir))
101         {
102             log<level::ERR>("Unable to create the network conf dir",
103                             entry("DIR=%s", confDir.c_str()));
104             elog<InternalFailure>();
105         }
106     }
107 }
108 
109 void Manager::createInterfaces()
110 {
111     // clear all the interfaces first
112     interfaces.clear();
113 
114     auto interfaceStrList = getInterfaces();
115 
116     for (auto& interface : interfaceStrList)
117     {
118         fs::path objPath = objectPath;
119         auto index = interface.find(".");
120 
121         // interface can be of vlan type or normal ethernet interface.
122         // vlan interface looks like "interface.vlanid",so here by looking
123         // at the interface name we decide that we need
124         // to create the vlaninterface or normal physical interface.
125         if (index != std::string::npos)
126         {
127             // it is vlan interface
128             auto interfaceName = interface.substr(0, index);
129             auto vlanid = interface.substr(index + 1);
130             uint32_t vlanInt = std::stoul(vlanid);
131 
132             interfaces[interfaceName]->loadVLAN(vlanInt);
133             continue;
134         }
135         // normal ethernet interface
136         objPath /= interface;
137 
138         auto dhcp = getDHCPValue(confDir, interface);
139 
140         auto intf = std::make_shared<phosphor::network::EthernetInterface>(
141             bus, objPath.string(), dhcp, *this);
142 
143         intf->createIPAddressObjects();
144         intf->createStaticNeighborObjects();
145         intf->loadNameServers();
146 
147         this->interfaces.emplace(
148             std::make_pair(std::move(interface), std::move(intf)));
149     }
150 }
151 
152 void Manager::createChildObjects()
153 {
154     routeTable.refresh();
155 
156     // creates the ethernet interface dbus object.
157     createInterfaces();
158 
159     systemConf.reset(nullptr);
160     dhcpConf.reset(nullptr);
161 
162     fs::path objPath = objectPath;
163     objPath /= "config";
164 
165     // create the system conf object.
166     systemConf = std::make_unique<phosphor::network::SystemConfiguration>(
167         bus, objPath.string());
168     // create the dhcp conf object.
169     objPath /= "dhcp";
170     dhcpConf = std::make_unique<phosphor::network::dhcp::Configuration>(
171         bus, objPath.string(), *this);
172 }
173 
174 ObjectPath Manager::vlan(IntfName interfaceName, uint32_t id)
175 {
176     if (!hasInterface(interfaceName))
177     {
178         using ResourceErr =
179             phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound;
180         elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str()));
181     }
182 
183     if (id == 0 || id >= 4095)
184     {
185         log<level::ERR>("VLAN ID is not valid", entry("VLANID=%u", id));
186         elog<InvalidArgument>(
187             Argument::ARGUMENT_NAME("VLANId"),
188             Argument::ARGUMENT_VALUE(std::to_string(id).c_str()));
189     }
190 
191     return interfaces[interfaceName]->createVLAN(id);
192 }
193 
194 void Manager::reset()
195 {
196     if (fs::is_directory(confDir))
197     {
198         for (const auto& file : fs::directory_iterator(confDir))
199         {
200             fs::remove(file.path());
201         }
202     }
203     createDefaultNetworkFiles();
204     log<level::INFO>("Network Factory Reset queued.");
205 }
206 
207 // Need to merge the below function with the code which writes the
208 // config file during factory reset.
209 // TODO openbmc/openbmc#1751
210 void Manager::writeToConfigurationFile()
211 {
212     // write all the static ip address in the systemd-network conf file
213     for (const auto& intf : interfaces)
214     {
215         intf.second->writeConfigurationFile();
216     }
217 }
218 
219 #ifdef SYNC_MAC_FROM_INVENTORY
220 void Manager::setFistBootMACOnInterface(
221     const std::pair<std::string, std::string>& inventoryEthPair)
222 {
223     for (const auto& interface : interfaces)
224     {
225         if (interface.first == inventoryEthPair.first)
226         {
227             auto returnMAC =
228                 interface.second->macAddress(inventoryEthPair.second);
229             if (returnMAC == inventoryEthPair.second)
230             {
231                 log<level::INFO>("Set the MAC on "),
232                     entry("interface : ", interface.first.c_str()),
233                     entry("MAC : ", inventoryEthPair.second.c_str());
234                 std::error_code ec;
235                 if (std::filesystem::is_directory("/var/lib/network", ec))
236                 {
237                     std::ofstream persistentFile(FirstBootFile +
238                                                  interface.first);
239                 }
240                 break;
241             }
242             else
243             {
244                 log<level::INFO>("MAC is Not Set on ethernet Interface");
245             }
246         }
247     }
248 }
249 
250 #endif
251 
252 void Manager::reloadConfigs()
253 {
254     reloadTimer->restartOnce(reloadTimeout);
255     // Ensure that the next refresh happens after reconfiguration
256     refreshObjectTimer->setRemaining(reloadTimeout + refreshTimeout);
257 }
258 
259 void Manager::doReloadConfigs()
260 {
261     for (auto& hook : reloadPreHooks)
262     {
263         try
264         {
265             hook();
266         }
267         catch (const std::exception& ex)
268         {
269             log<level::ERR>("Failed executing reload hook, ignoring",
270                             entry("ERR=%s", ex.what()));
271         }
272     }
273     reloadPreHooks.clear();
274     try
275     {
276         auto method = bus.new_method_call(NETWORKD_BUSNAME, NETWORKD_PATH,
277                                           NETWORKD_INTERFACE, "Reload");
278         bus.call_noreply(method);
279     }
280     catch (const sdbusplus::exception_t& ex)
281     {
282         log<level::ERR>("Failed to reload configuration",
283                         entry("ERR=%s", ex.what()));
284         elog<InternalFailure>();
285     }
286     // Ensure reconfiguration has enough time
287     refreshObjectTimer->setRemaining(refreshTimeout);
288 }
289 
290 } // namespace network
291 } // namespace phosphor
292