it2s-itss-facilities/src/tpm.c

1453 lines
61 KiB
C

#include "tpm.h"
#include "facilities.h"
#include <it2s-tender/constants.h>
#include <itss-security/SecurityRequest.h>
#include <itss-security/SecurityReply.h>
#include <itss-facilities/FacilitiesIndication.h>
#include <it2s-tender/time.h>
#include <itss-transport/TransportRequest.h>
#include <it2s-tender/space.h>
#include <it2s-tender/geodesy.h>
#include <it2s-tender/recorder.h>
#include <it2s-tender/packet.h>
#include <stdint.h>
#include <string.h>
static char* tts(int type) {
static char* stype[] = {"null", "entry", "exit", "single"};
if (type < 1 || type > 3) return stype[0];
else return stype[type];
}
int tpm_is_inside_zone(void* fc, tolling_info_t* ti) {
int rv = 0;
facilities_t* facilities = (facilities_t*) fc;
double point[2];
itss_space_lock();
itss_space_get();
point[0] = epv.space.latitude/1.0e7;
point[1] = epv.space.longitude/1.0e7;
itss_space_unlock();
if (itss_is_inside_polygon(point, ti->zone.polygon, ti->zone.polygon_len)) {
return 1;
}
return 0;
}
int tpm_should_retransmit(void* fc) {
facilities_t* facilities = (facilities_t*) fc;
tolling_t* tolling = &facilities->tolling;
uint64_t now = itss_ts_get(TIME_MICROSECONDS);
if (tolling->station.obu.rt_on) {
if (now > tolling->station.obu.rt_init + TOLLING_RT_TIMEOUT_MS*1000) {
tolling->station.obu.rt_on = false;
return 0;
}
if (now > tolling->station.obu.rt_t_trigger + TOLLING_RT_PERIOD_MS*1000) {
return 1;
}
}
return 0;
}
int tpm_pay(void* fc, tolling_info_t* info, void* security_socket, uint8_t* neighbour, uint8_t* dst_addr) {
int rv = 0;
facilities_t* facilities = (facilities_t*) fc;
tolling_t* tolling = (tolling_t*) &facilities->tolling;
const size_t buf_len = 2048;
uint8_t tpm_uper[buf_len];
uint8_t buf[buf_len];
if (!tolling->enabled) {
syslog_debug("[facilities] [tolling] tolling is disabled");
return rv;
}
TPM_t* tpm = NULL;
TransportRequest_t* tr = NULL;
SecurityRequest_t* sreq = NULL;
SecurityReply_t* srep = NULL;
FacilitiesIndication_t* fi = NULL;
pthread_mutex_lock(&tolling->lock);
tolling->station.obu.active = true;
tolling->station.obu.nonce = rand() + 1;
syslog_info("[facilities] [tolling] issuing toll %s.request | client: %ld nonce: %ld", tts(info->asn->tollType), tolling->station.obu.client_id, tolling->station.obu.nonce);
// TPM
tpm = calloc(1, sizeof(TPM_t));
tpm->header.messageID = 117;
tpm->header.protocolVersion = 0;
pthread_mutex_lock(&facilities->id.lock);
tpm->header.stationID = facilities->id.station_id;
pthread_mutex_unlock(&facilities->id.lock);
tpm->tpm = calloc(1, sizeof(TollingPaymentMessage_t));
// timestamp
asn_ulong2INTEGER(&tpm->tpm->timestamp, itss_time_get());
// stationType
tpm->tpm->stationType = facilities->station_type;
// referencePosition
itss_space_lock();
itss_space_get();
tpm->tpm->referencePosition.altitude.altitudeValue = epv.space.altitude;
tpm->tpm->referencePosition.altitude.altitudeConfidence = epv.space.altitude_conf;
tpm->tpm->referencePosition.latitude = epv.space.latitude;
tpm->tpm->referencePosition.longitude = epv.space.longitude;
uint16_t lat_conf = epv.space.latitude_conf;
uint16_t lon_conf = epv.space.longitude_conf;
itss_space_unlock();
if (lat_conf > lon_conf) {
tpm->tpm->referencePosition.positionConfidenceEllipse.semiMinorConfidence = lon_conf;
tpm->tpm->referencePosition.positionConfidenceEllipse.semiMajorConfidence = lat_conf;
tpm->tpm->referencePosition.positionConfidenceEllipse.semiMajorOrientation = 0;
} else {
tpm->tpm->referencePosition.positionConfidenceEllipse.semiMinorConfidence = lon_conf;
tpm->tpm->referencePosition.positionConfidenceEllipse.semiMajorConfidence = lat_conf;
tpm->tpm->referencePosition.positionConfidenceEllipse.semiMajorOrientation = 900;
}
tpm->tpm->tollingType = calloc(1, sizeof(TollingType_t));
TollingType_t* type = tpm->tpm->tollingType;
switch (info->asn->tollType) {
case TollType_entry:
if (tolling->station.obu.toll_type != -1 &&
!tolling->station.obu.rt_on) {
syslog_err("[facilities] [tolling] trying to issue entry.request but current tolling state is %s - resetting",
tts(tolling->station.obu.toll_type));
tolling->station.obu.toll_type = -1;
tolling->station.obu.active = false;
ASN_STRUCT_FREE(asn_DEF_TPM, tolling->station.obu.entry_proof);
tolling->station.obu.entry_proof = NULL;
}
type->present = TollingType_PR_entry;
type->choice.entry.present = TollingEntry_PR_request;
type->choice.entry.choice.request.clientId = tolling->station.obu.client_id;
type->choice.entry.choice.request.infoId = info->asn->id;
type->choice.entry.choice.request.transactionNonce = tolling->station.obu.nonce;
break;
case TollType_exit:
type->present = TollingType_PR_exit;
type->choice.exit = calloc(1, sizeof(TollingExit_t));
type->choice.exit->present = TollingExit_PR_request;
type->choice.exit->choice.request = calloc(1, sizeof(TollingExitRequest_t));
type->choice.exit->choice.request->clientId = tolling->station.obu.client_id;
type->choice.exit->choice.request->infoId = info->asn->id;
type->choice.exit->choice.request->transactionNonce = tolling->station.obu.nonce;
if (tolling->station.obu.toll_type != TollingType_PR_entry &&
!tolling->station.obu.rt_on) {
syslog_err("[facilities] [tolling] trying to issue exit.request but current tolling state is %s - will not provide entry proof",
tts(tolling->station.obu.toll_type));
} else {
uint8_t b_tep[1024];
asn_enc_rval_t e_tep = uper_encode_to_buffer(&asn_DEF_TPM, NULL, tolling->station.obu.entry_proof, b_tep, 1024);
if (e_tep.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding TPM entry proof (%s)", e_tep.failed_type->name);
ASN_STRUCT_FREE(asn_DEF_TPM, tolling->station.obu.entry_proof);
tolling->station.obu.entry_proof = NULL;
rv = 1;
goto cleanup;
}
uper_decode_complete(NULL, &asn_DEF_TPM, (void**) &type->choice.exit->choice.request->entryProof, b_tep, (e_tep.encoded+7)/8);
}
break;
case TollType_single:
if (tolling->station.obu.toll_type != -1 &&
!tolling->station.obu.rt_on) {
syslog_err("[facilities] [tolling] trying to issue single.request but current tolling state is %s",
tts(tolling->station.obu.toll_type));
rv = 1;
goto cleanup;
}
type->present = TollingType_PR_single;
type->choice.single.present = TollingSingle_PR_request;
type->choice.single.choice.request.clientId = tolling->station.obu.client_id;
type->choice.single.choice.request.infoId = info->asn->id;
type->choice.single.choice.request.transactionNonce = tolling->station.obu.nonce;
break;
}
tolling->station.obu.toll_type = info->asn->tollType;
// Encode TollingPaymentMessage
asn_enc_rval_t enc = oer_encode_to_buffer(&asn_DEF_TollingPaymentMessage, NULL, tpm->tpm, buf, buf_len);
if (enc.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding TollingPaymentMessage (%s)", enc.failed_type->name);
rv = 1;
goto cleanup;
}
if (tolling->protocol.p == TOLLING_PROTOCOL_SIMPLE) {
// Sign
sreq = calloc(1, sizeof(SecurityRequest_t));
sreq->present = SecurityRequest_PR_sign;
sreq->choice.sign.data.size = enc.encoded;
sreq->choice.sign.data.buf = malloc(enc.encoded);
memcpy(sreq->choice.sign.data.buf, buf, enc.encoded);
buf[0] = 4;
enc = oer_encode_to_buffer(&asn_DEF_SecurityRequest, NULL, sreq, buf+1, 2047);
syslog_debug("[facilities]->[security] SecurityRequest.sign (%ldB)", enc.encoded+1);
zmq_send(security_socket, buf, enc.encoded+1, 0);
int32_t rl = zmq_recv(security_socket, buf, buf_len, 0);
syslog_debug("[facilities]<-[security] SecurityReply.sign (%dB)", rl);
if (oer_decode(NULL, &asn_DEF_SecurityReply, (void**) &srep, buf, rl).code) {
syslog_err("[facilities] SecurityReply.sign decode failure");
rv = 1;
goto cleanup;
}
if (srep->returnCode == SecurityReplyReturnCode_rejected) {
syslog_err("[facilities] SecurityReply.sign rejected");
rv = 1;
goto cleanup;
}
tpm->tpmSignature = calloc(1, sizeof(TpmSignature_t));
tpm->tpmSignature->r.size = srep->data->choice.sign.r.size;
tpm->tpmSignature->r.buf = malloc(srep->data->choice.sign.r.size);
memcpy(tpm->tpmSignature->r.buf, srep->data->choice.sign.r.buf, srep->data->choice.sign.r.size);
tpm->tpmSignature->s.size = srep->data->choice.sign.s.size;
tpm->tpmSignature->s.buf = malloc(srep->data->choice.sign.s.size);
memcpy(tpm->tpmSignature->s.buf, srep->data->choice.sign.s.buf, srep->data->choice.sign.s.size);
tpm->tpmSignature->signer.size = srep->data->choice.sign.signer.size;
tpm->tpmSignature->signer.buf = malloc(srep->data->choice.sign.signer.size);
memcpy(tpm->tpmSignature->signer.buf, srep->data->choice.sign.signer.buf, srep->data->choice.sign.signer.size);
tpm->tpmSignature->type = srep->data->choice.sign.type;
}
// Encode TPM
enc = uper_encode_to_buffer(&asn_DEF_TPM, NULL, tpm, tpm_uper, buf_len);
if (enc.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding TPM.request (%s)", enc.failed_type->name);
rv = 1;
goto cleanup;
}
size_t tpm_uper_len = (enc.encoded + 7) / 8;
// [transport] request (TR)
tr = calloc(1, sizeof(TransportRequest_t));
tr->present = TransportRequest_PR_packet;
uint64_t id = 0;
switch (facilities->tolling.protocol.p) {
case TOLLING_PROTOCOL_SIMPLE:
tr->choice.packet.present = TransportPacketRequest_PR_btp;
BTPPacketRequest_t* bpr = &tr->choice.packet.choice.btp;
bpr->gn.securityProfile.encrypt = true;
bpr->gn.securityProfile.sign = true;
bpr->gn.securityNeighbour = calloc(1, sizeof(OCTET_STRING_t));
bpr->gn.securityNeighbour->size = 8;
bpr->gn.securityNeighbour->buf = malloc(8);
memcpy(bpr->gn.securityNeighbour->buf, neighbour, 8);
bpr->data.size = tpm_uper_len;
bpr->data.buf = malloc(tpm_uper_len);
memcpy(bpr->data.buf, tpm_uper, tpm_uper_len);
bpr->id = itss_id(bpr->data.buf, bpr->data.size);
id = bpr->id;
bpr->destinationPort = 7011;
bpr->btpType = BTPType_btpB;
bpr->gn.destinationAddress.buf = malloc(6);
for (int i = 0; i < 6; ++i) {
bpr->gn.destinationAddress.buf[i] = 0xff;
}
bpr->gn.destinationAddress.size = 6;
bpr->gn.trafficClass = 2;
bpr->gn.packetTransportType = PacketTransportType_shb;
break;
case TOLLING_PROTOCOL_TLS:
case TOLLING_PROTOCOL_TLS_GN:
case TOLLING_PROTOCOL_TLS_SHS:
sreq = calloc(1, sizeof(SecurityRequest_t));
sreq->present = SecurityRequest_PR_tlsSend;
sreq->choice.tlsSend.data.buf = malloc(tpm_uper_len);
sreq->choice.tlsSend.data.size = tpm_uper_len;
memcpy(sreq->choice.tlsSend.data.buf, tpm_uper, tpm_uper_len);
tlsc_t* tlsc = tolling_tlsc_get(tolling, dst_addr, 7011);
if (tlsc) {
id = tlsc->id;
} else {
tlsc = tolling_tlsc_new(tolling, dst_addr, 7011);
id = tlsc->id;
}
++tlsc->nmsg;
sreq->choice.tlsSend.connId = id;
buf[0] = 4;
asn_enc_rval_t enc = oer_encode_to_buffer(&asn_DEF_SecurityRequest, NULL, sreq, buf+1, buf_len-1);
syslog_debug("[facilities] [tolling]-> SecurityRequest.tlsSend ->[security] | size:%ldB", enc.encoded);
zmq_send(security_socket, buf, enc.encoded+1, 0);
int rc = zmq_recv(security_socket, buf, buf_len, 0);
if (rc == -1) {
syslog_err("[facilities] [tolling]-> SecurityRequest.tlsSend ->[security] <TIMEOUT>");
rv = 1;
goto cleanup;
}
syslog_debug("[facilities] [tolling]<- SecurityReply.tlsSend <-[security] | size:%dB", rc);
SecurityReply_t* srep = NULL;
asn_dec_rval_t dec = oer_decode(NULL, &asn_DEF_SecurityReply, (void**) &srep, buf, buf_len);
if (dec.code ||
srep->returnCode != SecurityReplyReturnCode_accepted ||
!srep->data ||
srep->data->present != SecurityReplyData_PR_tlsSend) {
syslog_err("[facilities] [tolling]<- SecurityReply.tlsSend rejected");
rv = 1;
goto cleanup;
}
// TR TCP
tr->choice.packet.present = TransportPacketRequest_PR_tcp;
TCPPacketRequest_t* tcp = &tr->choice.packet.choice.tcp;
tcp->id = id;
tcp->destinationAddress = calloc(1, sizeof(OCTET_STRING_t));
tcp->destinationAddress->buf = malloc(16);
tcp->destinationAddress->size = 16;
memcpy(tcp->destinationAddress->buf, dst_addr, 16);
tcp->destinationPort = 7011;
tcp->sourcePort = 7011;
tcp->data.buf = malloc(srep->data->choice.tlsSend.data.size);
tcp->data.size = srep->data->choice.tlsSend.data.size;
memcpy(tcp->data.buf, srep->data->choice.tlsSend.data.buf, srep->data->choice.tlsSend.data.size);
if (tolling->protocol.p == TOLLING_PROTOCOL_TLS_GN ||
tolling->protocol.p == TOLLING_PROTOCOL_TLS_SHS) {
tcp->gn = calloc(1, sizeof(GeonetworkingOutboundOptions_t));
tcp->gn->packetTransportType = PacketTransportType_shb;
tcp->gn->destinationAddress.buf = calloc(1, 6);
tcp->gn->destinationAddress.size = 6;
tcp->gn->securityProfile.encrypt = false;
tcp->gn->securityProfile.sign = true;
}
break;
}
// encode TR
buf[0] = 4;
enc = oer_encode_to_buffer(&asn_DEF_TransportRequest, NULL, tr, buf+1, buf_len-1);
if (enc.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding TR TPM.request (%s)", enc.failed_type->name);
rv = 1;
goto cleanup;
}
itss_queue_send(facilities->tx_queue, buf, enc.encoded+1, ITSS_TRANSPORT, id,
tolling->protocol.p == TOLLING_PROTOCOL_SIMPLE ? "TR.packet.btp" : "TR.packet.tcp");
// Retransmission
if (!tolling->station.obu.rt_on) {
tolling->station.obu.rt_init = itss_ts_get(TIME_MICROSECONDS);
tolling->station.obu.rt_on = true;
}
tolling->station.obu.rt_t_trigger = itss_ts_get(TIME_MICROSECONDS);
// Logging
if (facilities->logging.dbms) {
pthread_mutex_lock(&facilities->id.lock);
uint64_t station_id = facilities->id.station_id;
pthread_mutex_unlock(&facilities->id.lock);
itss_db_add(facilities->logging.dbms, station_id, id, true, 117, NULL, tpm_uper, tpm_uper_len);
}
// send to [applications]
fi = calloc(1, sizeof(FacilitiesIndication_t));
fi->present = FacilitiesIndication_PR_message;
fi->choice.message.id = id;
fi->choice.message.itsMessageType = 7011;
fi->choice.message.data.size = tpm_uper_len;
fi->choice.message.data.buf = malloc(tpm_uper_len);
memcpy(fi->choice.message.data.buf, tpm_uper, tpm_uper_len);
enc = oer_encode_to_buffer(&asn_DEF_FacilitiesIndication, NULL, fi, buf+1, buf_len-1);
if (enc.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding FI TPM.request (%s)", enc.failed_type->name);
rv = 1;
goto cleanup;
}
itss_queue_send(facilities->tx_queue, buf, enc.encoded+1, ITSS_APPLICATIONS, id, "FI.message (TPM.request)");
if (facilities->logging.recorder) {
uint16_t buffer_len = 2048;
uint8_t buffer[buffer_len];
int e = itss_management_record_packet_sdu(
buffer,
buffer_len,
tpm_uper,
tpm_uper_len,
id,
itss_time_get(),
ITSS_FACILITIES,
true);
if (e != -1) {
itss_queue_send(facilities->tx_queue, buffer, e, ITSS_MANAGEMENT, id, "MReq.packet.set");
}
}
cleanup:
pthread_mutex_unlock(&tolling->lock);
ASN_STRUCT_FREE(asn_DEF_TPM, tpm);
ASN_STRUCT_FREE(asn_DEF_TransportRequest, tr);
ASN_STRUCT_FREE(asn_DEF_SecurityRequest, sreq);
ASN_STRUCT_FREE(asn_DEF_SecurityReply, srep);
ASN_STRUCT_FREE(asn_DEF_FacilitiesIndication, fi);
return rv;
}
static void rsu_handle_recv(facilities_t* facilities, TPM_t* tpm_rx, void* security_socket, uint8_t* neighbour, uint8_t* src_addr) {
if (!tpm_rx->tpm->tollingType) {
syslog_err("[facilities] [tolling] received TPM does not have a type");
return;
}
TollingType_t* type_rx = tpm_rx->tpm->tollingType;
uint64_t client_id, nonce, info_id;
const uint32_t buf_len = 1800;
uint8_t buf[buf_len];
uint8_t tpm_uper[buf_len];
SecurityRequest_t* sreq = NULL;
SecurityReply_t* srep = NULL;
switch (type_rx->present) {
case TollingType_PR_entry:
if (type_rx->choice.entry.present != TollingEntry_PR_request) {
syslog_err("[facilities] [tolling] received TPM.entry is not request");
return;
}
client_id = type_rx->choice.entry.choice.request.clientId;
nonce = type_rx->choice.entry.choice.request.transactionNonce;
info_id = type_rx->choice.entry.choice.request.infoId;
break;
case TollingType_PR_exit:
if (!type_rx->choice.exit ||
type_rx->choice.exit->present != TollingExit_PR_request ||
!type_rx->choice.exit->choice.request
) {
syslog_err("[facilities] [tolling] received TPM.exit is not request");
return;
}
client_id = type_rx->choice.exit->choice.request->clientId;
nonce = type_rx->choice.exit->choice.request->transactionNonce;
info_id = type_rx->choice.exit->choice.request->infoId;
if (!type_rx->choice.exit->choice.request->entryProof) {
syslog_err("[facilities] [tolling] received TPM.exit.request does not contain entry proof");
return;
}
TPM_t* ep = (TPM_t*) type_rx->choice.exit->choice.request->entryProof;
if (!ep->tpmSignature) {
syslog_err("[facilities] [tolling] received TPM.exit.request.entryProof does not contain signature");;
return;
}
// Encode TollingPaymentMessage
asn_enc_rval_t enc = oer_encode_to_buffer(&asn_DEF_TollingPaymentMessage, NULL, ep->tpm, buf, buf_len);
if (enc.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding TollingPaymentMessage (%s)", enc.failed_type->name);
goto cleanup;
}
// Verify
sreq = calloc(1, sizeof(SecurityRequest_t));
sreq->present = SecurityRequest_PR_verify;
// message
sreq->choice.verify.message.size = enc.encoded;
sreq->choice.verify.message.buf = malloc(enc.encoded);
memcpy(sreq->choice.verify.message.buf, buf, enc.encoded);
// r
sreq->choice.verify.r.size = ep->tpmSignature->r.size;
sreq->choice.verify.r.buf = malloc(ep->tpmSignature->r.size);
memcpy(sreq->choice.verify.r.buf, ep->tpmSignature->r.buf, ep->tpmSignature->r.size);
// s
sreq->choice.verify.s.size = ep->tpmSignature->s.size;
sreq->choice.verify.s.buf = malloc(ep->tpmSignature->s.size);
memcpy(sreq->choice.verify.s.buf, ep->tpmSignature->s.buf, ep->tpmSignature->s.size);
// signer
sreq->choice.verify.signer.size = ep->tpmSignature->signer.size;
sreq->choice.verify.signer.buf = malloc(ep->tpmSignature->signer.size);
memcpy(sreq->choice.verify.signer.buf, ep->tpmSignature->signer.buf, ep->tpmSignature->signer.size);
// signature type
sreq->choice.verify.type = ep->tpmSignature->type;
buf[0] = 4;
enc = oer_encode_to_buffer(&asn_DEF_SecurityRequest, NULL, sreq, buf+1, buf_len-1);
syslog_debug("[facilities]->[security] SecurityRequest.verify (%ldB)", enc.encoded+1);
zmq_send(security_socket, buf, enc.encoded+1, 0);
int32_t rl = zmq_recv(security_socket, buf, buf_len, 0);
syslog_debug("[facilities]<-[security] SecurityReply.verify (%dB)", rl);
if (oer_decode(NULL, &asn_DEF_SecurityReply, (void**) &srep, buf, rl).code) {
syslog_err("[facilities] SecurityReply.verify decode failure");
goto cleanup;
}
if (srep->returnCode == SecurityReplyReturnCode_rejected) {
syslog_err("[facilities] SecurityReply.verify rejected");
goto cleanup;
}
if (srep->data->choice.verify.report != SecurityVerifyConfirmCode_success) {
syslog_debug("[facilities] entry proof signature verify failed");
goto cleanup;
}
ASN_STRUCT_FREE(asn_DEF_SecurityRequest, sreq);
ASN_STRUCT_FREE(asn_DEF_SecurityReply, srep);
sreq = NULL;
srep = NULL;
break;
case TollingType_PR_single:
if (type_rx->choice.single.present != TollingSingle_PR_request) {
syslog_err("[facilities] [tolling] received TPM.single is not request");
return;
}
client_id = type_rx->choice.single.choice.request.clientId;
nonce = type_rx->choice.single.choice.request.transactionNonce;
info_id = type_rx->choice.single.choice.request.infoId;
break;
default:
syslog_err("[facilities] [tolling] received TPM has unrecognized type");
return;
}
tolling_t* tolling = &facilities->tolling;
switch (tolling->protocol.p) {
case TOLLING_PROTOCOL_SIMPLE:
syslog_info("[facilities] [tolling] received toll %s.request | client: %lld (certificate id: 0x%02x%02x%02x) nonce: %lld",
tts(type_rx->present),
(long long) client_id,
neighbour ? neighbour[5] : 0,
neighbour ? neighbour[6] : 0,
neighbour ? neighbour[7] : 0,
(long long) nonce
);
break;
case TOLLING_PROTOCOL_TLS:
case TOLLING_PROTOCOL_TLS_GN:
case TOLLING_PROTOCOL_TLS_SHS:
syslog_info("[facilities] [tolling] received toll %s.request | client: %lld nonce: %lld",
tts(type_rx->present),
(long long) client_id,
(long long) nonce
);
break;
}
asn_enc_rval_t enc;
TransportRequest_t* tr = NULL;
FacilitiesIndication_t* fi = NULL;
TPM_t* tpm = NULL;
if (tolling->protocol.p == TOLLING_PROTOCOL_SIMPLE) {
if (!tpm_rx->tpmSignature) {
syslog_err("[facilities] [tolling] in simple mode but TPM without signature received");
goto cleanup;
}
// Encode TollingPaymentMessage
enc = oer_encode_to_buffer(&asn_DEF_TollingPaymentMessage, NULL, tpm_rx->tpm, buf, buf_len);
if (enc.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding TollingPaymentMessage (%s)", enc.failed_type->name);
goto cleanup;
}
// Verify
sreq = calloc(1, sizeof(SecurityRequest_t));
sreq->present = SecurityRequest_PR_verify;
// message
sreq->choice.verify.message.size = enc.encoded;
sreq->choice.verify.message.buf = malloc(enc.encoded);
memcpy(sreq->choice.verify.message.buf, buf, enc.encoded);
// r
sreq->choice.verify.r.size = tpm_rx->tpmSignature->r.size;
sreq->choice.verify.r.buf = malloc(tpm_rx->tpmSignature->r.size);
memcpy(sreq->choice.verify.r.buf, tpm_rx->tpmSignature->r.buf, tpm_rx->tpmSignature->r.size);
// s
sreq->choice.verify.s.size = tpm_rx->tpmSignature->s.size;
sreq->choice.verify.s.buf = malloc(tpm_rx->tpmSignature->s.size);
memcpy(sreq->choice.verify.s.buf, tpm_rx->tpmSignature->s.buf, tpm_rx->tpmSignature->s.size);
// signer
sreq->choice.verify.signer.size = tpm_rx->tpmSignature->signer.size;
sreq->choice.verify.signer.buf = malloc(tpm_rx->tpmSignature->signer.size);
memcpy(sreq->choice.verify.signer.buf, tpm_rx->tpmSignature->signer.buf, tpm_rx->tpmSignature->signer.size);
// signature type
sreq->choice.verify.type = tpm_rx->tpmSignature->type;
buf[0] = 4;
enc = oer_encode_to_buffer(&asn_DEF_SecurityRequest, NULL, sreq, buf+1, buf_len-1);
syslog_debug("[facilities]->[security] SecurityRequest.verify (%ldB)", enc.encoded+1);
zmq_send(security_socket, buf, enc.encoded+1, 0);
int32_t rl = zmq_recv(security_socket, buf, buf_len, 0);
syslog_debug("[facilities]<-[security] SecurityReply.verify (%dB)", rl);
if (oer_decode(NULL, &asn_DEF_SecurityReply, (void**) &srep, buf, rl).code) {
syslog_err("[facilities] SecurityReply.verify decode failure");
goto cleanup;
}
if (srep->returnCode == SecurityReplyReturnCode_rejected) {
syslog_err("[facilities] SecurityReply.verify rejected");
goto cleanup;
}
if (srep->data->choice.verify.report != SecurityVerifyConfirmCode_success) {
syslog_debug("[facilities] signature verify failed");
goto cleanup;
}
ASN_STRUCT_FREE(asn_DEF_SecurityRequest, sreq);
ASN_STRUCT_FREE(asn_DEF_SecurityReply, srep);
sreq = NULL;
srep = NULL;
}
// TPM
tpm = calloc(1, sizeof(TPM_t));
tpm->header.messageID = 117;
tpm->header.protocolVersion = 0;
pthread_mutex_lock(&facilities->id.lock);
tpm->header.stationID = facilities->id.station_id;
pthread_mutex_unlock(&facilities->id.lock);
tpm->tpm = calloc(1, sizeof(TollingPaymentMessage_t));
// timestamp
asn_ulong2INTEGER(&tpm->tpm->timestamp, itss_time_get());
// stationType
tpm->tpm->stationType = facilities->station_type;
// referencePosition
itss_space_lock();
itss_space_get();
tpm->tpm->referencePosition.altitude.altitudeValue = epv.space.altitude;
tpm->tpm->referencePosition.altitude.altitudeConfidence = epv.space.altitude_conf;
tpm->tpm->referencePosition.latitude = epv.space.latitude;
tpm->tpm->referencePosition.longitude = epv.space.longitude;
uint16_t lat_conf = epv.space.latitude_conf;
uint16_t lon_conf = epv.space.longitude_conf;
itss_space_unlock();
if (lat_conf > lon_conf) {
tpm->tpm->referencePosition.positionConfidenceEllipse.semiMinorConfidence = lon_conf;
tpm->tpm->referencePosition.positionConfidenceEllipse.semiMajorConfidence = lat_conf;
tpm->tpm->referencePosition.positionConfidenceEllipse.semiMajorOrientation = 0;
} else {
tpm->tpm->referencePosition.positionConfidenceEllipse.semiMinorConfidence = lon_conf;
tpm->tpm->referencePosition.positionConfidenceEllipse.semiMajorConfidence = lat_conf;
tpm->tpm->referencePosition.positionConfidenceEllipse.semiMajorOrientation = 900;
}
// Client management
for (int i = 0 ; i < tolling->station.rsu.clients_len; ++i) {
if (itss_time_get() > tolling->station.rsu.clients[i]->ts + TOLLING_CLIENT_LIFETIME_MS) {
free(tolling->station.rsu.clients[i]);
tolling->station.rsu.clients[i] = NULL;
for (int j = i; j < tolling->station.rsu.clients_len-1; ++j) {
tolling->station.rsu.clients[j] = tolling->station.rsu.clients[j+1];
}
--tolling->station.rsu.clients_len;
}
}
// Find client
toll_client_t* client = NULL;
for (int i = 0; i < tolling->station.rsu.clients_len; ++i) {
if (tolling->station.rsu.clients[i]->id == client_id) {
client = tolling->station.rsu.clients[i];
}
}
// Create client
if (!client) {
if (tolling->station.rsu.clients_len-1 >= TOLLING_MAX_CLIENTS) {
syslog_err("[facilities] [tolling] max clients reached");
goto cleanup;
}
tolling->station.rsu.clients[tolling->station.rsu.clients_len] = malloc(sizeof(toll_client_t));
client = tolling->station.rsu.clients[tolling->station.rsu.clients_len];
client->id = client_id;
client->station_id = tpm_rx->header.stationID;
client->ts = itss_time_get();
client->accepted = -1;
}
uint8_t confirmation_code = TollingConfirmationCode_rejected;
if (itss_time_get() > client->ts + TOLLING_PAYMENT_MIN_PERIOD_MS ||
client->accepted == -1) {
confirmation_code = TollingConfirmationCode_accepted;
client->accepted = true;
} else if (client->accepted &&
itss_time_get() < client->ts + TOLLING_PAYMENT_MIN_PERIOD_MS) {
confirmation_code = TollingConfirmationCode_alreadyAccepted;
}
tpm->tpm->tollingType = calloc(1, sizeof(TollingType_t));
switch (tpm_rx->tpm->tollingType->present) {
case TollingType_PR_entry:
tpm->tpm->tollingType->present = TollingType_PR_entry;
tpm->tpm->tollingType->choice.entry.present = TollingEntry_PR_reply;
tpm->tpm->tollingType->choice.entry.choice.reply.clientId = client_id;
tpm->tpm->tollingType->choice.entry.choice.reply.infoId = info_id;
tpm->tpm->tollingType->choice.entry.choice.reply.transactionNonce = nonce;
tpm->tpm->tollingType->choice.entry.choice.reply.confirmationCode = confirmation_code;
break;
case TollingType_PR_exit:
tpm->tpm->tollingType->present = TollingType_PR_exit;
tpm->tpm->tollingType->choice.exit = calloc(1, sizeof(TollingExit_t));
tpm->tpm->tollingType->choice.exit->present = TollingExit_PR_reply;
tpm->tpm->tollingType->choice.exit->choice.reply.clientId = client_id;
tpm->tpm->tollingType->choice.exit->choice.reply.infoId = info_id;
tpm->tpm->tollingType->choice.exit->choice.reply.transactionNonce = nonce;
tpm->tpm->tollingType->choice.exit->choice.reply.confirmationCode = confirmation_code;
break;
case TollingType_PR_single:
tpm->tpm->tollingType->present = TollingType_PR_single;
tpm->tpm->tollingType->choice.single.present = TollingSingle_PR_reply;
tpm->tpm->tollingType->choice.single.choice.reply.clientId = client_id;
tpm->tpm->tollingType->choice.single.choice.reply.infoId = info_id;
tpm->tpm->tollingType->choice.single.choice.reply.transactionNonce = nonce;
tpm->tpm->tollingType->choice.single.choice.reply.confirmationCode = confirmation_code;
break;
default:
break;
}
// TODO check clientId
// Encode TollingPaymentMessage
enc = oer_encode_to_buffer(&asn_DEF_TollingPaymentMessage, NULL, tpm->tpm, buf, buf_len);
if (enc.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding TollingPaymentMessage (%s)", enc.failed_type->name);
goto cleanup;
}
// Sign
sreq = calloc(1, sizeof(SecurityRequest_t));
sreq->present = SecurityRequest_PR_sign;
sreq->choice.sign.data.size = enc.encoded;
sreq->choice.sign.data.buf = malloc(enc.encoded);
memcpy(sreq->choice.sign.data.buf, buf, enc.encoded);
buf[0] = 4;
enc = oer_encode_to_buffer(&asn_DEF_SecurityRequest, NULL, sreq, buf+1, buf_len-1);
if (enc.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding SecurityRequest (%s)", enc.failed_type->name);
goto cleanup;
}
syslog_debug("[facilities]->[security] SecurityRequest.sign (%ldB)", enc.encoded+1);
zmq_send(security_socket, buf, enc.encoded+1, 0);
int rc = zmq_recv(security_socket, buf, buf_len, 0);
syslog_debug("[facilities]<-[security] SecurityReply.sign (%dB)", rc);
if (oer_decode(NULL, &asn_DEF_SecurityReply, (void**) &srep, buf, rc).code) {
syslog_err("[facilities] SecurityReply.sign decode failure");
goto cleanup;
}
if (srep->returnCode == SecurityReplyReturnCode_rejected) {
syslog_err("[facilities] SecurityReply.sign rejected");
goto cleanup;
}
tpm->tpmSignature = calloc(1, sizeof(TpmSignature_t));
tpm->tpmSignature->r.size = srep->data->choice.sign.r.size;
tpm->tpmSignature->r.buf = malloc(srep->data->choice.sign.r.size);
memcpy(tpm->tpmSignature->r.buf, srep->data->choice.sign.r.buf, srep->data->choice.sign.r.size);
tpm->tpmSignature->s.size = srep->data->choice.sign.s.size;
tpm->tpmSignature->s.buf = malloc(srep->data->choice.sign.s.size);
memcpy(tpm->tpmSignature->s.buf, srep->data->choice.sign.s.buf, srep->data->choice.sign.s.size);
tpm->tpmSignature->signer.size = srep->data->choice.sign.signer.size;
tpm->tpmSignature->signer.buf = malloc(srep->data->choice.sign.signer.size);
memcpy(tpm->tpmSignature->signer.buf, srep->data->choice.sign.signer.buf, srep->data->choice.sign.signer.size);
tpm->tpmSignature->type = srep->data->choice.sign.type;
ASN_STRUCT_FREE(asn_DEF_SecurityRequest, sreq);
ASN_STRUCT_FREE(asn_DEF_SecurityReply, srep);
sreq = NULL;
srep = NULL;
// encode TPM
enc = uper_encode_to_buffer(&asn_DEF_TPM, NULL, tpm, tpm_uper, buf_len);
if (enc.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding TPM.reply (%s)", enc.failed_type->name);
goto cleanup;
}
size_t tpm_uper_len = (enc.encoded + 7) / 8;
uint64_t id = 0;
tr = calloc(1, sizeof(TransportRequest_t));
tr->present = TransportRequest_PR_packet;
// [transport] request (TR)
switch (tolling->protocol.p) {
case TOLLING_PROTOCOL_SIMPLE:
tr->choice.packet.present = TransportPacketRequest_PR_btp;
BTPPacketRequest_t* bpr = &tr->choice.packet.choice.btp;
bpr->gn.securityProfile.encrypt = true;
bpr->gn.securityProfile.sign = true;
bpr->data.size = tpm_uper_len;
bpr->data.buf = malloc(tpm_uper_len);
memcpy(bpr->data.buf, tpm_uper, tpm_uper_len);
bpr->id = itss_id(bpr->data.buf, bpr->data.size);
id = bpr->id;
bpr->destinationPort = 7011;
bpr->btpType = BTPType_btpB;
bpr->gn.destinationAddress.buf = malloc(6);
for (int i = 0; i < 6; ++i) {
bpr->gn.destinationAddress.buf[i] = 0xff;
}
bpr->gn.destinationAddress.size = 6;
bpr->gn.trafficClass = 2;
bpr->gn.packetTransportType = PacketTransportType_shb;
if (neighbour) { /* neighbour ID for [security] encryption */
bpr->gn.securityNeighbour = calloc(1, sizeof(OCTET_STRING_t));
bpr->gn.securityNeighbour->size = 8;
bpr->gn.securityNeighbour->buf = malloc(8);
memcpy(bpr->gn.securityNeighbour->buf, neighbour, 8);
}
break;
case TOLLING_PROTOCOL_TLS:
case TOLLING_PROTOCOL_TLS_GN:
case TOLLING_PROTOCOL_TLS_SHS:
sreq = calloc(1, sizeof(SecurityRequest_t));
sreq->present = SecurityRequest_PR_tlsSend;
sreq->choice.tlsSend.data.buf = malloc(tpm_uper_len);
sreq->choice.tlsSend.data.size = tpm_uper_len;
memcpy(sreq->choice.tlsSend.data.buf, tpm_uper, tpm_uper_len);
tlsc_t* tlsc = tolling_tlsc_get(tolling, src_addr, 7011);
if (tlsc) {
id = tlsc->id;
} else {
tlsc = tolling_tlsc_new(tolling, src_addr, 7011);
id = tlsc->id;
}
++tlsc->nmsg;
sreq->choice.tlsSend.connId = id;
buf[0] = 4;
asn_enc_rval_t enc = oer_encode_to_buffer(&asn_DEF_SecurityRequest, NULL, sreq, buf+1, buf_len-1);
syslog_debug("[facilities] [tolling]-> SecurityRequest.tlsSend ->[security] | size:%ldB", enc.encoded);
zmq_send(security_socket, buf, enc.encoded+1, 0);
int rc = zmq_recv(security_socket, buf, buf_len, 0);
if (rc == -1) {
syslog_err("[facilities] [tolling]-> SecurityRequest.tlsSend ->[security] <TIMEOUT>");
goto cleanup;
}
syslog_debug("[facilities] [tolling]<- SecurityReply.tlsSend <-[security] | size:%dB", rc);
asn_dec_rval_t dec = oer_decode(NULL, &asn_DEF_SecurityReply, (void**) &srep, buf, rc);
if (dec.code ||
srep->returnCode != SecurityReplyReturnCode_accepted ||
!srep->data ||
srep->data->present != SecurityReplyData_PR_tlsSend) {
syslog_err("[facilities] [tolling]<- SecurityReply.tlsSend rejected");
goto cleanup;
}
// TR TCP
tr->choice.packet.present = TransportPacketRequest_PR_tcp;
TCPPacketRequest_t* tcp = &tr->choice.packet.choice.tcp;
tcp->id = id;
tcp->destinationAddress = calloc(1, sizeof(OCTET_STRING_t));
tcp->destinationAddress->buf = malloc(16);
tcp->destinationAddress->size = 16;
memcpy(tcp->destinationAddress->buf, src_addr, 16);
tcp->destinationPort = 7011;
tcp->sourcePort = 7011;
tcp->data.buf = malloc(srep->data->choice.tlsSend.data.size);
tcp->data.size = srep->data->choice.tlsSend.data.size;
memcpy(tcp->data.buf, srep->data->choice.tlsSend.data.buf, srep->data->choice.tlsSend.data.size);
if (tolling->protocol.p == TOLLING_PROTOCOL_TLS_GN) {
tcp->gn = calloc(1, sizeof(GeonetworkingOutboundOptions_t));
tcp->gn->packetTransportType = PacketTransportType_shb;
tcp->gn->destinationAddress.buf = calloc(1, 6);
tcp->gn->destinationAddress.size = 6;
tcp->gn->securityProfile.encrypt = false;
tcp->gn->securityProfile.sign = true;
}
break;
}
// encode TR
buf[0] = 4;
enc = oer_encode_to_buffer(&asn_DEF_TransportRequest, NULL, tr, buf+1, buf_len-1);
if (enc.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding TR TPM.reply (%s)", enc.failed_type->name);
goto cleanup;
}
itss_queue_send(facilities->tx_queue, buf, enc.encoded+1, ITSS_TRANSPORT, id,
tolling->protocol.p == TOLLING_PROTOCOL_SIMPLE ? "TR.packet.btp" : "TR.packet.tcp");
// Logging
if (facilities->logging.dbms) {
pthread_mutex_lock(&facilities->id.lock);
uint64_t station_id = facilities->id.station_id;
pthread_mutex_unlock(&facilities->id.lock);
itss_db_add(facilities->logging.dbms, station_id, id, true, 117, NULL, tpm_uper, tpm_uper_len);
}
// send to [applications]
fi = calloc(1, sizeof(FacilitiesIndication_t));
fi->present = FacilitiesIndication_PR_message;
fi->choice.message.id = id;
fi->choice.message.itsMessageType = 7011;
fi->choice.message.data.size = tpm_uper_len;
fi->choice.message.data.buf = malloc(tpm_uper_len);
memcpy(fi->choice.message.data.buf, tpm_uper, tpm_uper_len);
enc = oer_encode_to_buffer(&asn_DEF_FacilitiesIndication, NULL, fi, buf+1, buf_len-1);
if (enc.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding FI TPM.reply (%s)", enc.failed_type->name);
goto cleanup;
}
itss_queue_send(facilities->tx_queue, buf, enc.encoded+1, ITSS_APPLICATIONS, id, "FI.message (TPM.reply)");
if (facilities->logging.recorder) {
uint16_t buffer_len = 2048;
uint8_t buffer[buffer_len];
int e = itss_management_record_packet_sdu(
buffer,
buffer_len,
tpm_uper,
tpm_uper_len,
id,
itss_time_get(),
ITSS_FACILITIES,
true);
if (e != -1) {
itss_queue_send(facilities->tx_queue, buffer, e, ITSS_MANAGEMENT, id, "MReq.packet.set");
}
}
cleanup:
ASN_STRUCT_FREE(asn_DEF_TPM, tpm);
ASN_STRUCT_FREE(asn_DEF_TransportRequest, tr);
ASN_STRUCT_FREE(asn_DEF_SecurityRequest, sreq);
ASN_STRUCT_FREE(asn_DEF_SecurityReply, srep);
ASN_STRUCT_FREE(asn_DEF_FacilitiesIndication, fi);
}
static void veh_handle_recv(tolling_t* tolling, TPM_t* tpm_rx, void* security_socket, itss_queue_t* tx_queue, uint8_t* neighbour, uint8_t* src_addr) {
if (!tpm_rx->tpm->tollingType) {
syslog_err("[facilities] [tolling] received TPM does not have a type");
return;
}
TollingType_t* type_rx = tpm_rx->tpm->tollingType;
uint64_t client_id, nonce, info_id;
int confirmation_code;
SecurityRequest_t* sreq = NULL;
SecurityReply_t* srep = NULL;
TransportRequest_t* tr = NULL;
const uint16_t buf_len = 1024;
uint8_t buf[buf_len];
switch (type_rx->present) {
case TollingType_PR_entry:
if (type_rx->choice.entry.present != TollingEntry_PR_reply) {
syslog_err("[facilities] [tolling] received TPM.entry is not reply");
return;
}
if (tolling->station.obu.toll_type != TollingType_PR_entry) {
syslog_err("[facilities] [tolling] received TPM toll (%d) is not expected toll type (%d)",
TollingType_PR_entry, tolling->station.obu.toll_type);
}
client_id = type_rx->choice.entry.choice.reply.clientId;
nonce = type_rx->choice.entry.choice.reply.transactionNonce;
info_id = type_rx->choice.entry.choice.reply.infoId;
confirmation_code = type_rx->choice.entry.choice.reply.confirmationCode;
uint8_t b_tep[1024];
asn_enc_rval_t e_tep = uper_encode_to_buffer(&asn_DEF_TPM, NULL, tpm_rx, b_tep, 1024);
if (e_tep.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding received TPM as entry proof");
return;
}
uper_decode_complete(NULL, &asn_DEF_TPM, (void**) &tolling->station.obu.entry_proof, b_tep, (e_tep.encoded+7)/8);
break;
case TollingType_PR_exit:
if (!type_rx->choice.exit ||
type_rx->choice.exit->present != TollingExit_PR_reply
) {
syslog_err("[facilities] [tolling] received TPM.exit is not reply");
return;
}
if (tolling->station.obu.toll_type != TollingType_PR_exit) {
syslog_err("[facilities] [tolling] received TPM toll (%d) is not expected toll type (%d)",
TollingType_PR_exit, tolling->station.obu.toll_type);
return;
}
client_id = type_rx->choice.exit->choice.reply.clientId;
nonce = type_rx->choice.exit->choice.reply.transactionNonce;
info_id = type_rx->choice.exit->choice.reply.infoId;
confirmation_code = type_rx->choice.exit->choice.reply.confirmationCode;
break;
case TollingType_PR_single:
if (type_rx->choice.single.present != TollingSingle_PR_reply) {
syslog_err("[facilities] [tolling] received TPM.single is not reply");
return;
}
if (tolling->station.obu.toll_type != TollingType_PR_single) {
syslog_err("[facilities] [tolling] received TPM type (%d) is not expected toll type (%d)",
TollingType_PR_single, tolling->station.obu.toll_type);
}
client_id = type_rx->choice.single.choice.reply.clientId;
nonce = type_rx->choice.single.choice.reply.transactionNonce;
info_id = type_rx->choice.single.choice.reply.infoId;
confirmation_code = type_rx->choice.single.choice.reply.confirmationCode;
break;
default:
syslog_err("[facilities] [tolling] received TPM has unrecognized type");
return;
}
if (client_id != tolling->station.obu.client_id) {
syslog_debug("[facilities] [tolling]<- received TPM.reply clientId different from ego");
return;
}
if (nonce != tolling->station.obu.nonce) {
syslog_err("[facilities] [tolling]<- received TPM.reply nonce different from sent request");
return;
}
// Encode TollingPaymentMessage
asn_enc_rval_t enc = oer_encode_to_buffer(&asn_DEF_TollingPaymentMessage, NULL, tpm_rx->tpm, buf, buf_len);
if (enc.encoded == -1) {
syslog_err("[facilities] [tolling] error encoding TollingPaymentMessage (%s)", enc.failed_type->name);
goto cleanup;
}
bool accepted = true;
if (tolling->protocol.p == TOLLING_PROTOCOL_SIMPLE) {
// Verify
sreq = calloc(1, sizeof(SecurityRequest_t));
sreq->present = SecurityRequest_PR_verify;
// message
sreq->choice.verify.message.size = enc.encoded;
sreq->choice.verify.message.buf = malloc(enc.encoded);
memcpy(sreq->choice.verify.message.buf, buf, enc.encoded);
// r
sreq->choice.verify.r.size = tpm_rx->tpmSignature->r.size;
sreq->choice.verify.r.buf = malloc(tpm_rx->tpmSignature->r.size);
memcpy(sreq->choice.verify.r.buf, tpm_rx->tpmSignature->r.buf, tpm_rx->tpmSignature->r.size);
// s
sreq->choice.verify.s.size = tpm_rx->tpmSignature->s.size;
sreq->choice.verify.s.buf = malloc(tpm_rx->tpmSignature->s.size);
memcpy(sreq->choice.verify.s.buf, tpm_rx->tpmSignature->s.buf, tpm_rx->tpmSignature->s.size);
// signer
sreq->choice.verify.signer.size = tpm_rx->tpmSignature->signer.size;
sreq->choice.verify.signer.buf = malloc(tpm_rx->tpmSignature->signer.size);
memcpy(sreq->choice.verify.signer.buf, tpm_rx->tpmSignature->signer.buf, tpm_rx->tpmSignature->signer.size);
// signature type
sreq->choice.verify.type = tpm_rx->tpmSignature->type;
buf[0] = 4;
enc = oer_encode_to_buffer(&asn_DEF_SecurityRequest, NULL, sreq, buf+1, 2047);
syslog_debug("[facilities]->[security] SecurityRequest.verify (%ldB)", enc.encoded+1);
zmq_send(security_socket, buf, enc.encoded+1, 0);
int32_t rl = zmq_recv(security_socket, buf, buf_len, 0);
syslog_debug("[facilities]<-[security] SecurityReply.verify (%dB)", rl);
if (oer_decode(NULL, &asn_DEF_SecurityReply, (void**) &srep, buf, rl).code) {
syslog_err("[facilities] SecurityReply.verify decode failure");
goto cleanup;
}
if (srep->returnCode == SecurityReplyReturnCode_rejected) {
syslog_err("[facilities] SecurityReply.verify rejected");
goto cleanup;
}
if (srep->data->choice.verify.report != SecurityVerifyConfirmCode_success) {
syslog_debug("[facilities] signature verify failed");
goto cleanup;
}
accepted = confirmation_code == TollingConfirmationCode_accepted || confirmation_code == TollingConfirmationCode_alreadyAccepted;
}
switch (type_rx->present) {
case TollingType_PR_entry:
syslog_info("[facilities] [tolling] received entry.reply | client: %lld nonce: %ld accepted: %s", (long long) tolling->station.obu.client_id, tolling->station.obu.nonce, accepted ? "yes" : "no");
break;
case TollingType_PR_exit:
syslog_info("[facilities] [tolling] received exit.reply | client: %lld nonce: %ld accepted: %s", (long long) tolling->station.obu.client_id, tolling->station.obu.nonce, accepted ? "yes" : "no");
tolling->station.obu.toll_type = -1;
tolling->station.obu.active = false;
ASN_STRUCT_FREE(asn_DEF_TPM, tolling->station.obu.entry_proof);
tolling->station.obu.entry_proof = NULL;
break;
case TollingType_PR_single:
syslog_info("[facilities] [tolling] received single.reply | client: %lld nonce: %ld accepted: %s", (long long) tolling->station.obu.client_id, tolling->station.obu.nonce, accepted ? "yes" : "no");
tolling->station.obu.toll_type = -1;
tolling->station.obu.active = false;
break;
default:
break;
}
// Close TCP & TLS conn
if (tolling->protocol.p == TOLLING_PROTOCOL_TLS ||
tolling->protocol.p == TOLLING_PROTOCOL_TLS_GN ||
tolling->protocol.p == TOLLING_PROTOCOL_TLS_SHS) {
tlsc_t* tlsc = tolling_tlsc_get(tolling, src_addr, 7011);
if (tlsc) {
sreq = calloc(1, sizeof(SecurityRequest_t));
sreq->present = SecurityRequest_PR_tlsClose;
sreq->choice.tlsClose.connId = tlsc->id;
buf[0] = 4;
enc = oer_encode_to_buffer(&asn_DEF_SecurityRequest, NULL, sreq, buf+1, buf_len-1);
syslog_debug("[facilities]->[security] SecurityRequest.tlsClose (%ldB)", enc.encoded+1);
zmq_send(security_socket, buf, enc.encoded+1, 0);
int32_t rl = zmq_recv(security_socket, buf, buf_len, 0);
syslog_debug("[facilities]<-[security] SecurityReply.tlsClose (%dB)", rl);
uint32_t id = rand();
tr = calloc(1, sizeof(TransportRequest_t));
tr->present = TransportRequest_PR_data;
tr->choice.data.present = TransportDataRequest_PR_tcp;
tr->choice.data.choice.tcp.present = TCPDataRequest_PR_connCloseReq;
tr->choice.data.choice.tcp.choice.connCloseReq.closeByPeer = false;
tr->choice.data.choice.tcp.choice.connCloseReq.destinationPort = 7011;
tr->choice.data.choice.tcp.choice.connCloseReq.destinationAddress.size = 16;
tr->choice.data.choice.tcp.choice.connCloseReq.destinationAddress.buf = malloc(16);
memcpy(tr->choice.data.choice.tcp.choice.connCloseReq.destinationAddress.buf, src_addr, 16);
tr->choice.data.choice.tcp.choice.connCloseReq.id = id;
buf[0] = 4;
enc = oer_encode_to_buffer(&asn_DEF_TransportRequest, NULL, tr, buf+1, buf_len-1);
itss_queue_send(tx_queue, buf, enc.encoded+1, ITSS_TRANSPORT, id, "TR.data.tcp.connClose");
free(tlsc);
for (int i = 0; i < tolling->protocol.c.tls.n_tlsc; ++i) {
if (tlsc == tolling->protocol.c.tls.tls_conns[i]) {
for (int j = i; j < tolling->protocol.c.tls.n_tlsc-1; ++j) {
tolling->protocol.c.tls.tls_conns[j] = tolling->protocol.c.tls.tls_conns[j+1];
}
--tolling->protocol.c.tls.n_tlsc;
break;
}
}
} else {
syslog_debug("[facilities] [tolling] unable to close TLS connection, not found");
}
}
cleanup:
ASN_STRUCT_FREE(asn_DEF_SecurityRequest, sreq);
ASN_STRUCT_FREE(asn_DEF_SecurityReply, srep);
}
int tpm_recv(void* fc, TPM_t* tpm_rx, void* security_socket, uint8_t* neighbour, uint8_t* src_addr) {
facilities_t* facilities = (facilities_t*) fc;
tolling_t* tolling = (tolling_t*) &facilities->tolling;
if (!tolling->enabled) {
syslog_debug("[facilities] [tolling] tolling is disabled");
return 0;
}
if (!tpm_rx->tpm) {
syslog_err("[facilities] [tolling] received TPM does not have substructure TollingPaymentMessage");
return 1;
}
if (!tpm_rx->tpm->tollingType) {
syslog_err("[facilities] [tolling] received TPM does not have a type");
return 1;
}
pthread_mutex_lock(&tolling->lock);
switch (tpm_rx->tpm->tollingType->present) {
// Entry
case TollingType_PR_entry:
switch (tpm_rx->tpm->tollingType->choice.entry.present) {
case TollingSingle_PR_request:
if (facilities->station_type != 15) {
syslog_debug("[facilities] [tolling] received TPM.entry.request, ignoring");
goto cleanup;
}
rsu_handle_recv(facilities, tpm_rx, security_socket, neighbour, src_addr);
break;
case TollingSingle_PR_reply:
if (facilities->station_type == 15) {
syslog_debug("[facilities] [tolling] received TPM.entry.reply, ignoring");
goto cleanup;
}
itss_time_lock();
syslog_info("[facilities] [tolling] entry.reply took %ld us", itss_ts_get(TIME_MICROSECONDS) - tolling->station.obu.rt_init);
itss_time_unlock();
tolling->station.obu.rt_on = false;
veh_handle_recv(tolling, tpm_rx, security_socket, facilities->tx_queue, neighbour, src_addr);
break;
default:
break;
}
break;
// Exit
case TollingType_PR_exit:
if (!tpm_rx->tpm->tollingType->choice.exit) {
return 1;
}
switch (tpm_rx->tpm->tollingType->choice.exit->present) {
case TollingSingle_PR_request:
if (facilities->station_type != 15) {
syslog_debug("[facilities] [tolling] received TPM.exit.request, ignoring");
goto cleanup;
}
rsu_handle_recv(facilities, tpm_rx, security_socket, neighbour, src_addr);
break;
case TollingSingle_PR_reply:
if (facilities->station_type == 15) {
syslog_debug("[facilities] [tolling] received TPM.exit.reply, ignoring");
goto cleanup;
}
itss_time_lock();
syslog_info("[facilities] [tolling] exit.reply took %ld us", itss_ts_get(TIME_MICROSECONDS) - tolling->station.obu.rt_init);
itss_time_unlock();
tolling->station.obu.rt_on = false;
veh_handle_recv(tolling, tpm_rx, security_socket, facilities->tx_queue, neighbour, src_addr);
break;
default:
break;
}
break;
// Single
case TollingType_PR_single:
switch (tpm_rx->tpm->tollingType->choice.single.present) {
case TollingSingle_PR_request:
if (facilities->station_type != 15) {
syslog_debug("[facilities] [tolling] received TPM.single.request, ignoring");
goto cleanup;
}
rsu_handle_recv(facilities, tpm_rx, security_socket, neighbour, src_addr);
break;
case TollingSingle_PR_reply:
if (facilities->station_type == 15) {
syslog_debug("[facilities] [tolling] received TPM.single.reply, ignoring");
goto cleanup;
}
itss_time_lock();
syslog_info("[facilities] [tolling] single.reply took %ld us", itss_ts_get(TIME_MICROSECONDS) - tolling->station.obu.rt_init);
itss_time_unlock();
tolling->station.obu.rt_on = false;
veh_handle_recv(tolling, tpm_rx, security_socket, facilities->tx_queue, neighbour, src_addr);
break;
default:
break;
}
break;
default:
break;
}
cleanup:
pthread_mutex_unlock(&tolling->lock);
return 0;
}
int tolling_init(tolling_t* tolling, void* zmq_ctx, char* security_address, uint8_t station_type) {
pthread_mutex_init(&tolling->lock, NULL);
switch (station_type) {
case 15:
for (int i = 0; i < TOLLING_MAX_CLIENTS; ++i) {
tolling->station.rsu.clients[i] = NULL;
}
break;
default:
tolling->station.obu.toll_type = -1;
break;
}
return 0;
}
tolling_info_t* tolling_info_new(TollingPaymentInfo_t* tpi) {
tolling_info_t* ti = calloc(1, sizeof(tolling_info_t));
ti->timestamp = itss_time_get();
ti->asn = tpi;
ti->zone.polygon_len = tpi->zone.list.count;
ti->zone.polygon = malloc(tpi->zone.list.count * sizeof(double[2]));
for (int i = 0; i < tpi->zone.list.count; ++i) {
ti->zone.polygon[i][0] = tpi->zone.list.array[i]->latitude / 1.0e7;
ti->zone.polygon[i][1] = tpi->zone.list.array[i]->longitude / 1.0e7;
}
return ti;
}
void tolling_info_free(tolling_info_t* ti) {
ASN_STRUCT_FREE(asn_DEF_TollingPaymentInfo, ti->asn);
free(ti->zone.polygon);
free(ti);
}
tlsc_t* tolling_tlsc_new(tolling_t* tolling, uint8_t ipv6[16], uint16_t port) {
syslog_debug("[tolling] new tlsc, nconns=%d", tolling->protocol.c.tls.n_tlsc);
if (tolling->protocol.c.tls.n_tlsc >= TOLLING_MAX_CONNS - 1) {
return NULL;
}
tolling->protocol.c.tls.tls_conns[tolling->protocol.c.tls.n_tlsc] = calloc(1, sizeof(tlsc_t));
tlsc_t* tlsc = tolling->protocol.c.tls.tls_conns[tolling->protocol.c.tls.n_tlsc];
memcpy(tlsc->ipv6, ipv6, 16);
tlsc->port = port;
tlsc->id = rand();
tlsc->ts = itss_time_get();
tlsc->nmsg = 0;
tlsc->state = 0;
++tolling->protocol.c.tls.n_tlsc;
return tlsc;
}
tlsc_t* tolling_tlsc_get(tolling_t* tolling, uint8_t ipv6[16], uint16_t port) {
syslog_debug("[tolling] new get , nconns=%d", tolling->protocol.c.tls.n_tlsc);
tlsc_t* tlsc = NULL;
for (int i = 0; i < tolling->protocol.c.tls.n_tlsc; ++i) {
if (!memcmp(tolling->protocol.c.tls.tls_conns[i]->ipv6, ipv6, 16) &&
tolling->protocol.c.tls.tls_conns[i]->port == port) {
tlsc = tolling->protocol.c.tls.tls_conns[i];
tlsc->ts = itss_time_get();
break;
}
}
return tlsc;
}
void tolling_tlsc_mgmt(tolling_t* tolling, itss_queue_t* tx_queue, void* security_socket) {
pthread_mutex_lock(&tolling->lock);
uint64_t now = itss_time_get();
for (int i = 0; i < tolling->protocol.c.tls.n_tlsc; ++i) {
tlsc_t* tlsc = tolling->protocol.c.tls.tls_conns[i];
if (tlsc->ts + TOLLING_CONN_TIMEOUT_MS < now) {
SecurityRequest_t* sreq = NULL;
TransportRequest_t* tr = NULL;
const uint32_t buf_len = 1024;
uint8_t buf[buf_len];
syslog_debug("[facilities] [tolling] closing TCP/TLS connection");
sreq = calloc(1, sizeof(SecurityRequest_t));
sreq->present = SecurityRequest_PR_tlsClose;
sreq->choice.tlsClose.connId = tlsc->id;
buf[0] = 4;
asn_enc_rval_t enc = oer_encode_to_buffer(&asn_DEF_SecurityRequest, NULL, sreq, buf+1, buf_len-1);
syslog_debug("[facilities]->[security] SecurityRequest.tlsClose (%ldB)", enc.encoded+1);
zmq_send(security_socket, buf, enc.encoded+1, 0);
int32_t rl = zmq_recv(security_socket, buf, buf_len, 0);
syslog_debug("[facilities]<-[security] SecurityReply.tlsClose (%dB)", rl);
uint64_t id = rand() + 1;
tr = calloc(1, sizeof(TransportRequest_t));
tr->present = TransportRequest_PR_data;
tr->choice.data.present = TransportDataRequest_PR_tcp;
tr->choice.data.choice.tcp.present = TCPDataRequest_PR_connCloseReq;
tr->choice.data.choice.tcp.choice.connCloseReq.closeByPeer = false;
tr->choice.data.choice.tcp.choice.connCloseReq.destinationPort = 7011;
tr->choice.data.choice.tcp.choice.connCloseReq.destinationAddress.size = 16;
tr->choice.data.choice.tcp.choice.connCloseReq.destinationAddress.buf = malloc(16);
memcpy(tr->choice.data.choice.tcp.choice.connCloseReq.destinationAddress.buf, tlsc->ipv6, 16);
tr->choice.data.choice.tcp.choice.connCloseReq.id = id;
buf[0] = 4;
enc = oer_encode_to_buffer(&asn_DEF_TransportRequest, NULL, tr, buf+1, buf_len-1);
itss_queue_send(tx_queue, buf, enc.encoded+1, ITSS_TRANSPORT, id, "TR.data.tcp.connClose");
free(tlsc);
for (int j = i; j < tolling->protocol.c.tls.n_tlsc-1; ++j) {
tolling->protocol.c.tls.tls_conns[j] = tolling->protocol.c.tls.tls_conns[j+1];
}
--tolling->protocol.c.tls.n_tlsc;
--i;
}
}
pthread_mutex_unlock(&tolling->lock);
}