vmnet-bridged.m 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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. #include "qemu/osdep.h"
  11. #include "qapi/qapi-types-net.h"
  12. #include "qapi/error.h"
  13. #include "clients.h"
  14. #include "vmnet_int.h"
  15. #include <vmnet/vmnet.h>
  16. static bool validate_ifname(const char *ifname)
  17. {
  18. xpc_object_t shared_if_list = vmnet_copy_shared_interface_list();
  19. bool match = false;
  20. if (!xpc_array_get_count(shared_if_list)) {
  21. goto done;
  22. }
  23. match = !xpc_array_apply(
  24. shared_if_list,
  25. ^bool(size_t index, xpc_object_t value) {
  26. return strcmp(xpc_string_get_string_ptr(value), ifname) != 0;
  27. });
  28. done:
  29. xpc_release(shared_if_list);
  30. return match;
  31. }
  32. static char* get_valid_ifnames(void)
  33. {
  34. xpc_object_t shared_if_list = vmnet_copy_shared_interface_list();
  35. __block char *if_list = NULL;
  36. __block char *if_list_prev = NULL;
  37. if (!xpc_array_get_count(shared_if_list)) {
  38. goto done;
  39. }
  40. xpc_array_apply(
  41. shared_if_list,
  42. ^bool(size_t index, xpc_object_t value) {
  43. /* build list of strings like "en0 en1 en2 " */
  44. if_list = g_strconcat(xpc_string_get_string_ptr(value),
  45. " ",
  46. if_list_prev,
  47. NULL);
  48. g_free(if_list_prev);
  49. if_list_prev = if_list;
  50. return true;
  51. });
  52. done:
  53. xpc_release(shared_if_list);
  54. return if_list;
  55. }
  56. static bool validate_options(const Netdev *netdev, Error **errp)
  57. {
  58. const NetdevVmnetBridgedOptions *options = &(netdev->u.vmnet_bridged);
  59. char* if_list;
  60. if (!validate_ifname(options->ifname)) {
  61. if_list = get_valid_ifnames();
  62. if (if_list) {
  63. error_setg(errp,
  64. "unsupported ifname '%s', expected one of [ %s]",
  65. options->ifname,
  66. if_list);
  67. g_free(if_list);
  68. } else {
  69. error_setg(errp,
  70. "unsupported ifname '%s', no supported "
  71. "interfaces available",
  72. options->ifname);
  73. }
  74. return false;
  75. }
  76. if (__builtin_available(macOS 11, *)) {
  77. // clang requires a true branch
  78. } else {
  79. if (options->has_isolated) {
  80. error_setg(errp,
  81. "vmnet-bridged.isolated feature is "
  82. "unavailable: outdated vmnet.framework API");
  83. return false;
  84. }
  85. }
  86. return true;
  87. }
  88. static xpc_object_t build_if_desc(const Netdev *netdev)
  89. {
  90. const NetdevVmnetBridgedOptions *options = &(netdev->u.vmnet_bridged);
  91. xpc_object_t if_desc = xpc_dictionary_create(NULL, NULL, 0);
  92. xpc_dictionary_set_uint64(if_desc,
  93. vmnet_operation_mode_key,
  94. VMNET_BRIDGED_MODE
  95. );
  96. xpc_dictionary_set_string(if_desc,
  97. vmnet_shared_interface_name_key,
  98. options->ifname);
  99. if (__builtin_available(macOS 11, *)) {
  100. xpc_dictionary_set_bool(if_desc,
  101. vmnet_enable_isolation_key,
  102. options->isolated);
  103. }
  104. return if_desc;
  105. }
  106. static NetClientInfo net_vmnet_bridged_info = {
  107. .type = NET_CLIENT_DRIVER_VMNET_BRIDGED,
  108. .size = sizeof(VmnetState),
  109. .receive = vmnet_receive_common,
  110. .cleanup = vmnet_cleanup_common,
  111. };
  112. int net_init_vmnet_bridged(const Netdev *netdev, const char *name,
  113. NetClientState *peer, Error **errp)
  114. {
  115. NetClientState *nc = qemu_new_net_client(&net_vmnet_bridged_info,
  116. peer, "vmnet-bridged", name);
  117. xpc_object_t if_desc;
  118. int result = -1;
  119. if (!validate_options(netdev, errp)) {
  120. return result;
  121. }
  122. if_desc = build_if_desc(netdev);
  123. result = vmnet_if_create(nc, if_desc, errp);
  124. xpc_release(if_desc);
  125. return result;
  126. }