error = delta_add_vrp(delta, vrp);
if (error) {
- vrp_destroy(&vrp);
+ vrp_destroy(vrp);
goto error;
}
#include <err.h>
#include <errno.h>
+#include <stddef.h>
#include "pdu.h"
#include "pdu_sender.h"
+#include "vrps.h"
static int warn_unexpected_pdu(char *);
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
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;
}
#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.
*/
}
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;
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;
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;
}
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();
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);
}
#include <sys/types.h>
+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_ */
#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)
err(error, "Deltas DB couldn't be allocated");
return error;
}
+ current_serial = 0;
return 0;
}
if (result == NULL)
goto fail1;
- if (delta_init(result) != 0)
+ if (vrps_init(&result->vrps) != 0)
goto fail2;
return result;
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);
}
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;
}
#include <netinet/ip.h>
+#define NO_DATA_AVAILABLE -2
+#define DIFF_UNDETERMINED -1
+#define NO_DIFF 0
+#define DIFF_AVAILABLE 1
+
struct vrp {
u_int32_t asn;
union {
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);