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