diff options
author | Thomas Haller <thaller@redhat.com> | 2021-02-18 08:13:35 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2021-02-24 12:48:17 +0100 |
commit | 24393744572316d4371ce2ff2c112fb6c3226448 (patch) | |
tree | 67887fd4983f5c0ef702ee534becc5f446d08629 /shared | |
parent | 39225258d6a8fb0612c4576a75ac24d572742372 (diff) | |
download | NetworkManager-24393744572316d4371ce2ff2c112fb6c3226448.tar.gz |
build: move "shared/nm-platform" to "src/libnm-platform"
Diffstat (limited to 'shared')
-rw-r--r-- | shared/meson.build | 21 | ||||
-rw-r--r-- | shared/nm-platform/nm-netlink.c | 1518 | ||||
-rw-r--r-- | shared/nm-platform/nm-netlink.h | 616 | ||||
-rw-r--r-- | shared/nm-platform/nm-platform-utils.c | 1807 | ||||
-rw-r--r-- | shared/nm-platform/nm-platform-utils.h | 73 | ||||
-rw-r--r-- | shared/nm-platform/nmp-base.h | 94 | ||||
-rw-r--r-- | shared/nm-platform/nmp-netns.c | 767 | ||||
-rw-r--r-- | shared/nm-platform/nmp-netns.h | 56 | ||||
-rw-r--r-- | shared/nm-platform/tests/meson.build | 17 | ||||
-rw-r--r-- | shared/nm-platform/tests/test-nm-platform.c | 116 |
10 files changed, 0 insertions, 5085 deletions
diff --git a/shared/meson.build b/shared/meson.build index 955d80eadb..e07c66cca3 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -202,27 +202,6 @@ libnm_log_null = static_library( dependencies: glib_nm_default_dep, ) -libnm_platform = static_library( - 'nm-platform', - sources: [ - 'nm-platform/nm-netlink.c', - 'nm-platform/nm-platform-utils.c', - 'nm-platform/nmp-netns.c', - ], - dependencies: [ - glib_nm_default_dep, - ], -) - -libnm_platform_dep = declare_dependency( - include_directories: shared_inc, - dependencies: [ - libnm_glib_aux_dep_link, - ], - link_with: libnm_platform, -) - if enable_tests subdir('nm-glib-aux/tests') - subdir('nm-platform/tests') endif diff --git a/shared/nm-platform/nm-netlink.c b/shared/nm-platform/nm-netlink.c deleted file mode 100644 index 7a6d7e045b..0000000000 --- a/shared/nm-platform/nm-netlink.c +++ /dev/null @@ -1,1518 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-netlink.h" - -#include <unistd.h> -#include <fcntl.h> - -/*****************************************************************************/ - -#ifndef SOL_NETLINK - #define SOL_NETLINK 270 -#endif - -/*****************************************************************************/ - -#define NL_SOCK_PASSCRED (1 << 1) -#define NL_MSG_PEEK (1 << 3) -#define NL_MSG_PEEK_EXPLICIT (1 << 4) -#define NL_NO_AUTO_ACK (1 << 5) - -#ifndef NETLINK_EXT_ACK - #define NETLINK_EXT_ACK 11 -#endif - -struct nl_msg { - int nm_protocol; - struct sockaddr_nl nm_src; - struct sockaddr_nl nm_dst; - struct ucred nm_creds; - struct nlmsghdr * nm_nlh; - size_t nm_size; - bool nm_creds_has : 1; -}; - -struct nl_sock { - struct sockaddr_nl s_local; - struct sockaddr_nl s_peer; - int s_fd; - int s_proto; - unsigned int s_seq_next; - unsigned int s_seq_expect; - int s_flags; - size_t s_bufsize; -}; - -/*****************************************************************************/ - -NM_UTILS_ENUM2STR_DEFINE(nl_nlmsgtype2str, - int, - NM_UTILS_ENUM2STR(NLMSG_NOOP, "NOOP"), - NM_UTILS_ENUM2STR(NLMSG_ERROR, "ERROR"), - NM_UTILS_ENUM2STR(NLMSG_DONE, "DONE"), - NM_UTILS_ENUM2STR(NLMSG_OVERRUN, "OVERRUN"), ); - -NM_UTILS_FLAGS2STR_DEFINE(nl_nlmsg_flags2str, - int, - NM_UTILS_FLAGS2STR(NLM_F_REQUEST, "REQUEST"), - NM_UTILS_FLAGS2STR(NLM_F_MULTI, "MULTI"), - NM_UTILS_FLAGS2STR(NLM_F_ACK, "ACK"), - NM_UTILS_FLAGS2STR(NLM_F_ECHO, "ECHO"), - NM_UTILS_FLAGS2STR(NLM_F_ROOT, "ROOT"), - NM_UTILS_FLAGS2STR(NLM_F_MATCH, "MATCH"), - NM_UTILS_FLAGS2STR(NLM_F_ATOMIC, "ATOMIC"), - NM_UTILS_FLAGS2STR(NLM_F_REPLACE, "REPLACE"), - NM_UTILS_FLAGS2STR(NLM_F_EXCL, "EXCL"), - NM_UTILS_FLAGS2STR(NLM_F_CREATE, "CREATE"), - NM_UTILS_FLAGS2STR(NLM_F_APPEND, "APPEND"), ); - -/*****************************************************************************/ - -const char * -nl_nlmsghdr_to_str(const struct nlmsghdr *hdr, char *buf, gsize len) -{ - const char *b; - const char *s; - guint flags, flags_before; - const char *prefix; - - if (!nm_utils_to_string_buffer_init_null(hdr, &buf, &len)) - return buf; - - b = buf; - - switch (hdr->nlmsg_type) { - case RTM_GETLINK: - s = "RTM_GETLINK"; - break; - case RTM_NEWLINK: - s = "RTM_NEWLINK"; - break; - case RTM_DELLINK: - s = "RTM_DELLINK"; - break; - case RTM_SETLINK: - s = "RTM_SETLINK"; - break; - case RTM_GETADDR: - s = "RTM_GETADDR"; - break; - case RTM_NEWADDR: - s = "RTM_NEWADDR"; - break; - case RTM_DELADDR: - s = "RTM_DELADDR"; - break; - case RTM_GETROUTE: - s = "RTM_GETROUTE"; - break; - case RTM_NEWROUTE: - s = "RTM_NEWROUTE"; - break; - case RTM_DELROUTE: - s = "RTM_DELROUTE"; - break; - case RTM_GETRULE: - s = "RTM_GETRULE"; - break; - case RTM_NEWRULE: - s = "RTM_NEWRULE"; - break; - case RTM_DELRULE: - s = "RTM_DELRULE"; - break; - case RTM_GETQDISC: - s = "RTM_GETQDISC"; - break; - case RTM_NEWQDISC: - s = "RTM_NEWQDISC"; - break; - case RTM_DELQDISC: - s = "RTM_DELQDISC"; - break; - case RTM_GETTFILTER: - s = "RTM_GETTFILTER"; - break; - case RTM_NEWTFILTER: - s = "RTM_NEWTFILTER"; - break; - case RTM_DELTFILTER: - s = "RTM_DELTFILTER"; - break; - case NLMSG_NOOP: - s = "NLMSG_NOOP"; - break; - case NLMSG_ERROR: - s = "NLMSG_ERROR"; - break; - case NLMSG_DONE: - s = "NLMSG_DONE"; - break; - case NLMSG_OVERRUN: - s = "NLMSG_OVERRUN"; - break; - default: - s = NULL; - break; - } - - if (s) - nm_utils_strbuf_append_str(&buf, &len, s); - else - nm_utils_strbuf_append(&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type); - - flags = hdr->nlmsg_flags; - - if (!flags) { - nm_utils_strbuf_append_str(&buf, &len, ", flags 0"); - goto flags_done; - } - -#define _F(f, n) \ - G_STMT_START \ - { \ - if (NM_FLAGS_ALL(flags, f)) { \ - flags &= ~(f); \ - nm_utils_strbuf_append(&buf, &len, "%s%s", prefix, n); \ - if (!flags) \ - goto flags_done; \ - prefix = ","; \ - } \ - } \ - G_STMT_END - - prefix = ", flags "; - flags_before = flags; - _F(NLM_F_REQUEST, "request"); - _F(NLM_F_MULTI, "multi"); - _F(NLM_F_ACK, "ack"); - _F(NLM_F_ECHO, "echo"); - _F(NLM_F_DUMP_INTR, "dump_intr"); - _F(0x20 /*NLM_F_DUMP_FILTERED*/, "dump_filtered"); - - if (flags_before != flags) - prefix = ";"; - - switch (hdr->nlmsg_type) { - case RTM_NEWLINK: - case RTM_NEWADDR: - case RTM_NEWROUTE: - case RTM_NEWQDISC: - case RTM_NEWTFILTER: - _F(NLM_F_REPLACE, "replace"); - _F(NLM_F_EXCL, "excl"); - _F(NLM_F_CREATE, "create"); - _F(NLM_F_APPEND, "append"); - break; - case RTM_GETLINK: - case RTM_GETADDR: - case RTM_GETROUTE: - case RTM_DELQDISC: - case RTM_DELTFILTER: - _F(NLM_F_DUMP, "dump"); - _F(NLM_F_ROOT, "root"); - _F(NLM_F_MATCH, "match"); - _F(NLM_F_ATOMIC, "atomic"); - break; - } - -#undef _F - - if (flags_before != flags) - prefix = ";"; - nm_utils_strbuf_append(&buf, &len, "%s0x%04x", prefix, flags); - -flags_done: - - nm_utils_strbuf_append(&buf, &len, ", seq %u", (unsigned) hdr->nlmsg_seq); - - return b; -} - -/*****************************************************************************/ - -struct nlmsghdr * -nlmsg_hdr(struct nl_msg *n) -{ - return n->nm_nlh; -} - -void * -nlmsg_reserve(struct nl_msg *n, size_t len, int pad) -{ - char * buf = (char *) n->nm_nlh; - size_t nlmsg_len = n->nm_nlh->nlmsg_len; - size_t tlen; - - nm_assert(pad >= 0); - - if (len > n->nm_size) - return NULL; - - tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; - - if ((tlen + nlmsg_len) > n->nm_size) - return NULL; - - buf += nlmsg_len; - n->nm_nlh->nlmsg_len += tlen; - - if (tlen > len) - memset(buf + len, 0, tlen - len); - - return buf; -} - -/*****************************************************************************/ - -struct nlattr * -nla_reserve(struct nl_msg *msg, int attrtype, int attrlen) -{ - struct nlattr *nla; - int tlen; - - if (attrlen < 0) - return NULL; - - tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen); - - if (tlen > msg->nm_size) - return NULL; - - nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh); - nla->nla_type = attrtype; - nla->nla_len = nla_attr_size(attrlen); - - if (attrlen) - memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); - msg->nm_nlh->nlmsg_len = tlen; - - return nla; -} - -/*****************************************************************************/ - -struct nl_msg * -nlmsg_alloc_size(size_t len) -{ - struct nl_msg *nm; - - if (len < sizeof(struct nlmsghdr)) - len = sizeof(struct nlmsghdr); - - nm = g_slice_new(struct nl_msg); - *nm = (struct nl_msg){ - .nm_protocol = -1, - .nm_size = len, - .nm_nlh = g_malloc0(len), - }; - nm->nm_nlh->nlmsg_len = nlmsg_total_size(0); - return nm; -} - -/** - * Allocate a new netlink message with the default maximum payload size. - * - * Allocates a new netlink message without any further payload. The - * maximum payload size defaults to PAGESIZE or as otherwise specified - * with nlmsg_set_default_size(). - * - * @return Newly allocated netlink message or NULL. - */ -struct nl_msg * -nlmsg_alloc(void) -{ - return nlmsg_alloc_size(nm_utils_getpagesize()); -} - -struct nl_msg * -nlmsg_alloc_convert(struct nlmsghdr *hdr) -{ - struct nl_msg *nm; - - nm = nlmsg_alloc_size(NLMSG_ALIGN(hdr->nlmsg_len)); - memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len); - return nm; -} - -struct nl_msg * -nlmsg_alloc_simple(int nlmsgtype, int flags) -{ - struct nl_msg *nm; - struct nlmsghdr *new; - - nm = nlmsg_alloc(); - new = nm->nm_nlh; - new->nlmsg_type = nlmsgtype; - new->nlmsg_flags = flags; - return nm; -} - -void -nlmsg_free(struct nl_msg *msg) -{ - if (!msg) - return; - - g_free(msg->nm_nlh); - g_slice_free(struct nl_msg, msg); -} - -/*****************************************************************************/ - -int -nlmsg_append(struct nl_msg *n, const void *data, size_t len, int pad) -{ - void *tmp; - - nm_assert(n); - nm_assert(data); - nm_assert(len > 0); - nm_assert(pad >= 0); - - tmp = nlmsg_reserve(n, len, pad); - if (tmp == NULL) - return -ENOMEM; - - memcpy(tmp, data, len); - return 0; -} - -/*****************************************************************************/ - -int -nlmsg_parse(struct nlmsghdr * nlh, - int hdrlen, - struct nlattr * tb[], - int maxtype, - const struct nla_policy *policy) -{ - if (!nlmsg_valid_hdr(nlh, hdrlen)) - return -NME_NL_MSG_TOOSHORT; - - return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), policy); -} - -struct nlmsghdr * -nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, int type, int payload, int flags) -{ - struct nlmsghdr *nlh; - - if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN) - g_return_val_if_reached(NULL); - - nlh = (struct nlmsghdr *) n->nm_nlh; - nlh->nlmsg_type = type; - nlh->nlmsg_flags = flags; - nlh->nlmsg_pid = pid; - nlh->nlmsg_seq = seq; - - if (payload > 0 && nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL) - return NULL; - - return nlh; -} - -size_t -nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize) -{ - const char *src; - size_t srclen; - size_t len; - - /* - Always writes @dstsize bytes to @dst - * - Copies the first non-NUL characters to @dst. - * Any characters after the first NUL bytes in @nla are ignored. - * - If the string @nla is longer than @dstsize, the string - * gets truncated. @dst will always be NUL terminated. */ - - if (G_UNLIKELY(dstsize <= 1)) { - if (dstsize == 1) - dst[0] = '\0'; - if (nla && (srclen = nla_len(nla)) > 0) - return strnlen(nla_data(nla), srclen); - return 0; - } - - nm_assert(dst); - - if (nla) { - srclen = nla_len(nla); - if (srclen > 0) { - src = nla_data(nla); - srclen = strnlen(src, srclen); - if (srclen > 0) { - len = NM_MIN(dstsize - 1, srclen); - memcpy(dst, src, len); - memset(&dst[len], 0, dstsize - len); - return srclen; - } - } - } - - memset(dst, 0, dstsize); - return 0; -} - -size_t -nla_memcpy(void *dst, const struct nlattr *nla, size_t dstsize) -{ - size_t len; - int srclen; - - if (!nla) - return 0; - - srclen = nla_len(nla); - - if (srclen <= 0) { - nm_assert(srclen == 0); - return 0; - } - - len = NM_MIN((size_t) srclen, dstsize); - if (len > 0) { - /* there is a crucial difference between nla_strlcpy() and nla_memcpy(). - * The former always write @dstsize bytes (akin to strncpy()), here, we only - * write the bytes that we actually have (leaving the remainder undefined). */ - memcpy(dst, nla_data(nla), len); - } - - return srclen; -} - -int -nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data) -{ - struct nlattr *nla; - - nla = nla_reserve(msg, attrtype, datalen); - if (!nla) { - if (datalen < 0) - g_return_val_if_reached(-NME_BUG); - - return -ENOMEM; - } - - if (datalen > 0) - memcpy(nla_data(nla), data, datalen); - - return 0; -} - -struct nlattr * -nla_find(const struct nlattr *head, int len, int attrtype) -{ - const struct nlattr *nla; - int rem; - - nla_for_each_attr (nla, head, len, rem) { - if (nla_type(nla) == attrtype) - return (struct nlattr *) nla; - } - - return NULL; -} - -void -nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr) -{ - ssize_t len; - - len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) attr; - if (len < 0) - g_return_if_reached(); - else if (len > 0) { - msg->nm_nlh->nlmsg_len -= len; - memset(nlmsg_tail(msg->nm_nlh), 0, len); - } -} - -struct nlattr * -nla_nest_start(struct nl_msg *msg, int attrtype) -{ - struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh); - - if (nla_put(msg, NLA_F_NESTED | attrtype, 0, NULL) < 0) - return NULL; - - return start; -} - -static int -_nest_end(struct nl_msg *msg, struct nlattr *start, int keep_empty) -{ - size_t pad, len; - - len = (char *) nlmsg_tail(msg->nm_nlh) - (char *) start; - - if (len > USHRT_MAX || (!keep_empty && len == NLA_HDRLEN)) { - /* - * Max nlattr size exceeded or empty nested attribute, trim the - * attribute header again - */ - nla_nest_cancel(msg, start); - - /* Return error only if nlattr size was exceeded */ - return (len == NLA_HDRLEN) ? 0 : -NME_NL_ATTRSIZE; - } - - start->nla_len = len; - - pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len; - if (pad > 0) { - /* - * Data inside attribute does not end at a alignment boundary. - * Pad accordingly and account for the additional space in - * the message. nlmsg_reserve() may never fail in this situation, - * the allocate message buffer must be a multiple of NLMSG_ALIGNTO. - */ - if (!nlmsg_reserve(msg, pad, 0)) - g_return_val_if_reached(-NME_BUG); - } - - return 0; -} - -int -nla_nest_end(struct nl_msg *msg, struct nlattr *start) -{ - return _nest_end(msg, start, 0); -} - -static const uint16_t nla_attr_minlen[NLA_TYPE_MAX + 1] = { - [NLA_U8] = sizeof(uint8_t), - [NLA_U16] = sizeof(uint16_t), - [NLA_U32] = sizeof(uint32_t), - [NLA_U64] = sizeof(uint64_t), - [NLA_STRING] = 1, - [NLA_FLAG] = 0, -}; - -static int -validate_nla(const struct nlattr *nla, int maxtype, const struct nla_policy *policy) -{ - const struct nla_policy *pt; - unsigned int minlen = 0; - int type = nla_type(nla); - - if (type < 0 || type > maxtype) - return 0; - - pt = &policy[type]; - - if (pt->type > NLA_TYPE_MAX) - g_return_val_if_reached(-NME_BUG); - - if (pt->minlen) - minlen = pt->minlen; - else if (pt->type != NLA_UNSPEC) - minlen = nla_attr_minlen[pt->type]; - - if (nla_len(nla) < minlen) - return -NME_UNSPEC; - - if (pt->maxlen && nla_len(nla) > pt->maxlen) - return -NME_UNSPEC; - - if (pt->type == NLA_STRING) { - const char *data; - - nm_assert(minlen > 0); - - data = nla_data(nla); - if (data[nla_len(nla) - 1] != '\0') - return -NME_UNSPEC; - } - - return 0; -} - -int -nla_parse(struct nlattr * tb[], - int maxtype, - struct nlattr * head, - int len, - const struct nla_policy *policy) -{ - struct nlattr *nla; - int rem, nmerr; - - memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); - - nla_for_each_attr (nla, head, len, rem) { - int type = nla_type(nla); - - if (type > maxtype) - continue; - - if (policy) { - nmerr = validate_nla(nla, maxtype, policy); - if (nmerr < 0) - return nmerr; - } - - tb[type] = nla; - } - - return 0; -} - -/*****************************************************************************/ - -int -nlmsg_get_proto(struct nl_msg *msg) -{ - return msg->nm_protocol; -} - -void -nlmsg_set_proto(struct nl_msg *msg, int protocol) -{ - msg->nm_protocol = protocol; -} - -void -nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr) -{ - memcpy(&msg->nm_src, addr, sizeof(*addr)); -} - -struct ucred * -nlmsg_get_creds(struct nl_msg *msg) -{ - if (msg->nm_creds_has) - return &msg->nm_creds; - return NULL; -} - -void -nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds) -{ - if (creds) { - memcpy(&msg->nm_creds, creds, sizeof(*creds)); - msg->nm_creds_has = TRUE; - } else - msg->nm_creds_has = FALSE; -} - -/*****************************************************************************/ - -void * -genlmsg_put(struct nl_msg *msg, - uint32_t port, - uint32_t seq, - int family, - int hdrlen, - int flags, - uint8_t cmd, - uint8_t version) -{ - struct nlmsghdr * nlh; - struct genlmsghdr hdr = { - .cmd = cmd, - .version = version, - }; - - nlh = nlmsg_put(msg, port, seq, family, GENL_HDRLEN + hdrlen, flags); - if (nlh == NULL) - return NULL; - - memcpy(nlmsg_data(nlh), &hdr, sizeof(hdr)); - - return (char *) nlmsg_data(nlh) + GENL_HDRLEN; -} - -void * -genlmsg_data(const struct genlmsghdr *gnlh) -{ - return ((unsigned char *) gnlh + GENL_HDRLEN); -} - -void * -genlmsg_user_hdr(const struct genlmsghdr *gnlh) -{ - return genlmsg_data(gnlh); -} - -struct genlmsghdr * -genlmsg_hdr(struct nlmsghdr *nlh) -{ - return nlmsg_data(nlh); -} - -void * -genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen) -{ - return (char *) genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen); -} - -struct nlattr * -genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) -{ - return genlmsg_user_data(gnlh, hdrlen); -} - -int -genlmsg_len(const struct genlmsghdr *gnlh) -{ - const struct nlmsghdr *nlh; - - nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN); - return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); -} - -int -genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) -{ - return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen); -} - -int -genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen) -{ - struct genlmsghdr *ghdr; - - if (!nlmsg_valid_hdr(nlh, GENL_HDRLEN)) - return 0; - - ghdr = nlmsg_data(nlh); - if (genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen)) - return 0; - - return 1; -} - -int -genlmsg_parse(struct nlmsghdr * nlh, - int hdrlen, - struct nlattr * tb[], - int maxtype, - const struct nla_policy *policy) -{ - struct genlmsghdr *ghdr; - - if (!genlmsg_valid_hdr(nlh, hdrlen)) - return -NME_NL_MSG_TOOSHORT; - - ghdr = nlmsg_data(nlh); - return nla_parse(tb, - maxtype, - genlmsg_attrdata(ghdr, hdrlen), - genlmsg_attrlen(ghdr, hdrlen), - policy); -} - -static int -_genl_parse_getfamily(struct nl_msg *msg, void *arg) -{ - static const struct nla_policy ctrl_policy[] = { - [CTRL_ATTR_FAMILY_ID] = {.type = NLA_U16}, - [CTRL_ATTR_FAMILY_NAME] = {.type = NLA_STRING, .maxlen = GENL_NAMSIZ}, - [CTRL_ATTR_VERSION] = {.type = NLA_U32}, - [CTRL_ATTR_HDRSIZE] = {.type = NLA_U32}, - [CTRL_ATTR_MAXATTR] = {.type = NLA_U32}, - [CTRL_ATTR_OPS] = {.type = NLA_NESTED}, - [CTRL_ATTR_MCAST_GROUPS] = {.type = NLA_NESTED}, - }; - struct nlattr * tb[G_N_ELEMENTS(ctrl_policy)]; - struct nlmsghdr *nlh = nlmsg_hdr(msg); - gint32 * response_data = arg; - - if (genlmsg_parse_arr(nlh, 0, tb, ctrl_policy) < 0) - return NL_SKIP; - - if (tb[CTRL_ATTR_FAMILY_ID]) - *response_data = nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]); - - return NL_STOP; -} - -int -genl_ctrl_resolve(struct nl_sock *sk, const char *name) -{ - nm_auto_nlmsg struct nl_msg *msg = NULL; - int nmerr; - gint32 response_data = -1; - const struct nl_cb cb = { - .valid_cb = _genl_parse_getfamily, - .valid_arg = &response_data, - }; - - msg = nlmsg_alloc(); - - if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY, 1)) - return -ENOMEM; - - nmerr = nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, name); - if (nmerr < 0) - return nmerr; - - nmerr = nl_send_auto(sk, msg); - if (nmerr < 0) - return nmerr; - - nmerr = nl_recvmsgs(sk, &cb); - if (nmerr < 0) - return nmerr; - - /* If search was successful, request may be ACKed after data */ - nmerr = nl_wait_for_ack(sk, NULL); - if (nmerr < 0) - return nmerr; - - if (response_data < 0) - return -NME_UNSPEC; - - return response_data; -} - -/*****************************************************************************/ - -struct nl_sock * -nl_socket_alloc(void) -{ - struct nl_sock *sk; - - sk = g_slice_new0(struct nl_sock); - - sk->s_fd = -1; - sk->s_local.nl_family = AF_NETLINK; - sk->s_peer.nl_family = AF_NETLINK; - sk->s_seq_expect = sk->s_seq_next = time(NULL); - - return sk; -} - -void -nl_socket_free(struct nl_sock *sk) -{ - if (!sk) - return; - - if (sk->s_fd >= 0) - nm_close(sk->s_fd); - g_slice_free(struct nl_sock, sk); -} - -int -nl_socket_get_fd(const struct nl_sock *sk) -{ - return sk->s_fd; -} - -uint32_t -nl_socket_get_local_port(const struct nl_sock *sk) -{ - return sk->s_local.nl_pid; -} - -size_t -nl_socket_get_msg_buf_size(struct nl_sock *sk) -{ - return sk->s_bufsize; -} - -int -nl_socket_set_passcred(struct nl_sock *sk, int state) -{ - int err; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED, &state, sizeof(state)); - if (err < 0) - return -nm_errno_from_native(errno); - - if (state) - sk->s_flags |= NL_SOCK_PASSCRED; - else - sk->s_flags &= ~NL_SOCK_PASSCRED; - - return 0; -} - -int -nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize) -{ - sk->s_bufsize = bufsize; - - return 0; -} - -struct sockaddr_nl * -nlmsg_get_dst(struct nl_msg *msg) -{ - return &msg->nm_dst; -} - -int -nl_socket_set_nonblocking(const struct nl_sock *sk) -{ - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0) - return -nm_errno_from_native(errno); - - return 0; -} - -int -nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf) -{ - int err; - - if (rxbuf <= 0) - rxbuf = 32768; - - if (txbuf <= 0) - txbuf = 32768; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, &txbuf, sizeof(txbuf)); - if (err < 0) { - return -nm_errno_from_native(errno); - } - - err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF, &rxbuf, sizeof(rxbuf)); - if (err < 0) { - return -nm_errno_from_native(errno); - } - - return 0; -} - -int -nl_socket_add_memberships(struct nl_sock *sk, int group, ...) -{ - int err; - va_list ap; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - va_start(ap, group); - - while (group != 0) { - if (group < 0) { - va_end(ap); - g_return_val_if_reached(-NME_BUG); - } - - err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); - if (err < 0) { - int errsv = errno; - - va_end(ap); - return -nm_errno_from_native(errsv); - } - - group = va_arg(ap, int); - } - - va_end(ap); - - return 0; -} - -int -nl_socket_set_ext_ack(struct nl_sock *sk, gboolean enable) -{ - int err, val; - - if (sk->s_fd == -1) - return -NME_NL_BAD_SOCK; - - val = !!enable; - err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_EXT_ACK, &val, sizeof(val)); - if (err < 0) - return -nm_errno_from_native(errno); - - return 0; -} - -void -nl_socket_disable_msg_peek(struct nl_sock *sk) -{ - sk->s_flags |= NL_MSG_PEEK_EXPLICIT; - sk->s_flags &= ~NL_MSG_PEEK; -} - -int -nl_connect(struct nl_sock *sk, int protocol) -{ - int err, nmerr; - socklen_t addrlen; - struct sockaddr_nl local = {0}; - - if (sk->s_fd != -1) - return -NME_NL_BAD_SOCK; - - sk->s_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, protocol); - if (sk->s_fd < 0) { - nmerr = -nm_errno_from_native(errno); - goto errout; - } - - nmerr = nl_socket_set_buffer_size(sk, 0, 0); - if (nmerr < 0) - goto errout; - - nm_assert(sk->s_local.nl_pid == 0); - - err = bind(sk->s_fd, (struct sockaddr *) &sk->s_local, sizeof(sk->s_local)); - if (err != 0) { - nmerr = -nm_errno_from_native(errno); - goto errout; - } - - addrlen = sizeof(local); - err = getsockname(sk->s_fd, (struct sockaddr *) &local, &addrlen); - if (err < 0) { - nmerr = -nm_errno_from_native(errno); - goto errout; - } - - if (addrlen != sizeof(local)) { - nmerr = -NME_UNSPEC; - goto errout; - } - - if (local.nl_family != AF_NETLINK) { - nmerr = -NME_UNSPEC; - goto errout; - } - - sk->s_local = local; - sk->s_proto = protocol; - - return 0; - -errout: - if (sk->s_fd != -1) { - close(sk->s_fd); - sk->s_fd = -1; - } - return nmerr; -} - -/*****************************************************************************/ - -static void -_cb_init(struct nl_cb *dst, const struct nl_cb *src) -{ - nm_assert(dst); - - if (src) - *dst = *src; - else - memset(dst, 0, sizeof(*dst)); -} - -static int -ack_wait_handler(struct nl_msg *msg, void *arg) -{ - return NL_STOP; -} - -int -nl_wait_for_ack(struct nl_sock *sk, const struct nl_cb *cb) -{ - struct nl_cb cb2; - - _cb_init(&cb2, cb); - cb2.ack_cb = ack_wait_handler; - return nl_recvmsgs(sk, &cb2); -} - -#define NL_CB_CALL(cb, type, msg) \ - do { \ - const struct nl_cb *_cb = (cb); \ - \ - if (_cb && _cb->type##_cb) { \ - /* the returned value here must be either a negative - * netlink error number, or one of NL_SKIP, NL_STOP, NL_OK. */ \ - nmerr = _cb->type##_cb((msg), _cb->type##_arg); \ - switch (nmerr) { \ - case NL_OK: \ - nm_assert(nmerr == 0); \ - break; \ - case NL_SKIP: \ - goto skip; \ - case NL_STOP: \ - goto stop; \ - default: \ - if (nmerr >= 0) { \ - nm_assert_not_reached(); \ - nmerr = -NME_BUG; \ - } \ - goto out; \ - } \ - } \ - } while (0) - -int -nl_recvmsgs(struct nl_sock *sk, const struct nl_cb *cb) -{ - int n, nmerr = 0, multipart = 0, interrupted = 0, nrecv = 0; - gs_free unsigned char *buf = NULL; - struct nlmsghdr * hdr; - struct sockaddr_nl nla = {0}; - struct ucred creds; - gboolean creds_has; - -continue_reading: - n = nl_recv(sk, &nla, &buf, &creds, &creds_has); - if (n <= 0) - return n; - - hdr = (struct nlmsghdr *) buf; - while (nlmsg_ok(hdr, n)) { - nm_auto_nlmsg struct nl_msg *msg = NULL; - - msg = nlmsg_alloc_convert(hdr); - - nlmsg_set_proto(msg, sk->s_proto); - nlmsg_set_src(msg, &nla); - nlmsg_set_creds(msg, creds_has ? &creds : NULL); - - nrecv++; - - /* Only do sequence checking if auto-ack mode is enabled */ - if (!(sk->s_flags & NL_NO_AUTO_ACK)) { - if (hdr->nlmsg_seq != sk->s_seq_expect) { - nmerr = -NME_NL_SEQ_MISMATCH; - goto out; - } - } - - if (hdr->nlmsg_type == NLMSG_DONE || hdr->nlmsg_type == NLMSG_ERROR - || hdr->nlmsg_type == NLMSG_NOOP || hdr->nlmsg_type == NLMSG_OVERRUN) { - /* We can't check for !NLM_F_MULTI since some netlink - * users in the kernel are broken. */ - sk->s_seq_expect++; - } - - if (hdr->nlmsg_flags & NLM_F_MULTI) - multipart = 1; - - if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) { - /* - * We have to continue reading to clear - * all messages until a NLMSG_DONE is - * received and report the inconsistency. - */ - interrupted = 1; - } - - /* messages terminates a multipart message, this is - * usually the end of a message and therefore we slip - * out of the loop by default. the user may overrule - * this action by skipping this packet. */ - if (hdr->nlmsg_type == NLMSG_DONE) { - multipart = 0; - NL_CB_CALL(cb, finish, msg); - } - - /* Message to be ignored, the default action is to - * skip this message if no callback is specified. The - * user may overrule this action by returning - * NL_PROCEED. */ - else if (hdr->nlmsg_type == NLMSG_NOOP) - goto skip; - - /* Data got lost, report back to user. The default action is to - * quit parsing. The user may overrule this action by returning - * NL_SKIP or NL_PROCEED (dangerous) */ - else if (hdr->nlmsg_type == NLMSG_OVERRUN) { - nmerr = -NME_NL_MSG_OVERFLOW; - goto out; - } - - /* Message carries a nlmsgerr */ - else if (hdr->nlmsg_type == NLMSG_ERROR) { - struct nlmsgerr *e = nlmsg_data(hdr); - - if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) { - /* Truncated error message, the default action - * is to stop parsing. The user may overrule - * this action by returning NL_SKIP or - * NL_PROCEED (dangerous) */ - nmerr = -NME_NL_MSG_TRUNC; - goto out; - } - if (e->error) { - /* Error message reported back from kernel. */ - if (cb && cb->err_cb) { - /* the returned value here must be either a negative - * netlink error number, or one of NL_SKIP, NL_STOP, NL_OK. */ - nmerr = cb->err_cb(&nla, e, cb->err_arg); - if (nmerr < 0) - goto out; - else if (nmerr == NL_SKIP) - goto skip; - else if (nmerr == NL_STOP) { - nmerr = -nm_errno_from_native(e->error); - goto out; - } - nm_assert(nmerr == NL_OK); - } else { - nmerr = -nm_errno_from_native(e->error); - goto out; - } - } else - NL_CB_CALL(cb, ack, msg); - } else { - /* Valid message (not checking for MULTIPART bit to - * get along with broken kernels. NL_SKIP has no - * effect on this. */ - NL_CB_CALL(cb, valid, msg); - } -skip: - nmerr = 0; - hdr = nlmsg_next(hdr, &n); - } - - if (multipart) { - /* Multipart message not yet complete, continue reading */ - nm_clear_g_free(&buf); - - nmerr = 0; - goto continue_reading; - } - -stop: - nmerr = 0; - -out: - if (interrupted) - nmerr = -NME_NL_DUMP_INTR; - - nm_assert(nmerr <= 0); - return nmerr ?: nrecv; -} - -int -nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) -{ - int ret; - - if (sk->s_fd < 0) - return -NME_NL_BAD_SOCK; - - nlmsg_set_src(msg, &sk->s_local); - - ret = sendmsg(sk->s_fd, hdr, 0); - if (ret < 0) - return -nm_errno_from_native(errno); - - return ret; -} - -int -nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen) -{ - struct sockaddr_nl *dst; - struct ucred * creds; - struct msghdr hdr = { - .msg_name = (void *) &sk->s_peer, - .msg_namelen = sizeof(struct sockaddr_nl), - .msg_iov = iov, - .msg_iovlen = iovlen, - }; - char buf[CMSG_SPACE(sizeof(struct ucred))]; - - /* Overwrite destination if specified in the message itself, defaults - * to the peer address of the socket. - */ - dst = nlmsg_get_dst(msg); - if (dst->nl_family == AF_NETLINK) - hdr.msg_name = dst; - - /* Add credentials if present. */ - creds = nlmsg_get_creds(msg); - if (creds != NULL) { - struct cmsghdr *cmsg; - - hdr.msg_control = buf; - hdr.msg_controllen = sizeof(buf); - - cmsg = CMSG_FIRSTHDR(&hdr); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_CREDENTIALS; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred)); - } - - return nl_sendmsg(sk, msg, &hdr); -} - -void -nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg) -{ - struct nlmsghdr *nlh; - - nlh = nlmsg_hdr(msg); - if (nlh->nlmsg_pid == NL_AUTO_PORT) - nlh->nlmsg_pid = nl_socket_get_local_port(sk); - - if (nlh->nlmsg_seq == NL_AUTO_SEQ) - nlh->nlmsg_seq = sk->s_seq_next++; - - if (msg->nm_protocol == -1) - msg->nm_protocol = sk->s_proto; - - nlh->nlmsg_flags |= NLM_F_REQUEST; - - if (!(sk->s_flags & NL_NO_AUTO_ACK)) - nlh->nlmsg_flags |= NLM_F_ACK; -} - -int -nl_send(struct nl_sock *sk, struct nl_msg *msg) -{ - struct iovec iov = { - .iov_base = (void *) nlmsg_hdr(msg), - .iov_len = nlmsg_hdr(msg)->nlmsg_len, - }; - - return nl_send_iovec(sk, msg, &iov, 1); -} - -int -nl_send_auto(struct nl_sock *sk, struct nl_msg *msg) -{ - nl_complete_msg(sk, msg); - - return nl_send(sk, msg); -} - -int -nl_recv(struct nl_sock * sk, - struct sockaddr_nl *nla, - unsigned char ** buf, - struct ucred * out_creds, - gboolean * out_creds_has) -{ - ssize_t n; - int flags = 0; - struct iovec iov; - struct msghdr msg = { - .msg_name = (void *) nla, - .msg_namelen = sizeof(struct sockaddr_nl), - .msg_iov = &iov, - .msg_iovlen = 1, - }; - struct ucred tmpcreds; - gboolean tmpcreds_has = FALSE; - int retval; - int errsv; - - nm_assert(nla); - nm_assert(buf && !*buf); - nm_assert(!out_creds_has == !out_creds); - - if ((sk->s_flags & NL_MSG_PEEK) - || (!(sk->s_flags & NL_MSG_PEEK_EXPLICIT) && sk->s_bufsize == 0)) - flags |= MSG_PEEK | MSG_TRUNC; - - iov.iov_len = sk->s_bufsize ?: (((size_t) nm_utils_getpagesize()) * 4u); - iov.iov_base = g_malloc(iov.iov_len); - - if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) { - msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); - msg.msg_control = g_malloc(msg.msg_controllen); - } - -retry: - n = recvmsg(sk->s_fd, &msg, flags); - if (!n) { - retval = 0; - goto abort; - } - - if (n < 0) { - errsv = errno; - if (errsv == EINTR) - goto retry; - retval = -nm_errno_from_native(errsv); - goto abort; - } - - if (msg.msg_flags & MSG_CTRUNC) { - if (msg.msg_controllen == 0) { - retval = -NME_NL_MSG_TRUNC; - goto abort; - } - - msg.msg_controllen *= 2; - msg.msg_control = g_realloc(msg.msg_control, msg.msg_controllen); - goto retry; - } - - if (iov.iov_len < n || (msg.msg_flags & MSG_TRUNC)) { - /* respond with error to an incomplete message */ - if (flags == 0) { - retval = -NME_NL_MSG_TRUNC; - goto abort; - } - - /* Provided buffer is not long enough, enlarge it - * to size of n (which should be total length of the message) - * and try again. */ - iov.iov_base = g_realloc(iov.iov_base, n); - iov.iov_len = n; - flags = 0; - goto retry; - } - - if (flags != 0) { - /* Buffer is big enough, do the actual reading */ - flags = 0; - goto retry; - } - - if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { - retval = -NME_UNSPEC; - goto abort; - } - - if (out_creds && (sk->s_flags & NL_SOCK_PASSCRED)) { - struct cmsghdr *cmsg; - - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_level != SOL_SOCKET) - continue; - if (cmsg->cmsg_type != SCM_CREDENTIALS) - continue; - memcpy(&tmpcreds, CMSG_DATA(cmsg), sizeof(tmpcreds)); - tmpcreds_has = TRUE; - break; - } - } - - retval = n; - -abort: - g_free(msg.msg_control); - - if (retval <= 0) { - g_free(iov.iov_base); - return retval; - } - - *buf = iov.iov_base; - if (out_creds && tmpcreds_has) - *out_creds = tmpcreds; - NM_SET_OUT(out_creds_has, tmpcreds_has); - return retval; -} diff --git a/shared/nm-platform/nm-netlink.h b/shared/nm-platform/nm-netlink.h deleted file mode 100644 index 8de42531d3..0000000000 --- a/shared/nm-platform/nm-netlink.h +++ /dev/null @@ -1,616 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2018 Red Hat, Inc. - */ - -#ifndef __NM_NETLINK_H__ -#define __NM_NETLINK_H__ - -#include <linux/netlink.h> -#include <linux/rtnetlink.h> -#include <linux/genetlink.h> - -#include "nm-std-aux/unaligned.h" - -/*****************************************************************************/ - -#define NLMSGERR_ATTR_UNUSED 0 -#define NLMSGERR_ATTR_MSG 1 -#define NLMSGERR_ATTR_OFFS 2 -#define NLMSGERR_ATTR_COOKIE 3 -#define NLMSGERR_ATTR_MAX 3 - -#ifndef NLM_F_ACK_TLVS - #define NLM_F_ACK_TLVS 0x200 -#endif - -/*****************************************************************************/ - -/* Basic attribute data types */ -enum { - NLA_UNSPEC, /* Unspecified type, binary data chunk */ - NLA_U8, /* 8 bit integer */ - NLA_U16, /* 16 bit integer */ - NLA_U32, /* 32 bit integer */ - NLA_U64, /* 64 bit integer */ - NLA_STRING, /* NUL terminated character string */ - NLA_FLAG, /* Flag */ - NLA_MSECS, /* Micro seconds (64bit) */ - NLA_NESTED, /* Nested attributes */ - NLA_NESTED_COMPAT, - NLA_NUL_STRING, - NLA_BINARY, - NLA_S8, - NLA_S16, - NLA_S32, - NLA_S64, - __NLA_TYPE_MAX, -}; - -#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) - -struct nl_msg; - -/*****************************************************************************/ - -const char *nl_nlmsgtype2str(int type, char *buf, size_t size); - -const char *nl_nlmsg_flags2str(int flags, char *buf, size_t len); - -const char *nl_nlmsghdr_to_str(const struct nlmsghdr *hdr, char *buf, gsize len); - -/*****************************************************************************/ - -struct nla_policy { - /* Type of attribute or NLA_UNSPEC */ - uint16_t type; - - /* Minimal length of payload required */ - uint16_t minlen; - - /* Maximal length of payload allowed */ - uint16_t maxlen; -}; - -/*****************************************************************************/ - -/* static asserts that @tb and @policy are suitable arguments to nla_parse(). */ -#define _nl_static_assert_tb(tb, policy) \ - G_STMT_START \ - { \ - G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(tb) > 0); \ - \ - /* We allow @policy to be either a C array or NULL. The sizeof() - * must either match the expected array size or the sizeof(NULL), - * but not both. */ \ - G_STATIC_ASSERT_EXPR((sizeof(policy) == G_N_ELEMENTS(tb) * sizeof(struct nla_policy)) \ - ^ (sizeof(policy) == sizeof(NULL))); \ - } \ - G_STMT_END - -/*****************************************************************************/ - -static inline int -nla_attr_size(int payload) -{ - nm_assert(payload >= 0); - - return NLA_HDRLEN + payload; -} - -static inline int -nla_total_size(int payload) -{ - return NLA_ALIGN(nla_attr_size(payload)); -} - -static inline int -nla_padlen(int payload) -{ - return nla_total_size(payload) - nla_attr_size(payload); -} - -struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int attrlen); - -static inline int -nla_len(const struct nlattr *nla) -{ - nm_assert(nla); - nm_assert(nla->nla_len >= NLA_HDRLEN); - - return ((int) nla->nla_len) - NLA_HDRLEN; -} - -static inline int -nla_type(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= 0); - - return nla->nla_type & NLA_TYPE_MASK; -} - -static inline void * -nla_data(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= 0); - - return &(((char *) nla)[NLA_HDRLEN]); -} - -#define nla_data_as(type, nla) \ - ({ \ - const struct nlattr *_nla = (nla); \ - \ - nm_assert(nla_len(_nla) >= sizeof(type)); \ - \ - /* note that casting the pointer is undefined behavior in C, if - * the data has wrong alignment. Netlink data is aligned to 4 bytes, - * that means, if the alignment is larger than 4, this is invalid. */ \ - G_STATIC_ASSERT_EXPR(_nm_alignof(type) <= NLA_ALIGNTO); \ - \ - (type *) nla_data(_nla); \ - }) - -static inline uint8_t -nla_get_u8(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint8_t)); - - return *((const uint8_t *) nla_data(nla)); -} - -static inline int8_t -nla_get_s8(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(int8_t)); - - return *((const int8_t *) nla_data(nla)); -} - -static inline uint8_t -nla_get_u8_cond(/*const*/ struct nlattr *const *tb, int attr, uint8_t default_val) -{ - nm_assert(tb); - nm_assert(attr >= 0); - - return tb[attr] ? nla_get_u8(tb[attr]) : default_val; -} - -static inline uint16_t -nla_get_u16(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint16_t)); - - return *((const uint16_t *) nla_data(nla)); -} - -static inline uint32_t -nla_get_u32(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint32_t)); - - return *((const uint32_t *) nla_data(nla)); -} - -static inline int32_t -nla_get_s32(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(int32_t)); - - return *((const int32_t *) nla_data(nla)); -} - -static inline uint64_t -nla_get_u64(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint64_t)); - - return unaligned_read_ne64(nla_data(nla)); -} - -static inline uint64_t -nla_get_be64(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= sizeof(uint64_t)); - - return unaligned_read_be64(nla_data(nla)); -} - -static inline char * -nla_get_string(const struct nlattr *nla) -{ - nm_assert(nla_len(nla) >= 0); - - return (char *) nla_data(nla); -} - -size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize); - -size_t nla_memcpy(void *dst, const struct nlattr *nla, size_t dstsize); - -#define nla_memcpy_checked_size(dst, nla, dstsize) \ - G_STMT_START \ - { \ - void *const _dst = (dst); \ - const struct nlattr *const _nla = (nla); \ - const size_t _dstsize = (dstsize); \ - size_t _srcsize; \ - \ - /* assert that, if @nla is given, that it has the exact expected - * size. This implies that the caller previously verified the length - * of the attribute (via minlen/maxlen at nla_parse()). */ \ - \ - if (_nla) { \ - _srcsize = nla_memcpy(_dst, _nla, _dstsize); \ - nm_assert(_srcsize == _dstsize); \ - } \ - } \ - G_STMT_END - -int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data); - -static inline int -nla_put_string(struct nl_msg *msg, int attrtype, const char *str) -{ - nm_assert(str); - - return nla_put(msg, attrtype, strlen(str) + 1, str); -} - -static inline int -nla_put_uint8(struct nl_msg *msg, int attrtype, uint8_t val) -{ - return nla_put(msg, attrtype, sizeof(val), &val); -} - -static inline int -nla_put_uint16(struct nl_msg *msg, int attrtype, uint16_t val) -{ - return nla_put(msg, attrtype, sizeof(val), &val); -} - -static inline int -nla_put_uint32(struct nl_msg *msg, int attrtype, uint32_t val) -{ - return nla_put(msg, attrtype, sizeof(val), &val); -} - -#define NLA_PUT(msg, attrtype, attrlen, data) \ - G_STMT_START \ - { \ - if (nla_put(msg, attrtype, attrlen, data) < 0) \ - goto nla_put_failure; \ - } \ - G_STMT_END - -#define NLA_PUT_TYPE(msg, type, attrtype, value) \ - G_STMT_START \ - { \ - type __nla_tmp = value; \ - NLA_PUT(msg, attrtype, sizeof(type), &__nla_tmp); \ - } \ - G_STMT_END - -#define NLA_PUT_U8(msg, attrtype, value) NLA_PUT_TYPE(msg, uint8_t, attrtype, value) - -#define NLA_PUT_S8(msg, attrtype, value) NLA_PUT_TYPE(msg, int8_t, attrtype, value) - -#define NLA_PUT_U16(msg, attrtype, value) NLA_PUT_TYPE(msg, uint16_t, attrtype, value) - -#define NLA_PUT_U32(msg, attrtype, value) NLA_PUT_TYPE(msg, uint32_t, attrtype, value) - -#define NLA_PUT_S32(msg, attrtype, value) NLA_PUT_TYPE(msg, int32_t, attrtype, value) - -#define NLA_PUT_U64(msg, attrtype, value) NLA_PUT_TYPE(msg, uint64_t, attrtype, value) - -#define NLA_PUT_STRING(msg, attrtype, value) NLA_PUT(msg, attrtype, (int) strlen(value) + 1, value) - -#define NLA_PUT_FLAG(msg, attrtype) NLA_PUT(msg, attrtype, 0, NULL) - -struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype); - -static inline int -nla_ok(const struct nlattr *nla, int remaining) -{ - return remaining >= (int) sizeof(*nla) && nla->nla_len >= sizeof(*nla) - && nla->nla_len <= remaining; -} - -static inline struct nlattr * -nla_next(const struct nlattr *nla, int *remaining) -{ - int totlen = NLA_ALIGN(nla->nla_len); - - *remaining -= totlen; - return (struct nlattr *) ((char *) nla + totlen); -} - -#define nla_for_each_attr(pos, head, len, rem) \ - for (pos = head, rem = len; nla_ok(pos, rem); pos = nla_next(pos, &(rem))) - -#define nla_for_each_nested(pos, nla, rem) \ - for (pos = (struct nlattr *) nla_data(nla), rem = nla_len(nla); nla_ok(pos, rem); \ - pos = nla_next(pos, &(rem))) - -void nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr); -struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype); -int nla_nest_end(struct nl_msg *msg, struct nlattr *start); - -int nla_parse(struct nlattr * tb[], - int maxtype, - struct nlattr * head, - int len, - const struct nla_policy *policy); - -#define nla_parse_arr(tb, head, len, policy) \ - ({ \ - _nl_static_assert_tb((tb), (policy)); \ - \ - nla_parse((tb), G_N_ELEMENTS(tb) - 1, (head), (len), (policy)); \ - }) - -static inline int -nla_parse_nested(struct nlattr * tb[], - int maxtype, - struct nlattr * nla, - const struct nla_policy *policy) -{ - return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); -} - -#define nla_parse_nested_arr(tb, nla, policy) \ - ({ \ - _nl_static_assert_tb((tb), (policy)); \ - \ - nla_parse_nested((tb), G_N_ELEMENTS(tb) - 1, (nla), (policy)); \ - }) - -/*****************************************************************************/ - -struct nl_msg *nlmsg_alloc(void); - -struct nl_msg *nlmsg_alloc_size(size_t max); - -struct nl_msg *nlmsg_alloc_convert(struct nlmsghdr *hdr); - -struct nl_msg *nlmsg_alloc_simple(int nlmsgtype, int flags); - -void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad); - -int nlmsg_append(struct nl_msg *n, const void *data, size_t len, int pad); - -#define nlmsg_append_struct(n, data) nlmsg_append(n, (data), sizeof(*(data)), NLMSG_ALIGNTO) - -void nlmsg_free(struct nl_msg *msg); - -static inline int -nlmsg_size(int payload) -{ - nm_assert(payload >= 0 && payload < G_MAXINT - NLMSG_HDRLEN - 4); - return NLMSG_HDRLEN + payload; -} - -static inline int -nlmsg_total_size(int payload) -{ - return NLMSG_ALIGN(nlmsg_size(payload)); -} - -static inline int -nlmsg_ok(const struct nlmsghdr *nlh, int remaining) -{ - return (remaining >= (int) sizeof(struct nlmsghdr) && nlh->nlmsg_len >= sizeof(struct nlmsghdr) - && nlh->nlmsg_len <= remaining); -} - -static inline struct nlmsghdr * -nlmsg_next(struct nlmsghdr *nlh, int *remaining) -{ - int totlen = NLMSG_ALIGN(nlh->nlmsg_len); - - *remaining -= totlen; - - return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); -} - -int nlmsg_get_proto(struct nl_msg *msg); -void nlmsg_set_proto(struct nl_msg *msg, int protocol); - -void nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr); - -struct ucred *nlmsg_get_creds(struct nl_msg *msg); -void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds); - -static inline void -_nm_auto_nl_msg_cleanup(struct nl_msg **ptr) -{ - nlmsg_free(*ptr); -} -#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup) - -static inline void * -nlmsg_data(const struct nlmsghdr *nlh) -{ - return (unsigned char *) nlh + NLMSG_HDRLEN; -} - -static inline void * -nlmsg_tail(const struct nlmsghdr *nlh) -{ - return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len); -} - -struct nlmsghdr *nlmsg_hdr(struct nl_msg *n); - -static inline int -nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen) -{ - if (nlh->nlmsg_len < nlmsg_size(hdrlen)) - return 0; - - return 1; -} - -static inline int -nlmsg_datalen(const struct nlmsghdr *nlh) -{ - return nlh->nlmsg_len - NLMSG_HDRLEN; -} - -static inline int -nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) -{ - return NM_MAX((int) (nlmsg_datalen(nlh) - NLMSG_ALIGN(hdrlen)), 0); -} - -static inline struct nlattr * -nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) -{ - unsigned char *data = nlmsg_data(nlh); - return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); -} - -static inline struct nlattr * -nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype) -{ - return nla_find(nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), attrtype); -} - -int nlmsg_parse(struct nlmsghdr * nlh, - int hdrlen, - struct nlattr * tb[], - int maxtype, - const struct nla_policy *policy); - -#define nlmsg_parse_arr(nlh, hdrlen, tb, policy) \ - ({ \ - _nl_static_assert_tb((tb), (policy)); \ - G_STATIC_ASSERT_EXPR((hdrlen) >= 0); \ - \ - nlmsg_parse((nlh), (hdrlen), (tb), G_N_ELEMENTS(tb) - 1, (policy)); \ - }) - -struct nlmsghdr * -nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, int type, int payload, int flags); - -/*****************************************************************************/ - -#define NL_AUTO_PORT 0 -#define NL_AUTO_SEQ 0 - -struct nl_sock; - -struct nl_sock *nl_socket_alloc(void); - -void nl_socket_free(struct nl_sock *sk); - -int nl_socket_get_fd(const struct nl_sock *sk); - -struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *msg); - -size_t nl_socket_get_msg_buf_size(struct nl_sock *sk); -int nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize); - -int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf); - -int nl_socket_set_passcred(struct nl_sock *sk, int state); - -int nl_socket_set_nonblocking(const struct nl_sock *sk); - -void nl_socket_disable_msg_peek(struct nl_sock *sk); - -uint32_t nl_socket_get_local_port(const struct nl_sock *sk); - -int nl_socket_add_memberships(struct nl_sock *sk, int group, ...); - -int nl_connect(struct nl_sock *sk, int protocol); - -int nl_recv(struct nl_sock * sk, - struct sockaddr_nl *nla, - unsigned char ** buf, - struct ucred * out_creds, - gboolean * out_creds_has); - -int nl_send(struct nl_sock *sk, struct nl_msg *msg); - -int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg); - -/*****************************************************************************/ - -enum nl_cb_action { - /* Proceed with wathever would come next */ - NL_OK, - /* Skip this message */ - NL_SKIP, - /* Stop parsing altogether and discard remaining messages */ - NL_STOP, -}; - -typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg); - -typedef int (*nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg); - -struct nl_cb { - nl_recvmsg_msg_cb_t valid_cb; - void * valid_arg; - - nl_recvmsg_msg_cb_t finish_cb; - void * finish_arg; - - nl_recvmsg_msg_cb_t ack_cb; - void * ack_arg; - - nl_recvmsg_err_cb_t err_cb; - void * err_arg; -}; - -int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr); - -int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen); - -void nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg); - -int nl_recvmsgs(struct nl_sock *sk, const struct nl_cb *cb); - -int nl_wait_for_ack(struct nl_sock *sk, const struct nl_cb *cb); - -int nl_socket_set_ext_ack(struct nl_sock *sk, gboolean enable); - -/*****************************************************************************/ - -void * genlmsg_put(struct nl_msg *msg, - uint32_t port, - uint32_t seq, - int family, - int hdrlen, - int flags, - uint8_t cmd, - uint8_t version); -void * genlmsg_data(const struct genlmsghdr *gnlh); -void * genlmsg_user_hdr(const struct genlmsghdr *gnlh); -struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *nlh); -void * genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen); -struct nlattr * genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen); -int genlmsg_len(const struct genlmsghdr *gnlh); -int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen); -int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen); - -int genlmsg_parse(struct nlmsghdr * nlh, - int hdrlen, - struct nlattr * tb[], - int maxtype, - const struct nla_policy *policy); - -#define genlmsg_parse_arr(nlh, hdrlen, tb, policy) \ - ({ \ - _nl_static_assert_tb((tb), (policy)); \ - G_STATIC_ASSERT_EXPR((hdrlen) >= 0); \ - \ - genlmsg_parse((nlh), (hdrlen), (tb), G_N_ELEMENTS(tb) - 1, (policy)); \ - }) - -int genl_ctrl_resolve(struct nl_sock *sk, const char *name); - -/*****************************************************************************/ - -#endif /* __NM_NETLINK_H__ */ diff --git a/shared/nm-platform/nm-platform-utils.c b/shared/nm-platform/nm-platform-utils.c deleted file mode 100644 index c1c5b1a356..0000000000 --- a/shared/nm-platform/nm-platform-utils.c +++ /dev/null @@ -1,1807 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nm-platform-utils.h" - -#include <unistd.h> -#include <sys/ioctl.h> -#include <linux/ethtool.h> -#include <linux/sockios.h> -#include <linux/mii.h> -#include <linux/if.h> -#include <linux/version.h> -#include <linux/rtnetlink.h> -#include <fcntl.h> -#include <libudev.h> - -#include "nm-base/nm-ethtool-base.h" -#include "nm-log-core/nm-logging.h" - -/*****************************************************************************/ - -#define ONOFF(bool_val) ((bool_val) ? "on" : "off") - -/****************************************************************************** - * utils - *****************************************************************************/ - -extern char *if_indextoname(unsigned __ifindex, char *__ifname); -unsigned if_nametoindex(const char *__ifname); - -const char * -nmp_utils_if_indextoname(int ifindex, char *out_ifname /*IFNAMSIZ*/) -{ - g_return_val_if_fail(ifindex > 0, NULL); - g_return_val_if_fail(out_ifname, NULL); - - return if_indextoname(ifindex, out_ifname); -} - -int -nmp_utils_if_nametoindex(const char *ifname) -{ - g_return_val_if_fail(ifname, 0); - - return if_nametoindex(ifname); -} - -/*****************************************************************************/ - -NM_UTILS_LOOKUP_STR_DEFINE(nm_platform_link_duplex_type_to_string, - NMPlatformLinkDuplexType, - NM_UTILS_LOOKUP_DEFAULT_WARN(NULL), - NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_UNKNOWN, "unknown"), - NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_FULL, "full"), - NM_UTILS_LOOKUP_STR_ITEM(NM_PLATFORM_LINK_DUPLEX_HALF, "half"), ); - -/*****************************************************************************/ - -typedef struct { - int fd; - const int ifindex; - char ifname[IFNAMSIZ]; -} SocketHandle; - -#define SOCKET_HANDLE_INIT(_ifindex) \ - { \ - .fd = -1, .ifindex = (_ifindex), \ - } - -static void -_nm_auto_socket_handle(SocketHandle *shandle) -{ - if (shandle->fd >= 0) - nm_close(shandle->fd); -} - -#define nm_auto_socket_handle nm_auto(_nm_auto_socket_handle) - -/*****************************************************************************/ - -typedef enum { - IOCTL_CALL_DATA_TYPE_NONE, - IOCTL_CALL_DATA_TYPE_IFRDATA, - IOCTL_CALL_DATA_TYPE_IFRU, -} IoctlCallDataType; - -static int -_ioctl_call(const char * log_ioctl_type, - const char * log_subtype, - unsigned long int ioctl_request, - int ifindex, - int * inout_fd, - char * inout_ifname, - IoctlCallDataType edata_type, - gpointer edata, - gsize edata_size, - struct ifreq * out_ifreq) -{ - nm_auto_close int fd_close = -1; - int fd; - int r; - gpointer edata_backup = NULL; - gs_free gpointer edata_backup_free = NULL; - guint try_count; - char known_ifnames[2][IFNAMSIZ]; - const char * failure_reason = NULL; - struct ifreq ifr; - - nm_assert(ifindex > 0); - nm_assert(NM_IN_SET(edata_type, - IOCTL_CALL_DATA_TYPE_NONE, - IOCTL_CALL_DATA_TYPE_IFRDATA, - IOCTL_CALL_DATA_TYPE_IFRU)); - nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_NONE || edata_size == 0); - nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_IFRDATA || edata_size > 0); - nm_assert(edata_type != IOCTL_CALL_DATA_TYPE_IFRU - || (edata_size > 0 && edata_size <= sizeof(ifr.ifr_ifru))); - nm_assert(edata_size == 0 || edata); - - /* open a file descriptor (or use the one provided). */ - if (inout_fd && *inout_fd >= 0) - fd = *inout_fd; - else { - fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (fd < 0) { - r = -NM_ERRNO_NATIVE(errno); - failure_reason = "failed creating socket or ioctl"; - goto out; - } - if (inout_fd) - *inout_fd = fd; - else - fd_close = fd; - } - - /* resolve the ifindex to name (or use the one provided). */ - if (inout_ifname && inout_ifname[0]) - nm_utils_ifname_cpy(known_ifnames[0], inout_ifname); - else { - if (!nmp_utils_if_indextoname(ifindex, known_ifnames[0])) { - failure_reason = "cannot resolve ifindex"; - r = -ENODEV; - goto out; - } - if (inout_ifname) - nm_utils_ifname_cpy(inout_ifname, known_ifnames[0]); - } - - /* we might need to retry the request. Backup edata so that we can - * restore it on retry. */ - if (edata_size > 0) - edata_backup = nm_memdup_maybe_a(500, edata, edata_size, &edata_backup_free); - - try_count = 0; - -again: -{ - const char *ifname = known_ifnames[try_count % 2]; - - nm_assert(ifindex > 0); - nm_assert(ifname && nm_utils_ifname_valid_kernel(ifname, NULL)); - nm_assert(fd >= 0); - - memset(&ifr, 0, sizeof(ifr)); - nm_utils_ifname_cpy(ifr.ifr_name, ifname); - if (edata_type == IOCTL_CALL_DATA_TYPE_IFRDATA) - ifr.ifr_data = edata; - else if (edata_type == IOCTL_CALL_DATA_TYPE_IFRU) - memcpy(&ifr.ifr_ifru, edata, NM_MIN(edata_size, sizeof(ifr.ifr_ifru))); - - if (ioctl(fd, ioctl_request, &ifr) < 0) { - r = -NM_ERRNO_NATIVE(errno); - nm_log_trace(LOGD_PLATFORM, - "%s[%d]: %s, %s: failed: %s", - log_ioctl_type, - ifindex, - log_subtype, - ifname, - nm_strerror_native(-r)); - } else { - r = 0; - nm_log_trace(LOGD_PLATFORM, - "%s[%d]: %s, %s: success", - log_ioctl_type, - ifindex, - log_subtype, - ifname); - } -} - - try_count++; - - /* resolve the name again to see whether the ifindex still has the same name. */ - if (!nmp_utils_if_indextoname(ifindex, known_ifnames[try_count % 2])) { - /* we could not find the ifindex again. Probably the device just got - * removed. - * - * In both cases we return the error code we got from ioctl above. - * Either it failed because the device was gone already or it still - * managed to complete the call. In both cases, the error code is good. */ - failure_reason = - "cannot resolve ifindex after ioctl call. Probably the device was just removed"; - goto out; - } - - /* check whether the ifname changed in the meantime. If yes, would render the result - * invalid. Note that this cannot detect every race regarding renames, for example: - * - * - if_indextoname(#10) gives eth0 - * - rename(#10) => eth0_tmp - * - rename(#11) => eth0 - * - ioctl(eth0) (wrongly fetching #11, formerly eth1) - * - rename(#11) => eth_something - * - rename(#10) => eth0 - * - if_indextoname(#10) gives eth0 - */ - if (!nm_streq(known_ifnames[0], known_ifnames[1])) { - gboolean retry; - - /* we detected a possible(!) rename. - * - * For getters it's straight forward to just retry the call. - * - * For setters we also always retry. If our previous call operated on the right device, - * calling it again should have no bad effect (just setting the same thing more than once). - * - * The only potential bad thing is if there was a race involving swapping names, and we just - * set the ioctl option on the wrong device. But then the bad thing already happenned and - * we cannot detect it (nor do anything about it). At least, we can retry and set the - * option on the right interface. */ - retry = (try_count < 5); - - nm_log_trace(LOGD_PLATFORM, - "%s[%d]: %s: rename detected from \"%s\" to \"%s\". %s", - log_ioctl_type, - ifindex, - log_subtype, - known_ifnames[(try_count - 1) % 2], - known_ifnames[try_count % 2], - retry ? "Retry" : "No retry"); - if (inout_ifname) - nm_utils_ifname_cpy(inout_ifname, known_ifnames[try_count % 2]); - if (retry) { - if (edata_size > 0) - memcpy(edata, edata_backup, edata_size); - goto again; - } - } - -out: - if (failure_reason) { - nm_log_trace(LOGD_PLATFORM, - "%s[%d]: %s: %s: %s", - log_ioctl_type, - ifindex, - log_subtype, - failure_reason, - r < 0 ? nm_strerror_native(-r) : "assume success"); - } - if (r >= 0) - NM_SET_OUT(out_ifreq, ifr); - return r; -} - -/****************************************************************************** - * ethtool - *****************************************************************************/ - -static NM_UTILS_ENUM2STR_DEFINE(_ethtool_cmd_to_string, - guint32, - NM_UTILS_ENUM2STR(ETHTOOL_GCOALESCE, "ETHTOOL_GCOALESCE"), - NM_UTILS_ENUM2STR(ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"), - NM_UTILS_ENUM2STR(ETHTOOL_GFEATURES, "ETHTOOL_GFEATURES"), - NM_UTILS_ENUM2STR(ETHTOOL_GLINK, "ETHTOOL_GLINK"), - NM_UTILS_ENUM2STR(ETHTOOL_GPERMADDR, "ETHTOOL_GPERMADDR"), - NM_UTILS_ENUM2STR(ETHTOOL_GRINGPARAM, "ETHTOOL_GRINGPARAM"), - NM_UTILS_ENUM2STR(ETHTOOL_GSET, "ETHTOOL_GSET"), - NM_UTILS_ENUM2STR(ETHTOOL_GSSET_INFO, "ETHTOOL_GSSET_INFO"), - NM_UTILS_ENUM2STR(ETHTOOL_GSTATS, "ETHTOOL_GSTATS"), - NM_UTILS_ENUM2STR(ETHTOOL_GSTRINGS, "ETHTOOL_GSTRINGS"), - NM_UTILS_ENUM2STR(ETHTOOL_GWOL, "ETHTOOL_GWOL"), - NM_UTILS_ENUM2STR(ETHTOOL_SCOALESCE, "ETHTOOL_SCOALESCE"), - NM_UTILS_ENUM2STR(ETHTOOL_SFEATURES, "ETHTOOL_SFEATURES"), - NM_UTILS_ENUM2STR(ETHTOOL_SRINGPARAM, "ETHTOOL_SRINGPARAM"), - NM_UTILS_ENUM2STR(ETHTOOL_SSET, "ETHTOOL_SSET"), - NM_UTILS_ENUM2STR(ETHTOOL_SWOL, "ETHTOOL_SWOL"), ); - -static const char * -_ethtool_edata_to_string(gpointer edata, gsize edata_size, char *sbuf, gsize sbuf_len) -{ - nm_assert(edata); - nm_assert(edata_size >= sizeof(guint32)); - nm_assert((((intptr_t) edata) % _nm_alignof(guint32)) == 0); - - return _ethtool_cmd_to_string(*((guint32 *) edata), sbuf, sbuf_len); -} - -/*****************************************************************************/ - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) - #define ethtool_cmd_speed(pedata) ((pedata)->speed) - - #define ethtool_cmd_speed_set(pedata, speed) \ - G_STMT_START \ - { \ - (pedata)->speed = (guint16)(speed); \ - } \ - G_STMT_END -#endif - -static int -_ethtool_call_handle(SocketHandle *shandle, gpointer edata, gsize edata_size) -{ - char sbuf[50]; - - return _ioctl_call("ethtool", - _ethtool_edata_to_string(edata, edata_size, sbuf, sizeof(sbuf)), - SIOCETHTOOL, - shandle->ifindex, - &shandle->fd, - shandle->ifname, - IOCTL_CALL_DATA_TYPE_IFRDATA, - edata, - edata_size, - NULL); -} - -static int -_ethtool_call_once(int ifindex, gpointer edata, gsize edata_size) -{ - char sbuf[50]; - - return _ioctl_call("ethtool", - _ethtool_edata_to_string(edata, edata_size, sbuf, sizeof(sbuf)), - SIOCETHTOOL, - ifindex, - NULL, - NULL, - IOCTL_CALL_DATA_TYPE_IFRDATA, - edata, - edata_size, - NULL); -} - -/*****************************************************************************/ - -static struct ethtool_gstrings * -ethtool_get_stringset(SocketHandle *shandle, int stringset_id) -{ - struct { - struct ethtool_sset_info info; - guint32 sentinel; - } sset_info = { - .info.cmd = ETHTOOL_GSSET_INFO, - .info.reserved = 0, - .info.sset_mask = (1ULL << stringset_id), - }; - const guint32 * pdata; - gs_free struct ethtool_gstrings *gstrings = NULL; - gsize gstrings_len; - guint32 i, len; - - if (_ethtool_call_handle(shandle, &sset_info, sizeof(sset_info)) < 0) - return NULL; - if (!sset_info.info.sset_mask) - return NULL; - - pdata = (guint32 *) sset_info.info.data; - - len = *pdata; - - gstrings_len = sizeof(*gstrings) + (len * ETH_GSTRING_LEN); - gstrings = g_malloc0(gstrings_len); - gstrings->cmd = ETHTOOL_GSTRINGS; - gstrings->string_set = stringset_id; - gstrings->len = len; - if (gstrings->len > 0) { - if (_ethtool_call_handle(shandle, gstrings, gstrings_len) < 0) - return NULL; - for (i = 0; i < gstrings->len; i++) { - /* ensure NUL terminated */ - gstrings->data[i * ETH_GSTRING_LEN + (ETH_GSTRING_LEN - 1)] = '\0'; - } - } - - return g_steal_pointer(&gstrings); -} - -static int -ethtool_gstrings_find(const struct ethtool_gstrings *gstrings, const char *needle) -{ - guint32 i; - - /* ethtool_get_stringset() always ensures NUL terminated strings at ETH_GSTRING_LEN. - * that means, we cannot possibly request longer names. */ - nm_assert(needle && strlen(needle) < ETH_GSTRING_LEN); - - for (i = 0; i < gstrings->len; i++) { - if (nm_streq((char *) &gstrings->data[i * ETH_GSTRING_LEN], needle)) - return i; - } - return -1; -} - -static int -ethtool_get_stringset_index(SocketHandle *shandle, int stringset_id, const char *needle) -{ - gs_free struct ethtool_gstrings *gstrings = NULL; - - /* ethtool_get_stringset() always ensures NUL terminated strings at ETH_GSTRING_LEN. - * that means, we cannot possibly request longer names. */ - nm_assert(needle && strlen(needle) < ETH_GSTRING_LEN); - - gstrings = ethtool_get_stringset(shandle, stringset_id); - if (gstrings) - return ethtool_gstrings_find(gstrings, needle); - return -1; -} - -/*****************************************************************************/ - -static const NMEthtoolFeatureInfo _ethtool_feature_infos[_NM_ETHTOOL_ID_FEATURE_NUM] = { -#define ETHT_FEAT(eid, ...) \ - { \ - .ethtool_id = eid, .n_kernel_names = NM_NARG(__VA_ARGS__), \ - .kernel_names = ((const char *const[]){__VA_ARGS__}), \ - } - - /* the order does only matter for one thing: if it happens that more than one NMEthtoolID - * reference the same kernel-name, then the one that is mentioned *later* will win in - * case these NMEthtoolIDs are set. That mostly only makes sense for ethtool-ids which - * refer to multiple features ("feature-tso"), while also having more specific ids - * ("feature-tx-tcp-segmentation"). */ - - /* names from ethtool utility, which are aliases for multiple features. */ - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_SG, "tx-scatter-gather", "tx-scatter-gather-fraglist"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TSO, - "tx-tcp-segmentation", - "tx-tcp-ecn-segmentation", - "tx-tcp-mangleid-segmentation", - "tx-tcp6-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX, - "tx-checksum-ipv4", - "tx-checksum-ip-generic", - "tx-checksum-ipv6", - "tx-checksum-fcoe-crc", - "tx-checksum-sctp"), - - /* names from ethtool utility, which are aliases for one feature. */ - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_GRO, "rx-gro"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_GSO, "tx-generic-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_LRO, "rx-lro"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_NTUPLE, "rx-ntuple-filter"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX, "rx-checksum"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RXHASH, "rx-hashing"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RXVLAN, "rx-vlan-hw-parse"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TXVLAN, "tx-vlan-hw-insert"), - - /* names of features, as known by kernel. */ - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD, "esp-hw-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD, "esp-tx-csum-hw-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_FCOE_MTU, "fcoe-mtu"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_HIGHDMA, "highdma"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD, "hw-tc-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD, "l2-fwd-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_LOOPBACK, "loopback"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_MACSEC_HW_OFFLOAD, "macsec-hw-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_ALL, "rx-all"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_FCS, "rx-fcs"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_GRO_HW, "rx-gro-hw"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_GRO_LIST, "rx-gro-list"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_UDP_GRO_FORWARDING, "rx-udp-gro-forwarding"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD, "rx-udp_tunnel-port-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER, "rx-vlan-filter"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER, "rx-vlan-stag-filter"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE, "rx-vlan-stag-hw-parse"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD, "tls-hw-record"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_RX_OFFLOAD, "tls-hw-rx-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD, "tls-hw-tx-offload"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC, "tx-checksum-fcoe-crc"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4, "tx-checksum-ipv4"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6, "tx-checksum-ipv6"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC, "tx-checksum-ip-generic"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP, "tx-checksum-sctp"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION, "tx-esp-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION, "tx-fcoe-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION, "tx-gre-csum-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION, "tx-gre-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_LIST, "tx-gso-list"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL, "tx-gso-partial"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST, "tx-gso-robust"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION, "tx-ipxip4-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION, "tx-ipxip6-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY, "tx-nocache-copy"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER, "tx-scatter-gather"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST, "tx-scatter-gather-fraglist"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION, "tx-sctp-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION, "tx-tcp6-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION, "tx-tcp-ecn-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, "tx-tcp-mangleid-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION, "tx-tcp-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_TUNNEL_REMCSUM_SEGMENTATION, - "tx-tunnel-remcsum-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION, "tx-udp-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, "tx-udp_tnl-csum-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION, "tx-udp_tnl-segmentation"), - ETHT_FEAT(NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT, "tx-vlan-stag-hw-insert"), -}; - -/* the number of kernel features that we handle. It essentially is the sum of all - * kernel_names. So, all ethtool-ids that reference exactly one kernel-name - * (_NM_ETHTOOL_ID_FEATURE_NUM) + some extra, for ethtool-ids that are aliases - * for multiple kernel-names. */ -#define N_ETHTOOL_KERNEL_FEATURES (((guint) _NM_ETHTOOL_ID_FEATURE_NUM) + 8u) - -static void -_ASSERT_ethtool_feature_infos(void) -{ -#if NM_MORE_ASSERTS > 10 - guint i, k, n; - bool found[_NM_ETHTOOL_ID_FEATURE_NUM] = {}; - - G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(_ethtool_feature_infos) == _NM_ETHTOOL_ID_FEATURE_NUM); - - n = 0; - for (i = 0; i < G_N_ELEMENTS(_ethtool_feature_infos); i++) { - NMEthtoolFeatureState kstate; - const NMEthtoolFeatureInfo *inf = &_ethtool_feature_infos[i]; - - g_assert(inf->ethtool_id >= _NM_ETHTOOL_ID_FEATURE_FIRST); - g_assert(inf->ethtool_id <= _NM_ETHTOOL_ID_FEATURE_LAST); - g_assert(inf->n_kernel_names > 0); - - for (k = 0; k < i; k++) - g_assert(inf->ethtool_id != _ethtool_feature_infos[k].ethtool_id); - - g_assert(!found[_NM_ETHTOOL_ID_FEATURE_AS_IDX(inf->ethtool_id)]); - found[_NM_ETHTOOL_ID_FEATURE_AS_IDX(inf->ethtool_id)] = TRUE; - - kstate.idx_kernel_name = inf->n_kernel_names - 1; - g_assert((guint) kstate.idx_kernel_name == (guint)(inf->n_kernel_names - 1)); - - n += inf->n_kernel_names; - for (k = 0; k < inf->n_kernel_names; k++) { - const char *name = inf->kernel_names[k]; - - g_assert(nm_utils_strv_find_first((char **) inf->kernel_names, k, name) < 0); - - /* these offload features are only informational and cannot be set from user-space - * (NETIF_F_NEVER_CHANGE). We should not track them in _ethtool_feature_infos. */ - g_assert(!nm_streq(name, "netns-local")); - g_assert(!nm_streq(name, "tx-lockless")); - g_assert(!nm_streq(name, "vlan-challenged")); - } - } - - for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) - g_assert(found[i]); - - g_assert(n == N_ETHTOOL_KERNEL_FEATURES); -#endif -} - -static NMEthtoolFeatureStates * -ethtool_get_features(SocketHandle *shandle) -{ - gs_free NMEthtoolFeatureStates * states = NULL; - gs_free struct ethtool_gstrings *ss_features = NULL; - - _ASSERT_ethtool_feature_infos(); - - ss_features = ethtool_get_stringset(shandle, ETH_SS_FEATURES); - if (!ss_features) - return NULL; - - if (ss_features->len > 0) { - gs_free struct ethtool_gfeatures * gfeatures_free = NULL; - struct ethtool_gfeatures * gfeatures; - gsize gfeatures_len; - guint idx; - const NMEthtoolFeatureState * states_list0 = NULL; - const NMEthtoolFeatureState *const *states_plist0 = NULL; - guint states_plist_n = 0; - - gfeatures_len = sizeof(struct ethtool_gfeatures) - + (NM_DIV_ROUND_UP(ss_features->len, 32u) * sizeof(gfeatures->features[0])); - gfeatures = nm_malloc0_maybe_a(300, gfeatures_len, &gfeatures_free); - gfeatures->cmd = ETHTOOL_GFEATURES; - gfeatures->size = NM_DIV_ROUND_UP(ss_features->len, 32u); - if (_ethtool_call_handle(shandle, gfeatures, gfeatures_len) < 0) - return NULL; - - for (idx = 0; idx < G_N_ELEMENTS(_ethtool_feature_infos); idx++) { - const NMEthtoolFeatureInfo *info = &_ethtool_feature_infos[idx]; - guint idx_kernel_name; - - for (idx_kernel_name = 0; idx_kernel_name < info->n_kernel_names; idx_kernel_name++) { - NMEthtoolFeatureState *kstate; - const char * kernel_name = info->kernel_names[idx_kernel_name]; - int i_feature; - guint i_block; - guint32 i_flag; - - i_feature = ethtool_gstrings_find(ss_features, kernel_name); - if (i_feature < 0) - continue; - - i_block = ((guint) i_feature) / 32u; - i_flag = (guint32)(1u << (((guint) i_feature) % 32u)); - - if (!states) { - states = g_malloc0( - sizeof(NMEthtoolFeatureStates) - + (N_ETHTOOL_KERNEL_FEATURES * sizeof(NMEthtoolFeatureState)) - + ((N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos)) - * sizeof(NMEthtoolFeatureState *))); - states_list0 = &states->states_list[0]; - states_plist0 = (gpointer) &states_list0[N_ETHTOOL_KERNEL_FEATURES]; - states->n_ss_features = ss_features->len; - } - - nm_assert(states->n_states < N_ETHTOOL_KERNEL_FEATURES); - kstate = (NMEthtoolFeatureState *) &states_list0[states->n_states]; - states->n_states++; - - kstate->info = info; - kstate->idx_ss_features = i_feature; - kstate->idx_kernel_name = idx_kernel_name; - kstate->available = !!(gfeatures->features[i_block].available & i_flag); - kstate->requested = !!(gfeatures->features[i_block].requested & i_flag); - kstate->active = !!(gfeatures->features[i_block].active & i_flag); - kstate->never_changed = !!(gfeatures->features[i_block].never_changed & i_flag); - - nm_assert(states_plist_n - < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos)); - - if (!states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)]) - states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)] = - &states_plist0[states_plist_n]; - ((const NMEthtoolFeatureState **) states_plist0)[states_plist_n] = kstate; - states_plist_n++; - } - - if (states && states->states_indexed[_NM_ETHTOOL_ID_FEATURE_AS_IDX(info->ethtool_id)]) { - nm_assert(states_plist_n - < N_ETHTOOL_KERNEL_FEATURES + G_N_ELEMENTS(_ethtool_feature_infos)); - nm_assert(!states_plist0[states_plist_n]); - states_plist_n++; - } - } - } - - return g_steal_pointer(&states); -} - -NMEthtoolFeatureStates * -nmp_utils_ethtool_get_features(int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - NMEthtoolFeatureStates * features; - - g_return_val_if_fail(ifindex > 0, 0); - - features = ethtool_get_features(&shandle); - - if (!features) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure getting features", - ifindex, - "get-features"); - return NULL; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: retrieved kernel features", - ifindex, - "get-features"); - return features; -} - -static const char * -_ethtool_feature_state_to_string(char * buf, - gsize buf_size, - const NMEthtoolFeatureState *s, - const char * prefix) -{ - int l; - - l = g_snprintf(buf, - buf_size, - "%s %s%s", - prefix ?: "", - ONOFF(s->active), - (!s->available || s->never_changed) - ? ", [fixed]" - : ((s->requested != s->active) - ? (s->requested ? ", [requested on]" : ", [requested off]") - : "")); - nm_assert(l < buf_size); - return buf; -} - -gboolean -nmp_utils_ethtool_set_features( - int ifindex, - const NMEthtoolFeatureStates *features, - const NMOptionBool *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, - gboolean do_set /* or reset */) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - gs_free struct ethtool_sfeatures * sfeatures_free = NULL; - struct ethtool_sfeatures * sfeatures; - gsize sfeatures_len; - int r; - guint i, j; - struct { - const NMEthtoolFeatureState *f_state; - NMOptionBool requested; - } set_states[N_ETHTOOL_KERNEL_FEATURES]; - guint set_states_n = 0; - gboolean success = TRUE; - - g_return_val_if_fail(ifindex > 0, 0); - g_return_val_if_fail(features, 0); - g_return_val_if_fail(requested, 0); - - nm_assert(features->n_states <= N_ETHTOOL_KERNEL_FEATURES); - - for (i = 0; i < _NM_ETHTOOL_ID_FEATURE_NUM; i++) { - const NMEthtoolFeatureState *const *states_indexed; - - if (requested[i] == NM_OPTION_BOOL_DEFAULT) - continue; - - if (!(states_indexed = features->states_indexed[i])) { - if (do_set) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: set feature %s: skip (not found)", - ifindex, - "set-features", - nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname); - success = FALSE; - } - continue; - } - - for (j = 0; states_indexed[j]; j++) { - const NMEthtoolFeatureState *s = states_indexed[j]; - char sbuf[255]; - - if (set_states_n >= G_N_ELEMENTS(set_states)) - g_return_val_if_reached(FALSE); - - if (s->never_changed) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: %s feature %s (%s): %s, %s (skip feature marked as " - "never changed)", - ifindex, - "set-features", - do_set ? "set" : "reset", - nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname, - s->info->kernel_names[s->idx_kernel_name], - ONOFF(do_set ? requested[i] == NM_OPTION_BOOL_TRUE : s->active), - _ethtool_feature_state_to_string(sbuf, - sizeof(sbuf), - s, - do_set ? " currently:" : " before:")); - continue; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: %s feature %s (%s): %s, %s", - ifindex, - "set-features", - do_set ? "set" : "reset", - nm_ethtool_data[i + _NM_ETHTOOL_ID_FEATURE_FIRST]->optname, - s->info->kernel_names[s->idx_kernel_name], - ONOFF(do_set ? requested[i] == NM_OPTION_BOOL_TRUE : s->active), - _ethtool_feature_state_to_string(sbuf, - sizeof(sbuf), - s, - do_set ? " currently:" : " before:")); - - if (do_set && (!s->available || s->never_changed) - && (s->active != (requested[i] == NM_OPTION_BOOL_TRUE))) { - /* we request to change a flag which kernel reported as fixed. - * While the ethtool operation will silently succeed, mark the request - * as failure. */ - success = FALSE; - } - - set_states[set_states_n].f_state = s; - set_states[set_states_n].requested = requested[i]; - set_states_n++; - } - } - - if (set_states_n == 0) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: no feature requested", - ifindex, - "set-features"); - return TRUE; - } - - sfeatures_len = - sizeof(struct ethtool_sfeatures) - + (NM_DIV_ROUND_UP(features->n_ss_features, 32U) * sizeof(sfeatures->features[0])); - sfeatures = nm_malloc0_maybe_a(300, sfeatures_len, &sfeatures_free); - sfeatures->cmd = ETHTOOL_SFEATURES; - sfeatures->size = NM_DIV_ROUND_UP(features->n_ss_features, 32U); - - for (i = 0; i < set_states_n; i++) { - const NMEthtoolFeatureState *s = set_states[i].f_state; - guint i_block; - guint32 i_flag; - gboolean is_requested; - - i_block = s->idx_ss_features / 32u; - i_flag = (guint32)(1u << (s->idx_ss_features % 32u)); - - sfeatures->features[i_block].valid |= i_flag; - - if (do_set) - is_requested = (set_states[i].requested == NM_OPTION_BOOL_TRUE); - else - is_requested = s->active; - - if (is_requested) - sfeatures->features[i_block].requested |= i_flag; - else - sfeatures->features[i_block].requested &= ~i_flag; - } - - r = _ethtool_call_handle(&shandle, sfeatures, sfeatures_len); - if (r < 0) { - success = FALSE; - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure setting features (%s)", - ifindex, - "set-features", - nm_strerror_native(-r)); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: %s", - ifindex, - "set-features", - success ? "successfully setting features" - : "at least some of the features were not successfully set"); - return success; -} - -static gboolean -ethtool_get_coalesce(SocketHandle *shandle, NMEthtoolCoalesceState *coalesce) -{ - struct ethtool_coalesce eth_data; - - eth_data.cmd = ETHTOOL_GCOALESCE; - - if (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_coalesce)) != 0) - return FALSE; - - *coalesce = (NMEthtoolCoalesceState){ - .s = { - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS)] = - eth_data.rx_coalesce_usecs, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES)] = - eth_data.rx_max_coalesced_frames, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ)] = - eth_data.rx_coalesce_usecs_irq, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ)] = - eth_data.rx_max_coalesced_frames_irq, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS)] = - eth_data.tx_coalesce_usecs, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES)] = - eth_data.tx_max_coalesced_frames, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ)] = - eth_data.tx_coalesce_usecs_irq, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ)] = - eth_data.tx_max_coalesced_frames_irq, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS)] = - eth_data.stats_block_coalesce_usecs, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX)] = - eth_data.use_adaptive_rx_coalesce, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX)] = - eth_data.use_adaptive_tx_coalesce, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW)] = - eth_data.pkt_rate_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW)] = - eth_data.rx_coalesce_usecs_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW)] = - eth_data.rx_max_coalesced_frames_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW)] = - eth_data.tx_coalesce_usecs_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW)] = - eth_data.tx_max_coalesced_frames_low, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH)] = - eth_data.pkt_rate_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH)] = - eth_data.rx_coalesce_usecs_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH)] = - eth_data.rx_max_coalesced_frames_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH)] = - eth_data.tx_coalesce_usecs_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH)] = - eth_data.tx_max_coalesced_frames_high, - [_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL)] = - eth_data.rate_sample_interval, - }}; - return TRUE; -} - -gboolean -nmp_utils_ethtool_get_coalesce(int ifindex, NMEthtoolCoalesceState *coalesce) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(coalesce, FALSE); - - if (!ethtool_get_coalesce(&shandle, coalesce)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure getting coalesce settings", - ifindex, - "get-coalesce"); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: retrieved kernel coalesce settings", - ifindex, - "get-coalesce"); - return TRUE; -} - -static gboolean -ethtool_set_coalesce(SocketHandle *shandle, const NMEthtoolCoalesceState *coalesce) -{ - struct ethtool_coalesce eth_data; - gboolean success; - - nm_assert(shandle); - nm_assert(coalesce); - - eth_data = (struct ethtool_coalesce){ - .cmd = ETHTOOL_SCOALESCE, - .rx_coalesce_usecs = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS)], - .rx_max_coalesced_frames = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES)], - .rx_coalesce_usecs_irq = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_IRQ)], - .rx_max_coalesced_frames_irq = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_IRQ)], - .tx_coalesce_usecs = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS)], - .tx_max_coalesced_frames = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES)], - .tx_coalesce_usecs_irq = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_IRQ)], - .tx_max_coalesced_frames_irq = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_IRQ)], - .stats_block_coalesce_usecs = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_STATS_BLOCK_USECS)], - .use_adaptive_rx_coalesce = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_RX)], - .use_adaptive_tx_coalesce = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_ADAPTIVE_TX)], - .pkt_rate_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_LOW)], - .rx_coalesce_usecs_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_LOW)], - .rx_max_coalesced_frames_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_LOW)], - .tx_coalesce_usecs_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_LOW)], - .tx_max_coalesced_frames_low = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_LOW)], - .pkt_rate_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_PKT_RATE_HIGH)], - .rx_coalesce_usecs_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_USECS_HIGH)], - .rx_max_coalesced_frames_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_RX_FRAMES_HIGH)], - .tx_coalesce_usecs_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_USECS_HIGH)], - .tx_max_coalesced_frames_high = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_TX_FRAMES_HIGH)], - .rate_sample_interval = - coalesce->s[_NM_ETHTOOL_ID_COALESCE_AS_IDX(NM_ETHTOOL_ID_COALESCE_SAMPLE_INTERVAL)], - }; - - success = (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_coalesce)) == 0); - return success; -} - -gboolean -nmp_utils_ethtool_set_coalesce(int ifindex, const NMEthtoolCoalesceState *coalesce) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(coalesce, FALSE); - - if (!ethtool_set_coalesce(&shandle, coalesce)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure setting coalesce settings", - ifindex, - "set-coalesce"); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: set kernel coalesce settings", - ifindex, - "set-coalesce"); - return TRUE; -} - -static gboolean -ethtool_get_ring(SocketHandle *shandle, NMEthtoolRingState *ring) -{ - struct ethtool_ringparam eth_data; - - eth_data.cmd = ETHTOOL_GRINGPARAM; - - if (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_ringparam)) != 0) - return FALSE; - - ring->rx_pending = eth_data.rx_pending; - ring->rx_jumbo_pending = eth_data.rx_jumbo_pending; - ring->rx_mini_pending = eth_data.rx_mini_pending; - ring->tx_pending = eth_data.tx_pending; - - return TRUE; -} - -gboolean -nmp_utils_ethtool_get_ring(int ifindex, NMEthtoolRingState *ring) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(ring, FALSE); - - if (!ethtool_get_ring(&shandle, ring)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure getting ring settings", - ifindex, - "get-ring"); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: retrieved kernel ring settings", - ifindex, - "get-ring"); - return TRUE; -} - -static gboolean -ethtool_set_ring(SocketHandle *shandle, const NMEthtoolRingState *ring) -{ - gboolean success; - struct ethtool_ringparam eth_data; - - g_return_val_if_fail(shandle, FALSE); - g_return_val_if_fail(ring, FALSE); - - eth_data = (struct ethtool_ringparam){ - .cmd = ETHTOOL_SRINGPARAM, - .rx_pending = ring->rx_pending, - .rx_jumbo_pending = ring->rx_jumbo_pending, - .rx_mini_pending = ring->rx_mini_pending, - .tx_pending = ring->tx_pending, - }; - - success = (_ethtool_call_handle(shandle, ð_data, sizeof(struct ethtool_ringparam)) == 0); - return success; -} - -gboolean -nmp_utils_ethtool_set_ring(int ifindex, const NMEthtoolRingState *ring) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(ring, FALSE); - - if (!ethtool_set_ring(&shandle, ring)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %s: failure setting ring settings", - ifindex, - "set-ring"); - return FALSE; - } - - nm_log_trace(LOGD_PLATFORM, "ethtool[%d]: %s: set kernel ring settings", ifindex, "set-ring"); - return TRUE; -} - -/*****************************************************************************/ - -gboolean -nmp_utils_ethtool_get_driver_info(int ifindex, NMPUtilsEthtoolDriverInfo *data) -{ - struct ethtool_drvinfo *drvinfo; - - G_STATIC_ASSERT_EXPR(sizeof(*data) == sizeof(*drvinfo)); - G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, driver) - == offsetof(struct ethtool_drvinfo, driver)); - G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, version) - == offsetof(struct ethtool_drvinfo, version)); - G_STATIC_ASSERT_EXPR(offsetof(NMPUtilsEthtoolDriverInfo, fw_version) - == offsetof(struct ethtool_drvinfo, fw_version)); - G_STATIC_ASSERT_EXPR(sizeof(data->driver) == sizeof(drvinfo->driver)); - G_STATIC_ASSERT_EXPR(sizeof(data->version) == sizeof(drvinfo->version)); - G_STATIC_ASSERT_EXPR(sizeof(data->fw_version) == sizeof(drvinfo->fw_version)); - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail(data, FALSE); - - drvinfo = (struct ethtool_drvinfo *) data; - *drvinfo = (struct ethtool_drvinfo){ - .cmd = ETHTOOL_GDRVINFO, - }; - return _ethtool_call_once(ifindex, drvinfo, sizeof(*drvinfo)) >= 0; -} - -gboolean -nmp_utils_ethtool_get_permanent_address(int ifindex, guint8 *buf, size_t *length) -{ - struct { - struct ethtool_perm_addr e; - guint8 _extra_data[_NM_UTILS_HWADDR_LEN_MAX + 1]; - } edata = { - .e.cmd = ETHTOOL_GPERMADDR, - .e.size = _NM_UTILS_HWADDR_LEN_MAX, - }; - const guint8 *pdata; - - guint i; - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (_ethtool_call_once(ifindex, &edata, sizeof(edata)) < 0) - return FALSE; - - if (edata.e.size > _NM_UTILS_HWADDR_LEN_MAX) - return FALSE; - if (edata.e.size < 1) - return FALSE; - - pdata = (const guint8 *) edata.e.data; - - if (NM_IN_SET(pdata[0], 0, 0xFF)) { - /* Some drivers might return a permanent address of all zeros. - * Reject that (rh#1264024) - * - * Some drivers return a permanent address of all ones. Reject that too */ - for (i = 1; i < edata.e.size; i++) { - if (pdata[0] != pdata[i]) - goto not_all_0or1; - } - return FALSE; - } - -not_all_0or1: - memcpy(buf, pdata, edata.e.size); - *length = edata.e.size; - return TRUE; -} - -gboolean -nmp_utils_ethtool_supports_carrier_detect(int ifindex) -{ - struct ethtool_cmd edata = {.cmd = ETHTOOL_GLINK}; - - g_return_val_if_fail(ifindex > 0, FALSE); - - /* We ignore the result. If the ETHTOOL_GLINK call succeeded, then we - * assume the device supports carrier-detect, otherwise we assume it - * doesn't. - */ - return _ethtool_call_once(ifindex, &edata, sizeof(edata)) >= 0; -} - -gboolean -nmp_utils_ethtool_supports_vlans(int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - gs_free struct ethtool_gfeatures * features_free = NULL; - struct ethtool_gfeatures * features; - gsize features_len; - int idx, block, bit, size; - - g_return_val_if_fail(ifindex > 0, FALSE); - - idx = ethtool_get_stringset_index(&shandle, ETH_SS_FEATURES, "vlan-challenged"); - if (idx < 0) { - nm_log_dbg(LOGD_PLATFORM, - "ethtool[%d]: vlan-challenged ethtool feature does not exist?", - ifindex); - return FALSE; - } - - block = idx / 32; - bit = idx % 32; - size = block + 1; - - features_len = sizeof(*features) + (size * sizeof(struct ethtool_get_features_block)); - features = nm_malloc0_maybe_a(300, features_len, &features_free); - features->cmd = ETHTOOL_GFEATURES; - features->size = size; - - if (_ethtool_call_handle(&shandle, features, features_len) < 0) - return FALSE; - - return !(features->features[block].active & (1 << bit)); -} - -int -nmp_utils_ethtool_get_peer_ifindex(int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - gsize stats_len; - gs_free struct ethtool_stats * stats_free = NULL; - struct ethtool_stats * stats; - int peer_ifindex_stat; - - g_return_val_if_fail(ifindex > 0, 0); - - peer_ifindex_stat = ethtool_get_stringset_index(&shandle, ETH_SS_STATS, "peer_ifindex"); - if (peer_ifindex_stat < 0) { - nm_log_dbg(LOGD_PLATFORM, "ethtool[%d]: peer_ifindex stat does not exist?", ifindex); - return FALSE; - } - - stats_len = sizeof(*stats) + (peer_ifindex_stat + 1) * sizeof(guint64); - stats = nm_malloc0_maybe_a(300, stats_len, &stats_free); - stats->cmd = ETHTOOL_GSTATS; - stats->n_stats = peer_ifindex_stat + 1; - if (_ethtool_call_handle(&shandle, stats, stats_len) < 0) - return 0; - - return stats->data[peer_ifindex_stat]; -} - -gboolean -nmp_utils_ethtool_get_wake_on_lan(int ifindex) -{ - struct ethtool_wolinfo wol = { - .cmd = ETHTOOL_GWOL, - }; - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (_ethtool_call_once(ifindex, &wol, sizeof(wol)) < 0) - return FALSE; - - return wol.wolopts != 0; -} - -gboolean -nmp_utils_ethtool_get_link_settings(int ifindex, - gboolean * out_autoneg, - guint32 * out_speed, - NMPlatformLinkDuplexType *out_duplex) -{ - struct ethtool_cmd edata = { - .cmd = ETHTOOL_GSET, - }; - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (_ethtool_call_once(ifindex, &edata, sizeof(edata)) < 0) - return FALSE; - - NM_SET_OUT(out_autoneg, (edata.autoneg == AUTONEG_ENABLE)); - - if (out_speed) { - guint32 speed; - - speed = ethtool_cmd_speed(&edata); - if (speed == G_MAXUINT16 || speed == G_MAXUINT32) - speed = 0; - - *out_speed = speed; - } - - if (out_duplex) { - switch (edata.duplex) { - case DUPLEX_HALF: - *out_duplex = NM_PLATFORM_LINK_DUPLEX_HALF; - break; - case DUPLEX_FULL: - *out_duplex = NM_PLATFORM_LINK_DUPLEX_FULL; - break; - default: /* DUPLEX_UNKNOWN */ - *out_duplex = NM_PLATFORM_LINK_DUPLEX_UNKNOWN; - break; - } - } - - return TRUE; -} - -#define ADVERTISED_INVALID 0 -#define BASET_ALL_MODES \ - (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half \ - | ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full \ - | ADVERTISED_10000baseT_Full) - -static guint32 -get_baset_mode(guint32 speed, NMPlatformLinkDuplexType duplex) -{ - if (duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN) - return ADVERTISED_INVALID; - - if (duplex == NM_PLATFORM_LINK_DUPLEX_HALF) { - switch (speed) { - case 10: - return ADVERTISED_10baseT_Half; - case 100: - return ADVERTISED_100baseT_Half; - case 1000: - return ADVERTISED_1000baseT_Half; - default: - return ADVERTISED_INVALID; - } - } else { - switch (speed) { - case 10: - return ADVERTISED_10baseT_Full; - case 100: - return ADVERTISED_100baseT_Full; - case 1000: - return ADVERTISED_1000baseT_Full; - case 10000: - return ADVERTISED_10000baseT_Full; - default: - return ADVERTISED_INVALID; - } - } -} - -gboolean -nmp_utils_ethtool_set_link_settings(int ifindex, - gboolean autoneg, - guint32 speed, - NMPlatformLinkDuplexType duplex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - struct ethtool_cmd edata = { - .cmd = ETHTOOL_GSET, - }; - - g_return_val_if_fail(ifindex > 0, FALSE); - g_return_val_if_fail((speed && duplex != NM_PLATFORM_LINK_DUPLEX_UNKNOWN) - || (!speed && duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN), - FALSE); - - /* retrieve first current settings */ - if (_ethtool_call_handle(&shandle, &edata, sizeof(edata)) < 0) - return FALSE; - - /* FIXME: try first new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API - * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3f1ac7a700d039c61d8d8b99f28d605d489a60cf - */ - - /* then change the needed ones */ - edata.cmd = ETHTOOL_SSET; - if (autoneg) { - edata.autoneg = AUTONEG_ENABLE; - if (!speed) - edata.advertising = edata.supported; - else { - guint32 mode; - - mode = get_baset_mode(speed, duplex); - - if (!mode) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: %uBASE-T %s duplex mode cannot be advertised", - ifindex, - speed, - nm_platform_link_duplex_type_to_string(duplex)); - return FALSE; - } - if (!(edata.supported & mode)) { - nm_log_trace(LOGD_PLATFORM, - "ethtool[%d]: device does not support %uBASE-T %s duplex mode", - ifindex, - speed, - nm_platform_link_duplex_type_to_string(duplex)); - return FALSE; - } - edata.advertising = (edata.supported & ~BASET_ALL_MODES) | mode; - } - } else { - edata.autoneg = AUTONEG_DISABLE; - - if (speed) - ethtool_cmd_speed_set(&edata, speed); - - switch (duplex) { - case NM_PLATFORM_LINK_DUPLEX_HALF: - edata.duplex = DUPLEX_HALF; - break; - case NM_PLATFORM_LINK_DUPLEX_FULL: - edata.duplex = DUPLEX_FULL; - break; - case NM_PLATFORM_LINK_DUPLEX_UNKNOWN: - break; - default: - g_return_val_if_reached(FALSE); - } - } - - return _ethtool_call_handle(&shandle, &edata, sizeof(edata)) >= 0; -} - -gboolean -nmp_utils_ethtool_set_wake_on_lan(int ifindex, - _NMSettingWiredWakeOnLan wol, - const char * wol_password) -{ - struct ethtool_wolinfo wol_info = { - .cmd = ETHTOOL_SWOL, - .wolopts = 0, - }; - - g_return_val_if_fail(ifindex > 0, FALSE); - - if (wol == _NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE) - return TRUE; - - nm_log_dbg(LOGD_PLATFORM, - "ethtool[%d]: setting Wake-on-LAN options 0x%x, password '%s'", - ifindex, - (unsigned) wol, - wol_password); - - if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_PHY)) - wol_info.wolopts |= WAKE_PHY; - if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST)) - wol_info.wolopts |= WAKE_UCAST; - if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST)) - wol_info.wolopts |= WAKE_MCAST; - if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST)) - wol_info.wolopts |= WAKE_BCAST; - if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_ARP)) - wol_info.wolopts |= WAKE_ARP; - if (NM_FLAGS_HAS(wol, _NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) - wol_info.wolopts |= WAKE_MAGIC; - - if (wol_password) { - if (!_nm_utils_hwaddr_aton_exact(wol_password, wol_info.sopass, ETH_ALEN)) { - nm_log_dbg(LOGD_PLATFORM, - "ethtool[%d]: couldn't parse Wake-on-LAN password '%s'", - ifindex, - wol_password); - return FALSE; - } - wol_info.wolopts |= WAKE_MAGICSECURE; - } - - return _ethtool_call_once(ifindex, &wol_info, sizeof(wol_info)) >= 0; -} - -/****************************************************************************** - * mii - *****************************************************************************/ - -gboolean -nmp_utils_mii_supports_carrier_detect(int ifindex) -{ - nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT(ifindex); - int r; - struct ifreq ifr; - struct mii_ioctl_data * mii; - - g_return_val_if_fail(ifindex > 0, FALSE); - - r = _ioctl_call("mii", - "SIOCGMIIPHY", - SIOCGMIIPHY, - shandle.ifindex, - &shandle.fd, - shandle.ifname, - IOCTL_CALL_DATA_TYPE_NONE, - NULL, - 0, - &ifr); - if (r < 0) - return FALSE; - - /* If we can read the BMSR register, we assume that the card supports MII link detection */ - mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; - mii->reg_num = MII_BMSR; - - r = _ioctl_call("mii", - "SIOCGMIIREG", - SIOCGMIIREG, - shandle.ifindex, - &shandle.fd, - shandle.ifname, - IOCTL_CALL_DATA_TYPE_IFRU, - mii, - sizeof(*mii), - &ifr); - if (r < 0) - return FALSE; - - mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; - nm_log_trace(LOGD_PLATFORM, - "mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X", - ifindex, - shandle.ifname, - mii->val_out); - return TRUE; -} - -/****************************************************************************** - * udev - *****************************************************************************/ - -const char * -nmp_utils_udev_get_driver(struct udev_device *udevice) -{ - struct udev_device *parent = NULL, *grandparent = NULL; - const char * driver, *subsys; - - driver = udev_device_get_driver(udevice); - if (driver) - goto out; - - /* Try the parent */ - parent = udev_device_get_parent(udevice); - if (parent) { - driver = udev_device_get_driver(parent); - if (!driver) { - /* Try the grandparent if it's an ibmebus device or if the - * subsys is NULL which usually indicates some sort of - * platform device like a 'gadget' net interface. - */ - subsys = udev_device_get_subsystem(parent); - if ((g_strcmp0(subsys, "ibmebus") == 0) || (subsys == NULL)) { - grandparent = udev_device_get_parent(parent); - if (grandparent) - driver = udev_device_get_driver(grandparent); - } - } - } - -out: - /* Intern the string so we don't have to worry about memory - * management in NMPlatformLink. */ - return g_intern_string(driver); -} - -/****************************************************************************** - * utils - *****************************************************************************/ - -NMIPConfigSource -nmp_utils_ip_config_source_from_rtprot(guint8 rtprot) -{ - return ((int) rtprot) + 1; -} - -NMIPConfigSource -nmp_utils_ip_config_source_round_trip_rtprot(NMIPConfigSource source) -{ - /* when adding a route to kernel for a give @source, the resulting route - * will be put into the cache with a source of NM_IP_CONFIG_SOURCE_RTPROT_*. - * This function returns that. */ - return nmp_utils_ip_config_source_from_rtprot( - nmp_utils_ip_config_source_coerce_to_rtprot(source)); -} - -guint8 -nmp_utils_ip_config_source_coerce_to_rtprot(NMIPConfigSource source) -{ - /* when adding a route to kernel, we coerce the @source field - * to rtm_protocol. This is not lossless as we map different - * source values to the same RTPROT uint8 value. */ - if (source <= NM_IP_CONFIG_SOURCE_UNKNOWN) - return RTPROT_UNSPEC; - - if (source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST) - return source - 1; - - switch (source) { - case NM_IP_CONFIG_SOURCE_KERNEL: - return RTPROT_KERNEL; - case NM_IP_CONFIG_SOURCE_IP6LL: - return RTPROT_KERNEL; - case NM_IP_CONFIG_SOURCE_DHCP: - return RTPROT_DHCP; - case NM_IP_CONFIG_SOURCE_NDISC: - return RTPROT_RA; - - default: - return RTPROT_STATIC; - } -} - -NMIPConfigSource -nmp_utils_ip_config_source_coerce_from_rtprot(NMIPConfigSource source) -{ - /* When we receive a route from kernel and put it into the platform cache, - * we preserve the protocol field by converting it to a NMIPConfigSource - * via nmp_utils_ip_config_source_from_rtprot(). - * - * However, that is not the inverse of nmp_utils_ip_config_source_coerce_to_rtprot(). - * Instead, to go back to the original value, you need another step: - * nmp_utils_ip_config_source_coerce_from_rtprot (nmp_utils_ip_config_source_from_rtprot (rtprot)). - * - * This might partly restore the original source value, but of course that - * is not really possible because nmp_utils_ip_config_source_coerce_to_rtprot() - * is not injective. - * */ - switch (source) { - case NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC: - return NM_IP_CONFIG_SOURCE_UNKNOWN; - - case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: - case NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT: - return NM_IP_CONFIG_SOURCE_KERNEL; - - case NM_IP_CONFIG_SOURCE_RTPROT_RA: - return NM_IP_CONFIG_SOURCE_NDISC; - - case NM_IP_CONFIG_SOURCE_RTPROT_DHCP: - return NM_IP_CONFIG_SOURCE_DHCP; - - default: - return NM_IP_CONFIG_SOURCE_USER; - } -} - -const char * -nmp_utils_ip_config_source_to_string(NMIPConfigSource source, char *buf, gsize len) -{ - const char *s = NULL; - nm_utils_to_string_buffer_init(&buf, &len); - - if (!len) - return buf; - - switch (source) { - case NM_IP_CONFIG_SOURCE_UNKNOWN: - s = "unknown"; - break; - - case NM_IP_CONFIG_SOURCE_RTPROT_UNSPEC: - s = "rt-unspec"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_REDIRECT: - s = "rt-redirect"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_KERNEL: - s = "rt-kernel"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_BOOT: - s = "rt-boot"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_STATIC: - s = "rt-static"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_DHCP: - s = "rt-dhcp"; - break; - case NM_IP_CONFIG_SOURCE_RTPROT_RA: - s = "rt-ra"; - break; - - case NM_IP_CONFIG_SOURCE_KERNEL: - s = "kernel"; - break; - case NM_IP_CONFIG_SOURCE_SHARED: - s = "shared"; - break; - case NM_IP_CONFIG_SOURCE_IP4LL: - s = "ipv4ll"; - break; - case NM_IP_CONFIG_SOURCE_IP6LL: - s = "ipv6ll"; - break; - case NM_IP_CONFIG_SOURCE_PPP: - s = "ppp"; - break; - case NM_IP_CONFIG_SOURCE_WWAN: - s = "wwan"; - break; - case NM_IP_CONFIG_SOURCE_VPN: - s = "vpn"; - break; - case NM_IP_CONFIG_SOURCE_DHCP: - s = "dhcp"; - break; - case NM_IP_CONFIG_SOURCE_NDISC: - s = "ndisc"; - break; - case NM_IP_CONFIG_SOURCE_USER: - s = "user"; - break; - default: - break; - } - - if (source >= 1 && source <= 0x100) { - if (s) - g_snprintf(buf, len, "%s", s); - else - g_snprintf(buf, len, "rt-%d", ((int) source) - 1); - } else { - if (s) - g_strlcpy(buf, s, len); - else - g_snprintf(buf, len, "(%d)", source); - } - return buf; -} - -/** - * nmp_utils_sysctl_open_netdir: - * @ifindex: the ifindex for which to open "/sys/class/net/%s" - * @ifname_guess: (allow-none): optional argument, if present used as initial - * guess as the current name for @ifindex. If guessed right, - * it saves an additional if_indextoname() call. - * @out_ifname: (allow-none): if present, must be at least IFNAMSIZ - * characters. On success, this will contain the actual ifname - * found while opening the directory. - * - * Returns: a negative value on failure, on success returns the open fd - * to the "/sys/class/net/%s" directory for @ifindex. - */ -int -nmp_utils_sysctl_open_netdir(int ifindex, const char *ifname_guess, char *out_ifname) -{ -#define SYS_CLASS_NET "/sys/class/net/" - const char *ifname = ifname_guess; - char ifname_buf_last_try[IFNAMSIZ]; - char ifname_buf[IFNAMSIZ]; - guint try_count = 0; - char sysdir[NM_STRLEN(SYS_CLASS_NET) + IFNAMSIZ] = SYS_CLASS_NET; - char fd_buf[256]; - ssize_t nn; - - g_return_val_if_fail(ifindex >= 0, -1); - - ifname_buf_last_try[0] = '\0'; - - for (try_count = 0; try_count < 10; try_count++, ifname = NULL) { - nm_auto_close int fd_dir = -1; - nm_auto_close int fd_ifindex = -1; - - if (!ifname) { - ifname = nmp_utils_if_indextoname(ifindex, ifname_buf); - if (!ifname) - return -1; - } - - nm_assert(nm_utils_ifname_valid_kernel(ifname, NULL)); - - if (g_strlcpy(&sysdir[NM_STRLEN(SYS_CLASS_NET)], ifname, IFNAMSIZ) >= IFNAMSIZ) - g_return_val_if_reached(-1); - - /* we only retry, if the name changed since previous attempt. - * Hence, it is extremely unlikely that this loop runes until the - * end of the @try_count. */ - if (nm_streq(ifname, ifname_buf_last_try)) - return -1; - strcpy(ifname_buf_last_try, ifname); - - fd_dir = open(sysdir, O_DIRECTORY | O_CLOEXEC); - if (fd_dir < 0) - continue; - - fd_ifindex = openat(fd_dir, "ifindex", O_CLOEXEC); - if (fd_ifindex < 0) - continue; - - nn = nm_utils_fd_read_loop(fd_ifindex, fd_buf, sizeof(fd_buf) - 2, FALSE); - if (nn <= 0) - continue; - fd_buf[nn] = '\0'; - - if (ifindex != (int) _nm_utils_ascii_str_to_int64(fd_buf, 10, 1, G_MAXINT, -1)) - continue; - - if (out_ifname) - strcpy(out_ifname, ifname); - - return nm_steal_fd(&fd_dir); - } - - return -1; -} diff --git a/shared/nm-platform/nm-platform-utils.h b/shared/nm-platform/nm-platform-utils.h deleted file mode 100644 index d74723eb9f..0000000000 --- a/shared/nm-platform/nm-platform-utils.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_PLATFORM_UTILS_H__ -#define __NM_PLATFORM_UTILS_H__ - -#include "nm-base/nm-base.h" -#include "nm-platform/nmp-base.h" - -/*****************************************************************************/ - -const char *nmp_utils_ethtool_get_driver(int ifindex); -gboolean nmp_utils_ethtool_supports_carrier_detect(int ifindex); -gboolean nmp_utils_ethtool_supports_vlans(int ifindex); -int nmp_utils_ethtool_get_peer_ifindex(int ifindex); -gboolean nmp_utils_ethtool_get_wake_on_lan(int ifindex); -gboolean nmp_utils_ethtool_set_wake_on_lan(int ifindex, - _NMSettingWiredWakeOnLan wol, - const char * wol_password); - -const char *nm_platform_link_duplex_type_to_string(NMPlatformLinkDuplexType duplex); - -gboolean nmp_utils_ethtool_get_link_settings(int ifindex, - gboolean * out_autoneg, - guint32 * out_speed, - NMPlatformLinkDuplexType *out_duplex); -gboolean nmp_utils_ethtool_set_link_settings(int ifindex, - gboolean autoneg, - guint32 speed, - NMPlatformLinkDuplexType duplex); - -gboolean nmp_utils_ethtool_get_permanent_address(int ifindex, guint8 *buf, size_t *length); - -gboolean nmp_utils_ethtool_get_driver_info(int ifindex, NMPUtilsEthtoolDriverInfo *data); - -NMEthtoolFeatureStates *nmp_utils_ethtool_get_features(int ifindex); - -gboolean nmp_utils_ethtool_set_features( - int ifindex, - const NMEthtoolFeatureStates *features, - const NMOptionBool *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, - gboolean do_set /* or reset */); - -gboolean nmp_utils_ethtool_get_coalesce(int ifindex, NMEthtoolCoalesceState *coalesce); - -gboolean nmp_utils_ethtool_set_coalesce(int ifindex, const NMEthtoolCoalesceState *coalesce); - -gboolean nmp_utils_ethtool_get_ring(int ifindex, NMEthtoolRingState *ring); - -gboolean nmp_utils_ethtool_set_ring(int ifindex, const NMEthtoolRingState *ring); - -/*****************************************************************************/ - -gboolean nmp_utils_mii_supports_carrier_detect(int ifindex); - -struct udev_device; - -const char *nmp_utils_udev_get_driver(struct udev_device *udevice); - -NMIPConfigSource nmp_utils_ip_config_source_from_rtprot(guint8 rtprot) _nm_const; -guint8 nmp_utils_ip_config_source_coerce_to_rtprot(NMIPConfigSource source) _nm_const; -NMIPConfigSource nmp_utils_ip_config_source_coerce_from_rtprot(NMIPConfigSource source) _nm_const; -NMIPConfigSource nmp_utils_ip_config_source_round_trip_rtprot(NMIPConfigSource source) _nm_const; -const char *nmp_utils_ip_config_source_to_string(NMIPConfigSource source, char *buf, gsize len); - -const char *nmp_utils_if_indextoname(int ifindex, char *out_ifname /*IFNAMSIZ*/); -int nmp_utils_if_nametoindex(const char *ifname); - -int nmp_utils_sysctl_open_netdir(int ifindex, const char *ifname_guess, char *out_ifname); - -#endif /* __NM_PLATFORM_UTILS_H__ */ diff --git a/shared/nm-platform/nmp-base.h b/shared/nm-platform/nmp-base.h deleted file mode 100644 index 210c26d6bb..0000000000 --- a/shared/nm-platform/nmp-base.h +++ /dev/null @@ -1,94 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#ifndef __NMP_FWD_H__ -#define __NMP_FWD_H__ - -#include "nm-base/nm-base.h" - -/*****************************************************************************/ - -typedef enum { - NM_PLATFORM_LINK_DUPLEX_UNKNOWN, - NM_PLATFORM_LINK_DUPLEX_HALF, - NM_PLATFORM_LINK_DUPLEX_FULL, -} NMPlatformLinkDuplexType; - -/*****************************************************************************/ - -typedef struct { - /* We don't want to include <linux/ethtool.h> in header files, - * thus create a ABI compatible version of struct ethtool_drvinfo.*/ - guint32 _private_cmd; - char driver[32]; - char version[32]; - char fw_version[32]; - char _private_bus_info[32]; - char _private_erom_version[32]; - char _private_reserved2[12]; - guint32 _private_n_priv_flags; - guint32 _private_n_stats; - guint32 _private_testinfo_len; - guint32 _private_eedump_len; - guint32 _private_regdump_len; -} NMPUtilsEthtoolDriverInfo; - -typedef struct { - NMEthtoolID ethtool_id; - - guint8 n_kernel_names; - - /* one NMEthtoolID refers to one or more kernel_names. The reason for supporting this complexity - * (where one NMSettingEthtool option refers to multiple kernel features) is to follow what - * ethtool does, where "tx" is an alias for multiple features. */ - const char *const *kernel_names; -} NMEthtoolFeatureInfo; - -typedef struct { - const NMEthtoolFeatureInfo *info; - - guint idx_ss_features; - - /* one NMEthtoolFeatureInfo references one or more kernel_names. This is the index - * of the matching info->kernel_names */ - guint8 idx_kernel_name; - - bool available : 1; - bool requested : 1; - bool active : 1; - bool never_changed : 1; -} NMEthtoolFeatureState; - -typedef struct { - guint n_states; - - guint n_ss_features; - - /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */ - const NMEthtoolFeatureState *const *states_indexed[_NM_ETHTOOL_ID_FEATURE_NUM]; - - /* the same content, here as a list of n_states entries. */ - const NMEthtoolFeatureState states_list[]; -} NMEthtoolFeatureStates; - -/*****************************************************************************/ - -typedef struct { - guint32 - s[_NM_ETHTOOL_ID_COALESCE_NUM /* indexed by (NMEthtoolID - _NM_ETHTOOL_ID_COALESCE_FIRST) */ - ]; -} NMEthtoolCoalesceState; - -/*****************************************************************************/ - -typedef struct { - guint32 rx_pending; - guint32 rx_mini_pending; - guint32 rx_jumbo_pending; - guint32 tx_pending; -} NMEthtoolRingState; - -/*****************************************************************************/ - -typedef struct _NMPNetns NMPNetns; - -#endif /* __NMP_FWD_H__ */ diff --git a/shared/nm-platform/nmp-netns.c b/shared/nm-platform/nmp-netns.c deleted file mode 100644 index f97339a756..0000000000 --- a/shared/nm-platform/nmp-netns.c +++ /dev/null @@ -1,767 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#include "nm-glib-aux/nm-default-glib-i18n-lib.h" - -#include "nmp-netns.h" - -#include <fcntl.h> -#include <sys/mount.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <pthread.h> - -#include "nm-log-core/nm-logging.h" - -/*****************************************************************************/ - -/* NOTE: NMPNetns and all code used here must be thread-safe! */ - -/* we may not call logging functions from the main-thread alone. Hence, we - * require locking from nm-logging. Indicate that by setting NM_THREAD_SAFE_ON_MAIN_THREAD - * to zero. */ -#undef NM_THREAD_SAFE_ON_MAIN_THREAD -#define NM_THREAD_SAFE_ON_MAIN_THREAD 0 - -/*****************************************************************************/ - -#define PROC_SELF_NS_MNT "/proc/self/ns/mnt" -#define PROC_SELF_NS_NET "/proc/self/ns/net" - -#define _CLONE_NS_ALL ((int) (CLONE_NEWNS | CLONE_NEWNET)) -#define _CLONE_NS_ALL_V CLONE_NEWNS, CLONE_NEWNET - -static NM_UTILS_FLAGS2STR_DEFINE(_clone_ns_to_str, - int, - NM_UTILS_FLAGS2STR(CLONE_NEWNS, "mnt"), - NM_UTILS_FLAGS2STR(CLONE_NEWNET, "net"), ); - -static const char * -__ns_types_to_str(int ns_types, int ns_types_already_set, char *buf, gsize len) -{ - const char *b = buf; - char bb[200]; - - nm_utils_strbuf_append_c(&buf, &len, '['); - if (ns_types & ~ns_types_already_set) { - nm_utils_strbuf_append_str( - &buf, - &len, - _clone_ns_to_str(ns_types & ~ns_types_already_set, bb, sizeof(bb))); - } - if (ns_types & ns_types_already_set) { - if (ns_types & ~ns_types_already_set) - nm_utils_strbuf_append_c(&buf, &len, '/'); - nm_utils_strbuf_append_str( - &buf, - &len, - _clone_ns_to_str(ns_types & ns_types_already_set, bb, sizeof(bb))); - } - nm_utils_strbuf_append_c(&buf, &len, ']'); - return b; -} -#define _ns_types_to_str(ns_types, ns_types_already_set, buf) \ - __ns_types_to_str(ns_types, ns_types_already_set, buf, sizeof(buf)) - -/*****************************************************************************/ - -#define _NMLOG_DOMAIN LOGD_PLATFORM -#define _NMLOG_PREFIX_NAME "netns" -#define _NMLOG(level, netns, ...) \ - G_STMT_START \ - { \ - NMLogLevel _level = (level); \ - \ - if (nm_logging_enabled(_level, _NMLOG_DOMAIN)) { \ - NMPNetns *_netns = (netns); \ - char _sbuf[20]; \ - \ - _nm_log(_level, \ - _NMLOG_DOMAIN, \ - 0, \ - NULL, \ - NULL, \ - "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - _NMLOG_PREFIX_NAME, \ - (_netns ? nm_sprintf_buf(_sbuf, "[%p]", _netns) \ - : "") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } \ - G_STMT_END - -/*****************************************************************************/ - -NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_FD_NET, PROP_FD_MNT, ); - -typedef struct { - int fd_net; - int fd_mnt; -} NMPNetnsPrivate; - -struct _NMPNetns { - GObject parent; - NMPNetnsPrivate _priv; -}; - -struct _NMPNetnsClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE(NMPNetns, nmp_netns, G_TYPE_OBJECT); - -#define NMP_NETNS_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMPNetns, NMP_IS_NETNS) - -/*****************************************************************************/ - -typedef struct { - NMPNetns *netns; - int count; - int ns_types; -} NetnsInfo; - -static void _stack_push(GArray *netns_stack, NMPNetns *netns, int ns_types); -static NMPNetns *_netns_new(GError **error); - -/*****************************************************************************/ - -static NMPNetns * -_netns_get(NetnsInfo *info) -{ - nm_assert(!info || NMP_IS_NETNS(info->netns)); - return info ? info->netns : NULL; -} - -/*****************************************************************************/ - -static _nm_thread_local GArray *_netns_stack = NULL; - -static void -_netns_stack_clear_cb(gpointer data) -{ - NetnsInfo *info = data; - - nm_assert(NMP_IS_NETNS(info->netns)); - g_object_unref(info->netns); -} - -static GArray * -_netns_stack_get_impl(void) -{ - gs_unref_object NMPNetns *netns = NULL; - gs_free_error GError *error = NULL; - pthread_key_t key; - GArray * s; - - s = g_array_new(FALSE, FALSE, sizeof(NetnsInfo)); - g_array_set_clear_func(s, _netns_stack_clear_cb); - _netns_stack = s; - - /* register a destructor function to cleanup the array. If we fail - * to do so, we will leak NMPNetns instances (and their file descriptor) when the - * thread exits. */ - if (pthread_key_create(&key, (void (*)(void *)) g_array_unref) != 0) - _LOGE(NULL, "failure to initialize thread-local storage"); - else if (pthread_setspecific(key, s) != 0) - _LOGE(NULL, "failure to set thread-local storage"); - - /* at the bottom of the stack we must try to create a netns instance - * that we never pop. It's the base to which we need to return. */ - netns = _netns_new(&error); - - if (!netns) { - _LOGD(NULL, "failed to create initial netns: %s", error->message); - return s; - } - - /* we leak this instance inside the stack. */ - _stack_push(s, netns, _CLONE_NS_ALL); - - return s; -} - -#define _netns_stack_get() \ - ({ \ - GArray *_s = _netns_stack; \ - \ - if (G_UNLIKELY(!_s)) \ - _s = _netns_stack_get_impl(); \ - _s; \ - }) - -/*****************************************************************************/ - -static NMPNetns * -_stack_current_netns(GArray *netns_stack, int ns_types) -{ - guint j; - - nm_assert(netns_stack && netns_stack->len > 0); - - /* we search the stack top-down to find the netns that has - * all @ns_types set. */ - for (j = netns_stack->len; ns_types && j >= 1;) { - NetnsInfo *info; - - info = &g_array_index(netns_stack, NetnsInfo, --j); - - if (NM_FLAGS_ALL(info->ns_types, ns_types)) - return info->netns; - } - - g_return_val_if_reached(NULL); -} - -static int -_stack_current_ns_types(GArray *netns_stack, NMPNetns *netns, int ns_types) -{ - const int ns_types_check[] = {_CLONE_NS_ALL_V}; - guint i, j; - int res = 0; - - nm_assert(netns); - nm_assert(netns_stack && netns_stack->len > 0); - - /* we search the stack top-down to check which of @ns_types - * are already set to @netns. */ - for (j = netns_stack->len; ns_types && j >= 1;) { - NetnsInfo *info; - - info = &g_array_index(netns_stack, NetnsInfo, --j); - if (info->netns != netns) { - ns_types = NM_FLAGS_UNSET(ns_types, info->ns_types); - continue; - } - - for (i = 0; i < G_N_ELEMENTS(ns_types_check); i++) { - if (NM_FLAGS_ANY(ns_types, ns_types_check[i]) - && NM_FLAGS_ANY(info->ns_types, ns_types_check[i])) { - res = NM_FLAGS_SET(res, ns_types_check[i]); - ns_types = NM_FLAGS_UNSET(ns_types, ns_types_check[i]); - } - } - } - - return res; -} - -static NetnsInfo * -_stack_peek(GArray *netns_stack) -{ - if (netns_stack->len > 0) - return &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); - return NULL; -} - -static NetnsInfo * -_stack_bottom(GArray *netns_stack) -{ - if (netns_stack->len > 0) - return &g_array_index(netns_stack, NetnsInfo, 0); - return NULL; -} - -static void -_stack_push(GArray *netns_stack, NMPNetns *netns, int ns_types) -{ - NetnsInfo *info; - - nm_assert(netns_stack); - nm_assert(NMP_IS_NETNS(netns)); - nm_assert(NM_FLAGS_ANY(ns_types, _CLONE_NS_ALL)); - nm_assert(!NM_FLAGS_ANY(ns_types, ~_CLONE_NS_ALL)); - - g_array_set_size(netns_stack, netns_stack->len + 1); - - info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); - *info = (NetnsInfo){ - .netns = g_object_ref(netns), - .ns_types = ns_types, - .count = 1, - }; -} - -static void -_stack_pop(GArray *netns_stack) -{ - NetnsInfo *info; - - nm_assert(netns_stack); - nm_assert(netns_stack->len > 1); - - info = &g_array_index(netns_stack, NetnsInfo, (netns_stack->len - 1)); - - nm_assert(NMP_IS_NETNS(info->netns)); - nm_assert(info->count == 1); - - g_array_set_size(netns_stack, netns_stack->len - 1); -} - -static guint -_stack_size(GArray *netns_stack) -{ - nm_assert(netns_stack); - - return netns_stack->len; -} - -/*****************************************************************************/ - -static NMPNetns * -_netns_new(GError **error) -{ - NMPNetns *self; - int fd_net, fd_mnt; - int errsv; - - fd_net = open(PROC_SELF_NS_NET, O_RDONLY | O_CLOEXEC); - if (fd_net == -1) { - errsv = errno; - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "Failed opening netns: %s", - nm_strerror_native(errsv)); - errno = errsv; - return NULL; - } - - fd_mnt = open(PROC_SELF_NS_MNT, O_RDONLY | O_CLOEXEC); - if (fd_mnt == -1) { - errsv = errno; - g_set_error(error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "Failed opening mntns: %s", - nm_strerror_native(errsv)); - nm_close(fd_net); - errno = errsv; - return NULL; - } - - self = g_object_new(NMP_TYPE_NETNS, NMP_NETNS_FD_NET, fd_net, NMP_NETNS_FD_MNT, fd_mnt, NULL); - - _LOGD(self, "new netns (net:%d, mnt:%d)", fd_net, fd_mnt); - - return self; -} - -static int -_setns(NMPNetns *self, int type) -{ - char buf[100]; - int fd; - NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); - - nm_assert(NM_IN_SET(type, _CLONE_NS_ALL_V)); - - fd = (type == CLONE_NEWNET) ? priv->fd_net : priv->fd_mnt; - - _LOGt(self, "set netns(%s, %d)", _ns_types_to_str(type, 0, buf), fd); - - return setns(fd, type); -} - -static gboolean -_netns_switch_push(GArray *netns_stack, NMPNetns *self, int ns_types) -{ - int errsv; - - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) - && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET) - && _setns(self, CLONE_NEWNET) != 0) { - errsv = errno; - _LOGE(self, "failed to switch netns: %s", nm_strerror_native(errsv)); - return FALSE; - } - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNS) - && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNS) - && _setns(self, CLONE_NEWNS) != 0) { - errsv = errno; - _LOGE(self, "failed to switch mntns: %s", nm_strerror_native(errsv)); - - /* try to fix the mess by returning to the previous netns. */ - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) - && !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET)) { - self = _stack_current_netns(netns_stack, CLONE_NEWNET); - if (self && _setns(self, CLONE_NEWNET) != 0) { - errsv = errno; - _LOGE(self, "failed to restore netns: %s", nm_strerror_native(errsv)); - } - } - return FALSE; - } - - return TRUE; -} - -static gboolean -_netns_switch_pop(GArray *netns_stack, NMPNetns *self, int ns_types) -{ - int errsv; - NMPNetns *current; - int success = TRUE; - - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNET) - && (!self || !_stack_current_ns_types(netns_stack, self, CLONE_NEWNET))) { - current = _stack_current_netns(netns_stack, CLONE_NEWNET); - if (!current) { - g_warn_if_reached(); - success = FALSE; - } else if (_setns(current, CLONE_NEWNET) != 0) { - errsv = errno; - _LOGE(self, "failed to switch netns: %s", nm_strerror_native(errsv)); - success = FALSE; - } - } - if (NM_FLAGS_HAS(ns_types, CLONE_NEWNS) - && (!self || !_stack_current_ns_types(netns_stack, self, CLONE_NEWNS))) { - current = _stack_current_netns(netns_stack, CLONE_NEWNS); - if (!current) { - g_warn_if_reached(); - success = FALSE; - } else if (_setns(current, CLONE_NEWNS) != 0) { - errsv = errno; - _LOGE(self, "failed to switch mntns: %s", nm_strerror_native(errsv)); - success = FALSE; - } - } - - return success; -} - -/*****************************************************************************/ - -int -nmp_netns_get_fd_net(NMPNetns *self) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), 0); - - return NMP_NETNS_GET_PRIVATE(self)->fd_net; -} - -int -nmp_netns_get_fd_mnt(NMPNetns *self) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), 0); - - return NMP_NETNS_GET_PRIVATE(self)->fd_mnt; -} - -/*****************************************************************************/ - -static gboolean -_nmp_netns_push_type(NMPNetns *self, int ns_types) -{ - GArray * netns_stack = _netns_stack_get(); - NetnsInfo *info; - char sbuf[100]; - - info = _stack_peek(netns_stack); - g_return_val_if_fail(info, FALSE); - - if (info->netns == self && info->ns_types == ns_types) { - info->count++; - _LOGt(self, - "push#%u* %s (increase count to %d)", - _stack_size(netns_stack) - 1, - _ns_types_to_str(ns_types, ns_types, sbuf), - info->count); - return TRUE; - } - - _LOGD(self, - "push#%u %s", - _stack_size(netns_stack), - _ns_types_to_str(ns_types, _stack_current_ns_types(netns_stack, self, ns_types), sbuf)); - - if (!_netns_switch_push(netns_stack, self, ns_types)) - return FALSE; - - _stack_push(netns_stack, self, ns_types); - return TRUE; -} - -gboolean -nmp_netns_push(NMPNetns *self) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - - return _nmp_netns_push_type(self, _CLONE_NS_ALL); -} - -gboolean -nmp_netns_push_type(NMPNetns *self, int ns_types) -{ - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - g_return_val_if_fail(!NM_FLAGS_ANY(ns_types, ~_CLONE_NS_ALL), FALSE); - - return _nmp_netns_push_type(self, ns_types == 0 ? _CLONE_NS_ALL : ns_types); -} - -NMPNetns * -nmp_netns_new(void) -{ - GArray * netns_stack = _netns_stack_get(); - NMPNetns * self; - int errsv; - GError * error = NULL; - unsigned long mountflags = 0; - - if (!_stack_peek(netns_stack)) { - /* there are no netns instances. We cannot create a new one - * (because after unshare we couldn't return to the original one). */ - errno = ENOTSUP; - return NULL; - } - - if (unshare(_CLONE_NS_ALL) != 0) { - errsv = errno; - _LOGE(NULL, "failed to create new net and mnt namespace: %s", nm_strerror_native(errsv)); - return NULL; - } - - if (mount("", "/", "none", MS_SLAVE | MS_REC, NULL) != 0) { - errsv = errno; - _LOGE(NULL, "failed mount --make-rslave: %s", nm_strerror_native(errsv)); - goto err_out; - } - - if (umount2("/sys", MNT_DETACH) != 0) { - errsv = errno; - _LOGE(NULL, "failed umount /sys: %s", nm_strerror_native(errsv)); - goto err_out; - } - - if (access("/sys", W_OK) == -1) - mountflags = MS_RDONLY; - - if (mount("sysfs", "/sys", "sysfs", mountflags, NULL) != 0) { - errsv = errno; - _LOGE(NULL, "failed mount /sys: %s", nm_strerror_native(errsv)); - goto err_out; - } - - self = _netns_new(&error); - if (!self) { - errsv = errno; - _LOGE(NULL, "failed to create netns after unshare: %s", error->message); - g_clear_error(&error); - goto err_out; - } - - _stack_push(netns_stack, self, _CLONE_NS_ALL); - - return self; -err_out: - _netns_switch_pop(netns_stack, NULL, _CLONE_NS_ALL); - errno = errsv; - return NULL; -} - -gboolean -nmp_netns_pop(NMPNetns *self) -{ - GArray * netns_stack = _netns_stack_get(); - NetnsInfo *info; - int ns_types; - - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - - info = _stack_peek(netns_stack); - - g_return_val_if_fail(info, FALSE); - g_return_val_if_fail(info->netns == self, FALSE); - - if (info->count > 1) { - info->count--; - _LOGt(self, "pop#%u* (decrease count to %d)", _stack_size(netns_stack) - 1, info->count); - return TRUE; - } - g_return_val_if_fail(info->count == 1, FALSE); - - /* cannot pop the original netns. */ - g_return_val_if_fail(_stack_size(netns_stack) > 1, FALSE); - - _LOGD(self, "pop#%u", _stack_size(netns_stack) - 1); - - ns_types = info->ns_types; - - _stack_pop(netns_stack); - - return _netns_switch_pop(netns_stack, self, ns_types); -} - -NMPNetns * -nmp_netns_get_current(void) -{ - return _netns_get(_stack_peek(_netns_stack_get())); -} - -NMPNetns * -nmp_netns_get_initial(void) -{ - return _netns_get(_stack_bottom(_netns_stack_get())); -} - -gboolean -nmp_netns_is_initial(void) -{ - GArray *netns_stack = _netns_stack_get(); - - return (_netns_get(_stack_peek(netns_stack)) == _netns_get(_stack_bottom(netns_stack))); -} - -/*****************************************************************************/ - -gboolean -nmp_netns_bind_to_path(NMPNetns *self, const char *filename, int *out_fd) -{ - gs_free char * dirname = NULL; - int errsv; - int fd; - nm_auto_pop_netns NMPNetns *netns_pop = NULL; - - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - g_return_val_if_fail(filename && filename[0] == '/', FALSE); - - if (!nmp_netns_push_type(self, CLONE_NEWNET)) - return FALSE; - netns_pop = self; - - dirname = g_path_get_dirname(filename); - if (mkdir(dirname, 0) != 0) { - errsv = errno; - if (errsv != EEXIST) { - _LOGE(self, - "bind: failed to create directory %s: %s", - dirname, - nm_strerror_native(errsv)); - return FALSE; - } - } - - if ((fd = creat(filename, S_IRUSR | S_IRGRP | S_IROTH)) == -1) { - errsv = errno; - _LOGE(self, "bind: failed to create %s: %s", filename, nm_strerror_native(errsv)); - return FALSE; - } - nm_close(fd); - - if (mount(PROC_SELF_NS_NET, filename, "none", MS_BIND, NULL) != 0) { - errsv = errno; - _LOGE(self, - "bind: failed to mount %s to %s: %s", - PROC_SELF_NS_NET, - filename, - nm_strerror_native(errsv)); - unlink(filename); - return FALSE; - } - - if (out_fd) { - if ((fd = open(filename, O_RDONLY | O_CLOEXEC)) == -1) { - errsv = errno; - _LOGE(self, "bind: failed to open %s: %s", filename, nm_strerror_native(errsv)); - umount2(filename, MNT_DETACH); - unlink(filename); - return FALSE; - } - *out_fd = fd; - } - - return TRUE; -} - -gboolean -nmp_netns_bind_to_path_destroy(NMPNetns *self, const char *filename) -{ - int errsv; - - g_return_val_if_fail(NMP_IS_NETNS(self), FALSE); - g_return_val_if_fail(filename && filename[0] == '/', FALSE); - - if (umount2(filename, MNT_DETACH) != 0) { - errsv = errno; - _LOGE(self, "bind: failed to unmount2 %s: %s", filename, nm_strerror_native(errsv)); - return FALSE; - } - if (unlink(filename) != 0) { - errsv = errno; - _LOGE(self, "bind: failed to unlink %s: %s", filename, nm_strerror_native(errsv)); - return FALSE; - } - return TRUE; -} - -/*****************************************************************************/ - -static void -set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - NMPNetns * self = NMP_NETNS(object); - NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); - - switch (prop_id) { - case PROP_FD_NET: - /* construct-only */ - priv->fd_net = g_value_get_int(value); - g_return_if_fail(priv->fd_net > 0); - break; - case PROP_FD_MNT: - /* construct-only */ - priv->fd_mnt = g_value_get_int(value); - g_return_if_fail(priv->fd_mnt > 0); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void -nmp_netns_init(NMPNetns *self) -{} - -static void -dispose(GObject *object) -{ - NMPNetns * self = NMP_NETNS(object); - NMPNetnsPrivate *priv = NMP_NETNS_GET_PRIVATE(self); - - nm_close(priv->fd_net); - priv->fd_net = -1; - - nm_close(priv->fd_mnt); - priv->fd_mnt = -1; - - G_OBJECT_CLASS(nmp_netns_parent_class)->dispose(object); -} - -static void -nmp_netns_class_init(NMPNetnsClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - - object_class->set_property = set_property; - object_class->dispose = dispose; - - obj_properties[PROP_FD_NET] = - g_param_spec_int(NMP_NETNS_FD_NET, - "", - "", - 0, - G_MAXINT, - 0, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_FD_MNT] = - g_param_spec_int(NMP_NETNS_FD_MNT, - "", - "", - 0, - G_MAXINT, - 0, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); -} diff --git a/shared/nm-platform/nmp-netns.h b/shared/nm-platform/nmp-netns.h deleted file mode 100644 index b18bd03e76..0000000000 --- a/shared/nm-platform/nmp-netns.h +++ /dev/null @@ -1,56 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2016 Red Hat, Inc. - */ - -#ifndef __NMP_NETNS_UTILS_H__ -#define __NMP_NETNS_UTILS_H__ - -#include "nmp-base.h" - -/*****************************************************************************/ - -#define NMP_TYPE_NETNS (nmp_netns_get_type()) -#define NMP_NETNS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMP_TYPE_NETNS, NMPNetns)) -#define NMP_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), NMP_TYPE_NETNS, NMPNetnsClass)) -#define NMP_IS_NETNS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMP_TYPE_NETNS)) -#define NMP_IS_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMP_TYPE_NETNS)) -#define NMP_NETNS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), NMP_TYPE_NETNS, NMPNetnsClass)) - -#define NMP_NETNS_FD_NET "fd-net" -#define NMP_NETNS_FD_MNT "fd-mnt" - -typedef struct _NMPNetns NMPNetns; -typedef struct _NMPNetnsClass NMPNetnsClass; - -GType nmp_netns_get_type(void); - -NMPNetns *nmp_netns_new(void); - -gboolean nmp_netns_push(NMPNetns *self); -gboolean nmp_netns_push_type(NMPNetns *self, int ns_types); -gboolean nmp_netns_pop(NMPNetns *self); - -NMPNetns *nmp_netns_get_current(void); -NMPNetns *nmp_netns_get_initial(void); -gboolean nmp_netns_is_initial(void); - -int nmp_netns_get_fd_net(NMPNetns *self); -int nmp_netns_get_fd_mnt(NMPNetns *self); - -static inline void -_nm_auto_pop_netns(NMPNetns **p) -{ - if (*p) { - int errsv = errno; - - nmp_netns_pop(*p); - errno = errsv; - } -} -#define nm_auto_pop_netns nm_auto(_nm_auto_pop_netns) - -gboolean nmp_netns_bind_to_path(NMPNetns *self, const char *filename, int *out_fd); -gboolean nmp_netns_bind_to_path_destroy(NMPNetns *self, const char *filename); - -#endif /* __NMP_NETNS_UTILS_H__ */ diff --git a/shared/nm-platform/tests/meson.build b/shared/nm-platform/tests/meson.build deleted file mode 100644 index dd9c3f58ee..0000000000 --- a/shared/nm-platform/tests/meson.build +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later - -exe = executable( - 'test-nm-platform', - 'test-nm-platform.c', - dependencies: [ - libnm_log_core_dep, - libnm_platform_dep, - ], -) - -test( - 'shared/nm-glib-aux/test-nm-platform', - test_script, - args: test_args + [exe.full_path()], - timeout: default_test_timeout, -) diff --git a/shared/nm-platform/tests/test-nm-platform.c b/shared/nm-platform/tests/test-nm-platform.c deleted file mode 100644 index a3e9ff13ba..0000000000 --- a/shared/nm-platform/tests/test-nm-platform.c +++ /dev/null @@ -1,116 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-glib-aux/nm-default-glib-i18n-prog.h" - -#include "nm-log-core/nm-logging.h" -#include "nm-platform/nm-netlink.h" -#include "nm-platform/nmp-netns.h" - -#include "nm-utils/nm-test-utils.h" - -/*****************************************************************************/ - -void -_nm_logging_clear_platform_logging_cache(void) -{ - /* this symbols is required by nm-log-core library. */ -} - -/*****************************************************************************/ - -static void -test_use_symbols(void) -{ - static void (*const SYMBOLS[])(void) = { - (void (*)(void)) nl_nlmsghdr_to_str, - (void (*)(void)) nlmsg_hdr, - (void (*)(void)) nlmsg_reserve, - (void (*)(void)) nla_reserve, - (void (*)(void)) nlmsg_alloc_size, - (void (*)(void)) nlmsg_alloc, - (void (*)(void)) nlmsg_alloc_convert, - (void (*)(void)) nlmsg_alloc_simple, - (void (*)(void)) nlmsg_free, - (void (*)(void)) nlmsg_append, - (void (*)(void)) nlmsg_parse, - (void (*)(void)) nlmsg_put, - (void (*)(void)) nla_strlcpy, - (void (*)(void)) nla_memcpy, - (void (*)(void)) nla_put, - (void (*)(void)) nla_find, - (void (*)(void)) nla_nest_cancel, - (void (*)(void)) nla_nest_start, - (void (*)(void)) nla_nest_end, - (void (*)(void)) nla_parse, - (void (*)(void)) nlmsg_get_proto, - (void (*)(void)) nlmsg_set_proto, - (void (*)(void)) nlmsg_set_src, - (void (*)(void)) nlmsg_get_creds, - (void (*)(void)) nlmsg_set_creds, - (void (*)(void)) genlmsg_put, - (void (*)(void)) genlmsg_data, - (void (*)(void)) genlmsg_user_hdr, - (void (*)(void)) genlmsg_hdr, - (void (*)(void)) genlmsg_user_data, - (void (*)(void)) genlmsg_attrdata, - (void (*)(void)) genlmsg_len, - (void (*)(void)) genlmsg_attrlen, - (void (*)(void)) genlmsg_valid_hdr, - (void (*)(void)) genlmsg_parse, - (void (*)(void)) genl_ctrl_resolve, - (void (*)(void)) nl_socket_alloc, - (void (*)(void)) nl_socket_free, - (void (*)(void)) nl_socket_get_fd, - (void (*)(void)) nl_socket_get_local_port, - (void (*)(void)) nl_socket_get_msg_buf_size, - (void (*)(void)) nl_socket_set_passcred, - (void (*)(void)) nl_socket_set_msg_buf_size, - (void (*)(void)) nlmsg_get_dst, - (void (*)(void)) nl_socket_set_nonblocking, - (void (*)(void)) nl_socket_set_buffer_size, - (void (*)(void)) nl_socket_add_memberships, - (void (*)(void)) nl_socket_set_ext_ack, - (void (*)(void)) nl_socket_disable_msg_peek, - (void (*)(void)) nl_connect, - (void (*)(void)) nl_wait_for_ack, - (void (*)(void)) nl_recvmsgs, - (void (*)(void)) nl_sendmsg, - (void (*)(void)) nl_send_iovec, - (void (*)(void)) nl_complete_msg, - (void (*)(void)) nl_send, - (void (*)(void)) nl_send_auto, - (void (*)(void)) nl_recv, - - (void (*)(void)) nmp_netns_bind_to_path, - (void (*)(void)) nmp_netns_bind_to_path_destroy, - (void (*)(void)) nmp_netns_get_current, - (void (*)(void)) nmp_netns_get_fd_mnt, - (void (*)(void)) nmp_netns_get_fd_net, - (void (*)(void)) nmp_netns_get_initial, - (void (*)(void)) nmp_netns_is_initial, - (void (*)(void)) nmp_netns_new, - (void (*)(void)) nmp_netns_pop, - (void (*)(void)) nmp_netns_push, - (void (*)(void)) nmp_netns_push_type, - - NULL, - }; - - /* The only (not very exciting) purpose of this test is to see that - * we can use various symbols and don't get a linker error. */ - assert(G_N_ELEMENTS(SYMBOLS) == NM_PTRARRAY_LEN(SYMBOLS) + 1); -} - -/*****************************************************************************/ - -NMTST_DEFINE(); - -int -main(int argc, char **argv) -{ - nmtst_init(&argc, &argv, TRUE); - - g_test_add_func("/nm-platform/test_use_symbols", test_use_symbols); - - return g_test_run(); -} |