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,10 +55,10 @@ backtrace
```
Usage: backtrace [options]
-e Enable logging
-h Show help information
-s Disabe show ip info (default true)
-v Show version
-log Enable logging
-h Show help information
-s Disabe show ip info (default true)
-v Show version
```
## 卸载

View File

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

View File

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

View File

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

View File

@ -2,42 +2,43 @@ package backtrace
import (
"strings"
_ "embed"
)
//go:embed bk/prefix/as4809.txt
//go:embed prefix/as4809.txt
var as4809Data string
//go:embed bk/prefix/as4134.txt
//go:embed prefix/as4134.txt
var as4134Data string
//go:embed bk/prefix/as9929.txt
//go:embed prefix/as9929.txt
var as9929Data string
//go:embed bk/prefix/as4837.txt
//go:embed prefix/as4837.txt
var as4837Data string
//go:embed bk/prefix/as58807.txt
//go:embed prefix/as58807.txt
var as58807Data string
//go:embed bk/prefix/as9808.txt
//go:embed prefix/as9808.txt
var as9808Data string
//go:embed bk/prefix/as58453.txt
//go:embed prefix/as58453.txt
var as58453Data string
//go:embed bk/prefix/as23764.txt
//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
"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 中的某个前缀

View File

@ -116,16 +116,27 @@ func (t *Tracer) NewSession(ip net.IP) (*Session, error) {
}
func (t *Tracer) init() {
// 初始化IPv4连接
for _, network := range t.Networks {
if strings.HasPrefix(network, "ip4") {
t.conn, t.err = t.listen(network, t.Addr)
if t.err == nil {
go t.serve(t.conn)
break
}
}
}
// 初始化IPv4连接
for _, network := range t.Networks {
if strings.HasPrefix(network, "ip4") {
t.conn, t.err = t.listen(network, t.Addr)
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)
go t.serveIPv6(t.ipv6conn)
break
}
}
}
}
// Close closes listening socket.
@ -210,19 +221,30 @@ func (t *Tracer) serveData(from net.IP, b []byte) error {
func (t *Tracer) sendRequest(dst net.IP, ttl int) (*packet, error) {
id := uint16(atomic.AddUint32(&t.seq, 1))
var b []byte
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 {
return nil, err
}
return req, nil
}
return nil, errors.New("IPv6 connection not available")
} else {
// IPv4
b = newPacketV4(id, dst, ttl)
_, err := t.conn.WriteToIP(b, &net.IPAddr{IP: dst})
if err != nil {
return nil, err
}
return req, nil
}
req := &packet{dst, id, ttl, time.Now()}
_, err := t.conn.WriteToIP(b, &net.IPAddr{IP: dst})
if err != nil {
return nil, err
}
return req, nil
}
func (t *Tracer) addSession(s *Session) {

View File

@ -2,8 +2,11 @@ package backtrace
import (
"encoding/binary"
"fmt"
"net"
"strings"
. "github.com/oneclickvirt/defaultset"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv6"
)
@ -34,3 +37,103 @@ func newPacketV6(id uint16, dst net.IP, ttl int) []byte {
// 合并头部和ICMP数据
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")
}()
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.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(&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:])
if help {
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("同一目标地址多个线路时,可能检测已越过汇聚层,除了第一个线路外,后续信息可能无效"))
if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {