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