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-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 {
48  DEBUG_VALIDATE_BUG_ON(pkt == NULL);
49 
51 
52  /* Check minimum header size */
53  if (len < sizeof(NshHdr)) {
55  return TM_ECODE_FAILED;
56  }
57  if (!PacketIncreaseCheckLayers(p)) {
58  return TM_ECODE_FAILED;
59  }
60 
61  /* Sanity check the header version */
62  const NshHdr *hdr = (const NshHdr *)pkt;
63  uint16_t version = SCNtohs(hdr->ver_flags_len) >> 14;
64  if (version != 0) {
66  return TM_ECODE_OK;
67  }
68 
69  /* Should always be some data after the header */
70  uint16_t length = (SCNtohs(hdr->ver_flags_len) & 0x003f) * 4;
71  if (length >= len) {
73  return TM_ECODE_FAILED;
74  }
75 
76  /* Check for valid MD types */
77  uint8_t md_type = hdr->md_type;
78  if (md_type == 0 || md_type == 0xF) {
79  /* We should silently ignore these packets */
81  return TM_ECODE_OK;
82  } else if (md_type == 1) {
83  /* Fixed header length format */
84  if (length != 24) {
86  return TM_ECODE_FAILED;
87  }
88  } else if (md_type != 2) {
89  /* Not variable header length either */
91  return TM_ECODE_OK;
92  }
93 
94  /* Now we can safely read the rest of the header */
95  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  switch (next_protocol) {
108  case NSH_NEXT_PROTO_IPV4:
109  if (len - length > USHRT_MAX) {
110  return TM_ECODE_FAILED;
111  }
112  return DecodeIPV4(tv, dtv, p, pkt + length, (uint16_t)(len - length));
113  case NSH_NEXT_PROTO_IPV6:
114  if (len - length > USHRT_MAX) {
115  return TM_ECODE_FAILED;
116  }
117  return DecodeIPV6(tv, dtv, p, pkt + length, (uint16_t)(len - length));
119  return DecodeEthernet(tv, dtv, p, pkt + length, len - length);
120  case NSH_NEXT_PROTO_MPLS:
121  return DecodeMPLS(tv, dtv, p, pkt + length, len - length);
122  case NSH_NEXT_PROTO_NSH:
123  default:
124  SCLogDebug("NSH next protocol %u not supported", next_protocol);
126  break;
127  }
128  return TM_ECODE_OK;
129 }
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;
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);
153 
154  SCFree(p);
155  PASS;
156 }
157 
158 static int DecodeNSHTestUnsupportedVersion(void)
159 {
160  ThreadVars tv;
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;
174 
175  SCFree(p);
176  PASS;
177 }
178 
179 static int DecodeNSHTestPacketTooSmall(void)
180 {
181  ThreadVars tv;
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);
193 
194  SCFree(p);
195  PASS;
196 }
197 
198 static int DecodeNSHTestReservedType(void)
199 {
200  ThreadVars tv;
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;
214 
215  SCFree(p);
216  PASS;
217 }
218 
219 static int DecodeNSHTestInvalidType(void)
220 {
221  ThreadVars tv;
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;
235  SCFree(p);
236  PASS;
237 }
238 
239 static int DecodeNSHTestUnsupportedType(void)
240 {
241  ThreadVars tv;
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;
255 
256  SCFree(p);
257  PASS;
258 }
259 
260 static int DecodeNSHTestUnknownPayload(void)
261 {
262  ThreadVars tv;
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;
276 
277  SCFree(p);
278  PASS;
279 }
280 
281 #endif /* UNITTESTS */
282 
284 {
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 }
ENGINE_SET_EVENT
#define ENGINE_SET_EVENT(p, e)
Definition: decode.h:880
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:32
StatsIncr
void StatsIncr(ThreadVars *tv, uint16_t id)
Increments the local counter.
Definition: counters.c:167
NSH_UNKNOWN_PAYLOAD
@ NSH_UNKNOWN_PAYLOAD
Definition: decode-events.h:217
NSH_RESERVED_TYPE
@ NSH_RESERVED_TYPE
Definition: decode-events.h:215
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:46
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
NSH_UNSUPPORTED_VERSION
@ NSH_UNSUPPORTED_VERSION
Definition: decode-events.h:213
ENGINE_ISSET_EVENT
#define ENGINE_ISSET_EVENT(p, e)
Definition: decode.h:895
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:296
DecodeNSHRegisterTests
void DecodeNSHRegisterTests(void)
Definition: decode-nsh.c:283
NSH_NEXT_PROTO_IPV4
#define NSH_NEXT_PROTO_IPV4
Definition: decode-nsh.h:30
NSH_BAD_HEADER_LENGTH
@ NSH_BAD_HEADER_LENGTH
Definition: decode-events.h:214
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:83
util-unittest.h
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:82
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
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:57
DecodeMPLS
int DecodeMPLS(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len)
Definition: decode-mpls.c:49
Packet_
Definition: decode.h:425
DecodeThreadVars_::counter_nsh
uint16_t counter_nsh
Definition: decode.h:701
DecodeIPV6
int DecodeIPV6(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
Definition: decode-ipv6.c:564
next_protocol
uint8_t next_protocol
Definition: decode-nsh.h:2
decode-nsh.h
decode-events.h
dtv
DecodeThreadVars * dtv
Definition: fuzz_decodepcapfile.c:33
SCNtohs
#define SCNtohs(x)
Definition: suricata-common.h:403
suricata-common.h
NSH_UNSUPPORTED_TYPE
@ NSH_UNSUPPORTED_TYPE
Definition: decode-events.h:216
version
uint8_t version
Definition: decode-gre.h:1
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:32
util-validate.h
PacketGetFromAlloc
Packet * PacketGetFromAlloc(void)
Get a malloced packet.
Definition: decode.c:173
NSH_NEXT_PROTO_NSH
#define NSH_NEXT_PROTO_NSH
Definition: decode-nsh.h:33
md_type
uint8_t md_type
Definition: decode-nsh.h:1
SCFree
#define SCFree(p)
Definition: util-mem.h:61
SCNtohl
#define SCNtohl(x)
Definition: suricata-common.h:402
DecodeThreadVars_
Structure to hold thread specific data for all decode modules.
Definition: decode.h:659
NSH_HEADER_TOO_SMALL
@ NSH_HEADER_TOO_SMALL
Definition: decode-events.h:212
NSH_NEXT_PROTO_IPV6
#define NSH_NEXT_PROTO_IPV6
Definition: decode-nsh.h:31
suricata.h
ENGINE_SET_INVALID_EVENT
#define ENGINE_SET_INVALID_EVENT(p, e)
Definition: decode.h:888
NSH_NEXT_PROTO_MPLS
#define NSH_NEXT_PROTO_MPLS
Definition: decode-nsh.h:34
spi_si
uint32_t spi_si
Definition: decode-nsh.h:3
DecodeIPV4
int DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
Definition: decode-ipv4.c:520
SCLogDebugEnabled
int SCLogDebugEnabled(void)
Returns whether debug messages are enabled to be logged or not.
Definition: util-debug.c:636
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:111
DecodeEthernet
int DecodeEthernet(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len)
Definition: decode-ethernet.c:42