/*
 * Copyright (c) 2013-2015 Intel Corporation.  All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#include "iwarp_pm.h"
#include <endian.h>

/* iwpm config params */
static const char * iwpm_param_names[IWPM_PARAM_NUM] =
	{ "nl_sock_rbuf_size" };
static int iwpm_param_vals[IWPM_PARAM_NUM] =
	{ 0 };

/**
 * get_iwpm_param()
 */
static int get_iwpm_param(char *param_name, int val)
{
	int i, ret;
	for (i = 0; i < IWPM_PARAM_NUM; i++) {
		ret = strcmp(param_name, iwpm_param_names[i]);
		if (!ret && val > 0) {
			syslog(LOG_WARNING, "get_iwpm_param: Got param (name = %s val = %d)\n", param_name, val);
			iwpm_param_vals[i] = val;
			return ret;
		}
	}
	return ret;
}

/**
 * parse_iwpm_config()
 */
void parse_iwpm_config(FILE *fp)
{
	char line_buf[128];
	char param_name[IWPM_PARAM_NAME_LEN];
	int n, val, ret;
	char *str;

	str = fgets(line_buf, 128, fp);
	while (str) {
		if (line_buf[0] == '#' || line_buf[0] == '\n')
			goto parse_next_line;
		n = sscanf(line_buf, "%64[^= ] %*[=]%d", param_name, &val);
		if (n != 2) {
			syslog(LOG_WARNING, "parse_iwpm_config: Couldn't parse a line (n = %d, name = %s, val = %d\n", n, param_name, val);
			goto parse_next_line;
		}
		ret = get_iwpm_param(param_name, val);
		if (ret)
			syslog(LOG_WARNING, "parse_iwpm_config: Couldn't find param (ret = %d)\n", ret);
parse_next_line:
		str = fgets(line_buf, 128, fp);
	}
}

/**
 * create_iwpm_socket_v4 - Create an ipv4 socket for the iwarp port mapper
 * @bind_port: UDP port to bind the socket
 *
 * Return a handle of ipv4 socket
 */
int create_iwpm_socket_v4(__u16 bind_port)
{
	sockaddr_union bind_addr;
	struct sockaddr_in *bind_in4;
	int pm_sock;
	socklen_t sockname_len;
	char ip_address_text[INET6_ADDRSTRLEN];

	/* create a socket */
	pm_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (pm_sock < 0) {
		syslog(LOG_WARNING, "create_iwpm_socket_v4: Unable to create socket. %s.\n",
				strerror(errno));
		pm_sock = -errno;
		goto create_socket_v4_exit;
	}
	/* bind the socket to the given port */
	memset(&bind_addr, 0, sizeof(bind_addr));
	bind_in4 = &bind_addr.v4_sockaddr;
	bind_in4->sin_family = AF_INET;
	bind_in4->sin_addr.s_addr = htobe32(INADDR_ANY);
	bind_in4->sin_port = htobe16(bind_port);

	if (bind(pm_sock, &bind_addr.sock_addr, sizeof(struct sockaddr_in))) {
		syslog(LOG_WARNING, "create_iwpm_socket_v4: Unable to bind socket (port = %u). %s.\n",
				bind_port, strerror(errno));
		close(pm_sock);
		pm_sock = -errno;
		goto create_socket_v4_exit;
	}

	/* get the socket name (local port number) */
	sockname_len = sizeof(struct sockaddr_in);
	if (getsockname(pm_sock, &bind_addr.sock_addr, &sockname_len)) {
		syslog(LOG_WARNING, "create_iwpm_socket_v4: Unable to get socket name. %s.\n",
				strerror(errno));
		close(pm_sock);
		pm_sock = -errno;
		goto create_socket_v4_exit;
	}

	iwpm_debug(IWARP_PM_WIRE_DBG, "create_iwpm_socket_v4: Socket IP address:port %s:%u\n",
		inet_ntop(bind_in4->sin_family, &bind_in4->sin_addr.s_addr, ip_address_text,
			INET6_ADDRSTRLEN), be16toh(bind_in4->sin_port));
create_socket_v4_exit:
	return pm_sock;
}

