/** @file
 * @brief Misc network utility functions
 *
 */

/*
 * Copyright (c) 2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(net_utils, CONFIG_NET_UTILS_LOG_LEVEL);

#include <zephyr/kernel.h>
#include <stdlib.h>
#include <zephyr/internal/syscall_handler.h>
#include <zephyr/types.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#include <zephyr/sys/byteorder.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/net/net_core.h>
#include <zephyr/net/socketcan.h>

char *net_sprint_addr(sa_family_t af, const void *addr)
{
#define NBUFS 3
	static char buf[NBUFS][NET_IPV6_ADDR_LEN];
	static int i;
	char *s = buf[++i % NBUFS];

	return net_addr_ntop(af, addr, s, NET_IPV6_ADDR_LEN);
}

const char *net_verdict2str(enum net_verdict verdict)
{
	if (verdict == NET_OK) {
		return "NET_OK";
	} else if (verdict == NET_CONTINUE) {
		return "NET_CONTINUE";
	} else if (verdict == NET_DROP) {
		return "NET_DROP";
	}

	return "<unknown>";
}

const char *net_proto2str(int family, int proto)
{
	if (family == AF_INET || family == AF_INET6) {
		switch (proto) {
		case IPPROTO_ICMP:
			return "ICMPv4";
		case IPPROTO_TCP:
			return "TCP";
		case IPPROTO_UDP:
			return "UDP";
		case IPPROTO_ICMPV6:
			return "ICMPv6";
		default:
			break;
		}
	} else if (family == AF_CAN) {
		switch (proto) {
		case CAN_RAW:
			return "CAN_RAW";
		default:
			break;
		}
	}

	return "UNK_PROTO";
}

char *net_byte_to_hex(char *ptr, uint8_t byte, char base, bool pad)
{
	uint8_t high = (byte >> 4) & 0x0f;
	uint8_t low = byte & 0x0f;

	if (pad || (high > 0)) {
		*ptr++ = (high < 10) ? (char) (high + '0') : (char) (high - 10 + base);
	}

	*ptr++ = (low < 10) ? (char) (low + '0') : (char) (low - 10 + base);

	*ptr = '\0';

	return ptr;
}

char *net_sprint_ll_addr_buf(const uint8_t *ll, uint8_t ll_len,
			     char *buf, int buflen)
{
	uint8_t i, len, blen;
	char *ptr = buf;

	if (ll == NULL) {
		return "<unknown>";
	}

	switch (ll_len) {
	case 8:
		len = 8U;
		break;
	case 6:
		len = 6U;
		break;
	case 2:
		len = 2U;
		break;
	default:
		len = 6U;
		break;
	}

	for (i = 0U, blen = buflen; i < len && blen > 0; i++) {
		ptr = net_byte_to_hex(ptr, (char)ll[i], 'A', true);
		*ptr++ = ':';
		blen -= 3U;
	}

	if (!(ptr - buf)) {
		return NULL;
	}

	*(ptr - 1) = '\0';
	return buf;
}

static int net_value_to_udec(char *buf, uint32_t value, int precision)
{
	uint32_t divisor;
	int i;
	int temp;
	char *start = buf;

	divisor = 1000000000U;
	if (precision < 0) {
		precision = 1;
	}

	for (i = 9; i >= 0; i--, divisor /= 10U) {
		temp = value / divisor;
		value = value % divisor;
		if ((precision > i) || (temp != 0)) {
			precision = i;
			*buf++ = (char) (temp + '0');
		}
	}
	*buf = 0;

	return buf - start;
}

char *z_impl_net_addr_ntop(sa_family_t family, const void *src,
			   char *dst, size_t size)
{
	struct in_addr *addr = NULL;
	struct in6_addr *addr6 = NULL;
	uint16_t *w = NULL;
	int i;
	uint8_t longest = 1U;
	int pos = -1;
	char delim = ':';
	uint8_t zeros[8] = { 0 };
	char *ptr = dst;
	int len = -1;
	uint16_t value;
	bool needcolon = false;
	bool mapped = false;

	if (family == AF_INET6) {
		addr6 = (struct in6_addr *)src;
		w = (uint16_t *)addr6->s6_addr16;
		len = 8;

		if (net_ipv6_addr_is_v4_mapped(addr6)) {
			mapped = true;
		}

		for (i = 0; i < 8; i++) {
			for (int j = i; j < 8; j++) {
				if (UNALIGNED_GET(&w[j]) != 0) {
					break;
				}

				zeros[i]++;
			}
		}

		for (i = 0; i < 8; i++) {
			if (zeros[i] > longest) {
				longest = zeros[i];
				pos = i;
			}
		}

		if (longest == 1U) {
			pos = -1;
		}

	} else if (family == AF_INET) {
		addr = (struct in_addr *)src;
		len = 4;
		delim = '.';
	} else {
		return NULL;
	}

print_mapped:
	for (i = 0; i < len; i++) {
		/* IPv4 address a.b.c.d */
		if (len == 4) {
			uint8_t l;

			value = (uint16_t)addr->s4_addr[i];

			/* net_byte_to_udec() eats 0 */
			if (value == 0U) {
				*ptr++ = '0';
				*ptr++ = delim;
				continue;
			}

			l = net_value_to_udec(ptr, value, 0);

			ptr += l;
			*ptr++ = delim;

			continue;
		}

		if (mapped && (i > 5)) {
			delim = '.';
			len = 4;
			addr = (struct in_addr *)(&addr6->s6_addr32[3]);
			*ptr++ = ':';
			family = AF_INET;
			goto print_mapped;
		}

		/* IPv6 address */
		if (i == pos) {
			if (needcolon || i == 0U) {
				*ptr++ = ':';
			}

			*ptr++ = ':';
			needcolon = false;
			i += (int)longest - 1;

			continue;
		}

		if (needcolon) {
			*ptr++ = ':';
		}

		value = sys_be16_to_cpu(UNALIGNED_GET(&w[i]));
		uint8_t bh = value >> 8;
		uint8_t bl = value & 0xff;

		if (bh) {
			/* Convert high byte to hex without padding */
			ptr = net_byte_to_hex(ptr, bh, 'a', false);

			/* Always pad the low byte, since high byte is non - zero */
			ptr = net_byte_to_hex(ptr, bl, 'a', true);
		} else {
			/* For the case where the high byte is zero, only process the low byte
			 * Do not pad the low byte, since high byte is zero
			 */
			ptr = net_byte_to_hex(ptr, bl, 'a', false);
		}

		needcolon = true;
	}

	if (!(ptr - dst)) {
		return NULL;
	}

	if (family == AF_INET) {
		*(ptr - 1) = '\0';
	} else {
		*ptr = '\0';
	}

	return dst;
}

