]> git.ipfire.org Git - people/ms/mstpd.git/commitdiff
Initial import
authorVitalii Demianets <vitas@nppfactor.kiev.ua>
Thu, 22 Sep 2011 13:33:02 +0000 (13:33 +0000)
committerVitalii Demianets <vitas@nppfactor.kiev.ua>
Thu, 22 Sep 2011 13:33:02 +0000 (13:33 +0000)
git-svn-id: http://svn.code.sf.net/p/mstpd/code/trunk@1 fbe50366-0c72-4402-a84b-5d246361dba7

26 files changed:
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
bridge-stp [new file with mode: 0755]
bridge_ctl.h [new file with mode: 0644]
bridge_track.c [new file with mode: 0644]
brmon.c [new file with mode: 0644]
ctl_functions.h [new file with mode: 0644]
ctl_main.c [new file with mode: 0644]
ctl_socket_client.c [new file with mode: 0644]
ctl_socket_client.h [new file with mode: 0644]
ctl_socket_server.c [new file with mode: 0644]
ctl_socket_server.h [new file with mode: 0644]
epoll_loop.c [new file with mode: 0644]
epoll_loop.h [new file with mode: 0644]
hmac_md5.c [new file with mode: 0644]
libnetlink.c [new file with mode: 0644]
libnetlink.h [new file with mode: 0644]
list.h [new file with mode: 0644]
log.h [new file with mode: 0644]
main.c [new file with mode: 0644]
mstp.c [new file with mode: 0644]
mstp.h [new file with mode: 0644]
netif_utils.c [new file with mode: 0644]
netif_utils.h [new file with mode: 0644]
packet.c [new file with mode: 0644]
packet.h [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..b7b5f53
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,340 @@
+                   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.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..d7bc4ec
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,33 @@
+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
diff --git a/bridge-stp b/bridge-stp
new file mode 100755 (executable)
index 0000000..6bed90d
--- /dev/null
@@ -0,0 +1,57 @@
+#!/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
diff --git a/bridge_ctl.h b/bridge_ctl.h
new file mode 100644 (file)
index 0000000..1d241ae
--- /dev/null
@@ -0,0 +1,92 @@
+/*****************************************************************************
+  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 */
diff --git a/bridge_track.c b/bridge_track.c
new file mode 100644 (file)
index 0000000..ba261ca
--- /dev/null
@@ -0,0 +1,760 @@
+/*****************************************************************************
+  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;
+}
diff --git a/brmon.c b/brmon.c
new file mode 100644 (file)
index 0000000..796d347
--- /dev/null
+++ b/brmon.c
@@ -0,0 +1,225 @@
+/*
+ * 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;
+}
diff --git a/ctl_functions.h b/ctl_functions.h
new file mode 100644 (file)
index 0000000..da22dae
--- /dev/null
@@ -0,0 +1,490 @@
+/*****************************************************************************
+  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 */
diff --git a/ctl_main.c b/ctl_main.c
new file mode 100644 (file)
index 0000000..863e9a8
--- /dev/null
@@ -0,0 +1,1114 @@
+/*****************************************************************************
+  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);
+}
diff --git a/ctl_socket_client.c b/ctl_socket_client.c
new file mode 100644 (file)
index 0000000..05f9620
--- /dev/null
@@ -0,0 +1,177 @@
+/*****************************************************************************
+  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;
+}
diff --git a/ctl_socket_client.h b/ctl_socket_client.h
new file mode 100644 (file)
index 0000000..ef482c2
--- /dev/null
@@ -0,0 +1,37 @@
+/*****************************************************************************
+  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 */
diff --git a/ctl_socket_server.c b/ctl_socket_server.c
new file mode 100644 (file)
index 0000000..27f01a6
--- /dev/null
@@ -0,0 +1,196 @@
+/*****************************************************************************
+  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);
+}
diff --git a/ctl_socket_server.h b/ctl_socket_server.h
new file mode 100644 (file)
index 0000000..944e5ef
--- /dev/null
@@ -0,0 +1,38 @@
+/*****************************************************************************
+  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 */
diff --git a/epoll_loop.c b/epoll_loop.c
new file mode 100644 (file)
index 0000000..e2c3442
--- /dev/null
@@ -0,0 +1,148 @@
+/*****************************************************************************
+  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;
+}
diff --git a/epoll_loop.h b/epoll_loop.h
new file mode 100644 (file)
index 0000000..54f6a4d
--- /dev/null
@@ -0,0 +1,53 @@
+/*****************************************************************************
+  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 */
diff --git a/hmac_md5.c b/hmac_md5.c
new file mode 100644 (file)
index 0000000..bd2b249
--- /dev/null
@@ -0,0 +1,534 @@
+/* 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 */
diff --git a/libnetlink.c b/libnetlink.c
new file mode 100644 (file)
index 0000000..42238f9
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ * 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;
+}
diff --git a/libnetlink.h b/libnetlink.h
new file mode 100644 (file)
index 0000000..2ebb9a7
--- /dev/null
@@ -0,0 +1,55 @@
+#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__ */
diff --git a/list.h b/list.h
new file mode 100644 (file)
index 0000000..e404c7b
--- /dev/null
+++ b/list.h
@@ -0,0 +1,519 @@
+/**
+ * 
+ * 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
diff --git a/log.h b/log.h
new file mode 100644 (file)
index 0000000..2bf0fb8
--- /dev/null
+++ b/log.h
@@ -0,0 +1,91 @@
+/*****************************************************************************
+  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 */
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..7af48a2
--- /dev/null
+++ b/main.c
@@ -0,0 +1,287 @@
+/*****************************************************************************
+  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 */
diff --git a/mstp.c b/mstp.c
new file mode 100644 (file)
index 0000000..0ab4886
--- /dev/null
+++ b/mstp.c
@@ -0,0 +1,4392 @@
+/*
+ * 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);
+    }
+}
diff --git a/mstp.h b/mstp.h
new file mode 100644 (file)
index 0000000..65fff8c
--- /dev/null
+++ b/mstp.h
@@ -0,0 +1,756 @@
+/*
+ * 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 */
diff --git a/netif_utils.c b/netif_utils.c
new file mode 100644 (file)
index 0000000..da26a6f
--- /dev/null
@@ -0,0 +1,150 @@
+/*****************************************************************************
+  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;
+}
diff --git a/netif_utils.h b/netif_utils.h
new file mode 100644 (file)
index 0000000..b2919c8
--- /dev/null
@@ -0,0 +1,42 @@
+/*****************************************************************************
+  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 */
diff --git a/packet.c b/packet.c
new file mode 100644 (file)
index 0000000..0c08ae7
--- /dev/null
+++ b/packet.c
@@ -0,0 +1,187 @@
+/*****************************************************************************
+  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;
+}
diff --git a/packet.h b/packet.h
new file mode 100644 (file)
index 0000000..8d2e0ff
--- /dev/null
+++ b/packet.h
@@ -0,0 +1,33 @@
+/*****************************************************************************
+  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 */