blob: 6a60990358257c16795beb230d8315f4aced96f3 [file] [log] [blame]
William A. Kennington III0b111d42022-10-04 18:06:11 -07001#include "config.h"
2
3#include "inventory_mac.hpp"
4
5#include "network_manager.hpp"
6#include "types.hpp"
7
8#include <filesystem>
9#include <fstream>
10#include <memory>
11#include <nlohmann/json.hpp>
12#include <phosphor-logging/elog-errors.hpp>
13#include <phosphor-logging/log.hpp>
14#include <sdbusplus/bus.hpp>
15#include <sdbusplus/bus/match.hpp>
16#include <string>
17#include <vector>
18#include <xyz/openbmc_project/Common/error.hpp>
19
20namespace phosphor::network::inventory
21{
22
23using phosphor::logging::elog;
24using phosphor::logging::entry;
25using phosphor::logging::level;
26using phosphor::logging::log;
27using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
28
29using DbusObjectPath = std::string;
30using DbusInterface = std::string;
31using PropertyValue = std::string;
32using DbusService = std::string;
33using ObjectTree = string_umap<string_umap<std::vector<std::string>>>;
34
35constexpr auto firstBootPath = "/var/lib/network/firstBoot_";
36constexpr auto configFile = "/usr/share/network/config.json";
37
38constexpr auto invNetworkIntf =
39 "xyz.openbmc_project.Inventory.Item.NetworkInterface";
40constexpr auto invRoot = "/xyz/openbmc_project/inventory";
41constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper";
42constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper";
43constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper";
44constexpr auto propIntf = "org.freedesktop.DBus.Properties";
45constexpr auto methodGet = "Get";
46
47Manager* manager = nullptr;
48std::unique_ptr<sdbusplus::bus::match_t> EthInterfaceMatch = nullptr;
49std::vector<std::string> first_boot_status;
50
51void setFirstBootMACOnInterface(const std::string& intf, const std::string& mac)
52{
53 for (const auto& interface : manager->interfaces)
54 {
55 if (interface.first == intf)
56 {
57 auto returnMAC = interface.second->macAddress(mac);
58 if (returnMAC == mac)
59 {
60 log<level::INFO>(fmt::format("Setting MAC on {}", intf).c_str(),
61 entry("INTF=%s", intf.c_str()),
62 entry("MAC=%s", mac.c_str()));
63 std::error_code ec;
64 if (std::filesystem::is_directory("/var/lib/network", ec))
65 {
66 std::ofstream persistentFile(firstBootPath + intf);
67 }
68 break;
69 }
70 else
71 {
72 log<level::INFO>("MAC is Not Set on ethernet Interface");
73 }
74 }
75 }
76}
77
78ether_addr getfromInventory(sdbusplus::bus_t& bus, const std::string& intfName)
79{
80 std::string interfaceName = intfName;
81
82 // load the config JSON from the Read Only Path
83 std::ifstream in(configFile);
84 nlohmann::json configJson;
85 in >> configJson;
86 interfaceName = configJson[intfName];
87
88 std::vector<DbusInterface> interfaces;
89 interfaces.emplace_back(invNetworkIntf);
90
91 auto depth = 0;
92
93 auto mapperCall =
94 bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree");
95
96 mapperCall.append(invRoot, depth, interfaces);
97
98 auto mapperReply = bus.call(mapperCall);
99 if (mapperReply.is_method_error())
100 {
101 log<level::ERR>("Error in mapper call");
102 elog<InternalFailure>();
103 }
104
105 ObjectTree objectTree;
106 mapperReply.read(objectTree);
107
108 if (objectTree.empty())
109 {
110 log<level::ERR>("No Object has implemented the interface",
111 entry("INTERFACE=%s", invNetworkIntf));
112 elog<InternalFailure>();
113 }
114
115 DbusObjectPath objPath;
116 DbusService service;
117
118 if (1 == objectTree.size())
119 {
120 objPath = objectTree.begin()->first;
121 service = objectTree.begin()->second.begin()->first;
122 }
123 else
124 {
125 // If there are more than 2 objects, object path must contain the
126 // interface name
127 for (auto const& object : objectTree)
128 {
129 log<level::INFO>("interface",
130 entry("INT=%s", interfaceName.c_str()));
131 log<level::INFO>("object", entry("OBJ=%s", object.first.c_str()));
132
133 if (std::string::npos != object.first.find(interfaceName.c_str()))
134 {
135 objPath = object.first;
136 service = object.second.begin()->first;
137 break;
138 }
139 }
140
141 if (objPath.empty())
142 {
143 log<level::ERR>("Can't find the object for the interface",
144 entry("intfName=%s", interfaceName.c_str()));
145 elog<InternalFailure>();
146 }
147 }
148
149 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
150 propIntf, methodGet);
151
152 method.append(invNetworkIntf, "MACAddress");
153
154 auto reply = bus.call(method);
155 if (reply.is_method_error())
156 {
157 log<level::ERR>("Failed to get MACAddress",
158 entry("PATH=%s", objPath.c_str()),
159 entry("INTERFACE=%s", invNetworkIntf));
160 elog<InternalFailure>();
161 }
162
163 std::variant<std::string> value;
164 reply.read(value);
165 return ToAddr<ether_addr>{}(std::get<std::string>(value));
166}
167
168bool setInventoryMACOnSystem(sdbusplus::bus_t& bus,
169 const nlohmann::json& configJson,
170 const std::string& intfname)
171{
172 try
173 {
174 auto inventoryMAC = getfromInventory(bus, intfname);
175 if (inventoryMAC != ether_addr{})
176 {
177 auto macStr = std::to_string(inventoryMAC);
178 log<level::INFO>("Mac Address in Inventory on ",
179 entry("Interface : ", intfname.c_str()),
180 entry("MAC Address :", macStr.c_str()));
181 setFirstBootMACOnInterface(intfname, macStr);
182 first_boot_status.push_back(intfname);
183 bool status = true;
184 for (const auto& keys : configJson.items())
185 {
186 if (!(std::find(first_boot_status.begin(),
187 first_boot_status.end(),
188 keys.key()) != first_boot_status.end()))
189 {
190 log<level::INFO>("Interface MAC is NOT set from VPD"),
191 entry("INTERFACE", keys.key().c_str());
192 status = false;
193 }
194 }
195 if (status)
196 {
197 log<level::INFO>("Removing the match for ethernet interfaces");
198 EthInterfaceMatch = nullptr;
199 }
200 }
201 else
202 {
203 log<level::INFO>("Nothing is present in Inventory");
204 return false;
205 }
206 }
207 catch (const std::exception& e)
208 {
209 log<level::ERR>("Exception occurred during getting of MAC "
210 "address from Inventory");
211 return false;
212 }
213 return true;
214}
215
216// register the macthes to be monitored from inventory manager
217void registerSignals(sdbusplus::bus_t& bus, const nlohmann::json& configJson)
218{
219 log<level::INFO>("Registering the Inventory Signals Matcher");
220
221 static std::unique_ptr<sdbusplus::bus::match_t> MacAddressMatch;
222
223 auto callback = [&](sdbusplus::message_t& m) {
224 std::map<DbusObjectPath,
225 std::map<DbusInterface, std::variant<PropertyValue>>>
226 interfacesProperties;
227
228 sdbusplus::message::object_path objPath;
229 m.read(objPath, interfacesProperties);
230
231 for (const auto& pattern : configJson.items())
232 {
233 if (objPath.str.find(pattern.value()) != std::string::npos)
234 {
235 for (auto& interface : interfacesProperties)
236 {
237 if (interface.first == invNetworkIntf)
238 {
239 for (const auto& property : interface.second)
240 {
241 if (property.first == "MACAddress")
242 {
243 setFirstBootMACOnInterface(
244 pattern.key(),
245 std::get<std::string>(property.second));
246 break;
247 }
248 }
249 break;
250 }
251 }
252 }
253 }
254 };
255
256 MacAddressMatch = std::make_unique<sdbusplus::bus::match_t>(
257 bus,
258 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
259 "member='InterfacesAdded',path='/xyz/openbmc_project/"
260 "inventory'",
261 callback);
262}
263
264void watchEthernetInterface(sdbusplus::bus_t& bus,
265 const nlohmann::json& configJson)
266{
267 auto mycallback = [&](sdbusplus::message_t& m) {
268 std::map<DbusObjectPath,
269 std::map<DbusInterface, std::variant<PropertyValue>>>
270 interfacesProperties;
271
272 sdbusplus::message::object_path objPath;
273 std::pair<std::string, std::string> ethPair;
274 m.read(objPath, interfacesProperties);
275 for (const auto& interfaces : interfacesProperties)
276 {
277 if (interfaces.first ==
278 "xyz.openbmc_project.Network.EthernetInterface")
279 {
280 for (const auto& property : interfaces.second)
281 {
282 if (property.first == "InterfaceName")
283 {
284 std::string infname =
285 std::get<std::string>(property.second);
286
287 if (configJson.find(infname) == configJson.end())
288 {
289 // ethernet interface not found in configJSON
290 // check if it is not sit0 interface, as it is
291 // expected.
292 if (infname != "sit0")
293 {
294 log<level::ERR>(
295 "Wrong Interface Name in Config Json");
296 }
297 }
298 else
299 {
300 if (!setInventoryMACOnSystem(bus, configJson,
301 infname))
302 {
303 registerSignals(bus, configJson);
304 EthInterfaceMatch = nullptr;
305 }
306 }
307 break;
308 }
309 }
310 break;
311 }
312 }
313 };
314 // Incase if phosphor-inventory-manager started early and the VPD is already
315 // collected by the time network service has come up, better to check the
316 // VPD directly and set the MAC Address on the respective Interface.
317
318 bool registeredSignals = false;
319 for (const auto& interfaceString : configJson.items())
320 {
321 if ((FORCE_SYNC_MAC_FROM_INVENTORY ||
322 !std::filesystem::exists(firstBootPath + interfaceString.key())) &&
323 !registeredSignals)
324 {
325 auto msg = fmt::format("{}, check VPD for MAC",
326 (FORCE_SYNC_MAC_FROM_INVENTORY)
327 ? "Force sync enabled"
328 : "First boot file is not present");
329 log<level::INFO>(msg.c_str());
330 EthInterfaceMatch = std::make_unique<sdbusplus::bus::match_t>(
331 bus,
332 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
333 "member='InterfacesAdded',path='/xyz/openbmc_project/network'",
334 mycallback);
335 registeredSignals = true;
336 }
337 }
338}
339
340std::unique_ptr<Runtime> watch(sdbusplus::bus::bus& bus, Manager& m)
341{
342 manager = &m;
343 std::ifstream in(configFile);
344 nlohmann::json configJson;
345 in >> configJson;
346 watchEthernetInterface(bus, configJson);
347 return nullptr;
348}
349
350} // namespace phosphor::network::inventory