#if defined(CONFIG_USERSPACE)
char *z_vrfy_net_addr_ntop(sa_family_t family, const void *src,
			   char *dst, size_t size)
{
	char str[INET6_ADDRSTRLEN];
	struct in6_addr addr6;
	struct in_addr addr4;
	char *out;
	const void *addr;

	K_OOPS(K_SYSCALL_MEMORY_WRITE(dst, size));

	if (family == AF_INET) {
		K_OOPS(k_usermode_from_copy(&addr4, (const void *)src,
					sizeof(addr4)));
		addr = &addr4;
	} else if (family == AF_INET6) {
		K_OOPS(k_usermode_from_copy(&addr6, (const void *)src,
					sizeof(addr6)));
		addr = &addr6;
	} else {
		return 0;
	}

	out = z_impl_net_addr_ntop(family, addr, str, sizeof(str));
	if (!out) {
		return 0;
	}

	K_OOPS(k_usermode_to_copy((void *)dst, str, MIN(size, sizeof(str))));

	return dst;
}
#include <zephyr/syscalls/net_addr_ntop_mrsh.c>
#endif /* CONFIG_USERSPACE */

int z_impl_net_addr_pton(sa_family_t family, const char *src,
			 void *dst)
{
	if (family == AF_INET) {
		struct in_addr *addr = (struct in_addr *)dst;
		uint8_t index = 0, digits = 0;
		uint16_t value = 0, count = 0;

		(void)memset(addr, 0, sizeof(struct in_addr));

		/* A valid IPv4 address that can be used with inet_pton
		 * must be in the standard dotted-decimal notation:
		 *
		 *    - Four octets, each ranging from 0 to 255
		 *    - Separated by dots (.)
		 *    - No leading zeros in each octet
		 */

		while (index < sizeof(struct in_addr)) {
			if (*src == '\0' || *src == '.') {
				if (*src == '.') {
					count++;
				}

				if ((digits > 1 && value < 10) ||
				    (digits > 2 && value < 100)) {
					/* Preceding zeroes */
					return -EINVAL;
				}

				if (digits == 0 || value > UINT8_MAX) {
					return -EINVAL;
				}

				addr->s4_addr[index] = value;

				if (*src == '\0') {
					break;
				}

				index++;
				digits = 0;
				value = 0;
			} else if ('0' <= *src && *src <= '9') {
				if (++digits > 3) {
					/* Number too large */
					return -EINVAL;
				}

				value *= 10;
				value += *src - '0';
			} else {
				/* Invalid character */
				return -EINVAL;
			}

			src++;
		}

		if (count != 3) {
			/* Three dots needed */
			return -EINVAL;
		}

	} else if (family == AF_INET6) {
		/* If the string contains a '.', it means it's of the form
		 * X:X:X:X:X:X:x.x.x.x, and contains only 6 16-bit pieces
		 */
		int expected_groups = strchr(src, '.') ? 6 : 8;
		struct in6_addr *addr = (struct in6_addr *)dst;
		int i, len;

		if (*src == ':') {
			/* Ignore a leading colon, makes parsing neater */
			src++;
		}

		len = strlen(src);
		for (i = 0; i < len; i++) {
			if (!(src[i] >= '0' && src[i] <= '9') &&
			    !(src[i] >= 'A' && src[i] <= 'F') &&
			    !(src[i] >= 'a' && src[i] <= 'f') &&
			    src[i] != '.' && src[i] != ':') {
				return -EINVAL;
			}
		}

		for (i = 0; i < expected_groups; i++) {
			char *tmp;

			if (!src || *src == '\0') {
				return -EINVAL;
			}

			if (*src != ':') {
				/* Normal IPv6 16-bit piece */
				UNALIGNED_PUT(htons(strtol(src, NULL, 16)),
					      &addr->s6_addr16[i]);
				src = strchr(src, ':');
				if (src) {
					src++;
				} else {
					if (i < expected_groups - 1) {
						return -EINVAL;
					}
				}

				continue;
			}

			/* Two colons in a row */

			for (; i < expected_groups; i++) {
				UNALIGNED_PUT(0, &addr->s6_addr16[i]);
			}

			tmp = strrchr(src, ':');
			if (src == tmp && (expected_groups == 6 || !src[1])) {
				src++;
				break;
			}

			if (expected_groups == 6) {
				/* we need to drop the trailing
				 * colon since it's between the
				 * ipv6 and ipv4 addresses, rather than being
				 * a part of the ipv6 address
				 */
				tmp--;
			}

			/* Calculate the amount of skipped zeros */
			i = expected_groups - 1;
			do {
				if (*tmp == ':') {
					i--;
				}

				if (i < 0) {
					return -EINVAL;
				}
			} while (tmp-- != src);

			src++;
		}

		if (expected_groups == 6) {
			/* Parse the IPv4 part */
			for (i = 0; i < 4; i++) {
				if (!src || !*src) {
					return -EINVAL;
				}

				addr->s6_addr[12 + i] = strtol(src, NULL, 10);

				src = strchr(src, '.');
				if (src) {
					src++;
				} else {
					if (i < 3) {
						return -EINVAL;
					}
				}
			}
		}
	} else {
		return -EINVAL;
	}

	return 0;
}

