Coverage Report

Created: 2026-02-21 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ndpi/src/lib/protocols/rtp.c
Line
Count
Source
1
/*
2
 * rtp.c
3
 *
4
 * Copyright (C) 2009-11 - ipoque GmbH
5
 * Copyright (C) 2011-26 - ntop.org
6
 *
7
 * This file is part of nDPI, an open source deep packet inspection
8
 * library based on the OpenDPI and PACE technology by ipoque GmbH
9
 *
10
 * nDPI is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Lesser General Public License as published by
12
 * the Free Software Foundation, either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * nDPI is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU Lesser General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public License
21
 * along with nDPI.  If not, see <http://www.gnu.org/licenses/>.
22
 *
23
 */
24
25
#include "ndpi_protocol_ids.h"
26
27
#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_RTP
28
29
#include "ndpi_api.h"
30
#include "ndpi_private.h"
31
32
0
#define RTP_MIN_HEADER  12
33
0
#define RTCP_MIN_HEADER 8
34
35
static void ndpi_rtp_search(struct ndpi_detection_module_struct *ndpi_struct,
36
          struct ndpi_flow_struct *flow);
37
38
/* *************************************************************** */
39
40
/* https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
41
int is_valid_rtp_payload_type(uint8_t type)
42
0
{
43
0
  if(!(type <= 34 || (type >= 96 && type <= 127)))
44
0
    return 0;
45
0
  return 1;
46
0
}
47
48
/* *************************************************************** */
49
50
u_int8_t rtp_get_stream_type(u_int8_t payloadType, u_int8_t *s_type, u_int16_t sub_proto)
51
0
{
52
  /* General, from IANA */
53
0
  switch(payloadType) {
54
0
  case 0: /* G.711 u-Law */
55
0
  case 3: /* GSM 6.10 */
56
0
  case 4: /* G.723.1  */
57
0
  case 5: /* DVI4 */
58
0
  case 6: /* DVI4 */
59
0
  case 7: /* LPC */
60
0
  case 8: /* G.711 A-Law */
61
0
  case 9: /* G.722 */
62
0
  case 10: /* L16 */
63
0
  case 11: /* L16 */
64
0
  case 12: /* QCELP */
65
0
  case 13: /* Comfort Noise */
66
0
  case 14: /* MPA */
67
0
  case 15: /* G728 */
68
0
  case 16: /* DVI4 */
69
0
  case 17: /* DVI4 */
70
0
  case 18: /* G729 */
71
0
    *s_type |= ndpi_multimedia_audio_flow;
72
0
    return(1);
73
74
0
  case 25: /* CelB */
75
0
  case 26: /* JPEG */
76
0
  case 28: /* nv */
77
0
  case 31: /* H261 */
78
0
  case 32: /* MPV */
79
0
  case 34: /* H263 */
80
0
    *s_type |= ndpi_multimedia_video_flow;
81
0
    return(1);
82
0
  }
83
84
  /* Microsoft; from https://learn.microsoft.com/en-us/openspecs/office_protocols/ms-rtp/3b8dc3c6-34b8-4827-9b38-3b00154f471c */
85
0
  if(sub_proto == NDPI_PROTOCOL_MSTEAMS_CALL) {
86
0
    switch(payloadType) {
87
0
    case 103: /* SILK Narrowband */
88
0
    case 104: /* SILK Wideband */
89
0
    case 106: /* OPUS */
90
0
    case 111: /* Siren */
91
0
    case 112: /* G.722.1 */
92
0
    case 114: /* RT Audio Wideband */
93
0
    case 115: /* RT Audio Narrowband */
94
0
    case 116: /* G.726 */
95
0
    case 117: /* G.722 */
96
0
    case 118: /* Comfort Noise Wideband */
97
0
      *s_type |= ndpi_multimedia_audio_flow;
98
0
      return(1);
99
100
0
    case 34: /* H.263 [MS-H26XPF] */
101
0
    case 121: /* RT Video */
102
0
    case 122: /* H.264 [MS-H264PF] */
103
0
    case 123: /* H.264 FEC [MS-H264PF] */
104
0
      *s_type |= ndpi_multimedia_video_flow;
105
0
      return(1);
106
107
0
    default:
108
0
      *s_type |= ndpi_multimedia_unknown_flow;
109
0
      return(0);
110
0
    }
111
0
  }
112
113
  /* Dynamic PTs are... dynamic... :D
114
   * Looking at some traces, it seems specific applications keep using
115
   * always the same PT for audio/video...
116
   * TODO: something better?
117
   * Bottom line: checking only PT is very fast/easy, but we might have
118
   * false positives/negatives
119
   */
120
121
0
  if(sub_proto == NDPI_PROTOCOL_GOOGLE_CALL) {
122
0
    switch(payloadType) {
123
0
    case 111:
124
0
      *s_type |= ndpi_multimedia_audio_flow;
125
0
      return(1);
126
127
0
    case 96:
128
0
    case 100:
129
0
      *s_type |= ndpi_multimedia_video_flow;
130
0
      return(1);
131
132
0
    default:
133
0
      *s_type |= ndpi_multimedia_unknown_flow;
134
0
      return(0);
135
0
    }
136
0
  }
137
138
0
  if(sub_proto == NDPI_PROTOCOL_WHATSAPP_CALL) {
139
0
    switch(payloadType) {
140
0
    case 120:
141
0
      *s_type |= ndpi_multimedia_audio_flow;
142
0
      return(1);
143
144
0
    case 97:
145
0
    case 102:
146
0
      *s_type |= ndpi_multimedia_video_flow;
147
0
      return(1);
148
149
0
    default:
150
0
      *s_type |= ndpi_multimedia_unknown_flow;
151
0
      return(0);
152
0
    }
153
0
  }
154
155
0
  if(sub_proto == NDPI_PROTOCOL_FACEBOOK_VOIP) {
156
0
    switch(payloadType) {
157
0
    case 96:
158
0
    case 97:
159
0
    case 101:
160
0
    case 109:
161
0
      *s_type |= ndpi_multimedia_audio_flow;
162
0
      return(1);
163
164
0
    case 127:
165
0
      *s_type |= ndpi_multimedia_video_flow;
166
0
      return(1);
167
168
0
    default:
169
0
      *s_type |= ndpi_multimedia_unknown_flow;
170
0
      return(0);
171
0
    }
172
0
  }
173
174
0
  if(sub_proto == NDPI_PROTOCOL_TELEGRAM_VOIP) {
175
0
    switch(payloadType) {
176
0
    case 111:
177
0
      *s_type |= ndpi_multimedia_audio_flow;
178
0
      return(1);
179
180
0
    case 106:
181
0
      *s_type |= ndpi_multimedia_video_flow;
182
0
      return(1);
183
184
0
    default:
185
0
      *s_type |= ndpi_multimedia_unknown_flow;
186
0
      return(0);
187
0
    }
188
0
  }
189
190
0
  if(sub_proto == NDPI_PROTOCOL_SIGNAL_VOIP) {
191
0
    switch(payloadType) {
192
0
    case 102:
193
0
      *s_type |= ndpi_multimedia_audio_flow;
194
0
      return(1);
195
196
0
    case 108:
197
0
    case 120:
198
0
      *s_type |= ndpi_multimedia_video_flow;
199
0
      return(1);
200
201
0
    default:
202
0
      *s_type |= ndpi_multimedia_unknown_flow;
203
0
      return(0);
204
0
    }
205
0
  }
206
207
0
  *s_type |= ndpi_multimedia_unknown_flow;
208
0
  return(0);
209
0
}
210
211
/* *************************************************************** */
212
213
0
static int is_valid_rtcp_payload_type(uint8_t type) {
214
0
  return (type >= 192 && type <= 213);
215
0
}
216
217
/* *************************************************************** */
218
219
int is_rtp_or_rtcp(struct ndpi_detection_module_struct *ndpi_struct,
220
                   const u_int8_t *payload, u_int16_t payload_len, u_int16_t *seq)
