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 #include "clatd_packet_converter.h"
16 
17 #include <algorithm>
18 #include <climits>
19 #include <linux/if_tun.h>
20 #include <netinet/icmp6.h>
21 #include <netinet/in.h>
22 #include <netinet/ip.h>
23 #include <netinet/ip6.h>
24 #include <netinet/ip_icmp.h>
25 #include <netinet/tcp.h>
26 #include <netinet/udp.h>
27 #include <string>
28 #include <sys/uio.h>
29 #include <vector>
30 
31 #include "clat_constants.h"
32 #include "clat_utils.h"
33 #include "net_manager_constants.h"
34 #include "netnative_log_wrapper.h"
35 #include "securec.h"
36 
37 namespace OHOS {
38 namespace nmd {
39 using namespace OHOS::NetManagerStandard;
40 
ClatdPacketConverter(const uint8_t *inputPacket, size_t inputPacketSize, ClatdConvertType convertType, const in_addr &v4Addr, const in6_addr &v6Addr, const in6_addr &prefixAddr)41 ClatdPacketConverter::ClatdPacketConverter(const uint8_t *inputPacket, size_t inputPacketSize,
42                                            ClatdConvertType convertType, const in_addr &v4Addr, const in6_addr &v6Addr,
43                                            const in6_addr &prefixAddr)
44     : inputPacket_(inputPacket),
45       inputPacketSize_(inputPacketSize),
46       convertType_(convertType),
47       localV4Addr_(v4Addr),
48       localV6Addr_(v6Addr),
49       prefixAddr_(prefixAddr),
50       iovBufs_(CLATD_MAX),
51       iovBufLens_(CLATD_MAX)
52 {
53 }
54 
ConvertPacket(bool skip_csum)55 int32_t ClatdPacketConverter::ConvertPacket(bool skip_csum)
56 {
57     int32_t ret;
58     if (convertType_ == CONVERT_FROM_V4_TO_V6) {
59         ret = ConvertV4Packet(CLATD_IPHDR, inputPacket_, inputPacketSize_);
60         if (ret != NETMANAGER_SUCCESS) {
61             NETNATIVE_LOGW("fail to convert ipv4 packet");
62         }
63     } else if (convertType_ == CONVERT_FROM_V6_TO_V4) {
64         ret = ConvertV6Packet(CLATD_IPHDR, inputPacket_, inputPacketSize_);
65         if (ret != NETMANAGER_SUCCESS) {
66             NETNATIVE_LOGW("fail to convert ipv6 packet");
67         } else if (effectivePos_ > 0) {
68             WriteTunHeader(skip_csum);
69         }
70     } else {
71         NETNATIVE_LOGW("invalid convert type");
72         ret = NETMANAGER_ERR_INVALID_PARAMETER;
73     }
74     return ret;
75 }
76 
GetConvertedPacket(std::vector<iovec> &iovPackets, int &effectivePos)77 void ClatdPacketConverter::GetConvertedPacket(std::vector<iovec> &iovPackets, int &effectivePos)
78 {
79     for (size_t i = CLATD_TUNHDR; i < CLATD_MAX; i++) {
80         iovPackets[i].iov_base = iovBufs_[i].data();
81         iovPackets[i].iov_len = iovBufLens_[i];
82     }
83     effectivePos = effectivePos_;
84 }
85 
ConvertV4Packet(int pos, const uint8_t *inputPacket, size_t inputPacketSize)86 int32_t ClatdPacketConverter::ConvertV4Packet(int pos, const uint8_t *inputPacket, size_t inputPacketSize)
87 {
88     const iphdr *ipHeader = reinterpret_cast<const iphdr *>(inputPacket);
89     if (!IsV4PacketValid(ipHeader, inputPacketSize)) {
90         return NETMANAGER_ERR_INVALID_PARAMETER;
91     }
92 
93     // details about how to convert ip/icmp in RFC 6145
94     uint8_t v4TpProtocol = ipHeader->protocol;
95     uint8_t v6TpProtocol = v4TpProtocol;
96     if (v4TpProtocol == IPPROTO_ICMP) {
97         v6TpProtocol = IPPROTO_ICMPV6;
98     }
99 
100     ip6_hdr ip6Header;
101     WriteIpv6Header(&ip6Header, v6TpProtocol, ipHeader);
102     iovBufLens_[pos] = sizeof(ip6_hdr);
103 
104     ip6_frag ip6FragHeader;
105     size_t ip6FragHeaderLen = WriteFragHeader(&ip6FragHeader, &ip6Header, ipHeader);
106     iovBufLens_[pos + 1] = ip6FragHeaderLen;
107     iovBufs_[pos + 1].assign(reinterpret_cast<const char *>(&ip6FragHeader), iovBufLens_[pos + 1]);
108 
109     size_t tpLen = inputPacketSize - ipHeader->ihl * WORD_32BIT_IN_BYTE_UNIT;
110     const uint8_t *tpHeader = inputPacket + ipHeader->ihl * WORD_32BIT_IN_BYTE_UNIT;
111     if (ip6FragHeaderLen > 0 && (ip6FragHeader.ip6f_offlg & IP6F_OFF_MASK)) {
112         WritePayload(pos, tpHeader, tpLen);
113         ip6Header.ip6_plen = htons(GetIovPacketLength(pos));
114         iovBufs_[pos].assign(reinterpret_cast<const char *>(&ip6Header), iovBufLens_[pos]);
115         return NETMANAGER_SUCCESS;
116     }
117     return ConvertV4TpPacket(pos, ipHeader, &ip6Header, tpLen, v6TpProtocol);
118 }
119 
ConvertV4TpPacket(int pos, const iphdr *ipHeader, ip6_hdr *ip6Header, size_t tpLen, uint8_t v6TpProtocol)120 int32_t ClatdPacketConverter::ConvertV4TpPacket(int pos, const iphdr *ipHeader, ip6_hdr *ip6Header, size_t tpLen,
121                                                 uint8_t v6TpProtocol)
122 {
123     uint8_t v4TpProtocol = ipHeader->protocol;
124     uint32_t oldChecksum = CalV4PseudoHeaderChecksum(ipHeader, tpLen, v4TpProtocol);
125     uint32_t newChecksum = CalV6PseudoHeaderChecksum(ip6Header, tpLen, v6TpProtocol);
126     const uint8_t *tpHeader = reinterpret_cast<const uint8_t *>(ipHeader) + ipHeader->ihl * WORD_32BIT_IN_BYTE_UNIT;
127     int32_t ret;
128     switch (v6TpProtocol) {
129         case IPPROTO_ICMPV6:
130             ret = ConvertIcmpPacket(pos + IP_TP_PACKET_POSITION_DELTA, reinterpret_cast<const icmphdr *>(tpHeader),
131                                     newChecksum, tpLen);
132             break;
133         case IPPROTO_TCP:
134             ret = ConvertTcpPacket(pos + IP_TP_PACKET_POSITION_DELTA, reinterpret_cast<const tcphdr *>(tpHeader),
135                                    oldChecksum, newChecksum, tpLen);
136             break;
137         case IPPROTO_UDP:
138             ret = ConvertUdpPacket(pos + IP_TP_PACKET_POSITION_DELTA, reinterpret_cast<const udphdr *>(tpHeader),
139                                    oldChecksum, newChecksum, tpLen);
140             break;
141         case IPPROTO_GRE:
142         case IPPROTO_ESP:
143             WritePayload(pos, tpHeader, tpLen);
144             ret = NETMANAGER_SUCCESS;
145             break;
146         default:
147             NETNATIVE_LOGW("unknown transport protocol");
148             ret = NETMANAGER_ERR_INVALID_PARAMETER;
149     }
150     ip6Header->ip6_plen = htons(GetIovPacketLength(pos));
151     iovBufs_[pos].assign(reinterpret_cast<const char *>(ip6Header), iovBufLens_[pos]);
152     return ret;
153 }
154 
ConvertV6Packet(int pos, const uint8_t *inputPacket, size_t inputPacketSize)155 int32_t ClatdPacketConverter::ConvertV6Packet(int pos, const uint8_t *inputPacket, size_t inputPacketSize)
156 {
157     const ip6_hdr *ip6Header = reinterpret_cast<const ip6_hdr *>(inputPacket);
158     if (!IsV6PacketValid(ip6Header, inputPacketSize)) {
159         return NETMANAGER_ERR_INVALID_PARAMETER;
160     }
161 
162     // details about how to convert ip/icmp in RFC 6145
163     uint8_t v6TpProtocol = ip6Header->ip6_nxt;
164     uint8_t v4TpProtocol = v6TpProtocol == IPPROTO_ICMPV6 ? IPPROTO_ICMP : v6TpProtocol;
165 
166     iphdr ipHeader;
167     WriteIpv4Header(&ipHeader, v4TpProtocol, ip6Header);
168     iovBufLens_[pos] = sizeof(iphdr);
169 
170     size_t tpLen = inputPacketSize - sizeof(ip6_hdr);
171     const uint8_t *tpHeader = inputPacket + sizeof(ip6_hdr);
172 
173     const ip6_frag *ip6FragHeader = nullptr;
174     if (v6TpProtocol == IPPROTO_FRAGMENT) {
175         ip6FragHeader = reinterpret_cast<const ip6_frag *>(tpHeader);
176         // LCOV_EXCL_START
177         if (tpLen < sizeof(*ip6FragHeader)) {
178             NETNATIVE_LOGW("fail to convert ipv6 packet, fragment packet size is too small");
179             effectivePos_ = 0;
180             return NETMANAGER_ERR_INVALID_PARAMETER;
181         }
182         // LCOV_EXCL_STOP
183         tpHeader += sizeof(*ip6FragHeader);
184         tpLen -= sizeof(*ip6FragHeader);
185 
186         ProcessFragHeader(ip6FragHeader, &ipHeader, v6TpProtocol, v4TpProtocol);
187     }
188 
189     if (ip6FragHeader != nullptr && (ip6FragHeader->ip6f_offlg & IP6F_OFF_MASK)) {
190         WritePayload(pos, tpHeader, tpLen);
191         ipHeader.tot_len = htons(ntohs(ipHeader.tot_len) + GetIovPacketLength(pos));
192         ipHeader.check = CalChecksum(&ipHeader, sizeof(iphdr));
193         iovBufs_[pos].assign(reinterpret_cast<const char *>(&ipHeader), iovBufLens_[pos]);
194         return NETMANAGER_SUCCESS;
195     }
196 
197     return ConvertV6TpPacket(pos, ip6Header, &ipHeader, tpLen, v4TpProtocol);
198 }
199 
ConvertV6TpPacket(int pos, const ip6_hdr *ip6Header, iphdr *ipHeader, size_t tpLen, uint8_t v4TpProtocol)200 int32_t ClatdPacketConverter::ConvertV6TpPacket(int pos, const ip6_hdr *ip6Header, iphdr *ipHeader, size_t tpLen,
201                                                 uint8_t v4TpProtocol)
202 {
203     uint8_t v6TpProtocol = ip6Header->ip6_nxt;
204     uint32_t oldChecksum = CalV6PseudoHeaderChecksum(ip6Header, tpLen, v6TpProtocol);
205     uint32_t newChecksum = CalV4PseudoHeaderChecksum(ipHeader, tpLen, v4TpProtocol);
206     const uint8_t *tpHeader = reinterpret_cast<const uint8_t *>(ip6Header) + sizeof(ip6_hdr);
207     int32_t ret;
208     switch (v4TpProtocol) {
209         case IPPROTO_ICMP:
210             ret = ConvertIcmpv6Packet(pos + IP_TP_PACKET_POSITION_DELTA, reinterpret_cast<const icmp6_hdr *>(tpHeader),
211                                       tpLen);
212             break;
213         case IPPROTO_TCP:
214             ret = ConvertTcpPacket(pos + IP_TP_PACKET_POSITION_DELTA, reinterpret_cast<const tcphdr *>(tpHeader),
215                                    oldChecksum, newChecksum, tpLen);
216             break;
217         case IPPROTO_UDP:
218             ret = ConvertUdpPacket(pos + IP_TP_PACKET_POSITION_DELTA, reinterpret_cast<const udphdr *>(tpHeader),
219                                    oldChecksum, newChecksum, tpLen);
220             break;
221         case IPPROTO_GRE:
222         case IPPROTO_ESP:
223             WritePayload(pos, tpHeader, tpLen);
224             ret = NETMANAGER_SUCCESS;
225             break;
226         default:
227             NETNATIVE_LOGW("unknown transport protocol");
228             ret = NETMANAGER_ERR_INVALID_PARAMETER;
229     }
230     ipHeader->tot_len = htons(ntohs(ipHeader->tot_len) + GetIovPacketLength(pos));
231     ipHeader->check = CalChecksum(ipHeader, sizeof(iphdr));
232     iovBufs_[pos].assign(reinterpret_cast<const char *>(ipHeader), iovBufLens_[pos]);
233     return ret;
234 }
235 
IsV4PacketValid(const iphdr *ipHeader, size_t packetSize)236 bool ClatdPacketConverter::IsV4PacketValid(const iphdr *ipHeader, size_t packetSize)
237 {
238     if (packetSize < sizeof(iphdr)) {
239         NETNATIVE_LOGW("Invalid ipv4 packet, input packet size too small");
240         return false;
241     }
242     if (ipHeader->ihl * WORD_32BIT_IN_BYTE_UNIT < IPV4_HDR_MIN_LEN) {
243         NETNATIVE_LOGW("Invalid ipv4 packet, ip header length %{public}u smaller than 5", ipHeader->ihl);
244         return false;
245     }
246     if (static_cast<size_t>(ipHeader->ihl * WORD_32BIT_IN_BYTE_UNIT) > packetSize) {
247         NETNATIVE_LOGW("Invalid ipv4 packet, ip header length %{public}u larger than entire packet", ipHeader->ihl);
248         return false;
249     }
250     if (ipHeader->version != IPVERSION) {
251         NETNATIVE_LOGW("Invalid ipv4 packet, version %{public}u not 4", ipHeader->version);
252         return false;
253     }
254     return true;
255 }
256 
IsV6PacketValid(const ip6_hdr *ip6Header, size_t packetSize)257 bool ClatdPacketConverter::IsV6PacketValid(const ip6_hdr *ip6Header, size_t packetSize)
258 {
259     if (packetSize < sizeof(ip6_hdr)) {
260         NETNATIVE_LOGW("Invalid ipv6 packet, input packet size too small");
261         return false;
262     }
263 
264     if (IN6_IS_ADDR_MULTICAST(&ip6Header->ip6_dst)) {
265         NETNATIVE_LOGW("Invalid ipv6 packet, destination address is multicast");
266         return false;
267     }
268 
269     if (!(std::equal(prefixAddr_.s6_addr, prefixAddr_.s6_addr + CLAT_PREFIX_BYTE_LEN, ip6Header->ip6_src.s6_addr) &&
270           IN6_ARE_ADDR_EQUAL(&ip6Header->ip6_dst, &localV6Addr_)) &&
271         !(std::equal(prefixAddr_.s6_addr, prefixAddr_.s6_addr + CLAT_PREFIX_BYTE_LEN, ip6Header->ip6_dst.s6_addr) &&
272           IN6_ARE_ADDR_EQUAL(&ip6Header->ip6_src, &localV6Addr_)) &&
273         ip6Header->ip6_nxt != IPPROTO_ICMPV6) {
274         NETNATIVE_LOGW("Invalid ipv6 packet, unknown source/destination address");
275         return false;
276     }
277 
278     return true;
279 }
280 
WriteIpv6Header(ip6_hdr *ip6Header, uint8_t tpProtocol, const iphdr *ipHeader)281 void ClatdPacketConverter::WriteIpv6Header(ip6_hdr *ip6Header, uint8_t tpProtocol, const iphdr *ipHeader)
282 {
283     ip6Header->ip6_vfc = IPV6_VERSION_FLAG;
284     ip6Header->ip6_plen = 0;
285     ip6Header->ip6_nxt = tpProtocol;
286     ip6Header->ip6_hlim = ipHeader->ttl;
287 
288     ConvertV4Address(ipHeader->saddr, ip6Header->ip6_src);
289     ConvertV4Address(ipHeader->daddr, ip6Header->ip6_dst);
290 }
291 
WriteIpv4Header(iphdr *ipHeader, uint8_t tpProtocol, const ip6_hdr *ip6Header)292 void ClatdPacketConverter::WriteIpv4Header(iphdr *ipHeader, uint8_t tpProtocol, const ip6_hdr *ip6Header)
293 {
294     ipHeader->ihl = (IPV4_HDR_MIN_LEN / WORD_32BIT_IN_BYTE_UNIT);
295     ipHeader->version = IPVERSION;
296     ipHeader->tos = 0;
297     ipHeader->tot_len = htons(sizeof(iphdr));
298     ipHeader->id = 0;
299     ipHeader->frag_off = htons(IP_DF);
300     ipHeader->ttl = ip6Header->ip6_hlim;
301     ipHeader->protocol = tpProtocol;
302     ipHeader->check = 0;
303 
304     ConvertV6Address(ip6Header->ip6_src, ipHeader->saddr);
305     ConvertV6Address(ip6Header->ip6_dst, ipHeader->daddr);
306 
307     if (static_cast<uint32_t>(ipHeader->saddr) == INADDR_NONE) {
308         ipHeader->saddr = htonl((0xffffff << CHAR_BIT) + ip6Header->ip6_hlim);
309     }
310 }
311 
ConvertV4Address(uint32_t v4Addr, in6_addr &v6Addr)312 void ClatdPacketConverter::ConvertV4Address(uint32_t v4Addr, in6_addr &v6Addr)
313 {
314     if (v4Addr == localV4Addr_.s_addr) {
315         v6Addr = localV6Addr_;
316     } else {
317         v6Addr = prefixAddr_;
318         v6Addr.s6_addr32[CLAT_SUFFIX_OFFSET_IN_32] = v4Addr;
319     }
320 }
321 
ConvertV6Address(const in6_addr &v6Addr, uint32_t &v4Addr)322 void ClatdPacketConverter::ConvertV6Address(const in6_addr &v6Addr, uint32_t &v4Addr)
323 {
324     if (std::equal(prefixAddr_.s6_addr, prefixAddr_.s6_addr + CLAT_PREFIX_BYTE_LEN, v6Addr.s6_addr)) {
325         v4Addr = v6Addr.s6_addr32[CLAT_SUFFIX_OFFSET_IN_32];
326     } else if (IN6_ARE_ADDR_EQUAL(&v6Addr, &localV6Addr_)) {
327         v4Addr = localV4Addr_.s_addr;
328     } else {
329         v4Addr = INADDR_NONE;
330     }
331 }
332 
WriteFragHeader(ip6_frag *ip6FragHeader, ip6_hdr *ip6Header, const iphdr *ipHeader)333 size_t ClatdPacketConverter::WriteFragHeader(ip6_frag *ip6FragHeader, ip6_hdr *ip6Header, const iphdr *ipHeader)
334 {
335     uint16_t fragValue = ntohs(ipHeader->frag_off);
336     uint16_t fragOffset = fragValue & IP_OFFMASK;
337     if (fragOffset == 0 && (fragValue & IP_MF) == 0) {
338         return 0;
339     }
340 
341     ip6FragHeader->ip6f_nxt = ip6Header->ip6_nxt;
342     ip6FragHeader->ip6f_reserved = 0;
343     ip6FragHeader->ip6f_offlg = htons(fragOffset << IPV6_FRAG_OFFSET_BIT_SUPPLEMENTARY);
344     if (fragValue & IP_MF) {
345         ip6FragHeader->ip6f_offlg |= IP6F_MORE_FRAG;
346     }
347     ip6FragHeader->ip6f_ident = htonl(ntohs(ipHeader->id));
348     ip6Header->ip6_nxt = IPPROTO_FRAGMENT;
349 
350     return sizeof(*ip6FragHeader);
351 }
352 
ProcessFragHeader(const ip6_frag *ip6FragHeader, iphdr *ipHeader, uint8_t &v6TpProtocol, uint8_t &v4TpProtocol)353 void ClatdPacketConverter::ProcessFragHeader(const ip6_frag *ip6FragHeader, iphdr *ipHeader, uint8_t &v6TpProtocol,
354                                              uint8_t &v4TpProtocol)
355 {
356     uint16_t fragOffset = ntohs(ip6FragHeader->ip6f_offlg & IP6F_OFF_MASK) >> IPV6_FRAG_OFFSET_BIT_SUPPLEMENTARY;
357     if (ip6FragHeader->ip6f_offlg & IP6F_MORE_FRAG) {
358         fragOffset |= IP_MF;
359     }
360     ipHeader->frag_off = htons(fragOffset);
361     ipHeader->id = htons(ntohl(ip6FragHeader->ip6f_ident) & 0xffff);
362 
363     v6TpProtocol = ip6FragHeader->ip6f_nxt;
364     v4TpProtocol = v6TpProtocol == IPPROTO_ICMPV6 ? IPPROTO_ICMP : v6TpProtocol;
365     ipHeader->protocol = v4TpProtocol;
366 }
367 
CalV4PseudoHeaderChecksum(const iphdr *ipHeader, uint16_t tpLen, uint8_t tpProtocol)368 uint32_t ClatdPacketConverter::CalV4PseudoHeaderChecksum(const iphdr *ipHeader, uint16_t tpLen, uint8_t tpProtocol)
369 {
370     uint16_t len = htons(tpLen);
371     uint16_t protocol = htons(tpProtocol);
372     uint32_t sum = 0;
373 
374     sum = AddChecksum(sum, &(ipHeader->saddr), sizeof(uint32_t));
375     sum = AddChecksum(sum, &(ipHeader->daddr), sizeof(uint32_t));
376     sum = AddChecksum(sum, &len, sizeof(uint16_t));
377     sum = AddChecksum(sum, &protocol, sizeof(uint16_t));
378     return sum;
379 }
380 
CalV6PseudoHeaderChecksum(const ip6_hdr *ip6Header, uint32_t tpLen, uint8_t tpProtocol)381 uint32_t ClatdPacketConverter::CalV6PseudoHeaderChecksum(const ip6_hdr *ip6Header, uint32_t tpLen, uint8_t tpProtocol)
382 {
383     uint32_t len = htonl(tpLen);
384     uint32_t protocol = htonl(tpProtocol);
385     uint32_t sum = 0;
386 
387     sum = AddChecksum(sum, &(ip6Header->ip6_src), sizeof(in6_addr));
388     sum = AddChecksum(sum, &(ip6Header->ip6_dst), sizeof(in6_addr));
389     sum = AddChecksum(sum, &len, sizeof(uint32_t));
390     sum = AddChecksum(sum, &protocol, sizeof(uint32_t));
391     return sum;
392 }
393 
GetIovPacketLength(int pos)394 uint16_t ClatdPacketConverter::GetIovPacketLength(int pos)
395 {
396     size_t sum = 0;
397     for (size_t i = pos + 1; i < static_cast<int>(CLATD_MAX); i++) {
398         sum += iovBufLens_[i];
399     }
400     return sum;
401 }
402 
ConvertIcmpPacket(int pos, const icmphdr *icmpHeader, uint32_t checksum, size_t tpLen)403 int32_t ClatdPacketConverter::ConvertIcmpPacket(int pos, const icmphdr *icmpHeader, uint32_t checksum, size_t tpLen)
404 {
405     if (tpLen < sizeof(icmphdr)) {
406         NETNATIVE_LOGW("fail to convert icmp packet, packet length is too small");
407         effectivePos_ = 0;
408         return NETMANAGER_ERR_INVALID_PARAMETER;
409     }
410 
411     icmp6_hdr icmp6Header;
412 
413     ConvertIcmpTypeAndCode(icmpHeader->type, icmpHeader->code, icmp6Header.icmp6_type, icmp6Header.icmp6_code);
414 
415     iovBufLens_[pos] = sizeof(icmp6_hdr);
416 
417     const uint8_t *payload = reinterpret_cast<const uint8_t *>(icmpHeader + 1);
418     size_t payloadLen = tpLen - sizeof(icmphdr);
419 
420     int32_t ret;
421     if (pos == static_cast<int>(CLATD_TPHDR) &&
422         (icmp6Header.icmp6_type == ICMP6_DST_UNREACH || icmp6Header.icmp6_type == ICMP6_TIME_EXCEEDED)) {
423         ret = ConvertV4Packet(pos + 1, payload, payloadLen);
424 
425         checksum = checksum + htons(IPV6_HDR_LEN - IPV4_HDR_MIN_LEN);
426     } else if (icmp6Header.icmp6_type == ICMP6_ECHO_REQUEST || icmp6Header.icmp6_type == ICMP6_ECHO_REPLY) {
427         // Ping packet.
428         icmp6Header.icmp6_id = icmpHeader->un.echo.id;
429         icmp6Header.icmp6_seq = icmpHeader->un.echo.sequence;
430         iovBufs_[CLATD_PAYLOAD].assign(reinterpret_cast<const char *>(payload), payloadLen);
431         iovBufLens_[CLATD_PAYLOAD] = payloadLen;
432         effectivePos_ = CLATD_PAYLOAD + 1;
433         ret = NETMANAGER_SUCCESS;
434     } else {
435         effectivePos_ = 0;
436         ret = NETMANAGER_ERR_INVALID_PARAMETER;
437     }
438 
439     icmp6Header.icmp6_cksum = 0;
440     iovBufs_[pos].assign(reinterpret_cast<const char *>(&icmp6Header), iovBufLens_[pos]);
441     icmp6Header.icmp6_cksum = CalIovPacketChecksum(checksum, pos);
442     iovBufs_[pos].assign(reinterpret_cast<const char *>(&icmp6Header), iovBufLens_[pos]);
443     return ret;
444 }
445 
ConvertIcmpTypeAndCode(const uint8_t &icmpType, const uint8_t &icmpCode, uint8_t &icmp6Type, uint8_t &icmp6Code)446 void ClatdPacketConverter::ConvertIcmpTypeAndCode(const uint8_t &icmpType, const uint8_t &icmpCode, uint8_t &icmp6Type,
447                                                   uint8_t &icmp6Code)
448 {
449     switch (icmpType) {
450         case ICMP_ECHO:
451             icmp6Type = ICMP6_ECHO_REQUEST;
452             icmp6Code = icmpCode;
453             break;
454         case ICMP_ECHOREPLY:
455             icmp6Type = ICMP6_ECHO_REPLY;
456             icmp6Code = icmpCode;
457             break;
458         case ICMP_TIME_EXCEEDED:
459             icmp6Type = ICMP6_TIME_EXCEEDED;
460             icmp6Code = icmpCode;
461             break;
462         case ICMP_DEST_UNREACH:
463             switch (icmpCode) {
464                 case ICMP_UNREACH_NET:
465                 case ICMP_UNREACH_HOST:
466                 case ICMP_UNREACH_SRCFAIL:
467                 case ICMP_UNREACH_NET_UNKNOWN:
468                 case ICMP_UNREACH_HOST_UNKNOWN:
469                 case ICMP_UNREACH_ISOLATED:
470                 case ICMP_UNREACH_TOSNET:
471                 case ICMP_UNREACH_TOSHOST:
472                     icmp6Type = ICMP6_DST_UNREACH;
473                     icmp6Code = ICMP6_DST_UNREACH_NOROUTE;
474                     break;
475                 case ICMP_UNREACH_PORT:
476                     icmp6Type = ICMP6_DST_UNREACH;
477                     icmp6Code = ICMP6_DST_UNREACH_NOPORT;
478                     break;
479                 case ICMP_UNREACH_NET_PROHIB:
480                 case ICMP_UNREACH_HOST_PROHIB:
481                 case ICMP_UNREACH_FILTER_PROHIB:
482                 case ICMP_UNREACH_PRECEDENCE_CUTOFF:
483                     icmp6Type = ICMP6_DST_UNREACH;
484                     icmp6Code = ICMP6_DST_UNREACH_ADMIN;
485                     break;
486                 default:
487                     icmp6Type = ICMP6_PARAM_PROB;
488                     NETNATIVE_LOGW("fail to convert icmp packet type %{public}d code %{public}d", icmpType, icmpCode);
489                     break;
490             }
491             break;
492         default:
493             NETNATIVE_LOGW("fail to convert icmp packet type %{public}d", icmpType);
494             icmp6Type = ICMP6_PARAM_PROB;
495     }
496 }
497 
ConvertIcmpv6Packet(int pos, const icmp6_hdr *icmp6Header, size_t tpLen)498 int32_t ClatdPacketConverter::ConvertIcmpv6Packet(int pos, const icmp6_hdr *icmp6Header, size_t tpLen)
499 {
500     if (tpLen < sizeof(icmp6_hdr)) {
501         NETNATIVE_LOGW("fail to convert icmp6 packet, packet length is too small");
502         effectivePos_ = 0;
503         return NETMANAGER_ERR_INVALID_PARAMETER;
504     }
505 
506     icmphdr icmpHeader;
507     ConvertIcmpV6TypeAndCode(icmp6Header->icmp6_type, icmp6Header->icmp6_code, icmpHeader.type, icmpHeader.code);
508     iovBufLens_[pos] = sizeof(icmphdr);
509 
510     const uint8_t *payload = reinterpret_cast<const uint8_t *>(icmp6Header + 1);
511     size_t payloadLen = tpLen - sizeof(icmp6_hdr);
512     int32_t ret;
513     if (pos == CLATD_TPHDR && icmp6Header->icmp6_type < ICMP6_ECHO_REQUEST && icmpHeader.type != ICMP_PARAMETERPROB) {
514         ret = ConvertV6Packet(pos + 1, payload, payloadLen);
515     } else if (icmpHeader.type == ICMP_ECHO || icmpHeader.type == ICMP_ECHOREPLY) {
516         // Ping packet.
517         icmpHeader.un.echo.id = icmp6Header->icmp6_id;
518         icmpHeader.un.echo.sequence = icmp6Header->icmp6_seq;
519         iovBufs_[CLATD_PAYLOAD].assign(reinterpret_cast<const char *>(payload), payloadLen);
520         iovBufLens_[CLATD_PAYLOAD] = payloadLen;
521         effectivePos_ = CLATD_PAYLOAD + 1;
522         ret = NETMANAGER_SUCCESS;
523     } else {
524         effectivePos_ = 0;
525         ret = NETMANAGER_ERR_INVALID_PARAMETER;
526     }
527 
528     icmpHeader.checksum = 0;
529     iovBufs_[pos].assign(reinterpret_cast<const char *>(&icmpHeader), iovBufLens_[pos]);
530     icmpHeader.checksum = CalIovPacketChecksum(0, pos);
531     iovBufs_[pos].assign(reinterpret_cast<const char *>(&icmpHeader), iovBufLens_[pos]);
532 
533     return ret;
534 }
535 
ConvertIcmpV6TypeAndCode(const uint8_t &icmp6Type, const uint8_t &icmp6Code, uint8_t &icmpType, uint8_t &icmpCode)536 void ClatdPacketConverter::ConvertIcmpV6TypeAndCode(const uint8_t &icmp6Type, const uint8_t &icmp6Code,
537                                                     uint8_t &icmpType, uint8_t &icmpCode)
538 {
539     switch (icmp6Type) {
540         case ICMP6_ECHO_REQUEST:
541             icmpType = ICMP_ECHO;
542             icmpCode = icmp6Code;
543             break;
544         case ICMP6_ECHO_REPLY:
545             icmpType = ICMP_ECHOREPLY;
546             icmpCode = icmp6Code;
547             break;
548         case ICMP6_TIME_EXCEEDED:
549             icmpType = ICMP_TIME_EXCEEDED;
550             icmpCode = icmp6Code;
551             break;
552         case ICMP6_DST_UNREACH:
553             switch (icmp6Code) {
554                 case ICMP6_DST_UNREACH_NOROUTE:
555                 case ICMP6_DST_UNREACH_BEYONDSCOPE:
556                 case ICMP6_DST_UNREACH_ADDR:
557                     icmpType = ICMP_DEST_UNREACH;
558                     icmpCode = ICMP_UNREACH_HOST;
559                     break;
560 
561                 case ICMP6_DST_UNREACH_ADMIN:
562                     icmpType = ICMP_DEST_UNREACH;
563                     icmpCode = ICMP_UNREACH_HOST_PROHIB;
564                     break;
565 
566                 case ICMP6_DST_UNREACH_NOPORT:
567                     icmpType = ICMP_DEST_UNREACH;
568                     icmpCode = ICMP_UNREACH_PORT;
569                     break;
570                 default:
571                     NETNATIVE_LOGW("fail to convert icmpv6 packet type %{public}d", icmp6Type);
572                     icmpType = ICMP_PARAMETERPROB;
573             }
574             break;
575         default:
576             NETNATIVE_LOGW("fail to convert icmpv6 packet type %{public}d", icmp6Type);
577             icmpType = ICMP_PARAMETERPROB;
578     }
579 }
580 
CalIovPacketChecksum(uint32_t sum, int pos)581 uint16_t ClatdPacketConverter::CalIovPacketChecksum(uint32_t sum, int pos)
582 {
583     for (size_t i = pos; i < CLATD_MAX; i++) {
584         if (iovBufLens_[i] > 0) {
585             sum = AddChecksum(sum, iovBufs_[i].data(), iovBufLens_[i]);
586         }
587     }
588     return ~Checksum32To16(sum);
589 }
590 
ConvertTcpPacket(int pos, const tcphdr *tcpHeader, uint32_t oldChecksum, uint32_t newChecksum, size_t tpLen)591 int32_t ClatdPacketConverter::ConvertTcpPacket(int pos, const tcphdr *tcpHeader, uint32_t oldChecksum,
592                                                uint32_t newChecksum, size_t tpLen)
593 {
594     if (!IsTcpPacketValid(tcpHeader, tpLen)) {
595         return NETMANAGER_ERR_INVALID_PARAMETER;
596     }
597 
598     size_t tcpHdrLen = tcpHeader->doff * WORD_32BIT_IN_BYTE_UNIT;
599     iovBufLens_[pos] = tcpHdrLen;
600 
601     char tcpHdrBuf[TCP_HDR_MAX_LEN];
602     // LCOV_EXCL_START
603     if (memcpy_s(tcpHdrBuf, TCP_HDR_MAX_LEN, tcpHeader, tcpHdrLen) != EOK) {
604         return NETMANAGER_ERR_OPERATION_FAILED;
605     }
606     // LCOV_EXCL_STOP
607     tcphdr *tcpHeaderOut = reinterpret_cast<tcphdr *>(tcpHdrBuf);
608 
609     iovBufs_[CLATD_PAYLOAD].assign(reinterpret_cast<const char *>(tcpHeader) + tcpHdrLen, tpLen - tcpHdrLen);
610     iovBufLens_[CLATD_PAYLOAD] = tpLen - tcpHdrLen;
611     tcpHeaderOut->check = AdjustChecksum(tcpHeader->check, oldChecksum, newChecksum);
612     iovBufs_[pos].assign(reinterpret_cast<const char *>(tcpHdrBuf), tcpHdrLen);
613     effectivePos_ = CLATD_PAYLOAD + 1;
614     return NETMANAGER_SUCCESS;
615 }
616 
IsTcpPacketValid(const tcphdr *tcpHeader, size_t packetSize)617 bool ClatdPacketConverter::IsTcpPacketValid(const tcphdr *tcpHeader, size_t packetSize)
618 {
619     if (packetSize < sizeof(tcphdr)) {
620         NETNATIVE_LOGW("Invalid tcp packet, packet length is too small");
621         effectivePos_ = 0;
622         return false;
623     }
624 
625     if (tcpHeader->doff * WORD_32BIT_IN_BYTE_UNIT < TCP_HDR_MIN_LEN) {
626         NETNATIVE_LOGW("Invalid tcp packet, tcp header length %{public}u smaller than 5", tcpHeader->doff);
627         effectivePos_ = 0;
628         return false;
629     }
630 
631     if (static_cast<size_t>(tcpHeader->doff * WORD_32BIT_IN_BYTE_UNIT) > packetSize) {
632         NETNATIVE_LOGW("Invalid tcp packet, tcp header length %{public}u larger than entire packet", tcpHeader->doff);
633         effectivePos_ = 0;
634         return false;
635     }
636     // LCOV_EXCL_START
637     if (tcpHeader->doff * WORD_32BIT_IN_BYTE_UNIT > TCP_HDR_MAX_LEN) {
638         NETNATIVE_LOGW("Invalid tcp packet, tcp header length %{public}u larger than MAX_TCP_HDR", tcpHeader->doff);
639         effectivePos_ = 0;
640         return false;
641     }
642     // LCOV_EXCL_STOP
643     return true;
644 }
645 
ConvertUdpPacket(int pos, const udphdr *udpHeader, uint32_t oldChecksum, uint32_t newChecksum, size_t tpLen)646 int32_t ClatdPacketConverter::ConvertUdpPacket(int pos, const udphdr *udpHeader, uint32_t oldChecksum,
647                                                uint32_t newChecksum, size_t tpLen)
648 {
649     if (tpLen < sizeof(udphdr)) {
650         NETNATIVE_LOGW("Invalid udp packet, packet length is too small");
651         effectivePos_ = 0;
652         return NETMANAGER_ERR_INVALID_PARAMETER;
653     }
654 
655     iovBufLens_[pos] = sizeof(udphdr);
656 
657     iovBufLens_[CLATD_PAYLOAD] = tpLen - sizeof(udphdr);
658     iovBufs_[CLATD_PAYLOAD].assign(reinterpret_cast<const char *>(udpHeader + 1), tpLen - sizeof(udphdr));
659 
660     udphdr udpHeaderOut = *udpHeader;
661     // details about zero checksum in RFC 768
662     if (udpHeaderOut.check == 0) {
663         iovBufs_[pos].assign(reinterpret_cast<const char *>(&udpHeaderOut), sizeof(udphdr));
664         udpHeaderOut.check = CalIovPacketChecksum(newChecksum, pos);
665     } else {
666         udpHeaderOut.check = AdjustChecksum(udpHeader->check, oldChecksum, newChecksum);
667     }
668 
669     // LCOV_EXCL_START
670     if (udpHeaderOut.check == 0) {
671         udpHeaderOut.check = 0xffff;
672     }
673     // LCOV_EXCL_STOP
674     iovBufs_[pos].assign(reinterpret_cast<const char *>(&udpHeaderOut), sizeof(udphdr));
675     effectivePos_ = CLATD_PAYLOAD + 1;
676     return NETMANAGER_SUCCESS;
677 }
678 
WritePayload(int pos, const uint8_t *tpHeader, size_t tpLen)679 void ClatdPacketConverter::WritePayload(int pos, const uint8_t *tpHeader, size_t tpLen)
680 {
681     iovBufLens_[pos + IP_TP_PACKET_POSITION_DELTA] = 0;
682     iovBufs_[CLATD_PAYLOAD].assign(reinterpret_cast<const char *>(tpHeader), tpLen);
683     iovBufLens_[CLATD_PAYLOAD] = tpLen;
684     effectivePos_ = CLATD_MAX;
685 }
686 
WriteTunHeader(bool skip_csum)687 void ClatdPacketConverter::WriteTunHeader(bool skip_csum)
688 {
689     tun_pi tunProtocolInfo;
690     if (skip_csum) {
691         tunProtocolInfo.flags = htons(TP_CSUM_UNNECESSARY);
692     } else {
693         tunProtocolInfo.flags = 0;
694     }
695     tunProtocolInfo.proto = htons(ETH_P_IP);
696     iovBufLens_[CLATD_TUNHDR] = sizeof(tun_pi);
697     iovBufs_[CLATD_TUNHDR].assign(reinterpret_cast<const char *>(&tunProtocolInfo), sizeof(tun_pi));
698 }
699 
700 } // namespace nmd
701 } // namespace OHOS