Coverage Report

Created: 2025-09-17 07:06

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-25 - 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
3.91M
#define P_CONTROL_HARD_RESET_CLIENT_V1  (0x01 << 3)
46
3.86M
#define P_CONTROL_HARD_RESET_SERVER_V1  (0x02 << 3)
47
3.70M
#define P_CONTROL_V1                    (0x04 << 3)
48
3.67M
#define P_ACK_V1                        (0x05 << 3)
49
3.75M
#define P_CONTROL_HARD_RESET_CLIENT_V2  (0x07 << 3)
50
3.71M
#define P_CONTROL_HARD_RESET_SERVER_V2  (0x08 << 3)
51
3.55M
#define P_CONTROL_HARD_RESET_CLIENT_V3  (0x0A << 3)
52
1.58M
#define P_CONTROL_WKC_V1                (0x0B << 3)
53
54
3.21M
#define P_OPCODE_MASK 0xF8
55
#define P_SHA1_HMAC_SIZE 20
56
3.00k
#define P_HMAC_128 16                            // (RSA-)MD5, (RSA-)MD4, ..others
57
3.38k
#define P_HMAC_160 20                            // (RSA-|DSA-)SHA(1), ..others, SHA1 is openvpn default
58
863
#define P_HMAC_NONE 0                            // No HMAC
59
9.66k
#define P_HARD_RESET_PACKET_ID_OFFSET(hmac_size) (9 + hmac_size)
60
2.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
1.45k
{
67
1.45k
  if(ndpi_struct->cfg.openvpn_subclassification_by_ip &&
68
1.45k
     ndpi_struct->proto_defaults[flow->guessed_protocol_id_by_ip].protoCategory == NDPI_PROTOCOL_CATEGORY_VPN) {
69
96
    ndpi_set_detected_protocol(ndpi_struct, flow, flow->guessed_protocol_id_by_ip, NDPI_PROTOCOL_OPENVPN, confidence);
70
1.36k
  } else {
71
1.36k
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OPENVPN, NDPI_PROTOCOL_UNKNOWN, confidence);
72
1.36k
  }
