xref: /illumos-gate/usr/src/lib/librstp/common/transmit.c (revision a4faba164aa153855d621c694fc5aa75dd183b81)
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
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
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
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
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
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
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
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