Compare commits

..

38 Commits

Author SHA1 Message Date
sjlleo
c7df49ca8e improve: #4 case insensitive 2022-05-24 09:05:56 +08:00
sjlleo
4d3a52bb28 Add: IP Database Copyright 2022-05-24 08:55:41 +08:00
sjlleo
fbf7335bc8 update: "./" is no longer need here 2022-05-24 05:37:19 +08:00
sjlleo
d60c80e1ff Add files via upload 2022-05-23 21:28:10 +08:00
sjlleo
4f622b7afb Add files via upload 2022-05-23 21:27:50 +08:00
sjlleo
f51b07d388 Add files via upload 2022-05-23 21:17:43 +08:00
sjlleo
679dbbf01a Delete screenshot2.png 2022-05-23 21:17:18 +08:00
sjlleo
2a5e5572b4 update: fix command not found bug 2022-05-23 21:03:44 +08:00
sjlleo
974655a1b3 update: todo update version 2022-05-23 20:55:11 +08:00
sjlleo
eb9844f924 Add: screenshots 2022-05-23 20:41:48 +08:00
sjlleo
b8a51b3c44 add: screenshots 2022-05-23 20:40:37 +08:00
sjlleo
5d984fc3ac Merge pull request #3 from tsosunchia/main
对安装脚本一点小改善
2022-05-23 20:33:30 +08:00
tsosunchia
bcea9aa2cb 安装脚本增加checkVersion为之后增加自动更新功能做准备;优化了部分逻辑结构;在部分软件包管理器增加了安装依赖包之前更新软件源的步骤 2022-05-23 20:26:53 +08:00
sjlleo
ffc2c33e63 Merge pull request #2 from tsosunchia/main
修正安装脚本在macOS运行中出现的BUG
2022-05-23 19:55:39 +08:00
tsosunchia
919335133e 修正脚本在macOS运行中出现的BUG 2022-05-23 19:29:38 +08:00
sjlleo
4d0e3eb93d update: 修改用法 2022-05-23 18:10:40 +08:00
sjlleo
56d2f0554f update: sed is uncompatible in macOS, with 1:-1 instead (Thanks to nsnnns) 2022-05-23 18:07:48 +08:00
zhshch2002
e728b6b063 refactor: icmp 2022-05-23 17:35:40 +08:00
zhshch2002
8b2d2f3990 Merge branch 'main' of https://github.com/xgadget-lab/nexttrace 2022-05-23 17:01:15 +08:00
zhshch2002
4c51b2fbbe add: realtime and table output mode
Co-authored-by: sjlleo <sjlleo@users.noreply.github.com>
2022-05-23 17:00:24 +08:00
sjlleo
9a6586f27a update: import jq package 2022-05-23 16:01:16 +08:00
sjlleo
cc8e3e4838 update: 修复命令在直接使用sudo的情况下不可用 2022-05-23 10:32:26 +08:00
sjlleo
d69b7b9acb update: 新增国内镜像加速 2022-05-23 10:18:45 +08:00
sjlleo
483a90848d update: fix a bash problem 2022-05-23 10:05:48 +08:00
sjlleo
131a9e2e8a update: add some description 2022-05-23 09:47:06 +08:00
sjlleo
982e1064c2 update: sudo check first 2022-05-23 09:45:59 +08:00
sjlleo
5ff461af42 update: apt update first 2022-05-23 09:43:45 +08:00
sjlleo
8adc98a753 update: One-Key Install Script 2022-05-23 09:41:42 +08:00
sjlleo
937113ca33 add: 一键安装、升级脚本 2022-05-23 09:39:23 +08:00
zhshch2002
9f0c62506e update: README.md 快速安装脚本 2022-05-22 21:47:56 +08:00
sjlleo
cad5f944cb 完善 2022-05-22 21:23:39 +08:00
sjlleo
0f0fb91fb6 Add: Thank Member 2022-05-22 20:26:48 +08:00
sjlleo
e1c6f1ccf6 add: Thanks Member 2022-05-22 20:23:05 +08:00
sjlleo
5ac811bbae update: 完善ReadMe描述 2022-05-22 20:18:28 +08:00
zhshch2002
dbd8ae573c Merge branch 'main' of https://github.com/xgadget-lab/nexttrace 2022-05-22 20:03:43 +08:00
sjlleo
8db4c5e7b8 Add: logo.svg file 2022-05-22 20:02:54 +08:00
zhshch2002
7db2a717a4 Merge branch 'main' of https://github.com/xgadget-lab/nexttrace 2022-05-22 20:02:47 +08:00
zhshch2002
b0ba116c91 update: README.md 2022-05-22 20:01:48 +08:00
16 changed files with 476 additions and 169 deletions

