1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * ebt_stp 4 * 5 * Authors: 6 * Bart De Schuymer <bdschuym@pandora.be> 7 * Stephen Hemminger <shemminger@osdl.org> 8 * 9 * July, 2003 10 */ 11 #include <linux/etherdevice.h> 12 #include <linux/module.h> 13 #include <linux/netfilter/x_tables.h> 14 #include <linux/netfilter_bridge/ebtables.h> 15 #include <linux/netfilter_bridge/ebt_stp.h> 16 17 #define BPDU_TYPE_CONFIG 0 18 #define BPDU_TYPE_TCN 0x80 19 20 struct stp_header { 21 u8 dsap; 22 u8 ssap; 23 u8 ctrl; 24 u8 pid; 25 u8 vers; 26 u8 type; 27 }; 28 29 struct stp_config_pdu { 30 u8 flags; 31 u8 root[8]; 32 u8 root_cost[4]; 33 u8 sender[8]; 34 u8 port[2]; 35 u8 msg_age[2]; 36 u8 max_age[2]; 37 u8 hello_time[2]; 38 u8 forward_delay[2]; 39 }; 40 41 #define NR16(p) (p[0] << 8 | p[1]) 42 #define NR32(p) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]) 43 44 static bool ebt_filter_config(const struct ebt_stp_info *info, 45 const struct stp_config_pdu *stpc) 46 { 47 const struct ebt_stp_config_info *c; 48 u16 v16; 49 u32 v32; 50 51 c = &info->config; 52 if ((info->bitmask & EBT_STP_FLAGS) && 53 NF_INVF(info, EBT_STP_FLAGS, c->flags != stpc->flags)) 54 return false; 55 if (info->bitmask & EBT_STP_ROOTPRIO) { 56 v16 = NR16(stpc->root); 57 if (NF_INVF(info, EBT_STP_ROOTPRIO, 58 v16 < c->root_priol || v16 > c->root_priou)) 59 return false; 60 } 61 if (info->bitmask & EBT_STP_ROOTADDR) { 62 if (NF_INVF(info, EBT_STP_ROOTADDR, 63 !ether_addr_equal_masked(&stpc->root[2], 64 c->root_addr, 65 c->root_addrmsk))) 66 return false; 67 } 68 if (info->bitmask & EBT_STP_ROOTCOST) { 69 v32 = NR32(stpc->root_cost); 70 if (NF_INVF(info, EBT_STP_ROOTCOST, 71 v32 < c->root_costl || v32 > c->root_costu)) 72 return false; 73 } 74 if (info->bitmask & EBT_STP_SENDERPRIO) { 75 v16 = NR16(stpc->sender); 76 if (NF_INVF(info, EBT_STP_SENDERPRIO, 77 v16 < c->sender_priol || v16 > c->sender_priou)) 78 return false; 79 } 80 if (info->bitmask & EBT_STP_SENDERADDR) { 81 if (NF_INVF(info, EBT_STP_SENDERADDR, 82 !ether_addr_equal_masked(&stpc->sender[2], 83 c->sender_addr, 84 c->sender_addrmsk))) 85 return false; 86 } 87 if (info->bitmask & EBT_STP_PORT) { 88 v16 = NR16(stpc->port); 89 if (NF_INVF(info, EBT_STP_PORT, 90 v16 < c->portl || v16 > c->portu)) 91 return false; 92 } 93 if (info->bitmask & EBT_STP_MSGAGE) { 94 v16 = NR16(stpc->msg_age); 95 if (NF_INVF(info, EBT_STP_MSGAGE, 96 v16 < c->msg_agel || v16 > c->msg_ageu)) 97 return false; 98 } 99 if (info->bitmask & EBT_STP_MAXAGE) { 100 v16 = NR16(stpc->max_age); 101 if (NF_INVF(info, EBT_STP_MAXAGE, 102 v16 < c->max_agel || v16 > c->max_ageu)) 103 return false; 104 } 105 if (info->bitmask & EBT_STP_HELLOTIME) { 106 v16 = NR16(stpc->hello_time); 107 if (NF_INVF(info, EBT_STP_HELLOTIME, 108 v16 < c->hello_timel || v16 > c->hello_timeu)) 109 return false; 110 } 111 if (info->bitmask & EBT_STP_FWDD) { 112 v16 = NR16(stpc->forward_delay); 113 if (NF_INVF(info, EBT_STP_FWDD, 114 v16 < c->forward_delayl || v16 > c->forward_delayu)) 115 return false; 116 } 117 return true; 118 } 119 120 static bool 121 ebt_stp_mt(const struct sk_buff *skb, struct xt_action_param *par) 122 { 123 const struct ebt_stp_info *info = par->matchinfo; 124 const struct stp_header *sp; 125 struct stp_header _stph; 126 const u8 header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; 127 128 sp = skb_header_pointer(skb, 0, sizeof(_stph), &_stph); 129 if (sp == NULL) 130 return false; 131 132 /* The stp code only considers these */ 133 if (memcmp(sp, header, sizeof(header))) 134 return false; 135 136 if ((info->bitmask & EBT_STP_TYPE) && 137 NF_INVF(info, EBT_STP_TYPE, info->type != sp->type)) 138 return false; 139 140 if (sp->type == BPDU_TYPE_CONFIG && 141 info->bitmask & EBT_STP_CONFIG_MASK) { 142 const struct stp_config_pdu *st; 143 struct stp_config_pdu _stpc; 144 145 st = skb_header_pointer(skb, sizeof(_stph), 146 sizeof(_stpc), &_stpc); 147 if (st == NULL) 148 return false; 149 return ebt_filter_config(info, st); 150 } 151 return true; 152 } 153 154 static int ebt_stp_mt_check(const struct xt_mtchk_param *par) 155 { 156 const struct ebt_stp_info *info = par->matchinfo; 157 const struct ebt_entry *e = par->entryinfo; 158 159 if (info->bitmask & ~EBT_STP_MASK || info->invflags & ~EBT_STP_MASK || 160 !(info->bitmask & EBT_STP_MASK)) 161 return -EINVAL; 162 /* Make sure the match only receives stp frames */ 163 if (!par->nft_compat && 164 (!ether_addr_equal(e->destmac, eth_stp_addr) || 165 !(e->bitmask & EBT_DESTMAC) || 166 !is_broadcast_ether_addr(e->destmsk))) 167 return -EINVAL; 168 169 return 0; 170 } 171 172 static struct xt_match ebt_stp_mt_reg __read_mostly = { 173 .name = "stp", 174 .revision = 0, 175 .family = NFPROTO_BRIDGE, 176 .match = ebt_stp_mt, 177 .checkentry = ebt_stp_mt_check, 178 .matchsize = sizeof(struct ebt_stp_info), 179 .me = THIS_MODULE, 180 }; 181 182 static int __init ebt_stp_init(void) 183 { 184 return xt_register_match(&ebt_stp_mt_reg); 185 } 186 187 static void __exit ebt_stp_fini(void) 188 { 189 xt_unregister_match(&ebt_stp_mt_reg); 190 } 191 192 module_init(ebt_stp_init); 193 module_exit(ebt_stp_fini); 194 MODULE_DESCRIPTION("Ebtables: Spanning Tree Protocol packet match"); 195 MODULE_LICENSE("GPL"); 196