1 /*
2 * Copyright (c) 2023 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 "vpn_interface.h"
17
18 #include <cerrno>
19 #include <fcntl.h>
20 #include <sys/socket.h>
21 #include <sys/types.h>
22 #include <sys/un.h>
23 #include <unistd.h>
24
25 #include "net_manager_constants.h"
26 #include "netmgr_ext_log_wrapper.h"
27 #include "securec.h"
28
29 namespace OHOS {
30 namespace NetManagerStandard {
31
32 namespace {
33 static const sockaddr_un SERVER_PATH = {AF_UNIX, "/dev/unix/socket/tunfd"};
34 constexpr int32_t CONNECT_TIMEOUT = 1;
35 constexpr int32_t INVALID_FD = -1;
36 } // namespace
37
ConnectControl(int32_t sockfd,int32_t nsec)38 int32_t VpnInterface::ConnectControl(int32_t sockfd, int32_t nsec)
39 {
40 uint32_t flags = static_cast<uint32_t>(fcntl(sockfd, F_GETFL, 0));
41 fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
42
43 /* EINPROGRESS - Indicates that the connection establishment has been started but is not complete */
44 int32_t ret = connect(sockfd, reinterpret_cast<const sockaddr *>(&SERVER_PATH), sizeof(SERVER_PATH));
45 if ((ret < 0) && (errno != EINPROGRESS)) {
46 NETMGR_EXT_LOG_E("connect error: %{public}d", errno);
47 return NETMANAGER_EXT_ERR_INTERNAL;
48 } else if (ret == 0) {
49 /* connect completed immediately, This can happen when the server is on the client's host*/
50 fcntl(sockfd, F_SETFL, flags); /* restore file status flags */
51 NETMGR_EXT_LOG_I("connect success.");
52 return NETMANAGER_EXT_SUCCESS;
53 }
54
55 fd_set rset;
56 FD_ZERO(&rset);
57 FD_SET(sockfd, &rset);
58 fd_set wset = rset;
59
60 timeval tval;
61 tval.tv_sec = nsec;
62 tval.tv_usec = 0;
63 ret = select(sockfd + 1, &rset, &wset, NULL, nsec ? &tval : NULL);
64 if (ret < 0) { // select error.
65 NETMGR_EXT_LOG_E("select error: %{public}d", errno);
66 return NETMANAGER_EXT_ERR_INTERNAL;
67 } else if (ret == 0) { // timeout
68 NETMGR_EXT_LOG_E("connect timeout.");
69 return NETMANAGER_EXT_ERR_INTERNAL;
70 } else { // fd ready
71 int32_t result = NETMANAGER_EXT_ERR_INTERNAL;
72 socklen_t len = sizeof(result);
73 if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
74 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &result, &len) < 0) {
75 NETMGR_EXT_LOG_E("getsockopt error: %{public}d", errno);
76 return NETMANAGER_EXT_ERR_INTERNAL;
77 }
78 } else {
79 NETMGR_EXT_LOG_E("select error: sockfd not set");
80 return NETMANAGER_EXT_ERR_INTERNAL;
81 }
82
83 if (result != NETMANAGER_EXT_SUCCESS) { // connect failed.
84 NETMGR_EXT_LOG_E("connect failed. error: %{public}d", result);
85 return NETMANAGER_EXT_ERR_INTERNAL;
86 } else { // connect success.
87 fcntl(sockfd, F_SETFL, flags); /* restore file status flags */
88 NETMGR_EXT_LOG_I("connect success.");
89 return NETMANAGER_EXT_SUCCESS;
90 }
91 }
92 }
93
RecvMsgFromUnixServer(int32_t sockfd)94 int32_t VpnInterface::RecvMsgFromUnixServer(int32_t sockfd)
95 {
96 char buf[1] = {0};
97 iovec iov = {
98 .iov_base = buf,
99 .iov_len = sizeof(buf),
100 };
101 union {
102 cmsghdr align;
103 char cmsg[CMSG_SPACE(sizeof(int32_t))];
104 } cmsgu;
105 if (memset_s(cmsgu.cmsg, sizeof(cmsgu.cmsg), 0, sizeof(cmsgu.cmsg)) != EOK) {
106 NETMGR_EXT_LOG_E("memset_s cmsgu.cmsg failed!");
107 return NETMANAGER_EXT_ERR_INTERNAL;
108 }
109 msghdr message;
110 if (memset_s(&message, sizeof(message), 0, sizeof(message)) != EOK) {
111 NETMGR_EXT_LOG_E("memset_s message failed!");
112 return NETMANAGER_EXT_ERR_INTERNAL;
113 }
114 message.msg_iov = &iov;
115 message.msg_iovlen = 1;
116 message.msg_control = cmsgu.cmsg;
117 message.msg_controllen = sizeof(cmsgu.cmsg);
118 if (recvmsg(sockfd, &message, 0) < 0) {
119 NETMGR_EXT_LOG_E("recvmsg msg error: %{public}d", errno);
120 return NETMANAGER_EXT_ERR_INTERNAL;
121 }
122
123 cmsghdr *cmsgh = CMSG_FIRSTHDR(&message);
124 if (cmsgh == nullptr) {
125 NETMGR_EXT_LOG_E("cmsgh is nullptr");
126 return NETMANAGER_EXT_ERR_INTERNAL;
127 }
128 if (cmsgh->cmsg_level != SOL_SOCKET || cmsgh->cmsg_type != SCM_RIGHTS ||
129 cmsgh->cmsg_len != CMSG_LEN(sizeof(int32_t))) {
130 NETMGR_EXT_LOG_E("cmsg_level: [%{public}d], cmsg_type: [%{public}d], cmsg_len: [%{public}d]", cmsgh->cmsg_level,
131 cmsgh->cmsg_type, cmsgh->cmsg_len);
132 return NETMANAGER_EXT_ERR_INTERNAL;
133 }
134
135 if (memcpy_s(&tunFd_, sizeof(tunFd_), CMSG_DATA(cmsgh), sizeof(tunFd_)) != EOK) {
136 NETMGR_EXT_LOG_E("memcpy_s cmsgu failed!");
137 return NETMANAGER_EXT_ERR_INTERNAL;
138 }
139 return NETMANAGER_EXT_SUCCESS;
140 }
141
GetVpnInterfaceFd()142 int32_t VpnInterface::GetVpnInterfaceFd()
143 {
144 CloseVpnInterfaceFd();
145 int32_t sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
146 if (sockfd < 0) {
147 NETMGR_EXT_LOG_E("create unix SOCK_STREAM socket error: %{public}d", errno);
148 return INVALID_FD;
149 }
150
151 if (ConnectControl(sockfd, CONNECT_TIMEOUT) != NETMANAGER_EXT_SUCCESS) {
152 close(sockfd);
153 NETMGR_EXT_LOG_E("connect error: %{public}d", errno);
154 return INVALID_FD;
155 }
156
157 if (RecvMsgFromUnixServer(sockfd) != NETMANAGER_EXT_SUCCESS) {
158 close(sockfd);
159 return INVALID_FD;
160 }
161
162 close(sockfd);
163 NETMGR_EXT_LOG_I("receive tun device fd: [%{public}d]", tunFd_.load());
164 return tunFd_;
165 }
166
CloseVpnInterfaceFd()167 void VpnInterface::CloseVpnInterfaceFd()
168 {
169 if (tunFd_ > 0) {
170 NETMGR_EXT_LOG_I("close tunfd[%{public}d] of vpn interface", tunFd_.load());
171 close(tunFd_);
172 tunFd_ = 0;
173 }
174 }
175
176 } // namespace NetManagerStandard
177 } // namespace OHOS
178