/**
 * create_iwpm_socket_v6 - Create an ipv6 socket for the iwarp port mapper
 * @bind_port: UDP port to bind the socket
 *
 * Return a handle of ipv6 socket
 */
int create_iwpm_socket_v6(__u16 bind_port)
{
	sockaddr_union bind_addr;
	struct sockaddr_in6 *bind_in6;
	int pm_sock, ret_value, ipv6_only;
	socklen_t sockname_len;
	char ip_address_text[INET6_ADDRSTRLEN];

	/* create a socket */
	pm_sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
	if (pm_sock < 0) {
		syslog(LOG_WARNING, "create_iwpm_socket_v6: Unable to create socket. %s.\n",
				strerror(errno));
		pm_sock = -errno;
		goto create_socket_v6_exit;
	}

	ipv6_only = 1;
	ret_value = setsockopt(pm_sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only));
	if (ret_value < 0) {
		syslog(LOG_WARNING, "create_iwpm_socket_v6: Unable to set sock options. %s.\n",
				strerror(errno));
		close(pm_sock);
		pm_sock = -errno;
		goto create_socket_v6_exit;
	}

	/* bind the socket to the given port */
	memset(&bind_addr, 0, sizeof(bind_addr));
	bind_in6 = &bind_addr.v6_sockaddr;
	bind_in6->sin6_family = AF_INET6;
	bind_in6->sin6_addr = in6addr_any;
	bind_in6->sin6_port = htobe16(bind_port);

	if (bind(pm_sock, &bind_addr.sock_addr, sizeof(struct sockaddr_in6))) {
		syslog(LOG_WARNING, "create_iwpm_socket_v6: Unable to bind socket (port = %u). %s.\n",
				bind_port, strerror(errno));
		close(pm_sock);
		pm_sock = -errno;
		goto create_socket_v6_exit;
	}

	/* get the socket name (local port number) */
	sockname_len = sizeof(struct sockaddr_in6);
	if (getsockname(pm_sock, &bind_addr.sock_addr, &sockname_len)) {
		syslog(LOG_WARNING, "create_iwpm_socket_v6: Unable to get socket name. %s.\n",
				strerror(errno));
		close(pm_sock);
		pm_sock = -errno;
		goto create_socket_v6_exit;
	}

	iwpm_debug(IWARP_PM_WIRE_DBG, "create_iwpm_socket_v6: Socket IP address:port %s:%04X\n",
		inet_ntop(bind_in6->sin6_family, &bind_in6->sin6_addr, ip_address_text,
			INET6_ADDRSTRLEN), be16toh(bind_in6->sin6_port));
create_socket_v6_exit:
	return pm_sock;
}

/**
 * create_netlink_socket - Create netlink socket for the iwarp port mapper
 */
int create_netlink_socket(void)
{
	sockaddr_union bind_addr;
	struct sockaddr_nl *bind_nl;
	int nl_sock;
	__u32 rbuf_size, opt_len;

	/* create a socket */
	nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_RDMA);
	if (nl_sock < 0) {
		syslog(LOG_WARNING, "create_netlink_socket: Unable to create socket. %s.\n",
				strerror(errno));
		nl_sock = -errno;
		goto create_nl_socket_exit;
	}

	/* bind the socket */
	memset(&bind_addr, 0, sizeof(bind_addr));
	bind_nl = &bind_addr.nl_sockaddr;
	bind_nl->nl_family = AF_NETLINK;
	bind_nl->nl_pid = getpid();
	bind_nl->nl_groups = 3; /* != 0 support multicast */

	if (bind(nl_sock, &bind_addr.sock_addr, sizeof(struct sockaddr_nl))) {
		syslog(LOG_WARNING, "create_netlink_socket: Unable to bind socket. %s.\n",
				strerror(errno));
		close(nl_sock);
		nl_sock = -errno;
		goto create_nl_socket_exit;
	}
	if (iwpm_param_vals[NL_SOCK_RBUF_SIZE] > 0) {
		rbuf_size = iwpm_param_vals[NL_SOCK_RBUF_SIZE];

		if (setsockopt(nl_sock, SOL_SOCKET, SO_RCVBUFFORCE, &rbuf_size, sizeof rbuf_size)) {
			syslog(LOG_WARNING, "create_netlink_socket: Unable to set sock option "
				"(rbuf_size = %u). %s.\n", rbuf_size, strerror(errno));
			if (setsockopt(nl_sock, SOL_SOCKET, SO_RCVBUF,
					&rbuf_size, sizeof rbuf_size)) {
				syslog(LOG_WARNING, "create_netlink_socket: "
					"Unable to set sock option %s. Closing socket\n", strerror(errno));
				close(nl_sock);
				nl_sock = -errno;
				goto create_nl_socket_exit;
               		}
		}
	}
	getsockopt(nl_sock, SOL_SOCKET, SO_RCVBUF, &rbuf_size, &opt_len);
	iwpm_debug(IWARP_PM_NETLINK_DBG, "create_netlink_socket: Setting a sock option (rbuf_size = %u).\n", rbuf_size);

