Coverage Report

Created: 2026-09-19 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ndpi/src/lib/protocols/openvpn.c
Line
Count
Source
1
/*
2
 * openvpn.c
3
 *
4
 * Copyright (C) 2011-26 - ntop.org
5
 *
6
  *
7
 * nDPI is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as published by
9
 * the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * nDPI is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with nDPI.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 */
21
22
#include "ndpi_protocol_ids.h"
23
24
#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_OPENVPN
25
26
#include "ndpi_api.h"
27
#include "ndpi_private.h"
28
29
30
/*
31
 * OpenVPN TCP / UDP Detection - 128/160 hmac
32
 *
33
 * Detection based upon these openvpn protocol properties:
34
 *   - opcode
35
 *   - packet ID
36
 *   - session ID
37
 *
38
 * TODO
39
 *  - Support PSK only mode (instead of TLS)
40
 *  - Support PSK + TLS mode (PSK used for early authentication)
41
 *  - TLS certificate extraction
42
 *
43
 */
44
45
6.16M
#define P_CONTROL_HARD_RESET_CLIENT_V1  (0x01 << 3)
46
6.08M
#define P_CONTROL_HARD_RESET_SERVER_V1  (0x02 << 3)
47
5.77M
#define P_CONTROL_V1                    (0x04 << 3)
48
5.72M
#define P_ACK_V1                        (0x05 << 3)
49
5.88M
#define P_CONTROL_HARD_RESET_CLIENT_V2  (0x07 << 3)
50
5.80M
#define P_CONTROL_HARD_RESET_SERVER_V2  (0x08 << 3)
51
5.52M
#define P_CONTROL_HARD_RESET_CLIENT_V3  (0x0A << 3)
52
2.43M
#define P_CONTROL_WKC_V1                (0x0B << 3)
53
54
5.03M
#define P_OPCODE_MASK 0xF8
55
#define P_SHA1_HMAC_SIZE 20
56
8.10k
#define P_HMAC_128 16                            // (RSA-)MD5, (RSA-)MD4, ..others
57
8.15k
#define P_HMAC_160 20                            // (RSA-|DSA-)SHA(1), ..others, SHA1 is openvpn default
58
2.39k
#define P_HMAC_NONE 0                            // No HMAC
59
23.8k
#define P_HARD_RESET_PACKET_ID_OFFSET(hmac_size) (9 + hmac_size)
60
5.41k
#define P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size)  (P_HARD_RESET_PACKET_ID_OFFSET(hmac_size) + 8 * (!!(hmac_size)))
61
62
63
static void ndpi_int_openvpn_add_connection(struct ndpi_detection_module_struct * const ndpi_struct,
64
                                            struct ndpi_flow_struct * const flow,
65
                                            ndpi_confidence_t confidence)
66
2.50k
{
67
2.50k
  if(ndpi_struct->cfg.openvpn_subclassification_by_ip &&
68
2.50k
     ndpi_struct->proto_defaults[flow->core.guessed_protocol_id_by_ip].protoCategory == NDPI_PROTOCOL_CATEGORY_VPN) {
69
59
    ndpi_set_detected_protocol(ndpi_struct, &flow->core, flow->core.guessed_protocol_id_by_ip, NDPI_PROTOCOL_OPENVPN, confidence);
70
2.44k
  } else {
71
2.44k
    ndpi_set_detected_protocol(ndpi_struct, &flow->core, NDPI_PROTOCOL_OPENVPN, NDPI_PROTOCOL_UNKNOWN, confidence);
72
2.44k
  }
73
2.50k
}
74
75
static int is_opcode_valid(u_int8_t opcode)
76
2.95M
{
77
  /* Ignore:
78
     * P_DATA_V1/2: they don't have any (useful) info in the header
79
     * P_CONTROL_SOFT_RESET_V1: it is used to key renegotiation -> it is not at the beginning of the session
80
  */
81
2.95M
  return opcode == P_CONTROL_HARD_RESET_CLIENT_V1 ||
82
2.88M
   opcode == P_CONTROL_HARD_RESET_SERVER_V1 ||
83
2.82M
   opcode == P_CONTROL_V1 ||
84
2.76M
   opcode == P_ACK_V1 ||
85
2.70M
   opcode == P_CONTROL_HARD_RESET_CLIENT_V2 ||
86
2.64M
   opcode == P_CONTROL_HARD_RESET_SERVER_V2 ||
87
2.52M
   opcode == P_CONTROL_HARD_RESET_CLIENT_V3 ||
88
2.43M
   opcode == P_CONTROL_WKC_V1;
89
2.95M
}
90
91
15.8k
static u_int32_t get_packet_id(const u_int8_t * payload, u_int8_t hms) {
92
15.8k
  return(ntohl(*(u_int32_t*)(payload + P_HARD_RESET_PACKET_ID_OFFSET(hms))));
93
15.8k
}
94
95
/* From wireshark */
96
/* We check the leading 4 byte of a suspected hmac for 0x00 bytes,
97
   if more than 1 byte out of the 4 provided contains 0x00, the
98
   hmac is considered not valid, which suggests that no tls auth is used.
99
   unfortunatly there is no other way to detect tls auth on the fly */