73
1.45k
}
74
75
static int is_opcode_valid(u_int8_t opcode)
76
1.89M
{
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
1.89M
  return opcode == P_CONTROL_HARD_RESET_CLIENT_V1 ||
82
1.84M
   opcode == P_CONTROL_HARD_RESET_SERVER_V1 ||
83
1.81M
   opcode == P_CONTROL_V1 ||
84
1.78M
   opcode == P_ACK_V1 ||
85
1.74M
   opcode == P_CONTROL_HARD_RESET_CLIENT_V2 ||
86
1.70M
   opcode == P_CONTROL_HARD_RESET_SERVER_V2 ||
87
1.64M
   opcode == P_CONTROL_HARD_RESET_CLIENT_V3 ||
88
1.58M
   opcode == P_CONTROL_WKC_V1;
89
1.89M
}
90
91
6.00k
static u_int32_t get_packet_id(const u_int8_t * payload, u_int8_t hms) {
92
6.00k
  return(ntohl(*(u_int32_t*)(payload + P_HARD_RESET_PACKET_ID_OFFSET(hms))));
93
6.00k
}
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
2.82k
{
102
2.82k
  int c = 0;
103
104
2.82k
  if((hmac & 0x000000FF) == 0x00000000)
105
438
    c++;
106
2.82k
  if((hmac & 0x0000FF00) == 0x00000000)
107
423
    c++;
108
2.82k
  if ((hmac & 0x00FF0000) == 0x00000000)
109
404
    c++;
110
2.82k
  if ((hmac & 0xFF000000) == 0x00000000)
111
0
    c++;
112
2.82k
  if (c > 1)
113
314
    return 0;
114
2.51k
  return 1;
115
2.82k
}
116
117
3.76k
static int8_t detect_hmac_size(const u_int8_t *payload, int payload_len) {
118
  // try to guess
119
3.76k
  if((payload_len >= P_HARD_RESET_PACKET_ID_OFFSET(P_HMAC_160) + 4) &&
120
3.09k
     get_packet_id(payload, P_HMAC_160) == 1)
121
284
    return P_HMAC_160;
122
  
123
3.47k
  if((payload_len >= P_HARD_RESET_PACKET_ID_OFFSET(P_HMAC_128) + 4) &&
124
2.90k
     get_packet_id(payload, P_HMAC_128) == 1)
125
102
    return P_HMAC_128;
126
127
  /* Heuristic from Wireshark, to detect no-HMAC flows (i.e. tls-crypt) */
128
3.37k
  if(payload_len >= 14 &&
129
3.37k
     !(payload[9] > 0 &&
130
2.82k
       check_for_valid_hmac(ntohl(*(u_int32_t*)(payload + 9)))))
131
863
    return P_HMAC_NONE;
132
133
2.51k
  return(-1);
134
3.37k
}
135
136
static int search_standard(struct ndpi_detection_module_struct* ndpi_struct,
137
2.77M
                           struct ndpi_flow_struct* flow) {
138
2.77M
  struct ndpi_packet_struct* packet = &ndpi_struct->packet;
139
2.77M
  const u_int8_t * ovpn_payload = packet->payload;
140
2.77M
  const u_int8_t * session_remote;
141
2.77M
  u_int8_t opcode;
142
2.77M
  u_int8_t alen;
143
2.77M
  int8_t hmac_size;
144
2.77M
  int8_t failed = 0;
145
2.77M
  /* No u_ */int16_t ovpn_payload_len = packet->payload_packet_len;
146
2.77M
  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
2.77M
  if(ovpn_payload_len < 14 + 2 * (packet->tcp != NULL)) {
155
887k
    return 1; /* Exclude */
156
887k
  }
157
158
  /* Skip openvpn TCP transport packet size */
159
1.89M
  if(packet->tcp != NULL)
160
1.37M
    ovpn_payload += 2, ovpn_payload_len -= 2;
161
162
1.89M
  opcode = ovpn_payload[0] & P_OPCODE_MASK;
163
1.89M
  if(!is_opcode_valid(opcode)) {
164
1.55M
    return 1; /* Exclude */
165
1.55M
  }
166
  /* Maybe a strong assumption... */
167
340k
  if((ovpn_payload[0] & ~P_OPCODE_MASK) != 0) {
168
264k
    NDPI_LOG_DBG2(ndpi_struct, "Invalid key id\n");
169
264k
    return 1; /* Exclude */
170
264k
  }
171
76.0k
  if(flow->packet_direction_counter[dir] == 1 &&
172
64.7k
     !(opcode == P_CONTROL_HARD_RESET_CLIENT_V1 ||
173
52.6k
       opcode == P_CONTROL_HARD_RESET_CLIENT_V2 ||
174
46.3k
       opcode == P_CONTROL_HARD_RESET_SERVER_V1 ||
175
39.3k
       opcode == P_CONTROL_HARD_RESET_SERVER_V2 ||
176
21.8k
       opcode == P_CONTROL_HARD_RESET_CLIENT_V3)) {
177
16.6k
    NDPI_LOG_DBG2(ndpi_struct, "Invalid first packet\n");
178
16.6k
    return 1; /* Exclude */
179
16.6k
  }
180
  /* Resets are small packets */
181
59.4k
  if(packet->payload_packet_len >= 1200 &&
182
3.94k
     (opcode == P_CONTROL_HARD_RESET_CLIENT_V1 ||
183
3.20k
      opcode == P_CONTROL_HARD_RESET_CLIENT_V2 ||
184
2.82k
      opcode == P_CONTROL_HARD_RESET_SERVER_V1 ||
185
2.33k
      opcode == P_CONTROL_HARD_RESET_SERVER_V2 ||
186
3.57k
      opcode == P_CONTROL_HARD_RESET_CLIENT_V3)) {
187
3.57k
    NDPI_LOG_DBG2(ndpi_struct, "Invalid len first pkt (QUIC collision)\n");
188
3.57k
    return 1; /* Exclude */
189
3.57k
  }
190
55.8k
  if(flow->packet_direction_counter[dir] == 1 &&
191
44.6k
     packet->tcp &&
192
55.8k
     ntohs(*(u_int16_t *)(packet->payload)) != ovpn_payload_len) {
193
18.5k
    NDPI_LOG_DBG2(ndpi_struct, "Invalid tcp len on reset\n");
194
18.5k
    return 1; /* Exclude */
195
18.5k
  }
196
197
37.2k
  NDPI_LOG_DBG2(ndpi_struct, "[packets %d/%d][opcode: %u][len: %u]\n",
198
37.2k
                flow->packet_direction_counter[dir],
199
37.2k
                flow->packet_direction_counter[!dir],
200
37.2k
                opcode, ovpn_payload_len);
201
202
37.2k
  if(flow->packet_direction_counter[dir] > 1) {
203
11.1k
    if(memcmp(flow->ovpn_session_id[dir], ovpn_payload + 1, 8) != 0) {
204
2.17k
      NDPI_LOG_DBG2(ndpi_struct, "Invalid session id on two consecutive pkts in the same dir\n");
205
2.17k
      return 1; /* Exclude */
206
2.17k
    }
207
8.98k
    if(flow->packet_direction_counter[dir] >= 2 &&
208
8.98k
       flow->packet_direction_counter[!dir] >= 2) {
209
      /* (2) */
210
446
      NDPI_LOG_INFO(ndpi_struct,"found openvpn (session ids match on both direction)\n");
211
446
      return 2; /* Found */
212
446
    }
213
8.54k
    if(flow->packet_direction_counter[dir] >= 4 &&
214
1.09k
       flow->packet_direction_counter[!dir] == 0) {
215
      /* (3) */
216
611
      NDPI_LOG_INFO(ndpi_struct,"found openvpn (asymmetric)\n");
217
611
      return 2; /* Found */
218
611
    }
219
26.1k
  } else {
220
26.1k
    memcpy(flow->ovpn_session_id[dir], ovpn_payload + 1, 8);
221
26.1k
    NDPI_LOG_DBG2(ndpi_struct, "Session key [%d]: 0x%lx\n", dir,
222
26.1k
                  ndpi_ntohll(*(u_int64_t *)flow->ovpn_session_id[dir]));
223
26.1k
  }
224
225
  /* (1) */
226
34.0k
  if(flow->packet_direction_counter[!dir] > 0 &&
227
6.03k
     (opcode == P_CONTROL_HARD_RESET_SERVER_V1 ||
228
4.97k
      opcode == P_CONTROL_HARD_RESET_SERVER_V2)) {
229
230
3.76k
    hmac_size = detect_hmac_size(ovpn_payload, ovpn_payload_len);
231
3.76k
    NDPI_LOG_DBG2(ndpi_struct, "hmac size %d\n", hmac_size);
232
3.76k
    failed = 0;
233
3.76k
    if(hmac_size >= 0 &&
234
1.24k
       P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size) < ovpn_payload_len) {
235
1.16k
      u_int16_t offset = P_PACKET_ID_ARRAY_LEN_OFFSET(hmac_size);
236
237
1.16k
      alen = ovpn_payload[offset];
238
239
1.16k
      if(alen > 0) {
240
534
        offset += 1 + alen * 4;
241
242
534
        if((offset + 8) <= ovpn_payload_len) {
243
419
          session_remote = &ovpn_payload[offset];
244
245
419
          if(memcmp(flow->ovpn_session_id[!dir], session_remote, 8) == 0) {
246
253
            NDPI_LOG_INFO(ndpi_struct,"found openvpn\n");
247
253
            return 2; /* Found */
248
253
          } else {
249
166
            NDPI_LOG_DBG2(ndpi_struct, "key mismatch 0x%lx\n", ndpi_ntohll(*(u_int64_t *)session_remote));
250
166
          }
251
419
        }
252
281
        failed = 1;
253
635
      } else {
254
        /* Server reset without remote session id field; no failure */
255
635
      }
256
1.16k
    }
257
3.76k
  }
