Caps the maximum RTR version the server will be willing to serve.
Defaults to 0 because the RTRv1 implementation still doesn't do
anything useful, and the RTRv2 implementation is transient.
(They're still tweaking the RTRv2 spec.)
Also fixes a couple of bugs:
- Was not closing the connection when returning internal error PDUs
- Some error reports before the version validation would erroneously
indicate v255 in the header
} interval;
/** Number of iterations the deltas will be stored. */
unsigned int deltas_lifetime;
+
+ unsigned int max_rtr_version;
} server;
struct {
* Must not overflow when multiplied by interval.validation.
*/
.max = 1000,
+ }, {
+ .id = 5008,
+ .name = "server.max-rtr-version",
+ .type = >_uint,
+ .offset = offsetof(struct rpki_config, server.max_rtr_version),
+ .doc = "Maximum RTR version to serve (0-2).",
+ .min = 0,
+ .max = 2,
},
/* Prometheus fields */
rpki_config.server.interval.retry = 600;
rpki_config.server.interval.expire = 7200;
rpki_config.server.deltas_lifetime = 6;
+ rpki_config.server.max_rtr_version = 0;
rpki_config.prometheus.port = 0;
return rpki_config.server.deltas_lifetime;
}
+unsigned int
+max_rtr_version(void)
+{
+ return rpki_config.server.max_rtr_version;
+}
+
unsigned int
config_get_prometheus_port(void)
{
unsigned int config_get_interval_retry(void);
unsigned int config_get_interval_expire(void);
unsigned int config_get_deltas_lifetime(void);
+unsigned int max_rtr_version(void);
unsigned int config_get_prometheus_port(void);
char const *config_get_slurm(void);
#include <string.h>
#include "alloc.h"
+#include "config.h"
#include "log.h"
#include "data_structure/common.h"
#include "rtr/err_pdu.h"
int (*send)(struct rtr_stream *, unsigned char const *, int);
};
+static int
+validate_rtr_version(struct rtr_request *request)
+{
+ struct pdu_stream *stream = request->stream;
+ enum rtr_version reqver = request->pdu.rtr_version;
+
+ if (stream->rtr_version == -1) {
+ if (RTR_V0 <= reqver && reqver <= max_rtr_version()) {
+ pr_op_debug("Establishing RTR version: %d", reqver);
+ stream->rtr_version = reqver;
+ return 0;
+ }
+ pr_op_err("%s: Unsupported RTR version: %u",
+ stream->addr, reqver);
+ return -err_pdu_send_unsupported_proto_version(
+ stream->fd,
+ max_rtr_version(),
+ &request->pdu.raw,
+ "RTR version number is too high."
+ );
+ }
+
+ if (stream->rtr_version != reqver) {
+ pr_op_err("%s: Client changed RTR version: %u -> %u",
+ stream->addr, stream->rtr_version, reqver);
+ return err_pdu_send_unexpected_proto_version(
+ stream->fd,
+ stream->rtr_version,
+ &request->pdu.raw,
+ "The RTR version does not match the one we negotiated during the handshake."
+ );
+ }
+
+ return 0;
+}
+
static uint32_t
read_u32(unsigned char const *raw)
{
pr_op_debug("Reset Query. Request version: %u",
request->pdu.rtr_version);
+ error = validate_rtr_version(request);
+ if (error < 0)
+ return error;
+ if (error > 0)
+ return 0;
+
stream.fd = request->fd;
stream.ver = request->pdu.rtr_version;
request->pdu.obj.sq.session_id,
request->pdu.obj.sq.serial_number);
+ error = validate_rtr_version(request);
+ if (error < 0)
+ return error;
+ if (error > 0)
+ return 0;
+
stream.fd = request->fd;
stream.ver = request->pdu.rtr_version;
uint16_t session_id;
uint16_t reserved;
uint16_t error_code;
- } m; /* Note: "m" stands for "meh." I have no idea what to call this. */
+ } m; /* stands for "meh" */
uint32_t len;
};
free(stream);
}
+/* For when you need to send an error PDU before the version validation */
+static uint8_t
+guess_version(struct pdu_stream *stream, struct pdu_header *hdr)
+{
+ if (stream->rtr_version != -1)
+ return stream->rtr_version;
+
+ if (RTR_V0 <= hdr->version && hdr->version <= RTR_V2)
+ return hdr->version;
+
+ return RTR_V2;
+}
+
static size_t
get_length(struct pdu_stream *stream)
{
header->len = read_uint32(stream->start + 4);
}
-static int
-validate_rtr_version(struct pdu_stream *stream, struct pdu_header *hdr,
- struct rtr_buffer *request)
-{
- if (stream->rtr_version == -1) {
- if (RTR_V0 <= hdr->version && hdr->version <= RTR_V2) {
- stream->rtr_version = hdr->version;
- return 0;
- }
- return err_pdu_send_unsupported_proto_version(
- stream->fd, RTR_V2, request,
- "The maximum supported RTR version is 2."
- );
- }
-
- if (stream->rtr_version != hdr->version)
- return err_pdu_send_unexpected_proto_version(
- stream->fd, stream->rtr_version, request,
- "The RTR version does not match the one we negotiated during the handshake."
- );
-
- return 0;
-}
-
static int
load_serial_query(struct pdu_stream *stream, struct pdu_header *hdr,
struct rtr_request *result)
pr_op_err("%s: Header length is not %u: %u",
stream->addr, RTRPDU_SERIAL_QUERY_LEN, hdr->len);
return err_pdu_send_invalid_request(
- stream->fd, stream->rtr_version, &result->pdu.raw,
+ stream->fd,
+ guess_version(stream, hdr),
+ &result->pdu.raw,
"Expected length 12 for Serial Query PDUs."
);
}
pr_op_err("%s: Header length is not %u: %u",
stream->addr, RTRPDU_RESET_QUERY_LEN, hdr->len);
return err_pdu_send_invalid_request(
- stream->fd, stream->rtr_version, &result->pdu.raw,
+ stream->fd,
+ guess_version(stream, hdr),
+ &result->pdu.raw,
"Expected length 8 for Reset Query PDUs."
);
}
stream->addr, hdr.len, RTRPDU_MAX_LEN2);
err_pdu_send_invalid_request(
stream->fd,
- (stream->rtr_version != -1)
- ? stream->rtr_version
- : hdr.version,
+ guess_version(stream, &hdr),
&raw,
"PDU is too large."
);
goto fail;
}
- /* Validate version; Needs raw. */
- if (validate_rtr_version(stream, &hdr, &raw) != 0) {
- pr_op_err("%s: Bad RTR version: %u",
- stream->addr, hdr.version);
- goto fail;
- }
-
request = create_request(stream, &hdr, &raw);
raw.bytes = NULL; /* Ownership transferred */
break;
default:
pr_op_err("%s: Unknown PDU type: %u",
- stream->addr, hdr.version);
- err_pdu_send_unsupported_pdu_type(stream->fd,
- stream->rtr_version, &request->pdu.raw);
+ stream->addr, hdr.type);
+ err_pdu_send_unsupported_pdu_type(
+ stream->fd,
+ guess_version(stream, &hdr),
+ &request->pdu.raw
+ );
goto fail;
}
return NULL;
}
-static void
+static int
handle_request(struct rtr_request *req)
{
+ int error;
+
switch (req->pdu.type) {
case PDU_TYPE_SERIAL_QUERY:
- handle_serial_query_pdu(req);
+ error = handle_serial_query_pdu(req);
break;
case PDU_TYPE_RESET_QUERY:
- handle_reset_query_pdu(req);
+ error = handle_reset_query_pdu(req);
break;
default:
/* Should have been catched during constructor */
- pr_crit("Unexpected PDU type: %u",
- req->pdu.type);
+ pr_crit("Unexpected PDU type: %u", req->pdu.type);
}
rtreq_destroy(req);
+ return error;
}
static void *
do {
if (!pdustream_parse(client, &reqs)) {
- pr_op_debug("RTR worker thread: %s errored.",
+kick_client: pr_op_debug("RTR worker thread: %s errored.",
client->addr);
rtreqlist_clear(&reqs);
mutex_lock(&lock);
}
while ((req = rtreqlist_pop(&reqs)) != NULL)
- handle_request(req);
+ if (handle_request(req) != 0)
+ goto kick_client;
} while (true);
if (write(worker2poller[1], &one, 1) < 0)
MOCK(config_get_local_repository, char const *, "tmp", void)
MOCK_UINT(config_get_deltas_lifetime, deltas_lifetime, void)
+MOCK_UINT(max_rtr_version, 2, void)
MOCK_UINT(config_get_validation_interval, 3600, void)
MOCK_UINT(config_get_max_aspa_providers, 10, void)
/* Mocks */
MOCK(config_get_local_repository, char const *, "tmp", void)
+MOCK_UINT(max_rtr_version, 2, void)
MOCK_ABORT_INT(err_pdu_send_invalid_request, int fd, uint8_t version,
struct rtr_buffer const *request, char const *msg)
MOCK_ABORT_INT(err_pdu_send_unsupported_proto_version, int fd, uint8_t version,