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