258
259
33.8k
  if(failed || flow->packet_counter > 5)
260
422
    return 1; /* Exclude */
261
33.3k
  return 0; /* Continue */
262
33.8k
}
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
983k
                                     u_int8_t first_byte) {
277
983k
  u_int8_t opcode, found  = 0, i;
278
983k
  int dir = ndpi_struct->packet.packet_direction;
279
280
983k
  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
983k
  NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: [packets %d/%d msgs %d, dir %d][first byte 0x%x][opcode: 0x%x]\n",
292
983k
                flow->packet_direction_counter[0],
293
983k
                flow->packet_direction_counter[1],
294
983k
                flow->ovpn_heur_opcode__num_msgs,
295
983k
                dir, first_byte, opcode);
296
297
983k
  flow->ovpn_heur_opcode__num_msgs++;
298
299
983k
  if(flow->packet_direction_counter[dir] == 1) {
300
745k
    flow->ovpn_heur_opcode__resets[dir] = opcode;
301
745k
    if(flow->packet_direction_counter[!dir] > 0 &&
302
66.6k
       opcode == flow->ovpn_heur_opcode__resets[!dir]) {
303
54.9k
      NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: same resets\n");
304
54.9k
      return 1; /* Exclude */
305
54.9k
    }
306
690k
    return 0; /* Continue */
307
745k
  }
