softwarecontainer  0.18.0-739e8d7 2017-05-04
networkgateway.cpp
1 /*
2  * Copyright (C) 2016-2017 Pelagicore AB
3  *
4  * Permission to use, copy, modify, and/or distribute this software for
5  * any purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
9  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
11  * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
12  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
13  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
14  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15  * SOFTWARE.
16  *
17  * For further information see LICENSE
18  */
19 
20 #include <cstring>
21 #include "ifaddrs.h"
22 #include "unistd.h"
23 #include "networkgateway.h"
24 #include "networkgatewayparser.h"
25 #include "functionjob.h"
26 
27 namespace softwarecontainer {
28 
30  const std::string bridgeDevice,
31  const std::string gateway,
32  const uint8_t maskBits,
33  std::shared_ptr<ContainerAbstractInterface> container) :
34  Gateway(ID, container),
35  m_netmask(maskBits),
36  m_gateway(gateway),
37  m_bridgeDevice(bridgeDevice),
38  m_interfaceInitialized(false),
39  m_containerID(id)
40 {
41 }
42 
43 NetworkGateway::~NetworkGateway() { }
44 
45 bool NetworkGateway::readConfigElement(const json_t *element)
46 {
47  IPTableEntry e;
48  NetworkGatewayParser configParser;
49 
50  if (configParser.parseNetworkGatewayConfiguration(element, e)) {
51  m_entries.push_back(e);
52  return true;
53  } else {
54  return false;
55  }
56 }
57 
59 {
60  if (m_gateway.size() != 0) {
61  log_debug() << "Default gateway set to " << m_gateway;
62  } else {
63  log_warning() << "No gateway. Network access will be disabled";
64  return true;
65  }
66 
67  if (!isBridgeAvailable()) {
68  log_error() << "Bridge not available, expected gateway to be " << m_gateway;
69  return false;
70  }
71 
72  try {
73  m_ip.s_addr = m_functions.generateIP(m_netmask, m_gateway, m_containerID);
74  } catch (IPAllocationError &error) {
75  log_error() << error.what();
76  return false;
77  }
78 
79  bool returnValue = up();
80  if (!returnValue) {
81  log_error() << "Couldn't bring the network up";
82  return false;
83  }
84 
85  log_debug() << "Adding iptables entries";
86  for (auto entry : m_entries) {
87  FunctionJob job (getContainer(), [&] () {
88  return entry.applyRules() ? SUCCESS : FAILURE;
89  });
90  job.start();
91 
92  job.wait();
93  if (job.isError()) {
94  log_error() << "Failed to apply rules for entry: " << entry.toString();
95  return false;
96  }
97  }
98 
99  return true;
100 }
101 
103 {
104  return true;
105 }
106 
107 bool NetworkGateway::setDefaultGateway()
108 {
109  FunctionJob job(getContainer(), [this] {
110  Netlink n;
111  return n.setDefaultGateway(m_gateway.c_str()) ? SUCCESS : FAILURE;
112  });
113 
114  job.start();
115  job.wait();
116  return job.isSuccess();
117 }
118 
119 bool NetworkGateway::up()
120 {
121  static const constexpr int BAD_SETIP = 3;
122 
123  if (m_interfaceInitialized) {
124  log_debug() << "Interface already configured";
125  return true;
126  }
127 
128  log_debug() << "Attempting to bring up eth0";
129  FunctionJob jobBringUpEthernet(getContainer(), [this] {
130  Netlink n;
131 
132  Netlink::LinkInfo iface;
133  if (!n.findLink("eth0", iface)) {
134  return NO_LINK;
135  }
136 
137  int ifaceIndex = iface.first.ifi_index;
138  if (!n.linkUp(ifaceIndex)) {
139  return BAD_LINKUP;
140  }
141 
142  if (!n.setIP(ifaceIndex, m_ip, m_netmask)) {
143  return BAD_SETIP;
144  }
145 
146  return SUCCESS;
147  });
148 
149  jobBringUpEthernet.start();
150 
151  int returnCode = jobBringUpEthernet.wait();
152  switch(returnCode) {
153  case NO_LINK:
154  log_error() << "Could not find interface eth0 in container";
155  return false;
156  case BAD_LINKUP:
157  log_error() << "Could not bring interface eth0 up in container";
158  return false;
159  case BAD_SETIP:
160  log_error() << "Could not set IP-address";
161  return false;
162  case SUCCESS:
163  log_debug() << "Interface brought up, proceeding to set default gateway";
164  m_interfaceInitialized = true;
165  return setDefaultGateway();
166  default:
167  log_error() << "Unhandled case in NetworkGateway::up(), this is an error!";
168  return false;
169  }
170 }
171 
172 bool NetworkGateway::down()
173 {
174  log_debug() << "Attempting to configure eth0 to 'down state'";
175  FunctionJob job(getContainer(), [this] {
176  Netlink n;
177  Netlink::LinkInfo iface;
178  if (!n.findLink("eth0", iface)) {
179  return NO_LINK;
180  }
181 
182  if (!n.linkDown(iface.first.ifi_index)) {
183  return BAD_LINKDOWN;
184  }
185 
186  return SUCCESS;
187  });
188  job.start();
189  int returnCode = job.wait();
190  switch(returnCode)
191  {
192  case NO_LINK:
193  log_error() << "Could not find interface eth0 in container";
194  return false;
195  case BAD_LINKDOWN:
196  log_error() << "Could not bring interface eth0 down in container";
197  return false;
198  case SUCCESS:
199  return true;
200  default:
201  log_error() << "Unhandled case in NetworkGateway::down(), this is an error!";
202  return false;
203  }
204 }
205 
206 bool NetworkGateway::isBridgeAvailable()
207 {
208  log_debug() << "Is bridge available?";
209  Netlink::LinkInfo iface;
210  if (!m_netlinkHost.findLink(m_bridgeDevice.c_str(), iface)) {
211  log_error() << "Could not find " << m_bridgeDevice << " in the host";
212  }
213 
214  std::vector<Netlink::AddressInfo> addresses;
215  if (!m_netlinkHost.findAddresses(iface.first.ifi_index, addresses)) {
216  log_error() << "Could not fetch addresses for " << m_bridgeDevice << " in the host";
217  }
218 
219  log_debug() << "Could find bridge and could fetch address, running hasAddress";
220  bool retval = m_netlinkHost.hasAddress(addresses, AF_INET, m_gateway.c_str());
221  return retval;
222 }
223 
224 } // namespace softwarecontainer
Run a C++ function lambda inside a SoftwareContainer.
Definition: functionjob.h:30
bool activateGateway() override
Implements Gateway::activateGateway.
uint32_t generateIP(const uint32_t netmask, const std::string gatewayIP, const int32_t containerID)
Generate IP address for the container.
bool parseNetworkGatewayConfiguration(const json_t *element, IPTableEntry &e)
Parses NetworkGateway configuration into IPTableEntry.
Gateway base class for SoftwareContainer.
Definition: gateway.h:61
bool teardownGateway() override
Implements Gateway::teardownGateway.
std::shared_ptr< ContainerAbstractInterface > getContainer()
Get a handle to the associated container.
Definition: gateway.cpp:128
NetworkGateway(const int32_t id, const std::string bridgeDevice, const std::string gateway, const uint8_t maskBits, std::shared_ptr< ContainerAbstractInterface > container)
Creates a network gateway.
Developers guide to adding a config item:
A rules entry for the treatment of packets.
Definition: iptableentry.h:29
bool readConfigElement(const json_t *element) override
Gateway specific parsing of config elements.