create_nl_socket_exit:
	return nl_sock;
}

/**
 * destroy_iwpm_socket - Close socket
 */
void destroy_iwpm_socket(int pm_sock)
{
	if (pm_sock > 0)
		close(pm_sock);
	pm_sock = -1;
}

/**
 * check_iwpm_nlattr - Check for NULL netlink attribute
 */
static int check_iwpm_nlattr(struct nlattr *nltb[], int nla_count)
{
	int i, ret = 0;
        for (i = 1; i < nla_count; i++) {
		if (!nltb[i]) {
			iwpm_debug(IWARP_PM_NETLINK_DBG, "check_iwpm_nlattr: NULL (attr idx = %d)\n", i);
			ret = -EINVAL;
		}
	}
	return ret;
}

/**
 * parse_iwpm_nlmsg - Parse a netlink message
 * @req_nlh: netlink header of the received message to parse
 * @policy_max: the number of attributes in the policy
 * @nlmsg_policy: the attribute policy
 * @nltb: array to store the parsed attributes
 * @msg_type: netlink message type (dbg purpose)
 */
int parse_iwpm_nlmsg(struct nlmsghdr *req_nlh, int policy_max,
			struct nla_policy *nlmsg_policy, struct nlattr *nltb [],
			const char *msg_type)
{
	const char *str_err;
	int ret;

	if ((ret = nlmsg_validate(req_nlh, 0, policy_max-1, nlmsg_policy))) {
		str_err = "nlmsg_validate error";
		goto parse_nlmsg_error;
	}
	if ((ret = nlmsg_parse(req_nlh, 0, nltb, policy_max-1, nlmsg_policy))) {
		str_err = "nlmsg_parse error";
		goto parse_nlmsg_error;
	}
	if (check_iwpm_nlattr(nltb, policy_max)) {
		ret = -EINVAL;
		str_err = "NULL nlmsg attribute";
		goto parse_nlmsg_error;
	}
	return 0;
parse_nlmsg_error:
	syslog(LOG_WARNING, "parse_iwpm_nlmsg: msg type = %s (%s ret = %d)\n",
			msg_type, str_err, ret);
	return ret;
}

/**
 * send_iwpm_nlmsg - Send a netlink message
 * @nl_sock:  netlink socket to use for sending the message
 * @nlmsg:    netlink message to send
 * @dest_pid: pid of the destination of the nlmsg
 */
int send_iwpm_nlmsg(int nl_sock, struct nl_msg *nlmsg, int dest_pid)
{
	struct sockaddr_nl dest_addr;
	struct nlmsghdr *nlh = nlmsg_hdr(nlmsg);
	__u32 nlmsg_len = nlh->nlmsg_len;
	int len;

	/* fill in the netlink address of the client */
	memset(&dest_addr, 0, sizeof(dest_addr));
	dest_addr.nl_groups = 0;
	dest_addr.nl_family = AF_NETLINK;
	dest_addr.nl_pid = dest_pid;

	/* send response to the client */
	len = sendto(nl_sock, (char *)nlh, nlmsg_len, 0,
		     	(struct sockaddr *)&dest_addr, sizeof(dest_addr));
	if (len != nlmsg_len)
		return -errno;
	return 0;
}

/**
 * create_iwpm_nlmsg - Create a netlink message
 * @nlmsg_type: type of the netlink message
 * @client:     the port mapper client to receive the message
 */
