feat: 添加IPV6路由追踪包的构建逻辑

This commit is contained in:
spiritlhl 2025-04-05 05:13:56 +00:00
parent 7a644b403e
commit b93b8fbb4a
5 changed files with 168 additions and 196 deletions

View File

@ -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"

View File

@ -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
View 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 ""
}
}

View File

@ -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) {

View File

@ -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...)
}