Skip to content

Commit b11bc76

Browse files
committed
Introduce a netlink alloc driver .
We're now in the position to introduce a netlink driver. This driver allows cvdalloc to (transitively) set up nearly all of the network allocation and deallocation steps in a faster, more idiomatic way than shelling out to ip(8). Only one driver operation still requires shelling out to iptables(8). We can potentially use NETLINK_NETFILTER to do this in the future but this is actually somewhat more painful and involved than using netlink for the other driver operations. Maybe we can do this in the future; left as a TODO for now. To use the driver until the default is changed, one can rebuild cvd with the `--//allocd:driver=netlink` custom bazel flag; without the flag, the the iproute2 driver is the default, for reasons mentioned in earlier commits.
1 parent 3abdca5 commit b11bc76

File tree

5 files changed

+355
-30
lines changed

5 files changed

+355
-30
lines changed

base/cvd/allocd/BUILD.bazel

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,28 @@
1+
load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
2+
13
package(
24
default_visibility = ["//:android_cuttlefish"],
35
)
46

7+
string_flag(
8+
name = "driver",
9+
build_setting_default = "iproute2",
10+
)
11+
12+
config_setting(
13+
name = "netlink",
14+
flag_values = {
15+
":driver": "netlink"
16+
}
17+
)
18+
19+
config_setting(
20+
name = "iproute2",
21+
flag_values = {
22+
":driver": "iproute2"
23+
}
24+
)
25+
526
cc_library(
627
name = "alloc_utils",
728
srcs = [
@@ -10,8 +31,10 @@ cc_library(
1031
hdrs = [
1132
"alloc_utils.h",
1233
],
13-
deps = [
14-
":alloc_iproute2",
34+
deps = select({
35+
":netlink": [":alloc_netlink"],
36+
"//conditions:default": [":alloc_iproute2"],
37+
}) + [
1538
"//cuttlefish/common/libs/fs",
1639
"//cuttlefish/host/commands/cvd/utils:common",
1740
"//cuttlefish/host/libs/config:logging",
@@ -33,3 +56,23 @@ cc_library(
3356
"@abseil-cpp//absl/log",
3457
],
3558
)
59+
60+
cc_library(
61+
name = "alloc_netlink",
62+
srcs = [
63+
"alloc_netlink.cpp",
64+
],
65+
hdrs = [
66+
"alloc_driver.h",
67+
],
68+
deps = [
69+
"//allocd/net:netlink",
70+
"//cuttlefish/common/libs/fs",
71+
"//cuttlefish/result",
72+
"//libbase",
73+
"@abseil-cpp//absl/log",
74+
"@abseil-cpp//absl/log:check",
75+
"@abseil-cpp//absl/strings",
76+
"@abseil-cpp//absl/strings:str_format",
77+
],
78+
)

base/cvd/allocd/alloc_driver.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
namespace cuttlefish {
2121

22+
inline constexpr char kCvdNetworkGroupName[] = "cvdnetwork";
23+
2224
bool AddTapIface(std::string_view name);
2325
bool ShutdownIface(std::string_view name);
2426
bool BringUpIface(std::string_view name);

base/cvd/allocd/alloc_iproute2.cpp

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,34 +24,7 @@
2424

2525
namespace cuttlefish {
2626

27-
int RunExternalCommand(const std::string& command) {
28-
FILE* fp;
29-
LOG(INFO) << "Running external command: " << command;
30-
fp = popen(command.c_str(), "r");
31-
32-
if (fp == nullptr) {
33-
LOG(WARNING) << "Error running external command";
34-
return -1;
35-
}
36-
37-
int status = pclose(fp);
38-
int ret = -1;
39-
if (status == -1) {
40-
LOG(WARNING) << "pclose error";
41-
} else {
42-
if (WIFEXITED(status)) {
43-
LOG(INFO) << "child process exited normally";
44-
ret = WEXITSTATUS(status);
45-
} else if (WIFSIGNALED(status)) {
46-
int sig = WTERMSIG(status);
47-
LOG(WARNING) << "child process was terminated by signal "
48-
<< strsignal(sig) << " (" << sig << ")";
49-
} else {
50-
LOG(WARNING) << "child process did not terminate normally";
51-
}
52-
}
53-
return ret;
54-
}
27+
extern int RunExternalCommand(const std::string& name);
5528

5629
bool AddTapIface(std::string_view name) {
5730
std::stringstream ss;

base/cvd/allocd/alloc_netlink.cpp

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
/*
2+
* Copyright (C) 2020 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#include "allocd/alloc_driver.h"
17+
18+
#include <arpa/inet.h>
19+
#include <fcntl.h>
20+
#include <grp.h>
21+
#include <linux/if_tun.h>
22+
#include <linux/netlink.h>
23+
#include <linux/rtnetlink.h>
24+
#include <net/if.h>
25+
#include <net/if_arp.h>
26+
#include <netinet/in.h>
27+
#include <string.h>
28+
#include <sys/ioctl.h>
29+
#include <sys/socket.h>
30+
#include <sys/types.h>
31+
32+
#include <cstdint>
33+
#include <fstream>
34+
#include <string_view>
35+
36+
#include "absl/log/check.h"
37+
#include "absl/log/log.h"
38+
#include "absl/strings/numbers.h"
39+
#include "absl/strings/str_format.h"
40+
#include "absl/strings/strip.h"
41+
42+
#include "allocd/net/netlink_client.h"
43+
#include "cuttlefish/common/libs/fs/shared_fd.h"
44+
#include "cuttlefish/result/result.h"
45+
46+
namespace cuttlefish {
47+
48+
namespace {
49+
50+
Result<unsigned int> Index(std::string_view ifname) {
51+
unsigned int index = if_nametoindex(std::string(ifname).c_str());
52+
CF_EXPECT(index != 0, "Index: " << ifname);
53+
return index;
54+
}
55+
56+
int Prefix(std::string_view textual_netmask) {
57+
// Currently, the netmask argument is provided as, e.g., "/24".
58+
// TODO: Consider passing the number of prefix bits numerically, which
59+
// would require API changes across all drivers.
60+
std::string_view netmask = textual_netmask;
61+
CHECK(absl::ConsumePrefix(&netmask, "/"));
62+
63+
int prefix;
64+
CHECK(absl::SimpleAtoi(netmask, &prefix))
65+
<< "Prefix: couldn't get prefix from netmask: " << textual_netmask;
66+
return prefix;
67+
}
68+
69+
} // namespace
70+
71+
extern int RunExternalCommand(const std::string& name);
72+
73+
bool AddTapIface(std::string_view name) {
74+
SharedFD tunfd = SharedFD::Open("/dev/net/tun", O_RDWR | O_CLOEXEC);
75+
if (!tunfd->IsOpen()) {
76+
LOG(ERROR) << "AddTapIface: open: " << tunfd->StrError();
77+
return false;
78+
}
79+
80+
struct ifreq ifr;
81+
strlcpy(ifr.ifr_name, std::string(name).c_str(), IFNAMSIZ);
82+
ifr.ifr_flags = IFF_TAP | IFF_VNET_HDR;
83+
ifr.ifr_flags |= IFF_TUN_EXCL;
84+
int r = tunfd->Ioctl(TUNSETIFF, (void*)&ifr);
85+
if (r == -1) {
86+
LOG(ERROR) << "AddTapIface: TUNSETIFF: " << tunfd->StrError();
87+
return false;
88+
}
89+
90+
struct group* g = getgrnam(kCvdNetworkGroupName);
91+
if (g == NULL) {
92+
LOG(ERROR) << "AddTapIface: getgrnam: " << tunfd->StrError();
93+
return false;
94+
}
95+
96+
r = tunfd->Ioctl(TUNSETGROUP, (void*)(intptr_t)g->gr_gid);
97+
if (r == -1) {
98+
LOG(ERROR) << "AddTapIface: TUNSETGROUP: " << tunfd->StrError();
99+
return false;
100+
}
101+
102+
r = tunfd->Ioctl(TUNSETPERSIST, (void*)1);
103+
if (r == -1) {
104+
LOG(ERROR) << "AddTapIface: TUNSETPERSIST: " << tunfd->StrError();
105+
return false;
106+
}
107+
108+
tunfd->Close();
109+
110+
return true;
111+
}
112+
113+
bool ShutdownIface(std::string_view name) {
114+
VLOG(0) << "ShutdownIface: " << name;
115+
auto factory = NetlinkClientFactory::Default();
116+
std::unique_ptr<NetlinkClient> nl = factory->New(NETLINK_ROUTE);
117+
118+
NetlinkRequest req(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK);
119+
req.AddIfInfo(0, false);
120+
req.AddString(IFLA_IFNAME, std::string(name));
121+
bool res = nl->Send(req);
122+
if (!res) {
123+
LOG(ERROR) << "ShutdownIface: failed";
124+
}
125+
return res;
126+
}
127+
128+
bool BringUpIface(std::string_view name) {
129+
VLOG(0) << "BringUpIface: " << name;
130+
auto factory = NetlinkClientFactory::Default();
131+
std::unique_ptr<NetlinkClient> nl = factory->New(NETLINK_ROUTE);
132+
133+
NetlinkRequest req(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK);
134+
req.AddIfInfo(0, true);
135+
req.AddString(IFLA_IFNAME, std::string(name));
136+
bool res = nl->Send(req);
137+
if (!res) {
138+
LOG(ERROR) << "BringUpIface: failed";
139+
}
140+
return res;
141+
}
142+
143+
bool AddGateway(std::string_view name, std::string_view gateway,
144+
std::string_view netmask) {
145+
VLOG(0) << "AddGateway: " << name << ", " << gateway << netmask;
146+
auto index = Index(name);
147+
PCHECK(index.ok()) << "Index: " << name;
148+
149+
auto factory = NetlinkClientFactory::Default();
150+
std::unique_ptr<NetlinkClient> nl = factory->New(NETLINK_ROUTE);
151+
152+
NetlinkRequest req(RTM_NEWADDR, NLM_F_REQUEST | NLM_F_ACK);
153+
req.AddAddrInfo(*index, Prefix(netmask));
154+
in_addr_t inaddr = inet_addr(std::string(gateway).c_str());
155+
req.AddInAddr(IFA_LOCAL, &inaddr);
156+
req.AddInAddr(IFA_ADDRESS, &inaddr);
157+
158+
bool res = nl->Send(req);
159+
if (!res) {
160+
LOG(ERROR) << "AddGateway: failed";
161+
}
162+
return res;
163+
}
164+
165+
bool DestroyGateway(std::string_view name, std::string_view gateway,
166+
std::string_view netmask) {
167+
VLOG(0) << "DestroyGateway: " << name << ", " << gateway << netmask;
168+
auto index = Index(name);
169+
if (!index.ok()) {
170+
return false;
171+
}
172+
173+
auto factory = NetlinkClientFactory::Default();
174+
std::unique_ptr<NetlinkClient> nl = factory->New(NETLINK_ROUTE);
175+
176+
NetlinkRequest req(RTM_DELADDR, NLM_F_REQUEST | NLM_F_ACK);
177+
req.AddAddrInfo(*index, Prefix(netmask));
178+
in_addr_t inaddr = inet_addr(std::string(gateway).c_str());
179+
req.AddInAddr(IFA_LOCAL, &inaddr);
180+
req.AddInAddr(IFA_ADDRESS, &inaddr);
181+
182+
bool res = nl->Send(req);
183+
if (!res) {
184+
LOG(ERROR) << "DestroyGateway: failed";
185+
}
186+
return res;
187+
}
188+
189+
bool LinkTapToBridge(std::string_view tap_name, std::string_view bridge_name) {
190+
VLOG(0) << "LinkTapToBridge: " << tap_name << ", " << bridge_name;
191+
auto tap_index = Index(tap_name);
192+
PCHECK(tap_index.ok()) << "Index: " << tap_name;
193+
auto bridge_index = Index(bridge_name);
194+
PCHECK(bridge_index.ok()) << "Index: " << bridge_name;
195+
196+
auto factory = NetlinkClientFactory::Default();
197+
std::unique_ptr<NetlinkClient> nl = factory->New(NETLINK_ROUTE);
198+
199+
NetlinkRequest req(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK);
200+
req.AddIfInfo(*tap_index, true);
201+
req.AddInt(IFLA_MASTER, *bridge_index);
202+
203+
bool res = nl->Send(req);
204+
if (!res) {
205+
LOG(ERROR) << "LinkTapToBridge: failed";
206+
}
207+
return res;
208+
}
209+
210+
bool DeleteIface(std::string_view name) {
211+
VLOG(0) << "DeleteIface: " << name;
212+
auto index = Index(name);
213+
if (!index.ok()) {
214+
return false;
215+
}
216+
217+
auto factory = NetlinkClientFactory::Default();
218+
std::unique_ptr<NetlinkClient> nl = factory->New(NETLINK_ROUTE);
219+
220+
NetlinkRequest req(RTM_DELLINK, NLM_F_REQUEST | NLM_F_ACK);
221+
req.AddIfInfo(*index, false);
222+
223+
bool res = nl->Send(req);
224+
if (!res) {
225+
LOG(ERROR) << "DeleteIface: failed";
226+
}
227+
return res;
228+
}
229+
230+
bool BridgeExists(std::string_view name) {
231+
VLOG(0) << "BridgeExists: " << name;
232+
auto index = Index(name);
233+
if (!index.ok() && errno == ENODEV) {
234+
return false;
235+
}
236+
237+
return true;
238+
}
239+
240+
bool CreateBridge(std::string_view name) {
241+
VLOG(0) << "CreateBridge: " << name;
242+
auto factory = NetlinkClientFactory::Default();
243+
std::unique_ptr<NetlinkClient> nl = factory->New(NETLINK_ROUTE);
244+
245+
NetlinkRequest req(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE);
246+
req.Append(ifinfomsg{
247+
.ifi_type = ARPHRD_NETROM,
248+
});
249+
req.AddString(IFLA_IFNAME, std::string(name));
250+
req.PushList(IFLA_LINKINFO);
251+
req.AddString(IFLA_INFO_KIND, "bridge");
252+
req.PushList(IFLA_INFO_DATA);
253+
req.AddInt(IFLA_BR_FORWARD_DELAY, 0);
254+
req.AddInt(IFLA_BR_STP_STATE, 0);
255+
req.PopList();
256+
req.PopList();
257+
258+
bool res = nl->Send(req);
259+
if (!res) {
260+
LOG(ERROR) << "CreateBridge: failed";
261+
}
262+
return res;
263+
}
264+
265+
bool IptableConfig(std::string_view network, bool add) {
266+
// TODO: Use NETLINK_NETFILTER.
267+
std::stringstream ss;
268+
ss << "iptables -t nat " << (add ? "-A" : "-D") << " POSTROUTING -s "
269+
<< network << " -j MASQUERADE";
270+
271+
auto command = ss.str();
272+
LOG(INFO) << "iptable_config: " << command;
273+
int status = RunExternalCommand(command);
274+
275+
return status == 0;
276+
}
277+
278+
} // namespace cuttlefish

0 commit comments

Comments
 (0)