mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-05-09 05:58:01 +02:00
glib-aux: move inet related helpers to "nm-inet-utils.h"
This commit is contained in:
parent
f23e43b18a
commit
d65feb26e5
5 changed files with 897 additions and 882 deletions
|
|
@ -67,6 +67,7 @@
|
||||||
#include "libnm-glib-aux/nm-shared-utils.h"
|
#include "libnm-glib-aux/nm-shared-utils.h"
|
||||||
#include "libnm-glib-aux/nm-errno.h"
|
#include "libnm-glib-aux/nm-errno.h"
|
||||||
#include "libnm-glib-aux/nm-hash-utils.h"
|
#include "libnm-glib-aux/nm-hash-utils.h"
|
||||||
|
#include "libnm-glib-aux/nm-inet-utils.h"
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,529 @@
|
||||||
#include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
|
#include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
|
||||||
|
|
||||||
#include "nm-inet-utils.h"
|
#include "nm-inet-utils.h"
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
const NMIPAddr nm_ip_addr_zero = {};
|
||||||
|
|
||||||
|
/* We use _nm_alignas(NMIPAddr) to ensure that fields for in_addr_t and
|
||||||
|
* struct in6_addr have all the same alignment. Ensure that this is suitable. */
|
||||||
|
G_STATIC_ASSERT(_nm_alignof(in_addr_t) <= _nm_alignof(NMIPAddr));
|
||||||
|
G_STATIC_ASSERT(_nm_alignof(struct in_addr) <= _nm_alignof(NMIPAddr));
|
||||||
|
G_STATIC_ASSERT(_nm_alignof(struct in6_addr) <= _nm_alignof(NMIPAddr));
|
||||||
|
G_STATIC_ASSERT(_nm_alignof(NMEtherAddr) <= _nm_alignof(NMIPAddr));
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_ip_addr_cmp_for_sort(gconstpointer a, gconstpointer b, gpointer user_data)
|
||||||
|
{
|
||||||
|
/* This is a compare function that can be used for sorting IP addresses.
|
||||||
|
* Essentially, it calls memcmp(). @user_data must be GINT_TO_POINTER(addr_family).
|
||||||
|
* @a and @b must be either pointers to in_addr_t, struct in6_addr or NMIPAddr. */
|
||||||
|
return nm_ip_addr_cmp(GPOINTER_TO_INT(user_data), a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this initializes a struct in_addr/in6_addr and allows for untrusted
|
||||||
|
* arguments (like unsuitable @addr_family or @src_len). It's almost safe
|
||||||
|
* in the sense that it verifies input arguments strictly. Also, it
|
||||||
|
* uses memcpy() to access @src, so alignment is not an issue.
|
||||||
|
*
|
||||||
|
* Only potential pitfalls:
|
||||||
|
*
|
||||||
|
* - it allows for @addr_family to be AF_UNSPEC. If that is the case (and the
|
||||||
|
* caller allows for that), the caller MUST provide @out_addr_family.
|
||||||
|
* - when setting @dst to an IPv4 address, the trailing bytes are not touched.
|
||||||
|
* Meaning, if @dst is an NMIPAddr union, only the first bytes will be set.
|
||||||
|
* If that matter to you, clear @dst before. */
|
||||||
|
gboolean
|
||||||
|
nm_ip_addr_set_from_untrusted(int addr_family,
|
||||||
|
gpointer dst,
|
||||||
|
gconstpointer src,
|
||||||
|
gsize src_len,
|
||||||
|
int *out_addr_family)
|
||||||
|
{
|
||||||
|
nm_assert(dst);
|
||||||
|
|
||||||
|
switch (addr_family) {
|
||||||
|
case AF_UNSPEC:
|
||||||
|
if (!out_addr_family) {
|
||||||
|
/* when the callers allow undefined @addr_family, they must provide
|
||||||
|
* an @out_addr_family argument. */
|
||||||
|
nm_assert_not_reached();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
switch (src_len) {
|
||||||
|
case sizeof(struct in_addr):
|
||||||
|
addr_family = AF_INET;
|
||||||
|
break;
|
||||||
|
case sizeof(struct in6_addr):
|
||||||
|
addr_family = AF_INET6;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AF_INET:
|
||||||
|
if (src_len != sizeof(struct in_addr))
|
||||||
|
return FALSE;
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
if (src_len != sizeof(struct in6_addr))
|
||||||
|
return FALSE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* when the callers allow undefined @addr_family, they must provide
|
||||||
|
* an @out_addr_family argument. */
|
||||||
|
nm_assert(out_addr_family);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nm_assert(src);
|
||||||
|
|
||||||
|
memcpy(dst, src, src_len);
|
||||||
|
NM_SET_OUT(out_addr_family, addr_family);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_ip_addr_set_from_variant(int addr_family, gpointer dst, GVariant *variant, int *out_addr_family)
|
||||||
|
{
|
||||||
|
gconstpointer bytes;
|
||||||
|
gsize len;
|
||||||
|
|
||||||
|
g_return_val_if_fail(dst, FALSE);
|
||||||
|
g_return_val_if_fail(variant, FALSE);
|
||||||
|
|
||||||
|
/* This function always expects IP addressea a byte arrays ("ay"). Note that
|
||||||
|
* several NetworkManager API uses "u" (32 bit unsigned intergers) for IPv4 addresses.
|
||||||
|
* So this function won't work in those cases.
|
||||||
|
*
|
||||||
|
* Btw, using "u" for IPv4 address messes badly with the endianness (host
|
||||||
|
* vs network byte order). Don't do that.
|
||||||
|
*/
|
||||||
|
g_return_val_if_fail(g_variant_is_of_type(variant, G_VARIANT_TYPE("ay")), FALSE);
|
||||||
|
|
||||||
|
bytes = g_variant_get_fixed_array(variant, &len, sizeof(guint8));
|
||||||
|
|
||||||
|
return nm_ip_addr_set_from_untrusted(addr_family, dst, bytes, len, out_addr_family);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
guint32
|
||||||
|
_nm_utils_ip4_get_default_prefix0(in_addr_t ip)
|
||||||
|
{
|
||||||
|
/* The function is originally from ipcalc.c of Red Hat's initscripts. */
|
||||||
|
switch (ntohl(ip) >> 24) {
|
||||||
|
case 0 ... 127:
|
||||||
|
return 8; /* Class A */
|
||||||
|
case 128 ... 191:
|
||||||
|
return 16; /* Class B */
|
||||||
|
case 192 ... 223:
|
||||||
|
return 24; /* Class C */
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
guint32
|
||||||
|
_nm_utils_ip4_get_default_prefix(in_addr_t ip)
|
||||||
|
{
|
||||||
|
return _nm_utils_ip4_get_default_prefix0(ip) ?: 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_utils_ip_is_site_local(int addr_family, const void *address)
|
||||||
|
{
|
||||||
|
in_addr_t addr4;
|
||||||
|
|
||||||
|
switch (addr_family) {
|
||||||
|
case AF_INET:
|
||||||
|
/* RFC1918 private addresses
|
||||||
|
* 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 */
|
||||||
|
addr4 = ntohl(*((const in_addr_t *) address));
|
||||||
|
return (addr4 & 0xff000000) == 0x0a000000 || (addr4 & 0xfff00000) == 0xac100000
|
||||||
|
|| (addr4 & 0xffff0000) == 0xc0a80000;
|
||||||
|
case AF_INET6:
|
||||||
|
/* IN6_IS_ADDR_SITELOCAL() is for deprecated fec0::/10 addresses (see rfc3879, 4.).
|
||||||
|
* Note that for unique local IPv6 addresses (ULA, fc00::/7) this returns false,
|
||||||
|
* which may or may not be a bug. */
|
||||||
|
return IN6_IS_ADDR_SITELOCAL(address);
|
||||||
|
default:
|
||||||
|
g_return_val_if_reached(FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_utils_ip6_is_ula(const struct in6_addr *address)
|
||||||
|
{
|
||||||
|
/* Unique local IPv6 address (ULA) fc00::/7 */
|
||||||
|
return (address->s6_addr32[0] & htonl(0xfe000000u)) == htonl(0xfc000000u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
gconstpointer
|
||||||
|
nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint32 plen)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(dst, NULL);
|
||||||
|
|
||||||
|
switch (family) {
|
||||||
|
case AF_INET:
|
||||||
|
g_return_val_if_fail(plen <= 32, NULL);
|
||||||
|
|
||||||
|
if (!src) {
|
||||||
|
/* allow "self-assignment", by specifying %NULL as source. */
|
||||||
|
src = dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
*((guint32 *) dst) = nm_utils_ip4_address_clear_host_address(*((guint32 *) src), plen);
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
nm_utils_ip6_address_clear_host_address(dst, src, plen);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_return_val_if_reached(NULL);
|
||||||
|
}
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nm_utils_ip6_address_clear_host_address:
|
||||||
|
* @dst: destination output buffer, will contain the network part of the @src address
|
||||||
|
* @src: source ip6 address. If NULL, this does an in-place update of @dst.
|
||||||
|
* Also, @src and @dst are allowed to be the same pointers.
|
||||||
|
* @plen: prefix length of network
|
||||||
|
*
|
||||||
|
* Note: this function is self assignment safe, to update @src inplace, set both
|
||||||
|
* @dst and @src to the same destination or set @src NULL.
|
||||||
|
*/
|
||||||
|
const struct in6_addr *
|
||||||
|
nm_utils_ip6_address_clear_host_address(struct in6_addr *dst,
|
||||||
|
const struct in6_addr *src,
|
||||||
|
guint32 plen)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail(plen <= 128, NULL);
|
||||||
|
g_return_val_if_fail(dst, NULL);
|
||||||
|
|
||||||
|
if (!src)
|
||||||
|
src = dst;
|
||||||
|
|
||||||
|
if (plen < 128) {
|
||||||
|
guint nbytes = plen / 8;
|
||||||
|
guint nbits = plen % 8;
|
||||||
|
|
||||||
|
if (nbytes && dst != src)
|
||||||
|
memcpy(dst, src, nbytes);
|
||||||
|
if (nbits) {
|
||||||
|
dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits)));
|
||||||
|
nbytes++;
|
||||||
|
}
|
||||||
|
if (nbytes <= 15)
|
||||||
|
memset(&dst->s6_addr[nbytes], 0, 16 - nbytes);
|
||||||
|
} else if (src != dst)
|
||||||
|
*dst = *src;
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a,
|
||||||
|
const struct in6_addr *addr_b,
|
||||||
|
guint32 plen)
|
||||||
|
{
|
||||||
|
int nbytes;
|
||||||
|
guint8 va, vb, m;
|
||||||
|
|
||||||
|
if (plen >= 128) {
|
||||||
|
nm_assert(plen == 128);
|
||||||
|
NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, sizeof(struct in6_addr));
|
||||||
|
} else {
|
||||||
|
nbytes = plen / 8;
|
||||||
|
if (nbytes)
|
||||||
|
NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, nbytes);
|
||||||
|
|
||||||
|
plen = plen % 8;
|
||||||
|
if (plen != 0) {
|
||||||
|
m = ~((1 << (8 - plen)) - 1);
|
||||||
|
va = ((((const guint8 *) addr_a))[nbytes]) & m;
|
||||||
|
vb = ((((const guint8 *) addr_b))[nbytes]) & m;
|
||||||
|
NM_CMP_DIRECT(va, vb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error)
|
||||||
|
{
|
||||||
|
gs_free char *s_free = NULL;
|
||||||
|
struct in_addr a1;
|
||||||
|
guint8 bin[sizeof(a1)];
|
||||||
|
char *s;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (inet_aton(text, &a1) != 1) {
|
||||||
|
g_set_error_literal(error,
|
||||||
|
NM_UTILS_ERROR,
|
||||||
|
NM_UTILS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"address invalid according to inet_aton()");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OK, inet_aton() accepted the format. That's good, because we want
|
||||||
|
* to accept IPv4 addresses in octal format, like 255.255.000.000.
|
||||||
|
* That's what "legacy" means here. inet_pton() doesn't accept those.
|
||||||
|
*
|
||||||
|
* But inet_aton() also ignores trailing garbage and formats with fewer than
|
||||||
|
* 4 digits. That is just too crazy and we don't do that. Perform additional checks
|
||||||
|
* and reject some forms that inet_aton() accepted.
|
||||||
|
*
|
||||||
|
* Note that we still should (of course) accept everything that inet_pton()
|
||||||
|
* accepts. However this code never gets called if inet_pton() succeeds
|
||||||
|
* (see below, aside the assertion code). */
|
||||||
|
|
||||||
|
if (NM_STRCHAR_ANY(text, ch, (!(ch >= '0' && ch <= '9') && !NM_IN_SET(ch, '.', 'x')))) {
|
||||||
|
/* We only accepts '.', digits, and 'x' for "0x". */
|
||||||
|
g_set_error_literal(error,
|
||||||
|
NM_UTILS_ERROR,
|
||||||
|
NM_UTILS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"contains an invalid character");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = nm_memdup_maybe_a(300, text, strlen(text) + 1, &s_free);
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS(bin); i++) {
|
||||||
|
char *current_token = s;
|
||||||
|
gint32 v;
|
||||||
|
|
||||||
|
s = strchr(s, '.');
|
||||||
|
if (s) {
|
||||||
|
s[0] = '\0';
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((i == G_N_ELEMENTS(bin) - 1) != (s == NULL)) {
|
||||||
|
/* Exactly for the last digit, we expect to have no more following token.
|
||||||
|
* But this isn't the case. Abort. */
|
||||||
|
g_set_error(error,
|
||||||
|
NM_UTILS_ERROR,
|
||||||
|
NM_UTILS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"wrong number of tokens (index %d, token '%s')",
|
||||||
|
i,
|
||||||
|
s);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = _nm_utils_ascii_str_to_int64(current_token, 0, 0, 0xFF, -1);
|
||||||
|
if (v == -1) {
|
||||||
|
int errsv = errno;
|
||||||
|
|
||||||
|
/* we do accept octal and hex (even with leading "0x"). But something
|
||||||
|
* about this token is wrong. */
|
||||||
|
g_set_error(error,
|
||||||
|
NM_UTILS_ERROR,
|
||||||
|
NM_UTILS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"invalid token '%s': %s (%d)",
|
||||||
|
current_token,
|
||||||
|
nm_strerror_native(errsv),
|
||||||
|
errsv);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bin[i] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(bin, &a1, sizeof(bin)) != 0) {
|
||||||
|
/* our parsing did not agree with what inet_aton() gave. Something
|
||||||
|
* is wrong. Abort. */
|
||||||
|
g_set_error(
|
||||||
|
error,
|
||||||
|
NM_UTILS_ERROR,
|
||||||
|
NM_UTILS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"inet_aton() result 0x%08x differs from computed value 0x%02hhx%02hhx%02hhx%02hhx",
|
||||||
|
a1.s_addr,
|
||||||
|
bin[0],
|
||||||
|
bin[1],
|
||||||
|
bin[2],
|
||||||
|
bin[3]);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_addr = a1.s_addr;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_utils_parse_inaddr_bin_full(int addr_family,
|
||||||
|
gboolean accept_legacy,
|
||||||
|
const char *text,
|
||||||
|
int *out_addr_family,
|
||||||
|
gpointer out_addr)
|
||||||
|
{
|
||||||
|
NMIPAddr addrbin;
|
||||||
|
|
||||||
|
g_return_val_if_fail(text, FALSE);
|
||||||
|
|
||||||
|
if (addr_family == AF_UNSPEC) {
|
||||||
|
g_return_val_if_fail(!out_addr || out_addr_family, FALSE);
|
||||||
|
addr_family = strchr(text, ':') ? AF_INET6 : AF_INET;
|
||||||
|
} else
|
||||||
|
g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE);
|
||||||
|
|
||||||
|
if (inet_pton(addr_family, text, &addrbin) != 1) {
|
||||||
|
if (accept_legacy && addr_family == AF_INET
|
||||||
|
&& _parse_legacy_addr4(text, &addrbin.addr4, NULL)) {
|
||||||
|
/* The address is in some legacy format which inet_aton() accepts, but not inet_pton().
|
||||||
|
* Most likely octal digits (leading zeros). We accept the address. */
|
||||||
|
} else
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NM_MORE_ASSERTS > 10
|
||||||
|
if (addr_family == AF_INET) {
|
||||||
|
NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
|
||||||
|
gs_free_error GError *error = NULL;
|
||||||
|
in_addr_t a;
|
||||||
|
|
||||||
|
/* The legacy parser should accept everything that inet_pton() accepts too. Meaning,
|
||||||
|
* it should strictly parse *more* formats. And of course, parse it the same way. */
|
||||||
|
if (!_parse_legacy_addr4(text, &a, &error)) {
|
||||||
|
char buf[INET_ADDRSTRLEN];
|
||||||
|
|
||||||
|
g_error("unexpected assertion failure: could parse \"%s\" as %s, but not accepted by "
|
||||||
|
"legacy parser: %s",
|
||||||
|
text,
|
||||||
|
_nm_utils_inet4_ntop(addrbin.addr4, buf),
|
||||||
|
error->message);
|
||||||
|
}
|
||||||
|
nm_assert(addrbin.addr4 == a);
|
||||||
|
NM_PRAGMA_WARNING_REENABLE
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NM_SET_OUT(out_addr_family, addr_family);
|
||||||
|
if (out_addr)
|
||||||
|
nm_ip_addr_set(addr_family, out_addr, &addrbin);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_utils_parse_inaddr(int addr_family, const char *text, char **out_addr)
|
||||||
|
{
|
||||||
|
NMIPAddr addrbin;
|
||||||
|
char addrstr_buf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
|
||||||
|
|
||||||
|
g_return_val_if_fail(text, FALSE);
|
||||||
|
|
||||||
|
if (addr_family == AF_UNSPEC)
|
||||||
|
addr_family = strchr(text, ':') ? AF_INET6 : AF_INET;
|
||||||
|
else
|
||||||
|
g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE);
|
||||||
|
|
||||||
|
if (inet_pton(addr_family, text, &addrbin) != 1)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
NM_SET_OUT(out_addr,
|
||||||
|
g_strdup(inet_ntop(addr_family, &addrbin, addrstr_buf, sizeof(addrstr_buf))));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_utils_parse_inaddr_prefix_bin(int addr_family,
|
||||||
|
const char *text,
|
||||||
|
int *out_addr_family,
|
||||||
|
gpointer out_addr,
|
||||||
|
int *out_prefix)
|
||||||
|
{
|
||||||
|
gs_free char *addrstr_free = NULL;
|
||||||
|
int prefix = -1;
|
||||||
|
const char *slash;
|
||||||
|
const char *addrstr;
|
||||||
|
NMIPAddr addrbin;
|
||||||
|
|
||||||
|
g_return_val_if_fail(text, FALSE);
|
||||||
|
|
||||||
|
if (addr_family == AF_UNSPEC) {
|
||||||
|
g_return_val_if_fail(!out_addr || out_addr_family, FALSE);
|
||||||
|
addr_family = strchr(text, ':') ? AF_INET6 : AF_INET;
|
||||||
|
} else
|
||||||
|
g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE);
|
||||||
|
|
||||||
|
slash = strchr(text, '/');
|
||||||
|
if (slash)
|
||||||
|
addrstr = nm_strndup_a(300, text, slash - text, &addrstr_free);
|
||||||
|
else
|
||||||
|
addrstr = text;
|
||||||
|
|
||||||
|
if (inet_pton(addr_family, addrstr, &addrbin) != 1)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (slash) {
|
||||||
|
/* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't
|
||||||
|
* do that. */
|
||||||
|
prefix =
|
||||||
|
_nm_utils_ascii_str_to_int64(&slash[1], 10, 0, addr_family == AF_INET ? 32 : 128, -1);
|
||||||
|
if (prefix == -1)
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
NM_SET_OUT(out_addr_family, addr_family);
|
||||||
|
if (out_addr)
|
||||||
|
nm_ip_addr_set(addr_family, out_addr, &addrbin);
|
||||||
|
NM_SET_OUT(out_prefix, prefix);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_utils_parse_inaddr_prefix(int addr_family, const char *text, char **out_addr, int *out_prefix)
|
||||||
|
{
|
||||||
|
NMIPAddr addrbin;
|
||||||
|
char addrstr_buf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
|
||||||
|
|
||||||
|
if (!nm_utils_parse_inaddr_prefix_bin(addr_family, text, &addr_family, &addrbin, out_prefix))
|
||||||
|
return FALSE;
|
||||||
|
NM_SET_OUT(out_addr,
|
||||||
|
g_strdup(inet_ntop(addr_family, &addrbin, addrstr_buf, sizeof(addrstr_buf))));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_utils_ipaddr_is_valid(int addr_family, const char *str_addr)
|
||||||
|
{
|
||||||
|
nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6));
|
||||||
|
|
||||||
|
return str_addr && nm_utils_parse_inaddr_bin(addr_family, str_addr, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_utils_ipaddr_is_normalized(int addr_family, const char *str_addr)
|
||||||
|
{
|
||||||
|
NMIPAddr addr;
|
||||||
|
char sbuf[NM_UTILS_INET_ADDRSTRLEN];
|
||||||
|
|
||||||
|
nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6));
|
||||||
|
|
||||||
|
if (!str_addr)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!nm_utils_parse_inaddr_bin(addr_family, str_addr, &addr_family, &addr))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
nm_utils_inet_ntop(addr_family, &addr, sbuf);
|
||||||
|
return nm_streq(sbuf, str_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
NM_UTILS_ENUM2STR_DEFINE(nm_icmpv6_router_pref_to_string,
|
||||||
|
NMIcmpv6RouterPref,
|
||||||
|
NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_LOW, "low"),
|
||||||
|
NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_MEDIUM, "medium"),
|
||||||
|
NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_HIGH, "high"),
|
||||||
|
NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_INVALID, "invalid"), );
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,370 @@
|
||||||
#ifndef __NM_INET_UTILS_H__
|
#ifndef __NM_INET_UTILS_H__
|
||||||
#define __NM_INET_UTILS_H__
|
#define __NM_INET_UTILS_H__
|
||||||
|
|
||||||
|
typedef struct _NMIPAddr {
|
||||||
|
union {
|
||||||
|
guint8 addr_ptr[sizeof(struct in6_addr)];
|
||||||
|
in_addr_t addr4;
|
||||||
|
struct in_addr addr4_struct;
|
||||||
|
struct in6_addr addr6;
|
||||||
|
|
||||||
|
/* NMIPAddr is really a union for IP addresses.
|
||||||
|
* However, as ethernet addresses fit in here nicely, use
|
||||||
|
* it also for an ethernet MAC address. */
|
||||||
|
guint8 ether_addr_octet[6 /*ETH_ALEN*/];
|
||||||
|
NMEtherAddr ether_addr;
|
||||||
|
|
||||||
|
guint8 array[sizeof(struct in6_addr)];
|
||||||
|
};
|
||||||
|
} NMIPAddr;
|
||||||
|
|
||||||
|
#define NM_IP_ADDR_INIT \
|
||||||
|
{ \
|
||||||
|
.array = { 0 } \
|
||||||
|
}
|
||||||
|
|
||||||
|
extern const NMIPAddr nm_ip_addr_zero;
|
||||||
|
|
||||||
|
/* This doesn't really belong here, but since it's convenient to re-use nm_ip_addr_zero.ether_addr
|
||||||
|
* for NMEtherAddr, it is. */
|
||||||
|
#define nm_ether_addr_zero (nm_ip_addr_zero.ether_addr)
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
nm_ip_addr_cmp(int addr_family, gconstpointer a, gconstpointer b)
|
||||||
|
{
|
||||||
|
/* Note that @a and @b are not required to be full NMIPAddr unions.
|
||||||
|
* Depending on @addr_family, they can also be only in_addr_t or
|
||||||
|
* struct in6_addr. */
|
||||||
|
NM_CMP_SELF(a, b);
|
||||||
|
NM_CMP_DIRECT_MEMCMP(a, b, nm_utils_addr_family_to_size(addr_family));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_ip_addr_cmp_for_sort(gconstpointer a, gconstpointer b, gpointer user_data);
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
nm_ip_addr_equal(int addr_family, gconstpointer a, gconstpointer b)
|
||||||
|
{
|
||||||
|
return nm_ip_addr_cmp(addr_family, a, b) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
nm_ip_addr_is_null(int addr_family, gconstpointer addr)
|
||||||
|
{
|
||||||
|
nm_assert(addr);
|
||||||
|
|
||||||
|
if (NM_IS_IPv4(addr_family)) {
|
||||||
|
in_addr_t t;
|
||||||
|
|
||||||
|
/* also for in_addr_t type (AF_INET), we accept that the pointer might
|
||||||
|
* be unaligned. */
|
||||||
|
memcpy(&t, addr, sizeof(t));
|
||||||
|
return t == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IN6_IS_ADDR_UNSPECIFIED((const struct in6_addr *) addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
nm_ip_addr_set(int addr_family, gpointer dst, gconstpointer src)
|
||||||
|
{
|
||||||
|
nm_assert(dst);
|
||||||
|
nm_assert(src);
|
||||||
|
|
||||||
|
/* this MUST use memcpy() to support unaligned src/dst pointers. */
|
||||||
|
memcpy(dst, src, nm_utils_addr_family_to_size(addr_family));
|
||||||
|
|
||||||
|
/* Note that @dst is not necessarily a NMIPAddr, it could also be just
|
||||||
|
* an in_addr_t/struct in6_addr. We thus can only set the bytes that
|
||||||
|
* we know are present based on the address family.
|
||||||
|
*
|
||||||
|
* Using this function to initialize an NMIPAddr union (for IPv4) leaves
|
||||||
|
* uninitalized bytes. Avoid that by using nm_ip_addr_init() instead. */
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline NMIPAddr
|
||||||
|
nm_ip_addr_init(int addr_family, gconstpointer src)
|
||||||
|
{
|
||||||
|
NMIPAddr a;
|
||||||
|
|
||||||
|
nm_assert_addr_family(addr_family);
|
||||||
|
nm_assert(src);
|
||||||
|
|
||||||
|
G_STATIC_ASSERT_EXPR(sizeof(NMIPAddr) == sizeof(struct in6_addr));
|
||||||
|
|
||||||
|
/* this MUST use memcpy() to support unaligned src/dst pointers. */
|
||||||
|
|
||||||
|
if (NM_IS_IPv4(addr_family)) {
|
||||||
|
memcpy(&a, src, sizeof(in_addr_t));
|
||||||
|
|
||||||
|
/* ensure all bytes of the union are initialized. If only to make
|
||||||
|
* valgrind happy. */
|
||||||
|
memset(&a.array[sizeof(in_addr_t)], 0, sizeof(a) - sizeof(in_addr_t));
|
||||||
|
} else
|
||||||
|
memcpy(&a, src, sizeof(struct in6_addr));
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean nm_ip_addr_set_from_untrusted(int addr_family,
|
||||||
|
gpointer dst,
|
||||||
|
gconstpointer src,
|
||||||
|
gsize src_len,
|
||||||
|
int *out_addr_family);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_ip_addr_set_from_variant(int addr_family, gpointer dst, GVariant *variant, int *out_addr_family);
|
||||||
|
|
||||||
|
static inline gconstpointer
|
||||||
|
nm_ip_addr_from_packed_array(int addr_family, gconstpointer ipaddr_arr, gsize idx)
|
||||||
|
{
|
||||||
|
return NM_IS_IPv4(addr_family)
|
||||||
|
? ((gconstpointer) & (((const struct in_addr *) ipaddr_arr)[idx]))
|
||||||
|
: ((gconstpointer) & (((const struct in6_addr *) ipaddr_arr)[idx]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static inline guint32
|
||||||
|
_nm_utils_ip4_netmask_to_prefix(in_addr_t subnetmask)
|
||||||
|
{
|
||||||
|
G_STATIC_ASSERT_EXPR(__SIZEOF_INT__ == 4);
|
||||||
|
G_STATIC_ASSERT_EXPR(sizeof(int) == 4);
|
||||||
|
G_STATIC_ASSERT_EXPR(sizeof(guint) == 4);
|
||||||
|
G_STATIC_ASSERT_EXPR(sizeof(subnetmask) == 4);
|
||||||
|
|
||||||
|
return ((subnetmask != 0u) ? (guint32) (32 - __builtin_ctz(ntohl(subnetmask))) : 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _nm_utils_ip4_prefix_to_netmask:
|
||||||
|
* @prefix: a CIDR prefix
|
||||||
|
*
|
||||||
|
* Returns: the netmask represented by the prefix, in network byte order
|
||||||
|
**/
|
||||||
|
static inline in_addr_t
|
||||||
|
_nm_utils_ip4_prefix_to_netmask(guint32 prefix)
|
||||||
|
{
|
||||||
|
nm_assert(prefix <= 32);
|
||||||
|
return prefix < 32 ? ~htonl(0xFFFFFFFFu >> prefix) : 0xFFFFFFFFu;
|
||||||
|
}
|
||||||
|
|
||||||
|
guint32 _nm_utils_ip4_get_default_prefix0(in_addr_t ip);
|
||||||
|
guint32 _nm_utils_ip4_get_default_prefix(in_addr_t ip);
|
||||||
|
|
||||||
|
gconstpointer
|
||||||
|
nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint32 plen);
|
||||||
|
|
||||||
|
/* nm_utils_ip4_address_clear_host_address:
|
||||||
|
* @addr: source ip6 address
|
||||||
|
* @plen: prefix length of network
|
||||||
|
*
|
||||||
|
* returns: the input address, with the host address set to 0.
|
||||||
|
*/
|
||||||
|
static inline in_addr_t
|
||||||
|
nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint32 plen)
|
||||||
|
{
|
||||||
|
return addr & _nm_utils_ip4_prefix_to_netmask(plen);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct in6_addr *nm_utils_ip6_address_clear_host_address(struct in6_addr *dst,
|
||||||
|
const struct in6_addr *src,
|
||||||
|
guint32 plen);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
nm_utils_ip4_address_same_prefix_cmp(in_addr_t addr_a, in_addr_t addr_b, guint32 plen)
|
||||||
|
{
|
||||||
|
NM_CMP_DIRECT(htonl(nm_utils_ip4_address_clear_host_address(addr_a, plen)),
|
||||||
|
htonl(nm_utils_ip4_address_clear_host_address(addr_b, plen)));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a,
|
||||||
|
const struct in6_addr *addr_b,
|
||||||
|
guint32 plen);
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
nm_utils_ip4_address_same_prefix(in_addr_t addr_a, in_addr_t addr_b, guint32 plen)
|
||||||
|
{
|
||||||
|
return nm_utils_ip4_address_same_prefix_cmp(addr_a, addr_b, plen) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
nm_utils_ip6_address_same_prefix(const struct in6_addr *addr_a,
|
||||||
|
const struct in6_addr *addr_b,
|
||||||
|
guint8 plen)
|
||||||
|
{
|
||||||
|
return nm_utils_ip6_address_same_prefix_cmp(addr_a, addr_b, plen) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
nm_utils_ip_address_same_prefix_cmp(int addr_family,
|
||||||
|
gconstpointer addr_a,
|
||||||
|
gconstpointer addr_b,
|
||||||
|
guint8 plen)
|
||||||
|
{
|
||||||
|
NM_CMP_SELF(addr_a, addr_b);
|
||||||
|
|
||||||
|
if (NM_IS_IPv4(addr_family)) {
|
||||||
|
return nm_utils_ip4_address_same_prefix_cmp(*((const in_addr_t *) addr_a),
|
||||||
|
*((const in_addr_t *) addr_b),
|
||||||
|
plen);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nm_utils_ip6_address_same_prefix_cmp(addr_a, addr_b, plen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
nm_utils_ip_address_same_prefix(int addr_family,
|
||||||
|
gconstpointer addr_a,
|
||||||
|
gconstpointer addr_b,
|
||||||
|
guint8 plen)
|
||||||
|
{
|
||||||
|
return nm_utils_ip_address_same_prefix_cmp(addr_family, addr_a, addr_b, plen) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a, b, plen) \
|
||||||
|
NM_CMP_RETURN(nm_utils_ip4_address_same_prefix_cmp((a), (b), (plen)))
|
||||||
|
|
||||||
|
#define NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(a, b, plen) \
|
||||||
|
NM_CMP_RETURN(nm_utils_ip6_address_same_prefix_cmp((a), (b), (plen)))
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
gboolean nm_utils_ip_is_site_local(int addr_family, const void *address);
|
||||||
|
gboolean nm_utils_ip6_is_ula(const struct in6_addr *address);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define NM_IPV4LL_NETWORK ((in_addr_t) htonl(0xA9FE0000lu))
|
||||||
|
#define NM_IPV4LL_NETMASK ((in_addr_t) htonl(0xFFFF0000lu))
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
nm_utils_ip4_address_is_loopback(in_addr_t addr)
|
||||||
|
{
|
||||||
|
/* There is also IN_LOOPBACK() in <linux/in.h>, but there the
|
||||||
|
* argument is in host order not `in_addr_t`. */
|
||||||
|
return (addr & htonl(0xFF000000u)) == htonl(0x7F000000u);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
nm_utils_ip4_address_is_link_local(in_addr_t addr)
|
||||||
|
{
|
||||||
|
return (addr & NM_IPV4LL_NETMASK) == NM_IPV4LL_NETWORK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
nm_utils_ip4_address_is_zeronet(in_addr_t network)
|
||||||
|
{
|
||||||
|
/* Same as ipv4_is_zeronet() from kernel's include/linux/in.h. */
|
||||||
|
return (network & htonl(0xFF000000u)) == htonl(0x00000000u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define NM_UTILS_INET_ADDRSTRLEN INET6_ADDRSTRLEN
|
||||||
|
|
||||||
|
/* Forward declare function so we don't have to drag in <arpa/inet.h>. */
|
||||||
|
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
|
||||||
|
|
||||||
|
static inline const char *
|
||||||
|
nm_utils_inet_ntop(int addr_family, gconstpointer addr, char *dst)
|
||||||
|
{
|
||||||
|
const char *s;
|
||||||
|
|
||||||
|
nm_assert_addr_family(addr_family);
|
||||||
|
nm_assert(addr);
|
||||||
|
nm_assert(dst);
|
||||||
|
|
||||||
|
s = inet_ntop(addr_family,
|
||||||
|
addr,
|
||||||
|
dst,
|
||||||
|
addr_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN);
|
||||||
|
nm_assert(s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *
|
||||||
|
_nm_utils_inet4_ntop(in_addr_t addr, char dst[static INET_ADDRSTRLEN])
|
||||||
|
{
|
||||||
|
return nm_utils_inet_ntop(AF_INET, &addr, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *
|
||||||
|
_nm_utils_inet6_ntop(const struct in6_addr *addr, char dst[static INET6_ADDRSTRLEN])
|
||||||
|
{
|
||||||
|
return nm_utils_inet_ntop(AF_INET6, addr, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *
|
||||||
|
nm_utils_inet_ntop_dup(int addr_family, gconstpointer addr)
|
||||||
|
{
|
||||||
|
char buf[NM_UTILS_INET_ADDRSTRLEN];
|
||||||
|
|
||||||
|
return g_strdup(nm_utils_inet_ntop(addr_family, addr, buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *
|
||||||
|
nm_utils_inet4_ntop_dup(in_addr_t addr)
|
||||||
|
{
|
||||||
|
return nm_utils_inet_ntop_dup(AF_INET, &addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *
|
||||||
|
nm_utils_inet6_ntop_dup(const struct in6_addr *addr)
|
||||||
|
{
|
||||||
|
return nm_utils_inet_ntop_dup(AF_INET6, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
gboolean nm_utils_parse_inaddr_bin_full(int addr_family,
|
||||||
|
gboolean accept_legacy,
|
||||||
|
const char *text,
|
||||||
|
int *out_addr_family,
|
||||||
|
gpointer out_addr);
|
||||||
|
static inline gboolean
|
||||||
|
nm_utils_parse_inaddr_bin(int addr_family,
|
||||||
|
const char *text,
|
||||||
|
int *out_addr_family,
|
||||||
|
gpointer out_addr)
|
||||||
|
{
|
||||||
|
return nm_utils_parse_inaddr_bin_full(addr_family, FALSE, text, out_addr_family, out_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean nm_utils_parse_inaddr(int addr_family, const char *text, char **out_addr);
|
||||||
|
|
||||||
|
gboolean nm_utils_parse_inaddr_prefix_bin(int addr_family,
|
||||||
|
const char *text,
|
||||||
|
int *out_addr_family,
|
||||||
|
gpointer out_addr,
|
||||||
|
int *out_prefix);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
nm_utils_parse_inaddr_prefix(int addr_family, const char *text, char **out_addr, int *out_prefix);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
gboolean nm_utils_ipaddr_is_valid(int addr_family, const char *str_addr);
|
||||||
|
|
||||||
|
gboolean nm_utils_ipaddr_is_normalized(int addr_family, const char *str_addr);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
/* this enum is compatible with ICMPV6_ROUTER_PREF_* (from <linux/icmpv6.h>,
|
||||||
|
* the values for netlink attribute RTA_PREF) and "enum ndp_route_preference"
|
||||||
|
* from <ndp.h>. */
|
||||||
|
typedef enum _nm_packed {
|
||||||
|
NM_ICMPV6_ROUTER_PREF_MEDIUM = 0x0, /* ICMPV6_ROUTER_PREF_MEDIUM */
|
||||||
|
NM_ICMPV6_ROUTER_PREF_LOW = 0x3, /* ICMPV6_ROUTER_PREF_LOW */
|
||||||
|
NM_ICMPV6_ROUTER_PREF_HIGH = 0x1, /* ICMPV6_ROUTER_PREF_HIGH */
|
||||||
|
NM_ICMPV6_ROUTER_PREF_INVALID = 0x2, /* ICMPV6_ROUTER_PREF_INVALID */
|
||||||
|
} NMIcmpv6RouterPref;
|
||||||
|
|
||||||
|
const char *nm_icmpv6_router_pref_to_string(NMIcmpv6RouterPref pref, char *buf, gsize len);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
#endif /* __NM_INET_UTILS_H__ */
|
#endif /* __NM_INET_UTILS_H__ */
|
||||||
|
|
|
||||||
|
|
@ -35,111 +35,6 @@ const void *const _NM_PTRARRAY_EMPTY[1] = {NULL};
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
const NMIPAddr nm_ip_addr_zero = {};
|
|
||||||
|
|
||||||
/* We use _nm_alignas(NMIPAddr) to ensure that fields for in_addr_t and
|
|
||||||
* struct in6_addr have all the same alignment. Ensure that this is suitable. */
|
|
||||||
G_STATIC_ASSERT(_nm_alignof(in_addr_t) <= _nm_alignof(NMIPAddr));
|
|
||||||
G_STATIC_ASSERT(_nm_alignof(struct in_addr) <= _nm_alignof(NMIPAddr));
|
|
||||||
G_STATIC_ASSERT(_nm_alignof(struct in6_addr) <= _nm_alignof(NMIPAddr));
|
|
||||||
G_STATIC_ASSERT(_nm_alignof(NMEtherAddr) <= _nm_alignof(NMIPAddr));
|
|
||||||
|
|
||||||
int
|
|
||||||
nm_ip_addr_cmp_for_sort(gconstpointer a, gconstpointer b, gpointer user_data)
|
|
||||||
{
|
|
||||||
/* This is a compare function that can be used for sorting IP addresses.
|
|
||||||
* Essentially, it calls memcmp(). @user_data must be GINT_TO_POINTER(addr_family).
|
|
||||||
* @a and @b must be either pointers to in_addr_t, struct in6_addr or NMIPAddr. */
|
|
||||||
return nm_ip_addr_cmp(GPOINTER_TO_INT(user_data), a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this initializes a struct in_addr/in6_addr and allows for untrusted
|
|
||||||
* arguments (like unsuitable @addr_family or @src_len). It's almost safe
|
|
||||||
* in the sense that it verifies input arguments strictly. Also, it
|
|
||||||
* uses memcpy() to access @src, so alignment is not an issue.
|
|
||||||
*
|
|
||||||
* Only potential pitfalls:
|
|
||||||
*
|
|
||||||
* - it allows for @addr_family to be AF_UNSPEC. If that is the case (and the
|
|
||||||
* caller allows for that), the caller MUST provide @out_addr_family.
|
|
||||||
* - when setting @dst to an IPv4 address, the trailing bytes are not touched.
|
|
||||||
* Meaning, if @dst is an NMIPAddr union, only the first bytes will be set.
|
|
||||||
* If that matter to you, clear @dst before. */
|
|
||||||
gboolean
|
|
||||||
nm_ip_addr_set_from_untrusted(int addr_family,
|
|
||||||
gpointer dst,
|
|
||||||
gconstpointer src,
|
|
||||||
gsize src_len,
|
|
||||||
int *out_addr_family)
|
|
||||||
{
|
|
||||||
nm_assert(dst);
|
|
||||||
|
|
||||||
switch (addr_family) {
|
|
||||||
case AF_UNSPEC:
|
|
||||||
if (!out_addr_family) {
|
|
||||||
/* when the callers allow undefined @addr_family, they must provide
|
|
||||||
* an @out_addr_family argument. */
|
|
||||||
nm_assert_not_reached();
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
switch (src_len) {
|
|
||||||
case sizeof(struct in_addr):
|
|
||||||
addr_family = AF_INET;
|
|
||||||
break;
|
|
||||||
case sizeof(struct in6_addr):
|
|
||||||
addr_family = AF_INET6;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AF_INET:
|
|
||||||
if (src_len != sizeof(struct in_addr))
|
|
||||||
return FALSE;
|
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
if (src_len != sizeof(struct in6_addr))
|
|
||||||
return FALSE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* when the callers allow undefined @addr_family, they must provide
|
|
||||||
* an @out_addr_family argument. */
|
|
||||||
nm_assert(out_addr_family);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
nm_assert(src);
|
|
||||||
|
|
||||||
memcpy(dst, src, src_len);
|
|
||||||
NM_SET_OUT(out_addr_family, addr_family);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
nm_ip_addr_set_from_variant(int addr_family, gpointer dst, GVariant *variant, int *out_addr_family)
|
|
||||||
{
|
|
||||||
gconstpointer bytes;
|
|
||||||
gsize len;
|
|
||||||
|
|
||||||
g_return_val_if_fail(dst, FALSE);
|
|
||||||
g_return_val_if_fail(variant, FALSE);
|
|
||||||
|
|
||||||
/* This function always expects IP addressea a byte arrays ("ay"). Note that
|
|
||||||
* several NetworkManager API uses "u" (32 bit unsigned intergers) for IPv4 addresses.
|
|
||||||
* So this function won't work in those cases.
|
|
||||||
*
|
|
||||||
* Btw, using "u" for IPv4 address messes badly with the endianness (host
|
|
||||||
* vs network byte order). Don't do that.
|
|
||||||
*/
|
|
||||||
g_return_val_if_fail(g_variant_is_of_type(variant, G_VARIANT_TYPE("ay")), FALSE);
|
|
||||||
|
|
||||||
bytes = g_variant_get_fixed_array(variant, &len, sizeof(guint8));
|
|
||||||
|
|
||||||
return nm_ip_addr_set_from_untrusted(addr_family, dst, bytes, len, out_addr_family);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
G_STATIC_ASSERT(ETH_ALEN == sizeof(struct ether_addr));
|
G_STATIC_ASSERT(ETH_ALEN == sizeof(struct ether_addr));
|
||||||
G_STATIC_ASSERT(ETH_ALEN == 6);
|
G_STATIC_ASSERT(ETH_ALEN == 6);
|
||||||
G_STATIC_ASSERT(ETH_ALEN == sizeof(NMEtherAddr));
|
G_STATIC_ASSERT(ETH_ALEN == sizeof(NMEtherAddr));
|
||||||
|
|
@ -981,293 +876,6 @@ nm_utils_flags2str(const NMUtilsFlags2StrDesc *descs,
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
guint32
|
|
||||||
_nm_utils_ip4_get_default_prefix0(in_addr_t ip)
|
|
||||||
{
|
|
||||||
/* The function is originally from ipcalc.c of Red Hat's initscripts. */
|
|
||||||
switch (ntohl(ip) >> 24) {
|
|
||||||
case 0 ... 127:
|
|
||||||
return 8; /* Class A */
|
|
||||||
case 128 ... 191:
|
|
||||||
return 16; /* Class B */
|
|
||||||
case 192 ... 223:
|
|
||||||
return 24; /* Class C */
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
guint32
|
|
||||||
_nm_utils_ip4_get_default_prefix(in_addr_t ip)
|
|
||||||
{
|
|
||||||
return _nm_utils_ip4_get_default_prefix0(ip) ?: 24;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
nm_utils_ip_is_site_local(int addr_family, const void *address)
|
|
||||||
{
|
|
||||||
in_addr_t addr4;
|
|
||||||
|
|
||||||
switch (addr_family) {
|
|
||||||
case AF_INET:
|
|
||||||
/* RFC1918 private addresses
|
|
||||||
* 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 */
|
|
||||||
addr4 = ntohl(*((const in_addr_t *) address));
|
|
||||||
return (addr4 & 0xff000000) == 0x0a000000 || (addr4 & 0xfff00000) == 0xac100000
|
|
||||||
|| (addr4 & 0xffff0000) == 0xc0a80000;
|
|
||||||
case AF_INET6:
|
|
||||||
/* IN6_IS_ADDR_SITELOCAL() is for deprecated fec0::/10 addresses (see rfc3879, 4.).
|
|
||||||
* Note that for unique local IPv6 addresses (ULA, fc00::/7) this returns false,
|
|
||||||
* which may or may not be a bug. */
|
|
||||||
return IN6_IS_ADDR_SITELOCAL(address);
|
|
||||||
default:
|
|
||||||
g_return_val_if_reached(FALSE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
nm_utils_ip6_is_ula(const struct in6_addr *address)
|
|
||||||
{
|
|
||||||
/* Unique local IPv6 address (ULA) fc00::/7 */
|
|
||||||
return (address->s6_addr32[0] & htonl(0xfe000000u)) == htonl(0xfc000000u);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_parse_legacy_addr4(const char *text, in_addr_t *out_addr, GError **error)
|
|
||||||
{
|
|
||||||
gs_free char *s_free = NULL;
|
|
||||||
struct in_addr a1;
|
|
||||||
guint8 bin[sizeof(a1)];
|
|
||||||
char *s;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (inet_aton(text, &a1) != 1) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
NM_UTILS_ERROR,
|
|
||||||
NM_UTILS_ERROR_INVALID_ARGUMENT,
|
|
||||||
"address invalid according to inet_aton()");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* OK, inet_aton() accepted the format. That's good, because we want
|
|
||||||
* to accept IPv4 addresses in octal format, like 255.255.000.000.
|
|
||||||
* That's what "legacy" means here. inet_pton() doesn't accept those.
|
|
||||||
*
|
|
||||||
* But inet_aton() also ignores trailing garbage and formats with fewer than
|
|
||||||
* 4 digits. That is just too crazy and we don't do that. Perform additional checks
|
|
||||||
* and reject some forms that inet_aton() accepted.
|
|
||||||
*
|
|
||||||
* Note that we still should (of course) accept everything that inet_pton()
|
|
||||||
* accepts. However this code never gets called if inet_pton() succeeds
|
|
||||||
* (see below, aside the assertion code). */
|
|
||||||
|
|
||||||
if (NM_STRCHAR_ANY(text, ch, (!(ch >= '0' && ch <= '9') && !NM_IN_SET(ch, '.', 'x')))) {
|
|
||||||
/* We only accepts '.', digits, and 'x' for "0x". */
|
|
||||||
g_set_error_literal(error,
|
|
||||||
NM_UTILS_ERROR,
|
|
||||||
NM_UTILS_ERROR_INVALID_ARGUMENT,
|
|
||||||
"contains an invalid character");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
s = nm_memdup_maybe_a(300, text, strlen(text) + 1, &s_free);
|
|
||||||
|
|
||||||
for (i = 0; i < G_N_ELEMENTS(bin); i++) {
|
|
||||||
char *current_token = s;
|
|
||||||
gint32 v;
|
|
||||||
|
|
||||||
s = strchr(s, '.');
|
|
||||||
if (s) {
|
|
||||||
s[0] = '\0';
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((i == G_N_ELEMENTS(bin) - 1) != (s == NULL)) {
|
|
||||||
/* Exactly for the last digit, we expect to have no more following token.
|
|
||||||
* But this isn't the case. Abort. */
|
|
||||||
g_set_error(error,
|
|
||||||
NM_UTILS_ERROR,
|
|
||||||
NM_UTILS_ERROR_INVALID_ARGUMENT,
|
|
||||||
"wrong number of tokens (index %d, token '%s')",
|
|
||||||
i,
|
|
||||||
s);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
v = _nm_utils_ascii_str_to_int64(current_token, 0, 0, 0xFF, -1);
|
|
||||||
if (v == -1) {
|
|
||||||
int errsv = errno;
|
|
||||||
|
|
||||||
/* we do accept octal and hex (even with leading "0x"). But something
|
|
||||||
* about this token is wrong. */
|
|
||||||
g_set_error(error,
|
|
||||||
NM_UTILS_ERROR,
|
|
||||||
NM_UTILS_ERROR_INVALID_ARGUMENT,
|
|
||||||
"invalid token '%s': %s (%d)",
|
|
||||||
current_token,
|
|
||||||
nm_strerror_native(errsv),
|
|
||||||
errsv);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
bin[i] = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memcmp(bin, &a1, sizeof(bin)) != 0) {
|
|
||||||
/* our parsing did not agree with what inet_aton() gave. Something
|
|
||||||
* is wrong. Abort. */
|
|
||||||
g_set_error(
|
|
||||||
error,
|
|
||||||
NM_UTILS_ERROR,
|
|
||||||
NM_UTILS_ERROR_INVALID_ARGUMENT,
|
|
||||||
"inet_aton() result 0x%08x differs from computed value 0x%02hhx%02hhx%02hhx%02hhx",
|
|
||||||
a1.s_addr,
|
|
||||||
bin[0],
|
|
||||||
bin[1],
|
|
||||||
bin[2],
|
|
||||||
bin[3]);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
*out_addr = a1.s_addr;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
nm_utils_parse_inaddr_bin_full(int addr_family,
|
|
||||||
gboolean accept_legacy,
|
|
||||||
const char *text,
|
|
||||||
int *out_addr_family,
|
|
||||||
gpointer out_addr)
|
|
||||||
{
|
|
||||||
NMIPAddr addrbin;
|
|
||||||
|
|
||||||
g_return_val_if_fail(text, FALSE);
|
|
||||||
|
|
||||||
if (addr_family == AF_UNSPEC) {
|
|
||||||
g_return_val_if_fail(!out_addr || out_addr_family, FALSE);
|
|
||||||
addr_family = strchr(text, ':') ? AF_INET6 : AF_INET;
|
|
||||||
} else
|
|
||||||
g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE);
|
|
||||||
|
|
||||||
if (inet_pton(addr_family, text, &addrbin) != 1) {
|
|
||||||
if (accept_legacy && addr_family == AF_INET
|
|
||||||
&& _parse_legacy_addr4(text, &addrbin.addr4, NULL)) {
|
|
||||||
/* The address is in some legacy format which inet_aton() accepts, but not inet_pton().
|
|
||||||
* Most likely octal digits (leading zeros). We accept the address. */
|
|
||||||
} else
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if NM_MORE_ASSERTS > 10
|
|
||||||
if (addr_family == AF_INET) {
|
|
||||||
NM_PRAGMA_WARNING_DISABLE_DANGLING_POINTER
|
|
||||||
gs_free_error GError *error = NULL;
|
|
||||||
in_addr_t a;
|
|
||||||
|
|
||||||
/* The legacy parser should accept everything that inet_pton() accepts too. Meaning,
|
|
||||||
* it should strictly parse *more* formats. And of course, parse it the same way. */
|
|
||||||
if (!_parse_legacy_addr4(text, &a, &error)) {
|
|
||||||
char buf[INET_ADDRSTRLEN];
|
|
||||||
|
|
||||||
g_error("unexpected assertion failure: could parse \"%s\" as %s, but not accepted by "
|
|
||||||
"legacy parser: %s",
|
|
||||||
text,
|
|
||||||
_nm_utils_inet4_ntop(addrbin.addr4, buf),
|
|
||||||
error->message);
|
|
||||||
}
|
|
||||||
nm_assert(addrbin.addr4 == a);
|
|
||||||
NM_PRAGMA_WARNING_REENABLE
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
NM_SET_OUT(out_addr_family, addr_family);
|
|
||||||
if (out_addr)
|
|
||||||
nm_ip_addr_set(addr_family, out_addr, &addrbin);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
nm_utils_parse_inaddr(int addr_family, const char *text, char **out_addr)
|
|
||||||
{
|
|
||||||
NMIPAddr addrbin;
|
|
||||||
char addrstr_buf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
|
|
||||||
|
|
||||||
g_return_val_if_fail(text, FALSE);
|
|
||||||
|
|
||||||
if (addr_family == AF_UNSPEC)
|
|
||||||
addr_family = strchr(text, ':') ? AF_INET6 : AF_INET;
|
|
||||||
else
|
|
||||||
g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE);
|
|
||||||
|
|
||||||
if (inet_pton(addr_family, text, &addrbin) != 1)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
NM_SET_OUT(out_addr,
|
|
||||||
g_strdup(inet_ntop(addr_family, &addrbin, addrstr_buf, sizeof(addrstr_buf))));
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
nm_utils_parse_inaddr_prefix_bin(int addr_family,
|
|
||||||
const char *text,
|
|
||||||
int *out_addr_family,
|
|
||||||
gpointer out_addr,
|
|
||||||
int *out_prefix)
|
|
||||||
{
|
|
||||||
gs_free char *addrstr_free = NULL;
|
|
||||||
int prefix = -1;
|
|
||||||
const char *slash;
|
|
||||||
const char *addrstr;
|
|
||||||
NMIPAddr addrbin;
|
|
||||||
|
|
||||||
g_return_val_if_fail(text, FALSE);
|
|
||||||
|
|
||||||
if (addr_family == AF_UNSPEC) {
|
|
||||||
g_return_val_if_fail(!out_addr || out_addr_family, FALSE);
|
|
||||||
addr_family = strchr(text, ':') ? AF_INET6 : AF_INET;
|
|
||||||
} else
|
|
||||||
g_return_val_if_fail(NM_IN_SET(addr_family, AF_INET, AF_INET6), FALSE);
|
|
||||||
|
|
||||||
slash = strchr(text, '/');
|
|
||||||
if (slash)
|
|
||||||
addrstr = nm_strndup_a(300, text, slash - text, &addrstr_free);
|
|
||||||
else
|
|
||||||
addrstr = text;
|
|
||||||
|
|
||||||
if (inet_pton(addr_family, addrstr, &addrbin) != 1)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (slash) {
|
|
||||||
/* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't
|
|
||||||
* do that. */
|
|
||||||
prefix =
|
|
||||||
_nm_utils_ascii_str_to_int64(&slash[1], 10, 0, addr_family == AF_INET ? 32 : 128, -1);
|
|
||||||
if (prefix == -1)
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
NM_SET_OUT(out_addr_family, addr_family);
|
|
||||||
if (out_addr)
|
|
||||||
nm_ip_addr_set(addr_family, out_addr, &addrbin);
|
|
||||||
NM_SET_OUT(out_prefix, prefix);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
nm_utils_parse_inaddr_prefix(int addr_family, const char *text, char **out_addr, int *out_prefix)
|
|
||||||
{
|
|
||||||
NMIPAddr addrbin;
|
|
||||||
char addrstr_buf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
|
|
||||||
|
|
||||||
if (!nm_utils_parse_inaddr_prefix_bin(addr_family, text, &addr_family, &addrbin, out_prefix))
|
|
||||||
return FALSE;
|
|
||||||
NM_SET_OUT(out_addr,
|
|
||||||
g_strdup(inet_ntop(addr_family, &addrbin, addrstr_buf, sizeof(addrstr_buf))));
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
nm_utils_parse_next_line(const char **inout_ptr,
|
nm_utils_parse_next_line(const char **inout_ptr,
|
||||||
gsize *inout_len,
|
gsize *inout_len,
|
||||||
|
|
@ -1323,43 +931,6 @@ done:
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
gboolean
|
|
||||||
nm_utils_ipaddr_is_valid(int addr_family, const char *str_addr)
|
|
||||||
{
|
|
||||||
nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6));
|
|
||||||
|
|
||||||
return str_addr && nm_utils_parse_inaddr_bin(addr_family, str_addr, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
nm_utils_ipaddr_is_normalized(int addr_family, const char *str_addr)
|
|
||||||
{
|
|
||||||
NMIPAddr addr;
|
|
||||||
char sbuf[NM_UTILS_INET_ADDRSTRLEN];
|
|
||||||
|
|
||||||
nm_assert(NM_IN_SET(addr_family, AF_UNSPEC, AF_INET, AF_INET6));
|
|
||||||
|
|
||||||
if (!str_addr)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (!nm_utils_parse_inaddr_bin(addr_family, str_addr, &addr_family, &addr))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
nm_utils_inet_ntop(addr_family, &addr, sbuf);
|
|
||||||
return nm_streq(sbuf, str_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
NM_UTILS_ENUM2STR_DEFINE(nm_icmpv6_router_pref_to_string,
|
|
||||||
NMIcmpv6RouterPref,
|
|
||||||
NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_LOW, "low"),
|
|
||||||
NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_MEDIUM, "medium"),
|
|
||||||
NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_HIGH, "high"),
|
|
||||||
NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_INVALID, "invalid"), );
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nm_g_ascii_strtoll()
|
* nm_g_ascii_strtoll()
|
||||||
* @nptr: the string to parse
|
* @nptr: the string to parse
|
||||||
|
|
@ -6359,98 +5930,6 @@ _nm_utils_ssid_to_string_gbytes(GBytes *ssid)
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
gconstpointer
|
|
||||||
nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint32 plen)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(dst, NULL);
|
|
||||||
|
|
||||||
switch (family) {
|
|
||||||
case AF_INET:
|
|
||||||
g_return_val_if_fail(plen <= 32, NULL);
|
|
||||||
|
|
||||||
if (!src) {
|
|
||||||
/* allow "self-assignment", by specifying %NULL as source. */
|
|
||||||
src = dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
*((guint32 *) dst) = nm_utils_ip4_address_clear_host_address(*((guint32 *) src), plen);
|
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
nm_utils_ip6_address_clear_host_address(dst, src, plen);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
g_return_val_if_reached(NULL);
|
|
||||||
}
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* nm_utils_ip6_address_clear_host_address:
|
|
||||||
* @dst: destination output buffer, will contain the network part of the @src address
|
|
||||||
* @src: source ip6 address. If NULL, this does an in-place update of @dst.
|
|
||||||
* Also, @src and @dst are allowed to be the same pointers.
|
|
||||||
* @plen: prefix length of network
|
|
||||||
*
|
|
||||||
* Note: this function is self assignment safe, to update @src inplace, set both
|
|
||||||
* @dst and @src to the same destination or set @src NULL.
|
|
||||||
*/
|
|
||||||
const struct in6_addr *
|
|
||||||
nm_utils_ip6_address_clear_host_address(struct in6_addr *dst,
|
|
||||||
const struct in6_addr *src,
|
|
||||||
guint32 plen)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(plen <= 128, NULL);
|
|
||||||
g_return_val_if_fail(dst, NULL);
|
|
||||||
|
|
||||||
if (!src)
|
|
||||||
src = dst;
|
|
||||||
|
|
||||||
if (plen < 128) {
|
|
||||||
guint nbytes = plen / 8;
|
|
||||||
guint nbits = plen % 8;
|
|
||||||
|
|
||||||
if (nbytes && dst != src)
|
|
||||||
memcpy(dst, src, nbytes);
|
|
||||||
if (nbits) {
|
|
||||||
dst->s6_addr[nbytes] = (src->s6_addr[nbytes] & (0xFF << (8 - nbits)));
|
|
||||||
nbytes++;
|
|
||||||
}
|
|
||||||
if (nbytes <= 15)
|
|
||||||
memset(&dst->s6_addr[nbytes], 0, 16 - nbytes);
|
|
||||||
} else if (src != dst)
|
|
||||||
*dst = *src;
|
|
||||||
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a,
|
|
||||||
const struct in6_addr *addr_b,
|
|
||||||
guint32 plen)
|
|
||||||
{
|
|
||||||
int nbytes;
|
|
||||||
guint8 va, vb, m;
|
|
||||||
|
|
||||||
if (plen >= 128) {
|
|
||||||
nm_assert(plen == 128);
|
|
||||||
NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, sizeof(struct in6_addr));
|
|
||||||
} else {
|
|
||||||
nbytes = plen / 8;
|
|
||||||
if (nbytes)
|
|
||||||
NM_CMP_DIRECT_MEMCMP(addr_a, addr_b, nbytes);
|
|
||||||
|
|
||||||
plen = plen % 8;
|
|
||||||
if (plen != 0) {
|
|
||||||
m = ~((1 << (8 - plen)) - 1);
|
|
||||||
va = ((((const guint8 *) addr_a))[nbytes]) & m;
|
|
||||||
vb = ((((const guint8 *) addr_b))[nbytes]) & m;
|
|
||||||
NM_CMP_DIRECT(va, vb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
#define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/"
|
#define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/"
|
||||||
#define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/"
|
#define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -214,128 +214,6 @@ nm_ether_addr_equal(const NMEtherAddr *a, const NMEtherAddr *b)
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
union {
|
|
||||||
guint8 addr_ptr[sizeof(struct in6_addr)];
|
|
||||||
in_addr_t addr4;
|
|
||||||
struct in_addr addr4_struct;
|
|
||||||
struct in6_addr addr6;
|
|
||||||
|
|
||||||
/* NMIPAddr is really a union for IP addresses.
|
|
||||||
* However, as ethernet addresses fit in here nicely, use
|
|
||||||
* it also for an ethernet MAC address. */
|
|
||||||
guint8 ether_addr_octet[6 /*ETH_ALEN*/];
|
|
||||||
NMEtherAddr ether_addr;
|
|
||||||
|
|
||||||
guint8 array[sizeof(struct in6_addr)];
|
|
||||||
};
|
|
||||||
} NMIPAddr;
|
|
||||||
|
|
||||||
#define NM_IP_ADDR_INIT \
|
|
||||||
{ \
|
|
||||||
.array = { 0 } \
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const NMIPAddr nm_ip_addr_zero;
|
|
||||||
|
|
||||||
#define nm_ether_addr_zero (nm_ip_addr_zero.ether_addr)
|
|
||||||
|
|
||||||
static inline int
|
|
||||||
nm_ip_addr_cmp(int addr_family, gconstpointer a, gconstpointer b)
|
|
||||||
{
|
|
||||||
/* Note that @a and @b are not required to be full NMIPAddr unions.
|
|
||||||
* Depending on @addr_family, they can also be only in_addr_t or
|
|
||||||
* struct in6_addr. */
|
|
||||||
NM_CMP_SELF(a, b);
|
|
||||||
NM_CMP_DIRECT_MEMCMP(a, b, nm_utils_addr_family_to_size(addr_family));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nm_ip_addr_cmp_for_sort(gconstpointer a, gconstpointer b, gpointer user_data);
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
nm_ip_addr_equal(int addr_family, gconstpointer a, gconstpointer b)
|
|
||||||
{
|
|
||||||
return nm_ip_addr_cmp(addr_family, a, b) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
nm_ip_addr_is_null(int addr_family, gconstpointer addr)
|
|
||||||
{
|
|
||||||
nm_assert(addr);
|
|
||||||
|
|
||||||
if (NM_IS_IPv4(addr_family)) {
|
|
||||||
in_addr_t t;
|
|
||||||
|
|
||||||
/* also for in_addr_t type (AF_INET), we accept that the pointer might
|
|
||||||
* be unaligned. */
|
|
||||||
memcpy(&t, addr, sizeof(t));
|
|
||||||
return t == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return IN6_IS_ADDR_UNSPECIFIED((const struct in6_addr *) addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
nm_ip_addr_set(int addr_family, gpointer dst, gconstpointer src)
|
|
||||||
{
|
|
||||||
nm_assert(dst);
|
|
||||||
nm_assert(src);
|
|
||||||
|
|
||||||
/* this MUST use memcpy() to support unaligned src/dst pointers. */
|
|
||||||
memcpy(dst, src, nm_utils_addr_family_to_size(addr_family));
|
|
||||||
|
|
||||||
/* Note that @dst is not necessarily a NMIPAddr, it could also be just
|
|
||||||
* an in_addr_t/struct in6_addr. We thus can only set the bytes that
|
|
||||||
* we know are present based on the address family.
|
|
||||||
*
|
|
||||||
* Using this function to initialize an NMIPAddr union (for IPv4) leaves
|
|
||||||
* uninitalized bytes. Avoid that by using nm_ip_addr_init() instead. */
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline NMIPAddr
|
|
||||||
nm_ip_addr_init(int addr_family, gconstpointer src)
|
|
||||||
{
|
|
||||||
NMIPAddr a;
|
|
||||||
|
|
||||||
nm_assert_addr_family(addr_family);
|
|
||||||
nm_assert(src);
|
|
||||||
|
|
||||||
G_STATIC_ASSERT_EXPR(sizeof(NMIPAddr) == sizeof(struct in6_addr));
|
|
||||||
|
|
||||||
/* this MUST use memcpy() to support unaligned src/dst pointers. */
|
|
||||||
|
|
||||||
if (NM_IS_IPv4(addr_family)) {
|
|
||||||
memcpy(&a, src, sizeof(in_addr_t));
|
|
||||||
|
|
||||||
/* ensure all bytes of the union are initialized. If only to make
|
|
||||||
* valgrind happy. */
|
|
||||||
memset(&a.array[sizeof(in_addr_t)], 0, sizeof(a) - sizeof(in_addr_t));
|
|
||||||
} else
|
|
||||||
memcpy(&a, src, sizeof(struct in6_addr));
|
|
||||||
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean nm_ip_addr_set_from_untrusted(int addr_family,
|
|
||||||
gpointer dst,
|
|
||||||
gconstpointer src,
|
|
||||||
gsize src_len,
|
|
||||||
int *out_addr_family);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
nm_ip_addr_set_from_variant(int addr_family, gpointer dst, GVariant *variant, int *out_addr_family);
|
|
||||||
|
|
||||||
static inline gconstpointer
|
|
||||||
nm_ip_addr_from_packed_array(int addr_family, gconstpointer ipaddr_arr, gsize idx)
|
|
||||||
{
|
|
||||||
return NM_IS_IPv4(addr_family)
|
|
||||||
? ((gconstpointer) & (((const struct in_addr *) ipaddr_arr)[idx]))
|
|
||||||
: ((gconstpointer) & (((const struct in6_addr *) ipaddr_arr)[idx]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
struct ether_addr;
|
struct ether_addr;
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
|
|
@ -396,220 +274,6 @@ gboolean nm_utils_get_ipv6_interface_identifier(NMLinkType link_type,
|
||||||
NMUtilsIPv6IfaceId *out_iid);
|
NMUtilsIPv6IfaceId *out_iid);
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static inline guint32
|
|
||||||
_nm_utils_ip4_netmask_to_prefix(in_addr_t subnetmask)
|
|
||||||
{
|
|
||||||
G_STATIC_ASSERT_EXPR(__SIZEOF_INT__ == 4);
|
|
||||||
G_STATIC_ASSERT_EXPR(sizeof(int) == 4);
|
|
||||||
G_STATIC_ASSERT_EXPR(sizeof(guint) == 4);
|
|
||||||
G_STATIC_ASSERT_EXPR(sizeof(subnetmask) == 4);
|
|
||||||
|
|
||||||
return ((subnetmask != 0u) ? (guint32) (32 - __builtin_ctz(ntohl(subnetmask))) : 0u);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* _nm_utils_ip4_prefix_to_netmask:
|
|
||||||
* @prefix: a CIDR prefix
|
|
||||||
*
|
|
||||||
* Returns: the netmask represented by the prefix, in network byte order
|
|
||||||
**/
|
|
||||||
static inline in_addr_t
|
|
||||||
_nm_utils_ip4_prefix_to_netmask(guint32 prefix)
|
|
||||||
{
|
|
||||||
nm_assert(prefix <= 32);
|
|
||||||
return prefix < 32 ? ~htonl(0xFFFFFFFFu >> prefix) : 0xFFFFFFFFu;
|
|
||||||
}
|
|
||||||
|
|
||||||
guint32 _nm_utils_ip4_get_default_prefix0(in_addr_t ip);
|
|
||||||
guint32 _nm_utils_ip4_get_default_prefix(in_addr_t ip);
|
|
||||||
|
|
||||||
gconstpointer
|
|
||||||
nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint32 plen);
|
|
||||||
|
|
||||||
/* nm_utils_ip4_address_clear_host_address:
|
|
||||||
* @addr: source ip6 address
|
|
||||||
* @plen: prefix length of network
|
|
||||||
*
|
|
||||||
* returns: the input address, with the host address set to 0.
|
|
||||||
*/
|
|
||||||
static inline in_addr_t
|
|
||||||
nm_utils_ip4_address_clear_host_address(in_addr_t addr, guint32 plen)
|
|
||||||
{
|
|
||||||
return addr & _nm_utils_ip4_prefix_to_netmask(plen);
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct in6_addr *nm_utils_ip6_address_clear_host_address(struct in6_addr *dst,
|
|
||||||
const struct in6_addr *src,
|
|
||||||
guint32 plen);
|
|
||||||
|
|
||||||
static inline int
|
|
||||||
nm_utils_ip4_address_same_prefix_cmp(in_addr_t addr_a, in_addr_t addr_b, guint32 plen)
|
|
||||||
{
|
|
||||||
NM_CMP_DIRECT(htonl(nm_utils_ip4_address_clear_host_address(addr_a, plen)),
|
|
||||||
htonl(nm_utils_ip4_address_clear_host_address(addr_b, plen)));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a,
|
|
||||||
const struct in6_addr *addr_b,
|
|
||||||
guint32 plen);
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
nm_utils_ip4_address_same_prefix(in_addr_t addr_a, in_addr_t addr_b, guint32 plen)
|
|
||||||
{
|
|
||||||
return nm_utils_ip4_address_same_prefix_cmp(addr_a, addr_b, plen) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
nm_utils_ip6_address_same_prefix(const struct in6_addr *addr_a,
|
|
||||||
const struct in6_addr *addr_b,
|
|
||||||
guint8 plen)
|
|
||||||
{
|
|
||||||
return nm_utils_ip6_address_same_prefix_cmp(addr_a, addr_b, plen) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
|
||||||
nm_utils_ip_address_same_prefix_cmp(int addr_family,
|
|
||||||
gconstpointer addr_a,
|
|
||||||
gconstpointer addr_b,
|
|
||||||
guint8 plen)
|
|
||||||
{
|
|
||||||
NM_CMP_SELF(addr_a, addr_b);
|
|
||||||
|
|
||||||
if (NM_IS_IPv4(addr_family)) {
|
|
||||||
return nm_utils_ip4_address_same_prefix_cmp(*((const in_addr_t *) addr_a),
|
|
||||||
*((const in_addr_t *) addr_b),
|
|
||||||
plen);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nm_utils_ip6_address_same_prefix_cmp(addr_a, addr_b, plen);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
nm_utils_ip_address_same_prefix(int addr_family,
|
|
||||||
gconstpointer addr_a,
|
|
||||||
gconstpointer addr_b,
|
|
||||||
guint8 plen)
|
|
||||||
{
|
|
||||||
return nm_utils_ip_address_same_prefix_cmp(addr_family, addr_a, addr_b, plen) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX(a, b, plen) \
|
|
||||||
NM_CMP_RETURN(nm_utils_ip4_address_same_prefix_cmp((a), (b), (plen)))
|
|
||||||
|
|
||||||
#define NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX(a, b, plen) \
|
|
||||||
NM_CMP_RETURN(nm_utils_ip6_address_same_prefix_cmp((a), (b), (plen)))
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
gboolean nm_utils_ip_is_site_local(int addr_family, const void *address);
|
|
||||||
gboolean nm_utils_ip6_is_ula(const struct in6_addr *address);
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
#define NM_IPV4LL_NETWORK ((in_addr_t) htonl(0xA9FE0000lu))
|
|
||||||
#define NM_IPV4LL_NETMASK ((in_addr_t) htonl(0xFFFF0000lu))
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
nm_utils_ip4_address_is_loopback(in_addr_t addr)
|
|
||||||
{
|
|
||||||
/* There is also IN_LOOPBACK() in <linux/in.h>, but there the
|
|
||||||
* argument is in host order not `in_addr_t`. */
|
|
||||||
return (addr & htonl(0xFF000000u)) == htonl(0x7F000000u);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
nm_utils_ip4_address_is_link_local(in_addr_t addr)
|
|
||||||
{
|
|
||||||
return (addr & NM_IPV4LL_NETMASK) == NM_IPV4LL_NETWORK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline gboolean
|
|
||||||
nm_utils_ip4_address_is_zeronet(in_addr_t network)
|
|
||||||
{
|
|
||||||
/* Same as ipv4_is_zeronet() from kernel's include/linux/in.h. */
|
|
||||||
return (network & htonl(0xFF000000u)) == htonl(0x00000000u);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
#define NM_UTILS_INET_ADDRSTRLEN INET6_ADDRSTRLEN
|
|
||||||
|
|
||||||
/* Forward declare function so we don't have to drag in <arpa/inet.h>. */
|
|
||||||
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
|
|
||||||
|
|
||||||
static inline const char *
|
|
||||||
nm_utils_inet_ntop(int addr_family, gconstpointer addr, char *dst)
|
|
||||||
{
|
|
||||||
const char *s;
|
|
||||||
|
|
||||||
nm_assert_addr_family(addr_family);
|
|
||||||
nm_assert(addr);
|
|
||||||
nm_assert(dst);
|
|
||||||
|
|
||||||
s = inet_ntop(addr_family,
|
|
||||||
addr,
|
|
||||||
dst,
|
|
||||||
addr_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN);
|
|
||||||
nm_assert(s);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline const char *
|
|
||||||
_nm_utils_inet4_ntop(in_addr_t addr, char dst[static INET_ADDRSTRLEN])
|
|
||||||
{
|
|
||||||
return nm_utils_inet_ntop(AF_INET, &addr, dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline const char *
|
|
||||||
_nm_utils_inet6_ntop(const struct in6_addr *addr, char dst[static INET6_ADDRSTRLEN])
|
|
||||||
{
|
|
||||||
return nm_utils_inet_ntop(AF_INET6, addr, dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline char *
|
|
||||||
nm_utils_inet_ntop_dup(int addr_family, gconstpointer addr)
|
|
||||||
{
|
|
||||||
char buf[NM_UTILS_INET_ADDRSTRLEN];
|
|
||||||
|
|
||||||
return g_strdup(nm_utils_inet_ntop(addr_family, addr, buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline char *
|
|
||||||
nm_utils_inet4_ntop_dup(in_addr_t addr)
|
|
||||||
{
|
|
||||||
return nm_utils_inet_ntop_dup(AF_INET, &addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline char *
|
|
||||||
nm_utils_inet6_ntop_dup(const struct in6_addr *addr)
|
|
||||||
{
|
|
||||||
return nm_utils_inet_ntop_dup(AF_INET6, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
gboolean nm_utils_ipaddr_is_valid(int addr_family, const char *str_addr);
|
|
||||||
|
|
||||||
gboolean nm_utils_ipaddr_is_normalized(int addr_family, const char *str_addr);
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
/* this enum is compatible with ICMPV6_ROUTER_PREF_* (from <linux/icmpv6.h>,
|
|
||||||
* the values for netlink attribute RTA_PREF) and "enum ndp_route_preference"
|
|
||||||
* from <ndp.h>. */
|
|
||||||
typedef enum _nm_packed {
|
|
||||||
NM_ICMPV6_ROUTER_PREF_MEDIUM = 0x0, /* ICMPV6_ROUTER_PREF_MEDIUM */
|
|
||||||
NM_ICMPV6_ROUTER_PREF_LOW = 0x3, /* ICMPV6_ROUTER_PREF_LOW */
|
|
||||||
NM_ICMPV6_ROUTER_PREF_HIGH = 0x1, /* ICMPV6_ROUTER_PREF_HIGH */
|
|
||||||
NM_ICMPV6_ROUTER_PREF_INVALID = 0x2, /* ICMPV6_ROUTER_PREF_INVALID */
|
|
||||||
} NMIcmpv6RouterPref;
|
|
||||||
|
|
||||||
const char *nm_icmpv6_router_pref_to_string(NMIcmpv6RouterPref pref, char *buf, gsize len);
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
gboolean nm_utils_memeqzero(gconstpointer data, gsize length);
|
gboolean nm_utils_memeqzero(gconstpointer data, gsize length);
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
@ -1042,31 +706,6 @@ nm_utils_escaped_tokens_options_escape_val(const char *val, char **out_to_free)
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
gboolean nm_utils_parse_inaddr_bin_full(int addr_family,
|
|
||||||
gboolean accept_legacy,
|
|
||||||
const char *text,
|
|
||||||
int *out_addr_family,
|
|
||||||
gpointer out_addr);
|
|
||||||
static inline gboolean
|
|
||||||
nm_utils_parse_inaddr_bin(int addr_family,
|
|
||||||
const char *text,
|
|
||||||
int *out_addr_family,
|
|
||||||
gpointer out_addr)
|
|
||||||
{
|
|
||||||
return nm_utils_parse_inaddr_bin_full(addr_family, FALSE, text, out_addr_family, out_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean nm_utils_parse_inaddr(int addr_family, const char *text, char **out_addr);
|
|
||||||
|
|
||||||
gboolean nm_utils_parse_inaddr_prefix_bin(int addr_family,
|
|
||||||
const char *text,
|
|
||||||
int *out_addr_family,
|
|
||||||
gpointer out_addr,
|
|
||||||
int *out_prefix);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
nm_utils_parse_inaddr_prefix(int addr_family, const char *text, char **out_addr, int *out_prefix);
|
|
||||||
|
|
||||||
gboolean nm_utils_parse_next_line(const char **inout_ptr,
|
gboolean nm_utils_parse_next_line(const char **inout_ptr,
|
||||||
gsize *inout_len,
|
gsize *inout_len,
|
||||||
const char **out_line,
|
const char **out_line,
|
||||||
|
|
@ -1658,6 +1297,10 @@ nm_g_variant_new_au(const guint32 *data, gsize len)
|
||||||
return g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, data, len, sizeof(guint32));
|
return g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, data, len, sizeof(guint32));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct _NMIPAddr;
|
||||||
|
|
||||||
|
extern const struct _NMIPAddr nm_ip_addr_zero;
|
||||||
|
|
||||||
static inline GVariant *
|
static inline GVariant *
|
||||||
nm_g_variant_new_ay_inaddr(int addr_family, gconstpointer addr)
|
nm_g_variant_new_ay_inaddr(int addr_family, gconstpointer addr)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue