# SNMP Support

SNMP is a widely implemented feature for collecting network information from router and/or host. FRR itself does not support SNMP agent (server daemon) functionality but is able to connect to a SNMP agent using the the AgentX protocol (RFC 2741) and make the routing protocol MIBs available through it.

Note that SNMP Support needs to be enabled at compile-time and loaded as module on daemon startup. Refer to Loadable Module Support on the latter. If you do not start the daemons with snmp module support snmp will not work properly.

## Getting and installing an SNMP agent

The supported SNMP agent is AgentX. We recommend to use the latest version of net-snmp which was formerly known as ucd-snmp. It is free and open software and available at http://www.net-snmp.org/ and as binary package for most Linux distributions.

## NET-SNMP configuration

Routers with a heavy amount of routes (e.g. BGP full table) might experience problems with a hanging vtysh from time to time, 100% CPU on the snmpd or even crashes of the frr daemon(s) due to stalls within AgentX. Once snmp agents connects they start receiving a heavy amount of SNMP data (all the routes) which cannot be handled quick enough. It's recommended (by several vendors as well) to exclude these OID's unless you really need them, which can be achieved by amending the default view from SNMP

`/etc/snmp/snmpd.conf`:

    # This is the default view
    view all    included  .1 80
    # Remove ipRouteTable from view
    view all    excluded  .1.3.6.1.2.1.4.21
    # Remove ipNetToMediaTable from view
    view all    excluded  .1.3.6.1.2.1.4.22
    # Remove ipNetToPhysicalPhysAddress from view
    view all    excluded  .1.3.6.1.2.1.4.35
    # Remove ipCidrRouteTable  from view
    view all    excluded  .1.3.6.1.2.1.4.24
    # Optionally protect SNMP private/secret values
    view all    excluded  .1.3.6.1.6.3.15
    view all    excluded  .1.3.6.1.6.3.16
    view all    excluded  .1.3.6.1.6.3.18
    # Optionally allow SNMP public info (sysName, location, etc)
    view system included  .iso.org.dod.internet.mgmt.mib-2.system

## AgentX configuration

To enable AgentX protocol support, FRR must have been build with the `--enable-snmp` or --enable-snmp=agentx option. Both the master SNMP agent (snmpd) and each of the FRR daemons must be configured. In `/etc/snmp/snmpd.conf`, the `master agentx` directive should be added. In each of the FRR daemons, `agentx` command will enable AgentX support.

`/etc/snmp/zebra.conf`:

    #
    # example access restrictions setup
    #
    com2sec readonly default public
    group MyROGroup v1 readonly
    view all included .1 80
    access MyROGroup "" any noauth exact all none none
    #
    # enable master agent for AgentX subagents
    #
    master agentx

`/etc/frr/ospfd.conf:`

>     ! ... the rest of ospfd.conf has been omitted for clarity ...
>     !
>     agentx
>     !

Upon successful connection, you should get something like this in the log of each FRR daemons:

    2012/05/25 11:39:08 ZEBRA: snmp[info]: NET-SNMP version 5.4.3 AgentX subagent connected

Then, you can use the following command to check everything works as expected:

    # snmpwalk -c public -v1 localhost .1.3.6.1.2.1.14.1.1
    OSPF-MIB::ospfRouterId.0 = IpAddress: 192.168.42.109
    [...]

The AgentX protocol can be transported over a Unix socket or using TCP or UDP. It usually defaults to a Unix socket and depends on how NetSNMP was built. If need to configure FRR to use another transport, you can configure it through `/etc/snmp/frr.conf`:

    [snmpd]
    # Use a remote master agent
    agentXSocket tcp:192.168.15.12:705

Here is the syntax for using AgentX:

agentx

## Handling SNMP Traps

To handle snmp traps make sure your snmp setup of frr works correctly as described in the frr documentation in SNMP Support.

The BGP4 mib will send traps on peer up/down events. These should be visible in your snmp logs with a message similar to:

    snmpd[13733]: Got trap from peer on fd 14

To react on these traps they should be handled by a trapsink. Configure your trapsink by adding the following lines to `/etc/snmpd/snmpd.conf`:

    # send traps to the snmptrapd on localhost
    trapsink localhost

This will send all traps to an snmptrapd running on localhost. You can of course also use a dedicated management station to catch traps. Configure the snmptrapd daemon by adding the following line to `/etc/snmpd/snmptrapd.conf`:

    traphandle .1.3.6.1.4.1.3317.1.2.2 /etc/snmp/snmptrap_handle.sh

