From e97e8c903aff63d6d2e8abd9b83765a5f180f135 Mon Sep 17 00:00:00 2001 From: huangsimin Date: Thu, 9 Aug 2018 12:51:49 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A8=B3=E5=AE=9Av1.0.0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dutiesnet.go | 156 ++++++++++++++++++++++++++++++++++++++++++ dutiesvpn.go | 54 +++++++++++++++ main.go | 97 ++++++++++++++++++++++++++ pingworker.go | 64 +++++++++++++++++ pppoe_control.service | 13 ++++ 5 files changed, 384 insertions(+) create mode 100644 dutiesnet.go create mode 100644 dutiesvpn.go create mode 100644 main.go create mode 100644 pingworker.go create mode 100644 pppoe_control.service diff --git a/dutiesnet.go b/dutiesnet.go new file mode 100644 index 0000000..bfde4fb --- /dev/null +++ b/dutiesnet.go @@ -0,0 +1,156 @@ +package main + +import ( + "log" + "net" + "os/exec" + "strings" + "sync/atomic" + "time" +) + +// DutiesNet 保证PPPoe可行 +type DutiesNet struct { + PW *PingWorker + + NetProtectTime int64 // 当前时间大于保护时间,才能进行重启的计算操作(pppoe最大重启保护时间, 保证重启后不会重复接受重启命令而造成不停重启 ) + MaxProtectTime int64 // NetProtectTime保护时间的最大值 + CmdRestart int64 // 只要大于MaxRestart就重启 + MaxRestart int64 // 达到这个值就重启 +} + +// Default 设置默认值 +func (duties *DutiesNet) Default(pw *PingWorker) { + duties.PW = pw + atomic.StoreInt64(&duties.MaxProtectTime, 15) + atomic.StoreInt64(&duties.CmdRestart, 0) + duties.MaxRestart = 4 + go duties.Duties() +} + +// IsPPP0Up Pppoe是否是UP的状态 +func (duties *DutiesNet) IsPPP0Up() bool { + ifaces, err := net.Interfaces() + if ErrorLog(err) { + return false + } + + for _, i := range ifaces { + if i.Name == "ppp0" { + flags := strings.Split(i.Flags.String(), "|") + if len(flags) > 0 { + log.Println(flags) + if flags[0] != "up" { + return false + } + } + } + } + return true +} + +// IsPPP1Exist ppp1是否是UP的状态 +func (duties *DutiesNet) IsPPP1Exist() bool { + ifaces, err := net.Interfaces() + if ErrorLog(err) { + return false + } + + for _, i := range ifaces { + if i.Name == "ppp1" { + return true + } + } + return false +} + +// PPP0Restart ppp0 重启 +func (duties *DutiesNet) PPP0Restart() { + log.Println("PPP0Restart") + _, err := exec.Command("ifdown", "ppp1").Output() + ErrorLog(err) + _, err = exec.Command("ifdown", "ppp0").Output() + ErrorLog(err) + time.Sleep(time.Second * 1) + _, err = exec.Command("ifdown", "ppp0").Output() + ErrorLog(err) + time.Sleep(time.Second * 1) + _, err = exec.Command("ifup", "ppp0").Output() + ErrorLog(err) + time.Sleep(time.Second * 5) + _, err = exec.Command("ip", "route", "replace", "default", "dev", "ppp0").CombinedOutput() + ErrorLog(err) + + atomic.StoreInt64(&duties.NetProtectTime, time.Now().Unix()+duties.MaxProtectTime) + atomic.StoreInt64(&duties.CmdRestart, 0) +} + +// SetRestart 设置重启 异步 +func (duties *DutiesNet) SetRestart() { + log.Println("SetRestart") + now := time.Now().Unix() + // 当前时间大于保护时间,才能进行重启的计算操作 + if now >= duties.NetProtectTime { + log.Println("SetRestart Success") + atomic.AddInt64(&duties.CmdRestart, duties.MaxRestart+20) // 设置超过最大的值的20, 保证重启. + } +} + +func (duties *DutiesNet) checkError1() bool { + if duties.IsPPP1Exist() { + log.Println("IsPPP1Exist not") + duties.PPP0Restart() + return true + } + return false // 检测错误才返回true 否则 false +} + +func (duties *DutiesNet) checkError2() bool { + // 检测3次ppp0是否处于Up的状态(pppoe有可能存在Down的状态, 这种错误. 但是判断为已经成功拨号) + for i := 0; i < 4; i++ { + if duties.IsPPP0Up() { + return false + } + if i == 3 { + log.Println("IsPPP0Up not") + duties.PPP0Restart() // 强制重启, 不受最大值的限制 + return true + } + time.Sleep(time.Second * 1) + } + + return false +} + +//Duties 核心循环过程, 保证pppoe的网络可行 +func (duties *DutiesNet) Duties() { + for { + // 如果超过MaxRestart也会重启 + if atomic.LoadInt64(&duties.CmdRestart) > duties.MaxRestart { + log.Println("CmdRestart ", atomic.LoadInt64(&duties.CmdRestart)) + duties.PPP0Restart() + } + + time.Sleep(time.Second * 2) + + // 如果网络不能ping通 执行下面的处理 + if !duties.PW.PingNet() { + time.Sleep(time.Second * 1) + atomic.AddInt64(&duties.CmdRestart, 1) // 重启因素的积累, 出一次错误就积累一次. 达到最大值后重启 + + if duties.checkError1() { + continue + } + + if duties.checkError2() { + continue + } + + } else { + if atomic.LoadInt64(&duties.CmdRestart) > 0 { + atomic.AddInt64(&duties.CmdRestart, -1) // 如果Ping成功就减少重启因素积累. 0为最低限制值 + } + } + + } +} diff --git a/dutiesvpn.go b/dutiesvpn.go new file mode 100644 index 0000000..608af1a --- /dev/null +++ b/dutiesvpn.go @@ -0,0 +1,54 @@ +package main + +import ( + "os/exec" + "sync/atomic" + "time" +) + +// DutiesVPN VPN相关的责任 +type DutiesVPN struct { + PW *PingWorker + VPNProtectTime int64 // 保护时间 + MaxProtectTime int64 // 保护时间的最大值 +} + +// Default 设置默认值 +func (duties *DutiesVPN) Default(pw *PingWorker) { + duties.PW = pw + atomic.StoreInt64(&duties.MaxProtectTime, 40) //保护时间初始化为40秒 + go duties.Duties() +} + +//Reset 重置 +func (duties *DutiesVPN) Reset() { + atomic.StoreInt64(&duties.VPNProtectTime, time.Now().Unix()) +} + +// RestartConnect 重置Openvpn连接 +func (duties *DutiesVPN) RestartConnect() { + _, err := exec.Command("pkill", "openvpn").CombinedOutput() + ErrorLog(err) + atomic.StoreInt64(&duties.VPNProtectTime, time.Now().Unix()+duties.MaxProtectTime) //重启后给予40秒openvpn的重试时间 +} + +// Duties 保证openvpn的可行 +func (duties *DutiesVPN) Duties() { + + duties.Reset() + + for { + time.Sleep(time.Second * 2) + + if duties.PW.PingNet() { + if !duties.PW.PingVPN() { + now := time.Now().Unix() + // openvpn需要一段时间connect 才能知道是否连接成功. 所以所有一段保护时间确保这段时间内是进行connect. + if now >= atomic.LoadInt64(&duties.VPNProtectTime) { + duties.RestartConnect() + continue + } + } + } + } +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..94aca46 --- /dev/null +++ b/main.go @@ -0,0 +1,97 @@ +package main + +import ( + "log" + "net" + "net/http" + "os" + "strconv" + "time" +) + +// ErrorPanic 错误检测 +func ErrorPanic(err error) { + if err != nil { + log.Panic(err) + } +} + +// ErrorLog 错误检测 +func ErrorLog(err error) bool { + if err != nil { + // log.Println(err) + return true + } + return false +} + +// Worker 维护所有系统正常的核心类 +type Worker struct { + LogFile *os.File + + DNET *DutiesNet + DVPN *DutiesVPN +} + +func checkTunExist() bool { + ifaces, err := net.Interfaces() + if err != nil { + log.Println(err) + return false + } + + for _, i := range ifaces { + if i.Name == "tun0" { + return true + } + } + return false +} + +// CmdRestartHandler 接收到该命令就重启 +func (worker *Worker) CmdRestartHandler(w http.ResponseWriter, r *http.Request) { + _tm := r.PostFormValue("tm") + if _tm != "" { + tm, err := strconv.ParseInt(_tm, 10, 64) + if !ErrorLog(err) { + log.Println(tm) + worker.DNET.SetRestart() + } + } + w.Write([]byte("ok")) +} + +// CmdImOKHandler 可以ping通 +func (worker *Worker) CmdImOKHandler(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("ok")) +} + +func testSync(f *os.File) { + for { + time.Sleep(time.Second * 1) + f.Sync() + } +} + +func main() { + log.SetFlags(log.Lshortfile | log.LstdFlags) + f, err := os.OpenFile("/root/pppoe_control.log", os.O_SYNC|os.O_APPEND|os.O_CREATE|os.O_RDWR, 0660) + ErrorPanic(err) + log.SetOutput(f) + defer f.Close() + go testSync(f) + + pw := &PingWorker{} + + dnet := &DutiesNet{} + dnet.Default(pw) + + dvpn := &DutiesVPN{} + dvpn.Default(pw) + + worker := Worker{f, dnet, dvpn} + + http.HandleFunc("/pppoe/restart", worker.CmdRestartHandler) + http.HandleFunc("/pppoe/imok", worker.CmdImOKHandler) + http.ListenAndServe(":8800", nil) +} diff --git a/pingworker.go b/pingworker.go new file mode 100644 index 0000000..5d902c9 --- /dev/null +++ b/pingworker.go @@ -0,0 +1,64 @@ +package main + +import ( + "log" + "net/http" + "sync/atomic" + "time" +) + +// PingWorker 专为ping服务, 保证不存在多余的Ping的, 让终端服务的请求合理减少. +type PingWorker struct { + LastPingVPNTime int64 + LastPingVPN int64 //上次Ping的结果 0 或者 1 + + LastPingNetTime int64 + LastPingNet int64 //上次Ping的结果 0 或者 1 +} + +// PingNet 确认adsl网络通信 +func (pw *PingWorker) PingNet() bool { + now := time.Now().Unix() + if now-atomic.LoadInt64(&pw.LastPingNetTime) <= 2 { + return atomic.LoadInt64(&pw.LastPingNet) == 1 + } + + timeout := time.Duration(5 * time.Second) + client := http.Client{ + Timeout: timeout, + } + _, err := client.Get("http://14.17.96.148:3333/ippool/switch/imactive") + if err != nil { + log.Println("PingNet err http://14.17.96.148:3333/ippool/switch/imactive check Network") + atomic.StoreInt64(&pw.LastPingNet, 0) + atomic.StoreInt64(&pw.LastPingNetTime, time.Now().Unix()) + return false + } + atomic.StoreInt64(&pw.LastPingNet, 1) + atomic.StoreInt64(&pw.LastPingNetTime, time.Now().Unix()) + return true +} + +// PingVPN 确认确认外网网络通信 +func (pw *PingWorker) PingVPN() bool { + now := time.Now().Unix() + if now-atomic.LoadInt64(&pw.LastPingVPNTime) <= 2 { + return atomic.LoadInt64(&pw.LastPingVPN) == 1 + } + + timeout := time.Duration(5 * time.Second) + client := http.Client{ + Timeout: timeout, + } + _, err := client.Get("http://10.10.10.1:3333/ippool/switch/imactive") + if err != nil { + log.Println("PingVPN err http://10.10.10.1:3333/ippool/switch/imactive check Network") + atomic.StoreInt64(&pw.LastPingVPN, 0) + atomic.StoreInt64(&pw.LastPingVPNTime, time.Now().Unix()) + return false + } + + atomic.StoreInt64(&pw.LastPingVPN, 1) + atomic.StoreInt64(&pw.LastPingVPNTime, time.Now().Unix()) + return true +} diff --git a/pppoe_control.service b/pppoe_control.service new file mode 100644 index 0000000..84caff7 --- /dev/null +++ b/pppoe_control.service @@ -0,0 +1,13 @@ +[Unit] +Description=pppoe_control +After=network.target +Wants=network.target + +[Service] +Type=simple +PIDFile=/var/run/pppoe_control.pid +ExecStart=/usr/local/bin/pppoe_control +Restart=always + +[Install] +WantedBy=multi-user.target