def n_Debug(self, cbCtx, node): if debug: if len(node) > 2: f = node[2].attr else: f = node[1].attr debug.setLogger(debug.Debug(*f.split(',')))
def check_parser(): """ TBD :return: """ parser = argparse.ArgumentParser() parser.add_argument("--debug", help="Enable Debug Mode") args = parser.parse_args() if args.debug: debug.setLogger(debug.Debug('all'))
def __init__(self, additional_mib_search_paths=[], additional_mib_load_modules=[], debug=False, load_texts=True): if debug: # Enable Debugging pysnmp_debug.setLogger(pysnmp_debug.Debug('all')) # The pysnmp libraries will compile MIB files into python files, and # store them on the system in a cache directory under ~/.pysnmp/mibs # It only needs to do this once as it encounters new MIBs, and not # every time you run this program. Order of the loading matters. mib_modules = additional_mib_load_modules + DEFAULT_MIB_LOAD_MODULES mib_sources = additional_mib_search_paths + DEFAULT_MIB_SEARCH_PATHS self.mibBuilder = builder.MibBuilder() self.mibBuilder.loadTexts = load_texts # Loads mib text descriptions compiler.addMibCompiler(self.mibBuilder, sources=mib_sources) self.mibBuilder.loadModules(*mib_modules) self.mibView = view.MibViewController(self.mibBuilder)
def logger(): if not path.exists(CONFFILE): CONF = {} else: CONF = eval(open(CONFFILE).read()) fp = path.join(CONF['LOG_DIR'] if 'LOG_DIR' in CONF else '', SNMP_LOGFILE) a = 'a' if not path.exists(fp): a = 'w' stderr = open(fp, a) stdout = stderr p = debug.Printer(handler=logging.StreamHandler(stream=stderr)) debug.setLogger(debug.Debug( DEBUG, printer=p)) #'msgproc', 'dsp', 'io', 'app', flagIns)) global PIDFILE PIDFILE = path.join(getcwd(), CONF['PID_DIR'] if 'PID_DIR' in CONF else 'pids', PIDFILE)
import pyasn1 sys.stderr.write("""\ SNMP Simulator version %s, written by Ilya Etingof <*****@*****.**> Using foundation libraries: pysmi %s, pysnmp %s, pyasn1 %s. Python interpreter: %s Software documentation and support at http://snmplabs.com/snmpsim %s """ % (snmpsim.__version__, hasattr(pysmi, '__version__') and pysmi.__version__ or 'unknown', hasattr(pysnmp, '__version__') and pysnmp.__version__ or 'unknown', hasattr(pyasn1, '__version__') and pyasn1.__version__ or 'unknown', sys.version, helpMessage)) sys.exit(-1) elif opt[0] in ('--debug', '--debug-snmp'): pysnmp_debug.setLogger( pysnmp_debug.Debug(*opt[1].split(','), **dict(loggerName='snmprec.pysnmp'))) elif opt[0] == '--debug-asn1': pyasn1_debug.setLogger( pyasn1_debug.Debug(*opt[1].split(','), **dict(loggerName='snmprec.pyasn1'))) elif opt[0] == '--logging-method': loggingMethod = opt[1].split(':') elif opt[0] == '--log-level': loggingLevel = opt[1] elif opt[0] == '--quiet': log.setLogger('snmprec', 'null', force=True) elif opt[0] == '--v1': snmpVersion = 0 elif opt[0] == '--v2c': snmpVersion = 1 elif opt[0] == '--v3':
def n_Dump(self, cbCtx, node): if debug: debug.setLogger(debug.Debug('io'))
sys.stderr.write("""\ SNMP Simulator version %s, written by Ilya Etingof <*****@*****.**> Using foundation libraries: pysmi %s, pysnmp %s, pyasn1 %s. Python interpreter: %s Software documentation and support at http://snmplabs.com/snmpsim %s """ % (snmpsim.__version__, getattr(pysmi, '__version__', 'unknown'), getattr(pysnmp, '__version__', 'unknown'), getattr(pyasn1, '__version__', 'unknown'), sys.version, helpMessage)) sys.exit(-1) if opt[0] == '--debug': debug.setLogger(debug.Debug(*opt[1].split(','))) if opt[0] == '--quiet': verboseFlag = False if opt[0] == '--pysnmp-mib-dir': mibDirs.append(opt[1]) if opt[0] == '--mib-module': modNames.append(opt[1]) # obsolete begin if opt[0] == '--start-oid': startOID = univ.ObjectIdentifier(opt[1]) if opt[0] == '--stop-oid':
sys.exit(-1) if opt[0] == '-v' or opt[0] == '--version': import snmpfwd, pysnmp, pyasn1 sys.stderr.write("""\ SNMP Proxy Forwarder version %s, written by Ilya Etingof <*****@*****.**> Using foundation libraries: pysnmp %s, pyasn1 %s. Python interpreter: %s Software documentation and support at http://snmpfwd.sf.net %s """ % (snmpfwd.__version__, hasattr(pysnmp, '__version__') and pysnmp.__version__ or 'unknown', hasattr(pyasn1, '__version__') and pyasn1.__version__ or 'unknown', sys.version, helpMessage)) sys.exit(-1) elif opt[0] == '--debug-snmp': pysnmp_debug.setLogger( pysnmp_debug.Debug(*opt[1].split(','), **dict(loggerName=programName + '.pysnmp'))) elif opt[0] == '--debug-asn1': pyasn1_debug.setLogger( pyasn1_debug.Debug(*opt[1].split(','), **dict(loggerName=programName + '.pyasn1'))) elif opt[0] == '--daemonize': foregroundFlag = False elif opt[0] == '--process-user': procUser = opt[1] elif opt[0] == '--process-group': procGroup = opt[1] elif opt[0] == '--pid-file': pidFile = opt[1] elif opt[0] == '--logging-method': try: log.setLogger(programName, *opt[1].split(':'), **dict(force=True))
def main(): class CommandResponder(cmdrsp.CommandResponderBase): pduTypes = (rfc1905.SetRequestPDU.tagSet, rfc1905.GetRequestPDU.tagSet, rfc1905.GetNextRequestPDU.tagSet, rfc1905.GetBulkRequestPDU.tagSet) def handleMgmtOperation(self, snmpEngine, stateReference, contextName, pdu, acInfo): trunkReq = gCurrentRequestContext.copy() trunkReq['snmp-pdu'] = pdu pluginIdList = trunkReq['plugins-list'] logCtx = LogString(trunkReq) reqCtx = {} for pluginNum, pluginId in enumerate(pluginIdList): st, pdu = pluginManager.processCommandRequest( pluginId, snmpEngine, pdu, trunkReq, reqCtx) if st == status.BREAK: log.debug('plugin %s inhibits other plugins' % pluginId, ctx=logCtx) pluginIdList = pluginIdList[:pluginNum] break elif st == status.DROP: log.debug( 'received SNMP message, plugin %s muted request' % pluginId, ctx=logCtx) self.releaseStateInformation(stateReference) return elif st == status.RESPOND: log.debug( 'received SNMP message, plugin %s forced immediate response' % pluginId, ctx=logCtx) try: self.sendPdu(snmpEngine, stateReference, pdu) except PySnmpError: log.error('failure sending SNMP response', ctx=logCtx) else: self.releaseStateInformation(stateReference) return # pass query to trunk trunkIdList = trunkReq['trunk-id-list'] if trunkIdList is None: log.error('no route configured', ctx=logCtx) self.releaseStateInformation(stateReference) return for trunkId in trunkIdList: cbCtx = pluginIdList, trunkId, trunkReq, snmpEngine, stateReference, reqCtx try: msgId = trunkingManager.sendReq(trunkId, trunkReq, self.trunkCbFun, cbCtx) except SnmpfwdError: log.error( 'received SNMP message, message not sent to trunk "%s"' % sys.exc_info()[1], ctx=logCtx) return log.debug( 'received SNMP message, forwarded as trunk message #%s' % msgId, ctx=logCtx) def trunkCbFun(self, msgId, trunkRsp, cbCtx): pluginIdList, trunkId, trunkReq, snmpEngine, stateReference, reqCtx = cbCtx for key in tuple(trunkRsp): if key != 'callflow-id': trunkRsp['client-' + key] = trunkRsp[key] del trunkRsp[key] trunkRsp['callflow-id'] = trunkReq['callflow-id'] logCtx = LogString(trunkRsp) if trunkRsp['client-error-indication']: log.info( 'received trunk message #%s, remote end reported error-indication "%s", NOT responding' % (msgId, trunkRsp['client-error-indication']), ctx=logCtx) elif 'client-snmp-pdu' not in trunkRsp: log.info( 'received trunk message #%s, remote end does not send SNMP PDU, NOT responding' % msgId, ctx=logCtx) else: pdu = trunkRsp['client-snmp-pdu'] for pluginId in pluginIdList: st, pdu = pluginManager.processCommandResponse( pluginId, snmpEngine, pdu, trunkReq, reqCtx) if st == status.BREAK: log.debug('plugin %s inhibits other plugins' % pluginId, ctx=logCtx) break elif st == status.DROP: log.debug('plugin %s muted response' % pluginId, ctx=logCtx) self.releaseStateInformation(stateReference) return try: self.sendPdu(snmpEngine, stateReference, pdu) except PySnmpError: log.error('failure sending SNMP response', ctx=logCtx) else: log.debug( 'received trunk message #%s, forwarded as SNMP message' % msgId, ctx=logCtx) self.releaseStateInformation(stateReference) # # SNMPv3 NotificationReceiver implementation # class NotificationReceiver(ntfrcv.NotificationReceiver): pduTypes = (rfc1157.TrapPDU.tagSet, rfc1905.SNMPv2TrapPDU.tagSet) def processPdu(self, snmpEngine, messageProcessingModel, securityModel, securityName, securityLevel, contextEngineId, contextName, pduVersion, pdu, maxSizeResponseScopedPDU, stateReference): trunkReq = gCurrentRequestContext.copy() if messageProcessingModel == 0: pdu = rfc2576.v1ToV2(pdu) # TODO: why this is not automatic? v2c.apiTrapPDU.setDefaults(pdu) trunkReq['snmp-pdu'] = pdu pluginIdList = trunkReq['plugins-list'] logCtx = LogString(trunkReq) reqCtx = {} for pluginNum, pluginId in enumerate(pluginIdList): st, pdu = pluginManager.processNotificationRequest( pluginId, snmpEngine, pdu, trunkReq, reqCtx) if st == status.BREAK: log.debug('plugin %s inhibits other plugins' % pluginId, ctx=logCtx) pluginIdList = pluginIdList[:pluginNum] break elif st == status.DROP: log.debug('plugin %s muted request' % pluginId, ctx=logCtx) return elif st == status.RESPOND: log.debug('plugin %s NOT forced immediate response' % pluginId, ctx=logCtx) # TODO: implement immediate response for confirmed-class PDU return # pass query to trunk trunkIdList = trunkReq['trunk-id-list'] if trunkIdList is None: log.error('no route configured', ctx=logCtx) return for trunkId in trunkIdList: # TODO: pass messageProcessingModel to respond cbCtx = pluginIdList, trunkId, trunkReq, snmpEngine, stateReference, reqCtx try: msgId = trunkingManager.sendReq(trunkId, trunkReq, self.trunkCbFun, cbCtx) except SnmpfwdError: log.error( 'received SNMP message, message not sent to trunk "%s" %s' % (trunkId, sys.exc_info()[1]), ctx=logCtx) return log.debug( 'received SNMP message, forwarded as trunk message #%s' % msgId, ctx=logCtx) def trunkCbFun(self, msgId, trunkRsp, cbCtx): pluginIdList, trunkId, trunkReq, snmpEngine, stateReference, reqCtx = cbCtx for key in tuple(trunkRsp): if key != 'callflow-id': trunkRsp['client-' + key] = trunkRsp[key] del trunkRsp[key] trunkRsp['callflow-id'] = trunkReq['callflow-id'] logCtx = LazyLogString(trunkReq, trunkRsp) if trunkRsp['client-error-indication']: log.info( 'received trunk message #%s, remote end reported error-indication "%s", NOT responding' % (msgId, trunkRsp['client-error-indication']), ctx=logCtx) else: if 'client-snmp-pdu' not in trunkRsp: log.debug( 'received trunk message #%s -- unconfirmed SNMP message' % msgId, ctx=logCtx) return pdu = trunkRsp['client-snmp-pdu'] for pluginId in pluginIdList: st, pdu = pluginManager.processNotificationResponse( pluginId, snmpEngine, pdu, trunkReq, reqCtx) if st == status.BREAK: log.debug('plugin %s inhibits other plugins' % pluginId, ctx=logCtx) break elif st == status.DROP: log.debug( 'received trunk message #%s, plugin %s muted response' % (msgId, pluginId), ctx=logCtx) return log.debug( 'received trunk message #%s, forwarded as SNMP message' % msgId, ctx=logCtx) # TODO: implement response part # # Agent-side API complies with SMIv2 # if messageProcessingModel == 0: # PDU = rfc2576.v2ToV1(PDU, origPdu) # # statusInformation = {} # # # 3.4.3 # try: # snmpEngine.msgAndPduDsp.returnResponsePdu( # snmpEngine, messageProcessingModel, securityModel, # securityName, securityLevel, contextEngineId, # contextName, pduVersion, rspPDU, maxSizeResponseScopedPDU, # stateReference, statusInformation) # # except error.StatusInformation: # log.error('processPdu: stateReference %s, statusInformation %s' % (stateReference, sys.exc_info()[1])) class LogString(LazyLogString): GROUPINGS = [ ['callflow-id'], [ 'snmp-engine-id', 'snmp-transport-domain', 'snmp-bind-address', 'snmp-bind-port', 'snmp-security-model', 'snmp-security-level', 'snmp-security-name', 'snmp-credentials-id' ], ['snmp-context-engine-id', 'snmp-context-name', 'snmp-context-id'], ['snmp-pdu', 'snmp-content-id'], ['snmp-peer-address', 'snmp-peer-port', 'snmp-peer-id'], ['trunk-id'], ['client-snmp-pdu'], ] FORMATTERS = { 'client-snmp-pdu': LazyLogString.prettyVarBinds, 'snmp-pdu': LazyLogString.prettyVarBinds, } def securityAuditObserver(snmpEngine, execpoint, variables, cbCtx): securityModel = variables.get('securityModel', 0) logMsg = 'SNMPv%s auth failure' % securityModel logMsg += ' at %s:%s' % variables['transportAddress'].getLocalAddress() logMsg += ' from %s:%s' % variables['transportAddress'] statusInformation = variables.get('statusInformation', {}) if securityModel in (1, 2): logMsg += ' using snmp-community-name "%s"' % statusInformation.get( 'communityName', '?') elif securityModel == 3: logMsg += ' using snmp-usm-user "%s"' % statusInformation.get( 'msgUserName', '?') try: logMsg += ': %s' % statusInformation['errorIndication'] except KeyError: pass log.error(logMsg) def requestObserver(snmpEngine, execpoint, variables, cbCtx): trunkReq = { 'callflow-id': '%10.10x' % random.randint(0, 0xffffffffff), 'snmp-engine-id': snmpEngine.snmpEngineID, 'snmp-transport-domain': variables['transportDomain'], 'snmp-peer-address': variables['transportAddress'][0], 'snmp-peer-port': variables['transportAddress'][1], 'snmp-bind-address': variables['transportAddress'].getLocalAddress()[0], 'snmp-bind-port': variables['transportAddress'].getLocalAddress()[1], 'snmp-security-model': variables['securityModel'], 'snmp-security-level': variables['securityLevel'], 'snmp-security-name': variables['securityName'], 'snmp-context-engine-id': variables['contextEngineId'], 'snmp-context-name': variables['contextName'], } trunkReq['snmp-credentials-id'] = macro.expandMacro( credIdMap.get( (str(snmpEngine.snmpEngineID), variables['transportDomain'], variables['securityModel'], variables['securityLevel'], str(variables['securityName']))), trunkReq) k = '#'.join([ str(x) for x in (variables['contextEngineId'], variables['contextName']) ]) for x, y in contextIdList: if y.match(k): trunkReq['snmp-context-id'] = macro.expandMacro(x, trunkReq) break else: trunkReq['snmp-context-id'] = None addr = '%s:%s#%s:%s' % ( variables['transportAddress'][0], variables['transportAddress'][1], variables['transportAddress'].getLocalAddress()[0], variables['transportAddress'].getLocalAddress()[1]) for pat, peerId in peerIdMap.get(str(variables['transportDomain']), ()): if pat.match(addr): trunkReq['snmp-peer-id'] = macro.expandMacro(peerId, trunkReq) break else: trunkReq['snmp-peer-id'] = None pdu = variables['pdu'] if pdu.tagSet == v1.TrapPDU.tagSet: pdu = rfc2576.v1ToV2(pdu) v2c.apiTrapPDU.setDefaults(pdu) k = '#'.join([ snmpPduTypesMap.get(variables['pdu'].tagSet, '?'), '|'.join([str(x[0]) for x in v2c.apiTrapPDU.getVarBinds(pdu)]) ]) for x, y in contentIdList: if y.match(k): trunkReq['snmp-content-id'] = macro.expandMacro(x, trunkReq) break else: trunkReq['snmp-content-id'] = None trunkReq['plugins-list'] = pluginIdMap.get( (trunkReq['snmp-credentials-id'], trunkReq['snmp-context-id'], trunkReq['snmp-peer-id'], trunkReq['snmp-content-id']), []) trunkReq['trunk-id-list'] = trunkIdMap.get( (trunkReq['snmp-credentials-id'], trunkReq['snmp-context-id'], trunkReq['snmp-peer-id'], trunkReq['snmp-content-id'])) cbCtx.clear() cbCtx.update(trunkReq) # # main script starts here # helpMessage = """\ Usage: %s [--help] [--version ] [--debug-snmp=<%s>] [--debug-asn1=<%s>] [--daemonize] [--process-user=<uname>] [--process-group=<gname>] [--pid-file=<file>] [--logging-method=<%s[:args>]>] [--log-level=<%s>] [--config-file=<file>]""" % (sys.argv[0], '|'.join([ x for x in pysnmp_debug.flagMap.keys() if x != 'mibview' ]), '|'.join([x for x in pyasn1_debug.flagMap.keys()]), '|'.join( log.methodsMap.keys()), '|'.join(log.levelsMap)) try: opts, params = getopt.getopt(sys.argv[1:], 'hv', [ 'help', 'version', 'debug=', 'debug-snmp=', 'debug-asn1=', 'daemonize', 'process-user='******'process-group=', 'pid-file=', 'logging-method=', 'log-level=', 'config-file=' ]) except Exception: sys.stderr.write('ERROR: %s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage)) return if params: sys.stderr.write('ERROR: extra arguments supplied %s\r\n%s\r\n' % (params, helpMessage)) return pidFile = '' cfgFile = CONFIG_FILE foregroundFlag = True procUser = procGroup = None loggingMethod = ['stderr'] loggingLevel = None for opt in opts: if opt[0] == '-h' or opt[0] == '--help': sys.stderr.write("""\ Synopsis: SNMP Proxy Forwarder: server part. Receives SNMP requests at one or many built-in SNMP Agents and routes them to encrypted trunks established with Forwarder's Manager part(s) running elsewhere. Can implement complex routing logic through analyzing parts of SNMP messages and matching them against proxying rules. Documentation: http://snmpfwd.sourceforge.io/ %s """ % helpMessage) return if opt[0] == '-v' or opt[0] == '--version': import snmpfwd import pysnmp import pyasn1 sys.stderr.write("""\ SNMP Proxy Forwarder version %s, written by Ilya Etingof <*****@*****.**> Using foundation libraries: pysnmp %s, pyasn1 %s. Python interpreter: %s Software documentation and support at https://github.com/etingof/snmpfwd %s """ % (snmpfwd.__version__, hasattr(pysnmp, '__version__') and pysnmp.__version__ or 'unknown', hasattr(pyasn1, '__version__') and pyasn1.__version__ or 'unknown', sys.version, helpMessage)) return elif opt[0] == '--debug-snmp': pysnmp_debug.setLogger( pysnmp_debug.Debug(*opt[1].split(','), **dict(loggerName=PROGRAM_NAME + '.pysnmp'))) elif opt[0] == '--debug-asn1': pyasn1_debug.setLogger( pyasn1_debug.Debug(*opt[1].split(','), **dict(loggerName=PROGRAM_NAME + '.pyasn1'))) elif opt[0] == '--daemonize': foregroundFlag = False elif opt[0] == '--process-user': procUser = opt[1] elif opt[0] == '--process-group': procGroup = opt[1] elif opt[0] == '--pid-file': pidFile = opt[1] elif opt[0] == '--logging-method': loggingMethod = opt[1].split(':') elif opt[0] == '--log-level': loggingLevel = opt[1] elif opt[0] == '--config-file': cfgFile = opt[1] try: log.setLogger(PROGRAM_NAME, *loggingMethod, **dict(force=True)) if loggingLevel: log.setLevel(loggingLevel) except SnmpfwdError: sys.stderr.write('%s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage)) return try: cfgTree = cparser.Config().load(cfgFile) except SnmpfwdError: log.error('configuration parsing error: %s' % sys.exc_info()[1]) return if cfgTree.getAttrValue('program-name', '', default=None) != PROGRAM_NAME: log.error('config file %s does not match program name %s' % (cfgFile, PROGRAM_NAME)) return if cfgTree.getAttrValue('config-version', '', default=None) != CONFIG_VERSION: log.error( 'config file %s version is not compatible with program version %s' % (cfgFile, CONFIG_VERSION)) return random.seed() gCurrentRequestContext = {} credIdMap = {} peerIdMap = {} contextIdList = [] contentIdList = [] pluginIdMap = {} trunkIdMap = {} engineIdMap = {} transportDispatcher = AsynsockDispatcher() transportDispatcher.registerRoutingCbFun(lambda td, t, d: td) transportDispatcher.setSocketMap() # use global asyncore socket map # # Initialize plugin modules # pluginManager = PluginManager(macro.expandMacros( cfgTree.getAttrValue('plugin-modules-path-list', '', default=[], vector=True), {'config-dir': os.path.dirname(cfgFile)}), progId=PROGRAM_NAME, apiVer=PLUGIN_API_VERSION) for pluginCfgPath in cfgTree.getPathsToAttr('plugin-id'): pluginId = cfgTree.getAttrValue('plugin-id', *pluginCfgPath) pluginMod = cfgTree.getAttrValue('plugin-module', *pluginCfgPath) pluginOptions = macro.expandMacros( cfgTree.getAttrValue('plugin-options', *pluginCfgPath, **dict(default=[], vector=True)), {'config-dir': os.path.dirname(cfgFile)}) log.info( 'configuring plugin ID %s (at %s) from module %s with options %s...' % (pluginId, '.'.join(pluginCfgPath), pluginMod, ', '.join(pluginOptions) or '<none>')) try: pluginManager.loadPlugin(pluginId, pluginMod, pluginOptions) except SnmpfwdError: log.error('plugin %s not loaded: %s' % (pluginId, sys.exc_info()[1])) return for configEntryPath in cfgTree.getPathsToAttr('snmp-credentials-id'): credId = cfgTree.getAttrValue('snmp-credentials-id', *configEntryPath) configKey = [] log.info('configuring snmp-credentials %s (at %s)...' % (credId, '.'.join(configEntryPath))) engineId = cfgTree.getAttrValue('snmp-engine-id', *configEntryPath) if engineId in engineIdMap: snmpEngine, snmpContext, snmpEngineMap = engineIdMap[engineId] log.info('using engine-id %s' % snmpEngine.snmpEngineID.prettyPrint()) else: snmpEngine = engine.SnmpEngine(snmpEngineID=engineId) snmpContext = context.SnmpContext(snmpEngine) snmpEngineMap = {'transportDomain': {}, 'securityName': {}} snmpEngine.observer.registerObserver( securityAuditObserver, 'rfc2576.prepareDataElements:sm-failure', 'rfc3412.prepareDataElements:sm-failure', cbCtx=gCurrentRequestContext) snmpEngine.observer.registerObserver( requestObserver, 'rfc3412.receiveMessage:request', cbCtx=gCurrentRequestContext) CommandResponder(snmpEngine, snmpContext) NotificationReceiver(snmpEngine, None) engineIdMap[engineId] = snmpEngine, snmpContext, snmpEngineMap log.info('new engine-id %s' % snmpEngine.snmpEngineID.prettyPrint()) configKey.append(str(snmpEngine.snmpEngineID)) transportDomain = cfgTree.getAttrValue('snmp-transport-domain', *configEntryPath) transportDomain = rfc1902.ObjectName(transportDomain) if transportDomain in snmpEngineMap['transportDomain']: h, p, transportDomain = snmpEngineMap['transportDomain'][ transportDomain] log.info('using transport endpoint %s:%s, transport ID %s' % (h, p, transportDomain)) else: if transportDomain[:len(udp.domainName)] == udp.domainName: transport = udp.UdpTransport() elif transportDomain[:len(udp6.domainName)] == udp6.domainName: transport = udp6.Udp6Transport() else: log.error('unknown transport domain %s' % (transportDomain, )) return h, p = cfgTree.getAttrValue('snmp-bind-address', *configEntryPath).split(':', 1) snmpEngine.registerTransportDispatcher(transportDispatcher, transportDomain) transportOptions = cfgTree.getAttrValue( 'snmp-transport-options', *configEntryPath, **dict(default=[], vector=True)) t = transport.openServerMode((h, int(p))) if 'transparent-proxy' in transportOptions: t.enablePktInfo() t.enableTransparent() elif 'virtual-interface' in transportOptions: t.enablePktInfo() config.addSocketTransport(snmpEngine, transportDomain, t) snmpEngineMap['transportDomain'][ transportDomain] = h, p, transportDomain log.info( 'new transport endpoint %s:%s, options %s, transport ID %s' % (h, p, transportOptions and '/'.join(transportOptions) or '<none>', transportDomain)) configKey.append(transportDomain) securityModel = cfgTree.getAttrValue('snmp-security-model', *configEntryPath) securityModel = rfc1902.Integer(securityModel) securityLevel = cfgTree.getAttrValue('snmp-security-level', *configEntryPath) securityLevel = rfc1902.Integer(securityLevel) securityName = cfgTree.getAttrValue('snmp-security-name', *configEntryPath) if securityModel in (1, 2): if securityName in snmpEngineMap['securityName']: if snmpEngineMap['securityName'][ securityModel] == securityModel: log.info('using security-name %s' % securityName) else: raise SnmpfwdError( 'snmp-security-name %s already in use at snmp-security-model %s' % (securityName, securityModel)) else: communityName = cfgTree.getAttrValue('snmp-community-name', *configEntryPath) config.addV1System(snmpEngine, securityName, communityName, securityName=securityName) log.info( 'new community-name %s, security-model %s, security-name %s, security-level %s' % (communityName, securityModel, securityName, securityLevel)) snmpEngineMap['securityName'][securityName] = securityModel configKey.append(securityModel) configKey.append(securityLevel) configKey.append(securityName) elif securityModel == 3: if securityName in snmpEngineMap['securityName']: log.info('using USM security-name: %s' % securityName) else: usmUser = cfgTree.getAttrValue('snmp-usm-user', *configEntryPath) log.info( 'new USM user %s, security-model %s, security-level %s, security-name %s' % (usmUser, securityModel, securityLevel, securityName)) if securityLevel in (2, 3): usmAuthProto = cfgTree.getAttrValue( 'snmp-usm-auth-protocol', *configEntryPath, **dict(default=config.usmHMACMD5AuthProtocol)) usmAuthProto = rfc1902.ObjectName(usmAuthProto) usmAuthKey = cfgTree.getAttrValue('snmp-usm-auth-key', *configEntryPath) log.info( 'new USM authentication key: %s, authentication protocol: %s' % (usmAuthKey, usmAuthProto)) if securityLevel == 3: usmPrivProto = cfgTree.getAttrValue( 'snmp-usm-priv-protocol', *configEntryPath, **dict(default=config.usmDESPrivProtocol)) usmPrivProto = rfc1902.ObjectName(usmPrivProto) usmPrivKey = cfgTree.getAttrValue( 'snmp-usm-priv-key', *configEntryPath, **dict(default=None)) log.info( 'new USM encryption key: %s, encryption protocol: %s' % (usmPrivKey, usmPrivProto)) config.addV3User(snmpEngine, usmUser, usmAuthProto, usmAuthKey, usmPrivProto, usmPrivKey) else: config.addV3User(snmpEngine, usmUser, usmAuthProto, usmAuthKey) else: config.addV3User(snmpEngine, usmUser) snmpEngineMap['securityName'][securityName] = securityModel configKey.append(securityModel) configKey.append(securityLevel) configKey.append(securityName) else: raise SnmpfwdError('unknown snmp-security-model: %s' % securityModel) configKey = tuple(configKey) if configKey in credIdMap: log.error( 'ambiguous configuration for key snmp-credentials-id=%s at %s' % (credId, '.'.join(configEntryPath))) return credIdMap[configKey] = credId duplicates = {} for peerCfgPath in cfgTree.getPathsToAttr('snmp-peer-id'): peerId = cfgTree.getAttrValue('snmp-peer-id', *peerCfgPath) if peerId in duplicates: log.error( 'duplicate snmp-peer-id=%s at %s and %s' % (peerId, '.'.join(peerCfgPath), '.'.join(duplicates[peerId]))) return duplicates[peerId] = peerCfgPath log.info('configuring peer ID %s (at %s)...' % (peerId, '.'.join(peerCfgPath))) transportDomain = cfgTree.getAttrValue('snmp-transport-domain', *peerCfgPath) if transportDomain not in peerIdMap: peerIdMap[transportDomain] = [] for peerAddress in cfgTree.getAttrValue( 'snmp-peer-address-pattern-list', *peerCfgPath, **dict(vector=True)): for bindAddress in cfgTree.getAttrValue( 'snmp-bind-address-pattern-list', *peerCfgPath, **dict(vector=True)): peerIdMap[transportDomain].append( (re.compile(peerAddress + '#' + bindAddress), peerId)) duplicates = {} for contextCfgPath in cfgTree.getPathsToAttr('snmp-context-id'): contextId = cfgTree.getAttrValue('snmp-context-id', *contextCfgPath) if contextId in duplicates: log.error('duplicate snmp-context-id=%s at %s and %s' % (contextId, '.'.join(contextCfgPath), '.'.join( duplicates[contextId]))) return duplicates[contextId] = contextCfgPath k = '#'.join((cfgTree.getAttrValue('snmp-context-engine-id-pattern', *contextCfgPath), cfgTree.getAttrValue('snmp-context-name-pattern', *contextCfgPath))) log.info('configuring context ID %s (at %s), composite key: %s' % (contextId, '.'.join(contextCfgPath), k)) contextIdList.append((contextId, re.compile(k))) duplicates = {} for contentCfgPath in cfgTree.getPathsToAttr('snmp-content-id'): contentId = cfgTree.getAttrValue('snmp-content-id', *contentCfgPath) if contentId in duplicates: log.error('duplicate snmp-content-id=%s at %s and %s' % (contentId, '.'.join(contentCfgPath), '.'.join( duplicates[contentId]))) return duplicates[contentId] = contentCfgPath for x in cfgTree.getAttrValue('snmp-pdu-oid-prefix-pattern-list', *contentCfgPath, **dict(vector=True)): k = '#'.join([ cfgTree.getAttrValue('snmp-pdu-type-pattern', *contentCfgPath), x ]) log.info('configuring content ID %s (at %s), composite key: %s' % (contentId, '.'.join(contentCfgPath), k)) contentIdList.append((contentId, re.compile(k))) del duplicates for pluginCfgPath in cfgTree.getPathsToAttr('using-plugin-id-list'): pluginIdList = cfgTree.getAttrValue('using-plugin-id-list', *pluginCfgPath, **dict(vector=True)) log.info('configuring plugin ID(s) %s (at %s)...' % (','.join(pluginIdList), '.'.join(pluginCfgPath))) for credId in cfgTree.getAttrValue('matching-snmp-credentials-id-list', *pluginCfgPath, **dict(vector=True)): for peerId in cfgTree.getAttrValue('matching-snmp-peer-id-list', *pluginCfgPath, **dict(vector=True)): for contextId in cfgTree.getAttrValue( 'matching-snmp-context-id-list', *pluginCfgPath, **dict(vector=True)): for contentId in cfgTree.getAttrValue( 'matching-snmp-content-id-list', *pluginCfgPath, **dict(vector=True)): k = credId, contextId, peerId, contentId if k in pluginIdMap: log.error( 'duplicate snmp-credentials-id %s, snmp-context-id %s, snmp-peer-id %s, snmp-content-id %s at plugin-id(s) %s' % (credId, contextId, peerId, contentId, ','.join(pluginIdList))) return else: log.info( 'configuring plugin(s) %s (at %s), composite key: %s' % (','.join(pluginIdList), '.'.join(pluginCfgPath), '/'.join(k))) for pluginId in pluginIdList: if not pluginManager.hasPlugin(pluginId): log.error( 'undefined plugin ID %s referenced at %s' % (pluginId, '.'.join(pluginCfgPath))) return pluginIdMap[k] = pluginIdList for routeCfgPath in cfgTree.getPathsToAttr('using-trunk-id-list'): trunkIdList = cfgTree.getAttrValue('using-trunk-id-list', *routeCfgPath, **dict(vector=True)) log.info('configuring destination trunk ID(s) %s (at %s)...' % (','.join(trunkIdList), '.'.join(routeCfgPath))) for credId in cfgTree.getAttrValue('matching-snmp-credentials-id-list', *routeCfgPath, **dict(vector=True)): for peerId in cfgTree.getAttrValue('matching-snmp-peer-id-list', *routeCfgPath, **dict(vector=True)): for contextId in cfgTree.getAttrValue( 'matching-snmp-context-id-list', *routeCfgPath, **dict(vector=True)): for contentId in cfgTree.getAttrValue( 'matching-snmp-content-id-list', *routeCfgPath, **dict(vector=True)): k = credId, contextId, peerId, contentId if k in trunkIdMap: log.error( 'duplicate snmp-credentials-id %s, snmp-context-id %s, snmp-peer-id %s, snmp-content-id %s at trunk-id(s) %s' % (credId, contextId, peerId, contentId, ','.join(trunkIdList))) return else: trunkIdMap[k] = trunkIdList log.info( 'configuring trunk routing to %s (at %s), composite key: %s' % (','.join(trunkIdList), '.'.join(routeCfgPath), '/'.join(k))) def dataCbFun(trunkId, msgId, msg): log.debug('message ID %s received from trunk %s' % (msgId, trunkId)) trunkingManager = TrunkingManager(dataCbFun) def getTrunkAddr(a, port=0): f = lambda h, p=port: (h, int(p)) try: return f(*a.split(':')) except Exception: raise SnmpfwdError('improper IPv4 endpoint %s' % a) for trunkCfgPath in cfgTree.getPathsToAttr('trunk-id'): trunkId = cfgTree.getAttrValue('trunk-id', *trunkCfgPath) secret = cfgTree.getAttrValue('trunk-crypto-key', *trunkCfgPath, **dict(default='')) secret = secret and (secret * ((16 // len(secret)) + 1))[:16] log.info('configuring trunk ID %s (at %s)...' % (trunkId, '.'.join(trunkCfgPath))) connectionMode = cfgTree.getAttrValue('trunk-connection-mode', *trunkCfgPath) if connectionMode == 'client': trunkingManager.addClient( trunkId, getTrunkAddr( cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath)), getTrunkAddr( cfgTree.getAttrValue('trunk-peer-address', *trunkCfgPath), 30201), cfgTree.getAttrValue('trunk-ping-period', *trunkCfgPath, default=0, expect=int), secret) log.info( 'new trunking client from %s to %s' % (cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath), cfgTree.getAttrValue('trunk-peer-address', *trunkCfgPath))) if connectionMode == 'server': trunkingManager.addServer( getTrunkAddr( cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath), 30201), cfgTree.getAttrValue('trunk-ping-period', *trunkCfgPath, default=0, expect=int), secret) log.info( 'new trunking server at %s' % (cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath))) transportDispatcher.registerTimerCbFun(trunkingManager.setupTrunks, random.randrange(1, 5)) transportDispatcher.registerTimerCbFun(trunkingManager.monitorTrunks, random.randrange(1, 5)) try: daemon.dropPrivileges(procUser, procGroup) except Exception: log.error('can not drop privileges: %s' % sys.exc_info()[1]) return if not foregroundFlag: try: daemon.daemonize(pidFile) except Exception: log.error('can not daemonize process: %s' % sys.exc_info()[1]) return # Run mainloop log.info('starting I/O engine...') transportDispatcher.jobStarted(1) # server job would never finish # Python 2.4 does not support the "finally" clause while True: try: transportDispatcher.runDispatcher() except (PySnmpError, SnmpfwdError, socket.error): log.error(str(sys.exc_info()[1])) continue except Exception: transportDispatcher.closeDispatcher() raise
from pysnmp.smi import builder, view, compiler, error from pysnmp import debug debug.setLogger(debug.Debug('dsp')) # Create MIB loader/builder mibBuilder = builder.MibBuilder() # Optionally attach PySMI MIB compiler (if installed) print('Attaching MIB compiler...'), compiler.addMibCompiler( mibBuilder, sources=['/usr/share/snmp/mibs/', 'http://mibs.snmplabs.com/asn1/']) print('done') # Optionally set an alternative path to compiled MIBs print('Setting MIB sources...') mibBuilder.addMibSources(builder.DirMibSource('/usr/share/snmp/mibs/')) print(mibBuilder.getMibSources()) print('done') print('Loading MIB modules...'), mibBuilder.loadModules('SNMPv2-MIB', 'SNMP-FRAMEWORK-MIB', 'SNMP-COMMUNITY-MIB', 'IP-MIB', 'SNMPHANDLER-MIB') print('done') print('Indexing MIB objects...'), mibView = view.MibViewController(mibBuilder) print('done') print('MIB symbol name lookup by OID: '),
def main(): parser = argparse.ArgumentParser(description=DESCRIPTION) parser.add_argument( '-v', '--version', action='version', version=utils.TITLE) parser.add_argument( '--quiet', action='store_true', help='Do not print out informational messages') parser.add_argument( '--debug', choices=debug.flagMap, action='append', type=str, default=[], help='Enable one or more categories of SNMP debugging.') parser.add_argument( '--row-hint', dest='row_hint', action='store_true', help='Hint for MIBs Type') parser.add_argument( '--mib-source', dest='mib_sources', metavar='<URI|PATH>', action='append', type=str, default=['http://mibs.snmplabs.com/asn1/@mib@'], help='One or more URIs pointing to a collection of ASN.1 MIB files.' 'Optional "@mib@" token gets replaced with desired MIB module ' 'name during MIB search.') parser.add_argument( '--mib-module', dest='mib_modules', action='append', type=str, required=True, help='MIB module to generate simulation data from') parser.add_argument( '--start-object', metavar='<MIB::Object|OID>', type=_parse_mib_object, help='Drop all simulation data records prior to this OID specified ' 'as MIB object (MIB::Object) or OID (1.3.6.)') parser.add_argument( '--stop-object', metavar='<MIB::Object|OID>', type=functools.partial(_parse_mib_object, last=True), help='Drop all simulation data records after this OID specified ' 'as MIB object (MIB::Object) or OID (1.3.6.)') parser.add_argument( '--manual-values', action='store_true', help='Fill all managed objects values interactively') parser.add_argument( '--automatic-values', type=int, default=5000, help='Probe for suitable managed object value this many times ' 'prior to failing over to manual value specification') parser.add_argument( '--table-size', type=int, default=10, help='Generate SNMP conceptual tables with this many rows') parser.add_argument( '--destination-record-type', choices=RECORD_TYPES, default='snmprec', help='Produce simulation data with record of this type') parser.add_argument( '--output-file', metavar='<FILE>', type=str, help='SNMP simulation data file to write records to') parser.add_argument( '--string-pool', metavar='<words>', action='append', help='Words to use for simulated string values') parser.add_argument( '--string-pool-file', metavar='<FILE>', type=str, help='File containing the words for simulating SNMP string values') parser.add_argument( '--integer32-range', metavar='<min,max>', type=_parse_range, default=(0, 32), help='Range of values used to populate simulated Integer32 values') parser.add_argument( '--unsigned-range', metavar='<min,max>', type=_parse_range, default=(0, 65535), help='Range of values used to populate simulated Unsigned values') parser.add_argument( '--counter-range', metavar='<min,max>', type=_parse_range, default=(0, 0xffffffff), help='Range of values used to populate simulated Counter values') parser.add_argument( '--counter64-range', metavar='<min,max>', type=_parse_range, default=(0, 0xffffffffffffffff), help='Range of values used to populate simulated Counter64 values') parser.add_argument( '--gauge-range', metavar='<min,max>', type=_parse_range, default=(0, 0xffffffff), help='Range of values used to populate simulated Gauge values') parser.add_argument( '--timeticks-range', metavar='<min,max>', type=_parse_range, default=(0, 0xffffffff), help='Range of values used to populate simulated Timeticks values') args = parser.parse_args() if args.debug: debug.setLogger(debug.Debug(*args.debug)) if args.manual_values: args.automatic_values = 0 if args.string_pool_file: with open(args.string_pool_file) as fl: args.string_pool = fl.read().split() elif args.string_pool: args.string_pool = ['Jaded', 'zombies', 'acted', 'quaintly', 'but', 'kept', 'driving', 'their', 'oxen', 'forward'] if args.output_file: ext = os.path.extsep + RECORD_TYPES[args.destination_record_type].ext if not args.output_file.endswith(ext): args.output_file += ext args.output_file = RECORD_TYPES[args.destination_record_type].open( args.output_file, 'wb') else: args.output_file = sys.stdout if sys.version_info >= (3, 0, 0): # binary mode write args.output_file = sys.stdout.buffer elif sys.platform == "win32": import msvcrt msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) def get_value(syntax, hint='', automatic_values=args.automatic_values): make_guess = args.automatic_values val = None while True: if make_guess: if isinstance(syntax, rfc1902.IpAddress): val = '.'.join([str(random.randrange(1, 256)) for x in range(4)]) elif isinstance(syntax, rfc1902.TimeTicks): val = random.randrange(args.timeticks_range[0], args.timeticks_range[1]) elif isinstance(syntax, rfc1902.Gauge32): val = random.randrange(args.gauge_range[0], args.gauge_range[1]) elif isinstance(syntax, rfc1902.Counter32): val = random.randrange(args.counter_range[0], args.counter_range[1]) elif isinstance(syntax, rfc1902.Integer32): val = random.randrange(args.integer32_range[0], args.integer32_range[1]) elif isinstance(syntax, rfc1902.Unsigned32): val = random.randrange(args.unsigned_range[0], args.unsigned_range[1]) elif isinstance(syntax, rfc1902.Counter64): val = random.randrange(args.counter64_range[0], args.counter64_range[1]) elif isinstance(syntax, univ.OctetString): maxWords = 10 val = ' '.join([args.string_pool[random.randrange(0, len(args.string_pool))] for i in range(random.randrange(1, maxWords))]) elif isinstance(syntax, univ.ObjectIdentifier): val = '.'.join(['1', '3', '6', '1', '3'] + [ '%d' % random.randrange(0, 255) for x in range(random.randrange(0, 10))]) elif isinstance(syntax, rfc1902.Bits): val = [random.randrange(0, 256) for x in range(random.randrange(0, 9))] else: val = '?' # remove value enumeration try: if syntax.tagSet == rfc1902.Integer32.tagSet: return rfc1902.Integer32(syntax.clone(val)) if syntax.tagSet == rfc1902.Unsigned32.tagSet: return rfc1902.Unsigned32(syntax.clone(val)) if syntax.tagSet == rfc1902.Bits.tagSet: return rfc1902.OctetString(syntax.clone(val)) return syntax.clone(val) except PyAsn1Error as exc: if make_guess == 1: sys.stderr.write( '*** Inconsistent value: %s\r\n*** See constraints and ' 'suggest a better one for:\r\n' % exc) if make_guess: make_guess -= 1 continue sys.stderr.write('%s# Value [\'%s\'] ? ' % ( hint, (val is None and '<none>' or val),)) sys.stderr.flush() line = sys.stdin.readline().strip() if line: if line[:2] == '0x': if line[:4] == '0x0x': line = line[2:] elif isinstance(syntax, univ.OctetString): val = syntax.clone(hexValue=line[2:]) else: val = int(line[2:], 16) else: val = line data_file_handler = snmprec.SnmprecRecord() mib_builder = builder.MibBuilder() # Load MIB tree foundation classes (MibScalar, MibTable, MibTableRow, MibTableColumn) = mib_builder.importSymbols( 'SNMPv2-SMI', 'MibScalar', 'MibTable', 'MibTableRow', 'MibTableColumn' ) mib_view_controller = view.MibViewController(mib_builder) compiler.addMibCompiler(mib_builder, sources=args.mib_sources) try: if isinstance(args.start_object, ObjectIdentity): args.start_object.resolveWithMib(mib_view_controller) if isinstance(args.stop_object, ObjectIdentity): args.stop_object.resolveWithMib(mib_view_controller) except error.PySnmpError as exc: sys.stderr.write('ERROR: %s\r\n' % exc) return 1 output = [] # MIBs walk for modName in args.mib_modules: if not args.quiet: sys.stderr.write( '# MIB module: %s, from %s till ' '%s\r\n' % (modName, args.start_object or 'the beginning', args.stop_object or 'the end')) try: oid = ObjectIdentity(modName).resolveWithMib(mib_view_controller) except error.PySnmpError as exc: sys.stderr.write('ERROR: failed on MIB %s: ' '%s\r\n' % (modName, exc)) return 1 hint = row_hint = '' row_oid = None suffix = () this_table_size = 0 while True: try: oid, label, _ = mib_view_controller.getNextNodeName(oid) except error.NoSuchObjectError: break if row_oid and not row_oid.isPrefixOf(oid): this_table_size += 1 if args.automatic_values: if this_table_size < args.table_size: oid = tuple(row_oid) if not args.quiet: sys.stderr.write( '# Synthesizing row #%d of table %s\r\n' % ( this_table_size, row_oid)) else: if not args.quiet: sys.stderr.write( '# Finished table %s (%d rows)\r\n' % ( row_oid, this_table_size)) row_oid = None else: while True: sys.stderr.write( '# Synthesize row #%d for table %s (y/n)? ' % ( this_table_size, row_oid)) sys.stderr.flush() line = sys.stdin.readline().strip() if line: if line[0] in ('y', 'Y'): oid = tuple(row_oid) break elif line[0] in ('n', 'N'): if not args.quiet: sys.stderr.write( '# Finished table %s (%d rows)\r\n' % ( row_oid, this_table_size)) row_oid = None break if args.start_object and oid < args.start_object: continue # skip on premature OID if args.stop_object and oid > args.stop_object: break # stop on out of range condition mib_name, sym_name, _ = mib_view_controller.getNodeLocation(oid) node, = mib_builder.importSymbols(mib_name, sym_name) if isinstance(node, MibTable): hint = '# Table %s::%s\r\n' % (mib_name, sym_name) if not args.quiet: sys.stderr.write( '# Starting table %s::%s (%s)\r\n' % ( mib_name, sym_name, univ.ObjectIdentifier(oid))) continue elif isinstance(node, MibTableRow): row_indices = {} suffix = () row_hint = hint + '# Row %s::%s\r\n' % (mib_name, sym_name) for (implied_flag, idx_mod_name, idx_sym_name) in node.getIndexNames(): idxNode, = mib_builder.importSymbols( idx_mod_name, idx_sym_name) row_hint += '# Index %s::%s (type %s)\r\n' % ( idx_mod_name, idx_sym_name, idxNode.syntax.__class__.__name__) row_indices[idxNode.name] = get_value( idxNode.syntax, not args.quiet and row_hint or '') suffix = suffix + node.getAsName( row_indices[idxNode.name], implied_flag) if not row_indices: if not args.quiet: sys.stderr.write( '# WARNING: %s::%s table has no index!\r\n' % ( mib_name, sym_name)) if row_oid is None: this_table_size = 0 row_oid = univ.ObjectIdentifier(oid) continue elif isinstance(node, MibTableColumn): oid = node.name if oid in row_indices: val = row_indices[oid] else: hint = '' if not args.quiet: hint += row_hint hint += ('# Column %s::%s (type' ' %s)\r\n' % (mib_name, sym_name, node.syntax.__class__.__name__)) val = get_value(node.syntax, hint) elif isinstance(node, MibScalar): hint = '' if not args.row_hint: hint += ('# Scalar %s::%s (type %s)' '\r\n' % (mib_name, sym_name, node.syntax.__class__.__name__)) oid = node.name suffix = (0,) val = get_value(node.syntax, hint) else: hint = '' continue output.append((oid + suffix, val)) output.sort(key=lambda x: univ.ObjectIdentifier(x[0])) unique = set() for oid, val in output: if oid in unique: if not args.quiet: sys.stderr.write( '# Dropping duplicate OID %s\r\n' % ( univ.ObjectIdentifier(oid),)) else: try: args.output_file.write(data_file_handler.format(oid, val)) except SnmpsimError as exc: sys.stderr.write('ERROR: %s\r\n' % (exc,)) else: unique.add(oid) if not args.quiet: sys.stderr.write( '# End of %s, %s OID(s) dumped\r\n' % (modName, len(unique))) args.output_file.flush() args.output_file.close() return 0
def main(): class MibTreeProxyMixIn(object): MIB_INTRUMENTATION_CALL = None def _getMgmtFun(self, contextName): return self._routeToMibTree def _routeToMibTree(self, *varBinds, **context): cbFun = context['cbFun'] mibTreeReq = gCurrentRequestContext.copy() pdu = mibTreeReq['snmp-pdu'] pluginIdList = mibTreeReq['plugins-list'] logCtx = LogString(mibTreeReq) reqCtx = {} for pluginNum, pluginId in enumerate(pluginIdList): st, pdu = pluginManager.processCommandRequest( pluginId, snmpEngine, pdu, mibTreeReq, reqCtx) if st == status.BREAK: log.debug('plugin %s inhibits other plugins' % pluginId, ctx=logCtx) pluginIdList = pluginIdList[:pluginNum] break elif st == status.DROP: log.debug( 'received SNMP message, plugin %s muted request' % pluginId, ctx=logCtx) # TODO: need to report some special error to drop request cbFun(varBinds, **context) return elif st == status.RESPOND: log.debug( 'received SNMP message, plugin %s forced immediate response' % pluginId, ctx=logCtx) # TODO: should we respond something other than request? cbFun(varBinds, **context) return # Apply PDU to MIB(s) mibTreeId = mibTreeReq['mib-tree-id'] if not mibTreeId: log.error('no matching MIB tree route for the request', ctx=logCtx) cbFun(varBinds, **dict(context, error=smi_error.GenError())) return mibInstrum = mibTreeIdMap.get(mibTreeId) if not mibInstrum: log.error('MIB tree ID %s does not exist' % mibTreeId, ctx=logCtx) cbFun(varBinds, **dict(context, error=smi_error.GenError())) return log.debug('received SNMP message, applied on mib-tree-id %s' % mibTreeId, ctx=logCtx) cbCtx = pluginIdList, mibTreeId, mibTreeReq, snmpEngine, reqCtx, context[ 'cbFun'] mgmtFun = getattr(mibInstrum, self.MIB_INTRUMENTATION_CALL) mgmtFun( *varBinds, **dict(context, cbFun=self._mibTreeCbFun, cbCtx=cbCtx, acFun=None)) # TODO: it just occurred to me that `*varBinds` would look more consistent def _mibTreeCbFun(self, varBinds, **context): pluginIdList, mibTreeId, mibTreeReq, snmpEngine, reqCtx, cbFun = context[ 'cbCtx'] logCtx = LogString(mibTreeReq) err = context.get('error') if err: log.info('MIB operation resulted in error: %s' % err, ctx=logCtx) cbFun(varBinds, **dict(context, cbFun=cbFun)) # plugins need to work at var-binds level # # for key in tuple(mibTreeRsp): # pdu = mibTreeRsp['client-snmp-pdu'] # # for pluginId in pluginIdList: # st, pdu = pluginManager.processCommandResponse( # pluginId, snmpEngine, pdu, mibTreeReq, reqCtx # ) # # if st == status.BREAK: # log.debug('plugin %s inhibits other plugins' % pluginId, ctx=logCtx) # break # elif st == status.DROP: # log.debug('plugin %s muted response' % pluginId, ctx=logCtx) # self.releaseStateInformation(stateReference) # return # # try: # self.sendPdu(snmpEngine, stateReference, pdu) # # except PySnmpError: # log.error('mibTree message #%s, SNMP response error: %s' % (msgId, sys.exc_info()[1]), # ctx=logCtx) # # else: # log.debug('received mibTree message #%s, forwarded as SNMP message' % msgId, ctx=logCtx) class GetCommandResponder(MibTreeProxyMixIn, cmdrsp.GetCommandResponder): MIB_INTRUMENTATION_CALL = 'readMibObjects' class GetNextCommandResponder(MibTreeProxyMixIn, cmdrsp.NextCommandResponder): MIB_INTRUMENTATION_CALL = 'readNextMibObjects' class GetBulkCommandResponder(MibTreeProxyMixIn, cmdrsp.BulkCommandResponder): MIB_INTRUMENTATION_CALL = 'readNextMibObjects' class SetCommandResponder(MibTreeProxyMixIn, cmdrsp.SetCommandResponder): MIB_INTRUMENTATION_CALL = 'writeMibObjects' class LogString(LazyLogString): GROUPINGS = [ ['callflow-id'], [ 'snmp-engine-id', 'snmp-transport-domain', 'snmp-bind-address', 'snmp-bind-port', 'snmp-security-model', 'snmp-security-level', 'snmp-security-name', 'snmp-credentials-id' ], ['snmp-context-engine-id', 'snmp-context-name', 'snmp-context-id'], ['snmp-pdu', 'snmp-content-id'], ['snmp-peer-address', 'snmp-peer-port', 'snmp-peer-id'], ['mib-tree-id'], ['client-snmp-pdu'], ] FORMATTERS = { 'client-snmp-pdu': LazyLogString.prettyVarBinds, 'snmp-pdu': LazyLogString.prettyVarBinds, } def securityAuditObserver(snmpEngine, execpoint, variables, cbCtx): securityModel = variables.get('securityModel', 0) logMsg = 'SNMPv%s auth failure' % securityModel logMsg += ' at %s:%s' % variables['transportAddress'].getLocalAddress() logMsg += ' from %s:%s' % variables['transportAddress'] statusInformation = variables.get('statusInformation', {}) if securityModel in (1, 2): logMsg += ' using snmp-community-name "%s"' % statusInformation.get( 'communityName', '?') elif securityModel == 3: logMsg += ' using snmp-usm-user "%s"' % statusInformation.get( 'msgUserName', '?') try: logMsg += ': %s' % statusInformation['errorIndication'] except KeyError: pass log.error(logMsg) def usmRequestObserver(snmpEngine, execpoint, variables, cbCtx): mibTreeReq = {'snmp-security-engine-id': variables['securityEngineId']} cbCtx.clear() cbCtx.update(mibTreeReq) def requestObserver(snmpEngine, execpoint, variables, cbCtx): mibTreeReq = { 'callflow-id': '%10.10x' % random.randint(0, 0xffffffffff), 'snmp-engine-id': snmpEngine.snmpEngineID, 'snmp-transport-domain': variables['transportDomain'], 'snmp-peer-address': variables['transportAddress'][0], 'snmp-peer-port': variables['transportAddress'][1], 'snmp-bind-address': variables['transportAddress'].getLocalAddress()[0], 'snmp-bind-port': variables['transportAddress'].getLocalAddress()[1], 'snmp-security-model': variables['securityModel'], 'snmp-security-level': variables['securityLevel'], 'snmp-security-name': variables['securityName'], 'snmp-context-engine-id': variables['contextEngineId'], 'snmp-context-name': variables['contextName'], } try: mibTreeReq['snmp-security-engine-id'] = cbCtx.pop( 'snmp-security-engine-id') except KeyError: # SNMPv1/v2c mibTreeReq['snmp-security-engine-id'] = mibTreeReq[ 'snmp-engine-id'] mibTreeReq['snmp-credentials-id'] = macro.expandMacro( credIdMap.get( (str(snmpEngine.snmpEngineID), variables['transportDomain'], variables['securityModel'], variables['securityLevel'], str(variables['securityName']))), mibTreeReq) k = '#'.join([ str(x) for x in (variables['contextEngineId'], variables['contextName']) ]) for x, y in contextIdList: if y.match(k): mibTreeReq['snmp-context-id'] = macro.expandMacro( x, mibTreeReq) break else: mibTreeReq['snmp-context-id'] = None addr = '%s:%s#%s:%s' % ( variables['transportAddress'][0], variables['transportAddress'][1], variables['transportAddress'].getLocalAddress()[0], variables['transportAddress'].getLocalAddress()[1]) for pat, peerId in peerIdMap.get(str(variables['transportDomain']), ()): if pat.match(addr): mibTreeReq['snmp-peer-id'] = macro.expandMacro( peerId, mibTreeReq) break else: mibTreeReq['snmp-peer-id'] = None pdu = variables['pdu'] k = '#'.join([ snmpPduTypesMap.get(variables['pdu'].tagSet, '?'), '|'.join([str(x[0]) for x in v2c.apiPDU.getVarBinds(pdu)]) ]) for x, y in contentIdList: if y.match(k): mibTreeReq['snmp-content-id'] = macro.expandMacro( x, mibTreeReq) break else: mibTreeReq['snmp-content-id'] = None mibTreeReq['plugins-list'] = pluginIdMap.get( (mibTreeReq['snmp-credentials-id'], mibTreeReq['snmp-context-id'], mibTreeReq['snmp-peer-id'], mibTreeReq['snmp-content-id']), []) mibTreeReq['mib-tree-id'] = routingMap.get( (mibTreeReq['snmp-credentials-id'], mibTreeReq['snmp-context-id'], mibTreeReq['snmp-peer-id'], mibTreeReq['snmp-content-id'])) mibTreeReq['snmp-pdu'] = pdu cbCtx.clear() cbCtx.update(mibTreeReq) # # main script starts here # helpMessage = """\ Usage: %s [--help] [--version ] [--debug-snmp=<%s>] [--debug-asn1=<%s>] [--daemonize] [--process-user=<uname>] [--process-group=<gname>] [--pid-file=<file>] [--logging-method=<%s[:args>]>] [--log-level=<%s>] [--config-file=<file>]""" % (sys.argv[0], '|'.join([ x for x in getattr(pysnmp_debug, 'FLAG_MAP', getattr(pysnmp_debug, 'flagMap', ())) if x != 'mibview' ]), '|'.join([ x for x in getattr(pyasn1_debug, 'FLAG_MAP', getattr(pyasn1_debug, 'flagMap', ())) ]), '|'.join(log.methodsMap), '|'.join(log.levelsMap)) try: opts, params = getopt.getopt(sys.argv[1:], 'hv', [ 'help', 'version', 'debug=', 'debug-snmp=', 'debug-asn1=', 'daemonize', 'process-user='******'process-group=', 'pid-file=', 'logging-method=', 'log-level=', 'config-file=' ]) except Exception: sys.stderr.write('ERROR: %s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage)) return if params: sys.stderr.write('ERROR: extra arguments supplied %s\r\n%s\r\n' % (params, helpMessage)) return pidFile = '' cfgFile = CONFIG_FILE foregroundFlag = True procUser = procGroup = None loggingMethod = ['stderr'] loggingLevel = None for opt in opts: if opt[0] == '-h' or opt[0] == '--help': sys.stderr.write("""\ Synopsis: SNMP Command Responder. Runs one or more SNMP command responders (agents) and one or more trees of MIB objects representing SNMP-managed entities. The tool applies received messages onto one of the MIB trees chosen by tool's configuration. Documentation: http://snmplabs.com/snmpresponder/ %s """ % helpMessage) return if opt[0] == '-v' or opt[0] == '--version': import snmpresponder import pysnmp import pyasn1 sys.stderr.write("""\ SNMP Command Responder version %s, written by Ilya Etingof <*****@*****.**> Using foundation libraries: pysnmp %s, pyasn1 %s. Python interpreter: %s Software documentation and support at http://snmplabs.com/snmpresponder/ %s """ % (snmpresponder.__version__, hasattr(pysnmp, '__version__') and pysnmp.__version__ or 'unknown', hasattr(pyasn1, '__version__') and pyasn1.__version__ or 'unknown', sys.version, helpMessage)) return elif opt[0] == '--debug-snmp': pysnmp_debug.setLogger( pysnmp_debug.Debug(*opt[1].split(','), loggerName=PROGRAM_NAME + '.pysnmp')) elif opt[0] == '--debug-asn1': pyasn1_debug.setLogger( pyasn1_debug.Debug(*opt[1].split(','), loggerName=PROGRAM_NAME + '.pyasn1')) elif opt[0] == '--daemonize': foregroundFlag = False elif opt[0] == '--process-user': procUser = opt[1] elif opt[0] == '--process-group': procGroup = opt[1] elif opt[0] == '--pid-file': pidFile = opt[1] elif opt[0] == '--logging-method': loggingMethod = opt[1].split(':') elif opt[0] == '--log-level': loggingLevel = opt[1] elif opt[0] == '--config-file': cfgFile = opt[1] with daemon.PrivilegesOf(procUser, procGroup): try: log.setLogger(PROGRAM_NAME, *loggingMethod, force=True) if loggingLevel: log.setLevel(loggingLevel) except SnmpResponderError: sys.stderr.write('%s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage)) return try: cfgTree = cparser.Config().load(cfgFile) except SnmpResponderError: log.error('configuration parsing error: %s' % sys.exc_info()[1]) return if cfgTree.getAttrValue('program-name', '', default=None) != PROGRAM_NAME: log.error('config file %s does not match program name %s' % (cfgFile, PROGRAM_NAME)) return if cfgTree.getAttrValue('config-version', '', default=None) != CONFIG_VERSION: log.error( 'config file %s version is not compatible with program version %s' % (cfgFile, CONFIG_VERSION)) return random.seed() gCurrentRequestContext = {} credIdMap = {} peerIdMap = {} contextIdList = [] contentIdList = [] pluginIdMap = {} routingMap = {} mibTreeIdMap = {} engineIdMap = {} transportDispatcher = AsyncoreDispatcher() transportDispatcher.registerRoutingCbFun(lambda td, t, d: td) transportDispatcher.setSocketMap() # use global asyncore socket map # # Initialize plugin modules # pluginManager = PluginManager(macro.expandMacros( cfgTree.getAttrValue('plugin-modules-path-list', '', default=[], vector=True), {'config-dir': os.path.dirname(cfgFile)}), progId=PROGRAM_NAME, apiVer=PLUGIN_API_VERSION) for pluginCfgPath in cfgTree.getPathsToAttr('plugin-id'): pluginId = cfgTree.getAttrValue('plugin-id', *pluginCfgPath) pluginMod = cfgTree.getAttrValue('plugin-module', *pluginCfgPath) pluginOptions = macro.expandMacros( cfgTree.getAttrValue('plugin-options', *pluginCfgPath, default=[], vector=True), {'config-dir': os.path.dirname(cfgFile)}) log.info( 'configuring plugin ID %s (at %s) from module %s with options %s...' % (pluginId, '.'.join(pluginCfgPath), pluginMod, ', '.join(pluginOptions) or '<none>')) with daemon.PrivilegesOf(procUser, procGroup): try: pluginManager.loadPlugin(pluginId, pluginMod, pluginOptions) except SnmpResponderError: log.error('plugin %s not loaded: %s' % (pluginId, sys.exc_info()[1])) return for configEntryPath in cfgTree.getPathsToAttr('snmp-credentials-id'): credId = cfgTree.getAttrValue('snmp-credentials-id', *configEntryPath) configKey = [] log.info('configuring snmp-credentials %s (at %s)...' % (credId, '.'.join(configEntryPath))) engineId = cfgTree.getAttrValue('snmp-engine-id', *configEntryPath) if engineId in engineIdMap: snmpEngine, snmpContext, snmpEngineMap = engineIdMap[engineId] log.info('using engine-id %s' % snmpEngine.snmpEngineID.prettyPrint()) else: snmpEngine = engine.SnmpEngine(snmpEngineID=engineId) snmpContext = context.SnmpContext(snmpEngine) snmpEngineMap = {'transportDomain': {}, 'securityName': {}} snmpEngine.observer.registerObserver( securityAuditObserver, 'rfc2576.prepareDataElements:sm-failure', 'rfc3412.prepareDataElements:sm-failure', cbCtx=gCurrentRequestContext) snmpEngine.observer.registerObserver( requestObserver, 'rfc3412.receiveMessage:request', cbCtx=gCurrentRequestContext) snmpEngine.observer.registerObserver(usmRequestObserver, 'rfc3414.processIncomingMsg', cbCtx=gCurrentRequestContext) GetCommandResponder(snmpEngine, snmpContext) GetNextCommandResponder(snmpEngine, snmpContext) GetBulkCommandResponder(snmpEngine, snmpContext) SetCommandResponder(snmpEngine, snmpContext) engineIdMap[engineId] = snmpEngine, snmpContext, snmpEngineMap log.info('new engine-id %s' % snmpEngine.snmpEngineID.prettyPrint()) configKey.append(str(snmpEngine.snmpEngineID)) transportDomain = cfgTree.getAttrValue('snmp-transport-domain', *configEntryPath) transportDomain = rfc1902.ObjectName(transportDomain) if (transportDomain[:len(udp.DOMAIN_NAME)] != udp.DOMAIN_NAME and udp6 and transportDomain[:len(udp6.DOMAIN_NAME)] != udp6.DOMAIN_NAME): log.error('unknown transport domain %s' % (transportDomain, )) return if transportDomain in snmpEngineMap['transportDomain']: bindAddr, transportDomain = snmpEngineMap['transportDomain'][ transportDomain] log.info('using transport endpoint [%s]:%s, transport ID %s' % (bindAddr[0], bindAddr[1], transportDomain)) else: bindAddr = cfgTree.getAttrValue('snmp-bind-address', *configEntryPath) transportOptions = cfgTree.getAttrValue('snmp-transport-options', *configEntryPath, default=[], vector=True) try: bindAddr, bindAddrMacro = endpoint.parseTransportAddress( transportDomain, bindAddr, transportOptions) except SnmpResponderError: log.error('bad snmp-bind-address specification %s at %s' % (bindAddr, '.'.join(configEntryPath))) return if transportDomain[:len(udp.DOMAIN_NAME)] == udp.DOMAIN_NAME: transport = udp.UdpTransport() else: transport = udp6.Udp6Transport() t = transport.openServerMode(bindAddr) if 'transparent-proxy' in transportOptions: t.enablePktInfo() t.enableTransparent() elif 'virtual-interface' in transportOptions: t.enablePktInfo() snmpEngine.registerTransportDispatcher(transportDispatcher, transportDomain) config.addSocketTransport(snmpEngine, transportDomain, t) snmpEngineMap['transportDomain'][ transportDomain] = bindAddr, transportDomain log.info( 'new transport endpoint [%s]:%s, options %s, transport ID %s' % (bindAddr[0], bindAddr[1], transportOptions and '/'.join(transportOptions) or '<none>', transportDomain)) configKey.append(transportDomain) securityModel = cfgTree.getAttrValue('snmp-security-model', *configEntryPath) securityModel = rfc1902.Integer(securityModel) securityLevel = cfgTree.getAttrValue('snmp-security-level', *configEntryPath) securityLevel = rfc1902.Integer(securityLevel) securityName = cfgTree.getAttrValue('snmp-security-name', *configEntryPath) if securityModel in (1, 2): if securityName in snmpEngineMap['securityName']: if snmpEngineMap['securityName'][ securityModel] == securityModel: log.info('using security-name %s' % securityName) else: raise SnmpResponderError( 'snmp-security-name %s already in use at snmp-security-model %s' % (securityName, securityModel)) else: communityName = cfgTree.getAttrValue('snmp-community-name', *configEntryPath) config.addV1System(snmpEngine, securityName, communityName, securityName=securityName) log.info( 'new community-name %s, security-model %s, security-name %s, security-level %s' % (communityName, securityModel, securityName, securityLevel)) snmpEngineMap['securityName'][securityName] = securityModel configKey.append(securityModel) configKey.append(securityLevel) configKey.append(securityName) elif securityModel == 3: if securityName in snmpEngineMap['securityName']: log.info('using USM security-name: %s' % securityName) else: usmUser = cfgTree.getAttrValue('snmp-usm-user', *configEntryPath) securityEngineId = cfgTree.getAttrValue( 'snmp-security-engine-id', *configEntryPath, default=None) if securityEngineId: securityEngineId = rfc1902.OctetString(securityEngineId) log.info( 'new USM user %s, security-model %s, security-level %s, ' 'security-name %s, security-engine-id %s' % (usmUser, securityModel, securityLevel, securityName, securityEngineId and securityEngineId.prettyPrint() or '<none>')) if securityLevel in (2, 3): usmAuthProto = cfgTree.getAttrValue( 'snmp-usm-auth-protocol', *configEntryPath, default=config.USM_AUTH_HMAC96_MD5) try: usmAuthProto = authProtocols[usmAuthProto.upper()] except KeyError: pass usmAuthProto = rfc1902.ObjectName(usmAuthProto) usmAuthKey = cfgTree.getAttrValue('snmp-usm-auth-key', *configEntryPath) log.info( 'new USM authentication key: %s, authentication protocol: %s' % (usmAuthKey, usmAuthProto)) if securityLevel == 3: usmPrivProto = cfgTree.getAttrValue( 'snmp-usm-priv-protocol', *configEntryPath, default=config.USM_PRIV_CBC56_DES) try: usmPrivProto = privProtocols[usmPrivProto.upper()] except KeyError: pass usmPrivProto = rfc1902.ObjectName(usmPrivProto) usmPrivKey = cfgTree.getAttrValue('snmp-usm-priv-key', *configEntryPath, default=None) log.info( 'new USM encryption key: %s, encryption protocol: %s' % (usmPrivKey, usmPrivProto)) config.addV3User(snmpEngine, usmUser, usmAuthProto, usmAuthKey, usmPrivProto, usmPrivKey, securityEngineId=securityEngineId) else: config.addV3User(snmpEngine, usmUser, usmAuthProto, usmAuthKey, securityEngineId=securityEngineId) else: config.addV3User(snmpEngine, usmUser, securityEngineId=securityEngineId) snmpEngineMap['securityName'][securityName] = securityModel configKey.append(securityModel) configKey.append(securityLevel) configKey.append(securityName) else: raise SnmpResponderError('unknown snmp-security-model: %s' % securityModel) configKey = tuple(configKey) if configKey in credIdMap: log.error( 'ambiguous configuration for key snmp-credentials-id=%s at %s' % (credId, '.'.join(configEntryPath))) return credIdMap[configKey] = credId duplicates = {} for peerCfgPath in cfgTree.getPathsToAttr('snmp-peer-id'): peerId = cfgTree.getAttrValue('snmp-peer-id', *peerCfgPath) if peerId in duplicates: log.error( 'duplicate snmp-peer-id=%s at %s and %s' % (peerId, '.'.join(peerCfgPath), '.'.join(duplicates[peerId]))) return duplicates[peerId] = peerCfgPath log.info('configuring peer ID %s (at %s)...' % (peerId, '.'.join(peerCfgPath))) transportDomain = cfgTree.getAttrValue('snmp-transport-domain', *peerCfgPath) if transportDomain not in peerIdMap: peerIdMap[transportDomain] = [] for peerAddress in cfgTree.getAttrValue( 'snmp-peer-address-pattern-list', *peerCfgPath, vector=True): for bindAddress in cfgTree.getAttrValue( 'snmp-bind-address-pattern-list', *peerCfgPath, vector=True): peerIdMap[transportDomain].append( (re.compile(peerAddress + '#' + bindAddress), peerId)) duplicates = {} for contextCfgPath in cfgTree.getPathsToAttr('snmp-context-id'): contextId = cfgTree.getAttrValue('snmp-context-id', *contextCfgPath) if contextId in duplicates: log.error('duplicate snmp-context-id=%s at %s and %s' % (contextId, '.'.join(contextCfgPath), '.'.join( duplicates[contextId]))) return duplicates[contextId] = contextCfgPath k = '#'.join((cfgTree.getAttrValue('snmp-context-engine-id-pattern', *contextCfgPath), cfgTree.getAttrValue('snmp-context-name-pattern', *contextCfgPath))) log.info('configuring context ID %s (at %s), composite key: %s' % (contextId, '.'.join(contextCfgPath), k)) contextIdList.append((contextId, re.compile(k))) duplicates = {} for contentCfgPath in cfgTree.getPathsToAttr('snmp-content-id'): contentId = cfgTree.getAttrValue('snmp-content-id', *contentCfgPath) if contentId in duplicates: log.error('duplicate snmp-content-id=%s at %s and %s' % (contentId, '.'.join(contentCfgPath), '.'.join( duplicates[contentId]))) return duplicates[contentId] = contentCfgPath for x in cfgTree.getAttrValue('snmp-pdu-oid-prefix-pattern-list', *contentCfgPath, vector=True): k = '#'.join([ cfgTree.getAttrValue('snmp-pdu-type-pattern', *contentCfgPath), x ]) log.info('configuring content ID %s (at %s), composite key: %s' % (contentId, '.'.join(contentCfgPath), k)) contentIdList.append((contentId, re.compile(k))) del duplicates for pluginCfgPath in cfgTree.getPathsToAttr('using-plugin-id-list'): pluginIdList = cfgTree.getAttrValue('using-plugin-id-list', *pluginCfgPath, vector=True) log.info('configuring plugin ID(s) %s (at %s)...' % (','.join(pluginIdList), '.'.join(pluginCfgPath))) for credId in cfgTree.getAttrValue('matching-snmp-credentials-id-list', *pluginCfgPath, vector=True): for peerId in cfgTree.getAttrValue('matching-snmp-peer-id-list', *pluginCfgPath, vector=True): for contextId in cfgTree.getAttrValue( 'matching-snmp-context-id-list', *pluginCfgPath, vector=True): for contentId in cfgTree.getAttrValue( 'matching-snmp-content-id-list', *pluginCfgPath, vector=True): k = credId, contextId, peerId, contentId if k in pluginIdMap: log.error( 'duplicate snmp-credentials-id %s, snmp-context-id %s, snmp-peer-id %s, snmp-content-id %s at plugin-id(s) %s' % (credId, contextId, peerId, contentId, ','.join(pluginIdList))) return else: log.info( 'configuring plugin(s) %s (at %s), composite key: %s' % (','.join(pluginIdList), '.'.join(pluginCfgPath), '/'.join(k))) for pluginId in pluginIdList: if not pluginManager.hasPlugin(pluginId): log.error( 'undefined plugin ID %s referenced at %s' % (pluginId, '.'.join(pluginCfgPath))) return pluginIdMap[k] = pluginIdList for routeCfgPath in cfgTree.getPathsToAttr('using-mib-tree-id'): mibTreeId = cfgTree.getAttrValue('using-mib-tree-id', *routeCfgPath) log.info('configuring destination MIB tree ID(s) %s (at %s)...' % (mibTreeId, '.'.join(routeCfgPath))) for credId in cfgTree.getAttrValue('matching-snmp-credentials-id-list', *routeCfgPath, vector=True): for peerId in cfgTree.getAttrValue('matching-snmp-peer-id-list', *routeCfgPath, vector=True): for contextId in cfgTree.getAttrValue( 'matching-snmp-context-id-list', *routeCfgPath, vector=True): for contentId in cfgTree.getAttrValue( 'matching-snmp-content-id-list', *routeCfgPath, vector=True): k = credId, contextId, peerId, contentId if k in routingMap: log.error( 'duplicate snmp-credentials-id %s, snmp-context-id %s, snmp-peer-id %s, snmp-content-id %s at mib-tree-id(s) %s' % (credId, contextId, peerId, contentId, ','.join(mibTreeIdList))) return else: routingMap[k] = mibTreeId log.info( 'configuring MIB tree routing to %s (at %s), composite key: %s' % (mibTreeId, '.'.join(routeCfgPath), '/'.join(k))) for mibTreeCfgPath in cfgTree.getPathsToAttr('mib-tree-id'): mibTreeId = cfgTree.getAttrValue('mib-tree-id', *mibTreeCfgPath) log.info('configuring MIB tree ID %s (at %s)...' % (mibTreeId, '.'.join(mibTreeCfgPath))) mibTextPaths = cfgTree.getAttrValue('mib-text-search-path-list', *mibTreeCfgPath, default=[], vector=True) mibCodePatternPaths = macro.expandMacros( cfgTree.getAttrValue('mib-code-modules-pattern-list', *mibTreeCfgPath, default=[], vector=True), {'config-dir': os.path.dirname(cfgFile)}) mibBuilder = builder.MibBuilder() compiler.addMibCompiler(mibBuilder, sources=mibTextPaths) for topDir in mibCodePatternPaths: filenameRegExp = re.compile(os.path.basename(topDir)) topDir = os.path.dirname(topDir) for root, dirs, files in os.walk(topDir): if not files or root.endswith('__pycache__'): continue mibBuilder.setMibSources(builder.DirMibSource(root), *mibBuilder.getMibSources()) for filename in files: if not filenameRegExp.match(filename): log.debug( 'skipping non-matching file %s while loading ' 'MIB tree ID %s' % (filename, mibTreeId)) continue module, _ = os.path.splitext(filename) try: mibBuilder.loadModule(module) except PySnmpError as ex: log.error('fail to load MIB implementation from file ' '%s into MIB tree ID %s' % (os.path.join(root, filename), mibTreeId)) raise SnmpResponderError(str(ex)) log.info('loaded MIB implementation file %s into MIB tree ' 'ID %s' % (os.path.join(root, filename), mibTreeId)) mibCodePackages = macro.expandMacros( cfgTree.getAttrValue('mib-code-packages-pattern-list', *mibTreeCfgPath, default=[], vector=True), {'config-dir': os.path.dirname(cfgFile)}) for mibCodePackage in mibCodePackages: mibCodePackageRegExp = re.compile(mibCodePackage) for entryPoint in pkg_resources.iter_entry_points( 'snmpresponder.mibs'): log.debug('found extension entry point %s' % entryPoint.name) mibPackage = entryPoint.load() root = os.path.dirname(mibPackage.__file__) mibPathSet = False for filename in os.listdir(root): if filename.startswith('__init__'): continue if not os.path.isfile(os.path.join(root, filename)): continue mibPath = '.'.join((entryPoint.name, filename)) if not mibCodePackageRegExp.match(mibPath): log.debug( 'extension MIB %s from %s is NOT configured, ' 'skipping' % (mibPath, entryPoint.name)) continue if not mibPathSet: mibBuilder.setMibSources(builder.DirMibSource(root), *mibBuilder.getMibSources()) mibPathSet = True log.debug('loading extension MIB %s from %s into MIB tree ' 'ID %s' % (mibPath, entryPoint.name, mibTreeId)) module, _ = os.path.splitext(filename) try: mibBuilder.loadModule(module) except PySnmpError as ex: log.error('fail to load MIB implementation %s from ' '%s into MIB tree ID %s' % (mibPath, entryPoint.name, mibTreeId)) raise SnmpResponderError(str(ex)) log.info( 'loaded MIB implementation %s from %s into MIB tree ' 'ID %s' % (mibPath, entryPoint.name, mibTreeId)) mibTreeIdMap[mibTreeId] = instrum.MibInstrumController(mibBuilder) log.info('loaded new MIB tree ID %s' % mibTreeId) if not foregroundFlag: try: daemon.daemonize(pidFile) except Exception: log.error('can not daemonize process: %s' % sys.exc_info()[1]) return # Run mainloop log.info('starting I/O engine...') transportDispatcher.jobStarted(1) # server job would never finish with daemon.PrivilegesOf(procUser, procGroup, final=True): while True: try: transportDispatcher.runDispatcher() except (PySnmpError, SnmpResponderError, socket.error): log.error(str(sys.exc_info()[1])) continue except Exception: transportDispatcher.closeDispatcher() raise
def main(): variation_module = None parser = argparse.ArgumentParser(description=DESCRIPTION) parser.add_argument( '-v', '--version', action='version', version=utils.TITLE) parser.add_argument( '--quiet', action='store_true', help='Do not print out informational messages') parser.add_argument( '--debug', choices=pysnmp_debug.flagMap, action='append', type=str, default=[], help='Enable one or more categories of SNMP debugging.') parser.add_argument( '--debug-asn1', choices=pyasn1_debug.FLAG_MAP, action='append', type=str, default=[], help='Enable one or more categories of ASN.1 debugging.') parser.add_argument( '--logging-method', type=lambda x: x.split(':'), metavar='=<%s[:args]>]' % '|'.join(log.METHODS_MAP), default='stderr', help='Logging method.') parser.add_argument( '--log-level', choices=log.LEVELS_MAP, type=str, default='info', help='Logging level.') v1arch_group = parser.add_argument_group('SNMPv1/v2c parameters') v1arch_group.add_argument( '--protocol-version', choices=['1', '2c'], default='2c', help='SNMPv1/v2c protocol version') v1arch_group.add_argument( '--community', type=str, default='public', help='SNMP community name') v3arch_group = parser.add_argument_group('SNMPv3 parameters') v3arch_group.add_argument( '--v3-user', metavar='<STRING>', type=functools.partial(_parse_sized_string, min_length=1), help='SNMPv3 USM user (security) name') v3arch_group.add_argument( '--v3-auth-key', type=_parse_sized_string, help='SNMPv3 USM authentication key (must be > 8 chars)') v3arch_group.add_argument( '--v3-auth-proto', choices=AUTH_PROTOCOLS, type=lambda x: x.upper(), default='NONE', help='SNMPv3 USM authentication protocol') v3arch_group.add_argument( '--v3-priv-key', type=_parse_sized_string, help='SNMPv3 USM privacy (encryption) key (must be > 8 chars)') v3arch_group.add_argument( '--v3-priv-proto', choices=PRIV_PROTOCOLS, type=lambda x: x.upper(), default='NONE', help='SNMPv3 USM privacy (encryption) protocol') v3arch_group.add_argument( '--v3-context-engine-id', type=lambda x: univ.OctetString(hexValue=x[2:]), help='SNMPv3 context engine ID') v3arch_group.add_argument( '--v3-context-name', type=str, default='', help='SNMPv3 context engine ID') parser.add_argument( '--use-getbulk', action='store_true', help='Use SNMP GETBULK PDU for mass SNMP managed objects retrieval') parser.add_argument( '--getbulk-repetitions', type=int, default=25, help='Use SNMP GETBULK PDU for mass SNMP managed objects retrieval') endpoint_group = parser.add_mutually_exclusive_group(required=True) endpoint_group.add_argument( '--agent-udpv4-endpoint', type=endpoints.parse_endpoint, metavar='<[X.X.X.X]:NNNNN>', help='SNMP agent UDP/IPv4 address to pull simulation data ' 'from (name:port)') endpoint_group.add_argument( '--agent-udpv6-endpoint', type=functools.partial(endpoints.parse_endpoint, ipv6=True), metavar='<[X:X:..X]:NNNNN>', help='SNMP agent UDP/IPv6 address to pull simulation data ' 'from ([name]:port)') parser.add_argument( '--timeout', type=int, default=3, help='SNMP command response timeout (in seconds)') parser.add_argument( '--retries', type=int, default=3, help='SNMP command retries') parser.add_argument( '--start-object', metavar='<MIB::Object|OID>', type=_parse_mib_object, default=univ.ObjectIdentifier('1.3.6'), help='Drop all simulation data records prior to this OID specified ' 'as MIB object (MIB::Object) or OID (1.3.6.)') parser.add_argument( '--stop-object', metavar='<MIB::Object|OID>', type=functools.partial(_parse_mib_object, last=True), help='Drop all simulation data records after this OID specified ' 'as MIB object (MIB::Object) or OID (1.3.6.)') parser.add_argument( '--mib-source', dest='mib_sources', metavar='<URI|PATH>', action='append', type=str, default=['http://mibs.snmplabs.com/asn1/@mib@'], help='One or more URIs pointing to a collection of ASN.1 MIB files.' 'Optional "@mib@" token gets replaced with desired MIB module ' 'name during MIB search.') parser.add_argument( '--destination-record-type', choices=variation.RECORD_TYPES, default='snmprec', help='Produce simulation data with record of this type') parser.add_argument( '--output-file', metavar='<FILE>', type=str, help='SNMP simulation data file to write records to') parser.add_argument( '--continue-on-errors', metavar='<tolerance-level>', type=int, default=0, help='Keep on pulling SNMP data even if intermittent errors occur') variation_group = parser.add_argument_group( 'Simulation data variation options') parser.add_argument( '--variation-modules-dir', action='append', type=str, help='Search variation module by this path') variation_group.add_argument( '--variation-module', type=str, help='Pass gathered simulation data through this variation module') variation_group.add_argument( '--variation-module-options', type=str, default='', help='Variation module options') args = parser.parse_args() if args.debug: pysnmp_debug.setLogger(pysnmp_debug.Debug(*args.debug)) if args.debug_asn1: pyasn1_debug.setLogger(pyasn1_debug.Debug(*args.debug_asn1)) if args.output_file: ext = os.path.extsep ext += variation.RECORD_TYPES[args.destination_record_type].ext if not args.output_file.endswith(ext): args.output_file += ext record = variation.RECORD_TYPES[args.destination_record_type] args.output_file = record.open(args.output_file, 'wb') else: args.output_file = sys.stdout if sys.version_info >= (3, 0, 0): # binary mode write args.output_file = sys.stdout.buffer elif sys.platform == "win32": import msvcrt msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) # Catch missing params if args.protocol_version == '3': if not args.v3_user: sys.stderr.write('ERROR: --v3-user is missing\r\n') parser.print_usage(sys.stderr) return 1 if args.v3_priv_key and not args.v3_auth_key: sys.stderr.write('ERROR: --v3-auth-key is missing\r\n') parser.print_usage(sys.stderr) return 1 if AUTH_PROTOCOLS[args.v3_auth_proto] == config.usmNoAuthProtocol: if args.v3_auth_key: args.v3_auth_proto = 'MD5' else: if not args.v3_auth_key: sys.stderr.write('ERROR: --v3-auth-key is missing\r\n') parser.print_usage(sys.stderr) return 1 if PRIV_PROTOCOLS[args.v3_priv_proto] == config.usmNoPrivProtocol: if args.v3_priv_key: args.v3_priv_proto = 'DES' else: if not args.v3_priv_key: sys.stderr.write('ERROR: --v3-priv-key is missing\r\n') parser.print_usage(sys.stderr) return 1 proc_name = os.path.basename(sys.argv[0]) try: log.set_logger(proc_name, *args.logging_method, force=True) if args.log_level: log.set_level(args.log_level) except error.SnmpsimError as exc: sys.stderr.write('%s\r\n' % exc) parser.print_usage(sys.stderr) return 1 if args.use_getbulk and args.protocol_version == '1': log.info('will be using GETNEXT with SNMPv1!') args.use_getbulk = False # Load variation module if args.variation_module: for variation_modules_dir in ( args.variation_modules_dir or confdir.variation): log.info( 'Scanning "%s" directory for variation ' 'modules...' % variation_modules_dir) if not os.path.exists(variation_modules_dir): log.info('Directory "%s" does not exist' % variation_modules_dir) continue mod = os.path.join(variation_modules_dir, args.variation_module + '.py') if not os.path.exists(mod): log.info('Variation module "%s" not found' % mod) continue ctx = {'path': mod, 'moduleContext': {}} try: with open(mod) as fl: exec (compile(fl.read(), mod, 'exec'), ctx) except Exception as exc: log.error('Variation module "%s" execution failure: ' '%s' % (mod, exc)) return 1 variation_module = ctx log.info('Variation module "%s" loaded' % args.variation_module) break else: log.error('variation module "%s" not found' % args.variation_module) return 1 # SNMP configuration snmp_engine = engine.SnmpEngine() if args.protocol_version == '3': if args.v3_priv_key is None and args.v3_auth_key is None: secLevel = 'noAuthNoPriv' elif args.v3_priv_key is None: secLevel = 'authNoPriv' else: secLevel = 'authPriv' config.addV3User( snmp_engine, args.v3_user, AUTH_PROTOCOLS[args.v3_auth_proto], args.v3_auth_key, PRIV_PROTOCOLS[args.v3_priv_proto], args.v3_priv_key) log.info( 'SNMP version 3, Context EngineID: %s Context name: %s, SecurityName: %s, ' 'SecurityLevel: %s, Authentication key/protocol: %s/%s, Encryption ' '(privacy) key/protocol: ' '%s/%s' % ( args.v3_context_engine_id and args.v3_context_engine_id.prettyPrint() or '<default>', args.v3_context_name and args.v3_context_name.prettyPrint() or '<default>', args.v3_user, secLevel, args.v3_auth_key is None and '<NONE>' or args.v3_auth_key, args.v3_auth_proto, args.v3_priv_key is None and '<NONE>' or args.v3_priv_key, args.v3_priv_proto)) else: args.v3_user = '******' secLevel = 'noAuthNoPriv' config.addV1System(snmp_engine, args.v3_user, args.community) log.info( 'SNMP version %s, Community name: ' '%s' % (args.protocol_version, args.community)) config.addTargetParams( snmp_engine, 'pms', args.v3_user, secLevel, VERSION_MAP[args.protocol_version]) if args.agent_udpv6_endpoint: config.addSocketTransport( snmp_engine, udp6.domainName, udp6.Udp6SocketTransport().openClientMode()) config.addTargetAddr( snmp_engine, 'tgt', udp6.domainName, args.agent_udpv6_endpoint, 'pms', args.timeout * 100, args.retries) log.info('Querying UDP/IPv6 agent at [%s]:%s' % args.agent_udpv6_endpoint) elif args.agent_udpv4_endpoint: config.addSocketTransport( snmp_engine, udp.domainName, udp.UdpSocketTransport().openClientMode()) config.addTargetAddr( snmp_engine, 'tgt', udp.domainName, args.agent_udpv4_endpoint, 'pms', args.timeout * 100, args.retries) log.info('Querying UDP/IPv4 agent at %s:%s' % args.agent_udpv4_endpoint) log.info('Agent response timeout: %d secs, retries: ' '%s' % (args.timeout, args.retries)) if (isinstance(args.start_object, ObjectIdentity) or isinstance(args.stop_object, ObjectIdentity)): compiler.addMibCompiler( snmp_engine.getMibBuilder(), sources=args.mib_sources) mib_view_controller = view.MibViewController( snmp_engine.getMibBuilder()) try: if isinstance(args.start_object, ObjectIdentity): args.start_object.resolveWithMib(mib_view_controller) if isinstance(args.stop_object, ObjectIdentity): args.stop_object.resolveWithMib(mib_view_controller) except PySnmpError as exc: sys.stderr.write('ERROR: %s\r\n' % exc) return 1 # Variation module initialization if variation_module: log.info('Initializing variation module...') for x in ('init', 'record', 'shutdown'): if x not in variation_module: log.error('missing "%s" handler at variation module ' '"%s"' % (x, args.variation_module)) return 1 try: handler = variation_module['init'] handler(snmpEngine=snmp_engine, options=args.variation_module_options, mode='recording', startOID=args.start_object, stopOID=args.stop_object) except Exception as exc: log.error( 'Variation module "%s" initialization FAILED: ' '%s' % (args.variation_module, exc)) else: log.info( 'Variation module "%s" initialization OK' % args.variation_module) data_file_handler = variation.RECORD_TYPES[args.destination_record_type] # SNMP worker def cbFun(snmp_engine, send_request_handle, error_indication, error_status, error_index, var_bind_table, cb_ctx): if error_indication and not cb_ctx['retries']: cb_ctx['errors'] += 1 log.error('SNMP Engine error: %s' % error_indication) return # SNMPv1 response may contain noSuchName error *and* SNMPv2c exception, # so we ignore noSuchName error here if error_status and error_status != 2 or error_indication: log.error( 'Remote SNMP error %s' % ( error_indication or error_status.prettyPrint())) if cb_ctx['retries']: try: next_oid = var_bind_table[-1][0][0] except IndexError: next_oid = cb_ctx['lastOID'] else: log.error('Failed OID: %s' % next_oid) # fuzzy logic of walking a broken OID if len(next_oid) < 4: pass elif (args.continue_on_errors - cb_ctx['retries']) * 10 / args.continue_on_errors > 5: next_oid = next_oid[:-2] + (next_oid[-2] + 1,) elif next_oid[-1]: next_oid = next_oid[:-1] + (next_oid[-1] + 1,) else: next_oid = next_oid[:-2] + (next_oid[-2] + 1, 0) cb_ctx['retries'] -= 1 cb_ctx['lastOID'] = next_oid log.info( 'Retrying with OID %s (%s retries left)' '...' % (next_oid, cb_ctx['retries'])) # initiate another SNMP walk iteration if args.use_getbulk: cmd_gen.sendVarBinds( snmp_engine, 'tgt', args.v3_context_engine_id, args.v3_context_name, 0, args.getbulk_repetitions, [(next_oid, None)], cbFun, cb_ctx) else: cmd_gen.sendVarBinds( snmp_engine, 'tgt', args.v3_context_engine_id, args.v3_context_name, [(next_oid, None)], cbFun, cb_ctx) cb_ctx['errors'] += 1 return if args.continue_on_errors != cb_ctx['retries']: cb_ctx['retries'] += 1 if var_bind_table and var_bind_table[-1] and var_bind_table[-1][0]: cb_ctx['lastOID'] = var_bind_table[-1][0][0] stop_flag = False # Walk var-binds for var_bind_row in var_bind_table: for oid, value in var_bind_row: # EOM if args.stop_object and oid >= args.stop_object: stop_flag = True # stop on out of range condition elif (value is None or value.tagSet in (rfc1905.NoSuchObject.tagSet, rfc1905.NoSuchInstance.tagSet, rfc1905.EndOfMibView.tagSet)): stop_flag = True # remove value enumeration if value.tagSet == rfc1902.Integer32.tagSet: value = rfc1902.Integer32(value) if value.tagSet == rfc1902.Unsigned32.tagSet: value = rfc1902.Unsigned32(value) if value.tagSet == rfc1902.Bits.tagSet: value = rfc1902.OctetString(value) # Build .snmprec record context = { 'origOid': oid, 'origValue': value, 'count': cb_ctx['count'], 'total': cb_ctx['total'], 'iteration': cb_ctx['iteration'], 'reqTime': cb_ctx['reqTime'], 'args.start_object': args.start_object, 'stopOID': args.stop_object, 'stopFlag': stop_flag, 'variationModule': variation_module } try: line = data_file_handler.format(oid, value, **context) except error.MoreDataNotification as exc: cb_ctx['count'] = 0 cb_ctx['iteration'] += 1 more_data_notification = exc if 'period' in more_data_notification: log.info( '%s OIDs dumped, waiting %.2f sec(s)' '...' % (cb_ctx['total'], more_data_notification['period'])) time.sleep(more_data_notification['period']) # initiate another SNMP walk iteration if args.use_getbulk: cmd_gen.sendVarBinds( snmp_engine, 'tgt', args.v3_context_engine_id, args.v3_context_name, 0, args.getbulk_repetitions, [(args.start_object, None)], cbFun, cb_ctx) else: cmd_gen.sendVarBinds( snmp_engine, 'tgt', args.v3_context_engine_id, args.v3_context_name, [(args.start_object, None)], cbFun, cb_ctx) stop_flag = True # stop current iteration except error.NoDataNotification: pass except error.SnmpsimError as exc: log.error(exc) continue else: args.output_file.write(line) cb_ctx['count'] += 1 cb_ctx['total'] += 1 if cb_ctx['count'] % 100 == 0: log.info('OIDs dumped: %s/%s' % ( cb_ctx['iteration'], cb_ctx['count'])) # Next request time cb_ctx['reqTime'] = time.time() # Continue walking return not stop_flag cb_ctx = { 'total': 0, 'count': 0, 'errors': 0, 'iteration': 0, 'reqTime': time.time(), 'retries': args.continue_on_errors, 'lastOID': args.start_object } if args.use_getbulk: cmd_gen = cmdgen.BulkCommandGenerator() cmd_gen.sendVarBinds( snmp_engine, 'tgt', args.v3_context_engine_id, args.v3_context_name, 0, args.getbulk_repetitions, [(args.start_object, rfc1902.Null(''))], cbFun, cb_ctx) else: cmd_gen = cmdgen.NextCommandGenerator() cmd_gen.sendVarBinds( snmp_engine, 'tgt', args.v3_context_engine_id, args.v3_context_name, [(args.start_object, rfc1902.Null(''))], cbFun, cb_ctx) log.info( 'Sending initial %s request for %s (stop at %s)' '....' % (args.use_getbulk and 'GETBULK' or 'GETNEXT', args.start_object, args.stop_object or '<end-of-mib>')) started = time.time() try: snmp_engine.transportDispatcher.runDispatcher() except KeyboardInterrupt: log.info('Shutting down process...') finally: if variation_module: log.info('Shutting down variation module ' '%s...' % args.variation_module) try: handler = variation_module['shutdown'] handler(snmpEngine=snmp_engine, options=args.variation_module_options, mode='recording') except Exception as exc: log.error( 'Variation module %s shutdown FAILED: ' '%s' % (args.variation_module, exc)) else: log.info( 'Variation module %s shutdown OK' % args.variation_module) snmp_engine.transportDispatcher.closeDispatcher() started = time.time() - started cb_ctx['total'] += cb_ctx['count'] log.info( 'OIDs dumped: %s, elapsed: %.2f sec, rate: %.2f OIDs/sec, errors: ' '%d' % (cb_ctx['total'], started, started and cb_ctx['count'] // started or 0, cb_ctx['errors'])) args.output_file.flush() args.output_file.close() return cb_ctx.get('errors', 0) and 1 or 0
* with default Uptime * with default Agent Address * with Enterprise OID 1.3.6.1.4.1.20408.4.1.1.2 * include managed object information '1.3.6.1.2.1.1.1.0' = 'my system' Functionally similar to: | $ snmptrap -v1 -c public demo.snmplabs.com 1.3.6.1.4.1.20408.4.1.1.2 0.0.0.0 1 0 0 1.3.6.1.2.1.1.1.0 s "my system" """# from pysnmp.hlapi import * from pysnmp import debug # use specific flags or 'all' for full debugging debug.setLogger(debug.Debug('all')) errorIndication, errorStatus, errorIndex, varBinds = next( sendNotification(SnmpEngine(), CommunityData('public', mpModel=0), UdpTransportTarget(('demo.snmplabs.com', 162)), ContextData(), 'trap', NotificationType( ObjectIdentity('1.3.6.1.6.3.1.1.5.2') ).addVarBinds( ('1.3.6.1.6.3.1.1.4.3.0', '1.3.6.1.4.1.20408.4.1.1.2'), ('1.3.6.1.2.1.1.1.0', OctetString('my system')) ) )
* with SNMPv1, community 'public' * over IPv4/UDP * to an Agent at demo.snmplabs.com:161 * for two instances of SNMPv2-MIB::sysDescr.0 MIB object, Functionally similar to: | $ snmpget -v1 -c public demo.snmplabs.com SNMPv2-MIB::sysDescr.0 """# from pysnmp.hlapi import * from pysnmp import debug # use specific flags or 'all' for full debugging debug.setLogger(debug.Debug('dsp', 'msgproc')) errorIndication, errorStatus, errorIndex, varBinds = next( getCmd(SnmpEngine(), CommunityData('public', mpModel=0), UdpTransportTarget(('demo.snmplabs.com', 161)), ContextData(), ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))) ) if errorIndication: print(errorIndication) elif errorStatus: print('%s at %s' % ( errorStatus.prettyPrint(), errorIndex and varBinds[int(errorIndex)-1][0] or '?'
import pyasn1 sys.stderr.write("""\ SNMP Simulator version %s, written by Ilya Etingof <*****@*****.**> Using foundation libraries: pysmi %s, pysnmp %s, pyasn1 %s. Python interpreter: %s Software documentation and support at http://snmplabs.com/snmpsim %s """ % (snmpsim.__version__, hasattr(pysmi, '__version__') and pysmi.__version__ or 'unknown', hasattr(pysnmp, '__version__') and pysnmp.__version__ or 'unknown', hasattr(pyasn1, '__version__') and pyasn1.__version__ or 'unknown', sys.version, helpMessage)) sys.exit(-1) elif opt[0] in ('--debug', '--debug-snmp'): pysnmp_debug.setLogger( pysnmp_debug.Debug(*opt[1].split(','), **dict(loggerName='pcap2dev.pysnmp'))) elif opt[0] == '--debug-asn1': pyasn1_debug.setLogger( pyasn1_debug.Debug(*opt[1].split(','), **dict(loggerName='pcap2dev.pyasn1'))) elif opt[0] == '--logging-method': loggingMethod = opt[1].split(':') elif opt[0] == '--log-level': loggingLevel = opt[1] if opt[0] == '--quiet': verboseFlag = False # obsolete begin elif opt[0] == '--start-oid': startOID = univ.ObjectIdentifier(opt[1]) elif opt[0] == '--stop-oid': stopOID = univ.ObjectIdentifier(opt[1])
def main(): class LogString(LazyLogString): GROUPINGS = [ ['callflow-id'], ['trunk-id'], [ 'server-snmp-engine-id', 'server-snmp-transport-domain', 'server-snmp-peer-address', 'server-snmp-peer-port', 'server-snmp-bind-address', 'server-snmp-bind-port', 'server-snmp-security-model', 'server-snmp-security-level', 'server-snmp-security-name', 'server-snmp-context-engine-id', 'server-snmp-context-name', 'server-snmp-pdu', 'server-snmp-entity-id' ], [ 'server-snmp-credentials-id', 'server-snmp-context-id', 'server-snmp-content-id', 'server-snmp-peer-id', 'server-classification-id' ], [ 'snmp-peer-id', 'snmp-bind-address', 'snmp-bind-port', 'snmp-peer-address', 'snmp-peer-port', 'snmp-context-engine-id', 'snmp-context-name', 'snmp-pdu' ], ] FORMATTERS = { 'server-snmp-pdu': LazyLogString.prettyVarBinds, 'snmp-pdu': LazyLogString.prettyVarBinds, } def snmpCbFun(snmpEngine, sendRequestHandle, errorIndication, rspPDU, cbCtx): trunkId, msgId, trunkReq, pluginIdList, reqCtx = cbCtx trunkRsp = { 'callflow-id': trunkReq['callflow-id'], 'snmp-pdu': rspPDU, } logCtx = LogString(trunkRsp) if errorIndication: log.info('received SNMP error-indication "%s"' % errorIndication, ctx=logCtx) trunkRsp['error-indication'] = errorIndication if rspPDU: reqPdu = trunkReq['server-snmp-pdu'] for pluginId in pluginIdList: if reqPdu.tagSet in rfc3411.notificationClassPDUs: st, rspPDU = pluginManager.processNotificationResponse( pluginId, snmpEngine, rspPDU, trunkReq, reqCtx) elif reqPdu.tagSet not in rfc3411.unconfirmedClassPDUs: st, rspPDU = pluginManager.processCommandResponse( pluginId, snmpEngine, rspPDU, trunkReq, reqCtx) else: log.error('ignoring unsupported PDU', ctx=logCtx) break if st == status.BREAK: log.debug('plugin %s inhibits other plugins' % pluginId, ctx=logCtx) break elif st == status.DROP: log.debug( 'received SNMP %s, plugin %s muted response' % (errorIndication and 'error' or 'response', pluginId), ctx=logCtx) trunkRsp['snmp-pdu'] = None break try: trunkingManager.sendRsp(trunkId, msgId, trunkRsp) except SnmpfwdError: log.error('received SNMP %s message, trunk message not sent "%s"' % (msgId, sys.exc_info()[1]), ctx=logCtx) return log.debug('received SNMP %s message, forwarded as trunk message #%s' % (errorIndication and 'error' or 'response', msgId), ctx=logCtx) # # The following needs proper support in pysnmp. Meanwhile - monkey patching! # def makeTargetAddrOverride(targetAddr): endpoints = [] def getTargetAddr(snmpEngine, snmpTargetAddrName): addrInfo = list(targetAddr(snmpEngine, snmpTargetAddrName)) if endpoints: peerAddr, bindAddr = endpoints.pop(), endpoints.pop() try: addrInfo[1] = addrInfo[1].__class__( peerAddr).setLocalAddress(bindAddr) except Exception: raise PySnmpError( 'failure replacing bind address %s -> %s for transport ' 'domain %s' % (addrInfo[1], bindAddr, addrInfo[0])) return addrInfo def updateEndpoints(bindAddr, peerAddr): endpoints.extend((bindAddr, peerAddr)) return getTargetAddr, updateEndpoints lcd.getTargetAddr, updateEndpoints = makeTargetAddrOverride( lcd.getTargetAddr) def trunkCbFun(trunkId, msgId, trunkReq): for key in tuple(trunkReq): if key != 'callflow-id': trunkReq['server-' + key] = trunkReq[key] del trunkReq[key] trunkReq['trunk-id'] = trunkId k = [ str(x) for x in (trunkReq['server-snmp-engine-id'], trunkReq['server-snmp-transport-domain'], trunkReq['server-snmp-peer-address'] + ':' + str(trunkReq['server-snmp-peer-port']), trunkReq['server-snmp-bind-address'] + ':' + str(trunkReq['server-snmp-bind-port']), trunkReq['server-snmp-security-model'], trunkReq['server-snmp-security-level'], trunkReq['server-snmp-security-name'], trunkReq['server-snmp-context-engine-id'], trunkReq['server-snmp-context-name']) ] k.append(snmpPduTypesMap.get(trunkReq['server-snmp-pdu'].tagSet, '?')) k.append('|'.join([ str(x[0]) for x in v2c.apiPDU.getVarBinds(trunkReq['server-snmp-pdu']) ])) k = '#'.join(k) for x, y in origCredIdList: if y.match(k): origPeerId = trunkReq[ 'server-snmp-entity-id'] = macro.expandMacro(x, trunkReq) break else: origPeerId = None k = [ str(x) for x in (trunkReq['server-snmp-credentials-id'], trunkReq['server-snmp-context-id'], trunkReq['server-snmp-content-id'], trunkReq['server-snmp-peer-id']) ] k = '#'.join(k) for x, y in srvClassIdList: if y.match(k): srvClassId = trunkReq[ 'server-classification-id'] = macro.expandMacro( x, trunkReq) break else: srvClassId = None logCtx = LogString(trunkReq) errorIndication = None peerIdList = routingMap.get( (origPeerId, srvClassId, macro.expandMacro(trunkId, trunkReq))) if not peerIdList: log.error('unroutable trunk message #%s' % msgId, ctx=logCtx) errorIndication = 'no route to SNMP peer configured' cbCtx = trunkId, msgId, trunkReq, (), {} if errorIndication: snmpCbFun(None, None, errorIndication, None, cbCtx) return pluginIdList = pluginIdMap.get( (origPeerId, srvClassId, macro.expandMacro(trunkId, trunkReq))) for peerId in peerIdList: peerId = macro.expandMacro(peerId, trunkReq) trunkReqCopy = trunkReq.copy() (snmpEngine, contextEngineId, contextName, bindAddr, bindAddrMacro, peerAddr, peerAddrMacro) = peerIdMap[peerId] if bindAddrMacro: bindAddr = macro.expandMacro(bindAddrMacro, trunkReqCopy), 0 if peerAddrMacro: peerAddr = macro.expandMacro(peerAddrMacro, trunkReqCopy), 161 if bindAddr and peerAddr: updateEndpoints(bindAddr, peerAddr) trunkReqCopy['snmp-peer-id'] = peerId trunkReqCopy['snmp-context-engine-id'] = contextEngineId trunkReqCopy['snmp-context-name'] = contextName trunkReqCopy['snmp-bind-address'], trunkReqCopy[ 'snmp-bind-port'] = bindAddr trunkReqCopy['snmp-peer-address'], trunkReqCopy[ 'snmp-peer-port'] = peerAddr logCtx.update(trunkReqCopy) pdu = trunkReqCopy['server-snmp-pdu'] if pluginIdList: reqCtx = {} cbCtx = trunkId, msgId, trunkReqCopy, pluginIdList, reqCtx for pluginNum, pluginId in enumerate(pluginIdList): if pdu.tagSet in rfc3411.notificationClassPDUs: st, pdu = pluginManager.processNotificationRequest( pluginId, snmpEngine, pdu, trunkReqCopy, reqCtx) elif pdu.tagSet not in rfc3411.unconfirmedClassPDUs: st, pdu = pluginManager.processCommandRequest( pluginId, snmpEngine, pdu, trunkReqCopy, reqCtx) else: log.error('ignoring unsupported PDU', ctx=logCtx) break if st == status.BREAK: log.debug('plugin %s inhibits other plugins' % pluginId, ctx=logCtx) cbCtx = trunkId, msgId, trunkReqCopy, pluginIdList[: pluginNum], reqCtx break elif st == status.DROP: log.debug( 'received trunk message #%s, plugin %s muted request' % (msgId, pluginId), ctx=logCtx) snmpCbFun(snmpEngine, None, None, None, cbCtx) return elif st == status.RESPOND: log.debug( 'received trunk message #%s, plugin %s forced immediate response' % (msgId, pluginId), ctx=logCtx) snmpCbFun(snmpEngine, None, None, pdu, cbCtx) return snmpMessageSent = False if pdu.tagSet in rfc3411.notificationClassPDUs: if pdu.tagSet in rfc3411.unconfirmedClassPDUs: try: notificationOriginator.sendPdu( snmpEngine, peerId, macro.expandMacro(contextEngineId, trunkReq), macro.expandMacro(contextName, trunkReq), pdu) snmpMessageSent = True except PySnmpError: errorIndication = 'failure sending SNMP notification' log.error('trunk message #%s, SNMP error: %s' % (msgId, sys.exc_info()[1]), ctx=logCtx) else: errorIndication = None # respond to trunk right away snmpCbFun(snmpEngine, None, errorIndication, None, cbCtx) else: try: notificationOriginator.sendPdu( snmpEngine, peerId, macro.expandMacro(contextEngineId, trunkReq), macro.expandMacro(contextName, trunkReq), pdu, snmpCbFun, cbCtx) snmpMessageSent = True except PySnmpError: log.error('trunk message #%s, SNMP error: %s' % (msgId, sys.exc_info()[1]), ctx=logCtx) elif pdu.tagSet not in rfc3411.unconfirmedClassPDUs: try: commandGenerator.sendPdu( snmpEngine, peerId, macro.expandMacro(contextEngineId, trunkReq), macro.expandMacro(contextName, trunkReq), pdu, snmpCbFun, cbCtx) snmpMessageSent = True except PySnmpError: errorIndication = 'failure sending SNMP command' log.error('trunk message #%s, SNMP error: %s' % (msgId, sys.exc_info()[1]), ctx=logCtx) # respond to trunk right away snmpCbFun(snmpEngine, None, errorIndication, None, cbCtx) else: log.error('ignoring unsupported PDU', ctx=logCtx) if snmpMessageSent: log.debug( 'received trunk message #%s, forwarded as SNMP message' % msgId, ctx=logCtx) # # Main script body starts here # helpMessage = """\ Usage: %s [--help] [--version ] [--debug-snmp=<%s>] [--debug-asn1=<%s>] [--daemonize] [--process-user=<uname>] [--process-group=<gname>] [--pid-file=<file>] [--logging-method=<%s[:args>]>] [--log-level=<%s>] [--config-file=<file>]""" % (sys.argv[0], '|'.join([ x for x in getattr(pysnmp_debug, 'FLAG_MAP', getattr(pysnmp_debug, 'flagMap', ())) if x != 'mibview' ]), '|'.join([ x for x in getattr(pyasn1_debug, 'FLAG_MAP', getattr(pyasn1_debug, 'flagMap', ())) ]), '|'.join(log.methodsMap), '|'.join(log.levelsMap)) try: opts, params = getopt.getopt(sys.argv[1:], 'hv', [ 'help', 'version', 'debug=', 'debug-snmp=', 'debug-asn1=', 'daemonize', 'process-user='******'process-group=', 'pid-file=', 'logging-method=', 'log-level=', 'config-file=' ]) except Exception: sys.stderr.write('ERROR: %s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage)) return if params: sys.stderr.write('ERROR: extra arguments supplied %s\r\n%s\r\n' % (params, helpMessage)) return pidFile = '' cfgFile = CONFIG_FILE foregroundFlag = True procUser = procGroup = None loggingMethod = ['stderr'] loggingLevel = None for opt in opts: if opt[0] == '-h' or opt[0] == '--help': sys.stderr.write("""\ Synopsis: SNMP Proxy Forwarder: client part. Receives SNMP PDUs via one or many encrypted trunks established with the Forwarder's Agent part(s) running elsewhere and routes PDUs to built-in SNMP Managers for further transmission towards SNMP Agents. Can implement complex routing and protocol conversion logic through analyzing parts of SNMP messages and matching them against proxying rules. Documentation: http://snmplabs.com/snmpfwd/ %s """ % helpMessage) return if opt[0] == '-v' or opt[0] == '--version': import snmpfwd import pysnmp import pyasn1 sys.stderr.write("""\ SNMP Proxy Forwarder version %s, written by Ilya Etingof <*****@*****.**> Using foundation libraries: pysnmp %s, pyasn1 %s. Python interpreter: %s Software documentation and support at http://snmplabs.com/snmpfwd/ %s """ % (snmpfwd.__version__, hasattr(pysnmp, '__version__') and pysnmp.__version__ or 'unknown', hasattr(pyasn1, '__version__') and pyasn1.__version__ or 'unknown', sys.version, helpMessage)) return elif opt[0] == '--debug-snmp': pysnmp_debug.setLogger( pysnmp_debug.Debug(*opt[1].split(','), **dict(loggerName=PROGRAM_NAME + '.pysnmp'))) elif opt[0] == '--debug-asn1': pyasn1_debug.setLogger( pyasn1_debug.Debug(*opt[1].split(','), **dict(loggerName=PROGRAM_NAME + '.pyasn1'))) elif opt[0] == '--daemonize': foregroundFlag = False elif opt[0] == '--process-user': procUser = opt[1] elif opt[0] == '--process-group': procGroup = opt[1] elif opt[0] == '--pid-file': pidFile = opt[1] elif opt[0] == '--logging-method': loggingMethod = opt[1].split(':') elif opt[0] == '--log-level': loggingLevel = opt[1] elif opt[0] == '--config-file': cfgFile = opt[1] with daemon.PrivilegesOf(procUser, procGroup): try: log.setLogger(PROGRAM_NAME, *loggingMethod, **dict(force=True)) if loggingLevel: log.setLevel(loggingLevel) except SnmpfwdError: sys.stderr.write('%s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage)) return try: cfgTree = cparser.Config().load(cfgFile) except SnmpfwdError: log.error('configuration parsing error: %s' % sys.exc_info()[1]) return if cfgTree.getAttrValue('program-name', '', default=None) != PROGRAM_NAME: log.error('config file %s does not match program name %s' % (cfgFile, PROGRAM_NAME)) return if cfgTree.getAttrValue('config-version', '', default=None) != CONFIG_VERSION: log.error( 'config file %s version is not compatible with program version %s' % (cfgFile, CONFIG_VERSION)) return random.seed() # # SNMPv3 CommandGenerator & NotificationOriginator implementation # origCredIdList = [] srvClassIdList = [] peerIdMap = {} pluginIdMap = {} routingMap = {} engineIdMap = {} commandGenerator = cmdgen.CommandGenerator() notificationOriginator = ntforg.NotificationOriginator() transportDispatcher = AsynsockDispatcher() transportDispatcher.registerRoutingCbFun(lambda td, t, d: td) transportDispatcher.setSocketMap() # use global asyncore socket map pluginManager = PluginManager(macro.expandMacros( cfgTree.getAttrValue('plugin-modules-path-list', '', default=[], vector=True), {'config-dir': os.path.dirname(cfgFile)}), progId=PROGRAM_NAME, apiVer=PLUGIN_API_VERSION) for pluginCfgPath in cfgTree.getPathsToAttr('plugin-id'): pluginId = cfgTree.getAttrValue('plugin-id', *pluginCfgPath) pluginMod = cfgTree.getAttrValue('plugin-module', *pluginCfgPath) pluginOptions = macro.expandMacros( cfgTree.getAttrValue('plugin-options', *pluginCfgPath, **dict(default=[], vector=True)), {'config-dir': os.path.dirname(cfgFile)}) log.info( 'configuring plugin ID %s (at %s) from module %s with options %s...' % (pluginId, '.'.join(pluginCfgPath), pluginMod, ', '.join(pluginOptions) or '<none>')) with daemon.PrivilegesOf(procUser, procGroup): try: pluginManager.loadPlugin(pluginId, pluginMod, pluginOptions) except SnmpfwdError: log.error('plugin %s not loaded: %s' % (pluginId, sys.exc_info()[1])) return for peerEntryPath in cfgTree.getPathsToAttr('snmp-peer-id'): peerId = cfgTree.getAttrValue('snmp-peer-id', *peerEntryPath) if peerId in peerIdMap: log.error('duplicate snmp-peer-id=%s at %s' % (peerId, '.'.join(peerEntryPath))) return log.info('configuring SNMP peer %s (at %s)...' % (peerId, '.'.join(peerEntryPath))) engineId = cfgTree.getAttrValue('snmp-engine-id', *peerEntryPath) if engineId in engineIdMap: snmpEngine, snmpContext, snmpEngineMap = engineIdMap[engineId] log.info('using engine-id: %s' % snmpEngine.snmpEngineID.prettyPrint()) else: snmpEngine = engine.SnmpEngine(snmpEngineID=engineId) snmpContext = context.SnmpContext(snmpEngine) snmpEngineMap = { 'transportDomain': {}, 'securityName': {}, 'credIds': set() } engineIdMap[engineId] = snmpEngine, snmpContext, snmpEngineMap log.info('new engine-id %s' % snmpEngine.snmpEngineID.prettyPrint()) transportDomain = cfgTree.getAttrValue('snmp-transport-domain', *peerEntryPath) transportDomain = rfc1902.ObjectName(str(transportDomain)) if (transportDomain[:len(udp.domainName)] != udp.domainName and udp6 and transportDomain[:len(udp6.domainName)] != udp6.domainName): log.error('unknown transport domain %s' % (transportDomain, )) return transportOptions = cfgTree.getAttrValue( 'snmp-transport-options', *peerEntryPath, **dict(default=[], vector=True)) bindAddr = cfgTree.getAttrValue('snmp-bind-address', *peerEntryPath) try: bindAddr, bindAddrMacro = endpoint.parseTransportAddress( transportDomain, bindAddr, transportOptions) except SnmpfwdError: log.error('bad snmp-bind-address specification %s at %s' % (bindAddr, '.'.join(peerEntryPath))) return if transportDomain in snmpEngineMap['transportDomain']: log.info('using transport endpoint with transport ID %s' % (transportDomain, )) else: if transportDomain[:len(udp.domainName)] == udp.domainName: transport = udp.UdpTransport() else: transport = udp6.Udp6Transport() snmpEngine.registerTransportDispatcher(transportDispatcher, transportDomain) t = transport.openClientMode(bindAddr) if 'transparent-proxy' in transportOptions: t.enablePktInfo() t.enableTransparent() elif 'virtual-interface' in transportOptions: t.enablePktInfo() config.addSocketTransport(snmpEngine, transportDomain, t) snmpEngineMap['transportDomain'][transportDomain] = bindAddr[ 0], bindAddr[1], transportDomain log.info( 'new transport endpoint at bind address [%s]:%s, options %s, transport ID %s' % (bindAddr[0], bindAddr[1], transportOptions and '/'.join(transportOptions) or '<none>', transportDomain)) securityModel = cfgTree.getAttrValue('snmp-security-model', *peerEntryPath) securityModel = rfc1902.Integer(securityModel) securityLevel = cfgTree.getAttrValue('snmp-security-level', *peerEntryPath) securityLevel = rfc1902.Integer(securityLevel) securityName = cfgTree.getAttrValue('snmp-security-name', *peerEntryPath) contextEngineId = cfgTree.getAttrValue('snmp-context-engine-id', *peerEntryPath, **dict(default=None)) contextName = cfgTree.getAttrValue('snmp-context-name', *peerEntryPath, **dict(default='')) if securityModel in (1, 2): if securityName in snmpEngineMap['securityName']: if snmpEngineMap['securityName'][ securityName] == securityModel: log.info('using security-name %s' % securityName) else: log.error( 'security-name %s already in use at security-model %s' % (securityName, securityModel)) return else: communityName = cfgTree.getAttrValue('snmp-community-name', *peerEntryPath) config.addV1System(snmpEngine, securityName, communityName, securityName=securityName) log.info( 'new community-name %s, security-model %s, security-name %s, security-level %s' % (communityName, securityModel, securityName, securityLevel)) snmpEngineMap['securityName'][securityName] = securityModel elif securityModel == 3: if securityName in snmpEngineMap['securityName']: if snmpEngineMap['securityName'][ securityName] == securityModel: log.info('using USM security-name: %s' % securityName) else: raise SnmpfwdError( 'security-name %s already in use at security-model %s' % (securityName, securityModel)) else: usmUser = cfgTree.getAttrValue('snmp-usm-user', *peerEntryPath) securityEngineId = cfgTree.getAttrValue( 'snmp-security-engine-id', *peerEntryPath, **dict(default=None)) if securityEngineId: securityEngineId = rfc1902.OctetString(securityEngineId) log.info( 'new USM user %s, security-model %s, security-level %s, ' 'security-name %s, security-engine-id %s' % (usmUser, securityModel, securityLevel, securityName, securityEngineId and securityEngineId.prettyPrint() or '<none>')) if securityLevel in (2, 3): usmAuthProto = cfgTree.getAttrValue( 'snmp-usm-auth-protocol', *peerEntryPath, **dict(default=config.usmHMACMD5AuthProtocol)) try: usmAuthProto = authProtocols[usmAuthProto.upper()] except KeyError: pass usmAuthProto = rfc1902.ObjectName(usmAuthProto) usmAuthKey = cfgTree.getAttrValue('snmp-usm-auth-key', *peerEntryPath) log.info( 'new USM authentication key: %s, authentication protocol: %s' % (usmAuthKey, usmAuthProto)) if securityLevel == 3: usmPrivProto = cfgTree.getAttrValue( 'snmp-usm-priv-protocol', *peerEntryPath, **dict(default=config.usmDESPrivProtocol)) try: usmPrivProto = privProtocols[usmPrivProto.upper()] except KeyError: pass usmPrivProto = rfc1902.ObjectName(usmPrivProto) usmPrivKey = cfgTree.getAttrValue( 'snmp-usm-priv-key', *peerEntryPath, **dict(default=None)) log.info( 'new USM encryption key: %s, encryption protocol: %s' % (usmPrivKey, usmPrivProto)) config.addV3User( snmpEngine, usmUser, usmAuthProto, usmAuthKey, usmPrivProto, usmPrivKey, ) else: config.addV3User(snmpEngine, usmUser, usmAuthProto, usmAuthKey, securityEngineId=securityEngineId) else: config.addV3User(snmpEngine, usmUser, securityEngineId=securityEngineId) snmpEngineMap['securityName'][securityName] = securityModel else: log.error('unknown security-model: %s' % securityModel) sys.exit(1) credId = '/'.join( [str(x) for x in (securityName, securityLevel, securityModel)]) if credId in snmpEngineMap['credIds']: log.info('using credentials ID %s...' % credId) else: config.addTargetParams( snmpEngine, credId, securityName, securityLevel, securityModel == 3 and 3 or securityModel - 1) log.info( 'new credentials %s, security-name %s, security-level %s, security-model %s' % (credId, securityName, securityLevel, securityModel)) snmpEngineMap['credIds'].add(credId) peerAddr = cfgTree.getAttrValue('snmp-peer-address', *peerEntryPath) try: peerAddr, peerAddrMacro = endpoint.parseTransportAddress( transportDomain, peerAddr, transportOptions, defaultPort=161) except SnmpfwdError: log.error('bad snmp-peer-address specification %s at %s' % (peerAddr, '.'.join(peerEntryPath))) return timeout = cfgTree.getAttrValue('snmp-peer-timeout', *peerEntryPath) retries = cfgTree.getAttrValue('snmp-peer-retries', *peerEntryPath) config.addTargetAddr(snmpEngine, peerId, transportDomain, peerAddr, credId, timeout, retries) peerIdMap[ peerId] = snmpEngine, contextEngineId, contextName, bindAddr, bindAddrMacro, peerAddr, peerAddrMacro log.info( 'new peer ID %s, bind address %s, peer address %s, timeout %s*0.01 secs, retries %s, credentials ID %s' % (peerId, bindAddrMacro or '<default>', peerAddrMacro or '%s:%s' % peerAddr, timeout, retries, credId)) duplicates = {} # TODO: rename orig-* into server-* and orig-snmp-peer-id into server-snmp-entity-id for origCredCfgPath in cfgTree.getPathsToAttr('orig-snmp-peer-id'): origCredId = cfgTree.getAttrValue('orig-snmp-peer-id', *origCredCfgPath) if origCredId in duplicates: log.error('duplicate orig-snmp-peer-id=%s at %s and %s' % (origCredId, '.'.join(origCredCfgPath), '.'.join( duplicates[origCredId]))) return duplicates[origCredId] = origCredCfgPath k = '#'.join( (cfgTree.getAttrValue('orig-snmp-engine-id-pattern', *origCredCfgPath), cfgTree.getAttrValue('orig-snmp-transport-domain-pattern', *origCredCfgPath), cfgTree.getAttrValue('orig-snmp-peer-address-pattern', *origCredCfgPath), cfgTree.getAttrValue('orig-snmp-bind-address-pattern', *origCredCfgPath), cfgTree.getAttrValue('orig-snmp-security-model-pattern', *origCredCfgPath), cfgTree.getAttrValue('orig-snmp-security-level-pattern', *origCredCfgPath), cfgTree.getAttrValue('orig-snmp-security-name-pattern', *origCredCfgPath), cfgTree.getAttrValue('orig-snmp-context-engine-id-pattern', *origCredCfgPath), cfgTree.getAttrValue('orig-snmp-context-name-pattern', *origCredCfgPath), cfgTree.getAttrValue('orig-snmp-pdu-type-pattern', *origCredCfgPath), cfgTree.getAttrValue('orig-snmp-oid-prefix-pattern', *origCredCfgPath))) log.info( 'configuring original SNMP peer ID %s (at %s), composite key: %s' % (origCredId, '.'.join(origCredCfgPath), k)) origCredIdList.append((origCredId, re.compile(k))) duplicates = {} for srvClassCfgPath in cfgTree.getPathsToAttr('server-classification-id'): srvClassId = cfgTree.getAttrValue('server-classification-id', *srvClassCfgPath) if srvClassId in duplicates: log.error('duplicate server-classification-id=%s at %s and %s' % (srvClassId, '.'.join(srvClassCfgPath), '.'.join( duplicates[srvClassId]))) return duplicates[srvClassId] = srvClassCfgPath k = '#'.join( (cfgTree.getAttrValue('server-snmp-credentials-id-pattern', *srvClassCfgPath), cfgTree.getAttrValue('server-snmp-context-id-pattern', *srvClassCfgPath), cfgTree.getAttrValue('server-snmp-content-id-pattern', *srvClassCfgPath), cfgTree.getAttrValue('server-snmp-peer-id-pattern', *srvClassCfgPath))) log.info( 'configuring server classification ID %s (at %s), composite key: %s' % (srvClassId, '.'.join(srvClassCfgPath), k)) srvClassIdList.append((srvClassId, re.compile(k))) del duplicates for pluginCfgPath in cfgTree.getPathsToAttr('using-plugin-id-list'): pluginIdList = cfgTree.getAttrValue('using-plugin-id-list', *pluginCfgPath, **dict(vector=True)) log.info('configuring plugin ID(s) %s (at %s)...' % (','.join(pluginIdList), '.'.join(pluginCfgPath))) for credId in cfgTree.getAttrValue('matching-orig-snmp-peer-id-list', *pluginCfgPath, **dict(vector=True)): for srvClassId in cfgTree.getAttrValue( 'matching-server-classification-id-list', *pluginCfgPath, **dict(vector=True)): for trunkId in cfgTree.getAttrValue('matching-trunk-id-list', *pluginCfgPath, **dict(vector=True)): k = credId, srvClassId, trunkId if k in pluginIdMap: log.error( 'duplicate snmp-credentials-id=%s and server-classification-id=%s and trunk-id=%s at plugin-id %s' % (credId, srvClassId, trunkId, ','.join(pluginIdList))) return else: log.info( 'configuring plugin(s) %s (at %s), composite key: %s' % (','.join(pluginIdList), '.'.join(pluginCfgPath), '/'.join(k))) for pluginId in pluginIdList: if not pluginManager.hasPlugin(pluginId): log.error( 'undefined plugin ID %s referenced at %s' % (pluginId, '.'.join(pluginCfgPath))) return pluginIdMap[k] = pluginIdList for routeCfgPath in cfgTree.getPathsToAttr('using-snmp-peer-id-list'): peerIdList = cfgTree.getAttrValue('using-snmp-peer-id-list', *routeCfgPath, **dict(vector=True)) log.info('configuring routing entry with peer IDs %s (at %s)...' % (','.join(peerIdList), '.'.join(routeCfgPath))) for credId in cfgTree.getAttrValue('matching-orig-snmp-peer-id-list', *routeCfgPath, **dict(vector=True)): for srvClassId in cfgTree.getAttrValue( 'matching-server-classification-id-list', *routeCfgPath, **dict(vector=True)): for trunkId in cfgTree.getAttrValue('matching-trunk-id-list', *routeCfgPath, **dict(vector=True)): k = credId, srvClassId, trunkId if k in routingMap: log.error( 'duplicate snmp-credentials-id=%s and server-classification-id=%s and trunk-id=%s at snmp-peer-id %s' % (credId, srvClassId, trunkId, ','.join(peerIdList))) return else: for peerId in peerIdList: if peerId not in peerIdMap: log.error('missing peer-id %s at %s' % (peerId, '.'.join(routeCfgPath))) return routingMap[k] = peerIdList trunkingManager = TrunkingManager(trunkCbFun) for trunkCfgPath in cfgTree.getPathsToAttr('trunk-id'): trunkId = cfgTree.getAttrValue('trunk-id', *trunkCfgPath) secret = cfgTree.getAttrValue('trunk-crypto-key', *trunkCfgPath, **dict(default='')) secret = secret and (secret * ((16 // len(secret)) + 1))[:16] log.info('configuring trunk ID %s (at %s)...' % (trunkId, '.'.join(trunkCfgPath))) connectionMode = cfgTree.getAttrValue('trunk-connection-mode', *trunkCfgPath) if connectionMode == 'client': trunkingManager.addClient( trunkId, parseTrunkEndpoint( cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath)), parseTrunkEndpoint( cfgTree.getAttrValue('trunk-peer-address', *trunkCfgPath), 30201), cfgTree.getAttrValue('trunk-ping-period', *trunkCfgPath, default=0, expect=int), secret) log.info( 'new trunking client from %s to %s' % (cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath), cfgTree.getAttrValue('trunk-peer-address', *trunkCfgPath))) if connectionMode == 'server': trunkingManager.addServer( parseTrunkEndpoint( cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath), 30201), cfgTree.getAttrValue('trunk-ping-period', *trunkCfgPath, default=0, expect=int), secret) log.info( 'new trunking server at %s' % (cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath))) transportDispatcher.registerTimerCbFun(trunkingManager.setupTrunks, random.randrange(1, 5)) transportDispatcher.registerTimerCbFun(trunkingManager.monitorTrunks, random.randrange(1, 5)) if not foregroundFlag: try: daemon.daemonize(pidFile) except Exception: log.error('can not daemonize process: %s' % sys.exc_info()[1]) return # Run mainloop log.info('starting I/O engine...') transportDispatcher.jobStarted(1) # server job would never finish # Python 2.4 does not support the "finally" clause with daemon.PrivilegesOf(procUser, procGroup, final=True): while True: try: transportDispatcher.runDispatcher() except (PySnmpError, SnmpfwdError, socket.error): log.error(str(sys.exc_info()[1])) continue except Exception: transportDispatcher.closeDispatcher() raise