summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface-definitions/snmp.xml539
-rwxr-xr-xsrc/conf_mode/snmp.py234
2 files changed, 773 insertions, 0 deletions
diff --git a/interface-definitions/snmp.xml b/interface-definitions/snmp.xml
new file mode 100644
index 000000000..bcd5295ee
--- /dev/null
+++ b/interface-definitions/snmp.xml
@@ -0,0 +1,539 @@
+<?xml version="1.0"?>
+<!-- SNMP forwarder configuration -->
+<interfaceDefinition>
+ <node name="service">
+ <children>
+ <node name="snmp" owner="${vyos_conf_scripts_dir}/snmp.py">
+ <properties>
+ <help>Simple Network Management Protocol (SNMP)</help>
+ <priority>980</priority>
+ </properties>
+ <children>
+ <tagNode name="community">
+ <properties>
+ <help>Community name [REQUIRED]</help>
+ <constraint>
+ <regex>^[^%]+$</regex>
+ </constraint>
+ <constraintErrorMessage>Community string may not contain '%'</constraintErrorMessage>
+ </properties>
+ <children>
+ <leafNode name="authorization">
+ <properties>
+ <help>Authorization type (rw or ro) (default: 'ro')</help>
+ <constraint>
+ <regex>(ro|rw)</regex>
+ </constraint>
+ <constraintErrorMessage>Authorization type must be either 'rw' or 'ro'</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="client">
+ <properties>
+ <help>IP address of SNMP client allowed to contact system</help>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="network">
+ <properties>
+ <help>Subnet of SNMP client(s) allowed to contact system</help>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IP address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 address and prefix length</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-prefix"/>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <leafNode name="contact">
+ <properties>
+ <help>Contact information</help>
+ <constraint>
+ <regex>.{1,255}</regex>
+ </constraint>
+ <constraintErrorMessage>Contact information is limited to 255 characters or less</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="description">
+ <properties>
+ <help>Description information</help>
+ <constraint>
+ <regex>.{1,255}</regex>
+ </constraint>
+ <constraintErrorMessage>Description is limited to 255 characters or less</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <tagNode name="listen-address">
+ <properties>
+ <help>IP address to listen for incoming SNMP requests</help>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="port">
+ <properties>
+ <help>Port for SNMP service (default: '161')</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Numeric IP port</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ <constraintErrorMessage>Port number must be in range 1 to 65535</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <leafNode name="location">
+ <properties>
+ <help>Location information</help>
+ <constraint>
+ <regex>.{1,255}</regex>
+ </constraint>
+ <constraintErrorMessage>Location is limited to 255 characters or less</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="smux-peer">
+ <properties>
+ <help>Register a subtree for SMUX-based processing</help>
+ <valueHelp>
+ <format>oid</format>
+ <description>Object Identifier</description>
+ </valueHelp>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="trap-source">
+ <properties>
+ <help>SNMP trap source address</help>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <tagNode name="trap-target">
+ <properties>
+ <help>Address of trap target</help>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+ <children>
+ <leafNode name="community">
+ <properties>
+ <help>Community used when sending trap information</help>
+ </properties>
+ </leafNode>
+ <leafNode name="port">
+ <properties>
+ <help>Destination port used for trap notification</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Numeric IP port</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ <constraintErrorMessage>Port number must be in range 1 to 65535</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <node name="v3">
+ <properties>
+ <help>Simple Network Management Protocol (SNMP) v3</help>
+ </properties>
+ <children>
+ <leafNode name="engineid">
+ <properties>
+ <help>Specifies the EngineID that uniquely identify an agent (e.g. 0xff42)</help>
+ <constraint>
+ <regex>^(0x){0,1}([0-9a-f][0-9a-f]){1,18}$</regex>
+ </constraint>
+ <constraintErrorMessage>ID must contain an even number (from 2 to 36) of hex digits</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <tagNode name="group">
+ <properties>
+ <help>Specifies the group with name groupname</help>
+ </properties>
+ <children>
+ <leafNode name="mode">
+ <properties>
+ <help>Define group access permission (default: 'ro')</help>
+ <valueHelp>
+ <format>ro</format>
+ <description>read only</description>
+ </valueHelp>
+ <valueHelp>
+ <format>rw</format>
+ <description>read write</description>
+ </valueHelp>
+ <constraint>
+ <regex>(ro|rw)</regex>
+ </constraint>
+ <constraintErrorMessage>Authorization type must be either 'rw' or 'ro'</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="seclevel">
+ <properties>
+ <help>Defines security level</help>
+ <constraint>
+ <regex>(auth|priv)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="view">
+ <properties>
+ <help>Defines the name of view</help>
+ <completionHelp>
+ <path>service snmp v3 view</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <tagNode name="trap-target">
+ <properties>
+ <help>Defines SNMP target for inform or traps for IP</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IP address of trap target</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address of trap target</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ <validator name="ipv6-address"/>
+ </constraint>
+ </properties>
+ <children>
+ <node name="auth">
+ <properties>
+ <help>Defines the privacy</help>
+ </properties>
+ <children>
+ <leafNode name="encrypted-key">
+ <properties>
+ <help>Defines the encrypted key for authentication</help>
+ <constraint>
+ <regex>^0x[0-9a-f]*$</regex>
+ </constraint>
+ <constraintErrorMessage>Key must start from '0x' and contain hex digits</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="plaintext-key">
+ <properties>
+ <help>Defines the clear text key for authentication</help>
+ <constraint>
+ <regex>^.{8,}$</regex>
+ </constraint>
+ <constraintErrorMessage>Key must contain 8 or more characters</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="type">
+ <properties>
+ <help>Defines the protocol used for authentication (default: 'md5')</help>
+ <valueHelp>
+ <format>md5</format>
+ <description>Message Digest 5</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sha</format>
+ <description>Secure Hash Algorithm</description>
+ </valueHelp>
+ <constraint>
+ <regex>(md5|sha)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="engineid">
+ <properties>
+ <help>Specifies the EngineID that uniquely identify an agent (e.g. 0xff42)</help>
+ <constraint>
+ <regex>^(0x){0,1}([0-9a-f][0-9a-f]){1,18}$</regex>
+ </constraint>
+ <constraintErrorMessage>ID must contain from 2 to 36 hex digits</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="port">
+ <properties>
+ <help>Specifies TCP/UDP port of destination SNMP traps/informs (default: '162')</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Numeric IP port</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ <constraintErrorMessage>Port number must be in range 1 to 65535</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="privacy">
+ <properties>
+ <help>Defines the privacy</help>
+ </properties>
+ <children>
+ <leafNode name="encrypted-key">
+ <properties>
+ <help>Defines the encrypted key for privacy protocol</help>
+ <constraint>
+ <regex>^0x[0-9a-f]*$</regex>
+ </constraint>
+ <constraintErrorMessage>Key must start from '0x' and contain hex digits</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="plaintext-key">
+ <properties>
+ <help>Defines the clear text key for privacy protocol</help>
+ <constraint>
+ <regex>^.{8,}$</regex>
+ </constraint>
+ <constraintErrorMessage>Key must contain 8 or more characters</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="type">
+ <properties>
+ <help>Defines the protocol for privacy (default: 'des')</help>
+ <valueHelp>
+ <format>des</format>
+ <description>Data Encryption Standard</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes</format>
+ <description>Advanced Encryption Standard</description>
+ </valueHelp>
+ <constraint>
+ <regex>(des|aes)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="protocol">
+ <properties>
+ <help>Defines protocol for notification between TCP and UDP</help>
+ <valueHelp>
+ <format>tcp</format>
+ <description>Use Transmission Control Protocol for notifications</description>
+ </valueHelp>
+ <valueHelp>
+ <format>udp</format>
+ <description>Use User Datagram Protocol for notifications</description>
+ </valueHelp>
+ <constraint>
+ <regex>(tcp|udp)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="type">
+ <properties>
+ <help>Specifies the type of notification between inform and trap (default: 'inform')</help>
+ <valueHelp>
+ <format>inform</format>
+ <description>Use INFORM</description>
+ </valueHelp>
+ <valueHelp>
+ <format>trap</format>
+ <description>Use TRAP</description>
+ </valueHelp>
+ <constraint>
+ <regex>(inform|trap)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="user">
+ <properties>
+ <help>Defines username for authentication</help>
+ <completionHelp>
+ <path>service snmp v3 user</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ <node name="tsm">
+ <properties>
+ <help>Specifies that the snmpd uses encryption</help>
+ </properties>
+ <children>
+ <leafNode name="local-key">
+ <properties>
+ <help>Defines the server certificate fingerprint or key-file name</help>
+ </properties>
+ </leafNode>
+ <leafNode name="port">
+ <properties>
+ <help>Defines the port for TSM (default: '10161')</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Numeric IP port</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ <constraintErrorMessage>Port number must be in range 1 to 65535</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <tagNode name="user">
+ <properties>
+ <help>Specifies the user with name username</help>
+ <constraint>
+ <regex>^[^\(\)\|\-]+$</regex>
+ </constraint>
+ <constraintErrorMessage>Illegal characters in name</constraintErrorMessage>
+ </properties>
+ <children>
+ <node name="auth">
+ <properties>
+ <help>Specifies the auth</help>
+ </properties>
+ <children>
+ <leafNode name="encrypted-key">
+ <properties>
+ <help>Defines the encrypted key for authentication</help>
+ <constraint>
+ <regex>^0x[0-9a-f]*$</regex>
+ </constraint>
+ <constraintErrorMessage>Key must start from '0x' and contain hex digits</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="plaintext-key">
+ <properties>
+ <help>Defines the clear text key for authentication</help>
+ <constraint>
+ <regex>^.{8,}$</regex>
+ </constraint>
+ <constraintErrorMessage>Key must contain 8 or more characters</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="type">
+ <properties>
+ <help>Defines the protocol used for authentication (default: 'md5')</help>
+ <valueHelp>
+ <format>md5</format>
+ <description>Message Digest 5</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sha</format>
+ <description>Secure Hash Algorithm</description>
+ </valueHelp>
+ <constraint>
+ <regex>(md5|sha)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="engineid">
+ <properties>
+ <help>Specifies the EngineID that uniquely identify an agent (e.g. 0xff42)</help>
+ <constraint>
+ <regex>^(0x){0,1}([0-9a-f][0-9a-f]){1,18}$</regex>
+ </constraint>
+ <constraintErrorMessage>ID must contain from 2 to 36 hex digits</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="group">
+ <properties>
+ <help>Specifies group for user name</help>
+ <completionHelp>
+ <path>service snmp v3 group</path>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="mode">
+ <properties>
+ <help>Define users access permission (default: 'ro')</help>
+ <valueHelp>
+ <format>ro</format>
+ <description>read only</description>
+ </valueHelp>
+ <valueHelp>
+ <format>rw</format>
+ <description>read write</description>
+ </valueHelp>
+ <constraint>
+ <regex>(ro|rw)</regex>
+ </constraint>
+ <constraintErrorMessage>Authorization type must be either 'rw' or 'ro'</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="privacy">
+ <properties>
+ <help>Defines the privacy</help>
+ </properties>
+ <children>
+ <leafNode name="encrypted-key">
+ <properties>
+ <help>Defines the encrypted key for privacy protocol</help>
+ <constraint>
+ <regex>^0x[0-9a-f]*$</regex>
+ </constraint>
+ <constraintErrorMessage>Key must start from '0x' and contain hex digits</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="plaintext-key">
+ <properties>
+ <help>Defines the clear text key for privacy protocol</help>
+ <constraint>
+ <regex>^.{8,}$</regex>
+ </constraint>
+ <constraintErrorMessage>Key must contain 8 or more characters</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="type">
+ <properties>
+ <help>Defines the protocol for privacy (default: 'des')</help>
+ <valueHelp>
+ <format>des</format>
+ <description>Data Encryption Standard</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes</format>
+ <description>Advanced Encryption Standard</description>
+ </valueHelp>
+ <constraint>
+ <regex>(des|aes)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="tsm-key">
+ <properties>
+ <help>Specifies finger print or file name of TSM certificate</help>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
new file mode 100755
index 000000000..d32a9a343
--- /dev/null
+++ b/src/conf_mode/snmp.py
@@ -0,0 +1,234 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+import sys
+import os
+
+import jinja2
+
+from vyos.config import Config
+from vyos import ConfigError
+
+import vyos.version
+
+config_file_client = r'/etc/snmp/snmp.conf'
+config_file_daemon = r'/etc/snmp/snmpd.conf'
+
+# SNMPS template - be careful if you edit the template.
+client_config_tmpl = """
+### Autogenerated by snmp.py ###
+{% if trap_source -%}
+clientaddr {{ trap_source }}
+{% endif %}
+
+"""
+
+# SNMPS template - be careful if you edit the template.
+daemon_config_tmpl = """
+### Autogenerated by snmp.py ###
+# non configurable defaults
+sysObjectID 1.3.6.1.4.1.44641
+sysServices 14
+# maybe needed by lldpd
+master agentx
+agentXPerms 0755 0755
+# add hook to read IF-MIB::ifAlias from sysfs
+pass .1.3.6.1.2.1.31.1.1.1.18 /opt/vyatta/sbin/if-mib-alias
+# ospfd
+smuxpeer .1.3.6.1.4.1.3317.1.2.2
+# bgpd
+smuxpeer .1.3.6.1.4.1.3317.1.2.5
+# ripd
+smuxpeer .1.3.6.1.4.1.3317.1.2.3
+# mribd
+smuxpeer .1.3.6.1.4.1.3317.1.2.9
+# mribd
+smuxpeer .1.3.6.1.2.1.83
+# pimd
+smuxpeer .1.3.6.1.4.1.3317.1.2.8
+# pimd
+smuxpeer .1.3.6.1.2.1.157
+smuxsocket localhost
+
+# 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'
+## TODO!!!!
+iquerySecName vyatta3392514e4189da84
+
+# Modified from the default linkUpDownNotification
+# to include more OIDs and poll more frequently
+notificationEvent linkUpTrap linkUp ifIndex ifDescr ifType ifAdminStatus ifOperStatus
+notificationEvent linkDownTrap linkDown ifIndex ifDescr ifType ifAdminStatus ifOperStatus
+monitor -r 10 -e linkUpTrap "Generate linkUp" ifOperStatus != 2
+monitor -r 10 -e linkDownTrap "Generate linkDown" ifOperStatus == 2
+
+########################
+# configurable section #
+########################
+
+# Version
+sysDescr VyOS {{ version }}
+
+{% if description -%}
+# Description
+SysDescr {{ description }}
+{% endif %}
+
+# Listen
+agentaddress unix:/run/snmpd.socket{% for ip in listen_on %},udp:{{ ip.addr }}:{{ ip.port }}{% endfor %}
+
+
+# SNMP communities
+{% if communities -%}
+{% for c in communities %}
+{% if c.network -%}
+{% for network in c.network %}
+{{ c.authorization }}community {{ c.name }} {{ network }}
+{% endfor %}
+{% else %}
+{{ c.authorization }}community {{ c.name }}
+{% endif %}
+{% endfor %}
+{% endif %}
+
+{% if contact -%}
+# system contact information
+SysContact {{ contact }}
+{% endif %}
+
+{% if location -%}
+# system location information
+SysLocation {{ location }}
+{% endif %}
+
+{% if smux_peers -%}
+# additional smux peers
+{% for sp in smux_peers %}
+smuxpeer {{ sp }}
+{% endfor %}
+{% endif %}
+
+"""
+
+default_config_data = {
+ 'listen_on': [],
+ 'location' : '',
+ 'description' : '',
+ 'contact' : '',
+ 'communities': [],
+ 'trap_source': '',
+ 'smux_peers': []
+}
+
+def get_config():
+ snmp = default_config_data
+ conf = Config()
+ if not conf.exists('service snmp'):
+ return None
+ else:
+ conf.set_level('service snmp')
+
+ version_data = vyos.version.get_version_data()
+ snmp.setdefault('version', version_data['version'])
+
+ if conf.exists('community'):
+ for name in conf.list_nodes('community'):
+ community = {
+ 'name': name,
+ 'authorization': 'ro',
+ 'network': []
+ }
+
+ if conf.exists('community {0} authorization'.format(name)):
+ community['authorization'] = conf.return_value('community {0} authorization'.format(name))
+
+ if conf.exists('community {0} network'.format(name)):
+ community['network'] = conf.return_values('community {0} network'.format(name))
+
+ snmp['communities'].append(community)
+
+ if conf.exists('contact'):
+ snmp['contact'] = conf.return_value('contact')
+
+ if conf.exists('description'):
+ snmp['description'] = conf.return_value('description')
+
+ if conf.exists('listen-address'):
+ for addr in conf.list_nodes('listen-address'):
+ listen = {
+ 'addr': addr,
+ 'port': '161'
+ }
+
+ if conf.exists('listen-address {0} port'.format(addr)):
+ listen['port'] = conf.return_value('listen-address {0} port'.format(addr))
+
+ snmp['listen_on'].append(listen)
+
+ if conf.exists('location'):
+ snmp['location'] = conf.return_value('location')
+
+ if conf.exists('smux-peer'):
+ snmp['smux_peers'] = conf.return_values('smux-peer')
+
+ if conf.exists('trap-source'):
+ snmp['trap_source'] = conf.return_value('trap-source')
+
+ return snmp
+
+def verify(snmp):
+ return None
+
+def generate(snmp):
+ if snmp is None:
+ return None
+
+ tmpl = jinja2.Template(client_config_tmpl, trim_blocks=True)
+ config_text = tmpl.render(snmp)
+ with open(config_file_client, 'w') as f:
+ f.write(config_text)
+
+ tmpl = jinja2.Template(daemon_config_tmpl, trim_blocks=True)
+ config_text = tmpl.render(snmp)
+ with open(config_file_daemon, 'w') as f:
+ f.write(config_text)
+
+ return None
+
+def apply(snmp):
+ if snmp is not None:
+ os.system("sudo systemctl restart snmpd.service")
+ else:
+ # SNMP is removed in the commit
+ os.system("sudo systemctl stop snmpd.service")
+ os.unlink(config_file_client)
+ os.unlink(config_file_daemon)
+
+ return None
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ sys.exit(1)