/src/openthread/src/core/net/ip6.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2016, The OpenThread Authors. |
3 | | * All rights reserved. |
4 | | * |
5 | | * Redistribution and use in source and binary forms, with or without |
6 | | * modification, are permitted provided that the following conditions are met: |
7 | | * 1. Redistributions of source code must retain the above copyright |
8 | | * notice, this list of conditions and the following disclaimer. |
9 | | * 2. Redistributions in binary form must reproduce the above copyright |
10 | | * notice, this list of conditions and the following disclaimer in the |
11 | | * documentation and/or other materials provided with the distribution. |
12 | | * 3. Neither the name of the copyright holder nor the |
13 | | * names of its contributors may be used to endorse or promote products |
14 | | * derived from this software without specific prior written permission. |
15 | | * |
16 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
17 | | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 | | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 | | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
20 | | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
21 | | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
22 | | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
23 | | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
24 | | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
25 | | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | | * POSSIBILITY OF SUCH DAMAGE. |
27 | | */ |
28 | | |
29 | | /** |
30 | | * @file |
31 | | * This file implements IPv6 networking. |
32 | | */ |
33 | | |
34 | | #include "ip6.hpp" |
35 | | |
36 | | #include "instance/instance.hpp" |
37 | | |
38 | | using IcmpType = ot::Ip6::Icmp::Header::Type; |
39 | | |
40 | | static const IcmpType kForwardIcmpTypes[] = { |
41 | | IcmpType::kTypeDstUnreach, IcmpType::kTypePacketToBig, IcmpType::kTypeTimeExceeded, |
42 | | IcmpType::kTypeParameterProblem, IcmpType::kTypeEchoRequest, IcmpType::kTypeEchoReply, |
43 | | }; |
44 | | |
45 | | namespace ot { |
46 | | namespace Ip6 { |
47 | | |
48 | | RegisterLogModule("Ip6"); |
49 | | |
50 | | Ip6::Ip6(Instance &aInstance) |
51 | 21.3k | : InstanceLocator(aInstance) |
52 | 21.3k | , mReceiveFilterEnabled(false) |
53 | | #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
54 | | , mTmfOriginFilterEnabled(true) |
55 | | #endif |
56 | 21.3k | , mSendQueueTask(aInstance) |
57 | 21.3k | , mIcmp(aInstance) |
58 | 21.3k | , mUdp(aInstance) |
59 | 21.3k | , mMpl(aInstance) |
60 | | #if OPENTHREAD_CONFIG_TCP_ENABLE |
61 | 21.3k | , mTcp(aInstance) |
62 | | #endif |
63 | 21.3k | { |
64 | 21.3k | #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE |
65 | 21.3k | ResetBorderRoutingCounters(); |
66 | 21.3k | #endif |
67 | 21.3k | } |
68 | | |
69 | 3.06k | Message *Ip6::NewMessage(void) { return NewMessage(0); } |
70 | | |
71 | 4.74k | Message *Ip6::NewMessage(uint16_t aReserved) { return NewMessage(aReserved, Message::Settings::GetDefault()); } |
72 | | |
73 | | Message *Ip6::NewMessage(uint16_t aReserved, const Message::Settings &aSettings) |
74 | 276k | { |
75 | 276k | return Get<MessagePool>().Allocate( |
76 | 276k | Message::kTypeIp6, sizeof(Header) + sizeof(HopByHopHeader) + sizeof(MplOption) + aReserved, aSettings); |
77 | 276k | } |
78 | | |
79 | | Message *Ip6::NewMessageFromData(const uint8_t *aData, uint16_t aDataLength, const Message::Settings &aSettings) |
80 | 89.2k | { |
81 | 89.2k | Message *message = nullptr; |
82 | 89.2k | Message::Settings settings = aSettings; |
83 | 89.2k | const Header *header; |
84 | | |
85 | 89.2k | VerifyOrExit((aData != nullptr) && (aDataLength >= sizeof(Header))); |
86 | | |
87 | | // Determine priority from IPv6 header |
88 | 88.6k | header = reinterpret_cast<const Header *>(aData); |
89 | 88.6k | VerifyOrExit(header->IsValid()); |
90 | 88.0k | VerifyOrExit(sizeof(Header) + header->GetPayloadLength() == aDataLength); |
91 | 87.5k | settings.mPriority = DscpToPriority(header->GetDscp()); |
92 | | |
93 | 87.5k | message = Get<MessagePool>().Allocate(Message::kTypeIp6, /* aReserveHeader */ 0, settings); |
94 | | |
95 | 87.5k | VerifyOrExit(message != nullptr); |
96 | | |
97 | 86.8k | if (message->AppendBytes(aData, aDataLength) != kErrorNone) |
98 | 66 | { |
99 | 66 | message->Free(); |
100 | 66 | message = nullptr; |
101 | 66 | } |
102 | | |
103 | 89.2k | exit: |
104 | 89.2k | return message; |
105 | 86.8k | } |
106 | | |
107 | | Message::Priority Ip6::DscpToPriority(uint8_t aDscp) |
108 | 87.5k | { |
109 | 87.5k | Message::Priority priority; |
110 | 87.5k | uint8_t cs = aDscp & kDscpCsMask; |
111 | | |
112 | 87.5k | switch (cs) |
113 | 87.5k | { |
114 | 4.52k | case kDscpCs1: |
115 | 12.3k | case kDscpCs2: |
116 | 12.3k | priority = Message::kPriorityLow; |
117 | 12.3k | break; |
118 | | |
119 | 3.89k | case kDscpCs0: |
120 | 4.06k | case kDscpCs3: |
121 | 4.06k | priority = Message::kPriorityNormal; |
122 | 4.06k | break; |
123 | | |
124 | 2.16k | case kDscpCs4: |
125 | 2.61k | case kDscpCs5: |
126 | 70.7k | case kDscpCs6: |
127 | 71.1k | case kDscpCs7: |
128 | 71.1k | priority = Message::kPriorityHigh; |
129 | 71.1k | break; |
130 | | |
131 | 0 | default: |
132 | 0 | priority = Message::kPriorityNormal; |
133 | 0 | break; |
134 | 87.5k | } |
135 | | |
136 | 87.5k | return priority; |
137 | 87.5k | } |
138 | | |
139 | | uint8_t Ip6::PriorityToDscp(Message::Priority aPriority) |
140 | 205k | { |
141 | 205k | uint8_t dscp = kDscpCs0; |
142 | | |
143 | 205k | switch (aPriority) |
144 | 205k | { |
145 | 147 | case Message::kPriorityLow: |
146 | 147 | dscp = kDscpCs1; |
147 | 147 | break; |
148 | | |
149 | 3.14k | case Message::kPriorityNormal: |
150 | 205k | case Message::kPriorityNet: |
151 | 205k | dscp = kDscpCs0; |
152 | 205k | break; |
153 | | |
154 | 14 | case Message::kPriorityHigh: |
155 | 14 | dscp = kDscpCs4; |
156 | 14 | break; |
157 | 205k | } |
158 | | |
159 | 205k | return dscp; |
160 | 205k | } |
161 | | |
162 | | Error Ip6::AddMplOption(Message &aMessage, Header &aHeader) |
163 | 21.3k | { |
164 | 21.3k | Error error = kErrorNone; |
165 | 21.3k | HopByHopHeader hbhHeader; |
166 | 21.3k | MplOption mplOption; |
167 | 21.3k | PadOption padOption; |
168 | | |
169 | 21.3k | hbhHeader.SetNextHeader(aHeader.GetNextHeader()); |
170 | 21.3k | hbhHeader.SetLength(0); |
171 | 21.3k | mMpl.InitOption(mplOption, aHeader.GetSource()); |
172 | | |
173 | | // Check if MPL option may require padding |
174 | 21.3k | if (padOption.InitToPadHeaderWithSize(sizeof(HopByHopHeader) + mplOption.GetSize()) == kErrorNone) |
175 | 580 | { |
176 | 580 | SuccessOrExit(error = aMessage.PrependBytes(&padOption, padOption.GetSize())); |
177 | 580 | } |
178 | | |
179 | 21.1k | SuccessOrExit(error = aMessage.PrependBytes(&mplOption, mplOption.GetSize())); |
180 | 20.6k | SuccessOrExit(error = aMessage.Prepend(hbhHeader)); |
181 | 20.6k | aHeader.SetPayloadLength(aHeader.GetPayloadLength() + sizeof(hbhHeader) + sizeof(mplOption)); |
182 | 20.6k | aHeader.SetNextHeader(kProtoHopOpts); |
183 | | |
184 | 21.3k | exit: |
185 | 21.3k | return error; |
186 | 20.6k | } |
187 | | |
188 | | Error Ip6::PrepareMulticastToLargerThanRealmLocal(Message &aMessage, const Header &aHeader) |
189 | 18.2k | { |
190 | 18.2k | Error error = kErrorNone; |
191 | 18.2k | Header tunnelHeader; |
192 | 18.2k | const Address *source; |
193 | | |
194 | 18.2k | #if OPENTHREAD_FTD |
195 | 18.2k | if (aHeader.GetDestination().IsMulticastLargerThanRealmLocal() && |
196 | 18.2k | Get<ChildTable>().HasSleepyChildWithAddress(aHeader.GetDestination())) |
197 | 0 | { |
198 | 0 | Message *messageCopy = aMessage.Clone(); |
199 | |
|
200 | 0 | if (messageCopy != nullptr) |
201 | 0 | { |
202 | 0 | EnqueueDatagram(*messageCopy); |
203 | 0 | } |
204 | 0 | else |
205 | 0 | { |
206 | 0 | LogWarn("Failed to clone mcast message for indirect tx to sleepy children"); |
207 | 0 | } |
208 | 0 | } |
209 | 18.2k | #endif |
210 | | |
211 | | // Use IP-in-IP encapsulation (RFC2473) and ALL_MPL_FORWARDERS address. |
212 | 18.2k | tunnelHeader.InitVersionTrafficClassFlow(); |
213 | 18.2k | tunnelHeader.SetHopLimit(kDefaultHopLimit); |
214 | 18.2k | tunnelHeader.SetPayloadLength(aHeader.GetPayloadLength() + sizeof(tunnelHeader)); |
215 | 18.2k | tunnelHeader.GetDestination().SetToRealmLocalAllMplForwarders(); |
216 | 18.2k | tunnelHeader.SetNextHeader(kProtoIp6); |
217 | | |
218 | 18.2k | source = SelectSourceAddress(tunnelHeader.GetDestination()); |
219 | 18.2k | VerifyOrExit(source != nullptr, error = kErrorInvalidSourceAddress); |
220 | | |
221 | 18.0k | tunnelHeader.SetSource(*source); |
222 | | |
223 | 18.0k | SuccessOrExit(error = AddMplOption(aMessage, tunnelHeader)); |
224 | 17.2k | SuccessOrExit(error = aMessage.Prepend(tunnelHeader)); |
225 | | |
226 | 18.2k | exit: |
227 | 18.2k | return error; |
228 | 17.2k | } |
229 | | |
230 | | Error Ip6::InsertMplOption(Message &aMessage, Header &aHeader) |
231 | 14.7k | { |
232 | 14.7k | Error error = kErrorNone; |
233 | | |
234 | 14.7k | if (aHeader.GetDestination().IsMulticastLargerThanRealmLocal()) |
235 | 4.83k | { |
236 | 4.83k | error = PrepareMulticastToLargerThanRealmLocal(aMessage, aHeader); |
237 | 4.83k | ExitNow(); |
238 | 4.83k | } |
239 | | |
240 | 9.91k | VerifyOrExit(aHeader.GetDestination().IsRealmLocalMulticast()); |
241 | | |
242 | 6.48k | aMessage.RemoveHeader(sizeof(aHeader)); |
243 | | |
244 | 6.48k | if (aHeader.GetNextHeader() == kProtoHopOpts) |
245 | 3.26k | { |
246 | 3.26k | HopByHopHeader hbh; |
247 | 3.26k | uint16_t hbhSize; |
248 | 3.26k | MplOption mplOption; |
249 | 3.26k | PadOption padOption; |
250 | | |
251 | | // Read existing hop-by-hop option header |
252 | 3.26k | SuccessOrExit(error = aMessage.Read(0, hbh)); |
253 | 3.06k | hbhSize = hbh.GetSize(); |
254 | | |
255 | 3.06k | VerifyOrExit(hbhSize <= aHeader.GetPayloadLength(), error = kErrorParse); |
256 | | |
257 | | // Increment hop-by-hop option header length by one which |
258 | | // increases its total size by 8 bytes. |
259 | 2.85k | hbh.SetLength(hbh.GetLength() + 1); |
260 | 2.85k | aMessage.Write(0, hbh); |
261 | | |
262 | | // Make space for MPL Option + padding (8 bytes) at the end |
263 | | // of hop-by-hop header |
264 | 2.85k | SuccessOrExit(error = aMessage.InsertHeader(hbhSize, ExtensionHeader::kLengthUnitSize)); |
265 | | |
266 | | // Insert MPL Option |
267 | 2.85k | mMpl.InitOption(mplOption, aHeader.GetSource()); |
268 | 2.85k | aMessage.WriteBytes(hbhSize, &mplOption, mplOption.GetSize()); |
269 | | |
270 | | // Insert Pad Option (if needed) |
271 | 2.85k | if (padOption.InitToPadHeaderWithSize(mplOption.GetSize()) == kErrorNone) |
272 | 2.85k | { |
273 | 2.85k | aMessage.WriteBytes(hbhSize + mplOption.GetSize(), &padOption, padOption.GetSize()); |
274 | 2.85k | } |
275 | | |
276 | | // Update IPv6 Payload Length |
277 | 2.85k | aHeader.SetPayloadLength(aHeader.GetPayloadLength() + ExtensionHeader::kLengthUnitSize); |
278 | 2.85k | } |
279 | 3.21k | else |
280 | 3.21k | { |
281 | 3.21k | SuccessOrExit(error = AddMplOption(aMessage, aHeader)); |
282 | 3.21k | } |
283 | | |
284 | 6.07k | SuccessOrExit(error = aMessage.Prepend(aHeader)); |
285 | | |
286 | 14.7k | exit: |
287 | 14.7k | return error; |
288 | 6.07k | } |
289 | | |
290 | | Error Ip6::RemoveMplOption(Message &aMessage) |
291 | 200k | { |
292 | 200k | enum Action : uint8_t |
293 | 200k | { |
294 | 200k | kNoMplOption, |
295 | 200k | kShrinkHbh, |
296 | 200k | kRemoveHbh, |
297 | 200k | kReplaceMplWithPad, |
298 | 200k | }; |
299 | | |
300 | 200k | Error error = kErrorNone; |
301 | 200k | Action action = kNoMplOption; |
302 | 200k | Header ip6Header; |
303 | 200k | HopByHopHeader hbh; |
304 | 200k | Option option; |
305 | 200k | OffsetRange offsetRange; |
306 | 200k | OffsetRange mplOffsetRange; |
307 | 200k | PadOption padOption; |
308 | | |
309 | 200k | offsetRange.InitFromMessageFullLength(aMessage); |
310 | | |
311 | 200k | IgnoreError(aMessage.Read(offsetRange, ip6Header)); |
312 | 200k | offsetRange.AdvanceOffset(sizeof(ip6Header)); |
313 | | |
314 | 200k | VerifyOrExit(ip6Header.GetNextHeader() == kProtoHopOpts); |
315 | | |
316 | 3.64k | SuccessOrExit(error = ReadHopByHopHeader(aMessage, offsetRange, hbh)); |
317 | | |
318 | 18.6k | for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize())) |
319 | 15.2k | { |
320 | 15.2k | SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange)); |
321 | | |
322 | 15.2k | if (option.IsPadding()) |
323 | 6.94k | { |
324 | 6.94k | continue; |
325 | 6.94k | } |
326 | | |
327 | 8.33k | if (option.GetType() == MplOption::kType) |
328 | 5.35k | { |
329 | | // If multiple MPL options exist, discard packet |
330 | 5.35k | VerifyOrExit(action == kNoMplOption, error = kErrorParse); |
331 | | |
332 | | // `Option::ParseFrom()` already validated that the entire |
333 | | // option is present in the `offsetRange`. |
334 | | |
335 | 5.21k | mplOffsetRange = offsetRange; |
336 | 5.21k | mplOffsetRange.ShrinkLength(option.GetSize()); |
337 | | |
338 | 5.21k | VerifyOrExit(option.GetSize() <= sizeof(MplOption), error = kErrorParse); |
339 | | |
340 | 5.09k | if (mplOffsetRange.GetOffset() == sizeof(ip6Header) + sizeof(hbh) && hbh.GetLength() == 0) |
341 | 1.04k | { |
342 | | // First and only IPv6 Option, remove IPv6 HBH Option header |
343 | 1.04k | action = kRemoveHbh; |
344 | 1.04k | } |
345 | 4.05k | else if (mplOffsetRange.GetOffset() + ExtensionHeader::kLengthUnitSize == offsetRange.GetEndOffset()) |
346 | 1.56k | { |
347 | | // Last IPv6 Option, shrink the last 8 bytes |
348 | 1.56k | action = kShrinkHbh; |
349 | 1.56k | } |
350 | 5.09k | } |
351 | 2.97k | else if (action != kNoMplOption) |
352 | 198 | { |
353 | | // Encountered another option, now just replace |
354 | | // MPL Option with Pad Option |
355 | 198 | action = kReplaceMplWithPad; |
356 | 198 | } |
357 | 8.33k | } |
358 | | |
359 | 3.38k | switch (action) |
360 | 3.38k | { |
361 | 922 | case kNoMplOption: |
362 | 922 | break; |
363 | | |
364 | 1.35k | case kShrinkHbh: |
365 | 2.32k | case kRemoveHbh: |
366 | | // Last IPv6 Option, shrink HBH Option header by |
367 | | // 8 bytes (`kLengthUnitSize`) |
368 | 2.32k | aMessage.RemoveHeader(offsetRange.GetEndOffset() - ExtensionHeader::kLengthUnitSize, |
369 | 2.32k | ExtensionHeader::kLengthUnitSize); |
370 | | |
371 | 2.32k | if (action == kRemoveHbh) |
372 | 971 | { |
373 | 971 | ip6Header.SetNextHeader(hbh.GetNextHeader()); |
374 | 971 | } |
375 | 1.35k | else |
376 | 1.35k | { |
377 | | // Update HBH header length, decrement by one |
378 | | // which decreases its total size by 8 bytes. |
379 | | |
380 | 1.35k | hbh.SetLength(hbh.GetLength() - 1); |
381 | 1.35k | aMessage.Write(sizeof(ip6Header), hbh); |
382 | 1.35k | } |
383 | | |
384 | 2.32k | ip6Header.SetPayloadLength(ip6Header.GetPayloadLength() - ExtensionHeader::kLengthUnitSize); |
385 | 2.32k | aMessage.Write(0, ip6Header); |
386 | 2.32k | break; |
387 | | |
388 | 133 | case kReplaceMplWithPad: |
389 | 133 | padOption.InitForPadSize(static_cast<uint8_t>(mplOffsetRange.GetLength())); |
390 | 133 | aMessage.WriteBytes(mplOffsetRange.GetOffset(), &padOption, padOption.GetSize()); |
391 | 133 | break; |
392 | 3.38k | } |
393 | | |
394 | 200k | exit: |
395 | 200k | return error; |
396 | 3.38k | } |
397 | | |
398 | | void Ip6::EnqueueDatagram(Message &aMessage) |
399 | 217k | { |
400 | 217k | mSendQueue.Enqueue(aMessage); |
401 | 217k | mSendQueueTask.Post(); |
402 | 217k | } |
403 | | |
404 | | Error Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aIpProto) |
405 | 227k | { |
406 | 227k | Error error = kErrorNone; |
407 | 227k | Header header; |
408 | 227k | uint8_t dscp; |
409 | 227k | uint16_t payloadLength = aMessage.GetLength(); |
410 | | |
411 | 227k | if ((aIpProto == kProtoUdp) && |
412 | 227k | Get<Tmf::Agent>().IsTmfMessage(aMessageInfo.GetSockAddr(), aMessageInfo.GetPeerAddr(), |
413 | 226k | aMessageInfo.GetPeerPort())) |
414 | 22.0k | { |
415 | 22.0k | dscp = Tmf::Agent::PriorityToDscp(aMessage.GetPriority()); |
416 | 22.0k | } |
417 | 205k | else |
418 | 205k | { |
419 | 205k | dscp = PriorityToDscp(aMessage.GetPriority()); |
420 | 205k | } |
421 | | |
422 | 227k | header.InitVersionTrafficClassFlow(); |
423 | 227k | header.SetDscp(dscp); |
424 | 227k | header.SetEcn(aMessageInfo.GetEcn()); |
425 | 227k | header.SetPayloadLength(payloadLength); |
426 | 227k | header.SetNextHeader(aIpProto); |
427 | | |
428 | 227k | if (aMessageInfo.GetHopLimit() != 0 || aMessageInfo.ShouldAllowZeroHopLimit()) |
429 | 201k | { |
430 | 201k | header.SetHopLimit(aMessageInfo.GetHopLimit()); |
431 | 201k | } |
432 | 26.2k | else |
433 | 26.2k | { |
434 | 26.2k | header.SetHopLimit(kDefaultHopLimit); |
435 | 26.2k | } |
436 | | |
437 | 227k | if (aMessageInfo.GetSockAddr().IsUnspecified() || aMessageInfo.GetSockAddr().IsMulticast()) |
438 | 12.3k | { |
439 | 12.3k | const Address *source = SelectSourceAddress(aMessageInfo.GetPeerAddr()); |
440 | | |
441 | 12.3k | VerifyOrExit(source != nullptr, error = kErrorInvalidSourceAddress); |
442 | 12.3k | header.SetSource(*source); |
443 | 12.3k | } |
444 | 215k | else |
445 | 215k | { |
446 | 215k | header.SetSource(aMessageInfo.GetSockAddr()); |
447 | 215k | } |
448 | | |
449 | 227k | header.SetDestination(aMessageInfo.GetPeerAddr()); |
450 | | |
451 | 227k | if (aMessageInfo.GetPeerAddr().IsRealmLocalMulticast()) |
452 | 135 | { |
453 | 135 | SuccessOrExit(error = AddMplOption(aMessage, header)); |
454 | 135 | } |
455 | | |
456 | 227k | SuccessOrExit(error = aMessage.Prepend(header)); |
457 | | |
458 | 227k | Checksum::UpdateMessageChecksum(aMessage, header.GetSource(), header.GetDestination(), aIpProto); |
459 | | |
460 | 227k | if (aMessageInfo.GetPeerAddr().IsMulticastLargerThanRealmLocal()) |
461 | 13.4k | { |
462 | 13.4k | SuccessOrExit(error = PrepareMulticastToLargerThanRealmLocal(aMessage, header)); |
463 | 13.4k | } |
464 | | |
465 | 217k | aMessage.SetMulticastLoop(aMessageInfo.GetMulticastLoop()); |
466 | | |
467 | 217k | if (aMessage.GetLength() > kMaxDatagramLength) |
468 | 0 | { |
469 | 0 | error = FragmentDatagram(aMessage, aIpProto); |
470 | 0 | } |
471 | 217k | else |
472 | 217k | { |
473 | 217k | EnqueueDatagram(aMessage); |
474 | 217k | } |
475 | | |
476 | 227k | exit: |
477 | | |
478 | 227k | return error; |
479 | 217k | } |
480 | | |
481 | | void Ip6::HandleSendQueue(void) |
482 | 191k | { |
483 | 191k | Message *message; |
484 | | |
485 | 382k | while ((message = mSendQueue.GetHead()) != nullptr) |
486 | 191k | { |
487 | 191k | mSendQueue.Dequeue(*message); |
488 | 191k | IgnoreError(HandleDatagram(OwnedPtr<Message>(message))); |
489 | 191k | } |
490 | 191k | } |
491 | | |
492 | | Error Ip6::ReadHopByHopHeader(const Message &aMessage, OffsetRange &aOffsetRange, HopByHopHeader &aHbhHeader) const |
493 | 23.8k | { |
494 | | // Reads the HBH header from the message at the given offset range. |
495 | | // On success, updates `aOffsetRange` to indicate the location of |
496 | | // options within the HBH header. |
497 | | |
498 | 23.8k | Error error; |
499 | | |
500 | 23.8k | SuccessOrExit(error = aMessage.Read(aOffsetRange, aHbhHeader)); |
501 | 23.8k | VerifyOrExit(aOffsetRange.Contains(aHbhHeader.GetSize()), error = kErrorParse); |
502 | 23.4k | aOffsetRange.ShrinkLength(aHbhHeader.GetSize()); |
503 | 23.4k | aOffsetRange.AdvanceOffset(sizeof(HopByHopHeader)); |
504 | | |
505 | 23.8k | exit: |
506 | 23.8k | return error; |
507 | 23.4k | } |
508 | | |
509 | | Error Ip6::HandleOptions(Message &aMessage, const Header &aHeader, bool &aReceive) |
510 | 20.2k | { |
511 | 20.2k | Error error = kErrorNone; |
512 | 20.2k | HopByHopHeader hbhHeader; |
513 | 20.2k | Option option; |
514 | 20.2k | OffsetRange offsetRange; |
515 | | |
516 | 20.2k | offsetRange.InitFromMessageOffsetToEnd(aMessage); |
517 | | |
518 | 20.2k | SuccessOrExit(error = ReadHopByHopHeader(aMessage, offsetRange, hbhHeader)); |
519 | | |
520 | 97.3k | for (; !offsetRange.IsEmpty(); offsetRange.AdvanceOffset(option.GetSize())) |
521 | 80.5k | { |
522 | 80.5k | SuccessOrExit(error = option.ParseFrom(aMessage, offsetRange)); |
523 | | |
524 | 79.2k | if (option.IsPadding()) |
525 | 56.7k | { |
526 | 56.7k | continue; |
527 | 56.7k | } |
528 | | |
529 | 22.5k | if (option.GetType() == MplOption::kType) |
530 | 17.0k | { |
531 | 17.0k | SuccessOrExit(error = mMpl.ProcessOption(aMessage, offsetRange, aHeader.GetSource(), aReceive)); |
532 | 15.8k | continue; |
533 | 17.0k | } |
534 | | |
535 | 5.53k | VerifyOrExit(option.GetAction() == Option::kActionSkip, error = kErrorDrop); |
536 | 5.53k | } |
537 | | |
538 | 16.8k | aMessage.SetOffset(offsetRange.GetEndOffset()); |
539 | | |
540 | 20.2k | exit: |
541 | 20.2k | return error; |
542 | 16.8k | } |
543 | | |
544 | | #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE |
545 | | Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto) |
546 | 0 | { |
547 | 0 | Error error = kErrorNone; |
548 | 0 | Header header; |
549 | 0 | FragmentHeader fragmentHeader; |
550 | 0 | Message *fragment = nullptr; |
551 | 0 | uint16_t fragmentCnt = 0; |
552 | 0 | uint16_t payloadFragment = 0; |
553 | 0 | uint16_t offset = 0; |
554 | |
|
555 | 0 | uint16_t maxPayloadFragment = |
556 | 0 | FragmentHeader::MakeDivisibleByEight(kMinimalMtu - aMessage.GetOffset() - sizeof(fragmentHeader)); |
557 | 0 | uint16_t payloadLeft = aMessage.GetLength() - aMessage.GetOffset(); |
558 | |
|
559 | 0 | SuccessOrExit(error = aMessage.Read(0, header)); |
560 | 0 | header.SetNextHeader(kProtoFragment); |
561 | |
|
562 | 0 | fragmentHeader.Init(); |
563 | 0 | fragmentHeader.SetIdentification(Random::NonCrypto::GetUint32()); |
564 | 0 | fragmentHeader.SetNextHeader(aIpProto); |
565 | 0 | fragmentHeader.SetMoreFlag(); |
566 | |
|
567 | 0 | while (payloadLeft != 0) |
568 | 0 | { |
569 | 0 | if (payloadLeft < maxPayloadFragment) |
570 | 0 | { |
571 | 0 | fragmentHeader.ClearMoreFlag(); |
572 | |
|
573 | 0 | payloadFragment = payloadLeft; |
574 | 0 | payloadLeft = 0; |
575 | |
|
576 | 0 | LogDebg("Last Fragment"); |
577 | 0 | } |
578 | 0 | else |
579 | 0 | { |
580 | 0 | payloadLeft -= maxPayloadFragment; |
581 | 0 | payloadFragment = maxPayloadFragment; |
582 | 0 | } |
583 | |
|
584 | 0 | offset = fragmentCnt * FragmentHeader::BytesToFragmentOffset(maxPayloadFragment); |
585 | 0 | fragmentHeader.SetOffset(offset); |
586 | |
|
587 | 0 | VerifyOrExit((fragment = NewMessage()) != nullptr, error = kErrorNoBufs); |
588 | 0 | IgnoreError(fragment->SetPriority(aMessage.GetPriority())); |
589 | 0 | SuccessOrExit(error = fragment->SetLength(aMessage.GetOffset() + sizeof(fragmentHeader) + payloadFragment)); |
590 | | |
591 | 0 | header.SetPayloadLength(payloadFragment + sizeof(fragmentHeader)); |
592 | 0 | fragment->Write(0, header); |
593 | |
|
594 | 0 | fragment->SetOffset(aMessage.GetOffset()); |
595 | 0 | fragment->Write(aMessage.GetOffset(), fragmentHeader); |
596 | |
|
597 | 0 | fragment->WriteBytesFromMessage( |
598 | 0 | /* aWriteOffset */ aMessage.GetOffset() + sizeof(fragmentHeader), aMessage, |
599 | 0 | /* aReadOffset */ aMessage.GetOffset() + FragmentHeader::FragmentOffsetToBytes(offset), |
600 | 0 | /* aLength */ payloadFragment); |
601 | |
|
602 | 0 | EnqueueDatagram(*fragment); |
603 | |
|
604 | 0 | fragmentCnt++; |
605 | 0 | fragment = nullptr; |
606 | |
|
607 | 0 | LogInfo("Fragment %d with %d bytes sent", fragmentCnt, payloadFragment); |
608 | 0 | } |
609 | | |
610 | 0 | aMessage.Free(); |
611 | |
|
612 | 0 | exit: |
613 | |
|
614 | 0 | if (error == kErrorNoBufs) |
615 | 0 | { |
616 | 0 | LogWarn("No buffer for Ip6 fragmentation"); |
617 | 0 | } |
618 | |
|
619 | 0 | FreeMessageOnError(fragment, error); |
620 | 0 | return error; |
621 | 0 | } |
622 | | |
623 | | Error Ip6::HandleFragment(Message &aMessage) |
624 | 7.61k | { |
625 | 7.61k | Error error = kErrorNone; |
626 | 7.61k | Header header, headerBuffer; |
627 | 7.61k | FragmentHeader fragmentHeader; |
628 | 7.61k | Message *message = nullptr; |
629 | 7.61k | uint16_t offset = 0; |
630 | 7.61k | uint16_t payloadFragment = 0; |
631 | 7.61k | bool isFragmented = true; |
632 | | |
633 | 7.61k | SuccessOrExit(error = aMessage.Read(0, header)); |
634 | 7.61k | SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), fragmentHeader)); |
635 | | |
636 | 6.48k | if (fragmentHeader.GetOffset() == 0 && !fragmentHeader.IsMoreFlagSet()) |
637 | 2.22k | { |
638 | 2.22k | isFragmented = false; |
639 | 2.22k | aMessage.MoveOffset(sizeof(fragmentHeader)); |
640 | 2.22k | ExitNow(); |
641 | 2.22k | } |
642 | | |
643 | 4.25k | for (Message &msg : mReassemblyList) |
644 | 5.75k | { |
645 | 5.75k | SuccessOrExit(error = msg.Read(0, headerBuffer)); |
646 | | |
647 | 5.75k | if (msg.GetDatagramTag() == fragmentHeader.GetIdentification() && |
648 | 5.75k | headerBuffer.GetSource() == header.GetSource() && headerBuffer.GetDestination() == header.GetDestination()) |
649 | 927 | { |
650 | 927 | message = &msg; |
651 | 927 | break; |
652 | 927 | } |
653 | 5.75k | } |
654 | | |
655 | 4.25k | offset = FragmentHeader::FragmentOffsetToBytes(fragmentHeader.GetOffset()); |
656 | 4.25k | payloadFragment = aMessage.GetLength() - aMessage.GetOffset() - sizeof(fragmentHeader); |
657 | | |
658 | 4.25k | LogInfo("Fragment with id %lu received > %u bytes, offset %u", ToUlong(fragmentHeader.GetIdentification()), |
659 | 4.25k | payloadFragment, offset); |
660 | | |
661 | 4.25k | if (offset + payloadFragment + aMessage.GetOffset() > kMaxAssembledDatagramLength) |
662 | 277 | { |
663 | 277 | LogWarn("Packet too large for fragment buffer"); |
664 | 277 | ExitNow(error = kErrorNoBufs); |
665 | 277 | } |
666 | | |
667 | 3.97k | if (message == nullptr) |
668 | 3.06k | { |
669 | 3.06k | LogDebg("start reassembly"); |
670 | 3.06k | VerifyOrExit((message = NewMessage()) != nullptr, error = kErrorNoBufs); |
671 | 1.83k | mReassemblyList.Enqueue(*message); |
672 | | |
673 | 1.83k | message->SetTimestampToNow(); |
674 | 1.83k | message->SetOffset(0); |
675 | 1.83k | message->SetDatagramTag(fragmentHeader.GetIdentification()); |
676 | | |
677 | | // copying the non-fragmentable header to the fragmentation buffer |
678 | 1.83k | SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, 0, aMessage.GetOffset())); |
679 | | |
680 | 1.83k | Get<TimeTicker>().RegisterReceiver(TimeTicker::kIp6FragmentReassembler); |
681 | 1.83k | } |
682 | | |
683 | | // increase message buffer if necessary |
684 | 2.75k | if (message->GetLength() < offset + payloadFragment + aMessage.GetOffset()) |
685 | 1.87k | { |
686 | 1.87k | SuccessOrExit(error = message->SetLength(offset + payloadFragment + aMessage.GetOffset())); |
687 | 1.87k | } |
688 | | |
689 | | // copy the fragment payload into the message buffer |
690 | 2.59k | message->WriteBytesFromMessage( |
691 | 2.59k | /* aWriteOffset */ aMessage.GetOffset() + offset, aMessage, |
692 | 2.59k | /* aReadOffset */ aMessage.GetOffset() + sizeof(fragmentHeader), /* aLength */ payloadFragment); |
693 | | |
694 | | // check if it is the last frame |
695 | 2.59k | if (!fragmentHeader.IsMoreFlagSet()) |
696 | 1.27k | { |
697 | | // use the offset value for the whole ip message length |
698 | 1.27k | message->SetOffset(aMessage.GetOffset() + offset + payloadFragment); |
699 | | |
700 | | // creates the header for the reassembled ipv6 package |
701 | 1.27k | SuccessOrExit(error = aMessage.Read(0, header)); |
702 | 1.27k | header.SetPayloadLength(message->GetLength() - sizeof(header)); |
703 | 1.27k | header.SetNextHeader(fragmentHeader.GetNextHeader()); |
704 | 1.27k | message->Write(0, header); |
705 | | |
706 | 1.27k | LogDebg("Reassembly complete."); |
707 | | |
708 | 1.27k | mReassemblyList.Dequeue(*message); |
709 | | |
710 | 1.27k | IgnoreError(HandleDatagram(OwnedPtr<Message>(message), /* aIsReassembled */ true)); |
711 | 1.27k | } |
712 | | |
713 | 7.61k | exit: |
714 | 7.61k | if (error != kErrorDrop && error != kErrorNone && isFragmented) |
715 | 2.78k | { |
716 | 2.78k | if (message != nullptr) |
717 | 162 | { |
718 | 162 | mReassemblyList.DequeueAndFree(*message); |
719 | 162 | } |
720 | | |
721 | 2.78k | LogWarnOnError(error, "reassemble"); |
722 | 2.78k | } |
723 | | |
724 | 7.61k | if (isFragmented) |
725 | 5.38k | { |
726 | | // drop all fragments, the payload is stored in the fragment buffer |
727 | 5.38k | error = kErrorDrop; |
728 | 5.38k | } |
729 | | |
730 | 7.61k | return error; |
731 | 2.59k | } |
732 | | |
733 | 0 | void Ip6::CleanupFragmentationBuffer(void) { mReassemblyList.DequeueAndFreeAll(); } |
734 | | |
735 | | void Ip6::HandleTimeTick(void) |
736 | 0 | { |
737 | 0 | UpdateReassemblyList(); |
738 | |
|
739 | 0 | if (mReassemblyList.GetHead() == nullptr) |
740 | 0 | { |
741 | 0 | Get<TimeTicker>().UnregisterReceiver(TimeTicker::kIp6FragmentReassembler); |
742 | 0 | } |
743 | 0 | } |
744 | | |
745 | | void Ip6::UpdateReassemblyList(void) |
746 | 0 | { |
747 | 0 | TimeMilli now = TimerMilli::GetNow(); |
748 | |
|
749 | 0 | for (Message &message : mReassemblyList) |
750 | 0 | { |
751 | 0 | if (now - message.GetTimestamp() >= TimeMilli::SecToMsec(kReassemblyTimeout)) |
752 | 0 | { |
753 | 0 | LogNote("Reassembly timeout."); |
754 | 0 | SendIcmpError(message, Icmp::Header::kTypeTimeExceeded, Icmp::Header::kCodeFragmReasTimeEx); |
755 | |
|
756 | 0 | mReassemblyList.DequeueAndFree(message); |
757 | 0 | } |
758 | 0 | } |
759 | 0 | } |
760 | | |
761 | | void Ip6::SendIcmpError(Message &aMessage, Icmp::Header::Type aIcmpType, Icmp::Header::Code aIcmpCode) |
762 | 0 | { |
763 | 0 | Error error = kErrorNone; |
764 | 0 | Header header; |
765 | 0 | MessageInfo messageInfo; |
766 | |
|
767 | 0 | SuccessOrExit(error = aMessage.Read(0, header)); |
768 | | |
769 | 0 | messageInfo.SetPeerAddr(header.GetSource()); |
770 | 0 | messageInfo.SetSockAddr(header.GetDestination()); |
771 | 0 | messageInfo.SetHopLimit(header.GetHopLimit()); |
772 | |
|
773 | 0 | error = mIcmp.SendError(aIcmpType, aIcmpCode, messageInfo, aMessage); |
774 | |
|
775 | 0 | exit: |
776 | 0 | LogWarnOnError(error, "send ICMP"); |
777 | 0 | OT_UNUSED_VARIABLE(error); |
778 | 0 | } |
779 | | |
780 | | #else |
781 | | Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto) |
782 | | { |
783 | | OT_UNUSED_VARIABLE(aIpProto); |
784 | | |
785 | | EnqueueDatagram(aMessage); |
786 | | |
787 | | return kErrorNone; |
788 | | } |
789 | | |
790 | | Error Ip6::HandleFragment(Message &aMessage) |
791 | | { |
792 | | Error error = kErrorNone; |
793 | | FragmentHeader fragmentHeader; |
794 | | |
795 | | SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), fragmentHeader)); |
796 | | |
797 | | VerifyOrExit(fragmentHeader.GetOffset() == 0 && !fragmentHeader.IsMoreFlagSet(), error = kErrorDrop); |
798 | | |
799 | | aMessage.MoveOffset(sizeof(fragmentHeader)); |
800 | | |
801 | | exit: |
802 | | return error; |
803 | | } |
804 | | #endif // OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE |
805 | | |
806 | | Error Ip6::HandleExtensionHeaders(OwnedPtr<Message> &aMessagePtr, |
807 | | const Header &aHeader, |
808 | | uint8_t &aNextHeader, |
809 | | bool &aReceive) |
810 | 276k | { |
811 | 276k | Error error = kErrorNone; |
812 | 276k | ExtensionHeader extHeader; |
813 | | |
814 | 295k | while (aReceive || aNextHeader == kProtoHopOpts) |
815 | 80.7k | { |
816 | 80.7k | SuccessOrExit(error = aMessagePtr->Read(aMessagePtr->GetOffset(), extHeader)); |
817 | | |
818 | 76.7k | switch (aNextHeader) |
819 | 76.7k | { |
820 | 20.0k | case kProtoHopOpts: |
821 | 20.2k | case kProtoDstOpts: |
822 | 20.2k | SuccessOrExit(error = HandleOptions(*aMessagePtr, aHeader, aReceive)); |
823 | 16.8k | break; |
824 | | |
825 | 16.8k | case kProtoFragment: |
826 | 7.61k | IgnoreError(PassToHost(aMessagePtr, aHeader, aNextHeader, aReceive, Message::kCopyToUse)); |
827 | 7.61k | SuccessOrExit(error = HandleFragment(*aMessagePtr)); |
828 | 2.22k | break; |
829 | | |
830 | 2.22k | case kProtoIp6: |
831 | 1.23k | ExitNow(); |
832 | | |
833 | 207 | case kProtoRouting: |
834 | 401 | case kProtoNone: |
835 | 401 | ExitNow(error = kErrorDrop); |
836 | | |
837 | 47.2k | default: |
838 | 47.2k | ExitNow(); |
839 | 76.7k | } |
840 | | |
841 | 19.0k | aNextHeader = extHeader.GetNextHeader(); |
842 | 19.0k | } |
843 | | |
844 | 276k | exit: |
845 | 276k | return error; |
846 | 276k | } |
847 | | |
848 | | Error Ip6::TakeOrCopyMessagePtr(OwnedPtr<Message> &aTargetPtr, |
849 | | OwnedPtr<Message> &aMessagePtr, |
850 | | Message::Ownership aMessageOwnership) |
851 | 261k | { |
852 | 261k | switch (aMessageOwnership) |
853 | 261k | { |
854 | 49.6k | case Message::kTakeCustody: |
855 | 49.6k | aTargetPtr = aMessagePtr.PassOwnership(); |
856 | 49.6k | break; |
857 | | |
858 | 212k | case Message::kCopyToUse: |
859 | 212k | aTargetPtr.Reset(aMessagePtr->Clone()); |
860 | 212k | break; |
861 | 261k | } |
862 | | |
863 | 261k | return (aTargetPtr != nullptr) ? kErrorNone : kErrorNoBufs; |
864 | 261k | } |
865 | | |
866 | | Error Ip6::Receive(Header &aIp6Header, |
867 | | OwnedPtr<Message> &aMessagePtr, |
868 | | uint8_t aIpProto, |
869 | | Message::Ownership aMessageOwnership) |
870 | 47.2k | { |
871 | 47.2k | Error error = kErrorNone; |
872 | 47.2k | OwnedPtr<Message> messagePtr; |
873 | 47.2k | MessageInfo messageInfo; |
874 | | |
875 | 47.2k | messageInfo.Clear(); |
876 | 47.2k | messageInfo.SetPeerAddr(aIp6Header.GetSource()); |
877 | 47.2k | messageInfo.SetSockAddr(aIp6Header.GetDestination()); |
878 | 47.2k | messageInfo.SetHopLimit(aIp6Header.GetHopLimit()); |
879 | 47.2k | messageInfo.SetEcn(aIp6Header.GetEcn()); |
880 | | |
881 | 47.2k | switch (aIpProto) |
882 | 47.2k | { |
883 | 41.2k | case kProtoUdp: |
884 | 44.8k | case kProtoIcmp6: |
885 | 44.8k | break; |
886 | 0 | #if OPENTHREAD_CONFIG_TCP_ENABLE |
887 | 2.10k | case kProtoTcp: |
888 | 2.10k | break; |
889 | 0 | #endif |
890 | 322 | default: |
891 | 322 | ExitNow(); |
892 | 47.2k | } |
893 | | |
894 | 46.9k | SuccessOrExit(error = TakeOrCopyMessagePtr(messagePtr, aMessagePtr, aMessageOwnership)); |
895 | | |
896 | 46.9k | switch (aIpProto) |
897 | 46.9k | { |
898 | 0 | #if OPENTHREAD_CONFIG_TCP_ENABLE |
899 | 2.10k | case kProtoTcp: |
900 | 2.10k | error = mTcp.HandleMessage(aIp6Header, *messagePtr, messageInfo); |
901 | 2.10k | break; |
902 | 0 | #endif |
903 | 41.2k | case kProtoUdp: |
904 | 41.2k | error = mUdp.HandleMessage(*messagePtr, messageInfo); |
905 | 41.2k | break; |
906 | | |
907 | 3.54k | case kProtoIcmp6: |
908 | 3.54k | error = mIcmp.HandleMessage(*messagePtr, messageInfo); |
909 | 3.54k | break; |
910 | | |
911 | 0 | default: |
912 | 0 | break; |
913 | 46.9k | } |
914 | | |
915 | 47.2k | exit: |
916 | 47.2k | LogWarnOnError(error, "handle payload"); |
917 | 47.2k | return error; |
918 | 46.9k | } |
919 | | |
920 | | Error Ip6::PassToHost(OwnedPtr<Message> &aMessagePtr, |
921 | | const Header &aHeader, |
922 | | uint8_t aIpProto, |
923 | | bool aReceive, |
924 | | Message::Ownership aMessageOwnership) |
925 | 259k | { |
926 | | // This method passes the message to host by invoking the |
927 | | // registered IPv6 receive callback. When NAT64 is enabled, it |
928 | | // may also perform translation and invoke IPv4 receive |
929 | | // callback. |
930 | | |
931 | 259k | Error error = kErrorNone; |
932 | 259k | OwnedPtr<Message> messagePtr; |
933 | | |
934 | 259k | VerifyOrExit(aMessagePtr->IsLoopbackToHostAllowed()); |
935 | | |
936 | 259k | VerifyOrExit(mReceiveCallback.IsSet(), error = kErrorNoRoute); |
937 | | |
938 | | // Do not pass IPv6 packets that exceed kMinimalMtu. |
939 | 259k | VerifyOrExit(aMessagePtr->GetLength() <= kMinimalMtu, error = kErrorDrop); |
940 | | |
941 | 259k | #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTING_REACHABILITY_CHECK_ICMP6_ERROR_ENABLE |
942 | 259k | if (!aReceive) |
943 | 205k | { |
944 | 205k | Get<BorderRouter::RoutingManager>().CheckReachabilityToSendIcmpError(*aMessagePtr, aHeader); |
945 | 205k | } |
946 | 259k | #endif |
947 | | |
948 | | // If the sender used mesh-local address as source, do not pass to |
949 | | // host unless this message is intended for this device itself. |
950 | 259k | if (Get<Mle::Mle>().IsMeshLocalAddress(aHeader.GetSource())) |
951 | 28.7k | { |
952 | 28.7k | VerifyOrExit(aReceive, error = kErrorDrop); |
953 | 28.7k | } |
954 | | |
955 | 256k | if (mReceiveFilterEnabled && aReceive) |
956 | 53.6k | { |
957 | 53.6k | switch (aIpProto) |
958 | 53.6k | { |
959 | 3.18k | case kProtoIcmp6: |
960 | 3.18k | if (mIcmp.ShouldHandleEchoRequest(aHeader.GetDestination())) |
961 | 1.19k | { |
962 | 1.19k | Icmp::Header icmp; |
963 | | |
964 | 1.19k | IgnoreError(aMessagePtr->Read(aMessagePtr->GetOffset(), icmp)); |
965 | 1.19k | VerifyOrExit(icmp.GetType() != Icmp::Header::kTypeEchoRequest, error = kErrorDrop); |
966 | 1.19k | } |
967 | | |
968 | 2.21k | break; |
969 | | |
970 | 41.0k | case kProtoUdp: |
971 | 41.0k | { |
972 | 41.0k | Udp::Header udp; |
973 | | |
974 | 41.0k | IgnoreError(aMessagePtr->Read(aMessagePtr->GetOffset(), udp)); |
975 | 41.0k | VerifyOrExit(!Get<Udp>().IsPortInUse(udp.GetDestinationPort()), error = kErrorNoRoute); |
976 | 993 | break; |
977 | 41.0k | } |
978 | | |
979 | 993 | #if OPENTHREAD_CONFIG_TCP_ENABLE |
980 | | // Do not pass TCP message to avoid dual processing from both |
981 | | // OpenThread and POSIX TCP stacks. |
982 | 2.09k | case kProtoTcp: |
983 | 2.09k | error = kErrorNoRoute; |
984 | 2.09k | ExitNow(); |
985 | 0 | #endif |
986 | | |
987 | 7.28k | default: |
988 | 7.28k | break; |
989 | 53.6k | } |
990 | 53.6k | } |
991 | | |
992 | 213k | SuccessOrExit(error = TakeOrCopyMessagePtr(messagePtr, aMessagePtr, aMessageOwnership)); |
993 | | |
994 | 200k | IgnoreError(RemoveMplOption(*messagePtr)); |
995 | | |
996 | | #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE |
997 | | switch (Get<Nat64::Translator>().TranslateFromIp6(*messagePtr)) |
998 | | { |
999 | | case Nat64::Translator::kNotTranslated: |
1000 | | break; |
1001 | | |
1002 | | case Nat64::Translator::kDrop: |
1003 | | ExitNow(error = kErrorDrop); |
1004 | | |
1005 | | case Nat64::Translator::kForward: |
1006 | | VerifyOrExit(mIp4ReceiveCallback.IsSet(), error = kErrorNoRoute); |
1007 | | // Pass message to callback transferring its ownership. |
1008 | | mIp4ReceiveCallback.Invoke(messagePtr.Release()); |
1009 | | ExitNow(); |
1010 | | } |
1011 | | #endif |
1012 | | |
1013 | 200k | #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE |
1014 | 200k | UpdateBorderRoutingCounters(aHeader, messagePtr->GetLength(), /* aIsInbound */ false); |
1015 | 200k | #endif |
1016 | | |
1017 | | #if OPENTHREAD_CONFIG_IP6_RESTRICT_FORWARDING_LARGER_SCOPE_MCAST_WITH_LOCAL_SRC |
1018 | | // Some platforms (e.g. Android) currently doesn't restrict link-local/mesh-local source |
1019 | | // addresses when forwarding multicast packets. |
1020 | | // For a multicast packet sent from link-local/mesh-local address to scope larger |
1021 | | // than realm-local, set the hop limit to 1 before sending to host, so this packet |
1022 | | // will not be forwarded by host. |
1023 | | if (aHeader.GetDestination().IsMulticastLargerThanRealmLocal() && |
1024 | | (aHeader.GetSource().IsLinkLocalUnicast() || (Get<Mle::Mle>().IsMeshLocalAddress(aHeader.GetSource())))) |
1025 | | { |
1026 | | messagePtr->Write<uint8_t>(Header::kHopLimitFieldOffset, 1); |
1027 | | } |
1028 | | #endif |
1029 | | |
1030 | | // Pass message to callback transferring its ownership. |
1031 | 200k | mReceiveCallback.Invoke(messagePtr.Release()); |
1032 | | |
1033 | 259k | exit: |
1034 | 259k | return error; |
1035 | 200k | } |
1036 | | |
1037 | | Error Ip6::SendRaw(OwnedPtr<Message> aMessagePtr) |
1038 | 86.7k | { |
1039 | 86.7k | Error error = kErrorNone; |
1040 | 86.7k | Header header; |
1041 | | |
1042 | 86.7k | SuccessOrExit(error = header.ParseFrom(*aMessagePtr)); |
1043 | 86.7k | VerifyOrExit(!header.GetSource().IsMulticast(), error = kErrorInvalidSourceAddress); |
1044 | | |
1045 | | #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE |
1046 | | // The filtering rules don't apply to packets from DUA. |
1047 | | if (!Get<BackboneRouter::Leader>().IsDomainUnicast(header.GetSource())) |
1048 | | #endif |
1049 | 86.5k | { |
1050 | | // When the packet is forwarded from host to Thread, if its source is on-mesh or its destination is |
1051 | | // mesh-local, we'll drop the packet unless the packet originates from this device. |
1052 | 86.5k | if (Get<NetworkData::Leader>().IsOnMesh(header.GetSource()) || |
1053 | 86.5k | Get<Mle::Mle>().IsMeshLocalAddress(header.GetDestination())) |
1054 | 28.3k | { |
1055 | 28.3k | VerifyOrExit(Get<ThreadNetif>().HasUnicastAddress(header.GetSource()), error = kErrorDrop); |
1056 | 28.3k | } |
1057 | 86.5k | } |
1058 | | |
1059 | 85.5k | if (header.GetDestination().IsMulticast()) |
1060 | 14.7k | { |
1061 | 14.7k | SuccessOrExit(error = InsertMplOption(*aMessagePtr, header)); |
1062 | 14.7k | } |
1063 | | |
1064 | 84.1k | #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE |
1065 | 84.1k | UpdateBorderRoutingCounters(header, aMessagePtr->GetLength(), /* aIsInbound */ true); |
1066 | 84.1k | #endif |
1067 | | |
1068 | 84.1k | error = HandleDatagram(aMessagePtr.PassOwnership()); |
1069 | | |
1070 | 86.7k | exit: |
1071 | 86.7k | return error; |
1072 | 84.1k | } |
1073 | | |
1074 | | void Ip6::DetermineAction(const Message &aMessage, |
1075 | | const Header &aHeader, |
1076 | | bool &aForwardThread, |
1077 | | bool &aForwardHost, |
1078 | | bool &aReceive) const |
1079 | 276k | { |
1080 | | // Determine `aForwardThread`, `aForwardHost` and `aReceive` |
1081 | | // based on the destination address and message origin. |
1082 | | |
1083 | 276k | uint16_t rloc16; |
1084 | | |
1085 | 276k | aForwardThread = false; |
1086 | 276k | aForwardHost = false; |
1087 | 276k | aReceive = false; |
1088 | | |
1089 | 276k | if (aHeader.GetDestination().IsMulticast()) |
1090 | 204k | { |
1091 | | // Destination is multicast |
1092 | | |
1093 | | // Forward multicast message to thread unless we received it |
1094 | | // on Thread netif. |
1095 | | |
1096 | 204k | aForwardThread = !aMessage.IsOriginThreadNetif(); |
1097 | | |
1098 | 204k | #if OPENTHREAD_FTD |
1099 | 204k | if (aMessage.IsOriginThreadNetif() && aHeader.GetDestination().IsMulticastLargerThanRealmLocal() && |
1100 | 204k | Get<ChildTable>().HasSleepyChildWithAddress(aHeader.GetDestination())) |
1101 | 0 | { |
1102 | 0 | aForwardThread = true; |
1103 | 0 | } |
1104 | 204k | #endif |
1105 | | |
1106 | | // Always forward multicast packets to host network stack |
1107 | 204k | aForwardHost = true; |
1108 | | |
1109 | | // If subscribed to the multicast address, receive if it is from the |
1110 | | // Thread netif or if multicast loop is allowed. |
1111 | | |
1112 | 204k | if ((aMessage.IsOriginThreadNetif() || aMessage.GetMulticastLoop()) && |
1113 | 204k | Get<ThreadNetif>().IsMulticastSubscribed(aHeader.GetDestination())) |
1114 | 0 | { |
1115 | 0 | aReceive = true; |
1116 | 0 | } |
1117 | | |
1118 | 204k | ExitNow(); |
1119 | 204k | } |
1120 | | |
1121 | | // Destination is unicast |
1122 | | |
1123 | 71.8k | if (Get<ThreadNetif>().HasUnicastAddress(aHeader.GetDestination())) |
1124 | 54.8k | { |
1125 | 54.8k | aReceive = true; |
1126 | 54.8k | ExitNow(); |
1127 | 54.8k | } |
1128 | | |
1129 | 17.0k | if (aHeader.GetDestination().IsLinkLocalUnicast()) |
1130 | 6.62k | { |
1131 | | // Forward a message with a link-local destination address |
1132 | | // to thread, unless it is received on the Thread netif. |
1133 | | |
1134 | 6.62k | aForwardThread = !aMessage.IsOriginThreadNetif(); |
1135 | 6.62k | ExitNow(); |
1136 | 6.62k | } |
1137 | | |
1138 | 10.3k | if (IsOnLink(aHeader.GetDestination())) |
1139 | 2.72k | { |
1140 | | #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE |
1141 | | aForwardThread = (!aMessage.IsLoopbackToHostAllowed() || |
1142 | | !Get<BackboneRouter::Manager>().ShouldForwardDuaToBackbone(aHeader.GetDestination())); |
1143 | | aForwardHost = !aForwardThread; |
1144 | | #else |
1145 | 2.72k | aForwardThread = true; |
1146 | 2.72k | #endif |
1147 | 2.72k | ExitNow(); |
1148 | 2.72k | } |
1149 | | |
1150 | 7.65k | if (Get<NetworkData::Leader>().RouteLookup(aHeader.GetSource(), aHeader.GetDestination(), rloc16) != kErrorNone) |
1151 | 7.65k | { |
1152 | | // No route in mesh, forward to host (as a last resort). |
1153 | 7.65k | LogNote("Failed to find valid route for: %s", aHeader.GetDestination().ToString().AsCString()); |
1154 | 7.65k | aForwardHost = true; |
1155 | 7.65k | ExitNow(); |
1156 | 7.65k | } |
1157 | | |
1158 | | // `RouteLookup()` found a destination within the mesh. If we are |
1159 | | // the destination (device is acting as a Border Router), forward |
1160 | | // it to the host. Otherwise, forward to Thread. |
1161 | | |
1162 | 0 | if (Get<Mle::Mle>().HasRloc16(rloc16)) |
1163 | 0 | { |
1164 | 0 | aForwardHost = true; |
1165 | 0 | } |
1166 | 0 | else |
1167 | 0 | { |
1168 | 0 | aForwardThread = true; |
1169 | 0 | } |
1170 | |
|
1171 | 276k | exit: |
1172 | 276k | return; |
1173 | 0 | } |
1174 | | |
1175 | | Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled) |
1176 | 277k | { |
1177 | 277k | Error error; |
1178 | 277k | Header header; |
1179 | 277k | bool receive; |
1180 | 277k | bool forwardThread; |
1181 | 277k | bool forwardHost; |
1182 | 277k | uint8_t nextHeader; |
1183 | | |
1184 | 277k | SuccessOrExit(error = header.ParseFrom(*aMessagePtr)); |
1185 | | |
1186 | 276k | if (!aMessagePtr->IsOriginHostTrusted()) |
1187 | 84.1k | { |
1188 | 84.1k | VerifyOrExit(!header.GetSource().IsLoopback() && !header.GetDestination().IsLoopback(), error = kErrorDrop); |
1189 | 84.1k | } |
1190 | | |
1191 | 276k | DetermineAction(*aMessagePtr, header, forwardThread, forwardHost, receive); |
1192 | | |
1193 | 276k | aMessagePtr->SetOffset(sizeof(header)); |
1194 | | |
1195 | | // Process IPv6 Extension Headers |
1196 | 276k | nextHeader = header.GetNextHeader(); |
1197 | 276k | SuccessOrExit(error = HandleExtensionHeaders(aMessagePtr, header, nextHeader, receive)); |
1198 | | |
1199 | 262k | if (receive && (nextHeader == kProtoIp6)) |
1200 | 1.23k | { |
1201 | | // Process the embedded IPv6 message in an IPv6 tunnel message. |
1202 | | // If we need to `forwardThread` we create a copy by cloning |
1203 | | // the message, otherwise we take ownership of `aMessage` |
1204 | | // itself and use it directly. The encapsulating header is |
1205 | | // then removed before processing the embedded message. |
1206 | | |
1207 | 1.23k | OwnedPtr<Message> messagePtr; |
1208 | 1.23k | bool multicastLoop = aMessagePtr->GetMulticastLoop(); |
1209 | | |
1210 | 1.23k | SuccessOrExit(error = TakeOrCopyMessagePtr(messagePtr, aMessagePtr, |
1211 | 1.23k | forwardThread ? Message::kCopyToUse : Message::kTakeCustody)); |
1212 | 1.23k | messagePtr->SetMulticastLoop(multicastLoop); |
1213 | 1.23k | messagePtr->RemoveHeader(messagePtr->GetOffset()); |
1214 | | |
1215 | 1.23k | Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageReceive, *messagePtr); |
1216 | | |
1217 | 1.23k | IgnoreError(HandleDatagram(messagePtr.PassOwnership(), aIsReassembled)); |
1218 | | |
1219 | 1.23k | receive = false; |
1220 | 1.23k | forwardHost = false; |
1221 | 1.23k | } |
1222 | | |
1223 | 262k | if ((forwardHost || receive) && !aIsReassembled) |
1224 | 252k | { |
1225 | 252k | error = PassToHost(aMessagePtr, header, nextHeader, receive, |
1226 | 252k | (receive || forwardThread) ? Message::kCopyToUse : Message::kTakeCustody); |
1227 | 252k | } |
1228 | | |
1229 | 262k | if (receive) |
1230 | 47.2k | { |
1231 | 47.2k | error = Receive(header, aMessagePtr, nextHeader, forwardThread ? Message::kCopyToUse : Message::kTakeCustody); |
1232 | 47.2k | } |
1233 | | |
1234 | 262k | if (forwardThread) |
1235 | 212k | { |
1236 | 212k | if (aMessagePtr->IsOriginThreadNetif()) |
1237 | 0 | { |
1238 | 0 | VerifyOrExit(Get<Mle::Mle>().IsRouterOrLeader()); |
1239 | 0 | header.SetHopLimit(header.GetHopLimit() - 1); |
1240 | 0 | } |
1241 | | |
1242 | 212k | VerifyOrExit(header.GetHopLimit() > 0, error = kErrorDrop); |
1243 | | |
1244 | 206k | aMessagePtr->Write<uint8_t>(Header::kHopLimitFieldOffset, header.GetHopLimit()); |
1245 | | |
1246 | 206k | if (nextHeader == kProtoIcmp6) |
1247 | 1.14k | { |
1248 | 1.14k | uint8_t icmpType; |
1249 | | |
1250 | 1.14k | SuccessOrExit(error = aMessagePtr->Read(aMessagePtr->GetOffset(), icmpType)); |
1251 | | |
1252 | 880 | error = kErrorDrop; |
1253 | | |
1254 | 880 | for (IcmpType type : kForwardIcmpTypes) |
1255 | 4.72k | { |
1256 | 4.72k | if (icmpType == type) |
1257 | 264 | { |
1258 | 264 | error = kErrorNone; |
1259 | 264 | break; |
1260 | 264 | } |
1261 | 4.72k | } |
1262 | | |
1263 | 880 | SuccessOrExit(error); |
1264 | 880 | } |
1265 | | |
1266 | | #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE |
1267 | | if (mTmfOriginFilterEnabled) |
1268 | | #endif |
1269 | 205k | { |
1270 | 205k | if (aMessagePtr->IsOriginHostUntrusted() && (nextHeader == kProtoUdp)) |
1271 | 1.59k | { |
1272 | 1.59k | Udp::Header udpHeader; |
1273 | | |
1274 | 1.59k | SuccessOrExit(error = aMessagePtr->Read(aMessagePtr->GetOffset(), udpHeader)); |
1275 | | |
1276 | 1.30k | if (udpHeader.GetDestinationPort() == Tmf::kUdpPort) |
1277 | 391 | { |
1278 | 391 | LogNote("Dropping TMF message from untrusted origin"); |
1279 | 391 | ExitNow(error = kErrorDrop); |
1280 | 391 | } |
1281 | 1.30k | } |
1282 | 205k | } |
1283 | | |
1284 | | #if OPENTHREAD_CONFIG_MULTI_RADIO |
1285 | | // Since the message will be forwarded, we clear the radio |
1286 | | // type on the message to allow the radio type for tx to be |
1287 | | // selected later (based on the radios supported by the next |
1288 | | // hop). |
1289 | | aMessagePtr->ClearRadioType(); |
1290 | | #endif |
1291 | | |
1292 | 204k | Get<MeshForwarder>().SendMessage(aMessagePtr.PassOwnership()); |
1293 | 204k | } |
1294 | | |
1295 | 277k | exit: |
1296 | 277k | return error; |
1297 | 262k | } |
1298 | | |
1299 | | Error Ip6::SelectSourceAddress(MessageInfo &aMessageInfo) const |
1300 | 0 | { |
1301 | 0 | Error error = kErrorNone; |
1302 | 0 | const Address *source; |
1303 | |
|
1304 | 0 | source = SelectSourceAddress(aMessageInfo.GetPeerAddr()); |
1305 | 0 | VerifyOrExit(source != nullptr, error = kErrorNotFound); |
1306 | 0 | aMessageInfo.SetSockAddr(*source); |
1307 | |
|
1308 | 0 | exit: |
1309 | 0 | return error; |
1310 | 0 | } |
1311 | | |
1312 | | const Address *Ip6::SelectSourceAddress(const Address &aDestination) const |
1313 | 30.6k | { |
1314 | 30.6k | uint8_t destScope = aDestination.GetScope(); |
1315 | 30.6k | bool destIsRloc = Get<Mle::Mle>().IsRoutingLocator(aDestination); |
1316 | 30.6k | const Netif::UnicastAddress *bestAddr = nullptr; |
1317 | 30.6k | uint8_t bestMatchLen = 0; |
1318 | | |
1319 | 30.6k | for (const Netif::UnicastAddress &addr : Get<ThreadNetif>().GetUnicastAddresses()) |
1320 | 51.3k | { |
1321 | 51.3k | bool newAddrIsPreferred = false; |
1322 | 51.3k | uint8_t matchLen; |
1323 | 51.3k | uint8_t overrideScope; |
1324 | | |
1325 | 51.3k | if (Get<Mle::Mle>().IsAnycastLocator(addr.GetAddress())) |
1326 | 376 | { |
1327 | | // Don't use anycast address as source address. |
1328 | 376 | continue; |
1329 | 376 | } |
1330 | | |
1331 | 50.9k | matchLen = aDestination.PrefixMatch(addr.GetAddress()); |
1332 | | |
1333 | 50.9k | if (matchLen >= addr.mPrefixLength) |
1334 | 8.03k | { |
1335 | 8.03k | matchLen = addr.mPrefixLength; |
1336 | 8.03k | overrideScope = addr.GetScope(); |
1337 | 8.03k | } |
1338 | 42.9k | else |
1339 | 42.9k | { |
1340 | 42.9k | overrideScope = destScope; |
1341 | 42.9k | } |
1342 | | |
1343 | 50.9k | if (addr.GetAddress() == aDestination) |
1344 | 99 | { |
1345 | | // Rule 1: Prefer same address |
1346 | 99 | bestAddr = &addr; |
1347 | 99 | ExitNow(); |
1348 | 99 | } |
1349 | | |
1350 | 50.8k | if (bestAddr == nullptr) |
1351 | 30.4k | { |
1352 | 30.4k | newAddrIsPreferred = true; |
1353 | 30.4k | } |
1354 | 20.4k | else if (addr.GetScope() < bestAddr->GetScope()) |
1355 | 15.7k | { |
1356 | | // Rule 2: Prefer appropriate scope |
1357 | 15.7k | newAddrIsPreferred = (addr.GetScope() >= overrideScope); |
1358 | 15.7k | } |
1359 | 4.69k | else if (addr.GetScope() > bestAddr->GetScope()) |
1360 | 2.43k | { |
1361 | 2.43k | newAddrIsPreferred = (bestAddr->GetScope() < overrideScope); |
1362 | 2.43k | } |
1363 | 2.26k | else if (addr.mPreferred != bestAddr->mPreferred) |
1364 | 159 | { |
1365 | | // Rule 3: Avoid deprecated addresses |
1366 | 159 | newAddrIsPreferred = addr.mPreferred; |
1367 | 159 | } |
1368 | 2.10k | else if (matchLen > bestMatchLen) |
1369 | 600 | { |
1370 | | // Rule 6: Prefer matching label |
1371 | | // Rule 7: Prefer public address |
1372 | | // Rule 8: Use longest prefix matching |
1373 | | |
1374 | 600 | newAddrIsPreferred = true; |
1375 | 600 | } |
1376 | 1.50k | else if ((matchLen == bestMatchLen) && (destIsRloc == Get<Mle::Mle>().IsRoutingLocator(addr.GetAddress()))) |
1377 | 1.05k | { |
1378 | | // Additional rule: Prefer RLOC source for RLOC destination, EID source for anything else |
1379 | 1.05k | newAddrIsPreferred = true; |
1380 | 1.05k | } |
1381 | | |
1382 | 50.8k | if (newAddrIsPreferred) |
1383 | 35.4k | { |
1384 | 35.4k | bestAddr = &addr; |
1385 | 35.4k | bestMatchLen = matchLen; |
1386 | | |
1387 | | // Infer destination scope based on prefix match |
1388 | 35.4k | if (bestMatchLen >= bestAddr->mPrefixLength) |
1389 | 7.51k | { |
1390 | 7.51k | destScope = bestAddr->GetScope(); |
1391 | 7.51k | } |
1392 | 35.4k | } |
1393 | 50.8k | } |
1394 | | |
1395 | 30.6k | exit: |
1396 | 30.6k | return (bestAddr != nullptr) ? &bestAddr->GetAddress() : nullptr; |
1397 | 30.6k | } |
1398 | | |
1399 | | bool Ip6::IsOnLink(const Address &aAddress) const |
1400 | 10.3k | { |
1401 | 10.3k | bool isOnLink = false; |
1402 | | |
1403 | 10.3k | if (Get<NetworkData::Leader>().IsOnMesh(aAddress)) |
1404 | 766 | { |
1405 | 766 | ExitNow(isOnLink = true); |
1406 | 766 | } |
1407 | | |
1408 | 9.61k | for (const Netif::UnicastAddress &unicastAddr : Get<ThreadNetif>().GetUnicastAddresses()) |
1409 | 17.1k | { |
1410 | 17.1k | if (unicastAddr.GetAddress().PrefixMatch(aAddress) >= unicastAddr.mPrefixLength) |
1411 | 1.95k | { |
1412 | 1.95k | ExitNow(isOnLink = true); |
1413 | 1.95k | } |
1414 | 17.1k | } |
1415 | | |
1416 | 10.3k | exit: |
1417 | 10.3k | return isOnLink; |
1418 | 9.61k | } |
1419 | | |
1420 | | #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE |
1421 | | |
1422 | | void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLength, bool aIsInbound) |
1423 | 285k | { |
1424 | 285k | static constexpr uint8_t kPrefixLength = 48; |
1425 | | |
1426 | 285k | otPacketsAndBytes *counter = nullptr; |
1427 | 285k | otPacketsAndBytes *internetCounter = nullptr; |
1428 | | |
1429 | 285k | VerifyOrExit(!aHeader.GetSource().IsLinkLocalUnicast()); |
1430 | 86.3k | VerifyOrExit(!aHeader.GetDestination().IsLinkLocalUnicast()); |
1431 | 77.2k | VerifyOrExit(!Get<Mle::Mle>().IsMeshLocalAddress(aHeader.GetSource())); |
1432 | 50.0k | VerifyOrExit(!Get<Mle::Mle>().IsMeshLocalAddress(aHeader.GetDestination())); |
1433 | | |
1434 | 49.7k | if (aIsInbound) |
1435 | 40.9k | { |
1436 | 40.9k | VerifyOrExit(!Get<Netif>().HasUnicastAddress(aHeader.GetSource())); |
1437 | | |
1438 | 40.2k | if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength)) |
1439 | 33.6k | { |
1440 | 33.6k | internetCounter = &mBrCounters.mInboundInternet; |
1441 | 33.6k | } |
1442 | | |
1443 | 40.2k | if (aHeader.GetDestination().IsMulticast()) |
1444 | 13.3k | { |
1445 | 13.3k | VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal()); |
1446 | 3.84k | counter = &mBrCounters.mInboundMulticast; |
1447 | 3.84k | } |
1448 | 26.9k | else |
1449 | 26.9k | { |
1450 | 26.9k | counter = &mBrCounters.mInboundUnicast; |
1451 | 26.9k | } |
1452 | 40.2k | } |
1453 | 8.80k | else |
1454 | 8.80k | { |
1455 | 8.80k | VerifyOrExit(!Get<Netif>().HasUnicastAddress(aHeader.GetDestination())); |
1456 | | |
1457 | 4.26k | if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength)) |
1458 | 4.00k | { |
1459 | 4.00k | internetCounter = &mBrCounters.mOutboundInternet; |
1460 | 4.00k | } |
1461 | | |
1462 | 4.26k | if (aHeader.GetDestination().IsMulticast()) |
1463 | 2.98k | { |
1464 | 2.98k | VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal()); |
1465 | 0 | counter = &mBrCounters.mOutboundMulticast; |
1466 | 0 | } |
1467 | 1.28k | else |
1468 | 1.28k | { |
1469 | 1.28k | counter = &mBrCounters.mOutboundUnicast; |
1470 | 1.28k | } |
1471 | 4.26k | } |
1472 | | |
1473 | 285k | exit: |
1474 | | |
1475 | 285k | if (counter) |
1476 | 32.0k | { |
1477 | 32.0k | counter->mPackets++; |
1478 | 32.0k | counter->mBytes += aMessageLength; |
1479 | 32.0k | } |
1480 | 285k | if (internetCounter) |
1481 | 37.6k | { |
1482 | 37.6k | internetCounter->mPackets++; |
1483 | 37.6k | internetCounter->mBytes += aMessageLength; |
1484 | 37.6k | } |
1485 | 285k | } |
1486 | | |
1487 | | #endif // OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE |
1488 | | |
1489 | | // LCOV_EXCL_START |
1490 | | |
1491 | | const char *Ip6::IpProtoToString(uint8_t aIpProto) |
1492 | 0 | { |
1493 | 0 | static constexpr Stringify::Entry kIpProtoTable[] = { |
1494 | 0 | {kProtoHopOpts, "HopOpts"}, {kProtoTcp, "TCP"}, {kProtoUdp, "UDP"}, |
1495 | 0 | {kProtoIp6, "IP6"}, {kProtoRouting, "Routing"}, {kProtoFragment, "Frag"}, |
1496 | 0 | {kProtoIcmp6, "ICMP6"}, {kProtoNone, "None"}, {kProtoDstOpts, "DstOpts"}, |
1497 | 0 | }; |
1498 | |
|
1499 | 0 | static_assert(Stringify::IsSorted(kIpProtoTable), "kIpProtoTable is not sorted"); |
1500 | |
|
1501 | 0 | return Stringify::Lookup(aIpProto, kIpProtoTable, "Unknown"); |
1502 | 0 | } |
1503 | | |
1504 | | const char *Ip6::EcnToString(Ecn aEcn) |
1505 | 0 | { |
1506 | 0 | static const char *const kEcnStrings[] = { |
1507 | 0 | "no", // (0) kEcnNotCapable |
1508 | 0 | "e1", // (1) kEcnCapable1 (ECT1) |
1509 | 0 | "e0", // (2) kEcnCapable0 (ECT0) |
1510 | 0 | "ce", // (3) kEcnMarked (Congestion Encountered) |
1511 | 0 | }; |
1512 | |
|
1513 | 0 | struct EnumCheck |
1514 | 0 | { |
1515 | 0 | InitEnumValidatorCounter(); |
1516 | 0 | ValidateNextEnum(kEcnNotCapable); |
1517 | 0 | ValidateNextEnum(kEcnCapable1); |
1518 | 0 | ValidateNextEnum(kEcnCapable0); |
1519 | 0 | ValidateNextEnum(kEcnMarked); |
1520 | 0 | }; |
1521 | |
|
1522 | 0 | return kEcnStrings[aEcn]; |
1523 | 0 | } |
1524 | | |
1525 | | // LCOV_EXCL_STOP |
1526 | | |
1527 | | //--------------------------------------------------------------------------------------------------------------------- |
1528 | | // Headers |
1529 | | |
1530 | | Error Headers::ParseFrom(const Message &aMessage) |
1531 | 191k | { |
1532 | 191k | Error error = kErrorParse; |
1533 | | |
1534 | 191k | Clear(); |
1535 | | |
1536 | 191k | SuccessOrExit(mIp6Header.ParseFrom(aMessage)); |
1537 | | |
1538 | 191k | switch (mIp6Header.GetNextHeader()) |
1539 | 191k | { |
1540 | 191k | case kProtoUdp: |
1541 | 191k | SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mUdp)); |
1542 | 191k | break; |
1543 | 191k | case kProtoTcp: |
1544 | 0 | SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mTcp)); |
1545 | 0 | break; |
1546 | 0 | case kProtoIcmp6: |
1547 | 0 | SuccessOrExit(aMessage.Read(sizeof(Header), mHeader.mIcmp)); |
1548 | 0 | break; |
1549 | 0 | default: |
1550 | 0 | break; |
1551 | 191k | } |
1552 | | |
1553 | 191k | error = kErrorNone; |
1554 | | |
1555 | 191k | exit: |
1556 | 191k | return error; |
1557 | 191k | } |
1558 | | |
1559 | | Error Headers::DecompressFrom(const Message &aMessage, uint16_t aOffset, const Mac::Addresses &aMacAddrs) |
1560 | 0 | { |
1561 | 0 | static constexpr uint16_t kReadLength = sizeof(Lowpan::FragmentHeader::NextFrag) + sizeof(Headers); |
1562 | |
|
1563 | 0 | uint8_t frameBuffer[kReadLength]; |
1564 | 0 | uint16_t frameLength; |
1565 | 0 | FrameData frameData; |
1566 | |
|
1567 | 0 | frameLength = aMessage.ReadBytes(aOffset, frameBuffer, sizeof(frameBuffer)); |
1568 | 0 | frameData.Init(frameBuffer, frameLength); |
1569 | |
|
1570 | 0 | return DecompressFrom(frameData, aMacAddrs, aMessage.GetInstance()); |
1571 | 0 | } |
1572 | | |
1573 | | Error Headers::DecompressFrom(const FrameData &aFrameData, const Mac::Addresses &aMacAddrs, Instance &aInstance) |
1574 | 0 | { |
1575 | 0 | Error error = kErrorNone; |
1576 | 0 | FrameData frameData = aFrameData; |
1577 | 0 | Lowpan::FragmentHeader fragmentHeader; |
1578 | 0 | bool nextHeaderCompressed; |
1579 | |
|
1580 | 0 | if (fragmentHeader.ParseFrom(frameData) == kErrorNone) |
1581 | 0 | { |
1582 | | // Only the first fragment header is followed by a LOWPAN_IPHC header |
1583 | 0 | VerifyOrExit(fragmentHeader.GetDatagramOffset() == 0, error = kErrorNotFound); |
1584 | 0 | } |
1585 | | |
1586 | 0 | VerifyOrExit(Lowpan::Lowpan::IsLowpanHc(frameData), error = kErrorNotFound); |
1587 | | |
1588 | 0 | SuccessOrExit(error = aInstance.Get<Lowpan::Lowpan>().DecompressBaseHeader(mIp6Header, nextHeaderCompressed, |
1589 | 0 | aMacAddrs, frameData)); |
1590 | | |
1591 | 0 | switch (mIp6Header.GetNextHeader()) |
1592 | 0 | { |
1593 | 0 | case kProtoUdp: |
1594 | 0 | if (nextHeaderCompressed) |
1595 | 0 | { |
1596 | 0 | SuccessOrExit(error = aInstance.Get<Lowpan::Lowpan>().DecompressUdpHeader(mHeader.mUdp, frameData)); |
1597 | 0 | } |
1598 | 0 | else |
1599 | 0 | { |
1600 | 0 | SuccessOrExit(error = frameData.Read(mHeader.mUdp)); |
1601 | 0 | } |
1602 | 0 | break; |
1603 | | |
1604 | 0 | case kProtoTcp: |
1605 | 0 | SuccessOrExit(error = frameData.Read(mHeader.mTcp)); |
1606 | 0 | break; |
1607 | | |
1608 | 0 | case kProtoIcmp6: |
1609 | 0 | SuccessOrExit(error = frameData.Read(mHeader.mIcmp)); |
1610 | 0 | break; |
1611 | | |
1612 | 0 | default: |
1613 | 0 | break; |
1614 | 0 | } |
1615 | | |
1616 | 0 | exit: |
1617 | 0 | return error; |
1618 | 0 | } |
1619 | | |
1620 | | uint16_t Headers::GetSourcePort(void) const |
1621 | 191k | { |
1622 | 191k | uint16_t port = 0; |
1623 | | |
1624 | 191k | switch (GetIpProto()) |
1625 | 191k | { |
1626 | 191k | case kProtoUdp: |
1627 | 191k | port = mHeader.mUdp.GetSourcePort(); |
1628 | 191k | break; |
1629 | | |
1630 | 0 | case kProtoTcp: |
1631 | 0 | port = mHeader.mTcp.GetSourcePort(); |
1632 | 0 | break; |
1633 | | |
1634 | 0 | default: |
1635 | 0 | break; |
1636 | 191k | } |
1637 | | |
1638 | 191k | return port; |
1639 | 191k | } |
1640 | | |
1641 | | void Headers::SetSourcePort(uint16_t aSrcPort) |
1642 | 0 | { |
1643 | 0 | switch (GetIpProto()) |
1644 | 0 | { |
1645 | 0 | case kProtoUdp: |
1646 | 0 | mHeader.mUdp.SetSourcePort(aSrcPort); |
1647 | 0 | break; |
1648 | | |
1649 | 0 | case kProtoTcp: |
1650 | 0 | mHeader.mTcp.SetSourcePort(aSrcPort); |
1651 | 0 | break; |
1652 | | |
1653 | 0 | default: |
1654 | 0 | break; |
1655 | 0 | } |
1656 | 0 | } |
1657 | | |
1658 | | uint16_t Headers::GetDestinationPort(void) const |
1659 | 0 | { |
1660 | 0 | uint16_t port = 0; |
1661 | |
|
1662 | 0 | switch (GetIpProto()) |
1663 | 0 | { |
1664 | 0 | case kProtoUdp: |
1665 | 0 | port = mHeader.mUdp.GetDestinationPort(); |
1666 | 0 | break; |
1667 | | |
1668 | 0 | case kProtoTcp: |
1669 | 0 | port = mHeader.mTcp.GetDestinationPort(); |
1670 | 0 | break; |
1671 | | |
1672 | 0 | default: |
1673 | 0 | break; |
1674 | 0 | } |
1675 | | |
1676 | 0 | return port; |
1677 | 0 | } |
1678 | | |
1679 | | uint16_t Headers::GetChecksum(void) const |
1680 | 0 | { |
1681 | 0 | uint16_t checksum = 0; |
1682 | |
|
1683 | 0 | switch (GetIpProto()) |
1684 | 0 | { |
1685 | 0 | case kProtoUdp: |
1686 | 0 | checksum = mHeader.mUdp.GetChecksum(); |
1687 | 0 | break; |
1688 | | |
1689 | 0 | case kProtoTcp: |
1690 | 0 | checksum = mHeader.mTcp.GetChecksum(); |
1691 | 0 | break; |
1692 | | |
1693 | 0 | case kProtoIcmp6: |
1694 | 0 | checksum = mHeader.mIcmp.GetChecksum(); |
1695 | 0 | break; |
1696 | | |
1697 | 0 | default: |
1698 | 0 | break; |
1699 | 0 | } |
1700 | | |
1701 | 0 | return checksum; |
1702 | 0 | } |
1703 | | |
1704 | | } // namespace Ip6 |
1705 | | } // namespace ot |