xref: /openbmc/google-misc/acpi-power-state-daemon/subprojects/ncsid/src/net_config.cpp (revision d94dc2572377be146d766b7e7a531d9efa696700)
11285115cSWilliam A. Kennington III // Copyright 2021 Google LLC
21285115cSWilliam A. Kennington III //
31285115cSWilliam A. Kennington III // Licensed under the Apache License, Version 2.0 (the "License");
41285115cSWilliam A. Kennington III // you may not use this file except in compliance with the License.
51285115cSWilliam A. Kennington III // You may obtain a copy of the License at
61285115cSWilliam A. Kennington III //
71285115cSWilliam A. Kennington III //      http://www.apache.org/licenses/LICENSE-2.0
81285115cSWilliam A. Kennington III //
91285115cSWilliam A. Kennington III // Unless required by applicable law or agreed to in writing, software
101285115cSWilliam A. Kennington III // distributed under the License is distributed on an "AS IS" BASIS,
111285115cSWilliam A. Kennington III // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121285115cSWilliam A. Kennington III // See the License for the specific language governing permissions and
131285115cSWilliam A. Kennington III // limitations under the License.
141285115cSWilliam A. Kennington III 
151285115cSWilliam A. Kennington III #include "net_config.h"
161285115cSWilliam A. Kennington III 
17*d94dc257SMo Elbadry #include <linux/if.h>
18*d94dc257SMo Elbadry #include <net/if_arp.h>
19*d94dc257SMo Elbadry #include <sys/ioctl.h>
201285115cSWilliam A. Kennington III #include <sys/types.h>
211285115cSWilliam A. Kennington III #include <sys/wait.h>
221285115cSWilliam A. Kennington III #include <unistd.h>
231285115cSWilliam A. Kennington III 
241285115cSWilliam A. Kennington III #include <sdbusplus/bus.hpp>
25a5c9d7a4SWilliam A. Kennington III #include <stdplus/fd/create.hpp>
26a5c9d7a4SWilliam A. Kennington III #include <stdplus/fd/ops.hpp>
27253e6463SWilly Tu #include <stdplus/print.hpp>
281285115cSWilliam A. Kennington III #include <stdplus/util/string.hpp>
291285115cSWilliam A. Kennington III 
301285115cSWilliam A. Kennington III #include <cstdio>
311285115cSWilliam A. Kennington III #include <cstring>
32a5c9d7a4SWilliam A. Kennington III #include <filesystem>
33bb53161dSWilly Tu #include <format>
34*d94dc257SMo Elbadry #include <thread>
351285115cSWilliam A. Kennington III #include <utility>
361285115cSWilliam A. Kennington III #include <variant>
371285115cSWilliam A. Kennington III 
381285115cSWilliam A. Kennington III /* Most of the code for interacting with DBus is from
391285115cSWilliam A. Kennington III  * phosphor-host-ipmid/utils.cpp
401285115cSWilliam A. Kennington III  */
411285115cSWilliam A. Kennington III 
421285115cSWilliam A. Kennington III namespace net
431285115cSWilliam A. Kennington III {
441285115cSWilliam A. Kennington III 
451285115cSWilliam A. Kennington III namespace
461285115cSWilliam A. Kennington III {
471285115cSWilliam A. Kennington III 
481285115cSWilliam A. Kennington III constexpr auto IFACE_ROOT = "/xyz/openbmc_project/network/";
491285115cSWilliam A. Kennington III constexpr auto MAC_FORMAT = "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx";
501285115cSWilliam A. Kennington III // 2 chars for every byte + 5 colons + Null byte
511285115cSWilliam A. Kennington III constexpr auto MAC_FORMAT_LENGTH = 6 * 2 + 5 + 1;
521285115cSWilliam A. Kennington III constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
531285115cSWilliam A. Kennington III constexpr auto NETWORK_SERVICE = "xyz.openbmc_project.Network";
541285115cSWilliam A. Kennington III constexpr auto PROP_INTERFACE = "org.freedesktop.DBus.Properties";
551285115cSWilliam A. Kennington III 
561285115cSWilliam A. Kennington III std::string format_mac(const mac_addr_t& mac)
571285115cSWilliam A. Kennington III {
581285115cSWilliam A. Kennington III     // 2 chars for every byte + 5 colons + Null byte
591285115cSWilliam A. Kennington III     char mac_str[MAC_FORMAT_LENGTH];
601285115cSWilliam A. Kennington III     snprintf(mac_str, sizeof(mac_str), MAC_FORMAT, mac.octet[0], mac.octet[1],
611285115cSWilliam A. Kennington III              mac.octet[2], mac.octet[3], mac.octet[4], mac.octet[5]);
621285115cSWilliam A. Kennington III 
631285115cSWilliam A. Kennington III     return std::string{mac_str};
641285115cSWilliam A. Kennington III }
651285115cSWilliam A. Kennington III 
661285115cSWilliam A. Kennington III } // namespace
671285115cSWilliam A. Kennington III 
681285115cSWilliam A. Kennington III PhosphorConfig::PhosphorConfig(const std::string& iface_name) :
691285115cSWilliam A. Kennington III     iface_name_{iface_name}, iface_path_{std::string(IFACE_ROOT) + iface_name},
701285115cSWilliam A. Kennington III     shared_host_mac_(std::experimental::nullopt),
711285115cSWilliam A. Kennington III     bus(sdbusplus::bus::new_default())
721285115cSWilliam A. Kennington III {}
731285115cSWilliam A. Kennington III 
741285115cSWilliam A. Kennington III int PhosphorConfig::get_mac_addr(mac_addr_t* mac)
751285115cSWilliam A. Kennington III {
761285115cSWilliam A. Kennington III     if (mac == nullptr)
771285115cSWilliam A. Kennington III     {
78253e6463SWilly Tu         stdplus::println(stderr, "mac is nullptr");
791285115cSWilliam A. Kennington III         return -1;
801285115cSWilliam A. Kennington III     }
811285115cSWilliam A. Kennington III 
821285115cSWilliam A. Kennington III     // Cache hit: we have stored host MAC.
831285115cSWilliam A. Kennington III     if (shared_host_mac_)
841285115cSWilliam A. Kennington III     {
851285115cSWilliam A. Kennington III         *mac = shared_host_mac_.value();
861285115cSWilliam A. Kennington III     }
87*d94dc257SMo Elbadry     else // Cache miss: read from interface, cache it for future requests.
881285115cSWilliam A. Kennington III     {
89*d94dc257SMo Elbadry         struct ifreq ifr = {};
901285115cSWilliam A. Kennington III         try
911285115cSWilliam A. Kennington III         {
92*d94dc257SMo Elbadry             auto fd = stdplus::fd::socket(stdplus::fd::SocketDomain::INet6,
93*d94dc257SMo Elbadry                                           stdplus::fd::SocketType::Datagram,
94*d94dc257SMo Elbadry                                           stdplus::fd::SocketProto::IP);
95*d94dc257SMo Elbadry             call_nic(fd, ifr, SIOCGIFHWADDR);
961285115cSWilliam A. Kennington III         }
97*d94dc257SMo Elbadry         catch (const std::exception& ex)
981285115cSWilliam A. Kennington III         {
99*d94dc257SMo Elbadry             stdplus::println(
100*d94dc257SMo Elbadry                 stderr,
101*d94dc257SMo Elbadry                 "Failed to get MAC Addr for Interface {} writing file: {}",
102*d94dc257SMo Elbadry                 iface_name_, ex.what());
1031285115cSWilliam A. Kennington III             return -1;
1041285115cSWilliam A. Kennington III         }
105*d94dc257SMo Elbadry         std::copy_n(ifr.ifr_addr.sa_data, sizeof(*mac), mac->octet);
1061285115cSWilliam A. Kennington III         shared_host_mac_ = *mac;
1071285115cSWilliam A. Kennington III     }
1081285115cSWilliam A. Kennington III     return 0;
1091285115cSWilliam A. Kennington III }
1101285115cSWilliam A. Kennington III 
111*d94dc257SMo Elbadry void PhosphorConfig::call_nic(auto fd, struct ifreq& ifr, int op)
112*d94dc257SMo Elbadry {
113*d94dc257SMo Elbadry     std::copy_n(iface_name_.c_str(), iface_name_.size() + 1, ifr.ifr_name);
114*d94dc257SMo Elbadry     fd.ioctl(op, &ifr);
115*d94dc257SMo Elbadry }
116*d94dc257SMo Elbadry 
1171285115cSWilliam A. Kennington III int PhosphorConfig::set_mac_addr(const mac_addr_t& mac)
1181285115cSWilliam A. Kennington III {
1191285115cSWilliam A. Kennington III     std::variant<std::string> mac_value(format_mac(mac));
120*d94dc257SMo Elbadry     struct ifreq ifr = {};
121*d94dc257SMo Elbadry     short flags_copy;
1221285115cSWilliam A. Kennington III 
1231285115cSWilliam A. Kennington III     try
1241285115cSWilliam A. Kennington III     {
125bb53161dSWilly Tu         auto netdir = std::format("/run/systemd/network/00-bmc-{}.network.d",
126a5c9d7a4SWilliam A. Kennington III                                   iface_name_);
127a5c9d7a4SWilliam A. Kennington III         std::filesystem::create_directories(netdir);
128bb53161dSWilly Tu         auto netfile = std::format("{}/60-ncsi-mac.conf", netdir);
129a5c9d7a4SWilliam A. Kennington III         auto fd = stdplus::fd::open(
130a5c9d7a4SWilliam A. Kennington III             netfile,
131a5c9d7a4SWilliam A. Kennington III             stdplus::fd::OpenFlags(stdplus::fd::OpenAccess::WriteOnly)
132a5c9d7a4SWilliam A. Kennington III                 .set(stdplus::fd::OpenFlag::Create),
133a5c9d7a4SWilliam A. Kennington III             0644);
134bb53161dSWilly Tu         auto contents = std::format("[Link]\nMACAddress={}\n",
135a5c9d7a4SWilliam A. Kennington III                                     std::get<std::string>(mac_value));
136a5c9d7a4SWilliam A. Kennington III         stdplus::fd::writeExact(fd, contents);
137a5c9d7a4SWilliam A. Kennington III     }
138a5c9d7a4SWilliam A. Kennington III     catch (const std::exception& ex)
139a5c9d7a4SWilliam A. Kennington III     {
140253e6463SWilly Tu         stdplus::println(stderr, "Failed to set MAC Addr `{}` writing file: {}",
141a5c9d7a4SWilliam A. Kennington III                          std::get<std::string>(mac_value), ex.what());
142a5c9d7a4SWilliam A. Kennington III         return -1;
143a5c9d7a4SWilliam A. Kennington III     }
144a5c9d7a4SWilliam A. Kennington III     try
145a5c9d7a4SWilliam A. Kennington III     {
146*d94dc257SMo Elbadry         auto fd = stdplus::fd::socket(stdplus::fd::SocketDomain::INet6,
147*d94dc257SMo Elbadry                                       stdplus::fd::SocketType::Datagram,
148*d94dc257SMo Elbadry                                       stdplus::fd::SocketProto::IP);
149*d94dc257SMo Elbadry         // Try setting MAC Address directly without bringing interface down
150*d94dc257SMo Elbadry         try
1511285115cSWilliam A. Kennington III         {
152*d94dc257SMo Elbadry             std::copy_n(mac.octet, 6, ifr.ifr_hwaddr.sa_data);
153*d94dc257SMo Elbadry             call_nic(fd, ifr, SIOCSIFHWADDR);
154*d94dc257SMo Elbadry         }
155*d94dc257SMo Elbadry         catch (const std::exception& e)
156*d94dc257SMo Elbadry         {
157*d94dc257SMo Elbadry             // Regardless of error attempt to set MAC Address again after
158*d94dc257SMo Elbadry             // bringing interface down
159*d94dc257SMo Elbadry             stdplus::println(
160*d94dc257SMo Elbadry                 stderr,
161*d94dc257SMo Elbadry                 "Could not set MAC Address directly, retrying after bringing interface down, error = {}",
162*d94dc257SMo Elbadry                 e.what());
163*d94dc257SMo Elbadry             try
164*d94dc257SMo Elbadry             {
165*d94dc257SMo Elbadry                 // Read interface flags configuration and store (once interface
166*d94dc257SMo Elbadry                 // is brought down, existing state is lost)
167*d94dc257SMo Elbadry                 call_nic(fd, ifr, SIOCGIFFLAGS);
168*d94dc257SMo Elbadry                 flags_copy = ifr.ifr_flags;
169*d94dc257SMo Elbadry                 // set interface down
170*d94dc257SMo Elbadry                 ifr.ifr_flags &= ~IFF_UP;
171*d94dc257SMo Elbadry                 call_nic(fd, ifr, SIOCSIFFLAGS);
172*d94dc257SMo Elbadry                 // Wait for 1 milliseconds - sometimes interface is still
173*d94dc257SMo Elbadry                 // going down
174*d94dc257SMo Elbadry                 std::this_thread::sleep_for(std::chrono::milliseconds(1));
175*d94dc257SMo Elbadry                 // set MAC Address
176*d94dc257SMo Elbadry                 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
177*d94dc257SMo Elbadry                 std::copy_n(mac.octet, 6, ifr.ifr_hwaddr.sa_data);
178*d94dc257SMo Elbadry                 call_nic(fd, ifr, SIOCSIFHWADDR);
179*d94dc257SMo Elbadry                 // set interface up with the flags state prior to bringing
180*d94dc257SMo Elbadry                 // it down
181*d94dc257SMo Elbadry                 ifr.ifr_flags = flags_copy | IFF_UP;
182*d94dc257SMo Elbadry                 call_nic(fd, ifr, SIOCSIFFLAGS);
183*d94dc257SMo Elbadry             }
184*d94dc257SMo Elbadry             catch (const std::exception& e)
185*d94dc257SMo Elbadry             {
186*d94dc257SMo Elbadry                 stdplus::println(
187*d94dc257SMo Elbadry                     stderr, "Failed to set MAC Address {} writing file: {}",
188*d94dc257SMo Elbadry                     std::get<std::string>(mac_value), e.what());
1891285115cSWilliam A. Kennington III                 return -1;
1901285115cSWilliam A. Kennington III             }
191*d94dc257SMo Elbadry         }
192*d94dc257SMo Elbadry     }
193*d94dc257SMo Elbadry     catch (const std::exception& e)
194*d94dc257SMo Elbadry     {
195*d94dc257SMo Elbadry         stdplus::println(stderr, "Error creating socket: {}", e.what());
196*d94dc257SMo Elbadry         return -1;
197*d94dc257SMo Elbadry     }
198*d94dc257SMo Elbadry     stdplus::println(stderr, "Success setting Mac address for {}: {}",
199*d94dc257SMo Elbadry                      iface_name_, std::get<std::string>(mac_value));
2001285115cSWilliam A. Kennington III     shared_host_mac_ = std::experimental::nullopt;
2011285115cSWilliam A. Kennington III     return 0;
2021285115cSWilliam A. Kennington III }
2031285115cSWilliam A. Kennington III 
2041285115cSWilliam A. Kennington III int PhosphorConfig::set_nic_hostless(bool is_nic_hostless)
2051285115cSWilliam A. Kennington III {
2061285115cSWilliam A. Kennington III     // Ensure that we don't trigger the target multiple times. This is
2071285115cSWilliam A. Kennington III     // undesirable because it will cause any inactive services to re-trigger
2081285115cSWilliam A. Kennington III     // every time we run this code. Since the loop calling this executes this
2091285115cSWilliam A. Kennington III     // code every 1s, we don't want to keep re-executing services. A fresh
2101285115cSWilliam A. Kennington III     // start of the daemon will always trigger the service to ensure system
2111285115cSWilliam A. Kennington III     // consistency.
2121285115cSWilliam A. Kennington III     if (was_nic_hostless_ && is_nic_hostless == *was_nic_hostless_)
2131285115cSWilliam A. Kennington III     {
2141285115cSWilliam A. Kennington III         return 0;
2151285115cSWilliam A. Kennington III     }
2161285115cSWilliam A. Kennington III 
2171285115cSWilliam A. Kennington III     static constexpr auto systemdService = "org.freedesktop.systemd1";
2181285115cSWilliam A. Kennington III     static constexpr auto systemdRoot = "/org/freedesktop/systemd1";
2191285115cSWilliam A. Kennington III     static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
2201285115cSWilliam A. Kennington III 
2211285115cSWilliam A. Kennington III     auto method = bus.new_method_call(systemdService, systemdRoot,
2221285115cSWilliam A. Kennington III                                       systemdInterface, "StartUnit");
2231285115cSWilliam A. Kennington III     if (is_nic_hostless)
2241285115cSWilliam A. Kennington III     {
2251285115cSWilliam A. Kennington III         method.append(
2261285115cSWilliam A. Kennington III             stdplus::util::strCat("nic-hostless@", iface_name_, ".target"));
2271285115cSWilliam A. Kennington III     }
2281285115cSWilliam A. Kennington III     else
2291285115cSWilliam A. Kennington III     {
2301285115cSWilliam A. Kennington III         method.append(
2311285115cSWilliam A. Kennington III             stdplus::util::strCat("nic-hostful@", iface_name_, ".target"));
2321285115cSWilliam A. Kennington III     }
2331285115cSWilliam A. Kennington III 
2341285115cSWilliam A. Kennington III     // Specify --job-mode (see systemctl(1) for detail).
2351285115cSWilliam A. Kennington III     method.append("replace");
2361285115cSWilliam A. Kennington III 
2371285115cSWilliam A. Kennington III     try
2381285115cSWilliam A. Kennington III     {
2391285115cSWilliam A. Kennington III         bus.call_noreply(method);
2401285115cSWilliam A. Kennington III         was_nic_hostless_ = is_nic_hostless;
2411285115cSWilliam A. Kennington III         return 0;
2421285115cSWilliam A. Kennington III     }
2431285115cSWilliam A. Kennington III     catch (const sdbusplus::exception::SdBusError& ex)
2441285115cSWilliam A. Kennington III     {
245253e6463SWilly Tu         stdplus::println(stderr, "Failed to set systemd nic status: {}",
246253e6463SWilly Tu                          ex.what());
2471285115cSWilliam A. Kennington III         return 1;
2481285115cSWilliam A. Kennington III     }
2491285115cSWilliam A. Kennington III }
2501285115cSWilliam A. Kennington III 
2511285115cSWilliam A. Kennington III } // namespace net
252