-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgo-clat.go
More file actions
306 lines (252 loc) · 7.65 KB
/
go-clat.go
File metadata and controls
306 lines (252 loc) · 7.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
package main
import (
"log"
"net"
"os"
"os/exec"
"os/signal"
"syscall"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
func app() int {
log.Printf("Starting go-clat")
// Check pre-requisites
if !isNetLinkAvailable() {
log.Print("This operating system is currently not supported by go-clat. We need netlink support.")
return 1
}
// Check if ip6tables exists
_, err := exec.LookPath("ip6tables")
if err != nil {
log.Print("ip6tables not found, please install it")
return 1
}
// Obtain the NAT64 prefix
nat64Net, err := getNAT64Prefix()
if err != nil {
log.Print(err)
return 1
}
// Generate the IPv4 tunnel
ipAddr := getIPv4TunAddress()
iface, err := createIPv4Tun(ipAddr)
if err != nil {
log.Print(err)
return 1
}
// Generate the IPv6 addrs to use to send & receive packets
ipv6Prefix, ipv6Iface, err := getPublicIPv6()
if err != nil {
log.Print(err)
return 1
}
publicIPv6Addr, err := generateIPv6Address(ipAddr, ipv6Prefix)
if err != nil {
log.Print(err)
return 1
}
// Generate the IPv6 tunnel
tunnelIPv6NetIP, tunnelIPv6NetSrcIP := getIPv6TunNet(ipAddr)
iface6, err6 := createIPv6Tun(tunnelIPv6NetIP)
if err6 != nil {
log.Print(err)
return 1
}
// Check if ipv6 forwarding is enabled for the interface found
// Otherwise no packets will come in
settingName := "net.ipv6.conf.all.forwarding=1"
cmd := exec.Command("sysctl", "-n", settingName)
output, err := cmd.Output()
if err != nil {
log.Printf("Error checking IPv6 forwarding: %v", err)
return 1
}
if string(output) != "1\n" {
// Automatically enable forwarding by running sysctl -w
cmd := exec.Command("sysctl", "-w", settingName)
if err := cmd.Run(); err != nil {
log.Printf("Error enabling IPv6 forwarding: %v", err)
log.Printf("IPv6 forwarding is not enabled. Please enable it by running: sysctl -w %s", settingName)
return 1
}
}
// Log all addresses for debugging
log.Printf("IPv6 public address: %s", publicIPv6Addr)
log.Printf("IPv6 interface: %s", ipv6Iface.Name)
// Add the public IPv6 to the actual interface
err = addIPv6Address(ipv6Iface, publicIPv6Addr)
if err != nil {
log.Print(err)
return 1
}
// Remove the IPv6 when this program exits
defer func() {
if err := removeIPv6Address(ipv6Iface, publicIPv6Addr); err != nil {
log.Printf("Error removing IPv6 address: %v", err)
}
log.Printf("Removed extra IPv6 address")
}()
// Perform the neccesary firewall settings
err = configureIptables(publicIPv6Addr, tunnelIPv6NetSrcIP, ipv6Iface.Name)
if err != nil {
log.Print(err)
return 1
}
defer func() {
if err := deconfigureIptables(publicIPv6Addr, tunnelIPv6NetSrcIP, ipv6Iface.Name); err != nil {
log.Printf("Error removing IPv6 address: %v", err)
}
log.Printf("Removed IPv6 iptables config for go-clat")
}()
/** ====
* SETUP DONE!
* =====
*/
// All done, now listen for packets!
go func() {
// IPv6
log.Printf("Listening for incoming IPv6 packets on %s", iface6.Name())
packet := make([]byte, 2000)
for {
n, err := iface6.Read(packet)
if err != nil {
log.Fatal(err)
}
// Parse the IPv6 packet
// Use gopacket to parse the IPv4 packet
packetData := packet[:n]
packet := gopacket.NewPacket(packetData, layers.LayerTypeIPv6, gopacket.Default)
ipLayer := packet.Layer(layers.LayerTypeIPv6)
if ipLayer == nil {
// Not IPv6 packet, ignore because we are only translating IPv6 here
continue
}
ip, _ := ipLayer.(*layers.IPv6)
// If dest is not the tunneladdr, drop it, we are only interested in return packets
if !ip.DstIP.Equal(tunnelIPv6NetSrcIP) {
continue
}
// If the packet is not from the nat64 prefix, drop it
if !nat64Net.Contains(ip.SrcIP) && ip.NextLayerType() != layers.LayerTypeICMPv6 {
log.Printf("Dropping non-ICMP packet with wrong source %s", ip.SrcIP)
continue
}
// Translate the packet to IPv4
result := translateIPv6(packet, ipAddr, nat64Net, false)
// Put the resulting packet back onto the IPv4 interface
if result == nil {
log.Printf("Dropping incoming IPv6 packet to %s (%s), Next Layer: %s",
ip.DstIP, ipAddr, ip.NextLayerType())
continue
}
// Log the result packet
//log.Printf("Translated IPv6 packet to %x", result)
_, err = iface.Write(result)
if err != nil {
log.Printf("Error writing packet to IPv4 interface: %v", err)
}
}
}()
go func() {
// IPv4
log.Printf("Listening for incoming IPv4 packets on %s", iface.Name())
packet := make([]byte, 2000)
for {
n, err := iface.Read(packet)
if err != nil {
log.Fatal(err)
}
// Parse the IPv4 packet
// Use gopacket to parse the IPv4 packet
packetData := packet[:n]
packet := gopacket.NewPacket(packetData, layers.LayerTypeIPv4, gopacket.Default)
ipLayer := packet.Layer(layers.LayerTypeIPv4)
if ipLayer == nil {
// Not IPv4 packet, ignore because we are only translating IPv4 here
continue
}
ip, _ := ipLayer.(*layers.IPv4)
// https://datatracker.ietf.org/doc/html/rfc6145
// Safeguards
// If src is wrong, drop the packet
if !ip.SrcIP.Equal(ipAddr) {
continue
}
// If multicast, local or otherwise malformed, drop the packet
if ip.DstIP.IsMulticast() || ip.DstIP.IsLinkLocalUnicast() || ip.DstIP.IsLinkLocalMulticast() {
continue
}
// If the packet is fragmented ICMP, drop it
// Source: Fragmented ICMP/ICMPv6 packets will not be translated by the IP/ICMP translator.
if ip.Protocol == layers.IPProtocolICMPv4 &&
(ip.Flags&layers.IPv4MoreFragments != 0 || ip.FragOffset != 0) {
log.Printf("Dropping fragmented ICMP packet")
continue
}
// Drop packet if the packet is not a TCP, UDP or ICMP packet
if ip.Protocol != layers.IPProtocolTCP &&
ip.Protocol != layers.IPProtocolUDP &&
ip.Protocol != layers.IPProtocolICMPv4 {
log.Printf("Dropping packet with unsupported protocol %d", ip.Protocol)
continue
}
if int(ip.TTL) < 3 {
// Return immediately with an error
result := generateICMPv4FullTTLExceeded(packetData)
_, err = iface.Write(result)
if err != nil {
log.Printf("Error writing packet to IPv4 interface: %v", err)
}
continue
}
// Map the IPv4 destination onto the NAT64 prefix
ipv4Bytes := ip.DstIP.To4()
if ipv4Bytes == nil {
log.Printf("Invalid IPv4 address: %s", ip.DstIP)
continue
}
nat64DstIP := nat64Net.IP
copy(nat64DstIP[12:], ipv4Bytes)
// Translate the packet to IPv6
result := translateIPv4(packet, tunnelIPv6NetSrcIP, nat64DstIP)
if result == nil {
// We shouldn't translate this packet
log.Printf("Dropping IPv4 packet to %s (%s), Flags: %s, ID: %d, TTL: %d, Protocol: %s",
ip.DstIP, nat64DstIP, ip.Flags, ip.Id, ip.TTL, ip.Protocol)
continue
}
// Log the result packet
//log.Printf("Translated IPv4 packet to %x", result)
sendPacket(result, nat64DstIP, ip.Id)
}
}()
gracefulShutdown := make(chan os.Signal, 1)
signal.Notify(gracefulShutdown, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
<-gracefulShutdown
log.Println("Got a shutdown request, exiting gracefully...")
return 0
}
func sendPacket(packet []byte, dstIP net.IP, id uint16) {
// Open a raw socket for sending the translated packet
fd, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
if err != nil {
log.Printf("Error creating raw socket: %v", err)
return
}
// Destination address for the packet
addr := &syscall.SockaddrInet6{
Port: 0,
Addr: [16]byte{},
}
copy(addr.Addr[:], dstIP)
// Send the packet
err = syscall.Sendto(fd, packet, 0, addr)
syscall.Close(fd)
if err != nil {
log.Printf("Error sending translated packet for %d: %v", id, err)
}
}
func main() {
os.Exit(app())
}