1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "net_manager_constants.h"
17 #include "router_advertisement_daemon.h"
18 #include <csignal>
19 #include <net/if.h>
20 #include <sys/time.h>
21
22 namespace OHOS {
23 namespace NetManagerStandard {
24 namespace {
25 /** https://www.rfc-editor.org/rfc/rfc4861#section-6.2.1
26 * MaxRtrAdvInterval: MUST be no less than 4 seconds and no greater than 1800 seconds.
27 * Default: 600 seconds
28 * MinRtrAdvInterval: MUST be no less than 3seconds and no greater than 0.75 * MaxRtrAdvInterval.
29 * Default: 0.33 * MaxRtrAdvInterval If
30 * MaxRtrAdvInterval >= 9 seconds; otherwise, the
31 * Default is MaxRtrAdvInterval.
32 */
33 constexpr uint32_t MAX_URGENT_RTR_ADVERTISEMENTS = 10;
34 constexpr uint32_t RECV_RS_TIMEOUT = 1;
35 constexpr uint32_t SEND_RA_INTERVAL = 3;
36 constexpr uint32_t SEND_RA_DELAY = 1;
37 constexpr size_t RA_HEADER_SIZE = 16;
38
39 /**
40 * https://www.rfc-editor.org/rfc/rfc4861.html#section-6.1.2-4.2
41 * Note: If neither M nor O flags are set, this indicates that no
42 * information is available via DHCPv6.
43 */
44 constexpr uint8_t DEFAULT_ROUTER_PRE = 0x08;
45 constexpr uint8_t PREFIX_INFO_FLAGS = 0xc0;
46 constexpr uint32_t DEFAULT_HOP_LIMIT = 255;
47
48 /**
49 * https://tools.ietf.org/html/rfc4861#section-2.3
50 * all-nodes multicast address
51 * - the link-local scope address to reach all nodes,
52 * FF02::1.
53 */
54 constexpr const char *DST_IPV6 = "ff02::1";
55 } // namespace
56
57 RouterAdvertisementDaemon *RouterAdvertisementDaemon::pThis = nullptr;
RouterAdvertisementDaemon()58 RouterAdvertisementDaemon::RouterAdvertisementDaemon()
59 {
60 raParams_ = std::make_shared<RaParams>();
61 }
62
IsSocketValid()63 bool RouterAdvertisementDaemon::IsSocketValid()
64 {
65 return socket_ > 0;
66 }
67
HupRaThread()68 void RouterAdvertisementDaemon::HupRaThread()
69 {
70 stopRaThread_ = true;
71 }
72
Init(const std::string & ifaceName)73 int32_t RouterAdvertisementDaemon::Init(const std::string &ifaceName)
74 {
75 sendRaTimes_ = 0;
76 raParams_->name_ = ifaceName;
77 raParams_->index_ = if_nametoindex(ifaceName.c_str());
78 if (memset_s(&dstIpv6Addr_, sizeof(dstIpv6Addr_), 0, sizeof(dstIpv6Addr_)) != EOK) {
79 return NETMANAGER_EXT_ERR_MEMSET_FAIL;
80 }
81 dstIpv6Addr_.sin6_port = 0;
82 dstIpv6Addr_.sin6_family = AF_INET6;
83 dstIpv6Addr_.sin6_scope_id = 0;
84 inet_pton(AF_INET6, DST_IPV6, &dstIpv6Addr_.sin6_addr);
85 return NETMANAGER_EXT_SUCCESS;
86 }
87
StartRa()88 int32_t RouterAdvertisementDaemon::StartRa()
89 {
90 NETMGR_EXT_LOG_I("StartRa");
91 if (!CreateRASocket()) {
92 NETMGR_EXT_LOG_E("StartRa fail due to socket");
93 return NETMANAGER_EXT_ERR_PARAMETER_ERROR;
94 }
95 pThis = this;
96 stopRaThread_ = false;
97 recvRsThread_ = std::thread([this]() { this->RunRecvRsThread(); });
98 pthread_setname_np(recvRsThread_.native_handle(), "OH_Net_RecvRs");
99 recvRsThread_.detach();
100 return NETMANAGER_EXT_SUCCESS;
101 }
102
StopRa()103 void RouterAdvertisementDaemon::StopRa()
104 {
105 NETMGR_EXT_LOG_I("StopRa");
106 HupRaThread();
107 CloseRaSocket();
108 raParams_ = nullptr;
109
110 // close timer
111 itimerval value = {};
112 setitimer(ITIMER_REAL, &value, nullptr);
113 }
114
CreateRASocket()115 bool RouterAdvertisementDaemon::CreateRASocket()
116 {
117 NETMGR_EXT_LOG_I("CreateRASocket Start");
118 socket_ = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
119 if (socket_ < 0) {
120 NETMGR_EXT_LOG_E("CreateRASocket fail, errno[%{public}d]", errno);
121 return false;
122 }
123 timeval timeout = {};
124 timeout.tv_sec = RECV_RS_TIMEOUT;
125 if (setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
126 NETMGR_EXT_LOG_E("CreateRASocket setsockopt SO_RCVTIMEO fail");
127 close(socket_);
128 return false;
129 }
130 ifreq ifr = {};
131 if (strncpy_s(ifr.ifr_name, IFNAMSIZ - 1, raParams_->name_.c_str(), raParams_->name_.size()) != EOK) {
132 NETMGR_EXT_LOG_E("CreateRASocket strncopy fail");
133 close(socket_);
134 return false;
135 }
136 if (setsockopt(socket_, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
137 NETMGR_EXT_LOG_E("CreateRASocket setsockopt SO_BINDTODEVICE fail");
138 close(socket_);
139 return false;
140 }
141 uint32_t hoplimitNew = DEFAULT_HOP_LIMIT;
142 if (setsockopt(socket_, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (void *)&hoplimitNew, sizeof(hoplimitNew)) == -1) {
143 NETMGR_EXT_LOG_E(" setsockopt IPV6_UNICAST_HOPS fail");
144 }
145 return true;
146 }
147
CloseRaSocket()148 void RouterAdvertisementDaemon::CloseRaSocket()
149 {
150 NETMGR_EXT_LOG_I("CloseRaSocket Start");
151 if (socket_ > 0) {
152 close(socket_);
153 }
154 socket_ = -1;
155 }
156
MaybeSendRa(sockaddr_in6 & dest)157 bool RouterAdvertisementDaemon::MaybeSendRa(sockaddr_in6 &dest)
158 {
159 NETMGR_EXT_LOG_D("Send Ra Enter, socket_[%{public}d], raPacketLength_[%{public}hu]", socket_, raPacketLength_);
160 if (raPacketLength_ < RA_HEADER_SIZE) {
161 NETMGR_EXT_LOG_E("Send Ra failed due to Ra packet length less than RA header size");
162 return false;
163 }
164 if (!IsSocketValid()) {
165 NETMGR_EXT_LOG_E("Send Ra failed due to socket invalid");
166 return false;
167 }
168 int ret = sendto(socket_, raPacket_, raPacketLength_, 0, (sockaddr *)&dest, sizeof(dest));
169 if (ret < 0) {
170 NETMGR_EXT_LOG_E("Send Ra error, ret[%{public}d], errno[%{public}d]", ret, errno);
171 return false;
172 }
173 return true;
174 }
175
ProcessSendRaPacket(int inputSignal)176 void RouterAdvertisementDaemon::ProcessSendRaPacket(int inputSignal)
177 {
178 if (pThis == nullptr) {
179 NETMGR_EXT_LOG_E("pThis is nullptr!");
180 return;
181 }
182 if (!pThis->IsSocketValid() || pThis->stopRaThread_) {
183 NETMGR_EXT_LOG_E("socket closed or stopRaThread!");
184 return;
185 }
186 if (pThis->AssembleRaLocked()) {
187 pThis->MaybeSendRa(pThis->dstIpv6Addr_);
188 }
189 pThis->ResetRaRetryInterval();
190 }
191
RunRecvRsThread()192 void RouterAdvertisementDaemon::RunRecvRsThread()
193 {
194 NETMGR_EXT_LOG_I("Start to receive Rs thread, socket[%{public}d]", socket_);
195 if (signal(SIGALRM, ProcessSendRaPacket) == SIG_ERR) {
196 NETMGR_EXT_LOG_E("signal error!");
197 CloseRaSocket();
198 return;
199 }
200 itimerval setvalue = {};
201 setvalue.it_interval.tv_sec = SEND_RA_INTERVAL;
202 setvalue.it_value.tv_sec = SEND_RA_DELAY;
203 setitimer(ITIMER_REAL, &setvalue, nullptr);
204
205 sockaddr_in6 solicitor = {};
206 uint8_t solicitation[IPV6_MIN_MTU] = {};
207 socklen_t sendLen = sizeof(solicitation);
208 while (IsSocketValid() && !stopRaThread_) {
209 if (memset_s(solicitation, sizeof(solicitation), 0, sizeof(solicitation)) != EOK) {
210 break;
211 }
212 auto rval =
213 recvfrom(socket_, solicitation, IPV6_MIN_MTU, 0, reinterpret_cast<sockaddr *>(&solicitor), &sendLen);
214 if (rval <= 0 && errno != EAGAIN && errno != EINTR) {
215 NETMGR_EXT_LOG_E("recvfrom failed, rval[%{public}zd], errno[%{public}d]", rval, errno);
216 break;
217 }
218 if (solicitation[0] != ICMPV6_ND_ROUTER_SOLICIT_TYPE) {
219 continue;
220 }
221 if (AssembleRaLocked()) {
222 MaybeSendRa(solicitor);
223 }
224 }
225 CloseRaSocket();
226 }
227
GetDeprecatedRaParams(RaParams & oldRa,RaParams & newRa)228 RaParams RouterAdvertisementDaemon::GetDeprecatedRaParams(RaParams &oldRa, RaParams &newRa)
229 {
230 RaParams deprecateRa = {};
231 for (auto ipp : newRa.prefixes_) {
232 if (oldRa.ContainsPrefix(ipp)) {
233 deprecateRa.prefixes_.emplace_back(ipp);
234 }
235 }
236 for (auto dns : newRa.dnses_) {
237 if (oldRa.ContainsDns(dns)) {
238 deprecateRa.dnses_.emplace_back(dns);
239 }
240 }
241 return deprecateRa;
242 }
243
BuildNewRa(const RaParams & newRa)244 void RouterAdvertisementDaemon::BuildNewRa(const RaParams &newRa)
245 {
246 raParams_->Set(newRa);
247 }
248
ResetRaRetryInterval()249 void RouterAdvertisementDaemon::ResetRaRetryInterval()
250 {
251 if (sendRaTimes_ < MAX_URGENT_RTR_ADVERTISEMENTS) {
252 sendRaTimes_++;
253 return;
254 }
255 if (sendRaTimes_ == MAX_URGENT_RTR_ADVERTISEMENTS) {
256 itimerval setvalue = {};
257 itimerval oldvalue = {};
258 setvalue.it_interval.tv_sec = DEFAULT_RTR_INTERVAL_SEC;
259 setvalue.it_value.tv_sec = 1;
260 setitimer(ITIMER_REAL, &setvalue, &oldvalue);
261 sendRaTimes_++;
262 return;
263 }
264 }
265
AssembleRaLocked()266 bool RouterAdvertisementDaemon::AssembleRaLocked()
267 {
268 NETMGR_EXT_LOG_D("Generate Ra package start");
269 uint8_t raBuf[IPV6_MIN_MTU] = {};
270 uint8_t *ptr = raBuf;
271 uint16_t raHeadLen = PutRaHeader(ptr);
272 ptr += raHeadLen;
273 uint16_t raSllLen = PutRaSlla(ptr, raParams_->macAddr_);
274 ptr += raSllLen;
275 uint16_t raMtuLen = PutRaMtu(ptr, raParams_->mtu_);
276 ptr += raMtuLen;
277 uint16_t raPrefixLens = 0;
278 for (IpPrefix ipp : raParams_->prefixes_) {
279 uint16_t raPrefixLen = PutRaPio(ptr, ipp);
280 ptr += raPrefixLen;
281 raPrefixLens += raPrefixLen;
282 }
283 uint16_t raRdnsLen = PutRaRdnss(ptr);
284 ptr += raRdnsLen;
285 raPacketLength_ = raHeadLen + raSllLen + raMtuLen + raPrefixLens + raRdnsLen;
286 if (memset_s(&raPacket_, sizeof(raPacket_), 0, sizeof(raPacket_)) != EOK) {
287 return false;
288 }
289 if (memcpy_s(raPacket_, sizeof(raPacket_), raBuf, raPacketLength_) != EOK) {
290 return false;
291 }
292 NETMGR_EXT_LOG_D("Generate Ra package end, raPacketLength_: %{public}hu", raPacketLength_);
293 return true;
294 }
295
PutRaHeader(uint8_t * raBuf)296 uint16_t RouterAdvertisementDaemon::PutRaHeader(uint8_t *raBuf)
297 {
298 NETMGR_EXT_LOG_D("Append Ra header");
299 // https://datatracker.ietf.org/doc/html/rfc4861#section-4.2
300 // 0 1 2 3
301 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
302 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
303 // | Type | Code | Checksum |
304 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
305 // | Cur Hop Limit |M|O| Reserved | Router Lifetime |
306 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
307 // | Reachable Time |
308 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
309 // | Retrans Timer |
310 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
311 // | Options ...
312 // +-+-+-+-+-+-+-+-+-+-+-+-
313 Icmpv6HeadSt raHeadSt;
314 raHeadSt.type = ICMPV6_ND_ROUTER_ADVERT_TYPE;
315 raHeadSt.curHopLimit = DEFAULT_HOPLIMIT;
316 raHeadSt.flags = DEFAULT_ROUTER_PRE;
317 raHeadSt.routerLifetime = htons(DEFAULT_LIFETIME);
318 if (memcpy_s(raBuf, sizeof(Icmpv6HeadSt), &raHeadSt, sizeof(Icmpv6HeadSt)) != EOK) {
319 return 0;
320 }
321 return static_cast<uint16_t>(sizeof(Icmpv6HeadSt));
322 }
323
PutRaSlla(uint8_t * raBuf,const std::string & mac)324 uint16_t RouterAdvertisementDaemon::PutRaSlla(uint8_t *raBuf, const std::string &mac)
325 {
326 NETMGR_EXT_LOG_D("Append Ra source link lay address");
327 if (mac.size() < HW_MAC_STR_LENGTH) {
328 return 0;
329 }
330 // https://datatracker.ietf.org/doc/html/rfc4861#section-4.6.1
331 // 0 1 2 3
332 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
333 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
334 // | Type | Length | Link-Layer Address ...
335 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
336 Icmpv6SllOpt srcLinkAddrSt;
337 srcLinkAddrSt.type = ND_OPTION_SLLA_TYPE;
338 srcLinkAddrSt.len = sizeof(Icmpv6SllOpt) / UNITS_OF_OCTETS;
339 std::istringstream iss(mac);
340 int value = 0;
341 for (uint32_t i = 0; i < HW_MAC_LENGTH; i++) {
342 iss >> std::hex >> value;
343 srcLinkAddrSt.linkAddress[i] = static_cast<uint8_t>(value);
344 iss.ignore(1, ':');
345 }
346 if (memcpy_s(raBuf, sizeof(Icmpv6SllOpt), &srcLinkAddrSt, sizeof(Icmpv6SllOpt)) != EOK) {
347 return 0;
348 }
349 return static_cast<uint16_t>(sizeof(Icmpv6SllOpt));
350 }
351
PutRaMtu(uint8_t * raBuf,int32_t mtu)352 uint16_t RouterAdvertisementDaemon::PutRaMtu(uint8_t *raBuf, int32_t mtu)
353 {
354 NETMGR_EXT_LOG_D("Append Ra Mtu option");
355 // https://datatracker.ietf.org/doc/html/rfc4861#section-4.6.4
356 // 0 1 2 3
357 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
358 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
359 // | Type | Length | Reserved |
360 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
361 // | MTU |
362 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
363 Icmpv6MtuOpt mtuSt;
364 mtuSt.type = ND_OPTION_MTU_TYPE;
365 mtuSt.len = sizeof(Icmpv6MtuOpt) / UNITS_OF_OCTETS;
366 mtuSt.mtu = static_cast<uint32_t>(htonl(mtu));
367 if (memcpy_s(raBuf, sizeof(Icmpv6MtuOpt), &mtuSt, sizeof(Icmpv6MtuOpt)) != EOK) {
368 return 0;
369 }
370 return static_cast<uint16_t>(sizeof(Icmpv6MtuOpt));
371 }
372
PutRaPio(uint8_t * raBuf,IpPrefix & ipp)373 uint16_t RouterAdvertisementDaemon::PutRaPio(uint8_t *raBuf, IpPrefix &ipp)
374 {
375 NETMGR_EXT_LOG_D("Append Ra prefix information option");
376 // refer to https://tools.ietf.org/html/rfc4861#section-4.6.2
377 // 0 1 2 3
378 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
379 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
380 // | Type | Length | Prefix Length |L|A| Reserved1 |
381 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
382 // | Valid Lifetime |
383 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
384 // | Preferred Lifetime |
385 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
386 // | Reserved2 |
387 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
388 // | |
389 // + +
390 // | |
391 // + Prefix +
392 // | |
393 // + +
394 // | |
395 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
396 Icmpv6PrefixInfoOpt prefixInfoSt;
397 prefixInfoSt.type = ND_OPTION_PIO_TYPE;
398 prefixInfoSt.len = sizeof(Icmpv6PrefixInfoOpt) / UNITS_OF_OCTETS;
399 prefixInfoSt.prefixLen = ipp.prefixesLength;
400 prefixInfoSt.flag = PREFIX_INFO_FLAGS;
401 prefixInfoSt.validLifetime = htonl(DEFAULT_LIFETIME);
402 prefixInfoSt.prefLifetime = htonl(DEFAULT_LIFETIME);
403 prefixInfoSt.type = ND_OPTION_PIO_TYPE;
404 if (memcpy_s(prefixInfoSt.prefix, IPV6_ADDR_LEN, ipp.prefix.s6_addr, IPV6_ADDR_LEN) != EOK) {
405 return 0;
406 }
407 if (memcpy_s(raBuf, sizeof(Icmpv6PrefixInfoOpt), &prefixInfoSt, sizeof(Icmpv6PrefixInfoOpt)) != EOK) {
408 return 0;
409 }
410 return static_cast<uint16_t>(sizeof(Icmpv6PrefixInfoOpt));
411 }
412
PutRaRdnss(uint8_t * raBuf)413 uint16_t RouterAdvertisementDaemon::PutRaRdnss(uint8_t *raBuf)
414 {
415 NETMGR_EXT_LOG_D("Make RA DNS server option");
416 // https://datatracker.ietf.org/doc/rfc8106/
417 // 0 1 2 3
418 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
419 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
420 // | Type | Length | Reserved |
421 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
422 // | Lifetime |
423 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
424 // | |
425 // : Addresses of IPv6 Recursive DNS Servers :
426 // | |
427 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
428 Icmpv6RdnsOpt rdnsInfoSt;
429 size_t raRdnsNum = raParams_->prefixes_.size();
430 rdnsInfoSt.type = ND_OPTION_RDNSS_TYPE;
431 rdnsInfoSt.len = (sizeof(Icmpv6RdnsOpt) + raRdnsNum * IPV6_ADDR_LEN) / UNITS_OF_OCTETS;
432 rdnsInfoSt.lifetime = htonl(DEFAULT_LIFETIME);
433 if (memcpy_s(raBuf, sizeof(Icmpv6RdnsOpt), &rdnsInfoSt, sizeof(Icmpv6RdnsOpt)) != EOK) {
434 return 0;
435 }
436 raBuf += sizeof(Icmpv6RdnsOpt);
437 uint32_t index = 0;
438 for (IpPrefix ipp : raParams_->prefixes_) {
439 if (memcpy_s(raBuf + index * IPV6_ADDR_LEN, IPV6_ADDR_LEN, ipp.address.s6_addr, IPV6_ADDR_LEN) != EOK) {
440 return 0;
441 }
442 index++;
443 }
444 return static_cast<uint16_t>(sizeof(Icmpv6RdnsOpt) + raRdnsNum * IPV6_ADDR_LEN);
445 }
446 } // namespace NetManagerStandard
447 } // namespace OHOS
448