Compare commits

...

6 Commits

Author SHA1 Message Date
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
8 changed files with 232 additions and 72 deletions

View File

@ -55,7 +55,7 @@ backtrace
``` ```
Usage: backtrace [options] Usage: backtrace [options]
-e Enable logging -log Enable logging
-h Show help information -h Show help information
-s Disabe show ip info (default true) -s Disabe show ip info (default true)
-v Show version -v Show version

View File

@ -22,10 +22,10 @@ var (
"61.139.2.69", "119.6.6.6", "211.137.96.205", "61.139.2.69", "119.6.6.6", "211.137.96.205",
} }
ipv4Names = []string{ ipv4Names = []string{
"北京电信", "北京联通", "北京移动", "北京电信v4", "北京联通v4", "北京移动v4",
"上海电信", "上海联通", "上海移动", "上海电信v4", "上海联通v4", "上海移动v4",
"广州电信", "广州联通", "广州移动", "广州电信v4", "广州联通v4", "广州移动v4",
"成都电信", "成都联通", "成都移动", "成都电信v4", "成都联通v4", "成都移动v4",
} }
ipv6s = []string{ ipv6s = []string{
"2400:89c0:1053:3::69", // 北京电信 IPv6 "2400:89c0:1053:3::69", // 北京电信 IPv6

View File

@ -5,31 +5,64 @@ import (
"time" "time"
) )
func BackTrace() { func BackTrace(test bool) {
// 获取IP地址数量 if test {
ipCount := len(ipv4s) ipv4Count := len(ipv4s)
ipv6Count := len(ipv6s)
totalCount := ipv4Count + ipv6Count
var ( var (
s = make([]string, ipCount) // 动态分配切片大小 s = make([]string, totalCount)
c = make(chan Result) c = make(chan Result)
t = time.After(time.Second * 10) t = time.After(time.Second * 10)
) )
for i := range ipv4s { for i := range ipv4s {
go trace(c, i) go trace(c, i)
} }
for i := range ipv6s {
loop: go traceIPv6(c, i, ipv4Count)
}
loopIPv4v6:
for range s { for range s {
select { select {
case o := <-c: case o := <-c:
s[o.i] = o.s s[o.i] = o.s
case <-t: case <-t:
break loop 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(ipv4s)
var (
s = make([]string, ipCount)
c = make(chan Result)
t = time.After(time.Second * 10)
)
for i := range 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 { for _, r := range s {
if r != "" { if r != "" {
fmt.Println(r) fmt.Println(r)
} }
} }
} }
}

View File

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

View File

@ -2,30 +2,31 @@ package backtrace
import ( import (
"strings" "strings"
_ "embed"
) )
//go:embed bk/prefix/as4809.txt //go:embed prefix/as4809.txt
var as4809Data string var as4809Data string
//go:embed bk/prefix/as4134.txt //go:embed prefix/as4134.txt
var as4134Data string var as4134Data string
//go:embed bk/prefix/as9929.txt //go:embed prefix/as9929.txt
var as9929Data string var as9929Data string
//go:embed bk/prefix/as4837.txt //go:embed prefix/as4837.txt
var as4837Data string var as4837Data string
//go:embed bk/prefix/as58807.txt //go:embed prefix/as58807.txt
var as58807Data string var as58807Data string
//go:embed bk/prefix/as9808.txt //go:embed prefix/as9808.txt
var as9808Data string var as9808Data string
//go:embed bk/prefix/as58453.txt //go:embed prefix/as58453.txt
var as58453Data string var as58453Data string
//go:embed bk/prefix/as23764.txt //go:embed prefix/as23764.txt
var as23764Data string var as23764Data string
// ASN -> Prefix strings // ASN -> Prefix strings

View File

@ -126,6 +126,17 @@ func (t *Tracer) init() {
} }
} }
} }
// 初始化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)
go t.serveIPv6(t.ipv6conn)
break
}
}
}
} }
// Close closes listening socket. // Close closes listening socket.
@ -210,20 +221,31 @@ func (t *Tracer) serveData(from net.IP, b []byte) error {
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))
var b []byte var b []byte
req := &packet{dst, id, ttl, time.Now()}
if dst.To4() == nil { if dst.To4() == nil {
// IPv6 // IPv6
b = newPacketV6(id, dst, ttl) 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 {
return nil, err
}
return req, nil
}
return nil, errors.New("IPv6 connection not available")
} else { } else {
// IPv4 // IPv4
b = newPacketV4(id, dst, ttl) b = newPacketV4(id, dst, ttl)
}
req := &packet{dst, id, ttl, time.Now()}
_, 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()

