From: pcarana Date: Fri, 11 Oct 2019 22:30:12 +0000 (-0500) Subject: Remember last valid SLURM in case of syntax error with newer SLURM(s). X-Git-Tag: v1.2.0~72 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ee5123439b34f91144017706aad308c3b33d511c;p=thirdparty%2FFORT-validator.git Remember last valid SLURM in case of syntax error with newer SLURM(s). -Remove 'comment' member from slurm structs, there's no need to store its value. -Rename 'slurm/slurm_db.*' to 'slurm/db_slurm.*'. -Allocate SLURM data, so that the last valid SLURM can be used if needed; so, now the SLURM lives on the heap and is 'remembered' as part of the VRPs state. Also remember the date and time when the last valid SLURM was loaded. -Move 'slurm_bgpsec' and 'slurm_prefix' structs, and SLURM data flags to 'db_slurm.h'. -Update 'slurm_parser' to return a specific error in case of a syntax error, so that further actions can be taken (ignore slurm, use last valid version, or store as the last valid version). -In case a previous valid version of SLURM is utilized, log a WARNING indicating that such action is being taken, and log SLURM content at INFO level. -Fix bug at common function 'process_file', there was an issue before releasing temporal pointers. --- diff --git a/src/Makefile.am b/src/Makefile.am index 78b75eac..cbfb567f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -94,7 +94,7 @@ fort_SOURCES += rtr/db/roa.c rtr/db/roa.h fort_SOURCES += rtr/db/vrp.h fort_SOURCES += rtr/db/vrps.c rtr/db/vrps.h -fort_SOURCES += slurm/slurm_db.c slurm/slurm_db.h +fort_SOURCES += slurm/db_slurm.c slurm/db_slurm.h fort_SOURCES += slurm/slurm_loader.c slurm/slurm_loader.h fort_SOURCES += slurm/slurm_parser.c slurm/slurm_parser.h diff --git a/src/common.c b/src/common.c index 8ef84aca..5083adc7 100644 --- a/src/common.c +++ b/src/common.c @@ -120,8 +120,8 @@ process_file(char const *dir_name, char const *file_name, char const *file_ext, } error = cb(fullpath, arg); - free(tmp); free(fullpath); + free(tmp); return error; } diff --git a/src/rtr/db/vrps.c b/src/rtr/db/vrps.c index 9d5dd0b1..aad6646a 100644 --- a/src/rtr/db/vrps.c +++ b/src/rtr/db/vrps.c @@ -51,13 +51,15 @@ struct state { * during the current iteration.) */ struct db_table *base; - /** ROA changes to @base over time. */ + /** DB changes to @base over time. */ struct deltas_db deltas; + /* Last valid SLURM applied to base */ + struct db_slurm *slurm; + serial_t next_serial; uint16_t v0_session_id; uint16_t v1_session_id; - time_t last_modified_date; } state; /** Read/write lock, which protects @state and its inhabitants. */ @@ -95,6 +97,8 @@ vrps_init(void) ? (state.v0_session_id - 1) : (0xFFFFu); + state.slurm = NULL; + error = pthread_rwlock_init(&state_lock, NULL); if (error) { deltas_db_cleanup(&state.deltas, deltagroup_cleanup); @@ -116,6 +120,8 @@ vrps_destroy(void) { if (state.base != NULL) db_table_destroy(state.base); + if (state.slurm != NULL) + db_slurm_destroy(state.slurm); deltas_db_cleanup(&state.deltas, deltagroup_cleanup); /* Nothing to do with error codes from now on */ pthread_rwlock_destroy(&state_lock); @@ -274,16 +280,11 @@ vrps_update(bool *changed) rwlock_write_lock(&state_lock); - /* - * TODO (next iteration) Remember the last valid SLURM - * - * Currently SLURM is ignored if it has errors, the error is logged and - * the new_base isn't altered. Instead of this, the last valid SLURM - * should be remembered, and will be applied when a new SLURM has - * errors; a warning should be logged to indicate which version of the - * SLURM is being applied. - */ - slurm_apply(&new_base); + error = slurm_apply(&new_base, &state.slurm); + if (error) { + rwlock_unlock(&state_lock); + goto revert_base; + } if (state.base != NULL) { error = compute_deltas(state.base, new_base, &deltas); diff --git a/src/slurm/slurm_db.c b/src/slurm/db_slurm.c similarity index 59% rename from src/slurm/slurm_db.c rename to src/slurm/db_slurm.c index be592d85..06297bee 100644 --- a/src/slurm/slurm_db.c +++ b/src/slurm/db_slurm.c @@ -1,9 +1,12 @@ -#include "slurm_db.h" +#include "db_slurm.h" #include +#include +#include #include /* AF_INET, AF_INET6 (needed in OpenBSD) */ #include /* AF_INET, AF_INET6 (needed in OpenBSD) */ +#include "crypto/base64.h" #include "data_structure/array_list.h" #include "object/router_key.h" @@ -22,20 +25,35 @@ ARRAY_LIST(al_assertion_prefix, struct slurm_prefix_ctx) ARRAY_LIST(al_filter_bgpsec, struct slurm_bgpsec_ctx) ARRAY_LIST(al_assertion_bgpsec, struct slurm_bgpsec_ctx) -struct arraylist_db { +struct db_slurm { struct al_filter_prefix filter_pfx_al; struct al_assertion_prefix assertion_pfx_al; struct al_filter_bgpsec filter_bgps_al; struct al_assertion_bgpsec assertion_bgps_al; -} array_lists_db; + bool loaded_date_set; + time_t loaded_date; +}; -void -slurm_db_init(void) +char addr_buf[INET6_ADDRSTRLEN]; + +int +db_slurm_create(struct db_slurm **result) { - al_filter_prefix_init(&array_lists_db.filter_pfx_al); - al_assertion_prefix_init(&array_lists_db.assertion_pfx_al); - al_filter_bgpsec_init(&array_lists_db.filter_bgps_al); - al_assertion_bgpsec_init(&array_lists_db.assertion_bgps_al); + struct db_slurm *db; + + db = malloc(sizeof(struct db_slurm)); + if (db == NULL) + return pr_enomem(); + + /* Not ready yet (nor required yet) for multithreading */ + al_filter_prefix_init(&db->filter_pfx_al); + al_assertion_prefix_init(&db->assertion_pfx_al); + al_filter_bgpsec_init(&db->filter_bgps_al); + al_assertion_bgpsec_init(&db->assertion_bgps_al); + db->loaded_date_set = false; + + *result = db; + return 0; } static bool @@ -221,19 +239,19 @@ bgpsec_equal(struct slurm_bgpsec_ctx *left_ctx, struct slurm_bgpsec *right, #define ADD_FUNCS(name, type, list_name, db_list, db_alt_list, equal_cb,\ cont_cb, filter) \ static type * \ - name##_locate(type *obj, bool flt, int ctx) \ + name##_locate(struct db_slurm *db, type *obj, bool flt, int ctx)\ { \ type##_ctx *cursor; \ array_index i; \ \ - ARRAYLIST_FOREACH(db_list, cursor, i) \ + ARRAYLIST_FOREACH(&db->db_list, cursor, i) \ if (equal_cb(cursor, obj, ctx, filter, flt)) \ return &cursor->element; \ \ if (ctx < 0) \ return NULL; /* Avoid the next loop */ \ \ - ARRAYLIST_FOREACH(db_alt_list, cursor, i) \ + ARRAYLIST_FOREACH(&db->db_alt_list, cursor, i) \ if (cont_cb(cursor, obj, ctx)) \ return &cursor->element; \ \ @@ -241,66 +259,68 @@ bgpsec_equal(struct slurm_bgpsec_ctx *left_ctx, struct slurm_bgpsec *right, } \ \ static bool \ - name##_exists(type *obj, bool flt, int ctx) \ + name##_exists(struct db_slurm *db, type *obj, bool flt, int ctx)\ { \ - return name##_locate(obj, flt, ctx) != NULL; \ + return name##_locate(db, obj, flt, ctx) != NULL; \ } \ \ int \ - slurm_db_add_##name(type *elem, int ctx) \ + db_slurm_add_##name(struct db_slurm *db, type *elem, int ctx) \ { \ type##_ctx new_elem; \ - if (name##_exists(elem, !filter, ctx)) \ + if (name##_exists(db, elem, !filter, ctx)) \ return -EEXIST; \ new_elem.element = *elem; \ new_elem.ctx = ctx; \ - return list_name##_add(db_list, &new_elem); \ + return list_name##_add(&db->db_list, &new_elem); \ } -ADD_FUNCS(prefix_filter, struct slurm_prefix, al_filter_prefix, - &array_lists_db.filter_pfx_al, &array_lists_db.assertion_pfx_al, - prefix_equal, prefix_contained, true) -ADD_FUNCS(bgpsec_filter, struct slurm_bgpsec, al_filter_bgpsec, - &array_lists_db.filter_bgps_al, &array_lists_db.assertion_bgps_al, - bgpsec_equal, bgpsec_contained, true) +ADD_FUNCS(prefix_filter, struct slurm_prefix, al_filter_prefix, filter_pfx_al, + assertion_pfx_al, prefix_equal, prefix_contained, true) +ADD_FUNCS(bgpsec_filter, struct slurm_bgpsec, al_filter_bgpsec, filter_bgps_al, + assertion_bgps_al, bgpsec_equal, bgpsec_contained, true) ADD_FUNCS(prefix_assertion, struct slurm_prefix, al_assertion_prefix, - &array_lists_db.assertion_pfx_al, &array_lists_db.filter_pfx_al, - prefix_equal, prefix_contained, false) + assertion_pfx_al, filter_pfx_al, prefix_equal, prefix_contained, false) ADD_FUNCS(bgpsec_assertion, struct slurm_bgpsec, al_assertion_bgpsec, - &array_lists_db.assertion_bgps_al, &array_lists_db.filter_bgps_al, - bgpsec_equal, bgpsec_contained, false) + assertion_bgps_al, filter_bgps_al, bgpsec_equal, bgpsec_contained, false) bool -slurm_db_vrp_is_filtered(struct vrp const *vrp) +db_slurm_vrp_is_filtered(struct db_slurm *db, struct vrp const *vrp) { struct slurm_prefix slurm_prefix; slurm_prefix.data_flag = SLURM_COM_FLAG_ASN | SLURM_PFX_FLAG_PREFIX | SLURM_PFX_FLAG_MAX_LENGTH; slurm_prefix.vrp = *vrp; - slurm_prefix.comment = NULL; - return prefix_filter_exists(&slurm_prefix, true, -1); + return prefix_filter_exists(db, &slurm_prefix, true, -1); } -int -slurm_db_foreach_assertion_prefix(assertion_pfx_foreach_cb cb, void *arg) -{ - struct slurm_prefix_ctx *cursor; - array_index i; - int error; - - ARRAYLIST_FOREACH(&array_lists_db.assertion_pfx_al, cursor, i) { - error = cb(&cursor->element, arg); - if (error) - return error; +#define ITERATE_LIST_FUNC(type, object, db_list) \ + int \ + db_slurm_foreach_##type##_##object(struct db_slurm *db, \ + object##_foreach_cb cb, void *arg) \ + { \ + struct slurm_##object##_ctx *cursor; \ + array_index i; \ + int error; \ + \ + ARRAYLIST_FOREACH(&db->db_list, cursor, i) { \ + error = cb(&cursor->element, arg); \ + if (error) \ + return error; \ + } \ + \ + return 0; \ } - return 0; -} +ITERATE_LIST_FUNC(filter, prefix, filter_pfx_al) +ITERATE_LIST_FUNC(filter, bgpsec, filter_bgps_al) +ITERATE_LIST_FUNC(assertion, prefix, assertion_pfx_al) +ITERATE_LIST_FUNC(assertion, bgpsec, assertion_bgps_al) bool -slurm_db_bgpsec_is_filtered(struct router_key const *key) +db_slurm_bgpsec_is_filtered(struct db_slurm *db, struct router_key const *key) { struct slurm_bgpsec slurm_bgpsec; unsigned char *tmp; @@ -317,56 +337,150 @@ slurm_db_bgpsec_is_filtered(struct router_key const *key) slurm_bgpsec.ski = tmp; /* Router public key isn't used at filters */ slurm_bgpsec.router_public_key = NULL; - slurm_bgpsec.comment = NULL; - result = bgpsec_filter_exists(&slurm_bgpsec, true, -1); + result = bgpsec_filter_exists(db, &slurm_bgpsec, true, -1); free(tmp); return result; } -int -slurm_db_foreach_assertion_bgpsec(assertion_bgpsec_foreach_cb cb, void *arg) +static void +clean_slurm_bgpsec(struct slurm_bgpsec_ctx *bgpsec) { - struct slurm_bgpsec_ctx *cursor; - array_index i; - int error; + if ((bgpsec->element.data_flag & SLURM_BGPS_FLAG_SKI) > 0) + free(bgpsec->element.ski); + if ((bgpsec->element.data_flag & SLURM_BGPS_FLAG_ROUTER_KEY) > 0) + free(bgpsec->element.router_public_key); +} - ARRAYLIST_FOREACH(&array_lists_db.assertion_bgps_al, cursor, i) { - error = cb(&cursor->element, arg); - if (error) - return error; +void +db_slurm_update_time(struct db_slurm *db) +{ + db->loaded_date = time(NULL); + db->loaded_date_set = true; +} + +static char const * +strv4addr(struct in_addr const *addr) +{ + return inet_ntop(AF_INET, addr, addr_buf, INET6_ADDRSTRLEN); +} + +static char const * +strv6addr(struct in6_addr const *addr) +{ + return inet_ntop(AF_INET6, addr, addr_buf, INET6_ADDRSTRLEN); +} + +static int +print_prefix_data(struct slurm_prefix *prefix, void *arg) +{ + char *pad = " "; + + pr_info(" {"); + if (prefix->data_flag & SLURM_COM_FLAG_ASN) + pr_info("%s ASN: %u", pad, prefix->vrp.asn); + + if (prefix->data_flag & SLURM_PFX_FLAG_PREFIX) { + switch(prefix->vrp.addr_fam) { + case AF_INET: + pr_info("%s Prefix: %s/%u", pad, + strv4addr(&prefix->vrp.prefix.v4), + prefix->vrp.prefix_length); + break; + case AF_INET6: + pr_info("%s Prefix: %s/%u", pad, + strv6addr(&prefix->vrp.prefix.v6), + prefix->vrp.prefix_length); + break; + default: + pr_crit("Unknown addr family type: %u", + prefix->vrp.addr_fam); + } } + if (prefix->data_flag & SLURM_PFX_FLAG_MAX_LENGTH) + pr_info("%s Max prefix length: %u", pad, + prefix->vrp.max_prefix_length); + pr_info(" }"); + return 0; } -static void -clean_slurm_prefix(struct slurm_prefix_ctx *prefix) +static int +print_bgpsec_data(struct slurm_bgpsec *bgpsec, void *arg) { - if ((prefix->element.data_flag & SLURM_COM_FLAG_COMMENT) > 0) - free(prefix->element.comment); + char *pad = " "; + char *buf; + int error; + + pr_info(" {"); + if (bgpsec->data_flag & SLURM_COM_FLAG_ASN) + pr_info("%s ASN: %u", pad, bgpsec->asn); + + if (bgpsec->data_flag & SLURM_BGPS_FLAG_SKI) { + do { + error = base64url_encode(bgpsec->ski, RK_SKI_LEN, &buf); + if (error) { + pr_info("%s SKI: ", pad); + break; + } + pr_info("%s SKI: %s", pad, buf); + free(buf); + } while (0); + } + + if (bgpsec->data_flag & SLURM_BGPS_FLAG_ROUTER_KEY) { + do { + error = base64url_encode(bgpsec->router_public_key, + RK_SPKI_LEN, &buf); + if (error) { + pr_info("%s Router public key: ", + pad); + break; + } + pr_info("%s Router public key: %s", pad, buf); + free(buf); + } while (0); + } + pr_info(" }"); + + return 0; } -static void -clean_slurm_bgpsec(struct slurm_bgpsec_ctx *bgpsec) +void +db_slurm_log(struct db_slurm *db) { - if ((bgpsec->element.data_flag & SLURM_BGPS_FLAG_SKI) > 0) - free(bgpsec->element.ski); - if ((bgpsec->element.data_flag & SLURM_BGPS_FLAG_ROUTER_KEY) > 0) - free(bgpsec->element.router_public_key); - if ((bgpsec->element.data_flag & SLURM_COM_FLAG_COMMENT) > 0) - free(bgpsec->element.comment); + if (db->loaded_date_set) + pr_info("SLURM loaded at %s", + asctime(localtime(&db->loaded_date))); + pr_info("Validation output filters {"); + pr_info(" Prefix filters {"); + db_slurm_foreach_filter_prefix(db, print_prefix_data, NULL); + pr_info(" }"); + pr_info(" BGPsec filters {"); + db_slurm_foreach_filter_bgpsec(db, print_bgpsec_data, NULL); + pr_info(" }"); + pr_info("}"); + + pr_info("Locally added assertions {"); + pr_info(" Prefix assertions {"); + db_slurm_foreach_assertion_prefix(db, print_prefix_data, NULL); + pr_info(" }"); + pr_info(" BGPsec assertions {"); + db_slurm_foreach_assertion_bgpsec(db, print_bgpsec_data, NULL); + pr_info(" }"); + pr_info("}"); } void -slurm_db_cleanup(void) +db_slurm_destroy(struct db_slurm *db) { - al_filter_prefix_cleanup(&array_lists_db.filter_pfx_al, - clean_slurm_prefix); - al_filter_bgpsec_cleanup(&array_lists_db.filter_bgps_al, + /* No need to cleanup prefixes (filters or assertions) */ + al_filter_prefix_cleanup(&db->filter_pfx_al, NULL); + al_filter_bgpsec_cleanup(&db->filter_bgps_al, clean_slurm_bgpsec); - al_assertion_prefix_cleanup(&array_lists_db.assertion_pfx_al, - clean_slurm_prefix); - al_assertion_bgpsec_cleanup(&array_lists_db.assertion_bgps_al, + al_assertion_prefix_cleanup(&db->assertion_pfx_al, NULL); + al_assertion_bgpsec_cleanup(&db->assertion_bgps_al, clean_slurm_bgpsec); + free(db); } diff --git a/src/slurm/db_slurm.h b/src/slurm/db_slurm.h new file mode 100644 index 00000000..03227dcf --- /dev/null +++ b/src/slurm/db_slurm.h @@ -0,0 +1,63 @@ +#ifndef SRC_SLURM_db_slurm_H_ +#define SRC_SLURM_db_slurm_H_ + +#include +#include "rtr/db/vrp.h" + +/* Flags to get data from structs */ +#define SLURM_COM_FLAG_NONE 0x00 +#define SLURM_COM_FLAG_ASN 0x01 +#define SLURM_COM_FLAG_COMMENT 0x02 + +#define SLURM_PFX_FLAG_PREFIX 0x04 +#define SLURM_PFX_FLAG_MAX_LENGTH 0x08 + +#define SLURM_BGPS_FLAG_SKI 0x04 +#define SLURM_BGPS_FLAG_ROUTER_KEY 0x08 + +struct slurm_prefix { + uint8_t data_flag; + struct vrp vrp; +}; + +struct slurm_bgpsec { + uint8_t data_flag; + uint32_t asn; + unsigned char *ski; + unsigned char *router_public_key; +}; + +struct db_slurm; + +typedef int (*prefix_foreach_cb)(struct slurm_prefix *, void *); +typedef int (*bgpsec_foreach_cb)(struct slurm_bgpsec *, void *); + +int db_slurm_create(struct db_slurm **); + +int db_slurm_add_prefix_filter(struct db_slurm *, struct slurm_prefix *, int); +int db_slurm_add_prefix_assertion(struct db_slurm *, struct slurm_prefix *, + int); +int db_slurm_add_bgpsec_filter(struct db_slurm *, struct slurm_bgpsec *, int); +int db_slurm_add_bgpsec_assertion(struct db_slurm *, struct slurm_bgpsec *, + int); + +bool db_slurm_vrp_is_filtered(struct db_slurm *, struct vrp const *); +bool db_slurm_bgpsec_is_filtered(struct db_slurm *, struct router_key const *); + +int db_slurm_foreach_filter_prefix(struct db_slurm *, prefix_foreach_cb, + void *); +int db_slurm_foreach_filter_bgpsec(struct db_slurm *, bgpsec_foreach_cb, + void *); +int db_slurm_foreach_assertion_prefix(struct db_slurm *, prefix_foreach_cb, + void *); +int db_slurm_foreach_assertion_bgpsec(struct db_slurm *, bgpsec_foreach_cb, + void *); + +/* Set the last update to current datetime */ +void db_slurm_update_time(struct db_slurm *); +/* Log the DB in human readable form at INFO level */ +void db_slurm_log(struct db_slurm *); + +void db_slurm_destroy(struct db_slurm *); + +#endif /* SRC_SLURM_db_slurm_H_ */ diff --git a/src/slurm/slurm_db.h b/src/slurm/slurm_db.h deleted file mode 100644 index 7b5d9190..00000000 --- a/src/slurm/slurm_db.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef SRC_SLURM_SLURM_DB_H_ -#define SRC_SLURM_SLURM_DB_H_ - -#include -#include "slurm/slurm_parser.h" -#include "rtr/db/vrp.h" - -typedef int (*assertion_pfx_foreach_cb)(struct slurm_prefix *, void *); -typedef int (*assertion_bgpsec_foreach_cb)(struct slurm_bgpsec *, void *); - -void slurm_db_init(void); - -int slurm_db_add_prefix_filter(struct slurm_prefix *, int); -int slurm_db_add_prefix_assertion(struct slurm_prefix *, int); -int slurm_db_add_bgpsec_filter(struct slurm_bgpsec *, int); -int slurm_db_add_bgpsec_assertion(struct slurm_bgpsec *, int); - -bool slurm_db_vrp_is_filtered(struct vrp const *); -int slurm_db_foreach_assertion_prefix(assertion_pfx_foreach_cb, void *); - -bool slurm_db_bgpsec_is_filtered(struct router_key const *); -int slurm_db_foreach_assertion_bgpsec(assertion_bgpsec_foreach_cb, void *); - -void slurm_db_cleanup(void); - -#endif /* SRC_SLURM_SLURM_DB_H_ */ diff --git a/src/slurm/slurm_loader.c b/src/slurm/slurm_loader.c index 1e65a36b..517dbe9c 100644 --- a/src/slurm/slurm_loader.c +++ b/src/slurm/slurm_loader.c @@ -8,42 +8,53 @@ #include "log.h" #include "config.h" #include "common.h" -#include "slurm/slurm_db.h" #include "slurm/slurm_parser.h" #define SLURM_FILE_EXTENSION ".slurm" +/* + * Load the SLURM file(s) from the configured path, if the path is valid but no + * data is loaded (specific error for a SLURM folder) return -ENOENT error. + * + * Expect an EEXIST error from slurm_parse() if there's a syntax error. + */ static int -slurm_load(bool *loaded) +slurm_load(struct slurm_parser_params *params) { - int ctx = 0; /* Context (file number) */ - /* Optional configuration */ - *loaded = false; - if (config_get_slurm() == NULL) - return 0; + struct db_slurm *db; + int error; - *loaded = true; - slurm_db_init(); + error = db_slurm_create(&db); + if (error) + return error; - return process_file_or_dir(config_get_slurm(), SLURM_FILE_EXTENSION, - slurm_parse, &ctx); -} + params->db_slurm = db; -static void -slurm_cleanup(void) -{ - /* Only if the SLURM was configured */ - if (config_get_slurm() != NULL) - slurm_db_cleanup(); + error = process_file_or_dir(config_get_slurm(), SLURM_FILE_EXTENSION, + slurm_parse, params); + if (error) + goto err; + + /* A unmodified context means that no SLURM was loaded */ + if(params->cur_ctx == 0) { + error = -ENOENT; + goto err; + } + + return 0; +err: + db_slurm_destroy(db); + params->db_slurm = NULL; + return error; } static int slurm_pfx_filters_apply(struct vrp const *vrp, void *arg) { - struct db_table *table = arg; + struct slurm_parser_params *params = arg; - if (slurm_db_vrp_is_filtered(vrp)) - db_table_remove_roa(table, vrp); + if (db_slurm_vrp_is_filtered(params->db_slurm, vrp)) + db_table_remove_roa(params->db_table, vrp); return 0; } @@ -51,7 +62,7 @@ slurm_pfx_filters_apply(struct vrp const *vrp, void *arg) static int slurm_pfx_assertions_add(struct slurm_prefix *prefix, void *arg) { - struct db_table *table = arg; + struct slurm_parser_params *params = arg; struct ipv4_prefix prefix4; struct ipv6_prefix prefix6; struct vrp vrp; @@ -63,33 +74,33 @@ slurm_pfx_assertions_add(struct slurm_prefix *prefix, void *arg) if (vrp.addr_fam == AF_INET) { prefix4.addr = vrp.prefix.v4; prefix4.len = vrp.prefix_length; - return rtrhandler_handle_roa_v4(table, vrp.asn, &prefix4, - vrp.max_prefix_length); + return rtrhandler_handle_roa_v4(params->db_table, vrp.asn, + &prefix4, vrp.max_prefix_length); } if (vrp.addr_fam == AF_INET6) { prefix6.addr = vrp.prefix.v6; prefix6.len = vrp.prefix_length; - return rtrhandler_handle_roa_v6(table, vrp.asn, &prefix6, - vrp.max_prefix_length); + return rtrhandler_handle_roa_v6(params->db_table, vrp.asn, + &prefix6, vrp.max_prefix_length); } pr_crit("Unknown addr family type: %u", vrp.addr_fam); } static int -slurm_pfx_assertions_apply(struct db_table *base) +slurm_pfx_assertions_apply(struct slurm_parser_params *params) { - return slurm_db_foreach_assertion_prefix(slurm_pfx_assertions_add, - base); + return db_slurm_foreach_assertion_prefix(params->db_slurm, + slurm_pfx_assertions_add, params); } static int slurm_bgpsec_filters_apply(struct router_key const *key, void *arg) { - struct db_table *table = arg; + struct slurm_parser_params *params = arg; - if (slurm_db_bgpsec_is_filtered(key)) - db_table_remove_router_key(table, key); + if (db_slurm_bgpsec_is_filtered(params->db_slurm, key)) + db_table_remove_router_key(params->db_table, key); return 0; } @@ -97,69 +108,112 @@ slurm_bgpsec_filters_apply(struct router_key const *key, void *arg) static int slurm_bgpsec_assertions_add(struct slurm_bgpsec *bgpsec, void *arg) { - struct db_table *table = arg; + struct slurm_parser_params *params = arg; - return rtrhandler_handle_router_key(table, bgpsec->ski, + return rtrhandler_handle_router_key(params->db_table, bgpsec->ski, bgpsec->asn, bgpsec->router_public_key); } static int -slurm_bgpsec_assertions_apply(struct db_table *base) +slurm_bgpsec_assertions_apply(struct slurm_parser_params *params) { - return slurm_db_foreach_assertion_bgpsec(slurm_bgpsec_assertions_add, - base); + return db_slurm_foreach_assertion_bgpsec(params->db_slurm, + slurm_bgpsec_assertions_add, params); +} + +static int +slurm_create_parser_params(struct slurm_parser_params **result) +{ + struct slurm_parser_params *params; + + params = malloc(sizeof(struct slurm_parser_params)); + if (params == NULL) + return pr_enomem(); + + params->db_table = NULL; + params->db_slurm = NULL; + params->cur_ctx = 0; + + *result = params; + return 0; } -/* - * Load the SLURM file/dir and try to apply it on @base. - * - * On any error the SLURM won't be applied to @base. - */ int -slurm_apply(struct db_table **base) +slurm_apply(struct db_table **base, struct db_slurm **last_slurm) { - struct db_table *new_base; - bool loaded; + struct slurm_parser_params *params = NULL; int error; - loaded = false; - error = slurm_load(&loaded); + if (config_get_slurm() == NULL) + return 0; + + error = slurm_create_parser_params(¶ms); if (error) - goto cleanup; + return error; + + error = slurm_load(params); + switch(error) { + case 0: + /* Use as last valid slurm */ + if (*last_slurm != NULL) + db_slurm_destroy(*last_slurm); + *last_slurm = params->db_slurm; + db_slurm_update_time(*last_slurm); + break; + case -EEXIST: + /* Syntax error, use last valid slurm, log as info */ + if (*last_slurm != NULL) { + pr_warn("A previous valid version of the SLURM exists and will be applied"); + params->db_slurm = *last_slurm; + db_slurm_log(params->db_slurm); + } + break; + default: + /* Some other error, discard SLURM */ + if (*last_slurm != NULL) { + pr_info("Discarding previous valid SLURM"); + db_slurm_destroy(*last_slurm); + } + goto success; + } - if (!loaded) - return 0; + /* If there's no SLURM, stop */ + if (params->db_slurm == NULL) + goto success; /* Deep copy of the base so that updates can be reverted */ - error = db_table_clone(&new_base, *base); + error = db_table_clone(¶ms->db_table, *base); if (error) - goto cleanup; + goto release_slurm; - error = db_table_foreach_roa(new_base, slurm_pfx_filters_apply, - new_base); + error = db_table_foreach_roa(params->db_table, slurm_pfx_filters_apply, + params); if (error) - goto release_new; + goto release_table; - error = db_table_foreach_router_key(new_base, - slurm_bgpsec_filters_apply, new_base); + error = db_table_foreach_router_key(params->db_table, + slurm_bgpsec_filters_apply, params); if (error) - goto release_new; + goto release_table; - error = slurm_pfx_assertions_apply(new_base); - if (error) { - goto release_new; - } + error = slurm_pfx_assertions_apply(params); + if (error) + goto release_table; - error = slurm_bgpsec_assertions_apply(new_base); - if (!error) { - db_table_destroy(*base); - *base = new_base; - goto cleanup; + error = slurm_bgpsec_assertions_apply(params); + if (error) { + goto release_table; } -release_new: - db_table_destroy(new_base); -cleanup: - slurm_cleanup(); + db_table_destroy(*base); + *base = params->db_table; +success: + free(params); + return 0; +release_table: + db_table_destroy(params->db_table); +release_slurm: + db_slurm_destroy(params->db_slurm); + free(params); return error; } diff --git a/src/slurm/slurm_loader.h b/src/slurm/slurm_loader.h index c9801cc0..cf4845a3 100644 --- a/src/slurm/slurm_loader.h +++ b/src/slurm/slurm_loader.h @@ -2,7 +2,21 @@ #define SRC_SLURM_SLURM_LOADER_H_ #include "rtr/db/db_table.h" +#include "slurm/db_slurm.h" -int slurm_apply(struct db_table **); +/* + * Load the SLURM file/dir and try to apply it on @db_table, point to the SLURM + * applied at @db_slurm. + * + * Return error only when there's a major issue on the process (no memory, + * SLURM loaded but something happened applying it). + * + * Return 0 when there's no problem applying the SLURM: + * - There's no SLURM configured + * - The SLURM was successfully applied + * - The @last_slurm was applied due to a syntax problem with a newer SLURM + * - SLURM configured but couldn't be read (file doesn't exists, no permission) + */ +int slurm_apply(struct db_table **, struct db_slurm **); #endif /* SRC_SLURM_SLURM_LOADER_H_ */ diff --git a/src/slurm/slurm_parser.c b/src/slurm/slurm_parser.c index edba6d1a..2ef266c5 100644 --- a/src/slurm/slurm_parser.c +++ b/src/slurm/slurm_parser.c @@ -14,7 +14,6 @@ #include "address.h" #include "json_parser.h" #include "object/router_key.h" -#include "slurm/slurm_db.h" /* JSON members */ #define SLURM_VERSION "slurmVersion" @@ -38,29 +37,48 @@ return pr_err("SLURM member '%s' is required", name); /* Context value, local to avoid forwarding the parameter */ -int cur_ctx; +struct db_slurm *db; +unsigned int cur_ctx; -static int handle_json(json_t *, int *); +static int handle_json(json_t *, struct db_slurm *, unsigned int *); +/* + * Try to parse the SLURM file(s), any syntax error will return an EEXIST error + */ int slurm_parse(char const *location, void *arg) { + struct slurm_parser_params *params; json_t *json_root; json_error_t json_error; + unsigned int ctx; int error; + params = arg; + json_root = json_load_file(location, JSON_REJECT_DUPLICATES, &json_error); if (json_root == NULL) { pr_err("SLURM JSON error on line %d, column %d: %s", json_error.line, json_error.column, json_error.text); + /* File was read, but has a content error */ + if (json_error.position > 0) + goto syntax_err; return -ENOENT; } - error = handle_json(json_root, arg); - + ctx = params->cur_ctx; + error = handle_json(json_root, params->db_slurm, &ctx); json_decref(json_root); - return error; + if (error) + goto syntax_err; + + params->cur_ctx = ctx; + return 0; + +syntax_err: + /* File exists, but has a syntax/content error */ + return -EEXIST; } static int @@ -89,9 +107,9 @@ set_asn(json_t *object, bool is_assertion, uint32_t *result, uint8_t *flag, return 0; } +/* There's no need to store the comment */ static int -set_comment(json_t *object, char **comment, uint8_t *flag, - size_t *members_loaded) +set_comment(json_t *object, uint8_t *flag, size_t *members_loaded) { char const *tmp; int error; @@ -102,7 +120,6 @@ set_comment(json_t *object, char **comment, uint8_t *flag, else if (error) return error; - *comment = strdup(tmp); *flag = *flag | SLURM_COM_FLAG_COMMENT; (*members_loaded)++; @@ -314,7 +331,8 @@ set_router_pub_key(json_t *object, bool is_assertion, /* Required by assertions */ if (error && is_assertion) { if (error == -ENOENT) - return pr_err("SLURM assertion %s is required", ROUTER_PUBLIC_KEY); + return pr_err("SLURM assertion %s is required", + ROUTER_PUBLIC_KEY); return error; } @@ -356,7 +374,6 @@ init_slurm_prefix(struct slurm_prefix *slurm_prefix) slurm_prefix->vrp.prefix_length = 0; slurm_prefix->vrp.max_prefix_length = 0; slurm_prefix->vrp.addr_fam = 0; - slurm_prefix->comment = NULL; } static int @@ -387,37 +404,27 @@ load_single_prefix(json_t *object, bool is_assertion) if (error) return error; - error = set_comment(object, &result.comment, &result.data_flag, - &member_count); + error = set_comment(object, &result.data_flag, &member_count); if (error) return error; /* A single comment isn't valid */ - if (result.data_flag == SLURM_COM_FLAG_COMMENT) { - pr_err("Single comments aren't valid"); - error = -EINVAL; - goto release_comment; - } + if (result.data_flag == SLURM_COM_FLAG_COMMENT) + return pr_err("Single comments aren't valid"); /* A filter must have ASN and/or prefix */ if (!is_assertion) { if ((result.data_flag & - (SLURM_COM_FLAG_ASN | SLURM_PFX_FLAG_PREFIX)) == 0) { - pr_err("Prefix filter must have an asn and/or prefix"); - error = -EINVAL; - goto release_comment; - } + (SLURM_COM_FLAG_ASN | SLURM_PFX_FLAG_PREFIX)) == 0) + return pr_err("Prefix filter must have an asn and/or prefix"); /* Validate expected members */ - if (!json_valid_members_count(object, member_count)) { - pr_err("Prefix filter has unknown members (see RFC 8416 section 3.3.1)"); - error = -EINVAL; - goto release_comment; - } + if (!json_valid_members_count(object, member_count)) + return pr_err("Prefix filter has unknown members (see RFC 8416 section 3.3.1)"); - error = slurm_db_add_prefix_filter(&result, cur_ctx); + error = db_slurm_add_prefix_filter(db, &result, cur_ctx); if (error) - goto release_comment; + return error; return 0; } @@ -427,30 +434,19 @@ load_single_prefix(json_t *object, bool is_assertion) * set_asn and set_prefix */ - if ((result.data_flag & SLURM_PFX_FLAG_MAX_LENGTH) > 0) - if (result.vrp.prefix_length > result.vrp.max_prefix_length) { - pr_err( - "Prefix length is greater than max prefix length"); - error = -EINVAL; - goto release_comment; - } + if ((result.data_flag & SLURM_PFX_FLAG_MAX_LENGTH) > 0 && + result.vrp.prefix_length > result.vrp.max_prefix_length) + return pr_err("Prefix length is greater than max prefix length"); /* Validate expected members */ - if (!json_valid_members_count(object, member_count)) { - pr_err("Prefix assertion has unknown members (see RFC 8416 section 3.4.1)"); - error = -EINVAL; - goto release_comment; - } + if (!json_valid_members_count(object, member_count)) + return pr_err("Prefix assertion has unknown members (see RFC 8416 section 3.4.1)"); - error = slurm_db_add_prefix_assertion(&result, cur_ctx); + error = db_slurm_add_prefix_assertion(db, &result, cur_ctx); if (error) - goto release_comment; + return error; return 0; - -release_comment: - free(result.comment); - return error; } static int @@ -491,7 +487,6 @@ init_slurm_bgpsec(struct slurm_bgpsec *slurm_bgpsec) slurm_bgpsec->asn = 0; slurm_bgpsec->ski = NULL; slurm_bgpsec->router_public_key = NULL; - slurm_bgpsec->comment = NULL; } static int @@ -521,8 +516,7 @@ load_single_bgpsec(json_t *object, bool is_assertion) if (error) goto release_ski; - error = set_comment(object, &result.comment, &result.data_flag, - &member_count); + error = set_comment(object, &result.data_flag, &member_count); if (error) goto release_router_key; @@ -530,7 +524,7 @@ load_single_bgpsec(json_t *object, bool is_assertion) if (result.data_flag == SLURM_COM_FLAG_COMMENT) { pr_err("Single comments aren't valid"); error = -EINVAL; - goto release_comment; + goto release_router_key; } /* A filter must have ASN and/or SKI */ @@ -539,19 +533,19 @@ load_single_bgpsec(json_t *object, bool is_assertion) (SLURM_COM_FLAG_ASN | SLURM_BGPS_FLAG_SKI)) == 0) { pr_err("BGPsec filter must have an asn and/or SKI"); error = -EINVAL; - goto release_comment; + goto release_router_key; } /* Validate expected members */ if (!json_valid_members_count(object, member_count)) { pr_err("BGPsec filter has unknown members (see RFC 8416 section 3.3.2)"); error = -EINVAL; - goto release_comment; + goto release_router_key; } - error = slurm_db_add_bgpsec_filter(&result, cur_ctx); + error = db_slurm_add_bgpsec_filter(db, &result, cur_ctx); if (error) - goto release_comment; + goto release_router_key; return 0; } @@ -560,17 +554,15 @@ load_single_bgpsec(json_t *object, bool is_assertion) if (!json_valid_members_count(object, member_count)) { pr_err("BGPsec assertion has unknown members (see RFC 8416 section 3.4.2)"); error = -EINVAL; - goto release_comment; + goto release_router_key; } - error = slurm_db_add_bgpsec_assertion(&result, cur_ctx); + error = db_slurm_add_bgpsec_assertion(db, &result, cur_ctx); if (error) - goto release_comment; + goto release_router_key; return 0; -release_comment: - free(result.comment); release_router_key: free(result.router_public_key); release_ski: @@ -697,7 +689,7 @@ load_assertions(json_t *root) } static int -handle_json(json_t *root, int *ctx) +handle_json(json_t *root, struct db_slurm *db_slurm, unsigned int *ctx) { size_t expected_members; int error; @@ -706,6 +698,7 @@ handle_json(json_t *root, int *ctx) return pr_err("The root of the SLURM is not a JSON object."); cur_ctx = *ctx; + db = db_slurm; error = load_version(root); if (error) diff --git a/src/slurm/slurm_parser.h b/src/slurm/slurm_parser.h index 3e5338a8..a5fec8be 100644 --- a/src/slurm/slurm_parser.h +++ b/src/slurm/slurm_parser.h @@ -1,34 +1,15 @@ #ifndef SRC_SLURM_SLURM_PARSER_H_ #define SRC_SLURM_SLURM_PARSER_H_ -#include "rtr/db/vrp.h" +#include "rtr/db/db_table.h" +#include "slurm/db_slurm.h" -/* Flags to get data from structs */ -#define SLURM_COM_FLAG_NONE 0x00 -#define SLURM_COM_FLAG_ASN 0x01 -#define SLURM_COM_FLAG_COMMENT 0x02 - -#define SLURM_PFX_FLAG_PREFIX 0x04 -#define SLURM_PFX_FLAG_MAX_LENGTH 0x08 - -#define SLURM_BGPS_FLAG_SKI 0x04 -#define SLURM_BGPS_FLAG_ROUTER_KEY 0x08 - -struct slurm_prefix { - uint8_t data_flag; - struct vrp vrp; - char *comment; +struct slurm_parser_params { + struct db_table *db_table; + struct db_slurm *db_slurm; + unsigned int cur_ctx; /* Context (file number) */ }; -struct slurm_bgpsec { - uint8_t data_flag; - uint32_t asn; - unsigned char *ski; - unsigned char *router_public_key; - char *comment; -}; - - int slurm_parse(char const *, void *); diff --git a/test/rtr/db/vrps_test.c b/test/rtr/db/vrps_test.c index d6b0c669..962cc447 100644 --- a/test/rtr/db/vrps_test.c +++ b/test/rtr/db/vrps_test.c @@ -15,7 +15,7 @@ #include "rtr/db/db_table.c" #include "rtr/db/rtr_db_impersonator.c" #include "rtr/db/vrps.c" -#include "slurm/slurm_db.c" +#include "slurm/db_slurm.c" #include "slurm/slurm_loader.c" #include "slurm/slurm_parser.c" diff --git a/test/rtr/pdu_handler_test.c b/test/rtr/pdu_handler_test.c index ab338e70..efe1e358 100644 --- a/test/rtr/pdu_handler_test.c +++ b/test/rtr/pdu_handler_test.c @@ -22,7 +22,7 @@ #include "rtr/db/db_table.c" #include "rtr/db/rtr_db_impersonator.c" #include "rtr/db/vrps.c" -#include "slurm/slurm_db.c" +#include "slurm/db_slurm.c" #include "slurm/slurm_loader.c" #include "slurm/slurm_parser.c"