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() 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#if !defined(MAC_OS_VERSION_11_0) || \ 92 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_11_0 93 if (options->has_isolated) { 94 error_setg(errp, 95 "vmnet-bridged.isolated feature is " 96 "unavailable: outdated vmnet.framework API"); 97 return false; 98 } 99#endif 100 return true; 101} 102 103 104static xpc_object_t build_if_desc(const Netdev *netdev) 105{ 106 const NetdevVmnetBridgedOptions *options = &(netdev->u.vmnet_bridged); 107 xpc_object_t if_desc = xpc_dictionary_create(NULL, NULL, 0); 108 109 xpc_dictionary_set_uint64(if_desc, 110 vmnet_operation_mode_key, 111 VMNET_BRIDGED_MODE 112 ); 113 114 xpc_dictionary_set_string(if_desc, 115 vmnet_shared_interface_name_key, 116 options->ifname); 117 118#if defined(MAC_OS_VERSION_11_0) && \ 119 MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 120 xpc_dictionary_set_bool(if_desc, 121 vmnet_enable_isolation_key, 122 options->isolated); 123#endif 124 return if_desc; 125} 126 127 128static NetClientInfo net_vmnet_bridged_info = { 129 .type = NET_CLIENT_DRIVER_VMNET_BRIDGED, 130 .size = sizeof(VmnetState), 131 .receive = vmnet_receive_common, 132 .cleanup = vmnet_cleanup_common, 133}; 134 135 136int net_init_vmnet_bridged(const Netdev *netdev, const char *name, 137 NetClientState *peer, Error **errp) 138{ 139 NetClientState *nc = qemu_new_net_client(&net_vmnet_bridged_info, 140 peer, "vmnet-bridged", name); 141 xpc_object_t if_desc; 142 int result = -1; 143 144 if (!validate_options(netdev, errp)) { 145 return result; 146 } 147 148 if_desc = build_if_desc(netdev); 149 result = vmnet_if_create(nc, if_desc, errp); 150 xpc_release(if_desc); 151 return result; 152} 153