Compare commits

...

79 Commits
output ... main

Author SHA1 Message Date
github-actions
c1bb9b848c Update README.md with new tag v0.0.5-20250413040820 2025-04-13 04:08:21 +00:00
spiritlhl
d975fe6906 fix: 修复目标地址 2025-04-13 03:47:38 +00:00
spiritlhl
c550861ce5
fix: 修复json解析 2025-04-13 11:24:58 +08:00
github-actions[bot]
2d03ca1281 chore: 更新多个 ASN 的 IPv6 前缀 2025-04-13 02:38:26 +00:00
github-actions
d58a45dc56 Update README.md with new tag v0.0.5-20250411152044
Some checks failed
创建IPv6检测的前缀 / fetch-ipv6-prefixes (push) Has been cancelled
2025-04-11 15:20:45 +00:00
spiritlhl
3995e1a1bd
fix: 修复说明的tag自动修改 2025-04-11 23:19:39 +08:00
spiritlhl
a2a95e0444 fix: 修改说明,目前已支持IPV6路由检测 2025-04-11 15:14:53 +00:00
spiritlhl
fa1da298d7 fix: 减少日志输出,避免挤占日志 2025-04-11 15:05:03 +00:00
spiritlhl
9c58bb35cd fix: 减少日志输出,避免挤占日志 2025-04-11 15:03:40 +00:00
spiritlhl
b3be692d7e fix: 减少日志输出,避免挤占日志 2025-04-11 14:35:04 +00:00
spiritlhl
6f0fc98154 fix: 修复备用地址启用的逻辑 2025-04-11 14:34:13 +00:00
spiritlhl
a268e0b48b fix: 更新函数说明 2025-04-11 14:17:28 +00:00
spiritlhl
af5dd4eb3d fix: 依赖更新 2025-04-11 14:16:00 +00:00
spiritlhl
211c724509 fix: 请求的缓存结果移动并复用 2025-04-11 14:15:20 +00:00
spiritlhl
0c7727e3dc fix: 修复可能存在的重复请求问题 2025-04-11 14:07:13 +00:00
spiritlhl
2adf709e63 feat: 添加远程备用地址的相关逻辑 2025-04-11 13:54:12 +00:00
spiritlhl
aad93e51f8 feat: 结构化IPV4和IPV6的代码 2025-04-11 13:41:09 +00:00
spiritlhl
a832d354da feat: 结构化IPV4和IPV6的代码 2025-04-11 13:35:35 +00:00
spiritlhl
f1ccde0ea9 feat: 更新todo需求 2025-04-08 14:29:25 +00:00
spiritlhl
4caf507831 fix: 对齐V4和V6的输出 2025-04-08 14:22:56 +00:00
spiritlhl
ddc2f9b401 fix: 减少无效日志输出,对齐V4和V6的输出 2025-04-08 14:18:20 +00:00
spiritlhl
93434aeda1 fix: 修复IPV6包头识别 2025-04-08 14:05:47 +00:00
spiritlhl
341c21f1e5 fix: 进一步添加日志记录 2025-04-08 13:54:11 +00:00
spiritlhl
fcc0403612 fix: 添加ipv6的hop日志记录 2025-04-08 13:37:54 +00:00
spiritlhl
466c8dbe5d fix: 添加hop日志记录 2025-04-08 13:15:22 +00:00
spiritlhl
16484ea6ca
fix: 对齐输出 2025-04-06 21:22:01 +08:00
spiritlhl
ba10f8b64c fix: 修复ipv6日志输出 2025-04-06 13:20:24 +00:00
spiritlhl
09abb17c08 fix: 修复路径错误 2025-04-06 13:14:23 +00:00
spiritlhl
aa8bc941a6 feat: 添加IPV6初始化 2025-04-06 13:07:38 +00:00
spiritlhl
3b19d43eef feat: 添加IPV6初始化 2025-04-06 13:01:37 +00:00
spiritlhl
c9c62e86f3 feat: 添加测试模式,方便开发调试 2025-04-06 12:51:53 +00:00
spiritlhl
1a985cfbbe fix: 修复IPV6目标地址 2025-04-06 10:49:46 +00:00
spiritlhl
9dc08856e0
fix: 致谢添加 https://github.com/spiritLHLS/icmp_targets 2025-04-06 18:29:37 +08:00
github-actions[bot]
4c546a4239 chore: 更新多个 ASN 的 IPv6 前缀 2025-04-06 10:26:24 +00:00
spiritlhl
560b9e441e feat: 添加IPV6前缀判断的逻辑 2025-04-06 10:24:44 +00:00
spiritlhl
5b416aa419 fix: 修复IPV6包构建 2025-04-05 08:52:42 +00:00
spiritlhl
b93b8fbb4a feat: 添加IPV6路由追踪包的构建逻辑 2025-04-05 05:13:56 +00:00
spiritlhl
7a644b403e feat: 添加IPV6路由追踪包的构建逻辑 2025-04-05 03:48:50 +00:00
spiritlhl
7efc61cbe4 feat: 尝试添加IPV6路由追踪 2025-04-05 03:24:04 +00:00
spiritlhl
0c6b11883f
feat: 添加参考资料 2025-04-05 11:09:27 +08:00
spiritlhl
4f9603bee7 feat: 修复错误命名 2025-04-05 02:56:13 +00:00
spiritlhl
509f6a3dd6 feat: 支持CTG线路检测,优化系统适配性添加占位符 2025-04-05 02:52:00 +00:00
spiritlhl
8d69825c32 fix: CTG测不了一点,和CN2GIA差别不大 2025-04-05 02:26:31 +00:00
spiritlhl
153787b112 fix: 修复Hits徽章访问量统计,使用 https://github.com/oneclickvirt/hitscounter 2025-03-29 07:52:14 +00:00
spiritlhl
7b1ebd3cc7
feat: 修改TODO待添加的功能 2025-03-28 22:52:31 +08:00
GitHub Action Bot
7531a2a1c6 删除 README.md 中的 gitsponsors 徽章 2025-03-06 05:44:40 +01:00
spiritlhl
68f9d70d05
换行显示方便批量替换 2025-01-23 22:43:32 +08:00
spiritlhl
d97de33b68
Update README.md 2025-01-21 09:18:09 +08:00
spiritlhl
a64be2bb74
Update README.md 2025-01-21 09:17:33 +08:00
spiritlhl
ee429b012b
更新说明 2024-08-06 23:29:10 +08:00
spiritlhl
f578626489
Update README.md 2024-08-03 23:30:49 +08:00
spiritlhl
fae17eb727
更新避免WIN下自动关闭窗口 2024-07-28 21:38:05 +08:00
spiritlhl
091df1603d
Update README.md 2024-07-20 10:13:13 +08:00
spiritlhl
90905a19eb update 2024-07-02 14:06:23 +00:00
spiritlhl
9488f3cec7 update 2024-07-02 14:02:45 +00:00
spiritlhl
9a43a89d39
Update backtrace_install.sh 2024-06-28 15:49:18 +08:00
spiritlhl
76e4c09d37
Update README.md 2024-06-28 15:48:31 +08:00
spiritlhl
310b09e95b v0.0.4 - 增加不显示IP信息的参数 2024-06-24 09:02:28 +00:00
spiritlhl
9c658fb118 v0.0.3 - 增加多平台编译测试部分 2024-06-24 08:18:01 +00:00
spiritlhl
29c7c81f9a 0.0.2 - 增加日志记录的参数 2024-06-24 08:01:58 +00:00
spiritlhl
20ee739ddb
Update README.md 2024-06-24 13:30:51 +08:00
spiritlhl
cdab03d8c0 update 2024-06-24 05:25:43 +00:00
spiritlhl
985b09e60e 更新golang版本 2024-06-24 05:24:38 +00:00
spiritlhl
b3ce155391 update 2024-06-24 05:17:54 +00:00
spiritlhl
c99e477cd5 update 2024-06-24 05:07:48 +00:00
spiritlhl
bc36b6b030
增加北京上海CMN2的IP前缀 2024-06-02 00:27:23 +08:00
spiritlhl
ad2c9a714b
更新CMIN2的IP前缀 2024-06-01 20:49:25 +08:00
spiritlhl
94fc3a8d56
Update README.md 2024-05-22 00:20:03 +08:00
spiritlhl
7bc4772536 修复CMIN2识别的问题 2024-05-21 16:15:44 +00:00
spiritlhl
36d4abf98b
Update backtrace_install.sh 2024-05-21 16:10:20 +08:00
spiritlhl
f03b799501
更新说明 2024-05-18 12:47:27 +08:00
spiritlhl
d9e950ddaa
Update README.md 2024-05-18 12:46:33 +08:00
spiritlhl
47dcc4ce11 update 2024-05-18 03:00:03 +00:00
spiritlhl
dad36f7139 更新北京电信北京联通的IP地址 2024-05-18 01:41:43 +00:00
spiritlhl
40d13dd083
Update README.md 2024-05-15 18:07:42 +08:00
spiritlhl
21a31d69c6
Update README.md 2024-05-12 19:28:52 +08:00
spiritlhl
f07427bf03 update 2024-05-02 04:39:22 +00:00
spiritlhl
e1bd13a6ff
Update README.md 2024-05-02 11:32:46 +08:00
spiritlhl
ae40e9c6a4
Update backtrace_install.sh 2024-05-02 11:24:11 +08:00
36 changed files with 3556 additions and 502 deletions

