#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>
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)
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), );