Improve and fix validation of received BPDUs.
authorSrinivas Aji <Aji_Srinivas@emc.com>
Mon, 28 May 2007 18:22:43 +0000 (23:52 +0530)
committerSrinivas Aji <Aji_Srinivas@emc.com>
Wed, 27 Feb 2008 18:03:50 +0000 (23:33 +0530)
   Validate ethernet and LLC headers: Check destination address, length,
   and SAP fields. Also fix length checks for Config and RST BPDUs.

Signed-off-by: Srinivas Aji <Aji_Srinivas@emc.com>
bridge_track.c

index 00e60e3..e4b512d 100644 (file)
@@ -30,6 +30,7 @@
 #include <net/if.h>
 #include <stdlib.h>
 #include <linux/if_bridge.h>
+#include <linux/if_ether.h>
 #include <arpa/inet.h>
 #include <sys/types.h>
 
@@ -565,10 +566,26 @@ int bridge_notify(int br_index, int if_index, int newlink, int up)
        return 0;
 }
 
+struct llc_header
+{
+       uint8_t dest_addr[ETH_ALEN];
+       uint8_t src_addr[ETH_ALEN];
+       uint16_t len8023;
+       uint8_t d_sap;  /* 0x42 */
+       uint8_t s_sap;  /* 0x42 */
+       uint8_t llc_ui; /* 0x03 */
+} __attribute__((packed));
+
+const unsigned char bridge_group_address[ETH_ALEN] = {
+       0x01, 0x80, 0xc2, 0x00, 0x00, 0x00
+};
+
+const unsigned char STP_SAP = 0x42;
+
 void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len)
 {
        struct ifdata *ifc = find_if(if_index);
-       BPDU_T *bpdu = (BPDU_T *) (data + sizeof(MAC_HEADER_T));
+       BPDU_T *bpdu;
 
        LOG("ifindex %d, len %d", if_index, len);
        if (!ifc)
@@ -576,17 +593,35 @@ void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len)
 
        TST(ifc->up,);
        TST(ifc->master->stp_up,);
-       TST(len > sizeof(MAC_HEADER_T) + sizeof(ETH_HEADER_T) + sizeof(BPDU_HEADER_T),);
 
-       /* Do some validation */
+       /* Validate Ethernet and LLC header */
+       {
+               struct llc_header *h;
+               unsigned int l;
+               TST(len > sizeof(struct llc_header),);
+               h = (struct llc_header *)data;
+               TST(memcmp(h->dest_addr, bridge_group_address, ETH_ALEN) == 0,
+                       );
+               l = ntohs(h->len8023);
+               TST(l <= ETH_DATA_LEN && l <= len - ETH_HLEN && l >= 3,);
+               TST(h->d_sap == STP_SAP && h->s_sap == STP_SAP
+                   && (h->llc_ui & 0x3) == 0x3 /* LLC UI */,);
+               /* BPDU_T includes ETH_HEADER_T, i.e. {d_sap, s_sap, llc_ui} */
+               bpdu = (BPDU_T *)(data + sizeof(*h) - sizeof(ETH_HEADER_T));
+               len = l + 2; /* ETH_HEADER_T includes the 2 bytes of len8023 */
+       }
+
+       TST(len > sizeof(ETH_HEADER_T) + sizeof(BPDU_HEADER_T),);
+
+       /* Do some BPDU validation as per 9.3.4 of standard */
        if (bpdu->hdr.protocol[0] || bpdu->hdr.protocol[1])
                return;
 
        switch (bpdu->hdr.bpdu_type) {
        case BPDU_RSTP:
-               TST(len >= 36,);
+               TST(len >= sizeof(ETH_HEADER_T) + 36,);
        case BPDU_CONFIG_TYPE:
-               TST(len >= 35,);
+               TST(len >= sizeof(ETH_HEADER_T) + 35,);
                /* 802.1w doesn't ask for this */
                //    TST(ntohs(*(uint16_t*)bpdu.body.message_age)
                //        < ntohs(*(uint16_t*)bpdu.body.max_age), );