]> git.ipfire.org Git - people/ms/rstp.git/commitdiff
Initial commit
authorStephen Hemminger <shemminger@linux-foundation.org>
Wed, 21 Mar 2007 23:23:46 +0000 (16:23 -0700)
committerStephen Hemminger <shemminger@linux-foundation.org>
Wed, 21 Mar 2007 23:23:46 +0000 (16:23 -0700)
sources from  Aji_Srinivas@emc.com

97 files changed:
.depend [new file with mode: 0644]
CHANGES_TO_RSTPLIB [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile [new file with mode: 0644]
TODO [new file with mode: 0644]
bpdu_sock.c [new file with mode: 0644]
bpdu_sock.h [new file with mode: 0644]
bridge [new file with mode: 0755]
bridge_ctl.h [new file with mode: 0644]
bridge_track.c [new file with mode: 0644]
brmon.c [new file with mode: 0644]
brstate.c [new file with mode: 0644]
ctl_cli_wrap.c [new file with mode: 0644]
ctl_functions.h [new file with mode: 0644]
ctl_main.c [new file with mode: 0644]
ctl_socket.c [new file with mode: 0644]
ctl_socket.h [new file with mode: 0644]
ctl_socket_client.c [new file with mode: 0644]
ctl_socket_client.h [new file with mode: 0644]
ctl_socket_server.h [new file with mode: 0644]
epoll_loop.c [new file with mode: 0644]
epoll_loop.h [new file with mode: 0644]
include/libnetlink.h [new file with mode: 0644]
include/linux/llc.h [new file with mode: 0644]
include/linux/rtnetlink.h [new file with mode: 0644]
libnetlink.c [new file with mode: 0644]
log.h [new file with mode: 0644]
main.c [new file with mode: 0644]
netif_utils.c [new file with mode: 0644]
netif_utils.h [new file with mode: 0644]
rstp.spec [new file with mode: 0644]
rstpctl.8 [new file with mode: 0644]
rstpd.8 [new file with mode: 0644]
rstplib/AUTHORS [new file with mode: 0644]
rstplib/Authors [new file with mode: 0644]
rstplib/B5055.txt [new file with mode: 0644]
rstplib/B5056.txt [new file with mode: 0644]
rstplib/COPYING [new file with mode: 0644]
rstplib/CVS.HOWTO [new file with mode: 0644]
rstplib/ChangeLog [new file with mode: 0644]
rstplib/INSTALL [new file with mode: 0644]
rstplib/Makefile [new file with mode: 0644]
rstplib/NEWS [new file with mode: 0644]
rstplib/README [new file with mode: 0644]
rstplib/README.files [new file with mode: 0644]
rstplib/TODO [new file with mode: 0644]
rstplib/base.h [new file with mode: 0644]
rstplib/bitmap.h [new file with mode: 0644]
rstplib/bridge.c [new file with mode: 0644]
rstplib/choose.h [new file with mode: 0644]
rstplib/cli.c [new file with mode: 0644]
rstplib/cli.h [new file with mode: 0644]
rstplib/edge.c [new file with mode: 0644]
rstplib/edge.h [new file with mode: 0644]
rstplib/migrate.c [new file with mode: 0644]
rstplib/migrate.h [new file with mode: 0644]
rstplib/mngr.c [new file with mode: 0644]
rstplib/mngr.txt [new file with mode: 0644]
rstplib/p2p.c [new file with mode: 0644]
rstplib/p2p.h [new file with mode: 0644]
rstplib/pcost.c [new file with mode: 0644]
rstplib/pcost.h [new file with mode: 0644]
rstplib/port.c [new file with mode: 0644]
rstplib/port.h [new file with mode: 0644]
rstplib/portinfo.c [new file with mode: 0644]
rstplib/portinfo.h [new file with mode: 0644]
rstplib/rolesel.c [new file with mode: 0644]
rstplib/rolesel.h [new file with mode: 0644]
rstplib/roletrns.c [new file with mode: 0644]
rstplib/roletrns.h [new file with mode: 0644]
rstplib/statmch.c [new file with mode: 0644]
rstplib/statmch.h [new file with mode: 0644]
rstplib/stp_bpdu.h [new file with mode: 0644]
rstplib/stp_cli.c [new file with mode: 0644]
rstplib/stp_cli.h [new file with mode: 0644]
rstplib/stp_in.c [new file with mode: 0644]
rstplib/stp_in.h [new file with mode: 0644]
rstplib/stp_state.h [new file with mode: 0644]
rstplib/stp_to.c [new file with mode: 0644]
rstplib/stp_to.h [new file with mode: 0644]
rstplib/stpm.c [new file with mode: 0644]
rstplib/stpm.h [new file with mode: 0644]
rstplib/stpmgmt.c [new file with mode: 0644]
rstplib/sttrans.c [new file with mode: 0644]
rstplib/sttrans.h [new file with mode: 0644]
rstplib/times.c [new file with mode: 0644]
rstplib/times.h [new file with mode: 0644]
rstplib/topoch.c [new file with mode: 0644]
rstplib/topoch.h [new file with mode: 0644]
rstplib/transmit.c [new file with mode: 0644]
rstplib/transmit.h [new file with mode: 0644]
rstplib/uid.h [new file with mode: 0644]
rstplib/uid_sock.c [new file with mode: 0644]
rstplib/uid_sock.h [new file with mode: 0644]
rstplib/uid_stp.h [new file with mode: 0644]
rstplib/vector.c [new file with mode: 0644]
rstplib/vector.h [new file with mode: 0644]

diff --git a/.depend b/.depend
new file mode 100644 (file)
index 0000000..f5374a5
--- /dev/null
+++ b/.depend
@@ -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 (file)
index 0000000..b30b401
--- /dev/null
@@ -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)
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..b7b5f53
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..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 .
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..7bf8024
--- /dev/null
+++ b/TODO
@@ -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 (file)
index 0000000..4eb8b57
--- /dev/null
@@ -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 (file)
index 0000000..14413e4
--- /dev/null
@@ -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
diff --git a/bridge b/bridge
new file mode 100755 (executable)
index 0000000..cf0d1d3
--- /dev/null
+++ b/bridge
@@ -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 (file)
index 0000000..fdb1970
--- /dev/null
@@ -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 (file)
index 0000000..f761840
--- /dev/null
@@ -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
diff --git a/brmon.c b/brmon.c
new file mode 100644 (file)
index 0000000..4afe5e1
--- /dev/null
+++ b/brmon.c
@@ -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 (file)
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 (file)
index 0000000..7b17964
--- /dev/null
@@ -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 (file)
index 0000000..cf15fe9
--- /dev/null
@@ -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 (file)
index 0000000..6e2b840
--- /dev/null
@@ -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 (file)
index 0000000..c41728a
--- /dev/null
@@ -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 (file)
index 0000000..5e7d96f
--- /dev/null
@@ -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 (file)
index 0000000..66be8d2
--- /dev/null
@@ -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 (file)
index 0000000..2bfc9ea
--- /dev/null
@@ -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 (file)
index 0000000..9b74172
--- /dev/null
@@ -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 (file)
index 0000000..230dacc
--- /dev/null
@@ -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 (file)
index 0000000..f9cfbee
--- /dev/null
@@ -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 (file)
index 0000000..63cc3c8
--- /dev/null
@@ -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 (file)
index 0000000..09f2e6d
--- /dev/null
@@ -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 (file)
index 0000000..5e33a20
--- /dev/null
@@ -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 (file)
index 0000000..151968b
--- /dev/null
@@ -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;
+}
diff --git a/log.h b/log.h
new file mode 100644 (file)
index 0000000..e61a569
--- /dev/null
+++ b/log.h
@@ -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
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..3ff556d
--- /dev/null
+++ b/main.c
@@ -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 (file)
index 0000000..a14e0c3
--- /dev/null
@@ -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 (file)
index 0000000..99c99d5
--- /dev/null
@@ -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 (file)
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 (file)
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>
diff --git a/rstpd.8 b/rstpd.8
new file mode 100644 (file)
index 0000000..f0906df
--- /dev/null
+++ b/rstpd.8
@@ -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 (file)
index 0000000..71dc74e
--- /dev/null
@@ -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 (file)
index 0000000..9c141f0
--- /dev/null
@@ -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 (file)
index 0000000..7a065bf
--- /dev/null
@@ -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 (file)
index 0000000..48f976b
--- /dev/null
@@ -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 (file)
index 0000000..223ede7
--- /dev/null
@@ -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.
+\f
+  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.
+\f
+                 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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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
+\f
+           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 (file)
index 0000000..987724d
--- /dev/null
@@ -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 (file)
index 0000000..322987e
--- /dev/null
@@ -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 (file)
index 0000000..1085207
--- /dev/null
@@ -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 (file)
index 0000000..c282756
--- /dev/null
@@ -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 (file)
index 0000000..5ac0d57
--- /dev/null
@@ -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 (file)
index 0000000..abcdb9e
--- /dev/null
@@ -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 (file)
index 0000000..f08ea7d
--- /dev/null
@@ -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 (file)
index 0000000..7037509
--- /dev/null
@@ -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 (file)
index 0000000..d963714
--- /dev/null
@@ -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 (file)
index 0000000..955e188
--- /dev/null
@@ -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 (file)
index 0000000..bcbda71
--- /dev/null
@@ -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 (file)
index 0000000..7f614fa
--- /dev/null
@@ -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 (file)
index 0000000..a8e844e
--- /dev/null
@@ -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, &reg);
+    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, &reg);
+}
+
+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, &reg);
+  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 (file)
index 0000000..75c19be
--- /dev/null
@@ -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 (file)
index 0000000..db183a4
--- /dev/null
@@ -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 (file)
index 0000000..e41264d
--- /dev/null
@@ -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 (file)
index 0000000..c11c017
--- /dev/null
@@ -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 (file)
index 0000000..0c38606
--- /dev/null
@@ -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 (file)
index 0000000..021dc54
--- /dev/null
@@ -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 (file)
index 0000000..96a581d
--- /dev/null
@@ -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 (file)
index 0000000..1603ba7
--- /dev/null
@@ -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 (file)
index 0000000..ea892ab
--- /dev/null
@@ -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 (file)
index 0000000..7d20427
--- /dev/null
@@ -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 (file)
index 0000000..cb19ade
--- /dev/null
@@ -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 (file)
index 0000000..4d4e377
--- /dev/null
@@ -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 (file)
index 0000000..3bd23c2
--- /dev/null
@@ -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 (file)
index 0000000..961eb39
--- /dev/null
@@ -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 (file)
index 0000000..ef1dceb
--- /dev/null
@@ -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 (file)
index 0000000..70b3a64
--- /dev/null
@@ -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 (file)
index 0000000..38dad55
--- /dev/null
@@ -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 (file)
index 0000000..fb292a1
--- /dev/null
@@ -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 (file)
index 0000000..6ba116d
--- /dev/null
@@ -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 (file)
index 0000000..fcd5c44
--- /dev/null
@@ -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 (file)
index 0000000..4b399c7
--- /dev/null
@@ -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 (file)
index 0000000..11fe7fa
--- /dev/null
@@ -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 (file)
index 0000000..459c8fe
--- /dev/null
@@ -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 (file)
index 0000000..56484dc
--- /dev/null
@@ -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 (file)
index 0000000..31a2c9f
--- /dev/null
@@ -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 (file)
index 0000000..b95b30e
--- /dev/null
@@ -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 (file)
index 0000000..3a7fc87
--- /dev/null
@@ -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 (file)
index 0000000..c5dc8e1
--- /dev/null
@@ -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 (file)
index 0000000..688a2fb
--- /dev/null
@@ -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 (file)
index 0000000..f025523
--- /dev/null
@@ -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 (file)
index 0000000..2f272f9
--- /dev/null
@@ -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 \91Port 0\92. This removes the
+ * need for any \91per Bridge\92 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 \91per Port operation\92
+ * 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 (file)
index 0000000..8b7090f
--- /dev/null
@@ -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 (file)
index 0000000..9174d76
--- /dev/null
@@ -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 (file)
index 0000000..da0b81e
--- /dev/null
@@ -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 (file)
index 0000000..66b5ad0
--- /dev/null
@@ -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 (file)
index 0000000..4bdc1e2
--- /dev/null
@@ -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 (file)
index 0000000..f17068e
--- /dev/null
@@ -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 (file)
index 0000000..8b7324c
--- /dev/null
@@ -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 (file)
index 0000000..9f0aeaf
--- /dev/null
@@ -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 (file)
index 0000000..620013c
--- /dev/null
@@ -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 (file)
index 0000000..a3acc23
--- /dev/null
@@ -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 (file)
index 0000000..d872a10
--- /dev/null
@@ -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 (file)
index 0000000..a3d0fd7
--- /dev/null
@@ -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 (file)
index 0000000..5cdbb44
--- /dev/null
@@ -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 (file)
index 0000000..00e4282
--- /dev/null
@@ -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 (file)
index 0000000..e5e71f8
--- /dev/null
@@ -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__ */
+
+