xref: /openbmc/google-misc/subprojects/ncsid/src/net_config.cpp (revision 6461f396bb82c9431031bb4e2425f5c83bfcc0c0)
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 
17d94dc257SMo Elbadry #include <linux/if.h>
18d94dc257SMo Elbadry #include <net/if_arp.h>
19d94dc257SMo 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>
28*6461f396SMo Elbadry #include <stdplus/raw.hpp>
291285115cSWilliam A. Kennington III #include <stdplus/util/string.hpp>
301285115cSWilliam A. Kennington III 
311285115cSWilliam A. Kennington III #include <cstdio>
321285115cSWilliam A. Kennington III #include <cstring>
33a5c9d7a4SWilliam A. Kennington III #include <filesystem>
34bb53161dSWilly Tu #include <format>
35d94dc257SMo Elbadry #include <thread>
361285115cSWilliam A. Kennington III #include <utility>
371285115cSWilliam A. Kennington III #include <variant>
381285115cSWilliam A. Kennington III 
391285115cSWilliam A. Kennington III /* Most of the code for interacting with DBus is from
401285115cSWilliam A. Kennington III  * phosphor-host-ipmid/utils.cpp
411285115cSWilliam A. Kennington III  */
421285115cSWilliam A. Kennington III 
431285115cSWilliam A. Kennington III namespace net
441285115cSWilliam A. Kennington III {
451285115cSWilliam A. Kennington III 
461285115cSWilliam A. Kennington III namespace
471285115cSWilliam A. Kennington III {
481285115cSWilliam A. Kennington III 
491285115cSWilliam A. Kennington III constexpr auto IFACE_ROOT = "/xyz/openbmc_project/network/";
501285115cSWilliam A. Kennington III constexpr auto MAC_FORMAT = "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx";
511285115cSWilliam A. Kennington III // 2 chars for every byte + 5 colons + Null byte
521285115cSWilliam A. Kennington III constexpr auto MAC_FORMAT_LENGTH = 6 * 2 + 5 + 1;
531285115cSWilliam A. Kennington III constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
541285115cSWilliam A. Kennington III constexpr auto NETWORK_SERVICE = "xyz.openbmc_project.Network";
551285115cSWilliam A. Kennington III constexpr auto PROP_INTERFACE = "org.freedesktop.DBus.Properties";
561285115cSWilliam A. Kennington III 
format_mac(const mac_addr_t & mac)571285115cSWilliam A. Kennington III std::string format_mac(const mac_addr_t& mac)
581285115cSWilliam A. Kennington III {
591285115cSWilliam A. Kennington III     // 2 chars for every byte + 5 colons + Null byte
601285115cSWilliam A. Kennington III     char mac_str[MAC_FORMAT_LENGTH];
611285115cSWilliam A. Kennington III     snprintf(mac_str, sizeof(mac_str), MAC_FORMAT, mac.octet[0], mac.octet[1],
621285115cSWilliam A. Kennington III              mac.octet[2], mac.octet[3], mac.octet[4], mac.octet[5]);
631285115cSWilliam A. Kennington III 
641285115cSWilliam A. Kennington III     return std::string{mac_str};
651285115cSWilliam A. Kennington III }
661285115cSWilliam A. Kennington III 
671285115cSWilliam A. Kennington III } // namespace
681285115cSWilliam A. Kennington III 
PhosphorConfig(const std::string & iface_name)691285115cSWilliam A. Kennington III PhosphorConfig::PhosphorConfig(const std::string& iface_name) :
701285115cSWilliam A. Kennington III     iface_name_{iface_name}, iface_path_{std::string(IFACE_ROOT) + iface_name},
711285115cSWilliam A. Kennington III     shared_host_mac_(std::experimental::nullopt),
721285115cSWilliam A. Kennington III     bus(sdbusplus::bus::new_default())
731285115cSWilliam A. Kennington III {}
741285115cSWilliam A. Kennington III 
get_mac_addr(mac_addr_t * mac)751285115cSWilliam A. Kennington III int PhosphorConfig::get_mac_addr(mac_addr_t* mac)
761285115cSWilliam A. Kennington III {
771285115cSWilliam A. Kennington III     if (mac == nullptr)
781285115cSWilliam A. Kennington III     {
79253e6463SWilly Tu         stdplus::println(stderr, "mac is nullptr");
801285115cSWilliam A. Kennington III         return -1;
811285115cSWilliam A. Kennington III     }
821285115cSWilliam A. Kennington III 
831285115cSWilliam A. Kennington III     // Cache hit: we have stored host MAC.
841285115cSWilliam A. Kennington III     if (shared_host_mac_)
851285115cSWilliam A. Kennington III     {
861285115cSWilliam A. Kennington III         *mac = shared_host_mac_.value();
871285115cSWilliam A. Kennington III     }
88d94dc257SMo Elbadry     else // Cache miss: read from interface, cache it for future requests.
891285115cSWilliam A. Kennington III     {
90d94dc257SMo Elbadry         struct ifreq ifr = {};
911285115cSWilliam A. Kennington III         try
921285115cSWilliam A. Kennington III         {
93d94dc257SMo Elbadry             auto fd = stdplus::fd::socket(stdplus::fd::SocketDomain::INet6,
94d94dc257SMo Elbadry                                           stdplus::fd::SocketType::Datagram,
95d94dc257SMo Elbadry                                           stdplus::fd::SocketProto::IP);
96d94dc257SMo Elbadry             call_nic(fd, ifr, SIOCGIFHWADDR);
971285115cSWilliam A. Kennington III         }
98d94dc257SMo Elbadry         catch (const std::exception& ex)
991285115cSWilliam A. Kennington III         {
100d94dc257SMo Elbadry             stdplus::println(
101d94dc257SMo Elbadry                 stderr,
102d94dc257SMo Elbadry                 "Failed to get MAC Addr for Interface {} writing file: {}",
103d94dc257SMo Elbadry                 iface_name_, ex.what());
1041285115cSWilliam A. Kennington III             return -1;
1051285115cSWilliam A. Kennington III         }
106d94dc257SMo Elbadry         std::copy_n(ifr.ifr_addr.sa_data, sizeof(*mac), mac->octet);
1071285115cSWilliam A. Kennington III         shared_host_mac_ = *mac;
1081285115cSWilliam A. Kennington III     }
1091285115cSWilliam A. Kennington III     return 0;
1101285115cSWilliam A. Kennington III }
1111285115cSWilliam A. Kennington III 
call_nic(auto fd,struct ifreq & ifr,int op)112d94dc257SMo Elbadry void PhosphorConfig::call_nic(auto fd, struct ifreq& ifr, int op)
113d94dc257SMo Elbadry {
114d94dc257SMo Elbadry     std::copy_n(iface_name_.c_str(), iface_name_.size() + 1, ifr.ifr_name);
115d94dc257SMo Elbadry     fd.ioctl(op, &ifr);
116d94dc257SMo Elbadry }
117d94dc257SMo Elbadry 
set_mac_addr(const mac_addr_t & mac)1181285115cSWilliam A. Kennington III int PhosphorConfig::set_mac_addr(const mac_addr_t& mac)
1191285115cSWilliam A. Kennington III {
120*6461f396SMo Elbadry     std::string mac_value = format_mac(mac);
121d94dc257SMo Elbadry     struct ifreq ifr = {};
122d94dc257SMo Elbadry     short flags_copy;
123*6461f396SMo Elbadry     mac_addr_t cur_mac;
124*6461f396SMo Elbadry     int ret;
125*6461f396SMo Elbadry     ret = get_mac_addr(&cur_mac);
126*6461f396SMo Elbadry     if (ret == 0)
127*6461f396SMo Elbadry     {
128*6461f396SMo Elbadry         if (stdplus::raw::equal(cur_mac, mac))
129*6461f396SMo Elbadry         {
130*6461f396SMo Elbadry             // mac value is the same not doing anything, returning
131*6461f396SMo Elbadry             return 0;
132*6461f396SMo Elbadry         }
133*6461f396SMo Elbadry     }
1341285115cSWilliam A. Kennington III 
1351285115cSWilliam A. Kennington III     try
1361285115cSWilliam A. Kennington III     {
137bb53161dSWilly Tu         auto netdir = std::format("/run/systemd/network/00-bmc-{}.network.d",
138a5c9d7a4SWilliam A. Kennington III                                   iface_name_);
139a5c9d7a4SWilliam A. Kennington III         std::filesystem::create_directories(netdir);
140bb53161dSWilly Tu         auto netfile = std::format("{}/60-ncsi-mac.conf", netdir);
141a5c9d7a4SWilliam A. Kennington III         auto fd = stdplus::fd::open(
142a5c9d7a4SWilliam A. Kennington III             netfile,
143a5c9d7a4SWilliam A. Kennington III             stdplus::fd::OpenFlags(stdplus::fd::OpenAccess::WriteOnly)
144a5c9d7a4SWilliam A. Kennington III                 .set(stdplus::fd::OpenFlag::Create),
145a5c9d7a4SWilliam A. Kennington III             0644);
146*6461f396SMo Elbadry         auto contents = std::format("[Link]\nMACAddress={}\n", mac_value);
147a5c9d7a4SWilliam A. Kennington III         stdplus::fd::writeExact(fd, contents);
148a5c9d7a4SWilliam A. Kennington III     }
149a5c9d7a4SWilliam A. Kennington III     catch (const std::exception& ex)
150a5c9d7a4SWilliam A. Kennington III     {
151253e6463SWilly Tu         stdplus::println(stderr, "Failed to set MAC Addr `{}` writing file: {}",
152*6461f396SMo Elbadry                          mac_value, ex.what());
153a5c9d7a4SWilliam A. Kennington III         return -1;
154a5c9d7a4SWilliam A. Kennington III     }
155a5c9d7a4SWilliam A. Kennington III     try
156a5c9d7a4SWilliam A. Kennington III     {
157d94dc257SMo Elbadry         auto fd = stdplus::fd::socket(stdplus::fd::SocketDomain::INet6,
158d94dc257SMo Elbadry                                       stdplus::fd::SocketType::Datagram,
159d94dc257SMo Elbadry                                       stdplus::fd::SocketProto::IP);
160d94dc257SMo Elbadry         // Try setting MAC Address directly without bringing interface down
161d94dc257SMo Elbadry         try
1621285115cSWilliam A. Kennington III         {
163d94dc257SMo Elbadry             std::copy_n(mac.octet, 6, ifr.ifr_hwaddr.sa_data);
164d94dc257SMo Elbadry             call_nic(fd, ifr, SIOCSIFHWADDR);
165d94dc257SMo Elbadry         }
166d94dc257SMo Elbadry         catch (const std::exception& e)
167d94dc257SMo Elbadry         {
168d94dc257SMo Elbadry             // Regardless of error attempt to set MAC Address again after
169d94dc257SMo Elbadry             // bringing interface down
170d94dc257SMo Elbadry             stdplus::println(
171d94dc257SMo Elbadry                 stderr,
172d94dc257SMo Elbadry                 "Could not set MAC Address directly, retrying after bringing interface down, error = {}",
173d94dc257SMo Elbadry                 e.what());
174d94dc257SMo Elbadry             try
175d94dc257SMo Elbadry             {
176d94dc257SMo Elbadry                 // Read interface flags configuration and store (once interface
177d94dc257SMo Elbadry                 // is brought down, existing state is lost)
178d94dc257SMo Elbadry                 call_nic(fd, ifr, SIOCGIFFLAGS);
179d94dc257SMo Elbadry                 flags_copy = ifr.ifr_flags;
180d94dc257SMo Elbadry                 // set interface down
181d94dc257SMo Elbadry                 ifr.ifr_flags &= ~IFF_UP;
182d94dc257SMo Elbadry                 call_nic(fd, ifr, SIOCSIFFLAGS);
183d94dc257SMo Elbadry                 // Wait for 1 milliseconds - sometimes interface is still
184d94dc257SMo Elbadry                 // going down
185d94dc257SMo Elbadry                 std::this_thread::sleep_for(std::chrono::milliseconds(1));
186d94dc257SMo Elbadry                 // set MAC Address
187d94dc257SMo Elbadry                 ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
188d94dc257SMo Elbadry                 std::copy_n(mac.octet, 6, ifr.ifr_hwaddr.sa_data);
189d94dc257SMo Elbadry                 call_nic(fd, ifr, SIOCSIFHWADDR);
190d94dc257SMo Elbadry                 // set interface up with the flags state prior to bringing
191d94dc257SMo Elbadry                 // it down
192d94dc257SMo Elbadry                 ifr.ifr_flags = flags_copy | IFF_UP;
193d94dc257SMo Elbadry                 call_nic(fd, ifr, SIOCSIFFLAGS);
194d94dc257SMo Elbadry             }
195d94dc257SMo Elbadry             catch (const std::exception& e)
196d94dc257SMo Elbadry             {
197d94dc257SMo Elbadry                 stdplus::println(
198d94dc257SMo Elbadry                     stderr, "Failed to set MAC Address {} writing file: {}",
199*6461f396SMo Elbadry                     mac_value, e.what());
2001285115cSWilliam A. Kennington III                 return -1;
2011285115cSWilliam A. Kennington III             }
202d94dc257SMo Elbadry         }
203d94dc257SMo Elbadry     }
204d94dc257SMo Elbadry     catch (const std::exception& e)
205d94dc257SMo Elbadry     {
206d94dc257SMo Elbadry         stdplus::println(stderr, "Error creating socket: {}", e.what());
207d94dc257SMo Elbadry         return -1;
208d94dc257SMo Elbadry     }
209d94dc257SMo Elbadry     stdplus::println(stderr, "Success setting Mac address for {}: {}",
210*6461f396SMo Elbadry                      iface_name_, mac_value);
2111285115cSWilliam A. Kennington III     shared_host_mac_ = std::experimental::nullopt;
2121285115cSWilliam A. Kennington III     return 0;
2131285115cSWilliam A. Kennington III }
2141285115cSWilliam A. Kennington III 
set_nic_hostless(bool is_nic_hostless)2151285115cSWilliam A. Kennington III int PhosphorConfig::set_nic_hostless(bool is_nic_hostless)
2161285115cSWilliam A. Kennington III {
2171285115cSWilliam A. Kennington III     // Ensure that we don't trigger the target multiple times. This is
2181285115cSWilliam A. Kennington III     // undesirable because it will cause any inactive services to re-trigger
2191285115cSWilliam A. Kennington III     // every time we run this code. Since the loop calling this executes this
2201285115cSWilliam A. Kennington III     // code every 1s, we don't want to keep re-executing services. A fresh
2211285115cSWilliam A. Kennington III     // start of the daemon will always trigger the service to ensure system
2221285115cSWilliam A. Kennington III     // consistency.
2231285115cSWilliam A. Kennington III     if (was_nic_hostless_ && is_nic_hostless == *was_nic_hostless_)
2241285115cSWilliam A. Kennington III     {
2251285115cSWilliam A. Kennington III         return 0;
2261285115cSWilliam A. Kennington III     }
2271285115cSWilliam A. Kennington III 
2281285115cSWilliam A. Kennington III     static constexpr auto systemdService = "org.freedesktop.systemd1";
2291285115cSWilliam A. Kennington III     static constexpr auto systemdRoot = "/org/freedesktop/systemd1";
2301285115cSWilliam A. Kennington III     static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager";
2311285115cSWilliam A. Kennington III 
2321285115cSWilliam A. Kennington III     auto method = bus.new_method_call(systemdService, systemdRoot,
2331285115cSWilliam A. Kennington III                                       systemdInterface, "StartUnit");
2341285115cSWilliam A. Kennington III     if (is_nic_hostless)
2351285115cSWilliam A. Kennington III     {
2361285115cSWilliam A. Kennington III         method.append(
2371285115cSWilliam A. Kennington III             stdplus::util::strCat("nic-hostless@", iface_name_, ".target"));
2381285115cSWilliam A. Kennington III     }
2391285115cSWilliam A. Kennington III     else
2401285115cSWilliam A. Kennington III     {
2411285115cSWilliam A. Kennington III         method.append(
2421285115cSWilliam A. Kennington III             stdplus::util::strCat("nic-hostful@", iface_name_, ".target"));
2431285115cSWilliam A. Kennington III     }
2441285115cSWilliam A. Kennington III 
2451285115cSWilliam A. Kennington III     // Specify --job-mode (see systemctl(1) for detail).
2461285115cSWilliam A. Kennington III     method.append("replace");
2471285115cSWilliam A. Kennington III 
2481285115cSWilliam A. Kennington III     try
2491285115cSWilliam A. Kennington III     {
2501285115cSWilliam A. Kennington III         bus.call_noreply(method);
2511285115cSWilliam A. Kennington III         was_nic_hostless_ = is_nic_hostless;
2521285115cSWilliam A. Kennington III         return 0;
2531285115cSWilliam A. Kennington III     }
2541285115cSWilliam A. Kennington III     catch (const sdbusplus::exception::SdBusError& ex)
2551285115cSWilliam A. Kennington III     {
256253e6463SWilly Tu         stdplus::println(stderr, "Failed to set systemd nic status: {}",
257253e6463SWilly Tu                          ex.what());
2581285115cSWilliam A. Kennington III         return 1;
2591285115cSWilliam A. Kennington III     }
2601285115cSWilliam A. Kennington III }
2611285115cSWilliam A. Kennington III 
2621285115cSWilliam A. Kennington III } // namespace net
263