From 3bd4d4fb0c793cc2b59684c3ee0816ead4ed5a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=88=9F?= Date: Thu, 26 May 2022 14:22:13 +0800 Subject: [PATCH] feat: implement route checking and always restore route when exiting --- pkg/kt/command/clean/common.go | 8 +- pkg/kt/command/general/teardown.go | 6 ++ pkg/kt/command/options/clean.go | 5 - pkg/kt/command/options/options.go | 1 - pkg/kt/service/tun/tun_darwin.go | 31 ++++++- pkg/kt/service/tun/tun_linux.go | 30 +++++- pkg/kt/service/tun/tun_windows.go | 142 ++++++++++++++++++++++------- 7 files changed, 175 insertions(+), 48 deletions(-) diff --git a/pkg/kt/command/clean/common.go b/pkg/kt/command/clean/common.go index 0b9a4d72..f6f6d4a8 100644 --- a/pkg/kt/command/clean/common.go +++ b/pkg/kt/command/clean/common.go @@ -170,11 +170,9 @@ func TidyLocalResources() { dns.DropHosts() log.Debug().Msg("Cleaning DNS configuration") dns.Ins().RestoreNameServer() - if opt.Get().Clean.SweepLocalRoute { - log.Info().Msgf("Cleaning route table") - if err := tun.Ins().RestoreRoute(); err != nil { - log.Warn().Err(err).Msgf("Unable to clean up route table") - } + log.Info().Msgf("Cleaning route table") + if err := tun.Ins().RestoreRoute(); err != nil { + log.Warn().Err(err).Msgf("Unable to clean up route table") } } else { log.Info().Msgf("Not %s user, DNS cleanup skipped", util.GetAdminUserName()) diff --git a/pkg/kt/command/general/teardown.go b/pkg/kt/command/general/teardown.go index 1a989675..a1fb2ba2 100644 --- a/pkg/kt/command/general/teardown.go +++ b/pkg/kt/command/general/teardown.go @@ -6,6 +6,7 @@ import ( opt "github.com/alibaba/kt-connect/pkg/kt/command/options" "github.com/alibaba/kt-connect/pkg/kt/service/cluster" "github.com/alibaba/kt-connect/pkg/kt/service/dns" + "github.com/alibaba/kt-connect/pkg/kt/service/tun" "github.com/alibaba/kt-connect/pkg/kt/util" "github.com/rs/zerolog/log" "os" @@ -38,6 +39,11 @@ func recoverGlobalHostsAndProxy() { log.Debug().Msg("Dropping hosts records ...") dns.DropHosts() } + if strings.HasPrefix(opt.Get().Connect.DnsMode, util.DnsModeLocalDns) { + if err := tun.Ins().RestoreRoute(); err != nil { + log.Debug().Err(err).Msgf("Failed to restore route table") + } + } } func cleanLocalFiles() { diff --git a/pkg/kt/command/options/clean.go b/pkg/kt/command/options/clean.go index cb5686d8..d8a1f278 100644 --- a/pkg/kt/command/options/clean.go +++ b/pkg/kt/command/options/clean.go @@ -21,11 +21,6 @@ func CleanFlags() []OptionConfig { DefaultValue: false, Description: "Only check and restore local changes made by kt", }, - { - Target: "SweepLocalRoute", - DefaultValue: false, - Description: "(Beta) Also clean up local route table record created by kt", - }, } return flags } diff --git a/pkg/kt/command/options/options.go b/pkg/kt/command/options/options.go index 30c6ac43..a4e85276 100644 --- a/pkg/kt/command/options/options.go +++ b/pkg/kt/command/options/options.go @@ -60,7 +60,6 @@ type CleanOptions struct { DryRun bool ThresholdInMinus int64 LocalOnly bool - SweepLocalRoute bool } // ConfigOptions ... diff --git a/pkg/kt/service/tun/tun_darwin.go b/pkg/kt/service/tun/tun_darwin.go index 2706ef15..c761b79b 100644 --- a/pkg/kt/service/tun/tun_darwin.go +++ b/pkg/kt/service/tun/tun_darwin.go @@ -12,7 +12,7 @@ import ( // CheckContext check everything needed for tun setup func (s *Cli) CheckContext() error { - // TODO: check whether ifconfig and route command exists + // TODO: check whether ifconfig, route and netstat command exists return nil } @@ -70,11 +70,38 @@ func (s *Cli) SetRoute(ipRange []string) error { // CheckRoute check whether all route rule setup properly func (s *Cli) CheckRoute(ipRange []string) []string { - return []string{} + var failedIpRange []string + // run command: netstat -rn + out, _, err := util.RunAndWait(exec.Command("netstat", + "-rn", + )) + if err != nil { + log.Warn().Msgf("Failed to get route table") + return failedIpRange + } + util.BackgroundLogger.Write([]byte(">> Get route: " + out + util.Eol)) + + for _, ir := range ipRange { + found := false + for _, line := range strings.Split(out, util.Eol) { + ip := strings.Split(ir, "/")[0] + if strings.HasPrefix(line, ip) { + if strings.HasSuffix(line, s.GetName()) { + found = true + } + break + } + } + if !found { + failedIpRange = append(failedIpRange, ir) + } + } + return failedIpRange } // RestoreRoute delete route rules made by kt func (s *Cli) RestoreRoute() error { + // Route will be auto removed when tun device destroyed return nil } diff --git a/pkg/kt/service/tun/tun_linux.go b/pkg/kt/service/tun/tun_linux.go index 7896a7ea..50fe50a9 100644 --- a/pkg/kt/service/tun/tun_linux.go +++ b/pkg/kt/service/tun/tun_linux.go @@ -4,6 +4,7 @@ import ( "github.com/alibaba/kt-connect/pkg/kt/util" "github.com/rs/zerolog/log" "os/exec" + "strings" ) // CheckContext check everything needed for tun setup @@ -53,11 +54,38 @@ func (s *Cli) SetRoute(ipRange []string) error { // CheckRoute check whether all route rule setup properly func (s *Cli) CheckRoute(ipRange []string) []string { - return []string{} + var failedIpRange []string + // run command: ip route show + out, _, err := util.RunAndWait(exec.Command("ip", + "route", + "show", + )) + if err != nil { + log.Warn().Msgf("Failed to get route table") + return failedIpRange + } + util.BackgroundLogger.Write([]byte(">> Get route: " + out + util.Eol)) + + for _, ir := range ipRange { + found := false + for _, line := range strings.Split(out, util.Eol) { + if strings.HasPrefix(line, ir) { + if strings.HasSuffix(line, s.GetName()) { + found = true + } + break + } + } + if !found { + failedIpRange = append(failedIpRange, ir) + } + } + return failedIpRange } // RestoreRoute delete route rules made by kt func (s *Cli) RestoreRoute() error { + // Route will be auto removed when tun device destroyed return nil } diff --git a/pkg/kt/service/tun/tun_windows.go b/pkg/kt/service/tun/tun_windows.go index aeca0696..7f78ea0f 100644 --- a/pkg/kt/service/tun/tun_windows.go +++ b/pkg/kt/service/tun/tun_windows.go @@ -9,6 +9,12 @@ import ( "strings" ) +type RouteRecord struct { + TargetRange string + InterfaceIndex string + InterfaceName string +} + // CheckContext check everything needed for tun setup func (s *Cli) CheckContext() (err error) { defer func() { @@ -87,14 +93,92 @@ func (s *Cli) SetRoute(ipRange []string) error { // CheckRoute check whether all route rule setup properly func (s *Cli) CheckRoute(ipRange []string) []string { - return []string{} + var failedIpRange []string + records, err := getKtRouteRecords(s) + if err != nil { + log.Warn().Err(err).Msgf("Route check skipped") + return []string{} + } + + for _, ir := range ipRange { + found := false + for _, r := range records { + if ir == r.TargetRange { + found = true + break + } + } + if !found { + failedIpRange = append(failedIpRange, ir) + } + } + + return failedIpRange } // RestoreRoute delete route rules made by kt func (s *Cli) RestoreRoute() error { var lastErr error - // run command: netsh interface ipv4 show route store=persistent + records, err := getKtRouteRecords(s) + if err != nil { + return err + } + + for _, r := range records { + // run command: netsh interface ipv4 delete route store=persistent 172.20.0.0/16 29 172.20.0.0 + _, _, err = util.RunAndWait(exec.Command("netsh", + "interface", + "ipv4", + "delete", + "route", + "store=persistent", + r.TargetRange, + r.InterfaceIndex, + r.InterfaceName, + )) + if err != nil { + log.Warn().Msgf("Failed to clean route to %s", r.TargetRange) + lastErr = err + } else { + log.Info().Msgf(" * %s", r.TargetRange) + } + } + return lastErr +} + +func (s *Cli) GetName() string { + return util.TunNameWin +} + +func getKtRouteRecords(s *Cli) ([]RouteRecord, error) { + records := []RouteRecord{} + var ktIdx string + + // run command: netsh interface ipv4 show interfaces out, _, err := util.RunAndWait(exec.Command("netsh", + "interface", + "ipv4", + "show", + "interfaces", + )) + if err != nil { + log.Error().Msgf("failed to get network interfaces") + return nil, err + } + util.BackgroundLogger.Write([]byte(">> Get interfaces: " + out + util.Eol)) + + for _, line := range strings.Split(out, util.Eol) { + if strings.HasSuffix(line, s.GetName()) { + ktIdx = strings.SplitN(strings.TrimPrefix(line, " "), " ", 2)[0] + break + } + } + if ktIdx == "" { + return nil, fmt.Errorf("failed to found kt network interface") + } + + // run command: netsh interface ipv4 show route store=persistent + out, _, err = util.RunAndWait(exec.Command("netsh", "interface", "ipv4", "show", @@ -103,57 +187,47 @@ func (s *Cli) RestoreRoute() error { )) if err != nil { log.Warn().Msgf("failed to get route table") - return err + return nil, err } util.BackgroundLogger.Write([]byte(">> Get route: " + out + util.Eol)) + reachRecord := false for _, line := range strings.Split(out, util.Eol) { - // Assume only kt using gateway address of x.x.x.0 - if !strings.HasSuffix(line, ".0") { + if strings.HasPrefix(line, "--") && strings.HasSuffix(line, "--") { + reachRecord = true + continue + } + if !reachRecord { continue } parts := strings.Split(line, " ") ipRange := "" + idx := "" iface := "" - gateway := "" index := 0 - for i := len(parts) - 1; i >= 0; i-- { + for i := 0; i < len(parts); i++ { if parts[i] != "" { - index++ if index == 3 { ipRange = parts[i] break - } else if index == 2 { + } else if index == 4 { + idx = parts[i] + } else if index == 5 { iface = parts[i] - } else if index == 1 { - gateway = parts[i] + } else if index > 5 { + iface = fmt.Sprintf("%s %s", iface, parts[i]) } + index++ } } - if ipRange == "" { + if ktIdx != idx && ipRange != "" && iface != "" { continue } - // run command: netsh interface ipv4 delete route store=persistent 172.20.0.0/16 29 172.20.0.0 - _, _, err = util.RunAndWait(exec.Command("netsh", - "interface", - "ipv4", - "delete", - "route", - "store=persistent", - ipRange, - iface, - gateway, - )) - if err != nil { - log.Warn().Msgf("Failed to clean route to %s", ipRange) - lastErr = err - } else { - log.Info().Msgf(" * %s", ipRange) - } + records = append(records, RouteRecord{ + TargetRange: ipRange, + InterfaceIndex: idx, + InterfaceName: iface, + }) } - return lastErr -} - -func (s *Cli) GetName() string { - return util.TunNameWin + return records, nil }