1 /*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #elif defined(_MSC_VER)
27 #include "config-msvc.h"
28 #endif
29
30 #include "syshead.h"
31
32 #include "common.h"
33 #include "buffer.h"
34 #include "error.h"
35 #include "integer.h"
36 #include "mtu.h"
37 #include "options.h"
38
39 #include "memdbg.h"
40
41 /* allocate a buffer for socket or tun layer */
42 void
alloc_buf_sock_tun(struct buffer * buf,const struct frame * frame,const bool tuntap_buffer,const unsigned int align_mask)43 alloc_buf_sock_tun(struct buffer *buf,
44 const struct frame *frame,
45 const bool tuntap_buffer,
46 const unsigned int align_mask)
47 {
48 /* allocate buffer for overlapped I/O */
49 *buf = alloc_buf(BUF_SIZE(frame));
50 ASSERT(buf_init(buf, FRAME_HEADROOM_ADJ(frame, align_mask)));
51 buf->len = tuntap_buffer ? MAX_RW_SIZE_TUN(frame) : MAX_RW_SIZE_LINK(frame);
52 ASSERT(buf_safe(buf, 0));
53 }
54
55 void
frame_finalize(struct frame * frame,bool link_mtu_defined,int link_mtu,bool tun_mtu_defined,int tun_mtu)56 frame_finalize(struct frame *frame,
57 bool link_mtu_defined,
58 int link_mtu,
59 bool tun_mtu_defined,
60 int tun_mtu)
61 {
62 /* Set link_mtu based on command line options */
63 if (tun_mtu_defined)
64 {
65 ASSERT(!link_mtu_defined);
66 frame->link_mtu = tun_mtu + TUN_LINK_DELTA(frame);
67 }
68 else
69 {
70 ASSERT(link_mtu_defined);
71 frame->link_mtu = link_mtu;
72 }
73
74 if (TUN_MTU_SIZE(frame) < TUN_MTU_MIN)
75 {
76 msg(M_WARN, "TUN MTU value (%d) must be at least %d", TUN_MTU_SIZE(frame), TUN_MTU_MIN);
77 frame_print(frame, M_FATAL, "MTU is too small");
78 }
79
80 frame->link_mtu_dynamic = frame->link_mtu;
81 }
82
83 /*
84 * Set the tun MTU dynamically.
85 */
86 void
frame_set_mtu_dynamic(struct frame * frame,int mtu,unsigned int flags)87 frame_set_mtu_dynamic(struct frame *frame, int mtu, unsigned int flags)
88 {
89
90 #ifdef ENABLE_DEBUG
91 const int orig_mtu = mtu;
92 const int orig_link_mtu_dynamic = frame->link_mtu_dynamic;
93 #endif
94
95 ASSERT(mtu >= 0);
96
97 if (flags & SET_MTU_TUN)
98 {
99 mtu += TUN_LINK_DELTA(frame);
100 }
101
102 if (!(flags & SET_MTU_UPPER_BOUND) || mtu < frame->link_mtu_dynamic)
103 {
104 frame->link_mtu_dynamic = constrain_int(
105 mtu,
106 EXPANDED_SIZE_MIN(frame),
107 EXPANDED_SIZE(frame));
108 }
109
110 dmsg(D_MTU_DEBUG, "MTU DYNAMIC mtu=%d, flags=%u, %d -> %d",
111 orig_mtu,
112 flags,
113 orig_link_mtu_dynamic,
114 frame->link_mtu_dynamic);
115 }
116
117 /*
118 * Move extra_frame octets into extra_tun. Used by fragmenting code
119 * to adjust frame relative to its position in the buffer processing
120 * queue.
121 */
122 void
frame_subtract_extra(struct frame * frame,const struct frame * src)123 frame_subtract_extra(struct frame *frame, const struct frame *src)
124 {
125 frame->extra_frame -= src->extra_frame;
126 frame->extra_tun += src->extra_frame;
127 }
128
129 void
frame_init_mssfix(struct frame * frame,const struct options * options)130 frame_init_mssfix(struct frame *frame, const struct options *options)
131 {
132 if (options->ce.mssfix)
133 {
134 frame_set_mtu_dynamic(frame, options->ce.mssfix, SET_MTU_UPPER_BOUND);
135 }
136 }
137
138 void
frame_print(const struct frame * frame,int level,const char * prefix)139 frame_print(const struct frame *frame,
140 int level,
141 const char *prefix)
142 {
143 struct gc_arena gc = gc_new();
144 struct buffer out = alloc_buf_gc(256, &gc);
145 if (prefix)
146 {
147 buf_printf(&out, "%s ", prefix);
148 }
149 buf_printf(&out, "[");
150 buf_printf(&out, " L:%d", frame->link_mtu);
151 buf_printf(&out, " D:%d", frame->link_mtu_dynamic);
152 buf_printf(&out, " EF:%d", frame->extra_frame);
153 buf_printf(&out, " EB:%d", frame->extra_buffer);
154 buf_printf(&out, " ET:%d", frame->extra_tun);
155 buf_printf(&out, " EL:%d", frame->extra_link);
156 if (frame->align_flags && frame->align_adjust)
157 {
158 buf_printf(&out, " AF:%u/%d", frame->align_flags, frame->align_adjust);
159 }
160 buf_printf(&out, " ]");
161
162 msg(level, "%s", out.data);
163 gc_free(&gc);
164 }
165
166 #define MTUDISC_NOT_SUPPORTED_MSG "--mtu-disc is not supported on this OS"
167
168 void
set_mtu_discover_type(socket_descriptor_t sd,int mtu_type,sa_family_t proto_af)169 set_mtu_discover_type(socket_descriptor_t sd, int mtu_type, sa_family_t proto_af)
170 {
171 if (mtu_type >= 0)
172 {
173 switch (proto_af)
174 {
175 #if defined(IP_MTU_DISCOVER)
176 case AF_INET:
177 if (setsockopt(sd, IPPROTO_IP, IP_MTU_DISCOVER,
178 (void *) &mtu_type, sizeof(mtu_type)))
179 {
180 msg(M_ERR, "Error setting IP_MTU_DISCOVER type=%d on TCP/UDP socket",
181 mtu_type);
182 }
183 break;
184
185 #endif
186 #if defined(IPV6_MTU_DISCOVER)
187 case AF_INET6:
188 if (setsockopt(sd, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
189 (void *) &mtu_type, sizeof(mtu_type)))
190 {
191 msg(M_ERR, "Error setting IPV6_MTU_DISCOVER type=%d on TCP6/UDP6 socket",
192 mtu_type);
193 }
194 break;
195
196 #endif
197 default:
198 msg(M_FATAL, MTUDISC_NOT_SUPPORTED_MSG);
199 break;
200 }
201 }
202 }
203
204 int
translate_mtu_discover_type_name(const char * name)205 translate_mtu_discover_type_name(const char *name)
206 {
207 #if defined(IP_PMTUDISC_DONT) && defined(IP_PMTUDISC_WANT) && defined(IP_PMTUDISC_DO)
208 if (!strcmp(name, "yes"))
209 {
210 return IP_PMTUDISC_DO;
211 }
212 if (!strcmp(name, "maybe"))
213 {
214 return IP_PMTUDISC_WANT;
215 }
216 if (!strcmp(name, "no"))
217 {
218 return IP_PMTUDISC_DONT;
219 }
220 msg(M_FATAL,
221 "invalid --mtu-disc type: '%s' -- valid types are 'yes', 'maybe', or 'no'",
222 name);
223 #else /* if defined(IP_PMTUDISC_DONT) && defined(IP_PMTUDISC_WANT) && defined(IP_PMTUDISC_DO) */
224 msg(M_FATAL, MTUDISC_NOT_SUPPORTED_MSG);
225 #endif
226 return -1; /* NOTREACHED */
227 }
228
229 #if EXTENDED_SOCKET_ERROR_CAPABILITY
230
231 struct probehdr
232 {
233 uint32_t ttl;
234 struct timeval tv;
235 };
236
237 const char *
format_extended_socket_error(int fd,int * mtu,struct gc_arena * gc)238 format_extended_socket_error(int fd, int *mtu, struct gc_arena *gc)
239 {
240 int res;
241 struct probehdr rcvbuf;
242 struct iovec iov;
243 struct msghdr msg;
244 struct cmsghdr *cmsg;
245 struct sock_extended_err *e;
246 struct sockaddr_in addr;
247 struct buffer out = alloc_buf_gc(256, gc);
248 char *cbuf = (char *) gc_malloc(256, false, gc);
249
250 *mtu = 0;
251
252 while (true)
253 {
254 memset(&rcvbuf, -1, sizeof(rcvbuf));
255 iov.iov_base = &rcvbuf;
256 iov.iov_len = sizeof(rcvbuf);
257 msg.msg_name = (uint8_t *) &addr;
258 msg.msg_namelen = sizeof(addr);
259 msg.msg_iov = &iov;
260 msg.msg_iovlen = 1;
261 msg.msg_flags = 0;
262 msg.msg_control = cbuf;
263 msg.msg_controllen = 256; /* size of cbuf */
264
265 res = recvmsg(fd, &msg, MSG_ERRQUEUE);
266 if (res < 0)
267 {
268 goto exit;
269 }
270
271 e = NULL;
272
273 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
274 {
275 if (cmsg->cmsg_level == SOL_IP)
276 {
277 if (cmsg->cmsg_type == IP_RECVERR)
278 {
279 e = (struct sock_extended_err *) CMSG_DATA(cmsg);
280 }
281 else
282 {
283 buf_printf(&out,"CMSG=%d|", cmsg->cmsg_type);
284 }
285 }
286 }
287 if (e == NULL)
288 {
289 buf_printf(&out, "NO-INFO|");
290 goto exit;
291 }
292
293 switch (e->ee_errno)
294 {
295 case ETIMEDOUT:
296 buf_printf(&out, "ETIMEDOUT|");
297 break;
298
299 case EMSGSIZE:
300 buf_printf(&out, "EMSGSIZE Path-MTU=%d|", e->ee_info);
301 *mtu = e->ee_info;
302 break;
303
304 case ECONNREFUSED:
305 buf_printf(&out, "ECONNREFUSED|");
306 break;
307
308 case EPROTO:
309 buf_printf(&out, "EPROTO|");
310 break;
311
312 case EHOSTUNREACH:
313 buf_printf(&out, "EHOSTUNREACH|");
314 break;
315
316 case ENETUNREACH:
317 buf_printf(&out, "ENETUNREACH|");
318 break;
319
320 case EACCES:
321 buf_printf(&out, "EACCES|");
322 break;
323
324 default:
325 buf_printf(&out, "UNKNOWN|");
326 break;
327 }
328 }
329
330 exit:
331 buf_rmtail(&out, '|');
332 return BSTR(&out);
333 }
334
335 void
set_sock_extended_error_passing(int sd)336 set_sock_extended_error_passing(int sd)
337 {
338 int on = 1;
339 if (setsockopt(sd, SOL_IP, IP_RECVERR, (void *) &on, sizeof(on)))
340 {
341 msg(M_WARN | M_ERRNO,
342 "Note: enable extended error passing on TCP/UDP socket failed (IP_RECVERR)");
343 }
344 }
345
346 #endif /* if EXTENDED_SOCKET_ERROR_CAPABILITY */
347