1/* 2 * vmnet-bridged.m 3 * 4 * Copyright(c) 2022 Vladislav Yaroshchuk <vladislav.yaroshchuk@jetbrains.com> 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 * 9 */ 10 11#include "qemu/osdep.h" 12#include "qapi/qapi-types-net.h" 13#include "qapi/error.h" 14#include "clients.h" 15#include "vmnet_int.h" 16 17#include <vmnet/vmnet.h> 18 19 20static bool validate_ifname(const char *ifname) 21{ 22 xpc_object_t shared_if_list = vmnet_copy_shared_interface_list(); 23 bool match = false; 24 if (!xpc_array_get_count(shared_if_list)) { 25 goto done; 26 } 27 28 match = !xpc_array_apply( 29 shared_if_list, 30 ^bool(size_t index, xpc_object_t value) { 31 return strcmp(xpc_string_get_string_ptr(value), ifname) != 0; 32 }); 33 34done: 35 xpc_release(shared_if_list); 36 return match; 37} 38 39 40static char* get_valid_ifnames(void) 41{ 42 xpc_object_t shared_if_list = vmnet_copy_shared_interface_list(); 43 __block char *if_list = NULL; 44 __block char *if_list_prev = NULL; 45 46 if (!xpc_array_get_count(shared_if_list)) { 47 goto done; 48 } 49 50 xpc_array_apply( 51 shared_if_list, 52 ^bool(size_t index, xpc_object_t value) { 53 /* build list of strings like "en0 en1 en2 " */ 54 if_list = g_strconcat(xpc_string_get_string_ptr(value), 55 " ", 56 if_list_prev, 57 NULL); 58 g_free(if_list_prev); 59 if_list_prev = if_list; 60 return true; 61 }); 62 63done: 64 xpc_release(shared_if_list); 65 return if_list; 66} 67 68 69static bool validate_options(const Netdev *netdev, Error **errp) 70{ 71 const NetdevVmnetBridgedOptions *options = &(netdev->u.vmnet_bridged); 72 char* if_list; 73 74 if (!validate_ifname(options->ifname)) { 75 if_list = get_valid_ifnames(); 76 if (if_list) { 77 error_setg(errp, 78 "unsupported ifname '%s', expected one of [ %s]", 79 options->ifname, 80 if_list); 81 g_free(if_list); 82 } else { 83 error_setg(errp, 84 "unsupported ifname '%s', no supported " 85 "interfaces available", 86 options->ifname); 87 } 88 return false; 89 } 90 91 return true; 92} 93 94 95static xpc_object_t build_if_desc(const Netdev *netdev) 96{ 97 const NetdevVmnetBridgedOptions *options = &(netdev->u.vmnet_bridged); 98 xpc_object_t if_desc = xpc_dictionary_create(NULL, NULL, 0); 99 100 xpc_dictionary_set_uint64(if_desc, 101 vmnet_operation_mode_key, 102 VMNET_BRIDGED_MODE 103 ); 104 105 xpc_dictionary_set_string(if_desc, 106 vmnet_shared_interface_name_key, 107 options->ifname); 108 109 xpc_dictionary_set_bool(if_desc, 110 vmnet_enable_isolation_key, 111 options->isolated); 112 113 return if_desc; 114} 115 116 117static NetClientInfo net_vmnet_bridged_info = { 118 .type = NET_CLIENT_DRIVER_VMNET_BRIDGED, 119 .size = sizeof(VmnetState), 120 .receive = vmnet_receive_common, 121 .cleanup = vmnet_cleanup_common, 122}; 123 124 125int net_init_vmnet_bridged(const Netdev *netdev, const char *name, 126 NetClientState *peer, Error **errp) 127{ 128 NetClientState *nc = qemu_new_net_client(&net_vmnet_bridged_info, 129 peer, "vmnet-bridged", name); 130 xpc_object_t if_desc; 131 int result = -1; 132 133 if (!validate_options(netdev, errp)) { 134 return result; 135 } 136 137 if_desc = build_if_desc(netdev); 138 result = vmnet_if_create(nc, if_desc, errp); 139 xpc_release(if_desc); 140 return result; 141} 142