| /* | 
 |  * vmnet-bridged.m | 
 |  * | 
 |  * Copyright(c) 2022 Vladislav Yaroshchuk <vladislav.yaroshchuk@jetbrains.com> | 
 |  * | 
 |  * This work is licensed under the terms of the GNU GPL, version 2 or later. | 
 |  * See the COPYING file in the top-level directory. | 
 |  * | 
 |  */ | 
 |  | 
 | #include "qemu/osdep.h" | 
 | #include "qapi/qapi-types-net.h" | 
 | #include "qapi/error.h" | 
 | #include "clients.h" | 
 | #include "vmnet_int.h" | 
 |  | 
 | #include <vmnet/vmnet.h> | 
 |  | 
 |  | 
 | static bool validate_ifname(const char *ifname) | 
 | { | 
 |     xpc_object_t shared_if_list = vmnet_copy_shared_interface_list(); | 
 |     bool match = false; | 
 |     if (!xpc_array_get_count(shared_if_list)) { | 
 |         goto done; | 
 |     } | 
 |  | 
 |     match = !xpc_array_apply( | 
 |         shared_if_list, | 
 |         ^bool(size_t index, xpc_object_t value) { | 
 |             return strcmp(xpc_string_get_string_ptr(value), ifname) != 0; | 
 |         }); | 
 |  | 
 | done: | 
 |     xpc_release(shared_if_list); | 
 |     return match; | 
 | } | 
 |  | 
 |  | 
 | static char* get_valid_ifnames(void) | 
 | { | 
 |     xpc_object_t shared_if_list = vmnet_copy_shared_interface_list(); | 
 |     __block char *if_list = NULL; | 
 |     __block char *if_list_prev = NULL; | 
 |  | 
 |     if (!xpc_array_get_count(shared_if_list)) { | 
 |         goto done; | 
 |     } | 
 |  | 
 |     xpc_array_apply( | 
 |         shared_if_list, | 
 |         ^bool(size_t index, xpc_object_t value) { | 
 |             /* build list of strings like "en0 en1 en2 " */ | 
 |             if_list = g_strconcat(xpc_string_get_string_ptr(value), | 
 |                                   " ", | 
 |                                   if_list_prev, | 
 |                                   NULL); | 
 |             g_free(if_list_prev); | 
 |             if_list_prev = if_list; | 
 |             return true; | 
 |         }); | 
 |  | 
 | done: | 
 |     xpc_release(shared_if_list); | 
 |     return if_list; | 
 | } | 
 |  | 
 |  | 
 | static bool validate_options(const Netdev *netdev, Error **errp) | 
 | { | 
 |     const NetdevVmnetBridgedOptions *options = &(netdev->u.vmnet_bridged); | 
 |     char* if_list; | 
 |  | 
 |     if (!validate_ifname(options->ifname)) { | 
 |         if_list = get_valid_ifnames(); | 
 |         if (if_list) { | 
 |             error_setg(errp, | 
 |                        "unsupported ifname '%s', expected one of [ %s]", | 
 |                        options->ifname, | 
 |                        if_list); | 
 |             g_free(if_list); | 
 |         } else { | 
 |             error_setg(errp, | 
 |                        "unsupported ifname '%s', no supported " | 
 |                        "interfaces available", | 
 |                        options->ifname); | 
 |         } | 
 |         return false; | 
 |     } | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 |  | 
 | static xpc_object_t build_if_desc(const Netdev *netdev) | 
 | { | 
 |     const NetdevVmnetBridgedOptions *options = &(netdev->u.vmnet_bridged); | 
 |     xpc_object_t if_desc = xpc_dictionary_create(NULL, NULL, 0); | 
 |  | 
 |     xpc_dictionary_set_uint64(if_desc, | 
 |                               vmnet_operation_mode_key, | 
 |                               VMNET_BRIDGED_MODE | 
 |     ); | 
 |  | 
 |     xpc_dictionary_set_string(if_desc, | 
 |                               vmnet_shared_interface_name_key, | 
 |                               options->ifname); | 
 |  | 
 |     xpc_dictionary_set_bool(if_desc, | 
 |                             vmnet_enable_isolation_key, | 
 |                             options->isolated); | 
 |  | 
 |     return if_desc; | 
 | } | 
 |  | 
 |  | 
 | static NetClientInfo net_vmnet_bridged_info = { | 
 |     .type = NET_CLIENT_DRIVER_VMNET_BRIDGED, | 
 |     .size = sizeof(VmnetState), | 
 |     .receive = vmnet_receive_common, | 
 |     .cleanup = vmnet_cleanup_common, | 
 | }; | 
 |  | 
 |  | 
 | int net_init_vmnet_bridged(const Netdev *netdev, const char *name, | 
 |                            NetClientState *peer, Error **errp) | 
 | { | 
 |     NetClientState *nc = qemu_new_net_client(&net_vmnet_bridged_info, | 
 |                                              peer, "vmnet-bridged", name); | 
 |     xpc_object_t if_desc; | 
 |     int result = -1; | 
 |  | 
 |     if (!validate_options(netdev, errp)) { | 
 |         return result; | 
 |     } | 
 |  | 
 |     if_desc = build_if_desc(netdev); | 
 |     result = vmnet_if_create(nc, if_desc, errp); | 
 |     xpc_release(if_desc); | 
 |     return result; | 
 | } |