local skynet = require "skynet" local oo = require "Class" local gameCmd = require "GameCmd" local json =require "json" local log = require "Log" local sqlUrl = require "SqlUrl" local errorInfo = require "ErrorInfo" local curServerId = tonumber(skynet.getenv "serverId") local redisKeyUrl = require "RedisKeyUrl" local clusterServer = require "ClusterServer" local Monitor = oo.class(clusterServer) Monitor.per5Second = 5 Monitor.per5SecondIpCount = 100 --每5秒IP最大访问次数 Monitor.iPUnlockTime = 3600 --黑名单IP解锁时间 Monitor.IpOutTime = 1800 --访问IP过期时间 Monitor.MaxIllegalCount = 200 --最大非法数量 Monitor.ResetTriggerTime = 300 --重置触发规则时间为5分钟 --触发类型 Monitor.TriggerType_MsgTooFast = 1 --消息过快 Monitor.TriggerType_Max = 2 --触发最大类型 --[[ 触发信息 下面为通用参数,每条信息都必须带,其余参数可以根据条件自己定 比如触发MsgTooFast 的自定义参数为cmds type 触发类型 funcName 函数名,里面的字符串必须和定的函数名一样,否则不会触发相应功能 count 触发次数,达到一定数量会将IP加入到黑名单里 firstTime 第一次触发时间 ]] Monitor.TriggerInfo = { { type = Monitor.TriggerType_MsgTooFast , funcName = "CheckMsgTooFast" , count = 0 , firstTime = 0 , cmds = {} } --触发消息过快 参数 cmds 保存消息各种信息 } --初始化 function Monitor:Init() if true then return true end self.ipBlackList = {} --IP黑名单 self.ipList = {} --统计当前IP log.info("开始同步IP黑名单") local key = redisKeyUrl.MonitorServerIPBlackListZSet local ipList = skynet.server.redis:zrange( key , 0 , -1 , "withscores") ipList = redisKeyUrl:CovertTable(ipList) local ipCount = 0 for ip, recordTime in pairs( ipList ) do self.ipBlackList[ ip ] = {} self.ipBlackList[ ip ].recordTime = recordTime ipCount = ipCount + 1 end log.info(string.format("成功同步IP黑名单 数量 %d",ipCount)) end --跨天 function Monitor:OnNewDay() end --1秒Timer function Monitor:On1SecTimer() end --5秒Timer function Monitor:On5SecTimer() --self:CheckAttack() --self:CheckIpOutTime() --self:CheckUnlockIP() --self:ResetTrigger() end --检测攻击行为 --[[ function Monitor:CheckAttack() local curTime = skynet.GetTime() for k, v in pairs( self.ipList ) do if curTime <= v.record5SecTime + self.per5Second then if v.record5SecCount >= self.per5SecondIpCount then --在指定的时间内大于阈值就拉黑 self:AddIPBlackList( k ) end end end end ]] --是否为合法IP function Monitor:IsVaildIP( ip , cmd ) if self.ipBlackList[ ip ] then return errorInfo.ErrorCode.IPBlackList end local errorCode = errorInfo.Suc cmd = tostring(cmd) local curTime = skynet.GetTime() if not self.ipList[ ip ] then self.ipList[ ip ] = {} --一开始就需要用到该触发器可以在这里加,其它可以在触发功能的地方加都行。 self:AddTrigger( ip , self.TriggerType_MsgTooFast ) else --对该IP的各种触发检测 for k, v in pairs( self.ipList[ ip ].trigger ) do errorCode = self[ v.funcName ]( self , ip , cmd ) if errorInfo.Suc ~= errorCode then return errorCode end end end self.ipList[ ip ].lastRecordTime = curTime --记录最新时间 return errorInfo.Suc end --添加IP到黑名单 function Monitor:AddIPBlackList( ip ) local key = redisKeyUrl.MonitorServerIPBlackListZSet skynet.server.redis:zadd( key , skynet.GetTime() , ip ) self.ipBlackList[ ip ] = {} self.ipBlackList[ ip ].recordTime = skynet.GetTime() local c2sData = {} c2sData.sendServerId = curServerId c2sData.ip = ip local curServer = skynet.server.serverManage:GetServer() for k, v in pairs( curServer.clusterInfo ) do if curServerId ~= v.serverId then curServer:SendMsgToServer( v.serverId , curServer.Moniter2All_AddIPToBlack , c2sData ) end end log.info(string.format("监控服 IP %s 加入黑名单 " , ip )) end --检测IP是否能解锁 function Monitor:CheckUnlockIP() --删除当前时间之前一小时的黑名单IP local key =redisKeyUrl.MonitorServerIPBlackListZSet local unlockTime = skynet.GetTime() - self.iPUnlockTime skynet.server.redis:zremrangebyscore( key , "-inf" , unlockTime ) for ip, v in pairs( self.ipBlackList ) do if skynet.GetTime() >= v.recordTime + self.iPUnlockTime then self.ipBlackList[ ip ] = nil end end end --检测过期IP function Monitor:CheckIpOutTime() local curTime = skynet.GetTime() for k, v in pairs( self.ipList ) do if curTime >= v.lastRecordTime + self.IpOutTime then --删除过期IP self.ipList[ k ] = nil end end end --增加触发 function Monitor:AddTrigger( ip , triggerType ) triggerType = triggerType or 0 if triggerType <= 0 or triggerType >= self.TriggerType_Max then log.info(string.format("监控服 IP %s 增加触发 %d 类型失败 " , ip , triggerType )) return end if not self.ipList[ ip ].trigger then --初始化该IP的触发器 self.ipList[ ip ].trigger = {} end if not self.ipList[ ip ].trigger[ triggerType ] then --该IP没有指定触发类型就加进来 for k, v in pairs( self.TriggerInfo ) do if triggerType == v.type then self.ipList[ ip ].trigger[ triggerType ] = v break end end end end --重置触发 function Monitor:ResetTrigger() local curTime = skynet.GetTime() for k1, v1 in pairs( self.ipList ) do for k2, v2 in pairs( v1.trigger ) do if curTime - v2.firstTime >= self.ResetTriggerTime then --违规信息重置 v2.firstTime = 0 v2.count = 0 end end end end --获取触发 function Monitor:GetTrigger( ip , triggerType ) triggerType = triggerType or 0 if triggerType <= 0 or triggerType >= self.TriggerType_Max then log.info(string.format("监控服 IP %s 获取触发 %d 类型失败 " , ip , triggerType )) return end return self.ipList[ ip ].trigger[ triggerType ] end --触发违规 function Monitor:TriggerIllegal( ip , triggerType ) local curTrigger = self:GetTrigger( ip , triggerType ) curTrigger.count = curTrigger.count + 1 if 0 == curTrigger.firstTime then --记录下首次触发时间 curTrigger.firstTime = skynet.GetTime() end --5分钟内,触发最大次数,加入黑名单 if curTrigger.count >= self.MaxIllegalCount then self:AddIPBlackList( ip ) end end --检查是否消息过快 function Monitor:CheckMsgTooFast( ip , cmd ) local curTime = skynet.GetTime() --获取当前类型的触发器 local triggerType = self.TriggerType_MsgTooFast local curTrigger = self:GetTrigger( ip , triggerType ) if not curTrigger.cmds[ cmd ] then --对此命令进行初始化 curTrigger.cmds[ cmd ] = {} curTrigger.cmds[ cmd ].recordTime = curTime else local cmdInfo = curTrigger.cmds[ cmd ] if curTime - cmdInfo.recordTime <= 0 then --1秒内两次执行相同命令就返回消息过快的错误,然后触发非法类型 self:TriggerIllegal( ip , triggerType ) return errorInfo.ErrorCode.RequestMsgTooFast else curTrigger.cmds[ cmd ].recordTime = curTime end end return errorInfo.Suc end skynet.server.monitor = Monitor return Monitor