]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
decode/ppp: support different header formats
authorVictor Julien <vjulien@oisf.net>
Fri, 12 Apr 2024 10:42:37 +0000 (12:42 +0200)
committerVictor Julien <victor@inliniac.net>
Mon, 15 Apr 2024 18:34:38 +0000 (20:34 +0200)
Support compressed proto and optional HDLC header.

Bug: #6942.

src/decode-ppp.c

index 5bf682bd2fc6e4fbcec7fa13a3059817f3efd866..6507bc0a6ee9618c8ba97f278abfc0cc1b106d98 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2021 Open Information Security Foundation
+/* Copyright (C) 2007-2024 Open Information Security Foundation
  *
  * You can copy, redistribute or modify this Program under the terms of
  * the GNU General Public License version 2 as published by the Free
 #include "util-unittest.h"
 #include "util-debug.h"
 
-int DecodePPP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
-        const uint8_t *pkt, uint32_t len)
+static int DecodePPPCompressedProto(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
+        const uint8_t *pkt, uint32_t len, uint16_t proto_offset)
 {
-    DEBUG_VALIDATE_BUG_ON(pkt == NULL);
-
-    StatsIncr(tv, dtv->counter_ppp);
+    const uint32_t data_offset = proto_offset + 1;
+    switch (*(pkt + proto_offset)) {
+        case 0x21: { /* PPP_IP */
+            if (unlikely(len < (data_offset + IPV4_HEADER_LEN))) {
+                ENGINE_SET_INVALID_EVENT(p, PPPVJU_PKT_TOO_SMALL);
+                return TM_ECODE_FAILED;
+            }
+            DEBUG_VALIDATE_BUG_ON(len < data_offset);
+            uint16_t iplen = MIN(USHRT_MAX, (uint16_t)(len - data_offset));
+            return DecodeIPV4(tv, dtv, p, pkt + data_offset, iplen);
+        }
+        case 0x57: { /* PPP_IPV6 */
+            if (unlikely(len < (data_offset + IPV6_HEADER_LEN))) {
+                ENGINE_SET_INVALID_EVENT(p, PPPIPV6_PKT_TOO_SMALL);
+                return TM_ECODE_FAILED;
+            }
+            DEBUG_VALIDATE_BUG_ON(len < data_offset);
+            uint16_t iplen = MIN(USHRT_MAX, (uint16_t)(len - data_offset));
+            return DecodeIPV6(tv, dtv, p, pkt + data_offset, iplen);
+        }
+        case 0x2f: /* PPP_VJ_UCOMP */
+            if (unlikely(len < (data_offset + IPV4_HEADER_LEN))) {
+                ENGINE_SET_INVALID_EVENT(p, PPPVJU_PKT_TOO_SMALL);
+                return TM_ECODE_FAILED;
+            }
 
-    if (unlikely(len < PPP_HEADER_LEN)) {
-        ENGINE_SET_INVALID_EVENT(p, PPP_PKT_TOO_SMALL);
-        return TM_ECODE_FAILED;
-    }
-    if (!PacketIncreaseCheckLayers(p)) {
-        return TM_ECODE_FAILED;
-    }
+            if (unlikely(len > data_offset + USHRT_MAX)) {
+                return TM_ECODE_FAILED;
+            }
 
-    p->ppph = (PPPHdr *)pkt;
+            if (likely(IPV4_GET_RAW_VER((IPV4Hdr *)(pkt + data_offset)) == 4)) {
+                return DecodeIPV4(tv, dtv, p, pkt + data_offset, (uint16_t)(len - data_offset));
+            } else
+                return TM_ECODE_FAILED;
+            break;
 
-    SCLogDebug("p %p pkt %p PPP protocol %04x Len: %" PRIu32 "",
-        p, pkt, SCNtohs(p->ppph->protocol), len);
+        default:
+            ENGINE_SET_EVENT(p, PPP_UNSUP_PROTO);
+            return TM_ECODE_OK;
+    }
+}
 
