blob: f5ffc9b23fe428460bb70fe4e88fc57305ab4e89 [file] [log] [blame]
William A. Kennington III7d6fa422021-02-08 17:04:02 -08001#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
20namespace net
21{
22
23namespace
24{
25
26constexpr auto IFACE_ROOT = "/xyz/openbmc_project/network/";
27constexpr auto MAC_FORMAT = "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx";
28// 2 chars for every byte + 5 colons + Null byte
29constexpr auto MAC_FORMAT_LENGTH = 6 * 2 + 5 + 1;
30constexpr auto MAC_INTERFACE = "xyz.openbmc_project.Network.MACAddress";
31constexpr auto NETWORK_SERVICE = "xyz.openbmc_project.Network";
32constexpr auto PROP_INTERFACE = "org.freedesktop.DBus.Properties";
33
34int 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
43std::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
55PhosphorConfig::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
61sdbusplus::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
73int 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
116int 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
137int 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