221
0
{
222
0
  u_int8_t csrc_count, ext_header;
223
0
  u_int16_t ext_len;
224
0
  u_int32_t min_len;
225
226
0
  if(payload_len < 2)
227
0
    return NO_RTP_RTCP;
228
229
0
  if((payload[0] & 0xC0) != 0x80) { /* Version 2 */
230
0
    NDPI_LOG_DBG(ndpi_struct, "Not version 2\n");
231
0
    return NO_RTP_RTCP;
232
0
  }
233
234
0
  if(is_valid_rtp_payload_type(payload[1] & 0x7F) &&
235
0
     payload_len >= RTP_MIN_HEADER) {
236
    /* RTP */
237
0
    csrc_count = payload[0] & 0x0F;
238
0
    ext_header =  !!(payload[0] & 0x10);
239
0
    min_len = RTP_MIN_HEADER + 4 * csrc_count + 4 * ext_header;
240
0
    if(ext_header) {
241
0
      if(min_len > payload_len) {
242
0
        NDPI_LOG_DBG(ndpi_struct, "Too short (a) %d vs %d\n", min_len, payload_len);
243
0
        return NO_RTP_RTCP;
244
0
      }
245
0
      ext_len = ntohs(*(unsigned short *)&payload[min_len - 2]);
246
0
      min_len += ext_len * 4;
247
0
    }
248
0
    if(min_len > payload_len) {
249
0
      NDPI_LOG_DBG(ndpi_struct, "Too short (b) %d vs %d\n", min_len, payload_len);
250
0
      return NO_RTP_RTCP;
251
0
    }
252
    /* Check on padding doesn't work because:
253
     * we may have multiple RTP packets in the same TCP/UDP datagram
254
     * with SRTP, padding_length field is encrypted */
255
0
    if(seq)
256
0
      *seq = ntohs(*(unsigned short *)&payload[2]);
257
0
    return IS_RTP;
258
0
  } else if(is_valid_rtcp_payload_type(payload[1]) &&
259
0
            payload_len >= RTCP_MIN_HEADER) {
260
0
    min_len = (ntohs(*(unsigned short *)&payload[2]) + 1) * 4;
261
0
    if(min_len > payload_len) {
262
0
      NDPI_LOG_DBG(ndpi_struct, "Too short (c) %d vs %d\n", min_len, payload_len);
263
0
      return NO_RTP_RTCP;
264
0
    }
265
0
    return IS_RTCP;
266
0
  }
267
0
  NDPI_LOG_DBG(ndpi_struct, "not RTP/RTCP\n");
268
0
  return NO_RTP_RTCP;
269
0
}
270
271
/* ************************************************************ */
272
273
static int get_rtp_info(struct ndpi_detection_module_struct *ndpi_struct,
274
                        struct ndpi_flow_struct *flow,
275
                        const u_int8_t *payload,
276
0
                        u_int16_t payload_len) {
277
0
  u_int8_t packet_direction = current_pkt_from_client_to_server(ndpi_struct, flow) ? 0 : 1;
278
279
0
  if(flow->rtp[packet_direction].payload_detected == false) {
280
0
    flow->rtp[packet_direction].payload_type = payload[1] & 0x7F;
281
0
    flow->rtp[packet_direction].payload_detected = true;
282
283
    /* printf("********* [direction: %d] payload_type=%u\n", packet_direction, flow->protos.rtp[packet_direction].payload_type);  */
284
285
0
    if(((flow->rtp[packet_direction].payload_type == 126 /* Enhanced Voice Services (EVS) */)
286
0
        || (flow->rtp[packet_direction].payload_type == 127 /* Enhanced Voice Services (EVS) */))
287
0
       && (payload_len > 12 /* RTP header */)) {
288
0
      const u_int8_t *evs = &payload[12];
289
0
      u_int packet_len = payload_len - 12;
290
0
      u_int num_bits = packet_len * 8;
291
292
0
      flow->flow_multimedia_types = ndpi_multimedia_audio_flow;
293
      /* printf("********* %02X [bits %u]\n", evs[0], num_bits); */
294
295
0
      if(num_bits == 56) {
296
        /* A.2.1.3 Special case for 56 bit payload size (EVS Primary or EVS AMR-WB IO SID) */
297
298
0
        if((evs[0] & 0x80) == 0)
299
0
          flow->rtp[packet_direction].evs_subtype = evs[0] & 0xF;
300
0
        else
301
0
          flow->rtp[packet_direction].evs_subtype = evs[1] & 0xF;
302
0
      } else {
303
304
        /* See ndpi_rtp_payload_type2str() */
305
0
        switch(num_bits) {
306
0
        case    48:
307
0
        case   136:
308
0
        case   144:
309
0
        case   160:
310
0
        case   184:
311
0
        case   192:
312
0
        case   256:
313
0
        case   264:
314
0
        case   288:
315
0
        case   320:
316
0
        case   328:
317
0
        case   368:
318
0
        case   400:
319
0
        case   464:
320
0
        case   480:
321
0
        case   488:
322
0
        case   640:
323
0
        case   960:
324
0
        case  1280:
325
0
        case  1920:
326
0
        case  2560:
327
0
          flow->rtp[packet_direction].evs_subtype = num_bits;
328
0
          break;
329
330
0
        default:
331
0
          if((evs[0] >> 7) == 1) {
332
            /* EVS Codec Mode Request (EVS-CMR) */
333
0
            u_int8_t d_bits = evs[0] & 0X0F;
334
335
0
            flow->rtp[packet_direction].evs_subtype = d_bits + 30 /* dummy offset */;
336
0
          }
337
0
          break;
338
0
        }
339
0
      }
340
0
    }
341
0
  }
342
0
  return 0;
343
0
}
344
345
/* ************************************************************ */
346
347
0
static int keep_extra_dissection(struct ndpi_flow_struct *flow) {
348
0
  return ((flow->rtp[0].payload_detected && flow->rtp[1].payload_detected) ? false :true);
349
0
}
350
351
/* ************************************************************ */
352
353
static int rtp_search_again(struct ndpi_detection_module_struct *ndpi_struct,
354
0
          struct ndpi_flow_struct *flow) {
355
0
  NDPI_LOG_DBG2(ndpi_struct, "Again\n");
356
357
0
  ndpi_rtp_search(ndpi_struct, flow);
358
359
0
  return keep_extra_dissection(flow);
360
0
}
361
362
/* *************************************************************** */
363
364
static void ndpi_int_rtp_add_connection(struct ndpi_detection_module_struct *ndpi_struct,
365
                                        struct ndpi_flow_struct *flow,
366
                                        u_int16_t proto)
