OpenVPN 3 Core Library
Loading...
Searching...
No Matches
tunsetup.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// Client tun interface for Linux.
13
14#ifndef OPENVPN_TUN_LINUX_CLIENT_TUNSETUP_H
15#define OPENVPN_TUN_LINUX_CLIENT_TUNSETUP_H
16
17#include <sys/ioctl.h>
18#include <fcntl.h>
19#include <errno.h>
20#include <net/if.h>
21#include <linux/if_tun.h>
22
39
41
42OPENVPN_EXCEPTION(tun_linux_error);
43OPENVPN_EXCEPTION(tun_open_error);
44OPENVPN_EXCEPTION(tun_layer_error);
45OPENVPN_EXCEPTION(tun_ioctl_error);
46OPENVPN_EXCEPTION(tun_fcntl_error);
47OPENVPN_EXCEPTION(tun_name_error);
48OPENVPN_EXCEPTION(tun_tx_queue_len_error);
49OPENVPN_EXCEPTION(tun_ifconfig_error);
50
51template <class TUNMETHODS>
53{
54 public:
56
57 // This empty constructor shouldn't be needed, but due to a
58 // plausible compiler bug in GCC 4.8.5 (RHEL 7), this empty
59 // constructor is required to be able to build. This is
60 // related to the member initialization of the private
61 // remove_cmds_bypass_gw and remove_cmds class members.
63 {
64 }
65
67 {
68 std::string iface_name;
69 Layer layer; // OSI layer
70 std::string dev_name;
71 int txqueuelen = 0;
72 bool add_bypass_routes_on_establish = false; // required when not using tunbuilder
73 bool dco = false;
74
75#ifdef HAVE_JSON
76 virtual Json::Value to_json() override
77 {
78 Json::Value root(Json::objectValue);
79 root["iface_name"] = Json::Value(iface_name);
80 root["layer"] = Json::Value(layer.str());
81 root["dev_name"] = Json::Value(dev_name);
82 root["txqueuelen"] = Json::Value(txqueuelen);
83 root["dco"] = Json::Value(dco);
84 return root;
85 };
86
87 virtual void from_json(const Json::Value &root, const std::string &title) override
88 {
89 json::assert_dict(root, title);
90 json::to_string(root, iface_name, "iface_name", title);
91 layer = Layer::from_str(json::get_string(root, "layer", title));
92 json::to_string(root, dev_name, "dev_name", title);
93 json::to_int(root, txqueuelen, "txqueuelen", title);
94 json::to_bool(root, dco, "dco", title);
95 }
96#endif
97 };
98
99 void destroy(std::ostream &os) override
100 {
101 // remove added routes
103
104 // remove bypass route
106 }
107
108 bool add_bypass_route(const std::string &address,
109 bool ipv6,
110 std::ostream &os)
111 {
112 // nothing to do if we reconnect to the same gateway
113 if (connected_gw == address)
114 return true;
115
116 // remove previous bypass route
118 remove_cmds_bypass_gw->clear();
119
120 ActionList::Ptr add_cmds = new ActionList();
121 TUNMETHODS::add_bypass_route(tun_iface_name, address, ipv6, nullptr, *add_cmds, *remove_cmds_bypass_gw);
122
123 // add gateway bypass route
124 add_cmds->execute(os);
125 return true;
126 }
127
128 int establish(const TunBuilderCapture &pull, // defined by TunBuilderSetup::Base
130 Stop *stop,
131 std::ostream &os) override
132 {
133 // get configuration
134 Config *conf = dynamic_cast<Config *>(config);
135 if (!conf)
136 throw tun_linux_error("missing config");
137
138 int fd = -1;
139 if (!conf->dco)
140 {
141 fd = open_tun(conf);
142 }
143 else
144 {
145 // in DCO case device is already opened
147 }
148
149 ActionList::Ptr add_cmds = new ActionList();
150 ActionList::Ptr remove_cmds_new = new ActionListReversed();
151
152 // configure tun properties
153 TUNMETHODS::tun_config(tun_iface_name,
154 pull,
155 nullptr,
156 *add_cmds,
157 *remove_cmds_new,
159
160 // execute commands to bring up interface
161 add_cmds->execute(os);
162
163 // tear down old routes
165 std::swap(remove_cmds, remove_cmds_new);
166
168
169 return fd;
170 }
171
172 private:
173 int open_tun(Config *conf)
174 {
175 static const char node[] = "/dev/net/tun";
176 ScopedFD fd(open(node, O_RDWR));
177 if (!fd.defined())
178 OPENVPN_THROW(tun_open_error, "error opening tun device " << node << ": " << errinfo(errno));
179
180 struct ifreq ifr;
181 std::memset(&ifr, 0, sizeof(ifr));
182 ifr.ifr_flags = IFF_ONE_QUEUE;
183 ifr.ifr_flags |= IFF_NO_PI;
184 if (conf->layer() == Layer::OSI_LAYER_3)
185 ifr.ifr_flags |= IFF_TUN;
186 else if (conf->layer() == Layer::OSI_LAYER_2)
187 ifr.ifr_flags |= IFF_TAP;
188 else
189 throw tun_layer_error("unknown OSI layer");
190
191 open_unit(conf->dev_name, ifr, fd);
192
193 if (fcntl(fd(), F_SETFL, O_NONBLOCK) < 0)
194 throw tun_fcntl_error(errinfo(errno));
195
196 // Set the TX send queue size
197 if (conf->txqueuelen)
198 {
199 struct ifreq netifr;
200 ScopedFD ctl_fd(socket(AF_INET, SOCK_DGRAM, 0));
201
202 if (ctl_fd.defined())
203 {
204 std::memset(&netifr, 0, sizeof(netifr));
205 strcpy(netifr.ifr_name, ifr.ifr_name);
206 netifr.ifr_qlen = conf->txqueuelen;
207 if (ioctl(ctl_fd(), SIOCSIFTXQLEN, (void *)&netifr) < 0)
208 throw tun_tx_queue_len_error(errinfo(errno));
209 }
210 else
211 throw tun_tx_queue_len_error(errinfo(errno));
212 }
213 conf->iface_name = ifr.ifr_name;
214 tun_iface_name = ifr.ifr_name;
215
216 return fd.release();
217 }
218
219 void open_unit(const std::string &name, struct ifreq &ifr, ScopedFD &fd)
220 {
221 if (!name.empty())
222 {
223 const int max_units = 256;
224 for (int unit = 0; unit < max_units; ++unit)
225 {
226 std::string n = name;
227 if (unit)
228 n += openvpn::to_string(unit);
229 if (n.length() < IFNAMSIZ)
230 ::strcpy(ifr.ifr_name, n.c_str());
231 else
232 throw tun_name_error();
233 if (ioctl(fd(), TUNSETIFF, (void *)&ifr) == 0)
234 return;
235 }
236 const int eno = errno;
237 OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' after trying " << max_units << " units : " << errinfo(eno));
238 }
239 else
240 {
241 if (ioctl(fd(), TUNSETIFF, (void *)&ifr) < 0)
242 {
243 const int eno = errno;
244 OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' : " << errinfo(eno));
245 }
246 }
247 }
248
251
252 std::string connected_gw;
253
254 std::string tun_iface_name; // used to skip tun-based default gw when add bypass route
255};
256} // namespace openvpn::TunLinuxSetup
257
258#endif // OPENVPN_TUN_LINUX_CLIENT_TUNCLI_H
virtual std::unordered_set< std::string > execute(std::ostream &os)
Executes a sequence of actions and returns marks of failed actions.
Definition action.hpp:98
const char * str() const
Definition layer.hpp:61
static Layer from_str(const std::string &str)
Definition layer.hpp:91
The smart pointer class.
Definition rc.hpp:119
bool defined() const
Definition scoped_fd.hpp:58
std::string to_string() const
Returns a string representation of the remote address.
Definition capture.hpp:66
RemoteAddress remote_address
Definition capture.hpp:1082
void open_unit(const std::string &name, struct ifreq &ifr, ScopedFD &fd)
Definition tunsetup.hpp:219
ActionList::Ptr remove_cmds_bypass_gw
Definition tunsetup.hpp:249
bool add_bypass_route(const std::string &address, bool ipv6, std::ostream &os)
Definition tunsetup.hpp:108
void destroy(std::ostream &os) override
Definition tunsetup.hpp:99
ActionListReversed::Ptr remove_cmds
Definition tunsetup.hpp:250
int open_tun(Config *conf)
Definition tunsetup.hpp:173
int establish(const TunBuilderCapture &pull, TunBuilderSetup::Config *config, Stop *stop, std::ostream &os) override
Definition tunsetup.hpp:128
#define OPENVPN_EXCEPTION(C)
#define OPENVPN_THROW(exc, stuff)
void assert_dict(const Json::Value &obj, const TITLE &title)
void to_string(const Json::Value &root, std::string &dest, const NAME &name, const TITLE &title)
void to_int(const Json::Value &root, int &dest, const NAME &name, const TITLE &title)
void to_bool(const Json::Value &root, bool &dest, const NAME &name, const TITLE &title)
std::string get_string(const Json::Value &root, const NAME &name, const TITLE &title)
std::string to_string(const T &t)
Convert a value to a string.
Definition to_string.hpp:45
std::string errinfo(ErrorCode err)
Definition asioerr.hpp:23
virtual void from_json(const Json::Value &root, const std::string &title) override
Definition tunsetup.hpp:87
virtual Json::Value to_json() override
Definition tunsetup.hpp:76
remote_address ipv6
remote_address address
std::ostringstream os
static const char config[]