xref: /aosp_15_r20/external/ethtool/netlink/tunnels.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
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