1
mirror of https://github.com/xddxdd/bird-lg-go synced 2025-10-30 09:40:51 +01:00

69 Commits

Author SHA1 Message Date
Lan Tian
76174cdc08 release: v1.3.7 2024-03-12 17:47:29 -07:00
Lan Tian
088bb6fe5a readme: add mention of MTR based lgproxy docker image 2024-03-12 17:46:56 -07:00
Lan Tian
3951eed011 ci: fix dockerfile path for proxy image with mtr 2024-03-09 12:21:00 -08:00
Lan Tian
91c0a8962b ci: also test mtr binary in proxy image 2024-03-09 12:09:19 -08:00
Lan Tian
5f7850a903 ci: also build docker image with mtr on regular commits 2024-03-09 12:05:20 -08:00
Lan Tian
6a78cf2e80 proxy: add docker image with mtr 2024-03-09 12:04:05 -08:00
Lan Tian
5b5a44bcb6 release: v1.3.6 2024-02-25 19:47:39 -08:00
Marc 'risson' Schmitt
ac31862237 frontend: show dynamic BGP sessions without any color (#98)
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-02-18 22:11:20 -08:00
Lan Tian
86129190ab release: v1.3.5 2024-01-01 14:43:25 -08:00
Lan Tian
ff55064a20 frontend: adjust navbar display of many servers 2023-12-31 16:31:56 -08:00
dependabot[bot]
dbb02c04ed Merge pull request #95 from xddxdd/dependabot/go_modules/proxy/github.com/spf13/viper-1.18.2 2023-12-19 00:50:09 +00:00
dependabot[bot]
c2b7de2e17 build(deps): bump github.com/spf13/viper from 1.18.1 to 1.18.2 in /proxy
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.1 to 1.18.2.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.1...v1.18.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-19 00:48:47 +00:00
dependabot[bot]
c1b578e8db Merge pull request #94 from xddxdd/dependabot/go_modules/frontend/github.com/spf13/viper-1.18.2 2023-12-19 00:28:10 +00:00
dependabot[bot]
7b0e5689d4 build(deps): bump github.com/spf13/viper in /frontend
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.1 to 1.18.2.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.1...v1.18.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-19 00:26:13 +00:00
dependabot[bot]
3c46bda49d Merge pull request #93 from xddxdd/dependabot/go_modules/frontend/github.com/spf13/viper-1.18.1 2023-12-11 00:45:58 +00:00
dependabot[bot]
32e00d2ce3 build(deps): bump github.com/spf13/viper in /frontend
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.0 to 1.18.1.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.0...v1.18.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 00:44:41 +00:00
dependabot[bot]
a19750cdef Merge pull request #92 from xddxdd/dependabot/go_modules/proxy/github.com/spf13/viper-1.18.1 2023-12-11 00:04:27 +00:00
dependabot[bot]
7f1cdaa4ee build(deps): bump github.com/spf13/viper from 1.18.0 to 1.18.1 in /proxy
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.0 to 1.18.1.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.0...v1.18.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 00:03:09 +00:00
Lan Tian
2d2193041e release: v1.3.4 2023-12-06 23:54:47 -08:00
dependabot[bot]
aad8ee98d7 Merge pull request #91 from xddxdd/dependabot/go_modules/frontend/github.com/spf13/viper-1.18.0 2023-12-07 00:58:49 +00:00
dependabot[bot]
00b5c12787 build(deps): bump github.com/spf13/viper in /frontend
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.17.0 to 1.18.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.17.0...v1.18.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>
2023-12-07 00:57:01 +00:00
dependabot[bot]
55a1eb54fd Merge pull request #90 from xddxdd/dependabot/go_modules/proxy/github.com/spf13/viper-1.18.0 2023-12-07 00:33:12 +00:00
dependabot[bot]
0594edc69d build(deps): bump github.com/spf13/viper from 1.17.0 to 1.18.0 in /proxy
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.17.0 to 1.18.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.17.0...v1.18.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>
2023-12-07 00:31:37 +00:00
Lan Tian
38bf6aba09 frontend: fix unit tests 2023-11-25 10:47:37 -08:00
Lan Tian
d261c22235 frontend: add connection timeout 2023-11-25 10:30:13 -08:00
dependabot[bot]
19aa8c77c5 Merge pull request #88 from xddxdd/dependabot/go_modules/frontend/github.com/gorilla/handlers-1.5.2 2023-11-06 01:01:43 +00:00
dependabot[bot]
fe07ebb5a5 build(deps): bump github.com/gorilla/handlers in /frontend
Bumps [github.com/gorilla/handlers](https://github.com/gorilla/handlers) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/gorilla/handlers/releases)
- [Commits](https://github.com/gorilla/handlers/compare/v1.5.1...v1.5.2)

---
updated-dependencies:
- dependency-name: github.com/gorilla/handlers
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-06 01:00:08 +00:00
dependabot[bot]
66547ebfa9 Merge pull request #87 from xddxdd/dependabot/go_modules/proxy/github.com/gorilla/handlers-1.5.2 2023-11-06 00:24:24 +00:00
dependabot[bot]
d253e4311b build(deps): bump github.com/gorilla/handlers in /proxy
Bumps [github.com/gorilla/handlers](https://github.com/gorilla/handlers) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/gorilla/handlers/releases)
- [Commits](https://github.com/gorilla/handlers/compare/v1.5.1...v1.5.2)

---
updated-dependencies:
- dependency-name: github.com/gorilla/handlers
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-06 00:22:00 +00:00
Lan Tian
026498ba2f general: auto merge updates from dependabot 2023-10-24 23:32:55 -07:00
Lan Tian
27c348a864 release: v1.3.3 2023-10-21 00:03:02 -07:00
Lan Tian
43b4ad93dd general: only build docker develop images on master branch push 2023-10-08 21:08:07 -07:00
dependabot[bot]
6176c45006 build(deps): bump github.com/spf13/viper from 1.16.0 to 1.17.0 in /proxy (#86)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.16.0 to 1.17.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.16.0...v1.17.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>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-08 21:04:12 -07:00
dependabot[bot]
47113184f4 build(deps): bump github.com/spf13/viper in /frontend (#85)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.16.0 to 1.17.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.16.0...v1.17.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>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-08 21:04:04 -07:00
Simon Marsh
3c9a3e4339 Enable proxy to allow access by CIDR network as well as IP (#84) 2023-10-04 21:33:37 -07:00
Lan Tian
8457b18d46 release: v1.3.2.2 2023-09-09 01:44:16 -07:00
Lan Tian
f8f64b03a6 general: add tag for release Docker images 2023-09-09 01:42:38 -07:00
Lan Tian
cc818c1cc0 release: v1.3.2.1 2023-09-09 01:33:24 -07:00
Lan Tian
6224b43808 general: update GitHub actions 2023-09-09 01:32:56 -07:00
Lan Tian
17e0b14243 general: update GitHub actions 2023-09-09 01:29:49 -07:00
Lan Tian
b4c1bed9ba release: v1.3.2 2023-09-09 01:23:14 -07:00
Lan Tian
abb32abff3 general: add unit test for docker images 2023-09-08 18:45:58 -07:00
Lan Tian
b368c75aa3 frontend: fix whois client cannot get default whois port 2023-09-08 18:38:23 -07:00
Lan Tian
09405cdb38 frontend: also print whois client output on error 2023-09-08 18:22:31 -07:00
Lan Tian
f999d47d9f frontend: force enable whois client regex parser on alpine/musl 2023-09-07 19:14:04 -07:00
Lan Tian
005dfb1435 frontend: make docker image whois client try to use config file 2023-09-07 00:51:56 -07:00
Lan Tian
4bd7a6bb95 general: also release docker image to GitHub container registry 2023-09-06 21:06:10 -07:00
Lan Tian
462d76a2d0 general: reenable docker multiarch build 2023-09-06 21:02:32 -07:00
Lan Tian
58f217578c readme: add note about development version of docker image 2023-09-06 20:59:27 -07:00
Lan Tian
0e95727de1 general: reorganize GitHub Actions workflows and readd unit test 2023-09-06 20:55:45 -07:00
Lan Tian
a48f1c8040 general: move Docker image build to GitHub Actions 2023-09-06 20:48:14 -07:00
Lan Tian
81acde3a37 frontend: add whois client for more complex whois lookup 2023-09-06 20:35:30 -07:00
Lan Tian
7c0fe0d512 proxy: update traceroute version in Docker image 2023-09-06 20:33:40 -07:00
dependabot[bot]
a5f4452d02 build(deps): bump github.com/jarcoal/httpmock in /frontend (#82)
Bumps [github.com/jarcoal/httpmock](https://github.com/jarcoal/httpmock) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/jarcoal/httpmock/releases)
- [Commits](https://github.com/jarcoal/httpmock/compare/v1.3.0...v1.3.1)

---
updated-dependencies:
- dependency-name: github.com/jarcoal/httpmock
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-25 00:44:30 -07:00
Lan Tian
b237185ef7 release: v1.3.1 2023-06-18 20:14:41 -07:00
towalink
e949646790 Properly escape URL path (#81) 2023-06-10 15:14:10 -07:00
dependabot[bot]
bb479d22ae build(deps): bump github.com/spf13/viper from 1.15.0 to 1.16.0 in /proxy (#79)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.15.0...v1.16.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>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-02 00:58:12 -07:00
dependabot[bot]
d40f41b4d5 build(deps): bump github.com/spf13/viper in /frontend (#80)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.15.0...v1.16.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>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-02 00:58:06 -07:00
Lan Tian
cdc34704b5 release: v1.3.0 2023-05-14 12:37:46 -07:00
Lan Tian
db58bd3354 frontend: add a test case for lookup DNS -> WHOIS fallback 2023-05-06 00:26:40 -07:00
Lan Tian
a0246ccee2 general: add unit tests for >80% coverage
Includes a few minor fixes:
- frontend: support setting port for WHOIS server
- proxy: fix handling of very long lines
- proxy: refactor IP allowlist logic, parse allow IP list at startup
2023-05-06 00:23:28 -07:00
James Lu
ccd14af0c8 settings: treat empty environment variables as set (#77)
This allows disabling specific options like dns_interface or whois via environment variables.

ref: https://github.com/spf13/viper#working-with-environment-variables
2023-05-05 21:36:38 -07:00
Lan Tian
594ca80f50 frontend: fix whois lookup & only show bgpmap nexthop info on the first hop 2023-05-05 20:20:12 -07:00
Lan Tian
5625058e71 frontend: use ASN as bgpmap node identifier (instead of resolved whois result) 2023-05-05 19:52:30 -07:00
Lan Tian
7efa3237a9 frontend: refactor bgpmap code to fix #75 2023-05-05 01:58:05 -07:00
Lan Tian
7b0c8c0556 general: bump go version in go.mod 2023-01-26 22:01:47 -08:00
dependabot[bot]
ffd9165062 build(deps): bump github.com/spf13/viper in /frontend (#73)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.14.0 to 1.15.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.14.0...v1.15.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>
2023-01-23 10:50:42 -08:00
dependabot[bot]
24fd5203e8 build(deps): bump github.com/spf13/viper from 1.14.0 to 1.15.0 in /proxy (#74)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.14.0 to 1.15.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.14.0...v1.15.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>
2023-01-23 10:50:30 -08:00
Lan Tian
49a05767c1 ci: bump version for go-release-action 2023-01-08 01:16:23 -06:00
45 changed files with 5561 additions and 648 deletions

View File

@@ -1,127 +0,0 @@
version: 2.1
workflows:
docker:
jobs:
- build
- docker-frontend-deploy:
context:
- docker
requires:
- build
filters:
branches:
only: master
- docker-proxy-deploy:
context:
- docker
requires:
- build
filters:
branches:
only: master
jobs:
build:
docker:
- image: cimg/go:1.17
working_directory: /home/circleci/go/src/github.com/xddxdd/bird-lg-go
steps:
- checkout
- run:
name: Test frontend
command: |
export GO111MODULE=on
cd frontend
go get -v -t -d ./...
go test -v ./...
- run:
name: Test proxy
command: |
export GO111MODULE=on
cd proxy
go get -v -t -d ./...
go test -v ./...
docker-frontend-deploy:
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 \
--push frontend
docker buildx build \
--platform $BUILDX_PLATFORMS \
-t $DOCKER_USERNAME/bird-lg-go:latest \
--progress plain \
--push frontend
docker-proxy-deploy:
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 \
--push proxy
docker buildx build \
--platform $BUILDX_PLATFORMS \
-t $DOCKER_USERNAME/bird-lgproxy-go:latest \
--progress plain \
--push proxy

16
.github/workflows/auto-merge.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: auto-merge
on:
pull_request_target:
jobs:
auto-merge:
name: Dependabot Auto Merge
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]'
steps:
- uses: actions/checkout@v2
- uses: ahmadnassri/action-dependabot-auto-merge@v2
with:
target: minor
github-token: ${{ secrets.AUTOMERGE_TOKEN }}

127
.github/workflows/develop.yaml vendored Normal file
View File

@@ -0,0 +1,127 @@
on:
push:
branches:
- 'master'
pull_request:
branches:
- 'master'
jobs:
go-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Golang
uses: actions/setup-go@v4
- name: Run frontend unit test
run: |
export GO111MODULE=on
cd frontend
go get -v -t -d ./...
go test -v ./...
cd ..
- name: Run proxy unit test
run: |
export GO111MODULE=on
cd proxy
go get -v -t -d ./...
go test -v ./...
cd ..
docker-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Test whois binary in frontend image
run: |
docker build -t local/frontend frontend/
docker run --rm --net host --entrypoint whois local/frontend github.com || exit 1
docker run --rm --net host --entrypoint whois local/frontend -h whois.ripe.net github.com || exit 1
docker run --rm --net host --entrypoint whois local/frontend -h whois.ripe.net:43 github.com || exit 1
- name: Test traceroute binary in proxy image
run: |
docker build -t local/proxy proxy/
docker run --rm --net host --entrypoint traceroute local/proxy 127.0.0.1 || exit 1
docker run --rm --net host --entrypoint traceroute local/proxy ::1 || exit 1
- name: Test mtr binary in proxy image
run: |
docker build -t local/proxy:mtr -f proxy/Dockerfile.mtr proxy/
docker run --rm --net host --entrypoint mtr local/proxy:mtr -w -c1 -Z1 -G1 -b 127.0.0.1 || exit 1
docker run --rm --net host --entrypoint mtr local/proxy:mtr -w -c1 -Z1 -G1 -b ::1 || exit 1
docker-develop:
runs-on: ubuntu-latest
needs:
- go-test
- docker-test
if: github.event_name != 'pull_request'
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build frontend docker image
uses: docker/build-push-action@v4
with:
context: '{{defaultContext}}:frontend'
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
push: true
tags: |
xddxdd/bird-lg-go:develop
xddxdd/bird-lg-go:develop-${{ github.sha }}
ghcr.io/xddxdd/bird-lg-go:frontend-develop
ghcr.io/xddxdd/bird-lg-go:frontend-develop-${{ github.sha }}
- name: Build proxy docker image
uses: docker/build-push-action@v4
with:
context: '{{defaultContext}}:proxy'
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
push: true
tags: |
xddxdd/bird-lgproxy-go:develop
xddxdd/bird-lgproxy-go:develop-${{ github.sha }}
ghcr.io/xddxdd/bird-lg-go:proxy-develop
ghcr.io/xddxdd/bird-lg-go:proxy-develop-${{ github.sha }}
- name: Build proxy docker image
uses: docker/build-push-action@v4
with:
context: '{{defaultContext}}:proxy'
file: 'Dockerfile.mtr'
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
push: true
tags: |
xddxdd/bird-lgproxy-go:develop-mtr
xddxdd/bird-lgproxy-go:develop-${{ github.sha }}-mtr
ghcr.io/xddxdd/bird-lg-go:proxy-develop-mtr
ghcr.io/xddxdd/bird-lg-go:proxy-develop-${{ github.sha }}-mtr

View File

@@ -3,10 +3,11 @@ on:
types: [created]
jobs:
releases-matrix:
go-release:
name: Release Go Binary
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
goos: [linux, windows, darwin]
goarch: ["386", amd64, "arm", arm64]
@@ -18,18 +19,82 @@ jobs:
- goarch: "arm"
goos: windows
steps:
- uses: actions/checkout@v3
- uses: wangyoucao577/go-release-action@v1.30
- name: Checkout
uses: actions/checkout@v3
- name: Release frontend
uses: wangyoucao577/go-release-action@v1.40
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
project_path: "./frontend"
binary_name: "bird-lg-go"
- uses: wangyoucao577/go-release-action@v1.30
- name: Release proxy
uses: wangyoucao577/go-release-action@v1.40
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
project_path: "./proxy"
binary_name: "bird-lgproxy-go"
docker-release:
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build frontend docker image
uses: docker/build-push-action@v4
with:
context: '{{defaultContext}}:frontend'
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
push: true{context}/Dockerfile
tags: |
xddxdd/bird-lg-go:latest
xddxdd/bird-lg-go:${{ github.event.release.tag_name }}
ghcr.io/xddxdd/bird-lg-go:frontend
ghcr.io/xddxdd/bird-lg-go:frontend-${{ github.event.release.tag_name }}
- name: Build proxy docker image
uses: docker/build-push-action@v4
with:
context: '{{defaultContext}}:proxy'
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
push: true
tags: |
xddxdd/bird-lgproxy-go:latest
xddxdd/bird-lgproxy-go:${{ github.event.release.tag_name }}
ghcr.io/xddxdd/bird-lg-go:proxy
ghcr.io/xddxdd/bird-lg-go:proxy-${{ github.event.release.tag_name }}
- name: Build proxy docker image
uses: docker/build-push-action@v4
with:
context: '{{defaultContext}}:proxy'
file: 'Dockerfile.mtr'
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
push: true
tags: |
xddxdd/bird-lgproxy-go:latest-mtr
xddxdd/bird-lgproxy-go:${{ github.event.release.tag_name }}-mtr
ghcr.io/xddxdd/bird-lg-go:proxy-mtr
ghcr.io/xddxdd/bird-lg-go:proxy-${{ github.event.release.tag_name }}-mtr

4
.gitignore vendored
View File

@@ -20,7 +20,3 @@ proxy/proxy
# don't include generated bindata file
frontend/bindata.go
# don't include generated Dockerfiles
frontend/Dockerfile.*
proxy/Dockerfile.*

View File

@@ -90,7 +90,8 @@ Example: the following docker-compose.yml entry does the same as above, but by s
```yaml
services:
bird-lg:
image: xddxdd/bird-lg-go
# Use xddxdd/bird-lg-go:develop for the latest build from master branch
image: xddxdd/bird-lg-go:latest
container_name: bird-lg
restart: always
environment:
@@ -124,7 +125,7 @@ Configuration is handled by [viper](https://github.com/spf13/viper), any config
| 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 or networks 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 |
@@ -165,7 +166,9 @@ Example: the following docker-compose.yml entry does the same as above, but by s
```yaml
services:
bird-lgproxy:
image: xddxdd/bird-lgproxy-go
# Use xddxdd/bird-lgproxy-go:develop for the latest build from master branch
# Use xddxdd/bird-lgproxy-go:latest-mtr to use MTR instead of Traceroute
image: xddxdd/bird-lgproxy-go:latest
container_name: bird-lgproxy
restart: always
volumes:

View File

@@ -1 +1 @@
v1.2.0
v1.3.7

View File

@@ -1,4 +1,4 @@
FROM golang:buster AS step_0
FROM golang AS step_0
ENV CGO_ENABLED=0 GO111MODULE=on
WORKDIR /root
COPY . .
@@ -6,6 +6,28 @@ RUN go build -ldflags "-w -s" -o /frontend
################################################################################
FROM scratch AS step_1
FROM alpine:edge AS step_1
WORKDIR /root
RUN apk add --no-cache build-base pkgconf perl gettext \
libidn2-dev libidn2-static libunistring-dev libunistring-static gnu-libiconv-dev
RUN wget https://github.com/rfc1036/whois/archive/refs/tags/v5.5.18.tar.gz \
-O whois-5.5.18.tar.gz
RUN tar xvf whois-5.5.18.tar.gz \
&& cd whois-5.5.18 \
&& sed -i "s/#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L/#if 1/g" config.h \
&& make whois -j4 \
LDFLAGS="-static" CONFIG_FILE="/etc/whois.conf" PKG_CONFIG="pkg-config --static" HAVE_ICONV=1 \
&& strip /root/whois-5.5.18/whois
################################################################################
FROM scratch AS step_2
ENV PATH=/
ENV BIRDLG_WHOIS=/whois
COPY --from=step_0 /frontend /
COPY --from=step_1 /root/whois-5.5.18/whois /
COPY --from=step_1 /etc/services /etc/services
ENTRYPOINT ["/frontend"]

View File

@@ -113,7 +113,7 @@ func apiHandler(w http.ResponseWriter, r *http.Request) {
} else {
handler := apiHandlerMap[request.Type]
if handler == nil {
response = apiErrorHandler(errors.New("Invalid request type"))
response = apiErrorHandler(errors.New("invalid request type"))
} else {
response = handler(request)
}

207
frontend/api_test.go Normal file
View File

@@ -0,0 +1,207 @@
package main
import (
"bytes"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"github.com/jarcoal/httpmock"
"github.com/magiconair/properties/assert"
)
func TestApiServerListHandler(t *testing.T) {
setting.servers = []string{"alpha", "beta", "gamma"}
response := apiServerListHandler(apiRequest{})
assert.Equal(t, len(response.Result), 3)
assert.Equal(t, response.Result[0].(apiGenericResultPair).Server, "alpha")
assert.Equal(t, response.Result[1].(apiGenericResultPair).Server, "beta")
assert.Equal(t, response.Result[2].(apiGenericResultPair).Server, "gamma")
}
func TestApiGenericHandlerFactory(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
httpResponse := httpmock.NewStringResponder(200, BirdSummaryData)
httpmock.RegisterResponder("GET", "http://alpha:8000/bird?q="+url.QueryEscape("show protocols"), httpResponse)
setting.servers = []string{"alpha"}
setting.domain = ""
setting.proxyPort = 8000
request := apiRequest{
Servers: setting.servers,
Type: "bird",
Args: "show protocols",
}
handler := apiGenericHandlerFactory("bird")
response := handler(request)
assert.Equal(t, response.Error, "")
result := response.Result[0].(*apiGenericResultPair)
assert.Equal(t, result.Server, "alpha")
assert.Equal(t, result.Data, BirdSummaryData)
}
func TestApiSummaryHandler(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
httpResponse := httpmock.NewStringResponder(200, BirdSummaryData)
httpmock.RegisterResponder("GET", "http://alpha:8000/bird?q="+url.QueryEscape("show protocols"), httpResponse)
setting.servers = []string{"alpha"}
setting.domain = ""
setting.proxyPort = 8000
request := apiRequest{
Servers: setting.servers,
Type: "summary",
Args: "",
}
response := apiSummaryHandler(request)
assert.Equal(t, response.Error, "")
summary := response.Result[0].(*apiSummaryResultPair)
assert.Equal(t, summary.Server, "alpha")
// Protocol list will be sorted
assert.Equal(t, summary.Data[1].Name, "device1")
assert.Equal(t, summary.Data[1].Proto, "Device")
assert.Equal(t, summary.Data[1].Table, "---")
assert.Equal(t, summary.Data[1].State, "up")
assert.Equal(t, summary.Data[1].Since, "2021-08-27")
assert.Equal(t, summary.Data[1].Info, "")
}
func TestApiSummaryHandlerError(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
httpResponse := httpmock.NewStringResponder(200, "Mock backend error")
httpmock.RegisterResponder("GET", "http://alpha:8000/bird?q="+url.QueryEscape("show protocols"), httpResponse)
setting.servers = []string{"alpha"}
setting.domain = ""
setting.proxyPort = 8000
request := apiRequest{
Servers: setting.servers,
Type: "summary",
Args: "",
}
response := apiSummaryHandler(request)
assert.Equal(t, response.Error, "Mock backend error")
}
func TestApiWhoisHandler(t *testing.T) {
expectedData := "Mock Data"
server := WhoisServer{
t: t,
expectedQuery: "AS6939",
response: expectedData,
}
server.Listen()
go server.Run()
defer server.Close()
setting.whoisServer = server.server.Addr().String()
request := apiRequest{
Servers: []string{},
Type: "",
Args: "AS6939",
}
response := apiWhoisHandler(request)
assert.Equal(t, response.Error, "")
whoisResult := response.Result[0].(apiGenericResultPair)
assert.Equal(t, whoisResult.Server, "")
assert.Equal(t, whoisResult.Data, expectedData)
}
func TestApiErrorHandler(t *testing.T) {
err := errors.New("Mock Error")
response := apiErrorHandler(err)
assert.Equal(t, response.Error, "Mock Error")
}
func TestApiHandler(t *testing.T) {
setting.servers = []string{"alpha", "beta", "gamma"}
request := apiRequest{
Servers: []string{},
Type: "server_list",
Args: "",
}
requestJson, err := json.Marshal(request)
if err != nil {
t.Error(err)
}
r := httptest.NewRequest(http.MethodGet, "/api", bytes.NewReader(requestJson))
w := httptest.NewRecorder()
apiHandler(w, r)
var response apiResponse
err = json.Unmarshal(w.Body.Bytes(), &response)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(response.Result), 3)
// Hard to unmarshal JSON into apiGenericResultPair objects, won't check here
}
func TestApiHandlerBadJSON(t *testing.T) {
setting.servers = []string{"alpha", "beta", "gamma"}
r := httptest.NewRequest(http.MethodGet, "/api", strings.NewReader("{bad json}"))
w := httptest.NewRecorder()
apiHandler(w, r)
var response apiResponse
err := json.Unmarshal(w.Body.Bytes(), &response)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(response.Result), 0)
}
func TestApiHandlerInvalidType(t *testing.T) {
setting.servers = []string{"alpha", "beta", "gamma"}
request := apiRequest{
Servers: setting.servers,
Type: "invalid_type",
Args: "",
}
requestJson, err := json.Marshal(request)
if err != nil {
t.Error(err)
}
r := httptest.NewRequest(http.MethodGet, "/api", bytes.NewReader(requestJson))
w := httptest.NewRecorder()
apiHandler(w, r)
var response apiResponse
err = json.Unmarshal(w.Body.Bytes(), &response)
if err != nil {
t.Error(err)
}
assert.Equal(t, len(response.Result), 0)
}

83
frontend/asn_cache.go Normal file
View File

@@ -0,0 +1,83 @@
package main
import (
"fmt"
"net"
"strings"
)
type ASNCache map[string]string
func (cache ASNCache) _lookup(asn string) string {
// Try to get ASN representation using DNS
if setting.dnsInterface != "" {
records, err := net.LookupTXT(fmt.Sprintf("AS%s.%s", asn, setting.dnsInterface))
if err == nil {
result := strings.Join(records, " ")
if resultSplit := strings.Split(result, " | "); len(resultSplit) > 1 {
result = strings.Join(resultSplit[1:], "\n")
}
return fmt.Sprintf("AS%s\n%s", asn, result)
}
}
// Try to get ASN representation using WHOIS
if setting.whoisServer != "" {
if setting.bgpmapInfo == "" {
setting.bgpmapInfo = "asn,as-name,ASName,descr"
}
records := whois(fmt.Sprintf("AS%s", asn))
if records != "" {
recordsSplit := strings.Split(records, "\n")
var result []string
for _, title := range strings.Split(setting.bgpmapInfo, ",") {
if title == "asn" {
result = append(result, "AS"+asn)
}
}
for _, title := range strings.Split(setting.bgpmapInfo, ",") {
allow_multiline := false
if title[0] == ':' && len(title) >= 2 {
title = title[1:]
allow_multiline = true
}
for _, line := range recordsSplit {
if len(line) == 0 || line[0] == '%' || !strings.Contains(line, ":") {
continue
}
linearr := strings.SplitN(line, ":", 2)
line_title := linearr[0]
content := strings.TrimSpace(linearr[1])
if line_title != title {
continue
}
result = append(result, content)
if !allow_multiline {
break
}
}
}
if len(result) > 0 {
return strings.Join(result, "\n")
}
}
}
return ""
}
func (cache ASNCache) Lookup(asn string) string {
cachedValue, cacheOk := cache[asn]
if cacheOk {
return cachedValue
}
result := cache._lookup(asn)
if len(result) == 0 {
result = fmt.Sprintf("AS%s", asn)
}
cache[asn] = result
return result
}

View File

@@ -0,0 +1,52 @@
package main
import (
"strings"
"testing"
"github.com/magiconair/properties/assert"
)
func TestGetASNRepresentationDNS(t *testing.T) {
checkNetwork(t)
setting.dnsInterface = "asn.cymru.com"
setting.whoisServer = ""
cache := make(ASNCache)
result := cache.Lookup("6939")
if !strings.Contains(result, "HURRICANE") {
t.Errorf("Lookup AS6939 failed, got %s", result)
}
}
func TestGetASNRepresentationDNSFallback(t *testing.T) {
checkNetwork(t)
setting.dnsInterface = "invalid.example.com"
setting.whoisServer = "whois.arin.net"
cache := make(ASNCache)
result := cache.Lookup("6939")
if !strings.Contains(result, "HURRICANE") {
t.Errorf("Lookup AS6939 failed, got %s", result)
}
}
func TestGetASNRepresentationWhois(t *testing.T) {
checkNetwork(t)
setting.dnsInterface = ""
setting.whoisServer = "whois.arin.net"
cache := make(ASNCache)
result := cache.Lookup("6939")
if !strings.Contains(result, "HURRICANE") {
t.Errorf("Lookup AS6939 failed, got %s", result)
}
}
func TestGetASNRepresentationFallback(t *testing.T) {
setting.dnsInterface = ""
setting.whoisServer = ""
cache := make(ASNCache)
result := cache.Lookup("6939")
assert.Equal(t, result, "AS6939")
}

View File

@@ -8,6 +8,19 @@
<meta name="renderer" content="webkit">
<title>{{ html .Title }}</title>
<link rel="stylesheet" href="/static/jsdelivr/npm/bootstrap@4.5.1/dist/css/bootstrap.min.css" integrity="sha256-VoFZSlmyTXsegReQCNmbXrS4hBBUl/cexZvPmPWoJsY=" crossorigin="anonymous">
<style>
.navbar-nav {
flex-wrap: wrap;
}
@media (min-width: 768px) {
.navbar form {
min-width: 400px;
}
.nav-link {
padding: 0.2rem 0.5rem !important;
}
}
</style>
<meta name="robots" content="noindex, nofollow">
</head>
<body>
@@ -60,7 +73,7 @@
<option value="{{ html $k }}"{{ if eq $k $.URLOption }} selected{{end}}>{{ html $v }}</option>
{{ end }}
</select>
<input name="server" class="d-none" value="{{ html $server }}">
<input name="server" class="d-none" value="{{ html ($server | pathescape) }}">
<input name="target" class="form-control" placeholder="Target" aria-label="Target" value="{{ html $target }}">
<div class="input-group-append">
<button class="btn btn-outline-success" type="submit">&raquo;</button>

File diff suppressed because it is too large Load Diff

173
frontend/bgpmap_graph.go Normal file
View File

@@ -0,0 +1,173 @@
package main
import (
"encoding/json"
"fmt"
"strings"
)
type RouteAttrs map[string]string
type RoutePoint struct {
performLookup bool
attrs RouteAttrs
}
type RouteEdgeKey struct {
src string
dest string
}
type RouteEdgeValue struct {
label []string
attrs RouteAttrs
}
type RouteGraph struct {
points map[string]RoutePoint
edges map[RouteEdgeKey]RouteEdgeValue
}
func makeRouteGraph() RouteGraph {
return RouteGraph{
points: make(map[string]RoutePoint),
edges: make(map[RouteEdgeKey]RouteEdgeValue),
}
}
func makeRoutePoint() RoutePoint {
return RoutePoint{
performLookup: false,
attrs: make(RouteAttrs),
}
}
func makeRouteEdgeValue() RouteEdgeValue {
return RouteEdgeValue{
label: []string{},
attrs: make(RouteAttrs),
}
}
func (graph *RouteGraph) attrsToString(attrs RouteAttrs) string {
if len(attrs) == 0 {
return ""
}
result := ""
isFirst := true
for k, v := range attrs {
if isFirst {
isFirst = false
} else {
result += ","
}
result += graph.escape(k) + "=" + graph.escape(v) + ""
}
return "[" + result + "]"
}
func (graph *RouteGraph) escape(s string) string {
result, err := json.Marshal(s)
if err != nil {
return err.Error()
} else {
return string(result)
}
}
func (graph *RouteGraph) AddEdge(src string, dest string, label string, attrs RouteAttrs) {
// Add edges with same src/dest separately, multiple edges with same src/dest could exist
edge := RouteEdgeKey{
src: src,
dest: dest,
}
newValue, exists := graph.edges[edge]
if !exists {
newValue = makeRouteEdgeValue()
}
if len(label) != 0 {
newValue.label = append(newValue.label, label)
}
for k, v := range attrs {
newValue.attrs[k] = v
}
graph.edges[edge] = newValue
}
func (graph *RouteGraph) AddPoint(name string, performLookup bool, attrs RouteAttrs) {
newValue, exists := graph.points[name]
if !exists {
newValue = makeRoutePoint()
}
newValue.performLookup = performLookup
for k, v := range attrs {
newValue.attrs[k] = v
}
graph.points[name] = newValue
}
func (graph *RouteGraph) GetEdge(src string, dest string) *RouteEdgeValue {
key := RouteEdgeKey{
src: src,
dest: dest,
}
value, ok := graph.edges[key]
if ok {
return &value
} else {
return nil
}
}
func (graph *RouteGraph) GetPoint(name string) *RoutePoint {
value, ok := graph.points[name]
if ok {
return &value
} else {
return nil
}
}
func (graph *RouteGraph) ToGraphviz() string {
var result string
asnCache := make(ASNCache)
for name, value := range graph.points {
var representation string
if value.performLookup {
representation = asnCache.Lookup(name)
} else {
representation = name
}
attrsCopy := value.attrs
if attrsCopy == nil {
attrsCopy = make(RouteAttrs)
}
attrsCopy["label"] = representation
result += fmt.Sprintf("%s %s;\n", graph.escape(name), graph.attrsToString(value.attrs))
}
for key, value := range graph.edges {
attrsCopy := value.attrs
if attrsCopy == nil {
attrsCopy = make(RouteAttrs)
}
if len(value.label) > 0 {
attrsCopy["label"] = strings.Join(value.label, "\n")
}
result += fmt.Sprintf("%s -> %s %s;\n", graph.escape(key.src), graph.escape(key.dest), graph.attrsToString(attrsCopy))
}
return "digraph {\n" + result + "}\n"
}

View File

@@ -1,84 +1,23 @@
package main
import (
"io/ioutil"
"path"
"runtime"
"strings"
"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)
setting.dnsInterface = "asn.cymru.com"
setting.whoisServer = ""
result := getASNRepresentation("6939")
if !strings.Contains(result, "HURRICANE") {
t.Errorf("Lookup AS6939 failed, got %s", result)
}
}
func TestGetASNRepresentationWhois(t *testing.T) {
checkNetwork(t)
setting.dnsInterface = ""
setting.whoisServer = "whois.arin.net"
result := getASNRepresentation("6939")
if !strings.Contains(result, "HURRICANE") {
t.Errorf("Lookup AS6939 failed, got %s", result)
}
}
func TestGetASNRepresentationFallback(t *testing.T) {
setting.dnsInterface = ""
setting.whoisServer = ""
result := getASNRepresentation("6939")
if result != "AS6939" {
t.Errorf("Lookup AS6939 failed, got %s", result)
}
}
// Broken due to random order of attributes
func TestBirdRouteToGraphviz(t *testing.T) {
setting.dnsInterface = ""
// Don't change formatting of the following strings!
fakeResult := `192.168.0.1/32 unicast [alpha 2021-01-14 from 192.168.0.2] * (100) [AS12345i]
via 192.168.0.2 on eth0
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242422601
BGP.next_hop: 172.18.0.2`
expectedLinesInResult := []string{
`"AS4242422601" [`,
`"AS4242422601" -> "Target: 192.168.0.1" [`,
`"Target: 192.168.0.1" [`,
`"alpha" [`,
`"alpha" -> "AS4242422601" [`,
}
result := birdRouteToGraphviz([]string{
"alpha",
}, []string{
fakeResult,
}, "192.168.0.1")
for _, line := range expectedLinesInResult {
if !strings.Contains(result, line) {
t.Errorf("Expected line in result not found: %s", line)
}
func readDataFile(t *testing.T, filename string) string {
_, sourceName, _, _ := runtime.Caller(0)
projectRoot := path.Join(path.Dir(sourceName), "..")
dir := path.Join(projectRoot, filename)
data, err := ioutil.ReadFile(dir)
if err != nil {
t.Fatal(err)
}
return string(data)
}
func TestBirdRouteToGraphvizXSS(t *testing.T) {
@@ -98,3 +37,48 @@ func TestBirdRouteToGraphvizXSS(t *testing.T) {
t.Errorf("XSS injection succeeded: %s", result)
}
}
func TestBirdRouteToGraph(t *testing.T) {
setting.dnsInterface = ""
input := readDataFile(t, "frontend/test_data/bgpmap_case1.txt")
result := birdRouteToGraph([]string{"node"}, []string{input}, "target")
// Source node must exist
if result.GetPoint("node") == nil {
t.Error("Result doesn't contain point node")
}
// Last hop must exist
if result.GetPoint("4242423914") == nil {
t.Error("Result doesn't contain point 4242423914")
}
// Destination must exist
if result.GetPoint("target") == nil {
t.Error("Result doesn't contain point target")
}
// Verify that a few paths exist
if result.GetEdge("node", "4242423914") == nil {
t.Error("Result doesn't contain edge from node to 4242423914")
}
if result.GetEdge("node", "4242422688") == nil {
t.Error("Result doesn't contain edge from node to 4242422688")
}
if result.GetEdge("4242422688", "4242423914") == nil {
t.Error("Result doesn't contain edge from 4242422688 to 4242423914")
}
if result.GetEdge("4242423914", "target") == nil {
t.Error("Result doesn't contain edge from 4242423914 to target")
}
}
func TestBirdRouteToGraphviz(t *testing.T) {
setting.dnsInterface = ""
input := readDataFile(t, "frontend/test_data/bgpmap_case1.txt")
result := birdRouteToGraphviz([]string{"node"}, []string{input}, "target")
if !strings.Contains(result, "digraph {") {
t.Error("Response is not Graphviz data")
}
}

View File

@@ -65,7 +65,7 @@ func shortenWhoisFilter(whois string) string {
shouldSkip := false
shouldSkip = shouldSkip || len(s) == 0
shouldSkip = shouldSkip || len(s) > 0 && s[0] == '#'
shouldSkip = shouldSkip || strings.Contains(strings.ToUpper(s), "REDACTED FOR PRIVACY")
shouldSkip = shouldSkip || strings.Contains(strings.ToUpper(s), "REDACTED")
if shouldSkip {
skippedLinesLonger++

View File

@@ -28,3 +28,79 @@ func TestDN42WhoisFilterUnneeded(t *testing.T) {
t.Errorf("Output doesn't match expected: %s", result)
}
}
func TestShortenWhoisFilterShorterMode(t *testing.T) {
input := `
Information line that will be removed
# Comment that will be removed
Name: Redacted for privacy
Descr: This is a vvvvvvvvvvvvvvvvvvvvvvveeeeeeeeeeeeeeeeeeeerrrrrrrrrrrrrrrrrrrrrrrryyyyyyyyyyyyyyyyyyy long line that will be skipped.
Looooooooooooooooooooooong key: this line will be skipped.
Preserved1: this line isn't removed.
Preserved2: this line isn't removed.
Preserved3: this line isn't removed.
Preserved4: this line isn't removed.
Preserved5: this line isn't removed.
`
result := shortenWhoisFilter(input)
expectedResult := `Preserved1: this line isn't removed.
Preserved2: this line isn't removed.
Preserved3: this line isn't removed.
Preserved4: this line isn't removed.
Preserved5: this line isn't removed.
3 line(s) skipped.
`
if result != expectedResult {
t.Errorf("Output doesn't match expected: %s", result)
}
}
func TestShortenWhoisFilterLongerMode(t *testing.T) {
input := `
Information line that will be removed
# Comment that will be removed
Name: Redacted for privacy
Descr: This is a vvvvvvvvvvvvvvvvvvvvvvveeeeeeeeeeeeeeeeeeeerrrrrrrrrrrrrrrrrrrrrrrryyyyyyyyyyyyyyyyyyy long line that will be skipped.
Looooooooooooooooooooooong key: this line will be skipped.
Preserved1: this line isn't removed.
`
result := shortenWhoisFilter(input)
expectedResult := `Information line that will be removed
Descr: This is a vvvvvvvvvvvvvvvvvvvvvvveeeeeeeeeeeeeeeeeeeerrrrrrrrrrrrrrrrrrrrrrrryyyyyyyyyyyyyyyyyyy long line that will be skipped.
Looooooooooooooooooooooong key: this line will be skipped.
Preserved1: this line isn't removed.
7 line(s) skipped.
`
if result != expectedResult {
t.Errorf("Output doesn't match expected: %s", result)
}
}
func TestShortenWhoisFilterSkipNothing(t *testing.T) {
input := `Preserved1: this line isn't removed.
Preserved2: this line isn't removed.
Preserved3: this line isn't removed.
Preserved4: this line isn't removed.
Preserved5: this line isn't removed.
`
result := shortenWhoisFilter(input)
if result != input {
t.Errorf("Output doesn't match expected: %s", result)
}
}

View File

@@ -1,10 +1,33 @@
module github.com/xddxdd/bird-lg-go/frontend
go 1.16
go 1.17
require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/gorilla/handlers v1.5.2
github.com/jarcoal/httpmock v1.3.1
github.com/magiconair/properties v1.8.7
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.18.2
)
require (
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/gorilla/handlers v1.5.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.14.0
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.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
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/sys v0.15.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
)

File diff suppressed because it is too large Load Diff

View File

@@ -2,11 +2,14 @@ package main
import (
"io"
"net"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/jarcoal/httpmock"
)
type channelData struct {
@@ -14,6 +17,29 @@ type channelData struct {
data string
}
func createConnectionTimeoutRoundTripper(timeout int) http.RoundTripper {
context := net.Dialer{
Timeout: time.Duration(timeout) * time.Second,
}
// Prefer httpmock's transport if activated, so unit tests can work
if http.DefaultTransport == httpmock.DefaultTransport {
return httpmock.DefaultTransport
}
return &http.Transport{
DialContext: context.DialContext,
// Default options from transport.go
Proxy: http.ProxyFromEnvironment,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
}
// Send commands to lgproxy instances in parallel, and retrieve their responses
func batchRequest(servers []string, endpoint string, command string) []string {
// Channel and array for storing responses
@@ -47,7 +73,10 @@ func batchRequest(servers []string, endpoint string, command string) []string {
}
url := "http://" + hostname + ":" + strconv.Itoa(setting.proxyPort) + "/" + url.PathEscape(endpoint) + "?q=" + url.QueryEscape(command)
go func(url string, i int) {
client := http.Client{Timeout: time.Duration(setting.timeOut) * time.Second}
client := http.Client{
Transport: createConnectionTimeoutRoundTripper(setting.connectionTimeOut),
Timeout: time.Duration(setting.timeOut) * time.Second,
}
response, err := client.Get(url)
if err != nil {
ch <- channelData{i, "request failed: " + err.Error() + "\n"}
@@ -56,8 +85,8 @@ func batchRequest(servers []string, endpoint string, command string) []string {
buf := make([]byte, 65536)
n, err := io.ReadFull(response.Body, buf)
if err != nil && err != io.ErrUnexpectedEOF {
ch <- channelData{i, err.Error()}
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
ch <- channelData{i, "request failed: " + err.Error()}
} else {
ch <- channelData{i, string(buf[:n])}
}

163
frontend/lgproxy_test.go Normal file
View File

@@ -0,0 +1,163 @@
package main
import (
"errors"
"strings"
"testing"
"github.com/jarcoal/httpmock"
)
func TestBatchRequestIPv4(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
httpResponse := httpmock.NewStringResponder(200, "Mock Result")
httpmock.RegisterResponder("GET", "http://1.1.1.1:8000/mock?q=cmd", httpResponse)
httpmock.RegisterResponder("GET", "http://2.2.2.2:8000/mock?q=cmd", httpResponse)
httpmock.RegisterResponder("GET", "http://3.3.3.3:8000/mock?q=cmd", httpResponse)
setting.servers = []string{
"1.1.1.1",
"2.2.2.2",
"3.3.3.3",
}
setting.domain = ""
setting.proxyPort = 8000
response := batchRequest(setting.servers, "mock", "cmd")
if len(response) != 3 {
t.Error("Did not get response of all three mock servers")
}
for i := 0; i < len(response); i++ {
if response[i] != "Mock Result" {
t.Error("HTTP response mismatch")
}
}
}
func TestBatchRequestIPv6(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
httpResponse := httpmock.NewStringResponder(200, "Mock Result")
httpmock.RegisterResponder("GET", "http://[2001:db8::1]:8000/mock?q=cmd", httpResponse)
httpmock.RegisterResponder("GET", "http://[2001:db8::2]:8000/mock?q=cmd", httpResponse)
httpmock.RegisterResponder("GET", "http://[2001:db8::3]:8000/mock?q=cmd", httpResponse)
setting.servers = []string{
"2001:db8::1",
"2001:db8::2",
"2001:db8::3",
}
setting.domain = ""
setting.proxyPort = 8000
response := batchRequest(setting.servers, "mock", "cmd")
if len(response) != 3 {
t.Error("Did not get response of all three mock servers")
}
for i := 0; i < len(response); i++ {
if response[i] != "Mock Result" {
t.Error("HTTP response mismatch")
}
}
}
func TestBatchRequestEmptyResponse(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
httpResponse := httpmock.NewStringResponder(200, "")
httpmock.RegisterResponder("GET", "http://alpha:8000/mock?q=cmd", httpResponse)
httpmock.RegisterResponder("GET", "http://beta:8000/mock?q=cmd", httpResponse)
httpmock.RegisterResponder("GET", "http://gamma:8000/mock?q=cmd", httpResponse)
setting.servers = []string{
"alpha",
"beta",
"gamma",
}
setting.domain = ""
setting.proxyPort = 8000
response := batchRequest(setting.servers, "mock", "cmd")
if len(response) != 3 {
t.Error("Did not get response of all three mock servers")
}
for i := 0; i < len(response); i++ {
if !strings.Contains(response[i], "node returned empty response") {
t.Error("Did not produce error for empty response")
}
}
}
func TestBatchRequestDomainSuffix(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
httpResponse := httpmock.NewStringResponder(200, "Mock Result")
httpmock.RegisterResponder("GET", "http://alpha.suffix:8000/mock?q=cmd", httpResponse)
httpmock.RegisterResponder("GET", "http://beta.suffix:8000/mock?q=cmd", httpResponse)
httpmock.RegisterResponder("GET", "http://gamma.suffix:8000/mock?q=cmd", httpResponse)
setting.servers = []string{
"alpha",
"beta",
"gamma",
}
setting.domain = "suffix"
setting.proxyPort = 8000
response := batchRequest(setting.servers, "mock", "cmd")
if len(response) != 3 {
t.Error("Did not get response of all three mock servers")
}
for i := 0; i < len(response); i++ {
if response[i] != "Mock Result" {
t.Error("HTTP response mismatch")
}
}
}
func TestBatchRequestHTTPError(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
httpError := httpmock.NewErrorResponder(errors.New("Oops!"))
httpmock.RegisterResponder("GET", "http://alpha:8000/mock?q=cmd", httpError)
httpmock.RegisterResponder("GET", "http://beta:8000/mock?q=cmd", httpError)
httpmock.RegisterResponder("GET", "http://gamma:8000/mock?q=cmd", httpError)
setting.servers = []string{
"alpha",
"beta",
"gamma",
}
setting.domain = ""
setting.proxyPort = 8000
response := batchRequest(setting.servers, "mock", "cmd")
if len(response) != 3 {
t.Error("Did not get response of all three mock servers")
}
for i := 0; i < len(response); i++ {
if !strings.Contains(response[i], "request failed") {
t.Error("Did not produce HTTP error")
}
}
}
func TestBatchRequestInvalidServer(t *testing.T) {
setting.servers = []string{}
setting.domain = ""
setting.proxyPort = 8000
response := batchRequest([]string{"invalid"}, "mock", "cmd")
if len(response) != 1 {
t.Error("Did not get response of all mock servers")
}
if !strings.Contains(response[0], "invalid server") {
t.Error("Did not produce invalid server error")
}
}

View File

@@ -7,24 +7,25 @@ import (
)
type settingType struct {
servers []string
serversDisplay []string
domain string
proxyPort int
whoisServer string
listen string
dnsInterface string
netSpecificMode string
titleBrand string
navBarBrand string
navBarBrandURL string
navBarAllServer string
navBarAllURL string
bgpmapInfo string
telegramBotName string
protocolFilter []string
nameFilter string
timeOut int
servers []string
serversDisplay []string
domain string
proxyPort int
whoisServer string
listen string
dnsInterface string
netSpecificMode string
titleBrand string
navBarBrand string
navBarBrandURL string
navBarAllServer string
navBarAllURL string
bgpmapInfo string
telegramBotName string
protocolFilter []string
nameFilter string
timeOut int
connectionTimeOut int
}
var setting settingType

View File

@@ -193,6 +193,11 @@ func summaryParse(data string, serverName string) (TemplateSummary, error) {
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
args.Rows = append(args.Rows, row)
}

View File

@@ -8,6 +8,17 @@ import (
"testing"
)
const BirdSummaryData = `BIRD 2.0.8 ready.
Name Proto Table State Since Info
static1 Static master4 up 2021-08-27
static2 Static master6 up 2021-08-27
device1 Device --- up 2021-08-27
kernel1 Kernel master6 up 2021-08-27
kernel2 Kernel master4 up 2021-08-27
direct1 Direct --- up 2021-08-27
int_babel Babel --- up 2021-08-27
`
func initSettings() {
setting.servers = []string{"alpha"}
setting.serversDisplay = []string{"alpha"}
@@ -101,17 +112,8 @@ func TestSummaryTableXSS(t *testing.T) {
func TestSummaryTableProtocolFilter(t *testing.T) {
initSettings()
setting.protocolFilter = []string{"Static", "Direct", "Babel"}
data := `BIRD 2.0.8 ready.
Name Proto Table State Since Info
static1 Static master4 up 2021-08-27
static2 Static master6 up 2021-08-27
device1 Device --- up 2021-08-27
kernel1 Kernel master6 up 2021-08-27
kernel2 Kernel master4 up 2021-08-27
direct1 Direct --- up 2021-08-27
int_babel Babel --- up 2021-08-27 `
result := string(summaryTable(data, "testserver"))
result := string(summaryTable(BirdSummaryData, "testserver"))
expectedInclude := []string{"static1", "static2", "int_babel", "direct1"}
expectedExclude := []string{"device1", "kernel1", "kernel2"}
@@ -134,17 +136,8 @@ int_babel Babel --- up 2021-08-27 `
func TestSummaryTableNameFilter(t *testing.T) {
initSettings()
setting.nameFilter = "^static"
data := `BIRD 2.0.8 ready.
Name Proto Table State Since Info
static1 Static master4 up 2021-08-27
static2 Static master6 up 2021-08-27
device1 Device --- up 2021-08-27
kernel1 Kernel master6 up 2021-08-27
kernel2 Kernel master4 up 2021-08-27
direct1 Direct --- up 2021-08-27
int_babel Babel --- up 2021-08-27 `
result := string(summaryTable(data, "testserver"))
result := string(summaryTable(BirdSummaryData, "testserver"))
expectedInclude := []string{"device1", "kernel1", "kernel2", "direct1", "int_babel"}
expectedExclude := []string{"static1", "static2"}

View File

@@ -9,23 +9,24 @@ import (
)
type viperSettingType struct {
Servers string `mapstructure:"servers"`
Domain string `mapstructure:"domain"`
ProxyPort int `mapstructure:"proxy_port"`
WhoisServer string `mapstructure:"whois"`
Listen string `mapstructure:"listen"`
DNSInterface string `mapstructure:"dns_interface"`
NetSpecificMode string `mapstructure:"net_specific_mode"`
TitleBrand string `mapstructure:"title_brand"`
NavBarBrand string `mapstructure:"navbar_brand"`
NavBarBrandURL string `mapstructure:"navbar_brand_url"`
NavBarAllServer string `mapstructure:"navbar_all_servers"`
NavBarAllURL string `mapstructure:"navbar_all_url"`
BgpmapInfo string `mapstructure:"bgpmap_info"`
TelegramBotName string `mapstructure:"telegram_bot_name"`
ProtocolFilter string `mapstructure:"protocol_filter"`
NameFilter string `mapstructure:"name_filter"`
TimeOut int `mapstructure:"timeout"`
Servers string `mapstructure:"servers"`
Domain string `mapstructure:"domain"`
ProxyPort int `mapstructure:"proxy_port"`
WhoisServer string `mapstructure:"whois"`
Listen string `mapstructure:"listen"`
DNSInterface string `mapstructure:"dns_interface"`
NetSpecificMode string `mapstructure:"net_specific_mode"`
TitleBrand string `mapstructure:"title_brand"`
NavBarBrand string `mapstructure:"navbar_brand"`
NavBarBrandURL string `mapstructure:"navbar_brand_url"`
NavBarAllServer string `mapstructure:"navbar_all_servers"`
NavBarAllURL string `mapstructure:"navbar_all_url"`
BgpmapInfo string `mapstructure:"bgpmap_info"`
TelegramBotName string `mapstructure:"telegram_bot_name"`
ProtocolFilter string `mapstructure:"protocol_filter"`
NameFilter string `mapstructure:"name_filter"`
TimeOut int `mapstructure:"timeout"`
ConnectionTimeOut int `mapstructure:"connection_timeout"`
}
// Parse settings with viper, and convert to legacy setting format
@@ -33,6 +34,7 @@ func parseSettings() {
viper.AddConfigPath(".")
viper.AddConfigPath("/etc/bird-lg")
viper.SetConfigName("bird-lg")
viper.AllowEmptyEnv(true)
viper.AutomaticEnv()
viper.SetEnvPrefix("birdlg")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_", ".", "_"))
@@ -86,9 +88,12 @@ func parseSettings() {
pflag.String("name-filter", "", "protocol name regex to hide in summary tables (RE2 syntax); defaults to none if not set")
viper.BindPFlag("name_filter", pflag.Lookup("name-filter"))
pflag.Int("time-out", 120, "time before request timed out, in seconds; defaults to 120 if not set")
pflag.Int("time-out", 120, "time before backend HTTP request times out, in seconds; defaults to 120 if not set")
viper.BindPFlag("timeout", pflag.Lookup("time-out"))
pflag.Int("connection-time-out", 5, "time before backend TCP connection times out, in seconds; defaults to 5 if not set")
viper.BindPFlag("connection_timeout", pflag.Lookup("connection-time-out"))
pflag.Parse()
if err := viper.ReadInConfig(); err != nil {
@@ -138,6 +143,7 @@ func parseSettings() {
setting.nameFilter = viperSettings.NameFilter
setting.timeOut = viperSettings.TimeOut
setting.connectionTimeOut = viperSettings.ConnectionTimeOut
fmt.Printf("%#v\n", setting)
}

View File

@@ -0,0 +1,8 @@
package main
import "testing"
func TestParseSettings(t *testing.T) {
parseSettings()
// Good as long as it doesn't panic
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@ package main
import (
"embed"
"html/template"
"net/url"
"strings"
)
@@ -104,6 +105,12 @@ var requiredTemplates = [...]string{
"bird",
}
// define functions to be made available in templates
var funcMap = template.FuncMap{
"pathescape": url.PathEscape,
}
// import templates from embedded assets
func ImportTemplates() {
@@ -121,7 +128,7 @@ func ImportTemplates() {
}
// and add it to the template library
template, err := template.New(tmpl).Parse(string(def))
template, err := template.New(tmpl).Funcs(funcMap).Parse(string(def))
if err != nil {
panic("Unable to parse template (" + TEMPLATE_PATH + tmpl + ": " + err.Error())
}

25
frontend/template_test.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import (
"testing"
"github.com/magiconair/properties/assert"
)
func TestSummaryRowDataNameHasPrefix(t *testing.T) {
data := SummaryRowData{
Name: "mock",
}
assert.Equal(t, data.NameHasPrefix("m"), true)
assert.Equal(t, data.NameHasPrefix("n"), false)
}
func TestSummaryRowDataNameContains(t *testing.T) {
data := SummaryRowData{
Name: "mock",
}
assert.Equal(t, data.NameContains("oc"), true)
assert.Equal(t, data.NameContains("no"), false)
}

View File

@@ -0,0 +1,151 @@
Table master4:
172.20.0.53/32 unicast [ibgp_sjc2 2023-04-29 from fd86:bad:11b7:22::1] * (100/38) [AS4242423914i]
via 169.254.108.122 on igp-sjc2
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242423914
BGP.next_hop: 172.20.229.122
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,1) (64511,24) (64511,34)
BGP.large_community: (4242421080, 101, 44) (4242421080, 103, 122) (4242421080, 104, 1)
unicast [miaotony_2688 2023-04-29 from fe80::2688] (100) [AS4242423914i]
via 172.23.6.6 on dn42las-miaoton
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242422688 4242423914
BGP.next_hop: 172.23.6.6
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,3) (64511,24) (64511,34)
BGP.large_community: (4242421080, 104, 1) (4242421080, 101, 44) (4242421080, 103, 126)
unicast [imlonghao_1888 2023-04-17] (100) [AS4242423914i]
via fe80::1888 on dn42-imlonghao
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242421888 4242423914
BGP.next_hop: :: fe80::1888
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,1) (64511,24) (64511,34)
BGP.large_community: (4242421080, 104, 1) (4242421080, 101, 44) (4242421080, 103, 126)
unicast [ciplc_3021 2023-04-29 from fe80::943e] (100) [AS4242423914i]
via 172.23.33.161 on dn42-ciplc
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242423021 4242423914
BGP.next_hop: 172.23.33.161
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,1) (64511,24) (64511,34)
BGP.large_community: (4242421080, 104, 1) (4242421080, 101, 44) (4242421080, 103, 126)
unicast [iedon_2189 2023-04-29 from fe80::2189:ef] (100) [AS4242423914i]
via 172.23.91.114 on dn42-iedon
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242422189 4242423914
BGP.next_hop: 172.23.91.114
BGP.med: 65
BGP.local_pref: 100
BGP.community: (64511,24) (64511,33) (64511,3)
BGP.large_community: (4242422189, 1, 4) (4242421080, 104, 1) (4242421080, 101, 44) (4242421080, 103, 126)
unicast [prevarinite_2475 2023-04-19] (100) [AS4242423914i]
via fe80::7072:6576:6172:1 on dn42-prevarinit
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242422475 4242423192 4242423914
BGP.next_hop: :: fe80::7072:6576:6172:1
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,1) (64511,24) (64511,34)
BGP.large_community: (4242421080, 104, 1) (4242421080, 101, 44) (4242421080, 103, 126)
unicast [lare_3035 2023-04-29] (100) [AS4242423914i]
via fe80::3035:132 on dn42-lare
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242423035 4242423914
BGP.next_hop: :: fe80::3035:132
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,3) (64511,34) (64511,24)
BGP.large_community: (4242421080, 104, 1) (4242421080, 101, 44) (4242421080, 103, 126)
unicast [hinata_3724 2023-04-29 from fe80::3724] (100) [AS4242423914i]
via 172.23.215.228 on dn42las-hinata
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242423724 4201271111 4242423914
BGP.next_hop: 172.23.215.228
BGP.med: 70
BGP.local_pref: 100
BGP.community: (64511,22) (64511,1) (64511,34)
BGP.large_community: (4242421080, 104, 1) (4242421080, 101, 44) (4242421080, 103, 126)
unicast [liki4_0927 2023-04-21] (100) [AS4242423914i]
via fe80::927 on dn42-liki4
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242420927 4242421888 4242423914
BGP.next_hop: :: fe80::927
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,2) (64511,24) (64511,34)
BGP.large_community: (4242421080, 104, 1) (4242421080, 101, 44) (4242421080, 103, 126)
unicast [eastbound_2633 2023-04-29 from fe80::2633] (100) [AS4242423914i]
via 172.23.250.42 on dn42las-eastbnd
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242422633 4242423914
BGP.next_hop: 172.23.250.42
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,24) (64511,34) (64511,3)
BGP.large_community: (4242422633, 101, 44) (4242422633, 103, 36) (4242421080, 104, 1) (4242421080, 101, 44) (4242421080, 103, 126)
unicast [yura_2464 2023-04-29] (100) [AS4242423914i]
via fe80::2464 on dn42las-yura
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242422464 4242423914
BGP.next_hop: :: fe80::2464
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,1) (64511,24) (64511,34)
BGP.large_community: (4242422464, 2, 4242423914) (4242422464, 64511, 44) (4242422464, 64511, 1840) (4242421080, 104, 1) (4242421080, 101, 44) (4242421080, 103, 126)
unicast [ibgp_fra 2023-04-29 from fd86:bad:11b7:117::1] (100/186) [AS4242423914i]
via 169.254.108.113 on igp-chi
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242423914
BGP.next_hop: 172.20.229.117
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,1) (64511,24) (64511,34)
BGP.large_community: (4242421080, 101, 41) (4242421080, 103, 117) (4242421080, 104, 3)
unicast [ibgp_sgp 2023-04-29 from fd86:bad:11b7:239::1] (100/200) [AS4242423914i]
via 169.254.108.39 on igp-sgp
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242423914
BGP.next_hop: 172.22.108.39
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,4) (64511,24) (64511,34)
BGP.large_community: (4242421080, 101, 51) (4242421080, 103, 39) (4242421080, 104, 4)
unicast [ibgp_ymq 2023-04-30 from fd86:bad:11b7:23::1] (100/105) [AS4242423914i]
via 169.254.108.113 on igp-chi
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242423914
BGP.next_hop: 172.20.229.123
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,3) (64511,24) (64511,34)
BGP.large_community: (4242421080, 101, 42) (4242421080, 103, 123) (4242421080, 104, 2)
unicast [cola_3391 18:41:16.608 from fe80::3391] (100) [AS4242423914i]
via 172.22.96.65 on dn42-cola
Type: BGP univ
BGP.origin: IGP
BGP.as_path: 4242423391 4242420604 4242423914
BGP.next_hop: 172.22.96.65
BGP.med: 50
BGP.local_pref: 100
BGP.community: (64511,4) (64511,34) (64511,24)
BGP.large_community: (4242420604, 2, 50) (4242420604, 501, 4242423914) (4242420604, 502, 44) (4242420604, 504, 4) (4242421080, 104, 1) (4242421080, 101, 44) (4242421080, 103, 126)

View File

@@ -0,0 +1,89 @@
package main
import (
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"github.com/jarcoal/httpmock"
"github.com/magiconair/properties/assert"
)
func TestServerError(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/error", nil)
w := httptest.NewRecorder()
serverError(w, r)
assert.Equal(t, w.Code, http.StatusInternalServerError)
}
func TestWebHandlerWhois(t *testing.T) {
server := WhoisServer{
t: t,
expectedQuery: "AS6939",
response: AS6939Response,
}
server.Listen()
go server.Run()
defer server.Close()
setting.netSpecificMode = ""
setting.whoisServer = server.server.Addr().String()
r := httptest.NewRequest(http.MethodGet, "/whois/AS6939", nil)
w := httptest.NewRecorder()
webHandlerWhois(w, r)
assert.Equal(t, w.Code, http.StatusOK)
if !strings.Contains(w.Body.String(), "HURRICANE") {
t.Error("Body does not contain whois result")
}
}
func TestWebBackendCommunicator(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
input := readDataFile(t, "frontend/test_data/bgpmap_case1.txt")
httpResponse := httpmock.NewStringResponder(200, input)
httpmock.RegisterResponder("GET", "http://alpha:8000/bird?q="+url.QueryEscape("show route for 1.1.1.1 all"), httpResponse)
setting.servers = []string{"alpha"}
setting.domain = ""
setting.proxyPort = 8000
setting.dnsInterface = ""
setting.whoisServer = ""
r := httptest.NewRequest(http.MethodGet, "/route_bgpmap/alpha/1.1.1.1", nil)
w := httptest.NewRecorder()
handler := webBackendCommunicator("bird", "route_all")
handler(w, r)
assert.Equal(t, w.Code, http.StatusOK)
}
func TestWebHandlerBGPMap(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
input := readDataFile(t, "frontend/test_data/bgpmap_case1.txt")
httpResponse := httpmock.NewStringResponder(200, input)
httpmock.RegisterResponder("GET", "http://alpha:8000/bird?q="+url.QueryEscape("show route for 1.1.1.1 all"), httpResponse)
setting.servers = []string{"alpha"}
setting.domain = ""
setting.proxyPort = 8000
setting.dnsInterface = ""
setting.whoisServer = ""
r := httptest.NewRequest(http.MethodGet, "/route_bgpmap/alpha/1.1.1.1", nil)
w := httptest.NewRecorder()
handler := webHandlerBGPMap("bird", "route_bgpmap")
handler(w, r)
assert.Equal(t, w.Code, http.StatusOK)
}

Some files were not shown because too many files have changed in this diff Show More