proxy: autodetect traceroute args on startup (#69)
This commit is contained in:
24
README.md
24
README.md
@@ -79,6 +79,8 @@ Configuration is handled by [viper](https://github.com/spf13/viper), any config
|
||||
| name_filter | --name-filter | BIRDLG_NAME_FILTER | protocol names to hide in summary tables (RE2 syntax); defaults to none if not set |
|
||||
| timeout | --time-out | BIRDLG_TIMEOUT | time before request timed out, in seconds; defaults to 120 if not set |
|
||||
|
||||
### Examples
|
||||
|
||||
Example: the following command starts the frontend with 2 BIRD nodes, with domain name "gigsgigscloud.dn42.lantian.pub" and "hostdare.dn42.lantian.pub", and proxies are running on port 8000 on both nodes.
|
||||
|
||||
```bash
|
||||
@@ -122,16 +124,32 @@ Configuration can be set in:
|
||||
|
||||
Configuration is handled by [viper](https://github.com/spf13/viper), any config format supported by it can be used.
|
||||
|
||||
> Note: the config system is replaced with viper only recently (2022-07-08). If some config items do not work, please open an issue, and use commit [892a7bee22a1bb02d3b4da6d270c65b6e4e1321a](https://github.com/xddxdd/bird-lg-go/tree/892a7bee22a1bb02d3b4da6d270c65b6e4e1321a) (last version before config system replace) for the time being.
|
||||
|
||||
| Config Key | Parameter | Environment Variable | Description |
|
||||
| ---------- | --------- | -------------------- | ----------- |
|
||||
| allowed_ips | --allowed | ALLOWED_IPS | IPs allowed to access this proxy, separated by commas. Don't set to allow all IPs. (default "") |
|
||||
| bird_socket | --bird | BIRD_SOCKET | socket file for bird, set either in parameter or environment variable BIRD_SOCKET (default "/var/run/bird/bird.ctl") |
|
||||
| listen | --listen | BIRDLG_PROXY_PORT | listen address, set either in parameter or environment variable BIRDLG_PROXY_PORT(default "8000") |
|
||||
| traceroute_bin | --traceroute_bin | BIRDLG_TRACEROUTE_BIN | traceroute binary file, set either in parameter or environment variable BIRDLG_TRACEROUTE_BIN(default "traceroute") |
|
||||
| traceroute_bin | --traceroute_bin | BIRDLG_TRACEROUTE_BIN | traceroute binary file, set either in parameter or environment variable BIRDLG_TRACEROUTE_BIN |
|
||||
| traceroute_flags | --traceroute_flags | BIRDLG_TRACEROUTE_FLAGS | traceroute flags, repeat for multiple flags. |
|
||||
| traceroute_raw | --traceroute_raw | BIRDLG_TRACEROUTE_RAW | whether to display traceroute outputs raw (default false) |
|
||||
|
||||
### Traceroute Binary Autodetection
|
||||
|
||||
If `traceroute_bin` or `traceroute_flags` is not set, then on startup, the proxy will try to `traceroute 127.0.0.1` with different traceroute binaries and arguments, in order to use the most optimized setting available, while maintaining compatibility with multiple variants of traceroute binaries.
|
||||
|
||||
Traceroute binaries will be autodetected in the following order:
|
||||
|
||||
1. If `traceroute_bin` is set:
|
||||
1. `[traceroute_bin] -q1 -N32 -w1 127.0.0.1` (Corresponds to Traceroute on Debian)
|
||||
2. `[traceroute_bin] -q1 -w1 127.0.0.1` (Corresponds to Traceroute on FreeBSD)
|
||||
3. `[traceroute_bin] 127.0.0.1` (Corresponds to Busybox Traceroute)
|
||||
2. `mtr -w -c1 -Z1 -G1 -b 127.0.0.1` (MTR)
|
||||
3. `traceroute -q1 -N32 -w1 127.0.0.1` (Corresponds to Traceroute on Debian)
|
||||
4. `traceroute -q1 -w1 127.0.0.1` (Corresponds to Traceroute on FreeBSD)
|
||||
5. `traceroute 127.0.0.1` (Corresponds to Busybox Traceroute)
|
||||
|
||||
### Examples
|
||||
|
||||
Example: start proxy with default configuration, should work "out of the box" on Debian 9 with BIRDv1:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -54,6 +55,7 @@ type settingType struct {
|
||||
listen string
|
||||
allowedIPs []string
|
||||
tr_bin string
|
||||
tr_flags []string
|
||||
tr_raw bool
|
||||
}
|
||||
|
||||
@@ -62,6 +64,9 @@ var setting settingType
|
||||
// Wrapper of tracer
|
||||
func main() {
|
||||
parseSettings()
|
||||
tracerouteAutodetect()
|
||||
|
||||
fmt.Printf("Listening on %s...\n", setting.listen)
|
||||
|
||||
var l net.Listener
|
||||
var err error
|
||||
|
||||
@@ -4,16 +4,18 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/shlex"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type viperSettingType struct {
|
||||
BirdSocket string `mapstructure:"bird_socket"`
|
||||
Listen string `mapstructure:"listen"`
|
||||
AllowedIPs string `mapstructure:"allowed_ips"`
|
||||
TracerouteBin string `mapstructure:"traceroute_bin"`
|
||||
TracerouteRaw bool `mapstructure:"traceroute_raw"`
|
||||
BirdSocket string `mapstructure:"bird_socket"`
|
||||
Listen string `mapstructure:"listen"`
|
||||
AllowedIPs string `mapstructure:"allowed_ips"`
|
||||
TracerouteBin string `mapstructure:"traceroute_bin"`
|
||||
TracerouteFlags string `mapstructure:"traceroute_flags"`
|
||||
TracerouteRaw bool `mapstructure:"traceroute_raw"`
|
||||
}
|
||||
|
||||
// Parse settings with viper, and convert to legacy setting format
|
||||
@@ -39,9 +41,12 @@ func parseSettings() {
|
||||
pflag.String("allowed", "", "IPs allowed to access this proxy, separated by commas. Don't set to allow all IPs.")
|
||||
viper.BindPFlag("allowed_ips", pflag.Lookup("allowed"))
|
||||
|
||||
pflag.String("traceroute_bin", "traceroute", "traceroute binary file, set either in parameter or environment variable BIRDLG_TRACEROUTE_BIN")
|
||||
pflag.String("traceroute_bin", "", "traceroute binary file, set either in parameter or environment variable BIRDLG_TRACEROUTE_BIN")
|
||||
viper.BindPFlag("traceroute_bin", pflag.Lookup("traceroute_bin"))
|
||||
|
||||
pflag.String("traceroute_flags", "", "traceroute flags, repeat for multiple flags.")
|
||||
viper.BindPFlag("traceroute_flags", pflag.Lookup("traceroute_flags"))
|
||||
|
||||
pflag.Bool("traceroute_raw", false, "whether to display traceroute outputs raw; set via parameter or environment variable BIRDLG_TRACEROUTE_RAW")
|
||||
viper.BindPFlag("traceroute_raw", pflag.Lookup("traceroute_raw"))
|
||||
|
||||
@@ -65,7 +70,13 @@ func parseSettings() {
|
||||
setting.allowedIPs = []string{""}
|
||||
}
|
||||
|
||||
var err error
|
||||
setting.tr_bin = viperSettings.TracerouteBin
|
||||
setting.tr_flags, err = shlex.Split(viperSettings.TracerouteFlags)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
setting.tr_raw = viperSettings.TracerouteRaw
|
||||
|
||||
fmt.Printf("%#v\n", setting)
|
||||
|
||||
@@ -5,28 +5,81 @@ import (
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/shlex"
|
||||
)
|
||||
|
||||
func tracerouteTryExecute(cmd []string, args [][]string) ([]byte, string) {
|
||||
var output []byte
|
||||
var errString = ""
|
||||
for i := range cmd {
|
||||
var err error
|
||||
var cmdCombined = cmd[i] + " " + strings.Join(args[i], " ")
|
||||
func tracerouteArgsToString(cmd string, args []string, target []string) string {
|
||||
var cmdCombined = append([]string{cmd}, args...)
|
||||
cmdCombined = append(cmdCombined, target...)
|
||||
return strings.Join(cmdCombined, " ")
|
||||
}
|
||||
|
||||
instance := exec.Command(cmd[i], args[i]...)
|
||||
output, err = instance.CombinedOutput()
|
||||
if err == nil {
|
||||
return output, ""
|
||||
}
|
||||
errString += fmt.Sprintf("+ (Try %d) %s\n%s\n\n", (i + 1), cmdCombined, output)
|
||||
func tracerouteTryExecute(cmd string, args []string, target []string) ([]byte, error) {
|
||||
instance := exec.Command(cmd, append(args, target...)...)
|
||||
output, err := instance.CombinedOutput()
|
||||
if err == nil {
|
||||
return output, nil
|
||||
}
|
||||
return nil, errString
|
||||
|
||||
return output, err
|
||||
}
|
||||
|
||||
func tracerouteDetect(cmd string, args []string) bool {
|
||||
target := []string{"127.0.0.1"}
|
||||
success := false
|
||||
if result, err := tracerouteTryExecute(cmd, args, target); err == nil {
|
||||
setting.tr_bin = cmd
|
||||
setting.tr_flags = args
|
||||
success = true
|
||||
fmt.Printf("Traceroute autodetect success: %s\n", tracerouteArgsToString(cmd, args, target))
|
||||
} else {
|
||||
fmt.Printf("Traceroute autodetect fail, continuing: %s (%s)\n%s", tracerouteArgsToString(cmd, args, target), err.Error(), result)
|
||||
}
|
||||
|
||||
return success
|
||||
}
|
||||
|
||||
func tracerouteAutodetect() {
|
||||
if setting.tr_bin != "" && setting.tr_flags != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Traceroute (custom binary)
|
||||
if setting.tr_bin != "" {
|
||||
if tracerouteDetect(setting.tr_bin, []string{"-q1", "-N32", "-w1"}) {
|
||||
return
|
||||
}
|
||||
if tracerouteDetect(setting.tr_bin, []string{"-q1", "-w1"}) {
|
||||
return
|
||||
}
|
||||
if tracerouteDetect(setting.tr_bin, []string{}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// MTR
|
||||
if tracerouteDetect("mtr", []string{"-w", "-c1", "-Z1", "-G1", "-b"}) {
|
||||
return
|
||||
}
|
||||
|
||||
// Traceroute
|
||||
if tracerouteDetect("traceroute", []string{"-q1", "-N32", "-w1"}) {
|
||||
return
|
||||
}
|
||||
if tracerouteDetect("traceroute", []string{"-q1", "-w1"}) {
|
||||
return
|
||||
}
|
||||
if tracerouteDetect("traceroute", []string{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// Unsupported
|
||||
setting.tr_bin = ""
|
||||
setting.tr_flags = nil
|
||||
println("Traceroute autodetect failed! Traceroute will be disabled")
|
||||
}
|
||||
|
||||
func tracerouteHandler(httpW http.ResponseWriter, httpR *http.Request) {
|
||||
@@ -44,52 +97,30 @@ func tracerouteHandler(httpW http.ResponseWriter, httpR *http.Request) {
|
||||
}
|
||||
|
||||
var result []byte
|
||||
var errString string
|
||||
skippedCounter := 0
|
||||
|
||||
if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" || runtime.GOOS == "openbsd" {
|
||||
result, errString = tracerouteTryExecute(
|
||||
[]string{
|
||||
setting.tr_bin,
|
||||
setting.tr_bin,
|
||||
},
|
||||
[][]string{
|
||||
append([]string{"-q1", "-w1"}, args...),
|
||||
args,
|
||||
},
|
||||
)
|
||||
} else if runtime.GOOS == "linux" {
|
||||
result, errString = tracerouteTryExecute(
|
||||
[]string{
|
||||
setting.tr_bin,
|
||||
setting.tr_bin,
|
||||
setting.tr_bin,
|
||||
},
|
||||
[][]string{
|
||||
append([]string{"-q1", "-N32", "-w1"}, args...),
|
||||
append([]string{"-q1", "-w1"}, args...),
|
||||
args,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
if setting.tr_bin == "" {
|
||||
httpW.WriteHeader(http.StatusInternalServerError)
|
||||
httpW.Write([]byte("traceroute not supported on this node.\n"))
|
||||
return
|
||||
}
|
||||
if errString != "" {
|
||||
|
||||
result, err = tracerouteTryExecute(setting.tr_bin, setting.tr_flags, args)
|
||||
if err != nil {
|
||||
httpW.WriteHeader(http.StatusInternalServerError)
|
||||
httpW.Write([]byte(errString))
|
||||
httpW.Write([]byte(fmt.Sprintf("Error executing traceroute: %s\n\n", err.Error())))
|
||||
}
|
||||
|
||||
if result != nil {
|
||||
if setting.tr_raw {
|
||||
httpW.Write(result)
|
||||
} else {
|
||||
errString = string(result)
|
||||
errString = regexp.MustCompile(`(?m)^\s*(\d*)\s*\*\n`).ReplaceAllStringFunc(errString, func(w string) string {
|
||||
resultString := string(result)
|
||||
resultString = regexp.MustCompile(`(?m)^\s*(\d*)\s*\*\n`).ReplaceAllStringFunc(resultString, func(w string) string {
|
||||
skippedCounter++
|
||||
return ""
|
||||
})
|
||||
httpW.Write([]byte(strings.TrimSpace(errString)))
|
||||
httpW.Write([]byte(strings.TrimSpace(resultString)))
|
||||
if skippedCounter > 0 {
|
||||
httpW.Write([]byte("\n\n" + strconv.Itoa(skippedCounter) + " hops not responding."))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user