From: pcarana Date: Tue, 26 Feb 2019 22:45:18 +0000 (-0600) Subject: Implement serial logic, prepare to calculate diff between serials X-Git-Tag: v0.0.2~52^2~55 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=31256228f819fabb10e2b02162a1316cd0ab6329;p=thirdparty%2FFORT-validator.git Implement serial logic, prepare to calculate diff between serials --- diff --git a/src/csv.c b/src/csv.c index 77faa3b1..b9275f88 100644 --- a/src/csv.c +++ b/src/csv.c @@ -165,7 +165,7 @@ add_vrp(char *line, struct delta *delta) error = delta_add_vrp(delta, vrp); if (error) { - vrp_destroy(&vrp); + vrp_destroy(vrp); goto error; } diff --git a/src/rtr/pdu_handler.c b/src/rtr/pdu_handler.c index 7eaba7d3..f34aa29b 100644 --- a/src/rtr/pdu_handler.c +++ b/src/rtr/pdu_handler.c @@ -2,9 +2,11 @@ #include #include +#include #include "pdu.h" #include "pdu_sender.h" +#include "vrps.h" static int warn_unexpected_pdu(char *); @@ -22,54 +24,93 @@ handle_serial_notify_pdu(int fd, void *pdu) return warn_unexpected_pdu("Serial Notify"); } +static int +send_commmon_exchange(struct sender_common *common) +{ + int error; + + // Send Cache response PDU + error = send_cache_response_pdu(common); + if (error) + return error; + + // Send Payload PDUs + error = send_payload_pdus(common); + if (error) + return error; + + // Send End of data PDU + return send_end_of_data_pdu(common); +} + int handle_serial_query_pdu(int fd, void *pdu) { - struct reset_query_pdu *received = pdu; - u_int8_t version; - - version = received->header.protocol_version; - /* - * TODO The Serial should be read to get updates, so - * more work needs to be done here. - */ - return send_cache_reset_pdu(fd, version); + struct serial_query_pdu *received = pdu; + struct sender_common common; + int error, updates; + u_int32_t current_serial; + + current_serial = last_serial_number(); + /* TODO Handle sessions and its ID */ + init_sender_common(&common, fd, received->header.protocol_version, + &received->header.session_id, &received->serial_number, + ¤t_serial); + + updates = deltas_db_status(common.start_serial); + switch (updates) { + /* TODO Implement error */ +// case NO_DATA_AVAILABLE: + /* https://tools.ietf.org/html/rfc8210#section-8.4 */ +// return send_error_pdu; + case DIFF_UNDETERMINED: + /* https://tools.ietf.org/html/rfc8210#section-8.3 */ + return send_cache_reset_pdu(&common); + case DIFF_AVAILABLE: + /* https://tools.ietf.org/html/rfc8210#section-8.2 */ + return send_commmon_exchange(&common); + case NO_DIFF: + /* Typical exchange with no Payloads */ + error = send_cache_response_pdu(&common); + if (error) + return error; + return send_end_of_data_pdu(&common); + default: + error = -EINVAL; + err(error, "Reached 'unreachable' code"); + return error; + } } int handle_reset_query_pdu(int fd, void *pdu) { struct reset_query_pdu *received = pdu; + struct sender_common common; + u_int32_t current_serial; u_int16_t session_id; - u_int8_t version; - int error; - - /* - * FIXME Complete behaviour: - * - Do I have data? - * + NO: Send error - * https://tools.ietf.org/html/rfc8210#section-8.4 - * + YES: Send data (cache response -> payloads -> end of data) - * https://tools.ietf.org/html/rfc8210#section-8.1 - */ + int error, updates; - /* FIXME Handle sessions and its ID */ + current_serial = last_serial_number(); + /* TODO Handle sessions and its ID */ session_id = 1; - version = received->header.protocol_version; - - // Send Cache response PDU - error = send_cache_response_pdu(fd, version, session_id); - if (error) + init_sender_common(&common, fd, received->header.protocol_version, + &session_id, NULL, ¤t_serial); + + updates = deltas_db_status(common.start_serial); + switch (updates) { + /* TODO Implement error */ +// case NO_DATA_AVAILABLE: + /* https://tools.ietf.org/html/rfc8210#section-8.4 */ +// return send_error_pdu; + case DIFF_AVAILABLE: + /* https://tools.ietf.org/html/rfc8210#section-8.1 */ + return send_commmon_exchange(&common); + default: + error = -EINVAL; + err(error, "Reached 'unreachable' code"); return error; - - // Send Payload PDUs - // TODO ..and handle Serial Number - error = send_payload_pdus(fd, version, 1); - if (error) - return error; - - // Send End of data PDU - return send_end_of_data_pdu(fd, version, session_id); + } } int @@ -105,6 +146,12 @@ handle_cache_reset_pdu(int fd, void *pdu) int handle_error_report_pdu(int fd, void *pdu) { - /* TODO */ - return -EUNIMPLEMENTED; + struct error_report_pdu *received = pdu; + struct sender_common common; + + init_sender_common(&common, fd, received->header.protocol_version, + NULL, NULL, NULL); + + /* TODO complete handler */ + return 0; } diff --git a/src/rtr/pdu_sender.c b/src/rtr/pdu_sender.c index e302f64a..b6e385af 100644 --- a/src/rtr/pdu_sender.c +++ b/src/rtr/pdu_sender.c @@ -17,6 +17,16 @@ #define IPV4_PREFIX_LENGTH 12 #define IPV6_PREFIX_LENGTH 24 +void +init_sender_common(struct sender_common *common, int fd, u_int8_t version, + u_int16_t *session_id, u_int32_t *start_serial, u_int32_t *end_serial) +{ + common->fd = fd; + common->version = version; + common->session_id = session_id == NULL ? 0 : session_id; + common->start_serial = start_serial; + common->end_serial = end_serial; +} /* * Set all the header values, EXCEPT length field. */ @@ -79,46 +89,45 @@ send_response(int fd, char *data, size_t data_len) } int -send_cache_reset_pdu(int fd, u_int8_t version) +send_cache_reset_pdu(struct sender_common *common) { struct cache_reset_pdu pdu; char data[BUFFER_SIZE]; size_t len; /* This PDU has only the header */ - set_header_values(&pdu.header, version, CACHE_RESET_PDU_TYPE, 0); + set_header_values(&pdu.header, common->version, CACHE_RESET_PDU_TYPE, 0); pdu.header.length = HEADER_LENGTH; len = serialize_cache_reset_pdu(&pdu, data); - return send_response(fd, data, len); + return send_response(common->fd, data, len); } int -send_cache_response_pdu(int fd, u_int8_t version, u_int16_t session_id) +send_cache_response_pdu(struct sender_common *common) { struct cache_response_pdu pdu; char data[BUFFER_SIZE]; size_t len; /* This PDU has only the header */ - set_header_values(&pdu.header, version, - CACHE_RESPONSE_PDU_TYPE, session_id); + set_header_values(&pdu.header, common->version, + CACHE_RESPONSE_PDU_TYPE, *common->session_id); pdu.header.length = HEADER_LENGTH; len = serialize_cache_response_pdu(&pdu, data); - /* TODO wait for the ACK? */ - return send_response(fd, data, len); + + return send_response(common->fd, data, len); } static int -send_ipv4_prefix_pdu(int fd, u_int8_t version, u_int32_t serial, - struct vrp *vrp) +send_ipv4_prefix_pdu(struct sender_common *common, struct vrp *vrp) { struct ipv4_prefix_pdu pdu; char data[BUFFER_SIZE]; size_t len; - set_header_values(&pdu.header, version, IPV4_PREFIX_PDU_TYPE, 0); + set_header_values(&pdu.header, common->version, IPV4_PREFIX_PDU_TYPE, 0); pdu.flags = vrp->flags; pdu.prefix_length = vrp->prefix_length; @@ -129,19 +138,18 @@ send_ipv4_prefix_pdu(int fd, u_int8_t version, u_int32_t serial, pdu.header.length = length_ipvx_prefix_pdu(true); len = serialize_ipv4_prefix_pdu(&pdu, data); - /* TODO wait for the ACK? */ - return send_response(fd, data, len); + + return send_response(common->fd, data, len); } static int -send_ipv6_prefix_pdu(int fd, u_int8_t version, u_int32_t serial, - struct vrp *vrp) +send_ipv6_prefix_pdu(struct sender_common *common, struct vrp *vrp) { struct ipv6_prefix_pdu pdu; char data[BUFFER_SIZE]; size_t len; - set_header_values(&pdu.header, version, IPV6_PREFIX_PDU_TYPE, 0); + set_header_values(&pdu.header, common->version, IPV6_PREFIX_PDU_TYPE, 0); pdu.flags = vrp->flags; pdu.prefix_length = vrp->prefix_length; @@ -152,24 +160,27 @@ send_ipv6_prefix_pdu(int fd, u_int8_t version, u_int32_t serial, pdu.header.length = length_ipvx_prefix_pdu(false); len = serialize_ipv6_prefix_pdu(&pdu, data); - /* TODO wait for the ACK? */ - return send_response(fd, data, len); + + return send_response(common->fd, data, len); } int -send_payload_pdus(int fd, u_int8_t version, u_int32_t serial) +send_payload_pdus(struct sender_common *common) { - struct vrp **vrps, **ptr; + struct vrp *vrps, *ptr; unsigned int len, i; int error; - vrps = get_vrps_delta(serial, &len); + len = get_vrps_delta(common->start_serial, common->end_serial, &vrps); + if (len == 0) + return 0; + ptr = vrps; for (i = 0; i < len; i++) { - if ((*ptr)->in_addr_len == INET_ADDRSTRLEN) - error = send_ipv4_prefix_pdu(fd, version, serial, *ptr); + if (ptr->in_addr_len == INET_ADDRSTRLEN) + error = send_ipv4_prefix_pdu(common, ptr); else - error = send_ipv6_prefix_pdu(fd, version, serial, *ptr); + error = send_ipv6_prefix_pdu(common, ptr); if (error) return error; @@ -180,15 +191,15 @@ send_payload_pdus(int fd, u_int8_t version, u_int32_t serial) } int -send_end_of_data_pdu(int fd, u_int8_t version, u_int16_t session_id) +send_end_of_data_pdu(struct sender_common *common) { struct end_of_data_pdu pdu; char data[BUFFER_SIZE]; size_t len; - set_header_values(&pdu.header, version, END_OF_DATA_PDU_TYPE, session_id); - pdu.serial_number = last_serial_number(); - if (version == RTR_V1) { + set_header_values(&pdu.header, common->version, END_OF_DATA_PDU_TYPE, *common->session_id); + pdu.serial_number = *common->end_serial; + if (common->version == RTR_V1) { pdu.refresh_interval = config_get_refresh_interval(); pdu.retry_interval = config_get_retry_interval(); pdu.expire_interval = config_get_expire_interval(); @@ -196,6 +207,6 @@ send_end_of_data_pdu(int fd, u_int8_t version, u_int16_t session_id) pdu.header.length = length_end_of_data_pdu(&pdu); len = serialize_end_of_data_pdu(&pdu, data); - /* TODO wait for the ACK? */ - return send_response(fd, data, len); + + return send_response(common->fd, data, len); } diff --git a/src/rtr/pdu_sender.h b/src/rtr/pdu_sender.h index c49bcea9..d91bf40d 100644 --- a/src/rtr/pdu_sender.h +++ b/src/rtr/pdu_sender.h @@ -3,11 +3,21 @@ #include +struct sender_common { + int fd; + u_int8_t version; + u_int16_t *session_id; + u_int32_t *start_serial; + u_int32_t *end_serial; +}; -int send_cache_reset_pdu(int, u_int8_t); -int send_cache_response_pdu(int, u_int8_t, u_int16_t); -int send_payload_pdus(int, u_int8_t, u_int32_t); -int send_end_of_data_pdu(int, u_int8_t, u_int16_t); +void init_sender_common(struct sender_common *, int, u_int8_t, u_int16_t *, + u_int32_t *, u_int32_t *); + +int send_cache_reset_pdu(struct sender_common *); +int send_cache_response_pdu(struct sender_common *); +int send_payload_pdus(struct sender_common *); +int send_end_of_data_pdu(struct sender_common *); #endif /* SRC_RTR_PDU_SENDER_H_ */ diff --git a/src/vrps.c b/src/vrps.c index 343e5e2a..5ab42de2 100644 --- a/src/vrps.c +++ b/src/vrps.c @@ -5,10 +5,17 @@ #define FLAG_WITHDRAWAL 0 #define FLAG_ANNOUNCEMENT 1 -ARRAY_LIST(delta, struct vrp *) +ARRAY_LIST(vrps, struct vrp) + +struct delta { + u_int32_t serial; + struct vrps vrps; +}; + ARRAY_LIST(deltasdb, struct delta) struct deltasdb db; +u_int32_t current_serial; int deltas_db_init(void) @@ -20,6 +27,7 @@ deltas_db_init(void) err(error, "Deltas DB couldn't be allocated"); return error; } + current_serial = 0; return 0; } @@ -33,7 +41,7 @@ create_delta(void) if (result == NULL) goto fail1; - if (delta_init(result) != 0) + if (vrps_init(&result->vrps) != 0) goto fail2; return result; @@ -94,25 +102,26 @@ create_vrp6(u_int32_t asn, struct in6_addr ipv6_prefix, u_int8_t prefix_length, int deltas_db_add_delta(struct delta *delta) { + delta->serial = current_serial++; return deltasdb_add(&db, delta); } int delta_add_vrp(struct delta *delta, struct vrp *vrp) { - return delta_add(delta, &vrp); + return vrps_add(&delta->vrps, vrp); } void -vrp_destroy(struct vrp **vrp) +vrp_destroy(struct vrp *vrp) { - free(*vrp); + free(vrp); } void delta_destroy(struct delta *delta) { - delta_cleanup(delta, vrp_destroy); + vrps_cleanup(&delta->vrps, vrp_destroy); free(delta); } @@ -122,21 +131,102 @@ deltas_db_destroy(void) deltasdb_cleanup(&db, delta_destroy); } -struct vrp ** -get_vrps_delta(u_int32_t serial, unsigned int *len) +static unsigned int +get_delta_diff(struct delta *start_delta, struct delta *end_delta, + struct vrp **result) +{ + /* TODO Do some magic to get the diff */ + *result = db.array[db.len - 1].vrps.array; + return db.array[db.len - 1].vrps.len; +} + +/* + * Get a status to know the difference between the delta with serial SERIAL and + * the last delta at DB. + * + * If SERIAL is received as NULL, and there's data at DB then the status will + * be DIFF_AVAILABLE. + * + * The possible return values are: + * NO_DATA_AVAILABLE -> There's no data at the DB + * DIFF_UNDETERMINED -> The diff can't be determined + * NO_DIFF -> There's no difference + * DIFF_AVAILABLE -> There are differences between SERIAL and the last DB serial + */ +int +deltas_db_status(u_int32_t *serial) +{ + struct delta *delta; + + if (db.len == 0) + return NO_DATA_AVAILABLE; + + // No serial to match, and there's data at DB + if (serial == NULL) + return DIFF_AVAILABLE; + + /* Get the delta corresponding to the serial */ + ARRAYLIST_FOREACH(&db, delta) { + if (delta->serial == *serial) + break; + } + + /* Reached end, diff can't be determined */ + if (delta == NULL) + return DIFF_UNDETERMINED; + + /* Is the last version? */ + if (delta->serial == db.array[db.len-1].serial) + return NO_DIFF; + + return DIFF_AVAILABLE; +} + +/* + * Get the number of updates from serial START_SERIAL to END_SERIAL, set them + * at RESULT. + * + * Return 0 if no updates are available or couldn't be calculated with the + * received values. + */ +unsigned int +get_vrps_delta(u_int32_t *start_serial, u_int32_t *end_serial, + struct vrp **result) { - /* - * TODO Return the VRPs according to delta - */ - *len = db.array[0].len; - return db.array[0].array; + struct delta *delta0, *delta1; + + /* No data */ + if (db.len == 0) + return 0; + + /* NULL start? Send the last version, there's no need to iterate DB */ + if (start_serial == NULL) { + *result = db.array[db.len - 1].vrps.array; + return db.array[db.len - 1].vrps.len; + /* TODO Send all data as ANNOUNCEMENTS */ + } + + /* Apparently nothing to return */ + if (*start_serial > *end_serial) + return 0; + + /* Get the delta corresponding to the serials */ + ARRAYLIST_FOREACH(&db, delta1) { + if (delta1->serial == *start_serial) + delta0 = delta1; + if (delta1->serial == *end_serial) + break; + } + + /* Reached end or no delta0 found, diff can't be determined, send error */ + if (delta1 == NULL || delta0 == NULL) + return 0; + + return get_delta_diff(delta0, delta1, result); } u_int32_t last_serial_number(void) { - /* - * TODO Return the last serial number of the DB - */ - return 1; + return current_serial; } diff --git a/src/vrps.h b/src/vrps.h index a5a6e5f4..445ba628 100644 --- a/src/vrps.h +++ b/src/vrps.h @@ -3,6 +3,11 @@ #include +#define NO_DATA_AVAILABLE -2 +#define DIFF_UNDETERMINED -1 +#define NO_DIFF 0 +#define DIFF_AVAILABLE 1 + struct vrp { u_int32_t asn; union { @@ -22,12 +27,14 @@ int deltas_db_init(void); struct delta *create_delta(void); struct vrp *create_vrp4(u_int32_t, struct in_addr, u_int8_t, u_int8_t); struct vrp *create_vrp6(u_int32_t, struct in6_addr, u_int8_t, u_int8_t); -struct vrp **get_vrps_delta(u_int32_t, unsigned int *); int delta_add_vrp(struct delta *, struct vrp *); int deltas_db_add_delta(struct delta *); +int deltas_db_status(u_int32_t *); + +unsigned int get_vrps_delta(u_int32_t *, u_int32_t *, struct vrp **); -void vrp_destroy(struct vrp **); +void vrp_destroy(struct vrp *); void delta_destroy(struct delta *); void deltas_db_destroy(void);