Coverage Report

Created: 2025-08-28 06:38

/src/ndpi/src/lib/protocols/natpmp.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * natpmp.c
3
 *
4
 * Copyright (C) 2022-23 - ntop.org
5
 *
6
 * nDPI is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * nDPI is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with nDPI.  If not, see <http://www.gnu.org/licenses/>.
18
 *
19
 */
20
21
22
#include "ndpi_protocol_ids.h"
23
24
#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_NATPMP
25
26
#include "ndpi_api.h"
27
#include "ndpi_private.h"
28
29
6
#define NATPMP_PORT 5351
30
31
enum natpmp_type {
32
  NATPMP_REQUEST_ADDRESS      = 0x00,
33
  NATPMP_REQUEST_UDP_MAPPING  = 0x01,
34
  NATPMP_REQUEST_TCP_MAPPING  = 0x02,
35
  NATPMP_RESPONSE_ADDRESS     = 0x80,
36
  NATPMP_RESPONSE_UDP_MAPPING = 0x81,
37
  NATPMP_RESPONSE_TCP_MAPPING = 0x82
38
};
39
40
static int ndpi_search_natpmp_extra(struct ndpi_detection_module_struct *ndpi_struct,
41
                                    struct ndpi_flow_struct *flow);
42
43
static void ndpi_int_natpmp_add_connection(struct ndpi_detection_module_struct * const ndpi_struct,
44
                                           struct ndpi_flow_struct * const flow)
45
0
{
46
0
  NDPI_LOG_INFO(ndpi_struct, "found nat-pmp\n");
47
0
  ndpi_set_detected_protocol(ndpi_struct, flow,
48
0
                             NDPI_PROTOCOL_NATPMP,
49
0
                             NDPI_PROTOCOL_UNKNOWN,
50
0
                             NDPI_CONFIDENCE_DPI);
51
0
  if (flow->extra_packets_func == NULL)
52
0
  {
53
0
    flow->max_extra_packets_to_check = 5;
54
0
    flow->extra_packets_func = ndpi_search_natpmp_extra;
55
0
  }
56
0
}
57
58
static void natpmp_disable_extra_dissection(struct ndpi_flow_struct * const flow)
59
0
{
60
0
  flow->max_extra_packets_to_check = 0;
61
0
  flow->extra_packets_func = NULL;
62
0
}
63
64
static int natpmp_is_common_header(struct ndpi_packet_struct const * const packet)
65
833
{
66
833
  return packet->payload_packet_len >= 2 && packet->payload[0] == 0x00 /* Protocol version: 0x00 */;
67
833
}
68
69
static int natpmp_is_valid(struct ndpi_packet_struct const * const packet, enum natpmp_type * const natpmp_type)
70
833
{
71
833
  if (natpmp_is_common_header(packet) == 0)
72
638
  {
73
638
    return 0;
74
638
  }
75
76
195
  *natpmp_type = packet->payload[1];
77
195
  switch (*natpmp_type)
78
195
  {
79
117
    case NATPMP_REQUEST_ADDRESS:
80
117
      if (packet->payload_packet_len != 2)
81
116
      {
82
116
        return 0;
83
116
      }
84
1
      break;
85
18
    case NATPMP_REQUEST_UDP_MAPPING:
86
19
    case NATPMP_REQUEST_TCP_MAPPING:
87
19
      if (packet->payload_packet_len != 12 || get_u_int16_t(packet->payload, 2) != 0x0000)
88
18
      {
89
18
        return 0;
90
18
      }
91
1
      break;
92
1
    case NATPMP_RESPONSE_ADDRESS:
93
2
    case NATPMP_RESPONSE_UDP_MAPPING:
94
3
    case NATPMP_RESPONSE_TCP_MAPPING:
95
3
      if ((*natpmp_type == NATPMP_RESPONSE_ADDRESS && packet->payload_packet_len != 12) ||
96
3
          (*natpmp_type != NATPMP_RESPONSE_ADDRESS && packet->payload_packet_len != 16))
97
3
      {
98
3
        return 0;
99
3
      }
100
101
0
      {
102
0
        u_int16_t result_code = ntohs(get_u_int16_t(packet->payload, 2));
103
0
        if (result_code > 5)
104
0
        {
105
0
          return 0;
106
0
        }
107
0
      }
108
0
      break;
109
110
56
    default:
111
56
      return 0;
112
195
  }
113
114
2
  return 1;
115
195
}
116
117
static int ndpi_search_natpmp_extra(struct ndpi_detection_module_struct *ndpi_struct,
118
                                    struct ndpi_flow_struct *flow)
