diff options
author | Stephen Hemminger <shemminger@linux-foundation.org> | 2007-03-21 16:23:46 -0700 |
---|---|---|
committer | Stephen Hemminger <shemminger@linux-foundation.org> | 2007-03-21 16:23:46 -0700 |
commit | ad02a0eb3054ed375fab1d527874f5b5d2b9f9a2 (patch) | |
tree | 97bd9c6d466aa5fdfda672cd756e5de51254f177 | |
download | rstp-ad02a0eb3054ed375fab1d527874f5b5d2b9f9a2.tar.gz |
Initial commit
sources from Aji_Srinivas@emc.com
97 files changed, 17213 insertions, 0 deletions
@@ -0,0 +1,16 @@ +bpdu_sock.o: bpdu_sock.c bpdu_sock.h epoll_loop.h include/linux/llc.h +bridge_config.o: bridge_config.c bridge_ctl.h bpdu_sock.h \ + rstplib/bitmap.h rstplib/uid_stp.h rstplib/stp_bpdu.h rstplib/stp_in.h \ + rstplib/stp_to.h log.h +brmon.o: brmon.c include/libnetlink.h include/linux/rtnetlink.h \ + bridge_ctl.h epoll_loop.h +brstate.o: brstate.c include/libnetlink.h include/linux/rtnetlink.h \ + bridge_ctl.h +epoll_loop.o: epoll_loop.c epoll_loop.h bridge_ctl.h +libnetlink.o: libnetlink.c include/libnetlink.h include/linux/rtnetlink.h +ctl_socket.o: ctl_socket.c ctl_socket.h rstplib/bitmap.h \ + rstplib/uid_stp.h rstplib/stp_bpdu.h rstplib/stp_in.h rstplib/stp_to.h \ + ctl_socket_server.h epoll_loop.h log.h +main.o: main.c epoll_loop.h bridge_ctl.h bpdu_sock.h ctl_socket_server.h \ + log.h +rstpd: bpdu_sock.o bridge_config.o brmon.o brstate.o epoll_loop.o libnetlink.o ctl_socket.o main.o diff --git a/CHANGES_TO_RSTPLIB b/CHANGES_TO_RSTPLIB new file mode 100644 index 0000000..b30b401 --- /dev/null +++ b/CHANGES_TO_RSTPLIB @@ -0,0 +1,574 @@ +Some changes have been made to RSTPLIB version rsttplib.1.1.0.2 for use in this +program. The changes are given in the following diff. +The changes fall into 3 categories: + +1. Changes to support dynamic addition and deletion of bridge ports. +2. Changes to support multiple bridges. +3. Fixes to protocol part based on 802.1w conformance testing results. + + +diff -Naur rstplib.1.1.02/base.h rstplib/base.h +--- rstplib.1.1.02/base.h 2002-01-20 00:33:22.000000000 -0800 ++++ rstplib/base.h 2006-10-26 13:43:15.000000000 -0700 +@@ -75,6 +75,7 @@ + CHOOSE(STP_Imlicite_Instance_Create_Failed), \ + CHOOSE(STP_Small_Bridge_Priority), \ + CHOOSE(STP_Large_Bridge_Priority), \ ++ CHOOSE(STP_Bridge_Priority_Not_A_Multiple_Of_4096), \ + CHOOSE(STP_Small_Hello_Time), \ + CHOOSE(STP_Large_Hello_Time), \ + CHOOSE(STP_Small_Max_Age), \ +@@ -83,6 +84,10 @@ + CHOOSE(STP_Large_Forward_Delay), \ + CHOOSE(STP_Forward_Delay_And_Max_Age_Are_Inconsistent),\ + CHOOSE(STP_Hello_Time_And_Max_Age_Are_Inconsistent), \ ++ CHOOSE(STP_Small_Port_Priority), \ ++ CHOOSE(STP_Large_Port_Priority), \ ++ CHOOSE(STP_Port_Priority_Not_A_Multiple_Of_16), \ ++ CHOOSE(STP_Large_Port_PCost), \ + CHOOSE(STP_Vlan_Had_Not_Yet_Been_Created), \ + CHOOSE(STP_Port_Is_Absent_In_The_Vlan), \ + CHOOSE(STP_Big_len8023_Format), \ +@@ -176,11 +181,11 @@ + + /* for debug trace messages */ + +-#ifdef __LINUX__ ++#ifdef __LINUX__USE_PRINTF_FOR_STRACE + extern char* sprint_time_stump (void); + #define stp_trace(F, B...) printf("%s:" F "\n", sprint_time_stump(), ##B) + #else +-extern ULONG stp_trace (const char* fmt, ...); ++extern void stp_trace (const char* fmt, ...); + #endif + + #endif /* _STP_BASE_H__ */ +diff -Naur rstplib.1.1.02/pcost.c rstplib/pcost.c +--- rstplib.1.1.02/pcost.c 2002-01-20 00:34:09.000000000 -0800 ++++ rstplib/pcost.c 2006-10-20 16:04:16.000000000 -0700 +@@ -70,8 +70,10 @@ + } + + static void +-updPortPathCost (STATE_MACH_T *this) ++updPortPathCost (PORT_T *port) + { ++ port->reselect = True; ++ port->selected = False; + } + + void +@@ -97,7 +99,7 @@ + port->usedSpeed = -1; + break; + case STABLE: +- updPortPathCost (this); ++ updPortPathCost (port); + break; + } + } +diff -Naur rstplib.1.1.02/port.c rstplib/port.c +--- rstplib.1.1.02/port.c 2002-01-20 00:34:10.000000000 -0800 ++++ rstplib/port.c 2006-10-20 16:04:16.000000000 -0700 +@@ -139,10 +139,10 @@ + this->port_id, + this->port_id); + STP_copy_times (&this->designTimes, &stpm->rootTimes); ++ this->fdWhile = 0; + } + + /* reset timers */ +- this->fdWhile = + this->helloWhen = + this->mdelayWhile = + this->rbWhile = +diff -Naur rstplib.1.1.02/portinfo.c rstplib/portinfo.c +--- rstplib.1.1.02/portinfo.c 2002-01-20 00:34:10.000000000 -0800 ++++ rstplib/portinfo.c 2006-10-20 16:04:16.000000000 -0700 +@@ -75,6 +75,12 @@ + + if (BPDU_RSTP == port->msgBpduType) { + port->msgPortRole = (port->msgFlags & PORT_ROLE_MASK) >> PORT_ROLE_OFFS; ++#ifndef ORIG ++ if (RSTP_PORT_ROLE_UNKN == port->msgPortRole) { ++ port->msgBpduVersion = FORCE_STP_COMPAT; ++ port->msgBpduType = BPDU_CONFIG_TYPE; ++ } ++#endif + } + + if (RSTP_PORT_ROLE_DESGN == port->msgPortRole || +@@ -109,10 +115,14 @@ + } + } + +- if (RSTP_PORT_ROLE_ROOT == port->msgBpduType && ++ if (RSTP_PORT_ROLE_ROOT == port->msgPortRole && + port->operPointToPointMac && ++ ! STP_VECT_compare_bridge_id (&port->msgPrio.root_bridge, ++ &port->portPrio.root_bridge) && ++ port->msgPrio.root_path_cost == port->portPrio.root_path_cost && + ! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, + &port->portPrio.design_bridge) && ++ port->msgPrio.design_port == port->portPrio.design_port && + AGREEMENT_BIT & port->msgFlags) { + #ifdef STP_DBG + if (this->debug) { +diff -Naur rstplib.1.1.02/stp_in.c rstplib/stp_in.c +--- rstplib.1.1.02/stp_in.c 2002-01-20 00:34:13.000000000 -0800 ++++ rstplib/stp_in.c 2006-10-20 16:04:16.000000000 -0700 +@@ -170,6 +170,11 @@ + return STP_Large_Bridge_Priority; + } + ++ if (uid_cfg->bridge_priority & ~MASK_BR_PRIO) { ++ stp_trace ("%d bridge_priority must be a multiple of 4096", (int) uid_cfg->bridge_priority); ++ return STP_Bridge_Priority_Not_A_Multiple_Of_4096; ++ } ++ + if (uid_cfg->hello_time < MIN_BR_HELLOT) { + stp_trace ("%d hello_time small", (int) uid_cfg->hello_time); + return STP_Small_Hello_Time; +@@ -815,8 +820,13 @@ + return 0; + } + ++#ifdef ORIG + int + STP_IN_set_port_cfg (IN int vlan_id, IN UID_STP_PORT_CFG_T* uid_cfg) ++#else ++int ++STP_IN_set_port_cfg (int vlan_id, int port_index, UID_STP_PORT_CFG_T* uid_cfg) ++#endif + { + register STPM_T* this; + register PORT_T* port; +@@ -831,12 +841,21 @@ + return STP_Vlan_Had_Not_Yet_Been_Created; + } + ++#ifdef ORIG + for (port_no = 1; port_no <= max_port; port_no++) { + if (! BitmapGetBit(&uid_cfg->port_bmp, port_no - 1)) continue; ++#else ++ port_no = port_index; ++ { ++#endif + + port = _stpapi_port_find (this, port_no); + if (! port) {/* port is absent in the stpm :( */ ++#ifdef ORIG + continue; ++#else ++ return STP_Port_Is_Absent_In_The_Vlan; ++#endif + } + + if (PT_CFG_MCHECK & uid_cfg->field_mask) { +@@ -845,10 +864,18 @@ + } + + if (PT_CFG_COST & uid_cfg->field_mask) { ++ if (uid_cfg->admin_port_path_cost > MAX_PORT_PCOST) ++ return STP_Large_Port_PCost; + port->adminPCost = uid_cfg->admin_port_path_cost; + } + + if (PT_CFG_PRIO & uid_cfg->field_mask) { ++ if (uid_cfg->port_priority < MIN_PORT_PRIO) ++ return STP_Small_Port_Priority; ++ if (uid_cfg->port_priority > MAX_PORT_PRIO) ++ return STP_Large_Port_Priority; ++ if (uid_cfg->port_priority & ~MASK_PORT_PRIO) ++ return STP_Port_Priority_Not_A_Multiple_Of_16; + port->port_id = (uid_cfg->port_priority << 8) + port_no; + } + +@@ -955,3 +982,114 @@ + return rstp_error_names[rstp_err_no]; + } + ++/*---------------- Dynamic port create / delete ------------------*/ ++ ++int STP_IN_port_create(int vlan_id, int port_index) ++{ ++ register STPM_T* this; ++ ++ this = stpapi_stpm_find (vlan_id); ++ ++ if (! this) { /* can't create stpm :( */ ++ return STP_Vlan_Had_Not_Yet_Been_Created; ++ } ++ ++ PORT_T *port = STP_port_create (this, port_index); ++ if (! port) { ++ /* can't add port :( */ ++ stp_trace ("can't create port %d", (int) port_index); ++ return STP_Cannot_Create_Instance_For_Port; ++ } ++ STP_port_init(port, this, True); ++ ++ STP_compute_bridge_id(this); ++ STP_stpm_update_after_bridge_management (this); ++ STP_stpm_update (this); ++ return 0; ++} ++ ++int STP_IN_port_delete(int vlan_id, int port_index) ++{ ++ register STPM_T* this; ++ PORT_T *port; ++ ++ this = stpapi_stpm_find (vlan_id); ++ ++ if (! this) { /* can't find stpm :( */ ++ return STP_Vlan_Had_Not_Yet_Been_Created; ++ } ++ ++ port = _stpapi_port_find (this, port_index); ++ if (! port) { ++ return STP_Port_Is_Absent_In_The_Vlan; ++ } ++ ++ STP_port_delete (port); ++ ++ STP_compute_bridge_id(this); ++ STP_stpm_update_after_bridge_management (this); ++ STP_stpm_update (this); ++ return 0; ++} ++ ++ ++/*--- For multiple STP instances - non multithread use ---*/ ++ ++struct stp_instance ++{ ++ STPM_T *bridges; ++#ifdef STP_DBG ++ int dbg_rstp_deny; ++#endif ++ int max_port; /* Remove this */ ++ int nev; ++ RSTP_EVENT_T tev; ++}; ++ ++struct stp_instance *STP_IN_instance_create(void) ++{ ++ struct stp_instance *p; ++ p = malloc(sizeof(*p)); ++ if (!p) return p; ++ p->bridges = NULL; ++#ifdef STP_DBG ++ p->dbg_rstp_deny = 0; ++#endif ++ p->max_port = 1024; ++ p->tev = RSTP_EVENT_LAST_DUMMY; ++ p->nev = 0; ++ return p; ++} ++ ++void STP_IN_instance_begin(struct stp_instance *p) ++{ ++ bridges = p->bridges; ++#ifdef STP_DBG ++ dbg_rstp_deny = p->dbg_rstp_deny; ++#endif ++ max_port = p->max_port; ++ tev = p->tev; ++ nev = p->nev; ++} ++ ++void STP_IN_instance_end(struct stp_instance *p) ++{ ++ p->bridges = bridges; ++#ifdef STP_DBG ++ p->dbg_rstp_deny = dbg_rstp_deny; ++#endif ++ p->max_port = max_port; ++ p->tev = tev; ++ p->nev = nev; ++} ++ ++void STP_IN_instance_delete(struct stp_instance *p) ++{ ++ STP_IN_instance_begin(p); ++ STP_IN_delete_all(); ++ STP_IN_instance_end(p); ++ free(p); ++} ++ ++ ++ +diff -Naur rstplib.1.1.02/stp_in.h rstplib/stp_in.h +--- rstplib.1.1.02/stp_in.h 2002-01-20 00:33:52.000000000 -0800 ++++ rstplib/stp_in.h 2006-10-20 16:04:16.000000000 -0700 +@@ -56,6 +56,7 @@ + #define DEF_BR_PRIO 32768 + #define MIN_BR_PRIO 0 + #define MAX_BR_PRIO 61440 ++#define MASK_BR_PRIO 0xf000 + + #define DEF_BR_HELLOT 2 + #define MIN_BR_HELLOT 1 +@@ -76,12 +77,15 @@ + #define DEF_PORT_PRIO 128 + #define MIN_PORT_PRIO 0 + #define MAX_PORT_PRIO 240 /* in steps of 16 */ ++#define MASK_PORT_PRIO 0xf0 + + #define DEF_ADMIN_NON_STP False + #define DEF_ADMIN_EDGE True + #define DEF_LINK_DELAY 3 /* see edge.c */ + #define DEF_P2P P2P_AUTO + ++#define MAX_PORT_PCOST 200000000 ++ + /* Section 1: Create/Delete/Start/Stop the RSTP instance */ + + void /* init the engine */ +@@ -101,6 +105,12 @@ + int + STP_IN_delete_all (void); + ++/* Port create/delete */ ++ ++int STP_IN_port_create(int vlan_id, int port_index); ++ ++int STP_IN_port_delete(int vlan_id, int port_index); ++ + /* Section 2. "Get" management */ + + Bool +@@ -136,9 +146,15 @@ + BITMAP_T* port_bmp, + UID_STP_CFG_T* uid_cfg); + ++#ifdef ORIG + int + STP_IN_set_port_cfg (int vlan_id, + UID_STP_PORT_CFG_T* uid_cfg); ++#else ++int ++STP_IN_set_port_cfg (int vlan_id, int port_index, ++ UID_STP_PORT_CFG_T* uid_cfg); ++#endif + + #ifdef STP_DBG + int STP_IN_dbg_set_port_trace (char* mach_name, int enadis, +@@ -168,6 +184,19 @@ + STP_IN_rx_bpdu (int vlan_id, int port_index, BPDU_T* bpdu, size_t len); + #endif + ++/*--- For multiple STP instances - non multithread use ---*/ ++ ++struct stp_instance; ++/* Create struct to hold STP instance state and initialize it. ++ A copy of all global state in the library. */ ++struct stp_instance *STP_IN_instance_create(void); ++/* Set context from this STP instance */ ++void STP_IN_instance_begin(struct stp_instance *p); ++/* Save context back to this STP instance */ ++void STP_IN_instance_end(struct stp_instance *p); ++/* Delete this STP instance */ ++void STP_IN_instance_delete(struct stp_instance *p); ++ + #ifdef _STP_MACHINE_H__ + /* Inner usage definitions & functions */ + +diff -Naur rstplib.1.1.02/stp_state.h rstplib/stp_state.h +--- rstplib.1.1.02/stp_state.h 1969-12-31 16:00:00.000000000 -0800 ++++ rstplib/stp_state.h 2006-10-20 16:04:16.000000000 -0700 +@@ -0,0 +1,7 @@ ++#ifndef _STP_STATE_H__ ++#define _STP_STATE_H__ ++ ++ ++ ++ ++#endif +diff -Naur rstplib.1.1.02/stpm.c rstplib/stpm.c +--- rstplib.1.1.02/stpm.c 2002-01-20 00:34:14.000000000 -0800 ++++ rstplib/stpm.c 2006-10-30 19:21:51.000000000 -0800 +@@ -26,7 +26,11 @@ + #include "stpm.h" + #include "stp_to.h" /* for STP_OUT_flush_lt */ + +-static STPM_T *bridges = NULL; ++/*static*/ STPM_T *bridges = NULL; ++ ++/* We can flush learned fdb by port, so set this in stpm.c and topoch.c */ ++/* This doesn't seem to solve the topology change problems. Don't use it yet */ ++//#define STRONGLY_SPEC_802_1W + + static int + _stp_stpm_init_machine (STATE_MACH_T* this) +@@ -217,9 +221,11 @@ + { + register PORT_T* port; + ++#ifdef ORIG + if (! this->ports) { /* there are not any ports :( */ + return STP_There_Are_No_Ports; + } ++#endif + + if (! STP_compute_bridge_id (this)) {/* can't compute bridge id ? :( */ + return STP_Cannot_Compute_Bridge_Prio; +@@ -289,19 +295,16 @@ + STP_compute_bridge_id (STPM_T* this) + { + register PORT_T* port; +- register PORT_T* min_num_port; +- int port_index = 0; ++ unsigned char old[6], new[6]; ++ memset(&old, 0xff, sizeof(old)); + + for (port = this->ports; port; port = port->next) { +- if (! port_index || port->port_index < port_index) { +- min_num_port = port; +- port_index = port->port_index; +- } ++ STP_OUT_get_port_mac (port->port_index, new); ++ if (memcmp(new, old, sizeof(old)) < 0) ++ memcpy(old, new, sizeof(old)); + } + +- if (! min_num_port) return NULL; /* IMHO, it may not be */ +- +- STP_OUT_get_port_mac (min_num_port->port_index, this->BrId.addr); ++ memcpy(this->BrId.addr, old, sizeof(old)); + + return &this->BrId; + } +diff -Naur rstplib.1.1.02/stpm.h rstplib/stpm.h +--- rstplib.1.1.02/stpm.h 2002-01-20 00:33:54.000000000 -0800 ++++ rstplib/stpm.h 2006-10-20 16:04:16.000000000 -0700 +@@ -103,6 +103,8 @@ + STPM_T * + STP_stpm_get_the_list (void); + ++extern STPM_T *bridges; ++ + void + STP_stpm_update_after_bridge_management (STPM_T* this); + +diff -Naur rstplib.1.1.02/stpmgmt.c rstplib/stpmgmt.c +--- rstplib.1.1.02/stpmgmt.c 2002-01-20 00:34:14.000000000 -0800 ++++ rstplib/stpmgmt.c 2006-10-20 16:04:16.000000000 -0700 +@@ -50,6 +50,11 @@ + this->BrTimes.ForwardDelay = init_cfg.forward_delay; + this->ForceVersion = (PROTOCOL_VERSION_T) init_cfg.force_version; + } ++#ifdef ORIG ++#else ++ if (this->admin_state != STP_ENABLED) ++ err_code = STP_stpm_enable(this, STP_ENABLED); ++#endif + + RSTP_CRITICAL_PATH_END; + return err_code; +@@ -145,10 +150,11 @@ + int + STP_IN_delete_all (void) + { +- register STPM_T* stpm; ++ register STPM_T* stpm, *next; + + RSTP_CRITICAL_PATH_START; +- for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { ++ for (stpm = STP_stpm_get_the_list (); stpm; stpm = next) { ++ next = stpm->next; + STP_stpm_enable (stpm, STP_DISABLED); + STP_stpm_delete (stpm); + } +diff -Naur rstplib.1.1.02/topoch.c rstplib/topoch.c +--- rstplib.1.1.02/topoch.c 2002-01-20 00:34:16.000000000 -0800 ++++ rstplib/topoch.c 2006-10-30 19:22:01.000000000 -0800 +@@ -40,6 +40,10 @@ + #define GET_STATE_NAME STP_topoch_get_state_name + #include "choose.h" + ++/* We can flush learned fdb by port, so set this in stpm.c and topoch.c */ ++/* This doesn't seem to solve the topology change problems. Don't use it yet */ ++//#define STRONGLY_SPEC_802_1W ++ + #ifndef STRONGLY_SPEC_802_1W + /* + * In many kinds of hardware the function +@@ -61,12 +65,13 @@ + if (this->debug) { + stp_trace("%s (%s, %s, %s, '%s')", + "flush", port->port_name, port->owner->name, +- LT_FLASH_ONLY_THE_PORT == type ? "this port" : "other ports", ++ "this port", + reason); + } + + bret = STP_OUT_flush_lt (port->port_index, port->owner->vlan_id, + LT_FLASH_ONLY_THE_PORT, reason); ++ return bret; + } + #endif + +@@ -103,7 +108,11 @@ + if (port->sendRSTP && port->operPointToPointMac) { + return 2 * port->owner->rootTimes.HelloTime; + } ++#ifdef ORIG + return port->owner->rootTimes.MaxAge; ++#else ++ return port->owner->rootTimes.MaxAge + port->owner->rootTimes.ForwardDelay; ++#endif + } + + void +diff -Naur rstplib.1.1.02/transmit.c rstplib/transmit.c +--- rstplib.1.1.02/transmit.c 2002-01-20 00:34:17.000000000 -0800 ++++ rstplib/transmit.c 2006-10-20 16:04:16.000000000 -0700 +@@ -99,7 +99,11 @@ + { + unsigned short len8023; + ++#ifdef ORIG + STP_OUT_get_port_mac (port_index, bpdu_packet.mac.src_mac); ++#else ++ /* Don't bother. LLC trasmits with correct source MAC, we don't supply it */ ++#endif + + bpdu_packet.hdr.bpdu_type = bpdu_type; + bpdu_packet.hdr.version = (BPDU_RSTP == bpdu_type) ? +@@ -110,7 +114,11 @@ + len8023 = htons ((unsigned short) (pkt_len + 3)); + memcpy (&bpdu_packet.eth.len8023, &len8023, 2); + ++#ifdef ORIG + if (pkt_len < MIN_FRAME_LENGTH) pkt_len = MIN_FRAME_LENGTH; ++#else ++ /* Don't do this. LLC puts in 802.3 length based on what we transmit */ ++#endif + return pkt_len; + } + +@@ -235,7 +243,7 @@ + + pkt_len = build_bpdu_header (port->port_index, + BPDU_RSTP, +- sizeof (BPDU_HEADER_T) + sizeof (BPDU_BODY_T) + 2); ++ sizeof (BPDU_HEADER_T) + sizeof (BPDU_BODY_T) + 1); + build_config_bpdu (port, False); + + switch (port->selectedRole) { +@@ -258,7 +266,12 @@ + } + + bpdu_packet.body.flags |= (role << PORT_ROLE_OFFS); +- ++#ifndef ORIG ++ if (port->forwarding) ++ bpdu_packet.body.flags |= FORWARD_BIT; ++ if (port->learning) ++ bpdu_packet.body.flags |= LEARN_BIT; ++#endif + if (port->synced) { + #if 0 /* def STP_DBG */ + if (port->roletrns->debug) @@ -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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cace491 --- /dev/null +++ b/Makefile @@ -0,0 +1,59 @@ + +DSOURCES = brmon.c brstate.c libnetlink.c epoll_loop.c bridge_track.c ctl_socket.c netif_utils.c main.c + +DOBJECTS = $(DSOURCES:.c=.o) + +CTLSOURCES = ctl_main.c ctl_cli_wrap.c ctl_socket_client.c + +CTLOBJECTS = $(CTLSOURCES:.c=.o) + +CC=gcc +CFLAGS = -Wall -Werror -O2 -g -D_REENTRANT -D__LINUX__ -DVERSION=$(version) -DBUILD=$(build) -DSTP_DBG=1 -I. -I./include -I./rstplib + +all: rstpd rstpctl + +rstplib: + make -C rstplib librstp.a + +.PHONY: rstplib + +rstpd: $(DOBJECTS) rstplib + $(CC) -o $@ $(DOBJECTS) -L ./rstplib -lrstp + +rstpctl: $(CTLOBJECTS) + $(CC) -o $@ $(CTLOBJECTS) + +clean: + rm -f *.o rstpd rstpctl + make -C rstplib clean + rm -fr $(TOPDIR) $(BUILDDIR) + +install: all + install -m 755 -d $(INSTALLPREFIX)/sbin + install -m 755 rstpd $(INSTALLPREFIX)/sbin + install -m 755 rstpctl $(INSTALLPREFIX)/sbin + install -m 755 -d $(INSTALLPREFIX)/usr/share/man/man8 + install -m 644 rstpd.8 $(INSTALLPREFIX)/usr/share/man/man8 + install -m 644 rstpctl.8 $(INSTALLPREFIX)/usr/share/man/man8 + +# RPM Building, as non root +version := 0.16 +build := 1 + +BUILDROOT := $(CURDIR)/rpm_buildroot +TOPDIR := $(CURDIR)/rpm_topdir + +RPMBUILD=rpmbuild +RPMDEFS=\ + --buildroot=$(BUILDROOT) \ + --define='_topdir $(TOPDIR)' \ + --define='VERSION $(version)' \ + --define='BUILD $(build)' + +rpm: + mkdir -p $(BUILDROOT) $(TOPDIR)/BUILD $(TOPDIR)/SOURCES $(TOPDIR)/RPMS + (cd .. ; tar cfz $(TOPDIR)/SOURCES/rstp-$(version).tgz --exclude rstp-$(version)/rpm_buildroot --exclude rstp-$(version)/rpm_topdir rstp-$(version)) + $(RPMBUILD) $(RPMDEFS) -bb rstp.spec + cp $(TOPDIR)/RPMS/*/rstp-$(version)-$(build).*.rpm . + cp $(TOPDIR)/RPMS/*/rstp-debuginfo-$(version)-$(build).*.rpm . + @@ -0,0 +1,6 @@ +Now, the STP instance is fully shutdown when the bridge is down, so it +is not possible to reconfigure RSTP at that time. + +We need better notifications about bridges and bridge ports getting +added and removed, independent of whether the interfaces are up or +down. diff --git a/bpdu_sock.c b/bpdu_sock.c new file mode 100644 index 0000000..4eb8b57 --- /dev/null +++ b/bpdu_sock.c @@ -0,0 +1,153 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#include "bpdu_sock.h" +#include "epoll_loop.h" +#include "netif_utils.h" + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <stdint.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/llc.h> + +#include "log.h" + +#ifndef AF_LLC +#define AF_LLC 26 +#endif + +static const uint8_t stp_mc[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; + +void bpdu_send(struct epoll_event_handler *h, unsigned char *data, int len) +{ + struct sockaddr_llc to; + memset(&to, 0, sizeof(to)); + to.sllc_family = AF_LLC; + to.sllc_arphrd = ARPHRD_ETHER; + to.sllc_sap = LLC_SAP_BSPAN; + memcpy(to.sllc_mac, stp_mc, ETH_ALEN); + + if (fcntl(h->fd, F_SETFL, 0) < 0) + ERROR("Error unsetting O_NONBLOCK: %m"); + + int l = sendto(h->fd, data, len, 0, (struct sockaddr *)&to, sizeof(to)); + if (l < 0) + ERROR("sendto failed: %m"); + else if (l != len) + ERROR("short write in sendto: %d instead of %d", l, len); + + if (fcntl(h->fd, F_SETFL, O_NONBLOCK) < 0) + ERROR("Error setting O_NONBLOCK: %m"); +} + +void bpdu_rcv_handler(uint32_t events, struct epoll_event_handler *h) +{ + struct sockaddr_llc from; + socklen_t fromlen = sizeof(from); + int cc; + unsigned char buf[2048]; + + cc = recvfrom(h->fd, &buf, sizeof(buf), 0, + (struct sockaddr *) &from, &fromlen); + if (cc <= 0) { + ERROR("recvfrom failed: %m"); + return; + } + +#if 0 + printf("Src %02x:%02x:%02x:%02x:%02x:%02x\n", + from.sllc_mac[0], from.sllc_mac[1], + from.sllc_mac[2], from.sllc_mac[3], + from.sllc_mac[4], from.sllc_mac[5]); + 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 + + bpdu_rcv(h->arg, buf, cc); +} + + +/* We added name as an arg here because we can't do if_indextoname here, + That needs <net/if.h> which conflicts with <linux/if.h> */ +/* Needs fixing. Socket should be closed in case of errors */ +int bpdu_sock_create(struct epoll_event_handler *h, + int if_index, char *name, struct ifdata *arg) +{ + struct sockaddr_llc llc_addr; + memset(&llc_addr, 0, sizeof(llc_addr)); + llc_addr.sllc_family = AF_LLC; + llc_addr.sllc_arphrd = ARPHRD_ETHER; + llc_addr.sllc_sap = LLC_SAP_BSPAN; + + int s; + TSTM((s = socket(AF_LLC, SOCK_DGRAM, 0)) >= 0, -1, "%m"); + + TST(get_hwaddr(name, llc_addr.sllc_mac) == 0, -1); + + TSTM(bind(s, (struct sockaddr *) &llc_addr, sizeof(llc_addr)) == 0, -1, + "Can't bind to LLC SAP %#x: %m", llc_addr.sllc_sap); + { + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name, IFNAMSIZ); + ifr.ifr_hwaddr.sa_family = AF_UNSPEC; + memcpy(ifr.ifr_hwaddr.sa_data, stp_mc, ETH_ALEN); + + TSTM(ioctl(s, SIOCADDMULTI, &ifr) == 0, -1, + "can't set multicast address for %s: %m", ifr.ifr_name); + } + + TSTM(fcntl(s, F_SETFL, O_NONBLOCK) == 0, -1, "%m"); + + h->fd = s; + h->arg = arg; + h->handler = bpdu_rcv_handler; + + if (add_epoll(h) < 0) + return -1; + + return 0; +} + +void bpdu_sock_delete(struct epoll_event_handler *h) +{ + remove_epoll(h); + close(h->fd); +} + diff --git a/bpdu_sock.h b/bpdu_sock.h new file mode 100644 index 0000000..14413e4 --- /dev/null +++ b/bpdu_sock.h @@ -0,0 +1,42 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#ifndef BPDU_SOCK_H +#define BPDU_SOCK_H + +#include "epoll_loop.h" + +struct ifdata; + +void bpdu_send(struct epoll_event_handler *h, unsigned char *data, int len); + +int bpdu_sock_create(struct epoll_event_handler *h, + int if_index, char *name, struct ifdata *ifdata); + +void bpdu_sock_delete(struct epoll_event_handler *h); + +/* Externally provided, we call it */ +void bpdu_rcv(struct ifdata *ifdata, unsigned char *data, int len); + +#endif @@ -0,0 +1,199 @@ +#!/bin/sh +# +# chkconfig: 2345 11 89 +# description: Brings up and configures the ethernet bridge +# processname: bridge + +# Source function library. +. /etc/init.d/functions + +# Check that networking is up. +if [ "$NETWORKING" = "no" ] +then + exit 0 +fi + +RETVAL=0 + +[ -f /etc/rsbridgeinit.conf ] && . /etc/rsbridgeinit.conf + +###### Sample of what /etc/rsbridgeinit.conf should look like +# bridgeprefix="gbr" +# #UPDATE_STRING=-b eth2 eth3 +# BRIDGES="0" +# CSIF[0]="eth2" +# SSIF[0]="eth3" +########################## + +#Enable RSTP if we have /sbin/rstpd +RSTPD=/sbin/rstpd +RSTPCTL=/sbin/rstpctl +RSTP=0 +[ -x $RSTPD -a -x $RSTPCTL ] && RSTP=1 + +slaves () { + cat /proc/net/bonding/$1 | grep 'Slave Interface' | cut -d: -f2 +} + +# set interrupt affinity to first cpu +setsmpaffinity() { + if [[ $1 == bond* ]] ; then + for sl in `slaves $1`; do + irq=`grep $sl /proc/interrupts | cut -d: -f1` + echo 1 > /proc/irq/$irq/smp_affinity + done + else + irq=`grep $1 /proc/interrupts | cut -d: -f1` + echo 1 > /proc/irq/$irq/smp_affinity + fi +} + +start () { + [ $RSTP == 1 ] && echo Starting rstpd && daemon $RSTPD ">>" /var/log/rstpd.log "2>&1" + for b in $BRIDGES ; do + echo Starting service bridge $bridgeprefix$b + brctl addbr $bridgeprefix$b || RETVAL=1 + if [ $RSTP == 0 ] ; then + brctl stp $bridgeprefix$b on + brctl setbridgeprio $bridgeprefix$b 65000 + fi + + for br in ${CSIF[$b]} ; do + echo Adding CSIF $br on $bridgeprefix$b + ifup $br + brctl addif $bridgeprefix$b $br || RETVAL=1 + done + + if [ "$1" != "client" ]; then + for br in ${SSIF[$b]} ; do + echo Adding SSIF $br on $bridgeprefix$b + ifup $br + if [[ $br == bond* ]] ; then + for sl in `slaves $br`; do + ifconfig $sl down + done + else + ifconfig $br down + fi + brctl addif $bridgeprefix$b $br || RETVAL=1 + done + fi + ifup $bridgeprefix$b + if [ $RSTP == 1 ]; then + rstpctl rstp $bridgeprefix$b on + rstpctl setbridgeprio $bridgeprefix$b 61440 + fi + done + + for b in $BRIDGES ; do + + . /etc/sysconfig/network-scripts/ifcfg-$bridgeprefix$b +# We will always have the subnet route entry. If there is a default gateway +# on that subnet, we will have an entry for that as well + if [ -n "$GATEWAY" ] ; then rttarget=2 ; else rttarget=1 ; fi + rtcount=x + + count=1 + while true ; do + new_rtcount=`grep -c $bridgeprefix$b /proc/net/route`; + if [ $new_rtcount != $rtcount ]; then +#DEBUG echo Number of route entries for $bridgeprefix$b is $new_rtcount + rtcount=$new_rtcount + fi + if [ $rtcount == $rttarget ]; then +#DEBUG echo Reached target for $bridgeprefix$b + break; + fi + count=`expr $count + 1` + if [ $count -gt 12 ]; then + echo Incomplete IP configuration for $bridgeprefix$b. Check network config. Aborting. + break; + fi + echo Incomplete IP configuration for $bridgeprefix$b. Waiting 5 seconds. + sleep 5 + done + done +} + +stop () { + for b in $BRIDGES ; do + echo "Shutting down service bridge $bridgeprefix$b" + for br in ${SSIF[$b]} ; do + echo Removing SSIF $br on $bridgeprefix$b + brctl delif $bridgeprefix$b $br || RETVAL=1 + done + for br in ${CSIF[$b]} ; do + echo Removing CSIF $br on $bridgeprefix$b + brctl delif $bridgeprefix$b $br || RETVAL=1 + done + ifconfig $bridgeprefix$b down || RETVAL=1 + brctl delbr $bridgeprefix$b || RETVAL=1 + done + [ $RSTP == 1 ] && killproc rstpd + +} + +serverif () { + case "$1" in + up) + for b in $BRIDGES ; do + for br in ${SSIF[$b]} ; do + echo Enabling $br on $bridgeprefix$b + if [[ $br == bond* ]] ; then + for sl in `slaves $br`; do + echo ' ' Enabling slave $sl of $br + ifconfig $sl up + done + else + ifconfig $br up + fi + done + done + ;; + down) + for b in $BRIDGES ; do + for br in ${SSIF[$b]} ; do + echo Disabling $br on $bridgeprefix$b + if [[ $br == bond* ]]; then + for sl in `slaves $br`; do + echo ' ' Disabling slave $sl of $br + ifconfig $sl down + done + else + ifconfig $br down + fi + done + done + ;; + *) + exit 1 + esac +} + +# See how we were called. +case "$1" in + start) + start $2 + ;; + stop) + stop $2 + ;; + status) + for b in $BRIDGES ; do + ifconfig $bridgeprefix$b + brctl showstp $bridgeprefix$b + done + ;; + serverif) + serverif $2 + ;; + restart|reload) + stop + start + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|reload}" + exit 1 +esac + +exit $RETVAL diff --git a/bridge_ctl.h b/bridge_ctl.h new file mode 100644 index 0000000..fdb1970 --- /dev/null +++ b/bridge_ctl.h @@ -0,0 +1,42 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#ifndef BRIDGE_CTL_H +#define BRIDGE_CTL_H + +int init_bridge_ops(void); + +void bridge_get_configuration(void); + +int bridge_set_state(int ifindex, int state); + +int bridge_send_bpdu(int ifindex, const unsigned char *data, int len); + +int bridge_notify(int br_index, int if_index, int newlink, int up); + +void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len); + +void bridge_one_second(void); + +#endif diff --git a/bridge_track.c b/bridge_track.c new file mode 100644 index 0000000..f761840 --- /dev/null +++ b/bridge_track.c @@ -0,0 +1,919 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#include "bridge_ctl.h" +#include "netif_utils.h" + +#include <unistd.h> +#include <net/if.h> +#include <linux/if_bridge.h> +#include <arpa/inet.h> +#include <sys/types.h> + +#include <bitmap.h> +#include <uid_stp.h> +#include <stp_bpdu.h> +#include <stp_in.h> +#include <stp_to.h> + +#include <stdio.h> +#include <string.h> + +#include "log.h" + + +/*------------------------------------------------------------*/ + +struct ifdata +{ + int if_index; + struct ifdata *next; + int up; + char name[IFNAMSIZ]; + + int is_bridge; + /* If bridge */ + struct ifdata *bridge_next; + struct ifdata *port_list; + int do_stp; + int stp_up; + struct stp_instance *stp; + UID_BRIDGE_ID_T bridge_id; + /* Bridge config */ + UID_STP_MODE_T stp_enabled; + int bridge_priority; + int max_age; + int hello_time; + int forward_delay; + int force_version; + int hold_time; + + /* If port */ + int speed; + int duplex; + struct ifdata *master; + struct ifdata *port_next; + /* STP port index */ + int port_index; + /* STP port config */ + int port_priority; + int admin_port_path_cost; + ADMIN_P2P_T admin_point2point; + unsigned char admin_edge; + unsigned char admin_non_stp; /* 1- doesn't participate in STP, 1 - regular */ +}; + +/* Instances */ +struct ifdata *current_br = NULL; + +void instance_begin(struct ifdata *br) +{ + if (current_br) { + ERROR("BUG: Trying to set instance over existing instance."); + ERROR("%d", *(int *)0); /* ABORT */ + } + current_br = br; + STP_IN_instance_begin(br->stp); +} + +void instance_end(void) +{ + STP_IN_instance_end(current_br->stp); + current_br = NULL; +} + +struct ifdata *find_port(int port_index) +{ + struct ifdata *ifc = current_br->port_list; + while (ifc && ifc->port_index != port_index) + ifc=ifc->port_next; + return ifc; +} + + +/*************************************************************/ +/* Bridge and port defaults */ + +UID_STP_CFG_T default_bridge_stp_cfg = { + .field_mask = BR_CFG_ALL, + .bridge_priority = DEF_BR_PRIO, + .max_age = DEF_BR_MAXAGE, + .hello_time = DEF_BR_HELLOT, + .forward_delay = DEF_BR_FWDELAY, + .force_version = DEF_FORCE_VERS, /*NORMAL_RSTP*/ +}; + +void update_bridge_stp_config(struct ifdata *br, UID_STP_CFG_T *cfg) +{ + if (cfg->field_mask & BR_CFG_PRIO) + br->bridge_priority = cfg->bridge_priority; + if (cfg->field_mask & BR_CFG_AGE) + br->max_age = cfg->max_age; + if (cfg->field_mask & BR_CFG_HELLO) + br->hello_time = cfg->hello_time; + if (cfg->field_mask & BR_CFG_DELAY) + br->forward_delay = cfg->forward_delay; + if (cfg->field_mask & BR_CFG_FORCE_VER) + br->force_version = cfg->force_version; +} + +UID_STP_PORT_CFG_T default_port_stp_cfg = { + .field_mask = PT_CFG_ALL, + .port_priority = DEF_PORT_PRIO, + .admin_non_stp = DEF_ADMIN_NON_STP, + .admin_edge = False, // DEF_ADMIN_EDGE, + .admin_port_path_cost = ADMIN_PORT_PATH_COST_AUTO, + .admin_point2point = DEF_P2P, +}; + +void update_port_stp_config(struct ifdata *ifc, UID_STP_PORT_CFG_T *cfg) +{ + if (cfg->field_mask & PT_CFG_PRIO) + ifc->port_priority = cfg->port_priority; + if (cfg->field_mask & PT_CFG_NON_STP) + ifc->admin_non_stp = cfg->admin_non_stp; + if (cfg->field_mask & PT_CFG_EDGE) + ifc->admin_edge = cfg->admin_edge; + if (cfg->field_mask & PT_CFG_COST) + ifc->admin_port_path_cost = cfg->admin_port_path_cost; + if (cfg->field_mask & PT_CFG_P2P) + ifc->admin_point2point = cfg->admin_point2point; +} + +/**************************************************************/ + +int add_port_stp(struct ifdata *ifc) +{ /* Bridge is ifc->master */ + char name[IFNAMSIZ]; + TST(if_indextoname(ifc->if_index, name) != 0, -1); + TST((ifc->port_index = get_bridge_portno(ifc->name)) >= 0, -1); + + /* Add port to STP */ + instance_begin(ifc->master); + int r = STP_IN_port_create(0, ifc->port_index); + if (r == 0) { /* Update bridge ID */ + UID_STP_STATE_T state; + STP_IN_stpm_get_state(0, &state); + ifc->master->bridge_id = state.bridge_id; + } + instance_end(); + if ( r/* check for failure */) { + ERROR("Couldn't add port for ifindex %d to STP", ifc->if_index); + return -1; + } + return 0; +} + +void remove_port_stp(struct ifdata *ifc) +{ + /* Remove port from STP */ + instance_begin(ifc->master); + int r = STP_IN_port_delete(0, ifc->port_index); + instance_end(); + ifc->port_index = -1; + if (r != 0) { + ERROR("removing port %s failed for bridge %s: %s", + ifc->name, ifc->master->name, STP_IN_get_error_explanation(r)); + } +} + +int init_rstplib_instance(struct ifdata *br) +{ + br->stp = STP_IN_instance_create(); + if (br->stp == NULL) { + ERROR("Couldn't create STP instance for bridge %s", br->name); + return -1; + } + + BITMAP_T ports; BitmapClear(&ports); + instance_begin(br); + int r = STP_IN_stpm_create(0, br->name, &ports); + instance_end(); + if (r != 0) { + ERROR("stpm create failed for bridge %s: %s", + br->name, STP_IN_get_error_explanation(r)); + return -1; + } + + return 0; +} + +void clear_rstplib_instance(struct ifdata *br) +{ + instance_begin(br); + int r = STP_IN_delete_all(); + instance_end(); + if (r != 0) { + ERROR("stpm delete failed for bridge %s: %s", + br->name, STP_IN_get_error_explanation(r)); + } + + STP_IN_instance_delete(br->stp); + br->stp = NULL; +} + +int init_bridge_stp(struct ifdata *br) +{ + if (br->stp_up) { + ERROR("STP already started"); + return 0; + } + + /* Init STP state */ + TST(init_rstplib_instance(br) == 0, -1); + + struct ifdata *p = br->port_list; + while (p) { + if (add_port_stp(p) != 0) + break; + p = p->port_next; + } + if (p) { + struct ifdata *q = br->port_list; + while (q != p) { + remove_port_stp(q); + q = q->port_next; + } + /* Clear bridge STP state */ + clear_rstplib_instance(br); + return -1; + } + br->stp_up = 1; + return 0; +} + +void clear_bridge_stp(struct ifdata *br) +{ + if (!br->stp_up) + return; + br->stp_up = 0; + struct ifdata *p = br->port_list; + while (p) { + remove_port_stp(p); + p = p->port_next; + } + /* Clear bridge STP state */ + clear_rstplib_instance(br); +} + + +struct ifdata *if_head = NULL; +struct ifdata *br_head = NULL; + +struct ifdata *find_if(int if_index) +{ + struct ifdata *p = if_head; + while (p && p->if_index != if_index) + p = p->next; + return p; +} + +#define ADD_TO_LIST(_list, _next, _ifc) \ + do { \ + (_ifc)->_next = (_list); \ + (_list) = (_ifc); \ + } while (0) + +#define REMOVE_FROM_LIST(_list, _next, _ifc, _error_fmt, _args...) \ + do { \ + struct ifdata **_prev = &(_list); \ + while (*_prev && *_prev != (_ifc)) \ + _prev = &(*_prev)->_next; \ + if (*_prev != (_ifc)) \ + ERROR(_error_fmt, ##_args); \ + else \ + *_prev = (_ifc)->_next; \ + } while (0) + +/* Caller ensures that there isn't any ifdata with this index */ +/* If br is NULL, new interface is a bridge, else it is a port of br */ +struct ifdata *create_if(int if_index, struct ifdata *br) +{ + struct ifdata *p; + TST((p = malloc(sizeof(*p))) != NULL, NULL); + + /* Init fields */ + p->if_index = if_index; + p->is_bridge = (br == NULL); + memset(p->name, 0, sizeof(p->name)); + if_indextoname(if_index, p->name); + if (p->is_bridge) { + INFO("Add bridge %s", p->name); + /* Init slave list */ + p->port_list = NULL; + + p->do_stp = 0; + p->up = 0; + p->stp_up = 0; + p->stp = NULL; + update_bridge_stp_config(p, &default_bridge_stp_cfg); + ADD_TO_LIST(br_head, bridge_next, p); /* Add to bridge list */ + } + else { + INFO("Add iface %s to bridge %s", p->name, br->name); + p->up = 0; + p->speed = 0; + p->duplex = 0; + p->master = br; + update_port_stp_config(p, &default_port_stp_cfg); + ADD_TO_LIST(br->port_list, port_next, p); /* Add to bridge port list */ + if (br->stp_up) { + add_port_stp(p); + } + } + /* Add to interface list */ + ADD_TO_LIST(if_head, next, p); + + return p; +} + +void delete_if(struct ifdata *ifc) +{ + INFO("Delete iface %s", ifc->name); + if (ifc->is_bridge) { /* Bridge: */ + /* Stop STP */ + clear_bridge_stp(ifc); + /* Delete ports */ + while (ifc->port_list) + delete_if(ifc->port_list); + /* Remove from bridge list */ + REMOVE_FROM_LIST(br_head, bridge_next, ifc, + "Can't find interface ifindex %d bridge list", + ifc->if_index); + } + else { /* Port */ + if (ifc->master->stp_up) + remove_port_stp(ifc); + /* Remove from bridge port list */ + REMOVE_FROM_LIST(ifc->master->port_list, port_next, ifc, + "Can't find interface ifindex %d on br %d's port list", + ifc->if_index, ifc->master->if_index); + } + /* Remove from bridge interface list */ + REMOVE_FROM_LIST(if_head, next, ifc, + "Can't find interface ifindex %d on iflist", + ifc->if_index); +} + +void set_br_up(struct ifdata *br, int up) +{ + if (up != br->up) { + br->up = up; + if (br->do_stp) + up?(void)init_bridge_stp(br):clear_bridge_stp(br); + } +} + +void set_if_up(struct ifdata *ifc, int up) +{ + INFO("Port %s : %s", ifc->name, (up?"up":"down")); + int speed = -1; + int duplex = -1; + int notify_flags = 0; + const int NOTIFY_UP = 1, NOTIFY_SPEED = 2, NOTIFY_DUPLEX = 4; + if (!up) { /* Down */ + if (ifc->up) { + ifc->up = up; + notify_flags |= NOTIFY_UP; + } + } + else { /* Up */ + int r = ethtool_get_speed_duplex(ifc->name, &speed, &duplex); + if (r < 0) { /* Didn't succeed */ + } + if (speed < 0) speed = 10; + if (duplex < 0) duplex = 0; /* Assume half duplex */ + + if (speed != ifc->speed) { + ifc->speed = speed; + notify_flags |= NOTIFY_SPEED; + } + if (duplex != ifc->duplex) { + ifc->duplex = duplex; + notify_flags |= NOTIFY_DUPLEX; + } + if (!ifc->up) { + ifc->up = 1; + notify_flags |= NOTIFY_UP; + } + } + if (notify_flags && ifc->master->stp_up) { + instance_begin(ifc->master); + + if (notify_flags & NOTIFY_SPEED) + STP_IN_changed_port_speed(ifc->port_index, speed); + if (notify_flags & NOTIFY_DUPLEX) + STP_IN_changed_port_duplex(ifc->port_index); + if (notify_flags & NOTIFY_UP) + STP_IN_enable_port(ifc->port_index, ifc->up); + + instance_end(); + } +} + +/*------------------------------------------------------------*/ + +int bridge_notify(int br_index, int if_index, int newlink, int up) +{ + if (up) up = 1; + LOG("br_index %d, if_index %d, up %d", br_index, if_index, up); + + struct ifdata *br = NULL; + if (br_index >= 0) { + br = find_if(br_index); + if (br && !br->is_bridge) { + ERROR("Notification shows non bridge interface %d as bridge.", br_index); + return -1; + } + if (!br) + br = create_if(br_index, NULL); + if (!br) { + ERROR("Couldn't create data for bridge interface %d", br_index); + return -1; + } + /* Bridge must be up if we get such notifications */ + if (!br->up) + set_br_up(br, 1); + } + + struct ifdata *ifc = find_if(if_index); + + if (br) { + if (ifc) { + if (ifc->is_bridge) { + ERROR("Notification shows bridge interface %d as slave of %d", + if_index, br_index); + return -1; + } + if (ifc->master != br) { + INFO("Device %d has come to bridge %d. " + "Missed notify for deletion from bridge %d", + if_index, br_index, ifc->master->if_index); + delete_if(ifc); + ifc = NULL; + } + } + if (!ifc) + ifc = create_if(if_index, br); + if (!ifc) { + ERROR("Couldn't create data for interface %d (master %d)", + if_index, br_index); + return -1; + } + if (!newlink && !is_bridge_slave(br->name, ifc->name)) { + /* brctl delif generates a DELLINK, but so does ifconfig <slave> down. + So check and delete if it has been removed. + */ + delete_if(ifc); + return 0; + } + if (ifc->up != up) + set_if_up(ifc, up); /* And speed and duplex */ + } + else { /* No br_index */ + if (!newlink) { + /* DELLINK not from bridge means interface unregistered. */ + /* Cleanup removed bridge or removed bridge slave */ + if (ifc) + delete_if(ifc); + return 0; + } + else { /* This may be a new link */ + if (!ifc) { + char ifname[IFNAMSIZ]; + if (if_indextoname(if_index, ifname) && is_bridge(ifname)) { + ifc = create_if(if_index, NULL); + if (!ifc) { + ERROR("Couldn't create data for bridge interface %d", if_index); + return -1; + } + } + } + if (ifc && !ifc->is_bridge && + !is_bridge_slave(ifc->master->name, ifc->name)) { + /* Interface might have left bridge and we might have missed deletion */ + delete_if(ifc); + return 0; + } + if (ifc && ifc->up != up) { + if (ifc->is_bridge) + set_br_up(ifc, up); + else + set_if_up(ifc, up); + } + } + } + return 0; +} + +void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len) +{ + LOG("ifindex %d, len %d", if_index, len); + struct ifdata *ifc = find_if(if_index); + TST(ifc && !ifc->is_bridge, ); + TST(ifc->up && ifc->master->stp_up, ); + BPDU_T bpdu; + memset(&bpdu.eth, 0, sizeof(bpdu.eth)); + if (len > sizeof(bpdu) - sizeof(bpdu.eth)) + len = sizeof(bpdu) - sizeof(bpdu.eth); + memcpy(&bpdu.hdr, data, len); + /* Do some validation */ + TST(len >= 4, ); + TST(bpdu.hdr.protocol[0] == 0 && bpdu.hdr.protocol[1] == 0, ); + switch (bpdu.hdr.bpdu_type) { + case BPDU_RSTP: + TST(len >= 36, ); + case BPDU_CONFIG_TYPE: + TST(len >= 35, ); + /* 802.1w doesn't ask for this */ + // TST(ntohs(*(uint16_t*)bpdu.body.message_age) + // < ntohs(*(uint16_t*)bpdu.body.max_age), ); + TST(memcmp(bpdu.body.bridge_id, &ifc->master->bridge_id, 8) != 0 || + (ntohs(*(uint16_t *)bpdu.body.port_id) & 0xfff) != ifc->port_index, ); + break; + case BPDU_TOPO_CHANGE_TYPE: + break; + default: + TST(0, ); + } + + // dump_hex(data, len); + instance_begin(ifc->master); + int r = STP_IN_rx_bpdu(0, ifc->port_index, &bpdu, len + sizeof(bpdu.eth)); + if (r) + ERROR("STP_IN_rx_bpdu on port %s returned %s", ifc->name, + STP_IN_get_error_explanation(r)); + instance_end(); +} + +void bridge_one_second(void) +{ + // LOG(""); + struct ifdata *br; + for (br = br_head; br; br = br->bridge_next) { + if (br->stp_up) { + instance_begin(br); + STP_IN_one_second(); + instance_end(); + } + } + + /* To get information about port changes when bridge is down */ + /* But won't work so well since we will not sense deletions */ + static int count = 0; + count++; + if (count % 60 == 0) + bridge_get_configuration(); + +} + +/* Implementing STP_OUT functions */ + +int flush_port(char *sys_name) +{ + FILE *f = fopen(sys_name, "w"); + TSTM(f, -1, "Couldn't open flush file %s for write.", sys_name); + int r = fwrite("1", 1, 1, f); + fclose(f); + TST(r == 1, -1); + return 0; +} + +int +STP_OUT_flush_lt (IN int port_index, IN int vlan_id, + IN LT_FLASH_TYPE_T type, IN char* reason) +{ + LOG("port index %d, flash type %d, reason %s", port_index, type, reason); + TST(vlan_id == 0, 0); + + char fname[128]; + if (port_index == 0) {/* i.e. passed port_index was 0 */ + sprintf(fname, "/sys/class/net/%s/bridge/flush", current_br->name); + flush_port(fname); + } + else if (type == LT_FLASH_ONLY_THE_PORT) { + struct ifdata *port = find_port(port_index); + TST(port != NULL, 0); + sprintf(fname, "/sys/class/net/%s/brif/%s/flush", + current_br->name, port->name); + flush_port(fname); + } + else if (type == LT_FLASH_ALL_PORTS_EXCLUDE_THIS) { + struct ifdata *port; + for (port = current_br->port_list; port; port = port->port_next) { + if (port->port_index != port_index) { + sprintf(fname, "/sys/class/net/%s/brif/%s/flush", + current_br->name, port->name); + flush_port(fname); + } + } + } + else + TST(0, 0); + + return 0; +} + +void /* for bridge id calculation */ +STP_OUT_get_port_mac (IN int port_index, OUT unsigned char* mac) +{ + LOG("port index %d", port_index); + struct ifdata *port = find_port(port_index); + TST(port != NULL, ); + get_hwaddr(port->name, mac); +} + +unsigned long +STP_OUT_get_port_oper_speed (IN unsigned int port_index) +{ + LOG("port index %d", port_index); + struct ifdata *port = find_port(port_index); + TST(port != NULL, 0); + LOG("Speed: %d", port->speed); + return port->speed; +} + +int /* 1- Up, 0- Down */ +STP_OUT_get_port_link_status (IN int port_index) +{ + LOG("port index %d", port_index); + struct ifdata *port = find_port(port_index); + TST(port != NULL, 0); + LOG("Link status: %d", port->up); + return port->up; +} + +int /* 1- Full, 0- Half */ +STP_OUT_get_duplex (IN int port_index) +{ + LOG("port index %d", port_index); + struct ifdata *port = find_port(port_index); + TST(port != NULL, 0); + LOG("Duplex: %d", port->duplex); + return port->duplex; +} + +int +STP_OUT_set_port_state (IN int port_index, IN int vlan_id, IN RSTP_PORT_STATE state) +{ + LOG("port index %d, state %d", port_index, state); + struct ifdata *port = find_port(port_index); + TST(port != NULL, 0); + TST(vlan_id == 0, 0); + + int br_state; + switch (state) { + case UID_PORT_DISCARDING: + br_state = BR_STATE_BLOCKING; break; + case UID_PORT_LEARNING: + br_state = BR_STATE_LEARNING; break; + case UID_PORT_FORWARDING: + br_state = BR_STATE_FORWARDING; break; + default: + fprintf(stderr, "set_port_state: Unexpected state %d\n", state); + return -1; + } + if (port->up) + bridge_set_state(port->if_index, br_state); + return 0; +} + +int +STP_OUT_set_hardware_mode (int vlan_id, UID_STP_MODE_T mode) +{ + LOG("vlan id %d, mode %d", vlan_id, mode); + return 0; +} + +int +STP_OUT_tx_bpdu (IN int port_index, IN int vlan_id, + IN unsigned char* bpdu, + IN size_t bpdu_len) +{ + LOG("port index %d, len %zd", port_index, bpdu_len); + struct ifdata *port = find_port(port_index); + TST(port != NULL, 0); + TST(vlan_id == 0, 0); + // dump_hex(bpdu + sizeof(MAC_HEADER_T) + sizeof(ETH_HEADER_T), + // bpdu_len - (sizeof(MAC_HEADER_T) + sizeof(ETH_HEADER_T))); + bridge_send_bpdu(port->if_index, + bpdu + sizeof(MAC_HEADER_T) + sizeof(ETH_HEADER_T), + bpdu_len); // The length we get excludes headers! + return 0; +} + +const char * +STP_OUT_get_port_name (IN int port_index) +{ + LOG("port index %d", port_index); + struct ifdata *port = find_port(port_index); + TST(port != NULL, 0); + return port->name; +} + +int +STP_OUT_get_init_stpm_cfg (IN int vlan_id, + INOUT UID_STP_CFG_T* cfg) +{ + LOG(""); + TST(vlan_id == 0, 0); + + cfg->bridge_priority = current_br->bridge_priority; + cfg->max_age = current_br->max_age; + cfg->hello_time = current_br->hello_time; + cfg->forward_delay = current_br->forward_delay; + cfg->force_version = current_br->force_version; + + return 0; +} + +int +STP_OUT_get_init_port_cfg (IN int vlan_id, + IN int port_index, + INOUT UID_STP_PORT_CFG_T* cfg) +{ + LOG("port index %d", port_index); + struct ifdata *port = find_port(port_index); + TST(port != NULL, 0); + TST(vlan_id == 0, 0); + + cfg->port_priority = port->port_priority; + cfg->admin_non_stp = port->admin_non_stp; + cfg->admin_edge = port->admin_edge; + cfg->admin_port_path_cost = port->admin_port_path_cost; + cfg->admin_point2point = port->admin_point2point; + + return 0; +} + +extern void stp_trace (const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vDprintf(LOG_LEVEL_RSTPLIB, fmt, ap); + va_end(ap); +} + +/* Commands and status */ +#include "ctl_functions.h" + +#define CTL_CHECK_BRIDGE \ + struct ifdata *br = find_if(br_index); \ + if (br == NULL || !br->is_bridge) return Err_Interface_not_a_bridge; \ + if (!br->do_stp) return Err_Bridge_RSTP_not_enabled; \ + if (!br->stp_up) return Err_Bridge_is_down; \ + do { } while (0) + +#define CTL_CHECK_BRIDGE_PORT \ + CTL_CHECK_BRIDGE; \ + struct ifdata *port = find_if(port_index); \ + if (port == NULL || port->is_bridge || port->master != br) \ + return Err_Port_does_not_belong_to_bridge; \ + do { } while (0) + +int CTL_enable_bridge_rstp(int br_index, int enable) +{ + INFO("bridge %d, enable %d", br_index, enable); + int r = 0; + if (enable) enable = 1; + struct ifdata *br = find_if(br_index); + if (br == NULL) { + char ifname[IFNAMSIZ]; + if (if_indextoname(br_index, ifname) && is_bridge(ifname)) + br = create_if(br_index, NULL); + } + if (br == NULL || !br->is_bridge) return Err_Interface_not_a_bridge; + if (br->do_stp != enable) { + br->do_stp = enable; + if (br->up) + r = enable?init_bridge_stp(br):(clear_bridge_stp(br), 0); + } + return r; +} + +int CTL_get_bridge_state(int br_index, + UID_STP_CFG_T *cfg, UID_STP_STATE_T *state) +{ + LOG("bridge %d", br_index); + CTL_CHECK_BRIDGE; + int r; + instance_begin(br); + r = STP_IN_stpm_get_state (0, state); + if (r) { + ERROR("Error getting bridge state for %d: %s", br_index, + STP_IN_get_error_explanation(r)); + instance_end(); + return r; + } + r = STP_IN_stpm_get_cfg(0, cfg); + if (r) { + ERROR("Error getting bridge config for %d: %s", br_index, + STP_IN_get_error_explanation(r)); + instance_end(); + return r; + } + instance_end(); + return 0; +} + +int CTL_set_bridge_config(int br_index, + UID_STP_CFG_T *cfg) +{ + INFO("bridge %d, flags %#lx", br_index, cfg->field_mask); + CTL_CHECK_BRIDGE; + int r; + instance_begin(br); + r = STP_IN_stpm_set_cfg (0, NULL, cfg); + if (r) { + ERROR("Error setting bridge config for %d: %s", br_index, + STP_IN_get_error_explanation(r)); + instance_end(); + return r; + } + instance_end(); + /* Change init config in ifdata so it will be applied if we + disable and enable rstp*/ + update_bridge_stp_config(br, cfg); + return 0; +} + +int CTL_get_port_state(int br_index, int port_index, + UID_STP_PORT_CFG_T *cfg, UID_STP_PORT_STATE_T *state) +{ + LOG("bridge %d port %d", br_index, port_index); + CTL_CHECK_BRIDGE_PORT; + int r; + instance_begin(br); + state->port_no = port->port_index; + r = STP_IN_port_get_state (0, state); + if (r) { + ERROR("Error getting port state for port %d, bridge %d: %s", + port->port_index, br_index, STP_IN_get_error_explanation(r)); + instance_end(); + return r; + } + r = STP_IN_port_get_cfg(0, port->port_index, cfg); + if (r) { + ERROR("Error getting port config for port %d, bridge %d: %s", + port->port_index, br_index, STP_IN_get_error_explanation(r)); + instance_end(); + return r; + } + instance_end(); + return 0; + +} + +int CTL_set_port_config(int br_index, int port_index, + UID_STP_PORT_CFG_T *cfg) +{ + INFO("bridge %d, port %d, flags %#lx", br_index, port_index, + cfg->field_mask); + CTL_CHECK_BRIDGE_PORT; + int r; + instance_begin(br); + r = STP_IN_set_port_cfg (0, port->port_index, cfg); + if (r) { + ERROR("Error setting port config for port %d, bridge %d: %s", + port->port_index, br_index, STP_IN_get_error_explanation(r)); + instance_end(); + return r; + } + instance_end(); + /* Change init config in ifdata so it will be applied if we + disable and enable rstp*/ + update_port_stp_config(port, cfg); + return 0; +} + +int CTL_set_debug_level(int level) +{ + INFO("level %d", level); + log_level = level; + return 0; +} + + +#undef CTL_CHECK_BRIDGE_PORT +#undef CTL_CHECK_BRIDGE @@ -0,0 +1,300 @@ +/* + * brmon.c RTnetlink listener. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Stephen Hemminger <shemminger@osdl.org> + * + * Modified by Srinivas Aji <Aji_Srinivas@emc.com> for use + * in RSTP daemon. - 2006-09-01 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <net/if.h> +#include <netinet/in.h> +#include <linux/if_bridge.h> +#include <string.h> + +#include "libnetlink.h" + +#include "bridge_ctl.h" + +static const char SNAPSHOT[] = "v0.1"; + + +/* 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 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; + + if (n->nlmsg_type == NLMSG_DONE) + return 0; + + len -= NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) { + return -1; + } + +#if 0 + + if (filter.ifindex && ifi->ifi_index != filter.ifindex) + return 0; + + if (filter.up && !(ifi->ifi_flags&IFF_UP)) + return 0; +#endif + if (ifi->ifi_family != AF_BRIDGE && ifi->ifi_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; + + /* Check for BPDU */ + if (tb[IFLA_PRIORITY] && af_family == AF_BRIDGE) { + bridge_bpdu_rcv(ifi->ifi_index, + RTA_DATA(tb[IFLA_PRIORITY]), + RTA_PAYLOAD(tb[IFLA_PRIORITY])); + 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, + tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>"); + + + 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); + { + int newlink = (n->nlmsg_type == RTM_NEWLINK); + int up = 0; + if (newlink && tb[IFLA_OPERSTATE]) { + int state = *(int*)RTA_DATA(tb[IFLA_OPERSTATE]); + up = (state == IF_OPER_UP) || (state == IF_OPER_UNKNOWN); + } + + bridge_notify((tb[IFLA_MASTER]?*(int*)RTA_DATA(tb[IFLA_MASTER]):-1), + ifi->ifi_index, newlink, up); + } + return 0; +} + +#if 0 +static void usage(void) +{ + fprintf(stderr, "Usage: brmon\n"); + exit(-1); +} + +static int matches(const char *cmd, const char *pattern) +{ + int len = strlen(cmd); + if (len > strlen(pattern)) + return -1; + return memcmp(pattern, cmd, len); +} + +int +main(int argc, char **argv) +{ + struct rtnl_handle rth; + unsigned groups = ~RTMGRP_TC; + int llink = 0; + int laddr = 0; + + while (argc > 1) { + if (matches(argv[1], "-Version") == 0) { + printf("brmon %s\n", SNAPSHOT); + exit(0); + } else if (matches(argv[1], "link") == 0) { + llink=1; + groups = 0; + } else if (matches(argv[1], "bridge") == 0) { + laddr=1; + groups = 0; + } else if (strcmp(argv[1], "all") == 0) { + groups = ~RTMGRP_TC; + } else if (matches(argv[1], "help") == 0) { + usage(); + } else { + fprintf(stderr, "Argument \"%s\" is unknown, try \"rtmon help\".\n", argv[1]); + exit(-1); + } + argc--; argv++; + } + + if (llink) + groups |= RTMGRP_LINK; + + if (rtnl_open(&rth, groups) < 0) + exit(1); + + if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETLINK) < 0) { + perror("Cannot send dump request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, dump_msg, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + return 1; + } + + if (rtnl_listen(&rth, dump_msg, stdout) < 0) + exit(2); + + exit(0); +} +#endif + +#include "bridge_ctl.h" +#include "epoll_loop.h" + +struct rtnl_handle rth; +struct epoll_event_handler br_handler; + +struct rtnl_handle rth_state; + +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; +} + +/* Send message. Response is through bridge_notify */ +void bridge_get_configuration(void) +{ + if (rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETLINK) < 0) { + fprintf(stderr, "Cannot send dump request: %m\n"); + } +} diff --git a/brstate.c b/brstate.c new file mode 100644 index 0000000..f0f9d36 --- /dev/null +++ b/brstate.c @@ -0,0 +1,160 @@ +/* + * brstate.c RTnetlink port state change + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Stephen Hemminger <shemminger@osdl.org> + * + * Modified by Srinivas Aji <Aji_Srinivas@emc.com> for use + * in RSTP daemon. - 2006-09-01 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <net/if.h> +#include <netinet/in.h> +#include <linux/if_bridge.h> +#include <string.h> + +#include "libnetlink.h" + +#if 0 +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 int portstate(const char *name) +{ + int i; + + for (i = 0; i < sizeof(port_states)/sizeof(port_states[0]); i++) { + if (strcasecmp(name, port_states[i]) == 0) + return i; + } + return -1; +} +#endif + +static int br_set_state(struct rtnl_handle *rth, unsigned ifindex, __u8 state) +{ + struct { + struct nlmsghdr n; + struct ifinfomsg ifi; + char buf[256]; + } req; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_REPLACE; + req.n.nlmsg_type = RTM_SETLINK; + req.ifi.ifi_family = AF_BRIDGE; + req.ifi.ifi_index = ifindex; + + addattr32(&req.n, sizeof(req.buf), IFLA_PROTINFO, state); + + return rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL); +} + +static int br_send_bpdu(struct rtnl_handle *rth, unsigned ifindex, + const unsigned char *data, int len) +{ + struct { + struct nlmsghdr n; + struct ifinfomsg ifi; + char buf[256]; + } req; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_REPLACE; + req.n.nlmsg_type = RTM_SETLINK; + req.ifi.ifi_family = AF_BRIDGE; + req.ifi.ifi_index = ifindex; + + addattr_l(&req.n, sizeof(req.buf), IFLA_PRIORITY, data, len); + + return rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL); +} + +#if 0 +int main(int argc, char **argv) +{ + unsigned int ifindex; + int err, brstate; + struct rtnl_handle rth; + + + if (argc != 3) { + fprintf(stderr, + "Usage: brstate ifname state\n"); + exit(-1); + } + + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "brstate: can't setup netlink\n"); + exit(1); + } + + ifindex = if_nametoindex(argv[1]); + if (ifindex == 0) { + fprintf(stderr, "brstate: unknown interface '%s'\n", argv[1]); + exit(1); + } + + brstate = portstate(argv[2]); + if (brstate < 0) { + fprintf(stderr, "brstate: unknown port state '%s'\n", + argv[2]); + exit(1); + } + + err = br_set_state(&rth, ifindex, brstate); + if (err) { + fprintf(stderr, "brstate: set %d, %d failed %d\n", + ifindex, brstate, err); + exit(1); + } + + rtnl_close(&rth); + return 0; +} +#endif + +#include "bridge_ctl.h" + +extern struct rtnl_handle rth_state; + +int bridge_set_state(int ifindex, int brstate) +{ + int err = br_set_state(&rth_state, ifindex, brstate); + if (err < 0) { + fprintf(stderr, "Couldn't set bridge state, ifindex %d, state %d\n", + ifindex, brstate); + return -1; + } + return 0; +} + +int bridge_send_bpdu(int ifindex, const unsigned char *data, int len) +{ + int err = br_send_bpdu(&rth_state, ifindex, data, len); + if (err < 0) { + fprintf(stderr, "Couldn't send bpdu, ifindex %d\n", ifindex); + return -1; + } + return 0; +} diff --git a/ctl_cli_wrap.c b/ctl_cli_wrap.c new file mode 100644 index 0000000..7b17964 --- /dev/null +++ b/ctl_cli_wrap.c @@ -0,0 +1,67 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#include "ctl_functions.h" +#include "ctl_socket.h" +#include "ctl_socket_client.h" +#include "log.h" + +CLIENT_SIDE_FUNCTION(enable_bridge_rstp) +CLIENT_SIDE_FUNCTION(get_bridge_state) +CLIENT_SIDE_FUNCTION(set_bridge_config) +CLIENT_SIDE_FUNCTION(get_port_state) +CLIENT_SIDE_FUNCTION(set_port_config) +CLIENT_SIDE_FUNCTION(set_debug_level) + +#include <base.h> + +const char *CTL_error_explanation (int err_no) +{ +#define CHOOSE(a) #a + static const char* rstp_error_names[] = RSTP_ERRORS; + static const char *ctl_error_names[] = { CTL_ERRORS }; + +#undef CHOOSE + if (err_no < 0) + return "Error doing ctl command"; + else if (err_no >= STP_OK && err_no < STP_LAST_DUMMY) + return rstp_error_names[err_no]; + else if (err_no > Err_Dummy_Start && err_no < Err_Dummy_End) + return ctl_error_names[err_no - Err_Dummy_Start - 1]; + + static char buf[32]; + sprintf(buf, "Unknown error code %d", err_no); + return buf; +} + +void Dprintf(int level, const char* fmt, ...) +{ + char logbuf[256]; + 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_functions.h b/ctl_functions.h new file mode 100644 index 0000000..cf15fe9 --- /dev/null +++ b/ctl_functions.h @@ -0,0 +1,65 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#ifndef CTL_FUNCTIONS_H +#define CTL_FUNCTIONS_H + +#include <bitmap.h> +#include <uid_stp.h> + +int CTL_enable_bridge_rstp(int br_index, int enable); + +int CTL_get_bridge_state(int br_index, + UID_STP_CFG_T *cfg, UID_STP_STATE_T *state); + +int CTL_set_bridge_config(int br_index, + UID_STP_CFG_T *cfg); + +int CTL_get_port_state(int br_index, int port_index, + UID_STP_PORT_CFG_T *cfg, UID_STP_PORT_STATE_T *state); + +int CTL_set_port_config(int br_index, int port_index, + UID_STP_PORT_CFG_T *cfg); + +int CTL_set_debug_level(int level); + +#define CTL_ERRORS \ + CHOOSE(Err_Interface_not_a_bridge), \ + CHOOSE(Err_Bridge_RSTP_not_enabled), \ + CHOOSE(Err_Bridge_is_down), \ + CHOOSE(Err_Port_does_not_belong_to_bridge), \ + +#define CHOOSE(a) a + +enum Errors { + Err_Dummy_Start = 1000, + CTL_ERRORS + Err_Dummy_End +}; + +#undef CHOOSE + +const char *CTL_error_explanation(int err); + +#endif diff --git a/ctl_main.c b/ctl_main.c new file mode 100644 index 0000000..6e2b840 --- /dev/null +++ b/ctl_main.c @@ -0,0 +1,783 @@ +/* + Command parsing taken from brctl utility. + Display code from stp_cli.c in rstplib. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/errno.h> +#include <getopt.h> + +#include <net/if.h> + +/* For scanning through sysfs directories */ +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "ctl_socket_client.h" +#include "ctl_functions.h" + +#ifndef False +# define False 0 +# define True 1 +#endif + +#define STP_IN_get_error_explanation CTL_error_explanation + +static void +print_bridge_id (UID_BRIDGE_ID_T *bridge_id, unsigned char cr) +{ + printf("%04lX-%02x%02x%02x%02x%02x%02x", + (unsigned long) bridge_id->prio, + (unsigned char) bridge_id->addr[0], + (unsigned char) bridge_id->addr[1], + (unsigned char) bridge_id->addr[2], + (unsigned char) bridge_id->addr[3], + (unsigned char) bridge_id->addr[4], + (unsigned char) bridge_id->addr[5]); + if (cr) + printf("\n"); +} + +static char * +stp_state2str (RSTP_PORT_STATE stp_port_state, int detail) +{ + if (detail) { + switch (stp_port_state) { + case UID_PORT_DISABLED: return "Disabled"; + case UID_PORT_DISCARDING: return "Discarding"; + case UID_PORT_LEARNING: return "Learning"; + case UID_PORT_FORWARDING: return "Forwarding"; + case UID_PORT_NON_STP: return "NoStp"; + default: return "Unknown"; + } + } + + switch (stp_port_state) { + case UID_PORT_DISABLED: return "Dis"; + case UID_PORT_DISCARDING: return "Blk"; + case UID_PORT_LEARNING: return "Lrn"; + case UID_PORT_FORWARDING: return "Fwd"; + case UID_PORT_NON_STP: return "Non"; + default: return "Unk"; + } +} + +static void CLI_out_port_id (int port, unsigned char cr) +{ + static char ifname[IFNAMSIZ]; + if (if_indextoname(port, ifname)) + printf("%s", ifname); + else + printf ("Ifindex %02d", port); + if (cr) + printf("\n"); +} + +int get_index_die(const char *ifname, const char *doc, int die) +{ + int r = if_nametoindex(ifname); + if (r == 0) { + fprintf(stderr, "Can't find index for %s %s. Not a valid interface.\n", + doc, ifname); + if (die) + exit(1); + return -1; + } + return r; +} + +int get_index(const char *ifname, const char *doc) +{ + return get_index_die(ifname, doc, 1); +} + +static int cmd_rstp(int argc, char *const* argv) +{ + int stp, r; + int br_index = get_index(argv[1], "bridge"); + + if (!strcmp(argv[2], "on") || !strcmp(argv[2], "yes") + || !strcmp(argv[2], "1")) + stp = 1; + else if (!strcmp(argv[2], "off") || !strcmp(argv[2], "no") + || !strcmp(argv[2], "0")) + stp = 0; + else { + fprintf(stderr, "expect on/off for argument\n"); + return 1; + } + r = CTL_enable_bridge_rstp(br_index, stp); + if (r) { + fprintf(stderr, "Failed to enable/disable RSTP: %s\n", + CTL_error_explanation(r)); + return -1; + } + return 0; +} + +static int do_showbridge(const char *br_name) +{ + UID_STP_STATE_T uid_state; + UID_STP_CFG_T uid_cfg; + + int br_index = get_index_die(br_name, "bridge", 0); + if (br_index < 0) + return -1; + + int r = CTL_get_bridge_state(br_index, &uid_cfg, &uid_state); + if (r) { + fprintf(stderr, "Failed to get bridge state: %s\n", + CTL_error_explanation(r)); + return -1; + } + +#if 0 + printf("Interface: %-7s (tag:%d) State: ", + uid_state.vlan_name, (int) uid_state.vlan_id); +#else + printf("Bridge: %-7s State:", + uid_state.vlan_name); +#endif + switch (uid_state.stp_enabled) { + case STP_ENABLED: printf("enabled\n"); break; + case STP_DISABLED: printf("disabled\n");break; + default: printf("unknown\n"); return 0; + } + + printf("BridgeId: "); print_bridge_id (&uid_state.bridge_id, 0); + printf(" Bridge Proirity: %lu (0x%lX)\n", + (unsigned long) uid_state.bridge_id.prio, (unsigned long) uid_state.bridge_id.prio); + if (uid_cfg.force_version < 2) + printf("Force Version: stp\n"); + + printf("Designated Root: "); print_bridge_id (&uid_state.designated_root, 1); + if (uid_state.root_port) { + printf("Root Port: %04lx", (unsigned long) uid_state.root_port); + // CLI_out_port_id (uid_state.root_port & 0xfff, False); + // printf("not implemented"); // XXX + printf(", Root Cost: %-lu\n", (unsigned long) uid_state.root_path_cost); + } else { + printf("Root Port: none\n"); + } + + if (uid_state.Topo_Change) + printf ("Topology Change Count: %lu\n", uid_state.Topo_Change_Count); + else + printf ("Time Since Topology Change: %lu\n", uid_state.timeSince_Topo_Change); + + printf ("Max Age: %2d Bridge Max Age: %-2d\n", + (int) uid_state.max_age, (int) uid_cfg.max_age); + printf ("Hello Time: %2d Bridge Hello Time: %-2d\n", + (int) uid_state.hello_time, (int) uid_cfg.hello_time); + printf ("Forward Delay: %2d Bridge Forward Delay: %-2d\n", + (int) uid_state.forward_delay, (int) uid_cfg.forward_delay); + printf ("Hold Time: %2d\n", (int) uid_cfg.hold_time); + + 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]; + struct stat st; + + snprintf(path, SYSFS_PATH_MAX, SYSFS_CLASS_NET "/%s/bridge", + entry->d_name); + return stat(path, &st) == 0 && S_ISDIR(st.st_mode); +} + +static int cmd_showbridge(int argc, char *const* argv) +{ + int i, count = 0; + int r = 0; + struct dirent **namelist; + + if (argc > 1) { + count = argc - 1; + } + else { + count = scandir(SYSFS_CLASS_NET, &namelist, isbridge, alphasort); + if (count < 0) { + fprintf(stderr, "Error getting list of all bridges\n"); + return -1; + } + } + + for (i = 0; i < count; i++) { + const char *name; + if (argc > 1) + name = argv[i+1]; + else + name = namelist[i]->d_name; + + int err = do_showbridge(name); + if (err) + r = err; + } + + if (argc <= 1) { + for (i = 0; i < count; i++) + free(namelist[i]); + free(namelist); + } + + return r; +} + +int detail = 0; + +static int do_showport(int br_index, const char *port_name, + UID_STP_STATE_T *uid_state) +{ + UID_STP_PORT_STATE_T uid_port; + UID_STP_PORT_CFG_T uid_cfg; + int r = 0; + int port_index = get_index_die(port_name, "port", 0); + if (port_index < 0) + return -1; + + memset (&uid_cfg, 0, sizeof (UID_STP_PORT_CFG_T)); + r = CTL_get_port_state(br_index, port_index, &uid_cfg, &uid_port); + if (r) { + fprintf(stderr, "Failed to get port state for port %d: %s\n", + port_index, CTL_error_explanation(r)); + return -1; + } + + if (detail) { + printf("Stp Port "); CLI_out_port_id (port_index, False); +#if 0 + printf(": PortId: %04lx in vlan '%s' with tag %d:\n", + (unsigned long) uid_port.port_id, uid_state->vlan_name, (int) uid_state->vlan_id); +#else + printf(": PortId: %04lx in Bridge '%s':\n", + (unsigned long) uid_port.port_id, uid_state->vlan_name); +#endif + printf ("Priority: %-d\n", (int) (uid_port.port_id >> 8)); + printf ("State: %-16s", stp_state2str (uid_port.state, 1)); + printf (" Uptime: %-9lu\n", uid_port.uptime); + printf ("PortPathCost: admin: "); + if (ADMIN_PORT_PATH_COST_AUTO == uid_cfg.admin_port_path_cost) + printf ("%-9s", "Auto"); + else + printf ("%-9lu", uid_cfg.admin_port_path_cost); + printf (" oper: %-9lu\n", uid_port.oper_port_path_cost); + + printf ("Point2Point: admin: "); + switch (uid_cfg.admin_point2point) { + case P2P_FORCE_TRUE: + printf ("%-9s", "ForceYes"); + break; + case P2P_FORCE_FALSE: + printf ("%-9s", "ForceNo"); + break; + case P2P_AUTO: + printf ("%-9s", "Auto"); + break; + } + printf (" oper: %-9s\n", uid_port.oper_point2point ? "Yes" : "No"); + printf ("Edge: admin: %-9s oper: %-9s\n", + uid_cfg.admin_edge ? "Y" : "N", + uid_port.oper_edge ? "Y" : "N"); + printf ("Partner: oper: %-9s\n", + uid_port.oper_stp_neigb ? "Slow" : "Rapid"); + + if (' ' != uid_port.role) { + if ('-' != uid_port.role) { + printf("PathCost: %-lu\n", (unsigned long) (uid_port.path_cost)); + printf("Designated Root: "); print_bridge_id (&uid_port.designated_root, 1); + printf("Designated Cost: %-ld\n", (unsigned long) uid_port.designated_cost); + printf("Designated Bridge: "); print_bridge_id (&uid_port.designated_bridge, 1); + printf("Designated Port: %-4lx\n\r", (unsigned long) uid_port.designated_port); + } + printf("Role: "); + switch (uid_port.role) { + case 'A': printf("Alternate\n"); break; + case 'B': printf("Backup\n"); break; + case 'R': printf("Root\n"); break; + case 'D': printf("Designated\n"); break; + case '-': printf("NonStp\n"); break; + default: printf("Unknown(%c)\n", uid_port.role); break; + } + + if ('R' == uid_port.role || 'D' == uid_port.role) { + /* printf("Tc: %c ", uid_port.tc ? 'Y' : 'n'); */ + printf("TcAck: %c ", + uid_port.top_change_ack ? 'Y' : 'N'); + printf("TcWhile: %3d\n", (int) uid_port.tcWhile); + } + } + + if (UID_PORT_DISABLED == uid_port.state || '-' == uid_port.role) { +#if 0 + printf("helloWhen: %3d ", (int) uid_port.helloWhen); + printf("lnkWhile: %3d\n", (int) uid_port.lnkWhile); + printf("fdWhile: %3d\n", (int) uid_port.fdWhile); +#endif + } else if ('-' != uid_port.role) { + printf("fdWhile: %3d ", (int) uid_port.fdWhile); + printf("rcvdInfoWhile: %3d\n", (int) uid_port.rcvdInfoWhile); + printf("rbWhile: %3d ", (int) uid_port.rbWhile); + printf("rrWhile: %3d\n", (int) uid_port.rrWhile); +#if 0 + printf("mdelayWhile: %3d ", (int) uid_port.mdelayWhile); + printf("lnkWhile: %3d\n", (int) uid_port.lnkWhile); + printf("helloWhen: %3d ", (int) uid_port.helloWhen); + printf("txCount: %3d\n", (int) uid_port.txCount); +#endif + } + + printf("RSTP BPDU rx: %lu\n", (unsigned long) uid_port.rx_rstp_bpdu_cnt); + printf("CONFIG BPDU rx: %lu\n", (unsigned long) uid_port.rx_cfg_bpdu_cnt); + printf("TCN BPDU rx: %lu\n", (unsigned long) uid_port.rx_tcn_bpdu_cnt); + } else { + printf("%c%c%c ", + (uid_port.oper_point2point) ? ' ' : '*', + (uid_port.oper_edge) ? 'E' : ' ', + (uid_port.oper_stp_neigb) ? 's' : ' '); + CLI_out_port_id (port_index, False); + printf(" %04lx %3s ", (unsigned long) uid_port.port_id, + stp_state2str (uid_port.state, 0)); + printf (" "); + print_bridge_id (&uid_port.designated_root, 0); + printf(" "); + print_bridge_id (&uid_port.designated_bridge, 0); + printf(" %4lx %c", (unsigned long) uid_port.designated_port, uid_port.role); + printf ("\n"); + } + return 0; +} + +static int not_dot_dotdot(const struct dirent *entry) +{ + char *n = entry->d_name; + return + !(n[0] == '.' && (n[1] == 0 || (n[1] == '.' && n[2] == 0))); +} + +static int cmd_showport(int argc, char *const* argv) +{ + UID_STP_STATE_T uid_state; + UID_STP_CFG_T uid_br_cfg; + int r = 0; + + int br_index = get_index(argv[1], "bridge"); + + r = CTL_get_bridge_state(br_index, &uid_br_cfg, &uid_state); + if (r) { + fprintf(stderr, "Failed to get bridge state: %s\n", + CTL_error_explanation(r)); + return -1; + } + + int i, count = 0; + struct dirent **namelist; + + if (argc > 2) { + count = argc - 2; + } + else { + char buf[SYSFS_PATH_MAX]; + snprintf(buf, sizeof(buf), SYSFS_CLASS_NET "/%s/brif", argv[1]); + count = scandir(buf, &namelist, not_dot_dotdot, alphasort); + if (count < 0) { + 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 (argc > 2) + name = argv[i+2]; + else + name = namelist[i]->d_name; + + int err = do_showport(br_index, name, &uid_state); + if (err) + r = err; + } + + if (argc <= 2) { + 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); +} + +unsigned int getuint(const char *s) +{ + char *end; + long l; + l = strtoul(s, &end, 0); + if (*s == 0 || *end != 0 || l > INT_MAX) { + fprintf(stderr, "Invalid unsigned int arg %s\n", s); + exit(1); + } + return l; +} + +int getenum(const char *s, const char *opt[]) +{ + int i; + for (i = 0; opt[i] != NULL; i++) + if (strcmp(s, opt[i]) == 0) + 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); +} + +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 set_bridge_cfg_value (int br_index, unsigned long value, + unsigned long val_mask) +{ + UID_STP_CFG_T uid_cfg; + char* val_name; + int rc; + + uid_cfg.field_mask = val_mask; + switch (val_mask) { + case BR_CFG_STATE: + uid_cfg.stp_enabled = value; + val_name = "state"; + break; + case BR_CFG_PRIO: + uid_cfg.bridge_priority = value; + val_name = "priority"; + break; + case BR_CFG_AGE: + uid_cfg.max_age = value; + val_name = "max_age"; + break; + case BR_CFG_HELLO: + uid_cfg.hello_time = value; + val_name = "hello_time"; + break; + case BR_CFG_DELAY: + uid_cfg.forward_delay = value; + val_name = "forward_delay"; + break; + case BR_CFG_FORCE_VER: + uid_cfg.force_version = value; + val_name = "force_version"; + break; + case BR_CFG_AGE_MODE: + case BR_CFG_AGE_TIME: + default: printf ("Invalid value mask 0X%lx\n", val_mask); return -1; + break; + } + + rc = CTL_set_bridge_config(br_index, &uid_cfg); + + if (0 != rc) { + printf ("Can't change rstp bridge %s:%s\n", val_name, STP_IN_get_error_explanation (rc)); + return -1; + } + return 0; +} + +static int cmd_setbridgestate(int argc, char *const* argv) +{ + + int br_index = get_index(argv[1], "bridge"); + return set_bridge_cfg_value(br_index, + getyesno(argv[2], "on", "off"), + BR_CFG_STATE); +} + +static int cmd_setbridgeprio(int argc, char *const* argv) +{ + + int br_index = get_index(argv[1], "bridge"); + return set_bridge_cfg_value(br_index, getuint(argv[2]), BR_CFG_PRIO); +} + +static int cmd_setbridgemaxage(int argc, char *const* argv) +{ + + int br_index = get_index(argv[1], "bridge"); + return set_bridge_cfg_value(br_index, getuint(argv[2]), BR_CFG_AGE); +} + +static int cmd_setbridgehello(int argc, char *const* argv) +{ + + int br_index = get_index(argv[1], "bridge"); + return set_bridge_cfg_value(br_index, getuint(argv[2]), BR_CFG_HELLO); +} + +static int cmd_setbridgefdelay(int argc, char *const* argv) +{ + + int br_index = get_index(argv[1], "bridge"); + return set_bridge_cfg_value(br_index, getuint(argv[2]), BR_CFG_DELAY); +} + +static int cmd_setbridgeforcevers(int argc, char *const* argv) +{ + + int br_index = get_index(argv[1], "bridge"); + return set_bridge_cfg_value(br_index, + 2 * getyesno(argv[2], "normal", "slow"), + BR_CFG_FORCE_VER); +} + + +static int +set_port_cfg_value (int br_index, int port_index, + unsigned long value, + unsigned long val_mask) +{ + UID_STP_PORT_CFG_T uid_cfg; + int rc; + char *val_name; + + BitmapClear(&uid_cfg.port_bmp); + uid_cfg.field_mask = val_mask; + switch (val_mask) { + case PT_CFG_MCHECK: + val_name = "mcheck"; + break; + case PT_CFG_COST: + uid_cfg.admin_port_path_cost = value; + val_name = "path cost"; + break; + case PT_CFG_PRIO: + uid_cfg.port_priority = value; + val_name = "priority"; + break; + case PT_CFG_P2P: + uid_cfg.admin_point2point = (ADMIN_P2P_T) value; + val_name = "p2p flag"; + break; + case PT_CFG_EDGE: + uid_cfg.admin_edge = value; + val_name = "adminEdge"; + break; + case PT_CFG_NON_STP: + uid_cfg.admin_non_stp = value; + val_name = "adminNonStp"; + break; +#ifdef STP_DBG + case PT_CFG_DBG_SKIP_TX: + uid_cfg.skip_tx = value; + val_name = "skip tx"; + break; + case PT_CFG_DBG_SKIP_RX: + uid_cfg.skip_rx = value; + val_name = "skip rx"; + break; +#endif + case PT_CFG_STATE: + default: + printf ("Invalid value mask 0X%lx\n", val_mask); + return -1; + } + + rc = CTL_set_port_config(br_index, port_index, &uid_cfg); + + if (0 != rc) { + printf ("can't change rstp port[s] %s: %s\n", + val_name, STP_IN_get_error_explanation (rc)); + return -1; + } + return 0; +} + +static int cmd_setportprio(int argc, char *const* argv) +{ + + int br_index = get_index(argv[1], "bridge"); + int port_index = get_index(argv[2], "port"); + return set_port_cfg_value(br_index, port_index, + getuint(argv[3]), PT_CFG_PRIO); +} + +static int cmd_setportpathcost(int argc, char *const* argv) +{ + + int br_index = get_index(argv[1], "bridge"); + int port_index = get_index(argv[2], "port"); + return set_port_cfg_value(br_index, port_index, + getuint(argv[3]), PT_CFG_COST); +} + +static int cmd_setportedge(int argc, char *const* argv) +{ + + int br_index = get_index(argv[1], "bridge"); + int port_index = get_index(argv[2], "port"); + return set_port_cfg_value(br_index, port_index, + getyesno(argv[3], "yes", "no"), PT_CFG_EDGE); +} + +static int cmd_setportnonstp(int argc, char *const* argv) +{ + + int br_index = get_index(argv[1], "bridge"); + int port_index = get_index(argv[2], "port"); + return set_port_cfg_value(br_index, port_index, + getyesno(argv[3], "yes", "no"), PT_CFG_NON_STP); +} + +static int cmd_setportp2p(int argc, char *const* argv) +{ + + int br_index = get_index(argv[1], "bridge"); + int port_index = get_index(argv[2], "port"); + const char *opts[] = {"yes", "no", "auto", NULL }; + int vals[] = { P2P_FORCE_TRUE, P2P_FORCE_FALSE, P2P_AUTO }; + + return set_port_cfg_value(br_index, port_index, + vals[getenum(argv[3], opts)], PT_CFG_P2P); +} + +static int cmd_portmcheck(int argc, char *const* argv) +{ + + int br_index = get_index(argv[1], "bridge"); + int port_index = get_index(argv[2], "port"); + return set_port_cfg_value(br_index, port_index, 0, PT_CFG_MCHECK); +} + +static int cmd_debuglevel(int argc, char *const* argv) +{ + return CTL_set_debug_level(getuint(argv[1])); +} + +struct command +{ + int nargs; + int optargs; + const char *name; + int (*func)(int argc, char *const* argv); + const char *help; +}; + +static const struct command commands[] = { + { 0, 32, "showbridge", cmd_showbridge, "[<bridge> ... ]\t\tshow bridge state" }, + { 1, 32, "showport", cmd_showport, "<bridge> [<port> ... ]\tshow port state" }, + { 1, 32, "showportdetail", cmd_showportdetail, "<bridge> [<port> ... ]\tshow port state (detail)" }, + { 2, 0, "rstp", cmd_rstp, "<bridge> {on|off}\tenable/disable rstpd control" }, + { 2, 0, "setbridgestate", cmd_setbridgestate, "<bridge> {on|off}\tstart/stop rstp (when enabled)" }, + { 2, 0, "setbridgeprio", cmd_setbridgeprio, "<bridge> <priority>\tset bridge priority (0-61440)" }, + { 2, 0, "sethello", cmd_setbridgehello, "<bridge> <hellotime>\tset bridge hello time (1-10)" }, + { 2, 0, "setmaxage", cmd_setbridgemaxage, "<bridge> <maxage>\tset bridge max age (6-40)" }, + { 2, 0, "setfdelay", cmd_setbridgefdelay, "<bridge> <fwd_delay>\tset bridge forward delay (4-30)" }, + { 2, 0, "setforcevers", cmd_setbridgeforcevers, "<bridge> {normal|slow}\tnormal RSTP or force to STP" }, + { 3, 0, "setportprio", cmd_setportprio, "<bridge> <port> <priority>\tset port priority (0-240)" }, + { 3, 0, "setportpathcost", cmd_setportpathcost, "<bridge> <port> <cost>\tset port path cost" }, + { 3, 0, "setportedge", cmd_setportedge, "<bridge> <port> {yes|no}\tconfigure if it is an edge port" }, + { 3, 0, "setportnonstp", cmd_setportnonstp, "<bridge> <port> {yes|no}\tdisable STP for the port" }, + { 3, 0, "setportp2p", cmd_setportp2p, "<bridge> <port> {yes|no|auto}\tset whether p2p connection" }, + { 2, 0, "portmcheck", cmd_portmcheck, "<bridge> <port>\ttry to get back from STP to RSTP mode" }, + { 1, 0, "debuglevel", cmd_debuglevel, "<level>\t\tLevel of verbosity" }, +}; + +const struct command *command_lookup(const char *cmd) +{ + int i; + + for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) { + if (!strcmp(cmd, commands[i].name)) + return &commands[i]; + } + + return NULL; +} + +void command_helpall(void) +{ + int i; + + for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) { + printf("\t%-10s\t%s\n", commands[i].name, commands[i].help); + } +} + +static void help() +{ + printf("Usage: rstpctl [commands]\n"); + printf("commands:\n"); + command_helpall(); +} + +#define PACKAGE_VERSION2(v, b) "rstp, " #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 ((f = getopt_long(argc, argv, "Vh", options, NULL)) != EOF) + 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 ((cmd = command_lookup(argv[0])) == NULL) { + 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: rstpctl %s %s\n", cmd->name, cmd->help); + return 1; + } + + return cmd->func(argc, argv); + +help: + help(); + return 1; +} diff --git a/ctl_socket.c b/ctl_socket.c new file mode 100644 index 0000000..c41728a --- /dev/null +++ b/ctl_socket.c @@ -0,0 +1,145 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#include "ctl_socket.h" +#include "ctl_socket_server.h" +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> + +#include "epoll_loop.h" +#include "log.h" + + +int server_socket(void) +{ + struct sockaddr_un sa; + int s; + + TST (strlen(RSTP_SERVER_SOCK_NAME) < sizeof(sa.sun_path), -1); + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + ERROR("Couldn't open unix socket: %m"); + return -1; + } + + set_socket_address(&sa, RSTP_SERVER_SOCK_NAME); + + if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) != 0) { + ERROR("Couldn't bind socket: %m"); + close(s); + return -1; + } + + return s; +} + + +int handle_message(int cmd, void *inbuf, int lin, void *outbuf, int *lout) +{ + switch (cmd) { + SERVER_MESSAGE_CASE(enable_bridge_rstp); + SERVER_MESSAGE_CASE(get_bridge_state); + SERVER_MESSAGE_CASE(set_bridge_config); + SERVER_MESSAGE_CASE(get_port_state); + SERVER_MESSAGE_CASE(set_port_config); + SERVER_MESSAGE_CASE(set_debug_level); + + default: + ERROR("CTL: Unknown command %d", cmd); + return -1; + } +} + +#define msg_buf_len 1024 +unsigned char msg_inbuf[1024]; +unsigned char msg_outbuf[1024]; + +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[2]; + int l; + + msg.msg_name = &sa; msg.msg_namelen = sizeof(sa); + msg.msg_iov = iov; msg.msg_iovlen = 2; + 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; + l = recvmsg(p->fd, &msg, MSG_NOSIGNAL | MSG_DONTWAIT); + TST(l > 0, ); + if (msg.msg_flags != 0 || l < sizeof(mhdr) || + l != sizeof(mhdr) + mhdr.lin || + mhdr.lout > msg_buf_len || mhdr.cmd < 0) { + ERROR("CTL: Unexpected message. Ignoring"); + return; + } + + + if (mhdr.lout) + mhdr.res = handle_message(mhdr.cmd, msg_inbuf, mhdr.lin, + msg_outbuf, &mhdr.lout); + else + mhdr.res = handle_message(mhdr.cmd, msg_inbuf, mhdr.lin, + NULL, NULL); + + if (mhdr.res < 0) + mhdr.lout = 0; + iov[1].iov_base = msg_outbuf; iov[1].iov_len = mhdr.lout; + l = sendmsg(p->fd, &msg, MSG_NOSIGNAL); + if (l < 0) + ERROR("CTL: Couldn't send response: %m"); + else if (l != sizeof(mhdr) + mhdr.lout) { + ERROR("CTL: Couldn't send full response, sent %d bytes instead of %zd.", + l, sizeof(mhdr) + mhdr.lout); + } +} + +struct epoll_event_handler ctl_handler; + +int ctl_socket_init(void) +{ + int s = server_socket(); + if (s < 0) + 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.h b/ctl_socket.h new file mode 100644 index 0000000..5e7d96f --- /dev/null +++ b/ctl_socket.h @@ -0,0 +1,167 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#ifndef CTL_SOCKET_H +#define CTL_SOCKET_H + +#include <sys/types.h> +#include <string.h> + +#include "ctl_functions.h" + +struct ctl_msg_hdr +{ + int cmd; + int lin; + int lout; + int res; +}; + +#define set_socket_address(sa, string) \ + do {\ + (sa)->sun_family = AF_UNIX; \ + memset((sa)->sun_path, 0, sizeof((sa)->sun_path)); \ + strcpy((sa)->sun_path + 1, (string)); \ + } while (0) + +#define RSTP_SERVER_SOCK_NAME ".rstp_server" + +/* COMMANDS */ + +#if 0 +int CTL_enable_bridge_rstp(int br_index, int enable); +#endif +#define CMD_CODE_enable_bridge_rstp 101 +#define enable_bridge_rstp_ARGS (int br_index, int enable) +struct enable_bridge_rstp_IN { int br_index; int enable; }; +struct enable_bridge_rstp_OUT { }; +#define enable_bridge_rstp_COPY_IN \ + ({ in->br_index = br_index; in->enable = enable; }) +#define enable_bridge_rstp_COPY_OUT ({ (void)0; }) +#define enable_bridge_rstp_CALL (in->br_index, in->enable) + +#if 0 +int CTL_get_bridge_state(int br_index, + UID_STP_CFG_T *cfg, UID_STP_STATE_T *state); +#endif +#define CMD_CODE_get_bridge_state 102 +#define get_bridge_state_ARGS (int br_index, UID_STP_CFG_T *cfg, UID_STP_STATE_T *state) +struct get_bridge_state_IN { int br_index; }; +struct get_bridge_state_OUT { UID_STP_CFG_T cfg; UID_STP_STATE_T state; }; +#define get_bridge_state_COPY_IN \ + ({ in->br_index = br_index; }) +#define get_bridge_state_COPY_OUT ({ *cfg = out->cfg; *state = out->state; }) +#define get_bridge_state_CALL (in->br_index, &out->cfg, &out->state) + +#if 0 +int CTL_set_bridge_config(int br_index, + UID_STP_CFG_T *cfg); +#endif +#define CMD_CODE_set_bridge_config 103 +#define set_bridge_config_ARGS (int br_index, UID_STP_CFG_T *cfg) +struct set_bridge_config_IN { int br_index; UID_STP_CFG_T cfg; }; +struct set_bridge_config_OUT { }; +#define set_bridge_config_COPY_IN \ + ({ in->br_index = br_index; in->cfg = *cfg; }) +#define set_bridge_config_COPY_OUT ({ (void)0; }) +#define set_bridge_config_CALL (in->br_index, &in->cfg) + +#if 0 +int CTL_get_port_state(int br_index, int port_index, + UID_STP_PORT_CFG_T *cfg, UID_STP_PORT_STATE_T *state); +#endif +#define CMD_CODE_get_port_state 104 +#define get_port_state_ARGS (int br_index, int port_index, UID_STP_PORT_CFG_T *cfg, UID_STP_PORT_STATE_T *state) +struct get_port_state_IN { int br_index; int port_index; }; +struct get_port_state_OUT { UID_STP_PORT_CFG_T cfg; UID_STP_PORT_STATE_T state; }; +#define get_port_state_COPY_IN \ + ({ in->br_index = br_index; in->port_index = port_index; }) +#define get_port_state_COPY_OUT ({ *cfg = out->cfg; *state = out->state; }) +#define get_port_state_CALL (in->br_index, in->port_index, &out->cfg, &out->state) + +#if 0 +int CTL_set_port_config(int br_index, int port_index, + UID_STP_PORT_CFG_T *cfg); +#endif +#define CMD_CODE_set_port_config 105 +#define set_port_config_ARGS (int br_index, int port_index, UID_STP_PORT_CFG_T *cfg) +struct set_port_config_IN { int br_index; int port_index; UID_STP_PORT_CFG_T cfg; }; +struct set_port_config_OUT { }; +#define set_port_config_COPY_IN \ + ({ in->br_index = br_index; in->port_index = port_index; in->cfg = *cfg; }) +#define set_port_config_COPY_OUT ({ (void)0; }) +#define set_port_config_CALL (in->br_index, in->port_index, &in->cfg) + + +#if 0 +int CTL_set_debug_level(int level); +#endif +#define CMD_CODE_set_debug_level 106 +#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) + +/* General case part in ctl command server switch */ +#define SERVER_MESSAGE_CASE(name) \ +case CMD_CODE_ ## name : do { \ + if (0) LOG("CTL command " #name); \ + struct name ## _IN in0, *in = &in0; \ + struct name ## _OUT out0, *out = &out0; \ + if (sizeof(*in) != lin || sizeof(*out) != (outbuf?*lout:0)) { \ + LOG("Bad sizes lin %d != %zd or lout %d != %zd", \ + lin, sizeof(*in), lout?*lout:0, 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) \ +int CTL_ ## name name ## _ARGS \ +{ \ + struct name ## _IN in0, *in=&in0; \ + struct name ## _OUT out0, *out = &out0; \ + int l = sizeof(*out); \ + name ## _COPY_IN; \ + int res = 0; \ + int r = send_ctl_message(CMD_CODE_ ## name, in, sizeof(*in), out, &l, \ + &res); \ + if (r || res) LOG("Got return code %d, %d", r, res); \ + if (r) return r; \ + if (res) return res; \ + name ## _COPY_OUT; \ + return r; \ +} + + + +#endif diff --git a/ctl_socket_client.c b/ctl_socket_client.c new file mode 100644 index 0000000..66be8d2 --- /dev/null +++ b/ctl_socket_client.c @@ -0,0 +1,150 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#include "ctl_socket.h" +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <sys/poll.h> +#include <stdio.h> +#include <stdlib.h> + +#include "log.h" + +static int fd = -1; + +int ctl_client_init(void) +{ + struct sockaddr_un sa_svr; + int s; + TST (strlen(RSTP_SERVER_SOCK_NAME) < sizeof(sa_svr.sun_path), -1); + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + ERROR("Couldn't open unix socket: %m"); + return -1; + } + + set_socket_address(&sa_svr, RSTP_SERVER_SOCK_NAME); + + struct sockaddr_un sa; + char tmpname[64]; + sprintf(tmpname, "RSTPCTL_%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 (bind(s, (struct sockaddr *)&sa, sizeof(sa)) != 0) { + ERROR("Couldn't bind socket: %m"); + close(s); + return -1; + } + + if (connect(s, (struct sockaddr *)&sa_svr, sizeof(sa_svr)) != 0) { + 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, + int *res) +{ + struct ctl_msg_hdr mhdr; + struct msghdr msg; + struct iovec iov[2]; + int l; + + msg.msg_name = NULL; msg.msg_namelen = 0; + msg.msg_iov = iov; msg.msg_iovlen = 2; + msg.msg_control = NULL; msg.msg_controllen = 0; + + mhdr.cmd = cmd; + mhdr.lin = lin; + mhdr.lout = lout != NULL ? *lout : 0; + iov[0].iov_base = &mhdr; iov[0].iov_len = sizeof(mhdr); + iov[1].iov_base = (void *)inbuf; iov[1].iov_len = lin; + + l = sendmsg(fd, &msg, 0); + if (l < 0) { + 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 != NULL ? *lout : 0; + + { + struct pollfd pfd; + int timeout = 5000; /* 5 s */ + int r; + + pfd.fd = fd; + pfd.events = POLLIN; + do { + r = poll(&pfd, 1, timeout); + if (r == 0) { + ERROR("Error getting message from server: Timeout"); + return -1; + } + if (r < 0) { + ERROR("Error getting message from server: poll error: %m"); + return -1; + } + } while ((pfd.revents & (POLLERR | POLLHUP | POLLNVAL | POLLIN)) == 0); + + l = recvmsg(fd, &msg, 0); + if (l < 0) { + ERROR("Error getting message from server: %m"); + return -1; + } + if (l < sizeof(mhdr) || l != sizeof(mhdr) + mhdr.lout || mhdr.cmd != cmd) { + ERROR("Error getting message from server: Bad format"); + return -1; + } + } + if (lout) + *lout = mhdr.lout; + if (res) + *res = mhdr.res; + + return 0; +} diff --git a/ctl_socket_client.h b/ctl_socket_client.h new file mode 100644 index 0000000..2bfc9ea --- /dev/null +++ b/ctl_socket_client.h @@ -0,0 +1,36 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#ifndef CTL_SOCKET_CLIENT_H +#define CTL_SOCKET_CLIENT_H + +int send_ctl_message(int cmd, void *inbuf, int lin, void *outbuf, int *lout, + int *res); + + +int ctl_client_init(void); + +void ctl_client_cleanup(void); + +#endif diff --git a/ctl_socket_server.h b/ctl_socket_server.h new file mode 100644 index 0000000..9b74172 --- /dev/null +++ b/ctl_socket_server.h @@ -0,0 +1,33 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#ifndef CTL_SOCKET_SERVER_H +#define CTL_SOCKET_SERVER_H + +int ctl_socket_init(void); +void ctl_socket_cleanup(void); + +#endif + + diff --git a/epoll_loop.c b/epoll_loop.c new file mode 100644 index 0000000..230dacc --- /dev/null +++ b/epoll_loop.c @@ -0,0 +1,141 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#include "epoll_loop.h" + +#include <sys/epoll.h> +#include <errno.h> +#include <sys/time.h> +#include <time.h> +#include <stdio.h> +#include <unistd.h> + +#include "bridge_ctl.h" + +// globals +int epoll_fd = -1; +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); +} + +int time_diff(struct timeval *second, struct timeval *first) +{ + return (second->tv_sec - first->tv_sec)*1000 + + (second->tv_usec - first->tv_usec)/1000; +} + +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..f9cfbee --- /dev/null +++ b/epoll_loop.h @@ -0,0 +1,52 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#ifndef EPOLL_LOOP_H +#define EPOLL_LOOP_H + +#include <sys/epoll.h> +#include <errno.h> +#include <sys/time.h> +#include <time.h> + +struct epoll_event_handler +{ + int fd; + void *arg; + void (*handler)(uint32_t events, struct epoll_event_handler *p); + struct epoll_event *ref_ev; /* if set, epoll loop has reference to this, + so mark that ref as NULL while freeing */ +}; + +int init_epoll(void); + +void clear_epoll(void); + +int epoll_main_loop(void); + +int add_epoll(struct epoll_event_handler *h); + +int remove_epoll(struct epoll_event_handler *h); + +#endif diff --git a/include/libnetlink.h b/include/libnetlink.h new file mode 100644 index 0000000..63cc3c8 --- /dev/null +++ b/include/libnetlink.h @@ -0,0 +1,57 @@ +#ifndef __LIBNETLINK_H__ +#define __LIBNETLINK_H__ 1 + +#include <asm/types.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + +struct rtnl_handle +{ + int fd; + struct sockaddr_nl local; + struct sockaddr_nl peer; + __u32 seq; + __u32 dump; +}; + +extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions); +extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol); +extern void rtnl_close(struct rtnl_handle *rth); +extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type); +extern 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 *); +extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter, + void *arg1, + rtnl_filter_t junk, + void *arg2); +extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, + unsigned groups, struct nlmsghdr *answer, + rtnl_filter_t junk, + void *jarg); +extern int rtnl_send(struct rtnl_handle *rth, const char *buf, int); + + +extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data); +extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen); +extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len); +extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data); +extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen); + +extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len); +extern 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))) + +extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler, + void *jarg); +extern 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/include/linux/llc.h b/include/linux/llc.h new file mode 100644 index 0000000..09f2e6d --- /dev/null +++ b/include/linux/llc.h @@ -0,0 +1,80 @@ +#ifndef __LINUX_LLC_H +#define __LINUX_LLC_H +/* + * IEEE 802.2 User Interface SAPs for Linux, data structures and indicators. + * + * Copyright (c) 2001 by Jay Schulist <jschlst@samba.org> + * + * This program can be redistributed or modified under the terms of the + * GNU General Public License as published by the Free Software Foundation. + * This program is distributed without any warranty or implied warranty + * of merchantability or fitness for a particular purpose. + * + * See the GNU General Public License for more details. + */ +#define __LLC_SOCK_SIZE__ 16 /* sizeof(sockaddr_llc), word align. */ +struct sockaddr_llc { + sa_family_t sllc_family; /* AF_LLC */ + sa_family_t sllc_arphrd; /* ARPHRD_ETHER */ + unsigned char sllc_test; + unsigned char sllc_xid; + unsigned char sllc_ua; /* UA data, only for SOCK_STREAM. */ + unsigned char sllc_sap; + unsigned char sllc_mac[IFHWADDRLEN]; + unsigned char __pad[__LLC_SOCK_SIZE__ - sizeof(sa_family_t) * 2 - + sizeof(unsigned char) * 4 - IFHWADDRLEN]; +}; + +/* sockopt definitions. */ +enum llc_sockopts { + LLC_OPT_UNKNOWN = 0, + LLC_OPT_RETRY, /* max retrans attempts. */ + LLC_OPT_SIZE, /* max PDU size (octets). */ + LLC_OPT_ACK_TMR_EXP, /* ack expire time (secs). */ + LLC_OPT_P_TMR_EXP, /* pf cycle expire time (secs). */ + LLC_OPT_REJ_TMR_EXP, /* rej sent expire time (secs). */ + LLC_OPT_BUSY_TMR_EXP, /* busy state expire time (secs). */ + LLC_OPT_TX_WIN, /* tx window size. */ + LLC_OPT_RX_WIN, /* rx window size. */ + LLC_OPT_MAX +}; + +#define LLC_OPT_MAX_RETRY 100 +#define LLC_OPT_MAX_SIZE 4196 +#define LLC_OPT_MAX_WIN 127 +#define LLC_OPT_MAX_ACK_TMR_EXP 60 +#define LLC_OPT_MAX_P_TMR_EXP 60 +#define LLC_OPT_MAX_REJ_TMR_EXP 60 +#define LLC_OPT_MAX_BUSY_TMR_EXP 60 + +/* LLC SAP types. */ +#define LLC_SAP_NULL 0x00 /* NULL SAP. */ +#define LLC_SAP_LLC 0x02 /* LLC Sublayer Managment. */ +#define LLC_SAP_SNA 0x04 /* SNA Path Control. */ +#define LLC_SAP_PNM 0x0E /* Proway Network Managment. */ +#define LLC_SAP_IP 0x06 /* TCP/IP. */ +#define LLC_SAP_BSPAN 0x42 /* Bridge Spanning Tree Proto */ +#define LLC_SAP_MMS 0x4E /* Manufacturing Message Srv. */ +#define LLC_SAP_8208 0x7E /* ISO 8208 */ +#define LLC_SAP_3COM 0x80 /* 3COM. */ +#define LLC_SAP_PRO 0x8E /* Proway Active Station List */ +#define LLC_SAP_SNAP 0xAA /* SNAP. */ +#define LLC_SAP_BANYAN 0xBC /* Banyan. */ +#define LLC_SAP_IPX 0xE0 /* IPX/SPX. */ +#define LLC_SAP_NETBEUI 0xF0 /* NetBEUI. */ +#define LLC_SAP_LANMGR 0xF4 /* LanManager. */ +#define LLC_SAP_IMPL 0xF8 /* IMPL */ +#define LLC_SAP_DISC 0xFC /* Discovery */ +#define LLC_SAP_OSI 0xFE /* OSI Network Layers. */ +#define LLC_SAP_LAR 0xDC /* LAN Address Resolution */ +#define LLC_SAP_RM 0xD4 /* Resource Management */ +#define LLC_SAP_GLOBAL 0xFF /* Global SAP. */ + +#ifdef __KERNEL__ +#define LLC_SAP_DYN_START 0xC0 +#define LLC_SAP_DYN_STOP 0xDE +#define LLC_SAP_DYN_TRIES 4 + +#define llc_ui_skb_cb(__skb) ((struct sockaddr_llc *)&((__skb)->cb[0])) +#endif /* __KERNEL__ */ +#endif /* __LINUX_LLC_H */ diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h new file mode 100644 index 0000000..5e33a20 --- /dev/null +++ b/include/linux/rtnetlink.h @@ -0,0 +1,910 @@ +#ifndef __LINUX_RTNETLINK_H +#define __LINUX_RTNETLINK_H + +#include <linux/netlink.h> + +/**** + * Routing/neighbour discovery messages. + ****/ + +/* Types of messages */ + +enum { + RTM_BASE = 16, +#define RTM_BASE RTM_BASE + + RTM_NEWLINK = 16, +#define RTM_NEWLINK RTM_NEWLINK + RTM_DELLINK, +#define RTM_DELLINK RTM_DELLINK + RTM_GETLINK, +#define RTM_GETLINK RTM_GETLINK + RTM_SETLINK, +#define RTM_SETLINK RTM_SETLINK + + RTM_NEWADDR = 20, +#define RTM_NEWADDR RTM_NEWADDR + RTM_DELADDR, +#define RTM_DELADDR RTM_DELADDR + RTM_GETADDR, +#define RTM_GETADDR RTM_GETADDR + + RTM_NEWROUTE = 24, +#define RTM_NEWROUTE RTM_NEWROUTE + RTM_DELROUTE, +#define RTM_DELROUTE RTM_DELROUTE + RTM_GETROUTE, +#define RTM_GETROUTE RTM_GETROUTE + + RTM_NEWNEIGH = 28, +#define RTM_NEWNEIGH RTM_NEWNEIGH + RTM_DELNEIGH, +#define RTM_DELNEIGH RTM_DELNEIGH + RTM_GETNEIGH, +#define RTM_GETNEIGH RTM_GETNEIGH + + RTM_NEWRULE = 32, +#define RTM_NEWRULE RTM_NEWRULE + RTM_DELRULE, +#define RTM_DELRULE RTM_DELRULE + RTM_GETRULE, +#define RTM_GETRULE RTM_GETRULE + + RTM_NEWQDISC = 36, +#define RTM_NEWQDISC RTM_NEWQDISC + RTM_DELQDISC, +#define RTM_DELQDISC RTM_DELQDISC + RTM_GETQDISC, +#define RTM_GETQDISC RTM_GETQDISC + + RTM_NEWTCLASS = 40, +#define RTM_NEWTCLASS RTM_NEWTCLASS + RTM_DELTCLASS, +#define RTM_DELTCLASS RTM_DELTCLASS + RTM_GETTCLASS, +#define RTM_GETTCLASS RTM_GETTCLASS + + RTM_NEWTFILTER = 44, +#define RTM_NEWTFILTER RTM_NEWTFILTER + RTM_DELTFILTER, +#define RTM_DELTFILTER RTM_DELTFILTER + RTM_GETTFILTER, +#define RTM_GETTFILTER RTM_GETTFILTER + + RTM_NEWACTION = 48, +#define RTM_NEWACTION RTM_NEWACTION + RTM_DELACTION, +#define RTM_DELACTION RTM_DELACTION + RTM_GETACTION, +#define RTM_GETACTION RTM_GETACTION + + RTM_NEWPREFIX = 52, +#define RTM_NEWPREFIX RTM_NEWPREFIX + RTM_GETPREFIX = 54, +#define RTM_GETPREFIX RTM_GETPREFIX + + RTM_GETMULTICAST = 58, +#define RTM_GETMULTICAST RTM_GETMULTICAST + + RTM_GETANYCAST = 62, +#define RTM_GETANYCAST RTM_GETANYCAST + + RTM_NEWNEIGHTBL = 64, +#define RTM_NEWNEIGHTBL RTM_NEWNEIGHTBL + RTM_GETNEIGHTBL = 66, +#define RTM_GETNEIGHTBL RTM_GETNEIGHTBL + RTM_SETNEIGHTBL, +#define RTM_SETNEIGHTBL RTM_SETNEIGHTBL + + __RTM_MAX, +#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) +}; + +#define RTM_NR_MSGTYPES (RTM_MAX + 1 - RTM_BASE) +#define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2) +#define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2) + +/* + Generic structure for encapsulation of optional route information. + It is reminiscent of sockaddr, but with sa_family replaced + with attribute type. + */ + +struct rtattr +{ + unsigned short rta_len; + unsigned short rta_type; +}; + +/* Macros to handle rtattributes */ + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) +#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \ + (rta)->rta_len >= sizeof(struct rtattr) && \ + (rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ + (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) +#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0))) +#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) + + + + +/****************************************************************************** + * Definitions used in routing table administration. + ****/ + +struct rtmsg +{ + unsigned char rtm_family; + unsigned char rtm_dst_len; + unsigned char rtm_src_len; + unsigned char rtm_tos; + + unsigned char rtm_table; /* Routing table id */ + unsigned char rtm_protocol; /* Routing protocol; see below */ + unsigned char rtm_scope; /* See below */ + unsigned char rtm_type; /* See below */ + + unsigned rtm_flags; +}; + +/* rtm_type */ + +enum +{ + RTN_UNSPEC, + RTN_UNICAST, /* Gateway or direct route */ + RTN_LOCAL, /* Accept locally */ + RTN_BROADCAST, /* Accept locally as broadcast, + send as broadcast */ + RTN_ANYCAST, /* Accept locally as broadcast, + but send as unicast */ + RTN_MULTICAST, /* Multicast route */ + RTN_BLACKHOLE, /* Drop */ + RTN_UNREACHABLE, /* Destination is unreachable */ + RTN_PROHIBIT, /* Administratively prohibited */ + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ + __RTN_MAX +}; + +#define RTN_MAX (__RTN_MAX - 1) + + +/* rtm_protocol */ + +#define RTPROT_UNSPEC 0 +#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; + not used by current IPv4 */ +#define RTPROT_KERNEL 2 /* Route installed by kernel */ +#define RTPROT_BOOT 3 /* Route installed during boot */ +#define RTPROT_STATIC 4 /* Route installed by administrator */ + +/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; + they are just passed from user and back as is. + It will be used by hypothetical multiple routing daemons. + Note that protocol values should be standardized in order to + avoid conflicts. + */ + +#define RTPROT_GATED 8 /* Apparently, GateD */ +#define RTPROT_RA 9 /* RDISC/ND router advertisements */ +#define RTPROT_MRT 10 /* Merit MRT */ +#define RTPROT_ZEBRA 11 /* Zebra */ +#define RTPROT_BIRD 12 /* BIRD */ +#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ +#define RTPROT_XORP 14 /* XORP */ +#define RTPROT_NTK 15 /* Netsukuku */ + +/* rtm_scope + + Really it is not scope, but sort of distance to the destination. + NOWHERE are reserved for not existing destinations, HOST is our + local addresses, LINK are destinations, located on directly attached + link and UNIVERSE is everywhere in the Universe. + + Intermediate values are also possible f.e. interior routes + could be assigned a value between UNIVERSE and LINK. +*/ + +enum rt_scope_t +{ + RT_SCOPE_UNIVERSE=0, +/* User defined values */ + RT_SCOPE_SITE=200, + RT_SCOPE_LINK=253, + RT_SCOPE_HOST=254, + RT_SCOPE_NOWHERE=255 +}; + +/* rtm_flags */ + +#define RTM_F_NOTIFY 0x100 /* Notify user of route change */ +#define RTM_F_CLONED 0x200 /* This route is cloned */ +#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ +#define RTM_F_PREFIX 0x800 /* Prefix addresses */ + +/* Reserved table identifiers */ + +enum rt_class_t +{ + RT_TABLE_UNSPEC=0, +/* User defined values */ + RT_TABLE_DEFAULT=253, + RT_TABLE_MAIN=254, + RT_TABLE_LOCAL=255, + __RT_TABLE_MAX +}; +#define RT_TABLE_MAX (__RT_TABLE_MAX - 1) + + + +/* Routing message attributes */ + +enum rtattr_type_t +{ + RTA_UNSPEC, + RTA_DST, + RTA_SRC, + RTA_IIF, + RTA_OIF, + RTA_GATEWAY, + RTA_PRIORITY, + RTA_PREFSRC, + RTA_METRICS, + RTA_MULTIPATH, + RTA_PROTOINFO, + RTA_FLOW, + RTA_CACHEINFO, + RTA_SESSION, + RTA_MP_ALGO, + __RTA_MAX +}; + +#define RTA_MAX (__RTA_MAX - 1) + +#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))) +#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg)) + +/* RTM_MULTIPATH --- array of struct rtnexthop. + * + * "struct rtnexthop" describes all necessary nexthop information, + * i.e. parameters of path to a destination via this nexthop. + * + * At the moment it is impossible to set different prefsrc, mtu, window + * and rtt for different paths from multipath. + */ + +struct rtnexthop +{ + unsigned short rtnh_len; + unsigned char rtnh_flags; + unsigned char rtnh_hops; + int rtnh_ifindex; +}; + +/* rtnh_flags */ + +#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ +#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ +#define RTNH_F_ONLINK 4 /* Gateway is forced on link */ + +/* Macros to handle hexthops */ + +#define RTNH_ALIGNTO 4 +#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) ) +#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \ + ((int)(rtnh)->rtnh_len) <= (len)) +#define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len))) +#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len)) +#define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len)) +#define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0))) + +/* RTM_CACHEINFO */ + +struct rta_cacheinfo +{ + __u32 rta_clntref; + __u32 rta_lastuse; + __s32 rta_expires; + __u32 rta_error; + __u32 rta_used; + +#define RTNETLINK_HAVE_PEERINFO 1 + __u32 rta_id; + __u32 rta_ts; + __u32 rta_tsage; +}; + +/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */ + +enum +{ + RTAX_UNSPEC, +#define RTAX_UNSPEC RTAX_UNSPEC + RTAX_LOCK, +#define RTAX_LOCK RTAX_LOCK + RTAX_MTU, +#define RTAX_MTU RTAX_MTU + RTAX_WINDOW, +#define RTAX_WINDOW RTAX_WINDOW + RTAX_RTT, +#define RTAX_RTT RTAX_RTT + RTAX_RTTVAR, +#define RTAX_RTTVAR RTAX_RTTVAR + RTAX_SSTHRESH, +#define RTAX_SSTHRESH RTAX_SSTHRESH + RTAX_CWND, +#define RTAX_CWND RTAX_CWND + RTAX_ADVMSS, +#define RTAX_ADVMSS RTAX_ADVMSS + RTAX_REORDERING, +#define RTAX_REORDERING RTAX_REORDERING + RTAX_HOPLIMIT, +#define RTAX_HOPLIMIT RTAX_HOPLIMIT + RTAX_INITCWND, +#define RTAX_INITCWND RTAX_INITCWND + RTAX_FEATURES, +#define RTAX_FEATURES RTAX_FEATURES + __RTAX_MAX +}; + +#define RTAX_MAX (__RTAX_MAX - 1) + +#define RTAX_FEATURE_ECN 0x00000001 +#define RTAX_FEATURE_SACK 0x00000002 +#define RTAX_FEATURE_TIMESTAMP 0x00000004 +#define RTAX_FEATURE_ALLFRAG 0x00000008 + +struct rta_session +{ + __u8 proto; + __u8 pad1; + __u16 pad2; + + union { + struct { + __u16 sport; + __u16 dport; + } ports; + + struct { + __u8 type; + __u8 code; + __u16 ident; + } icmpt; + + __u32 spi; + } u; +}; + + +/********************************************************* + * Interface address. + ****/ + +struct ifaddrmsg +{ + unsigned char ifa_family; + unsigned char ifa_prefixlen; /* The prefix length */ + unsigned char ifa_flags; /* Flags */ + unsigned char ifa_scope; /* See above */ + int ifa_index; /* Link index */ +}; + +enum +{ + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, + IFA_MULTICAST, + __IFA_MAX +}; + +#define IFA_MAX (__IFA_MAX - 1) + +/* ifa_flags */ + +#define IFA_F_SECONDARY 0x01 +#define IFA_F_TEMPORARY IFA_F_SECONDARY + +#define IFA_F_DEPRECATED 0x20 +#define IFA_F_TENTATIVE 0x40 +#define IFA_F_PERMANENT 0x80 + +struct ifa_cacheinfo +{ + __u32 ifa_prefered; + __u32 ifa_valid; + __u32 cstamp; /* created timestamp, hundredths of seconds */ + __u32 tstamp; /* updated timestamp, hundredths of seconds */ +}; + + +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) + +/* + Important comment: + IFA_ADDRESS is prefix address, rather than local interface address. + It makes no difference for normally configured broadcast interfaces, + but for point-to-point IFA_ADDRESS is DESTINATION address, + local address is supplied in IFA_LOCAL attribute. + */ + +/************************************************************** + * Neighbour discovery. + ****/ + +struct ndmsg +{ + unsigned char ndm_family; + unsigned char ndm_pad1; + unsigned short ndm_pad2; + int ndm_ifindex; /* Link index */ + __u16 ndm_state; + __u8 ndm_flags; + __u8 ndm_type; +}; + +enum +{ + NDA_UNSPEC, + NDA_DST, + NDA_LLADDR, + NDA_CACHEINFO, + NDA_PROBES, + __NDA_MAX +}; + +#define NDA_MAX (__NDA_MAX - 1) + +#define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) +#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg)) + +/* + * Neighbor Cache Entry Flags + */ + +#define NTF_PROXY 0x08 /* == ATF_PUBL */ +#define NTF_ROUTER 0x80 + +/* + * Neighbor Cache Entry States. + */ + +#define NUD_INCOMPLETE 0x01 +#define NUD_REACHABLE 0x02 +#define NUD_STALE 0x04 +#define NUD_DELAY 0x08 +#define NUD_PROBE 0x10 +#define NUD_FAILED 0x20 + +/* Dummy states */ +#define NUD_NOARP 0x40 +#define NUD_PERMANENT 0x80 +#define NUD_NONE 0x00 + + +struct nda_cacheinfo +{ + __u32 ndm_confirmed; + __u32 ndm_used; + __u32 ndm_updated; + __u32 ndm_refcnt; +}; + + +/***************************************************************** + * Neighbour tables specific messages. + * + * To retrieve the neighbour tables send RTM_GETNEIGHTBL with the + * NLM_F_DUMP flag set. Every neighbour table configuration is + * spread over multiple messages to avoid running into message + * size limits on systems with many interfaces. The first message + * in the sequence transports all not device specific data such as + * statistics, configuration, and the default parameter set. + * This message is followed by 0..n messages carrying device + * specific parameter sets. + * Although the ordering should be sufficient, NDTA_NAME can be + * used to identify sequences. The initial message can be identified + * by checking for NDTA_CONFIG. The device specific messages do + * not contain this TLV but have NDTPA_IFINDEX set to the + * corresponding interface index. + * + * To change neighbour table attributes, send RTM_SETNEIGHTBL + * with NDTA_NAME set. Changeable attribute include NDTA_THRESH[1-3], + * NDTA_GC_INTERVAL, and all TLVs in NDTA_PARMS unless marked + * otherwise. Device specific parameter sets can be changed by + * setting NDTPA_IFINDEX to the interface index of the corresponding + * device. + ****/ + +struct ndt_stats +{ + __u64 ndts_allocs; + __u64 ndts_destroys; + __u64 ndts_hash_grows; + __u64 ndts_res_failed; + __u64 ndts_lookups; + __u64 ndts_hits; + __u64 ndts_rcv_probes_mcast; + __u64 ndts_rcv_probes_ucast; + __u64 ndts_periodic_gc_runs; + __u64 ndts_forced_gc_runs; +}; + +enum { + NDTPA_UNSPEC, + NDTPA_IFINDEX, /* u32, unchangeable */ + NDTPA_REFCNT, /* u32, read-only */ + NDTPA_REACHABLE_TIME, /* u64, read-only, msecs */ + NDTPA_BASE_REACHABLE_TIME, /* u64, msecs */ + NDTPA_RETRANS_TIME, /* u64, msecs */ + NDTPA_GC_STALETIME, /* u64, msecs */ + NDTPA_DELAY_PROBE_TIME, /* u64, msecs */ + NDTPA_QUEUE_LEN, /* u32 */ + NDTPA_APP_PROBES, /* u32 */ + NDTPA_UCAST_PROBES, /* u32 */ + NDTPA_MCAST_PROBES, /* u32 */ + NDTPA_ANYCAST_DELAY, /* u64, msecs */ + NDTPA_PROXY_DELAY, /* u64, msecs */ + NDTPA_PROXY_QLEN, /* u32 */ + NDTPA_LOCKTIME, /* u64, msecs */ + __NDTPA_MAX +}; +#define NDTPA_MAX (__NDTPA_MAX - 1) + +struct ndtmsg +{ + __u8 ndtm_family; + __u8 ndtm_pad1; + __u16 ndtm_pad2; +}; + +struct ndt_config +{ + __u16 ndtc_key_len; + __u16 ndtc_entry_size; + __u32 ndtc_entries; + __u32 ndtc_last_flush; /* delta to now in msecs */ + __u32 ndtc_last_rand; /* delta to now in msecs */ + __u32 ndtc_hash_rnd; + __u32 ndtc_hash_mask; + __u32 ndtc_hash_chain_gc; + __u32 ndtc_proxy_qlen; +}; + +enum { + NDTA_UNSPEC, + NDTA_NAME, /* char *, unchangeable */ + NDTA_THRESH1, /* u32 */ + NDTA_THRESH2, /* u32 */ + NDTA_THRESH3, /* u32 */ + NDTA_CONFIG, /* struct ndt_config, read-only */ + NDTA_PARMS, /* nested TLV NDTPA_* */ + NDTA_STATS, /* struct ndt_stats, read-only */ + NDTA_GC_INTERVAL, /* u64, msecs */ + __NDTA_MAX +}; +#define NDTA_MAX (__NDTA_MAX - 1) + +#define NDTA_RTA(r) ((struct rtattr*)(((char*)(r)) + \ + NLMSG_ALIGN(sizeof(struct ndtmsg)))) +#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg)) + + +/**** + * General form of address family dependent message. + ****/ + +struct rtgenmsg +{ + unsigned char rtgen_family; +}; + +/***************************************************************** + * Link layer specific messages. + ****/ + +/* struct ifinfomsg + * passes link level specific information, not dependent + * on network protocol. + */ + +struct ifinfomsg +{ + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; /* ARPHRD_* */ + int ifi_index; /* Link index */ + unsigned ifi_flags; /* IFF_* flags */ + unsigned ifi_change; /* IFF_* change mask */ +}; + +/******************************************************************** + * prefix information + ****/ + +struct prefixmsg +{ + unsigned char prefix_family; + unsigned char prefix_pad1; + unsigned short prefix_pad2; + int prefix_ifindex; + unsigned char prefix_type; + unsigned char prefix_len; + unsigned char prefix_flags; + unsigned char prefix_pad3; +}; + +enum +{ + PREFIX_UNSPEC, + PREFIX_ADDRESS, + PREFIX_CACHEINFO, + __PREFIX_MAX +}; + +#define PREFIX_MAX (__PREFIX_MAX - 1) + +struct prefix_cacheinfo +{ + __u32 preferred_time; + __u32 valid_time; +}; + +/* The struct should be in sync with struct net_device_stats */ +struct rtnl_link_stats +{ + __u32 rx_packets; /* total packets received */ + __u32 tx_packets; /* total packets transmitted */ + __u32 rx_bytes; /* total bytes received */ + __u32 tx_bytes; /* total bytes transmitted */ + __u32 rx_errors; /* bad packets received */ + __u32 tx_errors; /* packet transmit problems */ + __u32 rx_dropped; /* no space in linux buffers */ + __u32 tx_dropped; /* no space available in linux */ + __u32 multicast; /* multicast packets received */ + __u32 collisions; + + /* detailed rx_errors: */ + __u32 rx_length_errors; + __u32 rx_over_errors; /* receiver ring buff overflow */ + __u32 rx_crc_errors; /* recved pkt with crc error */ + __u32 rx_frame_errors; /* recv'd frame alignment error */ + __u32 rx_fifo_errors; /* recv'r fifo overrun */ + __u32 rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ + __u32 tx_aborted_errors; + __u32 tx_carrier_errors; + __u32 tx_fifo_errors; + __u32 tx_heartbeat_errors; + __u32 tx_window_errors; + + /* for cslip etc */ + __u32 rx_compressed; + __u32 tx_compressed; +}; + +/* The struct should be in sync with struct ifmap */ +struct rtnl_link_ifmap +{ + __u64 mem_start; + __u64 mem_end; + __u64 base_addr; + __u16 irq; + __u8 dma; + __u8 port; +}; + +enum +{ + IFLA_UNSPEC, + IFLA_ADDRESS, + IFLA_BROADCAST, + IFLA_IFNAME, + IFLA_MTU, + IFLA_LINK, + IFLA_QDISC, + IFLA_STATS, + IFLA_COST, +#define IFLA_COST IFLA_COST + IFLA_PRIORITY, +#define IFLA_PRIORITY IFLA_PRIORITY + IFLA_MASTER, +#define IFLA_MASTER IFLA_MASTER + IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ +#define IFLA_WIRELESS IFLA_WIRELESS + IFLA_PROTINFO, /* Protocol specific information for a link */ +#define IFLA_PROTINFO IFLA_PROTINFO + IFLA_TXQLEN, +#define IFLA_TXQLEN IFLA_TXQLEN + IFLA_MAP, +#define IFLA_MAP IFLA_MAP + IFLA_WEIGHT, +#define IFLA_WEIGHT IFLA_WEIGHT + IFLA_OPERSTATE, + IFLA_LINKMODE, + __IFLA_MAX +}; + + +#define IFLA_MAX (__IFLA_MAX - 1) + +#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) + +/* ifi_flags. + + IFF_* flags. + + The only change is: + IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are + more not changeable by user. They describe link media + characteristics and set by device driver. + + Comments: + - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid + - If neither of these three flags are set; + the interface is NBMA. + + - IFF_MULTICAST does not mean anything special: + multicasts can be used on all not-NBMA links. + IFF_MULTICAST means that this media uses special encapsulation + for multicast frames. Apparently, all IFF_POINTOPOINT and + IFF_BROADCAST devices are able to use multicasts too. + */ + +/* IFLA_LINK. + For usual devices it is equal ifi_index. + If it is a "virtual interface" (f.e. tunnel), ifi_link + can point to real physical interface (f.e. for bandwidth calculations), + or maybe 0, what means, that real media is unknown (usual + for IPIP tunnels, when route to endpoint is allowed to change) + */ + +/* Subtype attributes for IFLA_PROTINFO */ +enum +{ + IFLA_INET6_UNSPEC, + IFLA_INET6_FLAGS, /* link flags */ + IFLA_INET6_CONF, /* sysctl parameters */ + IFLA_INET6_STATS, /* statistics */ + IFLA_INET6_MCAST, /* MC things. What of them? */ + IFLA_INET6_CACHEINFO, /* time values and max reasm size */ + __IFLA_INET6_MAX +}; + +#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) + +struct ifla_cacheinfo +{ + __u32 max_reasm_len; + __u32 tstamp; /* ipv6InterfaceTable updated timestamp */ + __u32 reachable_time; + __u32 retrans_time; +}; + +/***************************************************************** + * Traffic control messages. + ****/ + +struct tcmsg +{ + unsigned char tcm_family; + unsigned char tcm__pad1; + unsigned short tcm__pad2; + int tcm_ifindex; + __u32 tcm_handle; + __u32 tcm_parent; + __u32 tcm_info; +}; + +enum +{ + TCA_UNSPEC, + TCA_KIND, + TCA_OPTIONS, + TCA_STATS, + TCA_XSTATS, + TCA_RATE, + TCA_FCNT, + TCA_STATS2, + __TCA_MAX +}; + +#define TCA_MAX (__TCA_MAX - 1) + +#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) +#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) + +/* RTnetlink multicast groups - backwards compatibility for userspace */ +#define RTMGRP_LINK 1 +#define RTMGRP_NOTIFY 2 +#define RTMGRP_NEIGH 4 +#define RTMGRP_TC 8 + +#define RTMGRP_IPV4_IFADDR 0x10 +#define RTMGRP_IPV4_MROUTE 0x20 +#define RTMGRP_IPV4_ROUTE 0x40 +#define RTMGRP_IPV4_RULE 0x80 + +#define RTMGRP_IPV6_IFADDR 0x100 +#define RTMGRP_IPV6_MROUTE 0x200 +#define RTMGRP_IPV6_ROUTE 0x400 +#define RTMGRP_IPV6_IFINFO 0x800 + +#define RTMGRP_DECnet_IFADDR 0x1000 +#define RTMGRP_DECnet_ROUTE 0x4000 + +#define RTMGRP_IPV6_PREFIX 0x20000 + +/* RTnetlink multicast groups */ +enum rtnetlink_groups { + RTNLGRP_NONE, +#define RTNLGRP_NONE RTNLGRP_NONE + RTNLGRP_LINK, +#define RTNLGRP_LINK RTNLGRP_LINK + RTNLGRP_NOTIFY, +#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY + RTNLGRP_NEIGH, +#define RTNLGRP_NEIGH RTNLGRP_NEIGH + RTNLGRP_TC, +#define RTNLGRP_TC RTNLGRP_TC + RTNLGRP_IPV4_IFADDR, +#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR + RTNLGRP_IPV4_MROUTE, +#define RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_MROUTE + RTNLGRP_IPV4_ROUTE, +#define RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_ROUTE + RTNLGRP_IPV4_RULE, +#define RTNLGRP_IPV4_RULE RTNLGRP_IPV4_RULE + RTNLGRP_IPV6_IFADDR, +#define RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_IFADDR + RTNLGRP_IPV6_MROUTE, +#define RTNLGRP_IPV6_MROUTE RTNLGRP_IPV6_MROUTE + RTNLGRP_IPV6_ROUTE, +#define RTNLGRP_IPV6_ROUTE RTNLGRP_IPV6_ROUTE + RTNLGRP_IPV6_IFINFO, +#define RTNLGRP_IPV6_IFINFO RTNLGRP_IPV6_IFINFO + RTNLGRP_DECnet_IFADDR, +#define RTNLGRP_DECnet_IFADDR RTNLGRP_DECnet_IFADDR + RTNLGRP_NOP2, + RTNLGRP_DECnet_ROUTE, +#define RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_ROUTE + RTNLGRP_NOP3, + RTNLGRP_NOP4, + RTNLGRP_IPV6_PREFIX, +#define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX + __RTNLGRP_MAX +}; +#define RTNLGRP_MAX (__RTNLGRP_MAX - 1) + +/* TC action piece */ +struct tcamsg +{ + unsigned char tca_family; + unsigned char tca__pad1; + unsigned short tca__pad2; +}; +#define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg)))) +#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) +#define TCA_ACT_TAB 1 /* attr type must be >=1 */ +#define TCAA_MAX 1 + +/* End of information exported to user level */ + + + +#endif /* __LINUX_RTNETLINK_H */ diff --git a/libnetlink.c b/libnetlink.c new file mode 100644 index 0000000..151968b --- /dev/null +++ b/libnetlink.c @@ -0,0 +1,592 @@ +/* + * libnetlink.c RTnetlink service routines. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <fcntl.h> +#include <net/if_arp.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <sys/uio.h> + +#include "libnetlink.h" + +void rtnl_close(struct rtnl_handle *rth) +{ + close(rth->fd); +} + +int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, + int protocol) +{ + socklen_t addr_len; + int sndbuf = 32768; + int rcvbuf = 32768; + + memset(rth, 0, sizeof(rth)); + + rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol); + if (rth->fd < 0) { + perror("Cannot open netlink socket"); + return -1; + } + + if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) { + perror("SO_SNDBUF"); + return -1; + } + + if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) { + perror("SO_RCVBUF"); + return -1; + } + + memset(&rth->local, 0, sizeof(rth->local)); + rth->local.nl_family = AF_NETLINK; + rth->local.nl_groups = subscriptions; + + if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) { + perror("Cannot bind netlink socket"); + return -1; + } + addr_len = sizeof(rth->local); + if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) { + perror("Cannot getsockname"); + return -1; + } + if (addr_len != sizeof(rth->local)) { + fprintf(stderr, "Wrong address length %d\n", addr_len); + return -1; + } + if (rth->local.nl_family != AF_NETLINK) { + fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family); + return -1; + } + rth->seq = time(NULL); + return 0; +} + +int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) +{ + return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); +} + +int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) +{ + struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = type; + req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = rth->dump = ++rth->seq; + req.g.rtgen_family = family; + + return sendto(rth->fd, (void*)&req, sizeof(req), 0, + (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +int rtnl_send(struct rtnl_handle *rth, const char *buf, int len) +{ + struct sockaddr_nl nladdr; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); +} + +int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) +{ + struct nlmsghdr nlh; + struct sockaddr_nl nladdr; + struct iovec iov[2] = { + { .iov_base = &nlh, .iov_len = sizeof(nlh) }, + { .iov_base = req, .iov_len = len } + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = iov, + .msg_iovlen = 2, + }; + + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + + nlh.nlmsg_len = NLMSG_LENGTH(len); + nlh.nlmsg_type = type; + nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; + nlh.nlmsg_pid = 0; + nlh.nlmsg_seq = rth->dump = ++rth->seq; + + return sendmsg(rth->fd, &msg, 0); +} + +int rtnl_dump_filter(struct rtnl_handle *rth, + rtnl_filter_t filter, + void *arg1, + rtnl_filter_t junk, + void *arg2) +{ + struct sockaddr_nl nladdr; + struct iovec iov; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + char buf[16384]; + + iov.iov_base = buf; + while (1) { + int status; + struct nlmsghdr *h; + + iov.iov_len = sizeof(buf); + status = recvmsg(rth->fd, &msg, 0); + + if (status < 0) { + if (errno == EINTR) + continue; + perror("OVERRUN"); + continue; + } + + if (status == 0) { + fprintf(stderr, "EOF on netlink\n"); + return -1; + } + + h = (struct nlmsghdr*)buf; + while (NLMSG_OK(h, status)) { + int err; + + if (nladdr.nl_pid != 0 || + h->nlmsg_pid != rth->local.nl_pid || + h->nlmsg_seq != rth->dump) { + if (junk) { + err = junk(&nladdr, h, arg2); + if (err < 0) + return err; + } + goto skip_it; + } + + if (h->nlmsg_type == NLMSG_DONE) + return 0; + if (h->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { + fprintf(stderr, "ERROR truncated\n"); + } else { + errno = -err->error; + perror("RTNETLINK answers"); + } + return -1; + } + err = filter(&nladdr, h, arg1); + if (err < 0) + return err; + +skip_it: + h = NLMSG_NEXT(h, status); + } + if (msg.msg_flags & MSG_TRUNC) { + fprintf(stderr, "Message truncated\n"); + continue; + } + if (status) { + fprintf(stderr, "!!!Remnant of size %d\n", status); + exit(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); + exit(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); + exit(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); + exit(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); + exit(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); + exit(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); + exit(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 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; +} @@ -0,0 +1,69 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#ifndef LOG_H +#define LOG_H + +#include <stdio.h> +#include <stdarg.h> + +#define LOG_LEVEL_NONE 0 +#define LOG_LEVEL_ERROR 1 +#define LOG_LEVEL_INFO 2 +#define LOG_LEVEL_DEBUG 3 +#define LOG_LEVEL_RSTPLIB 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) + +#define ERROR(_fmt, _args...) PRINT(LOG_LEVEL_ERROR, "error, %s: " _fmt, __PRETTY_FUNCTION__, ##_args) + +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 @@ -0,0 +1,110 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#include "epoll_loop.h" +#include "bridge_ctl.h" +#include "ctl_socket_server.h" +#include "netif_utils.h" +#include "log.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> +#include <syslog.h> + +static int become_daemon = 1; +static int is_daemon = 0; +int log_level = LOG_LEVEL_DEFAULT; + +int main(int argc, char *argv[]) +{ + int c; + 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; + } + } + + TST(init_epoll() == 0, -1); + TST(ctl_socket_init() == 0, -1); + + TST(netsock_init() == 0, -1); + TST(init_bridge_ops() == 0, -1); + if (become_daemon) { + openlog("rstpd", 0, LOG_DAEMON); + daemon(0,0); + is_daemon = 1; + } + return epoll_main_loop(); +} + +/*********************** Logging *********************/ + +#include <stdarg.h> +#include <time.h> + +void vDprintf(int level, const char* fmt, va_list ap) +{ + if (level > log_level) + return; + + if (!is_daemon) { + char logbuf[256]; + logbuf[255] = 0; + time_t clock; + struct tm *local_tm; + time(&clock); + local_tm = localtime (&clock); + int l = strftime(logbuf, sizeof(logbuf)-1, "%F %T ", local_tm); + vsnprintf(logbuf + l, sizeof(logbuf) - l - 1, fmt, ap); + printf("%s\n", logbuf); + } + else { + vsyslog((level <= LOG_LEVEL_INFO) ? LOG_INFO : LOG_DEBUG, fmt, ap); + } +} + +void Dprintf(int level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vDprintf(level, fmt, ap); + va_end(ap); +} diff --git a/netif_utils.c b/netif_utils.c new file mode 100644 index 0000000..a14e0c3 --- /dev/null +++ b/netif_utils.c @@ -0,0 +1,141 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#include "netif_utils.h" + +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdint.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#include <net/if.h> +#include <linux/if_ether.h> +#include <linux/ethtool.h> +#include <linux/sockios.h> + +#include "log.h" + + + +int netsock = -1; + +int netsock_init(void) +{ + LOG(""); + netsock = socket(AF_INET, SOCK_DGRAM, 0); + if (netsock < 0) { + ERROR("Couldn't open inet socket for ioctls: %m\n"); + return -1; + } + return 0; +} + +int get_hwaddr(char *ifname, unsigned char *hwaddr) +{ + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(netsock, SIOCGIFHWADDR, &ifr) < 0) { + ERROR("%s: get hw address failed: %m", ifname); + return -1; + } + memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + 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 (ioctl(netsock, SIOCETHTOOL, &ifr) < 0) { + ERROR("Cannot get link status 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 */ +int is_bridge(char *if_name) +{ + char path[32 + IFNAMSIZ]; + sprintf(path, "/sys/class/net/%s/bridge", if_name); + return (access(path, R_OK) == 0); +} + +/* This will work even less if the bridge port is renamed after being + joined to the bridge. +*/ +int is_bridge_slave(char *br_name, char *if_name) +{ + char path[32 + 2 * IFNAMSIZ]; + sprintf(path, "/sys/class/net/%s/brif/%s", br_name, if_name); + return (access(path, R_OK) == 0); +} + +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 (l == 0) { + ERROR("Empty port index file"); + goto out; + } + else if (l == sizeof(buf) - 1) { + ERROR("port_index file too long"); + goto out; + } + buf[l] = 0; + if (buf[l-1] == '\n') + buf[l-1] = 0; + char *end; + res = strtoul(buf, &end, 0); + if (*end != 0 || res > INT_MAX) { + 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..99c99d5 --- /dev/null +++ b/netif_utils.h @@ -0,0 +1,42 @@ +/***************************************************************************** + Copyright (c) 2006 EMC Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Authors: Srinivas Aji <Aji_Srinivas@emc.com> + +******************************************************************************/ + +#ifndef 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); + +/********* Sysfs based utility functions *************/ +int is_bridge(char *if_name); + +int is_bridge_slave(char *br_name, char *if_name); + +int get_bridge_portno(char *if_name); + +#endif diff --git a/rstp.spec b/rstp.spec new file mode 100644 index 0000000..5680985 --- /dev/null +++ b/rstp.spec @@ -0,0 +1,63 @@ +Summary: RSTP daemon +Name: rstp +Version: %{VERSION} +Release: %{BUILD} +License: LGPL, GPL +URL: http://www.rainfinity.com/ +Vendor: Rainfinity +Packager: Rainfinity +Source0: rstp-%{VERSION}.tgz +Group: Network/Admin +#Requires: bridge-utils = 1.0.4-4 + + +%description +Bridge init script for RainStorage + +%prep + +%setup -q + +%build +make + +%install +make install INSTALLPREFIX=$RPM_BUILD_ROOT + +%pre + +%post + +%preun + +%postun + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root) +/sbin +/usr/share/man/man8 + +%changelog +* Mon Oct 30 2006 Srinivas Aji <Aji_Srinivas@emc.com> +- version 0.16, Don't define STRONGLY_SPEC_802_1_W, not helping much +- Fix logging to go through syslog + +* Fri Oct 20 2006 Srinivas Aji <Aji_Srinivas@emc.com> +- version 0.15, fixes based on second round of UNH testing, 802.1w +- rstpctl syntax changes, list all bridges, ports of a bridge + +* Wed Sep 13 2006 Srinivas Aji <Aji_Srinivas@emc.com> +- version 0.14, small fixes. fix an fd leak. + +* Tue Sep 5 2006 Srinivas Aji <Aji_Srinivas@emc.com> +- version 0.13, Use netlink patch to send/recv BPDUs, instead of LLC +- Some fixes based on first round of UNH testing + +* Sun Aug 20 2006 Srinivas Aji <Aji_Srinivas@emc.com> +- version 0.12, some bugfixes, added man pages. + +* Thu Aug 10 2006 Srinivas Aji <Aji_Srinivas@emc.com> +- Initial build. diff --git a/rstpctl.8 b/rstpctl.8 new file mode 100644 index 0000000..456a879 --- /dev/null +++ b/rstpctl.8 @@ -0,0 +1,146 @@ +.TH RSTPCTL 8 "August 20, 2006" "" "" +.SH NAME +rstpctl \- rstpd spanning tree protocol administration +.SH SYNOPSIS +.BR "rstpctl [command]" +.SH DESCRIPTION +.B rstpctl +is used to set up, maintain, and inspect the bridge RSTP +configuration for ethernet bridges as implemented using +.BR rstpd (8). +.BR rstpd +implements the spanning tree algorithm specified by the Rapid Spanning +Tree Protocol (RSTP). An implementation of the original Spanning Tree +Protocol (STP) is provided by the Linux kernel bridging code and can +be enabled and controlled using the +.BR "brctl" +command. + +.SH COMMANDS + +.B rstpctl rstp <bridge> {on|off} +configures whether rstpd implements RSTP (Rapid Spanning Tree +Protocol) for the specified bridge. The remaining commands are +applicable only if rstpd is configured to run RSTP on that bridge. + +.B rstpctl showbridge [<bridge> ... ] +displays the spanning tree configuration and status for the specified +bridges. If no bridges are given, it displays the information for all +bridges. + +.B rstpctl showport <bridge> [<port> ... ] +displays, as one line per port, the spanning tree configuration and +status for the specified ports. If no ports are specified, it displays +the information for all ports of the bridge. + +The format of the line is : +.br +.B "pes name iiii sss rrrr-rootbridgeid dddd-desgbridgeid dprt R" +.br +where the following abbreviations are used. + +\fBp\fR: '*' if the port link is not point to point, ' ' otherwise. +.br +\fBe\fR: 'E' if the port is operating as an edge port, ' ' otherwise. +.br +\fBs\fR: 's' if the port is in STP (slow) mode, ' ' otherwise. +.br +\fBname\fR: The name of the port, i.e, the network interface name. +.br +\fBiiii\fR: The port id, containing the port priority and port number +.br +\fBsss\fR: The port state, one of Dis (disabled), Blk (blocking), Lrn +(learning), Fwd (forwading), Non (non-stp), Unk (unknown). +.br +\fBrrrr-rootbridgeid\fR: Root bridge id, including priority. +.br +\fBdddd-desgbridgeid\fR: Designated bridge id, including priority +.br +\fBdprt\fR: Designated port id +.br +\fBR\fR: Port role, one of 'A' (alternate), 'B' (backup), 'R' (root), 'D' +(designated), '-' (non-stp) + + +.B rstpctl showportdetail <bridge> [<port> ... ] +displays the spanning tree configuration and status for the specified +ports much more verbosely. If no ports are specified, it displays the +information for all ports of the bridge. + +.B rstpctl setbridgestate <bridge> {on|off} +enables/disables the operation of RSTP. When RSTP is configured with +the +.BR rstp +subcommand listed above, the default is enabled. + +.B rstpctl setbridgeprio <bridge> <priority> +sets the bridge's priority to <priority>. The priority value is a +number between 0 and 61440 in steps of 4096, and defaults to 32768. +Lower priority values are 'better'. The bridge with the lowest +priority will be elected 'root bridge'. + +.B rstpctl sethello <bridge> <time> +sets the bridge's 'bridge hello time' to <time> seconds. + +.B rstpctl setmaxage <bridge> <time> +sets the bridge's 'maximum message age' to <time> seconds. + +.B rstpctl setfdelay <bridge> <time> +sets the bridge's 'bridge forward delay' to <time> seconds. + +.B rstpctl setforcevers <bridge> {normal|slow} +sets the bridge's spanning tree algorithm to operate in normal +(i.e. RSTP) mode or force it to operate in slow (i.e. old STP) +mode. In normal mode, RSTP falls back to STP on ports where it +sees other hosts operating in STP mode. + +.B rstpctl setportprio <bridge> <port> <priority> +sets the ports's priority to <priority>. The priority value is a +number from 0 to 240 in steps of 16, defaulting to 128. + +.B rstpctl setportpathcost <bridge> <port> <cost> +sets the ports's path cost to <cost>. This is by default set +automatically based on the link speed. This setting overrides the +automatic setting. Setting this to zero puts it back in automatic +mode. + +.B rstpctl setportedge <bridge> <port> {yes|no} +sets or unsets the port's configures as an edge port. If a port is an +edge port, then it is assumed to be a leaf link in the graph, not +connected to any other bridges. Receiving any STP BPDU's on a port +configured as an edge port turns off edge port behaviour for the port. + +.B rstpctl setportnonstp <bridge> <port> {yes|no} +: Setting this to yes disables RSTP operation on the port, which then +is always kept in FORWARDING state. + +.B rstpctl portmcheck <bridge> <port> +: This command is used when the port is operating in STP compatibility +mode. I causes the bridge to transmit RSTP BPDUs and to test the +hypothesis that non RSTP hosts have been removed from the LAN +connected to the port, and if that is the case, the remaining hosts +switch back to RSTP mode. + +.B rstpctl debuglevel <level> +sets the level of verbosity of rstpd's logging. + +.SH NOTES +TODO: Indicate lack of persistence of configuration across restarts of +daemon. + +.SH SEE ALSO +.BR rstpd(8), +.BR brctl(8) + +.SH COPYRIGHT +This manual page Copyright (c) 2006 EMC Corporation. + +This manual page 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. It comes with NO WARRANTY. +.\" The full GNU General Public License is included in this distribution in the +.\" file called LICENSE. + +.SH AUTHOR +Srinivas Aji <Aji_Srinivas@emc.com> @@ -0,0 +1,65 @@ +.TH RSTPCTL 8 "August 20, 2006" "" "" +.SH NAME +rstpd \- Daemon implementing Rapid Spanning Tree Protocol for Linux +bridges. +.SH SYNOPSIS +.BR "rstpd [\-d] [\-v <level>]" +.SH DESCRIPTION +.B rstpd +implements the Rapid Spanning Tree Protocol (RSTP) on Linux +bridges. When started, it merely monitors the ethernet bridges on the +machine until instructed to run RSTP on any of them using +.BR rstpctl . +When run with the +.BR "\-d" +option, it runs in the foreground and outputs messages which are +normally logged through syslog to standard output and standard +error. With the +.BR "\-v" +option, the level of verbosity of the logs can be controlled. +See +.BR rstpctl (8) +for more information on configuring RSTP. + +.SH SPANNING TREE PROTOCOL +Multiple ethernet bridges can work together to create even larger +networks of ethernets using the IEEE 802.1d spanning tree +protocol. This protocol is used for finding the shortest path between +two ethernets, and for eliminating loops from the topology. As this +protocol is a standard, linux bridges will interwork properly with +other third party bridge products. Bridges communicate with eachother +by sending and receiving BPDUs (Bridge Protocol Data Units). These +BPDUs can be recognised by an ethernet destination address of +01:80:c2:00:00:00. + +The spanning tree protocol can also be turned off (for those +situations where it just doesn't make sense, for example when this +linux box is the only bridge on the LAN, or when you know that there +are no loops in the topology.) + +The 2004 version of the 802.1D specification describes the Rapid +Spanning Tree Protocol, which supercedes the older Spanning Tree +Protocol while providing for backward compatible operation with it. +It provides for faster reconfiguration during network changes than +STP in many cases. + +.SH NOTES +TODO: Indicate lack of persistence of configuration across restarts of +daemon. + +.SH SEE ALSO +.BR rstpctl(8), +.BR brctl(8) + +.SH COPYRIGHT +This manual page Copyright (c) 2006 EMC Corporation. + +This manual page 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. It comes with NO WARRANTY. +.\" The full GNU General Public License is included in this distribution in the +.\" file called LICENSE. + +.SH AUTHOR +Srinivas Aji <Aji_Srinivas@emc.com> diff --git a/rstplib/AUTHORS b/rstplib/AUTHORS new file mode 100644 index 0000000..71dc74e --- /dev/null +++ b/rstplib/AUTHORS @@ -0,0 +1,3 @@ +Alex Rozin <alexr@nbase.co.il> +Michael Rozhavsky <mike@nbase.co.il> + diff --git a/rstplib/Authors b/rstplib/Authors new file mode 100644 index 0000000..9c141f0 --- /dev/null +++ b/rstplib/Authors @@ -0,0 +1,2 @@ +Alex Rozin <alexr@nbase.co.il> +Michael Rozhavsky <mike@nbase.co.il> diff --git a/rstplib/B5055.txt b/rstplib/B5055.txt new file mode 100644 index 0000000..7a065bf --- /dev/null +++ b/rstplib/B5055.txt @@ -0,0 +1,47 @@ +alex alex ~/Stp.emu>./bridge +my pid: 5055 +12:52:57:clearFDB (all, start stpm) +12:52:57 B5055 > connected port p02 +12:57:16 B5055 > 13:00:03:brige 0 root port became p02 +get-st-p + E p01 8001 Dis 0400-00c013000001 8000-00bf13000001 8001 + p02 8002 Fwd 0400-00c013000001 0400-00c013000001 8002 R + E p03 8003 Dis 0400-00c013000001 8000-00bf13000001 8003 + E p04 8004 Dis 0400-00c013000001 8000-00bf13000001 8004 +13:01:30 B5055 > +13:01:30 B5055 > get-st-b +Bridge: B5055 State:enabled +BridgeId: 8000-00bf13000001 Bridge Proirity: 32768 (0x8000) +Designated Root: 0400-00c013000001 +Root Port: 8002 (p02 +), Root Cost: 20000 +Max Age: 20 Bridge Max Age: 20 +Hello Time: 2 Bridge Hello Time: 2 +Forward Delay: 15 Bridge Forward Delay: 15 +13:01:48 B5055 > +13:01:48 B5055 > get-st-p 2 +Stp Port p02: PortId: 8002 in Bridge 'B5055': +Priority: 128 +State: Forwarding +PathCost: 20000 +Designated Root: 0400-00c013000001 +Designated Cost: 0 +Designated Bridge: 0400-00c013000001 +Designated Port: 8002 +Role: Root +Tc: n TcAck: n +TcWhile: 0 +fdWhile: 0 rcvdInfoWhile: 5 +helloWhen: 2 rrWhile: 15 +mdelayWhile: 0 lnkWhile: 0 +rbWhile: 0 txCount: 0 +RSTP BPDU rx: 69 +CONFIG BPDU rx: 0 +TCN BPDU rx: 0 +13:02:13 B5055 > +13:02:13 B5055 > disconnected port p02 +13:02:47:clearFDB (2, all, this port, 'disable port') +13:02:47 B5055 > 13:02:47:brige 0 became root +shutdown from manager :( +alex alex ~/Stp.emu> + diff --git a/rstplib/B5056.txt b/rstplib/B5056.txt new file mode 100644 index 0000000..48f976b --- /dev/null +++ b/rstplib/B5056.txt @@ -0,0 +1,141 @@ +alex alex ~/Stp.emu>./bridge +my pid: 5056 +12:53:09:clearFDB (all, start stpm) +12:53:09 B5056 > ? +List of possible commands: +enable enable rstp +disbale disbale rstp +get-br-cfg get bridge config +get-port-cfg get port config +set-br-prio set bridge priority +set-br-maxage set bridge max. age +set-br-fdelay set bridge forward delay +set-br-force-version set bridge force version +set-prt-prio set port priority +set-prt-pcost set port path cost +set-prt-p2p set port admin 'Point To Point' +set-prt-edge set port admin edge +set-trace set trace +'standard' commands: +quit 'shutdown' bridge + alias 'ex' (from 'exit') +? help + alias 'h' (from 'help') +12:53:23 B5056 > set-trace 2 all disable +port p02 tag 0 pcost (was disabled) now disabled +port p02 tag 0 edge (was disabled) now disabled +port p02 tag 0 p2p (was disabled) now disabled +port p02 tag 0 transmit (was disabled) now disabled +port p02 tag 0 migrate (was disabled) now disabled +port p02 tag 0 topoch (was disabled) now disabled +port p02 tag 0 sttrans (was disabled) now disabled +port p02 tag 0 roletrns (was disabled) now disabled +port p02 tag 0 info (was disabled) now disabled +12:53:39 B5056 > set-trace 2 edge enable +port p02 tag 0 edge (was disabled) now enabled +12:54:38 B5056 > set-trace 2 topoch enable +port p02 tag 0 topoch (was disabled) now enabled +12:54:57 B5056 > set-trace 2 roletrns enable +port p02 tag 0 roletrns (was disabled) now enabled +12:55:14 B5056 > help get-st-p +get-port-cfg: get port config +----------------------------- + (possible short name: 'get-st-p') + + arguments: + ---------- + arg: port index; default 'all ports' +12:55:57 B5056 > get-st-pcfg + E p01 8001 Dis 8000-00c013000001 8000-00c013000001 8001 + E p02 8002 Dis 8000-00c013000001 8000-00c013000001 8002 + E p03 8003 Dis 8000-00c013000001 8000-00c013000001 8003 + E p04 8004 Dis 8000-00c013000001 8000-00c013000001 8004 +12:56:19 B5056 > +12:56:19 B5056 > get-st-p 2 +Stp Port p02: PortId: 8002 in Bridge 'B5056': +Priority: 128 +State: Disabled +PathCost: 20000 +Designated Root: 8000-00c013000001 +Designated Cost: 0 +Designated Bridge: 8000-00c013000001 +Designated Port: 8002 +Flags: operEdge +RSTP BPDU rx: 0 +CONFIG BPDU rx: 0 +TCN BPDU rx: 0 +12:56:46 B5056 > connected port p02 +12:57:16:Port p02 became 'enable' adminEdge=Y +12:57:16 B5056 > 12:57:16:edge(B5056-p02): DISABLED=>DETECTED +12:57:16:edge(B5056-p02): DETECTED=>DELEAYED +12:57:16:Age-B5056:port p02 => Designated +12:57:16:roletrns(B5056-p02): DESIGNATED_PORT=>DESIGNATED_PROPOSE +12:57:16:roletrns(B5056-p02): DESIGNATED_PORT=>DESIGNATED_SYNCED +12:57:16:topoch(B5056-p02): INACTIVE=>TCACTIVE +12:57:16:port p02 'edge' resolved by BPDU incame +12:57:16:edge(B5056-p02): DELEAYED=>RESOLVED +12:57:16:brige 0 root port became p02 +12:57:16:Rec-B5056:port p02 => Root +12:57:16:roletrns(B5056-p02): DESIGNATED_PORT=>ROOT_PORT +12:57:16:roletrns(B5056-p02): ROOT_PORT=>REROOT +12:57:16:roletrns(B5056-p02): REROOT=>ROOT_PORT +12:57:16:roletrns(B5056-p02): ROOT_PORT=>ROOT_AGREED +12:57:16:roletrns(B5056-p02): ROOT_AGREED=>ROOT_PORT +12:57:16:roletrns(B5056-p02): ROOT_PORT=>ROOT_LEARN +12:57:16:roletrns(B5056-p02): ROOT_LEARN=>ROOT_PORT +12:57:16:roletrns(B5056-p02): ROOT_PORT=>ROOT_FORWARD +12:57:16:roletrns(B5056-p02): ROOT_FORWARD=>ROOT_PORT +12:57:16:roletrns(B5056-p02): ROOT_PORT=>REROOTED +12:57:16:roletrns(B5056-p02): REROOTED=>ROOT_PORT +12:57:16:topoch(B5056-p02): TCACTIVE=>DETECTED +12:57:16:clearFDB (2, 0, other ports, 'DETECTED') +12:57:16:topoch(B5056-p02): DETECTED=>TCACTIVE +12:57:18:port p02 rx rcvdTc 0X4d +12:57:18:topoch(B5056-p02): TCACTIVE=>NOTIFIED_TC +12:57:18:clearFDB (2, 0, other ports, 'NOTIFIED_TC') +12:57:18:topoch(B5056-p02): NOTIFIED_TC=>TCACTIVE +12:59:11 B5056 > get-st-p + E p01 8001 Dis 8000-00bf13000001 8000-00c013000001 8001 + p02 8002 Fwd 8000-00bf13000001 8000-00bf13000001 8002 R + E p03 8003 Dis 8000-00bf13000001 8000-00c013000001 8003 + E p04 8004 Dis 8000-00bf13000001 8000-00c013000001 8004 +12:59:15 B5056 > +12:59:15 B5056 > set-br-prio 0x400 +STP_rolesel_force +12:59:59:brige 0 became root +12:59:59:rec-B5056:port p02 => Backup +Changed rstp bridge priority +12:59:59 B5056 > 12:59:59:rec-B5056:port p02 => Backup +12:59:59:roletrns(B5056-p02): ROOT_PORT=>BLOCK_PORT +12:59:59:topoch(B5056-p02): TCACTIVE=>INIT +12:59:59:topoch(B5056-p02): INIT=>INACTIVE +12:59:59:roletrns(B5056-p02): BLOCK_PORT=>BLOCKED_PORT +12:59:59:roletrns(B5056-p02): BLOCKED_PORT=>BACKUP_PORT +12:59:59:roletrns(B5056-p02): BACKUP_PORT=>BLOCKED_PORT +13:00:00:roletrns(B5056-p02): BLOCKED_PORT=>BACKUP_PORT +13:00:00:roletrns(B5056-p02): BACKUP_PORT=>BLOCKED_PORT +13:00:01:roletrns(B5056-p02): BLOCKED_PORT=>BACKUP_PORT +13:00:01:roletrns(B5056-p02): BACKUP_PORT=>BLOCKED_PORT +13:00:02:roletrns(B5056-p02): BLOCKED_PORT=>BACKUP_PORT +13:00:02:roletrns(B5056-p02): BACKUP_PORT=>BLOCKED_PORT +13:00:03:roletrns(B5056-p02): BLOCKED_PORT=>BACKUP_PORT +13:00:03:roletrns(B5056-p02): BACKUP_PORT=>BLOCKED_PORT +13:00:03:Age-B5056:port p02 => Designated +13:00:03:roletrns(B5056-p02): DESIGNATED_PORT=>DESIGNATED_PROPOSE +13:00:03:roletrns(B5056-p02): DESIGNATED_PORT=>DESIGNATED_SYNCED +13:00:03:topoch(B5056-p02): INACTIVE=>TCACTIVE +13:00:03:!AGREEMENT! on port p02 +13:00:03:roletrns(B5056-p02): DESIGNATED_PORT=>DESIGNATED_LEARN +13:00:03:roletrns(B5056-p02): DESIGNATED_PORT=>DESIGNATED_FORWARD +13:00:03:topoch(B5056-p02): TCACTIVE=>DETECTED +13:00:03:clearFDB (2, 0, other ports, 'DETECTED') +13:00:03:topoch(B5056-p02): DETECTED=>TCACTIVE +get-st-p + E p01 8001 Dis 0400-00c013000001 0400-00c013000001 8001 + p02 8002 Fwd 0400-00c013000001 0400-00c013000001 8002 D + E p03 8003 Dis 0400-00c013000001 0400-00c013000001 8003 + E p04 8004 Dis 0400-00c013000001 0400-00c013000001 8004 +13:00:29 B5056 > +13:02:47 B5056 > quit +alex alex ~/Stp.emu> + diff --git a/rstplib/COPYING b/rstplib/COPYING new file mode 100644 index 0000000..223ede7 --- /dev/null +++ b/rstplib/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/rstplib/CVS.HOWTO b/rstplib/CVS.HOWTO new file mode 100644 index 0000000..987724d --- /dev/null +++ b/rstplib/CVS.HOWTO @@ -0,0 +1,22 @@ +Anonymous CVS Access + +This project's SourceForge CVS repository can be checked out through +anonymous (pserver) CVS with the following instruction set. The module +you wish to check out must be specified as the modulename. When prompted +for a password for anonymous, simply press the Enter key. + +cvs -d:pserver:anonymous@cvs.rstplib.sourceforge.net:/cvsroot/rstplib login + +cvs -z3 -d:pserver:anonymous@cvs.rstplib.sourceforge.net:/cvsroot/rstplib co modulename + +Updates from within the module's directory do not need the -d parameter. + +Developer CVS Access via SSH + +Only project developers can access the CVS tree via this method. SSH1 must +be installed on your client machine. Substitute modulename and developername +with the proper values. Enter your site password when prompted. + +export CVS_RSH=ssh + +cvs -z3 -d:ext:developername@cvs.rstplib.sourceforge.net:/cvsroot/rstplib co modulename diff --git a/rstplib/ChangeLog b/rstplib/ChangeLog new file mode 100644 index 0000000..322987e --- /dev/null +++ b/rstplib/ChangeLog @@ -0,0 +1,55 @@ +ChangeLog +========= +## +- $Log: ChangeLog,v $ +- Revision 1.7 2002/01/20 07:34:36 ralex +- - 'clilib' has been changed for 'readline' version 4.2 (thanks to +- Michael Rozhavsky <mike@nbase.co.il>) +- - 'The bug in clilib has been fixed +- +- Revision 1.6 2002/01/09 06:58:08 ralex +- The first version is to outcome +- +- Revision 1.5 2001/11/29 09:54:31 ralex +- - The bug in "Port Role Transitions state machine" has been +- fixed (hop to the DESIGNATED_LISTEN) +- - Defaults for 'bridge' and 'ports' configuration have +- been moved; order of the initialization has been changed +- +- Revision 1.4 2001/11/26 08:02:12 ralex +- All management entities from 14.8.1 & 14.8.2 are now supported +- +- Revision 1.3 2001/11/21 14:32:27 ralex +- The file ChangeLog has been 'synchrinyzed' +- +- Revision 1.2 2001/11/21 14:22:12 ralex +- - In the librstp.a : drastic change in Port Role Selection +- state machine as a result of posting +- of Mick Seaman and implementation of 802.1y Z1 +- - In libcli.c : readline completion works; for it +- the structure CMD_DSCR_T has been redisgned +- and the languages (both in 'mngr' and in 'bridge') +- have been changed +- +- Revision 1.1 2001/11/14 14:10:44 ralex +- - All per Port variables have been moved from the State +- Machines into the Port instance (it made the state +- machines much more clear) +- +- - In libcli.a instead of stupid fgets() function we use +- now readline (thanks to Michel Roshavsky) +- +- - 'mcheck' support +- +- - 'nonStp' support (I know, that it is out the standard, +- but it seems to be useful (see a discussion on +- http://www1.ietf.org/mail-archive/working-groups/bridge/current/msg00038.html) +- and our customers demand it +- +- - The function rolesel.c has been drastically fixed, IMHO +- closer to the standard +- +- - Nicer output + +## + diff --git a/rstplib/INSTALL b/rstplib/INSTALL new file mode 100644 index 0000000..1085207 --- /dev/null +++ b/rstplib/INSTALL @@ -0,0 +1,16 @@ +Basic Installation +================== + +While there is no a special configure process, the simplest way to +compile this package is: + +1. `cd' to the directory containing the package's source code and type + `make'. There may problems in compiling on another platforms. + +2. After that the packege is ready to usage. You will get 3 libraies + (libcli.a librstp.a libuid.a) and 2 executables: mngr & bridge + +In the future 'configure' file will be designed for another +platfors. + + diff --git a/rstplib/Makefile b/rstplib/Makefile new file mode 100644 index 0000000..c282756 --- /dev/null +++ b/rstplib/Makefile @@ -0,0 +1,102 @@ +#************************************************************************ +#* RSTP library - Rapid Spanning Tree (802.1t, 802.1w) +#* Copyright (C) 2001-2003 Optical Access +#* Author: Alex Rozin +#* +#* This file is part of RSTP library. +#* +#* RSTP library is free software; you can redistribute it and/or modify it +#* under the terms of the GNU Lesser General Public License as published by the +#* Free Software Foundation; version 2.1 +#* +#* RSTP library is distributed in the hope that it will be useful, but +#* WITHOUT ANY WARRANTY; without even the implied warranty of +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +#* General Public License for more details. +#* +#* You should have received a copy of the GNU Lesser General Public License +#* along with RSTP library; see the file COPYING. If not, write to the Free +#* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +#* 02111-1307, USA. +#**********************************************************************/ + +DEFS= +CC = gcc #-E +#CFLAGS = -g -Wall -D_REENTRANT -D__LINUX__ -DSTP_DBG=1 -DOLD_READLINE +CFLAGS = -g -Wall -D_REENTRANT -D__LINUX__ -DSTP_DBG=1 # -DOLD_READLINE + +INCLUDES = -I. +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(CFLAGS) +LIBS= -lutil -lreadline -ltermcap -lpthread + +AR = ar +#ARFLAGS = rcs +ARFLAGS = cru +RANLIB = ranlib +SOFLAGS = -nostartfiles -shared + +EXECUTABLE= bridge mngr +RSTPLIBS = rstp +RSTPLIBF = lib$(RSTPLIBS).a +UIDLIBS = uid +UIDLIBF = lib$(UIDLIBS).a +CLILIBS = cli +CLILIBF = lib$(CLILIBS).a + +MNGRCFILES = mngr.c +MNGROFILES = mngr.o +BRIDGECFILES = bridge.c stp_cli.c stp_to.c +BRIDGEOFILES = bridge.o stp_cli.o stp_to.o + +RSTPCFILES = statmch.c stpm.c port.c stpmgmt.c stp_in.c rolesel.c \ +vector.c times.c \ +portinfo.c roletrns.c sttrans.c topoch.c migrate.c transmit.c \ +p2p.c edge.c pcost.c +RSTPOFILES = statmch.o stpm.o port.o stpmgmt.o stp_in.o rolesel.o \ +vector.o times.o \ +portinfo.o roletrns.o sttrans.o topoch.o migrate.o transmit.o \ +p2p.o edge.o pcost.o + +UIDCFILES = uid_sock.c +UIDOFILES = uid_sock.o + +CLICFILES = cli.c +CLIOFILES = cli.o + +CFILES= $(MNGRCFILES) $(BRIDGECFILES) $(RSTPCFILES) $(UIDCFILES) $(CLICFILES) + +OFILES= $(MNGROFILES) $(BRIDGEOFILES) $(RSTPOFILES) $(UIDOFILES) $(CLIOFILES) + +.SUFFIXES: .S .c .o .s + +all: depend $(EXECUTABLE) + +mngr: ${MNGROFILES} $(UIDLIBF) $(CLILIBF) + $(CC) ${MNGROFILES} $(LIBS) -L. -l$(UIDLIBS) -l$(CLILIBS) -o $@ + +bridge: ${BRIDGEOFILES} $(RSTPLIBF) $(UIDLIBF) $(CLILIBF) + $(CC) ${BRIDGEOFILES} $(LIBS) -L. -l$(RSTPLIBS) -l$(UIDLIBS) -l$(CLILIBS) -o $@ + +$(RSTPLIBF): ${RSTPOFILES} + -rm -f $@ + $(AR) $(ARFLAGS) $@ $(RSTPOFILES) + $(RANLIB) $@ + +$(UIDLIBF): ${UIDOFILES} + -rm -f $@ + $(AR) $(ARFLAGS) $@ $(UIDOFILES) + $(RANLIB) $@ + +$(CLILIBF): $(CLIOFILES) + -rm -f $@ + $(AR) $(ARFLAGS) $@ $(CLIOFILES) + $(RANLIB) $@ + +clean: + rm -f all *.o *.a *~ $(EXECUTABLE) *.bak make.depend + +depend: + echo "# DO NOT DELETE THIS LINE -- make depend depends on it." > make.depend + makedepend -- $(DEFS) $(INCLUDES) $(CFLAGS) -- $(CFILES) -f make.depend + +-include make.depend diff --git a/rstplib/NEWS b/rstplib/NEWS new file mode 100644 index 0000000..5ac0d57 --- /dev/null +++ b/rstplib/NEWS @@ -0,0 +1,19 @@ +- All per Port variables have been moved from the State + Machines into the Port instance (it made the state + machines much more clear) + +- In libcli.a instead of stupid fgets() function we use + now readline (thanks to Michel Roshavsky) + +- 'mcheck' support + +- 'nonStp' support (I know, that it is out the standard, + but it seems to be useful (see a discussion on +http://www1.ietf.org/mail-archive/working-groups/bridge/current/msg00038.html) + and our customers demand it + +- The function rolesel.c has been drastically fixed, IMHO + closer to the standard + +- Nicer output + diff --git a/rstplib/README b/rstplib/README new file mode 100644 index 0000000..abcdb9e --- /dev/null +++ b/rstplib/README @@ -0,0 +1,37 @@ + + +The Rapid Spanning Tree Library project contains a full implementation +of 802.1s as an library with API. There is two processes, using this +library: 'bridge' & 'mngr'. First simulates RSTP bridge behavior, second +is dedicated to link/unlink 'bridges' into virtual RSTP domain (VRSTPD). +Both 'bridge' & 'mngr' has its own simple CLI like language of commands; +these commands allow to manage the VRSTPD. There are tools to trace +state machine transitions and get traps about drastic changes. + +Purpose: studying, debugging, development. + +The library may be used in real bridges/routers while bounding to a real +system depending environment. + +To run: +1. In one shell run 'mngr' +./mngr +You will get prompt of 'mngr'; type '?' and get full help of +'mngr' commands. + +2. In another shell run bridge instance. +./bridge +You will get prompt of 'bridge'; type '?' and get full help +of bridge management commands. + +3. You may (and should) run a number of bridge instances, each in its +separate shell (this way you will be able manage them). + +For example, if there were two bridge instances, you may see examples +of the dialog in files mngr.txt, B5055.txt and B5056.txt. + +Note: prompt both of 'mngr' and of 'bridge' instance contains time stamp, +while all these process run onto the same computer, these time stamps +are synchronized. + + diff --git a/rstplib/README.files b/rstplib/README.files new file mode 100644 index 0000000..f08ea7d --- /dev/null +++ b/rstplib/README.files @@ -0,0 +1,58 @@ +This guide describes the list of the files of the project. +========================================================== + +There are two target binaries: mngr & bridge +o The first is a simplest tools to connect/disconnect + bridges and check their current connection. These its + functions are managed from command line simple language + and use the library libcli.a (see below). Beside it mngr + serves to transport BPDU messages between bridges; for + this purpose mngr uses the library libuid.a + The source code of the mngr: file mngr.c + +o The second is a simulation of virtual RSTP bridge. It + accepts two types of messages: UID_CNTRL & UID_BPDU (see + file uid.h). This program is linked with the same two + libraries libcli.a & libuid.a; beside it uses a system + independent librstp.a: implementation of Rapid Spanning + Tree (802.1w) - see below. + The source code of the bridge: files bridge.c,stp_cli.c, + stp_to.c + * bridge.c - simulates the main bridge behavior + * stp_cli.c - consists from command line functions + * stp_to.c - API, that librstp.a uses for its purposes. + The management communication between bridge and librstp.a + uses structures and definitions from the header uid_stp.h + +o libcli.a - library for command line features. It has only + one file cli.c, the API is described in the header cli.h. + +o libuid.a - the 'transport' library: the source code you + may find in the file uid_sock.c and in the two headers: + uid.h & uid_sock.h + +o (so far, so good) librstp.a - it is a heart of the project + Actually, it implements 802.1w state machines. Files + stpm.c - the RSTP instance (some reflection of Port0) + port.c - the RSTP port instance + portinfo.c - Port Information State Machine, 17.21 + rolesel.c - Port Role Selection State Machine, 17.22 + roletrns.c - Port Role Transition State Machine, 17.23 + sttrans.c - Port State Transition State Machine, 17.24 + topoch.c - Topology Change State Machine, 17.25 + migrate.c - Port Protocol Migration State Machine, 17.26 + transmit.c - Port Transmit State Machine 17.27 + pcost.c - Path Cost Resolution State Machine + edge.c - operEdge Port Resolution State Machine + p2p.c - operPointToPoit Resolution State Machine + statmch.c - generic state machine implementation + vector.c - Priority Vectors manipulations + times.c - Times manipulations + stp_in.c - API for calls from outside. + sttrans.c - API for calls from outside (dedicated for creation + deleting, starting & stopping the RSTP instance) less + relevant to the project. + + + + diff --git a/rstplib/TODO b/rstplib/TODO new file mode 100644 index 0000000..7037509 --- /dev/null +++ b/rstplib/TODO @@ -0,0 +1,22 @@ +This guide describes our plans. +============================== + +Volunteers are welcomed ! +========================== + +1. To support a full set of management features + from 14.8 (like timeSince_Topo_Change, 14.8.1.1.3.b) + +2. To send traps about topology changes (as call to stp_to.c) + +3. To rewrite edge.c for more exact correspondence with the + standard (now portEnabled variable is realized in 'dirty' way), + simulate MAC Operational and MAC Enabled. + +4. To support SNMP management via AgentX + +5. To move the project in he direction toward 802.1s (MSTP) + + + + diff --git a/rstplib/base.h b/rstplib/base.h new file mode 100644 index 0000000..d963714 --- /dev/null +++ b/rstplib/base.h @@ -0,0 +1,192 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Mutual RSTP definitions */ + +#ifndef _STP_BASE_H__ +#define _STP_BASE_H__ + +#include <stdlib.h> +#include <string.h> + +#ifdef DEBUG +# define STP_DBG 1 +#endif + +#ifdef __LINUX__ +# include <stddef.h> +# include <stdio.h> +# include <netinet/in.h> +# include "bitmap.h" +# include "uid_stp.h" +# define Print printf +#else +# include <psos.h> +# include "comdef.h" +# include "comdef.x" +# include "Bitmap/bitmap.h" +# include "Bitmap/bitmap.x" +# include "Ui/uid_stp.h" +#endif + +#ifndef INOUT +# define IN /* consider as comments near 'input' parameters */ +# define OUT /* consider as comments near 'output' parameters */ +# define INOUT /* consider as comments near 'input/output' parameters */ +#endif + +#ifndef Zero +# define Zero 0 +# define One 1 +#endif + +#ifndef Bool +# define Bool int +# define False 0 +# define True 1 +#endif + +#include "stp_bpdu.h" +#include "vector.h" +#include "times.h" + +#define RSTP_ERRORS { \ + CHOOSE(STP_OK), \ + CHOOSE(STP_Cannot_Find_Vlan), \ + CHOOSE(STP_Imlicite_Instance_Create_Failed), \ + CHOOSE(STP_Small_Bridge_Priority), \ + CHOOSE(STP_Large_Bridge_Priority), \ + CHOOSE(STP_Bridge_Priority_Not_A_Multiple_Of_4096), \ + CHOOSE(STP_Small_Hello_Time), \ + CHOOSE(STP_Large_Hello_Time), \ + CHOOSE(STP_Small_Max_Age), \ + CHOOSE(STP_Large_Max_Age), \ + CHOOSE(STP_Small_Forward_Delay), \ + CHOOSE(STP_Large_Forward_Delay), \ + CHOOSE(STP_Forward_Delay_And_Max_Age_Are_Inconsistent),\ + CHOOSE(STP_Hello_Time_And_Max_Age_Are_Inconsistent), \ + CHOOSE(STP_Small_Port_Priority), \ + CHOOSE(STP_Large_Port_Priority), \ + CHOOSE(STP_Port_Priority_Not_A_Multiple_Of_16), \ + CHOOSE(STP_Large_Port_PCost), \ + CHOOSE(STP_Vlan_Had_Not_Yet_Been_Created), \ + CHOOSE(STP_Port_Is_Absent_In_The_Vlan), \ + CHOOSE(STP_Big_len8023_Format), \ + CHOOSE(STP_Small_len8023_Format), \ + CHOOSE(STP_len8023_Format_Gt_Len), \ + CHOOSE(STP_Not_Proper_802_3_Packet), \ + CHOOSE(STP_Invalid_Protocol), \ + CHOOSE(STP_Invalid_Version), \ + CHOOSE(STP_Had_Not_Yet_Been_Enabled_On_The_Vlan), \ + CHOOSE(STP_Cannot_Create_Instance_For_Vlan), \ + CHOOSE(STP_Cannot_Create_Instance_For_Port), \ + CHOOSE(STP_Invalid_Bridge_Priority), \ + CHOOSE(STP_There_Are_No_Ports), \ + CHOOSE(STP_Cannot_Compute_Bridge_Prio), \ + CHOOSE(STP_Another_Error), \ + CHOOSE(STP_Nothing_To_Do), \ + CHOOSE(STP_LAST_DUMMY), \ +} + +#define CHOOSE(a) a +typedef enum RSTP_ERRORS RSTP_ERRORS_T; +#undef CHOOSE + +#ifndef __LINUX__ +extern char* strdup (const char *s); + +extern USHORT Ntohs (USHORT n); +extern ULONG Htonl (ULONG h); +extern USHORT Htons (USHORT h); +extern ULONG Ntohl (ULONG n); + +#define htonl Htonl +#define htons Htons +#define ntohl Ntohl +#define ntohs Ntohs + +#endif + +#ifdef __LINUX__ +#define STP_FATAL(TXT, MSG, EXCOD) \ + {stp_trace ("FATAL:%s failed: %s:%d", TXT, MSG, EXCOD); \ + exit (EXCOD);} +#else +#define STP_FATAL(TXT, MSG, EXCOD) \ + Print ("FATAL: %s code %s:%d\n", TXT, MSG, EXCOD) +#endif + +#define STP_MALLOC(PTR, TYPE, MSG) \ + { \ + PTR = (TYPE*) calloc (1, sizeof (TYPE)); \ + if (! PTR) { \ + STP_FATAL("malloc", MSG, -6); \ + } \ + } + +#define STP_FREE(PTR, MSG) \ + { \ + if (! PTR) { \ + STP_FATAL("free", MSG, -66); \ + } \ + free (PTR); \ + PTR = NULL; \ + } + +#define STP_STRDUP(PTR, SRC, MSG) \ + { \ + PTR = strdup (SRC); \ + if (! PTR) { \ + STP_FATAL("strdup", MSG, -7); \ + } \ + } + +#define STP_NEW_IN_LIST(WHAT, TYPE, LIST, MSG) \ + { \ + STP_MALLOC(WHAT, TYPE, MSG); \ + WHAT->next = LIST; \ + LIST = WHAT; \ + } + +typedef enum { + RSTP_PORT_EN_T, + RSTP_PORT_DIS_T, + RSTP_PORT_SPEED_T, + RSTP_PORT_DPLEX_T, + RSTP_PORT_RX_T, + RSTP_PORT_TIME_T, + RSTP_PORT_MNGR_T, + RSTP_EVENT_LAST_DUMMY + +} RSTP_EVENT_T; + +/* for debug trace messages */ + +#ifdef __LINUX__USE_PRINTF_FOR_STRACE +extern char* sprint_time_stump (void); +#define stp_trace(F, B...) printf("%s:" F "\n", sprint_time_stump(), ##B) +#else +extern void stp_trace (const char* fmt, ...); +#endif + +#endif /* _STP_BASE_H__ */ + diff --git a/rstplib/bitmap.h b/rstplib/bitmap.h new file mode 100644 index 0000000..955e188 --- /dev/null +++ b/rstplib/bitmap.h @@ -0,0 +1,82 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#ifndef __BITMAP_H +#define __BITMAP_H + +#define NUMBER_OF_PORTS 4 + +typedef struct tagBITMAP +{ + unsigned long part0; /* Least Significant part */ +} BITMAP_T; + +#define BitmapClear(BitmapPtr) \ + { (BitmapPtr)->part0 = 0; } + +#define BitmapSetAllBits(BitmapPtr) \ + { (BitmapPtr)->part0 = 0xF; } + +#define BitmapClearBits(BitmapPtr,BitmapBitsPtr) \ + { (BitmapPtr)->part0 &= ~((BitmapBitsPtr)->part0); } + +#define BitmapSetBits(BitmapPtr,BitmapBitsPtr) \ + { (BitmapPtr)->part0 |= ((BitmapBitsPtr)->part0); } + +#define BitmapOr(ResultPtr,BitmapPtr1,BitmapPtr2) \ + { (ResultPtr)->part0 = (BitmapPtr1)->part0 | (BitmapPtr2)->part0; } + +#define BitmapAnd(ResultPtr,BitmapPtr1,BitmapPtr2) \ + { (ResultPtr)->part0 = (BitmapPtr1)->part0 & (BitmapPtr2)->part0; } + +#define BitmapNot(ResultPtr,BitmapPtr) \ + { (ResultPtr)->part0 = ~((BitmapPtr)->part0); } + + +/* Return zero if identical */ +#define BitmapCmp(BitmapPtr1,BitmapPtr2) \ + ((BitmapPtr1)->part0 != (BitmapPtr2)->part0) + +#define BitmapIsZero(BitmapPtr) \ + (!(BitmapPtr)->part0) + +#define BitmapIsAllOnes(BitmapPtr) \ + ((BitmapPtr)->part0 == 0xF) + +#define BitmapGetBit(BitmapPtr,Bit) \ + ((BitmapPtr)->part0 & (1 << (Bit))) + +/* Bit range [0 .. 63] */ +#define BitmapSetBit(BitmapPtr,Bit) \ + {(BitmapPtr)->part0 |= (1 << (Bit)); } + +#define BitmapClearBit(BitmapPtr,Bit) \ + (BitmapPtr)->part0 &= ~(1 << (Bit)); + +#define BitmapCopy(BitmapDstPtr,BitmapSrcPtr) \ + { (BitmapDstPtr)->part0 = (BitmapSrcPtr)->part0; } + +#define BitmapXor(ResultPtr,BitmapPtr1,BitmapPtr2) \ + { (ResultPtr)->part0 = (BitmapPtr1)->part0 ^ (BitmapPtr2)->part0; } + +#endif /* __BITMAP_H */ + diff --git a/rstplib/bridge.c b/rstplib/bridge.c new file mode 100644 index 0000000..bcbda71 --- /dev/null +++ b/rstplib/bridge.c @@ -0,0 +1,307 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/types.h> +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include <readline/readline.h> + +#include "cli.h" +#include "uid.h" +#include "stp_cli.h" + +#include "base.h" +#include "bitmap.h" +#include "uid_stp.h" +#include "stp_in.h" + +long my_pid = 0; +BITMAP_T enabled_ports; +UID_SOCKET_T uid_socket; + +int bridge_tx_bpdu (int port_index, unsigned char *bpdu, size_t bpdu_len) +{ + UID_MSG_T msg; + + msg.header.sender_pid = my_pid; + msg.header.cmd_type = UID_BPDU; + msg.header.source_port = port_index; + msg.header.body_len = bpdu_len; + memcpy (&msg.body, bpdu, bpdu_len); + UiD_SocketSendto (&uid_socket, &msg, sizeof (UID_MSG_T)); + return 0; +} + +int bridge_start (void) +{ + BITMAP_T ports; + UID_MSG_T msg; + UID_STP_CFG_T uid_cfg; + register int iii; + + //rl_callback_handler_install (get_prompt (), rl_read_cli); + + if (0 != UiD_SocketInit (&uid_socket, UID_REPL_PATH, UID_BIND_AS_CLIENT)) { + printf ("FATAL: can't init the connection\n"); + exit (-3); + } + + /* send HANDSHAKE */ + msg.header.sender_pid = my_pid; + msg.header.cmd_type = UID_CNTRL; + msg.body.cntrl.cmd = UID_BRIDGE_HANDSHAKE; + msg.body.cntrl.param1 = NUMBER_OF_PORTS; + iii = UiD_SocketSendto (&uid_socket, &msg, sizeof (UID_MSG_T)); + if (iii < 0) { + printf ("can't send HANDSHAKE: %s\n", strerror(errno)); + printf ("May be 'mngr' is not alive ? :(\n"); + return (-4); + } + + stp_cli_init (); + + STP_IN_init (NUMBER_OF_PORTS); + BitmapClear(&enabled_ports); + BitmapClear(&ports); + for (iii = 1; iii <= NUMBER_OF_PORTS; iii++) { + BitmapSetBit(&ports, iii - 1); + } + + uid_cfg.field_mask = BR_CFG_STATE; + uid_cfg.stp_enabled = STP_ENABLED; + snprintf (uid_cfg.vlan_name, NAME_LEN - 1, "B%ld", (long) my_pid); + iii = STP_IN_stpm_set_cfg (0, &ports, &uid_cfg); + if (STP_OK != iii) { + printf ("FATAL: can't enable:%s\n", + STP_IN_get_error_explanation (iii)); + return (-1); + } + return 0; +} + +void bridge_shutdown (void) +{ + UID_MSG_T msg; + int rc; + + /* send SHUTDOWN */ + msg.header.sender_pid = my_pid; + msg.header.cmd_type = UID_CNTRL; + msg.body.cntrl.cmd = UID_BRIDGE_SHUTDOWN; + UiD_SocketSendto (&uid_socket, &msg, sizeof (UID_MSG_T)); + + rc = STP_IN_stpm_delete (0); + if (STP_OK != rc) { + printf ("FATAL: can't delete:%s\n", + STP_IN_get_error_explanation (rc)); + exit (1); + } +} + +char *get_prompt (void) +{ + static char prompt[MAX_CLI_PROMT]; + snprintf (prompt, MAX_CLI_PROMT - 1, "%s B%ld > ", UT_sprint_time_stamp(), my_pid); + return prompt; +} + +int bridge_control (int port_index, + UID_CNTRL_BODY_T* cntrl) +{ + switch (cntrl->cmd) { + case UID_PORT_CONNECT: + printf ("connected port p%02d\n", port_index); + BitmapSetBit(&enabled_ports, port_index - 1); + STP_IN_enable_port (port_index, True); + break; + case UID_PORT_DISCONNECT: + printf ("disconnected port p%02d\n", port_index); + BitmapClearBit(&enabled_ports, port_index - 1); + STP_IN_enable_port (port_index, False); + break; + case UID_BRIDGE_SHUTDOWN: + printf ("shutdown from manager :(\n"); + return 1; + default: + printf ("Unknown control command <%d> for port %d\n", + cntrl->cmd, port_index); + } + return 0; +} + +int bridge_rx_bpdu (UID_MSG_T* msg, size_t msgsize) +{ + register int port_index; + + if (I_am_a_stupid_hub) { // flooding + msg->header.sender_pid = my_pid; + for (port_index = 1; port_index <= NUMBER_OF_PORTS; port_index++) { + if (BitmapGetBit (&enabled_ports, (port_index - 1)) && + msg->header.destination_port != port_index) { + msg->header.source_port = port_index; + UiD_SocketSendto (&uid_socket, msg, msgsize); + } + } + } else { + STP_IN_rx_bpdu (0, msg->header.destination_port, + (BPDU_T*) (msg->body.bpdu + sizeof (MAC_HEADER_T)), + msg->header.body_len - sizeof (MAC_HEADER_T)); + } + return 0; +} + +char read_uid (UID_SOCKET_T* uid_sock) +{ + char buff[MAX_UID_MSG_SIZE]; + UID_MSG_T* msg; + size_t msgsize; + int rc; + + msgsize = UiD_SocketRecvfrom (uid_sock, buff, MAX_UID_MSG_SIZE, 0); + if (msgsize <= 0) { + printf ("Something wrong in UIF ?\n"); + return 0; + } + + msg = (UID_MSG_T*) buff; + switch (msg->header.cmd_type) { + case UID_CNTRL: + rc = bridge_control (msg->header.destination_port, + &msg->body.cntrl); + break; + case UID_BPDU: + rc = bridge_rx_bpdu (msg, msgsize); + break; + default: + printf ("Unknown message type %d\n", (int) msg->header.cmd_type); + rc = 0; + } + + return rc; +} + +char shutdown_flag = 0; + +int main_loop (void) +{ + fd_set readfds; + struct timeval tv; + struct timeval now, earliest; + int rc, numfds, sock, kkk; + + + sock = GET_FILE_DESCRIPTOR(&uid_socket); + + gettimeofday (&earliest, NULL); + earliest.tv_sec++; + + do { + numfds = -1; + FD_ZERO(&readfds); + + kkk = 0; /* stdin for commands */ + FD_SET(kkk, &readfds); + if (kkk > numfds) numfds = kkk; + + FD_SET(sock, &readfds); + if (sock > numfds) numfds = sock; + + if (numfds < 0) + numfds = 0; + else + numfds++; + + gettimeofday (&now, 0); + tv.tv_usec = 0; + tv.tv_sec = 0; + + if (now.tv_sec < earliest.tv_sec) { /* we must wait more than 1 sec. */ + tv.tv_sec = 1; + tv.tv_usec = 0; + } else if (now.tv_sec == earliest.tv_sec) { + if (now.tv_usec < earliest.tv_usec) { + if (earliest.tv_usec < now.tv_usec) + tv.tv_usec = 0; + else + tv.tv_usec = earliest.tv_usec - now.tv_usec; + } + } + + //printf ("wait %ld-%ld\n", (long) tv.tv_sec, (long) tv.tv_usec); + rc = select (numfds, &readfds, NULL, NULL, &tv); + if (rc < 0) { // Error + if (EINTR == errno) continue; // don't break + printf ("FATAL_MODE:select failed: %s\n", strerror(errno)); + return -2; + } + + if (! rc) { // Timeout expired + STP_IN_one_second (); + gettimeofday (&earliest, NULL); + //printf ("tick %ld-%ld\n", (long) earliest.tv_sec - 1005042800L, (long) earliest.tv_usec); + + earliest.tv_sec++; + continue; + } + + if (FD_ISSET(0, &readfds)) { + rl_callback_read_char (); + } + + if (FD_ISSET(sock, &readfds)) { + shutdown_flag |= read_uid (&uid_socket); + } + + } while(! shutdown_flag); + return 0; +} + +int main (int argc, char** argv) +{ + rl_init (); + + my_pid = getpid(); + printf ("my pid: %ld\n", my_pid); + + if (0 == bridge_start ()) { + main_loop (); + } + + bridge_shutdown (); + + rl_shutdown (); + + return 0; +} + +char* sprint_time_stump (void) +{ + return UT_sprint_time_stamp(); +} + diff --git a/rstplib/choose.h b/rstplib/choose.h new file mode 100644 index 0000000..7f614fa --- /dev/null +++ b/rstplib/choose.h @@ -0,0 +1,42 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#ifndef _STP_CHOOSE_H__ +#define _STP_CHOOSE_H__ + +/* State machines states & debug tools. Sorry, if these are no readable enogth :( */ + +#define CHOOSE(a) a +typedef enum STATES THE_STATE_T; +#undef CHOOSE + +char * GET_STATE_NAME (int state) +{ +#define CHOOSE(a) #a +static char *state_names[] = STATES; +#undef CHOOSE + + if (BEGIN == state) return "Begin"; + return state_names[state]; +} + +#endif /* _STP_CHOOSE_H__ */ diff --git a/rstplib/cli.c b/rstplib/cli.c new file mode 100644 index 0000000..a8e844e --- /dev/null +++ b/rstplib/cli.c @@ -0,0 +1,511 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/types.h> +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include <readline/readline.h> +#include <readline/history.h> + +#include "cli.h" + +static CMD_DSCR_T* TheList = 0; + +static int +cli_parse_parms (const char* line, int skip_words, char** argv) +{ + static char pool[MAX_CLI_BUFF]; + register char* ptr; + register int argc = 0, iii; + + ptr = strchr (line, '\n'); + if (ptr) *ptr = '\0'; + memcpy (pool, line, MAX_CLI_BUFF); + pool[MAX_CLI_BUFF - 1] = '\0'; + + for (iii = 0; iii < MAXPARAMNUM; iii++) { + ptr = strtok (iii ? NULL : pool, "\t\n "); + if (! ptr) break; + if (skip_words-- <= 0) { + /* printf ("\targv[%d]='%s' skip_words=%d\n", argc, ptr, skip_words); */ + argv[argc++] = ptr; + } else { + /* printf ("\tskip '%s' skip_words now %d\n", ptr, skip_words); */ + } + } + + return argc; +} + +int cli_count_words (char* line) +{ + static char pool[MAX_CLI_BUFF]; + register char* ptr = NULL; + register int cnt; + + ptr = strchr (line, '\n'); + if (ptr) *ptr = '\0'; + strncpy (pool, line, MAX_CLI_BUFF); + pool[MAX_CLI_BUFF - 1] = '\0'; + + for (cnt = 0;; cnt++) { + ptr = strtok (cnt ? NULL : pool, "\t\n "); + if (! ptr) { + break; + } + } + + return cnt - 1; +} + +void cli_register_language (const CMD_DSCR_T* cmd_list) +{ + TheList = ( CMD_DSCR_T*) cmd_list; +} + +static int help_on_param (int iii, CMD_PAR_DSCR_T* par, int cry_on_empty) +{ + register int kkk; + + if (! par->param_help) { + if (cry_on_empty) + printf ("absent parameter #%d\n", iii); + return 1; + } + + printf (" arg%2d: %s", iii + 1, par->param_help); + switch (par->param_type) { + case CMD_PAR_NUMBER: + printf ("; integer in [%ld, %ld]",par->number_limits.min,par->number_limits.max); + break; + case CMD_PAR_STRING: + break; + case CMD_PAR_BOOL_YN: + case CMD_PAR_ENUM: + printf ("\n"); + for (kkk = 0; par->string_selector[kkk].string_value; kkk++) { + printf (" %-20s\t%s\n", + par->string_selector[kkk].string_value, + par->string_selector[kkk].string_help); + } + break; + } + + if (par->default_value) { + printf (" default '%s'", par->default_value); + } + printf (" \n"); + return 0; +} + +void help_on_command (CMD_DSCR_T* reg, char brief) +{ + register CMD_PAR_DSCR_T* par; + register int iii; + + if (brief) { + printf ("%-20s %s\n", reg->cmd_name, reg->cmd_help); + return; + } + printf ("%s: %s\n", reg->cmd_name, reg->cmd_help); + for (iii = 0; iii < 2 + strlen (reg->cmd_name) + strlen (reg->cmd_help); iii++) + printf ("-"); + + printf ("\n"); + if (reg->param->param_help) + printf (" arguments:\n ----------\n"); + for (iii = 0, par = reg->param; ; iii++, par++) { + if (! par->param_help) break; + help_on_param (iii + 1, par, 0); + } + if (reg->param->param_help) + printf ("\n"); +} + +static int cli_dummy (int argc, char** argv) +{ + return 0; +} + +static CMD_DSCR_T stdcmd[] = { + THE_COMMAND("exit", "'shutdown'") + THE_FUNC(cli_dummy) + + THE_COMMAND("?", "help") + THE_FUNC(cli_dummy) + + END_OF_LANG +}; + +static char* get_commnad_name (int list_index) +{ + register CMD_DSCR_T* reg; + + if (list_index < 2 && list_index >= 0) { + return stdcmd[list_index].cmd_name; + } + + list_index -= 2; + reg = TheList + list_index; + if (reg->cmd_name && *reg->cmd_name) + return reg->cmd_name; + return NULL; +} + +static int find_command (const char* line, char forHelp, CMD_DSCR_T **reg_ptr) +{ + register CMD_DSCR_T* reg; + register int iii = 0; + + for (reg = stdcmd; reg->cmd_name; reg++) { + if (! strncasecmp (reg->cmd_name, line, strlen (line))) { + *reg_ptr = reg; iii++; + if (forHelp) + help_on_command (reg, 1); + } + } + + for (reg = TheList; reg->cmd_name; reg++) { + if (! strncasecmp (reg->cmd_name, line, strlen (line))) { + *reg_ptr = reg; iii++; + if (forHelp) + help_on_command (reg, 1); + } + } + + return iii; +} + +void usage (void) +{ + CMD_DSCR_T* reg; + + if (! TheList) { + printf ("Sorry, command list hasn't been registered\n"); + return; + } + + printf ("List of possible commands:\n"); + for (reg = TheList; reg->cmd_name; reg++) { + help_on_command (reg, 1); + } + + printf ("'standard' commands:\n"); + for (reg = stdcmd; reg->cmd_name; reg++) { + help_on_command (reg, 1); + } +} + +void cli_debug_dump_args (char* title, int argc, char** argv) +{ + int iii; + printf ("in %s argc=%d\n", title, argc); + for (iii = 0; iii < argc; iii++) + printf ("\targv[%d]='%s'\n", iii, argv[iii]); + printf ("\n"); +} + + +static int count_command_paramms (CMD_DSCR_T* reg) +{ + register int iii; + register CMD_PAR_DSCR_T* par; + + for (iii = 0, par = reg->param; ; iii++, par++) { + if (! par->param_help) break; + } + return iii; +} + +static void +cli_help_brosed_line (int argc, char** argv, const char* line) +{ + char pool[MAX_CLI_BUFF]; + CMD_DSCR_T* reg; + int iii, nf; + + printf ("\n"); +#if 0 + cli_debug_dump_args ("cli_help_brosed_line", argc, argv); +#endif + + memset (pool, 0, MAX_CLI_BUFF); + for (iii = 0; iii < argc; iii++) { + if (iii) strcat (pool, " "); + strcat (pool, argv[iii]); + nf = find_command (pool, 0, ®); + if (1 == nf) { + nf = count_command_paramms (reg); + iii++; +#if 0 + printf ("iii=%d argc=%d nf=%d\n", iii, argc, nf); +#endif + nf = strlen (line); + if (nf && ' ' == line[nf - 1]) + argc++; + if (iii < argc) { + iii = argc - iii - 1; + if (! help_on_param (iii + 1, reg->param + iii, 1)) { + return; + } + } + help_on_command (reg, 0); + return; + } else if (! nf) { + printf ("\nunknown <%s>\n", pool); + usage (); + return; + } + } + find_command (pool, 1, ®); +} + +void cli_help (int argc, char** argv, const char* line) +{ +#if 0 + cli_debug_dump_args ("cli_help", argc, argv); +#endif + if (argc > 1) + cli_help_brosed_line (argc - 1, argv + 1, line); + else + usage (); +} + +static void cli_set_defaults (CMD_DSCR_T* reg, char** argv) +{ + register int iii; + register CMD_PAR_DSCR_T* par; + + for (iii = 0, par = reg->param; ; iii++, par++) { + if (! par->param_help) break; + argv[iii + 1] = par->default_value; + } +} + +static int cli_call_callback (CMD_DSCR_T* reg, const char* line, int* argc, char** argv) +{ + int cnt; + + if (reg->clbk) { + cnt = cli_count_words (reg->cmd_name); + /* printf ("cli_count_words returned %d\n", cnt); */ + cli_set_defaults (reg, argv); + *argc = cli_parse_parms (line, cnt, argv); + return (*reg->clbk) (*argc, argv); + } + printf ("<Empty command !>\n"); + return 0; +} + +int cli_execute_command (const char* line) +{ + CMD_DSCR_T* reg; + int argc, nf; + char *argv[MAXPARAMNUM]; + + if ('\n' == *line || ! *line) return 0; + + /* check "common commands" */ + if ('q' == *line || ! strncasecmp ("ex", line, 2)) { + return 4; + } + + if ('?' == *line || 'h' == *line) { + argc = cli_parse_parms (line, 0, argv); + cli_help (argc, argv, line); + return 0; + } + + if (! TheList) { + printf ("Sorry, command list hasn't been registered\n"); + return -11; + } + + for (reg = TheList; reg->cmd_name; reg++) { + if (! strncasecmp (reg->cmd_name, line, strlen (reg->cmd_name))) { + return cli_call_callback (reg, line, &argc, argv); + } + } + + nf = find_command (line, 0, ®); + if (1 == nf) + return cli_call_callback (reg, line, &argc, argv); + + printf ("unknown command: <%s>", line); + usage (); + return 0; +} + +extern char shutdown_flag; + +char read_cli (void) +{ + if (!rl_line_buffer) { + return 1; + } + + if (*rl_line_buffer != 0) { + add_history (rl_line_buffer); + /** printf ("\n try to call <%s>\n", rl_line_buffer); **/ + + if (0 != cli_execute_command (rl_line_buffer)) { + printf("Goodbye, I'm a gonner\n"); + rl_callback_handler_remove (); + return 2; + } + } + + return 0; +} + +#ifdef OLD_READLINE +void rl_read_cli (void) +#else +void rl_read_cli (char *str) +#endif +{ + shutdown_flag |= read_cli (); + rl_callback_handler_install (get_prompt (), rl_read_cli); +} + +char* UT_sprint_time_stamp (void) +{ + time_t clock; + struct tm *local_tm; + static char time_str[20]; + + time(&clock); + local_tm = localtime (&clock); + strftime(time_str, 20 - 1, "%H:%M:%S", local_tm); + return time_str; +} + +int complete_status; + +/* To disable readline's filename completion */ +#ifdef OLD_READLINE +int cli_completion_entry_fucntion (int ignore, int invoking_key) +{ return 0; } +#else +char* cli_completion_entry_fucntion (const char *str, int ignore) +{ return NULL; } +#endif + +#ifdef OLD_READLINE +char* command_generator (char* text, int state) +#else +char* command_generator (const char* text, int state) +#endif +{ + static int list_index, len; + char *name, dlen; + +/**** + printf (" state=%d list_index=%d rl_line_buffer'%s' text'%s'\n", + state, list_index, rl_line_buffer, text); +****/ + + dlen = strlen (rl_line_buffer) - strlen (text); + if (! state) { + list_index = 0; + len = strlen (rl_line_buffer); +/**** + printf ("\tlen=%d text<%s>\n", len, text); +****/ + } + + for (;;) { + name = get_commnad_name (list_index); + if (! name) break; + list_index++; + if (! strncmp (rl_line_buffer, name, len)) { +/**** + printf (" find <%s> => return '%s'\n", name, name + dlen); +****/ + return strdup (name + dlen); + } + } + + return ((char *)NULL); +} + +#ifdef OLD_READLINE +int cli_inline_help (void) +#else +int cli_inline_help (int a, int b) +#endif +{ + int argc; + char *argv[MAXPARAMNUM]; + + if (! *rl_line_buffer) { + usage (); + } else { + argc = cli_parse_parms (rl_line_buffer, 0, argv); + cli_help_brosed_line (argc, argv, (const char*) rl_line_buffer); + } + + rl_on_new_line(); + return 0; + +} + +char ** +cli_private_completion (char *text, int start, int end) +{ + char **matches = NULL; + +#ifdef OLD_READLINE + matches = completion_matches (text, command_generator); +#else + matches = rl_completion_matches (text, command_generator); +#endif + + return matches; +} + +void rl_init () +{ + /* disable completion */ +#if 0 + rl_bind_key ('\t', rl_insert); +#else + rl_callback_handler_install (get_prompt (), rl_read_cli); + rl_bind_key ('?', cli_inline_help); + rl_completion_entry_function = cli_completion_entry_fucntion; + rl_attempted_completion_function = (CPPFunction *)cli_private_completion; + rl_completion_append_character = '\0'; +#endif +} + +void rl_shutdown () +{ + rl_initialize (); +} + diff --git a/rstplib/cli.h b/rstplib/cli.h new file mode 100644 index 0000000..75c19be --- /dev/null +++ b/rstplib/cli.h @@ -0,0 +1,93 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#ifndef _CLI_API__ +#define _CLI_API__ + +#define MAX_CLI_BUFF 80 +#define MAXPARAMNUM 6 +#define MAXPARAMLEN 40 +#define MAX_CLI_PROMT 24 +#define MAX_SELECTOR 12 + +typedef int CLI_CMD_CLBK (int argc, char** argv); + +typedef enum { + CMD_PAR_NUMBER, + CMD_PAR_STRING, + CMD_PAR_BOOL_YN, + CMD_PAR_ENUM, +} CMD_PARAM_TYPE_T; + +typedef struct cmd_par_number_limits_s { + unsigned long min; + unsigned long max; +} CMD_PAR_LIMITS; + +typedef struct cmd_par_string_selector_s { + char* string_value; + char* string_help; +} CMD_PAR_SELECTOR; + +typedef struct cmd_par_dscr_s { + char* param_help; + CMD_PARAM_TYPE_T param_type; + CMD_PAR_LIMITS number_limits; + CMD_PAR_SELECTOR string_selector[MAX_SELECTOR]; + char* default_value; +} CMD_PAR_DSCR_T; + +typedef struct cmd_dscr_s { + char* cmd_name; + char* cmd_help; + CMD_PAR_DSCR_T param[MAXPARAMNUM]; + CLI_CMD_CLBK* clbk; +} CMD_DSCR_T; + +#define THE_COMMAND(x, y) {x, y, { +#define PARAM_NUMBER(x,zmin,zmax,def) {x,CMD_PAR_NUMBER, {zmin, zmax}, {}, def}, +#define PARAM_STRING(x, def) {x,CMD_PAR_STRING, {}, {}, def}, +#define PARAM_ENUM(x) {x,CMD_PAR_ENUM, {}, { +#define PARAM_ENUM_SEL(x, y) {x, y}, +#define PARAM_ENUM_DEFAULT(def) }, def}, +#define PARAM_BOOL(x,yesd,nod,def) {x, CMD_PAR_ENUM, {}, {{"y",yesd},{"n",nod}},def} +#define THE_FUNC(x) }, &x}, +#define END_OF_LANG {NULL,NULL} + +char *get_prompt (void); /* this function not from the lib ! */ + +void cli_debug_dump_args (char* title, int argc, char** argv); + +void cli_register_language (const CMD_DSCR_T* cmd_list); +void usage (void); +int cli_execute_command (const char* line); +#ifdef OLD_READLINE +void rl_read_cli (void); +#else +void rl_read_cli (char *); +#endif +void rl_init (void); +void rl_shutdown (void); +char* UT_sprint_time_stamp (void); + +#endif /* _CLI_API__ */ + diff --git a/rstplib/edge.c b/rstplib/edge.c new file mode 100644 index 0000000..db183a4 --- /dev/null +++ b/rstplib/edge.c @@ -0,0 +1,114 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Note: this state mashine distinkts from described in P802.1t Clause 18. */ +/* I am ready to discuss it */ + +#include "base.h" +#include "stpm.h" + +#define STATES { \ + CHOOSE(DISABLED), \ + CHOOSE(DETECTED), \ + CHOOSE(DELEAYED), \ + CHOOSE(RESOLVED), \ +} + +#define GET_STATE_NAME STP_edge_get_state_name +#include "choose.h" + +#define DEFAULT_LINK_DELAY 3 + +void +STP_edge_enter_state (STATE_MACH_T *s) +{ + register PORT_T *port = s->owner.port; + + switch (s->State) { + case BEGIN: + break; + case DISABLED: + port->operEdge = port->adminEdge; + port->wasInitBpdu = False; + port->lnkWhile = 0; + port->portEnabled = False; + break; + case DETECTED: + port->portEnabled = True; + port->lnkWhile = port->LinkDelay; + port->operEdge = False; + break; + case DELEAYED: + break; + case RESOLVED: + if (! port->wasInitBpdu) { + port->operEdge = port->adminEdge; + } + break; + } +} + +Bool +STP_edge_check_conditions (STATE_MACH_T *s) +{ + register PORT_T *port = s->owner.port; + + switch (s->State) { + case BEGIN: + return STP_hop_2_state (s, DISABLED); + case DISABLED: + if (port->adminEnable) { + return STP_hop_2_state (s, DETECTED); + } + break; + case DETECTED: + return STP_hop_2_state (s, DELEAYED); + case DELEAYED: + if (port->wasInitBpdu) { +#ifdef STP_DBG + if (s->debug) + stp_trace ("port %s 'edge' resolved by BPDU", port->port_name); +#endif + return STP_hop_2_state (s, RESOLVED); + } + + if (! port->lnkWhile) { +#ifdef STP_DBG + if (s->debug) + stp_trace ("port %s 'edge' resolved by timer", port->port_name); +#endif + return STP_hop_2_state (s, RESOLVED); + } + + if (! port->adminEnable) { + return STP_hop_2_state (s, DISABLED); + } + break; + case RESOLVED: + if (! port->adminEnable) { + return STP_hop_2_state (s, DISABLED); + } + break; + } + return False; +} + diff --git a/rstplib/edge.h b/rstplib/edge.h new file mode 100644 index 0000000..e41264d --- /dev/null +++ b/rstplib/edge.h @@ -0,0 +1,38 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Note: this state mashine distinkts from described in P802.1t Clause 18. */ +/* I am ready to discuss it */ + +#ifndef _STP_EDGE_H__ +#define _STP_EDGE_H__ + +void +STP_edge_enter_state (STATE_MACH_T* s); + +Bool +STP_edge_check_conditions (STATE_MACH_T* s); + +char* +STP_edge_get_state_name (int state); + +#endif /* _STP_EDGE_H__ */ diff --git a/rstplib/migrate.c b/rstplib/migrate.c new file mode 100644 index 0000000..c11c017 --- /dev/null +++ b/rstplib/migrate.c @@ -0,0 +1,119 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Protocol Migration state machine : 17.26 */ + +#include "base.h" +#include "stpm.h" + +#define STATES { \ + CHOOSE(INIT), \ + CHOOSE(SEND_RSTP), \ + CHOOSE(SENDING_RSTP), \ + CHOOSE(SEND_STP), \ + CHOOSE(SENDING_STP), \ +} + +#define GET_STATE_NAME STP_migrate_get_state_name +#include "choose.h" + +#define MigrateTime 3 /* 17,16.4 */ + +void +STP_migrate_enter_state (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + case INIT: + port->initPm = True; + port->mcheck = False; + break; + case SEND_RSTP: + port->mdelayWhile = MigrateTime; + port->mcheck = port->initPm = False; + port->sendRSTP = True; + break; + case SENDING_RSTP: + port->rcvdRSTP = port->rcvdSTP = False; + break; + case SEND_STP: + port->mdelayWhile = MigrateTime; + port->sendRSTP = False; + port->initPm = False; + break; + case SENDING_STP: + port->rcvdRSTP = port->rcvdSTP = False; + break; + } +} + +Bool +STP_migrate_check_conditions (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + if ((!port->portEnabled && !port->initPm) || BEGIN == this->State) + return STP_hop_2_state (this, INIT); + + switch (this->State) { + case INIT: + if (port->portEnabled) { + return STP_hop_2_state (this, (port->owner->ForceVersion >= 2) ? + SEND_RSTP : SEND_STP); + } + break; + case SEND_RSTP: + return STP_hop_2_state (this, SENDING_RSTP); + case SENDING_RSTP: + if (port->mcheck) + return STP_hop_2_state (this, SEND_RSTP); + if (port->mdelayWhile && + (port->rcvdSTP || port->rcvdRSTP)) { + return STP_hop_2_state (this, SENDING_RSTP); + } + + if (!port->mdelayWhile && port->rcvdSTP) { + return STP_hop_2_state (this, SEND_STP); + } + + if (port->owner->ForceVersion < 2) { + return STP_hop_2_state (this, SEND_STP); + } + + break; + case SEND_STP: + return STP_hop_2_state (this, SENDING_STP); + case SENDING_STP: + if (port->mcheck) + return STP_hop_2_state (this, SEND_RSTP); + if (port->mdelayWhile && + (port->rcvdSTP || port->rcvdRSTP)) + return STP_hop_2_state (this, SENDING_STP); + if (!port->mdelayWhile && port->rcvdRSTP) + return STP_hop_2_state (this, SEND_RSTP); + break; + } + return False; +} + diff --git a/rstplib/migrate.h b/rstplib/migrate.h new file mode 100644 index 0000000..0c38606 --- /dev/null +++ b/rstplib/migrate.h @@ -0,0 +1,37 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Protocol Migration state machine : 17.26 */ + +#ifndef _STP_MIGRATE_H__ +#define _STP_MIGRATE_H__ + +void +STP_migrate_enter_state (STATE_MACH_T* s); + +Bool +STP_migrate_check_conditions (STATE_MACH_T* s); + +char* +STP_migrate_get_state_name (int state); + +#endif /* _STP_MIGRATE_H__ */ diff --git a/rstplib/mngr.c b/rstplib/mngr.c new file mode 100644 index 0000000..021dc54 --- /dev/null +++ b/rstplib/mngr.c @@ -0,0 +1,559 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/types.h> +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include <readline/readline.h> + +#include "cli.h" +#include "bitmap.h" +#include "uid.h" + +UID_SOCKET_T main_sock; + +typedef struct port_s { + int port; + struct bridge_s *bridge_partner; + int port_partner; +} PORT_T; + +typedef struct bridge_s { + long pid; + long number_of_ports; + PORT_T* ports; + UID_SOCKET_T sock; + struct bridge_s* next; +} BRIDGE_T; + +static BRIDGE_T* br_lst = 0; + +int disconnect_port (PORT_T* port, char reset_myself) +{ + UID_MSG_T msg; + if (! port->bridge_partner) { + printf ("can't disconnect from port p%02d: it has not a partner\n", + port->port_partner); + return -1; + } + + printf ("disconnect from port p%02d for bridge B%ld\n", + port->port_partner, port->bridge_partner->pid); + + msg.header.sender_pid = getpid (); + msg.header.cmd_type = UID_CNTRL; + msg.body.cntrl.cmd = UID_PORT_DISCONNECT; + + msg.header.destination_port = port->port_partner; + UiD_SocketSendto (&port->bridge_partner->sock, &msg, sizeof (UID_MSG_T)); + + if (reset_myself) + port->bridge_partner = NULL; + else { + port = port->bridge_partner->ports + port->port_partner - 1; + port->bridge_partner = NULL; + } + + return 0; +} + +int register_bridge (UID_MSG_T* msg, UID_SOCKET_T* socket_4_reply) +{ + register BRIDGE_T* newbr; + + /* check if such bridge has just been registered */ + for (newbr = br_lst; newbr; newbr = newbr->next) + if (newbr->pid == msg->header.sender_pid) { + printf ("Sorry, such bridge has just been registered\n"); + /* TBT: may be send him SHUTDOWN ? */ + } + + newbr = (BRIDGE_T*) malloc (sizeof (BRIDGE_T)); + if (! newbr) { + printf ("Sorry, there is no memory for it :(\n"); + return 0; + } + + newbr->pid = msg->header.sender_pid; + newbr->ports = (PORT_T*) calloc (msg->body.cntrl.param1, sizeof (PORT_T)); + if (! newbr->ports) { + printf ("Sorry, there is no memory for its ports :(\n"); + free (newbr); + return 0; + } + newbr->number_of_ports = msg->body.cntrl.param1; + memcpy (&newbr->sock, socket_4_reply, sizeof (UID_SOCKET_T)); + + /* bind it to the begin of list */ + newbr->next = br_lst; + br_lst = newbr; + + return 0; +} + +int unregister_bridge (UID_MSG_T* msg) +{ + register BRIDGE_T* oldbr; + register BRIDGE_T* prev = NULL; + register PORT_T* port; + register int indx; + + /* check if such bridge has just been registered */ + for (oldbr = br_lst; oldbr; oldbr = oldbr->next) + if (oldbr->pid == msg->header.sender_pid) { + break; + } else + prev = oldbr; + + if (! oldbr) { + printf ("Sorry, this bridge has not yet been registered ? :(\n"); + return 0; + } + + /* disconnect all its connceted ports */ + for (indx = 0; indx < oldbr->number_of_ports; indx++) { + port = oldbr->ports + indx; + if (port->bridge_partner) { + disconnect_port (port, 0); + } + } + + /* delete from the list & free */ + if (prev) + prev->next = oldbr->next; + else + br_lst = oldbr->next; + + free (oldbr->ports); + free (oldbr); + return 0; +} + +static long scan_br_name (char* param) +{ + if ('B' == param[0]) + return strtoul(param + 1, 0, 10); + else + return strtoul(param, 0, 10); +} + +static int show_bridge (int argc, char** argv) +{ + long pid = 0; + register BRIDGE_T* br = NULL; + register PORT_T* port; + register int indx, cnt = 0; + + if (argc > 1) { + pid = scan_br_name (argv[1]); + printf ("You wanted to see B%ld, didn't you ?\n", pid); + } + + for (br = br_lst; br; br = br->next) + if (! pid || pid == br->pid) { + printf ("%2d. Bridge B%ld has %ld ports:\n", + ++cnt, + (long) br->pid, br->number_of_ports); + for (indx = 0; indx < br->number_of_ports; indx++) { + port = br->ports + indx; + if (port->bridge_partner) { + printf ("port p%02d ", (int) indx + 1); + printf ("connected to B%ld port p%02d\n", + port->bridge_partner->pid, + port->port_partner); + } + } + } + + if (! cnt) { + printf ("There are no such bridges :(\n"); + } + return 0; +} + +static void _link_two_ports (BRIDGE_T* br1, PORT_T* port1, int indx1, + BRIDGE_T* br2, PORT_T* port2, int indx2) +{ + UID_MSG_T msg; + + port1->bridge_partner = br2; + port1->port_partner = indx2; + port2->bridge_partner = br1; + port2->port_partner = indx1; + + msg.header.sender_pid = getpid (); + msg.header.cmd_type = UID_CNTRL; + msg.body.cntrl.cmd = UID_PORT_CONNECT; + + msg.header.destination_port = indx1; + UiD_SocketSendto (&br1->sock, &msg, sizeof (UID_MSG_T)); + msg.header.destination_port = indx2; + UiD_SocketSendto (&br2->sock, &msg, sizeof (UID_MSG_T)); +} + +static int link_bridges (int argc, char** argv) +{ + long pid1, pid2; + int indx1, indx2; + BRIDGE_T* br; + BRIDGE_T* br1; + BRIDGE_T* br2; + PORT_T* port1; + PORT_T* port2; + + if (argc < 5) { + printf ("for this command must be 4 argumenta :(\n"); + return 0; + } + + pid1 = scan_br_name (argv[1]); + indx1 = strtoul(argv[2], 0, 10); + pid2 = scan_br_name (argv[3]); + indx2 = strtoul(argv[4], 0, 10); + printf ("connect B%ld port p%02d to B%ld port p%02d\n", + pid1, indx1, pid2, indx2); + + for (br = br_lst; br; br = br->next) { + //printf ("check B%ld\n", br->pid); + if (br->pid == pid1) br1 = br; + if (br->pid == pid2) br2 = br; + } + + if (! br1 || ! br2) { + printf ("Sorry, one of these bridges is absent :(\n"); + return 0; + } + + if (indx1 > br1->number_of_ports || indx1 < 0) { + printf ("Sorry, p%02d invalid\n", indx1); + return 0; + } + + if (indx2 > br2->number_of_ports || indx2 < 0) { + printf ("Sorry, p%02d invalid\n", indx2); + return 0; + } + + port1 = br1->ports + indx1 - 1; + port2 = br2->ports + indx2 - 1; + + if (port1->bridge_partner) + printf ("port p%02d is connected\n", indx1); + if (port2->bridge_partner) + printf ("port p%02d is connected\n", indx2); + if (port1->bridge_partner || port2->bridge_partner) + return 0; + + _link_two_ports (br1, port1, indx1, + br2, port2, indx2); + return 0; +} + +static int unlink_port (int argc, char** argv) +{ + long pid1; + int indx1; + BRIDGE_T* br; + BRIDGE_T* br1; + BRIDGE_T* br2; + PORT_T* port1; + PORT_T* port2; + + if (argc < 3) { + printf ("for this command must be 2 argumenta :(\n"); + return 0; + } + + pid1 = scan_br_name (argv[1]); + indx1 = strtoul(argv[2], 0, 10); + + + for (br = br_lst; br; br = br->next) { + //printf ("check B%ld\n", br->pid); + if (br->pid == pid1) br1 = br; + } + + if (! br1) { + printf ("Sorry, the bridge B%ldis absent :(\n", pid1); + return 0; + } + + if (indx1 > br1->number_of_ports || indx1 < 0) { + printf ("Sorry, port p%02d invalid\n", indx1); + return 0; + } + + port1 = br1->ports + indx1 - 1; + + if (! port1->bridge_partner) { + printf ("port p%02d is disconnected\n", indx1); + return 0; + } + + br2 = port1->bridge_partner; + port2 = br2->ports + port1->port_partner - 1; + disconnect_port (port1, 1); + disconnect_port (port2, 1); + + return 0; +} + +static int link_ring (int argc, char** argv) +{ + BRIDGE_T* br1; + BRIDGE_T* br2; + PORT_T* port1; + PORT_T* port2; + register int indx; + + /* unlink all */ + for (br1 = br_lst; br1; br1 = br1->next) { + /* disconnect all its connceted ports */ + for (indx = 0; indx < br1->number_of_ports; indx++) { + port1 = br1->ports + indx; + if (port1->bridge_partner) { + printf ("disconnect B%ld ", br1->pid); + printf ("port p%02d (with B%ld-p%02d)\n", + indx + 1, + port1->bridge_partner->pid, + port1->port_partner); + br2 = port1->bridge_partner; + port2 = br2->ports + port1->port_partner - 1; + disconnect_port (port1, 1); + disconnect_port (port2, 1); + } + } + } + + /* buid ring */ + for (br1 = br_lst; br1; br1 = br1->next) { + br2 = br1->next; + if (! br2) br2 = br_lst; + _link_two_ports (br1, br1->ports + 1, 2, + br2, br2->ports + 0, 1); + } + + return 0; +} + +static CMD_DSCR_T lang[] = { + THE_COMMAND("show", "get bridge[s] connuctivity") + PARAM_STRING("bridge name", "all") + THE_FUNC(show_bridge) + + THE_COMMAND("link", "link two bridges") + PARAM_STRING("first bridge name", NULL) + PARAM_NUMBER("port number on first bridge", 1, NUMBER_OF_PORTS, NULL) + PARAM_STRING("second bridge name", NULL) + PARAM_NUMBER("port number on second bridge", 1, NUMBER_OF_PORTS, NULL) + THE_FUNC(link_bridges) + + THE_COMMAND("unlink", "unlink the port of the bridge") + PARAM_STRING("bridge name", NULL) + PARAM_NUMBER("port number on bridge", 1, NUMBER_OF_PORTS, NULL) + THE_FUNC(unlink_port) + + THE_COMMAND("ring", "link all bridges into a ring") + THE_FUNC(link_ring) + + END_OF_LANG +}; + +void mngr_start (void) +{ + if (0 != UiD_SocketInit (&main_sock, UID_REPL_PATH, UID_BIND_AS_SERVER)) { + printf ("FATAL: can't init the connection\n"); + exit (-3); + } + + cli_register_language (lang); +} + +void mngr_shutdown (void) +{ + UID_MSG_T msg; + BRIDGE_T* br; + + msg.header.sender_pid = getpid (); + msg.header.cmd_type = UID_CNTRL; + msg.body.cntrl.cmd = UID_BRIDGE_SHUTDOWN; + + for (br = br_lst; br; br = br->next) { + UiD_SocketSendto (&br->sock, &msg, sizeof (UID_MSG_T)); + } +} + +char *get_prompt (void) +{ + static char prompt[MAX_CLI_PROMT]; + snprintf (prompt, MAX_CLI_PROMT - 1, "%s Mngr > ", UT_sprint_time_stamp()); + return prompt; +} + +int mngr_control (UID_MSG_T* msg, UID_SOCKET_T* sock_4_reply) +{ + switch (msg->body.cntrl.cmd) { + default: + case UID_PORT_CONNECT: + case UID_PORT_DISCONNECT: + printf ("Unexpected contol message '%d'\n", (int) msg->body.cntrl.cmd); + break; + case UID_BRIDGE_SHUTDOWN: + printf ("Bridge B%ld shutdown :(\n", (long) msg->header.sender_pid); + unregister_bridge (msg); + break; + case UID_BRIDGE_HANDSHAKE: + printf ("Bridge B%ld hello :)\n", (long) msg->header.sender_pid); + register_bridge (msg, sock_4_reply); + break; + } + + return 0; +} + +int mngr_rx_bpdu (UID_MSG_T* msg, size_t msgsize) +{ + BRIDGE_T* br; + PORT_T* port; + + for (br = br_lst; br; br = br->next) { + if (br->pid == msg->header.sender_pid) { + break; + } + } + + if (! br) { + printf ("RX BPDU from unknown B%ld\n", msg->header.sender_pid); + return 0; + } + + port = br->ports + msg->header.source_port - 1; + if (! port->bridge_partner) { + printf ("RX BPDU from unlinked port p%02d of bridge B%ld ?\n", + (int) msg->header.source_port, + msg->header.sender_pid); + return 0; + } + + br = port->bridge_partner; + msg->header.destination_port = port->port_partner; + UiD_SocketSendto (&br->sock, msg, sizeof (UID_MSG_T)); + + return 0; +} + +char read_uid (void) +{ + UID_SOCKET_T sock_4_reply; + UID_MSG_T msg; + size_t msgsize; + int rc = 0; + + msgsize = UiD_SocketRecvfrom (&main_sock, &msg, MAX_UID_MSG_SIZE, &sock_4_reply); + if (msgsize <= 0) { + printf ("Something wrong in UIF ?\n"); + return 0; + } + + switch (msg.header.cmd_type) { + case UID_CNTRL: + rc = mngr_control (&msg, &sock_4_reply); + break; + case UID_BPDU: + rc = mngr_rx_bpdu (&msg, msgsize); + break; + default: + printf ("Unknown message type %d\n", (int) msg.header.cmd_type); + rc = 0; + } + + return rc; +} + +char shutdown_flag = 0; + +int main_loop (void) +{ + fd_set readfds; + int rc, numfds, sock, kkk; + + //rl_callback_handler_install (get_prompt (), rl_read_cli); + + sock = GET_FILE_DESCRIPTOR(&main_sock); + + do { + numfds = -1; + FD_ZERO(&readfds); + + kkk = 0; /* stdin for commands */ + FD_SET(kkk, &readfds); + if (kkk > numfds) numfds = kkk; + + FD_SET(sock, &readfds); + if (sock > numfds) numfds = sock; + + if (numfds < 0) + numfds = 0; + else + numfds++; + + rc = select (numfds, &readfds, NULL, NULL, NULL); + if (rc < 0) { // Error + if (EINTR == errno) continue; // don't break + printf ("FATAL_MODE:select failed: %s\n", strerror(errno)); + return -2; + } + + if (FD_ISSET(0, &readfds)) { + rl_callback_read_char (); + } + + if (FD_ISSET(sock, &readfds)) { + shutdown_flag |= read_uid (); + } + + } while(! shutdown_flag); + return 0; +} + +int main (int argc, char** argv) +{ + rl_init (); + + mngr_start (); + + main_loop (); + + mngr_shutdown (); + + rl_shutdown (); + + return 0; +} + diff --git a/rstplib/mngr.txt b/rstplib/mngr.txt new file mode 100644 index 0000000..96a581d --- /dev/null +++ b/rstplib/mngr.txt @@ -0,0 +1,21 @@ +alex alex ~/Stp.emu>./mngr +12:52:36 Mngr > Bridge B5055 hello :) +12:52:57 Mngr > Bridge B5056 hello :) +12:53:09 Mngr > get + 1. Bridge B5056 has 4 ports: +port p01 disconnected +port p02 disconnected +port p03 disconnected +port p04 disconnected + 2. Bridge B5055 has 4 ports: +port p01 disconnected +port p02 disconnected +port p03 disconnected +port p04 disconnected +12:57:02 Mngr > link B5056 2 B5055 2 +connect B5056 port p02 to B5055 port p02 +12:57:16 Mngr > Bridge B5056 shutdown :( +(0x804c4fc) disconnect from port p02 for bridge B5055 +13:02:47 Mngr > quit +alex alex ~/Stp.emu> + diff --git a/rstplib/p2p.c b/rstplib/p2p.c new file mode 100644 index 0000000..1603ba7 --- /dev/null +++ b/rstplib/p2p.c @@ -0,0 +1,90 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Point To Point MAC mode selection machine : 6.4.3, 6.5.1 */ + +#include "base.h" +#include "stpm.h" +#include "stp_to.h" /* for STP_OUT_get_duplex */ + +#define STATES { \ + CHOOSE(INIT), \ + CHOOSE(RECOMPUTE), \ + CHOOSE(STABLE), \ +} + +#define GET_STATE_NAME STP_p2p_get_state_name +#include "choose.h" + +static Bool +computeP2P (PORT_T *port) +{ + switch (port->adminPointToPointMac) { + case P2P_FORCE_TRUE: + return True; + case P2P_FORCE_FALSE: + return False; + default: + case P2P_AUTO: + return STP_OUT_get_duplex (port->port_index); + } +} + +void +STP_p2p_enter_state (STATE_MACH_T* s) +{ + register PORT_T* port = s->owner.port; + + switch (s->State) { + case BEGIN: + case INIT: + port->p2p_recompute = True; + break; + case RECOMPUTE: + port->operPointToPointMac = computeP2P (port); + port->p2p_recompute = False; + break; + case STABLE: + break; + } +} + +Bool +STP_p2p_check_conditions (STATE_MACH_T* s) +{ + register PORT_T* port = s->owner.port; + + switch (s->State) { + case BEGIN: + case INIT: + return STP_hop_2_state (s, STABLE); + case RECOMPUTE: + return STP_hop_2_state (s, STABLE); + case STABLE: + if (port->p2p_recompute) { + return STP_hop_2_state (s, RECOMPUTE); + } + break; + } + return False; +} + diff --git a/rstplib/p2p.h b/rstplib/p2p.h new file mode 100644 index 0000000..ea892ab --- /dev/null +++ b/rstplib/p2p.h @@ -0,0 +1,37 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Point To Point MAC mode selection machine : 6.4.3, 6.5.1 */ + +#ifndef _STP_P2P_H__ +#define _STP_P2P_H__ + +void +STP_p2p_enter_state (STATE_MACH_T* s); + +Bool +STP_p2p_check_conditions (STATE_MACH_T* s); + +char* +STP_p2p_get_state_name (int state); + +#endif /* _STP_P2P_H__ */ diff --git a/rstplib/pcost.c b/rstplib/pcost.c new file mode 100644 index 0000000..7d20427 --- /dev/null +++ b/rstplib/pcost.c @@ -0,0 +1,133 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Path Cost monitoring state machine */ + +#include "base.h" +#include "stpm.h" +#include "stp_to.h" /* for STP_OUT_get_port_oper_speed */ + +#define STATES { \ + CHOOSE(AUTO), \ + CHOOSE(FORSE), \ + CHOOSE(STABLE), \ +} + +#define GET_STATE_NAME STP_pcost_get_state_name +#include "choose.h" + +static long +computeAutoPCost (STATE_MACH_T *this) +{ + long lret; + register PORT_T* port = this->owner.port; + + if (port->usedSpeed < 10L) { /* < 10Mb/s */ + lret = 20000000; + } else if (port->usedSpeed <= 10L) { /* 10 Mb/s */ + lret = 2000000; + } else if (port->usedSpeed <= 100L) { /* 100 Mb/s */ + lret = 200000; + } else if (port->usedSpeed <= 1000L) { /* 1 Gb/s */ + lret = 20000; + } else if (port->usedSpeed <= 10000L) { /* 10 Gb/s */ + lret = 2000; + } else if (port->usedSpeed <= 100000L) { /* 100 Gb/s */ + lret = 200; + } else if (port->usedSpeed <= 1000000L) { /* 1 GTb/s */ + lret = 20; + } else if (port->usedSpeed <= 10000000L) { /* 10 Tb/s */ + lret = 2; + } else /* ??? */ { /* > Tb/s */ + lret = 1; + } +#ifdef STP_DBG + if (port->pcost->debug) { + stp_trace ("usedSpeed=%lu lret=%ld", port->usedSpeed, lret); + } +#endif + + return lret; +} + +static void +updPortPathCost (PORT_T *port) +{ + port->reselect = True; + port->selected = False; +} + +void +STP_pcost_enter_state (STATE_MACH_T *this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + break; + case AUTO: + port->operSpeed = STP_OUT_get_port_oper_speed (port->port_index); +#ifdef STP_DBG + if (port->pcost->debug) { + stp_trace ("AUTO:operSpeed=%lu", port->operSpeed); + } +#endif + port->usedSpeed = port->operSpeed; + port->operPCost = computeAutoPCost (this); + break; + case FORSE: + port->operPCost = port->adminPCost; + port->usedSpeed = -1; + break; + case STABLE: + updPortPathCost (port); + break; + } +} + +Bool +STP_pcost_check_conditions (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + return STP_hop_2_state (this, AUTO); + case AUTO: + return STP_hop_2_state (this, STABLE); + case FORSE: + return STP_hop_2_state (this, STABLE); + case STABLE: + if (ADMIN_PORT_PATH_COST_AUTO == port->adminPCost && + port->operSpeed != port->usedSpeed) { + return STP_hop_2_state (this, AUTO); + } + + if (ADMIN_PORT_PATH_COST_AUTO != port->adminPCost && + port->operPCost != port->adminPCost) { + return STP_hop_2_state (this, FORSE); + } + break; + } + return False; +} + diff --git a/rstplib/pcost.h b/rstplib/pcost.h new file mode 100644 index 0000000..cb19ade --- /dev/null +++ b/rstplib/pcost.h @@ -0,0 +1,37 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Path Cost monitoring state machine */ + +#ifndef _STP_PCOST_H__ +#define _STP_PCOST_H__ + +void +STP_pcost_enter_state (STATE_MACH_T* s); + +Bool +STP_pcost_check_conditions (STATE_MACH_T* s); + +char* +STP_pcost_get_state_name (int state); + +#endif /* _STP_PCOST_H__ */ diff --git a/rstplib/port.c b/rstplib/port.c new file mode 100644 index 0000000..4d4e377 --- /dev/null +++ b/rstplib/port.c @@ -0,0 +1,243 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* STP PORT instance : 17.18, 17.15 */ + +#include "base.h" +#include "stpm.h" +#include "stp_in.h" + +/* #include "rolesel.h" */ +#include "portinfo.h" +#include "roletrns.h" +#include "sttrans.h" +#include "topoch.h" +#include "migrate.h" +#include "transmit.h" +#include "p2p.h" +#include "pcost.h" +#include "edge.h" + +#include "stp_to.h" /* for STP_OUT_get_port_name & STP_OUT_get_port_link_status */ + +PORT_T * +STP_port_create (STPM_T* stpm, int port_index) +{ + PORT_T* this; + UID_STP_PORT_CFG_T port_cfg; + register int iii; + unsigned short port_prio; + + /* check, if the port has just been added */ + for (this = stpm->ports; this; this = this->next) { + if (this->port_index == port_index) { + return NULL; + } + } + + STP_NEW_IN_LIST(this, PORT_T, stpm->ports, "port create"); + + this->owner = stpm; + this->machines = NULL; + this->port_index = port_index; + this->port_name = strdup (STP_OUT_get_port_name (port_index)); + this->uptime = 0; + + STP_OUT_get_init_port_cfg (stpm->vlan_id, port_index, &port_cfg); + port_prio = port_cfg.port_priority; + this->admin_non_stp = port_cfg.admin_non_stp; + this->adminEdge = port_cfg.admin_edge; + this->adminPCost = port_cfg.admin_port_path_cost; + this->adminPointToPointMac = port_cfg.admin_point2point; + + this->LinkDelay = DEF_LINK_DELAY; + this->port_id = (port_prio << 8) + port_index; + + iii = 0; + this->timers[iii++] = &this->fdWhile; + this->timers[iii++] = &this->helloWhen; + this->timers[iii++] = &this->mdelayWhile; + this->timers[iii++] = &this->rbWhile; + this->timers[iii++] = &this->rcvdInfoWhile; + this->timers[iii++] = &this->rrWhile; + this->timers[iii++] = &this->tcWhile; + this->timers[iii++] = &this->txCount; + this->timers[iii++] = &this->lnkWhile; + + /* create and bind port state machines */ + STP_STATE_MACH_IN_LIST(topoch); + + STP_STATE_MACH_IN_LIST(migrate); + + STP_STATE_MACH_IN_LIST(p2p); + + STP_STATE_MACH_IN_LIST(edge); + + STP_STATE_MACH_IN_LIST(pcost) + + STP_STATE_MACH_IN_LIST(info); + + STP_STATE_MACH_IN_LIST(roletrns); + + STP_STATE_MACH_IN_LIST(sttrans); + + STP_STATE_MACH_IN_LIST(transmit); + +#ifdef STP_DBG + +#if 0 + this->roletrns->ignoreHop2State = 14; /* DESIGNATED_PORT; */ + this->info->ignoreHop2State = 3; /* CURRENT */ + this->transmit->ignoreHop2State = 3; /* IDLE */ + this->edge->ignoreHop2State = 0; /* DISABLED; */ +#endif + +#if 0 + this->info->debug = 1; + this->pcost->debug = 1; + this->p2p->debug = 1; + this->edge->debug = 1; + this->migrate->debug = 1; + this->sttrans->debug = 1; + this->topoch->debug = 1; + this->roletrns->debug = 1; +#endif + this->sttrans->debug = 1; + +#endif + return this; +} + +void +STP_port_init (PORT_T* this, STPM_T* stpm, Bool check_link) +{ + if (check_link) { + this->adminEnable = STP_OUT_get_port_link_status (this->port_index); + STP_VECT_create (&this->designPrio, + &stpm->BrId, + 0, + &stpm->BrId, + this->port_id, + this->port_id); + STP_copy_times (&this->designTimes, &stpm->rootTimes); + this->fdWhile = 0; + } + + /* reset timers */ + this->helloWhen = + this->mdelayWhile = + this->rbWhile = + this->rcvdInfoWhile = + this->rrWhile = + this->tcWhile = + this->txCount = 0; + + this->msgPortRole = RSTP_PORT_ROLE_UNKN; + this->selectedRole = DisabledPort; + this->sendRSTP = True; + this->operSpeed = STP_OUT_get_port_oper_speed (this->port_index); + this->p2p_recompute = True; +} + +void +STP_port_delete (PORT_T* this) +{ + STPM_T* stpm; + register PORT_T* prev; + register PORT_T* tmp; + register STATE_MACH_T* stater; + register void* pv; + + stpm = this->owner; + + free (this->port_name); + for (stater = this->machines; stater; ) { + pv = (void*) stater->next; + STP_state_mach_delete (stater); + stater = (STATE_MACH_T*) pv; + } + + prev = NULL; + for (tmp = stpm->ports; tmp; tmp = tmp->next) { + if (tmp->port_index == this->port_index) { + if (prev) { + prev->next = this->next; + } else { + stpm->ports = this->next; + } + STP_FREE(this, "stp instance"); + break; + } + prev = tmp; + } +} + +int +STP_port_rx_bpdu (PORT_T* this, BPDU_T* bpdu, size_t len) +{ + STP_info_rx_bpdu (this, bpdu, len); + + return 0; +} + +#ifdef STP_DBG +int STP_port_trace_state_machine (PORT_T* this, char* mach_name, int enadis, int vlan_id) +{ + register struct state_mach_t* stater; + + for (stater = this->machines; stater; stater = stater->next) { + if (! strcmp (mach_name, "all") || ! strcmp (mach_name, stater->name)) { + /* if (stater->debug != enadis) */ + { + stp_trace ("port %s on %s trace %-8s (was %s) now %s", + this->port_name, this->owner->name, + stater->name, + stater->debug ? " enabled" :"disabled", + enadis ? " enabled" :"disabled"); + } + stater->debug = enadis; + } + } + + return 0; +} + +void STP_port_trace_flags (char* title, PORT_T* this) +{ +#if 0 /* it may be opened for more deep debugging */ + unsigned long flag = 0L; + + if (this->reRoot) flag |= 0x000001L; + if (this->sync) flag |= 0x000002L; + if (this->synced) flag |= 0x000004L; + + if (this->proposed) flag |= 0x000010L; + if (this->proposing) flag |= 0x000020L; + if (this->agreed) flag |= 0x000040L; + if (this->updtInfo) flag |= 0x000080L; + + if (this->operEdge) flag |= 0x000100L; + stp_trace (" %-12s: flags=0X%04lx port=%s", title, flag, this->port_name); +#endif +} + +#endif diff --git a/rstplib/port.h b/rstplib/port.h new file mode 100644 index 0000000..3bd23c2 --- /dev/null +++ b/rstplib/port.h @@ -0,0 +1,185 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* STP PORT instance : 17.18, 17.15 */ + +#ifndef _STP_PORT_H__ +#define _STP_PORT_H__ + +#include "statmch.h" + +#define TIMERS_NUMBER 9 +typedef unsigned int PORT_TIMER_T; + +typedef enum { + Mine, + Aged, + Received, + Disabled +} INFO_IS_T; + +typedef enum { + SuperiorDesignateMsg, + RepeatedDesignateMsg, + ConfirmedRootMsg, + OtherMsg +} RCVD_MSG_T; + +typedef enum { + DisabledPort = 0, + AlternatePort, + BackupPort, + RootPort, + DesignatedPort, + NonStpPort +} PORT_ROLE_T; + +typedef struct port_t { + struct port_t* next; + + /* per Port state machines */ + STATE_MACH_T* info; /* 17.21 */ + STATE_MACH_T* roletrns; /* 17.23 */ + STATE_MACH_T* sttrans; /* 17.24 */ + STATE_MACH_T* topoch; /* 17.25 */ + STATE_MACH_T* migrate; /* 17.26 */ + STATE_MACH_T* transmit; /* 17.26 */ + STATE_MACH_T* p2p; /* 6.4.3, 6.5.1 */ + STATE_MACH_T* edge; /* */ + STATE_MACH_T* pcost; /* */ + + STATE_MACH_T* machines; /* list of machines */ + + struct stpm_t* owner; /* Bridge, that this port belongs to */ + + /* per port Timers */ + PORT_TIMER_T fdWhile; /* 17.15.1 */ + PORT_TIMER_T helloWhen; /* 17.15.2 */ + PORT_TIMER_T mdelayWhile; /* 17.15.3 */ + PORT_TIMER_T rbWhile; /* 17.15.4 */ + PORT_TIMER_T rcvdInfoWhile;/* 17.15.5 */ + PORT_TIMER_T rrWhile; /* 17.15.6 */ + PORT_TIMER_T tcWhile; /* 17.15.7 */ + PORT_TIMER_T txCount; /* 17.18.40 */ + PORT_TIMER_T lnkWhile; + + PORT_TIMER_T* timers[TIMERS_NUMBER]; /*list of timers */ + + Bool agreed; /* 17.18.1 */ + PRIO_VECTOR_T designPrio; /* 17.18.2 */ + TIMEVALUES_T designTimes; /* 17.18.3 */ + Bool forward; /* 17.18.4 */ + Bool forwarding; /* 17.18.5 */ + INFO_IS_T infoIs; /* 17.18.6 */ + Bool initPm; /* 17.18.7 */ + Bool learn; /* 17.18.8 */ + Bool learning; /* 17.18.9 */ + Bool mcheck; /* 17.18.10 */ + PRIO_VECTOR_T msgPrio; /* 17.18.11 */ + TIMEVALUES_T msgTimes; /* 17.18.12 */ + Bool newInfo; /* 17.18.13 */ + Bool operEdge; /* 17.18.14 */ + Bool adminEdge; /* 17.18.14 */ + Bool portEnabled; /* 17.18.15 */ + PORT_ID port_id; /* 17.18.16 */ + PRIO_VECTOR_T portPrio; /* 17.18.17 */ + TIMEVALUES_T portTimes; /* 17.18.18 */ + Bool proposed; /* 17.18.19 */ + Bool proposing; /* 17.18.20 */ + Bool rcvdBpdu; /* 17.18.21 */ + RCVD_MSG_T rcvdMsg; /* 17.18.22 */ + Bool rcvdRSTP; /* 17/18.23 */ + Bool rcvdSTP; /* 17.18.24 */ + Bool rcvdTc; /* 17.18.25 */ + Bool rcvdTcAck; /* 17.18.26 */ + Bool rcvdTcn; /* 17.18.27 */ + Bool reRoot; /* 17.18.28 */ + Bool reselect; /* 17.18.29 */ + PORT_ROLE_T role; /* 17.18.30 */ + Bool selected; /* 17.18.31 */ + PORT_ROLE_T selectedRole; /* 17.18.32 */ + Bool sendRSTP; /* 17.18.33 */ + Bool sync; /* 17.18.34 */ + Bool synced; /* 17.18.35 */ + Bool tc; /* 17.18.36 */ + Bool tcAck; /* 17.18.37 */ + Bool tcProp; /* 17.18.38 */ + + Bool updtInfo; /* 17.18.41 */ + + /* message information */ + unsigned char msgBpduVersion; + unsigned char msgBpduType; + unsigned char msgPortRole; + unsigned char msgFlags; + + unsigned long adminPCost; /* may be ADMIN_PORT_PATH_COST_AUTO */ + unsigned long operPCost; + unsigned long operSpeed; + unsigned long usedSpeed; + int LinkDelay; /* TBD: LinkDelay may be managed ? */ + Bool adminEnable; /* 'has LINK' */ + Bool wasInitBpdu; + Bool admin_non_stp; + + Bool p2p_recompute; + Bool operPointToPointMac; + ADMIN_P2P_T adminPointToPointMac; + + /* statistics */ + unsigned long rx_cfg_bpdu_cnt; + unsigned long rx_rstp_bpdu_cnt; + unsigned long rx_tcn_bpdu_cnt; + + unsigned long uptime; /* 14.8.2.1.3.a */ + + int port_index; + char* port_name; + +#ifdef STP_DBG + unsigned int skip_rx; + unsigned int skip_tx; +#endif +} PORT_T; + +PORT_T* +STP_port_create (struct stpm_t* stpm, int port_index); + +void +STP_port_delete (PORT_T* this); + +int +STP_port_rx_bpdu (PORT_T* this, BPDU_T* bpdu, size_t len); + +void +STP_port_init (PORT_T* this, struct stpm_t* stpm, Bool check_link); + +#ifdef STP_DBG +int +STP_port_trace_state_machine (PORT_T* this, char* mach_name, int enadis, int vlan_id); + +void +STP_port_trace_flags (char* title, PORT_T* this); +#endif + +#endif /* _STP_PORT_H__ */ + diff --git a/rstplib/portinfo.c b/rstplib/portinfo.c new file mode 100644 index 0000000..961eb39 --- /dev/null +++ b/rstplib/portinfo.c @@ -0,0 +1,508 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#include "base.h" +#include "stpm.h" + +/* The Port Information State Machine : 17.21 */ + +#define STATES { \ + CHOOSE(DISABLED), \ + CHOOSE(ENABLED), \ + CHOOSE(AGED), \ + CHOOSE(UPDATE), \ + CHOOSE(CURRENT), \ + CHOOSE(RECEIVE), \ + CHOOSE(SUPERIOR), \ + CHOOSE(REPEAT), \ + CHOOSE(AGREEMENT), \ +} + +#define GET_STATE_NAME STP_info_get_state_name +#include "choose.h" + +#if 0 /* for debug */ +void +_stp_dump (char* title, unsigned char* buff, int len) +{ + register int iii; + + printf ("\n%s:", title); + for (iii = 0; iii < len; iii++) { + if (! (iii % 24)) Print ("\n%6d:", iii); + if (! (iii % 8)) Print (" "); + Print ("%02lx", (unsigned long) buff[iii]); + } + Print ("\n"); +} +#endif + +static RCVD_MSG_T +rcvBpdu (STATE_MACH_T* this) +{/* 17.19.8 */ + int bridcmp; + register PORT_T* port = this->owner.port; + + if (port->msgBpduType == BPDU_TOPO_CHANGE_TYPE) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("%s", "OtherMsg:BPDU_TOPO_CHANGE_TYPE"); + } +#endif + return OtherMsg; + } + + port->msgPortRole = RSTP_PORT_ROLE_UNKN; + + if (BPDU_RSTP == port->msgBpduType) { + port->msgPortRole = (port->msgFlags & PORT_ROLE_MASK) >> PORT_ROLE_OFFS; +#ifndef ORIG + if (RSTP_PORT_ROLE_UNKN == port->msgPortRole) { + port->msgBpduVersion = FORCE_STP_COMPAT; + port->msgBpduType = BPDU_CONFIG_TYPE; + } +#endif + } + + if (RSTP_PORT_ROLE_DESGN == port->msgPortRole || + BPDU_CONFIG_TYPE == port->msgBpduType) { + bridcmp = STP_VECT_compare_vector (&port->msgPrio, &port->portPrio); + + if (bridcmp < 0 || + (! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, + &port->portPrio.design_bridge) && + port->msgPrio.design_port == port->portPrio.design_port && + STP_compare_times (&port->msgTimes, &port->portTimes))) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("SuperiorDesignateMsg:bridcmp=%d", (int) bridcmp); + } +#endif + return SuperiorDesignateMsg; + } + } + + if (BPDU_CONFIG_TYPE == port->msgBpduType || + RSTP_PORT_ROLE_DESGN == port->msgPortRole) { + if (! STP_VECT_compare_vector (&port->msgPrio, + &port->portPrio) && + ! STP_compare_times (&port->msgTimes, &port->portTimes)) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("%s", "RepeatedDesignateMsg"); + } +#endif + return RepeatedDesignateMsg; + } + } + + if (RSTP_PORT_ROLE_ROOT == port->msgPortRole && + port->operPointToPointMac && + ! STP_VECT_compare_bridge_id (&port->msgPrio.root_bridge, + &port->portPrio.root_bridge) && + port->msgPrio.root_path_cost == port->portPrio.root_path_cost && + ! STP_VECT_compare_bridge_id (&port->msgPrio.design_bridge, + &port->portPrio.design_bridge) && + port->msgPrio.design_port == port->portPrio.design_port && + AGREEMENT_BIT & port->msgFlags) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("%s", "ConfirmedRootMsg"); + } +#endif + return ConfirmedRootMsg; + } + +#ifdef STP_DBG + if (this->debug) { + stp_trace ("%s", "OtherMsg"); + } +#endif + return OtherMsg; +} + +static Bool +recordProposed (STATE_MACH_T* this, char* reason) +{/* 17.19.9 */ + register PORT_T* port = this->owner.port; + + if (RSTP_PORT_ROLE_DESGN == port->msgPortRole && + (PROPOSAL_BIT & port->msgFlags) && + port->operPointToPointMac) { + return True; + } + return False; +} + +static Bool +setTcFlags (STATE_MACH_T* this) +{/* 17.19.13 */ + register PORT_T* port = this->owner.port; + + if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("port %s rx rcvdTcn", port->port_name); + } +#endif + port->rcvdTcn = True; + } else { + if (TOLPLOGY_CHANGE_BIT & port->msgFlags) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("(%s-%s) rx rcvdTc 0X%lx", + port->owner->name, port->port_name, + (unsigned long) port->msgFlags); + } +#endif + port->rcvdTc = True; + } + if (TOLPLOGY_CHANGE_ACK_BIT & port->msgFlags) { +#ifdef STP_DBG + if (this->debug) { + stp_trace ("port %s rx rcvdTcAck 0X%lx", + port->port_name, + (unsigned long) port->msgFlags); + } +#endif + port->rcvdTcAck = True; + } + } + + return True; +} + +static Bool +updtBPDUVersion (STATE_MACH_T* this) +{/* 17.19.18 */ + register PORT_T* port = this->owner.port; + + if (BPDU_TOPO_CHANGE_TYPE == port->msgBpduType) { + port->rcvdSTP = True; + } + + if (port->msgBpduVersion < 2) { + port->rcvdSTP = True; + } + + if (BPDU_RSTP == port->msgBpduType) { + /* port->port->owner->ForceVersion >= NORMAL_RSTP + we have checked in STP_info_rx_bpdu */ + port->rcvdRSTP = True; + } + + return True; +} + +static Bool +updtRcvdInfoWhile (STATE_MACH_T* this) +{/* 17.19.19 */ + register int eff_age, dm, dt; + register int hello3; + register PORT_T* port = this->owner.port; + + eff_age = ( + port->portTimes.MaxAge) / 16; + if (eff_age < 1) eff_age = 1; + eff_age += port->portTimes.MessageAge; + + if (eff_age <= port->portTimes.MaxAge) { + hello3 = 3 * port->portTimes.HelloTime; + dm = port->portTimes.MaxAge - eff_age; + if (dm > hello3) + dt = hello3; + else + dt = dm; + port->rcvdInfoWhile = dt; +/**** + stp_trace ("ma=%d eff_age=%d dm=%d dt=%d p=%s", + (int) port->portTimes.MessageAge, + (int) eff_age, (int) dm, (int) dt, port->port_name); +****/ + } else { + port->rcvdInfoWhile = 0; +/****/ +#ifdef STP_DBG + /*if (this->debug) */ + { + stp_trace ("port %s: MaxAge=%d MessageAge=%d HelloTime=%d rcvdInfoWhile=null !", + port->port_name, + (int) port->portTimes.MaxAge, + (int) port->portTimes.MessageAge, + (int) port->portTimes.HelloTime); + } +#endif +/****/ + } + + return True; +} + + +void +STP_info_rx_bpdu (PORT_T* port, struct stp_bpdu_t* bpdu, size_t len) +{ +#if 0 + _stp_dump ("\nall BPDU", ((unsigned char*) bpdu) - 12, len + 12); + _stp_dump ("ETH_HEADER", (unsigned char*) &bpdu->eth, 5); + _stp_dump ("BPDU_HEADER", (unsigned char*) &bpdu->hdr, 4); + printf ("protocol=%02x%02x version=%02x bpdu_type=%02x\n", + bpdu->hdr.protocol[0], bpdu->hdr.protocol[1], + bpdu->hdr.version, bpdu->hdr.bpdu_type); + + _stp_dump ("\nBPDU_BODY", (unsigned char*) &bpdu->body, sizeof (BPDU_BODY_T) + 2); + printf ("flags=%02x\n", bpdu->body.flags); + _stp_dump ("root_id", bpdu->body.root_id, 8); + _stp_dump ("root_path_cost", bpdu->body.root_path_cost, 4); + _stp_dump ("bridge_id", bpdu->body.bridge_id, 8); + _stp_dump ("port_id", bpdu->body.port_id, 2); + _stp_dump ("message_age", bpdu->body.message_age, 2); + _stp_dump ("max_age", bpdu->body.max_age, 2); + _stp_dump ("hello_time", bpdu->body.hello_time, 2); + _stp_dump ("forward_delay", bpdu->body.forward_delay, 2); + _stp_dump ("ver_1_len", bpdu->ver_1_len, 2); +#endif + + /* check bpdu type */ + switch (bpdu->hdr.bpdu_type) { + case BPDU_CONFIG_TYPE: + port->rx_cfg_bpdu_cnt++; +#if 0 /* def STP_DBG */ + if (port->info->debug) + stp_trace ("CfgBpdu on port %s", port->port_name); +#endif + if (port->admin_non_stp) return; + port->rcvdBpdu = True; + break; + case BPDU_TOPO_CHANGE_TYPE: + port->rx_tcn_bpdu_cnt++; +#if 0 /* def STP_DBG */ + if (port->info->debug) + stp_trace ("TcnBpdu on port %s", port->port_name); +#endif + if (port->admin_non_stp) return; + port->rcvdBpdu = True; + port->msgBpduVersion = bpdu->hdr.version; + port->msgBpduType = bpdu->hdr.bpdu_type; + return; + default: + stp_trace ("RX undef bpdu type=%d", (int) bpdu->hdr.bpdu_type); + return; + case BPDU_RSTP: + port->rx_rstp_bpdu_cnt++; + if (port->admin_non_stp) return; + if (port->owner->ForceVersion >= NORMAL_RSTP) { + port->rcvdBpdu = True; + } else { + return; + } +#if 0 /* def STP_DBG */ + if (port->info->debug) + stp_trace ("BPDU_RSTP on port %s", port->port_name); +#endif + break; + } + + port->msgBpduVersion = bpdu->hdr.version; + port->msgBpduType = bpdu->hdr.bpdu_type; + port->msgFlags = bpdu->body.flags; + + /* 17.18.11 */ + STP_VECT_get_vector (&bpdu->body, &port->msgPrio); + port->msgPrio.bridge_port = port->port_id; + + /* 17.18.12 */ + STP_get_times (&bpdu->body, &port->msgTimes); + + /* 17.18.25, 17.18.26 : see setTcFlags() */ +} + +void STP_info_enter_state (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + port->rcvdMsg = OtherMsg; + port->msgBpduType = -1; + port->msgPortRole = RSTP_PORT_ROLE_UNKN; + port->msgFlags = 0; + + /* clear port statistics */ + port->rx_cfg_bpdu_cnt = + port->rx_rstp_bpdu_cnt = + port->rx_tcn_bpdu_cnt = 0; + + case DISABLED: + port->rcvdBpdu = port->rcvdRSTP = port->rcvdSTP = False; + port->updtInfo = port->proposing = False; /* In DISABLED */ + port->agreed = port->proposed = False; + port->rcvdInfoWhile = 0; + port->infoIs = Disabled; + port->reselect = True; + port->selected = False; + break; + case ENABLED: /* IEEE 802.1y, 17.21, Z.14 */ + STP_VECT_copy (&port->portPrio, &port->designPrio); + STP_copy_times (&port->portTimes, &port->designTimes); + break; + case AGED: + port->infoIs = Aged; + port->reselect = True; + port->selected = False; + break; + case UPDATE: + STP_VECT_copy (&port->portPrio, &port->designPrio); + STP_copy_times (&port->portTimes, &port->designTimes); + port->updtInfo = False; + port->agreed = port->synced = False; /* In UPDATE */ + port->proposed = port->proposing = False; /* in UPDATE */ + port->infoIs = Mine; + port->newInfo = True; +#ifdef STP_DBG + if (this->debug) { + STP_VECT_br_id_print ("updated: portPrio.design_bridge", + &port->portPrio.design_bridge, True); + } +#endif + break; + case CURRENT: + break; + case RECEIVE: + port->rcvdMsg = rcvBpdu (this); + updtBPDUVersion (this); + setTcFlags (this); + port->rcvdBpdu = False; + break; + case SUPERIOR: + STP_VECT_copy (&port->portPrio, &port->msgPrio); + STP_copy_times (&port->portTimes, &port->msgTimes); + updtRcvdInfoWhile (this); +#if 1 /* due 802.1y, Z.7 */ + port->agreed = False; /* deleted due 802.y in SUPERIOR */ + port->synced = False; /* due 802.y deleted in SUPERIOR */ +#endif + port->proposing = False; /* in SUPERIOR */ + port->proposed = recordProposed (this, "SUPERIOR"); + port->infoIs = Received; + port->reselect = True; + port->selected = False; +#ifdef STP_DBG + if (this->debug) { + STP_VECT_br_id_print ("stored: portPrio.design_bridge", + &port->portPrio.design_bridge, True); + stp_trace ("proposed=%d on port %s", + (int) port->proposed, port->port_name); + } +#endif + break; + case REPEAT: + port->proposed = recordProposed (this, "REPEAT"); + updtRcvdInfoWhile (this); + break; + case AGREEMENT: +#ifdef STP_DBG + if (port->roletrns->debug) { + stp_trace ("(%s-%s) rx AGREEMENT flag !", + port->owner->name, port->port_name); + } +#endif + + port->agreed = True; + port->proposing = False; /* In AGREEMENT */ + break; + } + +} + +Bool STP_info_check_conditions (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + if ((! port->portEnabled && port->infoIs != Disabled) || BEGIN == this->State) { + return STP_hop_2_state (this, DISABLED); + } + + switch (this->State) { + case DISABLED: + if (port->updtInfo) { + return STP_hop_2_state (this, DISABLED); + } + if (port->portEnabled && port->selected) { + return STP_hop_2_state (this, ENABLED); + } + if (port->rcvdBpdu) { + return STP_hop_2_state (this, DISABLED); + } + break; + case ENABLED: /* IEEE 802.1y, 17.21, Z.14 */ + return STP_hop_2_state (this, AGED); + break; + case AGED: + if (port->selected && port->updtInfo) { + return STP_hop_2_state (this, UPDATE); + } + break; + case UPDATE: + return STP_hop_2_state (this, CURRENT); + break; + case CURRENT: + if (port->selected && port->updtInfo) { + return STP_hop_2_state (this, UPDATE); + } + + if (Received == port->infoIs && + ! port->rcvdInfoWhile && + ! port->updtInfo && + ! port->rcvdBpdu) { + return STP_hop_2_state (this, AGED); + } + if (port->rcvdBpdu && !port->updtInfo) { + return STP_hop_2_state (this, RECEIVE); + } + break; + case RECEIVE: + switch (port->rcvdMsg) { + case SuperiorDesignateMsg: + return STP_hop_2_state (this, SUPERIOR); + case RepeatedDesignateMsg: + return STP_hop_2_state (this, REPEAT); + case ConfirmedRootMsg: + return STP_hop_2_state (this, AGREEMENT); + default: + return STP_hop_2_state (this, CURRENT); + } + break; + case SUPERIOR: + return STP_hop_2_state (this, CURRENT); + break; + case REPEAT: + return STP_hop_2_state (this, CURRENT); + break; + case AGREEMENT: + return STP_hop_2_state (this, CURRENT); + break; + } + + return False; +} + + diff --git a/rstplib/portinfo.h b/rstplib/portinfo.h new file mode 100644 index 0000000..ef1dceb --- /dev/null +++ b/rstplib/portinfo.h @@ -0,0 +1,40 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* The Port Information State Machine : 17.21 */ + +#ifndef _STP_INFOR_H__ +#define _STP_INFOR_H__ + +void +STP_info_enter_state (STATE_MACH_T* s); + +Bool +STP_info_check_conditions (STATE_MACH_T* s); + +void +STP_info_rx_bpdu (PORT_T* this, struct stp_bpdu_t* bpdu, size_t len); + +char* +STP_info_get_state_name (int state); + +#endif /* _STP_INFOR_H__ */ diff --git a/rstplib/rolesel.c b/rstplib/rolesel.c new file mode 100644 index 0000000..70b3a64 --- /dev/null +++ b/rstplib/rolesel.c @@ -0,0 +1,369 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Role Selection state machine : 17.22 */ + +#include "base.h" +#include "stpm.h" + +#define STATES { \ + CHOOSE(INIT_BRIDGE), \ + CHOOSE(ROLE_SELECTION), \ +} + +#define GET_STATE_NAME STP_rolesel_get_state_name +#include "choose.h" + +#ifdef STP_DBG +void stp_dbg_break_point (PORT_T * port, STPM_T* stpm) +{ +} +#endif + +static Bool +_is_backup_port (PORT_T* port, STPM_T* this) +{ + if (!STP_VECT_compare_bridge_id + (&port->portPrio.design_bridge, &this->BrId)) { +#if 0 /* def STP_DBG */ + if (port->info->debug) { + STP_VECT_br_id_print ("portPrio.design_bridge", + &port->portPrio.design_bridge, True); + STP_VECT_br_id_print (" this->BrId", + &this->BrId, True); + } + stp_dbg_break_point (port, this); +#endif + return True; + } else { + return False; + } +} + +static void +setRoleSelected (char* reason, STPM_T* stpm, PORT_T* port, + PORT_ROLE_T newRole) +{ + char* new_role_name; + + port->selectedRole = newRole; + + if (newRole == port->role) + return; + + switch (newRole) { + case DisabledPort: + new_role_name = "Disabled"; + break; + case AlternatePort: + new_role_name = "Alternate"; + break; + case BackupPort: + new_role_name = "Backup"; + break; + case RootPort: + new_role_name = "Root"; + break; + case DesignatedPort: + new_role_name = "Designated"; + break; + case NonStpPort: + new_role_name = "NonStp"; + port->role = newRole; + break; + default: + stp_trace ("%s-%s:port %s => Unknown (%d ?)", + reason, stpm->name, port->port_name, (int) newRole); + return; + } + +#ifdef STP_DBG + if (port->roletrns->debug) + stp_trace ("%s(%s-%s) => %s", + reason, stpm->name, port->port_name, new_role_name); +#endif +} + +static void +updtRoleDisableBridge (STPM_T* this) +{ /* 17.10.20 */ + register PORT_T *port; + + for (port = this->ports; port; port = port->next) { + port->selectedRole = DisabledPort; + } +} + +static void +clearReselectBridge (STPM_T* this) +{ /* 17.19.1 */ + register PORT_T *port; + + for (port = this->ports; port; port = port->next) { + port->reselect = False; + } +} + +static void +updtRootPrio (STATE_MACH_T* this) +{ + PRIO_VECTOR_T rootPathPrio; /* 17.4.2.2 */ + register PORT_T *port; + register STPM_T *stpm; + register unsigned int dm; + + stpm = this->owner.stpm; + + for (port = stpm->ports; port; port = port->next) { + if (port->admin_non_stp) { + continue; + } + + if (Disabled == port->infoIs) + continue; + if (Aged == port->infoIs) + continue; + if (Mine == port->infoIs) { +#if 0 /* def STP_DBG */ + stp_dbg_break_point (port); /* for debugger break point */ +#endif + continue; + } + + STP_VECT_copy (&rootPathPrio, &port->portPrio); + rootPathPrio.root_path_cost += port->operPCost; + + if (STP_VECT_compare_vector (&rootPathPrio, &stpm->rootPrio) < 0) { + STP_VECT_copy (&stpm->rootPrio, &rootPathPrio); + STP_copy_times (&stpm->rootTimes, &port->portTimes); + dm = (8 + stpm->rootTimes.MaxAge) / 16; + if (!dm) + dm = 1; + stpm->rootTimes.MessageAge += dm; +#ifdef STP_DBG + if (port->roletrns->debug) + stp_trace ("updtRootPrio: dm=%d rootTimes.MessageAge=%d on port %s", + (int) dm, (int) stpm->rootTimes.MessageAge, + port->port_name); +#endif + } + } +} + +static void +updtRolesBridge (STATE_MACH_T* this) +{ /* 17.19.21 */ + register PORT_T* port; + register STPM_T* stpm; + PORT_ID old_root_port; /* for tracing of root port changing */ + + stpm = this->owner.stpm; + old_root_port = stpm->rootPortId; + + STP_VECT_create (&stpm->rootPrio, &stpm->BrId, 0, &stpm->BrId, 0, 0); + STP_copy_times (&stpm->rootTimes, &stpm->BrTimes); + stpm->rootPortId = 0; + + updtRootPrio (this); + + for (port = stpm->ports; port; port = port->next) { + if (port->admin_non_stp) { + continue; + } + STP_VECT_create (&port->designPrio, + &stpm->rootPrio.root_bridge, + stpm->rootPrio.root_path_cost, + &stpm->BrId, port->port_id, port->port_id); + STP_copy_times (&port->designTimes, &stpm->rootTimes); + +#if 0 +#ifdef STP_DBG + if (port->roletrns->debug) { + STP_VECT_br_id_print ("ch:designPrio.design_bridge", + &port->designPrio.design_bridge, True); + } +#endif +#endif + } + + stpm->rootPortId = stpm->rootPrio.bridge_port; + +#ifdef STP_DBG + if (old_root_port != stpm->rootPortId) { + if (! stpm->rootPortId) { + stp_trace ("\nbrige %s became root", stpm->name); + } else { + stp_trace ("\nbrige %s new root port: %s", + stpm->name, + STP_stpm_get_port_name_by_id (stpm, stpm->rootPortId)); + } + } +#endif + + for (port = stpm->ports; port; port = port->next) { + if (port->admin_non_stp) { + setRoleSelected ("Non", stpm, port, NonStpPort); + port->forward = port->learn = True; + continue; + } + + switch (port->infoIs) { + case Disabled: + setRoleSelected ("Dis", stpm, port, DisabledPort); + break; + case Aged: + setRoleSelected ("Age", stpm, port, DesignatedPort); + port->updtInfo = True; + break; + case Mine: + setRoleSelected ("Mine", stpm, port, DesignatedPort); + if (0 != STP_VECT_compare_vector (&port->portPrio, + &port->designPrio) || + 0 != STP_compare_times (&port->portTimes, + &port->designTimes)) { + port->updtInfo = True; + } + break; + case Received: + if (stpm->rootPortId == port->port_id) { + setRoleSelected ("Rec", stpm, port, RootPort); + } else if (STP_VECT_compare_vector (&port->designPrio, &port->portPrio) < 0) { + /* Note: this important piece has been inserted after + * discussion with Mick Sieman and reading 802.1y Z1 */ + setRoleSelected ("Rec", stpm, port, DesignatedPort); + port->updtInfo = True; + break; + } else { + if (_is_backup_port (port, stpm)) { + setRoleSelected ("rec", stpm, port, BackupPort); + } else { + setRoleSelected ("rec", stpm, port, AlternatePort); + } + } + port->updtInfo = False; + break; + default: + stp_trace ("undef infoIs=%d", (int) port->infoIs); + break; + } + } + +} + + +static Bool +setSelectedBridge (STPM_T* this) +{ + register PORT_T* port; + + for (port = this->ports; port; port = port->next) { + if (port->reselect) { +#ifdef STP_DBG + stp_trace ("setSelectedBridge: TRUE=reselect on port %s", port->port_name); +#endif + return False; + } + } + + for (port = this->ports; port; port = port->next) { + port->selected = True; + } + + return True; +} + +void +STP_rolesel_enter_state (STATE_MACH_T* this) +{ + STPM_T* stpm; + + stpm = this->owner.stpm; + + switch (this->State) { + case BEGIN: + case INIT_BRIDGE: + updtRoleDisableBridge (stpm); + break; + case ROLE_SELECTION: + clearReselectBridge (stpm); + updtRolesBridge (this); + setSelectedBridge (stpm); + break; + } +} + +Bool +STP_rolesel_check_conditions (STATE_MACH_T* s) +{ + STPM_T* stpm; + register PORT_T* port; + + if (BEGIN == s->State) { + STP_hop_2_state (s, INIT_BRIDGE); + } + + switch (s->State) { + case BEGIN: + return STP_hop_2_state (s, INIT_BRIDGE); + case INIT_BRIDGE: + return STP_hop_2_state (s, ROLE_SELECTION); + case ROLE_SELECTION: + stpm = s->owner.stpm; + for (port = stpm->ports; port; port = port->next) { + if (port->reselect) { + /* stp_trace ("reselect on port %s", port->port_name); */ + return STP_hop_2_state (s, ROLE_SELECTION); + } + } + break; + } + + return False; +} + +void +STP_rolesel_update_stpm (STPM_T* this) +{ + register PORT_T* port; + PRIO_VECTOR_T rootPathPrio; /* 17.4.2.2 */ + + stp_trace ("%s", "??? STP_rolesel_update_stpm ???"); + STP_VECT_create (&rootPathPrio, &this->BrId, 0, &this->BrId, 0, 0); + + if (!this->rootPortId || + STP_VECT_compare_vector (&rootPathPrio, &this->rootPrio) < 0) { + STP_VECT_copy (&this->rootPrio, &rootPathPrio); + } + + for (port = this->ports; port; port = port->next) { + STP_VECT_create (&port->designPrio, + &this->rootPrio.root_bridge, + this->rootPrio.root_path_cost, + &this->BrId, port->port_id, port->port_id); + if (Received != port->infoIs || this->rootPortId == port->port_id) { + STP_VECT_copy (&port->portPrio, &port->designPrio); + } + port->reselect = True; + port->selected = False; + } +} + diff --git a/rstplib/rolesel.h b/rstplib/rolesel.h new file mode 100644 index 0000000..38dad55 --- /dev/null +++ b/rstplib/rolesel.h @@ -0,0 +1,41 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Role Selection state machine : 17.22 */ + +#ifndef _STP_ROLES_SELECT_H +#define _STP_ROLES_SELECT_H + +void +STP_rolesel_enter_state (STATE_MACH_T* s); + +Bool +STP_rolesel_check_conditions (STATE_MACH_T* s); + +void +STP_rolesel_update_stpm (struct stpm_t* this); + +char* +STP_rolesel_get_state_name (int state); + +#endif /* _STP_ROLES_SELECT_H */ + diff --git a/rstplib/roletrns.c b/rstplib/roletrns.c new file mode 100644 index 0000000..fb292a1 --- /dev/null +++ b/rstplib/roletrns.c @@ -0,0 +1,431 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Role Transitions state machine : 17.24 */ + +#include "base.h" + +#include "stpm.h" + +#define STATES { \ + CHOOSE(INIT_PORT), \ + CHOOSE(BLOCK_PORT), \ + CHOOSE(BLOCKED_PORT), \ + CHOOSE(BACKUP_PORT), \ + CHOOSE(ROOT_PROPOSED), \ + CHOOSE(ROOT_AGREED), \ + CHOOSE(REROOT), \ + CHOOSE(ROOT_PORT), \ + CHOOSE(REROOTED), \ + CHOOSE(ROOT_LEARN), \ + CHOOSE(ROOT_FORWARD), \ + CHOOSE(DESIGNATED_PROPOSE), \ + CHOOSE(DESIGNATED_SYNCED), \ + CHOOSE(DESIGNATED_RETIRED), \ + CHOOSE(DESIGNATED_PORT), \ + CHOOSE(DESIGNATED_LISTEN), \ + CHOOSE(DESIGNATED_LEARN), \ + CHOOSE(DESIGNATED_FORWARD), \ +} + +#define GET_STATE_NAME STP_roletrns_get_state_name +#include "choose.h" + +static void +setSyncBridge (STATE_MACH_T *this) +{ + register PORT_T* port; + + for (port = this->owner.port->owner->ports; port; port = port->next) { + port->sync = True; /* in ROOT_PROPOSED (setSyncBridge) */ + } +} + +static void +setReRootBridge (STATE_MACH_T *this) +{ + register PORT_T* port; + + for (port = this->owner.port->owner->ports; port; port = port->next) { + port->reRoot = True; /* In setReRootBridge */ + } +} + +static Bool +compute_all_synced (PORT_T* this) +{ + register PORT_T* port; + + for (port = this->owner->ports; port; port = port->next) { + if (port->port_index == this->port_index) continue; + if (! port->synced) { + return False; + } + } + + return True; +} + +static Bool +compute_re_rooted (PORT_T* this) +{ + register PORT_T* port; + + for (port = this->owner->ports; port; port = port->next) { + if (port->port_index == this->port_index) continue; + if (port->rrWhile) { + return False; + } + } + return True; +} + +void +STP_roletrns_enter_state (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + register STPM_T* stpm; + + stpm = port->owner; + + switch (this->State) { + case BEGIN: + case INIT_PORT: +#if 0 /* due 802.1y Z.4 */ + port->role = DisabledPort; +#else + port->role = port->selectedRole = DisabledPort; + port->reselect = True; +#endif + port->synced = False; /* in INIT */ + port->sync = True; /* in INIT */ + port->reRoot = True; /* in INIT_PORT */ + port->rrWhile = stpm->rootTimes.ForwardDelay; + port->fdWhile = stpm->rootTimes.ForwardDelay; + port->rbWhile = 0; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("after init", port); +#endif + break; + case BLOCK_PORT: + port->role = port->selectedRole; + port->learn = + port->forward = False; + break; + case BLOCKED_PORT: + port->fdWhile = stpm->rootTimes.ForwardDelay; + port->synced = True; /* In BLOCKED_PORT */ + port->rrWhile = 0; + port->sync = port->reRoot = False; /* BLOCKED_PORT */ + break; + case BACKUP_PORT: + port->rbWhile = 2 * stpm->rootTimes.HelloTime; + break; + + /* 17.23.2 */ + case ROOT_PROPOSED: + setSyncBridge (this); + port->proposed = False; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("ROOT_PROPOSED", port); +#endif + break; + case ROOT_AGREED: + port->proposed = port->sync = False; /* in ROOT_AGREED */ + port->synced = True; /* In ROOT_AGREED */ + port->newInfo = True; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("ROOT_AGREED", port); +#endif + break; + case REROOT: + setReRootBridge (this); +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("REROOT", port); +#endif + break; + case ROOT_PORT: + port->role = RootPort; + port->rrWhile = stpm->rootTimes.ForwardDelay; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("ROOT_PORT", port); +#endif + break; + case REROOTED: + port->reRoot = False; /* In REROOTED */ +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("REROOTED", port); +#endif + break; + case ROOT_LEARN: + port->fdWhile = stpm->rootTimes.ForwardDelay; + port->learn = True; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("ROOT_LEARN", port); +#endif + break; + case ROOT_FORWARD: + port->fdWhile = 0; + port->forward = True; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("ROOT_FORWARD", port); +#endif + break; + + /* 17.23.3 */ + case DESIGNATED_PROPOSE: + port->proposing = True; /* in DESIGNATED_PROPOSE */ + port->newInfo = True; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_PROPOSE", port); +#endif + break; + case DESIGNATED_SYNCED: + port->rrWhile = 0; + port->synced = True; /* DESIGNATED_SYNCED */ + port->sync = False; /* DESIGNATED_SYNCED */ +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_SYNCED", port); +#endif + break; + case DESIGNATED_RETIRED: + port->reRoot = False; /* DESIGNATED_RETIRED */ +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_RETIRED", port); +#endif + break; + case DESIGNATED_PORT: + port->role = DesignatedPort; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_PORT", port); +#endif + break; + case DESIGNATED_LISTEN: + port->learn = port->forward = False; + port->fdWhile = stpm->rootTimes.ForwardDelay; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_LISTEN", port); +#endif + break; + case DESIGNATED_LEARN: + port->learn = True; + port->fdWhile = stpm->rootTimes.ForwardDelay; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_LEARN", port); +#endif + break; + case DESIGNATED_FORWARD: + port->forward = True; + port->fdWhile = 0; +#ifdef STP_DBG + if (this->debug) + STP_port_trace_flags ("DESIGNATED_FORWARD", port); +#endif + break; + }; +} + +Bool +STP_roletrns_check_conditions (STATE_MACH_T* this) +{ + register PORT_T *port = this->owner.port; + register STPM_T *stpm; + Bool allSynced; + Bool allReRooted; + + stpm = port->owner; + + if (BEGIN == this->State) { + return STP_hop_2_state (this, INIT_PORT); + } + + if (port->role != port->selectedRole && + port->selected && + ! port->updtInfo) { + switch (port->selectedRole) { + case DisabledPort: + case AlternatePort: + case BackupPort: +#if 0 /* def STP_DBG */ + if (this->debug) { + stp_trace ("hop to BLOCK_PORT role=%d selectedRole=%d", + (int) port->role, (int) port->selectedRole); + } +#endif + return STP_hop_2_state (this, BLOCK_PORT); + case RootPort: + return STP_hop_2_state (this, ROOT_PORT); + case DesignatedPort: + return STP_hop_2_state (this, DESIGNATED_PORT); + default: + return False; + } + } + + switch (this->State) { + /* 17.23.1 */ + case INIT_PORT: + return STP_hop_2_state (this, BLOCK_PORT); + case BLOCK_PORT: + if (!port->selected || port->updtInfo) break; + if (!port->learning && !port->forwarding) { + return STP_hop_2_state (this, BLOCKED_PORT); + } + break; + case BLOCKED_PORT: + if (!port->selected || port->updtInfo) break; + if (port->fdWhile != stpm->rootTimes.ForwardDelay || + port->sync || + port->reRoot || + !port->synced) { + return STP_hop_2_state (this, BLOCKED_PORT); + } + if (port->rbWhile != 2 * stpm->rootTimes.HelloTime && + port->role == BackupPort) { + return STP_hop_2_state (this, BACKUP_PORT); + } + break; + case BACKUP_PORT: + return STP_hop_2_state (this, BLOCKED_PORT); + + /* 17.23.2 */ + case ROOT_PROPOSED: + return STP_hop_2_state (this, ROOT_PORT); + case ROOT_AGREED: + return STP_hop_2_state (this, ROOT_PORT); + case REROOT: + return STP_hop_2_state (this, ROOT_PORT); + case ROOT_PORT: + if (!port->selected || port->updtInfo) break; + if (!port->forward && !port->reRoot) { + return STP_hop_2_state (this, REROOT); + } + allSynced = compute_all_synced (port); + if ((port->proposed && allSynced) || + (!port->synced && allSynced)) { + return STP_hop_2_state (this, ROOT_AGREED); + } + if (port->proposed && !port->synced) { + return STP_hop_2_state (this, ROOT_PROPOSED); + } + + allReRooted = compute_re_rooted (port); + if ((!port->fdWhile || + ((allReRooted && !port->rbWhile) && stpm->ForceVersion >=2)) && + port->learn && !port->forward) { + return STP_hop_2_state (this, ROOT_FORWARD); + } + if ((!port->fdWhile || + ((allReRooted && !port->rbWhile) && stpm->ForceVersion >=2)) && + !port->learn) { + return STP_hop_2_state (this, ROOT_LEARN); + } + + if (port->reRoot && port->forward) { + return STP_hop_2_state (this, REROOTED); + } + if (port->rrWhile != stpm->rootTimes.ForwardDelay) { + return STP_hop_2_state (this, ROOT_PORT); + } + break; + case REROOTED: + return STP_hop_2_state (this, ROOT_PORT); + case ROOT_LEARN: + return STP_hop_2_state (this, ROOT_PORT); + case ROOT_FORWARD: + return STP_hop_2_state (this, ROOT_PORT); + + /* 17.23.3 */ + case DESIGNATED_PROPOSE: + return STP_hop_2_state (this, DESIGNATED_PORT); + case DESIGNATED_SYNCED: + return STP_hop_2_state (this, DESIGNATED_PORT); + case DESIGNATED_RETIRED: + return STP_hop_2_state (this, DESIGNATED_PORT); + case DESIGNATED_PORT: + if (!port->selected || port->updtInfo) break; + + if (!port->forward && !port->agreed && !port->proposing && !port->operEdge) { + return STP_hop_2_state (this, DESIGNATED_PROPOSE); + } + + if (!port->rrWhile && port->reRoot) { + return STP_hop_2_state (this, DESIGNATED_RETIRED); + } + + if (!port->learning && !port->forwarding && !port->synced) { + return STP_hop_2_state (this, DESIGNATED_SYNCED); + } + + if (port->agreed && !port->synced) { + return STP_hop_2_state (this, DESIGNATED_SYNCED); + } + if (port->operEdge && !port->synced) { + return STP_hop_2_state (this, DESIGNATED_SYNCED); + } + if (port->sync && port->synced) { + return STP_hop_2_state (this, DESIGNATED_SYNCED); + } + + if ((!port->fdWhile || port->agreed || port->operEdge) && + (!port->rrWhile || !port->reRoot) && + !port->sync && + (port->learn && !port->forward)) { + return STP_hop_2_state (this, DESIGNATED_FORWARD); + } + if ((!port->fdWhile || port->agreed || port->operEdge) && + (!port->rrWhile || !port->reRoot) && + !port->sync && !port->learn) { + return STP_hop_2_state (this, DESIGNATED_LEARN); + } + if (((port->sync && !port->synced) || + (port->reRoot && port->rrWhile)) && + !port->operEdge && (port->learn || port->forward)) { + return STP_hop_2_state (this, DESIGNATED_LISTEN); + } + break; + case DESIGNATED_LISTEN: + return STP_hop_2_state (this, DESIGNATED_PORT); + case DESIGNATED_LEARN: + return STP_hop_2_state (this, DESIGNATED_PORT); + case DESIGNATED_FORWARD: + return STP_hop_2_state (this, DESIGNATED_PORT); + }; + + return False; +} + + diff --git a/rstplib/roletrns.h b/rstplib/roletrns.h new file mode 100644 index 0000000..6ba116d --- /dev/null +++ b/rstplib/roletrns.h @@ -0,0 +1,37 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Role Transitions state machine : 17.24 */ + +#ifndef _STP_ROLES_TRANSIT_H__ +#define _STP_ROLES_TRANSIT_H__ + +void +STP_roletrns_enter_state (STATE_MACH_T* s); + +Bool +STP_roletrns_check_conditions (STATE_MACH_T* s); + +char* STP_roletrns_get_state_name (int state); + +#endif /* _STP_ROLES_TRANSIT_H__ */ + diff --git a/rstplib/statmch.c b/rstplib/statmch.c new file mode 100644 index 0000000..fcd5c44 --- /dev/null +++ b/rstplib/statmch.c @@ -0,0 +1,122 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Generic (abstract) state machine : 17.13, 17.14 */ + +#include "base.h" +#include "statmch.h" + +#if STP_DBG +# include "stpm.h" +#endif + +STATE_MACH_T * +STP_state_mach_create (void (*concreteEnterState) (STATE_MACH_T*), + Bool (*concreteCheckCondition) (STATE_MACH_T*), + char *(*concreteGetStatName) (int), + void *owner, char *name) +{ + STATE_MACH_T *this; + + STP_MALLOC(this, STATE_MACH_T, "state machine"); + + this->State = BEGIN; + this->name = (char*) strdup (name); + this->changeState = False; +#if STP_DBG + this->debug = False; + this->ignoreHop2State = BEGIN; +#endif + this->concreteEnterState = concreteEnterState; + this->concreteCheckCondition = concreteCheckCondition; + this->concreteGetStatName = concreteGetStatName; + this->owner.owner = owner; + + return this; +} + +void +STP_state_mach_delete (STATE_MACH_T *this) +{ + free (this->name); + STP_FREE(this, "state machine"); +} + +Bool +STP_check_condition (STATE_MACH_T* this) +{ + Bool bret; + + bret = (*(this->concreteCheckCondition)) (this); + if (bret) { + this->changeState = True; + } + + return bret; +} + +Bool +STP_change_state (STATE_MACH_T* this) +{ + register int number_of_loops; + + for (number_of_loops = 0; ; number_of_loops++) { + if (! this->changeState) return number_of_loops; + (*(this->concreteEnterState)) (this); + this->changeState = False; + STP_check_condition (this); + } + + return number_of_loops; +} + +Bool +STP_hop_2_state (STATE_MACH_T* this, unsigned int new_state) +{ +#ifdef STP_DBG + switch (this->debug) { + case 0: break; + case 1: + if (new_state == this->State || new_state == this->ignoreHop2State) break; + stp_trace ("%-8s(%s-%s): %s=>%s", + this->name, + *this->owner.port->owner->name ? this->owner.port->owner->name : "Glbl", + this->owner.port->port_name, + (*(this->concreteGetStatName)) (this->State), + (*(this->concreteGetStatName)) (new_state)); + break; + case 2: + if (new_state == this->State) break; + stp_trace ("%s(%s): %s=>%s", + this->name, + *this->owner.stpm->name ? this->owner.stpm->name : "Glbl", + (*(this->concreteGetStatName)) (this->State), + (*(this->concreteGetStatName)) (new_state)); + break; + } +#endif + + this->State = new_state; + this->changeState = True; + return True; +} + diff --git a/rstplib/statmch.h b/rstplib/statmch.h new file mode 100644 index 0000000..4b399c7 --- /dev/null +++ b/rstplib/statmch.h @@ -0,0 +1,87 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Generic (abstract state machine) state machine : 17.13, 17.14 */ + +#ifndef _STP_STATER_H__ +#define _STP_STATER_H__ + +#define BEGIN 9999 /* distinct from any valid state */ + +typedef struct state_mach_t { + struct state_mach_t* next; + + char* name; /* for debugging */ +#ifdef STP_DBG + char debug; /* 0- no dbg, 1 - port, 2 - stpm */ + unsigned int ignoreHop2State; +#endif + + Bool changeState; + unsigned int State; + + void (* concreteEnterState) (struct state_mach_t * ); + Bool (* concreteCheckCondition) (struct state_mach_t * ); + char* (* concreteGetStatName) (int); + union { + struct stpm_t* stpm; + struct port_t* port; + void * owner; + } owner; + +} STATE_MACH_T; + +#define STP_STATE_MACH_IN_LIST(WHAT) \ + { \ + STATE_MACH_T* abstr; \ + \ + abstr = STP_state_mach_create (STP_##WHAT##_enter_state, \ + STP_##WHAT##_check_conditions, \ + STP_##WHAT##_get_state_name, \ + this, \ + #WHAT); \ + abstr->next = this->machines; \ + this->machines = abstr; \ + this->WHAT = abstr; \ + } + + +STATE_MACH_T * +STP_state_mach_create (void (* concreteEnterState) (STATE_MACH_T*), + Bool (* concreteCheckCondition) (STATE_MACH_T*), + char * (* concreteGetStatName) (int), + void* owner, char* name); + +void +STP_state_mach_delete (STATE_MACH_T* this); + +Bool +STP_check_condition (STATE_MACH_T* this); + +Bool +STP_change_state (STATE_MACH_T* this); + +Bool +STP_hop_2_state (STATE_MACH_T* this, unsigned int new_state); + +#endif /* _STP_STATER_H__ */ + diff --git a/rstplib/stp_bpdu.h b/rstplib/stp_bpdu.h new file mode 100644 index 0000000..11fe7fa --- /dev/null +++ b/rstplib/stp_bpdu.h @@ -0,0 +1,91 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* BPDU formats: 9.1 - 9.3, 17.28 */ + +#ifndef _STP_BPDU_H__ +#define _STP_BPDU_H__ + +#define MIN_BPDU 7 +#define BPDU_L_SAP 0x42 +#define LLC_UI 0x03 +#define BPDU_PROTOCOL_ID 0x0000 +#define BPDU_VERSION_ID 0x00 +#define BPDU_VERSION_RAPID_ID 0x02 + +#define BPDU_TOPO_CHANGE_TYPE 0x80 +#define BPDU_CONFIG_TYPE 0x00 +#define BPDU_RSTP 0x02 + +#define TOLPLOGY_CHANGE_BIT 0x01 +#define PROPOSAL_BIT 0x02 +#define PORT_ROLE_OFFS 2 /* 0x04 & 0x08 */ +#define PORT_ROLE_MASK (0x03 << PORT_ROLE_OFFS) +#define LEARN_BIT 0x10 +#define FORWARD_BIT 0x20 +#define AGREEMENT_BIT 0x40 +#define TOLPLOGY_CHANGE_ACK_BIT 0x80 + +#define RSTP_PORT_ROLE_UNKN 0x00 +#define RSTP_PORT_ROLE_ALTBACK 0x01 +#define RSTP_PORT_ROLE_ROOT 0x02 +#define RSTP_PORT_ROLE_DESGN 0x03 + +typedef struct mac_header_t { + unsigned char dst_mac[6]; + unsigned char src_mac[6]; +} MAC_HEADER_T; + +typedef struct eth_header_t { + unsigned char len8023[2]; + unsigned char dsap; + unsigned char ssap; + unsigned char llc; +} ETH_HEADER_T; + +typedef struct bpdu_header_t { + unsigned char protocol[2]; + unsigned char version; + unsigned char bpdu_type; +} BPDU_HEADER_T; + +typedef struct bpdu_body_t { + unsigned char flags; + unsigned char root_id[8]; + unsigned char root_path_cost[4]; + unsigned char bridge_id[8]; + unsigned char port_id[2]; + unsigned char message_age[2]; + unsigned char max_age[2]; + unsigned char hello_time[2]; + unsigned char forward_delay[2]; +} BPDU_BODY_T; + +typedef struct stp_bpdu_t { + ETH_HEADER_T eth; + BPDU_HEADER_T hdr; + BPDU_BODY_T body; + unsigned char ver_1_len[2]; +} BPDU_T; + +#endif /* _STP_BPDU_H__ */ + diff --git a/rstplib/stp_cli.c b/rstplib/stp_cli.c new file mode 100644 index 0000000..459c8fe --- /dev/null +++ b/rstplib/stp_cli.c @@ -0,0 +1,796 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> + +#include "cli.h" +#include "stp_cli.h" +#include "bitmap.h" +#include "uid_stp.h" +#include "stp_in.h" +#include "stp_to.h" + +int I_am_a_stupid_hub = 0; + +static void +print_bridge_id (UID_BRIDGE_ID_T *bridge_id, unsigned char cr) +{ + printf("%04lX-%02x%02x%02x%02x%02x%02x", + (unsigned long) bridge_id->prio, + (unsigned char) bridge_id->addr[0], + (unsigned char) bridge_id->addr[1], + (unsigned char) bridge_id->addr[2], + (unsigned char) bridge_id->addr[3], + (unsigned char) bridge_id->addr[4], + (unsigned char) bridge_id->addr[5]); + if (cr) + printf("\n"); +} + +static char * +stp_state2str (RSTP_PORT_STATE stp_port_state, int detail) +{ + if (detail) { + switch (stp_port_state) { + case UID_PORT_DISABLED: return "Disabled"; + case UID_PORT_DISCARDING: return "Discarding"; + case UID_PORT_LEARNING: return "Learning"; + case UID_PORT_FORWARDING: return "Forwarding"; + case UID_PORT_NON_STP: return "NoStp"; + default: return "Unknown"; + } + } + + switch (stp_port_state) { + case UID_PORT_DISABLED: return "Dis"; + case UID_PORT_DISCARDING: return "Blk"; + case UID_PORT_LEARNING: return "Lrn"; + case UID_PORT_FORWARDING: return "Fwd"; + case UID_PORT_NON_STP: return "Non"; + default: return "Unk"; + } +} + +static void CLI_out_port_id (int port, unsigned char cr) +{ + printf ("%s", STP_OUT_get_port_name (port)); + if (cr) + printf("\n"); +} + +static int cli_enable (int argc, char** argv) +{ + UID_STP_CFG_T uid_cfg; + int rc; + + uid_cfg.field_mask = BR_CFG_STATE; + uid_cfg.stp_enabled = STP_ENABLED; + rc = STP_IN_stpm_set_cfg (0, NULL, &uid_cfg); + if (rc) { + printf ("can't enable: %s\n", STP_IN_get_error_explanation (rc)); + } else + I_am_a_stupid_hub = 0; + + return 0; +} + +static int cli_disable (int argc, char** argv) +{ + UID_STP_CFG_T uid_cfg; + int rc; + + uid_cfg.field_mask = BR_CFG_STATE; + uid_cfg.stp_enabled = STP_DISABLED; + rc = STP_IN_stpm_set_cfg (0, NULL, &uid_cfg); + if (rc) { + printf ("can't disable: %s\n", STP_IN_get_error_explanation (rc)); + } else + I_am_a_stupid_hub = 1; + + return 0; +} + +static int cli_br_get_cfg (int argc, char** argv) +{ + UID_STP_STATE_T uid_state; + UID_STP_CFG_T uid_cfg; + int rc; + + rc = STP_IN_stpm_get_state (0, &uid_state); + if (rc) { + printf ("can't get rstp bridge state: %s\n", STP_IN_get_error_explanation (rc)); + return 0; + } + rc = STP_IN_stpm_get_cfg (0, &uid_cfg); + if (rc) { + printf ("can't get rstp bridge configuration: %s\n", STP_IN_get_error_explanation (rc)); + return 0; + } + + +#if 0 + printf("Interface: %-7s (tag:%d) State: ", + uid_state.vlan_name, (int) uid_state.vlan_id); +#else + printf("Bridge: %-7s State:", + uid_state.vlan_name); +#endif + switch (uid_state.stp_enabled) { + case STP_ENABLED: printf("enabled\n"); break; + case STP_DISABLED: printf("disabled\n");break; + default: printf("unknown\n"); return 0; + } + + printf("BridgeId: "); print_bridge_id (&uid_state.bridge_id, 0); + printf(" Bridge Proirity: %lu (0x%lX)\n", + (unsigned long) uid_state.bridge_id.prio, (unsigned long) uid_state.bridge_id.prio); + if (uid_cfg.force_version < 2) + printf("Force Version: stp\n"); + + printf("Designated Root: "); print_bridge_id (&uid_state.designated_root, 1); + if (uid_state.root_port) { + printf("Root Port: %04lx (", (unsigned long) uid_state.root_port); + CLI_out_port_id (uid_state.root_port & 0xfff, False); + printf("), Root Cost: %-lu\n", (unsigned long) uid_state.root_path_cost); + } else { + printf("Root Port: none\n"); + } + + if (uid_state.Topo_Change) + printf ("Topology Change Count: %lu\n", uid_state.Topo_Change_Count); + else + printf ("Time Since Topology Change: %lu\n", uid_state.timeSince_Topo_Change); + + printf ("Max Age: %2d Bridge Max Age: %-2d\n", + (int) uid_state.max_age, (int) uid_cfg.max_age); + printf ("Hello Time: %2d Bridge Hello Time: %-2d\n", + (int) uid_state.hello_time, (int) uid_cfg.hello_time); + printf ("Forward Delay: %2d Bridge Forward Delay: %-2d\n", + (int) uid_state.forward_delay, (int) uid_cfg.forward_delay); + printf ("Hold Time: %2d\n", (int) uid_cfg.hold_time); + + return 0; +} + +static void +show_rstp_port (BITMAP_T* ports_bitmap, int detail) +{ + UID_STP_STATE_T uid_state; + UID_STP_PORT_STATE_T uid_port; + UID_STP_PORT_CFG_T uid_cfg; + int port_index; + int rc; + + rc = STP_IN_stpm_get_state (0, &uid_state); + if (rc) { + printf ("can't get rstp bridge state: %s\n", STP_IN_get_error_explanation (rc)); + } else if (! detail) { + printf (" BridgeId: "); print_bridge_id (&uid_state.bridge_id, 0); + printf (" RootId: "); print_bridge_id (&uid_state.designated_root, 1); + } + + for (port_index = 0; port_index <= NUMBER_OF_PORTS; port_index++) { + if (! BitmapGetBit(ports_bitmap, port_index - 1)) continue; + uid_port.port_no = port_index; + rc = STP_IN_port_get_state (0, &uid_port); + if (rc) { + printf ("can't get rstp port state: %s\n", STP_IN_get_error_explanation (rc)); + continue; + } + memset (&uid_cfg, 0, sizeof (UID_STP_PORT_CFG_T)); + rc = STP_IN_port_get_cfg (0, uid_port.port_no, &uid_cfg); + if (rc) { + printf ("can't get rstp port config: %s\n", STP_IN_get_error_explanation (rc)); + continue; + } + + if (detail) { + printf("Stp Port "); CLI_out_port_id (port_index, False); +#if 0 + printf(": PortId: %04lx in vlan '%s' with tag %d:\n", + (unsigned long) uid_port.port_id, uid_state.vlan_name, (int) uid_state.vlan_id); +#else + printf(": PortId: %04lx in Bridge '%s':\n", + (unsigned long) uid_port.port_id, uid_state.vlan_name); +#endif + printf ("Priority: %-d\n", (int) (uid_port.port_id >> 8)); + printf ("State: %-16s", stp_state2str (uid_port.state, 1)); + printf (" Uptime: %-9lu\n", uid_port.uptime); + printf ("PortPathCost: admin: "); + if (ADMIN_PORT_PATH_COST_AUTO == uid_cfg.admin_port_path_cost) + printf ("%-9s", "Auto"); + else + printf ("%-9lu", uid_cfg.admin_port_path_cost); + printf (" oper: %-9lu\n", uid_port.oper_port_path_cost); + + printf ("Point2Point: admin: "); + switch (uid_cfg.admin_point2point) { + case P2P_FORCE_TRUE: + printf ("%-9s", "ForceYes"); + break; + case P2P_FORCE_FALSE: + printf ("%-9s", "ForceNo"); + break; + case P2P_AUTO: + printf ("%-9s", "Auto"); + break; + } + printf (" oper: %-9s\n", uid_port.oper_point2point ? "Yes" : "No"); + printf ("Edge: admin: %-9s oper: %-9s\n", + uid_cfg.admin_edge ? "Y" : "N", + uid_port.oper_edge ? "Y" : "N"); + printf ("Partner: oper: %-9s\n", + uid_port.oper_stp_neigb ? "Slow" : "Rapid"); + + if (' ' != uid_port.role) { + if ('-' != uid_port.role) { + printf("PathCost: %-lu\n", (unsigned long) (uid_port.path_cost)); + printf("Designated Root: "); print_bridge_id (&uid_port.designated_root, 1); + printf("Designated Cost: %-ld\n", (unsigned long) uid_port.designated_cost); + printf("Designated Bridge: "); print_bridge_id (&uid_port.designated_bridge, 1); + printf("Designated Port: %-4lx\n\r", (unsigned long) uid_port.designated_port); + } + printf("Role: "); + switch (uid_port.role) { + case 'A': printf("Alternate\n"); break; + case 'B': printf("Backup\n"); break; + case 'R': printf("Root\n"); break; + case 'D': printf("Designated\n"); break; + case '-': printf("NonStp\n"); break; + default: printf("Unknown(%c)\n", uid_port.role); break; + } + + if ('R' == uid_port.role || 'D' == uid_port.role) { + /* printf("Tc: %c ", uid_port.tc ? 'Y' : 'n'); */ + printf("TcAck: %c ", + uid_port.top_change_ack ? 'Y' : 'N'); + printf("TcWhile: %3d\n", (int) uid_port.tcWhile); + } + } + + if (UID_PORT_DISABLED == uid_port.state || '-' == uid_port.role) { +#if 0 + printf("helloWhen: %3d ", (int) uid_port.helloWhen); + printf("lnkWhile: %3d\n", (int) uid_port.lnkWhile); + printf("fdWhile: %3d\n", (int) uid_port.fdWhile); +#endif + } else if ('-' != uid_port.role) { + printf("fdWhile: %3d ", (int) uid_port.fdWhile); + printf("rcvdInfoWhile: %3d\n", (int) uid_port.rcvdInfoWhile); + printf("rbWhile: %3d ", (int) uid_port.rbWhile); + printf("rrWhile: %3d\n", (int) uid_port.rrWhile); +#if 0 + printf("mdelayWhile: %3d ", (int) uid_port.mdelayWhile); + printf("lnkWhile: %3d\n", (int) uid_port.lnkWhile); + printf("helloWhen: %3d ", (int) uid_port.helloWhen); + printf("txCount: %3d\n", (int) uid_port.txCount); +#endif + } + + printf("RSTP BPDU rx: %lu\n", (unsigned long) uid_port.rx_rstp_bpdu_cnt); + printf("CONFIG BPDU rx: %lu\n", (unsigned long) uid_port.rx_cfg_bpdu_cnt); + printf("TCN BPDU rx: %lu\n", (unsigned long) uid_port.rx_tcn_bpdu_cnt); + } else { + printf("%c%c%c ", + (uid_port.oper_point2point) ? ' ' : '*', + (uid_port.oper_edge) ? 'E' : ' ', + (uid_port.oper_stp_neigb) ? 's' : ' '); + CLI_out_port_id (port_index, False); + printf(" %04lx %3s ", (unsigned long) uid_port.port_id, + stp_state2str (uid_port.state, 0)); + printf (" "); + print_bridge_id (&uid_port.designated_root, 0); + printf(" "); + print_bridge_id (&uid_port.designated_bridge, 0); + printf(" %4lx %c", (unsigned long) uid_port.designated_port, uid_port.role); + printf ("\n"); + } + } +} + +static int cli_pr_get_cfg (int argc, char** argv) +{ + BITMAP_T ports_bitmap; + int port_index; + char detail; + + if ('a' == argv[1][0]) { + BitmapSetAllBits(&ports_bitmap); + detail = 0; + } else { + port_index = strtoul(argv[1], 0, 10); + BitmapClear(&ports_bitmap); + BitmapSetBit(&ports_bitmap, port_index - 1); + detail = 1; + } + + show_rstp_port (&ports_bitmap, detail); + + return 0; +} + +static void +set_bridge_cfg_value (unsigned long value, unsigned long val_mask) +{ + UID_STP_CFG_T uid_cfg; + char* val_name; + int rc; + + uid_cfg.field_mask = val_mask; + switch (val_mask) { + case BR_CFG_STATE: + uid_cfg.stp_enabled = value; + val_name = "state"; + break; + case BR_CFG_PRIO: + uid_cfg.bridge_priority = value; + val_name = "priority"; + break; + case BR_CFG_AGE: + uid_cfg.max_age = value; + val_name = "max_age"; + break; + case BR_CFG_HELLO: + uid_cfg.hello_time = value; + val_name = "hello_time"; + break; + case BR_CFG_DELAY: + uid_cfg.forward_delay = value; + val_name = "forward_delay"; + break; + case BR_CFG_FORCE_VER: + uid_cfg.force_version = value; + val_name = "force_version"; + break; + case BR_CFG_AGE_MODE: + case BR_CFG_AGE_TIME: + default: printf ("Invalid value mask 0X%lx\n", val_mask); return; + break; + } + + rc = STP_IN_stpm_set_cfg (0, NULL, &uid_cfg); + + if (0 != rc) { + printf ("Can't change rstp bridge %s:%s", val_name, STP_IN_get_error_explanation (rc)); + } else { + printf ("Changed rstp bridge %s\n", val_name); + } +} + +static int cli_br_prio (int argc, char** argv) +{ + long br_prio = 32768L; + + if (strlen (argv[1]) > 2 && + (! strncmp (argv[1], "0x", 2) || ! strncmp (argv[1], "0X", 2))) { + br_prio = strtoul(argv[1] + 2, 0, 16); + } else { + br_prio = strtoul(argv[1], 0, 10); + } + + if (! br_prio) { + printf ("Warning: newPriority=0, are you sure ?\n"); + } + + set_bridge_cfg_value (br_prio, BR_CFG_PRIO); + + return 0; +} + +static int cli_br_maxage (int argc, char** argv) +{ + long value = 20L; + + value = strtoul(argv[1], 0, 10); + set_bridge_cfg_value (value, BR_CFG_AGE); + return 0; +} + +static int cli_br_fdelay (int argc, char** argv) +{ + long value = 15L; + + value = strtoul(argv[1], 0, 10); + set_bridge_cfg_value (value, BR_CFG_DELAY); + return 0; +} + +static int cli_br_fvers (int argc, char** argv) +{ + long value = 2L; + + switch (argv[1][0]) { + case '0': + case '1': + case 'f': + case 'F': + value = 0L; + printf ("Accepted 'force_slow'\n"); + break; + case '2': + case 'r': + case 'R': + printf ("Accepted 'rapid'\n"); + value = 2L; + break; + default: + printf ("Invalid argument '%s'\n", argv[1]); + return 0; + } + + set_bridge_cfg_value (value, BR_CFG_FORCE_VER); + return 0; +} + +static void +set_rstp_port_cfg_value (int port_index, + unsigned long value, + unsigned long val_mask) +{ + UID_STP_PORT_CFG_T uid_cfg; + int rc, detail; + char *val_name; + + if (port_index > 0) { + BitmapClear(&uid_cfg.port_bmp); + BitmapSetBit(&uid_cfg.port_bmp, port_index - 1); + detail = 1; + } else { + BitmapSetAllBits(&uid_cfg.port_bmp); + detail = 0; + } + + uid_cfg.field_mask = val_mask; + switch (val_mask) { + case PT_CFG_MCHECK: + val_name = "mcheck"; + break; + case PT_CFG_COST: + uid_cfg.admin_port_path_cost = value; + val_name = "path cost"; + break; + case PT_CFG_PRIO: + uid_cfg.port_priority = value; + val_name = "priority"; + break; + case PT_CFG_P2P: + uid_cfg.admin_point2point = (ADMIN_P2P_T) value; + val_name = "p2p flag"; + break; + case PT_CFG_EDGE: + uid_cfg.admin_edge = value; + val_name = "adminEdge"; + break; + case PT_CFG_NON_STP: + uid_cfg.admin_non_stp = value; + val_name = "adminNonStp"; + break; +#ifdef STP_DBG + case PT_CFG_DBG_SKIP_TX: + uid_cfg.skip_tx = value; + val_name = "skip tx"; + break; + case PT_CFG_DBG_SKIP_RX: + uid_cfg.skip_rx = value; + val_name = "skip rx"; + break; +#endif + case PT_CFG_STATE: + default: + printf ("Invalid value mask 0X%lx\n", val_mask); + return; + } + + rc = STP_IN_set_port_cfg (0, &uid_cfg); + if (0 != rc) { + printf ("can't change rstp port[s] %s: %s\n", + val_name, STP_IN_get_error_explanation (rc)); + } else { + printf ("changed rstp port[s] %s\n", val_name); + } + + /* show_rstp_port (&uid_cfg.port_bmp, 0); */ +} + +static int cli_prt_prio (int argc, char** argv) +{ + int port_index = 0; + unsigned long value = 128; + + if ('a' != argv[1][0]) + port_index = strtoul(argv[1], 0, 10); + + value = strtoul(argv[2], 0, 10); + set_rstp_port_cfg_value (port_index, value, PT_CFG_PRIO); + return 0; +} + +static int cli_prt_pcost (int argc, char** argv) +{ + int port_index = 0; + unsigned long value = 0; + + if ('a' != argv[1][0]) + port_index = strtoul(argv[1], 0, 10); + + value = strtoul(argv[2], 0, 10); + set_rstp_port_cfg_value (port_index, value, PT_CFG_COST); + return 0; +} + +static int cli_prt_mcheck (int argc, char** argv) +{ + int port_index = 0; + + if ('a' != argv[1][0]) + port_index = strtoul(argv[1], 0, 10); + set_rstp_port_cfg_value (port_index, 0, PT_CFG_MCHECK); + return 0; +} + +static int get_bool_arg (int narg, int argc, char** argv, + unsigned long* value) +{ + switch (argv[narg][0]) { + case 'y': + case 'Y': + *value = 1; + break; + case 'n': + case 'N': + *value = 0; + break; + default: + printf ("Invalid Bollean parameter '%s'\n", argv[narg]); + return -1; + } + return 0; +} + +static int cli_prt_edge (int argc, char** argv) +{ + int port_index = 0; + unsigned long value = 1; + + if ('a' != argv[1][0]) + port_index = strtoul(argv[1], 0, 10); + + if (0 != get_bool_arg (2, argc, argv, &value)) + return 0; + + set_rstp_port_cfg_value (port_index, value, PT_CFG_EDGE); + return 0; +} + +static int cli_prt_non_stp (int argc, char** argv) +{ + int port_index = 0; + unsigned long value = 0; + + if ('a' != argv[1][0]) + port_index = strtoul(argv[1], 0, 10); + + if (0 != get_bool_arg (2, argc, argv, &value)) + return 0; + + set_rstp_port_cfg_value (port_index, value, PT_CFG_NON_STP); + return 0; +} + +static int cli_prt_p2p (int argc, char** argv) +{ + int port_index = 0; + unsigned long value = P2P_FORCE_TRUE; + + if ('a' != argv[1][0]) + port_index = strtoul(argv[1], 0, 10); + + switch (argv[2][0]) { + case 'y': + case 'Y': + value = P2P_FORCE_TRUE; + break; + case 'n': + case 'N': + value = P2P_FORCE_FALSE; + break; + case 'a': + case 'A': + value = P2P_AUTO; + break; + default: + printf ("Invalid parameter '%s'\n", argv[2]); + return 0; + } + + set_rstp_port_cfg_value (port_index, (ADMIN_P2P_T) value, PT_CFG_P2P); + return 0; +} + +#ifdef STP_DBG +static int cli_trace (int argc, char** argv) +{ + BITMAP_T ports_bitmap; + int port_index; + + if ('a' == argv[1][0]) { + BitmapSetAllBits(&ports_bitmap); + } else { + port_index = strtoul(argv[1], 0, 10); + BitmapClear(&ports_bitmap); + BitmapSetBit(&ports_bitmap, port_index - 1); + } + + STP_IN_dbg_set_port_trace (argv[2], + argv[3][0] != 'n' && argv[3][0] != 'N', + 0, &ports_bitmap, + 1); + return 0; +} + +/**** + PARAM_NUMBER("port number", 1, NUMBER_OF_PORTS, "all") + PARAM_ENUM("receive or/and transmit") + PARAM_ENUM_SEL("rx", "receive") + PARAM_ENUM_SEL("tx", "transmit") + PARAM_ENUM_DEFAULT("all") + PARAM_NUMBER("number of BPDU to skip", 0, 10000, "1") +****/ +static int cli_skip (int argc, char** argv) +{ + int port_index = 0, to_skip; + + if ('a' != argv[1][0]) + port_index = strtoul(argv[1], 0, 10); + + to_skip = atoi (argv[3]); + + if ('a' == argv[2][0] || 'r' == argv[2][0]) { + set_rstp_port_cfg_value (port_index, to_skip, PT_CFG_DBG_SKIP_RX); + } + + if ('a' == argv[2][0] || 't' == argv[2][0]) { + set_rstp_port_cfg_value (port_index, to_skip, PT_CFG_DBG_SKIP_TX); + } + return 0; +} + +static int cli_sleep (int argc, char** argv) +{ + int delay = atoi (argv[1]); + sleep (delay); + return 0; +} + +#endif + +static CMD_DSCR_T lang[] = { + THE_COMMAND("enable", "enable rstp") + THE_FUNC(cli_enable) + + THE_COMMAND("disable", "disable rstp") + THE_FUNC(cli_disable) + + THE_COMMAND("show bridge", "get bridge config") + THE_FUNC(cli_br_get_cfg) + + THE_COMMAND("show port", "get port config") + PARAM_NUMBER("port number on bridge", 1, NUMBER_OF_PORTS, "all") + THE_FUNC(cli_pr_get_cfg) + + THE_COMMAND("bridge priority", "set bridge priority") + PARAM_NUMBER("priority", MIN_BR_PRIO, MAX_BR_PRIO, "0x8000") + THE_FUNC(cli_br_prio) + + THE_COMMAND("bridge maxage", "set bridge maxAge") + PARAM_NUMBER("maxAge", MIN_BR_MAXAGE, MAX_BR_MAXAGE, "20") + THE_FUNC(cli_br_maxage) + + THE_COMMAND("bridge fdelay", "set bridge forwardDelay") + PARAM_NUMBER("forwardDelay", MIN_BR_FWDELAY, MAX_BR_FWDELAY, "15") + THE_FUNC(cli_br_fdelay) + + THE_COMMAND("bridge forseVersion", "set bridge forseVersion") + PARAM_BOOL("forseVersion", "forse slow", "regular", "no") + THE_FUNC(cli_br_fvers) + + THE_COMMAND("port priority", "set port priority") + PARAM_NUMBER("port number", 1, NUMBER_OF_PORTS, "all") + PARAM_NUMBER("priority", MIN_PORT_PRIO, MAX_PORT_PRIO, "128") + THE_FUNC(cli_prt_prio) + + THE_COMMAND("port pcost", "set port path cost") + PARAM_NUMBER("port number", 1, NUMBER_OF_PORTS, "all") + PARAM_NUMBER("path cost (0- for auto)", 0, 200000000, 0) + THE_FUNC(cli_prt_pcost) + + THE_COMMAND("port mcheck", "set port mcheck") + PARAM_NUMBER("port number", 1, NUMBER_OF_PORTS, "all") + THE_FUNC(cli_prt_mcheck) + + THE_COMMAND("port edge", "set port adminEdge") + PARAM_NUMBER("port number", 1, NUMBER_OF_PORTS, "all") + PARAM_BOOL("adminEdge", "Edge", "noEdge", "Y") + THE_FUNC(cli_prt_edge) + + THE_COMMAND("port nonStp", "set port adminNonStp") + PARAM_NUMBER("port number", 1, NUMBER_OF_PORTS, "all") + PARAM_BOOL("adminEdge", "Doesn't participate", "Paricipates", "n") + THE_FUNC(cli_prt_non_stp) + + THE_COMMAND("port p2p", "set port adminPoit2Point") + PARAM_NUMBER("port number", 1, NUMBER_OF_PORTS, "all") + PARAM_ENUM("adminPoit2Point") + PARAM_ENUM_SEL("y", "forcePointToPoint") + PARAM_ENUM_SEL("n", "forcePointToMultiPoint") + PARAM_ENUM_SEL("a", "autoPointToPoint") + PARAM_ENUM_DEFAULT("a") + THE_FUNC(cli_prt_p2p) + +#ifdef STP_DBG + THE_COMMAND("trace", "set port trace") + PARAM_NUMBER("port number", 1, NUMBER_OF_PORTS, "all") + PARAM_ENUM("state machine name") + PARAM_ENUM_SEL("info", "info") + PARAM_ENUM_SEL("roletrns", "roletrns") + PARAM_ENUM_SEL("sttrans", "sttrans") + PARAM_ENUM_SEL("topoch", "topoch") + PARAM_ENUM_SEL("migrate", "migrate") + PARAM_ENUM_SEL("transmit", "transmit") + PARAM_ENUM_SEL("p2p", "p2p") + PARAM_ENUM_SEL("edge", "edge") + PARAM_ENUM_SEL("pcost", "pcost") + PARAM_ENUM_DEFAULT("all") + PARAM_BOOL("enable/disable", "trace it", "don't trace it", "n") + THE_FUNC(cli_trace) + + THE_COMMAND("skip", "skip BPDU processing") + PARAM_NUMBER("port number", 1, NUMBER_OF_PORTS, "all") + PARAM_ENUM("receive or/and transmit") + PARAM_ENUM_SEL("rx", "receive") + PARAM_ENUM_SEL("tx", "transmit") + PARAM_ENUM_DEFAULT("all") + PARAM_NUMBER("number of BPDU to skip", 0, 10000, "1") + THE_FUNC(cli_skip) + + THE_COMMAND("sleep", "sleep") + PARAM_NUMBER("delay in sec.", 1, 4000, "4") + THE_FUNC(cli_sleep) +#endif + + END_OF_LANG +}; + +int stp_cli_init (void) +{ + I_am_a_stupid_hub = 0; + cli_register_language (lang); + return 0; +} + diff --git a/rstplib/stp_cli.h b/rstplib/stp_cli.h new file mode 100644 index 0000000..56484dc --- /dev/null +++ b/rstplib/stp_cli.h @@ -0,0 +1,30 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#ifndef _STP_CLI__ +#define _STP_CLI__ + +extern int I_am_a_stupid_hub; + +int stp_cli_init (); + +#endif /* _STP_CLI__ */ diff --git a/rstplib/stp_in.c b/rstplib/stp_in.c new file mode 100644 index 0000000..31a2c9f --- /dev/null +++ b/rstplib/stp_in.c @@ -0,0 +1,1095 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* This file contains API from an operation system to the RSTP library */ + +#include "base.h" +#include "stpm.h" +#include "stp_in.h" +#include "stp_to.h" + +int max_port = 1024; + +#define INCR100(nev) { nev++; if (nev > 99) nev = 0;} + +RSTP_EVENT_T tev = RSTP_EVENT_LAST_DUMMY; +int nev = 0; + +void * +stp_in_stpm_create (int vlan_id, char* name, BITMAP_T* port_bmp, int* err_code) +{ + int port_index; + register STPM_T* this; + + /* stp_trace ("stp_in_stpm_create(%s)", name); */ + this = stpapi_stpm_find (vlan_id); + if (this) { /* it had just been created :( */ + *err_code = STP_Nothing_To_Do; + return this; + } + + this = STP_stpm_create (vlan_id, name); + if (! this) { /* can't create stpm :( */ + *err_code = STP_Cannot_Create_Instance_For_Vlan; + return NULL; + } + + for (port_index = 1; port_index <= max_port; port_index++) { + if (BitmapGetBit(port_bmp, (port_index - 1))) { + if (! STP_port_create (this, port_index)) { + /* can't add port :( */ + stp_trace ("can't create port %d", (int) port_index); + STP_stpm_delete (this); + *err_code =STP_Cannot_Create_Instance_For_Port; + return NULL; + } + } + } + + *err_code = STP_OK; + return this; +} + +int +_stp_in_stpm_enable (int vlan_id, char* name, + BITMAP_T* port_bmp, + UID_STP_MODE_T admin_state) +{ + register STPM_T* this; + Bool created_here = False; + int rc, err_code; + + /* stp_trace ("_stp_in_stpm_enable(%s)", name); */ + this = stpapi_stpm_find (vlan_id); + + if (STP_DISABLED != admin_state) { + if (! vlan_id) { /* STP_IN_stop_all (); */ + register STPM_T* stpm; + + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (STP_DISABLED != stpm->admin_state) { + STP_OUT_set_hardware_mode (stpm->vlan_id, STP_DISABLED); + STP_stpm_enable (stpm, STP_DISABLED); + } + } + } + } + + if (! this) { /* it had not yet been created */ + if (STP_ENABLED == admin_state) {/* try to create it */ + stp_trace ("implicit create to vlan '%s'", name); + this = stp_in_stpm_create (vlan_id, name, port_bmp, &err_code); + if (! this) { + stp_trace ("implicit create to vlan '%s' failed", name); + return STP_Imlicite_Instance_Create_Failed; + } + created_here = True; + } else {/* try to disable nothing ? */ + return 0; + } + } + + if (this->admin_state == admin_state) { /* nothing to do :) */ + return 0; + } + + rc = STP_stpm_enable (this, admin_state); + if (! rc) { + STP_OUT_set_hardware_mode (vlan_id, admin_state); + } + + if (rc && created_here) { + STP_stpm_delete (this); + } + + return rc; +} + + +STPM_T * +stpapi_stpm_find (int vlan_id) +{ + register STPM_T* this; + + for (this = STP_stpm_get_the_list (); this; this = this->next) + if (vlan_id == this->vlan_id) + return this; + + return NULL; +} + +static PORT_T * +_stpapi_port_find (STPM_T* this, int port_index) +{ + register PORT_T* port; + + for (port = this->ports; port; port = port->next) + if (port_index == port->port_index) { + return port; + } + + return NULL; +} + + +static void +_conv_br_id_2_uid (IN BRIDGE_ID* f, OUT UID_BRIDGE_ID_T* t) +{ + memcpy (t, f, sizeof (UID_BRIDGE_ID_T)); +} + +static int +_check_stpm_config (IN UID_STP_CFG_T* uid_cfg) +{ + if (uid_cfg->bridge_priority < MIN_BR_PRIO) { + stp_trace ("%d bridge_priority small", (int) uid_cfg->bridge_priority); + return STP_Small_Bridge_Priority; + } + + if (uid_cfg->bridge_priority > MAX_BR_PRIO) { + stp_trace ("%d bridge_priority large", (int) uid_cfg->bridge_priority); + return STP_Large_Bridge_Priority; + } + + if (uid_cfg->bridge_priority & ~MASK_BR_PRIO) { + stp_trace ("%d bridge_priority must be a multiple of 4096", (int) uid_cfg->bridge_priority); + return STP_Bridge_Priority_Not_A_Multiple_Of_4096; + } + + if (uid_cfg->hello_time < MIN_BR_HELLOT) { + stp_trace ("%d hello_time small", (int) uid_cfg->hello_time); + return STP_Small_Hello_Time; + } + + if (uid_cfg->hello_time > MAX_BR_HELLOT) { + stp_trace ("%d hello_time large", (int) uid_cfg->hello_time); + return STP_Large_Hello_Time; + } + + if (uid_cfg->max_age < MIN_BR_MAXAGE) { + stp_trace ("%d max_age small", (int) uid_cfg->max_age); + return STP_Small_Max_Age; + } + + if (uid_cfg->max_age > MAX_BR_MAXAGE) { + stp_trace ("%d max_age large", (int) uid_cfg->max_age); + return STP_Large_Max_Age; + } + + if (uid_cfg->forward_delay < MIN_BR_FWDELAY) { + stp_trace ("%d forward_delay small", (int) uid_cfg->forward_delay); + return STP_Small_Forward_Delay; + } + + if (uid_cfg->forward_delay > MAX_BR_FWDELAY) { + stp_trace ("%d forward_delay large", (int) uid_cfg->forward_delay); + return STP_Large_Forward_Delay; + } + + if (2 * (uid_cfg->forward_delay - 1) < uid_cfg->max_age) { + return STP_Forward_Delay_And_Max_Age_Are_Inconsistent; + } + + if (uid_cfg->max_age < 2 * (uid_cfg->hello_time + 1)) { + return STP_Hello_Time_And_Max_Age_Are_Inconsistent; + } + + return 0; +} + +static void +_stp_in_enable_port_on_stpm (STPM_T* stpm, int port_index, Bool enable) +{ + register PORT_T* port; + + port = _stpapi_port_find (stpm, port_index); + if (! port) return; + if (port->portEnabled == enable) {/* nothing to do :) */ + return; + } + + port->uptime = 0; + if (enable) { /* clear port statistics */ + port->rx_cfg_bpdu_cnt = + port->rx_rstp_bpdu_cnt = + port->rx_tcn_bpdu_cnt = 0; + } + +#ifdef STP_DBG + if (port->edge->debug) { + stp_trace ("Port %s became '%s' adminEdge=%c", + port->port_name, enable ? "enable" : "disable", + port->adminEdge ? 'Y' : 'N'); + } +#endif + + port->adminEnable = enable; + STP_port_init (port, stpm, False); + + port->reselect = True; + port->selected = False; +} + +void +STP_IN_init (int max_port_index) +{ + max_port = max_port_index; + RSTP_INIT_CRITICAL_PATH_PROTECTIO; +} + +int +STP_IN_stpm_get_cfg (IN int vlan_id, OUT UID_STP_CFG_T* uid_cfg) +{ + register STPM_T* this; + + uid_cfg->field_mask = 0; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (!this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + if (this->admin_state != STP_DISABLED) { + uid_cfg->field_mask |= BR_CFG_STATE; + } + uid_cfg->stp_enabled = this->admin_state; + + if (this->ForceVersion != 2) { + uid_cfg->field_mask |= BR_CFG_FORCE_VER; + } + uid_cfg->force_version = this->ForceVersion; + + if (this->BrId.prio != DEF_BR_PRIO) { + uid_cfg->field_mask |= BR_CFG_PRIO; + } + uid_cfg->bridge_priority = this->BrId.prio; + + if (this->BrTimes.MaxAge != DEF_BR_MAXAGE) { + uid_cfg->field_mask |= BR_CFG_AGE; + } + uid_cfg->max_age = this->BrTimes.MaxAge; + + if (this->BrTimes.HelloTime != DEF_BR_HELLOT) { + uid_cfg->field_mask |= BR_CFG_HELLO; + } + uid_cfg->hello_time = this->BrTimes.HelloTime; + + if (this->BrTimes.ForwardDelay != DEF_BR_FWDELAY) { + uid_cfg->field_mask |= BR_CFG_DELAY; + } + uid_cfg->forward_delay = this->BrTimes.ForwardDelay; + + uid_cfg->hold_time = TxHoldCount; + + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_port_get_cfg (int vlan_id, int port_index, UID_STP_PORT_CFG_T* uid_cfg) +{ + register STPM_T* this; + register PORT_T* port; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (!this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + port = _stpapi_port_find (this, port_index); + if (! port) {/* port is absent in the stpm :( */ + RSTP_CRITICAL_PATH_END; + return STP_Port_Is_Absent_In_The_Vlan; + } + + uid_cfg->field_mask = 0; + + uid_cfg->port_priority = port->port_id >> 8; + if (uid_cfg->port_priority != DEF_PORT_PRIO) + uid_cfg->field_mask |= PT_CFG_PRIO; + + uid_cfg->admin_port_path_cost = port->adminPCost; + if (uid_cfg->admin_port_path_cost != ADMIN_PORT_PATH_COST_AUTO) + uid_cfg->field_mask |= PT_CFG_COST; + + uid_cfg->admin_point2point = port->adminPointToPointMac; + if (uid_cfg->admin_point2point != DEF_P2P) + uid_cfg->field_mask |= PT_CFG_P2P; + + uid_cfg->admin_edge = port->adminEdge; + if (uid_cfg->admin_edge != DEF_ADMIN_EDGE) + uid_cfg->field_mask |= PT_CFG_EDGE; + + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_port_get_state (IN int vlan_id, INOUT UID_STP_PORT_STATE_T* entry) +{ + register STPM_T* this; + register PORT_T* port; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (!this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + port = _stpapi_port_find (this, entry->port_no); + if (! port) {/* port is absent in the stpm :( */ + RSTP_CRITICAL_PATH_END; + return STP_Port_Is_Absent_In_The_Vlan; + } + + entry->port_id = port->port_id; + if (DisabledPort == port->role) { + entry->state = UID_PORT_DISABLED; + } else if (! port->forward && ! port->learn) { + entry->state = UID_PORT_DISCARDING; + } else if (! port->forward && port->learn) { + entry->state = UID_PORT_LEARNING; + } else { + entry->state = UID_PORT_FORWARDING; + } + + entry->uptime = port->uptime; + entry->path_cost = port->operPCost; + _conv_br_id_2_uid (&port->portPrio.root_bridge, &entry->designated_root); + entry->designated_cost = port->portPrio.root_path_cost; + _conv_br_id_2_uid (&port->portPrio.design_bridge, &entry->designated_bridge); + entry->designated_port = port->portPrio.design_port; + + switch (port->role) { + case DisabledPort: entry->role = ' '; break; + case AlternatePort: entry->role = 'A'; break; + case BackupPort: entry->role = 'B'; break; + case RootPort: entry->role = 'R'; break; + case DesignatedPort: entry->role = 'D'; break; + case NonStpPort: entry->role = '-'; break; + default: entry->role = '?'; break; + } + + if (DisabledPort == port->role || NonStpPort == port->role) { + memset (&entry->designated_root, 0, sizeof (UID_BRIDGE_ID_T)); + memset (&entry->designated_bridge, 0, sizeof (UID_BRIDGE_ID_T)); + entry->designated_cost = 0; + entry->designated_port = port->port_id; + } + + if (DisabledPort == port->role) { + entry->oper_point2point = (P2P_FORCE_FALSE == port->adminPointToPointMac) ? 0 : 1; + entry->oper_edge = port->adminEdge; + entry->oper_stp_neigb = 0; + } else { + entry->oper_point2point = port->operPointToPointMac ? 1 : 0; + entry->oper_edge = port->operEdge ? 1 : 0; + entry->oper_stp_neigb = port->sendRSTP ? 0 : 1; + } + entry->oper_port_path_cost = port->operPCost; + + entry->rx_cfg_bpdu_cnt = port->rx_cfg_bpdu_cnt; + entry->rx_rstp_bpdu_cnt = port->rx_rstp_bpdu_cnt; + entry->rx_tcn_bpdu_cnt = port->rx_tcn_bpdu_cnt; + + entry->fdWhile = port->fdWhile; /* 17.15.1 */ + entry->helloWhen = port->helloWhen; /* 17.15.2 */ + entry->mdelayWhile = port->mdelayWhile; /* 17.15.3 */ + entry->rbWhile = port->rbWhile; /* 17.15.4 */ + entry->rcvdInfoWhile = port->rcvdInfoWhile;/* 17.15.5 */ + entry->rrWhile = port->rrWhile; /* 17.15.6 */ + entry->tcWhile = port->tcWhile; /* 17.15.7 */ + entry->txCount = port->txCount; /* 17.18.40 */ + entry->lnkWhile = port->lnkWhile; + + entry->rcvdInfoWhile = port->rcvdInfoWhile; + entry->top_change_ack = port->tcAck; + entry->tc = port->tc; + + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_stpm_get_state (IN int vlan_id, OUT UID_STP_STATE_T* entry) +{ + register STPM_T* this; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (!this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + strncpy (entry->vlan_name, this->name, NAME_LEN); + entry->vlan_id = this->vlan_id; + _conv_br_id_2_uid (&this->rootPrio.root_bridge, &entry->designated_root); + entry->root_path_cost = this->rootPrio.root_path_cost; + entry->root_port = this->rootPortId; + entry->max_age = this->rootTimes.MaxAge; + entry->forward_delay = this->rootTimes.ForwardDelay; + entry->hello_time = this->rootTimes.HelloTime; + + _conv_br_id_2_uid (&this->BrId, &entry->bridge_id); + + entry->stp_enabled = this->admin_state; + + entry->timeSince_Topo_Change = this->timeSince_Topo_Change; + entry->Topo_Change_Count = this->Topo_Change_Count; + entry->Topo_Change = this->Topo_Change; + + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_stpm_get_name_by_vlan_id (int vlan_id, char* name, size_t buffsize) +{ + register STPM_T* stpm; + int iret = -1; + + RSTP_CRITICAL_PATH_START; + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (vlan_id == stpm->vlan_id) { + if (stpm->name) + strncpy (name, stpm->name, buffsize); + else + memset (name, 0, buffsize); + iret = 0; + break; + } + } + RSTP_CRITICAL_PATH_END; + return iret; +} + +int /* call it, when link Up/Down */ +STP_IN_enable_port (int port_index, Bool enable) +{ + register STPM_T* stpm; + + RSTP_CRITICAL_PATH_START; + tev = enable ? RSTP_PORT_EN_T : RSTP_PORT_DIS_T; INCR100(nev); + if (! enable) { +#ifdef STP_DBG + stp_trace("%s (p%02d, all, %s, '%s')", + "clearFDB", (int) port_index, "this port", "disable port"); +#endif + STP_OUT_flush_lt (port_index, 0, LT_FLASH_ONLY_THE_PORT, "disable port"); + } + + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (STP_ENABLED != stpm->admin_state) continue; + + _stp_in_enable_port_on_stpm (stpm, port_index, enable); + /* STP_stpm_update (stpm);*/ + } + + RSTP_CRITICAL_PATH_END; + return 0; +} + +int /* call it, when port speed has been changed, speed in Kb/s */ +STP_IN_changed_port_speed (int port_index, long speed) +{ + register STPM_T* stpm; + register PORT_T* port; + + RSTP_CRITICAL_PATH_START; + tev = RSTP_PORT_SPEED_T; INCR100(nev); + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (STP_ENABLED != stpm->admin_state) continue; + + port = _stpapi_port_find (stpm, port_index); + if (! port) continue; + port->operSpeed = speed; +#ifdef STP_DBG + if (port->pcost->debug) { + stp_trace ("changed operSpeed=%lu", port->operSpeed); + } +#endif + + port->reselect = True; + port->selected = False; + } + RSTP_CRITICAL_PATH_END; + return 0; +} + +int /* call it, when port duplex mode has been changed */ +STP_IN_changed_port_duplex (int port_index) +{ + register STPM_T* stpm; + register PORT_T* port; + + RSTP_CRITICAL_PATH_START; + tev = RSTP_PORT_DPLEX_T; INCR100(nev); + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (STP_ENABLED != stpm->admin_state) continue; + + port = _stpapi_port_find (stpm, port_index); + if (! port) continue; +#ifdef STP_DBG + if (port->p2p->debug) { + stp_trace ("STP_IN_changed_port_duplex(%s)", port->port_name); + } +#endif + port->p2p_recompute = True; + port->reselect = True; + port->selected = False; + } + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_check_bpdu_header (BPDU_T* bpdu, size_t len) +{ + unsigned short len8023; + + len8023 = ntohs (*(unsigned short*) bpdu->eth.len8023); + if (len8023 > 1500) {/* big len8023 format :( */ + return STP_Big_len8023_Format; + } + + if (len8023 < MIN_BPDU) { /* small len8023 format :( */ + return STP_Small_len8023_Format; + } + + if (len8023 + 14 > len) { /* len8023 format gt len :( */ + return STP_len8023_Format_Gt_Len; + } + + if (bpdu->eth.dsap != BPDU_L_SAP || + bpdu->eth.ssap != BPDU_L_SAP || + bpdu->eth.llc != LLC_UI) { + /* this is not a proper 802.3 pkt! :( */ + return STP_Not_Proper_802_3_Packet; + } + + if (bpdu->hdr.protocol[0] || bpdu->hdr.protocol[1]) { + return STP_Invalid_Protocol; + } + +#if 0 + if (bpdu->hdr.version != BPDU_VERSION_ID) { + return STP_Invalid_Version; + } +#endif + /* see also 9.3.4: think & TBD :( */ + return 0; +} + +#ifdef STP_DBG +int dbg_rstp_deny = 0; +#endif + + +int +STP_IN_rx_bpdu (int vlan_id, int port_index, BPDU_T* bpdu, size_t len) +{ + register PORT_T* port; + register STPM_T* this; + int iret; + +#ifdef STP_DBG + if (1 == dbg_rstp_deny) { + return 0; + } +#endif + + RSTP_CRITICAL_PATH_START; + tev = RSTP_PORT_RX_T; INCR100(nev); + this = stpapi_stpm_find (vlan_id); + if (! this) { /* the stpm had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + if (STP_DISABLED == this->admin_state) {/* the stpm had not yet been enabled :( */ + RSTP_CRITICAL_PATH_END; + return STP_Had_Not_Yet_Been_Enabled_On_The_Vlan; + } + + port = _stpapi_port_find (this, port_index); + if (! port) {/* port is absent in the stpm :( */ + stp_trace ("RX bpdu vlan_id=%d port=%d port is absent in the stpm :(", (int) vlan_id, (int) port_index); + RSTP_CRITICAL_PATH_END; + return STP_Port_Is_Absent_In_The_Vlan; + } + +#ifdef STP_DBG + if (port->skip_rx > 0) { + if (1 == port->skip_rx) + stp_trace ("port %s stop rx skipping", + port->port_name); + else + stp_trace ("port %s skip rx %d", + port->port_name, port->skip_rx); + port->skip_rx--; + RSTP_CRITICAL_PATH_END; + return STP_Nothing_To_Do; + } +#endif + + if (port->operEdge && ! port->lnkWhile && port->portEnabled) { +#ifdef STP_DBG + if (port->topoch->debug) { + stp_trace ("port %s tc=TRUE by operEdge", port->port_name); + } +#endif + port->tc = True; /* IEEE 802.1y, 17.30 */ + } + + if (! port->portEnabled) {/* port link change indication will come later :( */ + _stp_in_enable_port_on_stpm (this, port->port_index, True); + } + +#ifdef STP_DBG + if (port->edge->debug && port->operEdge) { + stp_trace ("port %s not operEdge !", port->port_name); + } +#endif + + port->operEdge = False; + port->wasInitBpdu = True; + + iret = STP_port_rx_bpdu (port, bpdu, len); + STP_stpm_update (this); + RSTP_CRITICAL_PATH_END; + + return iret; +} + +int +STP_IN_one_second (void) +{ + register STPM_T* stpm; + register int dbg_cnt = 0; + + RSTP_CRITICAL_PATH_START; + tev = RSTP_PORT_TIME_T; INCR100(nev); + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (STP_ENABLED == stpm->admin_state) { + /* stp_trace ("STP_IN_one_second vlan_id=%d", (int) stpm->vlan_id); */ + STP_stpm_one_second (stpm); + dbg_cnt++; + } + } + + RSTP_CRITICAL_PATH_END; + + return dbg_cnt; +} + +int +STP_IN_stpm_set_cfg (IN int vlan_id, + IN BITMAP_T* port_bmp, + IN UID_STP_CFG_T* uid_cfg) +{ + int rc = 0, prev_prio, err_code; + Bool created_here, enabled_here; + register STPM_T* this; + UID_STP_CFG_T old; + + /* stp_trace ("STP_IN_stpm_set_cfg"); */ + if (0 != STP_IN_stpm_get_cfg (vlan_id, &old)) { + STP_OUT_get_init_stpm_cfg (vlan_id, &old); + } + + RSTP_CRITICAL_PATH_START; + tev = RSTP_PORT_MNGR_T; INCR100(nev); + if (BR_CFG_PRIO & uid_cfg->field_mask) { + old.bridge_priority = uid_cfg->bridge_priority; + } + + if (BR_CFG_AGE & uid_cfg->field_mask) { + old.max_age = uid_cfg->max_age; + } + + if (BR_CFG_HELLO & uid_cfg->field_mask) { + old.hello_time = uid_cfg->hello_time; + } + + if (BR_CFG_DELAY & uid_cfg->field_mask) { + old.forward_delay = uid_cfg->forward_delay; + } + + if (BR_CFG_FORCE_VER & uid_cfg->field_mask) { + old.force_version = uid_cfg->force_version; + } + + rc = _check_stpm_config (&old); + if (0 != rc) { + stp_trace ("_check_stpm_config failed %d", (int) rc); + RSTP_CRITICAL_PATH_END; + return rc; + } + + if ((BR_CFG_STATE & uid_cfg->field_mask) && + (STP_DISABLED == uid_cfg->stp_enabled)) { + rc = _stp_in_stpm_enable (vlan_id, uid_cfg->vlan_name, port_bmp, STP_DISABLED); + if (0 != rc) { + stp_trace ("can't disable rc=%d", (int) rc); + RSTP_CRITICAL_PATH_END; + return rc; + } + uid_cfg->field_mask &= ! BR_CFG_STATE; + if (! uid_cfg->field_mask) { + RSTP_CRITICAL_PATH_END; + return 0; + } + } + + /* get current state */ + this = stpapi_stpm_find (vlan_id); + created_here = False; + enabled_here = False; + if (! this) { /* it had not yet been created */ + this = stp_in_stpm_create (vlan_id, uid_cfg->vlan_name, port_bmp, &err_code);/*STP_IN_stpm_set_cfg*/ + if (! this) { + RSTP_CRITICAL_PATH_END; + return err_code; + } + } + + prev_prio = this->BrId.prio; + this->BrId.prio = old.bridge_priority; + if (STP_ENABLED == this->admin_state) { + if (0 != STP_stpm_check_bridge_priority (this)) { + this->BrId.prio = prev_prio; + stp_trace ("%s", "STP_stpm_check_bridge_priority failed"); + RSTP_CRITICAL_PATH_END; + return STP_Invalid_Bridge_Priority; + } + } + + this->BrTimes.MaxAge = old.max_age; + this->BrTimes.HelloTime = old.hello_time; + this->BrTimes.ForwardDelay = old.forward_delay; + this->ForceVersion = (PROTOCOL_VERSION_T) old.force_version; + + if ((BR_CFG_STATE & uid_cfg->field_mask) && + STP_DISABLED != uid_cfg->stp_enabled && + STP_DISABLED == this->admin_state) { + rc = _stp_in_stpm_enable (vlan_id, uid_cfg->vlan_name, port_bmp, uid_cfg->stp_enabled); + if (0 != rc) { + stp_trace ("%s", "cannot enable"); + if (created_here) { + STP_stpm_delete (this); + } + RSTP_CRITICAL_PATH_END; + return rc; + } + enabled_here = True; + } + + if (! enabled_here && STP_DISABLED != this->admin_state) { + STP_stpm_update_after_bridge_management (this); + } + RSTP_CRITICAL_PATH_END; + return 0; +} + +#ifdef ORIG +int +STP_IN_set_port_cfg (IN int vlan_id, IN UID_STP_PORT_CFG_T* uid_cfg) +#else +int +STP_IN_set_port_cfg (int vlan_id, int port_index, UID_STP_PORT_CFG_T* uid_cfg) +#endif +{ + register STPM_T* this; + register PORT_T* port; + register int port_no; + + RSTP_CRITICAL_PATH_START; + tev = RSTP_PORT_MNGR_T; INCR100(nev); + this = stpapi_stpm_find (vlan_id); + if (! this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + Print ("RSTP instance with tag %d hasn't been created\n", (int) vlan_id); + return STP_Vlan_Had_Not_Yet_Been_Created; + } + +#ifdef ORIG + for (port_no = 1; port_no <= max_port; port_no++) { + if (! BitmapGetBit(&uid_cfg->port_bmp, port_no - 1)) continue; +#else + port_no = port_index; + { +#endif + + port = _stpapi_port_find (this, port_no); + if (! port) {/* port is absent in the stpm :( */ +#ifdef ORIG + continue; +#else + return STP_Port_Is_Absent_In_The_Vlan; +#endif + } + + if (PT_CFG_MCHECK & uid_cfg->field_mask) { + if (this->ForceVersion >= NORMAL_RSTP) + port->mcheck = True; + } + + if (PT_CFG_COST & uid_cfg->field_mask) { + if (uid_cfg->admin_port_path_cost > MAX_PORT_PCOST) + return STP_Large_Port_PCost; + port->adminPCost = uid_cfg->admin_port_path_cost; + } + + if (PT_CFG_PRIO & uid_cfg->field_mask) { + if (uid_cfg->port_priority < MIN_PORT_PRIO) + return STP_Small_Port_Priority; + if (uid_cfg->port_priority > MAX_PORT_PRIO) + return STP_Large_Port_Priority; + if (uid_cfg->port_priority & ~MASK_PORT_PRIO) + return STP_Port_Priority_Not_A_Multiple_Of_16; + port->port_id = (uid_cfg->port_priority << 8) + port_no; + } + + if (PT_CFG_P2P & uid_cfg->field_mask) { + port->adminPointToPointMac = uid_cfg->admin_point2point; + port->p2p_recompute = True; + } + + if (PT_CFG_EDGE & uid_cfg->field_mask) { + port->adminEdge = uid_cfg->admin_edge; + port->operEdge = port->adminEdge; +#ifdef STP_DBG + if (port->edge->debug) { + stp_trace ("port %s is operEdge=%c in STP_IN_set_port_cfg", + port->port_name, + port->operEdge ? 'Y' : 'n'); + } +#endif + } + + if (PT_CFG_NON_STP & uid_cfg->field_mask) { +#ifdef STP_DBG + if (port->roletrns->debug && port->admin_non_stp != uid_cfg->admin_non_stp) { + stp_trace ("port %s is adminNonStp=%c in STP_IN_set_port_cfg", + port->port_name, + uid_cfg->admin_non_stp ? 'Y' : 'n'); + } +#endif + port->admin_non_stp = uid_cfg->admin_non_stp; + } + +#ifdef STP_DBG + if (PT_CFG_DBG_SKIP_RX & uid_cfg->field_mask) { + port->skip_rx = uid_cfg->skip_rx; + } + + if (PT_CFG_DBG_SKIP_TX & uid_cfg->field_mask) { + port->skip_tx = uid_cfg->skip_tx; + } + +#endif + + port->reselect = True; + port->selected = False; + } + + STP_stpm_update (this); + + RSTP_CRITICAL_PATH_END; + + return 0; +} + +#ifdef STP_DBG +int +STP_IN_dbg_set_port_trace (char* mach_name, int enadis, + int vlan_id, BITMAP_T* ports, + int is_print_err) +{ + register STPM_T* this; + register PORT_T* port; + register int port_no; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + if (! this) { /* it had not yet been created :( */ + RSTP_CRITICAL_PATH_END; + if (is_print_err) { + Print ("RSTP instance with tag %d hasn't been created\n", (int) vlan_id); + } + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + for (port_no = 1; port_no <= max_port; port_no++) { + if (! BitmapGetBit(ports, port_no - 1)) continue; + + port = _stpapi_port_find (this, port_no); + if (! port) {/* port is absent in the stpm :( */ + continue; + } + STP_port_trace_state_machine (port, mach_name, enadis, vlan_id); + } + + RSTP_CRITICAL_PATH_END; + + return 0; +} + +#endif + +const char* +STP_IN_get_error_explanation (int rstp_err_no) +{ +#define CHOOSE(a) #a +static char* rstp_error_names[] = RSTP_ERRORS; +#undef CHOOSE + if (rstp_err_no < STP_OK) { + return "Too small error code :("; + } + if (rstp_err_no >= STP_LAST_DUMMY) { + return "Too big error code :("; + } + + return rstp_error_names[rstp_err_no]; +} + +/*---------------- Dynamic port create / delete ------------------*/ + +int STP_IN_port_create(int vlan_id, int port_index) +{ + register STPM_T* this; + + this = stpapi_stpm_find (vlan_id); + + if (! this) { /* can't create stpm :( */ + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + PORT_T *port = STP_port_create (this, port_index); + if (! port) { + /* can't add port :( */ + stp_trace ("can't create port %d", (int) port_index); + return STP_Cannot_Create_Instance_For_Port; + } + STP_port_init(port, this, True); + + STP_compute_bridge_id(this); + STP_stpm_update_after_bridge_management (this); + STP_stpm_update (this); + return 0; +} + +int STP_IN_port_delete(int vlan_id, int port_index) +{ + register STPM_T* this; + PORT_T *port; + + this = stpapi_stpm_find (vlan_id); + + if (! this) { /* can't find stpm :( */ + return STP_Vlan_Had_Not_Yet_Been_Created; + } + + port = _stpapi_port_find (this, port_index); + if (! port) { + return STP_Port_Is_Absent_In_The_Vlan; + } + + STP_port_delete (port); + + STP_compute_bridge_id(this); + STP_stpm_update_after_bridge_management (this); + STP_stpm_update (this); + return 0; +} + + +/*--- For multiple STP instances - non multithread use ---*/ + +struct stp_instance +{ + STPM_T *bridges; +#ifdef STP_DBG + int dbg_rstp_deny; +#endif + int max_port; /* Remove this */ + int nev; + RSTP_EVENT_T tev; +}; + +struct stp_instance *STP_IN_instance_create(void) +{ + struct stp_instance *p; + p = malloc(sizeof(*p)); + if (!p) return p; + p->bridges = NULL; +#ifdef STP_DBG + p->dbg_rstp_deny = 0; +#endif + p->max_port = 1024; + p->tev = RSTP_EVENT_LAST_DUMMY; + p->nev = 0; + return p; +} + +void STP_IN_instance_begin(struct stp_instance *p) +{ + bridges = p->bridges; +#ifdef STP_DBG + dbg_rstp_deny = p->dbg_rstp_deny; +#endif + max_port = p->max_port; + tev = p->tev; + nev = p->nev; +} + +void STP_IN_instance_end(struct stp_instance *p) +{ + p->bridges = bridges; +#ifdef STP_DBG + p->dbg_rstp_deny = dbg_rstp_deny; +#endif + p->max_port = max_port; + p->tev = tev; + p->nev = nev; +} + +void STP_IN_instance_delete(struct stp_instance *p) +{ + STP_IN_instance_begin(p); + STP_IN_delete_all(); + STP_IN_instance_end(p); + free(p); +} + + + diff --git a/rstplib/stp_in.h b/rstplib/stp_in.h new file mode 100644 index 0000000..b95b30e --- /dev/null +++ b/rstplib/stp_in.h @@ -0,0 +1,229 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + + /* This file contains prototypes for API from an operation + system to the RSTP */ + +#ifndef _STP_API_H__ +#define _STP_API_H__ + +/************************ + * Common base constants + ************************/ + +#ifndef INOUT +# define IN /* consider as comments near 'input' parameters */ +# define OUT /* consider as comments near 'output' parameters */ +# define INOUT /* consider as comments near 'input/output' parameters */ +#endif + +#ifndef Zero +# define Zero 0 +# define One 1 +#endif + +#ifndef Bool +# define Bool int +# define False 0 +# define True 1 +#endif + +/******************************************** + * constants: default values and linitations + *********************************************/ + +/* bridge configuration */ + +#define DEF_BR_PRIO 32768 +#define MIN_BR_PRIO 0 +#define MAX_BR_PRIO 61440 +#define MASK_BR_PRIO 0xf000 + +#define DEF_BR_HELLOT 2 +#define MIN_BR_HELLOT 1 +#define MAX_BR_HELLOT 10 + +#define DEF_BR_MAXAGE 20 +#define MIN_BR_MAXAGE 6 +#define MAX_BR_MAXAGE 40 + +#define DEF_BR_FWDELAY 15 +#define MIN_BR_FWDELAY 4 +#define MAX_BR_FWDELAY 30 + +#define DEF_FORCE_VERS 2 /* NORMAL_RSTP */ + +/* port configuration */ + +#define DEF_PORT_PRIO 128 +#define MIN_PORT_PRIO 0 +#define MAX_PORT_PRIO 240 /* in steps of 16 */ +#define MASK_PORT_PRIO 0xf0 + +#define DEF_ADMIN_NON_STP False +#define DEF_ADMIN_EDGE True +#define DEF_LINK_DELAY 3 /* see edge.c */ +#define DEF_P2P P2P_AUTO + +#define MAX_PORT_PCOST 200000000 + +/* Section 1: Create/Delete/Start/Stop the RSTP instance */ + +void /* init the engine */ +STP_IN_init (int max_port_index); + +#ifdef __BITMAP_H +int +STP_IN_stpm_create (int vlan_id, char* name, BITMAP_T* port_bmp); +#endif + +int +STP_IN_stpm_delete (int vlan_id); + +int +STP_IN_stop_all (void); + +int +STP_IN_delete_all (void); + +/* Port create/delete */ + +int STP_IN_port_create(int vlan_id, int port_index); + +int STP_IN_port_delete(int vlan_id, int port_index); + +/* Section 2. "Get" management */ + +Bool +STP_IN_get_is_stpm_enabled (int vlan_id); + +int +STP_IN_stpm_get_vlan_id_by_name (char* name, int* vlan_id); + +int +STP_IN_stpm_get_name_by_vlan_id (int vlan_id, char* name, size_t buffsize); + +const char* +STP_IN_get_error_explanation (int rstp_err_no); + +#ifdef _UID_STP_H__ +int +STP_IN_stpm_get_cfg (int vlan_id, UID_STP_CFG_T* uid_cfg); + +int +STP_IN_stpm_get_state (int vlan_id, UID_STP_STATE_T* entry); + +int +STP_IN_port_get_cfg (int vlan_id, int port_index, UID_STP_PORT_CFG_T* uid_cfg); + +int +STP_IN_port_get_state (int vlan_id, UID_STP_PORT_STATE_T* entry); +#endif + +/* Section 3. "Set" management */ + +int +STP_IN_stpm_set_cfg (int vlan_id, + BITMAP_T* port_bmp, + UID_STP_CFG_T* uid_cfg); + +#ifdef ORIG +int +STP_IN_set_port_cfg (int vlan_id, + UID_STP_PORT_CFG_T* uid_cfg); +#else +int +STP_IN_set_port_cfg (int vlan_id, int port_index, + UID_STP_PORT_CFG_T* uid_cfg); +#endif + +#ifdef STP_DBG +int STP_IN_dbg_set_port_trace (char* mach_name, int enadis, + int vlan_id, BITMAP_T* ports, + int is_print_err); +#endif + +/* Section 4. RSTP functionality events */ + +int +STP_IN_one_second (void); + +int /* for Link UP/DOWN */ +STP_IN_enable_port (int port_index, Bool enable); + +int /* call it, when port speed has been changed, speed in Kb/s */ +STP_IN_changed_port_speed (int port_index, long speed); + +int /* call it, when current port duplex mode has been changed */ +STP_IN_changed_port_duplex (int port_index); + +#ifdef _STP_BPDU_H__ +int +STP_IN_check_bpdu_header (BPDU_T* bpdu, size_t len); + +int +STP_IN_rx_bpdu (int vlan_id, int port_index, BPDU_T* bpdu, size_t len); +#endif + +/*--- For multiple STP instances - non multithread use ---*/ + +struct stp_instance; +/* Create struct to hold STP instance state and initialize it. + A copy of all global state in the library. */ +struct stp_instance *STP_IN_instance_create(void); +/* Set context from this STP instance */ +void STP_IN_instance_begin(struct stp_instance *p); +/* Save context back to this STP instance */ +void STP_IN_instance_end(struct stp_instance *p); +/* Delete this STP instance */ +void STP_IN_instance_delete(struct stp_instance *p); + +#ifdef _STP_MACHINE_H__ +/* Inner usage definitions & functions */ + +extern int max_port; + +#ifdef __LINUX__ +# define RSTP_INIT_CRITICAL_PATH_PROTECTIO +# define RSTP_CRITICAL_PATH_START +# define RSTP_CRITICAL_PATH_END +#else +# define RSTP_INIT_CRITICAL_PATH_PROTECTIO STP_OUT_psos_init_semaphore () +# define RSTP_CRITICAL_PATH_START STP_OUT_psos_close_semaphore () +# define RSTP_CRITICAL_PATH_END STP_OUT_psos_open_semaphore () + extern void STP_OUT_psos_init_semaphore (void); + extern void STP_OUT_psos_close_semaphore (void); + extern void STP_OUT_psos_open_semaphore (void); +#endif + +STPM_T* stpapi_stpm_find (int vlan_id); + +int stp_in_stpm_enable (int vlan_id, char* name, + BITMAP_T* port_bmp, + UID_STP_MODE_T admin_state); +void* stp_in_stpm_create (int vlan_id, char* name, BITMAP_T* port_bmp, + int* err_code); + +#endif /* _STP_MACHINE_H__ */ + + +#endif /* _STP_API_H__ */ diff --git a/rstplib/stp_state.h b/rstplib/stp_state.h new file mode 100644 index 0000000..3a7fc87 --- /dev/null +++ b/rstplib/stp_state.h @@ -0,0 +1,7 @@ +#ifndef _STP_STATE_H__ +#define _STP_STATE_H__ + + + + +#endif diff --git a/rstplib/stp_to.c b/rstplib/stp_to.c new file mode 100644 index 0000000..c5dc8e1 --- /dev/null +++ b/rstplib/stp_to.c @@ -0,0 +1,193 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* This file contains system dependent API + from the RStp to a operation system (see stp_to.h) */ + +/* stp_to API for Linux */ + +#include <stddef.h> +#include <stdio.h> +#include <stdarg.h> +#include <time.h> +#include <unistd.h> + +#include "base.h" +#include "stpm.h" +#include "stp_in.h" +#include "stp_to.h" + +extern BITMAP_T enabled_ports; + +/************* +void +stp_trace (const char *format, ...) +{ + #define MAX_MSG_LEN 128 + char msg[MAX_MSG_LEN]; + va_list args; + + va_start(args, format); + vsnprintf (msg, MAX_MSG_LEN-1, format, args); + printf ("%s\n", msg); + va_end(args); + +} +***********/ + +#ifdef STRONGLY_SPEC_802_1W +int +STP_OUT_set_learning (int port_index, int vlan_id, int enable) +{ + return STP_OK; +} + +int +STP_OUT_set_forwarding (int port_index, int vlan_id, int enable) +{ + return STP_OK; +} +#else +/* + * In many kinds of hardware the state of ports may + * be changed with another method + */ +int +STP_OUT_set_port_state (IN int port_index, IN int vlan_id, + IN RSTP_PORT_STATE state) +{ + return STP_OK; + //return AR_INT_STP_set_port_state (port_index, vlan_id, state); +} +#endif + + +void +STP_OUT_get_port_mac (int port_index, unsigned char *mac) +{ + static long pid = -1; + static unsigned char mac_beg[] = {'\0', '\0', '\0', '\0', '\0', '\0'}; + + if (pid < 0) { + pid = getpid (); + memcpy (mac_beg + 1, &pid, 4); + } + memcpy (mac, mac_beg, 5); + mac[5] = port_index; + //memcpy (mac, STP_MAIN_get_port_mac (port_index), 6); +} + +int /* 1- Up, 0- Down */ +STP_OUT_get_port_link_status (int port_index) +{ + if (BitmapGetBit (&enabled_ports, (port_index - 1))) return 1; + return 0; +} + +int +STP_OUT_flush_lt (IN int port_index, IN int vlan_id, LT_FLASH_TYPE_T type, char* reason) +{ +/**** + stp_trace("clearFDB (%d, %s, '%s')", + port_index, + (type == LT_FLASH_ALL_PORTS_EXCLUDE_THIS) ? "Exclude" : "Only", + reason); +****/ + + return STP_OK; +} + +int +STP_OUT_set_hardware_mode (int vlan_id, UID_STP_MODE_T mode) +{ + return STP_OK; + //return AR_INT_STP_set_mode (vlan_id, mode); +} + + +int +STP_OUT_tx_bpdu (int port_index, int vlan_id, + unsigned char *bpdu, size_t bpdu_len) +{ +extern int bridge_tx_bpdu (int port_index, unsigned char *bpdu, size_t bpdu_len); + return bridge_tx_bpdu (port_index, bpdu, bpdu_len); +} + +const char * +STP_OUT_get_port_name (IN int port_index) +{ + static char tmp[4]; + sprintf (tmp, "p%02d", (int) port_index); + return tmp; + //return port2str (port_index, &sys_config); +} + +unsigned long +STP_OUT_get_deafult_port_path_cost (IN unsigned int portNo) +{ + return 20000; +} + +unsigned long STP_OUT_get_port_oper_speed (unsigned int portNo) +{ + if (portNo <= 2) + return 1000000L; + else + return 1000L; +} + +int /* 1- Full, 0- Half */ +STP_OUT_get_duplex (IN int port_index) +{ + return 1; +} + +int +STP_OUT_get_init_stpm_cfg (IN int vlan_id, + INOUT UID_STP_CFG_T* cfg) +{ + cfg->bridge_priority = DEF_BR_PRIO; + cfg->max_age = DEF_BR_MAXAGE; + cfg->hello_time = DEF_BR_HELLOT; + cfg->forward_delay = DEF_BR_FWDELAY; + cfg->force_version = NORMAL_RSTP; + + return STP_OK; +} + + +int +STP_OUT_get_init_port_cfg (IN int vlan_id, + IN int port_index, + INOUT UID_STP_PORT_CFG_T* cfg) +{ + cfg->port_priority = DEF_PORT_PRIO; + cfg->admin_non_stp = DEF_ADMIN_NON_STP; + cfg->admin_edge = DEF_ADMIN_EDGE; + cfg->admin_port_path_cost = ADMIN_PORT_PATH_COST_AUTO; + cfg->admin_point2point = DEF_P2P; + + return STP_OK; +} + + + diff --git a/rstplib/stp_to.h b/rstplib/stp_to.h new file mode 100644 index 0000000..688a2fb --- /dev/null +++ b/rstplib/stp_to.h @@ -0,0 +1,92 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* This file contains prototypes for system dependent API + from the RSTP to an operation system */ + +#ifndef _STP_OUT_H__ +#define _STP_OUT_H__ + +/* In the best case: clean all Learning entries with + the vlan_id and the port (if 'exclude'=0) or for all ports, + exclude the port (if ''exclude'=1). If 'port'=0, delete all + entries with the vlan_id, don't care to 'exclude' */ +typedef enum { + LT_FLASH_ALL_PORTS_EXCLUDE_THIS, + LT_FLASH_ONLY_THE_PORT +} LT_FLASH_TYPE_T; + +int +STP_OUT_flush_lt (IN int port_index, IN int vlan_id, + IN LT_FLASH_TYPE_T type, IN char* reason); + +void /* for bridge id calculation */ +STP_OUT_get_port_mac (IN int port_index, OUT unsigned char* mac); + +unsigned long +STP_OUT_get_port_oper_speed (IN unsigned int portNo); + +int /* 1- Up, 0- Down */ +STP_OUT_get_port_link_status (IN int port_index); + +int /* 1- Full, 0- Half */ +STP_OUT_get_duplex (IN int port_index); + +#ifdef STRONGLY_SPEC_802_1W +int +STP_OUT_set_learning (IN int port_index, IN int vlan_id, IN int enable); + +int +STP_OUT_set_forwarding (IN int port_index, IN int vlan_id, IN int enable); +#else +/* + * In many kinds of hardware the state of ports may + * be changed with another method + */ +int +STP_OUT_set_port_state (IN int port_index, IN int vlan_id, IN RSTP_PORT_STATE state); +#endif + +int +STP_OUT_set_hardware_mode (int vlan_id, UID_STP_MODE_T mode); + +int +STP_OUT_tx_bpdu (IN int port_index, IN int vlan_id, + IN unsigned char* bpdu, + IN size_t bpdu_len); + +const char * +STP_OUT_get_port_name (IN int port_index); + +int +STP_OUT_get_init_stpm_cfg (IN int vlan_id, + INOUT UID_STP_CFG_T* cfg); + + +int +STP_OUT_get_init_port_cfg (IN int vlan_id, + IN int port_index, + INOUT UID_STP_PORT_CFG_T* cfg); + + +#endif /* _STP_OUT_H__ */ + diff --git a/rstplib/stpm.c b/rstplib/stpm.c new file mode 100644 index 0000000..f025523 --- /dev/null +++ b/rstplib/stpm.c @@ -0,0 +1,361 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + + /* STP machine instance : bridge per VLAN: 17.17 */ + +#include "base.h" +#include "stpm.h" +#include "stp_to.h" /* for STP_OUT_flush_lt */ + +/*static*/ STPM_T *bridges = NULL; + +/* We can flush learned fdb by port, so set this in stpm.c and topoch.c */ +/* This doesn't seem to solve the topology change problems. Don't use it yet */ +//#define STRONGLY_SPEC_802_1W + +static int +_stp_stpm_init_machine (STATE_MACH_T* this) +{ + this->State = BEGIN; + (*(this->concreteEnterState)) (this); + return 0; +} + +static int +_stp_stpm_iterate_machines (STPM_T* this, + int (*iter_callb) (STATE_MACH_T*), + Bool exit_on_non_zero_ret) +{ + register STATE_MACH_T* stater; + register PORT_T* port; + int iret, mret = 0; + + /* state machines per bridge */ + for (stater = this->machines; stater; stater = stater->next) { + iret = (*iter_callb) (stater); + if (exit_on_non_zero_ret && iret) + return iret; + else + mret += iret; + } + + /* state machines per port */ + for (port = this->ports; port; port = port->next) { + for (stater = port->machines; stater; stater = stater->next) { + iret = (*iter_callb) (stater); + if (exit_on_non_zero_ret && iret) + return iret; + else + mret += iret; + } + } + + return mret; +} + +void +_stp_stpm_init_data (STPM_T* this) +{ + STP_VECT_create (&this->rootPrio, + &this->BrId, + 0, + &this->BrId, + 0, 0); + + this->BrTimes.MessageAge = 0; + + STP_copy_times (&this->rootTimes, &this->BrTimes); +} + +static unsigned char +_check_topoch (STPM_T* this) +{ + register PORT_T* port; + + for (port = this->ports; port; port = port->next) { + if (port->tcWhile) { + return 1; + } + } + return 0; +} + +void +STP_stpm_one_second (STPM_T* param) +{ + STPM_T* this = (STPM_T*) param; + register PORT_T* port; + register int iii; + + if (STP_ENABLED != this->admin_state) return; + + for (port = this->ports; port; port = port->next) { + for (iii = 0; iii < TIMERS_NUMBER; iii++) { + if (*(port->timers[iii]) > 0) { + (*port->timers[iii])--; + } + } + port->uptime++; + } + + STP_stpm_update (this); + this->Topo_Change = _check_topoch (this); + if (this->Topo_Change) { + this->Topo_Change_Count++; + this->timeSince_Topo_Change = 0; + } else { + this->Topo_Change_Count = 0; + this->timeSince_Topo_Change++; + } +} + +STPM_T* +STP_stpm_create (int vlan_id, char* name) +{ + STPM_T* this; + + STP_NEW_IN_LIST(this, STPM_T, bridges, "stp instance"); + + this->admin_state = STP_DISABLED; + + this->vlan_id = vlan_id; + if (name) { + STP_STRDUP(this->name, name, "stp bridge name"); + } + + this->machines = NULL; + this->ports = NULL; + + STP_STATE_MACH_IN_LIST(rolesel); + +#ifdef STP_DBG + /* this->rolesel->debug = 2; */ +#endif + + return this; +} + +int +STP_stpm_enable (STPM_T* this, UID_STP_MODE_T admin_state) +{ + int rc = 0; + + if (admin_state == this->admin_state) { + /* nothing to do :) */ + return 0; + } + + if (STP_ENABLED == admin_state) { + rc = STP_stpm_start (this); + this->admin_state = admin_state; + } else { + this->admin_state = admin_state; + STP_stpm_stop (this); + } + + return rc; +} + +void +STP_stpm_delete (STPM_T* this) +{ + register STPM_T* tmp; + register STPM_T* prev; + register STATE_MACH_T* stater; + register PORT_T* port; + register void* pv; + + STP_stpm_enable (this, STP_DISABLED); + + for (stater = this->machines; stater; ) { + pv = (void*) stater->next; + STP_state_mach_delete (stater); + this->machines = stater = (STATE_MACH_T*) pv; + } + + for (port = this->ports; port; ) { + pv = (void*) port->next; + STP_port_delete (port); + this->ports = port = (PORT_T*) pv; + } + + prev = NULL; + for (tmp = bridges; tmp; tmp = tmp->next) { + if (tmp->vlan_id == this->vlan_id) { + if (prev) { + prev->next = this->next; + } else { + bridges = this->next; + } + + if (this->name) + STP_FREE(this->name, "stp bridge name"); + STP_FREE(this, "stp instance"); + break; + } + prev = tmp; + } +} + +int +STP_stpm_start (STPM_T* this) +{ + register PORT_T* port; + +#ifdef ORIG + if (! this->ports) { /* there are not any ports :( */ + return STP_There_Are_No_Ports; + } +#endif + + if (! STP_compute_bridge_id (this)) {/* can't compute bridge id ? :( */ + return STP_Cannot_Compute_Bridge_Prio; + } + + /* check, that the stpm has unique bridge Id */ + if (0 != STP_stpm_check_bridge_priority (this)) { + /* there is an enabled bridge with same ID :( */ + return STP_Invalid_Bridge_Priority; + } + + _stp_stpm_init_data (this); + + for (port = this->ports; port; port = port->next) { + STP_port_init (port, this, True); + } + +#ifndef STRONGLY_SPEC_802_1W + /* A. see comment near STRONGLY_SPEC_802_1W in topoch.c */ + /* B. port=0 here means: delete for all ports */ +#ifdef STP_DBG + stp_trace("%s (all, start stpm)", + "clearFDB"); +#endif + + STP_OUT_flush_lt (0, this->vlan_id, LT_FLASH_ONLY_THE_PORT, "start stpm"); +#endif + + _stp_stpm_iterate_machines (this, _stp_stpm_init_machine, False); + STP_stpm_update (this); + + return 0; +} + +void +STP_stpm_stop (STPM_T* this) +{ +} + +int +STP_stpm_update (STPM_T* this) /* returns number of loops */ +{ + register Bool need_state_change; + register int number_of_loops = 0; + + need_state_change = False; + + for (;;) {/* loop until not need changes */ + need_state_change = _stp_stpm_iterate_machines (this, + STP_check_condition, + True); + if (! need_state_change) return number_of_loops; + + number_of_loops++; + /* here we know, that at least one stater must be + updated (it has changed state) */ + number_of_loops += _stp_stpm_iterate_machines (this, + STP_change_state, + False); + + } + + return number_of_loops; +} + +BRIDGE_ID * +STP_compute_bridge_id (STPM_T* this) +{ + register PORT_T* port; + unsigned char old[6], new[6]; + memset(&old, 0xff, sizeof(old)); + + for (port = this->ports; port; port = port->next) { + STP_OUT_get_port_mac (port->port_index, new); + if (memcmp(new, old, sizeof(old)) < 0) + memcpy(old, new, sizeof(old)); + } + + memcpy(this->BrId.addr, old, sizeof(old)); + + return &this->BrId; +} + +STPM_T* +STP_stpm_get_the_list (void) +{ + return bridges; +} + +void +STP_stpm_update_after_bridge_management (STPM_T* this) +{ + register PORT_T* port; + + for (port = this->ports; port; port = port->next) { + port->reselect = True; + port->selected = False; + } +} + +int +STP_stpm_check_bridge_priority (STPM_T* this) +{ + register STPM_T* oth; + + for (oth = bridges; oth; oth = oth->next) { + if (STP_ENABLED == oth->admin_state && oth != this && + ! STP_VECT_compare_bridge_id (&this->BrId, &oth->BrId)) { + return STP_Invalid_Bridge_Priority; + } + } + + return 0; +} + +const char* +STP_stpm_get_port_name_by_id (STPM_T* this, PORT_ID port_id) +{ + register PORT_T* port; + + for (port = this->ports; port; port = port->next) { + if (port_id == port->port_id) { + return port->port_name; + } + } + + return "Undef?"; +} + + + + + diff --git a/rstplib/stpm.h b/rstplib/stpm.h new file mode 100644 index 0000000..2f272f9 --- /dev/null +++ b/rstplib/stpm.h @@ -0,0 +1,117 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* STP machine instance : bridge per VLAN: 17.17 */ +/* The Clause 17.13 points: "NOTE:The operation of the Bridge as a whole can + * be represented by the interaction between Bridge Ports specified, + * and by parameters of the Bridge stored in ‘Port 0’. This removes the + * need for any ‘per Bridge’ specification elements, and helps ensure + * the minimum dependencies between Bridge Ports. This in turn supports + * the development of implementations that scale well with increasing + * numbers of Bridge Ports. This shift of focus to ‘per Port operation’ + * for the RSTP is supported by underlying technical changes from the + * Spanning Tree Algorithm and Protocol (Clause 8):" + * Newetheless, it seems to me, the behaviour of of the bridge, its variables + * and functions are so distinct from Port, that I decided to design Bridge + * instance. I called this object 'stpm' from STP machine. I'd like to see + * another procedural model, more corresponding to this note on Clause 17.13 */ + +#ifndef _STP_MACHINE_H__ +#define _STP_MACHINE_H__ + +#include "port.h" +#include "rolesel.h" + +#define TxHoldCount 3 /* 17.16.6, 17.28.2(Table 17-5) */ + +typedef enum {/* 17.12, 17.16.1 */ + FORCE_STP_COMPAT = 0, + NORMAL_RSTP = 2 +} PROTOCOL_VERSION_T; + +typedef struct stpm_t { + struct stpm_t* next; + + struct port_t* ports; + + /* The only "per bridge" state machine */ + STATE_MACH_T* rolesel; /* the Port Role Selection State machione: 17.22 */ + STATE_MACH_T* machines; + + /* variables */ + PROTOCOL_VERSION_T ForceVersion; /* 17.12, 17.16.1 */ + BRIDGE_ID BrId; /* 17.17.2 */ + TIMEVALUES_T BrTimes; /* 17.17.4 */ + PORT_ID rootPortId; /* 17.17.5 */ + PRIO_VECTOR_T rootPrio; /* 17.17.6 */ + TIMEVALUES_T rootTimes; /* 17.17.7 */ + + int vlan_id; /* let's say: tag */ + char* name; /* name of the VLAN, maily for debugging */ + UID_STP_MODE_T admin_state; /* STP_DISABLED or STP_ENABLED; type see in UiD */ + + unsigned long timeSince_Topo_Change; /* 14.8.1.1.3.b */ + unsigned long Topo_Change_Count; /* 14.8.1.1.3.c */ + unsigned char Topo_Change; /* 14.8.1.1.3.d */ +} STPM_T; + +/* Functions prototypes */ + +void +STP_stpm_one_second (STPM_T* param); + +STPM_T* +STP_stpm_create (int vlan_id, char* name); + +int +STP_stpm_enable (STPM_T* this, UID_STP_MODE_T admin_state); + +void +STP_stpm_delete (STPM_T* this); + +int +STP_stpm_start (STPM_T* this); + +void +STP_stpm_stop (STPM_T* this); + +int +STP_stpm_update (STPM_T* this); + +BRIDGE_ID * +STP_compute_bridge_id (STPM_T* this); + +STPM_T * +STP_stpm_get_the_list (void); + +extern STPM_T *bridges; + +void +STP_stpm_update_after_bridge_management (STPM_T* this); + +int +STP_stpm_check_bridge_priority (STPM_T* this); + +const char* +STP_stpm_get_port_name_by_id (STPM_T* this, PORT_ID port_id); + +#endif /* _STP_MACHINE_H__ */ diff --git a/rstplib/stpmgmt.c b/rstplib/stpmgmt.c new file mode 100644 index 0000000..8b7090f --- /dev/null +++ b/rstplib/stpmgmt.c @@ -0,0 +1,165 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* This file contains API from an operation system to the RSTP library */ + +#include "base.h" +#include "stpm.h" +#include "stp_in.h" /* for bridge defaults */ +#include "stp_to.h" + + +int +STP_IN_stpm_create (int vlan_id, char* name, BITMAP_T* port_bmp) +{ + register STPM_T* this; + int err_code; + UID_STP_CFG_T init_cfg; + + stp_trace ("STP_IN_stpm_create(%s)", name); + + init_cfg.field_mask = BR_CFG_ALL; + STP_OUT_get_init_stpm_cfg (vlan_id, &init_cfg); + init_cfg.field_mask = 0; + + RSTP_CRITICAL_PATH_START; + this = stp_in_stpm_create (vlan_id, name, port_bmp, &err_code); + if (this) { + this->BrId.prio = init_cfg.bridge_priority; + this->BrTimes.MaxAge = init_cfg.max_age; + this->BrTimes.HelloTime = init_cfg.hello_time; + this->BrTimes.ForwardDelay = init_cfg.forward_delay; + this->ForceVersion = (PROTOCOL_VERSION_T) init_cfg.force_version; + } +#ifdef ORIG +#else + if (this->admin_state != STP_ENABLED) + err_code = STP_stpm_enable(this, STP_ENABLED); +#endif + + RSTP_CRITICAL_PATH_END; + return err_code; +} + +int +STP_IN_stpm_delete (int vlan_id) +{ + register STPM_T* this; + int iret = 0; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (! this) { /* it had not yet been created :( */ + iret = STP_Vlan_Had_Not_Yet_Been_Created; + } else { + + if (STP_ENABLED == this->admin_state) { + if (0 != STP_stpm_enable (this, STP_DISABLED)) {/* can't disable :( */ + iret = STP_Another_Error; + } else + STP_OUT_set_hardware_mode (vlan_id, STP_DISABLED); + } + + if (0 == iret) { + STP_stpm_delete (this); + } + } + RSTP_CRITICAL_PATH_END; + return iret; +} + +int +STP_IN_stpm_get_vlan_id_by_name (char* name, int* vlan_id) +{ + register STPM_T* stpm; + int iret = STP_Cannot_Find_Vlan; + + RSTP_CRITICAL_PATH_START; + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (stpm->name && ! strcmp (stpm->name, name)) { + *vlan_id = stpm->vlan_id; + iret = 0; + break; + } + } + RSTP_CRITICAL_PATH_END; + + return iret; +} + + +Bool +STP_IN_get_is_stpm_enabled (int vlan_id) +{ + STPM_T* this; + Bool iret = False; + + RSTP_CRITICAL_PATH_START; + this = stpapi_stpm_find (vlan_id); + + if (this) { + if (this->admin_state == STP_ENABLED) { + iret = True; + } + } else { + ; /* it had not yet been created :( */ + } + + RSTP_CRITICAL_PATH_END; + return iret; +} + +int +STP_IN_stop_all (void) +{ + register STPM_T* stpm; + + RSTP_CRITICAL_PATH_START; + + for (stpm = STP_stpm_get_the_list (); stpm; stpm = stpm->next) { + if (STP_DISABLED != stpm->admin_state) { + STP_OUT_set_hardware_mode (stpm->vlan_id, STP_DISABLED); + STP_stpm_enable (stpm, STP_DISABLED); + } + } + + RSTP_CRITICAL_PATH_END; + return 0; +} + +int +STP_IN_delete_all (void) +{ + register STPM_T* stpm, *next; + + RSTP_CRITICAL_PATH_START; + for (stpm = STP_stpm_get_the_list (); stpm; stpm = next) { + next = stpm->next; + STP_stpm_enable (stpm, STP_DISABLED); + STP_stpm_delete (stpm); + } + + RSTP_CRITICAL_PATH_END; + return 0; +} + diff --git a/rstplib/sttrans.c b/rstplib/sttrans.c new file mode 100644 index 0000000..9174d76 --- /dev/null +++ b/rstplib/sttrans.c @@ -0,0 +1,143 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port State Transition state machine : 17.24 */ + +#include "base.h" +#include "stpm.h" +#include "stp_to.h" + +#define STATES { \ + CHOOSE(DISCARDING), \ + CHOOSE(LEARNING), \ + CHOOSE(FORWARDING), \ +} + +#define GET_STATE_NAME STP_sttrans_get_state_name +#include "choose.h" + + +#ifdef STRONGLY_SPEC_802_1W +static Bool +disableLearning (STATE_MACH_T *this) +{ + register PORT_T *port = this->owner.port; + + return STP_OUT_set_learning (port->port_index, port->owner->vlan_id, False); +} + +static Bool +enableLearning (STATE_MACH_T *this) +{ + register PORT_T *port = this->owner.port; + + return STP_OUT_set_learning (port->port_index, port->owner->vlan_id, True); +} + +static Bool +disableForwarding (STATE_MACH_T *this) +{ + register PORT_T *port = this->owner.port; + + return STP_OUT_set_forwarding (port->port_index, port->owner->vlan_id, False); +} + +static Bool +enableForwarding (STATE_MACH_T *this) +{ + register PORT_T *port = this->owner.port; + + return STP_OUT_set_forwarding (port->port_index, port->owner->vlan_id, True); +} +#endif + +void +STP_sttrans_enter_state (STATE_MACH_T *this) +{ + register PORT_T *port = this->owner.port; + + switch (this->State) { + case BEGIN: + case DISCARDING: + port->learning = False; + port->forwarding = False; +#ifdef STRONGLY_SPEC_802_1W + disableLearning (this); + disableForwarding (this); +#else + STP_OUT_set_port_state (port->port_index, port->owner->vlan_id, UID_PORT_DISCARDING); +#endif + break; + case LEARNING: + port->learning = True; +#ifdef STRONGLY_SPEC_802_1W + enableLearning (this); +#else + STP_OUT_set_port_state (port->port_index, port->owner->vlan_id, UID_PORT_LEARNING); +#endif + break; + case FORWARDING: + port->tc = !port->operEdge; + port->forwarding = True; +#ifdef STRONGLY_SPEC_802_1W + enableForwarding (this); +#else + STP_OUT_set_port_state (port->port_index, port->owner->vlan_id, UID_PORT_FORWARDING); +#endif + break; + } + +} + +Bool +STP_sttrans_check_conditions (STATE_MACH_T *this) +{ + register PORT_T *port = this->owner.port; + + if (BEGIN == this->State) { + return STP_hop_2_state (this, DISCARDING); + } + + switch (this->State) { + case DISCARDING: + if (port->learn) { + return STP_hop_2_state (this, LEARNING); + } + break; + case LEARNING: + if (port->forward) { + return STP_hop_2_state (this, FORWARDING); + } + if (!port->learn) { + return STP_hop_2_state (this, DISCARDING); + } + break; + case FORWARDING: + if (!port->forward) { + return STP_hop_2_state (this, DISCARDING); + } + break; + } + + return False; +} + diff --git a/rstplib/sttrans.h b/rstplib/sttrans.h new file mode 100644 index 0000000..da0b81e --- /dev/null +++ b/rstplib/sttrans.h @@ -0,0 +1,38 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port State Transition state machine : 17.24 */ + +#ifndef _STP_STATE_TRANSIT_H__ +#define _STP_STATE_TRANSIT_H__ + +void +STP_sttrans_enter_state (STATE_MACH_T* s); + +Bool +STP_sttrans_check_conditions (STATE_MACH_T* s); + +char* +STP_sttrans_get_state_name (int state); + +#endif /* _STP_STATE_TRANSIT_H__ */ + diff --git a/rstplib/times.c b/rstplib/times.c new file mode 100644 index 0000000..66b5ad0 --- /dev/null +++ b/rstplib/times.c @@ -0,0 +1,76 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* "Times" API : bridgeTime, rootTimes, portTimes, designatedTimes, msgTimes */ + +#include "base.h" + +int +STP_compare_times (IN TIMEVALUES_T *t1, IN TIMEVALUES_T *t2) +{ + if (t1->MessageAge < t2->MessageAge) return -1; + if (t1->MessageAge > t2->MessageAge) return 1; + + if (t1->MaxAge < t2->MaxAge) return -2; + if (t1->MaxAge > t2->MaxAge) return 2; + + if (t1->ForwardDelay < t2->ForwardDelay) return -3; + if (t1->ForwardDelay > t2->ForwardDelay) return 3; + + if (t1->HelloTime < t2->HelloTime) return -4; + if (t1->HelloTime > t2->HelloTime) return 4; + + return 0; +} + +void +STP_get_times (IN BPDU_BODY_T *b, OUT TIMEVALUES_T *v) +{ + v->MessageAge = ntohs (*((unsigned short*) b->message_age)) >> 8; + v->MaxAge = ntohs (*((unsigned short*) b->max_age)) >> 8; + v->ForwardDelay = ntohs (*((unsigned short*) b->forward_delay)) >> 8; + v->HelloTime = ntohs (*((unsigned short*) b->hello_time)) >> 8; +} + +void +STP_set_times (IN TIMEVALUES_T *v, OUT BPDU_BODY_T *b) +{ + unsigned short mt; + #define STP_SET_TIME(f, t) \ + mt = htons (f << 8); \ + memcpy (t, &mt, 2); + + STP_SET_TIME(v->MessageAge, b->message_age); + STP_SET_TIME(v->MaxAge, b->max_age); + STP_SET_TIME(v->ForwardDelay, b->forward_delay); + STP_SET_TIME(v->HelloTime, b->hello_time); +} + +void +STP_copy_times (OUT TIMEVALUES_T *t, IN TIMEVALUES_T *f) +{ + t->MessageAge = f->MessageAge; + t->MaxAge = f->MaxAge; + t->ForwardDelay = f->ForwardDelay; + t->HelloTime = f->HelloTime; +} + diff --git a/rstplib/times.h b/rstplib/times.h new file mode 100644 index 0000000..4bdc1e2 --- /dev/null +++ b/rstplib/times.h @@ -0,0 +1,50 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* "Times" API : bridgeTime, rootTimes, portTimes, designatedTimes, msgTimes */ + +#ifndef _RSTP_TIMES_H__ +#define _RSTP_TIMES_H__ + +typedef struct timevalues_t { + unsigned short MessageAge; + unsigned short MaxAge; + unsigned short ForwardDelay; + unsigned short HelloTime; +} TIMEVALUES_T; + +int +STP_compare_times (IN TIMEVALUES_T* t1, IN TIMEVALUES_T* t2); + +void +STP_get_times (IN BPDU_BODY_T* b, OUT TIMEVALUES_T* v); + +void +STP_set_times (IN TIMEVALUES_T* v, OUT BPDU_BODY_T* b); + +void +STP_copy_times (OUT TIMEVALUES_T* t, IN TIMEVALUES_T* f); + +#endif /* _RSTP_TIMES_H__ */ + + + diff --git a/rstplib/topoch.c b/rstplib/topoch.c new file mode 100644 index 0000000..f17068e --- /dev/null +++ b/rstplib/topoch.c @@ -0,0 +1,236 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Topolgy Change state machine : 17.25 */ + +#include "base.h" +#include "stpm.h" +#include "stp_to.h" /* for STP_OUT_flush_lt */ + +#define STATES { \ + CHOOSE(INIT), \ + CHOOSE(INACTIVE), \ + CHOOSE(TCACTIVE), \ + CHOOSE(DETECTED), \ + CHOOSE(NOTIFIED_TC), \ + CHOOSE(PROPAGATING), \ + CHOOSE(ACKNOWLEDGED), \ + CHOOSE(NOTIFIED_TCN), \ +} + +#define GET_STATE_NAME STP_topoch_get_state_name +#include "choose.h" + +/* We can flush learned fdb by port, so set this in stpm.c and topoch.c */ +/* This doesn't seem to solve the topology change problems. Don't use it yet */ +//#define STRONGLY_SPEC_802_1W + +#ifndef STRONGLY_SPEC_802_1W +/* + * In many kinds of hardware the function + * STP_OUT_flush_lt is a) is very hard and b) cannot + * delete learning emtries per port. The alternate + * method may be used: we don't care operEdge flag here, + * but clean learning table once for TopologyChange + * for all ports, except the received port. I am ready to discuss :( + * See below word STRONGLY_SPEC_802_1W + */ +#else +static Bool +flush (STATE_MACH_T *this, char* reason) /* 17.19.9 */ +{ + register PORT_T* port = this->owner.port; + Bool bret; + + if (port->operEdge) return True; + if (this->debug) { + stp_trace("%s (%s, %s, %s, '%s')", + "flush", port->port_name, port->owner->name, + "this port", + reason); + } + + bret = STP_OUT_flush_lt (port->port_index, port->owner->vlan_id, + LT_FLASH_ONLY_THE_PORT, reason); + return bret; +} +#endif + +static void +setTcPropBridge (STATE_MACH_T* this, char* reason) /* 17.19.14 */ +{ + register PORT_T* port = this->owner.port; + register PORT_T* tmp; + + for (tmp = port->owner->ports; tmp; tmp = tmp->next) { + if (tmp->port_index != port->port_index) + tmp->tcProp = True; + } + +#ifndef STRONGLY_SPEC_802_1W +#ifdef STP_DBG + if (this->debug) { + stp_trace("%s (%s, %s, %s, '%s')", + "clearFDB", port->port_name, port->owner->name, + "other ports", reason); + } +#endif + + STP_OUT_flush_lt (port->port_index, port->owner->vlan_id, + LT_FLASH_ALL_PORTS_EXCLUDE_THIS, reason); +#endif +} + +static unsigned int +newTcWhile (STATE_MACH_T* this) /* 17.19.7 */ +{ + register PORT_T* port = this->owner.port; + + if (port->sendRSTP && port->operPointToPointMac) { + return 2 * port->owner->rootTimes.HelloTime; + } +#ifdef ORIG + return port->owner->rootTimes.MaxAge; +#else + return port->owner->rootTimes.MaxAge + port->owner->rootTimes.ForwardDelay; +#endif +} + +void +STP_topoch_enter_state (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + case INIT: +#ifdef STRONGLY_SPEC_802_1W + flush (this, "topoch INIT"); +#endif + port->tcWhile = 0; + port->tc = + port->tcProp = + port->tcAck = False; + break; + case INACTIVE: + port->rcvdTc = + port->rcvdTcn = + port->rcvdTcAck = port->tc = port->tcProp = False; + break; + case TCACTIVE: + break; + case DETECTED: + port->tcWhile = newTcWhile (this); +#ifdef STP_DBG + if (this->debug) + stp_trace("DETECTED: tcWhile=%d on port %s", + port->tcWhile, port->port_name); +#endif + setTcPropBridge (this, "DETECTED"); + port->tc = False; + break; + case NOTIFIED_TC: + port->rcvdTcn = port->rcvdTc = False; + if (port->role == DesignatedPort) { + port->tcAck = True; + } + setTcPropBridge (this, "NOTIFIED_TC"); + break; + case PROPAGATING: + port->tcWhile = newTcWhile (this); +#ifdef STP_DBG + if (this->debug) + stp_trace("PROPAGATING: tcWhile=%d on port %s", + port->tcWhile, port->port_name); +#endif +#ifdef STRONGLY_SPEC_802_1W + flush (this, "topoch PROPAGATING"); +#endif + port->tcProp = False; + break; + case ACKNOWLEDGED: + port->tcWhile = 0; +#ifdef STP_DBG + if (this->debug) + stp_trace("ACKNOWLEDGED: tcWhile=%d on port %s", + port->tcWhile, port->port_name); +#endif + port->rcvdTcAck = False; + break; + case NOTIFIED_TCN: + port->tcWhile = newTcWhile (this); +#ifdef STP_DBG + if (this->debug) + stp_trace("NOTIFIED_TCN: tcWhile=%d on port %s", + port->tcWhile, port->port_name); +#endif + break; + }; +} + +Bool +STP_topoch_check_conditions (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + if (BEGIN == this->State) { + return STP_hop_2_state (this, INIT); + } + + switch (this->State) { + case INIT: + return STP_hop_2_state (this, INACTIVE); + case INACTIVE: + if (port->role == RootPort || port->role == DesignatedPort) + return STP_hop_2_state (this, TCACTIVE); + if (port->rcvdTc || port->rcvdTcn || port->rcvdTcAck || + port->tc || port->tcProp) + return STP_hop_2_state (this, INACTIVE); + break; + case TCACTIVE: + if (port->role != RootPort && (port->role != DesignatedPort)) + return STP_hop_2_state (this, INIT); + if (port->tc) + return STP_hop_2_state (this, DETECTED); + if (port->rcvdTcn) + return STP_hop_2_state (this, NOTIFIED_TCN); + if (port->rcvdTc) + return STP_hop_2_state (this, NOTIFIED_TC); + if (port->tcProp && !port->operEdge) + return STP_hop_2_state (this, PROPAGATING); + if (port->rcvdTcAck) + return STP_hop_2_state (this, ACKNOWLEDGED); + break; + case DETECTED: + return STP_hop_2_state (this, TCACTIVE); + case NOTIFIED_TC: + return STP_hop_2_state (this, TCACTIVE); + case PROPAGATING: + return STP_hop_2_state (this, TCACTIVE); + case ACKNOWLEDGED: + return STP_hop_2_state (this, TCACTIVE); + case NOTIFIED_TCN: + return STP_hop_2_state (this, NOTIFIED_TC); + }; + return False; +} + diff --git a/rstplib/topoch.h b/rstplib/topoch.h new file mode 100644 index 0000000..8b7324c --- /dev/null +++ b/rstplib/topoch.h @@ -0,0 +1,37 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Topolgy Change state machine : 17.25 */ + +#ifndef _STP_TOPO_CHANGE_H__ +#define _STP_TOPO_CHANGE_H__ + +void +STP_topoch_enter_state (STATE_MACH_T* s); + +Bool +STP_topoch_check_conditions (STATE_MACH_T* s); + +char* STP_topoch_get_state_name (int state); + +#endif /* _STP_TOPO_CHANGE_H__ */ + diff --git a/rstplib/transmit.c b/rstplib/transmit.c new file mode 100644 index 0000000..9f0aeaf --- /dev/null +++ b/rstplib/transmit.c @@ -0,0 +1,382 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Transmit state machine : 17.27 */ + +#include "base.h" +#include "stpm.h" +#include "stp_to.h" /* for STP_OUT_get_port_mac & STP_OUT_tx_bpdu */ + +#define BPDU_LEN8023_OFF 12 + +#define STATES { \ + CHOOSE(TRANSMIT_INIT), \ + CHOOSE(TRANSMIT_PERIODIC), \ + CHOOSE(IDLE), \ + CHOOSE(TRANSMIT_CONFIG), \ + CHOOSE(TRANSMIT_TCN), \ + CHOOSE(TRANSMIT_RSTP), \ +} + +#define GET_STATE_NAME STP_transmit_get_state_name +#include "choose.h" + +#define MIN_FRAME_LENGTH 64 + + +typedef struct tx_tcn_bpdu_t { + MAC_HEADER_T mac; + ETH_HEADER_T eth; + BPDU_HEADER_T hdr; +} TCN_BPDU_T; + +typedef struct tx_stp_bpdu_t { + MAC_HEADER_T mac; + ETH_HEADER_T eth; + BPDU_HEADER_T hdr; + BPDU_BODY_T body; +} CONFIG_BPDU_T; + +typedef struct tx_rstp_bpdu_t { + MAC_HEADER_T mac; + ETH_HEADER_T eth; + BPDU_HEADER_T hdr; + BPDU_BODY_T body; + unsigned char ver_1_length[2]; +} RSTP_BPDU_T; + + +static RSTP_BPDU_T bpdu_packet = { + {/* MAC_HEADER_T */ + {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}, /* dst_mac */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* src_mac */ + }, + { /* ETH_HEADER_T */ + {0x00, 0x00}, /* len8023 */ + BPDU_L_SAP, BPDU_L_SAP, LLC_UI /* dsap, ssap, llc */ + }, + {/* BPDU_HEADER_T */ + {0x00, 0x00}, /* protocol */ + BPDU_VERSION_ID, 0x00 /* version, bpdu_type */ + }, + { + 0x00, /* flags; */ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* root_id[8]; */ + {0x00,0x00,0x00,0x00}, /* root_path_cost[4]; */ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, /* bridge_id[8]; */ + {0x00,0x00}, /* port_id[2]; */ + {0x00,0x00}, /* message_age[2]; */ + {0x00,0x00}, /* max_age[2]; */ + {0x00,0x00}, /* hello_time[2]; */ + {0x00,0x00}, /* forward_delay[2]; */ + }, + {0x00,0x00}, /* ver_1_length[2]; */ +}; + +static size_t +build_bpdu_header (int port_index, + unsigned char bpdu_type, + unsigned short pkt_len) +{ + unsigned short len8023; + +#ifdef ORIG + STP_OUT_get_port_mac (port_index, bpdu_packet.mac.src_mac); +#else + /* Don't bother. LLC trasmits with correct source MAC, we don't supply it */ +#endif + + bpdu_packet.hdr.bpdu_type = bpdu_type; + bpdu_packet.hdr.version = (BPDU_RSTP == bpdu_type) ? + BPDU_VERSION_RAPID_ID : + BPDU_VERSION_ID; + + /* NOTE: I suppose, that sizeof(unsigned short)=2 ! */ + len8023 = htons ((unsigned short) (pkt_len + 3)); + memcpy (&bpdu_packet.eth.len8023, &len8023, 2); + +#ifdef ORIG + if (pkt_len < MIN_FRAME_LENGTH) pkt_len = MIN_FRAME_LENGTH; +#else + /* Don't do this. LLC puts in 802.3 length based on what we transmit */ +#endif + return pkt_len; +} + +static int +txTcn (STATE_MACH_T* this) +{ /* 17.19.17 (page 68) & 9.3.2 (page 25) */ + register size_t pkt_len; + register int port_index, vlan_id; + +#ifdef STP_DBG + if (this->owner.port->skip_tx > 0) { + if (1 == this->owner.port->skip_tx) + stp_trace ("port %s stop tx skipping", + this->owner.port->port_name); + this->owner.port->skip_tx--; + return STP_Nothing_To_Do; + } +#endif + + if (this->owner.port->admin_non_stp) return 1; + port_index = this->owner.port->port_index; + vlan_id = this->owner.port->owner->vlan_id; + + pkt_len = build_bpdu_header (port_index, + BPDU_TOPO_CHANGE_TYPE, + sizeof (BPDU_HEADER_T)); + +#ifdef STP_DBG + if (this->debug) + stp_trace ("port %s txTcn", this->owner.port->port_name); +#endif + return STP_OUT_tx_bpdu (port_index, vlan_id, + (unsigned char *) &bpdu_packet, + pkt_len); +} + +static void +build_config_bpdu (PORT_T* port, Bool set_topo_ack_flag) +{ + bpdu_packet.body.flags = 0; + if (port->tcWhile) { +#ifdef STP_DBG + if (port->topoch->debug) + stp_trace ("tcWhile=%d =>tx TOLPLOGY_CHANGE_BIT to port %s", + (int) port->tcWhile, port->port_name); +#endif + bpdu_packet.body.flags |= TOLPLOGY_CHANGE_BIT; + } + + if (set_topo_ack_flag && port->tcAck) { + bpdu_packet.body.flags |= TOLPLOGY_CHANGE_ACK_BIT; + } + + STP_VECT_set_vector (&port->portPrio, &bpdu_packet.body); + STP_set_times (&port->portTimes, &bpdu_packet.body); +} + +static int +txConfig (STATE_MACH_T* this) +{/* 17.19.15 (page 67) & 9.3.1 (page 23) */ + register size_t pkt_len; + register PORT_T* port = NULL; + register int port_index, vlan_id; + +#ifdef STP_DBG + if (this->owner.port->skip_tx > 0) { + if (1 == this->owner.port->skip_tx) + stp_trace ("port %s stop tx skipping", + this->owner.port->port_name); + this->owner.port->skip_tx--; + return STP_Nothing_To_Do; + } +#endif + + port = this->owner.port; + if (port->admin_non_stp) return 1; + port_index = port->port_index; + vlan_id = port->owner->vlan_id; + + pkt_len = build_bpdu_header (port->port_index, + BPDU_CONFIG_TYPE, + sizeof (BPDU_HEADER_T) + sizeof (BPDU_BODY_T)); + build_config_bpdu (port, True); + +#ifdef STP_DBG + if (this->debug) + stp_trace ("port %s txConfig flags=0X%lx", + port->port_name, + (unsigned long) bpdu_packet.body.flags); +#endif + return STP_OUT_tx_bpdu (port_index, vlan_id, + (unsigned char *) &bpdu_packet, + pkt_len); +} + +static int +txRstp (STATE_MACH_T* this) +{/* 17.19.16 (page 68) & 9.3.3 (page 25) */ + register size_t pkt_len; + register PORT_T* port = NULL; + register int port_index, vlan_id; + unsigned char role; + +#ifdef STP_DBG + if (this->owner.port->skip_tx > 0) { + if (1 == this->owner.port->skip_tx) + stp_trace ("port %s stop tx skipping", + this->owner.port->port_name); + else + stp_trace ("port %s skip tx %d", + this->owner.port->port_name, this->owner.port->skip_tx); + + this->owner.port->skip_tx--; + return STP_Nothing_To_Do; + } +#endif + + port = this->owner.port; + if (port->admin_non_stp) return 1; + port_index = port->port_index; + vlan_id = port->owner->vlan_id; + + pkt_len = build_bpdu_header (port->port_index, + BPDU_RSTP, + sizeof (BPDU_HEADER_T) + sizeof (BPDU_BODY_T) + 1); + build_config_bpdu (port, False); + + switch (port->selectedRole) { + default: + case DisabledPort: + role = RSTP_PORT_ROLE_UNKN; + break; + case AlternatePort: + role = RSTP_PORT_ROLE_ALTBACK; + break; + case BackupPort: + role = RSTP_PORT_ROLE_ALTBACK; + break; + case RootPort: + role = RSTP_PORT_ROLE_ROOT; + break; + case DesignatedPort: + role = RSTP_PORT_ROLE_DESGN; + break; + } + + bpdu_packet.body.flags |= (role << PORT_ROLE_OFFS); +#ifndef ORIG + if (port->forwarding) + bpdu_packet.body.flags |= FORWARD_BIT; + if (port->learning) + bpdu_packet.body.flags |= LEARN_BIT; +#endif + if (port->synced) { +#if 0 /* def STP_DBG */ + if (port->roletrns->debug) + stp_trace ("tx AGREEMENT_BIT to port %s", port->port_name); +#endif + bpdu_packet.body.flags |= AGREEMENT_BIT; + } + + if (port->proposing) { +#if 0 /* def STP_DBG */ + if (port->roletrns->debug) + stp_trace ("tx PROPOSAL_BIT to port %s", port->port_name); +#endif + bpdu_packet.body.flags |= PROPOSAL_BIT; + } + +#ifdef STP_DBG + if (this->debug) + stp_trace ("port %s txRstp flags=0X%lx", + port->port_name, + (unsigned long) bpdu_packet.body.flags); +#endif + + return STP_OUT_tx_bpdu (port_index, vlan_id, + (unsigned char *) &bpdu_packet, + pkt_len); +} + +void +STP_transmit_enter_state (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + switch (this->State) { + case BEGIN: + case TRANSMIT_INIT: + port->newInfo = False; + port->helloWhen = 0; + port->txCount = 0; + break; + case TRANSMIT_PERIODIC: + port->newInfo = port->newInfo || + ((port->role == DesignatedPort) || + ((port->role == RootPort) && port->tcWhile)); + port->helloWhen = port->owner->rootTimes.HelloTime; + break; + case IDLE: + break; + case TRANSMIT_CONFIG: + port->newInfo = False; + txConfig (this); + port->txCount++; + port->tcAck = False; + break; + case TRANSMIT_TCN: + port->newInfo = False; + txTcn (this); + port->txCount++; + break; + case TRANSMIT_RSTP: + port->newInfo = False; + txRstp (this); + port->txCount++; + port->tcAck = False; + break; + }; +} + +Bool +STP_transmit_check_conditions (STATE_MACH_T* this) +{ + register PORT_T* port = this->owner.port; + + if (BEGIN == this->State) return STP_hop_2_state (this, TRANSMIT_INIT); + + switch (this->State) { + case TRANSMIT_INIT: + return STP_hop_2_state (this, IDLE); + case TRANSMIT_PERIODIC: + return STP_hop_2_state (this, IDLE); + case IDLE: + if (!port->helloWhen) return STP_hop_2_state (this, TRANSMIT_PERIODIC); + if (!port->sendRSTP && port->newInfo && + (port->txCount < TxHoldCount) && + (port->role == DesignatedPort) && + port->helloWhen) + return STP_hop_2_state (this, TRANSMIT_CONFIG); + if (!port->sendRSTP && port->newInfo && + (port->txCount < TxHoldCount) && + (port->role == RootPort) && + port->helloWhen) + return STP_hop_2_state (this, TRANSMIT_TCN); + if (port->sendRSTP && port->newInfo && + (port->txCount < TxHoldCount) && + ((port->role == RootPort) || + (port->role == DesignatedPort))) + return STP_hop_2_state (this, TRANSMIT_RSTP); + break; + case TRANSMIT_CONFIG: + return STP_hop_2_state (this, IDLE); + case TRANSMIT_TCN: + return STP_hop_2_state (this, IDLE); + case TRANSMIT_RSTP: + return STP_hop_2_state (this, IDLE); + }; + return False; +} + diff --git a/rstplib/transmit.h b/rstplib/transmit.h new file mode 100644 index 0000000..620013c --- /dev/null +++ b/rstplib/transmit.h @@ -0,0 +1,38 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* Port Transmit state machine : 17.27 */ + +#ifndef _STP_TRANSMIT_H__ +#define _STP_TRANSMIT_H__ + +void +STP_transmit_enter_state (STATE_MACH_T* s); + +Bool +STP_transmit_check_conditions (STATE_MACH_T* s); + +char* +STP_transmit_get_state_name (int state); + +#endif /* _STP_TRANSMIT_H__ */ + diff --git a/rstplib/uid.h b/rstplib/uid.h new file mode 100644 index 0000000..a3acc23 --- /dev/null +++ b/rstplib/uid.h @@ -0,0 +1,63 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#include "uid_sock.h" + + +typedef enum { + UID_CNTRL = 0, + UID_BPDU +} UID_CMD_TYPE_T; + +typedef enum { + UID_PORT_CONNECT, + UID_PORT_DISCONNECT, + UID_BRIDGE_SHUTDOWN, + UID_BRIDGE_HANDSHAKE, + UID_LAST_DUMMY +} UID_CNTRL_CMD_T; + +typedef struct uid_port_control_s { + UID_CNTRL_CMD_T cmd; + unsigned long param1; + unsigned long param2; +} UID_CNTRL_BODY_T; + +typedef struct uid_msg_header_s { + UID_CMD_TYPE_T cmd_type; + long sender_pid; + int destination_port; + int source_port; + size_t body_len; +} UID_MSG_HEADER_T; + +typedef struct uid_msg_s { + UID_MSG_HEADER_T header; + union { + UID_CNTRL_BODY_T cntrl; + char bpdu[64]; + } body; + +} UID_MSG_T; + +#define MAX_UID_MSG_SIZE sizeof(UID_MSG_T) + diff --git a/rstplib/uid_sock.c b/rstplib/uid_sock.c new file mode 100644 index 0000000..d872a10 --- /dev/null +++ b/rstplib/uid_sock.c @@ -0,0 +1,208 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/time.h> +#include <unistd.h> + +#include "uid_sock.h" + +static int Socket(int family, int type, int protocol) +{ + int sock_fd; + + sock_fd = socket(family, type, protocol); + + if (sock_fd < 0) { + return -1; + } + + return sock_fd; +} + +#define UID_SET_SOCKET_ID(SPTR, X) \ + strncpy((SPTR)->socket_id, (X),SOCKET_NAME_LENGTH); \ + (SPTR)->socket_id[SOCKET_NAME_LENGTH - 1] = '\0'; + +#define UID_SET_SOCKET_SERVER_ID(SPTR, X) \ + (SPTR)->serverAddr.sun_path[0] = '\0'; \ + strncpy((SPTR)->serverAddr.sun_path + 1, (X),SOCKET_NAME_LENGTH - 1); \ + (SPTR)->serverAddr.sun_path[SOCKET_NAME_LENGTH - 1] = '\0'; + +#define UID_SET_SOCKET_CLIENT_ID(SPTR, X) \ + (SPTR)->clientAddr.sun_path[0] = '\0'; \ + strncpy((SPTR)->clientAddr.sun_path + 1, (X),SOCKET_NAME_LENGTH - 1); \ + (SPTR)->clientAddr.sun_path[SOCKET_NAME_LENGTH - 1] = '\0'; + +int +UiD_SocketCompare (UID_SOCKET_T* s, UID_SOCKET_T* t) +{ + register char* ps; + register char* pt; + + ps = s->clientAddr.sun_path; + if (! *ps) ps++; + if (! *ps) return 1; + + pt = t->clientAddr.sun_path; + if (! *pt) pt++; + if (! *pt) return 2; + + if (strcmp (pt, ps)) + return 3; + else { + return 0; + } +} + +int UiD_SocketInit(UID_SOCKET_T* s, + UID_SOCK_ID socket_id, + TYPE_OF_BINDING binding) +{ + bzero(s, sizeof (UID_SOCKET_T)); + + s->sock_fd = Socket(AF_LOCAL, SOCK_DGRAM, 0); + + s->clientAddr.sun_family = AF_LOCAL; + + s->serverAddr.sun_family = AF_LOCAL; + + + switch (binding) { + case UID_BIND_AS_CLIENT: + strncpy (s->socket_id, tmpnam(NULL),SOCKET_NAME_LENGTH ); + UID_SET_SOCKET_CLIENT_ID(s,s->socket_id ); + if (bind(s->sock_fd, (SA *)&(s->clientAddr), SIZE_OF_ADDRESS) < 0) { + return -2; + } + bzero(&s->serverAddr, SIZE_OF_ADDRESS); + s->serverAddr.sun_family = AF_LOCAL; + UID_SET_SOCKET_SERVER_ID(s, socket_id); + + break; + + case UID_BIND_AS_SERVER: + unlink(socket_id); + strncpy (s->socket_id, socket_id, SOCKET_NAME_LENGTH); + UID_SET_SOCKET_SERVER_ID(s,s->socket_id); + + if (bind(s->sock_fd, (SA *)&(s->serverAddr), SIZE_OF_ADDRESS) < 0) { + perror ("Error:"); + fflush (stdout); + return -4; + } + + break; + + default: + return -5; + } + + s->binding = binding; + + return 0; +} + +int UiD_SocketRecvfrom (UID_SOCKET_T* sock, + void* msg, int buffer_size, + UID_SOCKET_T* sock_4_reply) +{ + int size; + socklen_t len = SIZE_OF_ADDRESS; + + while (1) { + size = recvfrom(sock->sock_fd, msg, buffer_size, 0, + (struct sockaddr *)(((UID_BIND_AS_CLIENT == sock->binding) || !sock_4_reply) ? + NULL : &(sock_4_reply->clientAddr)), + (UID_BIND_AS_CLIENT == (sock->binding)) ? + NULL : &len); + if (size < 0 && errno == EINTR) continue; + break; + } + + if ((UID_BIND_AS_CLIENT != sock->binding) && sock_4_reply) { + sock_4_reply->sock_fd = sock->sock_fd; + sock_4_reply->binding = UID_BIND_AS_SERVER; + } + + return size; +} + +int UiD_SocketSendto (UID_SOCKET_T* sock, void* msg, int msg_size) +{ + int rc, size = 0; + + while (size != msg_size) { + rc = sendto (sock->sock_fd, (msg+size), (msg_size-size), 0, + (struct sockaddr *)((UID_BIND_AS_CLIENT == sock->binding) ? &sock->serverAddr : &sock->clientAddr), + SIZE_OF_ADDRESS); + + if (rc < 0) { + if (errno == EINTR) { + continue; + } else { + return -1; + } + } + size += rc; + } + + return 0; +} + +int UiD_SocketClose(UID_SOCKET_T* s) +{ + close (s->sock_fd); + + return 0; +} + +int UiD_SocketSetReadTimeout (UID_SOCKET_T* s, int timeout) +{ + int retval = -1; + struct timeval wait; + fd_set read_mask; + + wait.tv_sec = timeout; + wait.tv_usec = 0; + + FD_ZERO(&read_mask); + FD_SET(s->sock_fd, &read_mask); + + retval = select (s->sock_fd + 1, &read_mask, NULL, NULL, &wait); + + if (retval < 0) { // Error + fprintf (stderr, "UiD_SocketSetReadTimeout failed: %s\n", strerror(errno)); + return 0; + } else if (! retval) { // Timeout expired + return 0; + } else { // 0 + ; + } + + return retval; +} + + diff --git a/rstplib/uid_sock.h b/rstplib/uid_sock.h new file mode 100644 index 0000000..a3d0fd7 --- /dev/null +++ b/rstplib/uid_sock.h @@ -0,0 +1,81 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +#ifndef _UID_SOCKET_H__ +#define _UID_SOCKET_H__ + +/* Socket API */ + +#include <sys/socket.h> /* basic socket definitions */ +#include <netinet/in.h> +#include <linux/un.h> /* for Unix domain sockets */ + +#define UID_REPL_PATH "/tmp/UidSocketFile" + +typedef struct sockaddr SA; + +#define SOCKET_NAME_LENGTH 108 +#define SIZE_OF_ADDRESS sizeof(struct sockaddr_un) + +typedef enum { + UID_BIND_AS_CLIENT, + UID_BIND_AS_SERVER +} TYPE_OF_BINDING; + + +typedef char UID_SOCK_ID[SOCKET_NAME_LENGTH]; + +typedef struct{ + int sock_fd; + struct sockaddr_un clientAddr; + struct sockaddr_un serverAddr; // Only for socket of UID_BIND_AS_CLIENT + UID_SOCK_ID socket_id; + TYPE_OF_BINDING binding; +} UID_SOCKET_T; + +#define MESSAGE_SIZE 2048 + +int UiD_SocketInit(UID_SOCKET_T* sock, + UID_SOCK_ID id, + TYPE_OF_BINDING binding); + +int UiD_SocketRecvfrom (UID_SOCKET_T* sock, + void* msg_buffer, + int buffer_size, + UID_SOCKET_T* sock_4_reply); + +int UiD_SocketSendto (UID_SOCKET_T* sock, + void* msg_buffer, + int buffer_size); + +int UiD_SocketClose(UID_SOCKET_T* sock); + +int UiD_SocketSetReadTimeout (UID_SOCKET_T* s, int timeout); + +int +UiD_SocketCompare (UID_SOCKET_T* s, UID_SOCKET_T* t); + +#define GET_FILE_DESCRIPTOR(sock) (((UID_SOCKET_T*)sock)->sock_fd) + +#endif /* _UID_SOCKET_H__ */ + + diff --git a/rstplib/uid_stp.h b/rstplib/uid_stp.h new file mode 100644 index 0000000..5cdbb44 --- /dev/null +++ b/rstplib/uid_stp.h @@ -0,0 +1,204 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* External management communication API definitions */ + +#ifndef _UID_STP_H__ +#define _UID_STP_H__ + +#define NAME_LEN 20 + +typedef enum { + STP_DISABLED, + STP_ENABLED, +} UID_STP_MODE_T; + +typedef struct { + unsigned short prio; + unsigned char addr[6]; +} UID_BRIDGE_ID_T; + +typedef struct { + char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ + char action; /* 1-create, 0- delete */ +} UID_STP_BR_CTRL_T; + +#define BR_CFG_STATE (1L << 0) +#define BR_CFG_PRIO (1L << 1) +#define BR_CFG_AGE (1L << 2) +#define BR_CFG_HELLO (1L << 3) +#define BR_CFG_DELAY (1L << 4) +#define BR_CFG_FORCE_VER (1L << 5) +#define BR_CFG_AGE_MODE (1L << 6) +#define BR_CFG_AGE_TIME (1L << 7) +#define BR_CFG_HOLD_TIME (1L << 8) +#define BR_CFG_ALL BR_CFG_STATE | \ + BR_CFG_PRIO | \ + BR_CFG_AGE | \ + BR_CFG_HELLO | \ + BR_CFG_DELAY | \ + BR_CFG_FORCE_VER | \ + BR_CFG_AGE_MODE | \ + BR_CFG_AGE_TIME | \ + BR_CFG_HOLD_TIME + +typedef struct { + /* service data */ + unsigned long field_mask; /* which fields to change */ + UID_STP_MODE_T stp_enabled; + char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ + + /* protocol data */ + int bridge_priority; + int max_age; + int hello_time; + int forward_delay; + int force_version; + int hold_time; +} UID_STP_CFG_T; + +typedef struct { + /* service data */ + char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ + unsigned long vlan_id; + UID_STP_MODE_T stp_enabled; + + /* protocol data */ + UID_BRIDGE_ID_T designated_root; + unsigned long root_path_cost; + + unsigned long timeSince_Topo_Change; /* 14.8.1.1.3.b: TBD */ + unsigned long Topo_Change_Count; /* 14.8.1.1.3.c: TBD */ + unsigned char Topo_Change; /* 14.8.1.1.3.d: TBD */ + + unsigned short root_port; + int max_age; + int hello_time; + int forward_delay; + UID_BRIDGE_ID_T bridge_id; +} UID_STP_STATE_T; + +typedef enum { + UID_PORT_DISABLED = 0, + UID_PORT_DISCARDING, + UID_PORT_LEARNING, + UID_PORT_FORWARDING, + UID_PORT_NON_STP +} RSTP_PORT_STATE; + +typedef unsigned short UID_PORT_ID; + +typedef enum { + P2P_FORCE_TRUE, + P2P_FORCE_FALSE, + P2P_AUTO, +} ADMIN_P2P_T; + +#ifdef __BITMAP_H + +#define PT_CFG_STATE (1L << 0) +#define PT_CFG_COST (1L << 1) +#define PT_CFG_PRIO (1L << 2) +#define PT_CFG_P2P (1L << 3) +#define PT_CFG_EDGE (1L << 4) +#define PT_CFG_MCHECK (1L << 5) +#define PT_CFG_NON_STP (1L << 6) +#ifdef STP_DBG +#define PT_CFG_DBG_SKIP_RX (1L << 16) +#define PT_CFG_DBG_SKIP_TX (1L << 17) +#endif + +#define PT_CFG_ALL PT_CFG_STATE | \ + PT_CFG_COST | \ + PT_CFG_PRIO | \ + PT_CFG_P2P | \ + PT_CFG_EDGE | \ + PT_CFG_MCHECK | \ + PT_CFG_NON_STP + +#define ADMIN_PORT_PATH_COST_AUTO 0 + +typedef struct { + /* service data */ + unsigned long field_mask; /* which fields to change */ + BITMAP_T port_bmp; + char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ + + /* protocol data */ + int port_priority; + unsigned long admin_port_path_cost; /* ADMIN_PORT_PATH_COST_AUTO - auto sence */ + ADMIN_P2P_T admin_point2point; + unsigned char admin_edge; + unsigned char admin_non_stp; /* 1- doesn't participate in STP, 1 - regular */ +#ifdef STP_DBG + unsigned int skip_rx; + unsigned int skip_tx; +#endif + +} UID_STP_PORT_CFG_T; +#endif + +typedef struct { + /* service data */ + char vlan_name[NAME_LEN]; /* name of the VLAN, key of the bridge */ + unsigned int port_no; /* key of the entry */ + + /* protocol data */ + UID_PORT_ID port_id; + RSTP_PORT_STATE state; + unsigned long path_cost; + + UID_BRIDGE_ID_T designated_root; + unsigned long designated_cost; + UID_BRIDGE_ID_T designated_bridge; + UID_PORT_ID designated_port; + +#if 0 + int infoIs; + unsigned short handshake_flags; +#endif + + unsigned long rx_cfg_bpdu_cnt; + unsigned long rx_rstp_bpdu_cnt; + unsigned long rx_tcn_bpdu_cnt; + int fdWhile; /* 17.15.1 */ + int helloWhen; /* 17.15.2 */ + int mdelayWhile; /* 17.15.3 */ + int rbWhile; /* 17.15.4 */ + int rcvdInfoWhile;/* 17.15.5 */ + int rrWhile; /* 17.15.6 */ + int tcWhile; /* 17.15.7 */ + int txCount; /* 17.18.40 */ + int lnkWhile; + + unsigned long uptime; /* 14.8.2.1.3.a */ + unsigned long oper_port_path_cost; + unsigned char role; + unsigned char oper_point2point; + unsigned char oper_edge; + unsigned char oper_stp_neigb; + unsigned char top_change_ack; + unsigned char tc; +} UID_STP_PORT_STATE_T; + +#endif + diff --git a/rstplib/vector.c b/rstplib/vector.c new file mode 100644 index 0000000..00e4282 --- /dev/null +++ b/rstplib/vector.c @@ -0,0 +1,177 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* STP priority vectors API : 17.4.2 */ + +#include "base.h" +#include "stp_bpdu.h" +#include "vector.h" + +int +STP_VECT_compare_bridge_id (BRIDGE_ID* b1, BRIDGE_ID* b2) +{ + if (b1->prio < b2->prio) + return -1; + + if (b1->prio > b2->prio) + return 1; + return memcmp (b1->addr, b2->addr, 6); +} + +void +STP_VECT_copy (OUT PRIO_VECTOR_T* t, IN PRIO_VECTOR_T* f) +{ + memcpy (t, f, sizeof (PRIO_VECTOR_T)); +} + +void +STP_VECT_create (OUT PRIO_VECTOR_T* t, + IN BRIDGE_ID* root_br, + IN unsigned long root_path_cost, + IN BRIDGE_ID* design_bridge, + IN PORT_ID design_port, + IN PORT_ID bridge_port) +{ + memcpy (&t->root_bridge, root_br, sizeof (BRIDGE_ID)); + t->root_path_cost = root_path_cost; + memcpy (&t->design_bridge, design_bridge, sizeof (BRIDGE_ID)); + t->design_port = design_port; + t->bridge_port = bridge_port; +} + +int +STP_VECT_compare_vector (PRIO_VECTOR_T* v1, PRIO_VECTOR_T* v2) +{ + int bridcmp; + + bridcmp = STP_VECT_compare_bridge_id (&v1->root_bridge, &v2->root_bridge); + if (bridcmp < 0) return bridcmp; + + if (! bridcmp) { + bridcmp = v1->root_path_cost - v2->root_path_cost; + if (bridcmp < 0) return bridcmp; + if (! bridcmp) { + bridcmp = STP_VECT_compare_bridge_id (&v1->design_bridge, &v2->design_bridge); + if (bridcmp < 0) return bridcmp; + if (! bridcmp) { + bridcmp = v1->design_port - v2->design_port; + if (bridcmp < 0) return bridcmp; + if (! bridcmp) + return v1->bridge_port - v2->bridge_port; + } + } + } + + return bridcmp; +} + +static unsigned short +stp_vect_get_short (IN unsigned char* f) +{ + return ntohs (*(unsigned short *)f); +} + +static void +stp_vect_set_short (IN unsigned short f, OUT unsigned char* t) +{ + *(unsigned short *)t = htons (f); +} + +static void +stp_vect_get_bridge_id (IN unsigned char* c_br, OUT BRIDGE_ID* bridge_id) +{ + bridge_id->prio = stp_vect_get_short (c_br); + memcpy (bridge_id->addr, c_br + 2, 6); +} + +static void +stp_vect_set_bridge_id (IN BRIDGE_ID* bridge_id, OUT unsigned char* c_br) +{ + stp_vect_set_short (bridge_id->prio, c_br); + memcpy (c_br + 2, bridge_id->addr, 6); +} + +void +STP_VECT_get_vector (IN BPDU_BODY_T* b, OUT PRIO_VECTOR_T* v) +{ + stp_vect_get_bridge_id (b->root_id, &v->root_bridge); + + v->root_path_cost = ntohl (*((long*) b->root_path_cost)); + + stp_vect_get_bridge_id (b->bridge_id, &v->design_bridge); + + v->design_port = stp_vect_get_short (b->port_id); +} + +void +STP_VECT_set_vector (IN PRIO_VECTOR_T* v, OUT BPDU_BODY_T* b) +{ + unsigned long root_path_cost; + + stp_vect_set_bridge_id (&v->root_bridge, b->root_id); + + root_path_cost = htonl (v->root_path_cost); + memcpy (b->root_path_cost, &root_path_cost, 4); + + stp_vect_set_bridge_id (&v->design_bridge, b->bridge_id); + + stp_vect_set_short (v->design_port, b->port_id); +} + +#ifdef STP_DBG + +void +STP_VECT_br_id_print (IN char *title, IN BRIDGE_ID* br_id, IN Bool cr) +{ + Print ("%s=%04lX-%02x%02x%02x%02x%02x%02x", + title, + (unsigned long) br_id->prio, + (unsigned char) br_id->addr[0], + (unsigned char) br_id->addr[1], + (unsigned char) br_id->addr[2], + (unsigned char) br_id->addr[3], + (unsigned char) br_id->addr[4], + (unsigned char) br_id->addr[5]); + Print (cr ? "\n" : " "); +} + +void +STP_VECT_print (IN char *title, IN PRIO_VECTOR_T *v) +{ + Print ("%s:", title); + STP_VECT_br_id_print ("rootBr", &v->root_bridge, False); + +/**** + Print (" rpc=%ld ", (long) v->root_path_cost); +****/ + + STP_VECT_br_id_print ("designBr", &v->design_bridge, False); + +/****/ + Print (" dp=%lx bp=%lx ", + (unsigned long) v->design_port, + (unsigned long) v->bridge_port); +/***********/ + Print ("\n"); +} +#endif + diff --git a/rstplib/vector.h b/rstplib/vector.h new file mode 100644 index 0000000..e5e71f8 --- /dev/null +++ b/rstplib/vector.h @@ -0,0 +1,77 @@ +/************************************************************************ + * RSTP library - Rapid Spanning Tree (802.1t, 802.1w) + * Copyright (C) 2001-2003 Optical Access + * Author: Alex Rozin + * + * This file is part of RSTP library. + * + * RSTP library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation; version 2.1 + * + * RSTP library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with RSTP library; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + **********************************************************************/ + +/* STP priority vectors API : 17.4.2 */ + +#ifndef _PRIO_VECTOR_H__ +#define _PRIO_VECTOR_H__ + +typedef struct bridge_id +{ + unsigned short prio; + unsigned char addr[6]; +} BRIDGE_ID; + +typedef unsigned short PORT_ID; + +typedef struct prio_vector_t { + BRIDGE_ID root_bridge; + unsigned long root_path_cost; + BRIDGE_ID design_bridge; + PORT_ID design_port; + PORT_ID bridge_port; +} PRIO_VECTOR_T; + +void +STP_VECT_create (OUT PRIO_VECTOR_T* t, + IN BRIDGE_ID* root_br, + IN unsigned long root_path_cost, + IN BRIDGE_ID* design_bridge, + IN PORT_ID design_port, + IN PORT_ID bridge_port); +void +STP_VECT_copy (OUT PRIO_VECTOR_T* t, IN PRIO_VECTOR_T* f); + +int +STP_VECT_compare_bridge_id (IN BRIDGE_ID* b1, IN BRIDGE_ID* b2); + +int +STP_VECT_compare_vector (IN PRIO_VECTOR_T* v1, IN PRIO_VECTOR_T* v2); + +void +STP_VECT_get_vector (IN BPDU_BODY_T* b, OUT PRIO_VECTOR_T* v); + +void +STP_VECT_set_vector (IN PRIO_VECTOR_T* v, OUT BPDU_BODY_T* b); + +#ifdef STP_DBG +void +STP_VECT_print (IN char* title, IN PRIO_VECTOR_T* v); + +void +STP_VECT_br_id_print (IN char *title, IN BRIDGE_ID* br_id, IN Bool cr); + +#endif + +#endif /* _PRIO_VECTOR_H__ */ + + |