Juniper SRX - Automated Route Monitoring

Note: I may receive commissions for purchases made through links in this post. This is to help support my blog and does not have any impact on my recommendations.


I have always said that I’m not sure I could write code for a living, but I do really enjoy writing scripts that make my life easier. Today’s post is a great example of that. The ease of use offered by the Juniper SRX firewalls and JunOS is something that I wish I had in all of my networking infrastructure. Even better, a brand new SRX 300 can be purchased on Amazon for less than $300 – which made a great addition to my home lab. Now I have a place to develop and test automation without breaking production 🙂

Anyways – I had a requirement to monitor my SRX clusters for route changes. Specifically I’m using this with devices where I have implemented customer peering via BGP. The SRXs don’t natively offer any form of monitoring and alerting for this, and the current monitoring applications at my disposal don’t either. So I decided to write something myself, which took significantly less time than I had assumed.

Code has been posted up to my GitHub – but I’m going to walk through some of it here. This script is intended to run as a cron job on a 5 or 10 minute interval.

# Imports
from jnpr.junos import Device
from jnpr.junos.op.routes import RouteTable

On my initial research to see how easy this would be to pull off, I found a nifty thing in the JunOS PyEZ package. Turns out they already offer a module literally called RouteTable that can pull the information I need. Originally, I spent a bit of time trying to figure out how to use the standard device API to pull this info, but this module made everything 10x easier.

#################
# Required values
#################
deviceName = 'SRXFirewall'
deviceIP = '0.0.0.0'
apiuser = 'username'
apipassword = 'password'
SMTP = 'smtp-alias'
fromName = 'BGP Monitor'
fromAddr = '[email protected]'
toName = 'contactName'
toAddr = '[email protected]'
prefixDict = {
        "FriendlyName": "Prefix",
        "Route-to-Inet": "0.0.0.0/0"
    }
################

The section above contains a list of the required variables for this script to function. A lot of them are going to be self-explanatory – but I wanted to take a moment to look at the prefixDict. This is a Python dictionary that maps a friendly name to a route prefix, which is used when we check our routing table. For example, if you wanted to monitor the SRX device for a route for 10.10.10.0/24 which belongs to Customer1, then we would just add and entry in this dictionary for “Customer1”: “10.10.10.0/24”

Alright – now let’s skip to the good stuff:

# Function to check received BGP routes
def checkBGP():
    try:
        # Open SRX session
        dev.open()
    except:
        sys.exit(0)

    # Pull device routing table, then keep only BGP originated routes
    allroutes = RouteTable(dev)
    bgp = allroutes.get(protocol="bgp").keys()
    
    # Close SRX session
    dev.close()

The section above is the beginning of the checkBGP function. Simple enough – just open an API session to the SRX and grab the entire routing table. That module I was talking about earlier makes it super easy! Next, we pull only the routes originating from BGP and assign them to a variable called bgp.

So in order to run this script repeatedly as a cron, I needed a place to persistently store the last retrieved routing info. For the time being, this is done by simply writing a temp file with the information:

    if not os.path.isfile(tempfile):
        with open(tempfile, 'w+b') as a:
            a.write(str(bgp))
        sys.exit(0)

    # Local file used to keep track of BGP learned routes
    with open(tempfile, 'ab') as a:
        lastroutes = a.readlines()
        # Compare if routes are different
        if str(bgp) == str(lastroutes[0]):
            sys.exit(0)
        if str(bgp) != str(lastroutes[0]):
            pass
    # Delete file, then re-create with new route list
    #os.remove(tempfile)
    with open(tempfile, 'w+b') as a:
        a.write(str(bgp))

The logic in the comparison is simple enough. If the current string of routes pulled from the SRX equals what is in the temp file (from the last run), then we assume no changes have occurred – and the script ends. Otherwise, if they don’t match then something has changed.

Once we know something has changed, we’ll go ahead and find out exactly which route entry is missing:

# Create Status list, by checking received routes in bgp object status = [] for name,prefix in prefixDict.items(): if prefix in bgp: status.append("%s - RECEIVED" % name) if not prefix in bgp: status.append("%s - MISSING" % name)

This is where our prefixDict from earlier comes into play. We’ll look at every BGP prefix that we defined in that dictionary, and see if it exists in the current SRX routing table. All of this information (both routes received and missing) get added to an alert email.

Lastly, we’ll go ahead and compose our alert email to send out:

    # Send alert message
    sendMail(str(lastroutes[0]), str(bgp), status)

I’m not going to cover the email function here, since it’s pretty straightforward.

That’s it! The existence of the JunOS RouteTables module made creating this script a piece of cake. I’m considering adding onto this later with some additional functionality, so be sure to check my GitHub repo if you’re interested.

Hope this is useful to someone else out there – Let me know in the comments!