Junos Automation (Scripting)
Reply
Contributor
Ribo
Posts: 40
Registered: ‎06-03-2009

RFC: VPLS Loop prevention script.

[ Edited ]

Hi folks,

 

I'd like to share a script I wrote that disables an interface-unit if a loop is seen within a vpls instance or bridge domain on an MX routers. I would appreciate the groups feedback on any improvements or efficiencies here.

 

This was written to help protect customer vpls instances from accidental layer2 or fiber loops but it acts on any configured bridge domain that is reporting mac-moves in "show l2-learning mac-move-buffer" on JunOS 9.3 or higher.

---

 Script attached...

 

/* 
*  NAME: 	vpls-loop-protect.slax 
*
* 
*  FUNCTION: 	To shutdown a customer facing interface unit that is looping 
*		traffic back into a VPLS instance.
*
*  METHOD:   	The script is triggered by event "l2ald_mac_move_notification"
*		It firstly creates the event-policy to call itself in config.
*		It checks the contents of the command  
*		"show l2-learning mac-move-buffer extensive".
*		It creates variables for the new-interface and the mac-address.
*		It then separates the interface into physical and unit variables
*		If the interface is "lsi" a message is written to syslog.
*		If the interface is not "lsi" the interface-unit is disabled.
*		and a message is sent to syslog....and no zombies are hurt.
*
*  CREDITS:
*    		CREATED: 05/21/12
*    		BY: Rich Boldy (Time Warner Cable) 
*    		LAST MOD:07/05/12
*    		BY: Rich Boldy
*    		SCRIPT VERSION: 4.2
*
*  MODIFICATION HISTORY:
*    		v1.0 = Initial Draft
*       	v1.1 = event-policy creation - working.
*		v2.0 = variable testing and show command parse working.
*		v2.1 = variables and error testing passing to config template.
*		v2.2 = phyiscial interface shutdown testing and working.
*		v3.0 = unit shutdown testing and working with syslog messages.
*		v4.0 = restrticted to not attempt to shutdown lsi interfaces.   
*		v4.1 = clean-up and edits and log messages fixed - tested and working.
*		v4.2 = The juniper config needed for script to run added to notes below and log messages cleaned up.
*
*  REQUIREMENTS:
*     		!!!! This script only works if the following config is applied to the router !!!!
*
*       set event-options event-script file vpls-loop-protect.slax source http://insert-url-of-local-script-server/vpls-loop-protect.slax
*       set event-options event-script file vpls-loop-protect.slax source http://insert-url-of-local-script-server/vpls-loop-protect.slax refresh
*       run file copy /var/db/scripts/event/vpls-loop-protect.slax re1:/var/db/scripts/event/.
*       set protocols l2-learning global-mac-move threshold-count 2
*
*     		a commit is required after the above.
*
*/ 
version 1.1;
ns junos = "http://xml.juniper.net/junos/*/junos";
ns xnm = "http://xml.juniper.net/xnm/1.1/xnm";
ns jcs = "http://xml.juniper.net/junos/commit-scripts/1.0";
import "../import/junos.xsl";

/* Create the event policy in config */

var $event-definition = {
    <event-options> {
        <policy> {
            <name> 'vpls-loop-protect';
            <events> 'L2ALD_MAC_MOVE_NOTIFICATION';
            <then> {
                <event-script> {
                    <name> 'vpls-loop-protect.slax';
                }
            }
        }
    }
}

/* Set variable $self for name of this script */

var $self="vpls-loop-protect.slax";

/* Create a match condition for the trigger event */

match / {
	<op-script-results> {
		/* Analyse the triggering event. */
		if (not(event-script-input/trigger-event/id)) {
		    expr jcs:syslog("user.err", $self, "[ERROR]: Called without a <trigger-event>/<id> element.");
		}
		else if (event-script-input/trigger-event/id == "L2ALD_MAC_MOVE_NOTIFICATION") {
		call collect-data();
		}
	}
}

