1 /************************************************************************
2 * RSTP library - Rapid Spanning Tree (802.1t, 802.1w)
3 * Copyright (C) 2001-2003 Optical Access
4 * Author: Alex Rozin
5 *
6 * This file is part of RSTP library.
7 *
8 * RSTP library is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by the
10 * Free Software Foundation; version 2.1
11 *
12 * RSTP library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with RSTP library; see the file COPYING. If not, write to the Free
19 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 * 02111-1307, USA.
21 **********************************************************************/
22
23 /* Port Transmit state machine : 17.27 */
24
25 #include "base.h"
26 #include "stpm.h"
27 #include "stp_to.h" /* for STP_OUT_get_port_mac & STP_OUT_tx_bpdu */
28
29 #define BPDU_LEN8023_OFF 12
30
31 #define STATES { \
32 CHOOSE(TRANSMIT_INIT), \
33 CHOOSE(TRANSMIT_PERIODIC), \
34 CHOOSE(IDLE), \
35 CHOOSE(TRANSMIT_CONFIG), \
36 CHOOSE(TRANSMIT_TCN), \
37 CHOOSE(TRANSMIT_RSTP) \
38 }
39
40 #define GET_STATE_NAME STP_transmit_get_state_name
41 #include "choose.h"
42
43 #define MIN_FRAME_LENGTH 64
44
45
46 typedef struct tx_tcn_bpdu_t {
47 MAC_HEADER_T mac;
48 ETH_HEADER_T eth;
49 BPDU_HEADER_T hdr;
50 } TCN_BPDU_T;
51
52 typedef struct tx_stp_bpdu_t {
53 MAC_HEADER_T mac;
54 ETH_HEADER_T eth;
55 BPDU_HEADER_T hdr;
56 BPDU_BODY_T body;
57 } CONFIG_BPDU_T;
58
59 typedef struct tx_rstp_bpdu_t {
60 MAC_HEADER_T mac;
61 ETH_HEADER_T eth;
62 BPDU_HEADER_T hdr;
63 BPDU_BODY_T body;
64 unsigned char ver_1_length[2];
65 } RSTP_BPDU_T;
66
67
68 static RSTP_BPDU_T bpdu_packet = {
69 {/* MAC_HEADER_T */
70 {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}, /* dst_mac */
71 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* src_mac */
72 },
73 { /* ETH_HEADER_T */
74 {0x00, 0x00}, /* len8023 */
75 BPDU_L_SAP, BPDU_L_SAP, LLC_UI /* dsap, ssap, llc */
76 },
77 {/* BPDU_HEADER_T */
78 {0x00, 0x00}, /* protocol */
79 BPDU_VERSION_ID, 0x00 /* version, bpdu_type */
80 },
81 {
82 0x00, /* flags; */
83 {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* root_id[8]; */
84 {0x00,0x00,0x00,0x00}, /* root_path_cost[4]; */
85 {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* bridge_id[8]; */
86 {0x00,0x00}, /* port_id[2]; */
87 {0x00,0x00}, /* message_age[2]; */
88 {0x00,0x00}, /* max_age[2]; */
89 {0x00,0x00}, /* hello_time[2]; */
90 {0x00,0x00}, /* forward_delay[2]; */
91 },
92 {0x00,0x00}, /* ver_1_length[2]; */
93 };
94
95 static size_t
build_bpdu_header(int port_index,unsigned char bpdu_type,unsigned short pkt_len)96 build_bpdu_header (int port_index,
97 unsigned char bpdu_type,
98 unsigned short pkt_len)
99 {
100 unsigned short len8023;
101
102 STP_OUT_get_port_mac (port_index, bpdu_packet.mac.src_mac);
103
104 bpdu_packet.hdr.bpdu_type = bpdu_type;
105 bpdu_packet.hdr.version = (BPDU_RSTP == bpdu_type) ?
106 BPDU_VERSION_RAPID_ID :
107 BPDU_VERSION_ID;
108
109 /* NOTE: I suppose, that sizeof(unsigned short)=2 ! */
110 len8023 = htons ((unsigned short) (pkt_len + 3));
111 (void) memcpy (&bpdu_packet.eth.len8023, &len8023, 2);
112
113 if (pkt_len < MIN_FRAME_LENGTH) pkt_len = MIN_FRAME_LENGTH;
114 return pkt_len;
115 }
116
117 static int
txTcn(STATE_MACH_T * this)118 txTcn (STATE_MACH_T* this)
119 { /* 17.19.17 (page 68) & 9.3.2 (page 25) */
120 register size_t pkt_len;
121 register int port_index, vlan_id;
122
123 #ifdef STP_DBG
124 if (this->owner.port->skip_tx > 0) {
125 if (1 == this->owner.port->skip_tx)
126 stp_trace ("port %s stop tx skipping",
127 this->owner.port->port_name);
128 this->owner.port->skip_tx--;
129 return STP_Nothing_To_Do;
130 }
131 #endif
132
133 if (this->owner.port->admin_non_stp) return 1;
134 port_index = this->owner.port->port_index;
135 vlan_id = this->owner.port->owner->vlan_id;
136
137 pkt_len = build_bpdu_header (port_index,
138 BPDU_TOPO_CHANGE_TYPE,
139 sizeof (BPDU_HEADER_T));
140
141 #ifdef STP_DBG
142 if (this->debug)
143 stp_trace ("port %s txTcn", this->owner.port->port_name);
144 #endif
145 return STP_OUT_tx_bpdu (port_index, vlan_id,
146 (unsigned char *) &bpdu_packet,
147 pkt_len);
148 }
149
150 static void
build_config_bpdu(PORT_T * port,Bool set_topo_ack_flag)151 build_config_bpdu (PORT_T* port, Bool set_topo_ack_flag)
152 {
153 bpdu_packet.body.flags = 0;
154 if (port->tcWhile) {
155 #ifdef STP_DBG
156 if (port->topoch->debug)
157 stp_trace ("tcWhile=%d =>tx TOPOLOGY_CHANGE_BIT to port %s",
158 (int) port->tcWhile, port->port_name);
159 #endif
160 bpdu_packet.body.flags |= TOPOLOGY_CHANGE_BIT;
161 }
162
163 if (set_topo_ack_flag && port->tcAck) {
164 bpdu_packet.body.flags |= TOPOLOGY_CHANGE_ACK_BIT;
165 }
166
167 STP_VECT_set_vector (&port->portPrio, &bpdu_packet.body);
168 STP_set_times (&port->portTimes, &bpdu_packet.body);
169 }
170
171 static int
txConfig(STATE_MACH_T * this)172 txConfig (STATE_MACH_T* this)
173 {/* 17.19.15 (page 67) & 9.3.1 (page 23) */
174 register size_t pkt_len;
175 register PORT_T* port = NULL;
176 register int port_index, vlan_id;
177
178 #ifdef STP_DBG
179 if (this->owner.port->skip_tx > 0) {
180 if (1 == this->owner.port->skip_tx)
181 stp_trace ("port %s stop tx skipping",
182 this->owner.port->port_name);
183 this->owner.port->skip_tx--;
184 return STP_Nothing_To_Do;
185 }
186 #endif
187
188 port = this->owner.port;
189 if (port->admin_non_stp) return 1;
190 port_index = port->port_index;
191 vlan_id = port->owner->vlan_id;
192
193 pkt_len = build_bpdu_header (port->port_index,
194 BPDU_CONFIG_TYPE,
195 sizeof (BPDU_HEADER_T) + sizeof (BPDU_BODY_T));
196 build_config_bpdu (port, True);
197
198 #ifdef STP_DBG
199 if (this->debug)
200 stp_trace ("port %s txConfig flags=0X%lx",
201 port->port_name,
202 (unsigned long) bpdu_packet.body.flags);
203 #endif
204 return STP_OUT_tx_bpdu (port_index, vlan_id,
205 (unsigned char *) &bpdu_packet,
206 pkt_len);
207 }
208
209 static int
txRstp(STATE_MACH_T * this)210 txRstp (STATE_MACH_T* this)
211 {/* 17.19.16 (page 68) & 9.3.3 (page 25) */
212 register size_t pkt_len;
213 register PORT_T* port = NULL;
214 register int port_index, vlan_id;
215 unsigned char role;
216
217 #ifdef STP_DBG
218 if (this->owner.port->skip_tx > 0) {
219 if (1 == this->owner.port->skip_tx)
220 stp_trace ("port %s stop tx skipping",
221 this->owner.port->port_name);
222 else
223 stp_trace ("port %s skip tx %d",
224 this->owner.port->port_name, this->owner.port->skip_tx);
225
226 this->owner.port->skip_tx--;
227 return STP_Nothing_To_Do;
228 }
229 #endif
230
231 port = this->owner.port;
232 if (port->admin_non_stp) return 1;
233 port_index = port->port_index;
234 vlan_id = port->owner->vlan_id;
235
236 pkt_len = build_bpdu_header (port->port_index,
237 BPDU_RSTP,
238 sizeof (BPDU_HEADER_T) + sizeof (BPDU_BODY_T) + 2);
239 build_config_bpdu (port, False);
240
241 switch (port->selectedRole) {
242 default:
243 case DisabledPort:
244 role = RSTP_PORT_ROLE_UNKN;
245 break;
246 case AlternatePort:
247 role = RSTP_PORT_ROLE_ALTBACK;
248 break;
249 case BackupPort:
250 role = RSTP_PORT_ROLE_ALTBACK;
251 break;
252 case RootPort:
253 role = RSTP_PORT_ROLE_ROOT;
254 break;
255 case DesignatedPort:
256 role = RSTP_PORT_ROLE_DESGN;
257 break;
258 }
259
260 bpdu_packet.body.flags |= (role << PORT_ROLE_OFFS);
261
262 if (port->synced) {
263 #if 0 /* def STP_DBG */
264 if (port->roletrns->debug)
265 stp_trace ("tx AGREEMENT_BIT to port %s", port->port_name);
266 #endif
267 bpdu_packet.body.flags |= AGREEMENT_BIT;
268 }
269
270 if (port->proposing) {
271 #if 0 /* def STP_DBG */
272 if (port->roletrns->debug)
273 stp_trace ("tx PROPOSAL_BIT to port %s", port->port_name);
274 #endif
275 bpdu_packet.body.flags |= PROPOSAL_BIT;
276 }
277
278 #ifdef STP_DBG
279 if (this->debug)
280 stp_trace ("port %s txRstp flags=0X%lx",
281 port->port_name,
282 (unsigned long) bpdu_packet.body.flags);
283 #endif
284
285 return STP_OUT_tx_bpdu (port_index, vlan_id,
286 (unsigned char *) &bpdu_packet,
287 pkt_len);
288 }
289
290 void
STP_transmit_enter_state(STATE_MACH_T * this)291 STP_transmit_enter_state (STATE_MACH_T* this)
292 {
293 register PORT_T* port = this->owner.port;
294
295 switch (this->State) {
296 case BEGIN:
297 case TRANSMIT_INIT:
298 port->newInfo = False;
299 port->helloWhen = 0;
300 port->txCount = 0;
301 break;
302 case TRANSMIT_PERIODIC:
303 port->newInfo = port->newInfo ||
304 ((port->role == DesignatedPort) ||
305 ((port->role == RootPort) && port->tcWhile));
306 port->helloWhen = port->owner->rootTimes.HelloTime;
307 break;
308 case IDLE:
309 break;
310 case TRANSMIT_CONFIG:
311 port->newInfo = False;
312 (void) txConfig (this);
313 port->txCount++;
314 port->tcAck = False;
315 break;
316 case TRANSMIT_TCN:
317 port->newInfo = False;
318 (void) txTcn (this);
319 port->txCount++;
320 break;
321 case TRANSMIT_RSTP:
322 port->newInfo = False;
323 (void) txRstp (this);
324 port->txCount++;
325 port->tcAck = False;
326 break;
327 };
328 }
329
330 Bool
STP_transmit_check_conditions(STATE_MACH_T * this)331 STP_transmit_check_conditions (STATE_MACH_T* this)
332 {
333 register PORT_T* port = this->owner.port;
334
335 switch (this->State) {
336 case BEGIN:
337 return STP_hop_2_state (this, TRANSMIT_INIT);
338 case TRANSMIT_INIT:
339 return STP_hop_2_state (this, IDLE);
340 case TRANSMIT_PERIODIC:
341 return STP_hop_2_state (this, IDLE);
342 case IDLE:
343 if (!port->helloWhen) return STP_hop_2_state (this, TRANSMIT_PERIODIC);
344 if (!port->sendRSTP && port->newInfo &&
345 (port->txCount < TxHoldCount) &&
346 (port->role == DesignatedPort) &&
347 port->helloWhen)
348 return STP_hop_2_state (this, TRANSMIT_CONFIG);
349 if (!port->sendRSTP && port->newInfo &&
350 (port->txCount < TxHoldCount) &&
351 (port->role == RootPort) &&
352 port->helloWhen)
353 return STP_hop_2_state (this, TRANSMIT_TCN);
354 if (port->sendRSTP && port->newInfo &&
355 (port->txCount < TxHoldCount) &&
356 ((port->role == RootPort) ||
357 (port->role == DesignatedPort)))
358 return STP_hop_2_state (this, TRANSMIT_RSTP);
359 break;
360 case TRANSMIT_CONFIG:
361 return STP_hop_2_state (this, IDLE);
362 case TRANSMIT_TCN:
363 return STP_hop_2_state (this, IDLE);
364 case TRANSMIT_RSTP:
365 return STP_hop_2_state (this, IDLE);
366 };
367 return False;
368 }
369