#if defined(CONFIG_USERSPACE)
int z_vrfy_net_addr_pton(sa_family_t family, const char *src,
			 void *dst)
{
	char str[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)] = {};
	struct in6_addr addr6;
	struct in_addr addr4;
	void *addr;
	size_t size;
	int err;

	if (family == AF_INET) {
		size = sizeof(struct in_addr);
		addr = &addr4;
	} else if (family == AF_INET6) {
		size = sizeof(struct in6_addr);
		addr = &addr6;
	} else {
		return -EINVAL;
	}

	if (k_usermode_string_copy(str, (char *)src, sizeof(str)) != 0) {
		return -EINVAL;
	}

	K_OOPS(K_SYSCALL_MEMORY_WRITE(dst, size));

	err = z_impl_net_addr_pton(family, str, addr);
	if (err) {
		return err;
	}

	K_OOPS(k_usermode_to_copy((void *)dst, addr, size));

	return 0;
}
#include <zephyr/syscalls/net_addr_pton_mrsh.c>
#endif /* CONFIG_USERSPACE */


#ifdef CONFIG_LITTLE_ENDIAN
#define CHECKSUM_BIG_ENDIAN 0
#else
#define CHECKSUM_BIG_ENDIAN 1
#endif

static uint16_t offset_based_swap8(const uint8_t *data)
{
	uint16_t data16 = (uint16_t)*data;

	if (((uintptr_t)(data) & 1) == CHECKSUM_BIG_ENDIAN) {
		return data16;
	} else {
		return data16 << 8;
	}
}

