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