/*
 * dbh_nl.c - netlink interface to kernel.
 *
 * Copyright (C) 2021-2024 IOPSYS Software Solutions AB. All rights reserved.
 * Copyright (C) 2025 Genexis AB.
 *
 * Author: jakob.olsson@genexis.eu
 *
 * See LICENSE file for license related information.
 *
 */
#ifdef DYNBH
#include "dynbh_nl.h"

#include <easy/utils.h>
#include <easy/if_utils.h>
#include <netlink/attr.h>
#include <netlink/genl/genl.h>
#include <stdbool.h>
#include <linux/if.h>

#include "dynbh.h"
#include "agent.h"
#include "../utils/debug.h"

int dynbh_handle_nlevents_link(struct agent *a, struct nlmsghdr *hdr, bool add)
{
	struct dynbh_ctx *ctx = &a->dbh;
	struct ifinfomsg *ifi = nlmsg_data(hdr);
	struct nlattr *nla[__IFLA_MAX];
	struct ethport *port;
	uint8_t macaddr[6] = {0};
	char ifname[16] = {0};
	int br_ifindex = 0;
	uint8_t operstate;

	agnt_trace(LOG_DYNBH, "%s: ===>\n", __func__);

	/* bypass any backhaul handling with forced local controller */
	if (!dynbh_is_enabled(a))
		return -1;

	if (!nlmsg_valid_hdr(hdr, sizeof(*ifi)))
		return -1;

	nlmsg_parse(hdr, sizeof(*ifi), nla, __IFLA_MAX - 1, NULL);
	if (!nla[IFLA_IFNAME])
		return -1;

	nla_memcpy(ifname, nla[IFLA_IFNAME], 15);
	nla_memcpy(macaddr, nla[IFLA_ADDRESS], sizeof(macaddr));
	nla_memcpy(&operstate, nla[IFLA_OPERSTATE], 1);

	port = dynbh_get_ethport(ctx, ifname);
	if (!port)
		return -1;

	if (!!(ifi->ifi_flags & IFF_RUNNING)) {
		agnt_dbg(LOG_DYNBH, "%s: %s is UP RUNNING\n", __func__, ifname);
	}

	if (!(ifi->ifi_flags & IFF_UP)) {
		agnt_dbg(LOG_DYNBH, "%s: %s is down. skip..\n", __func__, ifname);
		return NL_OK;
	}

	br_ifindex = if_isbridge_interface(ifname);
	if (br_ifindex < 0) {
		agnt_dbg(LOG_DYNBH, "%s: %s error getting br_ifindex\n", __func__, ifname);
		return -1;
	}

	agnt_dbg(LOG_DYNBH, "%s: %s : %s (" MACFMT ", %d), master = %d, fam = %d, flags = 0x%x operstate:0x%x\n",
	        __func__, (add ? "NEWLINK" : "DELLINK"), ifname,
		MAC2STR(macaddr), ifi->ifi_index, br_ifindex, ifi->ifi_family,
		ifi->ifi_flags, operstate);
	if (br_ifindex > 0 && ifi->ifi_family == AF_BRIDGE &&
	    port->operstate == IF_OPER_DOWN && operstate == IF_OPER_UP) {
		/* port got connected */
		dynbh_port_connected(a, port);
	} else if (port->operstate == IF_OPER_UP && operstate == IF_OPER_DOWN) {
		/* port got disconnected */
		dynbh_port_disconnected(a, port);
	}
	port->operstate = operstate;

	return 0;
}
#endif