100
static int check_for_valid_hmac(u_int32_t hmac)
101
7.06k
{
102
7.06k
  int c = 0;
103
104
7.06k
  if((hmac & 0x000000FF) == 0x00000000)
105
924
    c++;
106
7.06k
  if((hmac & 0x0000FF00) == 0x00000000)
107
839
    c++;
108
7.06k
  if ((hmac & 0x00FF0000) == 0x00000000)
109
720
    c++;
110
7.06k
  if ((hmac & 0xFF000000) == 0x00000000)
111
0
    c++;
112
7.06k
  if (c > 1)
113
504
    return 0;
114
6.55k
  return 1;
115
7.06k
}
116
117
9.35k
static int8_t detect_hmac_size(const u_int8_t *payload, int payload_len) {
118
  // try to guess
119
9.35k
  if((payload_len >= P_HARD_RESET_PACKET_ID_OFFSET(P_HMAC_160) + 4) &&
120
7.92k
     get_packet_id(payload, P_HMAC_160) == 1)
121
232
    return P_HMAC_160;
122
  
123
9.11k
  if((payload_len >= P_HARD_RESET_PACKET_ID_OFFSET(P_HMAC_128) + 4) &&
124
7.94k
     get_packet_id(payload, P_HMAC_128) == 1)
125
160
    return P_HMAC_128;
126
127
  /* Heuristic from Wireshark, to detect no-HMAC flows (i.e. tls-crypt) */
128
8.95k
  if(payload_len >= 14 &&
129
8.95k
     !(payload[9] > 0 &&
130
7.06k
       check_for_valid_hmac(ntohl(*(u_int32_t*)(payload + 9)))))
131
2.39k
    return P_HMAC_NONE;
132
133
6.55k
  return(-1);
134
8.95k
}
135
136
static int search_standard(struct ndpi_detection_module_struct* ndpi_struct,
137
4.33M
                           struct ndpi_flow_struct* flow) {
138
4.33M
  struct ndpi_packet_struct* packet = &ndpi_struct->packet;
139
4.33M
  const u_int8_t * ovpn_payload = packet->payload;
140
4.33M
  const u_int8_t * session_remote;
141
4.33M
  u_int8_t opcode;
142
4.33M
  u_int8_t alen;
143
4.33M
  int8_t hmac_size;
144
4.33M
  int8_t failed = 0;
145
4.33M
  /* No u_ */int16_t ovpn_payload_len = packet->payload_packet_len;
146
4.33M
  int dir = packet->packet_direction;
147
148
  /* Detection:
149
   * (1) server and client resets matching (via session id -> remote session id)
150
   * (2) consecutive packets (in both directions) with the same session id
151
   * (3) asymmetric traffic
152
  */
153
154
4.33M
  if(ovpn_payload_len < 14 + 2 * (packet->tcp != NULL)) {
155
1.37M
    return 1; /* Exclude */
156
1.37M
  }
157
158
  /* Skip openvpn TCP transport packet size */
159
2.95M
  if(packet->tcp != NULL)
160
2.01M
    ovpn_payload += 2, ovpn_payload_len -= 2;
161
162
2.95M
  opcode = ovpn_payload[0] & P_OPCODE_MASK;
163
2.95M
  if(!is_opcode_valid(opcode)) {
164
2.37M
    return 1; /* Exclude */
165
2.37M
  }
166
  /* Maybe a strong assumption... */
167
577k
  if((ovpn_payload[0] & ~P_OPCODE_MASK) != 0) {
168
437k
    NDPI_LOG_DBG2(ndpi_struct, "Invalid key id\n");
169
437k
    return 1; /* Exclude */
170
437k
  }
171
140k
  if(flow->core.packet_direction_counter[dir] == 1 &&
172
117k
     !(opcode == P_CONTROL_HARD_RESET_CLIENT_V1 ||
173
96.9k
       opcode == P_CONTROL_HARD_RESET_CLIENT_V2 ||
174
88.5k
       opcode == P_CONTROL_HARD_RESET_SERVER_V1 ||
175
70.8k
       opcode == P_CONTROL_HARD_RESET_SERVER_V2 ||
176
40.7k
       opcode == P_CONTROL_HARD_RESET_CLIENT_V3)) {
177
32.6k
    NDPI_LOG_DBG2(ndpi_struct, "Invalid first packet\n");
178
32.6k
    return 1; /* Exclude */
179
32.6k
  }
180
  /* Resets are small packets */
181
108k
  if(packet->payload_packet_len >= 1200 &&
182
6.20k
     (opcode == P_CONTROL_HARD_RESET_CLIENT_V1 ||
183
5.24k
      opcode == P_CONTROL_HARD_RESET_CLIENT_V2 ||
184
4.82k
      opcode == P_CONTROL_HARD_RESET_SERVER_V1 ||
185
3.14k
      opcode == P_CONTROL_HARD_RESET_SERVER_V2 ||
186
5.97k
      opcode == P_CONTROL_HARD_RESET_CLIENT_V3)) {
187
5.97k
    NDPI_LOG_DBG2(ndpi_struct, "Invalid len first pkt (QUIC collision)\n");
188
5.97k
    return 1; /* Exclude */
189
5.97k
  }
190
102k
  if(flow->core.packet_direction_counter[dir] == 1 &&
191
79.0k
     packet->tcp &&
192
102k
     ntohs(*(u_int16_t *)(packet->payload)) != ovpn_payload_len) {
193
27.1k
    NDPI_LOG_DBG2(ndpi_struct, "Invalid tcp len on reset\n");
194
27.1k
    return 1; /* Exclude */
195
27.1k
  }
196
197
74.9k
  NDPI_LOG_DBG2(ndpi_struct, "[packets %d/%d][opcode: %u][len: %u]\n",
198
74.9k
                flow->core.packet_direction_counter[dir],
199
74.9k
                flow->core.packet_direction_counter[!dir],
200
74.9k
                opcode, ovpn_payload_len);
201
202
74.9k
  if(flow->core.packet_direction_counter[dir] > 1) {
203
23.0k
    if(memcmp(flow->metadata.openvpn.ovpn_session_id[dir], ovpn_payload + 1, 8) != 0) {
204
3.53k
      NDPI_LOG_DBG2(ndpi_struct, "Invalid session id on two consecutive pkts in the same dir\n");
205
3.53k
      return 1; /* Exclude */
206
3.53k
    }
207
19.5k
    if(flow->core.packet_direction_counter[dir] >= 2 &&
208
19.5k
       flow->core.packet_direction_counter[!dir] >= 2) {
209
      /* (2) */
210
1.02k
      NDPI_LOG_INFO(ndpi_struct,"found openvpn (session ids match on both direction)\n");
211
1.02k
      return 2; /* Found */
212
1.02k
    }
213
18.5k
    if(flow->core.packet_direction_counter[dir] >= 4 &&
214
2.25k
       flow->core.packet_direction_counter[!dir] == 0) {
215
      /* (3) */
216
1.21k
      NDPI_LOG_INFO(ndpi_struct,"found openvpn (asymmetric)\n");
217
1.21k
      return 2; /* Found */
218
1.21k
    }
219
51.9k
  } else {
220
51.9k
    memcpy(flow->metadata.openvpn.ovpn_session_id[dir], ovpn_payload + 1, 8);
221
51.9k
    NDPI_LOG_DBG2(ndpi_struct, "Session key [%d]: 0x%lx\n", dir,
222
51.9k
                  ndpi_ntohll(*(u_int64_t *)flow->metadata.openvpn.ovpn_session_id[dir]));
223
51.9k
  }
224
225
  /* (1) */
226
69.2k
  if(flow->core.packet_direction_counter[!dir] > 0 &&
227
12.1k
     (opcode == P_CONTROL_HARD_RESET_SERVER_V1 ||
228
9.35k
      opcode == P_CONTROL_HARD_RESET_SERVER_V2)) {
229
230
9.35k
    hmac_size = detect_hmac_size(ovpn_payload, ovpn_payload_len);
231
9.35k
    NDPI_LOG_DBG2(ndpi_struct, "hmac size %d\n", hmac_size);
232
9.35k
    failed = 0;
233
9.35k
    if(hmac_size >= 0 &&
234
2.79k
       P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size) < ovpn_payload_len) {
235
2.62k
      u_int16_t offset = P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size);
236
237
2.62k
      alen = ovpn_payload[offset];
238
239
2.62k
      if(alen > 0) {
240
614
        offset += 1 + alen * 4;
241
242
614
        if((offset + 8) <= ovpn_payload_len) {
243
383
          session_remote = &ovpn_payload[offset];
244
245
383
          if(memcmp(flow->metadata.openvpn.ovpn_session_id[!dir], session_remote, 8) == 0) {
246
149
            NDPI_LOG_INFO(ndpi_struct,"found openvpn\n");
247
149
            return 2; /* Found */
248
234
          } else {
249
234
            NDPI_LOG_DBG2(ndpi_struct, "key mismatch 0x%lx\n", ndpi_ntohll(*(u_int64_t *)session_remote));
250
234
          }
251
383
        }
252
465
        failed = 1;
253
2.01k
      } else {
254
        /* Server reset without remote session id field; no failure */
255
2.01k
      }
256
2.62k
    }
