Coverage Report

Created: 2026-05-16 07:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}