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