@@ -30,6 +30,7 @@ ethtool_SOURCES += \
netlink/nlsock.h netlink/strset.c netlink/strset.h \
netlink/monitor.c netlink/bitset.c netlink/bitset.h \
netlink/settings.c netlink/parser.c netlink/parser.h \
+ netlink/permaddr.c \
uapi/linux/ethtool_netlink.h \
uapi/linux/netlink.h uapi/linux/genetlink.h \
uapi/linux/rtnetlink.h uapi/linux/if_link.h
@@ -5372,6 +5372,7 @@ static const struct option args[] = {
{
.opts = "-P|--show-permaddr",
.func = do_permaddr,
+ .nlfunc = nl_permaddr,
.help = "Show permanent hardware address"
},
{
@@ -17,6 +17,7 @@ void netlink_done(struct cmd_context *ctx);
int nl_gset(struct cmd_context *ctx);
int nl_sset(struct cmd_context *ctx);
+int nl_permaddr(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
void nl_monitor_usage(void);
@@ -38,6 +39,7 @@ static inline void nl_monitor_usage(void)
#define nl_gset NULL
#define nl_sset NULL
+#define nl_permaddr NULL
#endif /* ETHTOOL_ENABLE_NETLINK */
@@ -27,6 +27,7 @@ struct nl_context {
uint32_t ethnl_mongrp;
struct nl_socket *ethnl_socket;
struct nl_socket *ethnl2_socket;
+ struct nl_socket *rtnl_socket;
bool is_monitor;
uint32_t filter_cmds[CMDMASK_WORDS];
const char *filter_devname;
@@ -76,4 +77,11 @@ static inline int netlink_init_ethnl2_socket(struct nl_context *nlctx)
return nlsock_init(nlctx, &nlctx->ethnl2_socket, NETLINK_GENERIC);
}
+static inline int netlink_init_rtnl_socket(struct nl_context *nlctx)
+{
+ if (nlctx->rtnl_socket)
+ return 0;
+ return nlsock_init(nlctx, &nlctx->rtnl_socket, NETLINK_ROUTE);
+}
+
#endif /* ETHTOOL_NETLINK_INT_H__ */
new file mode 100644
@@ -0,0 +1,114 @@
+/*
+ * permaddr.c - netlink implementation of permanent address request
+ *
+ * Implementation of "ethtool -P <dev>"
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_link.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+
+/* PERMADDR_GET */
+
+static int permaddr_prep_request(struct nl_socket *nlsk)
+{
+ unsigned int nlm_flags = NLM_F_REQUEST | NLM_F_ACK;
+ struct nl_context *nlctx = nlsk->nlctx;
+ const char *devname = nlctx->ctx->devname;
+ struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
+ struct ifinfomsg *ifinfo;
+ struct nlmsghdr *nlhdr;
+ int ret;
+
+ if (devname && !strcmp(devname, WILDCARD_DEVNAME)) {
+ devname = NULL;
+ nlm_flags |= NLM_F_DUMP;
+ }
+ nlctx->is_dump = !devname;
+
+ ret = msgbuff_realloc(msgbuff, MNL_SOCKET_BUFFER_SIZE);
+ if (ret < 0)
+ return ret;
+ memset(msgbuff->buff, '\0', NLMSG_HDRLEN + sizeof(*ifinfo));
+
+ nlhdr = mnl_nlmsg_put_header(msgbuff->buff);
+ nlhdr->nlmsg_type = RTM_GETLINK;
+ nlhdr->nlmsg_flags = nlm_flags;
+ msgbuff->nlhdr = nlhdr;
+ ifinfo = mnl_nlmsg_put_extra_header(nlhdr, sizeof(*ifinfo));
+
+ if (devname) {
+ uint16_t type = IFLA_IFNAME;
+
+ if (strlen(devname) >= IFNAMSIZ)
+ type = IFLA_ALT_IFNAME;
+ if (ethnla_put_strz(msgbuff, type, devname))
+ return -EMSGSIZE;
+ }
+ if (ethnla_put_u32(msgbuff, IFLA_EXT_MASK, RTEXT_FILTER_SKIP_STATS))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+int permaddr_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[__IFLA_MAX] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ const uint8_t *permaddr;
+ unsigned int i;
+ int ret;
+
+ if (nlhdr->nlmsg_type != RTM_NEWLINK)
+ goto err;
+ ret = mnl_attr_parse(nlhdr, sizeof(struct ifinfomsg), attr_cb,
+ &tb_info);
+ if (ret < 0 || !tb[IFLA_IFNAME])
+ goto err;
+ nlctx->devname = mnl_attr_get_str(tb[IFLA_IFNAME]);
+ if (!dev_ok(nlctx))
+ goto err;
+
+ if (!tb[IFLA_PERM_ADDRESS]) {
+ if (!nlctx->is_dump)
+ printf("Permanent address: not set\n");
+ return MNL_CB_OK;
+ }
+
+ if (nlctx->is_dump)
+ printf("Permanent address of %s:", nlctx->devname);
+ else
+ printf("Permanent address:");
+ permaddr = mnl_attr_get_payload(tb[IFLA_PERM_ADDRESS]);
+ for (i = 0; i < mnl_attr_get_payload_len(tb[IFLA_PERM_ADDRESS]); i++)
+ printf("%c%02x", i ? ':' : ' ', permaddr[i]);
+ putchar('\n');
+ return MNL_CB_OK;
+
+err:
+ if (nlctx->is_dump || nlctx->is_monitor)
+ return MNL_CB_OK;
+ nlctx->exit_code = 2;
+ return MNL_CB_ERROR;
+}
+
+int nl_permaddr(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ int ret;
+
+ ret = netlink_init_rtnl_socket(nlctx);
+ if (ret < 0)
+ return ret;
+ ret = permaddr_prep_request(nlctx->rtnl_socket);
+ if (ret < 0)
+ return ret;
+ return nlsock_send_get_request(nlctx->rtnl_socket, permaddr_reply_cb);
+}
Implement "ethtool -P <dev>" subcommand. This retrieves and displays information traditionally provided by ETHTOOL_GPERMADDR ioctl request. Permanent hardware address is provided by rtnetlink RTM_GETLINK request so that basic support for rtnetlink (which is not based on genetlink) requests is also added. The IFLA_PERM_ADDRESS attribute in RTM_NEWLINK message is not set if the device driver did not set its permanent hardware address which is hard to distinguish from ethtool running on top of an older kernel not supporting the attribute. As IFLA_PERM_ADDRESS attribute was added to kernel shortly before the initial portion of ethtool netlink interface, we let ethtool initialize ethtool netlink even if it is not needed so that we can fall back to ioctl on older kernels and assume that absence of IFLA_PERM_ADDRESS means the permanent address is not set. This would be only wrong on some distribution kernel which would backport ethtool netlink interface but not IFLA_PERM_ADDRESS which is unlikely. Permanent address is immutable (or not mutable using standard interfaces) so that there is no related notification message. Signed-off-by: Michal Kubecek <mkubecek@suse.cz> --- Makefile.am | 1 + ethtool.c | 1 + netlink/extapi.h | 2 + netlink/netlink.h | 8 ++++ netlink/permaddr.c | 114 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+) create mode 100644 netlink/permaddr.c