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