mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-26 21:12:51 +08:00
💋 Sync 2026-09-20 23:11:08
This commit is contained in:
1 parent
8df28edee1
commit
ab8e741007
59 files changed
+3502
-928
No files matched your search
@@ -5,8 +5,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=BitSrunLoginGo
|
||||
PKG_VERSION:=1.6.7
|
||||
PKG_RELEASE:=6
|
||||
PKG_VERSION:=1.6.8
|
||||
PKG_RELEASE:=7
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/Mmx233/BitSrunLoginGo/tar.gz/v$(PKG_VERSION)?
|
||||
|
||||
+3
-3
@@ -5,10 +5,10 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=dae
|
||||
PKG_VERSION:=2026.09.12
|
||||
PKG_RELEASE:=44
|
||||
PKG_VERSION:=2026.09.18
|
||||
PKG_RELEASE:=45
|
||||
|
||||
PKG_SOURCE:=dae-src-2026.09.12-187058462a1f.tar.gz
|
||||
PKG_SOURCE:=dae-src-2026.09.18-dc29e60bab23.tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
PKG_HASH:=skip
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,24 +1,19 @@
|
||||
--- a/trace/kern/trace.c
|
||||
+++ b/trace/kern/trace.c
|
||||
@@ -228,7 +228,7 @@ KPROBE_SKB_AT(1)
|
||||
@@ -452,7 +452,6 @@ KPROBE_SKB_AT(1)
|
||||
KPROBE_SKB_AT(2)
|
||||
KPROBE_SKB_AT(3)
|
||||
KPROBE_SKB_AT(4)
|
||||
-KPROBE_SKB_AT(5)
|
||||
+//KPROBE_SKB_AT(5)
|
||||
|
||||
SEC("kprobe/skb_lifetime_termination")
|
||||
int kprobe_skb_lifetime_termination(struct pt_regs *ctx)
|
||||
--- a/trace/trace.go
|
||||
+++ b/trace/trace.go
|
||||
@@ -212,8 +212,8 @@ func attachBpfToTargets(objs *bpfObjects
|
||||
kp, err = link.Kprobe(fn, objs.KprobeSkb3, nil)
|
||||
case 4:
|
||||
kp, err = link.Kprobe(fn, objs.KprobeSkb4, nil)
|
||||
- case 5:
|
||||
- kp, err = link.Kprobe(fn, objs.KprobeSkb5, nil)
|
||||
+ //case 5:
|
||||
+ // kp, err = link.Kprobe(fn, objs.KprobeSkb5, nil)
|
||||
@@ -191,7 +191,6 @@ func attachBpfToTargets(objs *bpfObjects, targets map[string]int) (links []link.Link, err error) {
|
||||
1: objs.KprobeSkb1,
|
||||
2: objs.KprobeSkb2,
|
||||
3: objs.KprobeSkb3,
|
||||
4: objs.KprobeSkb4,
|
||||
- 5: objs.KprobeSkb5,
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to attach kprobe to %s: %+v\n", fn, err)
|
||||
+3
-3
@@ -5,10 +5,10 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=daed
|
||||
PKG_VERSION:=2026.09.12
|
||||
PKG_RELEASE:=55
|
||||
PKG_VERSION:=2026.09.18
|
||||
PKG_RELEASE:=56
|
||||
|
||||
PKG_SOURCE:=daed-src-2026.09.12-a0181f729855.tar.gz
|
||||
PKG_SOURCE:=daed-src-2026.09.18-0d4c9d0c95b9.tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
PKG_HASH:=skip
|
||||
|
||||
@@ -0,0 +1,837 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: kenzok8 <kenzok8@gmail.com>
|
||||
Date: Sun, 20 Sep 2026 16:59:23 +0800
|
||||
Subject: [PATCH] Adapt wing to new dae core
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
改动:适配新核心生命周期,并修复空配置停止和重载回滚。
|
||||
|
||||
原因:旧接口删除且新控制面交接流程已改变。
|
||||
---
|
||||
dae-core/config/marshal.go | 5 +
|
||||
dae/run.go | 402 +++++++++++++-----
|
||||
dae/run_handoff_test.go | 33 ++
|
||||
dae/runtime.go | 6 +-
|
||||
dae/utils.go | 9 +-
|
||||
db/node_utils.go | 3 +-
|
||||
graphql/service/config/mutation_utils.go | 9 +-
|
||||
graphql/service/node/latency_cache.go | 2 +-
|
||||
.../service/node/latency_mutation_utils.go | 59 ++-
|
||||
graphql/service/routing/resolver.go | 2 +-
|
||||
10 files changed, 415 insertions(+), 115 deletions(-)
|
||||
create mode 100644 dae/run_handoff_test.go
|
||||
|
||||
diff --git a/dae-core/config/marshal.go b/dae-core/config/marshal.go
|
||||
index f68006a..69a5325 100644
|
||||
--- a/dae-core/config/marshal.go
|
||||
+++ b/dae-core/config/marshal.go
|
||||
@@ -42,6 +42,11 @@ type Marshaller struct {
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
+// Bytes returns the serialized section buffer without exposing mutable state.
|
||||
+func (m *Marshaller) Bytes() []byte {
|
||||
+ return m.buf.Bytes()
|
||||
+}
|
||||
+
|
||||
func (m *Marshaller) writeLine(depth int, line string) {
|
||||
if depth < 0 {
|
||||
depth = 0
|
||||
diff --git a/dae/run.go b/dae/run.go
|
||||
index a0a665d..96b579c 100644
|
||||
--- a/dae/run.go
|
||||
+++ b/dae/run.go
|
||||
@@ -6,6 +6,8 @@
|
||||
package dae
|
||||
|
||||
import (
|
||||
+ "context"
|
||||
+ "errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
@@ -28,6 +30,22 @@ type ReloadMessage struct {
|
||||
Callback chan<- error
|
||||
}
|
||||
|
||||
+func closeReloadResources(closers ...func() error) error {
|
||||
+ var errs error
|
||||
+ for _, closeResource := range closers {
|
||||
+ if closeResource != nil {
|
||||
+ errs = errors.Join(errs, closeResource())
|
||||
+ }
|
||||
+ }
|
||||
+ return errs
|
||||
+}
|
||||
+
|
||||
+func notifyReloadCallback(callback chan<- error, err error) {
|
||||
+ if callback != nil {
|
||||
+ callback <- err
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
var ChReloadConfigs = make(chan *ReloadMessage)
|
||||
var GracefullyExit = make(chan struct{})
|
||||
var EmptyConfig *daeConfig.Config
|
||||
@@ -77,88 +95,115 @@ func Run(log *logrus.Logger, conf *daeConfig.Config, externGeoDataDirs []string,
|
||||
case nil:
|
||||
break dryLoop
|
||||
default:
|
||||
- newConf.Callback <- nil
|
||||
+ notifyReloadCallback(newConf.Callback, nil)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// New c.
|
||||
- c, err = newControlPlane(log, nil, nil, conf, externGeoDataDirs)
|
||||
+ processSessions := control.NewSessionManager(context.Background())
|
||||
+ defer func() { _ = processSessions.Close() }()
|
||||
+ c, err = newControlPlane(context.Background(), log, nil, nil, conf, externGeoDataDirs, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
+ if err = c.AttachSessionManager(processSessions); err != nil {
|
||||
+ _ = c.Close()
|
||||
+ return err
|
||||
+ }
|
||||
+ datapathDetached := false
|
||||
+ if conf == EmptyConfig {
|
||||
+ if err = c.DetachBpfHooks(); err != nil {
|
||||
+ _ = c.Close()
|
||||
+ return fmt.Errorf("detach initial empty datapath: %w", err)
|
||||
+ }
|
||||
+ datapathDetached = true
|
||||
+ }
|
||||
|
||||
- // Serve tproxy TCP/UDP server util signals.
|
||||
var listener *control.Listener
|
||||
+ if err = control.GetDaeNetns().With(func() error {
|
||||
+ var listenErr error
|
||||
+ listener, listenErr = c.Listen(conf.Global.TproxyPort)
|
||||
+ if listenErr != nil {
|
||||
+ return listenErr
|
||||
+ }
|
||||
+ return listener.ValidateCurrentNetns()
|
||||
+ }); err != nil {
|
||||
+ if listener != nil {
|
||||
+ _ = listener.Close()
|
||||
+ }
|
||||
+ _ = c.Close()
|
||||
+ return fmt.Errorf("listen in dae netns: %w", err)
|
||||
+ }
|
||||
+ readyChan := make(chan bool, 1)
|
||||
+ serveDone := make(chan error, 1)
|
||||
+ go func() { serveDone <- c.Serve(readyChan, listener) }()
|
||||
+ if !<-readyChan {
|
||||
+ serveErr := <-serveDone
|
||||
+ _ = closeReloadResources(listener.Close, c.Close)
|
||||
+ if serveErr == nil {
|
||||
+ serveErr = fmt.Errorf("initial control plane was not ready")
|
||||
+ }
|
||||
+ return serveErr
|
||||
+ }
|
||||
+ log.Infoln("Ready")
|
||||
go func() {
|
||||
- readyChan := make(chan bool, 1)
|
||||
- go func() {
|
||||
- <-readyChan
|
||||
- log.Infoln("Ready")
|
||||
- }()
|
||||
- control.GetDaeNetns().With(func() error {
|
||||
- if listener, err = c.ListenAndServe(readyChan, conf.Global.TproxyPort); err != nil {
|
||||
- log.Errorln("ListenAndServe:", err)
|
||||
- }
|
||||
- return err
|
||||
- })
|
||||
- // Exit
|
||||
+ if serveErr := <-serveDone; serveErr != nil {
|
||||
+ log.WithError(serveErr).Errorln("control plane serve stopped")
|
||||
+ }
|
||||
ChReloadConfigs <- nil
|
||||
}()
|
||||
reloading := false
|
||||
- /* dae-wing start */
|
||||
- var errReload error
|
||||
- var chCallback chan<- error
|
||||
- /* dae-wing end */
|
||||
loop:
|
||||
for newReloadMsg := range ChReloadConfigs {
|
||||
switch newReloadMsg {
|
||||
case nil:
|
||||
- /* dae-wing start */
|
||||
- // We will receive nil after control plane being Closed.
|
||||
- // We'll judge if we are in a reloading.
|
||||
- /* dae-wing end */
|
||||
-
|
||||
if reloading {
|
||||
- if listener == nil {
|
||||
- // Failed to listen. Exit.
|
||||
- break loop
|
||||
- }
|
||||
- // Serve.
|
||||
+ // The old generation's Serve loop reports its shutdown after the
|
||||
+ // candidate has already taken over the cloned listener.
|
||||
reloading = false
|
||||
- log.Warnln("[Reload] Serve")
|
||||
- readyChan := make(chan bool, 1)
|
||||
- go func() {
|
||||
- if err := c.Serve(readyChan, listener); err != nil {
|
||||
- log.Errorln("ListenAndServe:", err)
|
||||
- }
|
||||
- // Exit
|
||||
- ChReloadConfigs <- nil
|
||||
- }()
|
||||
- <-readyChan
|
||||
- log.Warnln("[Reload] Finished")
|
||||
- /* dae-wing start */
|
||||
- chCallback <- errReload
|
||||
- /* dae-wing end */
|
||||
+ continue
|
||||
} else {
|
||||
- // Listening error.
|
||||
break loop
|
||||
}
|
||||
default:
|
||||
// Reload signal.
|
||||
log.Warnln("[Reload] Received reload signal; prepare to reload")
|
||||
|
||||
- /* dae-wing start */
|
||||
newConf := newReloadMsg.Config
|
||||
- /* dae-wing end */
|
||||
-
|
||||
- // openwrt-daede: dashboard stop reloads to EmptyConfig; detach hooks.
|
||||
- if newConf == EmptyConfig {
|
||||
- if e := c.DetachBpfHooks(); e != nil {
|
||||
- log.Errorln("[Stop] DetachBpfHooks:", e)
|
||||
+ if datapathDetached && newConf == EmptyConfig {
|
||||
+ notifyReloadCallback(newReloadMsg.Callback, nil)
|
||||
+ continue
|
||||
+ }
|
||||
+ oldC := c
|
||||
+ wasStopped := datapathDetached
|
||||
+ failReload := func(cause, cleanupErr error) error {
|
||||
+ cause = errors.Join(cause, cleanupErr)
|
||||
+ if cleanupErr == nil && wasStopped {
|
||||
+ if detachErr := oldC.DetachBpfHooks(); detachErr != nil {
|
||||
+ cause = errors.Join(cause, fmt.Errorf("restore stopped datapath: %w", detachErr))
|
||||
+ cleanupErr = detachErr
|
||||
+ } else {
|
||||
+ datapathDetached = true
|
||||
+ }
|
||||
}
|
||||
+ notifyReloadCallback(newReloadMsg.Callback, cause)
|
||||
+ if cleanupErr != nil {
|
||||
+ return cause
|
||||
+ }
|
||||
+ return nil
|
||||
}
|
||||
+ if wasStopped {
|
||||
+ if restoreErr := oldC.RestoreDatapathForReloadRollback(); restoreErr != nil {
|
||||
+ if fatalErr := failReload(fmt.Errorf("prepare stopped datapath: %w", restoreErr), nil); fatalErr != nil {
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ continue
|
||||
+ }
|
||||
+ datapathDetached = false
|
||||
+ }
|
||||
+
|
||||
// New logger.
|
||||
oldLogOutput := log.Out
|
||||
log = logrus.New()
|
||||
@@ -167,65 +212,223 @@ loop:
|
||||
log.SetOutput(oldLogOutput) // FIXME: THIS IS A HACK.
|
||||
logrus.SetOutput(oldLogOutput)
|
||||
|
||||
- // New control plane.
|
||||
- obj := c.EjectBpf()
|
||||
var dnsCache map[string]*control.DnsCache
|
||||
if conf.Dns.IpVersionPrefer == newConf.Dns.IpVersionPrefer {
|
||||
// Only keep dns cache when ip version preference not change.
|
||||
dnsCache = c.CloneDnsCache()
|
||||
}
|
||||
- log.Warnln("[Reload] Load new control plane")
|
||||
- newC, err := newControlPlane(log, obj, dnsCache, newConf, externGeoDataDirs)
|
||||
+ log.Warnln("[Reload] Prepare new control plane")
|
||||
+ freshState, err := c.SnapshotFreshDatapathState()
|
||||
if err != nil {
|
||||
- /* dae-wing start */
|
||||
- errReload = err
|
||||
- /* dae-wing end */
|
||||
-
|
||||
- log.WithFields(logrus.Fields{
|
||||
- "err": err,
|
||||
- }).Errorln("[Reload] Failed to reload; try to roll back configuration")
|
||||
- // Load last config back.
|
||||
- newC, err = newControlPlane(log, obj, dnsCache, conf, externGeoDataDirs)
|
||||
- if err != nil {
|
||||
- obj.Close()
|
||||
- c.Close()
|
||||
- log.WithFields(logrus.Fields{
|
||||
- "err": err,
|
||||
- }).Fatalln("[Reload] Failed to roll back configuration")
|
||||
+ if fatalErr := failReload(err, nil); fatalErr != nil {
|
||||
+ return fatalErr
|
||||
}
|
||||
- newConf = conf
|
||||
- log.Errorln("[Reload] Last reload failed; rolled back configuration")
|
||||
- } else {
|
||||
- log.Warnln("[Reload] Stopped old control plane")
|
||||
-
|
||||
- /* dae-wing start */
|
||||
- errReload = nil
|
||||
- /* dae-wing end */
|
||||
+ continue
|
||||
}
|
||||
+ newC, err := newControlPlane(context.Background(), log, freshState, dnsCache, newConf, externGeoDataDirs, true)
|
||||
+ if err == nil {
|
||||
+ err = newC.AttachSessionManager(processSessions)
|
||||
+ }
|
||||
+ if err != nil {
|
||||
+ var cleanupErr error
|
||||
+ if newC != nil {
|
||||
+ cleanupErr = newC.Close()
|
||||
+ }
|
||||
+ if fatalErr := failReload(err, cleanupErr); fatalErr != nil {
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ continue
|
||||
+ }
|
||||
+ stagedListener, err := listener.Clone()
|
||||
+ if err != nil {
|
||||
+ if fatalErr := failReload(fmt.Errorf("clone listener: %w", err), newC.Close()); fatalErr != nil {
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ continue
|
||||
+ }
|
||||
+ if err = oldC.StopDNSListener(); err != nil {
|
||||
+ cleanupErr := closeReloadResources(stagedListener.Close, newC.Close, oldC.RestartDNSListener)
|
||||
+ if fatalErr := failReload(fmt.Errorf("stop old DNS listener: %w", err), cleanupErr); fatalErr != nil {
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ continue
|
||||
+ }
|
||||
+ dnsStopped := true
|
||||
+ readyChan := make(chan bool, 1)
|
||||
+ serveDone := make(chan error, 1)
|
||||
+ go func() { serveDone <- newC.Serve(readyChan, stagedListener) }()
|
||||
+ ready := <-readyChan
|
||||
+ if !ready {
|
||||
+ serveErr := <-serveDone
|
||||
+ if serveErr == nil {
|
||||
+ serveErr = fmt.Errorf("prepared control plane was not ready")
|
||||
+ }
|
||||
+ cleanupErr := closeReloadResources(
|
||||
+ stagedListener.Close,
|
||||
+ newC.Close,
|
||||
+ oldC.RestartDNSListener,
|
||||
+ )
|
||||
+ dnsStopped = false
|
||||
+ if fatalErr := failReload(serveErr, cleanupErr); fatalErr != nil {
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ continue
|
||||
+ }
|
||||
+ hookPrepared := false
|
||||
+ provisionalOwner := false
|
||||
+ hookFlipCommitted := false
|
||||
+ hookSetAdopted := false
|
||||
+ flowDatapathAdopted := false
|
||||
+ rollbackCleanupFailed := false
|
||||
+ rollbackCandidate := func(cause error) error {
|
||||
+ var rollbackErr error
|
||||
+ addRollbackErr := func(err error) {
|
||||
+ if err != nil {
|
||||
+ rollbackCleanupFailed = true
|
||||
+ rollbackErr = errors.Join(rollbackErr, err)
|
||||
+ }
|
||||
+ }
|
||||
+ if flowDatapathAdopted {
|
||||
+ addRollbackErr(oldC.AdoptProcessFlowDatapath(newC))
|
||||
+ }
|
||||
+ if hookSetAdopted {
|
||||
+ if ownerErr := newC.RestorePreparedTCHookSet(oldC); ownerErr != nil {
|
||||
+ rollbackCleanupFailed = true
|
||||
+ return errors.Join(cause, rollbackErr, fmt.Errorf("restore TC HookSet owner: %w", ownerErr))
|
||||
+ }
|
||||
+ }
|
||||
+ if hookFlipCommitted {
|
||||
+ addRollbackErr(newC.RollbackPreparedBpfHookFlip())
|
||||
+ }
|
||||
+ if hookPrepared && !hookSetAdopted && !hookFlipCommitted {
|
||||
+ addRollbackErr(newC.ClearPreparedTCHookHandoff())
|
||||
+ }
|
||||
+ if provisionalOwner {
|
||||
+ newC.UnregisterProvisionalRoutingEpochExecutionOwner()
|
||||
+ }
|
||||
+ addRollbackErr(newC.DetachBpfHooks())
|
||||
+ addRollbackErr(stagedListener.Close())
|
||||
+ addRollbackErr(newC.Close())
|
||||
+ if dnsStopped {
|
||||
+ addRollbackErr(oldC.RestartDNSListener())
|
||||
+ dnsStopped = false
|
||||
+ }
|
||||
+ return errors.Join(cause, rollbackErr)
|
||||
+ }
|
||||
+ if err = newC.PrepareTCHookHandoff(oldC); err != nil {
|
||||
+ rollbackErr := rollbackCandidate(err)
|
||||
+ if rollbackCleanupFailed {
|
||||
+ notifyReloadCallback(newReloadMsg.Callback, rollbackErr)
|
||||
+ return rollbackErr
|
||||
+ }
|
||||
+ if fatalErr := failReload(rollbackErr, nil); fatalErr != nil {
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ continue
|
||||
+ }
|
||||
+ hookPrepared = true
|
||||
+ if err = newC.RegisterProvisionalRoutingEpochExecutionOwner(); err != nil {
|
||||
+ rollbackErr := rollbackCandidate(err)
|
||||
+ if rollbackCleanupFailed {
|
||||
+ notifyReloadCallback(newReloadMsg.Callback, rollbackErr)
|
||||
+ return rollbackErr
|
||||
+ }
|
||||
+ if fatalErr := failReload(rollbackErr, nil); fatalErr != nil {
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ continue
|
||||
+ }
|
||||
+ provisionalOwner = true
|
||||
+ if err = newC.CommitPreparedBpfHookFlip(); err != nil {
|
||||
+ rollbackErr := rollbackCandidate(err)
|
||||
+ if rollbackCleanupFailed {
|
||||
+ notifyReloadCallback(newReloadMsg.Callback, rollbackErr)
|
||||
+ return rollbackErr
|
||||
+ }
|
||||
+ if fatalErr := failReload(rollbackErr, nil); fatalErr != nil {
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ continue
|
||||
+ }
|
||||
+ hookFlipCommitted = true
|
||||
+ if err = newC.AdoptPreparedTCHookSet(oldC); err != nil {
|
||||
+ rollbackErr := rollbackCandidate(err)
|
||||
+ if rollbackCleanupFailed {
|
||||
+ notifyReloadCallback(newReloadMsg.Callback, rollbackErr)
|
||||
+ return rollbackErr
|
||||
+ }
|
||||
+ if fatalErr := failReload(rollbackErr, nil); fatalErr != nil {
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ continue
|
||||
+ }
|
||||
+ hookSetAdopted = true
|
||||
+ if err = newC.AdoptProcessFlowDatapath(oldC); err != nil {
|
||||
+ rollbackErr := rollbackCandidate(err)
|
||||
+ if rollbackCleanupFailed {
|
||||
+ notifyReloadCallback(newReloadMsg.Callback, rollbackErr)
|
||||
+ return rollbackErr
|
||||
+ }
|
||||
+ if fatalErr := failReload(rollbackErr, nil); fatalErr != nil {
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ continue
|
||||
+ }
|
||||
+ flowDatapathAdopted = true
|
||||
+ newC.UnregisterProvisionalRoutingEpochExecutionOwner()
|
||||
+ provisionalOwner = false
|
||||
+ if err = newC.FinalizePreparedTCHooks(); err != nil {
|
||||
+ fatalErr := errors.Join(fmt.Errorf("finalize prepared TC hooks: %w", err), closeReloadResources(
|
||||
+ listener.Close,
|
||||
+ stagedListener.Close,
|
||||
+ newC.Close,
|
||||
+ oldC.Close,
|
||||
+ ))
|
||||
+ notifyReloadCallback(newReloadMsg.Callback, fatalErr)
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ hookPrepared = false
|
||||
+ hookFlipCommitted = false
|
||||
+ hookSetAdopted = false
|
||||
|
||||
- // Inject bpf objects into the new control plane life-cycle.
|
||||
- newC.InjectBpf(obj)
|
||||
-
|
||||
- // Prepare new context.
|
||||
- oldC := c
|
||||
+ oldListener := listener
|
||||
c = newC
|
||||
conf = newConf
|
||||
+ listener = stagedListener
|
||||
+ datapathDetached = false
|
||||
+ retireErr := closeReloadResources(oldListener.Close, oldC.Close)
|
||||
+ if newConf == EmptyConfig {
|
||||
+ if retireErr != nil {
|
||||
+ fatalErr := errors.Join(fmt.Errorf("retire previous control plane: %w", retireErr), closeReloadResources(stagedListener.Close, newC.Close))
|
||||
+ notifyReloadCallback(newReloadMsg.Callback, fatalErr)
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ if detachErr := newC.DetachBpfHooks(); detachErr != nil {
|
||||
+ fatalErr := errors.Join(fmt.Errorf("detach stopped datapath: %w", detachErr), closeReloadResources(stagedListener.Close, newC.Close))
|
||||
+ notifyReloadCallback(newReloadMsg.Callback, fatalErr)
|
||||
+ return fatalErr
|
||||
+ }
|
||||
+ datapathDetached = true
|
||||
+ } else if retireErr != nil {
|
||||
+ log.WithError(retireErr).Warnln("[Reload] Failed to close old control plane")
|
||||
+ }
|
||||
reloading = true
|
||||
- /* dae-wing start */
|
||||
- chCallback = newReloadMsg.Callback
|
||||
- /* dae-wing end */
|
||||
-
|
||||
- // Ready to close.
|
||||
- oldC.Close()
|
||||
+ notifyReloadCallback(newReloadMsg.Callback, nil)
|
||||
+ go func(done <-chan error) {
|
||||
+ if serveErr := <-done; serveErr != nil {
|
||||
+ log.WithError(serveErr).Errorln("control plane serve stopped")
|
||||
+ }
|
||||
+ ChReloadConfigs <- nil
|
||||
+ }(serveDone)
|
||||
}
|
||||
}
|
||||
- if e := c.Close(); e != nil {
|
||||
+ if e := closeReloadResources(listener.Close, c.Close); e != nil {
|
||||
return fmt.Errorf("close control plane: %w", e)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
-func newControlPlane(log *logrus.Logger, bpf interface{}, dnsCache map[string]*control.DnsCache, conf *daeConfig.Config, externGeoDataDirs []string) (c *control.ControlPlane, err error) {
|
||||
+func newControlPlane(ctx context.Context, log *logrus.Logger, bpf interface{}, dnsCache map[string]*control.DnsCache, conf *daeConfig.Config, externGeoDataDirs []string, prepared bool) (c *control.ControlPlane, err error) {
|
||||
|
||||
// Print configuration.
|
||||
if log.IsLevelEnabled(logrus.DebugLevel) {
|
||||
@@ -239,10 +442,6 @@ func newControlPlane(log *logrus.Logger, bpf interface{}, dnsCache map[string]*c
|
||||
control.PurgeStaleTCFilters(log)
|
||||
}
|
||||
|
||||
- // Init Direct Dialers.
|
||||
- direct.InitDirectDialers(conf.Global.FallbackResolver)
|
||||
- netutils.FallbackDns = netip.MustParseAddrPort(conf.Global.FallbackResolver)
|
||||
-
|
||||
if !conf.Global.DisableWaitingNetwork && len(conf.Global.WanInterface) > 0 {
|
||||
// Wait for network for WAN ready.
|
||||
if err = waitForNetwork(log); err != nil {
|
||||
@@ -266,7 +465,10 @@ func newControlPlane(log *logrus.Logger, bpf interface{}, dnsCache map[string]*c
|
||||
}
|
||||
|
||||
// New dae control plane.
|
||||
- c, err = control.NewControlPlane(
|
||||
+ directDialers := direct.NewDirectDialers(conf.Global.FallbackResolver)
|
||||
+ systemDNSResolver := netutils.NewSystemDNSResolver(netip.MustParseAddrPort(conf.Global.FallbackResolver))
|
||||
+ c, err = control.NewControlPlaneWithContextOptions(
|
||||
+ ctx,
|
||||
log,
|
||||
bpf,
|
||||
dnsCache,
|
||||
@@ -276,6 +478,14 @@ func newControlPlane(log *logrus.Logger, bpf interface{}, dnsCache map[string]*c
|
||||
&conf.Global,
|
||||
&conf.Dns,
|
||||
externGeoDataDirs,
|
||||
+ control.ControlPlaneBuildOptions{
|
||||
+ DelayDatapathCommit: prepared,
|
||||
+ DelayDNSListenerStart: prepared,
|
||||
+ IsReload: prepared || bpf != nil,
|
||||
+ DirectDialer: directDialers.Symmetric,
|
||||
+ FullconeDirectDialer: directDialers.Fullcone,
|
||||
+ SystemDNSResolver: systemDNSResolver,
|
||||
+ },
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
diff --git a/dae/run_handoff_test.go b/dae/run_handoff_test.go
|
||||
new file mode 100644
|
||||
index 0000000..0ffb2ec
|
||||
--- /dev/null
|
||||
+++ b/dae/run_handoff_test.go
|
||||
@@ -0,0 +1,33 @@
|
||||
+package dae
|
||||
+
|
||||
+import (
|
||||
+ "errors"
|
||||
+ "reflect"
|
||||
+ "testing"
|
||||
+)
|
||||
+
|
||||
+func TestCloseReloadResourcesOrder(t *testing.T) {
|
||||
+ var got []string
|
||||
+ err := closeReloadResources(
|
||||
+ func() error { got = append(got, "old-listener"); return nil },
|
||||
+ func() error { got = append(got, "staged-listener"); return nil },
|
||||
+ func() error { got = append(got, "new-control-plane"); return nil },
|
||||
+ func() error { got = append(got, "old-control-plane"); return nil },
|
||||
+ )
|
||||
+ if err != nil {
|
||||
+ t.Fatalf("closeReloadResources() error = %v", err)
|
||||
+ }
|
||||
+ want := []string{"old-listener", "staged-listener", "new-control-plane", "old-control-plane"}
|
||||
+ if !reflect.DeepEqual(got, want) {
|
||||
+ t.Fatalf("close order = %v, want %v", got, want)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+func TestCloseReloadResourcesJoinsErrors(t *testing.T) {
|
||||
+ one := errors.New("old listener")
|
||||
+ two := errors.New("old control plane")
|
||||
+ err := closeReloadResources(func() error { return one }, func() error { return two })
|
||||
+ if !errors.Is(err, one) || !errors.Is(err, two) {
|
||||
+ t.Fatalf("joined error = %v, want both close errors", err)
|
||||
+ }
|
||||
+}
|
||||
diff --git a/dae/runtime.go b/dae/runtime.go
|
||||
index 2ab0d68..af46089 100644
|
||||
--- a/dae/runtime.go
|
||||
+++ b/dae/runtime.go
|
||||
@@ -30,18 +30,16 @@ type RuntimeOverview struct {
|
||||
}
|
||||
|
||||
func GetRuntimeOverview(windowSec int, maxPoints int) (*RuntimeOverview, error) {
|
||||
- activeTCPConnections := 0
|
||||
ctl, err := ControlPlane()
|
||||
+ var snapshot control.RuntimeStatsSnapshot
|
||||
if err != nil {
|
||||
if !errors.Is(err, ErrControlPlaneNotInit) {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
- activeTCPConnections = ctl.ActiveTCPConnections()
|
||||
+ snapshot = ctl.SnapshotRuntimeStats(windowSec, maxPoints)
|
||||
}
|
||||
|
||||
- snapshot := control.SnapshotRuntimeStats(activeTCPConnections, control.DefaultUdpEndpointPool.Count(), windowSec, maxPoints)
|
||||
-
|
||||
samples := make([]RuntimeTrafficSample, 0, len(snapshot.Samples))
|
||||
for _, sample := range snapshot.Samples {
|
||||
samples = append(samples, RuntimeTrafficSample{
|
||||
diff --git a/dae/utils.go b/dae/utils.go
|
||||
index dc43cbc..ca61c85 100644
|
||||
--- a/dae/utils.go
|
||||
+++ b/dae/utils.go
|
||||
@@ -32,8 +32,11 @@ var (
|
||||
EmptyGlobalSection = `global {}`
|
||||
)
|
||||
|
||||
-func NecessaryOutbounds(routing *daeConfig.Routing) (outbounds []string) {
|
||||
- f := daeConfig.FunctionOrStringToFunction(routing.Fallback)
|
||||
+func NecessaryOutbounds(routing *daeConfig.Routing) (outbounds []string, err error) {
|
||||
+ f, err := daeConfig.ParseFunctionOrString(routing.Fallback)
|
||||
+ if err != nil {
|
||||
+ return nil, err
|
||||
+ }
|
||||
outbounds = append(outbounds, f.Name)
|
||||
for _, r := range routing.Rules {
|
||||
outbound := r.Outbound.Name
|
||||
@@ -42,7 +45,7 @@ func NecessaryOutbounds(routing *daeConfig.Routing) (outbounds []string) {
|
||||
}
|
||||
outbounds = append(outbounds, outbound)
|
||||
}
|
||||
- return daeCommon.Deduplicate(outbounds)
|
||||
+ return daeCommon.Deduplicate(outbounds), nil
|
||||
}
|
||||
|
||||
func ParseConfig(globalSection *string, dnsSection *string, routingSection *string) (*daeConfig.Config, error) {
|
||||
diff --git a/db/node_utils.go b/db/node_utils.go
|
||||
index b7b43b8..7ca8fc3 100644
|
||||
--- a/db/node_utils.go
|
||||
+++ b/db/node_utils.go
|
||||
@@ -6,6 +6,7 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
+ "context"
|
||||
"strings"
|
||||
|
||||
"github.com/daeuniverse/dae-wing/common"
|
||||
@@ -25,7 +26,7 @@ func NewNodeModel(link string, tag *string, subscriptionId *uint) (*Node, error)
|
||||
}
|
||||
_tag = *tag
|
||||
}
|
||||
- d, err := dialer.NewFromLink(&dialer.GlobalOption{
|
||||
+ d, err := dialer.NewFromLinkContext(context.Background(), &dialer.GlobalOption{
|
||||
Log: logrus.StandardLogger(),
|
||||
}, dialer.InstanceOption{DisableCheck: false}, link, _tag)
|
||||
if err != nil {
|
||||
diff --git a/graphql/service/config/mutation_utils.go b/graphql/service/config/mutation_utils.go
|
||||
index 59f0ebd..c7d6b78 100644
|
||||
--- a/graphql/service/config/mutation_utils.go
|
||||
+++ b/graphql/service/config/mutation_utils.go
|
||||
@@ -298,7 +298,7 @@ func ApplyIfRunning(ctx context.Context) error {
|
||||
func runLocked(ctx context.Context, noLoad bool) (n int32, err error) {
|
||||
//// Dry run.
|
||||
if noLoad {
|
||||
- ch := make(chan error)
|
||||
+ ch := make(chan error, 1)
|
||||
reloadMsg := &dae.ReloadMessage{
|
||||
Config: dae.EmptyConfig,
|
||||
Callback: ch,
|
||||
@@ -384,7 +384,10 @@ func runLocked(ctx context.Context, noLoad bool) (n int32, err error) {
|
||||
}
|
||||
/// Fill in necessary groups and nodes.
|
||||
// Find groups needed by routing.
|
||||
- outbounds := dae.NecessaryOutbounds(&c.Routing)
|
||||
+ outbounds, err := dae.NecessaryOutbounds(&c.Routing)
|
||||
+ if err != nil {
|
||||
+ return 0, err
|
||||
+ }
|
||||
var groups []db.Group
|
||||
q = d.Model(&db.Group{}).
|
||||
Where("name in ?", outbounds).
|
||||
@@ -523,7 +526,7 @@ func runLocked(ctx context.Context, noLoad bool) (n int32, err error) {
|
||||
readTx = nil
|
||||
|
||||
/// Reload with current config.
|
||||
- chReloadCallback := make(chan error)
|
||||
+ chReloadCallback := make(chan error, 1)
|
||||
reloadMsg := &dae.ReloadMessage{
|
||||
Config: c,
|
||||
Callback: chReloadCallback,
|
||||
diff --git a/graphql/service/node/latency_cache.go b/graphql/service/node/latency_cache.go
|
||||
index ba66794..e0ec91e 100644
|
||||
--- a/graphql/service/node/latency_cache.go
|
||||
+++ b/graphql/service/node/latency_cache.go
|
||||
@@ -162,7 +162,7 @@ func refreshLatencyCache(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
- results := testLatencyResultsForNodes(option, nodes)
|
||||
+ results := testLatencyResultsForNodes(ctx, option, nodes)
|
||||
storeLatencyResults(results)
|
||||
|
||||
if ctl, err := dae.ControlPlane(); err == nil {
|
||||
diff --git a/graphql/service/node/latency_mutation_utils.go b/graphql/service/node/latency_mutation_utils.go
|
||||
index 056f9fa..5026b33 100644
|
||||
--- a/graphql/service/node/latency_mutation_utils.go
|
||||
+++ b/graphql/service/node/latency_mutation_utils.go
|
||||
@@ -8,6 +8,8 @@ package node
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
+ "net/netip"
|
||||
+ "strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -15,6 +17,7 @@ import (
|
||||
"github.com/daeuniverse/dae-wing/dae"
|
||||
"github.com/daeuniverse/dae-wing/db"
|
||||
dialer "github.com/daeuniverse/dae/component/outbound/dialer"
|
||||
+ "github.com/daeuniverse/outbound/netproxy"
|
||||
"github.com/graph-gophers/graphql-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -32,12 +35,12 @@ func TestLatencies(ctx context.Context, ids *[]graphql.ID) ([]*LatencyResolver,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
- results := testLatencyResultsForNodes(option, nodes)
|
||||
+ results := testLatencyResultsForNodes(ctx, option, nodes)
|
||||
storeLatencyResults(results)
|
||||
return results, nil
|
||||
}
|
||||
|
||||
-func testLatencyResultsForNodes(option *dialer.GlobalOption, nodes []db.Node) []*LatencyResolver {
|
||||
+func testLatencyResultsForNodes(ctx context.Context, option *dialer.GlobalOption, nodes []db.Node) []*LatencyResolver {
|
||||
results := make([]*LatencyResolver, len(nodes))
|
||||
sem := make(chan struct{}, latencyProbeConcurrency)
|
||||
var wg sync.WaitGroup
|
||||
@@ -51,7 +54,7 @@ func testLatencyResultsForNodes(option *dialer.GlobalOption, nodes []db.Node) []
|
||||
defer func() { <-sem }()
|
||||
|
||||
node := nodes[index]
|
||||
- results[index] = testSingleNodeLatency(option, &node)
|
||||
+ results[index] = testSingleNodeLatency(ctx, option, &node)
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -93,14 +96,14 @@ func latencyProbeNodes(ctx context.Context, ids *[]graphql.ID) ([]db.Node, error
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
-func testSingleNodeLatency(option *dialer.GlobalOption, node *db.Node) *LatencyResolver {
|
||||
+func testSingleNodeLatency(ctx context.Context, option *dialer.GlobalOption, node *db.Node) *LatencyResolver {
|
||||
resolver := &LatencyResolver{
|
||||
NodeID: node.ID,
|
||||
AliveVal: false,
|
||||
TestedAtV: time.Now(),
|
||||
}
|
||||
|
||||
- d, err := dialer.NewFromLink(option, dialer.InstanceOption{DisableCheck: false}, node.Link, "")
|
||||
+ d, err := dialer.NewFromLinkContext(ctx, option, dialer.InstanceOption{DisableCheck: false}, node.Link, "")
|
||||
if err != nil {
|
||||
msg := err.Error()
|
||||
resolver.MessageV = &msg
|
||||
@@ -108,7 +111,7 @@ func testSingleNodeLatency(option *dialer.GlobalOption, node *db.Node) *LatencyR
|
||||
}
|
||||
defer d.Close()
|
||||
|
||||
- result, err := d.ProbeLatencyFast()
|
||||
+ result, err := probeLatencyFast(ctx, d)
|
||||
if err != nil {
|
||||
msg := err.Error()
|
||||
resolver.MessageV = &msg
|
||||
@@ -128,3 +131,47 @@ func testSingleNodeLatency(option *dialer.GlobalOption, node *db.Node) *LatencyR
|
||||
}
|
||||
return resolver
|
||||
}
|
||||
+
|
||||
+func probeLatencyFast(ctx context.Context, d *dialer.Dialer) (*dialer.LatencyProbeResult, error) {
|
||||
+ opt, err := d.TcpCheckOptionRaw.Option()
|
||||
+ if err != nil {
|
||||
+ return nil, err
|
||||
+ }
|
||||
+ network, err := netproxy.ParseMagicNetwork(d.TcpCheckOptionRaw.ResolverNetwork)
|
||||
+ if err != nil {
|
||||
+ return nil, err
|
||||
+ }
|
||||
+ result := &dialer.LatencyProbeResult{CheckedAt: time.Now()}
|
||||
+ var messages []string
|
||||
+ for _, probe := range []struct {
|
||||
+ index int
|
||||
+ ip netip.Addr
|
||||
+ }{
|
||||
+ {index: dialer.IdxTcp4, ip: opt.Ip4},
|
||||
+ {index: dialer.IdxTcp6, ip: opt.Ip6},
|
||||
+ } {
|
||||
+ if !probe.ip.IsValid() {
|
||||
+ continue
|
||||
+ }
|
||||
+ probeCtx, cancel := context.WithTimeout(ctx, 1500*time.Millisecond)
|
||||
+ started := time.Now()
|
||||
+ ok, probeErr := d.HttpCheck(probeCtx, probe.index, opt.Url, probe.ip, opt.Method, network.Mark, network.Mptcp)
|
||||
+ cancel()
|
||||
+ if ok {
|
||||
+ latency := time.Since(started)
|
||||
+ if !result.Alive || latency < result.Latency {
|
||||
+ result.Latency = latency
|
||||
+ }
|
||||
+ result.Alive = true
|
||||
+ } else if probeErr != nil {
|
||||
+ messages = append(messages, probeErr.Error())
|
||||
+ }
|
||||
+ }
|
||||
+ if !result.Alive {
|
||||
+ result.Message = strings.Join(messages, "; ")
|
||||
+ if result.Message == "" {
|
||||
+ result.Message = "no latency result"
|
||||
+ }
|
||||
+ }
|
||||
+ return result, nil
|
||||
+}
|
||||
diff --git a/graphql/service/routing/resolver.go b/graphql/service/routing/resolver.go
|
||||
index 7822d64..3ed61aa 100644
|
||||
--- a/graphql/service/routing/resolver.go
|
||||
+++ b/graphql/service/routing/resolver.go
|
||||
@@ -41,7 +41,7 @@ func (r *Resolver) Selected() bool {
|
||||
return r.Model.Selected
|
||||
}
|
||||
|
||||
-func (r *Resolver) ReferenceGroups() (outbounds []string) {
|
||||
+func (r *Resolver) ReferenceGroups() (outbounds []string, err error) {
|
||||
return dae.NecessaryOutbounds(r.DaeRouting)
|
||||
}
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
--- a/dae-core/trace/kern/trace.c
|
||||
+++ b/dae-core/trace/kern/trace.c
|
||||
@@ -228,7 +228,7 @@ KPROBE_SKB_AT(1)
|
||||
@@ -452,7 +452,6 @@ KPROBE_SKB_AT(1)
|
||||
KPROBE_SKB_AT(2)
|
||||
KPROBE_SKB_AT(3)
|
||||
KPROBE_SKB_AT(4)
|
||||
-KPROBE_SKB_AT(5)
|
||||
+//KPROBE_SKB_AT(5)
|
||||
|
||||
SEC("kprobe/skb_lifetime_termination")
|
||||
int kprobe_skb_lifetime_termination(struct pt_regs *ctx)
|
||||
--- a/dae-core/trace/trace.go
|
||||
+++ b/dae-core/trace/trace.go
|
||||
@@ -206,8 +206,8 @@ func attachBpfToTargets(objs *bpfObjects
|
||||
kp, err = link.Kprobe(fn, objs.KprobeSkb3, nil)
|
||||
case 4:
|
||||
kp, err = link.Kprobe(fn, objs.KprobeSkb4, nil)
|
||||
- case 5:
|
||||
- kp, err = link.Kprobe(fn, objs.KprobeSkb5, nil)
|
||||
+ //case 5:
|
||||
+ // kp, err = link.Kprobe(fn, objs.KprobeSkb5, nil)
|
||||
@@ -191,7 +191,6 @@ func attachBpfToTargets(objs *bpfObjects, targets map[string]int) (links []link.Link, err error) {
|
||||
1: objs.KprobeSkb1,
|
||||
2: objs.KprobeSkb2,
|
||||
3: objs.KprobeSkb3,
|
||||
4: objs.KprobeSkb4,
|
||||
- 5: objs.KprobeSkb5,
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Debugf("failed to attach kprobe to %s: %+v\n", fn, err)
|
||||
+2
-2
@@ -6,8 +6,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=gowebdav
|
||||
PKG_VERSION:=5.15.1
|
||||
PKG_RELEASE:=21
|
||||
PKG_VERSION:=5.16.0
|
||||
PKG_RELEASE:=22
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/hacdias/webdav/tar.gz/v$(PKG_VERSION)?
|
||||
|
||||
@@ -12,7 +12,7 @@ LUCI_DEPENDS:=+luci-base +logd +jsonfilter
|
||||
LUCI_PKGARCH:=all
|
||||
|
||||
PKG_VERSION:=0.1.44
|
||||
PKG_RELEASE:=26
|
||||
PKG_RELEASE:=27
|
||||
|
||||
# Reproducible build: honor SOURCE_DATE_EPOCH from the build environment (set by docker-sdk.sh / CI).
|
||||
ifdef SOURCE_DATE_EPOCH
|
||||
@@ -22,12 +22,12 @@ endif
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
PKG_MAINTAINER:=Lucas Albers <lucas.b.albers@gmail.com>
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
define Package/luci-app-fwlive/prerm
|
||||
#!/bin/sh
|
||||
[ -n "$${IPKG_INSTROOT}" ] && exit 0
|
||||
[ "$$1" = "remove" ] || exit 0
|
||||
case "$${IPKG_INSTROOT}" in
|
||||
''|/) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
. /usr/libexec/fwlive-logging.sh
|
||||
# Best-effort restore: always exit 0 so a transient lock/pending-staging
|
||||
# failure cannot strand the package half-removed. Baseline is kept on
|
||||
@@ -37,4 +37,6 @@ restore_wan_log_baseline || logger -t fwlive "WAN log baseline restore failed du
|
||||
exit 0
|
||||
endef
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
@@ -38,7 +38,7 @@ No `luasrc/` — modern JS-only app.
|
||||
- Optional reverse DNS uses BusyBox `nslookup` (stock image; not a package depend)
|
||||
- No hard `firewall4` dependency
|
||||
- Menu depends on ACL only (no `fs` AND of `nft`+`iptables` — that hid the entry on stock fw3 and fw4)
|
||||
- Runtime backend detection selects **fw4/nft** (22.03+) or **iptables LOG** (21.02 fw3); best-effort iptables when nft absent
|
||||
- Runtime backend detection selects **fw4/nft** on supported **23.05+** images; log lines tagged **iptables** still classify. OpenWrt **21.02** / **22.03** are unsupported
|
||||
|
||||
## Maintenance
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
* Modules must not mutate state. host is cleared then rebuilt (idempotent replace).
|
||||
*/
|
||||
|
||||
function chipValueNodes(field, val) {
|
||||
function chipValueNodes(spec, val) {
|
||||
const key = spec.key;
|
||||
const label = spec.label || key;
|
||||
const p = log.parseFilterValue(val);
|
||||
if (!p.value) return [''];
|
||||
|
||||
@@ -26,19 +28,19 @@ function chipValueNodes(field, val) {
|
||||
return [
|
||||
E('span', { 'class': 'fwlive-chip-polarity' }, [_('is')]),
|
||||
' ',
|
||||
log.formatFilterChipLabel(field, val)
|
||||
log.formatFilterChipLabel(label, val)
|
||||
];
|
||||
}
|
||||
|
||||
if (field === 'q' || field === 'src' || field === 'dst')
|
||||
if (key === 'q' || key === 'src' || key === 'dst')
|
||||
return [
|
||||
field + ': ',
|
||||
label + ': ',
|
||||
E('strong', { 'class': 'fwlive-chip-not' }, [_('not')]),
|
||||
' contains ',
|
||||
valueNode
|
||||
];
|
||||
|
||||
return [field + ': ', E('strong', { 'class': 'fwlive-chip-not' }, [_('not')]), ' ', valueNode];
|
||||
return [label + ': ', E('strong', { 'class': 'fwlive-chip-not' }, [_('not')]), ' ', valueNode];
|
||||
}
|
||||
|
||||
function chipLeadingSym(negated) {
|
||||
@@ -69,7 +71,7 @@ function renderFilterChips(host, state, callbacks) {
|
||||
const lead = chipLeadingSym(negated);
|
||||
if (lead) kids.push(lead);
|
||||
|
||||
kids.push(E('span', { 'class': 'fwlive-chip-label' }, chipValueNodes(spec.label, val)));
|
||||
kids.push(E('span', { 'class': 'fwlive-chip-label' }, chipValueNodes(spec, val)));
|
||||
kids.push(
|
||||
E(
|
||||
'span',
|
||||
|
||||
@@ -93,28 +93,19 @@ function addrFilterLink(field, ip, showHostnames, hostnameCache, onFilterClick)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} hint - rule hint token
|
||||
* @param {string} firewallBackend - 'nft' or 'iptables'
|
||||
*/
|
||||
function ruleAdminPath(hint, firewallBackend) {
|
||||
if (hint === 'fw4') return 'admin/network/firewall/rules';
|
||||
|
||||
if (firewallBackend === 'iptables') return 'admin/status/iptables';
|
||||
|
||||
return 'admin/status/nftables';
|
||||
function ruleAdminPath() {
|
||||
return 'admin/network/firewall/rules';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} hint - rule hint token
|
||||
* @param {string} label - display label
|
||||
* @param {string} firewallBackend - 'nft' or 'iptables'
|
||||
* @param {function} onFilterClick - callback(field, value, ev)
|
||||
*/
|
||||
function ruleAdminLink(hint, label, firewallBackend, onFilterClick) {
|
||||
function ruleAdminLink(hint, label, onFilterClick) {
|
||||
if (!hint) return log.formatCell(hint);
|
||||
|
||||
const path = ruleAdminPath(hint, firewallBackend);
|
||||
const path = ruleAdminPath();
|
||||
const url = '%s#%s'.format(luciUrl(path), encodeURIComponent(hint));
|
||||
const text = label || hint;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*
|
||||
* renderManualTestNodes(host, state, callbacks) → void
|
||||
* host - <ul> element inside #fwlive-help (cleared and rebuilt)
|
||||
* state - { firewallBackend }
|
||||
* state - unused; nft-only instruction
|
||||
*
|
||||
* Empty-state helpers:
|
||||
* buildEmptyStateNodes(state, callbacks) → Node[]
|
||||
@@ -330,21 +330,12 @@ function renderEmptyState(host, state, callbacks) {
|
||||
}
|
||||
|
||||
/**
|
||||
* renderManualTestNodes — fills a <li> host element with the backend-specific
|
||||
* manual test instruction. Call from addFooter() after render() has inserted
|
||||
* renderManualTestNodes — fills a <li> host element with the nft manual test
|
||||
* instruction. Call from addFooter() after render() has inserted
|
||||
* the placeholder <li id="fwlive-manual-test">.
|
||||
*/
|
||||
function renderManualTestNodes(host, state, _callbacks) {
|
||||
function renderManualTestNodes(host, _state, _callbacks) {
|
||||
host.innerHTML = '';
|
||||
if (state.firewallBackend === 'iptables') {
|
||||
host.appendChild(document.createTextNode(_('Manual test (System → Terminal): ')));
|
||||
host.appendChild(
|
||||
E('code', {}, [
|
||||
'iptables -I INPUT -p icmp --icmp-type echo-request -j LOG --log-prefix "fwlive-ping: "'
|
||||
])
|
||||
);
|
||||
host.appendChild(document.createTextNode(_(' then ping the router.')));
|
||||
} else {
|
||||
host.appendChild(document.createTextNode(_('Manual test (System → Terminal): ')));
|
||||
host.appendChild(
|
||||
E('code', {}, [
|
||||
@@ -353,7 +344,6 @@ function renderManualTestNodes(host, state, _callbacks) {
|
||||
);
|
||||
host.appendChild(document.createTextNode(_(' then ping the router.')));
|
||||
}
|
||||
}
|
||||
|
||||
return baseclass.extend({
|
||||
CONSENT_STORAGE_KEY: CONSENT_STORAGE_KEY,
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* host - <tbody> element (kept; normal polls reuse keyed rows)
|
||||
* state - shallow copy: { rows, columns, viewMode, messageLayout,
|
||||
* expandedRowId, rowTint, showHostnames,
|
||||
* hostnameCache, firewallBackend }
|
||||
* hostnameCache }
|
||||
* callbacks - { onRowClick(rowId, ev), onFilterClick(field, value, ev),
|
||||
* actionRowTintClass(action) }
|
||||
*
|
||||
@@ -147,12 +147,7 @@ function buildColumnCell(col, row, state, callbacks) {
|
||||
return E('td', { 'class': log.actionRowClass(row.action) }, [actionCell]);
|
||||
case 'rule':
|
||||
return E('td', { 'class': columnCellClass(col) }, [
|
||||
links.ruleAdminLink(
|
||||
row.rule_hint,
|
||||
row.rule_label,
|
||||
state.firewallBackend,
|
||||
onFilterClick
|
||||
)
|
||||
links.ruleAdminLink(row.rule_hint, row.rule_label, onFilterClick)
|
||||
]);
|
||||
case 'iface':
|
||||
return E('td', { 'class': columnCellClass(col) }, [
|
||||
@@ -279,8 +274,7 @@ function rowRenderKey(row, state, columns) {
|
||||
state.messageLayout,
|
||||
state.expandedRowId === row.id,
|
||||
!!state.rowTint,
|
||||
!!state.showHostnames,
|
||||
state.firewallBackend
|
||||
!!state.showHostnames
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -635,7 +635,6 @@ return view.extend({
|
||||
},
|
||||
|
||||
backendDisplayLabel() {
|
||||
if (this.firewallBackend === 'iptables') return _('using iptables');
|
||||
if (this.firewallBackend === 'nft') return _('using fw4');
|
||||
return '';
|
||||
},
|
||||
@@ -664,6 +663,13 @@ return view.extend({
|
||||
text = text ? text + ' \u00b7 ' + warn : warn;
|
||||
degraded = true;
|
||||
}
|
||||
if (warnings.indexOf('legacy_iptables_detected') >= 0) {
|
||||
const warn = _(
|
||||
'Live view may be incomplete — a legacy iptables table is registered'
|
||||
);
|
||||
text = text ? text + ' \u00b7 ' + warn : warn;
|
||||
degraded = true;
|
||||
}
|
||||
label.textContent = text;
|
||||
label.classList.toggle('fwlive-backend-warn', degraded);
|
||||
}
|
||||
@@ -1847,8 +1853,7 @@ return view.extend({
|
||||
expandedRowId: this.expandedRowId,
|
||||
rowTint: this.rowTintEnabled(),
|
||||
showHostnames: !!this.showHostnames,
|
||||
hostnameCache: this.hostnameCache,
|
||||
firewallBackend: this.firewallBackend
|
||||
hostnameCache: this.hostnameCache
|
||||
},
|
||||
{
|
||||
onRowClick: (rowId, ev) => this.onRowClick(rowId, ev),
|
||||
@@ -2471,8 +2476,7 @@ return view.extend({
|
||||
this.updateTintWarnUi();
|
||||
this.renderRows(true);
|
||||
const testLi = document.getElementById('fwlive-manual-test');
|
||||
if (testLi)
|
||||
logging.renderManualTestNodes(testLi, { firewallBackend: this.firewallBackend }, {});
|
||||
if (testLi) logging.renderManualTestNodes(testLi, {}, {});
|
||||
if (this.showHostnames) this.resolveHostnamesForEntries(this.filteredRows());
|
||||
}
|
||||
});
|
||||
@@ -753,9 +753,9 @@ msgstr "turn it back off with the WAN logging on control on the watch strip."
|
||||
msgid "using fw4"
|
||||
msgstr "mit fw4"
|
||||
|
||||
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:510
|
||||
msgid "using iptables"
|
||||
msgstr "mit iptables"
|
||||
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js
|
||||
msgid "Live view may be incomplete — a legacy iptables table is registered"
|
||||
msgstr "Live view may be incomplete — a legacy iptables table is registered"
|
||||
|
||||
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:89
|
||||
msgid "· %s"
|
||||
|
||||
@@ -758,9 +758,9 @@ msgstr "turn it back off with the WAN logging on control on the watch strip."
|
||||
msgid "using fw4"
|
||||
msgstr "иÑполÑзÑеÑÑÑ fw4"
|
||||
|
||||
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:510
|
||||
msgid "using iptables"
|
||||
msgstr "иÑполÑзÑеÑÑÑ iptables"
|
||||
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js
|
||||
msgid "Live view may be incomplete — a legacy iptables table is registered"
|
||||
msgstr "Live view may be incomplete — a legacy iptables table is registered"
|
||||
|
||||
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:89
|
||||
msgid "· %s"
|
||||
|
||||
@@ -687,8 +687,8 @@ msgstr ""
|
||||
msgid "using fw4"
|
||||
msgstr ""
|
||||
|
||||
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:510
|
||||
msgid "using iptables"
|
||||
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js
|
||||
msgid "Live view may be incomplete — a legacy iptables table is registered"
|
||||
msgstr ""
|
||||
|
||||
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:89
|
||||
|
||||
@@ -722,9 +722,9 @@ msgstr "turn it back off with the WAN logging on control on the watch strip."
|
||||
msgid "using fw4"
|
||||
msgstr "ä½¿ç¨ fw4"
|
||||
|
||||
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:510
|
||||
msgid "using iptables"
|
||||
msgstr "ä½¿ç¨ iptables"
|
||||
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js
|
||||
msgid "Live view may be incomplete — a legacy iptables table is registered"
|
||||
msgstr "Live view may be incomplete — a legacy iptables table is registered"
|
||||
|
||||
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/fwlive/logging.js:89
|
||||
msgid "· %s"
|
||||
|
||||
@@ -39,8 +39,6 @@ RESOLVE_BUDGET=5
|
||||
POLL_LINES_MAX=2000
|
||||
# Bound nft ruleset dumps (corrupted/hung nft must not block LuCI rules load).
|
||||
NFT_TIMEOUT=5
|
||||
# CLI-only iptables fixture path for __rulesmap_iptables (no arbitrary paths).
|
||||
RULESMAP_IPTABLES_FILE='/tmp/rulesmap'
|
||||
|
||||
# json_escape lives in fwlive-logging.sh so prerm can source that file alone.
|
||||
# shellcheck disable=SC1090,SC1091 # LOGGING_SH resolves to sibling ../fwlive-logging.sh
|
||||
@@ -55,9 +53,6 @@ slug_key() {
|
||||
# Cap rules JSON size so a huge ruleset cannot unbound an rpcd reply.
|
||||
RULES_MAP_MAX_KEYS=512
|
||||
RULES_MAP_MAX_BYTES=65536
|
||||
# Same wall-clock bound as nft for iptables-save / ip6tables-save.
|
||||
# Overridable for host tests (e.g. FWLIVE_IPTABLES_TIMEOUT=1).
|
||||
IPTABLES_TIMEOUT="${FWLIVE_IPTABLES_TIMEOUT:-5}"
|
||||
|
||||
map_add() {
|
||||
key="$1"
|
||||
@@ -147,25 +142,6 @@ run_with_timeout() {
|
||||
fi
|
||||
}
|
||||
|
||||
map_log_prefix_entry() {
|
||||
prefix="$1"
|
||||
comment="$2"
|
||||
pass="${3:-all}"
|
||||
label=''
|
||||
|
||||
if [ -n "$comment" ] && is_uci_style_name "$comment"; then
|
||||
label="$comment"
|
||||
fi
|
||||
|
||||
[ -n "$prefix" ] || return 0
|
||||
case "$pass" in
|
||||
labeled) [ -n "$label" ] || return 0 ;;
|
||||
unlabeled) [ -z "$label" ] || return 0 ;;
|
||||
esac
|
||||
|
||||
map_prefix_with_label "$prefix" "$label"
|
||||
}
|
||||
|
||||
map_uci_rule_names() {
|
||||
# Line-wise (here-doc, not an unquoted for-loop) so a name with
|
||||
# spaces stays one token. Names that fail is_uci_style_name are
|
||||
@@ -221,20 +197,6 @@ map_from_nft_stream() {
|
||||
done
|
||||
}
|
||||
|
||||
map_from_iptables_save_stream() {
|
||||
pass="${1:-all}"
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
-*) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
prefix=$(echo "$line" | sed -n 's/.*--log-prefix "\([^"\\]*\(\\.[^"\\]*\)*\)".*/\1/p')
|
||||
comment=$(echo "$line" | sed -n 's/.*--comment "\([^"\\]*\(\\.[^"\\]*\)*\)".*/\1/p')
|
||||
[ -n "$prefix" ] || continue
|
||||
map_log_prefix_entry "$prefix" "$comment" "$pass"
|
||||
done
|
||||
}
|
||||
|
||||
nft_list_ruleset() {
|
||||
run_with_timeout "$NFT_TIMEOUT" nft list ruleset
|
||||
}
|
||||
@@ -244,10 +206,6 @@ detect_firewall_backend() {
|
||||
printf '%s' nft
|
||||
return 0
|
||||
fi
|
||||
if command -v iptables-save >/dev/null 2>&1; then
|
||||
printf '%s' iptables
|
||||
return 0
|
||||
fi
|
||||
printf '%s' unknown
|
||||
}
|
||||
|
||||
@@ -282,57 +240,16 @@ _fwlive_mktemp() {
|
||||
|
||||
enrich_rules_dump() {
|
||||
_dump_prefix="$1"
|
||||
_dump_kind="$2"
|
||||
|
||||
case "$_dump_kind" in
|
||||
nft)
|
||||
_dump_error='nft_failed'
|
||||
;;
|
||||
iptables)
|
||||
_dump_error='iptables_failed'
|
||||
;;
|
||||
ip6tables)
|
||||
_dump_error='ip6tables_failed'
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
_rules_dump=$(_fwlive_mktemp "$_dump_prefix") || _rules_dump=''
|
||||
if [ -n "$_rules_dump" ]; then
|
||||
# Keep the dump command and stream parser paired by backend. The
|
||||
# ip6tables caller is availability-gated below; its empty-error
|
||||
# precedence is intentionally preserved in the mktemp branch.
|
||||
if case "$_dump_kind" in
|
||||
nft)
|
||||
nft_list_ruleset >"$_rules_dump" 2>/dev/null
|
||||
;;
|
||||
iptables|ip6tables)
|
||||
run_with_timeout "$IPTABLES_TIMEOUT" "${_dump_kind}-save" >"$_rules_dump" 2>/dev/null
|
||||
;;
|
||||
esac
|
||||
then
|
||||
case "$_dump_kind" in
|
||||
nft)
|
||||
if nft_list_ruleset >"$_rules_dump" 2>/dev/null; then
|
||||
map_from_nft_stream labeled <"$_rules_dump"
|
||||
map_from_nft_stream unlabeled <"$_rules_dump"
|
||||
;;
|
||||
iptables|ip6tables)
|
||||
map_from_iptables_save_stream labeled <"$_rules_dump"
|
||||
map_from_iptables_save_stream unlabeled <"$_rules_dump"
|
||||
;;
|
||||
esac
|
||||
elif [ -z "$_enrich_error" ]; then
|
||||
_enrich_error="$_dump_error"
|
||||
_enrich_error='nft_failed'
|
||||
fi
|
||||
rm -f "$_rules_dump"
|
||||
elif [ "$_dump_kind" = ip6tables ]; then
|
||||
# IPv6 enrichment is optional and must not replace an earlier
|
||||
# IPv4/backend error when its temp file cannot be allocated.
|
||||
if [ -z "$_enrich_error" ]; then
|
||||
_enrich_error='mktemp_failed'
|
||||
fi
|
||||
else
|
||||
_enrich_error='mktemp_failed'
|
||||
fi
|
||||
@@ -349,17 +266,9 @@ build_rules_map() {
|
||||
# First-wins keeps UCI over nft and !fw4: over cosmetic.
|
||||
map_uci_rule_names
|
||||
|
||||
case "$backend" in
|
||||
nft)
|
||||
enrich_rules_dump fwlive-nft nft
|
||||
;;
|
||||
iptables)
|
||||
enrich_rules_dump fwlive-ipt iptables
|
||||
if command -v ip6tables-save >/dev/null 2>&1; then
|
||||
enrich_rules_dump fwlive-ip6t ip6tables
|
||||
if [ "$backend" = nft ]; then
|
||||
enrich_rules_dump fwlive-nft
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$backend" = unknown ] && [ -z "$_enrich_error" ]; then
|
||||
_enrich_error='no_backend'
|
||||
@@ -373,22 +282,6 @@ build_rules_map() {
|
||||
printf '%s}' "$_rules_json"
|
||||
}
|
||||
|
||||
rulesmap_from_iptables_file() {
|
||||
file="$1"
|
||||
OUT=''
|
||||
_map_keys=0
|
||||
_map_overflow=''
|
||||
[ "$file" = "$RULESMAP_IPTABLES_FILE" ] || return 1
|
||||
[ -f "$file" ] || return 1
|
||||
map_from_iptables_save_stream labeled < "$file"
|
||||
map_from_iptables_save_stream unlabeled < "$file"
|
||||
_rules_json="{\"backend\":\"iptables\",\"rules\":{$OUT}"
|
||||
if [ -n "$_map_overflow" ]; then
|
||||
_rules_json="${_rules_json},\"error\":\"rules_truncated\""
|
||||
fi
|
||||
printf '%s}' "$_rules_json"
|
||||
}
|
||||
|
||||
poll_clamp_lines() {
|
||||
# Contract: non-digit input returns default 50; digit strings are
|
||||
# clamped to 1..POLL_LINES_MAX with leading zeros stripped. Validates
|
||||
@@ -927,11 +820,6 @@ case "${1:-}" in
|
||||
run_selftest
|
||||
exit $?
|
||||
;;
|
||||
__rulesmap_iptables)
|
||||
# CLI selftest only — never a ubus method. Fixed path only (no argv file read).
|
||||
rulesmap_from_iptables_file "$RULESMAP_IPTABLES_FILE"
|
||||
exit $?
|
||||
;;
|
||||
__poll_clamp)
|
||||
poll_clamp_lines "${2:-}"
|
||||
exit $?
|
||||
@@ -988,4 +876,8 @@ case "${1:-}" in
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# Removed CLI hooks (including __rulesmap_iptables) must not succeed.
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall
|
||||
PKG_VERSION:=26.9.16
|
||||
PKG_RELEASE:=284
|
||||
PKG_RELEASE:=285
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
|
||||
@@ -96,6 +96,7 @@ if load_urltest_options then -- [[ URLTest Start ]]
|
||||
o = s:option(MultiValue, "urltest_node", translate("URLTest node list"), translate("List of nodes to test, <a target='_blank' href='https://sing-box.sagernet.org/configuration/outbound/urltest'>document</a>"))
|
||||
o:depends({ node_add_mode = "manual" })
|
||||
o.widget = "checkbox"
|
||||
o.cast = "table"
|
||||
o.template = m:template_path("/cbi/nodes_multivalue")
|
||||
o.group = {}
|
||||
for k1, v1 in pairs(node_list) do
|
||||
@@ -106,30 +107,6 @@ if load_urltest_options then -- [[ URLTest Start ]]
|
||||
end
|
||||
end
|
||||
end
|
||||
-- 读取旧 DynamicList
|
||||
function o.cfgvalue(self, section)
|
||||
return table.concat(m:get(section, "urltest_node") or {}, " ")
|
||||
end
|
||||
-- 写入保持 DynamicList
|
||||
function o.write(self, section, value)
|
||||
local old = m:get(section, "urltest_node") or {}
|
||||
local new, set = {}, {}
|
||||
for v in value:gmatch("%S+") do
|
||||
new[#new + 1] = v
|
||||
set[v] = 1
|
||||
end
|
||||
for _, v in ipairs(old) do
|
||||
if not set[v] then
|
||||
m:set(section, "urltest_node", new)
|
||||
return
|
||||
end
|
||||
set[v] = nil
|
||||
end
|
||||
for _ in pairs(set) do
|
||||
m:set(section, "urltest_node", new)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(MultiValue, "node_group", translate("Select Group"))
|
||||
o:depends({ node_add_mode = "batch" })
|
||||
|
||||
@@ -90,6 +90,7 @@ if load_balancing_options then -- [[ Load balancing Start ]]
|
||||
o = s:option(MultiValue, "balancing_node", translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://xtls.github.io/config/routing.html#balancerobject'>document</a>"))
|
||||
o:depends({ node_add_mode = "manual" })
|
||||
o.widget = "checkbox"
|
||||
o.cast = "table"
|
||||
o.template = m:template_path("/cbi/nodes_multivalue")
|
||||
o.group = {}
|
||||
for k1, v1 in pairs(node_list) do
|
||||
@@ -100,30 +101,6 @@ if load_balancing_options then -- [[ Load balancing Start ]]
|
||||
end
|
||||
end
|
||||
end
|
||||
-- 读取旧 DynamicList
|
||||
function o.cfgvalue(self, section)
|
||||
return table.concat(m:get(section, "balancing_node") or {}, " ")
|
||||
end
|
||||
-- 写入保持 DynamicList
|
||||
function o.write(self, section, value)
|
||||
local old = m:get(section, "balancing_node") or {}
|
||||
local new, set = {}, {}
|
||||
for v in value:gmatch("%S+") do
|
||||
new[#new + 1] = v
|
||||
set[v] = 1
|
||||
end
|
||||
for _, v in ipairs(old) do
|
||||
if not set[v] then
|
||||
m:set(section, "balancing_node", new)
|
||||
return
|
||||
end
|
||||
set[v] = nil
|
||||
end
|
||||
for _ in pairs(set) do
|
||||
m:set(section, "balancing_node", new)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(MultiValue, "node_group", translate("Select Group"))
|
||||
o:depends({ node_add_mode = "batch" })
|
||||
|
||||
@@ -36,7 +36,7 @@ o = s:option(Flag, "firewall_allow", translate("Firewall Allow"))
|
||||
o.default = "0"
|
||||
|
||||
o = s:option(Value, "firewall_allow_src", translate("Source zone"))
|
||||
o.rmempty = false
|
||||
o.rmempty = not m.is_js_luci
|
||||
o.nocreate = true
|
||||
o.allowany = true
|
||||
o.default = "wan"
|
||||
|
||||
@@ -50,7 +50,7 @@ o.validate = function(self, value)
|
||||
if v then return v end
|
||||
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
|
||||
end
|
||||
o.cfgvalue = function(self, section, value)
|
||||
o.cfgvalue = function(self, section)
|
||||
local config_str = m:get(section, "config_str")
|
||||
if config_str then
|
||||
return api.base64Decode(config_str)
|
||||
@@ -79,7 +79,8 @@ o = s:option(Value, "port", translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
o:depends({ custom = false })
|
||||
|
||||
o = s:option(DynamicList, "users", translate("User"))
|
||||
o = s:option(MultiValue, "users", translate("User"))
|
||||
o.cast = "table"
|
||||
for i, v in ipairs(user_list) do
|
||||
o:value(v[".name"], v.username)
|
||||
end
|
||||
@@ -535,7 +536,7 @@ o.default = "0"
|
||||
o:depends({ custom = false })
|
||||
|
||||
o = s:option(Value, "firewall_allow_src", translate("Source zone"))
|
||||
o.rmempty = false
|
||||
o.rmempty = not m.is_js_luci
|
||||
o.nocreate = true
|
||||
o.allowany = true
|
||||
o.default = "wan"
|
||||
|
||||
@@ -46,7 +46,7 @@ o.validate = function(self, value)
|
||||
if v then return v end
|
||||
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
|
||||
end
|
||||
o.cfgvalue = function(self, section, value)
|
||||
o.cfgvalue = function(self, section)
|
||||
local config_str = m:get(section, "config_str")
|
||||
if config_str then
|
||||
return api.base64Decode(config_str)
|
||||
@@ -72,7 +72,8 @@ o = s:option(Value, "port", translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
o:depends({ custom = false })
|
||||
|
||||
o = s:option(DynamicList, "users", translate("User"))
|
||||
o = s:option(MultiValue, "users", translate("User"))
|
||||
o.cast = "table"
|
||||
for i, v in ipairs(user_list) do
|
||||
o:value(v[".name"], v.username)
|
||||
end
|
||||
@@ -521,7 +522,7 @@ o.default = "0"
|
||||
o:depends({ custom = false })
|
||||
|
||||
o = s:option(Value, "firewall_allow_src", translate("Source zone"))
|
||||
o.rmempty = false
|
||||
o.rmempty = not m.is_js_luci
|
||||
o.nocreate = true
|
||||
o.allowany = true
|
||||
o.default = "wan"
|
||||
|
||||
@@ -41,7 +41,7 @@ o.validate = function(self, value)
|
||||
if v then return v end
|
||||
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
|
||||
end
|
||||
o.cfgvalue = function(self, section, value)
|
||||
o.cfgvalue = function(self, section)
|
||||
local config_str = m:get(section, "config_str")
|
||||
if config_str then
|
||||
return api.base64Decode(config_str)
|
||||
@@ -79,7 +79,7 @@ o.default = "0"
|
||||
o:depends({ custom = false })
|
||||
|
||||
o = s:option(Value, "firewall_allow_src", translate("Source zone"))
|
||||
o.rmempty = false
|
||||
o.rmempty = not m.is_js_luci
|
||||
o.nocreate = true
|
||||
o.allowany = true
|
||||
o.default = "wan"
|
||||
|
||||
@@ -54,7 +54,7 @@ o.validate = function(self, value)
|
||||
if v then return v end
|
||||
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
|
||||
end
|
||||
o.cfgvalue = function(self, section, value)
|
||||
o.cfgvalue = function(self, section)
|
||||
local config_str = m:get(section, "config_str")
|
||||
if config_str then
|
||||
return api.base64Decode(config_str)
|
||||
@@ -110,7 +110,7 @@ o.default = "0"
|
||||
o:depends({ custom = false })
|
||||
|
||||
o = s:option(Value, "firewall_allow_src", translate("Source zone"))
|
||||
o.rmempty = false
|
||||
o.rmempty = not m.is_js_luci
|
||||
o.nocreate = true
|
||||
o.allowany = true
|
||||
o.default = "wan"
|
||||
|
||||
@@ -31,7 +31,7 @@ o.validate = function(self, value)
|
||||
if v then return v end
|
||||
return nil, translate("Custom Config") .. " " .. translate("Must be JSON text!")
|
||||
end
|
||||
o.cfgvalue = function(self, section, value)
|
||||
o.cfgvalue = function(self, section)
|
||||
local config_str = m:get(section, "config_str")
|
||||
if config_str then
|
||||
return api.base64Decode(config_str)
|
||||
@@ -45,7 +45,8 @@ o = s:option(Value, "port", translate("Listen Port"))
|
||||
o.datatype = "port"
|
||||
o:depends({ custom = false })
|
||||
|
||||
o = s:option(DynamicList, "users", translate("User"))
|
||||
o = s:option(MultiValue, "users", translate("User"))
|
||||
o.cast = "table"
|
||||
for i, v in ipairs(user_list) do
|
||||
o:value(v[".name"], v.username)
|
||||
end
|
||||
@@ -168,7 +169,7 @@ o.default = "0"
|
||||
o:depends({ custom = false })
|
||||
|
||||
o = s:option(Value, "firewall_allow_src", translate("Source zone"))
|
||||
o.rmempty = false
|
||||
o.rmempty = not m.is_js_luci
|
||||
o.nocreate = true
|
||||
o.allowany = true
|
||||
o.default = "wan"
|
||||
|
||||
@@ -19,7 +19,7 @@ OPENWRT_ARCH = nil
|
||||
DISTRIB_ARCH = nil
|
||||
OPENWRT_BOARD = nil
|
||||
|
||||
LOCK_PREFIX = "/tmp/lock/" .. c_config
|
||||
LOCK_PREFIX = "/var/lock/" .. c_config
|
||||
LOG_FILE = "/tmp/log/" .. c_config .. ".log"
|
||||
TMP_PATH = "/tmp/etc/" .. c_config
|
||||
CACHE_PATH = TMP_PATH .. "_tmp"
|
||||
@@ -1542,11 +1542,18 @@ function set_type_cbi(s)
|
||||
obj.option_prefix = s_self.option_prefix
|
||||
obj.option = s_self.option_prefix .. option
|
||||
obj.cfgvalue = function(self, section)
|
||||
local v
|
||||
if self.rewrite_option then
|
||||
return self.map:get(section, self.rewrite_option)
|
||||
v = self.map:get(section, self.rewrite_option)
|
||||
else
|
||||
return self.map:get(section, self.config_option)
|
||||
v = self.map:get(section, self.config_option)
|
||||
end
|
||||
if util.instanceof(self, cbi.MultiValue) then
|
||||
if v and self.cast == "table" then
|
||||
return table.concat(v, " ")
|
||||
end
|
||||
end
|
||||
return v
|
||||
end
|
||||
obj.write = function(self, section, value)
|
||||
if s1.fields["type"]:formvalue(s_self.section) == s_self.type_name then
|
||||
@@ -1564,6 +1571,21 @@ function set_type_cbi(s)
|
||||
new_val = new_t
|
||||
end
|
||||
end
|
||||
if util.instanceof(self, cbi.MultiValue) then
|
||||
local new_t = {}
|
||||
if type(value) == "table" then
|
||||
new_t = table_remove_duplicates(value)
|
||||
else
|
||||
string.gsub(value, '[^' .. " " .. ']+', function(v)
|
||||
new_t[#new_t + 1] = v
|
||||
end)
|
||||
end
|
||||
if self.cast == "string" then
|
||||
new_val = table.concat(new_t, " ")
|
||||
else
|
||||
new_val = new_t
|
||||
end
|
||||
end
|
||||
if self.rewrite_option then
|
||||
self.map:set(section, self.rewrite_option, new_val)
|
||||
else
|
||||
@@ -1619,6 +1641,7 @@ end
|
||||
function type_cbi_section(s, s2)
|
||||
for i, v in ipairs(s2.children) do
|
||||
local o = s2.children[i]
|
||||
o.section = s
|
||||
s:append(o)
|
||||
s.fields[o.option] = o
|
||||
end
|
||||
|
||||
@@ -118,8 +118,16 @@ function copy_instance(var)
|
||||
local TMP_DNSMASQ_PATH = var["-TMP_DNSMASQ_PATH"]
|
||||
local conf_lines = {}
|
||||
local DEFAULT_DNSMASQ_CFGID = sys.exec("echo -n $(uci -q show dhcp.@dnsmasq[0] | awk 'NR==1 {split($0, conf, /[.=]/); print conf[2]}')")
|
||||
if fs.access("/var/etc/dnsmasq.conf." .. DEFAULT_DNSMASQ_CFGID) then
|
||||
for line in io.lines("/var/etc/dnsmasq.conf." .. DEFAULT_DNSMASQ_CFGID) do
|
||||
local conf_file = "/var/etc/dnsmasq.conf." .. DEFAULT_DNSMASQ_CFGID
|
||||
|
||||
local retry = 5
|
||||
while not fs.access(conf_file) and retry > 0 do
|
||||
api.nixio.nanosleep(1, 0)
|
||||
retry = retry - 1
|
||||
end
|
||||
|
||||
if fs.access(conf_file) then
|
||||
for line in io.lines(conf_file) do
|
||||
local filter
|
||||
if line:find("passwall") then filter = true end
|
||||
if line:find("ubus") then filter = true end
|
||||
@@ -139,7 +147,10 @@ function copy_instance(var)
|
||||
tinsert(conf_lines, line)
|
||||
end
|
||||
end
|
||||
else
|
||||
sys.call("logger -t passwall 'ERROR: dnsmasq config " .. conf_file .. " not found after 5s wait! DNS hijacking will fail.'")
|
||||
end
|
||||
|
||||
tinsert(conf_lines, "port=" .. LISTEN_PORT)
|
||||
if TMP_DNSMASQ_PATH then
|
||||
sys.call("rm -rf " .. TMP_DNSMASQ_PATH .. "/*passwall*")
|
||||
|
||||
@@ -6,7 +6,7 @@ CONFIG=passwall
|
||||
APP_PATH=/usr/share/${CONFIG}
|
||||
TMP_PATH=/tmp/etc/${CONFIG}
|
||||
TMP_PATH2=${TMP_PATH}_tmp
|
||||
LOCK_PATH=/tmp/lock
|
||||
LOCK_PATH=/var/lock
|
||||
LOG_FILE=/tmp/log/${CONFIG}.log
|
||||
TMP_ACL_PATH=${TMP_PATH}/acl
|
||||
TMP_BIN_PATH=${TMP_PATH}/bin
|
||||
|
||||
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall2
|
||||
PKG_VERSION:=26.9.16
|
||||
PKG_RELEASE:=130
|
||||
PKG_RELEASE:=131
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
|
||||
@@ -99,6 +99,7 @@ if load_urltest_options then -- [[ URLTest Start ]]
|
||||
o = s:option(MultiValue, "urltest_node", translate("URLTest node list"), translate("List of nodes to test, <a target='_blank' href='https://sing-box.sagernet.org/configuration/outbound/urltest'>document</a>"))
|
||||
o:depends({ node_add_mode = "manual" })
|
||||
o.widget = "checkbox"
|
||||
o.cast = "table"
|
||||
o.template = m:template_path("/cbi/nodes_multivalue")
|
||||
o.group = {}
|
||||
for k1, v1 in pairs(node_list) do
|
||||
@@ -109,28 +110,6 @@ if load_urltest_options then -- [[ URLTest Start ]]
|
||||
end
|
||||
end
|
||||
end
|
||||
function o.cfgvalue(self, section)
|
||||
return table.concat(m:get(section, "urltest_node") or {}, " ")
|
||||
end
|
||||
function o.write(self, section, value)
|
||||
local old = m:get(section, "urltest_node") or {}
|
||||
local new, set = {}, {}
|
||||
for v in value:gmatch("%S+") do
|
||||
new[#new + 1] = v
|
||||
set[v] = 1
|
||||
end
|
||||
for _, v in ipairs(old) do
|
||||
if not set[v] then
|
||||
m:set(section, "urltest_node", new)
|
||||
return
|
||||
end
|
||||
set[v] = nil
|
||||
end
|
||||
for _ in pairs(set) do
|
||||
m:set(section, "urltest_node", new)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(MultiValue, "node_group", translate("Select Group"))
|
||||
o:depends({ node_add_mode = "batch" })
|
||||
|
||||
@@ -91,6 +91,7 @@ if load_balancing_options then -- [[ Load balancing Start ]]
|
||||
o = s:option(MultiValue, "balancing_node", translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://xtls.github.io/config/routing.html#balancerobject'>document</a>"))
|
||||
o:depends({ node_add_mode = "manual" })
|
||||
o.widget = "checkbox"
|
||||
o.cast = "table"
|
||||
o.template = m:template_path("/cbi/nodes_multivalue")
|
||||
o.group = {}
|
||||
for k1, v1 in pairs(node_list) do
|
||||
@@ -101,28 +102,6 @@ if load_balancing_options then -- [[ Load balancing Start ]]
|
||||
end
|
||||
end
|
||||
end
|
||||
function o.cfgvalue(self, section)
|
||||
return table.concat(m:get(section, "balancing_node") or {}, " ")
|
||||
end
|
||||
function o.write(self, section, value)
|
||||
local old = m:get(section, "balancing_node") or {}
|
||||
local new, set = {}, {}
|
||||
for v in value:gmatch("%S+") do
|
||||
new[#new + 1] = v
|
||||
set[v] = 1
|
||||
end
|
||||
for _, v in ipairs(old) do
|
||||
if not set[v] then
|
||||
m:set(section, "balancing_node", new)
|
||||
return
|
||||
end
|
||||
set[v] = nil
|
||||
end
|
||||
for _ in pairs(set) do
|
||||
m:set(section, "balancing_node", new)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
o = s:option(MultiValue, "node_group", translate("Select Group"))
|
||||
o:depends({ node_add_mode = "batch" })
|
||||
|
||||
@@ -20,7 +20,7 @@ command_timeout = 300
|
||||
OPENWRT_ARCH = nil
|
||||
DISTRIB_ARCH = nil
|
||||
|
||||
LOCK_PREFIX = "/tmp/lock/" .. c_config
|
||||
LOCK_PREFIX = "/var/lock/" .. c_config
|
||||
LOG_FILE = "/tmp/log/" .. c_config .. ".log"
|
||||
TMP_PATH = "/tmp/etc/" .. c_config
|
||||
CACHE_PATH = TMP_PATH .. "_tmp"
|
||||
|
||||
@@ -120,8 +120,17 @@ function copy_instance(var)
|
||||
local TMP_DNSMASQ_PATH = var["TMP_DNSMASQ_PATH"]
|
||||
local conf_lines = {}
|
||||
local DEFAULT_DNSMASQ_CFGID = sys.exec("echo -n $(uci -q show dhcp.@dnsmasq[0] | awk 'NR==1 {split($0, conf, /[.=]/); print conf[2]}')")
|
||||
if fs.access("/var/etc/dnsmasq.conf." .. DEFAULT_DNSMASQ_CFGID) then
|
||||
for line in io.lines("/var/etc/dnsmasq.conf." .. DEFAULT_DNSMASQ_CFGID) do
|
||||
|
||||
local conf_file = "/var/etc/dnsmasq.conf." .. DEFAULT_DNSMASQ_CFGID
|
||||
|
||||
local retry = 5
|
||||
while not fs.access(conf_file) and retry > 0 do
|
||||
api.nixio.nanosleep(1, 0)
|
||||
retry = retry - 1
|
||||
end
|
||||
|
||||
if fs.access(conf_file) then
|
||||
for line in io.lines(conf_file) do
|
||||
local filter
|
||||
if line:find("passwall2") then filter = true end
|
||||
if line:find("ubus") then filter = true end
|
||||
@@ -140,7 +149,10 @@ function copy_instance(var)
|
||||
tinsert(conf_lines, line)
|
||||
end
|
||||
end
|
||||
else
|
||||
sys.call("logger -t passwall2 'ERROR: dnsmasq config " .. conf_file .. " not found after 5s wait! DNS hijacking will fail.'")
|
||||
end
|
||||
|
||||
tinsert(conf_lines, "port=" .. LISTEN_PORT)
|
||||
--dhcp.leases to hostsMore actions
|
||||
local hosts = api.CACHE_PATH .. "/dhcp-hosts"
|
||||
|
||||
@@ -6,7 +6,7 @@ CONFIG=passwall2
|
||||
APP_PATH=/usr/share/${CONFIG}
|
||||
TMP_PATH=/tmp/etc/${CONFIG}
|
||||
TMP_PATH2=${TMP_PATH}_tmp
|
||||
LOCK_PATH=/tmp/lock
|
||||
LOCK_PATH=/var/lock
|
||||
LOG_FILE=/tmp/log/${CONFIG}.log
|
||||
TMP_ACL_PATH=${TMP_PATH}/acl
|
||||
TMP_BIN_PATH=${TMP_PATH}/bin
|
||||
|
||||
@@ -1,150 +1,508 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require dom';
|
||||
'require poll';
|
||||
'require uci';
|
||||
'require rpc';
|
||||
'require form';
|
||||
'require uci';
|
||||
'require fs';
|
||||
'require ui';
|
||||
|
||||
// 辅助工具:统一纯净数组转换
|
||||
function toArray(val) {
|
||||
if (Array.isArray(val)) return val.filter(Boolean);
|
||||
return typeof val === 'string' ? val.trim().split(/\s+/).filter(Boolean) : [];
|
||||
}
|
||||
|
||||
// 辅助工具:提取首个 IP 并去除可能的 CIDR 掩码(容错支持 String、Array 及 null/undefined)
|
||||
function getFirstIp(val, defaultVal) {
|
||||
if (Array.isArray(val)) val = val[0];
|
||||
if (typeof val === 'string') return val.split('/')[0].trim();
|
||||
return defaultVal || '';
|
||||
}
|
||||
|
||||
// 辅助工具:通用值深度比对(支持标量与数组)
|
||||
function isEqual(a, b) {
|
||||
if (Array.isArray(a) || Array.isArray(b)) {
|
||||
var arrA = toArray(a).sort(), arrB = toArray(b).sort();
|
||||
return arrA.length === arrB.length && arrA.every(function(v, i) { return v === arrB[i]; });
|
||||
}
|
||||
return String(a == null ? '' : a) === String(b == null ? '' : b);
|
||||
}
|
||||
|
||||
// 辅助工具:检查数组是否包含元素(兼容低版本环境)
|
||||
function has(arr, item) {
|
||||
return Array.isArray(arr) && arr.indexOf(item) !== -1;
|
||||
}
|
||||
|
||||
// 辅助工具:安全删除 UCI 选项(只有当该选项在 UCI 内存中真实存在时才调用 unset,防止 ubus 报错)
|
||||
function safeUnset(conf, sid, opt) {
|
||||
try {
|
||||
if (uci.get(conf, sid, opt) != null) {
|
||||
uci.unset(conf, sid, opt);
|
||||
return true;
|
||||
}
|
||||
} catch(e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 辅助工具:智能判断无线物理网卡的频段 (2.4G / 5G / 6G)
|
||||
function getRadioBand(devName) {
|
||||
if (!devName) return '2.4G';
|
||||
var b = uci.get('wireless', devName, 'band');
|
||||
if (b === '6g') return '6G';
|
||||
if (b === '5g') return '5G';
|
||||
if (b === '2g') return '2.4G';
|
||||
|
||||
var ch = parseInt(uci.get('wireless', devName, 'channel'), 10);
|
||||
if (!isNaN(ch)) {
|
||||
if (ch >= 1 && ch <= 14) return '2.4G';
|
||||
if (ch >= 32 && ch <= 196) return '5G';
|
||||
if (ch > 196) return '6G';
|
||||
}
|
||||
|
||||
var hw = (uci.get('wireless', devName, 'hwmode') || '').toLowerCase();
|
||||
if (hw.indexOf('a') !== -1) return '5G';
|
||||
if (hw.indexOf('b') !== -1 || hw.indexOf('g') !== -1) return '2.4G';
|
||||
|
||||
var dl = devName.toLowerCase();
|
||||
if (dl.indexOf('6g') !== -1) return '6G';
|
||||
if (dl.indexOf('5g') !== -1) return '5G';
|
||||
return '2.4G';
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: async function () {
|
||||
const promises = await Promise.all([
|
||||
fs.exec('/etc/init.d/wizard', ['reconfig']),
|
||||
uci.changes(),
|
||||
L.resolveDefault(uci.load('wireless')),
|
||||
uci.load('wizard'),
|
||||
L.resolveDefault(fs.stat('/www/luci-static/istorex/style.css'), null),
|
||||
L.resolveDefault(fs.stat('/www/luci-static/routerdog/style.css'), null),
|
||||
L.resolveDefault(fs.stat('/usr/sbin/nginx'), null)
|
||||
]);
|
||||
const data = {
|
||||
istorex: promises[4],
|
||||
routerdog: promises[5],
|
||||
nginx: promises[6]
|
||||
initialValues: {},
|
||||
initialShortcuts: [],
|
||||
hasWireless: false,
|
||||
hasNginx: false,
|
||||
map: null,
|
||||
fields: null,
|
||||
optMap: null,
|
||||
|
||||
// 1. 直连加载系统真实配置包,无需任何中间文件
|
||||
load: function() {
|
||||
var pkgs = ['network', 'wireless', 'dhcp', 'uhttpd', 'firewall', 'luci', 'wizard'];
|
||||
var tasks = pkgs.map(function(p) {
|
||||
return uci.load(p).catch(function() { return null; });
|
||||
});
|
||||
tasks.push(fs.stat('/etc/nginx/uci.conf').then(function() { return true; }).catch(function() { return false; }));
|
||||
|
||||
return Promise.all(tasks).then(function(res) {
|
||||
var wifiSecs = (res && res) ? (uci.sections('wireless', 'wifi-device') || []) : [];
|
||||
return {
|
||||
hasWireless: wifiSecs.length > 0,
|
||||
hasNginx: !!res[7]
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
// 2. 统一聚合读取系统当前各子模块的实时配置
|
||||
getSystemConfig: function() {
|
||||
var isSideRouter = !!uci.get('network', 'lan', 'gateway') && uci.get('network', 'wan', 'auto') === '0';
|
||||
|
||||
var ap = null;
|
||||
if (this.hasWireless) {
|
||||
var ifaces = uci.sections('wireless', 'wifi-iface') || [];
|
||||
for (var i = 0; i < ifaces.length; i++) {
|
||||
if (ifaces[i].mode === 'ap' || !ifaces[i].mode) {
|
||||
ap = ifaces[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 剥离现有频段后缀,确保输入框展示纯净的基准名称
|
||||
var rawSsid = (ap && ap.ssid) || (this.hasWireless ? 'Kwrt' : '');
|
||||
var baseSsid = rawSsid.replace(/_(2\.4G|5G|6G)$/i, '').trim();
|
||||
|
||||
return {
|
||||
wan_proto: isSideRouter ? 'siderouter' : (uci.get('network', 'wan', 'proto') || 'dhcp'),
|
||||
wan_pppoe_user: uci.get('network', 'wan', 'username') || '',
|
||||
wan_pppoe_pass: uci.get('network', 'wan', 'password') || '',
|
||||
lan_ipaddr: getFirstIp(uci.get('network', 'lan', 'ipaddr'), '10.0.0.1'),
|
||||
lan_gateway: getFirstIp(uci.get('network', 'lan', 'gateway'), ''),
|
||||
lan_dns: toArray(uci.get('network', 'lan', 'dns')),
|
||||
dhcp: uci.get('dhcp', 'lan', 'ignore') === '1' ? '0' : '1',
|
||||
ipv6: uci.get('network', 'wan6', 'auto') === '0' ? '0' : '1',
|
||||
https: uci.get('wizard', 'default', 'https') || '0',
|
||||
cookie_p: uci.get('wizard', 'default', 'persistent_cookies') || '1',
|
||||
landing_page: uci.get('wizard', 'default', 'landing_page') || 'default',
|
||||
autoupgrade_fm: uci.get('wizard', 'default', 'autoupgrade_fm') || '1',
|
||||
coremark: uci.get('wizard', 'default', 'coremark') || '0',
|
||||
wifi_ssid: baseSsid || (this.hasWireless ? 'Kwrt' : ''),
|
||||
wifi_key: (ap && ap.key) || ''
|
||||
};
|
||||
return data;
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var m, s, o;
|
||||
var has_wifi = false;
|
||||
this.hasWireless = !!data.hasWireless;
|
||||
this.hasNginx = !!data.hasNginx;
|
||||
|
||||
if (uci.sections('wireless', 'wifi-device').length > 0) {
|
||||
has_wifi = true;
|
||||
}
|
||||
var sys = this.getSystemConfig();
|
||||
this.initialValues = Object.assign({}, sys);
|
||||
|
||||
m = new form.Map('wizard', [_('Initial Router Setup')],
|
||||
_('If you are using this router for the first time, please configure it here.'));
|
||||
// 缓存初始 shortcuts 配置快照,用于保存时比对差异
|
||||
var shortcuts = uci.sections('wizard', 'shortcuts') || [];
|
||||
this.initialShortcuts = shortcuts.map(function(sec) {
|
||||
return {
|
||||
shortcut: sec.shortcut || '',
|
||||
to_url: sec.to_url || '',
|
||||
comments: sec.comments || ''
|
||||
};
|
||||
});
|
||||
|
||||
s = m.section(form.NamedSection, 'default', 'wizard');
|
||||
s.addremove = false;
|
||||
s.tab('netsetup', _('Net Settings'), _('Three different ways to access the Internet, please choose according to your own situation.'));
|
||||
var m = new form.Map('wizard', _('Setup Wizard'),
|
||||
_('Quickly configure common network, wireless, and system settings.'));
|
||||
|
||||
o = s.taboption('netsetup', form.ListValue, 'wan_proto', _('Protocol'));
|
||||
o.rmempty = false;
|
||||
o.value('dhcp', _('DHCP client'));
|
||||
o.value('pppoe', _('PPPoE'));
|
||||
o.value('siderouter', _('Siderouter'));
|
||||
|
||||
o = s.taboption('netsetup', form.Value, 'wan_pppoe_user', _('PAP/CHAP username'));
|
||||
o.depends('wan_proto', 'pppoe');
|
||||
|
||||
o = s.taboption('netsetup', form.Value, 'wan_pppoe_pass', _('PAP/CHAP password'));
|
||||
o.depends('wan_proto', 'pppoe');
|
||||
o.password = true;
|
||||
|
||||
o = s.taboption('netsetup', form.Value, 'lan_ipaddr', _('IPv4 address'));
|
||||
o.datatype = 'ip4addr';
|
||||
|
||||
o = s.taboption('netsetup', form.DynamicList, 'lan_dns', _('Use custom DNS servers'), _('Leave empty to use ISP DNS'));
|
||||
o.datatype = 'ip4addr';
|
||||
o.cast = 'string';
|
||||
|
||||
o = s.taboption('netsetup', form.Value, 'lan_gateway', _('IPv4 gateway'));
|
||||
o.depends('wan_proto', 'siderouter');
|
||||
o.datatype = 'ip4addr';
|
||||
o.placeholder = _('Enter the main router IP');
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('netsetup', form.Flag, 'dhcp', _('DHCP Server'),
|
||||
_('To turn on this DHCP, you need to turn off the DHCP of the main router, and to turn off this DHCP, you need to manually change the gateway and DNS of all Internet devices to the IP of this bypass router'));
|
||||
o.depends('wan_proto', 'siderouter');
|
||||
o.default = o.enabled;
|
||||
|
||||
o = s.taboption('netsetup', form.Flag, 'ipv6', _('Enable IPv6'), _('Enable/Disable IPv6'));
|
||||
o.default = o.enabled;
|
||||
|
||||
s.tab('firmware', _('Firmware Settings'));
|
||||
|
||||
// o = s.taboption('firmware', form.Flag, 'autoupgrade_pkg', _('Packages Auto Upgrade'),_('谨慎开启'));
|
||||
// o.rmempty = false;
|
||||
|
||||
o = s.taboption('firmware', form.Flag, 'autoupgrade_fm', _('Firmware Upgrade Notice'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('firmware', form.Flag, 'coremark', _('CoreMark'), _('第一次开机后是否运行CPU跑分测试'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('firmware', form.Flag, 'cookie_p', _('Persistent cookies'),
|
||||
_('Keep the background login state to avoid the need to log in again every time the browser is closed'));
|
||||
o.default = o.enabled;
|
||||
|
||||
o = s.taboption('firmware', form.Flag, 'https', _('Force the use of HTTPS in the backend.'));
|
||||
|
||||
if (data.istorex || data.routerdog){
|
||||
o = s.taboption('firmware', form.ListValue, 'landing_page', _('主题模式'));
|
||||
o.value('default', _('默认'));
|
||||
if (data.routerdog){
|
||||
o.value('routerdog', _('路由狗(专业NAS模式)'));
|
||||
}
|
||||
if (data.istorex){
|
||||
o.value('nas', _('NAS模式'));
|
||||
o.value('next-nas', _('NEXT-NAS模式'));
|
||||
o.value('router', _('路由模式'));
|
||||
}
|
||||
o.default = 'default';
|
||||
}
|
||||
|
||||
if (has_wifi) {
|
||||
s.tab('wifisetup', _('Wireless Settings'), _('Set the router\'s wireless name and password. For more advanced settings, please go to the Network-Wireless page.'));
|
||||
o = s.taboption('wifisetup', form.Value, 'wifi_ssid', _('<abbr title=\"Extended Service Set Identifier\">ESSID</abbr>'));
|
||||
o.datatype = 'maxlength(32)';
|
||||
|
||||
o = s.taboption("wifisetup", form.Value, "wifi_key", _("Key"));
|
||||
o.datatype = 'wpakey';
|
||||
o.password = true;
|
||||
}
|
||||
|
||||
if (data.nginx) {
|
||||
s.tab('shortcuts', _('Shortcuts'), _('比如设置google.com的快捷方式为字母g,则在此路由器网络的任何浏览器中输入g/即可访问google.com'));
|
||||
|
||||
o = s.taboption('shortcuts', form.SectionValue, 'shortcuts', form.GridSection, 'shortcuts', null,
|
||||
_('Shortcuts'));
|
||||
|
||||
s = o.subsection;
|
||||
s.addremove = true;
|
||||
var s = m.section(form.NamedSection, 'default', 'wizard');
|
||||
s.anonymous = true;
|
||||
s.addremove = false;
|
||||
|
||||
o = s.option(form.Value, 'shortcut', _('Shortcut'));
|
||||
o.rmempty = false;
|
||||
s.tab('netsetup', _('Net Settings'));
|
||||
s.tab('firmware', _('Firmware Settings'));
|
||||
if (this.hasWireless) s.tab('wifisetup', _('Wireless Settings'));
|
||||
|
||||
o = s.option(form.Value, 'to_url', _('Target URL'));
|
||||
o.rmempty = false;
|
||||
o.placeholder = 'https://example.com';
|
||||
o.validate = function(section_id, value) {
|
||||
if (value.match(/^https?:\/\/.+/i)) {
|
||||
if (this.hasNginx) {
|
||||
s.tab('shortcuts', _('Shortcuts'), _('比如设置google.com的快捷方式为字母g,则在此路由器网络的任何浏览器中输入g/即可访问google.com'));
|
||||
}
|
||||
|
||||
// 3. 声明式配置项驱动表
|
||||
var opt, fields = [
|
||||
// 网络设置
|
||||
{ tab: 'netsetup', type: form.ListValue, id: 'wan_proto', title: _('WAN Protocol / Mode'),
|
||||
choices: { dhcp: _('DHCP Client (Default Router)'), pppoe: _('PPPoE Dial-up'), siderouter: _('Side-Router Mode (Bypass Gateway)') } },
|
||||
{ tab: 'netsetup', type: form.Value, id: 'wan_pppoe_user', title: _('PPPoE Username'), depends: { wan_proto: 'pppoe' } },
|
||||
{ tab: 'netsetup', type: form.Value, id: 'wan_pppoe_pass', title: _('PPPoE Password'), depends: { wan_proto: 'pppoe' }, password: true },
|
||||
{ tab: 'netsetup', type: form.Value, id: 'lan_ipaddr', title: _('LAN IPv4 Address'), datatype: 'ip4addr', placeholder: '10.0.0.1' },
|
||||
{ tab: 'netsetup', type: form.DynamicList, id: 'lan_dns', title: _('Custom DNS Server(s)'), datatype: 'ipaddr', placeholder: '223.5.5.5' },
|
||||
{ tab: 'netsetup', type: form.Value, id: 'lan_gateway', title: _('Gateway Address'), datatype: 'ip4addr', placeholder: '', depends: { wan_proto: 'siderouter' }, desc: _('Primary router IP address used when operating in side-router mode.') },
|
||||
{ tab: 'netsetup', type: form.Flag, id: 'dhcp', title: _('Enable DHCP Server'), desc: _('Enable or disable LAN DHCP server (usually disabled in side-router mode to avoid IP address conflicts).') },
|
||||
{ tab: 'netsetup', type: form.Flag, id: 'ipv6', title: _('IPv6 Support'), desc: _('Enable or disable IPv6 router advertisements and DHCPv6.') },
|
||||
|
||||
// 固件与系统设置
|
||||
{ tab: 'firmware', type: form.Flag, id: 'autoupgrade_fm', title: _('Firmware Upgrade Notice'), desc: _('Check and display notices for newer firmware versions.') },
|
||||
{ tab: 'firmware', type: form.Flag, id: 'coremark', title: _('Run CoreMark on Boot'), desc: _('Run CPU benchmark asynchronously upon router initialization.') },
|
||||
{ tab: 'firmware', type: form.Flag, id: 'cookie_p', title: _('Persistent Cookie Session'), desc: _('Maintain persistent login sessions in the web browser.') },
|
||||
{ tab: 'firmware', type: form.Flag, id: 'https', title: _('Enforce HTTPS Access'), desc: _('Automatically redirect HTTP requests to secure HTTPS.') },
|
||||
{ tab: 'firmware', type: form.ListValue, id: 'landing_page', title: _('Landing Dashboard Mode'),
|
||||
choices: { 'default': _('Default'), routerdog: _('RouterDog'), nas: _('NAS'), 'next-nas': _('Next-NAS'), router: _('Router') } }
|
||||
];
|
||||
|
||||
// 无线配置根据设备硬件动态追加
|
||||
if (this.hasWireless) {
|
||||
fields.push(
|
||||
{ tab: 'wifisetup', type: form.Value, id: 'wifi_ssid', title: _('Wireless Network Name (SSID)'), placeholder: 'Kwrt' },
|
||||
{ tab: 'wifisetup', type: form.Value, id: 'wifi_key', title: _('Wireless Password (Key)'), password: true, placeholder: _('Leave empty for open network or 8+ characters') }
|
||||
);
|
||||
}
|
||||
|
||||
var optMap = {};
|
||||
fields.forEach(function(f) {
|
||||
opt = s.taboption(f.tab, f.type, f.id, f.title, f.desc);
|
||||
opt.cfgvalue = function() { return sys[f.id]; };
|
||||
opt.default = sys[f.id];
|
||||
|
||||
opt.write = function() {};
|
||||
opt.remove = function() {};
|
||||
|
||||
if (f.password) opt.password = true;
|
||||
if (f.datatype) opt.datatype = f.datatype;
|
||||
if (f.placeholder) opt.placeholder = f.placeholder;
|
||||
if (f.choices) Object.keys(f.choices).forEach(function(k) { opt.value(k, f.choices[k]); });
|
||||
if (f.depends) Object.keys(f.depends).forEach(function(k) { opt.depends(k, f.depends[k]); });
|
||||
|
||||
// 为无线密码增加 8~64 位安全校验,防止 hostapd 崩溃
|
||||
if (f.id === 'wifi_key') {
|
||||
opt.validate = function(section_id, value) {
|
||||
if (!value) return true;
|
||||
if (value.length < 8 || value.length > 64) {
|
||||
return _('Wireless key must be between 8 and 64 characters long');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
optMap[f.id] = opt;
|
||||
});
|
||||
|
||||
// Shortcuts GridSection 渲染
|
||||
if (this.hasNginx) {
|
||||
var so = s.taboption('shortcuts', form.SectionValue, '_shortcuts', form.GridSection, 'shortcuts', null, _('Shortcuts'));
|
||||
var ss = so.subsection;
|
||||
ss.addremove = true;
|
||||
ss.anonymous = true;
|
||||
ss.sortable = true;
|
||||
|
||||
var o_sc = ss.option(form.Value, 'shortcut', _('Shortcut'));
|
||||
o_sc.rmempty = false;
|
||||
o_sc.placeholder = 'g';
|
||||
o_sc.validate = function(section_id, value) {
|
||||
if (!value)
|
||||
return _('Shortcut phrase cannot be empty');
|
||||
if (!value.match(/^[a-zA-Z0-9_-]+$/))
|
||||
return _('Only alphanumeric characters, dashes and underscores are allowed');
|
||||
return true;
|
||||
};
|
||||
|
||||
var o_url = ss.option(form.Value, 'to_url', _('Target URL'));
|
||||
o_url.rmempty = false;
|
||||
o_url.placeholder = 'https://example.com';
|
||||
o_url.validate = function(section_id, value) {
|
||||
if (value && value.match(/^https?:\/\/.+/i)) {
|
||||
return true;
|
||||
}
|
||||
return _('Please enter a valid URL starting with http:// or https://');
|
||||
};
|
||||
|
||||
o = s.option(form.Value, 'comments', _('Comments'));
|
||||
o.optional = true;
|
||||
var o_comm = ss.option(form.Value, 'comments', _('Comments'));
|
||||
o_comm.optional = true;
|
||||
o_comm.placeholder = _('Optional');
|
||||
}
|
||||
|
||||
setTimeout("document.getElementsByClassName('cbi-button-apply')[0].children[3].children[0].value='1'", 1000)
|
||||
this.map = m;
|
||||
this.fields = fields;
|
||||
this.optMap = optMap;
|
||||
|
||||
return m.render();
|
||||
},
|
||||
|
||||
// 4. 重写 View 级别的 handleSave:精准写入发生变化的配置到 UCI 暂存区
|
||||
handleSave: function(ev) {
|
||||
var self = this;
|
||||
if (!this.map) return Promise.resolve(false);
|
||||
|
||||
return this.map.parse().then(function() {
|
||||
var cur = {};
|
||||
self.fields.forEach(function(f) {
|
||||
var v = self.optMap[f.id].formvalue('default');
|
||||
// 对 Flag 进行布尔归一化,彻底消除 null 与 '0' 比对产生的假变动
|
||||
if (f.type === form.Flag) {
|
||||
cur[f.id] = (v == '1') ? '1' : '0';
|
||||
} else {
|
||||
cur[f.id] = (v != null) ? v : '';
|
||||
}
|
||||
});
|
||||
cur.lan_dns = toArray(cur.lan_dns);
|
||||
|
||||
// 精准筛选出发生变化的常规字段
|
||||
var changed = self.fields.map(function(f) { return f.id; }).filter(function(id) {
|
||||
return !isEqual(cur[id], self.initialValues[id]);
|
||||
});
|
||||
|
||||
// 检测 shortcuts 是否有增删改
|
||||
var curShortcuts = (uci.sections('wizard', 'shortcuts') || []).map(function(sec) {
|
||||
return {
|
||||
shortcut: sec.shortcut || '',
|
||||
to_url: sec.to_url || '',
|
||||
comments: sec.comments || ''
|
||||
};
|
||||
});
|
||||
var shortcutsChanged = JSON.stringify(curShortcuts) !== JSON.stringify(self.initialShortcuts);
|
||||
|
||||
// 未做任何改动直接提示并退出
|
||||
if (changed.length === 0 && !shortcutsChanged) {
|
||||
ui.addNotification(null, E('p', _('There are no changes to apply')), 'info');
|
||||
return false;
|
||||
}
|
||||
|
||||
// A. 无线配置直接写回 wireless(全频段适配:2.4G加_2.4G,5G加_5G,6G加_6G)
|
||||
if (self.hasWireless && (has(changed, 'wifi_ssid') || has(changed, 'wifi_key'))) {
|
||||
var rawInputSsid = (cur.wifi_ssid || '').trim();
|
||||
var baseSsid = rawInputSsid.replace(/_(2\.4G|5G|6G)$/i, '').trim();
|
||||
if (!baseSsid) baseSsid = rawInputSsid;
|
||||
|
||||
var ifaces = uci.sections('wireless', 'wifi-iface') || [];
|
||||
ifaces.forEach(function(ifc) {
|
||||
if (ifc.mode === 'ap' || !ifc.mode) {
|
||||
var band = getRadioBand(ifc.device);
|
||||
var autoSsid = baseSsid ? (baseSsid + '_' + band) : '';
|
||||
|
||||
if (has(changed, 'wifi_ssid') && autoSsid) {
|
||||
uci.set('wireless', ifc['.name'], 'ssid', autoSsid);
|
||||
}
|
||||
|
||||
if (has(changed, 'wifi_key')) {
|
||||
if (cur.wifi_key) {
|
||||
uci.set('wireless', ifc['.name'], 'key', cur.wifi_key);
|
||||
var enc = ifc.encryption || uci.get('wireless', ifc['.name'], 'encryption') || '';
|
||||
if (enc.indexOf('psk') === -1 && enc.indexOf('sae') === -1) {
|
||||
uci.set('wireless', ifc['.name'], 'encryption', 'psk2');
|
||||
}
|
||||
} else {
|
||||
safeUnset('wireless', ifc['.name'], 'key');
|
||||
uci.set('wireless', ifc['.name'], 'encryption', 'none');
|
||||
}
|
||||
}
|
||||
|
||||
// 解除物理无线与接口禁用状态
|
||||
if (ifc.device) {
|
||||
safeUnset('wireless', ifc.device, 'disabled');
|
||||
}
|
||||
safeUnset('wireless', ifc['.name'], 'disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// B. WAN 模式与 PPPoE(单网卡判空保护 + LAN 防火墙动态伪装清理)
|
||||
if (has(changed, 'wan_proto') || has(changed, 'wan_pppoe_user') || has(changed, 'wan_pppoe_pass')) {
|
||||
var zones = uci.sections('firewall', 'zone') || [];
|
||||
var lanZone = null;
|
||||
for (var zi = 0; zi < zones.length; zi++) {
|
||||
if (zones[zi].name === 'lan') {
|
||||
lanZone = zones[zi];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var hasWanSec = !!uci.get('network', 'wan');
|
||||
|
||||
if (cur.wan_proto === 'siderouter') {
|
||||
if (hasWanSec) uci.set('network', 'wan', 'auto', '0');
|
||||
// 旁路由模式:开启 LAN 区域动态伪装 (masq)
|
||||
if (lanZone) uci.set('firewall', lanZone['.name'], 'masq', '1');
|
||||
} else {
|
||||
if (hasWanSec) {
|
||||
uci.set('network', 'wan', 'auto', '1');
|
||||
uci.set('network', 'wan', 'proto', cur.wan_proto);
|
||||
if (cur.wan_proto === 'pppoe') {
|
||||
uci.set('network', 'wan', 'username', cur.wan_pppoe_user);
|
||||
uci.set('network', 'wan', 'password', cur.wan_pppoe_pass);
|
||||
} else {
|
||||
safeUnset('network', 'wan', 'username');
|
||||
safeUnset('network', 'wan', 'password');
|
||||
}
|
||||
}
|
||||
// 切换回主路由模式:恢复清除 LAN 区域的动态伪装 (masq)
|
||||
if (lanZone) safeUnset('firewall', lanZone['.name'], 'masq');
|
||||
}
|
||||
}
|
||||
|
||||
// C. LAN IP(兼容 list ipaddr / option ipaddr / CIDR 掩码)
|
||||
if (has(changed, 'lan_ipaddr') && cur.lan_ipaddr) {
|
||||
var origIpRaw = uci.get('network', 'lan', 'ipaddr');
|
||||
var firstIp = Array.isArray(origIpRaw) ? origIpRaw[0] : origIpRaw;
|
||||
var mask = '';
|
||||
|
||||
if (typeof firstIp === 'string') {
|
||||
var slashIdx = firstIp.indexOf('/');
|
||||
if (slashIdx !== -1) {
|
||||
mask = '/' + firstIp.substring(slashIdx + 1).trim();
|
||||
}
|
||||
}
|
||||
|
||||
var cleanIp = (typeof cur.lan_ipaddr === 'string') ? cur.lan_ipaddr.split('/')[0].trim() : String(cur.lan_ipaddr);
|
||||
var targetVal = cleanIp + mask;
|
||||
|
||||
if (Array.isArray(origIpRaw)) {
|
||||
var newArr = origIpRaw.slice();
|
||||
newArr[0] = targetVal;
|
||||
uci.set('network', 'lan', 'ipaddr', newArr);
|
||||
} else {
|
||||
uci.set('network', 'lan', 'ipaddr', targetVal);
|
||||
if (!mask && !uci.get('network', 'lan', 'netmask')) {
|
||||
uci.set('network', 'lan', 'netmask', '255.255.255.0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// D. 自定义 LAN DNS
|
||||
if (has(changed, 'lan_dns')) {
|
||||
if (cur.lan_dns && cur.lan_dns.length > 0) {
|
||||
uci.set('network', 'lan', 'dns', cur.lan_dns);
|
||||
} else {
|
||||
safeUnset('network', 'lan', 'dns');
|
||||
}
|
||||
}
|
||||
|
||||
// E. LAN 网关(独立变更或切换至旁路由模式时更新)
|
||||
if (has(changed, 'lan_gateway') || has(changed, 'wan_proto')) {
|
||||
if (cur.wan_proto === 'siderouter') {
|
||||
if (cur.lan_gateway) {
|
||||
uci.set('network', 'lan', 'gateway', cur.lan_gateway);
|
||||
} else {
|
||||
safeUnset('network', 'lan', 'gateway');
|
||||
}
|
||||
} else {
|
||||
safeUnset('network', 'lan', 'gateway');
|
||||
}
|
||||
}
|
||||
|
||||
// F. DHCP 开关(根据勾选状态精准配置,解除模式死锁冲突)
|
||||
if (has(changed, 'dhcp') || has(changed, 'wan_proto')) {
|
||||
if (cur.dhcp === '0') {
|
||||
uci.set('dhcp', 'lan', 'ignore', '1');
|
||||
} else {
|
||||
safeUnset('dhcp', 'lan', 'ignore');
|
||||
}
|
||||
}
|
||||
|
||||
// G. IPv6
|
||||
if (has(changed, 'ipv6')) {
|
||||
var enabled = (cur.ipv6 === '1');
|
||||
if (uci.get('network', 'wan6')) {
|
||||
uci.set('network', 'wan6', 'auto', enabled ? '1' : '0');
|
||||
}
|
||||
uci.set('dhcp', 'lan', 'ra', enabled ? 'server' : 'disabled');
|
||||
uci.set('dhcp', 'lan', 'dhcpv6', enabled ? 'server' : 'disabled');
|
||||
uci.set('dhcp', 'lan', 'ndp', enabled ? 'server' : 'disabled');
|
||||
}
|
||||
|
||||
// H. HTTPS 访问重定向(由 wizard.init 调度底层生效)
|
||||
if (has(changed, 'https')) {
|
||||
uci.set('wizard', 'default', 'https', cur.https);
|
||||
}
|
||||
|
||||
// I. 页面与会话设置(持久Cookie双向同步至luci系统底层与向导配置包)
|
||||
if (has(changed, 'cookie_p')) {
|
||||
uci.set('wizard', 'default', 'persistent_cookies', cur.cookie_p);
|
||||
}
|
||||
if (has(changed, 'landing_page')) {
|
||||
uci.set('wizard', 'default', 'landing_page', cur.landing_page);
|
||||
}
|
||||
|
||||
// J. 向导专属项(完整保留 autoupgrade_fm 与 coremark)
|
||||
if (has(changed, 'autoupgrade_fm')) {
|
||||
uci.set('wizard', 'default', 'autoupgrade_fm', cur.autoupgrade_fm);
|
||||
}
|
||||
if (has(changed, 'coremark')) {
|
||||
uci.set('wizard', 'default', 'coremark', cur.coremark);
|
||||
}
|
||||
|
||||
self.initialValues = Object.assign({}, cur);
|
||||
self.initialShortcuts = curShortcuts;
|
||||
|
||||
return uci.save().then(function() {
|
||||
return true;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// 5. 重写 View 级别的 handleSaveApply:应用生效并实现 LAN IP 变更平滑迁移与自动跳转
|
||||
handleSaveApply: function(ev, mode) {
|
||||
var self = this;
|
||||
return this.handleSave(ev).then(function(hasChanges) {
|
||||
if (!hasChanges) return false;
|
||||
|
||||
var oldIp = self.initialValues.lan_ipaddr;
|
||||
var newIp = (self.optMap['lan_ipaddr'].formvalue('default') || '').split('/')[0].trim();
|
||||
var ipChanged = newIp && (newIp !== oldIp);
|
||||
|
||||
return ui.changes.apply(mode == '0').then(function() {
|
||||
if (ipChanged) {
|
||||
var sec = 15;
|
||||
ui.showModal(_('LAN IP Address Changed'), [
|
||||
E('p', _('LAN IP changed to %s. Redirecting in %d seconds...').format(newIp, sec)),
|
||||
E('div', { 'class': 'spinning', 'style': 'margin: 1em auto;' })
|
||||
]);
|
||||
var timer = window.setInterval(function() {
|
||||
sec--;
|
||||
if (sec <= 0) {
|
||||
window.clearInterval(timer);
|
||||
window.location.href = window.location.protocol + '//' + newIp + window.location.pathname;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
handleReset: null
|
||||
});
|
||||
@@ -22,7 +22,7 @@ function landing_page()
|
||||
http.redirect(luci.dispatcher.build_url("admin","quickstart"));
|
||||
end
|
||||
else
|
||||
http.redirect(luci.dispatcher.build_url("admin","status"))
|
||||
http.redirect(luci.dispatcher.build_url("admin","status", "overview"))
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,75 +1,140 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8\n"
|
||||
|
||||
msgid "Wizard"
|
||||
msgid "Setup Wizard"
|
||||
msgstr "设置向导"
|
||||
|
||||
msgid "Initial Router Setup"
|
||||
msgstr "路由器设置向导"
|
||||
msgid "Quickly configure common network, wireless, and system settings."
|
||||
msgstr "快速配置常用网络、无线与系统设置。"
|
||||
|
||||
msgid "Net Settings"
|
||||
msgstr "网络设置"
|
||||
|
||||
msgid "Firmware Settings"
|
||||
msgstr "固件与系统"
|
||||
|
||||
msgid "Wireless Settings"
|
||||
msgstr "无线设置"
|
||||
|
||||
msgid "If you are using this router for the first time, please configure it here."
|
||||
msgstr "如果你首次使用这个路由器,请在这里简单设置。"
|
||||
|
||||
msgid "Three different ways to access the Internet, please choose according to your own situation."
|
||||
msgstr "二种不同的上网方式,请您根据自身情况进行选择。"
|
||||
|
||||
msgid "Set the router's wireless name and password. For more advanced settings, please go to the Network-Wireless page."
|
||||
msgstr "设置路由器的无线名字和密码,更多高级设置请进入 网络-无线 页面。"
|
||||
|
||||
msgid "Leave empty to use ISP DNS"
|
||||
msgstr "留空则使用运营商DNS"
|
||||
|
||||
msgid "Enter the main router IP"
|
||||
msgstr "请输入主路由IP"
|
||||
|
||||
msgid "Siderouter"
|
||||
msgstr "旁路由模式"
|
||||
|
||||
msgid "To turn on this DHCP, you need to turn off the DHCP of the main router, and to turn off this DHCP, you need to manually change the gateway and DNS of all Internet devices to the IP of this bypass router"
|
||||
msgstr "开启此DHCP则需要关闭主路由的DHCP, 关闭此DHCP则需要手动将所有上网设备的网关和DNS改为此旁路由的IP"
|
||||
|
||||
msgid "Enable IPv6"
|
||||
msgstr "启用IPv6"
|
||||
|
||||
msgid "Enable/Disable IPv6"
|
||||
msgstr "一键开启/关闭 IPv6"
|
||||
|
||||
msgid "Firmware Settings"
|
||||
msgstr "固件设置"
|
||||
|
||||
msgid "Packages Auto Upgrade"
|
||||
msgstr "软件包自动更新"
|
||||
|
||||
msgid "Firmware Upgrade Notice"
|
||||
msgstr "固件更新提醒"
|
||||
|
||||
msgid "Persistent cookies"
|
||||
msgstr "持久化登录"
|
||||
|
||||
msgid "Keep the background login state to avoid the need to log in again every time the browser is closed"
|
||||
msgstr "保持后台登录状态,避免每次关闭浏览器后都需要重新登录"
|
||||
|
||||
msgid "Home"
|
||||
msgstr "首页"
|
||||
|
||||
msgid "Shortcuts"
|
||||
msgstr "快捷访问"
|
||||
|
||||
msgid "Shortcut"
|
||||
msgstr "快捷方式"
|
||||
|
||||
msgid "Target URL"
|
||||
msgstr "目标URL"
|
||||
msgid "WAN Protocol / Mode"
|
||||
msgstr "WAN 协议 / 工作模式"
|
||||
|
||||
msgid "Please enter a valid URL starting with http:// or https://"
|
||||
msgstr "URL需以http:// 或 https://开头"
|
||||
msgid "DHCP Client (Default Router)"
|
||||
msgstr "DHCP 客户端 (标准主路由模式)"
|
||||
|
||||
msgid "Force the use of HTTPS in the backend."
|
||||
msgstr "后台强制使用https"
|
||||
msgid "PPPoE Dial-up"
|
||||
msgstr "PPPoE 拨号上网"
|
||||
|
||||
msgid "Side-Router Mode (Bypass Gateway)"
|
||||
msgstr "旁路由模式 (网关旁路)"
|
||||
|
||||
msgid "PPPoE Username"
|
||||
msgstr "宽带拨号账号"
|
||||
|
||||
msgid "PPPoE Password"
|
||||
msgstr "宽带拨号密码"
|
||||
|
||||
msgid "LAN IPv4 Address"
|
||||
msgstr "LAN 口 IPv4 地址"
|
||||
|
||||
msgid "Custom DNS Server(s)"
|
||||
msgstr "自定义 DNS 服务器,留空默认运营商DNS"
|
||||
|
||||
msgid "Gateway Address"
|
||||
msgstr "网关地址"
|
||||
|
||||
msgid "Primary router IP address used when operating in side-router mode."
|
||||
msgstr "处于旁路由模式时的主路由 IP 地址。"
|
||||
|
||||
msgid "Enable DHCP Server"
|
||||
msgstr "启用 DHCP 服务"
|
||||
|
||||
msgid "Usually disabled in side-router mode to avoid IP address assignment conflicts with the main router."
|
||||
msgstr "在旁路由模式下通常关闭此项,避免与主路由的 IP 分配冲突。"
|
||||
|
||||
msgid "IPv6 Support"
|
||||
msgstr "IPv6 支持"
|
||||
|
||||
msgid "Enable or disable IPv6 router advertisements and DHCPv6."
|
||||
msgstr "启用或关闭 IPv6 路由通告与 DHCPv6 服务。"
|
||||
|
||||
msgid "Firmware Upgrade Notice"
|
||||
msgstr "固件升级提示"
|
||||
|
||||
msgid "Check and display notices for newer firmware versions."
|
||||
msgstr "检查并显示新版本固件更新提示。"
|
||||
|
||||
msgid "Run CoreMark on Boot"
|
||||
msgstr "开机 CoreMark 跑分"
|
||||
|
||||
msgid "Run CPU benchmark upon initial router initialization."
|
||||
msgstr "在路由器初始化启动时运行 CPU 基准测试。"
|
||||
|
||||
msgid "Persistent Cookie Session"
|
||||
msgstr "持久 Cookie 登录"
|
||||
|
||||
msgid "Maintain persistent login sessions in the web browser."
|
||||
msgstr "在浏览器中保持登录会话状态,避免频繁重复登录。"
|
||||
|
||||
msgid "Enforce HTTPS Access"
|
||||
msgstr "强制 HTTPS 访问"
|
||||
|
||||
msgid "Automatically redirect HTTP requests to secure HTTPS."
|
||||
msgstr "将所有 HTTP 请求自动重定向至安全加密的 HTTPS。"
|
||||
|
||||
msgid "Landing Dashboard Mode"
|
||||
msgstr "主页展示模式"
|
||||
|
||||
msgid "Default"
|
||||
msgstr "默认"
|
||||
|
||||
msgid "RouterDog"
|
||||
msgstr "路由狗"
|
||||
|
||||
msgid "NAS"
|
||||
msgstr "NAS 模式"
|
||||
|
||||
msgid "Next-NAS"
|
||||
msgstr "Next-NAS 模式"
|
||||
|
||||
msgid "Router"
|
||||
msgstr "路由器模式"
|
||||
|
||||
msgid "Wireless Network Name (SSID)"
|
||||
msgstr "无线网络名称 (SSID)"
|
||||
|
||||
msgid "Wireless Password (Key)"
|
||||
msgstr "无线网络密码"
|
||||
|
||||
msgid "Leave empty for open network or 8+ characters"
|
||||
msgstr "留空为开放无密码网络,或输入 8 位以上密码"
|
||||
|
||||
msgid "URL Shortcuts"
|
||||
msgstr "快捷方式列表"
|
||||
|
||||
msgid "Applying Changes"
|
||||
msgstr "正在应用修改"
|
||||
|
||||
msgid "Applying modified settings: %s..."
|
||||
msgstr "正在应用已修改的设置项: %s..."
|
||||
|
||||
msgid "No settings were modified. Services remain uninterrupted."
|
||||
msgstr "未检测到任何修改,无需保存或重启服务。"
|
||||
|
||||
msgid "Settings successfully applied: %s"
|
||||
msgstr "已成功应用并重载修改项: %s"
|
||||
|
||||
msgid "Failed to apply settings: %s"
|
||||
msgstr "应用设置失败: %s"
|
||||
|
||||
msgid "LAN IP Address Changed"
|
||||
msgstr "LAN 口 IP 地址已变更"
|
||||
|
||||
msgid "LAN IP changed to %s. Redirecting to the new address in %d seconds..."
|
||||
msgstr "LAN 口 IP 已变更为 %s。将在 %d 秒后自动跳转至新地址..."
|
||||
|
||||
msgid "Redirect Now"
|
||||
msgstr "立即跳转"
|
||||
@@ -1,182 +1,70 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=19
|
||||
START=99
|
||||
USE_PROCD=1
|
||||
|
||||
extra_command "reconfig" ""
|
||||
|
||||
boot() {
|
||||
exit 0;
|
||||
}
|
||||
|
||||
add_wizard() {
|
||||
local cfg="$1"
|
||||
local wan_proto wan_pppoe_user wan_pppoe_pass
|
||||
local lan_ipaddr lan_dns lan_gateway dhcp
|
||||
local device ipv6 old_ipv6 wifi_ssid wifi_key old_wifi_ssid old_wifi_key
|
||||
|
||||
config_get wan_proto "$cfg" wan_proto
|
||||
config_get lan_gateway "$cfg" lan_gateway
|
||||
config_get dhcp "$cfg" dhcp
|
||||
config_get lan_dns "$cfg" lan_dns
|
||||
config_get ipv6 "$cfg" ipv6
|
||||
config_get old_ipv6 "$cfg" old_ipv6
|
||||
config_get lan_ipaddr "$cfg" lan_ipaddr
|
||||
config_get autoupgrade_pkg "$cfg" autoupgrade_pkg
|
||||
|
||||
test -n "${lan_ipaddr}" && {
|
||||
uci set network.lan.ipaddr="${lan_ipaddr}"
|
||||
}
|
||||
|
||||
device=$(uci -q get network.wan.device || uci -q get wizard.default.old_network_device)
|
||||
case "${wan_proto}" in
|
||||
dhcp)
|
||||
uci -q del_list network.@device[0].ports="$device"
|
||||
uci -q del network.lan.gateway
|
||||
uci -q del dhcp.lan.ignore
|
||||
uci -q del network.lan.dns
|
||||
uci -q del firewall.@zone[0].masq
|
||||
uci delete network.wan
|
||||
uci set network.wan=interface
|
||||
uci set network.wan.device="$device"
|
||||
uci set network.wan.proto='dhcp'
|
||||
;;
|
||||
pppoe)
|
||||
config_get wan_pppoe_user "$cfg" wan_pppoe_user
|
||||
config_get wan_pppoe_pass "$cfg" wan_pppoe_pass
|
||||
[[ "${wan_pppoe_user} != $(uci -q get network.wan.username)" || "${wan_pppoe_pass} != $(uci -q get network.wan.password)" ]] && {
|
||||
uci -q del_list network.@device[0].ports="$device"
|
||||
uci -q del network.lan.gateway
|
||||
uci -q del dhcp.lan.ignore
|
||||
uci -q del network.lan.dns
|
||||
uci -q del firewall.@zone[0].masq
|
||||
uci delete network.wan
|
||||
uci set network.wan=interface
|
||||
uci set network.wan.proto='pppoe'
|
||||
uci set network.wan.device="$device"
|
||||
uci set network.wan.username="${wan_pppoe_user}"
|
||||
uci set network.wan.password="${wan_pppoe_pass}"
|
||||
[ -n "${ipv6}" ] && {
|
||||
uci -q set network.wan.ipv6='0'
|
||||
uci -q set network.wan.delegate='0'
|
||||
}
|
||||
}
|
||||
;;
|
||||
siderouter)
|
||||
[ "$dhcp" == "0" ] && dhcp="1"
|
||||
if [[ "${lan_gateway}" != "$(uci -q get network.lan.gateway)" || "${dhcp}" != "$(uci -q get dhcp.lan.ignore)" ]]; then
|
||||
uci -q set network.lan.gateway="${lan_gateway}"
|
||||
[ -n "$lan_dns" ] || lan_dns='223.5.5.5'
|
||||
[ -n "$dhcp" ] && uci -q set dhcp.lan.ignore="1" || uci -q del dhcp.lan.ignore
|
||||
uci -q set firewall.@zone[0].masq='1'
|
||||
WAN=$(uci -q get network.wan.device)
|
||||
uci -q add_list network.@device[0].ports="$WAN"
|
||||
uci -q add_list network.@device[0].ports="$WAN"
|
||||
uci -q set wizard.default.old_network_device="$WAN"
|
||||
;;
|
||||
esac
|
||||
|
||||
[ -n "$lan_dns" ] && {
|
||||
[ "$(uci -q get dhcp.@dnsmasq[0].noresolv)" == 1 ] && {
|
||||
uci -q del dhcp.@dnsmasq[0].noresolv
|
||||
}
|
||||
uci -q set network.wan.peerdns='0'
|
||||
uci -q set network.lan.dns="${lan_dns}"
|
||||
} || {
|
||||
uci -q del network.wan.peerdns
|
||||
uci -q del network.lan.dns
|
||||
}
|
||||
|
||||
if [ "${ipv6}" != "${old_ipv6}" ]; then
|
||||
if [ -n "${ipv6}" ]; then
|
||||
uci -q delete dhcp.lan.ra
|
||||
uci -q delete dhcp.lan.dhcpv6
|
||||
uci -q set network.lan.delegate='0'
|
||||
uci -q set network.wan.ipv6='0'
|
||||
uci -q set network.wan.delegate='0'
|
||||
uci -q delete network.globals.ula_prefix
|
||||
else
|
||||
uci -q set dhcp.lan.ra='hybrid'
|
||||
uci -q set dhcp.lan.dhcpv6='hybrid'
|
||||
uci -q del network.lan.delegate
|
||||
uci -q set network.wan.ipv6='auto'
|
||||
uci -q del network.wan.delegate
|
||||
fi
|
||||
uci -q set wizard.default.old_ipv6="${ipv6}"
|
||||
fi
|
||||
|
||||
wifi_setup_radio(){
|
||||
local radio=$1
|
||||
uci -q get wireless.${radio} >/dev/null 2>&1 && {
|
||||
if [ "$(uci -q get wireless.${radio}.band)" = "5g" ]; then
|
||||
uci -q set wireless.default_${radio}.ssid="${wifi_ssid}_5G"
|
||||
elif [ "$(uci -q get wireless.${radio}.band)" = "2g" ]; then
|
||||
uci -q set wireless.default_${radio}.ssid="${wifi_ssid}_2.4G"
|
||||
fi
|
||||
uci -q set wireless.default_${radio}.device="${radio}"
|
||||
if [ "${wifi_key}" ]; then
|
||||
uci -q set wireless.default_${radio}.encryption='psk2'
|
||||
uci -q set wireless.default_${radio}.key="${wifi_key}"
|
||||
else
|
||||
uci -q set wireless.default_${radio}.encryption='none'
|
||||
fi
|
||||
}
|
||||
}
|
||||
|
||||
config_get wifi_ssid "$cfg" wifi_ssid
|
||||
config_get wifi_key "$cfg" wifi_key
|
||||
config_get old_wifi_ssid "$cfg" old_wifi_ssid
|
||||
config_get old_wifi_key "$cfg" old_wifi_key
|
||||
|
||||
if [[ "${wifi_ssid}" != "${old_wifi_ssid}" || "${wifi_key}" != "${old_wifi_key}" ]]; then
|
||||
test -n "${wifi_ssid}" && {
|
||||
for radio in radio0 radio1 radio2 radio3; do
|
||||
wifi_setup_radio ${radio}
|
||||
# 清理历史已生成的快捷方式(防止改名或删除后遗留失效条目)
|
||||
clean_shortcuts() {
|
||||
local sec
|
||||
# 清理 nginx 中由 wizard 生成的 _sc_* server 节
|
||||
for sec in $(uci -q show nginx 2>/dev/null | sed -n 's/^nginx\.\(_sc_[^=.]*\)=server$/\1/p'); do
|
||||
uci -q delete nginx."$sec"
|
||||
done
|
||||
|
||||
# 清理 dhcp (dnsmasq) 中由 wizard 生成的 _sc_* domain 节
|
||||
for sec in $(uci -q show dhcp 2>/dev/null | sed -n 's/^dhcp\.\(_sc_[^=.]*\)=domain$/\1/p'); do
|
||||
uci -q delete dhcp."$sec"
|
||||
done
|
||||
uci commit wireless
|
||||
uci -q set wizard.default.old_wifi_ssid="${wifi_ssid}"
|
||||
uci -q set wizard.default.old_wifi_key="${wifi_key}"
|
||||
}
|
||||
|
||||
# 处理单条 shortcut 节
|
||||
process_shortcut() {
|
||||
local shortcut to_url comments lanaddr
|
||||
local cfg="$1"
|
||||
|
||||
# 获取当前 LAN 口 IP,支持 DSA 列表与 CIDR 掩码剔除
|
||||
lanaddr="$(uci -q get network.lan.ipaddr | tr ' ' '\n' | head -n1)"
|
||||
lanaddr="${lanaddr%%/*}"
|
||||
[ -z "$lanaddr" ] && lanaddr="10.0.0.1"
|
||||
|
||||
config_get shortcut "$cfg" shortcut
|
||||
config_get to_url "$cfg" to_url
|
||||
config_get comments "$cfg" comments
|
||||
|
||||
# 校验必要参数
|
||||
if [ -z "$shortcut" ] || [ -z "$to_url" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
sethttps
|
||||
# 1. 配置 Nginx 302 重定向 (仅在存在 /etc/nginx/uci.conf 时配置,仅监听80,禁止未配证书监听443)
|
||||
if [ -f /etc/nginx/uci.conf ]; then
|
||||
uci -q set nginx._sc_$shortcut=server
|
||||
uci -q set nginx._sc_$shortcut.server_name="$shortcut"
|
||||
uci -q add_list nginx._sc_$shortcut.listen="80"
|
||||
uci -q set nginx._sc_$shortcut.return="302 $to_url"
|
||||
|
||||
uci commit wizard
|
||||
uci commit dhcp
|
||||
uci commit firewall
|
||||
uci commit network
|
||||
|
||||
if [ "${autoupgrade_pkg}" != "1" ]; then
|
||||
sed -i '/opkg-upgrade/d' /etc/crontabs/root
|
||||
else
|
||||
grep -q "opkg-upgrade" /etc/crontabs/root || {
|
||||
hour="$(grep -m1 -ao '[4-6]' /dev/urandom | head -n1)"
|
||||
min="$(grep -m1 -ao '[0-5][0-9]' /dev/urandom | head -n1)"
|
||||
echo "$min $hour * * * . /etc/hotplug.d/online/51-opkg-upgrade >/dev/null 2>&1" >> /etc/crontabs/root
|
||||
}
|
||||
# 2. 配置 Dnsmasq (DHCP) 本地域名劫持
|
||||
uci -q set dhcp._sc_$shortcut=domain
|
||||
uci -q set dhcp._sc_$shortcut.name="$shortcut"
|
||||
uci -q set dhcp._sc_$shortcut.ip="$lanaddr"
|
||||
[ -n "$comments" ] && uci -q set dhcp._sc_$shortcut.comments="$comments"
|
||||
fi
|
||||
}
|
||||
|
||||
reconfig() {
|
||||
uci -q set wizard.default.wan_proto="$(uci -q get network.wan.proto)"
|
||||
uci -q set wizard.default.wan_pppoe_user="$(uci -q get network.wan.username)"
|
||||
uci -q set wizard.default.wan_pppoe_pass="$(uci -q get network.wan.password)"
|
||||
uci -q set wizard.default.lan_ipaddr="$(uci -q get network.lan.ipaddr)"
|
||||
uci -q set wizard.default.lan_gateway="$(uci -q get network.lan.gateway)"
|
||||
uci -q set wizard.default.lan_dns="$(uci -q get network.lan.dns)"
|
||||
[ "$(uci -q get dhcp.lan.ignore)" ] && uci -q set wizard.default.dhcp="0" || uci -q del wizard.default.dhcp
|
||||
uci commit wizard
|
||||
}
|
||||
|
||||
# 配置 HTTPS 访问重定向 (适配 Nginx / Lighttpd / uHTTPd)
|
||||
sethttps() {
|
||||
config_get https "$cfg" https
|
||||
if [ -n "$(command -v nginx)" ]; then
|
||||
if [ -z "`uci -q get nginx._redirect2ssl`" ]; then
|
||||
local cfg="${1:-default}"
|
||||
local https
|
||||
config_get https "$cfg" https 0
|
||||
|
||||
if [ -x /etc/init.d/nginx ] && [ -f /etc/nginx/uci.conf ]; then
|
||||
if [ "x${https}" = "x1" ]; then
|
||||
if [ -z "$(uci -q get nginx._redirect2ssl)" ]; then
|
||||
uci -q set nginx._redirect2ssl=server
|
||||
uci -q set nginx._redirect2ssl.server_name='_redirect2ssl'
|
||||
uci -q set nginx._redirect2ssl.return='302 https://$host$request_uri'
|
||||
uci -q set nginx._redirect2ssl.access_log='off; # logd openwrt'
|
||||
fi
|
||||
if [ "x${https}" = "x1" ] ; then
|
||||
uci -q del nginx.default_server.listen
|
||||
uci -q add_list nginx.default_server.listen='80'
|
||||
uci -q add_list nginx.default_server.listen='[::]:80'
|
||||
@@ -184,78 +72,79 @@ sethttps() {
|
||||
uci -q add_list nginx._redirect2ssl.listen='80 default_server'
|
||||
uci -q add_list nginx._redirect2ssl.listen='[::]:80 default_server'
|
||||
else
|
||||
uci -q del nginx._redirect2ssl.listen
|
||||
uci -q add_list nginx._redirect2ssl.listen='80'
|
||||
uci -q add_list nginx._redirect2ssl.listen='[::]:80'
|
||||
uci -q delete nginx._redirect2ssl
|
||||
uci -q del nginx.default_server.listen
|
||||
uci -q add_list nginx.default_server.listen='80 default_server'
|
||||
uci -q add_list nginx.default_server.listen='[::]:80 default_server'
|
||||
fi
|
||||
uci commit nginx
|
||||
/etc/init.d/nginx reload
|
||||
elif [ -n "$(command -v lighttpd)" ]; then
|
||||
uci -q commit nginx
|
||||
/etc/init.d/nginx reload >/dev/null 2>&1
|
||||
elif [ -x /etc/init.d/lighttpd ] && [ -f /etc/lighttpd/conf.d/82-https.conf ]; then
|
||||
if [ "x${https}" = "x1" ]; then
|
||||
sed -i "s/ # url.redirect =/ url.redirect =/" /etc/lighttpd/conf.d/82-https.conf
|
||||
sed -i "s/^[ \t]*#*[ \t]*url\.redirect[ \t]*=.*/\turl.redirect =/" /etc/lighttpd/conf.d/82-https.conf 2>/dev/null
|
||||
else
|
||||
sed -i "s/ url.redirect =/ # url.redirect =/" /etc/lighttpd/conf.d/82-https.conf
|
||||
sed -i "s/^[ \t]*url\.redirect[ \t]*=.*/\t# url.redirect =/" /etc/lighttpd/conf.d/82-https.conf 2>/dev/null
|
||||
fi
|
||||
/etc/init.d/lighttpd reload
|
||||
else
|
||||
/etc/init.d/lighttpd reload >/dev/null 2>&1
|
||||
elif [ -x /etc/init.d/uhttpd ]; then
|
||||
if [ "x${https}" = "x1" ]; then
|
||||
uci -q set uhttpd.main.redirect_https='1'
|
||||
else
|
||||
uci -q set uhttpd.main.redirect_https='0'
|
||||
fi
|
||||
uci commit uhttpd
|
||||
/etc/init.d/uhttpd reload
|
||||
uci -q commit uhttpd
|
||||
/etc/init.d/uhttpd reload >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
process_shortcut() {
|
||||
local shortcut to_url comments lanaddr
|
||||
local cfg="$1"
|
||||
lanaddr="$(uci get network.lan.ipaddr)"
|
||||
[ -n $lanaddr ] && lanaddr="$(echo "$lanaddr"| cut -d/ -f1)"
|
||||
# 提交配置并平滑重载底层服务
|
||||
reload_services() {
|
||||
uci -q commit dhcp
|
||||
if [ -x /etc/init.d/dnsmasq ]; then
|
||||
/etc/init.d/dnsmasq reload >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
config_get shortcut "$cfg" shortcut
|
||||
config_get to_url "$cfg" to_url
|
||||
config_get comments "$cfg" comments
|
||||
|
||||
uci -q set nginx._sc_$shortcut=server
|
||||
uci -q set nginx._sc_$shortcut.server_name="$shortcut"
|
||||
uci -q add_list nginx._sc_$shortcut.listen="80"
|
||||
uci -q add_list nginx._sc_$shortcut.listen="443"
|
||||
uci -q set nginx._sc_$shortcut.return="302 $to_url"
|
||||
|
||||
uci -q set dhcp._sc_$shortcut=domain
|
||||
uci -q set dhcp._sc_$shortcut.name="$shortcut"
|
||||
uci -q set dhcp._sc_$shortcut.ip="$lanaddr"
|
||||
uci -q set dhcp._sc_$shortcut.comments="$comments"
|
||||
if [ -f /etc/nginx/uci.conf ] && [ -x /etc/init.d/nginx ]; then
|
||||
uci -q commit nginx
|
||||
/etc/init.d/nginx reload >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
start() {
|
||||
start_service() {
|
||||
config_load wizard
|
||||
config_foreach add_wizard wizard
|
||||
if [ -n "$(command -v nginx)" ]; then
|
||||
for server in $(uci show nginx | grep '_sc_' | grep '=server' | cut -d. -f2 | cut -d= -f1); do
|
||||
uci delete nginx.$server
|
||||
done
|
||||
for server in $(uci show dhcp | grep '_sc_' | grep '=domain' | cut -d. -f2 | cut -d= -f1); do
|
||||
uci delete dhcp.$server
|
||||
done
|
||||
config_foreach process_shortcut shortcuts
|
||||
uci commit nginx
|
||||
uci commit dhcp
|
||||
/etc/init.d/nginx reload
|
||||
|
||||
# 1. 开机 CoreMark 跑分处理 (后台异步执行,杜绝阻塞系统启动与前端保存卡死)
|
||||
local coremark
|
||||
config_get coremark default coremark 0
|
||||
if [ "$coremark" = "1" ] && [ -x /bin/coremark ] && && [ -x /sbin/coremark ][ ! -f /etc/bench.log ] && [ ! -f /var/lock/opkg.lock ]; then
|
||||
(sleep 5;/sbin/coremark >/dev/null 2>&1) &
|
||||
fi
|
||||
(
|
||||
/etc/init.d/network restart
|
||||
/etc/init.d/firewall reload
|
||||
/etc/init.d/dnsmasq reload
|
||||
|
||||
)&
|
||||
# 2. 持久 Cookie 会话配置同步到 LuCI 底层系统
|
||||
local cookie_p
|
||||
config_get cookie_p default persistent_cookies 1
|
||||
if [ -n "$cookie_p" ]; then
|
||||
[ -z "$(uci -q get luci.sgi)" ] && uci -q set luci.sgi=sgi
|
||||
uci -q set luci.sgi.persistent_cookies="$cookie_p"
|
||||
uci -q commit luci
|
||||
fi
|
||||
|
||||
# 3. 清理并重新配置所有 Shortcuts
|
||||
clean_shortcuts
|
||||
config_foreach process_shortcut shortcuts
|
||||
|
||||
# 4. 设置 HTTPS 重定向
|
||||
sethttps default
|
||||
|
||||
# 5. 提交并重载生效服务
|
||||
reload_services
|
||||
}
|
||||
|
||||
restart() {
|
||||
start
|
||||
stop_service() {
|
||||
clean_shortcuts
|
||||
reload_services
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "wizard"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
[ ! -f "/usr/share/ucitrack/luci-app-wizard.json" ] && {
|
||||
cat > /usr/share/ucitrack/luci-app-wizard.json << EEOF
|
||||
{
|
||||
"config": "wizard",
|
||||
"init": "wizard"
|
||||
}
|
||||
EEOF
|
||||
}
|
||||
|
||||
touch /etc/config/wizard
|
||||
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
delete ucitrack.@wizard[-1]
|
||||
add ucitrack wizard
|
||||
set ucitrack.@wizard[-1].init=wizard
|
||||
commit ucitrack
|
||||
EOF
|
||||
|
||||
chmod +x /etc/init.d/wizard 2>/dev/null
|
||||
rm -f /tmp/luci-indexcache
|
||||
|
||||
exit 0
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"admin/system/initsetup": {
|
||||
"title": "Wizard",
|
||||
"order": 1,
|
||||
"admin/system/wizard": {
|
||||
"title": "Setup Wizard",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "wizard/initsetup"
|
||||
|
||||
@@ -1,14 +1,30 @@
|
||||
{
|
||||
"luci-app-wizard": {
|
||||
"description": "Grant UCI access for luci-app-wizard",
|
||||
"description": "Grant UCI and filesystem access for LuCI Setup Wizard",
|
||||
"read": {
|
||||
"uci": [
|
||||
"network",
|
||||
"wireless",
|
||||
"dhcp",
|
||||
"uhttpd",
|
||||
"firewall",
|
||||
"luci",
|
||||
"wizard"
|
||||
],
|
||||
"file": {
|
||||
"/etc/init.d/wizard reconfig": [ "exec" ]
|
||||
},
|
||||
"uci": [ "wizard", "wireless" ]
|
||||
"/etc/nginx/uci.conf": [ "stat" ]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "wizard" ]
|
||||
"uci": [
|
||||
"network",
|
||||
"wireless",
|
||||
"dhcp",
|
||||
"uhttpd",
|
||||
"firewall",
|
||||
"luci",
|
||||
"wizard"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"config": "wizard",
|
||||
"init": "wizard"
|
||||
}
|
||||
@@ -17,12 +17,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-wwand
|
||||
PKG_VERSION:=1.6.7_p5
|
||||
PKG_RELEASE:=23
|
||||
PKG_VERSION:=1.6.7_p11
|
||||
PKG_RELEASE:=24
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/ddimension/luci-app-wwand.git
|
||||
PKG_SOURCE_VERSION:=28b49c1034a2944b6b0b3a76746938eb9813cdd9
|
||||
PKG_SOURCE_VERSION:=2ca9a94506f972d89a398d560d5667a809e89d9f
|
||||
PKG_MIRROR_HASH:=skip
|
||||
|
||||
PKG_LICENSE:=GPL-2.0-only
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
.Dashboard {
|
||||
--dashboard-surface-alt: var(--background-color-medium, #f2f4f7);
|
||||
--dashboard-text: var(--text-color-high, #1f2933);
|
||||
--dashboard-text-muted: var(--text-color-medium, #6b7280);
|
||||
--dashboard-text-muted: var(--text-color-high, #4b5563);
|
||||
--dashboard-border: var(--border-color-medium, rgba(0, 0, 0, 0.12));
|
||||
--dashboard-primary: var(--primary-color-high, #1976d2);
|
||||
--dashboard-success: var(--success-color-high, #00ac59);
|
||||
@@ -33,7 +33,7 @@
|
||||
[data-darkmode="true"] .Dashboard {
|
||||
--dashboard-surface-alt: var(--background-color-medium, #262b34);
|
||||
--dashboard-text: var(--text-color-high, #e6e8eb);
|
||||
--dashboard-text-muted: var(--text-color-medium, #9aa3ad);
|
||||
--dashboard-text-muted: var(--text-color-high, #9aa3ad);
|
||||
--dashboard-border: var(--border-color-medium, rgba(255, 255, 255, 0.12));
|
||||
--dashboard-series-4: #a98cf0;
|
||||
--dashboard-series-5: #e5652c;
|
||||
@@ -46,7 +46,7 @@
|
||||
:root:not([data-darkmode="false"]) .Dashboard {
|
||||
--dashboard-surface-alt: var(--background-color-medium, #262b34);
|
||||
--dashboard-text: var(--text-color-high, #e6e8eb);
|
||||
--dashboard-text-muted: var(--text-color-medium, #9aa3ad);
|
||||
--dashboard-text-muted: var(--text-color-high, #9aa3ad);
|
||||
--dashboard-border: var(--border-color-medium, rgba(255, 255, 255, 0.12));
|
||||
--dashboard-series-4: #a98cf0;
|
||||
--dashboard-series-5: #e5652c;
|
||||
@@ -337,7 +337,7 @@
|
||||
.dashboard-bars-stack i.dashboard-bar-mid { background: var(--dashboard-warn); }
|
||||
.dashboard-bars-stack i.dashboard-bar-up { background: var(--dashboard-series-1); }
|
||||
.dashboard-bars-stack i.dashboard-bar-down { background: var(--dashboard-series-2); }
|
||||
.dashboard-bars-stack i.dashboard-bar-none { background: var(--dashboard-text-muted); }
|
||||
.dashboard-bars-stack i.dashboard-bar-none { background: var(--dashboard-border); }
|
||||
|
||||
.dashboard-bars-label {
|
||||
font-size: 10.5px;
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-proto-wwand
|
||||
PKG_VERSION:=1.6.7_p2
|
||||
PKG_RELEASE:=12
|
||||
PKG_VERSION:=1.6.7_p3
|
||||
PKG_RELEASE:=13
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/ddimension/luci-proto-wwand.git
|
||||
PKG_SOURCE_VERSION:=ebacded705a3bd5886c42c54ba66a1e26b7e4f42
|
||||
PKG_SOURCE_VERSION:=0e8dad065d301320163f65a34469d8f57b44b2da
|
||||
PKG_MIRROR_HASH:=skip
|
||||
|
||||
PKG_LICENSE:=GPL-2.0-only
|
||||
|
||||
@@ -10,7 +10,7 @@ Write narrow `[data-page]`/class-scoped overrides with native declarations, stil
|
||||
|
||||
The `@reference` + `@apply` route still _compiles_ for theme-repo patches and has real upsides — build-time validation (a typo'd utility fails the build; a typo'd `var()` fails silently at runtime) and the shared `dark:`/`md:`/`hover:` vocabulary — native is the default for the size numbers above, not a hard gate. App-shipped patches bypass the build entirely and have always been plain-CSS-only.
|
||||
|
||||
Globally-applicable chrome tweaks (e.g. icon opacity) belong in `_shared.css`, not here.
|
||||
Before writing a patch, ask whether the symptom is really page-specific. Something that breaks the same way on any app's page is a theme bug: fix it in the core CSS so every page gets the fix, and keep globally-applicable chrome tweaks (e.g. icon opacity) in `_shared.css`, not here. A patch is for what is genuinely local to one app — or, for a preference the theme should not adopt for everyone, the global `admin.css` case below.
|
||||
|
||||
## Discovery and matching
|
||||
|
||||
@@ -20,6 +20,8 @@ Globally-applicable chrome tweaks (e.g. icon opacity) belong in `_shared.css`, n
|
||||
|
||||
Because discovery is at render time, **any package — not just the theme — may drop a `<page-prefix>.css` into `luci-static/shadcn/patches/`** and it takes effect immediately, no theme rebuild required.
|
||||
|
||||
**`patches/admin.css` is therefore the global patch.** Every admin page's path starts with `admin`, so the shortest prefix matches all of them; sorting loads it first and the page-specific patches cascade on top (the login page loads no patches at all). That is how a user carries a preference the theme should not ship for everyone — a CJK-friendly monospace UI font, say — without forking it: write the file by hand on the device at `/www/luci-static/shadcn/patches/admin.css`. The theme neither builds nor installs that path, so a theme upgrade leaves it alone.
|
||||
|
||||
`PATCH_ALIASES` in `vite.config.ts` can duplicate one built payload (CSS and JS) under several page names when unrelated pages share it. It is currently empty — the log viewer ships only `patches/admin-status-logs.js`, whose prefix covers both log tabs on every supported release; its CSS is core-page styling and lives in `components/_syslog.css` inside `main.css`, where the `.syslog-view` markup contract is deliberately global so other packages can reuse the viewer. `_`-prefixed files in `patches/` are `@import`-only fragments, never entries.
|
||||
|
||||
## JS payloads
|
||||
|
||||
@@ -9,7 +9,7 @@ LUCI_TITLE:=A modern sidebar LuCI theme for OpenWrt, built with shadcn/ui design
|
||||
LUCI_DEPENDS:=+luci-base
|
||||
|
||||
PKG_VERSION:=0.6.0
|
||||
PKG_RELEASE:=6
|
||||
PKG_RELEASE:=7
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
LUCI_MINIFY_CSS:=
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ MISE_HASH_x86_64:=3832f39c325e343f81fe3d92b2447c5d1a5eea1bc85092bb7b6c2580622264
|
||||
MISE_HASH_aarch64:=06186cfbfe947049b21d58575fb0ea800cc26ed1375f20f4b678cb3a9d679437
|
||||
|
||||
PKG_NAME:=mise
|
||||
PKG_VERSION:=2026.9.11
|
||||
PKG_RELEASE:=17
|
||||
PKG_VERSION:=2026.9.12
|
||||
PKG_RELEASE:=18
|
||||
PKG_SOURCE:=$(PKG_NAME)-v$(PKG_VERSION)-linux-$(MISE_ARCH)-musl.tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/jdx/mise/releases/download/v$(PKG_VERSION)/
|
||||
PKG_HASH:=skip
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=opera-proxy
|
||||
PKG_VERSION:=1.29.0
|
||||
PKG_RELEASE:=10
|
||||
PKG_VERSION:=1.30.0
|
||||
PKG_RELEASE:=11
|
||||
|
||||
PKG_MAINTAINER:=Konstantine Shevlakov <shevlakov@132lan.ru>
|
||||
PKG_LICENSE:=MIT
|
||||
|
||||
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=shadowsocksr-libev
|
||||
PKG_VERSION:=2.5.6
|
||||
PKG_RELEASE:=4
|
||||
PKG_RELEASE:=5
|
||||
|
||||
PKG_LICENSE:=GPL-3.0
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/src/obfs/obfs.c
|
||||
+++ b/src/obfs/obfs.c
|
||||
@@ -88,7 +88,7 @@ obfs_class *new_obfs_class(const char *p
|
||||
@@ -88,10 +88,11 @@ obfs_class *new_obfs_class(const char *p
|
||||
plugin->client_decode = tls12_ticket_auth_client_decode;
|
||||
|
||||
return plugin;
|
||||
@@ -9,7 +9,19 @@
|
||||
obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs_class));
|
||||
plugin->init_data = init_data;
|
||||
plugin->new_obfs = verify_simple_new_obfs;
|
||||
@@ -115,7 +115,7 @@ obfs_class *new_obfs_class(const char *p
|
||||
+ plugin->get_overhead = get_overhead;
|
||||
plugin->get_server_info = get_server_info;
|
||||
plugin->set_server_info = set_server_info;
|
||||
plugin->dispose = verify_simple_dispose;
|
||||
@@ -106,6 +107,7 @@ obfs_class *new_obfs_class(const char *p
|
||||
obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs_class));
|
||||
plugin->init_data = auth_simple_init_data;
|
||||
plugin->new_obfs = auth_simple_new_obfs;
|
||||
+ plugin->get_overhead = get_overhead;
|
||||
plugin->get_server_info = get_server_info;
|
||||
plugin->set_server_info = set_server_info;
|
||||
plugin->dispose = auth_simple_dispose;
|
||||
@@ -115,7 +117,7 @@ obfs_class *new_obfs_class(const char *p
|
||||
plugin->client_udp_pre_encrypt = NULL;
|
||||
plugin->client_udp_post_decrypt = NULL;
|
||||
|
||||
|
||||
@@ -0,0 +1,610 @@
|
||||
From a7291ea725b74dc64c5806385100c535a15e889e Mon Sep 17 00:00:00 2001
|
||||
From: Max Lv <max.c.lv@gmail.com>
|
||||
Date: Tue, 30 Apr 2019 09:22:54 +0800
|
||||
Subject: [PATCH] Enlarge the socket buffer size to 16KB.
|
||||
|
||||
After five years, memory footprint is not a problem for most of
|
||||
low-end boxes and high-end routers now.
|
||||
|
||||
It should also improve the speed on loopback and high-speed LAN
|
||||
networks.
|
||||
---
|
||||
src/local.c | 26 +++++++++++---------------
|
||||
src/netutils.h | 2 ++
|
||||
src/redir.c | 24 ++++++++++++------------
|
||||
src/server.c | 24 ++++++++++--------------
|
||||
src/tunnel.c | 18 +++++++-----------
|
||||
5 files changed, 42 insertions(+), 52 deletions(-)
|
||||
|
||||
--- a/server/netutils.h
|
||||
+++ b/server/netutils.h
|
||||
@@ -60,6 +60,8 @@
|
||||
/** byte size of ip6 address */
|
||||
#define INET6_SIZE 16
|
||||
|
||||
+#define SOCKET_BUF_SIZE (16 * 1024) // 16KB
|
||||
+
|
||||
size_t get_sockaddr_len(struct sockaddr *addr);
|
||||
ssize_t get_sockaddr(char *host, char *port,
|
||||
struct sockaddr_storage *storage, int block,
|
||||
--- a/server/server.c
|
||||
+++ b/server/server.c
|
||||
@@ -73,10 +73,6 @@
|
||||
#define EWOULDBLOCK EAGAIN
|
||||
#endif
|
||||
|
||||
-#ifndef BUF_SIZE
|
||||
-#define BUF_SIZE 2048
|
||||
-#endif
|
||||
-
|
||||
#ifndef SSMAXCONN
|
||||
#define SSMAXCONN 1024
|
||||
#endif
|
||||
@@ -142,13 +138,13 @@ stat_update_cb(EV_P_ ev_timer *watcher,
|
||||
struct sockaddr_un svaddr, claddr;
|
||||
int sfd = -1;
|
||||
size_t msgLen;
|
||||
- char resp[BUF_SIZE];
|
||||
+ char resp[SOCKET_BUF_SIZE];
|
||||
|
||||
if (verbose) {
|
||||
LOGI("update traffic stat: tx: %" PRIu64 " rx: %" PRIu64 "", tx, rx);
|
||||
}
|
||||
|
||||
- snprintf(resp, BUF_SIZE, "stat: {\"%s\":%" PRIu64 "}", server_port, tx + rx);
|
||||
+ snprintf(resp, SOCKET_BUF_SIZE, "stat: {\"%s\":%" PRIu64 "}", server_port, tx + rx);
|
||||
msgLen = strlen(resp) + 1;
|
||||
|
||||
ss_addr_t ip_addr = { .host = NULL, .port = NULL };
|
||||
@@ -625,14 +621,14 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
ev_timer_again(EV_A_ & server->recv_ctx->watcher);
|
||||
}
|
||||
|
||||
- if (len > BUF_SIZE) {
|
||||
+ if (len > SOCKET_BUF_SIZE) {
|
||||
ERROR("out of recv buffer");
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
close_and_free_server(EV_A_ server);
|
||||
return;
|
||||
}
|
||||
|
||||
- ssize_t r = recv(server->fd, buf->array + len, BUF_SIZE - len, 0);
|
||||
+ ssize_t r = recv(server->fd, buf->array + len, SOCKET_BUF_SIZE - len, 0);
|
||||
|
||||
if (r == 0) {
|
||||
// connection closed
|
||||
@@ -712,7 +708,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
}
|
||||
|
||||
if (needsendback) {
|
||||
- size_t capacity = BUF_SIZE;
|
||||
+ size_t capacity = SOCKET_BUF_SIZE;
|
||||
char *sendback_buf = (char*)malloc(capacity);
|
||||
obfs_class *obfs_plugin = server->obfs_plugin;
|
||||
if (obfs_plugin->server_encode) {
|
||||
@@ -725,7 +721,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
}
|
||||
}
|
||||
|
||||
- int err = ss_decrypt(buf, server->d_ctx, BUF_SIZE);
|
||||
+ int err = ss_decrypt(buf, server->d_ctx, SOCKET_BUF_SIZE);
|
||||
|
||||
if (err) {
|
||||
report_addr(server->fd, MALICIOUS);
|
||||
@@ -797,7 +793,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
|
||||
if (server->stage == STAGE_HANDSHAKE) {
|
||||
size_t header_len = server->header_buf->len;
|
||||
- if (brealloc(server->header_buf, server->buf->len + header_len, BUF_SIZE) != 0) {
|
||||
+ if (brealloc(server->header_buf, server->buf->len + header_len, SOCKET_BUF_SIZE) != 0) {
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
close_and_free_server(EV_A_ server);
|
||||
return;
|
||||
@@ -809,7 +805,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
int ret = is_header_complete(server->buf);
|
||||
|
||||
if (ret == 1) {
|
||||
- brealloc(server->buf, server->header_buf->len, BUF_SIZE);
|
||||
+ brealloc(server->buf, server->header_buf->len, SOCKET_BUF_SIZE);
|
||||
memcpy(server->buf->array, server->header_buf->array, server->header_buf->len);
|
||||
server->buf->len = server->header_buf->len;
|
||||
bfree(server->header_buf);
|
||||
@@ -826,7 +822,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
|
||||
// handshake and transmit data
|
||||
if (server->stage == STAGE_STREAM) {
|
||||
- if (server->auth && !ss_check_hash(remote->buf, server->chunk, server->d_ctx, BUF_SIZE)) {
|
||||
+ if (server->auth && !ss_check_hash(remote->buf, server->chunk, server->d_ctx, SOCKET_BUF_SIZE)) {
|
||||
LOGE("hash error");
|
||||
report_addr(server->fd, BAD);
|
||||
close_and_free_server(EV_A_ server);
|
||||
@@ -1040,7 +1036,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
LOGI("connect to %s:%d", host, ntohs(port));
|
||||
}
|
||||
|
||||
- if (server->auth && !ss_check_hash(server->buf, server->chunk, server->d_ctx, BUF_SIZE)) {
|
||||
+ if (server->auth && !ss_check_hash(server->buf, server->chunk, server->d_ctx, SOCKET_BUF_SIZE)) {
|
||||
LOGE("hash error");
|
||||
report_addr(server->fd, BAD);
|
||||
close_and_free_server(EV_A_ server);
|
||||
@@ -1258,7 +1254,7 @@ remote_recv_cb(EV_P_ ev_io *w, int reven
|
||||
|
||||
ev_timer_again(EV_A_ & server->recv_ctx->watcher);
|
||||
|
||||
- ssize_t r = recv(remote->fd, server->buf->array, BUF_SIZE, 0);
|
||||
+ ssize_t r = recv(remote->fd, server->buf->array, SOCKET_BUF_SIZE, 0);
|
||||
|
||||
if (r == 0) {
|
||||
// connection closed
|
||||
@@ -1300,7 +1296,7 @@ remote_recv_cb(EV_P_ ev_io *w, int reven
|
||||
}
|
||||
}
|
||||
|
||||
- int err = ss_encrypt(server->buf, server->e_ctx, BUF_SIZE);
|
||||
+ int err = ss_encrypt(server->buf, server->e_ctx, SOCKET_BUF_SIZE);
|
||||
|
||||
if (err) {
|
||||
LOGE("invalid password or cipher");
|
||||
@@ -1459,7 +1455,7 @@ new_remote(int fd)
|
||||
ev_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, EV_READ);
|
||||
ev_io_init(&remote->send_ctx->io, remote_send_cb, fd, EV_WRITE);
|
||||
|
||||
- balloc(remote->buf, BUF_SIZE);
|
||||
+ balloc(remote->buf, SOCKET_BUF_SIZE);
|
||||
|
||||
return remote;
|
||||
}
|
||||
@@ -1538,8 +1534,8 @@ new_server(int fd, listen_ctx_t *listene
|
||||
ev_timer_init(&server->recv_ctx->watcher, server_timeout_cb,
|
||||
request_timeout, listener->timeout);
|
||||
|
||||
- balloc(server->buf, BUF_SIZE);
|
||||
- balloc(server->header_buf, BUF_SIZE);
|
||||
+ balloc(server->buf, SOCKET_BUF_SIZE);
|
||||
+ balloc(server->header_buf, SOCKET_BUF_SIZE);
|
||||
|
||||
server->chunk = (chunk_t *)malloc(sizeof(chunk_t));
|
||||
memset(server->chunk, 0, sizeof(chunk_t));
|
||||
--- a/src/local.c
|
||||
+++ b/src/local.c
|
||||
@@ -92,10 +92,6 @@
|
||||
#define EWOULDBLOCK EAGAIN
|
||||
#endif
|
||||
|
||||
-#ifndef BUF_SIZE
|
||||
-#define BUF_SIZE 2048
|
||||
-#endif
|
||||
-
|
||||
int verbose = 0;
|
||||
int keep_resolving = 1;
|
||||
|
||||
@@ -305,7 +301,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
buf = remote->buf;
|
||||
}
|
||||
|
||||
- r = recv(server->fd, buf->array + buf->len, BUF_SIZE - buf->len, 0);
|
||||
+ r = recv(server->fd, buf->array + buf->len, SOCKET_BUF_SIZE - buf->len, 0);
|
||||
|
||||
if (r == 0) {
|
||||
// connection closed
|
||||
@@ -372,7 +368,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
);
|
||||
}
|
||||
}
|
||||
- int err = ss_encrypt(&server_env->cipher, remote->buf, server->e_ctx, BUF_SIZE);
|
||||
+ int err = ss_encrypt(&server_env->cipher, remote->buf, server->e_ctx, SOCKET_BUF_SIZE);
|
||||
|
||||
if (err) {
|
||||
LOGE("server invalid password or cipher");
|
||||
@@ -592,7 +588,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
|
||||
buffer_t resp_to_send;
|
||||
buffer_t *resp_buf = &resp_to_send;
|
||||
- balloc(resp_buf, BUF_SIZE);
|
||||
+ balloc(resp_buf, SOCKET_BUF_SIZE);
|
||||
|
||||
memcpy(resp_buf->array, &response, sizeof(struct socks5_response));
|
||||
memcpy(resp_buf->array + sizeof(struct socks5_response),
|
||||
@@ -624,7 +620,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
|
||||
buffer_t ss_addr_to_send;
|
||||
buffer_t *abuf = &ss_addr_to_send;
|
||||
- balloc(abuf, BUF_SIZE);
|
||||
+ balloc(abuf, SOCKET_BUF_SIZE);
|
||||
|
||||
abuf->array[abuf->len++] = request->atyp;
|
||||
int atyp = request->atyp;
|
||||
@@ -694,7 +690,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
else if (p == tls_protocol->default_port)
|
||||
ret = tls_protocol->parse_packet(buf->array + 3 + abuf->len,
|
||||
buf->len - 3 - abuf->len, &hostname);
|
||||
- if (ret == -1 && buf->len < BUF_SIZE) {
|
||||
+ if (ret == -1 && buf->len < SOCKET_BUF_SIZE) {
|
||||
server->stage = STAGE_PARSE;
|
||||
bfree(abuf);
|
||||
return;
|
||||
@@ -880,7 +876,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
_server_info.key = enc_get_key(&server_env->cipher);
|
||||
_server_info.key_len = enc_get_key_len(&server_env->cipher);
|
||||
_server_info.tcp_mss = 1452;
|
||||
- _server_info.buffer_size = BUF_SIZE;
|
||||
+ _server_info.buffer_size = SOCKET_BUF_SIZE;
|
||||
_server_info.cipher_env = &server_env->cipher;
|
||||
|
||||
_server_info.param = server_env->obfs_param;
|
||||
@@ -909,7 +905,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
}
|
||||
// SSR end
|
||||
|
||||
- brealloc(remote->buf, buf->len + abuf->len, BUF_SIZE);
|
||||
+ brealloc(remote->buf, buf->len + abuf->len, SOCKET_BUF_SIZE);
|
||||
memcpy(remote->buf->array, abuf->array, abuf->len);
|
||||
remote->buf->len = buf->len + abuf->len;
|
||||
|
||||
@@ -1012,7 +1008,7 @@ remote_recv_cb(EV_P_ ev_io *w, int reven
|
||||
stat_update_cb();
|
||||
#endif
|
||||
|
||||
- ssize_t r = recv(remote->fd, server->buf->array, BUF_SIZE, 0);
|
||||
+ ssize_t r = recv(remote->fd, server->buf->array, SOCKET_BUF_SIZE, 0);
|
||||
|
||||
if (r == 0) {
|
||||
// connection closed
|
||||
@@ -1094,7 +1090,7 @@ remote_recv_cb(EV_P_ ev_io *w, int reven
|
||||
}
|
||||
}
|
||||
if (server->buf->len > 0) {
|
||||
- int err = ss_decrypt(&server_env->cipher, server->buf, server->d_ctx, BUF_SIZE);
|
||||
+ int err = ss_decrypt(&server_env->cipher, server->buf, server->d_ctx, SOCKET_BUF_SIZE);
|
||||
if (err) {
|
||||
LOGE("remote invalid password or cipher");
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
@@ -1220,7 +1216,7 @@ new_remote(int fd, int timeout) {
|
||||
remote->buf = ss_malloc(sizeof(buffer_t));
|
||||
remote->recv_ctx = ss_malloc(sizeof(remote_ctx_t));
|
||||
remote->send_ctx = ss_malloc(sizeof(remote_ctx_t));
|
||||
- balloc(remote->buf, BUF_SIZE);
|
||||
+ balloc(remote->buf, SOCKET_BUF_SIZE);
|
||||
memset(remote->recv_ctx, 0, sizeof(remote_ctx_t));
|
||||
memset(remote->send_ctx, 0, sizeof(remote_ctx_t));
|
||||
remote->recv_ctx->connected = 0;
|
||||
@@ -1276,7 +1272,7 @@ new_server(int fd, listen_ctx_t *profile
|
||||
server->recv_ctx = ss_malloc(sizeof(server_ctx_t));
|
||||
server->send_ctx = ss_malloc(sizeof(server_ctx_t));
|
||||
server->buf = ss_malloc(sizeof(buffer_t));
|
||||
- balloc(server->buf, BUF_SIZE);
|
||||
+ balloc(server->buf, SOCKET_BUF_SIZE);
|
||||
memset(server->recv_ctx, 0, sizeof(server_ctx_t));
|
||||
memset(server->send_ctx, 0, sizeof(server_ctx_t));
|
||||
server->stage = STAGE_INIT;
|
||||
--- a/src/netutils.h
|
||||
+++ b/src/netutils.h
|
||||
@@ -60,6 +60,8 @@
|
||||
/** byte size of ip6 address */
|
||||
#define INET6_SIZE 16
|
||||
|
||||
+#define SOCKET_BUF_SIZE (16 * 1024) // 16KB
|
||||
+
|
||||
size_t get_sockaddr_len(struct sockaddr *addr);
|
||||
ssize_t get_sockaddr(char *host, char *port,
|
||||
struct sockaddr_storage *storage, int block,
|
||||
--- a/src/redir.c
|
||||
+++ b/src/redir.c
|
||||
@@ -63,10 +63,6 @@
|
||||
#define EWOULDBLOCK EAGAIN
|
||||
#endif
|
||||
|
||||
-#ifndef BUF_SIZE
|
||||
-#define BUF_SIZE 2048
|
||||
-#endif
|
||||
-
|
||||
#ifndef IP6T_SO_ORIGINAL_DST
|
||||
#define IP6T_SO_ORIGINAL_DST 80
|
||||
#endif
|
||||
@@ -226,7 +222,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
server_def_t *server_env = server->server_env;
|
||||
|
||||
ssize_t r = recv(server->fd, remote->buf->array + remote->buf->len,
|
||||
- BUF_SIZE - remote->buf->len, 0);
|
||||
+ SOCKET_BUF_SIZE - remote->buf->len, 0);
|
||||
|
||||
if (r == 0) {
|
||||
// connection closed
|
||||
@@ -299,7 +295,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
remote->buf->len = protocol_plugin->client_pre_encrypt(server->protocol, &remote->buf->array, remote->buf->len, &remote->buf->capacity);
|
||||
}
|
||||
}
|
||||
- int err = ss_encrypt(&server_env->cipher, remote->buf, server->e_ctx, BUF_SIZE);
|
||||
+ int err = ss_encrypt(&server_env->cipher, remote->buf, server->e_ctx, SOCKET_BUF_SIZE);
|
||||
|
||||
if (err) {
|
||||
LOGE("invalid password or cipher");
|
||||
@@ -416,7 +412,7 @@ remote_recv_cb(EV_P_ ev_io *w, int reven
|
||||
|
||||
ev_timer_again(EV_A_ & remote->recv_ctx->watcher);
|
||||
|
||||
- ssize_t r = recv(remote->fd, server->buf->array, BUF_SIZE, 0);
|
||||
+ ssize_t r = recv(remote->fd, server->buf->array, SOCKET_BUF_SIZE, 0);
|
||||
|
||||
if (r == 0) {
|
||||
// connection closed
|
||||
@@ -482,7 +478,7 @@ remote_recv_cb(EV_P_ ev_io *w, int reven
|
||||
if ( server->buf->len == 0 )
|
||||
return;
|
||||
|
||||
- int err = ss_decrypt(&server_env->cipher, server->buf, server->d_ctx, BUF_SIZE);
|
||||
+ int err = ss_decrypt(&server_env->cipher, server->buf, server->d_ctx, SOCKET_BUF_SIZE);
|
||||
if (err) {
|
||||
LOGE("invalid password or cipher");
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
@@ -550,7 +546,7 @@ remote_send_cb(EV_P_ ev_io *w, int reven
|
||||
// send destaddr
|
||||
buffer_t ss_addr_to_send;
|
||||
buffer_t *abuf = &ss_addr_to_send;
|
||||
- balloc(abuf, BUF_SIZE);
|
||||
+ balloc(abuf, SOCKET_BUF_SIZE);
|
||||
|
||||
if (server->hostname_len > 0
|
||||
&& validate_hostname(server->hostname, server->hostname_len)) { // HTTP/SNI
|
||||
@@ -591,12 +587,12 @@ remote_send_cb(EV_P_ ev_io *w, int reven
|
||||
abuf->len += 2;
|
||||
|
||||
if (remote->buf->len > 0) {
|
||||
- brealloc(remote->buf, remote->buf->len + abuf->len, BUF_SIZE);
|
||||
+ brealloc(remote->buf, remote->buf->len + abuf->len, SOCKET_BUF_SIZE);
|
||||
memmove(remote->buf->array + abuf->len, remote->buf->array, remote->buf->len);
|
||||
memcpy(remote->buf->array, abuf->array, abuf->len);
|
||||
remote->buf->len += abuf->len;
|
||||
} else {
|
||||
- brealloc(remote->buf, abuf->len, BUF_SIZE);
|
||||
+ brealloc(remote->buf, abuf->len, SOCKET_BUF_SIZE);
|
||||
memcpy(remote->buf->array, abuf->array, abuf->len);
|
||||
remote->buf->len = abuf->len;
|
||||
}
|
||||
@@ -616,7 +612,7 @@ remote_send_cb(EV_P_ ev_io *w, int reven
|
||||
}
|
||||
}
|
||||
|
||||
- int err = ss_encrypt(&server_env->cipher, remote->buf, server->e_ctx, BUF_SIZE);
|
||||
+ int err = ss_encrypt(&server_env->cipher, remote->buf, server->e_ctx, SOCKET_BUF_SIZE);
|
||||
if (err) {
|
||||
LOGE("invalid password or cipher");
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
@@ -683,7 +679,7 @@ new_remote(int fd, int timeout)
|
||||
remote->buf = ss_malloc(sizeof(buffer_t));
|
||||
remote->recv_ctx = ss_malloc(sizeof(remote_ctx_t));
|
||||
remote->send_ctx = ss_malloc(sizeof(remote_ctx_t));
|
||||
- balloc(remote->buf, BUF_SIZE);
|
||||
+ balloc(remote->buf, SOCKET_BUF_SIZE);
|
||||
memset(remote->recv_ctx, 0, sizeof(remote_ctx_t));
|
||||
memset(remote->send_ctx, 0, sizeof(remote_ctx_t));
|
||||
remote->recv_ctx->connected = 0;
|
||||
@@ -741,7 +737,7 @@ new_server(int fd, listen_ctx_t* profile
|
||||
server->recv_ctx = ss_malloc(sizeof(server_ctx_t));
|
||||
server->send_ctx = ss_malloc(sizeof(server_ctx_t));
|
||||
server->buf = ss_malloc(sizeof(buffer_t));
|
||||
- balloc(server->buf, BUF_SIZE);
|
||||
+ balloc(server->buf, SOCKET_BUF_SIZE);
|
||||
memset(server->recv_ctx, 0, sizeof(server_ctx_t));
|
||||
memset(server->send_ctx, 0, sizeof(server_ctx_t));
|
||||
server->recv_ctx->connected = 0;
|
||||
@@ -1028,7 +1024,7 @@ accept_cb(EV_P_ ev_io *w, int revents)
|
||||
_server_info.key = enc_get_key(&server_env->cipher);
|
||||
_server_info.key_len = enc_get_key_len(&server_env->cipher);
|
||||
_server_info.tcp_mss = 1452;
|
||||
- _server_info.buffer_size = BUF_SIZE;
|
||||
+ _server_info.buffer_size = SOCKET_BUF_SIZE;
|
||||
_server_info.cipher_env = &server_env->cipher;
|
||||
|
||||
if (server_env->obfs_plugin) {
|
||||
--- a/src/server.c
|
||||
+++ b/src/server.c
|
||||
@@ -71,10 +71,6 @@
|
||||
#define EWOULDBLOCK EAGAIN
|
||||
#endif
|
||||
|
||||
-#ifndef BUF_SIZE
|
||||
-#define BUF_SIZE 2048
|
||||
-#endif
|
||||
-
|
||||
#ifndef SSMAXCONN
|
||||
#define SSMAXCONN 1024
|
||||
#endif
|
||||
@@ -138,13 +134,13 @@ stat_update_cb(EV_P_ ev_timer *watcher,
|
||||
|
||||
int sfd = -1;
|
||||
size_t msgLen;
|
||||
- char resp[BUF_SIZE];
|
||||
+ char resp[SOCKET_BUF_SIZE];
|
||||
|
||||
if (verbose) {
|
||||
LOGI("update traffic stat: tx: %" PRIu64 " rx: %" PRIu64 "", tx, rx);
|
||||
}
|
||||
|
||||
- snprintf(resp, BUF_SIZE, "stat: {\"%s\":%" PRIu64 "}", server_port, tx + rx);
|
||||
+ snprintf(resp, SOCKET_BUF_SIZE, "stat: {\"%s\":%" PRIu64 "}", server_port, tx + rx);
|
||||
msgLen = strlen(resp) + 1;
|
||||
|
||||
ss_addr_t ip_addr = { .host = NULL, .port = NULL };
|
||||
@@ -600,14 +596,14 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
ev_timer_again(EV_A_ & server->recv_ctx->watcher);
|
||||
}
|
||||
|
||||
- if (len > BUF_SIZE) {
|
||||
+ if (len > SOCKET_BUF_SIZE) {
|
||||
ERROR("out of recv buffer");
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
close_and_free_server(EV_A_ server);
|
||||
return;
|
||||
}
|
||||
|
||||
- ssize_t r = recv(server->fd, buf->array + len, BUF_SIZE - len, 0);
|
||||
+ ssize_t r = recv(server->fd, buf->array + len, SOCKET_BUF_SIZE - len, 0);
|
||||
|
||||
if (r == 0) {
|
||||
// connection closed
|
||||
@@ -649,7 +645,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
buf->len = r;
|
||||
}
|
||||
|
||||
- int err = ss_decrypt(&cipher_env, buf, server->d_ctx, BUF_SIZE);
|
||||
+ int err = ss_decrypt(&cipher_env, buf, server->d_ctx, SOCKET_BUF_SIZE);
|
||||
|
||||
if (err) {
|
||||
report_addr(server->fd, MALICIOUS);
|
||||
@@ -678,7 +674,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
|
||||
if (server->stage == STAGE_HANDSHAKE) {
|
||||
size_t header_len = server->header_buf->len;
|
||||
- brealloc(server->header_buf, server->buf->len + header_len, BUF_SIZE);
|
||||
+ brealloc(server->header_buf, server->buf->len + header_len, SOCKET_BUF_SIZE);
|
||||
memcpy(server->header_buf->array + header_len,
|
||||
server->buf->array, server->buf->len);
|
||||
server->header_buf->len = server->buf->len + header_len;
|
||||
@@ -686,7 +682,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
int ret = is_header_complete(server->buf);
|
||||
|
||||
if (ret == 1) {
|
||||
- brealloc(server->buf, server->header_buf->len, BUF_SIZE);
|
||||
+ brealloc(server->buf, server->header_buf->len, SOCKET_BUF_SIZE);
|
||||
memcpy(server->buf->array, server->header_buf->array, server->header_buf->len);
|
||||
server->buf->len = server->header_buf->len;
|
||||
bfree(server->header_buf);
|
||||
@@ -1086,7 +1082,7 @@ remote_recv_cb(EV_P_ ev_io *w, int reven
|
||||
|
||||
ev_timer_again(EV_A_ & server->recv_ctx->watcher);
|
||||
|
||||
- ssize_t r = recv(remote->fd, server->buf->array, BUF_SIZE, 0);
|
||||
+ ssize_t r = recv(remote->fd, server->buf->array, SOCKET_BUF_SIZE, 0);
|
||||
|
||||
if (r == 0) {
|
||||
// connection closed
|
||||
@@ -1112,7 +1108,7 @@ remote_recv_cb(EV_P_ ev_io *w, int reven
|
||||
rx += r;
|
||||
|
||||
server->buf->len = r;
|
||||
- int err = ss_encrypt(&cipher_env, server->buf, server->e_ctx, BUF_SIZE);
|
||||
+ int err = ss_encrypt(&cipher_env, server->buf, server->e_ctx, SOCKET_BUF_SIZE);
|
||||
|
||||
if (err) {
|
||||
LOGE("invalid password or cipher");
|
||||
@@ -1249,7 +1245,7 @@ new_remote(int fd)
|
||||
remote->recv_ctx = ss_malloc(sizeof(remote_ctx_t));
|
||||
remote->send_ctx = ss_malloc(sizeof(remote_ctx_t));
|
||||
remote->buf = ss_malloc(sizeof(buffer_t));
|
||||
- balloc(remote->buf, BUF_SIZE);
|
||||
+ balloc(remote->buf, SOCKET_BUF_SIZE);
|
||||
memset(remote->recv_ctx, 0, sizeof(remote_ctx_t));
|
||||
memset(remote->send_ctx, 0, sizeof(remote_ctx_t));
|
||||
remote->fd = fd;
|
||||
@@ -1313,8 +1309,8 @@ new_server(int fd, listen_ctx_t *listene
|
||||
server->header_buf = ss_malloc(sizeof(buffer_t));
|
||||
memset(server->recv_ctx, 0, sizeof(server_ctx_t));
|
||||
memset(server->send_ctx, 0, sizeof(server_ctx_t));
|
||||
- balloc(server->buf, BUF_SIZE);
|
||||
- balloc(server->header_buf, BUF_SIZE);
|
||||
+ balloc(server->buf, SOCKET_BUF_SIZE);
|
||||
+ balloc(server->header_buf, SOCKET_BUF_SIZE);
|
||||
server->fd = fd;
|
||||
server->recv_ctx->server = server;
|
||||
server->recv_ctx->connected = 0;
|
||||
--- a/src/tunnel.c
|
||||
+++ b/src/tunnel.c
|
||||
@@ -67,10 +67,6 @@
|
||||
#define EWOULDBLOCK EAGAIN
|
||||
#endif
|
||||
|
||||
-#ifndef BUF_SIZE
|
||||
-#define BUF_SIZE 2048
|
||||
-#endif
|
||||
-
|
||||
#include "includeobfs.h" // I don't want to modify makefile
|
||||
|
||||
static void accept_cb(EV_P_ ev_io *w, int revents);
|
||||
@@ -180,7 +176,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
return;
|
||||
}
|
||||
|
||||
- ssize_t r = recv(server->fd, remote->buf->array, BUF_SIZE, 0);
|
||||
+ ssize_t r = recv(server->fd, remote->buf->array, SOCKET_BUF_SIZE, 0);
|
||||
|
||||
if (r == 0) {
|
||||
// connection closed
|
||||
@@ -209,7 +205,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven
|
||||
remote->buf->len = protocol_plugin->client_pre_encrypt(server->protocol, &remote->buf->array, remote->buf->len, &remote->buf->capacity);
|
||||
}
|
||||
}
|
||||
- int err = ss_encrypt(&cipher_env, remote->buf, server->e_ctx, BUF_SIZE);
|
||||
+ int err = ss_encrypt(&cipher_env, remote->buf, server->e_ctx, SOCKET_BUF_SIZE);
|
||||
|
||||
if (err) {
|
||||
LOGE("server invalid password or cipher");
|
||||
@@ -325,7 +321,7 @@ remote_recv_cb(EV_P_ ev_io *w, int reven
|
||||
remote_t *remote = remote_recv_ctx->remote;
|
||||
server_t *server = remote->server;
|
||||
|
||||
- ssize_t r = recv(remote->fd, server->buf->array, BUF_SIZE, 0);
|
||||
+ ssize_t r = recv(remote->fd, server->buf->array, SOCKET_BUF_SIZE, 0);
|
||||
|
||||
if (r == 0) {
|
||||
// connection closed
|
||||
@@ -391,7 +387,7 @@ remote_recv_cb(EV_P_ ev_io *w, int reven
|
||||
if ( server->buf->len == 0 )
|
||||
return;
|
||||
|
||||
- int err = ss_decrypt(&cipher_env, server->buf, server->d_ctx, BUF_SIZE);
|
||||
+ int err = ss_decrypt(&cipher_env, server->buf, server->d_ctx, SOCKET_BUF_SIZE);
|
||||
|
||||
if (err) {
|
||||
LOGE("invalid password or cipher");
|
||||
@@ -456,7 +452,7 @@ remote_send_cb(EV_P_ ev_io *w, int reven
|
||||
|
||||
buffer_t ss_addr_to_send;
|
||||
buffer_t *abuf = &ss_addr_to_send;
|
||||
- balloc(abuf, BUF_SIZE);
|
||||
+ balloc(abuf, SOCKET_BUF_SIZE);
|
||||
|
||||
ss_addr_t *sa = &server->destaddr;
|
||||
struct cork_ip ip;
|
||||
@@ -503,12 +499,12 @@ remote_send_cb(EV_P_ ev_io *w, int reven
|
||||
abuf->len += 2;
|
||||
|
||||
if (remote->buf->len > 0) {
|
||||
- brealloc(remote->buf, remote->buf->len + abuf->len, BUF_SIZE);
|
||||
+ brealloc(remote->buf, remote->buf->len + abuf->len, SOCKET_BUF_SIZE);
|
||||
memmove(remote->buf->array + abuf->len, remote->buf->array, remote->buf->len);
|
||||
memcpy(remote->buf->array, abuf->array, abuf->len);
|
||||
remote->buf->len += abuf->len;
|
||||
} else {
|
||||
- brealloc(remote->buf, abuf->len, BUF_SIZE);
|
||||
+ brealloc(remote->buf, abuf->len, SOCKET_BUF_SIZE);
|
||||
memcpy(remote->buf->array, abuf->array, abuf->len);
|
||||
remote->buf->len = abuf->len;
|
||||
}
|
||||
@@ -528,7 +524,7 @@ remote_send_cb(EV_P_ ev_io *w, int reven
|
||||
}
|
||||
}
|
||||
|
||||
- int err = ss_encrypt(&cipher_env, remote->buf, server->e_ctx, BUF_SIZE);
|
||||
+ int err = ss_encrypt(&cipher_env, remote->buf, server->e_ctx, SOCKET_BUF_SIZE);
|
||||
if (err) {
|
||||
LOGE("invalid password or cipher");
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
@@ -610,7 +606,7 @@ new_remote(int fd, int timeout)
|
||||
remote->buf = ss_malloc(sizeof(buffer_t));
|
||||
remote->recv_ctx = ss_malloc(sizeof(remote_ctx_t));
|
||||
remote->send_ctx = ss_malloc(sizeof(remote_ctx_t));
|
||||
- balloc(remote->buf, BUF_SIZE);
|
||||
+ balloc(remote->buf, SOCKET_BUF_SIZE);
|
||||
memset(remote->recv_ctx, 0, sizeof(remote_ctx_t));
|
||||
memset(remote->send_ctx, 0, sizeof(remote_ctx_t));
|
||||
remote->fd = fd;
|
||||
@@ -665,7 +661,7 @@ new_server(int fd, int method)
|
||||
server->buf = ss_malloc(sizeof(buffer_t));
|
||||
server->recv_ctx = ss_malloc(sizeof(server_ctx_t));
|
||||
server->send_ctx = ss_malloc(sizeof(server_ctx_t));
|
||||
- balloc(server->buf, BUF_SIZE);
|
||||
+ balloc(server->buf, SOCKET_BUF_SIZE);
|
||||
memset(server->recv_ctx, 0, sizeof(server_ctx_t));
|
||||
memset(server->send_ctx, 0, sizeof(server_ctx_t));
|
||||
server->fd = fd;
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
From 38e83d6b5d4648fb87ae6680c8fa159df54c73ce Mon Sep 17 00:00:00 2001
|
||||
From: Max Lv <max.c.lv@gmail.com>
|
||||
Date: Tue, 30 Apr 2019 09:34:31 +0800
|
||||
Subject: [PATCH] Make sure socket buffer size no larger than max chunk size
|
||||
|
||||
---
|
||||
src/netutils.h | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/server/netutils.h
|
||||
+++ b/server/netutils.h
|
||||
@@ -60,7 +60,7 @@
|
||||
/** byte size of ip6 address */
|
||||
#define INET6_SIZE 16
|
||||
|
||||
-#define SOCKET_BUF_SIZE (16 * 1024) // 16KB
|
||||
+#define SOCKET_BUF_SIZE (16 * 1024 - 1) // 16383 Byte, equals to the max chunk size
|
||||
|
||||
size_t get_sockaddr_len(struct sockaddr *addr);
|
||||
ssize_t get_sockaddr(char *host, char *port,
|
||||
--- a/src/netutils.h
|
||||
+++ b/src/netutils.h
|
||||
@@ -60,7 +60,7 @@
|
||||
/** byte size of ip6 address */
|
||||
#define INET6_SIZE 16
|
||||
|
||||
-#define SOCKET_BUF_SIZE (16 * 1024) // 16KB
|
||||
+#define SOCKET_BUF_SIZE (16 * 1024 - 1) // 16383 Byte, equals to the max chunk size
|
||||
|
||||
size_t get_sockaddr_len(struct sockaddr *addr);
|
||||
ssize_t get_sockaddr(char *host, char *port,
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
--- a/server/auth.c
|
||||
+++ b/server/auth.c
|
||||
@@ -27,7 +27,7 @@ typedef struct auth_simple_local_data {
|
||||
|
||||
void auth_simple_local_data_init(auth_simple_local_data* local) {
|
||||
local->has_sent_header = 0;
|
||||
- local->recv_buffer = (char*)malloc(16384);
|
||||
+ local->recv_buffer = (char*)malloc(SSR_RECV_BUF_SIZE);
|
||||
local->recv_buffer_size = 0;
|
||||
local->recv_id = 1;
|
||||
local->pack_id = 1;
|
||||
@@ -173,7 +173,7 @@ int auth_simple_client_post_decrypt(obfs
|
||||
char *plaindata = *pplaindata;
|
||||
auth_simple_local_data *local = (auth_simple_local_data*)self->l_data;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
@@ -290,7 +290,7 @@ int auth_sha1_client_post_decrypt(obfs *
|
||||
char *plaindata = *pplaindata;
|
||||
auth_simple_local_data *local = (auth_simple_local_data*)self->l_data;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
@@ -428,7 +428,7 @@ int auth_sha1_v2_client_post_decrypt(obf
|
||||
char *plaindata = *pplaindata;
|
||||
auth_simple_local_data *local = (auth_simple_local_data*)self->l_data;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
@@ -586,7 +586,7 @@ int auth_sha1_v4_client_post_decrypt(obf
|
||||
char *plaindata = *pplaindata;
|
||||
auth_simple_local_data *local = (auth_simple_local_data*)self->l_data;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
@@ -843,7 +843,7 @@ int auth_aes128_sha1_client_post_decrypt
|
||||
auth_simple_local_data *local = (auth_simple_local_data*)self->l_data;
|
||||
//server_info *server = (server_info*)&self->server;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
--- a/server/obfs.h
|
||||
+++ b/server/obfs.h
|
||||
@@ -10,6 +10,9 @@
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
+/* Keep room for a TCP read, a pending obfs record and a protocol frame. */
|
||||
+#define SSR_RECV_BUF_SIZE (32 * 1024)
|
||||
+
|
||||
typedef struct server_info {
|
||||
char host[64];
|
||||
uint16_t port;
|
||||
--- a/server/verify.c
|
||||
+++ b/server/verify.c
|
||||
@@ -9,7 +9,7 @@ typedef struct verify_simple_local_data
|
||||
}verify_simple_local_data;
|
||||
|
||||
void verify_simple_local_data_init(verify_simple_local_data* local) {
|
||||
- local->recv_buffer = (char*)malloc(16384);
|
||||
+ local->recv_buffer = (char*)malloc(SSR_RECV_BUF_SIZE);
|
||||
local->recv_buffer_size = 0;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ int verify_simple_client_post_decrypt(ob
|
||||
char *plaindata = *pplaindata;
|
||||
verify_simple_local_data *local = (verify_simple_local_data*)self->l_data;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
@@ -144,7 +144,7 @@ int verify_simple_server_post_decrypt(ob
|
||||
char *plaindata = *pplaindata;
|
||||
verify_simple_local_data *local = (verify_simple_local_data*)self->l_data;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
{
|
||||
LOGE("verify_simple: wrong buf length %d", local->recv_buffer_size + datalength);
|
||||
return -1;
|
||||
--- a/src/obfs/auth.c
|
||||
+++ b/src/obfs/auth.c
|
||||
@@ -33,7 +33,7 @@ typedef struct auth_simple_local_data {
|
||||
|
||||
void auth_simple_local_data_init(auth_simple_local_data* local) {
|
||||
local->has_sent_header = 0;
|
||||
- local->recv_buffer = (char*)malloc(16384);
|
||||
+ local->recv_buffer = (char*)malloc(SSR_RECV_BUF_SIZE);
|
||||
local->recv_buffer_size = 0;
|
||||
local->recv_id = 1;
|
||||
local->pack_id = 1;
|
||||
@@ -176,7 +176,7 @@ int auth_simple_client_post_decrypt(obfs
|
||||
char *plaindata = *pplaindata;
|
||||
auth_simple_local_data *local = (auth_simple_local_data*)self->l_data;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
@@ -293,7 +293,7 @@ int auth_sha1_client_post_decrypt(obfs *
|
||||
char *plaindata = *pplaindata;
|
||||
auth_simple_local_data *local = (auth_simple_local_data*)self->l_data;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
@@ -431,7 +431,7 @@ int auth_sha1_v2_client_post_decrypt(obf
|
||||
char *plaindata = *pplaindata;
|
||||
auth_simple_local_data *local = (auth_simple_local_data*)self->l_data;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
@@ -589,7 +589,7 @@ int auth_sha1_v4_client_post_decrypt(obf
|
||||
char *plaindata = *pplaindata;
|
||||
auth_simple_local_data *local = (auth_simple_local_data*)self->l_data;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
@@ -859,7 +859,7 @@ int auth_aes128_sha1_client_post_decrypt
|
||||
auth_simple_local_data *local = (auth_simple_local_data*)self->l_data;
|
||||
//server_info *server = (server_info*)&self->server;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
--- a/src/obfs/auth_chain.c
|
||||
+++ b/src/obfs/auth_chain.c
|
||||
@@ -130,7 +130,7 @@ typedef struct auth_chain_local_data {
|
||||
|
||||
void auth_chain_local_data_init(auth_chain_local_data *local) {
|
||||
local->has_sent_header = 0;
|
||||
- local->recv_buffer = (char *) malloc(16384);
|
||||
+ local->recv_buffer = (char *) malloc(SSR_RECV_BUF_SIZE);
|
||||
local->recv_buffer_size = 0;
|
||||
local->recv_id = 1;
|
||||
local->pack_id = 1;
|
||||
@@ -904,7 +904,7 @@ int auth_chain_a_client_post_decrypt(obf
|
||||
auth_chain_local_data *local = (auth_chain_local_data *) self->l_data;
|
||||
server_info *server = (server_info *) &self->server;
|
||||
uint8_t *recv_buffer = (uint8_t *) local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
--- a/src/obfs/obfs.h
|
||||
+++ b/src/obfs/obfs.h
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
#define OBFS_HMAC_SHA1_LEN 10
|
||||
|
||||
+/* Keep room for a TCP read, a pending obfs record and a protocol frame. */
|
||||
+#define SSR_RECV_BUF_SIZE (32 * 1024)
|
||||
+
|
||||
typedef struct {
|
||||
char host[256];
|
||||
uint16_t port;
|
||||
--- a/src/obfs/verify.c
|
||||
+++ b/src/obfs/verify.c
|
||||
@@ -12,7 +12,7 @@ typedef struct verify_simple_local_data
|
||||
}verify_simple_local_data;
|
||||
|
||||
void verify_simple_local_data_init(verify_simple_local_data* local) {
|
||||
- local->recv_buffer = (char*)malloc(16384);
|
||||
+ local->recv_buffer = (char*)malloc(SSR_RECV_BUF_SIZE);
|
||||
local->recv_buffer_size = 0;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ int verify_simple_client_post_decrypt(ob
|
||||
char *plaindata = *pplaindata;
|
||||
verify_simple_local_data *local = (verify_simple_local_data*)self->l_data;
|
||||
uint8_t * recv_buffer = (uint8_t *)local->recv_buffer;
|
||||
- if (local->recv_buffer_size + datalength > 16384)
|
||||
+ if (local->recv_buffer_size + datalength > SSR_RECV_BUF_SIZE)
|
||||
return -1;
|
||||
memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength);
|
||||
local->recv_buffer_size += datalength;
|
||||
+44
-3
@@ -11,6 +11,7 @@
|
||||
# wwand-ncm - NCM/ECM backend (DEPENDS wwand)
|
||||
# wwand-mhi - PCIe/MHI transport + drivers (DEPENDS wwand; add a backend)
|
||||
# wwand-esim - eSIM management (DEPENDS wwand + lpac; no backend)
|
||||
# wwand-gps - GNSS/NMEA glue for ugps (DEPENDS wwand + ugps; no backend)
|
||||
# A typical QMI router installs `wwand-qmi` (which pulls in `wwand`); add
|
||||
# `wwand-mbim` / `wwand-ncm` for those modems, `wwand-mhi` for a PCIe/MHI modem.
|
||||
# The ucode tree ships PRECOMPILED to bytecode (repo-root CMakeLists.txt,
|
||||
@@ -49,12 +50,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=wwand
|
||||
PKG_VERSION:=1.6.7_p46
|
||||
PKG_RELEASE:=47
|
||||
PKG_VERSION:=1.6.7_p55
|
||||
PKG_RELEASE:=48
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/ddimension/wwand.git
|
||||
PKG_SOURCE_VERSION:=6ae4cab239ecd02e570d7cd51584d60101704732
|
||||
PKG_SOURCE_VERSION:=aaf2ba064bbf751eaa0237e00f94c1619f05b648
|
||||
PKG_MIRROR_HASH:=skip
|
||||
|
||||
PKG_LICENSE:=GPL-2.0-only
|
||||
@@ -437,6 +438,45 @@ define Package/wwand-esim/install
|
||||
$(INSTALL_DATA) $(WWAND_UCODE)/esim_bridge.uc $(1)$(UCDIR)/
|
||||
endef
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# wwand-gps: point ugps at the modem's NMEA port. Backend-neutral — finding the
|
||||
# port and starting the receiver are both in the base package, and neither
|
||||
# depends on the control protocol.
|
||||
# ---------------------------------------------------------------------------
|
||||
define Package/wwand-gps
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
SUBMENU:=WWAN
|
||||
TITLE:=GNSS/NMEA glue for wwand (ugps)
|
||||
# ugps is OpenWrt base: it parses NMEA, can set the clock, and publishes a
|
||||
# `gps` ubus object. It knows nothing about modems, and it reads its tty from
|
||||
# a STATIC /etc/config/gps — which is exactly the gap this package fills,
|
||||
# because wwand's NMEA port is discovered and can move between boots or when
|
||||
# a modem is replaced.
|
||||
DEPENDS:=+wwand +ugps
|
||||
endef
|
||||
|
||||
define Package/wwand-gps/description
|
||||
Joins three pieces that already existed with nothing between them: wwand finds
|
||||
the modem's NMEA port during enumeration (reported as `gps_port`), `option
|
||||
gnss` starts the receiver with the vendor AT command — QMI's LOC service is
|
||||
broken on Quectel and AT is what works, and only wwand has the port — and ugps
|
||||
reads the NMEA and publishes a position.
|
||||
|
||||
What this adds is the config write that connects them, plus a `modem_gps` ubus
|
||||
method that answers with both halves at once: the port and receiver state wwand
|
||||
knows, merged with whatever ugps reports.
|
||||
|
||||
Good citizen: it manages exactly one `config gps` section and only one it
|
||||
created itself (marked `option wwand '1'`). An operator's own section — a hat
|
||||
GPS on a serial port, a second receiver — is never touched or repointed.
|
||||
endef
|
||||
|
||||
define Package/wwand-gps/install
|
||||
$(INSTALL_DIR) $(1)$(UCDIR)
|
||||
$(INSTALL_DATA) $(WWAND_UCODE)/gps.uc $(1)$(UCDIR)/
|
||||
endef
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# wwand-datapath-rmnet_nss: Qualcomm NSS offload behind the VENDOR qmi_wwan_q.
|
||||
# A datapath add-on, not a control backend — it plugs into wwand's datapath
|
||||
@@ -527,6 +567,7 @@ $(eval $(call BuildPackage,wwand-mbim))
|
||||
$(eval $(call BuildPackage,wwand-ncm))
|
||||
$(eval $(call BuildPackage,wwand-mhi))
|
||||
$(eval $(call BuildPackage,wwand-esim))
|
||||
$(eval $(call BuildPackage,wwand-gps))
|
||||
$(eval $(call BuildPackage,wwand-apntest))
|
||||
define Package/wwand-datapath-rmnet_nss_mhi
|
||||
SECTION:=net
|
||||
|
||||
Reference in new issue
Block a user