257
9.35k
  }
258
259
69.0k
  if(failed || flow->core.packet_counter > 5)
260
729
    return 1; /* Exclude */
261
68.3k
  return 0; /* Continue */
262
69.0k
}
263
264
/* Heuristic to detect encrypted/obfusctaed OpenVPN flows, based on
265
   https://www.usenix.org/conference/usenixsecurity22/presentation/xue-diwen.
266
   Main differences between the paper and our implementation:
267
    * only op-code fingerprint
268
269
   Core idea: even if the OpenVPN packets are somehow encrypted to avoid trivial
270
   detection, the distibution of the first byte of the messages (i.e. the
271
   distribution of the op-codes) might still be unique
272
*/
273
274
static int search_heur_opcode_common(struct ndpi_detection_module_struct* ndpi_struct,
275
                                     struct ndpi_flow_struct* flow,
276
1.50M
                                     u_int8_t first_byte) {
277
1.50M
  u_int8_t opcode, found  = 0, i;
278
1.50M
  int dir = ndpi_struct->packet.packet_direction;
279
280
1.50M
  opcode = first_byte & P_OPCODE_MASK;
281
282
  /* Handshake:
283
      * 2 different resets
284
      * up to 3 different opcodes (ack, control, wkc)
285
      * 1 data (v1 or v2)
286
     So, other than the resets:
287
      * at least 2 different opcodes (ack, control)
288
      * no more than 4 (i.e. OPENVPN_HEUR_MAX_NUM_OPCODES) different opcodes
289
  */
290
291
1.50M
  NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: [packets %d/%d msgs %d, dir %d][first byte 0x%x][opcode: 0x%x]\n",
292
1.50M
                flow->core.packet_direction_counter[0],
293
1.50M
                flow->core.packet_direction_counter[1],
294
1.50M
                flow->metadata.openvpn.ovpn_heur_opcode__num_msgs,
295
1.50M
                dir, first_byte, opcode);
