diff options
Diffstat (limited to 'net/bridge/br_stp_bpdu.c')
-rw-r--r-- | net/bridge/br_stp_bpdu.c | 196 |
1 files changed, 100 insertions, 96 deletions
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index 296f6a487c52..8934a54792be 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c | |||
@@ -15,158 +15,162 @@ | |||
15 | 15 | ||
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/netfilter_bridge.h> | 17 | #include <linux/netfilter_bridge.h> |
18 | #include <linux/etherdevice.h> | ||
19 | #include <linux/llc.h> | ||
20 | #include <net/llc.h> | ||
21 | #include <net/llc_pdu.h> | ||
18 | 22 | ||
19 | #include "br_private.h" | 23 | #include "br_private.h" |
20 | #include "br_private_stp.h" | 24 | #include "br_private_stp.h" |
21 | 25 | ||
22 | #define JIFFIES_TO_TICKS(j) (((j) << 8) / HZ) | 26 | #define STP_HZ 256 |
23 | #define TICKS_TO_JIFFIES(j) (((j) * HZ) >> 8) | ||
24 | 27 | ||
25 | static void br_send_bpdu(struct net_bridge_port *p, unsigned char *data, int length) | 28 | #define LLC_RESERVE sizeof(struct llc_pdu_un) |
29 | |||
30 | static void br_send_bpdu(struct net_bridge_port *p, | ||
31 | const unsigned char *data, int length) | ||
26 | { | 32 | { |
27 | struct net_device *dev; | ||
28 | struct sk_buff *skb; | 33 | struct sk_buff *skb; |
29 | int size; | ||
30 | 34 | ||
31 | if (!p->br->stp_enabled) | 35 | if (!p->br->stp_enabled) |
32 | return; | 36 | return; |
33 | 37 | ||
34 | size = length + 2*ETH_ALEN + 2; | 38 | skb = dev_alloc_skb(length+LLC_RESERVE); |
35 | if (size < 60) | 39 | if (!skb) |
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; | 40 | return; |
43 | } | ||
44 | 41 | ||
45 | skb->dev = dev; | 42 | skb->dev = p->dev; |
46 | skb->protocol = htons(ETH_P_802_2); | 43 | skb->protocol = htons(ETH_P_802_2); |
47 | skb->mac.raw = skb_put(skb, size); | 44 | |
48 | memcpy(skb->mac.raw, bridge_ula, ETH_ALEN); | 45 | skb_reserve(skb, LLC_RESERVE); |
49 | memcpy(skb->mac.raw+ETH_ALEN, dev->dev_addr, ETH_ALEN); | 46 | memcpy(__skb_put(skb, length), data, length); |
50 | skb->mac.raw[2*ETH_ALEN] = 0; | 47 | |
51 | skb->mac.raw[2*ETH_ALEN+1] = length; | 48 | llc_pdu_header_init(skb, LLC_PDU_TYPE_U, LLC_SAP_BSPAN, |
52 | skb->nh.raw = skb->mac.raw + 2*ETH_ALEN + 2; | 49 | LLC_SAP_BSPAN, LLC_PDU_CMD); |
53 | memcpy(skb->nh.raw, data, length); | 50 | llc_pdu_init_as_ui_cmd(skb); |
54 | memset(skb->nh.raw + length, 0xa5, size - length - 2*ETH_ALEN - 2); | 51 | |
52 | llc_mac_hdr_init(skb, p->dev->dev_addr, p->br->group_addr); | ||
55 | 53 | ||
56 | NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, | 54 | NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, |
57 | dev_queue_xmit); | 55 | dev_queue_xmit); |
58 | } | 56 | } |
59 | 57 | ||
60 | static __inline__ void br_set_ticks(unsigned char *dest, int jiff) | 58 | static inline void br_set_ticks(unsigned char *dest, int j) |
61 | { | 59 | { |
62 | __u16 ticks; | 60 | unsigned long ticks = (STP_HZ * j)/ HZ; |
63 | 61 | ||
64 | ticks = JIFFIES_TO_TICKS(jiff); | 62 | *((__be16 *) dest) = htons(ticks); |
65 | dest[0] = (ticks >> 8) & 0xFF; | ||
66 | dest[1] = ticks & 0xFF; | ||
67 | } | 63 | } |
68 | 64 | ||
69 | static __inline__ int br_get_ticks(unsigned char *dest) | 65 | static inline int br_get_ticks(const unsigned char *src) |
70 | { | 66 | { |
71 | return TICKS_TO_JIFFIES((dest[0] << 8) | dest[1]); | 67 | unsigned long ticks = ntohs(*(__be16 *)src); |
68 | |||
69 | return (ticks * HZ + STP_HZ - 1) / STP_HZ; | ||
72 | } | 70 | } |
73 | 71 | ||
74 | /* called under bridge lock */ | 72 | /* called under bridge lock */ |
75 | void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) | 73 | void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu) |
76 | { | 74 | { |
77 | unsigned char buf[38]; | 75 | unsigned char buf[35]; |
78 | 76 | ||
79 | buf[0] = 0x42; | 77 | buf[0] = 0; |
80 | buf[1] = 0x42; | 78 | buf[1] = 0; |
81 | buf[2] = 0x03; | 79 | buf[2] = 0; |
82 | buf[3] = 0; | 80 | buf[3] = BPDU_TYPE_CONFIG; |
83 | buf[4] = 0; | 81 | buf[4] = (bpdu->topology_change ? 0x01 : 0) | |
84 | buf[5] = 0; | ||
85 | buf[6] = BPDU_TYPE_CONFIG; | ||
86 | buf[7] = (bpdu->topology_change ? 0x01 : 0) | | ||
87 | (bpdu->topology_change_ack ? 0x80 : 0); | 82 | (bpdu->topology_change_ack ? 0x80 : 0); |
88 | buf[8] = bpdu->root.prio[0]; | 83 | buf[5] = bpdu->root.prio[0]; |
89 | buf[9] = bpdu->root.prio[1]; | 84 | buf[6] = bpdu->root.prio[1]; |
90 | buf[10] = bpdu->root.addr[0]; | 85 | buf[7] = bpdu->root.addr[0]; |
91 | buf[11] = bpdu->root.addr[1]; | 86 | buf[8] = bpdu->root.addr[1]; |
92 | buf[12] = bpdu->root.addr[2]; | 87 | buf[9] = bpdu->root.addr[2]; |
93 | buf[13] = bpdu->root.addr[3]; | 88 | buf[10] = bpdu->root.addr[3]; |
94 | buf[14] = bpdu->root.addr[4]; | 89 | buf[11] = bpdu->root.addr[4]; |
95 | buf[15] = bpdu->root.addr[5]; | 90 | buf[12] = bpdu->root.addr[5]; |
96 | buf[16] = (bpdu->root_path_cost >> 24) & 0xFF; | 91 | buf[13] = (bpdu->root_path_cost >> 24) & 0xFF; |
97 | buf[17] = (bpdu->root_path_cost >> 16) & 0xFF; | 92 | buf[14] = (bpdu->root_path_cost >> 16) & 0xFF; |
98 | buf[18] = (bpdu->root_path_cost >> 8) & 0xFF; | 93 | buf[15] = (bpdu->root_path_cost >> 8) & 0xFF; |
99 | buf[19] = bpdu->root_path_cost & 0xFF; | 94 | buf[16] = bpdu->root_path_cost & 0xFF; |
100 | buf[20] = bpdu->bridge_id.prio[0]; | 95 | buf[17] = bpdu->bridge_id.prio[0]; |
101 | buf[21] = bpdu->bridge_id.prio[1]; | 96 | buf[18] = bpdu->bridge_id.prio[1]; |
102 | buf[22] = bpdu->bridge_id.addr[0]; | 97 | buf[19] = bpdu->bridge_id.addr[0]; |
103 | buf[23] = bpdu->bridge_id.addr[1]; | 98 | buf[20] = bpdu->bridge_id.addr[1]; |
104 | buf[24] = bpdu->bridge_id.addr[2]; | 99 | buf[21] = bpdu->bridge_id.addr[2]; |
105 | buf[25] = bpdu->bridge_id.addr[3]; | 100 | buf[22] = bpdu->bridge_id.addr[3]; |
106 | buf[26] = bpdu->bridge_id.addr[4]; | 101 | buf[23] = bpdu->bridge_id.addr[4]; |
107 | buf[27] = bpdu->bridge_id.addr[5]; | 102 | buf[24] = bpdu->bridge_id.addr[5]; |
108 | buf[28] = (bpdu->port_id >> 8) & 0xFF; | 103 | buf[25] = (bpdu->port_id >> 8) & 0xFF; |
109 | buf[29] = bpdu->port_id & 0xFF; | 104 | buf[26] = bpdu->port_id & 0xFF; |
110 | 105 | ||
111 | br_set_ticks(buf+30, bpdu->message_age); | 106 | br_set_ticks(buf+27, bpdu->message_age); |
112 | br_set_ticks(buf+32, bpdu->max_age); | 107 | br_set_ticks(buf+29, bpdu->max_age); |
113 | br_set_ticks(buf+34, bpdu->hello_time); | 108 | br_set_ticks(buf+31, bpdu->hello_time); |
114 | br_set_ticks(buf+36, bpdu->forward_delay); | 109 | br_set_ticks(buf+33, bpdu->forward_delay); |
115 | 110 | ||
116 | br_send_bpdu(p, buf, 38); | 111 | br_send_bpdu(p, buf, 35); |
117 | } | 112 | } |
118 | 113 | ||
119 | /* called under bridge lock */ | 114 | /* called under bridge lock */ |
120 | void br_send_tcn_bpdu(struct net_bridge_port *p) | 115 | void br_send_tcn_bpdu(struct net_bridge_port *p) |
121 | { | 116 | { |
122 | unsigned char buf[7]; | 117 | unsigned char buf[4]; |
123 | 118 | ||
124 | buf[0] = 0x42; | 119 | buf[0] = 0; |
125 | buf[1] = 0x42; | 120 | buf[1] = 0; |
126 | buf[2] = 0x03; | 121 | buf[2] = 0; |
127 | buf[3] = 0; | 122 | buf[3] = BPDU_TYPE_TCN; |
128 | buf[4] = 0; | ||
129 | buf[5] = 0; | ||
130 | buf[6] = BPDU_TYPE_TCN; | ||
131 | br_send_bpdu(p, buf, 7); | 123 | br_send_bpdu(p, buf, 7); |
132 | } | 124 | } |
133 | 125 | ||
134 | static const unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00}; | 126 | /* |
135 | 127 | * Called from llc. | |
136 | /* NO locks, but rcu_read_lock (preempt_disabled) */ | 128 | * |
137 | int br_stp_handle_bpdu(struct sk_buff *skb) | 129 | * NO locks, but rcu_read_lock (preempt_disabled) |
130 | */ | ||
131 | int br_stp_rcv(struct sk_buff *skb, struct net_device *dev, | ||
132 | struct packet_type *pt, struct net_device *orig_dev) | ||
138 | { | 133 | { |
139 | struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); | 134 | const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb); |
135 | const unsigned char *dest = eth_hdr(skb)->h_dest; | ||
136 | struct net_bridge_port *p = rcu_dereference(dev->br_port); | ||
140 | struct net_bridge *br; | 137 | struct net_bridge *br; |
141 | unsigned char *buf; | 138 | const unsigned char *buf; |
142 | 139 | ||
143 | if (!p) | 140 | if (!p) |
144 | goto err; | 141 | goto err; |
145 | 142 | ||
146 | br = p->br; | 143 | if (pdu->ssap != LLC_SAP_BSPAN |
147 | spin_lock(&br->lock); | 144 | || pdu->dsap != LLC_SAP_BSPAN |
145 | || pdu->ctrl_1 != LLC_PDU_TYPE_U) | ||
146 | goto err; | ||
148 | 147 | ||
149 | if (p->state == BR_STATE_DISABLED || !(br->dev->flags & IFF_UP)) | 148 | if (!pskb_may_pull(skb, 4)) |
150 | goto out; | 149 | goto err; |
150 | |||
151 | /* compare of protocol id and version */ | ||
152 | buf = skb->data; | ||
153 | if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0) | ||
154 | goto err; | ||
151 | 155 | ||
152 | /* insert into forwarding database after filtering to avoid spoofing */ | 156 | br = p->br; |
153 | br_fdb_update(br, p, eth_hdr(skb)->h_source); | 157 | spin_lock(&br->lock); |
154 | 158 | ||
155 | if (!br->stp_enabled) | 159 | if (p->state == BR_STATE_DISABLED |
160 | || !br->stp_enabled | ||
161 | || !(br->dev->flags & IFF_UP)) | ||
156 | goto out; | 162 | goto out; |
157 | 163 | ||
158 | /* need at least the 802 and STP headers */ | 164 | if (compare_ether_addr(dest, br->group_addr) != 0) |
159 | if (!pskb_may_pull(skb, sizeof(header)+1) || | ||
160 | memcmp(skb->data, header, sizeof(header))) | ||
161 | goto out; | 165 | goto out; |
162 | 166 | ||
163 | buf = skb_pull(skb, sizeof(header)); | 167 | buf = skb_pull(skb, 3); |
164 | 168 | ||
165 | if (buf[0] == BPDU_TYPE_CONFIG) { | 169 | if (buf[0] == BPDU_TYPE_CONFIG) { |
166 | struct br_config_bpdu bpdu; | 170 | struct br_config_bpdu bpdu; |
167 | 171 | ||
168 | if (!pskb_may_pull(skb, 32)) | 172 | if (!pskb_may_pull(skb, 32)) |
169 | goto out; | 173 | goto out; |
170 | 174 | ||
171 | buf = skb->data; | 175 | buf = skb->data; |
172 | bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0; | 176 | bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0; |