--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+version := 0.01
+
+DSOURCES = main.c epoll_loop.c brmon.c bridge_track.c libnetlink.c mstp.c \
+ packet.c netif_utils.c ctl_socket_server.c hmac_md5.c
+
+DOBJECTS = $(DSOURCES:.c=.o)
+
+CTLSOURCES = ctl_main.c ctl_socket_client.c
+
+CTLOBJECTS = $(CTLSOURCES:.c=.o)
+
+CFLAGS += -Werror -O2 -D_REENTRANT -D__LINUX__ -DVERSION=$(version) -I.
+
+all: mstpd mstpctl
+
+mstpd: $(DOBJECTS)
+ $(CC) -o $@ $(DOBJECTS)
+
+mstpctl: $(CTLOBJECTS)
+ $(CC) -o $@ $(CTLOBJECTS)
+
+-include .depend
+
+clean:
+ rm -f *.o *~ .depend.bak mstpd mstpctl
+
+romfs: all
+ $(ROMFSINST) /sbin/mstpd
+ $(ROMFSINST) /sbin/mstpctl
+ $(ROMFSINST) /sbin/bridge-stp
+
+#depend:
+# makedepend -I. -Y *.c -f .depend
--- /dev/null
+#!/bin/bash
+#
+# Script to start/stop spanning tree called from kernel
+# Make sure umask is sane
+umask 022
+
+# Set up a default search path.
+PATH="/sbin:/usr/sbin:/bin:/usr/bin"
+export PATH
+
+if [ $# -ne 2 ]; then
+ echo "Usage: bridge-stp <bridge> {start|stop}"
+ exit 1
+fi
+bridge=$1
+service=mstpd
+pid_file=/var/run/${service}.pid
+
+# Set this to the list of bridges for which MSTP should be used
+MSTP_BRIDGES="br0"
+
+# Set $pid to pids from /var/run* for {program}. $pid should be declared
+# local in the caller.
+# Returns LSB exit code for the 'status' action.
+checkpid()
+{
+ pid=
+ if [ -f "$1" ] ; then
+ local line p
+ read line < "$pid_file"
+ for p in $line ; do
+ [ -z "${p//[0-9]/}" -a -d "/proc/$p" ] && pid="$pid $p"
+ done
+ if [ -n "$pid" ]; then
+ return 0
+ fi
+ return 1 # "Program is dead and /var/run pid file exists"
+ fi
+ return 3 # "Program is not running"
+}
+
+case $2 in
+ start)
+ checkpid $pid_file || exit 1
+ for b in $MSTP_BRIDGES; do
+ if [ "$bridge" == "$b" ]; then
+ exit 0;
+ fi
+ done
+ exit 1 ;;
+ stop)
+ exit 0 ;;
+ *)
+ echo "Unknown action:" $2
+ echo "Usage: bridge-stp <bridge> {start|stop}"
+ exit 1
+esac
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#ifndef BRIDGE_CTL_H
+#define BRIDGE_CTL_H
+
+#include <stdbool.h>
+#include <net/if.h>
+#include <linux/if_ether.h>
+
+typedef struct
+{
+ int if_index;
+ __u8 macaddr[ETH_ALEN];
+ char name[IFNAMSIZ];
+
+ bool up, stp_up;
+} sysdep_br_data_t;
+
+typedef struct
+{
+ int if_index;
+ __u8 macaddr[ETH_ALEN];
+ char name[IFNAMSIZ];
+
+ bool up;
+ int speed, duplex;
+} sysdep_if_data_t;
+
+#define GET_PORT_SPEED(port) ((port)->sysdeps.speed)
+#define GET_PORT_DUPLEX(port) ((port)->sysdeps.duplex)
+
+/* Logging macros for mstp.c - they use system dependent info */
+#define ERROR_BRNAME(_br, _fmt, _args...) ERROR("%s " _fmt, \
+ _br->sysdeps.name, ##_args)
+#define INFO_BRNAME(_br, _fmt, _args...) INFO("%s " _fmt, \
+ _br->sysdeps.name, ##_args)
+#define LOG_BRNAME(_br, _fmt, _args...) LOG("%s " _fmt, \
+ _br->sysdeps.name, ##_args)
+#define ERROR_PRTNAME(_br, _prt, _fmt, _args...) ERROR("%s:%s " _fmt, \
+ _br->sysdeps.name, _prt->sysdeps.name, ##_args)
+#define INFO_PRTNAME(_br, _prt, _fmt, _args...) INFO("%s:%s " _fmt, \
+ _br->sysdeps.name, _prt->sysdeps.name, ##_args)
+#define LOG_PRTNAME(_br, _prt, _fmt, _args...) LOG("%s:%s " _fmt, \
+ _br->sysdeps.name, _prt->sysdeps.name, ##_args)
+#define ERROR_MSTINAME(_br,_prt,_ptp,_fmt,_args...) ERROR("%s:%s:%hu " _fmt, \
+ _br->sysdeps.name, _prt->sysdeps.name, __be16_to_cpu(ptp->MSTID), ##_args)
+#define INFO_MSTINAME(_br,_prt,_ptp,_fmt,_args...) INFO("%s:%s:%hu " _fmt, \
+ _br->sysdeps.name, _prt->sysdeps.name, __be16_to_cpu(ptp->MSTID), ##_args)
+#define LOG_MSTINAME(_br,_prt,_ptp,_fmt,_args...) LOG("%s:%s:%hu " _fmt, \
+ _br->sysdeps.name, _prt->sysdeps.name, __be16_to_cpu(ptp->MSTID), ##_args)
+#define SMLOG_MSTINAME(_ptp, _fmt, _args...) \
+ PRINT(LOG_LEVEL_STATE_MACHINE_TRANSITION, "%s: %s:%s:%hu " _fmt, \
+ __PRETTY_FUNCTION__, _ptp->port->bridge->sysdeps.name, \
+ _ptp->port->sysdeps.name, __be16_to_cpu(ptp->MSTID), ##_args)
+
+extern struct rtnl_handle rth_state;
+
+int init_bridge_ops(void);
+
+int bridge_set_state(int ifindex, int state);
+
+int bridge_notify(int br_index, int if_index, bool newlink, bool up);
+
+void bridge_bpdu_rcv(int ifindex, const unsigned char *data, int len);
+
+void bridge_one_second(void);
+
+#endif /* BRIDGE_CTL_H */
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#include <string.h>
+#include <linux/if_bridge.h>
+#include <asm/byteorder.h>
+
+#include "bridge_ctl.h"
+#include "netif_utils.h"
+#include "packet.h"
+#include "log.h"
+#include "mstp.h"
+
+static LIST_HEAD(bridges);
+
+static bridge_t * create_br(int if_index)
+{
+ bridge_t *br;
+ TST((br = calloc(1, sizeof(*br))) != NULL, NULL);
+
+ /* Init system dependent info */
+ br->sysdeps.if_index = if_index;
+ if_indextoname(if_index, br->sysdeps.name);
+ get_hwaddr(br->sysdeps.name, br->sysdeps.macaddr);
+
+ INFO("Add bridge %s", br->sysdeps.name);
+ if(!MSTP_IN_bridge_create(br, br->sysdeps.macaddr))
+ {
+ free(br);
+ return NULL;
+ }
+
+ list_add_tail(&br->list, &bridges);
+ return br;
+}
+
+static bridge_t * find_br(int if_index)
+{
+ bridge_t *br;
+ list_for_each_entry(br, &bridges, list)
+ {
+ if(br->sysdeps.if_index == if_index)
+ return br;
+ }
+ return NULL;
+}
+
+static port_t * create_if(bridge_t * br, int if_index)
+{
+ port_t *ifc;
+ TST((ifc = calloc(1, sizeof(*ifc))) != NULL, NULL);
+
+ /* Init system dependent info */
+ ifc->sysdeps.if_index = if_index;
+ if_indextoname(if_index, ifc->sysdeps.name);
+ get_hwaddr(ifc->sysdeps.name, ifc->sysdeps.macaddr);
+
+ int portno;
+ if(0 > (portno = get_bridge_portno(ifc->sysdeps.name)))
+ {
+ ERROR("Couldn't get port number for %s", ifc->sysdeps.name);
+ free(ifc);
+ return NULL;
+ }
+ if((0 == portno) || (portno > MAX_PORT_NUMBER))
+ {
+ ERROR("Port number for %s is invalid (%d)", ifc->sysdeps.name, portno);
+ free(ifc);
+ return NULL;
+ }
+
+ INFO("Add iface %s as port#%d to bridge %s", ifc->sysdeps.name,
+ portno, br->sysdeps.name);
+ ifc->bridge = br;
+ if(!MSTP_IN_port_create_and_add_tail(ifc, portno))
+ {
+ free(ifc);
+ return NULL;
+ }
+
+ return ifc;
+}
+
+static port_t * find_if(bridge_t * br, int if_index)
+{
+ port_t *ifc;
+ list_for_each_entry(ifc, &br->ports, br_list)
+ {
+ if(ifc->sysdeps.if_index == if_index)
+ return ifc;
+ }
+ return NULL;
+}
+
+static inline void delete_if(port_t * ifc)
+{
+ list_del(&ifc->br_list);
+ MSTP_IN_delete_port(ifc);
+ free(ifc);
+}
+
+static inline bool delete_if_byindex(bridge_t * br, int if_index)
+{
+ port_t *ifc;
+ if(!(ifc = find_if(br, if_index)))
+ return false;
+ delete_if(ifc);
+ return true;
+}
+
+static bool delete_br_byindex(int if_index)
+{
+ bridge_t *br;
+ if(!(br = find_br(if_index)))
+ return false;
+ list_del(&br->list);
+ MSTP_IN_delete_bridge(br);
+ free(br);
+ return true;
+}
+
+void bridge_one_second(void)
+{
+ bridge_t *br;
+ list_for_each_entry(br, &bridges, list)
+ MSTP_IN_one_second(br);
+}
+
+/* New MAC address is stored in addr, which also holds the old value on entry.
+ Return true if the address changed */
+static bool check_mac_address(char *name, __u8 *addr)
+{
+ __u8 temp_addr[ETH_ALEN];
+ if(get_hwaddr(name, temp_addr))
+ {
+ LOG("Error getting hw address: %s", name);
+ /* Error. Ignore the new value */
+ return false;
+ }
+ if(memcmp(addr, temp_addr, sizeof(temp_addr)) == 0)
+ return false;
+ else
+ {
+ memcpy(addr, temp_addr, sizeof(temp_addr));
+ return true;
+ }
+}
+
+static int stp_enabled(bridge_t * br)
+{
+ char path[40 + IFNAMSIZ];
+ sprintf(path, "/sys/class/net/%s/bridge/stp_state", br->sysdeps.name);
+ FILE *f = fopen(path, "r");
+ int enabled = 0;
+ if(!f || (1 != fscanf(f, "%d", &enabled)))
+ ERROR("Can't read from %s", path);
+ fclose(f);
+ INFO("STP on %s state %d", br->sysdeps.name, enabled);
+
+ return enabled == 2; /* ie user mode STP */
+}
+
+static void set_br_up(bridge_t * br, bool up)
+{
+ int stp_up = stp_enabled(br);
+ INFO("%s was %s stp was %s", br->sysdeps.name,
+ br->sysdeps.up ? "up" : "down", br->sysdeps.stp_up ? "up" : "down");
+ INFO("Set bridge %s %s stp %s" , br->sysdeps.name,
+ up ? "up" : "down", stp_up ? "up" : "down");
+
+ bool changed = false;
+
+ if(up != br->sysdeps.up)
+ {
+ br->sysdeps.up = up;
+ changed = true;
+ }
+
+ if(br->sysdeps.stp_up != stp_up)
+ {
+ br->sysdeps.stp_up = stp_up;
+ changed = true;
+ }
+
+ if(check_mac_address(br->sysdeps.name, br->sysdeps.macaddr))
+ {
+ /* MAC address changed */
+ /* Notify bridge address change */
+ MSTP_IN_set_bridge_address(br, br->sysdeps.macaddr);
+ }
+
+ if(changed)
+ MSTP_IN_set_bridge_enable(br, br->sysdeps.up && br->sysdeps.stp_up);
+}
+
+static void set_if_up(port_t * ifc, bool up)
+{
+ INFO("Port %s : %s", ifc->sysdeps.name, (up ? "up" : "down"));
+ int speed = -1;
+ int duplex = -1;
+ bool changed = false;
+
+ if(check_mac_address(ifc->sysdeps.name, ifc->sysdeps.macaddr))
+ {
+ /* MAC address changed */
+ if(check_mac_address(ifc->bridge->sysdeps.name,
+ ifc->bridge->sysdeps.macaddr))
+ {
+ /* Notify bridge address change */
+ MSTP_IN_set_bridge_address(ifc->bridge,
+ ifc->bridge->sysdeps.macaddr);
+ }
+ }
+
+ if(!up)
+ { /* Down */
+ if(ifc->sysdeps.up)
+ {
+ ifc->sysdeps.up = false;
+ changed = true;
+ }
+ }
+ else
+ { /* Up */
+ int r = ethtool_get_speed_duplex(ifc->sysdeps.name, &speed, &duplex);
+ if((r < 0) || (speed < 0))
+ speed = 10;
+ if((r < 0) || (duplex < 0))
+ duplex = 0; /* Assume half duplex */
+
+ if(speed != ifc->sysdeps.speed)
+ {
+ ifc->sysdeps.speed = speed;
+ changed = true;
+ }
+ if(duplex != ifc->sysdeps.duplex)
+ {
+ ifc->sysdeps.duplex = duplex;
+ changed = true;
+ }
+ if(!ifc->sysdeps.up)
+ {
+ ifc->sysdeps.up = true;
+ changed = true;
+ }
+ }
+ if(changed)
+ MSTP_IN_set_port_enable(ifc, ifc->sysdeps.up, ifc->sysdeps.speed,
+ ifc->sysdeps.duplex);
+}
+
+/* br_index == if_index means: interface is bridge master */
+int bridge_notify(int br_index, int if_index, bool newlink, bool up)
+{
+ port_t *ifc;
+ bridge_t *br = NULL, *other_br;
+
+ LOG("br_index %d, if_index %d, newlink %d, up %d",
+ br_index, if_index, newlink, up);
+
+ if((br_index >= 0) && (br_index != if_index))
+ {
+ if(!(br = find_br(br_index)))
+ br = create_br(br_index);
+ if(!br)
+ {
+ ERROR("Couldn't create data for bridge interface %d", br_index);
+ return -1;
+ }
+ int br_up = ethtool_get_link(br->sysdeps.name);
+ if(br_up >= 0)
+ set_br_up(br, !!br_up);
+ }
+
+ if(br)
+ {
+ if(!(ifc = find_if(br, if_index)))
+ {
+ if(!newlink)
+ {
+ INFO("Got DELLINK for unknown port %d on "
+ "bridge %d", if_index, br_index);
+ return -1;
+ }
+ /* Check if this interface is slave of another bridge */
+ list_for_each_entry(other_br, &bridges, list)
+ {
+ if(other_br != br)
+ if(delete_if_byindex(other_br, if_index))
+ {
+ INFO("Device %d has come to bridge %d. "
+ "Missed notify for deletion from bridge %d",
+ if_index, br_index, other_br->sysdeps.if_index);
+ break;
+ }
+ }
+ ifc = create_if(br, if_index);
+ }
+ if(!ifc)
+ {
+ ERROR("Couldn't create data for interface %d (master %d)",
+ if_index, br_index);
+ return -1;
+ }
+ if(!newlink)
+ {
+ delete_if(ifc);
+ return 0;
+ }
+ set_if_up(ifc, up); /* And speed and duplex */
+ }
+ else
+ { /* Interface is not a bridge slave */
+ if(!newlink)
+ {
+ /* DELLINK not from bridge means interface unregistered. */
+ /* Cleanup removed bridge or removed bridge slave */
+ if(!delete_br_byindex(if_index))
+ list_for_each_entry(br, &bridges, list)
+ {
+ if(delete_if_byindex(br, if_index))
+ break;
+ }
+ return 0;
+ }
+ else
+ { /* This may be a new link */
+ if(br_index == if_index)
+ {
+ if(!(br = find_br(br_index)))
+ {
+ if(!(br = create_br(br_index)))
+ {
+ ERROR("Couldn't create data for bridge interface %d",
+ br_index);
+ return -1;
+ }
+ }
+ set_br_up(br, up);
+ }
+ }
+ }
+ return 0;
+}
+
+struct llc_header
+{
+ __u8 dest_addr[ETH_ALEN];
+ __u8 src_addr[ETH_ALEN];
+ __be16 len8023;
+ __u8 d_sap;
+ __u8 s_sap;
+ __u8 llc_ctrl;
+} __attribute__((packed));
+
+/* LLC_PDU_xxx defines snitched from linux/net/llc_pdu.h */
+#define LLC_PDU_LEN_U 3 /* header and 1 control byte */
+#define LLC_PDU_TYPE_U 3 /* first two bits */
+
+/* 7.12.3 of 802.1D */
+#define LLC_SAP_BSPAN 0x42
+static const __u8 bridge_group_address[ETH_ALEN] =
+{
+ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00
+};
+
+void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len)
+{
+ port_t *ifc = NULL;
+ bridge_t *br;
+
+ LOG("ifindex %d, len %d", if_index, len);
+
+ list_for_each_entry(br, &bridges, list)
+ {
+ if((ifc = find_if(br, if_index)))
+ break;
+ }
+ if(!ifc)
+ return;
+
+ /* sanity checks */
+ TST(br == ifc->bridge,);
+ TST(ifc->sysdeps.up,);
+ if(!br->sysdeps.stp_up)
+ return;
+
+ /* Validate Ethernet and LLC header,
+ * maybe we can skip this check thanks to Berkeley filter in packet socket?
+ */
+ struct llc_header *h;
+ unsigned int l;
+ TST(len > sizeof(struct llc_header),);
+ h = (struct llc_header *)data;
+ TST(0 == memcmp(h->dest_addr, bridge_group_address, ETH_ALEN),
+ INFO("ifindex %d, len %d, %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
+ if_index, len,
+ h->dest_addr[0], h->dest_addr[1], h->dest_addr[2],
+ h->dest_addr[3], h->dest_addr[4], h->dest_addr[5])
+ );
+ l = __be16_to_cpu(h->len8023);
+ TST(l <= ETH_DATA_LEN && l <= len - ETH_HLEN && l >= LLC_PDU_LEN_U, );
+ TST(h->d_sap == LLC_SAP_BSPAN && h->s_sap == LLC_SAP_BSPAN && (h->llc_ctrl & 0x3) == LLC_PDU_TYPE_U,);
+
+ MSTP_IN_rx_bpdu(ifc,
+ /* Don't include LLC header */
+ (bpdu_t *)(data + sizeof(*h)), l - LLC_PDU_LEN_U);
+}
+
+/* External actions for MSTP protocol */
+
+void MSTP_OUT_set_state(per_tree_port_t *ptp, int new_state)
+{
+ char * state_name;
+ port_t *ifc = ptp->port;
+ bridge_t *br = ifc->bridge;
+
+ switch(new_state)
+ {
+ case BR_STATE_LISTENING:
+ state_name = "listening";
+ break;
+ case BR_STATE_LEARNING:
+ state_name = "learning";
+ break;
+ case BR_STATE_FORWARDING:
+ state_name = "forwarding";
+ break;
+ case BR_STATE_BLOCKING:
+ state_name = "blocking";
+ break;
+ default:
+ ERROR_MSTINAME(br, ifc, ptp, "attempt to set invalid state %d",
+ new_state);
+ new_state = BR_STATE_DISABLED;
+ case BR_STATE_DISABLED:
+ state_name = "disabled";
+ break;
+ }
+
+ if(ptp->state == new_state)
+ return;
+
+ /* TODO: command driver to put the br:port:tree into new state */
+
+ ptp->state = new_state;
+ INFO_MSTINAME(br, ifc, ptp, "entering %s state", state_name);
+}
+
+/* This function initiates process of flushing
+ * all entries for the given port in all FIDs for the
+ * given tree.
+ * When this process finishes, implementation should signal
+ * this by calling MSTP_IN_all_fids_flushed(per_tree_port_t *ptp)
+ */
+void MSTP_OUT_flush_all_fids(per_tree_port_t * ptp)
+{
+ /* TODO: do real flushing.
+ * Make it asynchronous, with completion function calling
+ * MSTP_IN_all_fids_flushed(ptp)
+ */
+ MSTP_IN_all_fids_flushed(ptp);
+}
+
+/* ageingTime < 0 => command driver to use its internal setting */
+void MSTP_OUT_set_ageing_time(bridge_t * br, int ageingTime)
+{
+ /* TODO: do set new ageing time */
+}
+
+void MSTP_OUT_tx_bpdu(port_t * ifc, bpdu_t * bpdu, int size)
+{
+ char *bpdu_type;
+ bridge_t *br = ifc->bridge;
+
+ switch(bpdu->protocolVersion)
+ {
+ case protoSTP:
+ switch(bpdu->bpduType)
+ {
+ case bpduTypeConfig:
+ bpdu_type = "STP-Config";
+ break;
+ case bpduTypeTCN:
+ bpdu_type = "STP-TCN";
+ break;
+ default:
+ bpdu_type = "STP-UnknownType";
+ }
+ break;
+ case protoRSTP:
+ bpdu_type = "RST";
+ break;
+ case protoMSTP:
+ bpdu_type = "MST";
+ break;
+ default:
+ bpdu_type = "UnknownProto";
+ }
+
+ LOG_PRTNAME(br, ifc, "sending %s BPDU", bpdu_type);
+
+ struct llc_header h;
+ memcpy(h.dest_addr, bridge_group_address, ETH_ALEN);
+ memcpy(h.src_addr, ifc->sysdeps.macaddr, ETH_ALEN);
+ h.len8023 = __cpu_to_be16(size + LLC_PDU_LEN_U);
+ h.d_sap = h.s_sap = LLC_SAP_BSPAN;
+ h.llc_ctrl = LLC_PDU_TYPE_U;
+
+ struct iovec iov[2] =
+ {
+ { .iov_base = &h, .iov_len = sizeof(h) },
+ { .iov_base = bpdu, .iov_len = size }
+ };
+
+ packet_send(ifc->sysdeps.if_index, iov, 2, sizeof(h) + size);
+}
+
+/* User interface commands */
+
+#define CTL_CHECK_BRIDGE \
+ bridge_t *br = find_br(br_index); \
+ if(NULL == br) \
+ { \
+ ERROR("Couldn't find bridge with index %d", br_index); \
+ return -1; \
+ }
+
+#define CTL_CHECK_BRIDGE_PORT \
+ CTL_CHECK_BRIDGE; \
+ port_t *prt = find_if(br, port_index); \
+ if(NULL == prt) \
+ { \
+ ERROR_BRNAME(br, "Couldn't find port with index %d", port_index); \
+ return -1; \
+ }
+
+#define CTL_CHECK_BRIDGE_TREE \
+ CTL_CHECK_BRIDGE; \
+ tree_t *tree; \
+ bool found = false; \
+ __be16 MSTID = __cpu_to_be16(mstid); \
+ list_for_each_entry(tree, &br->trees, bridge_list) \
+ if(tree->MSTID == MSTID) \
+ { \
+ found = true; \
+ break; \
+ } \
+ if(!found) \
+ { \
+ ERROR_BRNAME(br, "Couldn't find MSTI with ID %hu", mstid); \
+ return -1; \
+ }
+
+#define CTL_CHECK_BRIDGE_PERTREEPORT \
+ CTL_CHECK_BRIDGE_PORT; \
+ per_tree_port_t *ptp; \
+ bool found = false; \
+ __be16 MSTID = __cpu_to_be16(mstid); \
+ list_for_each_entry(ptp, &prt->trees, port_list) \
+ if(ptp->MSTID == MSTID) \
+ { \
+ found = true; \
+ break; \
+ } \
+ if(!found) \
+ { \
+ ERROR_PRTNAME(br, prt, "Couldn't find MSTI with ID %hu", mstid); \
+ return -1; \
+ }
+
+int CTL_get_cist_bridge_status(int br_index, CIST_BridgeStatus *status,
+ char *root_port_name)
+{
+ tree_t *cist;
+ per_tree_port_t *ptp;
+
+ CTL_CHECK_BRIDGE;
+ MSTP_IN_get_cist_bridge_status(br, status);
+
+ /* find root port name by root_port_id */
+ cist = GET_CIST_TREE(br);
+ *root_port_name = '\0';
+ list_for_each_entry(ptp, &cist->ports, tree_list)
+ if(ptp->portId == status->root_port_id)
+ {
+ strncpy(root_port_name, ptp->port->sysdeps.name, IFNAMSIZ);
+ break;
+ }
+ return 0;
+}
+
+int CTL_get_msti_bridge_status(int br_index, __u16 mstid,
+ MSTI_BridgeStatus *status, char *root_port_name)
+{
+ per_tree_port_t *ptp;
+
+ CTL_CHECK_BRIDGE_TREE;
+ MSTP_IN_get_msti_bridge_status(tree, status);
+
+ /* find root port name by root_port_id */
+ *root_port_name = '\0';
+ list_for_each_entry(ptp, &tree->ports, tree_list)
+ if(ptp->portId == status->root_port_id)
+ {
+ strncpy(root_port_name, ptp->port->sysdeps.name, IFNAMSIZ);
+ break;
+ }
+ return 0;
+}
+
+int CTL_set_cist_bridge_config(int br_index, CIST_BridgeConfig *cfg)
+{
+ CTL_CHECK_BRIDGE;
+ return MSTP_IN_set_cist_bridge_config(br, cfg);
+}
+
+int CTL_set_msti_bridge_config(int br_index, __u16 mstid, __u8 bridge_priority)
+{
+ CTL_CHECK_BRIDGE_TREE;
+ return MSTP_IN_set_msti_bridge_config(tree, bridge_priority);
+}
+
+int CTL_get_cist_port_status(int br_index, int port_index,
+ CIST_PortStatus *status)
+{
+ CTL_CHECK_BRIDGE_PORT;
+ MSTP_IN_get_cist_port_status(prt, status);
+ return 0;
+}
+
+int CTL_get_msti_port_status(int br_index, int port_index, __u16 mstid,
+ MSTI_PortStatus *status)
+{
+ CTL_CHECK_BRIDGE_PERTREEPORT;
+ MSTP_IN_get_msti_port_status(ptp, status);
+ return 0;
+}
+
+int CTL_set_cist_port_config(int br_index, int port_index,
+ CIST_PortConfig *cfg)
+{
+ CTL_CHECK_BRIDGE_PORT;
+ return MSTP_IN_set_cist_port_config(prt, cfg);
+}
+
+int CTL_set_msti_port_config(int br_index, int port_index, __u16 mstid,
+ MSTI_PortConfig *cfg)
+{
+ CTL_CHECK_BRIDGE_PERTREEPORT;
+ return MSTP_IN_set_msti_port_config(ptp, cfg);
+}
+
+int CTL_port_mcheck(int br_index, int port_index)
+{
+ CTL_CHECK_BRIDGE_PORT;
+ return MSTP_IN_port_mcheck(prt);
+}
+
+int CTL_set_debug_level(int level)
+{
+ INFO("level %d", level);
+ log_level = level;
+ return 0;
+}
+
+int CTL_get_mstilist(int br_index, int *num_mstis, __u16 *mstids)
+{
+ CTL_CHECK_BRIDGE;
+ return MSTP_IN_get_mstilist(br, num_mstis, mstids) ? 0 : -1;
+}
+
+int CTL_create_msti(int br_index, __u16 mstid)
+{
+ CTL_CHECK_BRIDGE;
+ return MSTP_IN_create_msti(br, mstid) ? 0 : -1;
+}
+
+int CTL_delete_msti(int br_index, __u16 mstid)
+{
+ CTL_CHECK_BRIDGE;
+ return MSTP_IN_delete_msti(br, mstid) ? 0 : -1;
+}
+
+int CTL_get_mstconfid(int br_index, mst_configuration_identifier_t *cfg)
+{
+ CTL_CHECK_BRIDGE;
+ *cfg = br->MstConfigId;
+ return 0;
+}
+
+int CTL_set_mstconfid(int br_index, __u16 revision, char *name)
+{
+ CTL_CHECK_BRIDGE;
+ MSTP_IN_set_mst_config_id(br, revision, name);
+ return 0;
+}
+
+int CTL_get_vids2fids(int br_index, __u16 *vids2fids)
+{
+ CTL_CHECK_BRIDGE;
+ memcpy(vids2fids, br->vid2fid, sizeof(br->vid2fid));
+ return 0;
+}
+
+int CTL_get_fids2mstids(int br_index, __u16 *fids2mstids)
+{
+ CTL_CHECK_BRIDGE;
+ int i;
+ for(i = 0; i < COUNT_OF(br->fid2mstid); ++i)
+ fids2mstids[i] = __be16_to_cpu(br->fid2mstid[i]);
+ return 0;
+}
+
+int CTL_set_vid2fid(int br_index, __u16 vid, __u16 fid)
+{
+ CTL_CHECK_BRIDGE;
+ return MSTP_IN_set_vid2fid(br, vid, fid) ? 0 : -1;
+}
+
+int CTL_set_fid2mstid(int br_index, __u16 fid, __u16 mstid)
+{
+ CTL_CHECK_BRIDGE;
+ return MSTP_IN_set_fid2mstid(br, fid, mstid) ? 0 : -1;
+}
+
+int CTL_set_vids2fids(int br_index, __u16 *vids2fids)
+{
+ CTL_CHECK_BRIDGE;
+ return MSTP_IN_set_all_vids2fids(br, vids2fids) ? 0 : -1;
+}
+
+int CTL_set_fids2mstids(int br_index, __u16 *fids2mstids)
+{
+ CTL_CHECK_BRIDGE;
+ return MSTP_IN_set_all_fids2mstids(br, fids2mstids) ? 0 : -1;
+}
--- /dev/null
+/*
+ * brmon.c RTnetlink listener.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Stephen Hemminger <shemminger@osdl.org>
+ * Modified by Srinivas Aji <Aji_Srinivas@emc.com>
+ * for use in RSTP daemon. - 2006-09-01
+ * Modified by Vitalii Demianets <vitas@nppfactor.kiev.ua>
+ * for use in MSTP daemon. - 2011-07-18
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <linux/if_bridge.h>
+
+#include "libnetlink.h"
+#include "bridge_ctl.h"
+#include "netif_utils.h"
+#include "epoll_loop.h"
+
+/* RFC 2863 operational status */
+enum
+{
+ IF_OPER_UNKNOWN,
+ IF_OPER_NOTPRESENT,
+ IF_OPER_DOWN,
+ IF_OPER_LOWERLAYERDOWN,
+ IF_OPER_TESTING,
+ IF_OPER_DORMANT,
+ IF_OPER_UP,
+};
+
+/* link modes */
+enum
+{
+ IF_LINK_MODE_DEFAULT,
+ IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */
+};
+
+static const char *port_states[] =
+{
+ [BR_STATE_DISABLED] = "disabled",
+ [BR_STATE_LISTENING] = "listening",
+ [BR_STATE_LEARNING] = "learning",
+ [BR_STATE_FORWARDING] = "forwarding",
+ [BR_STATE_BLOCKING] = "blocking",
+};
+
+static struct rtnl_handle rth;
+static struct epoll_event_handler br_handler;
+
+struct rtnl_handle rth_state;
+
+static int dump_msg(const struct sockaddr_nl *who, struct nlmsghdr *n,
+ void *arg)
+{
+ FILE *fp = arg;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct rtattr * tb[IFLA_MAX + 1];
+ int len = n->nlmsg_len;
+ char b1[IFNAMSIZ];
+ int af_family = ifi->ifi_family;
+ bool newlink, up;
+ int br_index;
+
+ if(n->nlmsg_type == NLMSG_DONE)
+ return 0;
+
+ len -= NLMSG_LENGTH(sizeof(*ifi));
+ if(len < 0)
+ {
+ return -1;
+ }
+
+ if(af_family != AF_BRIDGE && af_family != AF_UNSPEC)
+ return 0;
+
+ if(n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
+ return 0;
+
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+
+ /* Check if we got this from bonding */
+ if(tb[IFLA_MASTER] && af_family != AF_BRIDGE)
+ return 0;
+
+ if(tb[IFLA_IFNAME] == NULL)
+ {
+ fprintf(stderr, "BUG: nil ifname\n");
+ return -1;
+ }
+
+ if(n->nlmsg_type == RTM_DELLINK)
+ fprintf(fp, "Deleted ");
+
+ fprintf(fp, "%d: %s ", ifi->ifi_index, (char*)RTA_DATA(tb[IFLA_IFNAME]));
+
+ if(tb[IFLA_OPERSTATE])
+ {
+ int state = *(int*)RTA_DATA(tb[IFLA_OPERSTATE]);
+ switch (state)
+ {
+ case IF_OPER_UNKNOWN:
+ fprintf(fp, "Unknown ");
+ break;
+ case IF_OPER_NOTPRESENT:
+ fprintf(fp, "Not Present ");
+ break;
+ case IF_OPER_DOWN:
+ fprintf(fp, "Down ");
+ break;
+ case IF_OPER_LOWERLAYERDOWN:
+ fprintf(fp, "Lowerlayerdown ");
+ break;
+ case IF_OPER_TESTING:
+ fprintf(fp, "Testing ");
+ break;
+ case IF_OPER_DORMANT:
+ fprintf(fp, "Dormant ");
+ break;
+ case IF_OPER_UP:
+ fprintf(fp, "Up ");
+ break;
+ default:
+ fprintf(fp, "State(%d) ", state);
+ }
+ }
+
+ if(tb[IFLA_MTU])
+ fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
+
+ if(tb[IFLA_MASTER])
+ {
+ fprintf(fp, "master %s ",
+ if_indextoname(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
+ }
+
+ if(tb[IFLA_PROTINFO])
+ {
+ uint8_t state = *(uint8_t *)RTA_DATA(tb[IFLA_PROTINFO]);
+ if(state <= BR_STATE_BLOCKING)
+ fprintf(fp, "state %s", port_states[state]);
+ else
+ fprintf(fp, "state (%d)", state);
+ }
+
+ fprintf(fp, "\n");
+ fflush(fp);
+
+ newlink = (n->nlmsg_type == RTM_NEWLINK);
+ up = false;
+ if(newlink && tb[IFLA_OPERSTATE])
+ {
+ int state = *(uint8_t*)RTA_DATA(tb[IFLA_OPERSTATE]);
+ up = (state == IF_OPER_UP) || (state == IF_OPER_UNKNOWN);
+ }
+
+ if(tb[IFLA_MASTER])
+ br_index = *(int*)RTA_DATA(tb[IFLA_MASTER]);
+ else if(is_bridge((char*)RTA_DATA(tb[IFLA_IFNAME])))
+ br_index = ifi->ifi_index;
+ else
+ br_index = -1;
+
+ bridge_notify(br_index, ifi->ifi_index, newlink, up);
+
+ return 0;
+}
+
+static inline void br_ev_handler(uint32_t events, struct epoll_event_handler *h)
+{
+ if(rtnl_listen(&rth, dump_msg, stdout) < 0)
+ {
+ fprintf(stderr, "Error on bridge monitoring socket\n");
+ exit(-1);
+ }
+}
+
+int init_bridge_ops(void)
+{
+ if(rtnl_open(&rth, ~RTMGRP_TC) < 0)
+ {
+ fprintf(stderr, "Couldn't open rtnl socket for monitoring\n");
+ return -1;
+ }
+
+ if(rtnl_open(&rth_state, 0) < 0)
+ {
+ fprintf(stderr, "Couldn't open rtnl socket for setting state\n");
+ return -1;
+ }
+
+ if(rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETLINK) < 0)
+ {
+ fprintf(stderr, "Cannot send dump request: %m\n");
+ return -1;
+ }
+
+ if(rtnl_dump_filter(&rth, dump_msg, stdout, NULL, NULL) < 0)
+ {
+ fprintf(stderr, "Dump terminated\n");
+ return -1;
+ }
+
+ if(fcntl(rth.fd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ fprintf(stderr, "Error setting O_NONBLOCK: %m\n");
+ return -1;
+ }
+
+ br_handler.fd = rth.fd;
+ br_handler.arg = NULL;
+ br_handler.handler = br_ev_handler;
+
+ if(add_epoll(&br_handler) < 0)
+ return -1;
+
+ return 0;
+}
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#ifndef CTL_SOCKET_H
+#define CTL_SOCKET_H
+
+#include <linux/if_bridge.h>
+#include <asm/byteorder.h>
+
+#include "mstp.h"
+
+struct ctl_msg_hdr
+{
+ int cmd;
+ int lin;
+ int lout;
+ int llog;
+ int res;
+};
+
+#define LOG_STRING_LEN 256
+
+typedef struct _log_string
+{
+ char buf[LOG_STRING_LEN];
+} LogString;
+
+#define set_socket_address(sa, string) do{ \
+ struct sockaddr_un * tmp_sa = (sa); \
+ memset(tmp_sa, 0, sizeof(*tmp_sa)); \
+ tmp_sa->sun_family = AF_UNIX; \
+ strcpy(tmp_sa->sun_path + 1, (string)); \
+ }while(0)
+
+#define MSTP_SERVER_SOCK_NAME ".mstp_server"
+
+/* COMMANDS */
+#define CTL_DECLARE(name) \
+int CTL_ ## name name ## _ARGS
+
+/* get_cist_bridge_status */
+#define CMD_CODE_get_cist_bridge_status 101
+#define get_cist_bridge_status_ARGS (int br_index, CIST_BridgeStatus *status, \
+ char *root_port_name)
+struct get_cist_bridge_status_IN
+{
+ int br_index;
+};
+struct get_cist_bridge_status_OUT
+{
+ CIST_BridgeStatus status;
+ char root_port_name[IFNAMSIZ];
+};
+#define get_cist_bridge_status_COPY_IN ({ in->br_index = br_index; })
+#define get_cist_bridge_status_COPY_OUT ({ *status = out->status; \
+ strncpy(root_port_name, out->root_port_name, IFNAMSIZ); })
+#define get_cist_bridge_status_CALL (in->br_index, &out->status, \
+ out->root_port_name)
+CTL_DECLARE(get_cist_bridge_status);
+
+/* get_msti_bridge_status */
+#define CMD_CODE_get_msti_bridge_status 102
+#define get_msti_bridge_status_ARGS (int br_index, __u16 mstid, \
+ MSTI_BridgeStatus *status, \
+ char *root_port_name)
+struct get_msti_bridge_status_IN
+{
+ int br_index;
+ __u16 mstid;
+};
+struct get_msti_bridge_status_OUT
+{
+ MSTI_BridgeStatus status;
+ char root_port_name[IFNAMSIZ];
+};
+#define get_msti_bridge_status_COPY_IN \
+ ({ in->br_index = br_index; in->mstid = mstid; })
+#define get_msti_bridge_status_COPY_OUT ({ *status = out->status; \
+ strncpy(root_port_name, out->root_port_name, IFNAMSIZ); })
+#define get_msti_bridge_status_CALL (in->br_index, in->mstid, &out->status, \
+ out->root_port_name)
+CTL_DECLARE(get_msti_bridge_status);
+
+/* set_cist_bridge_config */
+#define CMD_CODE_set_cist_bridge_config 103
+#define set_cist_bridge_config_ARGS (int br_index, CIST_BridgeConfig *cfg)
+struct set_cist_bridge_config_IN
+{
+ int br_index;
+ CIST_BridgeConfig cfg;
+};
+struct set_cist_bridge_config_OUT
+{
+};
+#define set_cist_bridge_config_COPY_IN \
+ ({ in->br_index = br_index; in->cfg = *cfg; })
+#define set_cist_bridge_config_COPY_OUT ({ (void)0; })
+#define set_cist_bridge_config_CALL (in->br_index, &in->cfg)
+CTL_DECLARE(set_cist_bridge_config);
+
+/* set_msti_bridge_config */
+#define CMD_CODE_set_msti_bridge_config 104
+#define set_msti_bridge_config_ARGS (int br_index, __u16 mstid, \
+ __u8 bridge_priority)
+struct set_msti_bridge_config_IN
+{
+ int br_index;
+ __u16 mstid;
+ __u8 bridge_priority;
+};
+struct set_msti_bridge_config_OUT
+{
+};
+#define set_msti_bridge_config_COPY_IN \
+ ({ in->br_index = br_index; in->mstid = mstid; \
+ in->bridge_priority = bridge_priority; })
+#define set_msti_bridge_config_COPY_OUT ({ (void)0; })
+#define set_msti_bridge_config_CALL (in->br_index, in->mstid, \
+ in->bridge_priority)
+CTL_DECLARE(set_msti_bridge_config);
+
+/* get_cist_port_status */
+#define CMD_CODE_get_cist_port_status 105
+#define get_cist_port_status_ARGS (int br_index, int port_index, \
+ CIST_PortStatus *status)
+struct get_cist_port_status_IN
+{
+ int br_index;
+ int port_index;
+};
+struct get_cist_port_status_OUT
+{
+ CIST_PortStatus status;
+};
+#define get_cist_port_status_COPY_IN \
+ ({ in->br_index = br_index; in->port_index = port_index; })
+#define get_cist_port_status_COPY_OUT ({ *status = out->status; })
+#define get_cist_port_status_CALL (in->br_index, in->port_index, &out->status)
+CTL_DECLARE(get_cist_port_status);
+
+/* get_msti_port_status */
+#define CMD_CODE_get_msti_port_status 106
+#define get_msti_port_status_ARGS (int br_index, int port_index, __u16 mstid, \
+ MSTI_PortStatus *status)
+struct get_msti_port_status_IN
+{
+ int br_index;
+ int port_index;
+ __u16 mstid;
+};
+struct get_msti_port_status_OUT
+{
+ MSTI_PortStatus status;
+};
+#define get_msti_port_status_COPY_IN \
+ ({ in->br_index = br_index; in->port_index = port_index; \
+ in->mstid = mstid; })
+#define get_msti_port_status_COPY_OUT ({ *status = out->status; })
+#define get_msti_port_status_CALL (in->br_index, in->port_index, in->mstid, \
+ &out->status)
+CTL_DECLARE(get_msti_port_status);
+
+/* set_cist_port_config */
+#define CMD_CODE_set_cist_port_config 107
+#define set_cist_port_config_ARGS (int br_index, int port_index, \
+ CIST_PortConfig *cfg)
+struct set_cist_port_config_IN
+{
+ int br_index;
+ int port_index;
+ CIST_PortConfig cfg;
+};
+struct set_cist_port_config_OUT
+{
+};
+#define set_cist_port_config_COPY_IN \
+ ({ in->br_index = br_index; in->port_index = port_index; in->cfg = *cfg; })
+#define set_cist_port_config_COPY_OUT ({ (void)0; })
+#define set_cist_port_config_CALL (in->br_index, in->port_index, &in->cfg)
+CTL_DECLARE(set_cist_port_config);
+
+/* set_msti_port_config */
+#define CMD_CODE_set_msti_port_config 108
+#define set_msti_port_config_ARGS (int br_index, int port_index, __u16 mstid, \
+ MSTI_PortConfig *cfg)
+struct set_msti_port_config_IN
+{
+ int br_index;
+ int port_index;
+ __u16 mstid;
+ MSTI_PortConfig cfg;
+};
+struct set_msti_port_config_OUT
+{
+};
+#define set_msti_port_config_COPY_IN \
+ ({ in->br_index = br_index; in->port_index = port_index; \
+ in->mstid = mstid; in->cfg = *cfg; })
+#define set_msti_port_config_COPY_OUT ({ (void)0; })
+#define set_msti_port_config_CALL (in->br_index, in->port_index, in->mstid, \
+ &in->cfg)
+CTL_DECLARE(set_msti_port_config);
+
+/* port_mcheck */
+#define CMD_CODE_port_mcheck 109
+#define port_mcheck_ARGS (int br_index, int port_index)
+struct port_mcheck_IN
+{
+ int br_index;
+ int port_index;
+};
+struct port_mcheck_OUT
+{
+};
+#define port_mcheck_COPY_IN \
+ ({ in->br_index = br_index; in->port_index = port_index; })
+#define port_mcheck_COPY_OUT ({ (void)0; })
+#define port_mcheck_CALL (in->br_index, in->port_index)
+CTL_DECLARE(port_mcheck);
+
+/* set_debug_level */
+#define CMD_CODE_set_debug_level 110
+#define set_debug_level_ARGS (int level)
+struct set_debug_level_IN
+{
+ int level;
+};
+struct set_debug_level_OUT
+{
+};
+#define set_debug_level_COPY_IN ({ in->level = level; })
+#define set_debug_level_COPY_OUT ({ (void)0; })
+#define set_debug_level_CALL (in->level)
+CTL_DECLARE(set_debug_level);
+
+/* get_mstilist */
+#define CMD_CODE_get_mstilist 111
+#define get_mstilist_ARGS (int br_index, int *num_mstis, __u16 *mstids)
+struct get_mstilist_IN
+{
+ int br_index;
+};
+struct get_mstilist_OUT
+{
+ int num_mstis;
+ __u16 mstids[MAX_IMPLEMENTATION_MSTIS + 1]; /* +1 - for the CIST */
+};
+#define get_mstilist_COPY_IN \
+ ({ in->br_index = br_index; })
+#define get_mstilist_COPY_OUT ({ *num_mstis = out->num_mstis; \
+ memcpy(mstids, out->mstids, (*num_mstis) * sizeof(out->mstids[0])); })
+#define get_mstilist_CALL (in->br_index, &out->num_mstis, out->mstids)
+CTL_DECLARE(get_mstilist);
+
+/* create_msti */
+#define CMD_CODE_create_msti 112
+#define create_msti_ARGS (int br_index, __u16 mstid)
+struct create_msti_IN
+{
+ int br_index;
+ __u16 mstid;
+};
+struct create_msti_OUT
+{
+};
+#define create_msti_COPY_IN \
+ ({ in->br_index = br_index; in->mstid = mstid; })
+#define create_msti_COPY_OUT ({ (void)0; })
+#define create_msti_CALL (in->br_index, in->mstid)
+CTL_DECLARE(create_msti);
+
+/* delete_msti */
+#define CMD_CODE_delete_msti 113
+#define delete_msti_ARGS (int br_index, __u16 mstid)
+struct delete_msti_IN
+{
+ int br_index;
+ __u16 mstid;
+};
+struct delete_msti_OUT
+{
+};
+#define delete_msti_COPY_IN \
+ ({ in->br_index = br_index; in->mstid = mstid; })
+#define delete_msti_COPY_OUT ({ (void)0; })
+#define delete_msti_CALL (in->br_index, in->mstid)
+CTL_DECLARE(delete_msti);
+
+/* get_mstconfid */
+#define CMD_CODE_get_mstconfid 114
+#define get_mstconfid_ARGS (int br_index, mst_configuration_identifier_t *cfg)
+struct get_mstconfid_IN
+{
+ int br_index;
+};
+struct get_mstconfid_OUT
+{
+ mst_configuration_identifier_t cfg;
+};
+#define get_mstconfid_COPY_IN ({ in->br_index = br_index; })
+#define get_mstconfid_COPY_OUT ({ *cfg = out->cfg; })
+#define get_mstconfid_CALL (in->br_index, &out->cfg)
+CTL_DECLARE(get_mstconfid);
+
+/* set_mstconfid */
+#define CMD_CODE_set_mstconfid 115
+#define set_mstconfid_ARGS (int br_index, __u16 revision, char *name)
+struct set_mstconfid_IN
+{
+ int br_index;
+ __u16 revision;
+ __u8 name[CONFIGURATION_NAME_LEN];
+};
+struct set_mstconfid_OUT
+{
+};
+#define set_mstconfid_COPY_IN ({ in->br_index = br_index; \
+ in->revision = revision; strncpy(in->name, name, sizeof(in->name)); })
+#define set_mstconfid_COPY_OUT ({ (void)0; })
+#define set_mstconfid_CALL (in->br_index, in->revision, in->name)
+CTL_DECLARE(set_mstconfid);
+
+/* get_vids2fids */
+#define CMD_CODE_get_vids2fids 116
+#define get_vids2fids_ARGS (int br_index, __u16 *vids2fids)
+struct get_vids2fids_IN
+{
+ int br_index;
+};
+struct get_vids2fids_OUT
+{
+ __u16 vids2fids[MAX_VID + 1];
+};
+#define get_vids2fids_COPY_IN ({ in->br_index = br_index; })
+#define get_vids2fids_COPY_OUT ({ \
+ memcpy(vids2fids, out->vids2fids, sizeof(out->vids2fids)); })
+#define get_vids2fids_CALL (in->br_index, out->vids2fids)
+CTL_DECLARE(get_vids2fids);
+
+/* get_fids2mstids */
+#define CMD_CODE_get_fids2mstids 117
+#define get_fids2mstids_ARGS (int br_index, __u16 *fids2mstids)
+struct get_fids2mstids_IN
+{
+ int br_index;
+};
+struct get_fids2mstids_OUT
+{
+ __u16 fids2mstids[MAX_FID + 1];
+};
+#define get_fids2mstids_COPY_IN ({ in->br_index = br_index; })
+#define get_fids2mstids_COPY_OUT ({ \
+ memcpy(fids2mstids, out->fids2mstids, sizeof(out->fids2mstids)); })
+#define get_fids2mstids_CALL (in->br_index, out->fids2mstids)
+CTL_DECLARE(get_fids2mstids);
+
+/* set_vid2fid */
+#define CMD_CODE_set_vid2fid 118
+#define set_vid2fid_ARGS (int br_index, __u16 vid, __u16 fid)
+struct set_vid2fid_IN
+{
+ int br_index;
+ __u16 vid, fid;
+};
+struct set_vid2fid_OUT
+{
+};
+#define set_vid2fid_COPY_IN ({ in->br_index = br_index; in->vid = vid; \
+ in->fid = fid; })
+#define set_vid2fid_COPY_OUT ({ (void)0; })
+#define set_vid2fid_CALL (in->br_index, in->vid, in->fid)
+CTL_DECLARE(set_vid2fid);
+
+/* set_fid2mstid */
+#define CMD_CODE_set_fid2mstid 119
+#define set_fid2mstid_ARGS (int br_index, __u16 fid, __u16 mstid)
+struct set_fid2mstid_IN
+{
+ int br_index;
+ __u16 fid, mstid;
+};
+struct set_fid2mstid_OUT
+{
+};
+#define set_fid2mstid_COPY_IN ({ in->br_index = br_index; in->fid = fid; \
+ in->mstid = mstid; })
+#define set_fid2mstid_COPY_OUT ({ (void)0; })
+#define set_fid2mstid_CALL (in->br_index, in->fid, in->mstid)
+CTL_DECLARE(set_fid2mstid);
+
+/* set_vids2fids */
+#define CMD_CODE_set_vids2fids 120
+#define set_vids2fids_ARGS (int br_index, __u16 *vids2fids)
+struct set_vids2fids_IN
+{
+ int br_index;
+ __u16 vids2fids[MAX_VID + 1];
+};
+struct set_vids2fids_OUT
+{
+};
+#define set_vids2fids_COPY_IN ({ in->br_index = br_index; \
+ memcpy(in->vids2fids, vids2fids, sizeof(in->vids2fids)); })
+#define set_vids2fids_COPY_OUT ({ (void)0; })
+#define set_vids2fids_CALL (in->br_index, in->vids2fids)
+CTL_DECLARE(set_vids2fids);
+
+/* set_fids2mstids */
+#define CMD_CODE_set_fids2mstids 121
+#define set_fids2mstids_ARGS (int br_index, __u16 *fids2mstids)
+struct set_fids2mstids_IN
+{
+ int br_index;
+ __u16 fids2mstids[MAX_FID + 1];
+};
+struct set_fids2mstids_OUT
+{
+};
+#define set_fids2mstids_COPY_IN ({ in->br_index = br_index; \
+ memcpy(in->fids2mstids, fids2mstids, sizeof(in->fids2mstids)); })
+#define set_fids2mstids_COPY_OUT ({ (void)0; })
+#define set_fids2mstids_CALL (in->br_index, in->fids2mstids)
+CTL_DECLARE(set_fids2mstids);
+
+/* General case part in ctl command server switch */
+#define SERVER_MESSAGE_CASE(name) \
+ case CMD_CODE_ ## name : do \
+ { \
+ struct name ## _IN in0, *in = &in0; \
+ struct name ## _OUT out0, *out = &out0; \
+ if(sizeof(*in) != lin || sizeof(*out) != lout) \
+ { \
+ LOG("Bad sizes lin %d != %zd or lout %d != %zd", \
+ lin, sizeof(*in), lout, sizeof(*out)); \
+ return -1; \
+ } \
+ memcpy(in, inbuf, lin); \
+ int r = CTL_ ## name name ## _CALL; \
+ if(r) \
+ return r; \
+ if(outbuf) \
+ memcpy(outbuf, out, lout); \
+ return r; \
+ }while(0)
+
+/* Wraper for the control functions in the control command client */
+#define CLIENT_SIDE_FUNCTION(name) \
+CTL_DECLARE(name) \
+{ \
+ struct name ## _IN in0, *in = &in0; \
+ struct name ## _OUT out0, *out = &out0; \
+ name ## _COPY_IN; \
+ int res = 0; \
+ LogString log = { .buf = "" }; \
+ int r = send_ctl_message(CMD_CODE_ ## name, in, sizeof(*in), \
+ out, sizeof(*out), &log, &res); \
+ if(r || res) \
+ LOG("Got return code %d, %d\n%s", r, res, log.buf); \
+ if(r) \
+ return r; \
+ if(res) \
+ return res; \
+ name ## _COPY_OUT; \
+ return 0; \
+}
+
+#endif /* CTL_SOCKET_H */
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#include <string.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/stat.h>
+
+#include "ctl_socket_client.h"
+#include "log.h"
+
+static int get_index_die(const char *ifname, const char *doc, bool die)
+{
+ int r = if_nametoindex(ifname);
+ if(0 == r)
+ {
+ fprintf(stderr,
+ "Can't find index for %s %s. Not a valid interface.\n",
+ doc, ifname);
+ if(die)
+ exit(1);
+ return -1;
+ }
+ return r;
+}
+
+static inline int get_index(const char *ifname, const char *doc)
+{
+ return get_index_die(ifname, doc, true);
+}
+
+static inline int get_id(const char *str, const char *doc, unsigned int max_id)
+{
+ int id = strtol(str, NULL, 10);
+ if((0 > id) || (max_id < id)
+ || ((0 == id) && ('0' != str[0]))
+ )
+ {
+ fprintf(stderr, "Bad %s %s\n", doc, str);
+ return -1;
+ }
+ return id;
+}
+
+#define GET_NUM_FROM_PRIO(p) (__be16_to_cpu(p) & 0x0FFF)
+
+#define BR_ID_FMT "%01hhX.%03hX.%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX"
+#define BR_ID_ARGS(x) ((GET_PRIORITY_FROM_IDENTIFIER(x) >> 4) & 0x0F), \
+ GET_NUM_FROM_PRIO((x).s.priority), \
+ x.s.mac_address[0], x.s.mac_address[1], x.s.mac_address[2], \
+ x.s.mac_address[3], x.s.mac_address[4], x.s.mac_address[5]
+
+#define PRT_ID_FMT "%01hhX.%03hX"
+#define PRT_ID_ARGS(x) ((GET_PRIORITY_FROM_IDENTIFIER(x) >> 4) & 0x0F), \
+ GET_NUM_FROM_PRIO(x)
+
+#define BOOL_STR(x) ((x) ? "yes" : "no")
+
+static int do_showbridge(const char *br_name)
+{
+ CIST_BridgeStatus s;
+ char root_port_name[IFNAMSIZ];
+ unsigned int root_portno;
+ int br_index = get_index_die(br_name, "bridge", false);
+ if(0 > br_index)
+ return br_index;
+
+ if(CTL_get_cist_bridge_status(br_index, &s, root_port_name))
+ return -1;
+ printf("%s CIST info\n", br_name);
+ printf(" enabled %s\n", BOOL_STR(s.enabled));
+ printf(" bridge id "BR_ID_FMT"\n", BR_ID_ARGS(s.bridge_id));
+ printf(" designated root "BR_ID_FMT"\n", BR_ID_ARGS(s.designated_root));
+ printf(" regional root "BR_ID_FMT"\n", BR_ID_ARGS(s.regional_root));
+ printf(" root port ");
+ if(0 != (root_portno = GET_NUM_FROM_PRIO(s.root_port_id)))
+ printf("%s (#%u)\n", root_port_name, root_portno);
+ else
+ printf("none\n");
+ printf(" path cost %-10u ", s.root_path_cost);
+ printf("internal path cost %u\n", s.internal_path_cost);
+ printf(" max age %-10u ", s.root_max_age);
+ printf("bridge max age %u\n", s.bridge_max_age);
+ printf(" forward delay %-10u ", s.root_forward_delay);
+ printf("bridge forward delay %u\n", s.bridge_forward_delay);
+ printf(" tx hold count %-10u ", s.tx_hold_count);
+ printf("max hops %hhu\n", s.max_hops);
+ printf(" force protocol version %u\n", s.protocol_version);
+ printf(" time since topology change %u\n", s.time_since_topology_change);
+ printf(" toplogy change count %u\n", s.topology_change_count);
+ printf(" topology change %s\n", BOOL_STR(s.topology_change));
+
+ return 0;
+}
+
+#define SYSFS_PATH_MAX 256
+#define SYSFS_CLASS_NET "/sys/class/net"
+
+static int isbridge(const struct dirent *entry)
+{
+ char path[SYSFS_PATH_MAX];
+ int save_errno;
+ bool result;
+ struct stat st;
+
+ snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "/%s/bridge",
+ entry->d_name);
+ save_errno = errno;
+ result = (0 == stat(path, &st)) && S_ISDIR(st.st_mode);
+ errno = save_errno;
+ return result;
+}
+
+static int cmd_showbridge(int argc, char *const *argv)
+{
+ int i, count = 0;
+ int r = 0;
+ struct dirent **namelist;
+
+ if(1 < argc)
+ {
+ count = argc - 1;
+ }
+ else
+ {
+ /* TODO: use versionsort, if available */
+ count = scandir(SYSFS_CLASS_NET, &namelist, isbridge, alphasort);
+ if(0 > count)
+ {
+ fprintf(stderr, "Error getting list of all bridges\n");
+ return -1;
+ }
+ }
+
+ for(i = 0; i < count; ++i)
+ {
+ const char *name;
+ if(1 < argc)
+ name = argv[i + 1];
+ else
+ name = namelist[i]->d_name;
+
+ int err = do_showbridge(name);
+ if(err)
+ r = err;
+ }
+
+ if(1 >= argc)
+ {
+ for(i = 0; i < count; ++i)
+ free(namelist[i]);
+ free(namelist);
+ }
+
+ return r;
+}
+
+static int cmd_showtree(int argc, char *const *argv)
+{
+ MSTI_BridgeStatus s;
+ char root_port_name[IFNAMSIZ];
+ unsigned int root_portno;
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int mstid = get_id(argv[2], "mstid", MAX_MSTID);
+ if(0 > mstid)
+ return mstid;
+
+ if(CTL_get_msti_bridge_status(br_index, mstid, &s, root_port_name))
+ return -1;
+
+ printf("%s MSTI %hu info\n", argv[1], mstid);
+ printf(" bridge id "BR_ID_FMT"\n", BR_ID_ARGS(s.bridge_id));
+ printf(" regional root "BR_ID_FMT"\n", BR_ID_ARGS(s.regional_root));
+ printf(" root port ");
+ if(0 != (root_portno = GET_NUM_FROM_PRIO(s.root_port_id)))
+ printf("%s (#%u)\n", root_port_name, root_portno);
+ else
+ printf("none\n");
+ printf(" internal path cost %u\n", s.internal_path_cost);
+ printf(" time since topology change %u\n", s.time_since_topology_change);
+ printf(" toplogy change count %u\n", s.topology_change_count);
+ printf(" topology change %s\n", BOOL_STR(s.topology_change));
+
+ return 0;
+}
+
+#define STATE_STR(_state) \
+ ({ \
+ int _s = _state; \
+ char *_str = "unknown"; \
+ switch(_s) \
+ { \
+ case BR_STATE_DISABLED: \
+ case BR_STATE_BLOCKING: \
+ case BR_STATE_LISTENING: _str = "discarding"; break; \
+ case BR_STATE_LEARNING: _str = "learning"; break; \
+ case BR_STATE_FORWARDING:_str = "forwarding"; break; \
+ } \
+ _str; \
+ })
+
+#define SHORT_STATE_STR(_state) \
+ ({ \
+ int _s = _state; \
+ char *_str = "unkn"; \
+ switch(_s) \
+ { \
+ case BR_STATE_DISABLED: \
+ case BR_STATE_BLOCKING: \
+ case BR_STATE_LISTENING: _str = "disc"; break; \
+ case BR_STATE_LEARNING: _str = "lear"; break; \
+ case BR_STATE_FORWARDING:_str = "forw"; break; \
+ } \
+ _str; \
+ })
+
+#define ADMIN_P2P_STR(_state) \
+ ({ \
+ admin_p2p_t _s = _state; \
+ char *_str = "unkn"; \
+ switch(_s) \
+ { \
+ case p2pForceFalse:_str = "no"; break; \
+ case p2pForceTrue: _str = "yes"; break; \
+ case p2pAuto: _str = "auto"; break; \
+ } \
+ _str; \
+ })
+
+#define ROLE_STR(_role) \
+ ({ \
+ port_role_t _r = _role; \
+ char *_str = "Unknown"; \
+ switch(_r) \
+ { \
+ case roleRoot: _str = "Root"; break; \
+ case roleDesignated:_str = "Designated"; break; \
+ case roleAlternate: _str = "Alternate"; break; \
+ case roleBackup: _str = "Backup"; break; \
+ case roleMaster: _str = "Master"; break; \
+ case roleDisabled: _str = "Disabled"; break; \
+ } \
+ _str; \
+ })
+
+#define SHORT_ROLE_STR(_role) \
+ ({ \
+ port_role_t _r = _role; \
+ char *_str = "Unkn"; \
+ switch(_r) \
+ { \
+ case roleRoot: _str = "Root"; break; \
+ case roleDesignated:_str = "Desg"; break; \
+ case roleAlternate: _str = "Altn"; break; \
+ case roleBackup: _str = "Back"; break; \
+ case roleMaster: _str = "Mstr"; break; \
+ case roleDisabled: _str = "Disa"; break; \
+ } \
+ _str; \
+ })
+
+
+static int detail = 0;
+
+static int do_showport(int br_index, const char *bridge_name,
+ const char *port_name)
+{
+ CIST_PortStatus s;
+ int r = 0;
+ int port_index = get_index_die(port_name, "port", false);
+ if(0 > port_index)
+ return port_index;
+
+ if((r = CTL_get_cist_port_status(br_index, port_index, &s)))
+ {
+ fprintf(stderr, "%s:%s Failed to get port state\n",
+ bridge_name, port_name);
+ return -1;
+ }
+
+ if(detail)
+ {
+ printf("%s:%s CIST info\n", bridge_name, port_name);
+ printf(" enabled %-23s ", BOOL_STR(s.enabled));
+ printf("role %s\n", ROLE_STR(s.role));
+ printf(" port id "PRT_ID_FMT" ",
+ PRT_ID_ARGS(s.port_id));
+ printf("state %s\n", STATE_STR(s.state));
+ printf(" external port cost %-23u ", s.external_port_path_cost);
+ printf("admin external cost %u\n", s.admin_external_port_path_cost);
+ printf(" internal port cost %-23u ", s.internal_port_path_cost);
+ printf("admin internal cost %u\n", s.admin_internal_port_path_cost);
+ printf(" designated root "BR_ID_FMT" ",
+ BR_ID_ARGS(s.designated_root));
+ printf("dsgn external cost %u\n", s.designated_external_cost);
+ printf(" dsgn regional root "BR_ID_FMT" ",
+ BR_ID_ARGS(s.designated_regional_root));
+ printf("dsgn internal cost %u\n", s.designated_internal_cost);
+ printf(" designated bridge "BR_ID_FMT" ",
+ BR_ID_ARGS(s.designated_bridge));
+ printf("designated port "PRT_ID_FMT"\n",
+ PRT_ID_ARGS(s.designated_port));
+ printf(" admin edge port %-23s ", BOOL_STR(s.admin_edge_port));
+ printf("auto edge port %s\n", BOOL_STR(s.auto_edge_port));
+ printf(" oper edge port %-23s ", BOOL_STR(s.oper_edge_port));
+ printf("toplogy change ack %s\n", BOOL_STR(s.tc_ack));
+ printf(" point-to-point %-23s ", BOOL_STR(s.oper_p2p));
+ printf("admin point-to-point %s\n", ADMIN_P2P_STR(s.admin_p2p));
+ printf(" restricted role %-23s ", BOOL_STR(s.restricted_role));
+ printf("restricted TCN %s\n", BOOL_STR(s.restricted_tcn));
+ printf(" port hello time %-23u ", s.port_hello_time);
+ printf("disputed %s\n", BOOL_STR(s.disputed));
+ }
+ else
+ {
+ printf("%c%c %-5s "PRT_ID_FMT" %4s "BR_ID_FMT" "BR_ID_FMT" "PRT_ID_FMT" %s\n",
+ (s.oper_p2p) ? ' ' : '*',
+ (s.oper_edge_port) ? 'E' : ' ',
+ port_name,
+ PRT_ID_ARGS(s.port_id),
+ s.enabled ? SHORT_STATE_STR(s.state) : "down",
+ BR_ID_ARGS(s.designated_root),
+ BR_ID_ARGS(s.designated_bridge),
+ PRT_ID_ARGS(s.designated_port),
+ SHORT_ROLE_STR(s.role));
+ }
+ return 0;
+}
+
+static int not_dot_dotdot(const struct dirent *entry)
+{
+ const char *n = entry->d_name;
+
+ return !('.' == n[0] && (0 == n[1] || ('.' == n[1] && 0 == n[2])));
+}
+
+static int cmd_showport(int argc, char *const *argv)
+{
+ int r = 0;
+
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+
+ int i, count = 0;
+ struct dirent **namelist;
+
+ if(2 < argc)
+ {
+ count = argc - 2;
+ }
+ else
+ {
+ char buf[SYSFS_PATH_MAX];
+ snprintf(buf, sizeof(buf), SYSFS_CLASS_NET "/%s/brif", argv[1]);
+ /* TODO: use versionsort, if available */
+ count = scandir(buf, &namelist, not_dot_dotdot, alphasort);
+ if(0 > count)
+ {
+ fprintf(stderr, "Error getting list of all ports of bridge %s\n",
+ argv[1]);
+ return -1;
+ }
+ }
+
+ for(i = 0; i < count; ++i)
+ {
+ const char *name;
+ if(2 < argc)
+ name = argv[i + 2];
+ else
+ name = namelist[i]->d_name;
+
+ int err = do_showport(br_index, argv[1], name);
+ if(err)
+ r = err;
+ }
+
+ if(2 >= argc)
+ {
+ for(i = 0; i < count; ++i)
+ free(namelist[i]);
+ free(namelist);
+ }
+
+ return r;
+}
+
+static int cmd_showportdetail(int argc, char *const *argv)
+{
+ detail = 1;
+ return cmd_showport(argc, argv);
+}
+
+static int cmd_showtreeport(int argc, char *const *argv)
+{
+ MSTI_PortStatus s;
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int port_index = get_index(argv[2], "port");
+ if(0 > port_index)
+ return port_index;
+ int mstid = get_id(argv[3], "mstid", MAX_MSTID);
+ if(0 > mstid)
+ return mstid;
+
+ if(CTL_get_msti_port_status(br_index, port_index, mstid, &s))
+ return -1;
+
+ printf("%s:%s MSTI %hu info\n", argv[1], argv[2], mstid);
+ printf(" role %-23s ", ROLE_STR(s.role));
+ printf("port id "PRT_ID_FMT"\n", PRT_ID_ARGS(s.port_id));
+ printf(" state %-23s ", STATE_STR(s.state));
+ printf("disputed %s\n", BOOL_STR(s.disputed));
+ printf(" internal port cost %-23u ", s.internal_port_path_cost);
+ printf("admin internal cost %u\n", s.admin_internal_port_path_cost);
+ printf(" dsgn regional root "BR_ID_FMT" ",
+ BR_ID_ARGS(s.designated_regional_root));
+ printf("dsgn internal cost %u\n", s.designated_internal_cost);
+ printf(" designated bridge "BR_ID_FMT" ",
+ BR_ID_ARGS(s.designated_bridge));
+ printf("designated port "PRT_ID_FMT"\n",
+ PRT_ID_ARGS(s.designated_port));
+
+ return 0;
+}
+
+static unsigned int getuint(const char *s)
+{
+ char *end;
+ long l;
+ l = strtoul(s, &end, 0);
+ if(0 == *s || 0 != *end || INT_MAX < l)
+ {
+ fprintf(stderr, "Invalid unsigned int arg %s\n", s);
+ exit(1);
+ }
+ return l;
+}
+
+static int getenum(const char *s, const char *opt[])
+{
+ int i;
+ for(i = 0; opt[i] != NULL; ++i)
+ if(0 == strcmp(s, opt[i]))
+ return i;
+
+ fprintf(stderr, "Invalid argument %s: expecting one of ", s);
+ for(i = 0; opt[i] != NULL; ++i)
+ fprintf(stderr, "%s%s", opt[i], (opt[i + 1] ? ", " : "\n"));
+
+ exit(1);
+}
+
+static int getyesno(const char *s, const char *yes, const char *no)
+{
+ /* Reverse yes and no so error message looks more normal */
+ const char *opt[] = { yes, no, NULL };
+ return 1 - getenum(s, opt);
+}
+
+static int cmd_setmstconfid(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ unsigned int revision = getuint(argv[2]);
+ if(revision > 0xFFFF)
+ {
+ fprintf(stderr, "Bad revision %s\n", argv[2]);
+ return -1;
+ }
+ return CTL_set_mstconfid(br_index, revision, argv[3]);
+}
+
+#define set_bridge_cfg(field, value) \
+ ({ \
+ CIST_BridgeConfig c; \
+ memset(&c, 0, sizeof(c)); \
+ c.field = value; \
+ c.set_ ## field = true; \
+ int r = CTL_set_cist_bridge_config(br_index, &c); \
+ if(r) \
+ printf("Couldn't change bridge " #field "\n"); \
+ r; \
+ })
+
+#define set_port_cfg(field, value) \
+ ({ \
+ CIST_PortConfig c; \
+ memset(&c, 0, sizeof(c)); \
+ c.field = value; \
+ c.set_ ## field = true; \
+ int r = CTL_set_cist_port_config(br_index, port_index, &c); \
+ if(r) \
+ printf("Couldn't change port " #field "\n"); \
+ r; \
+ })
+
+#define set_tree_port_cfg(field, value) \
+ ({ \
+ MSTI_PortConfig c; \
+ memset(&c, 0, sizeof(c)); \
+ c.field = value; \
+ c.set_ ## field = true; \
+ int r = CTL_set_msti_port_config(br_index, port_index, mstid, &c); \
+ if(r) \
+ printf("Couldn't change per-tree port " #field "\n"); \
+ r; \
+ })
+
+static int cmd_setbridgemaxage(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ return set_bridge_cfg(bridge_max_age, getuint(argv[2]));
+}
+
+static int cmd_setbridgefdelay(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ return set_bridge_cfg(bridge_forward_delay, getuint(argv[2]));
+}
+
+static int cmd_setbridgemaxhops(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ unsigned int max_hops = getuint(argv[2]);
+ if(max_hops > 255)
+ max_hops = 255;
+ return set_bridge_cfg(max_hops, max_hops);
+}
+
+static int cmd_setbridgeforcevers(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ const char *opts[] = { "stp", "rstp", "mstp", NULL };
+ int vals[] = { protoSTP, protoRSTP, protoMSTP };
+ return set_bridge_cfg(protocol_version, vals[getenum(argv[2], opts)]);
+}
+
+static int cmd_setbridgetxholdcount(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ return set_bridge_cfg(tx_hold_count, getuint(argv[2]));
+}
+
+static int cmd_settreeprio(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int mstid = get_id(argv[2], "mstid", MAX_MSTID);
+ if(0 > mstid)
+ return mstid;
+ unsigned int prio = getuint(argv[3]);
+ if(prio > 255)
+ prio = 255;
+ return CTL_set_msti_bridge_config(br_index, mstid, prio);
+}
+
+static int cmd_setportpathcost(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int port_index = get_index(argv[2], "port");
+ if(0 > port_index)
+ return port_index;
+ return set_port_cfg(admin_external_port_path_cost, getuint(argv[3]));
+}
+
+static int cmd_setportadminedge(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int port_index = get_index(argv[2], "port");
+ if(0 > port_index)
+ return port_index;
+ return set_port_cfg(admin_edge_port, getyesno(argv[3], "yes", "no"));
+}
+
+static int cmd_setportautoedge(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int port_index = get_index(argv[2], "port");
+ if(0 > port_index)
+ return port_index;
+ return set_port_cfg(auto_edge_port, getyesno(argv[3], "yes", "no"));
+}
+
+static int cmd_setportp2p(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int port_index = get_index(argv[2], "port");
+ if(0 > port_index)
+ return port_index;
+ const char *opts[] = { "no", "yes", "auto", NULL };
+ int vals[] = { p2pForceFalse, p2pForceTrue, p2pAuto };
+ return set_port_cfg(admin_p2p, vals[getenum(argv[3], opts)]);
+}
+
+static int cmd_setportrestrrole(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int port_index = get_index(argv[2], "port");
+ if(0 > port_index)
+ return port_index;
+ return set_port_cfg(restricted_role, getyesno(argv[3], "yes", "no"));
+}
+
+static int cmd_setportrestrtcn(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int port_index = get_index(argv[2], "port");
+ if(0 > port_index)
+ return port_index;
+ return set_port_cfg(restricted_tcn, getyesno(argv[3], "yes", "no"));
+}
+
+static int cmd_settreeportprio(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int port_index = get_index(argv[2], "port");
+ if(0 > port_index)
+ return port_index;
+ int mstid = get_id(argv[3], "mstid", MAX_MSTID);
+ if(0 > mstid)
+ return mstid;
+ unsigned int prio = getuint(argv[4]);
+ if(prio > 255)
+ prio = 255;
+ return set_tree_port_cfg(port_priority, prio);
+}
+
+static int cmd_settreeportcost(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int port_index = get_index(argv[2], "port");
+ if(0 > port_index)
+ return port_index;
+ int mstid = get_id(argv[3], "mstid", MAX_MSTID);
+ if(0 > mstid)
+ return mstid;
+ return set_tree_port_cfg(admin_internal_port_path_cost, getuint(argv[4]));
+}
+
+static int cmd_portmcheck(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int port_index = get_index(argv[2], "port");
+ if(0 > port_index)
+ return port_index;
+ return CTL_port_mcheck(br_index, port_index);
+}
+
+static int cmd_debuglevel(int argc, char *const *argv)
+{
+ return CTL_set_debug_level(getuint(argv[1]));
+}
+
+static int cmd_showmstilist(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int num_mstis = 0, i;
+ __u16 mstids[MAX_IMPLEMENTATION_MSTIS + 1]; /* +1 - for the CIST */
+
+ if(CTL_get_mstilist(br_index, &num_mstis, mstids))
+ return -1;
+
+ printf("%s list of known MSTIs:\n", argv[1]);
+ for(i = 0; i < num_mstis; ++i)
+ printf(" %hu", mstids[i]);
+ printf("\n");
+
+ return 0;
+}
+
+static int cmd_showmstconfid(int argc, char *const *argv)
+{
+ mst_configuration_identifier_t cfgid;
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int i;
+
+ if(CTL_get_mstconfid(br_index, &cfgid))
+ return -1;
+
+ printf("%s MST Configuration Identifier:\n", argv[1]);
+ printf(" Format Selector: %hhu\n", cfgid.s.selector);
+ printf(" Configuration Name: %.*s\n", CONFIGURATION_NAME_LEN,
+ cfgid.s.configuration_name);
+ printf(" Revision Level: %hu\n",
+ __be16_to_cpu(cfgid.s.revision_level));
+ printf(" Configuration Digest: ");
+ for(i = 0; i < CONFIGURATION_DIGEST_LEN; ++i)
+ printf("%02hhX", cfgid.s.configuration_digest[i]);
+ printf("\n");
+
+ return 0;
+}
+
+static int cmd_createtree(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int mstid = get_id(argv[2], "mstid", MAX_MSTID);
+ if(0 > mstid)
+ return mstid;
+ return CTL_create_msti(br_index, mstid);
+}
+
+static int cmd_deletetree(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int mstid = get_id(argv[2], "mstid", MAX_MSTID);
+ if(0 > mstid)
+ return mstid;
+ return CTL_delete_msti(br_index, mstid);
+}
+
+static int cmd_showvid2fid(int argc, char *const *argv)
+{
+ __u16 vid2fid[MAX_VID + 2];
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+
+ if(CTL_get_vids2fids(br_index, vid2fid))
+ return -1;
+
+ printf("%s VID-to-FID allocation table:\n", argv[1]);
+ int i, cur_fid;
+ int interval_count;
+ vid2fid[MAX_VID + 1] = 0xFFFF; /* helps to finalize last interval */
+ do{
+ cur_fid = vid2fid[0];
+ for(i = 1; i <= MAX_VID; ++i)
+ if(cur_fid > vid2fid[i])
+ cur_fid = vid2fid[i];
+ if(cur_fid > MAX_FID)
+ break;
+ printf(" FID %u:", cur_fid);
+ for(i = 0, interval_count = 0; i <= (MAX_VID + 1); ++i)
+ {
+ if(cur_fid != vid2fid[i])
+ {
+ if(interval_count)
+ {
+ printf(" %u", i - interval_count);
+ if(1 < interval_count)
+ printf("-%u", i - 1);
+ interval_count = 0;
+ }
+ continue;
+ }
+ vid2fid[i] = 0xFFFF;
+ ++interval_count;
+ }
+ printf("\n");
+ }while(true);
+
+ return 0;
+}
+
+static int cmd_showfid2mstid(int argc, char *const *argv)
+{
+ __u16 fid2mstid[MAX_FID + 2];
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+
+ if(CTL_get_fids2mstids(br_index, fid2mstid))
+ return -1;
+
+ printf("%s FID-to-MSTID allocation table:\n", argv[1]);
+ int i, cur_mstid;
+ int interval_count;
+ fid2mstid[MAX_FID + 1] = 0xFFFF; /* helps to finalize last interval */
+ do{
+ cur_mstid = fid2mstid[0];
+ for(i = 1; i <= MAX_FID; ++i)
+ if(cur_mstid > fid2mstid[i])
+ cur_mstid = fid2mstid[i];
+ if(cur_mstid > MAX_MSTID)
+ break;
+ printf(" MSTID %u:", cur_mstid);
+ for(i = 0, interval_count = 0; i <= (MAX_FID + 1); ++i)
+ {
+ if(cur_mstid != fid2mstid[i])
+ {
+ if(interval_count)
+ {
+ printf(" %u", i - interval_count);
+ if(1 < interval_count)
+ printf("-%u", i - 1);
+ interval_count = 0;
+ }
+ continue;
+ }
+ fid2mstid[i] = 0xFFFF;
+ ++interval_count;
+ }
+ printf("\n");
+ }while(true);
+
+ return 0;
+}
+
+static int cmd_setvid2fid(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int vid = get_id(argv[2], "VID", MAX_VID);
+ if(0 > vid)
+ return vid;
+ if(0 == vid)
+ {
+ fprintf(stderr, "Bad VID %s\n", argv[2]);
+ return -1;
+ }
+ int fid = get_id(argv[3], "FID", MAX_FID);
+ if(0 > fid)
+ return fid;
+ return CTL_set_vid2fid(br_index, vid, fid);
+}
+
+static int cmd_setfid2mstid(int argc, char *const *argv)
+{
+ int br_index = get_index(argv[1], "bridge");
+ if(0 > br_index)
+ return br_index;
+ int fid = get_id(argv[2], "FID", MAX_FID);
+ if(0 > fid)
+ return fid;
+ int mstid = get_id(argv[3], "mstid", MAX_MSTID);
+ if(0 > mstid)
+ return mstid;
+ return CTL_set_fid2mstid(br_index, fid, mstid);
+}
+
+struct command
+{
+ int nargs;
+ int optargs;
+ const char *name;
+ int (*func) (int argc, char *const *argv);
+ const char *format;
+ const char *help;
+};
+
+static const struct command commands[] =
+{
+ /* Show global bridge */
+ {0, 32, "showbridge", cmd_showbridge,
+ "[<bridge> ... ]", "Show bridge state for the CIST"},
+ {1, 0, "showmstilist", cmd_showmstilist,
+ "<bridge>", "Show list of registered MSTIs"},
+ {1, 0, "showmstconfid", cmd_showmstconfid,
+ "<bridge>", "Show MST ConfigId"},
+ {1, 0, "showvid2fid", cmd_showvid2fid,
+ "<bridge>", "Show VID-to-FID allocation table"},
+ {1, 0, "showfid2mstid", cmd_showfid2mstid,
+ "<bridge>", "Show FID-to-MSTID allocation table"},
+ /* Show global port */
+ {1, 32, "showport", cmd_showport,
+ "<bridge> [<port> ... ]", "Show port state for the CIST"},
+ {1, 32, "showportdetail", cmd_showportdetail,
+ "<bridge> [<port> ... ]", "Show port detailed state for the CIST"},
+ /* Show tree bridge */
+ {2, 0, "showtree", cmd_showtree,
+ "<bridge> <mstid>", "Show bridge state for the given MSTI"},
+ /* Show tree port */
+ {3, 0, "showtreeport", cmd_showtreeport,
+ "<bridge> <port> <mstid>", "Show port detailed state for the given MSTI"},
+
+ /* Set global bridge */
+ {3, 0, "setmstconfid", cmd_setmstconfid,
+ "<bridge> <revision> <name>",
+ "Set MST ConfigId elements: Revision Level (0-65535) and Name"},
+ {3, 0, "setvid2fid", cmd_setvid2fid,
+ "<bridge> <VID> <FID>", "Set VID-to-FID allocation"},
+ {3, 0, "setfid2mstid", cmd_setfid2mstid,
+ "<bridge> <FID> <mstid>", "Set FID-to-MSTID allocation"},
+ {2, 0, "setmaxage", cmd_setbridgemaxage,
+ "<bridge> <max_age>", "Set bridge max age (6-40)"},
+ {2, 0, "setfdelay", cmd_setbridgefdelay,
+ "<bridge> <fwd_delay>", "Set bridge forward delay (4-30)"},
+ {2, 0, "setmaxhops", cmd_setbridgemaxhops,
+ "<bridge> <max_hops>", "Set bridge max hops (6-40)"},
+ {2, 0, "setforcevers", cmd_setbridgeforcevers,
+ "<bridge> {mstp|rstp|stp}", "Force Spanning Tree protocol version"},
+ {2, 0, "settxholdcount", cmd_setbridgetxholdcount,
+ "<bridge> <tx_hold_count>", "Set bridge transmit hold count (1-10)"},
+ /* Set tree bridge */
+ {2, 0, "createtree", cmd_createtree,
+ "<bridge> <mstid>", "Create new MSTI"},
+ {2, 0, "deletetree", cmd_deletetree,
+ "<bridge> <mstid>", "Delete existing MSTI"},
+ {3, 0, "settreeprio", cmd_settreeprio,
+ "<bridge> <mstid> <priority>",
+ "Set bridge priority (0-15) for the given MSTI"},
+ /* Set global port */
+ {3, 0, "setportpathcost", cmd_setportpathcost,
+ "<bridge> <port> <cost>",
+ "Set port external path cost for the CIST (0 = auto)"},
+ {3, 0, "setportadminedge", cmd_setportadminedge,
+ "<bridge> <port> {yes|no}", "Set initial edge state"},
+ {3, 0, "setportautoedge", cmd_setportautoedge,
+ "<bridge> <port> {yes|no}", "Enable auto transition to/from edge state"},
+ {3, 0, "setportp2p", cmd_setportp2p,
+ "<bridge> <port> {yes|no|auto}", "Set p2p detection mode"},
+ {3, 0, "setportrestrrole", cmd_setportrestrrole,
+ "<bridge> <port> {yes|no}", "Restrict port ability to take Root role"},
+ {3, 0, "setportrestrtcn", cmd_setportrestrtcn,
+ "<bridge> <port> {yes|no}",
+ "Restrict port ability to propagate received TCNs"},
+ {2, 0, "portmcheck", cmd_portmcheck,
+ "<bridge> <port>", "Try to get back from STP to rapid (RSTP/MSTP) mode"},
+ /* Set tree port */
+ {4, 0, "settreeportprio", cmd_settreeportprio,
+ "<bridge> <port> <mstid> <priority>",
+ "Set port priority (0-15) for the given MSTI"},
+ {4, 0, "settreeportcost", cmd_settreeportcost,
+ "<bridge> <port> <mstid> <cost>",
+ "Set port internal path cost for the given MSTI (0 = auto)"},
+
+ /* Other */
+ {1, 0, "debuglevel", cmd_debuglevel, "<level>", "Level of verbosity"},
+};
+
+static const struct command *command_lookup(const char *cmd)
+{
+ int i;
+
+ for(i = 0; i < COUNT_OF(commands); ++i)
+ {
+ if(!strcmp(cmd, commands[i].name))
+ return &commands[i];
+ }
+
+ return NULL;
+}
+
+static void command_helpall(void)
+{
+ int i;
+
+ for(i = 0; i < COUNT_OF(commands); ++i)
+ {
+ printf("-%s:\n %-16s %s\n", commands[i].help, commands[i].name,
+ commands[i].format);
+ }
+}
+
+static void help()
+{
+ printf("Usage: mstpctl [commands]\n");
+ printf("commands:\n");
+ command_helpall();
+}
+
+#define PACKAGE_VERSION2(v, b) "mstp, " #v "-" #b
+#define PACKAGE_VERSION(v, b) PACKAGE_VERSION2(v, b)
+
+int main(int argc, char *const *argv)
+{
+ const struct command *cmd;
+ int f;
+ static const struct option options[] =
+ {
+ {.name = "help", .val = 'h'},
+ {.name = "version", .val = 'V'},
+ {0}
+ };
+
+ while(EOF != (f = getopt_long(argc, argv, "Vh", options, NULL)))
+ switch(f)
+ {
+ case 'h':
+ help();
+ return 0;
+ case 'V':
+ printf("%s\n", PACKAGE_VERSION(VERSION, BUILD));
+ return 0;
+ default:
+ fprintf(stderr, "Unknown option '%c'\n", f);
+ goto help;
+ }
+
+ if(argc == optind)
+ goto help;
+
+ if(ctl_client_init())
+ {
+ fprintf(stderr, "can't setup control connection\n");
+ return 1;
+ }
+
+ argc -= optind;
+ argv += optind;
+ if(NULL == (cmd = command_lookup(argv[0])))
+ {
+ fprintf(stderr, "never heard of command [%s]\n", argv[0]);
+ goto help;
+ }
+
+ if(argc < cmd->nargs + 1 || argc > cmd->nargs + cmd->optargs + 1)
+ {
+ printf("Incorrect number of arguments for command\n");
+ printf("Usage: mstpctl %s %s\n %s\n",
+ cmd->name, cmd->format, cmd->help);
+ return 1;
+ }
+
+ return cmd->func(argc, argv);
+
+help:
+ help();
+ return 1;
+}
+
+/* Implementation of client-side functions */
+CLIENT_SIDE_FUNCTION(get_cist_bridge_status)
+CLIENT_SIDE_FUNCTION(get_msti_bridge_status)
+CLIENT_SIDE_FUNCTION(set_cist_bridge_config)
+CLIENT_SIDE_FUNCTION(set_msti_bridge_config)
+CLIENT_SIDE_FUNCTION(get_cist_port_status)
+CLIENT_SIDE_FUNCTION(get_msti_port_status)
+CLIENT_SIDE_FUNCTION(set_cist_port_config)
+CLIENT_SIDE_FUNCTION(set_msti_port_config)
+CLIENT_SIDE_FUNCTION(port_mcheck)
+CLIENT_SIDE_FUNCTION(set_debug_level)
+CLIENT_SIDE_FUNCTION(get_mstilist)
+CLIENT_SIDE_FUNCTION(create_msti)
+CLIENT_SIDE_FUNCTION(delete_msti)
+CLIENT_SIDE_FUNCTION(get_mstconfid)
+CLIENT_SIDE_FUNCTION(set_mstconfid)
+CLIENT_SIDE_FUNCTION(get_vids2fids)
+CLIENT_SIDE_FUNCTION(get_fids2mstids)
+CLIENT_SIDE_FUNCTION(set_vid2fid)
+CLIENT_SIDE_FUNCTION(set_fid2mstid)
+CLIENT_SIDE_FUNCTION(set_vids2fids)
+CLIENT_SIDE_FUNCTION(set_fids2mstids)
+
+/*********************** Logging *********************/
+
+void Dprintf(int level, const char *fmt, ...)
+{
+ char logbuf[LOG_STRING_LEN];
+ logbuf[sizeof(logbuf) - 1] = 0;
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(logbuf, sizeof(logbuf) - 1, fmt, ap);
+ va_end(ap);
+ printf("%s\n", logbuf);
+}
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#include <sys/un.h>
+#include <unistd.h>
+#include <sys/poll.h>
+
+#include "ctl_functions.h"
+#define NO_DAEMON
+#include "log.h"
+
+static int fd = -1;
+
+int ctl_client_init(void)
+{
+ struct sockaddr_un sa_svr;
+ int s;
+ TST(strlen(MSTP_SERVER_SOCK_NAME) < sizeof(sa_svr.sun_path), -1);
+
+ if(0 > (s = socket(PF_UNIX, SOCK_DGRAM, 0)))
+ {
+ ERROR("Couldn't open unix socket: %m");
+ return -1;
+ }
+
+ set_socket_address(&sa_svr, MSTP_SERVER_SOCK_NAME);
+
+ struct sockaddr_un sa;
+ char tmpname[64];
+ sprintf(tmpname, "MSTPCTL_%d", getpid());
+ set_socket_address(&sa, tmpname);
+ /* We need this bind. The autobind on connect isn't working properly.
+ * The server doesn't get a proper sockaddr in recvmsg if we don't do this.
+ */
+ if(0 != bind(s, (struct sockaddr *)&sa, sizeof(sa)))
+ {
+ ERROR("Couldn't bind socket: %m");
+ close(s);
+ return -1;
+ }
+
+ if(0 != connect(s, (struct sockaddr *)&sa_svr, sizeof(sa_svr)))
+ {
+ ERROR("Couldn't connect to server");
+ close(s);
+ return -1;
+ }
+ fd = s;
+
+ return 0;
+}
+
+void ctl_client_cleanup(void)
+{
+ if(fd >= 0)
+ {
+ close(fd);
+ fd = -1;
+ }
+}
+
+int send_ctl_message(int cmd, void *inbuf, int lin, void *outbuf, int lout,
+ LogString *log, int *res)
+{
+ struct ctl_msg_hdr mhdr;
+ struct msghdr msg;
+ struct iovec iov[3];
+ int l;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 3;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+
+ mhdr.cmd = cmd;
+ mhdr.lin = lin;
+ mhdr.lout = lout;
+ mhdr.llog = sizeof(log->buf) - 1;
+ iov[0].iov_base = &mhdr;
+ iov[0].iov_len = sizeof(mhdr);
+ iov[1].iov_base = (void *)inbuf;
+ iov[1].iov_len = lin;
+ iov[2].iov_base = log->buf;
+ iov[2].iov_len = 0;
+
+ l = sendmsg(fd, &msg, 0);
+ if(0 > l)
+ {
+ ERROR("Error sending message to server: %m");
+ return -1;
+ }
+ if(l != sizeof(mhdr) + lin)
+ {
+ ERROR("Error sending message to server: Partial write");
+ return -1;
+ }
+
+ iov[1].iov_base = outbuf;
+ iov[1].iov_len = lout;
+ iov[2].iov_base = log->buf;
+ iov[2].iov_len = sizeof(log->buf);
+
+ struct pollfd pfd;
+ int timeout = 5000; /* 5 s */
+ int r;
+
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ do
+ {
+ if(0 == (r = poll(&pfd, 1, timeout)))
+ {
+ ERROR("Error getting message from server: Timeout");
+ return -1;
+ }
+ if(0 > r)
+ {
+ ERROR("Error getting message from server: poll error: %m");
+ return -1;
+ }
+ }while(0 == (pfd.revents & (POLLERR | POLLHUP | POLLNVAL | POLLIN)));
+
+ l = recvmsg(fd, &msg, 0);
+ if(0 > l)
+ {
+ ERROR("Error getting message from server: %m");
+ return -1;
+ }
+ if((sizeof(mhdr) > l)
+ || (l != sizeof(mhdr) + mhdr.lout + mhdr.llog)
+ || (mhdr.cmd != cmd)
+ )
+ {
+ ERROR("Error getting message from server: Bad format");
+ return -1;
+ }
+ if(mhdr.lout != lout)
+ {
+ ERROR("Error, unexpected result length %d, expected %d\n",
+ mhdr.lout, lout);
+ return -1;
+ }
+ if(sizeof(log->buf) <= mhdr.llog)
+ {
+ ERROR("Invalid log message length %d", mhdr.llog);
+ return -1;
+ }
+ if(res)
+ *res = mhdr.res;
+ log->buf[mhdr.llog] = 0;
+ return 0;
+}
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#ifndef CTL_SOCKET_CLIENT_H
+#define CTL_SOCKET_CLIENT_H
+
+#include "ctl_functions.h"
+
+int send_ctl_message(int cmd, void *inbuf, int lin, void *outbuf, int lout,
+ LogString *log, int *res);
+int ctl_client_init(void);
+void ctl_client_cleanup(void);
+
+#endif /* CTL_SOCKET_CLIENT_H */
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "ctl_socket_client.h"
+#include "epoll_loop.h"
+#include "log.h"
+
+static int server_socket(void)
+{
+ struct sockaddr_un sa;
+ int s;
+
+ TST(strlen(MSTP_SERVER_SOCK_NAME) < sizeof(sa.sun_path), -1);
+
+ if(0 > (s = socket(PF_UNIX, SOCK_DGRAM, 0)))
+ {
+ ERROR("Couldn't open unix socket: %m");
+ return -1;
+ }
+
+ set_socket_address(&sa, MSTP_SERVER_SOCK_NAME);
+
+ if(0 != bind(s, (struct sockaddr *)&sa, sizeof(sa)))
+ {
+ ERROR("Couldn't bind socket: %m");
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+static int handle_message(int cmd, void *inbuf, int lin,
+ void *outbuf, int lout)
+{
+ switch(cmd)
+ {
+ SERVER_MESSAGE_CASE(get_cist_bridge_status);
+ SERVER_MESSAGE_CASE(get_msti_bridge_status);
+ SERVER_MESSAGE_CASE(set_cist_bridge_config);
+ SERVER_MESSAGE_CASE(set_msti_bridge_config);
+ SERVER_MESSAGE_CASE(get_cist_port_status);
+ SERVER_MESSAGE_CASE(get_msti_port_status);
+ SERVER_MESSAGE_CASE(set_cist_port_config);
+ SERVER_MESSAGE_CASE(set_msti_port_config);
+ SERVER_MESSAGE_CASE(port_mcheck);
+ SERVER_MESSAGE_CASE(set_debug_level);
+ SERVER_MESSAGE_CASE(get_mstilist);
+ SERVER_MESSAGE_CASE(create_msti);
+ SERVER_MESSAGE_CASE(delete_msti);
+ SERVER_MESSAGE_CASE(get_mstconfid);
+ SERVER_MESSAGE_CASE(set_mstconfid);
+ SERVER_MESSAGE_CASE(get_vids2fids);
+ SERVER_MESSAGE_CASE(get_fids2mstids);
+ SERVER_MESSAGE_CASE(set_vid2fid);
+ SERVER_MESSAGE_CASE(set_fid2mstid);
+ SERVER_MESSAGE_CASE(set_vids2fids);
+ SERVER_MESSAGE_CASE(set_fids2mstids);
+
+ default:
+ ERROR("CTL: Unknown command %d", cmd);
+ return -1;
+ }
+}
+
+int ctl_in_handler = 0;
+static unsigned char msg_logbuf[LOG_STRING_LEN];
+static unsigned int msg_log_offset;
+void _ctl_err_log(char *fmt, ...)
+{
+ if((sizeof(msg_logbuf) - 1) <= msg_log_offset)
+ return;
+ int r;
+ va_list ap;
+ va_start(ap, fmt);
+ r = vsnprintf((char *)msg_logbuf + msg_log_offset,
+ sizeof(msg_logbuf) - msg_log_offset,
+ fmt, ap);
+ va_end(ap);
+ msg_log_offset += r;
+ if(sizeof(msg_logbuf) <= msg_log_offset)
+ {
+ msg_log_offset = sizeof(msg_logbuf) - 1;
+ msg_logbuf[sizeof(msg_logbuf) - 1] = 0;
+ }
+}
+
+#define MSG_BUF_LEN 10000
+static unsigned char msg_inbuf[MSG_BUF_LEN];
+static unsigned char msg_outbuf[MSG_BUF_LEN];
+
+static void ctl_rcv_handler(uint32_t events, struct epoll_event_handler *p)
+{
+ struct ctl_msg_hdr mhdr;
+ struct msghdr msg;
+ struct sockaddr_un sa;
+ struct iovec iov[3];
+ int l;
+
+ msg.msg_name = &sa;
+ msg.msg_namelen = sizeof(sa);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 3;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ iov[0].iov_base = &mhdr;
+ iov[0].iov_len = sizeof(mhdr);
+ iov[1].iov_base = msg_inbuf;
+ iov[1].iov_len = MSG_BUF_LEN;
+ iov[2].iov_base = NULL;
+ iov[2].iov_len = 0;
+ l = recvmsg(p->fd, &msg, MSG_NOSIGNAL | MSG_DONTWAIT);
+ TST(l > 0,);
+ if((0 != msg.msg_flags) || (sizeof(mhdr) > l)
+ || (l != sizeof(mhdr) + mhdr.lin)
+ || (MSG_BUF_LEN < mhdr.lout)
+ || (0 > mhdr.cmd)
+ )
+ {
+ ERROR("CTL: Unexpected message. Ignoring");
+ return;
+ }
+
+ msg_log_offset = 0;
+ ctl_in_handler = 1;
+
+ mhdr.res = handle_message(mhdr.cmd, msg_inbuf, mhdr.lin,
+ msg_outbuf, mhdr.lout);
+
+ ctl_in_handler = 0;
+ if(0 > mhdr.res)
+ memset(msg_outbuf, 0, mhdr.lout);
+ if(msg_log_offset < mhdr.llog)
+ mhdr.llog = msg_log_offset;
+
+ iov[1].iov_base = msg_outbuf;
+ iov[1].iov_len = mhdr.lout;
+ iov[2].iov_base = msg_logbuf;
+ iov[2].iov_len = mhdr.llog;
+ l = sendmsg(p->fd, &msg, MSG_NOSIGNAL);
+ if(0 > l)
+ ERROR("CTL: Couldn't send response: %m");
+ else if(l != sizeof(mhdr) + mhdr.lout + mhdr.llog)
+ {
+ ERROR
+ ("CTL: Couldn't send full response, sent %d bytes instead of %zd.",
+ l, sizeof(mhdr) + mhdr.lout + mhdr.llog);
+ }
+}
+
+static struct epoll_event_handler ctl_handler = {0};
+
+int ctl_socket_init(void)
+{
+ int s = server_socket();
+ if(0 > s)
+ return -1;
+
+ ctl_handler.fd = s;
+ ctl_handler.handler = ctl_rcv_handler;
+
+ TST(add_epoll(&ctl_handler) == 0, -1);
+ return 0;
+}
+
+void ctl_socket_cleanup(void)
+{
+ remove_epoll(&ctl_handler);
+ close(ctl_handler.fd);
+}
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#ifndef CTL_SOCKET_SERVER_H
+#define CTL_SOCKET_SERVER_H
+
+int ctl_socket_init(void);
+void ctl_socket_cleanup(void);
+
+extern int ctl_in_handler;
+void _ctl_err_log(char *fmt, ...);
+
+#define ctl_err_log(_fmt...) ({ if (ctl_in_handler) _ctl_err_log(_fmt); })
+
+#endif /* CTL_SOCKET_SERVER_H */
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "epoll_loop.h"
+#include "bridge_ctl.h"
+
+/* globals */
+static int epoll_fd = -1;
+static struct timeval nexttimeout;
+
+int init_epoll(void)
+{
+ int r = epoll_create(128);
+ if(r < 0)
+ {
+ fprintf(stderr, "epoll_create failed: %m\n");
+ return -1;
+ }
+ epoll_fd = r;
+ return 0;
+}
+
+int add_epoll(struct epoll_event_handler *h)
+{
+ struct epoll_event ev =
+ {
+ .events = EPOLLIN,
+ .data.ptr = h,
+ };
+ h->ref_ev = NULL;
+ int r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, h->fd, &ev);
+ if(r < 0)
+ {
+ fprintf(stderr, "epoll_ctl_add: %m\n");
+ return -1;
+ }
+ return 0;
+}
+
+int remove_epoll(struct epoll_event_handler *h)
+{
+ int r = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, h->fd, NULL);
+ if(r < 0)
+ {
+ fprintf(stderr, "epoll_ctl_del: %m\n");
+ return -1;
+ }
+ if(h->ref_ev && h->ref_ev->data.ptr == h)
+ {
+ h->ref_ev->data.ptr = NULL;
+ h->ref_ev = NULL;
+ }
+ return 0;
+}
+
+void clear_epoll(void)
+{
+ if(epoll_fd >= 0)
+ close(epoll_fd);
+}
+
+static inline int time_diff(struct timeval *second, struct timeval *first)
+{
+ return (second->tv_sec - first->tv_sec) * 1000
+ + (second->tv_usec - first->tv_usec) / 1000;
+}
+
+static inline void run_timeouts(void)
+{
+ bridge_one_second();
+ ++(nexttimeout.tv_sec);
+}
+
+int epoll_main_loop(void)
+{
+ gettimeofday(&nexttimeout, NULL);
+ ++(nexttimeout.tv_sec);
+#define EV_SIZE 8
+ struct epoll_event ev[EV_SIZE];
+
+ while(1)
+ {
+ int r, i;
+ int timeout;
+
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ timeout = time_diff(&nexttimeout, &tv);
+ if(timeout < 0)
+ {
+ run_timeouts();
+ timeout = 0;
+ }
+
+ r = epoll_wait(epoll_fd, ev, EV_SIZE, timeout);
+ if(r < 0 && errno != EINTR)
+ {
+ fprintf(stderr, "epoll_wait: %m\n");
+ return -1;
+ }
+ for(i = 0; i < r; ++i)
+ {
+ struct epoll_event_handler *p = ev[i].data.ptr;
+ if(p != NULL)
+ p->ref_ev = &ev[i];
+ }
+ for (i = 0; i < r; ++i)
+ {
+ struct epoll_event_handler *p = ev[i].data.ptr;
+ if(p && p->handler)
+ p->handler(ev[i].events, p);
+ }
+ for (i = 0; i < r; ++i)
+ {
+ struct epoll_event_handler *p = ev[i].data.ptr;
+ if(p != NULL)
+ p->ref_ev = NULL;
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#ifndef EPOLL_LOOP_H
+#define EPOLL_LOOP_H
+
+#include <sys/epoll.h>
+#include <errno.h>
+#include <sys/time.h>
+
+struct epoll_event_handler
+{
+ int fd;
+ void *arg;
+ void (*handler) (uint32_t events, struct epoll_event_handler * p);
+ struct epoll_event *ref_ev; /* if set, epoll loop has reference to this,
+ so mark that ref as NULL while freeing */
+};
+
+int init_epoll(void);
+
+void clear_epoll(void);
+
+int epoll_main_loop(void);
+
+int add_epoll(struct epoll_event_handler *h);
+
+int remove_epoll(struct epoll_event_handler *h);
+
+#endif /* EPOLL_LOOP_H */
--- /dev/null
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <asm/types.h>
+
+#include "mstp.h"
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* UINT2 defines a two byte word */
+typedef __u16 UINT2;
+
+/* UINT4 defines a four byte word */
+typedef __u32 UINT4;
+
+/* MD5 context. */
+typedef struct
+{
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD5_CTX;
+
+#define MD5_memcpy(output, input, len) memcpy((output), (input), (len))
+#define MD5_memset(output, value, len) memset((output), (value), (len))
+
+/* Constants for MD5Transform routine.
+ */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static const unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+static void Encode(output, input, len)
+unsigned char *output;
+UINT4 *input;
+unsigned int len;
+{
+ unsigned int i, j;
+
+ for(i = 0, j = 0; j < len; i++, j += 4)
+ {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j + 1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j + 2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j + 3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+static void Decode(output, input, len)
+UINT4 *output;
+unsigned char *input;
+unsigned int len;
+{
+ unsigned int i, j;
+
+ for(i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void MD5Transform(state, block)
+UINT4 state[4];
+unsigned char block[64];
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode(x, block, 64);
+
+ /* Round 1 */
+ FF(a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF(d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF(c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF(b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF(a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF(d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF(c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF(b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF(a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF(d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG(a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG(d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG(b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG(a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG(b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG(a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG(c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+ GG(b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG(d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG(c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH(a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH(d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH(a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH(d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH(c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH(d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH(c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH(b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH(a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH(b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II(a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II(d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II(b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II(d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II(b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II(a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II(c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II(a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II(c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II(b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information. */
+ /* No need in MSTP ;) */
+ /* MD5_memset((POINTER)x, 0, sizeof (x)); */
+}
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+static void MD5Init(context)
+MD5_CTX *context; /* context */
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants. */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+static void MD5Update(context, input, inputLen)
+MD5_CTX *context; /* context */
+unsigned char *input; /* input block */
+unsigned int inputLen; /* length of input block */
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible. */
+ if(inputLen >= partLen)
+ {
+ MD5_memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD5Transform(context->state, context->buffer);
+ for(i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform(context->state, &input[i]);
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ MD5_memcpy((POINTER)&context->buffer[index], (POINTER)&input[i],
+ inputLen - i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+static void MD5Final(digest, context)
+unsigned char digest[16]; /* message digest */
+MD5_CTX *context; /* context */
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode(bits, context->count, 8);
+
+ /* Pad out to 56 mod 64. */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD5Update(context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD5Update(context, bits, 8);
+
+ /* Store state in digest */
+ Encode(digest, context->state, 16);
+
+ /* Zeroize sensitive information. */
+ /* No need in MSTP ;) */
+ /* MD5_memset((POINTER)context, 0, sizeof(*context)); */
+}
+
+/*
+** Function: hmac_md5 from RFC-2104
+*/
+void hmac_md5(text, text_len, key, key_len, digest)
+unsigned char* text; /* pointer to data stream */
+int text_len; /* length of data stream */
+unsigned char* key; /* pointer to authentication key */
+int key_len; /* length of authentication key */
+caddr_t digest; /* caller digest to be filled in */
+{
+ MD5_CTX context;
+ unsigned char k_ipad[65]; /* inner padding -
+ * key XORd with ipad
+ */
+ unsigned char k_opad[65]; /* outer padding -
+ * key XORd with opad
+ */
+ unsigned char tk[16];
+ int i;
+ /* if key is longer than 64 bytes reset it to key=MD5(key) */
+ if(key_len > 64)
+ {
+ MD5_CTX tctx;
+
+ MD5Init(&tctx);
+ MD5Update(&tctx, key, key_len);
+ MD5Final(tk, &tctx);
+
+ key = tk;
+ key_len = 16;
+ }
+
+ /*
+ * the HMAC_MD5 transform looks like:
+ *
+ * MD5(K XOR opad, MD5(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected
+ */
+
+ /* start out by storing key in pads */
+ bzero(k_ipad, sizeof k_ipad);
+ bzero(k_opad, sizeof k_opad);
+ bcopy(key, k_ipad, key_len);
+ bcopy( key, k_opad, key_len);
+
+ /* XOR key with ipad and opad values */
+ for(i = 0; i < 64; ++i)
+ {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+ /*
+ * perform inner MD5
+ */
+ MD5Init(&context); /* init context for 1st
+ * pass */
+ MD5Update(&context, k_ipad, 64); /* start with inner pad */
+ MD5Update(&context, text, text_len); /* then text of datagram */
+ MD5Final(digest, &context); /* finish up 1st pass */
+ /*
+ * perform outer MD5
+ */
+ MD5Init(&context); /* init context for 2nd
+ * pass */
+ MD5Update(&context, k_opad, 64); /* start with outer pad */
+ MD5Update(&context, digest, 16); /* then results of 1st
+ * hash */
+ MD5Final(digest, &context); /* finish up 2nd pass */
+}
+
+#ifdef HMAC_MDS_TEST_FUNCTIONS
+/* Digests a string */
+static void MD5String(string, digest)
+char *string;
+caddr_t digest; /* caller digest to be filled in */
+{
+ MD5_CTX context;
+ unsigned int len = strlen(string);
+
+ MD5Init(&context);
+ MD5Update(&context, string, len);
+ MD5Final(digest, &context);
+}
+
+/* Digests a reference suite of strings */
+bool MD5TestSuite(void)
+{
+ unsigned char digest[16];
+ unsigned char key[16];
+ unsigned char mstp_key[16] = HMAC_KEY;
+ unsigned char data[4096 * 2];
+ int i;
+
+ /* Tests from RFC-1231 */
+ MD5String("", digest);
+ {
+ unsigned char expected_result[16] = {
+ 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
+ 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+ MD5String("a", digest);
+ {
+ unsigned char expected_result[16] = {
+ 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8,
+ 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+ MD5String("abc", digest);
+ {
+ unsigned char expected_result[16] = {
+ 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0,
+ 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+ MD5String("message digest", digest);
+ {
+ unsigned char expected_result[16] = {
+ 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d,
+ 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+ MD5String("abcdefghijklmnopqrstuvwxyz", digest);
+ {
+ unsigned char expected_result[16] = {
+ 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00,
+ 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+ MD5String("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ digest);
+ {
+ unsigned char expected_result[16] = {
+ 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5,
+ 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+ MD5String("1234567890123456789012345678901234567890"
+ "1234567890123456789012345678901234567890", digest);
+ {
+ unsigned char expected_result[16] = {
+ 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55,
+ 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+
+ /* Tests from RFC-2104 */
+ memset(key, 0x0B, 16);
+ hmac_md5("Hi There", 8, key, 16, digest);
+ {
+ unsigned char expected_result[16] = {
+ 0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c,
+ 0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+ hmac_md5("what do ya want for nothing?", 28, "Jefe", 4, digest);
+ {
+ unsigned char expected_result[16] = {
+ 0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03,
+ 0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+ memset(key, 0xAA, 16);
+ memset(data, 0xDD, 50);
+ hmac_md5(data, 50, key, 16, digest);
+ {
+ unsigned char expected_result[16] = {
+ 0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88,
+ 0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+
+ /* Tests from IEEE 802.1Q-2005 13.7 Table 13-2 */
+ memset(data, 0, 4096 * 2);
+ hmac_md5(data, 4096 * 2, mstp_key, 16, digest);
+ {
+ unsigned char expected_result[16] = {
+ 0xac, 0x36, 0x17, 0x7f, 0x50, 0x28, 0x3c, 0xd4,
+ 0xb8, 0x38, 0x21, 0xd8, 0xab, 0x26, 0xde, 0x62};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+ for(i = 3; i < 4095 * 2; i+= 2)
+ data[i] = 1;
+ hmac_md5(data, 4096 * 2, mstp_key, 16, digest);
+ {
+ unsigned char expected_result[16] = {
+ 0xe1, 0x3a, 0x80, 0xf1, 0x1e, 0xd0, 0x85, 0x6a,
+ 0xcd, 0x4e, 0xe3, 0x47, 0x69, 0x41, 0xc7, 0x3b};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+ for(i = 3; i < 4095 * 2; i+= 2)
+ data[i] = (i / 2) % 32 + 1;
+ hmac_md5(data, 4096 * 2, mstp_key, 16, digest);
+ {
+ unsigned char expected_result[16] = {
+ 0x9d, 0x14, 0x5c, 0x26, 0x7d, 0xbe, 0x9f, 0xb5,
+ 0xd8, 0x93, 0x44, 0x1b, 0xe3, 0xba, 0x08, 0xce};
+ if(memcmp(expected_result, digest, 16))
+ return false;
+ }
+
+ return true;
+}
+#endif /* HMAC_MDS_TEST_FUNCTIONS */
--- /dev/null
+/*
+ * libnetlink.c RTnetlink service routines.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/uio.h>
+
+#include "libnetlink.h"
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+ close(rth->fd);
+}
+
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
+ int protocol)
+{
+ socklen_t addr_len;
+ int sndbuf = 32768;
+ int rcvbuf = 32768;
+
+ memset(rth, 0, sizeof(*rth));
+
+ rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+ if (rth->fd < 0) {
+ perror("Cannot open netlink socket");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))
+ < 0) {
+ perror("SO_SNDBUF");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf))
+ < 0) {
+ perror("SO_RCVBUF");
+ return -1;
+ }
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ rth->local.nl_groups = subscriptions;
+
+ if (bind(rth->fd, (struct sockaddr *)&rth->local, sizeof(rth->local)) <
+ 0) {
+ perror("Cannot bind netlink socket");
+ return -1;
+ }
+ addr_len = sizeof(rth->local);
+ if (getsockname(rth->fd, (struct sockaddr *)&rth->local, &addr_len) < 0) {
+ perror("Cannot getsockname");
+ return -1;
+ }
+ if (addr_len != sizeof(rth->local)) {
+ fprintf(stderr, "Wrong address length %d\n", addr_len);
+ return -1;
+ }
+ if (rth->local.nl_family != AF_NETLINK) {
+ fprintf(stderr, "Wrong address family %d\n",
+ rth->local.nl_family);
+ return -1;
+ }
+ rth->seq = time(NULL);
+ return 0;
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+ return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.g.rtgen_family = family;
+
+ return sendto(rth->fd, (void *)&req, sizeof(req), 0,
+ (struct sockaddr *)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_send(struct rtnl_handle *rth, const char *buf, int len)
+{
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ return sendto(rth->fd, buf, len, 0, (struct sockaddr *)&nladdr,
+ sizeof(nladdr));
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+ struct nlmsghdr nlh;
+ struct sockaddr_nl nladdr;
+ struct iovec iov[2] = {
+ {.iov_base = &nlh,.iov_len = sizeof(nlh)}
+ ,
+ {.iov_base = req,.iov_len = len}
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ nlh.nlmsg_len = NLMSG_LENGTH(len);
+ nlh.nlmsg_type = type;
+ nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+ nlh.nlmsg_pid = 0;
+ nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+ return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+ rtnl_filter_t filter,
+ void *arg1, rtnl_filter_t junk, void *arg2)
+{
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ iov.iov_base = buf;
+ while (1) {
+ int status;
+ struct nlmsghdr *h;
+
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rth->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("OVERRUN");
+ continue;
+ }
+
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+
+ h = (struct nlmsghdr *)buf;
+ while (NLMSG_OK(h, status)) {
+ int err;
+
+ if (nladdr.nl_pid != 0 ||
+ h->nlmsg_pid != rth->local.nl_pid ||
+ h->nlmsg_seq != rth->dump) {
+ if (junk) {
+ err = junk(&nladdr, h, arg2);
+ if (err < 0)
+ return err;
+ }
+ goto skip_it;
+ }
+
+ if (h->nlmsg_type == NLMSG_DONE)
+ return 0;
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err =
+ (struct nlmsgerr *)NLMSG_DATA(h);
+ if (h->nlmsg_len <
+ NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ fprintf(stderr, "ERROR truncated\n");
+ } else {
+ errno = -err->error;
+ perror("RTNETLINK answers");
+ }
+ return -1;
+ }
+ err = filter(&nladdr, h, arg1);
+ if (err < 0)
+ return err;
+
+ skip_it:
+ h = NLMSG_NEXT(h, status);
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ return -1;
+ }
+ }
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ rtnl_filter_t junk, void *jarg)
+{
+ int status;
+ unsigned seq;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov = {
+ .iov_base = (void *)n,
+ .iov_len = n->nlmsg_len
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = peer;
+ nladdr.nl_groups = groups;
+
+ n->nlmsg_seq = seq = ++rtnl->seq;
+
+ if (answer == NULL)
+ n->nlmsg_flags |= NLM_F_ACK;
+
+ status = sendmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ perror("Cannot talk to rtnetlink");
+ return -1;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ iov.iov_base = buf;
+
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("OVERRUN");
+ continue;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "sender address length == %d\n",
+ msg.msg_namelen);
+ return -1;
+ }
+ for (h = (struct nlmsghdr *)buf; status >= sizeof(*h);) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l < 0 || len > status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr,
+ "!!!malformed message: len=%d\n", len);
+ return -1;
+ }
+
+ if (nladdr.nl_pid != peer ||
+ h->nlmsg_pid != rtnl->local.nl_pid ||
+ h->nlmsg_seq != seq) {
+ if (junk) {
+ err = junk(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+ /* Don't forget to skip that message. */
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr *)((char *)h +
+ NLMSG_ALIGN(len));
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err =
+ (struct nlmsgerr *)NLMSG_DATA(h);
+ if (l < sizeof(struct nlmsgerr)) {
+ fprintf(stderr, "ERROR truncated\n");
+ } else {
+ errno = -err->error;
+ if (errno == 0) {
+ if (answer)
+ memcpy(answer, h,
+ h->nlmsg_len);
+ return 0;
+ }
+ perror("RTNETLINK answers");
+ }
+ return -1;
+ }
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+
+ fprintf(stderr, "Unexpected reply!!!\n");
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ return -1;
+ }
+ }
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl, rtnl_filter_t handler, void *jarg)
+{
+ int status;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[8192];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ iov.iov_base = buf;
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ return 0;
+ perror("OVERRUN");
+ return -1;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "Sender address length == %d\n",
+ msg.msg_namelen);
+ return -1;
+ }
+ for (h = (struct nlmsghdr *)buf; status >= sizeof(*h);) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l < 0 || len > status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr,
+ "!!!malformed message: len=%d\n", len);
+ return -1;
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0) {
+ fprintf(stderr, "Handler returned %d\n", err);
+ return err;
+ }
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ return -1;
+ }
+ }
+}
+
+int rtnl_from_file(FILE * rtnl, rtnl_filter_t handler, void *jarg)
+{
+ int status;
+ struct sockaddr_nl nladdr;
+ char buf[8192];
+ struct nlmsghdr *h = (void *)buf;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ while (1) {
+ int err, len, type;
+ int l;
+
+ status = fread(&buf, 1, sizeof(*h), rtnl);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status == 0)
+ return 0;
+
+ len = h->nlmsg_len;
+ type = h->nlmsg_type;
+ l = len - sizeof(*h);
+
+ if (l < 0 || len > sizeof(buf)) {
+ fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
+ len, ftell(rtnl));
+ return -1;
+ }
+
+ status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
+
+ if (status < 0) {
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status < l) {
+ fprintf(stderr, "rtnl-from_file: truncated message\n");
+ return -1;
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *rta;
+ if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
+ fprintf(stderr,
+ "addattr32: Error! max allowed bound %d exceeded\n",
+ maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), &data, 4);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data)
+{
+ int len = RTA_LENGTH(1);
+ struct rtattr *rta;
+ if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
+ fprintf(stderr,
+ "addattr8: Error! max allowed bound %d exceeded\n",
+ maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), &data, 1);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
+ int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
+ fprintf(stderr,
+ "addattr_l ERROR: message exceeded bound of %d\n",
+ maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len)
+{
+ if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) {
+ fprintf(stderr,
+ "addraw_l ERROR: message exceeded bound of %d\n",
+ maxlen);
+ return -1;
+ }
+
+ memcpy(NLMSG_TAIL(n), data, len);
+ memset((void *)NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
+ return 0;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *subrta;
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ fprintf(stderr,
+ "rta_addattr32: Error! max allowed bound %d exceeded\n",
+ maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), &data, 4);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
+ const void *data, int alen)
+{
+ struct rtattr *subrta;
+ int len = RTA_LENGTH(alen);
+
+ if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) {
+ fprintf(stderr,
+ "rta_addattr_l: Error! max allowed bound %d exceeded\n",
+ maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max)
+ tb[rta->rta_type] = rta;
+ rta = RTA_NEXT(rta, len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len,
+ rta->rta_len);
+ return 0;
+}
+
+int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len)
+{
+ int i = 0;
+
+ memset(tb, 0, sizeof(struct rtattr *) * max);
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max && i < max)
+ tb[i++] = rta;
+ rta = RTA_NEXT(rta, len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len,
+ rta->rta_len);
+ return i;
+}
--- /dev/null
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+struct rtnl_handle
+{
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ __u32 seq;
+ __u32 dump;
+};
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
+ int protocol);
+void rtnl_close(struct rtnl_handle *rth);
+int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+
+typedef int (*rtnl_filter_t)(const struct sockaddr_nl *, struct nlmsghdr *n,
+ void *);
+int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
+ void *arg1, rtnl_filter_t junk, void *arg2);
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer, rtnl_filter_t junk,
+ void *jarg);
+int rtnl_send(struct rtnl_handle *rth, const char *buf, int);
+
+int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data);
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
+ int alen);
+int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len);
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
+ const void *data, int alen);
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len);
+
+#define parse_rtattr_nested(tb, max, rta) \
+ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
+
+int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler, void *jarg);
+int rtnl_from_file(FILE *, rtnl_filter_t handler, void *jarg);
+
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+#endif /* __LIBNETLINK_H__ */
--- /dev/null
+/**
+ *
+ * I grub it from linux kernel source code and fix it for user space
+ * program. Of course, this is a GPL licensed header file.
+ *
+ * Here is a recipe to cook list.h for user space program
+ *
+ * 1. copy list.h from linux/include/list.h
+ * 2. remove
+ * - #ifdef __KERNE__ and its #endif
+ * - all #include line
+ * - prefetch() and rcu related functions
+ * 3. add macro offsetof() and container_of
+ *
+ * - kazutomo@mcs.anl.gov
+ */
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+/**
+ * @name from other kernel headers
+ */
+/*@{*/
+
+/**
+ * Get offset of a member
+ */
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+/**
+ * Casts a member of a structure out to the containing structure
+ * @param ptr the pointer to the member.
+ * @param type the type of the container struct this is embedded in.
+ * @param member the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+/*@}*/
+
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+
+/**
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+static inline void __list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next)
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
+ pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use as a start point in
+ * list_for_each_entry_continue
+ * @pos: the type * to use as a start point
+ * @head: the head of the list
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_prepare_entry(pos, head, member) \
+ ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - iterate over list of given type
+ * continuing after existing point
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_continue(pos, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue - iterate over list of given type
+ * continuing after existing point safe against removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member) \
+ for (pos = list_entry(pos->member.next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse - iterate backwards over list of given type safe against
+ * removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member), \
+ n = list_entry(pos->member.prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+
+
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+ return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+ return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+ *pprev = next;
+ if (next)
+ next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->next = LIST_POISON1;
+ n->pprev = LIST_POISON2;
+}
+
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+ if (n->pprev) {
+ __hlist_del(n);
+ INIT_HLIST_NODE(n);
+ }
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+ n->pprev = &h->first;
+}
+
+
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ n->pprev = next->pprev;
+ n->next = next;
+ next->pprev = &n->next;
+ *(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ next->next = n->next;
+ n->next = next;
+ next->pprev = &n->next;
+
+ if(next->next)
+ next->next->pprev = &next->next;
+}
+
+
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+ pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+ pos = n)
+
+/**
+ * hlist_for_each_entry - iterate over list of given type
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member) \
+ for (pos = (pos)->next; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member) \
+ for (; pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @n: another &struct hlist_node to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ n = pos->next; 1; }) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = n)
+
+
+#endif
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#ifndef LOG_H
+#define LOG_H
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#define LOG_LEVEL_NONE 0
+#define LOG_LEVEL_ERROR 1
+#define LOG_LEVEL_INFO 2
+#define LOG_LEVEL_DEBUG 3
+#define LOG_LEVEL_STATE_MACHINE_TRANSITION 4
+#define LOG_LEVEL_MAX 100
+
+#define LOG_LEVEL_DEFAULT LOG_LEVEL_INFO
+
+extern void Dprintf(int level, const char *fmt, ...);
+extern void vDprintf(int level, const char *fmt, va_list ap);
+extern int log_level;
+
+#define PRINT(_level, _fmt, _args...) Dprintf(_level, _fmt, ##_args)
+
+#define TSTM(x, y, _fmt, _args...) \
+ do if(!(x)) \
+ { \
+ PRINT(LOG_LEVEL_ERROR, "Error in %s at %s:%d verifying %s. " _fmt, \
+ __PRETTY_FUNCTION__, __FILE__, __LINE__, #x, ##_args); \
+ return y; \
+ } while (0)
+
+#define TST(x, y) TSTM(x, y, "")
+
+#define LOG(_fmt, _args...) \
+ PRINT(LOG_LEVEL_DEBUG, "%s: " _fmt, __PRETTY_FUNCTION__, ##_args)
+
+#define INFO(_fmt, _args...) \
+ PRINT(LOG_LEVEL_INFO, "%s: " _fmt, __PRETTY_FUNCTION__, ##_args)
+
+#ifdef NO_DAEMON
+#define ERROR(_fmt, _args...) \
+ PRINT(LOG_LEVEL_ERROR, "%s: " _fmt, __PRETTY_FUNCTION__, ##_args)
+
+#else
+#include "ctl_socket_server.h"
+#define ERROR(_fmt, _args...) \
+ ({ \
+ PRINT(LOG_LEVEL_ERROR, "error, %s: " _fmt, __PRETTY_FUNCTION__, \
+ ##_args); \
+ ctl_err_log(_fmt "\n", ##_args); \
+ })
+#endif
+
+static inline void dump_hex(void *b, int l)
+{
+ unsigned char *buf = b;
+ char logbuf[80];
+ int i, j;
+ for (i = 0; i < l; i += 16) {
+ for (j = 0; j < 16 && i + j < l; ++j)
+ sprintf(logbuf + j * 3, " %02x", buf[i + j]);
+ PRINT(LOG_LEVEL_INFO, "%s", logbuf);
+ }
+ PRINT(LOG_LEVEL_INFO, "\n");
+}
+
+#endif /* LOG_H */
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+/* #define MISC_TEST_FUNCS */
+
+#include <unistd.h>
+#include <syslog.h>
+
+#include "epoll_loop.h"
+#include "bridge_ctl.h"
+#include "netif_utils.h"
+#include "packet.h"
+#include "log.h"
+#include "mstp.h"
+#include "ctl_socket_server.h"
+
+#define APP_NAME "mstpd"
+
+static int become_daemon = 1;
+static int is_daemon = 0;
+int log_level = LOG_LEVEL_DEFAULT;
+
+#ifdef MISC_TEST_FUNCS
+static bool test_ports_trees_mesh(void);
+#endif /* MISC_TEST_FUNCS */
+
+int main(int argc, char *argv[])
+{
+ int c;
+
+ /* Sanity check */
+ {
+ bridge_identifier_t BridgeIdentifier;
+ mst_configuration_identifier_t MST_ConfigurationIdentifier;
+ TST(sizeof(BridgeIdentifier) == 8, -1);
+ TST(sizeof(BridgeIdentifier.u) == 8, -1);
+ TST(sizeof(BridgeIdentifier.s) == 8, -1);
+ TST(sizeof(BridgeIdentifier.s.priority) == 2, -1);
+ TST(sizeof(BridgeIdentifier.s.mac_address) == 6, -1);
+ TST(sizeof(MST_ConfigurationIdentifier) == 51, -1);
+ TST(sizeof(MST_ConfigurationIdentifier.a) == 51, -1);
+ TST(sizeof(MST_ConfigurationIdentifier.s) == 51, -1);
+#ifdef HMAC_MDS_TEST_FUNCTIONS
+ TST(MD5TestSuite(), -1);
+#endif /* HMAC_MDS_TEST_FUNCTIONS */
+#ifdef MISC_TEST_FUNCS
+ TST(test_ports_trees_mesh(), -1);
+#endif /* MISC_TEST_FUNCS */
+ INFO("Sanity checks succeeded");
+ }
+
+ while((c = getopt(argc, argv, "dv:")) != -1)
+ {
+ switch (c)
+ {
+ case 'd':
+ become_daemon = 0;
+ break;
+ case 'v':
+ {
+ char *end;
+ long l;
+ l = strtoul(optarg, &end, 0);
+ if(*optarg == 0 || *end != 0 || l > LOG_LEVEL_MAX)
+ {
+ ERROR("Invalid loglevel %s", optarg);
+ exit(1);
+ }
+ log_level = l;
+ break;
+ }
+ default:
+ return -1;
+ }
+ }
+
+ if(become_daemon)
+ {
+ FILE *f = fopen("/var/run/"APP_NAME".pid", "w");
+ if(!f)
+ {
+ ERROR("can't open /var/run/"APP_NAME".pid");
+ return -1;
+ }
+ openlog(APP_NAME, 0, LOG_DAEMON);
+ if(daemon(0, 0))
+ {
+ ERROR("can't daemonize");
+ return -1;
+ }
+ is_daemon = 1;
+ fprintf(f, "%d", getpid());
+ fclose(f);
+ }
+
+ TST(init_epoll() == 0, -1);
+ TST(ctl_socket_init() == 0, -1);
+ TST(packet_sock_init() == 0, -1);
+ TST(netsock_init() == 0, -1);
+ TST(init_bridge_ops() == 0, -1);
+
+ return epoll_main_loop();
+}
+
+/*********************** Logging *********************/
+
+#include <stdarg.h>
+#include <time.h>
+
+void vDprintf(int level, const char *fmt, va_list ap)
+{
+ if(level > log_level)
+ return;
+
+ if(!is_daemon)
+ {
+ char logbuf[256];
+ logbuf[255] = 0;
+ time_t clock;
+ struct tm *local_tm;
+ time(&clock);
+ local_tm = localtime(&clock);
+ int l = strftime(logbuf, sizeof(logbuf) - 1, "%F %T ", local_tm);
+ vsnprintf(logbuf + l, sizeof(logbuf) - l - 1, fmt, ap);
+ printf("%s\n", logbuf);
+ }
+ else
+ {
+ vsyslog((level <= LOG_LEVEL_INFO) ? LOG_INFO : LOG_DEBUG, fmt, ap);
+ }
+}
+
+void Dprintf(int level, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vDprintf(level, fmt, ap);
+ va_end(ap);
+}
+
+/*********************** Testing *********************/
+#ifdef MISC_TEST_FUNCS
+
+#include <string.h>
+#include <asm/byteorder.h>
+
+static void printout_mesh(bridge_t *br)
+{
+ tree_t *tree;
+ port_t *prt;
+ per_tree_port_t *ptp;
+
+ printf("Ports:\n");
+ list_for_each_entry(prt, &br->ports, br_list)
+ {
+ printf(" %s(%03hX)", prt->sysdeps.name, __be16_to_cpu(prt->port_number));
+ list_for_each_entry(ptp, &prt->trees, port_list)
+ printf("->%03hX", __be16_to_cpu(ptp->MSTID));
+ printf("\n");
+ }
+
+ printf("\nTrees:\n");
+ list_for_each_entry(tree, &br->trees, bridge_list)
+ {
+ printf(" %03hX", __be16_to_cpu(tree->MSTID));
+ list_for_each_entry(ptp, &tree->ports, tree_list)
+ printf("->%s", ptp->port->sysdeps.name);
+ printf("\n");
+ }
+ printf("\n");
+}
+
+static bool test_ports_trees_mesh(void)
+{
+ bridge_t *br = calloc(1, sizeof(*br));
+ if(!br)
+ return false;
+ strcpy(br->sysdeps.name, "BR_TEST");
+ br->sysdeps.macaddr[5] = 0xED;
+ if(!MSTP_IN_bridge_create(br, br->sysdeps.macaddr))
+ {
+ free(br);
+ return false;
+ }
+
+ port_t *prt[5];
+ int i;
+
+ for(i = 0; i < 5; ++i)
+ {
+ if(!(prt[i] = calloc(1, sizeof(port_t))))
+ return false;
+ prt[i]->bridge = br;
+ }
+
+ if(!MSTP_IN_create_msti(br, 0xF91))
+ {
+error_exit:
+ MSTP_IN_delete_bridge(br);
+ free(br);
+ return false;
+ }
+ if(!MSTP_IN_create_msti(br, 0xE10))
+ goto error_exit;
+
+ strcpy(prt[0]->sysdeps.name, "PRT_10C");
+ if(!MSTP_IN_port_create_and_add_tail(prt[0], 0x10C))
+ goto error_exit;
+
+ strcpy(prt[1]->sysdeps.name, "PRT_001");
+ if(!MSTP_IN_port_create_and_add_tail(prt[1], 0x001))
+ goto error_exit;
+
+ strcpy(prt[2]->sysdeps.name, "PRT_C01");
+ if(!MSTP_IN_port_create_and_add_tail(prt[2], 0xC01))
+ goto error_exit;
+
+ if(!MSTP_IN_create_msti(br, 0xE12))
+ goto error_exit;
+ if(!MSTP_IN_create_msti(br, 0x001))
+ goto error_exit;
+
+ strcpy(prt[3]->sysdeps.name, "PRT_002");
+ if(!MSTP_IN_port_create_and_add_tail(prt[3], 0x002))
+ goto error_exit;
+ strcpy(prt[4]->sysdeps.name, "PRT_003");
+ if(!MSTP_IN_port_create_and_add_tail(prt[4], 0x003))
+ goto error_exit;
+
+ if(!MSTP_IN_create_msti(br, 0x005))
+ goto error_exit;
+
+ printout_mesh(br);
+
+ list_del(&prt[1]->br_list);
+ MSTP_IN_delete_port(prt[1]);
+ if(!MSTP_IN_delete_msti(br, 0xE12))
+ goto error_exit;
+ list_del(&prt[3]->br_list);
+ MSTP_IN_delete_port(prt[3]);
+ if(!MSTP_IN_delete_msti(br, 0x005))
+ goto error_exit;
+
+ printout_mesh(br);
+
+ if(!MSTP_IN_create_msti(br, 0x102))
+ goto error_exit;
+ strcpy(prt[1]->sysdeps.name, "PRT_504");
+ if(!MSTP_IN_port_create_and_add_tail(prt[1], 0x504))
+ goto error_exit;
+
+ if(!MSTP_IN_create_msti(br, 0x105))
+ goto error_exit;
+ strcpy(prt[3]->sysdeps.name, "PRT_777");
+ if(!MSTP_IN_port_create_and_add_tail(prt[3], 0x777))
+ goto error_exit;
+
+ printout_mesh(br);
+
+ MSTP_IN_delete_bridge(br);
+ free(br);
+ return true;
+}
+#endif /* MISC_TEST_FUNCS */
--- /dev/null
+/*
+ * mstp.c State machines from IEEE 802.1Q-2005
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+ */
+
+/* NOTE: The standard messes up Hello_Time timer management.
+ * The portTimes and msgTimes structures have it, while
+ * designatedTimes, rootTimes and BridgeTimes do not!
+ * And there are places, where standard says:
+ * "portTimes = designatedTimes" (13.32)
+ * "rootTimes = portTimes" (13.26.23)
+ * ---- Bad IEEE! ----
+ * For now I decide: All structures will hold Hello_Time,
+ * because in 802.1D they do.
+ * Maybe 802.1Q-2011 clarifies this, but I don't have the text.
+ */
+
+/* 802.1Q-2005 does not define but widely use variable name newInfoXst.
+ * From the 802.1s I can guess that it means:
+ * - "newInfo" when tree is CIST;
+ * - "newInfoMsti" when tree is not CIST (is MSTI).
+ * But that is only a guess and I could be wrong here ;)
+ */
+
+#include <string.h>
+#include <linux/if_bridge.h>
+#include <asm/byteorder.h>
+
+#include "mstp.h"
+#include "log.h"
+
+static void PTSM_tick(port_t *prt);
+static void TCSM_run(per_tree_port_t *ptp);
+static void BDSM_begin(port_t *prt);
+static void br_state_machines_begin(bridge_t *br);
+static void prt_state_machines_begin(port_t *prt);
+static void tree_state_machines_begin(tree_t *tree);
+static void br_state_machines_run(bridge_t *br);
+
+#define FOREACH_PORT_IN_BRIDGE(port, bridge) \
+ list_for_each_entry((port), &(bridge)->ports, br_list)
+#define FOREACH_TREE_IN_BRIDGE(tree, bridge) \
+ list_for_each_entry((tree), &(bridge)->trees, bridge_list)
+#define FOREACH_PTP_IN_TREE(ptp, tree) \
+ list_for_each_entry((ptp), &(tree)->ports, tree_list)
+#define FOREACH_PTP_IN_PORT(ptp, port) \
+ list_for_each_entry((ptp), &(port)->trees, port_list)
+
+/* 17.20.11 of 802.1D */
+#define rstpVersion(br) ((br)->ForceProtocolVersion >= protoRSTP)
+
+/*
+ * Recalculate configuration digest. (13.7)
+ */
+static void RecalcConfigDigest(bridge_t *br)
+{
+ __be16 vid2mstid[MAX_VID + 2];
+ unsigned char mstp_key[] = HMAC_KEY;
+ int vid;
+
+ vid2mstid[0] = vid2mstid[MAX_VID + 1] = 0;
+ for(vid = 1; vid <= MAX_VID; ++vid)
+ vid2mstid[vid] = br->fid2mstid[br->vid2fid[vid]];
+
+ hmac_md5((void *)vid2mstid, sizeof(vid2mstid), mstp_key, sizeof(mstp_key),
+ br->MstConfigId.s.configuration_digest);
+}
+
+/*
+ * 13.37.1 - Table 13-3
+ */
+static __u32 compute_pcost(int speed)
+{
+ /* speed is in MB/s*/
+ if(speed > 0)
+ return (speed < 20000000) ? 20000000 / speed : 1;
+ else
+ return MAX_PATH_COST;
+}
+
+static tree_t * create_tree(bridge_t *br, __u8 *macaddr, __be16 MSTID)
+{
+ /* Initialize all fields except anchor */
+ tree_t *tree = calloc(1, sizeof(*tree));
+ if(!tree)
+ {
+ ERROR_BRNAME(br, "Out of memory");
+ return NULL;
+ }
+ tree->bridge = br;
+ tree->MSTID = MSTID;
+ INIT_LIST_HEAD(&tree->ports);
+
+ memcpy(tree->BridgeIdentifier.s.mac_address, macaddr, ETH_ALEN);
+ /* 0x8000 = default bridge priority (17.14 of 802.1D) */
+ tree->BridgeIdentifier.s.priority = __constant_cpu_to_be16(0x8000) | MSTID;
+ assign(tree->BridgePriority.RootID, tree->BridgeIdentifier);
+ assign(tree->BridgePriority.RRootID, tree->BridgeIdentifier);
+ assign(tree->BridgePriority.DesignatedBridgeID, tree->BridgeIdentifier);
+ assign(tree->BridgeTimes.remainingHops, br->MaxHops); /* 13.23.4 */
+ assign(tree->BridgeTimes.Forward_Delay, br->Forward_Delay); /* 13.23.4 */
+ assign(tree->BridgeTimes.Max_Age, br->Max_Age); /* 13.23.4 */
+ /* 13.23.4 */
+ assign(tree->BridgeTimes.Message_Age, __constant_cpu_to_be16(0));
+ /* 17.14 of 802.1D */
+ assign(tree->BridgeTimes.Hello_Time, __constant_cpu_to_be16(2));
+
+ /* 12.8.1.1.3.(b,c,d) */
+ tree->time_since_topology_change = 0;
+ tree->topology_change_count = 0;
+ tree->topology_change = false; /* since all tcWhile are initialized to 0 */
+
+ /* The following are initialized in BEGIN state:
+ * - rootPortId, rootPriority, rootTimes: in Port Role Selection SM
+ */
+ return tree;
+}
+
+static per_tree_port_t * create_ptp(tree_t *tree, port_t *prt)
+{
+ /* Initialize all fields except anchors */
+ per_tree_port_t *ptp = calloc(1, sizeof(*ptp));
+ if(!ptp)
+ {
+ ERROR_PRTNAME(prt->bridge, prt, "Out of memory");
+ return NULL;
+ }
+ ptp->port = prt;
+ ptp->tree = tree;
+ ptp->MSTID = tree->MSTID;
+
+ ptp->state = BR_STATE_DISABLED;
+ assign(ptp->rcvdTc, boolFalse);
+ assign(ptp->tcProp, boolFalse);
+ assign(ptp->updtInfo, boolFalse);
+ /* 0x80 = default port priority (17.14 of 802.1D) */
+ ptp->portId = __constant_cpu_to_be16(0x8000) | prt->port_number;
+ assign(ptp->master, boolFalse); /* 13.24.5 */
+ assign(ptp->AdminInternalPortPathCost, 0u);
+ assign(ptp->InternalPortPathCost, compute_pcost(GET_PORT_SPEED(prt)));
+ /* 802.1Q leaves portPriority and portTimes uninitialized */
+ assign(ptp->portPriority, tree->BridgePriority);
+ assign(ptp->portTimes, tree->BridgeTimes);
+
+ /* The following are initialized in BEGIN state:
+ * - rcvdMsg: in Port Receive SM
+ * - fdWhile, rrWhile, rbWhile, role, learn, forward,
+ * sync, synced, reRoot: in Port Role Transitions SM
+ * - tcWhile, fdbFlush: Topology Change SM
+ * - rcvdInfoWhile, proposed, proposing, agree, agreed,
+ * infoIs, reselect, selected: Port Information SM
+ * - forwarding, learning: Port State Transition SM
+ * - selectedRole, designatedPriority, designatedTimes: Port Role Selection SM
+ */
+
+ /* The following are not initialized (set to zero thanks to calloc):
+ * - disputed
+ * - rcvdInfo
+ * - mastered
+ * - msgPriority
+ * - msgTimes
+ */
+ return ptp;
+}
+
+/* External events */
+
+bool MSTP_IN_bridge_create(bridge_t *br, __u8 *macaddr)
+{
+ tree_t *cist;
+
+ /* Initialize all fields except sysdeps and anchor */
+ INIT_LIST_HEAD(&br->ports);
+ INIT_LIST_HEAD(&br->trees);
+ assign(br->bridgeEnabled, boolFalse);
+ memset(br->vid2fid, 0, sizeof(br->vid2fid));
+ memset(br->fid2mstid, 0, sizeof(br->fid2mstid));
+ assign(br->MstConfigId.s.selector, (__u8)0);
+ sprintf(br->MstConfigId.s.configuration_name,
+ "%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
+ macaddr[0], macaddr[1], macaddr[2],
+ macaddr[3], macaddr[4], macaddr[5]);
+ assign(br->MstConfigId.s.revision_level, __constant_cpu_to_be16(0));
+ RecalcConfigDigest(br); /* set br->MstConfigId.s.configuration_digest */
+ assign(br->MaxHops, (__u8)20); /* 13.37.3 */
+ assign(br->ForceProtocolVersion, protoMSTP);
+ assign(br->Forward_Delay, __constant_cpu_to_be16(15)); /* 17.14 of 802.1D */
+ assign(br->Max_Age, __constant_cpu_to_be16(20)); /* 17.14 of 802.1D */
+ assign(br->Transmit_Hold_Count, 6u); /* 17.14 of 802.1D */
+ assign(br->Migrate_Time, 3u); /* 17.14 of 802.1D */
+ assign(br->rapidAgeingWhile, 0u);
+
+ br->uptime = 0;
+
+ /* Create CIST */
+ if(!(cist = create_tree(br, macaddr, 0)))
+ return false;
+ list_add_tail(&cist->bridge_list, &br->trees);
+
+ br_state_machines_begin(br);
+ return true;
+}
+
+bool MSTP_IN_port_create_and_add_tail(port_t *prt, __u16 portno)
+{
+ tree_t *tree;
+ per_tree_port_t *ptp, *nxt;
+ bridge_t *br = prt->bridge;
+
+ /* Initialize all fields except sysdeps and bridge */
+ INIT_LIST_HEAD(&prt->trees);
+ prt->port_number = __cpu_to_be16(portno);
+
+ assign(prt->AdminExternalPortPathCost, 0u);
+ /* Default for operP2P is false because by default AdminP2P
+ * says to auto-detect p2p state, and it is derived from duplex
+ * and initially port is in down state and in this down state
+ * duplex is set to false (half) */
+ prt->AdminP2P = p2pAuto;
+ assign(prt->operPointToPointMAC, boolFalse);
+ assign(prt->portEnabled, boolFalse);
+ assign(prt->infoInternal, boolFalse);
+ assign(prt->rcvdInternal, boolFalse);
+ assign(prt->rcvdTcAck, boolFalse);
+ assign(prt->rcvdTcn, boolFalse);
+ assign(prt->restrictedRole, boolFalse); /* 13.25.14 */
+ assign(prt->restrictedTcn, boolFalse); /* 13.25.15 */
+ assign(prt->ExternalPortPathCost, MAX_PATH_COST); /* 13.37.1 */
+ assign(prt->AdminEdgePort, boolFalse); /* 13.25 */
+ assign(prt->AutoEdge, boolTrue); /* 13.25 */
+
+ /* The following are initialized in BEGIN state:
+ * - mdelayWhile. mcheck, sendRSTP: in Port Protocol Migration SM
+ * - helloWhen, newInfo, newInfoMsti, txCount: in Port Transmit SM
+ * - edgeDelayWhile, rcvdBpdu, rcvdRSTP, rcvdSTP : in Port Receive SM
+ * - operEdge: in Bridge Detection SM
+ * - tcAck: in Topology Change SM
+ */
+
+ /* Create PerTreePort structures for all existing trees */
+ FOREACH_TREE_IN_BRIDGE(tree, br)
+ {
+ if(!(ptp = create_ptp(tree, prt)))
+ {
+ /* Remove and free all previously created entries in port's list */
+ list_for_each_entry_safe(ptp, nxt, &prt->trees, port_list)
+ {
+ list_del(&ptp->port_list);
+ list_del(&ptp->tree_list);
+ free(ptp);
+ }
+ return false;
+ }
+ list_add_tail(&ptp->port_list, &prt->trees);
+ list_add_tail(&ptp->tree_list, &tree->ports);
+ }
+
+ /* Add new port to the tail of the list in the bridge */
+ /* NOTE: if one wants add port NOT to the tail of the list of ports,
+ * one should revise above loop (FOREACH_TREE_IN_BRIDGE)
+ * because it heavily depends on the fact that port is added to the tail.
+ */
+ list_add_tail(&prt->br_list, &br->ports);
+
+ prt_state_machines_begin(prt);
+ return true;
+}
+
+void MSTP_IN_delete_port(port_t *prt)
+{
+ per_tree_port_t *ptp, *nxt;
+ bridge_t *br = prt->bridge;
+
+ if(prt->portEnabled)
+ {
+ assign(prt->portEnabled, boolFalse);
+ br_state_machines_run(br);
+ }
+
+ list_for_each_entry_safe(ptp, nxt, &prt->trees, port_list)
+ {
+ list_del(&ptp->port_list);
+ list_del(&ptp->tree_list);
+ free(ptp);
+ }
+
+ br_state_machines_run(br);
+}
+
+void MSTP_IN_delete_bridge(bridge_t *br)
+{
+ tree_t *tree, *nxt_tree;
+ port_t *prt, *nxt_prt;
+
+ assign(br->bridgeEnabled, boolFalse);
+
+ /* We SHOULD first delete all ports and only THEN delete all tree_t
+ * structures as the tree_t structure contains the head for the per-port
+ * list of tree data (tree_t.ports).
+ * If this list_head will be deleted before all the per_tree_ports
+ * bad things will happen ;)
+ */
+
+ list_for_each_entry_safe(prt, nxt_prt, &br->ports, br_list)
+ {
+ list_del(&prt->br_list);
+ MSTP_IN_delete_port(prt);
+ free(prt);
+ }
+
+ list_for_each_entry_safe(tree, nxt_tree, &br->trees, bridge_list)
+ {
+ list_del(&tree->bridge_list);
+ free(tree);
+ }
+}
+
+void MSTP_IN_set_bridge_address(bridge_t *br, __u8 *macaddr)
+{
+ tree_t *tree;
+ bool changed = false;
+
+ FOREACH_TREE_IN_BRIDGE(tree, br)
+ {
+ if(0 == memcmp(tree->BridgeIdentifier.s.mac_address, macaddr, ETH_ALEN))
+ continue;
+ changed = true;
+ memcpy(tree->BridgeIdentifier.s.mac_address, macaddr, ETH_ALEN);
+ tree->BridgePriority.RootID = tree->BridgePriority.RRootID =
+ tree->BridgePriority.DesignatedBridgeID = tree->BridgeIdentifier;
+ }
+
+ if(changed)
+ br_state_machines_begin(br);
+}
+
+void MSTP_IN_set_bridge_enable(bridge_t *br, bool up)
+{
+ if(br->bridgeEnabled == up)
+ return;
+ assign(br->bridgeEnabled, up);
+ br_state_machines_begin(br);
+}
+
+void MSTP_IN_set_port_enable(port_t *prt, bool up, int speed, int duplex)
+{
+ __u32 computed_pcost, new_ExternalPathCost, new_InternalPathCost;
+ per_tree_port_t *ptp;
+ bool new_p2p;
+ bool changed = false;
+
+ if(up)
+ {
+ computed_pcost = compute_pcost(speed);
+ new_ExternalPathCost = (0 == prt->AdminExternalPortPathCost) ?
+ computed_pcost
+ : prt->AdminExternalPortPathCost;
+ if(prt->ExternalPortPathCost != new_ExternalPathCost)
+ {
+ assign(prt->ExternalPortPathCost, new_ExternalPathCost);
+ changed = true;
+ }
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ {
+ new_InternalPathCost = (0 == ptp->AdminInternalPortPathCost) ?
+ computed_pcost
+ : ptp->AdminInternalPortPathCost;
+ if(ptp->InternalPortPathCost != new_InternalPathCost)
+ {
+ assign(ptp->InternalPortPathCost, new_InternalPathCost);
+ changed = true;
+ }
+ }
+
+ switch(prt->AdminP2P)
+ {
+ case p2pForceTrue:
+ new_p2p = true;
+ break;
+ case p2pForceFalse:
+ new_p2p = false;
+ break;
+ case p2pAuto:
+ default:
+ new_p2p = !!duplex;
+ break;
+ }
+ if(prt->operPointToPointMAC != new_p2p)
+ {
+ assign(prt->operPointToPointMAC, new_p2p);
+ changed = true;
+ }
+
+ if(!prt->portEnabled)
+ {
+ assign(prt->portEnabled, boolTrue);
+ changed = true;
+ }
+ }
+ else
+ {
+ if(prt->portEnabled)
+ {
+ assign(prt->portEnabled, boolFalse);
+ changed = true;
+ }
+ }
+
+ if(changed)
+ br_state_machines_run(prt->bridge);
+}
+
+void MSTP_IN_one_second(bridge_t *br)
+{
+ port_t *prt;
+ tree_t *tree;
+
+ ++(br->uptime);
+
+ if(!br->bridgeEnabled)
+ return;
+
+ FOREACH_TREE_IN_BRIDGE(tree, br)
+ if(!(tree->topology_change))
+ ++(tree->time_since_topology_change);
+
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ PTSM_tick(prt);
+ /* support for rapid ageing */
+ if(br->rapidAgeingWhile)
+ {
+ if((--(br->rapidAgeingWhile)) == 0)
+ MSTP_OUT_set_ageing_time(br, -1);
+ }
+
+ br_state_machines_run(br);
+}
+
+void MSTP_IN_all_fids_flushed(per_tree_port_t *ptp)
+{
+ bridge_t *br = ptp->port->bridge;
+ if(!br->bridgeEnabled)
+ return;
+ assign(ptp->fdbFlush, boolFalse);
+ TCSM_run(ptp);
+ br_state_machines_run(br);
+}
+
+/* NOTE: bpdu pointer is unaligned, but it works because
+ * bpdu_t is packed. Don't try to cast bpdu to non-packed type ;)
+ */
+void MSTP_IN_rx_bpdu(port_t *prt, bpdu_t *bpdu, int size)
+{
+ int mstis_size;
+ bridge_t *br = prt->bridge;
+
+ if(!br->bridgeEnabled)
+ {
+ INFO_PRTNAME(br, prt, "Received BPDU while bridge is disabled");
+ return;
+ }
+
+ if(prt->rcvdBpdu)
+ {
+ ERROR_PRTNAME(br, prt, "Port hasn't processed previous BPDU");
+ return;
+ }
+
+ /* 14.4 Validation */
+ if((TCN_BPDU_SIZE > size) || (0 != bpdu->protocolIdentifier))
+ {
+bpdu_validation_failed:
+ INFO_PRTNAME(br, prt, "BPDU validation failed");
+ return;
+ }
+ switch(bpdu->bpduType)
+ {
+ case bpduTypeTCN:
+ /* 14.4.b) */
+ /* Valid TCN BPDU */
+ bpdu->protocolVersion = protoSTP;
+ LOG_PRTNAME(br, prt, "received TCN BPDU");
+ break;
+ case bpduTypeConfig:
+ /* 14.4.a) */
+ if(CONFIG_BPDU_SIZE > size)
+ goto bpdu_validation_failed;
+ /* Valid Config BPDU */
+ bpdu->protocolVersion = protoSTP;
+ LOG_PRTNAME(br, prt, "received Config BPDU");
+ break;
+ case bpduTypeRST:
+ if(protoRSTP == bpdu->protocolVersion)
+ { /* 14.4.c) */
+ if(RST_BPDU_SIZE > size)
+ goto bpdu_validation_failed;
+ /* Valid RST BPDU */
+ /* bpdu->protocolVersion = protoRSTP; */
+ LOG_PRTNAME(br, prt, "received RST BPDU");
+ break;
+ }
+ if(protoMSTP > bpdu->protocolVersion)
+ goto bpdu_validation_failed;
+ /* Yes, 802.1Q-2005 says here to check if it contains
+ * "35 or more octets", not 36! (see 14.4.d).1) )
+ * That's why I check size against CONFIG_BPDU_SIZE
+ * and not RST_BPDU_SIZE.
+ */
+ if(CONFIG_BPDU_SIZE > size)
+ goto bpdu_validation_failed;
+ mstis_size = __be16_to_cpu(bpdu->version3_len)
+ - MST_BPDU_VER3LEN_WO_MSTI_MSGS;
+ if((MST_BPDU_SIZE_WO_MSTI_MSGS > size) || (0 != bpdu->version1_len)
+ || (0 > mstis_size)
+ || ((MAX_STANDARD_MSTIS * sizeof(msti_configuration_message_t))
+ < mstis_size)
+ || (0 != (mstis_size % sizeof(msti_configuration_message_t)))
+ )
+ { /* 14.4.d) */
+ /* Valid RST BPDU */
+ bpdu->protocolVersion = protoRSTP;
+ LOG_PRTNAME(br, prt, "received RST BPDU");
+ break;
+ }
+ /* 14.4.e) */
+ /* Valid MST BPDU */
+ bpdu->protocolVersion = protoMSTP;
+ prt->rcvdBpduNumOfMstis = mstis_size
+ / sizeof(msti_configuration_message_t);
+ LOG_PRTNAME(br, prt, "received MST BPDU with %d MSTIs",
+ prt->rcvdBpduNumOfMstis);
+ break;
+ default:
+ goto bpdu_validation_failed;
+ }
+
+ assign(prt->rcvdBpduData, *bpdu);
+ assign(prt->rcvdBpdu, boolTrue);
+
+ br_state_machines_run(br);
+}
+
+/* 12.8.1.1 Read CIST Bridge Protocol Parameters */
+void MSTP_IN_get_cist_bridge_status(bridge_t *br, CIST_BridgeStatus *status)
+{
+ tree_t *cist = GET_CIST_TREE(br);
+ assign(status->bridge_id, cist->BridgeIdentifier);
+ assign(status->time_since_topology_change,
+ cist->time_since_topology_change);
+ assign(status->topology_change_count, cist->topology_change_count);
+ assign(status->topology_change, cist->topology_change);
+ assign(status->designated_root, cist->rootPriority.RootID);
+ assign(status->root_path_cost,
+ __be32_to_cpu(cist->rootPriority.ExtRootPathCost));
+ assign(status->regional_root, cist->rootPriority.RRootID);
+ assign(status->internal_path_cost,
+ __be32_to_cpu(cist->rootPriority.IntRootPathCost));
+ assign(status->root_port_id, cist->rootPortId);
+ status->root_max_age = __be16_to_cpu(cist->rootTimes.Max_Age);
+ status->root_forward_delay = __be16_to_cpu(cist->rootTimes.Forward_Delay);
+ status->bridge_max_age = __be16_to_cpu(br->Max_Age);
+ status->bridge_forward_delay = __be16_to_cpu(br->Forward_Delay);
+ status->max_hops = br->MaxHops;
+ assign(status->tx_hold_count, br->Transmit_Hold_Count);
+ assign(status->protocol_version, br->ForceProtocolVersion);
+ assign(status->enabled, br->bridgeEnabled);
+}
+
+/* 12.8.1.2 Read MSTI Bridge Protocol Parameters */
+void MSTP_IN_get_msti_bridge_status(tree_t *tree, MSTI_BridgeStatus *status)
+{
+ assign(status->bridge_id, tree->BridgeIdentifier);
+ assign(status->time_since_topology_change,
+ tree->time_since_topology_change);
+ assign(status->topology_change_count, tree->topology_change_count);
+ assign(status->topology_change, tree->topology_change);
+ assign(status->regional_root, tree->rootPriority.RRootID);
+ assign(status->internal_path_cost,
+ __be32_to_cpu(tree->rootPriority.IntRootPathCost));
+ assign(status->root_port_id, tree->rootPortId);
+}
+
+/* 12.8.1.3 Set CIST Bridge Protocol Parameters */
+int MSTP_IN_set_cist_bridge_config(bridge_t *br, CIST_BridgeConfig *cfg)
+{
+ bool changed, changedBridgeTimes, init;
+ int r = 0;
+ unsigned int new_forward_delay, new_max_age;
+ __be16 valueMaxAge, valueForwardDelay;
+ tree_t *tree;
+ port_t *prt;
+ per_tree_port_t *ptp;
+
+ /* Firstly, validation */
+ if(cfg->set_bridge_max_age)
+ {
+ new_max_age = cfg->bridge_max_age;
+ if((6 > new_max_age) || (40 < new_max_age))
+ {
+ ERROR_BRNAME(br, "Bridge Max Age must be between 6 and 40");
+ r = -1;
+ }
+ }
+ else
+ new_max_age = __be16_to_cpu(br->Max_Age);
+
+ if(cfg->set_bridge_forward_delay)
+ {
+ new_forward_delay = cfg->bridge_forward_delay;
+ if((4 > new_forward_delay) || (30 < new_forward_delay))
+ {
+ ERROR_BRNAME(br, "Bridge Forward Delay must be between 4 and 30");
+ r = -1;
+ }
+ }
+ else
+ new_forward_delay = __be16_to_cpu(br->Forward_Delay);
+
+ if(cfg->set_bridge_max_age || cfg->set_bridge_forward_delay)
+ {
+ if((2 * (new_forward_delay - 1)) < new_max_age)
+ {
+ ERROR_BRNAME(br, "Configured Bridge Times don't meet "
+ "2 * (Bridge Foward Delay - 1) >= Bridge Max Age");
+ r = -1;
+ }
+ }
+
+ if(cfg->set_protocol_version)
+ {
+ switch(cfg->protocol_version)
+ {
+ case protoSTP:
+ case protoRSTP:
+ case protoMSTP:
+ break;
+ default:
+ ERROR_BRNAME(br, "Bad protocol version (%d)",
+ cfg->protocol_version);
+ r = -1;
+ }
+ }
+
+ if(cfg->set_tx_hold_count)
+ {
+ if((1 > cfg->tx_hold_count) || (10 < cfg->tx_hold_count))
+ {
+ ERROR_BRNAME(br, "Transmit Hold Count must be between 1 and 10\n");
+ r = -1;
+ }
+ }
+
+ if(cfg->set_max_hops)
+ {
+ if((6 > cfg->max_hops) || (40 < cfg->max_hops))
+ {
+ ERROR_BRNAME(br, "Bridge Max Hops must be between 6 and 40");
+ r = -1;
+ }
+ }
+
+ if(r)
+ return r;
+
+ /* Secondly, do set */
+ changed = changedBridgeTimes = init = false;
+
+ if(cfg->set_bridge_max_age || cfg->set_bridge_forward_delay)
+ {
+ valueMaxAge = __cpu_to_be16(new_max_age);
+ valueForwardDelay = __cpu_to_be16(new_forward_delay);
+ if(cmp(valueMaxAge, !=, br->Max_Age)
+ || cmp(valueForwardDelay, !=, br->Forward_Delay)
+ )
+ {
+ assign(br->Max_Age, valueMaxAge);
+ assign(br->Forward_Delay, valueForwardDelay);
+ changed = changedBridgeTimes = true;
+ }
+ }
+
+ if((cfg->set_protocol_version)
+ && (cfg->protocol_version != br->ForceProtocolVersion)
+ )
+ {
+ br->ForceProtocolVersion = cfg->protocol_version;
+ changed = init = true;
+ }
+
+ if(cfg->set_tx_hold_count)
+ {
+ if(cfg->tx_hold_count != br->Transmit_Hold_Count)
+ {
+ assign(br->Transmit_Hold_Count, cfg->tx_hold_count);
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ assign(prt->txCount, 0u);
+ changed = true;
+ }
+ }
+
+ if(cfg->set_max_hops)
+ {
+ if(cfg->max_hops != br->MaxHops)
+ {
+ assign(br->MaxHops, cfg->max_hops);
+ changed = changedBridgeTimes = true;
+ }
+ }
+
+ /* Thirdly, finalize changes */
+ if(changedBridgeTimes)
+ {
+ FOREACH_TREE_IN_BRIDGE(tree, br)
+ {
+ assign(tree->BridgeTimes.remainingHops, br->MaxHops);
+ assign(tree->BridgeTimes.Forward_Delay, br->Forward_Delay);
+ assign(tree->BridgeTimes.Max_Age, br->Max_Age);
+ /* Comment found in rstpd by Srinivas Aji:
+ * Do this for any change in BridgeTimes.
+ * Otherwise we fail UNH rstp.op_D test 3.2 since when administratively
+ * setting BridgeForwardDelay, etc, the values don't propagate from
+ * rootTimes to designatedTimes immediately without this change.
+ */
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ {
+ assign(ptp->selected, boolFalse);
+ assign(ptp->reselect, boolTrue);
+ }
+ }
+ }
+
+ if(changed && br->bridgeEnabled)
+ {
+ if(init)
+ br_state_machines_begin(br);
+ else
+ br_state_machines_run(br);
+ }
+
+ return 0;
+}
+
+/* 12.8.1.4 Set MSTI Bridge Protocol Parameters */
+int MSTP_IN_set_msti_bridge_config(tree_t *tree, __u8 bridge_priority)
+{
+ per_tree_port_t *ptp;
+ __u8 valuePri;
+
+ if(15 < bridge_priority)
+ {
+ ERROR_BRNAME(tree->bridge,
+ "MSTI %hu: Bridge Priority must be between 0 and 15",
+ __be16_to_cpu(tree->MSTID));
+ return -1;
+ }
+
+ valuePri = bridge_priority << 4;
+ if(GET_PRIORITY_FROM_IDENTIFIER(tree->BridgeIdentifier) == valuePri)
+ return 0;
+ SET_PRIORITY_IN_IDENTIFIER(valuePri, tree->BridgeIdentifier);
+ tree->BridgePriority.RootID = tree->BridgePriority.RRootID =
+ tree->BridgePriority.DesignatedBridgeID = tree->BridgeIdentifier;
+ /* 12.8.1.4.4 do not require reselect, but I think it is needed,
+ * because 12.8.1.3.4.c) requires it */
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ {
+ assign(ptp->selected, boolFalse);
+ assign(ptp->reselect, boolTrue);
+ }
+ return 0;
+}
+
+/* 12.8.2.1 Read CIST Port Parameters */
+void MSTP_IN_get_cist_port_status(port_t *prt, CIST_PortStatus *status)
+{
+ per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
+ /* 12.8.2.2.3 b) */
+ status->uptime = (signed int)((prt->bridge)->uptime)
+ - (signed int)(cist->start_time);
+ status->state = cist->state;
+ assign(status->port_id, cist->portId);
+ assign(status->admin_external_port_path_cost,
+ prt->AdminExternalPortPathCost);
+ assign(status->external_port_path_cost, prt->ExternalPortPathCost);
+ assign(status->designated_root, cist->portPriority.RootID);
+ assign(status->designated_external_cost,
+ __be32_to_cpu(cist->portPriority.ExtRootPathCost));
+ assign(status->designated_bridge, cist->portPriority.DesignatedBridgeID);
+ assign(status->designated_port, cist->portPriority.DesignatedPortID);
+ assign(status->designated_regional_root, cist->portPriority.RRootID);
+ assign(status->designated_internal_cost,
+ __be32_to_cpu(cist->portPriority.IntRootPathCost));
+ assign(status->tc_ack, prt->tcAck);
+ status->port_hello_time = __be16_to_cpu(cist->portTimes.Hello_Time);
+ assign(status->admin_edge_port, prt->AdminEdgePort);
+ assign(status->auto_edge_port, prt->AutoEdge);
+ assign(status->oper_edge_port, prt->operEdge);
+ assign(status->enabled, prt->portEnabled);
+ assign(status->admin_p2p, prt->AdminP2P);
+ assign(status->oper_p2p, prt->operPointToPointMAC);
+ assign(status->restricted_role, prt->restrictedRole);
+ assign(status->restricted_tcn, prt->restrictedTcn);
+ assign(status->role, cist->role);
+ assign(status->disputed, cist->disputed);
+ assign(status->admin_internal_port_path_cost,
+ cist->AdminInternalPortPathCost);
+ assign(status->internal_port_path_cost, cist->InternalPortPathCost);
+}
+
+/* 12.8.2.2 Read MSTI Port Parameters */
+void MSTP_IN_get_msti_port_status(per_tree_port_t *ptp,
+ MSTI_PortStatus *status)
+{
+ status->uptime = (signed int)((ptp->port->bridge)->uptime)
+ - (signed int)(ptp->start_time);
+ status->state = ptp->state;
+ assign(status->port_id, ptp->portId);
+ assign(status->admin_internal_port_path_cost,
+ ptp->AdminInternalPortPathCost);
+ assign(status->internal_port_path_cost, ptp->InternalPortPathCost);
+ assign(status->designated_regional_root, ptp->portPriority.RRootID);
+ assign(status->designated_internal_cost,
+ __be32_to_cpu(ptp->portPriority.IntRootPathCost));
+ assign(status->designated_bridge, ptp->portPriority.DesignatedBridgeID);
+ assign(status->designated_port, ptp->portPriority.DesignatedPortID);
+ assign(status->role, ptp->role);
+ assign(status->disputed, ptp->disputed);
+}
+
+/* 12.8.2.3 Set CIST port parameters */
+int MSTP_IN_set_cist_port_config(port_t *prt, CIST_PortConfig *cfg)
+{
+ bool changed;
+ __u32 new_ExternalPathCost;
+ bool new_p2p;
+ per_tree_port_t *cist;
+
+ /* Firstly, validation */
+ if(cfg->set_admin_p2p)
+ {
+ switch(cfg->admin_p2p)
+ {
+ case p2pAuto:
+ case p2pForceTrue:
+ case p2pForceFalse:
+ break;
+ default:
+ cfg->admin_p2p = p2pAuto;
+ }
+ }
+
+ /* Secondly, do set */
+ changed = false;
+
+ if(cfg->set_admin_external_port_path_cost)
+ {
+ prt->AdminExternalPortPathCost = cfg->admin_external_port_path_cost;
+ new_ExternalPathCost = (0 == prt->AdminExternalPortPathCost) ?
+ compute_pcost(GET_PORT_SPEED(prt))
+ : prt->AdminExternalPortPathCost;
+ if(prt->ExternalPortPathCost != new_ExternalPathCost)
+ {
+ assign(prt->ExternalPortPathCost, new_ExternalPathCost);
+ changed = true;
+ /* 12.8.2.3.4 */
+ cist = GET_CIST_PTP_FROM_PORT(prt);
+ assign(cist->selected, boolFalse);
+ assign(cist->reselect, boolTrue);
+ }
+ }
+
+ if(cfg->set_admin_p2p)
+ {
+ prt->AdminP2P = cfg->admin_p2p;
+ switch(prt->AdminP2P)
+ {
+ case p2pForceTrue:
+ new_p2p = true;
+ break;
+ case p2pForceFalse:
+ new_p2p = false;
+ break;
+ case p2pAuto:
+ default:
+ new_p2p = !!GET_PORT_DUPLEX(prt);
+ break;
+ }
+ if(prt->operPointToPointMAC != new_p2p)
+ {
+ assign(prt->operPointToPointMAC, new_p2p);
+ changed = true;
+ }
+ }
+
+ if(cfg->set_admin_edge_port)
+ {
+ if(prt->AdminEdgePort != cfg->admin_edge_port)
+ {
+ prt->AdminEdgePort = cfg->admin_edge_port;
+ BDSM_begin(prt);
+ changed = true;
+ }
+ }
+
+ if(cfg->set_auto_edge_port)
+ {
+ if(prt->AutoEdge != cfg->auto_edge_port)
+ {
+ prt->AutoEdge = cfg->auto_edge_port;
+ changed = true;
+ }
+ }
+
+ if(cfg->set_restricted_role)
+ {
+ if(prt->restrictedRole != cfg->restricted_role)
+ {
+ prt->restrictedRole = cfg->restricted_role;
+ changed = true;
+ }
+ }
+
+ if(cfg->set_restricted_tcn)
+ {
+ if(prt->restrictedTcn != cfg->restricted_tcn)
+ {
+ prt->restrictedTcn = cfg->restricted_tcn;
+ changed = true;
+ }
+ }
+
+ if(changed && prt->portEnabled)
+ br_state_machines_run(prt->bridge);
+
+ return 0;
+}
+
+/* 12.8.2.4 Set MSTI port parameters */
+int MSTP_IN_set_msti_port_config(per_tree_port_t *ptp, MSTI_PortConfig *cfg)
+{
+ __u8 valuePri;
+ __u32 new_InternalPathCost;
+ bool changed = false;
+ port_t *prt = ptp->port;
+ bridge_t *br = prt->bridge;
+
+ if(cfg->set_port_priority)
+ {
+ if(15 < cfg->port_priority)
+ {
+ ERROR_MSTINAME(br, prt, ptp,
+ "Port Priority must be between 0 and 15");
+ return -1;
+ }
+ valuePri = cfg->port_priority << 4;
+ if(GET_PRIORITY_FROM_IDENTIFIER(ptp->portId) != valuePri)
+ {
+ SET_PRIORITY_IN_IDENTIFIER(valuePri, ptp->portId);
+ changed = true;
+ }
+ }
+
+ if(cfg->set_admin_internal_port_path_cost)
+ {
+ ptp->AdminInternalPortPathCost = cfg->admin_internal_port_path_cost;
+ new_InternalPathCost = (0 == ptp->AdminInternalPortPathCost) ?
+ compute_pcost(GET_PORT_SPEED(prt))
+ : ptp->AdminInternalPortPathCost;
+ if(ptp->InternalPortPathCost != new_InternalPathCost)
+ {
+ assign(ptp->InternalPortPathCost, new_InternalPathCost);
+ changed = true;
+ }
+ }
+
+ if(changed && prt->portEnabled)
+ {
+ /* 12.8.2.4.4 */
+ assign(ptp->selected, boolFalse);
+ assign(ptp->reselect, boolTrue);
+
+ br_state_machines_run(br);
+ }
+
+ return 0;
+}
+
+/* 12.8.2.5 Force BPDU Migration Check */
+int MSTP_IN_port_mcheck(port_t *prt)
+{
+ bridge_t *br = prt->bridge;
+
+ if(rstpVersion(br) && prt->portEnabled && br->bridgeEnabled)
+ {
+ assign(prt->mcheck, boolTrue);
+ br_state_machines_run(br);
+ }
+
+ return 0;
+}
+
+/* 12.10.3.8 Set VID to FID allocation */
+bool MSTP_IN_set_vid2fid(bridge_t *br, __u16 vid, __u16 fid)
+{
+ bool vid2mstid_changed;
+
+ if((vid < 1) || (vid > MAX_VID) || (fid > MAX_FID))
+ {
+ ERROR_BRNAME(br, "Error allocating VID(%hu) to FID(%hu)", vid, fid);
+ return false;
+ }
+
+ vid2mstid_changed =
+ (br->fid2mstid[fid] != br->fid2mstid[br->vid2fid[vid]]);
+ br->vid2fid[vid] = fid;
+ if(vid2mstid_changed)
+ {
+ RecalcConfigDigest(br);
+ br_state_machines_begin(br);
+ }
+
+ return true;
+}
+
+/* Set all VID-to-FID mappings at once */
+bool MSTP_IN_set_all_vids2fids(bridge_t *br, __u16 *vids2fids)
+{
+ bool vid2mstid_changed;
+ int vid;
+
+ for(vid = 1; vid <= MAX_VID; ++vid)
+ if(vids2fids[vid] > MAX_FID)
+ {
+ ERROR_BRNAME(br, "Error allocating VID(%hu) to FID(%hu)",
+ vid, vids2fids[vid]);
+ return false;
+ }
+
+ vid2mstid_changed = false;
+ for(vid = 1; vid <= MAX_VID; ++vid)
+ {
+ if(br->fid2mstid[vids2fids[vid]] != br->fid2mstid[br->vid2fid[vid]])
+ {
+ vid2mstid_changed = true;
+ break;
+ }
+ }
+ memcpy(br->vid2fid, vids2fids, sizeof(br->vid2fid));
+ if(vid2mstid_changed)
+ {
+ RecalcConfigDigest(br);
+ br_state_machines_begin(br);
+ }
+
+ return true;
+}
+
+/* 12.12.2.2 Set FID to MSTID allocation */
+bool MSTP_IN_set_fid2mstid(bridge_t *br, __u16 fid, __u16 mstid)
+{
+ tree_t *tree;
+ __be16 MSTID;
+ bool found;
+ int vid;
+
+ if(fid > MAX_FID)
+ {
+ ERROR_BRNAME(br, "Bad FID(%hu)", fid);
+ return false;
+ }
+
+ MSTID = __cpu_to_be16(mstid);
+ found = false;
+ FOREACH_TREE_IN_BRIDGE(tree, br)
+ {
+ if(tree->MSTID == MSTID)
+ {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ {
+ ERROR_BRNAME(br, "MSTID(%hu) not found", mstid);
+ return false;
+ }
+
+ if(br->fid2mstid[fid] != MSTID)
+ {
+ br->fid2mstid[fid] = MSTID;
+ /* check if there are VLANs using this FID */
+ for(vid = 1; vid <= MAX_VID; ++vid)
+ {
+ if(br->vid2fid[vid] == fid)
+ {
+ RecalcConfigDigest(br);
+ br_state_machines_begin(br);
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+/* Set all FID-to-MSTID mappings at once */
+bool MSTP_IN_set_all_fids2mstids(bridge_t *br, __u16 *fids2mstids)
+{
+ tree_t *tree;
+ __be16 MSTID[MAX_FID + 1];
+ bool found, vid2mstid_changed;
+ int fid, vid;
+ __be16 prev_vid2mstid[MAX_VID + 2];
+
+ for(fid = 0; fid <= MAX_FID; ++fid)
+ {
+ MSTID[fid] = __cpu_to_be16(fids2mstids[fid]);
+ found = false;
+ FOREACH_TREE_IN_BRIDGE(tree, br)
+ {
+ if(tree->MSTID == MSTID[fid])
+ {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ {
+ ERROR_BRNAME(br,
+ "Error allocating FID(%hu) to MSTID(%hu): MSTID not found",
+ fid, fids2mstids[fid]);
+ return false;
+ }
+ }
+
+ for(vid = 1; vid <= MAX_VID; ++vid)
+ prev_vid2mstid[vid] = br->fid2mstid[br->vid2fid[vid]];
+ memcpy(br->fid2mstid, MSTID, sizeof(br->fid2mstid));
+ vid2mstid_changed = false;
+ for(vid = 1; vid <= MAX_VID; ++vid)
+ {
+ if(prev_vid2mstid[vid] != br->fid2mstid[br->vid2fid[vid]])
+ {
+ vid2mstid_changed = true;
+ break;
+ }
+ }
+ if(vid2mstid_changed)
+ {
+ RecalcConfigDigest(br);
+ br_state_machines_begin(br);
+ }
+
+ return true;
+}
+
+/* 12.12.1.1 Read MSTI List */
+bool MSTP_IN_get_mstilist(bridge_t *br, int *num_mstis, __u16 *mstids)
+{
+ tree_t *tree;
+
+ *num_mstis = 0;
+ FOREACH_TREE_IN_BRIDGE(tree, br)
+ {
+ mstids[*num_mstis] = __be16_to_cpu(tree->MSTID);
+ /* Check for "<", not for "<=", as num_mstis include CIST */
+ if(MAX_IMPLEMENTATION_MSTIS < ++(*num_mstis))
+ break;
+ }
+
+ return true;
+}
+
+/* 12.12.1.2 Create MSTI */
+bool MSTP_IN_create_msti(bridge_t *br, __u16 mstid)
+{
+ tree_t *tree, *tree_after, *new_tree;
+ per_tree_port_t *ptp, *nxt, *ptp_after, *new_ptp;
+ int num_of_mstis;
+ __be16 MSTID;
+
+ if((mstid < 1) || (mstid > MAX_MSTID))
+ {
+ ERROR_BRNAME(br, "Bad MSTID(%hu)", mstid);
+ return false;
+ }
+
+ MSTID = __cpu_to_be16(mstid);
+ /* Find place where to insert new MSTID.
+ * Also check if such MSTID is already in the list.
+ * Also count existing mstis.
+ */
+ tree_after = NULL;
+ num_of_mstis = 0;
+ FOREACH_TREE_IN_BRIDGE(tree, br)
+ {
+ if(tree->MSTID == MSTID)
+ {
+ INFO_BRNAME(br, "MSTID(%hu) is already in the list", mstid);
+ return true; /* yes, it is success */
+ }
+ if(cmp(tree->MSTID, <, MSTID))
+ tree_after = tree;
+ ++num_of_mstis;
+ }
+ /* Sanity check */
+ if(NULL == tree_after)
+ {
+ ERROR_BRNAME(br, "Can't add MSTID(%hu): no CIST in the list", mstid);
+ return false;
+ }
+ /* End of Sanity check */
+
+ /* Check for "<", not for "<=", as num_of_mstis include CIST */
+ if(MAX_IMPLEMENTATION_MSTIS < num_of_mstis)
+ {
+ ERROR_BRNAME(br, "Can't add MSTID(%hu): maximum count(%u) reached",
+ mstid, MAX_IMPLEMENTATION_MSTIS);
+ return false;
+ }
+
+ /* Create new tree and its list of PerTreePort structures */
+ tree = GET_CIST_TREE(br);
+ if(!(new_tree=create_tree(br,tree->BridgeIdentifier.s.mac_address,MSTID)))
+ return false;
+
+ FOREACH_PTP_IN_TREE(ptp_after, tree_after)
+ {
+ if(!(new_ptp = create_ptp(new_tree, ptp_after->port)))
+ {
+ /* Remove and free all previously created entries in tree's list */
+ list_for_each_entry_safe(ptp, nxt, &new_tree->ports, tree_list)
+ {
+ list_del(&ptp->port_list);
+ list_del(&ptp->tree_list);
+ free(ptp);
+ }
+ return false;
+ }
+ list_add(&new_ptp->port_list, &ptp_after->port_list);
+ list_add_tail(&new_ptp->tree_list, &new_tree->ports);
+ }
+
+ list_add(&new_tree->bridge_list, &tree_after->bridge_list);
+ /* There are no FIDs allocated to this MSTID, so VID-to-MSTID mapping
+ * did not change. So, no need in RecalcConfigDigest.
+ * Just initialize state machines for this tree.
+ */
+ tree_state_machines_begin(new_tree);
+ return true;
+}
+
+/* 12.12.1.3 Delete MSTI */
+bool MSTP_IN_delete_msti(bridge_t *br, __u16 mstid)
+{
+ tree_t *tree;
+ per_tree_port_t *ptp, *nxt;
+ int fid;
+ bool found;
+ __be16 MSTID = __cpu_to_be16(mstid);
+
+ if((mstid < 1) || (mstid > MAX_MSTID))
+ {
+ ERROR_BRNAME(br, "Bad MSTID(%hu)", mstid);
+ return false;
+ }
+
+ /* Check if there are FIDs associated with this MSTID */
+ for(fid = 0; fid <= MAX_FID; ++fid)
+ {
+ if(br->fid2mstid[fid] == MSTID)
+ {
+ ERROR_BRNAME(br,
+ "Can't delete MSTID(%hu): there are FIDs allocated to it",
+ mstid);
+ return false;
+ }
+ }
+
+ found = false;
+ FOREACH_TREE_IN_BRIDGE(tree, br)
+ {
+ if(tree->MSTID == MSTID)
+ {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ {
+ INFO_BRNAME(br, "MSTID(%hu) is not in the list", mstid);
+ return true; /* yes, it is success */
+ }
+
+ list_del(&tree->bridge_list);
+ list_for_each_entry_safe(ptp, nxt, &tree->ports, tree_list)
+ {
+ list_del(&ptp->port_list);
+ list_del(&ptp->tree_list);
+ free(ptp);
+ }
+ free(tree);
+
+ /* There are no FIDs allocated to this MSTID, so VID-to-MSTID mapping
+ * did not change. So, no need in RecalcConfigDigest.
+ * Give state machine a spare run, just for the case...
+ */
+ br_state_machines_run(br);
+ return true;
+}
+
+/* 12.12.3.4 Set MST Configuration Identifier Elements */
+void MSTP_IN_set_mst_config_id(bridge_t *br, __u16 revision, __u8 *name)
+{
+ __be16 valueRevision = __cpu_to_be16(revision);
+ bool changed = (0 != strncmp(name, br->MstConfigId.s.configuration_name,
+ sizeof(br->MstConfigId.s.configuration_name))
+ )
+ || (valueRevision != br->MstConfigId.s.revision_level);
+
+ if(changed)
+ {
+ assign(br->MstConfigId.s.revision_level, valueRevision);
+ memset(br->MstConfigId.s.configuration_name, 0,
+ sizeof(br->MstConfigId.s.configuration_name));
+ strncpy(br->MstConfigId.s.configuration_name, name,
+ sizeof(br->MstConfigId.s.configuration_name));
+ br_state_machines_begin(br);
+ }
+}
+
+/*
+ * If hint_SetToYes == true, some tcWhile in this tree has non-zero value.
+ * If hint_SetToYes == false, some tcWhile in this tree has just became zero,
+ * so we should check all other tcWhile's in this tree.
+ */
+static void set_TopologyChange(tree_t *tree, bool hint_SetToYes)
+{
+ per_tree_port_t *ptp;
+ bool prev_tc_not_set = !tree->topology_change;
+
+ if(hint_SetToYes)
+ {
+ tree->topology_change = true;
+ tree->time_since_topology_change = 0;
+ if(prev_tc_not_set)
+ ++(tree->topology_change_count);
+ return;
+ }
+
+ /* Some tcWhile has just became zero. Check if we need reset
+ * topology_change flag */
+ if(prev_tc_not_set)
+ return;
+
+ tree->topology_change = false;
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ {
+ if(0 != ptp->tcWhile)
+ {
+ tree->topology_change = true;
+ tree->time_since_topology_change = 0;
+ return;
+ }
+ }
+}
+
+/* Helper functions, compare two priority vectors */
+static bool samePriorityAndTimers(port_priority_vector_t *vec1,
+ port_priority_vector_t *vec2,
+ times_t *time1,
+ times_t *time2,
+ bool cist)
+{
+ if(cist)
+ {
+ if(cmp(time1->Forward_Delay, !=, time2->Forward_Delay))
+ return false;
+ if(cmp(time1->Max_Age, !=, time2->Max_Age))
+ return false;
+ if(cmp(time1->Message_Age, !=, time2->Message_Age))
+ return false;
+ if(cmp(time1->Hello_Time, !=, time2->Hello_Time))
+ return false;
+
+ if(cmp(vec1->RootID, !=, vec2->RootID))
+ return false;
+ if(cmp(vec1->ExtRootPathCost, !=, vec2->ExtRootPathCost))
+ return false;
+ }
+
+ if(cmp(time1->remainingHops, !=, time2->remainingHops))
+ return false;
+
+ if(cmp(vec1->RRootID, !=, vec2->RRootID))
+ return false;
+ if(cmp(vec1->IntRootPathCost, !=, vec2->IntRootPathCost))
+ return false;
+ if(cmp(vec1->DesignatedBridgeID, !=, vec2->DesignatedBridgeID))
+ return false;
+ if(cmp(vec1->DesignatedPortID, !=, vec2->DesignatedPortID))
+ return false;
+
+ return true;
+}
+
+static bool betterorsamePriority(port_priority_vector_t *vec1,
+ port_priority_vector_t *vec2,
+ port_identifier_t pId1,
+ port_identifier_t pId2,
+ bool cist)
+{
+ int result;
+
+ if(cist)
+ {
+ if(0 < (result = _ncmp(vec1->RootID, vec2->RootID)))
+ return false; /* worse */
+ else if(0 > result)
+ return true; /* better */
+ /* The same. Check further. */
+ if(0 < (result = _ncmp(vec1->ExtRootPathCost, vec2->ExtRootPathCost)))
+ return false; /* worse */
+ else if(0 > result)
+ return true; /* better */
+ /* The same. Check further. */
+ }
+
+ if(0 < (result = _ncmp(vec1->RRootID, vec2->RRootID)))
+ return false; /* worse */
+ else if(0 > result)
+ return true; /* better */
+ /* The same. Check further. */
+
+ if(0 < (result = _ncmp(vec1->IntRootPathCost, vec2->IntRootPathCost)))
+ return false; /* worse */
+ else if(0 > result)
+ return true; /* better */
+ /* The same. Check further. */
+
+ if(0 < (result = _ncmp(vec1->DesignatedBridgeID, vec2->DesignatedBridgeID)))
+ return false; /* worse */
+ else if(0 > result)
+ return true; /* better */
+ /* The same. Check further. */
+
+ if(0 < (result = _ncmp(vec1->DesignatedPortID, vec2->DesignatedPortID)))
+ return false; /* worse */
+ else if(0 > result)
+ return true; /* better */
+ /* The same. Check further. */
+
+ /* Port ID is a tie-breaker */
+ return cmp(pId1, <=, pId2);
+}
+
+/* 13.26.1 betterorsameInfo */
+static bool betterorsameInfo(per_tree_port_t *ptp, port_info_origin_t newInfoIs)
+{
+ if((ioReceived == newInfoIs) && (ioReceived == ptp->infoIs))
+ return betterorsamePriority(&ptp->msgPriority,
+ &ptp->portPriority,
+ 0, 0, (0 == ptp->MSTID));
+ else if((ioMine == newInfoIs) && (ioMine == ptp->infoIs))
+ return betterorsamePriority(&ptp->designatedPriority,
+ &ptp->portPriority,
+ 0, 0, (0 == ptp->MSTID));
+ return false;
+}
+
+/* 13.26.2 clearAllRcvdMsgs */
+static void clearAllRcvdMsgs(port_t *prt)
+{
+ per_tree_port_t *ptp;
+
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ assign(ptp->rcvdMsg, boolFalse);
+}
+
+/* 13.26.3 clearReselectTree */
+static void clearReselectTree(tree_t *tree)
+{
+ per_tree_port_t *ptp;
+
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ assign(ptp->reselect, boolFalse);
+}
+
+/* 13.26.4 fromSameRegion */
+static bool fromSameRegion(port_t *prt)
+{
+ /* Check for rcvdRSTP is superfluous here */
+ if((protoMSTP > prt->rcvdBpduData.protocolVersion)/* || (!prt->rcvdRSTP)*/)
+ return false;
+ return cmp(prt->bridge->MstConfigId,
+ ==, prt->rcvdBpduData.mstConfigurationIdentifier);
+}
+
+/* 13.26.5 newTcWhile */
+static void newTcWhile(per_tree_port_t *ptp)
+{
+ if(0 != ptp->tcWhile)
+ return;
+
+ tree_t *tree = ptp->tree;
+ port_t *prt = ptp->port;
+
+ if(prt->sendRSTP)
+ {
+ per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
+ unsigned int HelloTime = __be16_to_cpu(cist->portTimes.Hello_Time);
+
+ assign(ptp->tcWhile, HelloTime + 1);
+ set_TopologyChange(tree, true);
+
+ if(0 == ptp->MSTID)
+ assign(prt->newInfo, boolTrue);
+ else
+ assign(prt->newInfoMsti, boolTrue);
+ return;
+ }
+
+ times_t *times = &tree->rootTimes;
+
+ ptp->tcWhile = __be16_to_cpu(times->Max_Age)
+ + __be16_to_cpu(times->Forward_Delay);
+ set_TopologyChange(tree, true);
+}
+
+/* 13.26.6 rcvInfo */
+static port_info_t rcvInfo(per_tree_port_t *ptp)
+{
+ msti_configuration_message_t *msti_msg;
+ per_tree_port_t *ptp_1;
+ bool roleIsDesignated, cist;
+ bool msg_Better_port, msg_SamePriorityAndTimers_port;
+ port_priority_vector_t *mPri = &(ptp->msgPriority);
+ times_t *mTimes = &(ptp->msgTimes);
+ port_t *prt = ptp->port;
+ bpdu_t *b = &(prt->rcvdBpduData);
+
+ if(bpduTypeTCN == b->bpduType)
+ {
+ assign(prt->rcvdTcn, boolTrue);
+ FOREACH_PTP_IN_PORT(ptp_1, prt)
+ assign(ptp_1->rcvdTc, boolTrue);
+ return OtherInfo;
+ }
+
+ if(0 == ptp->MSTID)
+ { /* CIST */
+ switch(BPDU_FLAGS_ROLE_GET(b->flags))
+ {
+ case encodedRoleAlternateBackup:
+ case encodedRoleRoot:
+ roleIsDesignated = false;
+ break;
+ case encodedRoleDesignated:
+ roleIsDesignated = true;
+ break;
+ default:
+ return OtherInfo;
+ }
+ cist = true;
+
+ assign(mPri->RRootID, b->cistRRootID);
+ assign(mPri->DesignatedPortID, b->cistPortID);
+ assign(mPri->RootID, b->cistRootID);
+ assign(mPri->ExtRootPathCost, b->cistExtRootPathCost);
+ /* messageTimes */
+ assign(mTimes->Forward_Delay, b->ForwardDelay);
+ assign(mTimes->Max_Age, b->MaxAge);
+ assign(mTimes->Message_Age, b->MessageAge);
+ assign(mTimes->Hello_Time, b->HelloTime);
+ if(protoMSTP > b->protocolVersion)
+ { /* STP or RSTP Configuration BPDU */
+ /* 13.26.6.NOTE: A Configuration BPDU implicitly conveys a
+ * Designated Port Role */
+ roleIsDesignated = true;
+ assign(mPri->IntRootPathCost, 0u);
+ assign(mPri->DesignatedBridgeID, b->cistRRootID);
+ /* messageTimes.remainingHops */
+ assign(mTimes->remainingHops, prt->bridge->MaxHops);
+ }
+ else
+ { /* MST BPDU */
+ assign(mPri->IntRootPathCost, b->cistIntRootPathCost);
+ assign(mPri->DesignatedBridgeID, b->cistBridgeID);
+ /* messageTimes.remainingHops */
+ assign(mTimes->remainingHops, b->cistRemainingHops);
+ }
+ }
+ else
+ { /* MSTI */
+ if(protoMSTP > b->protocolVersion)
+ return OtherInfo;
+ msti_msg = ptp->rcvdMstiConfig;
+ switch(BPDU_FLAGS_ROLE_GET(msti_msg->flags))
+ {
+ case encodedRoleAlternateBackup:
+ case encodedRoleRoot:
+ roleIsDesignated = false;
+ break;
+ case encodedRoleDesignated:
+ roleIsDesignated = true;
+ break;
+ default:
+ return OtherInfo;
+ }
+ cist = false;
+
+ assign(mPri->RRootID, msti_msg->mstiRRootID);
+ assign(mPri->IntRootPathCost, msti_msg->mstiIntRootPathCost);
+ /* Build MSTI DesignatedBridgeID */
+ assign(mPri->DesignatedBridgeID, b->cistBridgeID);
+ assign(mPri->DesignatedBridgeID.s.priority, ptp->MSTID);
+ SET_PRIORITY_IN_IDENTIFIER(msti_msg->bridgeIdentifierPriority,
+ mPri->DesignatedBridgeID);
+ /* Build MSTI DesignatedPortID */
+ assign(mPri->DesignatedPortID, b->cistPortID);
+ SET_PRIORITY_IN_IDENTIFIER(msti_msg->portIdentifierPriority,
+ mPri->DesignatedPortID);
+ /* messageTimes */
+ assign(mTimes->remainingHops, msti_msg->remainingHops);
+ }
+
+ msg_Better_port = !betterorsamePriority(&(ptp->portPriority), mPri,
+ 0, 0, cist);
+ if(roleIsDesignated)
+ {
+ /* a).1) */
+ if(msg_Better_port
+ || ((0 == memcmp(mPri->DesignatedBridgeID.s.mac_address,
+ ptp->portPriority.DesignatedBridgeID.s.mac_address,
+ ETH_ALEN)
+ )
+ && (0 == ((mPri->DesignatedPortID
+ ^ ptp->portPriority.DesignatedPortID
+ ) & __constant_cpu_to_be16(0x0FFF)
+ )
+ )
+ )
+ )
+ return SuperiorDesignatedInfo;
+
+ /* a).2) */
+ /* We already know that msgPriority _IS_NOT_BETTER_than portPriority.
+ * So, if msgPriority _IS_SAME_OR_BETTER_than portPriority then
+ * msgPriority _IS_SAME_as portPriority.
+ */
+ msg_SamePriorityAndTimers_port =
+ samePriorityAndTimers(mPri, &(ptp->portPriority),
+ mTimes, &(ptp->portTimes),
+ cist);
+ if((!msg_SamePriorityAndTimers_port)
+ && betterorsamePriority(mPri, &(ptp->portPriority), 0, 0, cist)
+ )
+ return SuperiorDesignatedInfo;
+
+ /* b) */
+ if(msg_SamePriorityAndTimers_port && (ioReceived == ptp->infoIs))
+ return RepeatedDesignatedInfo;
+
+ /* c) */
+ return InferiorDesignatedInfo;
+ }
+
+ /* d) */
+ if(!msg_Better_port)
+ return InferiorRootAlternateInfo;
+
+ return OtherInfo;
+}
+
+/* 13.26.7 recordAgreement */
+static void recordAgreement(per_tree_port_t *ptp)
+{
+ bool cist_agreed, cist_proposing;
+ per_tree_port_t *cist;
+ port_t *prt = ptp->port;
+ bpdu_t *b = &(prt->rcvdBpduData);
+
+ if(0 == ptp->MSTID)
+ { /* CIST */
+ if(rstpVersion(prt->bridge) && prt->operPointToPointMAC
+ && (b->flags & (1 << offsetAgreement))
+ )
+ {
+ assign(ptp->agreed, boolTrue);
+ assign(ptp->proposing, boolFalse);
+ }
+ else
+ assign(ptp->agreed, boolFalse);
+ cist_agreed = ptp->agreed;
+ cist_proposing = ptp->proposing;
+ if(!prt->rcvdInternal)
+ list_for_each_entry_continue(ptp, &prt->trees, port_list)
+ {
+ assign(ptp->agreed, cist_agreed);
+ assign(ptp->proposing, cist_proposing);
+ }
+ return;
+ }
+ /* MSTI */
+ cist = GET_CIST_PTP_FROM_PORT(prt);
+ if(prt->operPointToPointMAC
+ && cmp(b->cistRootID, ==, cist->portPriority.RootID)
+ && cmp(b->cistExtRootPathCost, ==, cist->portPriority.ExtRootPathCost)
+ && cmp(b->cistRRootID, ==, cist->portPriority.RRootID)
+ && (ptp->rcvdMstiConfig->flags & (1 << offsetAgreement))
+ )
+ {
+ assign(ptp->agreed, boolTrue);
+ assign(ptp->proposing, boolFalse);
+ }
+ else
+ assign(ptp->agreed, boolFalse);
+}
+
+/* 13.26.8 recordDispute */
+static void recordDispute(per_tree_port_t *ptp)
+{
+ port_t *prt;
+
+ if(0 == ptp->MSTID)
+ { /* CIST */
+ prt = ptp->port;
+ /* 802.1Q-2005 is somewhat unclear for the case (!prt->rcvdInternal):
+ * if we should record dispute for all MSTIs unconditionally
+ * or only when CIST Learning flag is set in BPDU.
+ * I guess that in this case MSTIs should be in sync with CIST
+ * so record dispute for the MSTIs only when the same is done for CIST.
+ * Additional supporting argument to this guess is that in
+ * setTcFlags() we do the same.
+ * But that is only a guess and I could be wrong here ;)
+ * Maybe 802.1Q-2011 clarifies this, but I don't have the text.
+ */
+ if(prt->rcvdBpduData.flags & (1 << offsetLearnig))
+ {
+ assign(ptp->disputed, boolTrue);
+ assign(ptp->agreed, boolFalse);
+ if(!prt->rcvdInternal)
+ list_for_each_entry_continue(ptp, &prt->trees, port_list)
+ {
+ assign(ptp->disputed, boolTrue);
+ assign(ptp->agreed, boolFalse);
+ }
+ }
+ return;
+ }
+ /* MSTI */
+ if(ptp->rcvdMstiConfig->flags & (1 << offsetLearnig))
+ {
+ assign(ptp->disputed, boolTrue);
+ assign(ptp->agreed, boolFalse);
+ }
+}
+
+/* 13.26.9 recordMastered */
+static void recordMastered(per_tree_port_t *ptp)
+{
+ port_t *prt = ptp->port;
+
+ if(0 == ptp->MSTID)
+ { /* CIST */
+ if(!prt->rcvdInternal)
+ list_for_each_entry_continue(ptp, &prt->trees, port_list)
+ assign(ptp->mastered, boolFalse);
+ return;
+ }
+ /* MSTI */
+ ptp->mastered = prt->operPointToPointMAC
+ && (ptp->rcvdMstiConfig->flags & (1 << offsetMaster));
+}
+
+/* 13.26.f) recordPriority */
+static void recordPriority(per_tree_port_t *ptp)
+{
+ assign(ptp->portPriority, ptp->msgPriority);
+}
+
+/* 13.26.10 recordProposal */
+static void recordProposal(per_tree_port_t *ptp)
+{
+ bool cist_proposed;
+ port_t *prt;
+
+ /* 802.1Q-2005 says to check if received message conveys
+ * a Designated Port Role. But there is no need in this check,
+ * as it is always true. This function is called only in two states:
+ * PISM_SUPERIOR_DESIGNATED and PISM_REPEATED_DESIGNATED, which
+ * can be entered only if rcvInfo returns
+ * SuperiorDesignatedInfo or RepeatedDesignatedInfo.
+ * Which in turn can only happen if message conveys designated role
+ * (see rcvInfo).
+ */
+ if(0 == ptp->MSTID)
+ { /* CIST */
+ prt = ptp->port;
+ if(prt->rcvdBpduData.flags & (1 << offsetProposal))
+ assign(ptp->proposed, boolTrue);
+ cist_proposed = ptp->proposed;
+ if(!prt->rcvdInternal)
+ list_for_each_entry_continue(ptp, &prt->trees, port_list)
+ assign(ptp->proposed, cist_proposed);
+ return;
+ }
+ /* MSTI */
+ if(ptp->rcvdMstiConfig->flags & (1 << offsetProposal))
+ assign(ptp->proposed, boolTrue);
+}
+
+/* 13.26.11 recordTimes */
+static void recordTimes(per_tree_port_t *ptp)
+{
+ assign(ptp->portTimes, ptp->msgTimes);
+ if(__be16_to_cpu(ptp->portTimes.Hello_Time) < MIN_COMPAT_HELLO_TIME)
+ assign(ptp->portTimes.Hello_Time,
+ __constant_cpu_to_be16(MIN_COMPAT_HELLO_TIME));
+}
+
+/* 13.24.s) + 17.19.7 of 802.1D : fdbFlush */
+static void set_fdbFlush(per_tree_port_t *ptp)
+{
+ port_t *prt = ptp->port;
+
+ if(prt->operEdge)
+ {
+ assign(ptp->fdbFlush, boolFalse);
+ return;
+ }
+
+ bridge_t *br = prt->bridge;
+
+ if(rstpVersion(br))
+ {
+ assign(ptp->fdbFlush, boolTrue);
+ MSTP_OUT_flush_all_fids(ptp);
+ }
+ else
+ {
+ per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
+ unsigned int FwdDelay =
+ __be16_to_cpu(cist->designatedTimes.Forward_Delay);
+ /* Initiate rapid ageing */
+ MSTP_OUT_set_ageing_time(br, FwdDelay);
+ assign(br->rapidAgeingWhile, FwdDelay);
+ assign(ptp->fdbFlush, boolFalse);
+ }
+}
+
+/* 13.26.12 setRcvdMsgs */
+static void setRcvdMsgs(port_t *prt)
+{
+ msti_configuration_message_t *msti_msg;
+ int i;
+ __be16 msg_MSTID;
+ bool found;
+ per_tree_port_t *ptp = GET_CIST_PTP_FROM_PORT(prt);
+ ptp->rcvdMsg = true;
+
+ /* 802.1Q-2005 says:
+ * "Make the received CST or CIST message available to the CIST Port
+ * Information state machines"
+ * No need to do something special here, we already have rcvdBpduData.
+ */
+
+ if(prt->rcvdInternal)
+ {
+ list_for_each_entry_continue(ptp, &prt->trees, port_list)
+ {
+ found = false;
+ /* Find if message for this MSTI is conveyed in the BPDU */
+ for(i = 0, msti_msg = prt->rcvdBpduData.mstConfiguration;
+ i < prt->rcvdBpduNumOfMstis;
+ ++i, ++msti_msg)
+ {
+ msg_MSTID = msti_msg->mstiRRootID.s.priority
+ & __constant_cpu_to_be16(0x0FFF);
+ if(msg_MSTID == ptp->MSTID)
+ {
+ found = true;
+ break;
+ }
+ }
+ if(found)
+ {
+ ptp->rcvdMsg = true;
+ /* 802.1Q-2005 says:
+ * "Make available each MSTI message and the common parts of
+ * the CIST message priority (the CIST Root Identifier,
+ * External Root Path Cost and Regional Root Identifier)
+ * to the Port Information state machine for that MSTI"
+ * We set pointer to the MSTI configuration message for
+ * fast access, while do not anything special for common
+ * parts of the message, as the whole message is available
+ * in rcvdBpduData.
+ */
+ ptp->rcvdMstiConfig = msti_msg;
+ }
+ }
+ }
+}
+
+/* 13.26.13 setReRootTree */
+static void setReRootTree(tree_t *tree)
+{
+ per_tree_port_t *ptp;
+
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ assign(ptp->reRoot, boolTrue);
+}
+
+/* 13.26.14 setSelectedTree */
+static void setSelectedTree(tree_t *tree)
+{
+ per_tree_port_t *ptp;
+
+ /*
+ * 802.1Q-2005 says that I should check "reselect" var
+ * and take no action if it is "true" for any of the ports.
+ * But there is no need in this check as setSelectedTree is called
+ * only from PRSSM_to_ROLE_SELECTION, which is atomic, and it is called
+ * in this sequence (13.33):
+ * clearReselectTree(tree);
+ * updtRolesTree(tree);
+ * setSelectedTree(tree);
+ * And we know that clearReselectTree resets "reselect" for all ports
+ * and updtRolesTree() does not change value of "reselect".
+ */
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ assign(ptp->selected, boolTrue);
+}
+
+/* 13.26.15 setSyncTree */
+static void setSyncTree(tree_t *tree)
+{
+ per_tree_port_t *ptp;
+
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ assign(ptp->sync, boolTrue);
+}
+
+/* 13.26.16 setTcFlags */
+static void setTcFlags(per_tree_port_t *ptp)
+{
+ __u8 cistFlags;
+ port_t *prt;
+
+ if(0 == ptp->MSTID)
+ { /* CIST */
+ prt = ptp->port;
+ cistFlags = prt->rcvdBpduData.flags;
+ if(cistFlags & (1 << offsetTcAck))
+ assign(prt->rcvdTcAck, boolTrue);
+ if(cistFlags & (1 << offsetTc))
+ {
+ assign(ptp->rcvdTc, boolTrue);
+ if(!prt->rcvdInternal)
+ list_for_each_entry_continue(ptp, &prt->trees, port_list)
+ assign(ptp->proposed, boolTrue);
+ }
+ return;
+ }
+ /* MSTI */
+ if(ptp->rcvdMstiConfig->flags & (1 << offsetTc))
+ assign(ptp->rcvdTc, boolTrue);
+}
+
+/* 13.26.17 setTcPropTree */
+static void setTcPropTree(per_tree_port_t *ptp)
+{
+ per_tree_port_t *ptp_1;
+
+ if(ptp->port->restrictedTcn)
+ return;
+
+ FOREACH_PTP_IN_TREE(ptp_1, ptp->tree)
+ {
+ if(ptp != ptp_1)
+ assign(ptp_1->tcProp, boolTrue);
+ }
+}
+
+/* 13.26.18 syncMaster */
+static void syncMaster(bridge_t *br)
+{
+ per_tree_port_t *ptp;
+ tree_t *tree = GET_CIST_TREE(br);
+
+ /* For each MSTI */
+ list_for_each_entry_continue(tree, &br->trees, bridge_list)
+ {
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ {
+ /* for each Port that has infoInternal set */
+ if(ptp->port->infoInternal)
+ {
+ assign(ptp->agree, boolFalse);
+ assign(ptp->agreed, boolFalse);
+ assign(ptp->synced, boolFalse);
+ assign(ptp->sync, boolTrue);
+ }
+ }
+ }
+}
+
+/* 13.26.19 txConfig */
+static void txConfig(port_t *prt)
+{
+ bpdu_t b;
+ per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
+
+ b.protocolIdentifier = 0;
+ b.protocolVersion = protoSTP;
+ b.bpduType = bpduTypeConfig;
+ /* Standard says "tcWhile ... for the Port". Which one tcWhile?
+ * I guess that this means tcWhile for the CIST.
+ * But that is only a guess and I could be wrong here ;)
+ * Maybe 802.1Q-2011 clarifies this, but I don't have the text.
+ */
+ b.flags = (0 != cist->tcWhile) ? (1 << offsetTc) : 0;
+ if(prt->tcAck)
+ b.flags |= (1 << offsetTcAck);
+ assign(b.cistRootID, cist->designatedPriority.RootID);
+ assign(b.cistExtRootPathCost, cist->designatedPriority.ExtRootPathCost);
+ assign(b.cistRRootID, cist->designatedPriority.DesignatedBridgeID);
+ assign(b.cistPortID, cist->designatedPriority.DesignatedPortID);
+ assign(b.MessageAge, cist->designatedTimes.Message_Age);
+ assign(b.MaxAge, cist->designatedTimes.Max_Age);
+ assign(b.HelloTime, cist->portTimes.Hello_Time); /* ! use portTimes ! */
+ assign(b.ForwardDelay, cist->designatedTimes.Forward_Delay);
+
+ MSTP_OUT_tx_bpdu(prt, &b, CONFIG_BPDU_SIZE);
+}
+
+static inline __u8 message_role_from_port_role(per_tree_port_t *ptp)
+{
+ switch(ptp->role)
+ {
+ case roleRoot:
+ return encodedRoleRoot;
+ case roleDesignated:
+ return encodedRoleDesignated;
+ case roleAlternate:
+ case roleBackup:
+ return encodedRoleAlternateBackup;
+ case roleMaster:
+ return encodedRoleMaster;
+ default:
+ ERROR_PRTNAME(ptp->port->bridge, ptp->port,
+ "Attempt to send from port with Disabled role");
+ return encodedRoleAlternateBackup;
+ }
+}
+
+/* 13.26.20 txMstp */
+static void txMstp(port_t *prt)
+{
+ bpdu_t b;
+ bridge_t *br = prt->bridge;
+ per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
+ int msti_msgs_total_size;
+ per_tree_port_t *ptp;
+ msti_configuration_message_t *msti_msg;
+
+ b.protocolIdentifier = 0;
+ b.bpduType = bpduTypeRST;
+ /* Standard says "{tcWhile, agree, proposing, role} ... for the Port".
+ * Which one {tcWhile, agree, proposing, role}?
+ * I guess that this means {tcWhile, agree, proposing, role} for the CIST.
+ * But that is only a guess and I could be wrong here ;)
+ * Maybe 802.1Q-2011 clarifies this, but I don't have the text.
+ */
+ b.flags = BPDU_FLAGS_ROLE_SET(message_role_from_port_role(cist));
+ if(0 != cist->tcWhile)
+ b.flags |= (1 << offsetTc);
+ if(cist->proposing)
+ b.flags |= (1 << offsetProposal);
+ if(cist->learning)
+ b.flags |= (1 << offsetLearnig);
+ if(cist->forwarding)
+ b.flags |= (1 << offsetForwarding);
+ if(cist->agree)
+ b.flags |= (1 << offsetAgreement);
+ assign(b.cistRootID, cist->designatedPriority.RootID);
+ assign(b.cistExtRootPathCost, cist->designatedPriority.ExtRootPathCost);
+ assign(b.cistRRootID, cist->designatedPriority.RRootID);
+ assign(b.cistPortID, cist->designatedPriority.DesignatedPortID);
+ assign(b.MessageAge, cist->designatedTimes.Message_Age);
+ assign(b.MaxAge, cist->designatedTimes.Max_Age);
+ assign(b.HelloTime, cist->portTimes.Hello_Time); /* ! use portTimes ! */
+ assign(b.ForwardDelay, cist->designatedTimes.Forward_Delay);
+
+ b.version1_len = 0;
+
+ if(br->ForceProtocolVersion < protoMSTP)
+ {
+ b.protocolVersion = protoRSTP;
+ MSTP_OUT_tx_bpdu(prt, &b, RST_BPDU_SIZE);
+ return;
+ }
+
+ b.protocolVersion = protoMSTP;
+
+ /* MST specific fields */
+ assign(b.mstConfigurationIdentifier, br->MstConfigId);
+ assign(b.cistIntRootPathCost, cist->designatedPriority.IntRootPathCost);
+ assign(b.cistBridgeID, cist->designatedPriority.DesignatedBridgeID);
+ assign(b.cistRemainingHops, cist->designatedTimes.remainingHops);
+
+ msti_msgs_total_size = 0;
+ ptp = cist;
+ msti_msg = b.mstConfiguration;
+ /* 13.26.20.f) requires that msti configs should be inserted in
+ * MSTID order. This is met by inserting trees in port's list of trees
+ * in sorted (by MSTID) order (see MSTP_IN_create_msti) */
+ list_for_each_entry_continue(ptp, &prt->trees, port_list)
+ {
+ msti_msg->flags =
+ BPDU_FLAGS_ROLE_SET(message_role_from_port_role(ptp));
+ if(0 != ptp->tcWhile)
+ msti_msg->flags |= (1 << offsetTc);
+ if(ptp->proposing)
+ msti_msg->flags |= (1 << offsetProposal);
+ if(ptp->learning)
+ msti_msg->flags |= (1 << offsetLearnig);
+ if(ptp->forwarding)
+ msti_msg->flags |= (1 << offsetForwarding);
+ if(ptp->agree)
+ msti_msg->flags |= (1 << offsetAgreement);
+ if(ptp->master)
+ msti_msg->flags |= (1 << offsetMaster);
+ assign(msti_msg->mstiRRootID, ptp->designatedPriority.RRootID);
+ assign(msti_msg->mstiIntRootPathCost,
+ ptp->designatedPriority.IntRootPathCost);
+ msti_msg->bridgeIdentifierPriority =
+ GET_PRIORITY_FROM_IDENTIFIER(ptp->designatedPriority.DesignatedBridgeID);
+ msti_msg->portIdentifierPriority =
+ GET_PRIORITY_FROM_IDENTIFIER(ptp->designatedPriority.DesignatedPortID);
+ assign(msti_msg->remainingHops, ptp->designatedTimes.remainingHops);
+
+ msti_msgs_total_size += sizeof(msti_configuration_message_t);
+ ++msti_msg;
+ }
+
+ assign(b.version3_len, __cpu_to_be16(MST_BPDU_VER3LEN_WO_MSTI_MSGS
+ + msti_msgs_total_size));
+ MSTP_OUT_tx_bpdu(prt, &b, MST_BPDU_SIZE_WO_MSTI_MSGS
+ + msti_msgs_total_size);
+}
+
+/* 13.26.a) txTcn */
+static void txTcn(port_t *prt)
+{
+ bpdu_t b;
+
+ b.protocolIdentifier = 0;
+ b.protocolVersion = protoSTP;
+ b.bpduType = bpduTypeTCN;
+
+ MSTP_OUT_tx_bpdu(prt, &b, TCN_BPDU_SIZE);
+}
+
+/* 13.26.21 updtBPDUVersion */
+static void updtBPDUVersion(port_t *prt)
+{
+ if(protoRSTP <= prt->rcvdBpduData.protocolVersion)
+ assign(prt->rcvdRSTP, boolTrue);
+ else
+ assign(prt->rcvdSTP, boolTrue);
+}
+
+/* 13.26.22 updtRcvdInfoWhile */
+static void updtRcvdInfoWhile(per_tree_port_t *ptp)
+{
+ port_t *prt = ptp->port;
+ per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
+ unsigned int Message_Age = __be16_to_cpu(cist->portTimes.Message_Age);
+ unsigned int Max_Age = __be16_to_cpu(cist->portTimes.Max_Age);
+ unsigned int Hello_Time = __be16_to_cpu(cist->portTimes.Hello_Time);
+
+ if((!prt->rcvdInternal && ((Message_Age + 1) <= Max_Age))
+ || (prt->rcvdInternal && (cist->portTimes.remainingHops > 1))
+ )
+ ptp->rcvdInfoWhile = 3 * Hello_Time;
+ else
+ ptp->rcvdInfoWhile = 0;
+}
+
+/* 13.26.24 updtRolesDisabledTree */
+static void updtRolesDisabledTree(tree_t *tree)
+{
+ per_tree_port_t *ptp;
+
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ assign(ptp->selectedRole, roleDisabled);
+}
+
+/* 13.26.23 updtRolesTree */
+static void updtRolesTree(tree_t *tree)
+{
+ per_tree_port_t *ptp, *root_ptp = NULL;
+ port_priority_vector_t root_path_priority;
+ bridge_identifier_t prevRRootID = tree->rootPriority.RRootID;
+ __be32 prevExtRootPathCost = tree->rootPriority.ExtRootPathCost;
+ bool cist = (0 == tree->MSTID);
+ times_t *timesOfRootPort;
+
+ /* a), b) Select new root priority vector = {rootPriority, rootPortId} */
+ /* Initial value = bridge priority vector = {BridgePriority, 0} */
+ assign(tree->rootPriority, tree->BridgePriority);
+ assign(tree->rootPortId, __constant_cpu_to_be16(0));
+ /* Now check root path priority vectors of all ports in tree and see if
+ * there is a better vector */
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ {
+ port_t *prt = ptp->port;
+ /* 802.1Q says to calculate root priority vector only if port
+ * is not Disabled, but check (infoIs == ioReceived) covers
+ * the case (infoIs != ioDisabled).
+ */
+ if((ioReceived == ptp->infoIs) && !prt->restrictedRole
+ && cmp(ptp->portPriority.DesignatedBridgeID, !=,
+ tree->BridgeIdentifier)
+ )
+ {
+ root_path_priority = ptp->portPriority;
+ if(prt->rcvdInternal)
+ {
+ assign(root_path_priority.IntRootPathCost,
+ __cpu_to_be32(__be32_to_cpu(root_path_priority.IntRootPathCost)
+ + ptp->InternalPortPathCost)
+ );
+ }
+ else
+ {
+ assign(root_path_priority.ExtRootPathCost,
+ __cpu_to_be32(__be32_to_cpu(root_path_priority.ExtRootPathCost)
+ + prt->ExternalPortPathCost)
+ );
+ assign(root_path_priority.IntRootPathCost,
+ __constant_cpu_to_be32(0));
+ }
+ if(betterorsamePriority(&root_path_priority, &tree->rootPriority,
+ ptp->portId, tree->rootPortId, cist))
+ {
+ assign(tree->rootPriority, root_path_priority);
+ assign(tree->rootPortId, ptp->portId);
+ root_ptp = ptp;
+ }
+ }
+ }
+
+ /* 802.1q-2005 says, that at some point we need compare portTimes with
+ * "... one for the Root Port ...". Bad IEEE! Why not mention explicit
+ * var names??? (see 13.26.23.g) for instance)
+ * So, now I should guess what will work for the timesOfRootPort.
+ * Below is the result of my guess. I could be wrong, of course:
+ * timesOfRootPort = root_ptp ? &root_ptp->portTimes
+ * : &tree->BridgeTimes;
+ * NOTE: Both Alex Rozin (author of rstplib) and Srinivas Aji (author
+ * of rstpd) compare portTimes with designatedTimes instead of
+ * timesOfRootPort. This differs from my interpretation of the standard
+ * because designatedTimes have incremented Message_Age (or decremented
+ * remainingHops if rcvdInternal).
+ */
+
+ /* c) Set new rootTimes */
+ if(root_ptp)
+ {
+ assign(tree->rootTimes, root_ptp->portTimes);
+ port_t *prt = root_ptp->port;
+ if(prt->rcvdInternal)
+ {
+ if(tree->rootTimes.remainingHops)
+ --(tree->rootTimes.remainingHops);
+ }
+ else
+ assign(tree->rootTimes.Message_Age,
+ __cpu_to_be16(__be16_to_cpu(tree->rootTimes.Message_Age)
+ + 1)
+ );
+ timesOfRootPort = &root_ptp->portTimes;
+ }
+ else
+ {
+ assign(tree->rootTimes, tree->BridgeTimes);
+ timesOfRootPort = &tree->BridgeTimes;
+ }
+
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ {
+ port_t *prt = ptp->port;
+
+ /* d) Set new designatedPriority */
+ assign(ptp->designatedPriority, tree->rootPriority);
+ assign(ptp->designatedPriority.DesignatedBridgeID,
+ tree->BridgeIdentifier);
+ assign(ptp->designatedPriority.DesignatedPortID, ptp->portId);
+ /* I am not sure which condition to check here, as 802.1Q-2005 says:
+ * "... If {Port} is attached to a LAN that has one or more STP Bridges
+ * attached (as determined by the Port Protocol Migration state
+ * machine) ..." -- why not to mention explicit var name? Bad IEEE.
+ * But I guess that sendSTP (i.e. !sendRSTP) var will do ;)
+ */
+ if(cist && !prt->sendRSTP)
+ assign(ptp->designatedPriority.RRootID, tree->BridgeIdentifier);
+
+ /* e) Set new designatedTimes */
+ assign(ptp->designatedTimes, tree->rootTimes);
+ }
+
+ /* syncMaster */
+ if(cist && cmp(tree->rootPriority.RRootID, !=, prevRRootID)
+ && ((0 != tree->rootPriority.ExtRootPathCost)
+ || (0 != prevExtRootPathCost)
+ )
+ )
+ syncMaster(tree->bridge);
+
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ {
+ port_t *prt = ptp->port;
+
+ /* f) Set Disabled role */
+ if(ioDisabled == ptp->infoIs)
+ {
+ assign(ptp->selectedRole, roleDisabled);
+ continue;
+ }
+
+ if(!cist && (ioReceived == ptp->infoIs)
+ && !prt->infoInternal)
+ {
+ /* g) Set role for the boundary port in MSTI */
+ per_tree_port_t *cist_tree = GET_CIST_PTP_FROM_PORT(prt);
+ if(roleRoot == cist_tree->selectedRole)
+ {
+ assign(ptp->selectedRole, roleMaster);
+ if(!samePriorityAndTimers(&ptp->portPriority,
+ &ptp->designatedPriority,
+ &ptp->portTimes,
+ timesOfRootPort,
+ /*cist*/ false))
+ assign(ptp->updtInfo, boolTrue);
+ continue;
+ }
+ if(roleAlternate == cist_tree->selectedRole)
+ {
+ assign(ptp->selectedRole, roleAlternate);
+ if(!samePriorityAndTimers(&ptp->portPriority,
+ &ptp->designatedPriority,
+ &ptp->portTimes,
+ timesOfRootPort,
+ /*cist*/ false))
+ assign(ptp->updtInfo, boolTrue);
+ continue;
+ }
+ }
+ else /* if(cist || (ioReceived != ptp->infoIs) || prt->infoInternal) */
+ {
+ /* h) Set role for the aged info */
+ if(ioAged == ptp->infoIs)
+ {
+ assign(ptp->selectedRole, roleDesignated);
+ assign(ptp->updtInfo, boolTrue);
+ continue;
+ }
+ /* i) Set role for the mine info */
+ if(ioMine == ptp->infoIs)
+ {
+ assign(ptp->selectedRole, roleDesignated);
+ if(!samePriorityAndTimers(&ptp->portPriority,
+ &ptp->designatedPriority,
+ &ptp->portTimes,
+ timesOfRootPort,
+ /*cist*/ false))
+ assign(ptp->updtInfo, boolTrue);
+ continue;
+ }
+ if(ioReceived == ptp->infoIs)
+ {
+ /* j) Set Root role */
+ if(root_ptp == ptp)
+ {
+ assign(ptp->selectedRole, roleRoot);
+ assign(ptp->updtInfo, boolFalse);
+ continue;
+ }
+ if(betterorsamePriority(&ptp->portPriority,
+ &ptp->designatedPriority,
+ 0, 0, cist))
+ {
+ if(cmp(ptp->portPriority.DesignatedBridgeID, !=,
+ tree->BridgeIdentifier))
+ {
+ /* k) Set Alternate role */
+ assign(ptp->selectedRole, roleAlternate);
+ }
+ else
+ {
+ /* l) Set Backup role */
+ assign(ptp->selectedRole, roleBackup);
+ }
+ /* reset updtInfo for both k) and l) */
+ assign(ptp->updtInfo, boolFalse);
+ continue;
+ }
+ else /* designatedPriority is better than portPriority */
+ {
+ /* m) Set Designated role */
+ assign(ptp->selectedRole, roleDesignated);
+ assign(ptp->updtInfo, boolTrue);
+ continue;
+ }
+ }
+ }
+ }
+}
+
+/* 13.27 The Port Timers state machine */
+
+static void PTSM_tick(port_t *prt)
+{
+ per_tree_port_t *ptp;
+
+ if(prt->helloWhen)
+ --(prt->helloWhen);
+ if(prt->mdelayWhile)
+ --(prt->mdelayWhile);
+ if(prt->edgeDelayWhile)
+ --(prt->edgeDelayWhile);
+ if(prt->txCount)
+ --(prt->txCount);
+
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ {
+ if(ptp->fdWhile)
+ --(ptp->fdWhile);
+ if(ptp->rrWhile)
+ --(ptp->rrWhile);
+ if(ptp->rbWhile)
+ --(ptp->rbWhile);
+ if(ptp->tcWhile)
+ {
+ if(0 == --(ptp->tcWhile))
+ set_TopologyChange(ptp->tree, false);
+ }
+ if(ptp->rcvdInfoWhile)
+ --(ptp->rcvdInfoWhile);
+ }
+}
+
+/* 13.28 Port Receive state machine */
+#define PRSM_begin(prt) PRSM_to_DISCARD(prt)
+static void PRSM_to_DISCARD(port_t *prt/*, bool begin*/)
+{
+ prt->PRSM_state = PRSM_DISCARD;
+
+ assign(prt->rcvdBpdu, boolFalse);
+ assign(prt->rcvdRSTP, boolFalse);
+ assign(prt->rcvdSTP, boolFalse);
+ clearAllRcvdMsgs(prt);
+ assign(prt->edgeDelayWhile, prt->bridge->Migrate_Time);
+
+ /* No need to run, no one condition will be met
+ * if(!begin)
+ * PRSM_run(prt); */
+}
+
+static void PRSM_to_RECEIVE(port_t *prt)
+{
+ prt->PRSM_state = PRSM_RECEIVE;
+
+ updtBPDUVersion(prt);
+ prt->rcvdInternal = fromSameRegion(prt);
+ setRcvdMsgs(prt);
+ assign(prt->operEdge, boolFalse);
+ assign(prt->rcvdBpdu, boolFalse);
+ assign(prt->edgeDelayWhile, prt->bridge->Migrate_Time);
+
+ /* No need to run, no one condition will be met
+ PRSM_run(prt); */
+}
+
+static void PRSM_run(port_t *prt)
+{
+ per_tree_port_t *ptp;
+ bool rcvdAnyMsg;
+
+ if((prt->rcvdBpdu || (prt->edgeDelayWhile != prt->bridge->Migrate_Time))
+ && !prt->portEnabled)
+ {
+ PRSM_to_DISCARD(prt);
+ return;
+ }
+
+ switch(prt->PRSM_state)
+ {
+ case PRSM_DISCARD:
+ if(prt->rcvdBpdu && prt->portEnabled)
+ PRSM_to_RECEIVE(prt);
+ return;
+ case PRSM_RECEIVE:
+ rcvdAnyMsg = false;
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ {
+ if(ptp->rcvdMsg)
+ {
+ rcvdAnyMsg = true;
+ break;
+ }
+ }
+ if(prt->rcvdBpdu && prt->portEnabled && !rcvdAnyMsg)
+ PRSM_to_RECEIVE(prt);
+ return;
+ }
+}
+
+/* 13.29 Port Protocol Migration state machine */
+
+static void PPMSM_run(port_t *prt);
+#define PPMSM_begin(prt) PPMSM_to_CHECKING_RSTP(prt)
+
+static void PPMSM_to_CHECKING_RSTP(port_t *prt/*, bool begin*/)
+{
+ prt->PPMSM_state = PPMSM_CHECKING_RSTP;
+
+ bridge_t *br = prt->bridge;
+ assign(prt->mcheck, boolFalse);
+ prt->sendRSTP = rstpVersion(br);
+ assign(prt->mdelayWhile, br->Migrate_Time);
+
+ /* No need to run, no one condition will be met
+ * if(!begin)
+ * PPMSM_run(prt); */
+}
+
+static void PPMSM_to_SELECTING_STP(port_t *prt)
+{
+ prt->PPMSM_state = PPMSM_SELECTING_STP;
+
+ assign(prt->sendRSTP, boolFalse);
+ assign(prt->mdelayWhile, prt->bridge->Migrate_Time);
+
+ PPMSM_run(prt);
+}
+
+static void PPMSM_to_SENSING(port_t *prt)
+{
+ prt->PPMSM_state = PPMSM_SENSING;
+
+ assign(prt->rcvdRSTP, boolFalse);
+ assign(prt->rcvdSTP, boolFalse);
+
+ PPMSM_run(prt);
+}
+
+static void PPMSM_run(port_t *prt)
+{
+ bridge_t *br = prt->bridge;
+
+ switch(prt->PPMSM_state)
+ {
+ case PPMSM_CHECKING_RSTP:
+ if((prt->mdelayWhile != br->Migrate_Time)
+ && !prt->portEnabled)
+ {
+ PPMSM_to_CHECKING_RSTP(prt);
+ return;
+ }
+ if(0 == prt->mdelayWhile)
+ PPMSM_to_SENSING(prt);
+ return;
+ case PPMSM_SELECTING_STP:
+ if(0 == prt->mdelayWhile || !prt->portEnabled || prt->mcheck)
+ PPMSM_to_SENSING(prt);
+ return;
+ case PPMSM_SENSING:
+ if(!prt->portEnabled || prt->mcheck
+ || (rstpVersion(br) && !prt->sendRSTP && prt->rcvdRSTP))
+ {
+ PPMSM_to_CHECKING_RSTP(prt);
+ return;
+ }
+ if(prt->sendRSTP && prt->rcvdSTP)
+ PPMSM_to_SELECTING_STP(prt);
+ return;
+ }
+}
+
+/* 13.30 Bridge Detection state machine */
+static void BDSM_to_EDGE(port_t *prt/*, bool begin*/)
+{
+ prt->BDSM_state = BDSM_EDGE;
+
+ assign(prt->operEdge, boolTrue);
+
+ /* No need to run, no one condition will be met
+ * if(!begin)
+ * BDSM_run(prt); */
+}
+
+static void BDSM_to_NOT_EDGE(port_t *prt/*, bool begin*/)
+{
+ prt->BDSM_state = BDSM_NOT_EDGE;
+
+ assign(prt->operEdge, boolFalse);
+
+ /* No need to run, no one condition will be met
+ * if(!begin)
+ * BDSM_run(prt); */
+}
+
+static void BDSM_begin(port_t *prt/*, bool begin*/)
+{
+ if(prt->AdminEdgePort)
+ BDSM_to_EDGE(prt/*, begin*/);
+ else
+ BDSM_to_NOT_EDGE(prt/*, begin*/);
+}
+
+static void BDSM_run(port_t *prt)
+{
+ per_tree_port_t *cist;
+
+ switch(prt->BDSM_state)
+ {
+ case BDSM_EDGE:
+ if((!prt->portEnabled && !prt->AdminEdgePort) || !prt->operEdge)
+ BDSM_to_NOT_EDGE(prt);
+ return;
+ case BDSM_NOT_EDGE:
+ cist = GET_CIST_PTP_FROM_PORT(prt);
+ /* NOTE: 802.1Q-2005 is not clear, which of the per-tree
+ * "proposing" flags to use here, or one should combine
+ * them all for all trees? Maybe 802.1Q-2011 clarifies
+ * this, but I don't have the text.
+ * So, I decide that it will be the "proposing" flag
+ * from CIST tree - it seems like a good bet.
+ */
+ if((!prt->portEnabled && prt->AdminEdgePort)
+ || ((0 == prt->edgeDelayWhile) && prt->AutoEdge && prt->sendRSTP
+ && cist->proposing)
+ )
+ BDSM_to_EDGE(prt);
+ return;
+ }
+}
+
+/* 13.31 Port Transmit state machine */
+
+static void PTSM_run(port_t *prt);
+#define PTSM_begin(prt) PTSM_to_TRANSMIT_INIT((prt), true)
+
+static void PTSM_to_TRANSMIT_INIT(port_t *prt, bool begin)
+{
+ prt->PTSM_state = PTSM_TRANSMIT_INIT;
+
+ assign(prt->newInfo, boolTrue);
+ assign(prt->newInfoMsti, boolTrue);
+ assign(prt->txCount, 0u);
+
+ if(!begin && prt->portEnabled) /* prevent infinite loop */
+ PTSM_run(prt);
+}
+
+static void PTSM_to_TRANSMIT_CONFIG(port_t *prt)
+{
+ prt->PTSM_state = PTSM_TRANSMIT_CONFIG;
+
+ assign(prt->newInfo, boolFalse);
+ txConfig(prt);
+ ++(prt->txCount);
+ assign(prt->tcAck, boolFalse);
+
+ PTSM_run(prt);
+}
+
+static void PTSM_to_TRANSMIT_TCN(port_t *prt)
+{
+ prt->PTSM_state = PTSM_TRANSMIT_TCN;
+
+ assign(prt->newInfo, boolFalse);
+ txTcn(prt);
+ ++(prt->txCount);
+
+ PTSM_run(prt);
+}
+
+static void PTSM_to_TRANSMIT_RSTP(port_t *prt)
+{
+ prt->PTSM_state = PTSM_TRANSMIT_RSTP;
+
+ assign(prt->newInfo, boolFalse);
+ assign(prt->newInfoMsti, boolFalse);
+ txMstp(prt);
+ ++(prt->txCount);
+ assign(prt->tcAck, boolFalse);
+
+ PTSM_run(prt);
+}
+
+static void PTSM_to_TRANSMIT_PERIODIC(port_t *prt)
+{
+ prt->PTSM_state = PTSM_TRANSMIT_PERIODIC;
+
+ per_tree_port_t *ptp = GET_CIST_PTP_FROM_PORT(prt);
+ bool cistDesignatedOrTCpropagatingRootPort =
+ (roleDesignated == ptp->role)
+ || ((roleRoot == ptp->role) && (0 != ptp->tcWhile));
+ bool mstiDesignatedOrTCpropagatingRootPort;
+
+ mstiDesignatedOrTCpropagatingRootPort = false;
+ list_for_each_entry_continue(ptp, &prt->trees, port_list)
+ {
+ if((roleDesignated == ptp->role)
+ || ((roleRoot == ptp->role) && (0 != ptp->tcWhile))
+ )
+ {
+ mstiDesignatedOrTCpropagatingRootPort = true;
+ break;
+ }
+ }
+
+ prt->newInfo = prt->newInfo || cistDesignatedOrTCpropagatingRootPort;
+ prt->newInfoMsti = prt->newInfoMsti
+ || mstiDesignatedOrTCpropagatingRootPort;
+
+ PTSM_run(prt);
+}
+
+static void PTSM_to_IDLE(port_t *prt)
+{
+ prt->PTSM_state = PTSM_IDLE;
+
+ per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
+ prt->helloWhen = __be16_to_cpu(cist->portTimes.Hello_Time);
+
+ PTSM_run(prt);
+}
+
+static void PTSM_run(port_t *prt)
+{
+ /* bool allTransmitReady; */
+ per_tree_port_t *ptp;
+ port_role_t cistRole;
+ bool mstiMasterPort;
+
+ if(!prt->portEnabled)
+ {
+ PTSM_to_TRANSMIT_INIT(prt, false);
+ return;
+ }
+
+ switch(prt->PTSM_state)
+ {
+ case PTSM_TRANSMIT_INIT:
+ /* return; */
+ case PTSM_TRANSMIT_CONFIG:
+ /* return; */
+ case PTSM_TRANSMIT_TCN:
+ /* return; */
+ case PTSM_TRANSMIT_RSTP:
+ /* return; */
+ case PTSM_TRANSMIT_PERIODIC:
+ PTSM_to_IDLE(prt); /* UnConditional Transition */
+ return;
+ case PTSM_IDLE:
+ /* allTransmitReady = true; */
+ ptp = GET_CIST_PTP_FROM_PORT(prt);
+ if(!ptp->selected || ptp->updtInfo)
+ {
+ /* allTransmitReady = false; */
+ return;
+ }
+ assign(cistRole, ptp->role);
+ mstiMasterPort = false;
+ list_for_each_entry_continue(ptp, &prt->trees, port_list)
+ {
+ if(!ptp->selected || ptp->updtInfo)
+ {
+ /* allTransmitReady = false; */
+ return;
+ }
+ if(roleMaster == ptp->role)
+ mstiMasterPort = true;
+ }
+ if(0 == prt->helloWhen)
+ {
+ PTSM_to_TRANSMIT_PERIODIC(prt);
+ return;
+ }
+ if(!(prt->txCount < prt->bridge->Transmit_Hold_Count))
+ return;
+ if(prt->sendRSTP)
+ { /* implement MSTP */
+ if(prt->newInfo || (prt->newInfoMsti && !mstiMasterPort))
+ {
+ PTSM_to_TRANSMIT_RSTP(prt);
+ return;
+ }
+ }
+ else
+ { /* fallback to STP */
+ if(prt->newInfo && (roleDesignated == cistRole))
+ {
+ PTSM_to_TRANSMIT_CONFIG(prt);
+ return;
+ }
+ if(prt->newInfo && (roleRoot == cistRole))
+ {
+ PTSM_to_TRANSMIT_TCN(prt);
+ return;
+ }
+ }
+ return;
+ }
+}
+
+/* 13.32 Port Information state machine */
+
+#ifdef PISM_ENABLE_LOG
+#define PISM_LOG(_fmt, _args...) SMLOG_MSTINAME(ptp, _fmt, ##_args)
+#else
+#define PISM_LOG(_fmt, _args...) {}
+#endif /* PISM_ENABLE_LOG */
+
+static void PISM_run(per_tree_port_t *ptp);
+#define PISM_begin(ptp) PISM_to_DISABLED((ptp), true)
+
+static void PISM_to_DISABLED(per_tree_port_t *ptp, bool begin)
+{
+ PISM_LOG("");
+ ptp->PISM_state = PISM_DISABLED;
+
+ assign(ptp->rcvdMsg, boolFalse);
+ assign(ptp->proposing, boolFalse);
+ assign(ptp->proposed, boolFalse);
+ assign(ptp->agree, boolFalse);
+ assign(ptp->agreed, boolFalse);
+ assign(ptp->rcvdInfoWhile, 0u);
+ assign(ptp->infoIs, ioDisabled);
+ assign(ptp->reselect, boolTrue);
+ assign(ptp->selected, boolFalse);
+
+ if(!begin)
+ PISM_run(ptp);
+}
+
+static void PISM_to_AGED(per_tree_port_t *ptp)
+{
+ PISM_LOG("");
+ ptp->PISM_state = PISM_AGED;
+
+ assign(ptp->infoIs, ioAged);
+ assign(ptp->reselect, boolTrue);
+ assign(ptp->selected, boolFalse);
+
+ PISM_run(ptp);
+}
+
+static void PISM_to_UPDATE(per_tree_port_t *ptp)
+{
+ PISM_LOG("");
+ ptp->PISM_state = PISM_UPDATE;
+
+ assign(ptp->proposing, boolFalse);
+ assign(ptp->proposed, boolFalse);
+ ptp->agreed = ptp->agreed && betterorsameInfo(ptp, ioMine);
+ ptp->synced = ptp->synced && ptp->agreed;
+ assign(ptp->portPriority, ptp->designatedPriority);
+ assign(ptp->portTimes, ptp->designatedTimes);
+ assign(ptp->updtInfo, boolFalse);
+ assign(ptp->infoIs, ioMine);
+ /* newInfoXst = TRUE; */
+ port_t *prt = ptp->port;
+ if(0 == ptp->MSTID)
+ assign(prt->newInfo, boolTrue);
+ else
+ assign(prt->newInfoMsti, boolTrue);
+
+ PISM_run(ptp);
+}
+
+static void PISM_to_SUPERIOR_DESIGNATED(per_tree_port_t *ptp)
+{
+ PISM_LOG("");
+ ptp->PISM_state = PISM_SUPERIOR_DESIGNATED;
+
+ port_t *prt = ptp->port;
+
+ assign(prt->infoInternal, prt->rcvdInternal);
+ assign(ptp->agreed, boolFalse);
+ assign(ptp->proposing, boolFalse);
+ recordProposal(ptp);
+ setTcFlags(ptp);
+ ptp->agree = ptp->agree && betterorsameInfo(ptp, ioReceived);
+ recordAgreement(ptp);
+ ptp->synced = ptp->synced && ptp->agreed;
+ recordPriority(ptp);
+ recordTimes(ptp);
+ updtRcvdInfoWhile(ptp);
+ assign(ptp->infoIs, ioReceived);
+ assign(ptp->reselect, boolTrue);
+ assign(ptp->selected, boolFalse);
+ assign(ptp->rcvdMsg, boolFalse);
+
+ PISM_run(ptp);
+}
+
+static void PISM_to_REPEATED_DESIGNATED(per_tree_port_t *ptp)
+{
+ PISM_LOG("");
+ ptp->PISM_state = PISM_REPEATED_DESIGNATED;
+
+ port_t *prt = ptp->port;
+
+ assign(prt->infoInternal, prt->rcvdInternal);
+ recordProposal(ptp);
+ setTcFlags(ptp);
+ recordAgreement(ptp);
+ updtRcvdInfoWhile(ptp);
+ assign(ptp->rcvdMsg, boolFalse);
+
+ PISM_run(ptp);
+}
+
+static void PISM_to_INFERIOR_DESIGNATED(per_tree_port_t *ptp)
+{
+ PISM_LOG("");
+ ptp->PISM_state = PISM_INFERIOR_DESIGNATED;
+
+ recordDispute(ptp);
+ assign(ptp->rcvdMsg, boolFalse);
+
+ PISM_run(ptp);
+}
+
+static void PISM_to_NOT_DESIGNATED(per_tree_port_t *ptp)
+{
+ PISM_LOG("");
+ ptp->PISM_state = PISM_NOT_DESIGNATED;
+
+ recordAgreement(ptp);
+ setTcFlags(ptp);
+ assign(ptp->rcvdMsg, boolFalse);
+
+ PISM_run(ptp);
+}
+
+static void PISM_to_OTHER(per_tree_port_t *ptp)
+{
+ PISM_LOG("");
+ ptp->PISM_state = PISM_OTHER;
+
+ assign(ptp->rcvdMsg, boolFalse);
+
+ PISM_run(ptp);
+}
+
+static void PISM_to_CURRENT(per_tree_port_t *ptp)
+{
+ PISM_LOG("");
+ ptp->PISM_state = PISM_CURRENT;
+
+ PISM_run(ptp);
+}
+
+static void PISM_to_RECEIVE(per_tree_port_t *ptp)
+{
+ PISM_LOG("");
+ ptp->PISM_state = PISM_RECEIVE;
+
+ ptp->rcvdInfo = rcvInfo(ptp);
+ recordMastered(ptp);
+
+ PISM_run(ptp);
+}
+
+static void PISM_run(per_tree_port_t *ptp)
+{
+ bool rcvdXstMsg, updtXstInfo;
+ port_t *prt = ptp->port;
+
+ if((!prt->portEnabled) && (ioDisabled != ptp->infoIs))
+ {
+ PISM_to_DISABLED(ptp, false);
+ return;
+ }
+
+ switch(ptp->PISM_state)
+ {
+ case PISM_DISABLED:
+ if(prt->portEnabled)
+ {
+ PISM_to_AGED(ptp);
+ return;
+ }
+ if(ptp->rcvdMsg)
+ PISM_to_DISABLED(ptp, false);
+ return;
+ case PISM_AGED:
+ if(ptp->selected && ptp->updtInfo)
+ PISM_to_UPDATE(ptp);
+ return;
+ case PISM_UPDATE:
+ /* return; */
+ case PISM_SUPERIOR_DESIGNATED:
+ /* return; */
+ case PISM_REPEATED_DESIGNATED:
+ /* return; */
+ case PISM_INFERIOR_DESIGNATED:
+ /* return; */
+ case PISM_NOT_DESIGNATED:
+ /* return; */
+ case PISM_OTHER:
+ PISM_to_CURRENT(ptp);
+ return;
+ case PISM_CURRENT:
+ /*
+ * Although 802.1Q-2005 does not define rcvdXstMsg and updtXstInfo
+ * from 802.1s we can conclude that they are:
+ * - rcvdXstMsg = rcvdCistMsg, if tree is CIST
+ * rcvdMstiMsg, if tree is MSTI.
+ * - updtXstInfo = updtCistInfo, if tree is CIST
+ * updtMstiInfo, if tree is MSTI.
+ */
+ if(0 == ptp->MSTID)
+ { /* CIST */
+ rcvdXstMsg = ptp->rcvdMsg; /* 13.25.12 */
+ updtXstInfo = ptp->updtInfo; /* 13.25.16 */
+ }
+ else
+ { /* MSTI */
+ per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
+ rcvdXstMsg = !cist->rcvdMsg && ptp->rcvdMsg; /* 13.25.13 */
+ updtXstInfo = ptp->updtInfo || cist->updtInfo; /* 13.25.17 */
+ }
+ if(rcvdXstMsg && !updtXstInfo)
+ {
+ PISM_to_RECEIVE(ptp);
+ return;
+ }
+ if((ioReceived == ptp->infoIs) && (0 == ptp->rcvdInfoWhile)
+ && !ptp->updtInfo && !rcvdXstMsg)
+ {
+ PISM_to_AGED(ptp);
+ return;
+ }
+ if(ptp->selected && ptp->updtInfo)
+ PISM_to_UPDATE(ptp);
+ return;
+ case PISM_RECEIVE:
+ switch(ptp->rcvdInfo)
+ {
+ case SuperiorDesignatedInfo:
+ PISM_to_SUPERIOR_DESIGNATED(ptp);
+ return;
+ case RepeatedDesignatedInfo:
+ PISM_to_REPEATED_DESIGNATED(ptp);
+ return;
+ case InferiorDesignatedInfo:
+ PISM_to_INFERIOR_DESIGNATED(ptp);
+ return;
+ case InferiorRootAlternateInfo:
+ PISM_to_NOT_DESIGNATED(ptp);
+ return;
+ case OtherInfo:
+ PISM_to_OTHER(ptp);
+ return;
+ }
+ return;
+ }
+}
+
+/* 13.33 Port Role Selection state machine */
+
+static void PRSSM_run(tree_t *tree);
+#define PRSSM_begin(tree) PRSSM_to_INIT_TREE(tree)
+
+static void PRSSM_to_INIT_TREE(tree_t *tree/*, bool begin*/)
+{
+ tree->PRSSM_state = PRSSM_INIT_TREE;
+
+ updtRolesDisabledTree(tree);
+
+ /* No need to check, as we assume begin = true here
+ * because transition to this state can be initiated only by BEGIN var.
+ * In other words, this function is called via xxx_begin macro only.
+ * if(!begin)
+ * PRSSM_run(prt); */
+}
+
+static void PRSSM_to_ROLE_SELECTION(tree_t *tree)
+{
+ tree->PRSSM_state = PRSSM_ROLE_SELECTION;
+
+ clearReselectTree(tree);
+ updtRolesTree(tree);
+ setSelectedTree(tree);
+
+ /* No need to run, no one condition will be met
+ PRSSM_run(tree); */
+}
+
+static void PRSSM_run(tree_t *tree)
+{
+ per_tree_port_t *ptp;
+
+ switch(tree->PRSSM_state)
+ {
+ case PRSSM_INIT_TREE:
+ PRSSM_to_ROLE_SELECTION(tree);
+ return;
+ case PRSSM_ROLE_SELECTION:
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ if(ptp->reselect)
+ {
+ PRSSM_to_ROLE_SELECTION(tree);
+ return;
+ }
+ return;
+ }
+}
+
+/* 13.34 Port Role Transitions state machine */
+
+#ifdef PRTSM_ENABLE_LOG
+#define PRTSM_LOG(_fmt, _args...) SMLOG_MSTINAME(ptp, _fmt, ##_args)
+#else
+#define PRTSM_LOG(_fmt, _args...) {}
+#endif /* PRTSM_ENABLE_LOG */
+
+static void PRTSM_runr(per_tree_port_t *ptp, bool recursive_call);
+#define PRTSM_run(ptp) PRTSM_runr((ptp), false)
+#define PRTSM_begin(ptp) PRTSM_to_INIT_PORT(ptp)
+
+ /* Disabled Port role transitions */
+
+static void PRTSM_to_INIT_PORT(per_tree_port_t *ptp/*, bool begin*/)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_INIT_PORT;
+
+ unsigned int MaxAge, FwdDelay;
+ per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(ptp->port);
+
+ assign(ptp->role, roleDisabled);
+ assign(ptp->learn, boolFalse);
+ assign(ptp->forward, boolFalse);
+ assign(ptp->synced, boolFalse);
+ assign(ptp->sync, boolTrue);
+ assign(ptp->reRoot, boolTrue);
+ /* 13.25.6 */
+ FwdDelay = __be16_to_cpu(cist->designatedTimes.Forward_Delay);
+ assign(ptp->rrWhile, FwdDelay);
+ /* 13.25.8 */
+ MaxAge = __be16_to_cpu(cist->designatedTimes.Max_Age);
+ assign(ptp->fdWhile, MaxAge);
+ assign(ptp->rbWhile, 0u);
+
+ /* No need to check, as we assume begin = true here
+ * because transition to this state can be initiated only by BEGIN var.
+ * In other words, this function is called via xxx_begin macro only.
+ * if(!begin)
+ * PRTSM_runr(ptp, false); */
+}
+
+static void PRTSM_to_DISABLE_PORT(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_DISABLE_PORT;
+
+ /* Although 802.1Q-2005 says here to do role = selectedRole
+ * I have difficulties with it in the next scenario:
+ * 1) port was designated (role == selectedRole == roleDesignated)
+ * 2) some config event occurs, e.g. MAC address changes and
+ * br_state_machines_begin is called
+ * 3) role == selectedRole == roleDisabled, PRTSM_state = PRTSM_INIT_PORT
+ * Because port was not actually down, on the next run
+ * Port Role Selection state machine sets selectedRole = roleDesignated
+ * and updtInfo = true:
+ * 4) we have unconditional transition to DISABLE_PORT, and because
+ * updtInfo = true we can not follow transition to DESIGNATED_PORT
+ * 5) if we follow standard, role = selectedRole = roleDesignated and
+ * on the next run we have transition to the DISABLED_PORT
+ * And there we stuck. role == selectedRole, so we can not transit to
+ * DESIGNATED_PORT (it requires role != selectedRole ).
+ *
+ * Solution: do not follow the standard, and do role = roleDisabled
+ * instead of role = selectedRole.
+ */
+ assign(ptp->role, roleDisabled);
+ assign(ptp->learn, boolFalse);
+ assign(ptp->forward, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_DISABLED_PORT(per_tree_port_t *ptp, unsigned int MaxAge)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_DISABLED_PORT;
+
+ assign(ptp->fdWhile, MaxAge);
+ assign(ptp->synced, boolTrue);
+ assign(ptp->rrWhile, 0u);
+ assign(ptp->sync, boolFalse);
+ assign(ptp->reRoot, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+ /* MasterPort role transitions */
+
+static void PRTSM_to_MASTER_PROPOSED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_MASTER_PROPOSED;
+
+ setSyncTree(ptp->tree);
+ assign(ptp->proposed, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_MASTER_AGREED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_MASTER_AGREED;
+
+ assign(ptp->proposed, boolFalse);
+ assign(ptp->sync, boolFalse);
+ assign(ptp->agree, boolTrue);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_MASTER_SYNCED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_MASTER_SYNCED;
+
+ assign(ptp->rrWhile, 0u);
+ assign(ptp->synced, boolTrue);
+ assign(ptp->sync, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_MASTER_RETIRED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_MASTER_RETIRED;
+
+ assign(ptp->reRoot, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_MASTER_FORWARD(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_MASTER_FORWARD;
+
+ assign(ptp->forward, boolTrue);
+ assign(ptp->fdWhile, 0u);
+ assign(ptp->agreed, ptp->port->sendRSTP);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_MASTER_LEARN(per_tree_port_t *ptp, unsigned int forwardDelay)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_MASTER_LEARN;
+
+ assign(ptp->learn, boolTrue);
+ assign(ptp->fdWhile, forwardDelay);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_MASTER_DISCARD(per_tree_port_t *ptp, unsigned int forwardDelay)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_MASTER_DISCARD;
+
+ assign(ptp->learn, boolFalse);
+ assign(ptp->forward, boolFalse);
+ assign(ptp->disputed, boolFalse);
+ assign(ptp->fdWhile, forwardDelay);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_MASTER_PORT(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_MASTER_PORT;
+
+ assign(ptp->role, roleMaster);
+
+ PRTSM_runr(ptp, true);
+}
+
+ /* RootPort role transitions */
+
+static void PRTSM_to_ROOT_PROPOSED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_ROOT_PROPOSED;
+
+ setSyncTree(ptp->tree);
+ assign(ptp->proposed, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_ROOT_AGREED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_ROOT_AGREED;
+
+ assign(ptp->proposed, boolFalse);
+ assign(ptp->sync, boolFalse);
+ assign(ptp->agree, boolTrue);
+ /* newInfoXst = TRUE; */
+ port_t *prt = ptp->port;
+ if(0 == ptp->MSTID)
+ assign(prt->newInfo, boolTrue);
+ else
+ assign(prt->newInfoMsti, boolTrue);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_ROOT_SYNCED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_ROOT_SYNCED;
+
+ assign(ptp->synced, boolTrue);
+ assign(ptp->sync, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_REROOT(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_REROOT;
+
+ setReRootTree(ptp->tree);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_ROOT_FORWARD(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_ROOT_FORWARD;
+
+ assign(ptp->fdWhile, 0u);
+ assign(ptp->forward, boolTrue);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_ROOT_LEARN(per_tree_port_t *ptp, unsigned int forwardDelay)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_ROOT_LEARN;
+
+ assign(ptp->fdWhile, forwardDelay);
+ assign(ptp->learn, boolTrue);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_REROOTED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_REROOTED;
+
+ assign(ptp->reRoot, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_ROOT_PORT(per_tree_port_t *ptp, unsigned int FwdDelay)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_ROOT_PORT;
+
+ assign(ptp->role, roleRoot);
+ assign(ptp->rrWhile, FwdDelay);
+
+ PRTSM_runr(ptp, true);
+}
+
+ /* DesignatedPort role transitions */
+
+static void PRTSM_to_DESIGNATED_PROPOSE(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_DESIGNATED_PROPOSE;
+
+ port_t *prt = ptp->port;
+
+ assign(ptp->proposing, boolTrue);
+ /* newInfoXst = TRUE; */
+ if(0 == ptp->MSTID)
+ { /* CIST */
+ /* 13.25.8. This tree is CIST. */
+ unsigned int MaxAge = __be16_to_cpu(ptp->designatedTimes.Max_Age);
+ /* 13.25.c) -> 17.20.4 of 802.1D : EdgeDelay */
+ unsigned int EdgeDelay = prt->operPointToPointMAC ?
+ prt->bridge->Migrate_Time
+ : MaxAge;
+ assign(prt->edgeDelayWhile, EdgeDelay);
+ assign(prt->newInfo, boolTrue);
+ }
+ else
+ assign(prt->newInfoMsti, boolTrue);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_DESIGNATED_AGREED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_DESIGNATED_AGREED;
+
+ assign(ptp->proposed, boolFalse);
+ assign(ptp->sync, boolFalse);
+ assign(ptp->agree, boolTrue);
+ /* newInfoXst = TRUE; */
+ port_t *prt = ptp->port;
+ if(0 == ptp->MSTID)
+ assign(prt->newInfo, boolTrue);
+ else
+ assign(prt->newInfoMsti, boolTrue);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_DESIGNATED_SYNCED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_DESIGNATED_SYNCED;
+
+ assign(ptp->rrWhile, 0u);
+ assign(ptp->synced, boolTrue);
+ assign(ptp->sync, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_DESIGNATED_RETIRED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_DESIGNATED_RETIRED;
+
+ assign(ptp->reRoot, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_DESIGNATED_FORWARD(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_DESIGNATED_FORWARD;
+
+ assign(ptp->forward, boolTrue);
+ assign(ptp->fdWhile, 0u);
+ assign(ptp->agreed, ptp->port->sendRSTP);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_DESIGNATED_LEARN(per_tree_port_t *ptp, unsigned int forwardDelay)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_DESIGNATED_LEARN;
+
+ assign(ptp->learn, boolTrue);
+ assign(ptp->fdWhile, forwardDelay);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_DESIGNATED_DISCARD(per_tree_port_t *ptp, unsigned int forwardDelay)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_DESIGNATED_DISCARD;
+
+ assign(ptp->learn, boolFalse);
+ assign(ptp->forward, boolFalse);
+ assign(ptp->disputed, boolFalse);
+ assign(ptp->fdWhile, forwardDelay);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_DESIGNATED_PORT(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_DESIGNATED_PORT;
+
+ assign(ptp->role, roleDesignated);
+
+ PRTSM_runr(ptp, true);
+}
+
+ /* AlternatePort and BackupPort role transitions */
+
+static void PRTSM_to_BLOCK_PORT(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_BLOCK_PORT;
+
+ assign(ptp->role, ptp->selectedRole);
+ assign(ptp->learn, boolFalse);
+ assign(ptp->forward, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_BACKUP_PORT(per_tree_port_t *ptp, unsigned int HelloTime)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_BACKUP_PORT;
+
+ assign(ptp->rbWhile, 2 * HelloTime);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_ALTERNATE_PROPOSED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_ALTERNATE_PROPOSED;
+
+ setSyncTree(ptp->tree);
+ assign(ptp->proposed, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_ALTERNATE_AGREED(per_tree_port_t *ptp)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_ALTERNATE_AGREED;
+
+ assign(ptp->proposed, boolFalse);
+ assign(ptp->agree, boolTrue);
+ /* newInfoXst = TRUE; */
+ port_t *prt = ptp->port;
+ if(0 == ptp->MSTID)
+ assign(prt->newInfo, boolTrue);
+ else
+ assign(prt->newInfoMsti, boolTrue);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_to_ALTERNATE_PORT(per_tree_port_t *ptp, unsigned int forwardDelay)
+{
+ PRTSM_LOG("");
+ ptp->PRTSM_state = PRTSM_ALTERNATE_PORT;
+
+ assign(ptp->fdWhile, forwardDelay);
+ assign(ptp->synced, boolTrue);
+ assign(ptp->rrWhile, 0u);
+ assign(ptp->sync, boolFalse);
+ assign(ptp->reRoot, boolFalse);
+
+ PRTSM_runr(ptp, true);
+}
+
+static void PRTSM_runr(per_tree_port_t *ptp, bool recursive_call)
+{
+ /* Following vars do not need recalculating on recursive calls */
+ static unsigned int MaxAge, FwdDelay, forwardDelay, HelloTime;
+ static port_t *prt;
+ static tree_t *tree;
+ static per_tree_port_t *cist;
+ /* Following vars are recalculated on each state transition */
+ bool allSynced, reRooted;
+ /* Following vars are auxiliary and don't depend on recursive_call */
+ per_tree_port_t *ptp_1;
+
+ if(!recursive_call)
+ { /* calculate these intermediate vars only first time in chain of
+ * recursive calls */
+ prt = ptp->port;
+ tree = ptp->tree;
+
+ cist = GET_CIST_PTP_FROM_PORT(prt);
+
+ /* 13.25.6 */
+ FwdDelay = __be16_to_cpu(cist->designatedTimes.Forward_Delay);
+
+ /* 13.25.7 */
+ HelloTime = __be16_to_cpu(cist->portTimes.Hello_Time);
+
+ /* 13.25.d) -> 17.20.5 of 802.1D */
+ forwardDelay = prt->sendRSTP ? HelloTime : FwdDelay;
+
+ /* 13.25.8 */
+ MaxAge = __be16_to_cpu(cist->designatedTimes.Max_Age);
+ }
+
+ PRTSM_LOG("role = %d, selectedRole = %d, selected = %d, updtInfo = %d",
+ ptp->role, ptp->selectedRole, ptp->selected, ptp->updtInfo);
+ if((ptp->role != ptp->selectedRole) && ptp->selected && !ptp->updtInfo)
+ {
+ switch(ptp->selectedRole)
+ {
+ case roleDisabled:
+ PRTSM_to_DISABLE_PORT(ptp);
+ return;
+ case roleMaster:
+ PRTSM_to_MASTER_PORT(ptp);
+ return;
+ case roleRoot:
+ PRTSM_to_ROOT_PORT(ptp, FwdDelay);
+ return;
+ case roleDesignated:
+ PRTSM_to_DESIGNATED_PORT(ptp);
+ return;
+ case roleAlternate:
+ case roleBackup:
+ PRTSM_to_BLOCK_PORT(ptp);
+ return;
+ }
+ }
+
+ /* 13.25.1 */
+ allSynced = true;
+ FOREACH_PTP_IN_TREE(ptp_1, tree)
+ {
+ /* a) */
+ if(!ptp_1->selected
+ || (ptp_1->role != ptp_1->selectedRole)
+ || ptp_1->updtInfo
+ )
+ {
+ allSynced = false;
+ break;
+ }
+
+ /* b) */
+ switch(ptp->role)
+ {
+ case roleRoot:
+ case roleAlternate:
+ if((roleRoot != ptp_1->role) && !ptp_1->synced)
+ allSynced = false;
+ break;
+ case roleDesignated:
+ case roleMaster:
+ if((ptp != ptp_1) && !ptp_1->synced)
+ allSynced = false;
+ break;
+ default:
+ allSynced = false;
+ }
+ if(!allSynced)
+ break;
+ }
+
+ switch(ptp->PRTSM_state)
+ {
+ /* Disabled Port role transitions */
+ case PRTSM_INIT_PORT:
+ PRTSM_to_DISABLE_PORT(ptp);
+ return;
+ case PRTSM_DISABLE_PORT:
+ if(ptp->selected && !ptp->updtInfo
+ && !ptp->learning && !ptp->forwarding
+ )
+ PRTSM_to_DISABLED_PORT(ptp, MaxAge);
+ return;
+ case PRTSM_DISABLED_PORT:
+ if(ptp->selected && !ptp->updtInfo
+ && (ptp->sync || ptp->reRoot || !ptp->synced
+ || (ptp->fdWhile != MaxAge))
+ )
+ PRTSM_to_DISABLED_PORT(ptp, MaxAge);
+ return;
+ /* MasterPort role transitions */
+ case PRTSM_MASTER_PROPOSED:
+ /* return; */
+ case PRTSM_MASTER_AGREED:
+ /* return; */
+ case PRTSM_MASTER_SYNCED:
+ /* return; */
+ case PRTSM_MASTER_RETIRED:
+ /* return; */
+ case PRTSM_MASTER_FORWARD:
+ /* return; */
+ case PRTSM_MASTER_LEARN:
+ /* return; */
+ case PRTSM_MASTER_DISCARD:
+ PRTSM_to_MASTER_PORT(ptp);
+ return;
+ case PRTSM_MASTER_PORT:
+ if(!(ptp->selected && !ptp->updtInfo))
+ return;
+ if(ptp->reRoot && (0 == ptp->rrWhile))
+ {
+ PRTSM_to_MASTER_RETIRED(ptp);
+ return;
+ }
+ if((!ptp->learning && !ptp->forwarding && !ptp->synced)
+ || (ptp->agreed && !ptp->synced)
+ || (prt->operEdge && !ptp->synced)
+ || (ptp->sync && ptp->synced)
+ )
+ {
+ PRTSM_to_MASTER_SYNCED(ptp);
+ return;
+ }
+ if((allSynced && !ptp->agree)
+ || (ptp->proposed && ptp->agree)
+ )
+ {
+ PRTSM_to_MASTER_AGREED(ptp);
+ return;
+ }
+ if(ptp->proposed && !ptp->agree)
+ {
+ PRTSM_to_MASTER_PROPOSED(ptp);
+ return;
+ }
+ if(((0 == ptp->fdWhile) || allSynced)
+ && ptp->learn && !ptp->forward
+ )
+ {
+ PRTSM_to_MASTER_FORWARD(ptp);
+ return;
+ }
+ if(((0 == ptp->fdWhile) || allSynced)
+ && !ptp->learn
+ )
+ {
+ PRTSM_to_MASTER_LEARN(ptp, forwardDelay);
+ return;
+ }
+ if(((ptp->sync && !ptp->synced)
+ || (ptp->reRoot && (0 != ptp->rrWhile))
+ || ptp->disputed
+ )
+ && !prt->operEdge && (ptp->learn || ptp->forward)
+ )
+ {
+ PRTSM_to_MASTER_DISCARD(ptp, forwardDelay);
+ return;
+ }
+ return;
+ /* RootPort role transitions */
+ case PRTSM_ROOT_PROPOSED:
+ /* return; */
+ case PRTSM_ROOT_AGREED:
+ /* return; */
+ case PRTSM_ROOT_SYNCED:
+ /* return; */
+ case PRTSM_REROOT:
+ /* return; */
+ case PRTSM_ROOT_FORWARD:
+ /* return; */
+ case PRTSM_ROOT_LEARN:
+ /* return; */
+ case PRTSM_REROOTED:
+ PRTSM_to_ROOT_PORT(ptp, FwdDelay);
+ return;
+ case PRTSM_ROOT_PORT:
+ if(!(ptp->selected && !ptp->updtInfo))
+ return;
+ if(!ptp->forward && !ptp->reRoot)
+ {
+ PRTSM_to_REROOT(ptp);
+ return;
+ }
+ if((ptp->agreed && !ptp->synced) || (ptp->sync && ptp->synced))
+ {
+ PRTSM_to_ROOT_SYNCED(ptp);
+ return;
+ }
+ if((allSynced && !ptp->agree) || (ptp->proposed && ptp->agree))
+ {
+ PRTSM_to_ROOT_AGREED(ptp);
+ return;
+ }
+ if(ptp->proposed && !ptp->agree)
+ {
+ PRTSM_to_ROOT_PROPOSED(ptp);
+ return;
+ }
+ /* 17.20.10 of 802.1D : reRooted */
+ reRooted = true;
+ FOREACH_PTP_IN_TREE(ptp_1, tree)
+ {
+ if((ptp != ptp_1) && (0 != ptp_1->rrWhile))
+ {
+ reRooted = false;
+ break;
+ }
+ }
+ if((0 == ptp->fdWhile)
+ || (reRooted && (0 == ptp->rbWhile) && rstpVersion(prt->bridge))
+ )
+ {
+ if(!ptp->learn)
+ {
+ PRTSM_to_ROOT_LEARN(ptp, forwardDelay);
+ return;
+ }
+ else if(!ptp->forward)
+ {
+ PRTSM_to_ROOT_FORWARD(ptp);
+ return;
+ }
+ }
+ if(ptp->reRoot && ptp->forward)
+ {
+ PRTSM_to_REROOTED(ptp);
+ return;
+ }
+ if(ptp->rrWhile != FwdDelay)
+ {
+ PRTSM_to_ROOT_PORT(ptp, FwdDelay);
+ return;
+ }
+ return;
+ /* DesignatedPort role transitions */
+ case PRTSM_DESIGNATED_PROPOSE:
+ /* return; */
+ case PRTSM_DESIGNATED_AGREED:
+ /* return; */
+ case PRTSM_DESIGNATED_SYNCED:
+ /* return; */
+ case PRTSM_DESIGNATED_RETIRED:
+ /* return; */
+ case PRTSM_DESIGNATED_FORWARD:
+ /* return; */
+ case PRTSM_DESIGNATED_LEARN:
+ /* return; */
+ case PRTSM_DESIGNATED_DISCARD:
+ PRTSM_to_DESIGNATED_PORT(ptp);
+ return;
+ case PRTSM_DESIGNATED_PORT:
+ if(!(ptp->selected && !ptp->updtInfo))
+ return;
+ if(ptp->reRoot && (0 == ptp->rrWhile))
+ {
+ PRTSM_to_DESIGNATED_RETIRED(ptp);
+ return;
+ }
+ if((!ptp->learning && !ptp->forwarding && !ptp->synced)
+ || (ptp->agreed && !ptp->synced)
+ || (prt->operEdge && !ptp->synced)
+ || (ptp->sync && ptp->synced)
+ )
+ {
+ PRTSM_to_DESIGNATED_SYNCED(ptp);
+ return;
+ }
+ if(allSynced && (ptp->proposed || !ptp->agree))
+ {
+ PRTSM_to_DESIGNATED_AGREED(ptp);
+ return;
+ }
+ if(!ptp->forward && !ptp->agreed && !ptp->proposing
+ && !prt->operEdge)
+ {
+ PRTSM_to_DESIGNATED_PROPOSE(ptp);
+ return;
+ }
+ if(((0 == ptp->fdWhile) || ptp->agreed || prt->operEdge)
+ && ((0 == ptp->rrWhile) || !ptp->reRoot) && !ptp->sync
+ )
+ {
+ if(!ptp->learn)
+ {
+ PRTSM_to_DESIGNATED_LEARN(ptp, forwardDelay);
+ return;
+ }
+ else if(!ptp->forward)
+ {
+ PRTSM_to_DESIGNATED_FORWARD(ptp);
+ return;
+ }
+ }
+ if(((ptp->sync && !ptp->synced)
+ || (ptp->reRoot && (0 != ptp->rrWhile))
+ || ptp->disputed
+ )
+ && !prt->operEdge && (ptp->learn || ptp->forward)
+ )
+ {
+ PRTSM_to_DESIGNATED_DISCARD(ptp, forwardDelay);
+ return;
+ }
+ return;
+ /* AlternatePort and BackupPort role transitions */
+ case PRTSM_BLOCK_PORT:
+ if(ptp->selected && !ptp->updtInfo
+ && !ptp->learning && !ptp->forwarding
+ )
+ PRTSM_to_ALTERNATE_PORT(ptp, forwardDelay);
+ return;
+ case PRTSM_BACKUP_PORT:
+ /* return; */
+ case PRTSM_ALTERNATE_PROPOSED:
+ /* return; */
+ case PRTSM_ALTERNATE_AGREED:
+ PRTSM_to_ALTERNATE_PORT(ptp, forwardDelay);
+ return;
+ case PRTSM_ALTERNATE_PORT:
+ if(!(ptp->selected && !ptp->updtInfo))
+ return;
+ if((allSynced && !ptp->agree) || (ptp->proposed && ptp->agree))
+ {
+ PRTSM_to_ALTERNATE_AGREED(ptp);
+ return;
+ }
+ if(ptp->proposed && !ptp->agree)
+ {
+ PRTSM_to_ALTERNATE_PROPOSED(ptp);
+ return;
+ }
+ if((ptp->rbWhile != 2 * HelloTime) && (roleBackup == ptp->role))
+ {
+ PRTSM_to_BACKUP_PORT(ptp, HelloTime);
+ return;
+ }
+ if((ptp->fdWhile != forwardDelay) || ptp->sync || ptp->reRoot
+ || !ptp->synced)
+ {
+ PRTSM_to_ALTERNATE_PORT(ptp, forwardDelay);
+ return;
+ }
+ return;
+ }
+}
+
+/* 13.35 Port State Transition state machine */
+
+static void PSTSM_run(per_tree_port_t *ptp);
+#define PSTSM_begin(ptp) PSTSM_to_DISCARDING((ptp), true)
+
+static void PSTSM_to_DISCARDING(per_tree_port_t *ptp, bool begin)
+{
+ ptp->PSTSM_state = PSTSM_DISCARDING;
+
+ /* This effectively sets BLOCKING state:
+ disableLearning();
+ disableForwarding();
+ */
+ if(BR_STATE_BLOCKING != ptp->state)
+ MSTP_OUT_set_state(ptp, BR_STATE_BLOCKING);
+ assign(ptp->learning, boolFalse);
+ assign(ptp->forwarding, boolFalse);
+
+ if(!begin)
+ PSTSM_run(ptp);
+}
+
+static void PSTSM_to_LEARNING(per_tree_port_t *ptp)
+{
+ ptp->PSTSM_state = PSTSM_LEARNING;
+
+ /* enableLearning(); */
+ if(BR_STATE_LEARNING != ptp->state)
+ MSTP_OUT_set_state(ptp, BR_STATE_LEARNING);
+ assign(ptp->learning, boolTrue);
+
+ PSTSM_run(ptp);
+}
+
+static void PSTSM_to_FORWARDING(per_tree_port_t *ptp)
+{
+ ptp->PSTSM_state = PSTSM_FORWARDING;
+
+ /* enableForwarding(); */
+ if(BR_STATE_FORWARDING != ptp->state)
+ MSTP_OUT_set_state(ptp, BR_STATE_FORWARDING);
+ assign(ptp->forwarding, boolTrue);
+
+ /* No need to run, no one condition will be met
+ PSTSM_run(ptp); */
+}
+
+static void PSTSM_run(per_tree_port_t *ptp)
+{
+ switch(ptp->PSTSM_state)
+ {
+ case PSTSM_DISCARDING:
+ if(ptp->learn)
+ PSTSM_to_LEARNING(ptp);
+ return;
+ case PSTSM_LEARNING:
+ if(!ptp->learn)
+ PSTSM_to_DISCARDING(ptp, false);
+ else if(ptp->forward)
+ PSTSM_to_FORWARDING(ptp);
+ return;
+ case PSTSM_FORWARDING:
+ if(!ptp->forward)
+ PSTSM_to_DISCARDING(ptp, false);
+ return;
+ }
+}
+
+/* 13.36 Topology Change state machine */
+
+#define TCSM_begin(ptp) TCSM_to_INACTIVE((ptp), true)
+
+static void TCSM_to_INACTIVE(per_tree_port_t *ptp, bool begin)
+{
+ ptp->TCSM_state = TCSM_INACTIVE;
+
+ set_fdbFlush(ptp);
+ assign(ptp->tcWhile, 0u);
+ set_TopologyChange(ptp->tree, false);
+ if(0 == ptp->MSTID) /* CIST */
+ assign(ptp->port->tcAck, boolFalse);
+
+ if(!begin)
+ TCSM_run(ptp);
+}
+
+static void TCSM_to_LEARNING(per_tree_port_t *ptp)
+{
+ ptp->TCSM_state = TCSM_LEARNING;
+
+ if(0 == ptp->MSTID) /* CIST */
+ {
+ port_t *prt = ptp->port;
+ assign(prt->rcvdTcn, boolFalse);
+ assign(prt->rcvdTcAck, boolFalse);
+ }
+ assign(ptp->rcvdTc, boolFalse);
+ assign(ptp->tcProp, boolFalse);
+
+ TCSM_run(ptp);
+}
+
+static void TCSM_to_DETECTED(per_tree_port_t *ptp)
+{
+ ptp->TCSM_state = TCSM_DETECTED;
+
+ newTcWhile(ptp);
+ setTcPropTree(ptp);
+ /* newInfoXst = TRUE; */
+ port_t *prt = ptp->port;
+ if(0 == ptp->MSTID)
+ assign(prt->newInfo, boolTrue);
+ else
+ assign(prt->newInfoMsti, boolTrue);
+
+ TCSM_run(ptp);
+}
+
+static void TCSM_to_NOTIFIED_TCN(per_tree_port_t *ptp)
+{
+ ptp->TCSM_state = TCSM_NOTIFIED_TCN;
+
+ newTcWhile(ptp);
+
+ TCSM_run(ptp);
+}
+
+static void TCSM_to_NOTIFIED_TC(per_tree_port_t *ptp)
+{
+ ptp->TCSM_state = TCSM_NOTIFIED_TC;
+
+ assign(ptp->rcvdTc, boolFalse);
+ if(0 == ptp->MSTID) /* CIST */
+ {
+ port_t *prt = ptp->port;
+ assign(prt->rcvdTcn, boolFalse);
+ if(roleDesignated == ptp->role)
+ assign(prt->tcAck, boolTrue);
+ }
+ setTcPropTree(ptp);
+
+ TCSM_run(ptp);
+}
+
+static void TCSM_to_PROPAGATING(per_tree_port_t *ptp)
+{
+ ptp->TCSM_state = TCSM_PROPAGATING;
+
+ newTcWhile(ptp);
+ set_fdbFlush(ptp);
+ assign(ptp->tcProp, boolFalse);
+
+ TCSM_run(ptp);
+}
+
+static void TCSM_to_ACKNOWLEDGED(per_tree_port_t *ptp)
+{
+ ptp->TCSM_state = TCSM_ACKNOWLEDGED;
+
+ assign(ptp->tcWhile, 0u);
+ set_TopologyChange(ptp->tree, false);
+ assign(ptp->port->rcvdTcAck, boolFalse);
+
+ TCSM_run(ptp);
+}
+
+static void TCSM_to_ACTIVE(per_tree_port_t *ptp)
+{
+ ptp->TCSM_state = TCSM_ACTIVE;
+
+ TCSM_run(ptp);
+}
+
+static void TCSM_run(per_tree_port_t *ptp)
+{
+ bool active_port;
+ port_t *prt = ptp->port;
+
+ switch(ptp->TCSM_state)
+ {
+ case TCSM_INACTIVE:
+ if(ptp->learn && !ptp->fdbFlush)
+ TCSM_to_LEARNING(ptp);
+ return;
+ case TCSM_LEARNING:
+ active_port = (roleRoot == ptp->role)
+ || (roleDesignated == ptp->role)
+ || (roleMaster == ptp->role);
+ if(active_port && ptp->forward && !prt->operEdge)
+ {
+ TCSM_to_DETECTED(ptp);
+ return;
+ }
+ if(ptp->rcvdTc || prt->rcvdTcn || prt->rcvdTcAck || ptp->tcProp)
+ {
+ TCSM_to_LEARNING(ptp);
+ return;
+ }
+ else if(!active_port && !(ptp->learn || ptp->learning))
+ TCSM_to_INACTIVE(ptp, false);
+ return;
+ case TCSM_NOTIFIED_TCN:
+ TCSM_to_NOTIFIED_TC(ptp);
+ return;
+ case TCSM_DETECTED:
+ /* return; */
+ case TCSM_NOTIFIED_TC:
+ /* return; */
+ case TCSM_PROPAGATING:
+ /* return; */
+ case TCSM_ACKNOWLEDGED:
+ TCSM_to_ACTIVE(ptp);
+ return;
+ case TCSM_ACTIVE:
+ active_port = (roleRoot == ptp->role)
+ || (roleDesignated == ptp->role)
+ || (roleMaster == ptp->role);
+ if(!active_port || prt->operEdge)
+ {
+ TCSM_to_LEARNING(ptp);
+ return;
+ }
+ if(prt->rcvdTcn)
+ {
+ TCSM_to_NOTIFIED_TCN(ptp);
+ return;
+ }
+ if(ptp->rcvdTc)
+ {
+ TCSM_to_NOTIFIED_TC(ptp);
+ return;
+ }
+ if(ptp->tcProp/* && !prt->operEdge */)
+ {
+ TCSM_to_PROPAGATING(ptp);
+ return;
+ }
+ if(prt->rcvdTcAck)
+ {
+ TCSM_to_ACKNOWLEDGED(ptp);
+ return;
+ }
+ return;
+ }
+}
+
+/* Execute BEGIN state. We do not define BEGIN variable
+ * but instead xxx_state_machines_begin execute begin state
+ * abd do one step out of it
+ */
+
+static void tree_state_machines_begin(tree_t *tree)
+{
+ bridge_t *br = tree->bridge;
+ per_tree_port_t *ptp;
+
+ if(!br->bridgeEnabled)
+ return;
+
+ /* 13.32 Port Information state machine */
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ {
+ ptp->start_time = br->uptime; /* 12.8.2.2.3 b) */
+ PISM_begin(ptp);
+ }
+
+ /* 13.33 Port Role Selection state machine */
+ PRSSM_begin(tree);
+
+ /* 13.34 Port Role Transitions state machine */
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ PRTSM_begin(ptp);
+ /* 13.35 Port State Transition state machine */
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ PSTSM_begin(ptp);
+ /* 13.36 Topology Change state machine */
+ FOREACH_PTP_IN_TREE(ptp, tree)
+ TCSM_begin(ptp);
+
+ br_state_machines_run(br);
+}
+
+static void prt_state_machines_begin(port_t *prt)
+{
+ bridge_t *br = prt->bridge;
+ tree_t *tree;
+ per_tree_port_t *ptp;
+
+ if(!br->bridgeEnabled)
+ return;
+
+ /* 13.28 Port Receive state machine */
+ PRSM_begin(prt);
+ /* 13.29 Port Protocol Migration state machine */
+ PPMSM_begin(prt);
+ /* 13.30 Bridge Detection state machine */
+ BDSM_begin(prt);
+ /* 13.31 Port Transmit state machine */
+ PTSM_begin(prt);
+
+ /* 13.32 Port Information state machine */
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ {
+ ptp->start_time = br->uptime; /* 12.8.2.2.3 b) */
+ PISM_begin(ptp);
+ }
+
+ /* 13.33 Port Role Selection state machine */
+ FOREACH_TREE_IN_BRIDGE(tree, br)
+ PRSSM_run(tree);
+
+ /* 13.34 Port Role Transitions state machine */
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ PRTSM_begin(ptp);
+ /* 13.35 Port State Transition state machine */
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ PSTSM_begin(ptp);
+ /* 13.36 Topology Change state machine */
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ TCSM_begin(ptp);
+
+ br_state_machines_run(br);
+}
+
+static void br_state_machines_begin(bridge_t *br)
+{
+ port_t *prt;
+ per_tree_port_t *ptp;
+ tree_t *tree;
+
+ if(!br->bridgeEnabled)
+ return;
+
+ /* 13.28 Port Receive state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ PRSM_begin(prt);
+ /* 13.29 Port Protocol Migration state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ PPMSM_begin(prt);
+ /* 13.30 Bridge Detection state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ BDSM_begin(prt);
+ /* 13.31 Port Transmit state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ PTSM_begin(prt);
+
+ /* 13.32 Port Information state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ {
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ {
+ ptp->start_time = br->uptime; /* 12.8.2.2.3 b) */
+ PISM_begin(ptp);
+ }
+ }
+
+ /* 13.33 Port Role Selection state machine */
+ FOREACH_TREE_IN_BRIDGE(tree, br)
+ PRSSM_begin(tree);
+
+ /* 13.34 Port Role Transitions state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ {
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ PRTSM_begin(ptp);
+ }
+ /* 13.35 Port State Transition state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ {
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ PSTSM_begin(ptp);
+ }
+ /* 13.36 Topology Change state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ {
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ TCSM_begin(ptp);
+ }
+
+ br_state_machines_run(br);
+}
+
+/* Run each state machine */
+static void br_state_machines_run(bridge_t *br)
+{
+ port_t *prt;
+ per_tree_port_t *ptp;
+ tree_t *tree;
+
+ if(!br->bridgeEnabled)
+ return;
+
+ /* 13.28 Port Receive state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ PRSM_run(prt);
+ /* 13.29 Port Protocol Migration state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ PPMSM_run(prt);
+ /* 13.30 Bridge Detection state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ BDSM_run(prt);
+ /* 13.31 Port Transmit state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ PTSM_run(prt);
+
+ /* 13.32 Port Information state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ {
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ PISM_run(ptp);
+ }
+
+ /* 13.33 Port Role Selection state machine */
+ FOREACH_TREE_IN_BRIDGE(tree, br)
+ PRSSM_run(tree);
+
+ /* 13.34 Port Role Transitions state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ {
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ PRTSM_run(ptp);
+ }
+ /* 13.35 Port State Transition state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ {
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ PSTSM_run(ptp);
+ }
+ /* 13.36 Topology Change state machine */
+ FOREACH_PORT_IN_BRIDGE(prt, br)
+ {
+ FOREACH_PTP_IN_PORT(ptp, prt)
+ TCSM_run(ptp);
+ }
+}
--- /dev/null
+/*
+ * mstp.h State machines from IEEE 802.1Q-2005
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+ */
+
+#ifndef MSTP_H
+#define MSTP_H
+
+#include <stdlib.h>
+
+#include "bridge_ctl.h"
+#include "list.h"
+
+/* #define HMAC_MDS_TEST_FUNCTIONS */
+
+/* Useful macro for counting number of elements in array */
+#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
+
+/*
+ * assign() and cmp() macros that also do strict type-checking. See the
+ * "unnecessary" pointer comparison.
+ * NOTE: potential double-evaluation of the first argument in assign macro!
+ * It is the price for type-safety ;)
+ */
+#define assign(x, y) ({ \
+ typeof(x) _assign1 = (x); \
+ typeof(y) _assign2 = (y); \
+ (void)(&_assign1 == &_assign2); \
+ (x) = _assign2; })
+#define _ncmp(x, y) ({ \
+ typeof(x) _cmp1 = (x); \
+ typeof(y) _cmp2 = (y); \
+ (void)(&_cmp1 == &_cmp2); \
+ memcmp(&_cmp1, &_cmp2, sizeof(_cmp1)); })
+#define cmp(x, _op, y) (_ncmp((x), (y)) _op 0)
+
+/* Stricter version of bool constants, for the assign macro */
+#define boolFalse ((bool)false)
+#define boolTrue ((bool)true)
+
+/* 13.7, Table 13-1 */
+#define HMAC_KEY {0x13, 0xAC, 0x06, 0xA6, 0x2E, 0x47, 0xFD, 0x51, \
+ 0xF9, 0x5D, 0x2B, 0xA2, 0x43, 0xCD, 0x03, 0x46}
+extern void hmac_md5(unsigned char * text, int text_len, unsigned char * key,
+ int key_len, caddr_t digest);
+#ifdef HMAC_MDS_TEST_FUNCTIONS
+extern bool MD5TestSuite(void);
+#endif /* HMAC_MDS_TEST_FUNCTIONS */
+
+#define MAX_PORT_NUMBER 4095
+#define MAX_VID 4094
+#define MAX_FID 4095
+#define MAX_MSTID 4094
+
+/* MAX_xxx_MSTIS: CIST not counted */
+#define MAX_STANDARD_MSTIS 64
+#define MAX_IMPLEMENTATION_MSTIS 63
+
+/* 13.37.1 */
+#define MAX_PATH_COST 200000000u
+
+typedef union
+{
+ __u64 u;
+ struct
+ {
+ __be16 priority;
+ __u8 mac_address[ETH_ALEN];
+ } __attribute__((packed)) s;
+} bridge_identifier_t;
+
+typedef __be16 port_identifier_t;
+
+/* These macros work well for both PortID and BridgeID */
+#define GET_PRIORITY_FROM_IDENTIFIER(id) (((__u8 *)(&(id)))[0] & 0xF0)
+#define SET_PRIORITY_IN_IDENTIFIER(pri, id) do{ \
+ __u8 *first_octet = (__u8 *)(&(id)); \
+ *first_octet &= 0x0F; \
+ *first_octet |= (pri) & 0xF0; \
+ }while(0)
+
+#define CONFIGURATION_NAME_LEN 32
+#define CONFIGURATION_DIGEST_LEN 16
+typedef union
+{
+ __u8 a[1 + CONFIGURATION_NAME_LEN + 2 + CONFIGURATION_DIGEST_LEN];
+ struct
+ {
+ __u8 selector; /* always 0 */
+ __u8 configuration_name[CONFIGURATION_NAME_LEN];
+ __be16 revision_level;
+ __u8 configuration_digest[CONFIGURATION_DIGEST_LEN];
+ } __attribute__((packed)) s;
+} __attribute__((packed)) mst_configuration_identifier_t;
+
+typedef struct
+{
+ bridge_identifier_t RRootID;
+ __be32 IntRootPathCost;
+ bridge_identifier_t DesignatedBridgeID;
+ port_identifier_t DesignatedPortID;
+ /* not used for MSTIs, only for CIST */
+ bridge_identifier_t RootID;
+ __be32 ExtRootPathCost;
+} port_priority_vector_t;
+
+/* 17.14 of 802.1D, Table 17-1 */
+#define MIN_COMPAT_HELLO_TIME 1
+
+typedef struct
+{
+ __u8 remainingHops;
+ /* not used for MSTIs, only for CIST */
+ __be16 Forward_Delay;
+ __be16 Max_Age;
+ __be16 Message_Age;
+ __be16 Hello_Time;
+} times_t;
+
+typedef struct
+{
+ /* see bpduFlagOffset_t enum for offsets of flag bits */
+ __u8 flags;
+ bridge_identifier_t mstiRRootID;
+ __be32 mstiIntRootPathCost;
+ /* only bits 7..4, bits 3..0 are zero on Tx and ignored on Rx */
+ __u8 bridgeIdentifierPriority;
+ /* only bits 7..4, bits 3..0 are zero on Tx and ignored on Rx */
+ __u8 portIdentifierPriority;
+ __u8 remainingHops;
+} __attribute__((packed)) msti_configuration_message_t;
+
+typedef struct
+{
+ /* always zero for the Spanning Tree BPDUs */
+ __be16 protocolIdentifier;
+ /* protoSTP for the Config and TCN
+ * protoRSTP for the RST
+ * protoMSTP for the MST
+ * (see protocol_version_t enum) */
+ __u8 protocolVersion;
+ /* values are defined in bpduType_t enum */
+ __u8 bpduType;
+ /* TCN BPDU ends here */
+ /* see bpduFlagOffset_t enum for offsets of flag bits */
+ __u8 flags;
+ bridge_identifier_t cistRootID;
+ __be32 cistExtRootPathCost;
+ bridge_identifier_t cistRRootID;
+ port_identifier_t cistPortID;
+ __be16 MessageAge;
+ __be16 MaxAge;
+ __be16 HelloTime;
+ __be16 ForwardDelay;
+ /* Config BPDU ends here */
+ __u8 version1_len; /* always zero */
+ /* RST BPDU ends here */
+ __be16 version3_len;
+ mst_configuration_identifier_t mstConfigurationIdentifier;
+ __be32 cistIntRootPathCost;
+ bridge_identifier_t cistBridgeID;
+ __u8 cistRemainingHops;
+ msti_configuration_message_t mstConfiguration[MAX_STANDARD_MSTIS];
+} __attribute__((packed)) bpdu_t;
+
+#define TCN_BPDU_SIZE offsetof(bpdu_t, flags)
+#define CONFIG_BPDU_SIZE offsetof(bpdu_t, version1_len)
+#define RST_BPDU_SIZE offsetof(bpdu_t, version3_len)
+#define MST_BPDU_SIZE_WO_MSTI_MSGS offsetof(bpdu_t, mstConfiguration)
+#define MST_BPDU_VER3LEN_WO_MSTI_MSGS (MST_BPDU_SIZE_WO_MSTI_MSGS \
+ - offsetof(bpdu_t, mstConfigurationIdentifier))
+
+typedef enum
+{
+ OtherInfo,
+ SuperiorDesignatedInfo,
+ RepeatedDesignatedInfo,
+ InferiorDesignatedInfo,
+ InferiorRootAlternateInfo
+} port_info_t;
+
+typedef enum
+{
+ _io_Disabled,
+ _io_Mine,
+ _io_Aged,
+ _io_Received
+} port_info_origin_t;
+
+typedef enum
+{
+ _pr_Disabled,
+ _pr_Root,
+ _pr_Designated,
+ _pr_Alternate,
+ _pr_Backup,
+ _pr_Master
+} port_role_t;
+
+typedef enum
+{
+ encodedRoleMaster = 0,
+ encodedRoleAlternateBackup = 1,
+ encodedRoleRoot = 2,
+ encodedRoleDesignated = 3
+} port_encoded_role_t;
+
+typedef enum
+{
+ _pv_STP = 0,
+ _pv_RSTP = 2,
+ _pv_MSTP = 3
+} protocol_version_t;
+
+typedef enum
+{
+ bpduTypeConfig = 0,
+ bpduTypeRST = 2,
+ bpduTypeTCN = 128
+} bpduType_t;
+
+typedef enum
+{
+ offsetTc = 0,
+ offsetProposal = 1,
+ offsetRole = 2, /* actually, role is coded in two-bit field */
+ offsetRole1 = 3, /* second bit of two-bit role field */
+ offsetLearnig = 4,
+ offsetForwarding = 5,
+ offsetAgreement = 6,
+ offsetTcAck = 7
+/* in MSTI Configuration Message flags bit7 is used for Master flag */
+#define offsetMaster offsetTcAck
+} bpduFlagOffset_t;
+
+#define BPDU_FLAGS_ROLE_SET(role) (((role) & 3) << offsetRole)
+#define BPDU_FLAGS_ROLE_GET(flags) (((flags) >> offsetRole) & 3)
+
+typedef enum
+{
+ p2pAuto,
+ p2pForceTrue,
+ p2pForceFalse
+}
+admin_p2p_t;
+
+/* Stricter version of port_info_origin & port_role & protocol_version
+ * constants, for the assign macro */
+#define ioDisabled ((port_info_origin_t)_io_Disabled)
+#define ioMine ((port_info_origin_t)_io_Mine)
+#define ioAged ((port_info_origin_t)_io_Aged)
+#define ioReceived ((port_info_origin_t)_io_Received)
+
+#define roleDisabled ((port_role_t)_pr_Disabled)
+#define roleRoot ((port_role_t)_pr_Root)
+#define roleDesignated ((port_role_t)_pr_Designated)
+#define roleAlternate ((port_role_t)_pr_Alternate)
+#define roleBackup ((port_role_t)_pr_Backup)
+#define roleMaster ((port_role_t)_pr_Master)
+
+#define protoSTP ((protocol_version_t)_pv_STP)
+#define protoRSTP ((protocol_version_t)_pv_RSTP)
+#define protoMSTP ((protocol_version_t)_pv_MSTP)
+
+/* 13.28 Port Receive state machine */
+typedef enum
+{
+ PRSM_DISCARD,
+ PRSM_RECEIVE
+} PRSM_states_t;
+
+/* 13.29 Port Protocol Migration state machine */
+typedef enum
+{
+ PPMSM_CHECKING_RSTP,
+ PPMSM_SELECTING_STP,
+ PPMSM_SENSING
+} PPMSM_states_t;
+
+/* 13.30 Bridge Detection state machine */
+typedef enum
+{
+ BDSM_EDGE,
+ BDSM_NOT_EDGE
+} BDSM_states_t;
+
+/* 13.31 Port Transmit state machine */
+typedef enum
+{
+ PTSM_TRANSMIT_INIT,
+ PTSM_TRANSMIT_CONFIG,
+ PTSM_TRANSMIT_TCN,
+ PTSM_TRANSMIT_RSTP,
+ PTSM_TRANSMIT_PERIODIC,
+ PTSM_IDLE
+} PTSM_states_t;
+
+/* 13.32 Port Information state machine */
+/* #define PISM_ENABLE_LOG */
+typedef enum
+{
+ PISM_DISABLED,
+ PISM_AGED,
+ PISM_UPDATE,
+ PISM_SUPERIOR_DESIGNATED,
+ PISM_REPEATED_DESIGNATED,
+ PISM_INFERIOR_DESIGNATED,
+ PISM_NOT_DESIGNATED,
+ PISM_OTHER,
+ PISM_CURRENT,
+ PISM_RECEIVE
+} PISM_states_t;
+
+/* 13.33 Port Role Selection state machine */
+typedef enum
+{
+ PRSSM_INIT_TREE,
+ PRSSM_ROLE_SELECTION
+} PRSSM_states_t;
+
+/* 13.34 Port Role Transitions state machine */
+/* #define PRTSM_ENABLE_LOG */
+typedef enum
+{
+ /* Disabled Port role transitions */
+ PRTSM_INIT_PORT,
+ PRTSM_DISABLE_PORT,
+ PRTSM_DISABLED_PORT,
+ /* MasterPort role transitions */
+ PRTSM_MASTER_PROPOSED,
+ PRTSM_MASTER_AGREED,
+ PRTSM_MASTER_SYNCED,
+ PRTSM_MASTER_RETIRED,
+ PRTSM_MASTER_FORWARD,
+ PRTSM_MASTER_LEARN,
+ PRTSM_MASTER_DISCARD,
+ PRTSM_MASTER_PORT,
+ /* RootPort role transitions */
+ PRTSM_ROOT_PROPOSED,
+ PRTSM_ROOT_AGREED,
+ PRTSM_ROOT_SYNCED,
+ PRTSM_REROOT,
+ PRTSM_ROOT_FORWARD,
+ PRTSM_ROOT_LEARN,
+ PRTSM_REROOTED,
+ PRTSM_ROOT_PORT,
+ /* DesignatedPort role transitions */
+ PRTSM_DESIGNATED_PROPOSE,
+ PRTSM_DESIGNATED_AGREED,
+ PRTSM_DESIGNATED_SYNCED,
+ PRTSM_DESIGNATED_RETIRED,
+ PRTSM_DESIGNATED_FORWARD,
+ PRTSM_DESIGNATED_LEARN,
+ PRTSM_DESIGNATED_DISCARD,
+ PRTSM_DESIGNATED_PORT,
+ /* AlternatePort and BackupPort role transitions */
+ PRTSM_BLOCK_PORT,
+ PRTSM_BACKUP_PORT,
+ PRTSM_ALTERNATE_PROPOSED,
+ PRTSM_ALTERNATE_AGREED,
+ PRTSM_ALTERNATE_PORT
+} PRTSM_states_t;
+
+/* 13.35 Port State Transition state machine */
+typedef enum
+{
+ PSTSM_DISCARDING,
+ PSTSM_LEARNING,
+ PSTSM_FORWARDING
+} PSTSM_states_t;
+
+/* 13.36 Topology Change state machine */
+typedef enum
+{
+ TCSM_INACTIVE,
+ TCSM_LEARNING,
+ TCSM_DETECTED,
+ TCSM_NOTIFIED_TCN,
+ TCSM_NOTIFIED_TC,
+ TCSM_PROPAGATING,
+ TCSM_ACKNOWLEDGED,
+ TCSM_ACTIVE
+} TCSM_states_t;
+
+/*
+ * Following standard-defined variables are not defined as variables.
+ * Their functionality is implemented indirectly by other means:
+ * - BEGIN, tick, ageingTime.
+ */
+
+typedef struct
+{
+ struct list_head list; /* anchor in global list of bridges */
+
+ /* List of all ports */
+ struct list_head ports;
+ /* List of all tree instances, first in list (trees.next) is CIST */
+ struct list_head trees;
+#define GET_CIST_TREE(br) list_entry((br)->trees.next, tree_t, bridge_list)
+
+ bool bridgeEnabled;
+
+ /* Per-bridge configuration parameters */
+ mst_configuration_identifier_t MstConfigId; /* 13.24.b */
+ __u8 MaxHops; /* 13.22.o */
+ protocol_version_t ForceProtocolVersion; /* 13.22.e */
+ __be16 Forward_Delay; /* 13.22.f */
+ __be16 Max_Age; /* 13.22.i */
+ unsigned int Transmit_Hold_Count; /* 13.22.g */
+ unsigned int Migrate_Time; /* 13.22.h */
+ unsigned int rapidAgeingWhile;
+
+ __u16 vid2fid[MAX_VID + 1];
+ __be16 fid2mstid[MAX_FID + 1];
+
+ /* not in standard */
+ unsigned int uptime;
+
+ sysdep_br_data_t sysdeps;
+} bridge_t;
+
+typedef struct
+{
+ struct list_head bridge_list; /* anchor in bridge's list of trees */
+ bridge_t * bridge;
+ __be16 MSTID; /* 0 == CIST */
+
+ /* List of the per-port data structures for this tree instance */
+ struct list_head ports;
+
+ /* 13.23.(c,f,g) Per-bridge per-tree variables */
+ bridge_identifier_t BridgeIdentifier;
+ port_identifier_t rootPortId;
+ port_priority_vector_t rootPriority;
+
+ /* 13.23.d This is totally calculated from BridgeIdentifier */
+ port_priority_vector_t BridgePriority;
+
+ /* 13.23.e Some waste of space here, as MSTIs only use
+ * remainingHops member of the struct times_t,
+ * but saves extra checks and improves readability */
+ times_t BridgeTimes, rootTimes;
+
+ /* 12.8.1.1.3.(b,c,d) */
+ unsigned int time_since_topology_change;
+ unsigned int topology_change_count;
+ bool topology_change;
+
+ /* State machines */
+ PRSSM_states_t PRSSM_state;
+
+} tree_t;
+
+typedef struct
+{
+ struct list_head br_list; /* anchor in bridge's list of ports */
+ bridge_t * bridge;
+ __be16 port_number;
+
+ /* List of all tree instances, first in list (trees.next) is CIST.
+ * List is sorted by MSTID (by insertion procedure MSTP_IN_create_msti).
+ */
+ struct list_head trees;
+#define GET_CIST_PTP_FROM_PORT(prt) \
+ list_entry((prt)->trees.next, per_tree_port_t, port_list)
+
+ /* 13.21.(a,b,c) Per-port timers */
+ unsigned int mdelayWhile, helloWhen, edgeDelayWhile;
+
+ /* 13.24.(b,c,e,f,g,j,k,l,m,n,o,p,q,r,aw) Per-port variables */
+ unsigned int txCount;
+ bool operEdge, portEnabled, infoInternal, rcvdInternal;
+ bool mcheck, rcvdBpdu, rcvdRSTP, rcvdSTP, rcvdTcAck, rcvdTcn, sendRSTP;
+ bool tcAck, newInfo, newInfoMsti;
+
+ /* 6.4.3 */
+ bool operPointToPointMAC;
+
+ /* Per-port configuration parameters */
+ bool restrictedRole, restrictedTcn; /* 13.24.(h,i) */
+ __u32 ExternalPortPathCost; /* 13.22.p */
+ __u32 AdminExternalPortPathCost; /* 0 = calculate from speed */
+ admin_p2p_t AdminP2P; /* 6.4.3 */
+ bool AdminEdgePort; /* 13.22.k */
+ bool AutoEdge; /* 13.22.m */
+
+ /* State machines */
+ PRSM_states_t PRSM_state;
+ PPMSM_states_t PPMSM_state;
+ BDSM_states_t BDSM_state;
+ PTSM_states_t PTSM_state;
+
+ /* Copy of the received BPDU */
+ bpdu_t rcvdBpduData;
+ int rcvdBpduNumOfMstis;
+
+ sysdep_if_data_t sysdeps;
+} port_t;
+
+typedef struct
+{
+ struct list_head port_list; /* anchor in port's list of trees */
+ struct list_head tree_list; /* anchor in tree's list of per-port data */
+ port_t *port;
+ tree_t *tree;
+ __be16 MSTID; /* 0 == CIST */
+
+ int state; /* BR_STATE_xxx */
+
+ /* 13.21.(d,e,f,g,h) Per-port per-tree timers */
+ unsigned int fdWhile, rrWhile, rbWhile, tcWhile, rcvdInfoWhile;
+
+ /* 13.24.(s,t,u,v,w,x,y,z,aa,ab,ac,ad,ae,af,ag,ai,aj,ak,ap,as,at,au,av)
+ * Per-port per-tree variables */
+ bool agree, agreed, disputed, forward, forwarding, learn, learning;
+ port_info_t rcvdInfo;
+ port_info_origin_t infoIs;
+ bool proposed, proposing, rcvdMsg, rcvdTc, reRoot, reselect, selected;
+ bool fdbFlush, tcProp, updtInfo, sync, synced;
+ port_identifier_t portId;
+ port_role_t role, selectedRole;
+
+ /* 13.24.(al,an,aq) Some waste of space here, as MSTIs don't use
+ * RootID and ExtRootPathCost members of the struct port_priority_vector_t,
+ * but saves extra checks and improves readability */
+ port_priority_vector_t designatedPriority, msgPriority, portPriority;
+
+ /* 13.24.(am,ao,ar) Some waste of space here, as MSTIs only use
+ * remainingHops member of the struct times_t,
+ * but saves extra checks and improves readability */
+ times_t designatedTimes, msgTimes, portTimes;
+
+ /* 13.24.(ax,ay) Per-port per-MSTI variables, not applicable to CIST */
+ bool master, mastered;
+
+ /* Per-port per-tree configuration parameters */
+ __u32 InternalPortPathCost; /* 13.22.q */
+ __u32 AdminInternalPortPathCost; /* 0 = calculate from speed */
+
+ /* not in standard, used for calculation of port uptime */
+ unsigned int start_time;
+
+ /* State machines */
+ PISM_states_t PISM_state;
+ PRTSM_states_t PRTSM_state;
+ PSTSM_states_t PSTSM_state;
+ TCSM_states_t TCSM_state;
+
+ /* Pointer to the corresponding MSTI Configuration Message
+ * in the port->rcvdBpduData */
+ msti_configuration_message_t *rcvdMstiConfig;
+} per_tree_port_t;
+
+/* External events (inputs) */
+bool MSTP_IN_bridge_create(bridge_t *br, __u8 *macaddr);
+bool MSTP_IN_port_create_and_add_tail(port_t *prt, __u16 portno);
+void MSTP_IN_delete_port(port_t *prt);
+void MSTP_IN_delete_bridge(bridge_t *br);
+void MSTP_IN_set_bridge_address(bridge_t *br, __u8 *macaddr);
+void MSTP_IN_set_bridge_enable(bridge_t *br, bool up);
+void MSTP_IN_set_port_enable(port_t *prt, bool up, int speed, int duplex);
+void MSTP_IN_one_second(bridge_t *br);
+void MSTP_IN_all_fids_flushed(per_tree_port_t *ptp);
+void MSTP_IN_rx_bpdu(port_t *prt, bpdu_t *bpdu, int size);
+
+bool MSTP_IN_set_vid2fid(bridge_t *br, __u16 vid, __u16 fid);
+bool MSTP_IN_set_all_vids2fids(bridge_t *br, __u16 *vids2fids);
+bool MSTP_IN_set_fid2mstid(bridge_t *br, __u16 fid, __u16 mstid);
+bool MSTP_IN_set_all_fids2mstids(bridge_t *br, __u16 *fids2mstids);
+bool MSTP_IN_get_mstilist(bridge_t *br, int *num_mstis, __u16 *mstids);
+bool MSTP_IN_create_msti(bridge_t *br, __u16 mstid);
+bool MSTP_IN_delete_msti(bridge_t *br, __u16 mstid);
+void MSTP_IN_set_mst_config_id(bridge_t *br, __u16 revision, __u8 *name);
+
+/* External actions (outputs) */
+void MSTP_OUT_set_state(per_tree_port_t *ptp, int new_state);
+void MSTP_OUT_flush_all_fids(per_tree_port_t *ptp);
+void MSTP_OUT_set_ageing_time(bridge_t *br, int ageingTime);
+void MSTP_OUT_tx_bpdu(port_t *prt, bpdu_t *bpdu, int size);
+
+/* Structures for communicating with user */
+ /* 12.8.1.1 Read CIST Bridge Protocol Parameters */
+typedef struct
+{
+ bridge_identifier_t bridge_id;
+ unsigned int time_since_topology_change;
+ unsigned int topology_change_count;
+ bool topology_change;
+ bridge_identifier_t designated_root;
+ unsigned int root_path_cost;
+ port_identifier_t root_port_id;
+ unsigned int root_max_age;
+ unsigned int root_forward_delay;
+ unsigned int bridge_max_age;
+ unsigned int bridge_forward_delay;
+ unsigned int tx_hold_count;
+ protocol_version_t protocol_version;
+ bridge_identifier_t regional_root;
+ unsigned int internal_path_cost;
+ bool enabled; /* not in standard */
+ __u8 max_hops;
+} CIST_BridgeStatus;
+
+void MSTP_IN_get_cist_bridge_status(bridge_t *br, CIST_BridgeStatus *status);
+
+ /* 12.8.1.2 Read MSTI Bridge Protocol Parameters */
+typedef struct
+{
+ bridge_identifier_t bridge_id;
+ unsigned int time_since_topology_change;
+ unsigned int topology_change_count;
+ bool topology_change;
+ bridge_identifier_t regional_root;
+ unsigned int internal_path_cost;
+ port_identifier_t root_port_id;
+} MSTI_BridgeStatus;
+
+void MSTP_IN_get_msti_bridge_status(tree_t *tree, MSTI_BridgeStatus *status);
+
+/* 12.8.1.3 Set CIST Bridge Protocol Parameters */
+typedef struct
+{
+ unsigned int bridge_max_age;
+ bool set_bridge_max_age;
+
+ unsigned int bridge_forward_delay;
+ bool set_bridge_forward_delay;
+
+ /* Superseded by MSTP_IN_set_msti_bridge_config for the CIST.
+ * __u8 bridge_priority;
+ * bool set_bridge_priority; */
+
+ protocol_version_t protocol_version;
+ bool set_protocol_version;
+
+ unsigned int tx_hold_count;
+ bool set_tx_hold_count;
+
+ __u8 max_hops;
+ bool set_max_hops;
+} CIST_BridgeConfig;
+
+int MSTP_IN_set_cist_bridge_config(bridge_t *br, CIST_BridgeConfig *cfg);
+
+/* 12.8.1.4 Set MSTI Bridge Protocol Parameters */
+ /* No need in special structure for single parameter Bridge Priority */
+
+int MSTP_IN_set_msti_bridge_config(tree_t *tree, __u8 bridge_priority);
+
+/* 12.8.2.1 Read CIST Port Parameters */
+typedef struct
+{
+ unsigned int uptime;
+ int state; /* BR_STATE_xxx */
+ port_identifier_t port_id;
+ __u32 admin_external_port_path_cost; /* not in standard. 0 = auto */
+ __u32 external_port_path_cost;
+ bridge_identifier_t designated_root; /* from portPriority */
+ __u32 designated_external_cost; /* from portPriority */
+ bridge_identifier_t designated_bridge; /* from portPriority */
+ port_identifier_t designated_port; /* from portPriority */
+ bool tc_ack; /* tcAck */
+ unsigned int port_hello_time; /* from portTimes */
+ bool admin_edge_port;
+ bool auto_edge_port; /* not in standard */
+ bool oper_edge_port;
+ /* 802.1Q-2005 wants here MAC_Enabled & MAC_Operational. We don't know
+ * neither of these. Return portEnabled and feel happy. */
+ bool enabled;
+ admin_p2p_t admin_p2p;
+ bool oper_p2p;
+ bool restricted_role;
+ bool restricted_tcn;
+ port_role_t role;
+ bool disputed;
+ bridge_identifier_t designated_regional_root; /* from portPriority */
+ __u32 designated_internal_cost; /* from portPriority */
+ __u32 admin_internal_port_path_cost; /* not in standard. 0 = auto */
+ __u32 internal_port_path_cost; /* not in standard */
+} CIST_PortStatus;
+
+void MSTP_IN_get_cist_port_status(port_t *prt, CIST_PortStatus *status);
+
+/* 12.8.2.2 Read MSTI Port Parameters */
+typedef struct
+{
+ unsigned int uptime;
+ int state; /* BR_STATE_xxx */
+ port_identifier_t port_id;
+ __u32 admin_internal_port_path_cost; /* not in standard. 0 = auto */
+ __u32 internal_port_path_cost;
+ bridge_identifier_t designated_regional_root; /* from portPriority */
+ __u32 designated_internal_cost; /* from portPriority */
+ bridge_identifier_t designated_bridge; /* from portPriority */
+ port_identifier_t designated_port; /* from portPriority */
+ port_role_t role;
+ bool disputed;
+} MSTI_PortStatus;
+
+void MSTP_IN_get_msti_port_status(per_tree_port_t *ptp,
+ MSTI_PortStatus *status);
+
+/* 12.8.2.3 Set CIST port parameters */
+typedef struct
+{
+ __u32 admin_external_port_path_cost; /* not in standard. 0 = auto */
+ bool set_admin_external_port_path_cost;
+
+ /* Superseded by MSTP_IN_set_msti_port_config for the CIST.
+ * __u32 admin_internal_port_path_cost;
+ * bool set_admin_internal_port_path_cost;
+ *
+ * __u8 port_priority;
+ * bool set_port_priority;
+ */
+
+ bool admin_edge_port;
+ bool set_admin_edge_port;
+
+ bool auto_edge_port; /* not in standard */
+ bool set_auto_edge_port;
+
+ admin_p2p_t admin_p2p;
+ bool set_admin_p2p;
+
+ bool restricted_role;
+ bool set_restricted_role;
+
+ bool restricted_tcn;
+ bool set_restricted_tcn;
+} CIST_PortConfig;
+
+int MSTP_IN_set_cist_port_config(port_t *prt, CIST_PortConfig *cfg);
+
+/* 12.8.2.4 Set MSTI port parameters */
+typedef struct
+{
+ __u32 admin_internal_port_path_cost; /* 0 = auto */
+ bool set_admin_internal_port_path_cost;
+
+ __u8 port_priority;
+ bool set_port_priority;
+} MSTI_PortConfig;
+
+int MSTP_IN_set_msti_port_config(per_tree_port_t *ptp, MSTI_PortConfig *cfg);
+
+/* 12.8.2.5 Force BPDU Migration Check */
+int MSTP_IN_port_mcheck(port_t *prt);
+
+#endif /* MSTP_H */
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/if_ether.h>
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+
+#include "log.h"
+
+int netsock = -1;
+
+int netsock_init(void)
+{
+ netsock = socket(AF_INET, SOCK_DGRAM, 0);
+ if(0 > netsock)
+ {
+ ERROR("Couldn't open inet socket for ioctls: %m\n");
+ return -1;
+ }
+ return 0;
+}
+
+int get_hwaddr(char *ifname, __u8 *hwaddr)
+{
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ if(0 > ioctl(netsock, SIOCGIFHWADDR, &ifr))
+ {
+ ERROR("%s: get hw address failed: %m", ifname);
+ return -1;
+ }
+ memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+ return 0;
+}
+
+int ethtool_get_link(char *ifname)
+{
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ struct ethtool_value eval;
+
+ eval.cmd = ETHTOOL_GLINK;
+ ifr.ifr_data = (caddr_t)&eval;
+ if(0 > ioctl(netsock, SIOCETHTOOL, &ifr))
+ {
+ ERROR("Cannot get link status for %s: %m\n", ifname);
+ return -1;
+ }
+ if(eval.data)
+ return 1;
+ return 0;
+}
+
+int ethtool_get_speed_duplex(char *ifname, int *speed, int *duplex)
+{
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+ struct ethtool_cmd ecmd;
+
+ ecmd.cmd = ETHTOOL_GSET;
+ ifr.ifr_data = (caddr_t)&ecmd;
+ if(0 > ioctl(netsock, SIOCETHTOOL, &ifr))
+ {
+ ERROR("Cannot get speed/duplex for %s: %m\n", ifname);
+ return -1;
+ }
+ *speed = ecmd.speed; /* Ethtool speed is in Mbps */
+ *duplex = ecmd.duplex; /* We have same convention as ethtool.
+ 0 = half, 1 = full */
+ return 0;
+}
+
+/********* Sysfs based utility functions *************/
+
+/* This sysfs stuff might break with interface renames */
+bool is_bridge(char *if_name)
+{
+ char path[32 + IFNAMSIZ];
+ sprintf(path, "/sys/class/net/%s/bridge", if_name);
+ return (0 == access(path, R_OK));
+}
+
+int get_bridge_portno(char *if_name)
+{
+ char path[32 + IFNAMSIZ];
+ sprintf(path, "/sys/class/net/%s/brport/port_no", if_name);
+ char buf[128];
+ int fd;
+ long res = -1;
+ TSTM((fd = open(path, O_RDONLY)) >= 0, -1, "%m");
+ int l;
+ TSTM((l = read(fd, buf, sizeof(buf) - 1)) >= 0, -1, "%m");
+ if(0 == l)
+ {
+ ERROR("Empty port index file");
+ goto out;
+ }
+ else if((sizeof(buf) - 1) == l)
+ {
+ ERROR("port_index file too long");
+ goto out;
+ }
+ buf[l] = 0;
+ if('\n' == buf[l - 1])
+ buf[l - 1] = 0;
+ char *end;
+ res = strtoul(buf, &end, 0);
+ if(0 != *end || INT_MAX < res)
+ {
+ ERROR("Invalid port index %s", buf);
+ res = -1;
+ }
+out:
+ close(fd);
+ return res;
+}
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+ Copyright (c) 2011 Factor-SPE
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Vitalii Demianets <vitas@nppfactor.kiev.ua>
+
+******************************************************************************/
+
+#ifndef NETIF_UTILS_H
+#define NETIF_UTILS_H
+
+/* An inet socket for everyone to use for ifreqs. */
+int netsock_init(void);
+
+int get_hwaddr(char *ifname, unsigned char *hwaddr);
+
+int ethtool_get_speed_duplex(char *ifname, int *speed, int *duplex);
+int ethtool_get_link(char *ifname);
+
+bool is_bridge(char *if_name);
+
+int get_bridge_portno(char *if_name);
+
+#endif /* NETIF_UTILS_H */
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+ Authors: Stephen Hemminger <shemminger@linux-foundation.org>
+
+******************************************************************************/
+
+/* #define PACKET_DEBUG */
+
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <linux/if_packet.h>
+#include <linux/filter.h>
+
+#include "epoll_loop.h"
+#include "netif_utils.h"
+#include "bridge_ctl.h"
+#include "packet.h"
+#include "log.h"
+
+static struct epoll_event_handler packet_event;
+
+#ifdef PACKET_DEBUG
+static void dump_packet(const unsigned char *buf, int cc)
+{
+ int i, j;
+ for(i = 0; i < cc; i += 16)
+ {
+ for(j = 0; j < 16 && i + j < cc; ++j)
+ printf(" %02x", buf[i + j]);
+ printf("\n");
+ }
+ printf("\n");
+ fflush(stdout);
+}
+#endif
+
+/*
+ * To send/receive Spanning Tree packets we use PF_PACKET because
+ * it allows the filtering we want but gives raw data
+ */
+void packet_send(int ifindex, const struct iovec *iov, int iov_count, int len)
+{
+ int l;
+ struct sockaddr_ll sl =
+ {
+ .sll_family = AF_PACKET,
+ .sll_protocol = htons(ETH_P_802_2),
+ .sll_ifindex = ifindex,
+ .sll_halen = ETH_ALEN,
+ };
+
+ if(iov_count > 0 && iov[0].iov_len > ETH_ALEN)
+ memcpy(&sl.sll_addr, iov[0].iov_base, ETH_ALEN);
+
+ struct msghdr msg =
+ {
+ .msg_name = &sl,
+ .msg_namelen = sizeof(sl),
+ .msg_iov = (struct iovec *)iov,
+ .msg_iovlen = iov_count,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0,
+ };
+
+#ifdef PACKET_DEBUG
+ printf("Transmit Dst index %d %02x:%02x:%02x:%02x:%02x:%02x\n",
+ sl.sll_ifindex,
+ sl.sll_addr[0], sl.sll_addr[1], sl.sll_addr[2],
+ sl.sll_addr[3], sl.sll_addr[4], sl.sll_addr[5]);
+ {
+ int i;
+ for(i = 0; i < iov_count; ++i)
+ dump_packet(iov[i].iov_base, iov[i].iov_len);
+ }
+#endif
+
+ l = sendmsg(packet_event.fd, &msg, 0);
+
+ if(l < 0)
+ {
+ if(errno != EWOULDBLOCK)
+ ERROR("send failed: %m");
+ }
+ else if(l != len)
+ ERROR("short write in sendto: %d instead of %d", l, len);
+}
+
+static void packet_rcv(uint32_t events, struct epoll_event_handler *h)
+{
+ int cc;
+ unsigned char buf[2048];
+ struct sockaddr_ll sl;
+ socklen_t salen = sizeof sl;
+
+ cc = recvfrom(h->fd, &buf, sizeof(buf), 0, (struct sockaddr *) &sl, &salen);
+ if(cc <= 0)
+ {
+ ERROR("recvfrom failed: %m");
+ return;
+ }
+
+#ifdef PACKET_DEBUG
+ printf("Receive Src ifindex %d %02x:%02x:%02x:%02x:%02x:%02x\n",
+ sl.sll_ifindex,
+ sl.sll_addr[0], sl.sll_addr[1], sl.sll_addr[2],
+ sl.sll_addr[3], sl.sll_addr[4], sl.sll_addr[5]);
+
+ dump_packet(buf, cc);
+#endif
+
+ bridge_bpdu_rcv(sl.sll_ifindex, buf, cc);
+}
+
+/* Berkeley Packet filter code to filter out spanning tree packets.
+ from tcpdump -s 1152 -dd stp
+ */
+static struct sock_filter stp_filter[] = {
+ { 0x28, 0, 0, 0x0000000c },
+ { 0x25, 3, 0, 0x000005dc },
+ { 0x30, 0, 0, 0x0000000e },
+ { 0x15, 0, 1, 0x00000042 },
+ { 0x6, 0, 0, 0x00000480 },
+ { 0x6, 0, 0, 0x00000000 },
+};
+
+/*
+ * Open up a raw packet socket to catch all 802.2 packets.
+ * and install a packet filter to only see STP (SAP 42)
+ *
+ * Since any bridged devices are already in promiscious mode
+ * no need to add multicast address.
+ */
+int packet_sock_init(void)
+{
+ int s;
+ struct sock_fprog prog =
+ {
+ .len = sizeof(stp_filter) / sizeof(stp_filter[0]),
+ .filter = stp_filter,
+ };
+
+ s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_802_2));
+ if(s < 0)
+ {
+ ERROR("socket failed: %m");
+ return -1;
+ }
+
+ if(setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0)
+ ERROR("setsockopt packet filter failed: %m");
+ else if(fcntl(s, F_SETFL, O_NONBLOCK) < 0)
+ ERROR("fcntl set nonblock failed: %m");
+ else
+ {
+ packet_event.fd = s;
+ packet_event.handler = packet_rcv;
+
+ if(0 == add_epoll(&packet_event))
+ return 0;
+ }
+
+ close(s);
+ return -1;
+}
--- /dev/null
+/*****************************************************************************
+ Copyright (c) 2006 EMC Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program 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 General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Authors: Srinivas Aji <Aji_Srinivas@emc.com>
+
+******************************************************************************/
+
+#ifndef PACKET_SOCK_H
+#define PACKET_SOCK_H
+
+#include <sys/uio.h>
+
+void packet_send(int ifindex, const struct iovec *iov, int iov_count, int len);
+int packet_sock_init(void);
+
+#endif /* PACKET_SOCK_H */