367
0
{
368
0
  ndpi_set_detected_protocol(ndpi_struct, flow,
369
0
                             NDPI_PROTOCOL_UNKNOWN, proto,
370
0
                             NDPI_CONFIDENCE_DPI);
371
0
  if(ndpi_struct->cfg.rtp_search_for_stun) {
372
    /* It makes sense to look for STUN only if we didn't capture the entire flow,
373
       from the beginning */
374
0
    if(!(flow->l4_proto == IPPROTO_TCP && ndpi_seen_flow_beginning(flow))) {
375
0
      NDPI_LOG_DBG(ndpi_struct, "Enabling (STUN) extra dissection\n");
376
0
      switch_extra_dissection_to_stun(ndpi_struct, flow, 1);
377
0
    }
378
0
  } else if(proto == NDPI_PROTOCOL_RTP) {
379
0
    if(!flow->extra_packets_func &&
380
0
       keep_extra_dissection(flow) &&
381
0
       ndpi_struct->cfg.rtp_max_packets_extra_dissection > 0) {
382
0
      NDPI_LOG_DBG(ndpi_struct, "Enabling extra dissection\n");
383
0
      flow->max_extra_packets_to_check = ndpi_struct->cfg.rtp_max_packets_extra_dissection;
384
0
      flow->extra_packets_func = rtp_search_again;
385
0
    }
386
0
  }
387
0
}
388
389
/* *************************************************************** */
390
391
static void ndpi_rtp_search(struct ndpi_detection_module_struct *ndpi_struct,
392
0
          struct ndpi_flow_struct *flow) {
393
0
  u_int8_t is_rtp;
394
0
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
395
0
  const u_int8_t *payload = packet->payload;
396
0
  u_int16_t payload_len = packet->payload_packet_len;
397
0
  u_int16_t seq;
398
399
0
  if(packet->payload_packet_len == 0 || packet->tcp_retransmission)
400
0
    return;
401
402
0
  if(packet->tcp != NULL) {
403
0
    if (payload_len < 2) {
404
0
      NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
405
0
      return;
406
0
    }
407
0
    payload += 2; /* Skip the length field */
408
0
    payload_len -= 2;
409
0
  }
410
0
  NDPI_LOG_DBG(ndpi_struct, "search RTP (stage %d/%d)\n", flow->rtp_stage, flow->rtcp_stage);
411
412
  /* * Let some "unknown" packets at the beginning:
413
   * search for 3/4 consecutive RTP/RTCP packets.
414
   * Wait a little longer (4 vs 3 pkts) for RTCP to try to tell if there are only
415
   * RTCP packets in the flow or if RTP/RTCP are multiplexed together */
416
417
0
  if(flow->packet_counter > 3 &&
418
0
     flow->rtp_stage == 0 &&
419
0
     flow->rtcp_stage == 0) {
420
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
421
0
    return;
422
0
  }
423
424
0
  is_rtp = is_rtp_or_rtcp(ndpi_struct, payload, payload_len, &seq);
425
426
0
  if(is_rtp == IS_RTP) {
427
0
    if(flow->rtp_stage == 2) {
428
0
      if(flow->l4_proto == IPPROTO_UDP &&
429
0
         flow->l4.udp.line_pkts[0] >= 2 && flow->l4.udp.line_pkts[1] >= 2) {
430
        /* It seems that it is a LINE stuff; let its dissector to evaluate */
431
0
      } else if(flow->l4_proto == IPPROTO_UDP && flow->l4.udp.epicgames_stage > 0) {
432
        /* It seems that it is a EpicGames stuff; let its dissector to evaluate */
433
0
      } else if(flow->rtp_seq_set[packet->packet_direction] &&
434
0
                flow->rtp_seq[packet->packet_direction] == seq) {
435
        /* Simple heuristic to avoid false positives. Tradeoff between:
436
      - consecutive RTP packets should have different sequence number
437
      - we should handle duplicated traffic */
438
0
        NDPI_LOG_DBG(ndpi_struct, "Same seq on consecutive pkts\n");
439
0
        flow->rtp_stage = 0;
440
0
        flow->rtcp_stage = 0;
441
0
        NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
442
0
      } else {
443
0
        get_rtp_info(ndpi_struct, flow, payload, payload_len);
444
0
        rtp_get_stream_type(flow->rtp[packet->packet_direction].payload_type,
445
0
          &flow->flow_multimedia_types, NDPI_PROTOCOL_UNKNOWN);
446
447
0
        NDPI_LOG_INFO(ndpi_struct, "Found RTP\n");
448
0
        ndpi_int_rtp_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_RTP);
449
0
      }
450
0
      return;
451
0
    }
452
0
    if(flow->rtp_stage == 0) {
453
0
      flow->rtp_seq[packet->packet_direction] = seq;
454
0
      flow->rtp_seq_set[packet->packet_direction] = 1;
455
0
    }
456
0
    flow->rtp_stage += 1;
457
0
  } else if(is_rtp == IS_RTCP && flow->rtp_stage > 0) {
458
    /* RTCP after (some) RTP. Keep looking for RTP */
459
0
  } else if(is_rtp == IS_RTCP && flow->rtp_stage == 0) {
460
0
    if(flow->rtcp_stage == 3) {
461
0
      NDPI_LOG_INFO(ndpi_struct, "Found RTCP\n");
462
0
      ndpi_int_rtp_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_RTCP);
463
0
      return;
464
0
    }