296
297
1.50M
  flow->metadata.openvpn.ovpn_heur_opcode__num_msgs++;
298
299
1.50M
  if(flow->core.packet_direction_counter[dir] == 1) {
300
1.15M
    flow->metadata.openvpn.ovpn_heur_opcode__resets[dir] = opcode;
301
1.15M
    if(flow->core.packet_direction_counter[!dir] > 0 &&
302
95.2k
       opcode == flow->metadata.openvpn.ovpn_heur_opcode__resets[!dir]) {
303
80.2k
      NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: same resets\n");
304
80.2k
      return 1; /* Exclude */
305
80.2k
    }
306
1.07M
    return 0; /* Continue */
307
1.15M
  }
308
309
348k
  if(opcode == flow->metadata.openvpn.ovpn_heur_opcode__resets[dir]) {
310
318k
    if(flow->metadata.openvpn.ovpn_heur_opcode__codes_num > 0) {
311
489
      NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: resets after other opcodes\n");
312
489
      return 1; /* Exclude */
313
489
    }
314
318k
    return 0; /* Continue */
315
318k
  }
316
29.4k
  if(flow->core.packet_direction_counter[!dir] > 0 &&
317
13.9k
     opcode == flow->metadata.openvpn.ovpn_heur_opcode__resets[!dir]) {
318
3.99k
    NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: same resets\n");
319
3.99k
    return 1; /* Exclude */
320
3.99k
  }
