From 14f63cb3244e976af2031671c91bb37035db24a4 Mon Sep 17 00:00:00 2001 From: stephb9959 Date: Thu, 19 Oct 2023 11:08:48 -0700 Subject: [PATCH] https://telecominfraproject.atlassian.net/browse/WIFI-12692 Signed-off-by: stephb9959 --- PROTOCOL.md | 20 +- openapi/owgw.yaml | 208 +++++++++++++++++- src/RESTAPI/RESTAPI_device_commandHandler.cpp | 148 ++++++++++++- src/RESTAPI/RESTAPI_device_commandHandler.h | 6 + src/RESTObjects/RESTAPI_GWobjects.cpp | 21 ++ src/RESTObjects/RESTAPI_GWobjects.h | 15 ++ src/framework/ow_constants.h | 20 +- 7 files changed, 426 insertions(+), 12 deletions(-) diff --git a/PROTOCOL.md b/PROTOCOL.md index 91d06690..aaf7be1d 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -861,16 +861,18 @@ The device should answer: ##### Kick ```json -{ "action" : "kick" , +{ + "action" : "kick" , "addr" : , - "reson": , (default: 5, https://www.cisco.com/assets/sol/sb/WAP371_Emulators/WAP371_Emulator_v1-0-1-5/help/Apx_ReasonCodes2.html) + "reason": , (default: 5, https://www.cisco.com/assets/sol/sb/WAP371_Emulators/WAP371_Emulator_v1-0-1-5/help/Apx_ReasonCodes2.html) "ban_time": (seconds, optional) } ``` ##### Channel Switch Announcement ```json -{ "action" : "channel_switch" , +{ + "action" : "channel_switch" , "bssid" : , (all other SSIDs on the same radio will perform the same action) "channel" : (HT/HW mode will be retained upon issuing the CSA) } @@ -878,7 +880,8 @@ The device should answer: ##### Change TX-Power ```json -{ "action" : "tx_power" , +{ + "action" : "tx_power" , "bssid" : , (all other SSIDs on the same radio will perform the same action) "level" : (DBm inside the positive number space) } @@ -886,7 +889,8 @@ The device should answer: ##### Beacon Scan ```json -{ "action" : "beacon_request" , +{ + "action" : "beacon_request" , "addr" : , "ssid": , (the SSID the client shall scan for on all frequencies), "channel": (the channel that shall be scanned) @@ -895,7 +899,8 @@ The device should answer: ##### BSS Transition ```json -{ "action" : "bss_transition" , +{ + "action" : "bss_transition" , "addr" : , "neighbors": [ ], (an array of BSSIDs the client shall consider as roamin candidates) } @@ -903,7 +908,8 @@ The device should answer: ##### Update neighbours ```json -{ "action" : "neighbors" , +{ + "action" : "neighbors" , "bssid" : , (the SSID of the specific VAP) "neighbors": [ [ , , ] ] } diff --git a/openapi/owgw.yaml b/openapi/owgw.yaml index 6be9a127..2b87cd55 100644 --- a/openapi/owgw.yaml +++ b/openapi/owgw.yaml @@ -903,6 +903,106 @@ components: kafkaClients: type: integer + RRM_Kick: + type: object + properties: + action: + type: string + enum: + - kick + addr: + type: string + format: mac + reason: + type: integer + default: 5 + ban_time: + type: integer + format: int64 + + RRM_channel_switch: + type: object + properties: + action: + type: string + enum: + - channel_switch + bssid: + type: string + format: mac + channel: + type: integer + + RRM_tx_power: + type: object + properties: + action: + type: string + enum: + - tx_power + bssid: + type: string + format: mac + level: + type: integer + + RRM_beacon_request: + type: object + properties: + action: + type: string + enum: + - beacon_request + addr: + type: string + format: mac + ssid: + type: string + channel: + type: integer + + RRM_bss_transition: + type: object + properties: + action: + type: string + enum: + - bss_transition + addr: + type: string + format: mac + neighbors: + type: array + items: + type: string + format: mac + + RRM_neighbors: + type: object + properties: + action: + type: string + enum: + - neighbors + bssid: + type: string + format: mac + neighbors: + type: array + items: + type: string + format: mac + + RRM_action: + type: object + oneOf: + - $ref: '#/components/schemas/RRM_Kick' + - $ref: '#/components/schemas/RRM_channel_switch' + - $ref: '#/components/schemas/RRM_tx_power' + - $ref: '#/components/schemas/RRM_beacon_request' + - $ref: '#/components/schemas/RRM_bss_transition' + - $ref: '#/components/schemas/RRM_neighbors' + ######################################################################################### ## ## These are endpoints that all services in the uCentral stack must provide @@ -1435,6 +1535,28 @@ components: userName: type: string + DeviceTransferRequest: + type: object + properties: + serialNumber: + type: string + format: uuid + server: + type: string + format: hostname + port: + type: integer + format: int32 + + DeviceCertificateUpdateRequest: + type: object + properties: + serialNumber: + type: string + format: uuid + encodedCertificate: + type: string + format: base64 paths: /devices: @@ -2790,6 +2912,90 @@ paths: 404: $ref: '#/components/responses/NotFound' + /device/{serialNumber}/rrm: + post: + tags: + - Commands + summary: Send RRM commands to a device. + operationId: sendRRMcommandsForADevice + parameters: + - in: path + name: serialNumber + schema: + type: string + required: true + requestBody: + description: Commands to send + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/RRM_action' + responses: + 200: + $ref: '#/components/responses/Success' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + + /device/{serialNumber}/transfer: + post: + tags: + - Commands + summary: Transfer a device to a new redirector. + operationId: transferDevice + parameters: + - in: path + name: serialNumber + schema: + type: string + required: true + requestBody: + description: Transfer details + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DeviceTransferRequest' + responses: + 200: + $ref: '#/components/responses/Success' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + + /device/{serialNumber}/certupdate: + post: + tags: + - Commands + summary: Update the certificates for a device. + operationId: updateCertificates + parameters: + - in: path + name: serialNumber + schema: + type: string + required: true + requestBody: + description: Certificate update details + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DeviceCertificateUpdateRequest' + responses: + 200: + $ref: '#/components/responses/Success' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + /ouis: get: tags: @@ -3296,8 +3502,6 @@ paths: 404: $ref: '#/components/responses/NotFound' - - /deviceDashboard: get: tags: diff --git a/src/RESTAPI/RESTAPI_device_commandHandler.cpp b/src/RESTAPI/RESTAPI_device_commandHandler.cpp index 05c59ccb..69958aac 100644 --- a/src/RESTAPI/RESTAPI_device_commandHandler.cpp +++ b/src/RESTAPI/RESTAPI_device_commandHandler.cpp @@ -163,8 +163,10 @@ namespace OpenWifi { {APCommands::Commands::telemetry, false, true, &RESTAPI_device_commandHandler::Telemetry, 30000ms}, {APCommands::Commands::ping, false, true, &RESTAPI_device_commandHandler::Ping, 60000ms}, - {APCommands::Commands::script, false, true, &RESTAPI_device_commandHandler::Script, - 300000ms}}; + {APCommands::Commands::rrm, false, true, &RESTAPI_device_commandHandler::RRM, 60000ms}, + {APCommands::Commands::certupdate, false, true, &RESTAPI_device_commandHandler::CertUpdate, 60000ms}, + {APCommands::Commands::transfer, false, true, &RESTAPI_device_commandHandler::Transfer, 60000ms} + }; void RESTAPI_device_commandHandler::DoPost() { if (!ValidateParameters()) { @@ -1339,4 +1341,146 @@ namespace OpenWifi { } return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); } + + void RESTAPI_device_commandHandler::RRM( + const std::string &CMD_UUID, uint64_t CMD_RPC, + [[maybe_unused]] std::chrono::milliseconds timeout, + [[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) { + poco_debug(Logger_, fmt::format("RRM({},{}): TID={} user={} serial={}", CMD_UUID, + CMD_RPC, TransactionId_, Requester(), SerialNumber_)); + + if(IsDeviceSimulated(SerialNumber_)) { + CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::SimulatedDeviceNotSupported); + return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported); + } + + const auto & Actions = *ParsedBody_->getArray("actions"); + + // perform some validation on the commands. + for(const auto &action:Actions) { + auto ActionDetails = action.extract(); + if(!ActionDetails->has("action")) { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + auto ActionStr = ActionDetails->get("action").toString(); + if( ActionStr != "kick" + && ActionStr != "channel_switch" + && ActionStr != "tx_power" + && ActionStr != "beacon_request" + && ActionStr != "bss_transition" + && ActionStr != "neighbors" ) { + return BadRequest(RESTAPI::Errors::InvalidRRMAction); + } + } + + Poco::JSON::Object Params; + Params.set(uCentralProtocol::SERIAL, SerialNumber_); + Params.set(uCentralProtocol::ACTIONS, Actions); + + GWObjects::CommandDetails Cmd; + Cmd.SerialNumber = SerialNumber_; + Cmd.SubmittedBy = Requester(); + Cmd.UUID = CMD_UUID; + Cmd.Command = uCentralProtocol::RRM; + std::ostringstream os; + Params.stringify(os); + Cmd.Details = os.str(); + Cmd.RunAt = 0; + Cmd.ErrorCode = 0; + Cmd.WaitingForFile = 0; + + return RESTAPI_RPC::WaitForCommand(CMD_RPC, APCommands::Commands::rrm, false, Cmd, + Params, *Request, *Response, timeout, nullptr, this, + Logger_); + } + + void RESTAPI_device_commandHandler::Transfer( + const std::string &CMD_UUID, uint64_t CMD_RPC, + [[maybe_unused]] std::chrono::milliseconds timeout, + [[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) { + + if(UserInfo_.userinfo.userRole != SecurityObjects::ROOT && + UserInfo_.userinfo.userRole != SecurityObjects::ADMIN) { + CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::ACCESS_DENIED); + return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); + } + + poco_debug(Logger_, fmt::format("TRANSFER({},{}): TID={} user={} serial={}", CMD_UUID, + CMD_RPC, TransactionId_, Requester(), SerialNumber_)); + + if(IsDeviceSimulated(SerialNumber_)) { + CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::SimulatedDeviceNotSupported); + return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported); + } + + GWObjects::DeviceTransferRequest TR; + if(!TR.from_json(ParsedBody_)) { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + + GWObjects::CommandDetails Cmd; + Cmd.SerialNumber = SerialNumber_; + Cmd.SubmittedBy = Requester(); + Cmd.UUID = CMD_UUID; + Cmd.Command = uCentralProtocol::TRANSFER; + std::ostringstream os; + ParsedBody_->stringify(os); + Cmd.Details = os.str(); + Cmd.RunAt = 0; + Cmd.ErrorCode = 0; + Cmd.WaitingForFile = 0; + + return RESTAPI_RPC::WaitForCommand(CMD_RPC, APCommands::Commands::transfer, false, Cmd, + *ParsedBody_, *Request, *Response, timeout, nullptr, this, + Logger_); + } + + void RESTAPI_device_commandHandler::CertUpdate( + const std::string &CMD_UUID, uint64_t CMD_RPC, + [[maybe_unused]] std::chrono::milliseconds timeout, + [[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) { + + poco_debug(Logger_, fmt::format("CERTUPDATE({},{}): TID={} user={} serial={}", CMD_UUID, + CMD_RPC, TransactionId_, Requester(), SerialNumber_)); + + if(UserInfo_.userinfo.userRole != SecurityObjects::ROOT && + UserInfo_.userinfo.userRole != SecurityObjects::ADMIN) { + CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::ACCESS_DENIED); + return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); + } + + if(IsDeviceSimulated(SerialNumber_)) { + CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::SimulatedDeviceNotSupported); + return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported); + } + + GWObjects::DeviceCertificateUpdateRequest CR; + if(!CR.from_json(ParsedBody_)) { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + + GWObjects::DeviceTransferRequest TR; + if(!TR.from_json(ParsedBody_)) { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + + GWObjects::CommandDetails Cmd; + Cmd.SerialNumber = SerialNumber_; + Cmd.SubmittedBy = Requester(); + Cmd.UUID = CMD_UUID; + Cmd.Command = uCentralProtocol::CERTUPDATE; + std::ostringstream os; + ParsedBody_->stringify(os); + Cmd.Details = os.str(); + Cmd.RunAt = 0; + Cmd.ErrorCode = 0; + Cmd.WaitingForFile = 0; + + return RESTAPI_RPC::WaitForCommand(CMD_RPC, APCommands::Commands::certupdate, false, Cmd, + *ParsedBody_, *Request, *Response, timeout, nullptr, this, + Logger_); + + + } + } // namespace OpenWifi diff --git a/src/RESTAPI/RESTAPI_device_commandHandler.h b/src/RESTAPI/RESTAPI_device_commandHandler.h index 44e58329..57c84a06 100644 --- a/src/RESTAPI/RESTAPI_device_commandHandler.h +++ b/src/RESTAPI/RESTAPI_device_commandHandler.h @@ -62,6 +62,12 @@ namespace OpenWifi { const GWObjects::DeviceRestrictions &R); void Script(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout, const GWObjects::DeviceRestrictions &R); + void RRM(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout, + const GWObjects::DeviceRestrictions &R); + void CertUpdate(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout, + const GWObjects::DeviceRestrictions &R); + void Transfer(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout, + const GWObjects::DeviceRestrictions &R); static auto PathName() { return std::list{"/api/v1/device/{serialNumber}/{command}"}; diff --git a/src/RESTObjects/RESTAPI_GWobjects.cpp b/src/RESTObjects/RESTAPI_GWobjects.cpp index 6010d8d4..505b7794 100644 --- a/src/RESTObjects/RESTAPI_GWobjects.cpp +++ b/src/RESTObjects/RESTAPI_GWobjects.cpp @@ -694,4 +694,25 @@ namespace OpenWifi::GWObjects { return false; } + bool DeviceTransferRequest::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "serialNumber", serialNumber); + field_from_json(Obj, "server", server); + field_from_json(Obj, "port", port); + return true; + } catch (const Poco::Exception &E) { + } + return false; + } + + bool DeviceCertificateUpdateRequest::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "serialNumber", serialNumber); + field_from_json(Obj, "encodedCertificate", encodedCertificate); + return true; + } catch (const Poco::Exception &E) { + } + return false; + } + } // namespace OpenWifi::GWObjects diff --git a/src/RESTObjects/RESTAPI_GWobjects.h b/src/RESTObjects/RESTAPI_GWobjects.h index 10423567..d5e696de 100644 --- a/src/RESTObjects/RESTAPI_GWobjects.h +++ b/src/RESTObjects/RESTAPI_GWobjects.h @@ -496,4 +496,19 @@ namespace OpenWifi::GWObjects { } } + struct DeviceTransferRequest { + std::string serialNumber; + std::string server; + std::uint64_t port; + + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct DeviceCertificateUpdateRequest { + std::string serialNumber; + std::string encodedCertificate; + + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + } // namespace OpenWifi::GWObjects diff --git a/src/framework/ow_constants.h b/src/framework/ow_constants.h index 33f88190..ed4e246b 100644 --- a/src/framework/ow_constants.h +++ b/src/framework/ow_constants.h @@ -431,6 +431,8 @@ namespace OpenWifi::RESTAPI::Errors { static const struct msg InvalidRadsecSecret { 1190, "Invalid Radsec Secret." }; static const struct msg InvalidRadiusServer { 1191, "Invalid Radius Server." }; + static const struct msg InvalidRRMAction { 1192, "Invalid RRM Action." }; + static const struct msg SimulationDoesNotExist { 7000, "Simulation Instance ID does not exist." }; @@ -561,6 +563,10 @@ namespace OpenWifi::RESTAPI::Protocol { static const char *CONTENTDISPOSITION = "Content-Disposition"; static const char *CONTENTTYPE = "Content-Type"; + static const char *TRANSFER = "transfer"; + static const char *CERTUPDATE = "certupdate"; + static const char *RRM = "rrm"; + static const char *REQUIREMENTS = "requirements"; static const char *PASSWORDPATTERN = "passwordPattern"; static const char *ACCESSPOLICY = "accessPolicy"; @@ -678,6 +684,12 @@ namespace OpenWifi::uCentralProtocol { static const char *RADIUSCOA = "coa"; static const char *RADIUSDST = "dst"; static const char *IES = "ies"; + + static const char *TRANSFER = "transfer"; + static const char *CERTUPDATE = "certupdate"; + static const char *RRM = "rrm"; + static const char *ACTIONS = "actions"; + } // namespace OpenWifi::uCentralProtocol namespace OpenWifi::uCentralProtocol::Events { @@ -770,6 +782,9 @@ namespace OpenWifi::APCommands { telemetry, ping, script, + rrm, + certupdate, + transfer, unknown }; @@ -782,7 +797,10 @@ namespace OpenWifi::APCommands { RESTAPI::Protocol::LEDS, RESTAPI::Protocol::TRACE, RESTAPI::Protocol::REQUEST, RESTAPI::Protocol::WIFISCAN, RESTAPI::Protocol::EVENTQUEUE, RESTAPI::Protocol::TELEMETRY, - RESTAPI::Protocol::PING, RESTAPI::Protocol::SCRIPT}; + RESTAPI::Protocol::PING, RESTAPI::Protocol::SCRIPT, + RESTAPI::Protocol::RRM, RESTAPI::Protocol::CERTUPDATE, + RESTAPI::Protocol::TRANSFER + }; inline const char *to_string(Commands Cmd) { return uCentralAPCommands[(uint8_t)Cmd]; }