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
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
117static int
118txTcn (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
150static void
151build_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
171static int
172txConfig (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
209static int
210txRstp (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
290void
291STP_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
330Bool
331STP_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