Skip to content

Commit

Permalink
improved logging around websocket connection; allowing 8 characters A…
Browse files Browse the repository at this point in the history
…uthorizationKey now

Signed-off-by: pietfried <[email protected]>
  • Loading branch information
Pietfried committed Jul 11, 2023
1 parent b80fcaa commit a9e5437
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 39 deletions.
2 changes: 1 addition & 1 deletion config/v16/profile_schemas/Security.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"AuthorizationKey": {
"type": "string",
"description": "The basic authentication password is used for HTTP Basic Authentication",
"minLength": 16,
"minLength": 8,
"readOnly": false
},
"CertificateSignedMaxChainSize": {
Expand Down
3 changes: 3 additions & 0 deletions include/ocpp/common/websocket/websocket_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ class WebsocketBase {
/// \brief getter for authorization header for connection with basic authentication
std::optional<std::string> getAuthorizationHeader();

/// \brief Logs websocket connection error
void log_on_fail(const std::error_code& ec, const boost::system::error_code& transport_ec, const int http_status);

/// \brief send a websocket ping
virtual void ping() = 0;

Expand Down
10 changes: 5 additions & 5 deletions include/ocpp/v16/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,11 @@ std::ostream& operator<<(std::ostream& os, const SupportedFeatureProfiles& suppo

/// \brief Contains the different connection states of the charge point
enum ChargePointConnectionState {
Disconnected,
Connected,
Booted,
Pending,
Rejected,
Disconnected, // state when disconnected
Connected, // state when ws is connected
Booted, // state when ws is connected and BootNotifcation had been Accepted
Pending, // state when ws is connected and state when BootNotifcation is Pending
Rejected, // state when ws is connected and state when BootNotifcation had been Rejected
};
namespace conversions {
/// \brief Converts the given ChargePointConnectionState \p e to std::string
Expand Down
19 changes: 16 additions & 3 deletions lib/ocpp/common/websocket/websocket_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ WebsocketBase::WebsocketBase(const WebsocketConnectionOptions& connection_option
message_callback(nullptr),
reconnect_timer(nullptr) {
this->ping_timer = std::make_unique<Everest::SteadyTimer>();
const auto auth_key = connection_options.authorization_key;
if (auth_key.has_value() and auth_key.value().length() < 16) {
EVLOG_warning << "AuthorizationKey with only " << auth_key.value().length()
<< " characters has been configured";
}
}

WebsocketBase::~WebsocketBase() {
Expand Down Expand Up @@ -76,17 +81,25 @@ bool WebsocketBase::is_connected() {
std::optional<std::string> WebsocketBase::getAuthorizationHeader() {
std::optional<std::string> auth_header = std::nullopt;
const auto authorization_key = this->connection_options.authorization_key;
if (authorization_key) {
if (authorization_key.has_value()) {
EVLOG_debug << "AuthorizationKey present, encoding authentication header";
std::string plain_auth_header = this->connection_options.chargepoint_id + ":" + authorization_key.value();
auth_header.emplace(std::string("Basic ") + websocketpp::base64_encode(plain_auth_header));

EVLOG_info << "Basic Auth header: " << auth_header.value();
EVLOG_debug << "Basic Auth header: " << auth_header.value();
}

return auth_header;
}

void WebsocketBase::log_on_fail(const std::error_code& ec, const boost::system::error_code& transport_ec,
const int http_status) {
EVLOG_error << "Failed to connect to websocket server"
<< ", error_code: " << ec.value() << ", reason: " << ec.message()
<< ", HTTP response code: " << http_status << ", category: " << ec.category().name()
<< ", transport error code: " << transport_ec.value()
<< ", Transport error category: " << transport_ec.category().name();
}

void WebsocketBase::set_websocket_ping_interval(int32_t interval_s) {
if (this->ping_timer) {
this->ping_timer->stop();
Expand Down
15 changes: 5 additions & 10 deletions lib/ocpp/common/websocket/websocket_plain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ void WebsocketPlain::connect_plain(int32_t security_profile, bool try_once) {

void WebsocketPlain::on_open_plain(client* c, websocketpp::connection_hdl hdl, int32_t security_profile) {
(void)c; // client is not used in this function
EVLOG_info << "Connected to plain websocket successfully. Executing connected callback";
EVLOG_info << "OCPP client successfully connected to plain websocket server";
this->m_is_connected = true;
this->connection_options.security_profile = security_profile;
this->set_websocket_ping_interval(this->connection_options.ping_interval_s);
Expand Down Expand Up @@ -201,17 +201,12 @@ void WebsocketPlain::on_close_plain(client* c, websocketpp::connection_hdl hdl)

void WebsocketPlain::on_fail_plain(client* c, websocketpp::connection_hdl hdl, bool try_once) {
client::connection_ptr con = c->get_con_from_hdl(hdl);
auto error_code = con->get_ec();
EVLOG_error << "Failed to connect to plain websocket server " << con->get_response_header("Server")
<< ", code: " << error_code.value() << ", reason: " << error_code.message()
<< ", response code: " << con->get_response_code();
EVLOG_error << "Failed to connect to plain websocket server "
<< ", code: " << con->get_transport_ec().value() << ", reason: " << con->get_transport_ec().message()
<< ", category: " << con->get_transport_ec().category().name();
EVLOG_error << "Close code: " << con->get_local_close_code() << ", close reason: " << con->get_local_close_reason();
const auto ec = con->get_ec();
this->log_on_fail(ec, con->get_transport_ec(), con->get_response_code());

// when connecting with new security profile websocket should only try to connect once
if (!try_once) {
this->reconnect(error_code, this->reconnect_interval_ms);
this->reconnect(ec, this->reconnect_interval_ms);
} else {
this->disconnected_callback();
}
Expand Down
15 changes: 4 additions & 11 deletions lib/ocpp/common/websocket/websocket_tls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ void WebsocketTLS::connect_tls(int32_t security_profile, bool try_once) {
}
void WebsocketTLS::on_open_tls(tls_client* c, websocketpp::connection_hdl hdl, int32_t security_profile) {
(void)c; // tlc_client is not used in this function
EVLOG_info << "Connected to TLS websocket successfully";
EVLOG_info << "OCPP client successfully connected to TLS websocket server";
this->m_is_connected = true;
this->connection_options.security_profile = security_profile;
this->set_websocket_ping_interval(this->connection_options.ping_interval_s);
Expand Down Expand Up @@ -289,15 +289,8 @@ void WebsocketTLS::on_close_tls(tls_client* c, websocketpp::connection_hdl hdl)
}
void WebsocketTLS::on_fail_tls(tls_client* c, websocketpp::connection_hdl hdl, bool try_once) {
tls_client::connection_ptr con = c->get_con_from_hdl(hdl);
auto error_code = con->get_ec();
auto transport_ec = con->get_transport_ec();
EVLOG_error << "Failed to connect to TLS websocket server " << con->get_response_header("Server")
<< ", code: " << error_code.value() << ", reason: " << error_code.message()
<< ", response code: " << con->get_response_code();
EVLOG_error << "Failed to connect to TLS websocket server "
<< ", code: " << transport_ec.value() << ", reason: " << transport_ec.message()
<< ", category: " << transport_ec.category().name();
EVLOG_error << "Close code: " << con->get_local_close_code() << ", close reason: " << con->get_local_close_reason();
const auto ec = con->get_ec();
this->log_on_fail(ec, con->get_transport_ec(), con->get_response_code());

// TODO(piet): Trigger SecurityEvent in case InvalidCentralSystemCertificate

Expand All @@ -308,7 +301,7 @@ void WebsocketTLS::on_fail_tls(tls_client* c, websocketpp::connection_hdl hdl, b
}

if (!try_once) {
this->reconnect(error_code, this->reconnect_interval_ms);
this->reconnect(ec, this->reconnect_interval_ms);
} else {
this->disconnected_callback();
}
Expand Down
4 changes: 2 additions & 2 deletions lib/ocpp/v16/charge_point_configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2175,11 +2175,11 @@ ConfigurationStatus ChargePointConfiguration::set(CiString<50> key, CiString<500
}
if (key == "AuthorizationKey") {
std::string authorization_key = value.get();
if (authorization_key.length() >= 16) {
if (authorization_key.length() >= 8) {
this->setAuthorizationKey(value.get());
return ConfigurationStatus::Accepted;
} else {
EVLOG_debug << "AuthorizationKey is < 16 bytes";
EVLOG_warning << "Attempt to change AuthorizationKey to value with < 8 characters";
return ConfigurationStatus::Rejected;
}
}
Expand Down
17 changes: 10 additions & 7 deletions lib/ocpp/v16/charge_point_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,10 @@ void ChargePointImpl::connected_callback() {
}
break;
}
case ChargePointConnectionState::Pending: {
// in Pending state this can happen when we reconnected while the BootNotification had not been yet accepted
break;
}
default:
EVLOG_error << "Connected but not in state 'Disconnected' or 'Booted', something is wrong: "
<< this->connection_state;
Expand Down Expand Up @@ -1023,16 +1027,15 @@ void ChargePointImpl::handleBootNotificationResponse(ocpp::CallResult<BootNotifi
}
case RegistrationStatus::Pending:
this->connection_state = ChargePointConnectionState::Pending;

EVLOG_debug << "BootNotification response is pending.";
EVLOG_info << "BootNotification response is pending.";
this->boot_notification_timer->timeout(std::chrono::seconds(call_result.msg.interval));
break;
default:
this->connection_state = ChargePointConnectionState::Rejected;
// In this state we are not allowed to send any messages to the central system, even when
// requested. The first time we are allowed to send a message (a BootNotification) is
// after boot_time + heartbeat_interval if the msg.interval is 0, or after boot_timer + msg.interval
EVLOG_debug << "BootNotification was rejected, trying again in " << this->configuration->getHeartbeatInterval()
EVLOG_info << "BootNotification was rejected, trying again in " << this->configuration->getHeartbeatInterval()
<< "s";

this->boot_notification_timer->timeout(std::chrono::seconds(call_result.msg.interval));
Expand Down Expand Up @@ -1128,19 +1131,19 @@ void ChargePointImpl::handleChangeConfigurationRequest(ocpp::Call<ChangeConfigur
} else if (call.msg.key == "ClockAlignedDataInterval") {
this->update_clock_aligned_meter_values_interval();
} else if (call.msg.key == "AuthorizationKey") {
/*SECURITYLOG*/ EVLOG_debug << "AuthorizationKey was changed by central system";
EVLOG_info << "AuthorizationKey was changed by central system";
if (this->configuration->getSecurityProfile() == 0) {
EVLOG_debug << "AuthorizationKey was changed while on security profile 0.";
EVLOG_info << "AuthorizationKey was changed while on security profile 0.";
} else if (this->configuration->getSecurityProfile() == 1 ||
this->configuration->getSecurityProfile() == 2) {
EVLOG_debug
EVLOG_info
<< "AuthorizationKey was changed while on security profile 1 or 2. Reconnect Websocket.";
ocpp::CallResult<ChangeConfigurationResponse> call_result(response, call.uniqueId);
this->send<ChangeConfigurationResponse>(call_result);
responded = true;
this->websocket->reconnect(std::error_code(), 1000);
} else {
EVLOG_debug << "AuthorizationKey was changed while on security profile 3. Nothing to do.";
EVLOG_info << "AuthorizationKey was changed while on security profile 3. Nothing to do.";
}
} else if (call.msg.key == "SecurityProfile") {
try {
Expand Down

0 comments on commit a9e5437

Please sign in to comment.