Skip to content

Commit 6a14802

Browse files
polycubed: add namespace class
The namespace class allows to perform different operation with namespaces as create, delete, open and run code on them. A namespace object is a handler of an existing namespace, this can be created by: Namespace::create(): Creates a new namespace and returns an object representing it Namesapce::open(): Opens an already existing namespace and returns an object for handling it Other methods are: execute(): runs a piece of code in in this namespace namespace. remove(): removes the namespace set_id(): sets the netlink id for this The threads of polycubed cannot be moved to another namespace because it could cause issues with the injected eBPF programs, so this class has an worker thread (shared among all instances) that is used to run functions in different namespaces. An event queue with a condition variable are used to send tasks to the worker thread, also a lazy logic is implemented to have the thread only when there are Namespace instances. Signed-off-by: Mauricio Vasquez B <mauriciovasquezbernal@gmail.com>
1 parent d624396 commit 6a14802

3 files changed

Lines changed: 346 additions & 0 deletions

File tree

src/polycubed/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ set(polycubed_sources
5858
datapath_log.cpp
5959
id_generator.cpp
6060
utils/extiface_info.cpp
61+
utils/ns.cpp
6162
utils/netlink.cpp
6263
utils/utils.cpp
6364
${server_sources}

src/polycubed/src/utils/ns.cpp

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
/*
2+
* Copyright 2019 The Polycube Authors
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+
17+
#include "ns.h"
18+
19+
#include <iostream>
20+
#include <sstream>
21+
#include <cstring>
22+
23+
#include <stdio.h>
24+
#include <sys/stat.h>
25+
#include <fcntl.h>
26+
#include <unistd.h>
27+
#include <sched.h>
28+
#include <sys/mount.h>
29+
#include <linux/limits.h>
30+
31+
#include <sys/types.h>
32+
#include <sys/syscall.h>
33+
34+
#include <netlink/netlink.h>
35+
#include <netlink/route/addr.h>
36+
#include <netlink/route/link.h>
37+
#include <netlink/route/qdisc.h>
38+
#include <netlink/socket.h>
39+
40+
#include <linux/net_namespace.h>
41+
42+
#define NETNS_RUN_DIR "/var/run/netns"
43+
44+
namespace polycube::polycubed {
45+
46+
struct Namespace::Event {
47+
std::function<void()> f;
48+
std::promise<void> barrier;
49+
};
50+
51+
std::unique_ptr<std::thread> Namespace::executor;
52+
std::condition_variable Namespace::cv;
53+
std::mutex Namespace::mutex;
54+
std::queue<Namespace::Event *> Namespace::queue;
55+
bool Namespace::finished(false);
56+
std::atomic<int> Namespace::count(0);
57+
58+
59+
Namespace Namespace::create(const std::string &name) {
60+
if (count.fetch_add(1) == 0) {
61+
start();
62+
}
63+
64+
try {
65+
int fd;
66+
67+
auto f = [&]() {
68+
create_ns(name);
69+
char netns_path[PATH_MAX];
70+
::snprintf(netns_path, sizeof(netns_path), "%s/%s",
71+
NETNS_RUN_DIR, name.c_str());
72+
fd = ::open(netns_path, O_RDONLY|O_EXCL, 0);
73+
if (fd < 0) {
74+
throw std::runtime_error(std::string("Error opening namespace: ") +
75+
std::strerror(errno));
76+
}
77+
};
78+
79+
execute_in_worker(f);
80+
return Namespace(name, fd);
81+
} catch (...) {
82+
if (count.fetch_sub(1) == 1) {
83+
stop();
84+
}
85+
throw;
86+
}
87+
}
88+
89+
Namespace Namespace::open(const std::string &name) {
90+
if (count.fetch_add(1) == 0) {
91+
start();
92+
}
93+
94+
try {
95+
int fd;
96+
97+
auto f = [&]() {
98+
char netns_path[PATH_MAX];
99+
::snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR,
100+
name.c_str());
101+
fd = ::open(netns_path, O_RDONLY|O_EXCL, 0);
102+
if (fd < 0) {
103+
throw std::runtime_error(std::string("Error opening namespace: ") +
104+
std::strerror(errno));
105+
}
106+
};
107+
108+
execute_in_worker(f);
109+
return Namespace(name, fd);
110+
} catch (...) {
111+
if (count.fetch_sub(1) == 1) {
112+
stop();
113+
}
114+
throw;
115+
}
116+
}
117+
118+
Namespace::Namespace(const std::string &name, int fd) : name_(name), fd_(fd){}
119+
120+
Namespace::~Namespace() {
121+
close(fd_);
122+
123+
if (count.fetch_sub(1) == 1) {
124+
stop();
125+
}
126+
}
127+
128+
void Namespace::execute(std::function<void()> f) {
129+
std::function<void()> f_ = [&]() {
130+
if (setns(fd_, CLONE_NEWNET)) {
131+
throw std::runtime_error(std::string("setns error: ") +
132+
std::strerror(errno));
133+
}
134+
f();
135+
};
136+
137+
execute_in_worker(f_);
138+
}
139+
140+
void Namespace::remove() {
141+
char netns_path[PATH_MAX];
142+
snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR,
143+
name_.c_str());
144+
::close(fd_);
145+
146+
::umount2(netns_path, MNT_DETACH);
147+
if (unlink(netns_path) < 0) {
148+
throw std::runtime_error(std::string("umount error : ") +
149+
std::strerror(errno));
150+
}
151+
}
152+
153+
154+
void Namespace::set_id(int id) {
155+
int err;
156+
157+
struct rtnl_link *link;
158+
struct nl_cache *cache;
159+
struct nl_sock *sock;
160+
161+
sock = nl_socket_alloc();
162+
if ((err = nl_connect(sock, NETLINK_ROUTE)) < 0) {
163+
throw std::runtime_error(std::string("Unable to connect socket: ") +
164+
nl_geterror(err));
165+
}
166+
167+
if ((err = rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache)) < 0) {
168+
throw std::runtime_error(std::string("Unable to allocate cache: ") +
169+
nl_geterror(err));
170+
}
171+
172+
struct nl_msg *msg;
173+
struct nlmsghdr *hdr;
174+
175+
struct nlattr *opts;
176+
177+
if (!(msg = nlmsg_alloc())) {
178+
throw std::runtime_error("Unable allocate nlmsg");
179+
}
180+
181+
struct rtgenmsg t;
182+
t.rtgen_family = AF_UNSPEC;
183+
184+
hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, RTM_NEWNSID, sizeof(t),
185+
NLM_F_REQUEST);
186+
memcpy(nlmsg_data(hdr), &t, sizeof(t));
187+
188+
NLA_PUT_U32(msg, NETNSA_FD, fd_);
189+
NLA_PUT_U32(msg, NETNSA_NSID, id);
190+
191+
if(nl_send_sync(sock, msg)) {
192+
throw std::runtime_error("nl_send_sync failed");
193+
}
194+
195+
// TODO: read response from the kernel
196+
nl_cache_free(cache);
197+
nl_socket_free(sock);
198+
199+
return;
200+
201+
nla_put_failure:
202+
throw std::runtime_error("Error constructing nlmsg");
203+
}
204+
205+
void Namespace::create_ns(const std::string &name) {
206+
std::string netns_path(std::string(NETNS_RUN_DIR) + "/" + name);
207+
208+
::mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
209+
210+
int fd = ::open(netns_path.c_str(), O_RDONLY|O_CREAT|O_EXCL, 0);
211+
if (fd < 0) {
212+
throw std::runtime_error(std::string("open error: ") +
213+
std::strerror(errno));
214+
}
215+
216+
::close(fd);
217+
218+
if(::unshare(CLONE_NEWNET) == -1) {
219+
throw std::runtime_error(std::string("unshare error: ") +
220+
std::strerror(errno));
221+
}
222+
223+
std::ostringstream os;
224+
os << "/proc/self/task/" << syscall(__NR_gettid) << "/ns/net";
225+
226+
if(::mount(os.str().c_str(), netns_path.c_str(), "none", MS_BIND, NULL)) {
227+
throw std::runtime_error(std::string("mount error: ") +
228+
std::strerror(errno));
229+
}
230+
}
231+
232+
void Namespace::execute_in_worker(std::function<void()> f) {
233+
Event ev;
234+
ev.f = f;
235+
236+
{
237+
std::lock_guard<std::mutex> lk(mutex);
238+
queue.push(&ev);
239+
}
240+
241+
cv.notify_one();
242+
243+
std::future<void> barrier_future = ev.barrier.get_future();
244+
barrier_future.wait();
245+
}
246+
247+
248+
void Namespace::worker() {
249+
std::unique_lock<std::mutex> lk(mutex);
250+
while (1) {
251+
cv.wait(lk, []{return queue.size() >= 1 || finished;});
252+
if (finished) {
253+
return;
254+
}
255+
256+
Event *ev = queue.front();
257+
ev->f();
258+
ev->barrier.set_value();
259+
queue.pop();
260+
}
261+
}
262+
263+
void Namespace::stop() {
264+
{
265+
std::lock_guard<std::mutex> lk(mutex);
266+
finished = true;
267+
}
268+
269+
cv.notify_all();
270+
executor->join();
271+
executor.reset();
272+
}
273+
274+
void Namespace::start() {
275+
finished = false;
276+
277+
executor = std::make_unique<std::thread>(worker);
278+
}
279+
280+
} // namespace polycube::polycube

src/polycubed/src/utils/ns.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2019 The Polycube Authors
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+
17+
#pragma once
18+
19+
#include <string>
20+
#include <functional>
21+
#include <thread>
22+
#include <mutex>
23+
#include <condition_variable>
24+
#include <queue>
25+
#include <future>
26+
27+
namespace polycube::polycubed {
28+
29+
class Namespace {
30+
public:
31+
static Namespace create(const std::string &name);
32+
static Namespace open(const std::string &name);
33+
34+
void execute(std::function<void()> f);
35+
void remove();
36+
void set_id(int id);
37+
38+
~Namespace();
39+
40+
private:
41+
Namespace(const std::string &name, int fd);
42+
static void create_ns(const std::string &name);
43+
44+
static void execute_in_worker(std::function<void()> f);
45+
46+
static void stop();
47+
static void start();
48+
49+
std::string name_;
50+
int fd_;
51+
52+
struct Event;
53+
54+
static std::unique_ptr<std::thread> executor;
55+
static std::condition_variable cv;
56+
static std::mutex mutex;
57+
static std::queue<Event*> queue;
58+
static bool finished;
59+
60+
static std::atomic<int> count;
61+
62+
static void worker();
63+
};
64+
65+
} // namespace polycube::polycube

0 commit comments

Comments
 (0)