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.
62 Setup() = default;
63
65 {
66 std::string iface_name;
67 Layer layer; // OSI layer
68 std::string dev_name;
69 int txqueuelen = 0;
70 bool add_bypass_routes_on_establish = false; // required when not using tunbuilder
71 bool dco = false;
72
73#ifdef HAVE_JSON
74 Json::Value to_json() override
75 {
76 Json::Value root(Json::objectValue);
77 root["iface_name"] = Json::Value(iface_name);
78 root["layer"] = Json::Value(layer.str());
79 root["dev_name"] = Json::Value(dev_name);
80 root["txqueuelen"] = Json::Value(txqueuelen);
81 root["dco"] = Json::Value(dco);
82 return root;
83 };
84
85 void from_json(const Json::Value &root, const std::string &title) override
86 {
87 json::assert_dict(root, title);
88 json::to_string(root, iface_name, "iface_name", title);
89 layer = Layer::from_str(json::get_string(root, "layer", title));
90 json::to_string(root, dev_name, "dev_name", title);
91 json::to_int(root, txqueuelen, "txqueuelen", title);
92 json::to_bool(root, dco, "dco", title);
93 }
94#endif
95 };
96
97 void destroy(std::ostream &os) override
98 {
99 // remove added routes
101
102 // remove bypass route
104 }
105
106 bool add_bypass_route(const std::string &address,
107 bool ipv6,
108 std::ostream &os)
109 {
110 // nothing to do if we reconnect to the same gateway
111 if (connected_gw == address)
112 return true;
113
114 // remove previous bypass route
116 remove_cmds_bypass_gw->clear();
117
118 const ActionList::Ptr add_cmds = new ActionList();
119 TUNMETHODS::add_bypass_route(tun_iface_name, address, ipv6, nullptr, *add_cmds, *remove_cmds_bypass_gw);
120
121 // add gateway bypass route
122 add_cmds->execute(os);
123 return true;
124 }
125
126 int establish(const TunBuilderCapture &pull, // defined by TunBuilderSetup::Base
128 Stop *stop,
129 std::ostream &os) override
130 {
131 // get configuration
132 Config *conf = dynamic_cast<Config *>(config);
133 if (!conf)
134 throw tun_linux_error("missing config");
135
136 int fd = -1;
137 if (!conf->dco)
138 {
139 fd = open_tun(conf);
140 }
141 else
142 {
143 // in DCO case device is already opened
145 }
146
147 const ActionList::Ptr add_cmds = new ActionList();
148 ActionList::Ptr remove_cmds_new = new ActionListReversed();
149
150 // configure tun properties
151 TUNMETHODS::tun_config(tun_iface_name,
152 pull,
153 nullptr,
154 *add_cmds,
155 *remove_cmds_new,
157
158 // execute commands to bring up interface
159 add_cmds->execute(os);
160
161 // tear down old routes
163 std::swap(remove_cmds, remove_cmds_new);
164
166
167 return fd;
168 }
169
170 private:
171 int open_tun(Config *conf)
172 {
173 static const char node[] = "/dev/net/tun";
174 ScopedFD fd(open(node, O_RDWR));
175 if (!fd.defined())
176 OPENVPN_THROW(tun_open_error, "error opening tun device " << node << ": " << errinfo(errno));
177
178 struct ifreq ifr;
179 std::memset(&ifr, 0, sizeof(ifr));
180 ifr.ifr_flags = IFF_ONE_QUEUE;
181 ifr.ifr_flags |= IFF_NO_PI;
182 if (conf->layer() == Layer::OSI_LAYER_3)
183 ifr.ifr_flags |= IFF_TUN;
184 else if (conf->layer() == Layer::OSI_LAYER_2)
185 ifr.ifr_flags |= IFF_TAP;
186 else
187 throw tun_layer_error("unknown OSI layer");
188
189 open_unit(conf->dev_name, ifr, fd);
190
191 if (fcntl(fd(), F_SETFL, O_NONBLOCK) < 0)
192 throw tun_fcntl_error(errinfo(errno));
193
194 // Set the TX send queue size
195 if (conf->txqueuelen)
196 {
197 struct ifreq netifr;
198 const ScopedFD ctl_fd(socket(AF_INET, SOCK_DGRAM, 0));
199
200 if (ctl_fd.defined())
201 {
202 std::memset(&netifr, 0, sizeof(netifr));
203 strcpy(netifr.ifr_name, ifr.ifr_name);
204 netifr.ifr_qlen = conf->txqueuelen;
205 if (ioctl(ctl_fd(), SIOCSIFTXQLEN, (void *)&netifr) < 0)
206 throw tun_tx_queue_len_error(errinfo(errno));
207 }
208 else
209 throw tun_tx_queue_len_error(errinfo(errno));
210 }
211 conf->iface_name = ifr.ifr_name;
212 tun_iface_name = ifr.ifr_name;
213
214 return fd.release();
215 }
216
217 void open_unit(const std::string &name, struct ifreq &ifr, ScopedFD &fd)
218 {
219 if (!name.empty())
220 {
221 const int max_units = 256;
222 for (int unit = 0; unit < max_units; ++unit)
223 {
224 std::string n = name;
225 if (unit)
226 n += openvpn::to_string(unit);
227 if (n.length() < IFNAMSIZ)
228 ::strcpy(ifr.ifr_name, n.c_str());
229 else
230 throw tun_name_error();
231 if (ioctl(fd(), TUNSETIFF, (void *)&ifr) == 0)
232 return;
233 }
234 const int eno = errno;
235 OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' after trying " << max_units << " units : " << errinfo(eno));
236 }
237 else
238 {
239 if (ioctl(fd(), TUNSETIFF, (void *)&ifr) < 0)
240 {
241 const int eno = errno;
242 OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' : " << errinfo(eno));
243 }
244 }
245 }
246
249
250 std::string connected_gw;
251
252 std::string tun_iface_name; // used to skip tun-based default gw when add bypass route
253};
254} // namespace openvpn::TunLinuxSetup
255
256#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:217
ActionList::Ptr remove_cmds_bypass_gw
Definition tunsetup.hpp:247
bool add_bypass_route(const std::string &address, bool ipv6, std::ostream &os)
Definition tunsetup.hpp:106
void destroy(std::ostream &os) override
Definition tunsetup.hpp:97
ActionListReversed::Ptr remove_cmds
Definition tunsetup.hpp:248
int open_tun(Config *conf)
Definition tunsetup.hpp:171
int establish(const TunBuilderCapture &pull, TunBuilderSetup::Config *config, Stop *stop, std::ostream &os) override
Definition tunsetup.hpp:126
#define OPENVPN_EXCEPTION(C)
Definition exception.hpp:99
#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
void from_json(const Json::Value &root, const std::string &title) override
Definition tunsetup.hpp:85
Json::Value to_json() override
Definition tunsetup.hpp:74
remote_address ipv6
remote_address address
std::ostringstream os
static const char config[]