blob: 4da20aba2571c27aba1ddbe1e782188dc47bd09e [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;
Patrick Williamsde333e12023-02-10 15:50:04 -060049std::unique_ptr<sdbusplus::bus::match_t> MacAddressMatch = nullptr;
William A. Kennington III0b111d42022-10-04 18:06:11 -070050std::vector<std::string> first_boot_status;
Patrick Williamsde333e12023-02-10 15:50:04 -060051nlohmann::json configJson;
William A. Kennington III0b111d42022-10-04 18:06:11 -070052
53void setFirstBootMACOnInterface(const std::string& intf, const std::string& mac)
54{
55 for (const auto& interface : manager->interfaces)
56 {
57 if (interface.first == intf)
58 {
59 auto returnMAC = interface.second->macAddress(mac);
60 if (returnMAC == mac)
61 {
62 log<level::INFO>(fmt::format("Setting MAC on {}", intf).c_str(),
63 entry("INTF=%s", intf.c_str()),
64 entry("MAC=%s", mac.c_str()));
65 std::error_code ec;
66 if (std::filesystem::is_directory("/var/lib/network", ec))
67 {
68 std::ofstream persistentFile(firstBootPath + intf);
69 }
70 break;
71 }
72 else
73 {
74 log<level::INFO>("MAC is Not Set on ethernet Interface");
75 }
76 }
77 }
78}
79
80ether_addr getfromInventory(sdbusplus::bus_t& bus, const std::string& intfName)
81{
Patrick Williamsde333e12023-02-10 15:50:04 -060082 std::string interfaceName = configJson[intfName];
William A. Kennington III0b111d42022-10-04 18:06:11 -070083
84 std::vector<DbusInterface> interfaces;
85 interfaces.emplace_back(invNetworkIntf);
86
87 auto depth = 0;
88
89 auto mapperCall =
90 bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree");
91
92 mapperCall.append(invRoot, depth, interfaces);
93
94 auto mapperReply = bus.call(mapperCall);
95 if (mapperReply.is_method_error())
96 {
97 log<level::ERR>("Error in mapper call");
98 elog<InternalFailure>();
99 }
100
101 ObjectTree objectTree;
102 mapperReply.read(objectTree);
103
104 if (objectTree.empty())
105 {
106 log<level::ERR>("No Object has implemented the interface",
107 entry("INTERFACE=%s", invNetworkIntf));
108 elog<InternalFailure>();
109 }
110
111 DbusObjectPath objPath;
112 DbusService service;
113
114 if (1 == objectTree.size())
115 {
116 objPath = objectTree.begin()->first;
117 service = objectTree.begin()->second.begin()->first;
118 }
119 else
120 {
121 // If there are more than 2 objects, object path must contain the
122 // interface name
123 for (auto const& object : objectTree)
124 {
125 log<level::INFO>("interface",
126 entry("INT=%s", interfaceName.c_str()));
127 log<level::INFO>("object", entry("OBJ=%s", object.first.c_str()));
128
129 if (std::string::npos != object.first.find(interfaceName.c_str()))
130 {
131 objPath = object.first;
132 service = object.second.begin()->first;
133 break;
134 }
135 }
136
137 if (objPath.empty())
138 {
139 log<level::ERR>("Can't find the object for the interface",
140 entry("intfName=%s", interfaceName.c_str()));
141 elog<InternalFailure>();
142 }
143 }
144
145 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
146 propIntf, methodGet);
147
148 method.append(invNetworkIntf, "MACAddress");
149
150 auto reply = bus.call(method);
151 if (reply.is_method_error())
152 {
153 log<level::ERR>("Failed to get MACAddress",
154 entry("PATH=%s", objPath.c_str()),
155 entry("INTERFACE=%s", invNetworkIntf));
156 elog<InternalFailure>();
157 }
158
159 std::variant<std::string> value;
160 reply.read(value);
161 return ToAddr<ether_addr>{}(std::get<std::string>(value));
162}
163
Patrick Williamsde333e12023-02-10 15:50:04 -0600164bool setInventoryMACOnSystem(sdbusplus::bus_t& bus, const std::string& intfname)
William A. Kennington III0b111d42022-10-04 18:06:11 -0700165{
166 try
167 {
168 auto inventoryMAC = getfromInventory(bus, intfname);
169 if (inventoryMAC != ether_addr{})
170 {
171 auto macStr = std::to_string(inventoryMAC);
172 log<level::INFO>("Mac Address in Inventory on ",
173 entry("Interface : ", intfname.c_str()),
174 entry("MAC Address :", macStr.c_str()));
175 setFirstBootMACOnInterface(intfname, macStr);
176 first_boot_status.push_back(intfname);
177 bool status = true;
178 for (const auto& keys : configJson.items())
179 {
180 if (!(std::find(first_boot_status.begin(),
181 first_boot_status.end(),
182 keys.key()) != first_boot_status.end()))
183 {
184 log<level::INFO>("Interface MAC is NOT set from VPD"),
185 entry("INTERFACE", keys.key().c_str());
186 status = false;
187 }
188 }
189 if (status)
190 {
191 log<level::INFO>("Removing the match for ethernet interfaces");
192 EthInterfaceMatch = nullptr;
193 }
194 }
195 else
196 {
197 log<level::INFO>("Nothing is present in Inventory");
198 return false;
199 }
200 }
201 catch (const std::exception& e)
202 {
203 log<level::ERR>("Exception occurred during getting of MAC "
204 "address from Inventory");
205 return false;
206 }
207 return true;
208}
209
210// register the macthes to be monitored from inventory manager
Patrick Williamsde333e12023-02-10 15:50:04 -0600211void registerSignals(sdbusplus::bus_t& bus)
William A. Kennington III0b111d42022-10-04 18:06:11 -0700212{
213 log<level::INFO>("Registering the Inventory Signals Matcher");
214
William A. Kennington III0b111d42022-10-04 18:06:11 -0700215 auto callback = [&](sdbusplus::message_t& m) {
216 std::map<DbusObjectPath,
217 std::map<DbusInterface, std::variant<PropertyValue>>>
218 interfacesProperties;
219
220 sdbusplus::message::object_path objPath;
221 m.read(objPath, interfacesProperties);
222
223 for (const auto& pattern : configJson.items())
224 {
225 if (objPath.str.find(pattern.value()) != std::string::npos)
226 {
227 for (auto& interface : interfacesProperties)
228 {
229 if (interface.first == invNetworkIntf)
230 {
231 for (const auto& property : interface.second)
232 {
233 if (property.first == "MACAddress")
234 {
235 setFirstBootMACOnInterface(
236 pattern.key(),
237 std::get<std::string>(property.second));
238 break;
239 }
240 }
241 break;
242 }
243 }
244 }
245 }
246 };
247
248 MacAddressMatch = std::make_unique<sdbusplus::bus::match_t>(
249 bus,
250 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
251 "member='InterfacesAdded',path='/xyz/openbmc_project/"
252 "inventory'",
253 callback);
254}
255
Patrick Williamsde333e12023-02-10 15:50:04 -0600256void watchEthernetInterface(sdbusplus::bus_t& bus)
William A. Kennington III0b111d42022-10-04 18:06:11 -0700257{
Patrick Williamsde333e12023-02-10 15:50:04 -0600258 auto handle_interface = [&](auto infname) {
259 if (configJson.find(infname) == configJson.end())
260 {
261 // ethernet interface not found in configJSON
262 // check if it is not sit0 interface, as it is
263 // expected.
264 if (infname != "sit0")
265 {
266 log<level::ERR>("Wrong Interface Name in Config Json");
267 }
268 }
269 else
270 {
271 registerSignals(bus);
272 EthInterfaceMatch = nullptr;
273
274 if (setInventoryMACOnSystem(bus, infname))
275 {
276 MacAddressMatch = nullptr;
277 }
278 }
279 };
280
281 auto mycallback = [&, handle_interface](sdbusplus::message_t& m) {
William A. Kennington III0b111d42022-10-04 18:06:11 -0700282 std::map<DbusObjectPath,
283 std::map<DbusInterface, std::variant<PropertyValue>>>
284 interfacesProperties;
285
286 sdbusplus::message::object_path objPath;
287 std::pair<std::string, std::string> ethPair;
288 m.read(objPath, interfacesProperties);
Patrick Williamsde333e12023-02-10 15:50:04 -0600289
William A. Kennington III0b111d42022-10-04 18:06:11 -0700290 for (const auto& interfaces : interfacesProperties)
291 {
Patrick Williamsde333e12023-02-10 15:50:04 -0600292 log<level::INFO>(interfaces.first.c_str());
William A. Kennington III0b111d42022-10-04 18:06:11 -0700293 if (interfaces.first ==
294 "xyz.openbmc_project.Network.EthernetInterface")
295 {
296 for (const auto& property : interfaces.second)
297 {
298 if (property.first == "InterfaceName")
299 {
Patrick Williamsde333e12023-02-10 15:50:04 -0600300 handle_interface(
301 std::get<std::string>(property.second));
William A. Kennington III0b111d42022-10-04 18:06:11 -0700302
William A. Kennington III0b111d42022-10-04 18:06:11 -0700303 break;
304 }
305 }
306 break;
307 }
308 }
309 };
310 // Incase if phosphor-inventory-manager started early and the VPD is already
311 // collected by the time network service has come up, better to check the
312 // VPD directly and set the MAC Address on the respective Interface.
313
314 bool registeredSignals = false;
315 for (const auto& interfaceString : configJson.items())
316 {
317 if ((FORCE_SYNC_MAC_FROM_INVENTORY ||
318 !std::filesystem::exists(firstBootPath + interfaceString.key())) &&
319 !registeredSignals)
320 {
321 auto msg = fmt::format("{}, check VPD for MAC",
322 (FORCE_SYNC_MAC_FROM_INVENTORY)
323 ? "Force sync enabled"
324 : "First boot file is not present");
325 log<level::INFO>(msg.c_str());
326 EthInterfaceMatch = std::make_unique<sdbusplus::bus::match_t>(
327 bus,
328 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
329 "member='InterfacesAdded',path='/xyz/openbmc_project/network'",
330 mycallback);
331 registeredSignals = true;
Patrick Williamsde333e12023-02-10 15:50:04 -0600332
333 for (const auto& intf : manager->interfaces)
334 {
335 if (intf.first == interfaceString.key())
336 {
337 handle_interface(intf.first);
338 }
339 }
William A. Kennington III0b111d42022-10-04 18:06:11 -0700340 }
341 }
342}
343
William A. Kennington III9ede1b72022-11-21 01:59:28 -0800344std::unique_ptr<Runtime> watch(stdplus::PinnedRef<sdbusplus::bus_t> bus,
345 stdplus::PinnedRef<Manager> m)
William A. Kennington III0b111d42022-10-04 18:06:11 -0700346{
William A. Kennington III9ede1b72022-11-21 01:59:28 -0800347 manager = &m.get();
William A. Kennington III0b111d42022-10-04 18:06:11 -0700348 std::ifstream in(configFile);
William A. Kennington III0b111d42022-10-04 18:06:11 -0700349 in >> configJson;
Patrick Williamsde333e12023-02-10 15:50:04 -0600350 watchEthernetInterface(bus);
William A. Kennington III0b111d42022-10-04 18:06:11 -0700351 return nullptr;
352}
353
354} // namespace phosphor::network::inventory