/src/suricata7/src/decode-nsh.c
Line | Count | Source |
1 | | /* Copyright (C) 2020-2021 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** |
19 | | * \ingroup decode |
20 | | * |
21 | | * @{ |
22 | | */ |
23 | | |
24 | | /** |
25 | | * \file |
26 | | * |
27 | | * \author Carl Smith <carl.smith@alliedtelesis.co.nz> |
28 | | * |
29 | | * Decodes Network Service Header (NSH) |
30 | | */ |
31 | | |
32 | | #include "suricata-common.h" |
33 | | #include "suricata.h" |
34 | | #include "decode.h" |
35 | | #include "decode-events.h" |
36 | | #include "decode-nsh.h" |
37 | | |
38 | | #include "util-validate.h" |
39 | | #include "util-unittest.h" |
40 | | #include "util-debug.h" |
41 | | |
42 | | /** |
43 | | * \brief Function to decode NSH packets |
44 | | */ |
45 | | |
46 | | int DecodeNSH(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len) |
47 | 10.6k | { |
48 | 10.6k | DEBUG_VALIDATE_BUG_ON(pkt == NULL); |
49 | | |
50 | 10.6k | StatsIncr(tv, dtv->counter_nsh); |
51 | | |
52 | | /* Check minimum header size */ |
53 | 10.6k | if (len < sizeof(NshHdr)) { |
54 | 1.04k | ENGINE_SET_INVALID_EVENT(p, NSH_HEADER_TOO_SMALL); |
55 | 1.04k | return TM_ECODE_FAILED; |
56 | 1.04k | } |
57 | 9.55k | if (!PacketIncreaseCheckLayers(p)) { |
58 | 497 | return TM_ECODE_FAILED; |
59 | 497 | } |
60 | | |
61 | | /* Sanity check the header version */ |
62 | 9.05k | const NshHdr *hdr = (const NshHdr *)pkt; |
63 | 9.05k | uint16_t version = SCNtohs(hdr->ver_flags_len) >> 14; |
64 | 9.05k | if (version != 0) { |
65 | 519 | ENGINE_SET_EVENT(p, NSH_UNSUPPORTED_VERSION); |
66 | 519 | return TM_ECODE_OK; |
67 | 519 | } |
68 | | |
69 | | /* Should always be some data after the header */ |
70 | 8.54k | uint16_t length = (SCNtohs(hdr->ver_flags_len) & 0x003f) * 4; |
71 | 8.54k | if (length >= len) { |
72 | 534 | ENGINE_SET_INVALID_EVENT(p, NSH_BAD_HEADER_LENGTH); |
73 | 534 | return TM_ECODE_FAILED; |
74 | 534 | } |
75 | | |
76 | | /* Check for valid MD types */ |
77 | 8.00k | uint8_t md_type = hdr->md_type; |
78 | 8.00k | if (md_type == 0 || md_type == 0xF) { |
79 | | /* We should silently ignore these packets */ |
80 | 1.00k | ENGINE_SET_EVENT(p, NSH_RESERVED_TYPE); |
81 | 1.00k | return TM_ECODE_OK; |
82 | 7.00k | } else if (md_type == 1) { |
83 | | /* Fixed header length format */ |
84 | 987 | if (length != 24) { |
85 | 369 | ENGINE_SET_INVALID_EVENT(p, NSH_BAD_HEADER_LENGTH); |
86 | 369 | return TM_ECODE_FAILED; |
87 | 369 | } |
88 | 6.01k | } else if (md_type != 2) { |
89 | | /* Not variable header length either */ |
90 | 431 | ENGINE_SET_EVENT(p, NSH_UNSUPPORTED_TYPE); |
91 | 431 | return TM_ECODE_OK; |
92 | 431 | } |
93 | | |
94 | | /* Now we can safely read the rest of the header */ |
95 | 6.20k | uint8_t next_protocol = hdr->next_protocol; |
96 | | #ifdef DEBUG |
97 | | if (SCLogDebugEnabled()) { |
98 | | uint32_t spi_si = SCNtohl(hdr->spi_si); |
99 | | uint32_t spi = ((spi_si & 0xFFFFFF00) >> 8); |
100 | | uint8_t si = (uint8_t)(spi_si & 0xFF); |
101 | | SCLogDebug("NSH: version %u length %u spi %u si %u next_protocol %u", version, length, spi, |
102 | | si, next_protocol); |
103 | | } |
104 | | #endif /* DEBUG */ |
105 | | |
106 | | /* Try to decode the payload */ |
107 | 6.20k | switch (next_protocol) { |
108 | 918 | case NSH_NEXT_PROTO_IPV4: |
109 | 918 | if (len - length > USHRT_MAX) { |
110 | 36 | return TM_ECODE_FAILED; |
111 | 36 | } |
112 | 882 | return DecodeIPV4(tv, dtv, p, pkt + length, (uint16_t)(len - length)); |
113 | 476 | case NSH_NEXT_PROTO_IPV6: |
114 | 476 | if (len - length > USHRT_MAX) { |
115 | 36 | return TM_ECODE_FAILED; |
116 | 36 | } |
117 | 440 | return DecodeIPV6(tv, dtv, p, pkt + length, (uint16_t)(len - length)); |
118 | 3.48k | case NSH_NEXT_PROTO_ETHERNET: |
119 | 3.48k | return DecodeEthernet(tv, dtv, p, pkt + length, len - length); |
120 | 549 | case NSH_NEXT_PROTO_MPLS: |
121 | 549 | return DecodeMPLS(tv, dtv, p, pkt + length, len - length); |
122 | 0 | case NSH_NEXT_PROTO_NSH: |
123 | 779 | default: |
124 | 779 | SCLogDebug("NSH next protocol %u not supported", next_protocol); |
125 | 779 | ENGINE_SET_EVENT(p, NSH_UNKNOWN_PAYLOAD); |
126 | 779 | break; |
127 | 6.20k | } |
128 | 779 | return TM_ECODE_OK; |
129 | 6.20k | } |
130 | | |
131 | | #ifdef UNITTESTS |
132 | | |
133 | | static uint8_t valid_nsh_packet[] = { 0x00, 0x04, 0x02, 0x01, 0x00, 0x00, 0x02, 0x02, 0x45, 0x10, |
134 | | 0x00, 0x3c, 0x78, 0x8f, 0x40, 0x00, 0x3f, 0x06, 0x79, 0x05, 0x0b, 0x06, 0x06, 0x06, 0x33, 0x06, |
135 | | 0x06, 0x06, 0xbd, 0x2e, 0x00, 0x16, 0xc9, 0xee, 0x07, 0x62, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, |
136 | | 0x16, 0xd0, 0x2f, 0x36, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0xa9, 0x5f, |
137 | | 0x7f, 0xed, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07 }; |
138 | | |
139 | | static int DecodeNSHTestHeaderTooSmall(void) |
140 | | { |
141 | | ThreadVars tv; |
142 | | DecodeThreadVars dtv; |
143 | | Packet *p; |
144 | | |
145 | | p = PacketGetFromAlloc(); |
146 | | FAIL_IF_NULL(p); |
147 | | memset(&dtv, 0, sizeof(DecodeThreadVars)); |
148 | | memset(&tv, 0, sizeof(ThreadVars)); |
149 | | |
150 | | /* A packet that is too small to have a complete NSH header */ |
151 | | DecodeNSH(&tv, &dtv, p, valid_nsh_packet, 7); |
152 | | FAIL_IF_NOT(ENGINE_ISSET_EVENT(p, NSH_HEADER_TOO_SMALL)); |
153 | | |
154 | | SCFree(p); |
155 | | PASS; |
156 | | } |
157 | | |
158 | | static int DecodeNSHTestUnsupportedVersion(void) |
159 | | { |
160 | | ThreadVars tv; |
161 | | DecodeThreadVars dtv; |
162 | | Packet *p; |
163 | | |
164 | | p = PacketGetFromAlloc(); |
165 | | FAIL_IF_NULL(p); |
166 | | memset(&dtv, 0, sizeof(DecodeThreadVars)); |
167 | | memset(&tv, 0, sizeof(ThreadVars)); |
168 | | |
169 | | /* Non-zero version field */ |
170 | | valid_nsh_packet[0] = 0xFF; |
171 | | DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet)); |
172 | | valid_nsh_packet[0] = 0x00; |
173 | | FAIL_IF_NOT(ENGINE_ISSET_EVENT(p, NSH_UNSUPPORTED_VERSION)); |
174 | | |
175 | | SCFree(p); |
176 | | PASS; |
177 | | } |
178 | | |
179 | | static int DecodeNSHTestPacketTooSmall(void) |
180 | | { |
181 | | ThreadVars tv; |
182 | | DecodeThreadVars dtv; |
183 | | Packet *p; |
184 | | |
185 | | p = PacketGetFromAlloc(); |
186 | | FAIL_IF_NULL(p); |
187 | | memset(&dtv, 0, sizeof(DecodeThreadVars)); |
188 | | memset(&tv, 0, sizeof(ThreadVars)); |
189 | | |
190 | | /* A packet that has no payload */ |
191 | | DecodeNSH(&tv, &dtv, p, valid_nsh_packet, 8); |
192 | | FAIL_IF_NOT(ENGINE_ISSET_EVENT(p, NSH_BAD_HEADER_LENGTH)); |
193 | | |
194 | | SCFree(p); |
195 | | PASS; |
196 | | } |
197 | | |
198 | | static int DecodeNSHTestReservedType(void) |
199 | | { |
200 | | ThreadVars tv; |
201 | | DecodeThreadVars dtv; |
202 | | Packet *p; |
203 | | |
204 | | p = PacketGetFromAlloc(); |
205 | | FAIL_IF_NULL(p); |
206 | | memset(&dtv, 0, sizeof(DecodeThreadVars)); |
207 | | memset(&tv, 0, sizeof(ThreadVars)); |
208 | | |
209 | | /* Reserved type */ |
210 | | valid_nsh_packet[2] = 0x00; |
211 | | DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet)); |
212 | | valid_nsh_packet[2] = 0x02; |
213 | | FAIL_IF_NOT(ENGINE_ISSET_EVENT(p, NSH_RESERVED_TYPE)); |
214 | | |
215 | | SCFree(p); |
216 | | PASS; |
217 | | } |
218 | | |
219 | | static int DecodeNSHTestInvalidType(void) |
220 | | { |
221 | | ThreadVars tv; |
222 | | DecodeThreadVars dtv; |
223 | | Packet *p; |
224 | | |
225 | | p = PacketGetFromAlloc(); |
226 | | FAIL_IF_NULL(p); |
227 | | memset(&dtv, 0, sizeof(DecodeThreadVars)); |
228 | | memset(&tv, 0, sizeof(ThreadVars)); |
229 | | |
230 | | /* Type length mismatch */ |
231 | | valid_nsh_packet[2] = 0x01; |
232 | | DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet)); |
233 | | valid_nsh_packet[2] = 0x02; |
234 | | FAIL_IF_NOT(ENGINE_ISSET_EVENT(p, NSH_BAD_HEADER_LENGTH)); |
235 | | SCFree(p); |
236 | | PASS; |
237 | | } |
238 | | |
239 | | static int DecodeNSHTestUnsupportedType(void) |
240 | | { |
241 | | ThreadVars tv; |
242 | | DecodeThreadVars dtv; |
243 | | Packet *p; |
244 | | |
245 | | p = PacketGetFromAlloc(); |
246 | | FAIL_IF_NULL(p); |
247 | | memset(&dtv, 0, sizeof(DecodeThreadVars)); |
248 | | memset(&tv, 0, sizeof(ThreadVars)); |
249 | | |
250 | | /* Unsupported type */ |
251 | | valid_nsh_packet[2] = 0x03; |
252 | | DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet)); |
253 | | valid_nsh_packet[2] = 0x02; |
254 | | FAIL_IF_NOT(ENGINE_ISSET_EVENT(p, NSH_UNSUPPORTED_TYPE)); |
255 | | |
256 | | SCFree(p); |
257 | | PASS; |
258 | | } |
259 | | |
260 | | static int DecodeNSHTestUnknownPayload(void) |
261 | | { |
262 | | ThreadVars tv; |
263 | | DecodeThreadVars dtv; |
264 | | Packet *p; |
265 | | |
266 | | p = PacketGetFromAlloc(); |
267 | | FAIL_IF_NULL(p); |
268 | | memset(&dtv, 0, sizeof(DecodeThreadVars)); |
269 | | memset(&tv, 0, sizeof(ThreadVars)); |
270 | | |
271 | | /* Unknown type */ |
272 | | valid_nsh_packet[3] = 0x99; |
273 | | DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet)); |
274 | | valid_nsh_packet[3] = 0x01; |
275 | | FAIL_IF_NOT(ENGINE_ISSET_EVENT(p, NSH_UNKNOWN_PAYLOAD)); |
276 | | |
277 | | SCFree(p); |
278 | | PASS; |
279 | | } |
280 | | |
281 | | #endif /* UNITTESTS */ |
282 | | |
283 | | void DecodeNSHRegisterTests(void) |
284 | 0 | { |
285 | | #ifdef UNITTESTS |
286 | | UtRegisterTest("DecodeNSHTestHeaderTooSmall", DecodeNSHTestHeaderTooSmall); |
287 | | UtRegisterTest("DecodeNSHTestUnsupportedVersion", DecodeNSHTestUnsupportedVersion); |
288 | | UtRegisterTest("DecodeNSHTestPacketTooSmall", DecodeNSHTestPacketTooSmall); |
289 | | UtRegisterTest("DecodeNSHTestReservedType", DecodeNSHTestReservedType); |
290 | | UtRegisterTest("DecodeNSHTestInvalidType", DecodeNSHTestInvalidType); |
291 | | UtRegisterTest("DecodeNSHTestUnsupportedType", DecodeNSHTestUnsupportedType); |
292 | | UtRegisterTest("DecodeNSHTestUnknownPayload", DecodeNSHTestUnknownPayload); |
293 | | #endif /* UNITTESTS */ |
294 | 0 | } |