1 /*
2 * permaddr.c - netlink implementation of permanent address request
3 *
4 * Implementation of "ethtool -P <dev>"
5 */
6
7 #include <errno.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <linux/rtnetlink.h>
11 #include <linux/if_link.h>
12
13 #include "../internal.h"
14 #include "../common.h"
15 #include "netlink.h"
16
17 /* PERMADDR_GET */
18
permaddr_prep_request(struct nl_socket * nlsk)19 static int permaddr_prep_request(struct nl_socket *nlsk)
20 {
21 unsigned int nlm_flags = NLM_F_REQUEST | NLM_F_ACK;
22 struct nl_context *nlctx = nlsk->nlctx;
23 const char *devname = nlctx->ctx->devname;
24 struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
25 struct ifinfomsg *ifinfo;
26 struct nlmsghdr *nlhdr;
27 int ret;
28
29 if (devname && !strcmp(devname, WILDCARD_DEVNAME)) {
30 devname = NULL;
31 nlm_flags |= NLM_F_DUMP;
32 }
33 nlctx->is_dump = !devname;
34
35 ret = msgbuff_realloc(msgbuff, MNL_SOCKET_BUFFER_SIZE);
36 if (ret < 0)
37 return ret;
38 memset(msgbuff->buff, '\0', NLMSG_HDRLEN + sizeof(*ifinfo));
39
40 nlhdr = mnl_nlmsg_put_header(msgbuff->buff);
41 nlhdr->nlmsg_type = RTM_GETLINK;
42 nlhdr->nlmsg_flags = nlm_flags;
43 msgbuff->nlhdr = nlhdr;
44 ifinfo = mnl_nlmsg_put_extra_header(nlhdr, sizeof(*ifinfo));
45
46 if (devname) {
47 uint16_t type = IFLA_IFNAME;
48
49 if (strlen(devname) >= IFNAMSIZ)
50 type = IFLA_ALT_IFNAME;
51 if (ethnla_put_strz(msgbuff, type, devname))
52 return -EMSGSIZE;
53 }
54 if (ethnla_put_u32(msgbuff, IFLA_EXT_MASK, RTEXT_FILTER_SKIP_STATS))
55 return -EMSGSIZE;
56
57 return 0;
58 }
59
permaddr_reply_cb(const struct nlmsghdr * nlhdr,void * data)60 int permaddr_reply_cb(const struct nlmsghdr *nlhdr, void *data)
61 {
62 const struct nlattr *tb[__IFLA_MAX] = {};
63 DECLARE_ATTR_TB_INFO(tb);
64 struct nl_context *nlctx = data;
65 const uint8_t *permaddr;
66 unsigned int i;
67 int ret;
68
69 if (nlhdr->nlmsg_type != RTM_NEWLINK)
70 goto err;
71 ret = mnl_attr_parse(nlhdr, sizeof(struct ifinfomsg), attr_cb,
72 &tb_info);
73 if (ret < 0 || !tb[IFLA_IFNAME])
74 goto err;
75 nlctx->devname = mnl_attr_get_str(tb[IFLA_IFNAME]);
76 if (!dev_ok(nlctx))
77 goto err;
78
79 if (!tb[IFLA_PERM_ADDRESS]) {
80 if (!nlctx->is_dump)
81 printf("Permanent address: not set\n");
82 return MNL_CB_OK;
83 }
84
85 if (nlctx->is_dump)
86 printf("Permanent address of %s:", nlctx->devname);
87 else
88 printf("Permanent address:");
89 permaddr = mnl_attr_get_payload(tb[IFLA_PERM_ADDRESS]);
90 for (i = 0; i < mnl_attr_get_payload_len(tb[IFLA_PERM_ADDRESS]); i++)
91 printf("%c%02x", i ? ':' : ' ', permaddr[i]);
92 putchar('\n');
93 return MNL_CB_OK;
94
95 err:
96 if (nlctx->is_dump || nlctx->is_monitor)
97 return MNL_CB_OK;
98 nlctx->exit_code = 2;
99 return MNL_CB_ERROR;
100 }
101
nl_permaddr(struct cmd_context * ctx)102 int nl_permaddr(struct cmd_context *ctx)
103 {
104 struct nl_context *nlctx = ctx->nlctx;
105 int ret;
106
107 ret = netlink_init_rtnl_socket(nlctx);
108 if (ret < 0)
109 return ret;
110 ret = permaddr_prep_request(nlctx->rtnl_socket);
111 if (ret < 0)
112 return ret;
113 return nlsock_send_get_request(nlctx->rtnl_socket, permaddr_reply_cb);
114 }
115