mirror of
				https://github.com/oneclickvirt/backtrace.git
				synced 2025-11-04 15:52:37 +08:00 
			
		
		
		
	fix: 修复间接上游识别
This commit is contained in:
		
							parent
							
								
									461c28f3d1
								
							
						
					
					
						commit
						cfed3329d2
					
				@ -15,13 +15,10 @@
 | 
			
		||||
- [x] 支持对```CTGNET```、```CN2GIA```和```CN2GT```线路的判断
 | 
			
		||||
- [x] 支持对```CMIN2```和```CMI```线路的判断
 | 
			
		||||
- [x] 支持对整个回程路由进行线路分析,一个目标IP可能会分析出多种线路
 | 
			
		||||
- [x] 支持对主流接入点的线路检测,方便分析国际互联能力
 | 
			
		||||
- [x] 增加对全平台的编译支持,原版[backtrace](https://github.com/zhanghanyun/backtrace)仅支持linux平台的amd64和arm64架构
 | 
			
		||||
- [x] 兼容额外的ICMP地址获取,若当前目标IP无法查询路由尝试额外的IP地址
 | 
			
		||||
 | 
			
		||||
## TODO
 | 
			
		||||
 | 
			
		||||
- [ ] 添加对主流ISP的POP点检测,区分国际互联能力
 | 
			
		||||
 | 
			
		||||
## 使用
 | 
			
		||||
 | 
			
		||||
下载、安装、更新
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										164
									
								
								bgptools/pop.go
									
									
									
									
									
								
							
							
						
						
									
										164
									
								
								bgptools/pop.go
									
									
									
									
									
								
							@ -10,6 +10,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/imroc/req/v3"
 | 
			
		||||
	"github.com/oneclickvirt/backtrace/model"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ASCard struct {
 | 
			
		||||
@ -39,82 +40,32 @@ type PoPResult struct {
 | 
			
		||||
	Result    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var tier1Global = map[string]string{
 | 
			
		||||
	"174":   "Cogent",
 | 
			
		||||
	"1299":  "Arelion",
 | 
			
		||||
	"3356":  "Lumen",
 | 
			
		||||
	"3257":  "GTT",
 | 
			
		||||
	"7018":  "AT&T",
 | 
			
		||||
	"701":   "Verizon",
 | 
			
		||||
	"2914":  "NTT",
 | 
			
		||||
	"6453":  "Tata",
 | 
			
		||||
	"3320":  "DTAG",
 | 
			
		||||
	"5511":  "Orange",
 | 
			
		||||
	"3491":  "PCCW",
 | 
			
		||||
	"6461":  "Zayo",
 | 
			
		||||
	"6830":  "Liberty",
 | 
			
		||||
	"6762":  "Sparkle",
 | 
			
		||||
	"12956": "Telxius",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var tier1Regional = map[string]string{
 | 
			
		||||
	"4134": "ChinaNet",
 | 
			
		||||
	"4837": "China Unicom",
 | 
			
		||||
	"9808": "China Mobile",
 | 
			
		||||
	"4766": "Korea Telecom",
 | 
			
		||||
	"2516": "KDDI",
 | 
			
		||||
	"7713": "Telkomnet",
 | 
			
		||||
	"9121": "Etisalat",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var tier2 = map[string]string{
 | 
			
		||||
	"6939":  "Hurricane Electric",
 | 
			
		||||
	"20485": "Transtelecom",
 | 
			
		||||
	"1273":  "Vodafone",
 | 
			
		||||
	"1239":  "Sprint",
 | 
			
		||||
	"6762":  "Sparkle",
 | 
			
		||||
	"6453":  "Tata",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var contentProviders = map[string]string{
 | 
			
		||||
	"15169": "Google",
 | 
			
		||||
	"32934": "Facebook",
 | 
			
		||||
	"54113": "Fastly",
 | 
			
		||||
	"20940": "Akamai",
 | 
			
		||||
	"13335": "Cloudflare",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var ixps = map[string]string{
 | 
			
		||||
	"5539":  "IX.br",
 | 
			
		||||
	"25291": "HKIX",
 | 
			
		||||
	"1200":  "AMS-IX",
 | 
			
		||||
	"6695":  "DE-CIX",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getISPAbbr(asn, name string) string {
 | 
			
		||||
	if abbr, ok := tier1Global[asn]; ok {
 | 
			
		||||
	if abbr, ok := model.Tier1Global[asn]; ok {
 | 
			
		||||
		return abbr
 | 
			
		||||
	}
 | 
			
		||||
	if idx := strings.Index(name, " "); idx != -1 {
 | 
			
		||||
	if idx := strings.Index(name, " "); idx != -1 && idx > 18 {
 | 
			
		||||
		return name[:idx]
 | 
			
		||||
	}
 | 
			
		||||
	return name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getISPType(asn string, tier1 bool) string {
 | 
			
		||||
func getISPType(asn string, tier1 bool, direct bool) string {
 | 
			
		||||
	switch {
 | 
			
		||||
	case tier1 && tier1Global[asn] != "":
 | 
			
		||||
	case tier1 && model.Tier1Global[asn] != "":
 | 
			
		||||
		return "Tier1 Global"
 | 
			
		||||
	case tier1Regional[asn] != "":
 | 
			
		||||
	case model.Tier1Regional[asn] != "":
 | 
			
		||||
		return "Tier1 Regional"
 | 
			
		||||
	case tier2[asn] != "":
 | 
			
		||||
	case model.Tier2[asn] != "":
 | 
			
		||||
		return "Tier2"
 | 
			
		||||
	case contentProviders[asn] != "":
 | 
			
		||||
	case model.ContentProviders[asn] != "":
 | 
			
		||||
		return "CDN Provider"
 | 
			
		||||
	case ixps[asn] != "":
 | 
			
		||||
	case model.IXPS[asn] != "":
 | 
			
		||||
		return "IXP"
 | 
			
		||||
	default:
 | 
			
		||||
	case direct:
 | 
			
		||||
		return "Direct"
 | 
			
		||||
	default:
 | 
			
		||||
		return "Indirect"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -236,7 +187,7 @@ func findUpstreams(targetASN string, nodes []ASCard, edges []Arrow) []Upstream {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		isTier1 := (n.Fill == "white" && n.Stroke == "#005ea5")
 | 
			
		||||
		upstreamType := getISPType(n.ASN, isTier1)
 | 
			
		||||
		upstreamType := getISPType(n.ASN, isTier1, true)
 | 
			
		||||
		upstreams = append(upstreams, Upstream{
 | 
			
		||||
			ASN:    n.ASN,
 | 
			
		||||
			Name:   n.Name,
 | 
			
		||||
@ -245,6 +196,55 @@ func findUpstreams(targetASN string, nodes []ASCard, edges []Arrow) []Upstream {
 | 
			
		||||
			Type:   upstreamType,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	if len(upstreams) == 1 {
 | 
			
		||||
		currentASN := upstreams[0].ASN
 | 
			
		||||
		for {
 | 
			
		||||
			nextUpstreams := map[string]bool{}
 | 
			
		||||
			for _, e := range edges {
 | 
			
		||||
				if e.From == currentASN {
 | 
			
		||||
					nextUpstreams[e.To] = true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if len(nextUpstreams) != 1 {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			var nextASN string
 | 
			
		||||
			for asn := range nextUpstreams {
 | 
			
		||||
				nextASN = asn
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			found := false
 | 
			
		||||
			for _, existing := range upstreams {
 | 
			
		||||
				if existing.ASN == nextASN {
 | 
			
		||||
					found = true
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if found {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			var nextNode *ASCard
 | 
			
		||||
			for _, n := range nodes {
 | 
			
		||||
				if n.ASN == nextASN {
 | 
			
		||||
					nextNode = &n
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if nextNode == nil {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			isTier1 := (nextNode.Fill == "white" && nextNode.Stroke == "#005ea5")
 | 
			
		||||
			upstreamType := getISPType(nextNode.ASN, isTier1, false)
 | 
			
		||||
			upstreams = append(upstreams, Upstream{
 | 
			
		||||
				ASN:    nextNode.ASN,
 | 
			
		||||
				Name:   nextNode.Name,
 | 
			
		||||
				Direct: false,
 | 
			
		||||
				Tier1:  isTier1,
 | 
			
		||||
				Type:   upstreamType,
 | 
			
		||||
			})
 | 
			
		||||
			currentASN = nextASN
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return upstreams
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -270,33 +270,39 @@ func GetPoPInfo(ip string) (*PoPResult, error) {
 | 
			
		||||
		return nil, fmt.Errorf("无法识别目标 ASN")
 | 
			
		||||
	}
 | 
			
		||||
	upstreams := findUpstreams(targetASN, nodes, edges)
 | 
			
		||||
	if len(upstreams) > 5 {
 | 
			
		||||
		upstreams = upstreams[:5]
 | 
			
		||||
	}
 | 
			
		||||
	colWidth := 16
 | 
			
		||||
	colWidth := 18
 | 
			
		||||
	center := func(s string) string {
 | 
			
		||||
		runeLen := len([]rune(s))
 | 
			
		||||
		if runeLen >= colWidth {
 | 
			
		||||
			return s[:colWidth]
 | 
			
		||||
			return string([]rune(s)[:colWidth])
 | 
			
		||||
		}
 | 
			
		||||
		padding := colWidth - runeLen
 | 
			
		||||
		left := padding / 2
 | 
			
		||||
		right := padding - left
 | 
			
		||||
		return strings.Repeat(" ", left) + s + strings.Repeat(" ", right)
 | 
			
		||||
	}
 | 
			
		||||
	var line1, line2, line3 []string
 | 
			
		||||
	for _, u := range upstreams {
 | 
			
		||||
		abbr := getISPAbbr(u.ASN, u.Name)
 | 
			
		||||
		line1 = append(line1, center("AS"+u.ASN))
 | 
			
		||||
		line2 = append(line2, center(abbr))
 | 
			
		||||
		line3 = append(line3, center(u.Type))
 | 
			
		||||
	}
 | 
			
		||||
	var result strings.Builder
 | 
			
		||||
	result.WriteString(strings.Join(line1, ""))
 | 
			
		||||
	result.WriteString("\n")
 | 
			
		||||
	result.WriteString(strings.Join(line2, ""))
 | 
			
		||||
	result.WriteString("\n")
 | 
			
		||||
	result.WriteString(strings.Join(line3, ""))
 | 
			
		||||
	perLine := 5
 | 
			
		||||
	for i := 0; i < len(upstreams); i += perLine {
 | 
			
		||||
		end := i + perLine
 | 
			
		||||
		if end > len(upstreams) {
 | 
			
		||||
			end = len(upstreams)
 | 
			
		||||
		}
 | 
			
		||||
		batch := upstreams[i:end]
 | 
			
		||||
		var line1, line2, line3 []string
 | 
			
		||||
		for _, u := range batch {
 | 
			
		||||
			abbr := getISPAbbr(u.ASN, u.Name)
 | 
			
		||||
			line1 = append(line1, center("AS"+u.ASN))
 | 
			
		||||
			line2 = append(line2, center(abbr))
 | 
			
		||||
			line3 = append(line3, center(u.Type))
 | 
			
		||||
		}
 | 
			
		||||
		result.WriteString(strings.Join(line1, ""))
 | 
			
		||||
		result.WriteString("\n")
 | 
			
		||||
		result.WriteString(strings.Join(line2, ""))
 | 
			
		||||
		result.WriteString("\n")
 | 
			
		||||
		result.WriteString(strings.Join(line3, ""))
 | 
			
		||||
		result.WriteString("\n")
 | 
			
		||||
	}
 | 
			
		||||
	return &PoPResult{
 | 
			
		||||
		TargetASN: targetASN,
 | 
			
		||||
		Upstreams: upstreams,
 | 
			
		||||
 | 
			
		||||
@ -6,12 +6,14 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestGetPoPInfo(t *testing.T) {
 | 
			
		||||
	result, err := GetPoPInfo("66.70.153.71")
 | 
			
		||||
	result, err := GetPoPInfo("206.190.233.1")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println(err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("目标 ASN: %s\n", result.TargetASN)
 | 
			
		||||
	fmt.Println(len(result.Upstreams))
 | 
			
		||||
	fmt.Println(result.Upstreams)
 | 
			
		||||
	fmt.Println("上游信息:")
 | 
			
		||||
	fmt.Print(result.Result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								cmd/main.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								cmd/main.go
									
									
									
									
									
								
							@ -9,6 +9,7 @@ import (
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/oneclickvirt/backtrace/bgptools"
 | 
			
		||||
	backtrace "github.com/oneclickvirt/backtrace/bk"
 | 
			
		||||
	"github.com/oneclickvirt/backtrace/model"
 | 
			
		||||
	"github.com/oneclickvirt/backtrace/utils"
 | 
			
		||||
@ -45,12 +46,12 @@ func main() {
 | 
			
		||||
		fmt.Println(model.BackTraceVersion)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	info := IpInfo{}
 | 
			
		||||
	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())
 | 
			
		||||
@ -61,6 +62,15 @@ func main() {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	preCheck := utils.CheckPublicAccess(3 * time.Second)
 | 
			
		||||
	if preCheck.Connected && info.Ip != "" {
 | 
			
		||||
		result, err := bgptools.GetPoPInfo(info.Ip)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println(err.Error())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Println("上游信息:")
 | 
			
		||||
		fmt.Print(result.Result)
 | 
			
		||||
	}
 | 
			
		||||
	if preCheck.Connected && preCheck.StackType == "DualStack" {
 | 
			
		||||
		backtrace.BackTrace(ipv6)
 | 
			
		||||
	} else if preCheck.Connected && preCheck.StackType == "IPv4" {
 | 
			
		||||
 | 
			
		||||
@ -76,3 +76,82 @@ var (
 | 
			
		||||
	CachedIcmpDataFetchTime time.Time
 | 
			
		||||
	ParsedIcmpTargets       []IcmpTarget
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var Tier1Global = map[string]string{
 | 
			
		||||
	"174":   "Cogent",
 | 
			
		||||
	"1299":  "Arelion",
 | 
			
		||||
	"3356":  "Lumen",
 | 
			
		||||
	"3257":  "GTT",
 | 
			
		||||
	"7018":  "AT&T",
 | 
			
		||||
	"701":   "Verizon",
 | 
			
		||||
	"2914":  "NTT",
 | 
			
		||||
	"6453":  "Tata",
 | 
			
		||||
	"3320":  "DTAG",
 | 
			
		||||
	"5511":  "Orange",
 | 
			
		||||
	"3491":  "PCCW",
 | 
			
		||||
	"6461":  "Zayo",
 | 
			
		||||
	"6830":  "Liberty",
 | 
			
		||||
	"6762":  "Sparkle",
 | 
			
		||||
	"12956": "Telxius",
 | 
			
		||||
	"702":   "Verizon",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var Tier1Regional = map[string]string{
 | 
			
		||||
	"4134":  "ChinaNet",
 | 
			
		||||
	"4837":  "China Unicom",
 | 
			
		||||
	"9808":  "China Mobile",
 | 
			
		||||
	"4766":  "Korea Telecom",
 | 
			
		||||
	"2516":  "KDDI",
 | 
			
		||||
	"7713":  "Telkomnet",
 | 
			
		||||
	"9121":  "Etisalat",
 | 
			
		||||
	"7473":  "SingTel",
 | 
			
		||||
	"4637":  "Telstra",
 | 
			
		||||
	"5400":  "British Telecom",
 | 
			
		||||
	"2497":  "IIJ",
 | 
			
		||||
	"3462":  "Chunghwa Telecom",
 | 
			
		||||
	"3463":  "TWNIC",
 | 
			
		||||
	"12389": "SoftBank",
 | 
			
		||||
	"3303":  "MTS",
 | 
			
		||||
	"45609": "Reliance Jio",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var Tier2 = map[string]string{
 | 
			
		||||
	"6939":  "HurricaneElectric",
 | 
			
		||||
	"20485": "Transtelecom",
 | 
			
		||||
	"1273":  "Vodafone",
 | 
			
		||||
	"1239":  "Sprint",
 | 
			
		||||
	"6453":  "Tata",
 | 
			
		||||
	"6762":  "Sparkle",
 | 
			
		||||
	"9002":  "RETN",
 | 
			
		||||
	"7922":  "Comcast",
 | 
			
		||||
	"23754": "Rostelecom",
 | 
			
		||||
	"3320":  "DTAG",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var ContentProviders = map[string]string{
 | 
			
		||||
	"15169": "Google",
 | 
			
		||||
	"32934": "Facebook",
 | 
			
		||||
	"54113": "Fastly",
 | 
			
		||||
	"20940": "Akamai",
 | 
			
		||||
	"13335": "Cloudflare",
 | 
			
		||||
	"14618": "Amazon AWS",
 | 
			
		||||
	"55102": "Netflix CDN",
 | 
			
		||||
	"4685":  "CacheFly",
 | 
			
		||||
	"16509": "Amazon",
 | 
			
		||||
	"36040": "Amazon CloudFront",
 | 
			
		||||
	"36459": "EdgeCast",
 | 
			
		||||
	"24940": "CDNetworks",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var IXPS = map[string]string{
 | 
			
		||||
	"5539":   "IX.br",
 | 
			
		||||
	"25291":  "HKIX",
 | 
			
		||||
	"1200":   "AMS-IX",
 | 
			
		||||
	"6695":   "DE-CIX",
 | 
			
		||||
	"58558":  "LINX",
 | 
			
		||||
	"395848": "France-IX",
 | 
			
		||||
	"4713":   "JPNAP",
 | 
			
		||||
	"4635":   "SIX",
 | 
			
		||||
	"2906":   "MSK-IX",
 | 
			
		||||
	"1273":   "NIX.CZ",
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user