• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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 "fwmark_network.h"
17 
18 #include <cerrno>
19 #include <pthread.h>
20 #include <sys/socket.h>
21 #include <sys/stat.h>
22 #include <sys/un.h>
23 #include <thread>
24 #include <unistd.h>
25 
26 #include "fwmark.h"
27 #include "fwmark_command.h"
28 #include "init_socket.h"
29 #include "netnative_log_wrapper.h"
30 #ifdef USE_SELINUX
31 #include "selinux/selinux.h"
32 #endif
33 #include "fwmark_epoller.h"
34 #include "securec.h"
35 
36 namespace OHOS {
37 namespace nmd {
38 using namespace NetManagerStandard;
39 static constexpr const uint16_t NETID_UNSET = 0;
40 static constexpr const int32_t NO_ERROR_CODE = 0;
41 static constexpr const int32_t ERROR_CODE_RECVMSG_FAILED = -1;
42 static constexpr const int32_t ERROR_CODE_SOCKETFD_INVALID = -2;
43 static constexpr const int32_t ERROR_CODE_WRITE_FAILED = -3;
44 static constexpr const int32_t ERROR_CODE_GETSOCKOPT_FAILED = -4;
45 static constexpr const int32_t ERROR_CODE_SETSOCKOPT_FAILED = -5;
46 static constexpr const int32_t ERROR_CODE_SET_MARK = -6;
47 static constexpr const int32_t MAX_CONCURRENT_CONNECTION_REQUESTS = 10;
48 union Cmsgu {
49     cmsghdr cmh;
50     char cmsg[CMSG_SPACE(sizeof(0))];
51 };
CloseSocket(int32_t * socket,int32_t ret,int32_t errorCode)52 void CloseSocket(int32_t *socket, int32_t ret, int32_t errorCode)
53 {
54     if (socket == nullptr) {
55         NETNATIVE_LOGE("CloseSocket failed, socket is nullptr");
56         return;
57     }
58     switch (errorCode) {
59         case ERROR_CODE_RECVMSG_FAILED:
60             NETNATIVE_LOGE("recvmsg failed, clientSockfd:%{public}d, ret:%{public}d, errno: %{public}d", *socket, ret,
61                            errno);
62             break;
63         case ERROR_CODE_SOCKETFD_INVALID:
64             NETNATIVE_LOGE("socketFd invalid:%{public}d, ret:%{public}d, errno: %{public}d", *socket, ret, errno);
65             break;
66         case ERROR_CODE_WRITE_FAILED:
67             NETNATIVE_LOGE("wirte failed, clientSockfd:%{public}d, ret:%{public}d, errno: %{public}d", *socket, ret,
68                            errno);
69             break;
70         case ERROR_CODE_GETSOCKOPT_FAILED:
71             NETNATIVE_LOGE("getsockopt failed, socketFd:%{public}d, ret:%{public}d, errno: %{public}d", *socket, ret,
72                            errno);
73             break;
74         case ERROR_CODE_SETSOCKOPT_FAILED:
75             NETNATIVE_LOGE("setsockopt failed socketFd:%{public}d, ret:%{public}d, errno: %{public}d", *socket, ret,
76                            errno);
77             break;
78         case ERROR_CODE_SET_MARK:
79             NETNATIVE_LOGE("SetMark failed, clientSockfd:%{public}d, ret:%{public}d, errno: %{public}d", *socket, ret,
80                            errno);
81             break;
82         default:
83             NETNATIVE_LOG_D("NO_ERROR_CODE CloseSocket socket:%{public}d, ret:%{public}d", *socket, ret);
84             break;
85     }
86     close(*socket);
87     *socket = -1;
88 }
89 
SetMark(int32_t * socketFd,FwmarkCommand * command)90 int32_t SetMark(int32_t *socketFd, FwmarkCommand *command)
91 {
92     if (command == nullptr || socketFd == nullptr) {
93         NETNATIVE_LOGE("SetMark failed, command or socketFd is nullptr");
94         return -1;
95     }
96     Fwmark fwmark;
97     socklen_t fwmarkLen = sizeof(fwmark.intValue);
98     int32_t ret = getsockopt(*socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue, &fwmarkLen);
99     if (ret != 0) {
100         CloseSocket(socketFd, ret, ERROR_CODE_GETSOCKOPT_FAILED);
101         return ret;
102     }
103     NETNATIVE_LOGI("FwmarkNetwork: SetMark netId: %{public}d, socketFd:%{public}d, cmd:%{public}d", command->netId,
104                    *socketFd, command->cmdId);
105     switch (command->cmdId) {
106         case FwmarkCommand::SELECT_NETWORK: {
107             fwmark.netId = command->netId;
108             if (command->netId == NETID_UNSET) {
109                 fwmark.explicitlySelected = false;
110                 fwmark.protectedFromVpn = false;
111                 fwmark.permission = PERMISSION_NONE;
112             } else {
113                 fwmark.explicitlySelected = true;
114             }
115             break;
116         }
117         case FwmarkCommand::PROTECT_FROM_VPN: {
118             fwmark.protectedFromVpn = true;
119             break;
120         }
121         default:
122             break;
123     }
124     ret = setsockopt(*socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue, sizeof(fwmark.intValue));
125     if (ret != 0) {
126         NETNATIVE_LOGE("FwmarkNetwork: SetMark failed, ret %{public}d.", ret);
127         CloseSocket(socketFd, ret, ERROR_CODE_SETSOCKOPT_FAILED);
128         return ret;
129     }
130     CloseSocket(socketFd, ret, NO_ERROR_CODE);
131     return ret;
132 }
133 
RunForClientFd(int32_t clientSockfd)134 void RunForClientFd(int32_t clientSockfd)
135 {
136     FwmarkCommand fwmCmd{};
137     iovec iov = {.iov_base = &fwmCmd, .iov_len = sizeof(fwmCmd)};
138     int32_t socketFd = -1;
139     Cmsgu cmsgu;
140     if (memset_s(cmsgu.cmsg, sizeof(cmsgu.cmsg), 0, sizeof(cmsgu.cmsg)) != EOK) {
141         CloseSocket(&clientSockfd, -1, ERROR_CODE_RECVMSG_FAILED);
142         return;
143     }
144     msghdr message;
145     if (memset_s(&message, sizeof(message), 0, sizeof(message)) != EOK) {
146         CloseSocket(&clientSockfd, -1, ERROR_CODE_RECVMSG_FAILED);
147         return;
148     }
149     message = {.msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsgu.cmsg, .msg_controllen = sizeof(cmsgu.cmsg)};
150     int32_t ret = recvmsg(clientSockfd, &message, 0);
151     if (ret < 0) {
152         CloseSocket(&clientSockfd, ret, ERROR_CODE_RECVMSG_FAILED);
153         return;
154     }
155     cmsghdr *const cmsgh = CMSG_FIRSTHDR(&message);
156     if (cmsgh && cmsgh->cmsg_level == SOL_SOCKET && cmsgh->cmsg_type == SCM_RIGHTS &&
157         cmsgh->cmsg_len == CMSG_LEN(sizeof(socketFd))) {
158         if (memcpy_s(&socketFd, sizeof(socketFd), CMSG_DATA(cmsgh), sizeof(socketFd)) != 0) {
159             return;
160         }
161     }
162     if (socketFd < 0) {
163         CloseSocket(&clientSockfd, ret, ERROR_CODE_SOCKETFD_INVALID);
164         return;
165     }
166     if ((ret = SetMark(&socketFd, &fwmCmd)) != 0) {
167         CloseSocket(&clientSockfd, ret, ERROR_CODE_SET_MARK);
168         return;
169     }
170     if ((ret = write(clientSockfd, &ret, sizeof(ret))) < 0) {
171         CloseSocket(&clientSockfd, ret, ERROR_CODE_WRITE_FAILED);
172         return;
173     }
174     CloseSocket(&clientSockfd, ret, NO_ERROR_CODE);
175 }
176 
StartListener()177 void StartListener()
178 {
179     int32_t serverSockfd = GetControlSocket("fwmarkd");
180 
181     int32_t result = listen(serverSockfd, MAX_CONCURRENT_CONNECTION_REQUESTS);
182     if (result < 0) {
183         NETNATIVE_LOGE("FwmarkNetwork: listen failed result %{public}d, errno: %{public}d", result, errno);
184         close(serverSockfd);
185         serverSockfd = -1;
186         return;
187     }
188     if (!FwmarkTool::MakeNonBlock(serverSockfd)) {
189         close(serverSockfd);
190         serverSockfd = -1;
191         return;
192     }
193     FwmarkTool::FwmarkEpollServer server(serverSockfd, RunForClientFd);
194     server.Run();
195     close(serverSockfd);
196     serverSockfd = -1;
197 }
198 
FwmarkNetwork()199 FwmarkNetwork::FwmarkNetwork()
200 {
201     ListenerClient();
202 }
203 
204 FwmarkNetwork::~FwmarkNetwork() = default;
205 
ListenerClient()206 void FwmarkNetwork::ListenerClient()
207 {
208     std::thread t(StartListener);
209     pthread_setname_np(t.native_handle(), "FwmarkListen");
210     t.detach();
211     NETNATIVE_LOGI("FwmarkNetwork: StartListener");
212 }
213 } // namespace nmd
214 } // namespace OHOS
215