OpenVPN
proto.c
Go to the documentation of this file.
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-2026 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, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29#include "proto.h"
30#include "error.h"
31
32#include "memdbg.h"
33
34/*
35 * If raw tunnel packet is IPv<X>, return true and increment
36 * buffer offset to start of IP header.
37 */
38static bool
39is_ipv_X(int tunnel_type, struct buffer *buf, int ip_ver)
40{
41 int offset;
42 const struct openvpn_iphdr *ih;
43
44 verify_align_4(buf);
45 if (tunnel_type == DEV_TYPE_TUN)
46 {
47 if (BLENZ(buf) < sizeof(struct openvpn_iphdr))
48 {
49 return false;
50 }
51 offset = 0;
52 }
53 else if (tunnel_type == DEV_TYPE_TAP)
54 {
55 const struct openvpn_ethhdr *eh;
56 if (BLENZ(buf) < sizeof(struct openvpn_ethhdr) + sizeof(struct openvpn_iphdr))
57 {
58 return false;
59 }
60 eh = (const struct openvpn_ethhdr *)BPTR(buf);
61
62 /* start by assuming this is a standard Eth fram */
63 uint16_t proto = eh->proto;
64 offset = sizeof(struct openvpn_ethhdr);
65
66 /* if this is a 802.1q frame, parse the header using the according
67 * format
68 */
69 if (proto == htons(OPENVPN_ETH_P_8021Q))
70 {
71 const struct openvpn_8021qhdr *evh;
72 if (BLENZ(buf) < sizeof(struct openvpn_8021qhdr) + sizeof(struct openvpn_iphdr))
73 {
74 return false;
75 }
76
77 evh = (const struct openvpn_8021qhdr *)BPTR(buf);
78
79 proto = evh->proto;
80 offset = sizeof(struct openvpn_8021qhdr);
81 }
82
83 if (ntohs(proto) != (ip_ver == 6 ? OPENVPN_ETH_P_IPV6 : OPENVPN_ETH_P_IPV4))
84 {
85 return false;
86 }
87 }
88 else
89 {
90 return false;
91 }
92
93 ih = (const struct openvpn_iphdr *)(BPTR(buf) + offset);
94
95 /* IP version is stored in the same bits for IPv4 or IPv6 header */
96 if (OPENVPN_IPH_GET_VER(ih->version_len) == ip_ver)
97 {
98 return buf_advance(buf, offset);
99 }
100 else
101 {
102 return false;
103 }
104}
105
106bool
107is_ipv4(int tunnel_type, struct buffer *buf)
108{
109 return is_ipv_X(tunnel_type, buf, 4);
110}
111bool
112is_ipv6(int tunnel_type, struct buffer *buf)
113{
114 return is_ipv_X(tunnel_type, buf, 6);
115}
116
117
118uint16_t
119ip_checksum(const sa_family_t af, const uint8_t *payload, const int len_payload,
120 const uint8_t *src_addr, const uint8_t *dest_addr, const int proto)
121{
122 uint32_t sum = 0;
123 int addr_len = (af == AF_INET) ? 4 : 16;
124
125 /*
126 * make 16 bit words out of every two adjacent 8 bit words and */
127 /* calculate the sum of all 16 bit words
128 */
129 for (int i = 0; i < len_payload; i += 2)
130 {
131 sum += (uint16_t)(((payload[i] << 8) & 0xFF00)
132 + ((i + 1 < len_payload) ? (payload[i + 1] & 0xFF) : 0));
133 }
134
135 /*
136 * add the pseudo header which contains the IP source and destination
137 * addresses
138 */
139 for (int i = 0; i < addr_len; i += 2)
140 {
141 sum += (uint16_t)((src_addr[i] << 8) & 0xFF00) + (src_addr[i + 1] & 0xFF);
142 }
143 for (int i = 0; i < addr_len; i += 2)
144 {
145 sum += (uint16_t)((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i + 1] & 0xFF);
146 }
147
148 /* the length of the payload */
149 sum += (uint16_t)len_payload;
150
151 /* The next header or proto field*/
152 sum += (uint16_t)proto;
153
154 /*
155 * keep only the last 16 bits of the 32 bit calculated sum and add
156 * the carries
157 */
158 while (sum >> 16)
159 {
160 sum = (sum & 0xFFFF) + (sum >> 16);
161 }
162
163 /* Take the one's complement of sum */
164 return ((uint16_t)~sum);
165}
166
167#ifdef PACKET_TRUNCATION_CHECK
168
169void
170ipv4_packet_size_verify(const uint8_t *data, const int size, const int tunnel_type,
171 const char *prefix, counter_type *errors)
172{
173 if (size > 0)
174 {
175 struct buffer buf;
176
177 buf_set_read(&buf, data, size);
178
179 if (is_ipv4(tunnel_type, &buf))
180 {
181 const struct openvpn_iphdr *pip;
182 int hlen;
183 int totlen;
184 const char *msgstr = "PACKET SIZE INFO";
186
187 if (BLENZ(&buf) < sizeof(struct openvpn_iphdr))
188 {
189 return;
190 }
191
192 verify_align_4(&buf);
193 pip = (struct openvpn_iphdr *)BPTR(&buf);
194
195 hlen = OPENVPN_IPH_GET_LEN(pip->version_len);
196 totlen = ntohs(pip->tot_len);
197
198 if (BLEN(&buf) != totlen)
199 {
200 msgstr = "PACKET TRUNCATION ERROR";
201 msglevel = D_PACKET_TRUNC_ERR;
202 if (errors)
203 {
204 ++(*errors);
205 }
206 }
207
208 msg(msglevel, "%s %s: size=%d totlen=%d hlen=%d errcount=" counter_format, msgstr,
209 prefix, BLEN(&buf), totlen, hlen, errors ? *errors : (counter_type)0);
210 }
211 }
212}
213
214#endif /* ifdef PACKET_TRUNCATION_CHECK */
#define BPTR(buf)
Return a pointer to the start of the buffer content.
Definition buffer.h:139
static void buf_set_read(struct buffer *buf, const uint8_t *data, size_t size)
Initialise a buffer with an externally provided read-only memory region.
Definition buffer.h:687
static bool buf_advance(struct buffer *buf, ssize_t size)
Advance the content start of a buffer, consuming bytes from the front.
Definition buffer.h:1188
#define verify_align_4(ptr)
Definition buffer.h:1878
#define BLEN(buf)
Return the length of the buffer content in bytes.
Definition buffer.h:151
#define BLENZ(buf)
Return the length of the buffer content as a size_t.
Definition buffer.h:153
uint64_t counter_type
Definition common.h:31
#define counter_format
Definition common.h:32
#define D_PACKET_TRUNC_ERR
Definition errlevel.h:99
#define D_PACKET_TRUNC_DEBUG
Definition errlevel.h:142
#define msg(flags,...)
Definition error.h:152
unsigned int msglvl_t
Definition error.h:77
bool is_ipv4(int tunnel_type, struct buffer *buf)
Definition proto.c:107
bool is_ipv6(int tunnel_type, struct buffer *buf)
Definition proto.c:112
static bool is_ipv_X(int tunnel_type, struct buffer *buf, int ip_ver)
Definition proto.c:39
uint16_t ip_checksum(const sa_family_t af, const uint8_t *payload, const int len_payload, const uint8_t *src_addr, const uint8_t *dest_addr, const int proto)
Calculates an IP or IPv6 checksum with a pseudo header as required by TCP, UDP and ICMPv6.
Definition proto.c:119
#define OPENVPN_ETH_P_8021Q
Definition proto.h:61
#define OPENVPN_IPH_GET_LEN(v)
Definition proto.h:92
#define DEV_TYPE_TAP
Definition proto.h:36
#define OPENVPN_ETH_P_IPV6
Definition proto.h:59
#define DEV_TYPE_TUN
Definition proto.h:35
#define OPENVPN_ETH_P_IPV4
Definition proto.h:58
#define OPENVPN_IPH_GET_VER(v)
Definition proto.h:91
Wrapper structure for dynamically allocated memory.
Definition buffer.h:71
uint8_t * data
Pointer to the allocated memory.
Definition buffer.h:78
uint16_t proto
Definition proto.h:75
uint16_t proto
Definition proto.h:62
uint16_t tot_len
Definition proto.h:96
uint8_t version_len
Definition proto.h:93
unsigned short sa_family_t
Definition syshead.h:409