119
0
{
120
0
  struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
121
0
  enum natpmp_type natpmp_type;
122
123
0
  if (natpmp_is_valid(packet, &natpmp_type) == 0)
124
0
  {
125
0
    ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid NATPMP Header");
126
0
    return 0;
127
0
  }
128
129
0
  switch (natpmp_type)
130
0
  {
131
0
    case NATPMP_REQUEST_ADDRESS:
132
0
      return 1; // Nothing to do here.
133
0
    case NATPMP_REQUEST_UDP_MAPPING:
134
0
    case NATPMP_REQUEST_TCP_MAPPING:
135
0
      flow->protos.natpmp.internal_port = ntohs(get_u_int16_t(packet->payload, 4));
136
0
      flow->protos.natpmp.external_port = ntohs(get_u_int16_t(packet->payload, 6));
137
0
      if (flow->protos.natpmp.internal_port == 0)
138
0
      {
139
0
        ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Request Port Mapping: Internal port must not 0");
140
0
      }
141
0
      break;
142
0
    case NATPMP_RESPONSE_ADDRESS:
143
0
      flow->protos.natpmp.result_code = ntohs(get_u_int16_t(packet->payload, 2));
144
0
      flow->protos.natpmp.external_address.ipv4 = get_u_int32_t(packet->payload, 8);
145
0
      if (flow->protos.natpmp.result_code != 0 && flow->protos.natpmp.external_address.ipv4 != 0)
146
0
      {
147
0
        ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Address Response: Result code indicates an error, but External IPv4 Address is set");
148
0
      }
149
0
      break;
150
0
    case NATPMP_RESPONSE_UDP_MAPPING:
151
0
    case NATPMP_RESPONSE_TCP_MAPPING:
152
0
    {
153
0
      flow->protos.natpmp.internal_port = ntohs(get_u_int16_t(packet->payload, 8));
154
0
      flow->protos.natpmp.external_port = ntohs(get_u_int16_t(packet->payload, 10));
155
0
      if (flow->protos.natpmp.internal_port == 0 || flow->protos.natpmp.external_port == 0)
156
0
      {
157
0
        ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Port Mapping Response: Internal/External port must not 0");
158
0
      }
159
0
      break;
160
0
    }
161
0
  }
162
163
0
  return 1;
164
0
}
165
166
static void ndpi_search_natpmp(struct ndpi_detection_module_struct *ndpi_struct,
167
                               struct ndpi_flow_struct *flow)
168
833
{
169
833
  struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
170
833
  enum natpmp_type natpmp_type;
171
172
833
  NDPI_LOG_DBG(ndpi_struct, "search nat-pmp\n");
173
174
833
  if (natpmp_is_valid(packet, &natpmp_type) == 0)
175
831
  {
176
831
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
177
831
    return;
178
831
  }
179
180
2
  if ((flow->packet_counter > 2 && natpmp_type != NATPMP_REQUEST_ADDRESS) ||
181
2
      ntohs(packet->udp->source) == NATPMP_PORT || ntohs(packet->udp->dest) == NATPMP_PORT)
182
0
  {
183
0
    ndpi_int_natpmp_add_connection(ndpi_struct, flow);
184
0
    if (ndpi_search_natpmp_extra(ndpi_struct, flow) == 0)
185
0
    {
186
0
      natpmp_disable_extra_dissection(flow);
187
0
    }
188
0
  }
189
2
}
190
191
void init_natpmp_dissector(struct ndpi_detection_module_struct *ndpi_struct)
192
8.21k
{
193
8.21k
  register_dissector("NAT-PMP", ndpi_struct,
194
8.21k
                     ndpi_search_natpmp,
195
8.21k
                     NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_UDP_WITH_PAYLOAD,
196
8.21k
                     1, NDPI_PROTOCOL_NATPMP);
197
8.21k
}