This will use the bash script `/etc/snmp/snmptrap_handle.sh` to handle the BGP4 traps. To add traps for other protocol daemons, lookup their appropriate OID from their mib. (For additional information about which traps are supported by your mib, lookup the mib on http://www.oidview.com/mibs/detail.html).

Make sure snmptrapd is started.

The snmptrap_handle.sh script I personally use for handling BGP4 traps is below. You can of course do all sorts of things when handling traps, like sound a siren, have your display flash, etc., be creative ;).

    #!/bin/bash

    # routers name
    ROUTER=`hostname -s`

    #email address use to sent out notification
    EMAILADDR="john@doe.com"
    #email address used (allongside above) where warnings should be sent
    EMAILADDR_WARN="sms-john@doe.com"

    # type of notification
    TYPE="Notice"

    # local snmp community for getting AS belonging to peer
    COMMUNITY="<community>"

    # if a peer address is in $WARN_PEERS a warning should be sent
    WARN_PEERS="192.0.2.1"

    # get stdin
    INPUT=`cat -`

    # get some vars from stdin
    uptime=`echo $INPUT | cut -d' ' -f5`
    peer=`echo $INPUT | cut -d' ' -f8 | sed -e 's/SNMPv2-SMI::mib-2.15.3.1.14.//g'`
    peerstate=`echo $INPUT | cut -d' ' -f13`
    errorcode=`echo $INPUT | cut -d' ' -f9 | sed -e 's/\\"//g'`
    suberrorcode=`echo $INPUT | cut -d' ' -f10 | sed -e 's/\\"//g'`
    remoteas=`snmpget -v2c -c $COMMUNITY localhost SNMPv2-SMI::mib-2.15.3.1.9.$peer | cut -d' ' -f4`

    WHOISINFO=`whois -h whois.ripe.net " -r AS$remoteas" | egrep '(as-name|descr)'`
    asname=`echo "$WHOISINFO" | grep "^as-name:" | sed -e 's/^as-name://g' -e 's/  //g' -e 's/^ //g' | uniq`
    asdescr=`echo "$WHOISINFO" | grep "^descr:" | sed -e 's/^descr://g' -e 's/  //g' -e 's/^ //g' | uniq`

    # if peer address is in $WARN_PEER, the email should also
    # be sent to $EMAILADDR_WARN
    for ip in $WARN_PEERS; do
    if [ "x$ip" == "x$peer" ]; then
    EMAILADDR="$EMAILADDR,$EMAILADDR_WARN"
    TYPE="WARNING"
    break
    fi
    done

    # convert peer state
    case "$peerstate" in
    1) peerstate="Idle" ;;
    2) peerstate="Connect" ;;
    3) peerstate="Active" ;;
    4) peerstate="Opensent" ;;
    5) peerstate="Openconfirm" ;;
    6) peerstate="Established" ;;
    *) peerstate="Unknown" ;;
    esac

    # get textual messages for errors
    case "$errorcode" in
    00)
    error="No error"
    suberror=""
    ;;
    01)
    error="Message Header Error"
    case "$suberrorcode" in
    01) suberror="Connection Not Synchronized" ;;
    02) suberror="Bad Message Length" ;;
    03) suberror="Bad Message Type" ;;
    *) suberror="Unknown" ;;
    esac
    ;;
    02)
    error="OPEN Message Error"
    case "$suberrorcode" in
    01) suberror="Unsupported Version Number" ;;
    02) suberror="Bad Peer AS" ;;
    03) suberror="Bad BGP Identifier" ;;
    04) suberror="Unsupported Optional Parameter" ;;
    05) suberror="Authentication Failure" ;;
    06) suberror="Unacceptable Hold Time" ;;
    *) suberror="Unknown" ;;
    esac
    ;;
    03)
    error="UPDATE Message Error"
    case "$suberrorcode" in
    01) suberror="Malformed Attribute List" ;;
    02) suberror="Unrecognized Well-known Attribute" ;;
    03) suberror="Missing Well-known Attribute" ;;
    04) suberror="Attribute Flags Error" ;;
    05) suberror="Attribute Length Error" ;;
    06) suberror="Invalid ORIGIN Attribute" ;;
    07) suberror="AS Routing Loop" ;;
    08) suberror="Invalid NEXT_HOP Attribute" ;;
    09) suberror="Optional Attribute Error" ;;
    10) suberror="Invalid Network Field" ;;
    11) suberror="Malformed AS_PATH" ;;
    *) suberror="Unknown" ;;
    esac
    ;;
    04)
    error="Hold Timer Expired"
    suberror=""
    ;;
    05)
    error="Finite State Machine Error"
    suberror=""
    ;;
    06)
    error="Cease"
    case "$suberrorcode" in
    01) suberror="Maximum Number of Prefixes Reached" ;;
    02) suberror="Administratively Shutdown" ;;
    03) suberror="Peer Unconfigured" ;;
    04) suberror="Administratively Reset" ;;
    05) suberror="Connection Rejected" ;;
    06) suberror="Other Configuration Change" ;;
    07) suberror="Connection collision resolution" ;;
    08) suberror="Out of Resource" ;;
    09) suberror="MAX" ;;
    *) suberror="Unknown" ;;
    esac
    ;;
    *)
    error="Unknown"
    suberror=""
    ;;
    esac

    # create textual message from errorcodes
    if [ "x$suberror" == "x" ]; then
    NOTIFY="$errorcode ($error)"
    else
    NOTIFY="$errorcode/$suberrorcode ($error/$suberror)"
    fi

    # form a decent subject
    SUBJECT="$TYPE: $ROUTER [bgp] $peer is $peerstate: $NOTIFY"
    # create the email body
    MAIL=`cat << EOF
    BGP notification on router $ROUTER.

    Peer: $peer
    AS: $remoteas
    New state: $peerstate
    Notification: $NOTIFY

    Info:
    $asname
    $asdescr

    Snmpd uptime: $uptime
    EOF`

    # mail the notification
    echo "$MAIL" | mail -s "$SUBJECT" $EMAILADDR