From 5bd98f09ea8d0d3b189619a9ea2ba19dab727f14 Mon Sep 17 00:00:00 2001 From: dv1tas Date: Thu, 22 Sep 2011 13:33:02 +0000 Subject: [PATCH] Initial import git-svn-id: svn://svn.code.sf.net/p/mstpd/code/trunk@1 fbe50366-0c72-4402-a84b-5d246361dba7 --- LICENSE | 340 ++++ Makefile | 33 + bridge-stp | 57 + bridge_ctl.h | 92 + bridge_track.c | 760 ++++++++ brmon.c | 225 +++ ctl_functions.h | 490 +++++ ctl_main.c | 1114 +++++++++++ ctl_socket_client.c | 177 ++ ctl_socket_client.h | 37 + ctl_socket_server.c | 196 ++ ctl_socket_server.h | 38 + epoll_loop.c | 148 ++ epoll_loop.h | 53 + hmac_md5.c | 534 ++++++ libnetlink.c | 632 +++++++ libnetlink.h | 55 + list.h | 519 +++++ log.h | 91 + main.c | 287 +++ mstp.c | 4392 +++++++++++++++++++++++++++++++++++++++++++ mstp.h | 756 ++++++++ netif_utils.c | 150 ++ netif_utils.h | 42 + packet.c | 187 ++ packet.h | 33 + 26 files changed, 11438 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100755 bridge-stp create mode 100644 bridge_ctl.h create mode 100644 bridge_track.c create mode 100644 brmon.c create mode 100644 ctl_functions.h create mode 100644 ctl_main.c create mode 100644 ctl_socket_client.c create mode 100644 ctl_socket_client.h create mode 100644 ctl_socket_server.c create mode 100644 ctl_socket_server.h create mode 100644 epoll_loop.c create mode 100644 epoll_loop.h create mode 100644 hmac_md5.c create mode 100644 libnetlink.c create mode 100644 libnetlink.h create mode 100644 list.h create mode 100644 log.h create mode 100644 main.c create mode 100644 mstp.c create mode 100644 mstp.h create mode 100644 netif_utils.c create mode 100644 netif_utils.h create mode 100644 packet.c create mode 100644 packet.h diff --git a/LICENSE b/LICENSE new file mode 100644 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. + + 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.) + +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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 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 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 index 0000000..6bed90d --- /dev/null +++ b/bridge-stp @@ -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 {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 {start|stop}" + exit 1 +esac diff --git a/bridge_ctl.h b/bridge_ctl.h new file mode 100644 index 0000000..1d241ae --- /dev/null +++ b/bridge_ctl.h @@ -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 + Authors: Vitalii Demianets + +******************************************************************************/ + +#ifndef BRIDGE_CTL_H +#define BRIDGE_CTL_H + +#include +#include +#include + +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 index 0000000..ba261ca --- /dev/null +++ b/bridge_track.c @@ -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 + Authors: Vitalii Demianets + +******************************************************************************/ + +#include +#include +#include + +#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 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 + * Modified by Srinivas Aji + * for use in RSTP daemon. - 2006-09-01 + * Modified by Vitalii Demianets + * for use in MSTP daemon. - 2011-07-18 + */ + +#include +#include +#include +#include +#include + +#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 index 0000000..da22dae --- /dev/null +++ b/ctl_functions.h @@ -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 + Authors: Vitalii Demianets + +******************************************************************************/ + +#ifndef CTL_SOCKET_H +#define CTL_SOCKET_H + +#include +#include + +#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 index 0000000..863e9a8 --- /dev/null +++ b/ctl_main.c @@ -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 + +******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#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, + "[ ... ]", "Show bridge state for the CIST"}, + {1, 0, "showmstilist", cmd_showmstilist, + "", "Show list of registered MSTIs"}, + {1, 0, "showmstconfid", cmd_showmstconfid, + "", "Show MST ConfigId"}, + {1, 0, "showvid2fid", cmd_showvid2fid, + "", "Show VID-to-FID allocation table"}, + {1, 0, "showfid2mstid", cmd_showfid2mstid, + "", "Show FID-to-MSTID allocation table"}, + /* Show global port */ + {1, 32, "showport", cmd_showport, + " [ ... ]", "Show port state for the CIST"}, + {1, 32, "showportdetail", cmd_showportdetail, + " [ ... ]", "Show port detailed state for the CIST"}, + /* Show tree bridge */ + {2, 0, "showtree", cmd_showtree, + " ", "Show bridge state for the given MSTI"}, + /* Show tree port */ + {3, 0, "showtreeport", cmd_showtreeport, + " ", "Show port detailed state for the given MSTI"}, + + /* Set global bridge */ + {3, 0, "setmstconfid", cmd_setmstconfid, + " ", + "Set MST ConfigId elements: Revision Level (0-65535) and Name"}, + {3, 0, "setvid2fid", cmd_setvid2fid, + " ", "Set VID-to-FID allocation"}, + {3, 0, "setfid2mstid", cmd_setfid2mstid, + " ", "Set FID-to-MSTID allocation"}, + {2, 0, "setmaxage", cmd_setbridgemaxage, + " ", "Set bridge max age (6-40)"}, + {2, 0, "setfdelay", cmd_setbridgefdelay, + " ", "Set bridge forward delay (4-30)"}, + {2, 0, "setmaxhops", cmd_setbridgemaxhops, + " ", "Set bridge max hops (6-40)"}, + {2, 0, "setforcevers", cmd_setbridgeforcevers, + " {mstp|rstp|stp}", "Force Spanning Tree protocol version"}, + {2, 0, "settxholdcount", cmd_setbridgetxholdcount, + " ", "Set bridge transmit hold count (1-10)"}, + /* Set tree bridge */ + {2, 0, "createtree", cmd_createtree, + " ", "Create new MSTI"}, + {2, 0, "deletetree", cmd_deletetree, + " ", "Delete existing MSTI"}, + {3, 0, "settreeprio", cmd_settreeprio, + " ", + "Set bridge priority (0-15) for the given MSTI"}, + /* Set global port */ + {3, 0, "setportpathcost", cmd_setportpathcost, + " ", + "Set port external path cost for the CIST (0 = auto)"}, + {3, 0, "setportadminedge", cmd_setportadminedge, + " {yes|no}", "Set initial edge state"}, + {3, 0, "setportautoedge", cmd_setportautoedge, + " {yes|no}", "Enable auto transition to/from edge state"}, + {3, 0, "setportp2p", cmd_setportp2p, + " {yes|no|auto}", "Set p2p detection mode"}, + {3, 0, "setportrestrrole", cmd_setportrestrrole, + " {yes|no}", "Restrict port ability to take Root role"}, + {3, 0, "setportrestrtcn", cmd_setportrestrtcn, + " {yes|no}", + "Restrict port ability to propagate received TCNs"}, + {2, 0, "portmcheck", cmd_portmcheck, + " ", "Try to get back from STP to rapid (RSTP/MSTP) mode"}, + /* Set tree port */ + {4, 0, "settreeportprio", cmd_settreeportprio, + " ", + "Set port priority (0-15) for the given MSTI"}, + {4, 0, "settreeportcost", cmd_settreeportcost, + " ", + "Set port internal path cost for the given MSTI (0 = auto)"}, + + /* Other */ + {1, 0, "debuglevel", cmd_debuglevel, "", "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 index 0000000..05f9620 --- /dev/null +++ b/ctl_socket_client.c @@ -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 + Authors: Vitalii Demianets + +******************************************************************************/ + +#include +#include +#include + +#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 index 0000000..ef482c2 --- /dev/null +++ b/ctl_socket_client.h @@ -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 + Authors: Vitalii Demianets + +******************************************************************************/ + +#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 index 0000000..27f01a6 --- /dev/null +++ b/ctl_socket_server.c @@ -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 + Authors: Vitalii Demianets + +******************************************************************************/ + +#include +#include + +#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 index 0000000..944e5ef --- /dev/null +++ b/ctl_socket_server.h @@ -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 + Authors: Vitalii Demianets + +******************************************************************************/ + +#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 index 0000000..e2c3442 --- /dev/null +++ b/epoll_loop.c @@ -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 + Authors: Vitalii Demianets + +******************************************************************************/ + +#include +#include + +#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 index 0000000..54f6a4d --- /dev/null +++ b/epoll_loop.h @@ -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 + Authors: Vitalii Demianets + +******************************************************************************/ + +#ifndef EPOLL_LOOP_H +#define EPOLL_LOOP_H + +#include +#include +#include + +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 index 0000000..bd2b249 --- /dev/null +++ b/hmac_md5.c @@ -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 +#include +#include + +#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 index 0000000..42238f9 --- /dev/null +++ b/libnetlink.c @@ -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, + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..2ebb9a7 --- /dev/null +++ b/libnetlink.h @@ -0,0 +1,55 @@ +#ifndef __LIBNETLINK_H__ +#define __LIBNETLINK_H__ + +#include +#include +#include + +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 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 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 + Authors: Vitalii Demianets + +******************************************************************************/ + +#ifndef LOG_H +#define LOG_H + +#include +#include + +#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 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 + Authors: Vitalii Demianets + +******************************************************************************/ + +/* #define MISC_TEST_FUNCS */ + +#include +#include + +#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 +#include + +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 +#include + +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 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 + */ + +/* 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 +#include +#include + +#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 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 + */ + +#ifndef MSTP_H +#define MSTP_H + +#include + +#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 index 0000000..da26a6f --- /dev/null +++ b/netif_utils.c @@ -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 + Authors: Vitalii Demianets + +******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..b2919c8 --- /dev/null +++ b/netif_utils.h @@ -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 + Authors: Vitalii Demianets + +******************************************************************************/ + +#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 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 + Authors: Stephen Hemminger + +******************************************************************************/ + +/* #define PACKET_DEBUG */ + +#include +#include +#include +#include +#include +#include +#include + +#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 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 + +******************************************************************************/ + +#ifndef PACKET_SOCK_H +#define PACKET_SOCK_H + +#include + +void packet_send(int ifindex, const struct iovec *iov, int iov_count, int len); +int packet_sock_init(void); + +#endif /* PACKET_SOCK_H */ -- 2.47.2