308
309
237k
  if(opcode == flow->ovpn_heur_opcode__resets[dir]) {
310
214k
    if(flow->ovpn_heur_opcode__codes_num > 0) {
311
405
      NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: resets after other opcodes\n");
312
405
      return 1; /* Exclude */
313
405
    }
314
213k
    return 0; /* Continue */
315
214k
  }
316
23.8k
  if(flow->packet_direction_counter[!dir] > 0 &&
317
12.4k
     opcode == flow->ovpn_heur_opcode__resets[!dir]) {
318
2.99k
    NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: same resets\n");
319
2.99k
    return 1; /* Exclude */
320
2.99k
  }
321
322
20.8k
  if(flow->packet_direction_counter[!dir] == 0) {
323
11.4k
    NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: opcode different than reset but not reset in the other direction\n");
324
11.4k
    return 1; /* Exclude */
325
11.4k
  }
326
327
9.41k
  if(flow->ovpn_heur_opcode__codes_num == OPENVPN_HEUR_MAX_NUM_OPCODES &&
328
475
     opcode != flow->ovpn_heur_opcode__codes[OPENVPN_HEUR_MAX_NUM_OPCODES - 1]) {
329
186
    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
186
    return 1; /* Exclude */
333
186
  }
334
335
21.9k
  for(i = 0; i < flow->ovpn_heur_opcode__codes_num; i++) {
336
12.6k
    if(flow->ovpn_heur_opcode__codes[i] == opcode)
337
5.29k
      found = 1;
338
12.6k
  }
339
9.23k
  if(found == 0) {
340
3.93k
    if(flow->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
3.93k
    flow->ovpn_heur_opcode__codes[flow->ovpn_heur_opcode__codes_num++] = opcode;
345
3.93k
  }
346
347
9.23k
  NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: Resets 0x%x,0x%x Num %d\n",
348
9.23k
                flow->ovpn_heur_opcode__resets[0],
349
9.23k
                flow->ovpn_heur_opcode__resets[1],
350
9.23k
                flow->ovpn_heur_opcode__codes_num);
351
352
9.23k
  if(flow->ovpn_heur_opcode__num_msgs < ndpi_struct->cfg.openvpn_heuristics_num_msgs)
353
9.06k
    return 0; /* Continue */
354
355
  /* Done. Check what we have found...*/