/* Word based checksum calculation based on:
 * https://blogs.igalia.com/dpino/2018/06/14/fast-checksum-computation/
 * It’s not necessary to add octets as 16-bit words. Due to the associative property of addition,
 * it is possible to do parallel addition using larger word sizes such as 32-bit or 64-bit words.
 * In those cases the variable that stores the accumulative sum has to be bigger too.
 * Once the sum is computed a final step folds the sum to a 16-bit word (adding carry if any).
 */
uint16_t calc_chksum(uint16_t sum_in, const uint8_t *data, size_t len)
{
	uint64_t sum;
	uint32_t *p;
	size_t i = 0;
	size_t pending = len;
	int odd_start = ((uintptr_t)data & 0x01);

	/* Sum in is in host endianness, working order endianness is both dependent on endianness
	 * and the offset of starting
	 */
	if (odd_start == CHECKSUM_BIG_ENDIAN) {
		sum = BSWAP_16(sum_in);
	} else {
		sum = sum_in;
	}

	/* Process up to 3 data elements up front, so the data is aligned further down the line */
	if ((((uintptr_t)data & 0x01) != 0) && (pending >= 1)) {
		sum += offset_based_swap8(data);
		data++;
		pending--;
	}
	if ((((uintptr_t)data & 0x02) != 0) && (pending >= sizeof(uint16_t))) {
		pending -= sizeof(uint16_t);
		sum = sum + *((uint16_t *)data);
		data += sizeof(uint16_t);
	}
	p = (uint32_t *)data;

	/* Do loop unrolling for the very large data sets */
	while (pending >= sizeof(uint32_t) * 4) {
		uint64_t sum_a = p[i];
		uint64_t sum_b = p[i + 1];

		pending -= sizeof(uint32_t) * 4;
		sum_a += p[i + 2];
		sum_b += p[i + 3];
		i += 4;
		sum += sum_a + sum_b;
	}
	while (pending >= sizeof(uint32_t)) {
		pending -= sizeof(uint32_t);
		sum = sum + p[i++];
	}
	data = (uint8_t *)(p + i);
	if (pending >= 2) {
		pending -= sizeof(uint16_t);
		sum = sum + *((uint16_t *)data);
		data += sizeof(uint16_t);
	}
	if (pending == 1) {
		sum += offset_based_swap8(data);
	}

	/* Fold sum into 16-bit word. */
	while (sum >> 16) {
		sum = (sum & 0xffff) + (sum >> 16);
	}

	/* Sum in is in host endianness, working order endianness is both dependent on endianness
	 * and the offset of starting
	 */
	if (odd_start == CHECKSUM_BIG_ENDIAN) {
		return BSWAP_16((uint16_t)sum);
	} else {
		return sum;
	}
}

