|
1 // Copyright (c) 2006-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 // ip6_frag.cpp - hook for IPv6 fragment header |
|
15 // |
|
16 |
|
17 #include "frag.h" |
|
18 #include <icmp6_hdr.h> // for ICMP Type symbols |
|
19 #include <ext_hdr.h> // for IPv6 fragment header |
|
20 #include "ip6_frag.h" |
|
21 #include <in_pkt.h> |
|
22 #include <in_chk.h> |
|
23 #include <timeout.h> |
|
24 #include "tcpip_ini.h" |
|
25 #include "inet6log.h" |
|
26 |
|
27 // TIp6FragmentHeader |
|
28 // ****************** |
|
29 // This header is at the beginning of each fragment in the |
|
30 // fragment queue. This attempts to be the same size as |
|
31 // the real IPv6 fragment header (8 octets), but the code |
|
32 // should work with any size of this [use of this specific |
|
33 // size avoids some TrimStart() calls in some cases.] |
|
34 // |
|
35 class TIp6FragmentHeader |
|
36 { |
|
37 public: |
|
38 TUint iOffset; // Offset of this fragment (bytes) |
|
39 TUint iLength; // Lenght of this fragment (bytes) |
|
40 }; |
|
41 |
|
42 // |
|
43 // RIp6Fragment |
|
44 // ************ |
|
45 // The IP6 fragment data structure is an RMBufChain containing |
|
46 // - TIp6FragmentHeader |
|
47 // - followed by fragment content |
|
48 // This is the interface to the RMBufFragQ class, a collection |
|
49 // of required methods for accessing the fragment information and |
|
50 // a join operation. |
|
51 // |
|
52 class RIp6Fragment : public RMBufFrag |
|
53 { |
|
54 public: |
|
55 // Internal utility to the other methods. Assumes that the fragment |
|
56 // start is properly aligned to be cast into class pointer. |
|
57 inline TIp6FragmentHeader *Header() const { return (TIp6FragmentHeader *)(First()->Ptr()); } |
|
58 // Return offset of the fragment (bytes) |
|
59 TUint Offset() const {return Header()->iOffset;} |
|
60 // Return length of the fragment (bytes) |
|
61 // (Does not include the fragment header, using |
|
62 // this->Length() will give larger value that |
|
63 // includes the header) |
|
64 TUint FragmentLength() const {return Header()->iLength;} |
|
65 // Join another fragment to this one |
|
66 void Join(RIp6Fragment& aFrag) |
|
67 { |
|
68 TInt overlap = Offset() + FragmentLength() - aFrag.Offset(); |
|
69 if (overlap < 0) |
|
70 { |
|
71 aFrag.Free(); // Should panic? This should not happen! |
|
72 User::Panic(_L("DEBUG"), 0); |
|
73 } |
|
74 else |
|
75 { |
|
76 Header()->iLength += aFrag.FragmentLength() - overlap; |
|
77 aFrag.TrimStart(sizeof(TIp6FragmentHeader) + overlap); |
|
78 Append(aFrag); |
|
79 } |
|
80 } |
|
81 }; |
|
82 |
|
83 |
|
84 // |
|
85 // CFragmentHandler |
|
86 // **************** |
|
87 // |
|
88 class CAssembly; |
|
89 class CFragmentHandler : public CFragmentHeaderHook |
|
90 { |
|
91 public: |
|
92 CFragmentHandler(MNetworkService *aNetwork) : CFragmentHeaderHook(aNetwork) {} |
|
93 ~CFragmentHandler(); |
|
94 inline MNetworkService *Network() { return iNetwork; } |
|
95 CAssembly *LookupL(const RMBufRecvInfo &aInfo, TUint32 aId, TUint aVersion); |
|
96 |
|
97 void ConstructL(); |
|
98 void Cancel(CAssembly *aAssembly); |
|
99 void CancelAll(); |
|
100 TInt ApplyL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo); |
|
101 void Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ); |
|
102 |
|
103 MTimeoutManager *iTimeoutManager; |
|
104 CAssembly *iCache; |
|
105 // |
|
106 TInt iCount; // Current incomplete assemblies |
|
107 TInt iMaxCount; // Maximum number of assemblies |
|
108 TInt iTotal; // Total amount of RMBuf space allocated to assemblies (bytes) |
|
109 TInt iMaxTotal; // Maximum allowed amount of RMBuf space |
|
110 private: |
|
111 TInt Ip6ApplyL(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, const TInet6HeaderFragment &aHdr); |
|
112 TInt Ip4ApplyL(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, const TInet6HeaderIP4 &aHdr); |
|
113 void Ip6Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ, TInet6HeaderIP &aHdr); |
|
114 void Ip4Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ, TInet6HeaderIP4 &aHdr); |
|
115 |
|
116 // |
|
117 // iFagmentId is only used for IPv6. For IPv4, the Id is already in |
|
118 // in each IP packet. |
|
119 // |
|
120 TInt iFragmentId; |
|
121 }; |
|
122 |
|
123 // |
|
124 // CAssembly |
|
125 // ********* |
|
126 // The data structure for a partially assembled packet |
|
127 // |
|
128 class CAssembly : public CBase |
|
129 { |
|
130 friend class CFragmentHandler; |
|
131 friend class CFragmentLinkage; |
|
132 |
|
133 CAssembly(CFragmentHandler &aHandler, const TIp6Addr &aSrc, const TIp6Addr &aDst, TInt aId, TInt aVersion); |
|
134 ~CAssembly(); |
|
135 |
|
136 TInt AddFragmentL(RMBufRecvPacket &aPacket, TInt aTrim, TInt aLength, TInt aOffset, TUint32 aFh0); |
|
137 TInt Add(RMBufRecvPacket &aPacket, TInt aTrim, TInt aLength, TInt aOffset, TUint32 aFh0 = 0); |
|
138 TInt CompletePacket(TInt aRestoreFH = 0); |
|
139 void Timeout(); |
|
140 public: |
|
141 // |
|
142 // Linkage and management |
|
143 // |
|
144 CFragmentHandler &iHandler; |
|
145 CAssembly *iNext; // Linkage of the assemblies in the cache |
|
146 |
|
147 RTimeout iTimeout; // Timer hook |
|
148 #ifndef _LOG |
|
149 protected: |
|
150 #endif |
|
151 const TUint8 iVersion; // Need to have, because TAHI wants FH in IPv6 TimeExceeded ICMP |
|
152 TUint iIsDead:1; // =1, if this assembly is "dead" (matching fragments are dropped) |
|
153 const TUint32 iId; // Fragment identification |
|
154 const TIp6Addr iSrcAddr; |
|
155 const TIp6Addr iDstAddr; |
|
156 |
|
157 TUint32 iFH0; // Saved first 32 bits of the first FH (for TAHI) |
|
158 |
|
159 TInt32 iLength; // Total length of the fragmentable part, when |
|
160 // known (e.g. last fragment received). |
|
161 // [As 32 bit int is used, there shouuld be no |
|
162 // overflow or wrap around problems because |
|
163 // offset field is only 16 bits (in bytes)] |
|
164 TInt iTotal; // The amount of "iTotal" from this assembly. |
|
165 // |
|
166 // The unfragmentable part of the first fragment (if received) |
|
167 // |
|
168 RMBufRecvPacket iHead; |
|
169 RMBufFragQ<RIp6Fragment> iQueue; |
|
170 }; |
|
171 |
|
172 // |
|
173 // CFragmentLinkage |
|
174 // **************** |
|
175 // Glue to bind timeout callback from the timeout manager into Timeout() call |
|
176 // on the CAssembly. |
|
177 // |
|
178 // *NOTE* |
|
179 // This kludgery is all static and compile time, and only used in the constructor |
|
180 // of CFragmentAssembly following this. |
|
181 // |
|
182 |
|
183 // This ungainly manoevure is forced on us because the offset is not evaluated early enough by GCC3.4 to be |
|
184 // passed as a template parameter |
|
185 #if defined(__X86GCC__) || defined(__GCCE__) |
|
186 #define KAssemblyTimeoutOffset 12 |
|
187 __ASSERT_COMPILE(KAssemblyTimeoutOffset == _FOFF(CAssembly, iTimeout)); |
|
188 #else |
|
189 #define KAssemblyTimeoutOffset _FOFF(CAssembly, iTimeout) |
|
190 #endif |
|
191 |
|
192 class CFragmentLinkage : public TimeoutLinkage<CAssembly, KAssemblyTimeoutOffset> |
|
193 { |
|
194 public: |
|
195 static void Timeout(RTimeout &aLink, const TTime & /*aNow*/, TAny * /*aPtr*/) |
|
196 { |
|
197 Object(aLink)->Timeout(); |
|
198 } |
|
199 }; |
|
200 |
|
201 #ifdef _LOG |
|
202 // |
|
203 // LogPrintId |
|
204 // ********** |
|
205 // Purely for LOG output, should not be compiled in the final release |
|
206 // |
|
207 static void LogPrintId(const TDesC &aText, const CAssembly &a) |
|
208 { |
|
209 TInetAddr src, dst; |
|
210 TBuf<70> sbuf; |
|
211 TBuf<70> dbuf; |
|
212 |
|
213 src.SetAddress(a.iSrcAddr); |
|
214 dst.SetAddress(a.iDstAddr); |
|
215 |
|
216 src.OutputWithScope(sbuf); |
|
217 dst.OutputWithScope(dbuf); |
|
218 Log::Printf(_L("%S [src=%S dst=%S id=%d] (%d)"), &aText, &sbuf, &dbuf, a.iId, a.iHandler.iCount); |
|
219 } |
|
220 #endif |
|
221 |
|
222 // |
|
223 // CFragmentHandler::Cancel |
|
224 // ************************ |
|
225 // Cancel a specific assembly (delete it) |
|
226 // |
|
227 void CFragmentHandler::Cancel(CAssembly *aAssembly) |
|
228 { |
|
229 for (CAssembly **h = &iCache; *h != NULL; h = &(*h)->iNext) |
|
230 if (aAssembly == *h) |
|
231 { |
|
232 *h = aAssembly->iNext; |
|
233 delete aAssembly; |
|
234 break; |
|
235 } |
|
236 // Should panic, if a was not found from the list! -- msa |
|
237 } |
|
238 |
|
239 // |
|
240 // CFragmentHandler::CancelAll |
|
241 // *************************** |
|
242 // Cancel all packets waiting for assembly (delete all) |
|
243 // |
|
244 void CFragmentHandler::CancelAll() |
|
245 { |
|
246 CAssembly *a; |
|
247 while ((a = iCache) != NULL) |
|
248 { |
|
249 iCache = a->iNext; |
|
250 delete a; |
|
251 } |
|
252 } |
|
253 |
|
254 // |
|
255 // ApplyL |
|
256 // ****** |
|
257 // Called when an IP packet contain IPv6 fragment header. |
|
258 // (does not care whether outer IP header is v4 or v6, both work!) |
|
259 // |
|
260 TInt CFragmentHandler::ApplyL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo) |
|
261 { |
|
262 for (;;) // *NOT REAL LOOP, JUST A CONSTRUCT TO ENABLE USE OF 'break! |
|
263 { |
|
264 if (aInfo.iProtocol != STATIC_CAST(TInt, KProtocolInet6Fragment)) |
|
265 break; // Incorrect call, should only get IPv6 fragments here! |
|
266 TInet6Packet<TInet6HeaderFragment> fh(aPacket, aInfo.iOffset); |
|
267 if (fh.iHdr == NULL) |
|
268 break; // Drop! (packet too short) |
|
269 |
|
270 if (aInfo.iIcmp == 0) |
|
271 return Ip6ApplyL(aPacket, aInfo, *fh.iHdr); |
|
272 |
|
273 if (aInfo.iIcmp != KProtocolInet6Icmp) |
|
274 break; // Only IPv6 complaints should get this far, drop! |
|
275 |
|
276 const TInt offset = aInfo.iOffset - aInfo.iOffsetIp; // Relative offset within problem packet |
|
277 if (aInfo.iType == KInet6ICMP_ParameterProblem && // A parameter problem... |
|
278 offset <= (TInt)aInfo.iParameter && // after start of this header? |
|
279 STATIC_CAST(TUint, offset + sizeof(TInet6HeaderFragment)) > STATIC_CAST(TUint, aInfo.iParameter)) // and before end of this header? |
|
280 break; // Drop! (someone doesn't like my fragments!) |
|
281 // |
|
282 // Error is not Fragment Header specific, pass it on in the chain |
|
283 // Skip over header, pass error processing to the next header |
|
284 aInfo.iPrevNextHdr = (TUint16)aInfo.iOffset; // Fragment next header is at +0 |
|
285 aInfo.iProtocol = fh.iHdr->NextHeader(); |
|
286 aInfo.iOffset += sizeof(TInet6HeaderFragment); |
|
287 return KIp6Hook_DONE; |
|
288 // WAS NOT LOOP, BE SURE TO EXIT IT ALWAYS! |
|
289 } |
|
290 aPacket.Free(); |
|
291 return -1; |
|
292 } |
|
293 |
|
294 // |
|
295 // Fragment |
|
296 // ******** |
|
297 // Called when an outgoing IP packet needs to be fragmented |
|
298 // |
|
299 void CFragmentHandler::Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ) |
|
300 { |
|
301 // |
|
302 // Decide on which fragmenting from the IP header. |
|
303 // |
|
304 TIpHeader *ip = ((RMBufPacketPeek &)aPacket).GetIpHeader(); |
|
305 if (ip) |
|
306 if (ip->ip4.Version() == 4) |
|
307 Ip4Fragment(aPacket, aMtu, aFragQ, ip->ip4); |
|
308 else if (ip->ip4.Version() == 6) |
|
309 Ip6Fragment(aPacket, aMtu, aFragQ, ip->ip6); |
|
310 } |
|
311 |
|
312 |
|
313 // CAssembly |
|
314 // ********* |
|
315 // |
|
316 CAssembly::CAssembly(CFragmentHandler &aHandler, const TIp6Addr &aSrc, const TIp6Addr &aDst, TInt aId, TInt aVersion) |
|
317 : iHandler(aHandler), iTimeout(CFragmentLinkage::Timeout), iVersion((TUint8)aVersion), iId(aId), |
|
318 iSrcAddr(aSrc), iDstAddr(aDst), iLength(65536) |
|
319 { |
|
320 } |
|
321 |
|
322 // CAssembly::~CAssebly() |
|
323 // ********************** |
|
324 // |
|
325 CAssembly::~CAssembly() |
|
326 { |
|
327 iHandler.iTotal -= iTotal; |
|
328 --iHandler.iCount; |
|
329 |
|
330 LOG(LogPrintId(_L("CAssebly::~CAssebly():"), *this)); |
|
331 |
|
332 iTimeout.Cancel(); |
|
333 iQueue.Free(); |
|
334 iHead.Free(); |
|
335 } |
|
336 |
|
337 |
|
338 LOCAL_C TInt CompareStructures() |
|
339 { |
|
340 return sizeof(TIp6FragmentHeader) - sizeof(TInet6HeaderFragment); |
|
341 } |
|
342 // |
|
343 // CAssembly::CompletePacket |
|
344 // ************************* |
|
345 // Merge the iHead and first fragment into a complete packet |
|
346 // (including the IPv4/IPv6 header stuff!) |
|
347 // |
|
348 // Returns, |
|
349 // == KErrNone, if packet in iHead built |
|
350 // != KErrNone, if no packet available |
|
351 // |
|
352 // *NOTE* |
|
353 // this is also used when reassembly timeout hits, thus the |
|
354 // "complete" can also be an incomplete packet (including |
|
355 // only the first fragment). |
|
356 // |
|
357 TInt CAssembly::CompletePacket(TInt aRestoreFH) |
|
358 { |
|
359 if (iHead.IsEmpty()) |
|
360 return -1; |
|
361 |
|
362 RMBufRecvInfo *const info = iHead.Info(); |
|
363 |
|
364 RIp6Fragment first; |
|
365 iQueue.Remove(first); |
|
366 |
|
367 TInt length = first.Header()->iLength; |
|
368 ASSERT(first.Header()->iOffset == 0); |
|
369 |
|
370 if (aRestoreFH) |
|
371 { |
|
372 // ********************************************************* |
|
373 // TAHI tester wants the returned ICMP to include the FH on |
|
374 // time exceeded message. This is for that... |
|
375 // ********************************************************* |
|
376 const TInt adjust = CompareStructures(); |
|
377 // As the value of adjust is known by compile time, compiler should eliminate |
|
378 // unnecessary code below (although, it may give a warnings about constants |
|
379 // being compared--this is indication that code elimination should work!) |
|
380 if (adjust > 0) |
|
381 first.TrimStart(adjust); |
|
382 else if (adjust < 0) |
|
383 { |
|
384 TInt err = first.Prepend(-adjust); |
|
385 if (err!=KErrNone) |
|
386 { |
|
387 first.Free(); |
|
388 return -1; |
|
389 } |
|
390 } |
|
391 TInet6HeaderFragment fake_fh; |
|
392 *(TUint32 *)&fake_fh = iFH0; // Saved 1st word of original FH |
|
393 fake_fh.SetId(iId); // ..and this SetId cover all of FH |
|
394 // (there is no need to zero anything else) |
|
395 first.CopyIn(TPtrC8((TUint8 *)&fake_fh, sizeof(TInet6HeaderFragment)), 0); |
|
396 // .. ugh, KProtocolInet6Fragment is TInt, casting to TUint8* might |
|
397 // cause "endian problems", thus this copy to true TUint8 variable... -- msa |
|
398 const TUint8 proto = KProtocolInet6Fragment; |
|
399 iHead.CopyIn(TPtrC8((TUint8 *)&proto, 1), info->iPrevNextHdr); |
|
400 length += sizeof(TInet6HeaderFragment); |
|
401 iLength = length; |
|
402 } |
|
403 else |
|
404 first.TrimStart(sizeof(TIp6FragmentHeader)); |
|
405 if (length > iLength) |
|
406 { |
|
407 // |
|
408 // Just to deal with some overlapping fragment, which extends |
|
409 // beyond the last fragment... (iLength is initialized to |
|
410 // 65536 and is only less, when last fragment has arrived!) |
|
411 length = iLength; |
|
412 first.TrimEnd(length); |
|
413 } |
|
414 |
|
415 iHead.Append(first); |
|
416 // |
|
417 // Fix info and IP header |
|
418 // |
|
419 // |
|
420 // Only the iLength needs to be updated, all other fields in the info |
|
421 // must already have the correct values! |
|
422 // |
|
423 info->iLength = info->iOffset + length; |
|
424 |
|
425 TInet6Packet<TIpHeader> ip(iHead, info->iOffsetIp); |
|
426 if (ip.iHdr) |
|
427 { |
|
428 TInt ver = ip.iHdr->ip4.Version(); |
|
429 if (ver == 4) |
|
430 { |
|
431 if (ip.iLength >= ip.iHdr->ip4.HeaderLength() && |
|
432 info->iLength < 65536) |
|
433 { |
|
434 // ..just to be tidy, make sure M=0 (nobody really cares) -- msa |
|
435 ip.iHdr->ip4.SetFlags((TUint8)(ip.iHdr->ip4.Flags() & ~KInet4IP_MF)); |
|
436 ip.iHdr->ip4.SetTotalLength(info->iLength); |
|
437 return KErrNone; |
|
438 } |
|
439 } |
|
440 else if (ver == 6) |
|
441 { |
|
442 if (ip.iLength >= TInet6HeaderIP::MinHeaderLength() && |
|
443 STATIC_CAST(TUint, info->iLength) < STATIC_CAST(TUint, 65536 + sizeof(TInet6HeaderIP))) |
|
444 { |
|
445 ip.iHdr->ip6.SetPayloadLength(info->iLength - sizeof(TInet6HeaderIP)); |
|
446 return KErrNone; |
|
447 } |
|
448 } |
|
449 } |
|
450 return -1; |
|
451 } |
|
452 |
|
453 // |
|
454 // CAssembly::Add |
|
455 // ********************** |
|
456 // |
|
457 TInt CAssembly::Add(RMBufRecvPacket &aPacket, TInt aTrim, TInt aOffset, TInt aLength, TUint32 aFH0) |
|
458 { |
|
459 if (iIsDead == 0) |
|
460 { |
|
461 TInt ret = 0; // [ = 0, only to silence compiler] |
|
462 TRAPD(err, ret = AddFragmentL(aPacket, aTrim, aOffset, aLength, aFH0)); |
|
463 if (err == KErrNone) |
|
464 return ret; |
|
465 // |
|
466 // AddFragmentL left, change the Assembly into "dead" state |
|
467 // (and make sure all unnecessary resources are released) |
|
468 // |
|
469 iIsDead = 1; |
|
470 iHead.Free(); |
|
471 iQueue.Free(); |
|
472 iHandler.iTotal -= iTotal; |
|
473 iTotal = 0; |
|
474 } |
|
475 ASSERT(iTotal == 0); |
|
476 // |
|
477 // The hook return code = -1 (=> packet handled and released) |
|
478 // |
|
479 aPacket.Free(); |
|
480 return -1; |
|
481 } |
|
482 |
|
483 |
|
484 // CAssembly::AddFragmentL |
|
485 // *********************** |
|
486 // This should only be called from Add, which must trap the leaves and |
|
487 // |
|
488 // returns |
|
489 // standard inbound hook return codes |
|
490 // leaves |
|
491 // when assembly should be declared "dead" (seed "Add") |
|
492 // |
|
493 TInt CAssembly::AddFragmentL(RMBufRecvPacket &aPacket, TInt aTrim, TInt aOffset, TInt aLength, TUint32 aFH0) |
|
494 { |
|
495 RMBufRecvInfo *info = aPacket.Info(); |
|
496 if (aOffset == 0) |
|
497 { |
|
498 // |
|
499 // Processing the first fragment (offset == 0!) |
|
500 // |
|
501 |
|
502 if (!iHead.IsEmpty()) |
|
503 aTrim += info->iOffset; |
|
504 else |
|
505 { |
|
506 const TInt unfrag = info->iOffset; // = Unfragmentable length (bytes) |
|
507 iTotal += unfrag; |
|
508 iHandler.iTotal += unfrag; |
|
509 |
|
510 iHead.Assign(aPacket); |
|
511 iHead.SplitL(unfrag, aPacket); |
|
512 iHead.SetInfo(info); |
|
513 aPacket.SetInfo(NULL); |
|
514 // |
|
515 // Patch in the next header field |
|
516 // |
|
517 iHead.CopyIn(TPtrC8((TUint8 *)&info->iProtocol, 1), info->iPrevNextHdr); |
|
518 // |
|
519 // This fragment is assigned as the first fragment. Save the FH0 for it. |
|
520 // (if there are multiple "first fragments", the first received is used) |
|
521 // (Only needed for IPv6 and to generate TAHI-compatible ICMP Time Exceeded) |
|
522 // *NOTE* The full first word is saved because, the reserved bits must be |
|
523 // kept also, if someone is using them -- msa |
|
524 iFH0 = aFH0; |
|
525 } |
|
526 } |
|
527 else |
|
528 aTrim += info->iOffset; |
|
529 // |
|
530 // Convert the header in the packet (if present) into |
|
531 // internal fragment header |
|
532 // |
|
533 aTrim -= sizeof(TIp6FragmentHeader); |
|
534 if (aTrim > 0) |
|
535 // Cast needed to avoid TrimStart() looking for info block!!! |
|
536 ((RMBufChain &)aPacket).TrimStart(aTrim); |
|
537 else if (aTrim < 0) |
|
538 aPacket.PrependL(-aTrim); |
|
539 // Make sure the fragment header is properly aligned in the First() buffer. |
|
540 aPacket.Align(sizeof(TIp6FragmentHeader)); |
|
541 TIp6FragmentHeader *frag = ((RIp6Fragment &)aPacket).Header(); |
|
542 frag->iLength = aLength; |
|
543 frag->iOffset = aOffset; |
|
544 // |
|
545 // Add fragment to the queue. This assumes that the RMBufChain |
|
546 // is removed from the aPacket (IsEmpty() becomes True). |
|
547 // |
|
548 iQueue.Add((RIp6Fragment &)aPacket); |
|
549 ASSERT(aPacket.IsEmpty()); |
|
550 |
|
551 if (iQueue.First().FragmentLength() >= (TUint)iLength) |
|
552 { |
|
553 #ifdef _LOG |
|
554 TBuf<80> buf; |
|
555 buf.Format(_L("CAssembly::AddFragmentL(): [%d..%d (%d)] COMPLETED"), aOffset, aOffset+aLength-1, aLength); |
|
556 LogPrintId(buf, *this); |
|
557 #endif |
|
558 // Packet has been fully assembled, restore the |
|
559 // complete packet into aPacket |
|
560 // |
|
561 if (CompletePacket() == KErrNone) |
|
562 { |
|
563 aPacket.Assign(iHead); |
|
564 if (info != iHead.Info()) |
|
565 { |
|
566 // |
|
567 // Need to copy the Information from the iHead |
|
568 // (can't just do "*info = *iHead.Info()", because |
|
569 // that would copy the RMBuf base class parts too!! |
|
570 // |
|
571 // ...this looks a bit scary, does this always work? Should |
|
572 // look for a cleaner way to do this? -- msa |
|
573 const TInt off = sizeof(RMBufCell); |
|
574 const TInt len = sizeof(*info) - off; |
|
575 TPtr8((TUint8 *)info + off, len).Copy(TPtrC8((TUint8 *)iHead.Info() + off, len)); |
|
576 } |
|
577 else |
|
578 { |
|
579 // |
|
580 // This only happens when the only fragment is full packet! |
|
581 // |
|
582 iHead.SetInfo(NULL); |
|
583 aPacket.SetInfo(info); |
|
584 } |
|
585 iHandler.Cancel(this); // Deletes this! |
|
586 return KIp6Hook_DONE; |
|
587 } |
|
588 aPacket.Free(); // (mainly for info block!) |
|
589 iHandler.Cancel(this); // Deletes this! |
|
590 return -1; |
|
591 } |
|
592 else |
|
593 { |
|
594 aPacket.FreeInfo(); |
|
595 #ifdef _LOG |
|
596 TBuf<80> buf; |
|
597 buf.Format(_L("CAssembly::AddFragmentL(): [%d..%d (%d)]"), aOffset, aOffset+aLength-1, aLength); |
|
598 LogPrintId(buf, *this); |
|
599 #endif |
|
600 // Maintain total number of allocated bytes (roughly, this "overestimates" |
|
601 // if fragments overlap!). |
|
602 // |
|
603 iTotal += aLength; |
|
604 iHandler.iTotal += aLength; |
|
605 if (iHandler.iTotal > iHandler.iMaxTotal) |
|
606 { |
|
607 #ifdef _LOG |
|
608 // ...borrow the 'buf' from above LOG block! |
|
609 buf.Format(_L("CAssembly::AddFragmentL(): %d went over max (assembly total=%d)"), iHandler.iTotal, iTotal); |
|
610 LogPrintId(buf, *this); |
|
611 #endif |
|
612 User::Leave(KErrOverflow); // make assebly "dead" by leaving |
|
613 } |
|
614 return -1; |
|
615 } |
|
616 } |
|
617 |
|
618 |
|
619 void CAssembly::Timeout() |
|
620 { |
|
621 // |
|
622 // Reconstruct start of IPv6 packet from the unfrag part |
|
623 // |
|
624 if (iVersion == 4) |
|
625 { |
|
626 if (CompletePacket() == KErrNone) |
|
627 iHandler.Network()->Icmp4Send(iHead, KInet4ICMP_TimeExceeded, 1, 0); |
|
628 } |
|
629 else |
|
630 { |
|
631 if (CompletePacket(1) == KErrNone) // Request restore of FH (for TAHI) |
|
632 iHandler.Network()->Icmp6Send(iHead, KInet6ICMP_TimeExceeded, 1, 0); |
|
633 } |
|
634 LOG(LogPrintId(_L("CAssembly::Timeout()"), *this)); |
|
635 iHandler.Cancel(this); // <-- Deletes this!!! |
|
636 } |
|
637 |
|
638 CAssembly *CFragmentHandler::LookupL(const RMBufRecvInfo &aInfo, TUint32 aId, TUint aVersion) |
|
639 { |
|
640 CAssembly *a = NULL; |
|
641 CAssembly *d = NULL; |
|
642 const TIp6Addr &src = TInetAddr::Cast(aInfo.iSrcAddr).Ip6Address(); |
|
643 const TIp6Addr &dst = TInetAddr::Cast(aInfo.iDstAddr).Ip6Address(); |
|
644 for (a = iCache; ; a = a->iNext) |
|
645 { |
|
646 if (a == NULL) |
|
647 { |
|
648 // If the quota for incomplete assemblies is all used |
|
649 if (iCount >= iMaxCount) |
|
650 { |
|
651 LOG(Log::Printf(_L("CFragmentHandler::LookupL() MaxCount %d reached"), iCount)); |
|
652 if (d == NULL) |
|
653 User::Leave(KErrOverflow); // ... should assign some specific reason code? |
|
654 Cancel(d); |
|
655 LOG(Log::Printf(_L("CFragmentHandler::LookupL() deleted dead assembly"))); |
|
656 } |
|
657 a = new (ELeave) CAssembly(*this, src, dst, aId, aVersion); |
|
658 a->iNext = iCache; |
|
659 iCache = a; |
|
660 a->iTimeout.Set(iTimeoutManager, 60); // 60 seconds or bust! |
|
661 iCount++; |
|
662 LOG(LogPrintId(_L("CFragmentHandler::LookupL(): NEW"), *a)); |
|
663 break; |
|
664 } |
|
665 else if (a->iId == aId && a->iSrcAddr.IsEqual(src) && a->iDstAddr.IsEqual(dst)) |
|
666 break; |
|
667 else if (a->iIsDead) |
|
668 d = a; // remember "oldest dead" assembly |
|
669 } |
|
670 return a; |
|
671 } |
|
672 |
|
673 // |
|
674 // CFragmentHeaderHook::NewL |
|
675 // |
|
676 CFragmentHeaderHook *CFragmentHeaderHook::NewL(MNetworkService *aNetwork) |
|
677 { |
|
678 return new (ELeave) CFragmentHandler(aNetwork); |
|
679 } |
|
680 |
|
681 void CFragmentHandler::ConstructL() |
|
682 { |
|
683 iTimeoutManager = TimeoutFactory::NewL(); |
|
684 iNetwork->BindL((CProtocolBase *)this, BindHookFor(KProtocolInet6Fragment)); |
|
685 |
|
686 |
|
687 // |
|
688 // Setup DOS protections |
|
689 // - maximum number of incomplete assemblies (iMaxCount) |
|
690 // - maximum amount of RMBUF space (iMaxTotal) |
|
691 // |
|
692 LOG(_LIT(KFormat, "\t[%S] %S = %d")); |
|
693 TInt value; |
|
694 if (iNetwork->Interfacer()->FindVar(TCPIP_INI_IP, TCPIP_INI_FRAG_COUNT, value)) |
|
695 iMaxCount = value; |
|
696 else |
|
697 iMaxCount = KTcpipIni_FragCount; |
|
698 LOG(Log::Printf(KFormat, &TCPIP_INI_IP, &TCPIP_INI_FRAG_COUNT, iMaxCount)); |
|
699 |
|
700 if (iNetwork->Interfacer()->FindVar(TCPIP_INI_IP, TCPIP_INI_FRAG_TOTAL, value)) |
|
701 iMaxTotal = value; |
|
702 else |
|
703 iMaxTotal = KTcpipIni_FragTotal; |
|
704 LOG(Log::Printf(KFormat, &TCPIP_INI_IP, &TCPIP_INI_FRAG_TOTAL, iMaxTotal)); |
|
705 } |
|
706 |
|
707 // |
|
708 // CFragmentHandler::~CFragmentHandler() |
|
709 // ************************************* |
|
710 // |
|
711 CFragmentHandler::~CFragmentHandler() |
|
712 { |
|
713 CancelAll(); |
|
714 delete iTimeoutManager; |
|
715 } |
|
716 |
|
717 |
|
718 // ****************** |
|
719 // Reassembly section |
|
720 // ****************** |
|
721 // This a normal extension header receiver hook which is called when the |
|
722 // processing of headers reaches the Fragmentation Header. |
|
723 // |
|
724 TInt CFragmentHandler::Ip6ApplyL(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, const TInet6HeaderFragment &aHdr) |
|
725 { |
|
726 // |
|
727 // Extract fragment information off from the RMBufs |
|
728 // (after this, the fragment header can be disposed as needed) |
|
729 // |
|
730 aInfo.iProtocol = aHdr.NextHeader(); |
|
731 const TUint32 fh0 = *(TUint32 *)&aHdr; // needed for ICMP Time Exceeded. |
|
732 const TInt length = aInfo.iLength - aInfo.iOffset - sizeof(TInet6HeaderFragment); |
|
733 const TInt offset = aHdr.FragmentOffset(); |
|
734 const TInt last = (aHdr.MFlag() == 0); |
|
735 |
|
736 CAssembly *a = LookupL(aInfo, aHdr.Id(), aInfo.iVersion); |
|
737 |
|
738 if (length <= 0 || ((length & 0x7) && !last)) |
|
739 { |
|
740 // Zero length fragment, or the length of non-last fragment payload is not multiple of 8. |
|
741 // (always reply with ICMPv6, because IPv6 fragment header was used. If the packet was |
|
742 // actually an IPv4 packet, just adjust use different the parameter offset). |
|
743 // |
|
744 LOG(Log::Printf(_L("CFragmentHandler::IP6ApplyL(...): Payload Length=%d"), length)); |
|
745 Network()->Icmp6Send(aPacket, KInet6ICMP_ParameterProblem, 0, |
|
746 aInfo.iVersion == 4 ? TInet6HeaderIP4::O_TotalLength : TInet6HeaderIP::O_PayloadLength); |
|
747 Cancel(a); |
|
748 // Note: Icmp6Send takes the packet -- no packet Free required! |
|
749 return -1; |
|
750 } |
|
751 if (offset + length > 65535) |
|
752 { |
|
753 // |
|
754 // This fragment would result too long payload for the final IPv6 packet |
|
755 // |
|
756 // *** above test is not correct! It should also take into account the |
|
757 // length of the unfragmentable part (if that includes extension |
|
758 // headers in addition to IPv6 header). However, this information |
|
759 // is not available until the first fragment is received. Thus, it |
|
760 // is possible that illegal fragments get entered into the fragment |
|
761 // queue! Needs some checking!! -- msa |
|
762 // |
|
763 LOG(Log::Printf(_L("CFragmentHandler::IP6ApplyL(...): offset(%d) + length(%d) > 65535"), offset, length)); |
|
764 Network()->Icmp6Send(aPacket, KInet6ICMP_ParameterProblem, 0, |
|
765 aInfo.iOffset + TInet6HeaderFragment::O_FragmentOffset); |
|
766 Cancel(a); |
|
767 // Note: Icmp6Send takes the packet -- no packet Free required! |
|
768 return -1; |
|
769 } |
|
770 if (last) |
|
771 { |
|
772 // |
|
773 // If this is the last fragment, the total length is now known |
|
774 // |
|
775 a->iLength = offset + length; |
|
776 } |
|
777 |
|
778 return a->Add(aPacket, sizeof(TInet6HeaderFragment), offset, length, fh0); |
|
779 } |
|
780 |
|
781 // ******************* |
|
782 // Fragmenting section |
|
783 // ******************* |
|
784 // When entered, aPacket contains full IPv6 packet, and |
|
785 // it has been already tested that this does not fit the |
|
786 // Path MTU! |
|
787 // If called with a packet that fits the MTU, this generates a |
|
788 // single fragment (just inserts the IPv6 fragment header into |
|
789 // the packet with offset==0 and M=1) |
|
790 // |
|
791 void CFragmentHandler::Ip6Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ, TInet6HeaderIP &aHdr) |
|
792 { |
|
793 // |
|
794 // Check extension headers that need to be included into |
|
795 // unfragmentable part |
|
796 // It is somewhat fuzzy what should be included in addition |
|
797 // of Hop-by-hop and Routing header. Currently including |
|
798 // destination options, if they are before the routing header |
|
799 // -- msa |
|
800 |
|
801 // unfrag_next |
|
802 // point always to the last next header field of the |
|
803 // unfragmentable part. |
|
804 TUint8 *unfrag_next = ((TUint8 *)&aHdr) + TInet6HeaderIP::O_NextHeader; |
|
805 // |
|
806 // unfrag_size |
|
807 // size of the unfragmentable part. |
|
808 TInt unfrag_size = sizeof(TInet6HeaderIP); |
|
809 |
|
810 TInt offset = sizeof(TInet6HeaderIP); |
|
811 for (TInt header = (TInt)*unfrag_next;;) |
|
812 { |
|
813 TInet6Packet<TInet6HeaderExtension> ext(aPacket, offset); |
|
814 if (ext.iHdr == NULL) |
|
815 // If the next header (8 octets) cannot be mapped from |
|
816 // the packet it cannot be a valid extension header anyways. |
|
817 // This search can be terminated with what we have now. |
|
818 break; |
|
819 offset += ext.iHdr->HeaderLength(); |
|
820 |
|
821 if (header == STATIC_CAST(TInt, KProtocolInet6HopOptions)) |
|
822 { |
|
823 // The mapped header is Hop-by-hop options. This is |
|
824 // included into unfragmentable part, but must look |
|
825 // forward if there is a routing header. Just remember |
|
826 // this part |
|
827 unfrag_size = offset; |
|
828 unfrag_next = (TUint8 *)ext.iHdr; // point to Next Hdr field. |
|
829 } |
|
830 else if (header != STATIC_CAST(TInt, KProtocolInet6DestinationOptions)) |
|
831 { |
|
832 // Terminate search (we got either unknown extension |
|
833 // or routing header). If the latter, then include it |
|
834 // into unfragmentable part. |
|
835 if (header == STATIC_CAST(TInt, KProtocolInet6RoutingHeader)) |
|
836 { |
|
837 unfrag_size = offset; |
|
838 unfrag_next = (TUint8 *)ext.iHdr; // point to Next Hdr field. |
|
839 } |
|
840 break; |
|
841 } |
|
842 // Look for more |
|
843 header = ext.iHdr->NextHeader(); |
|
844 } |
|
845 // |
|
846 // The NextHeader of the last header (either IPv6 or an extension |
|
847 // need to be changed into KProtocolInet6Fragment. And, the old |
|
848 // value is placed into next header of every generated fragment |
|
849 // header. |
|
850 TInt next_header = (TInt)*unfrag_next; |
|
851 *unfrag_next = KProtocolInet6Fragment; |
|
852 // |
|
853 // Remove the fragmentable part off the packet, leaving |
|
854 // only the unfragmentable part into the original packet |
|
855 // (and info block!) |
|
856 // |
|
857 RMBufChain fragmentable; // Fragmentable part |
|
858 RMBufSendPacket fragment; |
|
859 RMBufChain tailpart; |
|
860 RMBufPktInfo *info; |
|
861 TInet6Packet<TInet6HeaderFragment> fh; |
|
862 TInt remainder, chunk; |
|
863 |
|
864 TInt err = aPacket.Split(unfrag_size, fragmentable); |
|
865 if (err == KErrNone) |
|
866 err = aPacket.Append(sizeof(TInet6HeaderFragment)); |
|
867 if (err != KErrNone) |
|
868 goto drop_all; // -- probably out of memory (RMBUF's) |
|
869 |
|
870 remainder = aPacket.Info()->iLength - unfrag_size; |
|
871 // |
|
872 // Prepare access for the fragment header |
|
873 // |
|
874 fh.Set(aPacket, unfrag_size, sizeof(TInet6HeaderFragment)); |
|
875 unfrag_size += sizeof(TInet6HeaderFragment); |
|
876 if (err != KErrNone || // Memory allocation error, or... |
|
877 fh.iHdr == NULL || // Cannot access fragment header, or.. |
|
878 aMtu < (unfrag_size+8)) |
|
879 goto drop_all; // Mission impossible! Path MTU is less than |
|
880 // required unfragmentable size + minimal payload! |
|
881 fh.iHdr->ZeroAll(); |
|
882 fh.iHdr->SetNextHeader(next_header); |
|
883 fh.iHdr->SetId(++iFragmentId); |
|
884 fh.iHdr->SetMFlag(1); |
|
885 // |
|
886 // Fragment payload size is multiple of 8 |
|
887 // |
|
888 chunk = (aMtu - unfrag_size) & ~0x7; |
|
889 offset = 0; |
|
890 // |
|
891 // Patch in fragment payload size into IPv6 header, this will |
|
892 // be the same for all but last fragment, so it can be set outside |
|
893 // the loop... |
|
894 // (making a big assumption that the aHdr pointer is still valid!! |
|
895 // the current operations done to the aPacket are safe, but beware |
|
896 // when changing this code...) -- msa |
|
897 // |
|
898 aHdr.SetPayloadLength(unfrag_size + chunk - sizeof(TInet6HeaderIP)); |
|
899 while (remainder > chunk) |
|
900 { |
|
901 // |
|
902 // Setup unfragmentable part |
|
903 // |
|
904 TRAP(err, |
|
905 aPacket.CopyL(fragment); |
|
906 aPacket.CopyInfoL(fragment); |
|
907 ); |
|
908 if (err != KErrNone) |
|
909 goto drop_all; |
|
910 info = fragment.Info(); |
|
911 info->iLength = chunk + unfrag_size; |
|
912 // |
|
913 // Extract next fragment payload |
|
914 // |
|
915 err = fragmentable.Split(chunk, tailpart); |
|
916 if (err != KErrNone) |
|
917 goto drop_all; |
|
918 fragment.Append(fragmentable); |
|
919 fragment.Pack(); |
|
920 aFragQ.Append(fragment); |
|
921 fragmentable.Assign(tailpart); |
|
922 offset += chunk; |
|
923 remainder -= chunk; |
|
924 fh.iHdr->SetFragmentOffset(offset); |
|
925 } |
|
926 // |
|
927 // Make aPacket as the last fragment |
|
928 // |
|
929 fh.iHdr->SetMFlag(0); |
|
930 aHdr.SetPayloadLength(unfrag_size + remainder - sizeof(TInet6HeaderIP)); |
|
931 aPacket.Append(fragmentable); |
|
932 info = aPacket.Info(); |
|
933 info->iLength = unfrag_size + remainder; |
|
934 aPacket.Pack(); |
|
935 aFragQ.Append(aPacket); |
|
936 return; |
|
937 |
|
938 drop_all: |
|
939 aFragQ.Free(); |
|
940 fragmentable.Free(); |
|
941 fragment.Free(); |
|
942 tailpart.Free(); |
|
943 aPacket.Free(); |
|
944 LOG(Log::Printf(_L("CFragmentHandler::Ip6Fragment(...) FAILED"))); |
|
945 } |
|
946 |
|
947 |
|
948 // ********************** |
|
949 // IPv4 Fragment handling |
|
950 // ********************** |
|
951 |
|
952 // ****************** |
|
953 // Reassembly section |
|
954 // ****************** |
|
955 // This a called when an IPv4 fragment is received. On entry |
|
956 // aHead.ip4 already contains a full copy of the IPv4 header |
|
957 // and aHead.iOffset == ip4.HeaderLength() |
|
958 // |
|
959 // *NOTE* |
|
960 // On return, the IPv4 header checksum is not recomputed |
|
961 // (should it? maybe for possible ICMP error message? -- msa) |
|
962 // |
|
963 TInt CFragmentHandler::Ip4ApplyL(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, const TInet6HeaderIP4 &aHdr) |
|
964 { |
|
965 // |
|
966 // Extract fragment information off from the RMBufs |
|
967 // (after this, the fragment header can be disposed as needed) |
|
968 // |
|
969 aInfo.iProtocol = aHdr.Protocol(); // Put the protocol into info |
|
970 const TInt length = aInfo.iLength - aInfo.iOffset; // Actual payload length |
|
971 const TInt offset = (aHdr.FragmentOffset()) << 3; |
|
972 const TInt last = (aHdr.MF() == 0); |
|
973 // |
|
974 // Locate or create an assemble matching this fragment |
|
975 // |
|
976 CAssembly *a = LookupL(aInfo, aHdr.Identification() | (aInfo.iProtocol << 16), 4); |
|
977 |
|
978 if (length <= 0 || ((length & 0x7) && !last)) |
|
979 { |
|
980 // Zero length fragment or, |
|
981 // the length of non-last fragment payload is not multiple of 8. |
|
982 // |
|
983 Network()->Icmp4Send(aPacket, KInet4ICMP_ParameterProblem, 0, |
|
984 TInet6HeaderIP4::O_TotalLength); |
|
985 Cancel(a); |
|
986 // Note: Icmp4Send takes the packet -- no packet Free required! |
|
987 return -1; |
|
988 } |
|
989 if (offset + length > 65535 - TInet6HeaderIP4::MinHeaderLength()) |
|
990 { |
|
991 // |
|
992 // This fragment would result too long payload for the final IPv4 packet |
|
993 // (NOTE: above is not full proof, if the first fragment has options! |
|
994 // The intent is just to drop obviously wrong fragments before doing |
|
995 // anything more with them... -- msa) |
|
996 // |
|
997 Network()->Icmp4Send(aPacket, KInet4ICMP_ParameterProblem, 0, |
|
998 TInet6HeaderIP4::O_FragmentOffset); |
|
999 Cancel(a); |
|
1000 // Note: Icmp4Send takes the packet -- no packet Free required! |
|
1001 return -1; |
|
1002 } |
|
1003 |
|
1004 if (last) |
|
1005 a->iLength = offset + length; // Last fragment, real payload length is now known |
|
1006 |
|
1007 return a->Add(aPacket, 0, offset, length); |
|
1008 } |
|
1009 |
|
1010 // |
|
1011 // CrunchOptions |
|
1012 // ------------- |
|
1013 // Only options marked as "copy" are copied into every fragment header. |
|
1014 // |
|
1015 // This function takes options from the original packet and removes |
|
1016 // all except the copiable options (process in place). |
|
1017 // |
|
1018 // Returns the length of the "crunched" IPv4 header |
|
1019 // |
|
1020 static TInt CrunchOptions(TInet6HeaderIP4 &aHdr) |
|
1021 { |
|
1022 TInt length = TInet6HeaderIP4::MinHeaderLength(); |
|
1023 TUint8 *opt = ((TUint8 *)&aHdr) + length; |
|
1024 TUint8 *dst = opt; |
|
1025 TInt count = aHdr.HeaderLength() - length; |
|
1026 while (count > 0) |
|
1027 { |
|
1028 if (*opt < 2) |
|
1029 { |
|
1030 // Q: Is it legal to have "Copy" on PAD? If so, this |
|
1031 // needs some fixing yet! -- msa |
|
1032 // |
|
1033 ++opt; // END/PAD bytes, skip |
|
1034 count -= 1; |
|
1035 } |
|
1036 else |
|
1037 { |
|
1038 int optlen = opt[1]; |
|
1039 if (optlen > count || optlen < 2) |
|
1040 // An illegal option length, just bail out. |
|
1041 // (The packet is broken anyway) |
|
1042 break; |
|
1043 count -= optlen; |
|
1044 if (*opt & 0x80) |
|
1045 { |
|
1046 // |
|
1047 // Copy the option |
|
1048 // |
|
1049 length += optlen; |
|
1050 while (--optlen >= 0) |
|
1051 *dst++ = *opt++; |
|
1052 } |
|
1053 else |
|
1054 // |
|
1055 // Crunch this |
|
1056 // |
|
1057 opt += optlen; |
|
1058 } |
|
1059 } |
|
1060 // |
|
1061 // The IPv4 header length is multiple of 4, and thus options length |
|
1062 // must be padded, if not already aligned.. (Pad with END marker) |
|
1063 // |
|
1064 while (length & 0x3) |
|
1065 { |
|
1066 *dst++ = 0; // EMD |
|
1067 length++; |
|
1068 } |
|
1069 aHdr.SetHeaderLength(length); |
|
1070 return length; |
|
1071 } |
|
1072 |
|
1073 // ******************* |
|
1074 // Fragmenting section |
|
1075 // ******************* |
|
1076 // When entered, aPacket contains full IPv4 packet, and |
|
1077 // it has been already tested that this does not fit the |
|
1078 // Path MTU! |
|
1079 void CFragmentHandler::Ip4Fragment(RMBufPacketBase &aPacket, TInt aMtu, RMBufPktQ &aFragQ, TInet6HeaderIP4 &aHdr) |
|
1080 { |
|
1081 RMBufPacketBase fragmentable; // Fragmentable part |
|
1082 RMBufChain tailpart; // Just work space |
|
1083 TInt hlen, chunk, remainder, offset; |
|
1084 TInet6HeaderIP4 ip4; |
|
1085 TPtr8 ip4ptr((TUint8 *)&ip4, sizeof(ip4), sizeof(ip4)); |
|
1086 TInt err = KErrNone; |
|
1087 |
|
1088 offset = aHdr.FragmentOffset() << 3; |
|
1089 hlen = aHdr.HeaderLength(); |
|
1090 |
|
1091 chunk = (aMtu - hlen) & ~7; |
|
1092 if (chunk <= 0 || aHdr.DF()) |
|
1093 goto drop_all; // Unreasonable Mtu... or fragmenting not allowed |
|
1094 // |
|
1095 // Split off the first fragment... |
|
1096 // |
|
1097 err = aPacket.Split(hlen + chunk, fragmentable); |
|
1098 if (err != KErrNone) |
|
1099 goto drop_all; |
|
1100 aHdr.SetTotalLength(hlen + chunk); |
|
1101 offset += chunk; |
|
1102 remainder = aPacket.Info()->iLength - hlen - chunk; |
|
1103 aPacket.Info()->iLength = hlen + chunk; |
|
1104 // |
|
1105 // Setup a fragment header including the options that |
|
1106 // need to be copied for all the remaining fragments |
|
1107 // |
|
1108 ip4ptr = TPtrC8((TUint8 *)&aHdr, hlen); |
|
1109 hlen = CrunchOptions(ip4); |
|
1110 ip4ptr.SetLength(hlen); |
|
1111 // |
|
1112 // Generate "middle fragments". All of these will |
|
1113 // have the more (MF) as 1. |
|
1114 // |
|
1115 chunk = (aMtu - hlen) & ~7; |
|
1116 ip4.SetTotalLength(hlen + chunk); |
|
1117 ip4.SetFlags(KInet4IP_MF); |
|
1118 while (remainder > chunk) |
|
1119 { |
|
1120 // |
|
1121 // Extract next fragment payload |
|
1122 // |
|
1123 err = fragmentable.Split(chunk, tailpart); |
|
1124 if (err == KErrNone) |
|
1125 err = fragmentable.Prepend(hlen); |
|
1126 if (err != KErrNone) |
|
1127 goto drop_all; |
|
1128 ip4.SetFragmentOffset((TUint16)(offset >> 3)); |
|
1129 ip4.SetChecksum(0); |
|
1130 ip4.SetChecksum(TChecksum::ComplementedFold(TChecksum::Calculate((TUint16*)&ip4, hlen))); |
|
1131 fragmentable.CopyIn(ip4ptr); |
|
1132 TRAP(err, aPacket.CopyInfoL(fragmentable)); |
|
1133 if (err != KErrNone) |
|
1134 goto drop_all; |
|
1135 fragmentable.Info()->iLength = chunk + hlen; |
|
1136 fragmentable.Pack(); |
|
1137 aFragQ.Append(fragmentable); |
|
1138 fragmentable.Assign(tailpart); |
|
1139 offset += chunk; |
|
1140 remainder -= chunk; |
|
1141 } |
|
1142 // |
|
1143 // Generate the last fragment |
|
1144 // |
|
1145 err = fragmentable.Prepend(hlen); |
|
1146 if (err != KErrNone) |
|
1147 goto drop_all; |
|
1148 ip4.SetFragmentOffset((TUint16)(offset >> 3)); |
|
1149 ip4.SetTotalLength(hlen + remainder); |
|
1150 ip4.SetFlags((TUint8)(aHdr.Flags())); // need to copy More flag from the original packet! |
|
1151 ip4.SetChecksum(0); |
|
1152 ip4.SetChecksum(TChecksum::ComplementedFold(TChecksum::Calculate((TUint16*)&ip4, hlen))); |
|
1153 fragmentable.CopyIn(ip4ptr); |
|
1154 TRAP(err, aPacket.CopyInfoL(fragmentable)); |
|
1155 if (err != KErrNone) |
|
1156 goto drop_all; |
|
1157 fragmentable.Info()->iLength = hlen + remainder; |
|
1158 fragmentable.Pack(); |
|
1159 aFragQ.Append(fragmentable); |
|
1160 |
|
1161 // |
|
1162 // Complete the header of the first fragment |
|
1163 // |
|
1164 aHdr.SetFlags(KInet4IP_MF); |
|
1165 |
|
1166 aHdr.SetChecksum(0); |
|
1167 aHdr.SetChecksum(TChecksum::ComplementedFold(TChecksum::Calculate((TUint16*)&aHdr, aHdr.HeaderLength()))); |
|
1168 aPacket.Pack(); |
|
1169 aFragQ.Prepend(aPacket); |
|
1170 // |
|
1171 // All fragments successfully built. |
|
1172 // |
|
1173 return; |
|
1174 // |
|
1175 // Cleanup everything |
|
1176 // |
|
1177 drop_all: |
|
1178 aFragQ.Free(); |
|
1179 fragmentable.Free(); |
|
1180 tailpart.Free(); |
|
1181 aPacket.Free(); |
|
1182 LOG(Log::Printf(_L("CFragmentHandler::Ip4Fragment(...) FAILED"))); |
|
1183 } |