fea: cli for router tunnels.
Some checks failed
Coverage CI / build (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
Ubuntu CI / build (push) Has been cancelled

This commit is contained in:
Daniel Ding
2025-11-24 20:30:30 +08:00
parent c6dc0b1e28
commit ef79645ed8
17 changed files with 330 additions and 40 deletions

View File

@@ -13,10 +13,43 @@ type Ceci struct {
}
func (u Ceci) Url(prefix string) string {
return prefix + "/api/network/ceci"
}
func (u Ceci) List(c *cli.Context) error {
url := u.Url(c.String("url"))
clt := u.NewHttp(c.String("token"))
var data schema.Network
if err := clt.GetJSON(url, &data); err != nil {
return err
}
return u.Out(data, "yaml", "")
}
func (u Ceci) Commands(app *api.App) {
app.Command(&cli.Command{
Name: "ceci",
Usage: "Ceci TCP proxy",
Subcommands: []*cli.Command{
{
Name: "ls",
Usage: "List a ceci TCP",
Action: u.List,
},
CeciTCP{}.Commands(app),
},
})
}
type CeciTCP struct {
Cmd
}
func (u CeciTCP) Url(prefix string) string {
return prefix + "/api/network/ceci/tcp"
}
func (u Ceci) Add(c *cli.Context) error {
func (u CeciTCP) Add(c *cli.Context) error {
target := strings.Split(c.String("target"), ",")
data := &schema.CeciTcp{
Mode: c.String("mode"),
@@ -31,7 +64,7 @@ func (u Ceci) Add(c *cli.Context) error {
return nil
}
func (u Ceci) Remove(c *cli.Context) error {
func (u CeciTCP) Remove(c *cli.Context) error {
data := &schema.CeciTcp{
Listen: c.String("listen"),
}
@@ -43,8 +76,8 @@ func (u Ceci) Remove(c *cli.Context) error {
return nil
}
func (u Ceci) Commands(app *api.App) {
app.Command(&cli.Command{
func (u CeciTCP) Commands(app *api.App) *cli.Command {
return &cli.Command{
Name: "ceci",
Usage: "Ceci TCP proxy",
Subcommands: []*cli.Command{
@@ -68,5 +101,5 @@ func (u Ceci) Commands(app *api.App) {
Action: u.Remove,
},
},
})
}
}

View File

@@ -43,4 +43,5 @@ func Commands(app *api.App) {
BGP{}.Commands(app)
Ceci{}.Commands(app)
Prefix{}.Commands(app)
Router{}.Commands(app)
}

View File

@@ -10,12 +10,32 @@ type IPSec struct {
Cmd
}
func (o IPSec) Url(prefix string) string {
return prefix + "/api/network/ipsec"
}
func (o IPSec) List(c *cli.Context) error {
url := o.Url(c.String("url"))
clt := o.NewHttp(c.String("token"))
var data schema.Network
if err := clt.GetJSON(url, &data); err != nil {
return err
}
return o.Out(data, "yaml", "")
}
func (o IPSec) Commands(app *api.App) {
tunnel := IPSecTunnel{}
app.Command(&cli.Command{
Name: "ipsec",
Usage: "IPSec configuration",
Subcommands: []*cli.Command{
{
Name: "ls",
Usage: "Display ipsec network",
Aliases: []string{"ls"},
Action: o.List,
},
tunnel.Commands(),
},
})

109
cmd/api/v5/router.go Normal file
View File

@@ -0,0 +1,109 @@
package v5
import (
"github.com/luscis/openlan/cmd/api"
"github.com/luscis/openlan/pkg/schema"
"github.com/urfave/cli/v2"
)
// openlan router tunnel add --remote 1.1.1.2 --address 192.168.1.1 --protocol gre
// openlan router tunnel add --remote 1.1.1.2 --address 192.168.1.1 --protocol ipip
// openlan router tunnel remove --remote 1.1.1.2 --address 192.168.1.1
type Router struct {
Cmd
}
func (b Router) Url(prefix string) string {
return prefix + "/api/network/router"
}
func (b Router) List(c *cli.Context) error {
url := b.Url(c.String("url"))
clt := b.NewHttp(c.String("token"))
var data schema.Network
if err := clt.GetJSON(url, &data); err != nil {
return err
}
return b.Out(data, "yaml", "")
}
func (b Router) Commands(app *api.App) {
app.Command(&cli.Command{
Name: "router",
Usage: "Router",
Subcommands: []*cli.Command{
{
Name: "ls",
Usage: "Display router network",
Aliases: []string{"ls"},
Action: b.List,
},
RouterTunnel{}.Commands(),
},
})
}
type RouterTunnel struct {
Cmd
}
func (s RouterTunnel) Url(prefix string) string {
return prefix + "/api/network/router/tunnel"
}
func (s RouterTunnel) Add(c *cli.Context) error {
data := &schema.RouterTunnel{
Remote: c.String("remote"),
Address: c.String("address"),
Protocol: c.String("protocol"),
}
url := s.Url(c.String("url"))
clt := s.NewHttp(c.String("token"))
if err := clt.PostJSON(url, data, nil); err != nil {
return err
}
return nil
}
func (s RouterTunnel) Remove(c *cli.Context) error {
data := &schema.RouterTunnel{
Remote: c.String("remote"),
Protocol: c.String("protocol"),
}
url := s.Url(c.String("url"))
clt := s.NewHttp(c.String("token"))
if err := clt.DeleteJSON(url, data, nil); err != nil {
return err
}
return nil
}
func (s RouterTunnel) Commands() *cli.Command {
return &cli.Command{
Name: "tunnel",
Usage: "Router tunnels",
Subcommands: []*cli.Command{
{
Name: "add",
Usage: "Add router tunnel",
Flags: []cli.Flag{
&cli.StringFlag{Name: "address", Required: true},
&cli.StringFlag{Name: "remote", Required: true},
&cli.StringFlag{Name: "protocol", Value: "gre"},
},
Action: s.Add,
},
{
Name: "remove",
Aliases: []string{"rm"},
Usage: "Remove router tunnel",
Flags: []cli.Flag{
&cli.StringFlag{Name: "remote", Required: true},
&cli.StringFlag{Name: "protocol", Value: "gre"},
},
Action: s.Remove,
},
},
}
}

View File

@@ -156,11 +156,17 @@ type CeciApi interface {
DelTcp(data schema.CeciTcp)
}
type RouterApi interface {
AddTunnel(data schema.RouterTunnel) error
DelTunnel(data schema.RouterTunnel) error
}
type callApi struct {
ipsecApi IPSecApi
bgpApi BgpApi
ceciApi CeciApi
workers map[string]NetworkApi
ipsecApi IPSecApi
bgpApi BgpApi
ceciApi CeciApi
routerApi RouterApi
workers map[string]NetworkApi
}
func (i *callApi) AddWorker(name string, obj NetworkApi) {
@@ -189,6 +195,10 @@ func (i *callApi) SetCeciApi(value CeciApi) {
i.ceciApi = value
}
func (i *callApi) SetRouterApi(value RouterApi) {
i.routerApi = value
}
var Call = &callApi{
workers: make(map[string]NetworkApi),
}

View File

@@ -232,3 +232,52 @@ func (h DNAT) Delete(w http.ResponseWriter, r *http.Request) {
}
ResponseJson(w, "success")
}
type RouterTunnel struct {
cs SwitchApi
}
func (h RouterTunnel) Router(router *mux.Router) {
router.HandleFunc("/api/network/router/tunnel", h.Post).Methods("POST")
router.HandleFunc("/api/network/router/tunnel", h.Delete).Methods("DELETE")
}
func (h RouterTunnel) Post(w http.ResponseWriter, r *http.Request) {
caller := Call.routerApi
if caller == nil {
http.Error(w, "Router not found", http.StatusBadRequest)
return
}
value := schema.RouterTunnel{}
if err := GetData(r, &value); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := caller.AddTunnel(value); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
ResponseJson(w, "success")
}
func (h RouterTunnel) Delete(w http.ResponseWriter, r *http.Request) {
caller := Call.routerApi
if caller == nil {
http.Error(w, "Router not found", http.StatusBadRequest)
return
}
value := schema.RouterTunnel{}
if err := GetData(r, &value); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := caller.DelTunnel(value); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
ResponseJson(w, "success")
}

View File

@@ -30,5 +30,6 @@ func Add(router *mux.Router, cs SwitchApi) {
Rate{cs: cs}.Router(router)
SNAT{}.Router(router)
DNAT{}.Router(router)
RouterTunnel{}.Router(router)
Network{cs: cs}.Router(router)
}

View File

@@ -16,12 +16,10 @@ type network struct {
}
func (w *network) Add(n *models.Network) {
libol.Debug("network.Add %v", *n)
_ = w.Networks.Set(n.Name, n)
}
func (w *network) Del(name string) {
libol.Debug("network.Del %s", name)
w.Networks.Del(name)
}

View File

@@ -59,10 +59,14 @@ func (n *Network) NewSpecifies() any {
case "bgp":
n.Specifies = &BgpSpecifies{}
case "ceci":
n.Specifies = &CeciSpecifies{}
default:
n.Specifies = nil
}
if n.Specifies != nil {
n.Name = n.Provider
}
return n.Specifies
}

View File

@@ -47,3 +47,30 @@ func (n *RouterSpecifies) Correct() {
t.Correct()
}
}
func (n *RouterSpecifies) FindTunnel(value *RouterTunnel) (*RouterTunnel, int) {
for index, obj := range n.Tunnels {
if obj.Id() == value.Id() {
return obj, index
}
}
return nil, -1
}
func (n *RouterSpecifies) AddTunnel(value *RouterTunnel) bool {
_, index := n.FindTunnel(value)
if index == -1 {
n.Tunnels = append(n.Tunnels, value)
return true
}
return false
}
func (n *RouterSpecifies) DelTunnel(value *RouterTunnel) (*RouterTunnel, bool) {
older, index := n.FindTunnel(value)
if index != -1 {
n.Tunnels = append(n.Tunnels[:index], n.Tunnels[index+1:]...)
return older, true
}
return older, false
}

View File

@@ -45,10 +45,10 @@ type Network struct {
Name string `json:"name"`
Tenant string `json:"tenant,omitempty"`
Gateway string `json:"gateway,omitempty"`
Address string `json:"address"`
Address string `json:"address,omitempty"`
IpStart string `json:"startAt,omitempty"`
IpEnd string `json:"endAt,omitempty"`
Netmask string `json:"netmask"`
Netmask string `json:"netmask,omitempty"`
Routes []*Route `json:"routes,omitempty"`
Config interface{} `json:"config,omitempty"`
}

View File

@@ -52,3 +52,9 @@ type DNAT struct {
ToDest string `json:"todestination"`
ToDport int `json:"todport"`
}
type RouterTunnel struct {
Protocol string `json:"protocol"`
Remote string `json:"remote"`
Address string `json:"address"`
}

View File

@@ -25,6 +25,7 @@ func NewBgpWorker(c *co.Network) *BgpWorker {
w := &BgpWorker{
WorkerImpl: NewWorkerApi(c),
}
api.Call.SetBgpApi(w)
w.spec, _ = c.Specifies.(*co.BgpSpecifies)
return w
}
@@ -83,6 +84,7 @@ route-map {{ .Address }}-out permit 10
func (w *BgpWorker) Initialize() {
w.out.Info("BgpWorker.Initialize")
w.addCache()
}
func (w *BgpWorker) save() {

View File

@@ -24,12 +24,14 @@ func NewCeciWorker(c *co.Network) *CeciWorker {
w := &CeciWorker{
WorkerImpl: NewWorkerApi(c),
}
api.Call.SetCeciApi(w)
w.spec, _ = c.Specifies.(*co.CeciSpecifies)
return w
}
func (w *CeciWorker) Initialize() {
w.out.Info("CeciWorker.Initialize")
w.addCache()
}
func (w *CeciWorker) killPid(name string) {

View File

@@ -28,6 +28,8 @@ func NewIPSecWorker(c *co.Network) *IPSecWorker {
w := &IPSecWorker{
WorkerImpl: NewWorkerApi(c),
}
api.Call.SetIPSecApi(w)
w.spec, _ = c.Specifies.(*co.IPSecSpecifies)
return w
}
@@ -102,6 +104,7 @@ func (w *IPSecWorker) Initialize() {
if err := os.Mkdir(IPSecLogDir, 0600); err != nil {
w.out.Warn("IPSecWorker.Initialize %s", err)
}
w.addCache()
}
func (w *IPSecWorker) saveSec(name, tmpl string, data interface{}) error {

View File

@@ -23,19 +23,13 @@ func NewNetworker(c *co.Network) api.NetworkApi {
switch c.Provider {
case "ipsec":
secer := NewIPSecWorker(c)
api.Call.SetIPSecApi(secer)
obj = secer
obj = NewIPSecWorker(c)
case "bgp":
bgper := NewBgpWorker(c)
api.Call.SetBgpApi(bgper)
obj = bgper
obj = NewBgpWorker(c)
case "router":
obj = NewRouterWorker(c)
case "ceci":
cecer := NewCeciWorker(c)
api.Call.SetCeciApi(cecer)
obj = cecer
obj = NewCeciWorker(c)
default:
obj = NewOpenLANWorker(c)
}
@@ -85,6 +79,21 @@ func (w *WorkerImpl) Provider() string {
return w.cfg.Provider
}
func (w *WorkerImpl) addCache() {
cfg := w.cfg
n := models.Network{
Name: cfg.Name,
Config: cfg,
}
if cfg.Subnet != nil {
n.IpStart = cfg.Subnet.Start
n.IpEnd = cfg.Subnet.End
n.Netmask = cfg.Subnet.Netmask
n.Address = cfg.Bridge.Address
}
cache.Network.Add(&n)
}
func (w *WorkerImpl) Initialize() {
cfg := w.cfg
@@ -96,18 +105,8 @@ func (w *WorkerImpl) Initialize() {
w.acl = NewACL(cfg.Name)
w.acl.Initialize()
w.addCache()
w.findhop = NewFindHop(cfg.Name, cfg)
if cfg.Subnet != nil {
n := models.Network{
Name: cfg.Name,
IpStart: cfg.Subnet.Start,
IpEnd: cfg.Subnet.End,
Netmask: cfg.Subnet.Netmask,
Address: cfg.Bridge.Address,
Config: cfg,
}
cache.Network.Add(&n)
}
w.updateVPN()
w.createVPN()

View File

@@ -4,6 +4,7 @@ import (
"github.com/luscis/openlan/pkg/api"
co "github.com/luscis/openlan/pkg/config"
"github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/schema"
nl "github.com/vishvananda/netlink"
)
@@ -17,6 +18,7 @@ func NewRouterWorker(c *co.Network) *RouterWorker {
w := &RouterWorker{
WorkerImpl: NewWorkerApi(c),
}
api.Call.SetRouterApi(w)
w.spec, _ = c.Specifies.(*co.RouterSpecifies)
return w
}
@@ -74,7 +76,7 @@ func (w *RouterWorker) Start(v api.SwitchApi) {
w.uuid = v.UUID()
for _, tun := range w.spec.Tunnels {
w.AddTunnel(tun)
w.addTunnel(tun)
}
w.WorkerImpl.Start(v)
@@ -102,7 +104,7 @@ func (w *RouterWorker) Stop() {
w.WorkerImpl.Stop()
for _, tun := range w.spec.Tunnels {
w.DelTunnel(tun)
w.delTunnel(tun)
}
}
@@ -112,7 +114,7 @@ func (w *RouterWorker) Reload(v api.SwitchApi) {
w.Start(v)
}
func (w *RouterWorker) AddTunnel(data *co.RouterTunnel) {
func (w *RouterWorker) addTunnel(data *co.RouterTunnel) {
var link nl.Link
switch data.Protocol {
@@ -159,14 +161,38 @@ func (w *RouterWorker) AddTunnel(data *co.RouterTunnel) {
}
}
func (w *RouterWorker) DelTunnel(data *co.RouterTunnel) {
if data.Link == "" {
return
}
func (w *RouterWorker) delTunnel(data *co.RouterTunnel) {
if link, err := nl.LinkByName(data.Link); err == nil {
if err := nl.LinkDel(link); err != nil {
w.out.Error("RouterWorker.DelTunnel %s %s", data.Id(), err)
return
}
} else {
w.out.Warn("RouterWorker.DelTunnel notFound %s:%s", data.Id(), data.Link)
}
}
func (w *RouterWorker) AddTunnel(data schema.RouterTunnel) error {
obj := &co.RouterTunnel{
Remote: data.Remote,
Protocol: data.Protocol,
Address: data.Address,
}
obj.Correct()
if ok := w.spec.AddTunnel(obj); ok {
w.addTunnel(obj)
}
return nil
}
func (w *RouterWorker) DelTunnel(data schema.RouterTunnel) error {
obj := &co.RouterTunnel{
Remote: data.Remote,
Protocol: data.Protocol,
}
obj.Correct()
if old, ok := w.spec.DelTunnel(obj); ok {
w.delTunnel(old)
}
return nil
}