356
357
168
  if(flow->packet_direction_counter[0] == 0 ||
358
168
     flow->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
168
  if(flow->ovpn_heur_opcode__codes_num >= 2) {
364
148
    NDPI_LOG_INFO(ndpi_struct,"found openvpn (Heur-opcode)\n");
365
148
    return 2; /* Found */
366
148
  }
367
20
  NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: excluded\n");
368
20
  return 1; /* Exclude */
369
168
}
370
371
static int search_heur_opcode(struct ndpi_detection_module_struct* ndpi_struct,
372
3.13M
                              struct ndpi_flow_struct* flow) {
373
3.13M
  struct ndpi_packet_struct* packet = &ndpi_struct->packet;
374
3.13M
  const u_int8_t *ovpn_payload = packet->payload;
375
3.13M
  u_int16_t ovpn_payload_len = packet->payload_packet_len;
376
3.13M
  int dir = packet->packet_direction;
377
3.13M
  u_int16_t pdu_len;
378
3.13M
  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
3.13M
  if(is_flow_addr_informative(flow)) {
386
120k
    NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: flow to informative address. Exclude\n");
387
120k
    return 1; /* Exclude */
388
120k
  }
389
390
3.01M
  if(packet->tcp != NULL) {
391
    /* Two bytes field with pdu length */
392
393
2.21M
    NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: TCP length %d (remaining %d)\n",
394
2.21M
                  ovpn_payload_len,
395
2.21M
                  flow->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.21M
    if(flow->ovpn_heur_opcode__missing_bytes[dir] > 0) {
402
114k
      NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: TCP, remaining bytes to ignore %d length %d\n",
403
114k
                    flow->ovpn_heur_opcode__missing_bytes[dir], ovpn_payload_len);
404
114k
      if(flow->ovpn_heur_opcode__missing_bytes[dir] >= ovpn_payload_len) {
405
106k
        flow->ovpn_heur_opcode__missing_bytes[dir] -= ovpn_payload_len;
406
106k
        return 0; /* Continue */
407
106k
      } else {
408
8.68k
        offset = flow->ovpn_heur_opcode__missing_bytes[dir];
409
8.68k
        flow->ovpn_heur_opcode__missing_bytes[dir] = 0;
410
8.68k
      }
411
2.10M
    } else {
412
2.10M
      offset = 0;
413
2.10M
    }
414
415
#ifdef NDPI_ENABLE_DEBUG_MESSAGES
416
    iter = 0;
417
#endif
418
2.11M
    rc = 1; /* Exclude */
419
2.14M
    while(offset + 2 + 1 /* The first byte is the opcode */ <= ovpn_payload_len) {
420
1.79M
      pdu_len = ntohs((*(u_int16_t *)(ovpn_payload + offset)));
421
1.79M
      NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: TCP, iter %d offset %d pdu_length %d\n",
422
1.79M
                      iter, offset, pdu_len);
423
1.79M
      if(pdu_len < 14)
424
259k
        return 1; /* Exclude */
425
1.53M
      if(pdu_len > 4 * 1500) { /* 4 full size packets: simple threshold to avoid false positives */
426
1.27M
        NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: pdu_len %d too big. Exclude\n", pdu_len);
427
1.27M
        return 1; /* Exclude */
428
1.27M
      }
429
260k
      rc = search_heur_opcode_common(ndpi_struct, flow, *(ovpn_payload + offset + 2));
430
260k
      NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: TCP, rc %d\n", rc);
431
260k
      if(rc > 0) /* Exclude || Found --> stop */
432
4.93k
        return rc;
433
434
255k
      if(offset + 2 + pdu_len <= ovpn_payload_len) {
435
30.6k
        offset += 2 + pdu_len;
436
224k
      } else {
437
224k
        flow->ovpn_heur_opcode__missing_bytes[dir] = pdu_len - (ovpn_payload_len - (offset + 2));
438
224k
        NDPI_LOG_DBG2(ndpi_struct, "Heur-opcode: TCP, missing %d bytes\n",
439
224k
                      flow->ovpn_heur_opcode__missing_bytes[dir]);
440
224k
        return 0; /* Continue */
441
224k
      }
442
#ifdef NDPI_ENABLE_DEBUG_MESSAGES
443
      iter++;
444
#endif
445
255k
    }
