1 #include "network_manager.hpp"
2
3 #include "config_parser.hpp"
4 #include "ipaddress.hpp"
5 #include "system_queries.hpp"
6 #include "types.hpp"
7 #include "util.hpp"
8
9 #include <linux/if_addr.h>
10 #include <linux/neighbour.h>
11 #include <net/if.h>
12 #include <net/if_arp.h>
13
14 #include <phosphor-logging/elog-errors.hpp>
15 #include <phosphor-logging/lg2.hpp>
16 #include <sdbusplus/message.hpp>
17 #include <stdplus/numeric/str.hpp>
18 #include <stdplus/pinned.hpp>
19 #include <stdplus/print.hpp>
20 #include <stdplus/str/cat.hpp>
21 #include <xyz/openbmc_project/Common/error.hpp>
22
23 #include <filesystem>
24 #include <format>
25
26 namespace phosphor
27 {
28 namespace network
29 {
30
31 using namespace phosphor::logging;
32 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
33 using Argument = xyz::openbmc_project::Common::InvalidArgument;
34 using std::literals::string_view_literals::operator""sv;
35
36 static constexpr const char enabledMatch[] =
37 "type='signal',sender='org.freedesktop.network1',path_namespace='/org/"
38 "freedesktop/network1/"
39 "link',interface='org.freedesktop.DBus.Properties',member='"
40 "PropertiesChanged',arg0='org.freedesktop.network1.Link',";
41
Manager(stdplus::PinnedRef<sdbusplus::bus_t> bus,stdplus::PinnedRef<DelayedExecutor> reload,stdplus::zstring_view objPath,const std::filesystem::path & confDir)42 Manager::Manager(stdplus::PinnedRef<sdbusplus::bus_t> bus,
43 stdplus::PinnedRef<DelayedExecutor> reload,
44 stdplus::zstring_view objPath,
45 const std::filesystem::path& confDir) :
46 ManagerIface(bus, objPath.c_str(), ManagerIface::action::defer_emit),
47 reload(reload), bus(bus), objPath(std::string(objPath)), confDir(confDir),
48 systemdNetworkdEnabledMatch(
49 bus, enabledMatch,
50 [man = stdplus::PinnedRef(*this)](sdbusplus::message_t& m) {
51 std::string intf;
52 std::unordered_map<std::string, std::variant<std::string>> values;
53 try
54 {
55 m.read(intf, values);
56 auto it = values.find("AdministrativeState");
57 if (it == values.end())
58 {
59 return;
60 }
61 const std::string_view obj = m.get_path();
62 auto sep = obj.rfind('/');
63 if (sep == obj.npos || sep + 3 > obj.size())
64 {
65 throw std::invalid_argument("Invalid obj path");
66 }
67 auto ifidx = stdplus::StrToInt<10, uint16_t>{}(obj.substr(sep + 3));
68 const auto& state = std::get<std::string>(it->second);
69 man.get().handleAdminState(state, ifidx);
70 }
71 catch (const std::exception& e)
72 {
73 lg2::error("AdministrativeState match parsing failed: {ERROR}", "ERROR",
74 e);
75 }
76 })
77 {
__anond1a9d47b0202() 78 reload.get().setCallback([self = stdplus::PinnedRef(*this)]() {
79 for (auto& hook : self.get().reloadPreHooks)
80 {
81 try
82 {
83 hook();
84 }
85 catch (const std::exception& ex)
86 {
87 lg2::error("Failed executing reload hook, ignoring: {ERROR}",
88 "ERROR", ex);
89 }
90 }
91 self.get().reloadPreHooks.clear();
92 try
93 {
94 self.get()
95 .bus.get()
96 .new_method_call("org.freedesktop.network1",
97 "/org/freedesktop/network1",
98 "org.freedesktop.network1.Manager", "Reload")
99 .call();
100 lg2::info("Reloaded systemd-networkd");
101 }
102 catch (const sdbusplus::exception_t& ex)
103 {
104 lg2::error("Failed to reload configuration: {ERROR}", "ERROR", ex);
105 self.get().reloadPostHooks.clear();
106 }
107 for (auto& hook : self.get().reloadPostHooks)
108 {
109 try
110 {
111 hook();
112 }
113 catch (const std::exception& ex)
114 {
115 lg2::error("Failed executing reload hook, ignoring: {ERROR}",
116 "ERROR", ex);
117 }
118 }
119 self.get().reloadPostHooks.clear();
120 });
121 std::vector<
122 std::tuple<int32_t, std::string, sdbusplus::message::object_path>>
123 links;
124 try
125 {
126 auto rsp = bus.get()
127 .new_method_call("org.freedesktop.network1",
128 "/org/freedesktop/network1",
129 "org.freedesktop.network1.Manager",
130 "ListLinks")
131 .call();
132 rsp.read(links);
133 }
134 catch (const sdbusplus::exception::SdBusError& e)
135 {
136 // Any failures are systemd-network not being ready
137 }
138 for (const auto& link : links)
139 {
140 unsigned ifidx = std::get<0>(link);
141 stdplus::ToStrHandle<stdplus::IntToStr<10, unsigned>> tsh;
142 auto obj = stdplus::strCat("/org/freedesktop/network1/link/_3"sv,
143 tsh(ifidx));
144 auto req =
145 bus.get().new_method_call("org.freedesktop.network1", obj.c_str(),
146 "org.freedesktop.DBus.Properties", "Get");
147 req.append("org.freedesktop.network1.Link", "AdministrativeState");
148 auto rsp = req.call();
149 std::variant<std::string> val;
150 rsp.read(val);
151 handleAdminState(std::get<std::string>(val), ifidx);
152 }
153
154 std::filesystem::create_directories(confDir);
155 systemConf = std::make_unique<phosphor::network::SystemConfiguration>(
156 bus, (this->objPath / "config").str);
157 }
158
createInterface(const AllIntfInfo & info,bool enabled)159 void Manager::createInterface(const AllIntfInfo& info, bool enabled)
160 {
161 if (ignoredIntf.find(info.intf.idx) != ignoredIntf.end())
162 {
163 return;
164 }
165 if (auto it = interfacesByIdx.find(info.intf.idx);
166 it != interfacesByIdx.end())
167 {
168 if (info.intf.name && *info.intf.name != it->second->interfaceName())
169 {
170 interfaces.erase(it->second->interfaceName());
171 interfacesByIdx.erase(it);
172 }
173 else
174 {
175 it->second->updateInfo(info.intf);
176 return;
177 }
178 }
179 else if (info.intf.name)
180 {
181 auto it = interfaces.find(*info.intf.name);
182 if (it != interfaces.end())
183 {
184 it->second->updateInfo(info.intf);
185 return;
186 }
187 }
188 if (!info.intf.name)
189 {
190 lg2::error("Can't create interface without name: {NET_IDX}", "NET_IDX",
191 info.intf.idx);
192 return;
193 }
194 config::Parser config(config::pathForIntfConf(confDir, *info.intf.name));
195 auto intf = std::make_unique<EthernetInterface>(
196 bus, *this, info, objPath.str, config, enabled);
197 intf->loadNameServers(config);
198 intf->loadNTPServers(config);
199 auto ptr = intf.get();
200 interfaces.insert_or_assign(*info.intf.name, std::move(intf));
201 interfacesByIdx.insert_or_assign(info.intf.idx, ptr);
202 }
203
addInterface(const InterfaceInfo & info)204 void Manager::addInterface(const InterfaceInfo& info)
205 {
206 if (info.type != ARPHRD_ETHER)
207 {
208 ignoredIntf.emplace(info.idx);
209 return;
210 }
211 if (info.name)
212 {
213 const auto& ignored = internal::getIgnoredInterfaces();
214 if (ignored.find(*info.name) != ignored.end())
215 {
216 static std::unordered_set<std::string> ignored;
217 if (!ignored.contains(*info.name))
218 {
219 ignored.emplace(*info.name);
220 lg2::info("Ignoring interface {NET_INTF}", "NET_INTF",
221 *info.name);
222 }
223 ignoredIntf.emplace(info.idx);
224 return;
225 }
226 }
227
228 auto infoIt = intfInfo.find(info.idx);
229 if (infoIt != intfInfo.end())
230 {
231 infoIt->second.intf = info;
232 }
233 else
234 {
235 infoIt = std::get<0>(intfInfo.emplace(info.idx, AllIntfInfo{info}));
236 }
237
238 if (auto it = systemdNetworkdEnabled.find(info.idx);
239 it != systemdNetworkdEnabled.end())
240 {
241 createInterface(infoIt->second, it->second);
242 }
243 }
244
removeInterface(const InterfaceInfo & info)245 void Manager::removeInterface(const InterfaceInfo& info)
246 {
247 auto iit = interfacesByIdx.find(info.idx);
248 auto nit = interfaces.end();
249 if (info.name)
250 {
251 nit = interfaces.find(*info.name);
252 if (nit != interfaces.end() && iit != interfacesByIdx.end() &&
253 nit->second.get() != iit->second)
254 {
255 stdplus::print(stderr, "Removed interface desync detected\n");
256 fflush(stderr);
257 std::abort();
258 }
259 }
260 else if (iit != interfacesByIdx.end())
261 {
262 for (nit = interfaces.begin(); nit != interfaces.end(); ++nit)
263 {
264 if (nit->second.get() == iit->second)
265 {
266 break;
267 }
268 }
269 }
270
271 if (iit != interfacesByIdx.end())
272 {
273 interfacesByIdx.erase(iit);
274 }
275 else
276 {
277 ignoredIntf.erase(info.idx);
278 }
279 if (nit != interfaces.end())
280 {
281 interfaces.erase(nit);
282 }
283 intfInfo.erase(info.idx);
284 }
285
addAddress(const AddressInfo & info)286 void Manager::addAddress(const AddressInfo& info)
287 {
288 if (info.flags & IFA_F_DEPRECATED)
289 {
290 return;
291 }
292 if (auto it = intfInfo.find(info.ifidx); it != intfInfo.end())
293 {
294 it->second.addrs.insert_or_assign(info.ifaddr, info);
295 if (auto it = interfacesByIdx.find(info.ifidx);
296 it != interfacesByIdx.end())
297 {
298 it->second->addAddr(info);
299 }
300 }
301 else if (!ignoredIntf.contains(info.ifidx))
302 {
303 throw std::runtime_error(
304 std::format("Interface `{}` not found for addr", info.ifidx));
305 }
306 }
307
removeAddress(const AddressInfo & info)308 void Manager::removeAddress(const AddressInfo& info)
309 {
310 if (auto it = interfacesByIdx.find(info.ifidx); it != interfacesByIdx.end())
311 {
312 it->second->addrs.erase(info.ifaddr);
313 if (auto it = intfInfo.find(info.ifidx); it != intfInfo.end())
314 {
315 it->second.addrs.erase(info.ifaddr);
316 }
317 }
318 }
319
addNeighbor(const NeighborInfo & info)320 void Manager::addNeighbor(const NeighborInfo& info)
321 {
322 if (!(info.state & NUD_PERMANENT) || !info.addr)
323 {
324 return;
325 }
326 if (auto it = intfInfo.find(info.ifidx); it != intfInfo.end())
327 {
328 it->second.staticNeighs.insert_or_assign(*info.addr, info);
329 if (auto it = interfacesByIdx.find(info.ifidx);
330 it != interfacesByIdx.end())
331 {
332 it->second->addStaticNeigh(info);
333 }
334 }
335 else if (!ignoredIntf.contains(info.ifidx))
336 {
337 throw std::runtime_error(
338 std::format("Interface `{}` not found for neigh", info.ifidx));
339 }
340 }
341
removeNeighbor(const NeighborInfo & info)342 void Manager::removeNeighbor(const NeighborInfo& info)
343 {
344 if (!info.addr)
345 {
346 return;
347 }
348 if (auto it = intfInfo.find(info.ifidx); it != intfInfo.end())
349 {
350 it->second.staticNeighs.erase(*info.addr);
351 if (auto it = interfacesByIdx.find(info.ifidx);
352 it != interfacesByIdx.end())
353 {
354 it->second->staticNeighbors.erase(*info.addr);
355 }
356 }
357 }
358
addDefGw(unsigned ifidx,stdplus::InAnyAddr addr)359 void Manager::addDefGw(unsigned ifidx, stdplus::InAnyAddr addr)
360 {
361 if (auto it = intfInfo.find(ifidx); it != intfInfo.end())
362 {
363 std::visit(
364 [&](auto addr) {
365 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>)
366 {
367 it->second.defgw4.emplace(addr);
368 }
369 else
370 {
371 static_assert(std::is_same_v<stdplus::In6Addr, decltype(addr)>);
372 it->second.defgw6.emplace(addr);
373 }
374 },
375 addr);
376 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end())
377 {
378 std::visit(
379 [&](auto addr) {
380 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>)
381 {
382 it->second->EthernetInterfaceIntf::defaultGateway(
383 stdplus::toStr(addr));
384 }
385 else
386 {
387 static_assert(
388 std::is_same_v<stdplus::In6Addr, decltype(addr)>);
389 it->second->EthernetInterfaceIntf::defaultGateway6(
390 stdplus::toStr(addr));
391 }
392 },
393 addr);
394 }
395 }
396 else if (!ignoredIntf.contains(ifidx))
397 {
398 lg2::error("Interface {NET_IDX} not found for gw", "NET_IDX", ifidx);
399 }
400 }
401
removeDefGw(unsigned ifidx,stdplus::InAnyAddr addr)402 void Manager::removeDefGw(unsigned ifidx, stdplus::InAnyAddr addr)
403 {
404 if (auto it = intfInfo.find(ifidx); it != intfInfo.end())
405 {
406 std::visit(
407 [&](auto addr) {
408 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>)
409 {
410 if (it->second.defgw4 == addr)
411 {
412 it->second.defgw4.reset();
413 }
414 }
415 else
416 {
417 static_assert(std::is_same_v<stdplus::In6Addr, decltype(addr)>);
418 if (it->second.defgw6 == addr)
419 {
420 it->second.defgw6.reset();
421 }
422 }
423 },
424 addr);
425 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end())
426 {
427 std::visit(
428 [&](auto addr) {
429 if constexpr (std::is_same_v<stdplus::In4Addr, decltype(addr)>)
430 {
431 stdplus::ToStrHandle<stdplus::ToStr<stdplus::In4Addr>> tsh;
432 if (it->second->defaultGateway() == tsh(addr))
433 {
434 it->second->EthernetInterfaceIntf::defaultGateway("");
435 }
436 }
437 else
438 {
439 static_assert(
440 std::is_same_v<stdplus::In6Addr, decltype(addr)>);
441 stdplus::ToStrHandle<stdplus::ToStr<stdplus::In6Addr>> tsh;
442 if (it->second->defaultGateway6() == tsh(addr))
443 {
444 it->second->EthernetInterfaceIntf::defaultGateway6("");
445 }
446 }
447 },
448 addr);
449 }
450 }
451 }
452
vlan(std::string interfaceName,uint32_t id)453 ObjectPath Manager::vlan(std::string interfaceName, uint32_t id)
454 {
455 if (id == 0 || id >= 4095)
456 {
457 lg2::error("VLAN ID {NET_VLAN} is not valid", "NET_VLAN", id);
458 elog<InvalidArgument>(
459 Argument::ARGUMENT_NAME("VLANId"),
460 Argument::ARGUMENT_VALUE(std::to_string(id).c_str()));
461 }
462
463 auto it = interfaces.find(interfaceName);
464 if (it == interfaces.end())
465 {
466 using ResourceErr =
467 phosphor::logging::xyz::openbmc_project::Common::ResourceNotFound;
468 elog<ResourceNotFound>(ResourceErr::RESOURCE(interfaceName.c_str()));
469 }
470 return it->second->createVLAN(id);
471 }
472
reset()473 void Manager::reset()
474 {
475 for (const auto& dirent : std::filesystem::directory_iterator(confDir))
476 {
477 std::error_code ec;
478 std::filesystem::remove(dirent.path(), ec);
479 }
480 lg2::info("Network data purged.");
481 }
482
writeToConfigurationFile()483 void Manager::writeToConfigurationFile()
484 {
485 // write all the static ip address in the systemd-network conf file
486 for (const auto& intf : interfaces)
487 {
488 intf.second->writeConfigurationFile();
489 }
490 }
491
handleAdminState(std::string_view state,unsigned ifidx)492 void Manager::handleAdminState(std::string_view state, unsigned ifidx)
493 {
494 if (state == "initialized" || state == "linger")
495 {
496 systemdNetworkdEnabled.erase(ifidx);
497 }
498 else
499 {
500 bool managed = state != "unmanaged";
501 systemdNetworkdEnabled.insert_or_assign(ifidx, managed);
502 if (auto it = interfacesByIdx.find(ifidx); it != interfacesByIdx.end())
503 {
504 it->second->EthernetInterfaceIntf::nicEnabled(managed);
505 }
506 else if (auto it = intfInfo.find(ifidx); it != intfInfo.end())
507 {
508 createInterface(it->second, managed);
509 }
510 }
511 }
512
513 } // namespace network
514 } // namespace phosphor
515