#if defined(CONFIG_NET_NATIVE_IP)
static inline uint16_t pkt_calc_chksum(struct net_pkt *pkt, uint16_t sum)
{
	struct net_pkt_cursor *cur = &pkt->cursor;
	size_t len;

	if (!cur->buf || !cur->pos) {
		return sum;
	}

	len = cur->buf->len - (cur->pos - cur->buf->data);

	while (cur->buf) {
		sum = calc_chksum(sum, cur->pos, len);

		cur->buf = cur->buf->frags;
		if (!cur->buf || !cur->buf->len) {
			break;
		}

		cur->pos = cur->buf->data;

		if (len % 2) {
			sum += *cur->pos;
			if (sum < *cur->pos) {
				sum++;
			}

			cur->pos++;
			len = cur->buf->len - 1;
		} else {
			len = cur->buf->len;
		}
	}

	return sum;
}

uint16_t net_calc_chksum(struct net_pkt *pkt, uint8_t proto)
{
	size_t len = 0U;
	uint16_t sum = 0U;
	struct net_pkt_cursor backup;
	bool ow;

	if (IS_ENABLED(CONFIG_NET_IPV4) &&
	    net_pkt_family(pkt) == AF_INET) {
		if (proto != IPPROTO_ICMP && proto != IPPROTO_IGMP) {
			len = 2 * sizeof(struct in_addr);
			sum = net_pkt_get_len(pkt) -
				net_pkt_ip_hdr_len(pkt) -
				net_pkt_ipv4_opts_len(pkt) + proto;
		}
	} else if (IS_ENABLED(CONFIG_NET_IPV6) &&
		   net_pkt_family(pkt) == AF_INET6) {
		len = 2 * sizeof(struct in6_addr);
		sum =  net_pkt_get_len(pkt) -
			net_pkt_ip_hdr_len(pkt) -
			net_pkt_ipv6_ext_len(pkt) + proto;
	} else {
		NET_DBG("Unknown protocol family %d", net_pkt_family(pkt));
		return 0;
	}

	net_pkt_cursor_backup(pkt, &backup);
	net_pkt_cursor_init(pkt);

	ow = net_pkt_is_being_overwritten(pkt);
	net_pkt_set_overwrite(pkt, true);

	net_pkt_skip(pkt, net_pkt_ip_hdr_len(pkt) - len);

	sum = calc_chksum(sum, pkt->cursor.pos, len);
	net_pkt_skip(pkt, len + net_pkt_ip_opts_len(pkt));

	sum = pkt_calc_chksum(pkt, sum);

	sum = (sum == 0U) ? 0xffff : htons(sum);

	net_pkt_cursor_restore(pkt, &backup);

	net_pkt_set_overwrite(pkt, ow);

	return ~sum;
}
#endif

#if defined(CONFIG_NET_NATIVE_IPV4)
uint16_t net_calc_chksum_ipv4(struct net_pkt *pkt)
{
	uint16_t sum;

	sum = calc_chksum(0, pkt->buffer->data,
			  net_pkt_ip_hdr_len(pkt) +
			  net_pkt_ipv4_opts_len(pkt));

	sum = (sum == 0U) ? 0xffff : htons(sum);

	return ~sum;
}
#endif /* CONFIG_NET_NATIVE_IPV4 */

#if defined(CONFIG_NET_IPV4_IGMP)
uint16_t net_calc_chksum_igmp(struct net_pkt *pkt)
{
	return net_calc_chksum(pkt, IPPROTO_IGMP);
}
#endif /* CONFIG_NET_IPV4_IGMP */