465
0
    flow->rtcp_stage += 1;
466
0
  } else {
467
0
    if(flow->rtp_stage || flow->rtcp_stage) {
468
0
      u_int32_t unused;
469
0
      u_int16_t app_proto = NDPI_PROTOCOL_UNKNOWN;
470
0
      ndpi_protocol_category_t category;
471
      
472
      /* TODO: we should switch to the demultiplexing-code in stun dissector */
473
0
      if(is_stun(ndpi_struct, flow, &app_proto, &category) != 0 &&
474
0
         !is_dtls(packet->payload, packet->payload_packet_len, &unused)) {
475
0
        flow->rtp_stage = 0;
476
0
        flow->rtcp_stage = 0;
477
0
        NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
478
0
      }
479
0
    }
480
0
  }
481
0
}
482
483
/* *************************************************************** */
484
/* https://datatracker.ietf.org/doc/html/rfc4571
485
 * message format for RTP/RTCP over TCP:
486
 *     0                   1                   2                   3
487
 *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
488
 *      ---------------------------------------------------------------
489
 *     |             LENGTH            |  RTP or RTCP packet ...       |
490
 *      ---------------------------------------------------------------
491
 */
492
static void ndpi_search_rtp_tcp(struct ndpi_detection_module_struct *ndpi_struct,
493
        struct ndpi_flow_struct *flow)
