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> reloadTimer = nullptr;
56 
57 #ifdef SYNC_MAC_FROM_INVENTORY
58 std::unique_ptr<sdbusplus::bus::match_t> EthInterfaceMatch = nullptr;
59 std::vector<std::string> first_boot_status;
60 
61 bool setInventoryMACOnSystem(sdbusplus::bus_t& bus,
62                              const nlohmann::json& configJson,
63                              const std::string& intfname)
64 {
65     try
66     {
67         auto inventoryMAC = mac_address::getfromInventory(bus, intfname);
68         if (inventoryMAC != ether_addr{})
69         {
70             auto macStr = std::to_string(inventoryMAC);
71             log<level::INFO>("Mac Address in Inventory on ",
72                              entry("Interface : ", intfname.c_str()),
73                              entry("MAC Address :", macStr.c_str()));
74             manager->setFistBootMACOnInterface(
75                 std::make_pair(intfname.c_str(), std::move(macStr)));
76             first_boot_status.push_back(intfname.c_str());
77             bool status = true;
78             for (const auto& keys : configJson.items())
79             {
80                 if (!(std::find(first_boot_status.begin(),
81                                 first_boot_status.end(),
82                                 keys.key()) != first_boot_status.end()))
83                 {
84                     log<level::INFO>("Interface MAC is NOT set from VPD"),
85                         entry("INTERFACE", keys.key().c_str());
86                     status = false;
87                 }
88             }
89             if (status)
90             {
91                 log<level::INFO>("Removing the match for ethernet interfaces");
92                 EthInterfaceMatch = nullptr;
93             }
94         }
95         else
96         {
97             log<level::INFO>("Nothing is present in Inventory");
98             return false;
99         }
100     }
101     catch (const std::exception& e)
102     {
103         log<level::ERR>("Exception occurred during getting of MAC "
104                         "address from Inventory");
105         return false;
106     }
107     return true;
108 }
109 
110 // register the macthes to be monitored from inventory manager
111 void registerSignals(sdbusplus::bus_t& bus, const nlohmann::json& configJson)
112 {
113     log<level::INFO>("Registering the Inventory Signals Matcher");
114 
115     static std::unique_ptr<sdbusplus::bus::match_t> MacAddressMatch;
116 
117     auto callback = [&](sdbusplus::message_t& m) {
118         std::map<DbusObjectPath,
119                  std::map<DbusInterface, std::variant<PropertyValue>>>
120             interfacesProperties;
121 
122         sdbusplus::message::object_path objPath;
123         std::pair<std::string, std::string> ethPair;
124         m.read(objPath, interfacesProperties);
125 
126         for (const auto& pattern : configJson.items())
127         {
128             if (objPath.str.find(pattern.value()) != std::string::npos)
129             {
130                 for (auto& interface : interfacesProperties)
131                 {
132                     if (interface.first == invNetworkIntf)
133                     {
134                         for (const auto& property : interface.second)
135                         {
136                             if (property.first == "MACAddress")
137                             {
138                                 ethPair = std::make_pair(
139                                     pattern.key(),
140                                     std::get<std::string>(property.second));
141                                 break;
142                             }
143                         }
144                         break;
145                     }
146                 }
147                 if (!(ethPair.first.empty() || ethPair.second.empty()))
148                 {
149                     manager->setFistBootMACOnInterface(ethPair);
150                 }
151             }
152         }
153     };
154 
155     MacAddressMatch = std::make_unique<sdbusplus::bus::match_t>(
156         bus,
157         "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
158         "member='InterfacesAdded',path='/xyz/openbmc_project/"
159         "inventory'",
160         callback);
161 }
162 
163 void watchEthernetInterface(sdbusplus::bus_t& bus,
164                             const nlohmann::json& configJson)
165 {
166     auto mycallback = [&](sdbusplus::message_t& m) {
167         std::map<DbusObjectPath,
168                  std::map<DbusInterface, std::variant<PropertyValue>>>
169             interfacesProperties;
170 
171         sdbusplus::message::object_path objPath;
172         std::pair<std::string, std::string> ethPair;
173         m.read(objPath, interfacesProperties);
174         for (const auto& interfaces : interfacesProperties)
175         {
176             if (interfaces.first ==
177                 "xyz.openbmc_project.Network.EthernetInterface")
178             {
179                 for (const auto& property : interfaces.second)
180                 {
181                     if (property.first == "InterfaceName")
182                     {
183                         std::string infname =
184                             std::get<std::string>(property.second);
185 
186                         if (configJson.find(infname) == configJson.end())
187                         {
188                             // ethernet interface not found in configJSON
189                             // check if it is not sit0 interface, as it is
190                             // expected.
191                             if (infname != "sit0")
192                             {
193                                 log<level::ERR>(
194                                     "Wrong Interface Name in Config Json");
195                             }
196                         }
197                         else
198                         {
199                             if (!setInventoryMACOnSystem(bus, configJson,
200                                                          infname))
201                             {
202                                 registerSignals(bus, configJson);
203                                 EthInterfaceMatch = nullptr;
204                             }
205                         }
206                         break;
207                     }
208                 }
209                 break;
210             }
211         }
212     };
213     // Incase if phosphor-inventory-manager started early and the VPD is already
214     // collected by the time network service has come up, better to check the
215     // VPD directly and set the MAC Address on the respective Interface.
216 
217     bool registeredSignals = false;
218     for (const auto& interfaceString : configJson.items())
219     {
220         if ((FORCE_SYNC_MAC_FROM_INVENTORY ||
221              !std::filesystem::exists(firstBootPath + interfaceString.key())) &&
222             !registeredSignals)
223         {
224             auto msg = fmt::format("{}, check VPD for MAC",
225                                    (FORCE_SYNC_MAC_FROM_INVENTORY)
226                                        ? "Force sync enabled"
227                                        : "First boot file is not present");
228             log<level::INFO>(msg.c_str());
229             EthInterfaceMatch = std::make_unique<sdbusplus::bus::match_t>(
230                 bus,
231                 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
232                 "member='InterfacesAdded',path='/xyz/openbmc_project/network'",
233                 mycallback);
234             registeredSignals = true;
235         }
236     }
237 }
238 
239 #endif
240 
241 void reloadNetworkd()
242 {
243     if (manager)
244     {
245         log<level::INFO>("Sending networkd reload");
246         manager->doReloadConfigs();
247         log<level::INFO>("Done networkd reload");
248     }
249 }
250 
251 void initializeTimers(sdeventplus::Event& event)
252 {
253     reloadTimer = std::make_unique<Timer>(event, std::bind(reloadNetworkd));
254 }
255 
256 void termCb(sdeventplus::source::Signal& signal, const struct signalfd_siginfo*)
257 {
258     log<level::NOTICE>("Got TERM, exiting");
259     signal.get_event().exit(0);
260 }
261 
262 int main()
263 {
264     auto event = sdeventplus::Event::get_default();
265     stdplus::signal::block(SIGTERM);
266     sdeventplus::source::Signal(event, SIGTERM, termCb).set_floating(true);
267 
268     initializeTimers(event);
269 
270     auto bus = sdbusplus::bus::new_default();
271     // Attach the bus to sd_event to service user requests
272     bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
273 
274     // Add sdbusplus Object Manager for the 'root' path of the network manager.
275     sdbusplus::server::manager_t objManager(bus, DEFAULT_OBJPATH);
276     bus.request_name(DEFAULT_BUSNAME);
277 
278     manager = std::make_unique<Manager>(bus, DEFAULT_OBJPATH, NETWORK_CONF_DIR);
279 
280     // RTNETLINK event handler
281     netlink::Server svr(event, *manager);
282 
283 #ifdef SYNC_MAC_FROM_INVENTORY
284     std::ifstream in(configFile);
285     nlohmann::json configJson;
286     in >> configJson;
287     watchEthernetInterface(bus, configJson);
288 #endif
289 
290     return event.loop();
291 }
292 
293 } // namespace network
294 } // namespace phosphor
295 
296 int main(int /*argc*/, char** /*argv*/)
297 {
298     try
299     {
300         return phosphor::network::main();
301     }
302     catch (const std::exception& e)
303     {
304         fmt::print(stderr, "FAILED: {}", e.what());
305         fflush(stderr);
306         return 1;
307     }
308 }
309