Skip to content

Commit 7b1b99f

Browse files
committed
icmp: 支持指定 ttl、size
1 parent a12dc42 commit 7b1b99f

File tree

5 files changed

+91
-17
lines changed

5 files changed

+91
-17
lines changed

cmd/pping/cmd/icmp.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
type icmpFlags struct {
1313
privileged bool
1414
timeout time.Duration
15+
ttl int
16+
size int
1517
}
1618

1719
var icmpflag icmpFlags
@@ -27,6 +29,8 @@ func addIcmpCommand() {
2729

2830
cmd.Flags().DurationVarP(&icmpflag.timeout, "timeout", "w", time.Second*4, "timeout")
2931
cmd.Flags().BoolVarP(&icmpflag.privileged, "privileged", "p", false, "privileged")
32+
cmd.Flags().IntVarP(&icmpflag.ttl, "ttl", "l", 0, "time to live")
33+
cmd.Flags().IntVarP(&icmpflag.size, "size", "s", 0, "send buffer size")
3034
rootCmd.AddCommand(cmd)
3135
}
3236

@@ -35,5 +39,11 @@ func runicmp(cmd *cobra.Command, args []string) error {
3539
fmt.Printf("Ping %s:\n", host)
3640
p := ping.NewIcmpPing(host, icmpflag.timeout)
3741
p.Privileged = icmpflag.privileged
42+
if icmpflag.ttl > 0 {
43+
p.TTL = icmpflag.ttl
44+
}
45+
if icmpflag.size > 0 {
46+
p.Size = icmpflag.size
47+
}
3848
return RunPing(p)
3949
}

go.sum

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,13 @@ github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
88
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
99
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
1010
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
11-
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
12-
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
1311
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
1412
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
1513
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
1614
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
1715
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
1816
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
1917
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
20-
golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
21-
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
2218
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
2319
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
2420
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

pkg/ping/icmp.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ type IcmpPing struct {
4545

4646
ip net.IP
4747
Privileged bool
48+
TTL int
49+
Size int
4850
}
4951