48
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,48 @@
name: 创建IPv6检测的前缀
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0'
jobs:
fetch-ipv6-prefixes:
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: 获取并处理多个ASN的IPv6前缀
run: |
mkdir -p bk/prefix/
for asn in AS4809 AS4134 AS9929 AS4837 AS58807 AS9808 AS58453 AS23764; do
echo "处理 $asn..."
curl -s -A "Mozilla/5.0" \
"https://bgp.he.net/$asn" > "${asn}.html"
grep -oE '[0-9a-f:]+::/[0-9]+' "${asn}.html" | sort -u > tmp_prefixes.txt
{
while read prefix; do
ip_part=$(echo "$prefix" | cut -d/ -f1)
prefix_len=$(echo "$prefix" | cut -d/ -f2)
keep_segments=$((prefix_len / 16))
segments=$(echo "$ip_part" | tr ':' '\n' | grep -v '^$')
kept=$(echo "$segments" | head -n "$keep_segments" | tr '\n' ':' | sed 's/:$//')
echo "$kept"
done < tmp_prefixes.txt
} | sort -u > "bk/prefix/${asn,,}.txt"
rm -f "${asn}.html" tmp_prefixes.txt
done
- name: 提交更新到仓库
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add bk/prefix/*.txt
if git diff --cached --quiet; then
echo "无变更,跳过提交。"
else
git commit -m "chore: 更新多个 ASN 的 IPv6 前缀"
git push
fi

51
.github/workflows/ci.yaml vendored Normal file
View File

@ -0,0 +1,51 @@
name: CI
on:
workflow_dispatch:
jobs:
test:
strategy:
matrix:
go: ['1.22.x']
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go }}
- name: Test
run: go test ./... -coverprofile=coverage.txt
- name: Create Tag
if: success()
run: |
git config --global user.name 'github-actions'
git config --global user.email 'github-actions@github.com'
TAG="v0.0.5-$(date +'%Y%m%d%H%M%S')"
git tag $TAG
git push origin $TAG
echo "TAG=$TAG" >> $GITHUB_ENV
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update README.md
if: success()
run: |
sed -i "s|go get github.com/oneclickvirt/backtrace@.*|go get github.com/oneclickvirt/backtrace@${TAG}|" README.md
env:
TAG: ${{ env.TAG }}
- name: Commit and Push README.md
if: success()
run: |
git add README.md
git commit -m "Update README.md with new tag ${TAG}"
git push origin main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -36,7 +36,7 @@ jobs:
echo "Deleting asset with ID: $asset" echo "Deleting asset with ID: $asset"
curl -X DELETE -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "https://api.github.com/repos/oneclickvirt/backtrace/releases/assets/$asset" curl -X DELETE -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" "https://api.github.com/repos/oneclickvirt/backtrace/releases/assets/$asset"
done done
sleep 60 sleep 30
release-binary: release-binary:
name: Release Go Binary name: Release Go Binary

View File

@ -1,30 +1,81 @@
# backtrace # backtrace
[![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Foneclickvirt%2Fbacktrace&count_bg=%2323E01C&title_bg=%23555555&icon=sonarcloud.svg&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com) [![Build and Release](https://github.com/oneclickvirt/backtrace/actions/workflows/main.yaml/badge.svg)](https://github.com/oneclickvirt/backtrace/actions/workflows/main.yaml) [![Hits](https://hits.spiritlhl.net/backtrace.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false)](https://hits.spiritlhl.net)
[![Build and Release](https://github.com/oneclickvirt/backtrace/actions/workflows/main.yaml/badge.svg)](https://github.com/oneclickvirt/backtrace/actions/workflows/main.yaml)
三网回程路由线路测试 三网回程路由线路测试
基于 https://github.com/zhanghanyun/backtrace 的重构和优化,与原版存在很大不同 路由的线路判断最终还是得人工判断的才准确,本项目测试结果仅供参考
## 功能 ## 功能
- [x] 检测回程显示IPV4地址时的线路不显示IP地址时显示ASN检测不到原版[backtrace](https://github.com/zhanghanyun/backtrace)也支持 - [x] 检测回程显示IPV4/IPV6地址时的线路(使用1500字节的包)不显示IP地址时显示ASN检测不到
- [x] 支持对```4837```、```9929```和```163```线路的判断,原版[backtrace](https://github.com/zhanghanyun/backtrace)也支持 - [x] 支持对```9929```、```4837```和```163```线路的判断
- [x] 支持对```CN2GT```和```CN2GIA```线路的判断,原版[backtrace](https://github.com/zhanghanyun/backtrace)不支持 - [x] 支持对```CTGNET```、```CN2GIA```和```CN2GT```线路的判断
- [x] 支持对```CMIN2```和```CMI```线路的判断,原版[backtrace](https://github.com/zhanghanyun/backtrace)不支持 - [x] 支持对```CMIN2```和```CMI```线路的判断
- [x] 支持对整个回程路由进行线路分析,与原版[backtrace](https://github.com/zhanghanyun/backtrace)仅进行一次判断不同 - [x] 支持对整个回程路由进行线路分析一个目标IP可能会分析出多种线路
- [x] 修复原版[backtrace](https://github.com/zhanghanyun/backtrace)对IPV4地址信息获取时json解析失败依然打印信息的问题
- [x] 增加对全平台的编译支持,原版[backtrace](https://github.com/zhanghanyun/backtrace)仅支持linux平台的amd64和arm64架构 - [x] 增加对全平台的编译支持,原版[backtrace](https://github.com/zhanghanyun/backtrace)仅支持linux平台的amd64和arm64架构
- [x] 兼容额外的ICMP地址获取若当前目标IP无法查询路由尝试额外的IP地址
## TODO
- [ ] 自动检测汇聚层,裁剪结果不输出汇聚层后的线路(区分境内外段)
- [ ] 添加对主流ISP的POP点检测区分国际互联能力
## 使用 ## 使用
更新时间2024.05.02 下载、安装、更新
```shell ```shell
curl https://raw.githubusercontent.com/oneclickvirt/backtrace/main/backtrace_install.sh -sSf | sh curl https://raw.githubusercontent.com/oneclickvirt/backtrace/main/backtrace_install.sh -sSf | bash
``` ```
更多架构请查看 https://github.com/oneclickvirt/backtrace/releases/tag/output
```
curl https://cdn.spiritlhl.net/https://raw.githubusercontent.com/oneclickvirt/backtrace/main/backtrace_install.sh -sSf | bash
```
使用
```
backtrace
```
```
./backtrace
```
进行测试
无环境依赖,理论上适配所有系统和主流架构,更多架构请查看 https://github.com/oneclickvirt/backtrace/releases/tag/output
```
Usage: backtrace [options]
-h Show help information
-ipv6
Enable ipv6 testing
-log
Enable logging
-s Disabe show ip info (default true)
-v Show version
```
## 卸载
```
rm -rf /root/backtrace
rm -rf /usr/bin/backtrace
```
## 在Golang中使用
```
go get github.com/oneclickvirt/backtrace@v0.0.5-20250413040820
```
## 概览图 ## 概览图
@ -33,3 +84,9 @@ curl https://raw.githubusercontent.com/oneclickvirt/backtrace/main/backtrace_ins
![图片](https://github.com/oneclickvirt/backtrace/assets/103393591/2812a47d-4e6b-4091-9bb9-596af6c3c8bc) ![图片](https://github.com/oneclickvirt/backtrace/assets/103393591/2812a47d-4e6b-4091-9bb9-596af6c3c8bc)
![图片](https://github.com/oneclickvirt/backtrace/assets/103393591/2e5cc625-e0da-41ff-85ff-9d21c01114a3) ![图片](https://github.com/oneclickvirt/backtrace/assets/103393591/2e5cc625-e0da-41ff-85ff-9d21c01114a3)
## Thanks
部分代码基于 https://github.com/zhanghanyun/backtrace 的重构和优化,与原版存在很大不同
IPV4/IPV6可ICMP进行ping测试的 https://github.com/spiritLHLS/icmp_targets 收集仓库

4
back/README.md Normal file
View File

@ -0,0 +1,4 @@
# 部分参考资料
https://blog.sunflyer.cn/archives/594

View File

@ -1,157 +0,0 @@
package backtrace
import (
"fmt"
. "github.com/oneclickvirt/backtrace/defaultset"
"net"
"strings"
)
type Result struct {
i int
s string
}
var (
ips = []string{
"219.141.136.12", "202.106.50.1", "221.179.155.161",
"202.96.209.133", "210.22.97.1", "211.136.112.200",
"58.60.188.222", "210.21.196.6", "120.196.165.24",
"61.139.2.69", "119.6.6.6", "211.137.96.205",
}
names = []string{
"北京电信", "北京联通", "北京移动",
"上海电信", "上海联通", "上海移动",
"广州电信", "广州联通", "广州移动",
"成都电信", "成都联通", "成都移动",
}
m = map[string]string{
// [] 前的字符串个数中文占2个字符串
"AS4809a": "电信CN2GIA [精品线路]",
"AS4809b": "电信CN2GT [优质线路]",
"AS4134": "电信163 [普通线路]",
"AS9929": "联通9929 [优质线路]",
"AS4837": "联通4837 [普通线路]",
"AS58807": "移动CMIN2 [精品线路]",
"AS9808": "移动CMI [普通线路]",
"AS58453": "移动CMI [普通线路]",
}
)
func removeDuplicates(elements []string) []string {
encountered := map[string]bool{} // 用于存储已经遇到的元素
result := []string{} // 存储去重后的结果
for v := range elements { // 遍历切片中的元素
if encountered[elements[v]] == true { // 如果该元素已经遇到过
// 存在过就不加入了
} else {
encountered[elements[v]] = true // 将该元素标记为已经遇到
result = append(result, elements[v]) // 将该元素加入到结果切片中
}
}
return result // 返回去重后的结果切片
}
func trace(ch chan Result, i int, cmin2 []string) {
hops, err := Trace(net.ParseIP(ips[i]))
if err != nil {
s := fmt.Sprintf("%v %-15s %v", names[i], ips[i], err)
ch <- Result{i, s}
return
}
var asns []string
for _, h := range hops {
for _, n := range h.Nodes {
asn := ipAsn(n.IP.String(), cmin2)
if asn != "" {
asns = append(asns, asn)
}
}
}
// 处理CN2不同路线的区别
if asns != nil && len(asns) > 0 {
var tempText string
asns = removeDuplicates(asns)
tempText += fmt.Sprintf("%v ", names[i])
hasAS4134 := false
hasAS4809 := false
for _, asn := range asns {
if asn == "AS4134" {
hasAS4134 = true
}
if asn == "AS4809" {
hasAS4809 = true
}
}
if hasAS4134 && hasAS4809 {
// 同时包含 AS4134 和 AS4809 属于 CN2GT
asns = append(asns, "AS4809b")
} else if hasAS4809 {
// 仅包含 AS4809 属于 CN2GIA
asns = append(asns, "AS4809a")
}
tempText += fmt.Sprintf("%-15s ", ips[i])
for _, asn := range asns {
asnDescription := m[asn]
switch asn {
case "":
continue
case "AS4809": // 被 AS4809a 和 AS4809b 替代了
continue
case "AS9929":
if !strings.Contains(tempText, asnDescription) {
tempText += DarkGreen(asnDescription) + " "
}
case "AS4809a":
if !strings.Contains(tempText, asnDescription) {
tempText += DarkGreen(asnDescription) + " "
}
case "AS4809b":
if !strings.Contains(tempText, asnDescription) {
tempText += Green(asnDescription) + " "
}
case "AS58807":
if !strings.Contains(tempText, asnDescription) {
tempText += Green(asnDescription) + " "
}
default:
if !strings.Contains(tempText, asnDescription) {
tempText += White(asnDescription) + " "
}
}
}
if tempText == (fmt.Sprintf("%v ", names[i]) + fmt.Sprintf("%-15s ", ips[i])) {
tempText += fmt.Sprintf("%v", Red("检测不到已知线路的ASN"))
}
ch <- Result{i, tempText}
} else {
s := fmt.Sprintf("%v %-15s %v", names[i], ips[i], Red("检测不到回程路由节点的IP地址"))
ch <- Result{i, s}
}
}
func ipAsn(ip string, cmin2 []string) string {
switch {
case strings.HasPrefix(ip, "59.43"):
return "AS4809"
case strings.HasPrefix(ip, "202.97"):
return "AS4134"
case strings.HasPrefix(ip, "218.105") || strings.HasPrefix(ip, "210.51"):
return "AS9929"
case strings.HasPrefix(ip, "219.158"):
return "AS4837"
case strings.HasPrefix(ip, "223.118") || strings.HasPrefix(ip, "223.119") ||
strings.HasPrefix(ip, "223.120"):
for _, prefix := range cmin2 {
if strings.HasPrefix(ip, prefix) {
return "AS58807"
}
}
return "AS58453"
case strings.HasPrefix(ip, "103.11.109") || strings.HasPrefix(ip, "45.204.69") ||
strings.HasPrefix(ip, "223.121"):
return "AS58453"
default:
return ""
}
}

View File

@ -1,83 +0,0 @@
package backtrace
import (
"encoding/json"
"fmt"
"log"
"net/http"
"time"
. "github.com/oneclickvirt/backtrace/defaultset"
)
type IpInfo struct {
Ip string `json:"ip"`
City string `json:"city"`
Region string `json:"region"`
Country string `json:"country"`
Org string `json:"org"`
}
func BackTrace() {
var (
s [12]string // 对应 ips 目标地址数量
c = make(chan Result)
t = time.After(time.Second * 10)
cmin2 = []string{
// 以下均为 /24 地址
"223.118.32",
"223.119.32", "223.119.34", "223.119.35", "223.119.36", "223.119.37", "223.119.100", "223.119.253",
"223.120.165"}
prefixes = []string{
"223.119.8.0/21",
"223.120.128.0/17",
"223.120.134/23",
"223.120.136/23",
"223.120.138/23",
"223.120.154/23",
"223.120.158/23",
"223.120.164/22",
"223.120.168/22",
"223.120.172/22",
"223.120.174/23",
"223.120.184/22",
"223.120.188/22",
"223.120.192/23",
"223.120.200/23",
"223.120.210/23",
"223.120.212/23",
}
)
// 生成CMIN2的IPV4前缀地址
for _, prefix := range prefixes {
cmin2 = append(cmin2, GeneratePrefixList(prefix)...)
}
rsp, err := http.Get("http://ipinfo.io")
if err != nil {
log.Fatalln("Get ip info err", err)
} else {
info := IpInfo{}
err = json.NewDecoder(rsp.Body).Decode(&info)
if err != nil {
log.Fatalln("json decode err", err)
} else {
fmt.Println(Green("国家: ") + White(info.Country) + Green(" 城市: ") + White(info.City) +
Green(" 服务商: ") + Blue(info.Org))
}
}
for i := range ips {
go trace(c, i, cmin2)
}
loop:
for range s {
select {
case o := <-c:
s[o.i] = o.s
case <-t:
break loop
}
}
for _, r := range s {
fmt.Println(r)
}
}

View File

@ -1,26 +0,0 @@
package backtrace
import (
"net"
"syscall"
)
func (t *Tracer) listen(network string, laddr *net.IPAddr) (*net.IPConn, error) {
conn, err := net.ListenIP(network, laddr)
if err != nil {
return nil, err
}
raw, err := conn.SyscallConn()
if err != nil {
conn.Close()
return nil, err
}
_ = raw.Control(func(fd uintptr) {
err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1)
})
if err != nil {
conn.Close()
return nil, err
}
return conn, nil
}

View File

@ -1,26 +0,0 @@
package backtrace
import (
"net"
"syscall"
)
func (t *Tracer) listen(network string, laddr *net.IPAddr) (*net.IPConn, error) {
conn, err := net.ListenIP(network, laddr)
if err != nil {
return nil, err
}
raw, err := conn.SyscallConn()
if err != nil {
conn.Close()
return nil, err
}
_ = raw.Control(func(fd uintptr) {
err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1)
})
if err != nil {
conn.Close()
return nil, err
}
return conn, nil
}

View File

@ -1,22 +1,46 @@
#!/bin/bash #!/bin/bash
#From https://github.com/oneclickvirt/backtrace #From https://github.com/oneclickvirt/backtrace
#2024.05.02 #2024.07.02
rm -rf /usr/bin/backtrace rm -rf /usr/bin/backtrace
os=$(uname -s) os=$(uname -s)
arch=$(uname -m) arch=$(uname -m)
check_cdn() {
local o_url=$1
for cdn_url in "${cdn_urls[@]}"; do
if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then
export cdn_success_url="$cdn_url"
return
fi
sleep 0.5
done
export cdn_success_url=""
}
check_cdn_file() {
check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test"
if [ -n "$cdn_success_url" ]; then
echo "CDN available, using CDN"
else
echo "No CDN available, no use CDN"
fi
}
cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn3.spiritlhl.net/" "http://cdn1.spiritlhl.net/" "http://cdn2.spiritlhl.net/")
check_cdn_file
case $os in case $os in
Linux) Linux)
case $arch in case $arch in
"x86_64" | "x86" | "amd64" | "x64") "x86_64" | "x86" | "amd64" | "x64")
wget -O backtrace https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-linux-amd64 wget -O backtrace "${cdn_success_url}https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-linux-amd64"
;; ;;
"i386" | "i686") "i386" | "i686")
wget -O backtrace https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-linux-386 wget -O backtrace "${cdn_success_url}https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-linux-386"
;; ;;
"armv7l" | "armv8" | "armv8l" | "aarch64") "armv7l" | "armv8" | "armv8l" | "aarch64" | "arm64")
wget -O backtrace https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-linux-arm64 wget -O backtrace "${cdn_success_url}https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-linux-arm64"
;; ;;
*) *)
echo "Unsupported architecture: $arch" echo "Unsupported architecture: $arch"
@ -27,13 +51,13 @@ case $os in
Darwin) Darwin)
case $arch in case $arch in
"x86_64" | "x86" | "amd64" | "x64") "x86_64" | "x86" | "amd64" | "x64")
wget -O backtrace https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-darwin-amd64 wget -O backtrace "${cdn_success_url}https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-darwin-amd64"
;; ;;
"i386" | "i686") "i386" | "i686")
wget -O backtrace https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-darwin-386 wget -O backtrace "${cdn_success_url}https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-darwin-386"
;; ;;
"armv7l" | "armv8" | "armv8l" | "aarch64") "armv7l" | "armv8" | "armv8l" | "aarch64" | "arm64")
wget -O backtrace https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-darwin-arm64 wget -O backtrace "${cdn_success_url}https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-darwin-arm64"
;; ;;
*) *)
echo "Unsupported architecture: $arch" echo "Unsupported architecture: $arch"
@ -44,13 +68,13 @@ case $os in
FreeBSD) FreeBSD)
case $arch in case $arch in
amd64) amd64)
wget -O backtrace https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-freebsd-amd64 wget -O backtrace "${cdn_success_url}https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-freebsd-amd64"
;; ;;
"i386" | "i686") "i386" | "i686")
wget -O backtrace https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-freebsd-386 wget -O backtrace "${cdn_success_url}https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-freebsd-386"
;; ;;
"armv7l" | "armv8" | "armv8l" | "aarch64") "armv7l" | "armv8" | "armv8l" | "aarch64" | "arm64")
wget -O backtrace https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-freebsd-arm64 wget -O backtrace "${cdn_success_url}https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-freebsd-arm64"
;; ;;
*) *)
echo "Unsupported architecture: $arch" echo "Unsupported architecture: $arch"
@ -61,13 +85,13 @@ case $os in
OpenBSD) OpenBSD)
case $arch in case $arch in
amd64) amd64)
wget -O backtrace https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-openbsd-amd64 wget -O backtrace "${cdn_success_url}https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-openbsd-amd64"
;; ;;
"i386" | "i686") "i386" | "i686")
wget -O backtrace https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-openbsd-386 wget -O backtrace "${cdn_success_url}https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-openbsd-386"
;; ;;
"armv7l" | "armv8" | "armv8l" | "aarch64") "armv7l" | "armv8" | "armv8l" | "aarch64" | "arm64")
wget -O backtrace https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-openbsd-arm64 wget -O backtrace "${cdn_success_url}https://github.com/oneclickvirt/backtrace/releases/download/output/backtrace-openbsd-arm64"
;; ;;
*) *)
echo "Unsupported architecture: $arch" echo "Unsupported architecture: $arch"
@ -82,9 +106,4 @@ case $os in
esac esac
chmod 777 backtrace chmod 777 backtrace
if [ !-f /usr/bin/backtrace ]; then cp backtrace /usr/bin/backtrace
mv backtrace /usr/bin/
backtrace
else
./backtrace
fi

77
bk/backtrace.go Normal file
View File

@ -0,0 +1,77 @@
package backtrace
import (
"fmt"
"time"
"github.com/oneclickvirt/backtrace/model"
)
func BackTrace(enableIpv6 bool) {
if model.CachedIcmpData == "" || model.ParsedIcmpTargets == nil || time.Since(model.CachedIcmpDataFetchTime) > time.Hour {
model.CachedIcmpData = getData(model.IcmpTargets)
model.CachedIcmpDataFetchTime = time.Now()
if model.CachedIcmpData != "" {
model.ParsedIcmpTargets = parseIcmpTargets(model.CachedIcmpData)
}
}
if enableIpv6 {
ipv4Count := len(model.Ipv4s)
ipv6Count := len(model.Ipv6s)
totalCount := ipv4Count + ipv6Count
var (
s = make([]string, totalCount)
c = make(chan Result)
t = time.After(time.Second * 10)
)
for i := range model.Ipv4s {
go trace(c, i)
}
for i := range model.Ipv6s {
go traceIPv6(c, i, ipv4Count)
}
loopIPv4v6:
for range s {
select {
case o := <-c:
s[o.i] = o.s
case <-t:
break loopIPv4v6
}
}
for i := 0; i < ipv4Count; i++ {
if s[i] != "" {
fmt.Println(s[i])
}
}
for i := ipv4Count; i < totalCount; i++ {
if s[i] != "" {
fmt.Println(s[i])
}
}
} else {
ipCount := len(model.Ipv4s)
var (
s = make([]string, ipCount)
c = make(chan Result)
t = time.After(time.Second * 10)
)
for i := range model.Ipv4s {
go trace(c, i)
}
loopIPv4:
for range s {
select {
case o := <-c:
s[o.i] = o.s
case <-t:
break loopIPv4
}
}
for _, r := range s {
if r != "" {
fmt.Println(r)
}
}
}
}

View File

@ -16,5 +16,5 @@ import (
//} //}
func TestBackTrace(t *testing.T) { func TestBackTrace(t *testing.T) {
BackTrace() BackTrace(false)
} }

31
bk/ipv4_asn.go Normal file
View File

@ -0,0 +1,31 @@
package backtrace
import (
"strings"
)
func ipv4Asn(ip string) string {
if strings.Contains(ip, ":") {
return ipv6Asn(ip)
}
switch {
case strings.HasPrefix(ip, "59.43"):
return "AS4809"
case strings.HasPrefix(ip, "202.97"):
return "AS4134"
case strings.HasPrefix(ip, "218.105") || strings.HasPrefix(ip, "210.51"):
return "AS9929"
case strings.HasPrefix(ip, "219.158"):
return "AS4837"
case strings.HasPrefix(ip, "223.120.19") || strings.HasPrefix(ip, "223.120.17") || strings.HasPrefix(ip, "223.120.16") ||
strings.HasPrefix(ip, "223.120.140") || strings.HasPrefix(ip, "223.120.130") || strings.HasPrefix(ip, "223.120.131") ||
strings.HasPrefix(ip, "223.120.141"):
return "AS58807"
case strings.HasPrefix(ip, "223.118") || strings.HasPrefix(ip, "223.119") || strings.HasPrefix(ip, "223.120") || strings.HasPrefix(ip, "223.121"):
return "AS58453"
case strings.HasPrefix(ip, "69.194") || strings.HasPrefix(ip, "203.22"):
return "AS23764"
default:
return ""
}
}

59
bk/ipv6_asn.go Normal file
View File

@ -0,0 +1,59 @@
package backtrace
import (
"strings"
_ "embed"
)
//go:embed prefix/as4809.txt
var as4809Data string
//go:embed prefix/as4134.txt
var as4134Data string
//go:embed prefix/as9929.txt
var as9929Data string
//go:embed prefix/as4837.txt
var as4837Data string
//go:embed prefix/as58807.txt
var as58807Data string
//go:embed prefix/as9808.txt
var as9808Data string
//go:embed prefix/as58453.txt
var as58453Data string
//go:embed prefix/as23764.txt
var as23764Data string
// ASN -> Prefix strings
var asnPrefixes = map[string][]string{
"AS4809": strings.Split(as4809Data, "\n"), // 电信 CN2 GT/GIA
"AS4134": strings.Split(as4134Data, "\n"), // 电信 163 骨干网
"AS9929": strings.Split(as9929Data, "\n"), // 联通 9929 优质国际线路
"AS4837": strings.Split(as4837Data, "\n"), // 联通 AS4837 普通国际线路
"AS58807": strings.Split(as58807Data, "\n"), // 移动 CMIN2 国际精品网
"AS9808": strings.Split(as9808Data, "\n"), // 移动 CMI中国移动国际公司
"AS58453": strings.Split(as58453Data, "\n"), // 移动国际互联网CMI/HK
"AS23764": strings.Split(as23764Data, "\n"), // 电信 CTGNET/国际出口可能是CN2-B
}
// 判断 IPv6 地址是否匹配 ASN 中的某个前缀
func ipv6Asn(ip string) string {
ip = strings.ToLower(ip)
for asn, prefixes := range asnPrefixes {
for _, prefix := range prefixes {
prefix = strings.TrimSpace(prefix)
if prefix == "" {
continue
}
if strings.HasPrefix(ip, prefix) {
return asn
}
}
}
return ""
}

View File

@ -1,17 +1,29 @@
//go:build linux || freebsd || openbsd || darwin
// +build linux freebsd openbsd darwin
package backtrace package backtrace
import ( import (
"net" "net"
"syscall" "syscall"
"github.com/oneclickvirt/backtrace/model"
. "github.com/oneclickvirt/defaultset"
) )
func (t *Tracer) listen(network string, laddr *net.IPAddr) (*net.IPConn, error) { func (t *Tracer) listen(network string, laddr *net.IPAddr) (*net.IPConn, error) {
conn, err := net.ListenIP(network, laddr) conn, err := net.ListenIP(network, laddr)
if err != nil { if err != nil {
if model.EnableLoger {
Logger.Info(err.Error())
}
return nil, err return nil, err
} }
raw, err := conn.SyscallConn() raw, err := conn.SyscallConn()
if err != nil { if err != nil {
if model.EnableLoger {
Logger.Info(err.Error())
}
conn.Close() conn.Close()
return nil, err return nil, err
} }
@ -19,6 +31,9 @@ func (t *Tracer) listen(network string, laddr *net.IPAddr) (*net.IPConn, error)
err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1) err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1)
}) })
if err != nil { if err != nil {
if model.EnableLoger {
Logger.Info(err.Error())
}
conn.Close() conn.Close()
return nil, err return nil, err
} }

View File

@ -1,17 +1,33 @@
//go:build !linux && !freebsd && !openbsd && !darwin && !windows
// +build !linux,!freebsd,!openbsd,!darwin,!windows
package backtrace package backtrace
import ( import (
"net" "net"
"syscall" "syscall"
"github.com/oneclickvirt/backtrace/model"
. "github.com/oneclickvirt/defaultset"
) )
func (t *Tracer) listen(network string, laddr *net.IPAddr) (*net.IPConn, error) { func (t *Tracer) listen(network string, laddr *net.IPAddr) (*net.IPConn, error) {
if model.EnableLoger {
InitLogger()
defer Logger.Sync()
}
conn, err := net.ListenIP(network, laddr) conn, err := net.ListenIP(network, laddr)
if err != nil { if err != nil {
if model.EnableLoger {
Logger.Info(err.Error())
}
return nil, err return nil, err
} }
raw, err := conn.SyscallConn() raw, err := conn.SyscallConn()
if err != nil { if err != nil {
if model.EnableLoger {
Logger.Info(err.Error())
}
conn.Close() conn.Close()
return nil, err return nil, err
} }
@ -19,6 +35,9 @@ func (t *Tracer) listen(network string, laddr *net.IPAddr) (*net.IPConn, error)
err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1) err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1)
}) })
if err != nil { if err != nil {
if model.EnableLoger {
Logger.Info(err.Error())
}
conn.Close() conn.Close()
return nil, err return nil, err
} }

View File

@ -4,22 +4,30 @@
package backtrace package backtrace
import ( import (
. "github.com/oneclickvirt/backtrace/defaultset"
"golang.org/x/sys/windows"
"net" "net"
"github.com/oneclickvirt/backtrace/model"
. "github.com/oneclickvirt/defaultset"
"golang.org/x/sys/windows"
) )
func (t *Tracer) listen(network string, laddr *net.IPAddr) (*net.IPConn, error) { func (t *Tracer) listen(network string, laddr *net.IPAddr) (*net.IPConn, error) {
if model.EnableLoger {
InitLogger() InitLogger()
defer Logger.Sync() defer Logger.Sync()
}
conn, err := net.ListenIP(network, laddr) conn, err := net.ListenIP(network, laddr)
if err != nil { if err != nil {
if model.EnableLoger {
Logger.Info(err.Error()) Logger.Info(err.Error())
}
return nil, err return nil, err
} }
raw, err := conn.SyscallConn() raw, err := conn.SyscallConn()
if err != nil { if err != nil {
if model.EnableLoger {
Logger.Info(err.Error()) Logger.Info(err.Error())
}
conn.Close() conn.Close()
return nil, err return nil, err
} }
@ -27,7 +35,9 @@ func (t *Tracer) listen(network string, laddr *net.IPAddr) (*net.IPConn, error)
err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, windows.IP_HDRINCL, 1) err = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, windows.IP_HDRINCL, 1)
}) })
if err != nil { if err != nil {
if model.EnableLoger {
Logger.Info(err.Error()) Logger.Info(err.Error())
}
conn.Close() conn.Close()
return nil, err return nil, err
} }

23
bk/prefix/as23764.txt Normal file
View File

@ -0,0 +1,23 @@
2400:9380:9115
2400:9380:9116
2400:9380:9206
2400:9380:9262
2400:9380:a003
2400:9380:a00a
2400:9380:a00c
2400:9380:a01f
2400:9380:a022
2400:9380:a026
2400:9380:a028
2400:9380:a042
2400:9380:a110
2804:1e48:9003
2a04:f580:9001
2a04:f581:110a
2a04:f581:110b
2a04:f581:a123
2a04:f581:a125
2c0f:f7a8:1
2c0f:f7a8:29
2c0f:f7a8:37
2c0f:f7a8:47

223
bk/prefix/as4134.txt Normal file
View File

@ -0,0 +1,223 @@
2400:9380:8001
2400:9380:8003
2400:9380:8021
2400:9380:8030
2400:9380:8040
2400:9380:8140
2400:9380:8201
2400:9380:8221
2400:9380:8301
240e
240e:1
240e:100
240e:101
240e:103
240e:104
240e:106
240e:108
240e:11:8001
240e:12
240e:144
240e:16:1001
240e:16:1008
240e:16:1009
240e:184
240e:1c7
240e:2
240e:218
240e:219
240e:21a
240e:21b
240e:224
240e:225
240e:226
240e:227
240e:24
240e:240
240e:241
240e:242
240e:243
240e:244
240e:245
240e:246
240e:247
240e:2c
240e:318
240e:319
240e:31a
240e:31b
240e:324
240e:325
240e:326
240e:327
240e:340
240e:341
240e:342
240e:343
240e:350:205
240e:41
240e:418
240e:419
240e:41a
240e:41b
240e:42
240e:424
240e:425
240e:426
240e:427
240e:42:4000
240e:42:8010
240e:43
240e:43:8000
240e:44
240e:440
240e:441
240e:442
240e:443
240e:444
240e:445
240e:446
240e:447
240e:45
240e:45:8000
240e:46
240e:46:5008
240e:47
240e:47:4
240e:48
240e:49
240e:4a
240e:4b
240e:4b:2
240e:4c
240e:4c:4006
240e:4d
240e:4d:50ff
240e:4e
240e:4e:4000
240e:4f
240e:50
240e:51
240e:518
240e:519
240e:51a
240e:51b
240e:52
240e:524
240e:525
240e:526
240e:527
240e:53
240e:54
240e:540
240e:541
240e:542
240e:543
240e:544
240e:545
240e:546
240e:547
240e:55
240e:56
240e:57
240e:5a
240e:5b
240e:5c
240e:5d
240e:5e
240e:5f
240e:618
240e:619
240e:61a
240e:61b
240e:61d:1401
240e:624
240e:625
240e:626
240e:627
240e:638
240e:64
240e:640
240e:641
240e:642
240e:643
240e:659:1160
240e:7
240e:718
240e:719
240e:71a
240e:71b
240e:724
240e:725
240e:726
240e:727
240e:740
240e:741
240e:742
240e:743
240e:767
240e:840
240e:841
240e:9
240e:918
240e:919
240e:91a
240e:91b
240e:924
240e:925
240e:926
240e:927
240e:940
240e:941
240e:942
240e:943
240e:944
240e:945
240e:946
240e:947
240e:980
240e:981
240e:982
240e:983
240e:a0c
240e:a0d
240e:a0e
240e:a0f
240e:a18
240e:a19
240e:a1a
240e:a1b
240e:a24
240e:a25
240e:a26
240e:a27
240e:a40
240e:a41
240e:a42
240e:a43
240e:a44
240e:a45
240e:a46
240e:a47
240e:a5:8000
240e:b
240e:cd:8000
240e:d:100
2605:9d80:8001
2605:9d80:8011
2605:9d80:8021
2605:9d80:8031
2605:9d80:8041
2605:9d80:8081
2a04:f580:8010
2a04:f580:8011
2a04:f580:8090
2a04:f580:8210
2a04:f580:8211
2a04:f580:8290
2c0f:f7a8:8011
2c0f:f7a8:8050
2c0f:f7a8:805f
2c0f:f7a8:8150
2c0f:f7a8:815f
2c0f:f7a8:8211

160
bk/prefix/as4809.txt Normal file
View File

@ -0,0 +1,160 @@
2400:9380:9000
2400:9380:9001
2400:9380:9002
2400:9380:9005
2400:9380:9009
2400:9380:900a
2400:9380:9020
2400:9380:9021
2400:9380:9030
2400:9380:9031
2400:9380:9040
2400:9380:9041
2400:9380:9050
2400:9380:9051
2400:9380:9060
2400:9380:9061
2400:9380:9070
2400:9380:9071
2400:9380:9080
2400:9380:9081
2400:9380:90a0
2400:9380:90a1
2400:9380:90b0
2400:9380:90b1
2400:9380:90b2
2400:9380:90b3
2400:9380:90b4
2400:9380:90b5
2400:9380:90b6
2400:9380:90b7
2400:9380:9100
2400:9380:9101
2400:9380:9121
2400:9380:9201
2400:9380:9202
2400:9380:9205
2400:9380:9220
2400:9380:9221
2400:9380:9230
2400:9380:9231
2400:9380:9240
2400:9380:9241
2400:9380:9250
2400:9380:9251
2400:9380:9252
2400:9380:9260
2400:9380:9261
2400:9380:9270
2400:9380:9271
2400:9380:9272
2400:9380:9280
2400:9380:9281
2400:9380:9282
2400:9380:92a0
2400:9380:92a1
2400:9380:92b0
2400:9380:92b1
2400:9380:92b2
2400:9380:92b3
2400:9380:92b4
2400:9380:92b5
2400:9380:92b6
2400:9380:92b7
240e:182:5401
240e:182:5501
240e:409:9000
240e:409:9001
240e:410:ff00
240e:411:ff00
240e:414
240e:43d:fff1
240e:43d:fff3
240e:43d:fff4
240e:43d:fff7
240e:440:ac00
240e:440:ac01
240e:440:ac02
240e:441:ac00
240e:441:ac01
240e:441:ac02
240e:445:3f00
240e:446:3f00
240e:451:bfc0
240e:451:bfe0
240e:451:ffa0
240e:451:ffc0
240e:451:ffe0
240e:456:fe00
240e:457:fe00
240e:469:f400
240e:476:febf
240e:476:feff
240e:604:314
240e:60e:8000
240e:60e:8001
240e:615
240e:62c
240e:638:f
240e:63c
240e:640:178
240e:640:179
240e:640:17a
240e:640:17b
240e:648:1e
240e:64e
240e:659:f100
240e:669
240e:699:7a00
240e:699:7a01
240e:713:f020
240e:713:f021
240e:733:4c0
240e:f6:8002
2605:9d80:9003
2605:9d80:9013
2605:9d80:9023
2605:9d80:9033
2605:9d80:9042
2605:9d80:9052
2605:9d80:9061
2605:9d80:9071
2605:9d80:9092
2804:1e48
2804:1e48:9001
2804:1e48:9002
2a04:f580:9000
2a04:f580:9001
2a04:f580:9002
2a04:f580:9010
2a04:f580:9012
2a04:f580:9013
2a04:f580:9020
2a04:f580:9030
2a04:f580:9040
2a04:f580:9050
2a04:f580:9060
2a04:f580:9070
2a04:f580:9080
2a04:f580:9090
2a04:f580:9200
2a04:f580:9201
2a04:f580:9202
2a04:f580:9210
2a04:f580:9212
2a04:f580:9213
2a04:f580:9220
2a04:f580:9230
2a04:f580:9240
2a04:f580:9250
2a04:f580:9260
2a04:f580:9270
2a04:f580:9280
2a04:f580:9290
2c0f:f7a8:9010
2c0f:f7a8:9011
2c0f:f7a8:9020
2c0f:f7a8:9041
2c0f:f7a8:9210
2c0f:f7a8:9211
2c0f:f7a8:9220

414
bk/prefix/as4837.txt Normal file
View File

@ -0,0 +1,414 @@
2400:73e0:201
2402:f140:ff13
2402:f140:ff14
2404:6500:dcb3
2405:1480
2406:1e40
2406:cac0
2407:37c0:ffff
2407:6c40
2407:6c40:1500
2408
2408:8000
2408:8000:10fe
2408:8000:10ff
2408:8000:2
2408:8000:3
2408:8001
2408:802a
2408:802c
2408:8034
2408:803e
2408:8056
2408:80c2
2408:80c5
2408:80e2
2408:80e3
2408:80e9
2408:80f5
2408:80f9
2408:815f
2408:8181
2408:8182
2408:8183
2408:81a2
2408:81a3
2408:8210
2408:8211
2408:8212
2408:8213
2408:8214
2408:8215
2408:821a
2408:821b
2408:8220
2408:8221
2408:8226
2408:822a
2408:822b
2408:822e
2408:822f
2408:8230
2408:8231
2408:8232
2408:8233
2408:8234
2408:8235
2408:8236
2408:8237
2408:8238
2408:8239
2408:823c
2408:823d
2408:8240
2408:8248
2408:8249
2408:824a
2408:824b
2408:824c
2408:824e
2408:824f
2408:8250
2408:8251
2408:8252
2408:8253
2408:8254
2408:8255
2408:825c
2408:825d
2408:8260
2408:8262
2408:8263
2408:8264
2408:8265
2408:8266
2408:826a
2408:826c
2408:826d
2408:826e
2408:826f
2408:8270
2408:8274
2408:8275
2408:8276
2408:8277
2408:8278
2408:8279
2408:827a
2408:8310
2408:8311
2408:8312
2408:8313
2408:832a
2408:832e
2408:832f
2408:8330
2408:8331
2408:8332
2408:8333
2408:8338
2408:8340
2408:8348
2408:8349
2408:834a
2408:834b
2408:834e
2408:834f
2408:8350
2408:8351
2408:8352
2408:8353
2408:8354
2408:8355
2408:8360
2408:8361
2408:8362
2408:8363
2408:8364
2408:8365
2408:836c
2408:836d
2408:836e
2408:836f
2408:8374
2408:8375
2408:8376
2408:8377
2408:8378
2408:8379
2408:837a
2408:8410
2408:8411
2408:8412
2408:8413
2408:8414
2408:8415
2408:8417
2408:8418
2408:841a
2408:841b
2408:841c
2408:841d
2408:841e
2408:8420
2408:8421
2408:8422
2408:8426
2408:8427
2408:842a
2408:842b
2408:842c
2408:842e
2408:8431
2408:8434
2408:8435
2408:8436
2408:8437
2408:8438
2408:8439
2408:843c
2408:843d
2408:843e
2408:843f
2408:8440
2408:8441
2408:8448
2408:844b
2408:844c
2408:844d
2408:844e
2408:844f
2408:8452
2408:8453
2408:8454
2408:845c
2408:845d
2408:8460
2408:8461
2408:8462
2408:8463
2408:8464
2408:8465
2408:8466
2408:8469
2408:846a
2408:846b
2408:846c
2408:846d
2408:846e
2408:846f
2408:8470
2408:8471
2408:8474
2408:8475
2408:8476
2408:8477
2408:8478
2408:8479
2408:847a
2408:84e3
2408:84e4
2408:84e5
2408:84e6
2408:84e7
2408:84e9
2408:84eb
2408:84ec
2408:84ed
2408:84ee
2408:84ef
2408:84f0
2408:84f1
2408:84f2
2408:84f4
2408:84f5
2408:84f6
2408:84f7
2408:84f8
2408:84f9
2408:84fa
2408:84fb
2408:84fc
2408:84fd
2408:84fe
2408:84ff
2408:856c
2408:856d
2408:8610
2408:8611
2408:8612
2408:8613
2408:8614
2408:8615
2408:861a
2408:861b
2408:861c
2408:8620
2408:8621
2408:8624
2408:8625
2408:8626
2408:862a
2408:862b
2408:862d
2408:862e
2408:862f
2408:8630
2408:8631
2408:8632
2408:8633
2408:8634
2408:8635
2408:8636
2408:8637
2408:8638
2408:8639
2408:863c
2408:863d
2408:8640
2408:8642
2408:8648
2408:8649
2408:864c
2408:864e
2408:864f
2408:8650
2408:8651
2408:8652
2408:8653
2408:8654
2408:865c
2408:865d
2408:8660
2408:8662
2408:8663
2408:8664
2408:8665
2408:8666
2408:866a
2408:866b
2408:866c
2408:866d
2408:866e
2408:866f
2408:8670
2408:8674
2408:8675
2408:8676
2408:8677
2408:8678
2408:8679
2408:867a
2408:8710
2408:8711
2408:8712
2408:8713
2408:8719
2408:871a
2408:871b
2408:8720
2408:8721
2408:8722
2408:8723
2408:8726
2408:872b
2408:872f
2408:8730
2408:8731
2408:8732
2408:8733
2408:8734
2408:8735
2408:8736
2408:8738
2408:873c
2408:873d
2408:8740
2408:8742
2408:8748
2408:8749
2408:874a
2408:874b
2408:874c
2408:874d
2408:874e
2408:874f
2408:8752
2408:875c
2408:8760
2408:8762
2408:8763
2408:8764
2408:8765
2408:8766
2408:8768
2408:876a
2408:876c
2408:876d
2408:876e
2408:876f
2408:8770
2408:8772
2408:8773
2408:8774
2408:8776
2408:8777
2408:8778
2408:8779
2408:877a
2408:8812
2408:8813
2408:8814
2408:8815
2408:8816
2408:8817
2408:8818
2408:8819
2408:882c
2408:883a
2408:8862
2408:8863
2408:8864
2408:8865
2408:8866
2408:886e
2408:886f
2408:8872
2408:8878
2408:8879
2408:887e
2408:8912
2408:8913
2408:8914
2408:8915
2408:8916
2408:8917
2408:891c
2408:8920
2408:8924
2408:892c
2408:8936
2408:893a
2408:8940
2408:8948
2408:894c
2408:894e
2408:8962
2408:8963
2408:8964
2408:8965
2408:8966
2408:896c
2408:896e
2408:896f
2408:8972
2408:8978
2408:8979
2408:897a
2408:897b
2408:897e
2408:8a21
2408:8a23
2408:8a24
2408:8a26
2408:8a27

7
bk/prefix/as58453.txt Normal file
View File

@ -0,0 +1,7 @@
2400:8800:1f0e:5f
2400:8800:1f11:13
2401:cf80:620f:1
2402:4f00
2402:4f00:3000
2402:4f00:4000:4
2402:4f00:4003

2
bk/prefix/as58807.txt Normal file
View File

@ -0,0 +1,2 @@
2402:4f00
2402:4f00:f000

1335
bk/prefix/as9808.txt Normal file

File diff suppressed because it is too large Load Diff

25
bk/prefix/as9929.txt Normal file
View File

@ -0,0 +1,25 @@
2408:8120
2408:8120:1
2408:8120:2
2408:8610:3bff
2408:8614:1f0
2408:861c:1fff
2408:8625:10fb
2408:8626:f200
2408:862b
2408:862e:2ff
2408:8638:116
2408:8649:2a00
2408:8656:a52
2408:8660:100
2408:8660:ab00
2408:8678:1400
2408:8756:3efd
2408:8a00
2408:8a01
2408:8a02
2408:8a04
2408:8a06
2408:8a06:1
2408:8a06:100
2408:8a06:101

View File

@ -3,14 +3,19 @@ package backtrace
import ( import (
"context" "context"
"errors" "errors"
"golang.org/x/net/icmp" "fmt"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"net" "net"
"sort" "sort"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/oneclickvirt/backtrace/model"
. "github.com/oneclickvirt/defaultset"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
) )
// DefaultConfig is the default configuration for Tracer. // DefaultConfig is the default configuration for Tracer.
@ -19,7 +24,7 @@ var DefaultConfig = Config{
Timeout: 500 * time.Millisecond, Timeout: 500 * time.Millisecond,
MaxHops: 15, MaxHops: 15,
Count: 1, Count: 1,
Networks: []string{"ip4:icmp", "ip4:ip"}, Networks: []string{"ip4:icmp", "ip4:ip", "ip6:ipv6-icmp", "ip6:ip"},
} }
// DefaultTracer is a tracer with DefaultConfig. // DefaultTracer is a tracer with DefaultConfig.
@ -43,7 +48,8 @@ type Tracer struct {
Config Config
once sync.Once once sync.Once
conn *net.IPConn conn *net.IPConn // Ipv4连接
ipv6conn *ipv6.PacketConn // IPv6连接
err error err error
mu sync.RWMutex mu sync.RWMutex
@ -113,13 +119,36 @@ func (t *Tracer) NewSession(ip net.IP) (*Session, error) {
} }
func (t *Tracer) init() { func (t *Tracer) init() {
// 初始化IPv4连接
for _, network := range t.Networks { for _, network := range t.Networks {
if strings.HasPrefix(network, "ip4") {
t.conn, t.err = t.listen(network, t.Addr) t.conn, t.err = t.listen(network, t.Addr)
if t.err != nil { if t.err == nil {
go t.serve(t.conn)
break
}
}
}
// 初始化IPv6连接
for _, network := range t.Networks {
if strings.HasPrefix(network, "ip6") {
conn, err := net.ListenIP(network, t.Addr)
if err == nil {
t.ipv6conn = ipv6.NewPacketConn(conn)
err = t.ipv6conn.SetControlMessage(ipv6.FlagHopLimit|ipv6.FlagSrc|ipv6.FlagDst|ipv6.FlagInterface, true)
if err != nil {
if model.EnableLoger {
InitLogger()
defer Logger.Sync()
Logger.Info("设置IPv6控制消息失败: " + err.Error())
}
t.ipv6conn.Close()
continue continue
} }
go t.serve(t.conn) go t.serveIPv6(t.ipv6conn)
return break
}
}
} }
} }
@ -131,8 +160,10 @@ func (t *Tracer) Close() {
if t.conn != nil { if t.conn != nil {
t.conn.Close() t.conn.Close()
} }
if t.ipv6conn != nil {
t.ipv6conn.Close()
}
} }
func (t *Tracer) serve(conn *net.IPConn) error { func (t *Tracer) serve(conn *net.IPConn) error {
defer conn.Close() defer conn.Close()
buf := make([]byte, 1500) buf := make([]byte, 1500)
@ -150,17 +181,59 @@ func (t *Tracer) serve(conn *net.IPConn) error {
func (t *Tracer) serveData(from net.IP, b []byte) error { func (t *Tracer) serveData(from net.IP, b []byte) error {
if from.To4() == nil { if from.To4() == nil {
// TODO: implement ProtocolIPv6ICMP // IPv6处理
return errUnsupportedProtocol msg, err := icmp.ParseMessage(ProtocolIPv6ICMP, b)
if err != nil {
if model.EnableLoger {
Logger.Warn("解析IPv6 ICMP消息失败: " + err.Error())
} }
now := time.Now() return err
}
// 记录所有收到的消息类型,帮助调试
if model.EnableLoger {
Logger.Info(fmt.Sprintf("收到IPv6 ICMP消息: 类型=%v, 代码=%v", msg.Type, msg.Code))
}
// 处理不同类型的ICMP消息
if msg.Type == ipv6.ICMPTypeEchoReply {
if echo, ok := msg.Body.(*icmp.Echo); ok {
if model.EnableLoger {
Logger.Info(fmt.Sprintf("处理IPv6回显应答: ID=%d, Seq=%d", echo.ID, echo.Seq))
}
return t.serveReply(from, &packet{from, uint16(echo.ID), 1, time.Now()})
}
} else if msg.Type == ipv6.ICMPTypeTimeExceeded {
b = getReplyData(msg)
if len(b) < ipv6.HeaderLen {
if model.EnableLoger {
Logger.Warn("IPv6时间超过消息太短")
}
return errMessageTooShort
}
// 解析原始IPv6包头
if b[0]>>4 == ipv6.Version {
ip, err := ipv6.ParseHeader(b)
if err != nil {
if model.EnableLoger {
Logger.Warn("解析IPv6头部失败: " + err.Error())
}
return err
}
if model.EnableLoger {
Logger.Info(fmt.Sprintf("处理IPv6时间超过: 目标=%v, FlowLabel=%d, HopLimit=%d",
ip.Dst, ip.FlowLabel, ip.HopLimit))
}
return t.serveReply(ip.Dst, &packet{from, uint16(ip.FlowLabel), ip.HopLimit, time.Now()})
}
}
} else {
// 原有的IPv4处理逻辑
msg, err := icmp.ParseMessage(ProtocolICMP, b) msg, err := icmp.ParseMessage(ProtocolICMP, b)
if err != nil { if err != nil {
return err return err
} }
if msg.Type == ipv4.ICMPTypeEchoReply { if msg.Type == ipv4.ICMPTypeEchoReply {
echo := msg.Body.(*icmp.Echo) echo := msg.Body.(*icmp.Echo)
return t.serveReply(from, &packet{from, uint16(echo.ID), 1, now}) return t.serveReply(from, &packet{from, uint16(echo.ID), 1, time.Now()})
} }
b = getReplyData(msg) b = getReplyData(msg)
if len(b) < ipv4.HeaderLen { if len(b) < ipv4.HeaderLen {
@ -172,28 +245,47 @@ func (t *Tracer) serveData(from net.IP, b []byte) error {
if err != nil { if err != nil {
return err return err
} }
return t.serveReply(ip.Dst, &packet{from, uint16(ip.ID), ip.TTL, now}) return t.serveReply(ip.Dst, &packet{from, uint16(ip.ID), ip.TTL, time.Now()})
case ipv6.Version:
ip, err := ipv6.ParseHeader(b)
if err != nil {
return err
}
return t.serveReply(ip.Dst, &packet{from, uint16(ip.FlowLabel), ip.HopLimit, now})
default: default:
return errUnsupportedProtocol return errUnsupportedProtocol
} }
} }
return nil
}
func (t *Tracer) sendRequest(dst net.IP, ttl int) (*packet, error) { func (t *Tracer) sendRequest(dst net.IP, ttl int) (*packet, error) {
id := uint16(atomic.AddUint32(&t.seq, 1)) id := uint16(atomic.AddUint32(&t.seq, 1))
b := newPacket(id, dst, ttl) var b []byte
req := &packet{dst, id, ttl, time.Now()} req := &packet{dst, id, ttl, time.Now()}
if dst.To4() == nil {
// IPv6
b := newPacketV6(id, dst, ttl)
if t.ipv6conn != nil {
cm := &ipv6.ControlMessage{
HopLimit: ttl,
}
_, err := t.ipv6conn.WriteTo(b, cm, &net.IPAddr{IP: dst})
if err != nil {
if model.EnableLoger {
InitLogger()
defer Logger.Sync()
Logger.Info("发送IPv6请求失败: " + err.Error())
}
return nil, err
}
return req, nil
}
return nil, errors.New("IPv6连接不可用")
} else {
// IPv4
b = newPacketV4(id, dst, ttl)
_, err := t.conn.WriteToIP(b, &net.IPAddr{IP: dst}) _, err := t.conn.WriteToIP(b, &net.IPAddr{IP: dst})
if err != nil { if err != nil {
return nil, err return nil, err
} }
return req, nil return req, nil
} }
}
func (t *Tracer) addSession(s *Session) { func (t *Tracer) addSession(s *Session) {
t.mu.Lock() t.mu.Lock()
@ -217,10 +309,30 @@ func (t *Tracer) removeSession(s *Session) {
} }
func (t *Tracer) serveReply(dst net.IP, res *packet) error { func (t *Tracer) serveReply(dst net.IP, res *packet) error {
if model.EnableLoger {
Logger.Info(fmt.Sprintf("处理回复: 目标=%v, 来源=%v, ID=%d, TTL=%d",
dst, res.IP, res.ID, res.TTL))
}
// 确保使用正确的IP格式进行查找
shortDst := shortIP(dst)
t.mu.RLock() t.mu.RLock()
defer t.mu.RUnlock() defer t.mu.RUnlock()
a := t.sess[string(shortIP(dst))] // // 调试输出会话信息
// if model.EnableLoger && len(t.sess) > 0 {
// for ip, sessions := range t.sess {
// Logger.Info(fmt.Sprintf("会话信息: IP=%v, 会话数=%d",
// net.IP([]byte(ip)), len(sessions)))
// }
// }
// 查找对应的会话
a := t.sess[string(shortDst)]
if len(a) == 0 && model.EnableLoger {
Logger.Warn(fmt.Sprintf("找不到目标IP=%v的会话", dst))
}
for _, s := range a { for _, s := range a {
// if model.EnableLoger {
// Logger.Info(fmt.Sprintf("处理会话响应: 会话目标=%v", s.ip))
// }
s.handle(res) s.handle(res)
} }
return nil return nil
@ -284,12 +396,32 @@ func (s *Session) handle(res *packet) {
now := res.Time now := res.Time
n := 0 n := 0
var req *packet var req *packet
if model.EnableLoger {
Logger.Info(fmt.Sprintf("处理会话响应: 会话目标=%v, 响应源=%v, ID=%d, TTL=%d",
s.ip, res.IP, res.ID, res.TTL))
}
s.mu.Lock() s.mu.Lock()
// // 打印出所有待处理的探测包
// if model.EnableLoger && len(s.probes) > 0 {
// Logger.Info(fmt.Sprintf("当前会话有 %d 个待处理的探测包", len(s.probes)))
// for i, probe := range s.probes {
// Logger.Info(fmt.Sprintf("探测包 #%d: ID=%d, TTL=%d, 时间=%v",
// i, probe.ID, probe.TTL, probe.Time))
// }
// }
// 查找匹配的请求包
for _, r := range s.probes { for _, r := range s.probes {
if now.Sub(r.Time) > s.t.Timeout { if now.Sub(r.Time) > s.t.Timeout {
// if model.EnableLoger {
// Logger.Info(fmt.Sprintf("探测包超时: ID=%d, TTL=%d", r.ID, r.TTL))
// }
continue continue
} }
if r.ID == res.ID { // 对于IPv6 松散匹配
if r.ID == res.ID || res.IP.To4() == nil {
// if model.EnableLoger {
// Logger.Info(fmt.Sprintf("找到匹配的探测包: ID=%d, TTL=%d", r.ID, r.TTL))
// }
req = r req = r
continue continue
} }
@ -299,12 +431,19 @@ func (s *Session) handle(res *packet) {
s.probes = s.probes[:n] s.probes = s.probes[:n]
s.mu.Unlock() s.mu.Unlock()
if req == nil { if req == nil {
// if model.EnableLoger {
// Logger.Warn(fmt.Sprintf("未找到匹配的探测包: 响应ID=%d", res.ID))
// }
return return
} }
hops := req.TTL - res.TTL + 1 hops := req.TTL - res.TTL + 1
if hops < 1 { if hops < 1 {
hops = 1 hops = 1
} }
if model.EnableLoger {
Logger.Info(fmt.Sprintf("创建响应: IP=%v, RTT=%v, Hops=%d",
res.IP, res.Time.Sub(req.Time), hops))
}
select { select {
case s.ch <- &Reply{ case s.ch <- &Reply{
IP: res.IP, IP: res.IP,
@ -312,6 +451,9 @@ func (s *Session) handle(res *packet) {
Hops: hops, Hops: hops,
}: }:
default: default:
if model.EnableLoger {
Logger.Warn("发送响应到通道失败,通道已满")
}
} }
} }
@ -352,33 +494,6 @@ var (
errNoReplyData = errors.New("no reply data") errNoReplyData = errors.New("no reply data")
) )
func newPacket(id uint16, dst net.IP, ttl int) []byte {
// TODO: reuse buffers...
msg := icmp.Message{
Type: ipv4.ICMPTypeEcho,
Body: &icmp.Echo{
ID: int(id),
Seq: int(id),
},
}
p, _ := msg.Marshal(nil)
ip := &ipv4.Header{
Version: ipv4.Version,
Len: ipv4.HeaderLen,
TotalLen: ipv4.HeaderLen + len(p),
TOS: 16,
ID: int(id),
Dst: dst,
Protocol: ProtocolICMP,
TTL: ttl,
}
buf, err := ip.Marshal()
if err != nil {
return nil
}
return append(buf, p...)
}
// IANA Assigned Internet Protocol Numbers // IANA Assigned Internet Protocol Numbers
const ( const (
ProtocolICMP = 1 ProtocolICMP = 1

186
bk/trace_ipv4.go Normal file
View File

@ -0,0 +1,186 @@
package backtrace
import (
"fmt"
"net"
"strings"
"github.com/oneclickvirt/backtrace/model"
. "github.com/oneclickvirt/defaultset"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
)
func newPacketV4(id uint16, dst net.IP, ttl int) []byte {
// TODO: reuse buffers...
msg := icmp.Message{
Type: ipv4.ICMPTypeEcho,
Body: &icmp.Echo{
ID: int(id),
Seq: int(id),
},
}
p, _ := msg.Marshal(nil)
ip := &ipv4.Header{
Version: ipv4.Version,
Len: ipv4.HeaderLen,
TotalLen: ipv4.HeaderLen + len(p),
TOS: 16,
ID: int(id),
Dst: dst,
Protocol: ProtocolICMP,
TTL: ttl,
}
buf, err := ip.Marshal()
if err != nil {
return nil
}
return append(buf, p...)
}
// extractIpv4ASNsFromHops 从跃点中提取ASN列表
func extractIpv4ASNsFromHops(hops []*Hop, enableLogger bool) []string {
var asns []string
for _, h := range hops {
for _, n := range h.Nodes {
asn := ipv4Asn(n.IP.String())
if asn != "" {
asns = append(asns, asn)
if enableLogger {
Logger.Info(fmt.Sprintf("IP %s 对应的ASN: %s", n.IP.String(), asn))
}
}
}
}
return asns
}
// trace IPv4追踪函数
func trace(ch chan Result, i int) {
if model.EnableLoger {
InitLogger()
defer Logger.Sync()
Logger.Info(fmt.Sprintf("开始追踪 %s (%s)", model.Ipv4Names[i], model.Ipv4s[i]))
}
// 先尝试原始IP地址
hops, err := Trace(net.ParseIP(model.Ipv4s[i]))
if err != nil {
s := fmt.Sprintf("%v %-15s %v", model.Ipv4Names[i], model.Ipv4s[i], Red("检测不到回程路由节点的IP地址"))
if model.EnableLoger {
Logger.Error(fmt.Sprintf("追踪 %s (%s) 失败: %v", model.Ipv4Names[i], model.Ipv4s[i], err))
}
ch <- Result{i, s}
}
asns := extractIpv4ASNsFromHops(hops, model.EnableLoger)
// 如果没有找到ASN尝试备选IP
if len(asns) == 0 {
// 尝试从IcmpTargets获取备选IP
if tryAltIPs := tryAlternativeIPs(model.Ipv4Names[i], "v4"); len(tryAltIPs) > 0 {
for _, altIP := range tryAltIPs {
if model.EnableLoger {
Logger.Info(fmt.Sprintf("尝试备选IP %s 追踪 %s", altIP, model.Ipv4Names[i]))
}
hops, err = Trace(net.ParseIP(altIP))
if err == nil && len(hops) > 0 {
newAsns := extractIpv4ASNsFromHops(hops, model.EnableLoger)
asns = append(asns, newAsns...)
if len(newAsns) > 0 {
break // 成功找到可用IP
}
}
}
}
}
asns = removeDuplicates(asns)
// // 记录每个hop的信息
// if model.EnableLoger {
// for hopNum, hop := range hops {
// for nodeNum, node := range hop.Nodes {
// Logger.Info(fmt.Sprintf("追踪 %s (%s) - Hop %d, Node %d: %s (RTT: %v)",
// model.Ipv4Names[i], model.Ipv4s[i], hopNum+1, nodeNum+1, node.IP.String(), node.RTT))
// }
// }
// }
// 处理不同线路
if len(asns) > 0 {
var tempText string
asns = removeDuplicates(asns)
tempText += fmt.Sprintf("%v ", model.Ipv4Names[i])
hasAS4134 := false
hasAS4809 := false
for _, asn := range asns {
if asn == "AS4134" {
hasAS4134 = true
}
if asn == "AS4809" {
hasAS4809 = true
}
}
// 判断是否包含 AS4134 和 AS4809
if hasAS4134 && hasAS4809 {
// 同时包含 AS4134 和 AS4809 属于 CN2GT
asns = append([]string{"AS4809b"}, asns...)
if model.EnableLoger {
Logger.Info(fmt.Sprintf("%s (%s) 线路识别为: CN2GT", model.Ipv4Names[i], model.Ipv4s[i]))
}
} else if hasAS4809 {
// 仅包含 AS4809 属于 CN2GIA
asns = append([]string{"AS4809a"}, asns...)
if model.EnableLoger {
Logger.Info(fmt.Sprintf("%s (%s) 线路识别为: CN2GIA", model.Ipv4Names[i], model.Ipv4s[i]))
}
}
tempText += fmt.Sprintf("%-24s ", model.Ipv4s[i])
for _, asn := range asns {
asnDescription := model.M[asn]
switch asn {
case "":
continue
case "AS4809": // 被 AS4809a 和 AS4809b 替代了
continue
case "AS9929":
if !strings.Contains(tempText, asnDescription) {
tempText += DarkGreen(asnDescription) + " "
}
case "AS4809a":
if !strings.Contains(tempText, asnDescription) {
tempText += DarkGreen(asnDescription) + " "
}
case "AS23764":
if !strings.Contains(tempText, asnDescription) {
tempText += DarkGreen(asnDescription) + " "
}
case "AS4809b":
if !strings.Contains(tempText, asnDescription) {
tempText += Green(asnDescription) + " "
}
case "AS58807":
if !strings.Contains(tempText, asnDescription) {
tempText += Green(asnDescription) + " "
}
default:
if !strings.Contains(tempText, asnDescription) {
tempText += White(asnDescription) + " "
}
}
}
if tempText == (fmt.Sprintf("%v ", model.Ipv4Names[i]) + fmt.Sprintf("%-15s ", model.Ipv4s[i])) {
tempText += fmt.Sprintf("%v", Red("检测不到已知线路的ASN"))
if model.EnableLoger {
Logger.Warn(fmt.Sprintf("%s (%s) 检测不到已知线路的ASN", model.Ipv4Names[i], model.Ipv4s[i]))
}
}
if model.EnableLoger {
Logger.Info(fmt.Sprintf("%s (%s) 追踪完成,结果: %s", model.Ipv4Names[i], model.Ipv4s[i], tempText))
}
ch <- Result{i, tempText}
} else {
s := fmt.Sprintf("%v %-15s %v", model.Ipv4Names[i], model.Ipv4s[i], Red("检测不到回程路由节点的IPV4地址"))
if model.EnableLoger {
Logger.Warn(fmt.Sprintf("%s (%s) 检测不到回程路由节点的IPV4地址", model.Ipv4Names[i], model.Ipv4s[i]))
}
ch <- Result{i, s}
}
}

199
bk/trace_ipv6.go Normal file
View File

@ -0,0 +1,199 @@
package backtrace
import (
"fmt"
"net"
"strings"
"github.com/oneclickvirt/backtrace/model"
. "github.com/oneclickvirt/defaultset"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv6"
)
func newPacketV6(id uint16, dst net.IP, ttl int) []byte {
// 使用ipv6包的Echo请求
msg := icmp.Message{
Type: ipv6.ICMPTypeEchoRequest,
Code: 0,
Body: &icmp.Echo{
ID: int(id),
Seq: int(id),
Data: []byte("HELLO-R-U-THERE"),
},
}
// 序列化ICMP消息
icmpBytes, _ := msg.Marshal(nil)
return icmpBytes
}
func (t *Tracer) serveIPv6(conn *ipv6.PacketConn) error {
if model.EnableLoger {
InitLogger()
defer Logger.Sync()
}
defer conn.Close()
buf := make([]byte, 1500)
for {
n, cm, src, err := conn.ReadFrom(buf)
if err != nil {
if model.EnableLoger {
Logger.Error("读取IPv6响应失败: " + err.Error())
}
return err
}
if model.EnableLoger {
Logger.Info(fmt.Sprintf("收到IPv6响应: 来源=%v, 跳数=%d", src, cm.HopLimit))
}
fromIP := src.(*net.IPAddr).IP
err = t.serveData(fromIP, buf[:n])
if err != nil && model.EnableLoger {
Logger.Warn("处理IPv6数据失败: " + err.Error())
}
}
}
// extractIpv6ASNsFromHops 从跃点中提取ASN列表
func extractIpv6ASNsFromHops(hops []*Hop, enableLogger bool) []string {
var asns []string
for _, h := range hops {
for _, n := range h.Nodes {
asn := ipv6Asn(n.IP.String())
if asn != "" {
asns = append(asns, asn)
if enableLogger {
Logger.Info(fmt.Sprintf("IP %s 对应的ASN: %s", n.IP.String(), asn))
}
}
}
}
return asns
}
// traceIPv6 IPv6追踪函数
func traceIPv6(ch chan Result, i int, offset int) {
if model.EnableLoger {
InitLogger()
defer Logger.Sync()
Logger.Info(fmt.Sprintf("开始追踪 %s (%s)", model.Ipv6Names[i], model.Ipv6s[i]))
}
// 先尝试原始IP地址
hops, err := Trace(net.ParseIP(model.Ipv6s[i]))
if err != nil {
s := fmt.Sprintf("%v %-24s %v", model.Ipv6Names[i], model.Ipv6s[i], Red("检测不到回程路由节点的IP地址"))
if model.EnableLoger {
Logger.Warn(fmt.Sprintf("%s (%s) 检测不到回程路由节点的IP地址", model.Ipv6Names[i], model.Ipv6s[i]))
}
ch <- Result{i + offset, s}
}
asns := extractIpv6ASNsFromHops(hops, model.EnableLoger)
// 如果没有找到ASN尝试备选IP
if len(asns) == 0 {
// 尝试从IcmpTargets获取备选IP
if tryAltIPs := tryAlternativeIPs(model.Ipv4Names[i], "v4"); len(tryAltIPs) > 0 {
for _, altIP := range tryAltIPs {
if model.EnableLoger {
Logger.Info(fmt.Sprintf("尝试备选IP %s 追踪 %s", altIP, model.Ipv4Names[i]))
}
hops, err = Trace(net.ParseIP(altIP))
if err == nil && len(hops) > 0 {
newAsns := extractIpv6ASNsFromHops(hops, model.EnableLoger)
asns = append(asns, newAsns...)
if len(newAsns) > 0 {
break // 成功找到可用IP
}
}
}
}
}
asns = removeDuplicates(asns)
// // 记录每个hop的信息
// if model.EnableLoger {
// for hopNum, hop := range hops {
// for nodeNum, node := range hop.Nodes {
// Logger.Info(fmt.Sprintf("追踪 %s (%s) - Hop %d, Node %d: %s (RTT: %v)",
// model.Ipv6Names[i], model.Ipv6s[i], hopNum+1, nodeNum+1, node.IP.String(), node.RTT))
// }
// }
// }
// 处理不同线路
if len(asns) > 0 {
var tempText string
asns = removeDuplicates(asns)
tempText += fmt.Sprintf("%v ", model.Ipv6Names[i])
hasAS4134 := false
hasAS4809 := false
for _, asn := range asns {
if asn == "AS4134" {
hasAS4134 = true
}
if asn == "AS4809" {
hasAS4809 = true
}
}
// 判断是否包含 AS4134 和 AS4809
if hasAS4134 && hasAS4809 {
// 同时包含 AS4134 和 AS4809 属于 CN2GT
asns = append([]string{"AS4809b"}, asns...)
if model.EnableLoger {
Logger.Info(fmt.Sprintf("%s (%s) 线路识别为: CN2GT", model.Ipv6Names[i], model.Ipv6s[i]))
}
} else if hasAS4809 {
// 仅包含 AS4809 属于 CN2GIA
asns = append([]string{"AS4809a"}, asns...)
if model.EnableLoger {
Logger.Info(fmt.Sprintf("%s (%s) 线路识别为: CN2GIA", model.Ipv6Names[i], model.Ipv6s[i]))
}
}
tempText += fmt.Sprintf("%-24s ", model.Ipv6s[i])
for _, asn := range asns {
asnDescription := model.M[asn]
switch asn {
case "":
continue
case "AS4809": // 被 AS4809a 和 AS4809b 替代了
continue
case "AS9929":
if !strings.Contains(tempText, asnDescription) {
tempText += DarkGreen(asnDescription) + " "
}
case "AS4809a":
if !strings.Contains(tempText, asnDescription) {
tempText += DarkGreen(asnDescription) + " "
}
case "AS23764":
if !strings.Contains(tempText, asnDescription) {
tempText += DarkGreen(asnDescription) + " "
}
case "AS4809b":
if !strings.Contains(tempText, asnDescription) {
tempText += Green(asnDescription) + " "
}
case "AS58807":
if !strings.Contains(tempText, asnDescription) {
tempText += Green(asnDescription) + " "
}
default:
if !strings.Contains(tempText, asnDescription) {
tempText += White(asnDescription) + " "
}
}
}
if tempText == (fmt.Sprintf("%v ", model.Ipv6Names[i]) + fmt.Sprintf("%-40s ", model.Ipv6s[i])) {
tempText += fmt.Sprintf("%v", Red("检测不到已知线路的ASN"))
if model.EnableLoger {
Logger.Warn(fmt.Sprintf("%s (%s) 检测不到已知线路的ASN", model.Ipv6Names[i], model.Ipv6s[i]))
}
}
if model.EnableLoger {
Logger.Info(fmt.Sprintf("%s (%s) 追踪完成,结果: %s", model.Ipv6Names[i], model.Ipv6s[i], tempText))
}
ch <- Result{i + offset, tempText}
} else {
s := fmt.Sprintf("%v %-24s %v", model.Ipv6Names[i], model.Ipv6s[i], Red("检测不到回程路由节点的IPV6地址"))
if model.EnableLoger {
Logger.Warn(fmt.Sprintf("%s (%s) 检测不到回程路由节点的IPV6地址", model.Ipv6Names[i], model.Ipv6s[i]))
}
ch <- Result{i + offset, s}
}
}

142
bk/utils.go Normal file
View File

@ -0,0 +1,142 @@
package backtrace
import (
"encoding/json"
"fmt"
"io"
"strings"
"time"
"github.com/imroc/req/v3"
"github.com/oneclickvirt/backtrace/model"
. "github.com/oneclickvirt/defaultset"
)
type Result struct {
i int
s string
}
// removeDuplicates 切片去重
func removeDuplicates(elements []string) []string {
if elements == nil {
return nil
}
seen := make(map[string]struct{})
var result []string
for _, v := range elements {
if _, ok := seen[v]; !ok {
seen[v] = struct{}{}
result = append(result, v)
}
}
return result
}
// getData 获取目标地址的文本内容
func getData(endpoint string) string {
client := req.C()
client.SetTimeout(6 * time.Second)
client.R().
SetRetryCount(2).
SetRetryBackoffInterval(1*time.Second, 5*time.Second).
SetRetryFixedInterval(2 * time.Second)
if model.EnableLoger {
InitLogger()
defer Logger.Sync()
}
for _, baseUrl := range model.CdnList {
url := baseUrl + endpoint
resp, err := client.R().Get(url)
if err == nil {
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if strings.Contains(string(b), "error") {
continue
}
if err == nil {
if model.EnableLoger {
Logger.Info(fmt.Sprintf("Received data length: %d", len(b)))
}
return string(b)
}
}
if model.EnableLoger {
Logger.Info(fmt.Sprintf("HTTP request failed: %v", err))
}
}
return ""
}
// parseIcmpTargets 解析ICMP目标数据
func parseIcmpTargets(jsonData string) []model.IcmpTarget {
var targets []model.IcmpTarget
err := json.Unmarshal([]byte(jsonData), &targets)
if err != nil {
if model.EnableLoger {
Logger.Info(fmt.Sprintf("解析ICMP目标失败: %s", err.Error()))
}
return nil
}
return targets
}
// tryAlternativeIPs 从IcmpTargets获取备选IP地址
func tryAlternativeIPs(targetName string, ipVersion string) []string {
if model.ParsedIcmpTargets == nil || (model.ParsedIcmpTargets != nil && len(model.ParsedIcmpTargets) == 0) {
return nil
}
if model.EnableLoger {
Logger.Info(fmt.Sprintf("使用备选地址: %s %s", targetName, ipVersion))
}
// 从目标名称中提取省份和ISP信息
var targetProvince, targetISP string
if strings.Contains(targetName, "北京") {
targetProvince = "北京"
} else if strings.Contains(targetName, "上海") {
targetProvince = "上海"
} else if strings.Contains(targetName, "广州") {
targetProvince = "广东"
} else if strings.Contains(targetName, "成都") {
targetProvince = "四川"
}
if strings.Contains(targetName, "电信") {
targetISP = "电信"
} else if strings.Contains(targetName, "联通") {
targetISP = "联通"
} else if strings.Contains(targetName, "移动") {
targetISP = "移动"
}
// 如果没有提取到信息,返回空
if targetProvince == "" || targetISP == "" {
return nil
}
// 查找匹配条件的目标
var result []string
for _, target := range model.ParsedIcmpTargets {
// 检查省份是否匹配(可能带有"省"字或不带)
provinceMatch := (target.Province == targetProvince) || (target.Province == targetProvince+"省")
// 检查ISP和IP版本是否匹配
if provinceMatch && target.ISP == targetISP && target.IPVersion == ipVersion {
// 解析IP列表
if target.IPs != "" {
ips := strings.Split(target.IPs, ",")
// 最多返回3个IP地址
count := 0
for _, ip := range ips {
if ip != "" {
result = append(result, strings.TrimSpace(ip))
count++
if count >= 3 {
break
}
}
}
if len(result) > 0 {
return result
}
}
}
}
return nil
}

View File

@ -1,18 +1,68 @@
package main package main
import ( import (
"encoding/json"
"flag"
"fmt" "fmt"
"net/http" "net/http"
"github.com/oneclickvirt/backtrace/backtrace" "os"
. "github.com/oneclickvirt/backtrace/defaultset" "runtime"
backtrace "github.com/oneclickvirt/backtrace/bk"
"github.com/oneclickvirt/backtrace/model"
. "github.com/oneclickvirt/defaultset"
) )
type IpInfo struct {
Ip string `json:"ip"`
City string `json:"city"`
Region string `json:"region"`
Country string `json:"country"`
Org string `json:"org"`
}
func main() { func main() {
go func() { go func() {
http.Get("https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Foneclickvirt%2Fbacktrace&count_bg=%2323E01C&title_bg=%23555555&icon=sonarcloud.svg&icon_color=%23E7E7E7&title=hits&edge_flat=false") http.Get("https://hits.spiritlhl.net/backtrace.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false")
}() }()
fmt.Println(Green("项目地址:"), Yellow("https://github.com/oneclickvirt/backtrace")) fmt.Println(Green("项目地址:"), Yellow("https://github.com/oneclickvirt/backtrace"))
backtrace.BackTrace() var showVersion, showIpInfo, help, ipv6 bool
fmt.Println(Purple("同一目标地址显示多个线路时可能追踪IP地址已越过汇聚层此时除去第一个线路信息后续信息可能无效")) backtraceFlag := flag.NewFlagSet("backtrace", flag.ContinueOnError)
fmt.Println(Purple("准确线路请查看详细的路由自行判断")) backtraceFlag.BoolVar(&help, "h", false, "Show help information")
backtraceFlag.BoolVar(&showVersion, "v", false, "Show version")
backtraceFlag.BoolVar(&showIpInfo, "s", true, "Disabe show ip info")
backtraceFlag.BoolVar(&model.EnableLoger, "log", false, "Enable logging")
backtraceFlag.BoolVar(&ipv6, "ipv6", false, "Enable ipv6 testing")
backtraceFlag.Parse(os.Args[1:])
if help {
fmt.Printf("Usage: %s [options]\n", os.Args[0])
backtraceFlag.PrintDefaults()
return
}
if showVersion {
fmt.Println(model.BackTraceVersion)
return
}
if showIpInfo {
rsp, err := http.Get("http://ipinfo.io")
if err != nil {
fmt.Errorf("Get ip info err %v \n", err.Error())
} else {
info := IpInfo{}
err = json.NewDecoder(rsp.Body).Decode(&info)
if err != nil {
fmt.Errorf("json decode err %v \n", err.Error())
} else {
fmt.Println(Green("国家: ") + White(info.Country) + Green(" 城市: ") + White(info.City) +
Green(" 服务商: ") + Blue(info.Org))
}
}
}
backtrace.BackTrace(ipv6)
fmt.Println(Yellow("准确线路自行查看详细路由,本测试结果仅作参考"))
fmt.Println(Yellow("同一目标地址多个线路时,可能检测已越过汇聚层,除了第一个线路外,后续信息可能无效"))
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
fmt.Println("Press Enter to exit...")
fmt.Scanln()
}
} }

View File

@ -1,35 +0,0 @@
package defaultset
import "fmt"
func Red(text string) string {
return fmt.Sprintf("\033[31m\033[01m%s\033[0m", text)
}
func Green(text string) string {
return fmt.Sprintf("\033[32m\033[01m%s\033[0m", text)
}
func DarkGreen(text string) string {
return fmt.Sprintf("\033[32m\033[02m%s\033[0m", text)
}
func Yellow(text string) string {
return fmt.Sprintf("\033[33m\033[01m%s\033[0m", text)
}
func Blue(text string) string {
return fmt.Sprintf("\033[36m\033[01m%s\033[0m", text)
}
func Purple(text string) string {
return fmt.Sprintf("\033[35m\033[01m%s\033[0m", text)
}
func Cyan(text string) string {
return fmt.Sprintf("\033[36m\033[01m%s\033[0m", text)
}
func White(text string) string {
return fmt.Sprintf("\033[37m\033[01m%s\033[0m", text)
}

View File

@ -1,40 +0,0 @@
package defaultset
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var Logger *zap.Logger
func getZapConfig() zap.Config {
cfg := zap.Config{
Encoding: "console", // 日志编码格式
Level: zap.NewAtomicLevelAt(zap.InfoLevel), // 日志级别
OutputPaths: []string{"ecs.log"}, // 输出路径,可以是多个文件
ErrorOutputPaths: []string{}, // 错误输出路径,可以是多个文件
EncoderConfig: zapcore.EncoderConfig{
TimeKey: "timestamp", // 时间字段名称
LevelKey: "level", // 日志级别字段名称
NameKey: "logger", // 日志记录器名称字段名称
CallerKey: "caller", // 调用者信息字段名称
MessageKey: "message", // 日志消息字段名称
StacktraceKey: "stacktrace", // 堆栈跟踪信息字段名称
EncodeLevel: zapcore.LowercaseLevelEncoder, // 小写格式的日志级别编码器
EncodeTime: zapcore.ISO8601TimeEncoder, // ISO8601 时间格式编码器
EncodeCaller: zapcore.ShortCallerEncoder, // 编码短调用者信息
},
}
return cfg
}
// InitLogger 初始化日志记录器
func InitLogger() {
// 配置日志记录器
cfg := getZapConfig()
var err error
Logger, err = cfg.Build()
if err != nil {
panic("failed to initialize logger: " + err.Error())
}
}

31
go.mod
View File

@ -1,14 +1,33 @@
module github.com/oneclickvirt/backtrace module github.com/oneclickvirt/backtrace
go 1.21.5 go 1.22.4
require ( require (
go.uber.org/zap v1.27.0 github.com/imroc/req/v3 v3.50.0
golang.org/x/net v0.24.0 github.com/oneclickvirt/defaultset v0.0.0-20240624051018-30a50859e1b5
golang.org/x/sys v0.19.0 golang.org/x/net v0.33.0
golang.org/x/sys v0.28.0
) )
require ( require (
github.com/stretchr/testify v1.9.0 // indirect github.com/andybalholm/brotli v1.1.1 // indirect
go.uber.org/multierr v1.11.0 // indirect github.com/cloudflare/circl v1.5.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/onsi/ginkgo/v2 v2.22.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.48.2 // indirect
github.com/refraction-networking/utls v1.6.7 // indirect
go.uber.org/mock v0.5.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.28.0 // indirect
) )

65
go.sum
View File

@ -1,18 +1,71 @@
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/imroc/req/v3 v3.50.0 h1:n3BVnZiTRpvkN5T1IB79LC/THhFU9iXksNRMH4ZNVaY=
github.com/imroc/req/v3 v3.50.0/go.mod h1:tsOk8K7zI6cU4xu/VWCZVtq9Djw9IWm4MslKzme5woU=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/oneclickvirt/defaultset v0.0.0-20240624051018-30a50859e1b5 h1:TUM6XzOB7Z7OxyXi3fwlZY9KfuVbvUBusYiNbSfX208=
github.com/oneclickvirt/defaultset v0.0.0-20240624051018-30a50859e1b5/go.mod h1:e9Jt4tf2sbemCtc84/XgKcHy9EZ2jkc5x2sW1NiJS+E=
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

78
model/model.go Normal file
View File

@ -0,0 +1,78 @@
package model
import "time"
const BackTraceVersion = "v0.0.5"
var EnableLoger = false
// IcmpTarget 定义ICMP目标的JSON结构
type IcmpTarget struct {
Province string `json:"province"`
ISP string `json:"isp"`
IPVersion string `json:"ip_version"`
IPs string `json:"ips"` // IP列表以逗号分隔
}
var (
IcmpTargets = "https://raw.githubusercontent.com/spiritLHLS/icmp_targets/main/nodes.json"
CdnList = []string{
"http://cdn1.spiritlhl.net/",
"http://cdn2.spiritlhl.net/",
"http://cdn3.spiritlhl.net/",
"http://cdn4.spiritlhl.net/",
}
Ipv4s = []string{
"219.141.140.10", // 北京电信v4
"202.106.195.68", // 北京联通v4
"221.179.155.161", // 北京移动v4
"202.96.209.133", // 上海电信v4
"210.22.97.1", // 上海联通v4
"211.136.112.200", // 上海移动v4
"58.60.188.222", // 广州电信v4
"210.21.196.6", // 广州联通v4
"120.196.165.24", // 广州移动v4
"61.139.2.69", // 成都电信v4
"119.6.6.6", // 成都联通v4
"211.137.96.205", // 成都移动v4
}
Ipv4Names = []string{
"北京电信v4", "北京联通v4", "北京移动v4",
"上海电信v4", "上海联通v4", "上海移动v4",
"广州电信v4", "广州联通v4", "广州移动v4",
"成都电信v4", "成都联通v4", "成都移动v4",
}
Ipv6s = []string{
"2400:89c0:1053:3::69", // 北京电信 IPv6
"2400:89c0:1013:3::54", // 北京联通 IPv6
"2409:8c00:8421:1303::55", // 北京移动 IPv6
"240e:e1:aa00:4000::24", // 上海电信 IPV6
"2408:80f1:21:5003::a", // 上海联通 IPv6
"2409:8c1e:75b0:3003::26", // 上海移动 IPv6
"240e:97c:2f:3000::44", // 广州电信 IPv6
"2408:8756:f50:1001::c", // 广州联通 IPv6
"2409:8c54:871:1001::12", // 广州移动 IPv6
}
Ipv6Names = []string{
"北京电信v6", "北京联通v6", "北京移动v6",
"上海电信v6", "上海联通v6", "上海移动v6",
"广州电信v6", "广州联通v6", "广州移动v6",
}
M = map[string]string{
// [] 前的字符串个数中文占2个字符串
"AS23764": "电信CTGNET [精品线路]",
"AS4809a": "电信CN2GIA [精品线路]",
"AS4809b": "电信CN2GT [优质线路]",
"AS4809": "电信CN2 [优质线路]",
"AS4134": "电信163 [普通线路]",
"AS9929": "联通9929 [优质线路]",
"AS4837": "联通4837 [普通线路]",
"AS58807": "移动CMIN2 [精品线路]",
"AS9808": "移动CMI [普通线路]",
"AS58453": "移动CMI [普通线路]",
}
CachedIcmpData string
CachedIcmpDataFetchTime time.Time
ParsedIcmpTargets []IcmpTarget
)