xref: /aosp_15_r20/external/ethtool/netlink/strset.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
1 /*
2  * strset.c - string set handling
3  *
4  * Implementation of local cache of ethtool string sets.
5  */
6 
7 #include <errno.h>
8 #include <string.h>
9 
10 #include "../internal.h"
11 #include "netlink.h"
12 #include "nlsock.h"
13 #include "msgbuff.h"
14 
15 struct stringset {
16 	const char		**strings;
17 	void			*raw_data;
18 	unsigned int		count;
19 	bool			loaded;
20 };
21 
22 struct perdev_strings {
23 	int			ifindex;
24 	char			devname[ALTIFNAMSIZ];
25 	struct stringset	strings[ETH_SS_COUNT];
26 	struct perdev_strings	*next;
27 };
28 
29 /* universal string sets */
30 static struct stringset global_strings[ETH_SS_COUNT];
31 /* linked list of string sets related to network devices */
32 static struct perdev_strings *device_strings;
33 
drop_stringset(struct stringset * set)34 static void drop_stringset(struct stringset *set)
35 {
36 	if (!set)
37 		return;
38 
39 	free(set->strings);
40 	free(set->raw_data);
41 	memset(set, 0, sizeof(*set));
42 }
43 
import_stringset(struct stringset * dest,const struct nlattr * nest)44 static int import_stringset(struct stringset *dest, const struct nlattr *nest)
45 {
46 	const struct nlattr *tb_stringset[ETHTOOL_A_STRINGSET_MAX + 1] = {};
47 	DECLARE_ATTR_TB_INFO(tb_stringset);
48 	const struct nlattr *string;
49 	unsigned int size;
50 	unsigned int count;
51 	unsigned int idx;
52 	int ret;
53 
54 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_stringset_info);
55 	if (ret < 0)
56 		return ret;
57 	if (!tb_stringset[ETHTOOL_A_STRINGSET_ID] ||
58 	    !tb_stringset[ETHTOOL_A_STRINGSET_COUNT] ||
59 	    !tb_stringset[ETHTOOL_A_STRINGSET_STRINGS])
60 		return -EFAULT;
61 	idx = mnl_attr_get_u32(tb_stringset[ETHTOOL_A_STRINGSET_ID]);
62 	if (idx >= ETH_SS_COUNT)
63 		return 0;
64 	if (dest[idx].loaded)
65 		drop_stringset(&dest[idx]);
66 	dest[idx].loaded = true;
67 	count = mnl_attr_get_u32(tb_stringset[ETHTOOL_A_STRINGSET_COUNT]);
68 	if (count == 0)
69 		return 0;
70 
71 	size = mnl_attr_get_len(tb_stringset[ETHTOOL_A_STRINGSET_STRINGS]);
72 	ret = -ENOMEM;
73 	dest[idx].raw_data = malloc(size);
74 	if (!dest[idx].raw_data)
75 		goto err;
76 	memcpy(dest[idx].raw_data, tb_stringset[ETHTOOL_A_STRINGSET_STRINGS],
77 	       size);
78 	dest[idx].strings = calloc(count, sizeof(dest[idx].strings[0]));
79 	if (!dest[idx].strings)
80 		goto err;
81 	dest[idx].count = count;
82 
83 	nest = dest[idx].raw_data;
84 	mnl_attr_for_each_nested(string, nest) {
85 		const struct nlattr *tb[ETHTOOL_A_STRING_MAX + 1] = {};
86 		DECLARE_ATTR_TB_INFO(tb);
87 		unsigned int i;
88 
89 		if (mnl_attr_get_type(string) != ETHTOOL_A_STRINGS_STRING)
90 			continue;
91 		ret = mnl_attr_parse_nested(string, attr_cb, &tb_info);
92 		if (ret < 0)
93 			goto err;
94 		ret = -EFAULT;
95 		if (!tb[ETHTOOL_A_STRING_INDEX] || !tb[ETHTOOL_A_STRING_VALUE])
96 			goto err;
97 
98 		i = mnl_attr_get_u32(tb[ETHTOOL_A_STRING_INDEX]);
99 		if (i >= count)
100 			goto err;
101 		dest[idx].strings[i] =
102 			mnl_attr_get_payload(tb[ETHTOOL_A_STRING_VALUE]);
103 	}
104 
105 	return 0;
106 err:
107 	drop_stringset(&dest[idx]);
108 	return ret;
109 }
110 
get_perdev_by_ifindex(int ifindex)111 static struct perdev_strings *get_perdev_by_ifindex(int ifindex)
112 {
113 	struct perdev_strings *perdev = device_strings;
114 
115 	while (perdev && perdev->ifindex != ifindex)
116 		perdev = perdev->next;
117 	if (perdev)
118 		return perdev;
119 
120 	/* not found, allocate and insert into list */
121 	perdev = calloc(sizeof(*perdev), 1);
122 	if (!perdev)
123 		return NULL;
124 	perdev->ifindex = ifindex;
125 	perdev->next = device_strings;
126 	device_strings = perdev;
127 
128 	return perdev;
129 }
130 
strset_reply_cb(const struct nlmsghdr * nlhdr,void * data)131 static int strset_reply_cb(const struct nlmsghdr *nlhdr, void *data)
132 {
133 	const struct nlattr *tb[ETHTOOL_A_STRSET_MAX + 1] = {};
134 	DECLARE_ATTR_TB_INFO(tb);
135 	struct nl_context *nlctx = data;
136 	char devname[ALTIFNAMSIZ] = "";
137 	struct stringset *dest;
138 	struct nlattr *attr;
139 	int ifindex = 0;
140 	int ret;
141 
142 	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
143 	if (ret < 0)
144 		return ret;
145 	if (tb[ETHTOOL_A_STRSET_HEADER]) {
146 		ret = get_dev_info(tb[ETHTOOL_A_STRSET_HEADER], &ifindex,
147 				   devname);
148 		if (ret < 0)
149 			return MNL_CB_OK;
150 		if (ifindex && nlctx->filter_devname &&
151 		    strncmp(devname, nlctx->filter_devname, ALTIFNAMSIZ))
152 			return MNL_CB_OK;
153 	}
154 
155 	if (ifindex) {
156 		struct perdev_strings *perdev;
157 
158 		perdev = get_perdev_by_ifindex(ifindex);
159 		if (!perdev)
160 			return MNL_CB_OK;
161 		copy_devname(perdev->devname, devname);
162 		dest = perdev->strings;
163 	} else {
164 		dest = global_strings;
165 	}
166 
167 	if (!tb[ETHTOOL_A_STRSET_STRINGSETS])
168 		return MNL_CB_OK;
169 	mnl_attr_for_each_nested(attr, tb[ETHTOOL_A_STRSET_STRINGSETS]) {
170 		if (mnl_attr_get_type(attr) ==
171 		    ETHTOOL_A_STRINGSETS_STRINGSET)
172 			import_stringset(dest, attr);
173 	}
174 
175 	return MNL_CB_OK;
176 }
177 
fill_stringset_id(struct nl_msg_buff * msgbuff,unsigned int type)178 static int fill_stringset_id(struct nl_msg_buff *msgbuff, unsigned int type)
179 {
180 	struct nlattr *nest_sets;
181 	struct nlattr *nest_set;
182 
183 	nest_sets = ethnla_nest_start(msgbuff, ETHTOOL_A_STRSET_STRINGSETS);
184 	if (!nest_sets)
185 		return -EMSGSIZE;
186 	nest_set = ethnla_nest_start(msgbuff, ETHTOOL_A_STRINGSETS_STRINGSET);
187 	if (!nest_set)
188 		goto err;
189 	if (ethnla_put_u32(msgbuff, ETHTOOL_A_STRINGSET_ID, type))
190 		goto err;
191 	ethnla_nest_end(msgbuff, nest_set);
192 	ethnla_nest_end(msgbuff, nest_sets);
193 	return 0;
194 
195 err:
196 	ethnla_nest_cancel(msgbuff, nest_sets);
197 	return -EMSGSIZE;
198 }
199 
stringset_load_request(struct nl_socket * nlsk,const char * devname,int type,bool is_dump)200 static int stringset_load_request(struct nl_socket *nlsk, const char *devname,
201 				  int type, bool is_dump)
202 {
203 	struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
204 	int ret;
205 
206 	ret = msg_init(nlsk->nlctx, msgbuff, ETHTOOL_MSG_STRSET_GET,
207 		       NLM_F_REQUEST | NLM_F_ACK | (is_dump ? NLM_F_DUMP : 0));
208 	if (ret < 0)
209 		return ret;
210 	if (ethnla_fill_header(msgbuff, ETHTOOL_A_STRSET_HEADER, devname, 0))
211 		return -EMSGSIZE;
212 	if (type >= 0) {
213 		ret = fill_stringset_id(msgbuff, type);
214 		if (ret < 0)
215 			return ret;
216 	}
217 
218 	ret = nlsock_send_get_request(nlsk, strset_reply_cb);
219 	return ret;
220 }
221 
222 /* interface */
223 
global_stringset(unsigned int type,struct nl_socket * nlsk)224 const struct stringset *global_stringset(unsigned int type,
225 					 struct nl_socket *nlsk)
226 {
227 	int ret;
228 
229 	if (type >= ETH_SS_COUNT)
230 		return NULL;
231 	if (global_strings[type].loaded)
232 		return &global_strings[type];
233 	ret = stringset_load_request(nlsk, NULL, type, false);
234 	return ret < 0 ? NULL : &global_strings[type];
235 }
236 
perdev_stringset(const char * devname,unsigned int type,struct nl_socket * nlsk)237 const struct stringset *perdev_stringset(const char *devname, unsigned int type,
238 					 struct nl_socket *nlsk)
239 {
240 	const struct perdev_strings *p;
241 	int ret;
242 
243 	if (type >= ETH_SS_COUNT)
244 		return NULL;
245 	for (p = device_strings; p; p = p->next)
246 		if (!strcmp(p->devname, devname))
247 			return &p->strings[type];
248 
249 	ret = stringset_load_request(nlsk, devname, type, false);
250 	if (ret < 0)
251 		return NULL;
252 	for (p = device_strings; p; p = p->next)
253 		if (!strcmp(p->devname, devname))
254 			return &p->strings[type];
255 
256 	return NULL;
257 }
258 
get_count(const struct stringset * set)259 unsigned int get_count(const struct stringset *set)
260 {
261 	return set->count;
262 }
263 
get_string(const struct stringset * set,unsigned int idx)264 const char *get_string(const struct stringset *set, unsigned int idx)
265 {
266 	if (!set || idx >= set->count)
267 		return NULL;
268 	return set->strings[idx];
269 }
270 
preload_global_strings(struct nl_socket * nlsk)271 int preload_global_strings(struct nl_socket *nlsk)
272 {
273 	return stringset_load_request(nlsk, NULL, -1, false);
274 }
275 
preload_perdev_strings(struct nl_socket * nlsk,const char * dev)276 int preload_perdev_strings(struct nl_socket *nlsk, const char *dev)
277 {
278 	return stringset_load_request(nlsk, dev, -1, !dev);
279 }
280 
cleanup_all_strings(void)281 void cleanup_all_strings(void)
282 {
283 	struct perdev_strings *perdev;
284 	unsigned int i;
285 
286 	for (i = 0; i < ETH_SS_COUNT; i++)
287 		drop_stringset(&global_strings[i]);
288 
289 	perdev = device_strings;
290 	while (perdev) {
291 		device_strings = perdev->next;
292 		for (i = 0; i < ETH_SS_COUNT; i++)
293 			drop_stringset(&perdev->strings[i]);
294 		free(perdev);
295 		perdev = device_strings;
296 	}
297 }
298