1/* 2 * Spanning tree protocol; BPDU handling 3 * Linux ethernet bridge 4 * 5 * Authors: 6 * Lennert Buytenhek <buytenh@gnu.org> 7 * 8 * $Id: br_stp_bpdu.c,v 1.1.1.1 2008/10/15 03:27:33 james26_jang Exp $ 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public License 12 * as published by the Free Software Foundation; either version 13 * 2 of the License, or (at your option) any later version. 14 */ 15 16#include <linux/kernel.h> 17#include <linux/if_ether.h> 18#include <linux/if_bridge.h> 19#include "br_private.h" 20#include "br_private_stp.h" 21 22#define JIFFIES_TO_TICKS(j) (((j) << 8) / HZ) 23#define TICKS_TO_JIFFIES(j) (((j) * HZ) >> 8) 24 25static void br_send_bpdu(struct net_bridge_port *p, unsigned char *data, int length) 26{ 27 struct net_device *dev; 28 struct sk_buff *skb; 29 int size; 30 31 if (!p->br->stp_enabled) 32 return; 33 34 size = length + 2*ETH_ALEN + 2; 35 if (size < 60) 36 size = 60; 37 38 dev = p->dev; 39 40 if ((skb = dev_alloc_skb(size)) == NULL) { 41 printk(KERN_INFO "br: memory squeeze!\n"); 42 return; 43 } 44 45 skb->dev = dev; 46 skb->protocol = htons(ETH_P_802_2); 47 skb->mac.raw = skb_put(skb, size); 48 memcpy(skb->mac.raw, bridge_ula, ETH_ALEN); 49 memcpy(skb->mac.raw+ETH_ALEN, dev->dev_addr, ETH_ALEN); 50 skb->mac.raw[2*ETH_ALEN] = 0; 51 skb->mac.raw[2*ETH_ALEN+1] = length; 52 skb->nh.raw = skb->mac.raw + 2*ETH_ALEN + 2; 53 memcpy(skb->nh.raw, data, length); 54 memset(skb->nh.raw + length, 0xa5, size - length - 2*ETH_ALEN - 2); 55 56 dev_queue_xmit(skb); 57} 58 59static __inline__ void br_set_ticks(unsigned char *dest, int jiff) 60{ 61 __u16 ticks; 62 63 ticks = JIFFIES_TO_TICKS(jiff); 64 dest[0] = (ticks >> 8) & 0xFF; 65 dest[1] = ticks & 0xFF; 66} 67 68static __inline__ int br_get_ticks(unsigned char *dest) 69{ 70 return TICKS_TO_JIFFIES((dest[0] << 8) | dest[1]); 71} 72 73/* called under bridge lock */ 74void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) 75{ 76 unsigned char buf[38]; 77 78 buf[0] = 0x42; 79 buf[1] = 0x42; 80 buf[2] = 0x03; 81 buf[3] = 0; 82 buf[4] = 0; 83 buf[5] = 0; 84 buf[6] = BPDU_TYPE_CONFIG; 85 buf[7] = (bpdu->topology_change ? 0x01 : 0) | 86 (bpdu->topology_change_ack ? 0x80 : 0); 87 buf[8] = bpdu->root.prio[0]; 88 buf[9] = bpdu->root.prio[1]; 89 buf[10] = bpdu->root.addr[0]; 90 buf[11] = bpdu->root.addr[1]; 91 buf[12] = bpdu->root.addr[2]; 92 buf[13] = bpdu->root.addr[3]; 93 buf[14] = bpdu->root.addr[4]; 94 buf[15] = bpdu->root.addr[5]; 95 buf[16] = (bpdu->root_path_cost >> 24) & 0xFF; 96 buf[17] = (bpdu->root_path_cost >> 16) & 0xFF; 97 buf[18] = (bpdu->root_path_cost >> 8) & 0xFF; 98 buf[19] = bpdu->root_path_cost & 0xFF; 99 buf[20] = bpdu->bridge_id.prio[0]; 100 buf[21] = bpdu->bridge_id.prio[1]; 101 buf[22] = bpdu->bridge_id.addr[0]; 102 buf[23] = bpdu->bridge_id.addr[1]; 103 buf[24] = bpdu->bridge_id.addr[2]; 104 buf[25] = bpdu->bridge_id.addr[3]; 105 buf[26] = bpdu->bridge_id.addr[4]; 106 buf[27] = bpdu->bridge_id.addr[5]; 107 buf[28] = (bpdu->port_id >> 8) & 0xFF; 108 buf[29] = bpdu->port_id & 0xFF; 109 110 br_set_ticks(buf+30, bpdu->message_age); 111 br_set_ticks(buf+32, bpdu->max_age); 112 br_set_ticks(buf+34, bpdu->hello_time); 113 br_set_ticks(buf+36, bpdu->forward_delay); 114 115 br_send_bpdu(p, buf, 38); 116} 117 118/* called under bridge lock */ 119void br_send_tcn_bpdu(struct net_bridge_port *p) 120{ 121 unsigned char buf[7]; 122 123 buf[0] = 0x42; 124 buf[1] = 0x42; 125 buf[2] = 0x03; 126 buf[3] = 0; 127 buf[4] = 0; 128 buf[5] = 0; 129 buf[6] = BPDU_TYPE_TCN; 130 br_send_bpdu(p, buf, 7); 131} 132 133static unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; 134 135/* called under bridge lock */ 136void br_stp_handle_bpdu(struct sk_buff *skb) 137{ 138 unsigned char *buf; 139 struct net_bridge_port *p; 140 141 buf = skb->mac.raw + 14; 142 p = skb->dev->br_port; 143 if (!p->br->stp_enabled || memcmp(buf, header, 6)) { 144 kfree_skb(skb); 145 return; 146 } 147 148 if (buf[6] == BPDU_TYPE_CONFIG) { 149 struct br_config_bpdu bpdu; 150 151 bpdu.topology_change = (buf[7] & 0x01) ? 1 : 0; 152 bpdu.topology_change_ack = (buf[7] & 0x80) ? 1 : 0; 153 bpdu.root.prio[0] = buf[8]; 154 bpdu.root.prio[1] = buf[9]; 155 bpdu.root.addr[0] = buf[10]; 156 bpdu.root.addr[1] = buf[11]; 157 bpdu.root.addr[2] = buf[12]; 158 bpdu.root.addr[3] = buf[13]; 159 bpdu.root.addr[4] = buf[14]; 160 bpdu.root.addr[5] = buf[15]; 161 bpdu.root_path_cost = 162 (buf[16] << 24) | 163 (buf[17] << 16) | 164 (buf[18] << 8) | 165 buf[19]; 166 bpdu.bridge_id.prio[0] = buf[20]; 167 bpdu.bridge_id.prio[1] = buf[21]; 168 bpdu.bridge_id.addr[0] = buf[22]; 169 bpdu.bridge_id.addr[1] = buf[23]; 170 bpdu.bridge_id.addr[2] = buf[24]; 171 bpdu.bridge_id.addr[3] = buf[25]; 172 bpdu.bridge_id.addr[4] = buf[26]; 173 bpdu.bridge_id.addr[5] = buf[27]; 174 bpdu.port_id = (buf[28] << 8) | buf[29]; 175 176 bpdu.message_age = br_get_ticks(buf+30); 177 bpdu.max_age = br_get_ticks(buf+32); 178 bpdu.hello_time = br_get_ticks(buf+34); 179 bpdu.forward_delay = br_get_ticks(buf+36); 180 181 kfree_skb(skb); 182 br_received_config_bpdu(p, &bpdu); 183 return; 184 } 185 186 if (buf[6] == BPDU_TYPE_TCN) { 187 br_received_tcn_bpdu(p); 188 kfree_skb(skb); 189 return; 190 } 191 192 kfree_skb(skb); 193} 194