You've already forked bird-lg-go
mirror of
https://github.com/xddxdd/bird-lg-go
synced 2025-10-27 06:42:13 +01:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb933954a9 | ||
|
|
4898a095b1 | ||
|
|
76fa6c6d06 | ||
|
|
ae6460d2d3 | ||
|
|
d312f7de1b | ||
|
|
1097e69070 | ||
|
|
4a8c752157 | ||
|
|
5ad6a4d35c | ||
|
|
5422c8fd8c | ||
|
|
5042980d79 | ||
|
|
7884531a24 | ||
|
|
cc804e81b6 | ||
|
|
c9bab2ae2b | ||
|
|
b9a4f95978 | ||
|
|
7cd69746df | ||
|
|
e719859d68 | ||
|
|
5d06affefc | ||
|
|
060fe9bf8e | ||
|
|
f23d36f357 | ||
|
|
1dbc0fccd2 | ||
|
|
b2d64d19e3 | ||
|
|
4dee4b0806 | ||
|
|
cb279e0459 | ||
|
|
31ba36beaf | ||
|
|
5ab3b95d64 | ||
|
|
7bf654f35f | ||
|
|
5b33629a9d | ||
|
|
0868c5d42c | ||
|
|
bc61579e6a | ||
|
|
e9750a8278 | ||
|
|
d40dd3a4d3 | ||
|
|
ffdeeac06e | ||
|
|
7eb4d75bbf | ||
|
|
6e5e190d32 |
8
.github/workflows/release.yaml
vendored
8
.github/workflows/release.yaml
vendored
@@ -23,22 +23,26 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Release frontend
|
- name: Release frontend
|
||||||
uses: wangyoucao577/go-release-action@v1.40
|
uses: wangyoucao577/go-release-action@v1.53
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
goos: ${{ matrix.goos }}
|
goos: ${{ matrix.goos }}
|
||||||
goarch: ${{ matrix.goarch }}
|
goarch: ${{ matrix.goarch }}
|
||||||
project_path: "./frontend"
|
project_path: "./frontend"
|
||||||
binary_name: "bird-lg-go"
|
binary_name: "bird-lg-go"
|
||||||
|
pre_command: |
|
||||||
|
export CGO_ENABLED=0
|
||||||
|
|
||||||
- name: Release proxy
|
- name: Release proxy
|
||||||
uses: wangyoucao577/go-release-action@v1.40
|
uses: wangyoucao577/go-release-action@v1.53
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
goos: ${{ matrix.goos }}
|
goos: ${{ matrix.goos }}
|
||||||
goarch: ${{ matrix.goarch }}
|
goarch: ${{ matrix.goarch }}
|
||||||
project_path: "./proxy"
|
project_path: "./proxy"
|
||||||
binary_name: "bird-lgproxy-go"
|
binary_name: "bird-lgproxy-go"
|
||||||
|
pre_command: |
|
||||||
|
export CGO_ENABLED=0
|
||||||
|
|
||||||
docker-release:
|
docker-release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -73,13 +73,14 @@ func TestApiSummaryHandler(t *testing.T) {
|
|||||||
|
|
||||||
summary := response.Result[0].(*apiSummaryResultPair)
|
summary := response.Result[0].(*apiSummaryResultPair)
|
||||||
assert.Equal(t, summary.Server, "alpha")
|
assert.Equal(t, summary.Server, "alpha")
|
||||||
|
assert.Equal(t, len(summary.Data), 7)
|
||||||
// Protocol list will be sorted
|
// Protocol list will be sorted
|
||||||
assert.Equal(t, summary.Data[1].Name, "device1")
|
assert.Equal(t, summary.Data[0].Name, "device1")
|
||||||
assert.Equal(t, summary.Data[1].Proto, "Device")
|
assert.Equal(t, summary.Data[0].Proto, "Device")
|
||||||
assert.Equal(t, summary.Data[1].Table, "---")
|
assert.Equal(t, summary.Data[0].Table, "---")
|
||||||
assert.Equal(t, summary.Data[1].State, "up")
|
assert.Equal(t, summary.Data[0].State, "up")
|
||||||
assert.Equal(t, summary.Data[1].Since, "2021-08-27")
|
assert.Equal(t, summary.Data[0].Since, "2021-08-27")
|
||||||
assert.Equal(t, summary.Data[1].Info, "")
|
assert.Equal(t, summary.Data[0].Info, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApiSummaryHandlerError(t *testing.T) {
|
func TestApiSummaryHandlerError(t *testing.T) {
|
||||||
|
|||||||
@@ -1,33 +1,27 @@
|
|||||||
module github.com/xddxdd/bird-lg-go/frontend
|
module github.com/xddxdd/bird-lg-go/frontend
|
||||||
|
|
||||||
go 1.17
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||||
github.com/gorilla/handlers v1.5.2
|
github.com/gorilla/handlers v1.5.2
|
||||||
github.com/jarcoal/httpmock v1.4.0
|
github.com/jarcoal/httpmock v1.4.1
|
||||||
github.com/magiconair/properties v1.8.10
|
github.com/magiconair/properties v1.8.10
|
||||||
github.com/spf13/pflag v1.0.6
|
github.com/spf13/pflag v1.0.10
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.21.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
golang.org/x/text v0.28.0 // indirect
|
||||||
golang.org/x/sys v0.18.0 // indirect
|
|
||||||
golang.org/x/text v0.14.0 // indirect
|
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
2867
frontend/go.sum
2867
frontend/go.sum
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@ type settingType struct {
|
|||||||
domain string
|
domain string
|
||||||
proxyPort int
|
proxyPort int
|
||||||
whoisServer string
|
whoisServer string
|
||||||
listen string
|
listen []string
|
||||||
dnsInterface string
|
dnsInterface string
|
||||||
netSpecificMode string
|
netSpecificMode string
|
||||||
titleBrand string
|
titleBrand string
|
||||||
@@ -35,24 +35,29 @@ func main() {
|
|||||||
parseSettings()
|
parseSettings()
|
||||||
ImportTemplates()
|
ImportTemplates()
|
||||||
|
|
||||||
var l net.Listener
|
for _, listenAddr := range setting.listen {
|
||||||
var err error
|
go func(listenAddr string) {
|
||||||
|
var l net.Listener
|
||||||
|
var err error
|
||||||
|
|
||||||
if strings.HasPrefix(setting.listen, "/") {
|
if strings.HasPrefix(listenAddr, "/") {
|
||||||
// Delete existing socket file, ignore errors (will fail later anyway)
|
// Delete existing socket file, ignore errors (will fail later anyway)
|
||||||
os.Remove(setting.listen)
|
os.Remove(listenAddr)
|
||||||
l, err = net.Listen("unix", setting.listen)
|
l, err = net.Listen("unix", listenAddr)
|
||||||
} else {
|
} else {
|
||||||
listenAddr := setting.listen
|
if !strings.Contains(listenAddr, ":") {
|
||||||
if !strings.Contains(listenAddr, ":") {
|
listenAddr = ":" + listenAddr
|
||||||
listenAddr = ":" + listenAddr
|
}
|
||||||
}
|
l, err = net.Listen("tcp", listenAddr)
|
||||||
l, err = net.Listen("tcp", listenAddr)
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
webServerStart(l)
|
||||||
|
}(listenAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
select {}
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
webServerStart(l)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,15 +35,6 @@ var optionsMap = map[string]string{
|
|||||||
"traceroute": "traceroute ...",
|
"traceroute": "traceroute ...",
|
||||||
}
|
}
|
||||||
|
|
||||||
// pre-compiled regexp and constant statemap for summary rendering
|
|
||||||
var splitSummaryLine = regexp.MustCompile(`(\w+)(\s+)(\w+)(\s+)([\w-]+)(\s+)(\w+)(\s+)([0-9\-\. :]+)(.*)`)
|
|
||||||
var summaryStateMap = map[string]string{
|
|
||||||
"up": "success",
|
|
||||||
"down": "secondary",
|
|
||||||
"start": "danger",
|
|
||||||
"passive": "info",
|
|
||||||
}
|
|
||||||
|
|
||||||
// render the page template
|
// render the page template
|
||||||
func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, content template.HTML) {
|
func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, content template.HTML) {
|
||||||
path := r.URL.Path[1:]
|
path := r.URL.Path[1:]
|
||||||
@@ -143,63 +134,23 @@ func summaryParse(data string, serverName string) (TemplateSummary, error) {
|
|||||||
|
|
||||||
// parse each line
|
// parse each line
|
||||||
for _, line := range rows {
|
for _, line := range rows {
|
||||||
|
row := SummaryRowDataFromLine(line)
|
||||||
// Ignore empty lines
|
if row == nil {
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
if len(line) == 0 {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a total of 6 columns from bird summary
|
// Filter row name
|
||||||
lineSplitted := splitSummaryLine.FindStringSubmatch(line)
|
if setting.nameFilter != "" && nameFilterRegexp.MatchString(row.Name) {
|
||||||
if lineSplitted == nil {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var row SummaryRowData
|
// Filter away unwanted protocol types, if setting.protocolFilter is non-empty
|
||||||
|
if len(setting.protocolFilter) > 0 && !row.ProtocolMatches(setting.protocolFilter) {
|
||||||
if len(lineSplitted) >= 2 {
|
continue
|
||||||
row.Name = strings.TrimSpace(lineSplitted[1])
|
|
||||||
if setting.nameFilter != "" && nameFilterRegexp.MatchString(row.Name) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(lineSplitted) >= 4 {
|
|
||||||
row.Proto = strings.TrimSpace(lineSplitted[3])
|
|
||||||
// Filter away unwanted protocol types, if setting.protocolFilter is non-empty
|
|
||||||
found := false
|
|
||||||
for _, protocol := range setting.protocolFilter {
|
|
||||||
if strings.EqualFold(row.Proto, protocol) {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(setting.protocolFilter) > 0 && !found {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(lineSplitted) >= 6 {
|
|
||||||
row.Table = strings.TrimSpace(lineSplitted[5])
|
|
||||||
}
|
|
||||||
if len(lineSplitted) >= 8 {
|
|
||||||
row.State = strings.TrimSpace(lineSplitted[7])
|
|
||||||
row.MappedState = summaryStateMap[row.State]
|
|
||||||
}
|
|
||||||
if len(lineSplitted) >= 10 {
|
|
||||||
row.Since = strings.TrimSpace(lineSplitted[9])
|
|
||||||
}
|
|
||||||
if len(lineSplitted) >= 11 {
|
|
||||||
row.Info = strings.TrimSpace(lineSplitted[10])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dynamic BGP session, show without any color
|
|
||||||
if strings.Contains(row.Info, "Passive") {
|
|
||||||
row.MappedState = summaryStateMap["passive"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add to the result
|
// add to the result
|
||||||
args.Rows = append(args.Rows, row)
|
args.Rows = append(args.Rows, *row)
|
||||||
}
|
}
|
||||||
|
|
||||||
return args, nil
|
return args, nil
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
const BirdSummaryData = `BIRD 2.0.8 ready.
|
const BirdSummaryData = `Name Proto Table State Since Info
|
||||||
Name Proto Table State Since Info
|
|
||||||
static1 Static master4 up 2021-08-27
|
static1 Static master4 up 2021-08-27
|
||||||
static2 Static master6 up 2021-08-27
|
static2 Static master6 up 2021-08-27
|
||||||
device1 Device --- up 2021-08-27
|
device1 Device --- up 2021-08-27
|
||||||
|
|||||||
@@ -9,25 +9,25 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type viperSettingType struct {
|
type viperSettingType struct {
|
||||||
Servers string `mapstructure:"servers"`
|
Servers string `mapstructure:"servers"`
|
||||||
Domain string `mapstructure:"domain"`
|
Domain string `mapstructure:"domain"`
|
||||||
ProxyPort int `mapstructure:"proxy_port"`
|
ProxyPort int `mapstructure:"proxy_port"`
|
||||||
WhoisServer string `mapstructure:"whois"`
|
WhoisServer string `mapstructure:"whois"`
|
||||||
Listen string `mapstructure:"listen"`
|
Listen []string `mapstructure:"listen"`
|
||||||
DNSInterface string `mapstructure:"dns_interface"`
|
DNSInterface string `mapstructure:"dns_interface"`
|
||||||
NetSpecificMode string `mapstructure:"net_specific_mode"`
|
NetSpecificMode string `mapstructure:"net_specific_mode"`
|
||||||
TitleBrand string `mapstructure:"title_brand"`
|
TitleBrand string `mapstructure:"title_brand"`
|
||||||
NavBarBrand string `mapstructure:"navbar_brand"`
|
NavBarBrand string `mapstructure:"navbar_brand"`
|
||||||
NavBarBrandURL string `mapstructure:"navbar_brand_url"`
|
NavBarBrandURL string `mapstructure:"navbar_brand_url"`
|
||||||
NavBarAllServer string `mapstructure:"navbar_all_servers"`
|
NavBarAllServer string `mapstructure:"navbar_all_servers"`
|
||||||
NavBarAllURL string `mapstructure:"navbar_all_url"`
|
NavBarAllURL string `mapstructure:"navbar_all_url"`
|
||||||
BgpmapInfo string `mapstructure:"bgpmap_info"`
|
BgpmapInfo string `mapstructure:"bgpmap_info"`
|
||||||
TelegramBotName string `mapstructure:"telegram_bot_name"`
|
TelegramBotName string `mapstructure:"telegram_bot_name"`
|
||||||
ProtocolFilter string `mapstructure:"protocol_filter"`
|
ProtocolFilter string `mapstructure:"protocol_filter"`
|
||||||
NameFilter string `mapstructure:"name_filter"`
|
NameFilter string `mapstructure:"name_filter"`
|
||||||
TimeOut int `mapstructure:"timeout"`
|
TimeOut int `mapstructure:"timeout"`
|
||||||
ConnectionTimeOut int `mapstructure:"connection_timeout"`
|
ConnectionTimeOut int `mapstructure:"connection_timeout"`
|
||||||
TrustProxyHeaders bool `mapstructure:"trust_proxy_headers"`
|
TrustProxyHeaders bool `mapstructure:"trust_proxy_headers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse settings with viper, and convert to legacy setting format
|
// Parse settings with viper, and convert to legacy setting format
|
||||||
@@ -52,7 +52,7 @@ func parseSettings() {
|
|||||||
pflag.String("whois", "whois.verisign-grs.com", "whois server for queries")
|
pflag.String("whois", "whois.verisign-grs.com", "whois server for queries")
|
||||||
viper.BindPFlag("whois", pflag.Lookup("whois"))
|
viper.BindPFlag("whois", pflag.Lookup("whois"))
|
||||||
|
|
||||||
pflag.String("listen", "5000", "address or unix socket bird-lg is listening on")
|
pflag.StringSlice("listen", []string{"5000"}, "address or unix socket bird-lg is listening on")
|
||||||
viper.BindPFlag("listen", pflag.Lookup("listen"))
|
viper.BindPFlag("listen", pflag.Lookup("listen"))
|
||||||
|
|
||||||
pflag.String("dns-interface", "asn.cymru.com", "dns zone to query ASN information")
|
pflag.String("dns-interface", "asn.cymru.com", "dns zone to query ASN information")
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ package main
|
|||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// import templates and other assets
|
// import templates and other assets
|
||||||
|
//
|
||||||
//go:embed assets
|
//go:embed assets
|
||||||
var assets embed.FS
|
var assets embed.FS
|
||||||
|
|
||||||
@@ -64,6 +66,47 @@ func (r SummaryRowData) NameContains(prefix string) bool {
|
|||||||
return strings.Contains(r.Name, prefix)
|
return strings.Contains(r.Name, prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r SummaryRowData) ProtocolMatches(protocols []string) bool {
|
||||||
|
for _, protocol := range protocols {
|
||||||
|
if strings.EqualFold(r.Proto, protocol) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// pre-compiled regexp and constant statemap for summary rendering
|
||||||
|
var splitSummaryLine = regexp.MustCompile(`^([\w-]+)\s+(\w+)\s+([\w-]+)\s+(\w+)\s+([0-9\-\. :]+)(.*)$`)
|
||||||
|
var summaryStateMap = map[string]string{
|
||||||
|
"up": "success",
|
||||||
|
"down": "secondary",
|
||||||
|
"start": "danger",
|
||||||
|
"passive": "info",
|
||||||
|
}
|
||||||
|
|
||||||
|
func SummaryRowDataFromLine(line string) *SummaryRowData {
|
||||||
|
lineSplitted := splitSummaryLine.FindStringSubmatch(line)
|
||||||
|
if lineSplitted == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var row SummaryRowData
|
||||||
|
row.Name = strings.TrimSpace(lineSplitted[1])
|
||||||
|
row.Proto = strings.TrimSpace(lineSplitted[2])
|
||||||
|
row.Table = strings.TrimSpace(lineSplitted[3])
|
||||||
|
row.State = strings.TrimSpace(lineSplitted[4])
|
||||||
|
row.Since = strings.TrimSpace(lineSplitted[5])
|
||||||
|
row.Info = strings.TrimSpace(lineSplitted[6])
|
||||||
|
|
||||||
|
if strings.Contains(row.Info, "Passive") {
|
||||||
|
row.MappedState = summaryStateMap["passive"]
|
||||||
|
} else {
|
||||||
|
row.MappedState = summaryStateMap[row.State]
|
||||||
|
}
|
||||||
|
|
||||||
|
return &row
|
||||||
|
}
|
||||||
|
|
||||||
type TemplateSummary struct {
|
type TemplateSummary struct {
|
||||||
ServerName string
|
ServerName string
|
||||||
Raw string
|
Raw string
|
||||||
@@ -108,7 +151,7 @@ var requiredTemplates = [...]string{
|
|||||||
// define functions to be made available in templates
|
// define functions to be made available in templates
|
||||||
|
|
||||||
var funcMap = template.FuncMap{
|
var funcMap = template.FuncMap{
|
||||||
"pathescape": url.PathEscape,
|
"pathescape": url.PathEscape,
|
||||||
}
|
}
|
||||||
|
|
||||||
// import templates from embedded assets
|
// import templates from embedded assets
|
||||||
|
|||||||
@@ -23,3 +23,67 @@ func TestSummaryRowDataNameContains(t *testing.T) {
|
|||||||
assert.Equal(t, data.NameContains("oc"), true)
|
assert.Equal(t, data.NameContains("oc"), true)
|
||||||
assert.Equal(t, data.NameContains("no"), false)
|
assert.Equal(t, data.NameContains("no"), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSummaryRowDataFromLine(t *testing.T) {
|
||||||
|
data := SummaryRowDataFromLine("sys_device Device --- up 2025-06-27 21:23:08")
|
||||||
|
|
||||||
|
assert.Equal(t, data.Name, "sys_device")
|
||||||
|
assert.Equal(t, data.Proto, "Device")
|
||||||
|
assert.Equal(t, data.Table, "---")
|
||||||
|
assert.Equal(t, data.State, "up")
|
||||||
|
assert.Equal(t, data.Since, "2025-06-27 21:23:08")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummaryRowDataFromLineNumeric(t *testing.T) {
|
||||||
|
data := SummaryRowDataFromLine("12345 Device --- up 2025-06-27 21:23:08")
|
||||||
|
|
||||||
|
assert.Equal(t, data.Name, "12345")
|
||||||
|
assert.Equal(t, data.Proto, "Device")
|
||||||
|
assert.Equal(t, data.Table, "---")
|
||||||
|
assert.Equal(t, data.State, "up")
|
||||||
|
assert.Equal(t, data.Since, "2025-06-27 21:23:08")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummaryRowDataFromLinePipe(t *testing.T) {
|
||||||
|
data := SummaryRowDataFromLine("pipe Pipe --- up 2025-06-27 21:23:08 master4 <=> pipe_v4")
|
||||||
|
|
||||||
|
assert.Equal(t, data.Name, "pipe")
|
||||||
|
assert.Equal(t, data.Proto, "Pipe")
|
||||||
|
assert.Equal(t, data.Table, "---")
|
||||||
|
assert.Equal(t, data.State, "up")
|
||||||
|
assert.Equal(t, data.Since, "2025-06-27 21:23:08")
|
||||||
|
assert.Equal(t, data.Info, "master4 <=> pipe_v4")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummaryRowDataFromLineBGP(t *testing.T) {
|
||||||
|
data := SummaryRowDataFromLine("bgp BGP --- up 2025-06-30 20:45:33 Established")
|
||||||
|
|
||||||
|
assert.Equal(t, data.Name, "bgp")
|
||||||
|
assert.Equal(t, data.Proto, "BGP")
|
||||||
|
assert.Equal(t, data.Table, "---")
|
||||||
|
assert.Equal(t, data.State, "up")
|
||||||
|
assert.Equal(t, data.Since, "2025-06-30 20:45:33")
|
||||||
|
assert.Equal(t, data.Info, "Established")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummaryRowDataFromLineBGPPassive(t *testing.T) {
|
||||||
|
data := SummaryRowDataFromLine("passive BGP --- start 2025-06-27 21:23:08 Passive")
|
||||||
|
|
||||||
|
assert.Equal(t, data.Name, "passive")
|
||||||
|
assert.Equal(t, data.Proto, "BGP")
|
||||||
|
assert.Equal(t, data.Table, "---")
|
||||||
|
assert.Equal(t, data.State, "start")
|
||||||
|
assert.Equal(t, data.Since, "2025-06-27 21:23:08")
|
||||||
|
assert.Equal(t, data.Info, "Passive")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSummaryRowDataFromLineWithDash(t *testing.T) {
|
||||||
|
data := SummaryRowDataFromLine("ibgp_test-01 BGP --- up 07:16:51.656 Established")
|
||||||
|
|
||||||
|
assert.Equal(t, data.Name, "ibgp_test-01")
|
||||||
|
assert.Equal(t, data.Proto, "BGP")
|
||||||
|
assert.Equal(t, data.Table, "---")
|
||||||
|
assert.Equal(t, data.State, "up")
|
||||||
|
assert.Equal(t, data.Since, "07:16:51.656")
|
||||||
|
assert.Equal(t, data.Info, "Established")
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,19 +12,20 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
)
|
)
|
||||||
|
|
||||||
var primitiveMap = map[string]string{
|
var primitiveMap = map[string]string{
|
||||||
"summary": "show protocols",
|
"summary": "show protocols",
|
||||||
"detail": "show protocols all %s",
|
"detail": "show protocols all '%s'",
|
||||||
"route_from_protocol": "show route protocol %s",
|
"route_from_protocol": "show route protocol '%s'",
|
||||||
"route_from_protocol_all": "show route protocol %s all",
|
"route_from_protocol_all": "show route protocol '%s' all",
|
||||||
"route_from_protocol_primary": "show route protocol %s primary",
|
"route_from_protocol_primary": "show route protocol '%s' primary",
|
||||||
"route_from_protocol_all_primary": "show route protocol %s all primary",
|
"route_from_protocol_all_primary": "show route protocol '%s' all primary",
|
||||||
"route_filtered_from_protocol": "show route filtered protocol %s",
|
"route_filtered_from_protocol": "show route filtered protocol '%s'",
|
||||||
"route_filtered_from_protocol_all": "show route filtered protocol %s all",
|
"route_filtered_from_protocol_all": "show route filtered protocol '%s' all",
|
||||||
"route_from_origin": "show route where bgp_path.last = %s",
|
"route_from_origin": "show route where bgp_path.last = %s",
|
||||||
"route_from_origin_all": "show route where bgp_path.last = %s all",
|
"route_from_origin_all": "show route where bgp_path.last = %s all",
|
||||||
"route_from_origin_primary": "show route where bgp_path.last = %s primary",
|
"route_from_origin_primary": "show route where bgp_path.last = %s primary",
|
||||||
@@ -39,8 +40,10 @@ var primitiveMap = map[string]string{
|
|||||||
"traceroute": "%s",
|
"traceroute": "%s",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var webServerPrepared uint32 = 0
|
||||||
|
|
||||||
// serve up a generic error
|
// serve up a generic error
|
||||||
func serverError(w http.ResponseWriter, r *http.Request) {
|
func serverError(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte("500 Internal Server Error"))
|
w.Write([]byte("500 Internal Server Error"))
|
||||||
}
|
}
|
||||||
@@ -192,11 +195,11 @@ func webHandlerBGPMap(endpoint string, command string) func(w http.ResponseWrite
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up routing paths and start webserver
|
// set up routing paths
|
||||||
func webServerStart(l net.Listener) {
|
func webServerPrepare() {
|
||||||
// redirect main page to all server summary
|
// redirect main page to all server summary
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, "/summary/"+url.PathEscape(strings.Join(setting.servers, "+")), 302)
|
http.Redirect(w, r, "/summary/"+url.PathEscape(strings.Join(setting.servers, "+")), http.StatusFound)
|
||||||
})
|
})
|
||||||
|
|
||||||
// serve static pages using embedded assets from template.go
|
// serve static pages using embedded assets from template.go
|
||||||
@@ -235,8 +238,14 @@ func webServerStart(l net.Listener) {
|
|||||||
http.HandleFunc("/whois/", webHandlerWhois)
|
http.HandleFunc("/whois/", webHandlerWhois)
|
||||||
http.HandleFunc("/api/", apiHandler)
|
http.HandleFunc("/api/", apiHandler)
|
||||||
http.HandleFunc("/telegram/", webHandlerTelegramBot)
|
http.HandleFunc("/telegram/", webHandlerTelegramBot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// start webserver
|
||||||
|
func webServerStart(l net.Listener) {
|
||||||
|
if atomic.SwapUint32(&webServerPrepared, 1) == 0 {
|
||||||
|
webServerPrepare()
|
||||||
|
}
|
||||||
|
|
||||||
// Start HTTP server
|
|
||||||
var handler http.Handler
|
var handler http.Handler
|
||||||
handler = http.DefaultServeMux
|
handler = http.DefaultServeMux
|
||||||
if setting.trustProxyHeaders {
|
if setting.trustProxyHeaders {
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func TestWhoisWithoutServer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestWhoisConnectionError(t *testing.T) {
|
func TestWhoisConnectionError(t *testing.T) {
|
||||||
setting.whoisServer = "127.0.0.1:0"
|
setting.whoisServer = "127.0.0.1:1"
|
||||||
result := whois("AS6939")
|
result := whois("AS6939")
|
||||||
if !strings.Contains(result, "connect: connection refused") {
|
if !strings.Contains(result, "connect: connection refused") {
|
||||||
t.Errorf("Whois AS6939 without server produced output, got %s", result)
|
t.Errorf("Whois AS6939 without server produced output, got %s", result)
|
||||||
|
|||||||
32
proxy/go.mod
32
proxy/go.mod
@@ -1,32 +1,26 @@
|
|||||||
module github.com/xddxdd/bird-lg-go/proxy
|
module github.com/xddxdd/bird-lg-go/proxy
|
||||||
|
|
||||||
go 1.17
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||||
github.com/gorilla/handlers v1.5.2
|
github.com/gorilla/handlers v1.5.2
|
||||||
github.com/magiconair/properties v1.8.10
|
github.com/magiconair/properties v1.8.10
|
||||||
github.com/spf13/pflag v1.0.6
|
github.com/spf13/pflag v1.0.10
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.21.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
golang.org/x/text v0.28.0 // indirect
|
||||||
golang.org/x/sys v0.18.0 // indirect
|
|
||||||
golang.org/x/text v0.14.0 // indirect
|
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
2863
proxy/go.sum
2863
proxy/go.sum
File diff suppressed because it is too large
Load Diff
@@ -62,7 +62,7 @@ func accessHandler(next http.Handler) http.Handler {
|
|||||||
|
|
||||||
type settingType struct {
|
type settingType struct {
|
||||||
birdSocket string
|
birdSocket string
|
||||||
listen string
|
listen []string
|
||||||
allowedNets []*net.IPNet
|
allowedNets []*net.IPNet
|
||||||
tr_bin string
|
tr_bin string
|
||||||
tr_flags []string
|
tr_flags []string
|
||||||
@@ -76,32 +76,40 @@ func main() {
|
|||||||
parseSettings()
|
parseSettings()
|
||||||
tracerouteAutodetect()
|
tracerouteAutodetect()
|
||||||
|
|
||||||
fmt.Printf("Listening on %s...\n", setting.listen)
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
var l net.Listener
|
// Prepare HTTP server
|
||||||
var err error
|
mux.HandleFunc("/", invalidHandler)
|
||||||
|
mux.HandleFunc("/bird", birdHandler)
|
||||||
|
mux.HandleFunc("/bird6", birdHandler)
|
||||||
|
mux.HandleFunc("/traceroute", tracerouteHandler)
|
||||||
|
mux.HandleFunc("/traceroute6", tracerouteHandler)
|
||||||
|
|
||||||
if strings.HasPrefix(setting.listen, "/") {
|
for _, listenAddr := range setting.listen {
|
||||||
// Delete existing socket file, ignore errors (will fail later anyway)
|
go func(addr string) {
|
||||||
os.Remove(setting.listen)
|
fmt.Printf("Listening on %s...\n", addr)
|
||||||
l, err = net.Listen("unix", setting.listen)
|
|
||||||
} else {
|
var l net.Listener
|
||||||
listenAddr := setting.listen
|
var err error
|
||||||
if !strings.Contains(listenAddr, ":") {
|
|
||||||
listenAddr = ":" + listenAddr
|
if strings.HasPrefix(addr, "/") {
|
||||||
}
|
// Delete existing socket file, ignore errors (will fail later anyway)
|
||||||
l, err = net.Listen("tcp", listenAddr)
|
os.Remove(addr)
|
||||||
|
l, err = net.Listen("unix", addr)
|
||||||
|
} else {
|
||||||
|
if !strings.Contains(addr, ":") {
|
||||||
|
addr = ":" + addr
|
||||||
|
}
|
||||||
|
l, err = net.Listen("tcp", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Serve(l, handlers.LoggingHandler(os.Stdout, accessHandler(mux)))
|
||||||
|
}(listenAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
select {}
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start HTTP server
|
|
||||||
http.HandleFunc("/", invalidHandler)
|
|
||||||
http.HandleFunc("/bird", birdHandler)
|
|
||||||
http.HandleFunc("/bird6", birdHandler)
|
|
||||||
http.HandleFunc("/traceroute", tracerouteHandler)
|
|
||||||
http.HandleFunc("/traceroute6", tracerouteHandler)
|
|
||||||
http.Serve(l, handlers.LoggingHandler(os.Stdout, accessHandler(http.DefaultServeMux)))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type viperSettingType struct {
|
type viperSettingType struct {
|
||||||
BirdSocket string `mapstructure:"bird_socket"`
|
BirdSocket string `mapstructure:"bird_socket"`
|
||||||
Listen string `mapstructure:"listen"`
|
Listen []string `mapstructure:"listen"`
|
||||||
AllowedNets string `mapstructure:"allowed_ips"`
|
AllowedNets string `mapstructure:"allowed_ips"`
|
||||||
TracerouteBin string `mapstructure:"traceroute_bin"`
|
TracerouteBin string `mapstructure:"traceroute_bin"`
|
||||||
TracerouteFlags string `mapstructure:"traceroute_flags"`
|
TracerouteFlags string `mapstructure:"traceroute_flags"`
|
||||||
TracerouteRaw bool `mapstructure:"traceroute_raw"`
|
TracerouteRaw bool `mapstructure:"traceroute_raw"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse settings with viper, and convert to legacy setting format
|
// Parse settings with viper, and convert to legacy setting format
|
||||||
@@ -37,7 +37,7 @@ func parseSettings() {
|
|||||||
pflag.String("bird", "/var/run/bird/bird.ctl", "socket file for bird, set either in parameter or environment variable BIRD_SOCKET")
|
pflag.String("bird", "/var/run/bird/bird.ctl", "socket file for bird, set either in parameter or environment variable BIRD_SOCKET")
|
||||||
viper.BindPFlag("bird_socket", pflag.Lookup("bird"))
|
viper.BindPFlag("bird_socket", pflag.Lookup("bird"))
|
||||||
|
|
||||||
pflag.String("listen", "8000", "listen address, set either in parameter or environment variable BIRDLG_PROXY_PORT")
|
pflag.StringSlice("listen", []string{"8000"}, "listen address, set either in parameter or environment variable BIRDLG_PROXY_PORT")
|
||||||
viper.BindPFlag("listen", pflag.Lookup("listen"))
|
viper.BindPFlag("listen", pflag.Lookup("listen"))
|
||||||
|
|
||||||
pflag.String("allowed", "", "IPs or networks allowed to access this proxy, separated by commas. Don't set to allow all IPs.")
|
pflag.String("allowed", "", "IPs or networks allowed to access this proxy, separated by commas. Don't set to allow all IPs.")
|
||||||
|
|||||||
Reference in New Issue
Block a user