Menu

mstpd changes

2013-05-28
2013-07-17
1 2 3 > >> (Page 1 of 3)
  • Satish Ashok

    Satish Ashok - 2013-05-28

    Hi Vitalli,
    Thanks for the mstpd code, it looks good. We have started using the rstp/stp code and plan to use mstp in the future. We have identified some bugs, added support for some commands and have tested interoperability/conformance testing using IXIA ANVL. I have a list of changes below, please let me know which changes below you would like me to send patches for? I shall explain in detail about the fixes when I send out the patches.

    Bug fixes:

    1. When an operational UP root port is deleted from a bridge, port role selection for other ports is not done
    2. When bridge down is done, the ports in the bridge are not brought down(mentioned in the discussion forum)

    RSTP compliance IXIA ANVL fixes:

    1. Set hello time transmitted to be equal to configured bridge hello time
    2. The bridge and port identifier present in received BPDU is equal to the local port's values, discard the BPDU to identify possibly looped packets
    3. If unknown port role is received in RSTP version, treat the RSTP BPDU as a STP Config BPDU instead and set the role to be designated role

    Configuration commands:

    1. Add support for setageing in bridge(current ticket in sourceforge) and bridge hello time

    Additional functionality:
    I have also added some functionaility which is not part of 802.1Q but is present in other vendors implementation which we would like to add. If you would like these changes too, please let me know.

    1. BPDUGuard and Bridge assurance functionality

    Thanks,
    Satish

     
  • Vitalii Demianets

    Hello Satish!
    I'm glad to know that you've found mstpd useful, and I'm more than pleased to get feedback in the form of the patches. I'm happy to consider your changes, please don't hesitate to send them to me.

    I have a slight doubt about IXIA ANVL compliance fix #2 though (the one about dropping looped BPDUs). Why do you think is it necessary? I hadn't implemented loop check because:
    1) 802.1Q explicitly says that it is not necessary (see clause 14.4, NOTE 3 of the 802.1Q-2005);
    2) mstpd puts the port in the Backup/Discarding state after receiving looped BPDU, even when forced to the "stp" protocol.
    So, could you clarify why do you think this change is necessary? What bad behavior does it prevent?

    Looking forward to you patches,
    Vitalii

     
  • Satish Ashok

    Satish Ashok - 2013-05-28

    Configuration for bridge hello time patch

    Index: mstp.c
    ===================================================================
    --- mstp.c  (revision 37)
    +++ mstp.c  (working copy)
    @@ -117,7 +117,7 @@
         assign(tree->BridgeTimes.Max_Age, br->Max_Age);
         assign(tree->BridgeTimes.Message_Age, (__u8)0);
         /* 17.14 of 802.1D */
    -    assign(tree->BridgeTimes.Hello_Time, (__u8)2);
    +    assign(tree->BridgeTimes.Hello_Time, br->Hello_Time);
    
         /* 12.8.1.1.3.(b,c,d) */
         tree->time_since_topology_change = 0;
    @@ -205,6 +205,7 @@
         assign(br->Transmit_Hold_Count, 6u); /* 17.14 of 802.1D */
         assign(br->Migrate_Time, 3u); /* 17.14 of 802.1D */
         assign(br->Ageing_Time, 300u);/* 8.8.3 Table 8-3 */
    +    assign(br->Hello_Time, (__u8)2);
    
         br->uptime = 0;
    
    @@ -585,6 +586,7 @@
         assign(status->tx_hold_count, br->Transmit_Hold_Count);
         status->protocol_version = br->ForceProtocolVersion;
         status->enabled = br->bridgeEnabled;
    +    assign(status->bridge_hello_time, br->Hello_Time);
     }
    
     /* 12.8.1.2 Read MSTI Bridge Protocol Parameters */
    @@ -728,6 +730,17 @@
             }
         }
    
    +    if(cfg->set_bridge_hello_time)
    +    {
    +        if(cfg->bridge_hello_time != br->Hello_Time)
    +        {
    +            INFO_BRNAME(br, "bridge hello_time new-%d , old-%d",
    +                        cfg->bridge_hello_time, br->Hello_Time);
    +            assign(br->Hello_Time, cfg->bridge_hello_time);
    +            changed = changedBridgeTimes = true;
    +        }
    +    }
    +
         /* Thirdly, finalize changes */
         if(changedBridgeTimes)
         {
    @@ -736,6 +749,7 @@
                 assign(tree->BridgeTimes.remainingHops, br->MaxHops);
                 assign(tree->BridgeTimes.Forward_Delay, br->Forward_Delay);
                 assign(tree->BridgeTimes.Max_Age, br->Max_Age);
    +            assign(tree->BridgeTimes.Hello_Time, br->Hello_Time);
             /* Comment found in rstpd by Srinivas Aji:
              * Do this for any change in BridgeTimes.
              * Otherwise we fail UNH rstp.op_D test 3.2 since when administratively
    Index: ctl_main.c
    ===================================================================
    --- ctl_main.c  (revision 37)
    +++ ctl_main.c  (working copy)
    @@ -130,6 +130,7 @@
         PARAM_RESTRTCN,
         PARAM_PORTHELLOTIME,
         PARAM_DISPUTED,
    +    PARAM_BRHELLO,
     } param_id_t;
    
     typedef struct {
    @@ -155,6 +156,7 @@
         { PARAM_TOPCHNGTIME,  "time-since-topology-change" },
         { PARAM_TOPCHNGCNT,   "topology-change-count" },
         { PARAM_TOPCHNGSTATE, "topology-change" },
    +    { PARAM_BRHELLO,      "hello-time" },
     };
    
     static int do_showbridge(const char *br_name, param_id_t param_id)
    @@ -192,6 +194,7 @@
                 printf("bridge forward delay %hhu\n", s.bridge_forward_delay);
                 printf("  tx hold count %-10u ", s.tx_hold_count);
                 printf("max hops             %hhu\n", s.max_hops);
    +            printf("  hello time    %-10u ", s.bridge_hello_time);
                 printf("  force protocol version     %s\n",
                        PROTO_VERS_STR(s.protocol_version));
                 printf("  time since topology change %u\n",
    @@ -255,6 +258,9 @@
             case PARAM_TOPCHNGSTATE:
                 printf("%s\n", BOOL_STR(s.topology_change));
                 break;
    +        case PARAM_BRHELLO:
    +            printf("%u\n", s.bridge_hello_time);
    +            break;
             default:
                 return -2; /* -2 = unknown param */
         }
    @@ -925,6 +931,17 @@
         return set_bridge_cfg(bridge_max_age, max_age);
     }
    
    +static int cmd_setbridgehello(int argc, char *const *argv)
    +{
    +    int br_index = get_index(argv[1], "bridge");
    +    if(0 > br_index)
    +        return br_index;
    +    unsigned int hello_time = getuint(argv[2]);
    +    if(hello_time > 10)
    +        hello_time = 10;
    +    return set_bridge_cfg(bridge_hello_time, hello_time);
    +}
    +
     static int cmd_setbridgefdelay(int argc, char *const *argv)
     {
         int br_index = get_index(argv[1], "bridge");
    @@ -1468,6 +1485,8 @@
         {4, 0, "settreeportcost", cmd_settreeportcost,
          "<bridge> <port> <mstid> <cost>",
          "Set port internal path cost for the given MSTI (0 = auto)"},
    +    {2, 0, "sethello", cmd_setbridgehello,
    +     "<bridge> <hello_time>", "Set bridge hello time (1-10)"},
    
         /* Other */
         {1, 0, "debuglevel", cmd_debuglevel, "<level>", "Level of verbosity"},
    Index: mstp.h
    ===================================================================
    --- mstp.h  (revision 37)
    +++ mstp.h  (working copy)
    @@ -390,6 +390,7 @@
         __u8 MaxHops;             /* 13.22.o */
         __u8 Forward_Delay;       /* 13.22.f */
         __u8 Max_Age;             /* 13.22.i */
    +    __u8 Hello_Time;
         unsigned int Transmit_Hold_Count; /* 13.22.g */
         unsigned int Migrate_Time;        /* 13.22.h */
         unsigned int Ageing_Time;  /* 8.8.3 */
    @@ -588,6 +589,7 @@
         unsigned int internal_path_cost;
         bool enabled; /* not in standard */
         __u8 max_hops;
    +    __u8 bridge_hello_time;
     } CIST_BridgeStatus;
    
     void MSTP_IN_get_cist_bridge_status(bridge_t *br, CIST_BridgeStatus *status);
    @@ -627,6 +629,9 @@
    
         __u8 max_hops;
         bool set_max_hops;
    +
    +    __u8 bridge_hello_time;
    +    bool set_bridge_hello_time;
     } CIST_BridgeConfig;
    
     int MSTP_IN_set_cist_bridge_config(bridge_t *br, CIST_BridgeConfig *cfg);
    
     

    Last edit: Satish Ashok 2013-05-28
    • Vitalii Demianets

      Applied as revision [r38] with slight modifications (printf formatting, comments & range checking).
      Thanks, Satish!

       

      Related

      Commit: [r38]

  • Satish Ashok

    Satish Ashok - 2013-05-28

    Configuration for bridge set ageing patch

    Index: ctl_main.c
    ===================================================================
    --- ctl_main.c  (revision 37)
    +++ ctl_main.c  (working copy)
    @@ -130,6 +130,7 @@ typedef enum {
         PARAM_RESTRTCN,
         PARAM_PORTHELLOTIME,
         PARAM_DISPUTED,
    +    PARAM_BRAGEING,
     } param_id_t;
    
     typedef struct {
    @@ -155,6 +156,7 @@ static const cmd_param_t cist_bridge_params[] = {
         { PARAM_TOPCHNGTIME,  "time-since-topology-change" },
         { PARAM_TOPCHNGCNT,   "topology-change-count" },
         { PARAM_TOPCHNGSTATE, "topology-change" },
    +    { PARAM_BRAGEING,     "ageing-time" },
     };
    
     static int do_showbridge(const char *br_name, param_id_t param_id)
    @@ -192,6 +194,7 @@ static int do_showbridge(const char *br_name, para
                 printf("bridge forward delay %hhu\n", s.bridge_forward_delay);
                 printf("  tx hold count %-10u ", s.tx_hold_count);
                 printf("max hops             %hhu\n", s.max_hops);
    +            printf("ageing time          %u\n", s.Ageing_Time);
                 printf("  force protocol version     %s\n",
                        PROTO_VERS_STR(s.protocol_version));
                 printf("  time since topology change %u\n",
    @@ -255,6 +258,9 @@ static int do_showbridge(const char *br_name, para
             case PARAM_TOPCHNGSTATE:
                 printf("%s\n", BOOL_STR(s.topology_change));
                 break;
    +        case PARAM_BRAGEING:
    +            printf("%u\n", s.Ageing_Time);
    +            break;
             default:
                 return -2; /* -2 = unknown param */
         }
    @@ -965,6 +971,17 @@ static int cmd_setbridgetxholdcount(int argc, char
         return set_bridge_cfg(tx_hold_count, getuint(argv[2]));
     }
    
    +static int cmd_setbridgeageing(int argc, char *const *argv)
    +{
    +    int br_index = get_index(argv[1], "bridge");
    +    if(0 > br_index)
    +        return br_index;
    +    unsigned int ageing_time = getuint(argv[2]);
    +    if(ageing_time > 1000000)
    +        ageing_time = 1000000;
    +    return set_bridge_cfg(bridge_ageing_time, ageing_time);
    +}
    +
     static int cmd_settreeprio(int argc, char *const *argv)
     {
         int br_index = get_index(argv[1], "bridge");
    @@ -1468,6 +1485,8 @@ static const struct command commands[] =
         {4, 0, "settreeportcost", cmd_settreeportcost,
          "<bridge> <port> <mstid> <cost>",
          "Set port internal path cost for the given MSTI (0 = auto)"},
    +    {2, 0, "setageing", cmd_setbridgeageing,
    +     "<bridge> <ageing_time>", "Set bridge ageing time (10-1000000)"},
    
         /* Other */
         {1, 0, "debuglevel", cmd_debuglevel, "<level>", "Level of verbosity"},
    Index: mstp.h
    ===================================================================
    --- mstp.h  (revision 37)
    +++ mstp.h  (working copy)
    @@ -588,6 +588,7 @@ typedef struct
         unsigned int internal_path_cost;
         bool enabled; /* not in standard */
         __u8 max_hops;
    +    unsigned int Ageing_Time;
     } CIST_BridgeStatus;
    
     void MSTP_IN_get_cist_bridge_status(bridge_t *br, CIST_BridgeStatus *status);
    @@ -627,6 +628,9 @@ typedef struct
    
         __u8 max_hops;
         bool set_max_hops;
    +
    +    unsigned int bridge_ageing_time;
    +    bool set_bridge_ageing_time;
     } CIST_BridgeConfig;
    
     int MSTP_IN_set_cist_bridge_config(bridge_t *br, CIST_BridgeConfig *cfg);
    Index: mstp.c
    ===================================================================
    --- mstp.c  (revision 37)
    +++ mstp.c  (working copy)
    @@ -682,6 +682,16 @@ int MSTP_IN_set_cist_bridge_config(bridge_t *br, C
             }
         }
    
    +    if(cfg->set_bridge_ageing_time)
    +    {
    +        if((10 > cfg->bridge_ageing_time) || (1000000 < cfg->bridge_ageing_time))
    +        {
    +            ERROR_BRNAME(br,
    +                    "Bridge ageing time must be between 10 and 1000000 seconds");
    +            r = -1;
    +        }
    +    }
    +
         if(r)
             return r;
    
    @@ -728,6 +738,16 @@ int MSTP_IN_set_cist_bridge_config(bridge_t *br, C
             }
         }
    
    +    if(cfg->set_bridge_ageing_time)
    +    {
    +        if(cfg->bridge_ageing_time != br->Ageing_Time)
    +        {
    +            INFO_BRNAME(br, "bridge ageing_time new-%d , old-%d",
    +                        cfg->bridge_ageing_time, br->Ageing_Time);
    +            assign(br->Ageing_Time, cfg->bridge_ageing_time);
    +        }
    +    }
    +
         /* Thirdly, finalize changes */
         if(changedBridgeTimes)
         {
    
     

    Last edit: Satish Ashok 2013-05-29
    • Vitalii Demianets

      Applied as revision [r39] with slight modifications (printf formatting, comments & range checking). Also added missed copy of the current Ageing_Time into the status struct when reading current bridge status.

      Also, we need more work here. The Ageing_Time in the mstpd should be consistent with actual ageing time in the actual bridge device (Linux bridge and/or external bridge). I'm working on the patch and will send it for your revision soon.

       

      Related

      Commit: [r39]

      • Vitalii Demianets

        Satish, please review the following patch and tell me if it breaks something for you. The patch translates Ageing time set in the mstpd to the Linux bridge and (possible) external bridging device.
        It is based on revision [r39]

        Index: mstp.c
        ===================================================================
        --- mstp.c  (revision 39)
        +++ mstp.c  (working copy)
        @@ -278,6 +278,9 @@
              */
             list_add_tail(&prt->br_list, &br->ports);
        
        +    /* make ageing time consistent between mstpd and actual bridge device */
        +    MSTP_OUT_set_ageing_time(prt, br->Ageing_Time);
        +
             prt_state_machines_begin(prt);
             return true;
         }
        @@ -767,6 +770,14 @@
                     INFO_BRNAME(br, "bridge ageing_time new=%u, old=%u",
                                 cfg->bridge_ageing_time, br->Ageing_Time);
                     assign(br->Ageing_Time, cfg->bridge_ageing_time);
        +            /* make ageing time consistent between mstpd
        +             *  and actual bridge device
        +             */
        +            FOREACH_PORT_IN_BRIDGE(prt, br)
        +            {
        +                if(0 == prt->rapidAgeingWhile)
        +                    MSTP_OUT_set_ageing_time(prt, br->Ageing_Time);
        +            }
                 }
             }
        
         

        Related

        Commit: [r39]


        Last edit: Vitalii Demianets 2013-05-29
        • Satish Ashok

          Satish Ashok - 2013-05-30

          Hi Vitali,
          Couple of thoughts regarding this:
          1. Currently ageing_time in mstpd is really set in bridge driver only for STP version of the bridge, but with new changes, ageing_time will be set always, even for RSTP/MSTP version.
          2. If admin configures ageing time in bridge driver using brctl etc, and does not configure the ageing time in mstpctl, mstpctl will end up overriding the ageing time incorrectly.

          Thanks,
          Satish

           
          • Vitalii Demianets

            Ok, I think you are right and it is better to not invent additional path of changing bridge parameters. So, I am not applying my patch. Instead, I described the current situation on the new wiki page: [ImplementationFeatures]
            Thank you for sharing your consideration!

             

            Related

            Wiki: ImplementationFeatures

  • Satish Ashok

    Satish Ashok - 2013-05-28

    Issue:
    When an operational UP root port is deleted from a bridge, port role selection for other ports is not done. This is because, delete_if() deletes the port from the bridge list before invoking MSTP_IN_delete_port(). Also, since reselect is not set, port role selection state machine does not do role selection for other ports in the bridge.

    Bugfix:
    Set reselect to true

    Index: mstp.c
    ===================================================================
    --- mstp.c  (revision 37)
    +++ mstp.c  (working copy)
    @@ -290,6 +290,11 @@ void MSTP_IN_delete_port(port_t *prt)
         if(prt->portEnabled)
         {
             prt->portEnabled = false;
    +        FOREACH_PTP_IN_PORT(ptp, prt)
    +        {
    +            ptp->selected = false;
    +            ptp->reselect = true;
    +        }
             br_state_machines_run(br);
         }
    
     

    Last edit: Satish Ashok 2013-05-28
    • Vitalii Demianets

      I don't think your approach is safe. As you mentioned, the prt is already deleted from the list, so your patch sets the "reselect" property of the already deleted port. It may work now, but can easily be broken in future.

      I propose another solution: call MSTP_IN_delete_port() first, and only then (in the end of the function) delete port from the list. What would you say about the attached patch, does it fix the isuue?

      Index: bridge_track.c
      ===================================================================
      --- bridge_track.c  (revision 40)
      +++ bridge_track.c  (working copy)
      @@ -123,7 +123,6 @@
      
       static inline void delete_if(port_t * ifc)
       {
      -    list_del(&ifc->br_list);
           MSTP_IN_delete_port(ifc);
           free(ifc);
       }
      Index: main.c
      ===================================================================
      --- main.c  (revision 40)
      +++ main.c  (working copy)
      @@ -255,11 +255,9 @@
      
           printout_mesh(br);
      
      -    list_del(&prt[1]->br_list);
           MSTP_IN_delete_port(prt[1]);
           if(!MSTP_IN_delete_msti(br, 0xE12))
               goto error_exit;
      -    list_del(&prt[3]->br_list);
           MSTP_IN_delete_port(prt[3]);
           if(!MSTP_IN_delete_msti(br, 0x005))
               goto error_exit;
      Index: mstp.c
      ===================================================================
      --- mstp.c  (revision 40)
      +++ mstp.c  (working copy)
      @@ -300,6 +300,7 @@
               free(ptp);
           }
      
      +    list_del(&prt->br_list);
           br_state_machines_run(br);
       }
      
      @@ -319,7 +320,6 @@
      
           list_for_each_entry_safe(prt, nxt_prt, &br->ports, br_list)
           {
      -        list_del(&prt->br_list);
               MSTP_IN_delete_port(prt);
               free(prt);
           }
      
       
      • Satish Ashok

        Satish Ashok - 2013-05-30

        Hi Vitalii,
        I had explored this option and had tried this fix too, but this approach had a issue and the fix required adding another flag to indicate if port was already deleted.

        Since bridge driver deletes the port from the bridge and then sends netlink message to mstpd and since mstpd does not have a flag to indicate if port was deleted, there are a couple of harmless errors in the logs in this case, when mstpd tries to flush fdb and set port_state, as seen below..
        BTW, the functionality is correct with this fix, since port role selection goes through correctly. Due to this, I went with the above approach.

        May 30 05:09:54 mstpd: error, MSTP_OUT_set_state: br1:swp3 Couldn't set kernel bridge state blocking

        May 30 05:09:54 mstpd: Error in br_flush_port at bridge_track.c:431 verifying 0 <= fd. Couldn't open flush file /sys/class/net/swp3/brport/flush for write: No such file or directory
        May 30 05:09:54 mstpd: error, MSTP_OUT_flush_all_fids: br1:swp3 Couldn't flush kernel bridge forwarding database

        Thanks,
        Satish

         
        • Vitalii Demianets

          Hi Satish!

          I had explored this option and had tried this fix too, but this approach had a issue and the fix required adding another flag to indicate if port was already deleted.
          Since bridge driver deletes the port from the bridge and then sends netlink message to mstpd and since mstpd does not have a flag to indicate if port was deleted, there are a couple of harmless errors in the logs in this case, when mstpd tries to flush fdb and set port_state, as seen below.
          BTW, the functionality is correct with this fix, since port role selection goes through correctly. Due to this, I went with the above approach.

          Although it works now, I still consider your approach as dangerous one, as it relies on the fact that ptp nodes of the deleted port are still accessible (they are not traversed when doing FOREACH_PORT_IN_BRIDGE/FOREACH_PTP_IN_PORT but they still can be accessed via FOREACH_TREE_IN_BRIDGE/FOREACH_PTP_IN_TREE). But internal implementation of the state machines can change in the future, for example, the order of the ptp traversal can change from per-tree to the per-port, and then suddenly your approach stops working.

          So, as you mentioned, I'd rather prefer to improve my patch and implement additional flag ("deleted") which marks the port being deleted. Please, review the attached patch and tell me if it works for you.

          Thanks for the review!
          Vitalii

          Index: bridge_track.c
          ===================================================================
          --- bridge_track.c  (revision 40)
          +++ bridge_track.c  (working copy)
          @@ -123,7 +123,6 @@
          
           static inline void delete_if(port_t * ifc)
           {
          -    list_del(&ifc->br_list);
               MSTP_IN_delete_port(ifc);
               free(ifc);
           }
          Index: main.c
          ===================================================================
          --- main.c  (revision 40)
          +++ main.c  (working copy)
          @@ -255,11 +255,9 @@
          
               printout_mesh(br);
          
          -    list_del(&prt[1]->br_list);
               MSTP_IN_delete_port(prt[1]);
               if(!MSTP_IN_delete_msti(br, 0xE12))
                   goto error_exit;
          -    list_del(&prt[3]->br_list);
               MSTP_IN_delete_port(prt[3]);
               if(!MSTP_IN_delete_msti(br, 0x005))
                   goto error_exit;
          Index: mstp.c
          ===================================================================
          --- mstp.c  (revision 40)
          +++ mstp.c  (working copy)
          @@ -244,6 +244,7 @@
               prt->AdminEdgePort = false; /* 13.25 */
               prt->AutoEdge = true;       /* 13.25 */
               assign(prt->rapidAgeingWhile, 0u);
          +    prt->deleted = false;
          
               /* The following are initialized in BEGIN state:
                * - mdelayWhile. mcheck, sendRSTP: in Port Protocol Migration SM
          @@ -287,6 +288,7 @@
               per_tree_port_t *ptp, *nxt;
               bridge_t *br = prt->bridge;
          
          +    prt->deleted = true;
               if(prt->portEnabled)
               {
                   prt->portEnabled = false;
          @@ -300,6 +302,7 @@
                   free(ptp);
               }
          
          +    list_del(&prt->br_list);
               br_state_machines_run(br);
           }
          
          @@ -319,7 +322,6 @@
          
               list_for_each_entry_safe(prt, nxt_prt, &br->ports, br_list)
               {
          -        list_del(&prt->br_list);
                   MSTP_IN_delete_port(prt);
                   free(prt);
               }
          @@ -447,7 +449,10 @@
                   if(prt->rapidAgeingWhile)
                   {
                       if((--(prt->rapidAgeingWhile)) == 0)
          -                MSTP_OUT_set_ageing_time(prt, br->Ageing_Time);
          +            {
          +                if(!prt->deleted)
          +                    MSTP_OUT_set_ageing_time(prt, br->Ageing_Time);
          +            }
                   }
               }
          
          @@ -1921,7 +1926,7 @@
           {
               port_t *prt = ptp->port;
          
          -    if(prt->operEdge)
          +    if(prt->operEdge || prt->deleted)
               {
                   ptp->fdbFlush = false;
                   return;
          @@ -2109,6 +2114,9 @@
               bpdu_t b;
               per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
          
          +    if(prt->deleted)
          +        return;
          +
               b.protocolIdentifier = 0;
               b.protocolVersion = protoSTP;
               b.bpduType = bpduTypeConfig;
          @@ -2166,6 +2174,9 @@
               per_tree_port_t *ptp;
               msti_configuration_message_t *msti_msg;
          
          +    if(prt->deleted)
          +        return;
          +
               b.protocolIdentifier = 0;
               b.bpduType = bpduTypeRST;
               /* Standard says "{tcWhile, agree, proposing, role} ... for the Port".
          @@ -2261,6 +2272,9 @@
           {
               bpdu_t b;
          
          +    if(prt->deleted)
          +        return;
          +
               b.protocolIdentifier = 0;
               b.protocolVersion = protoSTP;
               b.bpduType = bpduTypeTCN;
          @@ -4287,7 +4301,10 @@
               disableForwarding();
               */
               if(BR_STATE_BLOCKING != ptp->state)
          -        MSTP_OUT_set_state(ptp, BR_STATE_BLOCKING);
          +    {
          +        if(!ptp->port->deleted)
          +            MSTP_OUT_set_state(ptp, BR_STATE_BLOCKING);
          +    }
               ptp->learning = false;
               ptp->forwarding = false;
          
          @@ -4301,7 +4318,10 @@
          
               /* enableLearning(); */
               if(BR_STATE_LEARNING != ptp->state)
          -        MSTP_OUT_set_state(ptp, BR_STATE_LEARNING);
          +    {
          +        if(!ptp->port->deleted)
          +            MSTP_OUT_set_state(ptp, BR_STATE_LEARNING);
          +    }
               ptp->learning = true;
          
               PSTSM_run(ptp, false /* actual run */);
          @@ -4313,7 +4333,10 @@
          
               /* enableForwarding(); */
               if(BR_STATE_FORWARDING != ptp->state)
          -        MSTP_OUT_set_state(ptp, BR_STATE_FORWARDING);
          +    {
          +        if(!ptp->port->deleted)
          +            MSTP_OUT_set_state(ptp, BR_STATE_FORWARDING);
          +    }
               ptp->forwarding = true;
          
               /* No need to run, no one condition will be met
          Index: mstp.h
          ===================================================================
          --- mstp.h  (revision 40)
          +++ mstp.h  (working copy)
          @@ -485,6 +485,8 @@
               bpdu_t rcvdBpduData;
               int rcvdBpduNumOfMstis;
          
          +    bool deleted;
          +
               sysdep_if_data_t sysdeps;
           } port_t;
          
           
          • Satish Ashok

            Satish Ashok - 2013-06-03

            Hi Vitalli,
            I verified that this patch works without any issues. Looks good..

            Thanks!!
            Satish

             
            • Vitalii Demianets

              Applied as revision [r41].
              Satish, thank you for reporting the issue, reviewing and testing!

               

              Related

              Commit: [r41]

  • Satish Ashok

    Satish Ashok - 2013-05-28

    Issue:
    When "ifconfig br1 down" is done, the bridge ports are not brought down in mstpd and also the stp state needs to be set to disabled in this case.

    Fix:
    Add a check to see if bridge is not enabled when port is not enabled checks are present in the FSM

    I have added a patch file as attachment too..

    Index: mstp.c
    ===================================================================
    --- mstp.c  (revision 37)
    +++ mstp.c  (working copy)
    @@ -49,7 +49,7 @@ static void BDSM_begin(port_t *prt);
     static void br_state_machines_begin(bridge_t *br);
     static void prt_state_machines_begin(port_t *prt);
     static void tree_state_machines_begin(tree_t *tree);
    -static void br_state_machines_run(bridge_t *br);
    +static void br_state_machines_run(bridge_t *br, bool abort_if_br_down);
    
     #define FOREACH_PORT_IN_BRIDGE(port, bridge) \
         list_for_each_entry((port), &(bridge)->ports, br_list)
    @@ -290,7 +290,7 @@ void MSTP_IN_delete_port(port_t *prt)
         if(prt->portEnabled)
         {
             prt->portEnabled = false;
    -        br_state_machines_run(br);
    +        br_state_machines_run(br, true);
         }
    
         list_for_each_entry_safe(ptp, nxt, &prt->trees, port_list)
    @@ -300,7 +300,7 @@ void MSTP_IN_delete_port(port_t *prt)
             free(ptp);
         }
    
    -    br_state_machines_run(br);
    +    br_state_machines_run(br, true);
     }
    
     void MSTP_IN_delete_bridge(bridge_t *br)
    @@ -355,7 +355,10 @@ void MSTP_IN_set_bridge_enable(bridge_t *br, bool
         if(br->bridgeEnabled == up)
             return;
         br->bridgeEnabled = up;
    -    br_state_machines_begin(br);
    +    if (!br->bridgeEnabled)
    +        br_state_machines_run(br, false);
    +    else
    +        br_state_machines_begin(br);
     }
    
     void MSTP_IN_set_port_enable(port_t *prt, bool up, int speed, int duplex)
    @@ -423,7 +426,7 @@ void MSTP_IN_set_port_enable(port_t *prt, bool up,
         }
    
         if(changed)
    -        br_state_machines_run(prt->bridge);
    +        br_state_machines_run(prt->bridge, true);
     }
    
     void MSTP_IN_one_second(bridge_t *br)
    @@ -451,7 +454,7 @@ void MSTP_IN_one_second(bridge_t *br)
             }
         }
    
    -    br_state_machines_run(br);
    +    br_state_machines_run(br, true);
     }
    
     void MSTP_IN_all_fids_flushed(per_tree_port_t *ptp)
    @@ -463,7 +466,7 @@ void MSTP_IN_all_fids_flushed(per_tree_port_t *ptp
         if(!ptp->calledFromFlushRoutine)
         {
             TCSM_run(ptp, false /* actual run */);
    -        br_state_machines_run(br);
    +        br_state_machines_run(br, true);
         }
     }
    
    @@ -558,7 +561,7 @@ bpdu_validation_failed:
         assign(prt->rcvdBpduData, *bpdu);
         prt->rcvdBpdu = true;
    
    -    br_state_machines_run(br);
    +    br_state_machines_run(br, true);
     }
    
     /* 12.8.1.1 Read CIST Bridge Protocol Parameters */
    @@ -755,7 +758,7 @@ int MSTP_IN_set_cist_bridge_config(bridge_t *br, C
             if(init)
                 br_state_machines_begin(br);
             else
    -            br_state_machines_run(br);
    +            br_state_machines_run(br, true);
         }
    
         return 0;
    @@ -951,7 +954,7 @@ int MSTP_IN_set_cist_port_config(port_t *prt, CIST
         }
    
         if(changed && prt->portEnabled)
    -        br_state_machines_run(prt->bridge);
    +        br_state_machines_run(prt->bridge, true);
    
         return 0;
     }
    @@ -1000,7 +1003,7 @@ int MSTP_IN_set_msti_port_config(per_tree_port_t *
             ptp->selected = false;
             ptp->reselect = true;
    
    -        br_state_machines_run(br);
    +        br_state_machines_run(br, true);
         }
    
         return 0;
    @@ -1014,7 +1017,7 @@ int MSTP_IN_port_mcheck(port_t *prt)
         if(rstpVersion(br) && prt->portEnabled && br->bridgeEnabled)
         {
             prt->mcheck = true;
    -        br_state_machines_run(br);
    +        br_state_machines_run(br, true);
         }
    
         return 0;
    @@ -1325,7 +1328,7 @@ bool MSTP_IN_delete_msti(bridge_t *br, __u16 mstid
          *  did not change. So, no need in RecalcConfigDigest.
          * Give state machine a spare run, just for the case...
          */
    -    br_state_machines_run(br);
    +    br_state_machines_run(br, true);
         return true;
     }
    
    @@ -2574,7 +2577,7 @@ static bool PRSM_run(port_t *prt, bool dry_run)
         bool rcvdAnyMsg;
    
         if((prt->rcvdBpdu || (prt->edgeDelayWhile != prt->bridge->Migrate_Time))
    -       && !prt->portEnabled)
    +       && ((!prt->portEnabled) || (!prt->bridge->bridgeEnabled)))
         {
             return PRSM_to_DISCARD(prt, dry_run);
         }
    @@ -2582,7 +2585,8 @@ static bool PRSM_run(port_t *prt, bool dry_run)
         switch(prt->PRSM_state)
         {
             case PRSM_DISCARD:
    -            if(prt->rcvdBpdu && prt->portEnabled)
    +            if(prt->rcvdBpdu && prt->portEnabled &&
    +                prt->bridge->bridgeEnabled)
                 {
                     if(dry_run) /* state change */
                         return true;
    @@ -2599,7 +2603,8 @@ static bool PRSM_run(port_t *prt, bool dry_run)
                         break;
                     }
                 }
    -            if(prt->rcvdBpdu && prt->portEnabled && !rcvdAnyMsg)
    +            if(prt->rcvdBpdu && prt->portEnabled &&
    +                prt->bridge->bridgeEnabled && !rcvdAnyMsg)
                 {
                     if(dry_run) /* at least rcvdBpdu will change */
                         return true;
    @@ -2656,7 +2661,7 @@ static bool PPMSM_run(port_t *prt, bool dry_run)
         {
             case PPMSM_CHECKING_RSTP:
                 if((prt->mdelayWhile != br->Migrate_Time)
    -               && !prt->portEnabled)
    +               && (!prt->portEnabled || !prt->bridge->bridgeEnabled))
                 {
                     if(dry_run) /* at least mdelayWhile will change */
                         return true;
    @@ -2671,7 +2676,8 @@ static bool PPMSM_run(port_t *prt, bool dry_run)
                 }
                 return false;
             case PPMSM_SELECTING_STP:
    -            if(0 == prt->mdelayWhile || !prt->portEnabled || prt->mcheck)
    +            if(0 == prt->mdelayWhile || !prt->portEnabled ||
    +                !prt->bridge->bridgeEnabled || prt->mcheck)
                 {
                     if(dry_run) /* state change */
                         return true;
    @@ -2679,7 +2685,7 @@ static bool PPMSM_run(port_t *prt, bool dry_run)
                 }
                 return false;
             case PPMSM_SENSING:
    -            if(!prt->portEnabled || prt->mcheck
    +            if((!prt->portEnabled || !prt->bridge->bridgeEnabled) || prt->mcheck
                    || (rstpVersion(br) && !prt->sendRSTP && prt->rcvdRSTP))
                 {
                     if(dry_run) /* state change */
    @@ -2737,7 +2743,8 @@ static bool BDSM_run(port_t *prt, bool dry_run)
         switch(prt->BDSM_state)
         {
             case BDSM_EDGE:
    -            if((!prt->portEnabled && !prt->AdminEdgePort) || !prt->operEdge)
    +            if((((!prt->portEnabled) || (!prt->bridge->bridgeEnabled)) &&
    +                !prt->AdminEdgePort) || !prt->operEdge)
                 {
                     if(dry_run) /* state change */
                         return true;
    @@ -2753,7 +2760,8 @@ static bool BDSM_run(port_t *prt, bool dry_run)
                  * So, I decide that it will be the "proposing" flag
                  *  from CIST tree - it seems like a good bet.
                  */
    -            if((!prt->portEnabled && prt->AdminEdgePort)
    +            if((((!prt->portEnabled) || (!prt->bridge->bridgeEnabled)) &&
    +                                                prt->AdminEdgePort)
                    || ((0 == prt->edgeDelayWhile) && prt->AutoEdge && prt->sendRSTP
                        && cist->proposing)
                   )
    @@ -2786,7 +2794,8 @@ static bool PTSM_to_TRANSMIT_INIT(port_t *prt, boo
         prt->newInfoMsti = true;
         assign(prt->txCount, 0u);
    
    -    if(!begin && prt->portEnabled) /* prevent infinite loop */
    +    if(!begin && prt->portEnabled && prt->bridge->bridgeEnabled)
    +        /* prevent infinite loop */
             PTSM_run(prt, false /* actual run */);
         return false;
     }
    @@ -2873,7 +2882,7 @@ static bool PTSM_run(port_t *prt, bool dry_run)
         port_role_t cistRole;
         bool mstiMasterPort;
    
    -    if(!prt->portEnabled)
    +    if((!prt->portEnabled) || (!prt->bridge->bridgeEnabled))
         {
             return PTSM_to_TRANSMIT_INIT(prt, false, dry_run);
         }
    @@ -3120,7 +3129,8 @@ static bool PISM_run(per_tree_port_t *ptp, bool dr
         bool rcvdXstMsg, updtXstInfo;
         port_t *prt = ptp->port;
    
    -    if((!prt->portEnabled) && (ioDisabled != ptp->infoIs))
    +    if(((!prt->portEnabled) || (!prt->bridge->bridgeEnabled)) &&
    +            (ioDisabled != ptp->infoIs))
         {
             if(dry_run) /* at least infoIs will change */
                 return true;
    @@ -3131,7 +3141,7 @@ static bool PISM_run(per_tree_port_t *ptp, bool dr
         switch(ptp->PISM_state)
         {
             case PISM_DISABLED:
    -            if(prt->portEnabled)
    +            if(prt->portEnabled && prt->bridge->bridgeEnabled)
                 {
                     if(dry_run) /* state change */
                         return true;
    @@ -4224,8 +4234,12 @@ static void PSTSM_to_DISCARDING(per_tree_port_t *p
         disableLearning();
         disableForwarding();
         */
    -    if(BR_STATE_BLOCKING != ptp->state)
    -        MSTP_OUT_set_state(ptp, BR_STATE_BLOCKING);
    +    if(BR_STATE_BLOCKING != ptp->state) {
    +        if (!ptp->port->bridge->bridgeEnabled)
    +            MSTP_OUT_set_state(ptp, BR_STATE_DISABLED);
    +        else
    +            MSTP_OUT_set_state(ptp, BR_STATE_BLOCKING);
    +    }
         ptp->learning = false;
         ptp->forwarding = false;
    
    @@ -4547,7 +4561,7 @@ static void tree_state_machines_begin(tree_t *tree
         FOREACH_PTP_IN_TREE(ptp, tree)
             TCSM_begin(ptp);
    
    -    br_state_machines_run(br);
    +    br_state_machines_run(br, true);
     }
    
     static void prt_state_machines_begin(port_t *prt)
    @@ -4589,7 +4603,7 @@ static void prt_state_machines_begin(port_t *prt)
         FOREACH_PTP_IN_PORT(ptp, prt)
             TCSM_begin(ptp);
    
    -    br_state_machines_run(br);
    +    br_state_machines_run(br, true);
     }
    
     static void br_state_machines_begin(bridge_t *br)
    @@ -4647,7 +4661,7 @@ static void br_state_machines_begin(bridge_t *br)
                 TCSM_begin(ptp);
         }
    
    -    br_state_machines_run(br);
    +    br_state_machines_run(br, true);
     }
    
     /* Run each state machine.
    @@ -4736,12 +4750,12 @@ static bool __br_state_machines_run(bridge_t *br,
     /* Run state machines until their state stabilizes.
      * Do not consume more than 1 second.
      */
    -static void br_state_machines_run(bridge_t *br)
    +static void br_state_machines_run(bridge_t *br, bool abort_if_br_down)
     {
         struct timeval tv, tv_end;
         signed long delta;
    
    -    if(!br->bridgeEnabled)
    +    if((abort_if_br_down == true) && !br->bridgeEnabled)
             return;
    
         gettimeofday(&tv_end, NULL);
    
     
    • Vitalii Demianets

      Hello Satish!

      Issue:
      When "ifconfig br1 down" is done, the bridge ports are not brought down in mstpd and also the stp state needs to be set to disabled in this case.
      Fix:
      Add a check to see if bridge is not enabled when port is not enabled checks are present in the FSM

      I am not sure I understand what issue you are trying to resolve here. If bridge is down, bridgeEnabled is false, thus state machines are not run and mstpd does not send any BPDUs. So, please, could you explain in more details what exact issue do you have:
      1) what is the sequence of commands;
      2) what is the expected result;
      3) what is the observed result, and why it is not satisfactory.

      I guess, you want all ports to be in BR_STATE_DISABLED state when the bridge is down, don't you? Then we can achieve this goal immediately by directly setting port->portEnabled to false and calling MSTP_OUT_set_state(ptp, BR_STATE_DISABLED) in MSTP_IN_set_bridge_enable().
      There is no BR_STATE_DISABLED in the state machines because 802.1Q does not define Disabled state for the MSTP - it is something external to the MSTP, the protocol knows only Blocking, Learning/Listening and Forwarding.

      Also, I guess, setting portEnabled to false and calling MSTP_OUT_set_state(ptp, BR_STATE_DISABLED) isn't enough. Perhaps more cleaning should be done here, like setting all the internal states to the default values (see MSTP_IN_port_create_and_add_tail() and create_ptp()).

      So, again, which problem are you facing? What erroneous behavior are you trying to improve?

       
      • Satish Ashok

        Satish Ashok - 2013-06-03

        Hi Vitali,
        Should have explained in detail earlier.
        Issue:
        The issue is that when bridge is brought down"ifconfig br1 down", since mstpd does not change the port state to disabled, the stp port state of the port is as was before bridge was down -i.e. - forwarding/blocking etc. Locally, mstpd will not send BPDU's, but the peer is sending BPDU's and MSTPD receives BPDU's every 2seconds and there is the below log every 2seconds. The only way MSTP will stop receiving BPDU's is when the port is in disabled state.

        mstpd Received BPDU while bridge is disabled

        Fix:
        The fix was was to take action in mstpd when bridge is disabled and ensure that the ports are disabled in this case. I did not change the portEnabled to False instead, since that might make mstpd out of sync with kernel port state(also it might not be clear for admin in this case). It just seemed cleaner to look at bridge disabled state instead.

        Thanks,
        Satish

         
        • Vitalii Demianets

          Hello Satish!
          Ok, I see your intention: you want to put ports in disabled state when bridge is disabled. But there is more here, we should ensure that state machines start in consistent state when bridge is enabled again, so more clean-up must be done here. Also, putting port in disabled state is not in the 802.1Q FSM description, so the best place for this is outside the state machines; after all, it is logical to put it in clear and straightforward manner into the MSTP_IN_set_bridge_enable().

          That said, here is the proposed patch. I believe it suites your needs (i.e. puts ports into the disabled state) and does all necessary clean up. Please tell me if it works for you.

          Index: mstp.c
          ===================================================================
          --- mstp.c  (revision 41)
          +++ mstp.c  (working copy)
          @@ -92,6 +92,64 @@
               return MAX_PATH_COST;
           }
          
          +static void bridge_default_internal_vars(bridge_t *br)
          +{
          +    br->uptime = 0;
          +}
          +
          +static void tree_default_internal_vars(tree_t *tree)
          +{
          +    /* 12.8.1.1.3.(b,c,d) */
          +    tree->time_since_topology_change = 0;
          +    tree->topology_change_count = 0;
          +    tree->topology_change = false; /* since all tcWhile are initialized to 0 */
          +
          +    /* The following are initialized in BEGIN state:
          +     *  - rootPortId, rootPriority, rootTimes: in Port Role Selection SM
          +     */
          +}
          +
          +static void port_default_internal_vars(port_t *prt)
          +{
          +    prt->infoInternal = false;
          +    prt->rcvdInternal = false;
          +    prt->rcvdTcAck = false;
          +    prt->rcvdTcn = false;
          +    assign(prt->rapidAgeingWhile, 0u);
          +
          +    /* The following are initialized in BEGIN state:
          +     * - mdelayWhile. mcheck, sendRSTP: in Port Protocol Migration SM
          +     * - helloWhen, newInfo, newInfoMsti, txCount: in Port Transmit SM
          +     * - edgeDelayWhile, rcvdBpdu, rcvdRSTP, rcvdSTP : in Port Receive SM
          +     * - operEdge: in Bridge Detection SM
          +     * - tcAck: in Topology Change SM
          +     */
          +}
          +
          +static void ptp_default_internal_vars(per_tree_port_t *ptp)
          +{
          +    ptp->rcvdTc = false;
          +    ptp->tcProp = false;
          +    ptp->updtInfo = false;
          +    ptp->master = false; /* 13.24.5 */
          +    ptp->disputed = false;
          +    assign(ptp->rcvdInfo, (port_info_t)0);
          +    ptp->mastered = false;
          +    memset(&ptp->msgPriority, 0, sizeof(ptp->msgPriority));
          +    memset(&ptp->msgTimes, 0, sizeof(ptp->msgTimes));
          +
          +    /* The following are initialized in BEGIN state:
          +     * - rcvdMsg: in Port Receive SM
          +     * - fdWhile, rrWhile, rbWhile, role, learn, forward,
          +     *   sync, synced, reRoot: in Port Role Transitions SM
          +     * - tcWhile, fdbFlush: Topology Change SM
          +     * - rcvdInfoWhile, proposed, proposing, agree, agreed,
          +     *   infoIs, reselect, selected: Port Information SM
          +     * - forwarding, learning: Port State Transition SM
          +     * - selectedRole, designatedPriority, designatedTimes: Port Role Selection SM
          +     */
          +}
          +
           static tree_t * create_tree(bridge_t *br, __u8 *macaddr, __be16 MSTID)
           {
               /* Initialize all fields except anchor */
          @@ -118,14 +176,8 @@
               assign(tree->BridgeTimes.Message_Age, (__u8)0);
               assign(tree->BridgeTimes.Hello_Time, br->Hello_Time);
          
          -    /* 12.8.1.1.3.(b,c,d) */
          -    tree->time_since_topology_change = 0;
          -    tree->topology_change_count = 0;
          -    tree->topology_change = false; /* since all tcWhile are initialized to 0 */
          +    tree_default_internal_vars(tree);
          
          -    /* The following are initialized in BEGIN state:
          -     *  - rootPortId, rootPriority, rootTimes: in Port Role Selection SM
          -     */
               return tree;
           }
          
          @@ -143,12 +195,8 @@
               ptp->MSTID = tree->MSTID;
          
               ptp->state = BR_STATE_DISABLED;
          -    ptp->rcvdTc = false;
          -    ptp->tcProp = false;
          -    ptp->updtInfo = false;
               /* 0x80 = default port priority (17.14 of 802.1D) */
               ptp->portId = __constant_cpu_to_be16(0x8000) | prt->port_number;
          -    ptp->master = false; /* 13.24.5 */
               assign(ptp->AdminInternalPortPathCost, 0u);
               assign(ptp->InternalPortPathCost, compute_pcost(GET_PORT_SPEED(prt)));
               /* 802.1Q leaves portPriority and portTimes uninitialized */
          @@ -157,24 +205,8 @@
          
               ptp->calledFromFlushRoutine = false;
          
          -    /* The following are initialized in BEGIN state:
          -     * - rcvdMsg: in Port Receive SM
          -     * - fdWhile, rrWhile, rbWhile, role, learn, forward,
          -     *   sync, synced, reRoot: in Port Role Transitions SM
          -     * - tcWhile, fdbFlush: Topology Change SM
          -     * - rcvdInfoWhile, proposed, proposing, agree, agreed,
          -     *   infoIs, reselect, selected: Port Information SM
          -     * - forwarding, learning: Port State Transition SM
          -     * - selectedRole, designatedPriority, designatedTimes: Port Role Selection SM
          -     */
          +    ptp_default_internal_vars(ptp);
          
          -    /* The following are not initialized (set to zero thanks to calloc):
          -     * - disputed
          -     * - rcvdInfo
          -     * - mastered
          -     * - msgPriority
          -     * - msgTimes
          -     */
               return ptp;
           }
          
          @@ -206,7 +238,7 @@
               assign(br->Ageing_Time, 300u);/* 8.8.3 Table 8-3 */
               assign(br->Hello_Time, (__u8)2);     /* 17.14 of 802.1D */
          
          -    br->uptime = 0;
          +    bridge_default_internal_vars(br);
          
               /* Create CIST */
               if(!(cist = create_tree(br, macaddr, 0)))
          @@ -234,25 +266,14 @@
               prt->AdminP2P = p2pAuto;
               prt->operPointToPointMAC = false;
               prt->portEnabled = false;
          -    prt->infoInternal = false;
          -    prt->rcvdInternal = false;
          -    prt->rcvdTcAck = false;
          -    prt->rcvdTcn = false;
               prt->restrictedRole = false; /* 13.25.14 */
               prt->restrictedTcn = false; /* 13.25.15 */
               assign(prt->ExternalPortPathCost, MAX_PATH_COST); /* 13.37.1 */
               prt->AdminEdgePort = false; /* 13.25 */
               prt->AutoEdge = true;       /* 13.25 */
          -    assign(prt->rapidAgeingWhile, 0u);
               prt->deleted = false;
          
          -    /* The following are initialized in BEGIN state:
          -     * - mdelayWhile. mcheck, sendRSTP: in Port Protocol Migration SM
          -     * - helloWhen, newInfo, newInfoMsti, txCount: in Port Transmit SM
          -     * - edgeDelayWhile, rcvdBpdu, rcvdRSTP, rcvdSTP : in Port Receive SM
          -     * - operEdge: in Bridge Detection SM
          -     * - tcAck: in Topology Change SM
          -     */
          +    port_default_internal_vars(prt);
          
               /* Create PerTreePort structures for all existing trees */
               FOREACH_TREE_IN_BRIDGE(tree, br)
          @@ -354,9 +375,41 @@
          
           void MSTP_IN_set_bridge_enable(bridge_t *br, bool up)
           {
          +    port_t *prt;
          +    per_tree_port_t *ptp;
          +    tree_t *tree;
          +
               if(br->bridgeEnabled == up)
                   return;
               br->bridgeEnabled = up;
          +
          +    /* Reset all internal states and variables,
          +     * except those which are user-configurable */
          +    bridge_default_internal_vars(br);
          +    FOREACH_TREE_IN_BRIDGE(tree, br)
          +    {
          +        tree_default_internal_vars(tree);
          +    }
          +    FOREACH_PORT_IN_BRIDGE(prt, br)
          +    {
          +        /* NOTE: Don't check prt->deleted here, as it is imposible condition */
          +        /* NOTE: In the port_default_internal_vars() rapidAgeingWhile will be
          +         *  reset, so we should stop rapid ageing procedure here.
          +         */
          +        if(prt->rapidAgeingWhile)
          +        {
          +            MSTP_OUT_set_ageing_time(prt, br->Ageing_Time);
          +        }
          +        port_default_internal_vars(prt);
          +        FOREACH_PTP_IN_PORT(ptp, prt)
          +        {
          +            if(BR_STATE_DISABLED != ptp->state)
          +            {
          +                MSTP_OUT_set_state(ptp, BR_STATE_DISABLED);
          +            }
          +            ptp_default_internal_vars(ptp);
          +        }
          +    }
               br_state_machines_begin(br);
           }
          
           
          • Satish Ashok

            Satish Ashok - 2013-06-19

            Hi Vitali,
            I verified that the bridge down patch which you have above works fine..

            Thanks,
            Satish

             
            • Vitalii Demianets

              Applied as revision [r48]. Thanks a lot!

               

              Related

              Commit: [r48]

  • Satish Ashok

    Satish Ashok - 2013-05-28

    Ixia RSTP ANVL Failure:

    RSTP-1.2
    Tue Apr 30 13:28:03 2013: TEST_DESCRIPTION
    Quick test to verify that DUT transmits RST BPDU from each port.
    The Unknown value of Port Role cannot be generated by a valid
    implementation; however, this value is accepted on receipt.
    (NOTE: The DUT will treat this RST-BPDU as a Configuration BPDU)
    (Test to verify that the incoming RST BPDU with Unknown Port role
    plays a role in the calculation of Active Spanning Tree)

    Tue Apr 30 13:28:03 2013: TEST_REFERENCE
    NEGATIVE
    IEEE Std 802.1D-2004 S9.2.9 Page 61

    Verification:

    DUT: Treat the received RST BPDU as CONFIG BPDU
    DUT: Select <bestBridgeID> B1 as Root Bridge
    ANVL: Listen (for upto 3 * <configBridgeHelloTime> seconds) on
    <DIface-1>
    DUT: Send RST BPDU
    Root Identifier field set to <bestBridgeID> B1
    

    Failure:
    ! Received RST BPDU doesn't contain expected
    ! Root Indentifier : 0 / 02:02:00:00:00:01Tue Apr 30 13:28:13 2013:

    Index: mstp.c
    ===================================================================
    --- mstp.c  (revision 37)
    +++ mstp.c  (working copy)
    @@ -1588,6 +1588,25 @@ static port_info_t rcvInfo(per_tree_port_t *ptp)
                     case encodedRoleDesignated:
                         roleIsDesignated = true;
                         break;
    +                case encodedRoleMaster:
    +                    /* 802.1D-2004 S9.2.9 P61. The Unknown value of Port Role
    +                     * cannot be generated by a valid implementation; however,
    +                     * this value is accepted on receipt. roleMaster in MSTP is
    +                     * roleUnknown in RSTP.
    +                     * NOTE.If the Unknown value of the Port Role parameter is
    +                     * received, the state machines will effectively treat the RST
    +                     * BPDU as if it were a Configuration BPDU
    +                     */
    +                    if (protoRSTP == b->protocolVersion)
    +                    {
    +                        roleIsDesignated = true;
    +                        break;
    +                    }
    +                    else
    +                    {
    +                        return OtherInfo;
    +                    }
    +                    break;
                     default:
                         return OtherInfo;
                 }
    
     
    • Vitalii Demianets

      Applied as revision [r40]. Thanks for fixing that!

       

      Related

      Commit: [r40]

  • Satish Ashok

    Satish Ashok - 2013-05-28

    Issue:
    The bridge and port identifier present in received BPDU is equal to the local port's values, discard the BPDU to identify possibly looped packets

    Vitali, this is the loopback issue, for which you had a couple of questions. This is a negative testcase and this is specified in 802.1D as mentioned below. Just felt that processing every loopbacked packet would not be good and also it would be better to syslog this as an error, so that admin knows of the issue and takes immediate action.

    Regarding clause 14.4, NOTE 3 of the 802.1Q-2005, not sure if it means that this validation was not specified in 802.1Q because it was already specified in 802.1D.

    Sure, I think this is open to discussion..

    Testcase 3.3 failure for RSTP suite

    RSTP-3.3
    Tue Apr 30 13:31:27 2013: TEST_DESCRIPTION
    A Spanning Tree Protocol Entity shall process a received BPDU
    if and only if the BPDU Type denotes a CONFIG BPDU and ..., and the
    Bridge Identifier and Port Identifier parameters from the
    received BPDU do not match the values that would be transmitted
    in a BPDU from this Port

    Tue Apr 30 13:31:27 2013: TEST_REFERENCE
    NEGATIVE
    IEEE Std 802.1D-2004 S9.3.4 P63 Validation of received BPDUs

    From 802.1D SPEC

    If the Bridge Identifier and Port Identifier both match the values that would be transmitted in a Configuration
    BPDU, the BPDU is discarded to prevent processing of the Port’s own BPDUs; for example, if they are received by the
    Port as a result of a loopback condition.

    Failure:

    ! Did not receive expected RST BPDU from
    ! DUT Interface: 1

    Index: mstp.c
    ===================================================================
    --- mstp.c  (revision 37)
    +++ mstp.c  (working copy)
    @@ -474,6 +474,7 @@ void MSTP_IN_rx_bpdu(port_t *prt, bpdu_t *bpdu, in
     {
         int mstis_size;
         bridge_t *br = prt->bridge;
    +    per_tree_port_t *cist = GET_CIST_PTP_FROM_PORT(prt);
    
         if(!br->bridgeEnabled)
         {
    @@ -487,6 +488,21 @@ void MSTP_IN_rx_bpdu(port_t *prt, bpdu_t *bpdu, in
             return;
         }
    
    +    /* If the Bridge Identifier and Port Identifier both match the values that
    +     * would be transmitted in a Configuration BPDU, the BPDU is discarded to
    +     * prevent processing of the Port's own BPDUs; for example, if they are
    +     * received by the Port as a result of a loopback condition.
    +     * 802.1D Spec D9.3.4 Page63
    +     */
    +    if ((0 == _ncmp(cist->designatedPriority.RRootID, bpdu->cistRRootID))
    +            && (0 == _ncmp(cist->designatedPriority.DesignatedPortID,
    +            bpdu->cistPortID))) {
    +        ERROR_PRTNAME(br, prt, "rcvd local port_id - "PRT_ID_FMT", "\
    +                "bridge_id "BR_ID_FMT"", PRT_ID_ARGS(bpdu->cistPortID),
    +                BR_ID_ARGS(bpdu->cistRRootID));
    +        return;
    +    }
    +
         /* 14.4 Validation */
         if((TCN_BPDU_SIZE > size) || (0 != bpdu->protocolIdentifier))
         {
    Index: ctl_main.c
    ===================================================================
    --- ctl_main.c  (revision 37)
    +++ ctl_main.c  (working copy)
    @@ -71,18 +71,6 @@ static inline int get_id(const char *str, const ch
         return id;
     }
    
    -#define GET_NUM_FROM_PRIO(p) (__be16_to_cpu(p) & 0x0FFF)
    -
    -#define BR_ID_FMT "%01hhX.%03hX.%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX"
    -#define BR_ID_ARGS(x) ((GET_PRIORITY_FROM_IDENTIFIER(x) >> 4) & 0x0F), \
    -    GET_NUM_FROM_PRIO((x).s.priority), \
    -    x.s.mac_address[0], x.s.mac_address[1], x.s.mac_address[2], \
    -    x.s.mac_address[3], x.s.mac_address[4], x.s.mac_address[5]
    -
    -#define PRT_ID_FMT "%01hhX.%03hX"
    -#define PRT_ID_ARGS(x) ((GET_PRIORITY_FROM_IDENTIFIER(x) >> 4) & 0x0F), \
    -                       GET_NUM_FROM_PRIO(x)
    -
     #define BOOL_STR(x) ((x) ? "yes" : "no")
     #define PROTO_VERS_STR(x)   ((protoRSTP == (x)) ? "rstp" : \
                                  ((protoMSTP <= (x)) ? "mstp" : "stp"))
    Index: mstp.h
    ===================================================================
    --- mstp.h  (revision 37)
    +++ mstp.h  (working copy)
    @@ -81,6 +81,18 @@ typedef __be16 port_identifier_t;
         *first_octet |= (pri) & 0xF0;               \
         }while(0)
    
    +#define GET_NUM_FROM_PRIO(p) (__be16_to_cpu(p) & 0x0FFF)
    +
    +#define BR_ID_FMT "%01hhX.%03hX.%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX"
    +#define BR_ID_ARGS(x) ((GET_PRIORITY_FROM_IDENTIFIER(x) >> 4) & 0x0F), \
    +    GET_NUM_FROM_PRIO((x).s.priority), \
    +    x.s.mac_address[0], x.s.mac_address[1], x.s.mac_address[2], \
    +    x.s.mac_address[3], x.s.mac_address[4], x.s.mac_address[5]
    +
    +#define PRT_ID_FMT "%01hhX.%03hX"
    +#define PRT_ID_ARGS(x) ((GET_PRIORITY_FROM_IDENTIFIER(x) >> 4) & 0x0F), \
    +                       GET_NUM_FROM_PRIO(x)
    +
     #define CONFIGURATION_NAME_LEN   32
     #define CONFIGURATION_DIGEST_LEN 16
     typedef union
    
     

    Last edit: Satish Ashok 2013-05-29
    • Vitalii Demianets

      Satish!

      Issue:
      The bridge and port identifier present in received BPDU is equal to the local port's values, discard the BPDU to identify possibly looped packets
      Vitali, this is the loopback issue, for which you had a couple of questions. This is a negative testcase and this is specified in 802.1D as mentioned below. Just felt that processing every loopbacked packet would not be good and also it would be better to syslog this as an error, so that admin knows of the issue and takes immediate action.

      After a lot of consideration I decided to NACK this patch. I wrote down the reasons in the wiki. Here is the citation:

      Mstpd will not drop looped-back BPDUs, and there are several good reasons for it:
      a) 802.1Q explicitly says that loopback check is not necessary (see clause 14.4, NOTE 3 of the 802.1Q-2005);
      b) mstpd puts the port in the Backup/Discarding state after receiving looped BPDU, even when forced to the "stp" protocol, so there is no problem - mstpd takes reasonable action in this case;
      c) if looped-back BPDUs were dropped, the port would remain in Forwarding state and bad things would happen, e.g. broadcast storms and undesired MAC learning on this port.
      There are reports that such behavior breaks 802.1D compliance. That is expected - after all, mstpd is compliant with 802.1Q, not 802.1D.

       
1 2 3 > >> (Page 1 of 3)

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.