• 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::mtap:
89         res = std::make_shared<MobileIface>(iface, uid, small_id, resource_id,
90                                             kMobileIp);
91         allocatedIface = res->AcquireResource();
92         pending_add_.insert({resource_id, res});
93         break;
94       case IfaceType::wtap: {
95         // TODO (paulkirth): change this to cvd-wbr, to test w/ today's
96         // debian package, this is required since the number of wireless
97         // bridges provided by the debian package has gone from 10 down to
98         // 1, but our debian packages in cloudtop are not up to date
99         auto w = std::make_shared<EthernetIface>(iface, uid, small_id,
100                                                  resource_id, "cvd-wbr-01",
101                                                  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>(iface, uid, small_id,
112                                                  resource_id, "cvd-ebr",
113                                                  kEthernetIp);
114         w->SetUseEbtablesLegacy(use_ebtables_legacy_);
115         w->SetHasIpv4(use_ipv4_bridge_);
116         w->SetHasIpv6(use_ipv6_bridge_);
117         res = w;
118         allocatedIface = res->AcquireResource();
119         pending_add_.insert({resource_id, res});
120         break;
121       }
122       case IfaceType::wbr:
123       case IfaceType::ebr:
124         allocatedIface = CreateBridge(iface);
125         break;
126       case IfaceType::Invalid:
127         break;
128     }
129   } else {
130     LOG(WARNING) << "Interface already in use: " << iface;
131   }
132 
133   if (didInsert && !allocatedIface) {
134     LOG(WARNING) << "Failed to allocate interface: " << iface;
135     active_interfaces_.erase(iface);
136     auto it = pending_add_.find(resource_id);
137     it->second->ReleaseResource();
138     pending_add_.erase(it);
139   }
140 
141   LOG(INFO) << "Finish CreateInterface Request";
142 
143   return allocatedIface;
144 }
145 
RemoveInterface(const std::string & iface,IfaceType ty)146 bool ResourceManager::RemoveInterface(const std::string& iface, IfaceType ty) {
147   bool isManagedIface = active_interfaces_.erase(iface) > 0;
148   bool removedIface = false;
149   if (isManagedIface) {
150     switch (ty) {
151       case IfaceType::mtap: {
152         const char* idp = iface.c_str() + (iface.size() - 3);
153         int id = atoi(idp);
154         removedIface = DestroyMobileIface(iface, id, kMobileIp);
155         break;
156       }
157       case IfaceType::wtap:
158       case IfaceType::etap:
159         removedIface = DestroyEthernetIface(
160             iface, use_ipv4_bridge_, use_ipv6_bridge_, use_ebtables_legacy_);
161         break;
162       case IfaceType::wbr:
163       case IfaceType::ebr:
164         removedIface = DestroyBridge(iface);
165         break;
166       case IfaceType::Invalid:
167         break;
168     }
169 
170   } else {
171     LOG(WARNING) << "Interface not managed: " << iface;
172   }
173 
174   if (isManagedIface) {
175     LOG(INFO) << "Removed interface: " << iface;
176   } else {
177     LOG(WARNING) << "Could not remove interface: " << iface;
178   }
179 
180   return isManagedIface;
181 }
182 
ValidateRequestList(const Json::Value & config)183 bool ResourceManager::ValidateRequestList(const Json::Value& config) {
184   if (!config.isMember("request_list") || !config["request_list"].isArray()) {
185     LOG(WARNING) << "Request has invalid 'request_list' field";
186     return false;
187   }
188 
189   auto request_list = config["request_list"];
190 
191   Json::ArrayIndex size = request_list.size();
192   if (size == 0) {
193     LOG(WARNING) << "Request has empty 'request_list' field";
194     return false;
195   }
196 
197   for (Json::ArrayIndex i = 0; i < size; ++i) {
198     if (!ValidateRequest(request_list[i])) {
199       return false;
200     }
201   }
202 
203   return true;
204 }
205 
ValidateConfigRequest(const Json::Value & config)206 bool ResourceManager::ValidateConfigRequest(const Json::Value& config) {
207   if (!config.isMember("config_request") ||
208       !config["config_request"].isObject()) {
209     LOG(WARNING) << "Request has invalid 'config_request' field";
210     return false;
211   }
212 
213   Json::Value config_request = config["config_request"];
214 
215   return ValidateRequestList(config_request);
216 }
217 
ValidateRequest(const Json::Value & request)218 bool ResourceManager::ValidateRequest(const Json::Value& request) {
219   if (!request.isMember("request_type") ||
220       !request["request_type"].isString() ||
221       StrToReqTy(request["request_type"].asString()) == RequestType::Invalid) {
222     LOG(WARNING) << "Request has invalid 'request_type' field";
223     return false;
224   }
225   return true;
226 }
227 
JsonServer()228 void ResourceManager::JsonServer() {
229   LOG(INFO) << "Starting server on " << kDefaultLocation;
230   auto server = SharedFD::SocketLocalServer(kDefaultLocation, false,
231                                             SOCK_STREAM, kSocketMode);
232   CHECK(server->IsOpen()) << "Could not start server at " << kDefaultLocation;
233   LOG(INFO) << "Accepting client connections";
234 
235   while (true) {
236     auto client_socket = SharedFD::Accept(*server);
237     CHECK(client_socket->IsOpen()) << "Error creating client socket";
238 
239     struct timeval timeout;
240     timeout.tv_sec = 10;
241     timeout.tv_usec = 0;
242 
243     int err = client_socket->SetSockOpt(SOL_SOCKET, SO_RCVTIMEO, &timeout,
244                                         sizeof(timeout));
245     if (err < 0) {
246       LOG(WARNING) << "Could not set socket timeout";
247       continue;
248     }
249 
250     auto req_opt = RecvJsonMsg(client_socket);
251 
252     if (!req_opt) {
253       LOG(WARNING) << "Invalid JSON Request, closing connection";
254       continue;
255     }
256 
257     Json::Value req = req_opt.value();
258 
259     if (!ValidateConfigRequest(req)) {
260       continue;
261     }
262 
263     Json::Value req_list = req["config_request"]["request_list"];
264 
265     Json::Value config_response;
266     Json::Value response_list;
267     Json::ArrayIndex req_list_size = req_list.size();
268 
269     // sentinel value, so we can populate the list of responses correctly
270     // without trying to satisfy requests that will be aborted
271     bool transaction_failed = false;
272 
273     for (Json::ArrayIndex i = 0; i < req_list_size; ++i) {
274       LOG(INFO) << "Processing Request: " << i;
275       auto req = req_list[i];
276       auto req_ty_str = req["request_type"].asString();
277       auto req_ty = StrToReqTy(req_ty_str);
278 
279       Json::Value response;
280       if (transaction_failed) {
281         response["request_type"] = req_ty_str;
282         response["request_status"] = "pending";
283         response["error"] = "";
284         response_list.append(response);
285         continue;
286       }
287 
288       switch (req_ty) {
289         case RequestType::ID: {
290           response = JsonHandleIdRequest();
291           break;
292         }
293         case RequestType::Shutdown: {
294           if (i != 0 || req_list_size != 1) {
295             response["request_type"] = req_ty_str;
296             response["request_status"] = "failed";
297             response["error"] =
298                 "Shutdown requests cannot be processed with other "
299                 "configuration requests";
300             response_list.append(response);
301             break;
302           } else {
303             response = JsonHandleShutdownRequest(client_socket);
304             response_list.append(response);
305             return;
306           }
307         }
308         case RequestType::CreateInterface: {
309           response = JsonHandleCreateInterfaceRequest(client_socket, req);
310           break;
311         }
312         case RequestType::DestroyInterface: {
313           response = JsonHandleDestroyInterfaceRequest(req);
314           break;
315         }
316         case RequestType::StopSession: {
317           response = JsonHandleStopSessionRequest(
318               req, GetUserIDFromSock(client_socket));
319           break;
320         }
321         case RequestType::Invalid: {
322           LOG(WARNING) << "Invalid Request Type: " << req["request_type"];
323           break;
324         }
325       }
326 
327       response_list.append(response);
328       if (!(response["request_status"].asString() ==
329             StatusToStr(RequestStatus::Success))) {
330         LOG(INFO) << "Request failed:" << req;
331         transaction_failed = true;
332         continue;
333       }
334     }
335 
336     config_response["response_list"] = response_list;
337 
338     auto status =
339         transaction_failed ? RequestStatus::Failure : RequestStatus::Success;
340     config_response["config_status"] = StatusToStr(status);
341 
342     if (!transaction_failed) {
343       auto session_id = AllocateSessionID();
344       config_response["session_id"] = session_id;
345       auto s = std::make_shared<Session>(session_id,
346                                          GetUserIDFromSock(client_socket));
347 
348       // commit the resources
349       s->Insert(pending_add_);
350       pending_add_.clear();
351       managed_sessions_.insert({session_id, s});
352     } else {
353       // be sure to release anything we've acquired if the transaction failed
354       for (auto& droped_resource : pending_add_) {
355         droped_resource.second->ReleaseResource();
356       }
357     }
358 
359     SendJsonMsg(client_socket, config_response);
360     LOG(INFO) << "Closing connection to client";
361     client_socket->Close();
362   }
363   server->Close();
364 }
365 
GetUserIDFromSock(SharedFD client_socket)366 uid_t GetUserIDFromSock(SharedFD client_socket) {
367   struct ucred ucred {};
368   socklen_t len = sizeof(struct ucred);
369 
370   if (-1 == client_socket->GetSockOpt(SOL_SOCKET, SO_PEERCRED, &ucred, &len)) {
371     LOG(WARNING) << "Failed to get Socket Credentials";
372     return -1;
373   }
374 
375   return ucred.uid;
376 }
377 
CheckCredentials(SharedFD client_socket,uid_t uid)378 bool ResourceManager::CheckCredentials(SharedFD client_socket, uid_t uid) {
379   uid_t sock_uid = GetUserIDFromSock(client_socket);
380 
381   if (sock_uid == -1) {
382     LOG(WARNING) << "Invalid Socket UID: " << uid;
383     return false;
384   }
385 
386   if (uid != sock_uid) {
387     LOG(WARNING) << "Message UID: " << uid
388                  << " does not match socket's EUID: " << sock_uid;
389     return false;
390   }
391 
392   return true;
393 }
394 
JsonHandleIdRequest()395 Json::Value ResourceManager::JsonHandleIdRequest() {
396   Json::Value resp;
397   resp["request_type"] = "allocate_id";
398   resp["request_status"] = StatusToStr(RequestStatus::Success);
399   resp["id"] = AllocateSessionID();
400   return resp;
401 }
402 
JsonHandleShutdownRequest(SharedFD client_socket)403 Json::Value ResourceManager::JsonHandleShutdownRequest(SharedFD client_socket) {
404   LOG(INFO) << "Received Shutdown Request";
405   shutdown_socket_ = client_socket;
406 
407   Json::Value resp;
408   resp["request_type"] = "shutdown";
409   resp["request_status"] = "pending";
410   resp["error"] = "";
411 
412   return resp;
413 }
414 
JsonHandleCreateInterfaceRequest(SharedFD client_socket,const Json::Value & request)415 Json::Value ResourceManager::JsonHandleCreateInterfaceRequest(
416     SharedFD client_socket, const Json::Value& request) {
417   LOG(INFO) << "Received CreateInterface Request";
418 
419   Json::Value resp;
420   resp["request_type"] = "create_interface";
421   resp["iface_name"] = "";
422   resp["request_status"] = StatusToStr(RequestStatus::Failure);
423   resp["error"] = "unknown";
424 
425   if (!request.isMember("uid") || !request["uid"].isUInt()) {
426     auto err_msg = "Input event doesn't have a valid 'uid' field";
427     LOG(WARNING) << err_msg;
428     resp["error"] = err_msg;
429     return resp;
430   }
431 
432   if (!request.isMember("iface_type") || !request["iface_type"].isString()) {
433     auto err_msg = "Input event doesn't have a valid 'iface_type' field";
434     LOG(WARNING) << err_msg;
435     resp["error"] = err_msg;
436     return resp;
437   }
438 
439   auto uid = request["uid"].asUInt();
440 
441   if (!CheckCredentials(client_socket, uid)) {
442     auto err_msg = "Credential check failed";
443     LOG(WARNING) << err_msg;
444     resp["error"] = err_msg;
445     return resp;
446   }
447 
448   auto user_opt = GetUserName(uid);
449 
450   bool addedIface = false;
451   std::stringstream ss;
452   if (!user_opt) {
453     auto err_msg = "UserName could not be matched to UID";
454     LOG(WARNING) << err_msg;
455     resp["error"] = err_msg;
456     return resp;
457   } else {
458     auto iface_ty_name = request["iface_type"].asString();
459     resp["iface_type"] = iface_ty_name;
460     auto iface_type = StrToIfaceTy(iface_ty_name);
461     auto attempts = kMaxIfaceNameId;
462     do {
463       auto id = AllocateResourceID();
464       resp["resource_id"] = id;
465       ss << "cvd-" << iface_ty_name << "-" << user_opt.value().substr(0, 4)
466          << std::setfill('0') << std::setw(2) << (id % kMaxIfaceNameId);
467       addedIface = AddInterface(ss.str(), iface_type, id, uid);
468       --attempts;
469     } while (!addedIface && (attempts > 0));
470   }
471 
472   if (addedIface) {
473     resp["request_status"] = StatusToStr(RequestStatus::Success);
474     resp["iface_name"] = ss.str();
475     resp["error"] = "";
476   }
477 
478   return resp;
479 }
480 
JsonHandleDestroyInterfaceRequest(const Json::Value & request)481 Json::Value ResourceManager::JsonHandleDestroyInterfaceRequest(
482     const Json::Value& request) {
483   Json::Value resp;
484   resp["request_type"] = "destroy_interface";
485   resp["request_status"] = StatusToStr(RequestStatus::Failure);
486   if (!request.isMember("iface_name") || !request["iface_name"].isString()) {
487     auto err_msg = "Input event doesn't have a valid 'iface_name' field";
488     LOG(WARNING) << err_msg;
489     resp["error"] = err_msg;
490     return resp;
491   }
492 
493   auto iface_name = request["iface_name"].asString();
494 
495   bool isManagedIface = active_interfaces_.erase(iface_name) > 0;
496 
497   if (!isManagedIface) {
498     auto msg = "Interface not managed: " + iface_name;
499     LOG(WARNING) << msg;
500     resp["error"] = msg;
501     return resp;
502   }
503 
504   if (!request.isMember("session_id") || !request["session_id"].isUInt()) {
505     auto err_msg = "Input event doesn't have a valid 'session_id' field";
506     LOG(WARNING) << err_msg;
507     resp["error"] = err_msg;
508     return resp;
509   }
510 
511   auto session_id = request["session_id"].asUInt();
512 
513   auto resource_id = request["resource_id"].asUInt();
514 
515   LOG(INFO) << "Received DestroyInterface Request for " << iface_name
516             << " in session: " << session_id
517             << ", resource_id: " << resource_id;
518 
519   auto sess_opt = FindSession(session_id);
520   if (!sess_opt) {
521     auto msg = "Interface " + iface_name +
522                " was not managed in session: " + std::to_string(session_id) +
523                " with resource_id: " + std::to_string(resource_id);
524     LOG(WARNING) << msg;
525     resp["error"] = msg;
526     return resp;
527   }
528 
529   auto s = sess_opt.value();
530 
531   // while we could wait to see if any acquisitions fail and delay releasing
532   // resources until they are all finished, this operation is inherently
533   // destructive, so should a release operation fail, there is no satisfactory
534   // method for aborting the transaction. Instead, we try to release the
535   // resource and then can signal to the rest of the transaction the failure
536   // state, which can then just stop the transaction, and revert any newly
537   // acquired resources, but any successful drop requests will persist
538   auto did_drop_resource = s->ReleaseResource(resource_id);
539 
540   if (did_drop_resource) {
541     resp["request_status"] = StatusToStr(RequestStatus::Success);
542   } else {
543     auto msg = "Interface " + iface_name +
544                " was not managed in session: " + std::to_string(session_id) +
545                " with resource_id: " + std::to_string(resource_id);
546     LOG(WARNING) << msg;
547     resp["error"] = msg;
548   }
549 
550   return resp;
551 }
552 
JsonHandleStopSessionRequest(const Json::Value & request,uid_t uid)553 Json::Value ResourceManager::JsonHandleStopSessionRequest(
554     const Json::Value& request, uid_t uid) {
555   Json::Value resp;
556   resp["request_type"] = ReqTyToStr(RequestType::StopSession);
557   resp["request_status"] = StatusToStr(RequestStatus::Failure);
558   if (!request.isMember("session_id") || !request["session_id"].isUInt()) {
559     auto err_msg = "Input event doesn't have a valid 'session_id' field";
560     LOG(WARNING) << err_msg;
561     resp["error"] = err_msg;
562     return resp;
563   }
564 
565   auto session_id = request["session_id"].asUInt();
566   LOG(INFO) << "Received StopSession Request for Session ID: " << session_id;
567 
568   auto it = managed_sessions_.find(session_id);
569   if (it == managed_sessions_.end()) {
570     auto msg = "Session not managed: " + std::to_string(session_id);
571     LOG(WARNING) << msg;
572     resp["error"] = msg;
573     return resp;
574   }
575 
576   if (it->second->GetUID() != uid) {
577     auto msg = "Effective user ID does not match session owner. socket uid: " +
578                std::to_string(uid);
579     LOG(WARNING) << msg;
580     resp["error"] = msg;
581     return resp;
582   }
583 
584   // while we could wait to see if any acquisitions fail and delay releasing
585   // resources until they are all finished, this operation is inherently
586   // destructive, so should a release operation fail, there is no satisfactory
587   // method for aborting the transaction. Instead, we try to release the
588   // resource and then can signal to the rest of the transaction the failure
589   // state
590   auto success = it->second->ReleaseAllResources();
591 
592   // release the names from the global list for reuse in future requests
593   for (auto& iface : it->second->GetActiveInterfaces()) {
594     active_interfaces_.erase(iface);
595   }
596 
597   if (success) {
598     managed_sessions_.erase(it);
599     resp["request_status"] = StatusToStr(RequestStatus::Success);
600   } else {
601     resp["error"] =
602         "unknown, allocd experienced an error ending the session id: " +
603         std::to_string(session_id);
604   }
605 
606   return resp;
607 }
608 
FindSession(uint32_t id)609 std::optional<std::shared_ptr<Session>> ResourceManager::FindSession(
610     uint32_t id) {
611   auto it = managed_sessions_.find(id);
612   if (it == managed_sessions_.end()) {
613     return std::nullopt;
614   } else {
615     return it->second;
616   }
617 }
618 
619 }  // namespace cuttlefish
620