#!/usr/bin/perl # # Module: vyatta-snmp.pl # # **** License **** # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # This code was originally developed by Vyatta, Inc. # Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. # All Rights Reserved. # # Author: Stig Thormodsrud # Date: October 2007 # Description: Script to glue vyatta cli to snmp daemon # # **** End License **** # use strict; use warnings; use lib "/opt/vyatta/share/perl5/"; use Vyatta::Config; use Vyatta::Misc; use NetAddr::IP; use Getopt::Long; use File::Copy; use Socket; use Socket6; my $mibdir = '/opt/vyatta/share/snmp/mibs'; my $snmp_init = 'invoke-rc.d snmpd'; my $snmp_conf = '/etc/snmp/snmpd.conf'; my $snmp_client = '/etc/snmp/snmp.conf'; my $snmp_tmp = "/tmp/snmpd.conf.$$"; my $snmp_snmpv3_user_conf = '/usr/share/snmp/snmpd.conf'; my $snmp_snmpv3_createuser_conf = '/var/lib/snmp/snmpd.conf'; my $versionfile = '/opt/vyatta/etc/version'; my $local_agent = 'unix:/var/run/snmpd.socket'; my $password_file = '/config/snmp/superuser_pass'; my $snmp_level = 'service snmp'; sub snmp_running { open (my $pidf, '<', "/var/run/snmpd.pid") or return; my $pid = <$pidf>; close $pidf; chomp $pid; my $exe = readlink "/proc/$pid/exe"; return (defined($exe) && $exe eq "/usr/sbin/snmpd"); } sub snmp_stop { system("$snmp_init stop > /dev/null 2>&1"); } sub snmp_start { # we must stop snmpd first for creating vyatta user system("$snmp_init stop > /dev/null 2>&1"); open (my $fh, '>', $snmp_tmp) or die "Couldn't open $snmp_tmp - $!"; select $fh; snmp_get_constants(); snmp_get_values(); snmp_get_traps(); close $fh; select STDOUT; snmp_client_config(); move($snmp_tmp, $snmp_conf) or die "Couldn't move $snmp_tmp to $snmp_conf - $!"; } sub get_version { my $version = "unknown-version"; if (open (my $f, '<', $versionfile)) { while (<$f>) { chomp; if (m/^Version\s*:\s*(.*)$/) { $version = $1; last; } } close $f; } return $version; } # convert address to snmpd transport syntax sub transport_syntax { my ($addr, $port) = @_; my $ip = new NetAddr::IP $addr; die "$addr: not a valid IP address" unless $ip; my $version = $ip->version(); return "udp:$addr:$port" if ($version == 4); return "udp6:[$addr]:$port" if ($version == 6); die "$addr: unknown IP version $version"; } # Test if IPv6 is possible by opening a socket sub ipv6_disabled { socket ( my $s, PF_INET6, SOCK_DGRAM, 0) or return 1; close($s); return; } # Find SNMP agent listening addresses sub get_listen_address { my $config = new Vyatta::Config; my @listen; $config->setLevel('service snmp listen-address'); my @address = $config->listNodes(); if(@address) { foreach my $addr (@address) { my $port = $config->returnValue("$addr port"); push @listen, transport_syntax($addr, $port); } } else { # default if no address specified @listen = ( 'udp:161' ); push @listen, 'udp6:161' unless ipv6_disabled(); return @listen; } return @listen; } sub snmp_get_constants { my $version = get_version(); my $now = localtime; my @addr = get_listen_address(); # add local unix domain target for use by operational commands unshift @addr, $local_agent; print "# autogenerated by vyatta-snmp.pl on $now\n"; print "sysDescr Vyatta $version\n"; print "sysObjectID 1.3.6.1.4.1.30803\n"; print "sysServices 14\n"; print "master agentx\n"; # maybe needed by lldpd print "agentaddress ", join(',',@addr), "\n"; # add hook to read IF-MIB::ifAlias from sysfs print "pass .1.3.6.1.2.1.31.1.1.1.18 /opt/vyatta/sbin/if-mib-alias\n"; print "smuxpeer .1.3.6.1.4.1.3317.1.2.2\n"; # ospfd print "smuxpeer .1.3.6.1.4.1.3317.1.2.5\n"; # bgpd print "smuxpeer .1.3.6.1.4.1.3317.1.2.3\n"; # ripd print "smuxpeer .1.3.6.1.4.1.3317.1.2.9\n"; # mribd print "smuxpeer .1.3.6.1.2.1.83\n"; # mribd print "smuxpeer .1.3.6.1.4.1.3317.1.2.8\n"; # pimd print "smuxpeer .1.3.6.1.2.1.157\n"; # pimd print "smuxsocket localhost\n"; } # generate a random character hex string sub randhex { my $length = shift; return join "", map { unpack "H*", chr(rand(256)) } 1..($length/2); } # output snmpd.conf file syntax for community sub print_community { my ($config, $community) = @_; my $ro = $config->returnValue('authorization'); $ro = 'ro' unless $ro; my @clients = $config->returnValues('client'); my @networks = $config->returnValues('network'); my @restriction = (@clients, @networks); if (!@restriction) { print $ro . "community $community\n"; print $ro . "community6 $community\n" unless ipv6_disabled(); return; } foreach my $addr (@restriction) { my $ip = new NetAddr::IP $addr; die "$addr: Not a valid IP address" unless $ip; if ($ip->version() == 4) { print $ro . "community $community $addr\n"; } elsif ($ip->version() == 6) { print $ro . "community6 $community $addr\n"; } else { die "$addr: bad IP version ", $ip->version(); } } } sub snmp_get_values { my $config = new Vyatta::Config; my @communities = $config->listNodes("service snmp community"); foreach my $community (@communities) { $config->setLevel("service snmp community $community"); print_community($config, $community); } $config->setLevel("service snmp smux-peer"); my @smuxpeers = $config->returnValues(); foreach my $smuxpeer (@smuxpeers) { print "smuxpeer $smuxpeer \n"; } $config->setLevel($snmp_level); my $contact = $config->returnValue("contact"); if (defined $contact) { print "SysContact $contact \n"; } my $description = $config->returnValue("description"); if (defined $description) { print "SysDescr $description \n"; } my $location = $config->returnValue("location"); if (defined $location) { print "SysLocation $location \n"; } } sub snmp_get_traps { my $config = new Vyatta::Config; $config->setLevel($snmp_level); # linkUp/Down configure the Event MIB tables to monitor # the ifTable for network interfaces being taken up or down # for making internal queries to retrieve any necessary information # create an internal snmpv3 user of the form 'vyattaxxxxxxxxxxxxxxxx' my $vyatta_user = "vyatta" . randhex(16); snmp_create_snmpv3_user($vyatta_user); snmp_write_snmpv3_user($vyatta_user); print "iquerySecName $vyatta_user\n"; # Modified from the default linkUpDownNotification # to include more OIDs and poll more frequently print <listNodes("trap-target"); return unless @trap_targets; foreach my $trap_target (@trap_targets) { my $port = $config->returnValue("trap-target $trap_target port"); my $community = $config->returnValue("trap-target $trap_target community"); print "trap2sink $trap_target"; print ":$port" if $port; print " $community" if $community; print "\n"; } } # Configure SNMP client parameters sub snmp_client_config { my $config = new Vyatta::Config; $config->setLevel($snmp_level); open (my $cf, '>', $snmp_client) or die "Couldn't open $snmp_client - $!"; my $version = get_version(); my $now = localtime; print {$cf} "# autogenerated by vyatta-snmp.pl on $now\n"; my $trap_source = $config->returnValue('trap-source'); print {$cf} "clientaddr $trap_source\n" if ($trap_source); close $cf; } sub snmp_create_snmpv3_user { my $vyatta_user = shift; my $passphrase = randhex(32); my $createuser = "createUser $vyatta_user MD5 \"$passphrase\" DES"; open(my $fh, '>', $snmp_snmpv3_createuser_conf) || die "Couldn't open $snmp_snmpv3_createuser_conf - $!"; print $fh $createuser; close $fh; open(my $pass_file, '>', $password_file) || die "Couldn't open $password_file - $!"; print $pass_file $passphrase; close $pass_file; } sub snmp_write_snmpv3_user { my $vyatta_user = shift; my $user = "rwuser $vyatta_user\n"; open(my $fh, '>', $snmp_snmpv3_user_conf) || die "Couldn't open $snmp_snmpv3_user_conf - $!"; print $fh $user; close $fh; } # # main # my $update_snmp; my $stop_snmp; GetOptions("update-snmp!" => \$update_snmp, "stop-snmp!" => \$stop_snmp); snmp_start() if ($update_snmp); snmp_stop() if ($stop_snmp);