template collect-data()   {

/*
* Run the command "show l2-learning mac-move-buffer.
* Associate the new interface to variable $new-interface.
* Associate the mac address to variable $mac-address.
* Create a variable called "$filter-name" for future use.
*/

var $show-mac-move = { <command> "show l2-learning mac-move-buffer extensive";
	       	}
var $mac-move = jcs:invoke($show-mac-move);
var $new-interface = $mac-move/l2ald-mac-move-entry/l2ald-mac-move-to-ifl;
var $mac-address = $mac-move/l2ald-mac-move-entry/l2ald-mac-address;
var $filter-name = "vpls-loop-protect-" _ $new-interface;

/* Parse interface to separate out physical and unit */

var $pattern = "[.]";
var $substrings = jcs:split($pattern, $new-interface);
var $physical = $substrings[1];
var $unit = $substrings[2];

/* Create variable "lsi" to match against */

var $lsi = "lsi";

/* Create match condition to exclude lsi interfaces from shutdown and log */

	if ($physical == $lsi) {
        expr jcs:syslog("user.warning", $self, " [VPLS-LOOP-DETECTED]: Learning of Source mac-address ", $mac-address, " flapping on interface ", $new-interface);
}

/* Create match condition to call template to shut interface unit and log  */
/* Variables $physical and $unit are passed to the called template */
 
	else {
	         call MakeConfigChange($physical = $physical, $unit = $unit);
	expr jcs:syslog("user.warning", $self, " [VPLS-LOOP-DETECTED]: Frame with source mac-address ", $mac-address, " looped back into vpls on ", $new-interface);
	} 
}

/* Create template for making the config change */