105
README.md
View File

@@ -1,14 +1,67 @@
<div align="center">
<img src="asset/logo.png" height="200px"/>
</div>
# NextTrace
可视化路由跟踪工具
一款开源的可视化路由跟踪工具使用Golang开发。
## How To Use
### Install
```bash
bash -c "$(curl -Ls https://raw.githubusercontent.com/xgadget-lab/nexttrace/main/nt_install.sh)"
```
### Get Started
`NextTrace`默认使用`icmp`协议发起`TraceRoute`请求,该协议同时支持`IPv4``IPv6`
```bash
# IPv4 ICMP Trace
nexttrace 1.0.0.1
# 获得 route-path
nexttrace -report 1.0.0.1
# 表格打印一次性输出全部跳数需等待20-40秒
nexttrace -table 1.0.0.1
# IPv6 ICMP Trace
nexttrace 2606:4700:4700::1111
```
`NextTrace`也可以使用`TCP``UDP`协议发起`Traceroute`请求,不过目前只支持`IPv4`
```bash
# TCP SYN Trace
nexttrace -T www.bing.com
# 可以自行指定端口[此处为443]默认80端口
nexttrace -T -p 443 1.0.0.1
# UDP Trace
nexttrace -U 1.0.0.1
nexttrace -U -p 53 1.0.0.1
```
### IP数据库
目前使用的IP数据库默认为我们自己搭建的API服务如果后期遇到滥用我们可能会选择关闭。
我们也会在后期开放服务端源代码您也可以根据该项目的源码自行搭建属于您的API服务器。
NextTrace所有的的IP地理位置`API DEMO`可以参考[这里](https://github.com/xgadget-lab/nexttrace/blob/main/ipgeo/)
### 全部用法详见Usage菜单
```shell
NextTrace v0.1.0 Alpha
xgadget-lab zhshch (xzhsh.ch) & leo (leo.moe)
Usage of nexttrace:
-T Use TCP SYN for tracerouting (default port is 80 in TCP, 53 in UDP)
-T Use TCP SYN for tracerouting (default port is 80)
-U Use UDP Package for tracerouting (default port is 53 in UDP)
-d string
Choose IP Geograph Data Provider [LeoMoeAPI, IP.SB, IPInfo, IPInsight] (default "LeoMoeAPI")
-displayMode string
@@ -24,10 +77,50 @@ Usage of nexttrace:
-rdns
Set whether rDNS will be display
-report
Auto-Generate a Route-Path Report by Traceroute
Route Path
```
## 项目截图
![](asset/screenshot2.png)
![](asset/screenshot3.png)
![](asset/screenshot.png)
## Thanks
Vincent Young (i@yyt.moe)
[Sam Sam](https://github.com/samleong123) (samsam123@samsam123.name.my)
[Vincent Young](https://github.com/missuo) (i@yyt.moe)
[waiting4new](https://github.com/waiting4new)
FFEE_CO
nsnnns
## IP Database Copyright
### IPv4 Database
#### China MainLand
* 项目组自行维护 ~ 御三家骨干网数据 ~ 5%
* 埃文科技 Paid Database ~ 95%
#### WorldWide
* 埃文科技 Paid Database ~ 15%
* IpInfo Free ~ 15%
* IPInSight Free ~ 70%
### IPv6 Database
This product includes IP2Location LITE data available from <a href="https://lite.ip2location.com">https://lite.ip2location.com</a>.
### Others
其他第三方API尽管集成在本项目内但是具体的TOS以及AUP请详见第三方API官网。如遇到IP数据错误也请直接联系他们纠错。

BIN
asset/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
asset/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 KiB

BIN
asset/screenshot2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

BIN
asset/screenshot3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

2
go.mod
View File

@@ -5,13 +5,13 @@ go 1.18
require (
github.com/google/gopacket v1.1.19
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
)
require (
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/panjf2000/ants/v2 v2.5.0 // indirect
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
)
require (

2
go.sum
View File

@@ -1,5 +1,6 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
@@ -53,4 +54,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,5 +1,7 @@
package ipgeo
import "strings"
type IPGeoData struct {
Asnumber string
Country string
@@ -13,12 +15,12 @@ type IPGeoData struct {
type Source = func(ip string) (*IPGeoData, error)
func GetSource(s string) Source {
switch s {
case "LeoMoeAPI":
switch strings.ToUpper(s) {
case "LEOMOEAPI":
return LeoIP
case "IP.SB":
return IPSB
case "IPInsight":
case "IPINSIGHT":
return IPInSight
default:
return nil

View File

@@ -3,21 +3,25 @@ package ipgeo
import (
"io/ioutil"
"net/http"
"time"
"github.com/tidwall/gjson"
)
func IPSB(ip string) (*IPGeoData, error) {
resp, err := http.Get("https://api.ip.sb/geoip/" + ip)
url := "https://api.ip.sb/geoip/" + ip
client := &http.Client{
// 2秒超时
Timeout: 2 * time.Second,
}
req, _ := http.NewRequest("GET", url, nil)
// 设置 UAip.sb 默认禁止 go-client User-Agent 的 api 请求
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0")
content, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
body, _ := ioutil.ReadAll(content.Body)
res := gjson.ParseBytes(body)
return &IPGeoData{

21
main.go
View File

@@ -21,9 +21,10 @@ var numMeasurements = flag.Int("q", 3, "Set the number of probes per each hop.")
var parallelRequests = flag.Int("r", 18, "Set ParallelRequests number. It should be 1 when there is a multi-routing.")
var maxHops = flag.Int("m", 30, "Set the max number of hops (max TTL to be reached).")
var dataOrigin = flag.String("d", "LeoMoeAPI", "Choose IP Geograph Data Provider [LeoMoeAPI, IP.SB, IPInfo, IPInsight]")
var displayMode = flag.String("displayMode", "table", "Choose The Display Mode [table, classic]")
var rdnsenable = flag.Bool("rdns", false, "Set whether rDNS will be display")
var routePath = flag.Bool("report", false, "Route Path")
var realtimePrint = flag.Bool("realtime", false, "Output trace results in runtime")
var tablePrint = flag.Bool("table", false, "Output trace results as table")
func flagApply() string {
flag.Parse()
@@ -68,8 +69,10 @@ func main() {
RDns: *rdnsenable,
IPGeoSource: ipgeo.GetSource(*dataOrigin),
Timeout: 2 * time.Second,
RoutePath: *routePath,
//Quic: false,
}
if m == trace.ICMPTrace && !*tablePrint {
conf.RealtimePrinter = printer.RealtimePrinter
}
res, err := trace.Traceroute(m, conf)
@@ -84,9 +87,15 @@ func main() {
return
}
if *displayMode == "table" {
if m == trace.ICMPTrace && *tablePrint {
printer.TracerouteTablePrinter(res)
} else {
printer.TraceroutePrinter(res)
}
if m == trace.TCPTrace || m == trace.UDPTrace {
if *realtimePrint {
printer.TraceroutePrinter(res)
} else {
printer.TracerouteTablePrinter(res)
}
}
}

266
nt_install.sh Normal file
View File

@@ -0,0 +1,266 @@
#!/bin/bash
usrPath="/usr/local/bin"
checkRootPermit() {
[[ $EUID -ne 0 ]] && echo "请使用sudo/root权限运行本脚本" && exit 1
}
checkSystemArch() {
arch=$(uname -m)
if [[ $arch == "x86_64" ]]; then
archParam="amd64"
fi
if [[ $arch == "aarch64" ]]; then
archParam="arm64"
fi
if [[ $arch == "arm64" ]]; then
archParam="arm64"
fi
if [[ $archParam == "" ]]; then
echo "未知的系统架构,请联系作者"
exit 1
fi
}
checkSystemDistribution() {
case "$OSTYPE" in
darwin*)
osDistribution="darwin"
downPath="/var/tmp/nexttrace"
;;
linux*)
osDistribution="linux"
downPath="/var/tmp/nexttrace"
;;
*)
echo "unknown: $OSTYPE"
exit 1
;;
esac
}
getLocation() {
echo "正在获取地理位置信息..."
countryCode=$(curl -s "http://ip-api.com/line/?fields=countryCode")
}
installWgetPackage() {
# macOS should install wget originally. Nothing to do
echo "wget 正在安装中..."
# try apt
apt-get -h &>/dev/null
if [ $? -eq 0 ]; then
# 先更新一下数据源有些机器数据源比较老可能会404
apt-get update -y &>/dev/null
apt-get install wget -y &>/dev/null
fi
# try yum
yum -h &>/dev/null
if [ $? -eq 0 ]; then
yum -y update &>/dev/null
yum install wget -y &>/dev/null
fi
# try dnf
dnf -h &>/dev/null
if [ $? -eq 0 ]; then
dnf check-update &>/dev/null
dnf install wget -y &>/dev/null
fi
# try pacman
pacman -h &>/dev/null
if [ $? -eq 0 ]; then
pacman -Sy &>/dev/null
pacman -S wget &>/dev/null
fi
wget -h &>/dev/null
if [ $? -ne 0 ]; then
echo "wget 安装失败"
exit 1
fi
}
installJqPackage() {
# macOS should install wget originally. Nothing to do
echo "jq 正在安装中..."
# try apt
apt-get -h &>/dev/null
if [ $? -eq 0 ]; then
# 先更新一下数据源有些机器数据源比较老可能会404
apt-get update -y &>/dev/null
apt-get install jq -y &>/dev/null
fi
# try yum
yum -h &>/dev/null
if [ $? -eq 0 ]; then
yum -y update &>/dev/null
yum install jq -y &>/dev/null
fi
# try dnf
dnf -h &>/dev/null
if [ $? -eq 0 ]; then
dnf check-update &>/dev/null
dnf install jq -y &>/dev/null
fi
# try pacman
pacman -h &>/dev/null
if [ $? -eq 0 ]; then
pacman -Sy &>/dev/null
pacman -S jq &>/dev/null
fi
jq -h &>/dev/null
if [ $? -ne 0 ]; then
echo "jq 安装失败"
exit 1
fi
}
checkWgetPackage() {
wget -h &>/dev/null
if [ $? -ne 0 ]; then
read -r -p "您还没有安装wget是否安装? (y/n)" input
case $input in
[yY][eE][sS] | [yY])
installWgetPackage
;;
[nN][oO] | [nN])
echo "您选择了取消安装,脚本即将退出"
exit 1
;;
*)
installWgetPackage
;;
esac
fi
}
checkVersion() {
echo "正在检查版本..."
version=$(curl -sL https://api.github.com/repos/xgadget-lab/nexttrace/releases/latest | jq -r '.tag_name')
if [[ $version == "" ]]; then
echo "获取版本失败,请检查网络连接"
exit 1
fi
echo 当前最新release版本${version}
read -r -p "是否安装/更新软件? (y/n)" input
case $input in
[yY][eE][sS] | [yY])
break
;;
[nN][oO] | [nN])
echo "您选择了取消安装/更新,脚本即将退出"
exit 1
;;
*)
break
;;
esac
}
checkJqPackage() {
jq -h &>/dev/null
if [ $? -ne 0 ]; then
echo "您还没有安装jq 当您取消安装我们会使用awk获取当前版本号。"
read -r -p "但是如遇Github变更API这可能会存在问题是否安装? (y/n)" input
case $input in
[yY][eE][sS] | [yY])
installJqPackage
;;
[nN][oO] | [nN])
echo "您选择了取消安装"
return 0
;;
*)
installJqPackage
;;
esac
fi
return 1
}
downloadBinrayFile() {
echo "正在获取最新版的 NextTrace 发行版文件信息..."
checkJqPackage
# 简单说明一下Github提供了一个API可以获取最新发行版本的二进制文件下载地址对应的是browser_download_url根据刚刚测得的osDistribution、archParam获取对应的下载地址
if [[ $? -eq 1 ]]; then
# 支持 jq 不回退
# echo nexttrace_${osDistribution}_${archParam}
latestURL=$(curl -s https://api.github.com/repos/xgadget-lab/nexttrace/releases/latest | jq ".assets[] | select(.name == \"nexttrace_${osDistribution}_${archParam}\") | .browser_download_url")
latestURL=${latestURL:1:-1}
else
# 不支持 jq用户拒绝安装回退 awk
latestURL=$(curl -s https://api.github.com/repos/xgadget-lab/nexttrace/releases/latest | grep -i "browser_download_url.*${osDistribution}.*${archParam}" | awk -F '"' '{print $4}')
fi
if [ "$countryCode" == "CN" ]; then
read -r -p "检测到国内网络环境,是否使用镜像下载以加速(y/n)" input
case $input in
[yY][eE][sS] | [yY])
latestURL="https://ghproxy.com/"$latestURL
;;
[nN][oO] | [nN])
echo "您选择了不使用镜像,下载可能会变得异常缓慢,或者失败"
;;
*)
latestURL="https://ghproxy.com/"$latestURL
;;
esac
fi
echo "正在下载 NextTrace 二进制文件..."
wget -O ${downPath} ${latestURL} &>/dev/null
if [ $? -eq 0 ]; then
echo "NextTrace 现在已经在您的系统中可用"
changeMode
mv ${downPath} ${usrPath}
if [[ ${osDistribution} == "macOS" ]]; then
xattr -r -d com.apple.quarantine ${usrPath}/nexttrace
fi
else
echo "NextTrace 下载失败,请检查您的网络是否正常"
exit 1
fi
}
changeMode() {
chmod +x ${downPath} &>/dev/null
}
runBinrayFileHelp() {
if [ -e ${usrPath} ]; then
${usrPath}/nexttrace -h
fi
}
# Check Procedure
checkRootPermit
checkSystemDistribution
checkSystemArch
checkWgetPackage
# TODO: 检查版本并更新
#checkVersion
# Download Procedure
getLocation
downloadBinrayFile
# Run Procedure
runBinrayFileHelp

View File

@@ -15,12 +15,12 @@ func TraceroutePrinter(res *trace.Result) {
for i, hop := range res.Hops {
fmt.Print(i + 1)
for _, h := range hop {
hopPrinter(h)
HopPrinter(h)
}
}
}
func hopPrinter(h trace.Hop) {
func HopPrinter(h trace.Hop) {
if h.Address == nil {
fmt.Println("\t*")
} else {

View File

@@ -0,0 +1,15 @@
package printer
import (
"fmt"
"github.com/xgadget-lab/nexttrace/trace"
)
func RealtimePrinter(res *trace.Result, ttl int) {
fmt.Print(ttl)
for i := range res.Hops[ttl] {
HopPrinter(res.Hops[ttl][i])
}
}

View File

@@ -1,38 +1,26 @@
package trace
import (
"fmt"
"log"
"net"
"os"
"strconv"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"golang.org/x/sync/semaphore"
)
type ICMPTracer struct {
Config
wg sync.WaitGroup
res Result
ctx context.Context
inflightRequest map[int]chan Hop
inflightRequestLock sync.Mutex
icmpListen net.PacketConn
workFork workFork
final int
finalLock sync.Mutex
sem *semaphore.Weighted
}
type workFork struct {
ttl int
num int
wg sync.WaitGroup
res Result
ctx context.Context
resCh chan Hop
icmpListen net.PacketConn
final int
finalLock sync.Mutex
}
func (t *ICMPTracer) Execute() (*Result, error) {
@@ -51,21 +39,24 @@ func (t *ICMPTracer) Execute() (*Result, error) {
var cancel context.CancelFunc
t.ctx, cancel = context.WithCancel(context.Background())
defer cancel()
t.inflightRequest = make(map[int]chan Hop)
t.resCh = make(chan Hop)
t.final = -1
go t.listenICMP()
t.sem = semaphore.NewWeighted(int64(t.ParallelRequests))
for t.workFork.ttl = 1; t.workFork.ttl <= t.MaxHops; t.workFork.ttl++ {
for ttl := 1; ttl <= t.MaxHops; ttl++ {
if t.final != -1 && ttl > t.final {
break
}
for i := 0; i < t.NumMeasurements; i++ {
t.wg.Add(1)
go t.send(workFork{t.workFork.ttl, i})
go t.send(ttl)
}
// 一组TTL全部退出收到应答或者超时终止以后再进行下一个TTL的包发送
t.wg.Wait()
t.workFork.num = 0
if t.RealtimePrinter != nil {
t.RealtimePrinter(&t.res, ttl-1)
}
}
t.res.reduce(t.final)
@@ -102,29 +93,15 @@ func (t *ICMPTracer) listenICMP() {
}
func (t *ICMPTracer) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte) {
t.inflightRequestLock.Lock()
defer t.inflightRequestLock.Unlock()
ch, ok := t.inflightRequest[t.workFork.num]
t.workFork.num += 1
if !ok {
return
}
ch <- Hop{
t.resCh <- Hop{
Success: true,
Address: msg.Peer,
}
}
func (t *ICMPTracer) send(fork workFork) error {
err := t.sem.Acquire(context.Background(), 1)
if err != nil {
return err
}
defer t.sem.Release(1)
func (t *ICMPTracer) send(ttl int) error {
defer t.wg.Done()
if t.final != -1 && fork.ttl > t.final {
if t.final != -1 && ttl > t.final {
return nil
}
@@ -136,7 +113,7 @@ func (t *ICMPTracer) send(fork workFork) error {
},
}
ipv4.NewPacketConn(t.icmpListen).SetTTL(fork.ttl)
ipv4.NewPacketConn(t.icmpListen).SetTTL(ttl)
wb, err := icmpHeader.Marshal(nil)
if err != nil {
@@ -150,69 +127,48 @@ func (t *ICMPTracer) send(fork workFork) error {
if err := t.icmpListen.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
log.Fatal(err)
}
t.inflightRequestLock.Lock()
hopCh := make(chan Hop)
t.inflightRequest[fork.num] = hopCh
t.inflightRequestLock.Unlock()
// defer func() {
// t.inflightRequestLock.Lock()
// close(hopCh)
// delete(t.inflightRequest, fork.ttl)
// t.inflightRequestLock.Unlock()
// }()
if fork.num == 0 && t.Config.RoutePath {
fmt.Print(strconv.Itoa(fork.ttl))
}
select {
case <-t.ctx.Done():
return nil
case h := <-hopCh:
case h := <-t.resCh:
rtt := time.Since(start)
if t.final != -1 && fork.ttl > t.final {
if t.final != -1 && ttl > t.final {
return nil
}
if addr, ok := h.Address.(*net.IPAddr); ok && addr.IP.Equal(t.DestIP) {
t.finalLock.Lock()
if t.final == -1 || fork.ttl < t.final {
t.final = fork.ttl
if t.final == -1 || ttl < t.final {
t.final = ttl
}
t.finalLock.Unlock()
} else if addr, ok := h.Address.(*net.TCPAddr); ok && addr.IP.Equal(t.DestIP) {
t.finalLock.Lock()
if t.final == -1 || fork.ttl < t.final {
t.final = fork.ttl
if t.final == -1 || ttl < t.final {
t.final = ttl
}
t.finalLock.Unlock()
}
h.TTL = fork.ttl
h.TTL = ttl
h.RTT = rtt
h.fetchIPData(t.Config)
if t.Config.RoutePath {
HopPrinter(h)
}
t.res.add(h)
case <-time.After(t.Timeout):
if t.final != -1 && fork.ttl > t.final {
if t.final != -1 && ttl > t.final {
return nil
}
t.res.add(Hop{
Success: false,
Address: nil,
TTL: fork.ttl,
TTL: ttl,
RTT: 0,
Error: ErrHopLimitTimeout,
})
if t.Config.RoutePath {
fmt.Println("\t" + "*")
}
}
return nil

View File

@@ -1,33 +1,26 @@
package trace
import (
"fmt"
"log"
"net"
"os"
"strconv"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv6"
"golang.org/x/sync/semaphore"
)
type ICMPTracerv6 struct {
Config
wg sync.WaitGroup
res Result
ctx context.Context
inflightRequest map[int]chan Hop
inflightRequestLock sync.Mutex
icmpListen net.PacketConn
workFork workFork
final int
finalLock sync.Mutex
sem *semaphore.Weighted
wg sync.WaitGroup
res Result
ctx context.Context
resCh chan Hop
icmpListen net.PacketConn
final int
finalLock sync.Mutex
}
func (t *ICMPTracerv6) Execute() (*Result, error) {
@@ -46,21 +39,24 @@ func (t *ICMPTracerv6) Execute() (*Result, error) {
var cancel context.CancelFunc
t.ctx, cancel = context.WithCancel(context.Background())
defer cancel()
t.inflightRequest = make(map[int]chan Hop)
t.resCh = make(chan Hop)
t.final = -1
go t.listenICMP()
t.sem = semaphore.NewWeighted(int64(t.ParallelRequests))
for t.workFork.ttl = 1; t.workFork.ttl <= t.MaxHops; t.workFork.ttl++ {
for ttl := 1; ttl <= t.MaxHops; ttl++ {
if t.final != -1 && ttl > t.final {
break
}
for i := 0; i < t.NumMeasurements; i++ {
t.wg.Add(1)
go t.send(workFork{t.workFork.ttl, i})
go t.send(ttl)
}
// 一组TTL全部退出收到应答或者超时终止以后再进行下一个TTL的包发送
t.wg.Wait()
t.workFork.num = 0
if t.RealtimePrinter != nil {
t.RealtimePrinter(&t.res, ttl-1)
}
}
t.res.reduce(t.final)
@@ -98,30 +94,15 @@ func (t *ICMPTracerv6) listenICMP() {
}
func (t *ICMPTracerv6) handleICMPMessage(msg ReceivedMessage, icmpType int8, data []byte) {
t.inflightRequestLock.Lock()
defer t.inflightRequestLock.Unlock()
ch, ok := t.inflightRequest[t.workFork.num]
t.workFork.num += 1
if !ok {
return
}
ch <- Hop{
t.resCh <- Hop{
Success: true,
Address: msg.Peer,
}
}
func (t *ICMPTracerv6) send(fork workFork) error {
err := t.sem.Acquire(context.Background(), 1)
if err != nil {
return err
}
defer t.sem.Release(1)
func (t *ICMPTracerv6) send(ttl int) error {
defer t.wg.Done()
if t.final != -1 && fork.ttl > t.final {
if t.final != -1 && ttl > t.final {
return nil
}
@@ -135,8 +116,8 @@ func (t *ICMPTracerv6) send(fork workFork) error {
p := ipv6.NewPacketConn(t.icmpListen)
icmpHeader.Body.(*icmp.Echo).Seq = fork.ttl
p.SetHopLimit(fork.ttl)
icmpHeader.Body.(*icmp.Echo).Seq = ttl
p.SetHopLimit(ttl)
wb, err := icmpHeader.Marshal(nil)
if err != nil {
@@ -150,69 +131,48 @@ func (t *ICMPTracerv6) send(fork workFork) error {
if err := t.icmpListen.SetReadDeadline(time.Now().Add(3 * time.Second)); err != nil {
log.Fatal(err)
}
t.inflightRequestLock.Lock()
hopCh := make(chan Hop)
t.inflightRequest[fork.num] = hopCh
t.inflightRequestLock.Unlock()
// defer func() {
// t.inflightRequestLock.Lock()
// close(hopCh)
// delete(t.inflightRequest, fork.ttl)
// t.inflightRequestLock.Unlock()
// }()
if fork.num == 0 && t.Config.RoutePath {
fmt.Print(strconv.Itoa(fork.ttl))
}
select {
case <-t.ctx.Done():
return nil
case h := <-hopCh:
case h := <-t.resCh:
rtt := time.Since(start)
if t.final != -1 && fork.ttl > t.final {
if t.final != -1 && ttl > t.final {
return nil
}
if addr, ok := h.Address.(*net.IPAddr); ok && addr.IP.Equal(t.DestIP) {
t.finalLock.Lock()
if t.final == -1 || fork.ttl < t.final {
t.final = fork.ttl
if t.final == -1 || ttl < t.final {
t.final = ttl
}
t.finalLock.Unlock()
} else if addr, ok := h.Address.(*net.TCPAddr); ok && addr.IP.Equal(t.DestIP) {
t.finalLock.Lock()
if t.final == -1 || fork.ttl < t.final {
t.final = fork.ttl
if t.final == -1 || ttl < t.final {
t.final = ttl
}
t.finalLock.Unlock()
}
h.TTL = fork.ttl
h.TTL = ttl
h.RTT = rtt
h.fetchIPData(t.Config)
if t.Config.RoutePath {
HopPrinter(h)
}
t.res.add(h)
case <-time.After(t.Timeout):
if t.final != -1 && fork.ttl > t.final {
if t.final != -1 && ttl > t.final {
return nil
}
t.res.add(Hop{
Success: false,
Address: nil,
TTL: fork.ttl,
TTL: ttl,
RTT: 0,
Error: ErrHopLimitTimeout,
})
if t.Config.RoutePath {
fmt.Println("\t" + "*")
}
}
return nil

View File

@@ -25,7 +25,7 @@ type Config struct {
Quic bool
IPGeoSource ipgeo.Source
RDns bool
RoutePath bool
RealtimePrinter func(res *Result, ttl int)
}
type Method string