From a95f6c71ee71b635fca9ecaa7caef534b30f53a4 Mon Sep 17 00:00:00 2001 From: dhfelix Date: Thu, 26 Mar 2020 12:20:08 -0600 Subject: [PATCH] Add checksum comparison for slurm files If no new or modified slurm files were found, same old slurm will apply and this will avoid to parse the same previous slurm file. --- src/crypto/hash.c | 10 ++- src/crypto/hash.h | 4 ++ src/rtr/db/vrps.c | 4 +- src/slurm/db_slurm.c | 32 +++++++++ src/slurm/db_slurm.h | 18 +++++ src/slurm/slurm_loader.c | 151 ++++++++++++++++++++++++++++++++++++--- 6 files changed, 206 insertions(+), 13 deletions(-) diff --git a/src/crypto/hash.c b/src/crypto/hash.c index 53ec81ed..cd747ccb 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -36,6 +36,14 @@ hash_matches(unsigned char const *expected, size_t expected_len, static int hash_file(char const *algorithm, struct rpki_uri *uri, unsigned char *result, unsigned int *result_len) +{ + return hash_local_file(algorithm, uri_get_local(uri), result, + result_len); +} + +int +hash_local_file(char const *algorithm, char const *uri, unsigned char *result, + unsigned int *result_len) { EVP_MD const *md; FILE *file; @@ -50,7 +58,7 @@ hash_file(char const *algorithm, struct rpki_uri *uri, unsigned char *result, if (error) return error; - error = file_open(uri_get_local(uri), &file, &stat); + error = file_open(uri, &file, &stat); if (error) return error; diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 43c2af61..65910287 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -15,4 +15,8 @@ int hash_validate(char const *, unsigned char const *, size_t, int hash_validate_octet_string(char const *, OCTET_STRING_t const*, OCTET_STRING_t const *); +int hash_local_file(char const *, char const *, unsigned char *, + unsigned int *); + + #endif /* SRC_HASH_H_ */ diff --git a/src/rtr/db/vrps.c b/src/rtr/db/vrps.c index 03ed98ee..a54dbc2c 100644 --- a/src/rtr/db/vrps.c +++ b/src/rtr/db/vrps.c @@ -290,10 +290,8 @@ __vrps_update(bool *changed) return error; error = slurm_apply(&new_base, &state.slurm); - if (error) { - rwlock_unlock(&state_lock); + if (error) goto revert_base; - } rwlock_write_lock(&state_lock); diff --git a/src/slurm/db_slurm.c b/src/slurm/db_slurm.c index 7a3e4250..bff42a93 100644 --- a/src/slurm/db_slurm.c +++ b/src/slurm/db_slurm.c @@ -36,6 +36,7 @@ struct db_slurm { struct slurm_lists *cache; bool loaded_date_set; time_t loaded_date; + struct slurm_csum_list csum_list; }; char addr_buf[INET6_ADDRSTRLEN]; @@ -112,6 +113,9 @@ db_slurm_create(struct db_slurm **result) db->loaded_date_set = false; db->cache = NULL; + SLIST_INIT(&db->csum_list); + db->csum_list.list_size = 0; + *result = db; return 0; } @@ -672,8 +676,36 @@ db_slurm_has_data(struct db_slurm *db) void db_slurm_destroy(struct db_slurm *db) { + struct slurm_file_csum *tmp; + slurm_lists_cleanup(&db->lists); if (db->cache) slurm_lists_destroy(db->cache); + + while (!SLIST_EMPTY(&db->csum_list)) { + tmp = SLIST_FIRST(&db->csum_list); + SLIST_REMOVE_HEAD(&db->csum_list, next); + free(tmp); + } + free(db); } + +int +db_slurm_set_csum_list(struct db_slurm *db, struct slurm_csum_list *list) +{ + if (!SLIST_EMPTY(&db->csum_list)) + return pr_err("Checksum list for SLURM DB must be empty"); + + db->csum_list.slh_first = list->slh_first; + db->csum_list.list_size = list->list_size; + return 0; +} + +void +db_slurm_get_csum_list(struct db_slurm *db, struct slurm_csum_list *result) +{ + result->list_size = db->csum_list.list_size; + result->slh_first = db->csum_list.slh_first; +} + diff --git a/src/slurm/db_slurm.h b/src/slurm/db_slurm.h index 9bb12abe..c644f8b3 100644 --- a/src/slurm/db_slurm.h +++ b/src/slurm/db_slurm.h @@ -2,6 +2,9 @@ #define SRC_SLURM_db_slurm_H_ #include +#include +#include + #include "rtr/db/vrp.h" /* Flags to get data from structs */ @@ -27,6 +30,18 @@ struct slurm_bgpsec { unsigned char *router_public_key; }; + +struct slurm_file_csum { + unsigned char csum[EVP_MAX_MD_SIZE]; + unsigned int csum_len; + SLIST_ENTRY(slurm_file_csum) next; +}; + +struct slurm_csum_list { + struct slurm_file_csum *slh_first; /* first element */ + unsigned int list_size; +}; + struct db_slurm; typedef int (*prefix_foreach_cb)(struct slurm_prefix *, void *); @@ -62,4 +77,7 @@ bool db_slurm_has_data(struct db_slurm *); void db_slurm_destroy(struct db_slurm *); +int db_slurm_set_csum_list(struct db_slurm *, struct slurm_csum_list *); +void db_slurm_get_csum_list(struct db_slurm *, struct slurm_csum_list *); + #endif /* SRC_SLURM_db_slurm_H_ */ diff --git a/src/slurm/slurm_loader.c b/src/slurm/slurm_loader.c index 17bbab49..00395e4d 100644 --- a/src/slurm/slurm_loader.c +++ b/src/slurm/slurm_loader.c @@ -8,6 +8,7 @@ #include "log.h" #include "config.h" #include "common.h" +#include "crypto/hash.h" #include "slurm/slurm_parser.h" #define SLURM_FILE_EXTENSION ".slurm" @@ -135,19 +136,71 @@ slurm_create_parser_params(struct slurm_parser_params **result) return 0; } -int -slurm_apply(struct db_table **base, struct db_slurm **last_slurm) +static int +__slurm_load_checksums(char const *location, void *arg) { - struct slurm_parser_params *params = NULL; + struct slurm_csum_list *list; + struct slurm_file_csum *csum; int error; - if (config_get_slurm() == NULL) - return 0; + list = arg; + csum = malloc(sizeof(struct slurm_file_csum)); + if (csum == NULL) + return pr_enomem(); - pr_info("Applying configured SLURM"); - error = slurm_create_parser_params(¶ms); - if (error) + + error = hash_local_file("sha256", location, csum->csum, + &csum->csum_len); + if (error) { + free(csum); + return pr_err("Calculating slurm hash"); + } + + SLIST_INSERT_HEAD(list, csum, next); + list->list_size++; + + return 0; +} + +static void +destroy_local_csum_list(struct slurm_csum_list *list) +{ + struct slurm_file_csum *tmp; + + while (!SLIST_EMPTY(list)) { + tmp = SLIST_FIRST(list); + SLIST_REMOVE_HEAD(list, next); + free(tmp); + } +} + +static int +slurm_load_checksums(struct slurm_csum_list *csum_list) +{ + struct slurm_csum_list result; + int error; + + SLIST_INIT(&result); + result.list_size = 0; + + error = process_file_or_dir(config_get_slurm(), SLURM_FILE_EXTENSION, + __slurm_load_checksums, &result); + if (error) { + destroy_local_csum_list(&result); return error; + } + + csum_list->list_size = result.list_size; + csum_list->slh_first = result.slh_first; + + return 0; +} + +static void +__slurm_apply(struct db_slurm **last_slurm, struct slurm_parser_params *params, + struct slurm_csum_list *csum_list) +{ + int error; error = slurm_load(params); if (!error) { @@ -155,8 +208,10 @@ slurm_apply(struct db_table **base, struct db_slurm **last_slurm) if (*last_slurm != NULL) db_slurm_destroy(*last_slurm); *last_slurm = params->db_slurm; - if (*last_slurm != NULL) + if (*last_slurm != NULL) { db_slurm_update_time(*last_slurm); + db_slurm_set_csum_list(*last_slurm, csum_list); + } } else { /* Any error: use last valid SLURM */ pr_warn("Error loading SLURM, the validation will still continue."); @@ -166,6 +221,84 @@ slurm_apply(struct db_table **base, struct db_slurm **last_slurm) /* Log applied SLURM as info */ db_slurm_log(params->db_slurm); } + destroy_local_csum_list(csum_list); + } +} + +static bool +are_csum_lists_equals(struct slurm_csum_list *new_list, + struct slurm_csum_list *old_list) +{ + struct slurm_file_csum *newcsum, *old; + bool found = false; + + if (new_list->list_size != old_list->list_size) { + return false; + } + + SLIST_FOREACH(newcsum, new_list, next) { + SLIST_FOREACH(old, old_list, next) { + + if (newcsum->csum_len != old->csum_len) + continue; + + if (memcmp(newcsum->csum, old->csum, + newcsum->csum_len) == 0) { + found = true; + break; + } + } + + if (!found) + return false; + + found = false; + } + + return true; +} + +int +slurm_apply(struct db_table **base, struct db_slurm **last_slurm) +{ + struct slurm_parser_params *params = NULL; + struct slurm_csum_list csum_list, old_csum_list; + int error; + bool list_equals = false; + + if (config_get_slurm() == NULL) + return 0; + + pr_info("Checking if there are new or modified SLURM files"); + error = slurm_load_checksums(&csum_list); + if (error) + return error; + + if (*last_slurm != NULL) { + db_slurm_get_csum_list(*last_slurm, &old_csum_list); + list_equals = are_csum_lists_equals(&csum_list, &old_csum_list); + } + + error = slurm_create_parser_params(¶ms); + if (error) + return error; + + if (list_equals) { + if (*last_slurm != NULL) { + pr_info("Applying same old SLURM, no changes found."); + params->db_slurm = *last_slurm; + } + destroy_local_csum_list(&csum_list); + } else { + if (csum_list.list_size == 0) { + if (*last_slurm != NULL) + db_slurm_destroy(*last_slurm); + *last_slurm = params->db_slurm; + /* Empty DIR or FILE SLURM not found */ + goto success; + } + pr_info("Applying configured SLURM"); + __slurm_apply(last_slurm, params, &csum_list); } /* If there's no new SLURM loaded, stop */ -- 2.47.3