struct nl_msg *create_iwpm_nlmsg(__u16 nlmsg_type, int client_idx)
{
	struct nl_msg *nlmsg;
	struct nlmsghdr *nlh;
	__u32 seq = 0;

	nlmsg = nlmsg_alloc();
	if (!nlmsg)
		return NULL;
	if (client_idx > 0)
		seq = client_list[client_idx].nl_seq++;

	nlh = nlmsg_put(nlmsg, getpid(), seq, nlmsg_type, 0, NLM_F_REQUEST);
	if (!nlh) {
		nlmsg_free(nlmsg);
		return NULL;
	}
	return nlmsg;
}

/**
 * parse_iwpm_msg - Parse iwarp port mapper wire message
 * @pm_msg: iwpm message to be parsed
 * @msg_parms: contains the parameters of the iwpm message after parsing
 */
int parse_iwpm_msg(iwpm_wire_msg *pm_msg, iwpm_msg_parms *msg_parms)
{
	int ret_value = 0;

	msg_parms->pmtime = pm_msg->pmtime;
	msg_parms->assochandle = be64toh(pm_msg->assochandle);
	msg_parms->ip_ver = (pm_msg->magic & IWARP_PM_IPVER_MASK) >> IWARP_PM_IPVER_SHIFT;
	switch (msg_parms->ip_ver) {
	case 4:
		msg_parms->address_family = AF_INET;
		break;
	case 6:
		msg_parms->address_family = AF_INET6;
		break;
	default:
		syslog(LOG_WARNING, "parse_iwpm_msg: Invalid IP version = %d.\n",
			msg_parms->ip_ver);
		return -EINVAL;
	}
	/* port mapper protocol version */
	msg_parms->ver = (pm_msg->magic & IWARP_PM_VER_MASK) >> IWARP_PM_VER_SHIFT;
	/* message type */
	msg_parms->mt = (pm_msg->magic & IWARP_PM_MT_MASK) >> IWARP_PM_MT_SHIFT;
	msg_parms->apport = pm_msg->apport; /* accepting peer port */
	msg_parms->cpport = pm_msg->cpport; /* connecting peer port */
	/* copy accepting peer IP address */
	memcpy(&msg_parms->apipaddr, &pm_msg->apipaddr, IWPM_IPADDR_SIZE);
	/* copy connecting peer IP address */
	memcpy(&msg_parms->cpipaddr, &pm_msg->cpipaddr, IWPM_IPADDR_SIZE);
	if (msg_parms->mt == IWARP_PM_MT_REQ) {
		msg_parms->mapped_cpport = pm_msg->reserved;
		memcpy(&msg_parms->mapped_cpipaddr, &pm_msg->mapped_cpipaddr, IWPM_IPADDR_SIZE);
	}
	return ret_value;
}

/**
 * form_iwpm_msg - Form iwarp port mapper wire message
 * @pm_msg: iwpm message to be formed
 * @msg_parms: the parameters to be packed in a iwpm message
 */
static void form_iwpm_msg(iwpm_wire_msg *pm_msg, iwpm_msg_parms *msg_parms)
{
	memset(pm_msg, 0, sizeof(struct iwpm_wire_msg));
	pm_msg->pmtime = msg_parms->pmtime;
	pm_msg->assochandle = htobe64(msg_parms->assochandle);
	/* record IP version, port mapper version, message type */
	pm_msg->magic = (msg_parms->ip_ver << IWARP_PM_IPVER_SHIFT) & IWARP_PM_IPVER_MASK;
	pm_msg->magic |= (msg_parms->ver << IWARP_PM_VER_SHIFT) & IWARP_PM_VER_MASK;
	pm_msg->magic |= (msg_parms->mt << IWARP_PM_MT_SHIFT) & IWARP_PM_MT_MASK;

	pm_msg->apport = msg_parms->apport;
	pm_msg->cpport = msg_parms->cpport;
	memcpy(&pm_msg->apipaddr, &msg_parms->apipaddr, IWPM_IPADDR_SIZE);
	memcpy(&pm_msg->cpipaddr, &msg_parms->cpipaddr, IWPM_IPADDR_SIZE);
	if (msg_parms->mt == IWARP_PM_MT_REQ) {
		pm_msg->reserved = msg_parms->mapped_cpport;
		memcpy(&pm_msg->mapped_cpipaddr, &msg_parms->mapped_cpipaddr, IWPM_IPADDR_SIZE);
	}
}