-    switch (SCNtohs(p->ppph->protocol))
-    {
+static int DecodePPPUncompressedProto(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
+        const uint8_t *pkt, uint32_t len, const uint16_t proto, const uint32_t data_offset)
+{
+    switch (proto) {
         case PPP_VJ_UCOMP:
-            if (unlikely(len < (PPP_HEADER_LEN + IPV4_HEADER_LEN))) {
-                ENGINE_SET_INVALID_EVENT(p,PPPVJU_PKT_TOO_SMALL);
-                p->ppph = NULL;
+            if (unlikely(len < (data_offset + IPV4_HEADER_LEN))) {
+                ENGINE_SET_INVALID_EVENT(p, PPPVJU_PKT_TOO_SMALL);
                 return TM_ECODE_FAILED;
             }
 
-            if (unlikely(len > PPP_HEADER_LEN + USHRT_MAX)) {
+            if (unlikely(len > data_offset + USHRT_MAX)) {
                 return TM_ECODE_FAILED;
             }
 
-            if (likely(IPV4_GET_RAW_VER((IPV4Hdr *)(pkt + PPP_HEADER_LEN)) == 4)) {
-                return DecodeIPV4(
-                        tv, dtv, p, pkt + PPP_HEADER_LEN, (uint16_t)(len - PPP_HEADER_LEN));
+            if (likely(IPV4_GET_RAW_VER((IPV4Hdr *)(pkt + data_offset)) == 4)) {
+                return DecodeIPV4(tv, dtv, p, pkt + data_offset, (uint16_t)(len - data_offset));
             } else
                 return TM_ECODE_FAILED;
             break;
 
         case PPP_IP:
-            if (unlikely(len < (PPP_HEADER_LEN + IPV4_HEADER_LEN))) {
-                ENGINE_SET_INVALID_EVENT(p,PPPIPV4_PKT_TOO_SMALL);
-                p->ppph = NULL;
+            if (unlikely(len < (data_offset + IPV4_HEADER_LEN))) {
+                ENGINE_SET_INVALID_EVENT(p, PPPIPV4_PKT_TOO_SMALL);
                 return TM_ECODE_FAILED;
             }
-            if (unlikely(len > PPP_HEADER_LEN + USHRT_MAX)) {
+            if (unlikely(len > data_offset + USHRT_MAX)) {
                 return TM_ECODE_FAILED;
             }
 
-            return DecodeIPV4(tv, dtv, p, pkt + PPP_HEADER_LEN, (uint16_t)(len - PPP_HEADER_LEN));
+            return DecodeIPV4(tv, dtv, p, pkt + data_offset, (uint16_t)(len - data_offset));
 
             /* PPP IPv6 was not tested */
         case PPP_IPV6:
-            if (unlikely(len < (PPP_HEADER_LEN + IPV6_HEADER_LEN))) {
-                ENGINE_SET_INVALID_EVENT(p,PPPIPV6_PKT_TOO_SMALL);
-                p->ppph = NULL;
+            if (unlikely(len < (data_offset + IPV6_HEADER_LEN))) {
+                ENGINE_SET_INVALID_EVENT(p, PPPIPV6_PKT_TOO_SMALL);
                 return TM_ECODE_FAILED;
             }
-            if (unlikely(len > PPP_HEADER_LEN + USHRT_MAX)) {
+            if (unlikely(len > data_offset + USHRT_MAX)) {
                 return TM_ECODE_FAILED;
             }
 
-            return DecodeIPV6(tv, dtv, p, pkt + PPP_HEADER_LEN, (uint16_t)(len - PPP_HEADER_LEN));
+            return DecodeIPV6(tv, dtv, p, pkt + data_offset, (uint16_t)(len - data_offset));
 
         case PPP_VJ_COMP:
         case PPP_IPX:
@@ -138,11 +161,70 @@ int DecodePPP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
             return TM_ECODE_OK;
 
         default:
-            SCLogDebug("unknown PPP protocol: %" PRIx32 "",SCNtohs(p->ppph->protocol));
+            SCLogDebug("unknown PPP protocol: %x", proto);
             ENGINE_SET_INVALID_EVENT(p, PPP_WRONG_TYPE);
             return TM_ECODE_OK;
     }
+}
 
+int DecodePPP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len)
+{
+    DEBUG_VALIDATE_BUG_ON(pkt == NULL);
+
+    StatsIncr(tv, dtv->counter_ppp);
+    if (unlikely(len < 1)) {
+        ENGINE_SET_INVALID_EVENT(p, PPP_PKT_TOO_SMALL);
+        return TM_ECODE_FAILED;
+    }
+
+    uint16_t proto_offset = 0;
+    /* 0xff means we have a HDLC header: proto will start at offset 2 */
+    if (*pkt == 0xff) {
+        proto_offset = 2;
+        /* make sure the proto field at the offset fits */
+        if (len < 3) {
+            ENGINE_SET_INVALID_EVENT(p, PPP_PKT_TOO_SMALL);
+            return TM_ECODE_FAILED;
+        }
+    }
+    uint8_t proto_size = 0;
+    uint8_t proto_byte = *(pkt + proto_offset);
+    /* check if compressed protocol bit is set. */
+    if (proto_byte & 0x01) {
+        proto_size = 1;
+    } else {
+        proto_size = 2;
+    }
+    if (len < (proto_size + proto_offset)) {
+        ENGINE_SET_INVALID_EVENT(p, PPP_PKT_TOO_SMALL);
+        return TM_ECODE_FAILED;
+    }
+    if (!PacketIncreaseCheckLayers(p)) {
+        return TM_ECODE_FAILED;
+    }
+
+    const uint32_t data_offset = proto_offset + proto_size;
+    if (data_offset != 4) {
+        if (proto_size == 1) {
+            return DecodePPPCompressedProto(tv, dtv, p, pkt, len, proto_offset);
+        } else {
+            const uint16_t proto = SCNtohs(*(uint16_t *)(pkt + proto_offset));
+            return DecodePPPUncompressedProto(tv, dtv, p, pkt, len, proto, data_offset);
+        }
+    }
+    /* implied proto_offset + proto_size == 4, so continue below */
+
+    p->ppph = (PPPHdr *)pkt;
+
+    SCLogDebug("p %p pkt %p PPP protocol %04x Len: %" PRIu32 "", p, pkt, SCNtohs(p->ppph->protocol),
+            len);
+
+    int r = DecodePPPUncompressedProto(
+            tv, dtv, p, pkt, len, SCNtohs(p->ppph->protocol), data_offset);
+    if (r < 0) {
+        p->ppph = NULL;
+    }
+    return r;
 }
 
 /* TESTS BELOW */