xref: /freebsd/lib/libifconfig/libifconfig_nl.c (revision a259b98fa211ed87bfee58c575de4e2de94ee0fa)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2025, Muhammad Saheed <saheed@FreeBSD.org>
5  */
6 
7 #include <netlink/netlink.h>
8 #include <netlink/netlink_snl.h>
9 #include <netlink/netlink_snl_route_parsers.h>
10 #include <netlink/route/common.h>
11 #include <netlink/route/interface.h>
12 
13 #include <errno.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 #include "libifconfig.h"
18 #include "libifconfig_internal.h"
19 
20 static int ifconfig_modify_flags(ifconfig_handle_t *h, const char *ifname,
21     int ifi_flags, int ifi_change);
22 
23 struct ifconfig_vf_status_storage {
24 	/* The public object must remain first so free can recover this object. */
25 	struct ifconfig_vf_status public;
26 	struct ifconfig_vf_info *vf_info;
27 };
28 
29 static int
30 ifconfig_modify_flags(ifconfig_handle_t *h, const char *ifname, int ifi_flags,
31     int ifi_change)
32 {
33 	int ret = 0;
34 	struct snl_state ss;
35 	struct snl_writer nw;
36 	struct nlmsghdr *hdr;
37 	struct ifinfomsg *ifi;
38 	struct snl_errmsg_data e = { 0 };
39 
40 	if (!snl_init(&ss, NETLINK_ROUTE)) {
41 		ifconfig_error(h, NETLINK, ENOTSUP);
42 		return (-1);
43 	}
44 
45 	snl_init_writer(&ss, &nw);
46 	hdr = snl_create_msg_request(&nw, NL_RTM_NEWLINK);
47 	ifi = snl_reserve_msg_object(&nw, struct ifinfomsg);
48 	snl_add_msg_attr_string(&nw, IFLA_IFNAME, ifname);
49 
50 	ifi->ifi_flags = ifi_flags;
51 	ifi->ifi_change = ifi_change;
52 
53 	hdr = snl_finalize_msg(&nw);
54 	if (hdr == NULL) {
55 		ifconfig_error(h, NETLINK, ENOMEM);
56 		ret = -1;
57 		goto out;
58 	}
59 
60 	if (!snl_send_message(&ss, hdr)) {
61 		ifconfig_error(h, NETLINK, EIO);
62 		ret = -1;
63 		goto out;
64 	}
65 
66 	if (!snl_read_reply_code(&ss, hdr->nlmsg_seq, &e)) {
67 		ifconfig_error(h, NETLINK, e.error);
68 		ret = -1;
69 		goto out;
70 	}
71 
72 out:
73 	snl_free(&ss);
74 	return (ret);
75 }
76 
77 int
78 ifconfig_set_up(ifconfig_handle_t *h, const char *ifname, bool up)
79 {
80 	int flag = up ? IFF_UP : ~IFF_UP;
81 
82 	return (ifconfig_modify_flags(h, ifname, flag, IFF_UP));
83 }
84 
85 void
86 ifconfig_free_vf_status(struct ifconfig_vf_status *status)
87 {
88 	struct ifconfig_vf_status_storage *storage;
89 	struct ifconfig_vf_extension_field *field;
90 	struct ifconfig_vf_extension *extension;
91 	struct ifconfig_vf_info *vf;
92 	size_t i, j, k;
93 
94 	if (status == NULL)
95 		return;
96 	storage = (struct ifconfig_vf_status_storage *)(void *)status;
97 	for (i = 0; status->vfs != NULL && i < status->num_vfs; i++) {
98 		vf = status->vfs[i];
99 		if (vf == NULL)
100 			continue;
101 		free(vf->api_version);
102 		for (j = 0; j < vf->num_extensions; j++) {
103 			extension = &vf->extensions[j];
104 			free(extension->name);
105 			for (k = 0; k < extension->num_fields; k++) {
106 				field = &extension->fields[k];
107 				free(field->name);
108 				if (field->type == IFCONFIG_VF_EXT_STRING)
109 					free(field->value.string);
110 				else if (field->type == IFCONFIG_VF_EXT_BINARY)
111 					free(field->value.binary.data);
112 			}
113 			free(extension->fields);
114 		}
115 		free(vf->extensions);
116 	}
117 	free(storage->vf_info);
118 	free(status->vfs);
119 	free(storage);
120 }
121 
122 static int
123 ifconfig_copy_vf_extension_field(struct ifconfig_vf_extension_field *dst,
124     const struct snl_parsed_vf_driver_field *src)
125 {
126 	size_t length;
127 
128 	dst->name = strdup(src->name);
129 	if (dst->name == NULL)
130 		return (ENOMEM);
131 	switch (src->type) {
132 	case SNL_VFDF_BOOL:
133 		dst->type = IFCONFIG_VF_EXT_BOOL;
134 		dst->value.boolean = src->boolean;
135 		break;
136 	case SNL_VFDF_NUMBER:
137 		dst->type = IFCONFIG_VF_EXT_NUMBER;
138 		dst->value.number = src->number;
139 		break;
140 	case SNL_VFDF_STRING:
141 		dst->type = IFCONFIG_VF_EXT_STRING;
142 		dst->value.string = strdup(src->string);
143 		if (dst->value.string == NULL)
144 			return (ENOMEM);
145 		break;
146 	case SNL_VFDF_BINARY:
147 		dst->type = IFCONFIG_VF_EXT_BINARY;
148 		length = NLA_DATA_LEN(src->binary);
149 		if (length == 0)
150 			return (EBADMSG);
151 		dst->value.binary.data = malloc(length);
152 		if (dst->value.binary.data == NULL)
153 			return (ENOMEM);
154 		memcpy(dst->value.binary.data, NLA_DATA(src->binary), length);
155 		dst->value.binary.length = length;
156 		break;
157 	default:
158 		return (EBADMSG);
159 	}
160 	return (0);
161 }
162 
163 static int
164 ifconfig_copy_vf_extensions(struct ifconfig_vf_info *dst,
165     const struct snl_parsed_vf *src)
166 {
167 	const struct snl_parsed_vf_driver_field *src_field;
168 	const struct snl_parsed_vf_driver *src_extension;
169 	struct ifconfig_vf_extension *extension;
170 	size_t i, j, num_extensions, num_fields;
171 	int error;
172 
173 	num_extensions = src->drivers.count;
174 	if (num_extensions == 0)
175 		return (0);
176 	dst->extensions = calloc(num_extensions, sizeof(*dst->extensions));
177 	if (dst->extensions == NULL)
178 		return (ENOMEM);
179 	dst->num_extensions = num_extensions;
180 	for (i = 0; i < dst->num_extensions; i++) {
181 		src_extension = src->drivers.items[i];
182 		extension = &dst->extensions[i];
183 		extension->name = strdup(src_extension->name);
184 		if (extension->name == NULL)
185 			return (ENOMEM);
186 		extension->version = src_extension->version;
187 		num_fields = src_extension->fields.count;
188 		if (num_fields == 0)
189 			continue;
190 		extension->fields = calloc(num_fields,
191 		    sizeof(*extension->fields));
192 		if (extension->fields == NULL)
193 			return (ENOMEM);
194 		extension->num_fields = num_fields;
195 		for (j = 0; j < extension->num_fields; j++) {
196 			src_field = src_extension->fields.items[j];
197 			error = ifconfig_copy_vf_extension_field(
198 			    &extension->fields[j], src_field);
199 			if (error != 0)
200 				return (error);
201 		}
202 	}
203 	return (0);
204 }
205 
206 static int
207 ifconfig_copy_vf(struct ifconfig_vf_info *dst,
208     const struct snl_parsed_vf *src)
209 {
210 
211 	dst->fields = src->attrs;
212 	dst->min_tx_rate_bps = src->min_tx_rate_bps;
213 	dst->max_tx_rate_bps = src->max_tx_rate_bps;
214 	dst->index = src->index;
215 	dst->vlan_count = src->vlan_count;
216 	dst->vlan_limit = src->vlan_limit;
217 	dst->tx_queue_count = src->tx_queue_count;
218 	dst->rx_queue_count = src->rx_queue_count;
219 	dst->vlan = src->vlan;
220 	dst->vlan_proto = src->vlan_proto;
221 	dst->vlan_pcp = src->vlan_pcp;
222 	if ((src->attrs & (1ULL << IFLAF_VF_MAC)) != 0) {
223 		if (NLA_DATA_LEN(src->mac) != sizeof(dst->mac))
224 			return (EBADMSG);
225 		memcpy(dst->mac, NLA_DATA(src->mac), sizeof(dst->mac));
226 	}
227 	dst->vlan_mode = (enum ifconfig_vf_vlan_mode)src->vlan_mode;
228 	dst->link_state_policy =
229 	    (enum ifconfig_vf_link_state)src->link_state_policy;
230 	dst->configured = src->configured;
231 	dst->initialized = src->initialized;
232 	dst->allow_set_mac = src->allow_set_mac;
233 	dst->allow_set_vlan = src->allow_set_vlan;
234 	dst->mac_anti_spoof = src->mac_anti_spoof;
235 	dst->allow_promisc = src->allow_promisc;
236 	dst->traffic_allowed = src->traffic_allowed;
237 	dst->fault_blocked = src->fault_blocked;
238 	dst->quarantined = src->quarantined;
239 	if ((src->attrs & (1ULL << IFLAF_VF_API_VERSION)) != 0) {
240 		dst->api_version = strdup(src->api_version);
241 		if (dst->api_version == NULL)
242 			return (ENOMEM);
243 	}
244 	return (ifconfig_copy_vf_extensions(dst, src));
245 }
246 
247 int
248 ifconfig_get_vf_status(ifconfig_handle_t *h, const char *name,
249     struct ifconfig_vf_status **statusp)
250 {
251 	struct ifconfig_vf_status_storage *storage;
252 	struct ifconfig_vf_status *status;
253 	struct snl_parsed_link link = {};
254 	struct snl_errmsg_data e = {};
255 	struct snl_state ss = {};
256 	struct snl_writer nw = {};
257 	struct nlmsghdr *hdr;
258 	ifconfig_errtype errtype;
259 	uint32_t seq;
260 	size_t i;
261 	int error;
262 
263 	if (h == NULL || name == NULL || statusp == NULL) {
264 		if (h != NULL)
265 			ifconfig_error(h, OTHER, EINVAL);
266 		return (-1);
267 	}
268 	*statusp = NULL;
269 	if (strnlen(name, IFNAMSIZ) == IFNAMSIZ) {
270 		ifconfig_error(h, OTHER, ENAMETOOLONG);
271 		return (-1);
272 	}
273 	if (!snl_init(&ss, NETLINK_ROUTE)) {
274 		ifconfig_error(h, NETLINK, ENOTSUP);
275 		return (-1);
276 	}
277 	errtype = NETLINK;
278 
279 	snl_init_writer(&ss, &nw);
280 	hdr = snl_create_msg_request(&nw, NL_RTM_GETLINK);
281 	if (hdr == NULL ||
282 	    snl_reserve_msg_object(&nw, struct ifinfomsg) == NULL ||
283 	    !snl_add_msg_attr_string(&nw, IFLA_IFNAME, name) ||
284 	    !snl_add_msg_attr_u32(&nw, IFLA_EXT_MASK, RTEXT_FILTER_VF) ||
285 	    (hdr = snl_finalize_msg(&nw)) == NULL) {
286 		error = ENOMEM;
287 		goto fail;
288 	}
289 	seq = hdr->nlmsg_seq;
290 	if (!snl_send_message(&ss, hdr)) {
291 		error = EIO;
292 		goto fail;
293 	}
294 	error = snl_grow_rxbuf_to_next_message(&ss);
295 	if (error != 0) {
296 		if (error == ENOMEM)
297 			errtype = OTHER;
298 		goto fail;
299 	}
300 	hdr = snl_read_reply(&ss, seq);
301 	if (hdr == NULL) {
302 		error = EIO;
303 		goto fail;
304 	}
305 	if (hdr->nlmsg_type == NLMSG_ERROR) {
306 		if (!snl_parse_errmsg(&ss, hdr, &e) || e.error == 0)
307 			error = EBADMSG;
308 		else
309 			error = e.error;
310 		goto fail;
311 	}
312 	if (hdr->nlmsg_type != NL_RTM_NEWLINK ||
313 	    !snl_parse_nlmsg(&ss, hdr, &snl_rtm_link_parser, &link)) {
314 		error = EBADMSG;
315 		goto fail;
316 	}
317 	if (!link.iflaf_vf_status.present) {
318 		error = EOPNOTSUPP;
319 		goto fail;
320 	}
321 	if (link.iflaf_vf_status.error != 0) {
322 		error = link.iflaf_vf_status.error;
323 		goto fail;
324 	}
325 	if (link.ifla_num_vf != link.iflaf_vf_status.vfs.count) {
326 		error = EBADMSG;
327 		goto fail;
328 	}
329 
330 	storage = calloc(1, sizeof(*storage));
331 	if (storage == NULL) {
332 		errtype = OTHER;
333 		error = ENOMEM;
334 		goto fail;
335 	}
336 	status = &storage->public;
337 	status->pf_link_state = (enum ifconfig_vf_link_state)
338 	    link.iflaf_vf_status.pf_link_state;
339 	status->pf_link_state_present = true;
340 	status->pf_link_speed = link.iflaf_vf_status.pf_link_speed;
341 	status->pf_link_speed_present = status->pf_link_speed != 0;
342 	status->num_vfs = link.iflaf_vf_status.vfs.count;
343 	if (status->num_vfs != 0) {
344 		status->vfs = calloc(status->num_vfs, sizeof(*status->vfs));
345 		storage->vf_info = calloc(status->num_vfs,
346 		    sizeof(*storage->vf_info));
347 		if (status->vfs == NULL || storage->vf_info == NULL) {
348 			errtype = OTHER;
349 			error = ENOMEM;
350 			goto fail_status;
351 		}
352 		for (i = 0; i < status->num_vfs; i++)
353 			status->vfs[i] = &storage->vf_info[i];
354 	}
355 	for (i = 0; i < status->num_vfs; i++) {
356 		error = ifconfig_copy_vf(status->vfs[i],
357 		    link.iflaf_vf_status.vfs.items[i]);
358 		if (error != 0) {
359 			if (error == ENOMEM)
360 				errtype = OTHER;
361 			goto fail_status;
362 		}
363 	}
364 	ifconfig_error_clear(h);
365 	*statusp = status;
366 	snl_free(&ss);
367 	return (0);
368 
369 fail_status:
370 	ifconfig_free_vf_status(status);
371 fail:
372 	ifconfig_error(h, errtype, error);
373 	snl_free(&ss);
374 	return (-1);
375 }
376