lxcpp: cgroups API implementation (part 1)
[platform/core/security/vasum.git] / tests / unit_tests / lxcpp / ut-network.cpp
1 /*
2  *  Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public
6  *  License version 2.1 as published by the Free Software Foundation.
7  *
8  *  This library is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  *  Lesser General Public License for more details.
12  *
13  *  You should have received a copy of the GNU Lesser General Public
14  *  License along with this library; if not, write to the Free Software
15  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
16  */
17
18 /**
19  * @file
20  * @author  Krzysztof Dynowski (k.dynowski@samsumg.com)
21  * @brief   Unit tests of lxcpp network helpers
22  */
23
24 #include "config.hpp"
25 #include "config/manager.hpp"
26 #include "logger/logger.hpp"
27 #include "lxcpp/network-config.hpp"
28 #include "lxcpp/process.hpp"
29 #include "utils/execute.hpp"
30 #include "ut.hpp"
31
32 #include <iostream>
33
34 #include <net/if.h>
35
36 using namespace lxcpp;
37
38 namespace {
39
40 struct Fixture {
41     Fixture() {}
42     ~Fixture() {}
43
44     static std::string getUniqueName(const std::string& prefix) {
45         std::vector<std::string> iflist = NetworkInterface::getInterfaces(0);
46         std::string name;
47         unsigned i = 0;
48         do {
49             name = prefix + std::to_string(i++);
50         } while (std::find(iflist.begin(), iflist.end(), name) != iflist.end());
51         return name;
52     }
53
54     static void sendCmd(int fd, const char *txt) {
55         if (::write(fd, txt, 2) != 2) {
56             throw std::runtime_error("pipe write error");
57         }
58     }
59 };
60
61
62 int child_exec(void *_fd)
63 {
64     int *fd = (int *)_fd;
65     char cmdbuf[2];
66     char cmd = '-';
67
68     ::close(fd[1]);
69     try {
70         lxcpp::NetworkInterface("lo").up();
71         for(;;) {
72             // child: waiting for parent
73             if (::read(fd[0], cmdbuf, 2) != 2) {
74                 break;
75             }
76
77             cmd = cmdbuf[0];
78
79             if (cmd == '0') {
80                 break;
81             } else if (cmd == 'a') {
82                 const char *argv[] = {
83                     "ip", "a",  NULL
84                 };
85                 if (!utils::executeAndWait("/sbin/ip", argv)) {
86                     throw std::runtime_error("ip addr failed");
87                 }
88             } else if (cmd == 'r') {
89                 const char *argv[] = {
90                     "ip", "route", "list",  NULL
91                 };
92                 if (!utils::executeAndWait("/sbin/ip", argv)) {
93                     throw std::runtime_error("ip route failed");
94                 }
95             } else if (cmd == 's') {
96                 const char *argv[] = {
97                     "bash", NULL
98                 };
99                 if (!utils::executeAndWait("/bin/bash", argv)) {
100                     throw std::runtime_error("bash failed");
101                 }
102             } else if (cmd == 'c') {
103                 LOGW("connecting ... to be done");
104             } else {
105                 continue;
106             }
107         }
108
109         //cleanup
110         ::close(fd[0]);
111         _exit(EXIT_SUCCESS);
112
113     } catch(...) {
114         _exit(EXIT_FAILURE);
115     }
116 }
117
118 } // namespace
119
120 /*
121  * NOTE: network inerface unit tests are not finished yet
122  * tests are developed/added together with network interface code
123  * and container code development
124  */
125
126 BOOST_FIXTURE_TEST_SUITE(LxcppNetworkSuite, Fixture)
127
128 BOOST_AUTO_TEST_CASE(NetworkListInterfaces)
129 {
130     std::vector<std::string> iflist;
131     BOOST_CHECK_NO_THROW(iflist=NetworkInterface::getInterfaces(0));
132     for (const auto& ifn : iflist) {
133         const Attrs& attrs = NetworkInterface(ifn).getAttrs();
134         BOOST_CHECK(attrs.size() > 0);
135     }
136
137 }
138
139 BOOST_AUTO_TEST_CASE(NetworkConfigSerialization)
140 {
141     std::string tmpConfigFile = "/tmp/netconfig.conf";
142     NetworkConfig cfg;
143     BOOST_CHECK_NO_THROW(config::saveToJsonString(cfg));
144
145     cfg.addInterfaceConfig("host-veth0", "zone-eth0", InterfaceType::VETH);
146     cfg.addInterfaceConfig("host-veth1", "zone-eth1", InterfaceType::BRIDGE);
147     cfg.addInterfaceConfig("host-veth2", "zone-eth2", InterfaceType::MACVLAN);
148
149     cfg.addInetConfig("zone-eth0", InetAddr("1.2.3.4", 24));
150
151     config::saveToJsonFile(tmpConfigFile, cfg);
152
153     NetworkConfig cfg2;
154     config::loadFromJsonFile(tmpConfigFile, cfg2);
155
156     int ifnum = cfg.getInterfaces().size();
157     for (int i = 0; i < ifnum; ++i) {
158         const NetworkInterfaceConfig& ni1 = cfg.getInterface(i);
159         const NetworkInterfaceConfig& ni2 = cfg2.getInterface(i);
160
161         BOOST_CHECK_EQUAL(ni1.getHostIf(), ni2.getHostIf());
162         BOOST_CHECK_EQUAL(ni1.getZoneIf(), ni2.getZoneIf());
163         BOOST_CHECK(ni1.getType() == ni2.getType());
164         BOOST_CHECK(ni1.getMode() == ni2.getMode());
165     }
166 }
167
168 BOOST_AUTO_TEST_CASE(NetworkBridgeCreateDestroy)
169 {
170     std::string name = getUniqueName("test-br");
171     NetworkInterface ni(name);
172     InetAddr myip("10.100.1.1", 32);
173
174     BOOST_CHECK_NO_THROW(ni.create(InterfaceType::BRIDGE));
175     ni.setMACAddress("12:22:33:44:55:66");    // note bit0=0 within first byte !!!
176     BOOST_CHECK_NO_THROW(ni.addInetAddr(myip));
177
178     std::vector<std::string> iflist = NetworkInterface::getInterfaces(0);
179     BOOST_CHECK(std::find(iflist.begin(), iflist.end(), name) != iflist.end());
180
181     std::vector<InetAddr> addrs = ni.getInetAddressList();
182     BOOST_CHECK(std::find(addrs.begin(), addrs.end(), myip) != addrs.end());
183
184     BOOST_CHECK_NO_THROW(ni.delInetAddr(myip));
185     BOOST_CHECK_NO_THROW(ni.destroy());
186     iflist = NetworkInterface::getInterfaces(0);
187     BOOST_CHECK(std::find(iflist.begin(), iflist.end(), ni.getName()) == iflist.end());
188 }
189
190 BOOST_AUTO_TEST_CASE(NetworkMacVLanCreateDestroy)
191 {
192     std::string masterif;
193     std::vector<std::string> iflist = NetworkInterface::getInterfaces(0);
194     for (const auto& ifn : iflist) {
195         if (ifn == "lo") {
196             continue;
197         }
198         NetworkInterface n(ifn);
199         if (n.status() == NetStatus::UP) {
200             masterif = ifn;
201             break;
202         }
203     }
204
205     NetworkInterface ni(getUniqueName("test-vlan"));
206     // creating MACVLAN on masterif
207     BOOST_CHECK_NO_THROW(ni.create(InterfaceType::MACVLAN, masterif, MacVLanMode::VEPA));
208
209     iflist = NetworkInterface::getInterfaces(0);
210     BOOST_CHECK(std::find(iflist.begin(), iflist.end(), ni.getName()) != iflist.end());
211
212     // destroy MACVLAN
213     BOOST_CHECK_NO_THROW(ni.destroy());
214
215     iflist = NetworkInterface::getInterfaces(0);
216     BOOST_CHECK(std::find(iflist.begin(), iflist.end(), ni.getName()) == iflist.end());
217 }
218
219 BOOST_AUTO_TEST_CASE(NetworkListRoutes)
220 {
221     unsigned mainLo = 0;
222     std::vector<Route> routes;
223     // tbl MAIN, all devs
224     BOOST_CHECK_NO_THROW(routes = NetworkInterface::getRoutes(0));
225     for (auto route : routes) {
226         if (route.ifname == "lo") {
227             ++mainLo;
228         }
229     }
230
231     // tbl LOCAL, all devs
232     BOOST_CHECK_NO_THROW(routes = NetworkInterface::getRoutes(0,RoutingTable::LOCAL));
233
234     // tbl DEFAULT, all devs
235     BOOST_CHECK_NO_THROW(routes = NetworkInterface::getRoutes(0,RoutingTable::DEFAULT));
236
237     NetworkInterface ni("lo");
238     // tbl MAIN, dev lo
239     BOOST_CHECK_NO_THROW(routes = ni.getRoutes());
240     BOOST_CHECK(routes.size() == mainLo);
241
242     // tbl LOCAL, dev lo
243     BOOST_CHECK_NO_THROW(routes = ni.getRoutes(RoutingTable::LOCAL));
244 }
245
246 BOOST_AUTO_TEST_CASE(NetworkAddDelRoute)
247 {
248     std::vector<Route> routes;
249
250     Route route = {
251         InetAddr("10.100.1.0", 24),//dst - destination network
252         InetAddr("", 0),           //src - not specified (prefix=0)
253         0,                         // metric
254         "",                        // ifname (used only when read routes)
255         RoutingTable::UNSPEC       // table (used only when read rotes)
256     };
257
258     NetworkInterface ni("lo");
259
260     BOOST_CHECK_NO_THROW(ni.addRoute(route));
261     BOOST_CHECK_NO_THROW(routes = ni.getRoutes());
262     BOOST_CHECK(std::find_if(routes.begin(), routes.end(),
263                             [&route](const Route& item) -> bool {
264                                 return item.dst == route.dst;
265                             }
266                          ) != routes.end()
267     );
268
269     BOOST_CHECK_NO_THROW(ni.delRoute(route));
270     BOOST_CHECK_NO_THROW(routes = ni.getRoutes());
271     BOOST_CHECK(std::find_if(routes.begin(), routes.end(),
272                             [&route](const Route& item) -> bool {
273                                 return item.dst == route.dst;
274                             }
275                          ) == routes.end()
276     );
277 }
278
279 BOOST_AUTO_TEST_CASE(NetworkNamespaceCreate)
280 {
281     int fd[2];
282     int r = ::pipe(fd);
283     BOOST_CHECK(r != -1);
284
285     pid_t pid = lxcpp::clone(child_exec, fd, CLONE_NEWNET);
286     ::close(fd[0]);
287
288     //directives for child process
289     sendCmd(fd[1], "0"); // exit
290
291     // waiting for child to finish
292     int status;
293     BOOST_CHECK_NO_THROW(status = lxcpp::waitpid(pid));
294
295     ::close(fd[1]);
296     BOOST_CHECK_MESSAGE(status == 0, "child failed");
297 }
298
299 // this test case shows how to create container with network
300 // Note: this test needs some preparation to successfuly connect an external site:
301 // 1. allow network forwading (echo 1 > /proc/sys/net/ipv4/ip_forward)
302 // 2. configure ip masquarading (iptables -t nat -A POSTROUTING -s 10.0.0.0/16 ! -d 10.0.0.0/16 -j MASQUERADE)
303 BOOST_AUTO_TEST_CASE(NetworkNamespaceVETH)
304 {
305     const char *vbr = "vbr";
306     const char *veth1 = "veth-ma";
307     const char *veth2 = "veth-sl";
308
309     int fd[2];
310     int r = ::pipe(fd);
311     BOOST_CHECK(r != -1);
312
313     pid_t pid = lxcpp::clone(child_exec, fd, CLONE_NEWNET);
314     ::close(fd[0]);
315
316     NetworkInterface br(vbr);
317     NetworkInterface v1(veth1);
318     NetworkInterface v2(veth2);
319
320     NetworkInterface("lo", pid).up();
321
322     // creating Bridge vbr
323     BOOST_CHECK_NO_THROW(br.create(InterfaceType::BRIDGE));
324     BOOST_CHECK_NO_THROW(br.up());
325     br.addInetAddr(InetAddr("10.0.0.1", 24));
326
327     // creating VETH pair  veth1 <-> veth2
328     BOOST_CHECK_NO_THROW(v1.create(InterfaceType::VETH, v2.getName()));
329
330     // add veth1 to bridge
331     BOOST_CHECK_NO_THROW(v1.addToBridge(br.getName()));
332
333     // move veth2 to network namespace (container)
334     BOOST_CHECK_NO_THROW(v2.moveToContainer(pid));
335     v2.up();
336     v2.addInetAddr(InetAddr("10.0.0.2", 24));
337
338     v1.up(); // after v2 up and configured
339
340     // add default route
341     v2.addRoute(Route{
342         InetAddr("10.0.0.1", 0), //dst - gateway
343         InetAddr("", 0),         //src - not specified (prefix=0)
344         0,
345         "",
346         RoutingTable::UNSPEC
347     });
348
349     //directives for child process
350     sendCmd(fd[1], "a"); // ip addr show
351     sendCmd(fd[1], "r"); // ip route list
352     sendCmd(fd[1], "c"); // connect extern (needs configured NAT)
353     //sendCmd(fd[1], "s"); // exec shell
354     sendCmd(fd[1], "0"); // exit
355
356     //  waiting for child to finish
357     int status;
358     BOOST_CHECK_NO_THROW(status = lxcpp::waitpid(pid));
359     ::close(fd[1]);
360     BOOST_CHECK_MESSAGE(status == 0, "child failed");
361
362     BOOST_CHECK_NO_THROW(br.destroy());
363 }
364
365 BOOST_AUTO_TEST_SUITE_END()