446
352k
    return rc;
447
2.11M
  } else {
448
798k
    if(ovpn_payload_len < 14)
449
74.8k
      return 1; /* Exclude */
450
723k
    return search_heur_opcode_common(ndpi_struct, flow, ovpn_payload[0]);
451
798k
  }
452
3.01M
}
453
454
455
static void ndpi_search_openvpn(struct ndpi_detection_module_struct* ndpi_struct,
456
3.21M
                                struct ndpi_flow_struct* flow) {
457
3.21M
  struct ndpi_packet_struct* packet = &ndpi_struct->packet;
458
459
3.21M
  NDPI_LOG_DBG(ndpi_struct, "Search opnvpn\n");
460
461
3.21M
  if(packet->payload_packet_len > 10 &&
462
3.21M
     ntohl(*(u_int32_t *)&packet->payload[4 + 2 * (packet->tcp != NULL)]) == 0x2112A442) {
463
11.0k
    NDPI_LOG_DBG2(ndpi_struct, "Avoid collision with STUN\n");
464
11.0k
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
465
11.0k
    return;
466
11.0k
  }
467
468
3.20M
  NDPI_LOG_DBG2(ndpi_struct, "States (before): %d %d\n",
469
3.20M
                flow->ovpn_alg_standard_state,
470
3.20M
                flow->ovpn_alg_heur_opcode_state);
471
472
3.20M
  if(flow->ovpn_alg_standard_state == 0) {
473
2.77M
    flow->ovpn_alg_standard_state = search_standard(ndpi_struct, flow);
474
2.77M
  }
475
3.20M
  if(ndpi_struct->cfg.openvpn_heuristics & NDPI_HEURISTICS_OPENVPN_OPCODE) {
476
3.14M
    if(flow->ovpn_alg_heur_opcode_state == 0) {
477
3.13M
      flow->ovpn_alg_heur_opcode_state = search_heur_opcode(ndpi_struct, flow);
478
3.13M
    }
479
3.14M
  } else {
480
54.8k
    flow->ovpn_alg_heur_opcode_state = 1;
481
54.8k
  }
482
483
3.20M
  NDPI_LOG_DBG2(ndpi_struct, "States (after): %d %d\n",
484
3.20M
                flow->ovpn_alg_standard_state,
485
3.20M
                flow->ovpn_alg_heur_opcode_state);
486
487
3.20M
  if(flow->ovpn_alg_standard_state == 2) {
488
1.31k
    ndpi_int_openvpn_add_connection(ndpi_struct, flow, NDPI_CONFIDENCE_DPI);
489
3.19M
  } else if (flow->ovpn_alg_heur_opcode_state == 2) {
490
148
    ndpi_int_openvpn_add_connection(ndpi_struct, flow, NDPI_CONFIDENCE_DPI_AGGRESSIVE);
491
148
    ndpi_set_risk(ndpi_struct, flow, NDPI_OBFUSCATED_TRAFFIC, "Obfuscated OpenVPN");
492
3.19M
  } else if(flow->ovpn_alg_standard_state == 1 &&
493
3.16M
            flow->ovpn_alg_heur_opcode_state == 1) {
494
2.18M
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
495
2.18M
  }
496
497
3.20M
}
498
499
10.7k
void init_openvpn_dissector(struct ndpi_detection_module_struct *ndpi_struct) {
500
10.7k
  register_dissector("OpenVPN", ndpi_struct,
501
10.7k
                     ndpi_search_openvpn,
502
10.7k
                     NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION,
503
10.7k
                     1, NDPI_PROTOCOL_OPENVPN);
504
10.7k
}