]> git.ipfire.org Git - people/ms/rstp.git/blame - rstplib/transmit.c
RSTP testing - PATCH: source MAC address of BPDU
[people/ms/rstp.git] / rstplib / transmit.c
CommitLineData
ad02a0eb
SH
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
46typedef 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
52typedef 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
59typedef 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
68static 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
95static size_t
96build_bpdu_header (int port_index,
97 unsigned char bpdu_type,
98 unsigned short pkt_len)
99{
100 unsigned short len8023;
101
ad02a0eb 102 STP_OUT_get_port_mac (port_index, bpdu_packet.mac.src_mac);
ad02a0eb
SH
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 memcpy (&bpdu_packet.eth.len8023, &len8023, 2);
112
113#ifdef ORIG
114 if (pkt_len < MIN_FRAME_LENGTH) pkt_len = MIN_FRAME_LENGTH;
115#else
116 /* Don't do this. LLC puts in 802.3 length based on what we transmit */
117#endif
118 return pkt_len;
119}
120
121static int
122txTcn (STATE_MACH_T* this)
123{ /* 17.19.17 (page 68) & 9.3.2 (page 25) */
124 register size_t pkt_len;
125 register int port_index, vlan_id;
126
127#ifdef STP_DBG
128 if (this->owner.port->skip_tx > 0) {
129 if (1 == this->owner.port->skip_tx)
130 stp_trace ("port %s stop tx skipping",
131 this->owner.port->port_name);
132 this->owner.port->skip_tx--;
133 return STP_Nothing_To_Do;
134 }
135#endif
136
137 if (this->owner.port->admin_non_stp) return 1;
138 port_index = this->owner.port->port_index;
139 vlan_id = this->owner.port->owner->vlan_id;
140
141 pkt_len = build_bpdu_header (port_index,
142 BPDU_TOPO_CHANGE_TYPE,
143 sizeof (BPDU_HEADER_T));
144
145#ifdef STP_DBG
146 if (this->debug)
147 stp_trace ("port %s txTcn", this->owner.port->port_name);
148#endif
149 return STP_OUT_tx_bpdu (port_index, vlan_id,
150 (unsigned char *) &bpdu_packet,
151 pkt_len);
152}
153
154static void
155build_config_bpdu (PORT_T* port, Bool set_topo_ack_flag)
156{
157 bpdu_packet.body.flags = 0;
158 if (port->tcWhile) {
159#ifdef STP_DBG
160 if (port->topoch->debug)
161 stp_trace ("tcWhile=%d =>tx TOLPLOGY_CHANGE_BIT to port %s",
162 (int) port->tcWhile, port->port_name);
163#endif
164 bpdu_packet.body.flags |= TOLPLOGY_CHANGE_BIT;
165 }
166
167 if (set_topo_ack_flag && port->tcAck) {
168 bpdu_packet.body.flags |= TOLPLOGY_CHANGE_ACK_BIT;
169 }
170
171 STP_VECT_set_vector (&port->portPrio, &bpdu_packet.body);
172 STP_set_times (&port->portTimes, &bpdu_packet.body);
173}
174
175static int
176txConfig (STATE_MACH_T* this)
177{/* 17.19.15 (page 67) & 9.3.1 (page 23) */
178 register size_t pkt_len;
179 register PORT_T* port = NULL;
180 register int port_index, vlan_id;
181
182#ifdef STP_DBG
183 if (this->owner.port->skip_tx > 0) {
184 if (1 == this->owner.port->skip_tx)
185 stp_trace ("port %s stop tx skipping",
186 this->owner.port->port_name);
187 this->owner.port->skip_tx--;
188 return STP_Nothing_To_Do;
189 }
190#endif
191
192 port = this->owner.port;
193 if (port->admin_non_stp) return 1;
194 port_index = port->port_index;
195 vlan_id = port->owner->vlan_id;
196
197 pkt_len = build_bpdu_header (port->port_index,
198 BPDU_CONFIG_TYPE,
199 sizeof (BPDU_HEADER_T) + sizeof (BPDU_BODY_T));
200 build_config_bpdu (port, True);
201
202#ifdef STP_DBG
203 if (this->debug)
204 stp_trace ("port %s txConfig flags=0X%lx",
205 port->port_name,
206 (unsigned long) bpdu_packet.body.flags);
207#endif
208 return STP_OUT_tx_bpdu (port_index, vlan_id,
209 (unsigned char *) &bpdu_packet,
210 pkt_len);
211}
212
213static int
214txRstp (STATE_MACH_T* this)
215{/* 17.19.16 (page 68) & 9.3.3 (page 25) */
216 register size_t pkt_len;
217 register PORT_T* port = NULL;
218 register int port_index, vlan_id;
219 unsigned char role;
220
221#ifdef STP_DBG
222 if (this->owner.port->skip_tx > 0) {
223 if (1 == this->owner.port->skip_tx)
224 stp_trace ("port %s stop tx skipping",
225 this->owner.port->port_name);
226 else
227 stp_trace ("port %s skip tx %d",
228 this->owner.port->port_name, this->owner.port->skip_tx);
229
230 this->owner.port->skip_tx--;
231 return STP_Nothing_To_Do;
232 }
233#endif
234
235 port = this->owner.port;
236 if (port->admin_non_stp) return 1;
237 port_index = port->port_index;
238 vlan_id = port->owner->vlan_id;
239
240 pkt_len = build_bpdu_header (port->port_index,
241 BPDU_RSTP,
242 sizeof (BPDU_HEADER_T) + sizeof (BPDU_BODY_T) + 1);
243 build_config_bpdu (port, False);
244
245 switch (port->selectedRole) {
246 default:
247 case DisabledPort:
248 role = RSTP_PORT_ROLE_UNKN;
249 break;
250 case AlternatePort:
251 role = RSTP_PORT_ROLE_ALTBACK;
252 break;
253 case BackupPort:
254 role = RSTP_PORT_ROLE_ALTBACK;
255 break;
256 case RootPort:
257 role = RSTP_PORT_ROLE_ROOT;
258 break;
259 case DesignatedPort:
260 role = RSTP_PORT_ROLE_DESGN;
261 break;
262 }
263
264 bpdu_packet.body.flags |= (role << PORT_ROLE_OFFS);
265#ifndef ORIG
266 if (port->forwarding)
267 bpdu_packet.body.flags |= FORWARD_BIT;
268 if (port->learning)
269 bpdu_packet.body.flags |= LEARN_BIT;
270#endif
271 if (port->synced) {
272#if 0 /* def STP_DBG */
273 if (port->roletrns->debug)
274 stp_trace ("tx AGREEMENT_BIT to port %s", port->port_name);
275#endif
276 bpdu_packet.body.flags |= AGREEMENT_BIT;
277 }
278
279 if (port->proposing) {
280#if 0 /* def STP_DBG */
281 if (port->roletrns->debug)
282 stp_trace ("tx PROPOSAL_BIT to port %s", port->port_name);
283#endif
284 bpdu_packet.body.flags |= PROPOSAL_BIT;
285 }
286
287#ifdef STP_DBG
288 if (this->debug)
289 stp_trace ("port %s txRstp flags=0X%lx",
290 port->port_name,
291 (unsigned long) bpdu_packet.body.flags);
292#endif
293
294 return STP_OUT_tx_bpdu (port_index, vlan_id,
295 (unsigned char *) &bpdu_packet,
296 pkt_len);
297}
298
299void
300STP_transmit_enter_state (STATE_MACH_T* this)
301{
302 register PORT_T* port = this->owner.port;
303
304 switch (this->State) {
305 case BEGIN:
306 case TRANSMIT_INIT:
307 port->newInfo = False;
308 port->helloWhen = 0;
309 port->txCount = 0;
310 break;
311 case TRANSMIT_PERIODIC:
312 port->newInfo = port->newInfo ||
313 ((port->role == DesignatedPort) ||
314 ((port->role == RootPort) && port->tcWhile));
315 port->helloWhen = port->owner->rootTimes.HelloTime;
316 break;
317 case IDLE:
318 break;
319 case TRANSMIT_CONFIG:
320 port->newInfo = False;
321 txConfig (this);
322 port->txCount++;
323 port->tcAck = False;
324 break;
325 case TRANSMIT_TCN:
326 port->newInfo = False;
327 txTcn (this);
328 port->txCount++;
329 break;
330 case TRANSMIT_RSTP:
331 port->newInfo = False;
332 txRstp (this);
333 port->txCount++;
334 port->tcAck = False;
335 break;
336 };
337}
338
339Bool
340STP_transmit_check_conditions (STATE_MACH_T* this)
341{
342 register PORT_T* port = this->owner.port;
343
344 if (BEGIN == this->State) return STP_hop_2_state (this, TRANSMIT_INIT);
345
346 switch (this->State) {
347 case TRANSMIT_INIT:
348 return STP_hop_2_state (this, IDLE);
349 case TRANSMIT_PERIODIC:
350 return STP_hop_2_state (this, IDLE);
351 case IDLE:
352 if (!port->helloWhen) return STP_hop_2_state (this, TRANSMIT_PERIODIC);
353 if (!port->sendRSTP && port->newInfo &&
354 (port->txCount < TxHoldCount) &&
355 (port->role == DesignatedPort) &&
356 port->helloWhen)
357 return STP_hop_2_state (this, TRANSMIT_CONFIG);
358 if (!port->sendRSTP && port->newInfo &&
359 (port->txCount < TxHoldCount) &&
360 (port->role == RootPort) &&
361 port->helloWhen)
362 return STP_hop_2_state (this, TRANSMIT_TCN);
363 if (port->sendRSTP && port->newInfo &&
364 (port->txCount < TxHoldCount) &&
365 ((port->role == RootPort) ||
366 (port->role == DesignatedPort)))
367 return STP_hop_2_state (this, TRANSMIT_RSTP);
368 break;
369 case TRANSMIT_CONFIG:
370 return STP_hop_2_state (this, IDLE);
371 case TRANSMIT_TCN:
372 return STP_hop_2_state (this, IDLE);
373 case TRANSMIT_RSTP:
374 return STP_hop_2_state (this, IDLE);
375 };
376 return False;
377}
378