summaryrefslogtreecommitdiffstats
path: root/netlink/permaddr.c
diff options
context:
space:
mode:
Diffstat (limited to 'netlink/permaddr.c')
-rw-r--r--netlink/permaddr.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/netlink/permaddr.c b/netlink/permaddr.c
new file mode 100644
index 0000000..006eac6
--- /dev/null
+++ b/netlink/permaddr.c
@@ -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);
+}