suricata
decode-vlan.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2022 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 /**
26  * \file
27  *
28  * \author Breno Silva <breno.silva@gmail.com>
29  *
30  * Decode 802.1q
31  */
32 
33 #include "suricata-common.h"
34 #include "decode.h"
35 #include "decode-vlan.h"
36 #include "decode-events.h"
37 
38 #include "util-validate.h"
39 #include "util-debug.h"
40 
41 /**
42  * \internal
43  * \brief this function is used to decode IEEE802.1q packets
44  *
45  * \param tv pointer to the thread vars
46  * \param dtv pointer code thread vars
47  * \param p pointer to the packet struct
48  * \param pkt pointer to the raw packet
49  * \param len packet len
50  * \param pq pointer to the packet queue
51  *
52  */
54  const uint8_t *pkt, uint32_t len)
55 {
56  DEBUG_VALIDATE_BUG_ON(pkt == NULL);
57 
58  uint16_t proto;
59 
60  if (p->vlan_idx == 0)
62  else if (p->vlan_idx == 1)
64  else if (p->vlan_idx == 2)
66 
67  if(len < VLAN_HEADER_LEN) {
69  return TM_ECODE_FAILED;
70  }
71  if (!PacketIncreaseCheckLayers(p)) {
72  return TM_ECODE_FAILED;
73  }
74  if (p->vlan_idx > VLAN_MAX_LAYER_IDX) {
76  return TM_ECODE_FAILED;
77  }
78 
79  VLANHdr *vlan_hdr = (VLANHdr *)pkt;
80 
81  proto = GET_VLAN_PROTO(vlan_hdr);
82 
83  SCLogDebug("p %p pkt %p VLAN protocol %04x VLAN PRI %d VLAN CFI %d VLAN ID %d Len: %" PRIu32 "",
84  p, pkt, proto, GET_VLAN_PRIORITY(vlan_hdr), GET_VLAN_CFI(vlan_hdr),
85  GET_VLAN_ID(vlan_hdr), len);
86 
87  p->vlan_id[p->vlan_idx++] = (uint16_t)GET_VLAN_ID(vlan_hdr);
88 
89  if (!DecodeNetworkLayer(tv, dtv, proto, p, pkt + VLAN_HEADER_LEN, len - VLAN_HEADER_LEN)) {
91  return TM_ECODE_FAILED;
92  }
93  return TM_ECODE_OK;
94 }
95 
96 typedef struct IEEE8021ahHdr_ {
97  uint32_t flags;
98  uint8_t c_destination[6];
99  uint8_t c_source[6];
100  uint16_t type; /**< next protocol */
101 } __attribute__((__packed__)) IEEE8021ahHdr;
102 
103 #define IEEE8021AH_HEADER_LEN sizeof(IEEE8021ahHdr)
104 
106  const uint8_t *pkt, uint32_t len)
107 {
108  DEBUG_VALIDATE_BUG_ON(pkt == NULL);
109 
111 
112  if (len < IEEE8021AH_HEADER_LEN) {
114  return TM_ECODE_FAILED;
115  }
116 
117  if (!PacketIncreaseCheckLayers(p)) {
118  return TM_ECODE_FAILED;
119  }
120  IEEE8021ahHdr *hdr = (IEEE8021ahHdr *)pkt;
121  const uint16_t next_proto = SCNtohs(hdr->type);
122 
123  DecodeNetworkLayer(tv, dtv, next_proto, p,
125 
126  return TM_ECODE_OK;
127 }
128 
129 #ifdef UNITTESTS
130 #include "util-unittest-helper.h"
131 #include "packet.h"
132 #include "flow-hash.h"
133 #include "tmqh-packetpool.h"
134 
135 /** \todo Must GRE+VLAN and Multi-Vlan packets to
136  * create more tests
137  */
138 
139 /**
140  * \test DecodeVLANTest01 test if vlan header is too small.
141  *
142  * \retval 1 on success
143  * \retval 0 on failure
144  */
145 static int DecodeVLANtest01 (void)
146 {
147  uint8_t raw_vlan[] = { 0x00, 0x20, 0x08 };
149  FAIL_IF_NULL(p);
150  ThreadVars tv;
152  memset(&tv, 0, sizeof(ThreadVars));
153  memset(&dtv, 0, sizeof(DecodeThreadVars));
154 
155  DecodeVLAN(&tv, &dtv, p, raw_vlan, sizeof(raw_vlan));
157 
158  PacketFree(p);
159  PASS;
160 }
161 
162 /**
163  * \test DecodeVLANTest02 test if vlan header has unknown type.
164  *
165  * \retval 1 on success
166  * \retval 0 on failure
167  */
168 static int DecodeVLANtest02 (void)
169 {
170  uint8_t raw_vlan[] = {
171  0x00, 0x20, 0x01, 0x00, 0x45, 0x00, 0x00, 0x34,
172  0x3b, 0x36, 0x40, 0x00, 0x40, 0x06, 0xb7, 0xc9,
173  0x83, 0x97, 0x20, 0x81, 0x83, 0x97, 0x20, 0x15,
174  0x04, 0x8a, 0x17, 0x70, 0x4e, 0x14, 0xdf, 0x55,
175  0x4d, 0x3d, 0x5a, 0x61, 0x80, 0x10, 0x6b, 0x50,
176  0x3c, 0x4c, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a,
177  0x00, 0x04, 0xf0, 0xc8, 0x01, 0x99, 0xa3, 0xf3};
179  FAIL_IF_NULL(p);
180  ThreadVars tv;
182  memset(&tv, 0, sizeof(ThreadVars));
183  memset(&dtv, 0, sizeof(DecodeThreadVars));
184 
185  DecodeVLAN(&tv, &dtv, p, raw_vlan, sizeof(raw_vlan));
186 
188  PacketFree(p);
189  PASS;
190 }
191 
192 /**
193  * \test DecodeVLANTest02 test a good vlan header.
194  *
195  * \retval 1 on success
196  * \retval 0 on failure
197  */
198 static int DecodeVLANtest03 (void)
199 {
200  uint8_t raw_vlan[] = {
201  0x00, 0x20, 0x08, 0x00, 0x45, 0x00, 0x00, 0x34,
202  0x3b, 0x36, 0x40, 0x00, 0x40, 0x06, 0xb7, 0xc9,
203  0x83, 0x97, 0x20, 0x81, 0x83, 0x97, 0x20, 0x15,
204  0x04, 0x8a, 0x17, 0x70, 0x4e, 0x14, 0xdf, 0x55,
205  0x4d, 0x3d, 0x5a, 0x61, 0x80, 0x10, 0x6b, 0x50,
206  0x3c, 0x4c, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a,
207  0x00, 0x04, 0xf0, 0xc8, 0x01, 0x99, 0xa3, 0xf3};
209  FAIL_IF_NULL(p);
210  ThreadVars tv;
212  memset(&tv, 0, sizeof(ThreadVars));
213  memset(&dtv, 0, sizeof(DecodeThreadVars));
214 
216 
217  DecodeVLAN(&tv, &dtv, p, raw_vlan, sizeof(raw_vlan));
218 
219  FAIL_IF(p->vlan_id[0] == 0);
222 
223  PacketFree(p);
224  FlowShutdown();
225  PASS;
226 }
227 
228 /**
229  * \test DecodeVLANtest04 test that the packet pool recycle path clears
230  * every VLAN id slot a decoder can use.
231  *
232  * DecodeVLAN() fills vlan_id[] up to VLAN_MAX_LAYERS, so a triple tagged
233  * (QinQinQ) frame sets all of them. The slots are handed to the next
234  * occupant of the pool slot unless PacketReinit() clears them all: the
235  * flow hash key (FlowHashIp4Fill()) and the flow comparison
236  * (CmpVlanIds()) fold in every slot regardless of vlan_idx, so a single
237  * stale slot makes the packets of one and the same stream hash into two
238  * different Flow objects, which splits reassembly over those two flows.
239  *
240  * \retval 1 on success
241  * \retval 0 on failure
242  */
243 static int DecodeVLANtest04(void)
244 {
245  /* QinQinQ: 3 vlan header layers, ids 32, 257 and 3843. */
246  uint8_t raw_qinqinq[] = { 0x00, 0x20, 0x81, 0x00, 0x01, 0x01, 0x81, 0x00, 0x0f, 0x03, 0x08,
247  0x00, 0x45, 0x00, 0x00, 0x34, 0x3b, 0x36, 0x40, 0x00, 0x40, 0x06, 0xb7, 0xc9, 0x83, 0x97,
248  0x20, 0x81, 0x83, 0x97, 0x20, 0x15, 0x04, 0x8a, 0x17, 0x70, 0x4e, 0x14, 0xdf, 0x55, 0x4d,
249  0x3d, 0x5a, 0x61, 0x80, 0x10, 0x6b, 0x50, 0x3c, 0x4c, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a,
250  0x00, 0x04, 0xf0, 0xc8, 0x01, 0x99, 0xa3, 0xf3 };
251  /* QinQ: 2 vlan header layers, ids 32 and 257, same inner packet. */
252  uint8_t raw_qinq[] = { 0x00, 0x20, 0x81, 0x00, 0x01, 0x01, 0x08, 0x00, 0x45, 0x00, 0x00, 0x34,
253  0x3b, 0x36, 0x40, 0x00, 0x40, 0x06, 0xb7, 0xc9, 0x83, 0x97, 0x20, 0x81, 0x83, 0x97, 0x20,
254  0x15, 0x04, 0x8a, 0x17, 0x70, 0x4e, 0x14, 0xdf, 0x55, 0x4d, 0x3d, 0x5a, 0x61, 0x80, 0x10,
255  0x6b, 0x50, 0x3c, 0x4c, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x00, 0x04, 0xf0, 0xc8, 0x01,
256  0x99, 0xa3, 0xf3 };
257 
259  FAIL_IF_NULL(p);
260  ThreadVars tv;
262  memset(&tv, 0, sizeof(ThreadVars));
263  memset(&dtv, 0, sizeof(DecodeThreadVars));
264 
265  FAIL_IF_NOT(TM_ECODE_OK == DecodeVLAN(&tv, &dtv, p, raw_qinqinq, sizeof(raw_qinqinq)));
267  for (unsigned i = 0; i < VLAN_MAX_LAYERS; i++) {
268  FAIL_IF(p->vlan_id[i] == 0);
269  }
270 
271  /* Hand the packet back to the pool it came from and take a packet out
272  * again: the very same slot is recycled. */
274  Packet *recycled = PacketPoolGetPacket();
275  FAIL_IF_NULL(recycled);
276  FAIL_IF(recycled != p);
277  FAIL_IF(recycled->vlan_idx != 0);
278  for (unsigned i = 0; i < VLAN_MAX_LAYERS; i++) {
279  FAIL_IF(recycled->vlan_id[i] != 0);
280  }
281 
282  /* And now the part that decides behaviour: decoding a double tagged
283  * frame into the recycled packet must key exactly like decoding it
284  * into a packet that never saw the QinQinQ one. */
285  FAIL_IF_NOT(TM_ECODE_OK == DecodeVLAN(&tv, &dtv, recycled, raw_qinq, sizeof(raw_qinq)));
286  FAIL_IF(recycled->vlan_idx != 2);
287  FAIL_IF(recycled->vlan_id[VLAN_MAX_LAYER_IDX] != 0);
288  const uint32_t recycled_hash = FlowGetIpPairProtoHash(recycled);
289 
290  Packet *fresh = PacketGetFromAlloc();
291  FAIL_IF_NULL(fresh);
292  FAIL_IF_NOT(TM_ECODE_OK == DecodeVLAN(&tv, &dtv, fresh, raw_qinq, sizeof(raw_qinq)));
293  const uint32_t fresh_hash = FlowGetIpPairProtoHash(fresh);
294 
295  FAIL_IF(recycled_hash != fresh_hash);
296 
297  PacketFree(fresh);
298  PacketPoolReturnPacket(recycled);
299  PASS;
300 }
301 #endif /* UNITTESTS */
302 
304 {
305 #ifdef UNITTESTS
306  UtRegisterTest("DecodeVLANtest01", DecodeVLANtest01);
307  UtRegisterTest("DecodeVLANtest02", DecodeVLANtest02);
308  UtRegisterTest("DecodeVLANtest03", DecodeVLANtest03);
309  UtRegisterTest("DecodeVLANtest04", DecodeVLANtest04);
310 #endif /* UNITTESTS */
311 }
312 
313 /**
314  * @}
315  */
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
DecodeThreadVars_::counter_vlan_qinqinq
StatsCounterId counter_vlan_qinqinq
Definition: decode.h:1041
PacketPoolReturnPacket
void PacketPoolReturnPacket(Packet *p)
Return packet to Packet pool.
Definition: tmqh-packetpool.c:168
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
VLAN_UNKNOWN_TYPE
@ VLAN_UNKNOWN_TYPE
Definition: decode-events.h:154
Packet_::vlan_idx
uint8_t vlan_idx
Definition: decode.h:544
flow-hash.h
GET_VLAN_CFI
#define GET_VLAN_CFI(vlanh)
Definition: decode-vlan.h:35
IEEE8021AH_HEADER_LEN
#define IEEE8021AH_HEADER_LEN
VLAN_HEADER_TOO_MANY_LAYERS
@ VLAN_HEADER_TOO_MANY_LAYERS
Definition: decode-events.h:155
p
Packet * p
Definition: fuzz_dataset.c:30
proto
uint8_t proto
Definition: decode-template.h:0
IEEE8021ahHdr_
Definition: decode-vlan.c:96
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:82
tmqh-packetpool.h
util-unittest-helper.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
__attribute__
struct IEEE8021ahHdr_ __attribute__((__packed__))
DNP3 link header.
Definition: decode-vlan.c:101
FlowInitConfig
void FlowInitConfig(bool quiet)
initialize the configuration
Definition: flow.c:574
decode.h
util-debug.h
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
GET_VLAN_PRIORITY
#define GET_VLAN_PRIORITY(vlanh)
Definition: decode-vlan.h:34
VLAN_HEADER_LEN
#define VLAN_HEADER_LEN
Definition: decode-vlan.h:46
IEEE8021ahHdr_::type
uint16_t type
Definition: decode-vlan.c:100
IEEE8021ahHdr_::flags
uint32_t flags
Definition: decode-vlan.c:97
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:57
GET_VLAN_PROTO
#define GET_VLAN_PROTO(vlanh)
Definition: decode-vlan.h:37
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
IEEE8021AH_HEADER_TOO_SMALL
@ IEEE8021AH_HEADER_TOO_SMALL
Definition: decode-events.h:157
DecodeThreadVars_::counter_ieee8021ah
StatsCounterId counter_ieee8021ah
Definition: decode.h:1045
Packet_
Definition: decode.h:516
decode-events.h
dtv
DecodeThreadVars * dtv
Definition: fuzz_decodepcapfile.c:35
decode-vlan.h
FlowGetIpPairProtoHash
uint32_t FlowGetIpPairProtoHash(const Packet *p)
Definition: flow-hash.c:151
DecodeVLANRegisterTests
void DecodeVLANRegisterTests(void)
Definition: decode-vlan.c:303
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
SCNtohs
#define SCNtohs(x)
Definition: suricata-common.h:436
suricata-common.h
FlowShutdown
void FlowShutdown(void)
shutdown the flow engine
Definition: flow.c:718
packet.h
VLAN_MAX_LAYERS
#define VLAN_MAX_LAYERS
Definition: decode-vlan.h:51
VLAN_HEADER_TOO_SMALL
@ VLAN_HEADER_TOO_SMALL
Definition: decode-events.h:153
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:34
DecodeThreadVars_::counter_vlan_qinq
StatsCounterId counter_vlan_qinq
Definition: decode.h:1040
util-validate.h
PacketGetFromAlloc
Packet * PacketGetFromAlloc(void)
Get a malloced packet.
Definition: decode.c:260
DecodeThreadVars_
Structure to hold thread specific data for all decode modules.
Definition: decode.h:995
PacketPoolGetPacket
Packet * PacketPoolGetPacket(void)
Get a new packet from the packet pool.
Definition: tmqh-packetpool.c:118
VLAN_MAX_LAYER_IDX
#define VLAN_MAX_LAYER_IDX
Definition: decode-vlan.h:52
IEEE8021ahHdr_::c_source
uint8_t c_source[6]
Definition: decode-vlan.c:99
ENGINE_SET_INVALID_EVENT
#define ENGINE_SET_INVALID_EVENT(p, e)
Definition: decode.h:1239
FLOW_QUIET
#define FLOW_QUIET
Definition: flow.h:43
GET_VLAN_ID
#define GET_VLAN_ID(vlanh)
Definition: decode-vlan.h:36
Packet_::vlan_id
uint16_t vlan_id[VLAN_MAX_LAYERS]
Definition: decode.h:543
DecodeIEEE8021ah
int DecodeIEEE8021ah(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t)
DecodeThreadVars_::counter_vlan
StatsCounterId counter_vlan
Definition: decode.h:1039
ThreadVars_::stats
StatsThreadContext stats
Definition: threadvars.h:120
DecodeVLAN
int DecodeVLAN(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len)
Definition: decode-vlan.c:53
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:109
IEEE8021ahHdr_::c_destination
uint8_t c_destination[6]
Definition: decode-vlan.c:98