From e8e9fa1d4aaa3d15171b258d63c1fd8722e24cc9 Mon Sep 17 00:00:00 2001 From: eson <474420502@qq.com> Date: Mon, 17 Dec 2018 02:58:52 +0800 Subject: [PATCH] v1.0.0 --- .gitignore | 2 + actives.json | 28 ++++++++ actives.yaml | 9 +++ alertover.go | 60 +++++++++++++++++ dip.go | 158 ++++++++++++++++++++++++++++++++++++++++++++ main.go | 31 +++++++++ switch.go | 175 +++++++++++++++++++++++++++++++++++++++++++++++++ switch_test.go | 14 ++++ 8 files changed, 477 insertions(+) create mode 100644 .gitignore create mode 100644 actives.json create mode 100644 actives.yaml create mode 100644 alertover.go create mode 100644 dip.go create mode 100644 main.go create mode 100644 switch.go create mode 100644 switch_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6dac615 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +IPCenter +ipcenter diff --git a/actives.json b/actives.json new file mode 100644 index 0000000..d61c80a --- /dev/null +++ b/actives.json @@ -0,0 +1,28 @@ +{ + "switch": { + "192.168.6.100:8080": { + "HostMap": {} + }, + "192.168.6.100:8082": { + "HostMap": {} + }, + "192.168.6.100:8083": { + "HostMap": {} + }, + "192.168.6.100:8084": { + "HostMap": {} + }, + "192.168.6.100:8085": { + "HostMap": {} + }, + "192.168.6.100:8086": { + "HostMap": {} + }, + "192.168.6.100:8087": { + "HostMap": {} + }, + "192.168.6.100:8088": { + "HostMap": {} + } + } +} \ No newline at end of file diff --git a/actives.yaml b/actives.yaml new file mode 100644 index 0000000..5f45e8a --- /dev/null +++ b/actives.yaml @@ -0,0 +1,9 @@ +switch: + 192.168.6.100:8080: ["10.10.10.70", "10.10.10.71"] + 192.168.6.100:8082: ["10.10.10.74", "10.10.10.75"] + 192.168.6.100:8083: ["10.10.10.76", "10.10.10.77"] + 192.168.6.100:8085: ["10.10.10.80", "10.10.10.81"] + 192.168.6.100:8087: ["10.10.10.84", "10.10.10.85"] + 192.168.6.100:8088: ["10.10.10.86", "10.10.10.87"] + 192.168.6.100:8091: ["10.10.10.92", "10.10.10.93"] +restart: 65 \ No newline at end of file diff --git a/alertover.go b/alertover.go new file mode 100644 index 0000000..6a768ac --- /dev/null +++ b/alertover.go @@ -0,0 +1,60 @@ +package main + +import ( + "log" + "sync" + "time" + + "github.com/levigross/grequests" +) + +// AlertOver 报警相关 +type AlertOver struct { + Collection sync.Map // k 是title v是时间戳 用于统计上次的发送时间, 发送题目不能重复 + Setting map[string]string +} + +// SetDefaultSetting 设置默认Setting +func (ao *AlertOver) SetDefaultSetting() { + ao.Setting = map[string]string{ + "source": "s-577f047d-763a-4f45-a652-475595dc", + "receiver": "g-5b4ce1ea-7f6b-422a-9127-a9049c65", + "priority": "1", + } +} + +func (ao *AlertOver) send(data map[string]string) { + // 合并初始化设置 + for k, v := range ao.Setting { + data[k] = v + } + + resp, err := grequests.Post("https://api.alertover.com/v1/alert", + &grequests.RequestOptions{Data: data}) + + now := time.Now().Unix() + ao.Collection.Store(data["title"], now) + + ErrorLog(err) + log.Println(resp.String()) +} + +// Alert 报警API +func (ao *AlertOver) Alert(title, content string) { + + data := map[string]string{ + "title": title, + "content": content, + } + + now := time.Now().Unix() + if _t, ok := ao.Collection.Load(title); ok { + lasttime := _t.(int64) + if now-lasttime >= 600 { + ao.send(data) + } + } else { + ao.Collection.Store(title, now) + ao.send(data) + } +} diff --git a/dip.go b/dip.go new file mode 100644 index 0000000..eaac63f --- /dev/null +++ b/dip.go @@ -0,0 +1,158 @@ +package main + +import ( + "io/ioutil" + "log" + "strconv" + "strings" + "sync" + "time" + + "474420502.top/eson/requests" + "gopkg.in/yaml.v2" +) + +type DIPWorker struct { + ForMatch map[string]*DIPGroup `yaml:"switch"` + ForLoop map[string]*DIPGroup + Restart int64 `yaml:"restart"` + Mutex *sync.Mutex +} + +// ShowGroupInfo 展示实时的 Group 信息动态 +func (worker *DIPWorker) ShowGroupInfo() string { + + now := time.Now().Unix() + + content := "" + for addr, group := range worker.ForLoop { + content += addr + ": [" + for _, dhost := range group.Group { + + overtimeLabel := "" + if now-dhost.ActiveTime >= 150 { + overtimeLabel = "<☠?>" + } + tm := time.Unix(dhost.ActiveTime, 0) + tm.Format("2006-01-02 15:04:05") //2018-07-11 15:10:19 + content += dhost.Host + "(" + tm.Format("2006-01-02 15:04:05") + overtimeLabel + ")," + } + content = strings.TrimRight(content, ",") + "]\n" + } + content = strings.TrimRight(content, "\n") + return content +} + +type DIPGroup struct { + Group map[string]*DHost // 原始参照 + Ready map[string]*DHost + IPTableNum string + Current *DHost + + Mutex *sync.Mutex +} + +func NewDIPGroup() *DIPGroup { + group := DIPGroup{} + group.Group = make(map[string]*DHost) + group.Ready = make(map[string]*DHost) + group.Mutex = &sync.Mutex{} + return &group +} + +func (group *DIPGroup) Choose(addr string) { + now := time.Now().Unix() + for ip, dhost := range group.Ready { + // 设置转换的iptable + if group.Current == nil { + group.Current = dhost + group.Current.ActiveTime = now + SetAddrForward(group.IPTableNum, addr, ip) + } else { + restartAddr := "http://" + group.Current.Host + ":8800/pppoe/restart" + if _, err := requests.NewSession().Get(restartAddr).Execute(); err != nil { + log.Println(err) + // group.Current.ActiveTime += 12 + } else { + group.Current = dhost + group.Current.ActiveTime = now + // log.Println("new set addr:", group.Current.Host, "restartAddr:", restartAddr, "resp", resp.Content()) + SetAddrForward(group.IPTableNum, addr, ip) + } + } + + delete(group.Ready, ip) + + break + } +} + +type DHost struct { + Host string + ActiveTime int64 +} + +func NewDHost(host string) *DHost { + dh := DHost{} + dh.ActiveTime = time.Now().Unix() + dh.Host = host + return &dh +} + +func (dh *DHost) OverTime(now int64) int64 { + if now-dh.ActiveTime >= 150 { + return now - dh.ActiveTime + } + return 0 +} + +func (dip *DIPWorker) UnmarshalYAML(unmarshal func(interface{}) error) error { + var data map[string]interface{} + if err := unmarshal(&data); err != nil { + return err + } + + log.Println(data["switch"]) + myswitch := data["switch"] + + dip.Restart = int64(data["restart"].(int)) + + num := 1 + for k, v := range myswitch.(map[interface{}]interface{}) { + + group := NewDIPGroup() + + // sudo iptables -t nat -R IPSWITCH 1 -p icmp -j DNAT --to 1.1.1.1:8885 + for _, host := range v.([]interface{}) { + dhost := NewDHost(host.(string)) + group.Group[dhost.Host] = dhost + dip.ForMatch[dhost.Host] = group + } + + dip.ForLoop[k.(string)] = group + group.IPTableNum = strconv.Itoa(num) + num++ + } + + return nil +} + +// NewDipWorker 创建一个 +func NewDipWorker(filename string) *DIPWorker { + worker := DIPWorker{} + worker.ForMatch = make(map[string]*DIPGroup) + worker.ForLoop = make(map[string]*DIPGroup) + + worker.Mutex = &sync.Mutex{} + + data, err := ioutil.ReadFile(filename) + if err != nil { + panic(err) + } + err = yaml.Unmarshal(data, &worker) + if err != nil { + panic(err) + } + + return &worker +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..df84c36 --- /dev/null +++ b/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "log" +) + +var passwd = "youmi" + +// ErrorLog 错误打日志 +func ErrorLog(err error) bool { + if err != nil { + log.Println(err) + return true + } + return false +} + +// ErrorPanic 错误打日志 +func ErrorPanic(err error) { + if err != nil { + panic(err) + } +} + +func main() { + log.SetFlags(log.Llongfile | log.LstdFlags) + swi := NewSwitch() + swi.Run("0.0.0.0:3333", `10\.10\..+`) + // swi.Run("0.0.0.0:3333", ``) + log.Fatal() +} diff --git a/switch.go b/switch.go new file mode 100644 index 0000000..8415950 --- /dev/null +++ b/switch.go @@ -0,0 +1,175 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + "os/exec" + "regexp" + "strings" + "time" +) + +// Switch 轮换IP相关类 +type Switch struct { + // config map[string]interface{} + // configMutex sync.Mutex + ipregion *regexp.Regexp + lastShow int64 + lastSwitch int64 + + Worker *DIPWorker + AO *AlertOver +} + +// SetAddrForward 设置当前ip 转发的节点 +func SetAddrForward(num, addr, ip string) { + port := strings.Split(addr, ":")[1] + cmd := exec.Command("/bin/sh", "-c", "sudo iptables -t nat -R IPSWITCH "+num+" -p tcp --dport "+port+" -j DNAT --to "+ip+":8885") + err := cmd.Run() + if err != nil { + panic(err) + } +} + +// NewSwitch 初始化默认 +func NewSwitch() *Switch { + swi := Switch{} + swi.Worker = NewDipWorker("actives.yaml") + swi.AO = &AlertOver{} + swi.AO.SetDefaultSetting() + + log.Println(swi.Worker.ShowGroupInfo()) + + http.HandleFunc("/ippool/switch/imactive", swi.imActive) + http.HandleFunc("/ippool/switch/update", swi.updateActives) + http.HandleFunc("/ippool/switch/actives", swi.switchActives) + + return &swi +} + +// Run addr 监听的地址addr +// ipregion 匹配adsl的规则, 作为ping服务器并且控制网络转发的调度 +func (swi *Switch) Run(addr string, ipregion string) { + swi.ipregion = regexp.MustCompile(ipregion) + + cmd := exec.Command("/bin/sh", "-c", "sudo iptables -t nat -N IPSWITCH") + cmd.Run() + + cmd = exec.Command("/bin/sh", "-c", "sudo iptables -t nat -F IPSWITCH") + cmd.Run() + + for range swi.Worker.ForLoop { + cmd := exec.Command("/bin/sh", "-c", "sudo iptables -t nat -A IPSWITCH -p icmp -s 1.1.1.1 -j DNAT --to 1.1.1.1:8885") + err := cmd.Run() + if err != nil { + panic(err) + } + } + + http.ListenAndServe(addr, nil) + +} + +// timeToSwitch 计算是否到切换时间 +func (swi *Switch) timeToSwitch(now int64) { + for addr, group := range swi.Worker.ForLoop { + if group.Current == nil { + group.Choose(addr) + } else { + if now >= group.Current.ActiveTime+swi.Worker.Restart { + // log.Println(now, group.Current.ActiveTime, swi.Worker.Restart) + group.Choose(addr) + } + } + } +} + +// checkInReady 签到的IP(vps的vpn节点IP)) +func (swi *Switch) checkInReady(ip string, now int64) { + + if dipg, ok := swi.Worker.ForMatch[ip]; ok { + + dipg.Group[ip].ActiveTime = now + + if dipg.Current == nil { + dhost := NewDHost(ip) + dhost.ActiveTime = now + dipg.Ready[ip] = dhost + } else { + + if dipg.Current.Host != ip { + if v, ok := dipg.Ready[ip]; ok { + v.ActiveTime = now + } else { + dhost := NewDHost(ip) + dhost.ActiveTime = now + dipg.Ready[ip] = dhost + } + } + + } + } +} + +// imActive 子节点访问 证明自己是活跃 +func (swi *Switch) imActive(w http.ResponseWriter, r *http.Request) { + + w.Write([]byte("ok")) + ip := strings.Split(r.RemoteAddr, ":")[0] + // log.Println(ip) // 后续可以把这些节点, 自动活跃与更新 + + swi.Worker.Mutex.Lock() + defer swi.Worker.Mutex.Unlock() + + now := time.Now().Unix() + + // 60秒show一次日志 + if now >= swi.lastShow+60 { + swi.lastShow = now + log.Println("\n" + swi.Worker.ShowGroupInfo()) + } + + // 5秒一次检测 + if now >= swi.lastSwitch+5 { + swi.lastSwitch = now + swi.timeToSwitch(now) + } + + swi.checkInReady(ip, now) +} + +// updateActives 更新最新配置 +func (swi *Switch) updateActives(w http.ResponseWriter, req *http.Request) { + + swi.Worker.Mutex.Lock() + defer swi.Worker.Mutex.Unlock() + + swi.Worker = NewDipWorker("actives.yaml") + w.Write([]byte("update success!")) +} + +func (swi *Switch) switchActives(w http.ResponseWriter, req *http.Request) { + + swi.Worker.Mutex.Lock() + defer swi.Worker.Mutex.Unlock() + + now := time.Now().Unix() + + var content []string + for addr, group := range swi.Worker.ForLoop { + + isappend := int64(1) + for _, dhost := range group.Group { + isappend *= dhost.OverTime(now) + } + if isappend == 0 { + content = append(content, addr) + } + } + jdata, err := json.Marshal(content) + ErrorLog(err) + _, err = w.Write(jdata) + ErrorLog(err) + +} diff --git a/switch_test.go b/switch_test.go new file mode 100644 index 0000000..a5760d9 --- /dev/null +++ b/switch_test.go @@ -0,0 +1,14 @@ +package main + +import ( + "testing" +) + +func TestSwitchYaml(t *testing.T) { + t.Error() + + swi := NewSwitch() + if swi == nil { + t.Error("swi is error") + } +}