astlpc: Introduce protocol v3 with integrity checks
v3 of the binding adds a CRC-32 value as a medium-specific trailer to
each packet passing over the binding interface.
The patch includes a naive bit-shift implementation of CRC-32, we can
improve it later as necessary.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Change-Id: I93a95bccef30010d56e10e29b6d84554268ab7af
diff --git a/astlpc.c b/astlpc.c
index 95d8701..053de11 100644
--- a/astlpc.c
+++ b/astlpc.c
@@ -18,11 +18,12 @@
#define pr_fmt(x) "astlpc: " x
+#include "container_of.h"
+#include "crc32.h"
#include "libmctp.h"
#include "libmctp-alloc.h"
#include "libmctp-log.h"
#include "libmctp-astlpc.h"
-#include "container_of.h"
#include "range.h"
#ifdef MCTP_HAVE_FILEIO
@@ -53,6 +54,8 @@
uint16_t version;
uint32_t (*packet_size)(uint32_t body);
uint32_t (*body_size)(uint32_t packet);
+ void (*pktbuf_protect)(struct mctp_pktbuf *pkt);
+ bool (*pktbuf_validate)(struct mctp_pktbuf *pkt);
};
struct mctp_binding_astlpc {
@@ -103,7 +106,7 @@
/* Support testing of new binding protocols */
#ifndef ASTLPC_VER_CUR
-#define ASTLPC_VER_CUR 2
+#define ASTLPC_VER_CUR 3
#endif
/* clang-format on */
@@ -125,21 +128,79 @@
return packet - 4;
}
+void astlpc_pktbuf_protect_v1(struct mctp_pktbuf *pkt)
+{
+ (void)pkt;
+}
+
+bool astlpc_pktbuf_validate_v1(struct mctp_pktbuf *pkt)
+{
+ (void)pkt;
+ return true;
+}
+
+static uint32_t astlpc_packet_size_v3(uint32_t body)
+{
+ assert((body + 4 + 4) > body);
+
+ return body + 4 + 4;
+}
+
+static uint32_t astlpc_body_size_v3(uint32_t packet)
+{
+ assert((packet - 4 - 4) < packet);
+
+ return packet - 4 - 4;
+}
+
+void astlpc_pktbuf_protect_v3(struct mctp_pktbuf *pkt)
+{
+ uint32_t code;
+
+ code = htobe32(crc32(mctp_pktbuf_hdr(pkt), mctp_pktbuf_size(pkt)));
+ mctp_prdebug("%s: 0x%" PRIx32, __func__, code);
+ mctp_pktbuf_push(pkt, &code, 4);
+}
+
+bool astlpc_pktbuf_validate_v3(struct mctp_pktbuf *pkt)
+{
+ uint32_t code;
+ void *check;
+
+ code = be32toh(crc32(mctp_pktbuf_hdr(pkt), mctp_pktbuf_size(pkt) - 4));
+ mctp_prdebug("%s: 0x%" PRIx32, __func__, code);
+ check = mctp_pktbuf_pop(pkt, 4);
+ return check && !memcmp(&code, check, 4);
+}
+
static const struct mctp_astlpc_protocol astlpc_protocol_version[] = {
[0] = {
.version = 0,
.packet_size = NULL,
.body_size = NULL,
+ .pktbuf_protect = NULL,
+ .pktbuf_validate = NULL,
},
[1] = {
.version = 1,
.packet_size = astlpc_packet_size_v1,
.body_size = astlpc_body_size_v1,
+ .pktbuf_protect = astlpc_pktbuf_protect_v1,
+ .pktbuf_validate = astlpc_pktbuf_validate_v1,
},
[2] = {
.version = 2,
.packet_size = astlpc_packet_size_v1,
.body_size = astlpc_body_size_v1,
+ .pktbuf_protect = astlpc_pktbuf_protect_v1,
+ .pktbuf_validate = astlpc_pktbuf_validate_v1,
+ },
+ [3] = {
+ .version = 3,
+ .packet_size = astlpc_packet_size_v3,
+ .body_size = astlpc_body_size_v3,
+ .pktbuf_protect = astlpc_pktbuf_protect_v3,
+ .pktbuf_validate = astlpc_pktbuf_validate_v3,
},
};
@@ -710,18 +771,24 @@
__func__, len, hdr->src, hdr->dest, hdr->flags_seq_tag);
if (len > astlpc->proto->body_size(astlpc->layout.tx.size)) {
- astlpc_prwarn(astlpc, "invalid TX len 0x%x", len);
+ astlpc_prwarn(astlpc, "invalid TX len %" PRIu32 ": %" PRIu32, len,
+ astlpc->proto->body_size(astlpc->layout.tx.size));
return -1;
}
len_be = htobe32(len);
mctp_astlpc_lpc_write(astlpc, &len_be, astlpc->layout.tx.offset,
sizeof(len_be));
+
+ astlpc->proto->pktbuf_protect(pkt);
+ len = mctp_pktbuf_size(pkt);
+
mctp_astlpc_lpc_write(astlpc, hdr, astlpc->layout.tx.offset + 4, len);
mctp_binding_set_tx_enabled(b, false);
mctp_astlpc_kcs_send(astlpc, 0x1);
+
return 0;
}
@@ -850,32 +917,48 @@
static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc)
{
struct mctp_pktbuf *pkt;
- uint32_t len;
+ uint32_t body, packet;
- mctp_astlpc_lpc_read(astlpc, &len, astlpc->layout.rx.offset,
- sizeof(len));
- len = be32toh(len);
+ mctp_astlpc_lpc_read(astlpc, &body, astlpc->layout.rx.offset,
+ sizeof(body));
+ body = be32toh(body);
- if (len > astlpc->proto->body_size(astlpc->layout.rx.size)) {
- astlpc_prwarn(astlpc, "invalid RX len 0x%x", len);
+ if (body > astlpc->proto->body_size(astlpc->layout.rx.size)) {
+ astlpc_prwarn(astlpc, "invalid RX len 0x%x", body);
return;
}
assert(astlpc->binding.pkt_size >= 0);
- if (len > (uint32_t)astlpc->binding.pkt_size) {
- mctp_prwarn("invalid RX len 0x%x", len);
- astlpc_prwarn(astlpc, "invalid RX len 0x%x", len);
+ if (body > (uint32_t)astlpc->binding.pkt_size) {
+ astlpc_prwarn(astlpc, "invalid RX len 0x%x", body);
return;
}
- pkt = mctp_pktbuf_alloc(&astlpc->binding, len);
+ /* Eliminate the medium-specific header that we just read */
+ packet = astlpc->proto->packet_size(body) - 4;
+ pkt = mctp_pktbuf_alloc(&astlpc->binding, packet);
if (!pkt)
goto out_complete;
+ /*
+ * Read payload and medium-specific trailer from immediately after the
+ * medium-specific header.
+ */
mctp_astlpc_lpc_read(astlpc, mctp_pktbuf_hdr(pkt),
- astlpc->layout.rx.offset + 4, len);
+ astlpc->layout.rx.offset + 4, packet);
- mctp_bus_rx(&astlpc->binding, pkt);
+ /*
+ * v3 will validate the CRC32 in the medium-specific trailer and adjust
+ * the packet size accordingly. On older protocols validation is a no-op
+ * that always returns true.
+ */
+ if (astlpc->proto->pktbuf_validate(pkt)) {
+ mctp_bus_rx(&astlpc->binding, pkt);
+ } else {
+ /* TODO: Drop any associated assembly */
+ mctp_pktbuf_free(pkt);
+ astlpc_prdebug(astlpc, "Dropped corrupt packet");
+ }
out_complete:
mctp_astlpc_kcs_send(astlpc, 0x2);
@@ -1049,8 +1132,8 @@
astlpc->binding.version = 1;
astlpc->binding.pkt_size =
MCTP_PACKET_SIZE(mtu > MCTP_BTU ? mtu : MCTP_BTU);
- astlpc->binding.pkt_header = 0;
- astlpc->binding.pkt_trailer = 0;
+ astlpc->binding.pkt_header = 4;
+ astlpc->binding.pkt_trailer = 4;
astlpc->binding.tx = mctp_binding_astlpc_tx;
if (mode == MCTP_BINDING_ASTLPC_MODE_BMC)
astlpc->binding.start = mctp_binding_astlpc_start_bmc;