mirror of
https://github.com/liloew/gvn.git
synced 2025-12-24 13:38:00 +08:00
implementation PING/PONG mechanism
This commit is contained in:
2
Makefile
2
Makefile
@@ -19,7 +19,7 @@ all:
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o target/gvn-Darwin-x86_64 main.go
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o target/gvn-Darwin-aarch64 main.go
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o target/gvn-Windows-x86_64.exe main.go
|
||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -o target/gvn-Freebsd-x86_64 main.go
|
||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -o target/gvn-Freebsd-amd64 main.go
|
||||
macOS:
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o target/gvn-Darwin-x86_64 main.go
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o target/gvn-Darwin-aarch64 main.go
|
||||
|
||||
@@ -26,6 +26,10 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const (
|
||||
INTERVAL = 30
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
@@ -55,6 +59,7 @@ func init() {
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is $HOME/.gvn.yaml)")
|
||||
rootCmd.PersistentFlags().BoolP("stdout", "", false, "logs to the stdout")
|
||||
rootCmd.PersistentFlags().BoolP("debug", "", false, "debug log level")
|
||||
|
||||
// Cobra also supports local flags, which will only run
|
||||
// when this action is called directly.
|
||||
@@ -76,6 +81,9 @@ func initConfig() {
|
||||
} else {
|
||||
logrus.SetOutput(file)
|
||||
}
|
||||
if debug, _ := rootCmd.Flags().GetBool("debug"); debug {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
if cfgFile == "" {
|
||||
home, err := os.UserHomeDir()
|
||||
|
||||
140
cmd/up.go
140
cmd/up.go
@@ -118,50 +118,66 @@ func upCommand(cmd *cobra.Command) {
|
||||
// DHCP for client mode
|
||||
if MODE(viper.GetUint("mode")) == MODECLIENT {
|
||||
go func() {
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
for _ = range ticker.C {
|
||||
req := dhcp.Request{
|
||||
Id: viper.GetString("id"),
|
||||
Name: viper.GetString("dev.name"),
|
||||
Subnets: viper.GetStringSlice("dev.subnets"),
|
||||
// HEARTBEAT
|
||||
req := dhcp.Request{
|
||||
Id: viper.GetString("id"),
|
||||
Name: viper.GetString("dev.name"),
|
||||
Subnets: viper.GetStringSlice("dev.subnets"),
|
||||
}
|
||||
if client, res := dhcp.NewRPCClient(host, rpcZone, viper.GetString("server"), req); client != nil {
|
||||
// ticker.Stop()
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"res": res,
|
||||
"req": req,
|
||||
}).Info("RPC - Client received data")
|
||||
devChan <- tun.Device{
|
||||
Name: req.Name,
|
||||
Ip: res.Ip,
|
||||
Mtu: res.Mtu,
|
||||
// ignore subnets because of self did't forward it to TUN
|
||||
// Subnets: res.Subnets,
|
||||
ServerVIP: res.ServerVIP,
|
||||
Port: viper.GetUint("port"),
|
||||
}
|
||||
if client, res := dhcp.NewRPCClient(host, rpcZone, viper.GetString("server"), req); client != nil {
|
||||
ticker.Stop()
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"res": res,
|
||||
"req": req,
|
||||
}).Info("RPC - Client received data")
|
||||
devChan <- tun.Device{
|
||||
Name: req.Name,
|
||||
Ip: res.Ip,
|
||||
Mtu: res.Mtu,
|
||||
// ignore subnets because of self did't forward it to TUN
|
||||
// Subnets: res.Subnets,
|
||||
ServerVIP: res.ServerVIP,
|
||||
}
|
||||
|
||||
// TODO: sleep or make sure call after tun.New
|
||||
// refresh local VIP table
|
||||
var ress []dhcp.Response
|
||||
if err := dhcp.Call("DHCPService", "Clients", req, &ress); err == nil {
|
||||
subnets := make([]string, 0)
|
||||
for _, r := range ress {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"VIP": r.Ip,
|
||||
"ID": r.Id,
|
||||
"Subnet": r.Subnets,
|
||||
}).Info("Refresh local vip table")
|
||||
route.Route.Add(strings.Split(r.Ip, "/")[0]+"/32", r.Id)
|
||||
if r.Id != host.ID().Pretty() {
|
||||
subnets = append(subnets, r.Subnets...)
|
||||
}
|
||||
}
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"subnets": subnets,
|
||||
}).Info("Refresh subnets")
|
||||
tun.RefreshRoute(subnets)
|
||||
}
|
||||
var ress []dhcp.Response
|
||||
if err := dhcp.Call("DHCPService", "Clients", req, &ress); err == nil {
|
||||
// subnets := make([]string, 0)
|
||||
for _, r := range ress {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"VIP": r.Ip,
|
||||
"ID": r.Id,
|
||||
"Subnet": r.Subnets,
|
||||
}).Info("Refresh local vip table")
|
||||
// route.Route.Add(strings.Split(r.Ip, "/")[0]+"/32", r.Id)
|
||||
subnet := strings.Split(r.Ip, "/")[0] + "/32"
|
||||
route.EventBus.Publish(route.ADD_ROUTE_TOPIC, route.RouteEvent{Id: r.Id, Subnets: []string{subnet}})
|
||||
if r.Id != host.ID().Pretty() {
|
||||
// does not change route via local ethernet
|
||||
// subnets = append(subnets, r.Subnets...)
|
||||
route.EventBus.Publish(route.ADD_ROUTE_TOPIC, route.RouteEvent{Id: r.Id, Subnets: r.Subnets})
|
||||
}
|
||||
}
|
||||
// logrus.WithFields(logrus.Fields{
|
||||
// "subnets": subnets,
|
||||
// }).Info("Refresh subnets")
|
||||
// tun.RefreshRoute(subnets)
|
||||
} else {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"ERROR": err,
|
||||
}).Error("Request clients error")
|
||||
}
|
||||
ticker := time.NewTicker(INTERVAL * time.Second)
|
||||
for range ticker.C {
|
||||
// TODO: sleep or make sure call after tun.New
|
||||
// refresh local VIP table
|
||||
if err := dhcp.Call("DHCPService", "Ping", req, nil); err != nil {
|
||||
// TODO: log error
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"ERROR": err,
|
||||
}).Error("RPC - Ping error")
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -175,7 +191,7 @@ func upCommand(cmd *cobra.Command) {
|
||||
for sig := range c {
|
||||
switch sig {
|
||||
case syscall.SIGINT:
|
||||
// exit when receive ctrl+c
|
||||
// exit when receive ctrl+c and others signal
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"SIG": sig,
|
||||
"dev": mainDev,
|
||||
@@ -293,3 +309,45 @@ func readData(stream network.Stream) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func contains(elems []string, v string) bool {
|
||||
for _, s := range elems {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// check pre contains pos elements, eg:
|
||||
// pre: [1, 3, 5, 6], pos: [1, 2, 3, 4]
|
||||
// result should be: [2, 4]
|
||||
func contains(left, right []string) []string {
|
||||
sort.Strings(left)
|
||||
sort.Strings(right)
|
||||
tmp := make([]string, 0)
|
||||
for _, item := range right {
|
||||
if !contains(left, item) {
|
||||
tmp = append(tmp, item)
|
||||
}
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
|
||||
func mins(left, right *[]string) {
|
||||
for _, item := range *right {
|
||||
left = remove(*left, item)
|
||||
}
|
||||
}
|
||||
|
||||
func remove(items []string, item string) []string {
|
||||
newitems := []string{}
|
||||
for _, i := range items {
|
||||
if i != item {
|
||||
newitems = append(newitems, i)
|
||||
}
|
||||
}
|
||||
return newitems
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
id: QmZiwVDDPhByWJsk5tJqajnPFjvM15KV1NuasSb9WN3ZJq
|
||||
server: "/ip4/172.168.1.134/tcp/6543/p2p/QmSyYjoecj3aQ46TAWSEAmork3CRS51Y2SZCcEsiX8XeAx"
|
||||
port: 6543
|
||||
dev:
|
||||
name: utun4
|
||||
mtu: 1420
|
||||
|
||||
@@ -17,9 +17,11 @@ package dhcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p-core/host"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
@@ -44,6 +46,7 @@ type RPC struct {
|
||||
|
||||
type Request struct {
|
||||
Id string
|
||||
Mode int
|
||||
Name string
|
||||
Subnets []string
|
||||
}
|
||||
@@ -55,6 +58,9 @@ type Response struct {
|
||||
Mtu int
|
||||
Subnets []string
|
||||
ServerVIP string
|
||||
Mode int
|
||||
Ttl int64
|
||||
LoginTime int64
|
||||
}
|
||||
|
||||
type DHCPService struct {
|
||||
@@ -75,6 +81,9 @@ func (s *DHCPService) DHCP(ctx context.Context, req Request, res *Response) erro
|
||||
data.Id = req.Id
|
||||
data.Name = req.Name
|
||||
data.Subnets = req.Subnets
|
||||
data.Mode = req.Mode
|
||||
data.LoginTime = time.Now().Unix()
|
||||
data.Ttl = 10 * 60 // 10 min
|
||||
|
||||
if MaxCIDR == "" {
|
||||
MaxCIDR = s.Cidr
|
||||
@@ -106,12 +115,17 @@ func (s *DHCPService) DHCP(ctx context.Context, req Request, res *Response) erro
|
||||
res.Mtu = data.Mtu
|
||||
res.Subnets = data.Subnets
|
||||
res.ServerVIP = data.ServerVIP
|
||||
res.Mode = data.Mode
|
||||
res.LoginTime = data.LoginTime
|
||||
res.Ttl = data.Ttl
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"res": res,
|
||||
}).Info("RPC - Client requested data")
|
||||
|
||||
// vip/mask -> vip/32
|
||||
route.Route.Add(strings.Split(data.Ip, "/")[0]+"/32", data.Id)
|
||||
// route.Route.Add(strings.Split(data.Ip, "/")[0]+"/32", data.Id)
|
||||
subnet := strings.Split(data.Ip, "/")[0] + "/32"
|
||||
route.EventBus.Publish(route.ADD_ROUTE_TOPIC, route.RouteEvent{Id: data.Id, Subnets: []string{subnet}})
|
||||
mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
@@ -119,11 +133,15 @@ func (s *DHCPService) DHCP(ctx context.Context, req Request, res *Response) erro
|
||||
func (s *DHCPService) Clients(ctx context.Context, req Request, res *[]Response) error {
|
||||
mu.Lock()
|
||||
for _, v := range s.KV {
|
||||
if v.Mode != 1 && time.Now().Unix()-v.LoginTime > v.Ttl {
|
||||
continue
|
||||
}
|
||||
r := Response{
|
||||
Id: v.Id,
|
||||
Name: v.Name,
|
||||
Ip: v.Ip,
|
||||
Mtu: v.Mtu,
|
||||
Mode: v.Mode,
|
||||
Subnets: v.Subnets,
|
||||
ServerVIP: v.ServerVIP,
|
||||
}
|
||||
@@ -133,6 +151,20 @@ func (s *DHCPService) Clients(ctx context.Context, req Request, res *[]Response)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DHCPService) Ping(ctx context.Context, req Request, res *Response) error {
|
||||
mu.Lock()
|
||||
if v, ok := s.KV[req.Id]; ok {
|
||||
v.Ttl = 10 * 60 // 10 min
|
||||
v.LoginTime = time.Now().Unix()
|
||||
s.KV[req.Id] = v
|
||||
} else {
|
||||
mu.Unlock()
|
||||
return errors.New("not found")
|
||||
}
|
||||
mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewRPCServer(host host.Host, zone string, cidr string, mtu int) {
|
||||
server := rpc.NewServer(host, protocol.ID(zone))
|
||||
service := DHCPService{KV: map[string]Response{}, Cidr: cidr, Mtu: mtu}
|
||||
@@ -141,6 +173,21 @@ func NewRPCServer(host host.Host, zone string, cidr string, mtu int) {
|
||||
"ERROR": err,
|
||||
}).Panic("RPC - build RPC service error")
|
||||
}
|
||||
// does not clean the zombie client
|
||||
/*
|
||||
go func() {
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
for range ticker.C {
|
||||
for k, v := range service.KV {
|
||||
if time.Now().Unix()-v.LoginTime > v.Ttl {
|
||||
mu.Lock()
|
||||
delete(service.KV, k)
|
||||
mu.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
*/
|
||||
// server register
|
||||
mu.Lock()
|
||||
service.KV[host.ID().Pretty()] = Response{
|
||||
@@ -148,6 +195,9 @@ func NewRPCServer(host host.Host, zone string, cidr string, mtu int) {
|
||||
Ip: cidr,
|
||||
Mtu: mtu,
|
||||
ServerVIP: cidr,
|
||||
Mode: 1,
|
||||
LoginTime: time.Now().Unix(),
|
||||
Ttl: 10 * 60, // 10 min
|
||||
}
|
||||
mu.Unlock()
|
||||
}
|
||||
@@ -161,7 +211,7 @@ func NewRPCClient(host host.Host, zone string, server string, req Request) (*rpc
|
||||
if err := client.Call(addr.ID, "DHCPService", "DHCP", req, &res); err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"ERROR": err,
|
||||
}).Error("RPC - call RPC serveice error")
|
||||
}).Error("RPC - call DHCP RPC serveice error")
|
||||
}
|
||||
return client, res
|
||||
}
|
||||
@@ -172,7 +222,11 @@ func NewRPCClient(host host.Host, zone string, server string, req Request) (*rpc
|
||||
func Call(svcName string, svcMethod string, req Request, res interface{}) error {
|
||||
if err := client.Call(serverId, svcName, svcMethod, req, &res); err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"ERROR": err,
|
||||
"ERROR": err,
|
||||
"svcName": svcName,
|
||||
"svcMethod": svcMethod,
|
||||
"req": req,
|
||||
"res": res,
|
||||
}).Error("RPC - call RPC serveice error")
|
||||
return err
|
||||
}
|
||||
|
||||
63
eventbus/eventbus.go
Normal file
63
eventbus/eventbus.go
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright © 2022 lilo <luolee.me@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package eventbus
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type DataEvent struct {
|
||||
Data interface{}
|
||||
Topic string
|
||||
}
|
||||
|
||||
// DataChannel is a channel which can accept an DataEvent
|
||||
type DataChannel chan DataEvent
|
||||
|
||||
// DataChannelSlice is a slice of DataChannels
|
||||
type DataChannelSlice []DataChannel
|
||||
|
||||
// EventBus stores the information about subscribers interested for a particular topic
|
||||
type EventBus struct {
|
||||
Subscribers map[string]DataChannelSlice
|
||||
rm sync.RWMutex
|
||||
}
|
||||
|
||||
func (eb *EventBus) Publish(topic string, data interface{}) {
|
||||
eb.rm.RLock()
|
||||
if chans, found := eb.Subscribers[topic]; found {
|
||||
// this is done because the slices refer to same array even though they are passed by value
|
||||
// thus we are creating a new slice with our elements thus preserve locking correctly.
|
||||
// special thanks for /u/freesid who pointed it out
|
||||
channels := append(DataChannelSlice{}, chans...)
|
||||
go func(data DataEvent, dataChannelSlices DataChannelSlice) {
|
||||
for _, ch := range dataChannelSlices {
|
||||
ch <- data
|
||||
}
|
||||
}(DataEvent{Data: data, Topic: topic}, channels)
|
||||
}
|
||||
eb.rm.RUnlock()
|
||||
}
|
||||
|
||||
func (eb *EventBus) Subscribe(topic string, ch DataChannel) {
|
||||
eb.rm.Lock()
|
||||
if prev, found := eb.Subscribers[topic]; found {
|
||||
eb.Subscribers[topic] = append(prev, ch)
|
||||
} else {
|
||||
eb.Subscribers[topic] = append([]DataChannel{}, ch)
|
||||
}
|
||||
eb.rm.Unlock()
|
||||
}
|
||||
@@ -36,14 +36,11 @@ var (
|
||||
routingDiscovery *discovery.RoutingDiscovery
|
||||
)
|
||||
|
||||
// func NewDHT(host host.Host, bootstraps []multiaddr.Multiaddr) *dht.IpfsDHT {
|
||||
func NewDHT(host host.Host, zone string, bootstraps []string) *dht.IpfsDHT {
|
||||
ctx := context.Background()
|
||||
addrs := make([]peer.AddrInfo, 0)
|
||||
if len(bootstraps) > 0 {
|
||||
for _, bootstrap := range bootstraps {
|
||||
// ids := strings.Split(bootstrap, "/")
|
||||
// bs := peer.AddrInfo{ID: peer.ID(ids[len(ids)-1]), Addrs: addrs}
|
||||
addr, err := peer.AddrInfoFromString(bootstrap)
|
||||
if err != nil {
|
||||
continue
|
||||
@@ -51,10 +48,6 @@ func NewDHT(host host.Host, zone string, bootstraps []string) *dht.IpfsDHT {
|
||||
addrs = append(addrs, *addr)
|
||||
}
|
||||
|
||||
// var options []dht.Option
|
||||
// options = append(options, dht.Mode(dht.ModeServer))
|
||||
// options = append(options, dht.BootstrapPeers(bs))
|
||||
|
||||
dstore := dsync.MutexWrap(ds.NewMapDatastore())
|
||||
|
||||
kdht = dht.NewDHT(ctx, host, dstore)
|
||||
|
||||
27
p2p/peer.go
27
p2p/peer.go
@@ -31,7 +31,6 @@ import (
|
||||
pubsub "github.com/libp2p/go-libp2p-pubsub"
|
||||
"github.com/liloew/gvn/dhcp"
|
||||
"github.com/liloew/gvn/route"
|
||||
"github.com/liloew/gvn/tun"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/songgao/water/waterutil"
|
||||
"github.com/spf13/viper"
|
||||
@@ -133,10 +132,11 @@ func NewPubSub(host host.Host, topic string) *Publisher {
|
||||
continue
|
||||
}
|
||||
// TODO: add MASQUERADE if self
|
||||
tun.RefreshRoute(message.Subnets)
|
||||
// tun.RefreshRoute(message.Subnets)
|
||||
for _, subnet := range message.Subnets {
|
||||
// Add will override the exist one
|
||||
route.Route.Add(strings.TrimSpace(subnet), message.Id)
|
||||
// route.Route.Add(strings.TrimSpace(subnet), message.Id)
|
||||
route.EventBus.Publish(route.REFRESH_ROUTE_TOPIC, route.RouteEvent{Id: message.Id, Subnets: []string{subnet}})
|
||||
}
|
||||
} else if message.MessageType == MessageTypeOnline {
|
||||
// refresh clients
|
||||
@@ -147,7 +147,7 @@ func NewPubSub(host host.Host, topic string) *Publisher {
|
||||
req := dhcp.Request{}
|
||||
var ress []dhcp.Response
|
||||
if err := dhcp.Call("DHCPService", "Clients", req, &ress); err == nil {
|
||||
subnets := make([]string, 0)
|
||||
// subnets := make([]string, 0)
|
||||
for _, r := range ress {
|
||||
if r.Id == host.ID().Pretty() {
|
||||
continue
|
||||
@@ -157,20 +157,25 @@ func NewPubSub(host host.Host, topic string) *Publisher {
|
||||
"ID": r.Id,
|
||||
"Subnet": r.Subnets,
|
||||
}).Info("Refresh local vip table")
|
||||
route.Route.Add(strings.Split(r.Ip, "/")[0]+"/32", r.Id)
|
||||
// route.Route.Add(strings.Split(r.Ip, "/")[0]+"/32", r.Id)
|
||||
subnet := strings.Split(r.Ip, "/")[0] + "/32"
|
||||
route.EventBus.Publish(route.REFRESH_ROUTE_TOPIC, route.RouteEvent{Id: r.Id, Subnets: []string{subnet}})
|
||||
if r.Id != host.ID().Pretty() {
|
||||
subnets = append(subnets, r.Subnets...)
|
||||
// subnets = append(subnets, r.Subnets...)
|
||||
route.EventBus.Publish(route.REFRESH_ROUTE_TOPIC, route.RouteEvent{Id: r.Id, Subnets: r.Subnets})
|
||||
}
|
||||
}
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"subnets": subnets,
|
||||
}).Info("Refresh subnets")
|
||||
tun.RefreshRoute(subnets)
|
||||
// logrus.WithFields(logrus.Fields{
|
||||
// "subnets": subnets,
|
||||
// }).Info("Refresh subnets")
|
||||
// tun.RefreshRoute(subnets)
|
||||
}
|
||||
|
||||
}
|
||||
} else if message.MessageType == MessageTypeOffline {
|
||||
route.Route.Remove(strings.Split(message.Vip, "/")[0] + "/32")
|
||||
// route.Route.Remove(strings.Split(message.Vip, "/")[0] + "/32")
|
||||
subnet := strings.Split(message.Vip, "/")[0] + "/32"
|
||||
route.EventBus.Publish(route.REMOVE_ROUTE_TOPIC, route.RouteEvent{Subnets: []string{subnet}})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
172
route/route.go
Normal file
172
route/route.go
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
Copyright © 2022 lilo <luolee.me@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package route
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/liloew/gvn/eventbus"
|
||||
"github.com/liloew/gvn/tun"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/zmap/go-iptree/iptree"
|
||||
)
|
||||
|
||||
type RouteEvent struct {
|
||||
Subnets []string
|
||||
Id string
|
||||
Vip string
|
||||
}
|
||||
|
||||
const (
|
||||
ADD_ROUTE_TOPIC = "ADD_ROUTE"
|
||||
REMOVE_ROUTE_TOPIC = "REMOVE_ROUTE"
|
||||
REFRESH_ROUTE_TOPIC = "REFRESH_ROUTE"
|
||||
ONLINE_TOPIC = "ONLINE"
|
||||
OFFLINE_TOPIC = "OFFLINE"
|
||||
)
|
||||
|
||||
var (
|
||||
Route = RouteTable{
|
||||
tree: iptree.New(),
|
||||
}
|
||||
EventBus = &eventbus.EventBus{
|
||||
Subscribers: map[string]eventbus.DataChannelSlice{},
|
||||
}
|
||||
LocalRoute = make([]string, 0)
|
||||
)
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
addRouteCh := make(chan eventbus.DataEvent)
|
||||
removeRouteCh := make(chan eventbus.DataEvent)
|
||||
refreshRouteCh := make(chan eventbus.DataEvent)
|
||||
onlineCh := make(chan eventbus.DataEvent)
|
||||
// TODO: ofline channel
|
||||
EventBus.Subscribe(ADD_ROUTE_TOPIC, addRouteCh)
|
||||
EventBus.Subscribe(REMOVE_ROUTE_TOPIC, removeRouteCh)
|
||||
EventBus.Subscribe(REFRESH_ROUTE_TOPIC, refreshRouteCh)
|
||||
EventBus.Subscribe(ONLINE_TOPIC, onlineCh)
|
||||
for {
|
||||
select {
|
||||
case data := <-addRouteCh:
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"Data": data.Data,
|
||||
"Topic": data.Topic,
|
||||
}).Debug("Add Route Channel")
|
||||
for _, subnet := range data.Data.(RouteEvent).Subnets {
|
||||
Route.add(subnet, data.Data.(RouteEvent).Id)
|
||||
}
|
||||
case data := <-removeRouteCh:
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"Data": data.Data,
|
||||
"Topic": data.Topic,
|
||||
}).Debug("Remove Route Channel")
|
||||
for _, subnet := range data.Data.(RouteEvent).Subnets {
|
||||
Route.remove(subnet)
|
||||
}
|
||||
case data := <-refreshRouteCh:
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"Data": data.Data,
|
||||
"Topic": data.Topic,
|
||||
}).Debug("Refresh Route Channel")
|
||||
for _, subnet := range data.Data.(RouteEvent).Subnets {
|
||||
Route.remove(subnet)
|
||||
Route.add(subnet, data.Data.(RouteEvent).Id)
|
||||
}
|
||||
case data := <-onlineCh:
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"Data": data.Data,
|
||||
"Topic": data.Topic,
|
||||
}).Debug("Online Channel")
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
type RouteTable struct {
|
||||
tree *iptree.IPTree
|
||||
rm sync.RWMutex
|
||||
}
|
||||
|
||||
// subnet - 192.168.1.0/24
|
||||
// peerId - P2P node ID
|
||||
func (r *RouteTable) refresh(subnet string, peerId string) {
|
||||
r.remove(subnet)
|
||||
r.add(subnet, peerId)
|
||||
}
|
||||
|
||||
func (r *RouteTable) remove(subnet string) {
|
||||
// TODO: LocalRoute remove
|
||||
r.rm.Lock()
|
||||
r.tree.DeleteByString(subnet)
|
||||
if err := tun.RemoveRoute([]string{subnet}); err == nil {
|
||||
LocalRoute = remove(LocalRoute, subnet)
|
||||
} else {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"ERROR": err,
|
||||
}).Error("Remove route error")
|
||||
}
|
||||
r.rm.Unlock()
|
||||
}
|
||||
|
||||
func (r *RouteTable) add(subnet string, peerId string) {
|
||||
r.rm.Lock()
|
||||
if contains(LocalRoute, subnet) {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"LocalRoute": LocalRoute,
|
||||
"Subnet": subnet,
|
||||
}).Info("Ignore the subnet becuase of existence")
|
||||
r.rm.Unlock()
|
||||
return
|
||||
}
|
||||
r.tree.AddByString(subnet, peerId)
|
||||
if err := tun.AddRoute([]string{subnet}); err == nil {
|
||||
LocalRoute = append(LocalRoute, subnet)
|
||||
} else {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"ERROR": err,
|
||||
}).Error("Add route error")
|
||||
}
|
||||
r.rm.Unlock()
|
||||
}
|
||||
|
||||
func (r *RouteTable) Get(ip string) (interface{}, bool, error) {
|
||||
return r.tree.GetByString(ip)
|
||||
}
|
||||
|
||||
func (r *RouteTable) Clean() {
|
||||
// TODO:
|
||||
r.tree = iptree.New()
|
||||
}
|
||||
|
||||
func contains(elems []string, v string) bool {
|
||||
for _, s := range elems {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func remove(items []string, item string) []string {
|
||||
newitems := []string{}
|
||||
for _, i := range items {
|
||||
if i != item {
|
||||
newitems = append(newitems, i)
|
||||
}
|
||||
}
|
||||
return newitems
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
Copyright © 2022 lilo <luolee.me@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package route
|
||||
|
||||
import (
|
||||
"github.com/zmap/go-iptree/iptree"
|
||||
)
|
||||
|
||||
var (
|
||||
Route = RouteTable{
|
||||
tree: iptree.New(),
|
||||
}
|
||||
)
|
||||
|
||||
type RouteTable struct {
|
||||
tree *iptree.IPTree
|
||||
}
|
||||
|
||||
// subnet - 192.168.1.0/24
|
||||
// peerId - P2P node ID
|
||||
func (r *RouteTable) Refresh(subnet string, peerId string) {
|
||||
r.Remove(subnet)
|
||||
r.Add(subnet, peerId)
|
||||
}
|
||||
|
||||
func (r *RouteTable) Remove(subnet string) {
|
||||
r.tree.DeleteByString(subnet)
|
||||
}
|
||||
|
||||
func (r *RouteTable) Add(subnet string, peerId string) {
|
||||
r.tree.AddByString(subnet, peerId)
|
||||
}
|
||||
|
||||
func (r *RouteTable) Get(ip string) (interface{}, bool, error) {
|
||||
return r.tree.GetByString(ip)
|
||||
}
|
||||
|
||||
func (r *RouteTable) Clean() {
|
||||
// TODO:
|
||||
r.tree = iptree.New()
|
||||
}
|
||||
Reference in New Issue
Block a user