321
322
25.4k
  if(flow->core.packet_direction_counter[!dir] == 0) {
323
15.4k
    NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: opcode different than reset but not reset in the other direction\n");
324
15.4k
    return 1; /* Exclude */
325
15.4k
  }
326
327
9.94k
  if(flow->metadata.openvpn.ovpn_heur_opcode__codes_num == OPENVPN_HEUR_MAX_NUM_OPCODES &&
328
685
     opcode != flow->metadata.openvpn.ovpn_heur_opcode__codes[OPENVPN_HEUR_MAX_NUM_OPCODES - 1]) {
329
241
    NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: once data we can't have other opcode\n");
330
    /* TODO: this check assumes that the "data" opcode is the 4th one (after the resets).
331
     * But we usually have only ack + control + data... */
332
241
    return 1; /* Exclude */
333
241
  }
334
335
22.4k
  for(i = 0; i < flow->metadata.openvpn.ovpn_heur_opcode__codes_num; i++) {
336
12.7k
    if(flow->metadata.openvpn.ovpn_heur_opcode__codes[i] == opcode)
337
5.09k
      found = 1;
338
12.7k
  }
339
9.70k
  if(found == 0) {
340
4.61k
    if(flow->metadata.openvpn.ovpn_heur_opcode__codes_num == OPENVPN_HEUR_MAX_NUM_OPCODES) {
341
0
      NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: too many opcodes. Early exclude\n");
342
0
      return 1; /* Exclude */
343
0
    }
344
4.61k
    flow->metadata.openvpn.ovpn_heur_opcode__codes[flow->metadata.openvpn.ovpn_heur_opcode__codes_num++] = opcode;
345
4.61k
  }
346
347
9.70k
  NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: Resets 0x%x,0x%x Num %d\n",
348
9.70k
                flow->metadata.openvpn.ovpn_heur_opcode__resets[0],
349
9.70k
                flow->metadata.openvpn.ovpn_heur_opcode__resets[1],
350
9.70k
                flow->metadata.openvpn.ovpn_heur_opcode__codes_num);
351
352
9.70k
  if(flow->metadata.openvpn.ovpn_heur_opcode__num_msgs < ndpi_struct->cfg.openvpn_heuristics_num_msgs)
353
9.55k
    return 0; /* Continue */
354
355
  /* Done. Check what we have found...*/
356
357
150
  if(flow->core.packet_direction_counter[0] == 0 ||
358
150
     flow->core.packet_direction_counter[1] == 0) {
359
0
    NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: excluded because asymmetric traffic\n");
360
0
    return 1; /* Exclude */
361
0
  }
362
363
150
  if(flow->metadata.openvpn.ovpn_heur_opcode__codes_num >= 2) {
364
119
    NDPI_LOG_INFO(ndpi_struct,"found openvpn (Heur-opcode)\n");
365
119
    return 2; /* Found */
366
119
  }
367
31
  NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: excluded\n");
368
31
  return 1; /* Exclude */
