1 /*
2 * tunnel.c - device tunnel information
3 *
4 * Implementation of "ethtool --show-tunnels <dev>"
5 */
6
7 #include <errno.h>
8 #include <string.h>
9 #include <stdio.h>
10 #include <arpa/inet.h>
11
12 #include "../common.h"
13 #include "../internal.h"
14
15 #include "bitset.h"
16 #include "netlink.h"
17 #include "strset.h"
18
19 /* TUNNEL_INFO_GET */
20
21 static int
tunnel_info_strings_load(struct nl_context * nlctx,const struct stringset ** strings)22 tunnel_info_strings_load(struct nl_context *nlctx,
23 const struct stringset **strings)
24 {
25 int ret;
26
27 if (*strings)
28 return 0;
29 ret = netlink_init_ethnl2_socket(nlctx);
30 if (ret < 0)
31 return ret;
32 *strings = global_stringset(ETH_SS_UDP_TUNNEL_TYPES,
33 nlctx->ethnl2_socket);
34 return 0;
35 }
36
37 static int
tunnel_info_dump_udp_entry(struct nl_context * nlctx,const struct nlattr * entry,const struct stringset ** strings)38 tunnel_info_dump_udp_entry(struct nl_context *nlctx, const struct nlattr *entry,
39 const struct stringset **strings)
40 {
41 const struct nlattr *entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_MAX + 1] = {};
42 DECLARE_ATTR_TB_INFO(entry_tb);
43 const struct nlattr *attr;
44 unsigned int port, type;
45 int ret;
46
47 if (tunnel_info_strings_load(nlctx, strings))
48 return 1;
49
50 ret = mnl_attr_parse_nested(entry, attr_cb, &entry_tb_info);
51 if (ret < 0) {
52 fprintf(stderr, "malformed netlink message (udp entry)\n");
53 return 1;
54 }
55
56 attr = entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_PORT];
57 if (!attr || mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
58 fprintf(stderr, "malformed netlink message (port)\n");
59 return 1;
60 }
61 port = ntohs(mnl_attr_get_u16(attr));
62
63 attr = entry_tb[ETHTOOL_A_TUNNEL_UDP_ENTRY_TYPE];
64 if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
65 fprintf(stderr, "malformed netlink message (tunnel type)\n");
66 return 1;
67 }
68 type = mnl_attr_get_u32(attr);
69
70 printf(" port %d, %s\n", port, get_string(*strings, type));
71
72 return 0;
73 }
74
tunnel_info_dump_cb(unsigned int idx,const char * name,bool val,void * data)75 static void tunnel_info_dump_cb(unsigned int idx, const char *name, bool val,
76 void *data)
77 {
78 bool *first = data;
79
80 if (!val)
81 return;
82
83 if (!*first)
84 putchar(',');
85 *first = false;
86
87 if (name)
88 printf(" %s", name);
89 else
90 printf(" bit%u", idx);
91 }
92
93 static int
tunnel_info_dump_list(struct nl_context * nlctx,const struct nlattr * attr,const struct stringset ** strings)94 tunnel_info_dump_list(struct nl_context *nlctx, const struct nlattr *attr,
95 const struct stringset **strings)
96 {
97 bool first = true;
98 int ret;
99
100 if (bitset_is_empty(attr, false, &ret) || ret) {
101 if (ret)
102 return 1;
103
104 printf(" Types: none (static entries)\n");
105 return 0;
106 }
107
108 if (bitset_is_compact(attr) &&
109 tunnel_info_strings_load(nlctx, strings))
110 return 1;
111
112 printf(" Types:");
113 ret = walk_bitset(attr, *strings, tunnel_info_dump_cb, &first);
114 putchar('\n');
115 return ret;
116 }
117
118 static int
tunnel_info_dump_udp_table(const struct nlattr * table,struct nl_context * nlctx,const struct stringset ** strings)119 tunnel_info_dump_udp_table(const struct nlattr *table, struct nl_context *nlctx,
120 const struct stringset **strings)
121 {
122 const struct nlattr *table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_MAX + 1] = {};
123 DECLARE_ATTR_TB_INFO(table_tb);
124 const struct nlattr *attr;
125 int i, ret;
126
127 ret = mnl_attr_parse_nested(table, attr_cb, &table_tb_info);
128 if (ret < 0) {
129 fprintf(stderr, "malformed netlink message (udp table)\n");
130 return 1;
131 }
132
133 attr = table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_SIZE];
134 if (!attr || mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
135 fprintf(stderr, "malformed netlink message (table size)\n");
136 return 1;
137 }
138 printf(" Size: %d\n", mnl_attr_get_u32(attr));
139
140 attr = table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_TYPES];
141 if (!attr || tunnel_info_dump_list(nlctx, attr, strings)) {
142 fprintf(stderr, "malformed netlink message (types)\n");
143 return 1;
144 }
145
146 if (!table_tb[ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY]) {
147 printf(" No entries\n");
148 return 0;
149 }
150
151 i = 0;
152 mnl_attr_for_each_nested(attr, table) {
153 if (mnl_attr_get_type(attr) == ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY)
154 i++;
155 }
156
157 printf(" Entries (%d):\n", i++);
158 mnl_attr_for_each_nested(attr, table) {
159 if (mnl_attr_get_type(attr) == ETHTOOL_A_TUNNEL_UDP_TABLE_ENTRY)
160 if (tunnel_info_dump_udp_entry(nlctx, attr, strings))
161 return 1;
162 }
163
164 return 0;
165 }
166
167 static int
tunnel_info_dump_udp(const struct nlattr * udp_ports,struct nl_context * nlctx)168 tunnel_info_dump_udp(const struct nlattr *udp_ports, struct nl_context *nlctx)
169 {
170 const struct stringset *strings = NULL;
171 const struct nlattr *table;
172 int i, err;
173
174 i = 0;
175 mnl_attr_for_each_nested(table, udp_ports) {
176 if (mnl_attr_get_type(table) != ETHTOOL_A_TUNNEL_UDP_TABLE)
177 continue;
178
179 printf(" UDP port table %d: \n", i++);
180 err = tunnel_info_dump_udp_table(table, nlctx, &strings);
181 if (err)
182 return err;
183 }
184
185 return 0;
186 }
187
tunnel_info_reply_cb(const struct nlmsghdr * nlhdr,void * data)188 static int tunnel_info_reply_cb(const struct nlmsghdr *nlhdr, void *data)
189 {
190 const struct nlattr *tb[ETHTOOL_A_TUNNEL_INFO_MAX + 1] = {};
191 DECLARE_ATTR_TB_INFO(tb);
192 const struct nlattr *udp_ports;
193 struct nl_context *nlctx = data;
194 bool silent;
195 int err_ret;
196 int ret;
197
198 silent = nlctx->is_dump;
199 err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
200 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
201 if (ret < 0)
202 return err_ret;
203 nlctx->devname = get_dev_name(tb[ETHTOOL_A_TUNNEL_INFO_HEADER]);
204 if (!dev_ok(nlctx))
205 return err_ret;
206
207 printf("Tunnel information for %s:\n", nlctx->devname);
208
209 udp_ports = tb[ETHTOOL_A_TUNNEL_INFO_UDP_PORTS];
210 if (udp_ports)
211 if (tunnel_info_dump_udp(udp_ports, nlctx))
212 return err_ret;
213
214 return MNL_CB_OK;
215 }
216
nl_gtunnels(struct cmd_context * ctx)217 int nl_gtunnels(struct cmd_context *ctx)
218 {
219 struct nl_context *nlctx = ctx->nlctx;
220 struct nl_socket *nlsk = nlctx->ethnl_socket;
221 int ret;
222
223 if (netlink_cmd_check(ctx, ETHTOOL_MSG_TUNNEL_INFO_GET, true))
224 return -EOPNOTSUPP;
225 if (ctx->argc > 0) {
226 fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
227 *ctx->argp);
228 return 1;
229 }
230
231 ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_TUNNEL_INFO_GET,
232 ETHTOOL_A_TUNNEL_INFO_HEADER, 0);
233 if (ret < 0)
234 return ret;
235 return nlsock_send_get_request(nlsk, tunnel_info_reply_cb);
236 }
237