/**
 * form_iwpm_request - Form iwarp port mapper request message
 * @pm_msg: iwpm message to be formed
 * @msg_parms: the parameters to be packed in a iwpm message
 **/
void form_iwpm_request(struct iwpm_wire_msg *pm_msg,
		      struct iwpm_msg_parms  *msg_parms)
{
	msg_parms->mt = IWARP_PM_MT_REQ;
	msg_parms->msize = IWARP_PM_MESSAGE_SIZE + IWPM_IPADDR_SIZE;
	form_iwpm_msg(pm_msg, msg_parms);
}

/**
 * form_iwpm_accept - Form iwarp port mapper accept message
 * @pm_msg: iwpm message to be formed
 * @msg_parms: the parameters to be packed in a iwpm message
 **/
void form_iwpm_accept(struct iwpm_wire_msg *pm_msg,
		     struct iwpm_msg_parms  *msg_parms)
{
	msg_parms->mt = IWARP_PM_MT_ACC;
	msg_parms->msize = IWARP_PM_MESSAGE_SIZE;
	form_iwpm_msg(pm_msg, msg_parms);
}

/**
 * form_iwpm_ack - Form iwarp port mapper ack message
 * @pm_msg: iwpm message to be formed
 * @msg_parms: the parameters to be packed in a iwpm message
 **/
void form_iwpm_ack(struct iwpm_wire_msg *pm_msg,
		  struct iwpm_msg_parms  *msg_parms)
{
	msg_parms->mt = IWARP_PM_MT_ACK;
	msg_parms->msize = IWARP_PM_MESSAGE_SIZE;
	form_iwpm_msg(pm_msg, msg_parms);
}

/**
 * form_iwpm_reject - Form iwarp port mapper reject message
 * @pm_msg: iwpm message to be formed
 * @msg_parms: the parameters to be packed in a iwpm message
 */
void form_iwpm_reject(struct iwpm_wire_msg *pm_msg,
		     struct iwpm_msg_parms  *msg_parms)
{
	msg_parms->mt = IWARP_PM_MT_REJ;
	msg_parms->msize = IWARP_PM_MESSAGE_SIZE;
	form_iwpm_msg(pm_msg, msg_parms);
}

/**
 * get_sockaddr_port - Report the tcp port number, contained in the sockaddr
 * @sockaddr: sockaddr storage to get the tcp port from
 */
__be16 get_sockaddr_port(struct sockaddr_storage *sockaddr)
{
	struct sockaddr_in *sockaddr_v4;
	struct sockaddr_in6 *sockaddr_v6;
	__be16 port = 0;

	switch (sockaddr->ss_family) {
	case AF_INET:
		sockaddr_v4 = (struct sockaddr_in *)sockaddr;
		port = sockaddr_v4->sin_port;
		break;
	case AF_INET6:
		sockaddr_v6 = (struct sockaddr_in6 *)sockaddr;
		port = sockaddr_v6->sin6_port;
		break;
	default:
		syslog(LOG_WARNING, "get_sockaddr_port: Invalid sockaddr family.\n");
		break;
	}
	return port;
}

/**
 * copy_iwpm_sockaddr - Copy (IP address and Port) from src to dst
 * @address_family: Internet address family
 * @src_sockaddr: socket address to copy (if NULL, use src_addr)
 * @dst_sockaddr: socket address to update (if NULL, use dst_addr)
 * @src_addr: IP address to copy (if NULL, use src_sockaddr)
 * @dst_addr: IP address to update (if NULL, use dst_sockaddr)
 * @src_port: port to copy in dst_sockaddr, if src_sockaddr = NULL
 *            port to update, if src_sockaddr != NULL and dst_sockaddr = NULL
 */
