Upstream tarball 20080509
[amule.git] / src / kademlia / net / PacketTracking.cpp
blob3292d47edbf7d7e6a835c19c8f3a28409dbdbd99
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2008 Dévai Tamás ( gonosztopi@amule.org )
5 // Copyright (c) 2008 aMule Team ( admin@amule.org / http://www.amule.org )
6 // Copyright (c) 2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.net") / http://www.emule-project.net )
7 //
8 // Any parts of this program derived from the xMule, lMule or eMule project,
9 // or contributed by third-party developers are copyrighted by their
10 // respective authors.
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "PacketTracking.h"
28 #include "../../amule.h"
29 #include "../../Logger.h"
30 #include "../../OtherFunctions.h"
31 #include "../../NetworkFunctions.h"
32 #include "../../ClientList.h"
33 #include "../../GetTickCount.h"
34 #include <common/Macros.h>
35 #include <protocol/kad/Client2Client/UDP.h>
36 #include <protocol/kad2/Client2Client/UDP.h>
39 using namespace Kademlia;
42 CPacketTracking::~CPacketTracking()
44 for (TrackedPacketInMap::iterator it = m_mapTrackPacketsIn.begin(); it != m_mapTrackPacketsIn.end(); ++it) {
45 delete it->second;
47 m_mapTrackPacketsIn.clear();
50 void CPacketTracking::AddTrackedOutPacket(uint32_t ip, uint8_t opcode)
52 // this tracklist tacks _outgoing_ request packets, to make sure incoming answer packets were requested
53 uint32_t now = ::GetTickCount();
54 TrackPackets_Struct track = { ip, now, opcode };
55 listTrackedRequests.push_front(track);
56 while (!listTrackedRequests.empty()) {
57 if (now - listTrackedRequests.back().inserted > SEC2MS(180)) {
58 listTrackedRequests.pop_back();
59 } else {
60 break;
65 bool CPacketTracking::IsOnOutTrackList(uint32_t ip, uint8_t opcode, bool dontRemove)
67 uint32_t now = ::GetTickCount();
68 for (TrackedPacketList::iterator it = listTrackedRequests.begin(); it != listTrackedRequests.end(); ++it) {
69 if (it->ip == ip && it->opcode == opcode && now - it->inserted < SEC2MS(180)) {
70 if (!dontRemove) {
71 listTrackedRequests.erase(it);
73 return true;
76 return false;
79 bool CPacketTracking::InTrackListIsAllowedPacket(uint32_t ip, uint8_t opcode, bool /*bValidSenderkey*/)
81 // this tracklist tacks _incoming_ request packets and acts as a general flood protection by dropping
82 // too frequent requests from a single IP, avoiding response floods, processing time DOS attacks and slowing down
83 // other possible attacks/behavior (scanning indexed files, fake publish floods, etc)
85 // first figure out if this is a request packet to be tracked and its timelimits
86 // timelimits are chosen by estimating the max. frequency of such packets on normal operation (+ buffer)
87 // (those limits are not meant to be fine to be used by normal usage, but only supposed to be a flood detection)
89 uint32_t allowedPacketsPerMinute;
90 const uint8_t dbgOrgOpcode = opcode;
92 switch (opcode) {
93 case KADEMLIA_BOOTSTRAP_REQ:
94 opcode = KADEMLIA2_BOOTSTRAP_REQ;
95 case KADEMLIA2_BOOTSTRAP_REQ:
96 allowedPacketsPerMinute = 2;
97 break;
98 case KADEMLIA_HELLO_REQ:
99 opcode = KADEMLIA2_HELLO_REQ;
100 case KADEMLIA2_HELLO_REQ:
101 allowedPacketsPerMinute = 3;
102 break;
103 case KADEMLIA_REQ:
104 opcode = KADEMLIA2_REQ;
105 case KADEMLIA2_REQ:
106 allowedPacketsPerMinute = 10;
107 break;
108 case KADEMLIA_SEARCH_NOTES_REQ:
109 opcode = KADEMLIA2_SEARCH_NOTES_REQ;
110 case KADEMLIA2_SEARCH_NOTES_REQ:
111 allowedPacketsPerMinute = 3;
112 break;
113 case KADEMLIA_SEARCH_REQ:
114 opcode = KADEMLIA2_SEARCH_KEY_REQ;
115 case KADEMLIA2_SEARCH_KEY_REQ:
116 allowedPacketsPerMinute = 3;
117 break;
118 case KADEMLIA2_SEARCH_SOURCE_REQ:
119 allowedPacketsPerMinute = 3;
120 break;
121 case KADEMLIA_PUBLISH_REQ:
122 opcode = KADEMLIA2_PUBLISH_KEY_REQ;
123 case KADEMLIA2_PUBLISH_KEY_REQ:
124 allowedPacketsPerMinute = 3;
125 break;
126 case KADEMLIA2_PUBLISH_SOURCE_REQ:
127 allowedPacketsPerMinute = 2;
128 break;
129 case KADEMLIA_PUBLISH_NOTES_REQ:
130 opcode = KADEMLIA2_PUBLISH_NOTES_REQ;
131 case KADEMLIA2_PUBLISH_NOTES_REQ:
132 allowedPacketsPerMinute = 2;
133 break;
134 case KADEMLIA_FIREWALLED2_REQ:
135 opcode = KADEMLIA_FIREWALLED_REQ;
136 case KADEMLIA_FIREWALLED_REQ:
137 allowedPacketsPerMinute = 2;
138 break;
139 case KADEMLIA_FINDBUDDY_REQ:
140 allowedPacketsPerMinute = 2;
141 break;
142 case KADEMLIA_CALLBACK_REQ:
143 allowedPacketsPerMinute = 1;
144 break;
145 case KADEMLIA2_PING:
146 allowedPacketsPerMinute = 2;
147 break;
148 default:
149 // not any request packets, so its a response packet - no further checks on this point
150 return true;
153 const uint32_t secondsPerPacket = 60 / allowedPacketsPerMinute;
154 const uint32_t currentTick = ::GetTickCount();
156 // time for cleaning up?
157 if (currentTick - lastTrackInCleanup > MIN2MS(12)) {
158 InTrackListCleanup();
161 // check for existing entries
162 TrackedPacketInMap::iterator it2 = m_mapTrackPacketsIn.find(ip);
163 TrackPacketsIn_Struct *trackEntry;
164 if (it2 == m_mapTrackPacketsIn.end()) {
165 trackEntry = new TrackPacketsIn_Struct();
166 trackEntry->m_ip = ip;
167 m_mapTrackPacketsIn[ip] = trackEntry;
168 } else {
169 trackEntry = it2->second;
172 // search specific request tracks
173 for (TrackPacketsIn_Struct::TrackedRequestList::iterator it = trackEntry->m_trackedRequests.begin(); it != trackEntry->m_trackedRequests.end(); ++it) {
174 if (it->m_opcode == opcode) {
175 // already tracked requests with this opcode, remove already expired request counts
176 if (it->m_count > 0 && currentTick - it->m_firstAdded > SEC2MS(secondsPerPacket)) {
177 uint32_t removeCount = (currentTick - it->m_firstAdded) / SEC2MS(secondsPerPacket);
178 if (removeCount > it->m_count) {
179 it->m_count = 0;
180 it->m_firstAdded = currentTick; // for the packet we just process
181 } else {
182 it->m_count -= removeCount;
183 it->m_firstAdded += SEC2MS(secondsPerPacket) * removeCount;
186 // we increase the counter in any case, even if we drop the packet later
187 it->m_count++;
188 // remember only for easier cleanup
189 trackEntry->m_lastExpire = std::max(trackEntry->m_lastExpire, it->m_firstAdded + SEC2MS(secondsPerPacket) * it->m_count);
191 // now the actual check if this request is allowed
192 if (it->m_count > allowedPacketsPerMinute * 5) {
193 // this is so far above the limit that it has to be an intentional flood / misuse in any case
194 // so we take the next higher punishment and ban the IP
195 AddDebugLogLineM(false, logKadPacketTracking, wxString::Format(wxT("Massive request flood detected for opcode 0x%X (0x%X) from IP "), opcode, dbgOrgOpcode) + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(ip)) + wxT(" - Banning IP"));
196 theApp->clientlist->AddBannedClient(wxUINT32_SWAP_ALWAYS(ip));
197 return false; // drop packet
198 } else if (it->m_count > allowedPacketsPerMinute) {
199 // over the limit, drop the packet but do nothing else
200 if (!it->m_dbgLogged) {
201 it->m_dbgLogged = true;
202 AddDebugLogLineM(false, logKadPacketTracking, wxString::Format(wxT("Request flood detected for opcode 0x%X (0x%X) from IP "), opcode, dbgOrgOpcode) + Uint32toStringIP(wxUINT32_SWAP_ALWAYS(ip)) + wxT(" - Droping packets with this opcode"));
204 return false; // drop packet
205 } else {
206 it->m_dbgLogged = false;
208 return true;
212 // add a new entry for this request, no checks needed since 1 is always ok
213 TrackPacketsIn_Struct::TrackedRequestIn_Struct curTrackedRequest;
214 curTrackedRequest.m_opcode = opcode;
215 curTrackedRequest.m_dbgLogged = false;
216 curTrackedRequest.m_firstAdded = currentTick;
217 curTrackedRequest.m_count = 1;
218 // remember only for easier cleanup
219 trackEntry->m_lastExpire = std::max(trackEntry->m_lastExpire, currentTick + SEC2MS(secondsPerPacket));
220 trackEntry->m_trackedRequests.push_back(curTrackedRequest);
221 return true;
224 void CPacketTracking::InTrackListCleanup()
226 const uint32_t currentTick = ::GetTickCount();
227 const uint32_t dbgOldSize = m_mapTrackPacketsIn.size();
228 lastTrackInCleanup = currentTick;
229 for (TrackedPacketInMap::iterator it = m_mapTrackPacketsIn.begin(); it != m_mapTrackPacketsIn.end();) {
230 TrackedPacketInMap::iterator it2 = it++;
231 if (it2->second->m_lastExpire < currentTick) {
232 delete it2->second;
233 m_mapTrackPacketsIn.erase(it2);
236 AddDebugLogLineM(false, logKadPacketTracking, wxString::Format(wxT("Cleaned up Kad Incoming Requests Tracklist, entries before: %u, after %u"), dbgOldSize, m_mapTrackPacketsIn.size()));