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 |
|
| 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 |
|
| 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.
|
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
|
```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.
|
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 |
|
| 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 "") |
|
| 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") |
|
| 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") |
|
| 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_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:
|
Example: start proxy with default configuration, should work "out of the box" on Debian 9 with BIRDv1:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -54,6 +55,7 @@ type settingType struct {
|
|||||||
listen string
|
listen string
|
||||||
allowedIPs []string
|
allowedIPs []string
|
||||||
tr_bin string
|
tr_bin string
|
||||||
|
tr_flags []string
|
||||||
tr_raw bool
|
tr_raw bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +64,9 @@ var setting settingType
|
|||||||
// Wrapper of tracer
|
// Wrapper of tracer
|
||||||
func main() {
|
func main() {
|
||||||
parseSettings()
|
parseSettings()
|
||||||
|
tracerouteAutodetect()
|
||||||
|
|
||||||
|
fmt.Printf("Listening on %s...\n", setting.listen)
|
||||||
|
|
||||||
var l net.Listener
|
var l net.Listener
|
||||||
var err error
|
var err error
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/shlex"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
@@ -13,6 +14,7 @@ type viperSettingType struct {
|
|||||||
Listen string `mapstructure:"listen"`
|
Listen string `mapstructure:"listen"`
|
||||||
AllowedIPs string `mapstructure:"allowed_ips"`
|
AllowedIPs string `mapstructure:"allowed_ips"`
|
||||||
TracerouteBin string `mapstructure:"traceroute_bin"`
|
TracerouteBin string `mapstructure:"traceroute_bin"`
|
||||||
|
TracerouteFlags string `mapstructure:"traceroute_flags"`
|
||||||
TracerouteRaw bool `mapstructure:"traceroute_raw"`
|
TracerouteRaw bool `mapstructure:"traceroute_raw"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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.")
|
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"))
|
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"))
|
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")
|
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"))
|
viper.BindPFlag("traceroute_raw", pflag.Lookup("traceroute_raw"))
|
||||||
|
|
||||||
@@ -65,7 +70,13 @@ func parseSettings() {
|
|||||||
setting.allowedIPs = []string{""}
|
setting.allowedIPs = []string{""}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
setting.tr_bin = viperSettings.TracerouteBin
|
setting.tr_bin = viperSettings.TracerouteBin
|
||||||
|
setting.tr_flags, err = shlex.Split(viperSettings.TracerouteFlags)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
setting.tr_raw = viperSettings.TracerouteRaw
|
setting.tr_raw = viperSettings.TracerouteRaw
|
||||||
|
|
||||||
fmt.Printf("%#v\n", setting)
|
fmt.Printf("%#v\n", setting)
|
||||||
|
|||||||
@@ -5,28 +5,81 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/shlex"
|
"github.com/google/shlex"
|
||||||
)
|
)
|
||||||
|
|
||||||
func tracerouteTryExecute(cmd []string, args [][]string) ([]byte, string) {
|
func tracerouteArgsToString(cmd string, args []string, target []string) string {
|
||||||
var output []byte
|
var cmdCombined = append([]string{cmd}, args...)
|
||||||
var errString = ""
|
cmdCombined = append(cmdCombined, target...)
|
||||||
for i := range cmd {
|
return strings.Join(cmdCombined, " ")
|
||||||
var err error
|
}
|
||||||
var cmdCombined = cmd[i] + " " + strings.Join(args[i], " ")
|
|
||||||
|
|
||||||
instance := exec.Command(cmd[i], args[i]...)
|
func tracerouteTryExecute(cmd string, args []string, target []string) ([]byte, error) {
|
||||||
output, err = instance.CombinedOutput()
|
instance := exec.Command(cmd, append(args, target...)...)
|
||||||
|
output, err := instance.CombinedOutput()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return output, ""
|
return output, nil
|
||||||
}
|
}
|
||||||
errString += fmt.Sprintf("+ (Try %d) %s\n%s\n\n", (i + 1), cmdCombined, output)
|
|
||||||
|
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 nil, errString
|
|
||||||
|
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) {
|
func tracerouteHandler(httpW http.ResponseWriter, httpR *http.Request) {
|
||||||
@@ -44,52 +97,30 @@ func tracerouteHandler(httpW http.ResponseWriter, httpR *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var result []byte
|
var result []byte
|
||||||
var errString string
|
|
||||||
skippedCounter := 0
|
skippedCounter := 0
|
||||||
|
|
||||||
if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" || runtime.GOOS == "openbsd" {
|
if setting.tr_bin == "" {
|
||||||
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 {
|
|
||||||
httpW.WriteHeader(http.StatusInternalServerError)
|
httpW.WriteHeader(http.StatusInternalServerError)
|
||||||
httpW.Write([]byte("traceroute not supported on this node.\n"))
|
httpW.Write([]byte("traceroute not supported on this node.\n"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if errString != "" {
|
|
||||||
|
result, err = tracerouteTryExecute(setting.tr_bin, setting.tr_flags, args)
|
||||||
|
if err != nil {
|
||||||
httpW.WriteHeader(http.StatusInternalServerError)
|
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 result != nil {
|
||||||
if setting.tr_raw {
|
if setting.tr_raw {
|
||||||
httpW.Write(result)
|
httpW.Write(result)
|
||||||
} else {
|
} else {
|
||||||
errString = string(result)
|
resultString := string(result)
|
||||||
errString = regexp.MustCompile(`(?m)^\s*(\d*)\s*\*\n`).ReplaceAllStringFunc(errString, func(w string) string {
|
resultString = regexp.MustCompile(`(?m)^\s*(\d*)\s*\*\n`).ReplaceAllStringFunc(resultString, func(w string) string {
|
||||||
skippedCounter++
|
skippedCounter++
|
||||||
return ""
|
return ""
|
||||||
})
|
})
|
||||||
httpW.Write([]byte(strings.TrimSpace(errString)))
|
httpW.Write([]byte(strings.TrimSpace(resultString)))
|
||||||
if skippedCounter > 0 {
|
if skippedCounter > 0 {
|
||||||
httpW.Write([]byte("\n\n" + strconv.Itoa(skippedCounter) + " hops not responding."))
|
httpW.Write([]byte("\n\n" + strconv.Itoa(skippedCounter) + " hops not responding."))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user