lxcpp: fix network config serialization
[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     ::unlink(tmpConfigFile.c_str());
143
144     NetworkConfig cfg;
145     BOOST_CHECK_NO_THROW(config::saveToJsonString(cfg));
146
147     cfg.addInterfaceConfig("host-veth0", "zone-eth0", InterfaceType::VETH);
148     cfg.addInterfaceConfig("host-veth1", "zone-eth1", InterfaceType::BRIDGE);
149     cfg.addInterfaceConfig("host-veth2", "zone-eth2", InterfaceType::MACVLAN);
150
151     cfg.addInetConfig("zone-eth0", InetAddr("1.2.3.4", 24));
152
153     config::saveToJsonFile(tmpConfigFile, cfg);
154
155     NetworkConfig cfg2;
156     BOOST_CHECK_NO_THROW(config::loadFromJsonFile(tmpConfigFile, cfg2));
157
158     int ifn1 = cfg.getInterfaces().size();
159     int ifn2 = cfg2.getInterfaces().size();
160     BOOST_CHECK_EQUAL(ifn1, ifn2);
161     for (int i = 0; i < ifn2; ++i) {
162         const NetworkInterfaceConfig& ni1 = cfg.getInterface(i);
163         const NetworkInterfaceConfig& ni2 = cfg2.getInterface(i);
164
165         BOOST_CHECK_EQUAL(ni1.getHostIf(), ni2.getHostIf());
166         BOOST_CHECK_EQUAL(ni1.getZoneIf(), ni2.getZoneIf());
167         BOOST_CHECK(ni1.getType() == ni2.getType());
168         BOOST_CHECK(ni1.getMode() == ni2.getMode());
169     }
170 }
171
172 BOOST_AUTO_TEST_CASE(NetworkBridgeCreateDestroy)
173 {
174     std::string name = getUniqueName("test-br");
175     NetworkInterface ni(name);
176     InetAddr myip("10.100.1.1", 32);
177
178     BOOST_CHECK_NO_THROW(ni.create(InterfaceType::BRIDGE));
179     ni.setMACAddress("12:22:33:44:55:66");    // note bit0=0 within first byte !!!
180     BOOST_CHECK_NO_THROW(ni.addInetAddr(myip));
181
182     std::vector<std::string> iflist = NetworkInterface::getInterfaces(0);
183     BOOST_CHECK(std::find(iflist.begin(), iflist.end(), name) != iflist.end());
184
185     std::vector<InetAddr> addrs = ni.getInetAddressList();
186     BOOST_CHECK(std::find(addrs.begin(), addrs.end(), myip) != addrs.end());
187
188     BOOST_CHECK_NO_THROW(ni.delInetAddr(myip));
189     BOOST_CHECK_NO_THROW(ni.destroy());
190     iflist = NetworkInterface::getInterfaces(0);
191     BOOST_CHECK(std::find(iflist.begin(), iflist.end(), ni.getName()) == iflist.end());
192 }
193
194 BOOST_AUTO_TEST_CASE(NetworkMacVLanCreateDestroy)
195 {
196     std::string masterif;
197     std::vector<std::string> iflist = NetworkInterface::getInterfaces(0);
198     for (const auto& ifn : iflist) {
199         if (ifn == "lo") {
200             continue;
201         }
202         NetworkInterface n(ifn);
203         if (n.status() == NetStatus::UP) {
204             masterif = ifn;
205             break;
206         }
207     }
208
209     NetworkInterface ni(getUniqueName("test-vlan"));
210     // creating MACVLAN on masterif
211     BOOST_CHECK_NO_THROW(ni.create(InterfaceType::MACVLAN, masterif, MacVLanMode::VEPA));
212
213     iflist = NetworkInterface::getInterfaces(0);
214     BOOST_CHECK(std::find(iflist.begin(), iflist.end(), ni.getName()) != iflist.end());
215
216     // destroy MACVLAN
217     BOOST_CHECK_NO_THROW(ni.destroy());
218
219     iflist = NetworkInterface::getInterfaces(0);
220     BOOST_CHECK(std::find(iflist.begin(), iflist.end(), ni.getName()) == iflist.end());
221 }
222
223 BOOST_AUTO_TEST_CASE(NetworkListRoutes)
224 {
225     unsigned mainLo = 0;
226     std::vector<Route> routes;
227     // tbl MAIN, all devs
228     BOOST_CHECK_NO_THROW(routes = NetworkInterface::getRoutes(0));
229     for (auto route : routes) {
230         if (route.ifname == "lo") {
231             ++mainLo;
232         }
233     }
234
235     // tbl LOCAL, all devs
236     BOOST_CHECK_NO_THROW(routes = NetworkInterface::getRoutes(0,RoutingTable::LOCAL));
237
238     // tbl DEFAULT, all devs
239     BOOST_CHECK_NO_THROW(routes = NetworkInterface::getRoutes(0,RoutingTable::DEFAULT));
240
241     NetworkInterface ni("lo");
242     // tbl MAIN, dev lo
243     BOOST_CHECK_NO_THROW(routes = ni.getRoutes());
244     BOOST_CHECK(routes.size() == mainLo);
245
246     // tbl LOCAL, dev lo
247     BOOST_CHECK_NO_THROW(routes = ni.getRoutes(RoutingTable::LOCAL));
248 }
249
250 BOOST_AUTO_TEST_CASE(NetworkAddDelRoute)
251 {
252     std::vector<Route> routes;
253
254     Route route = {
255         InetAddr("10.100.1.0", 24),//dst - destination network
256         InetAddr("", 0),           //src - not specified (prefix=0)
257         0,                         // metric
258         "",                        // ifname (used only when read routes)
259         RoutingTable::UNSPEC       // table (used only when read rotes)
260     };
261
262     NetworkInterface ni("lo");
263
264     BOOST_CHECK_NO_THROW(ni.addRoute(route));
265     BOOST_CHECK_NO_THROW(routes = ni.getRoutes());
266     BOOST_CHECK(std::find_if(routes.begin(), routes.end(),
267                             [&route](const Route& item) -> bool {
268                                 return item.dst == route.dst;
269                             }
270                          ) != routes.end()
271     );
272
273     BOOST_CHECK_NO_THROW(ni.delRoute(route));
274     BOOST_CHECK_NO_THROW(routes = ni.getRoutes());
275     BOOST_CHECK(std::find_if(routes.begin(), routes.end(),
276                             [&route](const Route& item) -> bool {
277                                 return item.dst == route.dst;
278                             }
279                          ) == routes.end()
280     );
281 }
282
283 BOOST_AUTO_TEST_CASE(NetworkNamespaceCreate)
284 {
285     int fd[2];
286     int r = ::pipe(fd);
287     BOOST_CHECK(r != -1);
288
289     pid_t pid = lxcpp::clone(child_exec, fd, CLONE_NEWNET);
290     ::close(fd[0]);
291
292     //directives for child process
293     sendCmd(fd[1], "0"); // exit
294
295     // waiting for child to finish
296     int status;
297     BOOST_CHECK_NO_THROW(status = lxcpp::waitpid(pid));
298
299     ::close(fd[1]);
300     BOOST_CHECK_MESSAGE(status == 0, "child failed");
301 }
302
303 // this test case shows how to create container with network
304 // Note: this test needs some preparation to successfuly connect an external site:
305 // 1. allow network forwading (echo 1 > /proc/sys/net/ipv4/ip_forward)
306 // 2. configure ip masquarading (iptables -t nat -A POSTROUTING -s 10.0.0.0/16 ! -d 10.0.0.0/16 -j MASQUERADE)
307 BOOST_AUTO_TEST_CASE(NetworkNamespaceVETH)
308 {
309     const char *vbr = "vbr";
310     const char *veth1 = "veth-ma";
311     const char *veth2 = "veth-sl";
312
313     int fd[2];
314     int r = ::pipe(fd);
315     BOOST_CHECK(r != -1);
316
317     pid_t pid = lxcpp::clone(child_exec, fd, CLONE_NEWNET);
318     ::close(fd[0]);
319
320     NetworkInterface br(vbr);
321     NetworkInterface v1(veth1);
322     NetworkInterface v2(veth2);
323
324     NetworkInterface("lo", pid).up();
325
326     // creating Bridge vbr
327     BOOST_CHECK_NO_THROW(br.create(InterfaceType::BRIDGE));
328     BOOST_CHECK_NO_THROW(br.up());
329     br.addInetAddr(InetAddr("10.0.0.1", 24));
330
331     // creating VETH pair  veth1 <-> veth2
332     BOOST_CHECK_NO_THROW(v1.create(InterfaceType::VETH, v2.getName()));
333
334     // add veth1 to bridge
335     BOOST_CHECK_NO_THROW(v1.addToBridge(br.getName()));
336
337     // move veth2 to network namespace (container)
338     BOOST_CHECK_NO_THROW(v2.moveToContainer(pid));
339     v2.up();
340     v2.addInetAddr(InetAddr("10.0.0.2", 24));
341
342     v1.up(); // after v2 up and configured
343
344     // add default route
345     v2.addRoute(Route{
346         InetAddr("10.0.0.1", 0), //dst - gateway
347         InetAddr("", 0),         //src - not specified (prefix=0)
348         0,
349         "",
350         RoutingTable::UNSPEC
351     });
352
353     //directives for child process
354     sendCmd(fd[1], "a"); // ip addr show
355     sendCmd(fd[1], "r"); // ip route list
356     sendCmd(fd[1], "c"); // connect extern (needs configured NAT)
357     //sendCmd(fd[1], "s"); // exec shell
358     sendCmd(fd[1], "0"); // exit
359
360     //  waiting for child to finish
361     int status;
362     BOOST_CHECK_NO_THROW(status = lxcpp::waitpid(pid));
363     ::close(fd[1]);
364     BOOST_CHECK_MESSAGE(status == 0, "child failed");
365
366     BOOST_CHECK_NO_THROW(br.destroy());
367 }
368
369 BOOST_AUTO_TEST_SUITE_END()