blob: dfb03e549d2023a02d614c5ac2ca0b1d32d1c60c [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>
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -070013#include <phosphor-logging/lg2.hpp>
William A. Kennington III0b111d42022-10-04 18:06:11 -070014#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;
William A. Kennington III0b111d42022-10-04 18:06:11 -070024using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
25
26using DbusObjectPath = std::string;
27using DbusInterface = std::string;
28using PropertyValue = std::string;
29using DbusService = std::string;
30using ObjectTree = string_umap<string_umap<std::vector<std::string>>>;
31
32constexpr auto firstBootPath = "/var/lib/network/firstBoot_";
33constexpr auto configFile = "/usr/share/network/config.json";
34
35constexpr auto invNetworkIntf =
36 "xyz.openbmc_project.Inventory.Item.NetworkInterface";
37constexpr auto invRoot = "/xyz/openbmc_project/inventory";
38constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper";
39constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper";
40constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper";
41constexpr auto propIntf = "org.freedesktop.DBus.Properties";
42constexpr auto methodGet = "Get";
43
44Manager* manager = nullptr;
45std::unique_ptr<sdbusplus::bus::match_t> EthInterfaceMatch = nullptr;
Patrick Williamsde333e12023-02-10 15:50:04 -060046std::unique_ptr<sdbusplus::bus::match_t> MacAddressMatch = nullptr;
William A. Kennington III0b111d42022-10-04 18:06:11 -070047std::vector<std::string> first_boot_status;
Patrick Williamsde333e12023-02-10 15:50:04 -060048nlohmann::json configJson;
William A. Kennington III0b111d42022-10-04 18:06:11 -070049
50void setFirstBootMACOnInterface(const std::string& intf, const std::string& mac)
51{
52 for (const auto& interface : manager->interfaces)
53 {
54 if (interface.first == intf)
55 {
56 auto returnMAC = interface.second->macAddress(mac);
57 if (returnMAC == mac)
58 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -070059 lg2::info(
60 "Setting MAC {MAC_ADDRESS} on interface {INTERFACE_NAME}",
61 "MAC_ADDRESS", mac, "INTERFACE_NAME", intf);
William A. Kennington III0b111d42022-10-04 18:06:11 -070062 std::error_code ec;
63 if (std::filesystem::is_directory("/var/lib/network", ec))
64 {
65 std::ofstream persistentFile(firstBootPath + intf);
66 }
67 break;
68 }
69 else
70 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -070071 lg2::info("MAC is Not Set on ethernet Interface");
William A. Kennington III0b111d42022-10-04 18:06:11 -070072 }
73 }
74 }
75}
76
77ether_addr getfromInventory(sdbusplus::bus_t& bus, const std::string& intfName)
78{
Patrick Williamsde333e12023-02-10 15:50:04 -060079 std::string interfaceName = configJson[intfName];
William A. Kennington III0b111d42022-10-04 18:06:11 -070080
81 std::vector<DbusInterface> interfaces;
82 interfaces.emplace_back(invNetworkIntf);
83
84 auto depth = 0;
85
86 auto mapperCall =
87 bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree");
88
89 mapperCall.append(invRoot, depth, interfaces);
90
91 auto mapperReply = bus.call(mapperCall);
92 if (mapperReply.is_method_error())
93 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -070094 lg2::error("Error in mapper call");
William A. Kennington III0b111d42022-10-04 18:06:11 -070095 elog<InternalFailure>();
96 }
97
98 ObjectTree objectTree;
99 mapperReply.read(objectTree);
100
101 if (objectTree.empty())
102 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700103 lg2::error("No Object has implemented the interface {INTERFACE_NAME}",
104 "INTERFACE_NAME", invNetworkIntf);
William A. Kennington III0b111d42022-10-04 18:06:11 -0700105 elog<InternalFailure>();
106 }
107
108 DbusObjectPath objPath;
109 DbusService service;
110
111 if (1 == objectTree.size())
112 {
113 objPath = objectTree.begin()->first;
114 service = objectTree.begin()->second.begin()->first;
115 }
116 else
117 {
118 // If there are more than 2 objects, object path must contain the
119 // interface name
120 for (auto const& object : objectTree)
121 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700122 lg2::info("Get info on interface {INTERFACE_NAME}, object {OBJECT}",
123 "INTERFACE_NAME", interfaceName, "OBJECT", object.first);
William A. Kennington III0b111d42022-10-04 18:06:11 -0700124
125 if (std::string::npos != object.first.find(interfaceName.c_str()))
126 {
127 objPath = object.first;
128 service = object.second.begin()->first;
129 break;
130 }
131 }
132
133 if (objPath.empty())
134 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700135 lg2::error(
136 "Can't find the object for the interface {INTERFACE_NAME}",
137 "INTERFACE_NAME", interfaceName);
William A. Kennington III0b111d42022-10-04 18:06:11 -0700138 elog<InternalFailure>();
139 }
140 }
141
142 auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
143 propIntf, methodGet);
144
145 method.append(invNetworkIntf, "MACAddress");
146
147 auto reply = bus.call(method);
148 if (reply.is_method_error())
149 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700150 lg2::error("Failed to get MACAddress for path {PATH} interface "
151 "{INTERFACE_NAME}",
152 "PATH", objPath, "INTERFACE_NAME", invNetworkIntf);
William A. Kennington III0b111d42022-10-04 18:06:11 -0700153 elog<InternalFailure>();
154 }
155
156 std::variant<std::string> value;
157 reply.read(value);
158 return ToAddr<ether_addr>{}(std::get<std::string>(value));
159}
160
Patrick Williamsde333e12023-02-10 15:50:04 -0600161bool setInventoryMACOnSystem(sdbusplus::bus_t& bus, const std::string& intfname)
William A. Kennington III0b111d42022-10-04 18:06:11 -0700162{
163 try
164 {
165 auto inventoryMAC = getfromInventory(bus, intfname);
166 if (inventoryMAC != ether_addr{})
167 {
168 auto macStr = std::to_string(inventoryMAC);
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700169 lg2::info("Mac Address {MAC_ADDRESS} in Inventory on Interface "
170 "{INTERFACE_NAME}",
171 "MAC_ADDRESS", macStr, "INTERFACE_NAME", intfname);
William A. Kennington III0b111d42022-10-04 18:06:11 -0700172 setFirstBootMACOnInterface(intfname, macStr);
173 first_boot_status.push_back(intfname);
174 bool status = true;
175 for (const auto& keys : configJson.items())
176 {
177 if (!(std::find(first_boot_status.begin(),
178 first_boot_status.end(),
179 keys.key()) != first_boot_status.end()))
180 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700181 lg2::info(
182 "Interface {INTERFACE_NAME} MAC is NOT set from VPD",
183 "INTERFACE_NAME", keys.key());
William A. Kennington III0b111d42022-10-04 18:06:11 -0700184 status = false;
185 }
186 }
187 if (status)
188 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700189 lg2::info("Removing the match for ethernet interfaces");
William A. Kennington III0b111d42022-10-04 18:06:11 -0700190 EthInterfaceMatch = nullptr;
191 }
192 }
193 else
194 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700195 lg2::info("Nothing is present in Inventory");
William A. Kennington III0b111d42022-10-04 18:06:11 -0700196 return false;
197 }
198 }
199 catch (const std::exception& e)
200 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700201 lg2::error("Exception occurred during getting of MAC "
202 "address from Inventory");
William A. Kennington III0b111d42022-10-04 18:06:11 -0700203 return false;
204 }
205 return true;
206}
207
208// register the macthes to be monitored from inventory manager
Patrick Williamsde333e12023-02-10 15:50:04 -0600209void registerSignals(sdbusplus::bus_t& bus)
William A. Kennington III0b111d42022-10-04 18:06:11 -0700210{
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700211 lg2::info("Registering the Inventory Signals Matcher");
William A. Kennington III0b111d42022-10-04 18:06:11 -0700212
William A. Kennington III0b111d42022-10-04 18:06:11 -0700213 auto callback = [&](sdbusplus::message_t& m) {
214 std::map<DbusObjectPath,
215 std::map<DbusInterface, std::variant<PropertyValue>>>
216 interfacesProperties;
217
218 sdbusplus::message::object_path objPath;
219 m.read(objPath, interfacesProperties);
220
221 for (const auto& pattern : configJson.items())
222 {
223 if (objPath.str.find(pattern.value()) != std::string::npos)
224 {
225 for (auto& interface : interfacesProperties)
226 {
227 if (interface.first == invNetworkIntf)
228 {
229 for (const auto& property : interface.second)
230 {
231 if (property.first == "MACAddress")
232 {
233 setFirstBootMACOnInterface(
234 pattern.key(),
235 std::get<std::string>(property.second));
236 break;
237 }
238 }
239 break;
240 }
241 }
242 }
243 }
244 };
245
246 MacAddressMatch = std::make_unique<sdbusplus::bus::match_t>(
247 bus,
248 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
249 "member='InterfacesAdded',path='/xyz/openbmc_project/"
250 "inventory'",
251 callback);
252}
253
Patrick Williamsde333e12023-02-10 15:50:04 -0600254void watchEthernetInterface(sdbusplus::bus_t& bus)
William A. Kennington III0b111d42022-10-04 18:06:11 -0700255{
Patrick Williamsde333e12023-02-10 15:50:04 -0600256 auto handle_interface = [&](auto infname) {
257 if (configJson.find(infname) == configJson.end())
258 {
259 // ethernet interface not found in configJSON
260 // check if it is not sit0 interface, as it is
261 // expected.
262 if (infname != "sit0")
263 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700264 lg2::error("Wrong Interface Name in Config Json");
Patrick Williamsde333e12023-02-10 15:50:04 -0600265 }
266 }
267 else
268 {
269 registerSignals(bus);
270 EthInterfaceMatch = nullptr;
271
272 if (setInventoryMACOnSystem(bus, infname))
273 {
274 MacAddressMatch = nullptr;
275 }
276 }
277 };
278
279 auto mycallback = [&, handle_interface](sdbusplus::message_t& m) {
William A. Kennington III0b111d42022-10-04 18:06:11 -0700280 std::map<DbusObjectPath,
281 std::map<DbusInterface, std::variant<PropertyValue>>>
282 interfacesProperties;
283
284 sdbusplus::message::object_path objPath;
285 std::pair<std::string, std::string> ethPair;
286 m.read(objPath, interfacesProperties);
Patrick Williamsde333e12023-02-10 15:50:04 -0600287
William A. Kennington III0b111d42022-10-04 18:06:11 -0700288 for (const auto& interfaces : interfacesProperties)
289 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700290 lg2::info("Check {INTERFACE_NAME} for sdbus response",
291 "INTERFACE_NAME", interfaces.first);
William A. Kennington III0b111d42022-10-04 18:06:11 -0700292 if (interfaces.first ==
293 "xyz.openbmc_project.Network.EthernetInterface")
294 {
295 for (const auto& property : interfaces.second)
296 {
297 if (property.first == "InterfaceName")
298 {
Patrick Williamsde333e12023-02-10 15:50:04 -0600299 handle_interface(
300 std::get<std::string>(property.second));
William A. Kennington III0b111d42022-10-04 18:06:11 -0700301
William A. Kennington III0b111d42022-10-04 18:06:11 -0700302 break;
303 }
304 }
305 break;
306 }
307 }
308 };
309 // Incase if phosphor-inventory-manager started early and the VPD is already
310 // collected by the time network service has come up, better to check the
311 // VPD directly and set the MAC Address on the respective Interface.
312
313 bool registeredSignals = false;
314 for (const auto& interfaceString : configJson.items())
315 {
316 if ((FORCE_SYNC_MAC_FROM_INVENTORY ||
317 !std::filesystem::exists(firstBootPath + interfaceString.key())) &&
318 !registeredSignals)
319 {
Jagpal Singh Gill49f9d252023-04-17 15:15:39 -0700320 lg2::info("Check VPD for MAC: {REASON}", "REASON",
321 (FORCE_SYNC_MAC_FROM_INVENTORY)
322 ? "Force sync enabled"
323 : "First boot file is not present");
William A. Kennington III0b111d42022-10-04 18:06:11 -0700324 EthInterfaceMatch = std::make_unique<sdbusplus::bus::match_t>(
325 bus,
326 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
327 "member='InterfacesAdded',path='/xyz/openbmc_project/network'",
328 mycallback);
329 registeredSignals = true;
Patrick Williamsde333e12023-02-10 15:50:04 -0600330
331 for (const auto& intf : manager->interfaces)
332 {
333 if (intf.first == interfaceString.key())
334 {
335 handle_interface(intf.first);
336 }
337 }
William A. Kennington III0b111d42022-10-04 18:06:11 -0700338 }
339 }
340}
341
William A. Kennington III9ede1b72022-11-21 01:59:28 -0800342std::unique_ptr<Runtime> watch(stdplus::PinnedRef<sdbusplus::bus_t> bus,
343 stdplus::PinnedRef<Manager> m)
William A. Kennington III0b111d42022-10-04 18:06:11 -0700344{
William A. Kennington III9ede1b72022-11-21 01:59:28 -0800345 manager = &m.get();
William A. Kennington III0b111d42022-10-04 18:06:11 -0700346 std::ifstream in(configFile);
William A. Kennington III0b111d42022-10-04 18:06:11 -0700347 in >> configJson;
Patrick Williamsde333e12023-02-10 15:50:04 -0600348 watchEthernetInterface(bus);
William A. Kennington III0b111d42022-10-04 18:06:11 -0700349 return nullptr;
350}
351
352} // namespace phosphor::network::inventory