Merge pull request #103 from lucannez64/fix/vivox-ifmedia

Handle SIOCGIFMEDIA for Vivox interface discovery
This commit is contained in:
Kalah authored and GitHub committed 2026-10-25 19:31:38 +01:00
commit 38344a9cde
5 files changed
+319

No files matched your search

+1
View File
@@ -37,6 +37,7 @@ jobs:
- run: python -m unittest discover -s tests -p 'test_exec_guest.py'
- run: python -m unittest discover -s tests -p 'test_close_league_client_in_game.py'
- run: python -m unittest discover -s tests -p 'test_recover_stale_client.py'
- run: python -m unittest discover -s tests -p 'test_ifmedia.py'
- run: python -m unittest discover -s tests -p 'test_clean_stale_helpers.py'
- run: python -m unittest discover -s tests -p 'test_hide_league_client_in_game.py'
- run: python -m unittest discover -s tests -p 'test_client_in_game_tricks.py'
+94
View File
@@ -6200,3 +6200,97 @@ diff --git a/darling/src/libsystem_kernel/emulation/src/linux_premigration/vchro
ctxt->current_root = prefix_path;
ctxt->current_root_len = prefix_path_len;
}
diff --git a/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/helper/ioctl/socket.c b/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/helper/ioctl/socket.c
index f890ab2..8969577 100644
--- a/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/helper/ioctl/socket.c
+++ b/darling/src/libsystem_kernel/emulation/src/xnu_syscall/bsd/helper/ioctl/socket.c
@@ -2,12 +2,29 @@
#include <sys/errno.h>
#include <sys/socket.h>
-#include <ifaddrs.h>
#include <net/if.h>
+#include <net/if_media.h>
#include <netinet/in.h>
#include <sys/sockio.h>
#include <netinet6/in6_var.h>
+#include <darling/emulation/xnu_syscall/bsd/impl/ioctl/ioctl.h>
+
+extern int strcmp(const char* a, const char* b);
+extern int strncmp(const char* a, const char* b, size_t n);
+extern char* strncpy(char* destination, const char* source, size_t length);
+
+#define LINUX_SIOCGIFFLAGS 0x8913
+#define LINUX_IFNAMSIZ 16
+
+struct linux_ifreq {
+ char lifr_name[LINUX_IFNAMSIZ];
+ union {
+ short lifr_flags;
+ char lifr_pad[24];
+ };
+};
+
int handle_socket(int fd, unsigned int cmd, void* arg, int* retval)
{
switch (cmd)
@@ -19,6 +36,57 @@ int handle_socket(int fd, unsigned int cmd, void* arg, int* retval)
case SIOCAIFADDR_IN6: // set IPv6 address
*retval = -ENOTSUP;
return IOCTL_HANDLED;
+ case SIOCGIFMEDIA:
+#ifdef SIOCGIFXMEDIA
+ case SIOCGIFXMEDIA:
+#endif
+ {
+ struct ifmediareq* ifmr = (struct ifmediareq*) arg;
+ struct linux_ifreq ifr;
+ int status;
+
+ if (ifmr == NULL || ifmr->ifm_count < 0)
+ {
+ *retval = -EINVAL;
+ return IOCTL_HANDLED;
+ }
+
+ ifr.lifr_name[LINUX_IFNAMSIZ - 1] = '\0';
+ strncpy(ifr.lifr_name, ifmr->ifm_name, LINUX_IFNAMSIZ - 1);
+ status = __real_ioctl(fd, LINUX_SIOCGIFFLAGS, &ifr);
+ if (status < 0)
+ {
+ *retval = status;
+ return IOCTL_HANDLED;
+ }
+
+ if (ifr.lifr_flags & IFF_LOOPBACK)
+ {
+ *retval = -ENOTSUP;
+ return IOCTL_HANDLED;
+ }
+
+ if (strncmp(ifmr->ifm_name, "wl", 2) == 0)
+ ifmr->ifm_current = IFM_IEEE80211 | IFM_AUTO;
+ else
+ ifmr->ifm_current = IFM_ETHER | IFM_AUTO;
+ ifmr->ifm_mask = 0;
+ ifmr->ifm_status = ((ifr.lifr_flags & IFF_UP) && (ifr.lifr_flags & IFF_RUNNING))
+ ? (IFM_AVALID | IFM_ACTIVE) : IFM_AVALID;
+ ifmr->ifm_active = ifmr->ifm_current;
+ if (ifmr->ifm_count > 0)
+ {
+ if (ifmr->ifm_ulist == NULL)
+ {
+ *retval = -EFAULT;
+ return IOCTL_HANDLED;
+ }
+ ifmr->ifm_ulist[0] = ifmr->ifm_current;
+ }
+ ifmr->ifm_count = 1;
+ *retval = 0;
+ return IOCTL_HANDLED;
+ }
}
return IOCTL_PASS;
}
+94
View File
@@ -0,0 +1,94 @@
"""Compile the added interface-media handler with inert ioctl fixtures."""
from pathlib import Path
import shutil
import unittest
import test_runtime_compatibility as sources
@unittest.skipUnless(shutil.which('cc'), 'C compiler required')
class InterfaceMediaTests(unittest.TestCase):
run_c = sources.CompatibilityChecks.run_c
def test_media_queries_preserve_buffers_and_exclude_loopback(self):
patch = (Path(__file__).resolve().parents[1] / 'patches/xnu.patch').read_text()
section = patch.split('diff --git a/darling/src/libsystem_kernel/emulation/src/'
'xnu_syscall/bsd/helper/ioctl/socket.c ', 1)[1]
added = '\n'.join(line[1:] for line in section.splitlines()
if line.startswith('+') and not line.startswith('+++'))
handler = added.split('\t\tcase SIOCGIFMEDIA:', 1)[1]
self.run_c(r'''
#include <assert.h>
#include <errno.h>
#include <string.h>
#define SIOCGIFMEDIA 1
#define SIOCGIFXMEDIA 2
#define IOCTL_HANDLED 1
#define IOCTL_PASS 0
#define IFF_UP 1
#define IFF_LOOPBACK 8
#define IFF_RUNNING 64
#define IFM_ETHER 0x20
#define IFM_IEEE80211 0x80
#define IFM_AUTO 0
#define IFM_AVALID 1
#define IFM_ACTIVE 2
#define LINUX_SIOCGIFFLAGS 0x8913
#define LINUX_IFNAMSIZ 16
struct linux_ifreq { char lifr_name[16]; short lifr_flags; char pad[22]; };
struct ifmediareq {
char ifm_name[16];
int ifm_current, ifm_mask, ifm_status, ifm_active, ifm_count;
int *ifm_ulist;
};
static short flags;
static int ioctl_result;
static int __real_ioctl(int fd, int command, struct linux_ifreq *request) {
assert(fd == 3 && command == LINUX_SIOCGIFFLAGS);
request->lifr_flags = flags; return ioctl_result;
}
static int handle_socket(int fd, unsigned cmd, void *arg, int *retval) {
switch (cmd) { case SIOCGIFMEDIA:
''' + handler + r'''
}
return IOCTL_PASS;
}
int main(void) {
struct ifmediareq request = {.ifm_name = "eth0"};
int result = 99, list[] = {-1, -2};
flags = IFF_UP | IFF_RUNNING;
assert(handle_socket(3, SIOCGIFMEDIA, &request, &result) == IOCTL_HANDLED);
assert(result == 0 && request.ifm_count == 1 && request.ifm_ulist == NULL);
assert(request.ifm_current == IFM_ETHER && request.ifm_active == IFM_ETHER);
assert(request.ifm_status == (IFM_AVALID | IFM_ACTIVE));
request.ifm_count = 2; request.ifm_ulist = list;
assert(handle_socket(3, SIOCGIFXMEDIA, &request, &result) == IOCTL_HANDLED);
assert(result == 0 && request.ifm_count == 1 && request.ifm_ulist == list);
assert(list[0] == IFM_ETHER && list[1] == -2);
strcpy(request.ifm_name, "wlan0"); request.ifm_count = 1;
handle_socket(3, SIOCGIFMEDIA, &request, &result);
assert(result == 0 && list[0] == IFM_IEEE80211);
flags = 0;
handle_socket(3, SIOCGIFMEDIA, &request, &result);
assert(result == 0 && request.ifm_status == IFM_AVALID);
flags = IFF_UP | IFF_RUNNING | IFF_LOOPBACK;
list[0] = -1;
handle_socket(3, SIOCGIFMEDIA, &request, &result);
assert(result == -ENOTSUP && list[0] == -1 && request.ifm_ulist == list);
flags = IFF_UP | IFF_RUNNING; request.ifm_count = -1;
handle_socket(3, SIOCGIFMEDIA, &request, &result);
assert(result == -EINVAL);
request.ifm_count = 1; request.ifm_ulist = NULL;
handle_socket(3, SIOCGIFMEDIA, &request, &result);
assert(result == -EFAULT);
handle_socket(3, SIOCGIFMEDIA, NULL, &result);
assert(result == -EINVAL);
request.ifm_count = 0; ioctl_result = -ENODEV;
handle_socket(3, SIOCGIFMEDIA, &request, &result);
assert(result == -ENODEV);
}
''')
if __name__ == '__main__':
unittest.main()
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
"""Build and run the standalone SIOCGIFMEDIA probe inside Darling.
No Riot or League process is started. The probe only enumerates local
interfaces and issues SIOCGIFMEDIA, which is the call Vivox uses before
vx_req_connector_create.
"""
import argparse
import json
import os
from pathlib import Path
import shutil
import subprocess
import sys
ROOT = Path(__file__).resolve().parents[1]
CONFIG = ROOT / 'build/config.json'
SOURCE = ROOT / 'tools/riot-ifmedia-check.c'
OUTPUT = ROOT / 'build/riot-ifmedia-check'
def find_sdk():
candidates = [
ROOT / 'build/darling-src/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk',
ROOT / 'build/darling-src/build/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk',
]
for candidate in candidates:
if (candidate / 'usr/include/sys/socket.h').is_file():
return candidate
raise SystemExit('MacOSX.sdk not found; run ./scripts/build.sh first')
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--guest-home', default=None,
help='guest home, e.g. /Users/example (default: build/config.json)')
parser.add_argument('--library-path', default=None,
help='extra guest DYLD_LIBRARY_PATH entry, e.g. a rebuilt kernel overlay')
parser.add_argument('--timeout', type=float, default=60)
args = parser.parse_args()
config = json.loads(CONFIG.read_text()) if CONFIG.exists() else {}
guest_home = args.guest_home or config.get('guest_home')
if not guest_home:
guest_home = '/Users/' + os.environ.get('USER', '')
sdk = find_sdk()
clang = shutil.which('clang')
if not clang:
raise SystemExit('clang not found')
OUTPUT.parent.mkdir(exist_ok=True)
subprocess.run([
clang, '-target', 'x86_64-apple-macosx11.0', '-D__DARWIN_UNIX03=0',
'-fuse-ld=lld', '-nostdlib', '-isysroot', str(sdk),
str(SOURCE), '-lSystem', '-o', str(OUTPUT),
], check=True)
libraries = guest_home.rstrip('/') + '/RuntimeLibraries'
if args.library_path:
libraries = args.library_path.rstrip('/') + ':' + libraries
guest_probe = '/Volumes/SystemRoot' + str(OUTPUT)
env = {key: os.environ[key] for key in ('PATH', 'HOME', 'USER', 'LOGNAME', 'DISPLAY',
'XAUTHORITY', 'XDG_RUNTIME_DIR', 'DBUS_SESSION_BUS_ADDRESS', 'LANG') if key in os.environ}
result = subprocess.run([
'darling', 'shell', 'env', 'DYLD_LIBRARY_PATH=' + libraries,
guest_probe,
], env=env, timeout=args.timeout)
return result.returncode
if __name__ == '__main__':
sys.exit(main())
+58
View File
@@ -0,0 +1,58 @@
// Vivox's network reachability provider calls SIOCGIFMEDIA on each interface
// and drops every interface whose ioctl fails. Darling used to pass the Darwin
// request through to Linux, which returns ENOTTY.
#include <ifaddrs.h>
#include <net/if.h>
#include <net/if_media.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
static int check(const char *name, int required) {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
struct ifmediareq ifmr;
memset(&ifmr, 0, sizeof(ifmr));
strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name) - 1);
errno = 0;
int rc = ioctl(sock, SIOCGIFMEDIA, &ifmr);
close(sock);
int active = (ifmr.ifm_status & (IFM_AVALID | IFM_ACTIVE)) == (IFM_AVALID | IFM_ACTIVE);
int known_type = IFM_TYPE(ifmr.ifm_current) == IFM_ETHER ||
IFM_TYPE(ifmr.ifm_current) == IFM_IEEE80211;
printf("IFMEDIA interface=%s rc=%d errno=%d current=0x%x status=0x%x active=%d type=%d\n",
name, rc, errno, ifmr.ifm_current, ifmr.ifm_status, active, known_type);
if (rc != 0) return required;
return required && (!active || !known_type);
}
int main(int argc, char **argv) {
int failures = 0;
if (argc > 1) {
for (int i = 1; i < argc; i++) failures += check(argv[i], 1);
} else {
struct ifaddrs *interfaces = NULL;
char name[IFNAMSIZ] = {0};
if (getifaddrs(&interfaces) != 0) {
printf("IFMEDIA getifaddrs failed errno=%d\n", errno);
return 1;
}
for (struct ifaddrs *entry = interfaces; entry; entry = entry->ifa_next) {
if (!entry->ifa_addr || entry->ifa_addr->sa_family != AF_INET) continue;
if (entry->ifa_flags & IFF_LOOPBACK) continue;
strncpy(name, entry->ifa_name, sizeof(name) - 1);
break;
}
freeifaddrs(interfaces);
if (!name[0]) {
printf("IFMEDIA no non-loopback IPv4 interface\n");
return 1;
}
failures += check(name, 1);
}
printf("IFMEDIA_CHECK failures=%d\n", failures);
return failures ? 1 : 0;
}