View File

@ -2,8 +2,11 @@ package backtrace
import ( import (
"encoding/binary" "encoding/binary"
"fmt"
"net" "net"
"strings"
. "github.com/oneclickvirt/defaultset"
"golang.org/x/net/icmp" "golang.org/x/net/icmp"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
) )
@ -34,3 +37,103 @@ func newPacketV6(id uint16, dst net.IP, ttl int) []byte {
// 合并头部和ICMP数据 // 合并头部和ICMP数据
return append(ipHeaderBytes, icmpData...) return append(ipHeaderBytes, icmpData...)
} }
func (t *Tracer) serveIPv6(conn *ipv6.PacketConn) error {
defer conn.Close()
buf := make([]byte, 1500)
for {
n, _, from, err := conn.ReadFrom(buf)
if err != nil {
return err
}
fromIP := from.(*net.IPAddr).IP
err = t.serveData(fromIP, buf[:n])
if err != nil {
continue
}
}
}
// IPv6追踪函数
func traceIPv6(ch chan Result, i int, offset int) {
hops, err := Trace(net.ParseIP(ipv6s[i]))
if err != nil {
s := fmt.Sprintf("%v %-40s %v", ipv6Names[i], ipv6s[i], err)
ch <- Result{i + offset, s}
return
}
var asns []string
for _, h := range hops {
for _, n := range h.Nodes {
asn := ipAsn(n.IP.String())
if asn != "" {
asns = append(asns, asn)
}
}
}
// 处理路由信息
if asns != nil && len(asns) > 0 {
var tempText string
asns = removeDuplicates(asns)
tempText += fmt.Sprintf("%v ", 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...)
} else if hasAS4809 {
// 仅包含 AS4809 属于 CN2GIA
asns = append([]string{"AS4809a"}, asns...)
}
tempText += fmt.Sprintf("%-40s ", ipv6s[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 "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 ", ipv6Names[i]) + fmt.Sprintf("%-40s ", ipv6s[i])) {
tempText += fmt.Sprintf("%v", Red("检测不到已知线路的ASN"))
}
ch <- Result{i + offset, tempText}
} else {
s := fmt.Sprintf("%v %-40s %v", ipv6Names[i], ipv6s[i], Red("检测不到回程路由节点的IP地址"))
ch <- Result{i + offset, s}
}
}

View File

@ -25,12 +25,13 @@ func main() {
http.Get("https://hits.spiritlhl.net/backtrace.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&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"))
var showVersion, showIpInfo, help bool var showVersion, showIpInfo, help, test bool
backtraceFlag := flag.NewFlagSet("backtrace", flag.ContinueOnError) backtraceFlag := flag.NewFlagSet("backtrace", flag.ContinueOnError)
backtraceFlag.BoolVar(&help, "h", false, "Show help information") backtraceFlag.BoolVar(&help, "h", false, "Show help information")
backtraceFlag.BoolVar(&showVersion, "v", false, "Show version") backtraceFlag.BoolVar(&showVersion, "v", false, "Show version")
backtraceFlag.BoolVar(&showIpInfo, "s", true, "Disabe show ip info") backtraceFlag.BoolVar(&showIpInfo, "s", true, "Disabe show ip info")
backtraceFlag.BoolVar(&backtrace.EnableLoger, "e", false, "Enable logging") backtraceFlag.BoolVar(&backtrace.EnableLoger, "log", false, "Enable logging")
backtraceFlag.BoolVar(&test, "test", false, "Test Mode")
backtraceFlag.Parse(os.Args[1:]) backtraceFlag.Parse(os.Args[1:])
if help { if help {
fmt.Printf("Usage: %s [options]\n", os.Args[0]) fmt.Printf("Usage: %s [options]\n", os.Args[0])
@ -56,7 +57,7 @@ func main() {
} }
} }
} }
backtrace.BackTrace() backtrace.BackTrace(test)
fmt.Println(Yellow("准确线路自行查看详细路由,本测试结果仅作参考")) fmt.Println(Yellow("准确线路自行查看详细路由,本测试结果仅作参考"))
fmt.Println(Yellow("同一目标地址多个线路时,可能检测已越过汇聚层,除了第一个线路外,后续信息可能无效")) fmt.Println(Yellow("同一目标地址多个线路时,可能检测已越过汇聚层,除了第一个线路外,后续信息可能无效"))
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {