• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "host/libs/allocd/resource_manager.h"
18 
19 #include <android-base/logging.h>
20 #include <pwd.h>
21 #include <sys/socket.h>
22 #include <sys/types.h>
23 #include <sys/wait.h>
24 #include <unistd.h>
25 
26 #include <atomic>
27 #include <cstdint>
28 #include <cstdlib>
29 #include <iomanip>
30 #include <memory>
31 #include <optional>
32 #include <sstream>
33 #include <string>
34 
35 #include "common/libs/fs/shared_fd.h"
36 #include "host/libs/allocd/alloc_utils.h"
37 #include "host/libs/allocd/request.h"
38 #include "host/libs/allocd/utils.h"
39 #include "json/forwards.h"
40 #include "json/value.h"
41 #include "json/writer.h"
42 
43 namespace cuttlefish {
44 
45 uid_t GetUserIDFromSock(SharedFD client_socket);
46 
~ResourceManager()47 ResourceManager::~ResourceManager() {
48   bool success = true;
49   for (auto& res : managed_sessions_) {
50     success &= res.second->ReleaseAllResources();
51   }
52 
53   Json::Value resp;
54   resp["request_type"] = "shutdown";
55   auto status = success ? RequestStatus::Success : RequestStatus::Failure;
56   resp["request_status"] = StatusToStr(status);
57   SendJsonMsg(shutdown_socket_, resp);
58   LOG(INFO) << "Daemon Shutdown complete";
59   unlink(location.c_str());
60 }
61 
SetSocketLocation(const std::string & sock_name)62 void ResourceManager::SetSocketLocation(const std::string& sock_name) {
63   location = sock_name;
64 }
65 
SetUseEbtablesLegacy(bool use_legacy)66 void ResourceManager::SetUseEbtablesLegacy(bool use_legacy) {
67   use_ebtables_legacy_ = use_legacy;
68 }
69 
AllocateResourceID()70 uint32_t ResourceManager::AllocateResourceID() {
71   return global_resource_id_.fetch_add(1, std::memory_order_relaxed);
72 }
73 
AllocateSessionID()74 uint32_t ResourceManager::AllocateSessionID() {
75   return session_id_.fetch_add(1, std::memory_order_relaxed);
76 }
77 
AddInterface(const std::string & iface,IfaceType ty,uint32_t resource_id,uid_t uid)78 bool ResourceManager::AddInterface(const std::string& iface, IfaceType ty,
79                                    uint32_t resource_id, uid_t uid) {
80   bool allocatedIface = false;
81   std::shared_ptr<StaticResource> res = nullptr;
82 
83   bool didInsert = active_interfaces_.insert(iface).second;
84   if (didInsert) {
85     const char* idp = iface.c_str() + (iface.size() - 3);
86     int small_id = atoi(idp);
87     switch (ty) {
88       case IfaceType::wifiap:
89         // TODO(seungjaeyoo) : Support AddInterface for wifiap
90         break;
91       case IfaceType::mtap:
92         // TODO(seungjaeyoo) : Support AddInterface for mtap uses IP prefix
93         // different from kMobileIp.
94         res = std::make_shared<MobileIface>(iface, uid, small_id, resource_id,
95                                             kMobileIp);
96         allocatedIface = res->AcquireResource();
97         pending_add_.insert({resource_id, res});
98         break;
99       case IfaceType::wtap: {
100         auto w = std::make_shared<EthernetIface>(
101             iface, uid, small_id, resource_id, "cvd-wbr", kWirelessIp);
102         w->SetUseEbtablesLegacy(use_ebtables_legacy_);
103         w->SetHasIpv4(use_ipv4_bridge_);
104         w->SetHasIpv6(use_ipv6_bridge_);
105         res = w;
106         allocatedIface = res->AcquireResource();
107         pending_add_.insert({resource_id, res});
108         break;
109       }
110       case IfaceType::etap: {
111         auto w = std::make_shared<EthernetIface>(
112             iface, uid, small_id, resource_id, "cvd-ebr", kEthernetIp);
113         w->SetUseEbtablesLegacy(use_ebtables_legacy_);
114         w->SetHasIpv4(use_ipv4_bridge_);
115         w->SetHasIpv6(use_ipv6_bridge_);
116         res = w;
117         allocatedIface = res->AcquireResource();
118         pending_add_.insert({resource_id, res});
119         break;
120       }
121       case IfaceType::wbr:
122       case IfaceType::ebr:
123         allocatedIface = CreateBridge(iface);
124         break;
125       case IfaceType::Invalid:
126         break;
127     }
128   } else {
129     LOG(WARNING) << "Interface already in use: " << iface;
130   }
131 
132   if (didInsert && !allocatedIface) {
133     LOG(WARNING) << "Failed to allocate interface: " << iface;
134     active_interfaces_.erase(iface);
135     auto it = pending_add_.find(resource_id);
136     it->second->ReleaseResource();
137     pending_add_.erase(it);
138   }
139 
140   LOG(INFO) << "Finish CreateInterface Request";
141 
142   return allocatedIface;
143 }
144 
RemoveInterface(const std::string & iface,IfaceType ty)145 bool ResourceManager::RemoveInterface(const std::string& iface, IfaceType ty) {
146   bool isManagedIface = active_interfaces_.erase(iface) > 0;
147   bool removedIface = false;
148   if (isManagedIface) {
149     switch (ty) {
150       case IfaceType::wifiap:
151         // TODO(seungjaeyoo) : Support RemoveInterface for wifiap
152         break;
153       case IfaceType::mtap: {
154         // TODO(seungjaeyoo) : Support RemoveInterface for mtap uses IP prefix
155         // different from kMobileIp.
156         const char* idp = iface.c_str() + (iface.size() - 3);
157         int id = atoi(idp);
158         removedIface = DestroyMobileIface(iface, id, kMobileIp);
159         break;
160       }
161       case IfaceType::wtap:
162       case IfaceType::etap:
163         removedIface = DestroyEthernetIface(
164             iface, use_ipv4_bridge_, use_ipv6_bridge_, use_ebtables_legacy_);
165         break;
166       case IfaceType::wbr:
167       case IfaceType::ebr:
168         removedIface = DestroyBridge(iface);
169         break;
170       case IfaceType::Invalid:
171         break;
172     }
173 
174   } else {
175     LOG(WARNING) << "Interface not managed: " << iface;
176   }
177 
178   if (removedIface) {
179     LOG(INFO) << "Removed interface: " << iface;
180   } else {
181     LOG(WARNING) << "Could not remove interface: " << iface;
182   }
183 
184   return isManagedIface;
185 }
186 
ValidateRequestList(const Json::Value & config)187 bool ResourceManager::ValidateRequestList(const Json::Value& config) {
188   if (!config.isMember("request_list") || !config["request_list"].isArray()) {
189     LOG(WARNING) << "Request has invalid 'request_list' field";
190     return false;
191   }
192 
193   auto request_list = config["request_list"];
194 
195   Json::ArrayIndex size = request_list.size();
196   if (size == 0) {
197     LOG(WARNING) << "Request has empty 'request_list' field";
198     return false;
199   }
200 
201   for (Json::ArrayIndex i = 0; i < size; ++i) {
202     if (!ValidateRequest(request_list[i])) {
203       return false;
204     }
205   }
206 
207   return true;
208 }
209 
ValidateConfigRequest(const Json::Value & config)210 bool ResourceManager::ValidateConfigRequest(const Json::Value& config) {
211   if (!config.isMember("config_request") ||
212       !config["config_request"].isObject()) {
213     LOG(WARNING) << "Request has invalid 'config_request' field";
214     return false;
215   }
216 
217   Json::Value config_request = config["config_request"];
218 
219   return ValidateRequestList(config_request);
220 }
221 
ValidateRequest(const Json::Value & request)222 bool ResourceManager::ValidateRequest(const Json::Value& request) {
223   if (!request.isMember("request_type") ||
224       !request["request_type"].isString() ||
225       StrToReqTy(request["request_type"].asString()) == RequestType::Invalid) {
226     LOG(WARNING) << "Request has invalid 'request_type' field";
227     return false;
228   }
229   return true;
230 }
231 
JsonServer()232 void ResourceManager::JsonServer() {
233   LOG(INFO) << "Starting server on " << kDefaultLocation;
234   auto server = SharedFD::SocketLocalServer(kDefaultLocation, false,
235                                             SOCK_STREAM, kSocketMode);
236   CHECK(server->IsOpen()) << "Could not start server at " << kDefaultLocation;
237   LOG(INFO) << "Accepting client connections";
238 
239   while (true) {
240     auto client_socket = SharedFD::Accept(*server);
241     CHECK(client_socket->IsOpen()) << "Error creating client socket";
242 
243     struct timeval timeout;
244     timeout.tv_sec = 10;
245     timeout.tv_usec = 0;
246 
247     int err = client_socket->SetSockOpt(SOL_SOCKET, SO_RCVTIMEO, &timeout,
248                                         sizeof(timeout));
249     if (err < 0) {
250       LOG(WARNING) << "Could not set socket timeout";
251       continue;
252     }
253 
254     auto req_opt = RecvJsonMsg(client_socket);
255 
256     if (!req_opt) {
257       LOG(WARNING) << "Invalid JSON Request, closing connection";
258       continue;
259     }
260 
261     Json::Value req = req_opt.value();
262 
263     if (!ValidateConfigRequest(req)) {
264       continue;
265     }
266 
267     Json::Value req_list = req["config_request"]["request_list"];
268 
269     Json::Value config_response;
270     Json::Value response_list;
271     Json::ArrayIndex req_list_size = req_list.size();
272 
273     // sentinel value, so we can populate the list of responses correctly
274     // without trying to satisfy requests that will be aborted
275     bool transaction_failed = false;
276 
277     for (Json::ArrayIndex i = 0; i < req_list_size; ++i) {
278       LOG(INFO) << "Processing Request: " << i;
279       auto req = req_list[i];
280       auto req_ty_str = req["request_type"].asString();
281       auto req_ty = StrToReqTy(req_ty_str);
282 
283       Json::Value response;
284       if (transaction_failed) {
285         response["request_type"] = req_ty_str;
286         response["request_status"] = "pending";
287         response["error"] = "";
288         response_list.append(response);
289         continue;
290       }
291 
292       switch (req_ty) {
293         case RequestType::ID: {
294           response = JsonHandleIdRequest();
295           break;
296         }
297         case RequestType::Shutdown: {
298           if (i != 0 || req_list_size != 1) {
299             response["request_type"] = req_ty_str;
300             response["request_status"] = "failed";
301             response["error"] =
302                 "Shutdown requests cannot be processed with other "
303                 "configuration requests";
304             response_list.append(response);
305             break;
306           } else {
307             response = JsonHandleShutdownRequest(client_socket);
308             response_list.append(response);
309             return;
310           }
311         }
312         case RequestType::CreateInterface: {
313           response = JsonHandleCreateInterfaceRequest(client_socket, req);
314           break;
315         }
316         case RequestType::DestroyInterface: {
317           response = JsonHandleDestroyInterfaceRequest(req);
318           break;
319         }
320         case RequestType::StopSession: {
321           response = JsonHandleStopSessionRequest(
322               req, GetUserIDFromSock(client_socket));
323           break;
324         }
325         case RequestType::Invalid: {
326           LOG(WARNING) << "Invalid Request Type: " << req["request_type"];
327           break;
328         }
329       }
330 
331       response_list.append(response);
332       if (!(response["request_status"].asString() ==
333             StatusToStr(RequestStatus::Success))) {
334         LOG(INFO) << "Request failed:" << req;
335         transaction_failed = true;
336         continue;
337       }
338     }
339 
340     config_response["response_list"] = response_list;
341 
342     auto status =
343         transaction_failed ? RequestStatus::Failure : RequestStatus::Success;
344     config_response["config_status"] = StatusToStr(status);
345 
346     if (!transaction_failed) {
347       auto session_id = AllocateSessionID();
348       config_response["session_id"] = session_id;
349       auto s = std::make_shared<Session>(session_id,
350                                          GetUserIDFromSock(client_socket));
351 
352       // commit the resources
353       s->Insert(pending_add_);
354       pending_add_.clear();
355       managed_sessions_.insert({session_id, s});
356     } else {
357       // be sure to release anything we've acquired if the transaction failed
358       for (auto& droped_resource : pending_add_) {
359         droped_resource.second->ReleaseResource();
360       }
361     }
362 
363     SendJsonMsg(client_socket, config_response);
364     LOG(INFO) << "Closing connection to client";
365     client_socket->Close();
366   }
367   server->Close();
368 }
369 
GetUserIDFromSock(SharedFD client_socket)370 uid_t GetUserIDFromSock(SharedFD client_socket) {
371   struct ucred ucred {};
372   socklen_t len = sizeof(struct ucred);
373 
374   if (-1 == client_socket->GetSockOpt(SOL_SOCKET, SO_PEERCRED, &ucred, &len)) {
375     LOG(WARNING) << "Failed to get Socket Credentials";
376     return -1;
377   }
378 
379   return ucred.uid;
380 }
381 
CheckCredentials(SharedFD client_socket,uid_t uid)382 bool ResourceManager::CheckCredentials(SharedFD client_socket, uid_t uid) {
383   uid_t sock_uid = GetUserIDFromSock(client_socket);
384 
385   if (sock_uid == -1) {
386     LOG(WARNING) << "Invalid Socket UID: " << uid;
387     return false;
388   }
389 
390   if (uid != sock_uid) {
391     LOG(WARNING) << "Message UID: " << uid
392                  << " does not match socket's EUID: " << sock_uid;
393     return false;
394   }
395 
396   return true;
397 }
398 
JsonHandleIdRequest()399 Json::Value ResourceManager::JsonHandleIdRequest() {
400   Json::Value resp;
401   resp["request_type"] = "allocate_id";
402   resp["request_status"] = StatusToStr(RequestStatus::Success);
403   resp["id"] = AllocateSessionID();
404   return resp;
405 }
406 
JsonHandleShutdownRequest(SharedFD client_socket)407 Json::Value ResourceManager::JsonHandleShutdownRequest(SharedFD client_socket) {
408   LOG(INFO) << "Received Shutdown Request";
409   shutdown_socket_ = client_socket;
410 
411   Json::Value resp;
412   resp["request_type"] = "shutdown";
413   resp["request_status"] = "pending";
414   resp["error"] = "";
415 
416   return resp;
417 }
418 
JsonHandleCreateInterfaceRequest(SharedFD client_socket,const Json::Value & request)419 Json::Value ResourceManager::JsonHandleCreateInterfaceRequest(
420     SharedFD client_socket, const Json::Value& request) {
421   LOG(INFO) << "Received CreateInterface Request";
422 
423   Json::Value resp;
424   resp["request_type"] = "create_interface";
425   resp["iface_name"] = "";
426   resp["request_status"] = StatusToStr(RequestStatus::Failure);
427   resp["error"] = "unknown";
428 
429   if (!request.isMember("uid") || !request["uid"].isUInt()) {
430     auto err_msg = "Input event doesn't have a valid 'uid' field";
431     LOG(WARNING) << err_msg;
432     resp["error"] = err_msg;
433     return resp;
434   }
435 
436   if (!request.isMember("iface_type") || !request["iface_type"].isString()) {
437     auto err_msg = "Input event doesn't have a valid 'iface_type' field";
438     LOG(WARNING) << err_msg;
439     resp["error"] = err_msg;
440     return resp;
441   }
442 
443   auto uid = request["uid"].asUInt();
444 
445   if (!CheckCredentials(client_socket, uid)) {
446     auto err_msg = "Credential check failed";
447     LOG(WARNING) << err_msg;
448     resp["error"] = err_msg;
449     return resp;
450   }
451 
452   auto user_opt = GetUserName(uid);
453 
454   bool addedIface = false;
455   std::stringstream ss;
456   if (!user_opt) {
457     auto err_msg = "UserName could not be matched to UID";
458     LOG(WARNING) << err_msg;
459     resp["error"] = err_msg;
460     return resp;
461   } else {
462     auto iface_ty_name = request["iface_type"].asString();
463     resp["iface_type"] = iface_ty_name;
464     auto iface_type = StrToIfaceTy(iface_ty_name);
465     auto attempts = kMaxIfaceNameId;
466     do {
467       auto id = AllocateResourceID();
468       resp["resource_id"] = id;
469       ss << "cvd-" << iface_ty_name << "-" << user_opt.value().substr(0, 4)
470          << std::setfill('0') << std::setw(2) << (id % kMaxIfaceNameId);
471       addedIface = AddInterface(ss.str(), iface_type, id, uid);
472       --attempts;
473     } while (!addedIface && (attempts > 0));
474   }
475 
476   if (addedIface) {
477     resp["request_status"] = StatusToStr(RequestStatus::Success);
478     resp["iface_name"] = ss.str();
479     resp["error"] = "";
480   }
481 
482   return resp;
483 }
484 
JsonHandleDestroyInterfaceRequest(const Json::Value & request)485 Json::Value ResourceManager::JsonHandleDestroyInterfaceRequest(
486     const Json::Value& request) {
487   Json::Value resp;
488   resp["request_type"] = "destroy_interface";
489   resp["request_status"] = StatusToStr(RequestStatus::Failure);
490   if (!request.isMember("iface_name") || !request["iface_name"].isString()) {
491     auto err_msg = "Input event doesn't have a valid 'iface_name' field";
492     LOG(WARNING) << err_msg;
493     resp["error"] = err_msg;
494     return resp;
495   }
496 
497   auto iface_name = request["iface_name"].asString();
498 
499   bool isManagedIface = active_interfaces_.erase(iface_name) > 0;
500 
501   if (!isManagedIface) {
502     auto msg = "Interface not managed: " + iface_name;
503     LOG(WARNING) << msg;
504     resp["error"] = msg;
505     return resp;
506   }
507 
508   if (!request.isMember("session_id") || !request["session_id"].isUInt()) {
509     auto err_msg = "Input event doesn't have a valid 'session_id' field";
510     LOG(WARNING) << err_msg;
511     resp["error"] = err_msg;
512     return resp;
513   }
514 
515   auto session_id = request["session_id"].asUInt();
516 
517   auto resource_id = request["resource_id"].asUInt();
518 
519   LOG(INFO) << "Received DestroyInterface Request for " << iface_name
520             << " in session: " << session_id
521             << ", resource_id: " << resource_id;
522 
523   auto sess_opt = FindSession(session_id);
524   if (!sess_opt) {
525     auto msg = "Interface " + iface_name +
526                " was not managed in session: " + std::to_string(session_id) +
527                " with resource_id: " + std::to_string(resource_id);
528     LOG(WARNING) << msg;
529     resp["error"] = msg;
530     return resp;
531   }
532 
533   auto s = sess_opt.value();
534 
535   // while we could wait to see if any acquisitions fail and delay releasing
536   // resources until they are all finished, this operation is inherently
537   // destructive, so should a release operation fail, there is no satisfactory
538   // method for aborting the transaction. Instead, we try to release the
539   // resource and then can signal to the rest of the transaction the failure
540   // state, which can then just stop the transaction, and revert any newly
541   // acquired resources, but any successful drop requests will persist
542   auto did_drop_resource = s->ReleaseResource(resource_id);
543 
544   if (did_drop_resource) {
545     resp["request_status"] = StatusToStr(RequestStatus::Success);
546   } else {
547     auto msg = "Interface " + iface_name +
548                " was not managed in session: " + std::to_string(session_id) +
549                " with resource_id: " + std::to_string(resource_id);
550     LOG(WARNING) << msg;
551     resp["error"] = msg;
552   }
553 
554   return resp;
555 }
556 
JsonHandleStopSessionRequest(const Json::Value & request,uid_t uid)557 Json::Value ResourceManager::JsonHandleStopSessionRequest(
558     const Json::Value& request, uid_t uid) {
559   Json::Value resp;
560   resp["request_type"] = ReqTyToStr(RequestType::StopSession);
561   resp["request_status"] = StatusToStr(RequestStatus::Failure);
562   if (!request.isMember("session_id") || !request["session_id"].isUInt()) {
563     auto err_msg = "Input event doesn't have a valid 'session_id' field";
564     LOG(WARNING) << err_msg;
565     resp["error"] = err_msg;
566     return resp;
567   }
568 
569   auto session_id = request["session_id"].asUInt();
570   LOG(INFO) << "Received StopSession Request for Session ID: " << session_id;
571 
572   auto it = managed_sessions_.find(session_id);
573   if (it == managed_sessions_.end()) {
574     auto msg = "Session not managed: " + std::to_string(session_id);
575     LOG(WARNING) << msg;
576     resp["error"] = msg;
577     return resp;
578   }
579 
580   if (it->second->GetUID() != uid) {
581     auto msg = "Effective user ID does not match session owner. socket uid: " +
582                std::to_string(uid);
583     LOG(WARNING) << msg;
584     resp["error"] = msg;
585     return resp;
586   }
587 
588   // while we could wait to see if any acquisitions fail and delay releasing
589   // resources until they are all finished, this operation is inherently
590   // destructive, so should a release operation fail, there is no satisfactory
591   // method for aborting the transaction. Instead, we try to release the
592   // resource and then can signal to the rest of the transaction the failure
593   // state
594   auto success = it->second->ReleaseAllResources();
595 
596   // release the names from the global list for reuse in future requests
597   for (auto& iface : it->second->GetActiveInterfaces()) {
598     active_interfaces_.erase(iface);
599   }
600 
601   if (success) {
602     managed_sessions_.erase(it);
603     resp["request_status"] = StatusToStr(RequestStatus::Success);
604   } else {
605     resp["error"] =
606         "unknown, allocd experienced an error ending the session id: " +
607         std::to_string(session_id);
608   }
609 
610   return resp;
611 }
612 
FindSession(uint32_t id)613 std::optional<std::shared_ptr<Session>> ResourceManager::FindSession(
614     uint32_t id) {
615   auto it = managed_sessions_.find(id);
616   if (it == managed_sessions_.end()) {
617     return std::nullopt;
618   } else {
619     return it->second;
620   }
621 }
622 
623 }  // namespace cuttlefish
624