#if defined(CONFIG_NET_IP)
static bool convert_port(const char *buf, uint16_t *port)
{
	unsigned long tmp;
	char *endptr;

	tmp = strtoul(buf, &endptr, 10);
	if ((endptr == buf && tmp == 0) ||
	    !(*buf != '\0' && *endptr == '\0') ||
	    ((unsigned long)(unsigned short)tmp != tmp)) {
		return false;
	}

	*port = tmp;

	return true;
}
#endif /* CONFIG_NET_IP */

#if defined(CONFIG_NET_IPV6)
static bool parse_ipv6(const char *str, size_t str_len,
		       struct sockaddr *addr, bool has_port)
{
	char *ptr = NULL;
	struct in6_addr *addr6;
	char ipaddr[INET6_ADDRSTRLEN + 1];
	int end, len, ret, i;
	uint16_t port;

	len = MIN(INET6_ADDRSTRLEN, str_len);

	for (i = 0; i < len; i++) {
		if (!str[i]) {
			len = i;
			break;
		}
	}

	if (has_port) {
		/* IPv6 address with port number */
		ptr = memchr(str, ']', len);
		if (!ptr) {
			return false;
		}

		end = MIN(len, ptr - (str + 1));
		memcpy(ipaddr, str + 1, end);
	} else {
		end = len;
		memcpy(ipaddr, str, end);
	}

	ipaddr[end] = '\0';

	addr6 = &net_sin6(addr)->sin6_addr;

	ret = net_addr_pton(AF_INET6, ipaddr, addr6);
	if (ret < 0) {
		return false;
	}

	net_sin6(addr)->sin6_family = AF_INET6;

	if (!has_port) {
		return true;
	}

	if ((ptr + 1) < (str + str_len) && *(ptr + 1) == ':') {
		/* -1 as end does not contain first [
		 * -2 as pointer is advanced by 2, skipping ]:
		 */
		len = str_len - end - 1 - 2;

		ptr += 2;

		for (i = 0; i < len; i++) {
			if (!ptr[i]) {
				len = i;
				break;
			}
		}

		/* Re-use the ipaddr buf for port conversion */
		memcpy(ipaddr, ptr, len);
		ipaddr[len] = '\0';

		ret = convert_port(ipaddr, &port);
		if (!ret) {
			return false;
		}

		net_sin6(addr)->sin6_port = htons(port);

		NET_DBG("IPv6 host %s port %d",
			net_addr_ntop(AF_INET6, addr6, ipaddr, sizeof(ipaddr) - 1),
			port);
	} else {
		NET_DBG("IPv6 host %s",
			net_addr_ntop(AF_INET6, addr6, ipaddr, sizeof(ipaddr) - 1));
	}

	return true;
}
#else
static inline bool parse_ipv6(const char *str, size_t str_len,
			      struct sockaddr *addr, bool has_port)
{
	return false;
}
#endif /* CONFIG_NET_IPV6 */

#if defined(CONFIG_NET_IPV4)
static bool parse_ipv4(const char *str, size_t str_len,
		       struct sockaddr *addr, bool has_port)
{
	char *ptr = NULL;
	char ipaddr[NET_IPV4_ADDR_LEN + 1];
	struct in_addr *addr4;
	int end, len, ret, i;
	uint16_t port;

	len = MIN(NET_IPV4_ADDR_LEN, str_len);

	for (i = 0; i < len; i++) {
		if (!str[i]) {
			len = i;
			break;
		}
	}

	if (has_port) {
		/* IPv4 address with port number */
		ptr = memchr(str, ':', len);
		if (!ptr) {
			return false;
		}

		end = MIN(len, ptr - str);
	} else {
		end = len;
	}

	memcpy(ipaddr, str, end);
	ipaddr[end] = '\0';

	addr4 = &net_sin(addr)->sin_addr;

	ret = net_addr_pton(AF_INET, ipaddr, addr4);
	if (ret < 0) {
		return false;
	}

	net_sin(addr)->sin_family = AF_INET;

	if (!has_port) {
		return true;
	}

	memcpy(ipaddr, ptr + 1, str_len - end - 1);
	ipaddr[str_len - end - 1] = '\0';

