Files

117 lines
2.8 KiB
Go

package trafficmode
import (
"fmt"
"strconv"
"strings"
)
type RunCommandFunc func(name string, args ...string) (stdout string, stderr string, code int, err error)
type RulesConfig struct {
RoutesTableName string
Mark string
MarkIngress string
PrefSelective int
PrefFull int
PrefMarkIngressReply int
ModeFull string
ModeSelective string
ModeDirect string
}
type RulesState struct {
Mark bool
Full bool
IngressReply bool
}
func PrefStr(v int) string {
return strconv.Itoa(v)
}
func ReadRules(cfg RulesConfig, run RunCommandFunc) RulesState {
if run == nil {
return RulesState{}
}
out, _, _, _ := run("ip", "rule", "show")
var st RulesState
for _, line := range strings.Split(out, "\n") {
l := strings.ToLower(strings.TrimSpace(line))
if l == "" {
continue
}
fields := strings.Fields(l)
if len(fields) == 0 {
continue
}
prefRaw := strings.TrimSuffix(fields[0], ":")
pref, _ := strconv.Atoi(prefRaw)
switch pref {
case cfg.PrefSelective:
if strings.Contains(l, "lookup "+cfg.RoutesTableName) {
st.Mark = true
}
case cfg.PrefFull:
if strings.Contains(l, "lookup "+cfg.RoutesTableName) {
st.Full = true
}
case cfg.PrefMarkIngressReply:
if strings.Contains(l, "fwmark "+strings.ToLower(cfg.MarkIngress)) && strings.Contains(l, "lookup main") {
st.IngressReply = true
}
}
}
return st
}
func DetectAppliedMode(cfg RulesConfig, rules RulesState) string {
if rules.Full {
return cfg.ModeFull
}
if rules.Mark {
return cfg.ModeSelective
}
return cfg.ModeDirect
}
func ProbeMode(cfg RulesConfig, mode string, iface string, run RunCommandFunc) (bool, string) {
if run == nil {
return false, "run command func is nil"
}
mode = strings.ToLower(strings.TrimSpace(mode))
iface = strings.TrimSpace(iface)
args := []string{"-4", "route", "get", "1.1.1.1"}
if mode == strings.ToLower(cfg.ModeSelective) {
args = append(args, "mark", cfg.Mark)
}
out, _, code, err := run("ip", args...)
if err != nil || code != 0 {
if err == nil {
err = fmt.Errorf("ip route get exited with %d", code)
}
return false, err.Error()
}
text := strings.ToLower(out)
switch mode {
case strings.ToLower(cfg.ModeDirect):
if strings.Contains(text, " table "+strings.ToLower(cfg.RoutesTableName)) {
return false, "route probe still uses agvpn table"
}
return true, "route probe direct path ok"
case strings.ToLower(cfg.ModeFull), strings.ToLower(cfg.ModeSelective):
if iface == "" {
return false, "route probe has empty iface"
}
if !strings.Contains(text, "dev "+strings.ToLower(iface)) {
return false, fmt.Sprintf("route probe mismatch: expected dev %s", iface)
}
return true, "route probe vpn path ok"
default:
return false, "route probe unknown mode"
}
}