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