	ret = convert_port(ipaddr, &port);
	if (!ret) {
		return false;
	}

	net_sin(addr)->sin_port = htons(port);

	NET_DBG("IPv4 host %s port %d",
		net_addr_ntop(AF_INET, addr4, ipaddr, sizeof(ipaddr) - 1),
		port);
	return true;
}
#else
static inline bool parse_ipv4(const char *str, size_t str_len,
			      struct sockaddr *addr, bool has_port)
{
	return false;
}
#endif /* CONFIG_NET_IPV4 */

bool net_ipaddr_parse(const char *str, size_t str_len, struct sockaddr *addr)
{
	int i, count;

	if (!str || str_len == 0) {
		return false;
	}

	/* We cannot accept empty string here */
	if (*str == '\0') {
		return false;
	}

	if (*str == '[') {
		return parse_ipv6(str, str_len, addr, true);
	}

	for (count = i = 0; i < str_len && str[i]; i++) {
		if (str[i] == ':') {
			count++;
		}
	}

	if (count == 1) {
		return parse_ipv4(str, str_len, addr, true);
	}

#if defined(CONFIG_NET_IPV4) && defined(CONFIG_NET_IPV6)
	if (!parse_ipv4(str, str_len, addr, false)) {
		return parse_ipv6(str, str_len, addr, false);
	}

	return true;
#endif

#if defined(CONFIG_NET_IPV4) && !defined(CONFIG_NET_IPV6)
	return parse_ipv4(str, str_len, addr, false);
#endif

#if defined(CONFIG_NET_IPV6) && !defined(CONFIG_NET_IPV4)
	return parse_ipv6(str, str_len, addr, false);
#endif
	return false;
}

int net_port_set_default(struct sockaddr *addr, uint16_t default_port)
{
	if (IS_ENABLED(CONFIG_NET_IPV4) && addr->sa_family == AF_INET &&
	    net_sin(addr)->sin_port == 0) {
		net_sin(addr)->sin_port = htons(default_port);
	} else if (IS_ENABLED(CONFIG_NET_IPV6) && addr->sa_family == AF_INET6 &&
		   net_sin6(addr)->sin6_port == 0) {
		net_sin6(addr)->sin6_port = htons(default_port);
	} else if ((IS_ENABLED(CONFIG_NET_IPV4) && addr->sa_family == AF_INET) ||
		   (IS_ENABLED(CONFIG_NET_IPV6) && addr->sa_family == AF_INET6)) {
		; /* Port is already set */
	} else {
		LOG_ERR("Unknown address family");
		return -EINVAL;
	}

	return 0;
}

int net_bytes_from_str(uint8_t *buf, int buf_len, const char *src)
{
	size_t i;
	size_t src_len = strlen(src);
	char *endptr;

	for (i = 0U; i < src_len; i++) {
		if (!isxdigit((unsigned char)src[i]) &&
		    src[i] != ':') {
			return -EINVAL;
		}
	}

	(void)memset(buf, 0, buf_len);

	for (i = 0U; i < (size_t)buf_len; i++) {
		buf[i] = (uint8_t)strtol(src, &endptr, 16);
		src = ++endptr;
	}

	return 0;
}

const char *net_family2str(sa_family_t family)
{
	switch (family) {
	case AF_UNSPEC:
		return "AF_UNSPEC";
	case AF_INET:
		return "AF_INET";
	case AF_INET6:
		return "AF_INET6";
	case AF_PACKET:
		return "AF_PACKET";
	case AF_CAN:
		return "AF_CAN";
	}

	return NULL;
}

const struct in_addr *net_ipv4_unspecified_address(void)
{
	static const struct in_addr addr;

	return &addr;
}

const struct in_addr *net_ipv4_broadcast_address(void)
{
	static const struct in_addr addr = { { { 255, 255, 255, 255 } } };

	return &addr;
}

/* IPv6 wildcard and loopback address defined by RFC2553 */
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;

const struct in6_addr *net_ipv6_unspecified_address(void)
{
	return &in6addr_any;
}
