diff --git a/confluent_server/confluent/config/attributes.py b/confluent_server/confluent/config/attributes.py index 26f77732..dc5b8d40 100644 --- a/confluent_server/confluent/config/attributes.py +++ b/confluent_server/confluent/config/attributes.py @@ -605,6 +605,10 @@ node = { 'description': ('SNMPv1 community string, it is highly recommended to' 'step up to SNMPv3'), }, + 'snmp.privacyprotocol': { + 'description': 'The privacy protocol to use for SNMPv3', + 'valid_values': ('aes', 'des'), + }, # 'secret.snmplocalizedkey': { # 'description': ("SNMPv3 key localized to this node's SNMP Engine id" # 'This can be used in lieu of snmppassphrase to avoid' diff --git a/confluent_server/confluent/networking/lldp.py b/confluent_server/confluent/networking/lldp.py index 5f17ab80..e2eba7e3 100644 --- a/confluent_server/confluent/networking/lldp.py +++ b/confluent_server/confluent/networking/lldp.py @@ -255,7 +255,13 @@ def _extract_neighbor_data_b(args): args are carried as a tuple, because of eventlet convenience """ - switch, password, user, cfm, force = args[:5] + # Safely unpack args with defaults to avoid IndexError + switch = args[0] if len(args) > 0 else None + password = args[1] if len(args) > 1 else None + user = args[2] if len(args) > 2 else None + cfm = args[3] if len(args) > 3 else None + privproto = args[4] if len(args) > 4 else None + force = args[5] if len(args) > 5 else False vintage = _neighdata.get(switch, {}).get('!!vintage', 0) now = util.monotonic_time() if vintage > (now - 60) and not force: @@ -265,7 +271,7 @@ def _extract_neighbor_data_b(args): return _extract_neighbor_data_https(switch, user, password, cfm, lldpdata) except Exception as e: pass - conn = snmp.Session(switch, password, user) + conn = snmp.Session(switch, password, user, privacy_protocol=privproto) sid = None for sysid in conn.walk('1.3.6.1.2.1.1.2'): sid = str(sysid[1][6:]) @@ -364,8 +370,8 @@ def _extract_neighbor_data(args): return _extract_neighbor_data_b(args) except Exception as e: yieldexc = False - if len(args) >= 6: - yieldexc = args[5] + if len(args) >= 7: + yieldexc = args[6] if yieldexc: return e else: diff --git a/confluent_server/confluent/networking/macmap.py b/confluent_server/confluent/networking/macmap.py index 5545e09a..a96a48ee 100644 --- a/confluent_server/confluent/networking/macmap.py +++ b/confluent_server/confluent/networking/macmap.py @@ -213,14 +213,14 @@ def _fast_backend_fixup(macs, switch): else: _nodesbymac[mac] = (nodename, nummacs) -def _offload_map_switch(switch, password, user): +def _offload_map_switch(switch, password, user, privprotocol=None): if _offloader is None: _start_offloader() evtid = random.randint(0, 4294967295) while evtid in _offloadevts: evtid = random.randint(0, 4294967295) _offloadevts[evtid] = eventlet.Event() - _offloader.stdin.write(msgpack.packb((evtid, switch, password, user), + _offloader.stdin.write(msgpack.packb((evtid, switch, password, user, privprotocol), use_bin_type=True)) _offloader.stdin.flush() result = _offloadevts[evtid].wait() @@ -280,12 +280,11 @@ def _map_switch_backend(args): # fallback if ifName is empty # global _macmap - if len(args) == 4: - switch, password, user, _ = args # 4th arg is for affluent only - if not user: - user = None - else: - switch, password = args + switch = args[0] if len(args) > 0 else None + password = args[1] if len(args) > 1 else None + user = args[2] if len(args) > 2 else None + privprotocol = args[4] if len(args) > 4 else None + if not user: # make '' be treated as None user = None if switch not in noaffluent: try: @@ -298,7 +297,7 @@ def _map_switch_backend(args): except Exception as e: pass mactobridge, ifnamemap, bridgetoifmap = _offload_map_switch( - switch, password, user) + switch, password, user, privprotocol) maccounts = {} bridgetoifvalid = False for mac in mactobridge: @@ -367,9 +366,9 @@ def _map_switch_backend(args): _nodesbymac[mac] = (nodename, maccounts[ifname]) _macsbyswitch[switch] = newmacs -def _snmp_map_switch_relay(rqid, switch, password, user): +def _snmp_map_switch_relay(rqid, switch, password, user, privprotocol=None): try: - res = _snmp_map_switch(switch, password, user) + res = _snmp_map_switch(switch, password, user, privprotocol) payload = msgpack.packb((rqid,) + res, use_bin_type=True) try: sys.stdout.buffer.write(payload) @@ -391,10 +390,10 @@ def _snmp_map_switch_relay(rqid, switch, password, user): finally: sys.stdout.flush() -def _snmp_map_switch(switch, password, user): +def _snmp_map_switch(switch, password, user, privprotocol=None): haveqbridge = False mactobridge = {} - conn = snmp.Session(switch, password, user) + conn = snmp.Session(switch, password, user, privacy_protocol=privprotocol) ifnamemap = get_portnamemap(conn) for vb in conn.walk('1.3.6.1.2.1.17.7.1.2.2.1.2'): haveqbridge = True diff --git a/confluent_server/confluent/networking/netutil.py b/confluent_server/confluent/networking/netutil.py index 48b2f028..65cd8236 100644 --- a/confluent_server/confluent/networking/netutil.py +++ b/confluent_server/confluent/networking/netutil.py @@ -21,7 +21,7 @@ import confluent.collective.manager as collective def get_switchcreds(configmanager, switches): switchcfg = configmanager.get_node_attributes( switches, ('secret.hardwaremanagementuser', 'secret.snmpcommunity', - 'secret.hardwaremanagementpassword', + 'secret.hardwaremanagementpassword', 'snmp.privacyprotocol', 'collective.managercandidates'), decrypt=True) switchauth = [] for switch in switches: @@ -47,7 +47,9 @@ def get_switchcreds(configmanager, switches): 'secret.hardwaremanagementuser', {}).get('value', None) if not user: user = None - switchauth.append((switch, password, user, configmanager)) + privacy_protocol = switchparms.get( + 'snmp.privacyprotocol', {}).get('value', None) + switchauth.append((switch, password, user, configmanager, privacy_protocol)) return switchauth diff --git a/confluent_server/confluent/snmputil.py b/confluent_server/confluent/snmputil.py index ce6e3759..6f03a31b 100644 --- a/confluent_server/confluent/snmputil.py +++ b/confluent_server/confluent/snmputil.py @@ -78,7 +78,7 @@ def _get_transport(name): class Session(object): - def __init__(self, server, secret, username=None, context=None): + def __init__(self, server, secret, username=None, context=None, privacy_protocol=None): """Create a new session to interrogate a switch If username is not given, it is assumed that @@ -97,9 +97,17 @@ class Session(object): # SNMP v2c self.authdata = snmp.CommunityData(secret, mpModel=1) else: + if privacy_protocol == 'aes': + privproto = snmp.usmAesCfb128Protocol + elif privacy_protocol in ('des', None): + privproto = snmp.usmDESPrivProtocol + else: + raise exc.ConfluentException('Unsupported SNMPv3 privacy protocol ' + '{0}'.format(privacy_protocol)) self.authdata = snmp.UsmUserData( username, authKey=secret, privKey=secret, - authProtocol=snmp.usmHMACSHAAuthProtocol) + authProtocol=snmp.usmHMACSHAAuthProtocol, + privProtocol=privproto) self.eng = snmp.SnmpEngine() def walk(self, oid):