# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. # include $(SRC)/lib/Makefile.lib HDRS = stp_bpdu.h stp_in.h stp_vectors.h uid_stp.h HDRDIR = common # Hammerhead: amd64-only SUBDIRS = $(MACH64) all : TARGET = all clean : TARGET = clean clobber : TARGET = clobber install : TARGET = install .KEEP_STATE: all clean clobber install: $(SUBDIRS) install_h: $(ROOTHDRS) check: $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: include $(SRC)/lib/Makefile.targ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. LIBRARY = librstp.a VERS = .1 OBJECTS = edge.o migrate.o p2p.o pcost.o port.o portinfo.o rolesel.o \ roletrns.o statmch.o stp_in.o stpm.o stpmgmt.o sttrans.o \ times.o topoch.o transmit.o vector.o include ../../Makefile.lib LIBS = $(DYNLIB) SRCDIR = ../common SRCS = $(OBJECTS:%.o=$(SRCDIR)/%.c) LDLIBS += -lc CFLAGS += $(CCVERBOSE) CPPFLAGS += -I$(SRCDIR) -D__SUN__ -D__STP_INTERNAL__ # not linted SMATCH=off .KEEP_STATE: all: $(LIBS) include ../../Makefile.targ # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2025 Hammerhead Project # include ../Makefile.com include $(SRC)/lib/Makefile.lib.64 install: all $(ROOTLIBS64) $(ROOTLINKS64) GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! SPANNING TREE SOFTWARE ChangeLog ========= james.d.carlson@sun.com - Removed bits and pieces not needed for use as a stand-alone library. - Fixed two unreachable statements (statmch.c and stpm.c). - Wrote Makefile for use with ON. - Changed rstplib into a real library; expecting the caller to provide symbol definitions is just wrong. ## - $Log: ChangeLog,v $ - Revision 1.7 2002/01/20 07:34:36 ralex - - 'clilib' has been changed for 'readline' version 4.2 (thanks to - Michael Rozhavsky ) - - 'The bug in clilib has been fixed - - Revision 1.6 2002/01/09 06:58:08 ralex - The first version is to outcome - - Revision 1.5 2001/11/29 09:54:31 ralex - - The bug in "Port Role Transitions state machine" has been - fixed (hop to the DESIGNATED_LISTEN) - - Defaults for 'bridge' and 'ports' configuration have - been moved; order of the initialization has been changed - - Revision 1.4 2001/11/26 08:02:12 ralex - All management entities from 14.8.1 & 14.8.2 are now supported - - Revision 1.3 2001/11/21 14:32:27 ralex - The file ChangeLog has been 'synchrinyzed' - - Revision 1.2 2001/11/21 14:22:12 ralex - - In the librstp.a : drastic change in Port Role Selection - state machine as a result of posting - of Mick Seaman and implementation of 802.1y Z1 - - In libcli.c : readline completion works; for it - the structure CMD_DSCR_T has been redisgned - and the languages (both in 'mngr' and in 'bridge') - have been changed - - Revision 1.1 2001/11/14 14:10:44 ralex - - All per Port variables have been moved from the State - Machines into the Port instance (it made the state - machines much more clear) - - - In libcli.a instead of stupid fgets() function we use - now readline (thanks to Michel Roshavsky) - - - 'mcheck' support - - - 'nonStp' support (I know, that it is out the standard, - but it seems to be useful (see a discussion on - http://www1.ietf.org/mail-archive/working-groups/bridge/current/msg00038.html) - and our customers demand it - - - The function rolesel.c has been drastically fixed, IMHO - closer to the standard - - - Nicer output ## The Rapid Spanning Tree Library project contains a full implementation of 802.1s as an library with API. There is two processes, using this library: 'bridge' & 'mngr'. First simulates RSTP bridge behavior, second is dedicated to link/unlink 'bridges' into virtual RSTP domain (VRSTPD). Both 'bridge' & 'mngr' has its own simple CLI like language of commands; these commands allow to manage the VRSTPD. There are tools to trace state machine transitions and get traps about drastic changes. Purpose: studying, debugging, development. The library may be used in real bridges/routers while bounding to a real system depending environment. To run: 1. In one shell run 'mngr' ./mngr You will get prompt of 'mngr'; type '?' and get full help of 'mngr' commands. 2. In another shell run bridge instance. ./bridge You will get prompt of 'bridge'; type '?' and get full help of bridge management commands. 3. You may (and should) run a number of bridge instances, each in its separate shell (this way you will be able manage them). For example, if there were two bridge instances, you may see examples of the dialog in files mngr.txt, B5055.txt and B5056.txt. Note: prompt both of 'mngr' and of 'bridge' instance contains time stamp, while all these process run onto the same computer, these time stamps are synchronized. Anonymous CVS Access This project's SourceForge CVS repository can be checked out through anonymous (pserver) CVS with the following instruction set. The module you wish to check out must be specified as the modulename. When prompted for a password for anonymous, simply press the Enter key. cvs -d:pserver:anonymous@cvs.rstplib.sourceforge.net:/cvsroot/rstplib login cvs -z3 -d:pserver:anonymous@cvs.rstplib.sourceforge.net:/cvsroot/rstplib co modulename Updates from within the module's directory do not need the -d parameter. Developer CVS Access via SSH Only project developers can access the CVS tree via this method. SSH1 must be installed on your client machine. Substitute modulename and developername with the proper values. Enter your site password when prompted. export CVS_RSH=ssh cvs -z3 -d:ext:developername@cvs.rstplib.sourceforge.net:/cvsroot/rstplib co modulename Alex Rozin Michael Rozhavsky This guide describes the list of the files of the project. ========================================================== There are two target binaries: mngr & bridge o The first is a simplest tools to connect/disconnect bridges and check their current connection. These its functions are managed from command line simple language and use the library libcli.a (see below). Beside it mngr serves to transport BPDU messages between bridges; for this purpose mngr uses the library libuid.a The source code of the mngr: file mngr.c o The second is a simulation of virtual RSTP bridge. It accepts two types of messages: UID_CNTRL & UID_BPDU (see file uid.h). This program is linked with the same two libraries libcli.a & libuid.a; beside it uses a system independent librstp.a: implementation of Rapid Spanning Tree (802.1w) - see below. The source code of the bridge: files bridge.c,stp_cli.c, stp_to.c * bridge.c - simulates the main bridge behavior * stp_cli.c - consists from command line functions * stp_to.c - API, that librstp.a uses for its purposes. The management communication between bridge and librstp.a uses structures and definitions from the header uid_stp.h o libcli.a - library for command line features. It has only one file cli.c, the API is described in the header cli.h. o libuid.a - the 'transport' library: the source code you may find in the file uid_sock.c and in the two headers: uid.h & uid_sock.h o (so far, so good) librstp.a - it is a heart of the project Actually, it implements 802.1w state machines. Files stpm.c - the RSTP instance (some reflection of Port0) port.c - the RSTP port instance portinfo.c - Port Information State Machine, 17.21 rolesel.c - Port Role Selection State Machine, 17.22 roletrns.c - Port Role Transition State Machine, 17.23 sttrans.c - Port State Transition State Machine, 17.24 topoch.c - Topology Change State Machine, 17.25 migrate.c - Port Protocol Migration State Machine, 17.26 transmit.c - Port Transmit State Machine 17.27 pcost.c - Path Cost Resolution State Machine edge.c - operEdge Port Resolution State Machine p2p.c - operPointToPoit Resolution State Machine statmch.c - generic state machine implementation vector.c - Priority Vectors manipulations times.c - Times manipulations stp_in.c - API for calls from outside. sttrans.c - API for calls from outside (dedicated for creation deleting, starting & stopping the RSTP instance) less relevant to the project. - All per Port variables have been moved from the State Machines into the Port instance (it made the state machines much more clear) - In libcli.a instead of stupid fgets() function we use now readline (thanks to Michel Roshavsky) - 'mcheck' support - 'nonStp' support (I know, that it is out the standard, but it seems to be useful (see a discussion on http://www1.ietf.org/mail-archive/working-groups/bridge/current/msg00038.html) and our customers demand it - The function rolesel.c has been drastically fixed, IMHO closer to the standard - Nicer output This guide describes our plans. ============================== Volunteers are welcomed ! ========================== 1. To support a full set of management features from 14.8 (like timeSince_Topo_Change, 14.8.1.1.3.b) 2. To send traps about topology changes (as call to stp_to.c) 3. To rewrite edge.c for more exact correspondence with the standard (now portEnabled variable is realized in 'dirty' way), simulate MAC Operational and MAC Enabled. 4. To support SNMP management via AgentX 5. To move the project in he direction toward 802.1s (MSTP) /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Mutual RSTP definitions */ #ifndef _STP_BASE_H__ #define _STP_BASE_H__ #include #include #define STP_DBG 1 #if defined(__LINUX__) || defined(__SUN__) # include # include # include # include "uid_stp.h" #else # include # include "comdef.h" # include "comdef.x" # include "Bitmap/bitmap.h" # include "Bitmap/bitmap.x" # include "Ui/uid_stp.h" #endif #ifndef INOUT # define IN /* consider as comments near 'input' parameters */ # define OUT /* consider as comments near 'output' parameters */ # define INOUT /* consider as comments near 'input/output' parameters */ #endif #ifndef Zero # define Zero 0 # define One 1 #endif #ifndef Bool # define Bool int # define False 0 # define True 1 #endif #include "stp_bpdu.h" #include "vector.h" #include "times.h" #define RSTP_ERRORS { \ CHOOSE(STP_OK), \ CHOOSE(STP_Cannot_Find_Vlan), \ CHOOSE(STP_Implicit_Instance_Create_Failed), \ CHOOSE(STP_Small_Bridge_Priority), \ CHOOSE(STP_Large_Bridge_Priority), \ CHOOSE(STP_Small_Hello_Time), \ CHOOSE(STP_Large_Hello_Time), \ CHOOSE(STP_Small_Max_Age), \ CHOOSE(STP_Large_Max_Age), \ CHOOSE(STP_Small_Forward_Delay), \ CHOOSE(STP_Large_Forward_Delay), \ CHOOSE(STP_Forward_Delay_And_Max_Age_Are_Inconsistent),\ CHOOSE(STP_Hello_Time_And_Max_Age_Are_Inconsistent), \ CHOOSE(STP_Vlan_Had_Not_Yet_Been_Created), \ CHOOSE(STP_Port_Is_Absent_In_The_Vlan), \ CHOOSE(STP_Big_len8023_Format), \ CHOOSE(STP_Small_len8023_Format), \ CHOOSE(STP_len8023_Format_Gt_Len), \ CHOOSE(STP_Not_Proper_802_3_Packet), \ CHOOSE(STP_Invalid_Protocol), \ CHOOSE(STP_Invalid_Version), \ CHOOSE(STP_Had_Not_Yet_Been_Enabled_On_The_Vlan), \ CHOOSE(STP_Cannot_Create_Instance_For_Vlan), \ CHOOSE(STP_Cannot_Create_Instance_For_Port), \ CHOOSE(STP_Invalid_Bridge_Priority), \ CHOOSE(STP_There_Are_No_Ports), \ CHOOSE(STP_Cannot_Compute_Bridge_Prio), \ CHOOSE(STP_Another_Error), \ CHOOSE(STP_Nothing_To_Do), \ CHOOSE(STP_No_Such_State_Machine), \ CHOOSE(STP_LAST_DUMMY) \ } #define CHOOSE(a) a typedef enum RSTP_ERRORS RSTP_ERRORS_T; #undef CHOOSE #if !defined(__LINUX__) && !defined(__SUN__) extern char* strdup (const char *s); extern USHORT Ntohs (USHORT n); extern ULONG Htonl (ULONG h); extern USHORT Htons (USHORT h); extern ULONG Ntohl (ULONG n); #define htonl Htonl #define htons Htons #define ntohl Ntohl #define ntohs Ntohs #endif #if defined(__LINUX__) || defined(__SUN__) #ifdef STP_DBG #define STP_FATAL(TXT, MSG, EXCOD) \ {stp_trace ("FATAL:%s failed: %s:%d", TXT, MSG, EXCOD); \ exit (EXCOD);} #else #define STP_FATAL(TXT, MSG, EXCOD) \ abort(); #endif #else #define STP_FATAL(TXT, MSG, EXCOD) \ printf("FATAL: %s code %s:%d\n", TXT, MSG, EXCOD) #endif #define STP_MALLOC(PTR, TYPE, MSG) \ { \ PTR = (TYPE*) calloc (1, sizeof (TYPE)); \ if (! PTR) { \ STP_FATAL("malloc", MSG, -6); \ } \ } #define STP_FREE(PTR, MSG) \ { \ if (! PTR) { \ STP_FATAL("free", MSG, -66); \ } \ free (PTR); \ PTR = NULL; \ } #define STP_STRDUP(PTR, SRC, MSG) \ { \ PTR = strdup (SRC); \ if (! PTR) { \ STP_FATAL("strdup", MSG, -7); \ } \ } #define STP_NEW_IN_LIST(WHAT, TYPE, LIST, MSG) \ { \ STP_MALLOC(WHAT, TYPE, MSG); \ WHAT->next = LIST; \ LIST = WHAT; \ } /* for debug trace messages */ #ifdef STP_DBG #if defined(__LINUX__) extern char* sprint_time_stump (void); #define stp_trace(F, B...) printf("%s:" F "\n", sprint_time_stump(), ##B) #elif defined(__SUN__) #define stp_trace (*stp_vectors->trace) #else extern ULONG stp_trace (const char* fmt, ...); #endif #else /* !STP_DBG */ #define stp_trace(F, B...) ((void)0) #endif /* STP_DBG */ /* Inner usage definitions & functions */ #if defined(__LINUX__) || defined(__SUN__) # define RSTP_INIT_CRITICAL_PATH_PROTECTIO # define RSTP_CRITICAL_PATH_START # define RSTP_CRITICAL_PATH_END #else # define RSTP_INIT_CRITICAL_PATH_PROTECTIO STP_OUT_psos_init_semaphore () # define RSTP_CRITICAL_PATH_START STP_OUT_psos_close_semaphore () # define RSTP_CRITICAL_PATH_END STP_OUT_psos_open_semaphore () extern void STP_OUT_psos_init_semaphore (void); extern void STP_OUT_psos_close_semaphore (void); extern void STP_OUT_psos_open_semaphore (void); #endif #endif /* _STP_BASE_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ #ifndef _STP_CHOOSE_H__ #define _STP_CHOOSE_H__ /* State machines states & debug tools. Sorry, if these are no readable enogth :( */ #define CHOOSE(a) a typedef enum STATES THE_STATE_T; #undef CHOOSE char * GET_STATE_NAME (int state) { #define CHOOSE(a) #a static char *state_names[] = STATES; #undef CHOOSE if (BEGIN == state) return "Begin"; return state_names[state]; } #endif /* _STP_CHOOSE_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Note: this state mashine distinkts from described in P802.1t Clause 18. */ /* I am ready to discuss it */ #include "base.h" #include "stpm.h" #include "stp_vectors.h" #define STATES { \ CHOOSE(DISABLED), \ CHOOSE(DETECTED), \ CHOOSE(DELAYED), \ CHOOSE(RESOLVED) \ } #define GET_STATE_NAME STP_edge_get_state_name #include "choose.h" #define DEFAULT_LINK_DELAY 3 void STP_edge_enter_state (STATE_MACH_T *s) { register PORT_T *port = s->owner.port; switch (s->State) { case BEGIN: break; case DISABLED: port->operEdge = port->adminEdge; port->wasInitBpdu = False; port->lnkWhile = 0; port->portEnabled = False; break; case DETECTED: port->portEnabled = True; port->lnkWhile = port->LinkDelay; port->operEdge = False; break; case DELAYED: break; case RESOLVED: if (! port->wasInitBpdu) { port->operEdge = port->adminEdge; } break; } } Bool STP_edge_check_conditions (STATE_MACH_T *s) { register PORT_T *port = s->owner.port; /* If we're disabled, then stay that way. */ if (!port->adminEnable) { if (s->State == DISABLED) return False; else return STP_hop_2_state (s, DISABLED); } switch (s->State) { case BEGIN: return STP_hop_2_state (s, DISABLED); case DISABLED: if (port->adminEnable) { return STP_hop_2_state (s, DETECTED); } break; case DETECTED: return STP_hop_2_state (s, DELAYED); case DELAYED: if (port->wasInitBpdu) { #ifdef STP_DBG if (s->debug) stp_trace ("port %s 'edge' resolved by BPDU", port->port_name); #endif return STP_hop_2_state (s, RESOLVED); } if (! port->lnkWhile) { #ifdef STP_DBG if (s->debug) stp_trace ("port %s 'edge' resolved by timer", port->port_name); #endif return STP_hop_2_state (s, RESOLVED); } break; case RESOLVED: break; } return False; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Note: this state mashine distinkts from described in P802.1t Clause 18. */ /* I am ready to discuss it */ #ifndef _STP_EDGE_H__ #define _STP_EDGE_H__ void STP_edge_enter_state (STATE_MACH_T* s); Bool STP_edge_check_conditions (STATE_MACH_T* s); char* STP_edge_get_state_name (int state); #endif /* _STP_EDGE_H__ */ # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. # # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION SUNWprivate_1.1 { global: STP_IN_init; STP_IN_stpm_create; STP_IN_stpm_delete; STP_IN_stop_all; STP_IN_delete_all; STP_IN_get_is_stpm_enabled; STP_IN_stpm_get_vlan_id_by_name; STP_IN_stpm_get_name_by_vlan_id; STP_IN_get_error_explanation; STP_IN_stpm_get_cfg; STP_IN_stpm_get_state; STP_IN_port_get_cfg; STP_IN_port_set_cfg; STP_IN_port_get_state; STP_IN_stpm_set_cfg; STP_IN_one_second; STP_IN_enable_port; STP_IN_changed_port_speed; STP_IN_changed_port_duplex; STP_IN_check_bpdu_header; STP_IN_rx_bpdu; STP_IN_dbg_set_port_trace; STP_IN_port_add; STP_IN_port_remove; STP_IN_get_bridge_id; STP_IN_state2str; local: *; }; /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Port Protocol Migration state machine : 17.26 */ #include "base.h" #include "stpm.h" #define STATES { \ CHOOSE(INIT), \ CHOOSE(SEND_RSTP), \ CHOOSE(SENDING_RSTP), \ CHOOSE(SEND_STP), \ CHOOSE(SENDING_STP) \ } #define GET_STATE_NAME STP_migrate_get_state_name #include "choose.h" #define MigrateTime 3 /* 17,16.4 */ void STP_migrate_enter_state (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; switch (this->State) { case BEGIN: case INIT: port->initPm = True; port->mcheck = False; break; case SEND_RSTP: port->mdelayWhile = MigrateTime; port->mcheck = port->initPm = False; port->sendRSTP = True; break; case SENDING_RSTP: port->rcvdRSTP = port->rcvdSTP = False; break; case SEND_STP: port->mdelayWhile = MigrateTime; port->sendRSTP = False; port->initPm = False; break; case SENDING_STP: port->rcvdRSTP = port->rcvdSTP = False; break; } } Bool STP_migrate_check_conditions (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; if ((!port->portEnabled && !port->initPm) || BEGIN == this->State) return STP_hop_2_state (this, INIT); switch (this->State) { case INIT: if (port->portEnabled) { return STP_hop_2_state (this, (port->owner->ForceVersion >= 2) ? SEND_RSTP : SEND_STP); } break; case SEND_RSTP: return STP_hop_2_state (this, SENDING_RSTP); case SENDING_RSTP: if (port->mcheck) return STP_hop_2_state (this, SEND_RSTP); if (port->mdelayWhile && (port->rcvdSTP || port->rcvdRSTP)) { return STP_hop_2_state (this, SENDING_RSTP); } if (!port->mdelayWhile && port->rcvdSTP) { return STP_hop_2_state (this, SEND_STP); } if (port->owner->ForceVersion < 2) { return STP_hop_2_state (this, SEND_STP); } break; case SEND_STP: return STP_hop_2_state (this, SENDING_STP); case SENDING_STP: if (port->mcheck) return STP_hop_2_state (this, SEND_RSTP); if (port->mdelayWhile && (port->rcvdSTP || port->rcvdRSTP)) return STP_hop_2_state (this, SENDING_STP); if (!port->mdelayWhile && port->rcvdRSTP) return STP_hop_2_state (this, SEND_RSTP); break; } return False; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Port Protocol Migration state machine : 17.26 */ #ifndef _STP_MIGRATE_H__ #define _STP_MIGRATE_H__ void STP_migrate_enter_state (STATE_MACH_T* s); Bool STP_migrate_check_conditions (STATE_MACH_T* s); char* STP_migrate_get_state_name (int state); #endif /* _STP_MIGRATE_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Point To Point MAC mode selection machine : 6.4.3, 6.5.1 */ #include "base.h" #include "stpm.h" #include "stp_to.h" /* for STP_OUT_get_duplex */ #define STATES { \ CHOOSE(INIT), \ CHOOSE(RECOMPUTE), \ CHOOSE(STABLE) \ } #define GET_STATE_NAME STP_p2p_get_state_name #include "choose.h" static Bool computeP2P (PORT_T *port) { switch (port->adminPointToPointMac) { case P2P_FORCE_TRUE: return True; case P2P_FORCE_FALSE: return False; default: case P2P_AUTO: return STP_OUT_get_duplex (port->port_index); } } void STP_p2p_enter_state (STATE_MACH_T* s) { register PORT_T* port = s->owner.port; switch (s->State) { case BEGIN: case INIT: port->p2p_recompute = True; break; case RECOMPUTE: port->operPointToPointMac = computeP2P (port); port->p2p_recompute = False; break; case STABLE: break; } } Bool STP_p2p_check_conditions (STATE_MACH_T* s) { register PORT_T* port = s->owner.port; switch (s->State) { case BEGIN: case INIT: return STP_hop_2_state (s, STABLE); case RECOMPUTE: return STP_hop_2_state (s, STABLE); case STABLE: if (port->p2p_recompute) { return STP_hop_2_state (s, RECOMPUTE); } break; } return False; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Point To Point MAC mode selection machine : 6.4.3, 6.5.1 */ #ifndef _STP_P2P_H__ #define _STP_P2P_H__ void STP_p2p_enter_state (STATE_MACH_T* s); Bool STP_p2p_check_conditions (STATE_MACH_T* s); char* STP_p2p_get_state_name (int state); #endif /* _STP_P2P_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Path Cost monitoring state machine */ #include "base.h" #include "stpm.h" #include "stp_to.h" /* for STP_OUT_get_port_oper_speed */ #define STATES { \ CHOOSE(AUTO), \ CHOOSE(FORSE), \ CHOOSE(STABLE) \ } #define GET_STATE_NAME STP_pcost_get_state_name #include "choose.h" static long computeAutoPCost (STATE_MACH_T *this) { long lret; register PORT_T* port = this->owner.port; if (port->usedSpeed < 10L) { /* < 10Mb/s */ lret = 20000000; } else if (port->usedSpeed <= 10L) { /* 10 Mb/s */ lret = 2000000; } else if (port->usedSpeed <= 100L) { /* 100 Mb/s */ lret = 200000; } else if (port->usedSpeed <= 1000L) { /* 1 Gb/s */ lret = 20000; } else if (port->usedSpeed <= 10000L) { /* 10 Gb/s */ lret = 2000; } else if (port->usedSpeed <= 100000L) { /* 100 Gb/s */ lret = 200; } else if (port->usedSpeed <= 1000000L) { /* 1 GTb/s */ lret = 20; } else if (port->usedSpeed <= 10000000L) { /* 10 Tb/s */ lret = 2; } else /* ??? */ { /* > Tb/s */ lret = 1; } #ifdef STP_DBG if (port->pcost->debug) { stp_trace ("usedSpeed=%lu lret=%ld", port->usedSpeed, lret); } #endif return lret; } /* ARGSUSED */ static void updPortPathCost (STATE_MACH_T *this) { } void STP_pcost_enter_state (STATE_MACH_T *this) { register PORT_T* port = this->owner.port; switch (this->State) { case BEGIN: break; case AUTO: port->operSpeed = STP_OUT_get_port_oper_speed (port->port_index); #ifdef STP_DBG if (port->pcost->debug) { stp_trace ("AUTO:operSpeed=%lu", port->operSpeed); } #endif port->usedSpeed = port->operSpeed; port->operPCost = computeAutoPCost (this); break; case FORSE: port->operPCost = port->adminPCost; port->usedSpeed = (unsigned long)-1; break; case STABLE: updPortPathCost (this); break; } } Bool STP_pcost_check_conditions (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; switch (this->State) { case BEGIN: return STP_hop_2_state (this, AUTO); case AUTO: return STP_hop_2_state (this, STABLE); case FORSE: return STP_hop_2_state (this, STABLE); case STABLE: if (ADMIN_PORT_PATH_COST_AUTO == port->adminPCost && port->operSpeed != port->usedSpeed) { return STP_hop_2_state (this, AUTO); } if (ADMIN_PORT_PATH_COST_AUTO != port->adminPCost && port->operPCost != port->adminPCost) { return STP_hop_2_state (this, FORSE); } break; } return False; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Path Cost monitoring state machine */ #ifndef _STP_PCOST_H__ #define _STP_PCOST_H__ void STP_pcost_enter_state (STATE_MACH_T* s); Bool STP_pcost_check_conditions (STATE_MACH_T* s); char* STP_pcost_get_state_name (int state); #endif /* _STP_PCOST_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* STP PORT instance : 17.18, 17.15 */ #include "base.h" #include "stpm.h" #include "stp_in.h" /* #include "rolesel.h" */ #include "portinfo.h" #include "roletrns.h" #include "sttrans.h" #include "topoch.h" #include "migrate.h" #include "transmit.h" #include "p2p.h" #include "pcost.h" #include "edge.h" #include "stp_to.h" /* for STP_OUT_get_port_name & STP_OUT_get_port_link_status */ int port_trace_flags; PORT_T * STP_port_create (STPM_T* stpm, int port_index) { PORT_T* this; UID_STP_PORT_CFG_T port_cfg; register int iii; unsigned short port_prio; /* check, if the port has just been added */ for (this = stpm->ports; this; this = this->next) { if (this->port_index == port_index) { return NULL; } } STP_NEW_IN_LIST(this, PORT_T, stpm->ports, "port create"); this->owner = stpm; this->machines = NULL; this->port_index = port_index; this->port_name = strdup (STP_OUT_get_port_name (port_index)); this->uptime = 0; STP_OUT_get_init_port_cfg (stpm->vlan_id, port_index, &port_cfg); port_prio = port_cfg.port_priority; this->admin_non_stp = port_cfg.admin_non_stp; this->adminEdge = port_cfg.admin_edge; this->adminPCost = port_cfg.admin_port_path_cost; this->adminPointToPointMac = port_cfg.admin_point2point; this->LinkDelay = DEF_LINK_DELAY; this->port_id = (port_prio << 8) + port_index; iii = 0; this->timers[iii++] = &this->fdWhile; this->timers[iii++] = &this->helloWhen; this->timers[iii++] = &this->mdelayWhile; this->timers[iii++] = &this->rbWhile; this->timers[iii++] = &this->rcvdInfoWhile; this->timers[iii++] = &this->rrWhile; this->timers[iii++] = &this->tcWhile; this->timers[iii++] = &this->txCount; this->timers[iii++] = &this->lnkWhile; /* create and bind port state machines */ STP_STATE_MACH_IN_LIST(topoch); STP_STATE_MACH_IN_LIST(migrate); STP_STATE_MACH_IN_LIST(p2p); STP_STATE_MACH_IN_LIST(edge); STP_STATE_MACH_IN_LIST(pcost) STP_STATE_MACH_IN_LIST(info); STP_STATE_MACH_IN_LIST(roletrns); STP_STATE_MACH_IN_LIST(sttrans); STP_STATE_MACH_IN_LIST(transmit); #ifdef STP_DBG #if 0 this->roletrns->ignoreHop2State = 14; /* DESIGNATED_PORT; */ this->info->ignoreHop2State = 3; /* CURRENT */ this->transmit->ignoreHop2State = 3; /* IDLE */ this->edge->ignoreHop2State = 0; /* DISABLED; */ #endif #if 0 this->info->debug = 1; this->pcost->debug = 1; this->p2p->debug = 1; this->edge->debug = 1; this->migrate->debug = 1; this->sttrans->debug = 1; this->topoch->debug = 1; this->roletrns->debug = 1; #endif this->sttrans->debug = 1; #endif return this; } void STP_port_init (PORT_T* this, STPM_T* stpm, Bool check_link) { if (check_link) { this->adminEnable = STP_OUT_get_port_link_status (this->port_index); STP_VECT_create (&this->designPrio, &stpm->BrId, 0, &stpm->BrId, this->port_id, this->port_id); STP_copy_times (&this->designTimes, &stpm->rootTimes); } /* reset timers */ this->fdWhile = this->helloWhen = this->mdelayWhile = this->rbWhile = this->rcvdInfoWhile = this->rrWhile = this->tcWhile = this->txCount = 0; this->msgPortRole = RSTP_PORT_ROLE_UNKN; this->selectedRole = DisabledPort; this->sendRSTP = True; this->operSpeed = STP_OUT_get_port_oper_speed (this->port_index); this->p2p_recompute = True; } void STP_port_delete (PORT_T* this) { STPM_T* stpm; register PORT_T* prev; register PORT_T* tmp; register STATE_MACH_T* stater; register void* pv; stpm = this->owner; free (this->port_name); for (stater = this->machines; stater; ) { pv = (void*) stater->next; STP_state_mach_delete (stater); stater = (STATE_MACH_T*) pv; } prev = NULL; for (tmp = stpm->ports; tmp; tmp = tmp->next) { if (tmp->port_index == this->port_index) { if (prev) { prev->next = this->next; } else { stpm->ports = this->next; } STP_FREE(this, "stp instance"); break; } prev = tmp; } } int STP_port_rx_bpdu (PORT_T* this, BPDU_T* bpdu, size_t len) { STP_info_rx_bpdu (this, bpdu, len); return 0; } #ifdef STP_DBG int STP_port_trace_state_machine (PORT_T* this, char* mach_name, int enadis) { STATE_MACH_T *stater; int nmatch = 0; for (stater = this->machines; stater; stater = stater->next) { if (! strcmp (mach_name, "all") || ! strcmp (mach_name, stater->name)) { if (stater->debug != enadis) { stp_trace ("port %s on %s trace %-8s (was %s) now %s", this->port_name, this->owner->name, stater->name, stater->debug ? " enabled" :"disabled", enadis ? " enabled" :"disabled"); } stater->debug = enadis; nmatch++; } } if (nmatch == 0) { stp_trace("port %s no such state machine as '%s'", this->port_name, mach_name); return STP_No_Such_State_Machine; } return 0; } void STP_port_trace_flags (char* title, PORT_T* this) { unsigned long flag = 0L; if (!port_trace_flags) return; if (this->reRoot) flag |= 0x000001L; if (this->sync) flag |= 0x000002L; if (this->synced) flag |= 0x000004L; if (this->proposed) flag |= 0x000010L; if (this->proposing) flag |= 0x000020L; if (this->agreed) flag |= 0x000040L; if (this->updtInfo) flag |= 0x000080L; if (this->operEdge) flag |= 0x000100L; stp_trace (" %-12s: flags=0x%04lx fdWhile=%d port=%s", title, flag, this->fdWhile, this->port_name); } #endif /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* STP PORT instance : 17.18, 17.15 */ #ifndef _STP_PORT_H__ #define _STP_PORT_H__ #include "statmch.h" #define TIMERS_NUMBER 9 typedef unsigned int PORT_TIMER_T; typedef enum { Mine, Aged, Received, Disabled } INFO_IS_T; typedef enum { SuperiorDesignateMsg, RepeatedDesignateMsg, ConfirmedRootMsg, OtherMsg } RCVD_MSG_T; typedef enum { DisabledPort = 0, AlternatePort, BackupPort, RootPort, DesignatedPort, NonStpPort } PORT_ROLE_T; typedef struct port_t { struct port_t* next; /* per Port state machines */ STATE_MACH_T* info; /* 17.21 */ STATE_MACH_T* roletrns; /* 17.23 */ STATE_MACH_T* sttrans; /* 17.24 */ STATE_MACH_T* topoch; /* 17.25 */ STATE_MACH_T* migrate; /* 17.26 */ STATE_MACH_T* transmit; /* 17.26 */ STATE_MACH_T* p2p; /* 6.4.3, 6.5.1 */ STATE_MACH_T* edge; /* */ STATE_MACH_T* pcost; /* */ STATE_MACH_T* machines; /* list of machines */ struct stpm_t* owner; /* Bridge, that this port belongs to */ /* per port Timers */ PORT_TIMER_T fdWhile; /* 17.15.1 */ PORT_TIMER_T helloWhen; /* 17.15.2 */ PORT_TIMER_T mdelayWhile; /* 17.15.3 */ PORT_TIMER_T rbWhile; /* 17.15.4 */ PORT_TIMER_T rcvdInfoWhile;/* 17.15.5 */ PORT_TIMER_T rrWhile; /* 17.15.6 */ PORT_TIMER_T tcWhile; /* 17.15.7 */ PORT_TIMER_T txCount; /* 17.18.40 */ PORT_TIMER_T lnkWhile; PORT_TIMER_T* timers[TIMERS_NUMBER]; /*list of timers */ Bool agreed; /* 17.18.1 */ PRIO_VECTOR_T designPrio; /* 17.18.2 */ TIMEVALUES_T designTimes; /* 17.18.3 */ Bool forward; /* 17.18.4 */ Bool forwarding; /* 17.18.5 */ INFO_IS_T infoIs; /* 17.18.6 */ Bool initPm; /* 17.18.7 */ Bool learn; /* 17.18.8 */ Bool learning; /* 17.18.9 */ Bool mcheck; /* 17.18.10 */ PRIO_VECTOR_T msgPrio; /* 17.18.11 */ TIMEVALUES_T msgTimes; /* 17.18.12 */ Bool newInfo; /* 17.18.13 */ Bool operEdge; /* 17.18.14 */ Bool adminEdge; /* 17.18.14 */ Bool portEnabled; /* 17.18.15 */ PORT_ID port_id; /* 17.18.16 */ PRIO_VECTOR_T portPrio; /* 17.18.17 */ TIMEVALUES_T portTimes; /* 17.18.18 */ Bool proposed; /* 17.18.19 */ Bool proposing; /* 17.18.20 */ Bool rcvdBpdu; /* 17.18.21 */ RCVD_MSG_T rcvdMsg; /* 17.18.22 */ Bool rcvdRSTP; /* 17/18.23 */ Bool rcvdSTP; /* 17.18.24 */ Bool rcvdTc; /* 17.18.25 */ Bool rcvdTcAck; /* 17.18.26 */ Bool rcvdTcn; /* 17.18.27 */ Bool reRoot; /* 17.18.28 */ Bool reselect; /* 17.18.29 */ PORT_ROLE_T role; /* 17.18.30 */ Bool selected; /* 17.18.31 */ PORT_ROLE_T selectedRole; /* 17.18.32 */ Bool sendRSTP; /* 17.18.33 */ Bool sync; /* 17.18.34 */ Bool synced; /* 17.18.35 */ Bool tc; /* 17.18.36 */ Bool tcAck; /* 17.18.37 */ Bool tcProp; /* 17.18.38 */ Bool updtInfo; /* 17.18.41 */ /* message information */ unsigned char msgBpduVersion; unsigned char msgBpduType; unsigned char msgPortRole; unsigned char msgFlags; unsigned long adminPCost; /* may be ADMIN_PORT_PATH_COST_AUTO */ unsigned long operPCost; unsigned long operSpeed; unsigned long usedSpeed; int LinkDelay; /* TBD: LinkDelay may be managed ? */ Bool adminEnable; /* 'has LINK' */ Bool wasInitBpdu; Bool admin_non_stp; Bool p2p_recompute; Bool operPointToPointMac; ADMIN_P2P_T adminPointToPointMac; /* statistics */ unsigned long rx_cfg_bpdu_cnt; unsigned long rx_rstp_bpdu_cnt; unsigned long rx_tcn_bpdu_cnt; unsigned long uptime; /* 14.8.2.1.3.a */ int port_index; char* port_name; #ifdef STP_DBG unsigned int skip_rx; unsigned int skip_tx; #endif } PORT_T; PORT_T* STP_port_create (struct stpm_t* stpm, int port_index); void STP_port_delete (PORT_T* this); int STP_port_rx_bpdu (PORT_T* this, BPDU_T* bpdu, size_t len); void STP_port_init (PORT_T* this, struct stpm_t* stpm, Bool check_link); #ifdef STP_DBG int STP_port_trace_state_machine (PORT_T* this, char* mach_name, int enadis); void STP_port_trace_flags (char* title, PORT_T* this); #endif #endif /* _STP_PORT_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ #include "base.h" #include "stpm.h" #include "stp_vectors.h" /* The Port Information State Machine : 17.21 */ #define STATES { \ CHOOSE(DISABLED), \ CHOOSE(ENABLED), \ CHOOSE(AGED), \ CHOOSE(UPDATE), \ CHOOSE(CURRENT), \ CHOOSE(RECEIVE), \ CHOOSE(SUPERIOR), \ CHOOSE(REPEAT), \ CHOOSE(AGREEMENT) \ } #define GET_STATE_NAME STP_info_get_state_name #include "choose.h" #if 0 /* for debug */ void _stp_dump (char* title, unsigned char* buff, int len) { register int iii; stp_trace ("\n%s:", title); for (iii = 0; iii < len; iii++) { if (! (iii % 24)) stp_trace ("\n%6d:", iii); if (! (iii % 8)) stp_trace (" "); stp_trace ("%02lx", (unsigned long) buff[iii]); } stp_trace ("\n"); } #endif static RCVD_MSG_T rcvBpdu (STATE_MACH_T* this) {/* 17.19.8 */ int bridcmp; register PORT_T* port = this->owner.port; if (port->msgBpduType == BPDU_TOPO_CHANGE_TYPE) { #ifdef STP_DBG if (this->debug) { stp_trace ("%s", "rcvBpdu: OtherMsg:BPDU_TOPO_CHANGE_TYPE"); } #endif return OtherMsg; } port->msgPortRole = RSTP_PORT_ROLE_UNKN; if (BPDU_RSTP == port->msgBpduType) { port->msgPortRole = (port->msgFlags & PORT_ROLE_MASK) >> PORT_ROLE_OFFS; } if (RSTP_PORT_ROLE_DESGN == port->msgPortRole || BPDU_CONFIG_TYPE == port->msgBpduType) { bridcmp = STP_VECT_compare_vector (&port->msgPrio, &port->portPrio); if (bridcmp < 0 || (! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, &port->portPrio.design_bridge) && port->msgPrio.design_port == port->portPrio.design_port && STP_compare_times (&port->msgTimes, &port->portTimes))) { #ifdef STP_DBG if (this->debug) { stp_trace ("rcvBpdu: SuperiorDesignateMsg:bridcmp=%d", (int) bridcmp); } #endif return SuperiorDesignateMsg; } } if (BPDU_CONFIG_TYPE == port->msgBpduType || RSTP_PORT_ROLE_DESGN == port->msgPortRole) { if (! STP_VECT_compare_vector (&port->msgPrio, &port->portPrio) && ! STP_compare_times (&port->msgTimes, &port->portTimes)) { #ifdef STP_DBG if (this->debug) { stp_trace ("%s", "rcvBpdu: RepeatedDesignateMsg"); } #endif return RepeatedDesignateMsg; } } if (RSTP_PORT_ROLE_ROOT == port->msgBpduType && port->operPointToPointMac && ! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, &port->portPrio.design_bridge) && AGREEMENT_BIT & port->msgFlags) { #ifdef STP_DBG if (this->debug) { stp_trace ("%s", "rcvBpdu: ConfirmedRootMsg"); } #endif return ConfirmedRootMsg; } #ifdef STP_DBG if (this->debug) { if (RSTP_PORT_ROLE_ROOT == port->msgBpduType) { if (!port->operPointToPointMac) { stp_trace("rcvBpdu: OtherMsg: not point-to-point MAC"); } else if (STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, &port->portPrio.design_bridge)) { STP_VECT_br_id_print("rcvBpdu: OtherMsg: msgPrio", &port->msgPrio.design_bridge, True); STP_VECT_br_id_print("rcvBpdu: portPrio", &port->portPrio.design_bridge, True); } else { stp_trace("rcvBpdu: OtherMsg: agreement bit not set"); } } else { stp_trace ("rcvBpdu: OtherMsg: type %d", port->msgBpduType); } } #endif return OtherMsg; } /* ARGSUSED */ static Bool recordProposed (STATE_MACH_T* this, char* reason) {/* 17.19.9 */ register PORT_T* port = this->owner.port; if (RSTP_PORT_ROLE_DESGN == port->msgPortRole && (PROPOSAL_BIT & port->msgFlags) && port->operPointToPointMac) { return True; } return False; } static void setTcFlags (STATE_MACH_T* this) {/* 17.19.13 */ register PORT_T* port = this->owner.port; if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) { #ifdef STP_DBG if (this->debug) { stp_trace ("port %s rx rcvdTcn", port->port_name); } #endif port->rcvdTcn = True; } else { if (TOPOLOGY_CHANGE_BIT & port->msgFlags) { #ifdef STP_DBG if (this->debug) { stp_trace ("(%s-%s) rx rcvdTc 0X%lx", port->owner->name, port->port_name, (unsigned long) port->msgFlags); } #endif port->rcvdTc = True; } if (TOPOLOGY_CHANGE_ACK_BIT & port->msgFlags) { #ifdef STP_DBG if (this->debug) { stp_trace ("port %s rx rcvdTcAck 0X%lx", port->port_name, (unsigned long) port->msgFlags); } #endif port->rcvdTcAck = True; } } } static void updtBPDUVersion (STATE_MACH_T* this) {/* 17.19.18 */ register PORT_T* port = this->owner.port; if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) { port->rcvdSTP = True; } if (port->msgBpduVersion < 2) { port->rcvdSTP = True; } if (BPDU_RSTP == port->msgBpduType) { /* port->port->owner->ForceVersion >= NORMAL_RSTP we have checked in STP_info_rx_bpdu */ port->rcvdRSTP = True; } } static void updtRcvdInfoWhile (STATE_MACH_T* this) {/* 17.19.19 */ register int eff_age, dm, dt; register int hello3; register PORT_T* port = this->owner.port; eff_age = ( + port->portTimes.MaxAge) / 16; if (eff_age < 1) eff_age = 1; eff_age += port->portTimes.MessageAge; if (eff_age <= port->portTimes.MaxAge) { hello3 = 3 * port->portTimes.HelloTime; dm = port->portTimes.MaxAge - eff_age; if (dm > hello3) dt = hello3; else dt = dm; port->rcvdInfoWhile = dt; /**** stp_trace ("ma=%d eff_age=%d dm=%d dt=%d p=%s", (int) port->portTimes.MessageAge, (int) eff_age, (int) dm, (int) dt, port->port_name); ****/ } else { port->rcvdInfoWhile = 0; /****/ #ifdef STP_DBG /*if (this->debug) */ { stp_trace ("port %s: MaxAge=%d MessageAge=%d HelloTime=%d rcvdInfoWhile=null !", port->port_name, (int) port->portTimes.MaxAge, (int) port->portTimes.MessageAge, (int) port->portTimes.HelloTime); } #endif /****/ } } /* ARGSUSED */ void STP_info_rx_bpdu (PORT_T* port, struct stp_bpdu_t* bpdu, size_t len) { #if 0 _stp_dump ("\nall BPDU", ((unsigned char*) bpdu) - 12, len + 12); _stp_dump ("ETH_HEADER", (unsigned char*) &bpdu->eth, 5); _stp_dump ("BPDU_HEADER", (unsigned char*) &bpdu->hdr, 4); stp_trace ("protocol=%02x%02x version=%02x bpdu_type=%02x\n", bpdu->hdr.protocol[0], bpdu->hdr.protocol[1], bpdu->hdr.version, bpdu->hdr.bpdu_type); _stp_dump ("\nBPDU_BODY", (unsigned char*) &bpdu->body, sizeof (BPDU_BODY_T) + 2); stp_trace ("flags=%02x\n", bpdu->body.flags); _stp_dump ("root_id", bpdu->body.root_id, 8); _stp_dump ("root_path_cost", bpdu->body.root_path_cost, 4); _stp_dump ("bridge_id", bpdu->body.bridge_id, 8); _stp_dump ("port_id", bpdu->body.port_id, 2); _stp_dump ("message_age", bpdu->body.message_age, 2); _stp_dump ("max_age", bpdu->body.max_age, 2); _stp_dump ("hello_time", bpdu->body.hello_time, 2); _stp_dump ("forward_delay", bpdu->body.forward_delay, 2); _stp_dump ("ver_1_len", bpdu->ver_1_len, 2); #endif /* check bpdu type */ switch (bpdu->hdr.bpdu_type) { case BPDU_CONFIG_TYPE: port->rx_cfg_bpdu_cnt++; #ifdef STP_DBG if (port->info->debug) stp_trace ("CfgBpdu on port %s", port->port_name); #endif if (port->admin_non_stp) return; port->rcvdBpdu = True; break; case BPDU_TOPO_CHANGE_TYPE: port->rx_tcn_bpdu_cnt++; #ifdef STP_DBG if (port->info->debug) stp_trace ("TcnBpdu on port %s", port->port_name); #endif if (port->admin_non_stp) return; port->rcvdBpdu = True; port->msgBpduVersion = bpdu->hdr.version; port->msgBpduType = bpdu->hdr.bpdu_type; return; default: stp_trace ("RX undef bpdu type=%d", (int) bpdu->hdr.bpdu_type); return; case BPDU_RSTP: port->rx_rstp_bpdu_cnt++; if (port->admin_non_stp) return; if (port->owner->ForceVersion >= NORMAL_RSTP) { port->rcvdBpdu = True; } else { return; } #ifdef STP_DBG if (port->info->debug) stp_trace ("BPDU_RSTP on port %s", port->port_name); #endif break; } port->msgBpduVersion = bpdu->hdr.version; port->msgBpduType = bpdu->hdr.bpdu_type; port->msgFlags = bpdu->body.flags; /* 17.18.11 */ STP_VECT_get_vector (&bpdu->body, &port->msgPrio); port->msgPrio.bridge_port = port->port_id; /* 17.18.12 */ STP_get_times (&bpdu->body, &port->msgTimes); /* 17.18.25, 17.18.26 : see setTcFlags() */ } void STP_info_enter_state (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; switch (this->State) { case BEGIN: port->rcvdMsg = OtherMsg; port->msgBpduType = (unsigned char)-1; port->msgPortRole = RSTP_PORT_ROLE_UNKN; port->msgFlags = 0; /* clear port statistics */ port->rx_cfg_bpdu_cnt = port->rx_rstp_bpdu_cnt = port->rx_tcn_bpdu_cnt = 0; /* FALLTHRU */ case DISABLED: port->rcvdBpdu = port->rcvdRSTP = port->rcvdSTP = False; port->updtInfo = port->proposing = False; /* In DISABLED */ port->agreed = port->proposed = False; port->rcvdInfoWhile = 0; port->infoIs = Disabled; port->reselect = True; port->selected = False; break; case ENABLED: /* IEEE 802.1y, 17.21, Z.14 */ STP_VECT_copy (&port->portPrio, &port->designPrio); STP_copy_times (&port->portTimes, &port->designTimes); break; case AGED: port->infoIs = Aged; port->reselect = True; port->selected = False; break; case UPDATE: STP_VECT_copy (&port->portPrio, &port->designPrio); STP_copy_times (&port->portTimes, &port->designTimes); port->updtInfo = False; port->agreed = port->synced = False; /* In UPDATE */ port->proposed = port->proposing = False; /* in UPDATE */ port->infoIs = Mine; port->newInfo = True; #ifdef STP_DBG if (this->debug) { STP_VECT_br_id_print ("updated: portPrio.design_bridge", &port->portPrio.design_bridge, True); STP_VECT_br_id_print ("updated: portPrio.root_bridge", &port->portPrio.root_bridge, True); } #endif break; case CURRENT: break; case RECEIVE: port->rcvdMsg = rcvBpdu (this); updtBPDUVersion (this); setTcFlags (this); port->rcvdBpdu = False; break; case SUPERIOR: STP_VECT_copy (&port->portPrio, &port->msgPrio); STP_copy_times (&port->portTimes, &port->msgTimes); updtRcvdInfoWhile (this); #if 1 /* due 802.1y, Z.7 */ port->agreed = False; /* deleted due 802.y in SUPERIOR */ port->synced = False; /* due 802.y deleted in SUPERIOR */ #endif port->proposing = False; /* in SUPERIOR */ port->proposed = recordProposed (this, "SUPERIOR"); port->infoIs = Received; port->reselect = True; port->selected = False; #ifdef STP_DBG if (this->debug) { STP_VECT_br_id_print ("stored: portPrio.design_bridge", &port->portPrio.design_bridge, True); STP_VECT_br_id_print ("stored: portPrio.root_bridge", &port->portPrio.root_bridge, True); stp_trace ("proposed=%d on port %s", (int) port->proposed, port->port_name); } #endif break; case REPEAT: port->proposed = recordProposed (this, "REPEAT"); updtRcvdInfoWhile (this); break; case AGREEMENT: #ifdef STP_DBG if (port->roletrns->debug) { stp_trace ("(%s-%s) rx AGREEMENT flag !", port->owner->name, port->port_name); } #endif port->agreed = True; port->proposing = False; /* In AGREEMENT */ break; } } Bool STP_info_check_conditions (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; if ((! port->portEnabled && port->infoIs != Disabled) || BEGIN == this->State) { return STP_hop_2_state (this, DISABLED); } switch (this->State) { case DISABLED: if (port->updtInfo) { return STP_hop_2_state (this, DISABLED); } if (port->portEnabled && port->selected) { return STP_hop_2_state (this, ENABLED); } if (port->rcvdBpdu) { return STP_hop_2_state (this, DISABLED); } break; case ENABLED: /* IEEE 802.1y, 17.21, Z.14 */ return STP_hop_2_state (this, AGED); case AGED: if (port->selected && port->updtInfo) { return STP_hop_2_state (this, UPDATE); } break; case UPDATE: return STP_hop_2_state (this, CURRENT); case CURRENT: if (port->selected && port->updtInfo) { return STP_hop_2_state (this, UPDATE); } if (Received == port->infoIs && ! port->rcvdInfoWhile && ! port->updtInfo && ! port->rcvdBpdu) { return STP_hop_2_state (this, AGED); } if (port->rcvdBpdu && !port->updtInfo) { return STP_hop_2_state (this, RECEIVE); } break; case RECEIVE: switch (port->rcvdMsg) { case SuperiorDesignateMsg: return STP_hop_2_state (this, SUPERIOR); case RepeatedDesignateMsg: return STP_hop_2_state (this, REPEAT); case ConfirmedRootMsg: return STP_hop_2_state (this, AGREEMENT); default: return STP_hop_2_state (this, CURRENT); } case SUPERIOR: return STP_hop_2_state (this, CURRENT); case REPEAT: return STP_hop_2_state (this, CURRENT); case AGREEMENT: return STP_hop_2_state (this, CURRENT); } return False; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* The Port Information State Machine : 17.21 */ #ifndef _STP_INFOR_H__ #define _STP_INFOR_H__ void STP_info_enter_state (STATE_MACH_T* s); Bool STP_info_check_conditions (STATE_MACH_T* s); void STP_info_rx_bpdu (PORT_T* this, struct stp_bpdu_t* bpdu, size_t len); char* STP_info_get_state_name (int state); #endif /* _STP_INFOR_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Port Role Selection state machine : 17.22 */ #include "base.h" #include "stpm.h" #include "stp_vectors.h" #define STATES { \ CHOOSE(INIT_BRIDGE), \ CHOOSE(ROLE_SELECTION) \ } #define GET_STATE_NAME STP_rolesel_get_state_name #include "choose.h" #if 0 void stp_dbg_break_point (PORT_T * port, STPM_T* stpm) { } #endif static Bool _is_backup_port (PORT_T* port, STPM_T* this) { if (!STP_VECT_compare_bridge_id (&port->portPrio.design_bridge, &this->BrId)) { #if 0 /* def STP_DBG */ if (port->info->debug) { STP_VECT_br_id_print ("portPrio.design_bridge", &port->portPrio.design_bridge, True); STP_VECT_br_id_print (" this->BrId", &this->BrId, True); } stp_dbg_break_point (port, this); #endif return True; } else { return False; } } /* ARGSUSED */ static void setRoleSelected (char* reason, STPM_T* stpm, PORT_T* port, PORT_ROLE_T newRole) { #ifdef STP_DBG char* new_role_name; #endif port->selectedRole = newRole; if (newRole == port->role) return; switch (newRole) { case DisabledPort: #ifdef STP_DBG new_role_name = "Disabled"; #endif break; case AlternatePort: #ifdef STP_DBG new_role_name = "Alternate"; #endif break; case BackupPort: #ifdef STP_DBG new_role_name = "Backup"; #endif break; case RootPort: #ifdef STP_DBG new_role_name = "Root"; #endif break; case DesignatedPort: #ifdef STP_DBG new_role_name = "Designated"; #endif break; case NonStpPort: #ifdef STP_DBG new_role_name = "NonStp"; #endif port->role = newRole; break; default: #ifdef STP_DBG stp_trace ("%s-%s:port %s => Unknown (%d ?)", reason, stpm->name, port->port_name, (int) newRole); #else abort(); #endif return; } #ifdef STP_DBG if (port->roletrns->debug) stp_trace ("%s(%s-%s) => %s", reason, stpm->name, port->port_name, new_role_name); #endif } static void updtRoleDisableBridge (STPM_T* this) { /* 17.10.20 */ register PORT_T *port; for (port = this->ports; port; port = port->next) { port->selectedRole = DisabledPort; } } static void clearReselectBridge (STPM_T* this) { /* 17.19.1 */ register PORT_T *port; for (port = this->ports; port; port = port->next) { port->reselect = False; } } static void updtRootPrio (STATE_MACH_T* this) { PRIO_VECTOR_T rootPathPrio; /* 17.4.2.2 */ register PORT_T *port; register STPM_T *stpm; register unsigned int dm; stpm = this->owner.stpm; for (port = stpm->ports; port; port = port->next) { if (port->admin_non_stp) { continue; } if (Disabled == port->infoIs) continue; if (Aged == port->infoIs) continue; if (Mine == port->infoIs) { #if 0 /* def STP_DBG */ stp_dbg_break_point (port); /* for debugger break point */ #endif continue; } STP_VECT_copy (&rootPathPrio, &port->portPrio); rootPathPrio.root_path_cost += port->operPCost; if (STP_VECT_compare_vector (&rootPathPrio, &stpm->rootPrio) < 0) { STP_VECT_copy (&stpm->rootPrio, &rootPathPrio); STP_copy_times (&stpm->rootTimes, &port->portTimes); dm = (8 + stpm->rootTimes.MaxAge) / 16; if (!dm) dm = 1; stpm->rootTimes.MessageAge += dm; #ifdef STP_DBG if (port->roletrns->debug) stp_trace ("updtRootPrio: dm=%d rootTimes.MessageAge=%d on port %s", (int) dm, (int) stpm->rootTimes.MessageAge, port->port_name); #endif } } } static void updtRolesBridge (STATE_MACH_T* this) { /* 17.19.21 */ register PORT_T* port; register STPM_T* stpm; #ifdef STP_DBG PORT_ID old_root_port; /* for tracing of root port changing */ #endif stpm = this->owner.stpm; #ifdef STP_DBG old_root_port = stpm->rootPortId; #endif STP_VECT_create (&stpm->rootPrio, &stpm->BrId, 0, &stpm->BrId, 0, 0); STP_copy_times (&stpm->rootTimes, &stpm->BrTimes); stpm->rootPortId = 0; updtRootPrio (this); for (port = stpm->ports; port; port = port->next) { if (port->admin_non_stp) { continue; } STP_VECT_create (&port->designPrio, &stpm->rootPrio.root_bridge, stpm->rootPrio.root_path_cost, &stpm->BrId, port->port_id, port->port_id); STP_copy_times (&port->designTimes, &stpm->rootTimes); #if 0 #ifdef STP_DBG if (port->roletrns->debug) { STP_VECT_br_id_print ("ch:designPrio.design_bridge", &port->designPrio.design_bridge, True); } #endif #endif } stpm->rootPortId = stpm->rootPrio.bridge_port; #ifdef STP_DBG if (old_root_port != stpm->rootPortId) { if (! stpm->rootPortId) { stp_trace ("bridge %s became root", stpm->name); } else { stp_trace ("bridge %s new root port: %s", stpm->name, STP_stpm_get_port_name_by_id (stpm, stpm->rootPortId)); } } #endif for (port = stpm->ports; port; port = port->next) { if (port->admin_non_stp) { setRoleSelected ("Non", stpm, port, NonStpPort); port->forward = port->learn = True; continue; } switch (port->infoIs) { case Disabled: setRoleSelected ("Dis", stpm, port, DisabledPort); break; case Aged: setRoleSelected ("Age", stpm, port, DesignatedPort); port->updtInfo = True; break; case Mine: setRoleSelected ("Mine", stpm, port, DesignatedPort); if (0 != STP_VECT_compare_vector (&port->portPrio, &port->designPrio) || 0 != STP_compare_times (&port->portTimes, &port->designTimes)) { port->updtInfo = True; } break; case Received: if (stpm->rootPortId == port->port_id) { setRoleSelected ("Rec", stpm, port, RootPort); } else if (STP_VECT_compare_vector (&port->designPrio, &port->portPrio) < 0) { /* Note: this important piece has been inserted after * discussion with Mick Sieman and reading 802.1y Z1 */ setRoleSelected ("Rec", stpm, port, DesignatedPort); port->updtInfo = True; break; } else { if (_is_backup_port (port, stpm)) { setRoleSelected ("rec", stpm, port, BackupPort); } else { setRoleSelected ("rec", stpm, port, AlternatePort); } } port->updtInfo = False; break; default: stp_trace ("undef infoIs=%d", (int) port->infoIs); break; } } } static Bool setSelectedBridge (STPM_T* this) { register PORT_T* port; for (port = this->ports; port; port = port->next) { if (port->reselect) { #ifdef STP_DBG stp_trace ("setSelectedBridge: TRUE=reselect on port %s", port->port_name); #endif return False; } } for (port = this->ports; port; port = port->next) { port->selected = True; } return True; } void STP_rolesel_enter_state (STATE_MACH_T* this) { STPM_T* stpm; stpm = this->owner.stpm; switch (this->State) { case BEGIN: case INIT_BRIDGE: updtRoleDisableBridge (stpm); break; case ROLE_SELECTION: clearReselectBridge (stpm); updtRolesBridge (this); (void) setSelectedBridge (stpm); break; } } Bool STP_rolesel_check_conditions (STATE_MACH_T* s) { STPM_T* stpm; register PORT_T* port; /* * This doesn't look right. Why should we hop state twice in a single check * condition call? It means we can never perform the enter-state action for * INIT_BRIDGE. */ #ifdef carlsonj_removed if (BEGIN == s->State) { (void) STP_hop_2_state (s, INIT_BRIDGE); } #endif switch (s->State) { case BEGIN: return STP_hop_2_state (s, INIT_BRIDGE); case INIT_BRIDGE: return STP_hop_2_state (s, ROLE_SELECTION); case ROLE_SELECTION: stpm = s->owner.stpm; for (port = stpm->ports; port; port = port->next) { if (port->reselect) { /* stp_trace ("reselect on port %s", port->port_name); */ return STP_hop_2_state (s, ROLE_SELECTION); } } break; } return False; } void STP_rolesel_update_stpm (STPM_T* this) { register PORT_T* port; PRIO_VECTOR_T rootPathPrio; /* 17.4.2.2 */ stp_trace ("%s", "??? STP_rolesel_update_stpm ???"); STP_VECT_create (&rootPathPrio, &this->BrId, 0, &this->BrId, 0, 0); if (!this->rootPortId || STP_VECT_compare_vector (&rootPathPrio, &this->rootPrio) < 0) { STP_VECT_copy (&this->rootPrio, &rootPathPrio); } for (port = this->ports; port; port = port->next) { STP_VECT_create (&port->designPrio, &this->rootPrio.root_bridge, this->rootPrio.root_path_cost, &this->BrId, port->port_id, port->port_id); if (Received != port->infoIs || this->rootPortId == port->port_id) { STP_VECT_copy (&port->portPrio, &port->designPrio); } port->reselect = True; port->selected = False; } } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Port Role Selection state machine : 17.22 */ #ifndef _STP_ROLES_SELECT_H #define _STP_ROLES_SELECT_H void STP_rolesel_enter_state (STATE_MACH_T* s); Bool STP_rolesel_check_conditions (STATE_MACH_T* s); void STP_rolesel_update_stpm (struct stpm_t* this); char* STP_rolesel_get_state_name (int state); #endif /* _STP_ROLES_SELECT_H */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Port Role Transitions state machine : 17.24 */ #include "base.h" #include "stpm.h" #define STATES { \ CHOOSE(INIT_PORT), \ CHOOSE(BLOCK_PORT), \ CHOOSE(BLOCKED_PORT), \ CHOOSE(BACKUP_PORT), \ CHOOSE(ROOT_PROPOSED), \ CHOOSE(ROOT_AGREED), \ CHOOSE(REROOT), \ CHOOSE(ROOT_PORT), \ CHOOSE(REROOTED), \ CHOOSE(ROOT_LEARN), \ CHOOSE(ROOT_FORWARD), \ CHOOSE(DESIGNATED_PROPOSE), \ CHOOSE(DESIGNATED_SYNCED), \ CHOOSE(DESIGNATED_RETIRED), \ CHOOSE(DESIGNATED_PORT), \ CHOOSE(DESIGNATED_LISTEN), \ CHOOSE(DESIGNATED_LEARN), \ CHOOSE(DESIGNATED_FORWARD) \ } #define GET_STATE_NAME STP_roletrns_get_state_name #include "choose.h" static void setSyncBridge (STATE_MACH_T *this) { register PORT_T* port; for (port = this->owner.port->owner->ports; port; port = port->next) { port->sync = True; /* in ROOT_PROPOSED (setSyncBridge) */ } } static void setReRootBridge (STATE_MACH_T *this) { register PORT_T* port; for (port = this->owner.port->owner->ports; port; port = port->next) { port->reRoot = True; /* In setReRootBridge */ } } static Bool compute_all_synced (PORT_T* this) { register PORT_T* port; for (port = this->owner->ports; port; port = port->next) { if (port->port_index == this->port_index) continue; if (! port->synced) { return False; } } return True; } static Bool compute_re_rooted (PORT_T* this) { register PORT_T* port; for (port = this->owner->ports; port; port = port->next) { if (port->port_index == this->port_index) continue; if (port->rrWhile) { return False; } } return True; } void STP_roletrns_enter_state (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; register STPM_T* stpm; stpm = port->owner; switch (this->State) { case BEGIN: case INIT_PORT: #if 0 /* due 802.1y Z.4 */ port->role = DisabledPort; #else port->role = port->selectedRole = DisabledPort; port->reselect = True; #endif port->synced = False; /* in INIT */ port->sync = True; /* in INIT */ port->reRoot = True; /* in INIT_PORT */ port->rrWhile = stpm->rootTimes.ForwardDelay; port->fdWhile = stpm->rootTimes.ForwardDelay; port->rbWhile = 0; #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("after init", port); #endif break; case BLOCK_PORT: port->role = port->selectedRole; port->learn = port->forward = False; break; case BLOCKED_PORT: port->fdWhile = stpm->rootTimes.ForwardDelay; port->synced = True; /* In BLOCKED_PORT */ port->rrWhile = 0; port->sync = port->reRoot = False; /* BLOCKED_PORT */ break; case BACKUP_PORT: port->rbWhile = 2 * stpm->rootTimes.HelloTime; break; /* 17.23.2 */ case ROOT_PROPOSED: setSyncBridge (this); port->proposed = False; #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("ROOT_PROPOSED", port); #endif break; case ROOT_AGREED: port->proposed = port->sync = False; /* in ROOT_AGREED */ port->synced = True; /* In ROOT_AGREED */ port->newInfo = True; #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("ROOT_AGREED", port); #endif break; case REROOT: setReRootBridge (this); #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("REROOT", port); #endif break; case ROOT_PORT: port->role = RootPort; port->rrWhile = stpm->rootTimes.ForwardDelay; #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("ROOT_PORT", port); #endif break; case REROOTED: port->reRoot = False; /* In REROOTED */ #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("REROOTED", port); #endif break; case ROOT_LEARN: port->fdWhile = stpm->rootTimes.ForwardDelay; port->learn = True; #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("ROOT_LEARN", port); #endif break; case ROOT_FORWARD: port->fdWhile = 0; port->forward = True; #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("ROOT_FORWARD", port); #endif break; /* 17.23.3 */ case DESIGNATED_PROPOSE: port->proposing = True; /* in DESIGNATED_PROPOSE */ port->newInfo = True; #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("DESIGNATED_PROPOSE", port); #endif break; case DESIGNATED_SYNCED: port->rrWhile = 0; port->synced = True; /* DESIGNATED_SYNCED */ port->sync = False; /* DESIGNATED_SYNCED */ #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("DESIGNATED_SYNCED", port); #endif break; case DESIGNATED_RETIRED: port->reRoot = False; /* DESIGNATED_RETIRED */ #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("DESIGNATED_RETIRED", port); #endif break; case DESIGNATED_PORT: port->role = DesignatedPort; #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("DESIGNATED_PORT", port); #endif break; case DESIGNATED_LISTEN: port->learn = port->forward = False; port->fdWhile = stpm->rootTimes.ForwardDelay; #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("DESIGNATED_LISTEN", port); #endif break; case DESIGNATED_LEARN: port->learn = True; port->fdWhile = stpm->rootTimes.ForwardDelay; #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("DESIGNATED_LEARN", port); #endif break; case DESIGNATED_FORWARD: port->forward = True; port->fdWhile = 0; #ifdef STP_DBG if (this->debug) STP_port_trace_flags ("DESIGNATED_FORWARD", port); #endif break; }; } Bool STP_roletrns_check_conditions (STATE_MACH_T* this) { register PORT_T *port = this->owner.port; register STPM_T *stpm; Bool allSynced; Bool allReRooted; stpm = port->owner; if (BEGIN == this->State) { return STP_hop_2_state (this, INIT_PORT); } if (port->role != port->selectedRole && port->selected && ! port->updtInfo) { switch (port->selectedRole) { case DisabledPort: case AlternatePort: case BackupPort: #if 0 /* def STP_DBG */ if (this->debug) { stp_trace ("hop to BLOCK_PORT role=%d selectedRole=%d", (int) port->role, (int) port->selectedRole); } #endif return STP_hop_2_state (this, BLOCK_PORT); case RootPort: return STP_hop_2_state (this, ROOT_PORT); case DesignatedPort: return STP_hop_2_state (this, DESIGNATED_PORT); default: return False; } } switch (this->State) { /* 17.23.1 */ case INIT_PORT: return STP_hop_2_state (this, BLOCK_PORT); case BLOCK_PORT: if (!port->selected || port->updtInfo) break; if (!port->learning && !port->forwarding) { return STP_hop_2_state (this, BLOCKED_PORT); } break; case BLOCKED_PORT: if (!port->selected || port->updtInfo) break; if (port->fdWhile != stpm->rootTimes.ForwardDelay || port->sync || port->reRoot || !port->synced) { return STP_hop_2_state (this, BLOCKED_PORT); } if (port->rbWhile != 2 * stpm->rootTimes.HelloTime && port->role == BackupPort) { return STP_hop_2_state (this, BACKUP_PORT); } break; case BACKUP_PORT: return STP_hop_2_state (this, BLOCKED_PORT); /* 17.23.2 */ case ROOT_PROPOSED: return STP_hop_2_state (this, ROOT_PORT); case ROOT_AGREED: return STP_hop_2_state (this, ROOT_PORT); case REROOT: return STP_hop_2_state (this, ROOT_PORT); case ROOT_PORT: if (!port->selected || port->updtInfo) break; if (!port->forward && !port->reRoot) { return STP_hop_2_state (this, REROOT); } allSynced = compute_all_synced (port); if ((port->proposed && allSynced) || (!port->synced && allSynced)) { return STP_hop_2_state (this, ROOT_AGREED); } if (port->proposed && !port->synced) { return STP_hop_2_state (this, ROOT_PROPOSED); } allReRooted = compute_re_rooted (port); if ((!port->fdWhile || ((allReRooted && !port->rbWhile) && stpm->ForceVersion >=2)) && port->learn && !port->forward) { return STP_hop_2_state (this, ROOT_FORWARD); } if ((!port->fdWhile || ((allReRooted && !port->rbWhile) && stpm->ForceVersion >=2)) && !port->learn) { return STP_hop_2_state (this, ROOT_LEARN); } if (port->reRoot && port->forward) { return STP_hop_2_state (this, REROOTED); } if (port->rrWhile != stpm->rootTimes.ForwardDelay) { return STP_hop_2_state (this, ROOT_PORT); } break; case REROOTED: return STP_hop_2_state (this, ROOT_PORT); case ROOT_LEARN: return STP_hop_2_state (this, ROOT_PORT); case ROOT_FORWARD: return STP_hop_2_state (this, ROOT_PORT); /* 17.23.3 */ case DESIGNATED_PROPOSE: return STP_hop_2_state (this, DESIGNATED_PORT); case DESIGNATED_SYNCED: return STP_hop_2_state (this, DESIGNATED_PORT); case DESIGNATED_RETIRED: return STP_hop_2_state (this, DESIGNATED_PORT); case DESIGNATED_PORT: if (!port->selected || port->updtInfo) break; if (!port->forward && !port->agreed && !port->proposing && !port->operEdge) { return STP_hop_2_state (this, DESIGNATED_PROPOSE); } if (!port->rrWhile && port->reRoot) { return STP_hop_2_state (this, DESIGNATED_RETIRED); } if (!port->learning && !port->forwarding && !port->synced) { return STP_hop_2_state (this, DESIGNATED_SYNCED); } if (port->agreed && !port->synced) { return STP_hop_2_state (this, DESIGNATED_SYNCED); } if (port->operEdge && !port->synced) { return STP_hop_2_state (this, DESIGNATED_SYNCED); } if (port->sync && port->synced) { return STP_hop_2_state (this, DESIGNATED_SYNCED); } if ((!port->fdWhile || port->agreed || port->operEdge) && (!port->rrWhile || !port->reRoot) && !port->sync && (port->learn && !port->forward)) { return STP_hop_2_state (this, DESIGNATED_FORWARD); } if ((!port->fdWhile || port->agreed || port->operEdge) && (!port->rrWhile || !port->reRoot) && !port->sync && !port->learn) { return STP_hop_2_state (this, DESIGNATED_LEARN); } if (((port->sync && !port->synced) || (port->reRoot && port->rrWhile)) && !port->operEdge && (port->learn || port->forward)) { return STP_hop_2_state (this, DESIGNATED_LISTEN); } break; case DESIGNATED_LISTEN: return STP_hop_2_state (this, DESIGNATED_PORT); case DESIGNATED_LEARN: return STP_hop_2_state (this, DESIGNATED_PORT); case DESIGNATED_FORWARD: return STP_hop_2_state (this, DESIGNATED_PORT); }; return False; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Port Role Transitions state machine : 17.24 */ #ifndef _STP_ROLES_TRANSIT_H__ #define _STP_ROLES_TRANSIT_H__ void STP_roletrns_enter_state (STATE_MACH_T* s); Bool STP_roletrns_check_conditions (STATE_MACH_T* s); char* STP_roletrns_get_state_name (int state); #endif /* _STP_ROLES_TRANSIT_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Generic (abstract) state machine : 17.13, 17.14 */ #include "base.h" #include "statmch.h" #include "stp_vectors.h" #if STP_DBG # include "stpm.h" #endif STATE_MACH_T * STP_state_mach_create (void (*concreteEnterState) (STATE_MACH_T*), Bool (*concreteCheckCondition) (STATE_MACH_T*), char *(*concreteGetStatName) (int), void *owner, char *name) { STATE_MACH_T *this; STP_MALLOC(this, STATE_MACH_T, "state machine"); this->State = BEGIN; this->name = (char*) strdup (name); this->changeState = False; #if STP_DBG this->debug = False; this->ignoreHop2State = BEGIN; #endif this->concreteEnterState = concreteEnterState; this->concreteCheckCondition = concreteCheckCondition; this->concreteGetStatName = concreteGetStatName; this->owner.owner = owner; return this; } void STP_state_mach_delete (STATE_MACH_T *this) { free (this->name); STP_FREE(this, "state machine"); } Bool STP_check_condition (STATE_MACH_T* this) { Bool bret; bret = (*(this->concreteCheckCondition)) (this); if (bret) { this->changeState = True; } return bret; } Bool STP_change_state (STATE_MACH_T* this) { register int number_of_loops; for (number_of_loops = 0; ; number_of_loops++) { if (! this->changeState) break; (*(this->concreteEnterState)) (this); this->changeState = False; (void) STP_check_condition (this); } return number_of_loops; } Bool STP_hop_2_state (STATE_MACH_T* this, unsigned int new_state) { #ifdef STP_DBG switch (this->debug) { case 0: break; case 1: if (new_state == this->State || new_state == this->ignoreHop2State) break; stp_trace ("%-8s(%s-%s): %s=>%s", this->name, *this->owner.port->owner->name ? this->owner.port->owner->name : "Glbl", this->owner.port->port_name, (*(this->concreteGetStatName)) (this->State), (*(this->concreteGetStatName)) (new_state)); break; case 2: if (new_state == this->State) break; stp_trace ("%s(%s): %s=>%s", this->name, *this->owner.stpm->name ? this->owner.stpm->name : "Glbl", (*(this->concreteGetStatName)) (this->State), (*(this->concreteGetStatName)) (new_state)); break; } #endif this->State = new_state; this->changeState = True; return True; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Generic (abstract state machine) state machine : 17.13, 17.14 */ #ifndef _STP_STATER_H__ #define _STP_STATER_H__ #define BEGIN 9999 /* distinct from any valid state */ #define STP_DBG 1 typedef struct state_mach_t { struct state_mach_t* next; char* name; /* for debugging */ #ifdef STP_DBG char debug; /* 0- no dbg, 1 - port, 2 - stpm */ unsigned int ignoreHop2State; #endif Bool changeState; unsigned int State; void (* concreteEnterState) (struct state_mach_t * ); Bool (* concreteCheckCondition) (struct state_mach_t * ); char* (* concreteGetStatName) (int); union { struct stpm_t* stpm; struct port_t* port; void * owner; } owner; } STATE_MACH_T; #define STP_STATE_MACH_IN_LIST(WHAT) \ { \ STATE_MACH_T* abstr; \ \ abstr = STP_state_mach_create (STP_##WHAT##_enter_state, \ STP_##WHAT##_check_conditions, \ STP_##WHAT##_get_state_name, \ this, \ #WHAT); \ abstr->next = this->machines; \ this->machines = abstr; \ this->WHAT = abstr; \ } STATE_MACH_T * STP_state_mach_create (void (* concreteEnterState) (STATE_MACH_T*), Bool (* concreteCheckCondition) (STATE_MACH_T*), char * (* concreteGetStatName) (int), void* owner, char* name); void STP_state_mach_delete (STATE_MACH_T* this); Bool STP_check_condition (STATE_MACH_T* this); Bool STP_change_state (STATE_MACH_T* this); Bool STP_hop_2_state (STATE_MACH_T* this, unsigned int new_state); #endif /* _STP_STATER_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* BPDU formats: 9.1 - 9.3, 17.28 */ #ifndef _STP_BPDU_H__ #define _STP_BPDU_H__ #define MIN_BPDU 7 #define BPDU_L_SAP 0x42 #define LLC_UI 0x03 #define BPDU_PROTOCOL_ID 0x0000 #define BPDU_VERSION_ID 0x00 #define BPDU_VERSION_RAPID_ID 0x02 #define BPDU_TOPO_CHANGE_TYPE 0x80 #define BPDU_CONFIG_TYPE 0x00 #define BPDU_RSTP 0x02 #define TOPOLOGY_CHANGE_BIT 0x01 #define PROPOSAL_BIT 0x02 #define PORT_ROLE_OFFS 2 /* 0x04 & 0x08 */ #define PORT_ROLE_MASK (0x03 << PORT_ROLE_OFFS) #define LEARN_BIT 0x10 #define FORWARD_BIT 0x20 #define AGREEMENT_BIT 0x40 #define TOPOLOGY_CHANGE_ACK_BIT 0x80 #define RSTP_PORT_ROLE_UNKN 0x00 #define RSTP_PORT_ROLE_ALTBACK 0x01 #define RSTP_PORT_ROLE_ROOT 0x02 #define RSTP_PORT_ROLE_DESGN 0x03 typedef struct mac_header_t { unsigned char dst_mac[6]; unsigned char src_mac[6]; } MAC_HEADER_T; typedef struct eth_header_t { unsigned char len8023[2]; unsigned char dsap; unsigned char ssap; unsigned char llc; } ETH_HEADER_T; typedef struct bpdu_header_t { unsigned char protocol[2]; unsigned char version; unsigned char bpdu_type; } BPDU_HEADER_T; typedef struct bpdu_body_t { unsigned char flags; unsigned char root_id[8]; unsigned char root_path_cost[4]; unsigned char bridge_id[8]; unsigned char port_id[2]; unsigned char message_age[2]; unsigned char max_age[2]; unsigned char hello_time[2]; unsigned char forward_delay[2]; } BPDU_BODY_T; typedef struct stp_bpdu_t { ETH_HEADER_T eth; BPDU_HEADER_T hdr; BPDU_BODY_T body; unsigned char ver_1_len[2]; } BPDU_T; #endif /* _STP_BPDU_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* This file contains API from an operation system to the RSTP library */ #include "base.h" #include "stpm.h" #include "stp_in.h" #include "stp_to.h" #define _stp_in_stpm_enable stp_in_stpm_enable STP_VECTORS_T *stp_vectors; void * stp_in_stpm_create (int vlan_id, char* name, int* err_code) { register STPM_T* this; /* stp_trace ("stp_in_stpm_create(%s)", name); */ this = stpapi_stpm_find (vlan_id); if (this) { /* it had just been created :( */ *err_code = STP_Nothing_To_Do; return this; } this = STP_stpm_create (vlan_id, name); if (! this) { /* can't create stpm :( */ *err_code = STP_Cannot_Create_Instance_For_Vlan; return NULL; } *err_code = STP_OK; return this; } int _stp_in_stpm_enable (int vlan_id, char* name, UID_STP_MODE_T admin_state) { register STPM_T* this; Bool created_here = False; int rc, err_code; /* stp_trace ("_stp_in_stpm_enable(%s)", name); */ this = stpapi_stpm_find (vlan_id); if (STP_DISABLED != admin_state) { if (! vlan_id) { /* STP_IN_stop_all (); */ register STPM_T* stpm; for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { if (STP_DISABLED != stpm->admin_state) { STP_OUT_set_hardware_mode (stpm->vlan_id, STP_DISABLED); (void) STP_stpm_enable (stpm, STP_DISABLED); } } } } if (! this) { /* it had not yet been created */ if (STP_ENABLED == admin_state) {/* try to create it */ stp_trace ("implicit create to vlan '%s'", name); this = stp_in_stpm_create (vlan_id, name, &err_code); if (! this) { stp_trace ("implicit create to vlan '%s' failed", name); return STP_Implicit_Instance_Create_Failed; } created_here = True; } else {/* try to disable nothing ? */ return 0; } } if (this->admin_state == admin_state) { /* nothing to do :) */ return 0; } rc = STP_stpm_enable (this, admin_state); if (! rc) { STP_OUT_set_hardware_mode (vlan_id, admin_state); } if (rc && created_here) { STP_stpm_delete (this); } return rc; } STPM_T * stpapi_stpm_find (int vlan_id) { register STPM_T* this; for (this = STP_stpm_get_the_list (); this; this = this->next) if (vlan_id == this->vlan_id) return this; return NULL; } static PORT_T * _stpapi_port_find (STPM_T* this, int port_index) { register PORT_T* port; for (port = this->ports; port; port = port->next) if (port_index == port->port_index) { return port; } return NULL; } static void _conv_br_id_2_uid (IN BRIDGE_ID* f, OUT UID_BRIDGE_ID_T* t) { (void) memcpy (t, f, sizeof (UID_BRIDGE_ID_T)); } static int _check_stpm_config (IN UID_STP_CFG_T* uid_cfg) { if (uid_cfg->bridge_priority < MIN_BR_PRIO) { stp_trace ("%d bridge_priority small", (int) uid_cfg->bridge_priority); return STP_Small_Bridge_Priority; } if (uid_cfg->bridge_priority > MAX_BR_PRIO) { stp_trace ("%d bridge_priority large", (int) uid_cfg->bridge_priority); return STP_Large_Bridge_Priority; } if (uid_cfg->hello_time < MIN_BR_HELLOT) { stp_trace ("%d hello_time small", (int) uid_cfg->hello_time); return STP_Small_Hello_Time; } if (uid_cfg->hello_time > MAX_BR_HELLOT) { stp_trace ("%d hello_time large", (int) uid_cfg->hello_time); return STP_Large_Hello_Time; } if (uid_cfg->max_age < MIN_BR_MAXAGE) { stp_trace ("%d max_age small", (int) uid_cfg->max_age); return STP_Small_Max_Age; } if (uid_cfg->max_age > MAX_BR_MAXAGE) { stp_trace ("%d max_age large", (int) uid_cfg->max_age); return STP_Large_Max_Age; } if (uid_cfg->forward_delay < MIN_BR_FWDELAY) { stp_trace ("%d forward_delay small", (int) uid_cfg->forward_delay); return STP_Small_Forward_Delay; } if (uid_cfg->forward_delay > MAX_BR_FWDELAY) { stp_trace ("%d forward_delay large", (int) uid_cfg->forward_delay); return STP_Large_Forward_Delay; } if (2 * (uid_cfg->forward_delay - 1) < uid_cfg->max_age) { return STP_Forward_Delay_And_Max_Age_Are_Inconsistent; } if (uid_cfg->max_age < 2 * (uid_cfg->hello_time + 1)) { return STP_Hello_Time_And_Max_Age_Are_Inconsistent; } return 0; } static void _stp_in_enable_port_on_stpm (STPM_T* stpm, int port_index, Bool enable) { register PORT_T* port; port = _stpapi_port_find (stpm, port_index); if (! port) return; if (port->portEnabled == enable) {/* nothing to do :) */ return; } port->uptime = 0; if (enable) { /* clear port statistics */ port->rx_cfg_bpdu_cnt = port->rx_rstp_bpdu_cnt = port->rx_tcn_bpdu_cnt = 0; } #ifdef STP_DBG if (port->edge->debug) { stp_trace ("Port %s became '%s' adminEdge=%c", port->port_name, enable ? "enable" : "disable", port->adminEdge ? 'Y' : 'N'); } #endif port->adminEnable = enable; STP_port_init (port, stpm, False); port->reselect = True; port->selected = False; } void STP_IN_init (STP_VECTORS_T *vectors) { RSTP_INIT_CRITICAL_PATH_PROTECTIO; stp_vectors = vectors; } int STP_IN_stpm_get_cfg (IN int vlan_id, OUT UID_STP_CFG_T* uid_cfg) { register STPM_T* this; uid_cfg->field_mask = 0; RSTP_CRITICAL_PATH_START; this = stpapi_stpm_find (vlan_id); if (!this) { /* it had not yet been created :( */ RSTP_CRITICAL_PATH_END; return STP_Vlan_Had_Not_Yet_Been_Created; } if (this->admin_state != STP_DISABLED) { uid_cfg->field_mask |= BR_CFG_STATE; } uid_cfg->stp_enabled = this->admin_state; if (this->ForceVersion != 2) { uid_cfg->field_mask |= BR_CFG_FORCE_VER; } uid_cfg->force_version = this->ForceVersion; if (this->BrId.prio != DEF_BR_PRIO) { uid_cfg->field_mask |= BR_CFG_PRIO; } uid_cfg->bridge_priority = this->BrId.prio; if (this->BrTimes.MaxAge != DEF_BR_MAXAGE) { uid_cfg->field_mask |= BR_CFG_AGE; } uid_cfg->max_age = this->BrTimes.MaxAge; if (this->BrTimes.HelloTime != DEF_BR_HELLOT) { uid_cfg->field_mask |= BR_CFG_HELLO; } uid_cfg->hello_time = this->BrTimes.HelloTime; if (this->BrTimes.ForwardDelay != DEF_BR_FWDELAY) { uid_cfg->field_mask |= BR_CFG_DELAY; } uid_cfg->forward_delay = this->BrTimes.ForwardDelay; uid_cfg->hold_time = TxHoldCount; RSTP_CRITICAL_PATH_END; return 0; } int STP_IN_port_get_cfg (int vlan_id, int port_index, UID_STP_PORT_CFG_T* uid_cfg) { register STPM_T* this; register PORT_T* port; RSTP_CRITICAL_PATH_START; this = stpapi_stpm_find (vlan_id); if (!this) { /* it had not yet been created :( */ RSTP_CRITICAL_PATH_END; return STP_Vlan_Had_Not_Yet_Been_Created; } port = _stpapi_port_find (this, port_index); if (! port) {/* port is absent in the stpm :( */ RSTP_CRITICAL_PATH_END; return STP_Port_Is_Absent_In_The_Vlan; } uid_cfg->field_mask = 0; uid_cfg->port_priority = port->port_id >> 8; if (uid_cfg->port_priority != DEF_PORT_PRIO) uid_cfg->field_mask |= PT_CFG_PRIO; uid_cfg->admin_port_path_cost = port->adminPCost; if (uid_cfg->admin_port_path_cost != ADMIN_PORT_PATH_COST_AUTO) uid_cfg->field_mask |= PT_CFG_COST; uid_cfg->admin_point2point = port->adminPointToPointMac; if (uid_cfg->admin_point2point != DEF_P2P) uid_cfg->field_mask |= PT_CFG_P2P; uid_cfg->admin_edge = port->adminEdge; if (uid_cfg->admin_edge != DEF_ADMIN_EDGE) uid_cfg->field_mask |= PT_CFG_EDGE; uid_cfg->admin_non_stp = port->admin_non_stp; if (uid_cfg->admin_non_stp != DEF_ADMIN_NON_STP) uid_cfg->field_mask |= PT_CFG_NON_STP; if (port->mcheck) uid_cfg->field_mask |= PT_CFG_MCHECK; RSTP_CRITICAL_PATH_END; return 0; } int STP_IN_port_get_state (IN int vlan_id, INOUT UID_STP_PORT_STATE_T* entry) { register STPM_T* this; register PORT_T* port; RSTP_CRITICAL_PATH_START; this = stpapi_stpm_find (vlan_id); if (!this) { /* it had not yet been created :( */ RSTP_CRITICAL_PATH_END; return STP_Vlan_Had_Not_Yet_Been_Created; } port = _stpapi_port_find (this, entry->port_no); if (! port) {/* port is absent in the stpm :( */ RSTP_CRITICAL_PATH_END; return STP_Port_Is_Absent_In_The_Vlan; } entry->port_id = port->port_id; if (DisabledPort == port->role) { entry->state = UID_PORT_DISABLED; } else if (! port->forward && ! port->learn) { entry->state = UID_PORT_DISCARDING; } else if (! port->forward && port->learn) { entry->state = UID_PORT_LEARNING; } else { entry->state = UID_PORT_FORWARDING; } entry->uptime = port->uptime; entry->path_cost = port->operPCost; _conv_br_id_2_uid (&port->portPrio.root_bridge, &entry->designated_root); entry->designated_cost = port->portPrio.root_path_cost; _conv_br_id_2_uid (&port->portPrio.design_bridge, &entry->designated_bridge); entry->designated_port = port->portPrio.design_port; switch (port->role) { case DisabledPort: entry->role = ' '; break; case AlternatePort: entry->role = 'A'; break; case BackupPort: entry->role = 'B'; break; case RootPort: entry->role = 'R'; break; case DesignatedPort: entry->role = 'D'; break; case NonStpPort: entry->role = '-'; break; default: entry->role = '?'; break; } if (DisabledPort == port->role || NonStpPort == port->role) { (void) memset (&entry->designated_root, 0, sizeof (UID_BRIDGE_ID_T)); (void) memset (&entry->designated_bridge, 0, sizeof (UID_BRIDGE_ID_T)); entry->designated_cost = 0; entry->designated_port = port->port_id; } if (DisabledPort == port->role) { entry->oper_point2point = (P2P_FORCE_FALSE == port->adminPointToPointMac) ? 0 : 1; entry->oper_edge = port->adminEdge; entry->oper_stp_neigb = 0; } else { entry->oper_point2point = port->operPointToPointMac ? 1 : 0; entry->oper_edge = port->operEdge ? 1 : 0; entry->oper_stp_neigb = port->sendRSTP ? 0 : 1; } entry->oper_port_path_cost = port->operPCost; entry->rx_cfg_bpdu_cnt = port->rx_cfg_bpdu_cnt; entry->rx_rstp_bpdu_cnt = port->rx_rstp_bpdu_cnt; entry->rx_tcn_bpdu_cnt = port->rx_tcn_bpdu_cnt; entry->fdWhile = port->fdWhile; /* 17.15.1 */ entry->helloWhen = port->helloWhen; /* 17.15.2 */ entry->mdelayWhile = port->mdelayWhile; /* 17.15.3 */ entry->rbWhile = port->rbWhile; /* 17.15.4 */ entry->rcvdInfoWhile = port->rcvdInfoWhile;/* 17.15.5 */ entry->rrWhile = port->rrWhile; /* 17.15.6 */ entry->tcWhile = port->tcWhile; /* 17.15.7 */ entry->txCount = port->txCount; /* 17.18.40 */ entry->lnkWhile = port->lnkWhile; entry->rcvdInfoWhile = port->rcvdInfoWhile; entry->top_change_ack = port->tcAck; entry->tc = port->tc; RSTP_CRITICAL_PATH_END; return 0; } int STP_IN_stpm_get_state (IN int vlan_id, OUT UID_STP_STATE_T* entry) { register STPM_T* this; RSTP_CRITICAL_PATH_START; this = stpapi_stpm_find (vlan_id); if (!this) { /* it had not yet been created :( */ RSTP_CRITICAL_PATH_END; return STP_Vlan_Had_Not_Yet_Been_Created; } (void) strncpy (entry->vlan_name, this->name, NAME_LEN); entry->vlan_id = this->vlan_id; _conv_br_id_2_uid (&this->rootPrio.root_bridge, &entry->designated_root); entry->root_path_cost = this->rootPrio.root_path_cost; entry->root_port = this->rootPortId; entry->max_age = this->rootTimes.MaxAge; entry->forward_delay = this->rootTimes.ForwardDelay; entry->hello_time = this->rootTimes.HelloTime; _conv_br_id_2_uid (&this->BrId, &entry->bridge_id); entry->stp_enabled = this->admin_state; entry->timeSince_Topo_Change = this->timeSince_Topo_Change; entry->Topo_Change_Count = this->Topo_Change_Count; entry->Topo_Change = this->Topo_Change; RSTP_CRITICAL_PATH_END; return 0; } int STP_IN_stpm_get_name_by_vlan_id (int vlan_id, char* name, size_t buffsize) { register STPM_T* stpm; int iret = -1; RSTP_CRITICAL_PATH_START; for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { if (vlan_id == stpm->vlan_id) { if (stpm->name) (void) strncpy (name, stpm->name, buffsize); else (void) memset (name, 0, buffsize); iret = 0; break; } } RSTP_CRITICAL_PATH_END; return iret; } int /* call it, when link Up/Down */ STP_IN_enable_port (int port_index, Bool enable) { register STPM_T* stpm; RSTP_CRITICAL_PATH_START; if (! enable) { #ifdef STP_DBG stp_trace("%s (p%02d, all, %s, '%s')", "clearFDB", (int) port_index, "this port", "disable port"); #endif STP_OUT_flush_lt (port_index, 0, LT_FLASH_ONLY_THE_PORT, "disable port"); } for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { if (STP_ENABLED != stpm->admin_state) continue; _stp_in_enable_port_on_stpm (stpm, port_index, enable); /* STP_stpm_update (stpm);*/ } RSTP_CRITICAL_PATH_END; return 0; } int /* call it, when port speed has been changed, speed in Kb/s */ STP_IN_changed_port_speed (int port_index, long speed) { register STPM_T* stpm; register PORT_T* port; RSTP_CRITICAL_PATH_START; for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { if (STP_ENABLED != stpm->admin_state) continue; port = _stpapi_port_find (stpm, port_index); if (! port) continue; port->operSpeed = speed; #ifdef STP_DBG if (port->pcost->debug) { stp_trace ("changed operSpeed=%lu", port->operSpeed); } #endif port->reselect = True; port->selected = False; } RSTP_CRITICAL_PATH_END; return 0; } int /* call it, when port duplex mode has been changed */ STP_IN_changed_port_duplex (int port_index) { register STPM_T* stpm; register PORT_T* port; RSTP_CRITICAL_PATH_START; for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { if (STP_ENABLED != stpm->admin_state) continue; port = _stpapi_port_find (stpm, port_index); if (! port) continue; #ifdef STP_DBG if (port->p2p->debug) { stp_trace ("STP_IN_changed_port_duplex(%s)", port->port_name); } #endif port->p2p_recompute = True; port->reselect = True; port->selected = False; } RSTP_CRITICAL_PATH_END; return 0; } int STP_IN_check_bpdu_header (BPDU_T* bpdu, size_t len) { unsigned short len8023; /* LINTED: alignment */ len8023 = ntohs (*(unsigned short*) bpdu->eth.len8023); if (len8023 > 1500) {/* big len8023 format :( */ return STP_Big_len8023_Format; } if (len8023 < MIN_BPDU) { /* small len8023 format :( */ return STP_Small_len8023_Format; } if (len8023 + 14 > len) { /* len8023 format gt len :( */ return STP_len8023_Format_Gt_Len; } if (bpdu->eth.dsap != BPDU_L_SAP || bpdu->eth.ssap != BPDU_L_SAP || bpdu->eth.llc != LLC_UI) { /* this is not a proper 802.3 pkt! :( */ return STP_Not_Proper_802_3_Packet; } if (bpdu->hdr.protocol[0] || bpdu->hdr.protocol[1]) { return STP_Invalid_Protocol; } #if 0 if (bpdu->hdr.version != BPDU_VERSION_ID) { return STP_Invalid_Version; } #endif /* see also 9.3.4: think & TBD :( */ return 0; } #ifdef STP_DBG int dbg_rstp_deny = 0; #endif int STP_IN_rx_bpdu (int vlan_id, int port_index, BPDU_T* bpdu, size_t len) { register PORT_T* port; register STPM_T* this; int iret; #ifdef STP_DBG if (1 == dbg_rstp_deny) { return 0; } #endif RSTP_CRITICAL_PATH_START; this = stpapi_stpm_find (vlan_id); if (! this) { /* the stpm had not yet been created :( */ RSTP_CRITICAL_PATH_END; return STP_Vlan_Had_Not_Yet_Been_Created; } if (STP_DISABLED == this->admin_state) {/* the stpm had not yet been enabled :( */ RSTP_CRITICAL_PATH_END; return STP_Had_Not_Yet_Been_Enabled_On_The_Vlan; } port = _stpapi_port_find (this, port_index); if (! port) {/* port is absent in the stpm :( */ #ifdef STP_DBG stp_trace ("RX bpdu vlan_id=%d port=%d port is absent in the stpm :(", (int) vlan_id, (int) port_index); #endif RSTP_CRITICAL_PATH_END; return STP_Port_Is_Absent_In_The_Vlan; } #ifdef STP_DBG if (port->skip_rx > 0) { if (1 == port->skip_rx) stp_trace ("port %s stop rx skipping", port->port_name); else stp_trace ("port %s skip rx %d", port->port_name, port->skip_rx); port->skip_rx--; RSTP_CRITICAL_PATH_END; return STP_Nothing_To_Do; } #endif if (port->operEdge && ! port->lnkWhile && port->portEnabled) { #ifdef STP_DBG if (port->topoch->debug) { stp_trace ("port %s tc=TRUE by operEdge", port->port_name); } #endif port->tc = True; /* IEEE 802.1y, 17.30 */ } /* port link change indication will come later :( */ if (! port->portEnabled && STP_OUT_get_port_link_status (port->port_index)) { _stp_in_enable_port_on_stpm (this, port->port_index, True); } #ifdef STP_DBG if (port->edge->debug && port->operEdge) { stp_trace ("port %s not operEdge !", port->port_name); } #endif port->operEdge = False; port->wasInitBpdu = True; iret = STP_port_rx_bpdu (port, bpdu, len); (void) STP_stpm_update (this); RSTP_CRITICAL_PATH_END; return iret; } int STP_IN_one_second (void) { register STPM_T* stpm; register int dbg_cnt = 0; RSTP_CRITICAL_PATH_START; for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { if (STP_ENABLED == stpm->admin_state) { /* stp_trace ("STP_IN_one_second vlan_id=%d", (int) stpm->vlan_id); */ STP_stpm_one_second (stpm); dbg_cnt++; } } RSTP_CRITICAL_PATH_END; return dbg_cnt; } int STP_IN_stpm_set_cfg (IN int vlan_id, IN UID_STP_CFG_T* uid_cfg) { int rc = 0, prev_prio, err_code; Bool created_here, enabled_here; register STPM_T* this; UID_STP_CFG_T old; /* stp_trace ("STP_IN_stpm_set_cfg"); */ if (0 != STP_IN_stpm_get_cfg (vlan_id, &old)) { STP_OUT_get_init_stpm_cfg (vlan_id, &old); } RSTP_CRITICAL_PATH_START; if (BR_CFG_PRIO & uid_cfg->field_mask) { old.bridge_priority = uid_cfg->bridge_priority; } if (BR_CFG_AGE & uid_cfg->field_mask) { old.max_age = uid_cfg->max_age; } if (BR_CFG_HELLO & uid_cfg->field_mask) { old.hello_time = uid_cfg->hello_time; } if (BR_CFG_DELAY & uid_cfg->field_mask) { old.forward_delay = uid_cfg->forward_delay; } if (BR_CFG_FORCE_VER & uid_cfg->field_mask) { old.force_version = uid_cfg->force_version; } rc = _check_stpm_config (&old); if (0 != rc) { stp_trace ("_check_stpm_config failed %d", (int) rc); RSTP_CRITICAL_PATH_END; return rc; } if ((BR_CFG_STATE & uid_cfg->field_mask) && (STP_DISABLED == uid_cfg->stp_enabled)) { rc = _stp_in_stpm_enable (vlan_id, uid_cfg->vlan_name, STP_DISABLED); if (0 != rc) { stp_trace ("can't disable rc=%d", (int) rc); RSTP_CRITICAL_PATH_END; return rc; } uid_cfg->field_mask &= ~BR_CFG_STATE; if (! uid_cfg->field_mask) { RSTP_CRITICAL_PATH_END; return 0; } } /* get current state */ this = stpapi_stpm_find (vlan_id); created_here = False; enabled_here = False; if (! this) { /* it had not yet been created */ this = stp_in_stpm_create (vlan_id, uid_cfg->vlan_name, &err_code); if (! this) { RSTP_CRITICAL_PATH_END; return err_code; } } prev_prio = this->BrId.prio; this->BrId.prio = old.bridge_priority; if (STP_ENABLED == this->admin_state) { if (0 != STP_stpm_check_bridge_priority (this)) { this->BrId.prio = prev_prio; stp_trace ("%s", "STP_stpm_check_bridge_priority failed"); RSTP_CRITICAL_PATH_END; return STP_Invalid_Bridge_Priority; } } this->BrTimes.MaxAge = old.max_age; this->BrTimes.HelloTime = old.hello_time; this->BrTimes.ForwardDelay = old.forward_delay; this->ForceVersion = (PROTOCOL_VERSION_T) old.force_version; if ((BR_CFG_STATE & uid_cfg->field_mask) && STP_DISABLED != uid_cfg->stp_enabled && STP_DISABLED == this->admin_state) { rc = _stp_in_stpm_enable (vlan_id, uid_cfg->vlan_name, uid_cfg->stp_enabled); if (0 != rc) { stp_trace ("%s", "cannot enable"); if (created_here) { STP_stpm_delete (this); } RSTP_CRITICAL_PATH_END; return rc; } enabled_here = True; } if (! enabled_here && STP_DISABLED != this->admin_state) { STP_stpm_update_after_bridge_management (this); } RSTP_CRITICAL_PATH_END; return 0; } int STP_IN_port_set_cfg (IN int vlan_id, IN int port_index, IN UID_STP_PORT_CFG_T* uid_cfg) { register STPM_T* this; register PORT_T* port; RSTP_CRITICAL_PATH_START; this = stpapi_stpm_find (vlan_id); if (! this) { /* it had not yet been created :( */ RSTP_CRITICAL_PATH_END; stp_trace ("RSTP instance with tag %d hasn't been created\n", vlan_id); return STP_Vlan_Had_Not_Yet_Been_Created; } port = _stpapi_port_find (this, port_index); if (! port) {/* port is absent in the stpm :( */ return STP_Port_Is_Absent_In_The_Vlan; } if (PT_CFG_MCHECK & uid_cfg->field_mask) { if (this->ForceVersion >= NORMAL_RSTP) port->mcheck = True; } if (PT_CFG_COST & uid_cfg->field_mask) { port->adminPCost = uid_cfg->admin_port_path_cost; } if (PT_CFG_PRIO & uid_cfg->field_mask) { port->port_id = (uid_cfg->port_priority << 8) + port_index; } if (PT_CFG_P2P & uid_cfg->field_mask) { port->adminPointToPointMac = uid_cfg->admin_point2point; port->p2p_recompute = True; } if (PT_CFG_EDGE & uid_cfg->field_mask) { port->adminEdge = uid_cfg->admin_edge; port->operEdge = port->adminEdge; #ifdef STP_DBG if (port->edge->debug) { stp_trace ("port %s is operEdge=%c in STP_IN_port_set_cfg", port->port_name, port->operEdge ? 'Y' : 'n'); } #endif } if (PT_CFG_NON_STP & uid_cfg->field_mask) { #ifdef STP_DBG if (port->roletrns->debug && port->admin_non_stp != uid_cfg->admin_non_stp) { stp_trace ("port %s is adminNonStp=%c in STP_IN_port_set_cfg", port->port_name, uid_cfg->admin_non_stp ? 'Y' : 'n'); } #endif port->admin_non_stp = uid_cfg->admin_non_stp; } #ifdef STP_DBG if (PT_CFG_DBG_SKIP_RX & uid_cfg->field_mask) { port->skip_rx = uid_cfg->skip_rx; } if (PT_CFG_DBG_SKIP_TX & uid_cfg->field_mask) { port->skip_tx = uid_cfg->skip_tx; } #endif port->reselect = True; port->selected = False; (void) STP_stpm_update (this); RSTP_CRITICAL_PATH_END; return 0; } #ifdef STP_DBG int STP_IN_dbg_set_port_trace (char* mach_name, int enadis, int vlan_id, int port_no) { register STPM_T* this; register PORT_T* port; int rc; RSTP_CRITICAL_PATH_START; this = stpapi_stpm_find (vlan_id); if (! this) { /* it had not yet been created :( */ RSTP_CRITICAL_PATH_END; stp_trace ("RSTP instance with tag %d hasn't been created\n", vlan_id); return STP_Vlan_Had_Not_Yet_Been_Created; } port = _stpapi_port_find (this, port_no); if (! port) {/* port is absent in the stpm :( */ return STP_Port_Is_Absent_In_The_Vlan; } rc = STP_port_trace_state_machine (port, mach_name, enadis); RSTP_CRITICAL_PATH_END; return rc; } #endif const char* STP_IN_get_error_explanation (int rstp_err_no) { #define CHOOSE(a) #a static char* rstp_error_names[] = RSTP_ERRORS; #undef CHOOSE if (rstp_err_no < STP_OK) { return "Too small error code :("; } if (rstp_err_no >= STP_LAST_DUMMY) { return "Too big error code :("; } return rstp_error_names[rstp_err_no]; } int STP_IN_port_add(int vlan_id, int port_index) { STPM_T *this; PORT_T *port; int rc = STP_OK; RSTP_CRITICAL_PATH_START; this = stpapi_stpm_find (vlan_id); if (!this) { /* it had not yet been created :( */ RSTP_CRITICAL_PATH_END; return STP_Vlan_Had_Not_Yet_Been_Created; } port = this->ports; if (! STP_port_create (this, port_index)) { /* can't add port :( */ stp_trace ("can't create port %d", port_index); RSTP_CRITICAL_PATH_END; return STP_Cannot_Create_Instance_For_Port; } if (!port) rc = STP_stpm_start (this); RSTP_CRITICAL_PATH_END; return rc; } int STP_IN_port_remove(int vlan_id, int port_index) { STPM_T *this; PORT_T *port; RSTP_CRITICAL_PATH_START; this = stpapi_stpm_find (vlan_id); if (!this) { /* it had not yet been created :( */ RSTP_CRITICAL_PATH_END; return STP_Vlan_Had_Not_Yet_Been_Created; } port = _stpapi_port_find (this, port_index); if (! port) {/* port is absent in the stpm :( */ RSTP_CRITICAL_PATH_END; return STP_Port_Is_Absent_In_The_Vlan; } STP_port_delete (port); if (!this->ports) STP_stpm_stop (this); RSTP_CRITICAL_PATH_END; return STP_OK; } void STP_IN_get_bridge_id(int vlan_id, unsigned short *priority, unsigned char *mac) { STPM_T *this; RSTP_CRITICAL_PATH_START; this = stpapi_stpm_find (vlan_id); *priority = this->BrId.prio; (void) memcpy(mac, this->BrId.addr, 6); RSTP_CRITICAL_PATH_END; } const char * STP_IN_state2str(RSTP_PORT_STATE state) { switch (state) { case UID_PORT_DISABLED: return ("disabled"); case UID_PORT_DISCARDING: return ("discarding"); case UID_PORT_LEARNING: return ("learning"); case UID_PORT_FORWARDING: return ("forwarding"); case UID_PORT_NON_STP: return ("non-stp"); case UID_PORT_BADSDU: /* synthetic state used by daemon */ return ("bad-mtu"); } return ("unknown"); } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* This file contains prototypes for API from an operation system to the RSTP */ #ifndef _STP_API_H__ #define _STP_API_H__ #include #define STP_DBG 1 /************************ * Common base constants ************************/ #ifndef INOUT # define IN /* consider as comments near 'input' parameters */ # define OUT /* consider as comments near 'output' parameters */ # define INOUT /* consider as comments near 'input/output' parameters */ #endif #ifndef Zero # define Zero 0 # define One 1 #endif #ifndef Bool # define Bool int # define False 0 # define True 1 #endif /******************************************** * constants: default values and linitations *********************************************/ /* bridge configuration */ #define DEF_BR_PRIO 32768 #define MIN_BR_PRIO 0 #define MAX_BR_PRIO 61440 #define DEF_BR_HELLOT 2 #define MIN_BR_HELLOT 1 #define MAX_BR_HELLOT 10 #define DEF_BR_MAXAGE 20 #define MIN_BR_MAXAGE 6 #define MAX_BR_MAXAGE 40 #define DEF_BR_FWDELAY 15 #define MIN_BR_FWDELAY 4 #define MAX_BR_FWDELAY 30 #define IEEE_TIMER_SCALE 256 /* Note that this works with unscaled values */ #define CHECK_BRIDGE_CONFIG(cfg) \ (2 * (cfg.forward_delay - 1) >= cfg.max_age && \ cfg.max_age >= 2 * (cfg.hello_time + 1)) /* * These macros provide limits and tests for displaying comprehensible errors. */ #define NO_MAXAGE(cfg) ((cfg.forward_delay - 1) < (cfg.hello_time + 1)) #define MIN_FWDELAY_NOM(cfg) \ (cfg.hello_time < MIN_BR_FWDELAY - 2 ? MIN_BR_FWDELAY : \ cfg.hello_time + 2) #define MAX_HELLOTIME_NOM(cfg) \ (cfg.forward_delay > MAX_BR_HELLOT + 2 ? MAX_BR_HELLOT : \ cfg.forward_delay - 2) #define SMALL_MAXAGE(cfg) (cfg.max_age < 2 * (cfg.hello_time + 1)) #define MIN_MAXAGE(cfg) \ (cfg.hello_time < (MIN_BR_MAXAGE / 2 - 1) ? MIN_BR_MAXAGE : \ (2 * (cfg.hello_time + 1))) #define MAX_HELLOTIME(cfg) \ (cfg.max_age > 2 * (MAX_BR_HELLOT + 1) ? MAX_BR_HELLOT : \ (cfg.max_age / 2 - 1)) #define MIN_FWDELAY(cfg) (cfg.max_age / 2 + 1) #define MAX_MAXAGE(cfg) \ (cfg.forward_delay > (MAX_BR_MAXAGE / 2 + 1) ? MAX_BR_MAXAGE : \ (2 * (cfg.forward_delay - 1))) #define CAPPED_MAXAGE(cfg) (cfg.forward_delay < (MAX_BR_MAXAGE / 2 + 1)) #define FLOORED_MAXAGE(cfg) (cfg.hello_time > (MIN_BR_MAXAGE / 2 - 1)) #define DEF_FORCE_VERS 2 /* NORMAL_RSTP */ /* port configuration */ #define DEF_PORT_PRIO 128 #define MIN_PORT_PRIO 0 #define MAX_PORT_PRIO 240 /* in steps of 16 */ #define DEF_ADMIN_NON_STP False #define DEF_ADMIN_EDGE True #define DEF_LINK_DELAY 3 /* see edge.c */ #define DEF_P2P P2P_AUTO #include #include #ifndef __STPM_T__ #define __STPM_T__ struct stpm_t; typedef struct stpm_t STPM_T; #endif #ifndef __STP_VECTORS_T__ #define __STP_VECTORS_T__ struct stp_vectors; typedef struct stp_vectors STP_VECTORS_T; #endif /* Section 1: Create/Delete/Start/Stop the RSTP instance */ void /* init the engine */ STP_IN_init (STP_VECTORS_T *vectors); int STP_IN_stpm_create (int vlan_id, char* name); int STP_IN_stpm_delete (int vlan_id); int STP_IN_port_add (int vlan_id, int port_index); int STP_IN_port_remove (int vlan_id, int port_index); int STP_IN_stop_all (void); int STP_IN_delete_all (void); /* Section 2. "Get" management */ Bool STP_IN_get_is_stpm_enabled (int vlan_id); int STP_IN_stpm_get_vlan_id_by_name (char* name, int* vlan_id); int STP_IN_stpm_get_name_by_vlan_id (int vlan_id, char* name, size_t buffsize); const char* STP_IN_get_error_explanation (int rstp_err_no); int STP_IN_stpm_get_cfg (int vlan_id, UID_STP_CFG_T* uid_cfg); int STP_IN_stpm_get_state (int vlan_id, UID_STP_STATE_T* entry); int STP_IN_port_get_cfg (int vlan_id, int port_index, UID_STP_PORT_CFG_T* uid_cfg); int STP_IN_port_get_state (int vlan_id, UID_STP_PORT_STATE_T* entry); const char * STP_IN_state2str(RSTP_PORT_STATE); /* Section 3. "Set" management */ int STP_IN_stpm_set_cfg (int vlan_id, UID_STP_CFG_T* uid_cfg); int STP_IN_port_set_cfg (int vlan_id, int port_index, UID_STP_PORT_CFG_T* uid_cfg); #ifdef STP_DBG int STP_IN_dbg_set_port_trace (char *mach_name, int enadis, int vlan_id, int port_index); #endif /* Section 4. RSTP functionality events */ int STP_IN_one_second (void); int /* for Link UP/DOWN */ STP_IN_enable_port (int port_index, Bool enable); int /* call it, when port speed has been changed, speed in Kb/s */ STP_IN_changed_port_speed (int port_index, long speed); int /* call it, when current port duplex mode has been changed */ STP_IN_changed_port_duplex (int port_index); int STP_IN_check_bpdu_header (BPDU_T* bpdu, size_t len); int STP_IN_rx_bpdu (int vlan_id, int port_index, BPDU_T* bpdu, size_t len); void STP_IN_get_bridge_id(int vlan_id, unsigned short *priority, unsigned char *mac); #endif /* _STP_API_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* This file contains prototypes for system dependent API from the RSTP to an operation system */ #ifndef _STP_OUT_H__ #define _STP_OUT_H__ #include "stp_vectors.h" #define STP_OUT_flush_lt (*stp_vectors->flush_lt) #define STP_OUT_get_port_mac (*stp_vectors->get_port_mac) #define STP_OUT_get_port_oper_speed (*stp_vectors->get_port_oper_speed) #define STP_OUT_get_port_link_status (*stp_vectors->get_port_link_status) #define STP_OUT_get_duplex (*stp_vectors->get_duplex) #ifdef STRONGLY_SPEC_802_1W #define STP_OUT_set_learning (*stp_vectors->set_learning) #define STP_OUT_set_forwarding (*stp_vectors->set_forwarding) #else #define STP_OUT_set_port_state (*stp_vectors->set_port_state) #endif #define STP_OUT_set_hardware_mode (*stp_vectors->set_hardware_mode) #define STP_OUT_tx_bpdu (*stp_vectors->tx_bpdu) #define STP_OUT_get_port_name (*stp_vectors->get_port_name) #define STP_OUT_get_init_stpm_cfg (*stp_vectors->get_init_stpm_cfg) #define STP_OUT_get_init_port_cfg (*stp_vectors->get_init_port_cfg) #endif /* _STP_OUT_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ #ifndef __STP_VECTORS_H__ #define __STP_VECTORS_H__ /* This file defines callback vectors to be supplied by library user */ /* In the best case: clean all Learning entries with the vlan_id and the port (if 'exclude'=0) or for all ports, exclude the port (if ''exclude'=1). If 'port'=0, delete all entries with the vlan_id, don't care to 'exclude' */ typedef enum { LT_FLASH_ALL_PORTS_EXCLUDE_THIS, LT_FLASH_ONLY_THE_PORT } LT_FLASH_TYPE_T; struct stp_vectors { int (*flush_lt)(IN int port_index, IN int vlan_id, IN LT_FLASH_TYPE_T type, IN char* reason); /* for bridge id calculation */ void (*get_port_mac) (IN int port_index, OUT unsigned char* mac); unsigned long (*get_port_oper_speed) (IN unsigned int portNo); /* 1- Up, 0- Down */ int (*get_port_link_status) (IN int port_index); /* 1- Full, 0- Half */ int (*get_duplex) (IN int port_index); #ifdef STRONGLY_SPEC_802_1W int (*set_learning) (IN int port_index, IN int vlan_id, IN int enable); int (*set_forwarding) (IN int port_index, IN int vlan_id, IN int enable); #else /* * In many kinds of hardware the state of ports may * be changed with another method */ int (*set_port_state) (IN int port_index, IN int vlan_id, IN RSTP_PORT_STATE state); #endif int (*set_hardware_mode) (int vlan_id, UID_STP_MODE_T mode); int (*tx_bpdu) (IN int port_index, IN int vlan_id, IN unsigned char* bpdu, IN size_t bpdu_len); const char *(*get_port_name) (IN int port_index); int (*get_init_stpm_cfg) (IN int vlan_id, INOUT UID_STP_CFG_T* cfg); int (*get_init_port_cfg) (IN int vlan_id, IN int port_index, INOUT UID_STP_PORT_CFG_T* cfg); void (*trace)(const char *fmt, ...); }; #ifndef __STP_VECTORS_T__ #define __STP_VECTORS_T__ typedef struct stp_vectors STP_VECTORS_T; #endif #ifdef __STP_INTERNAL__ extern STP_VECTORS_T *stp_vectors; #endif #endif /* __STP_VECTORS_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* STP machine instance : bridge per VLAN: 17.17 */ #include "base.h" #include "stpm.h" #include "stp_to.h" /* for STP_OUT_flush_lt */ static STPM_T *bridges = NULL; static int _stp_stpm_init_machine (STATE_MACH_T* this) { this->State = BEGIN; (*(this->concreteEnterState)) (this); return 0; } static int _stp_stpm_iterate_machines (STPM_T* this, int (*iter_callb) (STATE_MACH_T*), Bool exit_on_non_zero_ret) { register STATE_MACH_T* stater; register PORT_T* port; int iret, mret = 0; /* state machines per bridge */ for (stater = this->machines; stater; stater = stater->next) { iret = (*iter_callb) (stater); if (exit_on_non_zero_ret && iret) return iret; else mret += iret; } /* state machines per port */ for (port = this->ports; port; port = port->next) { for (stater = port->machines; stater; stater = stater->next) { iret = (*iter_callb) (stater); if (exit_on_non_zero_ret && iret) return iret; else mret += iret; } } return mret; } void _stp_stpm_init_data (STPM_T* this) { STP_VECT_create (&this->rootPrio, &this->BrId, 0, &this->BrId, 0, 0); this->BrTimes.MessageAge = 0; STP_copy_times (&this->rootTimes, &this->BrTimes); } static unsigned char _check_topoch (STPM_T* this) { register PORT_T* port; for (port = this->ports; port; port = port->next) { if (port->tcWhile) { return 1; } } return 0; } void STP_stpm_one_second (STPM_T* param) { STPM_T* this = (STPM_T*) param; register PORT_T* port; register int iii; if (STP_ENABLED != this->admin_state) return; for (port = this->ports; port; port = port->next) { for (iii = 0; iii < TIMERS_NUMBER; iii++) { if (*(port->timers[iii]) > 0) { (*port->timers[iii])--; } } port->uptime++; } (void) STP_stpm_update (this); this->Topo_Change = _check_topoch (this); if (this->Topo_Change) { this->Topo_Change_Count++; this->timeSince_Topo_Change = 0; } else { this->Topo_Change_Count = 0; this->timeSince_Topo_Change++; } } STPM_T* STP_stpm_create (int vlan_id, char* name) { STPM_T* this; STP_NEW_IN_LIST(this, STPM_T, bridges, "stp instance"); this->admin_state = STP_DISABLED; this->vlan_id = vlan_id; if (name) { STP_STRDUP(this->name, name, "stp bridge name"); } this->machines = NULL; this->ports = NULL; STP_STATE_MACH_IN_LIST(rolesel); #ifdef STP_DBG /* this->rolesel->debug = 2; */ #endif return this; } int STP_stpm_enable (STPM_T* this, UID_STP_MODE_T admin_state) { int rc = 0; if (admin_state == this->admin_state) { /* nothing to do :) */ return 0; } if (STP_ENABLED == admin_state) { if (this->ports) rc = STP_stpm_start (this); this->admin_state = admin_state; } else { this->admin_state = admin_state; STP_stpm_stop (this); } return rc; } void STP_stpm_delete (STPM_T* this) { register STPM_T* tmp; register STPM_T* prev; register STATE_MACH_T* stater; register PORT_T* port; register void* pv; (void) STP_stpm_enable (this, STP_DISABLED); for (stater = this->machines; stater; ) { pv = (void*) stater->next; STP_state_mach_delete (stater); this->machines = stater = (STATE_MACH_T*) pv; } for (port = this->ports; port; ) { pv = (void*) port->next; STP_port_delete (port); this->ports = port = (PORT_T*) pv; } prev = NULL; for (tmp = bridges; tmp; tmp = tmp->next) { if (tmp->vlan_id == this->vlan_id) { if (prev) { prev->next = this->next; } else { bridges = this->next; } if (this->name) STP_FREE(this->name, "stp bridge name"); STP_FREE(this, "stp instance"); break; } prev = tmp; } } int STP_stpm_start (STPM_T* this) { register PORT_T* port; if (! this->ports) { /* there are not any ports :( */ return STP_There_Are_No_Ports; } if (! STP_compute_bridge_id (this)) {/* can't compute bridge id ? :( */ return STP_Cannot_Compute_Bridge_Prio; } /* check, that the stpm has unique bridge Id */ if (0 != STP_stpm_check_bridge_priority (this)) { /* there is an enabled bridge with same ID :( */ return STP_Invalid_Bridge_Priority; } _stp_stpm_init_data (this); for (port = this->ports; port; port = port->next) { STP_port_init (port, this, True); } #ifndef STRONGLY_SPEC_802_1W /* A. see comment near STRONGLY_SPEC_802_1W in topoch.c */ /* B. port=0 here means: delete for all ports */ #ifdef STP_DBG stp_trace("%s (all, start stpm)", "clearFDB"); #endif STP_OUT_flush_lt (0, this->vlan_id, LT_FLASH_ONLY_THE_PORT, "start stpm"); #endif (void) _stp_stpm_iterate_machines (this, _stp_stpm_init_machine, False); (void) STP_stpm_update (this); return 0; } /* ARGSUSED */ void STP_stpm_stop (STPM_T* this) { } int STP_stpm_update (STPM_T* this) /* returns number of loops */ { register Bool need_state_change; register int number_of_loops = 0; need_state_change = False; for (;;) {/* loop until not need changes */ need_state_change = _stp_stpm_iterate_machines (this, STP_check_condition, True); if (! need_state_change) break; number_of_loops++; /* here we know, that at least one stater must be updated (it has changed state) */ number_of_loops += _stp_stpm_iterate_machines (this, STP_change_state, False); } return number_of_loops; } BRIDGE_ID * STP_compute_bridge_id (STPM_T* this) { register PORT_T* port; register PORT_T* min_num_port = NULL; int port_index = 0; for (port = this->ports; port; port = port->next) { if (! port_index || port->port_index < port_index) { min_num_port = port; port_index = port->port_index; } } if (! min_num_port) return NULL; /* IMHO, it may not be */ STP_OUT_get_port_mac (min_num_port->port_index, this->BrId.addr); return &this->BrId; } STPM_T* STP_stpm_get_the_list (void) { return bridges; } void STP_stpm_update_after_bridge_management (STPM_T* this) { register PORT_T* port; for (port = this->ports; port; port = port->next) { port->reselect = True; port->selected = False; } } int STP_stpm_check_bridge_priority (STPM_T* this) { register STPM_T* oth; for (oth = bridges; oth; oth = oth->next) { if (STP_ENABLED == oth->admin_state && oth != this && ! STP_VECT_compare_bridge_id (&this->BrId, &oth->BrId)) { return STP_Invalid_Bridge_Priority; } } return 0; } const char* STP_stpm_get_port_name_by_id (STPM_T* this, PORT_ID port_id) { register PORT_T* port; for (port = this->ports; port; port = port->next) { if (port_id == port->port_id) { return port->port_name; } } return "Undef?"; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* STP machine instance : bridge per VLAN: 17.17 */ /* The Clause 17.13 points: "NOTE:The operation of the Bridge as a whole can * be represented by the interaction between Bridge Ports specified, * and by parameters of the Bridge stored in 'Port 0'. This removes the * need for any 'per Bridge' specification elements, and helps ensure * the minimum dependencies between Bridge Ports. This in turn supports * the development of implementations that scale well with increasing * numbers of Bridge Ports. This shift of focus to 'per Port operation' * for the RSTP is supported by underlying technical changes from the * Spanning Tree Algorithm and Protocol (Clause 8):" * Newetheless, it seems to me, the behaviour of of the bridge, its variables * and functions are so distinct from Port, that I decided to design Bridge * instance. I called this object 'stpm' from STP machine. I'd like to see * another procedural model, more corresponding to this note on Clause 17.13 */ #ifndef _STP_MACHINE_H__ #define _STP_MACHINE_H__ #include "port.h" #include "rolesel.h" #define TxHoldCount 3 /* 17.16.6, 17.28.2(Table 17-5) */ typedef enum {/* 17.12, 17.16.1 */ FORCE_STP_COMPAT = 0, NORMAL_RSTP = 2 } PROTOCOL_VERSION_T; struct stpm_t { struct stpm_t* next; struct port_t* ports; /* The only "per bridge" state machine */ STATE_MACH_T* rolesel; /* the Port Role Selection State machione: 17.22 */ STATE_MACH_T* machines; /* variables */ PROTOCOL_VERSION_T ForceVersion; /* 17.12, 17.16.1 */ BRIDGE_ID BrId; /* 17.17.2 */ TIMEVALUES_T BrTimes; /* 17.17.4 */ PORT_ID rootPortId; /* 17.17.5 */ PRIO_VECTOR_T rootPrio; /* 17.17.6 */ TIMEVALUES_T rootTimes; /* 17.17.7 */ int vlan_id; /* let's say: tag */ char* name; /* name of the VLAN, maily for debugging */ UID_STP_MODE_T admin_state; /* STP_DISABLED or STP_ENABLED; type see in UiD */ unsigned long timeSince_Topo_Change; /* 14.8.1.1.3.b */ unsigned long Topo_Change_Count; /* 14.8.1.1.3.c */ unsigned char Topo_Change; /* 14.8.1.1.3.d */ }; #ifndef __STPM_T__ #define __STPM_T__ typedef struct stpm_t STPM_T; #endif /* Functions prototypes */ void STP_stpm_one_second (STPM_T* param); STPM_T* STP_stpm_create (int vlan_id, char* name); int STP_stpm_enable (STPM_T* this, UID_STP_MODE_T admin_state); void STP_stpm_delete (STPM_T* this); int STP_stpm_start (STPM_T* this); void STP_stpm_stop (STPM_T* this); int STP_stpm_update (STPM_T* this); BRIDGE_ID * STP_compute_bridge_id (STPM_T* this); STPM_T * STP_stpm_get_the_list (void); void STP_stpm_update_after_bridge_management (STPM_T* this); int STP_stpm_check_bridge_priority (STPM_T* this); const char* STP_stpm_get_port_name_by_id (STPM_T* this, PORT_ID port_id); STPM_T* stpapi_stpm_find (int vlan_id); int stp_in_stpm_enable (int vlan_id, char* name, UID_STP_MODE_T admin_state); void* stp_in_stpm_create (int vlan_id, char *name, int *err_code); #endif /* _STP_MACHINE_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* This file contains API from an operation system to the RSTP library */ #include "base.h" #include "stpm.h" #include "stp_in.h" /* for bridge defaults */ #include "stp_to.h" int STP_IN_stpm_create (int vlan_id, char* name) { register STPM_T* this; int err_code; UID_STP_CFG_T init_cfg; stp_trace ("STP_IN_stpm_create(%s)", name); init_cfg.field_mask = BR_CFG_ALL; STP_OUT_get_init_stpm_cfg (vlan_id, &init_cfg); init_cfg.field_mask = 0; RSTP_CRITICAL_PATH_START; this = stp_in_stpm_create (vlan_id, name, &err_code); if (this) { this->BrId.prio = init_cfg.bridge_priority; this->BrTimes.MaxAge = init_cfg.max_age; this->BrTimes.HelloTime = init_cfg.hello_time; this->BrTimes.ForwardDelay = init_cfg.forward_delay; this->ForceVersion = (PROTOCOL_VERSION_T) init_cfg.force_version; } RSTP_CRITICAL_PATH_END; return err_code; } int STP_IN_stpm_delete (int vlan_id) { register STPM_T* this; int iret = 0; RSTP_CRITICAL_PATH_START; this = stpapi_stpm_find (vlan_id); if (! this) { /* it had not yet been created :( */ iret = STP_Vlan_Had_Not_Yet_Been_Created; } else { if (STP_ENABLED == this->admin_state) { if (0 != STP_stpm_enable (this, STP_DISABLED)) {/* can't disable :( */ iret = STP_Another_Error; } else STP_OUT_set_hardware_mode (vlan_id, STP_DISABLED); } if (0 == iret) { STP_stpm_delete (this); } } RSTP_CRITICAL_PATH_END; return iret; } int STP_IN_stpm_get_vlan_id_by_name (char* name, int* vlan_id) { register STPM_T* stpm; int iret = STP_Cannot_Find_Vlan; RSTP_CRITICAL_PATH_START; for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { if (stpm->name && ! strcmp (stpm->name, name)) { *vlan_id = stpm->vlan_id; iret = 0; break; } } RSTP_CRITICAL_PATH_END; return iret; } Bool STP_IN_get_is_stpm_enabled (int vlan_id) { STPM_T* this; Bool iret = False; RSTP_CRITICAL_PATH_START; this = stpapi_stpm_find (vlan_id); if (this) { if (this->admin_state == STP_ENABLED) { iret = True; } #ifdef notdef } else { ; /* it had not yet been created :( */ #endif } RSTP_CRITICAL_PATH_END; return iret; } int STP_IN_stop_all (void) { register STPM_T* stpm; RSTP_CRITICAL_PATH_START; for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { if (STP_DISABLED != stpm->admin_state) { STP_OUT_set_hardware_mode (stpm->vlan_id, STP_DISABLED); (void) STP_stpm_enable (stpm, STP_DISABLED); } } RSTP_CRITICAL_PATH_END; return 0; } int STP_IN_delete_all (void) { register STPM_T* stpm; RSTP_CRITICAL_PATH_START; for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { (void) STP_stpm_enable (stpm, STP_DISABLED); STP_stpm_delete (stpm); } RSTP_CRITICAL_PATH_END; return 0; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Port State Transition state machine : 17.24 */ #include "base.h" #include "stpm.h" #include "stp_to.h" #define STATES { \ CHOOSE(DISCARDING), \ CHOOSE(LEARNING), \ CHOOSE(FORWARDING) \ } #define GET_STATE_NAME STP_sttrans_get_state_name #include "choose.h" #ifdef STRONGLY_SPEC_802_1W static Bool disableLearning (STATE_MACH_T *this) { register PORT_T *port = this->owner.port; return STP_OUT_set_learning (port->port_index, port->owner->vlan_id, False); } static Bool enableLearning (STATE_MACH_T *this) { register PORT_T *port = this->owner.port; return STP_OUT_set_learning (port->port_index, port->owner->vlan_id, True); } static Bool disableForwarding (STATE_MACH_T *this) { register PORT_T *port = this->owner.port; return STP_OUT_set_forwarding (port->port_index, port->owner->vlan_id, False); } static Bool enableForwarding (STATE_MACH_T *this) { register PORT_T *port = this->owner.port; return STP_OUT_set_forwarding (port->port_index, port->owner->vlan_id, True); } #endif void STP_sttrans_enter_state (STATE_MACH_T *this) { register PORT_T *port = this->owner.port; switch (this->State) { case BEGIN: case DISCARDING: port->learning = False; port->forwarding = False; #ifdef STRONGLY_SPEC_802_1W disableLearning (this); disableForwarding (this); #else STP_OUT_set_port_state (port->port_index, port->owner->vlan_id, UID_PORT_DISCARDING); #endif break; case LEARNING: port->learning = True; #ifdef STRONGLY_SPEC_802_1W enableLearning (this); #else STP_OUT_set_port_state (port->port_index, port->owner->vlan_id, UID_PORT_LEARNING); #endif break; case FORWARDING: port->tc = !port->operEdge; port->forwarding = True; #ifdef STRONGLY_SPEC_802_1W enableForwarding (this); #else STP_OUT_set_port_state (port->port_index, port->owner->vlan_id, UID_PORT_FORWARDING); #endif break; } } Bool STP_sttrans_check_conditions (STATE_MACH_T *this) { register PORT_T *port = this->owner.port; switch (this->State) { case BEGIN: return STP_hop_2_state (this, DISCARDING); case DISCARDING: if (port->learn) { return STP_hop_2_state (this, LEARNING); } break; case LEARNING: if (port->forward) { return STP_hop_2_state (this, FORWARDING); } if (!port->learn) { return STP_hop_2_state (this, DISCARDING); } break; case FORWARDING: if (!port->forward) { return STP_hop_2_state (this, DISCARDING); } break; } return False; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Port State Transition state machine : 17.24 */ #ifndef _STP_STATE_TRANSIT_H__ #define _STP_STATE_TRANSIT_H__ void STP_sttrans_enter_state (STATE_MACH_T* s); Bool STP_sttrans_check_conditions (STATE_MACH_T* s); char* STP_sttrans_get_state_name (int state); #endif /* _STP_STATE_TRANSIT_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* "Times" API : bridgeTime, rootTimes, portTimes, designatedTimes, msgTimes */ #include "base.h" int STP_compare_times (IN TIMEVALUES_T *t1, IN TIMEVALUES_T *t2) { if (t1->MessageAge < t2->MessageAge) return -1; if (t1->MessageAge > t2->MessageAge) return 1; if (t1->MaxAge < t2->MaxAge) return -2; if (t1->MaxAge > t2->MaxAge) return 2; if (t1->ForwardDelay < t2->ForwardDelay) return -3; if (t1->ForwardDelay > t2->ForwardDelay) return 3; if (t1->HelloTime < t2->HelloTime) return -4; if (t1->HelloTime > t2->HelloTime) return 4; return 0; } void STP_get_times (IN BPDU_BODY_T *b, OUT TIMEVALUES_T *v) { /* LINTED: alignment */ v->MessageAge = ntohs (*((unsigned short*) b->message_age)) >> 8; /* LINTED: alignment */ v->MaxAge = ntohs (*((unsigned short*) b->max_age)) >> 8; /* LINTED: alignment */ v->ForwardDelay = ntohs (*((unsigned short*) b->forward_delay)) >> 8; /* LINTED: alignment */ v->HelloTime = ntohs (*((unsigned short*) b->hello_time)) >> 8; } void STP_set_times (IN TIMEVALUES_T *v, OUT BPDU_BODY_T *b) { unsigned short mt; #define STP_SET_TIME(f, t) \ mt = htons (f << 8); \ (void) memcpy (t, &mt, 2); STP_SET_TIME(v->MessageAge, b->message_age); STP_SET_TIME(v->MaxAge, b->max_age); STP_SET_TIME(v->ForwardDelay, b->forward_delay); STP_SET_TIME(v->HelloTime, b->hello_time); } void STP_copy_times (OUT TIMEVALUES_T *t, IN TIMEVALUES_T *f) { t->MessageAge = f->MessageAge; t->MaxAge = f->MaxAge; t->ForwardDelay = f->ForwardDelay; t->HelloTime = f->HelloTime; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* "Times" API : bridgeTime, rootTimes, portTimes, designatedTimes, msgTimes */ #ifndef _RSTP_TIMES_H__ #define _RSTP_TIMES_H__ typedef struct timevalues_t { unsigned short MessageAge; unsigned short MaxAge; unsigned short ForwardDelay; unsigned short HelloTime; } TIMEVALUES_T; int STP_compare_times (IN TIMEVALUES_T* t1, IN TIMEVALUES_T* t2); void STP_get_times (IN BPDU_BODY_T* b, OUT TIMEVALUES_T* v); void STP_set_times (IN TIMEVALUES_T* v, OUT BPDU_BODY_T* b); void STP_copy_times (OUT TIMEVALUES_T* t, IN TIMEVALUES_T* f); #endif /* _RSTP_TIMES_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Topolgy Change state machine : 17.25 */ #include "base.h" #include "stpm.h" #include "stp_to.h" /* for STP_OUT_flush_lt */ #define STATES { \ CHOOSE(INIT), \ CHOOSE(INACTIVE), \ CHOOSE(TCACTIVE), \ CHOOSE(DETECTED), \ CHOOSE(NOTIFIED_TC), \ CHOOSE(PROPAGATING), \ CHOOSE(ACKNOWLEDGED), \ CHOOSE(NOTIFIED_TCN) \ } #define GET_STATE_NAME STP_topoch_get_state_name #include "choose.h" #ifndef STRONGLY_SPEC_802_1W /* * In many kinds of hardware the function * STP_OUT_flush_lt is a) is very hard and b) cannot * delete learning emtries per port. The alternate * method may be used: we don't care operEdge flag here, * but clean learning table once for TopologyChange * for all ports, except the received port. I am ready to discuss :( * See below word STRONGLY_SPEC_802_1W */ #else static Bool flush (STATE_MACH_T *this, char* reason) /* 17.19.9 */ { register PORT_T* port = this->owner.port; Bool bret; if (port->operEdge) return True; if (this->debug) { stp_trace("%s (%s, %s, %s, '%s')", "flush", port->port_name, port->owner->name, LT_FLASH_ONLY_THE_PORT == type ? "this port" : "other ports", reason); } bret = STP_OUT_flush_lt (port->port_index, port->owner->vlan_id, LT_FLASH_ONLY_THE_PORT, reason); } #endif static void setTcPropBridge (STATE_MACH_T* this, char* reason) /* 17.19.14 */ { register PORT_T* port = this->owner.port; register PORT_T* tmp; for (tmp = port->owner->ports; tmp; tmp = tmp->next) { if (tmp->port_index != port->port_index) tmp->tcProp = True; } #ifndef STRONGLY_SPEC_802_1W #ifdef STP_DBG if (this->debug) { stp_trace("%s (%s, %s, %s, '%s')", "clearFDB", port->port_name, port->owner->name, "other ports", reason); } #endif STP_OUT_flush_lt (port->port_index, port->owner->vlan_id, LT_FLASH_ALL_PORTS_EXCLUDE_THIS, reason); #endif } static unsigned int newTcWhile (STATE_MACH_T* this) /* 17.19.7 */ { register PORT_T* port = this->owner.port; if (port->sendRSTP && port->operPointToPointMac) { return 2 * port->owner->rootTimes.HelloTime; } return port->owner->rootTimes.MaxAge; } void STP_topoch_enter_state (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; switch (this->State) { case BEGIN: case INIT: #ifdef STRONGLY_SPEC_802_1W flush (this, "topoch INIT"); #endif port->tcWhile = 0; port->tc = port->tcProp = port->tcAck = False; break; case INACTIVE: port->rcvdTc = port->rcvdTcn = port->rcvdTcAck = port->tc = port->tcProp = False; break; case TCACTIVE: break; case DETECTED: port->tcWhile = newTcWhile (this); #ifdef STP_DBG if (this->debug) stp_trace("DETECTED: tcWhile=%d on port %s", port->tcWhile, port->port_name); #endif setTcPropBridge (this, "DETECTED"); port->tc = False; break; case NOTIFIED_TC: port->rcvdTcn = port->rcvdTc = False; if (port->role == DesignatedPort) { port->tcAck = True; } setTcPropBridge (this, "NOTIFIED_TC"); break; case PROPAGATING: port->tcWhile = newTcWhile (this); #ifdef STP_DBG if (this->debug) stp_trace("PROPAGATING: tcWhile=%d on port %s", port->tcWhile, port->port_name); #endif #ifdef STRONGLY_SPEC_802_1W flush (this, "topoch PROPAGATING"); #endif port->tcProp = False; break; case ACKNOWLEDGED: port->tcWhile = 0; #ifdef STP_DBG if (this->debug) stp_trace("ACKNOWLEDGED: tcWhile=%d on port %s", port->tcWhile, port->port_name); #endif port->rcvdTcAck = False; break; case NOTIFIED_TCN: port->tcWhile = newTcWhile (this); #ifdef STP_DBG if (this->debug) stp_trace("NOTIFIED_TCN: tcWhile=%d on port %s", port->tcWhile, port->port_name); #endif break; }; } Bool STP_topoch_check_conditions (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; if (BEGIN == this->State) { return STP_hop_2_state (this, INIT); } switch (this->State) { case INIT: return STP_hop_2_state (this, INACTIVE); case INACTIVE: if (port->role == RootPort || port->role == DesignatedPort) return STP_hop_2_state (this, TCACTIVE); if (port->rcvdTc || port->rcvdTcn || port->rcvdTcAck || port->tc || port->tcProp) return STP_hop_2_state (this, INACTIVE); break; case TCACTIVE: if (port->role != RootPort && (port->role != DesignatedPort)) return STP_hop_2_state (this, INIT); if (port->tc) return STP_hop_2_state (this, DETECTED); if (port->rcvdTcn) return STP_hop_2_state (this, NOTIFIED_TCN); if (port->rcvdTc) return STP_hop_2_state (this, NOTIFIED_TC); if (port->tcProp && !port->operEdge) return STP_hop_2_state (this, PROPAGATING); if (port->rcvdTcAck) return STP_hop_2_state (this, ACKNOWLEDGED); break; case DETECTED: return STP_hop_2_state (this, TCACTIVE); case NOTIFIED_TC: return STP_hop_2_state (this, TCACTIVE); case PROPAGATING: return STP_hop_2_state (this, TCACTIVE); case ACKNOWLEDGED: return STP_hop_2_state (this, TCACTIVE); case NOTIFIED_TCN: return STP_hop_2_state (this, NOTIFIED_TC); }; return False; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Topolgy Change state machine : 17.25 */ #ifndef _STP_TOPO_CHANGE_H__ #define _STP_TOPO_CHANGE_H__ void STP_topoch_enter_state (STATE_MACH_T* s); Bool STP_topoch_check_conditions (STATE_MACH_T* s); char* STP_topoch_get_state_name (int state); #endif /* _STP_TOPO_CHANGE_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Port Transmit state machine : 17.27 */ #include "base.h" #include "stpm.h" #include "stp_to.h" /* for STP_OUT_get_port_mac & STP_OUT_tx_bpdu */ #define BPDU_LEN8023_OFF 12 #define STATES { \ CHOOSE(TRANSMIT_INIT), \ CHOOSE(TRANSMIT_PERIODIC), \ CHOOSE(IDLE), \ CHOOSE(TRANSMIT_CONFIG), \ CHOOSE(TRANSMIT_TCN), \ CHOOSE(TRANSMIT_RSTP) \ } #define GET_STATE_NAME STP_transmit_get_state_name #include "choose.h" #define MIN_FRAME_LENGTH 64 typedef struct tx_tcn_bpdu_t { MAC_HEADER_T mac; ETH_HEADER_T eth; BPDU_HEADER_T hdr; } TCN_BPDU_T; typedef struct tx_stp_bpdu_t { MAC_HEADER_T mac; ETH_HEADER_T eth; BPDU_HEADER_T hdr; BPDU_BODY_T body; } CONFIG_BPDU_T; typedef struct tx_rstp_bpdu_t { MAC_HEADER_T mac; ETH_HEADER_T eth; BPDU_HEADER_T hdr; BPDU_BODY_T body; unsigned char ver_1_length[2]; } RSTP_BPDU_T; static RSTP_BPDU_T bpdu_packet = { {/* MAC_HEADER_T */ {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}, /* dst_mac */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* src_mac */ }, { /* ETH_HEADER_T */ {0x00, 0x00}, /* len8023 */ BPDU_L_SAP, BPDU_L_SAP, LLC_UI /* dsap, ssap, llc */ }, {/* BPDU_HEADER_T */ {0x00, 0x00}, /* protocol */ BPDU_VERSION_ID, 0x00 /* version, bpdu_type */ }, { 0x00, /* flags; */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* root_id[8]; */ {0x00,0x00,0x00,0x00}, /* root_path_cost[4]; */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* bridge_id[8]; */ {0x00,0x00}, /* port_id[2]; */ {0x00,0x00}, /* message_age[2]; */ {0x00,0x00}, /* max_age[2]; */ {0x00,0x00}, /* hello_time[2]; */ {0x00,0x00}, /* forward_delay[2]; */ }, {0x00,0x00}, /* ver_1_length[2]; */ }; static size_t build_bpdu_header (int port_index, unsigned char bpdu_type, unsigned short pkt_len) { unsigned short len8023; STP_OUT_get_port_mac (port_index, bpdu_packet.mac.src_mac); bpdu_packet.hdr.bpdu_type = bpdu_type; bpdu_packet.hdr.version = (BPDU_RSTP == bpdu_type) ? BPDU_VERSION_RAPID_ID : BPDU_VERSION_ID; /* NOTE: I suppose, that sizeof(unsigned short)=2 ! */ len8023 = htons ((unsigned short) (pkt_len + 3)); (void) memcpy (&bpdu_packet.eth.len8023, &len8023, 2); if (pkt_len < MIN_FRAME_LENGTH) pkt_len = MIN_FRAME_LENGTH; return pkt_len; } static int txTcn (STATE_MACH_T* this) { /* 17.19.17 (page 68) & 9.3.2 (page 25) */ register size_t pkt_len; register int port_index, vlan_id; #ifdef STP_DBG if (this->owner.port->skip_tx > 0) { if (1 == this->owner.port->skip_tx) stp_trace ("port %s stop tx skipping", this->owner.port->port_name); this->owner.port->skip_tx--; return STP_Nothing_To_Do; } #endif if (this->owner.port->admin_non_stp) return 1; port_index = this->owner.port->port_index; vlan_id = this->owner.port->owner->vlan_id; pkt_len = build_bpdu_header (port_index, BPDU_TOPO_CHANGE_TYPE, sizeof (BPDU_HEADER_T)); #ifdef STP_DBG if (this->debug) stp_trace ("port %s txTcn", this->owner.port->port_name); #endif return STP_OUT_tx_bpdu (port_index, vlan_id, (unsigned char *) &bpdu_packet, pkt_len); } static void build_config_bpdu (PORT_T* port, Bool set_topo_ack_flag) { bpdu_packet.body.flags = 0; if (port->tcWhile) { #ifdef STP_DBG if (port->topoch->debug) stp_trace ("tcWhile=%d =>tx TOPOLOGY_CHANGE_BIT to port %s", (int) port->tcWhile, port->port_name); #endif bpdu_packet.body.flags |= TOPOLOGY_CHANGE_BIT; } if (set_topo_ack_flag && port->tcAck) { bpdu_packet.body.flags |= TOPOLOGY_CHANGE_ACK_BIT; } STP_VECT_set_vector (&port->portPrio, &bpdu_packet.body); STP_set_times (&port->portTimes, &bpdu_packet.body); } static int txConfig (STATE_MACH_T* this) {/* 17.19.15 (page 67) & 9.3.1 (page 23) */ register size_t pkt_len; register PORT_T* port = NULL; register int port_index, vlan_id; #ifdef STP_DBG if (this->owner.port->skip_tx > 0) { if (1 == this->owner.port->skip_tx) stp_trace ("port %s stop tx skipping", this->owner.port->port_name); this->owner.port->skip_tx--; return STP_Nothing_To_Do; } #endif port = this->owner.port; if (port->admin_non_stp) return 1; port_index = port->port_index; vlan_id = port->owner->vlan_id; pkt_len = build_bpdu_header (port->port_index, BPDU_CONFIG_TYPE, sizeof (BPDU_HEADER_T) + sizeof (BPDU_BODY_T)); build_config_bpdu (port, True); #ifdef STP_DBG if (this->debug) stp_trace ("port %s txConfig flags=0X%lx", port->port_name, (unsigned long) bpdu_packet.body.flags); #endif return STP_OUT_tx_bpdu (port_index, vlan_id, (unsigned char *) &bpdu_packet, pkt_len); } static int txRstp (STATE_MACH_T* this) {/* 17.19.16 (page 68) & 9.3.3 (page 25) */ register size_t pkt_len; register PORT_T* port = NULL; register int port_index, vlan_id; unsigned char role; #ifdef STP_DBG if (this->owner.port->skip_tx > 0) { if (1 == this->owner.port->skip_tx) stp_trace ("port %s stop tx skipping", this->owner.port->port_name); else stp_trace ("port %s skip tx %d", this->owner.port->port_name, this->owner.port->skip_tx); this->owner.port->skip_tx--; return STP_Nothing_To_Do; } #endif port = this->owner.port; if (port->admin_non_stp) return 1; port_index = port->port_index; vlan_id = port->owner->vlan_id; pkt_len = build_bpdu_header (port->port_index, BPDU_RSTP, sizeof (BPDU_HEADER_T) + sizeof (BPDU_BODY_T) + 2); build_config_bpdu (port, False); switch (port->selectedRole) { default: case DisabledPort: role = RSTP_PORT_ROLE_UNKN; break; case AlternatePort: role = RSTP_PORT_ROLE_ALTBACK; break; case BackupPort: role = RSTP_PORT_ROLE_ALTBACK; break; case RootPort: role = RSTP_PORT_ROLE_ROOT; break; case DesignatedPort: role = RSTP_PORT_ROLE_DESGN; break; } bpdu_packet.body.flags |= (role << PORT_ROLE_OFFS); if (port->synced) { #if 0 /* def STP_DBG */ if (port->roletrns->debug) stp_trace ("tx AGREEMENT_BIT to port %s", port->port_name); #endif bpdu_packet.body.flags |= AGREEMENT_BIT; } if (port->proposing) { #if 0 /* def STP_DBG */ if (port->roletrns->debug) stp_trace ("tx PROPOSAL_BIT to port %s", port->port_name); #endif bpdu_packet.body.flags |= PROPOSAL_BIT; } #ifdef STP_DBG if (this->debug) stp_trace ("port %s txRstp flags=0X%lx", port->port_name, (unsigned long) bpdu_packet.body.flags); #endif return STP_OUT_tx_bpdu (port_index, vlan_id, (unsigned char *) &bpdu_packet, pkt_len); } void STP_transmit_enter_state (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; switch (this->State) { case BEGIN: case TRANSMIT_INIT: port->newInfo = False; port->helloWhen = 0; port->txCount = 0; break; case TRANSMIT_PERIODIC: port->newInfo = port->newInfo || ((port->role == DesignatedPort) || ((port->role == RootPort) && port->tcWhile)); port->helloWhen = port->owner->rootTimes.HelloTime; break; case IDLE: break; case TRANSMIT_CONFIG: port->newInfo = False; (void) txConfig (this); port->txCount++; port->tcAck = False; break; case TRANSMIT_TCN: port->newInfo = False; (void) txTcn (this); port->txCount++; break; case TRANSMIT_RSTP: port->newInfo = False; (void) txRstp (this); port->txCount++; port->tcAck = False; break; }; } Bool STP_transmit_check_conditions (STATE_MACH_T* this) { register PORT_T* port = this->owner.port; switch (this->State) { case BEGIN: return STP_hop_2_state (this, TRANSMIT_INIT); case TRANSMIT_INIT: return STP_hop_2_state (this, IDLE); case TRANSMIT_PERIODIC: return STP_hop_2_state (this, IDLE); case IDLE: if (!port->helloWhen) return STP_hop_2_state (this, TRANSMIT_PERIODIC); if (!port->sendRSTP && port->newInfo && (port->txCount < TxHoldCount) && (port->role == DesignatedPort) && port->helloWhen) return STP_hop_2_state (this, TRANSMIT_CONFIG); if (!port->sendRSTP && port->newInfo && (port->txCount < TxHoldCount) && (port->role == RootPort) && port->helloWhen) return STP_hop_2_state (this, TRANSMIT_TCN); if (port->sendRSTP && port->newInfo && (port->txCount < TxHoldCount) && ((port->role == RootPort) || (port->role == DesignatedPort))) return STP_hop_2_state (this, TRANSMIT_RSTP); break; case TRANSMIT_CONFIG: return STP_hop_2_state (this, IDLE); case TRANSMIT_TCN: return STP_hop_2_state (this, IDLE); case TRANSMIT_RSTP: return STP_hop_2_state (this, IDLE); }; return False; } /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* Port Transmit state machine : 17.27 */ #ifndef _STP_TRANSMIT_H__ #define _STP_TRANSMIT_H__ void STP_transmit_enter_state (STATE_MACH_T* s); Bool STP_transmit_check_conditions (STATE_MACH_T* s); char* STP_transmit_get_state_name (int state); #endif /* _STP_TRANSMIT_H__ */ /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* External management communication API definitions */ #ifndef _UID_STP_H__ #define _UID_STP_H__ #define STP_DBG 1 #define NAME_LEN 20 typedef enum { STP_DISABLED, STP_ENABLED } UID_STP_MODE_T; typedef struct { unsigned short prio; unsigned char addr[6]; } UID_BRIDGE_ID_T; typedef struct { char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ char action; /* 1-create, 0- delete */ } UID_STP_BR_CTRL_T; #define BR_CFG_STATE (1L << 0) #define BR_CFG_PRIO (1L << 1) #define BR_CFG_AGE (1L << 2) #define BR_CFG_HELLO (1L << 3) #define BR_CFG_DELAY (1L << 4) #define BR_CFG_FORCE_VER (1L << 5) #define BR_CFG_AGE_MODE (1L << 6) #define BR_CFG_AGE_TIME (1L << 7) #define BR_CFG_HOLD_TIME (1L << 8) #define BR_CFG_ALL BR_CFG_STATE | \ BR_CFG_PRIO | \ BR_CFG_AGE | \ BR_CFG_HELLO | \ BR_CFG_DELAY | \ BR_CFG_FORCE_VER | \ BR_CFG_AGE_MODE | \ BR_CFG_AGE_TIME | \ BR_CFG_HOLD_TIME typedef struct { /* service data */ unsigned long field_mask; /* which fields to change */ UID_STP_MODE_T stp_enabled; char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ /* protocol data */ int bridge_priority; int max_age; int hello_time; int forward_delay; int force_version; int hold_time; } UID_STP_CFG_T; typedef struct { /* service data */ char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ unsigned long vlan_id; UID_STP_MODE_T stp_enabled; /* protocol data */ UID_BRIDGE_ID_T designated_root; unsigned long root_path_cost; unsigned long timeSince_Topo_Change; /* 14.8.1.1.3.b: TBD */ unsigned long Topo_Change_Count; /* 14.8.1.1.3.c: TBD */ unsigned char Topo_Change; /* 14.8.1.1.3.d: TBD */ unsigned short root_port; int max_age; int hello_time; int forward_delay; UID_BRIDGE_ID_T bridge_id; } UID_STP_STATE_T; typedef enum { UID_PORT_DISABLED = 0, UID_PORT_DISCARDING, UID_PORT_LEARNING, UID_PORT_FORWARDING, UID_PORT_NON_STP, UID_PORT_BADSDU } RSTP_PORT_STATE; typedef unsigned short UID_PORT_ID; typedef enum { P2P_FORCE_TRUE, P2P_FORCE_FALSE, P2P_AUTO } ADMIN_P2P_T; #define PT_CFG_STATE (1L << 0) #define PT_CFG_COST (1L << 1) #define PT_CFG_PRIO (1L << 2) #define PT_CFG_P2P (1L << 3) #define PT_CFG_EDGE (1L << 4) #define PT_CFG_MCHECK (1L << 5) #define PT_CFG_NON_STP (1L << 6) #ifdef STP_DBG #define PT_CFG_DBG_SKIP_RX (1L << 16) #define PT_CFG_DBG_SKIP_TX (1L << 17) #endif #define PT_CFG_ALL PT_CFG_STATE | \ PT_CFG_COST | \ PT_CFG_PRIO | \ PT_CFG_P2P | \ PT_CFG_EDGE | \ PT_CFG_MCHECK | \ PT_CFG_NON_STP #define ADMIN_PORT_PATH_COST_AUTO 0 typedef struct { /* service data */ unsigned long field_mask; /* which fields to change */ char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ /* protocol data */ int port_priority; unsigned long admin_port_path_cost; /* ADMIN_PORT_PATH_COST_AUTO - auto sence */ ADMIN_P2P_T admin_point2point; unsigned char admin_edge; unsigned char admin_non_stp; /* 1- doesn't participate in STP, 1 - regular */ #ifdef STP_DBG unsigned int skip_rx; unsigned int skip_tx; #endif } UID_STP_PORT_CFG_T; typedef struct { /* service data */ char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ unsigned int port_no; /* key of the entry */ /* protocol data */ UID_PORT_ID port_id; RSTP_PORT_STATE state; unsigned long path_cost; UID_BRIDGE_ID_T designated_root; unsigned long designated_cost; UID_BRIDGE_ID_T designated_bridge; UID_PORT_ID designated_port; #if 0 int infoIs; unsigned short handshake_flags; #endif unsigned long rx_cfg_bpdu_cnt; unsigned long rx_rstp_bpdu_cnt; unsigned long rx_tcn_bpdu_cnt; int fdWhile; /* 17.15.1 */ int helloWhen; /* 17.15.2 */ int mdelayWhile; /* 17.15.3 */ int rbWhile; /* 17.15.4 */ int rcvdInfoWhile;/* 17.15.5 */ int rrWhile; /* 17.15.6 */ int tcWhile; /* 17.15.7 */ int txCount; /* 17.18.40 */ int lnkWhile; unsigned long uptime; /* 14.8.2.1.3.a */ unsigned long oper_port_path_cost; unsigned char role; unsigned char oper_point2point; unsigned char oper_edge; unsigned char oper_stp_neigb; unsigned char top_change_ack; unsigned char tc; } UID_STP_PORT_STATE_T; #endif /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* STP priority vectors API : 17.4.2 */ #include "base.h" #include "stp_bpdu.h" #include "vector.h" #include "stp_vectors.h" int STP_VECT_compare_bridge_id (BRIDGE_ID* b1, BRIDGE_ID* b2) { if (b1->prio < b2->prio) return -1; if (b1->prio > b2->prio) return 1; return memcmp (b1->addr, b2->addr, 6); } void STP_VECT_copy (OUT PRIO_VECTOR_T* t, IN PRIO_VECTOR_T* f) { (void) memcpy (t, f, sizeof (PRIO_VECTOR_T)); } void STP_VECT_create (OUT PRIO_VECTOR_T* t, IN BRIDGE_ID* root_br, IN unsigned long root_path_cost, IN BRIDGE_ID* design_bridge, IN PORT_ID design_port, IN PORT_ID bridge_port) { (void) memcpy (&t->root_bridge, root_br, sizeof (BRIDGE_ID)); t->root_path_cost = root_path_cost; (void) memcpy (&t->design_bridge, design_bridge, sizeof (BRIDGE_ID)); t->design_port = design_port; t->bridge_port = bridge_port; } int STP_VECT_compare_vector (PRIO_VECTOR_T* v1, PRIO_VECTOR_T* v2) { int bridcmp; bridcmp = STP_VECT_compare_bridge_id (&v1->root_bridge, &v2->root_bridge); if (bridcmp < 0) return bridcmp; if (! bridcmp) { bridcmp = v1->root_path_cost - v2->root_path_cost; if (bridcmp < 0) return bridcmp; if (! bridcmp) { bridcmp = STP_VECT_compare_bridge_id (&v1->design_bridge, &v2->design_bridge); if (bridcmp < 0) return bridcmp; if (! bridcmp) { bridcmp = v1->design_port - v2->design_port; if (bridcmp < 0) return bridcmp; if (! bridcmp) return v1->bridge_port - v2->bridge_port; } } } return bridcmp; } static unsigned short stp_vect_get_short (IN unsigned char* f) { /* LINTED: alignment */ return ntohs (*(unsigned short *)f); } static void stp_vect_set_short (IN unsigned short f, OUT unsigned char* t) { /* LINTED: alignment */ *(unsigned short *)t = htons (f); } static void stp_vect_get_bridge_id (IN unsigned char* c_br, OUT BRIDGE_ID* bridge_id) { bridge_id->prio = stp_vect_get_short (c_br); (void) memcpy (bridge_id->addr, c_br + 2, 6); } static void stp_vect_set_bridge_id (IN BRIDGE_ID* bridge_id, OUT unsigned char* c_br) { stp_vect_set_short (bridge_id->prio, c_br); (void) memcpy (c_br + 2, bridge_id->addr, 6); } void STP_VECT_get_vector (IN BPDU_BODY_T* b, OUT PRIO_VECTOR_T* v) { stp_vect_get_bridge_id (b->root_id, &v->root_bridge); /* LINTED: alignment */ v->root_path_cost = ntohl (*((long*) b->root_path_cost)); stp_vect_get_bridge_id (b->bridge_id, &v->design_bridge); v->design_port = stp_vect_get_short (b->port_id); } void STP_VECT_set_vector (IN PRIO_VECTOR_T* v, OUT BPDU_BODY_T* b) { unsigned long root_path_cost; stp_vect_set_bridge_id (&v->root_bridge, b->root_id); root_path_cost = htonl (v->root_path_cost); (void) memcpy (b->root_path_cost, &root_path_cost, 4); stp_vect_set_bridge_id (&v->design_bridge, b->bridge_id); stp_vect_set_short (v->design_port, b->port_id); } #ifdef STP_DBG /*ARGSUSED*/ void STP_VECT_br_id_print (IN char *title, IN BRIDGE_ID* br_id, IN Bool cr) { stp_trace ("%s=%04lX-%02x%02x%02x%02x%02x%02x", title, (unsigned long) br_id->prio, (unsigned char) br_id->addr[0], (unsigned char) br_id->addr[1], (unsigned char) br_id->addr[2], (unsigned char) br_id->addr[3], (unsigned char) br_id->addr[4], (unsigned char) br_id->addr[5]); #ifndef __SUN__ stp_trace (cr ? "\n" : " "); #endif } void STP_VECT_print (IN char *title, IN PRIO_VECTOR_T *v) { stp_trace ("%s:", title); STP_VECT_br_id_print ("rootBr", &v->root_bridge, False); /**** stp_trace (" rpc=%ld ", (long) v->root_path_cost); ****/ STP_VECT_br_id_print ("designBr", &v->design_bridge, False); /****/ stp_trace (" dp=%lx bp=%lx ", (unsigned long) v->design_port, (unsigned long) v->bridge_port); /***********/ stp_trace ("\n"); } #endif /************************************************************************ * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) * Copyright (C) 2001-2003 Optical Access * Author: Alex Rozin * * This file is part of RSTP library. * * RSTP library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; version 2.1 * * RSTP library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with RSTP library; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. **********************************************************************/ /* STP priority vectors API : 17.4.2 */ #ifndef _PRIO_VECTOR_H__ #define _PRIO_VECTOR_H__ #define STP_DBG 1 typedef struct bridge_id { unsigned short prio; unsigned char addr[6]; } BRIDGE_ID; typedef unsigned short PORT_ID; typedef struct prio_vector_t { BRIDGE_ID root_bridge; unsigned long root_path_cost; BRIDGE_ID design_bridge; PORT_ID design_port; PORT_ID bridge_port; } PRIO_VECTOR_T; void STP_VECT_create (OUT PRIO_VECTOR_T* t, IN BRIDGE_ID* root_br, IN unsigned long root_path_cost, IN BRIDGE_ID* design_bridge, IN PORT_ID design_port, IN PORT_ID bridge_port); void STP_VECT_copy (OUT PRIO_VECTOR_T* t, IN PRIO_VECTOR_T* f); int STP_VECT_compare_bridge_id (IN BRIDGE_ID* b1, IN BRIDGE_ID* b2); int STP_VECT_compare_vector (IN PRIO_VECTOR_T* v1, IN PRIO_VECTOR_T* v2); void STP_VECT_get_vector (IN BPDU_BODY_T* b, OUT PRIO_VECTOR_T* v); void STP_VECT_set_vector (IN PRIO_VECTOR_T* v, OUT BPDU_BODY_T* b); #ifdef STP_DBG void STP_VECT_print (IN char* title, IN PRIO_VECTOR_T* v); void STP_VECT_br_id_print (IN char *title, IN BRIDGE_ID* br_id, IN Bool cr); #endif #endif /* _PRIO_VECTOR_H__ */