369
150
}
370
371
static int search_heur_opcode(struct ndpi_detection_module_struct* ndpi_struct,
372
4.36M
                              struct ndpi_flow_struct* flow) {
373
4.36M
  struct ndpi_packet_struct* packet = &ndpi_struct->packet;
374
4.36M
  const u_int8_t *ovpn_payload = packet->payload;
375
4.36M
  u_int16_t ovpn_payload_len = packet->payload_packet_len;
376
4.36M
  int dir = packet->packet_direction;
377
4.36M
  u_int16_t pdu_len;
378
4.36M
  int rc, offset;
379
#ifdef NDPI_ENABLE_DEBUG_MESSAGES
380
  int iter;
381
#endif
382
383
  /* To reduce false positives number, trigger the heuristic only for flows to
384
     suspicious/unknown addresses */
385
4.36M
  if(is_flow_addr_informative(flow)) {
386
139k
    NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: flow to informative address. Exclude\n");
387
139k
    return 1; /* Exclude */
388
139k
  }
389
390
4.22M
  if(packet->tcp != NULL) {
391
    /* Two bytes field with pdu length */
392
393
2.91M
    NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: TCP length %d (remaining %d)\n",
394
2.91M
                  ovpn_payload_len,
395
2.91M
                  flow->metadata.openvpn.ovpn_heur_opcode__missing_bytes[dir]);
396
397
    /* We might need to "reassemble" the OpenVPN messages.
398
       Luckily, we are not interested in the message itself, but only in the first byte
399
       (after the length field), so as state we only need to know the "missing bytes"
400
       of the latest pdu (from the previous TCP packets) */
401
2.91M
    if(flow->metadata.openvpn.ovpn_heur_opcode__missing_bytes[dir] > 0) {
402
153k
      NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: TCP, remaining bytes to ignore %d length %d\n",
403
153k
                    flow->metadata.openvpn.ovpn_heur_opcode__missing_bytes[dir], ovpn_payload_len);
404
153k
      if(flow->metadata.openvpn.ovpn_heur_opcode__missing_bytes[dir] >= ovpn_payload_len) {
405
144k
        flow->metadata.openvpn.ovpn_heur_opcode__missing_bytes[dir] -= ovpn_payload_len;
406
144k
        return 0; /* Continue */
407
144k
      } else {
408
9.85k
        offset = flow->metadata.openvpn.ovpn_heur_opcode__missing_bytes[dir];
409
9.85k
        flow->metadata.openvpn.ovpn_heur_opcode__missing_bytes[dir] = 0;
410
9.85k
      }
411
2.75M
    } else {
412
2.75M
      offset = 0;
413
2.75M
    }
414
415
#ifdef NDPI_ENABLE_DEBUG_MESSAGES
416
    iter = 0;
417
#endif
418
2.76M
    rc = 1; /* Exclude */
419
2.80M
    while(offset + 2 + 1 /* The first byte is the opcode */ <= ovpn_payload_len) {
420
2.34M
      pdu_len = ntohs((*(u_int16_t *)(ovpn_payload + offset)));
421
2.34M
      NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: TCP, iter %d offset %d pdu_length %d\n",
422
2.34M
                      iter, offset, pdu_len);
423
2.34M
      if(pdu_len < 14)
424
334k
        return 1; /* Exclude */
425
2.00M
      if(pdu_len > 4 * 1500) { /* 4 full size packets: simple threshold to avoid false positives */
426
1.65M
        NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: pdu_len %d too big. Exclude\n", pdu_len);
427
1.65M
        return 1; /* Exclude */
428
1.65M
      }
429
350k
      rc = search_heur_opcode_common(ndpi_struct, flow, *(ovpn_payload + offset + 2));
430
350k
      NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: TCP, rc %d\n", rc);
431
350k
      if(rc > 0) /* Exclude || Found --> stop */
432
5.67k
        return rc;
433
434
345k
      if(offset + 2 + pdu_len <= ovpn_payload_len) {
435
35.2k
        offset += 2 + pdu_len;
436
309k
      } else {
437
309k
        flow->metadata.openvpn.ovpn_heur_opcode__missing_bytes[dir] = pdu_len - (ovpn_payload_len - (offset + 2));
438
309k
        NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: TCP, missing %d bytes\n",
439
309k
                      flow->metadata.openvpn.ovpn_heur_opcode__missing_bytes[dir]);
