mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-26 19:52:51 +08:00
🏅 Sync 2026-10-03 01:21:24
This commit is contained in:
1 parent
cb1d978497
commit
54d0dfd24f
9 files changed
+209
-78
No files matched your search
+5
-1
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=linkback
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=2
|
||||
PKG_RELEASE:=3
|
||||
PKG_MAINTAINER:=Dante
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
||||
@@ -56,6 +56,10 @@ define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_CONF) ./files/linkback.config $(1)/etc/config/linkback
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/linkback.init $(1)/etc/init.d/linkback
|
||||
|
||||
# Install hotplug script
|
||||
$(INSTALL_DIR) $(1)/etc/hotplug.d/iface
|
||||
$(INSTALL_BIN) ./files/90-linkback $(1)/etc/hotplug.d/iface/90-linkback
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/postinst
|
||||
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Copyright (C) 2026 Dante <dante@seckv.com>
|
||||
#
|
||||
# Hotplug event trigger script for LinkBack daemon.
|
||||
# Instantly wakes up linkbackd on interface ifup/ifdown state changes.
|
||||
|
||||
[ "$ACTION" = "ifup" ] || [ "$ACTION" = "ifdown" ] || exit 0
|
||||
[ -n "$INTERFACE" ] || exit 0
|
||||
|
||||
# Check if linkbackd is running
|
||||
PID=$(pidof linkbackd)
|
||||
[ -n "$PID" ] || exit 0
|
||||
|
||||
# Send SIGUSR1 to trigger immediate health check / short-circuit
|
||||
kill -USR1 $PID 2>/dev/null
|
||||
+125
-36
@@ -40,6 +40,12 @@ static struct ubus_context *ubus_ctx = NULL;
|
||||
// Prototypes
|
||||
static void restore_all_metrics(void);
|
||||
static bool validate_loaded_config(void);
|
||||
static volatile sig_atomic_t hotplug_triggered = 0;
|
||||
static void handle_sigusr1(int sig) {
|
||||
(void)sig;
|
||||
hotplug_triggered = 1;
|
||||
}
|
||||
|
||||
static void handle_signal(int sig) {
|
||||
syslog(LOG_INFO, "Received signal %d, exiting...", sig);
|
||||
keep_running = false;
|
||||
@@ -439,6 +445,9 @@ static bool load_config(void) {
|
||||
|
||||
const char *failover = uci_lookup_option_string(ctx, global_sec, "failover_delay");
|
||||
if (failover) global_cfg.failover_delay = atoi(failover);
|
||||
|
||||
global_cfg.is_up = false;
|
||||
global_cfg.last_is_up = false;
|
||||
}
|
||||
|
||||
// Parse links
|
||||
@@ -527,6 +536,7 @@ static bool load_config(void) {
|
||||
// Default runtime states
|
||||
link->healthy = true;
|
||||
link->is_up = false;
|
||||
link->last_is_up = false;
|
||||
link->last_checked = 0; // Force immediate check on startup
|
||||
|
||||
link_count++;
|
||||
@@ -843,9 +853,10 @@ int main(int argc, char **argv) {
|
||||
openlog("linkbackd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
|
||||
syslog(LOG_INFO, "Starting LinkBack daemon...");
|
||||
|
||||
// Register signal handlers for clean exits and metric restoration
|
||||
// Register signal handlers for clean exits, metric restoration, and hotplug wakeup
|
||||
signal(SIGTERM, handle_signal);
|
||||
signal(SIGINT, handle_signal);
|
||||
signal(SIGUSR1, handle_sigusr1);
|
||||
|
||||
// Load configuration
|
||||
if (!load_config()) {
|
||||
@@ -871,59 +882,137 @@ int main(int argc, char **argv) {
|
||||
time_t now = time(NULL);
|
||||
bool any_checked = false;
|
||||
|
||||
// If in multi_gw mode, resolve bind interface physical device once per cycle
|
||||
bool force_check = false;
|
||||
if (hotplug_triggered) {
|
||||
hotplug_triggered = 0;
|
||||
force_check = true;
|
||||
syslog(LOG_INFO, "[Diagnostic] Hotplug signal received (SIGUSR1), triggering immediate interface & health evaluation.");
|
||||
}
|
||||
|
||||
// In multi_gw mode, resolve and verify the bind interface state first
|
||||
if (global_cfg.mode == MODE_MULTI_GW) {
|
||||
char dev[MAX_NAME_LEN] = {0};
|
||||
char gw[MAX_IP_LEN] = {0};
|
||||
bool if_up = false;
|
||||
get_interface_ubus_status(global_cfg.interface, dev, sizeof(dev), gw, sizeof(gw), &if_up);
|
||||
strncpy(global_cfg.device, dev, sizeof(global_cfg.device) - 1);
|
||||
bool ubus_ok = get_interface_ubus_status(global_cfg.interface, dev, sizeof(dev), gw, sizeof(gw), &if_up);
|
||||
bool physical_up = ubus_ok && if_up && (dev[0] != '\0');
|
||||
|
||||
if (dev[0] != '\0') {
|
||||
strncpy(global_cfg.device, dev, sizeof(global_cfg.device) - 1);
|
||||
global_cfg.device[sizeof(global_cfg.device) - 1] = '\0';
|
||||
}
|
||||
global_cfg.is_up = physical_up;
|
||||
|
||||
// Diagnostic logging on bind interface UP/DOWN transition
|
||||
if (global_cfg.is_up != global_cfg.last_is_up) {
|
||||
syslog(LOG_NOTICE, "[Diagnostic] Multi-GW bind interface '%s' (%s) link state changed: %s -> %s",
|
||||
global_cfg.interface, global_cfg.device[0] ? global_cfg.device : "unknown",
|
||||
global_cfg.last_is_up ? "UP" : "DOWN",
|
||||
global_cfg.is_up ? "UP" : "DOWN");
|
||||
global_cfg.last_is_up = global_cfg.is_up;
|
||||
}
|
||||
|
||||
// P2 & Diagnostic: Global short-circuit when bind interface is DOWN.
|
||||
// Stop all gateway probe attempts immediately without wasting timeout intervals.
|
||||
if (!global_cfg.is_up) {
|
||||
bool state_changed = false;
|
||||
for (int i = 0; i < link_count; i++) {
|
||||
link_t *link = &links[i];
|
||||
link->is_up = false;
|
||||
if (global_cfg.device[0] != '\0') {
|
||||
strncpy(link->device, global_cfg.device, sizeof(link->device) - 1);
|
||||
link->device[sizeof(link->device) - 1] = '\0';
|
||||
}
|
||||
if (link->gateway_cfg[0] != '\0') {
|
||||
strncpy(link->gateway, link->gateway_cfg, sizeof(link->gateway) - 1);
|
||||
link->gateway[sizeof(link->gateway) - 1] = '\0';
|
||||
}
|
||||
|
||||
if (link->healthy || link->current_metric != 1000 + link->metric) {
|
||||
syslog(LOG_WARNING, "[Diagnostic] Multi-GW bind interface '%s' is DOWN. Short-circuiting probe for target '%s' (gw=%s) and demoting metric.",
|
||||
global_cfg.interface, link->name, link->gateway[0] ? link->gateway : "none");
|
||||
link->healthy = false;
|
||||
update_route_metric(link, 1000 + link->metric, -1);
|
||||
state_changed = true;
|
||||
}
|
||||
|
||||
link->current_score = 0;
|
||||
link->ping_ok = false;
|
||||
link->dns_ok = false;
|
||||
link->tcp_ok = false;
|
||||
link->consecutive_success = 0;
|
||||
link->consecutive_failure = link->failover_delay;
|
||||
link->last_checked = now;
|
||||
}
|
||||
|
||||
if (state_changed) {
|
||||
write_status_json();
|
||||
}
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < link_count; i++) {
|
||||
link_t *link = &links[i];
|
||||
|
||||
// Check if this link is due for checking
|
||||
if (now - link->last_checked < link->check_interval) {
|
||||
// Check if this link is due for checking (bypass interval if hotplug triggered)
|
||||
if (!force_check && (now - link->last_checked < link->check_interval)) {
|
||||
continue;
|
||||
}
|
||||
link->last_checked = now;
|
||||
any_checked = true;
|
||||
|
||||
// 1. Fetch real-time netifd status
|
||||
char dev[MAX_NAME_LEN] = {0};
|
||||
char gw[MAX_IP_LEN] = {0};
|
||||
bool is_up = false;
|
||||
|
||||
if (global_cfg.mode == MODE_MULTI_GW) {
|
||||
// Inherit physical device from global bind interface
|
||||
is_up = (global_cfg.device[0] != '\0');
|
||||
strncpy(dev, global_cfg.device, sizeof(dev) - 1);
|
||||
strncpy(gw, link->gateway_cfg, sizeof(gw) - 1);
|
||||
// Inherit verified UP device from global bind interface
|
||||
link->is_up = true;
|
||||
strncpy(link->device, global_cfg.device, sizeof(link->device) - 1);
|
||||
link->device[sizeof(link->device) - 1] = '\0';
|
||||
strncpy(link->gateway, link->gateway_cfg, sizeof(link->gateway) - 1);
|
||||
link->gateway[sizeof(link->gateway) - 1] = '\0';
|
||||
} else {
|
||||
// Query netifd for WAN interface
|
||||
get_interface_ubus_status(link->name, dev, sizeof(dev), gw, sizeof(gw), &is_up);
|
||||
}
|
||||
// Multi-WAN mode: query netifd for independent interface status
|
||||
char dev[MAX_NAME_LEN] = {0};
|
||||
char gw[MAX_IP_LEN] = {0};
|
||||
bool is_up = false;
|
||||
bool ubus_ok = get_interface_ubus_status(link->name, dev, sizeof(dev), gw, sizeof(gw), &is_up);
|
||||
bool physical_up = ubus_ok && is_up && (dev[0] != '\0');
|
||||
|
||||
link->is_up = is_up;
|
||||
strncpy(link->device, dev, MAX_NAME_LEN - 1);
|
||||
strncpy(link->gateway, gw, MAX_IP_LEN - 1);
|
||||
if (physical_up != link->last_is_up) {
|
||||
syslog(LOG_NOTICE, "[Diagnostic] Multi-WAN interface '%s' (%s) link state changed: %s -> %s",
|
||||
link->name, dev[0] ? dev : "unknown",
|
||||
link->last_is_up ? "UP" : "DOWN",
|
||||
physical_up ? "UP" : "DOWN");
|
||||
link->last_is_up = physical_up;
|
||||
}
|
||||
|
||||
// P2 optimization: Interface down short-circuit
|
||||
if (!is_up || dev[0] == '\0') {
|
||||
link->healthy = false;
|
||||
link->current_score = 0;
|
||||
link->ping_ok = false;
|
||||
link->dns_ok = false;
|
||||
link->tcp_ok = false;
|
||||
link->consecutive_success = 0;
|
||||
link->consecutive_failure = 0;
|
||||
link->is_up = physical_up;
|
||||
if (dev[0] != '\0') {
|
||||
strncpy(link->device, dev, sizeof(link->device) - 1);
|
||||
link->device[sizeof(link->device) - 1] = '\0';
|
||||
}
|
||||
if (gw[0] != '\0') {
|
||||
strncpy(link->gateway, gw, sizeof(link->gateway) - 1);
|
||||
link->gateway[sizeof(link->gateway) - 1] = '\0';
|
||||
}
|
||||
|
||||
// If it still has low metric, float it immediately
|
||||
if (link->current_metric == link->metric) {
|
||||
update_route_metric(link, 1000 + link->metric, -1);
|
||||
// Local short-circuit: interface DOWN, skip probe for this link
|
||||
if (!physical_up) {
|
||||
if (link->healthy || link->current_metric != 1000 + link->metric) {
|
||||
syslog(LOG_WARNING, "[Diagnostic] Multi-WAN interface '%s' is DOWN. Short-circuiting probe for link '%s' and demoting metric.",
|
||||
link->name, link->name);
|
||||
link->healthy = false;
|
||||
update_route_metric(link, 1000 + link->metric, -1);
|
||||
}
|
||||
link->current_score = 0;
|
||||
link->ping_ok = false;
|
||||
link->dns_ok = false;
|
||||
link->tcp_ok = false;
|
||||
link->consecutive_success = 0;
|
||||
link->consecutive_failure = link->failover_delay;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2. Perform health checks (P2: 1s non-blocking timeout)
|
||||
@@ -973,7 +1062,7 @@ int main(int argc, char **argv) {
|
||||
if (!link->healthy && link->consecutive_success >= link->recovery_delay) {
|
||||
// Recovered! Failback!
|
||||
link->healthy = true;
|
||||
syslog(LOG_NOTICE, "Link %s (%s, gw=%s, priority %d) recovered to healthy after %d successes.",
|
||||
syslog(LOG_NOTICE, "[Diagnostic] Link %s (%s, gw=%s, priority %d) recovered to healthy after %d successes.",
|
||||
link->name, link->device, link->gateway[0] ? link->gateway : "none", link->priority, link->consecutive_success);
|
||||
|
||||
// Restore original metric
|
||||
@@ -986,7 +1075,7 @@ int main(int argc, char **argv) {
|
||||
if (link->healthy && link->consecutive_failure >= link->failover_delay) {
|
||||
// Failed! Failover!
|
||||
link->healthy = false;
|
||||
syslog(LOG_WARNING, "Link %s (%s, gw=%s, priority %d) went down after %d failures.",
|
||||
syslog(LOG_WARNING, "[Diagnostic] Link %s (%s, gw=%s, priority %d) went down after %d failures.",
|
||||
link->name, link->device, link->gateway[0] ? link->gateway : "none", link->priority, link->consecutive_failure);
|
||||
|
||||
// Push metric out of choice range
|
||||
@@ -999,7 +1088,7 @@ int main(int argc, char **argv) {
|
||||
int expected_metric = (link->is_up && link->healthy) ? link->metric : (1000 + link->metric);
|
||||
int real_metric = get_system_route_metric(link->device, link->gateway, expected_metric);
|
||||
if (real_metric != -1 && real_metric != expected_metric) {
|
||||
syslog(LOG_WARNING, "Route metric mismatch detected on %s (%s, gw=%s, priority %d): expected %d, got %d. Correcting...",
|
||||
syslog(LOG_WARNING, "[Diagnostic] Route metric mismatch detected on %s (%s, gw=%s, priority %d): expected %d, got %d. Correcting...",
|
||||
link->name, link->device, link->gateway[0] ? link->gateway : "none", link->priority, expected_metric, real_metric);
|
||||
update_route_metric(link, expected_metric, real_metric);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ typedef struct {
|
||||
char device[MAX_NAME_LEN]; // physical interface name e.g., pppoe-wan, eth1, br-lan
|
||||
char gateway[MAX_IP_LEN]; // gateway IP address
|
||||
bool is_up; // whether interface is reported up by netifd
|
||||
bool last_is_up; // track interface UP/DOWN transition for logging
|
||||
bool healthy; // daemon link health state
|
||||
int consecutive_success;
|
||||
int consecutive_failure;
|
||||
@@ -73,6 +74,8 @@ typedef struct {
|
||||
linkback_mode_t mode;
|
||||
char interface[MAX_NAME_LEN]; // Bind interface in multi_gw mode (e.g. lan)
|
||||
char device[MAX_NAME_LEN]; // Resolved device in multi_gw mode (e.g. br-lan)
|
||||
bool is_up; // Bind interface netifd up status
|
||||
bool last_is_up; // track bind interface UP/DOWN transition for logging
|
||||
int check_interval;
|
||||
int check_timeout;
|
||||
int recovery_delay;
|
||||
|
||||
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-linkback
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=2
|
||||
PKG_RELEASE:=3
|
||||
PKG_MAINTAINER:=Dante
|
||||
|
||||
LUCI_TITLE:=LuCI support for Multi-WAN LinkBack Failover/Failback
|
||||
|
||||
@@ -168,6 +168,8 @@ return view.extend({
|
||||
uci.set('linkback', section_id, 'enabled', value);
|
||||
};
|
||||
|
||||
var refreshTargetsSection = null;
|
||||
|
||||
// 2. Working Mode
|
||||
o = s.option(form.ListValue, 'mode', _('Working Mode'),
|
||||
_('Choose failover mode: Multi-WAN interface failover or Single-WAN multi-gateway redundancy.'));
|
||||
@@ -177,17 +179,12 @@ return view.extend({
|
||||
o.rmempty = false;
|
||||
o.onchange = function(ev, section_id, value) {
|
||||
uci.set('linkback', section_id, 'mode', value);
|
||||
var links = uci.sections('linkback', 'link') || [];
|
||||
if (links.length > 0) {
|
||||
if (value === 'multi_gw') {
|
||||
ui.addNotification(null, E('p',
|
||||
_('已切换为单wan多网关模式。请确保监控目标列表中配置了有效的网关 IP。')
|
||||
), 'info');
|
||||
} else {
|
||||
ui.addNotification(null, E('p',
|
||||
_('已切换为多wan口模式。请确保监控目标列表中选择了正确的 WAN 接口。')
|
||||
), 'info');
|
||||
}
|
||||
// 切换工作模式时直接清空旧链路配置,保持单模式纯净性
|
||||
uci.sections('linkback', 'link').forEach(function(sec) {
|
||||
uci.remove('linkback', sec['.name']);
|
||||
});
|
||||
if (typeof refreshTargetsSection === 'function') {
|
||||
refreshTargetsSection();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -228,11 +225,37 @@ return view.extend({
|
||||
o.rmempty = false;
|
||||
|
||||
// --- Monitored Targets Section ---
|
||||
s = m.section(form.GridSection, 'link', _('Monitored Targets'),
|
||||
var targets_section = s = m.section(form.GridSection, 'link', _('Monitored Targets'),
|
||||
_('Configure monitored WAN interfaces or next-hop gateways with custom probe targets and priorities (1 = primary, 2 = backup).'));
|
||||
s.anonymous = true;
|
||||
s.addremove = true;
|
||||
|
||||
var origSectionRender = s.render;
|
||||
s.render = function() {
|
||||
var mode = getActiveMode();
|
||||
if (mode === 'multi_gw') {
|
||||
this.title = _('Monitored Gateways');
|
||||
this.description = _('Add next-hop gateways with custom probe targets and priorities (1 = primary, 2 = backup).');
|
||||
} else {
|
||||
this.title = _('Monitored WAN Interfaces');
|
||||
this.description = _('Add WAN interfaces from firewall zone with custom probe targets and priorities (1 = primary, 2 = backup).');
|
||||
}
|
||||
return origSectionRender.apply(this, arguments);
|
||||
};
|
||||
|
||||
refreshTargetsSection = function() {
|
||||
var old_node = document.getElementById('cbi-linkback-link') ||
|
||||
document.querySelector('.cbi-section[data-tab="link"]') ||
|
||||
document.querySelector('.cbi-section[id*="link"]');
|
||||
if (old_node && old_node.parentNode) {
|
||||
targets_section.render().then(function(new_node) {
|
||||
if (old_node.parentNode) {
|
||||
old_node.parentNode.replaceChild(new_node, old_node);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Custom dynamic Modal title
|
||||
s.modaltitle = function(section_id) {
|
||||
var parent_title = _('LinkBack 链路守护') + ' - ' + _('Settings');
|
||||
@@ -284,27 +307,19 @@ return view.extend({
|
||||
o.cfgvalue = function(section_id) {
|
||||
var gw = uci.get('linkback', section_id, 'gateway');
|
||||
var name = uci.get('linkback', section_id, 'name');
|
||||
var mode = getActiveMode();
|
||||
if (mode === 'multi_gw' || gw) {
|
||||
if (gw) {
|
||||
if (name && name !== gw) {
|
||||
return gw + ' (' + name + ')';
|
||||
}
|
||||
return gw;
|
||||
}
|
||||
return name ? (_('Missing Gateway IP: %s').format(name)) : _('Unset');
|
||||
} else {
|
||||
return name || _('Unselected');
|
||||
if (gw) {
|
||||
return (name && name !== gw) ? (gw + ' (' + name + ')') : gw;
|
||||
}
|
||||
return name || '-';
|
||||
};
|
||||
makeTableColumnExpand(o, '24%');
|
||||
|
||||
// 3a. Gateway IP (Multi-GW mode only, Modal only)
|
||||
var o_gw = s.option(form.Value, 'gateway', _('Gateway IP'),
|
||||
_('Next-hop IPv4 address of this gateway (e.g. 192.168.1.254).'));
|
||||
_('Next-hop IPv4 address of this gateway (e.g. 192.168.1.1).'));
|
||||
o_gw.datatype = 'ip4addr';
|
||||
o_gw.modalonly = true;
|
||||
o_gw.placeholder = '192.168.1.254';
|
||||
o_gw.placeholder = '192.168.1.1';
|
||||
var origRenderGw = o_gw.render;
|
||||
o_gw.render = function(option_index, section_id, in_table) {
|
||||
if (getActiveMode() !== 'multi_gw') {
|
||||
@@ -350,9 +365,9 @@ return view.extend({
|
||||
|
||||
// 3b. Gateway Alias (Multi-GW mode only, Modal only)
|
||||
var o_alias = s.option(form.Value, 'name_alias', _('Gateway Alias (Optional)'),
|
||||
_('Descriptive alias for this gateway (e.g. Bypass_GW, Main_Router). If empty, Gateway IP will be used.'));
|
||||
_('Descriptive alias for this gateway (e.g. Primary_GW, Backup_GW). If empty, Gateway IP will be used.'));
|
||||
o_alias.modalonly = true;
|
||||
o_alias.placeholder = 'Bypass_GW';
|
||||
o_alias.placeholder = 'Primary_GW';
|
||||
var origRenderAlias = o_alias.render;
|
||||
o_alias.render = function(option_index, section_id, in_table) {
|
||||
if (getActiveMode() !== 'multi_gw') {
|
||||
@@ -432,11 +447,19 @@ return view.extend({
|
||||
};
|
||||
|
||||
// 3. Priority
|
||||
o = s.option(form.Value, 'priority', _('Priority'),
|
||||
_('Lower number indicates higher priority (e.g. 1 = Primary, 2 = Backup).'));
|
||||
o = s.option(form.Value, 'priority', _('Priority'));
|
||||
o.datatype = 'uinteger';
|
||||
o.default = '1';
|
||||
o.rmempty = false;
|
||||
var origRenderPrio = o.render;
|
||||
o.render = function(option_index, section_id, in_table) {
|
||||
if (in_table) {
|
||||
this.description = null;
|
||||
} else {
|
||||
this.description = _('Lower number indicates higher priority (e.g. 1 = Primary, 2 = Backup).');
|
||||
}
|
||||
return origRenderPrio.call(this, option_index, section_id, in_table);
|
||||
};
|
||||
o.validate = function(section_id, value) {
|
||||
if (value == null || value === '') {
|
||||
return t_priority_empty;
|
||||
|
||||
@@ -98,16 +98,16 @@ msgid "Logical interface from firewall WAN zone."
|
||||
msgstr "来自防火墙 WAN 区域的逻辑接口。"
|
||||
|
||||
msgid "Gateway Alias (Optional)"
|
||||
msgstr "网关别名 (可选)"
|
||||
msgstr "网关别名 (选填)"
|
||||
|
||||
msgid "Descriptive alias for this gateway (e.g. Bypass_GW, Main_Router). If empty, Gateway IP will be used."
|
||||
msgstr "该网关的自定义别名 (例如:旁路由主网关、光猫备用网关)。若留空则自动使用网关 IP。"
|
||||
msgid "Descriptive alias for this gateway (e.g. Primary_GW, Backup_GW). If empty, Gateway IP will be used."
|
||||
msgstr "该网关的自定义名称 (例如:主网关、备用网关)。若留空则自动使用网关 IP。"
|
||||
|
||||
msgid "Gateway IP"
|
||||
msgstr "网关 IP"
|
||||
|
||||
msgid "Next-hop IPv4 address of this gateway (e.g. 192.168.1.254)."
|
||||
msgstr "该网关的下一跳 IPv4 地址 (例如:192.168.1.254)。"
|
||||
msgid "Next-hop IPv4 address of this gateway (e.g. 192.168.1.1)."
|
||||
msgstr "该网关的下一跳 IPv4 地址 (例如:192.168.1.1)。"
|
||||
|
||||
msgid "Gateway IP is required."
|
||||
msgstr "网关 IP 不能为空。"
|
||||
@@ -346,9 +346,5 @@ msgstr "未选择"
|
||||
msgid "WAN Interface is required."
|
||||
msgstr "WAN 网络接口不能为空。"
|
||||
|
||||
msgid "已切换为单wan多网关模式。请确保监控目标列表中配置了有效的网关 IP。"
|
||||
msgstr "已切换为单wan多网关模式。请确保监控目标列表中配置了有效的网关 IP。"
|
||||
|
||||
msgid "已切换为多wan口模式。请确保监控目标列表中选择了正确的 WAN 接口。"
|
||||
msgstr "已切换为多wan口模式。请确保监控目标列表中选择了正确的 WAN 接口。"
|
||||
|
||||
@@ -7,7 +7,7 @@ PKG_BASE_VERSION?=0.21.2
|
||||
|
||||
# Rewritten automatically by .github/workflows/sync-upstream.yml; the LuCI
|
||||
# package always carries the same version as the core it drives.
|
||||
PKG_VERSION?=$(PKG_BASE_VERSION).202610010807
|
||||
PKG_VERSION?=$(PKG_BASE_VERSION).202610021113
|
||||
PKG_RELEASE?=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ PKG_BASE_VERSION?=0.21.2
|
||||
# All-numeric dotted segments keep the version valid for both package
|
||||
# managers: apk -> meow-0.21.2.202610010430-r1.apk
|
||||
# ipk -> meow_0.21.2.202610010430-1_<arch>.ipk
|
||||
PKG_VERSION?=$(PKG_BASE_VERSION).202610010807
|
||||
PKG_VERSION?=$(PKG_BASE_VERSION).202610021113
|
||||
PKG_RELEASE?=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
Reference in new issue
Block a user