xref: /openbmc/phosphor-networkd/src/network_manager_main.cpp (revision 6a92363e90d14f6d4df41a0a5f25bfe9014fbe7a)
1 #include "config.h"
2 
3 #include "network_manager.hpp"
4 #include "rtnetlink_server.hpp"
5 #include "types.hpp"
6 #ifdef SYNC_MAC_FROM_INVENTORY
7 #include "util.hpp"
8 #endif
9 
10 #include <fmt/format.h>
11 #include <linux/netlink.h>
12 
13 #include <filesystem>
14 #include <fstream>
15 #include <functional>
16 #include <memory>
17 #ifdef SYNC_MAC_FROM_INVENTORY
18 #include <nlohmann/json.hpp>
19 #endif
20 #include <phosphor-logging/elog-errors.hpp>
21 #include <phosphor-logging/log.hpp>
22 #include <sdbusplus/bus.hpp>
23 #include <sdbusplus/bus/match.hpp>
24 #include <sdbusplus/server/manager.hpp>
25 #include <sdeventplus/event.hpp>
26 #include <sdeventplus/source/signal.hpp>
27 #include <stdplus/signal.hpp>
28 #include <xyz/openbmc_project/Common/error.hpp>
29 
30 using phosphor::logging::elog;
31 using phosphor::logging::entry;
32 using phosphor::logging::level;
33 using phosphor::logging::log;
34 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
35 using DbusObjectPath = std::string;
36 using DbusInterface = std::string;
37 using PropertyValue = std::string;
38 
39 constexpr char NETWORK_CONF_DIR[] = "/etc/systemd/network";
40 
41 constexpr char DEFAULT_OBJPATH[] = "/xyz/openbmc_project/network";
42 
43 constexpr auto firstBootPath = "/var/lib/network/firstBoot_";
44 constexpr auto configFile = "/usr/share/network/config.json";
45 
46 constexpr auto invNetworkIntf =
47     "xyz.openbmc_project.Inventory.Item.NetworkInterface";
48 
49 namespace phosphor
50 {
51 namespace network
52 {
53 
54 std::unique_ptr<Manager> manager = nullptr;
55 std::unique_ptr<Timer> refreshObjectTimer = nullptr;
56 std::unique_ptr<Timer> reloadTimer = nullptr;
57 
58 #ifdef SYNC_MAC_FROM_INVENTORY
59 std::unique_ptr<sdbusplus::bus::match_t> EthInterfaceMatch = nullptr;
60 std::vector<std::string> first_boot_status;
61 
62 bool setInventoryMACOnSystem(sdbusplus::bus_t& bus,
63                              const nlohmann::json& configJson,
64                              const std::string& intfname)
65 {
66     try
67     {
68         auto inventoryMAC = mac_address::getfromInventory(bus, intfname);
69         if (inventoryMAC != ether_addr{})
70         {
71             auto macStr = std::to_string(inventoryMAC);
72             log<level::INFO>("Mac Address in Inventory on ",
73                              entry("Interface : ", intfname.c_str()),
74                              entry("MAC Address :", macStr.c_str()));
75             manager->setFistBootMACOnInterface(
76                 std::make_pair(intfname.c_str(), std::move(macStr)));
77             first_boot_status.push_back(intfname.c_str());
78             bool status = true;
79             for (const auto& keys : configJson.items())
80             {
81                 if (!(std::find(first_boot_status.begin(),
82                                 first_boot_status.end(),
83                                 keys.key()) != first_boot_status.end()))
84                 {
85                     log<level::INFO>("Interface MAC is NOT set from VPD"),
86                         entry("INTERFACE", keys.key().c_str());
87                     status = false;
88                 }
89             }
90             if (status)
91             {
92                 log<level::INFO>("Removing the match for ethernet interfaces");
93                 EthInterfaceMatch = nullptr;
94             }
95         }
96         else
97         {
98             log<level::INFO>("Nothing is present in Inventory");
99             return false;
100         }
101     }
102     catch (const std::exception& e)
103     {
104         log<level::ERR>("Exception occurred during getting of MAC "
105                         "address from Inventory");
106         return false;
107     }
108     return true;
109 }
110 
111 // register the macthes to be monitored from inventory manager
112 void registerSignals(sdbusplus::bus_t& bus, const nlohmann::json& configJson)
113 {
114     log<level::INFO>("Registering the Inventory Signals Matcher");
115 
116     static std::unique_ptr<sdbusplus::bus::match_t> MacAddressMatch;
117 
118     auto callback = [&](sdbusplus::message_t& m) {
119         std::map<DbusObjectPath,
120                  std::map<DbusInterface, std::variant<PropertyValue>>>
121             interfacesProperties;
122 
123         sdbusplus::message::object_path objPath;
124         std::pair<std::string, std::string> ethPair;
125         m.read(objPath, interfacesProperties);
126 
127         for (const auto& pattern : configJson.items())
128         {
129             if (objPath.str.find(pattern.value()) != std::string::npos)
130             {
131                 for (auto& interface : interfacesProperties)
132                 {
133                     if (interface.first == invNetworkIntf)
134                     {
135                         for (const auto& property : interface.second)
136                         {
137                             if (property.first == "MACAddress")
138                             {
139                                 ethPair = std::make_pair(
140                                     pattern.key(),
141                                     std::get<std::string>(property.second));
142                                 break;
143                             }
144                         }
145                         break;
146                     }
147                 }
148                 if (!(ethPair.first.empty() || ethPair.second.empty()))
149                 {
150                     manager->setFistBootMACOnInterface(ethPair);
151                 }
152             }
153         }
154     };
155 
156     MacAddressMatch = std::make_unique<sdbusplus::bus::match_t>(
157         bus,
158         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
159         "member='InterfacesAdded',path='/xyz/openbmc_project/"
160         "inventory'",
161         callback);
162 }
163 
164 void watchEthernetInterface(sdbusplus::bus_t& bus,
165                             const nlohmann::json& configJson)
166 {
167     auto mycallback = [&](sdbusplus::message_t& m) {
168         std::map<DbusObjectPath,
169                  std::map<DbusInterface, std::variant<PropertyValue>>>
170             interfacesProperties;
171 
172         sdbusplus::message::object_path objPath;
173         std::pair<std::string, std::string> ethPair;
174         m.read(objPath, interfacesProperties);
175         for (const auto& interfaces : interfacesProperties)
176         {
177             if (interfaces.first ==
178                 "xyz.openbmc_project.Network.EthernetInterface")
179             {
180                 for (const auto& property : interfaces.second)
181                 {
182                     if (property.first == "InterfaceName")
183                     {
184                         std::string infname =
185                             std::get<std::string>(property.second);
186 
187                         if (configJson.find(infname) == configJson.end())
188                         {
189                             // ethernet interface not found in configJSON
190                             // check if it is not sit0 interface, as it is
191                             // expected.
192                             if (infname != "sit0")
193                             {
194                                 log<level::ERR>(
195                                     "Wrong Interface Name in Config Json");
196                             }
197                         }
198                         else
199                         {
200                             if (!setInventoryMACOnSystem(bus, configJson,
201                                                          infname))
202                             {
203                                 registerSignals(bus, configJson);
204                                 EthInterfaceMatch = nullptr;
205                             }
206                         }
207                         break;
208                     }
209                 }
210                 break;
211             }
212         }
213     };
214     // Incase if phosphor-inventory-manager started early and the VPD is already
215     // collected by the time network service has come up, better to check the
216     // VPD directly and set the MAC Address on the respective Interface.
217 
218     bool registeredSignals = false;
219     for (const auto& interfaceString : configJson.items())
220     {
221         if ((FORCE_SYNC_MAC_FROM_INVENTORY ||
222              !std::filesystem::exists(firstBootPath + interfaceString.key())) &&
223             !registeredSignals)
224         {
225             auto msg = fmt::format("{}, check VPD for MAC",
226                                    (FORCE_SYNC_MAC_FROM_INVENTORY)
227                                        ? "Force sync enabled"
228                                        : "First boot file is not present");
229             log<level::INFO>(msg.c_str());
230             EthInterfaceMatch = std::make_unique<sdbusplus::bus::match_t>(
231                 bus,
232                 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
233                 "member='InterfacesAdded',path='/xyz/openbmc_project/network'",
234                 mycallback);
235             registeredSignals = true;
236         }
237     }
238 }
239 
240 #endif
241 
242 /** @brief refresh the network objects. */
243 void refreshObjects()
244 {
245     if (manager)
246     {
247         log<level::INFO>("Refreshing the objects.");
248         manager->createChildObjects();
249         log<level::INFO>("Refreshing complete.");
250     }
251 }
252 
253 void reloadNetworkd()
254 {
255     if (manager)
256     {
257         log<level::INFO>("Sending networkd reload");
258         manager->doReloadConfigs();
259         log<level::INFO>("Done networkd reload");
260     }
261 }
262 
263 void initializeTimers(sdeventplus::Event& event)
264 {
265     refreshObjectTimer =
266         std::make_unique<Timer>(event, std::bind(refreshObjects));
267     reloadTimer = std::make_unique<Timer>(event, std::bind(reloadNetworkd));
268 }
269 
270 void termCb(sdeventplus::source::Signal& signal, const struct signalfd_siginfo*)
271 {
272     log<level::NOTICE>("Got TERM, exiting");
273     signal.get_event().exit(0);
274 }
275 
276 int main()
277 {
278     auto event = sdeventplus::Event::get_default();
279     stdplus::signal::block(SIGTERM);
280     sdeventplus::source::Signal(event, SIGTERM, termCb).set_floating(true);
281 
282     initializeTimers(event);
283 
284     auto bus = sdbusplus::bus::new_default();
285     // Attach the bus to sd_event to service user requests
286     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
287 
288     // Add sdbusplus Object Manager for the 'root' path of the network manager.
289     sdbusplus::server::manager_t objManager(bus, DEFAULT_OBJPATH);
290     bus.request_name(DEFAULT_BUSNAME);
291 
292     manager = std::make_unique<Manager>(bus, DEFAULT_OBJPATH, NETWORK_CONF_DIR);
293 
294     // RTNETLINK event handler
295     netlink::Server svr(event, *manager);
296 
297 #ifdef SYNC_MAC_FROM_INVENTORY
298     std::ifstream in(configFile);
299     nlohmann::json configJson;
300     in >> configJson;
301     watchEthernetInterface(bus, configJson);
302 #endif
303 
304     // Trigger the initial object scan
305     // This is intentionally deferred, to ensure that systemd-networkd is
306     // fully configured.
307     refreshObjectTimer->restartOnce(refreshTimeout);
308 
309     return event.loop();
310 }
311 
312 } // namespace network
313 } // namespace phosphor
314 
315 int main(int /*argc*/, char** /*argv*/)
316 {
317     try
318     {
319         return phosphor::network::main();
320     }
321     catch (const std::exception& e)
322     {
323         auto msg = fmt::format("FAILED: {}", e.what());
324         log<level::ERR>(msg.c_str(), entry("ERROR", e.what()));
325         return 1;
326     }
327 }
328