/*
 * libnl_link:	Handle dynamic linking to netlink libraries
 *
 * Authors:	P. Quentin Armitage <Quentin@Armitage.org.uk>
 *
 *		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.
 *
 *  Copyright (C) 2017-2017 Alexandre Cassen, <acassen@gmail.com>
 */

#include "config.h"

#ifdef _LIBNL_DYNAMIC_

#include <stdbool.h>

#include <dlfcn.h>
#include <netlink/netlink.h>
#include <netlink/socket.h>

#ifdef LIBIPVS_USE_NL
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#endif

#include "libnl_link.h"
#include "logger.h"


/* The addresses of the functions we want */
struct nl_sock * (*nl_socket_alloc_addr)(void);
void (*nl_socket_free_addr)(struct nl_sock *);
#ifdef LIBIPVS_USE_NL
int (*genl_connect_addr)(struct nl_sock *);
int (*genl_ctrl_resolve_addr)(struct nl_sock *, const char *);
int (*genlmsg_parse_addr)(struct nlmsghdr *, int, struct nlattr **, int, struct nla_policy *);
void * (*genlmsg_put_addr)(struct nl_msg *, uint32_t, uint32_t, int, int, int, uint8_t, uint8_t);
int (*nla_nest_end_addr)(struct nl_msg *, struct nlattr *);
struct nlattr * (*nla_nest_start_addr)(struct nl_msg *, int);
int (*nla_put_daddr)(struct nl_msg *, int, int, const void *);
struct nl_msg * (*nlmsg_alloc_addr)(void);
void (*nlmsg_free_addr)(struct nl_msg *);
struct nlmsghdr * (*nlmsg_hdr_addr)(struct nl_msg *);
int (*nl_recvmsgs_default_addr)(struct nl_sock *);
int (*nl_send_auto_addr)(struct nl_sock *,  struct nl_msg *);
int (*nl_socket_modify_cb_addr)(struct nl_sock *, enum nl_cb_type, enum nl_cb_kind, nl_recvmsg_msg_cb_t, void *);
int (*nl_socket_modify_err_cb_addr)(struct nl_sock *, enum nl_cb_kind, nl_recvmsg_err_cb_t, void *);
#ifdef _HAVE_LIBNL3_
void * (*nla_data_addr)(const struct nlattr *);
int32_t (*nla_get_s32_addr)(const struct nlattr *);
char * (*nla_get_string_addr)(const struct nlattr *);
uint16_t (*nla_get_u16_addr)(const struct nlattr *);
uint32_t (*nla_get_u32_addr)(const struct nlattr *);
uint64_t (*nla_get_u64_addr)(const struct nlattr *);
int (*nla_memcpy_addr)(void *, const struct nlattr *, int);
int (*nla_parse_nested_addr)(struct nlattr **, int, struct nlattr *, struct nla_policy *);
#endif
#endif


static void* libnl_handle;
#ifdef LIBIPVS_USE_NL
static void* libnl_genl_handle;
#endif

bool
libnl_init(void)
{
	if (libnl_handle)
		return true;

	/* Attempt to open the necessary libraries */
#ifdef _HAVE_LIBNL1_
#ifdef _WITH_LVS_
	if (!(libnl_handle = dlopen("libnl.so", RTLD_NOW)) &&
	    !(libnl_handle = dlopen(NL_LIB_NAME, RTLD_NOW))) {
		log_message(LOG_INFO, "Unable to load nl library - %s", dlerror());
		return false;
	}
	libnl_genl_handle = libnl_handle;
#endif
#else
	if (!(libnl_handle = dlopen("libnl-3.so", RTLD_NOW)) &&
	    !(libnl_handle = dlopen(NL3_LIB_NAME, RTLD_NOW))) {
		log_message(LOG_INFO, "Unable to load nl-3 library - %s", dlerror());
		return false;
	}
#ifdef _WITH_LVS_
	if (!(libnl_genl_handle = dlopen("libnl-genl-3.so", RTLD_NOW)) &&
	    !(libnl_genl_handle = dlopen(NL3_GENL_LIB_NAME, RTLD_NOW))) {
		log_message(LOG_INFO, "Unable to load nl-genl-3 library - %s", dlerror());
		return false;
	}
#endif
#endif

	if (
#ifdef _HAVE_LIBNL1_
	    !(nl_socket_alloc_addr = dlsym(libnl_handle, "nl_handle_alloc")) ||
	    !(nl_socket_free_addr = dlsym(libnl_handle, "nl_handle_destroy")) ||
#else
	    !(nl_socket_alloc_addr = dlsym(libnl_handle, "nl_socket_alloc")) ||
	    !(nl_socket_free_addr = dlsym(libnl_handle, "nl_socket_free")) ||
#endif
#ifdef _WITH_LVS_
	    !(genl_connect_addr = dlsym(libnl_genl_handle, "genl_connect")) ||
	    !(genl_ctrl_resolve_addr = dlsym(libnl_genl_handle, "genl_ctrl_resolve")) ||
	    !(genlmsg_parse_addr = dlsym(libnl_genl_handle, "genlmsg_parse")) ||
	    !(genlmsg_put_addr = dlsym(libnl_genl_handle, "genlmsg_put")) ||
	    !(nla_nest_end_addr = dlsym(libnl_handle, "nla_nest_end")) ||
	    !(nla_nest_start_addr = dlsym(libnl_handle, "nla_nest_start")) ||
	    !(nla_put_daddr = dlsym(libnl_handle, "nla_put")) ||
	    !(nlmsg_alloc_addr = dlsym(libnl_handle, "nlmsg_alloc")) ||
	    !(nlmsg_free_addr = dlsym(libnl_handle, "nlmsg_free")) ||
	    !(nlmsg_hdr_addr = dlsym(libnl_handle, "nlmsg_hdr")) ||
	    !(nl_recvmsgs_default_addr = dlsym(libnl_handle, "nl_recvmsgs_default")) ||
	    !(nl_send_auto_addr = dlsym(libnl_handle, "nl_send_auto")) ||
	    !(nl_socket_modify_cb_addr = dlsym(libnl_handle, "nl_socket_modify_cb")) ||
	    !(nl_socket_modify_err_cb_addr = dlsym(libnl_handle, "nl_socket_modify_err_cb")) ||
#ifdef _HAVE_LIBNL3_
	    !(nla_data_addr = dlsym(libnl_handle, "nla_data")) ||
	    !(nla_get_s32_addr = dlsym(libnl_handle, "nla_get_s32")) ||
	    !(nla_get_string_addr = dlsym(libnl_handle, "nla_get_string")) ||
	    !(nla_get_u16_addr = dlsym(libnl_handle, "nla_get_u16")) ||
	    !(nla_get_u32_addr = dlsym(libnl_handle, "nla_get_u32")) ||
	    !(nla_get_u64_addr = dlsym(libnl_handle, "nla_get_u64")) ||
	    !(nla_memcpy_addr = dlsym(libnl_handle, "nla_memcpy")) ||
	    !(nla_parse_nested_addr = dlsym(libnl_handle, "nla_parse_nested")) ||
#endif
#endif
	    false)
		log_message(LOG_INFO, "Failed to dynamic link a libnli/libnl-3 function");

	return true;
}
#endif