void copy_iwpm_sockaddr(__u16 addr_family, struct sockaddr_storage *src_sockaddr,
		      struct sockaddr_storage *dst_sockaddr,
		      char *src_addr, char *dst_addr, __be16 *src_port)
{
	char *src = NULL, *dst = NULL;

	if (src_addr)
		src = src_addr;
	if (dst_addr)
		dst = dst_addr;

	switch (addr_family) {
	case AF_INET:
		if (src_sockaddr) {
			src = (char *)&((struct sockaddr_in *)src_sockaddr)->sin_addr.s_addr;
			*src_port = ((struct sockaddr_in *)src_sockaddr)->sin_port;
		}
		if (dst_sockaddr) {
			dst = (char *)&(((struct sockaddr_in *)dst_sockaddr)->sin_addr.s_addr);
			((struct sockaddr_in *)dst_sockaddr)->sin_port = *src_port;
			((struct sockaddr_in *)dst_sockaddr)->sin_family = AF_INET;
		}
		break;
	case AF_INET6:
		if (src_sockaddr) {
			src = (char *)&((struct sockaddr_in6 *)src_sockaddr)->sin6_addr.s6_addr;
			*src_port = ((struct sockaddr_in6 *)src_sockaddr)->sin6_port;
		}
		if (dst_sockaddr) {
			dst = (char *)&(((struct sockaddr_in6 *)dst_sockaddr)->sin6_addr.s6_addr);
			((struct sockaddr_in6 *)dst_sockaddr)->sin6_port = *src_port;
			((struct sockaddr_in6 *)dst_sockaddr)->sin6_family = AF_INET6;
		}
		break;
	default:
		return;
	}

	memcpy(dst, src, IWPM_IPADDR_SIZE);
}

/**
 * is_wcard_ipaddr - Check if the search_addr has a wild card ip address
 */
int is_wcard_ipaddr(struct sockaddr_storage *search_addr)
{
	int ret = 0;

	switch (search_addr->ss_family) {
	case AF_INET: {
		struct sockaddr_in wcard_addr;
		struct sockaddr_in *in4addr = (struct sockaddr_in *)search_addr;
		inet_pton(AF_INET, "0.0.0.0", &wcard_addr.sin_addr);

		if (in4addr->sin_addr.s_addr == wcard_addr.sin_addr.s_addr)
			ret = 1;
		break;
	}
	case AF_INET6: {
		struct sockaddr_in6 wcard_addr;
		struct sockaddr_in6 *in6addr = (struct sockaddr_in6 *)search_addr;
		inet_pton(AF_INET6, "::", &wcard_addr.sin6_addr);

		if (!memcmp(in6addr->sin6_addr.s6_addr,
			wcard_addr.sin6_addr.s6_addr, IWPM_IPADDR_SIZE))
			ret = 1;
		break;
	}
	default:
		syslog(LOG_WARNING, "check_same_sockaddr: Invalid addr family 0x%02X\n",
			search_addr->ss_family);
		break;
	}
	return ret;
}

/**
 * print_iwpm_sockaddr - Print socket address (IP address and Port)
 * @sockaddr: socket address to print
 * @msg: message to print
 */
void print_iwpm_sockaddr(struct sockaddr_storage *sockaddr, const char *msg,
			 __u32 dbg_flag)
{
	struct sockaddr_in6 *sockaddr_v6;
	struct sockaddr_in *sockaddr_v4;
	char ip_address_text[INET6_ADDRSTRLEN];

	switch (sockaddr->ss_family) {
	case AF_INET:
		sockaddr_v4 = (struct sockaddr_in *)sockaddr;
		iwpm_debug(dbg_flag, "%s IPV4 %s:%u(0x%04X)\n", msg,
			inet_ntop(AF_INET, &sockaddr_v4->sin_addr, ip_address_text, INET6_ADDRSTRLEN),
			be16toh(sockaddr_v4->sin_port), be16toh(sockaddr_v4->sin_port));
		break;
	case AF_INET6:
		sockaddr_v6 = (struct sockaddr_in6 *)sockaddr;
		iwpm_debug(dbg_flag, "%s IPV6 %s:%u(0x%04X)\n", msg,
			inet_ntop(AF_INET6, &sockaddr_v6->sin6_addr, ip_address_text, INET6_ADDRSTRLEN),
			be16toh(sockaddr_v6->sin6_port), be16toh(sockaddr_v6->sin6_port));
		break;
	default:
		break;
	}
}
