using System.Globalization; namespace LibNftables.Tests; internal static class NativeTestSupport { private const string PrivilegedTestsEnvironmentVariable = "LIBNFTABLES_RUN_PRIVILEGED_TESTS"; internal static bool IsRunningAsRoot() { if (!OperatingSystem.IsLinux()) { return false; } try { foreach (var line in File.ReadLines("/proc/self/status")) { if (!line.StartsWith("Uid:", StringComparison.Ordinal)) { continue; } var parts = line["Uid:".Length..] .Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); return parts.Length > 0 && parts[0] == "0"; } } catch { // If uid probing fails, keep tests conservative. } return false; } internal static bool PrivilegedTestsRequested() { var value = Environment.GetEnvironmentVariable(PrivilegedTestsEnvironmentVariable); if (string.IsNullOrWhiteSpace(value)) { return false; } return value.Equals("1", StringComparison.Ordinal) || value.Equals("true", StringComparison.OrdinalIgnoreCase) || value.Equals("yes", StringComparison.OrdinalIgnoreCase); } internal static bool ShouldRunPrivilegedTests() { return PrivilegedTestsRequested() && IsRunningAsRoot() && HasCapNetAdmin(); } internal static bool HasCapNetAdmin() { const int capNetAdminBit = 12; const ulong mask = 1UL << capNetAdminBit; try { foreach (var line in File.ReadLines("/proc/self/status")) { if (!line.StartsWith("CapEff:", StringComparison.Ordinal)) { continue; } var hex = line["CapEff:".Length..].Trim(); if (ulong.TryParse(hex, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out var value)) { return (value & mask) != 0; } } } catch { // If capability probing fails, keep tests conservative. } return false; } internal static bool TryCreateContext(out NftContext context) { try { context = new NftContext(); return true; } catch (DllNotFoundException) { context = null!; return false; } catch (TypeInitializationException ex) when (ex.InnerException is DllNotFoundException) { context = null!; return false; } catch (BadImageFormatException) { context = null!; return false; } catch (EntryPointNotFoundException) { context = null!; return false; } } }