/* vi: set sw=4 ts=4: */
/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 *
 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
 */

#include <net/if.h>  /* struct ifreq and co. */

#include "libbb.h"
#include "libnetlink.h"
#include "ll_map.h"

struct idxmap {
	struct idxmap *next;
	int            index;
	int            type;
	int            alen;
	unsigned       flags;
	unsigned char  addr[8];
	char           name[16];
};

static struct idxmap **idxmap; /* treat as *idxmap[16] */

static struct idxmap *find_by_index(int idx)
{
	struct idxmap *im;

	if (idxmap)
		for (im = idxmap[idx & 0xF]; im; im = im->next)
			if (im->index == idx)
				return im;
	return NULL;
}

int FAST_FUNC ll_remember_index(const struct sockaddr_nl *who UNUSED_PARAM,
		struct nlmsghdr *n,
		void *arg UNUSED_PARAM)
{
	int h;
	struct ifinfomsg *ifi = NLMSG_DATA(n);
	struct idxmap *im, **imp;
	struct rtattr *tb[IFLA_MAX+1];

	if (n->nlmsg_type != RTM_NEWLINK)
		return 0;

	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
		return -1;

	memset(tb, 0, sizeof(tb));
	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
	if (tb[IFLA_IFNAME] == NULL)
		return 0;

	if (!idxmap)
		idxmap = xzalloc(sizeof(idxmap[0]) * 16);

	h = ifi->ifi_index & 0xF;
	for (imp = &idxmap[h]; (im = *imp) != NULL; imp = &im->next)
		if (im->index == ifi->ifi_index)
			goto found;

	im = xmalloc(sizeof(*im));
	im->next = *imp;
	im->index = ifi->ifi_index;
	*imp = im;
 found:
	im->type = ifi->ifi_type;
	im->flags = ifi->ifi_flags;
	if (tb[IFLA_ADDRESS]) {
		int alen;
		im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
		if (alen > (int)sizeof(im->addr))
			alen = sizeof(im->addr);
		memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen);
	} else {
		im->alen = 0;
		memset(im->addr, 0, sizeof(im->addr));
	}
	strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME]));
	return 0;
}

static
const char FAST_FUNC *ll_idx_n2a(int idx/*, char *buf*/)
{
	struct idxmap *im;

	if (idx == 0)
		return "*";
	im = find_by_index(idx);
	if (im)
		return im->name;
	//snprintf(buf, 16, "if%d", idx);
	//return buf;
	return auto_string(xasprintf("if%d", idx));
}

const char FAST_FUNC *ll_index_to_name(int idx)
{
	//static char nbuf[16];
	return ll_idx_n2a(idx/*, nbuf*/);
}

#ifdef UNUSED
int ll_index_to_type(int idx)
{
	struct idxmap *im;

	if (idx == 0)
		return -1;
	im = find_by_index(idx);
	if (im)
		return im->type;
	return -1;
}
#endif

unsigned FAST_FUNC ll_index_to_flags(int idx)
{
	struct idxmap *im;

	if (idx == 0)
		return 0;
	im = find_by_index(idx);
	if (im)
		return im->flags;
	return 0;
}

int FAST_FUNC xll_name_to_index(const char *name)
{
	int ret = 0;

/* caching is not warranted - no users which repeatedly call it */
#ifdef UNUSED
	static char ncache[16];
	static int icache;

	struct idxmap *im;
	int i;

	if (name == NULL)
		goto out;
	if (icache && strcmp(name, ncache) == 0) {
		ret = icache;
		goto out;
	}
	if (idxmap) {
		for (i = 0; i < 16; i++) {
			for (im = idxmap[i]; im; im = im->next) {
				if (strcmp(im->name, name) == 0) {
					icache = im->index;
					strcpy(ncache, name);
					ret = im->index;
					goto out;
				}
			}
		}
	}
#endif
	ret = if_nametoindex(name);
/* out:*/
	if (ret <= 0)
		bb_error_msg_and_die("can't find device '%s'", name);
	return ret;
}

int FAST_FUNC ll_init_map(struct rtnl_handle *rth)
{
	xrtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK);
	xrtnl_dump_filter(rth, ll_remember_index, NULL);
	return 0;
}