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  return DecodeIPV6(tv, dtv, p, pkt + length, len - length);
116  return DecodeEthernet(tv, dtv, p, pkt + length, len - length);
117  case NSH_NEXT_PROTO_MPLS:
118  return DecodeMPLS(tv, dtv, p, pkt + length, len - length);
119  case NSH_NEXT_PROTO_NSH:
120  default:
121  SCLogDebug("NSH next protocol %u not supported", next_protocol);
123  break;
124  }
125  return TM_ECODE_OK;
126 }
127 
128 #ifdef UNITTESTS
129 
130 static uint8_t valid_nsh_packet[] = { 0x00, 0x04, 0x02, 0x01, 0x00, 0x00, 0x02, 0x02, 0x45, 0x10,
131  0x00, 0x3c, 0x78, 0x8f, 0x40, 0x00, 0x3f, 0x06, 0x79, 0x05, 0x0b, 0x06, 0x06, 0x06, 0x33, 0x06,
132  0x06, 0x06, 0xbd, 0x2e, 0x00, 0x16, 0xc9, 0xee, 0x07, 0x62, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02,
133  0x16, 0xd0, 0x2f, 0x36, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0xa9, 0x5f,
134  0x7f, 0xed, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07 };
135 
136 static int DecodeNSHTestHeaderTooSmall(void)
137 {
138  ThreadVars tv;
140  Packet *p;
141 
142  p = PacketGetFromAlloc();
143  FAIL_IF_NULL(p);
144  memset(&dtv, 0, sizeof(DecodeThreadVars));
145  memset(&tv, 0, sizeof(ThreadVars));
146 
147  /* A packet that is too small to have a complete NSH header */
148  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, 7);
150 
151  PacketFree(p);
152  PASS;
153 }
154 
155 static int DecodeNSHTestUnsupportedVersion(void)
156 {
157  ThreadVars tv;
159  Packet *p;
160 
161  p = PacketGetFromAlloc();
162  FAIL_IF_NULL(p);
163  memset(&dtv, 0, sizeof(DecodeThreadVars));
164  memset(&tv, 0, sizeof(ThreadVars));
165 
166  /* Non-zero version field */
167  valid_nsh_packet[0] = 0xFF;
168  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet));
169  valid_nsh_packet[0] = 0x00;
171 
172  PacketFree(p);
173  PASS;
174 }
175 
176 static int DecodeNSHTestPacketTooSmall(void)
177 {
178  ThreadVars tv;
180  Packet *p;
181 
182  p = PacketGetFromAlloc();
183  FAIL_IF_NULL(p);
184  memset(&dtv, 0, sizeof(DecodeThreadVars));
185  memset(&tv, 0, sizeof(ThreadVars));
186 
187  /* A packet that has no payload */
188  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, 8);
190 
191  PacketFree(p);
192  PASS;
193 }
194 
195 static int DecodeNSHTestReservedType(void)
196 {
197  ThreadVars tv;
199  Packet *p;
200 
201  p = PacketGetFromAlloc();
202  FAIL_IF_NULL(p);
203  memset(&dtv, 0, sizeof(DecodeThreadVars));
204  memset(&tv, 0, sizeof(ThreadVars));
205 
206  /* Reserved type */
207  valid_nsh_packet[2] = 0x00;
208  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet));
209  valid_nsh_packet[2] = 0x02;
211 
212  PacketFree(p);
213  PASS;
214 }
215 
216 static int DecodeNSHTestInvalidType(void)
217 {
218  ThreadVars tv;
220  Packet *p;
221 
222  p = PacketGetFromAlloc();
223  FAIL_IF_NULL(p);
224  memset(&dtv, 0, sizeof(DecodeThreadVars));
225  memset(&tv, 0, sizeof(ThreadVars));
226 
227  /* Type length mismatch */
228  valid_nsh_packet[2] = 0x01;
229  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet));
230  valid_nsh_packet[2] = 0x02;
232  PacketFree(p);
233  PASS;
234 }
235 
236 static int DecodeNSHTestUnsupportedType(void)
237 {
238  ThreadVars tv;
240  Packet *p;
241 
242  p = PacketGetFromAlloc();
243  FAIL_IF_NULL(p);
244  memset(&dtv, 0, sizeof(DecodeThreadVars));
245  memset(&tv, 0, sizeof(ThreadVars));
246 
247  /* Unsupported type */
248  valid_nsh_packet[2] = 0x03;
249  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet));
250  valid_nsh_packet[2] = 0x02;
252 
253  PacketFree(p);
254  PASS;
255 }
256 
257 static int DecodeNSHTestUnknownPayload(void)
258 {
259  ThreadVars tv;
261  Packet *p;
262 
263  p = PacketGetFromAlloc();
264  FAIL_IF_NULL(p);
265  memset(&dtv, 0, sizeof(DecodeThreadVars));
266  memset(&tv, 0, sizeof(ThreadVars));
267 
268  /* Unknown type */
269  valid_nsh_packet[3] = 0x99;
270  DecodeNSH(&tv, &dtv, p, valid_nsh_packet, sizeof(valid_nsh_packet));
271  valid_nsh_packet[3] = 0x01;
273 
274  PacketFree(p);
275  PASS;
276 }
277 
278 #endif /* UNITTESTS */
279 
281 {
282 #ifdef UNITTESTS
283  UtRegisterTest("DecodeNSHTestHeaderTooSmall", DecodeNSHTestHeaderTooSmall);
284  UtRegisterTest("DecodeNSHTestUnsupportedVersion", DecodeNSHTestUnsupportedVersion);
285  UtRegisterTest("DecodeNSHTestPacketTooSmall", DecodeNSHTestPacketTooSmall);
286  UtRegisterTest("DecodeNSHTestReservedType", DecodeNSHTestReservedType);
287  UtRegisterTest("DecodeNSHTestInvalidType", DecodeNSHTestInvalidType);
288  UtRegisterTest("DecodeNSHTestUnsupportedType", DecodeNSHTestUnsupportedType);
289  UtRegisterTest("DecodeNSHTestUnknownPayload", DecodeNSHTestUnknownPayload);
290 #endif /* UNITTESTS */
291 }
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:46
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
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:280
NSH_NEXT_PROTO_IPV4
#define NSH_NEXT_PROTO_IPV4
Definition: decode-nsh.h:29
p
Packet * p
Definition: fuzz_iprep.c:21
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:82
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: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:58
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:164
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:49
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:439
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:438
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:121
SCLogDebugEnabled
int SCLogDebugEnabled(void)
Returns whether debug messages are enabled to be logged or not.
Definition: util-debug.c:768
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:42