1 /*
2 * tsinfo.c - netlink implementation of timestamping commands
3 *
4 * Implementation of "ethtool -T <dev>"
5 */
6
7 #include <errno.h>
8 #include <inttypes.h>
9 #include <string.h>
10 #include <stdio.h>
11
12 #include "../internal.h"
13 #include "../common.h"
14 #include "netlink.h"
15 #include "bitset.h"
16
17 /* TSINFO_GET */
18
tsinfo_show_stats(const struct nlattr * nest)19 static int tsinfo_show_stats(const struct nlattr *nest)
20 {
21 const struct nlattr *tb[ETHTOOL_A_TS_STAT_MAX + 1] = {};
22 DECLARE_ATTR_TB_INFO(tb);
23 static const struct {
24 unsigned int attr;
25 char *name;
26 } stats[] = {
27 { ETHTOOL_A_TS_STAT_TX_PKTS, "tx_pkts" },
28 { ETHTOOL_A_TS_STAT_TX_LOST, "tx_lost" },
29 { ETHTOOL_A_TS_STAT_TX_ERR, "tx_err" },
30 };
31 bool header = false;
32 unsigned int i;
33 __u64 val;
34 int ret;
35
36 ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
37 if (ret < 0)
38 return ret;
39
40 open_json_object("statistics");
41 for (i = 0; i < ARRAY_SIZE(stats); i++) {
42 char fmt[64];
43
44 if (!tb[stats[i].attr])
45 continue;
46
47 if (!header && !is_json_context()) {
48 printf("Statistics:\n");
49 header = true;
50 }
51
52 if (!mnl_attr_validate(tb[stats[i].attr], MNL_TYPE_U32)) {
53 val = mnl_attr_get_u32(tb[stats[i].attr]);
54 } else if (!mnl_attr_validate(tb[stats[i].attr], MNL_TYPE_U64)) {
55 val = mnl_attr_get_u64(tb[stats[i].attr]);
56 } else {
57 fprintf(stderr, "malformed netlink message (statistic)\n");
58 goto err_close_stats;
59 }
60
61 snprintf(fmt, sizeof(fmt), " %s: %%" PRIu64 "\n", stats[i].name);
62 print_u64(PRINT_ANY, stats[i].name, fmt, val);
63 }
64 close_json_object();
65
66 return 0;
67
68 err_close_stats:
69 close_json_object();
70 return -1;
71 }
72
tsinfo_dump_cb(unsigned int idx,const char * name,bool val,void * data __maybe_unused)73 static void tsinfo_dump_cb(unsigned int idx, const char *name, bool val,
74 void *data __maybe_unused)
75 {
76 if (!val)
77 return;
78
79 if (name)
80 printf("\t%s\n", name);
81 else
82 printf("\tbit%u\n", idx);
83 }
84
tsinfo_dump_list(struct nl_context * nlctx,const struct nlattr * attr,const char * label,const char * if_empty,unsigned int stringset_id)85 static int tsinfo_dump_list(struct nl_context *nlctx, const struct nlattr *attr,
86 const char *label, const char *if_empty,
87 unsigned int stringset_id)
88 {
89 const struct stringset *strings = NULL;
90 int ret;
91
92 printf("%s:", label);
93 ret = 0;
94 if (!attr || bitset_is_empty(attr, false, &ret)) {
95 printf("%s\n", if_empty);
96 return ret;
97 }
98 putchar('\n');
99 if (ret < 0)
100 return ret;
101
102 if (bitset_is_compact(attr)) {
103 ret = netlink_init_ethnl2_socket(nlctx);
104 if (ret < 0)
105 return ret;
106 strings = global_stringset(stringset_id, nlctx->ethnl2_socket);
107 }
108 return walk_bitset(attr, strings, tsinfo_dump_cb, NULL);
109 }
110
tsinfo_reply_cb(const struct nlmsghdr * nlhdr,void * data)111 int tsinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data)
112 {
113 const struct nlattr *tb[ETHTOOL_A_TSINFO_MAX + 1] = {};
114 DECLARE_ATTR_TB_INFO(tb);
115 struct nl_context *nlctx = data;
116 bool silent;
117 int err_ret;
118 int ret;
119
120 silent = nlctx->is_dump;
121 err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
122 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
123 if (ret < 0)
124 return err_ret;
125 nlctx->devname = get_dev_name(tb[ETHTOOL_A_TSINFO_HEADER]);
126 if (!dev_ok(nlctx))
127 return err_ret;
128
129 if (silent)
130 putchar('\n');
131 printf("Time stamping parameters for %s:\n", nlctx->devname);
132
133 ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_TIMESTAMPING],
134 "Capabilities", "", ETH_SS_SOF_TIMESTAMPING);
135 if (ret < 0)
136 return err_ret;
137
138 printf("PTP Hardware Clock: ");
139 if (tb[ETHTOOL_A_TSINFO_PHC_INDEX])
140 printf("%d\n",
141 mnl_attr_get_u32(tb[ETHTOOL_A_TSINFO_PHC_INDEX]));
142 else
143 printf("none\n");
144
145 ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_TX_TYPES],
146 "Hardware Transmit Timestamp Modes", " none",
147 ETH_SS_TS_TX_TYPES);
148 if (ret < 0)
149 return err_ret;
150
151 ret = tsinfo_dump_list(nlctx, tb[ETHTOOL_A_TSINFO_RX_FILTERS],
152 "Hardware Receive Filter Modes", " none",
153 ETH_SS_TS_RX_FILTERS);
154 if (ret < 0)
155 return err_ret;
156
157 if (tb[ETHTOOL_A_TSINFO_STATS]) {
158 ret = tsinfo_show_stats(tb[ETHTOOL_A_TSINFO_STATS]);
159 if (ret < 0)
160 return err_ret;
161 }
162
163 return MNL_CB_OK;
164 }
165
nl_tsinfo(struct cmd_context * ctx)166 int nl_tsinfo(struct cmd_context *ctx)
167 {
168 struct nl_context *nlctx = ctx->nlctx;
169 struct nl_socket *nlsk = nlctx->ethnl_socket;
170 u32 flags;
171 int ret;
172
173 if (netlink_cmd_check(ctx, ETHTOOL_MSG_TSINFO_GET, true))
174 return -EOPNOTSUPP;
175 if (ctx->argc > 0) {
176 fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
177 *ctx->argp);
178 return 1;
179 }
180
181 flags = get_stats_flag(nlctx, ETHTOOL_MSG_TSINFO_GET, ETHTOOL_A_TSINFO_HEADER);
182 ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_TSINFO_GET,
183 ETHTOOL_A_TSINFO_HEADER, flags);
184 if (ret < 0)
185 return ret;
186 return nlsock_send_get_request(nlsk, tsinfo_reply_cb);
187 }
188