mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-03 22:18:39 +00:00
190 lines
4.2 KiB
Go
190 lines
4.2 KiB
Go
package router
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"os/exec"
|
|
"sync/atomic"
|
|
|
|
"github.com/kardianos/service"
|
|
|
|
"github.com/Control-D-Inc/ctrld"
|
|
"github.com/Control-D-Inc/ctrld/internal/router/ddwrt"
|
|
"github.com/Control-D-Inc/ctrld/internal/router/edgeos"
|
|
"github.com/Control-D-Inc/ctrld/internal/router/firewalla"
|
|
"github.com/Control-D-Inc/ctrld/internal/router/merlin"
|
|
"github.com/Control-D-Inc/ctrld/internal/router/openwrt"
|
|
"github.com/Control-D-Inc/ctrld/internal/router/pfsense"
|
|
"github.com/Control-D-Inc/ctrld/internal/router/synology"
|
|
"github.com/Control-D-Inc/ctrld/internal/router/tomato"
|
|
"github.com/Control-D-Inc/ctrld/internal/router/ubios"
|
|
)
|
|
|
|
// Service is the interface to manage ctrld service on router.
|
|
type Service interface {
|
|
ConfigureService(*service.Config) error
|
|
Install(*service.Config) error
|
|
Uninstall(*service.Config) error
|
|
}
|
|
|
|
// Config is the interface to manage ctrld config on router.
|
|
type Config interface {
|
|
Configure() error
|
|
}
|
|
|
|
// Router is the interface for managing ctrld running on router.
|
|
type Router interface {
|
|
Service
|
|
Config
|
|
|
|
PreRun() error
|
|
Setup() error
|
|
Cleanup() error
|
|
}
|
|
|
|
// New returns new Router interface.
|
|
func New(cfg *ctrld.Config) Router {
|
|
switch Name() {
|
|
case ddwrt.Name:
|
|
return ddwrt.New(cfg)
|
|
case merlin.Name:
|
|
return merlin.New(cfg)
|
|
case openwrt.Name:
|
|
return openwrt.New(cfg)
|
|
case edgeos.Name:
|
|
return edgeos.New(cfg)
|
|
case ubios.Name:
|
|
return ubios.New(cfg)
|
|
case synology.Name:
|
|
return synology.New(cfg)
|
|
case tomato.Name:
|
|
return tomato.New(cfg)
|
|
case pfsense.Name:
|
|
return pfsense.New(cfg)
|
|
case firewalla.Name:
|
|
return firewalla.New(cfg)
|
|
}
|
|
return NewDummyRouter()
|
|
}
|
|
|
|
// IsGLiNet reports whether the router is an GL.iNet router.
|
|
func IsGLiNet() bool {
|
|
if Name() != openwrt.Name {
|
|
return false
|
|
}
|
|
buf, _ := os.ReadFile("/proc/version")
|
|
// The output of /proc/version contains "(glinet@glinet)".
|
|
return bytes.Contains(buf, []byte(" (glinet"))
|
|
}
|
|
|
|
// IsOldOpenwrt reports whether the router is an "old" version of Openwrt,
|
|
// aka versions which don't have "service" command.
|
|
func IsOldOpenwrt() bool {
|
|
if Name() != openwrt.Name {
|
|
return false
|
|
}
|
|
cmd, _ := exec.LookPath("service")
|
|
return cmd == ""
|
|
}
|
|
|
|
var routerPlatform atomic.Pointer[router]
|
|
|
|
type router struct {
|
|
name string
|
|
sendClientInfo bool
|
|
}
|
|
|
|
// IsSupported reports whether the given platform is supported by ctrld.
|
|
func IsSupported(platform string) bool {
|
|
switch platform {
|
|
case ddwrt.Name,
|
|
edgeos.Name,
|
|
firewalla.Name,
|
|
merlin.Name,
|
|
openwrt.Name,
|
|
pfsense.Name,
|
|
synology.Name,
|
|
tomato.Name,
|
|
ubios.Name:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// SupportedPlatforms return all platforms that can be configured to run with ctrld.
|
|
func SupportedPlatforms() []string {
|
|
return []string{
|
|
ddwrt.Name,
|
|
edgeos.Name,
|
|
firewalla.Name,
|
|
merlin.Name,
|
|
openwrt.Name,
|
|
pfsense.Name,
|
|
synology.Name,
|
|
tomato.Name,
|
|
ubios.Name,
|
|
}
|
|
}
|
|
|
|
// Name returns name of the router platform.
|
|
func Name() string {
|
|
if r := routerPlatform.Load(); r != nil {
|
|
return r.name
|
|
}
|
|
r := &router{}
|
|
r.name = distroName()
|
|
routerPlatform.Store(r)
|
|
return r.name
|
|
}
|
|
|
|
func distroName() string {
|
|
switch {
|
|
case bytes.HasPrefix(unameO(), []byte("DD-WRT")):
|
|
return ddwrt.Name
|
|
case bytes.HasPrefix(unameO(), []byte("ASUSWRT-Merlin")):
|
|
return merlin.Name
|
|
case haveFile("/etc/openwrt_version"):
|
|
return openwrt.Name
|
|
case haveDir("/data/unifi"):
|
|
return ubios.Name
|
|
case bytes.HasPrefix(unameU(), []byte("synology")):
|
|
return synology.Name
|
|
case bytes.HasPrefix(unameO(), []byte("Tomato")):
|
|
return tomato.Name
|
|
case haveDir("/config/scripts/post-config.d"):
|
|
return edgeos.Name
|
|
case haveFile("/etc/ubnt/init/vyatta-router"):
|
|
return edgeos.Name // For 2.x
|
|
case isPfsense():
|
|
return pfsense.Name
|
|
case haveFile("/etc/firewalla_release"):
|
|
return firewalla.Name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func haveFile(file string) bool {
|
|
_, err := os.Stat(file)
|
|
return err == nil
|
|
}
|
|
|
|
func haveDir(dir string) bool {
|
|
fi, _ := os.Stat(dir)
|
|
return fi != nil && fi.IsDir()
|
|
}
|
|
|
|
func unameO() []byte {
|
|
out, _ := exec.Command("uname", "-o").Output()
|
|
return out
|
|
}
|
|
|
|
func unameU() []byte {
|
|
out, _ := exec.Command("uname", "-u").Output()
|
|
return out
|
|
}
|
|
|
|
func isPfsense() bool {
|
|
b, err := os.ReadFile("/etc/platform")
|
|
return err == nil && bytes.HasPrefix(b, []byte("pfSense"))
|
|
}
|