template MakeConfigChange($physical, $unit) {

/* Declare config to change as a variable */

var $disable-unit = {
        <configuration> {
             <interfaces> {
                 <interface> {
                     <name> $physical; 
			<unit> {
			  <name> $unit; 
	                        call jcs:emit-comment();
				<disable>; 
			 	}
        	  	}
		}
	}
}

/* Open connection with mgd */

var $con = jcs:open();

/* Create condition to log error message if connect fails */

if (not($con)) {
         expr jcs:syslog("user.err", $self, "[Err]: Unable to connect to mgd.");
}

/* Create condition to apply config change and log resul */

else { 
          var $change-result := {
  		call jcs:load-configuration($connection=$con,$configuration=$disable-unit); 
                }
                if ($change-result//xnm:error) {
                        expr jcs:syslog("user.err", $self, " [Err]: Error during commit (",$change-results//xnm:error/message, ")");
                }
                else if ($change-result//xnm:warning) {
                        expr jcs:syslog("user.warning", $self, " [Warning]: Warning during commit (",$change-results//xnm:warning/message, ")");
                }
                else {
                expr jcs:syslog("user.warning", $self, " [VPLS-LOOP-DETECTED]: Interface ", $physical, ".", $unit, " shutdown due to loop.");
                }       
                expr jcs:close($con);
        }
}

 

 

 

 

 

Trusted Contributor
Bruno
Posts: 34
Registered: ‎06-30-2009
0

Re: RFC: VPLS Loop prevention script.

Hi Ribo,

 

I think this makes a lot of sense.

In fact, just a few days ago I suggested an event script based solution to a customer that faces the exact problem in their VPLS offering.

So it's good to see someone already worked on a solution ;-)

Thanks a lot for sharing.

 

Did you already deploy it in our network?

 

 

Cheers,

 

Bruno

 

 

JNCIE-M #15
Contributor
Ribo
Posts: 40
Registered: ‎06-03-2009
0

Re: RFC: VPLS Loop prevention script.

Thanks Bruno!

 

I'm in the process of field testing this now - will update with new version once tested.

 

Contributor
JoshTX
Posts: 65
Registered: ‎09-14-2009
0

Re: RFC: VPLS Loop prevention script.

+1 Kudos.

Contributor
Ribo
Posts: 40
Registered: ‎06-03-2009
0

Re: RFC: VPLS Loop prevention script.

The script was field tested and performed as expected.

 

We are doing some additional testing with different topologies - i.e.. Multiple VPLS end-points interfaces on the same MX router & DPC / MPC compatibility.

 

Will update.

Contributor
Ribo
Posts: 40
Registered: ‎06-03-2009
0

Re: RFC: VPLS Loop prevention script.

[ Edited ]

This has now been tested on MPC and DPC up to 10.4X15.1 and works as detailed in this thread.

 

Attached is the latest updated version. If anyone deploys this please let me know your experience. Feel free to change it to suit your needs - maybe just give me a mention in the credits :smileyhappy:

------

 

/* 
*  NAME: 	vpls-loop-protect.slax 
*
* 
*  FUNCTION: 	To shutdown a customer facing interface unit that is looping 
*		traffic back into a VPLS instance.
*
*  METHOD:   	The script is triggered by event "l2ald_mac_move_notification"
*		It firstly creates the event-policy to call itself in config.
*		It checks the contents of the command  
*		"show l2-learning mac-move-buffer extensive".
*		It creates variables for the new-interface and the mac-address.
*		It then separates the interface into physical and unit variables
*		If the interface is "lsi" a message is written to syslog.
*		If the interface is not "lsi" the interface-unit is disabled.
*		and a message is sent to syslog....and no zombies are hurt.
*
*  CREDITS:
*    		CREATED: 05/21/12
*    		BY: Rich Boldy (Time Warner Cable) 
*    		LAST MOD:07/05/12
*    		BY: Rich Boldy
*    		SCRIPT VERSION: 4.2
*
*  MODIFICATION HISTORY:
*    		v1.0 = Initial Draft
*       	v1.1 = event-policy creation - working.
*		v2.0 = variable testing and show command parse working.
*		v2.1 = variables and error testing passing to config template.
*		v2.2 = phyiscial interface shutdown testing and working.
*		v3.0 = unit shutdown testing and working with syslog messages.
*		v4.0 = restrticted to not attempt to shutdown lsi interfaces.   
*		v4.1 = clean-up and edits and log messages fixed - tested and working.
*		v4.2 = The juniper config needed for script to run added to notes below and log messages cleaned up.
*
*  REQUIREMENTS:
*     		!!!! This script only works if the following config is applied to the router !!!!
*
*       set event-options event-script file vpls-loop-protect.slax source http://24.73.240.173/junoscripts/vpls-loop-protect.slax
*       set event-options event-script file vpls-loop-protect.slax source http://24.73.240.173/junoscripts/vpls-loop-protect.slax refresh
*       run file copy /var/db/scripts/event/vpls-loop-protect.slax re1:/var/db/scripts/event/.
*       set protocols l2-learning global-mac-move threshold-count 2
*
*     		a commit is required after the above.
*
*/ 
version 1.1;
ns junos = "http://xml.juniper.net/junos/*/junos";
ns xnm = "http://xml.juniper.net/xnm/1.1/xnm";
ns jcs = "http://xml.juniper.net/junos/commit-scripts/1.0";
import "../import/junos.xsl";

/* Create the event policy in config */

var $event-definition = {
    <event-options> {
        <policy> {
            <name> 'vpls-loop-protect';
            <events> 'L2ALD_MAC_MOVE_NOTIFICATION';
            <then> {
                <event-script> {
                    <name> 'vpls-loop-protect.slax';
                }
            }
        }
    }
}

/* Set variable $self for name of this script */

var $self="vpls-loop-protect.slax";

/* Create a match condition for the trigger event */

match / {
	<op-script-results> {
		/* Analyse the triggering event. */
		if (not(event-script-input/trigger-event/id)) {
		    expr jcs:syslog("user.err", $self, "[ERROR]: Called without a <trigger-event>/<id> element.");
		}
		else if (event-script-input/trigger-event/id == "L2ALD_MAC_MOVE_NOTIFICATION") {
		call collect-data();
		}
	}
}

template collect-data()   {

/*
* Run the command "show l2-learning mac-move-buffer.
* Associate the new interface to variable $new-interface.
* Associate the mac address to variable $mac-address.
* Create a variable called "$filter-name" for future use.
*/

var $show-mac-move = { <command> "show l2-learning mac-move-buffer extensive";
	       	}
var $mac-move = jcs:invoke($show-mac-move);
var $new-interface = $mac-move/l2ald-mac-move-entry/l2ald-mac-move-to-ifl;
var $mac-address = $mac-move/l2ald-mac-move-entry/l2ald-mac-address;
var $filter-name = "vpls-loop-protect-" _ $new-interface;

/* Parse interface to separate out physical and unit */

var $pattern = "[.]";
var $substrings = jcs:split($pattern, $new-interface);
var $physical = $substrings[1];
var $unit = $substrings[2];

/* Create variable "lsi" to match against */

var $lsi = "lsi";

/* Create match condition to exclude lsi interfaces from shutdown and log */

	if ($physical == $lsi) {
        expr jcs:syslog("user.warning", $self, " [VPLS-LOOP-DETECTED]: Learning of Source mac-address ", $mac-address, " flapping on interface ", $new-interface);
}

/* Create match condition to call template to shut interface unit and log  */
/* Variables $physical and $unit are passed to the called template */
 
	else {
	         call MakeConfigChange($physical = $physical, $unit = $unit);
	expr jcs:syslog("user.warning", $self, " [VPLS-LOOP-DETECTED]: Frame with source mac-address ", $mac-address, " looped back into vpls on ", $new-interface);
	} 
}

/* Create template for making the config change */

template MakeConfigChange($physical, $unit) {

/* Declare config to change as a variable */

var $disable-unit = {
        <configuration> {
             <interfaces> {
                 <interface> {
                     <name> $physical; 
			<unit> {
			  <name> $unit; 
	                        call jcs:emit-comment();
				<disable>; 
			 	}
        	  	}
		}
	}
}

/* Open connection with mgd */

var $con = jcs:open();

/* Create condition to log error message if connect fails */

if (not($con)) {
         expr jcs:syslog("user.err", $self, "[Err]: Unable to connect to mgd.");
}

/* Create condition to apply config change and log resul */

else { 
          var $change-result := {
  		call jcs:load-configuration($connection=$con,$configuration=$disable-unit); 
                }
                if ($change-result//xnm:error) {
                        expr jcs:syslog("user.err", $self, " [Err]: Error during commit (",$change-results//xnm:error/message, ")");
                }
                else if ($change-result//xnm:warning) {
                        expr jcs:syslog("user.warning", $self, " [Warning]: Warning during commit (",$change-results//xnm:warning/message, ")");
                }
                else {
                expr jcs:syslog("user.warning", $self, " [VPLS-LOOP-DETECTED]: Interface ", $physical, ".", $unit, " shutdown due to loop.");
                }       
                expr jcs:close($con);
        }
}

 

 

Regular Visitor
nshetty
Posts: 1
Registered: ‎01-12-2011
0

Re: RFC: VPLS Loop prevention script.

Thanks , works like charm :smileyhappy:

Contributor
JoshTX
Posts: 65
Registered: ‎09-14-2009

Re: RFC: VPLS Loop prevention script.

[ Edited ]

We found a pretty serious bug in the script.  Attached is an updated version with a fix.

 

NOTE:  the 'opt-out' feature that was added in this version is not yet working.  We're still investigating why.  If you have any ideas, please share!

Contributor
Ribo
Posts: 40
Registered: ‎06-03-2009
0

Re: RFC: VPLS Loop prevention script.

[ Edited ]

Thanks Josh,

 

Nice fixes - the issues here are shown in the modification history in the script but just for everyone here:

 

*        v4.3 = Add exception for virtual MAC's used by VRRP/HSRP, fixed a
*               serious bug where oldest entry in buffer was evaluated rather
*               than newest, and added a method to 'opt-out' of the script.

 

Two issues addressed here are:

1) Where a FHRP is used within the VPLS instance then the MAC moving form one site to another on FHRP failover was seen by the script as a mac-move and thus the unit was disabled. v4.3 now looks at the mac-address first and ingores this mac-move is the mac-address matches the following:

 

00:00:5e:00:01:xx(VRRP)

00:00:0c:07:ac:xx(HSRP)

00:07:b4:00:01:xx(GLBP)

02:bf(WLBS)

 

2) It was expressed previously that the mac-move buffer should be cleared after each initiation of the script to prevent the script from looking at legacy data - a much more elegant solution provided by Josh here in 4.3 is to force the script to look at the last entty in the buffer - thus eliminating the need to clear the buffer manually. This came to light as an issue when there was more than 1 mac-move on one router at the same time of in a short time period before the buffer could be cleared.

-------------------

 

 

Contributor
JoshTX
Posts: 65
Registered: ‎09-14-2009
0

Re: RFC: VPLS Loop prevention script.

So, still haven't been able to figure out how to get the apply-macro feature working in this script.

 

I'm expecting that:

  • If a mac-move is detected:
    • If the routing-instance has an 'apply-macro' applied to it, then issue a warning to syslog and quit.
    • If the offending mac is known 'virtual' mac, then issue a warning to syslog and quit
    • If neither of the two above are true, then run MakeConfigChange, issue a warning to syslog and quit.

It seems the if statement on line 103 isn't correct since it never seems to match.  I'd sure appreciate anyone take a look and see what isn't apparent to me.

 

Copyright© 1999-2013 Juniper Networks, Inc. All rights reserved.