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