Files
ctrld/internal/router/router.go
Cuong Manh Le aec2596262 all: refactor router code to use interface
So the code is more modular, easier to read/maintain.
2023-08-09 23:54:23 +07:00

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"))
}