mirror of
https://github.com/oneclickvirt/backtrace.git
synced 2025-08-28 18:20:38 +08:00
fix: 多次路由取平均,输出方式提取到最外层调用
This commit is contained in:
parent
c7a4dd9475
commit
6f8f3ed60c
@ -16,6 +16,7 @@
|
||||
- [x] 支持对```CMIN2```和```CMI```线路的判断
|
||||
- [x] 支持对整个回程路由进行线路分析,一个目标IP可能会分析出多种线路
|
||||
- [x] 支持对主流接入点的线路检测,方便分析国际互联能力
|
||||
- [x] 多次并发路由检测以分析平均多次路由,避免单次路由因网络波动未能准确检测
|
||||
- [x] 增加对全平台的编译支持,原版[backtrace](https://github.com/zhanghanyun/backtrace)仅支持linux平台的amd64和arm64架构
|
||||
- [x] 兼容额外的ICMP地址获取,若当前目标IP无法查询路由尝试额外的IP地址
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
package backtrace
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/oneclickvirt/backtrace/model"
|
||||
)
|
||||
|
||||
func BackTrace(enableIpv6 bool) {
|
||||
func BackTrace(enableIpv6 bool) string {
|
||||
if model.CachedIcmpData == "" || model.ParsedIcmpTargets == nil || time.Since(model.CachedIcmpDataFetchTime) > time.Hour {
|
||||
model.CachedIcmpData = getData(model.IcmpTargets)
|
||||
model.CachedIcmpDataFetchTime = time.Now()
|
||||
@ -15,6 +15,7 @@ func BackTrace(enableIpv6 bool) {
|
||||
model.ParsedIcmpTargets = parseIcmpTargets(model.CachedIcmpData)
|
||||
}
|
||||
}
|
||||
var builder strings.Builder
|
||||
if enableIpv6 {
|
||||
ipv4Count := len(model.Ipv4s)
|
||||
ipv6Count := len(model.Ipv6s)
|
||||
@ -39,14 +40,18 @@ func BackTrace(enableIpv6 bool) {
|
||||
break loopIPv4v6
|
||||
}
|
||||
}
|
||||
// 收集 IPv4 结果
|
||||
for i := 0; i < ipv4Count; i++ {
|
||||
if s[i] != "" {
|
||||
fmt.Println(s[i])
|
||||
builder.WriteString(s[i])
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
}
|
||||
// 收集 IPv6 结果
|
||||
for i := ipv4Count; i < totalCount; i++ {
|
||||
if s[i] != "" {
|
||||
fmt.Println(s[i])
|
||||
builder.WriteString(s[i])
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -68,10 +73,15 @@ func BackTrace(enableIpv6 bool) {
|
||||
break loopIPv4
|
||||
}
|
||||
}
|
||||
// 收集结果
|
||||
for _, r := range s {
|
||||
if r != "" {
|
||||
fmt.Println(r)
|
||||
builder.WriteString(r)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
// 返回完整结果,去掉末尾的换行符
|
||||
result := builder.String()
|
||||
return strings.TrimSuffix(result, "\n")
|
||||
}
|
||||
|
@ -194,14 +194,15 @@ func (t *Tracer) serveData(from net.IP, b []byte) error {
|
||||
Logger.Info(fmt.Sprintf("收到IPv6 ICMP消息: 类型=%v, 代码=%v", msg.Type, msg.Code))
|
||||
}
|
||||
// 处理不同类型的ICMP消息
|
||||
if msg.Type == ipv6.ICMPTypeEchoReply {
|
||||
switch msg.Type {
|
||||
case 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 {
|
||||
case ipv6.ICMPTypeTimeExceeded:
|
||||
b = getReplyData(msg)
|
||||
if len(b) < ipv6.HeaderLen {
|
||||
if model.EnableLoger {
|
||||
|
@ -62,46 +62,56 @@ func trace(ch chan Result, i int) {
|
||||
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地址"))
|
||||
var allHops [][]*Hop
|
||||
var successfulTraces int
|
||||
// 尝试3次trace
|
||||
for attempt := 1; attempt <= 3; attempt++ {
|
||||
if model.EnableLoger {
|
||||
Logger.Error(fmt.Sprintf("追踪 %s (%s) 失败: %v", model.Ipv4Names[i], model.Ipv4s[i], err))
|
||||
Logger.Info(fmt.Sprintf("第%d次尝试追踪 %s (%s)", attempt, model.Ipv4Names[i], model.Ipv4s[i]))
|
||||
}
|
||||
ch <- Result{i, s}
|
||||
return
|
||||
}
|
||||
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 {
|
||||
// 先尝试原始IP地址
|
||||
hops, err := Trace(net.ParseIP(model.Ipv4s[i]))
|
||||
if err != nil {
|
||||
if model.EnableLoger {
|
||||
Logger.Warn(fmt.Sprintf("第%d次追踪 %s (%s) 失败: %v", attempt, model.Ipv4Names[i], model.Ipv4s[i], err))
|
||||
}
|
||||
// 如果原始IP失败,尝试备选IP
|
||||
if tryAltIPs := tryAlternativeIPs(model.Ipv4Names[i], "v4"); len(tryAltIPs) > 0 {
|
||||
for _, altIP := range tryAltIPs {
|
||||
if model.EnableLoger {
|
||||
Logger.Info(fmt.Sprintf("第%d次尝试备选IP %s 追踪 %s", attempt, altIP, model.Ipv4Names[i]))
|
||||
}
|
||||
hops, err = Trace(net.ParseIP(altIP))
|
||||
if err == nil && len(hops) > 0 {
|
||||
break // 成功找到可用IP
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil && len(hops) > 0 {
|
||||
allHops = append(allHops, hops)
|
||||
successfulTraces++
|
||||
if model.EnableLoger {
|
||||
Logger.Info(fmt.Sprintf("第%d次追踪 %s (%s) 成功,获得%d个hop", attempt, model.Ipv4Names[i], model.Ipv4s[i], len(hops)))
|
||||
}
|
||||
}
|
||||
}
|
||||
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))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// 如果3次都失败
|
||||
if successfulTraces == 0 {
|
||||
s := fmt.Sprintf("%v %-15s %v", model.Ipv4Names[i], model.Ipv4s[i], Red("检测不到回程路由节点的IP地址"))
|
||||
if model.EnableLoger {
|
||||
Logger.Error(fmt.Sprintf("%s (%s) 3次尝试都失败,检测不到回程路由节点的IP地址", model.Ipv4Names[i], model.Ipv4s[i]))
|
||||
}
|
||||
ch <- Result{i, s}
|
||||
return
|
||||
}
|
||||
// 合并hops结果
|
||||
mergedHops := mergeHops(allHops)
|
||||
if model.EnableLoger {
|
||||
Logger.Info(fmt.Sprintf("%s (%s) 完成%d次成功追踪,合并后获得%d个hop", model.Ipv4Names[i], model.Ipv4s[i], successfulTraces, len(mergedHops)))
|
||||
}
|
||||
// 从合并后的hops提取ASN
|
||||
asns := extractIpv4ASNsFromHops(mergedHops, model.EnableLoger)
|
||||
// 处理不同线路
|
||||
if len(asns) > 0 {
|
||||
var tempText string
|
||||
@ -167,18 +177,16 @@ func trace(ch chan Result, i int) {
|
||||
}
|
||||
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))
|
||||
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]))
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
func newPacketV6(id uint16, dst net.IP, ttl int) []byte {
|
||||
func newPacketV6(id uint16, _ net.IP, _ int) []byte {
|
||||
// 使用ipv6包的Echo请求
|
||||
msg := icmp.Message{
|
||||
Type: ipv6.ICMPTypeEchoRequest,
|
||||
@ -77,46 +77,56 @@ func traceIPv6(ch chan Result, i int, offset int) {
|
||||
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地址"))
|
||||
var allHops [][]*Hop
|
||||
var successfulTraces int
|
||||
// 尝试3次trace
|
||||
for attempt := 1; attempt <= 3; attempt++ {
|
||||
if model.EnableLoger {
|
||||
Logger.Warn(fmt.Sprintf("%s (%s) 检测不到回程路由节点的IP地址", model.Ipv6Names[i], model.Ipv6s[i]))
|
||||
Logger.Info(fmt.Sprintf("第%d次尝试追踪 %s (%s)", attempt, model.Ipv6Names[i], model.Ipv6s[i]))
|
||||
}
|
||||
ch <- Result{i + offset, s}
|
||||
return
|
||||
}
|
||||
asns := extractIpv6ASNsFromHops(hops, model.EnableLoger)
|
||||
// 如果没有找到ASN,尝试备选IP
|
||||
if len(asns) == 0 {
|
||||
// 尝试从IcmpTargets获取备选IP
|
||||
if tryAltIPs := tryAlternativeIPs(model.Ipv6Names[i], "v6"); len(tryAltIPs) > 0 {
|
||||
for _, altIP := range tryAltIPs {
|
||||
if model.EnableLoger {
|
||||
Logger.Info(fmt.Sprintf("尝试备选IP %s 追踪 %s", altIP, model.Ipv6Names[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 {
|
||||
// 先尝试原始IP地址
|
||||
hops, err := Trace(net.ParseIP(model.Ipv6s[i]))
|
||||
if err != nil {
|
||||
if model.EnableLoger {
|
||||
Logger.Warn(fmt.Sprintf("第%d次追踪 %s (%s) 失败: %v", attempt, model.Ipv6Names[i], model.Ipv6s[i], err))
|
||||
}
|
||||
// 如果原始IP失败,尝试备选IP
|
||||
if tryAltIPs := tryAlternativeIPs(model.Ipv6Names[i], "v6"); len(tryAltIPs) > 0 {
|
||||
for _, altIP := range tryAltIPs {
|
||||
if model.EnableLoger {
|
||||
Logger.Info(fmt.Sprintf("第%d次尝试备选IP %s 追踪 %s", attempt, altIP, model.Ipv6Names[i]))
|
||||
}
|
||||
hops, err = Trace(net.ParseIP(altIP))
|
||||
if err == nil && len(hops) > 0 {
|
||||
break // 成功找到可用IP
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil && len(hops) > 0 {
|
||||
allHops = append(allHops, hops)
|
||||
successfulTraces++
|
||||
if model.EnableLoger {
|
||||
Logger.Info(fmt.Sprintf("第%d次追踪 %s (%s) 成功,获得%d个hop", attempt, model.Ipv6Names[i], model.Ipv6s[i], len(hops)))
|
||||
}
|
||||
}
|
||||
}
|
||||
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))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// 如果3次都失败
|
||||
if successfulTraces == 0 {
|
||||
s := fmt.Sprintf("%v %-24s %v", model.Ipv6Names[i], model.Ipv6s[i], Red("检测不到回程路由节点的IP地址"))
|
||||
if model.EnableLoger {
|
||||
Logger.Warn(fmt.Sprintf("%s (%s) 3次尝试都失败,检测不到回程路由节点的IP地址", model.Ipv6Names[i], model.Ipv6s[i]))
|
||||
}
|
||||
ch <- Result{i + offset, s}
|
||||
return
|
||||
}
|
||||
// 合并hops结果
|
||||
mergedHops := mergeHops(allHops)
|
||||
if model.EnableLoger {
|
||||
Logger.Info(fmt.Sprintf("%s (%s) 完成%d次成功追踪,合并后获得%d个hop", model.Ipv6Names[i], model.Ipv6s[i], successfulTraces, len(mergedHops)))
|
||||
}
|
||||
// 从合并后的hops提取ASN
|
||||
asns := extractIpv6ASNsFromHops(mergedHops, model.EnableLoger)
|
||||
// 处理不同线路
|
||||
if len(asns) > 0 {
|
||||
var tempText string
|
||||
@ -187,7 +197,7 @@ func traceIPv6(ch chan Result, i int, offset int) {
|
||||
}
|
||||
}
|
||||
if model.EnableLoger {
|
||||
Logger.Info(fmt.Sprintf("%s (%s) 追踪完成,结果: %s", model.Ipv6Names[i], model.Ipv6s[i], tempText))
|
||||
Logger.Info(fmt.Sprintf("%s (%s) 追踪完成,最终结果: %s", model.Ipv6Names[i], model.Ipv6s[i], tempText))
|
||||
}
|
||||
ch <- Result{i + offset, tempText}
|
||||
} else {
|
||||
|
73
bk/utils.go
73
bk/utils.go
@ -140,3 +140,76 @@ func tryAlternativeIPs(targetName string, ipVersion string) []string {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mergeHops 合并多个hops结果
|
||||
func mergeHops(allHops [][]*Hop) []*Hop {
|
||||
if len(allHops) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(allHops) == 1 {
|
||||
return allHops[0]
|
||||
}
|
||||
// 找到最大长度
|
||||
maxLen := 0
|
||||
for _, hops := range allHops {
|
||||
if len(hops) > maxLen {
|
||||
maxLen = len(hops)
|
||||
}
|
||||
}
|
||||
var mergedHops []*Hop
|
||||
// 逐位置合并
|
||||
for pos := 0; pos < maxLen; pos++ {
|
||||
var availableHops []*Hop
|
||||
// 收集当前位置所有可用的hop
|
||||
for _, hops := range allHops {
|
||||
if pos < len(hops) {
|
||||
availableHops = append(availableHops, hops[pos])
|
||||
}
|
||||
}
|
||||
if len(availableHops) == 0 {
|
||||
continue
|
||||
}
|
||||
// 如果只有一个可用hop,直接使用
|
||||
if len(availableHops) == 1 {
|
||||
mergedHops = append(mergedHops, availableHops[0])
|
||||
continue
|
||||
}
|
||||
// 统计相同的hop(通过比较第一个node的IP)
|
||||
hopCount := make(map[string][]*Hop)
|
||||
for _, hop := range availableHops {
|
||||
var key string
|
||||
if len(hop.Nodes) > 0 && hop.Nodes[0].IP != nil {
|
||||
key = hop.Nodes[0].IP.String()
|
||||
} else {
|
||||
key = "*" // 超时或无响应
|
||||
}
|
||||
if _, exists := hopCount[key]; !exists {
|
||||
hopCount[key] = make([]*Hop, 0)
|
||||
}
|
||||
hopCount[key] = append(hopCount[key], hop)
|
||||
}
|
||||
// 按多数原则选择hop
|
||||
if len(hopCount) == 1 {
|
||||
// 所有hop都相同,选择第一个
|
||||
mergedHops = append(mergedHops, availableHops[0])
|
||||
} else {
|
||||
// 找出最多的hop类型
|
||||
maxCount := 0
|
||||
var majorityHops []*Hop
|
||||
for _, hops := range hopCount {
|
||||
if len(hops) > maxCount {
|
||||
maxCount = len(hops)
|
||||
majorityHops = hops
|
||||
}
|
||||
}
|
||||
// 如果有多数派,使用多数派的第一个
|
||||
if maxCount > 1 || len(hopCount) == 2 {
|
||||
mergedHops = append(mergedHops, majorityHops[0])
|
||||
} else {
|
||||
// 三个都不同,按请求早晚顺序选择第一个
|
||||
mergedHops = append(mergedHops, availableHops[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
return mergedHops
|
||||
}
|
||||
|
17
cmd/main.go
17
cmd/main.go
@ -1,4 +1,5 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
@ -7,12 +8,14 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/oneclickvirt/backtrace/bgptools"
|
||||
backtrace "github.com/oneclickvirt/backtrace/bk"
|
||||
"github.com/oneclickvirt/backtrace/model"
|
||||
"github.com/oneclickvirt/backtrace/utils"
|
||||
. "github.com/oneclickvirt/defaultset"
|
||||
)
|
||||
|
||||
type IpInfo struct {
|
||||
Ip string `json:"ip"`
|
||||
City string `json:"city"`
|
||||
@ -20,6 +23,7 @@ type IpInfo struct {
|
||||
Country string `json:"country"`
|
||||
Org string `json:"org"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
go func() {
|
||||
http.Get("https://hits.spiritlhl.net/backtrace.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false")
|
||||
@ -48,11 +52,11 @@ func main() {
|
||||
if showIpInfo {
|
||||
rsp, err := http.Get("http://ipinfo.io")
|
||||
if err != nil {
|
||||
fmt.Errorf("Get ip info err %v \n", err.Error())
|
||||
fmt.Printf("get ip info err %v \n", err.Error())
|
||||
} else {
|
||||
err = json.NewDecoder(rsp.Body).Decode(&info)
|
||||
if err != nil {
|
||||
fmt.Errorf("json decode err %v \n", err.Error())
|
||||
fmt.Printf("json decode err %v \n", err.Error())
|
||||
} else {
|
||||
fmt.Println(Green("国家: ") + White(info.Country) + Green(" 城市: ") + White(info.City) +
|
||||
Green(" 服务商: ") + Blue(info.Org))
|
||||
@ -75,11 +79,14 @@ func main() {
|
||||
}
|
||||
}
|
||||
if preCheck.Connected && preCheck.StackType == "DualStack" {
|
||||
backtrace.BackTrace(ipv6)
|
||||
result := backtrace.BackTrace(ipv6)
|
||||
fmt.Printf("%s\n", result)
|
||||
} else if preCheck.Connected && preCheck.StackType == "IPv4" {
|
||||
backtrace.BackTrace(false)
|
||||
result := backtrace.BackTrace(false)
|
||||
fmt.Printf("%s\n", result)
|
||||
} else if preCheck.Connected && preCheck.StackType == "IPv6" {
|
||||
backtrace.BackTrace(true)
|
||||
result := backtrace.BackTrace(true)
|
||||
fmt.Printf("%s\n", result)
|
||||
} else {
|
||||
fmt.Println(Red("PreCheck IP Type Failed"))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user