core,API: Add bridge support

This change introduces a facility to bridge messages between two
bindings.

This is implemented through a new mctp_bridge_busses() API, which
applies a new routing policy, sending packets from one binding to the
other. This is in contrast to the current policy of dropping all
non-local packets.

To do this, the message context code needs to know both source and
destination EIDs, so add them to the mctp_msg_ctx_lookup() criteria.

Also, add a small test for bridge mode.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Change-Id: If532613525ddbf81df249e26d0f23825996f7bda
diff --git a/tests/test_bridge.c b/tests/test_bridge.c
new file mode 100644
index 0000000..496ae19
--- /dev/null
+++ b/tests/test_bridge.c
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <libmctp.h>
+#include <libmctp-alloc.h>
+
+#include "test-utils.h"
+
+struct mctp_binding_bridge {
+	struct mctp_binding	binding;
+	int			rx_count;
+	int			tx_count;
+	uint8_t			last_pkt_data;
+};
+
+struct test_ctx {
+	struct mctp			*mctp;
+	struct mctp_binding_bridge	*bindings[2];
+};
+
+static int mctp_binding_bridge_tx(struct mctp_binding *b,
+		struct mctp_pktbuf *pkt)
+{
+	struct mctp_binding_bridge *binding = container_of(b,
+			struct mctp_binding_bridge, binding);
+
+	binding->tx_count++;
+	assert(mctp_pktbuf_size(pkt) == sizeof(struct mctp_hdr) + 1);
+	binding->last_pkt_data = *(uint8_t *)mctp_pktbuf_data(pkt);
+
+	return 0;
+}
+
+static int mctp_binding_bridge_rx(struct mctp_binding_bridge *binding,
+		uint8_t key)
+{
+	struct mctp_pktbuf *pkt;
+	struct mctp_hdr *hdr;
+	uint8_t *buf;
+
+	pkt = mctp_pktbuf_alloc(&binding->binding,
+			sizeof(struct mctp_hdr) + 1);
+
+	hdr = mctp_pktbuf_hdr(pkt);
+	hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM;
+
+	/* arbitrary src/dest, as we're bridging */
+	hdr->src = 1;
+	hdr->dest = 2;
+
+	buf = mctp_pktbuf_data(pkt);
+	*buf = key;
+
+	binding->rx_count++;
+	mctp_bus_rx(&binding->binding, pkt);
+}
+
+static struct mctp_binding_bridge *mctp_binding_bridge_init(void)
+{
+	struct mctp_binding_bridge *binding;
+
+	binding = __mctp_alloc(sizeof(*binding));
+	memset(binding, 0, sizeof(*binding));
+	binding->binding.name = "test";
+	binding->binding.version = 1;
+	binding->binding.tx = mctp_binding_bridge_tx;
+	binding->binding.pkt_size = MCTP_BMTU;
+	binding->binding.pkt_pad = 0;
+	return binding;
+}
+
+int main(void)
+{
+	struct test_ctx _ctx, *ctx = &_ctx;
+
+	ctx->mctp = mctp_init();
+	ctx->bindings[0] = mctp_binding_bridge_init();
+	ctx->bindings[1] = mctp_binding_bridge_init();
+
+	mctp_bridge_busses(ctx->mctp,
+			&ctx->bindings[0]->binding,
+			&ctx->bindings[1]->binding);
+
+	mctp_binding_set_tx_enabled(&ctx->bindings[0]->binding, true);
+	mctp_binding_set_tx_enabled(&ctx->bindings[1]->binding, true);
+
+	mctp_binding_bridge_rx(ctx->bindings[0], 0xaa);
+	assert(ctx->bindings[0]->tx_count == 0);
+	assert(ctx->bindings[1]->tx_count == 1);
+	assert(ctx->bindings[1]->last_pkt_data == 0xaa);
+
+	mctp_binding_bridge_rx(ctx->bindings[1], 0x55);
+	assert(ctx->bindings[1]->tx_count == 1);
+	assert(ctx->bindings[0]->tx_count == 1);
+	assert(ctx->bindings[0]->last_pkt_data == 0x55);
+
+	return EXIT_SUCCESS;
+}