/
dnat.go
166 lines (138 loc) · 3.61 KB
/
dnat.go
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
package dnat
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"os"
"strconv"
"syscall"
"unsafe"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/refraction-networking/conjure/pkg/core/interfaces"
"golang.org/x/sys/unix"
)
// NewDNAT returns an object that implements the DNAT interface
func NewDNAT() (interfaces.DNAT, error) {
const (
IFF_TUN = 0x0001
IFF_NO_PI = 0x1000
TUNSETIFF = 0x400454ca
)
tun, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0)
if err != nil {
return nil, err
}
var ifreq [0x28]byte
coreCountStr := os.Getenv("CJ_CORECOUNT")
coreCount, err := strconv.Atoi(coreCountStr)
if err != nil {
return nil, fmt.Errorf("error parsing core count: %v", err)
}
offsetStr := os.Getenv("OFFSET")
offset, err := strconv.Atoi(offsetStr)
if err != nil {
return nil, fmt.Errorf("error parsing offset: %v", err)
}
copy(ifreq[:], "tun"+strconv.Itoa(offset+coreCount))
flags := IFF_TUN | IFF_NO_PI
binary.LittleEndian.PutUint16(ifreq[0x10:], uint16(flags))
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, tun.Fd(), uintptr(TUNSETIFF), uintptr(unsafe.Pointer(&ifreq[0])))
if errno != 0 {
tun.Close()
return nil, errno
}
// Get the interface name
name := string(ifreq[:bytes.IndexByte(ifreq[:], 0)])
// Bring the interface up
err = setUp(tun, name)
if err != nil {
return nil, fmt.Errorf("error bring the interface up: %v", err)
}
return &dnat{
tun: tun,
}, nil
}
// setUp brings up a network interface represented by the given name.
func setUp(tun *os.File, name string) error {
ifreq, err := unix.NewIfreq(name)
if err != nil {
return fmt.Errorf("error creating ifreq: %v", err)
}
fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
if err != nil {
return fmt.Errorf("error creating socket: %v", err)
}
// Get the current interface flags
err = unix.IoctlIfreq(fd, syscall.SIOCGIFFLAGS, ifreq)
if err != nil {
return fmt.Errorf("error getting interface flags: %v", err)
}
ifreq.SetUint16(ifreq.Uint16() | syscall.IFF_UP)
// Set the new interface flags
err = unix.IoctlIfreq(fd, syscall.SIOCSIFFLAGS, ifreq)
if err != nil {
return fmt.Errorf("error setting interface flags: %v", err)
}
return nil
}
type dnat struct {
tun *os.File
}
func (d *dnat) AddEntry(src *net.IP, sport uint16, dst *net.IP, dport uint16) error {
type networkAndSerializable interface {
gopacket.SerializableLayer
gopacket.NetworkLayer
}
var ipLayer networkAndSerializable
if src.To4() != nil && dst.To4() != nil {
ipLayer = &layers.IPv4{
Version: 4,
IHL: 5,
TTL: 64,
SrcIP: *src,
DstIP: *dst,
Protocol: layers.IPProtocolUDP,
}
} else if src.To4() == nil && dst.To4() == nil {
ipLayer = &layers.IPv6{
Version: 6,
TrafficClass: 0,
HopLimit: 64,
SrcIP: *src,
DstIP: *dst,
NextHeader: layers.IPProtocolUDP,
}
} else {
return fmt.Errorf("both src and dst must be either IPv4 or IPv6")
}
udpLayer := &layers.UDP{
SrcPort: layers.UDPPort(sport),
DstPort: layers.UDPPort(dport),
}
err := udpLayer.SetNetworkLayerForChecksum(ipLayer)
if err != nil {
return err
}
payload := []byte("Hello world")
buffer := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
ComputeChecksums: true,
FixLengths: true,
}
err = gopacket.SerializeLayers(buffer, opts,
ipLayer,
udpLayer,
gopacket.Payload(payload),
)
if err != nil {
return fmt.Errorf("error serializing injected packet: %v", err)
}
pkt := buffer.Bytes()
_, err = d.tun.Write(pkt)
if err != nil {
return fmt.Errorf("error writing to tun interface: %v", err)
}
return nil
}