494
0
{
495
0
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
496
0
  const u_int8_t *payload = packet->payload;
497
498
0
  if(packet->payload_packet_len < 4){ /* (2) len field + (2) min rtp/rtcp*/
499
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
500
0
    return;
501
0
  }
502
503
0
  u_int16_t len = ntohs(get_u_int16_t(payload, 0));
504
0
  if(len + sizeof(len) != packet->payload_packet_len) { /*fragmented packets are not handled*/
505
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
506
0
  } else {
507
0
    ndpi_rtp_search(ndpi_struct, flow);
508
0
  }
509
510
0
}
511
512
/* *************************************************************** */
513
static void ndpi_search_rtp_udp(struct ndpi_detection_module_struct *ndpi_struct,
514
        struct ndpi_flow_struct *flow)
515
0
{
516
0
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
517
0
  u_int16_t source = ntohs(packet->udp->source);
518
0
  u_int16_t dest = ntohs(packet->udp->dest);
519
  /*
520
   * XXX: not sure if rtp/rtcp over tcp will also mix with Ethereum
521
   * for now, will not add it unitl we have a false positive.
522
   */
523
0
  if((source == 30303) || (dest == 30303 /* Avoid to mix it with Ethereum that looks alike */)
524
0
     || (dest == 5355  /* LLMNR_PORT */)
525
0
     || (dest == 5353  /* MDNS_PORT */)
526
0
     || (dest == 9600  /* FINS_PORT */)
527
0
     || (dest <= 1023)){
528
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
529
0
    return;
530
0
  }
531
0
  ndpi_rtp_search(ndpi_struct, flow);
532
0
}
533
534
/* *************************************************************** */
535
static void ndpi_search_rtp(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow)
536
0
{
537
0
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
538
0
  if(packet->tcp != NULL) {
539
0
    ndpi_search_rtp_tcp(ndpi_struct, flow);
540
0
  } else {
541
0
    ndpi_search_rtp_udp(ndpi_struct, flow);
542
0
  }
543
0
}
544
545
/* *************************************************************** */
546
547
0
void init_rtp_dissector(struct ndpi_detection_module_struct *ndpi_struct) {
548
0
  ndpi_register_dissector("RT(C)P", ndpi_struct,
549
0
                     ndpi_search_rtp,
550
0
                     NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION,
551
0
                     2, NDPI_PROTOCOL_RTP, NDPI_PROTOCOL_RTCP);
552
0
}