zephyr/subsys/net/ip/utils.c

913 lines
16 KiB
C
Raw Permalink Normal View History

/** @file
* @brief Misc network utility functions
*
*/
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(net_utils, CONFIG_NET_UTILS_LOG_LEVEL);
#include <kernel.h>
#include <stdlib.h>
#include <syscall_handler.h>
#include <zephyr/types.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <net/net_ip.h>
#include <net/net_pkt.h>
#include <net/net_core.h>
#include <net/socket_can.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_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)
{
int i, val;
for (i = 0, val = (byte & 0xf0) >> 4; i < 2; i++, val = byte & 0x0f) {
if (i == 0 && !pad && !val) {
continue;
}
if (val < 10) {
*ptr++ = (char) (val + '0');
} else {
*ptr++ = (char) (val - 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;
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;
struct in6_addr *addr6;
uint16_t *w;
uint8_t i, bl, bh, longest = 1U;
int8_t pos = -1;
char delim = ':';
unsigned char zeros[8] = { 0 };
char *ptr = dst;
int len = -1;
uint16_t value;
bool needcolon = false;
if (family == AF_INET6) {
addr6 = (struct in6_addr *)src;
w = (uint16_t *)addr6->s6_addr16;
len = 8;
for (i = 0U; i < 8; i++) {
uint8_t j;
for (j = i; j < 8; j++) {
if (UNALIGNED_GET(&w[j]) != 0) {
break;
}
zeros[i]++;
}
}
for (i = 0U; 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;
}
for (i = 0U; i < len; i++) {
/* IPv4 address a.b.c.d */
if (len == 4) {
uint8_t l;
value = (uint32_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;
}
/* IPv6 address */
if (i == pos) {
if (needcolon || i == 0U) {
*ptr++ = ':';
}
*ptr++ = ':';
needcolon = false;
i += longest - 1U;
continue;
}
if (needcolon) {
*ptr++ = ':';
needcolon = false;
}
value = (uint32_t)sys_be16_to_cpu(UNALIGNED_GET(&w[i]));
bh = value >> 8;
bl = value & 0xff;
if (bh) {
if (bh > 0x0f) {
ptr = net_byte_to_hex(ptr, bh, 'a', false);
} else {
if (bh < 10) {
*ptr++ = (char)(bh + '0');
} else {
*ptr++ = (char) (bh - 10 + 'a');
}
}
ptr = net_byte_to_hex(ptr, bl, 'a', true);
} else if (bl > 0x0f) {
ptr = net_byte_to_hex(ptr, bl, 'a', false);
} else {
if (bl < 10) {
*ptr++ = (char)(bl + '0');
} else {
*ptr++ = (char) (bl - 10 + 'a');
}
}
needcolon = true;
}
if (!(ptr - dst)) {
return NULL;
}
if (family == AF_INET) {
*(ptr - 1) = '\0';
} else {
*ptr = '\0';
}
return dst;
}
#if defined(CONFIG_USERSPACE)
userspace: Support for split 64 bit arguments System call arguments, at the arch layer, are single words. So passing wider values requires splitting them into two registers at call time. This gets even more complicated for values (e.g k_timeout_t) that may have different sizes depending on configuration. This patch adds a feature to gen_syscalls.py to detect functions with wide arguments and automatically generates code to split/unsplit them. Unfortunately the current scheme of Z_SYSCALL_DECLARE_* macros won't work with functions like this, because for N arguments (our current maximum N is 10) there are 2^N possible configurations of argument widths. So this generates the complete functions for each handler and wrapper, effectively doing in python what was originally done in the preprocessor. Another complexity is that traditional the z_hdlr_*() function for a system call has taken the raw list of word arguments, which does not work when some of those arguments must be 64 bit types. So instead of using a single Z_SYSCALL_HANDLER macro, this splits the job of z_hdlr_*() into two steps: An automatically-generated unmarshalling function, z_mrsh_*(), which then calls a user-supplied verification function z_vrfy_*(). The verification function is typesafe, and is a simple C function with exactly the same argument and return signature as the syscall impl function. It is also not responsible for validating the pointers to the extra parameter array or a wide return value, that code gets automatically generated. This commit includes new vrfy/msrh handling for all syscalls invoked during CI runs. Future commits will port the less testable code. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2019-08-06 22:34:31 +02:00
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;
Z_OOPS(Z_SYSCALL_MEMORY_WRITE(dst, size));
if (family == AF_INET) {
Z_OOPS(z_user_from_copy(&addr4, (const void *)src,
sizeof(addr4)));
addr = &addr4;
} else if (family == AF_INET6) {
Z_OOPS(z_user_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;
}
Z_OOPS(z_user_to_copy((void *)dst, str, MIN(size, sizeof(str))));
userspace: Support for split 64 bit arguments System call arguments, at the arch layer, are single words. So passing wider values requires splitting them into two registers at call time. This gets even more complicated for values (e.g k_timeout_t) that may have different sizes depending on configuration. This patch adds a feature to gen_syscalls.py to detect functions with wide arguments and automatically generates code to split/unsplit them. Unfortunately the current scheme of Z_SYSCALL_DECLARE_* macros won't work with functions like this, because for N arguments (our current maximum N is 10) there are 2^N possible configurations of argument widths. So this generates the complete functions for each handler and wrapper, effectively doing in python what was originally done in the preprocessor. Another complexity is that traditional the z_hdlr_*() function for a system call has taken the raw list of word arguments, which does not work when some of those arguments must be 64 bit types. So instead of using a single Z_SYSCALL_HANDLER macro, this splits the job of z_hdlr_*() into two steps: An automatically-generated unmarshalling function, z_mrsh_*(), which then calls a user-supplied verification function z_vrfy_*(). The verification function is typesafe, and is a simple C function with exactly the same argument and return signature as the syscall impl function. It is also not responsible for validating the pointers to the extra parameter array or a wide return value, that code gets automatically generated. This commit includes new vrfy/msrh handling for all syscalls invoked during CI runs. Future commits will port the less testable code. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2019-08-06 22:34:31 +02:00
return dst;
}
userspace: Support for split 64 bit arguments System call arguments, at the arch layer, are single words. So passing wider values requires splitting them into two registers at call time. This gets even more complicated for values (e.g k_timeout_t) that may have different sizes depending on configuration. This patch adds a feature to gen_syscalls.py to detect functions with wide arguments and automatically generates code to split/unsplit them. Unfortunately the current scheme of Z_SYSCALL_DECLARE_* macros won't work with functions like this, because for N arguments (our current maximum N is 10) there are 2^N possible configurations of argument widths. So this generates the complete functions for each handler and wrapper, effectively doing in python what was originally done in the preprocessor. Another complexity is that traditional the z_hdlr_*() function for a system call has taken the raw list of word arguments, which does not work when some of those arguments must be 64 bit types. So instead of using a single Z_SYSCALL_HANDLER macro, this splits the job of z_hdlr_*() into two steps: An automatically-generated unmarshalling function, z_mrsh_*(), which then calls a user-supplied verification function z_vrfy_*(). The verification function is typesafe, and is a simple C function with exactly the same argument and return signature as the syscall impl function. It is also not responsible for validating the pointers to the extra parameter array or a wide return value, that code gets automatically generated. This commit includes new vrfy/msrh handling for all syscalls invoked during CI runs. Future commits will port the less testable code. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2019-08-06 22:34:31 +02:00
#include <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;
size_t i, len;
len = strlen(src);
for (i = 0; i < len; i++) {
if (!(src[i] >= '0' && src[i] <= '9') &&
src[i] != '.') {
return -EINVAL;
}
}
(void)memset(addr, 0, sizeof(struct in_addr));
for (i = 0; i < sizeof(struct in_addr); i++) {
char *endptr;
addr->s4_addr[i] = strtol(src, &endptr, 10);
src = ++endptr;
}
} 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)
userspace: Support for split 64 bit arguments System call arguments, at the arch layer, are single words. So passing wider values requires splitting them into two registers at call time. This gets even more complicated for values (e.g k_timeout_t) that may have different sizes depending on configuration. This patch adds a feature to gen_syscalls.py to detect functions with wide arguments and automatically generates code to split/unsplit them. Unfortunately the current scheme of Z_SYSCALL_DECLARE_* macros won't work with functions like this, because for N arguments (our current maximum N is 10) there are 2^N possible configurations of argument widths. So this generates the complete functions for each handler and wrapper, effectively doing in python what was originally done in the preprocessor. Another complexity is that traditional the z_hdlr_*() function for a system call has taken the raw list of word arguments, which does not work when some of those arguments must be 64 bit types. So instead of using a single Z_SYSCALL_HANDLER macro, this splits the job of z_hdlr_*() into two steps: An automatically-generated unmarshalling function, z_mrsh_*(), which then calls a user-supplied verification function z_vrfy_*(). The verification function is typesafe, and is a simple C function with exactly the same argument and return signature as the syscall impl function. It is also not responsible for validating the pointers to the extra parameter array or a wide return value, that code gets automatically generated. This commit includes new vrfy/msrh handling for all syscalls invoked during CI runs. Future commits will port the less testable code. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2019-08-06 22:34:31 +02:00
int z_vrfy_net_addr_pton(sa_family_t family, const char *src,
void *dst)
{
char str[INET6_ADDRSTRLEN];
struct in6_addr addr6;
struct in_addr addr4;
void *addr;
size_t size;
size_t nlen;
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;
}
memset(str, 0, sizeof(str));
nlen = z_user_string_nlen((const char *)src, sizeof(str), &err);
if (err) {
return -EINVAL;
}
Z_OOPS(Z_SYSCALL_MEMORY_WRITE(dst, size));
Z_OOPS(Z_SYSCALL_MEMORY_READ(src, nlen));
Z_OOPS(z_user_from_copy(str, (const void *)src, nlen));
err = z_impl_net_addr_pton(family, str, addr);
if (err) {
return err;
}
Z_OOPS(z_user_to_copy((void *)dst, addr, size));
return 0;
}
userspace: Support for split 64 bit arguments System call arguments, at the arch layer, are single words. So passing wider values requires splitting them into two registers at call time. This gets even more complicated for values (e.g k_timeout_t) that may have different sizes depending on configuration. This patch adds a feature to gen_syscalls.py to detect functions with wide arguments and automatically generates code to split/unsplit them. Unfortunately the current scheme of Z_SYSCALL_DECLARE_* macros won't work with functions like this, because for N arguments (our current maximum N is 10) there are 2^N possible configurations of argument widths. So this generates the complete functions for each handler and wrapper, effectively doing in python what was originally done in the preprocessor. Another complexity is that traditional the z_hdlr_*() function for a system call has taken the raw list of word arguments, which does not work when some of those arguments must be 64 bit types. So instead of using a single Z_SYSCALL_HANDLER macro, this splits the job of z_hdlr_*() into two steps: An automatically-generated unmarshalling function, z_mrsh_*(), which then calls a user-supplied verification function z_vrfy_*(). The verification function is typesafe, and is a simple C function with exactly the same argument and return signature as the syscall impl function. It is also not responsible for validating the pointers to the extra parameter array or a wide return value, that code gets automatically generated. This commit includes new vrfy/msrh handling for all syscalls invoked during CI runs. Future commits will port the less testable code. Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2019-08-06 22:34:31 +02:00
#include <syscalls/net_addr_pton_mrsh.c>
#endif /* CONFIG_USERSPACE */
static uint16_t calc_chksum(uint16_t sum, const uint8_t *data, size_t len)
{
const uint8_t *end;
uint16_t tmp;
end = data + len - 1;
while (data < end) {
tmp = (data[0] << 8) + data[1];
sum += tmp;
if (sum < tmp) {
sum++;
}
data += 2;
}
if (data == end) {
tmp = data[0] << 8;
sum += tmp;
if (sum < tmp) {
sum++;
}
}
return sum;
}
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) {
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;
}
#if defined(CONFIG_NET_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_IPV4 */
#if defined(CONFIG_NET_IPV6) || defined(CONFIG_NET_IPV4)
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_IPV6 || CONFIG_NET_IPV4 */
#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",
log_strdup(net_addr_ntop(AF_INET6, addr6,
ipaddr, sizeof(ipaddr) - 1)),
port);
} else {
NET_DBG("IPv6 host %s",
log_strdup(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);
ipaddr[str_len - end] = '\0';
ret = convert_port(ipaddr, &port);
if (!ret) {
return false;
}
net_sin(addr)->sin_port = htons(port);
NET_DBG("IPv4 host %s port %d",
log_strdup(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; str[i] && i < str_len; 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_bytes_from_str(uint8_t *buf, int buf_len, const char *src)
{
unsigned int i;
char *endptr;
for (i = 0U; i < strlen(src); i++) {
if (!(src[i] >= '0' && src[i] <= '9') &&
!(src[i] >= 'A' && src[i] <= 'F') &&
!(src[i] >= 'a' && src[i] <= 'f') &&
src[i] != ':') {
return -EINVAL;
}
}
(void)memset(buf, 0, buf_len);
for (i = 0U; i < buf_len; i++) {
buf[i] = 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;
}