|
@@ -85,9 +85,8 @@ public partial class DeviceDetailsWindow : Window
|
|
|
RemoteConfigIpTextBlock.Text = "-";
|
|
RemoteConfigIpTextBlock.Text = "-";
|
|
|
RemoteConfigGatewayTextBlock.Text = "-";
|
|
RemoteConfigGatewayTextBlock.Text = "-";
|
|
|
RemoteConfigDnsTextBlock.Text = "-";
|
|
RemoteConfigDnsTextBlock.Text = "-";
|
|
|
- NewIpTextBox.Text = string.Empty;
|
|
|
|
|
- NewMaskTextBox.Text = string.Empty;
|
|
|
|
|
- NewGatewayTextBox.Text = string.Empty;
|
|
|
|
|
|
|
+ NewAddressesTextBox.Text = string.Empty;
|
|
|
|
|
+ NewRoutesTextBox.Text = string.Empty;
|
|
|
NewDnsTextBox.Text = string.Empty;
|
|
NewDnsTextBox.Text = string.Empty;
|
|
|
_configValidated = false;
|
|
_configValidated = false;
|
|
|
}
|
|
}
|
|
@@ -134,14 +133,13 @@ public partial class DeviceDetailsWindow : Window
|
|
|
var config = result.Data;
|
|
var config = result.Data;
|
|
|
RemoteConfigInterfaceTextBlock.Text = config.Interface;
|
|
RemoteConfigInterfaceTextBlock.Text = config.Interface;
|
|
|
RemoteConfigIpTextBlock.Text = FormatCurrentIp(config);
|
|
RemoteConfigIpTextBlock.Text = FormatCurrentIp(config);
|
|
|
- RemoteConfigGatewayTextBlock.Text = string.IsNullOrWhiteSpace(config.Gateway) ? "无" : config.Gateway;
|
|
|
|
|
|
|
+ RemoteConfigGatewayTextBlock.Text = FormatRoutes(config.EffectiveRoutes);
|
|
|
RemoteConfigDnsTextBlock.Text = config.DnsSummary;
|
|
RemoteConfigDnsTextBlock.Text = config.DnsSummary;
|
|
|
_suppressConfigChangeHandling = true;
|
|
_suppressConfigChangeHandling = true;
|
|
|
Dhcp4CheckBox.IsChecked = false;
|
|
Dhcp4CheckBox.IsChecked = false;
|
|
|
- NewIpTextBox.Text = config.IP;
|
|
|
|
|
- NewMaskTextBox.Text = PrefixToMask(config.Prefix);
|
|
|
|
|
- NewGatewayTextBox.Text = config.Gateway;
|
|
|
|
|
- NewDnsTextBox.Text = config.Dns?.FirstOrDefault() ?? string.Empty;
|
|
|
|
|
|
|
+ NewAddressesTextBox.Text = string.Join(Environment.NewLine, config.EffectiveAddresses.Select(item => $"{item.IP}/{item.Prefix}"));
|
|
|
|
|
+ NewRoutesTextBox.Text = string.Join(Environment.NewLine, config.EffectiveRoutes.Select(item => $"{item.To} via {item.Via}"));
|
|
|
|
|
+ NewDnsTextBox.Text = config.Dns is null ? string.Empty : string.Join(Environment.NewLine, config.Dns);
|
|
|
_suppressConfigChangeHandling = false;
|
|
_suppressConfigChangeHandling = false;
|
|
|
_configValidated = false;
|
|
_configValidated = false;
|
|
|
ShowStatusMessage("已读取Linux端IP配置。");
|
|
ShowStatusMessage("已读取Linux端IP配置。");
|
|
@@ -216,8 +214,8 @@ public partial class DeviceDetailsWindow : Window
|
|
|
|
|
|
|
|
var confirmMessage = $"将要把以下配置应用到接口 {selected.SystemName}:\n\n" +
|
|
var confirmMessage = $"将要把以下配置应用到接口 {selected.SystemName}:\n\n" +
|
|
|
$"模式:{(request.Dhcp4 ? "DHCP 自动获取" : "静态 IPv4")}\n" +
|
|
$"模式:{(request.Dhcp4 ? "DHCP 自动获取" : "静态 IPv4")}\n" +
|
|
|
- $"IP:{(request.Dhcp4 ? "自动获取" : $"{request.IP}/{request.Prefix}")}\n" +
|
|
|
|
|
- $"网关:{(string.IsNullOrWhiteSpace(request.Gateway) ? "无" : request.Gateway)}\n" +
|
|
|
|
|
|
|
+ $"IP:{(request.Dhcp4 ? "自动获取" : FormatAddresses(request.Addresses))}\n" +
|
|
|
|
|
+ $"路由:{(request.Dhcp4 ? "自动获取" : FormatRoutes(request.Routes))}\n" +
|
|
|
$"DNS:{(request.Dns.Count == 0 ? "无" : string.Join(", ", request.Dns))}\n\n" +
|
|
$"DNS:{(request.Dns.Count == 0 ? "无" : string.Join(", ", request.Dns))}\n\n" +
|
|
|
"请确认是否继续。";
|
|
"请确认是否继续。";
|
|
|
if (MessageBox.Show(this, confirmMessage, "确认应用配置", MessageBoxButton.OKCancel, MessageBoxImage.Question) != MessageBoxResult.OK)
|
|
if (MessageBox.Show(this, confirmMessage, "确认应用配置", MessageBoxButton.OKCancel, MessageBoxImage.Question) != MessageBoxResult.OK)
|
|
@@ -422,42 +420,138 @@ public partial class DeviceDetailsWindow : Window
|
|
|
private RemoteInterfaceConfig? BuildConfigRequest(string interfaceName)
|
|
private RemoteInterfaceConfig? BuildConfigRequest(string interfaceName)
|
|
|
{
|
|
{
|
|
|
var dhcp4 = Dhcp4CheckBox.IsChecked == true;
|
|
var dhcp4 = Dhcp4CheckBox.IsChecked == true;
|
|
|
- var prefix = 0;
|
|
|
|
|
- if (!dhcp4 && string.IsNullOrWhiteSpace(NewIpTextBox.Text))
|
|
|
|
|
|
|
+ var addresses = Array.Empty<RemoteInterfaceAddressConfig>();
|
|
|
|
|
+ var routes = Array.Empty<RemoteInterfaceRouteConfig>();
|
|
|
|
|
+ if (!dhcp4 && string.IsNullOrWhiteSpace(NewAddressesTextBox.Text))
|
|
|
{
|
|
{
|
|
|
- ShowStatusMessage("IP 地址不能为空。");
|
|
|
|
|
|
|
+ ShowStatusMessage("IP 地址不能为空,至少需要填写一行地址。");
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if (!dhcp4 && !TryMaskToPrefix(NewMaskTextBox.Text, out prefix))
|
|
|
|
|
|
|
+ if (!dhcp4 && !TryParseAddresses(NewAddressesTextBox.Text, out addresses, out var addressError))
|
|
|
{
|
|
{
|
|
|
- ShowStatusMessage("子网掩码格式不正确。");
|
|
|
|
|
|
|
+ ShowStatusMessage(addressError);
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- var dns = string.IsNullOrWhiteSpace(NewDnsTextBox.Text) ? Array.Empty<string>() : new[] { NewDnsTextBox.Text.Trim() };
|
|
|
|
|
|
|
+ if (!dhcp4 && !TryParseRoutes(NewRoutesTextBox.Text, out routes, out var routeError))
|
|
|
|
|
+ {
|
|
|
|
|
+ ShowStatusMessage(routeError);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var dns = ParseListText(NewDnsTextBox.Text);
|
|
|
return new RemoteInterfaceConfig
|
|
return new RemoteInterfaceConfig
|
|
|
{
|
|
{
|
|
|
Interface = interfaceName,
|
|
Interface = interfaceName,
|
|
|
Dhcp4 = dhcp4,
|
|
Dhcp4 = dhcp4,
|
|
|
- IP = dhcp4 ? string.Empty : NewIpTextBox.Text.Trim(),
|
|
|
|
|
- Prefix = prefix,
|
|
|
|
|
- Gateway = dhcp4 ? string.Empty : NewGatewayTextBox.Text.Trim(),
|
|
|
|
|
|
|
+ Addresses = dhcp4 ? Array.Empty<RemoteInterfaceAddressConfig>() : addresses,
|
|
|
|
|
+ Routes = dhcp4 ? Array.Empty<RemoteInterfaceRouteConfig>() : routes,
|
|
|
Dns = dhcp4 ? Array.Empty<string>() : dns,
|
|
Dns = dhcp4 ? Array.Empty<string>() : dns,
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private static string FormatCurrentIp(RemoteInterfaceConfig config)
|
|
private static string FormatCurrentIp(RemoteInterfaceConfig config)
|
|
|
{
|
|
{
|
|
|
- if (string.IsNullOrWhiteSpace(config.IP))
|
|
|
|
|
|
|
+ if (config.EffectiveAddresses.Count == 0)
|
|
|
{
|
|
{
|
|
|
return config.Dhcp4 ? "DHCP 自动获取,暂无 IPv4" : "无";
|
|
return config.Dhcp4 ? "DHCP 自动获取,暂无 IPv4" : "无";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- var text = $"{config.IP}/{config.Prefix}";
|
|
|
|
|
|
|
+ var text = FormatAddresses(config.EffectiveAddresses);
|
|
|
return config.Dhcp4 ? $"{text} (DHCP)" : text;
|
|
return config.Dhcp4 ? $"{text} (DHCP)" : text;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private static string FormatAddresses(IReadOnlyList<RemoteInterfaceAddressConfig> addresses)
|
|
|
|
|
+ {
|
|
|
|
|
+ return addresses.Count == 0 ? "无" : string.Join(Environment.NewLine, addresses.Select(item => $"{item.IP}/{item.Prefix}"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static string FormatRoutes(IReadOnlyList<RemoteInterfaceRouteConfig> routes)
|
|
|
|
|
+ {
|
|
|
|
|
+ return routes.Count == 0 ? "无" : string.Join(Environment.NewLine, routes.Select(item => $"{item.To} via {item.Via}"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static bool TryParseAddresses(string text, out RemoteInterfaceAddressConfig[] addresses, out string error)
|
|
|
|
|
+ {
|
|
|
|
|
+ var result = new List<RemoteInterfaceAddressConfig>();
|
|
|
|
|
+ foreach (var line in ParseListText(text))
|
|
|
|
|
+ {
|
|
|
|
|
+ var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
|
|
|
|
+ if (parts.Length == 1 && line.Contains('/'))
|
|
|
|
|
+ {
|
|
|
|
|
+ var cidrParts = line.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
|
|
|
|
+ if (cidrParts.Length != 2 || !int.TryParse(cidrParts[1], out var prefix) || prefix < 0 || prefix > 32)
|
|
|
|
|
+ {
|
|
|
|
|
+ addresses = [];
|
|
|
|
|
+ error = $"地址格式不正确:{line}";
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ result.Add(new RemoteInterfaceAddressConfig { IP = cidrParts[0], Prefix = prefix });
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (parts.Length != 2)
|
|
|
|
|
+ {
|
|
|
|
|
+ addresses = [];
|
|
|
|
|
+ error = $"地址格式不正确:{line}";
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!TryMaskOrPrefixToPrefix(parts[1], out var parsedPrefix))
|
|
|
|
|
+ {
|
|
|
|
|
+ addresses = [];
|
|
|
|
|
+ error = $"子网掩码或前缀格式不正确:{line}";
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ result.Add(new RemoteInterfaceAddressConfig { IP = parts[0], Prefix = parsedPrefix });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ addresses = result.ToArray();
|
|
|
|
|
+ error = string.Empty;
|
|
|
|
|
+ return addresses.Length > 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static bool TryParseRoutes(string text, out RemoteInterfaceRouteConfig[] routes, out string error)
|
|
|
|
|
+ {
|
|
|
|
|
+ var result = new List<RemoteInterfaceRouteConfig>();
|
|
|
|
|
+ foreach (var line in ParseListText(text))
|
|
|
|
|
+ {
|
|
|
|
|
+ var parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
|
|
|
|
+ if (parts.Length == 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ result.Add(new RemoteInterfaceRouteConfig { To = "default", Via = parts[0] });
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (parts.Length == 3 && parts[1].Equals("via", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
+ {
|
|
|
|
|
+ result.Add(new RemoteInterfaceRouteConfig { To = parts[0], Via = parts[2] });
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ routes = [];
|
|
|
|
|
+ error = $"路由格式不正确:{line}";
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ routes = result.ToArray();
|
|
|
|
|
+ error = string.Empty;
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static string[] ParseListText(string text)
|
|
|
|
|
+ {
|
|
|
|
|
+ return text.Split(['\r', '\n', ',', ';'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static bool TryMaskOrPrefixToPrefix(string text, out int prefix)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (int.TryParse(text, out prefix) && prefix >= 0 && prefix <= 32)
|
|
|
|
|
+ {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ return TryMaskToPrefix(text, out prefix);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private static string PrefixToMask(int prefix)
|
|
private static string PrefixToMask(int prefix)
|
|
|
{
|
|
{
|
|
|
if (prefix < 0 || prefix > 32)
|
|
if (prefix < 0 || prefix > 32)
|
|
@@ -645,9 +739,8 @@ public partial class DeviceDetailsWindow : Window
|
|
|
ValidateConfigButton.IsEnabled = canEdit;
|
|
ValidateConfigButton.IsEnabled = canEdit;
|
|
|
ApplyConfigButton.IsEnabled = !_isBusy && _configValidated && hasSelectedInterface;
|
|
ApplyConfigButton.IsEnabled = !_isBusy && _configValidated && hasSelectedInterface;
|
|
|
Dhcp4CheckBox.IsEnabled = canEdit;
|
|
Dhcp4CheckBox.IsEnabled = canEdit;
|
|
|
- NewIpTextBox.IsEnabled = canEdit && Dhcp4CheckBox.IsChecked != true;
|
|
|
|
|
- NewMaskTextBox.IsEnabled = canEdit && Dhcp4CheckBox.IsChecked != true;
|
|
|
|
|
- NewGatewayTextBox.IsEnabled = canEdit && Dhcp4CheckBox.IsChecked != true;
|
|
|
|
|
|
|
+ NewAddressesTextBox.IsEnabled = canEdit && Dhcp4CheckBox.IsChecked != true;
|
|
|
|
|
+ NewRoutesTextBox.IsEnabled = canEdit && Dhcp4CheckBox.IsChecked != true;
|
|
|
NewDnsTextBox.IsEnabled = canEdit && Dhcp4CheckBox.IsChecked != true;
|
|
NewDnsTextBox.IsEnabled = canEdit && Dhcp4CheckBox.IsChecked != true;
|
|
|
RebootButton.IsEnabled = !_isBusy;
|
|
RebootButton.IsEnabled = !_isBusy;
|
|
|
ShutdownButton.IsEnabled = !_isBusy;
|
|
ShutdownButton.IsEnabled = !_isBusy;
|