#include "config.h" #include "facilities.h" #include "saem.h" #include "tpm.h" #include "vcm.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int fetch_target_address(char** addresses, uint16_t addresses_len) { int index = -1; for (int i = 0; i < addresses_len; ++i) { char* addr = addresses[i]; // If TCP, ignore receiver addresses, e.g. tcp://*:[port] if (!memcmp(addr, "tcp", 3)) { bool found_astk = false; for (int j = 0; j < strlen(addr); ++j) { if (addr[j] == '*') { found_astk = true; break; } } if (found_astk) continue; } index = i; break; } return index; } int facilities_config() { int rv = 0; t2c_itss_t* itss_cfg = NULL; t2c_etsi_its_t* etsi_its_cfg = NULL; rv = t2c_itss_read("/etc/it2s/itss.toml", &itss_cfg); if (rv) { syslog_error("[config] read ITS-S config failed"); goto cleanup; } rv = t2c_etsi_its_read("/etc/it2s/etsi-its.toml", &etsi_its_cfg); if (rv) { syslog_error("[config] read ETSI ITS config failed"); goto cleanup; } // Logging - status messages bool use_syslog = false, use_std = false, use_file = false; for (int i = 0; i < itss_cfg->general.logging.status_len; ++i) { if (!strcmp(itss_cfg->general.logging.status[i], "syslog")) { use_syslog = true; } else if (!strcmp(itss_cfg->general.logging.status[i], "std")) { use_std = true; } else if (!strcmp(itss_cfg->general.logging.status[i], "file")) { use_file = true; } } log_init(use_syslog, use_std, use_file, "facilities", NULL); itss_0init(&facilities.exit); struct timeval time; gettimeofday(&time,NULL); srand((time.tv_sec * 1000) + (time.tv_usec / 1000)); facilities.zmq.responders = calloc(etsi_its_cfg->facilities.zmq.addresses_len + 1, sizeof(zmq_pollitem_t)); facilities.zmq.n_responders = 1; // Internal socket void* socket = itss_0bind(ZMQ_INTERNAL_ADDR, ZMQ_PAIR); facilities.zmq.responders[0].socket = socket; facilities.zmq.responders[0].events = ZMQ_POLLIN; for (int i = 0; i < etsi_its_cfg->facilities.zmq.addresses_len; ++i) { char* addr = etsi_its_cfg->facilities.zmq.addresses[i]; // IPC if (!memcmp(addr, "ipc", 3)) { // Create dir int lp = 0; for (int j = 0; j < strlen(addr); ++j) { if (addr[j] == '/') lp = j; } char dir[256]; memcpy(dir, addr+6, lp-6); dir[lp-6] = 0; struct stat st = {0}; if (stat(dir, &st) == -1) { mkdir(dir, 0777); } // Bind void* socket = itss_0bind(addr, ZMQ_REP); facilities.zmq.responders[facilities.zmq.n_responders].socket = socket; facilities.zmq.responders[facilities.zmq.n_responders].events = ZMQ_POLLIN; ++facilities.zmq.n_responders; } else if (!memcmp(addr, "tcp", 3)) { bool found_astk = false; for (int j = 0; j < strlen(addr); ++j) { if (addr[j] == '*') { found_astk = true; break; } } if (found_astk) { // Bind void* socket = itss_0bind(addr, ZMQ_REP); facilities.zmq.responders[facilities.zmq.n_responders].socket = socket; facilities.zmq.responders[facilities.zmq.n_responders].events = ZMQ_POLLIN; ++facilities.zmq.n_responders; } } } if (facilities.zmq.n_responders <= 1) { log_info("[config] a valid address to listen to was not found, exiting now"); rv = 1; goto cleanup; } // Fetch [networking] address int index = fetch_target_address(etsi_its_cfg->networking.zmq.addresses, etsi_its_cfg->networking.zmq.addresses_len); if (index != -1) { facilities.zmq.networking_address = malloc(strlen(etsi_its_cfg->networking.zmq.addresses[index])+1); strcpy(facilities.zmq.networking_address, etsi_its_cfg->networking.zmq.addresses[index]); } else { log_error("[config] a valid address for [networking] was not found"); rv = 1; goto cleanup; } // Fetch [applications] address index = fetch_target_address(itss_cfg->applications.zmq.addresses, itss_cfg->applications.zmq.addresses_len); if (index != -1) { facilities.zmq.applications_address = malloc(strlen(itss_cfg->applications.zmq.addresses[index])+1); strcpy(facilities.zmq.applications_address, itss_cfg->applications.zmq.addresses[index]); } else { log_error("[config] a valid address for [applications] was not found"); rv = 1; goto cleanup; } // Fetch [security] address index = fetch_target_address(itss_cfg->security.zmq.addresses, itss_cfg->security.zmq.addresses_len); if (index != -1) { facilities.zmq.security_address = malloc(strlen(itss_cfg->security.zmq.addresses[index])+1); strcpy(facilities.zmq.security_address, itss_cfg->security.zmq.addresses[index]); } else { log_error("[config] a valid address for [security] was not found"); rv = 1; goto cleanup; } // Fetch [management] address index = fetch_target_address(etsi_its_cfg->management.zmq.addresses, etsi_its_cfg->management.zmq.addresses_len); if (index != -1) { facilities.zmq.management_address = malloc(strlen(etsi_its_cfg->management.zmq.addresses[index])+1); strcpy(facilities.zmq.management_address, etsi_its_cfg->management.zmq.addresses[index]); } else { log_error("[config] a valid address for [management] was not found"); rv = 1; goto cleanup; } // Values // General if (!strcmp("obu", itss_cfg->general.itss_type)) { facilities.station_type = 5; } else if (!strcmp("rsu", itss_cfg->general.itss_type)) { facilities.station_type = 15; } else if (!strcmp("unknown", itss_cfg->general.itss_type)) { facilities.station_type = 0; } else if (!strcmp("pedestrian", itss_cfg->general.itss_type)) { facilities.station_type = 1; } else if (!strcmp("cyclist", itss_cfg->general.itss_type)) { facilities.station_type = 2; } else if (!strcmp("moped", itss_cfg->general.itss_type)) { facilities.station_type = 3; } else if (!strcmp("motorcycle", itss_cfg->general.itss_type)) { facilities.station_type = 4; } else if (!strcmp("passengerCar", itss_cfg->general.itss_type)) { facilities.station_type = 5; } else if (!strcmp("bus", itss_cfg->general.itss_type)) { facilities.station_type = 6; } else if (!strcmp("lightTruck", itss_cfg->general.itss_type)) { facilities.station_type = 7; } else if (!strcmp("heavyTruck", itss_cfg->general.itss_type)) { facilities.station_type = 8; } else if (!strcmp("trailer", itss_cfg->general.itss_type)) { facilities.station_type = 9; } else if (!strcmp("specialVehicles", itss_cfg->general.itss_type)) { facilities.station_type = 10; } else if (!strcmp("tram", itss_cfg->general.itss_type)) { facilities.station_type = 11; } else if (!strcmp("roadSideUnit", itss_cfg->general.itss_type)) { facilities.station_type = 15; } else { log_error("[config] unrecognized ITS-S type, running as OBU"); facilities.station_type = 5; } facilities.use_security = itss_cfg->security.use_security; pthread_mutex_init(&facilities.id.lock, NULL); pthread_mutex_init(&facilities.id.change.lock, NULL); facilities.id.change.random = itss_cfg->security.identity.random; if (facilities.id.change.random) { // Ask [security] for station id EIS_SecurityRequest_t* sREQ = calloc(1, sizeof(EIS_SecurityRequest_t)); EIS_SecurityReply_t* sREP = NULL; sREQ->present = EIS_SecurityRequest_PR_ids; sREQ->choice.ids.list.count = 2; sREQ->choice.ids.list.size = 2*sizeof(void*); sREQ->choice.ids.list.array = malloc(2*sizeof(void*)); sREQ->choice.ids.list.array[0] = calloc(1, sizeof(EIS_SecurityIdType_t)); *sREQ->choice.ids.list.array[0] = EIS_SecurityIdType_stationId; sREQ->choice.ids.list.array[1] = calloc(1, sizeof(EIS_SecurityIdType_t)); *sREQ->choice.ids.list.array[1] = EIS_SecurityIdType_ipv6Address; uint8_t b_tx[256], b_rx[256]; asn_enc_rval_t enc = oer_encode_to_buffer(&asn_DEF_EIS_SecurityRequest, NULL, sREQ, b_tx, 256); void* ss = itss_0connect(facilities.zmq.security_address, ZMQ_REQ); itss_0send(ss, b_tx, enc.encoded); int rl = itss_0recv_rt(&ss, b_rx, 256, b_tx, 256, -1); itss_0close(ss); if (rl == -1) { rv = 1; goto cleanup; } asn_dec_rval_t dec = oer_decode(NULL, &asn_DEF_EIS_SecurityReply, (void**) &sREP, b_rx, 256); if (sREP->returnCode == EIS_SecurityReplyReturnCode_rejected) { // TODO handle it goto cleanup; } for (int i = 0; i < sREP->data->choice.ids.list.count; ++i) { switch (sREP->data->choice.ids.list.array[i]->present) { case EIS_SecurityId_PR_stationId: facilities.id.station_id = sREP->data->choice.ids.list.array[i]->choice.stationId; break; case EIS_SecurityId_PR_ipv6Address: memcpy(facilities.id.ipv6_addr, sREP->data->choice.ids.list.array[i]->choice.ipv6Address.buf, 16); break; default: break; } } ASN_STRUCT_FREE(asn_DEF_EIS_SecurityRequest, sREQ); ASN_STRUCT_FREE(asn_DEF_EIS_SecurityReply, sREP); } else { facilities.id.station_id = itss_cfg->security.identity.station_id; uint8_t src_mac[6]; unsigned int tmp_uint[6]; sscanf(itss_cfg->security.identity.mac_address, "%02x:%02x:%02x:%02x:%02x:%02x", &tmp_uint[0], &tmp_uint[1], &tmp_uint[2], &tmp_uint[3], &tmp_uint[4], &tmp_uint[5]); src_mac[0] = (uint8_t)tmp_uint[0]; src_mac[1] = (uint8_t)tmp_uint[1]; src_mac[2] = (uint8_t)tmp_uint[2]; src_mac[3] = (uint8_t)tmp_uint[3]; src_mac[4] = (uint8_t)tmp_uint[4]; src_mac[5] = (uint8_t)tmp_uint[5]; memset(facilities.id.ipv6_addr, 0, 8); memcpy(facilities.id.ipv6_addr+8, src_mac, 3); facilities.id.ipv6_addr[11] = 0xff; facilities.id.ipv6_addr[12] = 0xfe; memcpy(facilities.id.ipv6_addr+13, src_mac+3, 3); facilities.id.ipv6_addr[8] ^= 0x02; } // Message version if (!strcmp("v1", etsi_its_cfg->facilities.mver.default_version)) { log_error("[config] only default message version 2 is supported"); return -1; } else if (!strcmp("v2", etsi_its_cfg->facilities.mver.default_version)) { facilities.pver.defaultv = 2; } else { log_warn("[config] unrecognized default messages version :: using version 2"); facilities.pver.defaultv = 2; } // DENM facilities.den.n_max_events = etsi_its_cfg->facilities.denm.nmax_active_events; // CAM facilities.lightship.active = etsi_its_cfg->facilities.cam.activate; facilities.lightship.vehicle_gen_min = etsi_its_cfg->facilities.cam.obu_period_min; facilities.lightship.vehicle_gen_max = etsi_its_cfg->facilities.cam.obu_period_max; facilities.lightship.rsu_gen_min = etsi_its_cfg->facilities.cam.rsu_period_min; facilities.lightship.rsu_vehicle_permanence = etsi_its_cfg->facilities.cam.rsu_vehicle_permanence; // IVIM facilities.infrastructure.n_max_services = etsi_its_cfg->facilities.ivim.nmax_active_services; facilities.infrastructure.replay_interval = etsi_its_cfg->facilities.ivim.replay_interval; facilities.infrastructure.default_service_duration = etsi_its_cfg->facilities.ivim.default_service_duration * 60000; // CPM facilities.dissemination.active = etsi_its_cfg->facilities.cpm.activate; facilities.dissemination.T_GenCpmMin = etsi_its_cfg->facilities.cpm.rsu_obu_period_min; facilities.dissemination.T_GenCpmMax = etsi_its_cfg->facilities.cpm.rsu_obu_period_max; // Rotation not needed anymore? //facilities.dissemination.radar_rotation = itss_cfg->radar.radar_rotation; facilities.dissemination.tmc_connect = etsi_its_cfg->facilities.cpm.tmc_connected; facilities.dissemination.T_AddSensorInformation = 1000; int oa_start = (360-facilities.dissemination.radar_rotation) * 10 - 500; int oa_end = (360-facilities.dissemination.radar_rotation) * 10 + 500; facilities.dissemination.opening_angle_start = (oa_start + 3600) % 3600; facilities.dissemination.opening_angle_end = (oa_end + 3600) % 3600; facilities.dissemination.int_radar = malloc(strlen(etsi_its_cfg->facilities.cpm.radar_interface)+1); strcpy(facilities.dissemination.int_radar,etsi_its_cfg->facilities.cpm.radar_interface); facilities.dissemination.ip_radar = malloc(strlen(etsi_its_cfg->facilities.cpm.radar_ip)+1); strcpy(facilities.dissemination.ip_radar,etsi_its_cfg->facilities.cpm.radar_ip); // TPM facilities.tolling.enabled = etsi_its_cfg->facilities.tpm.activate; if (!strcmp("gn-spki", etsi_its_cfg->facilities.tpm.protocol)) { facilities.tolling.protocol.p = TOLLING_PROTOCOL_GN_SPKI; } else if (!strcmp("gn-dpki", etsi_its_cfg->facilities.tpm.protocol)) { facilities.tolling.protocol.p = TOLLING_PROTOCOL_GN_DPKI; } else if (!strcmp("tls", etsi_its_cfg->facilities.tpm.protocol)) { facilities.tolling.protocol.p = TOLLING_PROTOCOL_TLS; } else if (!strcmp("tls-gn", etsi_its_cfg->facilities.tpm.protocol)) { facilities.tolling.protocol.p = TOLLING_PROTOCOL_TLS_GN; } else if (!strcmp("tls-shs", etsi_its_cfg->facilities.tpm.protocol)) { facilities.tolling.protocol.p = TOLLING_PROTOCOL_TLS_SHS; } else { log_error("[config] unrecognized tolling protocol, defaulting to 'gn-spki'"); facilities.tolling.protocol.p = TOLLING_PROTOCOL_GN_SPKI; } facilities.tolling.station.obu.client_id = etsi_its_cfg->facilities.tpm.client_id; // MCM facilities.coordination.active = etsi_its_cfg->facilities.mcm.activate; if (!strcmp("vcm-RR", etsi_its_cfg->facilities.mcm.protocol)) { facilities.coordination.protocol = MC_PROTOCOL_VCM_RR; } else if (!strcmp("vcm-RR1C", etsi_its_cfg->facilities.mcm.protocol)) { facilities.coordination.protocol = MC_PROTOCOL_VCM_RR1C; } else if (!strcmp("vcm-RRAC", etsi_its_cfg->facilities.mcm.protocol)) { facilities.coordination.protocol = MC_PROTOCOL_VCM_RRAC; } else { facilities.coordination.protocol = MC_PROTOCOL_VCM_RR; } facilities.coordination.vcm_period_min = etsi_its_cfg->facilities.mcm.period_min; facilities.coordination.vcm_period_max = etsi_its_cfg->facilities.mcm.period_max; // EVCSNM facilities.evm_args.activate = etsi_its_cfg->facilities.evcsnm.activate; // Replay facilities.replay = etsi_its_cfg->networking.replay.activate; // Forward unknown packets (in-stack) facilities.upf = etsi_its_cfg->general.unknown_packet_forwarding; // PZ if (facilities.station_type == 15) { int i = 0; DIR *d = opendir(etsi_its_cfg->facilities.protected_zones.path); struct dirent *dir; char file[256]; char pz_xml[DATA_BUFFER_SIZE]; if (d) { while ((dir = readdir(d)) != NULL && i < 16) { if (dir->d_name[0] == '.') continue; sprintf(file, "%s/%s", etsi_its_cfg->facilities.protected_zones.path, dir->d_name); FILE *fp = fopen(file, "r"); if (!fp) continue; fseek(fp, 0, SEEK_END); uint16_t size = ftell(fp); fseek(fp, 0, SEEK_SET); if (!size) { fclose(fp); continue; } if (!fread(pz_xml, 1, size, fp)) { fclose(fp); continue; } fclose(fp); EIS_ProtectedCommunicationZone_t *zone = calloc(1, sizeof(EIS_ProtectedCommunicationZone_t)); asn_dec_rval_t dec = xer_decode(NULL, &asn_DEF_EIS_ProtectedCommunicationZone, (void**) &zone, pz_xml, size); if (!dec.code) { facilities.lightship.protected_zones.pz[i] = (EI1_ProtectedCommunicationZone_t*)zone; ++facilities.lightship.protected_zones.pz_len; ++i; log_debug("[config] loaded protection zone @ (%lld, %lld)", zone->protectedZoneLatitude, zone->protectedZoneLongitude); } else { log_error("[config] failure to decode protection zone '%s'", dir->d_name); ASN_STRUCT_FREE(asn_DEF_EIS_ProtectedCommunicationZone, zone); } } closedir(d); } } // TZ if (facilities.station_type == 15) { int i = 0; DIR *d = opendir(etsi_its_cfg->facilities.tpm.tis_path); struct dirent *dir; char file[256]; char ti_xml[DATA_BUFFER_SIZE]; if (d) { while ((dir = readdir(d)) != NULL && i < TOLLING_INFOS_MAX_LENGTH) { if (dir->d_name[0] == '.') continue; sprintf(file, "%s/%s", etsi_its_cfg->facilities.tpm.tis_path, dir->d_name); FILE *fp = fopen(file, "r"); if (!fp) continue; fseek(fp, 0, SEEK_END); uint16_t size = ftell(fp); fseek(fp, 0, SEEK_SET); if (!size) { fclose(fp); continue; } if (!fread(ti_xml, 1, size, fp)) { fclose(fp); continue; } fclose(fp); EI1_TollingPaymentInfo_t *ti = calloc(1, sizeof(EI1_TollingPaymentInfo_t)); asn_dec_rval_t dec = xer_decode(NULL, &asn_DEF_EI1_TollingPaymentInfo, (void**) &ti, ti_xml, size); if (!dec.code) { facilities.tolling.infos.z[i] = calloc(1, sizeof(tolling_info_t)); facilities.tolling.infos.z[i]->asn = ti; ++facilities.tolling.infos.length; ++i; log_debug("[config] loaded tolling info | id:%lld type:%s", ti->id, ti->tollType==EI1_TollType_entry ? "entry": ti->tollType==EI1_TollType_exit ? "exit": "single" ); } else { log_error("[config] failure to decode tolling info '%s'", dir->d_name); ASN_STRUCT_FREE(asn_DEF_EI1_TollingPaymentInfo, ti); } } closedir(d); } } facilities.edm.enabled = itss_cfg->applications.extensions.enabled; EIS_ManagementRequest_t* mreq = calloc(1, sizeof(EIS_ManagementRequest_t)); mreq->present = EIS_ManagementRequest_PR_attributes; mreq->choice.attributes.present = EIS_ManagementRequestAttributes_PR_get; mreq->choice.attributes.choice.get.clockType = 1; mreq->choice.attributes.choice.get.clockOffset = 1; void* management_socket = itss_0connect(facilities.zmq.management_address, ZMQ_REQ); uint8_t b_tx[256], b_rx[256]; asn_enc_rval_t enc = oer_encode_to_buffer(&asn_DEF_EIS_ManagementRequest, NULL, mreq, b_tx, 256); itss_0send(management_socket, b_tx, enc.encoded); int rl = itss_0recv_rt(&management_socket, b_rx, 256, b_tx, 256, -1); itss_0close(management_socket); if (rl == -1) { rv = 1; goto cleanup; } EIS_ManagementReply_t* mrep = calloc(1, sizeof(EIS_ManagementReply_t)); oer_decode(NULL, &asn_DEF_EIS_ManagementReply, (void**) &mrep, b_rx, 256); long lat, lon, alt, alt_conf; if (mrep->returnCode == EIS_ManagementReplyReturnCode_accepted && mrep->data && mrep->data->choice.attributes.clockType && mrep->data->choice.attributes.clockOffset) { itss_epv_init(*mrep->data->choice.attributes.clockType, TIME_MILLISECONDS, itss_cfg->time.simulated.source); asn_INTEGER2uint64(mrep->data->choice.attributes.clockOffset, &epv.time.offset); } else { log_error("rejected MR attribute request"); rv = 1; goto cleanup; } ASN_STRUCT_FREE(asn_DEF_EIS_ManagementRequest, mreq); ASN_STRUCT_FREE(asn_DEF_EIS_ManagementReply, mrep); if (etsi_its_cfg->facilities.saem.activate) { // TODO handle various services facilities.bulletin.to_provide_len = 1; facilities.bulletin.to_provide[0] = calloc(1, sizeof(announcement_t)); facilities.bulletin.to_provide[0]->endpoint.port = 7010 + etsi_its_cfg->facilities.saem.service_to_advertise; facilities.bulletin.to_provide[0]->its_aid = etsi_its_cfg->facilities.saem.service_to_advertise; } facilities.vehicle.length = itss_cfg->vehicle.length; facilities.vehicle.width = itss_cfg->vehicle.width; facilities.vehicle.role = itss_cfg->vehicle.role; // Logging facilities.logging.recorder = etsi_its_cfg->facilities.logging.management; if (itss_cfg->general.logging.enabled && etsi_its_cfg->facilities.logging.dbms) { facilities.logging.dbms = calloc(1, sizeof(itss_db_t)); if (itss_db_init( facilities.logging.dbms, itss_cfg->general.logging.database, itss_cfg->general.logging.table_style, ITSS_FACILITIES, itss_cfg->general.logging.host, itss_cfg->general.logging.port, itss_cfg->general.logging.username, itss_cfg->general.logging.password )) { log_error("failed to initialize the database -> turning off logging"); free(facilities.logging.dbms); facilities.logging.dbms = NULL; } } cleanup: t2c_itss_free(itss_cfg); t2c_etsi_its_free(etsi_its_cfg); return rv; }