suricata
decode-nsh.c
Go to the documentation of this file.
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-debug.h"
40 
41 /**
42  * \brief Function to decode NSH packets
43  */
44 
45 int DecodeNSH(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len)
46 {
47  DEBUG_VALIDATE_BUG_ON(pkt == NULL);
48 
50 
51  /* Check minimum header size */
52  if (len < sizeof(NshHdr)) {
54  return TM_ECODE_FAILED;
55  }
56  if (!PacketIncreaseCheckLayers(p)) {
57  return TM_ECODE_FAILED;
58  }
59 
60  /* Sanity check the header version */
61  const NshHdr *hdr = (const NshHdr *)pkt;
62  uint16_t version = SCNtohs(hdr->ver_flags_len) >> 14;
63  if (version != 0) {
65  return TM_ECODE_OK;
66  }
67 
68  /* Should always be some data after the header */
69  uint16_t length = (SCNtohs(hdr->ver_flags_len) & 0x003f) * 4;
70  if (length >= len) {
72  return TM_ECODE_FAILED;
73  }
74 
75  /* Check for valid MD types */
76  uint8_t md_type = hdr->md_type;
77  if (md_type == 0 || md_type == 0xF) {
78  /* We should silently ignore these packets */
80  return TM_ECODE_OK;
81  } else if (md_type == 1) {
82  /* Fixed header length format */
83  if (length != 24) {
85  return TM_ECODE_FAILED;
86  }
87  } else if (md_type != 2) {
88  /* Not variable header length either */
90  return TM_ECODE_OK;
91  }
92 
93  /* Now we can safely read the rest of the header */
94  uint8_t next_protocol = hdr->next_protocol;
95 #ifdef DEBUG
96  if (SCLogDebugEnabled()) {
97  uint32_t spi_si = SCNtohl(hdr->spi_si);
98  uint32_t spi = ((spi_si & 0xFFFFFF00) >> 8);
99  uint8_t si = (uint8_t)(spi_si & 0xFF);
100  SCLogDebug("NSH: version %u length %u spi %u si %u next_protocol %u", version, length, spi,
101  si, next_protocol);
102  }
103 #endif /* DEBUG */
104 
105  /* Try to decode the payload */
106  switch (next_protocol) {
107  case NSH_NEXT_PROTO_IPV4:
108  if (len - length > USHRT_MAX) {
109  return TM_ECODE_FAILED;
110  }
111  return DecodeIPV4(tv, dtv, p, pkt + length, (uint16_t)(len - length));
112  case NSH_NEXT_PROTO_IPV6:
113  return DecodeIPV6(tv, dtv, p, pkt + length, len - length);
115  return DecodeEthernet(tv, dtv, p, pkt + length, len - length);
116  case NSH_NEXT_PROTO_MPLS:
117  return DecodeMPLS(tv, dtv, p, pkt + length, len - length);
118  case NSH_NEXT_PROTO_NSH:
119  default:
120  SCLogDebug("NSH next protocol %u not supported", next_protocol);
122  break;
123  }
124  return TM_ECODE_OK;
125 }
126 
127 #ifdef UNITTESTS
128 
129 static uint8_t valid_nsh_packet[] = { 0x00, 0x04, 0x02, 0x01, 0x00, 0x00, 0x02, 0x02, 0x45, 0x10,
130  0x00, 0x3c, 0x78, 0x8f, 0x40, 0x00, 0x3f, 0x06, 0x79, 0x05, 0x0b, 0x06, 0x06, 0x06, 0x33, 0x06,
131  0x06, 0x06, 0xbd, 0x2e, 0x00, 0x16, 0xc9, 0xee, 0x07, 0x62, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02,
132  0x16, 0xd0, 0x2f, 0x36, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0xa9, 0x5f,
133  0x7f, 0xed, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07 };
134 
135 static int DecodeNSHTestHeaderTooSmall(void)
136 {
137  ThreadVars tv;
139  Packet *p;
140 
141  p = PacketGetFromAlloc();
142  FAIL_IF_NULL(p);
143  memset(&dtv, 0, sizeof(DecodeThreadVars));
144  memset(&tv, 0, sizeof(ThreadVars));
145 
146  /* A packet that is too small to have a complete NSH header */
147  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, 7);
149 
150  PacketFree(p);
151  PASS;
152 }
153 
154 static int DecodeNSHTestUnsupportedVersion(void)
155 {
156  ThreadVars tv;
158  Packet *p;
159 
160  p = PacketGetFromAlloc();
161  FAIL_IF_NULL(p);
162  memset(&dtv, 0, sizeof(DecodeThreadVars));
163  memset(&tv, 0, sizeof(ThreadVars));
164 
165  /* Non-zero version field */
166  valid_nsh_packet[0] = 0xFF;
167  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet));
168  valid_nsh_packet[0] = 0x00;
170 
171  PacketFree(p);
172  PASS;
173 }
174 
175 static int DecodeNSHTestPacketTooSmall(void)
176 {
177  ThreadVars tv;
179  Packet *p;
180 
181  p = PacketGetFromAlloc();
182  FAIL_IF_NULL(p);
183  memset(&dtv, 0, sizeof(DecodeThreadVars));
184  memset(&tv, 0, sizeof(ThreadVars));
185 
186  /* A packet that has no payload */
187  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, 8);
189 
190  PacketFree(p);
191  PASS;
192 }
193 
194 static int DecodeNSHTestReservedType(void)
195 {
196  ThreadVars tv;
198  Packet *p;
199 
200  p = PacketGetFromAlloc();
201  FAIL_IF_NULL(p);
202  memset(&dtv, 0, sizeof(DecodeThreadVars));
203  memset(&tv, 0, sizeof(ThreadVars));
204 
205  /* Reserved type */
206  valid_nsh_packet[2] = 0x00;
207  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet));
208  valid_nsh_packet[2] = 0x02;
210 
211  PacketFree(p);
212  PASS;
213 }
214 
215 static int DecodeNSHTestInvalidType(void)
216 {
217  ThreadVars tv;
219  Packet *p;
220 
221  p = PacketGetFromAlloc();
222  FAIL_IF_NULL(p);
223  memset(&dtv, 0, sizeof(DecodeThreadVars));
224  memset(&tv, 0, sizeof(ThreadVars));
225 
226  /* Type length mismatch */
227  valid_nsh_packet[2] = 0x01;
228  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet));
229  valid_nsh_packet[2] = 0x02;
231  PacketFree(p);
232  PASS;
233 }
234 
235 static int DecodeNSHTestUnsupportedType(void)
236 {
237  ThreadVars tv;
239  Packet *p;
240 
241  p = PacketGetFromAlloc();
242  FAIL_IF_NULL(p);
243  memset(&dtv, 0, sizeof(DecodeThreadVars));
244  memset(&tv, 0, sizeof(ThreadVars));
245 
246  /* Unsupported type */
247  valid_nsh_packet[2] = 0x03;
248  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet));
249  valid_nsh_packet[2] = 0x02;
251 
252  PacketFree(p);
253  PASS;
254 }
255 
256 static int DecodeNSHTestUnknownPayload(void)
257 {
258  ThreadVars tv;
260  Packet *p;
261 
262  p = PacketGetFromAlloc();
263  FAIL_IF_NULL(p);
264  memset(&dtv, 0, sizeof(DecodeThreadVars));
265  memset(&tv, 0, sizeof(ThreadVars));
266 
267  /* Unknown type */
268  valid_nsh_packet[3] = 0x99;
269  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet));
270  valid_nsh_packet[3] = 0x01;
272 
273  PacketFree(p);
274  PASS;
275 }
276 
277 #endif /* UNITTESTS */
278 
280 {
281 #ifdef UNITTESTS
282  UtRegisterTest("DecodeNSHTestHeaderTooSmall", DecodeNSHTestHeaderTooSmall);
283  UtRegisterTest("DecodeNSHTestUnsupportedVersion", DecodeNSHTestUnsupportedVersion);
284  UtRegisterTest("DecodeNSHTestPacketTooSmall", DecodeNSHTestPacketTooSmall);
285  UtRegisterTest("DecodeNSHTestReservedType", DecodeNSHTestReservedType);
286  UtRegisterTest("DecodeNSHTestInvalidType", DecodeNSHTestInvalidType);
287  UtRegisterTest("DecodeNSHTestUnsupportedType", DecodeNSHTestUnsupportedType);
288  UtRegisterTest("DecodeNSHTestUnknownPayload", DecodeNSHTestUnknownPayload);
289 #endif /* UNITTESTS */
290 }
ENGINE_SET_EVENT
#define ENGINE_SET_EVENT(p, e)
Definition: decode.h:1231
len
uint8_t len
Definition: app-layer-dnp3.h:2
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
NSH_NEXT_PROTO_ETHERNET
#define NSH_NEXT_PROTO_ETHERNET
Definition: decode-nsh.h:31
DecodeNSH
int DecodeNSH(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len)
Function to decode NSH packets.
Definition: decode-nsh.c:45
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:101
ENGINE_ISSET_EVENT
#define ENGINE_ISSET_EVENT(p, e)
Definition: decode.h:1244
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
NSH_UNSUPPORTED_VERSION
@ NSH_UNSUPPORTED_VERSION
Definition: decode-events.h:233
DecodeThreadVars_::counter_nsh
StatsCounterId counter_nsh
Definition: decode.h:1055
DecodeNSHRegisterTests
void DecodeNSHRegisterTests(void)
Definition: decode-nsh.c:279
NSH_NEXT_PROTO_IPV4
#define NSH_NEXT_PROTO_IPV4
Definition: decode-nsh.h:29
p
Packet * p
Definition: fuzz_dataset.c:30
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:82
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
TM_ECODE_OK
@ TM_ECODE_OK
Definition: tm-threads-common.h:81
decode.h
util-debug.h
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
spi
uint32_t spi
Definition: decode-esp.h:0
length
uint16_t length
Definition: decode-sctp.h:2
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:57
NSH_BAD_HEADER_LENGTH
@ NSH_BAD_HEADER_LENGTH
Definition: decode-events.h:234
StatsCounterIncr
void StatsCounterIncr(StatsThreadContext *stats, StatsCounterId id)
Increments the local counter.
Definition: counters.c:163
PacketFree
void PacketFree(Packet *p)
Return a malloced packet.
Definition: decode.c:221
DecodeMPLS
int DecodeMPLS(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len)
Definition: decode-mpls.c:48
Packet_
Definition: decode.h:516
next_protocol
uint8_t next_protocol
Definition: decode-nsh.h:2
decode-nsh.h
DecodeIPV6
int DecodeIPV6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len)
Definition: decode-ipv6.c:551
decode-events.h
dtv
DecodeThreadVars * dtv
Definition: fuzz_decodepcapfile.c:35
SCNtohs
#define SCNtohs(x)
Definition: suricata-common.h:436
suricata-common.h
version
uint8_t version
Definition: decode-gre.h:1
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:34
util-validate.h
PacketGetFromAlloc
Packet * PacketGetFromAlloc(void)
Get a malloced packet.
Definition: decode.c:260
NSH_NEXT_PROTO_NSH
#define NSH_NEXT_PROTO_NSH
Definition: decode-nsh.h:32
md_type
uint8_t md_type
Definition: decode-nsh.h:1
SCNtohl
#define SCNtohl(x)
Definition: suricata-common.h:435
DecodeThreadVars_
Structure to hold thread specific data for all decode modules.
Definition: decode.h:995
NSH_NEXT_PROTO_IPV6
#define NSH_NEXT_PROTO_IPV6
Definition: decode-nsh.h:30
NSH_UNSUPPORTED_TYPE
@ NSH_UNSUPPORTED_TYPE
Definition: decode-events.h:236
NSH_UNKNOWN_PAYLOAD
@ NSH_UNKNOWN_PAYLOAD
Definition: decode-events.h:237
suricata.h
ENGINE_SET_INVALID_EVENT
#define ENGINE_SET_INVALID_EVENT(p, e)
Definition: decode.h:1239
NSH_NEXT_PROTO_MPLS
#define NSH_NEXT_PROTO_MPLS
Definition: decode-nsh.h:33
NSH_RESERVED_TYPE
@ NSH_RESERVED_TYPE
Definition: decode-events.h:235
spi_si
uint32_t spi_si
Definition: decode-nsh.h:3
NSH_HEADER_TOO_SMALL
@ NSH_HEADER_TOO_SMALL
Definition: decode-events.h:232
DecodeIPV4
int DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
Definition: decode-ipv4.c:515
ThreadVars_::stats
StatsThreadContext stats
Definition: threadvars.h:120
SCLogDebugEnabled
int SCLogDebugEnabled(void)
Returns whether debug messages are enabled to be logged or not.
Definition: util-debug.c:771
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:109
DecodeEthernet
int DecodeEthernet(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len)
Definition: decode-ethernet.c:41