在和朋友一起吃饭的时候,A 提了一个有意思的问题:怎样可以把一个机房内的路由设备从互联网「隐藏」呢?
「隐藏就是没有人可以知道这个设备的 IP 地址,这还不简单,只要禁用 ICMP 就可以了」, B说。
A 说,这样是可以。很多安全团队在实施起来也确实是这么做的,但是这样并不好:机房内所有的 IP 都无法 ping 通了。这样会增加 debug 的难度,得不偿失呀!
C 说,那就依然转发 ICMP 包,但是如果是 TTL=1 的包,就不要回复 ICMP Time Exceeded 了。
B 说,人家要的是「隐藏」,要是像你说的这么做,别人还是知道中间有一个设备的存在,没有完全符合要求。

事实是这样的。假设一个简单的物理拓扑是 A -> B -> C,B 不回复 ICMP Time Exceeded,那么 traceroute 看起来就是 A ? C,可以猜测得到中间有一个路由器,但是已经禁止回复 ICMP Time Exceeded。看起来像下面这样。
C 说,traceroute 的原理是发送 TTL=1, 2, 3, … 的包,不断让路由器回复 ICMP Time Exceeded 信息,来得到每一跳的 IP 地址。要想完全隐藏,只需要:
- 自己不回复
ICMP Time Exceeded; - 让下一跳回复,仿佛下一跳就在自己的位置;
这样就可以完全隐藏了。要达到这个目的,只需要:
- 对于 TTL=1 的包,不是丢弃,而是转发给下一跳,并且 TTL 依然保持为 1,即可。
A ---[TTL=1]---> B ---[TTL=1]---> C, 对于客户端的 traceroute,看起来就像:A → C。
这样(理论上)好像确实可行了。三人对这个结论满意了。
后来我把这个讨论记录在了博客上(你现在正在阅读的一个),一位读者马上就发现了问题:可是这样 C 会出现两次吧!
确实是这样,假设在 A -> B -> C 的链路中:
- TTL = 1 从 A 进入的时候,A 会在 ICMP 中回复自己的 IP;
- TTL = 2 从 A 进入的时候,B 会直接转发给 C,C 会在 ICMP 中回复自己的 IP;
- TTL = 3 从 A 进入的时候,B 会 TTL -1 转发给 C,C 会在 ICMP 中回复自己的 IP;
这样 C 就出现了 2 次!
看来,B 必须完全不减 TTL,直接转发,才能隐藏自己。不过这样就有出现环路2的风险了。
可是这样 C 会出现两次吧
说的对哦,那是不是全部都不减 TTL 直接转发,就可以了?
小心打环哦(
是有风险,不过严格只有一台设备这么做的话是没有风险的。
好像可以在网络都正常的情况,不减 TTL 直接转发好像就可以隐藏了
谢谢,我更新一下原文。
原文已更新,现在应该没有问题了。
请教个多线程问题,就是我有两台服务器10.58.3.21和10.58.3.23, 10.58.3.21会开启多个线程从10.58.3.23:9091服务拉取数据,每个线程都是独立或者32MiB数据
问题是:
如果客户端上面开启2个线程,那么每个线程的耗时为50ms
如果客户端上面开启4个线程,那么每个线程的耗时为100ms
如果客户端上面开启8个线程,那么每个线程的耗时为200ms
以此类推耗时成倍上升,我用c语言测试也是一样的结果,我可以理解多线程有损耗,但是这种成倍耗时上升,不清楚是不是正常的
服务器之间的带宽是万兆,服务器都是56C cpu,256G memory
golang代码如下:
10.58.3.21(client):
“`
package main
import (
“fmt”
“io”
“net”
“sync”
“time”
“flag”
)
//const concurrency = 32 // 并发 goroutine 数量
func main() {
concurrency := flag.Int(“c”, 1, “number of concurrent goroutines”)
port := flag.Int(“port”, 9090, “server port”)
host := flag.String(“host”, “127.0.0.1”, “server host”)
name := flag.String(“name”, “client1”, “server name”)
flag.Parse()
var wg sync.WaitGroup
for i := 0; i < *concurrency; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
start := time.Now() // goroutine 开始时间
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d",*host, *port))
if err != nil {
fmt.Printf("[%s][Worker %d] Dial error: %v\n", *name, id, err)
return
}
defer conn.Close()
buf := make([]byte, 4*1024*1024) // 1 MiB buffer
total := 0
count := 0
for {
count = count+1
//start1 := time.Now()
n, err := conn.Read(buf)
total += n
if err == io.EOF {
break
}
if err != nil {
fmt.Printf("[%s][Worker %d] Read error: %v\n", *name, id, err)
return
}
}
duration := time.Since(start)
fmt.Printf("[%s][Worker %d] Finished reading %d bytes, duration: %v\n",*name, id, total, duration)
}(i + 1)
}
wg.Wait()
fmt.Println("All goroutines completed")
}
“`
10.58.3.23:9091(server)代码:
“`
package main
import (
"fmt"
"math/rand"
"net"
"time"
)
const dataSize = 32*1024*1024 // 128 MiB
func main() {
ln, err := net.Listen("tcp", "10.58.3.23:9091")
if err != nil {
panic(err)
}
fmt.Println("Server listening on :9091")
rand.Seed(time.Now().UnixNano())
buf := make([]byte, 32*1024*1024) // 1 MiB buffer
for i := range buf {
buf[i] = byte(rand.Intn(256))
}
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println("Accept error:", err)
continue
}
go handleConn(conn, buf)
}
}
func handleConn(conn net.Conn, buf []byte) {
defer conn.Close()
fmt.Println("Client connected:", conn.RemoteAddr())
start := time.Now()
sent := 0
for sent < dataSize {
n, err := conn.Write(buf) // 写入初始化的数据
if err != nil {
fmt.Println("Write error:", err)
return
}
sent += n
}
duration := time.Since(start)
fmt.Printf("Sent 32 MiB to %v, duration: %v\n", conn.RemoteAddr(), duration)
}
“`
请教个多线程问题,就是我有两台服务器10.58.3.21和10.58.3.23, 10.58.3.21会开启多个线程从10.58.3.23:9091服务拉取数据,每个线程都是独立或者32MiB数据
问题是:
如果客户端上面开启2个线程,那么每个线程的耗时为50ms
如果客户端上面开启4个线程,那么每个线程的耗时为100ms
如果客户端上面开启8个线程,那么每个线程的耗时为200ms
以此类推耗时成倍上升,我用c语言测试也是一样的结果,我可以理解多线程有损耗,但是这种成倍耗时上升,不清楚是不是正常的
服务器之间的带宽是万兆,服务器都是56C cpu,256G memory
golang代码如下:
10.58.3.21(client):
“`
package main
import (
“fmt”
“io”
“net”
“sync”
“time”
“flag”
)
//const concurrency = 32 // 并发 goroutine 数量
func main() {
concurrency := flag.Int(“c”, 1, “number of concurrent goroutines”)
port := flag.Int(“port”, 9090, “server port”)
host := flag.String(“host”, “127.0.0.1”, “server host”)
name := flag.String(“name”, “client1”, “server name”)
flag.Parse()
var wg sync.WaitGroup
for i := 0; i < *concurrency; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
start := time.Now() // goroutine 开始时间
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d",*host, *port))
if err != nil {
fmt.Printf("[%s][Worker %d] Dial error: %v\n", *name, id, err)
return
}
defer conn.Close()
buf := make([]byte, 4*1024*1024) // 1 MiB buffer
total := 0
count := 0
for {
count = count+1
//start1 := time.Now()
n, err := conn.Read(buf)
total += n
if err == io.EOF {
break
}
if err != nil {
fmt.Printf("[%s][Worker %d] Read error: %v\n", *name, id, err)
return
}
}
duration := time.Since(start)
fmt.Printf("[%s][Worker %d] Finished reading %d bytes, duration: %v\n",*name, id, total, duration)
}(i + 1)
}
wg.Wait()
fmt.Println("All goroutines completed")
}
“`
10.58.3.23:9091(server)代码:
“`
package main
import (
"fmt"
"math/rand"
"net"
"time"
)
const dataSize = 32*1024*1024 // 128 MiB
func main() {
ln, err := net.Listen("tcp", "10.58.3.23:9091")
if err != nil {
panic(err)
}
fmt.Println("Server listening on :9091")
rand.Seed(time.Now().UnixNano())
buf := make([]byte, 32*1024*1024) // 1 MiB buffer
for i := range buf {
buf[i] = byte(rand.Intn(256))
}
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println("Accept error:", err)
continue
}
go handleConn(conn, buf)
}
}
func handleConn(conn net.Conn, buf []byte) {
defer conn.Close()
fmt.Println("Client connected:", conn.RemoteAddr())
start := time.Now()
sent := 0
for sent < dataSize {
n, err := conn.Write(buf) // 写入初始化的数据
if err != nil {
fmt.Println("Write error:", err)
return
}
sent += n
}
duration := time.Since(start)
fmt.Printf("Sent 32 MiB to %v, duration: %v\n", conn.RemoteAddr(), duration)
}
“`