def handle_connect(self): if self.isUp: return self.isUp = True if self.__announcementData: self.send(self.__announcementData) self.__announcementData = null log.debug('%s: trunk announcement sent' % (self,)) log.info('%s: client is now connected' % (self,))
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)
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('trunk message #%s, SNMP response error: %s' % (msgId, sys.exc_info()[1]), ctx=logCtx) else: log.debug( 'received trunk message #%s, forwarded as SNMP message' % msgId, ctx=logCtx) self.releaseStateInformation(stateReference)
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)
for lineNo, line in enumerate(open(configFile).readlines()): line = line.strip() if not line or line.startswith('#'): continue try: oidPatt, valPatt, valRepl, replCount = shlex.split(line) except ValueError: raise SnmpfwdError('%s: syntax error at %s:%d: %s' % (PLUGIN_NAME, configFile, lineNo + 1, sys.exc_info()[1])) debug( '%s: for OIDs like "%s" and values matching "%s" rewrite value into "%s" (max %s times)' % (PLUGIN_NAME, oidPatt, valPatt, valRepl, replCount)) rewriteList.append( (re.compile(oidPatt), re.compile(valPatt), valRepl, int(replCount))) except Exception: raise SnmpfwdError('%s: config file load failure: %s' % (PLUGIN_NAME, sys.exc_info()[1])) info('%s: plugin initialization complete' % PLUGIN_NAME) def processCommandResponse(pluginId, snmpEngine, pdu, trunkMsg, reqCtx): varBinds = []
def dataCbFun(trunkId, msgId, msg): log.debug('message ID %s received from trunk %s' % (msgId, trunkId))
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 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 processCommandRequest(pluginId, snmpEngine, pdu, trunkMsg, reqCtx): reqOids = [] reqCtx['req-oids'] = reqOids reqCtx['req-pdu'] = pdu if pdu.tagSet in (v2c.GetRequestPDU.tagSet, v2c.SetRequestPDU.tagSet): allDenied = True deniedOids = [] for varBindIdx, varBind in enumerate(v2c.apiPDU.getVarBindList(pdu)): oid, val = v2c.apiVarBind.getOIDVal(varBind) oid = tuple(oid) skip, aclIdx = findAcl(oid) # skipped to the next OID if skip != oid: aclIdx = None # OID went out of range if aclIdx is None: # report OIDs we are sending errors for if logDenials: deniedOids.append(str(varBind[0])) else: allDenied = False reqOids.append((oid, varBindIdx, aclIdx)) if logDenials and deniedOids: denialMsg = formatDenialMsg(pdu, trunkMsg) denialMsg += ' OIDs ' + ', '.join(deniedOids) denialMsg += ' denied' error(denialMsg) reqVarBinds = v2c.VarBindList() if allDenied: pdu = v2c.apiPDU.getResponse(pdu) for oid, _, _ in reqOids: varBind = v2c.VarBind() v2c.apiVarBind.setOIDVal(varBind, (oid, noSuchInstance)) reqVarBinds.append(varBind) nextAction = status.RESPOND else: for oid, _, aclIdx in reqOids: if aclIdx is None: continue varBind = v2c.VarBind() v2c.apiVarBind.setOIDVal(varBind, (oid, null)) reqVarBinds.append(varBind) nextAction = status.NEXT v2c.apiPDU.setVarBindList(pdu, reqVarBinds) return nextAction, pdu elif pdu.tagSet == v2c.GetNextRequestPDU.tagSet: allDenied = True skippedOids = [] for varBindIdx, varBind in enumerate(v2c.apiPDU.getVarBindList(pdu)): oid, val = v2c.apiVarBind.getOIDVal(varBind) oid = tuple(oid) skip, aclIdx = findAcl(oid, nextOid=True) if logDenials and oid != skip: skippedOids.append((oid, skip)) oid = skip # OID went out of range if aclIdx is not None: allDenied = False reqOids.append((oid, varBindIdx, aclIdx)) if logDenials and skippedOids: denialMsg = formatDenialMsg(pdu, trunkMsg) denialMsg += ' ' + ', '.join([ '%s not in range skipping to %s' % (v2c.ObjectIdentifier(x[0]), v2c.ObjectIdentifier(x[1])) for x in skippedOids ]) info(denialMsg) reqVarBinds = v2c.VarBindList() if allDenied: pdu = v2c.apiPDU.getResponse(pdu) for oid, _, _ in reqOids: varBind = v2c.VarBind() v2c.apiVarBind.setOIDVal(varBind, (oid, endOfMibView)) reqVarBinds.append(varBind) nextAction = status.RESPOND else: for oid, _, aclIdx in reqOids: if aclIdx is None: continue varBind = v2c.VarBind() v2c.apiVarBind.setOIDVal(varBind, (oid, null)) reqVarBinds.append(varBind) nextAction = status.NEXT v2c.apiPDU.setVarBindList(pdu, reqVarBinds) return nextAction, pdu elif pdu.tagSet == v2c.GetBulkRequestPDU.tagSet: nonRepeaters = v2c.apiBulkPDU.getNonRepeaters(pdu) maxRepeaters = v2c.apiBulkPDU.getMaxRepetitions(pdu) nonRepOids = [] linearizedOids = [] repOids = [] linearizedOidsMap = {} repeatersOidMap = {} reqCtx['linearized-oids-map'] = linearizedOidsMap reqCtx['repeaters-oids-map'] = repeatersOidMap reqCtx['non-repeaters'] = nonRepeaters allDenied = True skippedOids = [] for varBindIdx, varBind in enumerate( v2c.apiBulkPDU.getVarBindList(pdu)): oid, val = v2c.apiVarBind.getOIDVal(varBind) oid = tuple(oid) skip, aclIdx = findAcl(oid, nextOid=True) if logDenials and oid != skip: skippedOids.append((oid, skip)) oid = skip # original request var-binds reqOids.append((oid, varBindIdx, aclIdx)) # OID went beyond all ranges if aclIdx is None: continue allDenied = False # original request non-repeaters if varBindIdx < nonRepeaters: nonRepOids.append((oid, varBindIdx, aclIdx)) continue # original request repeaters skip, begin, end = oidsList[aclIdx] # move single-OID ranges from repeaters into non-repeaters startIdx = len(nonRepOids) + len(linearizedOids) endIdx = startIdx if begin == end: while endIdx - startIdx < maxRepeaters: linearizedOids.append((oid, varBindIdx, aclIdx)) endIdx += 1 aclIdx += 1 if aclIdx >= len(endOids): break oid, begin, end = oidsList[aclIdx] if begin != end: break linearizedOidsMap[varBindIdx] = startIdx, endIdx debug( '%s: repeating OID #%d (%s) converted into non-repeaters #%d..%d' % (PLUGIN_NAME, varBindIdx, varBind[0], startIdx, endIdx - 1)) continue # proceed with original repeaters repeatersOidMap[varBindIdx] = len(repOids) repOids.append((oid, varBindIdx, aclIdx)) if logDenials and skippedOids: denialMsg = formatDenialMsg(pdu, trunkMsg) denialMsg += ' ' + ', '.join([ '%s not in range skipping to %s' % (v2c.ObjectIdentifier(x[0]), v2c.ObjectIdentifier(x[1])) for x in skippedOids ]) info(denialMsg) # assemble new var-binds reqVarBinds = v2c.VarBindList() if allDenied: pdu = v2c.apiBulkPDU.getResponse(pdu) for oid, _, _ in reqOids: varBind = v2c.VarBind() v2c.apiVarBind.setOIDVal(varBind, (oid, endOfMibView)) reqVarBinds.append(varBind) nextAction = status.RESPOND else: for varBindIdx in repeatersOidMap: repeatersOidMap[varBindIdx] += len(nonRepOids) + len( linearizedOids) for oid, varBindIdx, aclIdx in nonRepOids + linearizedOids + repOids: if aclIdx is None: continue varBind = v2c.VarBind() v2c.apiVarBind.setOIDVal(varBind, (oid, null)) reqVarBinds.append(varBind) v2c.apiBulkPDU.setNonRepeaters(pdu, nonRepeaters + len(linearizedOids)) nextAction = status.NEXT v2c.apiPDU.setVarBindList(pdu, reqVarBinds) return nextAction, pdu else: return status.NEXT, pdu
if begin <= prev_begin: raise SnmpfwdError( '%s: begin OID %s not increasing at %s' % (PLUGIN_NAME, begin, configFile)) if end <= prev_end: raise SnmpfwdError( '%s: end OID %s not increasing at %s' % (PLUGIN_NAME, end, configFile)) if begin < prev_end: raise SnmpfwdError( '%s: non-adjacent end OID %s followed by begin OID %s at %s' % (PLUGIN_NAME, prev_end, begin, configFile)) idx += 1 debug('%s: #%d skip to %s allow from %s to %s' % (PLUGIN_NAME, idx, skip, begin, end)) # cast to built-in tuple type for better comparison performance down the road oidsList = [(tuple(skip), tuple(begin), tuple(end)) for skip, begin, end in oidsList] # we use this for pivoting dichotomy search endOids = [x[2] for x in oidsList] except Exception: raise SnmpfwdError('%s: config file load failure: %s' % (PLUGIN_NAME, sys.exc_info()[1])) elif optionName == 'log-denials': logDenials = optionValue == 'true' info('%s: will log denied OIDs' % PLUGIN_NAME)
if method == 'file': rotation = config.get('file', 'rotation') if rotation == 'timed': handler = handlers.TimedRotatingFileHandler( config.get('file', 'destination'), config.get('file', 'timescale'), int(config.get('file', 'interval')), int(config.get('file', 'backupcount')) ) else: raise SnmpfwdError('%s: unknown rotation method %s at %s' % (PLUGIN_NAME, rotation, configFile)) else: raise SnmpfwdError('%s: unknown logging method %s at %s' % (PLUGIN_NAME, method, configFile)) debug('%s: using %s logging method' % (PLUGIN_NAME, method)) logger.setLevel(logging.INFO) pdus = config.get('content', 'pdus') for pdu in pdus.split(): try: pduMap[getattr(v2c, pdu + 'PDU').tagSet] = pdu except AttributeError: raise SnmpfwdError('%s: unknown PDU %s' % (PLUGIN_NAME, pdu)) else: debug('%s: PDU ACL includes %s' % (PLUGIN_NAME, pdu)) try:
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)