|
1 // Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include <e32base.h> |
|
17 #include <es_sock.h> // for BigEndian |
|
18 #include <e32hal.h> |
|
19 #include <icmp6_hdr.h> |
|
20 #include <ip4_hdr.h> |
|
21 #include "translationtable.h" |
|
22 #include "hookdefs.h" |
|
23 #include "naptconfigtable.h" |
|
24 |
|
25 |
|
26 |
|
27 //NAPT Translation Table class Constructor |
|
28 CNaptIPPortMap *CNaptIPPortMap:: NewL() |
|
29 { |
|
30 CNaptIPPortMap *self = new(ELeave) CNaptIPPortMap; |
|
31 return self; |
|
32 } |
|
33 |
|
34 //NAPT Translation Table class Destructor |
|
35 CNaptIPPortMap::~CNaptIPPortMap() |
|
36 {} |
|
37 |
|
38 |
|
39 //NAPT Translation Table Manager Constructor.. |
|
40 TNaptTableMapMgr::TNaptTableMapMgr() |
|
41 { |
|
42 iIcmpQueryId = KNaptIcmpQuery_HIGH; |
|
43 iLastPort = KNaptPort_HIGH; |
|
44 iBucketsInUse= 0; |
|
45 iPublicGatewayIP=0; |
|
46 } |
|
47 |
|
48 //NAPT Translation Table Manager Destructor.. |
|
49 TNaptTableMapMgr::~TNaptTableMapMgr() |
|
50 { } |
|
51 |
|
52 CNaptIPPortMap* TNaptTableMapMgr::GetIPTranslationNode(TUint16 aDstPortNo) |
|
53 /** |
|
54 * Gets the translated ip & port number information |
|
55 * Mainly used in incoming packet conversion |
|
56 @internalTechnology |
|
57 @param aTrDstPort - Unique Translated Port Information |
|
58 * |
|
59 **/ |
|
60 { |
|
61 TInt bucket = FindBucket(aDstPortNo); |
|
62 CNaptIPPortMap* table = NULL; |
|
63 |
|
64 TNaptTableIter naptTableIter(iIPPortMap[bucket]); |
|
65 naptTableIter.SetToFirst(); |
|
66 |
|
67 while ((table = naptTableIter++) != NULL) |
|
68 { |
|
69 if (table->iTrPort == aDstPortNo) |
|
70 { |
|
71 return table; |
|
72 } |
|
73 }//while |
|
74 |
|
75 return NULL; |
|
76 } |
|
77 |
|
78 |
|
79 CNaptIPPortMap* TNaptTableMapMgr::AllocateTranslationNodeL(TUint aProtocolType,const TUint32& aSrcAddr,const TUint32& aDstAddr,const TUint16 aSrcPort ,const TUint16 aDstPort, const TNaptConfigInfo* aConfigInfo, TUint16 aQueryId) |
|
80 /** |
|
81 *Allocates Translation Node with a new unique translated port number for |
|
82 *translated IP for TCP/UDP connections. |
|
83 @param aProtocolType |
|
84 @param aSrcAddr |
|
85 @param aDstAddr |
|
86 @param aSrcPort |
|
87 @param aDstPort |
|
88 @param aConfigInfo |
|
89 @param aQueryId |
|
90 * |
|
91 * The function is called only once when the first packet in the session passes |
|
92 * through NAPT hook. |
|
93 * The function is called only when the corresponding arguments are not present |
|
94 * anywhere in the indexed translation table list. |
|
95 * For TCP/UDP protocols the function generates new unique port number in the |
|
96 * predefined range. The generated port is used as a key to index in the |
|
97 * translation table. |
|
98 * For ICMP packets like ICMP echo/reply(used in ping application), the function |
|
99 * doesnot generates a port number, instead it uses the QueryId of the ICMP |
|
100 * message, ie the QueryId is used as the key to index the translation table. |
|
101 * |
|
102 * The index is generated based on the key using the FindBucket Hash Function, |
|
103 * a new node is linked to the indexed node. |
|
104 * |
|
105 * returns the filled CNaptIPPortMap Translated Node information. |
|
106 **/ |
|
107 { |
|
108 |
|
109 TInt index =0; |
|
110 CNaptIPPortMap *tableNode=NULL; |
|
111 |
|
112 if (iLastPort == KNaptPort_LOW) //lower range of the port |
|
113 { |
|
114 iLastPort = KNaptPort_HIGH; |
|
115 } |
|
116 |
|
117 |
|
118 // we have to allocate memory..so we will allocate in the begining itself |
|
119 // fill all values except translated port number |
|
120 tableNode = CNaptIPPortMap::NewL(); |
|
121 |
|
122 tableNode->iCurTime= User::NTickCount(); |
|
123 tableNode->iProtocolType = aProtocolType; |
|
124 tableNode->iSrcPort = aSrcPort; //incase of ICMP echo request it stores original query id |
|
125 tableNode->iDstPort = aDstPort; |
|
126 |
|
127 //we should be knowing what tranlsated destination address to be filled |
|
128 tableNode->iTrIpAddr = iPublicGatewayIP; |
|
129 tableNode->iSrcIpAddr = aSrcAddr; |
|
130 tableNode->iDstIpAddr = aDstAddr; |
|
131 tableNode->iConfigInfo = const_cast<TNaptConfigInfo* >(aConfigInfo); |
|
132 |
|
133 //for ICMP messages or If full indexed TranslationTable is Full |
|
134 if (iBucketsInUse == KTranslationHashTableSize || aProtocolType == KProtocolInetIcmp) // table is full, |
|
135 { |
|
136 TInt key; |
|
137 if (aProtocolType == KProtocolInetIcmp) |
|
138 { |
|
139 key = aQueryId; |
|
140 } |
|
141 else |
|
142 { |
|
143 key = iLastPort--; |
|
144 } |
|
145 |
|
146 |
|
147 tableNode->iTrPort = key; |
|
148 |
|
149 index = FindBucket(key); |
|
150 if (iIPPortMap[index].IsEmpty()) |
|
151 { |
|
152 iBucketsInUse++; |
|
153 } |
|
154 iIPPortMap[index].AddFirst(*tableNode); |
|
155 |
|
156 } |
|
157 else |
|
158 { |
|
159 |
|
160 //following algorithm is the Open Addressing Algorithm for finding out the empty slot. |
|
161 TInt tempCnt=0; |
|
162 |
|
163 index = FindBucket(iLastPort); |
|
164 while (! iIPPortMap[index].IsEmpty() && (tempCnt <= iBucketsInUse) ) |
|
165 { |
|
166 --iLastPort; // decrementthe iLastPort, hope that next index generated will be empty in translation table. |
|
167 |
|
168 if (iLastPort <= KNaptPort_LOW) //lower range of the port |
|
169 { |
|
170 iLastPort = KNaptPort_HIGH; |
|
171 } |
|
172 |
|
173 ++tempCnt; |
|
174 index = FindBucket(iLastPort); |
|
175 |
|
176 }//while |
|
177 |
|
178 |
|
179 //found an empty slot... from above loop |
|
180 if (iIPPortMap[index].IsEmpty()) |
|
181 { |
|
182 iBucketsInUse++; |
|
183 } |
|
184 iIPPortMap[index].AddFirst( *tableNode); |
|
185 tableNode->iTrPort = iLastPort--; |
|
186 }//else |
|
187 |
|
188 //time.. to start timer |
|
189 if (iBucketsInUse == 1 ) //time.. to start timer |
|
190 { |
|
191 iTimerPtr->StartTimer(); |
|
192 } |
|
193 return tableNode; |
|
194 |
|
195 } |
|
196 |
|
197 |
|
198 CNaptIPPortMap* TNaptTableMapMgr::FindOrCreateNaptEntryL( TUint aProtocolType,const TUint32& aSrcAddr,const TUint32& aDstAddr,const TUint16 aSrcPort,const TUint16 aDstPort, const TNaptConfigInfo *aConfigInfo) |
|
199 /** |
|
200 *Finds Translation Node if already existing or creates a new node filled with unique translated port number for |
|
201 *translated IP for TCP/UDP/ICMP connections. |
|
202 @param aProtocolType- TCP/UDP/ICMP |
|
203 @param aSrcAddr - Source Address from which packet is originated |
|
204 @param aDstAddr - Destination Address to which packet is destined |
|
205 @param aSrcPort - Source Port from which packet is originated |
|
206 @param aDstPort - Destination Port to which packet is destined |
|
207 @param aConfigInfo - Configuration to be used for packet translation |
|
208 * |
|
209 * The function is called each time the packet needs translation passes through NAPT hook |
|
210 * Function First checks any NAPT table node exists with the matched arguments. |
|
211 * If the node does not exist, creates a new node. |
|
212 * returns the filled CNaptIPPortMap Translated Node information. |
|
213 **/ |
|
214 { |
|
215 CNaptIPPortMap *table=NULL; |
|
216 for (TInt index=0;index<KTranslationHashTableSize;index++) |
|
217 { |
|
218 |
|
219 TNaptTableIter naptTableIter(iIPPortMap[index]); |
|
220 naptTableIter.SetToFirst(); |
|
221 while ((table = naptTableIter++) != NULL) |
|
222 { |
|
223 if (table->iSrcPort == aSrcPort && table->iDstPort== aDstPort |
|
224 && table->iProtocolType == aProtocolType |
|
225 && table->iSrcIpAddr==aSrcAddr && table->iDstIpAddr==aDstAddr) |
|
226 { |
|
227 table->iCurTime = User::NTickCount(); |
|
228 return table; |
|
229 } |
|
230 } |
|
231 } |
|
232 //there is no entry in the translation table..make an entry.. |
|
233 if (aProtocolType == KProtocolInetIcmp) |
|
234 { |
|
235 if (iIcmpQueryId == KNaptIcmpQuery_LOW) |
|
236 { |
|
237 iIcmpQueryId=KNaptIcmpQuery_LOW; |
|
238 } |
|
239 } |
|
240 |
|
241 table = AllocateTranslationNodeL(aProtocolType, aSrcAddr,aDstAddr,aSrcPort,aDstPort,aConfigInfo,iIcmpQueryId--); |
|
242 |
|
243 return table; |
|
244 |
|
245 } |
|
246 |
|
247 void TNaptTableMapMgr::TimerComplete() |
|
248 /** |
|
249 * |
|
250 * Deletes the timed out transactions |
|
251 * Function is called after timer expiry interval. |
|
252 * If there are no entries present, Stop the Timer. |
|
253 * The functions scans through all translation table entries and deletes the |
|
254 * node entries which are old ones based on the configured protocol time out entries. |
|
255 * |
|
256 **/ |
|
257 { |
|
258 |
|
259 CNaptIPPortMap* table=NULL; |
|
260 TBool deleteFlag= EFalse; |
|
261 TUint32 curTime; |
|
262 TInt seconds=0; |
|
263 |
|
264 for (TInt index=0; index< KTranslationHashTableSize; index++) |
|
265 { |
|
266 TNaptTableIter naptTableIter(iIPPortMap[index]); |
|
267 naptTableIter.SetToFirst(); |
|
268 |
|
269 while ((table = naptTableIter++) != NULL) |
|
270 { |
|
271 deleteFlag = EFalse; |
|
272 curTime = User::NTickCount(); |
|
273 if (curTime <= table->iCurTime)//checking clock wrap around |
|
274 { |
|
275 seconds = ((KMaxTUint32 - table->iCurTime) + curTime)/1000; |
|
276 } |
|
277 else |
|
278 { |
|
279 seconds = (curTime - table->iCurTime)/1000; |
|
280 } |
|
281 |
|
282 //Depending on the protocol type delete transactions |
|
283 switch (table->iProtocolType) |
|
284 { |
|
285 case KProtocolInetTcp: |
|
286 if (seconds >= iTimerPtr->iNaptTcpIdleTimeout)//this is for TCP Inactive Connections.. |
|
287 { |
|
288 deleteFlag=ETrue; |
|
289 } |
|
290 if (seconds >= iTimerPtr->iNaptTcpCloseTimeout && table->iProtocolFlag==KTcpCloseDeletePacket ) |
|
291 { |
|
292 deleteFlag=ETrue; |
|
293 } |
|
294 if (seconds >= iTimerPtr->iNaptTcpOpenTimeout && table->iProtocolFlag==KTcpCtlSYN) |
|
295 { |
|
296 deleteFlag=ETrue; |
|
297 } |
|
298 |
|
299 break; |
|
300 |
|
301 case KProtocolInetUdp: |
|
302 if (seconds >= iTimerPtr->iNaptUdpIdleTimeout) |
|
303 { |
|
304 deleteFlag=ETrue; |
|
305 } |
|
306 break; |
|
307 |
|
308 case KProtocolInetIcmp: |
|
309 if (seconds >= iTimerPtr->iNaptIcmpIdleTimeout ) |
|
310 { |
|
311 deleteFlag=ETrue; |
|
312 } |
|
313 break; |
|
314 }//end of switch |
|
315 if (deleteFlag) |
|
316 { |
|
317 DeleteNodeInIndexedList(index,table); |
|
318 } |
|
319 }//end of while loop inside linked list |
|
320 }//end of for loop entire index |
|
321 } |
|
322 |
|
323 |
|
324 |
|
325 void TNaptTableMapMgr::DeleteNaptTableNode( CNaptIPPortMap *aTableNode) |
|
326 /** |
|
327 * |
|
328 * Deletes the specified NAPT Translation Node in the Indexed list. |
|
329 @param aTableNode - Table Node to be Deleted. |
|
330 * |
|
331 */ |
|
332 { |
|
333 TInt index= FindBucket(aTableNode->iTrPort); |
|
334 DeleteNodeInIndexedList(index,aTableNode); |
|
335 |
|
336 } |
|
337 |
|
338 void TNaptTableMapMgr::DeleteNodeInIndexedList(TInt aIndex, CNaptIPPortMap *aTableNode) |
|
339 /* |
|
340 * |
|
341 * Deletes the specified NAPT table node in the indexed linked list. |
|
342 @param----aIndex - index at which aTableNode exist. |
|
343 @param----aTableNode - napt table node to be deleted |
|
344 * After deleting the specified node, decrement the iBucketsInUse if the indexed list is empty. |
|
345 * Stop the timer, if there are no napt table nodes exists for translation. |
|
346 * |
|
347 */ |
|
348 { |
|
349 |
|
350 iIPPortMap[aIndex].Remove(*aTableNode); |
|
351 delete aTableNode; |
|
352 if (iIPPortMap[aIndex].IsEmpty())//check list is empty. |
|
353 { |
|
354 iBucketsInUse--; |
|
355 } |
|
356 |
|
357 if (iBucketsInUse == 0) //Stop the Timer as there are no connections to translate |
|
358 { |
|
359 iTimerPtr->Cancel(); |
|
360 } |
|
361 } |
|
362 |
|
363 TBool TNaptTableMapMgr::VerifySender(const CNaptIPPortMap *aTableNode, const TUint32 &aSrcIp,TUint16 aSrcPort) |
|
364 /* |
|
365 * |
|
366 * Function Matches the Sender IP and Port Number with the Stored NAPT translated node |
|
367 @param---aTableNode - Napt Table Node to which the Sender IP & Port Number to be matched. |
|
368 @param---aSrcIp - Sender's source IP Address |
|
369 @param----aSrcPort - Sender's source port number |
|
370 * |
|
371 */ |
|
372 { |
|
373 if ( aTableNode->iDstPort == aSrcPort && aTableNode->iDstIpAddr==aSrcIp) |
|
374 { |
|
375 return ETrue; |
|
376 } |
|
377 return EFalse; |
|
378 } |
|
379 |
|
380 |
|
381 |
|
382 void TNaptTableMapMgr::HandleTcpConnectionPhases(TInet6Checksum<TInet6HeaderTCP>& aTcpPacket, CNaptIPPortMap* aTableNode,const TInt aPacketDirection) |
|
383 /* |
|
384 * |
|
385 * Function Handles Tcp Close Connection Phase mainly.. and open sequence, |
|
386 * and open sequence,if it is originated during close sequence. |
|
387 @param---aTcpPacket - Tcp Packet to be checked for FIN,ACK,RST & SYN Bits |
|
388 @param----TableNode - Napt Table Node at which the TCP packet's above information to be stored. |
|
389 @param-----aPacketDirection - Direction which packet is travelling IN/OUT |
|
390 * The NAPT table node will be marked for deletion if corresponding FIN+ACK messages are exchanged. |
|
391 * But NAPT table node will be deleted immediately after the RST Flag is received from the private |
|
392 * |
|
393 */ |
|
394 { |
|
395 |
|
396 TUint8 synBit = aTcpPacket.iHdr->SYN(); |
|
397 if (synBit) |
|
398 { |
|
399 //Reset the protocol Flag to have only to have Syn Bit..so that if pending fin is cancelled. |
|
400 //The reset enables SYN to be received, ie new connection initiation request to pass through |
|
401 // and to cancel the NAPT table node entry node for deletion |
|
402 aTableNode->iProtocolFlag = KTcpCtlSYN; |
|
403 //its good to return now here as we dont need to check, as other bits like RST/FIN wont |
|
404 //be transmitted along with SYN bit message. |
|
405 return; |
|
406 } |
|
407 else |
|
408 { |
|
409 //Reset Syn Bit...to come out of TCP SYN open timer.. |
|
410 aTableNode->iProtocolFlag &= ~KTcpCtlSYN; |
|
411 } |
|
412 |
|
413 TUint8 ackBit = aTcpPacket.iHdr->ACK(); |
|
414 TUint8 finBit = aTcpPacket.iHdr->FIN(); |
|
415 TUint8 rstBit = aTcpPacket.iHdr->RST(); |
|
416 |
|
417 //Putting this below condition before the actual FIN bit, ensures that FIN+ACK in the same packet |
|
418 //would not mark the packet for deletion. The node is marked for delete after the receive of FIN |
|
419 //exchanges from both ends. |
|
420 //The NAPT table node is deleted only after close time out period after receiving last ACK which initiated FIN. |
|
421 if ((aTableNode->iProtocolFlag == (KTcpClosePacketOUT|KTcpClosePacketIN)) && ackBit ) |
|
422 { |
|
423 //The above condition ensures that ACK is received for the corresponding FIN which has been sent earlier. |
|
424 //However it is not checking ACK received is the one corresponding ACK for the FIN(can be done by sequence number matching) |
|
425 //As we are not deleting NAPT table node immediately, the execption of receiving ACK for the not |
|
426 //correct FIN can be ignored and we rely on TCP clien ends resolve the correct sequence number if they are not the same. |
|
427 |
|
428 |
|
429 //Also when FIN is sent by the end client(its in HALF close FIN_WAIT state), it wont send any |
|
430 //further data but it is able to recive data. The next message sent by the closing client, |
|
431 //is either FIN(retransmission) or ACK. |
|
432 |
|
433 //It handles many Close Scenarios like below(list.. is not complete) |
|
434 // 1.FIN ->, <- ACK, <- FIN, ACK -> (rare, but possible)[Simulteneous close] |
|
435 // 2.FIN+ACK ->, <- FIN+ACK, ACK -> |
|
436 // 3.FIN ->, <- FIN+ACK, ACK -> |
|
437 // 4.FIN+ACK ->, <- FIN, <- ACK, ACK ->(rare, but possible)[very Simulteneous close]..etc |
|
438 // However in last case..the table node is marked before the last ACk is originated. This seems ok, as we are not |
|
439 // deleting any packet |
|
440 aTableNode->iProtocolFlag |= ackBit ; //now it is KTcpCloseDeletePacket |
|
441 |
|
442 } |
|
443 if (finBit) |
|
444 { |
|
445 aTableNode->iProtocolFlag |= aPacketDirection; |
|
446 return; |
|
447 } |
|
448 |
|
449 |
|
450 //Delete NAPT table node entry only if RST is originated from private subnet |
|
451 //RST coming from global interface have to wait for Close timeout period |
|
452 //for the entry to be deleted. |
|
453 if (rstBit && (aPacketDirection == KTcpClosePacketOUT)) |
|
454 { |
|
455 DeleteNaptTableNode(aTableNode); |
|
456 } |
|
457 |
|
458 } |
|
459 |