// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Christophe Fontaine

#include "control_input.h"
#include "graph.h"
#include "iface.h"
#include "log.h"
#include "loopback.h"
#include "mbuf.h"

LOG_TYPE("graph");

static rte_edge_t control_to_loopback_input;

int loopback_input_send(struct rte_mbuf *m) {
	return post_to_stack(control_to_loopback_input, m);
}

enum {
	UNKNOWN_PROTO = 0,
	EDGE_COUNT,
};

static rte_edge_t l3_edges[UINT_NUM_VALUES(rte_be16_t)] = {UNKNOWN_PROTO};

void loopback_input_add_type(rte_be16_t eth_type, const char *next_node) {
	LOG(DEBUG, "loopback_input: type=0x%04x -> %s", rte_be_to_cpu_16(eth_type), next_node);
	if (l3_edges[eth_type] != UNKNOWN_PROTO)
		ABORT("next node already registered for ether type=0x%04x",
		      rte_be_to_cpu_16(eth_type));
	l3_edges[eth_type] = gr_node_attach_parent("loopback_input", next_node);
}

static uint16_t loopback_input_process(
	struct rte_graph *graph,
	struct rte_node *node,
	void **objs,
	uint16_t nb_objs
) {
	struct rte_mbuf *mbuf;
	rte_edge_t edge;

	for (uint16_t i = 0; i < nb_objs; i++) {
		mbuf = objs[i];

		if (gr_mbuf_is_traced(mbuf)
		    || mbuf_data(mbuf)->iface->flags & GR_IFACE_F_PACKET_TRACE) {
			gr_mbuf_trace_add(mbuf, node, 0);
		}

		switch (mbuf->packet_type) {
		case RTE_PTYPE_L3_IPV4:
			edge = l3_edges[RTE_BE16(RTE_ETHER_TYPE_IPV4)];
			break;
		case RTE_PTYPE_L3_IPV6:
			edge = l3_edges[RTE_BE16(RTE_ETHER_TYPE_IPV6)];
			break;
		default:
			edge = UNKNOWN_PROTO;
			break;
		}
		rte_node_enqueue_x1(graph, node, edge, mbuf);
	}

	return nb_objs;
}

static struct rte_node_register loopback_input_node = {
	.name = "loopback_input",
	.process = loopback_input_process,
	.nb_edges = EDGE_COUNT,
	.next_nodes = {
		[UNKNOWN_PROTO] = "loopback_unknown_proto",
	},
};

static void loopback_input_register(void) {
	control_to_loopback_input = gr_control_input_register_handler("loopback_input");
}

static struct gr_node_info info = {
	.node = &loopback_input_node,
	.type = GR_NODE_T_CONTROL | GR_NODE_T_L3,
	.register_callback = loopback_input_register,
};

GR_NODE_REGISTER(info);
GR_DROP_REGISTER(loopback_unknown_proto);
