1
mirror of https://github.com/xddxdd/bird-lg-go synced 2025-10-21 01:42:15 +02:00

14 Commits

Author SHA1 Message Date
Lan Tian
e7010f75f8 release: v1.2.0 2023-01-06 23:05:05 -06:00
Yuhui Xu
dba2af7634 proxy: fix description for --traceroute_flags (#70) 2022-12-27 15:38:41 -06:00
Yuhui Xu
049775319b proxy: autodetect traceroute args on startup (#69) 2022-12-25 15:41:29 -06:00
Lan Tian
47c66b125c release: v1.1.1 2022-12-18 16:26:47 -06:00
Yuhui Xu
9e17b116f1 frontend: refactor bgpmap and fix node colors (#67)
* frontend: refactor bgpmap and fix node colors

* frontend: alternative way to test bgpmap
2022-12-07 16:30:19 -06:00
Lan Tian
335ad40634 release: v1.1.0 2022-11-27 17:22:21 -06:00
dependabot[bot]
6ec0f2e7a6 build(deps): bump github.com/spf13/viper from 1.13.0 to 1.14.0 in /proxy (#65)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-07 20:26:44 -06:00
dependabot[bot]
4b73cf0fcb build(deps): bump github.com/spf13/viper in /frontend (#64)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-07 20:26:37 -06:00
Klara Modin
3b1d001543 frontend: sortable tables in summary (#61)
Adapted from https://stackoverflow.com/a/57080195.
Additions:
1. sortTable accepts a secondary column and sorting direction. The 'Name'
   column (number 0) is always used for the secondary
2. use classes 'ascSorted' and 'descSorted' to toggle between ascending and
   descending order
3. in the table functions, use .innerHTML instead of .innnerText so the
   link to detailed protocol information does not get lost. Also
   preserve .classList
2022-09-18 15:58:20 -05:00
dependabot[bot]
675cb26ed1 build(deps): bump github.com/spf13/viper from 1.12.0 to 1.13.0 in /proxy (#63)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-14 12:22:03 -05:00
dependabot[bot]
556d3e50d3 build(deps): bump github.com/spf13/viper in /frontend (#62)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-14 12:21:56 -05:00
Lan Tian
06796f546e general: remove failing docker image build tasks for PRs 2022-08-25 11:42:41 -05:00
Potat0
d029d6684c Fix the wrong order of examples (#60) 2022-08-17 11:44:35 -05:00
日下部 詩
5ce0f55f35 1. support local whois. 2 add some useful bird command (#59) 2022-08-11 22:34:39 -04:00
19 changed files with 904 additions and 341 deletions

View File

@@ -4,14 +4,6 @@ workflows:
docker:
jobs:
- build
- docker-frontend:
context:
- docker
requires:
- build
filters:
branches:
ignore: master
- docker-frontend-deploy:
context:
- docker
@@ -20,14 +12,6 @@ workflows:
filters:
branches:
only: master
- docker-proxy:
context:
- docker
requires:
- build
filters:
branches:
ignore: master
- docker-proxy-deploy:
context:
- docker
@@ -59,43 +43,6 @@ jobs:
go get -v -t -d ./...
go test -v ./...
docker-frontend:
machine:
image: ubuntu-2004:202111-02
environment:
BUILDX_PLATFORMS: linux/amd64,linux/arm64,linux/386,linux/arm/v7
steps:
- checkout
- run:
name: Install buildx
command: |
BUILDX_BINARY_URL="https://github.com/docker/buildx/releases/download/v0.7.1/buildx-v0.7.1.linux-amd64"
curl --output docker-buildx \
--silent --show-error --location --fail --retry 3 \
"$BUILDX_BINARY_URL"
mkdir -p ~/.docker/cli-plugins
mv docker-buildx ~/.docker/cli-plugins/
chmod a+x ~/.docker/cli-plugins/docker-buildx
docker buildx install
# Run binfmt
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- run:
name: Build Docker image
environment:
BUILD_ID: << pipeline.number >>
command: |
echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin
docker buildx create --name mybuilder --use
docker buildx build \
--platform $BUILDX_PLATFORMS \
-t $DOCKER_USERNAME/bird-lg-go:circleci-build$BUILD_ID \
--progress plain \
frontend
docker-frontend-deploy:
machine:
image: ubuntu-2004:202111-02
@@ -138,43 +85,6 @@ jobs:
--progress plain \
--push frontend
docker-proxy:
machine:
image: ubuntu-2004:202111-02
environment:
BUILDX_PLATFORMS: linux/amd64,linux/arm64,linux/386,linux/arm/v7
steps:
- checkout
- run:
name: Install buildx
command: |
BUILDX_BINARY_URL="https://github.com/docker/buildx/releases/download/v0.7.1/buildx-v0.7.1.linux-amd64"
curl --output docker-buildx \
--silent --show-error --location --fail --retry 3 \
"$BUILDX_BINARY_URL"
mkdir -p ~/.docker/cli-plugins
mv docker-buildx ~/.docker/cli-plugins/
chmod a+x ~/.docker/cli-plugins/docker-buildx
docker buildx install
# Run binfmt
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
- run:
name: Build Docker image
environment:
BUILD_ID: << pipeline.number >>
command: |
echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin
docker buildx create --name mybuilder --use
docker buildx build \
--platform $BUILDX_PLATFORMS \
-t $DOCKER_USERNAME/bird-lgproxy-go:circleci-build$BUILD_ID \
--progress plain \
proxy
docker-proxy-deploy:
machine:
image: ubuntu-2004:202111-02

View File

@@ -58,15 +58,13 @@ 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 |
| ---------- | --------- | -------------------- | ----------- |
| servers | --servers | BIRDLG_SERVERS | server name prefixes, separated by comma |
| domain | --domain | BIRDLG_DOMAIN | server name domain suffixes |
| listen | --listen | BIRDLG_LISTEN | address bird-lg is listening on (default "5000") |
| proxy_port | --proxy-port | BIRDLG_PROXY_PORT | port bird-lgproxy is running on (default 8000) |
| whois | --whois | BIRDLG_WHOIS | whois server for queries (default "whois.verisign-grs.com") |
| whois | --whois | BIRDLG_WHOIS | whois server for queries (default "whois.verisign-grs.com"). Start with "/" to spacify local whois binary("/usr/local/whois"). |
| dns_interface | --dns-interface | BIRDLG_DNS_INTERFACE | dns zone to query ASN information (default "asn.cymru.com") |
| bgpmap_info | --bgpmap-info | BIRDLG_BGPMAP_INFO | the infos displayed in bgpmap, separated by comma, start with `:` means allow multiline (default "asn,as-name,ASName,descr") |
| title_brand | --title-brand | BIRDLG_TITLE_BRAND | prefix of page titles in browser tabs (default "Bird-lg Go") |
@@ -79,6 +77,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 +122,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, supports multiple flags separated with space. |
| 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

View File

@@ -1 +1 @@
v1.0.0
v1.2.0

View File

@@ -153,9 +153,11 @@ Request:
```json
{
"servers": [],
"type": "server_list",
"args": ""
"servers": [
"alpha"
],
"type": "bird",
"args": "show status"
}
```
@@ -179,11 +181,9 @@ Request:
```json
{
"servers": [
"alpha"
],
"type": "bird",
"args": "show status"
"servers": [],
"type": "server_list",
"args": ""
}
```

View File

@@ -0,0 +1,73 @@
// adapted from https://stackoverflow.com/a/57080195
document.querySelectorAll('table.sortable')
.forEach((table)=> {
table.querySelectorAll('th')
.forEach((element, columnNo) => {
element.addEventListener('click', event => {
if(element.classList.contains('ascSorted')) {
dir = -1;
element.classList.remove('ascSorted');
element.classList.add('descSorted');
element.innerText = element.innerText.slice(0,-2) + " ↓";
} else if(element.classList.contains('descSorted')) {
dir = 1;
element.classList.remove('descSorted');
element.classList.add('ascSorted');
element.innerText = element.innerText.slice(0,-2) + " ↑";
} else {
dir = 1;
element.classList.add('ascSorted');
element.innerText += " ↑";
}
sortTable(table, columnNo, 0, dir, 1);
});
});
});
function sortTable(table, priCol, secCol, priDir, secDir) {
const tableBody = table.querySelector('tbody');
const tableData = table2data(tableBody);
tableData.sort((a, b) => {
if(a[priCol] === b[priCol]) {
if(a[secCol] > b[secCol]) {
return secDir;
} else {
return -secDir;
}
} else if(a[priCol] > b[priCol]) {
return priDir;
} else {
return -priDir;
}
});
data2table(tableBody, tableData);
}
function table2data(tableBody) {
const tableData = [];
tableBody.querySelectorAll('tr')
.forEach(row => {
const rowData = [];
row.querySelectorAll('td')
.forEach(cell => {
rowData.push(cell.innerHTML);
});
rowData.classList = row.classList.toString();
tableData.push(rowData);
});
return tableData;
}
function data2table(tableBody, tableData) {
tableBody.querySelectorAll('tr')
.forEach((row, i) => {
const rowData = tableData[i];
row.classList = rowData.classList;
row.querySelectorAll('td')
.forEach((cell, j) => {
cell.innerHTML = rowData[j];
});
tableData.push(rowData);
});
}

View File

@@ -76,6 +76,7 @@
<script src="/static/jsdelivr/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="/static/jsdelivr/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js" integrity="sha256-0IiaoZCI++9oAAvmCb5Y0r93XkuhvJpRalZLffQXLok=" crossorigin="anonymous"></script>
<script src="/static/sortTable.js"></script>
<script>
function goto() {

View File

@@ -1,6 +1,6 @@
{{ $ServerName := urlquery .ServerName }}
<table class="table table-striped table-bordered table-sm">
<table class="table table-striped table-bordered table-sm sortable">
<thead>
{{ range .Header }}
<th scope="col">{{ html . }}</th>

View File

@@ -1,13 +1,22 @@
package main
import (
"encoding/json"
"fmt"
"html"
"net"
"regexp"
"strings"
)
func graphvizEscape(s string) string {
result, err := json.Marshal(s)
if err != nil {
return err.Error()
} else {
return string(result)
}
}
func getASNRepresentation(asn string) string {
if setting.dnsInterface != "" {
// get ASN representation using DNS
@@ -15,9 +24,9 @@ func getASNRepresentation(asn string) string {
if err == nil {
result := strings.Join(records, " ")
if resultSplit := strings.Split(result, " | "); len(resultSplit) > 1 {
result = strings.Join(resultSplit[1:], "\\n")
result = strings.Join(resultSplit[1:], "\n")
}
return fmt.Sprintf("AS%s\\n%s", asn, result)
return fmt.Sprintf("AS%s\n%s", asn, result)
}
}
@@ -67,26 +76,28 @@ func getASNRepresentation(asn string) string {
}
func birdRouteToGraphviz(servers []string, responses []string, target string) string {
graph := make(map[string]string)
graph := make(map[string](map[string]string))
// Helper to add an edge
addEdge := func(src string, dest string, attr string) {
key := "\"" + html.EscapeString(src) + "\" -> \"" + html.EscapeString(dest) + "\""
addEdge := func(src string, dest string, attrKey string, attrValue string) {
key := graphvizEscape(src) + " -> " + graphvizEscape(dest)
_, present := graph[key]
// If there are multiple edges / routes between 2 nodes, only pick the first one
if present {
return
if !present {
graph[key] = map[string]string{}
}
if attrKey != "" {
graph[key][attrKey] = attrValue
}
graph[key] = attr
}
// Helper to set attribute for a point in graph
addPoint := func(name string, attr string) {
key := "\"" + html.EscapeString(name) + "\""
addPoint := func(name string, attrKey string, attrValue string) {
key := graphvizEscape(name)
_, present := graph[key]
// Do not remove point's attributes if it's already present
if present && len(attr) == 0 {
return
if !present {
graph[key] = map[string]string{}
}
if attrKey != "" {
graph[key][attrKey] = attrValue
}
graph[key] = attr
}
// The protocol name for each route (e.g. "ibgp_sea02") is encoded in the form:
// unicast [ibgp_sea02 2021-08-27 from fd86:bad:11b7:1::1] * (100/1015) [i]
@@ -95,13 +106,16 @@ func birdRouteToGraphviz(servers []string, responses []string, target string) st
// Possible values are defined at https://gitlab.nic.cz/labs/bird/-/blob/v2.0.8/nest/rt-attr.c#L81-87
routeSplitRe := regexp.MustCompile("(unicast|blackhole|unreachable|prohibited)")
addPoint("Target: "+target, "[color=red,shape=diamond]")
addPoint("Target: "+target, "color", "red")
addPoint("Target: "+target, "shape", "diamond")
for serverID, server := range servers {
response := responses[serverID]
if len(response) == 0 {
continue
}
addPoint(server, "[color=blue,shape=box]")
addPoint(server, "color", "blue")
addPoint(server, "shape", "box")
routes := routeSplitRe.Split(response, -1)
targetNodeName := "Target: " + target
@@ -153,15 +167,16 @@ func birdRouteToGraphviz(servers []string, responses []string, target string) st
// First step starting from originating server
if len(paths) > 0 {
attrs := []string{"fontsize=12.0"}
edgeTarget := getASNRepresentation(paths[0])
addEdge(server, edgeTarget, "fontsize", "12.0")
if routePreferred {
attrs = append(attrs, "color=red")
addEdge(server, edgeTarget, "color", "red")
// Only set color for next step, origin color is set to blue above
addPoint(edgeTarget, "color", "red")
}
if len(routeNexthop) > 0 {
attrs = append(attrs, fmt.Sprintf("label=\"%s\\n%s\"", protocolName, routeNexthop))
addEdge(server, edgeTarget, "label", protocolName + "\n" + routeNexthop)
}
formattedAttr := fmt.Sprintf("[%s]", strings.Join(attrs, ","))
addEdge(server, getASNRepresentation(paths[0]), formattedAttr)
}
// Following steps, edges between AS
@@ -169,29 +184,51 @@ func birdRouteToGraphviz(servers []string, responses []string, target string) st
if pathIndex == 0 {
continue
}
addEdge(getASNRepresentation(paths[pathIndex-1]), getASNRepresentation(paths[pathIndex]), (map[bool]string{true: "[color=red]"})[routePreferred])
if routePreferred {
addEdge(getASNRepresentation(paths[pathIndex-1]), getASNRepresentation(paths[pathIndex]), "color", "red")
// Only set color for next step, origin color is set to blue above
addPoint(getASNRepresentation(paths[pathIndex]), "color", "red")
} else {
addEdge(getASNRepresentation(paths[pathIndex-1]), getASNRepresentation(paths[pathIndex]), "", "")
}
}
// Last AS to destination
addEdge(getASNRepresentation(paths[len(paths)-1]), targetNodeName, (map[bool]string{true: "[color=red]"})[routePreferred])
if routePreferred {
addEdge(getASNRepresentation(paths[len(paths)-1]), targetNodeName, "color", "red")
} else {
addEdge(getASNRepresentation(paths[len(paths)-1]), targetNodeName, "", "")
}
}
if len(nonBGPRoutes) > 0 {
protocolsForRoute := fmt.Sprintf("label=\"%s\"", strings.Join(nonBGPRoutes, "\\n"))
attrs := []string{protocolsForRoute, "fontsize=12.0"}
addEdge(server, targetNodeName, "label", strings.Join(nonBGPRoutes, "\n"))
addEdge(server, targetNodeName, "fontsize", "12.0")
if nonBGPRoutePreferred {
attrs = append(attrs, "color=red")
addEdge(server, targetNodeName, "color", "red")
}
formattedAttr := fmt.Sprintf("[%s]", strings.Join(attrs, ","))
addEdge(server, targetNodeName, formattedAttr)
}
}
// Combine all graphviz commands
var result string
for edge, attr := range graph {
result += edge + " " + attr + ";\n"
result += edge;
if len(attr) != 0 {
result += " ["
isFirst := true
for k, v := range attr {
if isFirst {
isFirst = false
} else {
result += ","
}
result += graphvizEscape(k) + "=" + graphvizEscape(v) + "";
}
result += "]"
}
result += ";\n"
}
return "digraph {\n" + result + "}\n"
}

View File

@@ -5,6 +5,16 @@ import (
"testing"
)
func contains(s []string, str string) bool {
for _, v := range s {
if v == str {
return true
}
}
return false
}
func TestGetASNRepresentationDNS(t *testing.T) {
checkNetwork(t)
@@ -36,6 +46,7 @@ func TestGetASNRepresentationFallback(t *testing.T) {
}
}
// Broken due to random order of attributes
func TestBirdRouteToGraphviz(t *testing.T) {
setting.dnsInterface = ""
@@ -48,12 +59,13 @@ func TestBirdRouteToGraphviz(t *testing.T) {
BGP.as_path: 4242422601
BGP.next_hop: 172.18.0.2`
expectedResult := `digraph {
"Target: 192.168.0.1" [color=red,shape=diamond];
"alpha" [color=blue,shape=box];
"alpha" -> "AS4242422601" [fontsize=12.0,color=red,label="alpha*\n172.18.0.2"];
"AS4242422601" -> "Target: 192.168.0.1" [color=red];
}`
expectedLinesInResult := []string{
`"AS4242422601" [`,
`"AS4242422601" -> "Target: 192.168.0.1" [`,
`"Target: 192.168.0.1" [`,
`"alpha" [`,
`"alpha" -> "AS4242422601" [`,
}
result := birdRouteToGraphviz([]string{
"alpha",
@@ -61,9 +73,10 @@ func TestBirdRouteToGraphviz(t *testing.T) {
fakeResult,
}, "192.168.0.1")
for _, line := range strings.Split(result, "\n") {
if !strings.Contains(expectedResult, line) {
t.Errorf("Unexpected line in result: %s", line)
for _, line := range expectedLinesInResult {
if !strings.Contains(result, line) {
t.Errorf("Expected line in result not found: %s", line)
}
}
}

View File

@@ -5,10 +5,6 @@ go 1.16
require (
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/gorilla/handlers v1.5.1
github.com/pelletier/go-toml/v2 v2.0.2 // indirect
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.12.0
github.com/subosito/gotenv v1.4.0 // indirect
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d // indirect
gopkg.in/ini.v1 v1.66.6 // indirect
github.com/spf13/viper v1.14.0
)

File diff suppressed because it is too large Load Diff

View File

@@ -13,18 +13,26 @@ import (
// static options map
var optionsMap = map[string]string{
"summary": "show protocols",
"detail": "show protocols all",
"route": "show route for ...",
"route_all": "show route for ... all",
"route_bgpmap": "show route for ... (bgpmap)",
"route_where": "show route where net ~ [ ... ]",
"route_where_all": "show route where net ~ [ ... ] all",
"route_where_bgpmap": "show route where net ~ [ ... ] (bgpmap)",
"route_generic": "show route ...",
"generic": "show ...",
"whois": "whois ...",
"traceroute": "traceroute ...",
"summary": "show protocols",
"detail": "show protocols all ...",
"route_from_protocol": "show route protocol ...",
"route_from_protocol_all": "show route protocol ... all",
"route_from_protocol_all_primary": "show route protocol ... all primary",
"route_filtered_from_protocol": "show route filtered protocol ...",
"route_filtered_from_protocol_all": "show route filtered protocol ... all",
"route_from_origin": "show route where bgp_path.last = ...",
"route_from_origin_all": "show route where bgp_path.last = ... all",
"route_from_origin_all_primary": "show route where bgp_path.last = ... all primary",
"route": "show route for ...",
"route_all": "show route for ... all",
"route_bgpmap": "show route for ... (bgpmap)",
"route_where": "show route where net ~ [ ... ]",
"route_where_all": "show route where net ~ [ ... ] all",
"route_where_bgpmap": "show route where net ~ [ ... ] (bgpmap)",
"route_generic": "show route ...",
"generic": "show ...",
"whois": "whois ...",
"traceroute": "traceroute ...",
}
// pre-compiled regexp and constant statemap for summary rendering

View File

@@ -17,15 +17,26 @@ import (
)
var primitiveMap = map[string]string{
"summary": "show protocols",
"detail": "show protocols all %s",
"route": "show route for %s",
"route_all": "show route for %s all",
"route_where": "show route where net ~ [ %s ]",
"route_where_all": "show route where net ~ [ %s ] all",
"route_generic": "show route %s",
"generic": "show %s",
"traceroute": "%s",
"summary": "show protocols",
"detail": "show protocols all %s",
"route_from_protocol": "show route protocol %s",
"route_from_protocol_all": "show route protocol %s all",
"route_from_protocol_primary": "show route protocol %s 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_all": "show route filtered protocol %s all",
"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_primary": "show route where bgp_path.last = %s primary",
"route_from_origin_all_primary": "show route where bgp_path.last = %s all primary",
"route": "show route for %s",
"route_all": "show route for %s all",
"route_where": "show route where net ~ [ %s ]",
"route_where_all": "show route where net ~ [ %s ] all",
"route_generic": "show route %s",
"generic": "show %s",
"whois": "%s",
"traceroute": "%s",
}
// serve up a generic error
@@ -204,6 +215,16 @@ func webServerStart(l net.Listener) {
// backend routes
http.HandleFunc("/summary/", webBackendCommunicator("bird", "summary"))
http.HandleFunc("/detail/", webBackendCommunicator("bird", "detail"))
http.HandleFunc("/route_filtered_from_protocol/", webBackendCommunicator("bird", "route_filtered_from_protocol"))
http.HandleFunc("/route_filtered_from_protocol_all/", webBackendCommunicator("bird", "route_filtered_from_protocol_all"))
http.HandleFunc("/route_from_protocol/", webBackendCommunicator("bird", "route_from_protocol"))
http.HandleFunc("/route_from_protocol_all/", webBackendCommunicator("bird", "route_from_protocol_all"))
http.HandleFunc("/route_from_protocol_primary/", webBackendCommunicator("bird", "route_from_protocol_primary"))
http.HandleFunc("/route_from_protocol_all_primary/", webBackendCommunicator("bird", "route_from_protocol_all_primary"))
http.HandleFunc("/route_from_origin/", webBackendCommunicator("bird", "route_from_origin"))
http.HandleFunc("/route_from_origin_all/", webBackendCommunicator("bird", "route_from_origin_all"))
http.HandleFunc("/route_from_origin_primary/", webBackendCommunicator("bird", "route_from_origin_primary"))
http.HandleFunc("/route_from_origin_all_primary/", webBackendCommunicator("bird", "route_from_origin_all_primary"))
http.HandleFunc("/route/", webBackendCommunicator("bird", "route"))
http.HandleFunc("/route_all/", webBackendCommunicator("bird", "route_all"))
http.HandleFunc("/route_bgpmap/", webHandlerBGPMap("bird", "route_bgpmap"))

View File

@@ -3,6 +3,8 @@ package main
import (
"io"
"net"
"os/exec"
"strings"
"time"
)
@@ -12,18 +14,31 @@ func whois(s string) string {
return ""
}
conn, err := net.DialTimeout("tcp", setting.whoisServer+":43", 5*time.Second)
if err != nil {
return err.Error()
}
defer conn.Close()
if strings.HasPrefix(setting.whoisServer, "/") {
cmd := exec.Command(setting.whoisServer, s)
output, err := cmd.CombinedOutput()
if err != nil {
return err.Error()
}
if len(output) > 65535 {
output = output[:65535]
}
return string(output)
} else {
buf := make([]byte, 65536)
conn, err := net.DialTimeout("tcp", setting.whoisServer+":43", 5*time.Second)
if err != nil {
return err.Error()
}
defer conn.Close()
conn.Write([]byte(s + "\r\n"))
conn.Write([]byte(s + "\r\n"))
buf := make([]byte, 65536)
n, err := io.ReadFull(conn, buf)
if err != nil && err != io.ErrUnexpectedEOF {
return err.Error()
n, err := io.ReadFull(conn, buf)
if err != nil && err != io.ErrUnexpectedEOF {
return err.Error()
}
return string(buf[:n])
}
return string(buf[:n])
}

View File

@@ -6,10 +6,6 @@ require (
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/gorilla/handlers v1.5.1
github.com/pelletier/go-toml/v2 v2.0.2 // indirect
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.12.0
github.com/subosito/gotenv v1.4.0 // indirect
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d // indirect
gopkg.in/ini.v1 v1.66.6 // indirect
github.com/spf13/viper v1.14.0
)

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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, supports multiple flags separated with space.")
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)

View File

@@ -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."))
}