5052
func (this *IcmpPing) SetHost(host string) {
@@ -59,6 +61,7 @@ func (this *IcmpPing) Host() string {
5961
func NewIcmpPing(host string, timeout time.Duration) *IcmpPing {
6062
p := &IcmpPing{
6163
Timeout: timeout,
64+
Size: 32,
6265
}
6366
p.SetHost(host)
6467
return p
@@ -96,10 +99,17 @@ func (this *IcmpPing) rawping(network string) IPingResult {
9699
}
97100
defer conn.Close()
98101
conn.SetDeadline(time.Now().Add(this.Timeout))
102+
if this.TTL > 0 {
103+
if isipv6 {
104+
conn.IPv6PacketConn().SetHopLimit(this.TTL)
105+
} else {
106+
conn.IPv4PacketConn().SetTTL(this.TTL)
107+
}
108+
}
99109

100110
// 发送
101111
r := rand.New(rand.NewSource(time.Now().UnixNano()))
102-
sendData := make([]byte, 32)
112+
sendData := make([]byte, this.Size)
103113
r.Read(sendData)
104114
id := os.Getpid() & 0xffff
105115
sendMsg := this.getmsg(isipv6, id, 0, sendData)
@@ -123,7 +133,8 @@ func (this *IcmpPing) rawping(network string) IPingResult {
123133
break
124134
}
125135

126-
recvBytes := make([]byte, 1500)
136+
// 直接分配一个足够大的缓冲区
137+
recvBytes := make([]byte, this.Size+128)
127138
recvSize := 0
128139

129140
for {

pkg/ping/icmp_windows.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"encoding/binary"
88
"errors"
99
"fmt"
10+
"math/rand"
1011
"net"
1112
"syscall"
1213
"time"
@@ -67,13 +68,15 @@ func (this *IcmpPing) ping_rootless(ctx context.Context) IPingResult {
6768
IcmpCloseHandle(handle)
6869
}
6970
}()
71+
r := rand.New(rand.NewSource(time.Now().UnixNano()))
7072
if isipv6 {
7173
handle = Icmp6CreateFile()
7274
if handle == syscall.InvalidHandle {
7375
return this.errorResult(errors.New("IcmpCreateFile failed"))
7476
}
75-
data := make([]byte, 32)
76-
recv := Icmp6SendEcho(handle, ip, data, this.Timeout)
77+
data := make([]byte, this.Size)
78+
r.Read(data)
79+
recv := Icmp6SendEcho(handle, ip, data, this.Timeout, this.TTL)
7780
if recv == nil {
7881
return this.errorResult(errors.New("IcmpSendEcho failed"))
7982
}
@@ -92,8 +95,9 @@ func (this *IcmpPing) ping_rootless(ctx context.Context) IPingResult {
9295
if handle == syscall.InvalidHandle {
9396
return this.errorResult(errors.New("IcmpCreateFile failed"))
9497
}
95-
data := make([]byte, 32)
96-
recv := IcmpSendEcho(handle, ip, data, this.Timeout)
98+
data := make([]byte, this.Size)
99+
r.Read(data)
100+
recv := IcmpSendEcho(handle, ip, data, this.Timeout, this.TTL)
97101
if recv == nil {
98102
return this.errorResult(errors.New("IcmpSendEcho failed"))
99103
}
@@ -124,8 +128,14 @@ func IcmpCloseHandle(h syscall.Handle) uintptr {
124128
return ret
125129
}
126130

127-
func IcmpSendEcho(handle syscall.Handle, ip net.IP, data []byte, timeout time.Duration) []byte {
128-
buf := make([]byte, 1500)
131+
func IcmpSendEcho(handle syscall.Handle, ip net.IP, data []byte, timeout time.Duration, ttl int) []byte {
132+
buf := make([]byte, (int)(unsafe.Sizeof(icmp_echo_reply{}))+len(data))
133+
var pOptions *ip_option_information
134+
if ttl > 0 {
135+
pOptions = &ip_option_information{
136+
ttl: uint8(ttl),
137+
}
138+
}
129139
n, _, _ := icmpSendEcho2.Call(
130140
uintptr(handle), // icmphandle
131141
0, // event
@@ -134,7 +144,7 @@ func IcmpSendEcho(handle syscall.Handle, ip net.IP, data []byte, timeout time.Du
134144
uintptr(ipv4ToInt(ip)), // destinationaddress
135145
uintptr(unsafe.Pointer(&data[0])), // requestdata
136146
uintptr(len(data)), // requestsize
137-
0, // requestoptions
147+
uintptr(unsafe.Pointer(pOptions)), // requestoptions
138148
uintptr(unsafe.Pointer(&buf[0])), // replaybuffer
139149
uintptr(len(buf)), // replysize
140150
uintptr(timeout.Milliseconds()), // timeout
@@ -150,15 +160,21 @@ func Icmp6CreateFile() syscall.Handle {
150160
return syscall.Handle(h)
151161
}
152162

153-
func Icmp6SendEcho(handle syscall.Handle, ip net.IP, data []byte, timeout time.Duration) []byte {
163+
func Icmp6SendEcho(handle syscall.Handle, ip net.IP, data []byte, timeout time.Duration, ttl int) []byte {
154164
ip6source := syscall.RawSockaddrInet6{
155165
Family: syscall.AF_INET6,
156166
}
157167
ip6dest := syscall.RawSockaddrInet6{
158168
Family: syscall.AF_INET6,
159169
}
160170
copy(ip6dest.Addr[:], ip)
161-
buf := make([]byte, 1500)
171+
buf := make([]byte, (int)(unsafe.Sizeof(icmpv6_echo_reply{}))+len(data))
172+
var pOptions *ip_option_information
173+
if ttl > 0 {
174+
pOptions = &ip_option_information{
175+
ttl: uint8(ttl),
176+
}
177+
}
162178
n, _, _ := icmp6SendEcho2.Call(
163179
uintptr(handle), // icmphandle
164180
0, // event
@@ -168,7 +184,7 @@ func Icmp6SendEcho(handle syscall.Handle, ip net.IP, data []byte, timeout time.D
168184
uintptr(unsafe.Pointer(&ip6dest)), // destinationaddress
169185
uintptr(unsafe.Pointer(&data[0])), // requestdata
170186
uintptr(len(data)), // requestsize
171-
0, // requestoptions
187+
uintptr(unsafe.Pointer(pOptions)), // requestoptions
172188
uintptr(unsafe.Pointer(&buf[0])), // replaybuffer
173189
uintptr(len(buf)), // replysize
174190
uintptr(timeout.Milliseconds()), // timeout
@@ -187,6 +203,8 @@ func icmpStatusToString(status uint32) string {
187203
return "destination host was unreachable"
188204
case 11010:
189205
return "request timed out"
206+
case 11013:
207+
return "time exceeded"
190208
}
191209
return fmt.Sprintf("unknown error (%d)", status)
192210
}

test/pping_test.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package pping_test
22

33
import (
4+
"strings"
45
"testing"
56
"time"
67

78
"github.com/wzv5/pping/pkg/ping"
89
)
910

10-
const HOST = "www.baidu.com"
11+
const HOST = "www.microsoft.com"
1112

1213
func TestTls(t *testing.T) {
1314
p := ping.NewTlsPing(HOST, 443, time.Second*1, time.Second*3)
@@ -52,3 +53,41 @@ func TestIcmp6(t *testing.T) {
5253
t.Fatal(result.Error())
5354
}
5455
}
56+
57+
func TestIcmp_ttl(t *testing.T) {
58+
p := ping.NewIcmpPing(HOST, time.Second*1)
59+
p.TTL = 1
60+
result := p.Ping()
61+
if !strings.Contains(result.Error().Error(), "exceeded") {
62+
t.Fatal(result.Error())
63+
}
64+
}
65+
66+
func TestIcmp_ttl_root(t *testing.T) {
67+
p := ping.NewIcmpPing(HOST, time.Second*1)
68+
p.Privileged = true
69+
p.TTL = 1
70+
result := p.Ping()
71+
if !strings.Contains(result.Error().Error(), "exceeded") {
72+
t.Fatal(result.Error())
73+
}
74+
}
75+
76+
func TestIcmp_size(t *testing.T) {
77+
p := ping.NewIcmpPing(HOST, time.Second*1)
78+
p.Size = 60000
79+
result := p.Ping()
80+
if result.Error() != nil {
81+
t.Fatal(result.Error())
82+
}
83+
}
84+
85+
func TestIcmp_size_root(t *testing.T) {
86+
p := ping.NewIcmpPing(HOST, time.Second*1)
87+
p.Privileged = true
88+
p.Size = 60000
89+
result := p.Ping()
90+
if result.Error() != nil {
91+
t.Fatal(result.Error())
92+
}
93+
}

0 commit comments

Comments
 (0)