diff options
-rw-r--r-- | interface-definitions/snmp.xml | 52 | ||||
-rw-r--r-- | python/vyos/iflag.py | 38 | ||||
-rw-r--r-- | python/vyos/interface.py | 9 | ||||
-rw-r--r-- | python/vyos/ioctl.py | 34 | ||||
-rwxr-xr-x | src/conf_mode/snmp.py | 92 | ||||
-rwxr-xr-x | src/migration-scripts/snmp/0-to-1 | 56 |
6 files changed, 142 insertions, 139 deletions
diff --git a/interface-definitions/snmp.xml b/interface-definitions/snmp.xml index 1634bfb64..14aad90a0 100644 --- a/interface-definitions/snmp.xml +++ b/interface-definitions/snmp.xml @@ -307,15 +307,6 @@ </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> @@ -421,35 +412,6 @@ </leafNode> </children> </tagNode> - <node name="tsm"> - <properties> - <help>Specifies that SNMPv3 uses the Transport Security Model (TSM)</help> - </properties> - <children> - <leafNode name="local-key"> - <properties> - <help>Fingerprint of a TSM server certificate</help> - <constraint> - <regex>^[0-9A-F]{2}(:[0-9A-F]{2}){19}$</regex> - </constraint> - <constraintErrorMessage>Value can be finger print key or filename in /config/snmp/tls/certs</constraintErrorMessage> - </properties> - </leafNode> - <leafNode name="port"> - <properties> - <help>Defines the port used 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> @@ -503,15 +465,6 @@ </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> @@ -582,11 +535,6 @@ </constraint> </properties> </leafNode> - <leafNode name="tsm-key"> - <properties> - <help>Specifies finger print or file name of TSM certificate</help> - </properties> - </leafNode> </children> </node> </children> diff --git a/python/vyos/iflag.py b/python/vyos/iflag.py new file mode 100644 index 000000000..7ff8e5623 --- /dev/null +++ b/python/vyos/iflag.py @@ -0,0 +1,38 @@ +# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +from enum import Enum, unique, IntEnum + + +class IFlag(IntEnum): + """ net/if.h interface flags """ + + IFF_UP = 0x1 #: Interface up/down status + IFF_BROADCAST = 0x2 #: Broadcast address valid + IFF_DEBUG = 0x4, #: Debugging + IFF_LOOPBACK = 0x8 #: Is loopback network + IFF_POINTOPOINT = 0x10 #: Is point-to-point link + IFF_NOTRAILERS = 0x20 #: Avoid use of trailers + IFF_RUNNING = 0x40 #: Resources allocated + IFF_NOARP = 0x80 #: No address resolution protocol + IFF_PROMISC = 0x100 #: Promiscuous mode + IFF_ALLMULTI = 0x200 #: Receive all multicast + IFF_MASTER = 0x400 #: Load balancer master + IFF_SLAVE = 0x800 #: Load balancer slave + IFF_MULTICAST = 0x1000 #: Supports multicast + IFF_PORTSEL = 0x2000 #: Media type adjustable + IFF_AUTOMEDIA = 0x4000 #: Automatic media type enabled + IFF_DYNAMIC = 0x8000 #: Is a dial-up device with dynamic address + diff --git a/python/vyos/interface.py b/python/vyos/interface.py index 1aae6db60..cc726e5cc 100644 --- a/python/vyos/interface.py +++ b/python/vyos/interface.py @@ -16,6 +16,8 @@ import vyos from vyos.config import Config import vyos.interfaces +import vyos.ioctl +from vyos.iflag import IFlag import re import json @@ -33,11 +35,18 @@ class Interface(): intf = None intf_type = None valid = False + flags = None def __init__(self,intf): self.intf = intf self.intf_type = vyos.interfaces.get_type_of_interface(self.intf) self.valid = (self.intf in vyos.interfaces.list_interfaces()) + if (self.valid): + self.flags = vyos.ioctl.get_interface_flags(intf) + + def up(self): + """ return whether interface is up or not """ + return self.flags & IFlag.IFF_UP def print_interface(self): diff --git a/python/vyos/ioctl.py b/python/vyos/ioctl.py new file mode 100644 index 000000000..e57d261e4 --- /dev/null +++ b/python/vyos/ioctl.py @@ -0,0 +1,34 @@ +# Copyright 2019 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see <http://www.gnu.org/licenses/>. + +import os +import fcntl, struct, sys +from socket import * + +SIOCGIFFLAGS = 0x8913 + +def get_terminal_size(): + """ pull the terminal size """ + """ rows,cols = vyos.ioctl.get_terminal_size() """ + columns, rows = os.get_terminal_size(0) + return (rows,columns) + +def get_interface_flags(intf): + """ Pull the SIOCGIFFLAGS """ + nullif = '\0'*256 + sock = socket(AF_INET, SOCK_DGRAM) + raw = fcntl.ioctl(sock.fileno(), SIOCGIFFLAGS, intf + nullif) + flags, = struct.unpack('H', raw[16:18]) + return flags diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py index cba1fe319..74c17fff6 100755 --- a/src/conf_mode/snmp.py +++ b/src/conf_mode/snmp.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-2019 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 @@ -13,8 +13,6 @@ # # 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 @@ -78,7 +76,7 @@ createUser {{ u.name }} {%- elif u.authPassword %} createUser {{ u.name }} {{ u.authProtocol | upper }} "{{ u.authPassword }}" {{ u.privProtocol | upper }} {{ u.privPassword }} {%- else %} -usmUser 1 3 {{ u.engineID }} "{{ u.name }}" "{{ u.name }}" NULL {{ u.authOID }} {{ u.authMasterKey }} {{ u.privOID }} {{ u.privMasterKey }} 0x +usmUser 1 3 {{ v3_engineid }} "{{ u.name }}" "{{ u.name }}" NULL {{ u.authOID }} {{ u.authMasterKey }} {{ u.privOID }} {{ u.privMasterKey }} 0x {%- endif %} {%- endfor %} @@ -117,9 +115,6 @@ monitor -r 10 -e linkDownTrap "Generate linkDown" ifOperStatus == 2 ######################## # configurable section # ######################## -{% if v3_tsm_key %} -[snmp] localCert {{ v3_tsm_key }} -{%- endif %} # Default system description is VyOS version sysDescr VyOS {{ version }} @@ -130,7 +125,7 @@ SysDescr {{ description }} {%- endif %} # Listen -agentaddress unix:/run/snmpd.socket{% if listen_on %}{% for li in listen_on %},{{ li }}{% endfor %}{% else %},udp:161,udp6:161{% endif %}{% if v3_tsm_key %},tlstcp:{{ v3_tsm_port }},dtlsudp::{{ v3_tsm_port }}{% endif %} +agentaddress unix:/run/snmpd.socket{% if listen_on %}{% for li in listen_on %},{{ li }}{% endfor %}{% else %},udp:161,udp6:161{% endif %} # SNMP communities {%- for c in communities %} @@ -192,18 +187,16 @@ view {{ v.name }} included .{{ oid.oid }} # context sec.model sec.level match read write notif {%- for g in v3_groups %} access {{ g.name }} "" usm {{ g.seclevel }} exact {{ g.view }} {% if g.mode == 'ro' %}none{% else %}{{ g.view }}{% endif %} none -access {{ g.name }} "" tsm {{ g.seclevel }} exact {{ g.view }} {% if g.mode == 'ro' %}none{% else %}{{ g.view }}{% endif %} none {%- endfor %} # trap-target {%- for t in v3_traps %} -trapsess -v 3 {{ '-Ci' if t.type == 'inform' }} -e {{ t.engineID }} -u {{ t.secName }} -l {{ t.secLevel }} -a {{ t.authProtocol }} {% if t.authPassword %}-A {{ t.authPassword }}{% elif t.authMasterKey %}-3m {{ t.authMasterKey }}{% endif %} -x {{ t.privProtocol }} {% if t.privPassword %}-X {{ t.privPassword }}{% elif t.privMasterKey %}-3M {{ t.privMasterKey }}{% endif %} {{ t.ipProto }}:{{ t.ipAddr }}:{{ t.ipPort }} +trapsess -v 3 {{ '-Ci' if t.type == 'inform' }} -e {{ v3_engineid }} -u {{ t.secName }} -l {{ t.secLevel }} -a {{ t.authProtocol }} {% if t.authPassword %}-A {{ t.authPassword }}{% elif t.authMasterKey %}-3m {{ t.authMasterKey }}{% endif %} -x {{ t.privProtocol }} {% if t.privPassword %}-X {{ t.privPassword }}{% elif t.privMasterKey %}-3M {{ t.privMasterKey }}{% endif %} {{ t.ipProto }}:{{ t.ipAddr }}:{{ t.ipPort }} {%- endfor %} # group {%- for u in v3_users %} group {{ u.group }} usm {{ u.name }} -group {{ u.group }} tsm {{ u.name }} {% endfor %} {%- endif %} @@ -244,8 +237,6 @@ default_config_data = { 'v3_engineid': '', 'v3_groups': [], 'v3_traps': [], - 'v3_tsm_key': '', - 'v3_tsm_port': '10161', 'v3_users': [], 'v3_views': [], 'script_ext': {} @@ -375,15 +366,11 @@ def get_config(): else: snmp['v3_enabled'] = True - # # 'set service snmp v3 engineid' - # if conf.exists('v3 engineid'): snmp['v3_engineid'] = conf.return_value('v3 engineid') - # # 'set service snmp v3 group' - # if conf.exists('v3 group'): for group in conf.list_nodes('v3 group'): v3_group = { @@ -404,14 +391,11 @@ def get_config(): snmp['v3_groups'].append(v3_group) - # # 'set service snmp v3 trap-target' - # if conf.exists('v3 trap-target'): for trap in conf.list_nodes('v3 trap-target'): trap_cfg = { 'ipAddr': trap, - 'engineID': '', 'secName': '', 'authProtocol': 'md5', 'authPassword': '', @@ -425,11 +409,6 @@ def get_config(): 'secLevel': 'noAuthNoPriv' } - if conf.exists('v3 trap-target {0} engineid'.format(trap)): - # Set the context engineID used for SNMPv3 REQUEST messages scopedPdu. - # If not specified, this will default to the authoritative engineID. - trap_cfg['engineID'] = conf.return_value('v3 trap-target {0} engineid'.format(trap)) - if conf.exists('v3 trap-target {0} user'.format(trap)): # Set the securityName used for authenticated SNMPv3 messages. trap_cfg['secName'] = conf.return_value('v3 trap-target {0} user'.format(trap)) @@ -483,19 +462,7 @@ def get_config(): snmp['v3_traps'].append(trap_cfg) - # - # 'set service snmp v3 tsm' - # - if conf.exists('v3 tsm'): - if conf.exists('v3 tsm local-key'): - snmp['v3_tsm_key'] = conf.return_value('v3 tsm local-key') - - if conf.exists('v3 tsm port'): - snmp['v3_tsm_port'] = conf.return_value('v3 tsm port') - - # # 'set service snmp v3 user' - # if conf.exists('v3 user'): for user in conf.list_nodes('v3 user'): user_cfg = { @@ -504,19 +471,15 @@ def get_config(): 'authPassword': '', 'authProtocol': 'md5', 'authOID': 'none', - 'engineID': '', 'group': '', 'mode': 'ro', 'privMasterKey': '', 'privPassword': '', 'privOID': '', - 'privTsmKey': '', 'privProtocol': 'des' } - # # v3 user {0} auth - # if conf.exists('v3 user {0} auth encrypted-key'.format(user)): user_cfg['authMasterKey'] = conf.return_value('v3 user {0} auth encrypted-key'.format(user)) @@ -532,36 +495,21 @@ def get_config(): user_cfg['authProtocol'] = type user_cfg['authOID'] = OIDs[type] - # - # v3 user {0} engineid - # - if conf.exists('v3 user {0} engineid'.format(user)): - user_cfg['engineID'] = conf.return_value('v3 user {0} engineid'.format(user)) - - # # v3 user {0} group - # if conf.exists('v3 user {0} group'.format(user)): user_cfg['group'] = conf.return_value('v3 user {0} group'.format(user)) - # # v3 user {0} mode - # if conf.exists('v3 user {0} mode'.format(user)): user_cfg['mode'] = conf.return_value('v3 user {0} mode'.format(user)) - # # v3 user {0} privacy - # if conf.exists('v3 user {0} privacy encrypted-key'.format(user)): user_cfg['privMasterKey'] = conf.return_value('v3 user {0} privacy encrypted-key'.format(user)) if conf.exists('v3 user {0} privacy plaintext-key'.format(user)): user_cfg['privPassword'] = conf.return_value('v3 user {0} privacy plaintext-key'.format(user)) - if conf.exists('v3 user {0} privacy tsm-key'.format(user)): - user_cfg['privTsmKey'] = conf.return_value('v3 user {0} privacy tsm-key'.format(user)) - # load default value type = user_cfg['privProtocol'] if conf.exists('v3 user {0} privacy type'.format(user)): @@ -573,9 +521,7 @@ def get_config(): snmp['v3_users'].append(user_cfg) - # # 'set service snmp v3 view' - # if conf.exists('v3 view'): for view in conf.list_nodes('v3 view'): view_cfg = { @@ -601,7 +547,7 @@ def verify(snmp): if snmp['script_ext']: for ext in snmp['script_ext']: if not os.path.isfile(snmp['script_ext'][ext]): - print ("WARNING: script: " + snmp['script_ext'][ext] + " doesn\'t exist") + print ("WARNING: script: " + snmp['script_ext'][ext] + " doesn\'t exist") else: os.chmod(snmp['script_ext'][ext], 0o555) @@ -609,14 +555,6 @@ def verify(snmp): if not snmp['v3_enabled']: return None - tsmKeyPattern = re.compile('^[0-9A-F]{2}(:[0-9A-F]{2}){19}$', re.IGNORECASE) - - if snmp['v3_tsm_key']: - if not tsmKeyPattern.match(snmp['v3_tsm_key']): - if not os.path.isfile('/etc/snmp/tls/certs/' + snmp['v3_tsm_key']): - if not os.path.isfile('/config/snmp/tls/certs/' + snmp['v3_tsm_key']): - raise ConfigError('TSM key must be fingerprint or filename in "/config/snmp/tls/certs/" folder') - for listen in snmp['listen_address']: addr = listen[0] port = listen[1] @@ -683,13 +621,6 @@ def verify(snmp): if not 'privPassword' and 'privMasterKey' in trap.keys(): raise ConfigError('v3 trap: "user" must be specified') - if 'type' in trap.keys(): - if trap['type'] == 'trap' and trap['engineID'] == '': - raise ConfigError('must specify engineid if type is "trap"') - else: - raise ConfigError('"type" must be specified') - - if 'v3_users' in snmp.keys(): for user in snmp['v3_users']: # @@ -721,21 +652,9 @@ def verify(snmp): if user['privPassword'] == '' and user['privMasterKey'] == '': raise ConfigError('Must specify encrypted-key or plaintext-key for user privacy') - if user['privMasterKey'] and user['engineID'] == '': - raise ConfigError('Can not have "encrypted-key" without engineid') - - if user['authPassword'] == '' and user['authMasterKey'] == '' and user['privTsmKey'] == '': - raise ConfigError('Must specify auth or tsm-key for user auth') - if user['mode'] == '': raise ConfigError('Must specify user mode ro/rw') - if user['privTsmKey']: - if not tsmKeyPattern.match(snmp['v3_tsm_key']): - if not os.path.isfile('/etc/snmp/tls/certs/' + snmp['v3_tsm_key']): - if not os.path.isfile('/config/snmp/tls/certs/' + snmp['v3_tsm_key']): - raise ConfigError('User TSM key must be fingerprint or filename in "/config/snmp/tls/certs/" folder') - if 'v3_views' in snmp.keys(): for view in snmp['v3_views']: if not view['oids']: @@ -858,7 +777,6 @@ def apply(snmp): # Now update the running configuration # # Currently when executing os.system() the environment does not have the vyos_libexec_dir variable set, see T685 - os.system('vyos_libexec_dir=/usr/libexec/vyos /opt/vyatta/sbin/my_set service snmp v3 user "{0}" engineid {1} > /dev/null'.format(cfg['user'], engineID)) os.system('vyos_libexec_dir=/usr/libexec/vyos /opt/vyatta/sbin/my_set service snmp v3 user "{0}" auth encrypted-key {1} > /dev/null'.format(cfg['user'], cfg['auth_pw'])) os.system('vyos_libexec_dir=/usr/libexec/vyos /opt/vyatta/sbin/my_set service snmp v3 user "{0}" privacy encrypted-key {1} > /dev/null'.format(cfg['user'], cfg['priv_pw'])) os.system('vyos_libexec_dir=/usr/libexec/vyos /opt/vyatta/sbin/my_delete service snmp v3 user "{0}" auth plaintext-key > /dev/null'.format(cfg['user'])) diff --git a/src/migration-scripts/snmp/0-to-1 b/src/migration-scripts/snmp/0-to-1 new file mode 100755 index 000000000..a836f7011 --- /dev/null +++ b/src/migration-scripts/snmp/0-to-1 @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 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 +from vyos.configtree import ConfigTree + +if (len(sys.argv) < 1): + print("Must specify file name!") + sys.exit(1) + +file_name = sys.argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +config = ConfigTree(config_file) +config_base = ['service', 'snmp', 'v3'] + +if not config.exists(config_base): + # Nothing to do + sys.exit(0) +else: + # we no longer support a per trap target engine ID (https://phabricator.vyos.net/T818) + if config.exists(config_base + ['v3', 'trap-target']): + for target in config.list_nodes(config_base + ['v3', 'trap-target']): + config.delete(config_base + ['v3', 'trap-target', target, 'engineid']) + + # we no longer support a per user engine ID (https://phabricator.vyos.net/T818) + if config.exists(config_base + ['v3', 'user']): + for user in config.list_nodes(config_base + ['v3', 'user']): + config.delete(config_base + ['v3', 'user', user, 'engineid']) + + # we drop TSM support as there seem to be no users and this code is untested + # https://phabricator.vyos.net/T1769 + if config.exists(config_base + ['v3', 'tsm']): + config.delete(config_base + ['v3', 'tsm']) + + try: + with open(file_name, 'w') as f: + f.write(config.to_string()) + except OSError as e: + print("Failed to save the modified config: {}".format(e)) + sys.exit(1) |