1 #include "net_config.h" 2 3 #include <fmt/format.h> 4 #include <sys/types.h> 5 #include <sys/wait.h> 6 #include <unistd.h> 7 8 #include <sdbusplus/bus.hpp> 9 #include <stdplus/util/string.hpp> 10 11 #include <cstdio> 12 #include <cstring> 13 #include <utility> 14 #include <variant> 15 16 /* Most of the code for interacting with DBus is from 17 * phosphor-host-ipmid/utils.cpp 18 */ 19 20 namespace net 21 { 22 23 namespace 24 { 25 26 constexpr auto IFACE_ROOT = "/xyz/openbmc_project/network/"; 27 constexpr auto MAC_FORMAT = "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx"; 28 // 2 chars for every byte + 5 colons + Null byte 29 constexpr auto MAC_FORMAT_LENGTH = 6 * 2 + 5 + 1; 30 constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress"; 31 constexpr auto NETWORK_SERVICE = "xyz.openbmc_project.Network"; 32 constexpr auto PROP_INTERFACE = "org.freedesktop.DBus.Properties"; 33 34 int parse_mac(const std::string& mac_addr, mac_addr_t* mac) 35 { 36 int ret = 37 sscanf(mac_addr.c_str(), MAC_FORMAT, mac->octet, mac->octet + 1, 38 mac->octet + 2, mac->octet + 3, mac->octet + 4, mac->octet + 5); 39 40 return ret < 6 ? -1 : 0; 41 } 42 43 std::string format_mac(const mac_addr_t& mac) 44 { 45 // 2 chars for every byte + 5 colons + Null byte 46 char mac_str[MAC_FORMAT_LENGTH]; 47 snprintf(mac_str, sizeof(mac_str), MAC_FORMAT, mac.octet[0], mac.octet[1], 48 mac.octet[2], mac.octet[3], mac.octet[4], mac.octet[5]); 49 50 return std::string{mac_str}; 51 } 52 53 } // namespace 54 55 PhosphorConfig::PhosphorConfig(const std::string& iface_name) : 56 iface_name_{iface_name}, iface_path_{std::string(IFACE_ROOT) + iface_name}, 57 shared_host_mac_(std::experimental::nullopt), 58 bus(sdbusplus::bus::new_default()) 59 {} 60 61 sdbusplus::message::message 62 PhosphorConfig::new_networkd_call(sdbusplus::bus::bus* dbus, bool get) const 63 { 64 auto networkd_call = 65 dbus->new_method_call(NETWORK_SERVICE, iface_path_.c_str(), 66 PROP_INTERFACE, get ? "Get" : "Set"); 67 68 networkd_call.append(MAC_INTERFACE, "MACAddress"); 69 70 return networkd_call; 71 } 72 73 int PhosphorConfig::get_mac_addr(mac_addr_t* mac) 74 { 75 if (mac == nullptr) 76 { 77 fmt::print(stderr, "mac is nullptr\n"); 78 return -1; 79 } 80 81 // Cache hit: we have stored host MAC. 82 if (shared_host_mac_) 83 { 84 *mac = shared_host_mac_.value(); 85 } 86 else // Cache miss: read MAC over DBus, and store in cache. 87 { 88 std::string mac_string; 89 try 90 { 91 auto networkd_call = new_networkd_call(&bus, true); 92 auto reply = bus.call(networkd_call); 93 std::variant<std::string> result; 94 reply.read(result); 95 mac_string = std::get<std::string>(result); 96 } 97 catch (const sdbusplus::exception::SdBusError& ex) 98 { 99 fmt::print(stderr, "Failed to get MACAddress: {}\n", ex.what()); 100 return -1; 101 } 102 103 if (parse_mac(mac_string, mac) < 0) 104 { 105 fmt::print(stderr, "Failed to parse MAC Address `{}`\n", 106 mac_string); 107 return -1; 108 } 109 110 shared_host_mac_ = *mac; 111 } 112 113 return 0; 114 } 115 116 int PhosphorConfig::set_mac_addr(const mac_addr_t& mac) 117 { 118 auto networkd_call = new_networkd_call(&bus, false); 119 std::variant<std::string> mac_value(format_mac(mac)); 120 networkd_call.append(mac_value); 121 122 try 123 { 124 auto reply = bus.call(networkd_call); 125 } 126 catch (const sdbusplus::exception::SdBusError& ex) 127 { 128 fmt::print(stderr, "Failed to set MAC Addr `{}`: {}\n", 129 std::get<std::string>(mac_value), ex.what()); 130 return -1; 131 } 132 133 shared_host_mac_ = std::experimental::nullopt; 134 return 0; 135 } 136 137 int PhosphorConfig::set_nic_hostless(bool is_nic_hostless) 138 { 139 // Ensure that we don't trigger the target multiple times. This is 140 // undesirable because it will cause any inactive services to re-trigger 141 // every time we run this code. Since the loop calling this executes this 142 // code every 1s, we don't want to keep re-executing services. A fresh 143 // start of the daemon will always trigger the service to ensure system 144 // consistency. 145 if (was_nic_hostless_ && is_nic_hostless == *was_nic_hostless_) 146 { 147 return 0; 148 } 149 150 static constexpr auto systemdService = "org.freedesktop.systemd1"; 151 static constexpr auto systemdRoot = "/org/freedesktop/systemd1"; 152 static constexpr auto systemdInterface = "org.freedesktop.systemd1.Manager"; 153 154 auto method = bus.new_method_call(systemdService, systemdRoot, 155 systemdInterface, "StartUnit"); 156 if (is_nic_hostless) 157 { 158 method.append( 159 stdplus::util::strCat("nic-hostless@", iface_name_, ".target")); 160 } 161 else 162 { 163 method.append( 164 stdplus::util::strCat("nic-hostful@", iface_name_, ".target")); 165 } 166 167 // Specify --job-mode (see systemctl(1) for detail). 168 method.append("replace"); 169 170 try 171 { 172 bus.call_noreply(method); 173 was_nic_hostless_ = is_nic_hostless; 174 return 0; 175 } 176 catch (const sdbusplus::exception::SdBusError& ex) 177 { 178 fmt::print(stderr, "Failed to set systemd nic status: {}\n", ex.what()); 179 return 1; 180 } 181 } 182 183 } // namespace net 184