stp_bpdu.c
Go to the documentation of this file.
1 /**
2  * @file stp_bpdu.c
3  * @brief BPDU processing
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2019-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneSTP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL STP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "stp/stp.h"
36 #include "stp/stp_operation.h"
37 #include "stp/stp_bpdu.h"
38 #include "stp/stp_misc.h"
39 #include "debug.h"
40 
41 //Check TCP/IP stack configuration
42 #if (STP_SUPPORT == ENABLED)
43 
44 //Bridge group address (refer to IEEE Std 802.1D-1998, section 7.12.3)
45 const MacAddr STP_BRIDGE_GROUP_ADDR = {{{0x01, 0x80, 0xC2, 0x00, 0x00, 0x00}}};
46 
47 //Protocol versions
49 {
50  {STP_PROTOCOL_VERSION, "STP"}
51 };
52 
53 //BPDU types
55 {
56  {STP_BPDU_TYPE_CONFIG, "CONFIG"},
57  {STP_BPDU_TYPE_TCN, "TCN"}
58 };
59 
60 
61 /**
62  * @brief Process incoming LLC frame
63  * @param[in] interface Underlying network interface
64  * @param[in] ethHeader Pointer to the Ethernet header
65  * @param[in] data Pointer to the LLC frame
66  * @param[in] length Length of the LLC frame, in bytes
67  * @param[in] ancillary Additional options passed to the stack along with
68  * the packet
69  * @param[in] param Pointer to the STP bridge context
70  **/
71 
72 void stpProcessLlcFrame(NetInterface *interface, EthHeader *ethHeader,
73  const uint8_t *data, size_t length, NetRxAncillary *ancillary, void *param)
74 {
75  const LlcHeader *llcHeader;
76  const StpBpdu *bpdu;
77  StpBridgeContext *context;
79 
80  //Point to the STP bridge context
81  context = (StpBridgeContext *) param;
82 
83  //A MAC frame conveying a BPDU carries the Bridge Group Address in the
84  //destination address field (refer to IEEE Std 802.1D-1998, section 8.3.2)
85  if(!macCompAddr(&ethHeader->destAddr, &STP_BRIDGE_GROUP_ADDR))
86  return;
87 
88  //Check the length of the LLC frame
89  if(length < sizeof(LlcHeader))
90  return;
91 
92  //Point to the LLC header
93  llcHeader = (LlcHeader *) data;
94 
95  //The DSAP and SSAP fields must use the standard LLC address assigned to
96  //the Bridge Spanning Tree Protocol (refer to IEEE Std 802.1D-1998, section
97  //7.12.3)
98  if(llcHeader->dsap != STP_LLC_DSAP || llcHeader->ssap != STP_LLC_SSAP ||
99  llcHeader->control != STP_LLC_CTRL)
100  {
101  return;
102  }
103 
104  //Invalid port number?
105  if(ancillary->port < 1 || ancillary->port > context->numPorts)
106  return;
107 
108  //Retrieve the port that matches the specified port number
109  port = &context->ports[ancillary->port - 1];
110 
111  //BPDUs are encapsulated using 802.2 LLC header
112  bpdu = (StpBpdu *) (data + sizeof(LlcHeader));
113 
114  //Retrieve the length of the BPDU
115  length -= sizeof(LlcHeader);
116 
117  //Process incoming BPDU
118  stpProcessBpdu(port, bpdu, length);
119 }
120 
121 
122 /**
123  * @brief Process incoming bridge protocol data unit
124  * @param[in] port Pointer to the bridge port context
125  * @param[in] bpdu Pointer to the received BPDU
126  * @param[in] length Length of the BPDU, in bytes
127  * @return Error code
128  **/
129 
131  size_t length)
132 {
133  //Debug message
134  TRACE_INFO("Port %" PRIu8 ": BPDU received (%" PRIuSIZE " bytes)...\r\n",
135  port->portIndex, length);
136 
137  //Dump BPDU for debugging purpose
138  stpDumpBpdu(bpdu, length);
139 
140  //The BPDU must contain at least four octets
142  return ERROR_INVALID_LENGTH;
143 
144  //The Protocol Identifier must have the value specified for BPDUs
145  if(ntohs(bpdu->protocolId) != STP_PROTOCOL_ID)
146  return ERROR_INVALID_LENGTH;
147 
148  //Check BPDU type
149  if(bpdu->bpduType == STP_BPDU_TYPE_CONFIG)
150  {
151  //A Configuration BPDU must contain at least 35 octets
153  return ERROR_INVALID_LENGTH;
154 
155  //The BPDU's Message Age must be less than its Max Age parameter
156  if(ntohs(bpdu->messageAge) >= ntohs(bpdu->maxAge))
157  return ERROR_INVALID_PACKET;
158 
159  //The Received Configuration BPDU procedure is invoked
161  }
162  else if(bpdu->bpduType == STP_BPDU_TYPE_TCN)
163  {
164  //The Received Topology Change Notification BPDU procedure is invoked
165  stpReceivedTcnBpdu(port, bpdu);
166  }
167  else
168  {
169  //Invalid BPDU received
170  return ERROR_INVALID_TYPE;
171  }
172 
173  //Successful processing
174  return NO_ERROR;
175 }
176 
177 
178 /**
179  * @brief Send bridge protocol data unit
180  * @param[in] port Pointer to the bridge port context
181  * @param[in] bpdu Pointer to the BPDU to be transmitted
182  * @param[in] length Length of the BPDU, in bytes
183  * @return Error code
184  **/
185 
187  size_t length)
188 {
189  error_t error;
190  size_t offset;
191  LlcHeader *llcHeader;
192  NetBuffer *buffer;
193  NetTxAncillary ancillary;
194  StpBridgeContext *context;
195 
196  //Debug message
197  TRACE_INFO("Port %" PRIu8 ": Sending BPDU (%" PRIuSIZE " bytes)...\r\n",
198  port->portIndex, length);
199 
200  //Dump BPDU for debugging purpose
201  stpDumpBpdu(bpdu, length);
202 
203  //Point to the STP bridge context
204  context = port->context;
205 
206  //Allocate a buffer to hold the 802.2 LLC header
207  buffer = ethAllocBuffer(sizeof(LlcHeader), &offset);
208 
209  //Successful memory allocation?
210  if(buffer != NULL)
211  {
212  //Point to the LLC header
213  llcHeader = netBufferAt(buffer, offset);
214 
215  //The DSAP and SSAP fields must use the standard LLC address assigned
216  //to the Bridge Spanning Tree Protocol (refer to IEEE Std 802.1D-1998,
217  //section 7.12.3)
218  llcHeader->dsap = STP_LLC_DSAP;
219  llcHeader->ssap = STP_LLC_SSAP;
220  llcHeader->control = STP_LLC_CTRL;
221 
222  //BPDUs are encapsulated using 802.2 LLC header
223  error = netBufferAppend(buffer, bpdu, length);
224 
225  //Check status code
226  if(!error)
227  {
228  //Calculate the length of the LLC frame
229  length += sizeof(LlcHeader);
230 
231  //Additional options can be passed to the stack along with the packet
232  ancillary = NET_DEFAULT_TX_ANCILLARY;
233  //Specify the source MAC address
234  ancillary.srcMacAddr = port->macAddr;
235  //Specify the destination port
236  ancillary.port = port->portIndex;
237  //BPDUs are transmitted regardless of the port state
238  ancillary.override = TRUE;
239 
240  //The Bridge Group Address is used as destination MAC address to carry
241  //BPDUs between STP entities
242  error = ethSendFrame(context->interface, &STP_BRIDGE_GROUP_ADDR,
243  length, buffer, offset, &ancillary);
244  }
245 
246  //Free previously allocated memory
247  netBufferFree(buffer);
248  }
249  else
250  {
251  //Failed to allocate memory
252  error = ERROR_OUT_OF_MEMORY;
253  }
254 
255  //Return status code
256  return error;
257 }
258 
259 
260 /**
261  * @brief Dump BPDU for debugging purpose
262  * @param[in] bpdu Pointer to the BPDU to dump
263  * @param[in] length Length of the BPDU, in bytes
264  * @return Error code
265  **/
266 
267 error_t stpDumpBpdu(const StpBpdu *bpdu, size_t length)
268 {
269 #if (STP_TRACE_LEVEL >= TRACE_LEVEL_DEBUG)
270  uint32_t t;
271 
272  //The BPDU must contain at least four octets
274  return ERROR_INVALID_LENGTH;
275 
276  //Dump Protocol Identifier
277  TRACE_DEBUG(" Protocol Identifier = %" PRIu16 "\r\n",
278  ntohs(bpdu->protocolId));
279 
280  //Dump Protocol Version Identifier
281  TRACE_DEBUG(" Protocol Version Identifier = %" PRIu8 " (%s)\r\n",
282  bpdu->protocolVersionId, stpGetParamName(bpdu->protocolVersionId,
284 
285  //Dump BPDU Type
286  TRACE_DEBUG(" BPDU Type = 0x%02" PRIX8 " (%s)\r\n", bpdu->bpduType,
288 
289  //Check the length of the BPDU
291  {
292  //Dump Flags
293  stpDumpFlags(bpdu->flags);
294 
295  //Dump Root Identifier
296  TRACE_DEBUG(" Root Identifier = %" PRIu16 " / %s\r\n",
297  ntohs(bpdu->rootId.priority), macAddrToString(&bpdu->rootId.addr, NULL));
298 
299  //Dump Root Path Cost
300  TRACE_DEBUG(" Root Path Cost = %" PRIu32 "\r\n", ntohl(bpdu->rootPathCost));
301 
302  //Dump Bridge Identifier
303  TRACE_DEBUG(" Bridge Identifier = %" PRIu16 " / %s\r\n",
304  ntohs(bpdu->bridgeId.priority), macAddrToString(&bpdu->bridgeId.addr, NULL));
305 
306  //Dump Port Identifier
307  TRACE_DEBUG(" Port Identifier = 0x%04" PRIX16 "\r\n", ntohs(bpdu->portId));
308 
309  //Dump Message Age
310  t = ntohs(bpdu->messageAge) * 1000 / 256;
311  TRACE_DEBUG(" Message Age = %" PRIu32 ".%03" PRIu32 "\r\n", t / 1000, t % 1000);
312 
313  //Dump Max Age
314  t = ntohs(bpdu->maxAge) * 1000 / 256;
315  TRACE_DEBUG(" Max Age = %" PRIu32 ".%03" PRIu32 "\r\n", t / 1000, t % 1000);
316 
317  //Dump Hello Time
318  t = ntohs(bpdu->helloTime) * 1000 / 256;
319  TRACE_DEBUG(" Hello Time = %" PRIu32 ".%03" PRIu32 "\r\n", t / 1000, t % 1000);
320 
321  //Dump Forward Delay
322  t = ntohs(bpdu->forwardDelay) * 1000 / 256;
323  TRACE_DEBUG(" Forward Delay = %" PRIu32 ".%03" PRIu32 "\r\n", t / 1000, t % 1000);
324  }
325 #endif
326 
327  //Successful processing
328  return NO_ERROR;
329 }
330 
331 
332 /**
333  * @brief Dump Flags field for debugging purpose
334  * @param[in] flags Value of the Flags field
335  **/
336 
337 void stpDumpFlags(uint8_t flags)
338 {
339  //Dump Flags field
340  TRACE_DEBUG(" Flags = 0x%02" PRIX8, flags);
341 
342  //Check whether any flag is set
343  if(flags != 0)
344  {
345  //Debug message
346  TRACE_DEBUG(" (");
347 
348  //Decode flags
349  while(flags != 0)
350  {
351  if((flags & STP_BPDU_FLAG_TC_ACK) != 0)
352  {
353  //The Topology Change Acknowledgment flag is set
354  TRACE_DEBUG("TcAck");
355  //Clear flag
357  }
358  else if((flags & STP_BPDU_FLAG_TC) != 0)
359  {
360  //The Topology Change flag is set
361  TRACE_DEBUG("Tc");
362  //Clear flag
364  }
365 
366  //Any other flag set?
367  if(flags != 0)
368  {
369  TRACE_DEBUG(", ");
370  }
371  }
372 
373  //Debug message
374  TRACE_DEBUG(")");
375  }
376 
377  //Terminate with a line feed
378  TRACE_DEBUG("\r\n");
379 }
380 
381 #endif
#define PRIuSIZE
#define ntohl(value)
Definition: cpu_endian.h:422
#define ntohs(value)
Definition: cpu_endian.h:421
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
#define TRACE_INFO(...)
Definition: debug.h:95
uint16_t port
Definition: dns_common.h:267
error_t
Error codes.
Definition: error.h:43
@ ERROR_INVALID_TYPE
Definition: error.h:115
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
@ ERROR_INVALID_PACKET
Definition: error.h:140
@ ERROR_INVALID_LENGTH
Definition: error.h:111
error_t ethSendFrame(NetInterface *interface, const MacAddr *destAddr, uint16_t type, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send an Ethernet frame.
Definition: ethernet.c:399
char_t * macAddrToString(const MacAddr *macAddr, char_t *str)
Convert a MAC address to a dash delimited string.
Definition: ethernet.c:917
NetBuffer * ethAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold an Ethernet frame.
Definition: ethernet.c:775
LlcHeader
Definition: ethernet.h:235
uint8_t data[]
Definition: ethernet.h:222
#define macCompAddr(macAddr1, macAddr2)
Definition: ethernet.h:130
EthHeader
Definition: ethernet.h:223
MacAddr
Definition: ethernet.h:195
uint8_t t
Definition: lldp_ext_med.h:212
#define NetInterface
Definition: net.h:36
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:415
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
error_t netBufferAppend(NetBuffer *dest, const void *src, size_t length)
Append data a multi-part buffer.
Definition: net_mem.c:588
const NetTxAncillary NET_DEFAULT_TX_ANCILLARY
Definition: net_misc.c:71
#define NetRxAncillary
Definition: net_misc.h:40
#define NetTxAncillary
Definition: net_misc.h:36
#define arraysize(a)
Definition: os_port.h:71
#define TRUE
Definition: os_port.h:50
STP (Spanning Tree Protocol)
#define StpBridgeContext
Definition: stp.h:36
#define StpBridgePort
Definition: stp.h:40
error_t stpDumpBpdu(const StpBpdu *bpdu, size_t length)
Dump BPDU for debugging purpose.
Definition: stp_bpdu.c:267
const MacAddr STP_BRIDGE_GROUP_ADDR
Definition: stp_bpdu.c:45
const StpParamName stpBpduTypes[]
Definition: stp_bpdu.c:54
error_t stpProcessBpdu(StpBridgePort *port, const StpBpdu *bpdu, size_t length)
Process incoming bridge protocol data unit.
Definition: stp_bpdu.c:130
error_t stpSendBpdu(StpBridgePort *port, const StpBpdu *bpdu, size_t length)
Send bridge protocol data unit.
Definition: stp_bpdu.c:186
void stpDumpFlags(uint8_t flags)
Dump Flags field for debugging purpose.
Definition: stp_bpdu.c:337
void stpProcessLlcFrame(NetInterface *interface, EthHeader *ethHeader, const uint8_t *data, size_t length, NetRxAncillary *ancillary, void *param)
Process incoming LLC frame.
Definition: stp_bpdu.c:72
const StpParamName stpProtocolVersions[]
Definition: stp_bpdu.c:48
BPDU processing.
@ STP_BPDU_TYPE_CONFIG
Definition: stp_bpdu.h:57
@ STP_BPDU_TYPE_TCN
Definition: stp_bpdu.h:58
StpBpdu
Definition: stp_bpdu.h:99
#define STP_CONFIG_BPDU_SIZE
Definition: stp_bpdu.h:39
@ STP_BPDU_FLAG_TC
Definition: stp_bpdu.h:68
@ STP_BPDU_FLAG_TC_ACK
Definition: stp_bpdu.h:69
#define STP_LLC_DSAP
Definition: stp_common.h:83
#define STP_PROTOCOL_ID
Definition: stp_common.h:80
#define STP_LLC_CTRL
Definition: stp_common.h:85
@ STP_PROTOCOL_VERSION
STP version.
Definition: stp_common.h:97
#define STP_MIN_BPDU_SIZE
Definition: stp_common.h:88
#define STP_LLC_SSAP
Definition: stp_common.h:84
const char_t * stpGetParamName(uint_t value, const StpParamName *paramList, size_t paramListLen)
Convert a parameter to string representation.
Definition: stp_misc.c:701
STP helper functions.
void stpReceivedTcnBpdu(StpBridgePort *port, const StpBpdu *bpdu)
Received Topology Change Notification BPDU (8.7.2)
void stpReceivedConfigBpdu(StpBridgePort *port, const StpBpdu *bpdu)
Received Configuration BPDU (8.7.1)
Definition: stp_operation.c:52
Operation of the protocol.
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
Parameter value/name binding.
Definition: stp_misc.h:48
uint8_t length
Definition: tcp.h:368
uint8_t flags
Definition: tcp.h:351