def send(self, host, community, oid, value): self.pMod.apiPDU.setDefaults(self.reqPDU) self.pMod.apiPDU.setVarBinds( self.reqPDU, ((oid, self.pMod.OctetString(value)), ) ) # Build message reqMsg = self.pMod.Message() self.pMod.apiMessage.setDefaults(reqMsg) self.pMod.apiMessage.setCommunity(reqMsg, community) self.pMod.apiMessage.setPDU(reqMsg, self.reqPDU) transportDispatcher = AsynsockDispatcher() transportDispatcher.registerRecvCbFun(self.cbRecvFun) transportDispatcher.registerTimerCbFun(self.cbTimerFun) # UDP/IPv4 transportDispatcher.registerTransport( udp.domainName, udp.UdpSocketTransport().openClientMode() ) # Pass message to dispatcher transportDispatcher.sendMessage( encoder.encode(reqMsg), udp.domainName, (host, 161) ) transportDispatcher.jobStarted(1) # Dispatcher will finish as job#1 counter reaches zero transportDispatcher.runDispatcher() transportDispatcher.closeDispatcher()
def query(self, oidCountersDict): resultsDict = dict() # SNMP protocol version to use pMod = api.protoModules[api.protoVersion1] # Build PDU reqPDU = pMod.GetRequestPDU() pMod.apiPDU.setDefaults(reqPDU) # Create a tuple of OIDs tuples oidList = list() for oid in oidCountersDict.values(): oidList.append((oid, pMod.Null())) OIDs = tuple(oidList) pMod.apiPDU.setVarBinds(reqPDU, (OIDs)) # Build message reqMsg = pMod.Message() pMod.apiMessage.setDefaults(reqMsg) pMod.apiMessage.setCommunity(reqMsg, 'public') pMod.apiMessage.setPDU(reqMsg, reqPDU) def cbTimerFun(timeNow, startedAt=time()): if timeNow - startedAt > self.snmpTimeout: transportDispatcher.jobFinished(1) raise SNMPTimeOutError("The host %s has not responded in the specified timeout of %ss!" % (self.host, self.snmpTimeout)) def cbRecvFun(transportDispatcher, transportDomain, transportAddress, wholeMsg, reqPDU=reqPDU): while wholeMsg: rspMsg, wholeMsg = decoder.decode(wholeMsg, asn1Spec=pMod.Message()) rspPDU = pMod.apiMessage.getPDU(rspMsg) # Match response to request if pMod.apiPDU.getRequestID(reqPDU)==pMod.apiPDU.getRequestID(rspPDU): # Check for SNMP errors reported errorStatus = pMod.apiPDU.getErrorStatus(rspPDU) if errorStatus: print errorStatus.prettyPrint() raise SNMPoIDNotSupported("%s: An requested OID is not supported by this device!" % errorStatus.prettyPrint()) else: for oid, val in pMod.apiPDU.getVarBinds(rspPDU): #print '%s = %s' % (oid.prettyPrint(), val) resultsDict[str(oid)] = str(val) transportDispatcher.jobFinished(1) return wholeMsg transportDispatcher = AsynsockDispatcher() transportDispatcher.registerTransport(udp.domainName, udp.UdpSocketTransport().openClientMode()) transportDispatcher.registerRecvCbFun(cbRecvFun) transportDispatcher.registerTimerCbFun(cbTimerFun) transportDispatcher.sendMessage(encoder.encode(reqMsg), udp.domainName, (self.host, int(self.port))) transportDispatcher.jobStarted(1) transportDispatcher.runDispatcher() transportDispatcher.closeDispatcher() return resultsDict
def Send_GetSystemPDUName(reqMsg, hostname, port=161): transportDispatcher = AsynsockDispatcher() transportDispatcher.registerTransport(udp.domainName, udp.UdpSocketTransport().openClientMode()) transportDispatcher.registerRecvCbFun(cbRecvFun) transportDispatcher.registerTimerCbFun(cbTimerFun) transportDispatcher.sendMessage(encoder.encode(reqMsg), udp.domainName, (hostname, 161)) transportDispatcher.jobStarted(1) transportDispatcher.runDispatcher() transportDispatcher.closeDispatcher()
def sendSNMPMessage(self, reqMsg): transportDispatcher = AsynsockDispatcher() transportDispatcher.registerTransport(udp.domainName, udp.UdpSocketTransport().openClientMode()) transportDispatcher.registerRecvCbFun(self.cbRecvFun) transportDispatcher.registerTimerCbFun(self.cbTimerFun) transportDispatcher.sendMessage(encoder.encode(reqMsg), udp.domainName, (self.host, self.port)) transportDispatcher.jobStarted(1) transportDispatcher.runDispatcher() transportDispatcher.closeDispatcher()
def Send_GetSystemPDUName(reqMsg, hostname, port=161): transportDispatcher = AsynsockDispatcher() transportDispatcher.registerTransport( udp.domainName, udp.UdpSocketTransport().openClientMode()) transportDispatcher.registerRecvCbFun(cbRecvFun) transportDispatcher.registerTimerCbFun(cbTimerFun) transportDispatcher.sendMessage(encoder.encode(reqMsg), udp.domainName, (hostname, 161)) transportDispatcher.jobStarted(1) transportDispatcher.runDispatcher() transportDispatcher.closeDispatcher()
def _trap_receiver(trap_filter, host, port, timeout): started = time.time() def _trap_timer_cb(now): if now - started > timeout: raise AssertionError('No matching trap received in %s.' % robot.utils.secs_to_timestr(timeout)) def _trap_receiver_cb(transport, domain, sock, msg): if decodeMessageVersion(msg) != protoVersion2c: raise RuntimeError('Only SNMP v2c traps are supported.') req, msg = decoder.decode(msg, asn1Spec=v2c.Message()) pdu = v2c.apiMessage.getPDU(req) # ignore any non trap PDUs if not pdu.isSameTypeWith(v2c.TrapPDU()): return if trap_filter(domain, sock, pdu): raise StopListener() # Stop the receiver if the trap we are looking for was received. if False: raise StopListener() dispatcher = AsynsockDispatcher() dispatcher.registerRecvCbFun(_trap_receiver_cb) dispatcher.registerTimerCbFun(_trap_timer_cb) transport = udp.UdpSocketTransport().openServerMode((host, port)) dispatcher.registerTransport(udp.domainName, transport) # we'll never finish, except through an exception dispatcher.jobStarted(1) try: dispatcher.runDispatcher() except StopListener: pass finally: dispatcher.closeDispatcher()
class TrapReceiver: def __init__(self, rcv_ip, box_ip, event_name, port=162, inform=None): self.rcvr_ip = rcv_ip self.port = port self.box_ip = box_ip self.event_name = event_name self.gen_trap = GenerateTrap(self.box_ip) self.inform = inform def cbFun(self, transportDispatcher, transportDomain, cbCtx, wholeMsg, ): print "cbCtx :", cbCtx msgVer = int(api.decodeMessageVersion(wholeMsg)) #print "Inisde CB FUN..." print 'Inside CB FUN....' if msgVer in api.protoModules: pMod = api.protoModules[msgVer] else: print('Unsupported SNMP version %s' % msgVer) #return reqMsg, wholeMsg = decoder.decode( wholeMsg, asn1Spec=pMod.Message(), ) self.req = reqMsg print 'Type :', type(self.req) print 'Inform :', self.inform if self.inform: print 'Inside Inform IF........' if not 'InformRequestPDU()' in str(self.req): print 'Inform Message is not sent....failed...' print 'reqMsg : %s' % self.req #print 'wholeMsg : %s' % wholeMsg #print('Notification message from %s:%s: ' % ( # transportDomain, transportAddress # ) #) reqPDU = pMod.apiMessage.getPDU(reqMsg) print ',,,,reqPDU : %s' % reqPDU varBinds = pMod.apiPDU.getVarBindList(reqPDU) #transportDispatcher.jobFinished(1) self.terminationRequestedFlag = True if msgVer == api.protoVersion1: self.varBinds = pMod.apiTrapPDU.getVarBindList(reqPDU) self.specificTrap = pMod.apiTrapPDU.getSpecificTrap(reqPDU).prettyPrint() self.version = 1 if msgVer == api.protoVersion2c: self.varBinds = pMod.apiPDU.getVarBindList(reqPDU) self.specificTrap = None self.version = 2 def timerCb(self, timeNow=10): print "Inside Timer CB..." if self.terminationRequestedFlag: print "Finishing the Job..." self.transportDispatcher.jobFinished(1) else: self.terminationRequestedFlag = True def trap_listener(self): #print "RECEIVER IP:", self.rcvr_ip self.transportDispatcher = AsynsockDispatcher() self.terminationRequestedFlag = False #print "TER FLAG:", self.terminationRequestedFlag self.transportDispatcher.registerRecvCbFun(self.cbFun) self.transportDispatcher.registerTimerCbFun(self.timerCb) self.transportDispatcher.registerTransport(udp.domainName, udp.UdpSocketTransport().openServerMode(("%s" % self.rcvr_ip, 162)) ) #time.sleep(1) # Generate the trap for the given Event. self.gen_trap.generate_trap(self.event_name) self.transportDispatcher.jobStarted(1) print "Run Dispatcher...." self.transportDispatcher.runDispatcher() print "Close Dispatcher...." self.transportDispatcher.closeDispatcher() print "\n" #print "varBinds : ", self.varBinds #print "Version : ", self.version #print "specificTrap : ", self.specificTrap return self.varBinds, self.specificTrap, self.version
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
def trapCmd(self, port, intWaitSec): ## @brief print/log trap data nicely # @param varBinds variable bindings (object ID and value) # @param transportAddress IP address to identify by # @param strFname file name to open # @returns none def printItNice(varBinds, transportAddress, strFname): fHandle = open(sys.path[0] + '//' + strFname, 'a') timeMilli = datetime.datetime.now().microsecond / 1000 timeTrapTime = strftime("%Y-%b-%d %H:%M:%S.") + str(timeMilli) strPrintTxt = '\n\n%s :TRAP RECEIVED from %s: ' % ( timeTrapTime, transportAddress[0]) fHandle.write(strPrintTxt) if self.DEBUG: print strPrintTxt dicTrapData = {} dicTrapData['IP'] = transportAddress[0] for strOid, strVal in varBinds: strOid = strOid.prettyPrint() strVal = strVal.getComponent().getComponent().getComponent( ).prettyPrint() strPrintTxt = strOid + ' ' * (45 - len(strOid)) + strVal fHandle.write('\n' + strPrintTxt) if self.DEBUG: print strPrintTxt dicTrapData[strOid] = strVal dicTrapCollection[timeTrapTime] = dicTrapData fHandle.close() ## @brief function handler timer # @param timeTick time between events (reference period) # @returns none def timeFun(timeTick): timeNewTime = datetime.datetime.now() timeDelta = timeNewTime - timeReference if int(timeDelta.seconds) >= intWaitSec: transportDispatcher.jobFinished(1) transportDispatcher.closeDispatcher() ## @brief function handler for data processing # @param transportDispatcher I/O dispatcher # @param transportDomain transport domain # @param transportAddress destination address # @param wholeMsg data to process # @returns the processed data def cbFun(transportDispatcher, transportDomain, transportAddress, wholeMsg): strFname = 'Trap_' + str( transportAddress[0]) + '_' + str(port) + '.log' while wholeMsg: msgVer = int(api.decodeMessageVersion(wholeMsg)) if api.protoModules.has_key(msgVer): pMod = api.protoModules[msgVer] else: print 'Unsupported SNMP version %s' % msgVer return reqMsg, wholeMsg = decoder.decode( wholeMsg, asn1Spec=pMod.Message(), ) reqPDU = pMod.apiMessage.getPDU(reqMsg) if reqPDU.isSameTypeWith(pMod.TrapPDU()): if msgVer == api.protoVersion1: varBinds = pMod.apiTrapPDU.getVarBindList(reqPDU) else: varBinds = pMod.apiPDU.getVarBindList(reqPDU) printItNice(varBinds, transportAddress, strFname) return wholeMsg dicTrapCollection = {} timeReference = datetime.datetime.now() transportDispatcher = AsynsockDispatcher() transportDispatcher.timeout = 0.3 transportDispatcher.registerTransport( udp.domainName, udp.UdpSocketTransport().openServerMode(('0.0.0.0', port))) transportDispatcher.registerRecvCbFun(cbFun) transportDispatcher.registerTimerCbFun(timeFun) transportDispatcher.jobStarted(1) transportDispatcher.runDispatcher() return dicTrapCollection
def update(self): """Called by the processing thread to trigger an update of the data""" ip = self._ip hostname = self._hostname # Grab the lock lock = self.lock lock.acquire() try: # Retrieve locally available information self.activeRevision = getActiveRevision(hostname) self.generatedRevision = getGeneratedRevision(self._hostname) # Check that the host is reachable if not isHostUp(ip): self.reachable = False self.lastCheckedAt = time.time() self.attempt+=1 return self.reachable = True # Retrieve host status information pMod = api.protoModules[api.protoVersion1] reqPDU = pMod.GetRequestPDU() pMod.apiPDU.setDefaults(reqPDU) pMod.apiPDU.setVarBinds( reqPDU, ((CCS_REVISION_OID, pMod.Null()), (LOAD_AVG1_OID, pMod.Null()), (LOAD_AVG5_OID, pMod.Null()), (LOAD_AVG15_OID, pMod.Null()), (UPTIME_OID, pMod.Null()) ) ) # Build message reqMsg = pMod.Message() pMod.apiMessage.setDefaults(reqMsg) pMod.apiMessage.setCommunity(reqMsg, 'public') pMod.apiMessage.setPDU(reqMsg, reqPDU) # Dispatch Request transportDispatcher = AsynsockDispatcher() transportDispatcher.registerTransport( udp.domainName, udp.UdpSocketTransport().openClientMode() ) startedAt = time.time() def cbTimerFun(timeNow): if timeNow - startedAt > 3: transportDispatcher.jobFinished(1) transportDispatcher.closeDispatcher() self.attempt+=1 log_warn("Timeout fetching status from %s. Attempt #%s" % (hostname, self.attempt)) def cbRecvFun(transportDispatcher, transportDomain, \ transportAddress, wholeMsg, reqPDU=reqPDU): while wholeMsg: rspMsg, wholeMsg = decoder.decode(wholeMsg, \ asn1Spec=pMod.Message()) rspPDU = pMod.apiMessage.getPDU(rspMsg) # Match response to request if pMod.apiPDU.getRequestID(reqPDU) == \ pMod.apiPDU.getRequestID(rspPDU): # Check for SNMP errors reported errorStatus = pMod.apiPDU.getErrorStatus(rspPDU) if errorStatus: transportDispatcher.jobFinished(1) transportDispatcher.closeDispatcher() raise ccs_status_error(errorStatus.prettyPrint()) else: la1 = 0 la5 = 0 la15 = 0 for oid, val in pMod.apiPDU.getVarBinds(rspPDU): if oid == CCS_REVISION_OID: self.operatingRevision = str(val) self._parseOperatingRevision() elif oid == LOAD_AVG1_OID: la1 = val elif oid == LOAD_AVG5_OID: la5 = val elif oid == LOAD_AVG15_OID: la15 = val elif oid == UPTIME_OID: self.uptime = val self.currentLoad = (la1, la5, la15) transportDispatcher.jobFinished(1) self.attempt = 0 return wholeMsg transportDispatcher.registerRecvCbFun(cbRecvFun) transportDispatcher.registerTimerCbFun(cbTimerFun) transportDispatcher.sendMessage( encoder.encode(reqMsg), udp.domainName, (ip, 161) ) transportDispatcher.jobStarted(1) transportDispatcher.runDispatcher() transportDispatcher.closeDispatcher() # Record that the details were updated self.infoUpdatedAt = time.time() self.lastCheckedAt = time.time() finally: lock.release()
class SNMPProcess(threading_tools.process_obj): def __init__(self, name, conf_dict, **kwargs): self.__snmp_name = name self.__log_name, self.__log_destination = ( conf_dict["LOG_NAME"], conf_dict["LOG_DESTINATION"], ) self.__verbose = conf_dict.get("VERBOSE", False) self.debug_zmq = conf_dict.get("DEBUG_ZMQ", False) threading_tools.process_obj.__init__(self, name, busy_loop=True) if kwargs.get("ignore_signals", False): signal.signal(signal.SIGTERM, signal.SIG_IGN) def process_init(self): self.__log_template = logging_tools.get_logger( self.__log_name, self.__log_destination, zmq=True, context=self.zmq_context) self.__return_proc_name = None self.register_func("fetch_snmp", self._fetch_snmp) self.register_func("register_return", self._register_return) self.register_func("trigger_timeout", self._trigger_timeout) self._init_dispatcher() self.__job_dict = {} self.__envelope_dict = {} self.__req_id_lut = {} def log(self, what, log_level=logging_tools.LOG_LEVEL_OK): self.__log_template.log(log_level, what) def _init_dispatcher(self): self.log("init snmp_session object") self.__disp = AsynsockDispatcher() self.__disp.registerTransport( udp.domainName, udp.UdpSocketTransport().openClientMode()) self.__disp.registerRecvCbFun(self._recv_func) self.__disp.registerTimerCbFun(self._timer_func) self.v1_decoder = api.protoModules[api.protoVersion1] self.v2c_decoder = api.protoModules[api.protoVersion2c] def register_batch(self, cur_batch): if self.__verbose > 3: self.log("registered new batch {:d}".format(cur_batch.key)) self.__job_dict[cur_batch.key] = cur_batch self.__disp.jobStarted(cur_batch.key) def unregister_batch(self, cur_batch): # ids we will no longer handle because of finish to_keys = [ key for key, value in self.__req_id_lut.iteritems() if value == cur_batch ] if to_keys: for to_key in to_keys: del self.__req_id_lut[to_key] if self.__verbose > 3: cur_batch.log( "removed {} for batch {}".format( logging_tools.get_plural("request ID", len(to_keys)), cur_batch, ), ) del self.__job_dict[cur_batch.key] del self.__envelope_dict[cur_batch.envelope] self.__disp.jobFinished(cur_batch.key) def loop(self): try: while self["run_flag"]: self.__disp.runDispatcher() self.step(blocking=self["run_flag"]) except ValueConstraintError: self.log("caught ValueConstraintError, terminating process", logging_tools.LOG_LEVEL_CRITICAL) _term_cause = "ValueConstraintError" except: exc_info = process_tools.exception_info() self.log("exception in dispatcher, terminating process", logging_tools.LOG_LEVEL_CRITICAL) for log_line in exc_info.log_lines: self.log(" - {}".format(log_line), logging_tools.LOG_LEVEL_CRITICAL) _term_cause = "internal error" else: self.log("no more jobs running") _term_cause = "" self.log("jobs pending: {:d}".format(len(self.__job_dict))) # close all jobs if _term_cause: self._terminate_jobs(error="{}, check logs".format(_term_cause)) else: self._terminate_jobs() self.__disp.closeDispatcher() def _inject(self, cur_batch): try: next_tuple = cur_batch.iterator.next() except StopIteration: cur_batch.finish() else: self.send_next(cur_batch, next_tuple) def send_next(self, cur_batch, next_tuple): self.__req_id_lut[cur_batch.request_id] = cur_batch self.__disp.sendMessage(*next_tuple) def _register_return(self, proc_name, **kwargs): self.__return_proc_name = proc_name self.send_pool_message("hellox", "hello2", "hello3", target=self.__return_proc_name) def _fetch_snmp(self, *scheme_data, **kwargs): new_batch = SNMPBatch(self, *scheme_data, verbose=self.__verbose, **kwargs) self.__envelope_dict[new_batch.envelope] = new_batch self._inject(new_batch) def _trigger_timeout(self, *args, **kwargs): batch_id = args[0] if batch_id in self.__envelope_dict: self.log("triggering timeout for batch_id {}".format(batch_id), logging_tools.LOG_LEVEL_WARN) self.__envelope_dict[batch_id].trigger_timeout() else: self.log("unknown batch_id {}".format(batch_id), logging_tools.LOG_LEVEL_ERROR) def _timer_func(self, act_time): timed_out = [ key for key, cur_job in self.__job_dict.iteritems() if cur_job.timer_func(act_time) ] for to_key in timed_out: self.__job_dict[to_key].finish() self.step() if self["exit_requested"]: self._terminate_jobs(error="exit requested") def _terminate_jobs(self, **kwargs): _stop_keys = set(self.__job_dict.keys()) for _key in _stop_keys: if "error" in kwargs: self.__job_dict[_key].add_error(kwargs["error"]) self.__job_dict[_key].finish() def _recv_func(self, disp, domain, address, whole_msg): while whole_msg: # rsp_msg, whole_msg = decoder.decode(whole_msg, asn1Spec=self.__p_mod.Message()) try: rsp_msg, whole_msg = decoder.decode( whole_msg, asn1Spec=self.v2c_decoder.Message()) except: self.log( "error decoding message from {}: {}".format( address, process_tools.get_except_info()), logging_tools.LOG_LEVEL_CRITICAL) # send meaningfull error message to client ? TODO, FIXME whole_msg = None else: # rsp_pdu = self.__p_mod.apiMessage.getPDU(rsp_msg) rsp_pdu = self.v2c_decoder.apiMessage.getPDU(rsp_msg) cur_id = self.v2c_decoder.apiPDU.getRequestID(rsp_pdu) if cur_id in self.__req_id_lut: self.__req_id_lut[cur_id].feed_pdu(disp, domain, address, rsp_pdu) else: self.log("id {} in response not known".format(cur_id)) if cur_id in self.__req_id_lut: del self.__req_id_lut[cur_id] return whole_msg def loop_post(self): self.__log_template.close() def send_return(self, envelope, error_list, received, snmp): self.send_pool_message("snmp_finished", envelope, error_list, received, snmp, target=self.__return_proc_name or DEFAULT_RETURN_NAME)
def start_trap(self, pattern_end_trap, time_end_trap, timeout, port, queue): """ :param pattern_end_trap **OPTION** the pattern you are looking for in the trap to stop trap. If not defined, trap will be stopped after timeout value :param time_end_trap **OPTION** the time for running trap. Default: 30 :param timeout **OPTION** the timeout for snmp trap. Default: 300. :param port **OPTION** the port of snmp trap. :return: """ t.log("DEBUG", "Entering 'start_trap'\n"+__file__) import warnings warnings.simplefilter("ignore", ResourceWarning) startedat = time.time() #global results global RESPONSE RESPONSE = '' def cbfun(transportdispatcher, transportdomain, transportaddress, wholemsg): t.log("DEBUG", "Entering 'cbfun'\n"+__file__) #global results global RESPONSE while wholemsg: msgver = int(api.decodeMessageVersion(wholemsg)) if msgver in api.protoModules: pmod = api.protoModules[msgver] else: RESPONSE += 'Unsupported SNMP version %s' % msgver return reqmsg, wholemsg = decoder.decode( wholemsg, asn1Spec=pmod.Message(), ) RESPONSE += 'Notification message from %s:%s: \n' % ( transportdomain, transportaddress ) reqpdu = pmod.apiMessage.getPDU(reqmsg) if msgver == api.protoVersion1: varbinds = pmod.apiTrapPDU.getVarBindList(reqpdu) RESPONSE += 'Var-binds: \n' for oid, val in varbinds: RESPONSE += '%s = %s \n' % (oid.prettyPrint(), val.prettyPrint()) if pattern_end_trap and re.search(pattern_end_trap, RESPONSE, re.I): transportdispatcher.jobFinished(1) RESPONSE += "Stopped trap \n" RESPONSE += 'Enterprise: %s \n' % ( pmod.apiTrapPDU.getEnterprise(reqpdu).prettyPrint() ) RESPONSE += 'Agent Address: %s \n' % ( pmod.apiTrapPDU.getAgentAddr(reqpdu).prettyPrint() ) RESPONSE += 'Generic Trap: %s \n' % ( pmod.apiTrapPDU.getGenericTrap( reqpdu).prettyPrint()) RESPONSE += 'Specific Trap: %s \n' % ( pmod.apiTrapPDU.getSpecificTrap( reqpdu).prettyPrint() ) RESPONSE += 'Uptime: %s \n' % ( pmod.apiTrapPDU.getTimeStamp( reqpdu).prettyPrint() ) else: varbinds = pmod.apiPDU.getVarBindList(reqpdu) def cbtimerfun(timenow): t.log("DEBUG", "Entering 'cbtimerfun'\n"+__file__) #global results global RESPONSE if time_end_trap is not None: if timenow - startedat >= time_end_trap: transportdispatcher.jobFinished(1) RESPONSE += "Stopped trap \n" if timenow - startedat >= timeout: t.log(level="DEBUG", message="Request timed out \n") raise Exception("Request timed out \n") transportdispatcher = AsynsockDispatcher() transportdispatcher.registerRecvCbFun(cbfun) transportdispatcher.registerTimerCbFun(cbtimerfun) # UDP/IPv4 transportdispatcher.registerTransport( udp.domainName, udp.UdpSocketTransport().openServerMode( (self.trap_server_v4, port)) ) # UDP/IPv6 transportdispatcher.registerTransport( udp6.domainName, udp6.Udp6SocketTransport().openServerMode( (self.trap_server_v6, port)) ) transportdispatcher.jobStarted(1) try: # Dispatcher will never finish as job#1 never reaches zero transportdispatcher.runDispatcher() except: transportdispatcher.closeDispatcher() queue.put(RESPONSE) t.log(level="DEBUG", message="Exiting 'start_trap' with return value/code :\n") return
rspPDU): # Check for SNMP errors reported errorStatus = pMod.apiPDU.getErrorStatus(rspPDU) if errorStatus: print(errorStatus.prettyPrint()) else: for oid, val in pMod.apiPDU.getVarBinds(rspPDU): print('%s = %s' % (oid.prettyPrint(), val.prettyPrint())) transportDispatcher.jobFinished(1) return wholeMsg transportDispatcher = AsynsockDispatcher() transportDispatcher.registerRecvCbFun(Receive) transportDispatcher.registerTimerCbFun(Timer) # UDP/IPv4 transportDispatcher.registerTransport( udp.domainName, udp.UdpSocketTransport().openClientMode()) # Pass message to dispatcher transportDispatcher.sendMessage(encoder.encode(reqMsg), udp.domainName, ('192.168.1.101', 161)) transportDispatcher.jobStarted(1) """ ## UDP/IPv6 (second copy of the same PDU will be sent) transportDispatcher.registerTransport( udp6.domainName, udp6.Udp6SocketTransport().openClientMode() )
cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath)), getTrunkAddr( cfgTree.getAttrValue('trunk-peer-address', *trunkCfgPath), 30201), secret) log.msg('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), secret) log.msg('new trunking server at %s' % (cfgTree.getAttrValue('trunk-bind-address', *trunkCfgPath))) transportDispatcher.registerTimerCbFun(trunkingManager.monitorTrunks, random.randrange(1, 5)) try: daemon.dropPrivileges(procUser, procGroup) except: sys.stderr.write('ERROR: cant drop priveleges: %s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage)) sys.exit(-1) if not foregroundFlag: try: daemon.daemonize(pidFile) except: sys.stderr.write('ERROR: cant daemonize process: %s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage)) sys.exit(-1)
# Build message self.reqMsg = v2c.Message() v2c.apiMessage.setDefaults(self.reqMsg) v2c.apiMessage.setCommunity(self.reqMsg, self.community) v2c.apiMessage.setPDU(self.reqMsg, self.reqPDU) # Save the time when snmp request start self.snmp_request_start_time = time.time() # Prepare SNMP Request transportDispatcher = AsynsockDispatcher() transportDispatcher.registerTransport(udp.domainName, udp.UdpSocketTransport().openClientMode()) transportDispatcher.registerRecvCbFun(self.callback) transportDispatcher.registerTimerCbFun(self.callback_timer) transportDispatcher.sendMessage(encoder.encode(self.reqMsg), udp.domainName, (self.hostname, 161)) transportDispatcher.jobStarted(1) try: # launch SNMP Request transportDispatcher.runDispatcher() except Exception, e: logger.error('[SnmpBooster] SNMP Request error 1: %s' % str(e)) self.set_exit("SNMP Request error 1: " + str(e), rc=3) # LOCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK try: self.obj = self.memcached.get(self.obj_key)
def pollFn(): #Check the heater status errorIndication, errorStatus, errorIndex, varBinds = next( getCmd(SnmpEngine(), CommunityData('public',mpModel=0), UdpTransportTarget(('localhost', 1162)), ContextData(), ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'))) ) if errorIndication: print(errorIndication) elif errorStatus: print('%s at %s' % ( errorStatus.prettyPrint(), errorIndex and varBinds[int(errorIndex)-1][0] or '?' ) ) else: for varBind in varBinds: print(' = '.join([ x.prettyPrint() for x in varBind ])) # Get the temperature errorIndication, errorStatus, errorIndex, varBinds = next( getCmd(SnmpEngine(), CommunityData('public',mpModel=0), UdpTransportTarget(('localhost', 1161)), ContextData(), ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'))) ) if errorIndication: print(errorIndication) elif errorStatus: print('%s at %s' % ( errorStatus.prettyPrint(), errorIndex and varBinds[int(errorIndex)-1][0] or '?' ) ) else: #for varBind in varBinds: # print(' = '.join([ x.prettyPrint() for x in varBind ])) #SET Command #Check if the temperature is less than 10 for oid, val in varBinds: print('%s = %s' % (oid.prettyPrint(), val.prettyPrint())) returnedVal = int(val) if (returnedVal < 10): print returnedVal # Protocol version to use #pMod = api.protoModules[api.protoVersion1] pMod = api.protoModules[api.protoVersion2c] # Build PDU reqPDU = pMod.SetRequestPDU() pMod.apiPDU.setDefaults(reqPDU) pMod.apiPDU.setVarBinds( reqPDU, # Change the heater status to ON ( ('1.3.6.1.2.1.1.1.0', pMod.OctetString('ON')), # ('1.3.6.1.2.1.1.3.0', pMod.TimeTicks(12)) ) ) # Build message reqMsg = pMod.Message() pMod.apiMessage.setDefaults(reqMsg) pMod.apiMessage.setCommunity(reqMsg, 'public') pMod.apiMessage.setPDU(reqMsg, reqPDU) startedAt = time() def cbTimerFun(timeNow): if timeNow - startedAt > 3: raise Exception("Request timed out") def cbRecvFun(transportDispatcher, transportDomain, transportAddress, wholeMsg, reqPDU=reqPDU): while wholeMsg: rspMsg, wholeMsg = decoder.decode(wholeMsg, asn1Spec=pMod.Message()) rspPDU = pMod.apiMessage.getPDU(rspMsg) # Match response to request if pMod.apiPDU.getRequestID(reqPDU)==pMod.apiPDU.getRequestID(rspPDU): # Check for SNMP errors reported errorStatus = pMod.apiPDU.getErrorStatus(rspPDU) if errorStatus: print(errorStatus.prettyPrint()) else: for oid, val in pMod.apiPDU.getVarBinds(rspPDU): print('%s = %s' (oid.prettyPrint(), val.prettyPrint())) transportDispatcher.jobFinished(1) return wholeMsg transportDispatcher = AsynsockDispatcher() transportDispatcher.registerRecvCbFun(cbRecvFun) transportDispatcher.registerTimerCbFun(cbTimerFun) # UDP/IPv4 transportDispatcher.registerTransport( udp.domainName, udp.UdpSocketTransport().openClientMode() ) # Pass message to dispatcher transportDispatcher.sendMessage( encoder.encode(reqMsg), udp.domainName, ('localhost', 1162) ) transportDispatcher.jobStarted(1) # Dispatcher will finish as job#1 counter reaches zero transportDispatcher.runDispatcher() transportDispatcher.closeDispatcher() else: print "value greater than 10"
# Match response to request if pMod.apiPDU.getRequestID(reqPDU)==pMod.apiPDU.getRequestID(rspPDU): # Check for SNMP errors reported errorStatus = pMod.apiPDU.getErrorStatus(rspPDU) if errorStatus: print(errorStatus.prettyPrint()) else: for oid, val in pMod.apiPDU.getVarBinds(rspPDU): print('%s = %s' % (oid.prettyPrint(), val.prettyPrint())) transportDispatcher.jobFinished(1) return wholeMsg transportDispatcher = AsynsockDispatcher() transportDispatcher.registerRecvCbFun(Receive) transportDispatcher.registerTimerCbFun(Timer) # UDP/IPv4 transportDispatcher.registerTransport( udp.domainName, udp.UdpSocketTransport().openClientMode() ) # Pass message to dispatcher transportDispatcher.sendMessage( encoder.encode(reqMsg), udp.domainName, ('192.168.1.101', 161) ) transportDispatcher.jobStarted(1)
# Match response to request if pMod.apiPDU.getRequestID(reqPDU)==pMod.apiPDU.getRequestID(rspPDU): # Check for SNMP errors reported errorStatus = pMod.apiPDU.getErrorStatus(rspPDU) if errorStatus: print(errorStatus.prettyPrint()) else: for oid, val in pMod.apiPDU.getVarBinds(rspPDU): print('%s = %s' % (oid.prettyPrint(), val.prettyPrint())) transportDispatcher.jobFinished(1) return wholeMsg transportDispatcher = AsynsockDispatcher() transportDispatcher.registerRecvCbFun(cbRecvFun) transportDispatcher.registerTimerCbFun(cbTimerFun) # UDP/IPv4 transportDispatcher.registerTransport( udp.domainName, udp.UdpSocketTransport().openClientMode() ) # Pass message to dispatcher transportDispatcher.sendMessage( encoder.encode(reqMsg), udp.domainName, ('192.168.22.241', 161) ) transportDispatcher.jobStarted(1) ''' ## UDP/IPv6 (second copy of the same PDU will be sent) transportDispatcher.registerTransport(
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