101 lines
2.6 KiB
Go
101 lines
2.6 KiB
Go
package packet
|
|
|
|
import (
|
|
"context"
|
|
"net/netip"
|
|
|
|
"github.com/rs/zerolog"
|
|
)
|
|
|
|
// ICMPRouter sends ICMP messages and listens for their responses
|
|
type ICMPRouter interface {
|
|
// Serve starts listening for responses to the requests until context is done
|
|
Serve(ctx context.Context) error
|
|
// Request sends an ICMP message. Implementations should not modify pk after the function returns.
|
|
Request(pk *ICMP, responder FunnelUniPipe) error
|
|
}
|
|
|
|
// Upstream of raw packets
|
|
type Upstream interface {
|
|
// ReceivePacket waits for the next raw packet from upstream
|
|
ReceivePacket(ctx context.Context) (RawPacket, error)
|
|
}
|
|
|
|
// Router routes packets between Upstream and ICMPRouter. Currently it rejects all other type of ICMP packets
|
|
type Router struct {
|
|
upstream Upstream
|
|
returnPipe FunnelUniPipe
|
|
icmpRouter ICMPRouter
|
|
ipv4Src netip.Addr
|
|
ipv6Src netip.Addr
|
|
logger *zerolog.Logger
|
|
}
|
|
|
|
// GlobalRouterConfig is the configuration shared by all instance of Router.
|
|
type GlobalRouterConfig struct {
|
|
ICMPRouter ICMPRouter
|
|
IPv4Src netip.Addr
|
|
IPv6Src netip.Addr
|
|
Zone string
|
|
}
|
|
|
|
func NewRouter(globalConfig *GlobalRouterConfig, upstream Upstream, returnPipe FunnelUniPipe, logger *zerolog.Logger) *Router {
|
|
return &Router{
|
|
upstream: upstream,
|
|
returnPipe: returnPipe,
|
|
icmpRouter: globalConfig.ICMPRouter,
|
|
ipv4Src: globalConfig.IPv4Src,
|
|
ipv6Src: globalConfig.IPv6Src,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (r *Router) Serve(ctx context.Context) error {
|
|
icmpDecoder := NewICMPDecoder()
|
|
encoder := NewEncoder()
|
|
for {
|
|
rawPacket, err := r.upstream.ReceivePacket(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
icmpPacket, err := icmpDecoder.Decode(rawPacket)
|
|
if err != nil {
|
|
r.logger.Err(err).Msg("Failed to decode ICMP packet from quic datagram")
|
|
continue
|
|
}
|
|
|
|
if icmpPacket.TTL <= 1 {
|
|
if err := r.sendTTLExceedMsg(icmpPacket, rawPacket, encoder); err != nil {
|
|
r.logger.Err(err).Msg("Failed to return ICMP TTL exceed error")
|
|
}
|
|
continue
|
|
}
|
|
icmpPacket.TTL--
|
|
|
|
if err := r.icmpRouter.Request(icmpPacket, r.returnPipe); err != nil {
|
|
r.logger.Err(err).
|
|
Str("src", icmpPacket.Src.String()).
|
|
Str("dst", icmpPacket.Dst.String()).
|
|
Interface("type", icmpPacket.Type).
|
|
Msg("Failed to send ICMP packet")
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *Router) sendTTLExceedMsg(pk *ICMP, rawPacket RawPacket, encoder *Encoder) error {
|
|
var srcIP netip.Addr
|
|
if pk.Dst.Is4() {
|
|
srcIP = r.ipv4Src
|
|
} else {
|
|
srcIP = r.ipv6Src
|
|
}
|
|
ttlExceedPacket := NewICMPTTLExceedPacket(pk.IP, rawPacket, srcIP)
|
|
|
|
encodedTTLExceed, err := encoder.Encode(ttlExceedPacket)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return r.returnPipe.SendPacket(pk.Src, encodedTTLExceed)
|
|
}
|