mirror of
https://github.com/oneclickvirt/backtrace.git
synced 2025-05-09 12:29:59 +08:00
feat: 添加IPV6路由追踪包的构建逻辑
This commit is contained in:
parent
7a644b403e
commit
b93b8fbb4a
30
bk/asn.go
30
bk/asn.go
@ -14,19 +14,30 @@ type Result struct {
|
||||
}
|
||||
|
||||
var (
|
||||
ips = []string{
|
||||
ipv4s = []string{
|
||||
// "219.141.136.12", "202.106.50.1",
|
||||
"219.141.140.10", "202.106.195.68", "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{
|
||||
ipv6s = []string{
|
||||
"2408:80f0:4100:2005::10", // 北京电信 IPv6
|
||||
"2408:8000:1010:1::6", // 北京联通 IPv6
|
||||
"2409:8000:1003:5::5", // 北京移动 IPv6
|
||||
"2408:8026:1:1::6", // 上海联通 IPv6
|
||||
"2409:8089:1020:50::6", // 上海移动 IPv6
|
||||
}
|
||||
ipv4Names = []string{
|
||||
"北京电信", "北京联通", "北京移动",
|
||||
"上海电信", "上海联通", "上海移动",
|
||||
"广州电信", "广州联通", "广州移动",
|
||||
"成都电信", "成都联通", "成都移动",
|
||||
}
|
||||
ipv6Names = []string{
|
||||
"北京电信v6", "北京联通v6", "北京移动v6",
|
||||
"上海联通v6", "上海移动v6",
|
||||
}
|
||||
m = map[string]string{
|
||||
// [] 前的字符串个数,中文占2个字符串
|
||||
"AS23764": "电信CTGNET [精品线路]",
|
||||
@ -56,9 +67,9 @@ func removeDuplicates(elements []string) []string {
|
||||
}
|
||||
|
||||
func trace(ch chan Result, i int) {
|
||||
hops, err := Trace(net.ParseIP(ips[i]))
|
||||
hops, err := Trace(net.ParseIP(ipv4s[i]))
|
||||
if err != nil {
|
||||
s := fmt.Sprintf("%v %-15s %v", names[i], ips[i], err)
|
||||
s := fmt.Sprintf("%v %-15s %v", ipv4Names[i], ipv4s[i], err)
|
||||
ch <- Result{i, s}
|
||||
return
|
||||
}
|
||||
@ -75,7 +86,7 @@ func trace(ch chan Result, i int) {
|
||||
if asns != nil && len(asns) > 0 {
|
||||
var tempText string
|
||||
asns = removeDuplicates(asns)
|
||||
tempText += fmt.Sprintf("%v ", names[i])
|
||||
tempText += fmt.Sprintf("%v ", ipv4Names[i])
|
||||
hasAS4134 := false
|
||||
hasAS4809 := false
|
||||
for _, asn := range asns {
|
||||
@ -94,7 +105,7 @@ func trace(ch chan Result, i int) {
|
||||
// 仅包含 AS4809 属于 CN2GIA
|
||||
asns = append([]string{"AS4809a"}, asns...)
|
||||
}
|
||||
tempText += fmt.Sprintf("%-15s ", ips[i])
|
||||
tempText += fmt.Sprintf("%-15s ", ipv4s[i])
|
||||
for _, asn := range asns {
|
||||
asnDescription := m[asn]
|
||||
switch asn {
|
||||
@ -128,17 +139,20 @@ func trace(ch chan Result, i int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if tempText == (fmt.Sprintf("%v ", names[i]) + fmt.Sprintf("%-15s ", ips[i])) {
|
||||
if tempText == (fmt.Sprintf("%v ", ipv4Names[i]) + fmt.Sprintf("%-15s ", ipv4s[i])) {
|
||||
tempText += fmt.Sprintf("%v", Red("检测不到已知线路的ASN"))
|
||||
}
|
||||
ch <- Result{i, tempText}
|
||||
} else {
|
||||
s := fmt.Sprintf("%v %-15s %v", names[i], ips[i], Red("检测不到回程路由节点的IP地址"))
|
||||
s := fmt.Sprintf("%v %-15s %v", ipv4Names[i], ipv4s[i], Red("检测不到回程路由节点的IP地址"))
|
||||
ch <- Result{i, s}
|
||||
}
|
||||
}
|
||||
|
||||
func ipAsn(ip string) string {
|
||||
if strings.Contains(ip, ":") {
|
||||
return ipv6Asn(ip)
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(ip, "59.43"):
|
||||
return "AS4809"
|
||||
|
@ -6,24 +6,30 @@ import (
|
||||
)
|
||||
|
||||
func BackTrace() {
|
||||
var (
|
||||
s [12]string // 对应 ips 目标地址数量
|
||||
c = make(chan Result)
|
||||
t = time.After(time.Second * 10)
|
||||
)
|
||||
for i := range ips {
|
||||
go trace(c, i)
|
||||
}
|
||||
// 获取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 {
|
||||
fmt.Println(r)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
40
bk/ipv6_asn.go
Normal file
40
bk/ipv6_asn.go
Normal file
@ -0,0 +1,40 @@
|
||||
package backtrace
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 识别IPv6地址的ASN
|
||||
func ipv6Asn(ip string) string {
|
||||
switch {
|
||||
// 电信CN2GIA
|
||||
case strings.HasPrefix(ip, "2408:80"):
|
||||
return "AS4809a"
|
||||
// 电信CN2GT
|
||||
case strings.HasPrefix(ip, "2408:8000"):
|
||||
return "AS4809b"
|
||||
// 电信163
|
||||
case strings.HasPrefix(ip, "240e:") || strings.HasPrefix(ip, "2408:8"):
|
||||
return "AS4134"
|
||||
// 联通9929
|
||||
case strings.HasPrefix(ip, "2408:8026:"):
|
||||
return "AS9929"
|
||||
// 联通4837
|
||||
case strings.HasPrefix(ip, "2408:8000:"):
|
||||
return "AS4837"
|
||||
// 移动CMIN2
|
||||
case strings.HasPrefix(ip, "2409:8880:"):
|
||||
return "AS58807"
|
||||
// 移动CMI
|
||||
case strings.HasPrefix(ip, "2409:8000:") || strings.HasPrefix(ip, "2409:"):
|
||||
return "AS9808"
|
||||
// 移动CMI
|
||||
case strings.HasPrefix(ip, "2407:") || strings.HasPrefix(ip, "2401:"):
|
||||
return "AS58453"
|
||||
// 电信CTGNET
|
||||
case strings.HasPrefix(ip, "2402:0:") || strings.HasPrefix(ip, "2400:8:"):
|
||||
return "AS23764"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
@ -17,11 +17,11 @@ import (
|
||||
|
||||
// DefaultConfig is the default configuration for Tracer.
|
||||
var DefaultConfig = Config{
|
||||
Delay: 50 * time.Millisecond,
|
||||
Timeout: 500 * time.Millisecond,
|
||||
MaxHops: 15,
|
||||
Count: 1,
|
||||
Networks: []string{"ip4:icmp", "ip4:ip"},
|
||||
Delay: 50 * time.Millisecond,
|
||||
Timeout: 500 * time.Millisecond,
|
||||
MaxHops: 15,
|
||||
Count: 1,
|
||||
Networks: []string{"ip4:icmp", "ip4:ip", "ip6:ipv6-icmp", "ip6:ip"},
|
||||
}
|
||||
|
||||
// DefaultTracer is a tracer with DefaultConfig.
|
||||
@ -126,17 +126,6 @@ func (t *Tracer) init() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 初始化IPv6 ICMP连接
|
||||
c, err := net.ListenPacket("ip6:ipv6-icmp", "")
|
||||
if err == nil {
|
||||
p := ipv6.NewPacketConn(c)
|
||||
if err := p.SetControlMessage(ipv6.FlagHopLimit|ipv6.FlagSrc|ipv6.FlagDst|ipv6.FlagInterface, true); err == nil {
|
||||
t.ipv6conn = p
|
||||
go t.serveIPv6(p)
|
||||
} else {
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes listening socket.
|
||||
@ -167,55 +156,73 @@ func (t *Tracer) serve(conn *net.IPConn) error {
|
||||
}
|
||||
|
||||
func (t *Tracer) serveData(from net.IP, b []byte) error {
|
||||
if from.To4() == nil {
|
||||
// TODO: implement ProtocolIPv6ICMP
|
||||
return errUnsupportedProtocol
|
||||
}
|
||||
now := time.Now()
|
||||
msg, err := icmp.ParseMessage(ProtocolICMP, b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if msg.Type == ipv4.ICMPTypeEchoReply {
|
||||
echo := msg.Body.(*icmp.Echo)
|
||||
return t.serveReply(from, &packet{from, uint16(echo.ID), 1, now})
|
||||
}
|
||||
b = getReplyData(msg)
|
||||
if len(b) < ipv4.HeaderLen {
|
||||
return errMessageTooShort
|
||||
}
|
||||
switch b[0] >> 4 {
|
||||
case ipv4.Version:
|
||||
ip, err := ipv4.ParseHeader(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return t.serveReply(ip.Dst, &packet{from, uint16(ip.ID), ip.TTL, 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:
|
||||
return errUnsupportedProtocol
|
||||
}
|
||||
if from.To4() == nil {
|
||||
// IPv6 处理
|
||||
msg, err := icmp.ParseMessage(ProtocolIPv6ICMP, b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if msg.Type == ipv6.ICMPTypeEchoReply {
|
||||
echo := msg.Body.(*icmp.Echo)
|
||||
return t.serveReply(from, &packet{from, uint16(echo.ID), 1, time.Now()})
|
||||
}
|
||||
b = getReplyData(msg)
|
||||
if len(b) < ipv6.HeaderLen {
|
||||
return errMessageTooShort
|
||||
}
|
||||
switch b[0] >> 4 {
|
||||
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, time.Now()})
|
||||
default:
|
||||
return errUnsupportedProtocol
|
||||
}
|
||||
} else {
|
||||
// 原有的IPv4处理逻辑
|
||||
msg, err := icmp.ParseMessage(ProtocolICMP, b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if msg.Type == ipv4.ICMPTypeEchoReply {
|
||||
echo := msg.Body.(*icmp.Echo)
|
||||
return t.serveReply(from, &packet{from, uint16(echo.ID), 1, time.Now()})
|
||||
}
|
||||
b = getReplyData(msg)
|
||||
if len(b) < ipv4.HeaderLen {
|
||||
return errMessageTooShort
|
||||
}
|
||||
switch b[0] >> 4 {
|
||||
case ipv4.Version:
|
||||
ip, err := ipv4.ParseHeader(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return t.serveReply(ip.Dst, &packet{from, uint16(ip.ID), ip.TTL, time.Now()})
|
||||
default:
|
||||
return errUnsupportedProtocol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tracer) sendRequest(dst net.IP, ttl int) (*packet, error) {
|
||||
if dst.To4() == nil {
|
||||
// IPv6
|
||||
return t.sendRequestV6(dst, ttl)
|
||||
}
|
||||
// Ipv4
|
||||
id := uint16(atomic.AddUint32(&t.seq, 1))
|
||||
b := newPacketV4(id, dst, ttl)
|
||||
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
|
||||
id := uint16(atomic.AddUint32(&t.seq, 1))
|
||||
var b []byte
|
||||
if dst.To4() == nil {
|
||||
// IPv6
|
||||
b = newPacketV6(id, dst, ttl)
|
||||
} else {
|
||||
// IPv4
|
||||
b = newPacketV4(id, dst, ttl)
|
||||
}
|
||||
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) {
|
||||
|
119
bk/trace_ipv6.go
119
bk/trace_ipv6.go
@ -2,124 +2,29 @@ package backtrace
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
func newPacketV6(id uint16, dst net.IP, ttl int) ([]byte, error) {
|
||||
// 创建ICMPv6 Echo请求消息
|
||||
func newPacketV6(id uint16, dst net.IP, ttl int) []byte {
|
||||
msg := icmp.Message{
|
||||
Type: ipv6.ICMPTypeEchoRequest,
|
||||
Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: int(id),
|
||||
Seq: int(id),
|
||||
Data: []byte("TRACEROUTE"),
|
||||
ID: int(id),
|
||||
Seq: int(id),
|
||||
},
|
||||
}
|
||||
|
||||
// 直接序列化ICMPv6消息
|
||||
// 第一个参数是协议号,对于ICMPv6应该是58
|
||||
return msg.Marshal(nil)
|
||||
}
|
||||
|
||||
func (t *Tracer) sendRequestV6(dst net.IP, ttl int) (*packet, error) {
|
||||
id := uint16(atomic.AddUint32(&t.seq, 1))
|
||||
// 创建ICMPv6消息
|
||||
msg := icmp.Message{
|
||||
Type: ipv6.ICMPTypeEchoRequest,
|
||||
Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: int(id),
|
||||
Seq: int(id),
|
||||
Data: []byte("TRACEROUTE"),
|
||||
},
|
||||
p, _ := msg.Marshal(nil)
|
||||
ip := &ipv6.Header{
|
||||
Version: ipv6.Version,
|
||||
NextHeader: ProtocolIPv6ICMP,
|
||||
HopLimit: ttl,
|
||||
Dst: dst,
|
||||
}
|
||||
// 序列化ICMPv6消息
|
||||
b, err := msg.Marshal(nil)
|
||||
buf, err := ip.Marshal() //TODO 修复
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 获取底层连接
|
||||
ipv6Conn, err := t.getIPv6Conn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 设置IPv6数据包的跳数限制(TTL)
|
||||
if err := ipv6Conn.SetHopLimit(ttl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 发送数据包
|
||||
if _, err := ipv6Conn.WriteTo(b, nil, &net.IPAddr{IP: dst}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 创建一个数据包记录,用于后续匹配回复
|
||||
req := &packet{dst, id, ttl, time.Now()}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// getIPv6Conn 获取IPv6的PacketConn接口
|
||||
func (t *Tracer) getIPv6Conn() (*ipv6.PacketConn, error) {
|
||||
if t.ipv6conn != nil {
|
||||
return t.ipv6conn, nil
|
||||
}
|
||||
// 创建一个UDP连接
|
||||
c, err := net.ListenPacket("udp6", "::")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 将其包装为IPv6 PacketConn
|
||||
p := ipv6.NewPacketConn(c)
|
||||
// 设置控制消息
|
||||
if err := p.SetControlMessage(ipv6.FlagHopLimit|ipv6.FlagSrc|ipv6.FlagDst|ipv6.FlagInterface, true); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
t.ipv6conn = p
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (t *Tracer) serveIPv6(conn *ipv6.PacketConn) error {
|
||||
defer conn.Close()
|
||||
buf := make([]byte, 1500)
|
||||
for {
|
||||
n, cm, src, err := conn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 从控制消息中获取跳数限制
|
||||
hopLimit := 0
|
||||
if cm != nil {
|
||||
hopLimit = cm.HopLimit
|
||||
}
|
||||
// 解析ICMP消息
|
||||
msg, err := icmp.ParseMessage(ProtocolIPv6ICMP, buf[:n])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// 根据消息类型处理
|
||||
switch msg.Type {
|
||||
case ipv6.ICMPTypeEchoReply:
|
||||
echo := msg.Body.(*icmp.Echo)
|
||||
t.serveReply(src.(*net.IPAddr).IP, &packet{src.(*net.IPAddr).IP, uint16(echo.ID), hopLimit, time.Now()})
|
||||
case ipv6.ICMPTypeTimeExceeded:
|
||||
// 处理超时消息,获取原始数据包
|
||||
data := msg.Body.(*icmp.TimeExceeded).Data
|
||||
// 尝试提取嵌入的原始Echo请求
|
||||
if len(data) < 8 { // 至少需要IPv6头部前8个字节
|
||||
continue
|
||||
}
|
||||
// 跳过IPv6头部和ICMPv6头部,简化处理,实际可能需要更复杂的解析
|
||||
innerMsg, err := icmp.ParseMessage(ProtocolIPv6ICMP, data[48:])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if echo, ok := innerMsg.Body.(*icmp.Echo); ok {
|
||||
t.serveReply(src.(*net.IPAddr).IP, &packet{src.(*net.IPAddr).IP, uint16(echo.ID), hopLimit, time.Now()})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return append(buf, p...)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user