1 /* Copyright (C) 2007-2013 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 James Riden <jamesr@europe.com>
29 *
30 * PPPOE Decoder
31 */
32
33 #include "suricata-common.h"
34
35 #include "packet-queue.h"
36
37 #include "decode.h"
38 #include "decode-ppp.h"
39 #include "decode-pppoe.h"
40 #include "decode-events.h"
41
42 #include "flow.h"
43
44 #include "util-unittest.h"
45 #include "util-debug.h"
46
47 /**
48 * \brief Main decoding function for PPPOE Discovery packets
49 */
DecodePPPOEDiscovery(ThreadVars * tv,DecodeThreadVars * dtv,Packet * p,const uint8_t * pkt,uint32_t len)50 int DecodePPPOEDiscovery(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
51 const uint8_t *pkt, uint32_t len)
52 {
53 StatsIncr(tv, dtv->counter_pppoe);
54
55 if (len < PPPOE_DISCOVERY_HEADER_MIN_LEN) {
56 ENGINE_SET_INVALID_EVENT(p, PPPOE_PKT_TOO_SMALL);
57 return TM_ECODE_FAILED;
58 }
59
60 p->pppoedh = (PPPOEDiscoveryHdr *)pkt;
61 if (p->pppoedh == NULL)
62 return TM_ECODE_FAILED;
63
64 /* parse the PPPOE code */
65 switch (p->pppoedh->pppoe_code)
66 {
67 case PPPOE_CODE_PADI:
68 break;
69 case PPPOE_CODE_PADO:
70 break;
71 case PPPOE_CODE_PADR:
72 break;
73 case PPPOE_CODE_PADS:
74 break;
75 case PPPOE_CODE_PADT:
76 break;
77 default:
78 SCLogDebug("unknown PPPOE code: 0x%0"PRIX8"", p->pppoedh->pppoe_code);
79 ENGINE_SET_INVALID_EVENT(p, PPPOE_WRONG_CODE);
80 return TM_ECODE_OK;
81 }
82
83 /* parse any tags we have in the packet */
84
85 uint32_t tag_length = 0;
86 PPPOEDiscoveryTag* pppoedt = (PPPOEDiscoveryTag*) (p->pppoedh + PPPOE_DISCOVERY_HEADER_MIN_LEN);
87
88 uint32_t pppoe_length = SCNtohs(p->pppoedh->pppoe_length);
89 uint32_t packet_length = len - PPPOE_DISCOVERY_HEADER_MIN_LEN ;
90
91 SCLogDebug("pppoe_length %"PRIu32", packet_length %"PRIu32"",
92 pppoe_length, packet_length);
93
94 if (pppoe_length > packet_length) {
95 SCLogDebug("malformed PPPOE tags");
96 ENGINE_SET_INVALID_EVENT(p, PPPOE_MALFORMED_TAGS);
97 return TM_ECODE_OK;
98 }
99
100 while (pppoedt < (PPPOEDiscoveryTag*) (pkt + (len - sizeof(PPPOEDiscoveryTag))) && pppoe_length >=4 && packet_length >=4)
101 {
102 #ifdef DEBUG
103 uint16_t tag_type = SCNtohs(pppoedt->pppoe_tag_type);
104 #endif
105 tag_length = SCNtohs(pppoedt->pppoe_tag_length);
106
107 SCLogDebug ("PPPoE Tag type %x, length %"PRIu32, tag_type, tag_length);
108
109 if (pppoe_length >= (4 + tag_length)) {
110 pppoe_length -= (4 + tag_length);
111 } else {
112 pppoe_length = 0; // don't want an underflow
113 }
114
115 if (packet_length >= 4 + tag_length) {
116 packet_length -= (4 + tag_length);
117 } else {
118 packet_length = 0; // don't want an underflow
119 }
120
121 pppoedt = pppoedt + (4 + tag_length);
122 }
123
124 return TM_ECODE_OK;
125 }
126
127 /**
128 * \brief Main decoding function for PPPOE Session packets
129 */
DecodePPPOESession(ThreadVars * tv,DecodeThreadVars * dtv,Packet * p,const uint8_t * pkt,uint32_t len)130 int DecodePPPOESession(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
131 const uint8_t *pkt, uint32_t len)
132 {
133 StatsIncr(tv, dtv->counter_pppoe);
134
135 if (len < PPPOE_SESSION_HEADER_LEN) {
136 ENGINE_SET_INVALID_EVENT(p, PPPOE_PKT_TOO_SMALL);
137 return TM_ECODE_FAILED;
138 }
139
140 p->pppoesh = (PPPOESessionHdr *)pkt;
141 if (p->pppoesh == NULL)
142 return TM_ECODE_FAILED;
143
144 SCLogDebug("PPPOE VERSION %" PRIu32 " TYPE %" PRIu32 " CODE %" PRIu32 " SESSIONID %" PRIu32 " LENGTH %" PRIu32 "",
145 PPPOE_SESSION_GET_VERSION(p->pppoesh), PPPOE_SESSION_GET_TYPE(p->pppoesh), p->pppoesh->pppoe_code, SCNtohs(p->pppoesh->session_id), SCNtohs(p->pppoesh->pppoe_length));
146
147 /* can't use DecodePPP() here because we only get a single 2-byte word to indicate protocol instead of the full PPP header */
148
149 if (SCNtohs(p->pppoesh->pppoe_length) > 0) {
150 /* decode contained PPP packet */
151
152 switch (SCNtohs(p->pppoesh->protocol))
153 {
154 case PPP_VJ_COMP:
155 case PPP_IPX:
156 case PPP_OSI:
157 case PPP_NS:
158 case PPP_DECNET:
159 case PPP_APPLE:
160 case PPP_BRPDU:
161 case PPP_STII:
162 case PPP_VINES:
163 case PPP_HELLO:
164 case PPP_LUXCOM:
165 case PPP_SNS:
166 case PPP_MPLS_UCAST:
167 case PPP_MPLS_MCAST:
168 case PPP_IPCP:
169 case PPP_OSICP:
170 case PPP_NSCP:
171 case PPP_DECNETCP:
172 case PPP_APPLECP:
173 case PPP_IPXCP:
174 case PPP_STIICP:
175 case PPP_VINESCP:
176 case PPP_IPV6CP:
177 case PPP_MPLSCP:
178 case PPP_LCP:
179 case PPP_PAP:
180 case PPP_LQM:
181 case PPP_CHAP:
182 ENGINE_SET_EVENT(p,PPP_UNSUP_PROTO);
183 break;
184
185 case PPP_VJ_UCOMP:
186
187 if(len < (PPPOE_SESSION_HEADER_LEN + IPV4_HEADER_LEN)) {
188 ENGINE_SET_INVALID_EVENT(p, PPPVJU_PKT_TOO_SMALL);
189 return TM_ECODE_OK;
190 }
191 if (unlikely(len > PPPOE_SESSION_HEADER_LEN + USHRT_MAX)) {
192 return TM_ECODE_FAILED;
193 }
194
195 if(IPV4_GET_RAW_VER((IPV4Hdr *)(pkt + PPPOE_SESSION_HEADER_LEN)) == 4) {
196 DecodeIPV4(tv, dtv, p, pkt + PPPOE_SESSION_HEADER_LEN, len - PPPOE_SESSION_HEADER_LEN);
197 }
198 break;
199
200 case PPP_IP:
201 if(len < (PPPOE_SESSION_HEADER_LEN + IPV4_HEADER_LEN)) {
202 ENGINE_SET_INVALID_EVENT(p, PPPIPV4_PKT_TOO_SMALL);
203 return TM_ECODE_OK;
204 }
205 if (unlikely(len > PPPOE_SESSION_HEADER_LEN + USHRT_MAX)) {
206 return TM_ECODE_FAILED;
207 }
208
209 DecodeIPV4(tv, dtv, p, pkt + PPPOE_SESSION_HEADER_LEN, len - PPPOE_SESSION_HEADER_LEN);
210 break;
211
212 /* PPP IPv6 was not tested */
213 case PPP_IPV6:
214 if(len < (PPPOE_SESSION_HEADER_LEN + IPV6_HEADER_LEN)) {
215 ENGINE_SET_INVALID_EVENT(p, PPPIPV6_PKT_TOO_SMALL);
216 return TM_ECODE_OK;
217 }
218 if (unlikely(len > PPPOE_SESSION_HEADER_LEN + USHRT_MAX)) {
219 return TM_ECODE_FAILED;
220 }
221
222 DecodeIPV6(tv, dtv, p, pkt + PPPOE_SESSION_HEADER_LEN, len - PPPOE_SESSION_HEADER_LEN);
223 break;
224
225 default:
226 SCLogDebug("unknown PPP protocol: %" PRIx32 "",SCNtohs(p->pppoesh->protocol));
227 ENGINE_SET_INVALID_EVENT(p, PPP_WRONG_TYPE);
228 return TM_ECODE_OK;
229 }
230 }
231 return TM_ECODE_OK;
232 }
233
234 #ifdef UNITTESTS
235 /** DecodePPPOEtest01
236 * \brief Decode malformed PPPOE packet (too short)
237 * \retval 1 Expected test value
238 */
DecodePPPOEtest01(void)239 static int DecodePPPOEtest01 (void)
240 {
241
242 uint8_t raw_pppoe[] = { 0x11, 0x00, 0x00, 0x00, 0x00 };
243 Packet *p = PacketGetFromAlloc();
244 if (unlikely(p == NULL))
245 return 0;
246 ThreadVars tv;
247 DecodeThreadVars dtv;
248
249 memset(&tv, 0, sizeof(ThreadVars));
250 memset(&dtv, 0, sizeof(DecodeThreadVars));
251
252 DecodePPPOESession(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe));
253
254 if (ENGINE_ISSET_EVENT(p,PPPOE_PKT_TOO_SMALL)) {
255 SCFree(p);
256 return 1;
257 }
258
259 SCFree(p);
260 return 0;
261 }
262
263 /** DecodePPPOEtest02
264 * \brief Valid PPPOE packet - check the invalid ICMP type encapsulated is flagged
265 * \retval 0 Expected test value
266 */
DecodePPPOEtest02(void)267 static int DecodePPPOEtest02 (void)
268 {
269
270 uint8_t raw_pppoe[] = {
271 0x11, 0x00, 0x00, 0x01, 0x00, 0x40, 0x00, 0x21,
272 0x45, 0x00, 0x00, 0x3c, 0x05, 0x5c, 0x00, 0x00,
273 0x20, 0x01, 0xff, 0x30, 0xc0, 0xa8, 0x0a, 0x7f,
274 0xc0, 0xa8, 0x0a, 0x65, 0xab, 0xcd, 0x16, 0x5e,
275 0x02, 0x00, 0x37, 0x00, 0x41, 0x42, 0x43, 0x44,
276 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
277 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54,
278 0x55, 0x56, 0x57, 0x41, 0x42, 0x43, 0x44, 0x45,
279 0x46, 0x47, 0x48, 0x49 };
280
281 Packet *p = PacketGetFromAlloc();
282 if (unlikely(p == NULL))
283 return 0;
284 ThreadVars tv;
285 DecodeThreadVars dtv;
286 int ret = 0;
287
288 memset(&tv, 0, sizeof(ThreadVars));
289 memset(&dtv, 0, sizeof(DecodeThreadVars));
290
291 FlowInitConfig(FLOW_QUIET);
292
293 DecodePPPOESession(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe));
294
295 if(ENGINE_ISSET_EVENT(p,PPPOE_PKT_TOO_SMALL)) {
296 goto end;
297 }
298
299 // and we insist that the invalid ICMP encapsulated (type 0xab, code 0xcd) is flagged
300
301 if(! ENGINE_ISSET_EVENT(p,ICMPV4_UNKNOWN_TYPE)) {
302 goto end;
303 }
304
305 ret = 1;
306 end:
307 FlowShutdown();
308 SCFree(p);
309 return ret;
310 }
311
312
313 /** DecodePPPOEtest03
314 * \brief Valid example PADO packet PPPOE packet taken from RFC2516
315 * \retval 0 Expected test value
316 */
DecodePPPOEtest03(void)317 static int DecodePPPOEtest03 (void)
318 {
319
320 /* example PADO packet taken from RFC2516 */
321 uint8_t raw_pppoe[] = {
322 0x11, 0x07, 0x00, 0x00, 0x00, 0x20, 0x01, 0x01,
323 0x00, 0x00, 0x01, 0x02, 0x00, 0x18, 0x47, 0x6f,
324 0x20, 0x52, 0x65, 0x64, 0x42, 0x61, 0x63, 0x6b,
325 0x20, 0x2d, 0x20, 0x65, 0x73, 0x68, 0x73, 0x68,
326 0x65, 0x73, 0x68, 0x6f, 0x6f, 0x74
327 };
328
329 Packet *p = PacketGetFromAlloc();
330 if (unlikely(p == NULL))
331 return 0;
332 ThreadVars tv;
333 DecodeThreadVars dtv;
334
335 memset(&tv, 0, sizeof(ThreadVars));
336 memset(&dtv, 0, sizeof(DecodeThreadVars));
337
338 DecodePPPOEDiscovery(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe));
339 if (p->pppoedh == NULL) {
340 SCFree(p);
341 return 0;
342 }
343
344 SCFree(p);
345 return 1;
346 }
347
348 /** DecodePPPOEtest04
349 * \brief Valid example PPPOE packet taken from RFC2516 - but with wrong PPPOE code
350 * \retval 1 Expected test value
351 */
DecodePPPOEtest04(void)352 static int DecodePPPOEtest04 (void)
353 {
354
355 /* example PADI packet taken from RFC2516, but with wrong code */
356 uint8_t raw_pppoe[] = {
357 0x11, 0xbb, 0x00, 0x00, 0x00, 0x04, 0x01, 0x01,
358 0x00, 0x00
359 };
360
361 Packet *p = PacketGetFromAlloc();
362 if (unlikely(p == NULL))
363 return 0;
364 ThreadVars tv;
365 DecodeThreadVars dtv;
366
367 memset(&tv, 0, sizeof(ThreadVars));
368 memset(&dtv, 0, sizeof(DecodeThreadVars));
369
370 DecodePPPOEDiscovery(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe));
371
372 if(ENGINE_ISSET_EVENT(p,PPPOE_WRONG_CODE)) {
373 SCFree(p);
374 return 1;
375 }
376
377 SCFree(p);
378 return 0;
379 }
380
381 /** DecodePPPOEtest05
382 * \brief Valid exaple PADO PPPOE packet taken from RFC2516, but too short for given length
383 * \retval 0 Expected test value
384 */
DecodePPPOEtest05(void)385 static int DecodePPPOEtest05 (void)
386 {
387
388 /* example PADI packet taken from RFC2516 */
389 uint8_t raw_pppoe[] = {
390 0x11, 0x07, 0x00, 0x00, 0x00, 0x20, 0x01, 0x01,
391 0x00, 0x00, 0x01, 0x02, 0x00, 0x18, 0x47, 0x6f,
392 0x20, 0x52, 0x65, 0x64, 0x42, 0x61, 0x63, 0x6b,
393 0x20, 0x2d, 0x20, 0x65, 0x73, 0x68, 0x73, 0x68
394 };
395
396 Packet *p = PacketGetFromAlloc();
397 if (unlikely(p == NULL))
398 return 0;
399 ThreadVars tv;
400 DecodeThreadVars dtv;
401
402 memset(&tv, 0, sizeof(ThreadVars));
403 memset(&dtv, 0, sizeof(DecodeThreadVars));
404
405 DecodePPPOEDiscovery(&tv, &dtv, p, raw_pppoe, sizeof(raw_pppoe));
406
407 if(ENGINE_ISSET_EVENT(p,PPPOE_MALFORMED_TAGS)) {
408 SCFree(p);
409 return 1;
410 }
411
412 SCFree(p);
413 return 0;
414 }
415
416 /** DecodePPPOEtest06
417 * \brief Check that the macros work as expected. Type and version are
418 * fields of 4 bits length. So they are sharing the same var and the macros
419 * should extract the first 4 bits for version and the second 4 bits for type
420 * \retval 1 Expected test value
421 */
DecodePPPOEtest06(void)422 static int DecodePPPOEtest06 (void)
423 {
424
425 PPPOESessionHdr pppoesh;
426 PPPOEDiscoveryHdr pppoedh;
427 pppoesh.pppoe_version_type = 0xAB;
428 pppoedh.pppoe_version_type = 0xCD;
429
430 if (PPPOE_SESSION_GET_VERSION(&pppoesh) != 0x0A) {
431 printf("Error, PPPOE macro pppoe_session_get_version failed: ");
432 return 0;
433 }
434 if (PPPOE_SESSION_GET_TYPE(&pppoesh) != 0x0B) {
435 printf("Error, PPPOE macro pppoe_session_get_type failed: ");
436 return 0;
437 }
438 if (PPPOE_DISCOVERY_GET_VERSION(&pppoedh) != 0x0C) {
439 printf("Error, PPPOE macro pppoe_discovery_get_version failed: ");
440 return 0;
441 }
442 if (PPPOE_DISCOVERY_GET_TYPE(&pppoedh) != 0x0D) {
443 printf("Error, PPPOE macro pppoe_discovery_get_type failed: ");
444 return 0;
445 }
446
447 return 1;
448 }
449 #endif /* UNITTESTS */
450
451
452
453 /**
454 * \brief Registers PPPOE unit tests
455 * \todo More PPPOE tests
456 */
DecodePPPOERegisterTests(void)457 void DecodePPPOERegisterTests(void)
458 {
459 #ifdef UNITTESTS
460 UtRegisterTest("DecodePPPOEtest01", DecodePPPOEtest01);
461 UtRegisterTest("DecodePPPOEtest02", DecodePPPOEtest02);
462 UtRegisterTest("DecodePPPOEtest03", DecodePPPOEtest03);
463 UtRegisterTest("DecodePPPOEtest04", DecodePPPOEtest04);
464 UtRegisterTest("DecodePPPOEtest05", DecodePPPOEtest05);
465 UtRegisterTest("DecodePPPOEtest06", DecodePPPOEtest06);
466 #endif /* UNITTESTS */
467 }
468
469 /**
470 * @}
471 */
472