|
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 // in_pkt.cpp - packet handling routines |
|
15 // Mainly the RMBufPacketPeek implementation |
|
16 // + extension header locations |
|
17 // + method for adding a destination option |
|
18 // |
|
19 |
|
20 #include "in_pkt.h" |
|
21 #include "ext_hdr.h" |
|
22 |
|
23 #define OPTIMIZE_PADDING |
|
24 |
|
25 |
|
26 // RMBufPacketPeek |
|
27 // *************** |
|
28 |
|
29 EXPORT_C TUint8 *TInet6PacketBase::Access(RMBufChain &aPacket, TInt aOffset, TInt aSize, TInt aMin) |
|
30 /** |
|
31 * Accesses a contiguous block of memory within RMBufChain starting |
|
32 * at specified offset. |
|
33 * |
|
34 * Attempts to map aSize amount of contiguous RMBuf space at |
|
35 * the specified offset, by rearranging the RMBufs, |
|
36 * if necessary. |
|
37 * |
|
38 * If aSize amount is not available, the returned pointer maps |
|
39 * the maximum available space (< aSize). |
|
40 * |
|
41 * @param aSize |
|
42 * The size of the requested contiguous mapping (in octets). |
|
43 * The value cannot be larger than KMBufSmallSize. If aSize <= 1, |
|
44 * the method will return availabe contiguous mapping at the |
|
45 * indicated offset without rearranging the RMBuf chain. Values |
|
46 * larger than 1 may require rearrangement to get the requested |
|
47 * area into single RMBuf. |
|
48 * @param aOffset |
|
49 * The offset to the beginning of the area. The value MUST |
|
50 * be in range: 0 <= aOffset <= Length(). |
|
51 * @param aMin |
|
52 * The minimum accepted iLength. If iLength is less that aMin, |
|
53 * then returns NULL. |
|
54 * @return |
|
55 * A pointer to the data starting at indicated |
|
56 * offset. The iLength is set to the |
|
57 * maximum available contiguous data starting from the |
|
58 * requested offset. iLength is always <= KMBufSmallSize. |
|
59 * @note |
|
60 * The length can be either less or |
|
61 * larger than aSize. The caller must verify the returned |
|
62 * length to detect whether Access succeeded. |
|
63 */ |
|
64 { |
|
65 |
|
66 // |
|
67 // Can only map at most KMBufSmallSize bytes |
|
68 // (truncate aSize to avoid Panics from the Align) |
|
69 // |
|
70 if (aSize > KMBufSmallSize) |
|
71 aSize = KMBufSmallSize; |
|
72 |
|
73 for (int already_tried = 0; ;already_tried = 1) |
|
74 { |
|
75 RMBuf *p; |
|
76 TInt offset, len; |
|
77 iLength = 0; |
|
78 |
|
79 if (aPacket.IsEmpty() || !aPacket.Goto(aOffset, p, offset, len)) |
|
80 return NULL; |
|
81 else if ((aSize <= len && (offset & iAlign) == 0) || already_tried) |
|
82 { |
|
83 if (len < aMin) |
|
84 return NULL; |
|
85 iLength = len; |
|
86 return p->Buffer() + offset; |
|
87 } |
|
88 // The requested alignment value is not |
|
89 // satisfied! |
|
90 // |
|
91 // Brute force buffer mangling here: just |
|
92 // Split the chain at requested point, apply |
|
93 // align to the tail part, and then join the |
|
94 // pieces together (assuming this join operation |
|
95 // does not undo the align...) |
|
96 // (If this is going to occur frequently |
|
97 // some optimizations is in order... -- msa) |
|
98 // |
|
99 // |
|
100 // *NOTE* |
|
101 // If aOffset == 0, SplitL works weird (IMHO): it does |
|
102 // nothing (when it logically should move all to the |
|
103 // tail part!). Thus, zero offset needs to be handled |
|
104 // specially (sort of optimizing, so it's not too bad) |
|
105 // -- msa |
|
106 // |
|
107 if (aOffset > 0) |
|
108 { |
|
109 RMBufChain tail; |
|
110 TInt err = aPacket.Split(aOffset, tail); |
|
111 if (err == KErrNone && !tail.IsEmpty()) |
|
112 { |
|
113 tail.Align(aSize); |
|
114 aPacket.Append(tail); |
|
115 } |
|
116 } |
|
117 else |
|
118 aPacket.Align(aSize); |
|
119 // And now just retry the Goto and use that |
|
120 // result as is (either we succeeded or not) |
|
121 } |
|
122 } |
|
123 |
|
124 EXPORT_C TPtr8 RMBufPacketPeek::Access(TInt aSize, TUint aOffset) |
|
125 /** |
|
126 * Accesses a contiguous block of memory within RMBufChain starting |
|
127 * at specified offset. |
|
128 * |
|
129 * Attempts to map aSize amount of contiguous RMBuf space at |
|
130 * the specified offset into a TPtr8, by rearranging the RMBufs, |
|
131 * if necessary. |
|
132 * |
|
133 * If aSize amount is not available, the returned pointer maps |
|
134 * the maximum available space (< aSize). |
|
135 * |
|
136 * @param aSize |
|
137 * The size of the requested contiguous mapping (in octets). |
|
138 * The value cannot be larger than KMBufSmallSize. If aSize <= 1, |
|
139 * the method will return availabe contiguous mapping at the |
|
140 * indicated offset without rearranging the RMBuf chain. Values |
|
141 * larger than 1 may require rearrangement to get the requested |
|
142 * area into single RMBuf. |
|
143 * @param aOffset |
|
144 * The offset to the beginning of the area. The value MUST |
|
145 * be in range: 0 <= aOffset <= Length(). |
|
146 * @return |
|
147 * A pointer descriptor for the data starting at indicated |
|
148 * offset. The length of the descriptor is set to the |
|
149 * maximum available contiguous data starting from the |
|
150 * requested offset. Length is always <= KMBufSmallSize. |
|
151 * @note |
|
152 * The length can be either less or |
|
153 * larger than aSize. The caller must verify the returned |
|
154 * length to detect whether Access succeeded. |
|
155 */ |
|
156 { |
|
157 TInet6PacketBase map(TInet6PacketBase::EAlign1); |
|
158 TUint8 *const ptr = map.Access(*this, aOffset, aSize, 1); |
|
159 return TPtr8(ptr, map.iLength, map.iLength); |
|
160 } |
|
161 |
|
162 |
|
163 EXPORT_C TIpHeader *RMBufPacketPeek::GetIpHeader() |
|
164 /** |
|
165 * Access IP header (v4 or v6) from the packet at offset 0. |
|
166 * |
|
167 * @return IP header (or NULL). |
|
168 * A non-NULL return implies that full header, either IPv4 or |
|
169 * IPv6 is accessible through the pointer. This method is |
|
170 * intended to save code space, and not necessarily for |
|
171 * speed... |
|
172 */ |
|
173 { |
|
174 TInet6Packet<TIpHeader> ip(*this); |
|
175 if (ip.iHdr) |
|
176 { |
|
177 if (ip.iHdr->ip4.Version() == 4) |
|
178 { |
|
179 // Need to do sanity check on header length (because it can be garbage) |
|
180 const TInt hlen = ip.iHdr->ip4.HeaderLength(); |
|
181 if (hlen >= TInet6HeaderIP4::MinHeaderLength() && ip.iLength >= hlen) |
|
182 return (TIpHeader *)ip.iHdr; |
|
183 } |
|
184 else if (ip.iHdr->ip4.Version() == 6) |
|
185 { |
|
186 if (ip.iLength >= TInet6HeaderIP::MinHeaderLength()) |
|
187 return (TIpHeader *)ip.iHdr; |
|
188 } |
|
189 } |
|
190 return NULL; |
|
191 } |
|
192 |
|
193 // TPacketPocker |
|
194 // ************* |
|
195 |
|
196 EXPORT_C TPacketPoker::TPacketPoker(RMBufChain &aChain) : iCurrent(aChain.First()), iOffset(0), iTail(0) |
|
197 /** |
|
198 * Constructor. |
|
199 * |
|
200 * @param aChain The RMBuf chain to be poked. |
|
201 */ |
|
202 { |
|
203 // Find initial value for iTail. |
|
204 while (iCurrent) |
|
205 { |
|
206 iTail = iCurrent->Length(); |
|
207 if (iTail > 0) |
|
208 break; |
|
209 // Ugh. There have been RMBuf's with ZERO length! |
|
210 iCurrent = iCurrent->Next(); |
|
211 } |
|
212 } |
|
213 |
|
214 EXPORT_C void TPacketPoker::OverL(TInt aSize) |
|
215 /** |
|
216 * Skip over bytes. |
|
217 * |
|
218 * @param aSize The number of bytes to skip |
|
219 * @leave KErrEof, if skipped past end of chain. |
|
220 */ |
|
221 { |
|
222 for (;;) |
|
223 { |
|
224 if (aSize < iTail) |
|
225 { |
|
226 iTail -= aSize; // Results always: iTail > 0! |
|
227 iOffset += aSize; |
|
228 return; |
|
229 } |
|
230 aSize -= iTail; |
|
231 if ((iCurrent = iCurrent->Next()) == NULL) |
|
232 { |
|
233 iTail = 0; // All scanned, at end of chain |
|
234 if (aSize > 0) // --> Error, if request was more |
|
235 User::Leave(KErrEof); // KErrEof used (for lack of any better) |
|
236 return; |
|
237 } |
|
238 iOffset = 0; |
|
239 iTail = iCurrent->Length(); |
|
240 } |
|
241 } |
|
242 |
|
243 EXPORT_C TUint8 *TPacketPoker::AdjustL(TInt aSize) |
|
244 /** |
|
245 * Arrange contiguous run of bytes. |
|
246 * |
|
247 * Arragen content of RMBuf chain so that starting from |
|
248 * the current offset, a number of bytes is accessible |
|
249 * in contiguous memory. |
|
250 * |
|
251 * @param aSize The requested length, |
|
252 * @return The pointer to be beginning of area. |
|
253 * @leave KErrEof, if request cannot be satisfied. |
|
254 */ |
|
255 { |
|
256 RMBufChain chain(iCurrent); |
|
257 if (iOffset > 0) |
|
258 { |
|
259 RMBufChain tail; |
|
260 chain.SplitL(iOffset, tail); |
|
261 tail.Align(aSize); |
|
262 chain.Append(tail); |
|
263 } |
|
264 else |
|
265 chain.Align(aSize); |
|
266 ASSERT(iCurrent == chain.First()); |
|
267 iTail = iCurrent->Length() - iOffset; |
|
268 if (iTail < aSize) // Align failed? |
|
269 User::Leave(KErrEof); // KErrEof used (for lack of any better) |
|
270 return Ptr(); |
|
271 } |
|
272 |
|
273 // |
|
274 |
|
275 EXPORT_C TBool TPacketPoker::IsExtensionHeader(TInt aProtocolId) |
|
276 /** |
|
277 * Tests whether a protocol is a known IPv6 extension header using the standard format. |
|
278 * |
|
279 * @param aProtocolId Protocol ID to test. |
|
280 * @return ETrue, |
|
281 * if the protocol header follows the generic IPv6 extension |
|
282 * header format (TInet6HeaderExtension). |
|
283 */ |
|
284 { |
|
285 // If more of these are known, could perhaps implement a |
|
286 // static boolean table indexed by protocol -- msa |
|
287 return |
|
288 ( |
|
289 aProtocolId == STATIC_CAST(TInt, KProtocolInet6HopOptions) || |
|
290 aProtocolId == STATIC_CAST(TInt, KProtocolInet6DestinationOptions) || |
|
291 aProtocolId == STATIC_CAST(TInt, KProtocolInet6RoutingHeader) |
|
292 ); |
|
293 } |
|
294 |
|
295 // TPacketHead |
|
296 // *********** |
|
297 |
|
298 EXPORT_C TBool TPacketHead::ExtHdrGet(TInt aType, TInt& aOfs, TInt& aLen) |
|
299 /** |
|
300 * Gets the offset and length of an extension header. |
|
301 * |
|
302 * @param aType Extension header type |
|
303 * @retval aOfs On return, the header offset |
|
304 * @retval aLen On return, the header length |
|
305 * @return ETrue if the header was found, otherwise EFalse |
|
306 */ |
|
307 { |
|
308 if (iPacket.IsEmpty()) |
|
309 return EFalse; |
|
310 |
|
311 TInt type = ip6.NextHeader(); |
|
312 TInt ofs = 0; |
|
313 |
|
314 while (TPacketPoker::IsExtensionHeader(type) && ofs<iOffset) |
|
315 { |
|
316 //TInet6Options is about as generic Extension Header class as possible. |
|
317 //Thus no need to create another one. |
|
318 TInet6Packet<TInet6Options> hdr(iPacket, ofs); |
|
319 if (hdr.iHdr == NULL) return EFalse; |
|
320 |
|
321 if (type == aType) |
|
322 { |
|
323 aOfs = ofs; |
|
324 aLen = hdr.iHdr->HeaderLength(); |
|
325 return ETrue; |
|
326 } |
|
327 type = hdr.iHdr->NextHeader(); |
|
328 ofs += hdr.iHdr->HeaderLength(); |
|
329 } |
|
330 return EFalse; |
|
331 } |
|
332 |
|
333 EXPORT_C TBool TPacketHead::ExtHdrGetOrPrependL(TInt aType, TInt& aOfs, TInt& aLen) |
|
334 /** |
|
335 * Gets the offset and length of an extension header, or if it doesn't already |
|
336 * exist, creates the header before all other extension headers. |
|
337 * |
|
338 * @param aType |
|
339 * Extension header type |
|
340 * @retval aOfs |
|
341 * If the header is created, this specifies the header offset. If |
|
342 * the header already exists, on return: the actual header offset. |
|
343 * @retval aLen |
|
344 * If the header is created, this specifies the header length. If |
|
345 * the header already exists, on return: the actual header length. |
|
346 * @return |
|
347 * ETrue if the header was found, EFalse if the header was created. |
|
348 */ |
|
349 { |
|
350 if (ExtHdrGet(aType, aOfs, aLen)) |
|
351 return ETrue; |
|
352 ASSERT(aLen && ((aLen & 7) == 0)); |
|
353 |
|
354 if (iPacket.IsEmpty()) |
|
355 { |
|
356 iPacket.AllocL(aLen); |
|
357 } |
|
358 else |
|
359 { |
|
360 iPacket.PrependL(aLen); |
|
361 } |
|
362 |
|
363 //Zero out the header |
|
364 iPacket.FillZ(aLen); |
|
365 TInet6Packet<TInet6Options> hdr(iPacket, 0); |
|
366 if (hdr.iHdr == NULL) |
|
367 { |
|
368 iPacket.TrimStart(aLen); |
|
369 User::Leave(KErrGeneral); |
|
370 } |
|
371 |
|
372 hdr.iHdr->SetNextHeader(ip6.NextHeader()); |
|
373 ip6.SetNextHeader(aType); |
|
374 hdr.iHdr->SetHdrExtLen((aLen >> 3) - 1); |
|
375 |
|
376 aOfs = 0; |
|
377 iOffset += aLen; |
|
378 return EFalse; |
|
379 } |
|
380 |
|
381 EXPORT_C TBool TPacketHead::ExtHdrGetOrAppendL(TInt aType, TInt& aOfs, TInt& aLen) |
|
382 /** |
|
383 * Gets the offset and length of an extension header, or if it doesn't already |
|
384 * exist, creates the header after all other extension headers. |
|
385 * |
|
386 * @param aType Extension header type |
|
387 * @retval aOfs |
|
388 * If the header is created, this specifies the header offset. If |
|
389 * the header already exists, on return, the actual header offset. |
|
390 * @retval aLen |
|
391 * If the header is created, this specifies the header length. If |
|
392 * the header already exists, on return, the actual header length. |
|
393 * @return ETrue if the header was found, EFalse if the header was created |
|
394 */ |
|
395 { |
|
396 if (ExtHdrGet(aType, aOfs, aLen)) return ETrue; |
|
397 if (iPacket.IsEmpty()) return ExtHdrGetOrPrependL(aType, aOfs, aLen); |
|
398 ASSERT(aLen && ((aLen & 7) == 0)); |
|
399 |
|
400 TInt nxt = ip6.NextHeader(); |
|
401 TInt ofs = 0; |
|
402 FOREVER |
|
403 { |
|
404 TInet6Packet<TInet6Options> hdr(iPacket, ofs); |
|
405 if (hdr.iHdr == NULL) |
|
406 User::Leave(KErrGeneral); |
|
407 |
|
408 nxt = hdr.iHdr->NextHeader(); |
|
409 ofs += hdr.iHdr->HeaderLength(); |
|
410 if (ofs >= iOffset) |
|
411 { |
|
412 hdr.iHdr->SetNextHeader(aType); |
|
413 break; |
|
414 } |
|
415 } |
|
416 |
|
417 RMBufChain tail; |
|
418 tail.AllocL(aLen); |
|
419 tail.FillZ(); |
|
420 iPacket.Append(tail); |
|
421 |
|
422 TInet6Packet<TInet6Options> hdr(iPacket, iOffset); |
|
423 if (hdr.iHdr == NULL) |
|
424 { |
|
425 iPacket.TrimEnd(iOffset); |
|
426 User::Leave(KErrGeneral); |
|
427 } |
|
428 hdr.iHdr->SetNextHeader(nxt); |
|
429 hdr.iHdr->SetHdrExtLen((aLen >> 3) - 1); |
|
430 |
|
431 aOfs = iOffset; |
|
432 iOffset += aLen; |
|
433 return EFalse; |
|
434 } |
|
435 |
|
436 // |
|
437 // TODO: Possibility to replace existing padding with an option which |
|
438 // would preserve the alignment. (eg. Home Address is kind of Pad2) |
|
439 // |
|
440 EXPORT_C void TPacketHead::AddDestinationOptionL(const TUint8* aOption, TUint8 aLen, TUint8 aAlign/*=0*/, TUint8 aModulo/*=4*/) |
|
441 { |
|
442 /** |
|
443 * Adds a Destination Option extension header option. |
|
444 * |
|
445 * If no Destination Option extension header already exists, one is created |
|
446 * as the first extension header. The option is aligned according to the |
|
447 * specified aAlign and aModulo parameters. |
|
448 * |
|
449 * @param aOption |
|
450 * Option Data |
|
451 * @param aLen |
|
452 * Option Data length |
|
453 * @param aAlign |
|
454 * Option alignment requirement. The default is no alignment. |
|
455 * @param aModulo |
|
456 * Option must start at an octect position which is a multiple |
|
457 * of the specified value |
|
458 */ |
|
459 TPtrC8 ptr(aOption, aLen); |
|
460 AddDestinationOptionL(ptr, aAlign, aModulo); |
|
461 } |
|
462 |
|
463 EXPORT_C void TPacketHead::AddDestinationOptionL(const TPtrC8& aOption, TUint8 aAlign/*=0*/, TUint8 aModulo/*=4*/) |
|
464 /** |
|
465 * Adds a Destination Option extension header option. |
|
466 * |
|
467 * If no Destination Option extension header already exists, one is |
|
468 * created as the first extension header. The option is aligned |
|
469 * according to the specified aAlign and aModulo parameters. |
|
470 * |
|
471 * @param aOption |
|
472 * Pointer to the start of the Option Data |
|
473 * @param aAlign |
|
474 * Option alignment requirement. The default is no alignment. |
|
475 * @param aModulo |
|
476 * Option must start at an octect position which is a multiple |
|
477 * of the specified value |
|
478 */ |
|
479 { |
|
480 TUint padlen = aAlign?(TUint(aAlign - 2) % aModulo):0; //Leading pad |
|
481 TInt len = 2 + padlen + aOption.Length(); |
|
482 TInt trailpad = (8 - (len & 7)) & 7; |
|
483 len += trailpad; |
|
484 |
|
485 TInt ofs; |
|
486 if (! ExtHdrGetOrAppendL(KProtocolInet6DestinationOptions, ofs, len)) |
|
487 { |
|
488 //Simple case, new header was allocated and initialized with required length. |
|
489 //Just put the option in. |
|
490 ofs += 2; |
|
491 #ifdef OPTIMIZE_PADDING |
|
492 if (padlen > 1) |
|
493 { |
|
494 TInet6Packet<TInet6OptionBase> pad(iPacket, ofs); |
|
495 if (pad.iHdr == NULL) |
|
496 User::Leave(KErrGeneral); |
|
497 pad.iHdr->SetType(KDstOptionPadN); |
|
498 pad.iHdr->SetDataLen(padlen - 2); |
|
499 } |
|
500 #endif |
|
501 ofs += padlen; |
|
502 iPacket.CopyIn(aOption, ofs); |
|
503 #ifdef OPTIMIZE_PADDING |
|
504 ofs += aOption.Length(); |
|
505 if (trailpad > 1) |
|
506 { |
|
507 TInet6Packet<TInet6OptionBase> pad(iPacket, ofs); |
|
508 if (pad.iHdr == NULL) |
|
509 User::Leave(KErrGeneral); |
|
510 pad.iHdr->SetType(KDstOptionPadN); |
|
511 pad.iHdr->SetDataLen(trailpad - 2); |
|
512 } |
|
513 #endif |
|
514 return; |
|
515 } |
|
516 |
|
517 //Now we have the position and size of the header |
|
518 TInet6Packet<TInet6Options> opt(iPacket, ofs); |
|
519 if (opt.iHdr == NULL) |
|
520 User::Leave(KErrGeneral); |
|
521 |
|
522 //padlen = leading pad |
|
523 ofs += len; |
|
524 padlen = aAlign?(TUint(aAlign - ofs) % aModulo):0; |
|
525 TInt size = padlen + aOption.Length(); |
|
526 trailpad = (8 - (size & 7)) & 7; |
|
527 size += trailpad; |
|
528 |
|
529 RMBufChain pkt; |
|
530 pkt.AllocL(size); |
|
531 pkt.FillZ(); |
|
532 #ifdef OPTIMIZE_PADDING |
|
533 //Optimize leading pad |
|
534 if (padlen > 1) |
|
535 { |
|
536 TInet6Packet<TInet6OptionBase> pad(pkt, 0); |
|
537 if (pad.iHdr == NULL) |
|
538 User::Leave(KErrGeneral); |
|
539 pad.iHdr->SetType(KDstOptionPadN); |
|
540 pad.iHdr->SetDataLen(padlen - 2); |
|
541 } |
|
542 //Optimize trailing pad |
|
543 if (trailpad > 1) |
|
544 { |
|
545 TInet6Packet<TInet6OptionBase> pad(pkt, padlen+aOption.Length()); |
|
546 if (pad.iHdr == NULL) |
|
547 User::Leave(KErrGeneral); |
|
548 pad.iHdr->SetType(KDstOptionPadN); |
|
549 pad.iHdr->SetDataLen(trailpad - 2); |
|
550 } |
|
551 #endif |
|
552 pkt.CopyIn(aOption, padlen); |
|
553 |
|
554 iPacket.Append(pkt); |
|
555 |
|
556 iOffset += size; |
|
557 opt.iHdr->SetHdrExtLen(opt.iHdr->HdrExtLen() + (size >> 3)); |
|
558 } |