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