440
309k
        return 0; /* Continue */
441
309k
      }
442
#ifdef NDPI_ENABLE_DEBUG_MESSAGES
443
      iter++;
444
#endif
445
345k
    }
446
462k
    return rc;
447
2.76M
  } else {
448
1.30M
    if(ovpn_payload_len < 14)
449
158k
      return 1; /* Exclude */
450
1.15M
    return search_heur_opcode_common(ndpi_struct, flow, ovpn_payload[0]);
451
1.30M
  }
452
4.22M
}
453
454
455
static void ndpi_search_openvpn(struct ndpi_detection_module_struct* ndpi_struct,
456
4.93M
                                struct ndpi_flow_struct* flow) {
457
4.93M
  struct ndpi_packet_struct* packet = &ndpi_struct->packet;
458
459
4.93M
  NDPI_LOG_DBG(ndpi_struct, "Search opnvpn\n");
460
461
4.93M
  if(packet->payload_packet_len > 10 &&
462
4.93M
     ntohl(*(u_int32_t *)&packet->payload[4 + 2 * (packet->tcp != NULL)]) == 0x2112A442) {
463
9.28k
    NDPI_LOG_DBG2(ndpi_struct, "Avoid collision with STUN\n");
464
9.28k
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
465
9.28k
    return;
466
9.28k
  }
467
468
4.92M
  NDPI_LOG_DBG2(ndpi_struct, "States (before): %d %d\n",
469
4.92M
                flow->metadata.openvpn.ovpn_alg_standard_state,
470
4.92M
                flow->metadata.openvpn.ovpn_alg_heur_opcode_state);
471
472
4.92M
  if(flow->metadata.openvpn.ovpn_alg_standard_state == 0) {
473
4.33M
    flow->metadata.openvpn.ovpn_alg_standard_state = search_standard(ndpi_struct, flow);
474
4.33M
  }
475
4.92M
  if(ndpi_struct->cfg.openvpn_heuristics & NDPI_HEURISTICS_OPENVPN_OPCODE) {
476
4.37M
    if(flow->metadata.openvpn.ovpn_alg_heur_opcode_state == 0) {
477
4.36M
      flow->metadata.openvpn.ovpn_alg_heur_opcode_state = search_heur_opcode(ndpi_struct, flow);
478
4.36M
    }
479
4.37M
  } else {
480
553k
    flow->metadata.openvpn.ovpn_alg_heur_opcode_state = 1;
481
553k
  }
482
483
4.92M
  NDPI_LOG_DBG2(ndpi_struct, "States (after): %d %d\n",
484
4.92M
                flow->metadata.openvpn.ovpn_alg_standard_state,
485
4.92M
                flow->metadata.openvpn.ovpn_alg_heur_opcode_state);
486
487
4.92M
  if(flow->metadata.openvpn.ovpn_alg_standard_state == 2) {
488
2.38k
    ndpi_int_openvpn_add_connection(ndpi_struct, flow, NDPI_CONFIDENCE_DPI);
489
4.92M
  } else if (flow->metadata.openvpn.ovpn_alg_heur_opcode_state == 2) {
490
119
    ndpi_int_openvpn_add_connection(ndpi_struct, flow, NDPI_CONFIDENCE_DPI_AGGRESSIVE);
491
119
    ndpi_set_risk(ndpi_struct, &flow->core, NDPI_OBFUSCATED_TRAFFIC, "Obfuscated OpenVPN");
492
4.92M
  } else if(flow->metadata.openvpn.ovpn_alg_standard_state == 1 &&
493
4.85M
            flow->metadata.openvpn.ovpn_alg_heur_opcode_state == 1) {
494
3.37M
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
495
3.37M
  }
496
497
4.92M
}
498
499
16.5k
void init_openvpn_dissector(struct ndpi_detection_module_struct *ndpi_struct) {
500
16.5k
  ndpi_register_dissector("OpenVPN", ndpi_struct,
501
16.5k
                     ndpi_search_openvpn,
502
16.5k
                     NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION,
503
16.5k
                     DISSECTOR_LICENSE_LGPL,
504
16.5k
                     1, NDPI_PROTOCOL_OPENVPN);
505
16.5k
}