Skip to content

Commit 5d6aa12

Browse files
committed
vpn: reject with icmp-host-unreachable when no route to host
1 parent 514b44a commit 5d6aa12

File tree

4 files changed

+77
-4
lines changed

4 files changed

+77
-4
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.24
44

55
require (
66
github.com/coreos/go-oidc/v3 v3.12.0
7+
github.com/google/gopacket v1.1.19
78
github.com/gorilla/websocket v1.5.3
89
github.com/jedib0t/go-pretty/v6 v6.6.7
910
github.com/mdp/qrterminal/v3 v3.2.0

go.sum

+14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
88
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
99
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
1010
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
11+
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
12+
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
1113
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
1214
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
1315
github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo=
@@ -27,22 +29,34 @@ github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQ
2729
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
2830
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
2931
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
32+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
33+
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
3034
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
3135
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
36+
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
37+
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
38+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
39+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
3240
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
3341
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
3442
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
3543
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
44+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
45+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
46+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
3647
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
3748
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
3849
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
3950
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
4051
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
4152
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
53+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
4254
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
4355
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
4456
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
4557
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
58+
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
59+
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
4660
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
4761
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
4862
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=

vpn/icmp.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package vpn
2+
3+
import (
4+
"bytes"
5+
"log/slog"
6+
"net"
7+
8+
"github.com/google/gopacket"
9+
"github.com/google/gopacket/layers"
10+
)
11+
12+
// ICMPHostUnreachable build a icmp(6) packet
13+
//
14+
// icmp-host-unreachable for ipv4
15+
// icmp6-addr-unreachable for ipv6
16+
func ICMPHostUnreachable(srcIP, dstIP net.IP, data []byte) []byte {
17+
var packetLayers []gopacket.SerializableLayer
18+
19+
if srcIP.To4() != nil { // ipv4
20+
packetLayers = append(packetLayers, &layers.IPv4{
21+
Version: 4,
22+
IHL: 5,
23+
SrcIP: srcIP,
24+
DstIP: dstIP,
25+
TTL: 64,
26+
Protocol: layers.IPProtocolICMPv4,
27+
})
28+
packetLayers = append(packetLayers, &layers.ICMPv4{
29+
TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeDestinationUnreachable, layers.ICMPv4CodeHost),
30+
})
31+
} else { // ipv6
32+
ipLayer := &layers.IPv6{
33+
Version: 6,
34+
SrcIP: srcIP,
35+
DstIP: dstIP,
36+
HopLimit: 64,
37+
NextHeader: layers.IPProtocolICMPv6,
38+
}
39+
icmpv6Layer := &layers.ICMPv6{
40+
TypeCode: layers.CreateICMPv6TypeCode(layers.ICMPv6TypeDestinationUnreachable, layers.ICMPv6CodeAddressUnreachable),
41+
}
42+
_ = icmpv6Layer.SetNetworkLayerForChecksum(ipLayer)
43+
packetLayers = append(packetLayers, ipLayer)
44+
packetLayers = append(packetLayers, icmpv6Layer)
45+
packetLayers = append(packetLayers, gopacket.Payload(bytes.Repeat([]byte{0}, 4))) // 4 bytes reserved
46+
}
47+
48+
packetLayers = append(packetLayers, gopacket.Payload(data))
49+
50+
buffer := gopacket.NewSerializeBuffer()
51+
if err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}, packetLayers...); err != nil {
52+
slog.Error("Serialize icmp-host-unreachable", "err", err)
53+
return nil
54+
}
55+
56+
return buffer.Bytes()
57+
}

vpn/vpn.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func (vpn *VPN) packetConnRead(wg *sync.WaitGroup, packetConn net.PacketConn) {
145145
// packetConnWrite read ip packet from outbound channel and write to packet conn
146146
func (vpn *VPN) packetConnWrite(wg *sync.WaitGroup, packetConn net.PacketConn) {
147147
defer wg.Done()
148-
sendPacketToPeer := func(packet *nic.Packet, dstIP net.IP) {
148+
sendPacketToPeer := func(packet *nic.Packet, srcIP, dstIP net.IP) {
149149
defer nic.RecyclePacket(packet)
150150
if dstIP.IsMulticast() {
151151
slog.Log(context.Background(), -10, "DropMulticastIP", "dst", dstIP)
@@ -158,7 +158,8 @@ func (vpn *VPN) packetConnWrite(wg *sync.WaitGroup, packetConn net.PacketConn) {
158158
}
159159
return
160160
}
161-
slog.Log(context.Background(), -1, "DropPacketPeerNotFound", "ip", dstIP)
161+
// reject with icmp-host-unreachable
162+
vpn.inbound <- nic.GetPacket(ICMPHostUnreachable(dstIP, srcIP, packet.AsBytes()))
162163
}
163164
handle := func(pkt *nic.Packet) *nic.Packet {
164165
for _, out := range vpn.cfg.OutboundHandlers {
@@ -184,7 +185,7 @@ func (vpn *VPN) packetConnWrite(wg *sync.WaitGroup, packetConn net.PacketConn) {
184185
vpn.inbound <- packet
185186
continue
186187
}
187-
sendPacketToPeer(packet, header.Dst)
188+
sendPacketToPeer(packet, header.Src, header.Dst)
188189
continue
189190
}
190191
if packet.Ver() == 6 {
@@ -196,7 +197,7 @@ func (vpn *VPN) packetConnWrite(wg *sync.WaitGroup, packetConn net.PacketConn) {
196197
vpn.inbound <- packet
197198
continue
198199
}
199-
sendPacketToPeer(packet, header.Dst)
200+
sendPacketToPeer(packet, header.Src, header.Dst)
200201
continue
201202
}
202203
slog.Warn("Received invalid packet", "packet", hex.EncodeToString(pkt))

0 commit comments

Comments
 (0)