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